From 196fc09c9e55c038206d1208e1f9e514fc730dce Mon Sep 17 00:00:00 2001
From: Francesco Novy
Date: Wed, 10 Jul 2024 15:25:29 +0200
Subject: [PATCH 01/39] fix(otel): Do not add `otel.kind: INTERNAL` attribute
(#12841)
Only add this if this is some other value, e.g. `CLIENT` or `SERVER`. It
is useless to have the internal value there and potentially confusing
(why is this there? what does internal even mean?)
---
.../aws-serverless-esm/tests/basic.test.ts | 1 -
.../create-next-app/tests/server-transactions.test.ts | 1 -
.../nestjs/tests/span-decorator.test.ts | 2 --
.../nestjs/tests/transactions.test.ts | 4 ----
.../node-connect/tests/transactions.test.ts | 2 --
.../node-express-cjs-preload/tests/server.test.ts | 3 ---
.../node-express-esm-loader/tests/server.test.ts | 3 ---
.../node-express-esm-preload/tests/server.test.ts | 3 ---
.../node-express/tests/transactions.test.ts | 6 ------
.../node-fastify/tests/transactions.test.ts | 4 ----
.../node-hapi/tests/transactions.test.ts | 2 --
.../node-koa/tests/transactions.test.ts | 4 ----
.../node-otel-sdk-node/tests/transactions.test.ts | 6 ------
.../suites/tracing/apollo-graphql/test.ts | 2 --
.../suites/tracing/connect/test.ts | 1 -
.../suites/tracing/nestjs/test.ts | 1 -
.../suites/tracing/prisma-orm/test.ts | 11 -----------
packages/node/test/integration/scope.test.ts | 1 -
packages/node/test/integration/transactions.test.ts | 10 ----------
packages/opentelemetry/src/spanExporter.ts | 8 +++++---
packages/opentelemetry/test/integration/scope.test.ts | 1 -
.../test/integration/transactions.test.ts | 8 --------
packages/opentelemetry/test/trace.test.ts | 6 ------
.../test/server/instrumentation-otel/action.test.ts | 3 ---
.../test/server/instrumentation-otel/loader.test.ts | 4 ----
25 files changed, 5 insertions(+), 92 deletions(-)
diff --git a/dev-packages/e2e-tests/test-applications/aws-serverless-esm/tests/basic.test.ts b/dev-packages/e2e-tests/test-applications/aws-serverless-esm/tests/basic.test.ts
index 8e507a469235..83fab96ee117 100644
--- a/dev-packages/e2e-tests/test-applications/aws-serverless-esm/tests/basic.test.ts
+++ b/dev-packages/e2e-tests/test-applications/aws-serverless-esm/tests/basic.test.ts
@@ -31,7 +31,6 @@ test('AWS Serverless SDK sends events in ESM mode', async ({ request }) => {
'sentry.source': 'component',
'sentry.origin': 'auto.function.serverless',
'sentry.op': 'function.aws.lambda',
- 'otel.kind': 'INTERNAL',
},
op: 'function.aws.lambda',
origin: 'auto.function.serverless',
diff --git a/dev-packages/e2e-tests/test-applications/create-next-app/tests/server-transactions.test.ts b/dev-packages/e2e-tests/test-applications/create-next-app/tests/server-transactions.test.ts
index 1d789cb4950c..56be8b65d60b 100644
--- a/dev-packages/e2e-tests/test-applications/create-next-app/tests/server-transactions.test.ts
+++ b/dev-packages/e2e-tests/test-applications/create-next-app/tests/server-transactions.test.ts
@@ -40,7 +40,6 @@ test('Sends server-side transactions to Sentry', async ({ baseURL }) => {
spans: [
{
data: {
- 'otel.kind': 'INTERNAL',
'sentry.origin': 'manual',
},
description: 'test-span',
diff --git a/dev-packages/e2e-tests/test-applications/nestjs/tests/span-decorator.test.ts b/dev-packages/e2e-tests/test-applications/nestjs/tests/span-decorator.test.ts
index 3efdfb979d73..28c925727d89 100644
--- a/dev-packages/e2e-tests/test-applications/nestjs/tests/span-decorator.test.ts
+++ b/dev-packages/e2e-tests/test-applications/nestjs/tests/span-decorator.test.ts
@@ -24,7 +24,6 @@ test('Transaction includes span and correct value for decorated async function',
data: {
'sentry.origin': 'manual',
'sentry.op': 'wait and return a string',
- 'otel.kind': 'INTERNAL',
},
description: 'wait',
parent_span_id: expect.any(String),
@@ -60,7 +59,6 @@ test('Transaction includes span and correct value for decorated sync function',
data: {
'sentry.origin': 'manual',
'sentry.op': 'return a string',
- 'otel.kind': 'INTERNAL',
},
description: 'getString',
parent_span_id: expect.any(String),
diff --git a/dev-packages/e2e-tests/test-applications/nestjs/tests/transactions.test.ts b/dev-packages/e2e-tests/test-applications/nestjs/tests/transactions.test.ts
index 22cb0f8e6a8c..ae5d8b63b16d 100644
--- a/dev-packages/e2e-tests/test-applications/nestjs/tests/transactions.test.ts
+++ b/dev-packages/e2e-tests/test-applications/nestjs/tests/transactions.test.ts
@@ -54,7 +54,6 @@ test('Sends an API route transaction', async ({ baseURL }) => {
'express.name': '/test-transaction',
'express.type': 'request_handler',
'http.route': '/test-transaction',
- 'otel.kind': 'INTERNAL',
'sentry.origin': 'auto.http.otel.express',
'sentry.op': 'request_handler.express',
},
@@ -70,7 +69,6 @@ test('Sends an API route transaction', async ({ baseURL }) => {
},
{
data: {
- 'otel.kind': 'INTERNAL',
'sentry.origin': 'manual',
},
description: 'test-span',
@@ -84,7 +82,6 @@ test('Sends an API route transaction', async ({ baseURL }) => {
},
{
data: {
- 'otel.kind': 'INTERNAL',
'sentry.origin': 'manual',
},
description: 'child-span',
@@ -106,7 +103,6 @@ test('Sends an API route transaction', async ({ baseURL }) => {
'nestjs.version': expect.any(String),
'nestjs.type': 'handler',
'nestjs.callback': 'testTransaction',
- 'otel.kind': 'INTERNAL',
},
description: 'testTransaction',
parent_span_id: expect.any(String),
diff --git a/dev-packages/e2e-tests/test-applications/node-connect/tests/transactions.test.ts b/dev-packages/e2e-tests/test-applications/node-connect/tests/transactions.test.ts
index 031fa71ec581..f0da250d8be9 100644
--- a/dev-packages/e2e-tests/test-applications/node-connect/tests/transactions.test.ts
+++ b/dev-packages/e2e-tests/test-applications/node-connect/tests/transactions.test.ts
@@ -53,7 +53,6 @@ test('Sends an API route transaction', async ({ baseURL }) => {
{
data: {
'sentry.origin': 'manual',
- 'otel.kind': 'INTERNAL',
},
description: 'test-span',
parent_span_id: expect.any(String),
@@ -71,7 +70,6 @@ test('Sends an API route transaction', async ({ baseURL }) => {
'http.route': '/test-transaction',
'connect.type': 'request_handler',
'connect.name': '/test-transaction',
- 'otel.kind': 'INTERNAL',
},
op: 'request_handler.connect',
description: '/test-transaction',
diff --git a/dev-packages/e2e-tests/test-applications/node-express-cjs-preload/tests/server.test.ts b/dev-packages/e2e-tests/test-applications/node-express-cjs-preload/tests/server.test.ts
index 5b8fd26f1647..5316cc377358 100644
--- a/dev-packages/e2e-tests/test-applications/node-express-cjs-preload/tests/server.test.ts
+++ b/dev-packages/e2e-tests/test-applications/node-express-cjs-preload/tests/server.test.ts
@@ -66,7 +66,6 @@ test('Should record a transaction for route with parameters', async ({ request }
'express.name': 'query',
'express.type': 'middleware',
'http.route': '/',
- 'otel.kind': 'INTERNAL',
'sentry.origin': 'auto.http.otel.express',
'sentry.op': 'middleware.express',
},
@@ -86,7 +85,6 @@ test('Should record a transaction for route with parameters', async ({ request }
'express.name': 'expressInit',
'express.type': 'middleware',
'http.route': '/',
- 'otel.kind': 'INTERNAL',
'sentry.origin': 'auto.http.otel.express',
'sentry.op': 'middleware.express',
},
@@ -106,7 +104,6 @@ test('Should record a transaction for route with parameters', async ({ request }
'express.name': '/test-transaction/:param',
'express.type': 'request_handler',
'http.route': '/test-transaction/:param',
- 'otel.kind': 'INTERNAL',
'sentry.origin': 'auto.http.otel.express',
'sentry.op': 'request_handler.express',
},
diff --git a/dev-packages/e2e-tests/test-applications/node-express-esm-loader/tests/server.test.ts b/dev-packages/e2e-tests/test-applications/node-express-esm-loader/tests/server.test.ts
index 3b65819e2c29..06e2c2e09434 100644
--- a/dev-packages/e2e-tests/test-applications/node-express-esm-loader/tests/server.test.ts
+++ b/dev-packages/e2e-tests/test-applications/node-express-esm-loader/tests/server.test.ts
@@ -66,7 +66,6 @@ test('Should record a transaction for route with parameters', async ({ request }
'express.name': 'query',
'express.type': 'middleware',
'http.route': '/',
- 'otel.kind': 'INTERNAL',
'sentry.origin': 'auto.http.otel.express',
'sentry.op': 'middleware.express',
},
@@ -86,7 +85,6 @@ test('Should record a transaction for route with parameters', async ({ request }
'express.name': 'expressInit',
'express.type': 'middleware',
'http.route': '/',
- 'otel.kind': 'INTERNAL',
'sentry.origin': 'auto.http.otel.express',
'sentry.op': 'middleware.express',
},
@@ -106,7 +104,6 @@ test('Should record a transaction for route with parameters', async ({ request }
'express.name': '/test-transaction/:param',
'express.type': 'request_handler',
'http.route': '/test-transaction/:param',
- 'otel.kind': 'INTERNAL',
'sentry.origin': 'auto.http.otel.express',
'sentry.op': 'request_handler.express',
},
diff --git a/dev-packages/e2e-tests/test-applications/node-express-esm-preload/tests/server.test.ts b/dev-packages/e2e-tests/test-applications/node-express-esm-preload/tests/server.test.ts
index e7b9a3faa878..9d6b10a3dd74 100644
--- a/dev-packages/e2e-tests/test-applications/node-express-esm-preload/tests/server.test.ts
+++ b/dev-packages/e2e-tests/test-applications/node-express-esm-preload/tests/server.test.ts
@@ -66,7 +66,6 @@ test('Should record a transaction for route with parameters', async ({ request }
'express.name': 'query',
'express.type': 'middleware',
'http.route': '/',
- 'otel.kind': 'INTERNAL',
'sentry.origin': 'auto.http.otel.express',
'sentry.op': 'middleware.express',
},
@@ -86,7 +85,6 @@ test('Should record a transaction for route with parameters', async ({ request }
'express.name': 'expressInit',
'express.type': 'middleware',
'http.route': '/',
- 'otel.kind': 'INTERNAL',
'sentry.origin': 'auto.http.otel.express',
'sentry.op': 'middleware.express',
},
@@ -106,7 +104,6 @@ test('Should record a transaction for route with parameters', async ({ request }
'express.name': '/test-transaction/:param',
'express.type': 'request_handler',
'http.route': '/test-transaction/:param',
- 'otel.kind': 'INTERNAL',
'sentry.origin': 'auto.http.otel.express',
'sentry.op': 'request_handler.express',
},
diff --git a/dev-packages/e2e-tests/test-applications/node-express/tests/transactions.test.ts b/dev-packages/e2e-tests/test-applications/node-express/tests/transactions.test.ts
index 03262edcf6b5..fede408d258e 100644
--- a/dev-packages/e2e-tests/test-applications/node-express/tests/transactions.test.ts
+++ b/dev-packages/e2e-tests/test-applications/node-express/tests/transactions.test.ts
@@ -65,7 +65,6 @@ test('Sends an API route transaction', async ({ baseURL }) => {
'http.route': '/',
'express.name': 'query',
'express.type': 'middleware',
- 'otel.kind': 'INTERNAL',
},
description: 'query',
op: 'middleware.express',
@@ -85,7 +84,6 @@ test('Sends an API route transaction', async ({ baseURL }) => {
'http.route': '/',
'express.name': 'expressInit',
'express.type': 'middleware',
- 'otel.kind': 'INTERNAL',
},
description: 'expressInit',
op: 'middleware.express',
@@ -105,7 +103,6 @@ test('Sends an API route transaction', async ({ baseURL }) => {
'http.route': '/test-transaction',
'express.name': '/test-transaction',
'express.type': 'request_handler',
- 'otel.kind': 'INTERNAL',
},
description: '/test-transaction',
op: 'request_handler.express',
@@ -146,7 +143,6 @@ test('Sends an API route transaction for an errored route', async ({ baseURL })
'http.route': '/',
'express.name': 'query',
'express.type': 'middleware',
- 'otel.kind': 'INTERNAL',
},
description: 'query',
op: 'middleware.express',
@@ -166,7 +162,6 @@ test('Sends an API route transaction for an errored route', async ({ baseURL })
'http.route': '/',
'express.name': 'expressInit',
'express.type': 'middleware',
- 'otel.kind': 'INTERNAL',
},
description: 'expressInit',
op: 'middleware.express',
@@ -186,7 +181,6 @@ test('Sends an API route transaction for an errored route', async ({ baseURL })
'http.route': '/test-exception/:id',
'express.name': '/test-exception/:id',
'express.type': 'request_handler',
- 'otel.kind': 'INTERNAL',
},
description: '/test-exception/:id',
op: 'request_handler.express',
diff --git a/dev-packages/e2e-tests/test-applications/node-fastify/tests/transactions.test.ts b/dev-packages/e2e-tests/test-applications/node-fastify/tests/transactions.test.ts
index fa0ace0e1e5d..c9cb53afb94c 100644
--- a/dev-packages/e2e-tests/test-applications/node-fastify/tests/transactions.test.ts
+++ b/dev-packages/e2e-tests/test-applications/node-fastify/tests/transactions.test.ts
@@ -63,7 +63,6 @@ test('Sends an API route transaction', async ({ baseURL }) => {
'plugin.name': 'fastify -> sentry-fastify-error-handler',
'fastify.type': 'middleware',
'hook.name': 'onRequest',
- 'otel.kind': 'INTERNAL',
'sentry.origin': 'auto.http.otel.fastify',
'sentry.op': 'middleware.fastify',
},
@@ -83,7 +82,6 @@ test('Sends an API route transaction', async ({ baseURL }) => {
'plugin.name': 'fastify -> sentry-fastify-error-handler',
'fastify.type': 'request_handler',
'http.route': '/test-transaction',
- 'otel.kind': 'INTERNAL',
'sentry.op': 'request_handler.fastify',
'sentry.origin': 'auto.http.otel.fastify',
},
@@ -100,7 +98,6 @@ test('Sends an API route transaction', async ({ baseURL }) => {
expect(spans).toContainEqual({
data: {
- 'otel.kind': 'INTERNAL',
'sentry.origin': 'manual',
},
description: 'test-span',
@@ -115,7 +112,6 @@ test('Sends an API route transaction', async ({ baseURL }) => {
expect(spans).toContainEqual({
data: {
- 'otel.kind': 'INTERNAL',
'sentry.origin': 'manual',
},
description: 'child-span',
diff --git a/dev-packages/e2e-tests/test-applications/node-hapi/tests/transactions.test.ts b/dev-packages/e2e-tests/test-applications/node-hapi/tests/transactions.test.ts
index 4fd0c72ffd54..c3237de74a5a 100644
--- a/dev-packages/e2e-tests/test-applications/node-hapi/tests/transactions.test.ts
+++ b/dev-packages/e2e-tests/test-applications/node-hapi/tests/transactions.test.ts
@@ -63,7 +63,6 @@ test('Sends successful transaction', async ({ baseURL }) => {
'hapi.type': 'router',
'http.method': 'GET',
'http.route': '/test-success',
- 'otel.kind': 'INTERNAL',
'sentry.op': 'router.hapi',
'sentry.origin': 'auto.http.otel.hapi',
},
@@ -81,7 +80,6 @@ test('Sends successful transaction', async ({ baseURL }) => {
// this comes from "onPreResponse"
data: {
'hapi.type': 'server.ext',
- 'otel.kind': 'INTERNAL',
'sentry.op': 'server.ext.hapi',
'sentry.origin': 'auto.http.otel.hapi',
'server.ext.type': 'onPreResponse',
diff --git a/dev-packages/e2e-tests/test-applications/node-koa/tests/transactions.test.ts b/dev-packages/e2e-tests/test-applications/node-koa/tests/transactions.test.ts
index fd57b23cb8bb..4c52c932e7b4 100644
--- a/dev-packages/e2e-tests/test-applications/node-koa/tests/transactions.test.ts
+++ b/dev-packages/e2e-tests/test-applications/node-koa/tests/transactions.test.ts
@@ -53,7 +53,6 @@ test('Sends an API route transaction', async ({ baseURL }) => {
data: {
'koa.name': '',
'koa.type': 'middleware',
- 'otel.kind': 'INTERNAL',
'sentry.origin': 'auto.http.otel.koa',
'sentry.op': 'middleware.koa',
},
@@ -72,7 +71,6 @@ test('Sends an API route transaction', async ({ baseURL }) => {
'http.route': '/test-transaction',
'koa.name': '/test-transaction',
'koa.type': 'router',
- 'otel.kind': 'INTERNAL',
'sentry.origin': 'auto.http.otel.koa',
'sentry.op': 'router.koa',
},
@@ -88,7 +86,6 @@ test('Sends an API route transaction', async ({ baseURL }) => {
},
{
data: {
- 'otel.kind': 'INTERNAL',
'sentry.origin': 'manual',
},
description: 'test-span',
@@ -102,7 +99,6 @@ test('Sends an API route transaction', async ({ baseURL }) => {
},
{
data: {
- 'otel.kind': 'INTERNAL',
'sentry.origin': 'manual',
},
description: 'child-span',
diff --git a/dev-packages/e2e-tests/test-applications/node-otel-sdk-node/tests/transactions.test.ts b/dev-packages/e2e-tests/test-applications/node-otel-sdk-node/tests/transactions.test.ts
index 39a7d27e9cb1..f7fee0559a97 100644
--- a/dev-packages/e2e-tests/test-applications/node-otel-sdk-node/tests/transactions.test.ts
+++ b/dev-packages/e2e-tests/test-applications/node-otel-sdk-node/tests/transactions.test.ts
@@ -77,7 +77,6 @@ test('Sends an API route transaction', async ({ baseURL }) => {
'http.route': '/',
'express.name': 'query',
'express.type': 'middleware',
- 'otel.kind': 'INTERNAL',
},
description: 'query',
op: 'middleware.express',
@@ -97,7 +96,6 @@ test('Sends an API route transaction', async ({ baseURL }) => {
'http.route': '/',
'express.name': 'expressInit',
'express.type': 'middleware',
- 'otel.kind': 'INTERNAL',
},
description: 'expressInit',
op: 'middleware.express',
@@ -117,7 +115,6 @@ test('Sends an API route transaction', async ({ baseURL }) => {
'http.route': '/test-transaction',
'express.name': '/test-transaction',
'express.type': 'request_handler',
- 'otel.kind': 'INTERNAL',
},
description: '/test-transaction',
op: 'request_handler.express',
@@ -158,7 +155,6 @@ test('Sends an API route transaction for an errored route', async ({ baseURL })
'http.route': '/',
'express.name': 'query',
'express.type': 'middleware',
- 'otel.kind': 'INTERNAL',
},
description: 'query',
op: 'middleware.express',
@@ -178,7 +174,6 @@ test('Sends an API route transaction for an errored route', async ({ baseURL })
'http.route': '/',
'express.name': 'expressInit',
'express.type': 'middleware',
- 'otel.kind': 'INTERNAL',
},
description: 'expressInit',
op: 'middleware.express',
@@ -198,7 +193,6 @@ test('Sends an API route transaction for an errored route', async ({ baseURL })
'http.route': '/test-exception/:id',
'express.name': '/test-exception/:id',
'express.type': 'request_handler',
- 'otel.kind': 'INTERNAL',
},
description: '/test-exception/:id',
op: 'request_handler.express',
diff --git a/dev-packages/node-integration-tests/suites/tracing/apollo-graphql/test.ts b/dev-packages/node-integration-tests/suites/tracing/apollo-graphql/test.ts
index 593ccff485fe..5bf91f7653c1 100644
--- a/dev-packages/node-integration-tests/suites/tracing/apollo-graphql/test.ts
+++ b/dev-packages/node-integration-tests/suites/tracing/apollo-graphql/test.ts
@@ -9,7 +9,6 @@ describe('GraphQL/Apollo Tests', () => {
data: {
'graphql.operation.type': 'query',
'graphql.source': '{hello}',
- 'otel.kind': 'INTERNAL',
'sentry.origin': 'auto.graphql.otel.graphql',
},
description: 'query',
@@ -31,7 +30,6 @@ describe('GraphQL/Apollo Tests', () => {
'graphql.operation.name': 'Mutation',
'graphql.operation.type': 'mutation',
'graphql.source': 'mutation Mutation($email: String) {\n login(email: $email)\n}',
- 'otel.kind': 'INTERNAL',
'sentry.origin': 'auto.graphql.otel.graphql',
},
description: 'mutation Mutation',
diff --git a/dev-packages/node-integration-tests/suites/tracing/connect/test.ts b/dev-packages/node-integration-tests/suites/tracing/connect/test.ts
index e343e6edf6fd..ad49a4e4532d 100644
--- a/dev-packages/node-integration-tests/suites/tracing/connect/test.ts
+++ b/dev-packages/node-integration-tests/suites/tracing/connect/test.ts
@@ -15,7 +15,6 @@ describe('connect auto-instrumentation', () => {
'connect.name': '/',
'connect.type': 'request_handler',
'http.route': '/',
- 'otel.kind': 'INTERNAL',
'sentry.origin': 'auto.http.otel.connect',
'sentry.op': 'request_handler.connect',
}),
diff --git a/dev-packages/node-integration-tests/suites/tracing/nestjs/test.ts b/dev-packages/node-integration-tests/suites/tracing/nestjs/test.ts
index abb9cf6f0bdd..2b42f23c857a 100644
--- a/dev-packages/node-integration-tests/suites/tracing/nestjs/test.ts
+++ b/dev-packages/node-integration-tests/suites/tracing/nestjs/test.ts
@@ -27,7 +27,6 @@ conditionalTest({ min: 16 })('nestjs auto instrumentation', () => {
'nestjs.callback': 'getHello',
'nestjs.controller': 'AppController',
'nestjs.type': 'request_context',
- 'otel.kind': 'INTERNAL',
'sentry.op': 'http',
}),
}),
diff --git a/dev-packages/node-integration-tests/suites/tracing/prisma-orm/test.ts b/dev-packages/node-integration-tests/suites/tracing/prisma-orm/test.ts
index 3c96c6d80779..dd92de5d0292 100644
--- a/dev-packages/node-integration-tests/suites/tracing/prisma-orm/test.ts
+++ b/dev-packages/node-integration-tests/suites/tracing/prisma-orm/test.ts
@@ -11,7 +11,6 @@ conditionalTest({ min: 16 })('Prisma ORM Tests', () => {
method: 'create',
model: 'User',
name: 'User.create',
- 'otel.kind': 'INTERNAL',
'sentry.origin': 'auto.db.otel.prisma',
},
description: 'prisma:client:operation',
@@ -19,7 +18,6 @@ conditionalTest({ min: 16 })('Prisma ORM Tests', () => {
}),
expect.objectContaining({
data: {
- 'otel.kind': 'INTERNAL',
'sentry.origin': 'auto.db.otel.prisma',
},
description: 'prisma:client:serialize',
@@ -27,7 +25,6 @@ conditionalTest({ min: 16 })('Prisma ORM Tests', () => {
}),
expect.objectContaining({
data: {
- 'otel.kind': 'INTERNAL',
'sentry.origin': 'auto.db.otel.prisma',
},
description: 'prisma:client:connect',
@@ -35,7 +32,6 @@ conditionalTest({ min: 16 })('Prisma ORM Tests', () => {
}),
expect.objectContaining({
data: {
- 'otel.kind': 'INTERNAL',
'sentry.origin': 'auto.db.otel.prisma',
},
description: 'prisma:engine',
@@ -44,7 +40,6 @@ conditionalTest({ min: 16 })('Prisma ORM Tests', () => {
expect.objectContaining({
data: {
'db.type': 'postgres',
- 'otel.kind': 'INTERNAL',
'sentry.origin': 'auto.db.otel.prisma',
},
description: 'prisma:engine:connection',
@@ -55,7 +50,6 @@ conditionalTest({ min: 16 })('Prisma ORM Tests', () => {
'db.statement': expect.stringContaining(
'INSERT INTO "public"."User" ("createdAt","email","name") VALUES ($1,$2,$3) RETURNING "public"."User"."id", "public"."User"."createdAt", "public"."User"."email", "public"."User"."name" /* traceparent',
),
- 'otel.kind': 'INTERNAL',
'sentry.origin': 'auto.db.otel.prisma',
'db.system': 'prisma',
'sentry.op': 'db',
@@ -67,7 +61,6 @@ conditionalTest({ min: 16 })('Prisma ORM Tests', () => {
}),
expect.objectContaining({
data: {
- 'otel.kind': 'INTERNAL',
'sentry.origin': 'auto.db.otel.prisma',
},
description: 'prisma:engine:serialize',
@@ -75,7 +68,6 @@ conditionalTest({ min: 16 })('Prisma ORM Tests', () => {
}),
expect.objectContaining({
data: {
- 'otel.kind': 'INTERNAL',
'sentry.origin': 'auto.db.otel.prisma',
},
description: 'prisma:engine:response_json_serialization',
@@ -86,7 +78,6 @@ conditionalTest({ min: 16 })('Prisma ORM Tests', () => {
method: 'findMany',
model: 'User',
name: 'User.findMany',
- 'otel.kind': 'INTERNAL',
'sentry.origin': 'auto.db.otel.prisma',
},
description: 'prisma:client:operation',
@@ -94,7 +85,6 @@ conditionalTest({ min: 16 })('Prisma ORM Tests', () => {
}),
expect.objectContaining({
data: {
- 'otel.kind': 'INTERNAL',
'sentry.origin': 'auto.db.otel.prisma',
},
description: 'prisma:client:serialize',
@@ -102,7 +92,6 @@ conditionalTest({ min: 16 })('Prisma ORM Tests', () => {
}),
expect.objectContaining({
data: {
- 'otel.kind': 'INTERNAL',
'sentry.origin': 'auto.db.otel.prisma',
},
description: 'prisma:engine',
diff --git a/packages/node/test/integration/scope.test.ts b/packages/node/test/integration/scope.test.ts
index 036ab4741fa8..1a7a899ab423 100644
--- a/packages/node/test/integration/scope.test.ts
+++ b/packages/node/test/integration/scope.test.ts
@@ -100,7 +100,6 @@ describe('Integration | Scope', () => {
contexts: expect.objectContaining({
trace: {
data: {
- 'otel.kind': 'INTERNAL',
'sentry.origin': 'manual',
'sentry.source': 'custom',
'sentry.sample_rate': 1,
diff --git a/packages/node/test/integration/transactions.test.ts b/packages/node/test/integration/transactions.test.ts
index e3c9203ddaf5..048496f363b4 100644
--- a/packages/node/test/integration/transactions.test.ts
+++ b/packages/node/test/integration/transactions.test.ts
@@ -87,7 +87,6 @@ describe('Integration | Transactions', () => {
expect(transaction.contexts?.trace).toEqual({
data: {
- 'otel.kind': 'INTERNAL',
'sentry.op': 'test op',
'sentry.origin': 'auto.test',
'sentry.source': 'task',
@@ -133,7 +132,6 @@ describe('Integration | Transactions', () => {
expect(spans).toEqual([
{
data: {
- 'otel.kind': 'INTERNAL',
'sentry.origin': 'manual',
},
description: 'inner span 1',
@@ -147,7 +145,6 @@ describe('Integration | Transactions', () => {
},
{
data: {
- 'otel.kind': 'INTERNAL',
'test.inner': 'test value',
'sentry.origin': 'manual',
},
@@ -241,7 +238,6 @@ describe('Integration | Transactions', () => {
contexts: expect.objectContaining({
trace: {
data: {
- 'otel.kind': 'INTERNAL',
'sentry.op': 'test op',
'sentry.origin': 'auto.test',
'sentry.source': 'task',
@@ -280,7 +276,6 @@ describe('Integration | Transactions', () => {
contexts: expect.objectContaining({
trace: {
data: {
- 'otel.kind': 'INTERNAL',
'sentry.op': 'test op b',
'sentry.origin': 'manual',
'sentry.source': 'custom',
@@ -386,7 +381,6 @@ describe('Integration | Transactions', () => {
contexts: expect.objectContaining({
trace: {
data: {
- 'otel.kind': 'INTERNAL',
'sentry.origin': 'manual',
'sentry.source': 'custom',
'test.outer': 'test value',
@@ -422,7 +416,6 @@ describe('Integration | Transactions', () => {
contexts: expect.objectContaining({
trace: {
data: {
- 'otel.kind': 'INTERNAL',
'sentry.origin': 'manual',
'sentry.source': 'custom',
'test.outer': 'test value b',
@@ -495,7 +488,6 @@ describe('Integration | Transactions', () => {
contexts: expect.objectContaining({
trace: {
data: {
- 'otel.kind': 'INTERNAL',
'sentry.op': 'test op',
'sentry.origin': 'auto.test',
'sentry.source': 'task',
@@ -532,7 +524,6 @@ describe('Integration | Transactions', () => {
expect(spans).toEqual([
{
data: {
- 'otel.kind': 'INTERNAL',
'sentry.origin': 'manual',
},
description: 'inner span 1',
@@ -546,7 +537,6 @@ describe('Integration | Transactions', () => {
},
{
data: {
- 'otel.kind': 'INTERNAL',
'sentry.origin': 'manual',
},
description: 'inner span 2',
diff --git a/packages/opentelemetry/src/spanExporter.ts b/packages/opentelemetry/src/spanExporter.ts
index 3064de5818aa..5714a3d93970 100644
--- a/packages/opentelemetry/src/spanExporter.ts
+++ b/packages/opentelemetry/src/spanExporter.ts
@@ -352,9 +352,11 @@ function removeSentryAttributes(data: Record): Record {
const attributes = span.attributes;
- const data: Record = {
- 'otel.kind': SpanKind[span.kind],
- };
+ const data: Record = {};
+
+ if (span.kind !== SpanKind.INTERNAL) {
+ data['otel.kind'] = SpanKind[span.kind];
+ }
if (attributes[SEMATTRS_HTTP_STATUS_CODE]) {
const statusCode = attributes[SEMATTRS_HTTP_STATUS_CODE] as string;
diff --git a/packages/opentelemetry/test/integration/scope.test.ts b/packages/opentelemetry/test/integration/scope.test.ts
index d4cbdfa8f411..c2e3dcc86701 100644
--- a/packages/opentelemetry/test/integration/scope.test.ts
+++ b/packages/opentelemetry/test/integration/scope.test.ts
@@ -107,7 +107,6 @@ describe('Integration | Scope', () => {
contexts: expect.objectContaining({
trace: {
data: {
- 'otel.kind': 'INTERNAL',
'sentry.origin': 'manual',
'sentry.source': 'custom',
'sentry.sample_rate': 1,
diff --git a/packages/opentelemetry/test/integration/transactions.test.ts b/packages/opentelemetry/test/integration/transactions.test.ts
index 13f8c91af7b7..fe03fc8a1030 100644
--- a/packages/opentelemetry/test/integration/transactions.test.ts
+++ b/packages/opentelemetry/test/integration/transactions.test.ts
@@ -102,7 +102,6 @@ describe('Integration | Transactions', () => {
expect(transaction.contexts?.trace).toEqual({
data: {
- 'otel.kind': 'INTERNAL',
'sentry.op': 'test op',
'sentry.origin': 'auto.test',
'sentry.source': 'task',
@@ -148,7 +147,6 @@ describe('Integration | Transactions', () => {
expect(spans).toEqual([
{
data: {
- 'otel.kind': 'INTERNAL',
'sentry.origin': 'manual',
},
description: 'inner span 1',
@@ -162,7 +160,6 @@ describe('Integration | Transactions', () => {
},
{
data: {
- 'otel.kind': 'INTERNAL',
'test.inner': 'test value',
'sentry.origin': 'manual',
},
@@ -256,7 +253,6 @@ describe('Integration | Transactions', () => {
contexts: expect.objectContaining({
trace: {
data: {
- 'otel.kind': 'INTERNAL',
'sentry.op': 'test op',
'sentry.origin': 'auto.test',
'sentry.source': 'task',
@@ -295,7 +291,6 @@ describe('Integration | Transactions', () => {
contexts: expect.objectContaining({
trace: {
data: {
- 'otel.kind': 'INTERNAL',
'sentry.op': 'test op b',
'sentry.origin': 'manual',
'sentry.source': 'custom',
@@ -376,7 +371,6 @@ describe('Integration | Transactions', () => {
contexts: expect.objectContaining({
trace: {
data: {
- 'otel.kind': 'INTERNAL',
'sentry.op': 'test op',
'sentry.origin': 'auto.test',
'sentry.source': 'task',
@@ -413,7 +407,6 @@ describe('Integration | Transactions', () => {
expect(spans).toEqual([
{
data: {
- 'otel.kind': 'INTERNAL',
'sentry.origin': 'manual',
},
description: 'inner span 1',
@@ -427,7 +420,6 @@ describe('Integration | Transactions', () => {
},
{
data: {
- 'otel.kind': 'INTERNAL',
'sentry.origin': 'manual',
},
description: 'inner span 2',
diff --git a/packages/opentelemetry/test/trace.test.ts b/packages/opentelemetry/test/trace.test.ts
index 6fd4ada4dc46..d3ac52327bb6 100644
--- a/packages/opentelemetry/test/trace.test.ts
+++ b/packages/opentelemetry/test/trace.test.ts
@@ -379,7 +379,6 @@ describe('trace', () => {
'sentry.source': 'custom',
'sentry.sample_rate': 1,
'sentry.origin': 'manual',
- 'otel.kind': 'INTERNAL',
},
span_id: expect.any(String),
trace_id: expect.any(String),
@@ -403,7 +402,6 @@ describe('trace', () => {
data: {
'sentry.source': 'custom',
'sentry.origin': 'manual',
- 'otel.kind': 'INTERNAL',
'sentry.sample_rate': 1,
},
parent_span_id: innerParentSpanId,
@@ -630,7 +628,6 @@ describe('trace', () => {
'sentry.source': 'custom',
'sentry.sample_rate': 1,
'sentry.origin': 'manual',
- 'otel.kind': 'INTERNAL',
},
span_id: expect.any(String),
trace_id: expect.any(String),
@@ -654,7 +651,6 @@ describe('trace', () => {
data: {
'sentry.source': 'custom',
'sentry.origin': 'manual',
- 'otel.kind': 'INTERNAL',
'sentry.sample_rate': 1,
},
parent_span_id: innerParentSpanId,
@@ -918,7 +914,6 @@ describe('trace', () => {
'sentry.source': 'custom',
'sentry.sample_rate': 1,
'sentry.origin': 'manual',
- 'otel.kind': 'INTERNAL',
},
span_id: expect.any(String),
trace_id: expect.any(String),
@@ -942,7 +937,6 @@ describe('trace', () => {
data: {
'sentry.source': 'custom',
'sentry.origin': 'manual',
- 'otel.kind': 'INTERNAL',
'sentry.sample_rate': 1,
},
parent_span_id: innerParentSpanId,
diff --git a/packages/remix/test/integration/test/server/instrumentation-otel/action.test.ts b/packages/remix/test/integration/test/server/instrumentation-otel/action.test.ts
index a784cd3b8d9c..f883c4bfeee5 100644
--- a/packages/remix/test/integration/test/server/instrumentation-otel/action.test.ts
+++ b/packages/remix/test/integration/test/server/instrumentation-otel/action.test.ts
@@ -22,7 +22,6 @@ describe('Remix API Actions', () => {
data: {
'code.function': 'action',
'sentry.op': 'action.remix',
- 'otel.kind': 'INTERNAL',
'match.route.id': `routes/action-json-response${useV2 ? '.' : '/'}$id`,
'match.params.id': '123123',
},
@@ -31,7 +30,6 @@ describe('Remix API Actions', () => {
data: {
'code.function': 'loader',
'sentry.op': 'loader.remix',
- 'otel.kind': 'INTERNAL',
'match.route.id': `routes/action-json-response${useV2 ? '.' : '/'}$id`,
'match.params.id': '123123',
},
@@ -40,7 +38,6 @@ describe('Remix API Actions', () => {
data: {
'code.function': 'loader',
'sentry.op': 'loader.remix',
- 'otel.kind': 'INTERNAL',
'match.route.id': 'root',
'match.params.id': '123123',
},
diff --git a/packages/remix/test/integration/test/server/instrumentation-otel/loader.test.ts b/packages/remix/test/integration/test/server/instrumentation-otel/loader.test.ts
index 62e0bf78ac10..49b0fa7665fd 100644
--- a/packages/remix/test/integration/test/server/instrumentation-otel/loader.test.ts
+++ b/packages/remix/test/integration/test/server/instrumentation-otel/loader.test.ts
@@ -102,7 +102,6 @@ describe('Remix API Loaders', () => {
{
data: {
'code.function': 'loader',
- 'otel.kind': 'INTERNAL',
'sentry.op': 'loader.remix',
},
origin: 'manual',
@@ -110,7 +109,6 @@ describe('Remix API Loaders', () => {
{
data: {
'code.function': 'loader',
- 'otel.kind': 'INTERNAL',
'sentry.op': 'loader.remix',
},
origin: 'manual',
@@ -244,7 +242,6 @@ describe('Remix API Loaders', () => {
data: {
'code.function': 'loader',
'sentry.op': 'loader.remix',
- 'otel.kind': 'INTERNAL',
'match.route.id': `routes/loader-defer-response${useV2 ? '.' : '/'}$id`,
},
},
@@ -252,7 +249,6 @@ describe('Remix API Loaders', () => {
data: {
'code.function': 'loader',
'sentry.op': 'loader.remix',
- 'otel.kind': 'INTERNAL',
'match.route.id': 'root',
},
},
From 2f0422430e10cbb963807f6b2d09b7ed6ffc2511 Mon Sep 17 00:00:00 2001
From: Kevin Liu
Date: Wed, 10 Jul 2024 09:55:51 -0400
Subject: [PATCH 02/39] feat(tracing): add long animation frame tracing
(#12646)
Adds an option to trace long animation frames as per #11719. This
tracing feature is disabled by default.
Currently, this feature
https://developer.mozilla.org/en-US/docs/Web/API/Performance_API/Long_animation_frame_timing
is only supported by Chromium browsers.
Usage is opt-in:
```js
Sentry.init({
dsn: '__PUBLIC_DSN__',
integrations: [
Sentry.browserTracingIntegration({
enableLongAnimationFrame: true,
}),
],
tracesSampleRate: 1,
});
```
---
.size-limit.js | 2 +-
.../assets/script.js | 12 ++
.../long-animation-frame-disabled/init.js | 11 ++
.../template.html | 10 ++
.../long-animation-frame-disabled/test.ts | 27 +++++
.../assets/script.js | 25 ++++
.../long-animation-frame-enabled/init.js | 15 +++
.../template.html | 13 ++
.../long-animation-frame-enabled/test.ts | 112 +++++++++++++++++
.../assets/script.js | 12 ++
.../long-animation-frame-non-chromium/init.js | 11 ++
.../template.html | 10 ++
.../long-animation-frame-non-chromium/test.ts | 27 +++++
.../assets/script.js | 25 ++++
.../init.js | 15 +++
.../template.html | 13 ++
.../test.ts | 114 ++++++++++++++++++
.../assets/script.js | 12 ++
.../long-tasks-no-animation-frame/init.js | 15 +++
.../template.html | 10 ++
.../long-tasks-no-animation-frame/test.ts | 37 ++++++
packages/browser-utils/src/index.ts | 1 +
.../src/metrics/browserMetrics.ts | 54 +++++++++
.../browser-utils/src/metrics/instrument.ts | 11 ++
.../src/tracing/browserTracingIntegration.ts | 15 ++-
25 files changed, 607 insertions(+), 2 deletions(-)
create mode 100644 dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/long-animation-frame-disabled/assets/script.js
create mode 100644 dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/long-animation-frame-disabled/init.js
create mode 100644 dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/long-animation-frame-disabled/template.html
create mode 100644 dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/long-animation-frame-disabled/test.ts
create mode 100644 dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/long-animation-frame-enabled/assets/script.js
create mode 100644 dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/long-animation-frame-enabled/init.js
create mode 100644 dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/long-animation-frame-enabled/template.html
create mode 100644 dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/long-animation-frame-enabled/test.ts
create mode 100644 dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/long-animation-frame-non-chromium/assets/script.js
create mode 100644 dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/long-animation-frame-non-chromium/init.js
create mode 100644 dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/long-animation-frame-non-chromium/template.html
create mode 100644 dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/long-animation-frame-non-chromium/test.ts
create mode 100644 dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/long-tasks-and-animation-frame-enabled/assets/script.js
create mode 100644 dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/long-tasks-and-animation-frame-enabled/init.js
create mode 100644 dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/long-tasks-and-animation-frame-enabled/template.html
create mode 100644 dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/long-tasks-and-animation-frame-enabled/test.ts
create mode 100644 dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/long-tasks-no-animation-frame/assets/script.js
create mode 100644 dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/long-tasks-no-animation-frame/init.js
create mode 100644 dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/long-tasks-no-animation-frame/template.html
create mode 100644 dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/long-tasks-no-animation-frame/test.ts
diff --git a/.size-limit.js b/.size-limit.js
index 636b9c64413a..a5ce210ef737 100644
--- a/.size-limit.js
+++ b/.size-limit.js
@@ -177,7 +177,7 @@ module.exports = [
path: createCDNPath('bundle.tracing.replay.min.js'),
gzip: false,
brotli: false,
- limit: '220 KB',
+ limit: '221 KB',
},
{
name: 'CDN Bundle (incl. Tracing, Replay, Feedback) - uncompressed',
diff --git a/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/long-animation-frame-disabled/assets/script.js b/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/long-animation-frame-disabled/assets/script.js
new file mode 100644
index 000000000000..9ac3d6fb33d2
--- /dev/null
+++ b/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/long-animation-frame-disabled/assets/script.js
@@ -0,0 +1,12 @@
+(() => {
+ const startTime = Date.now();
+
+ function getElasped() {
+ const time = Date.now();
+ return time - startTime;
+ }
+
+ while (getElasped() < 101) {
+ //
+ }
+})();
diff --git a/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/long-animation-frame-disabled/init.js b/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/long-animation-frame-disabled/init.js
new file mode 100644
index 000000000000..e1b3f6b13b01
--- /dev/null
+++ b/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/long-animation-frame-disabled/init.js
@@ -0,0 +1,11 @@
+import * as Sentry from '@sentry/browser';
+
+window.Sentry = Sentry;
+
+Sentry.init({
+ dsn: 'https://public@dsn.ingest.sentry.io/1337',
+ integrations: [
+ Sentry.browserTracingIntegration({ enableLongTask: false, enableLongAnimationFrame: false, idleTimeout: 9000 }),
+ ],
+ tracesSampleRate: 1,
+});
diff --git a/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/long-animation-frame-disabled/template.html b/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/long-animation-frame-disabled/template.html
new file mode 100644
index 000000000000..4cd015b16f51
--- /dev/null
+++ b/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/long-animation-frame-disabled/template.html
@@ -0,0 +1,10 @@
+
+
+
+
+
+
+ Rendered Before Long Animation Frame
+
+
+
diff --git a/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/long-animation-frame-disabled/test.ts b/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/long-animation-frame-disabled/test.ts
new file mode 100644
index 000000000000..2527d5a67302
--- /dev/null
+++ b/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/long-animation-frame-disabled/test.ts
@@ -0,0 +1,27 @@
+import type { Route } from '@playwright/test';
+import { expect } from '@playwright/test';
+import type { Event } from '@sentry/types';
+
+import { sentryTest } from '../../../../utils/fixtures';
+import { getFirstSentryEnvelopeRequest, shouldSkipTracingTest } from '../../../../utils/helpers';
+
+sentryTest(
+ 'should not capture long animation frame when flag is disabled.',
+ async ({ browserName, getLocalTestPath, page }) => {
+ // Long animation frames only work on chrome
+ if (shouldSkipTracingTest() || browserName !== 'chromium') {
+ sentryTest.skip();
+ }
+
+ await page.route('**/path/to/script.js', (route: Route) =>
+ route.fulfill({ path: `${__dirname}/assets/script.js` }),
+ );
+
+ const url = await getLocalTestPath({ testDir: __dirname });
+
+ const eventData = await getFirstSentryEnvelopeRequest(page, url);
+ const uiSpans = eventData.spans?.filter(({ op }) => op?.startsWith('ui'));
+
+ expect(uiSpans?.length).toBe(0);
+ },
+);
diff --git a/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/long-animation-frame-enabled/assets/script.js b/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/long-animation-frame-enabled/assets/script.js
new file mode 100644
index 000000000000..10552eeb5bd5
--- /dev/null
+++ b/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/long-animation-frame-enabled/assets/script.js
@@ -0,0 +1,25 @@
+function getElapsed(startTime) {
+ const time = Date.now();
+ return time - startTime;
+}
+
+function handleClick() {
+ const startTime = Date.now();
+ while (getElapsed(startTime) < 105) {
+ //
+ }
+}
+
+function start() {
+ const startTime = Date.now();
+ while (getElapsed(startTime) < 105) {
+ //
+ }
+}
+
+// trigger 2 long-animation-frame events
+// one from the top-level and the other from an event-listener
+start();
+
+const button = document.getElementById('clickme');
+button.addEventListener('click', handleClick);
diff --git a/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/long-animation-frame-enabled/init.js b/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/long-animation-frame-enabled/init.js
new file mode 100644
index 000000000000..4be408ceab7e
--- /dev/null
+++ b/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/long-animation-frame-enabled/init.js
@@ -0,0 +1,15 @@
+import * as Sentry from '@sentry/browser';
+
+window.Sentry = Sentry;
+
+Sentry.init({
+ dsn: 'https://public@dsn.ingest.sentry.io/1337',
+ integrations: [
+ Sentry.browserTracingIntegration({
+ idleTimeout: 9000,
+ enableLongTask: false,
+ enableLongAnimationFrame: true,
+ }),
+ ],
+ tracesSampleRate: 1,
+});
diff --git a/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/long-animation-frame-enabled/template.html b/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/long-animation-frame-enabled/template.html
new file mode 100644
index 000000000000..ed02d1117097
--- /dev/null
+++ b/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/long-animation-frame-enabled/template.html
@@ -0,0 +1,13 @@
+
+
+
+
+
+
+ Rendered Before Long Animation Frame
+
+
+
+
diff --git a/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/long-animation-frame-enabled/test.ts b/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/long-animation-frame-enabled/test.ts
new file mode 100644
index 000000000000..850e75dbed1f
--- /dev/null
+++ b/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/long-animation-frame-enabled/test.ts
@@ -0,0 +1,112 @@
+import type { Route } from '@playwright/test';
+import { expect } from '@playwright/test';
+import type { Event } from '@sentry/types';
+
+import { SEMANTIC_ATTRIBUTE_SENTRY_OP, SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN } from '@sentry/browser';
+import { sentryTest } from '../../../../utils/fixtures';
+import { getFirstSentryEnvelopeRequest, shouldSkipTracingTest } from '../../../../utils/helpers';
+
+sentryTest(
+ 'should capture long animation frame for top-level script.',
+ async ({ browserName, getLocalTestPath, page }) => {
+ // Long animation frames only work on chrome
+ if (shouldSkipTracingTest() || browserName !== 'chromium') {
+ sentryTest.skip();
+ }
+
+ await page.route('**/path/to/script.js', (route: Route) =>
+ route.fulfill({ path: `${__dirname}/assets/script.js` }),
+ );
+
+ const url = await getLocalTestPath({ testDir: __dirname });
+
+ const promise = getFirstSentryEnvelopeRequest(page);
+
+ await page.goto(url);
+
+ await new Promise(resolve => setTimeout(resolve, 1000));
+
+ const eventData = await promise;
+
+ const uiSpans = eventData.spans?.filter(({ op }) => op?.startsWith('ui.long-animation-frame'));
+
+ expect(uiSpans?.length).toEqual(1);
+
+ const [topLevelUISpan] = uiSpans || [];
+ expect(topLevelUISpan).toEqual(
+ expect.objectContaining({
+ op: 'ui.long-animation-frame',
+ description: 'Main UI thread blocked',
+ parent_span_id: eventData.contexts?.trace?.span_id,
+ data: {
+ 'code.filepath': 'https://example.com/path/to/script.js',
+ 'browser.script.source_char_position': 0,
+ 'browser.script.invoker': 'https://example.com/path/to/script.js',
+ 'browser.script.invoker_type': 'classic-script',
+ [SEMANTIC_ATTRIBUTE_SENTRY_OP]: 'ui.long-animation-frame',
+ [SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: 'auto.ui.browser.metrics',
+ },
+ }),
+ );
+ const start = topLevelUISpan.start_timestamp ?? 0;
+ const end = topLevelUISpan.timestamp ?? 0;
+ const duration = end - start;
+
+ expect(duration).toBeGreaterThanOrEqual(0.1);
+ expect(duration).toBeLessThanOrEqual(0.15);
+ },
+);
+
+sentryTest(
+ 'should capture long animation frame for event listener.',
+ async ({ browserName, getLocalTestPath, page }) => {
+ // Long animation frames only work on chrome
+ if (shouldSkipTracingTest() || browserName !== 'chromium') {
+ sentryTest.skip();
+ }
+
+ await page.route('**/path/to/script.js', (route: Route) =>
+ route.fulfill({ path: `${__dirname}/assets/script.js` }),
+ );
+
+ const url = await getLocalTestPath({ testDir: __dirname });
+
+ const promise = getFirstSentryEnvelopeRequest(page);
+
+ await page.goto(url);
+
+ // trigger long animation frame function
+ await page.getByRole('button').click();
+
+ await new Promise(resolve => setTimeout(resolve, 1000));
+
+ const eventData = await promise;
+
+ const uiSpans = eventData.spans?.filter(({ op }) => op?.startsWith('ui.long-animation-frame'));
+
+ expect(uiSpans?.length).toEqual(2);
+
+ // ignore the first ui span (top-level long animation frame)
+ const [, eventListenerUISpan] = uiSpans || [];
+
+ expect(eventListenerUISpan).toEqual(
+ expect.objectContaining({
+ op: 'ui.long-animation-frame',
+ description: 'Main UI thread blocked',
+ parent_span_id: eventData.contexts?.trace?.span_id,
+ data: {
+ 'browser.script.invoker': 'BUTTON#clickme.onclick',
+ 'browser.script.invoker_type': 'event-listener',
+ [SEMANTIC_ATTRIBUTE_SENTRY_OP]: 'ui.long-animation-frame',
+ [SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: 'auto.ui.browser.metrics',
+ },
+ }),
+ );
+ const start = eventListenerUISpan.start_timestamp ?? 0;
+ const end = eventListenerUISpan.timestamp ?? 0;
+ const duration = end - start;
+
+ expect(duration).toBeGreaterThanOrEqual(0.1);
+ expect(duration).toBeLessThanOrEqual(0.15);
+ },
+);
diff --git a/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/long-animation-frame-non-chromium/assets/script.js b/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/long-animation-frame-non-chromium/assets/script.js
new file mode 100644
index 000000000000..9ac3d6fb33d2
--- /dev/null
+++ b/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/long-animation-frame-non-chromium/assets/script.js
@@ -0,0 +1,12 @@
+(() => {
+ const startTime = Date.now();
+
+ function getElasped() {
+ const time = Date.now();
+ return time - startTime;
+ }
+
+ while (getElasped() < 101) {
+ //
+ }
+})();
diff --git a/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/long-animation-frame-non-chromium/init.js b/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/long-animation-frame-non-chromium/init.js
new file mode 100644
index 000000000000..ca1bf10dcddd
--- /dev/null
+++ b/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/long-animation-frame-non-chromium/init.js
@@ -0,0 +1,11 @@
+import * as Sentry from '@sentry/browser';
+
+window.Sentry = Sentry;
+
+Sentry.init({
+ dsn: 'https://public@dsn.ingest.sentry.io/1337',
+ integrations: [
+ Sentry.browserTracingIntegration({ enableLongTask: true, enableLongAnimationFrame: true, idleTimeout: 9000 }),
+ ],
+ tracesSampleRate: 1,
+});
diff --git a/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/long-animation-frame-non-chromium/template.html b/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/long-animation-frame-non-chromium/template.html
new file mode 100644
index 000000000000..5c3a14114991
--- /dev/null
+++ b/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/long-animation-frame-non-chromium/template.html
@@ -0,0 +1,10 @@
+
+
+
+
+
+
+ Rendered Before Long Task
+
+
+
diff --git a/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/long-animation-frame-non-chromium/test.ts b/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/long-animation-frame-non-chromium/test.ts
new file mode 100644
index 000000000000..65fb6664ac82
--- /dev/null
+++ b/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/long-animation-frame-non-chromium/test.ts
@@ -0,0 +1,27 @@
+import type { Route } from '@playwright/test';
+import { expect } from '@playwright/test';
+import type { Event } from '@sentry/types';
+
+import { sentryTest } from '../../../../utils/fixtures';
+import { getFirstSentryEnvelopeRequest, shouldSkipTracingTest } from '../../../../utils/helpers';
+
+sentryTest(
+ 'should not capture long animation frame or long task when browser is non-chromium',
+ async ({ browserName, getLocalTestPath, page }) => {
+ // Only test non-chromium browsers
+ if (shouldSkipTracingTest() || browserName === 'chromium') {
+ sentryTest.skip();
+ }
+
+ await page.route('**/path/to/script.js', (route: Route) =>
+ route.fulfill({ path: `${__dirname}/assets/script.js` }),
+ );
+
+ const url = await getLocalTestPath({ testDir: __dirname });
+
+ const eventData = await getFirstSentryEnvelopeRequest(page, url);
+ const uiSpans = eventData.spans?.filter(({ op }) => op?.startsWith('ui'));
+
+ expect(uiSpans?.length).toBe(0);
+ },
+);
diff --git a/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/long-tasks-and-animation-frame-enabled/assets/script.js b/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/long-tasks-and-animation-frame-enabled/assets/script.js
new file mode 100644
index 000000000000..10552eeb5bd5
--- /dev/null
+++ b/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/long-tasks-and-animation-frame-enabled/assets/script.js
@@ -0,0 +1,25 @@
+function getElapsed(startTime) {
+ const time = Date.now();
+ return time - startTime;
+}
+
+function handleClick() {
+ const startTime = Date.now();
+ while (getElapsed(startTime) < 105) {
+ //
+ }
+}
+
+function start() {
+ const startTime = Date.now();
+ while (getElapsed(startTime) < 105) {
+ //
+ }
+}
+
+// trigger 2 long-animation-frame events
+// one from the top-level and the other from an event-listener
+start();
+
+const button = document.getElementById('clickme');
+button.addEventListener('click', handleClick);
diff --git a/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/long-tasks-and-animation-frame-enabled/init.js b/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/long-tasks-and-animation-frame-enabled/init.js
new file mode 100644
index 000000000000..d81b8932803c
--- /dev/null
+++ b/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/long-tasks-and-animation-frame-enabled/init.js
@@ -0,0 +1,15 @@
+import * as Sentry from '@sentry/browser';
+
+window.Sentry = Sentry;
+
+Sentry.init({
+ dsn: 'https://public@dsn.ingest.sentry.io/1337',
+ integrations: [
+ Sentry.browserTracingIntegration({
+ idleTimeout: 9000,
+ enableLongTask: true,
+ enableLongAnimationFrame: true,
+ }),
+ ],
+ tracesSampleRate: 1,
+});
diff --git a/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/long-tasks-and-animation-frame-enabled/template.html b/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/long-tasks-and-animation-frame-enabled/template.html
new file mode 100644
index 000000000000..ed02d1117097
--- /dev/null
+++ b/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/long-tasks-and-animation-frame-enabled/template.html
@@ -0,0 +1,13 @@
+
+
+
+
+
+
+ Rendered Before Long Animation Frame
+
+
+
+
diff --git a/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/long-tasks-and-animation-frame-enabled/test.ts b/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/long-tasks-and-animation-frame-enabled/test.ts
new file mode 100644
index 000000000000..1949e44bd398
--- /dev/null
+++ b/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/long-tasks-and-animation-frame-enabled/test.ts
@@ -0,0 +1,114 @@
+import type { Route } from '@playwright/test';
+import { expect } from '@playwright/test';
+import type { Event } from '@sentry/types';
+
+import { SEMANTIC_ATTRIBUTE_SENTRY_OP, SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN } from '@sentry/browser';
+import { sentryTest } from '../../../../utils/fixtures';
+import { getFirstSentryEnvelopeRequest, shouldSkipTracingTest } from '../../../../utils/helpers';
+
+sentryTest(
+ 'should capture long animation frame for top-level script.',
+ async ({ browserName, getLocalTestPath, page }) => {
+ // Long animation frames only work on chrome
+ if (shouldSkipTracingTest() || browserName !== 'chromium') {
+ sentryTest.skip();
+ }
+
+ // Long animation frame should take priority over long tasks
+
+ await page.route('**/path/to/script.js', (route: Route) =>
+ route.fulfill({ path: `${__dirname}/assets/script.js` }),
+ );
+
+ const url = await getLocalTestPath({ testDir: __dirname });
+
+ const promise = getFirstSentryEnvelopeRequest(page);
+
+ await page.goto(url);
+
+ await new Promise(resolve => setTimeout(resolve, 1000));
+
+ const eventData = await promise;
+
+ const uiSpans = eventData.spans?.filter(({ op }) => op?.startsWith('ui.long-animation-frame'));
+
+ expect(uiSpans?.length).toEqual(1);
+
+ const [topLevelUISpan] = uiSpans || [];
+ expect(topLevelUISpan).toEqual(
+ expect.objectContaining({
+ op: 'ui.long-animation-frame',
+ description: 'Main UI thread blocked',
+ parent_span_id: eventData.contexts?.trace?.span_id,
+ data: {
+ 'code.filepath': 'https://example.com/path/to/script.js',
+ 'browser.script.source_char_position': 0,
+ 'browser.script.invoker': 'https://example.com/path/to/script.js',
+ 'browser.script.invoker_type': 'classic-script',
+ [SEMANTIC_ATTRIBUTE_SENTRY_OP]: 'ui.long-animation-frame',
+ [SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: 'auto.ui.browser.metrics',
+ },
+ }),
+ );
+ const start = topLevelUISpan.start_timestamp ?? 0;
+ const end = topLevelUISpan.timestamp ?? 0;
+ const duration = end - start;
+
+ expect(duration).toBeGreaterThanOrEqual(0.1);
+ expect(duration).toBeLessThanOrEqual(0.15);
+ },
+);
+
+sentryTest(
+ 'should capture long animation frame for event listener.',
+ async ({ browserName, getLocalTestPath, page }) => {
+ // Long animation frames only work on chrome
+ if (shouldSkipTracingTest() || browserName !== 'chromium') {
+ sentryTest.skip();
+ }
+
+ await page.route('**/path/to/script.js', (route: Route) =>
+ route.fulfill({ path: `${__dirname}/assets/script.js` }),
+ );
+
+ const url = await getLocalTestPath({ testDir: __dirname });
+
+ const promise = getFirstSentryEnvelopeRequest(page);
+
+ await page.goto(url);
+
+ // trigger long animation frame function
+ await page.getByRole('button').click();
+
+ await new Promise(resolve => setTimeout(resolve, 1000));
+
+ const eventData = await promise;
+
+ const uiSpans = eventData.spans?.filter(({ op }) => op?.startsWith('ui.long-animation-frame'));
+
+ expect(uiSpans?.length).toEqual(2);
+
+ // ignore the first ui span (top-level long animation frame)
+ const [, eventListenerUISpan] = uiSpans || [];
+
+ expect(eventListenerUISpan).toEqual(
+ expect.objectContaining({
+ op: 'ui.long-animation-frame',
+ description: 'Main UI thread blocked',
+ parent_span_id: eventData.contexts?.trace?.span_id,
+ data: {
+ 'browser.script.invoker': 'BUTTON#clickme.onclick',
+ 'browser.script.invoker_type': 'event-listener',
+ [SEMANTIC_ATTRIBUTE_SENTRY_OP]: 'ui.long-animation-frame',
+ [SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: 'auto.ui.browser.metrics',
+ },
+ }),
+ );
+ const start = eventListenerUISpan.start_timestamp ?? 0;
+ const end = eventListenerUISpan.timestamp ?? 0;
+ const duration = end - start;
+
+ expect(duration).toBeGreaterThanOrEqual(0.1);
+ expect(duration).toBeLessThanOrEqual(0.15);
+ },
+);
diff --git a/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/long-tasks-no-animation-frame/assets/script.js b/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/long-tasks-no-animation-frame/assets/script.js
new file mode 100644
index 000000000000..5a2aef02028d
--- /dev/null
+++ b/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/long-tasks-no-animation-frame/assets/script.js
@@ -0,0 +1,12 @@
+(() => {
+ const startTime = Date.now();
+
+ function getElasped() {
+ const time = Date.now();
+ return time - startTime;
+ }
+
+ while (getElasped() < 105) {
+ //
+ }
+})();
diff --git a/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/long-tasks-no-animation-frame/init.js b/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/long-tasks-no-animation-frame/init.js
new file mode 100644
index 000000000000..0e35db50764f
--- /dev/null
+++ b/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/long-tasks-no-animation-frame/init.js
@@ -0,0 +1,15 @@
+import * as Sentry from '@sentry/browser';
+
+window.Sentry = Sentry;
+
+Sentry.init({
+ dsn: 'https://public@dsn.ingest.sentry.io/1337',
+ integrations: [
+ Sentry.browserTracingIntegration({
+ idleTimeout: 9000,
+ enableLongTask: true,
+ enableLongAnimationFrame: false,
+ }),
+ ],
+ tracesSampleRate: 1,
+});
diff --git a/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/long-tasks-no-animation-frame/template.html b/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/long-tasks-no-animation-frame/template.html
new file mode 100644
index 000000000000..5c3a14114991
--- /dev/null
+++ b/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/long-tasks-no-animation-frame/template.html
@@ -0,0 +1,10 @@
+
+
+
+
+
+
+ Rendered Before Long Task
+
+
+
diff --git a/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/long-tasks-no-animation-frame/test.ts b/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/long-tasks-no-animation-frame/test.ts
new file mode 100644
index 000000000000..6189758c0340
--- /dev/null
+++ b/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/long-tasks-no-animation-frame/test.ts
@@ -0,0 +1,37 @@
+import type { Route } from '@playwright/test';
+import { expect } from '@playwright/test';
+import type { Event } from '@sentry/types';
+
+import { sentryTest } from '../../../../utils/fixtures';
+import { getFirstSentryEnvelopeRequest, shouldSkipTracingTest } from '../../../../utils/helpers';
+
+sentryTest('should capture long task.', async ({ browserName, getLocalTestPath, page }) => {
+ // Long tasks only work on chrome
+ if (shouldSkipTracingTest() || browserName !== 'chromium') {
+ sentryTest.skip();
+ }
+
+ await page.route('**/path/to/script.js', (route: Route) => route.fulfill({ path: `${__dirname}/assets/script.js` }));
+
+ const url = await getLocalTestPath({ testDir: __dirname });
+
+ const eventData = await getFirstSentryEnvelopeRequest(page, url);
+ const uiSpans = eventData.spans?.filter(({ op }) => op?.startsWith('ui'));
+
+ expect(uiSpans?.length).toBeGreaterThan(0);
+
+ const [firstUISpan] = uiSpans || [];
+ expect(firstUISpan).toEqual(
+ expect.objectContaining({
+ op: 'ui.long-task',
+ description: 'Main UI thread blocked',
+ parent_span_id: eventData.contexts?.trace?.span_id,
+ }),
+ );
+ const start = firstUISpan.start_timestamp ?? 0;
+ const end = firstUISpan.timestamp ?? 0;
+ const duration = end - start;
+
+ expect(duration).toBeGreaterThanOrEqual(0.1);
+ expect(duration).toBeLessThanOrEqual(0.15);
+});
diff --git a/packages/browser-utils/src/index.ts b/packages/browser-utils/src/index.ts
index f59ccbf8da8f..c71b2d70e31d 100644
--- a/packages/browser-utils/src/index.ts
+++ b/packages/browser-utils/src/index.ts
@@ -11,6 +11,7 @@ export {
addPerformanceEntries,
startTrackingInteractions,
startTrackingLongTasks,
+ startTrackingLongAnimationFrames,
startTrackingWebVitals,
startTrackingINP,
registerInpInteractionListener,
diff --git a/packages/browser-utils/src/metrics/browserMetrics.ts b/packages/browser-utils/src/metrics/browserMetrics.ts
index 4e473e42ea47..cb48c2e8b675 100644
--- a/packages/browser-utils/src/metrics/browserMetrics.ts
+++ b/packages/browser-utils/src/metrics/browserMetrics.ts
@@ -8,6 +8,7 @@ import { spanToJSON } from '@sentry/core';
import { DEBUG_BUILD } from '../debug-build';
import { WINDOW } from '../types';
import {
+ type PerformanceLongAnimationFrameTiming,
addClsInstrumentationHandler,
addFidInstrumentationHandler,
addLcpInstrumentationHandler,
@@ -120,6 +121,59 @@ export function startTrackingLongTasks(): void {
});
}
+/**
+ * Start tracking long animation frames.
+ */
+export function startTrackingLongAnimationFrames(): void {
+ // NOTE: the current web-vitals version (3.5.2) does not support long-animation-frame, so
+ // we directly observe `long-animation-frame` events instead of through the web-vitals
+ // `observe` helper function.
+ const observer = new PerformanceObserver(list => {
+ for (const entry of list.getEntries() as PerformanceLongAnimationFrameTiming[]) {
+ if (!getActiveSpan()) {
+ return;
+ }
+ if (!entry.scripts[0]) {
+ return;
+ }
+
+ const startTime = msToSec((browserPerformanceTimeOrigin as number) + entry.startTime);
+ const duration = msToSec(entry.duration);
+
+ const attributes: SpanAttributes = {
+ [SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: 'auto.ui.browser.metrics',
+ };
+ const initialScript = entry.scripts[0];
+ if (initialScript) {
+ const { invoker, invokerType, sourceURL, sourceFunctionName, sourceCharPosition } = initialScript;
+ attributes['browser.script.invoker'] = invoker;
+ attributes['browser.script.invoker_type'] = invokerType;
+ if (sourceURL) {
+ attributes['code.filepath'] = sourceURL;
+ }
+ if (sourceFunctionName) {
+ attributes['code.function'] = sourceFunctionName;
+ }
+ if (sourceCharPosition !== -1) {
+ attributes['browser.script.source_char_position'] = sourceCharPosition;
+ }
+ }
+
+ const span = startInactiveSpan({
+ name: 'Main UI thread blocked',
+ op: 'ui.long-animation-frame',
+ startTime,
+ attributes,
+ });
+ if (span) {
+ span.end(startTime + duration);
+ }
+ }
+ });
+
+ observer.observe({ type: 'long-animation-frame', buffered: true });
+}
+
/**
* Start tracking interaction events.
*/
diff --git a/packages/browser-utils/src/metrics/instrument.ts b/packages/browser-utils/src/metrics/instrument.ts
index e22a345e3116..39292fb19b83 100644
--- a/packages/browser-utils/src/metrics/instrument.ts
+++ b/packages/browser-utils/src/metrics/instrument.ts
@@ -36,6 +36,17 @@ interface PerformanceEventTiming extends PerformanceEntry {
interactionId?: number;
}
+interface PerformanceScriptTiming extends PerformanceEntry {
+ sourceURL: string;
+ sourceFunctionName: string;
+ sourceCharPosition: number;
+ invoker: string;
+ invokerType: string;
+}
+export interface PerformanceLongAnimationFrameTiming extends PerformanceEntry {
+ scripts: PerformanceScriptTiming[];
+}
+
interface Metric {
/**
* The name of the metric (in acronym form).
diff --git a/packages/browser/src/tracing/browserTracingIntegration.ts b/packages/browser/src/tracing/browserTracingIntegration.ts
index c058b1930928..0423831219e2 100644
--- a/packages/browser/src/tracing/browserTracingIntegration.ts
+++ b/packages/browser/src/tracing/browserTracingIntegration.ts
@@ -5,6 +5,7 @@ import {
registerInpInteractionListener,
startTrackingINP,
startTrackingInteractions,
+ startTrackingLongAnimationFrames,
startTrackingLongTasks,
startTrackingWebVitals,
} from '@sentry-internal/browser-utils';
@@ -102,6 +103,13 @@ export interface BrowserTracingOptions {
*/
enableLongTask: boolean;
+ /**
+ * If true, Sentry will capture long animation frames and add them to the corresponding transaction.
+ *
+ * Default: false
+ */
+ enableLongAnimationFrame: boolean;
+
/**
* If true, Sentry will capture first input delay and add it to the corresponding transaction.
*
@@ -160,6 +168,7 @@ const DEFAULT_BROWSER_TRACING_OPTIONS: BrowserTracingOptions = {
instrumentPageLoad: true,
markBackgroundSpan: true,
enableLongTask: true,
+ enableLongAnimationFrame: false,
enableInp: true,
_experiments: {},
...defaultRequestInstrumentationOptions,
@@ -180,6 +189,7 @@ export const browserTracingIntegration = ((_options: Partial
Date: Wed, 10 Jul 2024 10:06:00 -0400
Subject: [PATCH 03/39] ref(typescript): Remove dom types from main tsconfig
(#12836)
Remove `dom` types from `packages/typescript/tsconfig.json` and add it
to the respective packages that need them.
This is important because we should only be adding `dom` types to
packages that will be used in the browser.
---
dev-packages/node-integration-tests/suites/tsconfig.json | 6 ++++++
dev-packages/node-integration-tests/tsconfig.json | 3 +++
dev-packages/node-integration-tests/tsconfig.test.json | 3 +++
packages/angular/tsconfig.ngc.json | 2 +-
packages/browser-utils/tsconfig.json | 2 +-
packages/browser/tsconfig.json | 2 +-
packages/core/tsconfig.test.json | 1 +
packages/ember/tsconfig.json | 1 +
packages/gatsby/tsconfig.json | 1 +
packages/react/tsconfig.json | 1 +
packages/remix/tsconfig.test.json | 1 +
packages/replay-canvas/tsconfig.json | 1 +
packages/replay-canvas/tsconfig.test.json | 1 +
packages/replay-internal/tsconfig.json | 1 +
packages/solid/tsconfig.json | 4 +++-
packages/svelte/tsconfig.json | 4 +++-
packages/typescript/tsconfig.json | 2 +-
packages/vercel-edge/tsconfig.json | 5 ++++-
packages/vue/tsconfig.json | 2 +-
packages/wasm/tsconfig.json | 2 +-
20 files changed, 36 insertions(+), 9 deletions(-)
diff --git a/dev-packages/node-integration-tests/suites/tsconfig.json b/dev-packages/node-integration-tests/suites/tsconfig.json
index 120c3aff3716..c8f7f2d29f65 100644
--- a/dev-packages/node-integration-tests/suites/tsconfig.json
+++ b/dev-packages/node-integration-tests/suites/tsconfig.json
@@ -1,3 +1,9 @@
{
"extends": "../tsconfig.test.json",
+
+ "compilerOptions": {
+ // Although this seems wrong to include `DOM` here, it's necessary to make
+ // global fetch available in tests in lower Node versions.
+ "lib": ["DOM", "ES2018"],
+ }
}
diff --git a/dev-packages/node-integration-tests/tsconfig.json b/dev-packages/node-integration-tests/tsconfig.json
index 92db70d5ca09..9ef29d983a08 100644
--- a/dev-packages/node-integration-tests/tsconfig.json
+++ b/dev-packages/node-integration-tests/tsconfig.json
@@ -4,6 +4,9 @@
"include": ["utils/**/*.ts", "src/**/*.ts"],
"compilerOptions": {
+ // Although this seems wrong to include `DOM` here, it's necessary to make
+ // global fetch available in tests in lower Node versions.
+ "lib": ["DOM", "ES2018"],
// package-specific options
"esModuleInterop": true,
"types": ["node", "jest"]
diff --git a/dev-packages/node-integration-tests/tsconfig.test.json b/dev-packages/node-integration-tests/tsconfig.test.json
index 5a37b90c4fe2..3bd3452a3ead 100644
--- a/dev-packages/node-integration-tests/tsconfig.test.json
+++ b/dev-packages/node-integration-tests/tsconfig.test.json
@@ -4,6 +4,9 @@
"include": ["suites/**/*.ts"],
"compilerOptions": {
+ // Although this seems wrong to include `DOM` here, it's necessary to make
+ // global fetch available in tests in lower Node versions.
+ "lib": ["DOM", "ES2018"],
// should include all types from `./tsconfig.json` plus types for all test frameworks used
"types": ["node", "jest"]
diff --git a/packages/angular/tsconfig.ngc.json b/packages/angular/tsconfig.ngc.json
index e915bd8cc32c..df29c7ffdfee 100644
--- a/packages/angular/tsconfig.ngc.json
+++ b/packages/angular/tsconfig.ngc.json
@@ -7,7 +7,7 @@
"compilerOptions": {
"target": "es2018",
"declarationMap": false,
- "lib": ["dom", "es2018"],
+ "lib": ["DOM", "ES2018"],
"baseUrl": "./"
},
"angularCompilerOptions": {
diff --git a/packages/browser-utils/tsconfig.json b/packages/browser-utils/tsconfig.json
index bf45a09f2d71..6d1756d12826 100644
--- a/packages/browser-utils/tsconfig.json
+++ b/packages/browser-utils/tsconfig.json
@@ -4,6 +4,6 @@
"include": ["src/**/*"],
"compilerOptions": {
- // package-specific options
+ "lib": ["DOM", "ES2018"],
}
}
diff --git a/packages/browser/tsconfig.json b/packages/browser/tsconfig.json
index f88d8939acf7..6b204e508047 100644
--- a/packages/browser/tsconfig.json
+++ b/packages/browser/tsconfig.json
@@ -4,6 +4,6 @@
"include": ["src/**/*", "test/loader.js"],
"compilerOptions": {
- // package-specific options
+ "lib": ["DOM", "ES2018"],
}
}
diff --git a/packages/core/tsconfig.test.json b/packages/core/tsconfig.test.json
index 87f6afa06b86..6fde53bec436 100644
--- a/packages/core/tsconfig.test.json
+++ b/packages/core/tsconfig.test.json
@@ -4,6 +4,7 @@
"include": ["test/**/*"],
"compilerOptions": {
+ "lib": ["DOM", "ES2018"],
// should include all types from `./tsconfig.json` plus types for all test frameworks used
"types": ["node", "jest"]
diff --git a/packages/ember/tsconfig.json b/packages/ember/tsconfig.json
index 95bb38c78628..584a13e19669 100644
--- a/packages/ember/tsconfig.json
+++ b/packages/ember/tsconfig.json
@@ -2,6 +2,7 @@
"extends": "../../tsconfig.json",
"compilerOptions": {
"target": "es2022",
+ "lib": ["DOM", "ES2022"],
"allowJs": true,
"moduleResolution": "node",
"allowSyntheticDefaultImports": true,
diff --git a/packages/gatsby/tsconfig.json b/packages/gatsby/tsconfig.json
index b2c40b91a630..77d5f63b9345 100644
--- a/packages/gatsby/tsconfig.json
+++ b/packages/gatsby/tsconfig.json
@@ -4,6 +4,7 @@
"include": ["src/**/*"],
"compilerOptions": {
+ "lib": ["DOM", "ES2018"],
// package-specific options
"jsx": "react"
}
diff --git a/packages/react/tsconfig.json b/packages/react/tsconfig.json
index f074f990a911..67260f799e86 100644
--- a/packages/react/tsconfig.json
+++ b/packages/react/tsconfig.json
@@ -4,6 +4,7 @@
"include": ["src/**/*"],
"compilerOptions": {
+ "lib": ["DOM", "ES2018"],
// package-specific options
"esModuleInterop": true,
"jsx": "react"
diff --git a/packages/remix/tsconfig.test.json b/packages/remix/tsconfig.test.json
index ffcc2b26016c..3d6564a39ff5 100644
--- a/packages/remix/tsconfig.test.json
+++ b/packages/remix/tsconfig.test.json
@@ -4,6 +4,7 @@
"include": ["test/**/*", "vitest.config.ts"],
"compilerOptions": {
+ "lib": ["DOM", "ES2018"],
"types": ["node", "jest"],
"esModuleInterop": true
}
diff --git a/packages/replay-canvas/tsconfig.json b/packages/replay-canvas/tsconfig.json
index f8f54556da93..cd1b8207ea06 100644
--- a/packages/replay-canvas/tsconfig.json
+++ b/packages/replay-canvas/tsconfig.json
@@ -1,6 +1,7 @@
{
"extends": "../../tsconfig.json",
"compilerOptions": {
+ "lib": ["DOM", "ES2018"],
"module": "esnext"
},
"include": ["src/**/*.ts"]
diff --git a/packages/replay-canvas/tsconfig.test.json b/packages/replay-canvas/tsconfig.test.json
index ad87caa06c48..3995d3e18e59 100644
--- a/packages/replay-canvas/tsconfig.test.json
+++ b/packages/replay-canvas/tsconfig.test.json
@@ -4,6 +4,7 @@
"include": ["test/**/*.ts", "jest.config.ts", "jest.setup.ts"],
"compilerOptions": {
+ "lib": ["DOM", "ES2018"],
"types": ["node", "jest"],
"esModuleInterop": true,
"allowJs": true,
diff --git a/packages/replay-internal/tsconfig.json b/packages/replay-internal/tsconfig.json
index f8f54556da93..cd1b8207ea06 100644
--- a/packages/replay-internal/tsconfig.json
+++ b/packages/replay-internal/tsconfig.json
@@ -1,6 +1,7 @@
{
"extends": "../../tsconfig.json",
"compilerOptions": {
+ "lib": ["DOM", "ES2018"],
"module": "esnext"
},
"include": ["src/**/*.ts"]
diff --git a/packages/solid/tsconfig.json b/packages/solid/tsconfig.json
index b0eb9ecb6476..6d1756d12826 100644
--- a/packages/solid/tsconfig.json
+++ b/packages/solid/tsconfig.json
@@ -3,5 +3,7 @@
"include": ["src/**/*"],
- "compilerOptions": {}
+ "compilerOptions": {
+ "lib": ["DOM", "ES2018"],
+ }
}
diff --git a/packages/svelte/tsconfig.json b/packages/svelte/tsconfig.json
index b0eb9ecb6476..6d1756d12826 100644
--- a/packages/svelte/tsconfig.json
+++ b/packages/svelte/tsconfig.json
@@ -3,5 +3,7 @@
"include": ["src/**/*"],
- "compilerOptions": {}
+ "compilerOptions": {
+ "lib": ["DOM", "ES2018"],
+ }
}
diff --git a/packages/typescript/tsconfig.json b/packages/typescript/tsconfig.json
index bee7b140cf96..4281e8d08380 100644
--- a/packages/typescript/tsconfig.json
+++ b/packages/typescript/tsconfig.json
@@ -7,7 +7,7 @@
"importHelpers": true,
"inlineSources": true,
"isolatedModules": true,
- "lib": ["es2018", "dom"],
+ "lib": ["ES2018"],
"moduleResolution": "node",
"noErrorTruncation": true,
"noFallthroughCasesInSwitch": true,
diff --git a/packages/vercel-edge/tsconfig.json b/packages/vercel-edge/tsconfig.json
index f288bd1b84e2..fc8caeb42e65 100644
--- a/packages/vercel-edge/tsconfig.json
+++ b/packages/vercel-edge/tsconfig.json
@@ -4,7 +4,10 @@
"include": ["src/**/*"],
"compilerOptions": {
- // package-specific options
+ // Note: using `dom` here is inaccurate for the vercel-edge runtime, but needed
+ // because @edge-runtime/types does not type things like fetch or RequestInit
+ // ref: https://github.com/vercel/edge-runtime/issues/506
+ "lib": ["DOM", "ES2018"],
"types": ["@edge-runtime/types"]
}
}
diff --git a/packages/vue/tsconfig.json b/packages/vue/tsconfig.json
index bf45a09f2d71..6d1756d12826 100644
--- a/packages/vue/tsconfig.json
+++ b/packages/vue/tsconfig.json
@@ -4,6 +4,6 @@
"include": ["src/**/*"],
"compilerOptions": {
- // package-specific options
+ "lib": ["DOM", "ES2018"],
}
}
diff --git a/packages/wasm/tsconfig.json b/packages/wasm/tsconfig.json
index bf45a09f2d71..6d1756d12826 100644
--- a/packages/wasm/tsconfig.json
+++ b/packages/wasm/tsconfig.json
@@ -4,6 +4,6 @@
"include": ["src/**/*"],
"compilerOptions": {
- // package-specific options
+ "lib": ["DOM", "ES2018"],
}
}
From 6ba59c508f6467a23ddf4607d47c04bf64af74b6 Mon Sep 17 00:00:00 2001
From: Sigrid Huemer <32902192+s1gr1d@users.noreply.github.com>
Date: Wed, 10 Jul 2024 17:06:10 +0200
Subject: [PATCH 04/39] docs(nuxt): Add additional information in readme
(#12850)
Co-authored-by: Francesco Novy
---
packages/nuxt/README.md | 50 ++++++++++++++++++++++++++++++++++++-----
1 file changed, 45 insertions(+), 5 deletions(-)
diff --git a/packages/nuxt/README.md b/packages/nuxt/README.md
index 163d92836897..9ce6e908a793 100644
--- a/packages/nuxt/README.md
+++ b/packages/nuxt/README.md
@@ -28,18 +28,30 @@ The minimum supported version of Nuxt is `3.0.0`.
This package is a wrapper around `@sentry/node` for the server and `@sentry/vue` for the client side, with added
functionality related to Nuxt.
-What is working:
+**What is working:**
- Error Reporting
+ - Vue
+ - Node
+ - Nitro
-What is partly working:
+**What is partly working:**
- Tracing by setting `tracesSampleRate`
+ - UI (Vue) traces
+ - HTTP (Node) traces
-What is not yet(!) included:
+**What is not yet(!) included:**
- Source Maps
-- Connected Traces
+- Nuxt-specific traces and connecting frontend & backend traces
+
+**Known Issues:**
+
+- When adding `sentry.server.config.(ts/js)`, you get this error: "Failed to register ESM hook", but the application
+ will still work
+- When initializing Sentry on the server with `instrument.server.(js|ts)`, you get an `'import-in-the-middle'` error,
+ and the application won't work
## Automatic Setup
@@ -96,10 +108,38 @@ Add a `sentry.server.config.(js|ts)` file to the root of your project:
import * as Sentry from '@sentry/nuxt';
Sentry.init({
- dsn: env.DSN,
+ dsn: process.env.DSN,
});
```
+**Alternative Setup (ESM-compatible)**
+
+This setup makes sure Sentry is imported on the server before any other imports. As of now, this however leads to an
+import-in-the-middle error ([related reproduction](https://github.com/getsentry/sentry-javascript-examples/pull/38)).
+
+Add a `instrument.server.(js|ts)` file to your `public` folder:
+
+```javascript
+import * as Sentry from '@sentry/nuxt';
+
+// Only run `init` when DSN is available
+if (process.env.SENTRY_DSN) {
+ Sentry.init({
+ dsn: process.env.DSN,
+ });
+}
+```
+
+Add an import flag to the node options, so the file loads before any other imports:
+
+```json
+{
+ "scripts": {
+ "preview": "NODE_OPTIONS='--import ./public/instrument.server.mjs' nuxt preview"
+ }
+}
+```
+
### 5. Vite Setup
todo: add vite setup
From 354977792e78789fc2823a169b95cd11e55b4c35 Mon Sep 17 00:00:00 2001
From: Sigrid Huemer <32902192+s1gr1d@users.noreply.github.com>
Date: Wed, 10 Jul 2024 18:13:33 +0200
Subject: [PATCH 05/39] docs(nuxt): Fix instrument filename in readme (#12853)
addition to https://github.com/getsentry/sentry-javascript/pull/12850
---
packages/nuxt/README.md | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/packages/nuxt/README.md b/packages/nuxt/README.md
index 9ce6e908a793..c75fa334b8a5 100644
--- a/packages/nuxt/README.md
+++ b/packages/nuxt/README.md
@@ -117,7 +117,7 @@ Sentry.init({
This setup makes sure Sentry is imported on the server before any other imports. As of now, this however leads to an
import-in-the-middle error ([related reproduction](https://github.com/getsentry/sentry-javascript-examples/pull/38)).
-Add a `instrument.server.(js|ts)` file to your `public` folder:
+Add an `instrument.server.mjs` file to your `public` folder:
```javascript
import * as Sentry from '@sentry/nuxt';
From efcd9f314262e4f5c04637f6ef1368d7af472f9e Mon Sep 17 00:00:00 2001
From: Abhijeet Prasad
Date: Thu, 11 Jul 2024 03:14:44 -0400
Subject: [PATCH 06/39] chore: Remove unused codecov package (#12854)
Removes the usage of the `codecov` package as we use the v4 version of
the codecov github action which uses the CLI directly.
Helps reduce the dependency count!
---
package.json | 2 -
yarn.lock | 124 ++++++++++++++++++++++++---------------------------
2 files changed, 58 insertions(+), 68 deletions(-)
diff --git a/package.json b/package.json
index a77d4f3a4202..549053f23b9a 100644
--- a/package.json
+++ b/package.json
@@ -19,7 +19,6 @@
"clean:deps": "lerna clean --yes && rm -rf node_modules && yarn",
"clean:tarballs": "rimraf **/*.tgz",
"clean:all": "run-s clean:build clean:tarballs clean:caches clean:deps",
- "codecov": "codecov",
"fix": "run-s fix:biome fix:prettier fix:lerna",
"fix:lerna": "lerna run fix",
"fix:biome": "biome check --apply .",
@@ -109,7 +108,6 @@
"@types/jsdom": "^21.1.6",
"@types/node": "^14.18.0",
"@vitest/coverage-v8": "^1.6.0",
- "codecov": "^3.6.5",
"deepmerge": "^4.2.2",
"downlevel-dts": "~0.11.0",
"eslint": "7.32.0",
diff --git a/yarn.lock b/yarn.lock
index a423fa79953f..a31caa49a1ac 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -9454,7 +9454,17 @@
dependencies:
"@types/unist" "*"
-"@types/history-4@npm:@types/history@4.7.8", "@types/history-5@npm:@types/history@4.7.8", "@types/history@*":
+"@types/history-4@npm:@types/history@4.7.8":
+ version "4.7.8"
+ resolved "https://registry.yarnpkg.com/@types/history/-/history-4.7.8.tgz#49348387983075705fe8f4e02fb67f7daaec4934"
+ integrity sha512-S78QIYirQcUoo6UJZx9CSP0O2ix9IaeAXwQi26Rhr/+mg7qqPy8TzaxHSUut7eGjL8WmLccT7/MXf304WjqHcA==
+
+"@types/history-5@npm:@types/history@4.7.8":
+ version "4.7.8"
+ resolved "https://registry.yarnpkg.com/@types/history/-/history-4.7.8.tgz#49348387983075705fe8f4e02fb67f7daaec4934"
+ integrity sha512-S78QIYirQcUoo6UJZx9CSP0O2ix9IaeAXwQi26Rhr/+mg7qqPy8TzaxHSUut7eGjL8WmLccT7/MXf304WjqHcA==
+
+"@types/history@*":
version "4.7.8"
resolved "https://registry.yarnpkg.com/@types/history/-/history-4.7.8.tgz#49348387983075705fe8f4e02fb67f7daaec4934"
integrity sha512-S78QIYirQcUoo6UJZx9CSP0O2ix9IaeAXwQi26Rhr/+mg7qqPy8TzaxHSUut7eGjL8WmLccT7/MXf304WjqHcA==
@@ -9775,7 +9785,15 @@
"@types/history" "^3"
"@types/react" "*"
-"@types/react-router-4@npm:@types/react-router@5.1.14", "@types/react-router-5@npm:@types/react-router@5.1.14":
+"@types/react-router-4@npm:@types/react-router@5.1.14":
+ version "5.1.14"
+ resolved "https://registry.yarnpkg.com/@types/react-router/-/react-router-5.1.14.tgz#e0442f4eb4c446541ad7435d44a97f8fe6df40da"
+ integrity sha512-LAJpqYUaCTMT2anZheoidiIymt8MuX286zoVFPM3DVb23aQBH0mAkFvzpd4LKqiolV8bBtZWT5Qp7hClCNDENw==
+ dependencies:
+ "@types/history" "*"
+ "@types/react" "*"
+
+"@types/react-router-5@npm:@types/react-router@5.1.14":
version "5.1.14"
resolved "https://registry.yarnpkg.com/@types/react-router/-/react-router-5.1.14.tgz#e0442f4eb4c446541ad7435d44a97f8fe6df40da"
integrity sha512-LAJpqYUaCTMT2anZheoidiIymt8MuX286zoVFPM3DVb23aQBH0mAkFvzpd4LKqiolV8bBtZWT5Qp7hClCNDENw==
@@ -11359,11 +11377,6 @@ adjust-sourcemap-loader@^4.0.0:
loader-utils "^2.0.0"
regex-parser "^2.2.11"
-agent-base@5:
- version "5.1.1"
- resolved "https://registry.yarnpkg.com/agent-base/-/agent-base-5.1.1.tgz#e8fb3f242959db44d63be665db7a8e739537a32c"
- integrity sha512-TMeqbNl2fMW0nMjTEPOwe3J/PRFP4vqeoNuQMG0HlMrtm5QxKqdvAkZ1pRBQ/ulIyDD5Yq0nJ7YbdD8ey0TO3g==
-
agent-base@6, agent-base@^6.0.2:
version "6.0.2"
resolved "https://registry.yarnpkg.com/agent-base/-/agent-base-6.0.2.tgz#49fff58577cfee3f37176feab4c22e00f86d7f77"
@@ -11809,11 +11822,6 @@ args@^5.0.3:
leven "2.1.0"
mri "1.1.4"
-argv@0.0.2:
- version "0.0.2"
- resolved "https://registry.yarnpkg.com/argv/-/argv-0.0.2.tgz#ecbd16f8949b157183711b1bda334f37840185ab"
- integrity sha1-7L0W+JSbFXGDcRsb2jNPN4QBhas=
-
aria-query@5.1.3:
version "5.1.3"
resolved "https://registry.yarnpkg.com/aria-query/-/aria-query-5.1.3.tgz#19db27cd101152773631396f7a95a3b58c22c35e"
@@ -14410,17 +14418,6 @@ code-red@^1.0.3:
estree-walker "^3.0.3"
periscopic "^3.1.0"
-codecov@^3.6.5:
- version "3.8.1"
- resolved "https://registry.yarnpkg.com/codecov/-/codecov-3.8.1.tgz#06fe026b75525ed1ce864d4a34f1010c52c51546"
- integrity sha512-Qm7ltx1pzLPsliZY81jyaQ80dcNR4/JpcX0IHCIWrHBXgseySqbdbYfkdiXd7o/xmzQpGRVCKGYeTrHUpn6Dcw==
- dependencies:
- argv "0.0.2"
- ignore-walk "3.0.3"
- js-yaml "3.14.0"
- teeny-request "6.0.1"
- urlgrey "0.4.4"
-
collect-v8-coverage@^1.0.0:
version "1.0.1"
resolved "https://registry.yarnpkg.com/collect-v8-coverage/-/collect-v8-coverage-1.0.1.tgz#cc2c8e94fc18bbdffe64d6534570c8a673b27f59"
@@ -20722,14 +20719,6 @@ https-proxy-agent@5.0.1, https-proxy-agent@^5.0.0, https-proxy-agent@^5.0.1:
agent-base "6"
debug "4"
-https-proxy-agent@^4.0.0:
- version "4.0.0"
- resolved "https://registry.yarnpkg.com/https-proxy-agent/-/https-proxy-agent-4.0.0.tgz#702b71fb5520a132a66de1f67541d9e62154d82b"
- integrity sha512-zoDhWrkR3of1l9QAL8/scJZyLu8j/gBkcwcaQOZh7Gyh/+uJQzGVETdgT30akuwkpL8HTRfssqI3BZuV18teDg==
- dependencies:
- agent-base "5"
- debug "4"
-
https-proxy-agent@^7.0.1:
version "7.0.4"
resolved "https://registry.yarnpkg.com/https-proxy-agent/-/https-proxy-agent-7.0.4.tgz#8e97b841a029ad8ddc8731f26595bad868cb4168"
@@ -20809,7 +20798,7 @@ ignore-by-default@^1.0.1:
resolved "https://registry.yarnpkg.com/ignore-by-default/-/ignore-by-default-1.0.1.tgz#48ca6d72f6c6a3af00a9ad4ae6876be3889e2b09"
integrity sha512-Ius2VYcGNk7T90CppJqcIkS5ooHUZyIQK+ClZfMfMNFEF9VSE73Fq+906u/CWu92x4gzZMWOwfFYckPObzdEbA==
-ignore-walk@3.0.3, ignore-walk@^3.0.3:
+ignore-walk@^3.0.3:
version "3.0.3"
resolved "https://registry.yarnpkg.com/ignore-walk/-/ignore-walk-3.0.3.tgz#017e2447184bfeade7c238e4aefdd1e8f95b1e37"
integrity sha512-m7o6xuOaT1aqheYHKf8W6J5pYH85ZI9w077erOzLje3JsB1gkafkAhHHY19dqjulgIZHFm32Cp5uNZgcQqdJKw==
@@ -22524,14 +22513,6 @@ js-tokens@^9.0.0:
resolved "https://registry.yarnpkg.com/js-tokens/-/js-tokens-9.0.0.tgz#0f893996d6f3ed46df7f0a3b12a03f5fd84223c1"
integrity sha512-WriZw1luRMlmV3LGJaR6QOJjWwgLUTf89OwT2lUOyjX2dJGBwgmIkbcz+7WFZjrZM635JOIR517++e/67CP9dQ==
-js-yaml@3.14.0:
- version "3.14.0"
- resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-3.14.0.tgz#a7a34170f26a21bb162424d8adacb4113a69e482"
- integrity sha512-/4IbIeHcD9VMHFqDR/gQ7EdZdLimOvW2DdcxFjdyyZ9NsbS+ccrXqVWDtab/lRl5AlUqmpBx8EhPaWR+OtY17A==
- dependencies:
- argparse "^1.0.7"
- esprima "^4.0.0"
-
js-yaml@4.1.0, js-yaml@^4.1.0:
version "4.1.0"
resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-4.1.0.tgz#c1fb65f8f5017901cdd2c951864ba18458a10602"
@@ -25612,7 +25593,7 @@ node-fetch-native@^1.6.1, node-fetch-native@^1.6.2, node-fetch-native@^1.6.3, no
resolved "https://registry.yarnpkg.com/node-fetch-native/-/node-fetch-native-1.6.4.tgz#679fc8fd8111266d47d7e72c379f1bed9acff06e"
integrity sha512-IhOigYzAKHd244OC0JIMIUrjzctirCmPkaIfhDeGcEETWof5zKYUW7e7MYvChGWh/4CJeXEgsRyGzuF334rOOQ==
-node-fetch@2.6.7, node-fetch@^2.2.0, node-fetch@^2.3.0, node-fetch@^2.6.0, node-fetch@^2.6.1, node-fetch@^2.6.7:
+node-fetch@2.6.7, node-fetch@^2.3.0, node-fetch@^2.6.0, node-fetch@^2.6.1, node-fetch@^2.6.7:
version "2.6.7"
resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-2.6.7.tgz#24de9fba827e3b4ae44dc8b20256a379160052ad"
integrity sha512-ZjMPFEfVx5j+y2yF35Kzx5sF7kDzxuDj6ziH4FFbOp87zKDZNx8yExJIb05OGF4Nlt9IHFIMBkRl41VdvcNdbQ==
@@ -28972,7 +28953,7 @@ react-is@^18.0.0:
dependencies:
"@remix-run/router" "1.0.2"
-"react-router-6@npm:react-router@6.3.0", react-router@6.3.0:
+"react-router-6@npm:react-router@6.3.0":
version "6.3.0"
resolved "https://registry.yarnpkg.com/react-router/-/react-router-6.3.0.tgz#3970cc64b4cb4eae0c1ea5203a80334fdd175557"
integrity sha512-7Wh1DzVQ+tlFjkeo+ujvjSqSJmkt1+8JO+T5xklPlgrh70y7ogx75ODRW0ThWhY7S+6yEDks8TYrtQe/aoboBQ==
@@ -28987,6 +28968,13 @@ react-router-dom@^6.2.2:
history "^5.2.0"
react-router "6.3.0"
+react-router@6.3.0:
+ version "6.3.0"
+ resolved "https://registry.yarnpkg.com/react-router/-/react-router-6.3.0.tgz#3970cc64b4cb4eae0c1ea5203a80334fdd175557"
+ integrity sha512-7Wh1DzVQ+tlFjkeo+ujvjSqSJmkt1+8JO+T5xklPlgrh70y7ogx75ODRW0ThWhY7S+6yEDks8TYrtQe/aoboBQ==
+ dependencies:
+ history "^5.2.0"
+
react@^18.0.0:
version "18.0.0"
resolved "https://registry.yarnpkg.com/react/-/react-18.0.0.tgz#b468736d1f4a5891f38585ba8e8fb29f91c3cb96"
@@ -31519,7 +31507,16 @@ string-template@~0.2.1:
resolved "https://registry.yarnpkg.com/string-template/-/string-template-0.2.1.tgz#42932e598a352d01fc22ec3367d9d84eec6c9add"
integrity sha1-QpMuWYo1LQH8IuwzZ9nYTuxsmt0=
-"string-width-cjs@npm:string-width@^4.2.0", string-width@4.2.3, string-width@^4.2.0, string-width@^4.2.2, string-width@^4.2.3:
+"string-width-cjs@npm:string-width@^4.2.0":
+ version "4.2.3"
+ resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010"
+ integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==
+ dependencies:
+ emoji-regex "^8.0.0"
+ is-fullwidth-code-point "^3.0.0"
+ strip-ansi "^6.0.1"
+
+string-width@4.2.3, string-width@^4.2.0, string-width@^4.2.2, string-width@^4.2.3:
version "4.2.3"
resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010"
integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==
@@ -31631,7 +31628,14 @@ stringify-object@^3.2.1:
is-obj "^1.0.1"
is-regexp "^1.0.0"
-"strip-ansi-cjs@npm:strip-ansi@^6.0.1", strip-ansi@6.0.1, strip-ansi@^6.0.0, strip-ansi@^6.0.1:
+"strip-ansi-cjs@npm:strip-ansi@^6.0.1":
+ version "6.0.1"
+ resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9"
+ integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==
+ dependencies:
+ ansi-regex "^5.0.1"
+
+strip-ansi@6.0.1, strip-ansi@^6.0.0, strip-ansi@^6.0.1:
version "6.0.1"
resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9"
integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==
@@ -32086,17 +32090,6 @@ tar@^6.2.0:
mkdirp "^1.0.3"
yallist "^4.0.0"
-teeny-request@6.0.1:
- version "6.0.1"
- resolved "https://registry.yarnpkg.com/teeny-request/-/teeny-request-6.0.1.tgz#9b1f512cef152945827ba7e34f62523a4ce2c5b0"
- integrity sha512-TAK0c9a00ELOqLrZ49cFxvPVogMUFaWY8dUsQc/0CuQPGF+BOxOQzXfE413BAk2kLomwNplvdtMpeaeGWmoc2g==
- dependencies:
- http-proxy-agent "^4.0.0"
- https-proxy-agent "^4.0.0"
- node-fetch "^2.2.0"
- stream-events "^1.0.5"
- uuid "^3.3.2"
-
teeny-request@^7.0.0:
version "7.0.1"
resolved "https://registry.yarnpkg.com/teeny-request/-/teeny-request-7.0.1.tgz#bdd41fdffea5f8fbc0d29392cb47bec4f66b2b4c"
@@ -33505,11 +33498,6 @@ url@^0.11.0:
punycode "1.3.2"
querystring "0.2.0"
-urlgrey@0.4.4:
- version "0.4.4"
- resolved "https://registry.yarnpkg.com/urlgrey/-/urlgrey-0.4.4.tgz#892fe95960805e85519f1cd4389f2cb4cbb7652f"
- integrity sha1-iS/pWWCAXoVRnxzUOJ8stMu3ZS8=
-
urlpattern-polyfill@8.0.2:
version "8.0.2"
resolved "https://registry.yarnpkg.com/urlpattern-polyfill/-/urlpattern-polyfill-8.0.2.tgz#99f096e35eff8bf4b5a2aa7d58a1523d6ebc7ce5"
@@ -33587,11 +33575,6 @@ uuid@8.3.2, uuid@^8.0.0, uuid@^8.3.1, uuid@^8.3.2:
resolved "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz#80d5b5ced271bb9af6c445f21a1a04c606cefbe2"
integrity sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==
-uuid@^3.3.2:
- version "3.4.0"
- resolved "https://registry.yarnpkg.com/uuid/-/uuid-3.4.0.tgz#b23e4358afa8a202fe7a100af1f5f883f02007ee"
- integrity sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A==
-
uuid@^9.0.0:
version "9.0.0"
resolved "https://registry.yarnpkg.com/uuid/-/uuid-9.0.0.tgz#592f550650024a38ceb0c562f2f6aa435761efb5"
@@ -34724,7 +34707,16 @@ workerpool@^6.4.0:
resolved "https://registry.yarnpkg.com/workerpool/-/workerpool-6.4.0.tgz#f8d5cfb45fde32fa3b7af72ad617c3369567a462"
integrity sha512-i3KR1mQMNwY2wx20ozq2EjISGtQWDIfV56We+yGJ5yDs8jTwQiLLaqHlkBHITlCuJnYlVRmXegxFxZg7gqI++A==
-"wrap-ansi-cjs@npm:wrap-ansi@^7.0.0", wrap-ansi@7.0.0, wrap-ansi@^7.0.0:
+"wrap-ansi-cjs@npm:wrap-ansi@^7.0.0":
+ version "7.0.0"
+ resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-7.0.0.tgz#67e145cff510a6a6984bdf1152911d69d2eb9e43"
+ integrity sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==
+ dependencies:
+ ansi-styles "^4.0.0"
+ string-width "^4.1.0"
+ strip-ansi "^6.0.0"
+
+wrap-ansi@7.0.0, wrap-ansi@^7.0.0:
version "7.0.0"
resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-7.0.0.tgz#67e145cff510a6a6984bdf1152911d69d2eb9e43"
integrity sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==
From 00fabe5ed7cc15dbc524e050e7bed34e392344ad Mon Sep 17 00:00:00 2001
From: Luca Forstner
Date: Thu, 11 Jul 2024 09:39:53 +0200
Subject: [PATCH 07/39] docs(nextjs): Clarify which API wrappers are intended
for which router (#12867)
---
packages/nextjs/src/common/wrapRouteHandlerWithSentry.ts | 4 +++-
packages/nextjs/src/index.types.ts | 4 +++-
2 files changed, 6 insertions(+), 2 deletions(-)
diff --git a/packages/nextjs/src/common/wrapRouteHandlerWithSentry.ts b/packages/nextjs/src/common/wrapRouteHandlerWithSentry.ts
index e55eedd9802e..bf0d475603f2 100644
--- a/packages/nextjs/src/common/wrapRouteHandlerWithSentry.ts
+++ b/packages/nextjs/src/common/wrapRouteHandlerWithSentry.ts
@@ -22,7 +22,9 @@ import {
import { vercelWaitUntil } from './utils/vercelWaitUntil';
/**
- * Wraps a Next.js route handler with performance and error instrumentation.
+ * Wraps a Next.js App Router Route handler with Sentry error and performance instrumentation.
+ *
+ * NOTICE: This wrapper is for App Router API routes. If you are looking to wrap Pages Router API routes use `wrapApiHandlerWithSentry` instead.
*/
// eslint-disable-next-line @typescript-eslint/no-explicit-any
export function wrapRouteHandlerWithSentry any>(
diff --git a/packages/nextjs/src/index.types.ts b/packages/nextjs/src/index.types.ts
index 629560b96312..afff0bd98a19 100644
--- a/packages/nextjs/src/index.types.ts
+++ b/packages/nextjs/src/index.types.ts
@@ -41,7 +41,9 @@ export declare const metrics: typeof clientSdk.metrics & typeof serverSdk.metric
export { withSentryConfig } from './config';
/**
- * Wraps a Next.js API handler with Sentry error and performance instrumentation.
+ * Wraps a Next.js Pages Router API route with Sentry error and performance instrumentation.
+ *
+ * NOTICE: This wrapper is for Pages Router API routes. If you are looking to wrap App Router API routes use `wrapRouteHandlerWithSentry` instead.
*
* @param handler The handler exported from the API route file.
* @param parameterizedRoute The page's parameterized route.
From 0e6d8021035ae59a19d294f6ee3a7158e90e34f6 Mon Sep 17 00:00:00 2001
From: Nicolas Hrubec
Date: Thu, 11 Jul 2024 10:26:14 +0200
Subject: [PATCH 08/39] test(e2e): Add nestjs e2e test documenting errors not
being properly caught in submodules (#12868)
---
.../nestjs/src/app.module.ts | 3 +-
.../nestjs/src/test-module/test.controller.ts | 12 ++++++++
.../nestjs/src/test-module/test.exception.ts | 5 ++++
.../nestjs/src/test-module/test.filter.ts | 13 +++++++++
.../nestjs/src/test-module/test.module.ts | 16 ++++++++++
.../nestjs/tests/errors.test.ts | 29 +++++++++++++++++++
6 files changed, 77 insertions(+), 1 deletion(-)
create mode 100644 dev-packages/e2e-tests/test-applications/nestjs/src/test-module/test.controller.ts
create mode 100644 dev-packages/e2e-tests/test-applications/nestjs/src/test-module/test.exception.ts
create mode 100644 dev-packages/e2e-tests/test-applications/nestjs/src/test-module/test.filter.ts
create mode 100644 dev-packages/e2e-tests/test-applications/nestjs/src/test-module/test.module.ts
diff --git a/dev-packages/e2e-tests/test-applications/nestjs/src/app.module.ts b/dev-packages/e2e-tests/test-applications/nestjs/src/app.module.ts
index b4f9d5588dda..932d1af99611 100644
--- a/dev-packages/e2e-tests/test-applications/nestjs/src/app.module.ts
+++ b/dev-packages/e2e-tests/test-applications/nestjs/src/app.module.ts
@@ -2,9 +2,10 @@ import { Module } from '@nestjs/common';
import { ScheduleModule } from '@nestjs/schedule';
import { AppController1, AppController2 } from './app.controller';
import { AppService1, AppService2 } from './app.service';
+import { TestModule } from './test-module/test.module';
@Module({
- imports: [ScheduleModule.forRoot()],
+ imports: [ScheduleModule.forRoot(), TestModule],
controllers: [AppController1],
providers: [AppService1],
})
diff --git a/dev-packages/e2e-tests/test-applications/nestjs/src/test-module/test.controller.ts b/dev-packages/e2e-tests/test-applications/nestjs/src/test-module/test.controller.ts
new file mode 100644
index 000000000000..150fb0e07546
--- /dev/null
+++ b/dev-packages/e2e-tests/test-applications/nestjs/src/test-module/test.controller.ts
@@ -0,0 +1,12 @@
+import { Controller, Get } from '@nestjs/common';
+import { TestException } from './test.exception';
+
+@Controller('test-module')
+export class TestController {
+ constructor() {}
+
+ @Get()
+ getTest(): string {
+ throw new TestException();
+ }
+}
diff --git a/dev-packages/e2e-tests/test-applications/nestjs/src/test-module/test.exception.ts b/dev-packages/e2e-tests/test-applications/nestjs/src/test-module/test.exception.ts
new file mode 100644
index 000000000000..b736596b6717
--- /dev/null
+++ b/dev-packages/e2e-tests/test-applications/nestjs/src/test-module/test.exception.ts
@@ -0,0 +1,5 @@
+export class TestException extends Error {
+ constructor() {
+ super('Something went wrong in the test module!');
+ }
+}
diff --git a/dev-packages/e2e-tests/test-applications/nestjs/src/test-module/test.filter.ts b/dev-packages/e2e-tests/test-applications/nestjs/src/test-module/test.filter.ts
new file mode 100644
index 000000000000..87a4ca0920e5
--- /dev/null
+++ b/dev-packages/e2e-tests/test-applications/nestjs/src/test-module/test.filter.ts
@@ -0,0 +1,13 @@
+import { ArgumentsHost, BadRequestException, Catch } from '@nestjs/common';
+import { BaseExceptionFilter } from '@nestjs/core';
+import { TestException } from './test.exception';
+
+@Catch(TestException)
+export class TestExceptionFilter extends BaseExceptionFilter {
+ catch(exception: unknown, host: ArgumentsHost) {
+ if (exception instanceof TestException) {
+ return super.catch(new BadRequestException(exception.message), host);
+ }
+ return super.catch(exception, host);
+ }
+}
diff --git a/dev-packages/e2e-tests/test-applications/nestjs/src/test-module/test.module.ts b/dev-packages/e2e-tests/test-applications/nestjs/src/test-module/test.module.ts
new file mode 100644
index 000000000000..37b6dbe7e819
--- /dev/null
+++ b/dev-packages/e2e-tests/test-applications/nestjs/src/test-module/test.module.ts
@@ -0,0 +1,16 @@
+import { Module } from '@nestjs/common';
+import { APP_FILTER } from '@nestjs/core';
+import { TestController } from './test.controller';
+import { TestExceptionFilter } from './test.filter';
+
+@Module({
+ imports: [],
+ controllers: [TestController],
+ providers: [
+ {
+ provide: APP_FILTER,
+ useClass: TestExceptionFilter,
+ },
+ ],
+})
+export class TestModule {}
diff --git a/dev-packages/e2e-tests/test-applications/nestjs/tests/errors.test.ts b/dev-packages/e2e-tests/test-applications/nestjs/tests/errors.test.ts
index 349b25b0eee9..ffb48f4e5e70 100644
--- a/dev-packages/e2e-tests/test-applications/nestjs/tests/errors.test.ts
+++ b/dev-packages/e2e-tests/test-applications/nestjs/tests/errors.test.ts
@@ -53,3 +53,32 @@ test('Does not send expected exception to Sentry', async ({ baseURL }) => {
expect(errorEventOccurred).toBe(false);
});
+
+test('Does not handle expected exception if exception is thrown in module', async ({ baseURL }) => {
+ const errorEventPromise = waitForError('nestjs', event => {
+ return !event.type && event.exception?.values?.[0]?.value === 'Something went wrong in the test module!';
+ });
+
+ const response = await fetch(`${baseURL}/test-module`);
+ expect(response.status).toBe(500); // should be 400
+
+ // should never arrive, but does because the exception is not handled properly
+ const errorEvent = await errorEventPromise;
+
+ expect(errorEvent.exception?.values).toHaveLength(1);
+ expect(errorEvent.exception?.values?.[0]?.value).toBe('Something went wrong in the test module!');
+
+ expect(errorEvent.request).toEqual({
+ method: 'GET',
+ cookies: {},
+ headers: expect.any(Object),
+ url: 'http://localhost:3030/test-module',
+ });
+
+ expect(errorEvent.transaction).toEqual('GET /test-module');
+
+ expect(errorEvent.contexts?.trace).toEqual({
+ trace_id: expect.any(String),
+ span_id: expect.any(String),
+ });
+});
From 1be3b4a6430cd288882f2a82ccaa657f30b18507 Mon Sep 17 00:00:00 2001
From: Ziyad Khalil <36865563+ziyadkhalil@users.noreply.github.com>
Date: Thu, 11 Jul 2024 12:22:20 +0300
Subject: [PATCH 09/39] fix(nextjs): Support automatic instrumentation for app
directory with custom page extensions (#12858)
Co-authored-by: Luca Forstner
---
.../src/config/loaders/wrappingLoader.ts | 3 +-
packages/nextjs/src/config/webpack.ts | 22 +++++++++-----
packages/nextjs/test/config/fixtures.ts | 1 +
packages/nextjs/test/config/loaders.test.ts | 30 +++++++++++++++++--
4 files changed, 45 insertions(+), 11 deletions(-)
diff --git a/packages/nextjs/src/config/loaders/wrappingLoader.ts b/packages/nextjs/src/config/loaders/wrappingLoader.ts
index a0d953d8315b..ea7828497f95 100644
--- a/packages/nextjs/src/config/loaders/wrappingLoader.ts
+++ b/packages/nextjs/src/config/loaders/wrappingLoader.ts
@@ -182,7 +182,8 @@ export default function wrappingLoader(
const componentTypeMatch = path.posix
.normalize(path.relative(appDir, this.resourcePath))
- .match(/\/?([^/]+)\.(?:js|ts|jsx|tsx)$/);
+ // eslint-disable-next-line @sentry-internal/sdk/no-regexp-constructor
+ .match(new RegExp(`/\\/?([^/]+)\\.(?:${pageExtensionRegex})$`));
if (componentTypeMatch && componentTypeMatch[1]) {
let componentType: ServerComponentContext['componentType'];
diff --git a/packages/nextjs/src/config/webpack.ts b/packages/nextjs/src/config/webpack.ts
index 4002db18f295..ecc39f7372dd 100644
--- a/packages/nextjs/src/config/webpack.ts
+++ b/packages/nextjs/src/config/webpack.ts
@@ -147,7 +147,7 @@ export function constructWebpackConfigFunction(
);
};
- const possibleMiddlewareLocations = ['js', 'jsx', 'ts', 'tsx'].map(middlewareFileEnding => {
+ const possibleMiddlewareLocations = pageExtensions.map(middlewareFileEnding => {
return path.join(middlewareLocationFolder, `middleware.${middlewareFileEnding}`);
});
const isMiddlewareResource = (resourcePath: string): boolean => {
@@ -163,7 +163,10 @@ export function constructWebpackConfigFunction(
return (
appDirPath !== undefined &&
normalizedAbsoluteResourcePath.startsWith(appDirPath + path.sep) &&
- !!normalizedAbsoluteResourcePath.match(/[\\/](page|layout|loading|head|not-found)\.(js|jsx|tsx)$/)
+ !!normalizedAbsoluteResourcePath.match(
+ // eslint-disable-next-line @sentry-internal/sdk/no-regexp-constructor
+ new RegExp(`[\\\\/](page|layout|loading|head|not-found)\\.(${pageExtensionRegex})$`),
+ )
);
};
@@ -172,7 +175,10 @@ export function constructWebpackConfigFunction(
return (
appDirPath !== undefined &&
normalizedAbsoluteResourcePath.startsWith(appDirPath + path.sep) &&
- !!normalizedAbsoluteResourcePath.match(/[\\/]route\.(js|jsx|ts|tsx)$/)
+ !!normalizedAbsoluteResourcePath.match(
+ // eslint-disable-next-line @sentry-internal/sdk/no-regexp-constructor
+ new RegExp(`[\\\\/]route\\.(${pageExtensionRegex})$`),
+ )
);
};
@@ -285,10 +291,12 @@ export function constructWebpackConfigFunction(
}
if (appDirPath) {
- const hasGlobalErrorFile = ['global-error.js', 'global-error.jsx', 'global-error.ts', 'global-error.tsx'].some(
- // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
- globalErrorFile => fs.existsSync(path.join(appDirPath!, globalErrorFile)),
- );
+ const hasGlobalErrorFile = pageExtensions
+ .map(extension => `global-error.${extension}`)
+ .some(
+ // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
+ globalErrorFile => fs.existsSync(path.join(appDirPath!, globalErrorFile)),
+ );
if (
!hasGlobalErrorFile &&
diff --git a/packages/nextjs/test/config/fixtures.ts b/packages/nextjs/test/config/fixtures.ts
index 7da47e37be33..a3c4feb0123b 100644
--- a/packages/nextjs/test/config/fixtures.ts
+++ b/packages/nextjs/test/config/fixtures.ts
@@ -13,6 +13,7 @@ export const EDGE_SDK_CONFIG_FILE = 'sentry.edge.config.js';
/** Mock next config object */
export const userNextConfig: NextConfigObject = {
publicRuntimeConfig: { location: 'dogpark', activities: ['fetch', 'chasing', 'digging'] },
+ pageExtensions: ['jsx', 'js', 'tsx', 'ts', 'custom.jsx', 'custom.js', 'custom.tsx', 'custom.ts'],
webpack: (incomingWebpackConfig: WebpackConfigObject, _options: BuildContext) => ({
...incomingWebpackConfig,
mode: 'universal-sniffing',
diff --git a/packages/nextjs/test/config/loaders.test.ts b/packages/nextjs/test/config/loaders.test.ts
index c2aaf0c9a707..c559ee643baf 100644
--- a/packages/nextjs/test/config/loaders.test.ts
+++ b/packages/nextjs/test/config/loaders.test.ts
@@ -96,6 +96,10 @@ describe('webpack loaders', () => {
resourcePath: '/Users/Maisey/projects/squirrelChasingSimulator/src/pages/testPage.tsx',
expectedWrappingTargetKind: 'page',
},
+ {
+ resourcePath: '/Users/Maisey/projects/squirrelChasingSimulator/src/pages/testPage.custom.tsx',
+ expectedWrappingTargetKind: 'page',
+ },
{
resourcePath: './src/pages/testPage.tsx',
expectedWrappingTargetKind: 'page',
@@ -133,6 +137,10 @@ describe('webpack loaders', () => {
resourcePath: '/Users/Maisey/projects/squirrelChasingSimulator/src/middleware.js',
expectedWrappingTargetKind: 'middleware',
},
+ {
+ resourcePath: '/Users/Maisey/projects/squirrelChasingSimulator/src/middleware.custom.js',
+ expectedWrappingTargetKind: 'middleware',
+ },
{
resourcePath: './src/middleware.js',
expectedWrappingTargetKind: 'middleware',
@@ -162,17 +170,33 @@ describe('webpack loaders', () => {
resourcePath: '/Users/Maisey/projects/squirrelChasingSimulator/src/pages/api/nested/testApiRoute.js',
expectedWrappingTargetKind: 'api-route',
},
+ {
+ resourcePath: '/Users/Maisey/projects/squirrelChasingSimulator/src/pages/api/nested/testApiRoute.custom.js',
+ expectedWrappingTargetKind: 'api-route',
+ },
+ {
+ resourcePath: '/Users/Maisey/projects/squirrelChasingSimulator/src/app/nested/route.ts',
+ expectedWrappingTargetKind: 'route-handler',
+ },
+ {
+ resourcePath: '/Users/Maisey/projects/squirrelChasingSimulator/src/app/nested/route.custom.ts',
+ expectedWrappingTargetKind: 'route-handler',
+ },
{
resourcePath: '/Users/Maisey/projects/squirrelChasingSimulator/src/app/page.js',
expectedWrappingTargetKind: 'server-component',
},
+ {
+ resourcePath: '/Users/Maisey/projects/squirrelChasingSimulator/src/app/page.custom.js',
+ expectedWrappingTargetKind: 'server-component',
+ },
{
resourcePath: '/Users/Maisey/projects/squirrelChasingSimulator/src/app/nested/page.js',
expectedWrappingTargetKind: 'server-component',
},
{
- resourcePath: '/Users/Maisey/projects/squirrelChasingSimulator/src/app/nested/page.ts', // ts is not a valid file ending for pages in the app dir
- expectedWrappingTargetKind: undefined,
+ resourcePath: '/Users/Maisey/projects/squirrelChasingSimulator/src/app/nested/page.ts',
+ expectedWrappingTargetKind: 'server-component',
},
{
resourcePath: '/Users/Maisey/projects/squirrelChasingSimulator/src/app/(group)/nested/page.tsx',
@@ -180,7 +204,7 @@ describe('webpack loaders', () => {
},
{
resourcePath: '/Users/Maisey/projects/squirrelChasingSimulator/src/app/(group)/nested/loading.ts',
- expectedWrappingTargetKind: undefined,
+ expectedWrappingTargetKind: 'server-component',
},
{
resourcePath: '/Users/Maisey/projects/squirrelChasingSimulator/src/app/layout.js',
From 9bb9c994c58e1b6fc96bdb53e40a4d5521f40c18 Mon Sep 17 00:00:00 2001
From: Nicolas Hrubec
Date: Thu, 11 Jul 2024 12:35:57 +0200
Subject: [PATCH 10/39] ci: Test external contributor workflow (#12876)
---
packages/nestjs/README.md | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/packages/nestjs/README.md b/packages/nestjs/README.md
index 8928327b1470..b9ac0c9371c2 100644
--- a/packages/nestjs/README.md
+++ b/packages/nestjs/README.md
@@ -41,7 +41,7 @@ Note that it is necessary to initialize Sentry **before you import any package t
## Span Decorator
Use the @SentryTraced() decorator to gain additional performance insights for any function within your NestJS
-application.
+applications.
```js
import { Injectable } from '@nestjs/common';
From 4852dc706d4c165409eb22bc0b71da551b5753eb Mon Sep 17 00:00:00 2001
From: Andrei <168741329+andreiborza@users.noreply.github.com>
Date: Thu, 11 Jul 2024 13:02:36 +0200
Subject: [PATCH 11/39] test(solidstart): Add solidstart e2e test app (#12873)
For now this only tests client side errors. It also doesn't use the
solidrouter integration yet because it fails to resolve the
`@sentry/solidstart/solidrouter` import properly in our repo. I have yet
to figure out what exactly is causing this (my guess is some pnpm
shenanigans). I couldn't reproduce this in a sample app outside of our
repo yet.
Closes: #12547
---
biome.json | 4 +-
.../solid-solidrouter/package.json | 2 +-
.../test-applications/solid/package.json | 2 +-
.../test-applications/solidstart/.gitignore | 46 +++++++++
.../test-applications/solidstart/.npmrc | 2 +
.../test-applications/solidstart/README.md | 45 +++++++++
.../solidstart/app.config.ts | 3 +
.../test-applications/solidstart/package.json | 34 +++++++
.../solidstart/playwright.config.mjs | 8 ++
.../solidstart/public/favicon.ico | Bin 0 -> 664 bytes
.../test-applications/solidstart/src/app.tsx | 19 ++++
.../solidstart/src/entry-client.tsx | 16 ++++
.../solidstart/src/entry-server.tsx | 21 ++++
.../solidstart/src/instrument.server.mjs | 8 ++
.../solidstart/src/routes/client-error.tsx | 75 +++++++++++++++
.../solidstart/src/routes/index.tsx | 17 ++++
.../solidstart/start-event-proxy.mjs | 6 ++
.../solidstart/tests/errorboundary.test.ts | 90 ++++++++++++++++++
.../solidstart/tests/errors.client.test.ts | 32 +++++++
.../solidstart/tsconfig.json | 19 ++++
.../solidstart/vitest.config.ts | 10 ++
21 files changed, 456 insertions(+), 3 deletions(-)
create mode 100644 dev-packages/e2e-tests/test-applications/solidstart/.gitignore
create mode 100644 dev-packages/e2e-tests/test-applications/solidstart/.npmrc
create mode 100644 dev-packages/e2e-tests/test-applications/solidstart/README.md
create mode 100644 dev-packages/e2e-tests/test-applications/solidstart/app.config.ts
create mode 100644 dev-packages/e2e-tests/test-applications/solidstart/package.json
create mode 100644 dev-packages/e2e-tests/test-applications/solidstart/playwright.config.mjs
create mode 100644 dev-packages/e2e-tests/test-applications/solidstart/public/favicon.ico
create mode 100644 dev-packages/e2e-tests/test-applications/solidstart/src/app.tsx
create mode 100644 dev-packages/e2e-tests/test-applications/solidstart/src/entry-client.tsx
create mode 100644 dev-packages/e2e-tests/test-applications/solidstart/src/entry-server.tsx
create mode 100644 dev-packages/e2e-tests/test-applications/solidstart/src/instrument.server.mjs
create mode 100644 dev-packages/e2e-tests/test-applications/solidstart/src/routes/client-error.tsx
create mode 100644 dev-packages/e2e-tests/test-applications/solidstart/src/routes/index.tsx
create mode 100644 dev-packages/e2e-tests/test-applications/solidstart/start-event-proxy.mjs
create mode 100644 dev-packages/e2e-tests/test-applications/solidstart/tests/errorboundary.test.ts
create mode 100644 dev-packages/e2e-tests/test-applications/solidstart/tests/errors.client.test.ts
create mode 100644 dev-packages/e2e-tests/test-applications/solidstart/tsconfig.json
create mode 100644 dev-packages/e2e-tests/test-applications/solidstart/vitest.config.ts
diff --git a/biome.json b/biome.json
index ccb69e4746db..cee069ac3127 100644
--- a/biome.json
+++ b/biome.json
@@ -43,7 +43,9 @@
".angular/**",
"angular.json",
"ember/instance-initializers/**",
- "ember/types.d.ts"
+ "ember/types.d.ts",
+ ".output",
+ ".vinxi"
]
},
"files": {
diff --git a/dev-packages/e2e-tests/test-applications/solid-solidrouter/package.json b/dev-packages/e2e-tests/test-applications/solid-solidrouter/package.json
index 3f0d7ca13fce..26f2ee5ba342 100644
--- a/dev-packages/e2e-tests/test-applications/solid-solidrouter/package.json
+++ b/dev-packages/e2e-tests/test-applications/solid-solidrouter/package.json
@@ -27,7 +27,7 @@
},
"dependencies": {
"@solidjs/router": "^0.13.5",
- "solid-js": "^1.8.11",
+ "solid-js": "^1.8.18",
"@sentry/solid": "latest || *"
}
}
diff --git a/dev-packages/e2e-tests/test-applications/solid/package.json b/dev-packages/e2e-tests/test-applications/solid/package.json
index ebfde066e6ef..6d56a61d08cf 100644
--- a/dev-packages/e2e-tests/test-applications/solid/package.json
+++ b/dev-packages/e2e-tests/test-applications/solid/package.json
@@ -26,7 +26,7 @@
"vite-plugin-solid": "^2.8.2"
},
"dependencies": {
- "solid-js": "^1.8.11",
+ "solid-js": "^1.8.18",
"@sentry/solid": "latest || *"
}
}
diff --git a/dev-packages/e2e-tests/test-applications/solidstart/.gitignore b/dev-packages/e2e-tests/test-applications/solidstart/.gitignore
new file mode 100644
index 000000000000..a51ed3c20c8d
--- /dev/null
+++ b/dev-packages/e2e-tests/test-applications/solidstart/.gitignore
@@ -0,0 +1,46 @@
+
+dist
+.solid
+.output
+.vercel
+.netlify
+.vinxi
+
+# Environment
+.env
+.env*.local
+
+# dependencies
+/node_modules
+/.pnp
+.pnp.js
+
+# IDEs and editors
+/.idea
+.project
+.classpath
+*.launch
+.settings/
+
+# Temp
+gitignore
+
+# testing
+/coverage
+
+# misc
+.DS_Store
+.env.local
+.env.development.local
+.env.test.local
+.env.production.local
+
+npm-debug.log*
+yarn-debug.log*
+yarn-error.log*
+
+/test-results/
+/playwright-report/
+/playwright/.cache/
+
+!*.d.ts
diff --git a/dev-packages/e2e-tests/test-applications/solidstart/.npmrc b/dev-packages/e2e-tests/test-applications/solidstart/.npmrc
new file mode 100644
index 000000000000..070f80f05092
--- /dev/null
+++ b/dev-packages/e2e-tests/test-applications/solidstart/.npmrc
@@ -0,0 +1,2 @@
+@sentry:registry=http://127.0.0.1:4873
+@sentry-internal:registry=http://127.0.0.1:4873
diff --git a/dev-packages/e2e-tests/test-applications/solidstart/README.md b/dev-packages/e2e-tests/test-applications/solidstart/README.md
new file mode 100644
index 000000000000..9a141e9c2f0d
--- /dev/null
+++ b/dev-packages/e2e-tests/test-applications/solidstart/README.md
@@ -0,0 +1,45 @@
+# SolidStart
+
+Everything you need to build a Solid project, powered by [`solid-start`](https://start.solidjs.com);
+
+## Creating a project
+
+```bash
+# create a new project in the current directory
+npm init solid@latest
+
+# create a new project in my-app
+npm init solid@latest my-app
+```
+
+## Developing
+
+Once you've created a project and installed dependencies with `npm install` (or `pnpm install` or `yarn`), start a
+development server:
+
+```bash
+npm run dev
+
+# or start the server and open the app in a new browser tab
+npm run dev -- --open
+```
+
+## Building
+
+Solid apps are built with _presets_, which optimise your project for deployment to different environments.
+
+By default, `npm run build` will generate a Node app that you can run with `npm start`. To use a different preset, add
+it to the `devDependencies` in `package.json` and specify in your `app.config.js`.
+
+## Testing
+
+Tests are written with `vitest`, `@solidjs/testing-library` and `@testing-library/jest-dom` to extend expect with some
+helpful custom matchers.
+
+To run them, simply start:
+
+```sh
+npm test
+```
+
+## This project was created with the [Solid CLI](https://solid-cli.netlify.app)
diff --git a/dev-packages/e2e-tests/test-applications/solidstart/app.config.ts b/dev-packages/e2e-tests/test-applications/solidstart/app.config.ts
new file mode 100644
index 000000000000..b3c737efe5ba
--- /dev/null
+++ b/dev-packages/e2e-tests/test-applications/solidstart/app.config.ts
@@ -0,0 +1,3 @@
+import { defineConfig } from '@solidjs/start/config';
+
+export default defineConfig({});
diff --git a/dev-packages/e2e-tests/test-applications/solidstart/package.json b/dev-packages/e2e-tests/test-applications/solidstart/package.json
new file mode 100644
index 000000000000..b4b81ffcbd88
--- /dev/null
+++ b/dev-packages/e2e-tests/test-applications/solidstart/package.json
@@ -0,0 +1,34 @@
+{
+ "name": "example-with-vitest",
+ "version": "0.0.0",
+ "scripts": {
+ "clean": "pnpx rimraf node_modules pnpm-lock.yaml .vinxi .output",
+ "dev": "NODE_OPTIONS='--import ./src/instrument.server.mjs' vinxi dev",
+ "build": "vinxi build",
+ "preview": "HOST=localhost PORT=3030 NODE_OPTIONS='--import ./src/instrument.server.mjs' vinxi start",
+ "test:prod": "TEST_ENV=production playwright test",
+ "test:build": "pnpm install && npx playwright install && pnpm build",
+ "test:assert": "pnpm test:prod"
+ },
+ "type": "module",
+ "dependencies": {
+ "@sentry/solidstart": "latest || *"
+ },
+ "devDependencies": {
+ "@playwright/test": "^1.44.1",
+ "@solidjs/meta": "^0.29.4",
+ "@solidjs/router": "^0.13.4",
+ "@solidjs/start": "^1.0.2",
+ "@solidjs/testing-library": "^0.8.7",
+ "@testing-library/jest-dom": "^6.4.2",
+ "@testing-library/user-event": "^14.5.2",
+ "@vitest/ui": "^1.5.0",
+ "jsdom": "^24.0.0",
+ "solid-js": "1.8.17",
+ "typescript": "^5.4.5",
+ "vinxi": "^0.3.12",
+ "vite": "^5.2.8",
+ "vite-plugin-solid": "^2.10.2",
+ "vitest": "^1.5.0"
+ }
+}
diff --git a/dev-packages/e2e-tests/test-applications/solidstart/playwright.config.mjs b/dev-packages/e2e-tests/test-applications/solidstart/playwright.config.mjs
new file mode 100644
index 000000000000..395acfc282f9
--- /dev/null
+++ b/dev-packages/e2e-tests/test-applications/solidstart/playwright.config.mjs
@@ -0,0 +1,8 @@
+import { getPlaywrightConfig } from '@sentry-internal/test-utils';
+
+const config = getPlaywrightConfig({
+ startCommand: 'pnpm preview',
+ port: 3030,
+});
+
+export default config;
diff --git a/dev-packages/e2e-tests/test-applications/solidstart/public/favicon.ico b/dev-packages/e2e-tests/test-applications/solidstart/public/favicon.ico
new file mode 100644
index 0000000000000000000000000000000000000000..fb282da0719ef6ab4c1732df93be6216b0d85520
GIT binary patch
literal 664
zcmV;J0%!e+P)m9ebk1R
zejT~~6f_`?;`cEd!+`7(hw@%%2;?RN8gX-L?z6cM(
zKoG@&w+0}f@Pfvwc+deid)qgE!L$ENKYjViZC_Zcr>L(`2oXUT8f0mRQ(6-=HN_Ai
zeBBEz3WP+1Cw`m!49Wf!MnZzp5bH8VkR~BcJ1s-j90TAS2Yo4j!J|KodxYR%3Numw
zA?gq6e`5@!W~F$_De3yt&uspo&2yLb$(NwcPPI-4LGc!}HdY%jfq@AFs8LiZ4k(p}
zZ!c9o+qbWYs-Mg
zgdyTALzJX&7QXHdI_DPTFL33;w}88{e6Zk)MX0kN{3DX9uz#O_L58&XRH$Nvvu;fO
zf&)7@?C~$z1K<>j0ga$$MIg+5xN;eQ?1-CA=`^Y169@Ab6!vcaNP=hxfKN%@Ly^R*
zK1iv*s1Yl6_dVyz8>ZqYhz6J4|3fQ@2LQeX@^%W(B~8>=MoEmBEGGD1;gHXlpX>!W
ym)!leA2L@`cpb^hy)P75=I!`pBYxP7<2VfQ3j76qLgzIA0000 (
+
+ SolidStart - with Vitest
+ {props.children}
+
+ )}
+ >
+
+
+ );
+}
diff --git a/dev-packages/e2e-tests/test-applications/solidstart/src/entry-client.tsx b/dev-packages/e2e-tests/test-applications/solidstart/src/entry-client.tsx
new file mode 100644
index 000000000000..cbcd8f9267ca
--- /dev/null
+++ b/dev-packages/e2e-tests/test-applications/solidstart/src/entry-client.tsx
@@ -0,0 +1,16 @@
+// @refresh reload
+import * as Sentry from '@sentry/solidstart';
+import { StartClient, mount } from '@solidjs/start/client';
+
+Sentry.init({
+ // We can't use env variables here, seems like they are stripped
+ // out in production builds.
+ dsn: 'https://public@dsn.ingest.sentry.io/1337',
+ environment: 'qa', // dynamic sampling bias to keep transactions
+ integrations: [Sentry.browserTracingIntegration()],
+ tunnel: 'http://localhost:3031/', // proxy server
+ // Performance Monitoring
+ tracesSampleRate: 1.0, // Capture 100% of the transactions
+});
+
+mount(() => , document.getElementById('app')!);
diff --git a/dev-packages/e2e-tests/test-applications/solidstart/src/entry-server.tsx b/dev-packages/e2e-tests/test-applications/solidstart/src/entry-server.tsx
new file mode 100644
index 000000000000..276935366318
--- /dev/null
+++ b/dev-packages/e2e-tests/test-applications/solidstart/src/entry-server.tsx
@@ -0,0 +1,21 @@
+// @refresh reload
+import { StartServer, createHandler } from '@solidjs/start/server';
+
+export default createHandler(() => (
+ (
+
+
+
+
+
+ {assets}
+
+
+ {children}
+ {scripts}
+
+
+ )}
+ />
+));
diff --git a/dev-packages/e2e-tests/test-applications/solidstart/src/instrument.server.mjs b/dev-packages/e2e-tests/test-applications/solidstart/src/instrument.server.mjs
new file mode 100644
index 000000000000..4146470295e1
--- /dev/null
+++ b/dev-packages/e2e-tests/test-applications/solidstart/src/instrument.server.mjs
@@ -0,0 +1,8 @@
+import * as Sentry from '@sentry/solidstart';
+
+Sentry.init({
+ dsn: process.env.E2E_TEST_DSN,
+ environment: 'qa', // dynamic sampling bias to keep transactions
+ tracesSampleRate: 1.0, // Capture 100% of the transactions
+ tunnel: 'http://localhost:3031/', // proxy server
+});
diff --git a/dev-packages/e2e-tests/test-applications/solidstart/src/routes/client-error.tsx b/dev-packages/e2e-tests/test-applications/solidstart/src/routes/client-error.tsx
new file mode 100644
index 000000000000..e997e4fbb1e3
--- /dev/null
+++ b/dev-packages/e2e-tests/test-applications/solidstart/src/routes/client-error.tsx
@@ -0,0 +1,75 @@
+import * as Sentry from '@sentry/solidstart';
+import type { ParentProps } from 'solid-js';
+import { ErrorBoundary, createSignal, onMount } from 'solid-js';
+
+const SentryErrorBoundary = Sentry.withSentryErrorBoundary(ErrorBoundary);
+
+const [count, setCount] = createSignal(1);
+const [caughtError, setCaughtError] = createSignal(false);
+
+export default function ClientErrorPage() {
+ return (
+
+ {caughtError() && (
+
+ )}
+
+
+
+
+
+
+
+
+
+ );
+}
+
+function Throw(props: { error: string }) {
+ onMount(() => {
+ throw new Error(props.error);
+ });
+ return null;
+}
+
+function SampleErrorBoundary(props: ParentProps) {
+ return (
+ (
+
+ Error Boundary Fallback
+
+ {error.message}
+
+
+
+ )}
+ >
+ {props.children}
+
+ );
+}
diff --git a/dev-packages/e2e-tests/test-applications/solidstart/src/routes/index.tsx b/dev-packages/e2e-tests/test-applications/solidstart/src/routes/index.tsx
new file mode 100644
index 000000000000..a6d09d5b36b5
--- /dev/null
+++ b/dev-packages/e2e-tests/test-applications/solidstart/src/routes/index.tsx
@@ -0,0 +1,17 @@
+import { A } from '@solidjs/router';
+
+export default function Home() {
+ return (
+ <>
+ Welcome to Solid Start
+
+ Visit docs.solidjs.com/solid-start to read the documentation
+
+
+ >
+ );
+}
diff --git a/dev-packages/e2e-tests/test-applications/solidstart/start-event-proxy.mjs b/dev-packages/e2e-tests/test-applications/solidstart/start-event-proxy.mjs
new file mode 100644
index 000000000000..d45a7a013f36
--- /dev/null
+++ b/dev-packages/e2e-tests/test-applications/solidstart/start-event-proxy.mjs
@@ -0,0 +1,6 @@
+import { startEventProxyServer } from '@sentry-internal/test-utils';
+
+startEventProxyServer({
+ port: 3031,
+ proxyServerName: 'solidstart',
+});
diff --git a/dev-packages/e2e-tests/test-applications/solidstart/tests/errorboundary.test.ts b/dev-packages/e2e-tests/test-applications/solidstart/tests/errorboundary.test.ts
new file mode 100644
index 000000000000..a4edf3c46236
--- /dev/null
+++ b/dev-packages/e2e-tests/test-applications/solidstart/tests/errorboundary.test.ts
@@ -0,0 +1,90 @@
+import { expect, test } from '@playwright/test';
+import { waitForError } from '@sentry-internal/test-utils';
+
+test('captures an exception', async ({ page }) => {
+ const errorEventPromise = waitForError('solidstart', errorEvent => {
+ return (
+ !errorEvent.type &&
+ errorEvent.exception?.values?.[0]?.value ===
+ 'Error 1 thrown from Sentry ErrorBoundary in Solid Start E2E test app'
+ );
+ });
+
+ await page.goto('/client-error');
+ await page.locator('#caughtErrorBtn').click();
+ const errorEvent = await errorEventPromise;
+
+ expect(errorEvent).toMatchObject({
+ exception: {
+ values: [
+ {
+ type: 'Error',
+ value: 'Error 1 thrown from Sentry ErrorBoundary in Solid Start E2E test app',
+ mechanism: {
+ type: 'generic',
+ handled: true,
+ },
+ },
+ ],
+ },
+ transaction: '/client-error',
+ });
+});
+
+test('captures a second exception after resetting the boundary', async ({ page }) => {
+ const firstErrorEventPromise = waitForError('solidstart', errorEvent => {
+ return (
+ !errorEvent.type &&
+ errorEvent.exception?.values?.[0]?.value ===
+ 'Error 1 thrown from Sentry ErrorBoundary in Solid Start E2E test app'
+ );
+ });
+
+ await page.goto('/client-error');
+ await page.locator('#caughtErrorBtn').click();
+ const firstErrorEvent = await firstErrorEventPromise;
+
+ expect(firstErrorEvent).toMatchObject({
+ exception: {
+ values: [
+ {
+ type: 'Error',
+ value: 'Error 1 thrown from Sentry ErrorBoundary in Solid Start E2E test app',
+ mechanism: {
+ type: 'generic',
+ handled: true,
+ },
+ },
+ ],
+ },
+ transaction: '/client-error',
+ });
+
+ const secondErrorEventPromise = waitForError('solidstart', errorEvent => {
+ return (
+ !errorEvent.type &&
+ errorEvent.exception?.values?.[0]?.value ===
+ 'Error 2 thrown from Sentry ErrorBoundary in Solid Start E2E test app'
+ );
+ });
+
+ await page.locator('#errorBoundaryResetBtn').click();
+ await page.locator('#caughtErrorBtn').click();
+ const secondErrorEvent = await secondErrorEventPromise;
+
+ expect(secondErrorEvent).toMatchObject({
+ exception: {
+ values: [
+ {
+ type: 'Error',
+ value: 'Error 2 thrown from Sentry ErrorBoundary in Solid Start E2E test app',
+ mechanism: {
+ type: 'generic',
+ handled: true,
+ },
+ },
+ ],
+ },
+ transaction: '/client-error',
+ });
+});
diff --git a/dev-packages/e2e-tests/test-applications/solidstart/tests/errors.client.test.ts b/dev-packages/e2e-tests/test-applications/solidstart/tests/errors.client.test.ts
new file mode 100644
index 000000000000..c9ab1db244b5
--- /dev/null
+++ b/dev-packages/e2e-tests/test-applications/solidstart/tests/errors.client.test.ts
@@ -0,0 +1,32 @@
+import { expect, test } from '@playwright/test';
+import { waitForError } from '@sentry-internal/test-utils';
+
+test.describe('client-side errors', () => {
+ test('captures error thrown on click', async ({ page }) => {
+ const errorPromise = waitForError('solidstart', async errorEvent => {
+ return errorEvent?.exception?.values?.[0]?.value === 'Error thrown from Solid Start E2E test app';
+ });
+
+ await page.goto(`/client-error`);
+ await page.locator('#errorBtn').click();
+ const error = await errorPromise;
+
+ expect(error).toMatchObject({
+ exception: {
+ values: [
+ {
+ type: 'Error',
+ value: 'Error thrown from Solid Start E2E test app',
+ mechanism: {
+ type: 'instrument',
+ handled: false,
+ },
+ },
+ ],
+ },
+ transaction: '/client-error',
+ });
+ expect(error.tags).toMatchObject({ runtime: 'browser' });
+ expect(error.transaction).toEqual('/client-error');
+ });
+});
diff --git a/dev-packages/e2e-tests/test-applications/solidstart/tsconfig.json b/dev-packages/e2e-tests/test-applications/solidstart/tsconfig.json
new file mode 100644
index 000000000000..6f11292cc5d8
--- /dev/null
+++ b/dev-packages/e2e-tests/test-applications/solidstart/tsconfig.json
@@ -0,0 +1,19 @@
+{
+ "compilerOptions": {
+ "target": "ESNext",
+ "module": "ESNext",
+ "moduleResolution": "node",
+ "allowSyntheticDefaultImports": true,
+ "esModuleInterop": true,
+ "jsx": "preserve",
+ "jsxImportSource": "solid-js",
+ "allowJs": true,
+ "strict": true,
+ "noEmit": true,
+ "types": ["vinxi/types/client", "vitest/globals", "@testing-library/jest-dom"],
+ "isolatedModules": true,
+ "paths": {
+ "~/*": ["./src/*"]
+ }
+ }
+}
diff --git a/dev-packages/e2e-tests/test-applications/solidstart/vitest.config.ts b/dev-packages/e2e-tests/test-applications/solidstart/vitest.config.ts
new file mode 100644
index 000000000000..6c2b639dc300
--- /dev/null
+++ b/dev-packages/e2e-tests/test-applications/solidstart/vitest.config.ts
@@ -0,0 +1,10 @@
+import solid from 'vite-plugin-solid';
+import { defineConfig } from 'vitest/config';
+
+export default defineConfig({
+ plugins: [solid()],
+ resolve: {
+ conditions: ['development', 'browser'],
+ },
+ envPrefix: 'PUBLIC_',
+});
From ab4ac07a0cabf1bc7ff0c800212ddb226e899f9f Mon Sep 17 00:00:00 2001
From: Francesco Novy
Date: Thu, 11 Jul 2024 13:02:44 +0200
Subject: [PATCH 12/39] fix(inp): Ensure INP spans have correct transaction
(#12871)
Fixes https://github.com/getsentry/sentry-javascript/issues/12855
Previously, we stored the route name of the pageload in a map for INP
interactions. However, in some frameworks - e.g. remix, but also others,
we update the pageload span name later (since we rely on e.g. react
hooks for this etc).
Since we store the name of the pageload span at the time the first
interaction is recorded, it can thus happen that these run out of sync.
This PR changes this so that instead of the routename itself, we store
the pageload span in a map, and pick the last name of this when
generating the INP span.
I added tests in a remix e2e tests that show the now correct behavior,
these used to fail (because `transaction` on the pageload INP was `/`).
---
.../create-remix-app/app/entry.client.tsx | 1 +
.../create-remix-app/app/routes/user.$id.tsx | 15 +-
.../create-remix-app/tests/client-inp.test.ts | 194 ++++++++++++++++++
packages/browser-utils/src/metrics/inp.ts | 39 ++--
.../src/tracing/browserTracingIntegration.ts | 2 +-
.../src/client/browserTracingIntegration.ts | 20 +-
packages/remix/src/client/performance.tsx | 10 +-
7 files changed, 239 insertions(+), 42 deletions(-)
create mode 100644 dev-packages/e2e-tests/test-applications/create-remix-app/tests/client-inp.test.ts
diff --git a/dev-packages/e2e-tests/test-applications/create-remix-app/app/entry.client.tsx b/dev-packages/e2e-tests/test-applications/create-remix-app/app/entry.client.tsx
index d0c95287e0c9..6a1a4b4c427e 100644
--- a/dev-packages/e2e-tests/test-applications/create-remix-app/app/entry.client.tsx
+++ b/dev-packages/e2e-tests/test-applications/create-remix-app/app/entry.client.tsx
@@ -19,6 +19,7 @@ Sentry.init({
replaysSessionSampleRate: 0.1, // This sets the sample rate at 10%. You may want to change it to 100% while in development and then sample at a lower rate in production.
replaysOnErrorSampleRate: 1.0, // If you're not already sampling the entire session, change the sample rate to 100% when sampling sessions where errors occur.
tunnel: 'http://localhost:3031/', // proxy server
+ release: 'e2e-test',
});
startTransition(() => {
diff --git a/dev-packages/e2e-tests/test-applications/create-remix-app/app/routes/user.$id.tsx b/dev-packages/e2e-tests/test-applications/create-remix-app/app/routes/user.$id.tsx
index 13b2e0a34d1e..ecbeb440219e 100644
--- a/dev-packages/e2e-tests/test-applications/create-remix-app/app/routes/user.$id.tsx
+++ b/dev-packages/e2e-tests/test-applications/create-remix-app/app/routes/user.$id.tsx
@@ -1,3 +1,16 @@
export default function User() {
- return I am a blank page
;
+ return (
+
+
I am a blank page
+
+
+ );
}
diff --git a/dev-packages/e2e-tests/test-applications/create-remix-app/tests/client-inp.test.ts b/dev-packages/e2e-tests/test-applications/create-remix-app/tests/client-inp.test.ts
new file mode 100644
index 000000000000..9469a4462563
--- /dev/null
+++ b/dev-packages/e2e-tests/test-applications/create-remix-app/tests/client-inp.test.ts
@@ -0,0 +1,194 @@
+import { expect, test } from '@playwright/test';
+import { waitForEnvelopeItem, waitForTransaction } from '@sentry-internal/test-utils';
+
+test('sends an INP span during pageload', async ({ page }) => {
+ const inpSpanPromise = waitForEnvelopeItem('create-remix-app', item => {
+ return item[0].type === 'span';
+ });
+
+ await page.goto(`/`);
+
+ await page.click('#exception-button');
+
+ await page.waitForTimeout(500);
+
+ // Page hide to trigger INP
+ await page.evaluate(() => {
+ window.dispatchEvent(new Event('pagehide'));
+ });
+
+ const inpSpan = await inpSpanPromise;
+
+ expect(inpSpan[1]).toEqual({
+ data: {
+ 'sentry.origin': 'auto.http.browser.inp',
+ 'sentry.op': 'ui.interaction.click',
+ release: 'e2e-test',
+ environment: 'qa',
+ transaction: 'routes/_index',
+ 'sentry.exclusive_time': expect.any(Number),
+ 'sentry.sample_rate': 1,
+ 'sentry.source': 'custom',
+ replay_id: expect.any(String),
+ },
+ description: 'body > div > input#exception-button[type="button"]',
+ op: 'ui.interaction.click',
+ parent_span_id: expect.any(String),
+ is_segment: true,
+ span_id: expect.any(String),
+ start_timestamp: expect.any(Number),
+ timestamp: expect.any(Number),
+ trace_id: expect.any(String),
+ origin: 'auto.http.browser.inp',
+ exclusive_time: expect.any(Number),
+ measurements: { inp: { unit: 'millisecond', value: expect.any(Number) } },
+ segment_id: expect.any(String),
+ });
+});
+
+test('sends an INP span after pageload', async ({ page }) => {
+ const transactionPromise = waitForTransaction('create-remix-app', transactionEvent => {
+ return transactionEvent.contexts?.trace?.op === 'pageload' && transactionEvent.transaction === 'routes/_index';
+ });
+
+ await page.goto(`/`);
+
+ await transactionPromise;
+
+ const inpSpanPromise1 = waitForEnvelopeItem('create-remix-app', item => {
+ return item[0].type === 'span';
+ });
+
+ await page.click('#exception-button');
+
+ await page.waitForTimeout(500);
+
+ // Page hide to trigger INP
+ await page.evaluate(() => {
+ window.dispatchEvent(new Event('pagehide'));
+ });
+
+ const inpSpan1 = await inpSpanPromise1;
+
+ expect(inpSpan1[1]).toEqual({
+ data: {
+ 'sentry.origin': 'auto.http.browser.inp',
+ 'sentry.op': 'ui.interaction.click',
+ release: 'e2e-test',
+ environment: 'qa',
+ transaction: 'routes/_index',
+ 'sentry.exclusive_time': expect.any(Number),
+ 'sentry.sample_rate': 1,
+ 'sentry.source': 'custom',
+ replay_id: expect.any(String),
+ },
+ description: 'body > div > input#exception-button[type="button"]',
+ op: 'ui.interaction.click',
+ parent_span_id: expect.any(String),
+ is_segment: true,
+ span_id: expect.any(String),
+ start_timestamp: expect.any(Number),
+ timestamp: expect.any(Number),
+ trace_id: expect.any(String),
+ origin: 'auto.http.browser.inp',
+ exclusive_time: expect.any(Number),
+ measurements: { inp: { unit: 'millisecond', value: expect.any(Number) } },
+ segment_id: expect.any(String),
+ });
+});
+
+test('sends an INP span during navigation', async ({ page }) => {
+ page.on('console', msg => console.log(msg.text()));
+ const inpSpanPromise = waitForEnvelopeItem('create-remix-app', item => {
+ return item[0].type === 'span';
+ });
+
+ await page.goto(`/`);
+
+ await page.click('#navigation');
+
+ await page.waitForTimeout(500);
+
+ // Page hide to trigger INP
+ await page.evaluate(() => {
+ window.dispatchEvent(new Event('pagehide'));
+ });
+
+ const inpSpan = await inpSpanPromise;
+
+ expect(inpSpan[1]).toEqual({
+ data: {
+ 'sentry.origin': 'auto.http.browser.inp',
+ 'sentry.op': 'ui.interaction.click',
+ release: 'e2e-test',
+ environment: 'qa',
+ transaction: 'routes/user.$id',
+ 'sentry.exclusive_time': expect.any(Number),
+ replay_id: expect.any(String),
+ },
+ description: '',
+ op: 'ui.interaction.click',
+ parent_span_id: expect.any(String),
+ span_id: expect.any(String),
+ start_timestamp: expect.any(Number),
+ timestamp: expect.any(Number),
+ trace_id: expect.any(String),
+ origin: 'auto.http.browser.inp',
+ exclusive_time: expect.any(Number),
+ measurements: { inp: { unit: 'millisecond', value: expect.any(Number) } },
+ segment_id: expect.any(String),
+ });
+});
+
+test('sends an INP span after navigation', async ({ page }) => {
+ page.on('console', msg => console.log(msg.text()));
+ const transactionPromise = waitForTransaction('create-remix-app', transactionEvent => {
+ return transactionEvent.contexts?.trace?.op === 'navigation' && transactionEvent.transaction === 'routes/user.$id';
+ });
+
+ await page.goto(`/`);
+
+ await page.click('#navigation');
+
+ await transactionPromise;
+
+ const inpSpanPromise = waitForEnvelopeItem('create-remix-app', item => {
+ return item[0].type === 'span';
+ });
+
+ await page.click('#button');
+
+ await page.waitForTimeout(500);
+
+ // Page hide to trigger INP
+ await page.evaluate(() => {
+ window.dispatchEvent(new Event('pagehide'));
+ });
+
+ const inpSpan = await inpSpanPromise;
+
+ expect(inpSpan[1]).toEqual({
+ data: {
+ 'sentry.origin': 'auto.http.browser.inp',
+ 'sentry.op': 'ui.interaction.click',
+ release: 'e2e-test',
+ environment: 'qa',
+ transaction: 'routes/user.$id',
+ 'sentry.exclusive_time': expect.any(Number),
+ replay_id: expect.any(String),
+ 'sentry.sample_rate': 1,
+ 'sentry.source': 'custom',
+ },
+ description: '',
+ op: 'ui.interaction.click',
+ is_segment: true,
+ span_id: expect.any(String),
+ start_timestamp: expect.any(Number),
+ timestamp: expect.any(Number),
+ trace_id: expect.any(String),
+ origin: 'auto.http.browser.inp',
+ exclusive_time: expect.any(Number),
+ measurements: { inp: { unit: 'millisecond', value: expect.any(Number) } },
+ segment_id: expect.any(String),
+ });
+});
diff --git a/packages/browser-utils/src/metrics/inp.ts b/packages/browser-utils/src/metrics/inp.ts
index 00e524c048b6..1055635bc32f 100644
--- a/packages/browser-utils/src/metrics/inp.ts
+++ b/packages/browser-utils/src/metrics/inp.ts
@@ -10,7 +10,7 @@ import {
spanToJSON,
startInactiveSpan,
} from '@sentry/core';
-import type { Integration, SpanAttributes } from '@sentry/types';
+import type { Integration, Span, SpanAttributes } from '@sentry/types';
import { browserPerformanceTimeOrigin, dropUndefinedKeys, htmlTreeAsString } from '@sentry/utils';
import {
addInpInstrumentationHandler,
@@ -19,13 +19,8 @@ import {
} from './instrument';
import { getBrowserPerformanceAPI, msToSec } from './utils';
-// We only care about name here
-interface PartialRouteInfo {
- name: string | undefined;
-}
-
const LAST_INTERACTIONS: number[] = [];
-const INTERACTIONS_ROUTE_MAP = new Map();
+const INTERACTIONS_SPAN_MAP = new Map();
/**
* Start tracking INP webvital events.
@@ -97,14 +92,15 @@ function _trackINP(): () => void {
const activeSpan = getActiveSpan();
const rootSpan = activeSpan ? getRootSpan(activeSpan) : undefined;
- // We first try to lookup the route name from our INTERACTIONS_ROUTE_MAP,
+ // We first try to lookup the span from our INTERACTIONS_SPAN_MAP,
// where we cache the route per interactionId
- const cachedRouteName = interactionId != null ? INTERACTIONS_ROUTE_MAP.get(interactionId) : undefined;
+ const cachedSpan = interactionId != null ? INTERACTIONS_SPAN_MAP.get(interactionId) : undefined;
+
+ const spanToUse = cachedSpan || rootSpan;
// Else, we try to use the active span.
// Finally, we fall back to look at the transactionName on the scope
- const routeName =
- cachedRouteName || (rootSpan ? spanToJSON(rootSpan).description : scope.getScopeData().transactionName);
+ const routeName = spanToUse ? spanToJSON(spanToUse).description : scope.getScopeData().transactionName;
const user = scope.getUser();
@@ -154,11 +150,17 @@ function _trackINP(): () => void {
});
}
-/** Register a listener to cache route information for INP interactions. */
-export function registerInpInteractionListener(latestRoute: PartialRouteInfo): void {
+/**
+ * Register a listener to cache route information for INP interactions.
+ * TODO(v9): `latestRoute` no longer needs to be passed in and will be removed in v9.
+ */
+export function registerInpInteractionListener(_latestRoute?: unknown): void {
const handleEntries = ({ entries }: { entries: PerformanceEntry[] }): void => {
+ const activeSpan = getActiveSpan();
+ const activeRootSpan = activeSpan && getRootSpan(activeSpan);
+
entries.forEach(entry => {
- if (!isPerformanceEventTiming(entry) || !latestRoute.name) {
+ if (!isPerformanceEventTiming(entry) || !activeRootSpan) {
return;
}
@@ -168,21 +170,20 @@ export function registerInpInteractionListener(latestRoute: PartialRouteInfo): v
}
// If the interaction was already recorded before, nothing more to do
- if (INTERACTIONS_ROUTE_MAP.has(interactionId)) {
+ if (INTERACTIONS_SPAN_MAP.has(interactionId)) {
return;
}
// We keep max. 10 interactions in the list, then remove the oldest one & clean up
if (LAST_INTERACTIONS.length > 10) {
const last = LAST_INTERACTIONS.shift() as number;
- INTERACTIONS_ROUTE_MAP.delete(last);
+ INTERACTIONS_SPAN_MAP.delete(last);
}
// We add the interaction to the list of recorded interactions
- // and store the route information for this interaction
- // (we clone the object because it is mutated when it changes)
+ // and store the span for this interaction
LAST_INTERACTIONS.push(interactionId);
- INTERACTIONS_ROUTE_MAP.set(interactionId, latestRoute.name);
+ INTERACTIONS_SPAN_MAP.set(interactionId, activeRootSpan);
});
};
diff --git a/packages/browser/src/tracing/browserTracingIntegration.ts b/packages/browser/src/tracing/browserTracingIntegration.ts
index 0423831219e2..3deaa195abe3 100644
--- a/packages/browser/src/tracing/browserTracingIntegration.ts
+++ b/packages/browser/src/tracing/browserTracingIntegration.ts
@@ -395,7 +395,7 @@ export const browserTracingIntegration = ((_options: Partial();
-
- if (!client) {
- return;
- }
-
startBrowserTracingPageLoadSpan(client, spanContext);
}
From 38ec761a016ecd42fbf428f8b1799751bda10a49 Mon Sep 17 00:00:00 2001
From: Francesco Novy
Date: Thu, 11 Jul 2024 14:40:48 +0200
Subject: [PATCH 13/39] ci: Run external-collaborator workflow on
pull_request_target (#12877)
See
https://docs.github.com/en/actions/using-workflows/events-that-trigger-workflows#pull_request_target
You can see e.g. here:
https://github.com/getsentry/sentry-javascript/actions/runs/9888865345/job/27313696886
that this lacks permissions when run as `pull_request`. Now, this should
ran in the context of the base branch, which grants proper rights etc.
and also prevents leaking etc.
---
.github/workflows/external-contributors.yml | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/.github/workflows/external-contributors.yml b/.github/workflows/external-contributors.yml
index a0869cc3d2d4..0feac33e1307 100644
--- a/.github/workflows/external-contributors.yml
+++ b/.github/workflows/external-contributors.yml
@@ -1,6 +1,6 @@
name: "CI: Mention external contributors"
on:
- pull_request:
+ pull_request_target:
types:
- closed
branches:
From 9d61b2ca123536c3892d49be36c71d497bf40f29 Mon Sep 17 00:00:00 2001
From: Luca Forstner
Date: Thu, 11 Jul 2024 15:00:23 +0200
Subject: [PATCH 14/39] ci(nextjs): Fix flakey types? (#12881)
---
packages/nextjs/tsconfig.test.json | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/packages/nextjs/tsconfig.test.json b/packages/nextjs/tsconfig.test.json
index 87f6afa06b86..f72f7d93a39e 100644
--- a/packages/nextjs/tsconfig.test.json
+++ b/packages/nextjs/tsconfig.test.json
@@ -5,8 +5,9 @@
"compilerOptions": {
// should include all types from `./tsconfig.json` plus types for all test frameworks used
- "types": ["node", "jest"]
+ "types": ["node", "jest"],
// other package-specific, test-specific options
+ "lib": ["DOM", "ESNext"]
}
}
From d3042486607fdc8822da6a7586a75bc7e73dbeae Mon Sep 17 00:00:00 2001
From: Gabriel Saillard
Date: Thu, 11 Jul 2024 15:22:42 +0200
Subject: [PATCH 15/39] fix(nestjs): do not make SentryTraced() decorated
functions async (#12879)
---
.../e2e-tests/test-applications/nestjs/src/app.service.ts | 8 +++++---
packages/nestjs/src/span-decorator.ts | 4 ++--
2 files changed, 7 insertions(+), 5 deletions(-)
diff --git a/dev-packages/e2e-tests/test-applications/nestjs/src/app.service.ts b/dev-packages/e2e-tests/test-applications/nestjs/src/app.service.ts
index b6fd70769e1f..f5666bffeb46 100644
--- a/dev-packages/e2e-tests/test-applications/nestjs/src/app.service.ts
+++ b/dev-packages/e2e-tests/test-applications/nestjs/src/app.service.ts
@@ -99,12 +99,14 @@ export class AppService1 {
}
@SentryTraced('return a string')
- getString(): string {
- return 'test';
+ getString(): { result: string } {
+ return { result: 'test' };
}
async testSpanDecoratorSync() {
- return this.getString();
+ const returned = this.getString();
+ // Will fail if getString() is async, because returned will be a Promise<>
+ return returned.result;
}
/*
diff --git a/packages/nestjs/src/span-decorator.ts b/packages/nestjs/src/span-decorator.ts
index c56056a26621..b9ef861bc3b2 100644
--- a/packages/nestjs/src/span-decorator.ts
+++ b/packages/nestjs/src/span-decorator.ts
@@ -6,7 +6,7 @@ import { startSpan } from '@sentry/node';
export function SentryTraced(op: string = 'function') {
return function (target: unknown, propertyKey: string, descriptor: PropertyDescriptor) {
// eslint-disable-next-line @typescript-eslint/no-explicit-any
- const originalMethod = descriptor.value as (...args: any[]) => Promise;
+ const originalMethod = descriptor.value as (...args: any[]) => Promise | any; // function can be sync or async
// eslint-disable-next-line @typescript-eslint/no-explicit-any
descriptor.value = function (...args: any[]) {
@@ -15,7 +15,7 @@ export function SentryTraced(op: string = 'function') {
op: op,
name: propertyKey,
},
- async () => {
+ () => {
return originalMethod.apply(this, args);
},
);
From 17bf308a93f5140d33effdba68e0b63ddb59f4dd Mon Sep 17 00:00:00 2001
From: Abhijeet Prasad
Date: Thu, 11 Jul 2024 10:31:49 -0400
Subject: [PATCH 16/39] feat: feat(cloudflare): Add basic cloudflare package
and tests (#12861)
resolves https://github.com/getsentry/sentry-javascript/issues/12685
Initial attempt:
https://github.com/getsentry/sentry-javascript/pull/12733
This PR introduces `@sentry/cloudflare`, our package for cloudflare
pages and cloudflare workers. It also adds a basic test with
[`miniflare` ](https://miniflare.dev/), the local simulator for
cloudflare workers.
In the next step, I'll add basic SDK initialization + error monitoring
for the cloudflare pages use case.
---
.../e2e-tests/verdaccio-config/config.yaml | 6 +
package.json | 1 +
packages/cloudflare/.eslintrc.js | 11 +
packages/cloudflare/LICENSE | 21 +
packages/cloudflare/README.md | 23 ++
packages/cloudflare/package.json | 76 ++++
packages/cloudflare/rollup.npm.config.mjs | 3 +
packages/cloudflare/src/debug-build.ts | 8 +
packages/cloudflare/src/index.ts | 1 +
packages/cloudflare/test/fixtures/worker.mjs | 8 +
packages/cloudflare/test/index.test.ts | 17 +
packages/cloudflare/test/tsconfig.json | 3 +
packages/cloudflare/tsconfig.json | 9 +
packages/cloudflare/tsconfig.test.json | 9 +
packages/cloudflare/tsconfig.types.json | 10 +
scripts/node-unit-tests.ts | 3 +-
yarn.lock | 389 +++++++++++++++++-
17 files changed, 593 insertions(+), 5 deletions(-)
create mode 100644 packages/cloudflare/.eslintrc.js
create mode 100644 packages/cloudflare/LICENSE
create mode 100644 packages/cloudflare/README.md
create mode 100644 packages/cloudflare/package.json
create mode 100644 packages/cloudflare/rollup.npm.config.mjs
create mode 100644 packages/cloudflare/src/debug-build.ts
create mode 100644 packages/cloudflare/src/index.ts
create mode 100644 packages/cloudflare/test/fixtures/worker.mjs
create mode 100644 packages/cloudflare/test/index.test.ts
create mode 100644 packages/cloudflare/test/tsconfig.json
create mode 100644 packages/cloudflare/tsconfig.json
create mode 100644 packages/cloudflare/tsconfig.test.json
create mode 100644 packages/cloudflare/tsconfig.types.json
diff --git a/dev-packages/e2e-tests/verdaccio-config/config.yaml b/dev-packages/e2e-tests/verdaccio-config/config.yaml
index 973d2173aefa..67ee55a9d9ce 100644
--- a/dev-packages/e2e-tests/verdaccio-config/config.yaml
+++ b/dev-packages/e2e-tests/verdaccio-config/config.yaml
@@ -62,6 +62,12 @@ packages:
unpublish: $all
# proxy: npmjs # Don't proxy for E2E tests!
+ '@sentry/cloudflare':
+ access: $all
+ publish: $all
+ unpublish: $all
+ # proxy: npmjs # Don't proxy for E2E tests!
+
'@sentry/deno':
access: $all
publish: $all
diff --git a/package.json b/package.json
index 549053f23b9a..3d2a137ab650 100644
--- a/package.json
+++ b/package.json
@@ -51,6 +51,7 @@
"packages/browser-utils",
"packages/bun",
"packages/core",
+ "packages/cloudflare",
"packages/deno",
"packages/ember",
"packages/eslint-config-sdk",
diff --git a/packages/cloudflare/.eslintrc.js b/packages/cloudflare/.eslintrc.js
new file mode 100644
index 000000000000..9d915d4f4c3b
--- /dev/null
+++ b/packages/cloudflare/.eslintrc.js
@@ -0,0 +1,11 @@
+module.exports = {
+ env: {
+ node: true,
+ },
+ extends: ['../../.eslintrc.js'],
+ rules: {
+ '@sentry-internal/sdk/no-optional-chaining': 'off',
+ '@sentry-internal/sdk/no-nullish-coalescing': 'off',
+ '@sentry-internal/sdk/no-class-field-initializers': 'off',
+ },
+};
diff --git a/packages/cloudflare/LICENSE b/packages/cloudflare/LICENSE
new file mode 100644
index 000000000000..63e7eb28e19c
--- /dev/null
+++ b/packages/cloudflare/LICENSE
@@ -0,0 +1,21 @@
+MIT License
+
+Copyright (c) 2024 Functional Software, Inc. dba Sentry
+
+Permission is hereby granted, free of charge, to any person obtaining a copy of
+this software and associated documentation files (the "Software"), to deal in
+the Software without restriction, including without limitation the rights to
+use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
+of the Software, and to permit persons to whom the Software is furnished to do
+so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
diff --git a/packages/cloudflare/README.md b/packages/cloudflare/README.md
new file mode 100644
index 000000000000..e85a64c490df
--- /dev/null
+++ b/packages/cloudflare/README.md
@@ -0,0 +1,23 @@
+
+
+
+
+
+
+# Official Sentry SDK for Cloudflare [UNRELEASED]
+
+[](https://www.npmjs.com/package/@sentry/cloudflare)
+[](https://www.npmjs.com/package/@sentry/cloudflare)
+[](https://www.npmjs.com/package/@sentry/cloudflare)
+
+## Links
+
+- [Official SDK Docs](https://docs.sentry.io/quickstart/)
+- [TypeDoc](http://getsentry.github.io/sentry-javascript/)
+
+**Note: This SDK is unreleased. Please follow the
+[tracking GH issue](https://github.com/getsentry/sentry-javascript/issues/12620) for updates.**
+
+## Usage
+
+TODO: Add usage instructions here.
diff --git a/packages/cloudflare/package.json b/packages/cloudflare/package.json
new file mode 100644
index 000000000000..eff219dd1cf8
--- /dev/null
+++ b/packages/cloudflare/package.json
@@ -0,0 +1,76 @@
+{
+ "name": "@sentry/cloudflare",
+ "version": "8.17.0",
+ "description": "Offical Sentry SDK for Cloudflare Workers and Pages",
+ "repository": "git://github.com/getsentry/sentry-javascript.git",
+ "homepage": "https://github.com/getsentry/sentry-javascript/tree/master/packages/cloudflare",
+ "author": "Sentry",
+ "license": "MIT",
+ "engines": {
+ "node": ">=14.18"
+ },
+ "files": [
+ "/build"
+ ],
+ "main": "build/cjs/index.js",
+ "module": "build/esm/index.js",
+ "types": "build/types/index.d.ts",
+ "exports": {
+ "./package.json": "./package.json",
+ ".": {
+ "import": {
+ "types": "./build/types/index.d.ts",
+ "default": "./build/esm/index.js"
+ },
+ "require": {
+ "types": "./build/types/index.d.ts",
+ "default": "./build/cjs/index.js"
+ }
+ }
+ },
+ "typesVersions": {
+ "<4.9": {
+ "build/types/index.d.ts": [
+ "build/types-ts3.8/index.d.ts"
+ ]
+ }
+ },
+ "publishConfig": {
+ "access": "public"
+ },
+ "dependencies": {
+ "@sentry/core": "8.17.0",
+ "@sentry/types": "8.17.0",
+ "@sentry/utils": "8.17.0"
+ },
+ "devDependencies": {
+ "@cloudflare/workers-types": "^4.20240620.0",
+ "miniflare": "^3.20240701.0",
+ "wrangler": "^3.63.2"
+ },
+ "scripts": {
+ "build": "run-p build:transpile build:types",
+ "build:dev": "yarn build",
+ "build:transpile": "rollup -c rollup.npm.config.mjs",
+ "build:types": "run-s build:types:core build:types:downlevel",
+ "build:types:core": "tsc -p tsconfig.types.json",
+ "build:types:downlevel": "yarn downlevel-dts build/types build/types-ts3.8 --to ts3.8",
+ "build:watch": "run-p build:transpile:watch build:types:watch",
+ "build:dev:watch": "yarn build:watch",
+ "build:transpile:watch": "rollup -c rollup.npm.config.mjs --watch",
+ "build:types:watch": "tsc -p tsconfig.types.json --watch",
+ "build:tarball": "npm pack",
+ "circularDepCheck": "madge --circular src/index.ts",
+ "clean": "rimraf build coverage sentry-cloudflare-*.tgz",
+ "fix": "eslint . --format stylish --fix",
+ "lint": "eslint . --format stylish",
+ "test": "yarn test:unit",
+ "test:unit": "vitest run",
+ "test:watch": "vitest --watch",
+ "yalc:publish": "yalc publish --push --sig"
+ },
+ "volta": {
+ "extends": "../../package.json"
+ },
+ "sideEffects": false
+}
diff --git a/packages/cloudflare/rollup.npm.config.mjs b/packages/cloudflare/rollup.npm.config.mjs
new file mode 100644
index 000000000000..84a06f2fb64a
--- /dev/null
+++ b/packages/cloudflare/rollup.npm.config.mjs
@@ -0,0 +1,3 @@
+import { makeBaseNPMConfig, makeNPMConfigVariants } from '@sentry-internal/rollup-utils';
+
+export default makeNPMConfigVariants(makeBaseNPMConfig());
diff --git a/packages/cloudflare/src/debug-build.ts b/packages/cloudflare/src/debug-build.ts
new file mode 100644
index 000000000000..60aa50940582
--- /dev/null
+++ b/packages/cloudflare/src/debug-build.ts
@@ -0,0 +1,8 @@
+declare const __DEBUG_BUILD__: boolean;
+
+/**
+ * This serves as a build time flag that will be true by default, but false in non-debug builds or if users replace `__SENTRY_DEBUG__` in their generated code.
+ *
+ * ATTENTION: This constant must never cross package boundaries (i.e. be exported) to guarantee that it can be used for tree shaking.
+ */
+export const DEBUG_BUILD = __DEBUG_BUILD__;
diff --git a/packages/cloudflare/src/index.ts b/packages/cloudflare/src/index.ts
new file mode 100644
index 000000000000..cb0ff5c3b541
--- /dev/null
+++ b/packages/cloudflare/src/index.ts
@@ -0,0 +1 @@
+export {};
diff --git a/packages/cloudflare/test/fixtures/worker.mjs b/packages/cloudflare/test/fixtures/worker.mjs
new file mode 100644
index 000000000000..2023f7471c43
--- /dev/null
+++ b/packages/cloudflare/test/fixtures/worker.mjs
@@ -0,0 +1,8 @@
+/**
+ * @type {import('@cloudflare/workers-types').ExportedHandler}
+ */
+export default {
+ async fetch(_request, _env, _ctx) {
+ return new Response('Hello Sentry!');
+ },
+};
diff --git a/packages/cloudflare/test/index.test.ts b/packages/cloudflare/test/index.test.ts
new file mode 100644
index 000000000000..30bd1f0962f6
--- /dev/null
+++ b/packages/cloudflare/test/index.test.ts
@@ -0,0 +1,17 @@
+import { describe, expect, test } from 'vitest';
+
+import { Miniflare } from 'miniflare';
+
+describe('index', () => {
+ test('simple test', async () => {
+ const mf = new Miniflare({
+ scriptPath: './test/fixtures/worker.mjs',
+ modules: true,
+ port: 8787,
+ });
+
+ const res = await mf.dispatchFetch('http://localhost:8787/');
+ expect(await res.text()).toBe('Hello Sentry!');
+ await mf.dispose();
+ });
+});
diff --git a/packages/cloudflare/test/tsconfig.json b/packages/cloudflare/test/tsconfig.json
new file mode 100644
index 000000000000..120c3aff3716
--- /dev/null
+++ b/packages/cloudflare/test/tsconfig.json
@@ -0,0 +1,3 @@
+{
+ "extends": "../tsconfig.test.json",
+}
diff --git a/packages/cloudflare/tsconfig.json b/packages/cloudflare/tsconfig.json
new file mode 100644
index 000000000000..18b3ec720bfe
--- /dev/null
+++ b/packages/cloudflare/tsconfig.json
@@ -0,0 +1,9 @@
+{
+ "extends": "../../tsconfig.json",
+
+ "include": ["src/**/*"],
+
+ "compilerOptions": {
+ "types": ["@cloudflare/workers-types"]
+ }
+}
diff --git a/packages/cloudflare/tsconfig.test.json b/packages/cloudflare/tsconfig.test.json
new file mode 100644
index 000000000000..42d9d0df227e
--- /dev/null
+++ b/packages/cloudflare/tsconfig.test.json
@@ -0,0 +1,9 @@
+{
+ "extends": "./tsconfig.json",
+
+ "include": ["test/**/*"],
+
+ "compilerOptions": {
+ // other package-specific, test-specific options
+ }
+}
diff --git a/packages/cloudflare/tsconfig.types.json b/packages/cloudflare/tsconfig.types.json
new file mode 100644
index 000000000000..65455f66bd75
--- /dev/null
+++ b/packages/cloudflare/tsconfig.types.json
@@ -0,0 +1,10 @@
+{
+ "extends": "./tsconfig.json",
+
+ "compilerOptions": {
+ "declaration": true,
+ "declarationMap": true,
+ "emitDeclarationOnly": true,
+ "outDir": "build/types"
+ }
+}
diff --git a/scripts/node-unit-tests.ts b/scripts/node-unit-tests.ts
index 233cb99b22b3..81ea2b0badf3 100644
--- a/scripts/node-unit-tests.ts
+++ b/scripts/node-unit-tests.ts
@@ -31,6 +31,7 @@ const DEFAULT_SKIP_TESTS_PACKAGES = [
const SKIP_TEST_PACKAGES: Record = {
'14': {
ignoredPackages: [
+ '@sentry/cloudflare',
'@sentry/solidstart',
'@sentry/sveltekit',
'@sentry/vercel-edge',
@@ -40,7 +41,7 @@ const SKIP_TEST_PACKAGES: Record = {
],
},
'16': {
- ignoredPackages: ['@sentry/vercel-edge', '@sentry/astro'],
+ ignoredPackages: ['@sentry/cloudflare', '@sentry/vercel-edge', '@sentry/astro'],
},
'18': {
ignoredPackages: [],
diff --git a/yarn.lock b/yarn.lock
index a31caa49a1ac..1319545eda21 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -3825,6 +3825,13 @@
resolved "https://registry.yarnpkg.com/@biomejs/cli-win32-x64/-/cli-win32-x64-1.4.0.tgz#0bb1292c5e279198912b6ec35649124ba8349b72"
integrity sha512-gN6DgyyBxIwoCovAUFJHFWVallb0cLosayDRtNyxU3MDv/atZxSXOWQezfVKBIbgmFPxYWJObd+awvbPYXwwww==
+"@cloudflare/kv-asset-handler@0.3.4":
+ version "0.3.4"
+ resolved "https://registry.yarnpkg.com/@cloudflare/kv-asset-handler/-/kv-asset-handler-0.3.4.tgz#5cc152847c8ae4d280ec5d7f4f6ba8c976b585c3"
+ integrity sha512-YLPHc8yASwjNkmcDMQMY35yiWjoKAKnhUbPRszBRS0YgH+IXtsMp61j+yTcnCE3oO2DgP0U3iejLC8FTtKDC8Q==
+ dependencies:
+ mime "^3.0.0"
+
"@cloudflare/kv-asset-handler@^0.3.1":
version "0.3.3"
resolved "https://registry.yarnpkg.com/@cloudflare/kv-asset-handler/-/kv-asset-handler-0.3.3.tgz#3c578996f3d00b60debee8178c41600f3b21bc0b"
@@ -3832,6 +3839,36 @@
dependencies:
mime "^3.0.0"
+"@cloudflare/workerd-darwin-64@1.20240701.0":
+ version "1.20240701.0"
+ resolved "https://registry.yarnpkg.com/@cloudflare/workerd-darwin-64/-/workerd-darwin-64-1.20240701.0.tgz#064d8ded54443ac8d4181bdb2d93113f7fb63c81"
+ integrity sha512-XAZa4ZP+qyTn6JQQACCPH09hGZXP2lTnWKkmg5mPwT8EyRzCKLkczAf98vPP5bq7JZD/zORdFWRY0dOTap8zTQ==
+
+"@cloudflare/workerd-darwin-arm64@1.20240701.0":
+ version "1.20240701.0"
+ resolved "https://registry.yarnpkg.com/@cloudflare/workerd-darwin-arm64/-/workerd-darwin-arm64-1.20240701.0.tgz#042e49592bf9ef9e74d7f85c885cc3bda356c96c"
+ integrity sha512-w80ZVAgfH4UwTz7fXZtk7KmS2FzlXniuQm4ku4+cIgRTilBAuKqjpOjwUCbx5g13Gqcm9NuiHce+IDGtobRTIQ==
+
+"@cloudflare/workerd-linux-64@1.20240701.0":
+ version "1.20240701.0"
+ resolved "https://registry.yarnpkg.com/@cloudflare/workerd-linux-64/-/workerd-linux-64-1.20240701.0.tgz#5ff73dcd0b0615877baa0ae4fa057ea244e326f3"
+ integrity sha512-UWLr/Anxwwe/25nGv451MNd2jhREmPt/ws17DJJqTLAx6JxwGWA15MeitAIzl0dbxRFAJa+0+R8ag2WR3F/D6g==
+
+"@cloudflare/workerd-linux-arm64@1.20240701.0":
+ version "1.20240701.0"
+ resolved "https://registry.yarnpkg.com/@cloudflare/workerd-linux-arm64/-/workerd-linux-arm64-1.20240701.0.tgz#b0e5e5bf00fb41ac94f93f7dea7ffd306f468685"
+ integrity sha512-3kCnF9kYgov1ggpuWbgpXt4stPOIYtVmPCa7MO2xhhA0TWP6JDUHRUOsnmIgKrvDjXuXqlK16cdg3v+EWsaPJg==
+
+"@cloudflare/workerd-windows-64@1.20240701.0":
+ version "1.20240701.0"
+ resolved "https://registry.yarnpkg.com/@cloudflare/workerd-windows-64/-/workerd-windows-64-1.20240701.0.tgz#710583329e7fef26092fdccf021e669434cc6acb"
+ integrity sha512-6IPGITRAeS67j3BH1rN4iwYWDt47SqJG7KlZJ5bB4UaNAia4mvMBSy/p2p4vA89bbXoDRjMtEvRu7Robu6O7hQ==
+
+"@cloudflare/workers-types@^4.20240620.0":
+ version "4.20240620.0"
+ resolved "https://registry.yarnpkg.com/@cloudflare/workers-types/-/workers-types-4.20240620.0.tgz#1e996c0b81a1dab392f0292bea036fd7bb3b73f3"
+ integrity sha512-CQD8YS6evRob7LChvIX3gE3zYo0KVgaLDOu1SwNP1BVIS2Sa0b+FC8S1e1hhrNN8/E4chYlVN+FDAgA4KRDUEQ==
+
"@cnakazawa/watch@^1.0.3":
version "1.0.4"
resolved "https://registry.yarnpkg.com/@cnakazawa/watch/-/watch-1.0.4.tgz#f864ae85004d0fcab6f50be9141c4da368d1656a"
@@ -3845,7 +3882,7 @@
resolved "https://registry.yarnpkg.com/@colors/colors/-/colors-1.5.0.tgz#bb504579c1cae923e6576a4f5da43d25f97bdbd9"
integrity sha512-ooWCrlZP11i8GImSjTHYHLkvFDP48nS4+204nGb1RiX/WXYHmJA2III9/e2DWVabCESdW7hBAEzHRqUn9OUVvQ==
-"@cspotcode/source-map-support@^0.8.0":
+"@cspotcode/source-map-support@0.8.1", "@cspotcode/source-map-support@^0.8.0":
version "0.8.1"
resolved "https://registry.yarnpkg.com/@cspotcode/source-map-support/-/source-map-support-0.8.1.tgz#00629c35a688e05a88b1cda684fb9d5e73f000a1"
integrity sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw==
@@ -4102,6 +4139,19 @@
broccoli-funnel "^3.0.5"
ember-cli-babel "^8.2.0"
+"@esbuild-plugins/node-globals-polyfill@^0.2.3":
+ version "0.2.3"
+ resolved "https://registry.yarnpkg.com/@esbuild-plugins/node-globals-polyfill/-/node-globals-polyfill-0.2.3.tgz#0e4497a2b53c9e9485e149bc92ddb228438d6bcf"
+ integrity sha512-r3MIryXDeXDOZh7ih1l/yE9ZLORCd5e8vWg02azWRGj5SPTuoh69A2AIyn0Z31V/kHBfZ4HgWJ+OK3GTTwLmnw==
+
+"@esbuild-plugins/node-modules-polyfill@^0.2.2":
+ version "0.2.2"
+ resolved "https://registry.yarnpkg.com/@esbuild-plugins/node-modules-polyfill/-/node-modules-polyfill-0.2.2.tgz#cefa3dc0bd1c16277a8338b52833420c94987327"
+ integrity sha512-LXV7QsWJxRuMYvKbiznh+U1ilIop3g2TeKRzUxOG5X3YITc8JyyTa90BmLwqqv0YnX4v32CSlG+vsziZp9dMvA==
+ dependencies:
+ escape-string-regexp "^4.0.0"
+ rollup-plugin-node-polyfills "^0.2.1"
+
"@esbuild/aix-ppc64@0.20.0":
version "0.20.0"
resolved "https://registry.yarnpkg.com/@esbuild/aix-ppc64/-/aix-ppc64-0.20.0.tgz#509621cca4e67caf0d18561a0c56f8b70237472f"
@@ -4117,6 +4167,11 @@
resolved "https://registry.yarnpkg.com/@esbuild/aix-ppc64/-/aix-ppc64-0.21.5.tgz#c7184a326533fcdf1b8ee0733e21c713b975575f"
integrity sha512-1SDgH6ZSPTlggy1yI6+Dbkiz8xzpHJEVAlF/AM1tHPLsf5STom9rwtjE4hKAF20FfXXNTFqEYXyJNWh1GiZedQ==
+"@esbuild/android-arm64@0.17.19":
+ version "0.17.19"
+ resolved "https://registry.yarnpkg.com/@esbuild/android-arm64/-/android-arm64-0.17.19.tgz#bafb75234a5d3d1b690e7c2956a599345e84a2fd"
+ integrity sha512-KBMWvEZooR7+kzY0BtbTQn0OAYY7CsiydT63pVEaPtVYF0hXbUaOyZog37DKxK7NF3XacBJOpYT4adIJh+avxA==
+
"@esbuild/android-arm64@0.18.20":
version "0.18.20"
resolved "https://registry.yarnpkg.com/@esbuild/android-arm64/-/android-arm64-0.18.20.tgz#984b4f9c8d0377443cc2dfcef266d02244593622"
@@ -4152,6 +4207,11 @@
resolved "https://registry.yarnpkg.com/@esbuild/android-arm/-/android-arm-0.15.18.tgz#266d40b8fdcf87962df8af05b76219bc786b4f80"
integrity sha512-5GT+kcs2WVGjVs7+boataCkO5Fg0y4kCjzkB5bAip7H4jfnOS3dA6KPiww9W1OEKTKeAcUVhdZGvgI65OXmUnw==
+"@esbuild/android-arm@0.17.19":
+ version "0.17.19"
+ resolved "https://registry.yarnpkg.com/@esbuild/android-arm/-/android-arm-0.17.19.tgz#5898f7832c2298bc7d0ab53701c57beb74d78b4d"
+ integrity sha512-rIKddzqhmav7MSmoFCmDIb6e2W57geRsM94gV2l38fzhXMwq7hZoClug9USI2pFRGL06f4IOPHHpFNOkWieR8A==
+
"@esbuild/android-arm@0.18.20":
version "0.18.20"
resolved "https://registry.yarnpkg.com/@esbuild/android-arm/-/android-arm-0.18.20.tgz#fedb265bc3a589c84cc11f810804f234947c3682"
@@ -4182,6 +4242,11 @@
resolved "https://registry.yarnpkg.com/@esbuild/android-arm/-/android-arm-0.21.5.tgz#9b04384fb771926dfa6d7ad04324ecb2ab9b2e28"
integrity sha512-vCPvzSjpPHEi1siZdlvAlsPxXl7WbOVUBBAowWug4rJHb68Ox8KualB+1ocNvT5fjv6wpkX6o/iEpbDrf68zcg==
+"@esbuild/android-x64@0.17.19":
+ version "0.17.19"
+ resolved "https://registry.yarnpkg.com/@esbuild/android-x64/-/android-x64-0.17.19.tgz#658368ef92067866d95fb268719f98f363d13ae1"
+ integrity sha512-uUTTc4xGNDT7YSArp/zbtmbhO0uEEK9/ETW29Wk1thYUJBz3IVnvgEiEwEa9IeLyvnpKrWK64Utw2bgUmDveww==
+
"@esbuild/android-x64@0.18.20":
version "0.18.20"
resolved "https://registry.yarnpkg.com/@esbuild/android-x64/-/android-x64-0.18.20.tgz#35cf419c4cfc8babe8893d296cd990e9e9f756f2"
@@ -4212,6 +4277,11 @@
resolved "https://registry.yarnpkg.com/@esbuild/android-x64/-/android-x64-0.21.5.tgz#29918ec2db754cedcb6c1b04de8cd6547af6461e"
integrity sha512-D7aPRUUNHRBwHxzxRvp856rjUHRFW1SdQATKXH2hqA0kAZb1hKmi02OpYRacl0TxIGz/ZmXWlbZgjwWYaCakTA==
+"@esbuild/darwin-arm64@0.17.19":
+ version "0.17.19"
+ resolved "https://registry.yarnpkg.com/@esbuild/darwin-arm64/-/darwin-arm64-0.17.19.tgz#584c34c5991b95d4d48d333300b1a4e2ff7be276"
+ integrity sha512-80wEoCfF/hFKM6WE1FyBHc9SfUblloAWx6FJkFWTWiCoht9Mc0ARGEM47e67W9rI09YoUxJL68WHfDRYEAvOhg==
+
"@esbuild/darwin-arm64@0.18.20":
version "0.18.20"
resolved "https://registry.yarnpkg.com/@esbuild/darwin-arm64/-/darwin-arm64-0.18.20.tgz#08172cbeccf95fbc383399a7f39cfbddaeb0d7c1"
@@ -4242,6 +4312,11 @@
resolved "https://registry.yarnpkg.com/@esbuild/darwin-arm64/-/darwin-arm64-0.21.5.tgz#e495b539660e51690f3928af50a76fb0a6ccff2a"
integrity sha512-DwqXqZyuk5AiWWf3UfLiRDJ5EDd49zg6O9wclZ7kUMv2WRFr4HKjXp/5t8JZ11QbQfUS6/cRCKGwYhtNAY88kQ==
+"@esbuild/darwin-x64@0.17.19":
+ version "0.17.19"
+ resolved "https://registry.yarnpkg.com/@esbuild/darwin-x64/-/darwin-x64-0.17.19.tgz#7751d236dfe6ce136cce343dce69f52d76b7f6cb"
+ integrity sha512-IJM4JJsLhRYr9xdtLytPLSH9k/oxR3boaUIYiHkAawtwNOXKE8KoU8tMvryogdcT8AU+Bflmh81Xn6Q0vTZbQw==
+
"@esbuild/darwin-x64@0.18.20":
version "0.18.20"
resolved "https://registry.yarnpkg.com/@esbuild/darwin-x64/-/darwin-x64-0.18.20.tgz#d70d5790d8bf475556b67d0f8b7c5bdff053d85d"
@@ -4272,6 +4347,11 @@
resolved "https://registry.yarnpkg.com/@esbuild/darwin-x64/-/darwin-x64-0.21.5.tgz#c13838fa57372839abdddc91d71542ceea2e1e22"
integrity sha512-se/JjF8NlmKVG4kNIuyWMV/22ZaerB+qaSi5MdrXtd6R08kvs2qCN4C09miupktDitvh8jRFflwGFBQcxZRjbw==
+"@esbuild/freebsd-arm64@0.17.19":
+ version "0.17.19"
+ resolved "https://registry.yarnpkg.com/@esbuild/freebsd-arm64/-/freebsd-arm64-0.17.19.tgz#cacd171665dd1d500f45c167d50c6b7e539d5fd2"
+ integrity sha512-pBwbc7DufluUeGdjSU5Si+P3SoMF5DQ/F/UmTSb8HXO80ZEAJmrykPyzo1IfNbAoaqw48YRpv8shwd1NoI0jcQ==
+
"@esbuild/freebsd-arm64@0.18.20":
version "0.18.20"
resolved "https://registry.yarnpkg.com/@esbuild/freebsd-arm64/-/freebsd-arm64-0.18.20.tgz#98755cd12707f93f210e2494d6a4b51b96977f54"
@@ -4302,6 +4382,11 @@
resolved "https://registry.yarnpkg.com/@esbuild/freebsd-arm64/-/freebsd-arm64-0.21.5.tgz#646b989aa20bf89fd071dd5dbfad69a3542e550e"
integrity sha512-5JcRxxRDUJLX8JXp/wcBCy3pENnCgBR9bN6JsY4OmhfUtIHe3ZW0mawA7+RDAcMLrMIZaf03NlQiX9DGyB8h4g==
+"@esbuild/freebsd-x64@0.17.19":
+ version "0.17.19"
+ resolved "https://registry.yarnpkg.com/@esbuild/freebsd-x64/-/freebsd-x64-0.17.19.tgz#0769456eee2a08b8d925d7c00b79e861cb3162e4"
+ integrity sha512-4lu+n8Wk0XlajEhbEffdy2xy53dpR06SlzvhGByyg36qJw6Kpfk7cp45DR/62aPH9mtJRmIyrXAS5UWBrJT6TQ==
+
"@esbuild/freebsd-x64@0.18.20":
version "0.18.20"
resolved "https://registry.yarnpkg.com/@esbuild/freebsd-x64/-/freebsd-x64-0.18.20.tgz#c1eb2bff03915f87c29cece4c1a7fa1f423b066e"
@@ -4332,6 +4417,11 @@
resolved "https://registry.yarnpkg.com/@esbuild/freebsd-x64/-/freebsd-x64-0.21.5.tgz#aa615cfc80af954d3458906e38ca22c18cf5c261"
integrity sha512-J95kNBj1zkbMXtHVH29bBriQygMXqoVQOQYA+ISs0/2l3T9/kj42ow2mpqerRBxDJnmkUDCaQT/dfNXWX/ZZCQ==
+"@esbuild/linux-arm64@0.17.19":
+ version "0.17.19"
+ resolved "https://registry.yarnpkg.com/@esbuild/linux-arm64/-/linux-arm64-0.17.19.tgz#38e162ecb723862c6be1c27d6389f48960b68edb"
+ integrity sha512-ct1Tg3WGwd3P+oZYqic+YZF4snNl2bsnMKRkb3ozHmnM0dGWuxcPTTntAF6bOP0Sp4x0PjSF+4uHQ1xvxfRKqg==
+
"@esbuild/linux-arm64@0.18.20":
version "0.18.20"
resolved "https://registry.yarnpkg.com/@esbuild/linux-arm64/-/linux-arm64-0.18.20.tgz#bad4238bd8f4fc25b5a021280c770ab5fc3a02a0"
@@ -4362,6 +4452,11 @@
resolved "https://registry.yarnpkg.com/@esbuild/linux-arm64/-/linux-arm64-0.21.5.tgz#70ac6fa14f5cb7e1f7f887bcffb680ad09922b5b"
integrity sha512-ibKvmyYzKsBeX8d8I7MH/TMfWDXBF3db4qM6sy+7re0YXya+K1cem3on9XgdT2EQGMu4hQyZhan7TeQ8XkGp4Q==
+"@esbuild/linux-arm@0.17.19":
+ version "0.17.19"
+ resolved "https://registry.yarnpkg.com/@esbuild/linux-arm/-/linux-arm-0.17.19.tgz#1a2cd399c50040184a805174a6d89097d9d1559a"
+ integrity sha512-cdmT3KxjlOQ/gZ2cjfrQOtmhG4HJs6hhvm3mWSRDPtZ/lP5oe8FWceS10JaSJC13GBd4eH/haHnqf7hhGNLerA==
+
"@esbuild/linux-arm@0.18.20":
version "0.18.20"
resolved "https://registry.yarnpkg.com/@esbuild/linux-arm/-/linux-arm-0.18.20.tgz#3e617c61f33508a27150ee417543c8ab5acc73b0"
@@ -4392,6 +4487,11 @@
resolved "https://registry.yarnpkg.com/@esbuild/linux-arm/-/linux-arm-0.21.5.tgz#fc6fd11a8aca56c1f6f3894f2bea0479f8f626b9"
integrity sha512-bPb5AHZtbeNGjCKVZ9UGqGwo8EUu4cLq68E95A53KlxAPRmUyYv2D6F0uUI65XisGOL1hBP5mTronbgo+0bFcA==
+"@esbuild/linux-ia32@0.17.19":
+ version "0.17.19"
+ resolved "https://registry.yarnpkg.com/@esbuild/linux-ia32/-/linux-ia32-0.17.19.tgz#e28c25266b036ce1cabca3c30155222841dc035a"
+ integrity sha512-w4IRhSy1VbsNxHRQpeGCHEmibqdTUx61Vc38APcsRbuVgK0OPEnQ0YD39Brymn96mOx48Y2laBQGqgZ0j9w6SQ==
+
"@esbuild/linux-ia32@0.18.20":
version "0.18.20"
resolved "https://registry.yarnpkg.com/@esbuild/linux-ia32/-/linux-ia32-0.18.20.tgz#699391cccba9aee6019b7f9892eb99219f1570a7"
@@ -4432,6 +4532,11 @@
resolved "https://registry.yarnpkg.com/@esbuild/linux-loong64/-/linux-loong64-0.15.5.tgz#91aef76d332cdc7c8942b600fa2307f3387e6f82"
integrity sha512-UHkDFCfSGTuXq08oQltXxSZmH1TXyWsL+4QhZDWvvLl6mEJQqk3u7/wq1LjhrrAXYIllaTtRSzUXl4Olkf2J8A==
+"@esbuild/linux-loong64@0.17.19":
+ version "0.17.19"
+ resolved "https://registry.yarnpkg.com/@esbuild/linux-loong64/-/linux-loong64-0.17.19.tgz#0f887b8bb3f90658d1a0117283e55dbd4c9dcf72"
+ integrity sha512-2iAngUbBPMq439a+z//gE+9WBldoMp1s5GWsUSgqHLzLJ9WoZLZhpwWuym0u0u/4XmZ3gpHmzV84PonE+9IIdQ==
+
"@esbuild/linux-loong64@0.18.20":
version "0.18.20"
resolved "https://registry.yarnpkg.com/@esbuild/linux-loong64/-/linux-loong64-0.18.20.tgz#e6fccb7aac178dd2ffb9860465ac89d7f23b977d"
@@ -4462,6 +4567,11 @@
resolved "https://registry.yarnpkg.com/@esbuild/linux-loong64/-/linux-loong64-0.21.5.tgz#ed62e04238c57026aea831c5a130b73c0f9f26df"
integrity sha512-uHf1BmMG8qEvzdrzAqg2SIG/02+4/DHB6a9Kbya0XDvwDEKCoC8ZRWI5JJvNdUjtciBGFQ5PuBlpEOXQj+JQSg==
+"@esbuild/linux-mips64el@0.17.19":
+ version "0.17.19"
+ resolved "https://registry.yarnpkg.com/@esbuild/linux-mips64el/-/linux-mips64el-0.17.19.tgz#f5d2a0b8047ea9a5d9f592a178ea054053a70289"
+ integrity sha512-LKJltc4LVdMKHsrFe4MGNPp0hqDFA1Wpt3jE1gEyM3nKUvOiO//9PheZZHfYRfYl6AwdTH4aTcXSqBerX0ml4A==
+
"@esbuild/linux-mips64el@0.18.20":
version "0.18.20"
resolved "https://registry.yarnpkg.com/@esbuild/linux-mips64el/-/linux-mips64el-0.18.20.tgz#eeff3a937de9c2310de30622a957ad1bd9183231"
@@ -4492,6 +4602,11 @@
resolved "https://registry.yarnpkg.com/@esbuild/linux-mips64el/-/linux-mips64el-0.21.5.tgz#e79b8eb48bf3b106fadec1ac8240fb97b4e64cbe"
integrity sha512-IajOmO+KJK23bj52dFSNCMsz1QP1DqM6cwLUv3W1QwyxkyIWecfafnI555fvSGqEKwjMXVLokcV5ygHW5b3Jbg==
+"@esbuild/linux-ppc64@0.17.19":
+ version "0.17.19"
+ resolved "https://registry.yarnpkg.com/@esbuild/linux-ppc64/-/linux-ppc64-0.17.19.tgz#876590e3acbd9fa7f57a2c7d86f83717dbbac8c7"
+ integrity sha512-/c/DGybs95WXNS8y3Ti/ytqETiW7EU44MEKuCAcpPto3YjQbyK3IQVKfF6nbghD7EcLUGl0NbiL5Rt5DMhn5tg==
+
"@esbuild/linux-ppc64@0.18.20":
version "0.18.20"
resolved "https://registry.yarnpkg.com/@esbuild/linux-ppc64/-/linux-ppc64-0.18.20.tgz#2f7156bde20b01527993e6881435ad79ba9599fb"
@@ -4522,6 +4637,11 @@
resolved "https://registry.yarnpkg.com/@esbuild/linux-ppc64/-/linux-ppc64-0.21.5.tgz#5f2203860a143b9919d383ef7573521fb154c3e4"
integrity sha512-1hHV/Z4OEfMwpLO8rp7CvlhBDnjsC3CttJXIhBi+5Aj5r+MBvy4egg7wCbe//hSsT+RvDAG7s81tAvpL2XAE4w==
+"@esbuild/linux-riscv64@0.17.19":
+ version "0.17.19"
+ resolved "https://registry.yarnpkg.com/@esbuild/linux-riscv64/-/linux-riscv64-0.17.19.tgz#7f49373df463cd9f41dc34f9b2262d771688bf09"
+ integrity sha512-FC3nUAWhvFoutlhAkgHf8f5HwFWUL6bYdvLc/TTuxKlvLi3+pPzdZiFKSWz/PF30TB1K19SuCxDTI5KcqASJqA==
+
"@esbuild/linux-riscv64@0.18.20":
version "0.18.20"
resolved "https://registry.yarnpkg.com/@esbuild/linux-riscv64/-/linux-riscv64-0.18.20.tgz#6628389f210123d8b4743045af8caa7d4ddfc7a6"
@@ -4552,6 +4672,11 @@
resolved "https://registry.yarnpkg.com/@esbuild/linux-riscv64/-/linux-riscv64-0.21.5.tgz#07bcafd99322d5af62f618cb9e6a9b7f4bb825dc"
integrity sha512-2HdXDMd9GMgTGrPWnJzP2ALSokE/0O5HhTUvWIbD3YdjME8JwvSCnNGBnTThKGEB91OZhzrJ4qIIxk/SBmyDDA==
+"@esbuild/linux-s390x@0.17.19":
+ version "0.17.19"
+ resolved "https://registry.yarnpkg.com/@esbuild/linux-s390x/-/linux-s390x-0.17.19.tgz#e2afd1afcaf63afe2c7d9ceacd28ec57c77f8829"
+ integrity sha512-IbFsFbxMWLuKEbH+7sTkKzL6NJmG2vRyy6K7JJo55w+8xDk7RElYn6xvXtDW8HCfoKBFK69f3pgBJSUSQPr+4Q==
+
"@esbuild/linux-s390x@0.18.20":
version "0.18.20"
resolved "https://registry.yarnpkg.com/@esbuild/linux-s390x/-/linux-s390x-0.18.20.tgz#255e81fb289b101026131858ab99fba63dcf0071"
@@ -4582,6 +4707,11 @@
resolved "https://registry.yarnpkg.com/@esbuild/linux-s390x/-/linux-s390x-0.21.5.tgz#b7ccf686751d6a3e44b8627ababc8be3ef62d8de"
integrity sha512-zus5sxzqBJD3eXxwvjN1yQkRepANgxE9lgOW2qLnmr8ikMTphkjgXu1HR01K4FJg8h1kEEDAqDcZQtbrRnB41A==
+"@esbuild/linux-x64@0.17.19":
+ version "0.17.19"
+ resolved "https://registry.yarnpkg.com/@esbuild/linux-x64/-/linux-x64-0.17.19.tgz#8a0e9738b1635f0c53389e515ae83826dec22aa4"
+ integrity sha512-68ngA9lg2H6zkZcyp22tsVt38mlhWde8l3eJLWkyLrp4HwMUr3c1s/M2t7+kHIhvMjglIBrFpncX1SzMckomGw==
+
"@esbuild/linux-x64@0.18.20":
version "0.18.20"
resolved "https://registry.yarnpkg.com/@esbuild/linux-x64/-/linux-x64-0.18.20.tgz#c7690b3417af318a9b6f96df3031a8865176d338"
@@ -4612,6 +4742,11 @@
resolved "https://registry.yarnpkg.com/@esbuild/linux-x64/-/linux-x64-0.21.5.tgz#6d8f0c768e070e64309af8004bb94e68ab2bb3b0"
integrity sha512-1rYdTpyv03iycF1+BhzrzQJCdOuAOtaqHTWJZCWvijKD2N5Xu0TtVC8/+1faWqcP9iBCWOmjmhoH94dH82BxPQ==
+"@esbuild/netbsd-x64@0.17.19":
+ version "0.17.19"
+ resolved "https://registry.yarnpkg.com/@esbuild/netbsd-x64/-/netbsd-x64-0.17.19.tgz#c29fb2453c6b7ddef9a35e2c18b37bda1ae5c462"
+ integrity sha512-CwFq42rXCR8TYIjIfpXCbRX0rp1jo6cPIUPSaWwzbVI4aOfX96OXY8M6KNmtPcg7QjYeDmN+DD0Wp3LaBOLf4Q==
+
"@esbuild/netbsd-x64@0.18.20":
version "0.18.20"
resolved "https://registry.yarnpkg.com/@esbuild/netbsd-x64/-/netbsd-x64-0.18.20.tgz#30e8cd8a3dded63975e2df2438ca109601ebe0d1"
@@ -4642,6 +4777,11 @@
resolved "https://registry.yarnpkg.com/@esbuild/netbsd-x64/-/netbsd-x64-0.21.5.tgz#bbe430f60d378ecb88decb219c602667387a6047"
integrity sha512-Woi2MXzXjMULccIwMnLciyZH4nCIMpWQAs049KEeMvOcNADVxo0UBIQPfSmxB3CWKedngg7sWZdLvLczpe0tLg==
+"@esbuild/openbsd-x64@0.17.19":
+ version "0.17.19"
+ resolved "https://registry.yarnpkg.com/@esbuild/openbsd-x64/-/openbsd-x64-0.17.19.tgz#95e75a391403cb10297280d524d66ce04c920691"
+ integrity sha512-cnq5brJYrSZ2CF6c35eCmviIN3k3RczmHz8eYaVlNasVqsNY+JKohZU5MKmaOI+KkllCdzOKKdPs762VCPC20g==
+
"@esbuild/openbsd-x64@0.18.20":
version "0.18.20"
resolved "https://registry.yarnpkg.com/@esbuild/openbsd-x64/-/openbsd-x64-0.18.20.tgz#7812af31b205055874c8082ea9cf9ab0da6217ae"
@@ -4672,6 +4812,11 @@
resolved "https://registry.yarnpkg.com/@esbuild/openbsd-x64/-/openbsd-x64-0.21.5.tgz#99d1cf2937279560d2104821f5ccce220cb2af70"
integrity sha512-HLNNw99xsvx12lFBUwoT8EVCsSvRNDVxNpjZ7bPn947b8gJPzeHWyNVhFsaerc0n3TsbOINvRP2byTZ5LKezow==
+"@esbuild/sunos-x64@0.17.19":
+ version "0.17.19"
+ resolved "https://registry.yarnpkg.com/@esbuild/sunos-x64/-/sunos-x64-0.17.19.tgz#722eaf057b83c2575937d3ffe5aeb16540da7273"
+ integrity sha512-vCRT7yP3zX+bKWFeP/zdS6SqdWB8OIpaRq/mbXQxTGHnIxspRtigpkUcDMlSCOejlHowLqII7K2JKevwyRP2rg==
+
"@esbuild/sunos-x64@0.18.20":
version "0.18.20"
resolved "https://registry.yarnpkg.com/@esbuild/sunos-x64/-/sunos-x64-0.18.20.tgz#d5c275c3b4e73c9b0ecd38d1ca62c020f887ab9d"
@@ -4702,6 +4847,11 @@
resolved "https://registry.yarnpkg.com/@esbuild/sunos-x64/-/sunos-x64-0.21.5.tgz#08741512c10d529566baba837b4fe052c8f3487b"
integrity sha512-6+gjmFpfy0BHU5Tpptkuh8+uw3mnrvgs+dSPQXQOv3ekbordwnzTVEb4qnIvQcYXq6gzkyTnoZ9dZG+D4garKg==
+"@esbuild/win32-arm64@0.17.19":
+ version "0.17.19"
+ resolved "https://registry.yarnpkg.com/@esbuild/win32-arm64/-/win32-arm64-0.17.19.tgz#9aa9dc074399288bdcdd283443e9aeb6b9552b6f"
+ integrity sha512-yYx+8jwowUstVdorcMdNlzklLYhPxjniHWFKgRqH7IFlUEa0Umu3KuYplf1HUZZ422e3NU9F4LGb+4O0Kdcaag==
+
"@esbuild/win32-arm64@0.18.20":
version "0.18.20"
resolved "https://registry.yarnpkg.com/@esbuild/win32-arm64/-/win32-arm64-0.18.20.tgz#73bc7f5a9f8a77805f357fab97f290d0e4820ac9"
@@ -4732,6 +4882,11 @@
resolved "https://registry.yarnpkg.com/@esbuild/win32-arm64/-/win32-arm64-0.21.5.tgz#675b7385398411240735016144ab2e99a60fc75d"
integrity sha512-Z0gOTd75VvXqyq7nsl93zwahcTROgqvuAcYDUr+vOv8uHhNSKROyU961kgtCD1e95IqPKSQKH7tBTslnS3tA8A==
+"@esbuild/win32-ia32@0.17.19":
+ version "0.17.19"
+ resolved "https://registry.yarnpkg.com/@esbuild/win32-ia32/-/win32-ia32-0.17.19.tgz#95ad43c62ad62485e210f6299c7b2571e48d2b03"
+ integrity sha512-eggDKanJszUtCdlVs0RB+h35wNlb5v4TWEkq4vZcmVt5u/HiDZrTXe2bWFQUez3RgNHwx/x4sk5++4NSSicKkw==
+
"@esbuild/win32-ia32@0.18.20":
version "0.18.20"
resolved "https://registry.yarnpkg.com/@esbuild/win32-ia32/-/win32-ia32-0.18.20.tgz#ec93cbf0ef1085cc12e71e0d661d20569ff42102"
@@ -4762,6 +4917,11 @@
resolved "https://registry.yarnpkg.com/@esbuild/win32-ia32/-/win32-ia32-0.21.5.tgz#1bfc3ce98aa6ca9a0969e4d2af72144c59c1193b"
integrity sha512-SWXFF1CL2RVNMaVs+BBClwtfZSvDgtL//G/smwAc5oVK/UPu2Gu9tIaRgFmYFFKrmg3SyAjSrElf0TiJ1v8fYA==
+"@esbuild/win32-x64@0.17.19":
+ version "0.17.19"
+ resolved "https://registry.yarnpkg.com/@esbuild/win32-x64/-/win32-x64-0.17.19.tgz#8cfaf2ff603e9aabb910e9c0558c26cf32744061"
+ integrity sha512-lAhycmKnVOuRYNtRtatQR1LPQf2oYCkRGkSFnseDAKPl8lu5SOsK/e1sXe5a0Pc5kHIHe6P2I/ilntNv2xf3cA==
+
"@esbuild/win32-x64@0.18.20":
version "0.18.20"
resolved "https://registry.yarnpkg.com/@esbuild/win32-x64/-/win32-x64-0.18.20.tgz#786c5f41f043b07afb1af37683d7c33668858f6d"
@@ -11324,6 +11484,13 @@ acorn-walk@^8.1.1:
resolved "https://registry.yarnpkg.com/acorn-walk/-/acorn-walk-8.2.0.tgz#741210f2e2426454508853a2f44d0ab83b7f69c1"
integrity sha512-k+iyHEuPgSw6SbuDpGQM+06HQUa04DZ3o+F6CSzXMvvI5KMvnaEqXe+YVe555R9nn6GPt404fos4wcgpw12SDA==
+acorn-walk@^8.2.0:
+ version "8.3.3"
+ resolved "https://registry.yarnpkg.com/acorn-walk/-/acorn-walk-8.3.3.tgz#9caeac29eefaa0c41e3d4c65137de4d6f34df43e"
+ integrity sha512-MxXdReSRhGO7VlFe1bRG/oI7/mdLV9B9JJT0N8vZOhF7gFRR5l3M8W9G8JxmKV+JC5mGqJ0QvqfSOLsCPa4nUw==
+ dependencies:
+ acorn "^8.11.0"
+
acorn@8.12.0, acorn@^8.11.0, acorn@^8.6.0:
version "8.12.0"
resolved "https://registry.yarnpkg.com/acorn/-/acorn-8.12.0.tgz#1627bfa2e058148036133b8d9b51a700663c294c"
@@ -11354,6 +11521,11 @@ acorn@^8.2.4, acorn@^8.4.1, acorn@^8.5.0, acorn@^8.7.0, acorn@^8.7.1:
resolved "https://registry.yarnpkg.com/acorn/-/acorn-8.8.0.tgz#88c0187620435c7f6015803f5539dae05a9dbea8"
integrity sha512-QOxyigPVrpZ2GXT+PFyZTl6TtOFc5egxHIP9IlQ+RbupQuX4RkT/Bee4/kQuC02Xkzg84JcT7oLYtDIQxp+v7w==
+acorn@^8.8.0:
+ version "8.12.1"
+ resolved "https://registry.yarnpkg.com/acorn/-/acorn-8.12.1.tgz#71616bdccbe25e27a54439e0046e89ca76df2248"
+ integrity sha512-tcpGyI9zbizT9JbV6oYE477V6mTlXvvi0T0G3SNIYE2apm/G5huBa1+K89VGeovbg+jycCrfhl3ADxErOuO6Jg==
+
acorn@^8.8.1, acorn@^8.8.2:
version "8.8.2"
resolved "https://registry.yarnpkg.com/acorn/-/acorn-8.8.2.tgz#1b2f25db02af965399b9776b0c2c391276d37c4a"
@@ -11973,6 +12145,13 @@ arrify@^2.0.0, arrify@^2.0.1:
resolved "https://registry.yarnpkg.com/arrify/-/arrify-2.0.1.tgz#c9655e9331e0abcd588d2a7cad7e9956f66701fa"
integrity sha512-3duEwti880xqi4eAMN8AyR4a0ByT90zoYdLlevfrvU43vb0YZwZVfxOgxWrLXXXpyugL0hNZc9G6BiB5B3nUug==
+as-table@^1.0.36:
+ version "1.0.55"
+ resolved "https://registry.yarnpkg.com/as-table/-/as-table-1.0.55.tgz#dc984da3937745de902cea1d45843c01bdbbec4f"
+ integrity sha512-xvsWESUJn0JN421Xb9MQw6AsMHRCUknCe0Wjlxvjud80mU4E6hQf1A6NzQKcYNmYw62MfzEtXc+badstZP3JpQ==
+ dependencies:
+ printable-characters "^1.0.42"
+
asap@~2.0.3:
version "2.0.6"
resolved "https://registry.yarnpkg.com/asap/-/asap-2.0.6.tgz#e50347611d7e690943208bbdafebcbc2fb866d46"
@@ -12754,6 +12933,11 @@ bl@^5.0.0:
inherits "^2.0.4"
readable-stream "^3.4.0"
+blake3-wasm@^2.1.5:
+ version "2.1.5"
+ resolved "https://registry.yarnpkg.com/blake3-wasm/-/blake3-wasm-2.1.5.tgz#b22dbb84bc9419ed0159caa76af4b1b132e6ba52"
+ integrity sha512-F1+K8EbfOZE49dtoPtmxUQrpXaBIl3ICvasLh+nJta0xkz+9kF/7uet9fLnwKqhDrmj6g+6K3Tw9yQPUg2ka5g==
+
blank-object@^1.0.1:
version "1.0.2"
resolved "https://registry.yarnpkg.com/blank-object/-/blank-object-1.0.2.tgz#f990793fbe9a8c8dd013fb3219420bec81d5f4b9"
@@ -13962,6 +14146,14 @@ caniuse-lite@^1.0.30001400, caniuse-lite@^1.0.30001406, caniuse-lite@^1.0.300015
resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001614.tgz#f894b4209376a0bf923d67d9c361d96b1dfebe39"
integrity sha512-jmZQ1VpmlRwHgdP1/uiKzgiAuGOfLEJsYFP4+GBou/QQ4U6IOJCB4NP1c+1p9RGLpwObcT94jA5/uO+F1vBbog==
+capnp-ts@^0.7.0:
+ version "0.7.0"
+ resolved "https://registry.yarnpkg.com/capnp-ts/-/capnp-ts-0.7.0.tgz#16fd8e76b667d002af8fcf4bf92bf15d1a7b54a9"
+ integrity sha512-XKxXAC3HVPv7r674zP0VC3RTXz+/JKhfyw94ljvF80yynK6VkTnqE3jMuN8b3dUVmmc43TjyxjW4KTsmB3c86g==
+ dependencies:
+ debug "^4.3.1"
+ tslib "^2.2.0"
+
capture-exit@^2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/capture-exit/-/capture-exit-2.0.0.tgz#fb953bfaebeb781f62898239dabb426d08a509a4"
@@ -15393,6 +15585,11 @@ dargs@^7.0.0:
resolved "https://registry.yarnpkg.com/dargs/-/dargs-7.0.0.tgz#04015c41de0bcb69ec84050f3d9be0caf8d6d5cc"
integrity sha512-2iy1EkLdlBzQGvbweYRFxmFath8+K7+AKB0TlhHWkNuH+TmovaMH/Wp7V7R4u7f4SnX3OgLsU9t1NI9ioDnUpg==
+data-uri-to-buffer@^2.0.0:
+ version "2.0.2"
+ resolved "https://registry.yarnpkg.com/data-uri-to-buffer/-/data-uri-to-buffer-2.0.2.tgz#d296973d5a4897a5dbe31716d118211921f04770"
+ integrity sha512-ND9qDTLc6diwj+Xe5cdAgVTbLVdXbtxTJRXRhli8Mowuaan+0EJOtdqJ0QCHNSSPyoXGx9HX2/VMnKeC34AChA==
+
data-uri-to-buffer@^3.0.1:
version "3.0.1"
resolved "https://registry.yarnpkg.com/data-uri-to-buffer/-/data-uri-to-buffer-3.0.1.tgz#594b8973938c5bc2c33046535785341abc4f3636"
@@ -15421,6 +15618,11 @@ date-fns@^2.29.2:
resolved "https://registry.yarnpkg.com/date-fns/-/date-fns-2.29.3.tgz#27402d2fc67eb442b511b70bbdf98e6411cd68a8"
integrity sha512-dDCnyH2WnnKusqvZZ6+jA1O51Ibt8ZMRNkDZdyAyK4YfbDwa/cEmuztzG5pk6hqlp9aSBPYcjOlktquahGwGeA==
+date-fns@^3.6.0:
+ version "3.6.0"
+ resolved "https://registry.yarnpkg.com/date-fns/-/date-fns-3.6.0.tgz#f20ca4fe94f8b754951b24240676e8618c0206bf"
+ integrity sha512-fRHTG8g/Gif+kSh50gaGEdToemgfj74aRX3swtiouboip5JDLAyDE9F11nHMIcvOaXeOC6D7SpNhi7uFyB7Uww==
+
dateformat@^3.0.3:
version "3.0.3"
resolved "https://registry.yarnpkg.com/dateformat/-/dateformat-3.0.3.tgz#a6e37499a4d9a9cf85ef5872044d62901c9889ae"
@@ -17508,6 +17710,34 @@ esbuild@0.15.5:
esbuild-windows-64 "0.15.5"
esbuild-windows-arm64 "0.15.5"
+esbuild@0.17.19:
+ version "0.17.19"
+ resolved "https://registry.yarnpkg.com/esbuild/-/esbuild-0.17.19.tgz#087a727e98299f0462a3d0bcdd9cd7ff100bd955"
+ integrity sha512-XQ0jAPFkK/u3LcVRcvVHQcTIqD6E2H1fvZMA5dQPSOWb3suUbWbfbRf94pjc0bNzRYLfIrDRQXr7X+LHIm5oHw==
+ optionalDependencies:
+ "@esbuild/android-arm" "0.17.19"
+ "@esbuild/android-arm64" "0.17.19"
+ "@esbuild/android-x64" "0.17.19"
+ "@esbuild/darwin-arm64" "0.17.19"
+ "@esbuild/darwin-x64" "0.17.19"
+ "@esbuild/freebsd-arm64" "0.17.19"
+ "@esbuild/freebsd-x64" "0.17.19"
+ "@esbuild/linux-arm" "0.17.19"
+ "@esbuild/linux-arm64" "0.17.19"
+ "@esbuild/linux-ia32" "0.17.19"
+ "@esbuild/linux-loong64" "0.17.19"
+ "@esbuild/linux-mips64el" "0.17.19"
+ "@esbuild/linux-ppc64" "0.17.19"
+ "@esbuild/linux-riscv64" "0.17.19"
+ "@esbuild/linux-s390x" "0.17.19"
+ "@esbuild/linux-x64" "0.17.19"
+ "@esbuild/netbsd-x64" "0.17.19"
+ "@esbuild/openbsd-x64" "0.17.19"
+ "@esbuild/sunos-x64" "0.17.19"
+ "@esbuild/win32-arm64" "0.17.19"
+ "@esbuild/win32-ia32" "0.17.19"
+ "@esbuild/win32-x64" "0.17.19"
+
esbuild@0.20.0:
version "0.20.0"
resolved "https://registry.yarnpkg.com/esbuild/-/esbuild-0.20.0.tgz#a7170b63447286cd2ff1f01579f09970e6965da4"
@@ -18242,6 +18472,11 @@ exists-sync@^0.1.0:
resolved "https://registry.yarnpkg.com/exists-sync/-/exists-sync-0.1.0.tgz#318d545213d2b2a31499e92c35f74c94196a22f7"
integrity sha512-qEfFekfBVid4b14FNug/RNY1nv+BADnlzKGHulc+t6ZLqGY4kdHGh1iFha8lnE3sJU/1WzMzKRNxS6EvSakJUg==
+exit-hook@^2.2.1:
+ version "2.2.1"
+ resolved "https://registry.yarnpkg.com/exit-hook/-/exit-hook-2.2.1.tgz#007b2d92c6428eda2b76e7016a34351586934593"
+ integrity sha512-eNTPlAD67BmP31LDINZ3U7HSF8l57TxOY2PmBJ1shpCvpnxBF93mWCE8YHBnXs8qiUZJc9WDcWIeC3a2HIAMfw==
+
exit@^0.1.2:
version "0.1.2"
resolved "https://registry.yarnpkg.com/exit/-/exit-0.1.2.tgz#0632638f8d877cc82107d30a0fff1a17cba1cd0c"
@@ -19321,6 +19556,14 @@ get-port@5.1.1, get-port@^5.1.1:
resolved "https://registry.npmjs.org/get-port/-/get-port-5.1.1.tgz#0469ed07563479de6efb986baf053dcd7d4e3193"
integrity sha512-g/Q1aTSDOxFpchXC4i8ZWvxA1lnPqx/JHqcpIw0/LX9T8x/GBbi6YnlN5nhaKIFkT8oFsscUKgDJYxfwfS6QsQ==
+get-source@^2.0.12:
+ version "2.0.12"
+ resolved "https://registry.yarnpkg.com/get-source/-/get-source-2.0.12.tgz#0b47d57ea1e53ce0d3a69f4f3d277eb8047da944"
+ integrity sha512-X5+4+iD+HoSeEED+uwrQ07BOQr0kEDFMVqqpBuI+RaZBpBpHCuXxo70bjar6f0b0u/DQJsJ7ssurpP0V60Az+w==
+ dependencies:
+ data-uri-to-buffer "^2.0.0"
+ source-map "^0.6.1"
+
get-stdin@^4.0.1:
version "4.0.1"
resolved "https://registry.yarnpkg.com/get-stdin/-/get-stdin-4.0.1.tgz#b968c6b0a04384324902e8bf1a5df32579a450fe"
@@ -23728,7 +23971,7 @@ magic-string@0.30.8, magic-string@~0.30.0:
dependencies:
"@jridgewell/sourcemap-codec" "^1.4.15"
-magic-string@^0.25.7:
+magic-string@^0.25.3, magic-string@^0.25.7:
version "0.25.9"
resolved "https://registry.yarnpkg.com/magic-string/-/magic-string-0.25.9.tgz#de7f9faf91ef8a1c91d02c2e5314c8277dbcdd1c"
integrity sha512-RmF0AsMzgt25qzqqLc1+MbHmhdx0ojF2Fvs4XnOqz2ZOBXzzkEwc/dJQZCYHAn7v1jbVOjAZfK8msRn4BxO4VQ==
@@ -24668,6 +24911,24 @@ mini-css-extract-plugin@2.6.1, mini-css-extract-plugin@^2.5.2:
dependencies:
schema-utils "^4.0.0"
+miniflare@3.20240701.0, miniflare@^3.20240701.0:
+ version "3.20240701.0"
+ resolved "https://registry.yarnpkg.com/miniflare/-/miniflare-3.20240701.0.tgz#1c23b45baa65ed199da7d94c55d93f69cb4d48d2"
+ integrity sha512-m9+I+7JNyqDGftCMKp9cK9pCZkK72hAL2mM9IWwhct+ZmucLBA8Uu6+rHQqA5iod86cpwOkrB2PrPA3wx9YNgw==
+ dependencies:
+ "@cspotcode/source-map-support" "0.8.1"
+ acorn "^8.8.0"
+ acorn-walk "^8.2.0"
+ capnp-ts "^0.7.0"
+ exit-hook "^2.2.1"
+ glob-to-regexp "^0.4.1"
+ stoppable "^1.1.0"
+ undici "^5.28.4"
+ workerd "1.20240701.0"
+ ws "^8.17.1"
+ youch "^3.2.2"
+ zod "^3.22.3"
+
minimalistic-assert@^1.0.0, minimalistic-assert@^1.0.1:
version "1.0.1"
resolved "https://registry.yarnpkg.com/minimalistic-assert/-/minimalistic-assert-1.0.1.tgz#2e194de044626d4a10e7f7fbc00ce73e83e4d5c7"
@@ -25268,7 +25529,7 @@ nan@^2.12.1:
resolved "https://registry.yarnpkg.com/nan/-/nan-2.14.2.tgz#f5376400695168f4cc694ac9393d0c9585eeea19"
integrity sha512-M2ufzIiINKCuDfBSAUr1vWQ+vuVcA9kqx8JJUsbQi6yf1uGRyb7HfpdfUr5qLXf3B/t8dPvcjhKMmlfnP47EzQ==
-nanoid@^3.3.4, nanoid@^3.3.7:
+nanoid@^3.3.3, nanoid@^3.3.4, nanoid@^3.3.7:
version "3.3.7"
resolved "https://registry.yarnpkg.com/nanoid/-/nanoid-3.3.7.tgz#d0c301a691bc8d54efa0a2226ccf3fe2fd656bd8"
integrity sha512-eSRppjcPIatRIMC1U6UngP8XFcz8MQWGQdt1MTBQ7NaAmvXDfvNxbvWV3x2y6CdEUciCSsDHDQZbhYaB8QEo2g==
@@ -27293,6 +27554,11 @@ path-to-regexp@^1.5.3, path-to-regexp@^1.7.0:
dependencies:
isarray "0.0.1"
+path-to-regexp@^6.2.0:
+ version "6.2.2"
+ resolved "https://registry.yarnpkg.com/path-to-regexp/-/path-to-regexp-6.2.2.tgz#324377a83e5049cbecadc5554d6a63a9a4866b36"
+ integrity sha512-GQX3SSMokngb36+whdpRXE+3f9V8UzyAorlYvOGx87ufGHehNTn5lCxrKtLyZ4Yl/wEKnNnr98ZzOwwDZV5ogw==
+
path-to-regexp@^6.2.1:
version "6.2.1"
resolved "https://registry.yarnpkg.com/path-to-regexp/-/path-to-regexp-6.2.1.tgz#d54934d6798eb9e5ef14e7af7962c945906918e5"
@@ -28442,6 +28708,11 @@ pretty-ms@^7.0.1:
dependencies:
parse-ms "^2.1.0"
+printable-characters@^1.0.42:
+ version "1.0.42"
+ resolved "https://registry.yarnpkg.com/printable-characters/-/printable-characters-1.0.42.tgz#3f18e977a9bd8eb37fcc4ff5659d7be90868b3d8"
+ integrity sha512-dKp+C4iXWK4vVYZmYSd0KBH5F/h1HoZRsbJ82AVKRO3PEo8L4lBS/vLwhVtpwwuYcoIsVY+1JYKR268yn480uQ==
+
printf@^0.6.1:
version "0.6.1"
resolved "https://registry.yarnpkg.com/printf/-/printf-0.6.1.tgz#b9afa3d3b55b7f2e8b1715272479fc756ed88650"
@@ -29725,6 +29996,11 @@ resolve.exports@^1.1.0:
resolved "https://registry.yarnpkg.com/resolve.exports/-/resolve.exports-1.1.0.tgz#5ce842b94b05146c0e03076985d1d0e7e48c90c9"
integrity sha512-J1l+Zxxp4XK3LUDZ9m60LRJF/mAe4z6a4xyabPHk7pvK5t35dACV32iIjJDFeWZFfZlO29w6SZ67knR0tHzJtQ==
+resolve.exports@^2.0.2:
+ version "2.0.2"
+ resolved "https://registry.yarnpkg.com/resolve.exports/-/resolve.exports-2.0.2.tgz#f8c934b8e6a13f539e38b7098e2e36134f01e800"
+ integrity sha512-X2UW6Nw3n/aMgDVy+0rSqgHlv39WZAlZrXCdnbyEiKm17DSqHX4MmQMaST3FbeWR5FTuRcUwYAziZajji0Y7mg==
+
resolve@1.22.1, resolve@^1.1.6, resolve@^1.1.7, resolve@^1.10.0, resolve@^1.11.1, resolve@^1.12.0, resolve@^1.13.1, resolve@^1.14.2, resolve@^1.17.0, resolve@^1.19.0, resolve@^1.20.0, resolve@^1.22.0, resolve@^1.22.1, resolve@^1.4.0, resolve@^1.5.0:
version "1.22.1"
resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.22.1.tgz#27cb2ebb53f91abb49470a928bba7558066ac177"
@@ -29957,6 +30233,15 @@ rollup-plugin-dts@^6.1.0:
optionalDependencies:
"@babel/code-frame" "^7.22.13"
+rollup-plugin-inject@^3.0.0:
+ version "3.0.2"
+ resolved "https://registry.yarnpkg.com/rollup-plugin-inject/-/rollup-plugin-inject-3.0.2.tgz#e4233855bfba6c0c12a312fd6649dff9a13ee9f4"
+ integrity sha512-ptg9PQwzs3orn4jkgXJ74bfs5vYz1NCZlSQMBUA0wKcGp5i5pA1AO3fOUEte8enhGUC+iapTCzEWw2jEFFUO/w==
+ dependencies:
+ estree-walker "^0.6.1"
+ magic-string "^0.25.3"
+ rollup-pluginutils "^2.8.1"
+
rollup-plugin-license@^3.3.1:
version "3.3.1"
resolved "https://registry.yarnpkg.com/rollup-plugin-license/-/rollup-plugin-license-3.3.1.tgz#73b68e33477524198d6f3f9befc905f59bf37c53"
@@ -29972,6 +30257,13 @@ rollup-plugin-license@^3.3.1:
spdx-expression-validate "~2.0.0"
spdx-satisfies "~5.0.1"
+rollup-plugin-node-polyfills@^0.2.1:
+ version "0.2.1"
+ resolved "https://registry.yarnpkg.com/rollup-plugin-node-polyfills/-/rollup-plugin-node-polyfills-0.2.1.tgz#53092a2744837164d5b8a28812ba5f3ff61109fd"
+ integrity sha512-4kCrKPTJ6sK4/gLL/U5QzVT8cxJcofO0OU74tnB19F40cmuAKSzH5/siithxlofFEjwvw1YAhPmbvGNA6jEroA==
+ dependencies:
+ rollup-plugin-inject "^3.0.0"
+
rollup-plugin-sourcemaps@^0.6.3:
version "0.6.3"
resolved "https://registry.yarnpkg.com/rollup-plugin-sourcemaps/-/rollup-plugin-sourcemaps-0.6.3.tgz#bf93913ffe056e414419607f1d02780d7ece84ed"
@@ -29990,7 +30282,7 @@ rollup-plugin-visualizer@^5.12.0:
source-map "^0.7.4"
yargs "^17.5.1"
-rollup-pluginutils@^2.8.2:
+rollup-pluginutils@^2.8.1, rollup-pluginutils@^2.8.2:
version "2.8.2"
resolved "https://registry.yarnpkg.com/rollup-pluginutils/-/rollup-pluginutils-2.8.2.tgz#72f2af0748b592364dbd3389e600e5a9444a351e"
integrity sha512-EEp9NhnUkwY8aif6bxgovPHMoMoNr2FulJziTndpt5H9RdwC47GSGuII9XxpSdzVGM0GWrNPHV6ie1LTNJPaLQ==
@@ -31364,6 +31656,14 @@ stacktrace-parser@^0.1.10:
dependencies:
type-fest "^0.7.1"
+stacktracey@^2.1.8:
+ version "2.1.8"
+ resolved "https://registry.yarnpkg.com/stacktracey/-/stacktracey-2.1.8.tgz#bf9916020738ce3700d1323b32bd2c91ea71199d"
+ integrity sha512-Kpij9riA+UNg7TnphqjH7/CzctQ/owJGNbFkfEeve4Z4uxT5+JapVLFXcsurIfN34gnTWZNJ/f7NMG0E8JDzTw==
+ dependencies:
+ as-table "^1.0.36"
+ get-source "^2.0.12"
+
stagehand@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/stagehand/-/stagehand-1.0.0.tgz#79515e2ad3a02c63f8720c7df9b6077ae14276d9"
@@ -31413,6 +31713,11 @@ stop-iteration-iterator@^1.0.0:
dependencies:
internal-slot "^1.0.4"
+stoppable@^1.1.0:
+ version "1.1.0"
+ resolved "https://registry.yarnpkg.com/stoppable/-/stoppable-1.1.0.tgz#32da568e83ea488b08e4d7ea2c3bcc9d75015d5b"
+ integrity sha512-KXDYZ9dszj6bzvnEMRYvxgeTHU74QBFL54XKtP3nyMuJ81CFYtABZ3bAzL2EdFUaEwJOBOgENyFj3R7oTzDyyw==
+
stream-browserify@^2.0.1:
version "2.0.2"
resolved "https://registry.yarnpkg.com/stream-browserify/-/stream-browserify-2.0.2.tgz#87521d38a44aa7ee91ce1cd2a47df0cb49dd660b"
@@ -32687,6 +32992,11 @@ tslib@^2.0.0, tslib@^2.0.1, tslib@^2.0.3, tslib@^2.1.0, tslib@^2.3.0, tslib@^2.3
resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.5.2.tgz#1b6f07185c881557b0ffa84b111a0106989e8338"
integrity sha512-5svOrSA2w3iGFDs1HibEVBGbDrAY82bFQ3HZ3ixB+88nsbsWQoKqDRb5UBYAUPEzbBn6dAp5gRNXglySbx1MlA==
+tslib@^2.2.0:
+ version "2.6.3"
+ resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.6.3.tgz#0438f810ad7a9edcde7a241c3d80db693c8cbfe0"
+ integrity sha512-xNvxJEOUiWPGhUuUdQgAJPKOOJfGnIyKySOc09XkKsgdUV/3E2zvwZYdejjmRgPCgcym1juLH3226yA7sEFJKQ==
+
tsutils@^3.21.0:
version "3.21.0"
resolved "https://registry.yarnpkg.com/tsutils/-/tsutils-3.21.0.tgz#b48717d394cea6c1e096983eed58e9d61715b623"
@@ -33030,6 +33340,18 @@ unenv@^1.9.0:
node-fetch-native "^1.6.1"
pathe "^1.1.1"
+"unenv@npm:unenv-nightly@1.10.0-1717606461.a117952":
+ version "1.10.0-1717606461.a117952"
+ resolved "https://registry.yarnpkg.com/unenv-nightly/-/unenv-nightly-1.10.0-1717606461.a117952.tgz#ff0b97e1e159f84be747271e1d55263b4b3eae7e"
+ integrity sha512-u3TfBX02WzbHTpaEfWEKwDijDSFAHcgXkayUZ+MVDrjhLFvgAJzFGTSTmwlEhwWi2exyRQey23ah9wELMM6etg==
+ dependencies:
+ consola "^3.2.3"
+ defu "^6.1.4"
+ mime "^3.0.0"
+ node-fetch-native "^1.6.4"
+ pathe "^1.1.2"
+ ufo "^1.5.3"
+
unhead@1.9.14:
version "1.9.14"
resolved "https://registry.yarnpkg.com/unhead/-/unhead-1.9.14.tgz#eb9f602a10072751b80907f00e4346beb4d48b6d"
@@ -34683,6 +35005,17 @@ worker-farm@^1.7.0:
dependencies:
errno "~0.1.7"
+workerd@1.20240701.0:
+ version "1.20240701.0"
+ resolved "https://registry.yarnpkg.com/workerd/-/workerd-1.20240701.0.tgz#aaed23a54158bae4faf313c6ed48aefe4b87cd5e"
+ integrity sha512-qSgNVqauqzNCij9MaJLF2c2ko3AnFioVSIxMSryGbRK+LvtGr9BKBt6JOxCb24DoJASoJDx3pe3DJHBVydUiBg==
+ optionalDependencies:
+ "@cloudflare/workerd-darwin-64" "1.20240701.0"
+ "@cloudflare/workerd-darwin-arm64" "1.20240701.0"
+ "@cloudflare/workerd-linux-64" "1.20240701.0"
+ "@cloudflare/workerd-linux-arm64" "1.20240701.0"
+ "@cloudflare/workerd-windows-64" "1.20240701.0"
+
workerpool@^3.1.1:
version "3.1.2"
resolved "https://registry.yarnpkg.com/workerpool/-/workerpool-3.1.2.tgz#b34e79243647decb174b7481ab5b351dc565c426"
@@ -34707,6 +35040,30 @@ workerpool@^6.4.0:
resolved "https://registry.yarnpkg.com/workerpool/-/workerpool-6.4.0.tgz#f8d5cfb45fde32fa3b7af72ad617c3369567a462"
integrity sha512-i3KR1mQMNwY2wx20ozq2EjISGtQWDIfV56We+yGJ5yDs8jTwQiLLaqHlkBHITlCuJnYlVRmXegxFxZg7gqI++A==
+wrangler@^3.63.2:
+ version "3.63.2"
+ resolved "https://registry.yarnpkg.com/wrangler/-/wrangler-3.63.2.tgz#f09ec6f26eb83bdb95a32519df2faec9f1d4c578"
+ integrity sha512-c7F46JtBGTIQehTOgfGbxfDMYgO9AjC70CXVSohxHiF9ajHz56HEV2k3aowhJJiP3MBB8sJMm8rdG10f5zUs+w==
+ dependencies:
+ "@cloudflare/kv-asset-handler" "0.3.4"
+ "@esbuild-plugins/node-globals-polyfill" "^0.2.3"
+ "@esbuild-plugins/node-modules-polyfill" "^0.2.2"
+ blake3-wasm "^2.1.5"
+ chokidar "^3.5.3"
+ date-fns "^3.6.0"
+ esbuild "0.17.19"
+ miniflare "3.20240701.0"
+ nanoid "^3.3.3"
+ path-to-regexp "^6.2.0"
+ resolve "^1.22.8"
+ resolve.exports "^2.0.2"
+ selfsigned "^2.0.1"
+ source-map "^0.6.1"
+ unenv "npm:unenv-nightly@1.10.0-1717606461.a117952"
+ xxhash-wasm "^1.0.1"
+ optionalDependencies:
+ fsevents "~2.3.2"
+
"wrap-ansi-cjs@npm:wrap-ansi@^7.0.0":
version "7.0.0"
resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-7.0.0.tgz#67e145cff510a6a6984bdf1152911d69d2eb9e43"
@@ -34807,6 +35164,11 @@ ws@^8.17.0:
resolved "https://registry.yarnpkg.com/ws/-/ws-8.17.1.tgz#9293da530bb548febc95371d90f9c878727d919b"
integrity sha512-6XQFvXTkbfUOZOKKILFG1PDK2NDQs4azKQl26T0YS5CxqWLgXajbPZ+h4gZekJyRqFU8pvnbAbbs/3TgRPy+GQ==
+ws@^8.17.1:
+ version "8.18.0"
+ resolved "https://registry.yarnpkg.com/ws/-/ws-8.18.0.tgz#0d7505a6eafe2b0e712d232b42279f53bc289bbc"
+ integrity sha512-8VbfWfHLbbwu3+N6OKsOMpBdT4kXPDDB9cJk2bJ6mh9ucxdlnNvH1e+roYkKmN9Nxw2yjz7VzeO9oOz2zJ04Pw==
+
ws@^8.4.2:
version "8.16.0"
resolved "https://registry.yarnpkg.com/ws/-/ws-8.16.0.tgz#d1cd774f36fbc07165066a60e40323eab6446fd4"
@@ -34850,6 +35212,11 @@ xtend@^4.0.0, xtend@~4.0.1:
resolved "https://registry.yarnpkg.com/xtend/-/xtend-4.0.2.tgz#bb72779f5fa465186b1f438f674fa347fdb5db54"
integrity sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==
+xxhash-wasm@^1.0.1:
+ version "1.0.2"
+ resolved "https://registry.yarnpkg.com/xxhash-wasm/-/xxhash-wasm-1.0.2.tgz#ecc0f813219b727af4d5f3958ca6becee2f2f1ff"
+ integrity sha512-ibF0Or+FivM9lNrg+HGJfVX8WJqgo+kCLDc4vx6xMeTce7Aj+DLttKbxxRR/gNLSAelRc1omAPlJ77N/Jem07A==
+
xxhashjs@~0.2.2:
version "0.2.2"
resolved "https://registry.yarnpkg.com/xxhashjs/-/xxhashjs-0.2.2.tgz#8a6251567621a1c46a5ae204da0249c7f8caa9d8"
@@ -35004,6 +35371,15 @@ yocto-queue@^1.0.0:
resolved "https://registry.yarnpkg.com/yocto-queue/-/yocto-queue-1.0.0.tgz#7f816433fb2cbc511ec8bf7d263c3b58a1a3c251"
integrity sha512-9bnSc/HEW2uRy67wc+T8UwauLuPJVn28jb+GtJY16iiKWyvmYJRXVT4UamsAEGQfPohgr2q4Tq0sQbQlxTfi1g==
+youch@^3.2.2:
+ version "3.3.3"
+ resolved "https://registry.yarnpkg.com/youch/-/youch-3.3.3.tgz#50cfdf5bc395ce664a5073e31b712ff4a859d928"
+ integrity sha512-qSFXUk3UZBLfggAW3dJKg0BMblG5biqSF8M34E06o5CSsZtH92u9Hqmj2RzGiHDi64fhe83+4tENFP2DB6t6ZA==
+ dependencies:
+ cookie "^0.5.0"
+ mustache "^4.2.0"
+ stacktracey "^2.1.8"
+
zhead@^2.2.4:
version "2.2.4"
resolved "https://registry.yarnpkg.com/zhead/-/zhead-2.2.4.tgz#87cd1e2c3d2f465fa9f43b8db23f9716dfe6bed7"
@@ -35018,6 +35394,11 @@ zip-stream@^6.0.1:
compress-commons "^6.0.2"
readable-stream "^4.0.0"
+zod@^3.22.3:
+ version "3.23.8"
+ resolved "https://registry.yarnpkg.com/zod/-/zod-3.23.8.tgz#e37b957b5d52079769fb8097099b592f0ef4067d"
+ integrity sha512-XBx9AXhXktjUqnepgTiE5flcKIYWi/rme0Eaj+5Y0lftuGBq+jyRu/md4WnuxqgP1ubdpNCsYEYPxrzVHD8d6g==
+
zod@^3.22.4:
version "3.22.4"
resolved "https://registry.yarnpkg.com/zod/-/zod-3.22.4.tgz#f31c3a9386f61b1f228af56faa9255e845cf3fff"
From deb12d5b55d7f4f655718ed8b014f517f080a258 Mon Sep 17 00:00:00 2001
From: Francesco Novy
Date: Thu, 11 Jul 2024 16:46:24 +0200
Subject: [PATCH 17/39] fix: Cleanup hooks when they are not used anymore
(#12852)
Small optimization using the new hook cleanup capabilities to remove
unused hooks.
Ref PR to do this in angular:
https://github.com/getsentry/sentry-javascript/pull/12786
---
.../tracing/trace-lifetime/navigation/test.ts | 8 +++
.../src/tracing/browserTracingIntegration.ts | 4 +-
packages/core/src/baseclient.ts | 18 ++---
packages/core/src/tracing/idleSpan.ts | 66 +++++++++++--------
packages/feedback/src/core/sendFeedback.ts | 3 +-
packages/react/src/errorboundary.tsx | 8 ++-
6 files changed, 62 insertions(+), 45 deletions(-)
diff --git a/dev-packages/browser-integration-tests/suites/tracing/trace-lifetime/navigation/test.ts b/dev-packages/browser-integration-tests/suites/tracing/trace-lifetime/navigation/test.ts
index b62923be0e9b..9d158ea5491e 100644
--- a/dev-packages/browser-integration-tests/suites/tracing/trace-lifetime/navigation/test.ts
+++ b/dev-packages/browser-integration-tests/suites/tracing/trace-lifetime/navigation/test.ts
@@ -74,6 +74,14 @@ sentryTest('error after navigation has navigation traceId', async ({ getLocalTes
sentryTest.skip();
}
+ await page.route('https://dsn.ingest.sentry.io/**/*', route => {
+ return route.fulfill({
+ status: 200,
+ contentType: 'application/json',
+ body: JSON.stringify({ id: 'test-id' }),
+ });
+ });
+
const url = await getLocalTestUrl({ testDir: __dirname });
// ensure pageload transaction is finished
diff --git a/packages/browser/src/tracing/browserTracingIntegration.ts b/packages/browser/src/tracing/browserTracingIntegration.ts
index 3deaa195abe3..9d5421f697cd 100644
--- a/packages/browser/src/tracing/browserTracingIntegration.ts
+++ b/packages/browser/src/tracing/browserTracingIntegration.ts
@@ -288,7 +288,7 @@ export const browserTracingIntegration = ((_options: Partial implements Client {
/** @inheritdoc */
public on(hook: string, callback: unknown): () => void {
- // Note that the code below, with nullish coalescing assignment,
- // may reduce the code, so it may be switched to when Node 14 support
- // is dropped (the `??=` operator is supported since Node 15).
- // (this._hooks[hook] ??= []).push(callback);
- if (!this._hooks[hook]) {
- this._hooks[hook] = [];
- }
+ const hooks = (this._hooks[hook] = this._hooks[hook] || []);
// @ts-expect-error We assue the types are correct
- this._hooks[hook].push(callback);
+ hooks.push(callback);
// This function returns a callback execution handler that, when invoked,
// deregisters a callback. This is crucial for managing instances where callbacks
// need to be unregistered to prevent self-referencing in callback closures,
// ensuring proper garbage collection.
return () => {
- const hooks = this._hooks[hook];
-
- if (hooks) {
- // @ts-expect-error We assue the types are correct
- const cbIndex = hooks.indexOf(callback);
+ // @ts-expect-error We assue the types are correct
+ const cbIndex = hooks.indexOf(callback);
+ if (cbIndex > -1) {
hooks.splice(cbIndex, 1);
}
};
diff --git a/packages/core/src/tracing/idleSpan.ts b/packages/core/src/tracing/idleSpan.ts
index 67093076f443..c3d66a4b7593 100644
--- a/packages/core/src/tracing/idleSpan.ts
+++ b/packages/core/src/tracing/idleSpan.ts
@@ -96,6 +96,8 @@ export function startIdleSpan(startSpanOptions: StartSpanOptions, options: Parti
let _autoFinishAllowed: boolean = !options.disableAutoFinish;
+ const _cleanupHooks: (() => void)[] = [];
+
const {
idleTimeout = TRACING_DEFAULTS.idleTimeout,
finalTimeout = TRACING_DEFAULTS.finalTimeout,
@@ -240,6 +242,8 @@ export function startIdleSpan(startSpanOptions: StartSpanOptions, options: Parti
_finished = true;
activities.clear();
+ _cleanupHooks.forEach(cleanup => cleanup());
+
_setSpanForScope(scope, previousActiveSpan);
const spanJSON = spanToJSON(span);
@@ -298,41 +302,47 @@ export function startIdleSpan(startSpanOptions: StartSpanOptions, options: Parti
}
}
- client.on('spanStart', startedSpan => {
- // If we already finished the idle span,
- // or if this is the idle span itself being started,
- // or if the started span has already been closed,
- // we don't care about it for activity
- if (_finished || startedSpan === span || !!spanToJSON(startedSpan).timestamp) {
- return;
- }
+ _cleanupHooks.push(
+ client.on('spanStart', startedSpan => {
+ // If we already finished the idle span,
+ // or if this is the idle span itself being started,
+ // or if the started span has already been closed,
+ // we don't care about it for activity
+ if (_finished || startedSpan === span || !!spanToJSON(startedSpan).timestamp) {
+ return;
+ }
- const allSpans = getSpanDescendants(span);
+ const allSpans = getSpanDescendants(span);
- // If the span that was just started is a child of the idle span, we should track it
- if (allSpans.includes(startedSpan)) {
- _pushActivity(startedSpan.spanContext().spanId);
- }
- });
+ // If the span that was just started is a child of the idle span, we should track it
+ if (allSpans.includes(startedSpan)) {
+ _pushActivity(startedSpan.spanContext().spanId);
+ }
+ }),
+ );
- client.on('spanEnd', endedSpan => {
- if (_finished) {
- return;
- }
+ _cleanupHooks.push(
+ client.on('spanEnd', endedSpan => {
+ if (_finished) {
+ return;
+ }
- _popActivity(endedSpan.spanContext().spanId);
- });
+ _popActivity(endedSpan.spanContext().spanId);
+ }),
+ );
- client.on('idleSpanEnableAutoFinish', spanToAllowAutoFinish => {
- if (spanToAllowAutoFinish === span) {
- _autoFinishAllowed = true;
- _restartIdleTimeout();
+ _cleanupHooks.push(
+ client.on('idleSpanEnableAutoFinish', spanToAllowAutoFinish => {
+ if (spanToAllowAutoFinish === span) {
+ _autoFinishAllowed = true;
+ _restartIdleTimeout();
- if (activities.size) {
- _restartChildSpanTimeout();
+ if (activities.size) {
+ _restartChildSpanTimeout();
+ }
}
- }
- });
+ }),
+ );
// We only start the initial idle timeout if we are not delaying the auto finish
if (!options.disableAutoFinish) {
diff --git a/packages/feedback/src/core/sendFeedback.ts b/packages/feedback/src/core/sendFeedback.ts
index 3f8c08a51ee3..ca9875284c6e 100644
--- a/packages/feedback/src/core/sendFeedback.ts
+++ b/packages/feedback/src/core/sendFeedback.ts
@@ -40,12 +40,13 @@ export const sendFeedback: SendFeedback = (
// After 5s, we want to clear anyhow
const timeout = setTimeout(() => reject('Unable to determine if Feedback was correctly sent.'), 5_000);
- client.on('afterSendEvent', (event: Event, response: TransportMakeRequestResponse) => {
+ const cleanup = client.on('afterSendEvent', (event: Event, response: TransportMakeRequestResponse) => {
if (event.event_id !== eventId) {
return;
}
clearTimeout(timeout);
+ cleanup();
// Require valid status codes, otherwise can assume feedback was not sent successfully
if (
diff --git a/packages/react/src/errorboundary.tsx b/packages/react/src/errorboundary.tsx
index e12ca9f44d79..abd532c29a53 100644
--- a/packages/react/src/errorboundary.tsx
+++ b/packages/react/src/errorboundary.tsx
@@ -77,6 +77,7 @@ class ErrorBoundary extends React.Component void;
public constructor(props: ErrorBoundaryProps) {
super(props);
@@ -87,7 +88,7 @@ class ErrorBoundary extends React.Component {
+ this._cleanupHook = client.on('afterSendEvent', event => {
if (!event.type && this._lastEventId && event.event_id === this._lastEventId) {
showReportDialog({ ...props.dialogOptions, eventId: this._lastEventId });
}
@@ -137,6 +138,11 @@ class ErrorBoundary extends React.Component void = () => {
From 9c613be354bc21f60fe82b939e879f8587a971dc Mon Sep 17 00:00:00 2001
From: "github-actions[bot]"
<41898282+github-actions[bot]@users.noreply.github.com>
Date: Thu, 11 Jul 2024 16:50:03 +0200
Subject: [PATCH 18/39] ref: Add external contributor to CHANGELOG.md (#12882)
This PR adds the external contributor to the CHANGELOG.md file, so that
they are credited for their contribution. See #12879
---
CHANGELOG.md | 2 ++
1 file changed, 2 insertions(+)
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 81b502aecd4a..68d01cdce384 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -8,6 +8,8 @@
- "You miss 100 percent of the chances you don't take. — Wayne Gretzky" — Michael Scott
+Work in this release was contributed by @GitSquared. Thank you for your contribution!
+
## 8.17.0
- feat: Upgrade OTEL deps (#12809)
From 963eab78cf990e62bcf7479069c4fcb0136071a7 Mon Sep 17 00:00:00 2001
From: Luca Forstner
Date: Thu, 11 Jul 2024 17:34:12 +0200
Subject: [PATCH 19/39] test(nextjs): Fix canary tests (#12875)
---
.../tests/client-app-routing-instrumentation.test.ts | 5 ++++-
.../nextjs-app-dir/tests/server-components.test.ts | 5 ++++-
2 files changed, 8 insertions(+), 2 deletions(-)
diff --git a/dev-packages/e2e-tests/test-applications/nextjs-app-dir/tests/client-app-routing-instrumentation.test.ts b/dev-packages/e2e-tests/test-applications/nextjs-app-dir/tests/client-app-routing-instrumentation.test.ts
index 8645d36c4c8a..9143bd0b2f90 100644
--- a/dev-packages/e2e-tests/test-applications/nextjs-app-dir/tests/client-app-routing-instrumentation.test.ts
+++ b/dev-packages/e2e-tests/test-applications/nextjs-app-dir/tests/client-app-routing-instrumentation.test.ts
@@ -39,7 +39,10 @@ test('Creates a navigation transaction for app router routes', async ({ page })
const serverComponentTransactionPromise = waitForTransaction('nextjs-app-dir', async transactionEvent => {
return (
- transactionEvent?.transaction === 'GET /server-component/parameter/foo/bar/baz' &&
+ // It seems to differ between Next.js versions whether the route is parameterized or not
+ (transactionEvent?.transaction === 'GET /server-component/parameter/foo/bar/baz' ||
+ transactionEvent?.transaction === 'GET /server-component/parameter/[...parameters]') &&
+ transactionEvent.contexts?.trace?.data?.['http.target'].startsWith('/server-component/parameter/foo/bar/baz') &&
(await clientNavigationTransactionPromise).contexts?.trace?.trace_id ===
transactionEvent.contexts?.trace?.trace_id
);
diff --git a/dev-packages/e2e-tests/test-applications/nextjs-app-dir/tests/server-components.test.ts b/dev-packages/e2e-tests/test-applications/nextjs-app-dir/tests/server-components.test.ts
index ba232ad558b0..49afe791328f 100644
--- a/dev-packages/e2e-tests/test-applications/nextjs-app-dir/tests/server-components.test.ts
+++ b/dev-packages/e2e-tests/test-applications/nextjs-app-dir/tests/server-components.test.ts
@@ -3,7 +3,10 @@ import { waitForError, waitForTransaction } from '@sentry-internal/test-utils';
test('Sends a transaction for a request to app router', async ({ page }) => {
const serverComponentTransactionPromise = waitForTransaction('nextjs-app-dir', transactionEvent => {
- return transactionEvent?.transaction === 'GET /server-component/parameter/[...parameters]';
+ return (
+ transactionEvent?.transaction === 'GET /server-component/parameter/[...parameters]' &&
+ transactionEvent.contexts?.trace?.data?.['http.target'].startsWith('/server-component/parameter/1337/42')
+ );
});
await page.goto('/server-component/parameter/1337/42');
From 9f07f992b30556489083b43ed567cee43623a0d7 Mon Sep 17 00:00:00 2001
From: Lukas Stracke
Date: Fri, 12 Jul 2024 11:22:24 +0200
Subject: [PATCH 20/39] fix(angular): Guard `ErrorEvent` check in ErrorHandler
to avoid throwing in Node environments (#12892)
Add a guard for referencing `ErrorEvent` which is only
available in browser environments but not in Node.
---
packages/angular/src/errorhandler.ts | 3 ++-
packages/angular/test/errorhandler.test.ts | 17 +++++++++++++++++
2 files changed, 19 insertions(+), 1 deletion(-)
diff --git a/packages/angular/src/errorhandler.ts b/packages/angular/src/errorhandler.ts
index 4ea44c2ffc63..28c06e1e6bfd 100644
--- a/packages/angular/src/errorhandler.ts
+++ b/packages/angular/src/errorhandler.ts
@@ -39,7 +39,8 @@ function extractHttpModuleError(error: HttpErrorResponse): string | Error {
}
// ... or an`ErrorEvent`, which can provide us with the message but no stack...
- if (error.error instanceof ErrorEvent && error.error.message) {
+ // guarding `ErrorEvent` against `undefined` as it's not defined in Node environments
+ if (typeof ErrorEvent !== 'undefined' && error.error instanceof ErrorEvent && error.error.message) {
return error.error.message;
}
diff --git a/packages/angular/test/errorhandler.test.ts b/packages/angular/test/errorhandler.test.ts
index 3c2b2f994446..c30a7a87efc9 100644
--- a/packages/angular/test/errorhandler.test.ts
+++ b/packages/angular/test/errorhandler.test.ts
@@ -45,6 +45,11 @@ describe('SentryErrorHandler', () => {
});
describe('handleError method', () => {
+ const originalErrorEvent = globalThis.ErrorEvent;
+ afterEach(() => {
+ globalThis.ErrorEvent = originalErrorEvent;
+ });
+
it('extracts `null` error', () => {
createErrorHandler().handleError(null);
@@ -223,6 +228,18 @@ describe('SentryErrorHandler', () => {
expect(captureExceptionSpy).toHaveBeenCalledWith('Handled unknown error', captureExceptionEventHint);
});
+ it('handles ErrorEvent being undefined', () => {
+ const httpErr = new ErrorEvent('http', { message: 'sentry-http-test' });
+ const err = new HttpErrorResponse({ error: httpErr });
+
+ // @ts-expect-error - this is fine in this test
+ delete globalThis.ErrorEvent;
+
+ expect(() => {
+ createErrorHandler().handleError(err);
+ }).not.toThrow();
+ });
+
it('extracts an Error with `ngOriginalError`', () => {
const ngErr = new Error('sentry-ng-test');
const err = {
From d67df353dfb025081fba6836b1e71911384dc3ab Mon Sep 17 00:00:00 2001
From: Luca Forstner
Date: Fri, 12 Jul 2024 11:29:11 +0200
Subject: [PATCH 21/39] feat(nextjs): Add `experimental_captureRequestError`
for `onRequestError` hook (#12885)
---
.../app/nested-rsc-error/[param]/page.tsx | 17 +++++++
.../[param]/client-page.tsx | 8 +++
.../app/streaming-rsc-error/[param]/page.tsx | 18 +++++++
.../nextjs-15/instrumentation.ts | 4 ++
.../test-applications/nextjs-15/package.json | 6 +--
.../nextjs-15/tests/nested-rsc-error.test.ts | 33 ++++++++++++
.../tests/streaming-rsc-error.test.ts | 33 ++++++++++++
.../nextjs/src/common/captureRequestError.ts | 50 +++++++++++++++++++
packages/nextjs/src/common/index.ts | 13 +----
packages/nextjs/src/index.types.ts | 2 +
10 files changed, 169 insertions(+), 15 deletions(-)
create mode 100644 dev-packages/e2e-tests/test-applications/nextjs-15/app/nested-rsc-error/[param]/page.tsx
create mode 100644 dev-packages/e2e-tests/test-applications/nextjs-15/app/streaming-rsc-error/[param]/client-page.tsx
create mode 100644 dev-packages/e2e-tests/test-applications/nextjs-15/app/streaming-rsc-error/[param]/page.tsx
create mode 100644 dev-packages/e2e-tests/test-applications/nextjs-15/tests/nested-rsc-error.test.ts
create mode 100644 dev-packages/e2e-tests/test-applications/nextjs-15/tests/streaming-rsc-error.test.ts
create mode 100644 packages/nextjs/src/common/captureRequestError.ts
diff --git a/dev-packages/e2e-tests/test-applications/nextjs-15/app/nested-rsc-error/[param]/page.tsx b/dev-packages/e2e-tests/test-applications/nextjs-15/app/nested-rsc-error/[param]/page.tsx
new file mode 100644
index 000000000000..675b248026be
--- /dev/null
+++ b/dev-packages/e2e-tests/test-applications/nextjs-15/app/nested-rsc-error/[param]/page.tsx
@@ -0,0 +1,17 @@
+import { Suspense } from 'react';
+
+export const dynamic = 'force-dynamic';
+
+export default async function Page() {
+ return (
+ Loading...
}>
+ {/* @ts-ignore */}
+ ;
+
+ );
+}
+
+async function Crash() {
+ throw new Error('I am technically uncatchable');
+ return unreachable
;
+}
diff --git a/dev-packages/e2e-tests/test-applications/nextjs-15/app/streaming-rsc-error/[param]/client-page.tsx b/dev-packages/e2e-tests/test-applications/nextjs-15/app/streaming-rsc-error/[param]/client-page.tsx
new file mode 100644
index 000000000000..7b66c3fbdeef
--- /dev/null
+++ b/dev-packages/e2e-tests/test-applications/nextjs-15/app/streaming-rsc-error/[param]/client-page.tsx
@@ -0,0 +1,8 @@
+'use client';
+
+import { use } from 'react';
+
+export function RenderPromise({ stringPromise }: { stringPromise: Promise }) {
+ const s = use(stringPromise);
+ return <>{s}>;
+}
diff --git a/dev-packages/e2e-tests/test-applications/nextjs-15/app/streaming-rsc-error/[param]/page.tsx b/dev-packages/e2e-tests/test-applications/nextjs-15/app/streaming-rsc-error/[param]/page.tsx
new file mode 100644
index 000000000000..9531f9a42139
--- /dev/null
+++ b/dev-packages/e2e-tests/test-applications/nextjs-15/app/streaming-rsc-error/[param]/page.tsx
@@ -0,0 +1,18 @@
+import { Suspense } from 'react';
+import { RenderPromise } from './client-page';
+
+export const dynamic = 'force-dynamic';
+
+export default async function Page() {
+ const crashingPromise = new Promise((_, reject) => {
+ setTimeout(() => {
+ reject(new Error('I am a data streaming error'));
+ }, 100);
+ });
+
+ return (
+ Loading...}>
+ ;
+
+ );
+}
diff --git a/dev-packages/e2e-tests/test-applications/nextjs-15/instrumentation.ts b/dev-packages/e2e-tests/test-applications/nextjs-15/instrumentation.ts
index 7b89a972e157..ca4a213e58ba 100644
--- a/dev-packages/e2e-tests/test-applications/nextjs-15/instrumentation.ts
+++ b/dev-packages/e2e-tests/test-applications/nextjs-15/instrumentation.ts
@@ -1,3 +1,5 @@
+import * as Sentry from '@sentry/nextjs';
+
export async function register() {
if (process.env.NEXT_RUNTIME === 'nodejs') {
await import('./sentry.server.config');
@@ -7,3 +9,5 @@ export async function register() {
await import('./sentry.edge.config');
}
}
+
+export const onRequestError = Sentry.experimental_captureRequestError;
diff --git a/dev-packages/e2e-tests/test-applications/nextjs-15/package.json b/dev-packages/e2e-tests/test-applications/nextjs-15/package.json
index ebd18c6fb10e..4c3f56b0aa0c 100644
--- a/dev-packages/e2e-tests/test-applications/nextjs-15/package.json
+++ b/dev-packages/e2e-tests/test-applications/nextjs-15/package.json
@@ -5,8 +5,8 @@
"scripts": {
"build": "next build > .tmp_build_stdout 2> .tmp_build_stderr || (cat .tmp_build_stdout && cat .tmp_build_stderr && exit 1)",
"clean": "npx rimraf node_modules pnpm-lock.yaml",
- "test:prod": "TEST_ENV=production playwright test",
- "test:dev": "TEST_ENV=development playwright test",
+ "test:prod": "TEST_ENV=production __NEXT_EXPERIMENTAL_INSTRUMENTATION=1 playwright test",
+ "test:dev": "TEST_ENV=development __NEXT_EXPERIMENTAL_INSTRUMENTATION=1 playwright test",
"test:build": "pnpm install && npx playwright install && pnpm build",
"test:build-canary": "pnpm install && pnpm add next@rc && pnpm add react@beta && pnpm add react-dom@beta && npx playwright install && pnpm build",
"test:build-latest": "pnpm install && pnpm add next@rc && pnpm add react@beta && pnpm add react-dom@beta && npx playwright install && pnpm build",
@@ -17,7 +17,7 @@
"@types/node": "18.11.17",
"@types/react": "18.0.26",
"@types/react-dom": "18.0.9",
- "next": "14.3.0-canary.73",
+ "next": "15.0.0-canary.63",
"react": "beta",
"react-dom": "beta",
"typescript": "4.9.5"
diff --git a/dev-packages/e2e-tests/test-applications/nextjs-15/tests/nested-rsc-error.test.ts b/dev-packages/e2e-tests/test-applications/nextjs-15/tests/nested-rsc-error.test.ts
new file mode 100644
index 000000000000..223da5b245e9
--- /dev/null
+++ b/dev-packages/e2e-tests/test-applications/nextjs-15/tests/nested-rsc-error.test.ts
@@ -0,0 +1,33 @@
+import { expect, test } from '@playwright/test';
+import { waitForError, waitForTransaction } from '@sentry-internal/test-utils';
+
+test('Should capture errors from nested server components when `Sentry.captureRequestError` is added to the `onRequestError` hook', async ({
+ page,
+}) => {
+ const errorEventPromise = waitForError('nextjs-15', errorEvent => {
+ return !!errorEvent?.exception?.values?.some(value => value.value === 'I am technically uncatchable');
+ });
+
+ const serverTransactionPromise = waitForTransaction('nextjs-15', async transactionEvent => {
+ return transactionEvent?.transaction === 'GET /nested-rsc-error/[param]';
+ });
+
+ await page.goto(`/nested-rsc-error/123`);
+ const errorEvent = await errorEventPromise;
+ const serverTransactionEvent = await serverTransactionPromise;
+
+ // error event is part of the transaction
+ expect(errorEvent.contexts?.trace?.trace_id).toBe(serverTransactionEvent.contexts?.trace?.trace_id);
+
+ expect(errorEvent.request).toMatchObject({
+ headers: expect.any(Object),
+ method: 'GET',
+ });
+
+ expect(errorEvent.contexts?.nextjs).toEqual({
+ route_type: 'render',
+ router_kind: 'App Router',
+ router_path: '/nested-rsc-error/[param]',
+ request_path: '/nested-rsc-error/123',
+ });
+});
diff --git a/dev-packages/e2e-tests/test-applications/nextjs-15/tests/streaming-rsc-error.test.ts b/dev-packages/e2e-tests/test-applications/nextjs-15/tests/streaming-rsc-error.test.ts
new file mode 100644
index 000000000000..b50e9688861e
--- /dev/null
+++ b/dev-packages/e2e-tests/test-applications/nextjs-15/tests/streaming-rsc-error.test.ts
@@ -0,0 +1,33 @@
+import { expect, test } from '@playwright/test';
+import { waitForError, waitForTransaction } from '@sentry-internal/test-utils';
+
+test('Should capture errors for crashing streaming promises in server components when `Sentry.captureRequestError` is added to the `onRequestError` hook', async ({
+ page,
+}) => {
+ const errorEventPromise = waitForError('nextjs-15', errorEvent => {
+ return !!errorEvent?.exception?.values?.some(value => value.value === 'I am a data streaming error');
+ });
+
+ const serverTransactionPromise = waitForTransaction('nextjs-15', async transactionEvent => {
+ return transactionEvent?.transaction === 'GET /streaming-rsc-error/[param]';
+ });
+
+ await page.goto(`/streaming-rsc-error/123`);
+ const errorEvent = await errorEventPromise;
+ const serverTransactionEvent = await serverTransactionPromise;
+
+ // error event is part of the transaction
+ expect(errorEvent.contexts?.trace?.trace_id).toBe(serverTransactionEvent.contexts?.trace?.trace_id);
+
+ expect(errorEvent.request).toMatchObject({
+ headers: expect.any(Object),
+ method: 'GET',
+ });
+
+ expect(errorEvent.contexts?.nextjs).toEqual({
+ route_type: 'render',
+ router_kind: 'App Router',
+ router_path: '/streaming-rsc-error/[param]',
+ request_path: '/streaming-rsc-error/123',
+ });
+});
diff --git a/packages/nextjs/src/common/captureRequestError.ts b/packages/nextjs/src/common/captureRequestError.ts
new file mode 100644
index 000000000000..7968907ad9bf
--- /dev/null
+++ b/packages/nextjs/src/common/captureRequestError.ts
@@ -0,0 +1,50 @@
+import { captureException, withScope } from '@sentry/core';
+
+type RequestInfo = {
+ url: string;
+ method: string;
+ headers: Record;
+};
+
+type ErrorContext = {
+ routerKind: string; // 'Pages Router' | 'App Router'
+ routePath: string;
+ routeType: string; // 'render' | 'route' | 'middleware'
+};
+
+/**
+ * Reports errors for the Next.js `onRequestError` instrumentation hook.
+ *
+ * Notice: This function is experimental and not intended for production use. Breaking changes may be done to this funtion in any release.
+ *
+ * @experimental
+ */
+export function experimental_captureRequestError(
+ error: unknown,
+ request: RequestInfo,
+ errorContext: ErrorContext,
+): void {
+ withScope(scope => {
+ scope.setSDKProcessingMetadata({
+ request: {
+ headers: request.headers,
+ method: request.method,
+ },
+ });
+
+ scope.setContext('nextjs', {
+ request_path: request.url,
+ router_kind: errorContext.routerKind,
+ router_path: errorContext.routePath,
+ route_type: errorContext.routeType,
+ });
+
+ scope.setTransactionName(errorContext.routePath);
+
+ captureException(error, {
+ mechanism: {
+ handled: false,
+ },
+ });
+ });
+}
diff --git a/packages/nextjs/src/common/index.ts b/packages/nextjs/src/common/index.ts
index e308537f1358..23ddfa383772 100644
--- a/packages/nextjs/src/common/index.ts
+++ b/packages/nextjs/src/common/index.ts
@@ -1,25 +1,14 @@
export { wrapGetStaticPropsWithSentry } from './wrapGetStaticPropsWithSentry';
-
export { wrapGetInitialPropsWithSentry } from './wrapGetInitialPropsWithSentry';
-
export { wrapAppGetInitialPropsWithSentry } from './wrapAppGetInitialPropsWithSentry';
-
export { wrapDocumentGetInitialPropsWithSentry } from './wrapDocumentGetInitialPropsWithSentry';
-
export { wrapErrorGetInitialPropsWithSentry } from './wrapErrorGetInitialPropsWithSentry';
-
export { wrapGetServerSidePropsWithSentry } from './wrapGetServerSidePropsWithSentry';
-
export { wrapServerComponentWithSentry } from './wrapServerComponentWithSentry';
-
export { wrapRouteHandlerWithSentry } from './wrapRouteHandlerWithSentry';
-
export { wrapApiHandlerWithSentryVercelCrons } from './wrapApiHandlerWithSentryVercelCrons';
-
export { wrapMiddlewareWithSentry } from './wrapMiddlewareWithSentry';
-
export { wrapPageComponentWithSentry } from './wrapPageComponentWithSentry';
-
export { wrapGenerationFunctionWithSentry } from './wrapGenerationFunctionWithSentry';
-
export { withServerActionInstrumentation } from './withServerActionInstrumentation';
+export { experimental_captureRequestError } from './captureRequestError';
diff --git a/packages/nextjs/src/index.types.ts b/packages/nextjs/src/index.types.ts
index afff0bd98a19..b093968bdebe 100644
--- a/packages/nextjs/src/index.types.ts
+++ b/packages/nextjs/src/index.types.ts
@@ -140,3 +140,5 @@ export declare function wrapApiHandlerWithSentryVercelCrons(WrappingTarget: C): C;
+
+export { experimental_captureRequestError } from './common/captureRequestError';
From 6797044ec12983d8c50a7d3205290179c8c4deca Mon Sep 17 00:00:00 2001
From: Andrei <168741329+andreiborza@users.noreply.github.com>
Date: Fri, 12 Jul 2024 14:40:47 +0200
Subject: [PATCH 22/39] test(solidstart): Add client performance e2e tests
(#12895)
---
.../test-applications/solidstart/package.json | 7 +-
.../test-applications/solidstart/src/app.tsx | 7 +-
.../solidstart/src/entry-client.tsx | 3 +-
.../solidstart/src/routes/index.tsx | 10 ++
.../solidstart/src/routes/users/[id].tsx | 6 ++
.../tests/performance.client.test.ts | 93 +++++++++++++++++++
6 files changed, 122 insertions(+), 4 deletions(-)
create mode 100644 dev-packages/e2e-tests/test-applications/solidstart/src/routes/users/[id].tsx
create mode 100644 dev-packages/e2e-tests/test-applications/solidstart/tests/performance.client.test.ts
diff --git a/dev-packages/e2e-tests/test-applications/solidstart/package.json b/dev-packages/e2e-tests/test-applications/solidstart/package.json
index b4b81ffcbd88..6409d191de5b 100644
--- a/dev-packages/e2e-tests/test-applications/solidstart/package.json
+++ b/dev-packages/e2e-tests/test-applications/solidstart/package.json
@@ -5,7 +5,12 @@
"clean": "pnpx rimraf node_modules pnpm-lock.yaml .vinxi .output",
"dev": "NODE_OPTIONS='--import ./src/instrument.server.mjs' vinxi dev",
"build": "vinxi build",
- "preview": "HOST=localhost PORT=3030 NODE_OPTIONS='--import ./src/instrument.server.mjs' vinxi start",
+ "//": [
+ "We are using `vinxi dev` to start the server because `vinxi start` is experimental and ",
+ "doesn't correctly resolve modules for @sentry/solidstart/solidrouter.",
+ "This is currently not an issue outside of our repo. See: https://github.com/nksaraf/vinxi/issues/177"
+ ],
+ "preview": "HOST=localhost PORT=3030 NODE_OPTIONS='--import ./src/instrument.server.mjs' vinxi dev",
"test:prod": "TEST_ENV=production playwright test",
"test:build": "pnpm install && npx playwright install && pnpm build",
"test:assert": "pnpm test:prod"
diff --git a/dev-packages/e2e-tests/test-applications/solidstart/src/app.tsx b/dev-packages/e2e-tests/test-applications/solidstart/src/app.tsx
index bb1f1095f125..3eb85218b575 100644
--- a/dev-packages/e2e-tests/test-applications/solidstart/src/app.tsx
+++ b/dev-packages/e2e-tests/test-applications/solidstart/src/app.tsx
@@ -1,11 +1,14 @@
+import { withSentryRouterRouting } from '@sentry/solidstart/solidrouter';
import { MetaProvider, Title } from '@solidjs/meta';
import { Router } from '@solidjs/router';
import { FileRoutes } from '@solidjs/start/router';
import { Suspense } from 'solid-js';
+const SentryRouter = withSentryRouterRouting(Router);
+
export default function App() {
return (
- (
SolidStart - with Vitest
@@ -14,6 +17,6 @@ export default function App() {
)}
>
-
+
);
}
diff --git a/dev-packages/e2e-tests/test-applications/solidstart/src/entry-client.tsx b/dev-packages/e2e-tests/test-applications/solidstart/src/entry-client.tsx
index cbcd8f9267ca..9391faa9652d 100644
--- a/dev-packages/e2e-tests/test-applications/solidstart/src/entry-client.tsx
+++ b/dev-packages/e2e-tests/test-applications/solidstart/src/entry-client.tsx
@@ -1,5 +1,6 @@
// @refresh reload
import * as Sentry from '@sentry/solidstart';
+import { solidRouterBrowserTracingIntegration } from '@sentry/solidstart/solidrouter';
import { StartClient, mount } from '@solidjs/start/client';
Sentry.init({
@@ -7,7 +8,7 @@ Sentry.init({
// out in production builds.
dsn: 'https://public@dsn.ingest.sentry.io/1337',
environment: 'qa', // dynamic sampling bias to keep transactions
- integrations: [Sentry.browserTracingIntegration()],
+ integrations: [solidRouterBrowserTracingIntegration()],
tunnel: 'http://localhost:3031/', // proxy server
// Performance Monitoring
tracesSampleRate: 1.0, // Capture 100% of the transactions
diff --git a/dev-packages/e2e-tests/test-applications/solidstart/src/routes/index.tsx b/dev-packages/e2e-tests/test-applications/solidstart/src/routes/index.tsx
index a6d09d5b36b5..f1635dee3b63 100644
--- a/dev-packages/e2e-tests/test-applications/solidstart/src/routes/index.tsx
+++ b/dev-packages/e2e-tests/test-applications/solidstart/src/routes/index.tsx
@@ -11,6 +11,16 @@ export default function Home() {
Client error
+
+
+ User 5
+
+
+
+
+ User 6
+
+
>
);
diff --git a/dev-packages/e2e-tests/test-applications/solidstart/src/routes/users/[id].tsx b/dev-packages/e2e-tests/test-applications/solidstart/src/routes/users/[id].tsx
new file mode 100644
index 000000000000..639ab0be8118
--- /dev/null
+++ b/dev-packages/e2e-tests/test-applications/solidstart/src/routes/users/[id].tsx
@@ -0,0 +1,6 @@
+import { useParams } from '@solidjs/router';
+
+export default function User() {
+ const params = useParams();
+ return User ID: {params.id}
;
+}
diff --git a/dev-packages/e2e-tests/test-applications/solidstart/tests/performance.client.test.ts b/dev-packages/e2e-tests/test-applications/solidstart/tests/performance.client.test.ts
new file mode 100644
index 000000000000..17e57ba47d8d
--- /dev/null
+++ b/dev-packages/e2e-tests/test-applications/solidstart/tests/performance.client.test.ts
@@ -0,0 +1,93 @@
+import { expect, test } from '@playwright/test';
+import { waitForTransaction } from '@sentry-internal/test-utils';
+
+test('sends a pageload transaction', async ({ page }) => {
+ const transactionPromise = waitForTransaction('solidstart', async transactionEvent => {
+ return transactionEvent?.transaction === '/' && transactionEvent.contexts?.trace?.op === 'pageload';
+ });
+
+ await page.goto('/');
+ const pageloadTransaction = await transactionPromise;
+
+ expect(pageloadTransaction).toMatchObject({
+ contexts: {
+ trace: {
+ op: 'pageload',
+ origin: 'auto.pageload.browser',
+ },
+ },
+ transaction: '/',
+ transaction_info: {
+ source: 'url',
+ },
+ });
+});
+
+test('sends a navigation transaction', async ({ page }) => {
+ const transactionPromise = waitForTransaction('solidstart', async transactionEvent => {
+ return transactionEvent?.transaction === '/users/5' && transactionEvent.contexts?.trace?.op === 'navigation';
+ });
+
+ await page.goto(`/`);
+ await page.locator('#navLink').click();
+ const navigationTransaction = await transactionPromise;
+
+ expect(navigationTransaction).toMatchObject({
+ contexts: {
+ trace: {
+ op: 'navigation',
+ origin: 'auto.navigation.solid.solidrouter',
+ },
+ },
+ transaction: '/users/5',
+ transaction_info: {
+ source: 'url',
+ },
+ });
+});
+
+test('updates the transaction when using the back button', async ({ page }) => {
+ // Solid Router sends a `-1` navigation when using the back button.
+ // The sentry solidRouterBrowserTracingIntegration tries to update such
+ // transactions with the proper name once the `useLocation` hook triggers.
+ const navigationTxnPromise = waitForTransaction('solidstart', async transactionEvent => {
+ return transactionEvent?.transaction === '/users/6' && transactionEvent.contexts?.trace?.op === 'navigation';
+ });
+
+ await page.goto(`/`);
+ await page.locator('#navLinkUserBack').click();
+ const navigationTxn = await navigationTxnPromise;
+
+ expect(navigationTxn).toMatchObject({
+ contexts: {
+ trace: {
+ op: 'navigation',
+ origin: 'auto.navigation.solid.solidrouter',
+ },
+ },
+ transaction: '/users/6',
+ transaction_info: {
+ source: 'url',
+ },
+ });
+
+ const backNavigationTxnPromise = waitForTransaction('solidstart', async transactionEvent => {
+ return transactionEvent?.transaction === '/' && transactionEvent.contexts?.trace?.op === 'navigation';
+ });
+
+ await page.goBack();
+ const backNavigationTxn = await backNavigationTxnPromise;
+
+ expect(backNavigationTxn).toMatchObject({
+ contexts: {
+ trace: {
+ op: 'navigation',
+ origin: 'auto.navigation.solid.solidrouter',
+ },
+ },
+ transaction: '/',
+ transaction_info: {
+ source: 'url',
+ },
+ });
+});
From f378772b3d1e7a0dbb3807f9ff12a31c01b312b5 Mon Sep 17 00:00:00 2001
From: Billy Vong
Date: Fri, 12 Jul 2024 11:14:30 -0230
Subject: [PATCH 23/39] feat(replay): Bump `rrweb` to 2.25.0 (#12478)
* Uses clean `Array.from` implementation
* Revert css parsing (player)
* Implements multitouch gestures (player)
* Many upstream fixes
---
.size-limit.js | 2 +-
.../canvas/manualSnapshot/template.html | 1 -
.../replay/canvas/records/template.html | 1 -
packages/replay-canvas/package.json | 2 +-
packages/replay-internal/package.json | 4 +-
packages/replay-internal/src/types/rrweb.ts | 5 ++-
yarn.lock | 43 ++++++++++---------
7 files changed, 30 insertions(+), 28 deletions(-)
diff --git a/.size-limit.js b/.size-limit.js
index a5ce210ef737..2e7899cb934a 100644
--- a/.size-limit.js
+++ b/.size-limit.js
@@ -177,7 +177,7 @@ module.exports = [
path: createCDNPath('bundle.tracing.replay.min.js'),
gzip: false,
brotli: false,
- limit: '221 KB',
+ limit: '230 KB',
},
{
name: 'CDN Bundle (incl. Tracing, Replay, Feedback) - uncompressed',
diff --git a/dev-packages/browser-integration-tests/suites/replay/canvas/manualSnapshot/template.html b/dev-packages/browser-integration-tests/suites/replay/canvas/manualSnapshot/template.html
index 5f23d569fcc2..bd12f84b090a 100644
--- a/dev-packages/browser-integration-tests/suites/replay/canvas/manualSnapshot/template.html
+++ b/dev-packages/browser-integration-tests/suites/replay/canvas/manualSnapshot/template.html
@@ -13,7 +13,6 @@
function draw() {
const canvas = document.getElementById("canvas");
if (canvas.getContext) {
- console.log('has canvas')
const ctx = canvas.getContext("2d");
ctx.fillRect(25, 25, 100, 100);
diff --git a/dev-packages/browser-integration-tests/suites/replay/canvas/records/template.html b/dev-packages/browser-integration-tests/suites/replay/canvas/records/template.html
index 5f23d569fcc2..bd12f84b090a 100644
--- a/dev-packages/browser-integration-tests/suites/replay/canvas/records/template.html
+++ b/dev-packages/browser-integration-tests/suites/replay/canvas/records/template.html
@@ -13,7 +13,6 @@
function draw() {
const canvas = document.getElementById("canvas");
if (canvas.getContext) {
- console.log('has canvas')
const ctx = canvas.getContext("2d");
ctx.fillRect(25, 25, 100, 100);
diff --git a/packages/replay-canvas/package.json b/packages/replay-canvas/package.json
index 9d0bdb6eac50..2af80dec1c27 100644
--- a/packages/replay-canvas/package.json
+++ b/packages/replay-canvas/package.json
@@ -66,7 +66,7 @@
"homepage": "https://docs.sentry.io/platforms/javascript/session-replay/",
"devDependencies": {
"@babel/core": "^7.17.5",
- "@sentry-internal/rrweb": "2.15.0"
+ "@sentry-internal/rrweb": "2.25.0"
},
"dependencies": {
"@sentry-internal/replay": "8.17.0",
diff --git a/packages/replay-internal/package.json b/packages/replay-internal/package.json
index 1ac8466f0e90..bc05dfb4ea80 100644
--- a/packages/replay-internal/package.json
+++ b/packages/replay-internal/package.json
@@ -69,8 +69,8 @@
"devDependencies": {
"@babel/core": "^7.17.5",
"@sentry-internal/replay-worker": "8.17.0",
- "@sentry-internal/rrweb": "2.15.0",
- "@sentry-internal/rrweb-snapshot": "2.15.0",
+ "@sentry-internal/rrweb": "2.25.0",
+ "@sentry-internal/rrweb-snapshot": "2.25.0",
"fflate": "^0.8.1",
"jest-matcher-utils": "^29.0.0",
"jsdom-worker": "^0.2.1"
diff --git a/packages/replay-internal/src/types/rrweb.ts b/packages/replay-internal/src/types/rrweb.ts
index a490a6e46c1b..cb194e193a5d 100644
--- a/packages/replay-internal/src/types/rrweb.ts
+++ b/packages/replay-internal/src/types/rrweb.ts
@@ -31,7 +31,7 @@ export type ReplayEventWithTime = {
/**
* This is a partial copy of rrweb's recording options which only contains the properties
- * we specifically us in the SDK. Users can specify additional properties, hence we add the
+ * we specifically use in the SDK. Users can specify additional properties, hence we add the
* Record union type.
*/
export type RrwebRecordOptions = {
@@ -52,6 +52,9 @@ export interface CanvasManagerInterface {
lock(): void;
unlock(): void;
snapshot(): void;
+ addWindow(win: typeof globalThis & Window): void;
+ addShadowRoot(shadowRoot: ShadowRoot): void;
+ resetShadowRoots(): void;
}
export interface CanvasManagerOptions {
diff --git a/yarn.lock b/yarn.lock
index 1319545eda21..7cb6273253bc 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -8055,22 +8055,22 @@
dependencies:
"@sentry-internal/rrweb-snapshot" "2.11.0"
-"@sentry-internal/rrdom@2.15.0":
- version "2.15.0"
- resolved "https://registry.yarnpkg.com/@sentry-internal/rrdom/-/rrdom-2.15.0.tgz#1ac070a7a00664b2c5351c8ba13979369024128a"
- integrity sha512-LDy2LbmEytIuV9vKTr2dK4iMCTTFTpNW/eJ6IoapB0syYBc4yuUsbH39s/gamxcR5Y7KjkySSh0XkMnCHyV5gg==
+"@sentry-internal/rrdom@2.25.0":
+ version "2.25.0"
+ resolved "https://registry.yarnpkg.com/@sentry-internal/rrdom/-/rrdom-2.25.0.tgz#4be842f7f4efae383bbd5a9dcbbecc212d378d70"
+ integrity sha512-YTxGHnCdv6D2JVJ6YFezMsGOHLy7CM8x8qMaY3Yh3QTubFOjdGpcGJGITF/9Lkx+rFVCTdjL32cQu9NUgEJO8g==
dependencies:
- "@sentry-internal/rrweb-snapshot" "2.15.0"
+ "@sentry-internal/rrweb-snapshot" "2.25.0"
"@sentry-internal/rrweb-snapshot@2.11.0":
version "2.11.0"
resolved "https://registry.yarnpkg.com/@sentry-internal/rrweb-snapshot/-/rrweb-snapshot-2.11.0.tgz#1af79130604afea989d325465b209ac015b27c9a"
integrity sha512-1nP22QlplMNooSNvTh+L30NSZ+E3UcfaJyxXSMLxUjQHTGPyM1VkndxZMmxlKhyR5X+rLbxi/+RvuAcpM43VoA==
-"@sentry-internal/rrweb-snapshot@2.15.0":
- version "2.15.0"
- resolved "https://registry.yarnpkg.com/@sentry-internal/rrweb-snapshot/-/rrweb-snapshot-2.15.0.tgz#04c79d3dc723ed80e4f10685d5ebc6c1b90fcf1b"
- integrity sha512-g/gqzKab6lQ/YvioIXVWQTaQXrUctepqIgXP7vYvpnU+ZmxmsOVd10gQuryDCSLYt2wQiwkffYyeaP2BVqxbwQ==
+"@sentry-internal/rrweb-snapshot@2.25.0":
+ version "2.25.0"
+ resolved "https://registry.yarnpkg.com/@sentry-internal/rrweb-snapshot/-/rrweb-snapshot-2.25.0.tgz#f20bd20436edac24ed1075b47fc4773894739d97"
+ integrity sha512-7j90eSGFRS1YWcuo0bXPtV9oDdCQxutilyYbim/I09GA7kx4/d8OG8ryxQl6WWXW+E50x6dEpDsZXWMPkSleEg==
"@sentry-internal/rrweb-types@2.11.0":
version "2.11.0"
@@ -8079,12 +8079,13 @@
dependencies:
"@sentry-internal/rrweb-snapshot" "2.11.0"
-"@sentry-internal/rrweb-types@2.15.0":
- version "2.15.0"
- resolved "https://registry.yarnpkg.com/@sentry-internal/rrweb-types/-/rrweb-types-2.15.0.tgz#caeabffc227405110946447f30893aa037493b23"
- integrity sha512-D3i9+G4h6gLlG/B1lkP3jc3pM84hP2d2WFGrapTBI0bJou822ERD3Wj9KBVPEkwsRM+qDZRqRMrq0PicdAqJAA==
+"@sentry-internal/rrweb-types@2.25.0":
+ version "2.25.0"
+ resolved "https://registry.yarnpkg.com/@sentry-internal/rrweb-types/-/rrweb-types-2.25.0.tgz#61662befc57ed7054a491eb35ad3deda7d66157c"
+ integrity sha512-sM2YdevhIRxQ/Kr89cfbNBO7/EFhycTmQT0NKg4owdKkIvuuqz1AhbRpMMdpJ4NJnos+h06VPObeXm6rcrffsw==
dependencies:
- "@sentry-internal/rrweb-snapshot" "2.15.0"
+ "@sentry-internal/rrweb-snapshot" "2.25.0"
+ "@types/css-font-loading-module" "0.0.7"
"@sentry-internal/rrweb@2.11.0":
version "2.11.0"
@@ -8100,14 +8101,14 @@
fflate "^0.4.4"
mitt "^3.0.0"
-"@sentry-internal/rrweb@2.15.0":
- version "2.15.0"
- resolved "https://registry.yarnpkg.com/@sentry-internal/rrweb/-/rrweb-2.15.0.tgz#a38dff464624c7ab421579b5ec626007e10c9da8"
- integrity sha512-WO2QJJMJYVcuc8aq6j4YEzNo512FZ2Ro7/04Ip1MYhPI4BpHhn3KI7lRoHvprZeVNYWXyBtiPy7JFehuVCppdw==
+"@sentry-internal/rrweb@2.25.0":
+ version "2.25.0"
+ resolved "https://registry.yarnpkg.com/@sentry-internal/rrweb/-/rrweb-2.25.0.tgz#0148f1904f1e9549f2c2cae209fe3d3fe891d3ec"
+ integrity sha512-0tgBI0CFpyO3Z3dw4IjS/D6AnQypro4dquRrcZZzqnMH65Vxw3yytGDtmvE/FzHzGC0vmKFTM+sTkzFY0bo+Bg==
dependencies:
- "@sentry-internal/rrdom" "2.15.0"
- "@sentry-internal/rrweb-snapshot" "2.15.0"
- "@sentry-internal/rrweb-types" "2.15.0"
+ "@sentry-internal/rrdom" "2.25.0"
+ "@sentry-internal/rrweb-snapshot" "2.25.0"
+ "@sentry-internal/rrweb-types" "2.25.0"
"@types/css-font-loading-module" "0.0.7"
"@xstate/fsm" "^1.4.0"
base64-arraybuffer "^1.0.1"
From 9c92ece0a4a594afa3673f1759f0dc1feb55f004 Mon Sep 17 00:00:00 2001
From: Lukas Stracke
Date: Fri, 12 Jul 2024 16:28:25 +0200
Subject: [PATCH 24/39] ref(e2e-tests): Make `startCommand` optional in shared
playwright config (#12842)
Not all our e2e tests require a `startCommand` as we sometimes directly
invoke an application from within tests. For example in our AWS lambda
e2e tests. This PR makes the `startCommand` option optional so that we
can avoid a larger override of the entire `webServer` playwright config
object.
---
.../aws-lambda-layer-cjs/playwright.config.ts | 80 +------------------
.../aws-serverless-esm/playwright.config.ts | 26 +-----
dev-packages/test-utils/.eslintrc.js | 9 ++-
.../test-utils/src/playwright-config.ts | 35 ++++----
4 files changed, 31 insertions(+), 119 deletions(-)
diff --git a/dev-packages/e2e-tests/test-applications/aws-lambda-layer-cjs/playwright.config.ts b/dev-packages/e2e-tests/test-applications/aws-lambda-layer-cjs/playwright.config.ts
index 7b14daadc6d1..174593c307df 100644
--- a/dev-packages/e2e-tests/test-applications/aws-lambda-layer-cjs/playwright.config.ts
+++ b/dev-packages/e2e-tests/test-applications/aws-lambda-layer-cjs/playwright.config.ts
@@ -1,79 +1,3 @@
-import type { PlaywrightTestConfig } from '@playwright/test';
-import { devices } from '@playwright/test';
+import { getPlaywrightConfig } from '@sentry-internal/test-utils';
-// Fix urls not resolving to localhost on Node v17+
-// See: https://github.com/axios/axios/issues/3821#issuecomment-1413727575
-import { setDefaultResultOrder } from 'dns';
-setDefaultResultOrder('ipv4first');
-
-const eventProxyPort = 3031;
-const lambdaPort = 3030;
-
-/**
- * See https://playwright.dev/docs/test-configuration.
- */
-const config: PlaywrightTestConfig = {
- testDir: './tests',
- /* Maximum time one test can run for. */
- timeout: 150_000,
- expect: {
- /**
- * Maximum time expect() should wait for the condition to be met.
- * For example in `await expect(locator).toHaveText();`
- */
- timeout: 5000,
- },
- /* Run tests in files in parallel */
- fullyParallel: true,
- /* Fail the build on CI if you accidentally left test.only in the source code. */
- forbidOnly: !!process.env.CI,
- /* Retry on CI only */
- retries: 0,
- /* Reporter to use. See https://playwright.dev/docs/test-reporters */
- reporter: 'list',
- /* Shared settings for all the projects below. See https://playwright.dev/docs/api/class-testoptions. */
- use: {
- /* Maximum time each action such as `click()` can take. Defaults to 0 (no limit). */
- actionTimeout: 0,
-
- /* Base URL to use in actions like `await page.goto('/')`. */
- baseURL: `http://localhost:${lambdaPort}`,
-
- /* Collect trace when retrying the failed test. See https://playwright.dev/docs/trace-viewer */
- trace: 'on-first-retry',
- },
-
- /* Configure projects for major browsers */
- projects: [
- {
- name: 'chromium',
- use: {
- ...devices['Desktop Chrome'],
- },
- },
- // For now we only test Chrome!
- // {
- // name: 'firefox',
- // use: {
- // ...devices['Desktop Firefox'],
- // },
- // },
- // {
- // name: 'webkit',
- // use: {
- // ...devices['Desktop Safari'],
- // },
- // },
- ],
-
- /* Run your local dev server before starting the tests */
- webServer: [
- {
- command: `node start-event-proxy.mjs && pnpm wait-port ${eventProxyPort}`,
- port: eventProxyPort,
- stdout: 'pipe',
- },
- ],
-};
-
-export default config;
+export default getPlaywrightConfig();
diff --git a/dev-packages/e2e-tests/test-applications/aws-serverless-esm/playwright.config.ts b/dev-packages/e2e-tests/test-applications/aws-serverless-esm/playwright.config.ts
index 9b4853af2033..174593c307df 100644
--- a/dev-packages/e2e-tests/test-applications/aws-serverless-esm/playwright.config.ts
+++ b/dev-packages/e2e-tests/test-applications/aws-serverless-esm/playwright.config.ts
@@ -1,27 +1,3 @@
import { getPlaywrightConfig } from '@sentry-internal/test-utils';
-// Fix urls not resolving to localhost on Node v17+
-// See: https://github.com/axios/axios/issues/3821#issuecomment-1413727575
-import { setDefaultResultOrder } from 'dns';
-setDefaultResultOrder('ipv4first');
-
-const eventProxyPort = 3031;
-
-/**
- * See https://playwright.dev/docs/test-configuration.
- */
-const config = getPlaywrightConfig(
- { startCommand: '' },
- {
- /* Run your local dev server before starting the tests */
- webServer: [
- {
- command: `node start-event-proxy.mjs && pnpm wait-port ${eventProxyPort}`,
- port: eventProxyPort,
- stdout: 'pipe',
- },
- ],
- },
-);
-
-export default config;
+export default getPlaywrightConfig();
diff --git a/dev-packages/test-utils/.eslintrc.js b/dev-packages/test-utils/.eslintrc.js
index 175b9389af00..98318aea5c41 100644
--- a/dev-packages/test-utils/.eslintrc.js
+++ b/dev-packages/test-utils/.eslintrc.js
@@ -3,5 +3,12 @@ module.exports = {
node: true,
},
extends: ['../../.eslintrc.js'],
- overrides: [],
+ overrides: [
+ {
+ files: ['**/*.ts'],
+ rules: {
+ '@sentry-internal/sdk/no-optional-chaining': 'off',
+ },
+ },
+ ],
};
diff --git a/dev-packages/test-utils/src/playwright-config.ts b/dev-packages/test-utils/src/playwright-config.ts
index 33de29f5a7fc..a48ca969ad06 100644
--- a/dev-packages/test-utils/src/playwright-config.ts
+++ b/dev-packages/test-utils/src/playwright-config.ts
@@ -2,8 +2,8 @@ import type { PlaywrightTestConfig } from '@playwright/test';
/** Get a playwright config to use in an E2E test app. */
export function getPlaywrightConfig(
- options: {
- startCommand: string;
+ options?: {
+ startCommand?: string;
port?: number;
eventProxyPort?: number;
eventProxyFile?: string;
@@ -11,10 +11,10 @@ export function getPlaywrightConfig(
overwriteConfig?: Partial,
): PlaywrightTestConfig {
const testEnv = process.env['TEST_ENV'] || 'production';
- const appPort = options.port || 3030;
- const eventProxyPort = options.eventProxyPort || 3031;
- const eventProxyFile = options.eventProxyFile || 'start-event-proxy.mjs';
- const { startCommand } = options;
+ const appPort = options?.port || 3030;
+ const eventProxyPort = options?.eventProxyPort || 3031;
+ const eventProxyFile = options?.eventProxyFile || 'start-event-proxy.mjs';
+ const startCommand = options?.startCommand;
/**
* See https://playwright.dev/docs/test-configuration.
@@ -76,18 +76,23 @@ export function getPlaywrightConfig(
stdout: 'pipe',
stderr: 'pipe',
},
- {
- command: startCommand,
- port: appPort,
- stdout: 'pipe',
- stderr: 'pipe',
- env: {
- PORT: appPort.toString(),
- },
- },
],
};
+ if (startCommand) {
+ // @ts-expect-error - we set `config.webserver` to an array above.
+ // TS just can't infer that and thinks it could also be undefined or an object.
+ config.webServer.push({
+ command: startCommand,
+ port: appPort,
+ stdout: 'pipe',
+ stderr: 'pipe',
+ env: {
+ PORT: appPort.toString(),
+ },
+ });
+ }
+
return {
...config,
...overwriteConfig,
From 673addc5d111b5ae27778df3ef1a2a5259059865 Mon Sep 17 00:00:00 2001
From: Abhijeet Prasad
Date: Fri, 12 Jul 2024 10:29:37 -0400
Subject: [PATCH 25/39] docs: Add apollo integration to migration docs (#12889)
resolves https://github.com/getsentry/sentry-javascript/issues/12887
---
MIGRATION.md | 16 ++++++++++++++++
1 file changed, 16 insertions(+)
diff --git a/MIGRATION.md b/MIGRATION.md
index 172d6cb433f3..88235b6fd235 100644
--- a/MIGRATION.md
+++ b/MIGRATION.md
@@ -711,6 +711,7 @@ Removed top-level exports: `enableAnrDetection`, `Anr`, `deepReadDirSync`, `runW
- [Removal of `enableAnrDetection` and `Anr` class](./MIGRATION.md#removal-of-enableanrdetection-and-anr-class)
- [Removal of `deepReadDirSync` method](./MIGRATION.md#removal-of-deepreaddirsync-method)
- [Removal of `runWithAsyncContext` method](./MIGRATION.md#removal-of-runwithasynccontext-method)
+- [Removal of `Apollo` integration](./MIGRATION.md#removal-of-apollo-integration)
#### Removal of `enableAnrDetection` and `Anr` class
@@ -737,6 +738,21 @@ Sentry.withIsolationScope(() => {
});
```
+#### Removal of Apollo integration
+
+The Apollo integration has been removed in `8.x` as `8.x` automatically adds GraphQL support via `graphqlIntegration`
+which is automatically enabled.
+
+```js
+// before (v7)
+Sentry.init({
+ integrations: [Sentry.integrations.Apollo()],
+});
+
+// after (v8)
+Sentry.init({});
+```
+
### Next.js SDK
Removed top-level exports: `withSentryApi`, `withSentryAPI`, `withSentryGetServerSideProps`, `withSentryGetStaticProps`,
From 289d06977eda4ba64cc3390ed3af9f400e40467f Mon Sep 17 00:00:00 2001
From: Jonas
Date: Fri, 12 Jul 2024 14:32:22 -0400
Subject: [PATCH 26/39] chore: add python version requirement to contributing
doc (#12901)
Adding a note here too as first time users are likely to be following
this doc when they see the build fail due to python version
incompatibility
---
CONTRIBUTING.md | 3 +++
1 file changed, 3 insertions(+)
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
index 215527c16495..b74d59693a18 100644
--- a/CONTRIBUTING.md
+++ b/CONTRIBUTING.md
@@ -40,6 +40,9 @@ able to use it. From the top level of the repo, there are three commands availab
dependencies (`utils`, `core`, `browser`, etc), and all packages which depend on it (currently `gatsby` and `nextjs`))
- `yarn build:dev:watch`, which runs `yarn build:dev` in watch mode (recommended)
+Note: Due to package incompatibilities between Python versions, building native binaries currently requires a Python
+version <3.12.
+
You can also run a production build via `yarn build`, which will build everything except for the tarballs for publishing
to NPM. You can use this if you want to bundle Sentry yourself. The build output can be found in the packages `build/`
folder, e.g. `packages/browser/build`. Bundled files can be found in `packages/browser/build/bundles`. Note that there
From eaf605550d643ede07bcbfb943210f15d943ca98 Mon Sep 17 00:00:00 2001
From: Jonas
Date: Fri, 12 Jul 2024 14:33:32 -0400
Subject: [PATCH 27/39] docs(profiling-node): add detailed docs on how to build
bindings (#12900)
Add better build docs on how to build profiling binaries from source
#12899
---
packages/profiling-node/README.md | 18 ++++++++++++++++++
1 file changed, 18 insertions(+)
diff --git a/packages/profiling-node/README.md b/packages/profiling-node/README.md
index 7203752643ed..4357e23bb194 100644
--- a/packages/profiling-node/README.md
+++ b/packages/profiling-node/README.md
@@ -56,6 +56,8 @@ there is a fairly good chance this will work out of the box. The required packag
**Windows:** If you are building on windows, you may need to install windows-build-tools
+**_Python:_** Python 3.12 is not supported yet so you will need a version of python that is lower than 3.12
+
```bash
# using yarn package manager
@@ -64,6 +66,22 @@ yarn global add windows-build-tools
npm i -g windows-build-tools
```
+After you have installed the toolchain, you should be able to build the binaries from source
+
+```bash
+# configure node-gyp using yarn
+yarn build:bindings:configure
+# or using npm
+npm run build:bindings:configure
+
+# compile the binaries using yarn
+yarn build:bindings
+# or using npm
+npm run build:bindings
+```
+
+After the binaries are built, you should see them inside the profiling-node/lib folder.
+
### Prebuilt binaries
We currently ship prebuilt binaries for a few of the most common platforms and node versions (v16-22).
From 616915b7aeeeb08f66024a940d88a64e241cb4ad Mon Sep 17 00:00:00 2001
From: Luca Forstner
Date: Mon, 15 Jul 2024 10:38:52 +0200
Subject: [PATCH 28/39] ref: Deprecate `enableTracing` (#12897)
---
CHANGELOG.md | 9 +++++++++
packages/core/src/utils/hasTracingEnabled.ts | 1 +
packages/node/src/sdk/index.ts | 1 +
packages/types/src/options.ts | 6 +++++-
4 files changed, 16 insertions(+), 1 deletion(-)
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 68d01cdce384..6fd671757241 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -8,6 +8,15 @@
- "You miss 100 percent of the chances you don't take. — Wayne Gretzky" — Michael Scott
+### Important Changes
+
+- **ref: Deprecate `enableTracing` (12897)**
+
+The `enableTracing` option has been deprecated and will be removed in the next major version. We recommend removing it
+in favor of the `tracesSampleRate` and `tracesSampler` options. If you want to enable performance monitoring, please set
+the `tracesSampleRate` to a sample rate of your choice, or provide a sampling function as `tracesSampler` option
+instead. If you wan't to disable performance monitoring, remove the `tracesSampler` and `tracesSampleRate` options.
+
Work in this release was contributed by @GitSquared. Thank you for your contribution!
## 8.17.0
diff --git a/packages/core/src/utils/hasTracingEnabled.ts b/packages/core/src/utils/hasTracingEnabled.ts
index 97463d9d5e5e..5e673bc08caa 100644
--- a/packages/core/src/utils/hasTracingEnabled.ts
+++ b/packages/core/src/utils/hasTracingEnabled.ts
@@ -17,6 +17,7 @@ export function hasTracingEnabled(
}
const options = maybeOptions || getClientOptions();
+ // eslint-disable-next-line deprecation/deprecation
return !!options && (options.enableTracing || 'tracesSampleRate' in options || 'tracesSampler' in options);
}
diff --git a/packages/node/src/sdk/index.ts b/packages/node/src/sdk/index.ts
index 2ac62a2b5b91..0fb7774827f9 100644
--- a/packages/node/src/sdk/index.ts
+++ b/packages/node/src/sdk/index.ts
@@ -92,6 +92,7 @@ function shouldAddPerformanceIntegrations(options: Options): boolean {
}
// We want to ensure `tracesSampleRate` is not just undefined/null here
+ // eslint-disable-next-line deprecation/deprecation
return options.enableTracing || options.tracesSampleRate != null || 'tracesSampler' in options;
}
diff --git a/packages/types/src/options.ts b/packages/types/src/options.ts
index 8b2dbebdd574..d6c407d60bd0 100644
--- a/packages/types/src/options.ts
+++ b/packages/types/src/options.ts
@@ -89,9 +89,13 @@ export interface ClientOptions
Date: Mon, 15 Jul 2024 10:39:20 +0200
Subject: [PATCH 29/39] ref(core): Small bundle size improvement (#12830)
---
packages/core/src/integrations/metadata.ts | 2 +-
packages/core/src/integrations/third-party-errors-filter.ts | 2 +-
2 files changed, 2 insertions(+), 2 deletions(-)
diff --git a/packages/core/src/integrations/metadata.ts b/packages/core/src/integrations/metadata.ts
index 48fa23d6b6ab..0bf11746dc1b 100644
--- a/packages/core/src/integrations/metadata.ts
+++ b/packages/core/src/integrations/metadata.ts
@@ -33,7 +33,7 @@ export const moduleMetadataIntegration = defineIntegration(() => {
client.on('applyFrameMetadata', event => {
// Only apply stack frame metadata to error events
- if (event.type !== undefined) {
+ if (event.type) {
return;
}
diff --git a/packages/core/src/integrations/third-party-errors-filter.ts b/packages/core/src/integrations/third-party-errors-filter.ts
index 1f1887604866..652ca23a6da6 100644
--- a/packages/core/src/integrations/third-party-errors-filter.ts
+++ b/packages/core/src/integrations/third-party-errors-filter.ts
@@ -56,7 +56,7 @@ export const thirdPartyErrorFilterIntegration = defineIntegration((options: Opti
client.on('applyFrameMetadata', event => {
// Only apply stack frame metadata to error events
- if (event.type !== undefined) {
+ if (event.type) {
return;
}
From b538f8979318eb1f49f5d040826764677405d876 Mon Sep 17 00:00:00 2001
From: Tim Fish
Date: Mon, 15 Jul 2024 14:02:29 +0200
Subject: [PATCH 30/39] docs(node): Update Node `README.md` (#12916)
---
packages/node/README.md | 38 ++++++++++++++++++++++++++++----------
1 file changed, 28 insertions(+), 10 deletions(-)
diff --git a/packages/node/README.md b/packages/node/README.md
index 20fe8cc175c3..6471538fb4f0 100644
--- a/packages/node/README.md
+++ b/packages/node/README.md
@@ -21,6 +21,11 @@ yarn add @sentry/node
## Usage
+Sentry should be initialized as early in your app as possible. It is essential that you call `Sentry.init` before you
+require any other modules in your application, otherwise auto-instrumentation of these modules will **not** work.
+
+You need to create a file named `instrument.js` that imports and initializes Sentry:
+
```js
// CJS Syntax
const Sentry = require('@sentry/node');
@@ -33,26 +38,39 @@ Sentry.init({
});
```
-Note that it is necessary to initialize Sentry **before you import any package that may be instrumented by us**.
+You need to require or import the `instrument.js` file before importing any other modules in your application. This is
+necessary to ensure that Sentry can automatically instrument all modules in your application:
+
+```js
+// Import this first!
+import './instrument';
+
+// Now import other modules
+import http from 'http';
-[More information on how to set up Sentry for Node in v8.](https://github.com/getsentry/sentry-javascript/blob/develop/docs/v8-node.md)
+// Your application code goes here
+```
### ESM Support
-Due to the way OpenTelemetry handles instrumentation, this only works out of the box for CommonJS (`require`)
-applications.
+When running your application in ESM mode, you should use the Node.js
+[`--import`](https://nodejs.org/api/cli.html#--importmodule) command line option to ensure that Sentry is loaded before
+the application code is evaluated.
-There is experimental support for running OpenTelemetry with ESM (`"type": "module"`):
+Adjust the Node.js call for your application to use the `--import` parameter and point it at `instrument.js`, which
+contains your `Sentry.init`() code:
```bash
-node --experimental-loader=@opentelemetry/instrumentation/hook.mjs ./app.js
+# Note: This is only available for Node v18.19.0 onwards.
+node --import ./instrument.mjs app.mjs
```
-You'll need to install `@opentelemetry/instrumentation` in your app to ensure this works.
+If it is not possible for you to pass the `--import` flag to the Node.js binary, you can alternatively use the
+`NODE_OPTIONS` environment variable as follows:
-See
-[OpenTelemetry Instrumentation Docs](https://github.com/open-telemetry/opentelemetry-js/tree/main/experimental/packages/opentelemetry-instrumentation#instrumentation-for-es-modules-in-nodejs-experimental)
-for details on this - but note that this is a) experimental, and b) does not work with all integrations.
+```bash
+NODE_OPTIONS="--import ./instrument.mjs" npm run start
+```
## Links
From 05b2754b28bbfc122d8e5897ce42d9531457a570 Mon Sep 17 00:00:00 2001
From: Luca Forstner
Date: Mon, 15 Jul 2024 14:19:46 +0200
Subject: [PATCH 31/39] ci: Fix canary test implosion (#12915)
---
.github/workflows/canary.yml | 1 +
1 file changed, 1 insertion(+)
diff --git a/.github/workflows/canary.yml b/.github/workflows/canary.yml
index 9dcbd43e5547..42c1594ebc5b 100644
--- a/.github/workflows/canary.yml
+++ b/.github/workflows/canary.yml
@@ -18,6 +18,7 @@ env:
${{ github.workspace }}/node_modules
${{ github.workspace }}/packages/*/node_modules
${{ github.workspace }}/dev-packages/*/node_modules
+ ${{ github.workspace }}/packages/utils/build
permissions:
contents: read
From 1d3e2087c8ea64d0439c3d558b8284d046cdce66 Mon Sep 17 00:00:00 2001
From: Tim Fish
Date: Mon, 15 Jul 2024 17:21:43 +0200
Subject: [PATCH 32/39] feat: Expose `exclude` and `include` options for ESM
loader (#12910)
Closes #12878
I added this feature to `import-in-the-middle` which was released in
v1.9.0:
- https://github.com/nodejs/import-in-the-middle/pull/124
This PR changes the hook from `@opentelemetry/instrumentation/hook.mjs`
to `import-in-the-middle/hook.mjs` as it was only pasing though anyway
and the otel hook doesn't pass the `initialize` export.
---
packages/node/src/sdk/index.ts | 2 +-
packages/node/src/sdk/initOtel.ts | 5 +++--
packages/node/src/types.ts | 18 ++++++++++++++++--
3 files changed, 20 insertions(+), 5 deletions(-)
diff --git a/packages/node/src/sdk/index.ts b/packages/node/src/sdk/index.ts
index 0fb7774827f9..7dd145854993 100644
--- a/packages/node/src/sdk/index.ts
+++ b/packages/node/src/sdk/index.ts
@@ -132,7 +132,7 @@ function _init(
}
if (!isCjs() && options.registerEsmLoaderHooks !== false) {
- maybeInitializeEsmLoader();
+ maybeInitializeEsmLoader(options.registerEsmLoaderHooks === true ? undefined : options.registerEsmLoaderHooks);
}
setOpenTelemetryContextAsyncContextStrategy();
diff --git a/packages/node/src/sdk/initOtel.ts b/packages/node/src/sdk/initOtel.ts
index 47c8879ae3e9..947486ba26cb 100644
--- a/packages/node/src/sdk/initOtel.ts
+++ b/packages/node/src/sdk/initOtel.ts
@@ -13,6 +13,7 @@ import { GLOBAL_OBJ, consoleSandbox, logger } from '@sentry/utils';
import { getOpenTelemetryInstrumentationToPreload } from '../integrations/tracing';
import { SentryContextManager } from '../otel/contextManager';
+import type { EsmLoaderHookOptions } from '../types';
import { isCjs } from '../utils/commonjs';
import type { NodeClient } from './client';
@@ -31,7 +32,7 @@ export function initOpenTelemetry(client: NodeClient): void {
}
/** Initialize the ESM loader. */
-export function maybeInitializeEsmLoader(): void {
+export function maybeInitializeEsmLoader(esmHookConfig?: EsmLoaderHookOptions): void {
const [nodeMajor = 0, nodeMinor = 0] = process.versions.node.split('.').map(Number);
// Register hook was added in v20.6.0 and v18.19.0
@@ -43,7 +44,7 @@ export function maybeInitializeEsmLoader(): void {
if (!GLOBAL_OBJ._sentryEsmLoaderHookRegistered && importMetaUrl) {
try {
// @ts-expect-error register is available in these versions
- moduleModule.register('@opentelemetry/instrumentation/hook.mjs', importMetaUrl);
+ moduleModule.register('import-in-the-middle/hook.mjs', importMetaUrl, { data: esmHookConfig });
GLOBAL_OBJ._sentryEsmLoaderHookRegistered = true;
} catch (error) {
logger.warn('Failed to register ESM hook', error);
diff --git a/packages/node/src/types.ts b/packages/node/src/types.ts
index 882114a013f9..9cf3047e6c0a 100644
--- a/packages/node/src/types.ts
+++ b/packages/node/src/types.ts
@@ -4,6 +4,11 @@ import type { ClientOptions, Options, SamplingContext, Scope, Span, TracePropaga
import type { NodeTransportOptions } from './transports';
+export interface EsmLoaderHookOptions {
+ include?: string[];
+ exclude?: string[];
+}
+
export interface BaseNodeOptions {
/**
* List of strings/regex controlling to which outgoing requests
@@ -87,13 +92,22 @@ export interface BaseNodeOptions {
/**
* Whether to register ESM loader hooks to automatically instrument libraries.
- * This is necessary to auto instrument libraries that are loaded via ESM imports, but might it can cause issues
+ * This is necessary to auto instrument libraries that are loaded via ESM imports, but it can cause issues
* with certain libraries. If you run into problems running your app with this enabled,
* please raise an issue in https://github.com/getsentry/sentry-javascript.
*
+ * You can optionally exclude specific modules or only include specific modules from being instrumented by providing
+ * an object with `include` or `exclude` properties.
+ *
+ * ```js
+ * registerEsmLoaderHooks: {
+ * exclude: ['openai'],
+ * }
+ * ```
+ *
* Defaults to `true`.
*/
- registerEsmLoaderHooks?: boolean;
+ registerEsmLoaderHooks?: boolean | EsmLoaderHookOptions;
/** Callback that is executed when a fatal global error occurs. */
onFatalError?(this: void, error: Error): void;
From 383743a8e7150d6f68913b439c3eba8c5cf8874d Mon Sep 17 00:00:00 2001
From: Andrei <168741329+andreiborza@users.noreply.github.com>
Date: Mon, 15 Jul 2024 17:22:38 +0200
Subject: [PATCH 33/39] fix(solidstart): Set proper sentry origin for solid
router integration when used in solidstart sdk (#12919)
The `@sentry/solid/solidrouter` integration is used within the solid
start sdk as well, so we need a way to update the `sentry.origin` for
navigation spans to be for the correct framework.
I opted to read this out of the sdk metadata instead of exposing extra
api surface and passing it through several levels.
---
.../solidstart/tests/performance.client.test.ts | 6 +++---
packages/solid/src/solidrouter.ts | 9 ++++++++-
packages/solid/test/solidrouter.test.tsx | 5 +++++
packages/solidstart/test/client/solidrouter.test.tsx | 9 +++++++--
4 files changed, 23 insertions(+), 6 deletions(-)
diff --git a/dev-packages/e2e-tests/test-applications/solidstart/tests/performance.client.test.ts b/dev-packages/e2e-tests/test-applications/solidstart/tests/performance.client.test.ts
index 17e57ba47d8d..6e5f43e016c8 100644
--- a/dev-packages/e2e-tests/test-applications/solidstart/tests/performance.client.test.ts
+++ b/dev-packages/e2e-tests/test-applications/solidstart/tests/performance.client.test.ts
@@ -36,7 +36,7 @@ test('sends a navigation transaction', async ({ page }) => {
contexts: {
trace: {
op: 'navigation',
- origin: 'auto.navigation.solid.solidrouter',
+ origin: 'auto.navigation.solidstart.solidrouter',
},
},
transaction: '/users/5',
@@ -62,7 +62,7 @@ test('updates the transaction when using the back button', async ({ page }) => {
contexts: {
trace: {
op: 'navigation',
- origin: 'auto.navigation.solid.solidrouter',
+ origin: 'auto.navigation.solidstart.solidrouter',
},
},
transaction: '/users/6',
@@ -82,7 +82,7 @@ test('updates the transaction when using the back button', async ({ page }) => {
contexts: {
trace: {
op: 'navigation',
- origin: 'auto.navigation.solid.solidrouter',
+ origin: 'auto.navigation.solidstart.solidrouter',
},
},
transaction: '/',
diff --git a/packages/solid/src/solidrouter.ts b/packages/solid/src/solidrouter.ts
index 2f343cfe9d7c..da0391dea35e 100644
--- a/packages/solid/src/solidrouter.ts
+++ b/packages/solid/src/solidrouter.ts
@@ -33,11 +33,18 @@ function handleNavigation(location: string): void {
return;
}
+ // The solid router integration will be used for both solid and solid start.
+ // To avoid increasing the api surface with internal properties, we look at
+ // the sdk metadata.
+ const metaData = client.getSdkMetadata();
+ const { name } = (metaData && metaData.sdk) || {};
+ const framework = name && name.includes('solidstart') ? 'solidstart' : 'solid';
+
startBrowserTracingNavigationSpan(client, {
name: location,
attributes: {
[SEMANTIC_ATTRIBUTE_SENTRY_OP]: 'navigation',
- [SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: 'auto.navigation.solid.solidrouter',
+ [SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: `auto.navigation.${framework}.solidrouter`,
[SEMANTIC_ATTRIBUTE_SENTRY_SOURCE]: 'url',
},
});
diff --git a/packages/solid/test/solidrouter.test.tsx b/packages/solid/test/solidrouter.test.tsx
index 029b90794b70..44268e6716ab 100644
--- a/packages/solid/test/solidrouter.test.tsx
+++ b/packages/solid/test/solidrouter.test.tsx
@@ -44,6 +44,11 @@ describe('solidRouterBrowserTracingIntegration', () => {
tracesSampleRate: 1,
transport: () => createTransport({ recordDroppedEvent: () => undefined }, _ => Promise.resolve({})),
stackParser: () => [],
+ _metadata: {
+ sdk: {
+ name: 'sentry.javascript.solid',
+ },
+ },
});
}
diff --git a/packages/solidstart/test/client/solidrouter.test.tsx b/packages/solidstart/test/client/solidrouter.test.tsx
index d6b161c3a7d9..681b8d7b5ce7 100644
--- a/packages/solidstart/test/client/solidrouter.test.tsx
+++ b/packages/solidstart/test/client/solidrouter.test.tsx
@@ -44,6 +44,11 @@ describe('solidRouterBrowserTracingIntegration', () => {
tracesSampleRate: 1,
transport: () => createTransport({ recordDroppedEvent: () => undefined }, _ => Promise.resolve({})),
stackParser: () => [],
+ _metadata: {
+ sdk: {
+ name: 'sentry.javascript.solidstart',
+ },
+ },
});
}
@@ -138,7 +143,7 @@ describe('solidRouterBrowserTracingIntegration', () => {
data: expect.objectContaining({
[SEMANTIC_ATTRIBUTE_SENTRY_SOURCE]: 'url',
[SEMANTIC_ATTRIBUTE_SENTRY_OP]: 'navigation',
- [SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: 'auto.navigation.solid.solidrouter',
+ [SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: 'auto.navigation.solidstart.solidrouter',
}),
}),
);
@@ -170,7 +175,7 @@ describe('solidRouterBrowserTracingIntegration', () => {
data: expect.objectContaining({
[SEMANTIC_ATTRIBUTE_SENTRY_SOURCE]: 'url',
[SEMANTIC_ATTRIBUTE_SENTRY_OP]: 'navigation',
- [SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: 'auto.navigation.solid.solidrouter',
+ [SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: 'auto.navigation.solidstart.solidrouter',
}),
}),
);
From f9ab13848b3c50bd144b096f0261a1c3e1c5b226 Mon Sep 17 00:00:00 2001
From: Francesco Novy
Date: Tue, 16 Jul 2024 09:46:50 +0200
Subject: [PATCH 34/39] ci: Do not run external contributor job for bots
(#12886)
While we specifically ignored dependabot, we did not ignore github bot.
So now, just generally skipping this for any bot users.
---
.github/workflows/external-contributors.yml | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/.github/workflows/external-contributors.yml b/.github/workflows/external-contributors.yml
index 0feac33e1307..50acb2be8e73 100644
--- a/.github/workflows/external-contributors.yml
+++ b/.github/workflows/external-contributors.yml
@@ -18,7 +18,7 @@ jobs:
&& github.event.pull_request.author_association != 'COLLABORATOR'
&& github.event.pull_request.author_association != 'MEMBER'
&& github.event.pull_request.author_association != 'OWNER'
- && github.actor != 'dependabot[bot]'
+ && endsWith(github.actor, '[bot]') == false
steps:
- uses: actions/checkout@v4
- name: Set up Node
From 9d1b35d4c62f9ad299790f549344c756951b36f1 Mon Sep 17 00:00:00 2001
From: Lukas Stracke
Date: Tue, 16 Jul 2024 12:43:32 +0200
Subject: [PATCH 35/39] feat(browser): Add user agent to INP standalone span
attributes (#12896)
Add the `window.navigator.userAgent` string as the
[`user_agent.original`](https://opentelemetry.io/docs/specs/semconv/attributes-registry/user-agent/)
attribute to INP standalone spans.
---
.../suites/tracing/metrics/web-vitals-inp-late/test.ts | 1 +
.../tracing/metrics/web-vitals-inp-parametrized-late/test.ts | 1 +
.../tracing/metrics/web-vitals-inp-parametrized/test.ts | 1 +
.../suites/tracing/metrics/web-vitals-inp/test.ts | 1 +
.../create-remix-app/tests/client-inp.test.ts | 4 ++++
.../test-applications/react-17/tests/transactions.test.ts | 1 +
.../react-router-6/tests/transactions.test.ts | 1 +
packages/browser-utils/src/metrics/inp.ts | 4 ++++
8 files changed, 14 insertions(+)
diff --git a/dev-packages/browser-integration-tests/suites/tracing/metrics/web-vitals-inp-late/test.ts b/dev-packages/browser-integration-tests/suites/tracing/metrics/web-vitals-inp-late/test.ts
index 1ec7ec50998a..1b6bc5bc686d 100644
--- a/dev-packages/browser-integration-tests/suites/tracing/metrics/web-vitals-inp-late/test.ts
+++ b/dev-packages/browser-integration-tests/suites/tracing/metrics/web-vitals-inp-late/test.ts
@@ -77,6 +77,7 @@ sentryTest('should capture an INP click event span after pageload', async ({ bro
'sentry.sample_rate': 1,
'sentry.source': 'custom',
transaction: 'test-url',
+ 'user_agent.original': expect.stringContaining('Chrome'),
},
measurements: {
inp: {
diff --git a/dev-packages/browser-integration-tests/suites/tracing/metrics/web-vitals-inp-parametrized-late/test.ts b/dev-packages/browser-integration-tests/suites/tracing/metrics/web-vitals-inp-parametrized-late/test.ts
index 1354c373253e..a9d5191b5cf3 100644
--- a/dev-packages/browser-integration-tests/suites/tracing/metrics/web-vitals-inp-parametrized-late/test.ts
+++ b/dev-packages/browser-integration-tests/suites/tracing/metrics/web-vitals-inp-parametrized-late/test.ts
@@ -80,6 +80,7 @@ sentryTest(
'sentry.sample_rate': 1,
'sentry.source': 'custom',
transaction: 'test-route',
+ 'user_agent.original': expect.stringContaining('Chrome'),
},
measurements: {
inp: {
diff --git a/dev-packages/browser-integration-tests/suites/tracing/metrics/web-vitals-inp-parametrized/test.ts b/dev-packages/browser-integration-tests/suites/tracing/metrics/web-vitals-inp-parametrized/test.ts
index 248cb7d1e510..87ba1fd8632c 100644
--- a/dev-packages/browser-integration-tests/suites/tracing/metrics/web-vitals-inp-parametrized/test.ts
+++ b/dev-packages/browser-integration-tests/suites/tracing/metrics/web-vitals-inp-parametrized/test.ts
@@ -76,6 +76,7 @@ sentryTest(
'sentry.op': 'ui.interaction.click',
'sentry.origin': 'auto.http.browser.inp',
transaction: 'test-route',
+ 'user_agent.original': expect.stringContaining('Chrome'),
},
measurements: {
inp: {
diff --git a/dev-packages/browser-integration-tests/suites/tracing/metrics/web-vitals-inp/test.ts b/dev-packages/browser-integration-tests/suites/tracing/metrics/web-vitals-inp/test.ts
index 3f9684cf7f2a..594bd9904052 100644
--- a/dev-packages/browser-integration-tests/suites/tracing/metrics/web-vitals-inp/test.ts
+++ b/dev-packages/browser-integration-tests/suites/tracing/metrics/web-vitals-inp/test.ts
@@ -77,6 +77,7 @@ sentryTest(
'sentry.op': 'ui.interaction.click',
'sentry.origin': 'auto.http.browser.inp',
transaction: 'test-url',
+ 'user_agent.original': expect.stringContaining('Chrome'),
},
measurements: {
inp: {
diff --git a/dev-packages/e2e-tests/test-applications/create-remix-app/tests/client-inp.test.ts b/dev-packages/e2e-tests/test-applications/create-remix-app/tests/client-inp.test.ts
index 9469a4462563..8fe1993db3f8 100644
--- a/dev-packages/e2e-tests/test-applications/create-remix-app/tests/client-inp.test.ts
+++ b/dev-packages/e2e-tests/test-applications/create-remix-app/tests/client-inp.test.ts
@@ -30,6 +30,7 @@ test('sends an INP span during pageload', async ({ page }) => {
'sentry.sample_rate': 1,
'sentry.source': 'custom',
replay_id: expect.any(String),
+ 'user_agent.original': expect.stringContaining('Chrome'),
},
description: 'body > div > input#exception-button[type="button"]',
op: 'ui.interaction.click',
@@ -81,6 +82,7 @@ test('sends an INP span after pageload', async ({ page }) => {
'sentry.sample_rate': 1,
'sentry.source': 'custom',
replay_id: expect.any(String),
+ 'user_agent.original': expect.stringContaining('Chrome'),
},
description: 'body > div > input#exception-button[type="button"]',
op: 'ui.interaction.click',
@@ -125,6 +127,7 @@ test('sends an INP span during navigation', async ({ page }) => {
transaction: 'routes/user.$id',
'sentry.exclusive_time': expect.any(Number),
replay_id: expect.any(String),
+ 'user_agent.original': expect.stringContaining('Chrome'),
},
description: '',
op: 'ui.interaction.click',
@@ -178,6 +181,7 @@ test('sends an INP span after navigation', async ({ page }) => {
replay_id: expect.any(String),
'sentry.sample_rate': 1,
'sentry.source': 'custom',
+ 'user_agent.original': expect.stringContaining('Chrome'),
},
description: '',
op: 'ui.interaction.click',
diff --git a/dev-packages/e2e-tests/test-applications/react-17/tests/transactions.test.ts b/dev-packages/e2e-tests/test-applications/react-17/tests/transactions.test.ts
index 665b5c02aafe..3b9c5ab1fdaf 100644
--- a/dev-packages/e2e-tests/test-applications/react-17/tests/transactions.test.ts
+++ b/dev-packages/e2e-tests/test-applications/react-17/tests/transactions.test.ts
@@ -82,6 +82,7 @@ test('sends an INP span', async ({ page }) => {
transaction: '/',
'sentry.exclusive_time': expect.any(Number),
replay_id: expect.any(String),
+ 'user_agent.original': expect.stringContaining('Chrome'),
},
description: 'body > div#root > input#exception-button[type="button"]',
op: 'ui.interaction.click',
diff --git a/dev-packages/e2e-tests/test-applications/react-router-6/tests/transactions.test.ts b/dev-packages/e2e-tests/test-applications/react-router-6/tests/transactions.test.ts
index 39e07b89c0ee..3f108931b00e 100644
--- a/dev-packages/e2e-tests/test-applications/react-router-6/tests/transactions.test.ts
+++ b/dev-packages/e2e-tests/test-applications/react-router-6/tests/transactions.test.ts
@@ -82,6 +82,7 @@ test('sends an INP span', async ({ page }) => {
transaction: '/',
'sentry.exclusive_time': expect.any(Number),
replay_id: expect.any(String),
+ 'user_agent.original': expect.stringContaining('Chrome'),
},
description: 'body > div#root > input#exception-button[type="button"]',
op: 'ui.interaction.click',
diff --git a/packages/browser-utils/src/metrics/inp.ts b/packages/browser-utils/src/metrics/inp.ts
index 1055635bc32f..c4186a20f17e 100644
--- a/packages/browser-utils/src/metrics/inp.ts
+++ b/packages/browser-utils/src/metrics/inp.ts
@@ -12,6 +12,7 @@ import {
} from '@sentry/core';
import type { Integration, Span, SpanAttributes } from '@sentry/types';
import { browserPerformanceTimeOrigin, dropUndefinedKeys, htmlTreeAsString } from '@sentry/utils';
+import { WINDOW } from '../types';
import {
addInpInstrumentationHandler,
addPerformanceInstrumentationHandler,
@@ -129,6 +130,9 @@ function _trackINP(): () => void {
user: userDisplay || undefined,
profile_id: profileId || undefined,
replay_id: replayId || undefined,
+ // INP score calculation in the sentry backend relies on the user agent
+ // to account for different INP values being reported from different browsers
+ 'user_agent.original': WINDOW.navigator && WINDOW.navigator.userAgent,
});
const span = startInactiveSpan({
From 475d66f29fc87282110fb256c69b77740dd36c7d Mon Sep 17 00:00:00 2001
From: Michael Cousins
Date: Tue, 16 Jul 2024 07:43:52 -0400
Subject: [PATCH 36/39] fix(sveltekit): Add Vite peer dep for proper type
resolution (#12926)
Add a simple, optional peer dependency on Vite to
`@sentry/sveltekit` to ensure that it picks up whatever version of Vite
is being used in the specific package it's installed in. This is necessary
because `pnpm` otherwise could resolve the `vite` import to a wrong
version, especially in monorepo environments
---
packages/sveltekit/package.json | 8 +++++++-
1 file changed, 7 insertions(+), 1 deletion(-)
diff --git a/packages/sveltekit/package.json b/packages/sveltekit/package.json
index cbf009831ef0..d2edf5487ed2 100644
--- a/packages/sveltekit/package.json
+++ b/packages/sveltekit/package.json
@@ -31,7 +31,13 @@
"access": "public"
},
"peerDependencies": {
- "@sveltejs/kit": "1.x || 2.x"
+ "@sveltejs/kit": "1.x || 2.x",
+ "vite": "*"
+ },
+ "peerDependenciesMeta": {
+ "vite": {
+ "optional": true
+ }
},
"dependencies": {
"@sentry/core": "8.17.0",
From 707afd6ad16d72ebc3f575bf7822bbc088738445 Mon Sep 17 00:00:00 2001
From: Francesco Novy
Date: Tue, 16 Jul 2024 13:47:47 +0200
Subject: [PATCH 37/39] fix(tracing): Ensure you can pass `null` as
`parentSpan` in `startSpan*` (#12928)
Noticed this while writing docs, this makes this a bit harder to
understand. With this change you can say that `parentSpan` behaves the
same and accepts the same as `withActiveSpan`.
See https://github.com/getsentry/sentry-docs/pull/10729
---
packages/core/src/tracing/trace.ts | 6 ++--
packages/core/test/lib/tracing/trace.test.ts | 25 +++++++++++++++
packages/opentelemetry/src/trace.ts | 4 +--
packages/opentelemetry/test/trace.test.ts | 32 ++++++++++++++++++++
packages/types/src/startSpanOptions.ts | 3 +-
5 files changed, 64 insertions(+), 6 deletions(-)
diff --git a/packages/core/src/tracing/trace.ts b/packages/core/src/tracing/trace.ts
index cdb2efd7221c..0cb98082c590 100644
--- a/packages/core/src/tracing/trace.ts
+++ b/packages/core/src/tracing/trace.ts
@@ -157,7 +157,7 @@ export function startInactiveSpan(options: StartSpanOptions): Span {
// If `options.parentSpan` is defined, we want to wrap the callback in `withActiveSpan`
const wrapper = options.scope
? (callback: () => Span) => withScope(options.scope, callback)
- : customParentSpan
+ : customParentSpan !== undefined
? (callback: () => Span) => withActiveSpan(customParentSpan, callback)
: (callback: () => Span) => callback();
@@ -445,8 +445,8 @@ function getParentSpan(scope: Scope): SentrySpan | undefined {
return span;
}
-function getActiveSpanWrapper(parentSpan?: Span): (callback: () => T) => T {
- return parentSpan
+function getActiveSpanWrapper(parentSpan: Span | undefined | null): (callback: () => T) => T {
+ return parentSpan !== undefined
? (callback: () => T) => {
return withActiveSpan(parentSpan, callback);
}
diff --git a/packages/core/test/lib/tracing/trace.test.ts b/packages/core/test/lib/tracing/trace.test.ts
index 33b8e0572835..fe58ce6f9f7d 100644
--- a/packages/core/test/lib/tracing/trace.test.ts
+++ b/packages/core/test/lib/tracing/trace.test.ts
@@ -282,6 +282,14 @@ describe('startSpan', () => {
expect(getActiveSpan()).toBe(undefined);
});
+ it('allows to pass parentSpan=null', () => {
+ startSpan({ name: 'GET users/[id]' }, () => {
+ startSpan({ name: 'GET users/[id]', parentSpan: null }, span => {
+ expect(spanToJSON(span).parent_span_id).toBe(undefined);
+ });
+ });
+ });
+
it('allows to force a transaction with forceTransaction=true', async () => {
const options = getDefaultTestClientOptions({ tracesSampleRate: 1.0 });
client = new TestClient(options);
@@ -693,6 +701,15 @@ describe('startSpanManual', () => {
expect(getActiveSpan()).toBe(undefined);
});
+ it('allows to pass parentSpan=null', () => {
+ startSpan({ name: 'GET users/[id]' }, () => {
+ startSpanManual({ name: 'child', parentSpan: null }, span => {
+ expect(spanToJSON(span).parent_span_id).toBe(undefined);
+ span.end();
+ });
+ });
+ });
+
it('allows to force a transaction with forceTransaction=true', async () => {
const options = getDefaultTestClientOptions({ tracesSampleRate: 1.0 });
client = new TestClient(options);
@@ -1014,6 +1031,14 @@ describe('startInactiveSpan', () => {
expect(getActiveSpan()).toBeUndefined();
});
+ it('allows to pass parentSpan=null', () => {
+ startSpan({ name: 'outer' }, () => {
+ const span = startInactiveSpan({ name: 'GET users/[id]', parentSpan: null });
+ expect(spanToJSON(span).parent_span_id).toBe(undefined);
+ span.end();
+ });
+ });
+
it('allows to force a transaction with forceTransaction=true', async () => {
const options = getDefaultTestClientOptions({ tracesSampleRate: 1.0 });
client = new TestClient(options);
diff --git a/packages/opentelemetry/src/trace.ts b/packages/opentelemetry/src/trace.ts
index 5ea5381a2db3..6f9fe5dad6d1 100644
--- a/packages/opentelemetry/src/trace.ts
+++ b/packages/opentelemetry/src/trace.ts
@@ -286,8 +286,8 @@ export function continueTrace(options: Parameters[0
});
}
-function getActiveSpanWrapper(parentSpan?: Span | SentrySpan): (callback: () => T) => T {
- return parentSpan
+function getActiveSpanWrapper(parentSpan: Span | SentrySpan | undefined | null): (callback: () => T) => T {
+ return parentSpan !== undefined
? (callback: () => T) => {
// We cast this, because the OTEL Span has a few more methods than our Span interface
// TODO: Add these missing methods to the Span interface
diff --git a/packages/opentelemetry/test/trace.test.ts b/packages/opentelemetry/test/trace.test.ts
index d3ac52327bb6..2332fd1ced05 100644
--- a/packages/opentelemetry/test/trace.test.ts
+++ b/packages/opentelemetry/test/trace.test.ts
@@ -325,6 +325,16 @@ describe('trace', () => {
expect(getActiveSpan()).toBe(undefined);
});
+ it('allows to pass parentSpan=null', () => {
+ startSpan({ name: 'GET users/[id' }, () => {
+ startSpan({ name: 'child', parentSpan: null }, span => {
+ // Due to the way we propagate the scope in OTEL,
+ // the parent_span_id is not actually undefined here, but comes from the propagation context
+ expect(spanToJSON(span).parent_span_id).toBe(getCurrentScope().getPropagationContext().spanId);
+ });
+ });
+ });
+
it('allows to force a transaction with forceTransaction=true', async () => {
const client = getClient()!;
const transactionEvents: Event[] = [];
@@ -577,6 +587,17 @@ describe('trace', () => {
expect(getActiveSpan()).toBe(undefined);
});
+ it('allows to pass parentSpan=null', () => {
+ startSpan({ name: 'outer' }, () => {
+ const span = startInactiveSpan({ name: 'test span', parentSpan: null });
+
+ // Due to the way we propagate the scope in OTEL,
+ // the parent_span_id is not actually undefined here, but comes from the propagation context
+ expect(spanToJSON(span).parent_span_id).toBe(getCurrentScope().getPropagationContext().spanId);
+ span.end();
+ });
+ });
+
it('allows to force a transaction with forceTransaction=true', async () => {
const client = getClient()!;
const transactionEvents: Event[] = [];
@@ -856,6 +877,17 @@ describe('trace', () => {
expect(getActiveSpan()).toBe(undefined);
});
+ it('allows to pass parentSpan=null', () => {
+ startSpan({ name: 'outer' }, () => {
+ startSpanManual({ name: 'GET users/[id]', parentSpan: null }, span => {
+ // Due to the way we propagate the scope in OTEL,
+ // the parent_span_id is not actually undefined here, but comes from the propagation context
+ expect(spanToJSON(span).parent_span_id).toBe(getCurrentScope().getPropagationContext().spanId);
+ span.end();
+ });
+ });
+ });
+
it('allows to force a transaction with forceTransaction=true', async () => {
const client = getClient()!;
const transactionEvents: Event[] = [];
diff --git a/packages/types/src/startSpanOptions.ts b/packages/types/src/startSpanOptions.ts
index 89e523f6c922..35d5326e32f3 100644
--- a/packages/types/src/startSpanOptions.ts
+++ b/packages/types/src/startSpanOptions.ts
@@ -20,8 +20,9 @@ export interface StartSpanOptions {
/**
* If provided, make the new span a child of this span.
* If this is not provided, the new span will be a child of the currently active span.
+ * If this is set to `null`, the new span will have no parent span.
*/
- parentSpan?: Span;
+ parentSpan?: Span | null;
/**
* If set to true, this span will be forced to be treated as a transaction in the Sentry UI, if possible and applicable.
From 6f4c0452f213ac263787727437efd38846c0505c Mon Sep 17 00:00:00 2001
From: Lukas Stracke
Date: Tue, 16 Jul 2024 13:56:55 +0200
Subject: [PATCH 38/39] fix(node): Ensure correct URL is passed to
`ignoreIncomingRequests` callback (#12929)
Fix an oversight in our Node `httpIntegration`. It looks like
we assumed that the `request` object being passed to
`ignoreIncomingRequestHook` and `ignoreOutgoingRequestHook` was of the
same type. However, it's not:
- `request` is of type `IncomingMessage` in `ignoreIncomingRequestHook`
- `request` is of type `RequestOptions` in `ignoreOutgoingRequestHook`
fix the bug by simply taking the request.url property instead and adds integration tests to properly test the two options.
---
.../node-integration-tests/src/index.ts | 10 +++-
.../server-ignoreIncomingRequests.js | 38 +++++++++++++
.../server-ignoreOutgoingRequests.js | 42 ++++++++++++++
.../suites/tracing/httpIntegration/test.ts | 55 +++++++++++++++++++
packages/node/src/integrations/http.ts | 14 ++++-
5 files changed, 153 insertions(+), 6 deletions(-)
create mode 100644 dev-packages/node-integration-tests/suites/tracing/httpIntegration/server-ignoreIncomingRequests.js
create mode 100644 dev-packages/node-integration-tests/suites/tracing/httpIntegration/server-ignoreOutgoingRequests.js
diff --git a/dev-packages/node-integration-tests/src/index.ts b/dev-packages/node-integration-tests/src/index.ts
index 08afc11fe7ea..4bd0a9ccce25 100644
--- a/dev-packages/node-integration-tests/src/index.ts
+++ b/dev-packages/node-integration-tests/src/index.ts
@@ -20,13 +20,17 @@ export function loggingTransport(_options: BaseTransportOptions): Transport {
/**
* Starts an express server and sends the port to the runner
+ * @param app Express app
+ * @param port Port to start the app on. USE WITH CAUTION! By default a random port will be chosen.
+ * Setting this port to something specific is useful for local debugging but dangerous for
+ * CI/CD environments where port collisions can cause flakes!
*/
-export function startExpressServerAndSendPortToRunner(app: Express): void {
- const server = app.listen(0, () => {
+export function startExpressServerAndSendPortToRunner(app: Express, port: number | undefined = undefined): void {
+ const server = app.listen(port || 0, () => {
const address = server.address() as AddressInfo;
// eslint-disable-next-line no-console
- console.log(`{"port":${address.port}}`);
+ console.log(`{"port":${port || address.port}}`);
});
}
diff --git a/dev-packages/node-integration-tests/suites/tracing/httpIntegration/server-ignoreIncomingRequests.js b/dev-packages/node-integration-tests/suites/tracing/httpIntegration/server-ignoreIncomingRequests.js
new file mode 100644
index 000000000000..f1e5d9870fcf
--- /dev/null
+++ b/dev-packages/node-integration-tests/suites/tracing/httpIntegration/server-ignoreIncomingRequests.js
@@ -0,0 +1,38 @@
+const { loggingTransport } = require('@sentry-internal/node-integration-tests');
+const Sentry = require('@sentry/node');
+
+Sentry.init({
+ dsn: 'https://public@dsn.ingest.sentry.io/1337',
+ release: '1.0',
+ tracesSampleRate: 1.0,
+ transport: loggingTransport,
+
+ integrations: [
+ Sentry.httpIntegration({
+ ignoreIncomingRequests: url => {
+ return url.includes('/liveness');
+ },
+ }),
+ ],
+});
+
+// express must be required after Sentry is initialized
+const express = require('express');
+const cors = require('cors');
+const { startExpressServerAndSendPortToRunner } = require('@sentry-internal/node-integration-tests');
+
+const app = express();
+
+app.use(cors());
+
+app.get('/test', (_req, res) => {
+ res.send({ response: 'response 1' });
+});
+
+app.get('/liveness', (_req, res) => {
+ res.send({ response: 'liveness' });
+});
+
+Sentry.setupExpressErrorHandler(app);
+
+startExpressServerAndSendPortToRunner(app);
diff --git a/dev-packages/node-integration-tests/suites/tracing/httpIntegration/server-ignoreOutgoingRequests.js b/dev-packages/node-integration-tests/suites/tracing/httpIntegration/server-ignoreOutgoingRequests.js
new file mode 100644
index 000000000000..ce520c999259
--- /dev/null
+++ b/dev-packages/node-integration-tests/suites/tracing/httpIntegration/server-ignoreOutgoingRequests.js
@@ -0,0 +1,42 @@
+const { loggingTransport } = require('@sentry-internal/node-integration-tests');
+const Sentry = require('@sentry/node');
+const http = require('http');
+
+Sentry.init({
+ dsn: 'https://public@dsn.ingest.sentry.io/1337',
+ release: '1.0',
+ tracesSampleRate: 1.0,
+ transport: loggingTransport,
+
+ integrations: [
+ Sentry.httpIntegration({
+ ignoreOutgoingRequests: url => {
+ return url.includes('example.com');
+ },
+ }),
+ ],
+});
+
+// express must be required after Sentry is initialized
+const express = require('express');
+const cors = require('cors');
+const { startExpressServerAndSendPortToRunner } = require('@sentry-internal/node-integration-tests');
+
+const app = express();
+
+app.use(cors());
+
+app.get('/test', (_req, response) => {
+ http
+ .request('http://example.com/', res => {
+ res.on('data', () => {});
+ res.on('end', () => {
+ response.send({ response: 'done' });
+ });
+ })
+ .end();
+});
+
+Sentry.setupExpressErrorHandler(app);
+
+startExpressServerAndSendPortToRunner(app);
diff --git a/dev-packages/node-integration-tests/suites/tracing/httpIntegration/test.ts b/dev-packages/node-integration-tests/suites/tracing/httpIntegration/test.ts
index 6be5d36e2ee3..aebe0dd676ba 100644
--- a/dev-packages/node-integration-tests/suites/tracing/httpIntegration/test.ts
+++ b/dev-packages/node-integration-tests/suites/tracing/httpIntegration/test.ts
@@ -75,4 +75,59 @@ describe('httpIntegration', () => {
.start(done)
.makeRequest('get', '/test');
});
+
+ test("doesn't create a root span for incoming requests ignored via `ignoreIncomingRequests`", done => {
+ const runner = createRunner(__dirname, 'server-ignoreIncomingRequests.js')
+ .expect({
+ transaction: {
+ contexts: {
+ trace: {
+ span_id: expect.any(String),
+ trace_id: expect.any(String),
+ data: {
+ url: expect.stringMatching(/\/test$/),
+ 'http.response.status_code': 200,
+ },
+ op: 'http.server',
+ status: 'ok',
+ },
+ },
+ transaction: 'GET /test',
+ },
+ })
+ .start(done);
+
+ runner.makeRequest('get', '/liveness'); // should be ignored
+ runner.makeRequest('get', '/test');
+ });
+
+ test("doesn't create child spans for outgoing requests ignored via `ignoreOutgoingRequests`", done => {
+ const runner = createRunner(__dirname, 'server-ignoreOutgoingRequests.js')
+ .expect({
+ transaction: {
+ contexts: {
+ trace: {
+ span_id: expect.any(String),
+ trace_id: expect.any(String),
+ data: {
+ url: expect.stringMatching(/\/test$/),
+ 'http.response.status_code': 200,
+ },
+ op: 'http.server',
+ status: 'ok',
+ },
+ },
+ transaction: 'GET /test',
+ spans: [
+ expect.objectContaining({ op: 'middleware.express', description: 'query' }),
+ expect.objectContaining({ op: 'middleware.express', description: 'expressInit' }),
+ expect.objectContaining({ op: 'middleware.express', description: 'corsMiddleware' }),
+ expect.objectContaining({ op: 'request_handler.express', description: '/test' }),
+ ],
+ },
+ })
+ .start(done);
+
+ runner.makeRequest('get', '/test');
+ });
});
diff --git a/packages/node/src/integrations/http.ts b/packages/node/src/integrations/http.ts
index 418fa8aa7853..632b6023e7a3 100644
--- a/packages/node/src/integrations/http.ts
+++ b/packages/node/src/integrations/http.ts
@@ -34,14 +34,20 @@ interface HttpOptions {
/**
* Do not capture spans or breadcrumbs for outgoing HTTP requests to URLs where the given callback returns `true`.
* This controls both span & breadcrumb creation - spans will be non recording if tracing is disabled.
+ *
+ * The `url` param contains the entire URL, including query string (if any), protocol, host, etc. of the outgoing request.
+ * For example: `'https://someService.com/users/details?id=123'`
*/
ignoreOutgoingRequests?: (url: string) => boolean;
/**
* Do not capture spans or breadcrumbs for incoming HTTP requests to URLs where the given callback returns `true`.
* This controls both span & breadcrumb creation - spans will be non recording if tracing is disabled.
+ *
+ * The `urlPath` param consists of the URL path and query string (if any) of the incoming request.
+ * For example: `'/users/details?id=123'`
*/
- ignoreIncomingRequests?: (url: string) => boolean;
+ ignoreIncomingRequests?: (urlPath: string) => boolean;
/**
* Additional instrumentation options that are passed to the underlying HttpInstrumentation.
@@ -103,7 +109,9 @@ export const instrumentHttp = Object.assign(
},
ignoreIncomingRequestHook: request => {
- const url = getRequestUrl(request);
+ // request.url is the only property that holds any information about the url
+ // it only consists of the URL path and query string (if any)
+ const urlPath = request.url;
const method = request.method?.toUpperCase();
// We do not capture OPTIONS/HEAD requests as transactions
@@ -112,7 +120,7 @@ export const instrumentHttp = Object.assign(
}
const _ignoreIncomingRequests = _httpOptions.ignoreIncomingRequests;
- if (_ignoreIncomingRequests && _ignoreIncomingRequests(url)) {
+ if (urlPath && _ignoreIncomingRequests && _ignoreIncomingRequests(urlPath)) {
return true;
}
From c57e363e23840a54eab796cdc016d912cb8256bf Mon Sep 17 00:00:00 2001
From: Lukas Stracke
Date: Tue, 16 Jul 2024 14:03:25 +0200
Subject: [PATCH 39/39] meta: Update CHANGELOG for 8.18.0
---
CHANGELOG.md | 25 +++++++++++++++++++++++--
1 file changed, 23 insertions(+), 2 deletions(-)
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 6fd671757241..392564b0c24e 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -8,6 +8,8 @@
- "You miss 100 percent of the chances you don't take. — Wayne Gretzky" — Michael Scott
+## 8.18.0
+
### Important Changes
- **ref: Deprecate `enableTracing` (12897)**
@@ -15,9 +17,28 @@
The `enableTracing` option has been deprecated and will be removed in the next major version. We recommend removing it
in favor of the `tracesSampleRate` and `tracesSampler` options. If you want to enable performance monitoring, please set
the `tracesSampleRate` to a sample rate of your choice, or provide a sampling function as `tracesSampler` option
-instead. If you wan't to disable performance monitoring, remove the `tracesSampler` and `tracesSampleRate` options.
+instead. If you want to disable performance monitoring, remove the `tracesSampler` and `tracesSampleRate` options.
+
+### Other Changes
-Work in this release was contributed by @GitSquared. Thank you for your contribution!
+- feat(node): Expose `exclude` and `include` options for ESM loader (#12910)
+- feat(browser): Add user agent to INP standalone span attributes (#12896)
+- feat(nextjs): Add `experimental_captureRequestError` for `onRequestError` hook (#12885)
+- feat(replay): Bump `rrweb` to 2.25.0 (#12478)
+- feat(tracing): Add long animation frame tracing (#12646)
+- fix: Cleanup hooks when they are not used anymore (#12852)
+- fix(angular): Guard `ErrorEvent` check in ErrorHandler to avoid throwing in Node environments (#12892)
+- fix(inp): Ensure INP spans have correct transaction (#12871)
+- fix(nestjs): Do not make SentryTraced() decorated functions async (#12879)
+- fix(nextjs): Support automatic instrumentation for app directory with custom page extensions (#12858)
+- fix(node): Ensure correct URL is passed to `ignoreIncomingRequests` callback (#12929)
+- fix(otel): Do not add `otel.kind: INTERNAL` attribute (#12841)
+- fix(solidstart): Set proper sentry origin for solid router integration when used in solidstart sdk (#12919)
+- fix(sveltekit): Add Vite peer dep for proper type resolution (#12926)
+- fix(tracing): Ensure you can pass `null` as `parentSpan` in `startSpan*` (#12928)
+- ref(core): Small bundle size improvement (#12830)
+
+Work in this release was contributed by @GitSquared and @mcous. Thank you for your contributions!
## 8.17.0