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 @@ +

+ + Sentry + +

+ +# Official Sentry SDK for Cloudflare [UNRELEASED] + +[![npm version](https://img.shields.io/npm/v/@sentry/cloudflare.svg)](https://www.npmjs.com/package/@sentry/cloudflare) +[![npm dm](https://img.shields.io/npm/dm/@sentry/cloudflare.svg)](https://www.npmjs.com/package/@sentry/cloudflare) +[![npm dt](https://img.shields.io/npm/dt/@sentry/cloudflare.svg)](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