From f4f42c52632a2893d604a712115d03b35f9c4b8b Mon Sep 17 00:00:00 2001 From: Lukas Stracke Date: Wed, 10 Sep 2025 09:52:52 +0200 Subject: [PATCH 01/61] test(nextjs): Remove Next 13 and pin Next 14 canary and latest tests (#17577) Looks like we tested canary and latest versions of NextJS 13 and 14 in a way that we'd actually always pull in the latest 15 (canary) versions. This PR fixes this by: - removing Next 13 canary tests (these haven't been updated in more than 2 years) - Pinning Next 13 latest to `@next-13` - Pining Next 14 canary to `~14.3.0-canary.0` - Pinning Next 14 latest to `@next-14` --- .github/workflows/canary.yml | 3 --- .../e2e-tests/test-applications/nextjs-13/package.json | 7 +------ .../e2e-tests/test-applications/nextjs-14/package.json | 4 ++-- 3 files changed, 3 insertions(+), 11 deletions(-) diff --git a/.github/workflows/canary.yml b/.github/workflows/canary.yml index b1c5f619de19..fbf476c369a4 100644 --- a/.github/workflows/canary.yml +++ b/.github/workflows/canary.yml @@ -81,9 +81,6 @@ jobs: - test-application: 'nextjs-app-dir' build-command: 'test:build-latest' label: 'nextjs-app-dir (latest)' - - test-application: 'nextjs-13' - build-command: 'test:build-canary' - label: 'nextjs-13 (canary)' - test-application: 'nextjs-13' build-command: 'test:build-latest' label: 'nextjs-13 (latest)' diff --git a/dev-packages/e2e-tests/test-applications/nextjs-13/package.json b/dev-packages/e2e-tests/test-applications/nextjs-13/package.json index 32430917ddc0..adb8005d1a9d 100644 --- a/dev-packages/e2e-tests/test-applications/nextjs-13/package.json +++ b/dev-packages/e2e-tests/test-applications/nextjs-13/package.json @@ -8,8 +8,7 @@ "test:prod": "TEST_ENV=production playwright test", "test:dev": "TEST_ENV=development playwright test", "test:build": "pnpm install && pnpm build", - "test:build-canary": "pnpm install && pnpm add next@canary && pnpm add react@beta && pnpm add react-dom@beta && pnpm build", - "test:build-latest": "pnpm install && pnpm add next@latest && pnpm build", + "test:build-latest": "pnpm install && pnpm add next@next-13 && pnpm build", "test:assert": "pnpm test:prod && pnpm test:dev" }, "dependencies": { @@ -31,10 +30,6 @@ }, "sentryTest": { "optionalVariants": [ - { - "build-command": "pnpm test:build-canary", - "label": "nextjs-13 (canary)" - }, { "build-command": "pnpm test:build-latest", "label": "nextjs-13 (latest)" diff --git a/dev-packages/e2e-tests/test-applications/nextjs-14/package.json b/dev-packages/e2e-tests/test-applications/nextjs-14/package.json index 822d321b2028..f2dcfee9d43b 100644 --- a/dev-packages/e2e-tests/test-applications/nextjs-14/package.json +++ b/dev-packages/e2e-tests/test-applications/nextjs-14/package.json @@ -8,8 +8,8 @@ "test:prod": "TEST_ENV=production playwright test", "test:dev": "TEST_ENV=development playwright test", "test:build": "pnpm install && pnpm build", - "test:build-canary": "pnpm install && pnpm add next@canary && pnpm add react@beta && pnpm add react-dom@beta && pnpm build", - "test:build-latest": "pnpm install && pnpm add next@latest && pnpm build", + "test:build-canary": "pnpm install && pnpm add next@~14.3.0-canary.0 && pnpm add react@beta && pnpm add react-dom@beta && pnpm build", + "test:build-latest": "pnpm install && pnpm add next@next-14 && pnpm build", "test:assert": "pnpm test:prod && pnpm test:dev" }, "dependencies": { From 1c657883a7f4b108786fa5e3e68b04ed0106c8e0 Mon Sep 17 00:00:00 2001 From: Tim Fish Date: Wed, 10 Sep 2025 15:13:13 +0200 Subject: [PATCH 02/61] feat(node): Tidy existing ESM loader hook (#17566) I was looking to add another loader hook but I found some issues with the existing code: - To ensure the hook only gets registered once, `GLOBAL_OBJ._sentryEsmLoaderHookRegistered` is used but it was never set to true anywhere - Hook registration was duplicated in `@sentry/node-core` and `@sentry/node` for preloading - I moved the Node version check/warning to `supportsEsmLoaderHooks()` so it can be reused in multiple hooks --- packages/node-core/src/index.ts | 3 +- .../node-core/src/integrations/modules.ts | 2 +- packages/node-core/src/sdk/esmLoader.ts | 50 +++++++++---------- packages/node-core/src/sdk/index.ts | 10 ++-- packages/node-core/src/utils/commonjs.ts | 8 --- .../createMissingInstrumentationContext.ts | 2 +- packages/node-core/src/utils/detection.ts | 39 +++++++++++++++ .../node-core/src/utils/ensureIsWrapped.ts | 2 +- packages/node/src/sdk/initOtel.ts | 43 +++------------- 9 files changed, 82 insertions(+), 77 deletions(-) delete mode 100644 packages/node-core/src/utils/commonjs.ts create mode 100644 packages/node-core/src/utils/detection.ts diff --git a/packages/node-core/src/index.ts b/packages/node-core/src/index.ts index cf581bd63b66..57d8e2ece0ae 100644 --- a/packages/node-core/src/index.ts +++ b/packages/node-core/src/index.ts @@ -35,7 +35,8 @@ export { getSentryRelease, defaultStackParser } from './sdk/api'; export { createGetModuleFromFilename } from './utils/module'; export { addOriginToSpan } from './utils/addOriginToSpan'; export { getRequestUrl } from './utils/getRequestUrl'; -export { isCjs } from './utils/commonjs'; +export { initializeEsmLoader } from './sdk/esmLoader'; +export { isCjs } from './utils/detection'; export { ensureIsWrapped } from './utils/ensureIsWrapped'; export { createMissingInstrumentationContext } from './utils/createMissingInstrumentationContext'; export { envToBool } from './utils/envToBool'; diff --git a/packages/node-core/src/integrations/modules.ts b/packages/node-core/src/integrations/modules.ts index 6724a473b5bb..7079f4a2fab8 100644 --- a/packages/node-core/src/integrations/modules.ts +++ b/packages/node-core/src/integrations/modules.ts @@ -1,7 +1,7 @@ import { existsSync, readFileSync } from 'node:fs'; import { dirname, join } from 'node:path'; import type { IntegrationFn } from '@sentry/core'; -import { isCjs } from '../utils/commonjs'; +import { isCjs } from '../utils/detection'; type ModuleInfo = Record; diff --git a/packages/node-core/src/sdk/esmLoader.ts b/packages/node-core/src/sdk/esmLoader.ts index 2f0d8b405333..14f24f0df311 100644 --- a/packages/node-core/src/sdk/esmLoader.ts +++ b/packages/node-core/src/sdk/esmLoader.ts @@ -1,31 +1,31 @@ -import { consoleSandbox, debug, GLOBAL_OBJ } from '@sentry/core'; +import { debug, GLOBAL_OBJ } from '@sentry/core'; import { createAddHookMessageChannel } from 'import-in-the-middle'; -import moduleModule from 'module'; +import * as moduleModule from 'module'; +import { supportsEsmLoaderHooks } from '../utils/detection'; -/** Initialize the ESM loader. */ -export function maybeInitializeEsmLoader(): void { - const [nodeMajor = 0, nodeMinor = 0] = process.versions.node.split('.').map(Number); +/** + * Initialize the ESM loader - This method is private and not part of the public + * API. + * + * @ignore + */ +export function initializeEsmLoader(): void { + if (!supportsEsmLoaderHooks()) { + return; + } + + if (!GLOBAL_OBJ._sentryEsmLoaderHookRegistered) { + GLOBAL_OBJ._sentryEsmLoaderHookRegistered = true; - // Register hook was added in v20.6.0 and v18.19.0 - if (nodeMajor >= 21 || (nodeMajor === 20 && nodeMinor >= 6) || (nodeMajor === 18 && nodeMinor >= 19)) { - if (!GLOBAL_OBJ._sentryEsmLoaderHookRegistered) { - try { - const { addHookMessagePort } = createAddHookMessageChannel(); - // @ts-expect-error register is available in these versions - moduleModule.register('import-in-the-middle/hook.mjs', import.meta.url, { - data: { addHookMessagePort, include: [] }, - transferList: [addHookMessagePort], - }); - } catch (error) { - debug.warn('Failed to register ESM hook', error); - } + try { + const { addHookMessagePort } = createAddHookMessageChannel(); + // @ts-expect-error register is available in these versions + moduleModule.register('import-in-the-middle/hook.mjs', import.meta.url, { + data: { addHookMessagePort, include: [] }, + transferList: [addHookMessagePort], + }); + } catch (error) { + debug.warn("Failed to register 'import-in-the-middle' hook", error); } - } else { - consoleSandbox(() => { - // eslint-disable-next-line no-console - console.warn( - `[Sentry] You are using Node.js v${process.versions.node} in ESM mode ("import syntax"). The Sentry Node.js SDK is not compatible with ESM in Node.js versions before 18.19.0 or before 20.6.0. Please either build your application with CommonJS ("require() syntax"), or upgrade your Node.js version.`, - ); - }); } } diff --git a/packages/node-core/src/sdk/index.ts b/packages/node-core/src/sdk/index.ts index e5b12166d962..c4a16d76a1d0 100644 --- a/packages/node-core/src/sdk/index.ts +++ b/packages/node-core/src/sdk/index.ts @@ -35,11 +35,11 @@ import { INTEGRATION_NAME as SPOTLIGHT_INTEGRATION_NAME, spotlightIntegration } import { systemErrorIntegration } from '../integrations/systemError'; import { makeNodeTransport } from '../transports'; import type { NodeClientOptions, NodeOptions } from '../types'; -import { isCjs } from '../utils/commonjs'; +import { isCjs } from '../utils/detection'; import { envToBool } from '../utils/envToBool'; import { defaultStackParser, getSentryRelease } from './api'; import { NodeClient } from './client'; -import { maybeInitializeEsmLoader } from './esmLoader'; +import { initializeEsmLoader } from './esmLoader'; /** * Get default integrations for the Node-Core SDK. @@ -106,8 +106,8 @@ function _init( } } - if (!isCjs() && options.registerEsmLoaderHooks !== false) { - maybeInitializeEsmLoader(); + if (options.registerEsmLoaderHooks !== false) { + initializeEsmLoader(); } setOpenTelemetryContextAsyncContextStrategy(); @@ -131,7 +131,7 @@ function _init( client.init(); - debug.log(`Running in ${isCjs() ? 'CommonJS' : 'ESM'} mode.`); + debug.log(`SDK initialized from ${isCjs() ? 'CommonJS' : 'ESM'}`); client.startClientReportTracking(); diff --git a/packages/node-core/src/utils/commonjs.ts b/packages/node-core/src/utils/commonjs.ts deleted file mode 100644 index 23a9b97f9fc1..000000000000 --- a/packages/node-core/src/utils/commonjs.ts +++ /dev/null @@ -1,8 +0,0 @@ -/** Detect CommonJS. */ -export function isCjs(): boolean { - try { - return typeof module !== 'undefined' && typeof module.exports !== 'undefined'; - } catch { - return false; - } -} diff --git a/packages/node-core/src/utils/createMissingInstrumentationContext.ts b/packages/node-core/src/utils/createMissingInstrumentationContext.ts index 1930bcf782eb..3af8dc9e176a 100644 --- a/packages/node-core/src/utils/createMissingInstrumentationContext.ts +++ b/packages/node-core/src/utils/createMissingInstrumentationContext.ts @@ -1,5 +1,5 @@ import type { MissingInstrumentationContext } from '@sentry/core'; -import { isCjs } from './commonjs'; +import { isCjs } from './detection'; export const createMissingInstrumentationContext = (pkg: string): MissingInstrumentationContext => ({ package: pkg, diff --git a/packages/node-core/src/utils/detection.ts b/packages/node-core/src/utils/detection.ts new file mode 100644 index 000000000000..f7ae9a792c27 --- /dev/null +++ b/packages/node-core/src/utils/detection.ts @@ -0,0 +1,39 @@ +import { consoleSandbox } from '@sentry/core'; +import { NODE_MAJOR, NODE_MINOR } from '../nodeVersion'; + +/** Detect CommonJS. */ +export function isCjs(): boolean { + try { + return typeof module !== 'undefined' && typeof module.exports !== 'undefined'; + } catch { + return false; + } +} + +let hasWarnedAboutNodeVersion: boolean | undefined; + +/** + * Check if the current Node.js version supports module.register + */ +export function supportsEsmLoaderHooks(): boolean { + if (isCjs()) { + return false; + } + + if (NODE_MAJOR >= 21 || (NODE_MAJOR === 20 && NODE_MINOR >= 6) || (NODE_MAJOR === 18 && NODE_MINOR >= 19)) { + return true; + } + + if (!hasWarnedAboutNodeVersion) { + hasWarnedAboutNodeVersion = true; + + consoleSandbox(() => { + // eslint-disable-next-line no-console + console.warn( + `[Sentry] You are using Node.js v${process.versions.node} in ESM mode ("import syntax"). The Sentry Node.js SDK is not compatible with ESM in Node.js versions before 18.19.0 or before 20.6.0. Please either build your application with CommonJS ("require() syntax"), or upgrade your Node.js version.`, + ); + }); + } + + return false; +} diff --git a/packages/node-core/src/utils/ensureIsWrapped.ts b/packages/node-core/src/utils/ensureIsWrapped.ts index 70253d9debb7..a73c087ebaf2 100644 --- a/packages/node-core/src/utils/ensureIsWrapped.ts +++ b/packages/node-core/src/utils/ensureIsWrapped.ts @@ -1,8 +1,8 @@ import { isWrapped } from '@opentelemetry/instrumentation'; import { consoleSandbox, getClient, getGlobalScope, hasSpansEnabled, isEnabled } from '@sentry/core'; import type { NodeClient } from '../sdk/client'; -import { isCjs } from './commonjs'; import { createMissingInstrumentationContext } from './createMissingInstrumentationContext'; +import { isCjs } from './detection'; /** * Checks and warns if a framework isn't wrapped by opentelemetry. diff --git a/packages/node/src/sdk/initOtel.ts b/packages/node/src/sdk/initOtel.ts index ef27be0514c3..67de29821537 100644 --- a/packages/node/src/sdk/initOtel.ts +++ b/packages/node/src/sdk/initOtel.ts @@ -7,11 +7,14 @@ import { ATTR_SERVICE_VERSION, SEMRESATTRS_SERVICE_NAMESPACE, } from '@opentelemetry/semantic-conventions'; -import { consoleSandbox, debug as coreDebug, GLOBAL_OBJ, SDK_VERSION } from '@sentry/core'; -import { type NodeClient, isCjs, SentryContextManager, setupOpenTelemetryLogger } from '@sentry/node-core'; +import { debug as coreDebug, SDK_VERSION } from '@sentry/core'; +import { + type NodeClient, + initializeEsmLoader, + SentryContextManager, + setupOpenTelemetryLogger, +} from '@sentry/node-core'; import { SentryPropagator, SentrySampler, SentrySpanProcessor } from '@sentry/opentelemetry'; -import { createAddHookMessageChannel } from 'import-in-the-middle'; -import moduleModule from 'module'; import { DEBUG_BUILD } from '../debug-build'; import { getOpenTelemetryInstrumentationToPreload } from '../integrations/tracing'; @@ -35,34 +38,6 @@ export function initOpenTelemetry(client: NodeClient, options: AdditionalOpenTel client.traceProvider = provider; } -/** Initialize the ESM loader. */ -export function maybeInitializeEsmLoader(): void { - const [nodeMajor = 0, nodeMinor = 0] = process.versions.node.split('.').map(Number); - - // Register hook was added in v20.6.0 and v18.19.0 - if (nodeMajor >= 21 || (nodeMajor === 20 && nodeMinor >= 6) || (nodeMajor === 18 && nodeMinor >= 19)) { - if (!GLOBAL_OBJ._sentryEsmLoaderHookRegistered) { - try { - const { addHookMessagePort } = createAddHookMessageChannel(); - // @ts-expect-error register is available in these versions - moduleModule.register('import-in-the-middle/hook.mjs', import.meta.url, { - data: { addHookMessagePort, include: [] }, - transferList: [addHookMessagePort], - }); - } catch (error) { - coreDebug.warn('Failed to register ESM hook', error); - } - } - } else { - consoleSandbox(() => { - // eslint-disable-next-line no-console - console.warn( - `[Sentry] You are using Node.js v${process.versions.node} in ESM mode ("import syntax"). The Sentry Node.js SDK is not compatible with ESM in Node.js versions before 18.19.0 or before 20.6.0. Please either build your application with CommonJS ("require() syntax"), or upgrade your Node.js version.`, - ); - }); - } -} - interface NodePreloadOptions { debug?: boolean; integrations?: string[]; @@ -80,9 +55,7 @@ export function preloadOpenTelemetry(options: NodePreloadOptions = {}): void { coreDebug.enable(); } - if (!isCjs()) { - maybeInitializeEsmLoader(); - } + initializeEsmLoader(); // These are all integrations that we need to pre-load to ensure they are set up before any other code runs getPreloadMethods(options.integrations).forEach(fn => { From 4f83fa702a7f328f47641995fb88ec99b593e2f7 Mon Sep 17 00:00:00 2001 From: Sergiy Dybskiy Date: Wed, 10 Sep 2025 09:14:08 -0400 Subject: [PATCH 03/61] feat(deno): Add OpenTelemetry support and vercelAI integration (#17445) Based on the Cloudflare OTel implementation --- packages/deno/package.json | 3 +- packages/deno/src/index.ts | 1 + .../deno/src/integrations/tracing/vercelai.ts | 45 ++++ packages/deno/src/opentelemetry/tracer.ts | 110 +++++++++ packages/deno/src/sdk.ts | 11 +- packages/deno/src/types.ts | 13 + packages/deno/test/opentelemetry.test.ts | 225 ++++++++++++++++++ 7 files changed, 406 insertions(+), 2 deletions(-) create mode 100644 packages/deno/src/integrations/tracing/vercelai.ts create mode 100644 packages/deno/src/opentelemetry/tracer.ts create mode 100644 packages/deno/test/opentelemetry.test.ts diff --git a/packages/deno/package.json b/packages/deno/package.json index 8b34ebcc838e..ec80eef93401 100644 --- a/packages/deno/package.json +++ b/packages/deno/package.json @@ -24,6 +24,7 @@ "/build" ], "dependencies": { + "@opentelemetry/api": "^1.9.0", "@sentry/core": "10.11.0" }, "scripts": { @@ -42,7 +43,7 @@ "lint:es-compatibility": "es-check es2022 ./build/esm/*.js --module", "install:deno": "node ./scripts/install-deno.mjs", "test": "run-s install:deno deno-types test:unit", - "test:unit": "deno test --allow-read --allow-run --no-check", + "test:unit": "deno test --allow-read --allow-run --allow-env --no-check", "test:unit:update": "deno test --allow-read --allow-write --allow-run -- --update", "yalc:publish": "yalc publish --push --sig" }, diff --git a/packages/deno/src/index.ts b/packages/deno/src/index.ts index 12bcedc35270..5f987b4459aa 100644 --- a/packages/deno/src/index.ts +++ b/packages/deno/src/index.ts @@ -101,3 +101,4 @@ export { normalizePathsIntegration } from './integrations/normalizepaths'; export { contextLinesIntegration } from './integrations/contextlines'; export { denoCronIntegration } from './integrations/deno-cron'; export { breadcrumbsIntegration } from './integrations/breadcrumbs'; +export { vercelAIIntegration } from './integrations/tracing/vercelai'; diff --git a/packages/deno/src/integrations/tracing/vercelai.ts b/packages/deno/src/integrations/tracing/vercelai.ts new file mode 100644 index 000000000000..c400babf0d64 --- /dev/null +++ b/packages/deno/src/integrations/tracing/vercelai.ts @@ -0,0 +1,45 @@ +/** + * This is a copy of the Vercel AI integration from the cloudflare SDK. + */ + +import type { IntegrationFn } from '@sentry/core'; +import { addVercelAiProcessors, defineIntegration } from '@sentry/core'; + +const INTEGRATION_NAME = 'VercelAI'; + +const _vercelAIIntegration = (() => { + return { + name: INTEGRATION_NAME, + setup(client) { + addVercelAiProcessors(client); + }, + }; +}) satisfies IntegrationFn; + +/** + * Adds Sentry tracing instrumentation for the [ai](https://www.npmjs.com/package/ai) library. + * This integration is not enabled by default, you need to manually add it. + * + * For more information, see the [`ai` documentation](https://sdk.vercel.ai/docs/ai-sdk-core/telemetry). + * + * You need to enable collecting spans for a specific call by setting + * `experimental_telemetry.isEnabled` to `true` in the first argument of the function call. + * + * ```javascript + * const result = await generateText({ + * model: openai('gpt-4-turbo'), + * experimental_telemetry: { isEnabled: true }, + * }); + * ``` + * + * If you want to collect inputs and outputs for a specific call, you must specifically opt-in to each + * function call by setting `experimental_telemetry.recordInputs` and `experimental_telemetry.recordOutputs` + * to `true`. + * + * ```javascript + * const result = await generateText({ + * model: openai('gpt-4-turbo'), + * experimental_telemetry: { isEnabled: true, recordInputs: true, recordOutputs: true }, + * }); + */ +export const vercelAIIntegration = defineIntegration(_vercelAIIntegration); diff --git a/packages/deno/src/opentelemetry/tracer.ts b/packages/deno/src/opentelemetry/tracer.ts new file mode 100644 index 000000000000..801badefa19f --- /dev/null +++ b/packages/deno/src/opentelemetry/tracer.ts @@ -0,0 +1,110 @@ +import type { Context, Span, SpanOptions, Tracer, TracerProvider } from '@opentelemetry/api'; +import { SpanKind, trace } from '@opentelemetry/api'; +import { + SEMANTIC_ATTRIBUTE_SENTRY_OP, + SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN, + startInactiveSpan, + startSpanManual, +} from '@sentry/core'; + +/** + * Set up a mock OTEL tracer to allow inter-op with OpenTelemetry emitted spans. + * This is not perfect but handles easy/common use cases. + */ +export function setupOpenTelemetryTracer(): void { + trace.setGlobalTracerProvider(new SentryDenoTraceProvider()); +} + +class SentryDenoTraceProvider implements TracerProvider { + private readonly _tracers: Map = new Map(); + + public getTracer(name: string, version?: string, options?: { schemaUrl?: string }): Tracer { + const key = `${name}@${version || ''}:${options?.schemaUrl || ''}`; + if (!this._tracers.has(key)) { + this._tracers.set(key, new SentryDenoTracer()); + } + + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion + return this._tracers.get(key)!; + } +} + +class SentryDenoTracer implements Tracer { + public startSpan(name: string, options?: SpanOptions): Span { + // Map OpenTelemetry SpanKind to Sentry operation + const op = this._mapSpanKindToOp(options?.kind); + + return startInactiveSpan({ + name, + ...options, + attributes: { + ...options?.attributes, + [SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: 'manual', + [SEMANTIC_ATTRIBUTE_SENTRY_OP]: op, + 'sentry.deno_tracer': true, + }, + }); + } + + /** + * NOTE: This does not handle `context` being passed in. It will always put spans on the current scope. + */ + public startActiveSpan unknown>(name: string, fn: F): ReturnType; + public startActiveSpan unknown>(name: string, options: SpanOptions, fn: F): ReturnType; + public startActiveSpan unknown>( + name: string, + options: SpanOptions, + context: Context, + fn: F, + ): ReturnType; + public startActiveSpan unknown>( + name: string, + options: unknown, + context?: unknown, + fn?: F, + ): ReturnType { + const opts = (typeof options === 'object' && options !== null ? options : {}) as SpanOptions; + + // Map OpenTelemetry SpanKind to Sentry operation + const op = this._mapSpanKindToOp(opts.kind); + + const spanOpts = { + name, + ...opts, + attributes: { + ...opts.attributes, + [SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: 'manual', + [SEMANTIC_ATTRIBUTE_SENTRY_OP]: op, + 'sentry.deno_tracer': true, + }, + }; + + const callback = ( + typeof options === 'function' + ? options + : typeof context === 'function' + ? context + : typeof fn === 'function' + ? fn + : () => {} + ) as F; + + // In OTEL the semantic matches `startSpanManual` because spans are not auto-ended + return startSpanManual(spanOpts, callback) as ReturnType; + } + + private _mapSpanKindToOp(kind?: SpanKind): string { + switch (kind) { + case SpanKind.CLIENT: + return 'http.client'; + case SpanKind.SERVER: + return 'http.server'; + case SpanKind.PRODUCER: + return 'message.produce'; + case SpanKind.CONSUMER: + return 'message.consume'; + default: + return 'otel.span'; + } + } +} diff --git a/packages/deno/src/sdk.ts b/packages/deno/src/sdk.ts index 955417842f94..e670a86f50d6 100644 --- a/packages/deno/src/sdk.ts +++ b/packages/deno/src/sdk.ts @@ -16,6 +16,7 @@ import { denoContextIntegration } from './integrations/context'; import { contextLinesIntegration } from './integrations/contextlines'; import { globalHandlersIntegration } from './integrations/globalhandlers'; import { normalizePathsIntegration } from './integrations/normalizepaths'; +import { setupOpenTelemetryTracer } from './opentelemetry/tracer'; import { makeFetchTransport } from './transports'; import type { DenoOptions } from './types'; @@ -97,5 +98,13 @@ export function init(options: DenoOptions = {}): Client { transport: options.transport || makeFetchTransport, }; - return initAndBind(DenoClient, clientOptions); + const client = initAndBind(DenoClient, clientOptions); + + // Set up OpenTelemetry compatibility to capture spans from libraries using @opentelemetry/api + // Note: This is separate from Deno's native OTEL support and doesn't capture auto-instrumented spans + if (!options.skipOpenTelemetrySetup) { + setupOpenTelemetryTracer(); + } + + return client; } diff --git a/packages/deno/src/types.ts b/packages/deno/src/types.ts index 422e561bb644..1659e7a635e1 100644 --- a/packages/deno/src/types.ts +++ b/packages/deno/src/types.ts @@ -23,6 +23,19 @@ export interface BaseDenoOptions { /** Sets an optional server name (device name) */ serverName?: string; + /** + * The Deno SDK is not OpenTelemetry native, however, we set up some OpenTelemetry compatibility + * via a custom trace provider. + * This ensures that any spans emitted via `@opentelemetry/api` will be captured by Sentry. + * HOWEVER, big caveat: This does not handle custom context handling, it will always work off the current scope. + * This should be good enough for many, but not all integrations. + * + * If you want to opt-out of setting up the OpenTelemetry compatibility tracer, set this to `true`. + * + * @default false + */ + skipOpenTelemetrySetup?: boolean; + /** Callback that is executed when a fatal global error occurs. */ onFatalError?(this: void, error: Error): void; } diff --git a/packages/deno/test/opentelemetry.test.ts b/packages/deno/test/opentelemetry.test.ts new file mode 100644 index 000000000000..2c37ddc48843 --- /dev/null +++ b/packages/deno/test/opentelemetry.test.ts @@ -0,0 +1,225 @@ +import { assertEquals, assertNotEquals } from 'https://deno.land/std@0.212.0/assert/mod.ts'; +import { context, propagation, trace } from 'npm:@opentelemetry/api@1'; +import type { DenoClient } from '../build/esm/index.js'; +import { getCurrentScope, getGlobalScope, getIsolationScope, init, startSpan } from '../build/esm/index.js'; + +function resetGlobals(): void { + getCurrentScope().clear(); + getCurrentScope().setClient(undefined); + getIsolationScope().clear(); + getGlobalScope().clear(); +} + +function cleanupOtel(): void { + // Disable all globally registered APIs + trace.disable(); + context.disable(); + propagation.disable(); +} + +function resetSdk(): void { + resetGlobals(); + cleanupOtel(); +} + +Deno.test('should not capture spans emitted via @opentelemetry/api when skipOpenTelemetrySetup is true', async () => { + resetSdk(); + const transactionEvents: any[] = []; + + const client = init({ + dsn: 'https://username@domain/123', + tracesSampleRate: 1, + skipOpenTelemetrySetup: true, + beforeSendTransaction: event => { + transactionEvents.push(event); + return null; + }, + }) as DenoClient; + + const tracer = trace.getTracer('test'); + const span = tracer.startSpan('test'); + span.end(); + + await client.flush(); + + tracer.startActiveSpan('test 2', { attributes: { 'test.attribute': 'test' } }, span2 => { + const span = tracer.startSpan('test 3', { attributes: { 'test.attribute': 'test2' } }); + span.end(); + span2.end(); + }); + + await client.flush(); + + assertEquals(transactionEvents.length, 0); +}); + +Deno.test('should capture spans emitted via @opentelemetry/api', async () => { + resetSdk(); + const transactionEvents: any[] = []; + + const client = init({ + dsn: 'https://username@domain/123', + tracesSampleRate: 1, + beforeSendTransaction: event => { + transactionEvents.push(event); + return null; + }, + }) as DenoClient; + + const tracer = trace.getTracer('test'); + const span = tracer.startSpan('test'); + span.end(); + + await client.flush(); + + tracer.startActiveSpan('test 2', { attributes: { 'test.attribute': 'test' } }, span2 => { + const span = tracer.startSpan('test 3', { attributes: { 'test.attribute': 'test2' } }); + span.end(); + span2.end(); + }); + + await client.flush(); + + assertEquals(transactionEvents.length, 2); + const [transactionEvent, transactionEvent2] = transactionEvents; + + assertEquals(transactionEvent?.spans?.length, 0); + assertEquals(transactionEvent?.transaction, 'test'); + assertEquals(transactionEvent?.contexts?.trace?.data?.['sentry.deno_tracer'], true); + assertEquals(transactionEvent?.contexts?.trace?.data?.['sentry.origin'], 'manual'); + assertEquals(transactionEvent?.contexts?.trace?.data?.['sentry.sample_rate'], 1); + assertEquals(transactionEvent?.contexts?.trace?.data?.['sentry.source'], 'custom'); + + assertEquals(transactionEvent2?.spans?.length, 1); + assertEquals(transactionEvent2?.transaction, 'test 2'); + assertEquals(transactionEvent2?.contexts?.trace?.data?.['sentry.deno_tracer'], true); + assertEquals(transactionEvent2?.contexts?.trace?.data?.['sentry.origin'], 'manual'); + assertEquals(transactionEvent2?.contexts?.trace?.data?.['sentry.sample_rate'], 1); + assertEquals(transactionEvent2?.contexts?.trace?.data?.['sentry.source'], 'custom'); + assertEquals(transactionEvent2?.contexts?.trace?.data?.['test.attribute'], 'test'); + + const childSpan = transactionEvent2?.spans?.[0]; + assertEquals(childSpan?.description, 'test 3'); + assertEquals(childSpan?.data?.['sentry.deno_tracer'], true); + assertEquals(childSpan?.data?.['sentry.origin'], 'manual'); + assertEquals(childSpan?.data?.['test.attribute'], 'test2'); +}); + +Deno.test('opentelemetry spans should interop with Sentry spans', async () => { + resetSdk(); + const transactionEvents: any[] = []; + + const client = init({ + dsn: 'https://username@domain/123', + tracesSampleRate: 1, + beforeSendTransaction: event => { + transactionEvents.push(event); + return null; + }, + }) as DenoClient; + + const tracer = trace.getTracer('test'); + + startSpan({ name: 'sentry span' }, () => { + const span = tracer.startSpan('otel span'); + span.end(); + }); + + await client.flush(); + + assertEquals(transactionEvents.length, 1); + const [transactionEvent] = transactionEvents; + + assertEquals(transactionEvent?.spans?.length, 1); + assertEquals(transactionEvent?.transaction, 'sentry span'); + assertEquals(transactionEvent?.contexts?.trace?.data?.['sentry.origin'], 'manual'); + assertEquals(transactionEvent?.contexts?.trace?.data?.['sentry.sample_rate'], 1); + assertEquals(transactionEvent?.contexts?.trace?.data?.['sentry.source'], 'custom'); + // Note: Sentry-created spans don't have the deno_tracer marker + assertEquals(transactionEvent?.contexts?.trace?.data?.['sentry.deno_tracer'], undefined); + + const otelSpan = transactionEvent?.spans?.[0]; + assertEquals(otelSpan?.description, 'otel span'); + assertEquals(otelSpan?.data?.['sentry.deno_tracer'], true); + assertEquals(otelSpan?.data?.['sentry.origin'], 'manual'); +}); + +Deno.test('should be compatible with native Deno OpenTelemetry', async () => { + resetSdk(); + + const providerBefore = trace.getTracerProvider(); + + const client = init({ + dsn: 'https://username@domain/123', + tracesSampleRate: 1, + beforeSendTransaction: () => null, + }) as DenoClient; + + const providerAfter = trace.getTracerProvider(); + assertEquals(providerBefore, providerAfter); + + const tracer = trace.getTracer('compat-test'); + const span = tracer.startSpan('test-span'); + span.setAttributes({ 'test.compatibility': true }); + span.end(); + + tracer.startActiveSpan('active-span', activeSpan => { + activeSpan.end(); + }); + + const otelSpan = tracer.startSpan('post-init-span'); + otelSpan.end(); + + startSpan({ name: 'sentry-span' }, () => { + const nestedOtelSpan = tracer.startSpan('nested-otel-span'); + nestedOtelSpan.end(); + }); + + await client.flush(); +}); + +Deno.test('should verify native Deno OpenTelemetry works when enabled', async () => { + resetSdk(); + + // Set environment variable to enable native OTel + const originalValue = Deno.env.get('OTEL_DENO'); + Deno.env.set('OTEL_DENO', 'true'); + + try { + const client = init({ + dsn: 'https://username@domain/123', + tracesSampleRate: 1, + beforeSendTransaction: () => null, + }) as DenoClient; + + const provider = trace.getTracerProvider(); + const tracer = trace.getTracer('native-verification'); + const span = tracer.startSpan('verification-span'); + + if (provider.constructor.name === 'Function') { + // Native OTel is active + assertNotEquals(span.constructor.name, 'NonRecordingSpan'); + + let contextWorks = false; + tracer.startActiveSpan('parent-span', parentSpan => { + if (trace.getActiveSpan() === parentSpan) { + contextWorks = true; + } + parentSpan.end(); + }); + assertEquals(contextWorks, true); + } + + span.setAttributes({ 'test.native_otel': true }); + span.end(); + + await client.flush(); + } finally { + // Restore original environment + if (originalValue === undefined) { + Deno.env.delete('OTEL_DENO'); + } else { + Deno.env.set('OTEL_DENO', originalValue); + } + } +}); From 3faa74a2683eee37e8bc6f6f50466b2663dfd171 Mon Sep 17 00:00:00 2001 From: Andrei <168741329+andreiborza@users.noreply.github.com> Date: Wed, 10 Sep 2025 17:15:31 +0200 Subject: [PATCH 04/61] feat(deps): Update OpenTelemetry dependencies (#17569) Co-authored-by: Lukas Stracke --- .size-limit.js | 4 +- CHANGELOG.md | 34 ++ .../package.json | 14 +- .../package.json | 18 +- .../node-core-express-otel-v2/package.json | 14 +- .../node-otel-custom-sampler/package.json | 2 +- .../node-otel-sdk-node/package.json | 4 +- .../node-otel-without-tracing/package.json | 8 +- .../test-applications/node-otel/package.json | 4 +- .../node-core-integration-tests/package.json | 14 +- packages/aws-serverless/package.json | 6 +- packages/nestjs/package.json | 8 +- packages/nextjs/package.json | 2 +- packages/node-core/package.json | 22 +- packages/node/package.json | 56 +-- packages/opentelemetry/package.json | 16 +- packages/react-router/package.json | 6 +- packages/remix/package.json | 4 +- packages/tanstackstart-react/package.json | 2 +- packages/vercel-edge/package.json | 8 +- yarn.lock | 338 ++++++++---------- 21 files changed, 290 insertions(+), 294 deletions(-) diff --git a/.size-limit.js b/.size-limit.js index 36bf0607e840..8eef4950f00d 100644 --- a/.size-limit.js +++ b/.size-limit.js @@ -233,7 +233,7 @@ module.exports = [ import: createImport('init'), ignore: [...builtinModules, ...nodePrefixedBuiltinModules], gzip: true, - limit: '154 KB', + limit: '156 KB', }, { name: '@sentry/node - without tracing', @@ -263,7 +263,7 @@ module.exports = [ import: createImport('init'), ignore: [...builtinModules, ...nodePrefixedBuiltinModules], gzip: true, - limit: '107 KB', + limit: '111 KB', }, ]; diff --git a/CHANGELOG.md b/CHANGELOG.md index a76e7d2696d2..832c152a0a1b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,40 @@ - "You miss 100 percent of the chances you don't take. — Wayne Gretzky" — Michael Scott +### Important Changes + +- **feat(deps): Update OpenTelemetry dependencies ([#17558](https://github.com/getsentry/sentry-javascript/pull/17558))** + - @opentelemetry/core bumped to ^2.1.0 + - @opentelemetry/context-async-hooks bumped to ^2.1.0 + - @opentelemetry/resources bumped to ^2.1.0 + - @opentelemetry/sdk-trace-base bumped to ^2.1.0 + - @opentelemetry/semantic-conventions bumped to ^1.37.0 + - @opentelemetry/instrumentation bumped to ^0.204.0 + - @opentelemetry/instrumentation-http bumped to ^0.204.0 + - @opentelemetry/instrumentation-amqplib bumped to ^0.51.0 + - @opentelemetry/instrumentation-aws-sdk bumped to ^0.59.0 + - @opentelemetry/instrumentation-connect bumped to ^0.48.0 + - @opentelemetry/instrumentation-dataloader bumped to ^0.22.0 + - @opentelemetry/instrumentation-express bumped to ^0.53.0 + - @opentelemetry/instrumentation-fs bumped from to ^0.24.0 + - @opentelemetry/instrumentation-generic-pool bumped to ^0.48.0 + - @opentelemetry/instrumentation-graphql bumped to ^0.52.0 + - @opentelemetry/instrumentation-hapi bumped to ^0.51.0 + - @opentelemetry/instrumentation-ioredis bumped to ^0.52.0 + - @opentelemetry/instrumentation-kafkajs bumped to ^0.14.0 + - @opentelemetry/instrumentation-knex bumped to ^0.49.0 + - @opentelemetry/instrumentation-koa bumped to ^0.52.0 + - @opentelemetry/instrumentation-lru-memoizer bumped to ^0.49.0 + - @opentelemetry/instrumentation-mongodb bumped from to ^0.57.0 + - @opentelemetry/instrumentation-mongoose bumped from to ^0.51.0 + - @opentelemetry/instrumentation-mysql bumped to ^0.50.0 + - @opentelemetry/instrumentation-mysql2 bumped to ^0.51.0 + - @opentelemetry/instrumentation-nestjs-core bumped to ^0.50.0 + - @opentelemetry/instrumentation-pg bumped to ^0.57.0 + - @opentelemetry/instrumentation-redis bumped to ^0.53.0 + - @opentelemetry/instrumentation-undici bumped to ^0.15.0 + - @prisma/instrumentation bumped to 6.15.0 + ## 10.11.0 ### Important Changes diff --git a/dev-packages/e2e-tests/test-applications/node-core-express-otel-v2-custom-sampler/package.json b/dev-packages/e2e-tests/test-applications/node-core-express-otel-v2-custom-sampler/package.json index 3b6c974f44b2..e29a40c2887e 100644 --- a/dev-packages/e2e-tests/test-applications/node-core-express-otel-v2-custom-sampler/package.json +++ b/dev-packages/e2e-tests/test-applications/node-core-express-otel-v2-custom-sampler/package.json @@ -12,13 +12,13 @@ }, "dependencies": { "@opentelemetry/api": "^1.9.0", - "@opentelemetry/context-async-hooks": "^2.0.0", - "@opentelemetry/core": "^2.0.0", - "@opentelemetry/instrumentation": "^0.203.0", - "@opentelemetry/instrumentation-http": "^0.203.0", - "@opentelemetry/resources": "^2.0.0", - "@opentelemetry/sdk-trace-node": "^2.0.0", - "@opentelemetry/semantic-conventions": "^1.34.0", + "@opentelemetry/context-async-hooks": "^2.1.0", + "@opentelemetry/core": "^2.1.0", + "@opentelemetry/instrumentation": "^0.204.0", + "@opentelemetry/instrumentation-http": "^0.204.0", + "@opentelemetry/resources": "^2.1.0", + "@opentelemetry/sdk-trace-node": "^2.1.0", + "@opentelemetry/semantic-conventions": "^1.37.0", "@sentry/node-core": "latest || *", "@sentry/opentelemetry": "latest || *", "@types/express": "4.17.17", diff --git a/dev-packages/e2e-tests/test-applications/node-core-express-otel-v2-sdk-node/package.json b/dev-packages/e2e-tests/test-applications/node-core-express-otel-v2-sdk-node/package.json index 9da336fdf8d1..34b050f350c1 100644 --- a/dev-packages/e2e-tests/test-applications/node-core-express-otel-v2-sdk-node/package.json +++ b/dev-packages/e2e-tests/test-applications/node-core-express-otel-v2-sdk-node/package.json @@ -12,15 +12,15 @@ }, "dependencies": { "@opentelemetry/api": "^1.9.0", - "@opentelemetry/context-async-hooks": "^2.0.0", - "@opentelemetry/core": "^2.0.0", - "@opentelemetry/instrumentation": "^0.203.0", - "@opentelemetry/instrumentation-http": "^0.203.0", - "@opentelemetry/resources": "^2.0.0", - "@opentelemetry/sdk-trace-node": "^2.0.0", - "@opentelemetry/semantic-conventions": "^1.34.0", - "@opentelemetry/sdk-node": "^0.203.0", - "@opentelemetry/exporter-trace-otlp-http": "^0.203.0", + "@opentelemetry/context-async-hooks": "^2.1.0", + "@opentelemetry/core": "^2.1.0", + "@opentelemetry/instrumentation": "^0.204.0", + "@opentelemetry/instrumentation-http": "^0.204.0", + "@opentelemetry/resources": "^2.1.0", + "@opentelemetry/sdk-trace-node": "^2.1.0", + "@opentelemetry/semantic-conventions": "^1.37.0", + "@opentelemetry/sdk-node": "^0.204.0", + "@opentelemetry/exporter-trace-otlp-http": "^0.204.0", "@sentry/node-core": "latest || *", "@sentry/opentelemetry": "latest || *", "@types/express": "4.17.17", diff --git a/dev-packages/e2e-tests/test-applications/node-core-express-otel-v2/package.json b/dev-packages/e2e-tests/test-applications/node-core-express-otel-v2/package.json index cab490640de9..2252750e423e 100644 --- a/dev-packages/e2e-tests/test-applications/node-core-express-otel-v2/package.json +++ b/dev-packages/e2e-tests/test-applications/node-core-express-otel-v2/package.json @@ -14,13 +14,13 @@ "@sentry/node-core": "latest || *", "@sentry/opentelemetry": "latest || *", "@opentelemetry/api": "^1.9.0", - "@opentelemetry/context-async-hooks": "^2.0.0", - "@opentelemetry/core": "^2.0.0", - "@opentelemetry/instrumentation": "^0.203.0", - "@opentelemetry/instrumentation-http": "^0.203.0", - "@opentelemetry/resources": "^2.0.0", - "@opentelemetry/sdk-trace-node": "^2.0.0", - "@opentelemetry/semantic-conventions": "^1.34.0", + "@opentelemetry/context-async-hooks": "^2.1.0", + "@opentelemetry/core": "^2.1.0", + "@opentelemetry/instrumentation": "^0.204.0", + "@opentelemetry/instrumentation-http": "^0.204.0", + "@opentelemetry/resources": "^2.1.0", + "@opentelemetry/sdk-trace-node": "^2.1.0", + "@opentelemetry/semantic-conventions": "^1.37.0", "@types/express": "^4.17.21", "@types/node": "^18.19.1", "express": "^4.21.2", diff --git a/dev-packages/e2e-tests/test-applications/node-otel-custom-sampler/package.json b/dev-packages/e2e-tests/test-applications/node-otel-custom-sampler/package.json index 92770106970e..9ba6f629e78c 100644 --- a/dev-packages/e2e-tests/test-applications/node-otel-custom-sampler/package.json +++ b/dev-packages/e2e-tests/test-applications/node-otel-custom-sampler/package.json @@ -12,7 +12,7 @@ }, "dependencies": { "@opentelemetry/api": "^1.9.0", - "@opentelemetry/sdk-trace-node": "^2.0.0", + "@opentelemetry/sdk-trace-node": "^2.1.0", "@sentry/node": "latest || *", "@sentry/opentelemetry": "latest || *", "@types/express": "4.17.17", diff --git a/dev-packages/e2e-tests/test-applications/node-otel-sdk-node/package.json b/dev-packages/e2e-tests/test-applications/node-otel-sdk-node/package.json index d26dc2db5843..7296f72218cd 100644 --- a/dev-packages/e2e-tests/test-applications/node-otel-sdk-node/package.json +++ b/dev-packages/e2e-tests/test-applications/node-otel-sdk-node/package.json @@ -11,8 +11,8 @@ "test:assert": "pnpm test" }, "dependencies": { - "@opentelemetry/sdk-node": "0.203.0", - "@opentelemetry/exporter-trace-otlp-http": "0.203.0", + "@opentelemetry/sdk-node": "0.204.0", + "@opentelemetry/exporter-trace-otlp-http": "0.204.0", "@sentry/node": "latest || *", "@types/express": "4.17.17", "@types/node": "^18.19.1", diff --git a/dev-packages/e2e-tests/test-applications/node-otel-without-tracing/package.json b/dev-packages/e2e-tests/test-applications/node-otel-without-tracing/package.json index 11b5509fb637..f13daab2ef6c 100644 --- a/dev-packages/e2e-tests/test-applications/node-otel-without-tracing/package.json +++ b/dev-packages/e2e-tests/test-applications/node-otel-without-tracing/package.json @@ -11,11 +11,11 @@ "test:assert": "pnpm test" }, "dependencies": { - "@opentelemetry/sdk-trace-node": "2.0.0", - "@opentelemetry/exporter-trace-otlp-http": "0.203.0", + "@opentelemetry/sdk-trace-node": "2.1.0", + "@opentelemetry/exporter-trace-otlp-http": "0.204.0", "@opentelemetry/instrumentation-undici": "0.13.2", - "@opentelemetry/instrumentation-http": "0.203.0", - "@opentelemetry/instrumentation": "0.203.0", + "@opentelemetry/instrumentation-http": "0.204.0", + "@opentelemetry/instrumentation": "0.204.0", "@sentry/node": "latest || *", "@types/express": "4.17.17", "@types/node": "^18.19.1", diff --git a/dev-packages/e2e-tests/test-applications/node-otel/package.json b/dev-packages/e2e-tests/test-applications/node-otel/package.json index 1a554ece3bf7..31cf99c32c91 100644 --- a/dev-packages/e2e-tests/test-applications/node-otel/package.json +++ b/dev-packages/e2e-tests/test-applications/node-otel/package.json @@ -11,8 +11,8 @@ "test:assert": "pnpm test" }, "dependencies": { - "@opentelemetry/sdk-node": "0.203.0", - "@opentelemetry/exporter-trace-otlp-http": "0.203.0", + "@opentelemetry/sdk-node": "0.204.0", + "@opentelemetry/exporter-trace-otlp-http": "0.204.0", "@sentry/node": "latest || *", "@types/express": "4.17.17", "@types/node": "^18.19.1", diff --git a/dev-packages/node-core-integration-tests/package.json b/dev-packages/node-core-integration-tests/package.json index 7ea4dc2b31ed..62a2e9cc91ab 100644 --- a/dev-packages/node-core-integration-tests/package.json +++ b/dev-packages/node-core-integration-tests/package.json @@ -27,13 +27,13 @@ "@nestjs/core": "10.4.6", "@nestjs/platform-express": "10.4.6", "@opentelemetry/api": "^1.9.0", - "@opentelemetry/context-async-hooks": "^2.0.0", - "@opentelemetry/core": "^2.0.0", - "@opentelemetry/instrumentation": "^0.203.0", - "@opentelemetry/instrumentation-http": "0.203.0", - "@opentelemetry/resources": "^2.0.0", - "@opentelemetry/sdk-trace-base": "^2.0.0", - "@opentelemetry/semantic-conventions": "^1.34.0", + "@opentelemetry/context-async-hooks": "^2.1.0", + "@opentelemetry/core": "^2.1.0", + "@opentelemetry/instrumentation": "^0.204.0", + "@opentelemetry/instrumentation-http": "0.204.0", + "@opentelemetry/resources": "^2.1.0", + "@opentelemetry/sdk-trace-base": "^2.1.0", + "@opentelemetry/semantic-conventions": "^1.37.0", "@sentry/core": "10.11.0", "@sentry/node-core": "10.11.0", "body-parser": "^1.20.3", diff --git a/packages/aws-serverless/package.json b/packages/aws-serverless/package.json index d04809c989a7..ea32abd115b5 100644 --- a/packages/aws-serverless/package.json +++ b/packages/aws-serverless/package.json @@ -66,9 +66,9 @@ }, "dependencies": { "@opentelemetry/api": "^1.9.0", - "@opentelemetry/instrumentation": "^0.203.0", - "@opentelemetry/instrumentation-aws-sdk": "0.57.0", - "@opentelemetry/semantic-conventions": "^1.36.0", + "@opentelemetry/instrumentation": "^0.204.0", + "@opentelemetry/instrumentation-aws-sdk": "0.59.0", + "@opentelemetry/semantic-conventions": "^1.37.0", "@sentry/core": "10.11.0", "@sentry/node": "10.11.0", "@types/aws-lambda": "^8.10.62" diff --git a/packages/nestjs/package.json b/packages/nestjs/package.json index 132ca2b802b4..f231b220149d 100644 --- a/packages/nestjs/package.json +++ b/packages/nestjs/package.json @@ -45,10 +45,10 @@ }, "dependencies": { "@opentelemetry/api": "^1.9.0", - "@opentelemetry/core": "^2.0.0", - "@opentelemetry/instrumentation": "^0.203.0", - "@opentelemetry/instrumentation-nestjs-core": "0.49.0", - "@opentelemetry/semantic-conventions": "^1.34.0", + "@opentelemetry/core": "^2.1.0", + "@opentelemetry/instrumentation": "^0.204.0", + "@opentelemetry/instrumentation-nestjs-core": "0.50.0", + "@opentelemetry/semantic-conventions": "^1.37.0", "@sentry/core": "10.11.0", "@sentry/node": "10.11.0" }, diff --git a/packages/nextjs/package.json b/packages/nextjs/package.json index 0fe39c265264..d4817c742010 100644 --- a/packages/nextjs/package.json +++ b/packages/nextjs/package.json @@ -77,7 +77,7 @@ }, "dependencies": { "@opentelemetry/api": "^1.9.0", - "@opentelemetry/semantic-conventions": "^1.34.0", + "@opentelemetry/semantic-conventions": "^1.37.0", "@rollup/plugin-commonjs": "28.0.1", "@sentry-internal/browser-utils": "10.11.0", "@sentry/bundler-plugin-core": "^4.3.0", diff --git a/packages/node-core/package.json b/packages/node-core/package.json index 695eea2c6626..8ca46387868a 100644 --- a/packages/node-core/package.json +++ b/packages/node-core/package.json @@ -58,12 +58,12 @@ }, "peerDependencies": { "@opentelemetry/api": "^1.9.0", - "@opentelemetry/context-async-hooks": "^1.30.1 || ^2.0.0", - "@opentelemetry/core": "^1.30.1 || ^2.0.0", + "@opentelemetry/context-async-hooks": "^1.30.1 || ^2.1.0", + "@opentelemetry/core": "^1.30.1 || ^2.1.0", "@opentelemetry/instrumentation": ">=0.57.1 <1", - "@opentelemetry/resources": "^1.30.1 || ^2.0.0", - "@opentelemetry/sdk-trace-base": "^1.30.1 || ^2.0.0", - "@opentelemetry/semantic-conventions": "^1.34.0" + "@opentelemetry/resources": "^1.30.1 || ^2.1.0", + "@opentelemetry/sdk-trace-base": "^1.30.1 || ^2.1.0", + "@opentelemetry/semantic-conventions": "^1.37.0" }, "dependencies": { "@sentry/core": "10.11.0", @@ -72,12 +72,12 @@ }, "devDependencies": { "@opentelemetry/api": "^1.9.0", - "@opentelemetry/context-async-hooks": "^2.0.0", - "@opentelemetry/core": "^2.0.0", - "@opentelemetry/instrumentation": "^0.203.0", - "@opentelemetry/resources": "^2.0.0", - "@opentelemetry/sdk-trace-base": "^2.0.0", - "@opentelemetry/semantic-conventions": "^1.34.0", + "@opentelemetry/context-async-hooks": "^2.1.0", + "@opentelemetry/core": "^2.1.0", + "@opentelemetry/instrumentation": "^0.204.0", + "@opentelemetry/resources": "^2.1.0", + "@opentelemetry/sdk-trace-base": "^2.1.0", + "@opentelemetry/semantic-conventions": "^1.37.0", "@types/node": "^18.19.1" }, "scripts": { diff --git a/packages/node/package.json b/packages/node/package.json index 77856d85df3a..ba44a688d33b 100644 --- a/packages/node/package.json +++ b/packages/node/package.json @@ -66,35 +66,35 @@ }, "dependencies": { "@opentelemetry/api": "^1.9.0", - "@opentelemetry/context-async-hooks": "^2.0.0", - "@opentelemetry/core": "^2.0.0", - "@opentelemetry/instrumentation": "^0.203.0", - "@opentelemetry/instrumentation-amqplib": "0.50.0", - "@opentelemetry/instrumentation-connect": "0.47.0", - "@opentelemetry/instrumentation-dataloader": "0.21.1", - "@opentelemetry/instrumentation-express": "0.52.0", - "@opentelemetry/instrumentation-fs": "0.23.0", - "@opentelemetry/instrumentation-generic-pool": "0.47.0", - "@opentelemetry/instrumentation-graphql": "0.51.0", - "@opentelemetry/instrumentation-hapi": "0.50.0", - "@opentelemetry/instrumentation-http": "0.203.0", + "@opentelemetry/context-async-hooks": "^2.1.0", + "@opentelemetry/core": "^2.1.0", + "@opentelemetry/instrumentation": "^0.204.0", + "@opentelemetry/instrumentation-amqplib": "0.51.0", + "@opentelemetry/instrumentation-connect": "0.48.0", + "@opentelemetry/instrumentation-dataloader": "0.22.0", + "@opentelemetry/instrumentation-express": "0.53.0", + "@opentelemetry/instrumentation-fs": "0.24.0", + "@opentelemetry/instrumentation-generic-pool": "0.48.0", + "@opentelemetry/instrumentation-graphql": "0.52.0", + "@opentelemetry/instrumentation-hapi": "0.51.0", + "@opentelemetry/instrumentation-http": "0.204.0", "@opentelemetry/instrumentation-ioredis": "0.52.0", - "@opentelemetry/instrumentation-kafkajs": "0.13.0", - "@opentelemetry/instrumentation-knex": "0.48.0", - "@opentelemetry/instrumentation-koa": "0.51.0", - "@opentelemetry/instrumentation-lru-memoizer": "0.48.0", - "@opentelemetry/instrumentation-mongodb": "0.56.0", - "@opentelemetry/instrumentation-mongoose": "0.50.0", - "@opentelemetry/instrumentation-mysql": "0.49.0", - "@opentelemetry/instrumentation-mysql2": "0.50.0", - "@opentelemetry/instrumentation-pg": "0.55.0", - "@opentelemetry/instrumentation-redis": "0.51.0", - "@opentelemetry/instrumentation-tedious": "0.22.0", - "@opentelemetry/instrumentation-undici": "0.14.0", - "@opentelemetry/resources": "^2.0.0", - "@opentelemetry/sdk-trace-base": "^2.0.0", - "@opentelemetry/semantic-conventions": "^1.34.0", - "@prisma/instrumentation": "6.14.0", + "@opentelemetry/instrumentation-kafkajs": "0.14.0", + "@opentelemetry/instrumentation-knex": "0.49.0", + "@opentelemetry/instrumentation-koa": "0.52.0", + "@opentelemetry/instrumentation-lru-memoizer": "0.49.0", + "@opentelemetry/instrumentation-mongodb": "0.57.0", + "@opentelemetry/instrumentation-mongoose": "0.51.0", + "@opentelemetry/instrumentation-mysql": "0.50.0", + "@opentelemetry/instrumentation-mysql2": "0.51.0", + "@opentelemetry/instrumentation-pg": "0.57.0", + "@opentelemetry/instrumentation-redis": "0.53.0", + "@opentelemetry/instrumentation-tedious": "0.23.0", + "@opentelemetry/instrumentation-undici": "0.15.0", + "@opentelemetry/resources": "^2.1.0", + "@opentelemetry/sdk-trace-base": "^2.1.0", + "@opentelemetry/semantic-conventions": "^1.37.0", + "@prisma/instrumentation": "6.15.0", "@sentry/core": "10.11.0", "@sentry/node-core": "10.11.0", "@sentry/opentelemetry": "10.11.0", diff --git a/packages/opentelemetry/package.json b/packages/opentelemetry/package.json index c21b7cfd782a..13f2c2d990e7 100644 --- a/packages/opentelemetry/package.json +++ b/packages/opentelemetry/package.json @@ -43,17 +43,17 @@ }, "peerDependencies": { "@opentelemetry/api": "^1.9.0", - "@opentelemetry/context-async-hooks": "^1.30.1 || ^2.0.0", - "@opentelemetry/core": "^1.30.1 || ^2.0.0", - "@opentelemetry/sdk-trace-base": "^1.30.1 || ^2.0.0", - "@opentelemetry/semantic-conventions": "^1.34.0" + "@opentelemetry/context-async-hooks": "^1.30.1 || ^2.1.0", + "@opentelemetry/core": "^1.30.1 || ^2.1.0", + "@opentelemetry/sdk-trace-base": "^1.30.1 || ^2.1.0", + "@opentelemetry/semantic-conventions": "^1.37.0" }, "devDependencies": { "@opentelemetry/api": "^1.9.0", - "@opentelemetry/context-async-hooks": "^2.0.0", - "@opentelemetry/core": "^2.0.0", - "@opentelemetry/sdk-trace-base": "^2.0.0", - "@opentelemetry/semantic-conventions": "^1.34.0" + "@opentelemetry/context-async-hooks": "^2.1.0", + "@opentelemetry/core": "^2.1.0", + "@opentelemetry/sdk-trace-base": "^2.1.0", + "@opentelemetry/semantic-conventions": "^1.37.0" }, "scripts": { "build": "run-p build:transpile build:types", diff --git a/packages/react-router/package.json b/packages/react-router/package.json index 659c39996819..4709b74853ef 100644 --- a/packages/react-router/package.json +++ b/packages/react-router/package.json @@ -46,9 +46,9 @@ }, "dependencies": { "@opentelemetry/api": "^1.9.0", - "@opentelemetry/core": "^2.0.0", - "@opentelemetry/instrumentation": "^0.203.0", - "@opentelemetry/semantic-conventions": "^1.34.0", + "@opentelemetry/core": "^2.1.0", + "@opentelemetry/instrumentation": "^0.204.0", + "@opentelemetry/semantic-conventions": "^1.37.0", "@sentry/browser": "10.11.0", "@sentry/cli": "^2.52.0", "@sentry/core": "10.11.0", diff --git a/packages/remix/package.json b/packages/remix/package.json index 903019b09cdc..0bf7416ef2fe 100644 --- a/packages/remix/package.json +++ b/packages/remix/package.json @@ -65,8 +65,8 @@ }, "dependencies": { "@opentelemetry/api": "^1.9.0", - "@opentelemetry/instrumentation": "^0.203.0", - "@opentelemetry/semantic-conventions": "^1.34.0", + "@opentelemetry/instrumentation": "^0.204.0", + "@opentelemetry/semantic-conventions": "^1.37.0", "@remix-run/router": "1.x", "@sentry/cli": "^2.52.0", "@sentry/core": "10.11.0", diff --git a/packages/tanstackstart-react/package.json b/packages/tanstackstart-react/package.json index 4eb3507ad0be..42d94c1188c8 100644 --- a/packages/tanstackstart-react/package.json +++ b/packages/tanstackstart-react/package.json @@ -51,7 +51,7 @@ }, "dependencies": { "@opentelemetry/api": "^1.9.0", - "@opentelemetry/semantic-conventions": "^1.34.0", + "@opentelemetry/semantic-conventions": "^1.37.0", "@sentry-internal/browser-utils": "10.11.0", "@sentry/core": "10.11.0", "@sentry/node": "10.11.0", diff --git a/packages/vercel-edge/package.json b/packages/vercel-edge/package.json index 7cb476c38f40..3afcf65244aa 100644 --- a/packages/vercel-edge/package.json +++ b/packages/vercel-edge/package.json @@ -40,14 +40,14 @@ }, "dependencies": { "@opentelemetry/api": "^1.9.0", - "@opentelemetry/resources": "^2.0.0", + "@opentelemetry/resources": "^2.1.0", "@sentry/core": "10.11.0" }, "devDependencies": { "@edge-runtime/types": "3.0.1", - "@opentelemetry/core": "^2.0.0", - "@opentelemetry/sdk-trace-base": "^2.0.0", - "@opentelemetry/semantic-conventions": "^1.34.0", + "@opentelemetry/core": "^2.1.0", + "@opentelemetry/sdk-trace-base": "^2.1.0", + "@opentelemetry/semantic-conventions": "^1.37.0", "@sentry/opentelemetry": "10.11.0" }, "scripts": { diff --git a/yarn.lock b/yarn.lock index db7641e5a892..82fff6f849f2 100644 --- a/yarn.lock +++ b/yarn.lock @@ -5858,13 +5858,6 @@ dependencies: "@octokit/openapi-types" "^18.0.0" -"@opentelemetry/api-logs@0.203.0": - version "0.203.0" - resolved "https://registry.yarnpkg.com/@opentelemetry/api-logs/-/api-logs-0.203.0.tgz#3309a76c51a848ea820cd7f00ee62daf36b06380" - integrity sha512-9B9RU0H7Ya1Dx/Rkyc4stuBZSGVQF27WigitInx2QQoj6KUpEFYPKoWjdFTunJYxmXmh17HeBvbMa1EhGyPmqQ== - dependencies: - "@opentelemetry/api" "^1.3.0" - "@opentelemetry/api-logs@0.204.0": version "0.204.0" resolved "https://registry.yarnpkg.com/@opentelemetry/api-logs/-/api-logs-0.204.0.tgz#c0285aa5c79625a1c424854393902d21732fd76b" @@ -5884,108 +5877,100 @@ resolved "https://registry.yarnpkg.com/@opentelemetry/api/-/api-1.9.0.tgz#d03eba68273dc0f7509e2a3d5cba21eae10379fe" integrity sha512-3giAOQvZiH5F9bMlMiv8+GSPMeqg0dbaeo58/0SlA9sxSqZhnUtxzX9/2FzyhS9sWQf5S0GJE0AKBrFqjpeYcg== -"@opentelemetry/context-async-hooks@^2.0.0": - version "2.0.0" - resolved "https://registry.yarnpkg.com/@opentelemetry/context-async-hooks/-/context-async-hooks-2.0.0.tgz#c98a727238ca199cda943780acf6124af8d8cd80" - integrity sha512-IEkJGzK1A9v3/EHjXh3s2IiFc6L4jfK+lNgKVgUjeUJQRRhnVFMIO3TAvKwonm9O1HebCuoOt98v8bZW7oVQHA== - -"@opentelemetry/core@2.0.0": - version "2.0.0" - resolved "https://registry.yarnpkg.com/@opentelemetry/core/-/core-2.0.0.tgz#37e9f0e9ddec4479b267aca6f32d88757c941b3a" - integrity sha512-SLX36allrcnVaPYG3R78F/UZZsBsvbc7lMCLx37LyH5MJ1KAAZ2E3mW9OAD3zGz0G8q/BtoS5VUrjzDydhD6LQ== - dependencies: - "@opentelemetry/semantic-conventions" "^1.29.0" +"@opentelemetry/context-async-hooks@^2.1.0": + version "2.1.0" + resolved "https://registry.yarnpkg.com/@opentelemetry/context-async-hooks/-/context-async-hooks-2.1.0.tgz#de1de21d9536abfe73769f822b52a59a8c97b083" + integrity sha512-zOyetmZppnwTyPrt4S7jMfXiSX9yyfF0hxlA8B5oo2TtKl+/RGCy7fi4DrBfIf3lCPrkKsRBWZZD7RFojK7FDg== -"@opentelemetry/core@2.0.1", "@opentelemetry/core@^2.0.0": - version "2.0.1" - resolved "https://registry.yarnpkg.com/@opentelemetry/core/-/core-2.0.1.tgz#44e1149d5666a4743cde943ef89841db3ce0f8bc" - integrity sha512-MaZk9SJIDgo1peKevlbhP6+IwIiNPNmswNL4AF0WaQJLbHXjr9SrZMgS12+iqr9ToV4ZVosCcc0f8Rg67LXjxw== +"@opentelemetry/core@2.1.0", "@opentelemetry/core@^2.0.0", "@opentelemetry/core@^2.1.0": + version "2.1.0" + resolved "https://registry.yarnpkg.com/@opentelemetry/core/-/core-2.1.0.tgz#5539f04eb9e5245e000b0c3f77bdfaa07557e3a7" + integrity sha512-RMEtHsxJs/GiHHxYT58IY57UXAQTuUnZVco6ymDEqTNlJKTimM4qPUPVe8InNFyBjhHBEAx4k3Q8LtNayBsbUQ== dependencies: "@opentelemetry/semantic-conventions" "^1.29.0" -"@opentelemetry/instrumentation-amqplib@0.50.0": - version "0.50.0" - resolved "https://registry.yarnpkg.com/@opentelemetry/instrumentation-amqplib/-/instrumentation-amqplib-0.50.0.tgz#91899a7e2821db956daeaa803d3bd8f5af8b8050" - integrity sha512-kwNs/itehHG/qaQBcVrLNcvXVPW0I4FCOVtw3LHMLdYIqD7GJ6Yv2nX+a4YHjzbzIeRYj8iyMp0Bl7tlkidq5w== +"@opentelemetry/instrumentation-amqplib@0.51.0": + version "0.51.0" + resolved "https://registry.yarnpkg.com/@opentelemetry/instrumentation-amqplib/-/instrumentation-amqplib-0.51.0.tgz#1779326433f1ab8a743bbf8e1957e1b1252cf036" + integrity sha512-XGmjYwjVRktD4agFnWBWQXo9SiYHKBxR6Ag3MLXwtLE4R99N3a08kGKM5SC1qOFKIELcQDGFEFT9ydXMH00Luw== dependencies: "@opentelemetry/core" "^2.0.0" - "@opentelemetry/instrumentation" "^0.203.0" + "@opentelemetry/instrumentation" "^0.204.0" "@opentelemetry/semantic-conventions" "^1.27.0" -"@opentelemetry/instrumentation-aws-sdk@0.57.0": - version "0.57.0" - resolved "https://registry.yarnpkg.com/@opentelemetry/instrumentation-aws-sdk/-/instrumentation-aws-sdk-0.57.0.tgz#22d0a2ac113718c85c39d3561ee338dfad2234f6" - integrity sha512-RfbyjaeZzX3mPhuaRHlSAQyfX3skfeWOl30jrqSXtE9k0DPdnIqpHhdYS0C/DEDuZbwTmruVJ4cUwMBw5Z6FAg== +"@opentelemetry/instrumentation-aws-sdk@0.59.0": + version "0.59.0" + resolved "https://registry.yarnpkg.com/@opentelemetry/instrumentation-aws-sdk/-/instrumentation-aws-sdk-0.59.0.tgz#bd612836a6158f1773369a5646984b95f805273d" + integrity sha512-GN/9YGBMb//s0vnchM2jMCkCaIKDB/Piau72fcuqcDXNBffMgu+AA9vCHZD2umriciXLtXJ2GXTh2/yaaHwLIw== dependencies: "@opentelemetry/core" "^2.0.0" - "@opentelemetry/instrumentation" "^0.203.0" - "@opentelemetry/propagation-utils" "^0.31.3" + "@opentelemetry/instrumentation" "^0.204.0" "@opentelemetry/semantic-conventions" "^1.34.0" -"@opentelemetry/instrumentation-connect@0.47.0": - version "0.47.0" - resolved "https://registry.yarnpkg.com/@opentelemetry/instrumentation-connect/-/instrumentation-connect-0.47.0.tgz#47271b8454fa88d97aa78e175c3d0cb7e10bd9e2" - integrity sha512-pjenvjR6+PMRb6/4X85L4OtkQCootgb/Jzh/l/Utu3SJHBid1F+gk9sTGU2FWuhhEfV6P7MZ7BmCdHXQjgJ42g== +"@opentelemetry/instrumentation-connect@0.48.0": + version "0.48.0" + resolved "https://registry.yarnpkg.com/@opentelemetry/instrumentation-connect/-/instrumentation-connect-0.48.0.tgz#4481c84315b33b54a67c6e787be0eb72a84b23b3" + integrity sha512-OMjc3SFL4pC16PeK+tDhwP7MRvDPalYCGSvGqUhX5rASkI2H0RuxZHOWElYeXkV0WP+70Gw6JHWac/2Zqwmhdw== dependencies: "@opentelemetry/core" "^2.0.0" - "@opentelemetry/instrumentation" "^0.203.0" + "@opentelemetry/instrumentation" "^0.204.0" "@opentelemetry/semantic-conventions" "^1.27.0" "@types/connect" "3.4.38" -"@opentelemetry/instrumentation-dataloader@0.21.1": - version "0.21.1" - resolved "https://registry.yarnpkg.com/@opentelemetry/instrumentation-dataloader/-/instrumentation-dataloader-0.21.1.tgz#46fbbe59d9d6796980707768cf733225d43adea5" - integrity sha512-hNAm/bwGawLM8VDjKR0ZUDJ/D/qKR3s6lA5NV+btNaPVm2acqhPcT47l2uCVi+70lng2mywfQncor9v8/ykuyw== +"@opentelemetry/instrumentation-dataloader@0.22.0": + version "0.22.0" + resolved "https://registry.yarnpkg.com/@opentelemetry/instrumentation-dataloader/-/instrumentation-dataloader-0.22.0.tgz#a34f8ac6ec18e8f1585dcd89f9f611570868d1a2" + integrity sha512-bXnTcwtngQsI1CvodFkTemrrRSQjAjZxqHVc+CJZTDnidT0T6wt3jkKhnsjU/Kkkc0lacr6VdRpCu2CUWa0OKw== dependencies: - "@opentelemetry/instrumentation" "^0.203.0" + "@opentelemetry/instrumentation" "^0.204.0" -"@opentelemetry/instrumentation-express@0.52.0": - version "0.52.0" - resolved "https://registry.yarnpkg.com/@opentelemetry/instrumentation-express/-/instrumentation-express-0.52.0.tgz#d87d2130fe779dd757db28edb78262af83510d5b" - integrity sha512-W7pizN0Wh1/cbNhhTf7C62NpyYw7VfCFTYg0DYieSTrtPBT1vmoSZei19wfKLnrMsz3sHayCg0HxCVL2c+cz5w== +"@opentelemetry/instrumentation-express@0.53.0": + version "0.53.0" + resolved "https://registry.yarnpkg.com/@opentelemetry/instrumentation-express/-/instrumentation-express-0.53.0.tgz#902634e3de640bd4fa5370924397e716608ecb90" + integrity sha512-r/PBafQmFYRjuxLYEHJ3ze1iBnP2GDA1nXOSS6E02KnYNZAVjj6WcDA1MSthtdAUUK0XnotHvvWM8/qz7DMO5A== dependencies: "@opentelemetry/core" "^2.0.0" - "@opentelemetry/instrumentation" "^0.203.0" + "@opentelemetry/instrumentation" "^0.204.0" "@opentelemetry/semantic-conventions" "^1.27.0" -"@opentelemetry/instrumentation-fs@0.23.0": - version "0.23.0" - resolved "https://registry.yarnpkg.com/@opentelemetry/instrumentation-fs/-/instrumentation-fs-0.23.0.tgz#e3cd3a53fa975c69de33e207b35561f3f90106f0" - integrity sha512-Puan+QopWHA/KNYvDfOZN6M/JtF6buXEyD934vrb8WhsX1/FuM7OtoMlQyIqAadnE8FqqDL4KDPiEfCQH6pQcQ== +"@opentelemetry/instrumentation-fs@0.24.0": + version "0.24.0" + resolved "https://registry.yarnpkg.com/@opentelemetry/instrumentation-fs/-/instrumentation-fs-0.24.0.tgz#edf0f7418f6a1cdcbe135857ab75629e7d94b910" + integrity sha512-HjIxJ6CBRD770KNVaTdMXIv29Sjz4C1kPCCK5x1Ujpc6SNnLGPqUVyJYZ3LUhhnHAqdbrl83ogVWjCgeT4Q0yw== dependencies: "@opentelemetry/core" "^2.0.0" - "@opentelemetry/instrumentation" "^0.203.0" + "@opentelemetry/instrumentation" "^0.204.0" -"@opentelemetry/instrumentation-generic-pool@0.47.0": - version "0.47.0" - resolved "https://registry.yarnpkg.com/@opentelemetry/instrumentation-generic-pool/-/instrumentation-generic-pool-0.47.0.tgz#f5fa9d42236eb7d57fa544954f316faee937b0b4" - integrity sha512-UfHqf3zYK+CwDwEtTjaD12uUqGGTswZ7ofLBEdQ4sEJp9GHSSJMQ2hT3pgBxyKADzUdoxQAv/7NqvL42ZI+Qbw== +"@opentelemetry/instrumentation-generic-pool@0.48.0": + version "0.48.0" + resolved "https://registry.yarnpkg.com/@opentelemetry/instrumentation-generic-pool/-/instrumentation-generic-pool-0.48.0.tgz#76fc08d76515db04f3833d730c5cb18cb0b237d4" + integrity sha512-TLv/On8pufynNR+pUbpkyvuESVASZZKMlqCm4bBImTpXKTpqXaJJ3o/MUDeMlM91rpen+PEv2SeyOKcHCSlgag== dependencies: - "@opentelemetry/instrumentation" "^0.203.0" + "@opentelemetry/instrumentation" "^0.204.0" -"@opentelemetry/instrumentation-graphql@0.51.0": - version "0.51.0" - resolved "https://registry.yarnpkg.com/@opentelemetry/instrumentation-graphql/-/instrumentation-graphql-0.51.0.tgz#1b29aa6330d196d523460e593167dca7dbcd42bb" - integrity sha512-LchkOu9X5DrXAnPI1+Z06h/EH/zC7D6sA86hhPrk3evLlsJTz0grPrkL/yUJM9Ty0CL/y2HSvmWQCjbJEz/ADg== +"@opentelemetry/instrumentation-graphql@0.52.0": + version "0.52.0" + resolved "https://registry.yarnpkg.com/@opentelemetry/instrumentation-graphql/-/instrumentation-graphql-0.52.0.tgz#a2d23a669bdd0a1b031f785fe447d5a34ac56343" + integrity sha512-3fEJ8jOOMwopvldY16KuzHbRhPk8wSsOTSF0v2psmOCGewh6ad+ZbkTx/xyUK9rUdUMWAxRVU0tFpj4Wx1vkPA== dependencies: - "@opentelemetry/instrumentation" "^0.203.0" + "@opentelemetry/instrumentation" "^0.204.0" -"@opentelemetry/instrumentation-hapi@0.50.0": - version "0.50.0" - resolved "https://registry.yarnpkg.com/@opentelemetry/instrumentation-hapi/-/instrumentation-hapi-0.50.0.tgz#c755e9c21bfeb82046221bfd51303f816ae649e8" - integrity sha512-5xGusXOFQXKacrZmDbpHQzqYD1gIkrMWuwvlrEPkYOsjUqGUjl1HbxCsn5Y9bUXOCgP1Lj6A4PcKt1UiJ2MujA== +"@opentelemetry/instrumentation-hapi@0.51.0": + version "0.51.0" + resolved "https://registry.yarnpkg.com/@opentelemetry/instrumentation-hapi/-/instrumentation-hapi-0.51.0.tgz#879926dfbb2e1609cc8658392167b1456c75d9e0" + integrity sha512-qyf27DaFNL1Qhbo/da+04MSCw982B02FhuOS5/UF+PMhM61CcOiu7fPuXj8TvbqyReQuJFljXE6UirlvoT/62g== dependencies: "@opentelemetry/core" "^2.0.0" - "@opentelemetry/instrumentation" "^0.203.0" + "@opentelemetry/instrumentation" "^0.204.0" "@opentelemetry/semantic-conventions" "^1.27.0" -"@opentelemetry/instrumentation-http@0.203.0": - version "0.203.0" - resolved "https://registry.yarnpkg.com/@opentelemetry/instrumentation-http/-/instrumentation-http-0.203.0.tgz#21f198547b5c72fc64e83ed25cdc991aef7b8fee" - integrity sha512-y3uQAcCOAwnO6vEuNVocmpVzG3PER6/YZqbPbbffDdJ9te5NkHEkfSMNzlC3+v7KlE+WinPGc3N7MR30G1HY2g== +"@opentelemetry/instrumentation-http@0.204.0": + version "0.204.0" + resolved "https://registry.yarnpkg.com/@opentelemetry/instrumentation-http/-/instrumentation-http-0.204.0.tgz#faaf009b75e6a68729923b0a2a5270dc7d336f1d" + integrity sha512-1afJYyGRA4OmHTv0FfNTrTAzoEjPQUYgd+8ih/lX0LlZBnGio/O80vxA0lN3knsJPS7FiDrsDrWq25K7oAzbkw== dependencies: - "@opentelemetry/core" "2.0.1" - "@opentelemetry/instrumentation" "0.203.0" + "@opentelemetry/core" "2.1.0" + "@opentelemetry/instrumentation" "0.204.0" "@opentelemetry/semantic-conventions" "^1.29.0" forwarded-parse "2.1.2" @@ -5998,129 +5983,120 @@ "@opentelemetry/redis-common" "^0.38.0" "@opentelemetry/semantic-conventions" "^1.27.0" -"@opentelemetry/instrumentation-kafkajs@0.13.0": - version "0.13.0" - resolved "https://registry.yarnpkg.com/@opentelemetry/instrumentation-kafkajs/-/instrumentation-kafkajs-0.13.0.tgz#f959fecd0a9d53bed2fd662e41a5c155295ffbc8" - integrity sha512-FPQyJsREOaGH64hcxlzTsIEQC4DYANgTwHjiB7z9lldmvua1LRMVn3/FfBlzXoqF179B0VGYviz6rn75E9wsDw== +"@opentelemetry/instrumentation-kafkajs@0.14.0": + version "0.14.0" + resolved "https://registry.yarnpkg.com/@opentelemetry/instrumentation-kafkajs/-/instrumentation-kafkajs-0.14.0.tgz#ffc30728b5845907d2c5b9f3883676c754ef4927" + integrity sha512-kbB5yXS47dTIdO/lfbbXlzhvHFturbux4EpP0+6H78Lk0Bn4QXiZQW7rmZY1xBCY16mNcCb8Yt0mhz85hTnSVA== dependencies: - "@opentelemetry/instrumentation" "^0.203.0" + "@opentelemetry/instrumentation" "^0.204.0" "@opentelemetry/semantic-conventions" "^1.30.0" -"@opentelemetry/instrumentation-knex@0.48.0": - version "0.48.0" - resolved "https://registry.yarnpkg.com/@opentelemetry/instrumentation-knex/-/instrumentation-knex-0.48.0.tgz#ed24a81dfe6099cfe56136a3fed90565e1259f58" - integrity sha512-V5wuaBPv/lwGxuHjC6Na2JFRjtPgstw19jTFl1B1b6zvaX8zVDYUDaR5hL7glnQtUSCMktPttQsgK4dhXpddcA== +"@opentelemetry/instrumentation-knex@0.49.0": + version "0.49.0" + resolved "https://registry.yarnpkg.com/@opentelemetry/instrumentation-knex/-/instrumentation-knex-0.49.0.tgz#8c04c80c00ead5fbdf600cd2460dcd21b4069157" + integrity sha512-NKsRRT27fbIYL4Ix+BjjP8h4YveyKc+2gD6DMZbr5R5rUeDqfC8+DTfIt3c3ex3BIc5Vvek4rqHnN7q34ZetLQ== dependencies: - "@opentelemetry/instrumentation" "^0.203.0" + "@opentelemetry/instrumentation" "^0.204.0" "@opentelemetry/semantic-conventions" "^1.33.1" -"@opentelemetry/instrumentation-koa@0.51.0": - version "0.51.0" - resolved "https://registry.yarnpkg.com/@opentelemetry/instrumentation-koa/-/instrumentation-koa-0.51.0.tgz#1ff57866b7882033639477d3d2d9bada19a2129f" - integrity sha512-XNLWeMTMG1/EkQBbgPYzCeBD0cwOrfnn8ao4hWgLv0fNCFQu1kCsJYygz2cvKuCs340RlnG4i321hX7R8gj3Rg== +"@opentelemetry/instrumentation-koa@0.52.0": + version "0.52.0" + resolved "https://registry.yarnpkg.com/@opentelemetry/instrumentation-koa/-/instrumentation-koa-0.52.0.tgz#7266785ea85334366c3e50dc2b45468df438eb3f" + integrity sha512-JJSBYLDx/mNSy8Ibi/uQixu2rH0bZODJa8/cz04hEhRaiZQoeJ5UrOhO/mS87IdgVsHrnBOsZ6vDu09znupyuA== dependencies: "@opentelemetry/core" "^2.0.0" - "@opentelemetry/instrumentation" "^0.203.0" + "@opentelemetry/instrumentation" "^0.204.0" "@opentelemetry/semantic-conventions" "^1.27.0" -"@opentelemetry/instrumentation-lru-memoizer@0.48.0": - version "0.48.0" - resolved "https://registry.yarnpkg.com/@opentelemetry/instrumentation-lru-memoizer/-/instrumentation-lru-memoizer-0.48.0.tgz#b9fbbd45b7a742a6795bf7166f65684251f184b1" - integrity sha512-KUW29wfMlTPX1wFz+NNrmE7IzN7NWZDrmFWHM/VJcmFEuQGnnBuTIdsP55CnBDxKgQ/qqYFp4udQFNtjeFosPw== +"@opentelemetry/instrumentation-lru-memoizer@0.49.0": + version "0.49.0" + resolved "https://registry.yarnpkg.com/@opentelemetry/instrumentation-lru-memoizer/-/instrumentation-lru-memoizer-0.49.0.tgz#6353b877628339e3f07189f4fb15919a73fe1503" + integrity sha512-ctXu+O/1HSadAxtjoEg2w307Z5iPyLOMM8IRNwjaKrIpNAthYGSOanChbk1kqY6zU5CrpkPHGdAT6jk8dXiMqw== dependencies: - "@opentelemetry/instrumentation" "^0.203.0" + "@opentelemetry/instrumentation" "^0.204.0" -"@opentelemetry/instrumentation-mongodb@0.56.0": - version "0.56.0" - resolved "https://registry.yarnpkg.com/@opentelemetry/instrumentation-mongodb/-/instrumentation-mongodb-0.56.0.tgz#81281d2d151c3bfb26864c50b938a82ba2831b2d" - integrity sha512-YG5IXUUmxX3Md2buVMvxm9NWlKADrnavI36hbJsihqqvBGsWnIfguf0rUP5Srr0pfPqhQjUP+agLMsvu0GmUpA== +"@opentelemetry/instrumentation-mongodb@0.57.0": + version "0.57.0" + resolved "https://registry.yarnpkg.com/@opentelemetry/instrumentation-mongodb/-/instrumentation-mongodb-0.57.0.tgz#e697261b2eac05280134e1851b72c89d5b4b3da8" + integrity sha512-KD6Rg0KSHWDkik+qjIOWoksi1xqSpix8TSPfquIK1DTmd9OTFb5PHmMkzJe16TAPVEuElUW8gvgP59cacFcrMQ== dependencies: - "@opentelemetry/instrumentation" "^0.203.0" + "@opentelemetry/instrumentation" "^0.204.0" "@opentelemetry/semantic-conventions" "^1.27.0" -"@opentelemetry/instrumentation-mongoose@0.50.0": - version "0.50.0" - resolved "https://registry.yarnpkg.com/@opentelemetry/instrumentation-mongoose/-/instrumentation-mongoose-0.50.0.tgz#1fae5d2769ca7e67d15291fb91b61403839ad91d" - integrity sha512-Am8pk1Ct951r4qCiqkBcGmPIgGhoDiFcRtqPSLbJrUZqEPUsigjtMjoWDRLG1Ki1NHgOF7D0H7d+suWz1AAizw== +"@opentelemetry/instrumentation-mongoose@0.51.0": + version "0.51.0" + resolved "https://registry.yarnpkg.com/@opentelemetry/instrumentation-mongoose/-/instrumentation-mongoose-0.51.0.tgz#688e9f3448e3d0979c4aaab5b566e14f30a1aa72" + integrity sha512-gwWaAlhhV2By7XcbyU3DOLMvzsgeaymwP/jktDC+/uPkCmgB61zurwqOQdeiRq9KAf22Y2dtE5ZLXxytJRbEVA== dependencies: "@opentelemetry/core" "^2.0.0" - "@opentelemetry/instrumentation" "^0.203.0" + "@opentelemetry/instrumentation" "^0.204.0" "@opentelemetry/semantic-conventions" "^1.27.0" -"@opentelemetry/instrumentation-mysql2@0.50.0": - version "0.50.0" - resolved "https://registry.yarnpkg.com/@opentelemetry/instrumentation-mysql2/-/instrumentation-mysql2-0.50.0.tgz#259344ba8771fd59c70a1a91360215b4b047a323" - integrity sha512-PoOMpmq73rOIE3nlTNLf3B1SyNYGsp7QXHYKmeTZZnJ2Ou7/fdURuOhWOI0e6QZ5gSem18IR1sJi6GOULBQJ9g== +"@opentelemetry/instrumentation-mysql2@0.51.0": + version "0.51.0" + resolved "https://registry.yarnpkg.com/@opentelemetry/instrumentation-mysql2/-/instrumentation-mysql2-0.51.0.tgz#7eec3a0b9e4b27759df5df1c82eaedcf34b27528" + integrity sha512-zT2Wg22Xn43RyfU3NOUmnFtb5zlDI0fKcijCj9AcK9zuLZ4ModgtLXOyBJSSfO+hsOCZSC1v/Fxwj+nZJFdzLQ== dependencies: - "@opentelemetry/instrumentation" "^0.203.0" + "@opentelemetry/instrumentation" "^0.204.0" "@opentelemetry/semantic-conventions" "^1.27.0" "@opentelemetry/sql-common" "^0.41.0" -"@opentelemetry/instrumentation-mysql@0.49.0": - version "0.49.0" - resolved "https://registry.yarnpkg.com/@opentelemetry/instrumentation-mysql/-/instrumentation-mysql-0.49.0.tgz#24fa7473134867236ed4068ee645e51922bcb654" - integrity sha512-QU9IUNqNsrlfE3dJkZnFHqLjlndiU39ll/YAAEvWE40sGOCi9AtOF6rmEGzJ1IswoZ3oyePV7q2MP8SrhJfVAA== +"@opentelemetry/instrumentation-mysql@0.50.0": + version "0.50.0" + resolved "https://registry.yarnpkg.com/@opentelemetry/instrumentation-mysql/-/instrumentation-mysql-0.50.0.tgz#25de9de05191cecf8b01df379544eba50fa6f548" + integrity sha512-duKAvMRI3vq6u9JwzIipY9zHfikN20bX05sL7GjDeLKr2qV0LQ4ADtKST7KStdGcQ+MTN5wghWbbVdLgNcB3rA== dependencies: - "@opentelemetry/instrumentation" "^0.203.0" + "@opentelemetry/instrumentation" "^0.204.0" "@opentelemetry/semantic-conventions" "^1.27.0" "@types/mysql" "2.15.27" -"@opentelemetry/instrumentation-nestjs-core@0.49.0": - version "0.49.0" - resolved "https://registry.yarnpkg.com/@opentelemetry/instrumentation-nestjs-core/-/instrumentation-nestjs-core-0.49.0.tgz#a79428e72c14250c44913a3a57f39c7297aab013" - integrity sha512-1R/JFwdmZIk3T/cPOCkVvFQeKYzbbUvDxVH3ShXamUwBlGkdEu5QJitlRMyVNZaHkKZKWgYrBarGQsqcboYgaw== +"@opentelemetry/instrumentation-nestjs-core@0.50.0": + version "0.50.0" + resolved "https://registry.yarnpkg.com/@opentelemetry/instrumentation-nestjs-core/-/instrumentation-nestjs-core-0.50.0.tgz#f803bbeb6c972ac8f0685885cca7f6e5a4e09056" + integrity sha512-10u2Gjw260W8vdUem6pM7ENrb8i+UAyrgouhjN7HRdQYh9rcit51tRhgrI52fxTsRjrrBNIItHkX0YM8WnEU2w== dependencies: - "@opentelemetry/instrumentation" "^0.203.0" + "@opentelemetry/instrumentation" "^0.204.0" "@opentelemetry/semantic-conventions" "^1.30.0" -"@opentelemetry/instrumentation-pg@0.55.0": - version "0.55.0" - resolved "https://registry.yarnpkg.com/@opentelemetry/instrumentation-pg/-/instrumentation-pg-0.55.0.tgz#f411d1e48c50b1c1f0f185d9fe94cfbb8812d8f6" - integrity sha512-yfJ5bYE7CnkW/uNsnrwouG/FR7nmg09zdk2MSs7k0ZOMkDDAE3WBGpVFFApGgNu2U+gtzLgEzOQG4I/X+60hXw== +"@opentelemetry/instrumentation-pg@0.57.0": + version "0.57.0" + resolved "https://registry.yarnpkg.com/@opentelemetry/instrumentation-pg/-/instrumentation-pg-0.57.0.tgz#346cb613ccd1100221cef9692271468a3fe92eb0" + integrity sha512-dWLGE+r5lBgm2A8SaaSYDE3OKJ/kwwy5WLyGyzor8PLhUL9VnJRiY6qhp4njwhnljiLtzeffRtG2Mf/YyWLeTw== dependencies: "@opentelemetry/core" "^2.0.0" - "@opentelemetry/instrumentation" "^0.203.0" - "@opentelemetry/semantic-conventions" "^1.27.0" + "@opentelemetry/instrumentation" "^0.204.0" + "@opentelemetry/semantic-conventions" "^1.34.0" "@opentelemetry/sql-common" "^0.41.0" - "@types/pg" "8.15.4" + "@types/pg" "8.15.5" "@types/pg-pool" "2.0.6" -"@opentelemetry/instrumentation-redis@0.51.0": - version "0.51.0" - resolved "https://registry.yarnpkg.com/@opentelemetry/instrumentation-redis/-/instrumentation-redis-0.51.0.tgz#70504ba6c3856fcb25e436b4915e85efaa7d38a6" - integrity sha512-uL/GtBA0u72YPPehwOvthAe+Wf8k3T+XQPBssJmTYl6fzuZjNq8zTfxVFhl9nRFjFVEe+CtiYNT0Q3AyqW1Z0A== +"@opentelemetry/instrumentation-redis@0.53.0": + version "0.53.0" + resolved "https://registry.yarnpkg.com/@opentelemetry/instrumentation-redis/-/instrumentation-redis-0.53.0.tgz#826cfeacebaf7ce571bb932ad410f23caf170b9c" + integrity sha512-WUHV8fr+8yo5RmzyU7D5BIE1zwiaNQcTyZPwtxlfr7px6NYYx7IIpSihJK7WA60npWynfxxK1T67RAVF0Gdfjg== dependencies: - "@opentelemetry/instrumentation" "^0.203.0" + "@opentelemetry/instrumentation" "^0.204.0" "@opentelemetry/redis-common" "^0.38.0" "@opentelemetry/semantic-conventions" "^1.27.0" -"@opentelemetry/instrumentation-tedious@0.22.0": - version "0.22.0" - resolved "https://registry.yarnpkg.com/@opentelemetry/instrumentation-tedious/-/instrumentation-tedious-0.22.0.tgz#f71374c52cb9c57a6b879bea3256a1465c02efbb" - integrity sha512-XrrNSUCyEjH1ax9t+Uo6lv0S2FCCykcF7hSxBMxKf7Xn0bPRxD3KyFUZy25aQXzbbbUHhtdxj3r2h88SfEM3aA== +"@opentelemetry/instrumentation-tedious@0.23.0": + version "0.23.0" + resolved "https://registry.yarnpkg.com/@opentelemetry/instrumentation-tedious/-/instrumentation-tedious-0.23.0.tgz#a781de2cb33ff71ef65bbefba11c9fe2d79c4b32" + integrity sha512-3TMTk/9VtlRonVTaU4tCzbg4YqW+Iq/l5VnN2e5whP6JgEg/PKfrGbqQ+CxQWNLfLaQYIUgEZqAn5gk/inh1uQ== dependencies: - "@opentelemetry/instrumentation" "^0.203.0" + "@opentelemetry/instrumentation" "^0.204.0" "@opentelemetry/semantic-conventions" "^1.27.0" "@types/tedious" "^4.0.14" -"@opentelemetry/instrumentation-undici@0.14.0": - version "0.14.0" - resolved "https://registry.yarnpkg.com/@opentelemetry/instrumentation-undici/-/instrumentation-undici-0.14.0.tgz#7a9cd276f7664773b5daf5ae53365b3593e6e7a9" - integrity sha512-2HN+7ztxAReXuxzrtA3WboAKlfP5OsPA57KQn2AdYZbJ3zeRPcLXyW4uO/jpLE6PLm0QRtmeGCmfYpqRlwgSwg== +"@opentelemetry/instrumentation-undici@0.15.0": + version "0.15.0" + resolved "https://registry.yarnpkg.com/@opentelemetry/instrumentation-undici/-/instrumentation-undici-0.15.0.tgz#c8193a162d4abe61c2fd247912e0cb8c0c3bc10c" + integrity sha512-sNFGA/iCDlVkNjzTzPRcudmI11vT/WAfAguRdZY9IspCw02N4WSC72zTuQhSMheh2a1gdeM9my1imnKRvEEvEg== dependencies: "@opentelemetry/core" "^2.0.0" - "@opentelemetry/instrumentation" "^0.203.0" - -"@opentelemetry/instrumentation@0.203.0", "@opentelemetry/instrumentation@^0.203.0": - version "0.203.0" - resolved "https://registry.yarnpkg.com/@opentelemetry/instrumentation/-/instrumentation-0.203.0.tgz#5c74a41cd6868f7ba47b346ff5a58ea7b18cf381" - integrity sha512-ke1qyM+3AK2zPuBPb6Hk/GCsc5ewbLvPNkEuELx/JmANeEp6ZjnZ+wypPAJSucTw0wvCGrUaibDSdcrGFoWxKQ== - dependencies: - "@opentelemetry/api-logs" "0.203.0" - import-in-the-middle "^1.8.1" - require-in-the-middle "^7.1.1" + "@opentelemetry/instrumentation" "^0.204.0" -"@opentelemetry/instrumentation@^0.204.0": +"@opentelemetry/instrumentation@0.204.0", "@opentelemetry/instrumentation@^0.204.0": version "0.204.0" resolved "https://registry.yarnpkg.com/@opentelemetry/instrumentation/-/instrumentation-0.204.0.tgz#587c104c02c9ccb38932ce508d9c81514ec7a7c4" integrity sha512-vV5+WSxktzoMP8JoYWKeopChy6G3HKk4UQ2hESCRDUUTZqQ3+nM3u8noVG0LmNfRWwcFBnbZ71GKC7vaYYdJ1g== @@ -6141,45 +6117,32 @@ semver "^7.5.2" shimmer "^1.2.1" -"@opentelemetry/propagation-utils@^0.31.3": - version "0.31.3" - resolved "https://registry.yarnpkg.com/@opentelemetry/propagation-utils/-/propagation-utils-0.31.3.tgz#42aab61a1a3cec64ce221cbcec5f3f6fc84e9701" - integrity sha512-ZI6LKjyo+QYYZY5SO8vfoCQ9A69r1/g+pyjvtu5RSK38npINN1evEmwqbqhbg2CdcIK3a4PN6pDAJz/yC5/gAA== - "@opentelemetry/redis-common@^0.38.0": version "0.38.0" resolved "https://registry.yarnpkg.com/@opentelemetry/redis-common/-/redis-common-0.38.0.tgz#87d2a792dcbcf466a41bb7dfb8a7cd094d643d0b" integrity sha512-4Wc0AWURII2cfXVVoZ6vDqK+s5n4K5IssdrlVrvGsx6OEOKdghKtJZqXAHWFiZv4nTDLH2/2fldjIHY8clMOjQ== -"@opentelemetry/resources@2.0.0": - version "2.0.0" - resolved "https://registry.yarnpkg.com/@opentelemetry/resources/-/resources-2.0.0.tgz#15c04794c32b7d0b3c7589225ece6ae9bba25989" - integrity sha512-rnZr6dML2z4IARI4zPGQV4arDikF/9OXZQzrC01dLmn0CZxU5U5OLd/m1T7YkGRj5UitjeoCtg/zorlgMQcdTg== - dependencies: - "@opentelemetry/core" "2.0.0" - "@opentelemetry/semantic-conventions" "^1.29.0" - -"@opentelemetry/resources@^2.0.0": - version "2.0.1" - resolved "https://registry.yarnpkg.com/@opentelemetry/resources/-/resources-2.0.1.tgz#0365d134291c0ed18d96444a1e21d0e6a481c840" - integrity sha512-dZOB3R6zvBwDKnHDTB4X1xtMArB/d324VsbiPkX/Yu0Q8T2xceRthoIVFhJdvgVM2QhGVUyX9tzwiNxGtoBJUw== +"@opentelemetry/resources@2.1.0", "@opentelemetry/resources@^2.1.0": + version "2.1.0" + resolved "https://registry.yarnpkg.com/@opentelemetry/resources/-/resources-2.1.0.tgz#11772e732af4f27953cf55567a6630d8b4d8282d" + integrity sha512-1CJjf3LCvoefUOgegxi8h6r4B/wLSzInyhGP2UmIBYNlo4Qk5CZ73e1eEyWmfXvFtm1ybkmfb2DqWvspsYLrWw== dependencies: - "@opentelemetry/core" "2.0.1" + "@opentelemetry/core" "2.1.0" "@opentelemetry/semantic-conventions" "^1.29.0" -"@opentelemetry/sdk-trace-base@^2.0.0": - version "2.0.0" - resolved "https://registry.yarnpkg.com/@opentelemetry/sdk-trace-base/-/sdk-trace-base-2.0.0.tgz#ebc06ea7537dea62f3882f8236c1234f4faf6b23" - integrity sha512-qQnYdX+ZCkonM7tA5iU4fSRsVxbFGml8jbxOgipRGMFHKaXKHQ30js03rTobYjKjIfnOsZSbHKWF0/0v0OQGfw== +"@opentelemetry/sdk-trace-base@^2.1.0": + version "2.1.0" + resolved "https://registry.yarnpkg.com/@opentelemetry/sdk-trace-base/-/sdk-trace-base-2.1.0.tgz#9d31474824e9ed215f94bf71260d5321f64d402a" + integrity sha512-uTX9FBlVQm4S2gVQO1sb5qyBLq/FPjbp+tmGoxu4tIgtYGmBYB44+KX/725RFDe30yBSaA9Ml9fqphe1hbUyLQ== dependencies: - "@opentelemetry/core" "2.0.0" - "@opentelemetry/resources" "2.0.0" + "@opentelemetry/core" "2.1.0" + "@opentelemetry/resources" "2.1.0" "@opentelemetry/semantic-conventions" "^1.29.0" -"@opentelemetry/semantic-conventions@^1.27.0", "@opentelemetry/semantic-conventions@^1.29.0", "@opentelemetry/semantic-conventions@^1.30.0", "@opentelemetry/semantic-conventions@^1.33.1", "@opentelemetry/semantic-conventions@^1.34.0", "@opentelemetry/semantic-conventions@^1.36.0": - version "1.36.0" - resolved "https://registry.yarnpkg.com/@opentelemetry/semantic-conventions/-/semantic-conventions-1.36.0.tgz#149449bd4df4d0464220915ad4164121e0d75d4d" - integrity sha512-TtxJSRD8Ohxp6bKkhrm27JRHAxPczQA7idtcTOMYI+wQRRrfgqxHv1cFbCApcSnNjtXkmzFozn6jQtFrOmbjPQ== +"@opentelemetry/semantic-conventions@^1.27.0", "@opentelemetry/semantic-conventions@^1.29.0", "@opentelemetry/semantic-conventions@^1.30.0", "@opentelemetry/semantic-conventions@^1.33.1", "@opentelemetry/semantic-conventions@^1.34.0", "@opentelemetry/semantic-conventions@^1.37.0": + version "1.37.0" + resolved "https://registry.yarnpkg.com/@opentelemetry/semantic-conventions/-/semantic-conventions-1.37.0.tgz#aa2b4fa0b910b66a050c5ddfcac1d262e91a321a" + integrity sha512-JD6DerIKdJGmRp4jQyX5FlrQjA4tjOw1cvfsPAZXfOOEErMUHjPcPSICS+6WnM0nB0efSFARh0KAZss+bvExOA== "@opentelemetry/sql-common@^0.41.0": version "0.41.0" @@ -6341,10 +6304,10 @@ resolved "https://registry.yarnpkg.com/@poppinss/exception/-/exception-1.2.2.tgz#8d30d42e126c54fe84e997433e4dcac610090743" integrity sha512-m7bpKCD4QMlFCjA/nKTs23fuvoVFoA83brRKmObCUNmi/9tVu8Ve3w4YQAnJu4q3Tjf5fr685HYIC/IA2zHRSg== -"@prisma/instrumentation@6.14.0": - version "6.14.0" - resolved "https://registry.yarnpkg.com/@prisma/instrumentation/-/instrumentation-6.14.0.tgz#8f6e43b73ee2b88b98cec901457f4da7da13aea3" - integrity sha512-Po/Hry5bAeunRDq0yAQueKookW3glpP+qjjvvyOfm6dI2KG5/Y6Bgg3ahyWd7B0u2E+Wf9xRk2rtdda7ySgK1A== +"@prisma/instrumentation@6.15.0": + version "6.15.0" + resolved "https://registry.yarnpkg.com/@prisma/instrumentation/-/instrumentation-6.15.0.tgz#40b066dc6b1ea621aa5ae0fd6d54319550b7d8c9" + integrity sha512-6TXaH6OmDkMOQvOxwLZ8XS51hU2v4A3vmE2pSijCIiGRJYyNeMcL6nMHQMyYdZRD8wl7LF3Wzc+AMPMV/9Oo7A== dependencies: "@opentelemetry/instrumentation" "^0.52.0 || ^0.53.0 || ^0.54.0 || ^0.55.0 || ^0.56.0 || ^0.57.0" @@ -8649,10 +8612,10 @@ dependencies: "@types/pg" "*" -"@types/pg@*", "@types/pg@8.15.4", "@types/pg@^8.6.5": - version "8.15.4" - resolved "https://registry.yarnpkg.com/@types/pg/-/pg-8.15.4.tgz#419f791c6fac8e0bed66dd8f514b60f8ba8db46d" - integrity sha512-I6UNVBAoYbvuWkkU3oosC8yxqH21f4/Jc4DK71JLG3dT2mdlGe1z+ep/LQGXaKaOgcvUrsQoPRqfgtMcvZiJhg== +"@types/pg@*", "@types/pg@8.15.5", "@types/pg@^8.6.5": + version "8.15.5" + resolved "https://registry.yarnpkg.com/@types/pg/-/pg-8.15.5.tgz#ef43e0f33b62dac95cae2f042888ec7980b30c09" + integrity sha512-LF7lF6zWEKxuT3/OR8wAZGzkg4ENGXFNyiV/JeOt9z5B+0ZVwbql9McqX5c/WStFq1GaGso7H1AzP/qSzmlCKQ== dependencies: "@types/node" "*" pg-protocol "*" @@ -28751,7 +28714,6 @@ stylus@0.59.0, stylus@^0.59.0: sucrase@^3.27.0, sucrase@^3.35.0, sucrase@getsentry/sucrase#es2020-polyfills: version "3.36.0" - uid fd682f6129e507c00bb4e6319cc5d6b767e36061 resolved "https://codeload.github.com/getsentry/sucrase/tar.gz/fd682f6129e507c00bb4e6319cc5d6b767e36061" dependencies: "@jridgewell/gen-mapping" "^0.3.2" From ebe6a67ea9da07b10ec0e22dd522e41d58688457 Mon Sep 17 00:00:00 2001 From: Lukas Stracke Date: Wed, 10 Sep 2025 17:41:12 +0200 Subject: [PATCH 05/61] fix(browser): Ensure propagated `parentSpanId` stays consistent during trace in TwP mode (#17526) This PR adjusts how we set treat the `parentSpanId` when propagating it in an SDK configured for [tracing without performance](https://develop.sentry.dev/sdk/telemetry/traces/tracing-without-performance). Previously, we didn't set the `propagationSpanId` on the `propagationContext` (which is supposed to be serialized to the parent span id in the `sentry-trace` (and `traceparent`) header, if no active span is present. This had the consequence that every time `getTraceData()` was called, we'd return a new, random (non-existing) parent span id. Meaning, that if we generate both, `sentry-trace` and `traceparent` headers via `getTraceData()` they'd end up with different parent span ids in the respective hedaers. With this change, we now set a `propagationSpanId` on the PC, which has two effects: 1. consistent parent span id in `sentry-trace` and `traceparent` headers for the same outgoing request 2. consistent parent span ids in all outgoing requests until the next navigation when the PC is recycled So this prolongs the time span in which we propagate the same parent span id. Since this span doesn't exist, I _think_ this is fine. Though happy to drop this if reviewers have concerns. The main benefit with this change is 1 in combination with us generating `traceparent` from raw data rather than parsing the already generated `sentry-trace` header and converting its parts into `traceparent`. (TwP was a mistake) --- .../meta-precedence/test.ts | 3 + .../subject.js | 3 +- .../test.ts | 23 +++- .../init.js | 11 ++ .../template.html | 17 +++ .../test.ts | 130 ++++++++++++++++++ .../src/tracing/browserTracingIntegration.ts | 21 ++- .../tracing/browserTracingIntegration.test.ts | 12 +- packages/core/src/utils/spanUtils.ts | 11 +- packages/core/src/utils/traceData.ts | 27 +--- packages/core/src/utils/tracing.ts | 11 ++ .../core/test/lib/utils/traceData.test.ts | 32 ----- packages/core/test/lib/utils/tracing.test.ts | 24 +++- 13 files changed, 259 insertions(+), 66 deletions(-) create mode 100644 dev-packages/browser-integration-tests/suites/tracing/trace-lifetime/tracing-without-performance-propagateTraceparent/init.js create mode 100644 dev-packages/browser-integration-tests/suites/tracing/trace-lifetime/tracing-without-performance-propagateTraceparent/template.html create mode 100644 dev-packages/browser-integration-tests/suites/tracing/trace-lifetime/tracing-without-performance-propagateTraceparent/test.ts diff --git a/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/linked-traces/consistent-sampling/meta-precedence/test.ts b/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/linked-traces/consistent-sampling/meta-precedence/test.ts index 1b55f5235088..ece2b1f85790 100644 --- a/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/linked-traces/consistent-sampling/meta-precedence/test.ts +++ b/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/linked-traces/consistent-sampling/meta-precedence/test.ts @@ -30,10 +30,13 @@ sentryTest.describe('When `consistentTraceSampling` is `true` and page contains const clientReportPromise = waitForClientReportRequest(page); await sentryTest.step('Initial pageload', async () => { + // negative sampling decision -> no pageload txn await page.goto(url); }); await sentryTest.step('Make fetch request', async () => { + // The fetch requests starts a new trace on purpose. So we only want the + // sampling decision and rand to be the same as from the meta tag but not the trace id or DSC const tracingHeadersPromise = waitForTracingHeadersOnUrl(page, 'http://sentry-test-external.io'); await page.locator('#btn2').click(); diff --git a/dev-packages/browser-integration-tests/suites/tracing/request/fetch-tracing-without-performance-propagateTraceparent/subject.js b/dev-packages/browser-integration-tests/suites/tracing/request/fetch-tracing-without-performance-propagateTraceparent/subject.js index e9a1ca98b5b0..60acaa1ebdf8 100644 --- a/dev-packages/browser-integration-tests/suites/tracing/request/fetch-tracing-without-performance-propagateTraceparent/subject.js +++ b/dev-packages/browser-integration-tests/suites/tracing/request/fetch-tracing-without-performance-propagateTraceparent/subject.js @@ -1 +1,2 @@ -fetch('http://sentry-test-site.example/0').then(); +fetch('http://sentry-test-site.example/0'); +fetch('http://sentry-test-site.example/1'); diff --git a/dev-packages/browser-integration-tests/suites/tracing/request/fetch-tracing-without-performance-propagateTraceparent/test.ts b/dev-packages/browser-integration-tests/suites/tracing/request/fetch-tracing-without-performance-propagateTraceparent/test.ts index 22cc7ba98eb8..718cca0c8701 100644 --- a/dev-packages/browser-integration-tests/suites/tracing/request/fetch-tracing-without-performance-propagateTraceparent/test.ts +++ b/dev-packages/browser-integration-tests/suites/tracing/request/fetch-tracing-without-performance-propagateTraceparent/test.ts @@ -12,21 +12,36 @@ sentryTest( const url = await getLocalTestUrl({ testDir: __dirname }); - const [, request] = await Promise.all([page.goto(url), page.waitForRequest('http://sentry-test-site.example/0')]); + const [, request0, request1] = await Promise.all([ + page.goto(url), + page.waitForRequest('http://sentry-test-site.example/0'), + page.waitForRequest('http://sentry-test-site.example/1'), + ]); - const requestHeaders = request.headers(); + const requestHeaders0 = request0.headers(); - const traceparentData = extractTraceparentData(requestHeaders['sentry-trace']); + const traceparentData = extractTraceparentData(requestHeaders0['sentry-trace']); expect(traceparentData).toMatchObject({ traceId: expect.stringMatching(/^([a-f0-9]{32})$/), parentSpanId: expect.stringMatching(/^([a-f0-9]{16})$/), parentSampled: undefined, }); - expect(requestHeaders).toMatchObject({ + expect(requestHeaders0).toMatchObject({ 'sentry-trace': `${traceparentData?.traceId}-${traceparentData?.parentSpanId}`, baggage: expect.stringContaining(`sentry-trace_id=${traceparentData?.traceId}`), traceparent: `00-${traceparentData?.traceId}-${traceparentData?.parentSpanId}-00`, }); + + const requestHeaders1 = request1.headers(); + expect(requestHeaders1).toMatchObject({ + 'sentry-trace': `${traceparentData?.traceId}-${traceparentData?.parentSpanId}`, + baggage: expect.stringContaining(`sentry-trace_id=${traceparentData?.traceId}`), + traceparent: `00-${traceparentData?.traceId}-${traceparentData?.parentSpanId}-00`, + }); + + expect(requestHeaders1['sentry-trace']).toBe(requestHeaders0['sentry-trace']); + expect(requestHeaders1['baggage']).toBe(requestHeaders0['baggage']); + expect(requestHeaders1['traceparent']).toBe(requestHeaders0['traceparent']); }, ); diff --git a/dev-packages/browser-integration-tests/suites/tracing/trace-lifetime/tracing-without-performance-propagateTraceparent/init.js b/dev-packages/browser-integration-tests/suites/tracing/trace-lifetime/tracing-without-performance-propagateTraceparent/init.js new file mode 100644 index 000000000000..b5b7d0354f77 --- /dev/null +++ b/dev-packages/browser-integration-tests/suites/tracing/trace-lifetime/tracing-without-performance-propagateTraceparent/init.js @@ -0,0 +1,11 @@ +import * as Sentry from '@sentry/browser'; + +window.Sentry = Sentry; + +Sentry.init({ + // in browser TwP means not setting tracesSampleRate but adding browserTracingIntegration, + dsn: 'https://public@dsn.ingest.sentry.io/1337', + integrations: [Sentry.browserTracingIntegration()], + tracePropagationTargets: ['http://sentry-test-site.example'], + propagateTraceparent: true, +}); diff --git a/dev-packages/browser-integration-tests/suites/tracing/trace-lifetime/tracing-without-performance-propagateTraceparent/template.html b/dev-packages/browser-integration-tests/suites/tracing/trace-lifetime/tracing-without-performance-propagateTraceparent/template.html new file mode 100644 index 000000000000..755df10f3a1a --- /dev/null +++ b/dev-packages/browser-integration-tests/suites/tracing/trace-lifetime/tracing-without-performance-propagateTraceparent/template.html @@ -0,0 +1,17 @@ + + + + + + + + + + + + + + diff --git a/dev-packages/browser-integration-tests/suites/tracing/trace-lifetime/tracing-without-performance-propagateTraceparent/test.ts b/dev-packages/browser-integration-tests/suites/tracing/trace-lifetime/tracing-without-performance-propagateTraceparent/test.ts new file mode 100644 index 000000000000..d2106b892b7b --- /dev/null +++ b/dev-packages/browser-integration-tests/suites/tracing/trace-lifetime/tracing-without-performance-propagateTraceparent/test.ts @@ -0,0 +1,130 @@ +import { expect } from '@playwright/test'; +import { extractTraceparentData } from '@sentry/core'; +import { sentryTest } from '../../../../utils/fixtures'; +import { shouldSkipTracingTest } from '../../../../utils/helpers'; + +const META_TAG_TRACE_ID = '12345678901234567890123456789012'; +const META_TAG_PARENT_SPAN_ID = '1234567890123456'; +const META_TAG_BAGGAGE = + 'sentry-trace_id=12345678901234567890123456789012,sentry-public_key=public,sentry-release=1.0.0,sentry-environment=prod,sentry-sample_rand=0.42'; + +sentryTest( + 'outgoing fetch requests have new traceId after navigation (with propagateTraceparent)', + async ({ getLocalTestUrl, page }) => { + if (shouldSkipTracingTest()) { + sentryTest.skip(); + } + + const url = await getLocalTestUrl({ testDir: __dirname }); + + await page.route('http://sentry-test-site.example/**', route => { + return route.fulfill({ + status: 200, + contentType: 'application/json', + body: JSON.stringify({}), + }); + }); + + await page.goto(url); + + const requestPromise = page.waitForRequest('http://sentry-test-site.example/*'); + await page.locator('#fetchBtn').click(); + const request = await requestPromise; + const headers = request.headers(); + + const sentryTraceParentData = extractTraceparentData(headers['sentry-trace']); + // sampling decision is deferred because TwP means we didn't sample any span + expect(sentryTraceParentData).toEqual({ + traceId: META_TAG_TRACE_ID, + parentSpanId: expect.stringMatching(/^[0-9a-f]{16}$/), + parentSampled: undefined, + }); + expect(headers['baggage']).toBe(META_TAG_BAGGAGE); + // but traceparent propagates a negative sampling decision because it has no concept of deferred sampling + expect(headers['traceparent']).toBe( + `00-${sentryTraceParentData?.traceId}-${sentryTraceParentData?.parentSpanId}-00`, + ); + + const requestPromise2 = page.waitForRequest('http://sentry-test-site.example/*'); + await page.locator('#fetchBtn').click(); + const request2 = await requestPromise2; + const headers2 = request2.headers(); + + const sentryTraceParentData2 = extractTraceparentData(headers2['sentry-trace']); + expect(sentryTraceParentData2).toEqual(sentryTraceParentData); + + await page.goto(`${url}#navigation`); + + const requestPromise3 = page.waitForRequest('http://sentry-test-site.example/*'); + await page.locator('#fetchBtn').click(); + const request3 = await requestPromise3; + const headers3 = request3.headers(); + + const sentryTraceParentData3 = extractTraceparentData(headers3['sentry-trace']); + // sampling decision is deferred because TwP means we didn't sample any span + expect(sentryTraceParentData3).toEqual({ + traceId: expect.not.stringContaining(sentryTraceParentData!.traceId!), + parentSpanId: expect.not.stringContaining(sentryTraceParentData!.parentSpanId!), + parentSampled: undefined, + }); + + expect(headers3['baggage']).toMatch( + /sentry-environment=production,sentry-public_key=public,sentry-trace_id=[0-9a-f]{32}/, + ); + expect(headers3['baggage']).not.toContain(`sentry-trace_id=${META_TAG_TRACE_ID}`); + // but traceparent propagates a negative sampling decision because it has no concept of deferred sampling + expect(headers3['traceparent']).toBe( + `00-${sentryTraceParentData3!.traceId}-${sentryTraceParentData3!.parentSpanId}-00`, + ); + + const requestPromise4 = page.waitForRequest('http://sentry-test-site.example/*'); + await page.locator('#fetchBtn').click(); + const request4 = await requestPromise4; + const headers4 = request4.headers(); + + const sentryTraceParentData4 = extractTraceparentData(headers4['sentry-trace']); + expect(sentryTraceParentData4).toEqual(sentryTraceParentData3); + }, +); + +sentryTest('outgoing XHR requests have new traceId after navigation', async ({ getLocalTestUrl, page }) => { + if (shouldSkipTracingTest()) { + sentryTest.skip(); + } + + const url = await getLocalTestUrl({ testDir: __dirname }); + + await page.route('http://sentry-test-site.example/**', route => { + return route.fulfill({ + status: 200, + contentType: 'application/json', + body: JSON.stringify({}), + }); + }); + + await page.goto(url); + + const requestPromise = page.waitForRequest('http://sentry-test-site.example/*'); + await page.locator('#xhrBtn').click(); + const request = await requestPromise; + const headers = request.headers(); + + // sampling decision is deferred because TwP means we didn't sample any span + expect(headers['sentry-trace']).toMatch(new RegExp(`^${META_TAG_TRACE_ID}-[0-9a-f]{16}$`)); + expect(headers['baggage']).toBe(META_TAG_BAGGAGE); + + await page.goto(`${url}#navigation`); + + const requestPromise2 = page.waitForRequest('http://sentry-test-site.example/*'); + await page.locator('#xhrBtn').click(); + const request2 = await requestPromise2; + const headers2 = request2.headers(); + + // sampling decision is deferred because TwP means we didn't sample any span + expect(headers2['sentry-trace']).toMatch(/^[0-9a-f]{32}-[0-9a-f]{16}$/); + expect(headers2['baggage']).not.toBe(`${META_TAG_TRACE_ID}-${META_TAG_PARENT_SPAN_ID}`); + expect(headers2['baggage']).toMatch( + /sentry-environment=production,sentry-public_key=public,sentry-trace_id=[0-9a-f]{32}/, + ); + expect(headers2['baggage']).not.toContain(`sentry-trace_id=${META_TAG_TRACE_ID}`); +}); diff --git a/packages/browser/src/tracing/browserTracingIntegration.ts b/packages/browser/src/tracing/browserTracingIntegration.ts index cd63678de16f..edec1b1617b6 100644 --- a/packages/browser/src/tracing/browserTracingIntegration.ts +++ b/packages/browser/src/tracing/browserTracingIntegration.ts @@ -5,6 +5,7 @@ import { browserPerformanceTimeOrigin, dateTimestampInSeconds, debug, + generateSpanId, generateTraceId, getClient, getCurrentScope, @@ -12,6 +13,7 @@ import { getIsolationScope, getLocationHref, GLOBAL_OBJ, + hasSpansEnabled, parseStringToURLObject, propagationContextFromHeaders, registerSpanErrorInstrumentation, @@ -512,10 +514,19 @@ export const browserTracingIntegration = ((_options: Partial { expect(oldCurrentScopePropCtx).toEqual({ traceId: expect.stringMatching(/[a-f0-9]{32}/), + propagationSpanId: expect.stringMatching(/[a-f0-9]{16}/), sampleRand: expect.any(Number), }); expect(oldIsolationScopePropCtx).toEqual({ @@ -731,15 +732,18 @@ describe('browserTracingIntegration', () => { }); expect(newCurrentScopePropCtx).toEqual({ traceId: expect.stringMatching(/[a-f0-9]{32}/), + propagationSpanId: expect.stringMatching(/[a-f0-9]{16}/), sampleRand: expect.any(Number), }); expect(newIsolationScopePropCtx).toEqual({ traceId: expect.stringMatching(/[a-f0-9]{32}/), + propagationSpanId: expect.stringMatching(/[a-f0-9]{16}/), sampleRand: expect.any(Number), }); expect(newIsolationScopePropCtx.traceId).not.toEqual(oldIsolationScopePropCtx.traceId); expect(newCurrentScopePropCtx.traceId).not.toEqual(oldCurrentScopePropCtx.traceId); + expect(newIsolationScopePropCtx.propagationSpanId).not.toEqual(oldIsolationScopePropCtx.propagationSpanId); }); it("saves the span's positive sampling decision and its DSC on the propagationContext when the span finishes", () => { @@ -758,7 +762,7 @@ describe('browserTracingIntegration', () => { }); const propCtxBeforeEnd = getCurrentScope().getPropagationContext(); - expect(propCtxBeforeEnd).toStrictEqual({ + expect(propCtxBeforeEnd).toEqual({ sampleRand: expect.any(Number), traceId: expect.stringMatching(/[a-f0-9]{32}/), }); @@ -766,7 +770,7 @@ describe('browserTracingIntegration', () => { navigationSpan!.end(); const propCtxAfterEnd = getCurrentScope().getPropagationContext(); - expect(propCtxAfterEnd).toStrictEqual({ + expect(propCtxAfterEnd).toEqual({ traceId: propCtxBeforeEnd.traceId, sampled: true, sampleRand: expect.any(Number), @@ -800,7 +804,7 @@ describe('browserTracingIntegration', () => { }); const propCtxBeforeEnd = getCurrentScope().getPropagationContext(); - expect(propCtxBeforeEnd).toStrictEqual({ + expect(propCtxBeforeEnd).toEqual({ traceId: expect.stringMatching(/[a-f0-9]{32}/), sampleRand: expect.any(Number), }); @@ -808,7 +812,7 @@ describe('browserTracingIntegration', () => { navigationSpan!.end(); const propCtxAfterEnd = getCurrentScope().getPropagationContext(); - expect(propCtxAfterEnd).toStrictEqual({ + expect(propCtxAfterEnd).toEqual({ traceId: propCtxBeforeEnd.traceId, sampled: false, sampleRand: expect.any(Number), diff --git a/packages/core/src/utils/spanUtils.ts b/packages/core/src/utils/spanUtils.ts index b6761c9930e7..89ecc6872efb 100644 --- a/packages/core/src/utils/spanUtils.ts +++ b/packages/core/src/utils/spanUtils.ts @@ -17,7 +17,7 @@ import type { SpanStatus } from '../types-hoist/spanStatus'; import { addNonEnumerableProperty } from '../utils/object'; import { generateSpanId } from '../utils/propagationContext'; import { timestampInSeconds } from '../utils/time'; -import { generateSentryTraceHeader } from '../utils/tracing'; +import { generateSentryTraceHeader, generateTraceparentHeader } from '../utils/tracing'; import { consoleSandbox } from './debug-logger'; import { _getSpanForScope } from './spanOnScope'; @@ -77,6 +77,15 @@ export function spanToTraceHeader(span: Span): string { return generateSentryTraceHeader(traceId, spanId, sampled); } +/** + * Convert a Span to a W3C traceparent header. + */ +export function spanToTraceparentHeader(span: Span): string { + const { traceId, spanId } = span.spanContext(); + const sampled = spanIsSampled(span); + return generateTraceparentHeader(traceId, spanId, sampled); +} + /** * Converts the span links array to a flattened version to be sent within an envelope. * diff --git a/packages/core/src/utils/traceData.ts b/packages/core/src/utils/traceData.ts index aa335dbd37bd..325e45a038e0 100644 --- a/packages/core/src/utils/traceData.ts +++ b/packages/core/src/utils/traceData.ts @@ -9,8 +9,8 @@ import type { Span } from '../types-hoist/span'; import type { SerializedTraceData } from '../types-hoist/tracing'; import { dynamicSamplingContextToSentryBaggageHeader } from './baggage'; import { debug } from './debug-logger'; -import { getActiveSpan, spanToTraceHeader } from './spanUtils'; -import { extractTraceparentData, generateSentryTraceHeader, TRACEPARENT_REGEXP } from './tracing'; +import { getActiveSpan, spanToTraceHeader, spanToTraceparentHeader } from './spanUtils'; +import { generateSentryTraceHeader, generateTraceparentHeader, TRACEPARENT_REGEXP } from './tracing'; /** * Extracts trace propagation data from the current span or from the client's scope (via transaction or propagation @@ -58,7 +58,7 @@ export function getTraceData( }; if (options.propagateTraceparent) { - const traceparent = _sentryTraceToTraceParentHeader(sentryTrace); + const traceparent = span ? spanToTraceparentHeader(span) : scopeToTraceparentHeader(scope); if (traceparent) { traceData.traceparent = traceparent; } @@ -75,22 +75,7 @@ function scopeToTraceHeader(scope: Scope): string { return generateSentryTraceHeader(traceId, propagationSpanId, sampled); } -/** - * Builds a W3C traceparent header from the given sentry-trace header. - * - * Why parse that header and not create traceparent from primitives? - * We want these two headers to always have the same ids. The easiest way to do this is to take - * one of them as the source of truth (sentry-trace) and derive the other from it. - * - * Most importantly, this guarantees parentSpanId consistency between sentry-trace and traceparent - * in tracing without performance (TwP) mode, where we always generate a random parentSpanId. - * - * Exported for testing - */ -export function _sentryTraceToTraceParentHeader(sentryTrace: string): string | undefined { - const { traceId, parentSpanId, parentSampled } = extractTraceparentData(sentryTrace) || {}; - if (!traceId || !parentSpanId) { - return undefined; - } - return `00-${traceId}-${parentSpanId}-${parentSampled ? '01' : '00'}`; +function scopeToTraceparentHeader(scope: Scope): string { + const { traceId, sampled, propagationSpanId } = scope.getPropagationContext(); + return generateTraceparentHeader(traceId, propagationSpanId, sampled); } diff --git a/packages/core/src/utils/tracing.ts b/packages/core/src/utils/tracing.ts index 0310cc8640e6..aa5a15153674 100644 --- a/packages/core/src/utils/tracing.ts +++ b/packages/core/src/utils/tracing.ts @@ -102,6 +102,17 @@ export function generateSentryTraceHeader( return `${traceId}-${spanId}${sampledString}`; } +/** + * Creates a W3C traceparent header from the given trace and span ids. + */ +export function generateTraceparentHeader( + traceId: string | undefined = generateTraceId(), + spanId: string | undefined = generateSpanId(), + sampled?: boolean, +): string { + return `00-${traceId}-${spanId}-${sampled ? '01' : '00'}`; +} + /** * Given any combination of an incoming trace, generate a sample rand based on its defined semantics. * diff --git a/packages/core/test/lib/utils/traceData.test.ts b/packages/core/test/lib/utils/traceData.test.ts index 37764cfc94a3..379103a8a48c 100644 --- a/packages/core/test/lib/utils/traceData.test.ts +++ b/packages/core/test/lib/utils/traceData.test.ts @@ -15,7 +15,6 @@ import { import { getAsyncContextStrategy } from '../../../src/asyncContext'; import { freezeDscOnSpan } from '../../../src/tracing/dynamicSamplingContext'; import type { Span } from '../../../src/types-hoist/span'; -import { _sentryTraceToTraceParentHeader } from '../../../src/utils/traceData'; import type { TestClientOptions } from '../../mocks/client'; import { getDefaultTestClientOptions, TestClient } from '../../mocks/client'; @@ -349,34 +348,3 @@ describe('getTraceData', () => { expect(traceData.traceparent).toMatch(/00-12345678901234567890123456789099-[0-9a-f]{16}-00/); }); }); - -describe('_sentryTraceToTraceParentHeader', () => { - it('returns positively sampled traceparent header for sentry-trace with positive sampling decision', () => { - const traceparent = _sentryTraceToTraceParentHeader('12345678901234567890123456789012-1234567890123456-1'); - expect(traceparent).toBe('00-12345678901234567890123456789012-1234567890123456-01'); - }); - - it('returns negatively sampled traceparent header for sentry-trace with negative sampling decision', () => { - const traceparent = _sentryTraceToTraceParentHeader('12345678901234567890123456789012-1234567890123456-0'); - expect(traceparent).toBe('00-12345678901234567890123456789012-1234567890123456-00'); - }); - - it('returns negatively sampled traceparent header for sentry-trace with no/deferred sampling decision', () => { - const traceparent = _sentryTraceToTraceParentHeader('12345678901234567890123456789012-1234567890123456'); - expect(traceparent).toBe('00-12345678901234567890123456789012-1234567890123456-00'); - }); - - it.each([ - '12345678901234567890123456789012--0', - '-12345678901234567890123456789012-0', - '--1', - '0', - '1', - '', - '00-12345678901234567890123456789012-1234567890123456-01', - '00-12345678901234567890123456789012-1234567890123456-00', - ])('returns undefined if the sentry-trace header is invalid (%s)', sentryTrace => { - const traceparent = _sentryTraceToTraceParentHeader(sentryTrace); - expect(traceparent).toBeUndefined(); - }); -}); diff --git a/packages/core/test/lib/utils/tracing.test.ts b/packages/core/test/lib/utils/tracing.test.ts index ea41190f3bb3..c950679a21be 100644 --- a/packages/core/test/lib/utils/tracing.test.ts +++ b/packages/core/test/lib/utils/tracing.test.ts @@ -1,5 +1,10 @@ import { describe, expect, it, test } from 'vitest'; -import { extractTraceparentData, propagationContextFromHeaders, shouldContinueTrace } from '../../../src/utils/tracing'; +import { + extractTraceparentData, + generateTraceparentHeader, + propagationContextFromHeaders, + shouldContinueTrace, +} from '../../../src/utils/tracing'; import { getDefaultTestClientOptions, TestClient } from '../../mocks/client'; const EXAMPLE_SENTRY_TRACE = '12312012123120121231201212312012-1121201211212012-1'; @@ -177,3 +182,20 @@ describe('shouldContinueTrace', () => { expect(result).toBe(false); }); }); + +describe('generateTraceparentHeader', () => { + test('returns a traceparent header with the given ids and positive sampling decision', () => { + const traceparent = generateTraceparentHeader('12345678901234567890123456789012', '1234567890123456', true); + expect(traceparent).toBe('00-12345678901234567890123456789012-1234567890123456-01'); + }); + + test('returns a traceparent header with the given ids and negative sampling decision', () => { + const traceparent = generateTraceparentHeader('12345678901234567890123456789012', '1234567890123456', false); + expect(traceparent).toBe('00-12345678901234567890123456789012-1234567890123456-00'); + }); + + test('no sampling decision passed, creates a negatively sampled traceparent header', () => { + const traceparent = generateTraceparentHeader('12345678901234567890123456789012', '1234567890123456'); + expect(traceparent).toBe('00-12345678901234567890123456789012-1234567890123456-00'); + }); +}); From 4cec9f7642f467a27a3430d51f1c5a6ce8efd7fe Mon Sep 17 00:00:00 2001 From: Lukas Stracke Date: Wed, 10 Sep 2025 18:40:49 +0200 Subject: [PATCH 06/61] chore(repo): Rename `.claude/settings.local.json` to `.claude/settings.json` (#17591) --- .claude/{settings.local.json => settings.json} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename .claude/{settings.local.json => settings.json} (100%) diff --git a/.claude/settings.local.json b/.claude/settings.json similarity index 100% rename from .claude/settings.local.json rename to .claude/settings.json From 0305685eb632ced13e0b81d17675713497b55d1b Mon Sep 17 00:00:00 2001 From: Sigrid Huemer <32902192+s1gr1d@users.noreply.github.com> Date: Thu, 11 Sep 2025 09:53:30 +0200 Subject: [PATCH 07/61] docs(issue-template): Add note about prioritization (#17590) Add a `` text at the end of the issue like [seen here](https://github.com/open-telemetry/opentelemetry-js-contrib/blob/main/.github/ISSUE_TEMPLATE/bug_report.md). We are using the issue form syntax, so it's added as pre-filled content. --- .github/ISSUE_TEMPLATE/bug.yml | 10 ++++++++++ .github/ISSUE_TEMPLATE/feature.yml | 10 ++++++++++ 2 files changed, 20 insertions(+) diff --git a/.github/ISSUE_TEMPLATE/bug.yml b/.github/ISSUE_TEMPLATE/bug.yml index 38da90a027b8..64709dd1f999 100644 --- a/.github/ISSUE_TEMPLATE/bug.yml +++ b/.github/ISSUE_TEMPLATE/bug.yml @@ -133,6 +133,16 @@ body: description: Logs? Screenshots? Yes, please. validations: required: true + - type: textarea + id: additional + attributes: + label: Additional Context + description: + Add any other context here. Please keep the pre-filled text, which helps us manage issue prioritization. + value: |- + Tip: React with 👍 to help prioritize this issue. Please use comments to provide useful context, avoiding `+1` or `me too`, to help us triage it. + validations: + required: false - type: markdown attributes: value: |- diff --git a/.github/ISSUE_TEMPLATE/feature.yml b/.github/ISSUE_TEMPLATE/feature.yml index 67039ae57441..2859c10d2dc0 100644 --- a/.github/ISSUE_TEMPLATE/feature.yml +++ b/.github/ISSUE_TEMPLATE/feature.yml @@ -23,6 +23,16 @@ body: Add a blender to Sentry. validations: required: true + - type: textarea + id: additional + attributes: + label: Additional Context + description: + Add any other context here. Please keep the pre-filled text, which helps us manage feature prioritization. + value: |- + Tip: React with 👍 to help prioritize this improvement. Please use comments to provide useful context, avoiding `+1` or `me too`, to help us triage it. + validations: + required: false - type: markdown attributes: value: |- From 7f40621aa349dd6ae48f7464c5b444bcd9b4a5b4 Mon Sep 17 00:00:00 2001 From: Rola Abuhasna Date: Thu, 11 Sep 2025 10:12:50 +0200 Subject: [PATCH 08/61] feat(cloudflare,vercel-edge): Add support for Anthropic AI instrumentation (#17571) Adds support for AnthropicAI manual instrumentation in @sentry/cloudflare and @sentry/vercel-edge. To instrument the AnthropicAI client, wrap it with Sentry.instrumentAnthropicAiClient and set recording settings. ``` import * as Sentry from '@sentry/cloudflare'; import Anthropic from '@anthropic-ai/sdk'; const anthropic = new Anthropic(); const client = Sentry.instrumentAnthropicAiClient(anthropic, { recordInputs: true, recordOutputs: true }); // use the wrapped client ``` --- .../suites/tracing/anthropic-ai/index.ts | 32 +++++++++ .../suites/tracing/anthropic-ai/mocks.ts | 68 +++++++++++++++++++ .../suites/tracing/anthropic-ai/test.ts | 41 +++++++++++ .../tracing/anthropic-ai/wrangler.jsonc | 6 ++ packages/cloudflare/src/index.ts | 1 + packages/core/src/index.ts | 7 +- packages/core/src/utils/anthropic-ai/index.ts | 7 +- packages/vercel-edge/src/index.ts | 1 + 8 files changed, 158 insertions(+), 5 deletions(-) create mode 100644 dev-packages/cloudflare-integration-tests/suites/tracing/anthropic-ai/index.ts create mode 100644 dev-packages/cloudflare-integration-tests/suites/tracing/anthropic-ai/mocks.ts create mode 100644 dev-packages/cloudflare-integration-tests/suites/tracing/anthropic-ai/test.ts create mode 100644 dev-packages/cloudflare-integration-tests/suites/tracing/anthropic-ai/wrangler.jsonc diff --git a/dev-packages/cloudflare-integration-tests/suites/tracing/anthropic-ai/index.ts b/dev-packages/cloudflare-integration-tests/suites/tracing/anthropic-ai/index.ts new file mode 100644 index 000000000000..08df5e24b05b --- /dev/null +++ b/dev-packages/cloudflare-integration-tests/suites/tracing/anthropic-ai/index.ts @@ -0,0 +1,32 @@ +import * as Sentry from '@sentry/cloudflare'; +import type { AnthropicAiClient } from '@sentry/core'; +import { MockAnthropic } from './mocks'; + +interface Env { + SENTRY_DSN: string; +} + +const mockClient = new MockAnthropic({ + apiKey: 'mock-api-key', +}); + +const client: AnthropicAiClient = Sentry.instrumentAnthropicAiClient(mockClient); + +export default Sentry.withSentry( + (env: Env) => ({ + dsn: env.SENTRY_DSN, + tracesSampleRate: 1.0, + }), + { + async fetch(_request, _env, _ctx) { + const response = await client.messages?.create({ + model: 'claude-3-haiku-20240307', + messages: [{ role: 'user', content: 'What is the capital of France?' }], + temperature: 0.7, + max_tokens: 100, + }); + + return new Response(JSON.stringify(response)); + }, + }, +); diff --git a/dev-packages/cloudflare-integration-tests/suites/tracing/anthropic-ai/mocks.ts b/dev-packages/cloudflare-integration-tests/suites/tracing/anthropic-ai/mocks.ts new file mode 100644 index 000000000000..cff87ca84cbc --- /dev/null +++ b/dev-packages/cloudflare-integration-tests/suites/tracing/anthropic-ai/mocks.ts @@ -0,0 +1,68 @@ +import type { AnthropicAiClient, AnthropicAiResponse } from '@sentry/core'; + +export class MockAnthropic implements AnthropicAiClient { + public messages: { + create: (...args: unknown[]) => Promise; + countTokens: (...args: unknown[]) => Promise; + }; + public models: { + list: (...args: unknown[]) => Promise; + get: (...args: unknown[]) => Promise; + }; + public completions: { + create: (...args: unknown[]) => Promise; + }; + public apiKey: string; + + public constructor(config: { apiKey: string }) { + this.apiKey = config.apiKey; + + // Main focus: messages.create functionality + this.messages = { + create: async (...args: unknown[]) => { + const params = args[0] as { model: string; stream?: boolean }; + // Simulate processing time + await new Promise(resolve => setTimeout(resolve, 10)); + + if (params.model === 'error-model') { + const error = new Error('Model not found'); + (error as unknown as { status: number }).status = 404; + (error as unknown as { headers: Record }).headers = { 'x-request-id': 'mock-request-123' }; + throw error; + } + + return { + id: 'msg_mock123', + type: 'message', + role: 'assistant', + model: params.model, + content: [ + { + type: 'text', + text: 'Hello from Anthropic mock!', + }, + ], + stop_reason: 'end_turn', + stop_sequence: null, + usage: { + input_tokens: 10, + output_tokens: 15, + cache_creation_input_tokens: 0, + cache_read_input_tokens: 0, + }, + }; + }, + countTokens: async (..._args: unknown[]) => ({ id: 'mock', type: 'model', model: 'mock', input_tokens: 0 }), + }; + + // Minimal implementations for required interface compliance + this.models = { + list: async (..._args: unknown[]) => ({ id: 'mock', type: 'model', model: 'mock' }), + get: async (..._args: unknown[]) => ({ id: 'mock', type: 'model', model: 'mock' }), + }; + + this.completions = { + create: async (..._args: unknown[]) => ({ id: 'mock', type: 'completion', model: 'mock' }), + }; + } +} diff --git a/dev-packages/cloudflare-integration-tests/suites/tracing/anthropic-ai/test.ts b/dev-packages/cloudflare-integration-tests/suites/tracing/anthropic-ai/test.ts new file mode 100644 index 000000000000..13966caaf460 --- /dev/null +++ b/dev-packages/cloudflare-integration-tests/suites/tracing/anthropic-ai/test.ts @@ -0,0 +1,41 @@ +import { expect, it } from 'vitest'; +import { createRunner } from '../../../runner'; + +// These tests are not exhaustive because the instrumentation is +// already tested in the node integration tests and we merely +// want to test that the instrumentation does not break in our +// cloudflare SDK. + +it('traces a basic message creation request', async () => { + const runner = createRunner(__dirname) + .ignore('event') + .expect(envelope => { + const transactionEvent = envelope[1]?.[0]?.[1] as any; + + expect(transactionEvent.transaction).toBe('GET /'); + expect(transactionEvent.spans).toEqual( + expect.arrayContaining([ + expect.objectContaining({ + data: expect.objectContaining({ + 'gen_ai.operation.name': 'messages', + 'sentry.op': 'gen_ai.messages', + 'sentry.origin': 'auto.ai.anthropic', + 'gen_ai.system': 'anthropic', + 'gen_ai.request.model': 'claude-3-haiku-20240307', + 'gen_ai.request.temperature': 0.7, + 'gen_ai.response.model': 'claude-3-haiku-20240307', + 'gen_ai.response.id': 'msg_mock123', + 'gen_ai.usage.input_tokens': 10, + 'gen_ai.usage.output_tokens': 15, + }), + description: 'messages claude-3-haiku-20240307', + op: 'gen_ai.messages', + origin: 'auto.ai.anthropic', + }), + ]), + ); + }) + .start(); + await runner.makeRequest('get', '/'); + await runner.completed(); +}); diff --git a/dev-packages/cloudflare-integration-tests/suites/tracing/anthropic-ai/wrangler.jsonc b/dev-packages/cloudflare-integration-tests/suites/tracing/anthropic-ai/wrangler.jsonc new file mode 100644 index 000000000000..d6be01281f0c --- /dev/null +++ b/dev-packages/cloudflare-integration-tests/suites/tracing/anthropic-ai/wrangler.jsonc @@ -0,0 +1,6 @@ +{ + "name": "worker-name", + "compatibility_date": "2025-06-17", + "main": "index.ts", + "compatibility_flags": ["nodejs_compat"], +} diff --git a/packages/cloudflare/src/index.ts b/packages/cloudflare/src/index.ts index 23e902c4dc2e..5272d475572d 100644 --- a/packages/cloudflare/src/index.ts +++ b/packages/cloudflare/src/index.ts @@ -70,6 +70,7 @@ export { // eslint-disable-next-line deprecation/deprecation inboundFiltersIntegration, instrumentOpenAiClient, + instrumentAnthropicAiClient, eventFiltersIntegration, linkedErrorsIntegration, requestDataIntegration, diff --git a/packages/core/src/index.ts b/packages/core/src/index.ts index ef61364ab3f0..d54ac9d435f2 100644 --- a/packages/core/src/index.ts +++ b/packages/core/src/index.ts @@ -131,7 +131,12 @@ export { OPENAI_INTEGRATION_NAME } from './utils/openai/constants'; export { instrumentAnthropicAiClient } from './utils/anthropic-ai'; export { ANTHROPIC_AI_INTEGRATION_NAME } from './utils/anthropic-ai/constants'; export type { OpenAiClient, OpenAiOptions, InstrumentedMethod } from './utils/openai/types'; -export type { AnthropicAiClient, AnthropicAiOptions, AnthropicAiInstrumentedMethod } from './utils/anthropic-ai/types'; +export type { + AnthropicAiClient, + AnthropicAiOptions, + AnthropicAiInstrumentedMethod, + AnthropicAiResponse, +} from './utils/anthropic-ai/types'; export type { FeatureFlag } from './utils/featureFlags'; export { diff --git a/packages/core/src/utils/anthropic-ai/index.ts b/packages/core/src/utils/anthropic-ai/index.ts index 563724d98c5c..a771dff4c75d 100644 --- a/packages/core/src/utils/anthropic-ai/index.ts +++ b/packages/core/src/utils/anthropic-ai/index.ts @@ -27,7 +27,6 @@ import { buildMethodPath, getFinalOperationName, getSpanOperation, setTokenUsage import { ANTHROPIC_AI_INTEGRATION_NAME } from './constants'; import { instrumentStream } from './streaming'; import type { - AnthropicAiClient, AnthropicAiInstrumentedMethod, AnthropicAiIntegration, AnthropicAiOptions, @@ -304,7 +303,7 @@ function instrumentMethod( /** * Create a deep proxy for Anthropic AI client instrumentation */ -function createDeepProxy(target: T, currentPath = '', options?: AnthropicAiOptions): T { +function createDeepProxy(target: T, currentPath = '', options?: AnthropicAiOptions): T { return new Proxy(target, { get(obj: object, prop: string): unknown { const value = (obj as Record)[prop]; @@ -332,11 +331,11 @@ function createDeepProxy(target: T, currentPath = ' * Instrument an Anthropic AI client with Sentry tracing * Can be used across Node.js, Cloudflare Workers, and Vercel Edge * - * @template T - The type of the client that extends AnthropicAiClient + * @template T - The type of the client that extends object * @param client - The Anthropic AI client to instrument * @param options - Optional configuration for recording inputs and outputs * @returns The instrumented client with the same type as the input */ -export function instrumentAnthropicAiClient(client: T, options?: AnthropicAiOptions): T { +export function instrumentAnthropicAiClient(client: T, options?: AnthropicAiOptions): T { return createDeepProxy(client, '', options); } diff --git a/packages/vercel-edge/src/index.ts b/packages/vercel-edge/src/index.ts index e2fd90eda5b7..374e5d8ef606 100644 --- a/packages/vercel-edge/src/index.ts +++ b/packages/vercel-edge/src/index.ts @@ -70,6 +70,7 @@ export { // eslint-disable-next-line deprecation/deprecation inboundFiltersIntegration, instrumentOpenAiClient, + instrumentAnthropicAiClient, eventFiltersIntegration, linkedErrorsIntegration, requestDataIntegration, From fefb37d84edea9f6bcd34111ed5442315b3a14d3 Mon Sep 17 00:00:00 2001 From: Sigrid Huemer <32902192+s1gr1d@users.noreply.github.com> Date: Thu, 11 Sep 2025 12:24:06 +0200 Subject: [PATCH 09/61] fix(nitro): Support nested `_platform` properties in Nitro 2.11.7+ (#17596) Since Nitro v2.11.7, the platform-specific properties like `cloudlfare` and `cf` are nested under `_platform` (https://github.com/nitrojs/nitro/pull/3224). The `isEventType` function is updated to reflect this change. stumbled across this while working on this: https://github.com/getsentry/sentry-javascript/issues/17588 --- packages/nuxt/src/runtime/plugins/index.ts | 1 - .../plugins/sentry-cloudflare.server.ts | 67 ++---- .../src/runtime/utils/event-type-check.ts | 105 +++++++++ .../runtime/utils/event-type-check.test.ts | 216 ++++++++++++++++++ 4 files changed, 339 insertions(+), 50 deletions(-) create mode 100644 packages/nuxt/src/runtime/utils/event-type-check.ts create mode 100644 packages/nuxt/test/runtime/utils/event-type-check.test.ts diff --git a/packages/nuxt/src/runtime/plugins/index.ts b/packages/nuxt/src/runtime/plugins/index.ts index dbe41b848a0c..b7a5c217edf2 100644 --- a/packages/nuxt/src/runtime/plugins/index.ts +++ b/packages/nuxt/src/runtime/plugins/index.ts @@ -1,2 +1 @@ -// fixme: Can this be exported like this? export { sentryCloudflareNitroPlugin } from './sentry-cloudflare.server'; diff --git a/packages/nuxt/src/runtime/plugins/sentry-cloudflare.server.ts b/packages/nuxt/src/runtime/plugins/sentry-cloudflare.server.ts index 4ddbe8749586..5438ac829d8a 100644 --- a/packages/nuxt/src/runtime/plugins/sentry-cloudflare.server.ts +++ b/packages/nuxt/src/runtime/plugins/sentry-cloudflare.server.ts @@ -1,4 +1,4 @@ -import type { ExecutionContext, IncomingRequestCfProperties } from '@cloudflare/workers-types'; +import type { IncomingRequestCfProperties } from '@cloudflare/workers-types'; import type { CloudflareOptions } from '@sentry/cloudflare'; import { setAsyncLocalStorageAsyncContextStrategy, wrapRequestHandler } from '@sentry/cloudflare'; import { debug, getDefaultIsolationScope, getIsolationScope, getTraceData } from '@sentry/core'; @@ -8,50 +8,7 @@ import type { NuxtRenderHTMLContext } from 'nuxt/app'; import { sentryCaptureErrorHook } from '../hooks/captureErrorHook'; import { updateRouteBeforeResponse } from '../hooks/updateRouteBeforeResponse'; import { addSentryTracingMetaTags } from '../utils'; - -interface CfEventType { - protocol: string; - host: string; - method: string; - headers: Record; - context: { - cf: { - httpProtocol?: string; - country?: string; - // ...other CF properties - }; - cloudflare: { - context: ExecutionContext; - request?: Record; - env?: Record; - }; - }; -} - -function isEventType(event: unknown): event is CfEventType { - if (event === null || typeof event !== 'object') return false; - - return ( - // basic properties - 'protocol' in event && - 'host' in event && - typeof event.protocol === 'string' && - typeof event.host === 'string' && - // context property - 'context' in event && - typeof event.context === 'object' && - event.context !== null && - // context.cf properties - 'cf' in event.context && - typeof event.context.cf === 'object' && - event.context.cf !== null && - // context.cloudflare properties - 'cloudflare' in event.context && - typeof event.context.cloudflare === 'object' && - event.context.cloudflare !== null && - 'context' in event.context.cloudflare - ); -} +import { getCfProperties, getCloudflareProperties, hasCfProperty, isEventType } from '../utils/event-type-check'; /** * Sentry Cloudflare Nitro plugin for when using the "cloudflare-pages" preset in Nuxt. @@ -107,13 +64,13 @@ export const sentryCloudflareNitroPlugin = const request = new Request(url, { method: event.method, headers: event.headers, - cf: event.context.cf, + cf: getCfProperties(event), }) as Request>; const requestHandlerOptions = { options: cloudflareOptions, request, - context: event.context.cloudflare.context, + context: getCloudflareProperties(event).context, }; return wrapRequestHandler(requestHandlerOptions, () => { @@ -124,7 +81,7 @@ export const sentryCloudflareNitroPlugin = const traceData = getTraceData(); if (traceData && Object.keys(traceData).length > 0) { // Storing trace data in the WeakMap using event.context.cf as key for later use in HTML meta-tags - traceDataMap.set(event.context.cf, traceData); + traceDataMap.set(getCfProperties(event), traceData); debug.log('Stored trace data for later use in HTML meta-tags: ', traceData); } @@ -144,7 +101,19 @@ export const sentryCloudflareNitroPlugin = // @ts-expect-error - 'render:html' is a valid hook name in the Nuxt context nitroApp.hooks.hook('render:html', (html: NuxtRenderHTMLContext, { event }: { event: H3Event }) => { - const storedTraceData = event?.context?.cf ? traceDataMap.get(event.context.cf) : undefined; + let storedTraceData: ReturnType | undefined = undefined; + + if ( + event?.context && + '_platform' in event.context && + event.context._platform && + hasCfProperty(event.context._platform) + ) { + storedTraceData = traceDataMap.get(event.context._platform.cf); + } else if (event?.context && hasCfProperty(event.context)) { + // legacy support (before Nitro v2.11.7 (PR: https://github.com/nitrojs/nitro/pull/3224)) + storedTraceData = traceDataMap.get(event.context.cf); + } if (storedTraceData && Object.keys(storedTraceData).length > 0) { debug.log('Using stored trace data for HTML meta-tags: ', storedTraceData); diff --git a/packages/nuxt/src/runtime/utils/event-type-check.ts b/packages/nuxt/src/runtime/utils/event-type-check.ts new file mode 100644 index 000000000000..1da0d8b2677a --- /dev/null +++ b/packages/nuxt/src/runtime/utils/event-type-check.ts @@ -0,0 +1,105 @@ +import type { CfProperties, ExecutionContext } from '@cloudflare/workers-types'; + +interface EventBase { + protocol: string; + host: string; + method: string; + headers: Record; +} + +interface MinimalCloudflareProps { + context: ExecutionContext; + request?: Record; + env?: Record; +} + +interface CloudflareContext { + cf: CfProperties; + cloudflare: MinimalCloudflareProps; +} + +// Direct shape: cf and cloudflare are directly on context +interface CfEventDirect extends EventBase { + context: CloudflareContext; +} + +// Nested shape: cf and cloudflare are under _platform +// Since Nitro v2.11.7 (PR: https://github.com/nitrojs/nitro/pull/3224) +interface CfEventPlatform extends EventBase { + context: { + _platform: CloudflareContext; + }; +} + +export type CfEventType = CfEventDirect | CfEventPlatform; + +function hasCloudflareProperty(context: unknown): boolean { + return ( + context !== null && + typeof context === 'object' && + // context.cloudflare properties + 'cloudflare' in context && + typeof context.cloudflare === 'object' && + context.cloudflare !== null && + 'context' in context.cloudflare + ); +} + +/** + * Type guard to check if an event context object has cf properties + */ +export function hasCfProperty(context: unknown): context is { cf: CfProperties } { + return ( + context !== null && + typeof context === 'object' && + // context.cf properties + 'cf' in context && + typeof context.cf === 'object' && + context.cf !== null + ); +} + +function hasCfAndCloudflare(context: unknown): context is CloudflareContext { + return hasCfProperty(context) && hasCloudflareProperty(context); +} + +/** + * Type guard to check if an event is a Cloudflare event (nested in _platform or direct) + */ +export function isEventType(event: unknown): event is CfEventType { + if (event === null || typeof event !== 'object') return false; + + return ( + // basic properties + 'protocol' in event && + 'host' in event && + typeof event.protocol === 'string' && + typeof event.host === 'string' && + // context property + 'context' in event && + typeof event.context === 'object' && + event.context !== null && + // context.cf properties + (hasCfAndCloudflare(event.context) || ('_platform' in event.context && hasCfAndCloudflare(event.context._platform))) + ); +} + +/** + * Extracts cf properties from a Cloudflare event + */ +export function getCfProperties(event: CfEventType): CfProperties { + if ('cf' in event.context) { + return event.context.cf; + } + return event.context._platform.cf; +} + +/** + * Extracts cloudflare properties from a Cloudflare event + */ +export function getCloudflareProperties(event: CfEventType): MinimalCloudflareProps { + if ('cloudflare' in event.context) { + return event.context.cloudflare; + } + return event.context._platform.cloudflare; +} diff --git a/packages/nuxt/test/runtime/utils/event-type-check.test.ts b/packages/nuxt/test/runtime/utils/event-type-check.test.ts new file mode 100644 index 000000000000..a63de96b193d --- /dev/null +++ b/packages/nuxt/test/runtime/utils/event-type-check.test.ts @@ -0,0 +1,216 @@ +import type { CfProperties } from '@cloudflare/workers-types'; +import { describe, expect, it, vi } from 'vitest'; +import { + type CfEventType, + getCfProperties, + getCloudflareProperties, + isEventType, +} from '../../../src/runtime/utils/event-type-check'; + +describe('event-type-check', () => { + const mockCfProperties: CfProperties = { + colo: 'IMLND', + country: 'IL', + region: 'CoreRegion', + timezone: 'ImagineLand/Core', + city: 'Core', + } as CfProperties; + + const mockCloudflareProperties = { + context: { + waitUntil: vi.fn(), + passThroughOnException: vi.fn(), + props: { key: 'value' }, + }, + request: { url: 'https://example.com' }, + env: { API_KEY: 'test' }, + }; + + const createUnnestedCfEvent = (): CfEventType => ({ + protocol: 'https', + host: 'example.com', + method: 'GET', + headers: { 'user-agent': 'test' }, + context: { + cf: mockCfProperties, + cloudflare: mockCloudflareProperties, + }, + }); + + const createPlatformCfEvent = (): CfEventType => ({ + protocol: 'https', + host: 'example.com', + method: 'POST', + headers: { 'content-type': 'application/json' }, + context: { + _platform: { + cf: mockCfProperties, + cloudflare: mockCloudflareProperties, + }, + }, + }); + + describe('isEventType', () => { + describe('should return true for valid Cloudflare events', () => { + it.each([ + ['direct cf event', createUnnestedCfEvent()], + ['platform cf event', createPlatformCfEvent()], + ])('%s', (_, event) => { + expect(isEventType(event)).toBe(true); + }); + }); + + describe('should return false for invalid inputs', () => { + it.each([ + ['null', null], + ['undefined', undefined], + ['string', 'invalid'], + ['number', 123], + ['boolean', true], + ['array', []], + ['empty object', {}], + ])('%s', (_, input) => { + expect(isEventType(input)).toBe(false); + }); + }); + + describe('should return false for objects missing required properties', () => { + const baseEvent = createUnnestedCfEvent(); + + it.each([ + ['missing protocol', { ...baseEvent, protocol: undefined }], + ['missing host', { ...baseEvent, host: undefined }], + ['missing context', { ...baseEvent, context: undefined }], + ['null context', { ...baseEvent, context: null }], + ['context without cf', { ...baseEvent, context: { cloudflare: mockCloudflareProperties } }], + ['context without cloudflare', { ...baseEvent, context: { cf: mockCfProperties } }], + ['context with null cf', { ...baseEvent, context: { cf: null, cloudflare: mockCloudflareProperties } }], + ['context with null cloudflare', { ...baseEvent, context: { cf: mockCfProperties, cloudflare: null } }], + [ + 'cloudflare without context property', + { + ...baseEvent, + context: { + cf: mockCfProperties, + cloudflare: { request: {}, env: {} }, + }, + }, + ], + ])('%s', (_, invalidEvent) => { + expect(isEventType(invalidEvent)).toBe(false); + }); + }); + + describe('should return false for platform events missing required properties', () => { + const basePlatformEvent = createPlatformCfEvent(); + + it.each([ + [ + 'platform without cf', + { + ...basePlatformEvent, + context: { + _platform: { + cloudflare: mockCloudflareProperties, + }, + }, + }, + ], + [ + 'platform without cloudflare', + { + ...basePlatformEvent, + context: { + _platform: { + cf: mockCfProperties, + }, + }, + }, + ], + [ + 'platform with null cf', + { + ...basePlatformEvent, + context: { + _platform: { + cf: null, + cloudflare: mockCloudflareProperties, + }, + }, + }, + ], + ])('%s', (_, invalidEvent) => { + expect(isEventType(invalidEvent)).toBe(false); + }); + }); + }); + + describe('getCfProperties', () => { + it.each([ + ['direct cf event', createUnnestedCfEvent()], + ['platform cf event', createPlatformCfEvent()], + ])('should extract cf properties from %s', (_, event) => { + const result = getCfProperties(event); + expect(result).toEqual(mockCfProperties); + expect(result.colo).toBe('IMLND'); + expect(result.country).toBe('IL'); + }); + + it('should return the same cf properties for both event types', () => { + const directEvent = createUnnestedCfEvent(); + const platformEvent = createPlatformCfEvent(); + + const directCf = getCfProperties(directEvent); + const platformCf = getCfProperties(platformEvent); + + expect(directCf).toEqual(platformCf); + }); + }); + + describe('getCloudflareProperties', () => { + it.each([ + ['direct cf event', createUnnestedCfEvent()], + ['platform cf event', createPlatformCfEvent()], + ])('should extract cloudflare properties from %s', (_, event) => { + const result = getCloudflareProperties(event); + expect(result).toEqual(mockCloudflareProperties); + expect(result.context).toBeDefined(); + expect(result.request).toEqual({ url: 'https://example.com' }); + expect(result.env).toEqual({ API_KEY: 'test' }); + }); + + it('should return the same cloudflare properties for both event types', () => { + const directEvent = createUnnestedCfEvent(); + const platformEvent = createPlatformCfEvent(); + + const directCloudflare = getCloudflareProperties(directEvent); + const platformCloudflare = getCloudflareProperties(platformEvent); + + expect(directCloudflare).toEqual(platformCloudflare); + }); + }); + + describe('integration tests', () => { + it('should work together for a complete workflow', () => { + const event = createUnnestedCfEvent(); + + expect(isEventType(event)).toBe(true); + + const cf = getCfProperties(event); + const cloudflare = getCloudflareProperties(event); + + expect(cf.country).toBe('IL'); + expect(cloudflare.request?.url).toBe('https://example.com'); + }); + + it('should handle both event structures consistently', () => { + const events = [createUnnestedCfEvent(), createPlatformCfEvent()]; + + events.forEach(event => { + expect(isEventType(event)).toBe(true); + expect(getCfProperties(event)).toEqual(mockCfProperties); + expect(getCloudflareProperties(event)).toEqual(mockCloudflareProperties); + }); + }); + }); +}); From 2cc1ef62e523fdb6a47901e2cf5c1978ba303267 Mon Sep 17 00:00:00 2001 From: Francesco Gringl-Novy Date: Thu, 11 Sep 2025 13:06:13 +0200 Subject: [PATCH 10/61] feat(node): Ensure `prismaIntegration` works with Prisma v5 (#17595) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This PR fixes the usage of the SDK in v10 with Prisma v5. We used to require/recommend that users manually installed `@prisma/instrumentation` v5 and passed this to our `prismaIntegration` like this: ```js const { PrismaInstrumentation } = require('@prisma/instrumentation'); prismaIntegration({ prismaInstrumentation: new PrismaInstrumentation() }); ``` However, this no longer works in v10 because that version pulls in v1 of `@opentelemetry/sdk-trace-base`, which is no longer compatible in v10 of our SDK 😢 So to fix this, this PR removes/deprecates the passing of `prismaInstrumentation` (this now no-ops). v5 is now supported out of the box! To make this work, some hacks and workarounds were necessary 😬 The problem is that v5 of the prisma instrumentation relies on manually creating span instances with specified ID and parent IDs. This is no longer possible in OTEL v2, as all these APIs are no longer exported 😞 (it was also weird from prisma to do this in the first place, and they no longer do this in v6 of the instrumentation). To get this to work, we manually overwrite the `tracer._idGenerator` with a mock that returns the IDs we need, kind of making this work as expected again. Hopefully this will not break in the future, we'll see - our tests should at least show us if that ever breaks. While at this, I also re-wrote our integration tests to run in CJS and ESM properly and use the new test runner structure instead of manually calling yarn etc. in there. Example trace before in v5 (without custom prismaInstrumentation, which broke at runtime): https://sentry-sdks.sentry.io/explore/discover/trace/49e2095162e4bb571074e27653586643/?dataset=transactions&field=title&field=project&field=user.display&field=timestamp&name=All%20Errors&node=span-61995a9d80c6925f&project=4508330866769920&query=&queryDataset=transaction-like&sort=-timestamp&source=discover&statsPeriod=1h×tamp=1757575919&yAxis=count%28%29 Example trace with this PR: https://sentry-sdks.sentry.io/explore/discover/trace/deaaa1990ba6204085779aa09888dc2c/?dataset=transactions&field=title&field=project&field=user.display&field=timestamp&fov=0%2C813.7861328125&name=All%20Errors&node=span-8eb41978be76824e&project=4508330866769920&query=&queryDataset=transaction-like&sort=-timestamp&source=discover&statsPeriod=1h×tamp=1757575780&yAxis=count%28%29 --------- Co-authored-by: Andrei <168741329+andreiborza@users.noreply.github.com> --- CHANGELOG.md | 6 + .../test-applications/node-koa/yarn.lock | 312 --- .../yarn.lock | 2311 ----------------- .../react-router-7-framework-spa/yarn.lock | 2311 ----------------- .../node-integration-tests/package.json | 2 + .../tracing/knex/mysql2/docker-compose.yml | 12 + .../instrument.mjs} | 0 .../scenario.mjs} | 0 .../suites/tracing/knex/mysql2/test.ts | 73 + .../tracing/knex/{ => pg}/docker-compose.yml | 10 - .../instrument.mjs} | 0 .../{scenario-withPg.mjs => pg/scenario.mjs} | 0 .../suites/tracing/knex/pg/test.ts | 71 + .../suites/tracing/knex/test.ts | 137 - .../tracing/prisma-orm-v5/instrument.mjs | 9 + .../suites/tracing/prisma-orm-v5/package.json | 21 - .../{scenario.js => scenario.mjs} | 16 +- .../suites/tracing/prisma-orm-v5/test.ts | 196 +- .../suites/tracing/prisma-orm-v5/yarn.lock | 58 - .../tracing/prisma-orm-v6/instrument.mjs | 9 + .../suites/tracing/prisma-orm-v6/package.json | 21 - .../{scenario.js => scenario.mjs} | 16 +- .../suites/tracing/prisma-orm-v6/test.ts | 162 +- .../suites/tracing/prisma-orm-v6/yarn.lock | 58 - .../node-integration-tests/utils/runner.ts | 43 +- .../node/src/integrations/tracing/prisma.ts | 216 +- yarn.lock | 148 +- 27 files changed, 709 insertions(+), 5509 deletions(-) delete mode 100644 dev-packages/e2e-tests/test-applications/node-koa/yarn.lock delete mode 100644 dev-packages/e2e-tests/test-applications/react-router-7-framework-spa-node-20-18/yarn.lock delete mode 100644 dev-packages/e2e-tests/test-applications/react-router-7-framework-spa/yarn.lock create mode 100644 dev-packages/node-integration-tests/suites/tracing/knex/mysql2/docker-compose.yml rename dev-packages/node-integration-tests/suites/tracing/knex/{instrument-withMysql2.mjs => mysql2/instrument.mjs} (100%) rename dev-packages/node-integration-tests/suites/tracing/knex/{scenario-withMysql2.mjs => mysql2/scenario.mjs} (100%) create mode 100644 dev-packages/node-integration-tests/suites/tracing/knex/mysql2/test.ts rename dev-packages/node-integration-tests/suites/tracing/knex/{ => pg}/docker-compose.yml (55%) rename dev-packages/node-integration-tests/suites/tracing/knex/{instrument-withPg.mjs => pg/instrument.mjs} (100%) rename dev-packages/node-integration-tests/suites/tracing/knex/{scenario-withPg.mjs => pg/scenario.mjs} (100%) create mode 100644 dev-packages/node-integration-tests/suites/tracing/knex/pg/test.ts delete mode 100644 dev-packages/node-integration-tests/suites/tracing/knex/test.ts create mode 100644 dev-packages/node-integration-tests/suites/tracing/prisma-orm-v5/instrument.mjs delete mode 100644 dev-packages/node-integration-tests/suites/tracing/prisma-orm-v5/package.json rename dev-packages/node-integration-tests/suites/tracing/prisma-orm-v5/{scenario.js => scenario.mjs} (64%) delete mode 100644 dev-packages/node-integration-tests/suites/tracing/prisma-orm-v5/yarn.lock create mode 100644 dev-packages/node-integration-tests/suites/tracing/prisma-orm-v6/instrument.mjs delete mode 100644 dev-packages/node-integration-tests/suites/tracing/prisma-orm-v6/package.json rename dev-packages/node-integration-tests/suites/tracing/prisma-orm-v6/{scenario.js => scenario.mjs} (64%) delete mode 100644 dev-packages/node-integration-tests/suites/tracing/prisma-orm-v6/yarn.lock diff --git a/CHANGELOG.md b/CHANGELOG.md index 832c152a0a1b..ea474e56355d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,12 @@ ### Important Changes +- **feat(node) Ensure `prismaIntegration` works with Prisma 5 ([#17595](https://github.com/getsentry/sentry-javascript/pull/17595))** + +We used to require to pass in the v5 version of `@prisma/instrumentation` into `prismaIntegration({ prismaInstrumentation: new PrismaInstrumentation() })`, if you wanted to get full instrumentation for Prisma v5. However, it turns out this does not work on v10 of the SDK anymore, because `@prisma/instrumentation@5` requires OTEL v1. + +With this release, we dropped the requirement to configure anything to get v5 support of Prisma. You do not need to configure anything in the integration anymore, and can remove the dependency on `@prisma/instrumentation@5` if you had it in your application. You only need to configure the `tracing` preview feature [according to our docs](https://docs.sentry.io/platforms/javascript/guides/node/configuration/integrations/prisma/). + - **feat(deps): Update OpenTelemetry dependencies ([#17558](https://github.com/getsentry/sentry-javascript/pull/17558))** - @opentelemetry/core bumped to ^2.1.0 - @opentelemetry/context-async-hooks bumped to ^2.1.0 diff --git a/dev-packages/e2e-tests/test-applications/node-koa/yarn.lock b/dev-packages/e2e-tests/test-applications/node-koa/yarn.lock deleted file mode 100644 index 201176ad5cdf..000000000000 --- a/dev-packages/e2e-tests/test-applications/node-koa/yarn.lock +++ /dev/null @@ -1,312 +0,0 @@ -# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. -# yarn lockfile v1 - - -"@koa/router@^12.0.1": - version "12.0.1" - resolved "https://registry.yarnpkg.com/@koa/router/-/router-12.0.1.tgz#1a66f92a630c02832cf5bbf0db06c9e53e423468" - integrity sha512-ribfPYfHb+Uw3b27Eiw6NPqjhIhTpVFzEWLwyc/1Xp+DCdwRRyIlAUODX+9bPARF6aQtUu1+/PHzdNvRzcs/+Q== - dependencies: - debug "^4.3.4" - http-errors "^2.0.0" - koa-compose "^4.1.0" - methods "^1.1.2" - path-to-regexp "^6.2.1" - -accepts@^1.3.5: - version "1.3.8" - resolved "https://registry.yarnpkg.com/accepts/-/accepts-1.3.8.tgz#0bf0be125b67014adcb0b0921e62db7bffe16b2e" - integrity sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw== - dependencies: - mime-types "~2.1.34" - negotiator "0.6.3" - -cache-content-type@^1.0.0: - version "1.0.1" - resolved "https://registry.yarnpkg.com/cache-content-type/-/cache-content-type-1.0.1.tgz#035cde2b08ee2129f4a8315ea8f00a00dba1453c" - integrity sha512-IKufZ1o4Ut42YUrZSo8+qnMTrFuKkvyoLXUywKz9GJ5BrhOFGhLdkx9sG4KAnVvbY6kEcSFjLQul+DVmBm2bgA== - dependencies: - mime-types "^2.1.18" - ylru "^1.2.0" - -co@^4.6.0: - version "4.6.0" - resolved "https://registry.yarnpkg.com/co/-/co-4.6.0.tgz#6ea6bdf3d853ae54ccb8e47bfa0bf3f9031fb184" - integrity sha512-QVb0dM5HvG+uaxitm8wONl7jltx8dqhfU33DcqtOZcLSVIKSDDLDi7+0LbAKiyI8hD9u42m2YxXSkMGWThaecQ== - -content-disposition@~0.5.2: - version "0.5.4" - resolved "https://registry.yarnpkg.com/content-disposition/-/content-disposition-0.5.4.tgz#8b82b4efac82512a02bb0b1dcec9d2c5e8eb5bfe" - integrity sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ== - dependencies: - safe-buffer "5.2.1" - -content-type@^1.0.4: - version "1.0.5" - resolved "https://registry.yarnpkg.com/content-type/-/content-type-1.0.5.tgz#8b773162656d1d1086784c8f23a54ce6d73d7918" - integrity sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA== - -cookies@~0.9.0: - version "0.9.1" - resolved "https://registry.yarnpkg.com/cookies/-/cookies-0.9.1.tgz#3ffed6f60bb4fb5f146feeedba50acc418af67e3" - integrity sha512-TG2hpqe4ELx54QER/S3HQ9SRVnQnGBtKUz5bLQWtYAQ+o6GpgMs6sYUvaiJjVxb+UXwhRhAEP3m7LbsIZ77Hmw== - dependencies: - depd "~2.0.0" - keygrip "~1.1.0" - -debug@^4.3.2, debug@^4.3.4: - version "4.3.4" - resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.4.tgz#1319f6579357f2338d3337d2cdd4914bb5dcc865" - integrity sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ== - dependencies: - ms "2.1.2" - -deep-equal@~1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/deep-equal/-/deep-equal-1.0.1.tgz#f5d260292b660e084eff4cdbc9f08ad3247448b5" - integrity sha512-bHtC0iYvWhyaTzvV3CZgPeZQqCOBGyGsVV7v4eevpdkLHfiSrXUdBG+qAuSz4RI70sszvjQ1QSZ98An1yNwpSw== - -delegates@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/delegates/-/delegates-1.0.0.tgz#84c6e159b81904fdca59a0ef44cd870d31250f9a" - integrity sha512-bd2L678uiWATM6m5Z1VzNCErI3jiGzt6HGY8OVICs40JQq/HALfbyNJmp0UDakEY4pMMaN0Ly5om/B1VI/+xfQ== - -depd@2.0.0, depd@^2.0.0, depd@~2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/depd/-/depd-2.0.0.tgz#b696163cc757560d09cf22cc8fad1571b79e76df" - integrity sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw== - -depd@~1.1.2: - version "1.1.2" - resolved "https://registry.yarnpkg.com/depd/-/depd-1.1.2.tgz#9bcd52e14c097763e749b274c4346ed2e560b5a9" - integrity sha512-7emPTl6Dpo6JRXOXjLRxck+FlLRX5847cLKEn00PLAgc3g2hTZZgr+e4c2v6QpSmLeFP3n5yUo7ft6avBK/5jQ== - -destroy@^1.0.4: - version "1.2.0" - resolved "https://registry.yarnpkg.com/destroy/-/destroy-1.2.0.tgz#4803735509ad8be552934c67df614f94e66fa015" - integrity sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg== - -ee-first@1.1.1: - version "1.1.1" - resolved "https://registry.yarnpkg.com/ee-first/-/ee-first-1.1.1.tgz#590c61156b0ae2f4f0255732a158b266bc56b21d" - integrity sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow== - -encodeurl@^1.0.2: - version "1.0.2" - resolved "https://registry.yarnpkg.com/encodeurl/-/encodeurl-1.0.2.tgz#ad3ff4c86ec2d029322f5a02c3a9a606c95b3f59" - integrity sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w== - -escape-html@^1.0.3: - version "1.0.3" - resolved "https://registry.yarnpkg.com/escape-html/-/escape-html-1.0.3.tgz#0258eae4d3d0c0974de1c169188ef0051d1d1988" - integrity sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow== - -fresh@~0.5.2: - version "0.5.2" - resolved "https://registry.yarnpkg.com/fresh/-/fresh-0.5.2.tgz#3d8cadd90d976569fa835ab1f8e4b23a105605a7" - integrity sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q== - -has-symbols@^1.0.3: - version "1.0.3" - resolved "https://registry.yarnpkg.com/has-symbols/-/has-symbols-1.0.3.tgz#bb7b2c4349251dce87b125f7bdf874aa7c8b39f8" - integrity sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A== - -has-tostringtag@^1.0.0: - version "1.0.2" - resolved "https://registry.yarnpkg.com/has-tostringtag/-/has-tostringtag-1.0.2.tgz#2cdc42d40bef2e5b4eeab7c01a73c54ce7ab5abc" - integrity sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw== - dependencies: - has-symbols "^1.0.3" - -http-assert@^1.3.0: - version "1.5.0" - resolved "https://registry.yarnpkg.com/http-assert/-/http-assert-1.5.0.tgz#c389ccd87ac16ed2dfa6246fd73b926aa00e6b8f" - integrity sha512-uPpH7OKX4H25hBmU6G1jWNaqJGpTXxey+YOUizJUAgu0AjLUeC8D73hTrhvDS5D+GJN1DN1+hhc/eF/wpxtp0w== - dependencies: - deep-equal "~1.0.1" - http-errors "~1.8.0" - -http-errors@^1.6.3, http-errors@~1.8.0: - version "1.8.1" - resolved "https://registry.yarnpkg.com/http-errors/-/http-errors-1.8.1.tgz#7c3f28577cbc8a207388455dbd62295ed07bd68c" - integrity sha512-Kpk9Sm7NmI+RHhnj6OIWDI1d6fIoFAtFt9RLaTMRlg/8w49juAStsrBgp0Dp4OdxdVbRIeKhtCUvoi/RuAhO4g== - dependencies: - depd "~1.1.2" - inherits "2.0.4" - setprototypeof "1.2.0" - statuses ">= 1.5.0 < 2" - toidentifier "1.0.1" - -http-errors@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/http-errors/-/http-errors-2.0.0.tgz#b7774a1486ef73cf7667ac9ae0858c012c57b9d3" - integrity sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ== - dependencies: - depd "2.0.0" - inherits "2.0.4" - setprototypeof "1.2.0" - statuses "2.0.1" - toidentifier "1.0.1" - -inherits@2.0.4: - version "2.0.4" - resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.4.tgz#0fa2c64f932917c3433a0ded55363aae37416b7c" - integrity sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ== - -is-generator-function@^1.0.7: - version "1.0.10" - resolved "https://registry.yarnpkg.com/is-generator-function/-/is-generator-function-1.0.10.tgz#f1558baf1ac17e0deea7c0415c438351ff2b3c72" - integrity sha512-jsEjy9l3yiXEQ+PsXdmBwEPcOxaXWLspKdplFUVI9vq1iZgIekeC0L167qeu86czQaxed3q/Uzuw0swL0irL8A== - dependencies: - has-tostringtag "^1.0.0" - -keygrip@~1.1.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/keygrip/-/keygrip-1.1.0.tgz#871b1681d5e159c62a445b0c74b615e0917e7226" - integrity sha512-iYSchDJ+liQ8iwbSI2QqsQOvqv58eJCEanyJPJi+Khyu8smkcKSFUCbPwzFcL7YVtZ6eONjqRX/38caJ7QjRAQ== - dependencies: - tsscmp "1.0.6" - -koa-compose@^4.1.0: - version "4.1.0" - resolved "https://registry.yarnpkg.com/koa-compose/-/koa-compose-4.1.0.tgz#507306b9371901db41121c812e923d0d67d3e877" - integrity sha512-8ODW8TrDuMYvXRwra/Kh7/rJo9BtOfPc6qO8eAfC80CnCvSjSl0bkRM24X6/XBBEyj0v1nRUQ1LyOy3dbqOWXw== - -koa-convert@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/koa-convert/-/koa-convert-2.0.0.tgz#86a0c44d81d40551bae22fee6709904573eea4f5" - integrity sha512-asOvN6bFlSnxewce2e/DK3p4tltyfC4VM7ZwuTuepI7dEQVcvpyFuBcEARu1+Hxg8DIwytce2n7jrZtRlPrARA== - dependencies: - co "^4.6.0" - koa-compose "^4.1.0" - -koa@^2.15.2: - version "2.15.2" - resolved "https://registry.yarnpkg.com/koa/-/koa-2.15.2.tgz#1e4afe1482d01bd24ed6e30f630a960411f5ebf2" - integrity sha512-MXTeZH3M6AJ8ukW2QZ8wqO3Dcdfh2WRRmjCBkEP+NhKNCiqlO5RDqHmSnsyNrbRJrdjyvIGSJho4vQiWgQJSVA== - dependencies: - accepts "^1.3.5" - cache-content-type "^1.0.0" - content-disposition "~0.5.2" - content-type "^1.0.4" - cookies "~0.9.0" - debug "^4.3.2" - delegates "^1.0.0" - depd "^2.0.0" - destroy "^1.0.4" - encodeurl "^1.0.2" - escape-html "^1.0.3" - fresh "~0.5.2" - http-assert "^1.3.0" - http-errors "^1.6.3" - is-generator-function "^1.0.7" - koa-compose "^4.1.0" - koa-convert "^2.0.0" - on-finished "^2.3.0" - only "~0.0.2" - parseurl "^1.3.2" - statuses "^1.5.0" - type-is "^1.6.16" - vary "^1.1.2" - -media-typer@0.3.0: - version "0.3.0" - resolved "https://registry.yarnpkg.com/media-typer/-/media-typer-0.3.0.tgz#8710d7af0aa626f8fffa1ce00168545263255748" - integrity sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ== - -methods@^1.1.2: - version "1.1.2" - resolved "https://registry.yarnpkg.com/methods/-/methods-1.1.2.tgz#5529a4d67654134edcc5266656835b0f851afcee" - integrity sha512-iclAHeNqNm68zFtnZ0e+1L2yUIdvzNoauKU4WBA3VvH/vPFieF7qfRlwUZU+DA9P9bPXIS90ulxoUoCH23sV2w== - -mime-db@1.52.0: - version "1.52.0" - resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.52.0.tgz#bbabcdc02859f4987301c856e3387ce5ec43bf70" - integrity sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg== - -mime-types@^2.1.18, mime-types@~2.1.24, mime-types@~2.1.34: - version "2.1.35" - resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-2.1.35.tgz#381a871b62a734450660ae3deee44813f70d959a" - integrity sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw== - dependencies: - mime-db "1.52.0" - -ms@2.1.2: - version "2.1.2" - resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.2.tgz#d09d1f357b443f493382a8eb3ccd183872ae6009" - integrity sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w== - -negotiator@0.6.3: - version "0.6.3" - resolved "https://registry.yarnpkg.com/negotiator/-/negotiator-0.6.3.tgz#58e323a72fedc0d6f9cd4d31fe49f51479590ccd" - integrity sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg== - -on-finished@^2.3.0: - version "2.4.1" - resolved "https://registry.yarnpkg.com/on-finished/-/on-finished-2.4.1.tgz#58c8c44116e54845ad57f14ab10b03533184ac3f" - integrity sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg== - dependencies: - ee-first "1.1.1" - -only@~0.0.2: - version "0.0.2" - resolved "https://registry.yarnpkg.com/only/-/only-0.0.2.tgz#2afde84d03e50b9a8edc444e30610a70295edfb4" - integrity sha512-Fvw+Jemq5fjjyWz6CpKx6w9s7xxqo3+JCyM0WXWeCSOboZ8ABkyvP8ID4CZuChA/wxSx+XSJmdOm8rGVyJ1hdQ== - -parseurl@^1.3.2: - version "1.3.3" - resolved "https://registry.yarnpkg.com/parseurl/-/parseurl-1.3.3.tgz#9da19e7bee8d12dff0513ed5b76957793bc2e8d4" - integrity sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ== - -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" - integrity sha512-JLyh7xT1kizaEvcaXOQwOc2/Yhw6KZOvPf1S8401UyLk86CU79LN3vl7ztXGm/pZ+YjoyAJ4rxmHwbkBXJX+yw== - -safe-buffer@5.2.1: - version "5.2.1" - resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.2.1.tgz#1eaf9fa9bdb1fdd4ec75f58f9cdb4e6b7827eec6" - integrity sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ== - -setprototypeof@1.2.0: - version "1.2.0" - resolved "https://registry.yarnpkg.com/setprototypeof/-/setprototypeof-1.2.0.tgz#66c9a24a73f9fc28cbe66b09fed3d33dcaf1b424" - integrity sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw== - -statuses@2.0.1: - version "2.0.1" - resolved "https://registry.yarnpkg.com/statuses/-/statuses-2.0.1.tgz#55cb000ccf1d48728bd23c685a063998cf1a1b63" - integrity sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ== - -"statuses@>= 1.5.0 < 2", statuses@^1.5.0: - version "1.5.0" - resolved "https://registry.yarnpkg.com/statuses/-/statuses-1.5.0.tgz#161c7dac177659fd9811f43771fa99381478628c" - integrity sha512-OpZ3zP+jT1PI7I8nemJX4AKmAX070ZkYPVWV/AaKTJl+tXCTGyVdC1a4SL8RUQYEwk/f34ZX8UTykN68FwrqAA== - -toidentifier@1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/toidentifier/-/toidentifier-1.0.1.tgz#3be34321a88a820ed1bd80dfaa33e479fbb8dd35" - integrity sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA== - -tsscmp@1.0.6: - version "1.0.6" - resolved "https://registry.yarnpkg.com/tsscmp/-/tsscmp-1.0.6.tgz#85b99583ac3589ec4bfef825b5000aa911d605eb" - integrity sha512-LxhtAkPDTkVCMQjt2h6eBVY28KCjikZqZfMcC15YBeNjkgUpdCfBu5HoiOTDu86v6smE8yOjyEktJ8hlbANHQA== - -type-is@^1.6.16: - version "1.6.18" - resolved "https://registry.yarnpkg.com/type-is/-/type-is-1.6.18.tgz#4e552cd05df09467dcbc4ef739de89f2cf37c131" - integrity sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g== - dependencies: - media-typer "0.3.0" - mime-types "~2.1.24" - -vary@^1.1.2: - version "1.1.2" - resolved "https://registry.yarnpkg.com/vary/-/vary-1.1.2.tgz#2299f02c6ded30d4a5961b0b9f74524a18f634fc" - integrity sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg== - -ylru@^1.2.0: - version "1.4.0" - resolved "https://registry.yarnpkg.com/ylru/-/ylru-1.4.0.tgz#0cf0aa57e9c24f8a2cbde0cc1ca2c9592ac4e0f6" - integrity sha512-2OQsPNEmBCvXuFlIni/a+Rn+R2pHW9INm0BxXJ4hVDA8TirqMj+J/Rp9ItLatT/5pZqWwefVrTQcHpixsxnVlA== diff --git a/dev-packages/e2e-tests/test-applications/react-router-7-framework-spa-node-20-18/yarn.lock b/dev-packages/e2e-tests/test-applications/react-router-7-framework-spa-node-20-18/yarn.lock deleted file mode 100644 index 43736103138f..000000000000 --- a/dev-packages/e2e-tests/test-applications/react-router-7-framework-spa-node-20-18/yarn.lock +++ /dev/null @@ -1,2311 +0,0 @@ -# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. -# yarn lockfile v1 - - -"@ampproject/remapping@^2.2.0", "@ampproject/remapping@^2.3.0": - version "2.3.0" - resolved "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.3.0.tgz#ed441b6fa600072520ce18b43d2c8cc8caecc7f4" - integrity sha512-30iZtAPgz+LTIYoeivqYo853f02jBYSd5uGnGpkFV0M3xOt9aN73erkgYAmZU43x4VfqcnLxW9Kpg3R5LC4YYw== - dependencies: - "@jridgewell/gen-mapping" "^0.3.5" - "@jridgewell/trace-mapping" "^0.3.24" - -"@babel/code-frame@^7.27.1": - version "7.27.1" - resolved "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.27.1.tgz#200f715e66d52a23b221a9435534a91cc13ad5be" - integrity sha512-cjQ7ZlQ0Mv3b47hABuTevyTuYN4i+loJKGeV9flcCgIK37cCXRh+L1bd3iBHlynerhQ7BhCkn2BPbQUL+rGqFg== - dependencies: - "@babel/helper-validator-identifier" "^7.27.1" - js-tokens "^4.0.0" - picocolors "^1.1.1" - -"@babel/compat-data@^7.27.2": - version "7.27.2" - resolved "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.27.2.tgz#4183f9e642fd84e74e3eea7ffa93a412e3b102c9" - integrity sha512-TUtMJYRPyUb/9aU8f3K0mjmjf6M9N5Woshn2CS6nqJSeJtTtQcpLUXjGt9vbF8ZGff0El99sWkLgzwW3VXnxZQ== - -"@babel/core@^7.21.8", "@babel/core@^7.23.7": - version "7.27.1" - resolved "https://registry.npmjs.org/@babel/core/-/core-7.27.1.tgz#89de51e86bd12246003e3524704c49541b16c3e6" - integrity sha512-IaaGWsQqfsQWVLqMn9OB92MNN7zukfVA4s7KKAI0KfrrDsZ0yhi5uV4baBuLuN7n3vsZpwP8asPPcVwApxvjBQ== - dependencies: - "@ampproject/remapping" "^2.2.0" - "@babel/code-frame" "^7.27.1" - "@babel/generator" "^7.27.1" - "@babel/helper-compilation-targets" "^7.27.1" - "@babel/helper-module-transforms" "^7.27.1" - "@babel/helpers" "^7.27.1" - "@babel/parser" "^7.27.1" - "@babel/template" "^7.27.1" - "@babel/traverse" "^7.27.1" - "@babel/types" "^7.27.1" - convert-source-map "^2.0.0" - debug "^4.1.0" - gensync "^1.0.0-beta.2" - json5 "^2.2.3" - semver "^6.3.1" - -"@babel/generator@^7.21.5", "@babel/generator@^7.27.1": - version "7.27.1" - resolved "https://registry.npmjs.org/@babel/generator/-/generator-7.27.1.tgz#862d4fad858f7208edd487c28b58144036b76230" - integrity sha512-UnJfnIpc/+JO0/+KRVQNGU+y5taA5vCbwN8+azkX6beii/ZF+enZJSOKo11ZSzGJjlNfJHfQtmQT8H+9TXPG2w== - dependencies: - "@babel/parser" "^7.27.1" - "@babel/types" "^7.27.1" - "@jridgewell/gen-mapping" "^0.3.5" - "@jridgewell/trace-mapping" "^0.3.25" - jsesc "^3.0.2" - -"@babel/helper-annotate-as-pure@^7.27.1": - version "7.27.1" - resolved "https://registry.npmjs.org/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.27.1.tgz#4345d81a9a46a6486e24d069469f13e60445c05d" - integrity sha512-WnuuDILl9oOBbKnb4L+DyODx7iC47XfzmNCpTttFsSp6hTG7XZxu60+4IO+2/hPfcGOoKbFiwoI/+zwARbNQow== - dependencies: - "@babel/types" "^7.27.1" - -"@babel/helper-compilation-targets@^7.27.1": - version "7.27.2" - resolved "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.27.2.tgz#46a0f6efab808d51d29ce96858dd10ce8732733d" - integrity sha512-2+1thGUUWWjLTYTHZWK1n8Yga0ijBz1XAhUXcKy81rd5g6yh7hGqMp45v7cadSbEHc9G3OTv45SyneRN3ps4DQ== - dependencies: - "@babel/compat-data" "^7.27.2" - "@babel/helper-validator-option" "^7.27.1" - browserslist "^4.24.0" - lru-cache "^5.1.1" - semver "^6.3.1" - -"@babel/helper-create-class-features-plugin@^7.27.1": - version "7.27.1" - resolved "https://registry.npmjs.org/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.27.1.tgz#5bee4262a6ea5ddc852d0806199eb17ca3de9281" - integrity sha512-QwGAmuvM17btKU5VqXfb+Giw4JcN0hjuufz3DYnpeVDvZLAObloM77bhMXiqry3Iio+Ai4phVRDwl6WU10+r5A== - dependencies: - "@babel/helper-annotate-as-pure" "^7.27.1" - "@babel/helper-member-expression-to-functions" "^7.27.1" - "@babel/helper-optimise-call-expression" "^7.27.1" - "@babel/helper-replace-supers" "^7.27.1" - "@babel/helper-skip-transparent-expression-wrappers" "^7.27.1" - "@babel/traverse" "^7.27.1" - semver "^6.3.1" - -"@babel/helper-member-expression-to-functions@^7.27.1": - version "7.27.1" - resolved "https://registry.npmjs.org/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.27.1.tgz#ea1211276be93e798ce19037da6f06fbb994fa44" - integrity sha512-E5chM8eWjTp/aNoVpcbfM7mLxu9XGLWYise2eBKGQomAk/Mb4XoxyqXTZbuTohbsl8EKqdlMhnDI2CCLfcs9wA== - dependencies: - "@babel/traverse" "^7.27.1" - "@babel/types" "^7.27.1" - -"@babel/helper-module-imports@^7.27.1": - version "7.27.1" - resolved "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.27.1.tgz#7ef769a323e2655e126673bb6d2d6913bbead204" - integrity sha512-0gSFWUPNXNopqtIPQvlD5WgXYI5GY2kP2cCvoT8kczjbfcfuIljTbcWrulD1CIPIX2gt1wghbDy08yE1p+/r3w== - dependencies: - "@babel/traverse" "^7.27.1" - "@babel/types" "^7.27.1" - -"@babel/helper-module-transforms@^7.27.1": - version "7.27.1" - resolved "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.27.1.tgz#e1663b8b71d2de948da5c4fb2a20ca4f3ec27a6f" - integrity sha512-9yHn519/8KvTU5BjTVEEeIM3w9/2yXNKoD82JifINImhpKkARMJKPP59kLo+BafpdN5zgNeIcS4jsGDmd3l58g== - dependencies: - "@babel/helper-module-imports" "^7.27.1" - "@babel/helper-validator-identifier" "^7.27.1" - "@babel/traverse" "^7.27.1" - -"@babel/helper-optimise-call-expression@^7.27.1": - version "7.27.1" - resolved "https://registry.npmjs.org/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.27.1.tgz#c65221b61a643f3e62705e5dd2b5f115e35f9200" - integrity sha512-URMGH08NzYFhubNSGJrpUEphGKQwMQYBySzat5cAByY1/YgIRkULnIy3tAMeszlL/so2HbeilYloUmSpd7GdVw== - dependencies: - "@babel/types" "^7.27.1" - -"@babel/helper-plugin-utils@^7.27.1": - version "7.27.1" - resolved "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.27.1.tgz#ddb2f876534ff8013e6c2b299bf4d39b3c51d44c" - integrity sha512-1gn1Up5YXka3YYAHGKpbideQ5Yjf1tDa9qYcgysz+cNCXukyLl6DjPXhD3VRwSb8c0J9tA4b2+rHEZtc6R0tlw== - -"@babel/helper-replace-supers@^7.27.1": - version "7.27.1" - resolved "https://registry.npmjs.org/@babel/helper-replace-supers/-/helper-replace-supers-7.27.1.tgz#b1ed2d634ce3bdb730e4b52de30f8cccfd692bc0" - integrity sha512-7EHz6qDZc8RYS5ElPoShMheWvEgERonFCs7IAonWLLUTXW59DP14bCZt89/GKyreYn8g3S83m21FelHKbeDCKA== - dependencies: - "@babel/helper-member-expression-to-functions" "^7.27.1" - "@babel/helper-optimise-call-expression" "^7.27.1" - "@babel/traverse" "^7.27.1" - -"@babel/helper-skip-transparent-expression-wrappers@^7.27.1": - version "7.27.1" - resolved "https://registry.npmjs.org/@babel/helper-skip-transparent-expression-wrappers/-/helper-skip-transparent-expression-wrappers-7.27.1.tgz#62bb91b3abba8c7f1fec0252d9dbea11b3ee7a56" - integrity sha512-Tub4ZKEXqbPjXgWLl2+3JpQAYBJ8+ikpQ2Ocj/q/r0LwE3UhENh7EUabyHjz2kCEsrRY83ew2DQdHluuiDQFzg== - dependencies: - "@babel/traverse" "^7.27.1" - "@babel/types" "^7.27.1" - -"@babel/helper-string-parser@^7.27.1": - version "7.27.1" - resolved "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.27.1.tgz#54da796097ab19ce67ed9f88b47bb2ec49367687" - integrity sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA== - -"@babel/helper-validator-identifier@^7.27.1": - version "7.27.1" - resolved "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.27.1.tgz#a7054dcc145a967dd4dc8fee845a57c1316c9df8" - integrity sha512-D2hP9eA+Sqx1kBZgzxZh0y1trbuU+JoDkiEwqhQ36nodYqJwyEIhPSdMNd7lOm/4io72luTPWH20Yda0xOuUow== - -"@babel/helper-validator-option@^7.27.1": - version "7.27.1" - resolved "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.27.1.tgz#fa52f5b1e7db1ab049445b421c4471303897702f" - integrity sha512-YvjJow9FxbhFFKDSuFnVCe2WxXk1zWc22fFePVNEaWJEu8IrZVlda6N0uHwzZrUM1il7NC9Mlp4MaJYbYd9JSg== - -"@babel/helpers@^7.27.1": - version "7.27.1" - resolved "https://registry.npmjs.org/@babel/helpers/-/helpers-7.27.1.tgz#ffc27013038607cdba3288e692c3611c06a18aa4" - integrity sha512-FCvFTm0sWV8Fxhpp2McP5/W53GPllQ9QeQ7SiqGWjMf/LVG07lFa5+pgK05IRhVwtvafT22KF+ZSnM9I545CvQ== - dependencies: - "@babel/template" "^7.27.1" - "@babel/types" "^7.27.1" - -"@babel/parser@^7.21.8", "@babel/parser@^7.23.6", "@babel/parser@^7.27.1", "@babel/parser@^7.27.2": - version "7.27.2" - resolved "https://registry.npmjs.org/@babel/parser/-/parser-7.27.2.tgz#577518bedb17a2ce4212afd052e01f7df0941127" - integrity sha512-QYLs8299NA7WM/bZAdp+CviYYkVoYXlDW2rzliy3chxd1PQjej7JORuMJDJXJUb9g0TT+B99EwaVLKmX+sPXWw== - dependencies: - "@babel/types" "^7.27.1" - -"@babel/plugin-syntax-decorators@^7.22.10": - version "7.27.1" - resolved "https://registry.npmjs.org/@babel/plugin-syntax-decorators/-/plugin-syntax-decorators-7.27.1.tgz#ee7dd9590aeebc05f9d4c8c0560007b05979a63d" - integrity sha512-YMq8Z87Lhl8EGkmb0MwYkt36QnxC+fzCgrl66ereamPlYToRpIk5nUjKUY3QKLWq8mwUB1BgbeXcTJhZOCDg5A== - dependencies: - "@babel/helper-plugin-utils" "^7.27.1" - -"@babel/plugin-syntax-jsx@^7.21.4", "@babel/plugin-syntax-jsx@^7.27.1": - version "7.27.1" - resolved "https://registry.npmjs.org/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.27.1.tgz#2f9beb5eff30fa507c5532d107daac7b888fa34c" - integrity sha512-y8YTNIeKoyhGd9O0Jiyzyyqk8gdjnumGTQPsz0xOZOQ2RmkVJeZ1vmmfIvFEKqucBG6axJGBZDE/7iI5suUI/w== - dependencies: - "@babel/helper-plugin-utils" "^7.27.1" - -"@babel/plugin-syntax-typescript@^7.27.1": - version "7.27.1" - resolved "https://registry.npmjs.org/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.27.1.tgz#5147d29066a793450f220c63fa3a9431b7e6dd18" - integrity sha512-xfYCBMxveHrRMnAWl1ZlPXOZjzkN82THFvLhQhFXFt81Z5HnN+EtUkZhv/zcKpmT3fzmWZB0ywiBrbC3vogbwQ== - dependencies: - "@babel/helper-plugin-utils" "^7.27.1" - -"@babel/plugin-transform-modules-commonjs@^7.27.1": - version "7.27.1" - resolved "https://registry.npmjs.org/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.27.1.tgz#8e44ed37c2787ecc23bdc367f49977476614e832" - integrity sha512-OJguuwlTYlN0gBZFRPqwOGNWssZjfIUdS7HMYtN8c1KmwpwHFBwTeFZrg9XZa+DFTitWOW5iTAG7tyCUPsCCyw== - dependencies: - "@babel/helper-module-transforms" "^7.27.1" - "@babel/helper-plugin-utils" "^7.27.1" - -"@babel/plugin-transform-typescript@^7.27.1": - version "7.27.1" - resolved "https://registry.npmjs.org/@babel/plugin-transform-typescript/-/plugin-transform-typescript-7.27.1.tgz#d3bb65598bece03f773111e88cc4e8e5070f1140" - integrity sha512-Q5sT5+O4QUebHdbwKedFBEwRLb02zJ7r4A5Gg2hUoLuU3FjdMcyqcywqUrLCaDsFCxzokf7u9kuy7qz51YUuAg== - dependencies: - "@babel/helper-annotate-as-pure" "^7.27.1" - "@babel/helper-create-class-features-plugin" "^7.27.1" - "@babel/helper-plugin-utils" "^7.27.1" - "@babel/helper-skip-transparent-expression-wrappers" "^7.27.1" - "@babel/plugin-syntax-typescript" "^7.27.1" - -"@babel/preset-typescript@^7.21.5": - version "7.27.1" - resolved "https://registry.npmjs.org/@babel/preset-typescript/-/preset-typescript-7.27.1.tgz#190742a6428d282306648a55b0529b561484f912" - integrity sha512-l7WfQfX0WK4M0v2RudjuQK4u99BS6yLHYEmdtVPP7lKV013zr9DygFuWNlnbvQ9LR+LS0Egz/XAvGx5U9MX0fQ== - dependencies: - "@babel/helper-plugin-utils" "^7.27.1" - "@babel/helper-validator-option" "^7.27.1" - "@babel/plugin-syntax-jsx" "^7.27.1" - "@babel/plugin-transform-modules-commonjs" "^7.27.1" - "@babel/plugin-transform-typescript" "^7.27.1" - -"@babel/template@^7.27.1": - version "7.27.2" - resolved "https://registry.npmjs.org/@babel/template/-/template-7.27.2.tgz#fa78ceed3c4e7b63ebf6cb39e5852fca45f6809d" - integrity sha512-LPDZ85aEJyYSd18/DkjNh4/y1ntkE5KwUHWTiqgRxruuZL2F1yuHligVHLvcHY2vMHXttKFpJn6LwfI7cw7ODw== - dependencies: - "@babel/code-frame" "^7.27.1" - "@babel/parser" "^7.27.2" - "@babel/types" "^7.27.1" - -"@babel/traverse@^7.23.2", "@babel/traverse@^7.23.7", "@babel/traverse@^7.27.1": - version "7.27.1" - resolved "https://registry.npmjs.org/@babel/traverse/-/traverse-7.27.1.tgz#4db772902b133bbddd1c4f7a7ee47761c1b9f291" - integrity sha512-ZCYtZciz1IWJB4U61UPu4KEaqyfj+r5T1Q5mqPo+IBpcG9kHv30Z0aD8LXPgC1trYa6rK0orRyAhqUgk4MjmEg== - dependencies: - "@babel/code-frame" "^7.27.1" - "@babel/generator" "^7.27.1" - "@babel/parser" "^7.27.1" - "@babel/template" "^7.27.1" - "@babel/types" "^7.27.1" - debug "^4.3.1" - globals "^11.1.0" - -"@babel/types@^7.22.5", "@babel/types@^7.23.6", "@babel/types@^7.27.1": - version "7.27.1" - resolved "https://registry.npmjs.org/@babel/types/-/types-7.27.1.tgz#9defc53c16fc899e46941fc6901a9eea1c9d8560" - integrity sha512-+EzkxvLNfiUeKMgy/3luqfsCWFRXLb7U6wNQTk60tovuckwB15B191tJWvpp4HjiQWdJkCxO3Wbvc6jlk3Xb2Q== - dependencies: - "@babel/helper-string-parser" "^7.27.1" - "@babel/helper-validator-identifier" "^7.27.1" - -"@emnapi/core@^1.4.3": - version "1.4.3" - resolved "https://registry.npmjs.org/@emnapi/core/-/core-1.4.3.tgz#9ac52d2d5aea958f67e52c40a065f51de59b77d6" - integrity sha512-4m62DuCE07lw01soJwPiBGC0nAww0Q+RY70VZ+n49yDIO13yyinhbWCeNnaob0lakDtWQzSdtNWzJeOJt2ma+g== - dependencies: - "@emnapi/wasi-threads" "1.0.2" - tslib "^2.4.0" - -"@emnapi/runtime@^1.4.3": - version "1.4.3" - resolved "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.4.3.tgz#c0564665c80dc81c448adac23f9dfbed6c838f7d" - integrity sha512-pBPWdu6MLKROBX05wSNKcNb++m5Er+KQ9QkB+WVM+pW2Kx9hoSrVTnu3BdkI5eBLZoKu/J6mW/B6i6bJB2ytXQ== - dependencies: - tslib "^2.4.0" - -"@emnapi/wasi-threads@1.0.2", "@emnapi/wasi-threads@^1.0.2": - version "1.0.2" - resolved "https://registry.npmjs.org/@emnapi/wasi-threads/-/wasi-threads-1.0.2.tgz#977f44f844eac7d6c138a415a123818c655f874c" - integrity sha512-5n3nTJblwRi8LlXkJ9eBzu+kZR8Yxcc7ubakyQTFzPMtIhFpUBRbsnc2Dv88IZDIbCDlBiWrknhB4Lsz7mg6BA== - dependencies: - tslib "^2.4.0" - -"@esbuild/aix-ppc64@0.25.4": - version "0.25.4" - resolved "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.25.4.tgz#830d6476cbbca0c005136af07303646b419f1162" - integrity sha512-1VCICWypeQKhVbE9oW/sJaAmjLxhVqacdkvPLEjwlttjfwENRSClS8EjBz0KzRyFSCPDIkuXW34Je/vk7zdB7Q== - -"@esbuild/android-arm64@0.25.4": - version "0.25.4" - resolved "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.25.4.tgz#d11d4fc299224e729e2190cacadbcc00e7a9fd67" - integrity sha512-bBy69pgfhMGtCnwpC/x5QhfxAz/cBgQ9enbtwjf6V9lnPI/hMyT9iWpR1arm0l3kttTr4L0KSLpKmLp/ilKS9A== - -"@esbuild/android-arm@0.25.4": - version "0.25.4" - resolved "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.25.4.tgz#5660bd25080553dd2a28438f2a401a29959bd9b1" - integrity sha512-QNdQEps7DfFwE3hXiU4BZeOV68HHzYwGd0Nthhd3uCkkEKK7/R6MTgM0P7H7FAs5pU/DIWsviMmEGxEoxIZ+ZQ== - -"@esbuild/android-x64@0.25.4": - version "0.25.4" - resolved "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.25.4.tgz#18ddde705bf984e8cd9efec54e199ac18bc7bee1" - integrity sha512-TVhdVtQIFuVpIIR282btcGC2oGQoSfZfmBdTip2anCaVYcqWlZXGcdcKIUklfX2wj0JklNYgz39OBqh2cqXvcQ== - -"@esbuild/darwin-arm64@0.25.4": - version "0.25.4" - resolved "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.25.4.tgz#b0b7fb55db8fc6f5de5a0207ae986eb9c4766e67" - integrity sha512-Y1giCfM4nlHDWEfSckMzeWNdQS31BQGs9/rouw6Ub91tkK79aIMTH3q9xHvzH8d0wDru5Ci0kWB8b3up/nl16g== - -"@esbuild/darwin-x64@0.25.4": - version "0.25.4" - resolved "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.25.4.tgz#e6813fdeba0bba356cb350a4b80543fbe66bf26f" - integrity sha512-CJsry8ZGM5VFVeyUYB3cdKpd/H69PYez4eJh1W/t38vzutdjEjtP7hB6eLKBoOdxcAlCtEYHzQ/PJ/oU9I4u0A== - -"@esbuild/freebsd-arm64@0.25.4": - version "0.25.4" - resolved "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.25.4.tgz#dc11a73d3ccdc308567b908b43c6698e850759be" - integrity sha512-yYq+39NlTRzU2XmoPW4l5Ifpl9fqSk0nAJYM/V/WUGPEFfek1epLHJIkTQM6bBs1swApjO5nWgvr843g6TjxuQ== - -"@esbuild/freebsd-x64@0.25.4": - version "0.25.4" - resolved "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.25.4.tgz#91da08db8bd1bff5f31924c57a81dab26e93a143" - integrity sha512-0FgvOJ6UUMflsHSPLzdfDnnBBVoCDtBTVyn/MrWloUNvq/5SFmh13l3dvgRPkDihRxb77Y17MbqbCAa2strMQQ== - -"@esbuild/linux-arm64@0.25.4": - version "0.25.4" - resolved "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.25.4.tgz#efc15e45c945a082708f9a9f73bfa8d4db49728a" - integrity sha512-+89UsQTfXdmjIvZS6nUnOOLoXnkUTB9hR5QAeLrQdzOSWZvNSAXAtcRDHWtqAUtAmv7ZM1WPOOeSxDzzzMogiQ== - -"@esbuild/linux-arm@0.25.4": - version "0.25.4" - resolved "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.25.4.tgz#9b93c3e54ac49a2ede6f906e705d5d906f6db9e8" - integrity sha512-kro4c0P85GMfFYqW4TWOpvmF8rFShbWGnrLqlzp4X1TNWjRY3JMYUfDCtOxPKOIY8B0WC8HN51hGP4I4hz4AaQ== - -"@esbuild/linux-ia32@0.25.4": - version "0.25.4" - resolved "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.25.4.tgz#be8ef2c3e1d99fca2d25c416b297d00360623596" - integrity sha512-yTEjoapy8UP3rv8dB0ip3AfMpRbyhSN3+hY8mo/i4QXFeDxmiYbEKp3ZRjBKcOP862Ua4b1PDfwlvbuwY7hIGQ== - -"@esbuild/linux-loong64@0.25.4": - version "0.25.4" - resolved "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.25.4.tgz#b0840a2707c3fc02eec288d3f9defa3827cd7a87" - integrity sha512-NeqqYkrcGzFwi6CGRGNMOjWGGSYOpqwCjS9fvaUlX5s3zwOtn1qwg1s2iE2svBe4Q/YOG1q6875lcAoQK/F4VA== - -"@esbuild/linux-mips64el@0.25.4": - version "0.25.4" - resolved "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.25.4.tgz#2a198e5a458c9f0e75881a4e63d26ba0cf9df39f" - integrity sha512-IcvTlF9dtLrfL/M8WgNI/qJYBENP3ekgsHbYUIzEzq5XJzzVEV/fXY9WFPfEEXmu3ck2qJP8LG/p3Q8f7Zc2Xg== - -"@esbuild/linux-ppc64@0.25.4": - version "0.25.4" - resolved "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.25.4.tgz#64f4ae0b923d7dd72fb860b9b22edb42007cf8f5" - integrity sha512-HOy0aLTJTVtoTeGZh4HSXaO6M95qu4k5lJcH4gxv56iaycfz1S8GO/5Jh6X4Y1YiI0h7cRyLi+HixMR+88swag== - -"@esbuild/linux-riscv64@0.25.4": - version "0.25.4" - resolved "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.25.4.tgz#fb2844b11fdddd39e29d291c7cf80f99b0d5158d" - integrity sha512-i8JUDAufpz9jOzo4yIShCTcXzS07vEgWzyX3NH2G7LEFVgrLEhjwL3ajFE4fZI3I4ZgiM7JH3GQ7ReObROvSUA== - -"@esbuild/linux-s390x@0.25.4": - version "0.25.4" - resolved "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.25.4.tgz#1466876e0aa3560c7673e63fdebc8278707bc750" - integrity sha512-jFnu+6UbLlzIjPQpWCNh5QtrcNfMLjgIavnwPQAfoGx4q17ocOU9MsQ2QVvFxwQoWpZT8DvTLooTvmOQXkO51g== - -"@esbuild/linux-x64@0.25.4": - version "0.25.4" - resolved "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.25.4.tgz#c10fde899455db7cba5f11b3bccfa0e41bf4d0cd" - integrity sha512-6e0cvXwzOnVWJHq+mskP8DNSrKBr1bULBvnFLpc1KY+d+irZSgZ02TGse5FsafKS5jg2e4pbvK6TPXaF/A6+CA== - -"@esbuild/netbsd-arm64@0.25.4": - version "0.25.4" - resolved "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.25.4.tgz#02e483fbcbe3f18f0b02612a941b77be76c111a4" - integrity sha512-vUnkBYxZW4hL/ie91hSqaSNjulOnYXE1VSLusnvHg2u3jewJBz3YzB9+oCw8DABeVqZGg94t9tyZFoHma8gWZQ== - -"@esbuild/netbsd-x64@0.25.4": - version "0.25.4" - resolved "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.25.4.tgz#ec401fb0b1ed0ac01d978564c5fc8634ed1dc2ed" - integrity sha512-XAg8pIQn5CzhOB8odIcAm42QsOfa98SBeKUdo4xa8OvX8LbMZqEtgeWE9P/Wxt7MlG2QqvjGths+nq48TrUiKw== - -"@esbuild/openbsd-arm64@0.25.4": - version "0.25.4" - resolved "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.25.4.tgz#f272c2f41cfea1d91b93d487a51b5c5ca7a8c8c4" - integrity sha512-Ct2WcFEANlFDtp1nVAXSNBPDxyU+j7+tId//iHXU2f/lN5AmO4zLyhDcpR5Cz1r08mVxzt3Jpyt4PmXQ1O6+7A== - -"@esbuild/openbsd-x64@0.25.4": - version "0.25.4" - resolved "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.25.4.tgz#2e25950bc10fa9db1e5c868e3d50c44f7c150fd7" - integrity sha512-xAGGhyOQ9Otm1Xu8NT1ifGLnA6M3sJxZ6ixylb+vIUVzvvd6GOALpwQrYrtlPouMqd/vSbgehz6HaVk4+7Afhw== - -"@esbuild/sunos-x64@0.25.4": - version "0.25.4" - resolved "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.25.4.tgz#cd596fa65a67b3b7adc5ecd52d9f5733832e1abd" - integrity sha512-Mw+tzy4pp6wZEK0+Lwr76pWLjrtjmJyUB23tHKqEDP74R3q95luY/bXqXZeYl4NYlvwOqoRKlInQialgCKy67Q== - -"@esbuild/win32-arm64@0.25.4": - version "0.25.4" - resolved "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.25.4.tgz#b4dbcb57b21eeaf8331e424c3999b89d8951dc88" - integrity sha512-AVUP428VQTSddguz9dO9ngb+E5aScyg7nOeJDrF1HPYu555gmza3bDGMPhmVXL8svDSoqPCsCPjb265yG/kLKQ== - -"@esbuild/win32-ia32@0.25.4": - version "0.25.4" - resolved "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.25.4.tgz#410842e5d66d4ece1757634e297a87635eb82f7a" - integrity sha512-i1sW+1i+oWvQzSgfRcxxG2k4I9n3O9NRqy8U+uugaT2Dy7kLO9Y7wI72haOahxceMX8hZAzgGou1FhndRldxRg== - -"@esbuild/win32-x64@0.25.4": - version "0.25.4" - resolved "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.25.4.tgz#0b17ec8a70b2385827d52314c1253160a0b9bacc" - integrity sha512-nOT2vZNw6hJ+z43oP1SPea/G/6AbN6X+bGNhNuq8NtRHy4wsMhw765IKLNmnjek7GvjWBYQ8Q5VBoYTFg9y1UQ== - -"@isaacs/cliui@^8.0.2": - version "8.0.2" - resolved "https://registry.npmjs.org/@isaacs/cliui/-/cliui-8.0.2.tgz#b37667b7bc181c168782259bab42474fbf52b550" - integrity sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA== - dependencies: - string-width "^5.1.2" - string-width-cjs "npm:string-width@^4.2.0" - strip-ansi "^7.0.1" - strip-ansi-cjs "npm:strip-ansi@^6.0.1" - wrap-ansi "^8.1.0" - wrap-ansi-cjs "npm:wrap-ansi@^7.0.0" - -"@isaacs/fs-minipass@^4.0.0": - version "4.0.1" - resolved "https://registry.npmjs.org/@isaacs/fs-minipass/-/fs-minipass-4.0.1.tgz#2d59ae3ab4b38fb4270bfa23d30f8e2e86c7fe32" - integrity sha512-wgm9Ehl2jpeqP3zw/7mo3kRHFp5MEDhqAdwy1fTGkHAwnkGOVsgpvQhL8B5n1qlb01jV3n/bI0ZfZp5lWA1k4w== - dependencies: - minipass "^7.0.4" - -"@jridgewell/gen-mapping@^0.3.5": - version "0.3.8" - resolved "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.8.tgz#4f0e06362e01362f823d348f1872b08f666d8142" - integrity sha512-imAbBGkb+ebQyxKgzv5Hu2nmROxoDOXHh80evxdoXNOrvAnVx7zimzc1Oo5h9RlfV4vPXaE2iM5pOFbvOCClWA== - dependencies: - "@jridgewell/set-array" "^1.2.1" - "@jridgewell/sourcemap-codec" "^1.4.10" - "@jridgewell/trace-mapping" "^0.3.24" - -"@jridgewell/resolve-uri@^3.1.0": - version "3.1.2" - resolved "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz#7a0ee601f60f99a20c7c7c5ff0c80388c1189bd6" - integrity sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw== - -"@jridgewell/set-array@^1.2.1": - version "1.2.1" - resolved "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.2.1.tgz#558fb6472ed16a4c850b889530e6b36438c49280" - integrity sha512-R8gLRTZeyp03ymzP/6Lil/28tGeGEzhx1q2k703KGWRAI1VdvPIXdG70VJc2pAMw3NA6JKL5hhFu1sJX0Mnn/A== - -"@jridgewell/sourcemap-codec@^1.4.10", "@jridgewell/sourcemap-codec@^1.4.14", "@jridgewell/sourcemap-codec@^1.5.0": - version "1.5.0" - resolved "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.0.tgz#3188bcb273a414b0d215fd22a58540b989b9409a" - integrity sha512-gv3ZRaISU3fjPAgNsriBRqGWQL6quFx04YMPW/zD8XMLsU32mhCCbfbO6KZFLjvYpCZ8zyDEgqsgf+PwPaM7GQ== - -"@jridgewell/trace-mapping@^0.3.24", "@jridgewell/trace-mapping@^0.3.25": - version "0.3.25" - resolved "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.25.tgz#15f190e98895f3fc23276ee14bc76b675c2e50f0" - integrity sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ== - dependencies: - "@jridgewell/resolve-uri" "^3.1.0" - "@jridgewell/sourcemap-codec" "^1.4.14" - -"@mjackson/node-fetch-server@^0.2.0": - version "0.2.0" - resolved "https://registry.npmjs.org/@mjackson/node-fetch-server/-/node-fetch-server-0.2.0.tgz#577c0c25d8aae9f69a97738b7b0d03d1471cdc49" - integrity sha512-EMlH1e30yzmTpGLQjlFmaDAjyOeZhng1/XCd7DExR8PNAnG/G1tyruZxEoUe11ClnwGhGrtsdnyyUx1frSzjng== - -"@napi-rs/wasm-runtime@^0.2.9": - version "0.2.10" - resolved "https://registry.npmjs.org/@napi-rs/wasm-runtime/-/wasm-runtime-0.2.10.tgz#f3b7109419c6670000b2401e0c778b98afc25f84" - integrity sha512-bCsCyeZEwVErsGmyPNSzwfwFn4OdxBj0mmv6hOFucB/k81Ojdu68RbZdxYsRQUPc9l6SU5F/cG+bXgWs3oUgsQ== - dependencies: - "@emnapi/core" "^1.4.3" - "@emnapi/runtime" "^1.4.3" - "@tybys/wasm-util" "^0.9.0" - -"@npmcli/git@^4.1.0": - version "4.1.0" - resolved "https://registry.npmjs.org/@npmcli/git/-/git-4.1.0.tgz#ab0ad3fd82bc4d8c1351b6c62f0fa56e8fe6afa6" - integrity sha512-9hwoB3gStVfa0N31ymBmrX+GuDGdVA/QWShZVqE0HK2Af+7QGGrCTbZia/SW0ImUTjTne7SP91qxDmtXvDHRPQ== - dependencies: - "@npmcli/promise-spawn" "^6.0.0" - lru-cache "^7.4.4" - npm-pick-manifest "^8.0.0" - proc-log "^3.0.0" - promise-inflight "^1.0.1" - promise-retry "^2.0.1" - semver "^7.3.5" - which "^3.0.0" - -"@npmcli/package-json@^4.0.1": - version "4.0.1" - resolved "https://registry.npmjs.org/@npmcli/package-json/-/package-json-4.0.1.tgz#1a07bf0e086b640500791f6bf245ff43cc27fa37" - integrity sha512-lRCEGdHZomFsURroh522YvA/2cVb9oPIJrjHanCJZkiasz1BzcnLr3tBJhlV7S86MBJBuAQ33is2D60YitZL2Q== - dependencies: - "@npmcli/git" "^4.1.0" - glob "^10.2.2" - hosted-git-info "^6.1.1" - json-parse-even-better-errors "^3.0.0" - normalize-package-data "^5.0.0" - proc-log "^3.0.0" - semver "^7.5.3" - -"@npmcli/promise-spawn@^6.0.0": - version "6.0.2" - resolved "https://registry.npmjs.org/@npmcli/promise-spawn/-/promise-spawn-6.0.2.tgz#c8bc4fa2bd0f01cb979d8798ba038f314cfa70f2" - integrity sha512-gGq0NJkIGSwdbUt4yhdF8ZrmkGKVz9vAdVzpOfnom+V8PLSmSOVhZwbNvZZS1EYcJN5hzzKBxmmVVAInM6HQLg== - dependencies: - which "^3.0.0" - -"@pkgjs/parseargs@^0.11.0": - version "0.11.0" - resolved "https://registry.npmjs.org/@pkgjs/parseargs/-/parseargs-0.11.0.tgz#a77ea742fab25775145434eb1d2328cf5013ac33" - integrity sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg== - -"@react-router/dev@^7.5.3": - version "7.6.0" - resolved "https://registry.npmjs.org/@react-router/dev/-/dev-7.6.0.tgz#9974e608be0398ee22282405992d0a62a3cc26ba" - integrity sha512-XSxEslex0ddJPxNNgdU1Eqmc9lsY/lhcLNCcRLAtlrOPyOz3Y8kIPpAf5T/U2AG3HGXFVBa9f8aQ7wXU3wTJSw== - dependencies: - "@babel/core" "^7.21.8" - "@babel/generator" "^7.21.5" - "@babel/parser" "^7.21.8" - "@babel/plugin-syntax-decorators" "^7.22.10" - "@babel/plugin-syntax-jsx" "^7.21.4" - "@babel/preset-typescript" "^7.21.5" - "@babel/traverse" "^7.23.2" - "@babel/types" "^7.22.5" - "@npmcli/package-json" "^4.0.1" - "@react-router/node" "7.6.0" - arg "^5.0.1" - babel-dead-code-elimination "^1.0.6" - chokidar "^4.0.0" - dedent "^1.5.3" - es-module-lexer "^1.3.1" - exit-hook "2.2.1" - fs-extra "^10.0.0" - jsesc "3.0.2" - lodash "^4.17.21" - pathe "^1.1.2" - picocolors "^1.1.1" - prettier "^2.7.1" - react-refresh "^0.14.0" - semver "^7.3.7" - set-cookie-parser "^2.6.0" - valibot "^0.41.0" - vite-node "3.0.0-beta.2" - -"@react-router/express@7.6.0": - version "7.6.0" - resolved "https://registry.npmjs.org/@react-router/express/-/express-7.6.0.tgz#1c9686e9154aab7b9411d4d1bcf4cc14145ebc94" - integrity sha512-nxSTCcTsVx94bXOI9JjG7Cg338myi8EdQWTOjA97v2ApX35wZm/ZDYos5MbrvZiMi0aB4KgAD62o4byNqF9Z1A== - dependencies: - "@react-router/node" "7.6.0" - -"@react-router/node@7.6.0", "@react-router/node@^7.5.3": - version "7.6.0" - resolved "https://registry.npmjs.org/@react-router/node/-/node-7.6.0.tgz#18c31c502f8004f2b8a1ca6ceebd0ac1e9307e12" - integrity sha512-agjDPUzisLdGJ7Q2lx/Z3OfdS2t1k6qv/nTvA45iahGsQJCMDvMqVoIi7iIULKQJwrn4HWjM9jqEp75+WsMOXg== - dependencies: - "@mjackson/node-fetch-server" "^0.2.0" - source-map-support "^0.5.21" - stream-slice "^0.1.2" - undici "^6.19.2" - -"@react-router/serve@^7.5.3": - version "7.6.0" - resolved "https://registry.npmjs.org/@react-router/serve/-/serve-7.6.0.tgz#ab0ff21cf80b3dbbb746fad1110d9d2903b24439" - integrity sha512-2O8ALEYgJfimvEdNRqMpnZb2N+DQ5UK/SKo9Xo3mTkt3no0rNTcNxzmhzD2tm92Q/HI7kHmMY1nBegNB2i1abA== - dependencies: - "@react-router/express" "7.6.0" - "@react-router/node" "7.6.0" - compression "^1.7.4" - express "^4.19.2" - get-port "5.1.1" - morgan "^1.10.0" - source-map-support "^0.5.21" - -"@rollup/rollup-android-arm-eabi@4.41.0": - version "4.41.0" - resolved "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.41.0.tgz#9145b38faf3fbfe3ec557130110e772f797335aa" - integrity sha512-KxN+zCjOYHGwCl4UCtSfZ6jrq/qi88JDUtiEFk8LELEHq2Egfc/FgW+jItZiOLRuQfb/3xJSgFuNPC9jzggX+A== - -"@rollup/rollup-android-arm64@4.41.0": - version "4.41.0" - resolved "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.41.0.tgz#d73d641c59e9d7827e5ce0af9dfbc168b95cce0f" - integrity sha512-yDvqx3lWlcugozax3DItKJI5j05B0d4Kvnjx+5mwiUpWramVvmAByYigMplaoAQ3pvdprGCTCE03eduqE/8mPQ== - -"@rollup/rollup-darwin-arm64@4.41.0": - version "4.41.0" - resolved "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.41.0.tgz#45d9d71d941117c98e7a5e77f60f0bc682d27e82" - integrity sha512-2KOU574vD3gzcPSjxO0eyR5iWlnxxtmW1F5CkNOHmMlueKNCQkxR6+ekgWyVnz6zaZihpUNkGxjsYrkTJKhkaw== - -"@rollup/rollup-darwin-x64@4.41.0": - version "4.41.0" - resolved "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.41.0.tgz#8d72fb5f81714cb43e90f263fb1674520cce3f2a" - integrity sha512-gE5ACNSxHcEZyP2BA9TuTakfZvULEW4YAOtxl/A/YDbIir/wPKukde0BNPlnBiP88ecaN4BJI2TtAd+HKuZPQQ== - -"@rollup/rollup-freebsd-arm64@4.41.0": - version "4.41.0" - resolved "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.41.0.tgz#a52b58852c9cec9255e382a2f335b08bc8c6111d" - integrity sha512-GSxU6r5HnWij7FoSo7cZg3l5GPg4HFLkzsFFh0N/b16q5buW1NAWuCJ+HMtIdUEi6XF0qH+hN0TEd78laRp7Dg== - -"@rollup/rollup-freebsd-x64@4.41.0": - version "4.41.0" - resolved "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.41.0.tgz#104511dc64612789ddda41d164ab07cdac84a6c1" - integrity sha512-KGiGKGDg8qLRyOWmk6IeiHJzsN/OYxO6nSbT0Vj4MwjS2XQy/5emsmtoqLAabqrohbgLWJ5GV3s/ljdrIr8Qjg== - -"@rollup/rollup-linux-arm-gnueabihf@4.41.0": - version "4.41.0" - resolved "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.41.0.tgz#643e3ad19c93903201fde89abd76baaee725e6c2" - integrity sha512-46OzWeqEVQyX3N2/QdiU/CMXYDH/lSHpgfBkuhl3igpZiaB3ZIfSjKuOnybFVBQzjsLwkus2mjaESy8H41SzvA== - -"@rollup/rollup-linux-arm-musleabihf@4.41.0": - version "4.41.0" - resolved "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.41.0.tgz#fdc6a595aec7b20c5bfdac81412028c56d734e63" - integrity sha512-lfgW3KtQP4YauqdPpcUZHPcqQXmTmH4nYU0cplNeW583CMkAGjtImw4PKli09NFi2iQgChk4e9erkwlfYem6Lg== - -"@rollup/rollup-linux-arm64-gnu@4.41.0": - version "4.41.0" - resolved "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.41.0.tgz#c28620bcd385496bdbbc24920b21f9fcca9ecbfa" - integrity sha512-nn8mEyzMbdEJzT7cwxgObuwviMx6kPRxzYiOl6o/o+ChQq23gfdlZcUNnt89lPhhz3BYsZ72rp0rxNqBSfqlqw== - -"@rollup/rollup-linux-arm64-musl@4.41.0": - version "4.41.0" - resolved "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.41.0.tgz#a6b71b1e8fa33bac9f65b6f879e8ed878035d120" - integrity sha512-l+QK99je2zUKGd31Gh+45c4pGDAqZSuWQiuRFCdHYC2CSiO47qUWsCcenrI6p22hvHZrDje9QjwSMAFL3iwXwQ== - -"@rollup/rollup-linux-loongarch64-gnu@4.41.0": - version "4.41.0" - resolved "https://registry.npmjs.org/@rollup/rollup-linux-loongarch64-gnu/-/rollup-linux-loongarch64-gnu-4.41.0.tgz#b06374601ce865a1110324b2f06db574d3a1b0e1" - integrity sha512-WbnJaxPv1gPIm6S8O/Wg+wfE/OzGSXlBMbOe4ie+zMyykMOeqmgD1BhPxZQuDqwUN+0T/xOFtL2RUWBspnZj3w== - -"@rollup/rollup-linux-powerpc64le-gnu@4.41.0": - version "4.41.0" - resolved "https://registry.npmjs.org/@rollup/rollup-linux-powerpc64le-gnu/-/rollup-linux-powerpc64le-gnu-4.41.0.tgz#8a2a1f6058c920889c2aff3753a20fead7a8cc26" - integrity sha512-eRDWR5t67/b2g8Q/S8XPi0YdbKcCs4WQ8vklNnUYLaSWF+Cbv2axZsp4jni6/j7eKvMLYCYdcsv8dcU+a6QNFg== - -"@rollup/rollup-linux-riscv64-gnu@4.41.0": - version "4.41.0" - resolved "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.41.0.tgz#8ef6f680d011b95a2f6546c6c31a37a33138035f" - integrity sha512-TWrZb6GF5jsEKG7T1IHwlLMDRy2f3DPqYldmIhnA2DVqvvhY2Ai184vZGgahRrg8k9UBWoSlHv+suRfTN7Ua4A== - -"@rollup/rollup-linux-riscv64-musl@4.41.0": - version "4.41.0" - resolved "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.41.0.tgz#9f4884c5955a7cd39b396f6e27aa59b3269988eb" - integrity sha512-ieQljaZKuJpmWvd8gW87ZmSFwid6AxMDk5bhONJ57U8zT77zpZ/TPKkU9HpnnFrM4zsgr4kiGuzbIbZTGi7u9A== - -"@rollup/rollup-linux-s390x-gnu@4.41.0": - version "4.41.0" - resolved "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.41.0.tgz#5619303cc51994e3df404a497f42c79dc5efd6eb" - integrity sha512-/L3pW48SxrWAlVsKCN0dGLB2bi8Nv8pr5S5ocSM+S0XCn5RCVCXqi8GVtHFsOBBCSeR+u9brV2zno5+mg3S4Aw== - -"@rollup/rollup-linux-x64-gnu@4.41.0": - version "4.41.0" - resolved "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.41.0.tgz#c3e42b66c04e25ad0f2a00beec42ede96ccc8983" - integrity sha512-XMLeKjyH8NsEDCRptf6LO8lJk23o9wvB+dJwcXMaH6ZQbbkHu2dbGIUindbMtRN6ux1xKi16iXWu6q9mu7gDhQ== - -"@rollup/rollup-linux-x64-musl@4.41.0": - version "4.41.0" - resolved "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.41.0.tgz#8d3452de42aa72fc5fc3e5ad1eb0b68030742a25" - integrity sha512-m/P7LycHZTvSQeXhFmgmdqEiTqSV80zn6xHaQ1JSqwCtD1YGtwEK515Qmy9DcB2HK4dOUVypQxvhVSy06cJPEg== - -"@rollup/rollup-win32-arm64-msvc@4.41.0": - version "4.41.0" - resolved "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.41.0.tgz#3b7bbd9f43f1c380061f306abce6f3f64de20306" - integrity sha512-4yodtcOrFHpbomJGVEqZ8fzD4kfBeCbpsUy5Pqk4RluXOdsWdjLnjhiKy2w3qzcASWd04fp52Xz7JKarVJ5BTg== - -"@rollup/rollup-win32-ia32-msvc@4.41.0": - version "4.41.0" - resolved "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.41.0.tgz#e27ef5c40bbec49fac3d4e4b1618fbe4597b40e5" - integrity sha512-tmazCrAsKzdkXssEc65zIE1oC6xPHwfy9d5Ta25SRCDOZS+I6RypVVShWALNuU9bxIfGA0aqrmzlzoM5wO5SPQ== - -"@rollup/rollup-win32-x64-msvc@4.41.0": - version "4.41.0" - resolved "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.41.0.tgz#b0b595ad4720259bbb81600750d26a655cac06be" - integrity sha512-h1J+Yzjo/X+0EAvR2kIXJDuTuyT7drc+t2ALY0nIcGPbTatNOf0VWdhEA2Z4AAjv6X1NJV7SYo5oCTYRJhSlVA== - -"@tailwindcss/node@4.1.7": - version "4.1.7" - resolved "https://registry.npmjs.org/@tailwindcss/node/-/node-4.1.7.tgz#6d2df06c6b84a6fd8255a535b4f537c5235a37ee" - integrity sha512-9rsOpdY9idRI2NH6CL4wORFY0+Q6fnx9XP9Ju+iq/0wJwGD5IByIgFmwVbyy4ymuyprj8Qh4ErxMKTUL4uNh3g== - dependencies: - "@ampproject/remapping" "^2.3.0" - enhanced-resolve "^5.18.1" - jiti "^2.4.2" - lightningcss "1.30.1" - magic-string "^0.30.17" - source-map-js "^1.2.1" - tailwindcss "4.1.7" - -"@tailwindcss/oxide-android-arm64@4.1.7": - version "4.1.7" - resolved "https://registry.npmjs.org/@tailwindcss/oxide-android-arm64/-/oxide-android-arm64-4.1.7.tgz#3b115097b64aff6487715304ebe0bb43d7a2d42a" - integrity sha512-IWA410JZ8fF7kACus6BrUwY2Z1t1hm0+ZWNEzykKmMNM09wQooOcN/VXr0p/WJdtHZ90PvJf2AIBS/Ceqx1emg== - -"@tailwindcss/oxide-darwin-arm64@4.1.7": - version "4.1.7" - resolved "https://registry.npmjs.org/@tailwindcss/oxide-darwin-arm64/-/oxide-darwin-arm64-4.1.7.tgz#6c9d8cd3cd631a2ac0dff58a766c958cff5b0046" - integrity sha512-81jUw9To7fimGGkuJ2W5h3/oGonTOZKZ8C2ghm/TTxbwvfSiFSDPd6/A/KE2N7Jp4mv3Ps9OFqg2fEKgZFfsvg== - -"@tailwindcss/oxide-darwin-x64@4.1.7": - version "4.1.7" - resolved "https://registry.npmjs.org/@tailwindcss/oxide-darwin-x64/-/oxide-darwin-x64-4.1.7.tgz#a022b5ef006570ac8bb92128ec7fc9cc2314bdd1" - integrity sha512-q77rWjEyGHV4PdDBtrzO0tgBBPlQWKY7wZK0cUok/HaGgbNKecegNxCGikuPJn5wFAlIywC3v+WMBt0PEBtwGw== - -"@tailwindcss/oxide-freebsd-x64@4.1.7": - version "4.1.7" - resolved "https://registry.npmjs.org/@tailwindcss/oxide-freebsd-x64/-/oxide-freebsd-x64-4.1.7.tgz#efc133e6065c3c6299a981caa9c5f426e1d60e5e" - integrity sha512-RfmdbbK6G6ptgF4qqbzoxmH+PKfP4KSVs7SRlTwcbRgBwezJkAO3Qta/7gDy10Q2DcUVkKxFLXUQO6J3CRvBGw== - -"@tailwindcss/oxide-linux-arm-gnueabihf@4.1.7": - version "4.1.7" - resolved "https://registry.npmjs.org/@tailwindcss/oxide-linux-arm-gnueabihf/-/oxide-linux-arm-gnueabihf-4.1.7.tgz#7a4dc1d9636c1b1e62936bd679a8867030dc0ad6" - integrity sha512-OZqsGvpwOa13lVd1z6JVwQXadEobmesxQ4AxhrwRiPuE04quvZHWn/LnihMg7/XkN+dTioXp/VMu/p6A5eZP3g== - -"@tailwindcss/oxide-linux-arm64-gnu@4.1.7": - version "4.1.7" - resolved "https://registry.npmjs.org/@tailwindcss/oxide-linux-arm64-gnu/-/oxide-linux-arm64-gnu-4.1.7.tgz#9a0da009c37d135fe983d1093bfb6d370fe926dc" - integrity sha512-voMvBTnJSfKecJxGkoeAyW/2XRToLZ227LxswLAwKY7YslG/Xkw9/tJNH+3IVh5bdYzYE7DfiaPbRkSHFxY1xA== - -"@tailwindcss/oxide-linux-arm64-musl@4.1.7": - version "4.1.7" - resolved "https://registry.npmjs.org/@tailwindcss/oxide-linux-arm64-musl/-/oxide-linux-arm64-musl-4.1.7.tgz#ef2398b48c426148c1b9949fdbdd86d364dca10d" - integrity sha512-PjGuNNmJeKHnP58M7XyjJyla8LPo+RmwHQpBI+W/OxqrwojyuCQ+GUtygu7jUqTEexejZHr/z3nBc/gTiXBj4A== - -"@tailwindcss/oxide-linux-x64-gnu@4.1.7": - version "4.1.7" - resolved "https://registry.npmjs.org/@tailwindcss/oxide-linux-x64-gnu/-/oxide-linux-x64-gnu-4.1.7.tgz#4d36aec4c4df87f8c332526bb6874b2c36230901" - integrity sha512-HMs+Va+ZR3gC3mLZE00gXxtBo3JoSQxtu9lobbZd+DmfkIxR54NO7Z+UQNPsa0P/ITn1TevtFxXTpsRU7qEvWg== - -"@tailwindcss/oxide-linux-x64-musl@4.1.7": - version "4.1.7" - resolved "https://registry.npmjs.org/@tailwindcss/oxide-linux-x64-musl/-/oxide-linux-x64-musl-4.1.7.tgz#6e3045edc70d5156089aba0cd4a9760caa53965c" - integrity sha512-MHZ6jyNlutdHH8rd+YTdr3QbXrHXqwIhHw9e7yXEBcQdluGwhpQY2Eku8UZK6ReLaWtQ4gijIv5QoM5eE+qlsA== - -"@tailwindcss/oxide-wasm32-wasi@4.1.7": - version "4.1.7" - resolved "https://registry.npmjs.org/@tailwindcss/oxide-wasm32-wasi/-/oxide-wasm32-wasi-4.1.7.tgz#79b41b0c8da6e2f1dc5b722fe43528aa324222d5" - integrity sha512-ANaSKt74ZRzE2TvJmUcbFQ8zS201cIPxUDm5qez5rLEwWkie2SkGtA4P+GPTj+u8N6JbPrC8MtY8RmJA35Oo+A== - dependencies: - "@emnapi/core" "^1.4.3" - "@emnapi/runtime" "^1.4.3" - "@emnapi/wasi-threads" "^1.0.2" - "@napi-rs/wasm-runtime" "^0.2.9" - "@tybys/wasm-util" "^0.9.0" - tslib "^2.8.0" - -"@tailwindcss/oxide-win32-arm64-msvc@4.1.7": - version "4.1.7" - resolved "https://registry.npmjs.org/@tailwindcss/oxide-win32-arm64-msvc/-/oxide-win32-arm64-msvc-4.1.7.tgz#6c695b870eb8a3f9a958d0afccc67b1b6ee7e78d" - integrity sha512-HUiSiXQ9gLJBAPCMVRk2RT1ZrBjto7WvqsPBwUrNK2BcdSxMnk19h4pjZjI7zgPhDxlAbJSumTC4ljeA9y0tEw== - -"@tailwindcss/oxide-win32-x64-msvc@4.1.7": - version "4.1.7" - resolved "https://registry.npmjs.org/@tailwindcss/oxide-win32-x64-msvc/-/oxide-win32-x64-msvc-4.1.7.tgz#012b2929f6f33ba720a48793e3dbabc34bb0212c" - integrity sha512-rYHGmvoHiLJ8hWucSfSOEmdCBIGZIq7SpkPRSqLsH2Ab2YUNgKeAPT1Fi2cx3+hnYOrAb0jp9cRyode3bBW4mQ== - -"@tailwindcss/oxide@4.1.7": - version "4.1.7" - resolved "https://registry.npmjs.org/@tailwindcss/oxide/-/oxide-4.1.7.tgz#d8370c4d524a3017a85d4f63c41ca2318770debd" - integrity sha512-5SF95Ctm9DFiUyjUPnDGkoKItPX/k+xifcQhcqX5RA85m50jw1pT/KzjdvlqxRja45Y52nR4MR9fD1JYd7f8NQ== - dependencies: - detect-libc "^2.0.4" - tar "^7.4.3" - optionalDependencies: - "@tailwindcss/oxide-android-arm64" "4.1.7" - "@tailwindcss/oxide-darwin-arm64" "4.1.7" - "@tailwindcss/oxide-darwin-x64" "4.1.7" - "@tailwindcss/oxide-freebsd-x64" "4.1.7" - "@tailwindcss/oxide-linux-arm-gnueabihf" "4.1.7" - "@tailwindcss/oxide-linux-arm64-gnu" "4.1.7" - "@tailwindcss/oxide-linux-arm64-musl" "4.1.7" - "@tailwindcss/oxide-linux-x64-gnu" "4.1.7" - "@tailwindcss/oxide-linux-x64-musl" "4.1.7" - "@tailwindcss/oxide-wasm32-wasi" "4.1.7" - "@tailwindcss/oxide-win32-arm64-msvc" "4.1.7" - "@tailwindcss/oxide-win32-x64-msvc" "4.1.7" - -"@tailwindcss/vite@^4.1.4": - version "4.1.7" - resolved "https://registry.npmjs.org/@tailwindcss/vite/-/vite-4.1.7.tgz#73fb55d2341982fb920f916e1ef6781099cf2df6" - integrity sha512-tYa2fO3zDe41I7WqijyVbRd8oWT0aEID1Eokz5hMT6wShLIHj3yvwj9XbfuloHP9glZ6H+aG2AN/+ZrxJ1Y5RQ== - dependencies: - "@tailwindcss/node" "4.1.7" - "@tailwindcss/oxide" "4.1.7" - tailwindcss "4.1.7" - -"@tybys/wasm-util@^0.9.0": - version "0.9.0" - resolved "https://registry.npmjs.org/@tybys/wasm-util/-/wasm-util-0.9.0.tgz#3e75eb00604c8d6db470bf18c37b7d984a0e3355" - integrity sha512-6+7nlbMVX/PVDCwaIQ8nTOPveOcFLSt8GcXdx8hD0bt39uWxYT88uXzqTd4fTvqta7oeUJqudepapKNt2DYJFw== - dependencies: - tslib "^2.4.0" - -"@types/estree@1.0.7": - version "1.0.7" - resolved "https://registry.npmjs.org/@types/estree/-/estree-1.0.7.tgz#4158d3105276773d5b7695cd4834b1722e4f37a8" - integrity sha512-w28IoSUCJpidD/TGviZwwMJckNESJZXFu7NBZ5YJ4mEUnNraUn9Pm8HSZm/jDF1pDWYKspWE7oVphigUPRakIQ== - -"@types/node@^20": - version "20.17.50" - resolved "https://registry.npmjs.org/@types/node/-/node-20.17.50.tgz#d2640af991fc839ba746f799516fc6a4e47ebe50" - integrity sha512-Mxiq0ULv/zo1OzOhwPqOA13I81CV/W3nvd3ChtQZRT5Cwz3cr0FKo/wMSsbTqL3EXpaBAEQhva2B8ByRkOIh9A== - dependencies: - undici-types "~6.19.2" - -"@types/react-dom@^19.1.2": - version "19.1.5" - resolved "https://registry.npmjs.org/@types/react-dom/-/react-dom-19.1.5.tgz#cdfe2c663742887372f54804b16e8dbc26bd794a" - integrity sha512-CMCjrWucUBZvohgZxkjd6S9h0nZxXjzus6yDfUb+xLxYM7VvjKNH1tQrE9GWLql1XoOP4/Ds3bwFqShHUYraGg== - -"@types/react@^19.1.2": - version "19.1.5" - resolved "https://registry.npmjs.org/@types/react/-/react-19.1.5.tgz#9feb3bdeb506d0c79d8533b6ebdcacdbcb4756db" - integrity sha512-piErsCVVbpMMT2r7wbawdZsq4xMvIAhQuac2gedQHysu1TZYEigE6pnFfgZT+/jQnrRuF5r+SHzuehFjfRjr4g== - dependencies: - csstype "^3.0.2" - -accepts@~1.3.8: - version "1.3.8" - resolved "https://registry.npmjs.org/accepts/-/accepts-1.3.8.tgz#0bf0be125b67014adcb0b0921e62db7bffe16b2e" - integrity sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw== - dependencies: - mime-types "~2.1.34" - negotiator "0.6.3" - -ansi-regex@^5.0.1: - version "5.0.1" - resolved "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz#082cb2c89c9fe8659a311a53bd6a4dc5301db304" - integrity sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ== - -ansi-regex@^6.0.1: - version "6.1.0" - resolved "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.1.0.tgz#95ec409c69619d6cb1b8b34f14b660ef28ebd654" - integrity sha512-7HSX4QQb4CspciLpVFwyRe79O3xsIZDDLER21kERQ71oaPodF8jL725AgJMFAYbooIqolJoRLuM81SpeUkpkvA== - -ansi-styles@^4.0.0: - version "4.3.0" - resolved "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz#edd803628ae71c04c85ae7a0906edad34b648937" - integrity sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg== - dependencies: - color-convert "^2.0.1" - -ansi-styles@^6.1.0: - version "6.2.1" - resolved "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.1.tgz#0e62320cf99c21afff3b3012192546aacbfb05c5" - integrity sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug== - -arg@^5.0.1: - version "5.0.2" - resolved "https://registry.npmjs.org/arg/-/arg-5.0.2.tgz#c81433cc427c92c4dcf4865142dbca6f15acd59c" - integrity sha512-PYjyFOLKQ9y57JvQ6QLo8dAgNqswh8M1RMJYdQduT6xbWSgK36P/Z/v+p888pM69jMMfS8Xd8F6I1kQ/I9HUGg== - -array-flatten@1.1.1: - version "1.1.1" - resolved "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz#9a5f699051b1e7073328f2a008968b64ea2955d2" - integrity sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg== - -babel-dead-code-elimination@^1.0.6: - version "1.0.10" - resolved "https://registry.npmjs.org/babel-dead-code-elimination/-/babel-dead-code-elimination-1.0.10.tgz#e230562b57bf72ff3de4639ac763ba54f15d37b0" - integrity sha512-DV5bdJZTzZ0zn0DC24v3jD7Mnidh6xhKa4GfKCbq3sfW8kaWhDdZjP3i81geA8T33tdYqWKw4D3fVv0CwEgKVA== - dependencies: - "@babel/core" "^7.23.7" - "@babel/parser" "^7.23.6" - "@babel/traverse" "^7.23.7" - "@babel/types" "^7.23.6" - -balanced-match@^1.0.0: - version "1.0.2" - resolved "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz#e83e3a7e3f300b34cb9d87f615fa0cbf357690ee" - integrity sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw== - -basic-auth@~2.0.1: - version "2.0.1" - resolved "https://registry.npmjs.org/basic-auth/-/basic-auth-2.0.1.tgz#b998279bf47ce38344b4f3cf916d4679bbf51e3a" - integrity sha512-NF+epuEdnUYVlGuhaxbbq+dvJttwLnGY+YixlXlME5KpQ5W3CnXA5cVTneY3SPbPDRkcjMbifrwmFYcClgOZeg== - dependencies: - safe-buffer "5.1.2" - -body-parser@1.20.3: - version "1.20.3" - resolved "https://registry.npmjs.org/body-parser/-/body-parser-1.20.3.tgz#1953431221c6fb5cd63c4b36d53fab0928e548c6" - integrity sha512-7rAxByjUMqQ3/bHJy7D6OGXvx/MMc4IqBn/X0fcM1QUcAItpZrBEYhWGem+tzXH90c+G01ypMcYJBO9Y30203g== - dependencies: - bytes "3.1.2" - content-type "~1.0.5" - debug "2.6.9" - depd "2.0.0" - destroy "1.2.0" - http-errors "2.0.0" - iconv-lite "0.4.24" - on-finished "2.4.1" - qs "6.13.0" - raw-body "2.5.2" - type-is "~1.6.18" - unpipe "1.0.0" - -brace-expansion@^2.0.1: - version "2.0.1" - resolved "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz#1edc459e0f0c548486ecf9fc99f2221364b9a0ae" - integrity sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA== - dependencies: - balanced-match "^1.0.0" - -browserslist@^4.24.0: - version "4.24.5" - resolved "https://registry.npmjs.org/browserslist/-/browserslist-4.24.5.tgz#aa0f5b8560fe81fde84c6dcb38f759bafba0e11b" - integrity sha512-FDToo4Wo82hIdgc1CQ+NQD0hEhmpPjrZ3hiUgwgOG6IuTdlpr8jdjyG24P6cNP1yJpTLzS5OcGgSw0xmDU1/Tw== - dependencies: - caniuse-lite "^1.0.30001716" - electron-to-chromium "^1.5.149" - node-releases "^2.0.19" - update-browserslist-db "^1.1.3" - -buffer-from@^1.0.0: - version "1.1.2" - resolved "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz#2b146a6fd72e80b4f55d255f35ed59a3a9a41bd5" - integrity sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ== - -bytes@3.1.2: - version "3.1.2" - resolved "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz#8b0beeb98605adf1b128fa4386403c009e0221a5" - integrity sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg== - -cac@^6.7.14: - version "6.7.14" - resolved "https://registry.npmjs.org/cac/-/cac-6.7.14.tgz#804e1e6f506ee363cb0e3ccbb09cad5dd9870959" - integrity sha512-b6Ilus+c3RrdDk+JhLKUAQfzzgLEPy6wcXqS7f/xe1EETvsDP6GORG7SFuOs6cID5YkqchW/LXZbX5bc8j7ZcQ== - -call-bind-apply-helpers@^1.0.1, call-bind-apply-helpers@^1.0.2: - version "1.0.2" - resolved "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz#4b5428c222be985d79c3d82657479dbe0b59b2d6" - integrity sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ== - dependencies: - es-errors "^1.3.0" - function-bind "^1.1.2" - -call-bound@^1.0.2: - version "1.0.4" - resolved "https://registry.npmjs.org/call-bound/-/call-bound-1.0.4.tgz#238de935d2a2a692928c538c7ccfa91067fd062a" - integrity sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg== - dependencies: - call-bind-apply-helpers "^1.0.2" - get-intrinsic "^1.3.0" - -caniuse-lite@^1.0.30001716: - version "1.0.30001718" - resolved "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001718.tgz#dae13a9c80d517c30c6197515a96131c194d8f82" - integrity sha512-AflseV1ahcSunK53NfEs9gFWgOEmzr0f+kaMFA4xiLZlr9Hzt7HxcSpIFcnNCUkz6R6dWKa54rUz3HUmI3nVcw== - -chokidar@^4.0.0: - version "4.0.3" - resolved "https://registry.npmjs.org/chokidar/-/chokidar-4.0.3.tgz#7be37a4c03c9aee1ecfe862a4a23b2c70c205d30" - integrity sha512-Qgzu8kfBvo+cA4962jnP1KkS6Dop5NS6g7R5LFYJr4b8Ub94PPQXUksCw9PvXoeXPRRddRNC5C1JQUR2SMGtnA== - dependencies: - readdirp "^4.0.1" - -chownr@^3.0.0: - version "3.0.0" - resolved "https://registry.npmjs.org/chownr/-/chownr-3.0.0.tgz#9855e64ecd240a9cc4267ce8a4aa5d24a1da15e4" - integrity sha512-+IxzY9BZOQd/XuYPRmrvEVjF/nqj5kgT4kEq7VofrDoM1MxoRjEWkrCC3EtLi59TVawxTAn+orJwFQcrqEN1+g== - -color-convert@^2.0.1: - version "2.0.1" - resolved "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz#72d3a68d598c9bdb3af2ad1e84f21d896abd4de3" - integrity sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ== - dependencies: - color-name "~1.1.4" - -color-name@~1.1.4: - version "1.1.4" - resolved "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz#c2a09a87acbde69543de6f63fa3995c826c536a2" - integrity sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA== - -compressible@~2.0.18: - version "2.0.18" - resolved "https://registry.npmjs.org/compressible/-/compressible-2.0.18.tgz#af53cca6b070d4c3c0750fbd77286a6d7cc46fba" - integrity sha512-AF3r7P5dWxL8MxyITRMlORQNaOA2IkAFaTr4k7BUumjPtRpGDTZpl0Pb1XCO6JeDCBdp126Cgs9sMxqSjgYyRg== - dependencies: - mime-db ">= 1.43.0 < 2" - -compression@^1.7.4: - version "1.8.0" - resolved "https://registry.npmjs.org/compression/-/compression-1.8.0.tgz#09420efc96e11a0f44f3a558de59e321364180f7" - integrity sha512-k6WLKfunuqCYD3t6AsuPGvQWaKwuLLh2/xHNcX4qE+vIfDNXpSqnrhwA7O53R7WVQUnt8dVAIW+YHr7xTgOgGA== - dependencies: - bytes "3.1.2" - compressible "~2.0.18" - debug "2.6.9" - negotiator "~0.6.4" - on-headers "~1.0.2" - safe-buffer "5.2.1" - vary "~1.1.2" - -content-disposition@0.5.4: - version "0.5.4" - resolved "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.4.tgz#8b82b4efac82512a02bb0b1dcec9d2c5e8eb5bfe" - integrity sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ== - dependencies: - safe-buffer "5.2.1" - -content-type@~1.0.4, content-type@~1.0.5: - version "1.0.5" - resolved "https://registry.npmjs.org/content-type/-/content-type-1.0.5.tgz#8b773162656d1d1086784c8f23a54ce6d73d7918" - integrity sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA== - -convert-source-map@^2.0.0: - version "2.0.0" - resolved "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz#4b560f649fc4e918dd0ab75cf4961e8bc882d82a" - integrity sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg== - -cookie-signature@1.0.6: - version "1.0.6" - resolved "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz#e303a882b342cc3ee8ca513a79999734dab3ae2c" - integrity sha512-QADzlaHc8icV8I7vbaJXJwod9HWYp8uCqf1xa4OfNu1T7JVxQIrUgOWtHdNDtPiywmFbiS12VjotIXLrKM3orQ== - -cookie@0.7.1: - version "0.7.1" - resolved "https://registry.npmjs.org/cookie/-/cookie-0.7.1.tgz#2f73c42142d5d5cf71310a74fc4ae61670e5dbc9" - integrity sha512-6DnInpx7SJ2AK3+CTUE/ZM0vWTUboZCegxhC2xiIydHR9jNuTAASBrfEpHhiGOZw/nX51bHt6YQl8jsGo4y/0w== - -cookie@^1.0.1: - version "1.0.2" - resolved "https://registry.npmjs.org/cookie/-/cookie-1.0.2.tgz#27360701532116bd3f1f9416929d176afe1e4610" - integrity sha512-9Kr/j4O16ISv8zBBhJoi4bXOYNTkFLOqSL3UDB0njXxCXNezjeyVrJyGOWtgfs/q2km1gwBcfH8q1yEGoMYunA== - -cross-spawn@^7.0.6: - version "7.0.6" - resolved "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz#8a58fe78f00dcd70c370451759dfbfaf03e8ee9f" - integrity sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA== - dependencies: - path-key "^3.1.0" - shebang-command "^2.0.0" - which "^2.0.1" - -csstype@^3.0.2: - version "3.1.3" - resolved "https://registry.npmjs.org/csstype/-/csstype-3.1.3.tgz#d80ff294d114fb0e6ac500fbf85b60137d7eff81" - integrity sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw== - -debug@2.6.9: - version "2.6.9" - resolved "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz#5d128515df134ff327e90a4c93f4e077a536341f" - integrity sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA== - dependencies: - ms "2.0.0" - -debug@^4.1.0, debug@^4.1.1, debug@^4.3.1, debug@^4.4.0: - version "4.4.1" - resolved "https://registry.npmjs.org/debug/-/debug-4.4.1.tgz#e5a8bc6cbc4c6cd3e64308b0693a3d4fa550189b" - integrity sha512-KcKCqiftBJcZr++7ykoDIEwSa3XWowTfNPo92BYxjXiyYEVrUQh2aLyhxBCwww+heortUFxEJYcRzosstTEBYQ== - dependencies: - ms "^2.1.3" - -dedent@^1.5.3: - version "1.6.0" - resolved "https://registry.npmjs.org/dedent/-/dedent-1.6.0.tgz#79d52d6389b1ffa67d2bcef59ba51847a9d503b2" - integrity sha512-F1Z+5UCFpmQUzJa11agbyPVMbpgT/qA3/SKyJ1jyBgm7dUcUEa8v9JwDkerSQXfakBwFljIxhOJqGkjUwZ9FSA== - -depd@2.0.0, depd@~2.0.0: - version "2.0.0" - resolved "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz#b696163cc757560d09cf22cc8fad1571b79e76df" - integrity sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw== - -destroy@1.2.0: - version "1.2.0" - resolved "https://registry.npmjs.org/destroy/-/destroy-1.2.0.tgz#4803735509ad8be552934c67df614f94e66fa015" - integrity sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg== - -detect-libc@^2.0.3, detect-libc@^2.0.4: - version "2.0.4" - resolved "https://registry.npmjs.org/detect-libc/-/detect-libc-2.0.4.tgz#f04715b8ba815e53b4d8109655b6508a6865a7e8" - integrity sha512-3UDv+G9CsCKO1WKMGw9fwq/SWJYbI0c5Y7LU1AXYoDdbhE2AHQ6N6Nb34sG8Fj7T5APy8qXDCKuuIHd1BR0tVA== - -dunder-proto@^1.0.1: - version "1.0.1" - resolved "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz#d7ae667e1dc83482f8b70fd0f6eefc50da30f58a" - integrity sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A== - dependencies: - call-bind-apply-helpers "^1.0.1" - es-errors "^1.3.0" - gopd "^1.2.0" - -eastasianwidth@^0.2.0: - version "0.2.0" - resolved "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz#696ce2ec0aa0e6ea93a397ffcf24aa7840c827cb" - integrity sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA== - -ee-first@1.1.1: - version "1.1.1" - resolved "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz#590c61156b0ae2f4f0255732a158b266bc56b21d" - integrity sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow== - -electron-to-chromium@^1.5.149: - version "1.5.155" - resolved "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.155.tgz#809dd0ae9ae1db87c358e0c0c17c09a2ffc432d1" - integrity sha512-ps5KcGGmwL8VaeJlvlDlu4fORQpv3+GIcF5I3f9tUKUlJ/wsysh6HU8P5L1XWRYeXfA0oJd4PyM8ds8zTFf6Ng== - -emoji-regex@^8.0.0: - version "8.0.0" - resolved "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz#e818fd69ce5ccfcb404594f842963bf53164cc37" - integrity sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A== - -emoji-regex@^9.2.2: - version "9.2.2" - resolved "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz#840c8803b0d8047f4ff0cf963176b32d4ef3ed72" - integrity sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg== - -encodeurl@~1.0.2: - version "1.0.2" - resolved "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz#ad3ff4c86ec2d029322f5a02c3a9a606c95b3f59" - integrity sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w== - -encodeurl@~2.0.0: - version "2.0.0" - resolved "https://registry.npmjs.org/encodeurl/-/encodeurl-2.0.0.tgz#7b8ea898077d7e409d3ac45474ea38eaf0857a58" - integrity sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg== - -enhanced-resolve@^5.18.1: - version "5.18.1" - resolved "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.18.1.tgz#728ab082f8b7b6836de51f1637aab5d3b9568faf" - integrity sha512-ZSW3ma5GkcQBIpwZTSRAI8N71Uuwgs93IezB7mf7R60tC8ZbJideoDNKjHn2O9KIlx6rkGTTEk1xUCK2E1Y2Yg== - dependencies: - graceful-fs "^4.2.4" - tapable "^2.2.0" - -err-code@^2.0.2: - version "2.0.3" - resolved "https://registry.npmjs.org/err-code/-/err-code-2.0.3.tgz#23c2f3b756ffdfc608d30e27c9a941024807e7f9" - integrity sha512-2bmlRpNKBxT/CRmPOlyISQpNj+qSeYvcym/uT0Jx2bMOlKLtSy1ZmLuVxSEKKyor/N5yhvp/ZiG1oE3DEYMSFA== - -es-define-property@^1.0.1: - version "1.0.1" - resolved "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz#983eb2f9a6724e9303f61addf011c72e09e0b0fa" - integrity sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g== - -es-errors@^1.3.0: - version "1.3.0" - resolved "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz#05f75a25dab98e4fb1dcd5e1472c0546d5057c8f" - integrity sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw== - -es-module-lexer@^1.3.1, es-module-lexer@^1.5.4: - version "1.7.0" - resolved "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-1.7.0.tgz#9159601561880a85f2734560a9099b2c31e5372a" - integrity sha512-jEQoCwk8hyb2AZziIOLhDqpm5+2ww5uIE6lkO/6jcOCusfk6LhMHpXXfBLXTZ7Ydyt0j4VoUQv6uGNYbdW+kBA== - -es-object-atoms@^1.0.0, es-object-atoms@^1.1.1: - version "1.1.1" - resolved "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.1.tgz#1c4f2c4837327597ce69d2ca190a7fdd172338c1" - integrity sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA== - dependencies: - es-errors "^1.3.0" - -esbuild@^0.25.0: - version "0.25.4" - resolved "https://registry.npmjs.org/esbuild/-/esbuild-0.25.4.tgz#bb9a16334d4ef2c33c7301a924b8b863351a0854" - integrity sha512-8pgjLUcUjcgDg+2Q4NYXnPbo/vncAY4UmyaCm0jZevERqCHZIaWwdJHkf8XQtu4AxSKCdvrUbT0XUr1IdZzI8Q== - optionalDependencies: - "@esbuild/aix-ppc64" "0.25.4" - "@esbuild/android-arm" "0.25.4" - "@esbuild/android-arm64" "0.25.4" - "@esbuild/android-x64" "0.25.4" - "@esbuild/darwin-arm64" "0.25.4" - "@esbuild/darwin-x64" "0.25.4" - "@esbuild/freebsd-arm64" "0.25.4" - "@esbuild/freebsd-x64" "0.25.4" - "@esbuild/linux-arm" "0.25.4" - "@esbuild/linux-arm64" "0.25.4" - "@esbuild/linux-ia32" "0.25.4" - "@esbuild/linux-loong64" "0.25.4" - "@esbuild/linux-mips64el" "0.25.4" - "@esbuild/linux-ppc64" "0.25.4" - "@esbuild/linux-riscv64" "0.25.4" - "@esbuild/linux-s390x" "0.25.4" - "@esbuild/linux-x64" "0.25.4" - "@esbuild/netbsd-arm64" "0.25.4" - "@esbuild/netbsd-x64" "0.25.4" - "@esbuild/openbsd-arm64" "0.25.4" - "@esbuild/openbsd-x64" "0.25.4" - "@esbuild/sunos-x64" "0.25.4" - "@esbuild/win32-arm64" "0.25.4" - "@esbuild/win32-ia32" "0.25.4" - "@esbuild/win32-x64" "0.25.4" - -escalade@^3.2.0: - version "3.2.0" - resolved "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz#011a3f69856ba189dffa7dc8fcce99d2a87903e5" - integrity sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA== - -escape-html@~1.0.3: - version "1.0.3" - resolved "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz#0258eae4d3d0c0974de1c169188ef0051d1d1988" - integrity sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow== - -etag@~1.8.1: - version "1.8.1" - resolved "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz#41ae2eeb65efa62268aebfea83ac7d79299b0887" - integrity sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg== - -exit-hook@2.2.1: - version "2.2.1" - resolved "https://registry.npmjs.org/exit-hook/-/exit-hook-2.2.1.tgz#007b2d92c6428eda2b76e7016a34351586934593" - integrity sha512-eNTPlAD67BmP31LDINZ3U7HSF8l57TxOY2PmBJ1shpCvpnxBF93mWCE8YHBnXs8qiUZJc9WDcWIeC3a2HIAMfw== - -express@^4.19.2: - version "4.21.2" - resolved "https://registry.npmjs.org/express/-/express-4.21.2.tgz#cf250e48362174ead6cea4a566abef0162c1ec32" - integrity sha512-28HqgMZAmih1Czt9ny7qr6ek2qddF4FclbMzwhCREB6OFfH+rXAnuNCwo1/wFvrtbgsQDb4kSbX9de9lFbrXnA== - dependencies: - accepts "~1.3.8" - array-flatten "1.1.1" - body-parser "1.20.3" - content-disposition "0.5.4" - content-type "~1.0.4" - cookie "0.7.1" - cookie-signature "1.0.6" - debug "2.6.9" - depd "2.0.0" - encodeurl "~2.0.0" - escape-html "~1.0.3" - etag "~1.8.1" - finalhandler "1.3.1" - fresh "0.5.2" - http-errors "2.0.0" - merge-descriptors "1.0.3" - methods "~1.1.2" - on-finished "2.4.1" - parseurl "~1.3.3" - path-to-regexp "0.1.12" - proxy-addr "~2.0.7" - qs "6.13.0" - range-parser "~1.2.1" - safe-buffer "5.2.1" - send "0.19.0" - serve-static "1.16.2" - setprototypeof "1.2.0" - statuses "2.0.1" - type-is "~1.6.18" - utils-merge "1.0.1" - vary "~1.1.2" - -fdir@^6.4.4: - version "6.4.4" - resolved "https://registry.npmjs.org/fdir/-/fdir-6.4.4.tgz#1cfcf86f875a883e19a8fab53622cfe992e8d2f9" - integrity sha512-1NZP+GK4GfuAv3PqKvxQRDMjdSRZjnkq7KfhlNrCNNlZ0ygQFpebfrnfnq/W7fpUnAv9aGWmY1zKx7FYL3gwhg== - -finalhandler@1.3.1: - version "1.3.1" - resolved "https://registry.npmjs.org/finalhandler/-/finalhandler-1.3.1.tgz#0c575f1d1d324ddd1da35ad7ece3df7d19088019" - integrity sha512-6BN9trH7bp3qvnrRyzsBz+g3lZxTNZTbVO2EV1CS0WIcDbawYVdYvGflME/9QP0h0pYlCDBCTjYa9nZzMDpyxQ== - dependencies: - debug "2.6.9" - encodeurl "~2.0.0" - escape-html "~1.0.3" - on-finished "2.4.1" - parseurl "~1.3.3" - statuses "2.0.1" - unpipe "~1.0.0" - -foreground-child@^3.1.0: - version "3.3.1" - resolved "https://registry.npmjs.org/foreground-child/-/foreground-child-3.3.1.tgz#32e8e9ed1b68a3497befb9ac2b6adf92a638576f" - integrity sha512-gIXjKqtFuWEgzFRJA9WCQeSJLZDjgJUOMCMzxtvFq/37KojM1BFGufqsCy0r4qSQmYLsZYMeyRqzIWOMup03sw== - dependencies: - cross-spawn "^7.0.6" - signal-exit "^4.0.1" - -forwarded@0.2.0: - version "0.2.0" - resolved "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz#2269936428aad4c15c7ebe9779a84bf0b2a81811" - integrity sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow== - -fresh@0.5.2: - version "0.5.2" - resolved "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz#3d8cadd90d976569fa835ab1f8e4b23a105605a7" - integrity sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q== - -fs-extra@^10.0.0: - version "10.1.0" - resolved "https://registry.npmjs.org/fs-extra/-/fs-extra-10.1.0.tgz#02873cfbc4084dde127eaa5f9905eef2325d1abf" - integrity sha512-oRXApq54ETRj4eMiFzGnHWGy+zo5raudjuxN0b8H7s/RU2oW0Wvsx9O0ACRN/kRq9E8Vu/ReskGB5o3ji+FzHQ== - dependencies: - graceful-fs "^4.2.0" - jsonfile "^6.0.1" - universalify "^2.0.0" - -fsevents@~2.3.2, fsevents@~2.3.3: - version "2.3.3" - resolved "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz#cac6407785d03675a2a5e1a5305c697b347d90d6" - integrity sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw== - -function-bind@^1.1.2: - version "1.1.2" - resolved "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz#2c02d864d97f3ea6c8830c464cbd11ab6eab7a1c" - integrity sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA== - -gensync@^1.0.0-beta.2: - version "1.0.0-beta.2" - resolved "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz#32a6ee76c3d7f52d46b2b1ae5d93fea8580a25e0" - integrity sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg== - -get-intrinsic@^1.2.5, get-intrinsic@^1.3.0: - version "1.3.0" - resolved "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz#743f0e3b6964a93a5491ed1bffaae054d7f98d01" - integrity sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ== - dependencies: - call-bind-apply-helpers "^1.0.2" - es-define-property "^1.0.1" - es-errors "^1.3.0" - es-object-atoms "^1.1.1" - function-bind "^1.1.2" - get-proto "^1.0.1" - gopd "^1.2.0" - has-symbols "^1.1.0" - hasown "^2.0.2" - math-intrinsics "^1.1.0" - -get-port@5.1.1: - version "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-proto@^1.0.1: - version "1.0.1" - resolved "https://registry.npmjs.org/get-proto/-/get-proto-1.0.1.tgz#150b3f2743869ef3e851ec0c49d15b1d14d00ee1" - integrity sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g== - dependencies: - dunder-proto "^1.0.1" - es-object-atoms "^1.0.0" - -glob@^10.2.2: - version "10.4.5" - resolved "https://registry.npmjs.org/glob/-/glob-10.4.5.tgz#f4d9f0b90ffdbab09c9d77f5f29b4262517b0956" - integrity sha512-7Bv8RF0k6xjo7d4A/PxYLbUCfb6c+Vpd2/mB2yRDlew7Jb5hEXiCD9ibfO7wpk8i4sevK6DFny9h7EYbM3/sHg== - dependencies: - foreground-child "^3.1.0" - jackspeak "^3.1.2" - minimatch "^9.0.4" - minipass "^7.1.2" - package-json-from-dist "^1.0.0" - path-scurry "^1.11.1" - -globals@^11.1.0: - version "11.12.0" - resolved "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz#ab8795338868a0babd8525758018c2a7eb95c42e" - integrity sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA== - -globrex@^0.1.2: - version "0.1.2" - resolved "https://registry.npmjs.org/globrex/-/globrex-0.1.2.tgz#dd5d9ec826232730cd6793a5e33a9302985e6098" - integrity sha512-uHJgbwAMwNFf5mLst7IWLNg14x1CkeqglJb/K3doi4dw6q2IvAAmM/Y81kevy83wP+Sst+nutFTYOGg3d1lsxg== - -gopd@^1.2.0: - version "1.2.0" - resolved "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz#89f56b8217bdbc8802bd299df6d7f1081d7e51a1" - integrity sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg== - -graceful-fs@^4.1.6, graceful-fs@^4.2.0, graceful-fs@^4.2.4: - version "4.2.11" - resolved "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz#4183e4e8bf08bb6e05bbb2f7d2e0c8f712ca40e3" - integrity sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ== - -has-symbols@^1.1.0: - version "1.1.0" - resolved "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz#fc9c6a783a084951d0b971fe1018de813707a338" - integrity sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ== - -hasown@^2.0.2: - version "2.0.2" - resolved "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz#003eaf91be7adc372e84ec59dc37252cedb80003" - integrity sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ== - dependencies: - function-bind "^1.1.2" - -hosted-git-info@^6.0.0, hosted-git-info@^6.1.1: - version "6.1.3" - resolved "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-6.1.3.tgz#2ee1a14a097a1236bddf8672c35b613c46c55946" - integrity sha512-HVJyzUrLIL1c0QmviVh5E8VGyUS7xCFPS6yydaVd1UegW+ibV/CohqTH9MkOLDp5o+rb82DMo77PTuc9F/8GKw== - dependencies: - lru-cache "^7.5.1" - -http-errors@2.0.0: - version "2.0.0" - resolved "https://registry.npmjs.org/http-errors/-/http-errors-2.0.0.tgz#b7774a1486ef73cf7667ac9ae0858c012c57b9d3" - integrity sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ== - dependencies: - depd "2.0.0" - inherits "2.0.4" - setprototypeof "1.2.0" - statuses "2.0.1" - toidentifier "1.0.1" - -iconv-lite@0.4.24: - version "0.4.24" - resolved "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz#2022b4b25fbddc21d2f524974a474aafe733908b" - integrity sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA== - dependencies: - safer-buffer ">= 2.1.2 < 3" - -inherits@2.0.4: - version "2.0.4" - resolved "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz#0fa2c64f932917c3433a0ded55363aae37416b7c" - integrity sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ== - -ipaddr.js@1.9.1: - version "1.9.1" - resolved "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz#bff38543eeb8984825079ff3a2a8e6cbd46781b3" - integrity sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g== - -is-core-module@^2.8.1: - version "2.16.1" - resolved "https://registry.npmjs.org/is-core-module/-/is-core-module-2.16.1.tgz#2a98801a849f43e2add644fbb6bc6229b19a4ef4" - integrity sha512-UfoeMA6fIJ8wTYFEUjelnaGI67v6+N7qXJEvQuIGa99l4xsCruSYOVSQ0uPANn4dAzm8lkYPaKLrrijLq7x23w== - dependencies: - hasown "^2.0.2" - -is-fullwidth-code-point@^3.0.0: - version "3.0.0" - resolved "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz#f116f8064fe90b3f7844a38997c0b75051269f1d" - integrity sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg== - -isbot@^5.1.27: - version "5.1.28" - resolved "https://registry.npmjs.org/isbot/-/isbot-5.1.28.tgz#a9a32e70c890cf19b76090971b1fccf6021a519b" - integrity sha512-qrOp4g3xj8YNse4biorv6O5ZShwsJM0trsoda4y7j/Su7ZtTTfVXFzbKkpgcSoDrHS8FcTuUwcU04YimZlZOxw== - -isexe@^2.0.0: - version "2.0.0" - resolved "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz#e8fbf374dc556ff8947a10dcb0572d633f2cfa10" - integrity sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw== - -jackspeak@^3.1.2: - version "3.4.3" - resolved "https://registry.npmjs.org/jackspeak/-/jackspeak-3.4.3.tgz#8833a9d89ab4acde6188942bd1c53b6390ed5a8a" - integrity sha512-OGlZQpz2yfahA/Rd1Y8Cd9SIEsqvXkLVoSw/cgwhnhFMDbsQFeZYoJJ7bIZBS9BcamUW96asq/npPWugM+RQBw== - dependencies: - "@isaacs/cliui" "^8.0.2" - optionalDependencies: - "@pkgjs/parseargs" "^0.11.0" - -jiti@^2.4.2: - version "2.4.2" - resolved "https://registry.npmjs.org/jiti/-/jiti-2.4.2.tgz#d19b7732ebb6116b06e2038da74a55366faef560" - integrity sha512-rg9zJN+G4n2nfJl5MW3BMygZX56zKPNVEYYqq7adpmMh4Jn2QNEwhvQlFy6jPVdcod7txZtKHWnyZiA3a0zP7A== - -js-tokens@^4.0.0: - version "4.0.0" - resolved "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz#19203fb59991df98e3a287050d4647cdeaf32499" - integrity sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ== - -jsesc@3.0.2: - version "3.0.2" - resolved "https://registry.npmjs.org/jsesc/-/jsesc-3.0.2.tgz#bb8b09a6597ba426425f2e4a07245c3d00b9343e" - integrity sha512-xKqzzWXDttJuOcawBt4KnKHHIf5oQ/Cxax+0PWFG+DFDgHNAdi+TXECADI+RYiFUMmx8792xsMbbgXj4CwnP4g== - -jsesc@^3.0.2: - version "3.1.0" - resolved "https://registry.npmjs.org/jsesc/-/jsesc-3.1.0.tgz#74d335a234f67ed19907fdadfac7ccf9d409825d" - integrity sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA== - -json-parse-even-better-errors@^3.0.0: - version "3.0.2" - resolved "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-3.0.2.tgz#b43d35e89c0f3be6b5fbbe9dc6c82467b30c28da" - integrity sha512-fi0NG4bPjCHunUJffmLd0gxssIgkNmArMvis4iNah6Owg1MCJjWhEcDLmsK6iGkJq3tHwbDkTlce70/tmXN4cQ== - -json5@^2.2.3: - version "2.2.3" - resolved "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz#78cd6f1a19bdc12b73db5ad0c61efd66c1e29283" - integrity sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg== - -jsonfile@^6.0.1: - version "6.1.0" - resolved "https://registry.npmjs.org/jsonfile/-/jsonfile-6.1.0.tgz#bc55b2634793c679ec6403094eb13698a6ec0aae" - integrity sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ== - dependencies: - universalify "^2.0.0" - optionalDependencies: - graceful-fs "^4.1.6" - -lightningcss-darwin-arm64@1.30.1: - version "1.30.1" - resolved "https://registry.npmjs.org/lightningcss-darwin-arm64/-/lightningcss-darwin-arm64-1.30.1.tgz#3d47ce5e221b9567c703950edf2529ca4a3700ae" - integrity sha512-c8JK7hyE65X1MHMN+Viq9n11RRC7hgin3HhYKhrMyaXflk5GVplZ60IxyoVtzILeKr+xAJwg6zK6sjTBJ0FKYQ== - -lightningcss-darwin-x64@1.30.1: - version "1.30.1" - resolved "https://registry.npmjs.org/lightningcss-darwin-x64/-/lightningcss-darwin-x64-1.30.1.tgz#e81105d3fd6330860c15fe860f64d39cff5fbd22" - integrity sha512-k1EvjakfumAQoTfcXUcHQZhSpLlkAuEkdMBsI/ivWw9hL+7FtilQc0Cy3hrx0AAQrVtQAbMI7YjCgYgvn37PzA== - -lightningcss-freebsd-x64@1.30.1: - version "1.30.1" - resolved "https://registry.npmjs.org/lightningcss-freebsd-x64/-/lightningcss-freebsd-x64-1.30.1.tgz#a0e732031083ff9d625c5db021d09eb085af8be4" - integrity sha512-kmW6UGCGg2PcyUE59K5r0kWfKPAVy4SltVeut+umLCFoJ53RdCUWxcRDzO1eTaxf/7Q2H7LTquFHPL5R+Gjyig== - -lightningcss-linux-arm-gnueabihf@1.30.1: - version "1.30.1" - resolved "https://registry.npmjs.org/lightningcss-linux-arm-gnueabihf/-/lightningcss-linux-arm-gnueabihf-1.30.1.tgz#1f5ecca6095528ddb649f9304ba2560c72474908" - integrity sha512-MjxUShl1v8pit+6D/zSPq9S9dQ2NPFSQwGvxBCYaBYLPlCWuPh9/t1MRS8iUaR8i+a6w7aps+B4N0S1TYP/R+Q== - -lightningcss-linux-arm64-gnu@1.30.1: - version "1.30.1" - resolved "https://registry.npmjs.org/lightningcss-linux-arm64-gnu/-/lightningcss-linux-arm64-gnu-1.30.1.tgz#eee7799726103bffff1e88993df726f6911ec009" - integrity sha512-gB72maP8rmrKsnKYy8XUuXi/4OctJiuQjcuqWNlJQ6jZiWqtPvqFziskH3hnajfvKB27ynbVCucKSm2rkQp4Bw== - -lightningcss-linux-arm64-musl@1.30.1: - version "1.30.1" - resolved "https://registry.npmjs.org/lightningcss-linux-arm64-musl/-/lightningcss-linux-arm64-musl-1.30.1.tgz#f2e4b53f42892feeef8f620cbb889f7c064a7dfe" - integrity sha512-jmUQVx4331m6LIX+0wUhBbmMX7TCfjF5FoOH6SD1CttzuYlGNVpA7QnrmLxrsub43ClTINfGSYyHe2HWeLl5CQ== - -lightningcss-linux-x64-gnu@1.30.1: - version "1.30.1" - resolved "https://registry.npmjs.org/lightningcss-linux-x64-gnu/-/lightningcss-linux-x64-gnu-1.30.1.tgz#2fc7096224bc000ebb97eea94aea248c5b0eb157" - integrity sha512-piWx3z4wN8J8z3+O5kO74+yr6ze/dKmPnI7vLqfSqI8bccaTGY5xiSGVIJBDd5K5BHlvVLpUB3S2YCfelyJ1bw== - -lightningcss-linux-x64-musl@1.30.1: - version "1.30.1" - resolved "https://registry.npmjs.org/lightningcss-linux-x64-musl/-/lightningcss-linux-x64-musl-1.30.1.tgz#66dca2b159fd819ea832c44895d07e5b31d75f26" - integrity sha512-rRomAK7eIkL+tHY0YPxbc5Dra2gXlI63HL+v1Pdi1a3sC+tJTcFrHX+E86sulgAXeI7rSzDYhPSeHHjqFhqfeQ== - -lightningcss-win32-arm64-msvc@1.30.1: - version "1.30.1" - resolved "https://registry.npmjs.org/lightningcss-win32-arm64-msvc/-/lightningcss-win32-arm64-msvc-1.30.1.tgz#7d8110a19d7c2d22bfdf2f2bb8be68e7d1b69039" - integrity sha512-mSL4rqPi4iXq5YVqzSsJgMVFENoa4nGTT/GjO2c0Yl9OuQfPsIfncvLrEW6RbbB24WtZ3xP/2CCmI3tNkNV4oA== - -lightningcss-win32-x64-msvc@1.30.1: - version "1.30.1" - resolved "https://registry.npmjs.org/lightningcss-win32-x64-msvc/-/lightningcss-win32-x64-msvc-1.30.1.tgz#fd7dd008ea98494b85d24b4bea016793f2e0e352" - integrity sha512-PVqXh48wh4T53F/1CCu8PIPCxLzWyCnn/9T5W1Jpmdy5h9Cwd+0YQS6/LwhHXSafuc61/xg9Lv5OrCby6a++jg== - -lightningcss@1.30.1: - version "1.30.1" - resolved "https://registry.npmjs.org/lightningcss/-/lightningcss-1.30.1.tgz#78e979c2d595bfcb90d2a8c0eb632fe6c5bfed5d" - integrity sha512-xi6IyHML+c9+Q3W0S4fCQJOym42pyurFiJUHEcEyHS0CeKzia4yZDEsLlqOFykxOdHpNy0NmvVO31vcSqAxJCg== - dependencies: - detect-libc "^2.0.3" - optionalDependencies: - lightningcss-darwin-arm64 "1.30.1" - lightningcss-darwin-x64 "1.30.1" - lightningcss-freebsd-x64 "1.30.1" - lightningcss-linux-arm-gnueabihf "1.30.1" - lightningcss-linux-arm64-gnu "1.30.1" - lightningcss-linux-arm64-musl "1.30.1" - lightningcss-linux-x64-gnu "1.30.1" - lightningcss-linux-x64-musl "1.30.1" - lightningcss-win32-arm64-msvc "1.30.1" - lightningcss-win32-x64-msvc "1.30.1" - -lodash@^4.17.21: - version "4.17.21" - resolved "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz#679591c564c3bffaae8454cf0b3df370c3d6911c" - integrity sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg== - -lru-cache@^10.2.0: - version "10.4.3" - resolved "https://registry.npmjs.org/lru-cache/-/lru-cache-10.4.3.tgz#410fc8a17b70e598013df257c2446b7f3383f119" - integrity sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ== - -lru-cache@^5.1.1: - version "5.1.1" - resolved "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz#1da27e6710271947695daf6848e847f01d84b920" - integrity sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w== - dependencies: - yallist "^3.0.2" - -lru-cache@^7.4.4, lru-cache@^7.5.1: - version "7.18.3" - resolved "https://registry.npmjs.org/lru-cache/-/lru-cache-7.18.3.tgz#f793896e0fd0e954a59dfdd82f0773808df6aa89" - integrity sha512-jumlc0BIUrS3qJGgIkWZsyfAM7NCWiBcCDhnd+3NNM5KbBmLTgHVfWBcg6W+rLUsIpzpERPsvwUP7CckAQSOoA== - -magic-string@^0.30.17: - version "0.30.17" - resolved "https://registry.npmjs.org/magic-string/-/magic-string-0.30.17.tgz#450a449673d2460e5bbcfba9a61916a1714c7453" - integrity sha512-sNPKHvyjVf7gyjwS4xGTaW/mCnF8wnjtifKBEhxfZ7E/S8tQ0rssrwGNn6q8JH/ohItJfSQp9mBtQYuTlH5QnA== - dependencies: - "@jridgewell/sourcemap-codec" "^1.5.0" - -math-intrinsics@^1.1.0: - version "1.1.0" - resolved "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz#a0dd74be81e2aa5c2f27e65ce283605ee4e2b7f9" - integrity sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g== - -media-typer@0.3.0: - version "0.3.0" - resolved "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz#8710d7af0aa626f8fffa1ce00168545263255748" - integrity sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ== - -merge-descriptors@1.0.3: - version "1.0.3" - resolved "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.3.tgz#d80319a65f3c7935351e5cfdac8f9318504dbed5" - integrity sha512-gaNvAS7TZ897/rVaZ0nMtAyxNyi/pdbjbAwUpFQpN70GqnVfOiXpeUUMKRBmzXaSQ8DdTX4/0ms62r2K+hE6mQ== - -methods@~1.1.2: - version "1.1.2" - resolved "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz#5529a4d67654134edcc5266656835b0f851afcee" - integrity sha512-iclAHeNqNm68zFtnZ0e+1L2yUIdvzNoauKU4WBA3VvH/vPFieF7qfRlwUZU+DA9P9bPXIS90ulxoUoCH23sV2w== - -mime-db@1.52.0: - version "1.52.0" - resolved "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz#bbabcdc02859f4987301c856e3387ce5ec43bf70" - integrity sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg== - -"mime-db@>= 1.43.0 < 2": - version "1.54.0" - resolved "https://registry.npmjs.org/mime-db/-/mime-db-1.54.0.tgz#cddb3ee4f9c64530dff640236661d42cb6a314f5" - integrity sha512-aU5EJuIN2WDemCcAp2vFBfp/m4EAhWJnUNSSw0ixs7/kXbd6Pg64EmwJkNdFhB8aWt1sH2CTXrLxo/iAGV3oPQ== - -mime-types@~2.1.24, mime-types@~2.1.34: - version "2.1.35" - resolved "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz#381a871b62a734450660ae3deee44813f70d959a" - integrity sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw== - dependencies: - mime-db "1.52.0" - -mime@1.6.0: - version "1.6.0" - resolved "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz#32cd9e5c64553bd58d19a568af452acff04981b1" - integrity sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg== - -minimatch@^9.0.4: - version "9.0.5" - resolved "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz#d74f9dd6b57d83d8e98cfb82133b03978bc929e5" - integrity sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow== - dependencies: - brace-expansion "^2.0.1" - -"minipass@^5.0.0 || ^6.0.2 || ^7.0.0", minipass@^7.0.4, minipass@^7.1.2: - version "7.1.2" - resolved "https://registry.npmjs.org/minipass/-/minipass-7.1.2.tgz#93a9626ce5e5e66bd4db86849e7515e92340a707" - integrity sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw== - -minizlib@^3.0.1: - version "3.0.2" - resolved "https://registry.npmjs.org/minizlib/-/minizlib-3.0.2.tgz#f33d638eb279f664439aa38dc5f91607468cb574" - integrity sha512-oG62iEk+CYt5Xj2YqI5Xi9xWUeZhDI8jjQmC5oThVH5JGCTgIjr7ciJDzC7MBzYd//WvR1OTmP5Q38Q8ShQtVA== - dependencies: - minipass "^7.1.2" - -mkdirp@^3.0.1: - version "3.0.1" - resolved "https://registry.npmjs.org/mkdirp/-/mkdirp-3.0.1.tgz#e44e4c5607fb279c168241713cc6e0fea9adcb50" - integrity sha512-+NsyUUAZDmo6YVHzL/stxSu3t9YS1iljliy3BSDrXJ/dkn1KYdmtZODGGjLcc9XLgVVpH4KshHB8XmZgMhaBXg== - -morgan@^1.10.0: - version "1.10.0" - resolved "https://registry.npmjs.org/morgan/-/morgan-1.10.0.tgz#091778abc1fc47cd3509824653dae1faab6b17d7" - integrity sha512-AbegBVI4sh6El+1gNwvD5YIck7nSA36weD7xvIxG4in80j/UoK8AEGaWnnz8v1GxonMCltmlNs5ZKbGvl9b1XQ== - dependencies: - basic-auth "~2.0.1" - debug "2.6.9" - depd "~2.0.0" - on-finished "~2.3.0" - on-headers "~1.0.2" - -ms@2.0.0: - version "2.0.0" - resolved "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz#5608aeadfc00be6c2901df5f9861788de0d597c8" - integrity sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A== - -ms@2.1.3, ms@^2.1.3: - version "2.1.3" - resolved "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz#574c8138ce1d2b5861f0b44579dbadd60c6615b2" - integrity sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA== - -nanoid@^3.3.8: - version "3.3.11" - resolved "https://registry.npmjs.org/nanoid/-/nanoid-3.3.11.tgz#4f4f112cefbe303202f2199838128936266d185b" - integrity sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w== - -negotiator@0.6.3: - version "0.6.3" - resolved "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz#58e323a72fedc0d6f9cd4d31fe49f51479590ccd" - integrity sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg== - -negotiator@~0.6.4: - version "0.6.4" - resolved "https://registry.npmjs.org/negotiator/-/negotiator-0.6.4.tgz#777948e2452651c570b712dd01c23e262713fff7" - integrity sha512-myRT3DiWPHqho5PrJaIRyaMv2kgYf0mUVgBNOYMuCH5Ki1yEiQaf/ZJuQ62nvpc44wL5WDbTX7yGJi1Neevw8w== - -node-releases@^2.0.19: - version "2.0.19" - resolved "https://registry.npmjs.org/node-releases/-/node-releases-2.0.19.tgz#9e445a52950951ec4d177d843af370b411caf314" - integrity sha512-xxOWJsBKtzAq7DY0J+DTzuz58K8e7sJbdgwkbMWQe8UYB6ekmsQ45q0M/tJDsGaZmbC+l7n57UV8Hl5tHxO9uw== - -normalize-package-data@^5.0.0: - version "5.0.0" - resolved "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-5.0.0.tgz#abcb8d7e724c40d88462b84982f7cbf6859b4588" - integrity sha512-h9iPVIfrVZ9wVYQnxFgtw1ugSvGEMOlyPWWtm8BMJhnwyEL/FLbYbTY3V3PpjI/BUK67n9PEWDu6eHzu1fB15Q== - dependencies: - hosted-git-info "^6.0.0" - is-core-module "^2.8.1" - semver "^7.3.5" - validate-npm-package-license "^3.0.4" - -npm-install-checks@^6.0.0: - version "6.3.0" - resolved "https://registry.npmjs.org/npm-install-checks/-/npm-install-checks-6.3.0.tgz#046552d8920e801fa9f919cad569545d60e826fe" - integrity sha512-W29RiK/xtpCGqn6f3ixfRYGk+zRyr+Ew9F2E20BfXxT5/euLdA/Nm7fO7OeTGuAmTs30cpgInyJ0cYe708YTZw== - dependencies: - semver "^7.1.1" - -npm-normalize-package-bin@^3.0.0: - version "3.0.1" - resolved "https://registry.npmjs.org/npm-normalize-package-bin/-/npm-normalize-package-bin-3.0.1.tgz#25447e32a9a7de1f51362c61a559233b89947832" - integrity sha512-dMxCf+zZ+3zeQZXKxmyuCKlIDPGuv8EF940xbkC4kQVDTtqoh6rJFO+JTKSA6/Rwi0getWmtuy4Itup0AMcaDQ== - -npm-package-arg@^10.0.0: - version "10.1.0" - resolved "https://registry.npmjs.org/npm-package-arg/-/npm-package-arg-10.1.0.tgz#827d1260a683806685d17193073cc152d3c7e9b1" - integrity sha512-uFyyCEmgBfZTtrKk/5xDfHp6+MdrqGotX/VoOyEEl3mBwiEE5FlBaePanazJSVMPT7vKepcjYBY2ztg9A3yPIA== - dependencies: - hosted-git-info "^6.0.0" - proc-log "^3.0.0" - semver "^7.3.5" - validate-npm-package-name "^5.0.0" - -npm-pick-manifest@^8.0.0: - version "8.0.2" - resolved "https://registry.npmjs.org/npm-pick-manifest/-/npm-pick-manifest-8.0.2.tgz#2159778d9c7360420c925c1a2287b5a884c713aa" - integrity sha512-1dKY+86/AIiq1tkKVD3l0WI+Gd3vkknVGAggsFeBkTvbhMQ1OND/LKkYv4JtXPKUJ8bOTCyLiqEg2P6QNdK+Gg== - dependencies: - npm-install-checks "^6.0.0" - npm-normalize-package-bin "^3.0.0" - npm-package-arg "^10.0.0" - semver "^7.3.5" - -object-inspect@^1.13.3: - version "1.13.4" - resolved "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.4.tgz#8375265e21bc20d0fa582c22e1b13485d6e00213" - integrity sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew== - -on-finished@2.4.1: - version "2.4.1" - resolved "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz#58c8c44116e54845ad57f14ab10b03533184ac3f" - integrity sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg== - dependencies: - ee-first "1.1.1" - -on-finished@~2.3.0: - version "2.3.0" - resolved "https://registry.npmjs.org/on-finished/-/on-finished-2.3.0.tgz#20f1336481b083cd75337992a16971aa2d906947" - integrity sha512-ikqdkGAAyf/X/gPhXGvfgAytDZtDbr+bkNUJ0N9h5MI/dmdgCs3l6hoHrcUv41sRKew3jIwrp4qQDXiK99Utww== - dependencies: - ee-first "1.1.1" - -on-headers@~1.0.2: - version "1.0.2" - resolved "https://registry.npmjs.org/on-headers/-/on-headers-1.0.2.tgz#772b0ae6aaa525c399e489adfad90c403eb3c28f" - integrity sha512-pZAE+FJLoyITytdqK0U5s+FIpjN0JP3OzFi/u8Rx+EV5/W+JTWGXG8xFzevE7AjBfDqHv/8vL8qQsIhHnqRkrA== - -package-json-from-dist@^1.0.0: - version "1.0.1" - resolved "https://registry.npmjs.org/package-json-from-dist/-/package-json-from-dist-1.0.1.tgz#4f1471a010827a86f94cfd9b0727e36d267de505" - integrity sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw== - -parseurl@~1.3.3: - version "1.3.3" - resolved "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz#9da19e7bee8d12dff0513ed5b76957793bc2e8d4" - integrity sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ== - -path-key@^3.1.0: - version "3.1.1" - resolved "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz#581f6ade658cbba65a0d3380de7753295054f375" - integrity sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q== - -path-scurry@^1.11.1: - version "1.11.1" - resolved "https://registry.npmjs.org/path-scurry/-/path-scurry-1.11.1.tgz#7960a668888594a0720b12a911d1a742ab9f11d2" - integrity sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA== - dependencies: - lru-cache "^10.2.0" - minipass "^5.0.0 || ^6.0.2 || ^7.0.0" - -path-to-regexp@0.1.12: - version "0.1.12" - resolved "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.12.tgz#d5e1a12e478a976d432ef3c58d534b9923164bb7" - integrity sha512-RA1GjUVMnvYFxuqovrEqZoxxW5NUZqbwKtYz/Tt7nXerk0LbLblQmrsgdeOxV5SFHf0UDggjS/bSeOZwt1pmEQ== - -pathe@^1.1.2: - version "1.1.2" - resolved "https://registry.npmjs.org/pathe/-/pathe-1.1.2.tgz#6c4cb47a945692e48a1ddd6e4094d170516437ec" - integrity sha512-whLdWMYL2TwI08hn8/ZqAbrVemu0LNaNNJZX73O6qaIdCTfXutsLhMkjdENX0qhsQ9uIimo4/aQOmXkoon2nDQ== - -picocolors@^1.1.1: - version "1.1.1" - resolved "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz#3d321af3eab939b083c8f929a1d12cda81c26b6b" - integrity sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA== - -picomatch@^4.0.2: - version "4.0.2" - resolved "https://registry.npmjs.org/picomatch/-/picomatch-4.0.2.tgz#77c742931e8f3b8820946c76cd0c1f13730d1dab" - integrity sha512-M7BAV6Rlcy5u+m6oPhAPFgJTzAioX/6B0DxyvDlo9l8+T3nLKbrczg2WLUyzd45L8RqfUMyGPzekbMvX2Ldkwg== - -postcss@^8.5.3: - version "8.5.3" - resolved "https://registry.npmjs.org/postcss/-/postcss-8.5.3.tgz#1463b6f1c7fb16fe258736cba29a2de35237eafb" - integrity sha512-dle9A3yYxlBSrt8Fu+IpjGT8SY8hN0mlaA6GY8t0P5PjIOZemULz/E2Bnm/2dcUOena75OTNkHI76uZBNUUq3A== - dependencies: - nanoid "^3.3.8" - picocolors "^1.1.1" - source-map-js "^1.2.1" - -prettier@^2.7.1: - version "2.8.8" - resolved "https://registry.npmjs.org/prettier/-/prettier-2.8.8.tgz#e8c5d7e98a4305ffe3de2e1fc4aca1a71c28b1da" - integrity sha512-tdN8qQGvNjw4CHbY+XXk0JgCXn9QiF21a55rBe5LJAU+kDyC4WQn4+awm2Xfk2lQMk5fKup9XgzTZtGkjBdP9Q== - -proc-log@^3.0.0: - version "3.0.0" - resolved "https://registry.npmjs.org/proc-log/-/proc-log-3.0.0.tgz#fb05ef83ccd64fd7b20bbe9c8c1070fc08338dd8" - integrity sha512-++Vn7NS4Xf9NacaU9Xq3URUuqZETPsf8L4j5/ckhaRYsfPeRyzGw+iDjFhV/Jr3uNmTvvddEJFWh5R1gRgUH8A== - -promise-inflight@^1.0.1: - version "1.0.1" - resolved "https://registry.npmjs.org/promise-inflight/-/promise-inflight-1.0.1.tgz#98472870bf228132fcbdd868129bad12c3c029e3" - integrity sha512-6zWPyEOFaQBJYcGMHBKTKJ3u6TBsnMFOIZSa6ce1e/ZrrsOlnHRHbabMjLiBYKp+n44X9eUI6VUPaukCXHuG4g== - -promise-retry@^2.0.1: - version "2.0.1" - resolved "https://registry.npmjs.org/promise-retry/-/promise-retry-2.0.1.tgz#ff747a13620ab57ba688f5fc67855410c370da22" - integrity sha512-y+WKFlBR8BGXnsNlIHFGPZmyDf3DFMoLhaflAnyZgV6rG6xu+JwesTo2Q9R6XwYmtmwAFCkAk3e35jEdoeh/3g== - dependencies: - err-code "^2.0.2" - retry "^0.12.0" - -proxy-addr@~2.0.7: - version "2.0.7" - resolved "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz#f19fe69ceab311eeb94b42e70e8c2070f9ba1025" - integrity sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg== - dependencies: - forwarded "0.2.0" - ipaddr.js "1.9.1" - -qs@6.13.0: - version "6.13.0" - resolved "https://registry.npmjs.org/qs/-/qs-6.13.0.tgz#6ca3bd58439f7e245655798997787b0d88a51906" - integrity sha512-+38qI9SOr8tfZ4QmJNplMUxqjbe7LKvvZgWdExBOmd+egZTtjLB67Gu0HRX3u/XOq7UU2Nx6nsjvS16Z9uwfpg== - dependencies: - side-channel "^1.0.6" - -range-parser@~1.2.1: - version "1.2.1" - resolved "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz#3cf37023d199e1c24d1a55b84800c2f3e6468031" - integrity sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg== - -raw-body@2.5.2: - version "2.5.2" - resolved "https://registry.npmjs.org/raw-body/-/raw-body-2.5.2.tgz#99febd83b90e08975087e8f1f9419a149366b68a" - integrity sha512-8zGqypfENjCIqGhgXToC8aB2r7YrBX+AQAfIPs/Mlk+BtPTztOvTS01NRW/3Eh60J+a48lt8qsCzirQ6loCVfA== - dependencies: - bytes "3.1.2" - http-errors "2.0.0" - iconv-lite "0.4.24" - unpipe "1.0.0" - -react-dom@^19.1.0: - version "19.1.0" - resolved "https://registry.npmjs.org/react-dom/-/react-dom-19.1.0.tgz#133558deca37fa1d682708df8904b25186793623" - integrity sha512-Xs1hdnE+DyKgeHJeJznQmYMIBG3TKIHJJT95Q58nHLSrElKlGQqDTR2HQ9fx5CN/Gk6Vh/kupBTDLU11/nDk/g== - dependencies: - scheduler "^0.26.0" - -react-refresh@^0.14.0: - version "0.14.2" - resolved "https://registry.npmjs.org/react-refresh/-/react-refresh-0.14.2.tgz#3833da01ce32da470f1f936b9d477da5c7028bf9" - integrity sha512-jCvmsr+1IUSMUyzOkRcvnVbX3ZYC6g9TDrDbFuFmRDq7PD4yaGbLKNQL6k2jnArV8hjYxh7hVhAZB6s9HDGpZA== - -react-router@^7.5.3: - version "7.6.0" - resolved "https://registry.npmjs.org/react-router/-/react-router-7.6.0.tgz#e2d0872d7bea8df79465a8bba9a20c87c32ce995" - integrity sha512-GGufuHIVCJDbnIAXP3P9Sxzq3UUsddG3rrI3ut1q6m0FI6vxVBF3JoPQ38+W/blslLH4a5Yutp8drkEpXoddGQ== - dependencies: - cookie "^1.0.1" - set-cookie-parser "^2.6.0" - -react@^19.1.0: - version "19.1.0" - resolved "https://registry.npmjs.org/react/-/react-19.1.0.tgz#926864b6c48da7627f004795d6cce50e90793b75" - integrity sha512-FS+XFBNvn3GTAWq26joslQgWNoFu08F4kl0J4CgdNKADkdSGXQyTCnKteIAJy96Br6YbpEU1LSzV5dYtjMkMDg== - -readdirp@^4.0.1: - version "4.1.2" - resolved "https://registry.npmjs.org/readdirp/-/readdirp-4.1.2.tgz#eb85801435fbf2a7ee58f19e0921b068fc69948d" - integrity sha512-GDhwkLfywWL2s6vEjyhri+eXmfH6j1L7JE27WhqLeYzoh/A3DBaYGEj2H/HFZCn/kMfim73FXxEJTw06WtxQwg== - -retry@^0.12.0: - version "0.12.0" - resolved "https://registry.npmjs.org/retry/-/retry-0.12.0.tgz#1b42a6266a21f07421d1b0b54b7dc167b01c013b" - integrity sha512-9LkiTwjUh6rT555DtE9rTX+BKByPfrMzEAtnlEtdEwr3Nkffwiihqe2bWADg+OQRjt9gl6ICdmB/ZFDCGAtSow== - -rollup@^4.34.9: - version "4.41.0" - resolved "https://registry.npmjs.org/rollup/-/rollup-4.41.0.tgz#17476835d2967759e3ffebe5823ed15fc4b7d13e" - integrity sha512-HqMFpUbWlf/tvcxBFNKnJyzc7Lk+XO3FGc3pbNBLqEbOz0gPLRgcrlS3UF4MfUrVlstOaP/q0kM6GVvi+LrLRg== - dependencies: - "@types/estree" "1.0.7" - optionalDependencies: - "@rollup/rollup-android-arm-eabi" "4.41.0" - "@rollup/rollup-android-arm64" "4.41.0" - "@rollup/rollup-darwin-arm64" "4.41.0" - "@rollup/rollup-darwin-x64" "4.41.0" - "@rollup/rollup-freebsd-arm64" "4.41.0" - "@rollup/rollup-freebsd-x64" "4.41.0" - "@rollup/rollup-linux-arm-gnueabihf" "4.41.0" - "@rollup/rollup-linux-arm-musleabihf" "4.41.0" - "@rollup/rollup-linux-arm64-gnu" "4.41.0" - "@rollup/rollup-linux-arm64-musl" "4.41.0" - "@rollup/rollup-linux-loongarch64-gnu" "4.41.0" - "@rollup/rollup-linux-powerpc64le-gnu" "4.41.0" - "@rollup/rollup-linux-riscv64-gnu" "4.41.0" - "@rollup/rollup-linux-riscv64-musl" "4.41.0" - "@rollup/rollup-linux-s390x-gnu" "4.41.0" - "@rollup/rollup-linux-x64-gnu" "4.41.0" - "@rollup/rollup-linux-x64-musl" "4.41.0" - "@rollup/rollup-win32-arm64-msvc" "4.41.0" - "@rollup/rollup-win32-ia32-msvc" "4.41.0" - "@rollup/rollup-win32-x64-msvc" "4.41.0" - fsevents "~2.3.2" - -safe-buffer@5.1.2: - version "5.1.2" - resolved "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz#991ec69d296e0313747d59bdfd2b745c35f8828d" - integrity sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g== - -safe-buffer@5.2.1: - version "5.2.1" - resolved "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz#1eaf9fa9bdb1fdd4ec75f58f9cdb4e6b7827eec6" - integrity sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ== - -"safer-buffer@>= 2.1.2 < 3": - version "2.1.2" - resolved "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz#44fa161b0187b9549dd84bb91802f9bd8385cd6a" - integrity sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg== - -scheduler@^0.26.0: - version "0.26.0" - resolved "https://registry.npmjs.org/scheduler/-/scheduler-0.26.0.tgz#4ce8a8c2a2095f13ea11bf9a445be50c555d6337" - integrity sha512-NlHwttCI/l5gCPR3D1nNXtWABUmBwvZpEQiD4IXSbIDq8BzLIK/7Ir5gTFSGZDUu37K5cMNp0hFtzO38sC7gWA== - -semver@^6.3.1: - version "6.3.1" - resolved "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz#556d2ef8689146e46dcea4bfdd095f3434dffcb4" - integrity sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA== - -semver@^7.1.1, semver@^7.3.5, semver@^7.3.7, semver@^7.5.3: - version "7.7.2" - resolved "https://registry.npmjs.org/semver/-/semver-7.7.2.tgz#67d99fdcd35cec21e6f8b87a7fd515a33f982b58" - integrity sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA== - -send@0.19.0: - version "0.19.0" - resolved "https://registry.npmjs.org/send/-/send-0.19.0.tgz#bbc5a388c8ea6c048967049dbeac0e4a3f09d7f8" - integrity sha512-dW41u5VfLXu8SJh5bwRmyYUbAoSB3c9uQh6L8h/KtsFREPWpbX1lrljJo186Jc4nmci/sGUZ9a0a0J2zgfq2hw== - dependencies: - debug "2.6.9" - depd "2.0.0" - destroy "1.2.0" - encodeurl "~1.0.2" - escape-html "~1.0.3" - etag "~1.8.1" - fresh "0.5.2" - http-errors "2.0.0" - mime "1.6.0" - ms "2.1.3" - on-finished "2.4.1" - range-parser "~1.2.1" - statuses "2.0.1" - -serve-static@1.16.2: - version "1.16.2" - resolved "https://registry.npmjs.org/serve-static/-/serve-static-1.16.2.tgz#b6a5343da47f6bdd2673848bf45754941e803296" - integrity sha512-VqpjJZKadQB/PEbEwvFdO43Ax5dFBZ2UECszz8bQ7pi7wt//PWe1P6MN7eCnjsatYtBT6EuiClbjSWP2WrIoTw== - dependencies: - encodeurl "~2.0.0" - escape-html "~1.0.3" - parseurl "~1.3.3" - send "0.19.0" - -set-cookie-parser@^2.6.0: - version "2.7.1" - resolved "https://registry.npmjs.org/set-cookie-parser/-/set-cookie-parser-2.7.1.tgz#3016f150072202dfbe90fadee053573cc89d2943" - integrity sha512-IOc8uWeOZgnb3ptbCURJWNjWUPcO3ZnTTdzsurqERrP6nPyv+paC55vJM0LpOlT2ne+Ix+9+CRG1MNLlyZ4GjQ== - -setprototypeof@1.2.0: - version "1.2.0" - resolved "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz#66c9a24a73f9fc28cbe66b09fed3d33dcaf1b424" - integrity sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw== - -shebang-command@^2.0.0: - version "2.0.0" - resolved "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz#ccd0af4f8835fbdc265b82461aaf0c36663f34ea" - integrity sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA== - dependencies: - shebang-regex "^3.0.0" - -shebang-regex@^3.0.0: - version "3.0.0" - resolved "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz#ae16f1644d873ecad843b0307b143362d4c42172" - integrity sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A== - -side-channel-list@^1.0.0: - version "1.0.0" - resolved "https://registry.npmjs.org/side-channel-list/-/side-channel-list-1.0.0.tgz#10cb5984263115d3b7a0e336591e290a830af8ad" - integrity sha512-FCLHtRD/gnpCiCHEiJLOwdmFP+wzCmDEkc9y7NsYxeF4u7Btsn1ZuwgwJGxImImHicJArLP4R0yX4c2KCrMrTA== - dependencies: - es-errors "^1.3.0" - object-inspect "^1.13.3" - -side-channel-map@^1.0.1: - version "1.0.1" - resolved "https://registry.npmjs.org/side-channel-map/-/side-channel-map-1.0.1.tgz#d6bb6b37902c6fef5174e5f533fab4c732a26f42" - integrity sha512-VCjCNfgMsby3tTdo02nbjtM/ewra6jPHmpThenkTYh8pG9ucZ/1P8So4u4FGBek/BjpOVsDCMoLA/iuBKIFXRA== - dependencies: - call-bound "^1.0.2" - es-errors "^1.3.0" - get-intrinsic "^1.2.5" - object-inspect "^1.13.3" - -side-channel-weakmap@^1.0.2: - version "1.0.2" - resolved "https://registry.npmjs.org/side-channel-weakmap/-/side-channel-weakmap-1.0.2.tgz#11dda19d5368e40ce9ec2bdc1fb0ecbc0790ecea" - integrity sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A== - dependencies: - call-bound "^1.0.2" - es-errors "^1.3.0" - get-intrinsic "^1.2.5" - object-inspect "^1.13.3" - side-channel-map "^1.0.1" - -side-channel@^1.0.6: - version "1.1.0" - resolved "https://registry.npmjs.org/side-channel/-/side-channel-1.1.0.tgz#c3fcff9c4da932784873335ec9765fa94ff66bc9" - integrity sha512-ZX99e6tRweoUXqR+VBrslhda51Nh5MTQwou5tnUDgbtyM0dBgmhEDtWGP/xbKn6hqfPRHujUNwz5fy/wbbhnpw== - dependencies: - es-errors "^1.3.0" - object-inspect "^1.13.3" - side-channel-list "^1.0.0" - side-channel-map "^1.0.1" - side-channel-weakmap "^1.0.2" - -signal-exit@^4.0.1: - version "4.1.0" - resolved "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz#952188c1cbd546070e2dd20d0f41c0ae0530cb04" - integrity sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw== - -source-map-js@^1.2.1: - version "1.2.1" - resolved "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz#1ce5650fddd87abc099eda37dcff024c2667ae46" - integrity sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA== - -source-map-support@^0.5.21: - version "0.5.21" - resolved "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.21.tgz#04fe7c7f9e1ed2d662233c28cb2b35b9f63f6e4f" - integrity sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w== - dependencies: - buffer-from "^1.0.0" - source-map "^0.6.0" - -source-map@^0.6.0: - version "0.6.1" - resolved "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz#74722af32e9614e9c287a8d0bbde48b5e2f1a263" - integrity sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g== - -spdx-correct@^3.0.0: - version "3.2.0" - resolved "https://registry.npmjs.org/spdx-correct/-/spdx-correct-3.2.0.tgz#4f5ab0668f0059e34f9c00dce331784a12de4e9c" - integrity sha512-kN9dJbvnySHULIluDHy32WHRUu3Og7B9sbY7tsFLctQkIqnMh3hErYgdMjTYuqmcXX+lK5T1lnUt3G7zNswmZA== - dependencies: - spdx-expression-parse "^3.0.0" - spdx-license-ids "^3.0.0" - -spdx-exceptions@^2.1.0: - version "2.5.0" - resolved "https://registry.npmjs.org/spdx-exceptions/-/spdx-exceptions-2.5.0.tgz#5d607d27fc806f66d7b64a766650fa890f04ed66" - integrity sha512-PiU42r+xO4UbUS1buo3LPJkjlO7430Xn5SVAhdpzzsPHsjbYVflnnFdATgabnLude+Cqu25p6N+g2lw/PFsa4w== - -spdx-expression-parse@^3.0.0: - version "3.0.1" - resolved "https://registry.npmjs.org/spdx-expression-parse/-/spdx-expression-parse-3.0.1.tgz#cf70f50482eefdc98e3ce0a6833e4a53ceeba679" - integrity sha512-cbqHunsQWnJNE6KhVSMsMeH5H/L9EpymbzqTQ3uLwNCLZ1Q481oWaofqH7nO6V07xlXwY6PhQdQ2IedWx/ZK4Q== - dependencies: - spdx-exceptions "^2.1.0" - spdx-license-ids "^3.0.0" - -spdx-license-ids@^3.0.0: - version "3.0.21" - resolved "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.21.tgz#6d6e980c9df2b6fc905343a3b2d702a6239536c3" - integrity sha512-Bvg/8F5XephndSK3JffaRqdT+gyhfqIPwDHpX80tJrF8QQRYMo8sNMeaZ2Dp5+jhwKnUmIOyFFQfHRkjJm5nXg== - -statuses@2.0.1: - version "2.0.1" - resolved "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz#55cb000ccf1d48728bd23c685a063998cf1a1b63" - integrity sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ== - -stream-slice@^0.1.2: - version "0.1.2" - resolved "https://registry.npmjs.org/stream-slice/-/stream-slice-0.1.2.tgz#2dc4f4e1b936fb13f3eb39a2def1932798d07a4b" - integrity sha512-QzQxpoacatkreL6jsxnVb7X5R/pGw9OUv2qWTYWnmLpg4NdN31snPy/f3TdQE1ZUXaThRvj1Zw4/OGg0ZkaLMA== - -"string-width-cjs@npm:string-width@^4.2.0", string-width@^4.1.0: - version "4.2.3" - resolved "https://registry.npmjs.org/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@^5.0.1, string-width@^5.1.2: - version "5.1.2" - resolved "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz#14f8daec6d81e7221d2a357e668cab73bdbca794" - integrity sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA== - dependencies: - eastasianwidth "^0.2.0" - emoji-regex "^9.2.2" - strip-ansi "^7.0.1" - -"strip-ansi-cjs@npm:strip-ansi@^6.0.1", strip-ansi@^6.0.0, strip-ansi@^6.0.1: - version "6.0.1" - resolved "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9" - integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A== - dependencies: - ansi-regex "^5.0.1" - -strip-ansi@^7.0.1: - version "7.1.0" - resolved "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz#d5b6568ca689d8561370b0707685d22434faff45" - integrity sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ== - dependencies: - ansi-regex "^6.0.1" - -tailwindcss@4.1.7, tailwindcss@^4.1.4: - version "4.1.7" - resolved "https://registry.npmjs.org/tailwindcss/-/tailwindcss-4.1.7.tgz#949f76d50667946ddd7291e0c7a4b5a7dfc9e765" - integrity sha512-kr1o/ErIdNhTz8uzAYL7TpaUuzKIE6QPQ4qmSdxnoX/lo+5wmUHQA6h3L5yIqEImSRnAAURDirLu/BgiXGPAhg== - -tapable@^2.2.0: - version "2.2.2" - resolved "https://registry.npmjs.org/tapable/-/tapable-2.2.2.tgz#ab4984340d30cb9989a490032f086dbb8b56d872" - integrity sha512-Re10+NauLTMCudc7T5WLFLAwDhQ0JWdrMK+9B2M8zR5hRExKmsRDCBA7/aV/pNJFltmBFO5BAMlQFi/vq3nKOg== - -tar@^7.4.3: - version "7.4.3" - resolved "https://registry.npmjs.org/tar/-/tar-7.4.3.tgz#88bbe9286a3fcd900e94592cda7a22b192e80571" - integrity sha512-5S7Va8hKfV7W5U6g3aYxXmlPoZVAwUMy9AOKyF2fVuZa2UD3qZjg578OrLRt8PcNN1PleVaL/5/yYATNL0ICUw== - dependencies: - "@isaacs/fs-minipass" "^4.0.0" - chownr "^3.0.0" - minipass "^7.1.2" - minizlib "^3.0.1" - mkdirp "^3.0.1" - yallist "^5.0.0" - -tinyglobby@^0.2.13: - version "0.2.13" - resolved "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.13.tgz#a0e46515ce6cbcd65331537e57484af5a7b2ff7e" - integrity sha512-mEwzpUgrLySlveBwEVDMKk5B57bhLPYovRfPAXD5gA/98Opn0rCDj3GtLwFvCvH5RK9uPCExUROW5NjDwvqkxw== - dependencies: - fdir "^6.4.4" - picomatch "^4.0.2" - -toidentifier@1.0.1: - version "1.0.1" - resolved "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz#3be34321a88a820ed1bd80dfaa33e479fbb8dd35" - integrity sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA== - -tsconfck@^3.0.3: - version "3.1.6" - resolved "https://registry.npmjs.org/tsconfck/-/tsconfck-3.1.6.tgz#da1f0b10d82237ac23422374b3fce1edb23c3ead" - integrity sha512-ks6Vjr/jEw0P1gmOVwutM3B7fWxoWBL2KRDb1JfqGVawBmO5UsvmWOQFGHBPl5yxYz4eERr19E6L7NMv+Fej4w== - -tslib@^2.4.0, tslib@^2.8.0: - version "2.8.1" - resolved "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz#612efe4ed235d567e8aba5f2a5fab70280ade83f" - integrity sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w== - -type-is@~1.6.18: - version "1.6.18" - resolved "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz#4e552cd05df09467dcbc4ef739de89f2cf37c131" - integrity sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g== - dependencies: - media-typer "0.3.0" - mime-types "~2.1.24" - -typescript@^5.8.3: - version "5.8.3" - resolved "https://registry.npmjs.org/typescript/-/typescript-5.8.3.tgz#92f8a3e5e3cf497356f4178c34cd65a7f5e8440e" - integrity sha512-p1diW6TqL9L07nNxvRMM7hMMw4c5XOo/1ibL4aAIGmSAt9slTE1Xgw5KWuof2uTOvCg9BY7ZRi+GaF+7sfgPeQ== - -undici-types@~6.19.2: - version "6.19.8" - resolved "https://registry.npmjs.org/undici-types/-/undici-types-6.19.8.tgz#35111c9d1437ab83a7cdc0abae2f26d88eda0a02" - integrity sha512-ve2KP6f/JnbPBFyobGHuerC9g1FYGn/F8n1LWTwNxCEzd6IfqTwUQcNXgEtmmQ6DlRrC1hrSrBnCZPokRrDHjw== - -undici@^6.19.2: - version "6.21.3" - resolved "https://registry.npmjs.org/undici/-/undici-6.21.3.tgz#185752ad92c3d0efe7a7d1f6854a50f83b552d7a" - integrity sha512-gBLkYIlEnSp8pFbT64yFgGE6UIB9tAkhukC23PmMDCe5Nd+cRqKxSjw5y54MK2AZMgZfJWMaNE4nYUHgi1XEOw== - -universalify@^2.0.0: - version "2.0.1" - resolved "https://registry.npmjs.org/universalify/-/universalify-2.0.1.tgz#168efc2180964e6386d061e094df61afe239b18d" - integrity sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw== - -unpipe@1.0.0, unpipe@~1.0.0: - version "1.0.0" - resolved "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz#b2bf4ee8514aae6165b4817829d21b2ef49904ec" - integrity sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ== - -update-browserslist-db@^1.1.3: - version "1.1.3" - resolved "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.1.3.tgz#348377dd245216f9e7060ff50b15a1b740b75420" - integrity sha512-UxhIZQ+QInVdunkDAaiazvvT/+fXL5Osr0JZlJulepYu6Jd7qJtDZjlur0emRlT71EN3ScPoE7gvsuIKKNavKw== - dependencies: - escalade "^3.2.0" - picocolors "^1.1.1" - -utils-merge@1.0.1: - version "1.0.1" - resolved "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz#9f95710f50a267947b2ccc124741c1028427e713" - integrity sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA== - -valibot@^0.41.0: - version "0.41.0" - resolved "https://registry.npmjs.org/valibot/-/valibot-0.41.0.tgz#5c2efd49c078e455f7862379365f6036f3cd9f96" - integrity sha512-igDBb8CTYr8YTQlOKgaN9nSS0Be7z+WRuaeYqGf3Cjz3aKmSnqEmYnkfVjzIuumGqfHpa3fLIvMEAfhrpqN8ng== - -validate-npm-package-license@^3.0.4: - version "3.0.4" - resolved "https://registry.npmjs.org/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz#fc91f6b9c7ba15c857f4cb2c5defeec39d4f410a" - integrity sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew== - dependencies: - spdx-correct "^3.0.0" - spdx-expression-parse "^3.0.0" - -validate-npm-package-name@^5.0.0: - version "5.0.1" - resolved "https://registry.npmjs.org/validate-npm-package-name/-/validate-npm-package-name-5.0.1.tgz#a316573e9b49f3ccd90dbb6eb52b3f06c6d604e8" - integrity sha512-OljLrQ9SQdOUqTaQxqL5dEfZWrXExyyWsozYlAWFawPVNuD83igl7uJD2RTkNMbniIYgt8l81eCJGIdQF7avLQ== - -vary@~1.1.2: - version "1.1.2" - resolved "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz#2299f02c6ded30d4a5961b0b9f74524a18f634fc" - integrity sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg== - -vite-node@3.0.0-beta.2: - version "3.0.0-beta.2" - resolved "https://registry.npmjs.org/vite-node/-/vite-node-3.0.0-beta.2.tgz#4208a6be384f9e7bba97570114d662ce9c957dc1" - integrity sha512-ofTf6cfRdL30Wbl9n/BX81EyIR5s4PReLmSurrxQ+koLaWUNOEo8E0lCM53OJkb8vpa2URM2nSrxZsIFyvY1rg== - dependencies: - cac "^6.7.14" - debug "^4.4.0" - es-module-lexer "^1.5.4" - pathe "^1.1.2" - vite "^5.0.0 || ^6.0.0" - -vite-tsconfig-paths@^5.1.4: - version "5.1.4" - resolved "https://registry.npmjs.org/vite-tsconfig-paths/-/vite-tsconfig-paths-5.1.4.tgz#d9a71106a7ff2c1c840c6f1708042f76a9212ed4" - integrity sha512-cYj0LRuLV2c2sMqhqhGpaO3LretdtMn/BVX4cPLanIZuwwrkVl+lK84E/miEXkCHWXuq65rhNN4rXsBcOB3S4w== - dependencies: - debug "^4.1.1" - globrex "^0.1.2" - tsconfck "^3.0.3" - -"vite@^5.0.0 || ^6.0.0", vite@^6.3.3: - version "6.3.5" - resolved "https://registry.npmjs.org/vite/-/vite-6.3.5.tgz#fec73879013c9c0128c8d284504c6d19410d12a3" - integrity sha512-cZn6NDFE7wdTpINgs++ZJ4N49W2vRp8LCKrn3Ob1kYNtOo21vfDoaV5GzBfLU4MovSAB8uNRm4jgzVQZ+mBzPQ== - dependencies: - esbuild "^0.25.0" - fdir "^6.4.4" - picomatch "^4.0.2" - postcss "^8.5.3" - rollup "^4.34.9" - tinyglobby "^0.2.13" - optionalDependencies: - fsevents "~2.3.3" - -which@^2.0.1: - version "2.0.2" - resolved "https://registry.npmjs.org/which/-/which-2.0.2.tgz#7c6a8dd0a636a0327e10b59c9286eee93f3f51b1" - integrity sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA== - dependencies: - isexe "^2.0.0" - -which@^3.0.0: - version "3.0.1" - resolved "https://registry.npmjs.org/which/-/which-3.0.1.tgz#89f1cd0c23f629a8105ffe69b8172791c87b4be1" - integrity sha512-XA1b62dzQzLfaEOSQFTCOd5KFf/1VSzZo7/7TUjnya6u0vGGKzU96UQBZTAThCb2j4/xjBAyii1OhRLJEivHvg== - dependencies: - isexe "^2.0.0" - -"wrap-ansi-cjs@npm:wrap-ansi@^7.0.0": - version "7.0.0" - resolved "https://registry.npmjs.org/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@^8.1.0: - version "8.1.0" - resolved "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-8.1.0.tgz#56dc22368ee570face1b49819975d9b9a5ead214" - integrity sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ== - dependencies: - ansi-styles "^6.1.0" - string-width "^5.0.1" - strip-ansi "^7.0.1" - -yallist@^3.0.2: - version "3.1.1" - resolved "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz#dbb7daf9bfd8bac9ab45ebf602b8cbad0d5d08fd" - integrity sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g== - -yallist@^5.0.0: - version "5.0.0" - resolved "https://registry.npmjs.org/yallist/-/yallist-5.0.0.tgz#00e2de443639ed0d78fd87de0d27469fbcffb533" - integrity sha512-YgvUTfwqyc7UXVMrB+SImsVYSmTS8X/tSrtdNZMImM+n7+QTriRXyXim0mBrTXNeqzVF0KWGgHPeiyViFFrNDw== diff --git a/dev-packages/e2e-tests/test-applications/react-router-7-framework-spa/yarn.lock b/dev-packages/e2e-tests/test-applications/react-router-7-framework-spa/yarn.lock deleted file mode 100644 index 43736103138f..000000000000 --- a/dev-packages/e2e-tests/test-applications/react-router-7-framework-spa/yarn.lock +++ /dev/null @@ -1,2311 +0,0 @@ -# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. -# yarn lockfile v1 - - -"@ampproject/remapping@^2.2.0", "@ampproject/remapping@^2.3.0": - version "2.3.0" - resolved "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.3.0.tgz#ed441b6fa600072520ce18b43d2c8cc8caecc7f4" - integrity sha512-30iZtAPgz+LTIYoeivqYo853f02jBYSd5uGnGpkFV0M3xOt9aN73erkgYAmZU43x4VfqcnLxW9Kpg3R5LC4YYw== - dependencies: - "@jridgewell/gen-mapping" "^0.3.5" - "@jridgewell/trace-mapping" "^0.3.24" - -"@babel/code-frame@^7.27.1": - version "7.27.1" - resolved "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.27.1.tgz#200f715e66d52a23b221a9435534a91cc13ad5be" - integrity sha512-cjQ7ZlQ0Mv3b47hABuTevyTuYN4i+loJKGeV9flcCgIK37cCXRh+L1bd3iBHlynerhQ7BhCkn2BPbQUL+rGqFg== - dependencies: - "@babel/helper-validator-identifier" "^7.27.1" - js-tokens "^4.0.0" - picocolors "^1.1.1" - -"@babel/compat-data@^7.27.2": - version "7.27.2" - resolved "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.27.2.tgz#4183f9e642fd84e74e3eea7ffa93a412e3b102c9" - integrity sha512-TUtMJYRPyUb/9aU8f3K0mjmjf6M9N5Woshn2CS6nqJSeJtTtQcpLUXjGt9vbF8ZGff0El99sWkLgzwW3VXnxZQ== - -"@babel/core@^7.21.8", "@babel/core@^7.23.7": - version "7.27.1" - resolved "https://registry.npmjs.org/@babel/core/-/core-7.27.1.tgz#89de51e86bd12246003e3524704c49541b16c3e6" - integrity sha512-IaaGWsQqfsQWVLqMn9OB92MNN7zukfVA4s7KKAI0KfrrDsZ0yhi5uV4baBuLuN7n3vsZpwP8asPPcVwApxvjBQ== - dependencies: - "@ampproject/remapping" "^2.2.0" - "@babel/code-frame" "^7.27.1" - "@babel/generator" "^7.27.1" - "@babel/helper-compilation-targets" "^7.27.1" - "@babel/helper-module-transforms" "^7.27.1" - "@babel/helpers" "^7.27.1" - "@babel/parser" "^7.27.1" - "@babel/template" "^7.27.1" - "@babel/traverse" "^7.27.1" - "@babel/types" "^7.27.1" - convert-source-map "^2.0.0" - debug "^4.1.0" - gensync "^1.0.0-beta.2" - json5 "^2.2.3" - semver "^6.3.1" - -"@babel/generator@^7.21.5", "@babel/generator@^7.27.1": - version "7.27.1" - resolved "https://registry.npmjs.org/@babel/generator/-/generator-7.27.1.tgz#862d4fad858f7208edd487c28b58144036b76230" - integrity sha512-UnJfnIpc/+JO0/+KRVQNGU+y5taA5vCbwN8+azkX6beii/ZF+enZJSOKo11ZSzGJjlNfJHfQtmQT8H+9TXPG2w== - dependencies: - "@babel/parser" "^7.27.1" - "@babel/types" "^7.27.1" - "@jridgewell/gen-mapping" "^0.3.5" - "@jridgewell/trace-mapping" "^0.3.25" - jsesc "^3.0.2" - -"@babel/helper-annotate-as-pure@^7.27.1": - version "7.27.1" - resolved "https://registry.npmjs.org/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.27.1.tgz#4345d81a9a46a6486e24d069469f13e60445c05d" - integrity sha512-WnuuDILl9oOBbKnb4L+DyODx7iC47XfzmNCpTttFsSp6hTG7XZxu60+4IO+2/hPfcGOoKbFiwoI/+zwARbNQow== - dependencies: - "@babel/types" "^7.27.1" - -"@babel/helper-compilation-targets@^7.27.1": - version "7.27.2" - resolved "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.27.2.tgz#46a0f6efab808d51d29ce96858dd10ce8732733d" - integrity sha512-2+1thGUUWWjLTYTHZWK1n8Yga0ijBz1XAhUXcKy81rd5g6yh7hGqMp45v7cadSbEHc9G3OTv45SyneRN3ps4DQ== - dependencies: - "@babel/compat-data" "^7.27.2" - "@babel/helper-validator-option" "^7.27.1" - browserslist "^4.24.0" - lru-cache "^5.1.1" - semver "^6.3.1" - -"@babel/helper-create-class-features-plugin@^7.27.1": - version "7.27.1" - resolved "https://registry.npmjs.org/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.27.1.tgz#5bee4262a6ea5ddc852d0806199eb17ca3de9281" - integrity sha512-QwGAmuvM17btKU5VqXfb+Giw4JcN0hjuufz3DYnpeVDvZLAObloM77bhMXiqry3Iio+Ai4phVRDwl6WU10+r5A== - dependencies: - "@babel/helper-annotate-as-pure" "^7.27.1" - "@babel/helper-member-expression-to-functions" "^7.27.1" - "@babel/helper-optimise-call-expression" "^7.27.1" - "@babel/helper-replace-supers" "^7.27.1" - "@babel/helper-skip-transparent-expression-wrappers" "^7.27.1" - "@babel/traverse" "^7.27.1" - semver "^6.3.1" - -"@babel/helper-member-expression-to-functions@^7.27.1": - version "7.27.1" - resolved "https://registry.npmjs.org/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.27.1.tgz#ea1211276be93e798ce19037da6f06fbb994fa44" - integrity sha512-E5chM8eWjTp/aNoVpcbfM7mLxu9XGLWYise2eBKGQomAk/Mb4XoxyqXTZbuTohbsl8EKqdlMhnDI2CCLfcs9wA== - dependencies: - "@babel/traverse" "^7.27.1" - "@babel/types" "^7.27.1" - -"@babel/helper-module-imports@^7.27.1": - version "7.27.1" - resolved "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.27.1.tgz#7ef769a323e2655e126673bb6d2d6913bbead204" - integrity sha512-0gSFWUPNXNopqtIPQvlD5WgXYI5GY2kP2cCvoT8kczjbfcfuIljTbcWrulD1CIPIX2gt1wghbDy08yE1p+/r3w== - dependencies: - "@babel/traverse" "^7.27.1" - "@babel/types" "^7.27.1" - -"@babel/helper-module-transforms@^7.27.1": - version "7.27.1" - resolved "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.27.1.tgz#e1663b8b71d2de948da5c4fb2a20ca4f3ec27a6f" - integrity sha512-9yHn519/8KvTU5BjTVEEeIM3w9/2yXNKoD82JifINImhpKkARMJKPP59kLo+BafpdN5zgNeIcS4jsGDmd3l58g== - dependencies: - "@babel/helper-module-imports" "^7.27.1" - "@babel/helper-validator-identifier" "^7.27.1" - "@babel/traverse" "^7.27.1" - -"@babel/helper-optimise-call-expression@^7.27.1": - version "7.27.1" - resolved "https://registry.npmjs.org/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.27.1.tgz#c65221b61a643f3e62705e5dd2b5f115e35f9200" - integrity sha512-URMGH08NzYFhubNSGJrpUEphGKQwMQYBySzat5cAByY1/YgIRkULnIy3tAMeszlL/so2HbeilYloUmSpd7GdVw== - dependencies: - "@babel/types" "^7.27.1" - -"@babel/helper-plugin-utils@^7.27.1": - version "7.27.1" - resolved "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.27.1.tgz#ddb2f876534ff8013e6c2b299bf4d39b3c51d44c" - integrity sha512-1gn1Up5YXka3YYAHGKpbideQ5Yjf1tDa9qYcgysz+cNCXukyLl6DjPXhD3VRwSb8c0J9tA4b2+rHEZtc6R0tlw== - -"@babel/helper-replace-supers@^7.27.1": - version "7.27.1" - resolved "https://registry.npmjs.org/@babel/helper-replace-supers/-/helper-replace-supers-7.27.1.tgz#b1ed2d634ce3bdb730e4b52de30f8cccfd692bc0" - integrity sha512-7EHz6qDZc8RYS5ElPoShMheWvEgERonFCs7IAonWLLUTXW59DP14bCZt89/GKyreYn8g3S83m21FelHKbeDCKA== - dependencies: - "@babel/helper-member-expression-to-functions" "^7.27.1" - "@babel/helper-optimise-call-expression" "^7.27.1" - "@babel/traverse" "^7.27.1" - -"@babel/helper-skip-transparent-expression-wrappers@^7.27.1": - version "7.27.1" - resolved "https://registry.npmjs.org/@babel/helper-skip-transparent-expression-wrappers/-/helper-skip-transparent-expression-wrappers-7.27.1.tgz#62bb91b3abba8c7f1fec0252d9dbea11b3ee7a56" - integrity sha512-Tub4ZKEXqbPjXgWLl2+3JpQAYBJ8+ikpQ2Ocj/q/r0LwE3UhENh7EUabyHjz2kCEsrRY83ew2DQdHluuiDQFzg== - dependencies: - "@babel/traverse" "^7.27.1" - "@babel/types" "^7.27.1" - -"@babel/helper-string-parser@^7.27.1": - version "7.27.1" - resolved "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.27.1.tgz#54da796097ab19ce67ed9f88b47bb2ec49367687" - integrity sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA== - -"@babel/helper-validator-identifier@^7.27.1": - version "7.27.1" - resolved "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.27.1.tgz#a7054dcc145a967dd4dc8fee845a57c1316c9df8" - integrity sha512-D2hP9eA+Sqx1kBZgzxZh0y1trbuU+JoDkiEwqhQ36nodYqJwyEIhPSdMNd7lOm/4io72luTPWH20Yda0xOuUow== - -"@babel/helper-validator-option@^7.27.1": - version "7.27.1" - resolved "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.27.1.tgz#fa52f5b1e7db1ab049445b421c4471303897702f" - integrity sha512-YvjJow9FxbhFFKDSuFnVCe2WxXk1zWc22fFePVNEaWJEu8IrZVlda6N0uHwzZrUM1il7NC9Mlp4MaJYbYd9JSg== - -"@babel/helpers@^7.27.1": - version "7.27.1" - resolved "https://registry.npmjs.org/@babel/helpers/-/helpers-7.27.1.tgz#ffc27013038607cdba3288e692c3611c06a18aa4" - integrity sha512-FCvFTm0sWV8Fxhpp2McP5/W53GPllQ9QeQ7SiqGWjMf/LVG07lFa5+pgK05IRhVwtvafT22KF+ZSnM9I545CvQ== - dependencies: - "@babel/template" "^7.27.1" - "@babel/types" "^7.27.1" - -"@babel/parser@^7.21.8", "@babel/parser@^7.23.6", "@babel/parser@^7.27.1", "@babel/parser@^7.27.2": - version "7.27.2" - resolved "https://registry.npmjs.org/@babel/parser/-/parser-7.27.2.tgz#577518bedb17a2ce4212afd052e01f7df0941127" - integrity sha512-QYLs8299NA7WM/bZAdp+CviYYkVoYXlDW2rzliy3chxd1PQjej7JORuMJDJXJUb9g0TT+B99EwaVLKmX+sPXWw== - dependencies: - "@babel/types" "^7.27.1" - -"@babel/plugin-syntax-decorators@^7.22.10": - version "7.27.1" - resolved "https://registry.npmjs.org/@babel/plugin-syntax-decorators/-/plugin-syntax-decorators-7.27.1.tgz#ee7dd9590aeebc05f9d4c8c0560007b05979a63d" - integrity sha512-YMq8Z87Lhl8EGkmb0MwYkt36QnxC+fzCgrl66ereamPlYToRpIk5nUjKUY3QKLWq8mwUB1BgbeXcTJhZOCDg5A== - dependencies: - "@babel/helper-plugin-utils" "^7.27.1" - -"@babel/plugin-syntax-jsx@^7.21.4", "@babel/plugin-syntax-jsx@^7.27.1": - version "7.27.1" - resolved "https://registry.npmjs.org/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.27.1.tgz#2f9beb5eff30fa507c5532d107daac7b888fa34c" - integrity sha512-y8YTNIeKoyhGd9O0Jiyzyyqk8gdjnumGTQPsz0xOZOQ2RmkVJeZ1vmmfIvFEKqucBG6axJGBZDE/7iI5suUI/w== - dependencies: - "@babel/helper-plugin-utils" "^7.27.1" - -"@babel/plugin-syntax-typescript@^7.27.1": - version "7.27.1" - resolved "https://registry.npmjs.org/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.27.1.tgz#5147d29066a793450f220c63fa3a9431b7e6dd18" - integrity sha512-xfYCBMxveHrRMnAWl1ZlPXOZjzkN82THFvLhQhFXFt81Z5HnN+EtUkZhv/zcKpmT3fzmWZB0ywiBrbC3vogbwQ== - dependencies: - "@babel/helper-plugin-utils" "^7.27.1" - -"@babel/plugin-transform-modules-commonjs@^7.27.1": - version "7.27.1" - resolved "https://registry.npmjs.org/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.27.1.tgz#8e44ed37c2787ecc23bdc367f49977476614e832" - integrity sha512-OJguuwlTYlN0gBZFRPqwOGNWssZjfIUdS7HMYtN8c1KmwpwHFBwTeFZrg9XZa+DFTitWOW5iTAG7tyCUPsCCyw== - dependencies: - "@babel/helper-module-transforms" "^7.27.1" - "@babel/helper-plugin-utils" "^7.27.1" - -"@babel/plugin-transform-typescript@^7.27.1": - version "7.27.1" - resolved "https://registry.npmjs.org/@babel/plugin-transform-typescript/-/plugin-transform-typescript-7.27.1.tgz#d3bb65598bece03f773111e88cc4e8e5070f1140" - integrity sha512-Q5sT5+O4QUebHdbwKedFBEwRLb02zJ7r4A5Gg2hUoLuU3FjdMcyqcywqUrLCaDsFCxzokf7u9kuy7qz51YUuAg== - dependencies: - "@babel/helper-annotate-as-pure" "^7.27.1" - "@babel/helper-create-class-features-plugin" "^7.27.1" - "@babel/helper-plugin-utils" "^7.27.1" - "@babel/helper-skip-transparent-expression-wrappers" "^7.27.1" - "@babel/plugin-syntax-typescript" "^7.27.1" - -"@babel/preset-typescript@^7.21.5": - version "7.27.1" - resolved "https://registry.npmjs.org/@babel/preset-typescript/-/preset-typescript-7.27.1.tgz#190742a6428d282306648a55b0529b561484f912" - integrity sha512-l7WfQfX0WK4M0v2RudjuQK4u99BS6yLHYEmdtVPP7lKV013zr9DygFuWNlnbvQ9LR+LS0Egz/XAvGx5U9MX0fQ== - dependencies: - "@babel/helper-plugin-utils" "^7.27.1" - "@babel/helper-validator-option" "^7.27.1" - "@babel/plugin-syntax-jsx" "^7.27.1" - "@babel/plugin-transform-modules-commonjs" "^7.27.1" - "@babel/plugin-transform-typescript" "^7.27.1" - -"@babel/template@^7.27.1": - version "7.27.2" - resolved "https://registry.npmjs.org/@babel/template/-/template-7.27.2.tgz#fa78ceed3c4e7b63ebf6cb39e5852fca45f6809d" - integrity sha512-LPDZ85aEJyYSd18/DkjNh4/y1ntkE5KwUHWTiqgRxruuZL2F1yuHligVHLvcHY2vMHXttKFpJn6LwfI7cw7ODw== - dependencies: - "@babel/code-frame" "^7.27.1" - "@babel/parser" "^7.27.2" - "@babel/types" "^7.27.1" - -"@babel/traverse@^7.23.2", "@babel/traverse@^7.23.7", "@babel/traverse@^7.27.1": - version "7.27.1" - resolved "https://registry.npmjs.org/@babel/traverse/-/traverse-7.27.1.tgz#4db772902b133bbddd1c4f7a7ee47761c1b9f291" - integrity sha512-ZCYtZciz1IWJB4U61UPu4KEaqyfj+r5T1Q5mqPo+IBpcG9kHv30Z0aD8LXPgC1trYa6rK0orRyAhqUgk4MjmEg== - dependencies: - "@babel/code-frame" "^7.27.1" - "@babel/generator" "^7.27.1" - "@babel/parser" "^7.27.1" - "@babel/template" "^7.27.1" - "@babel/types" "^7.27.1" - debug "^4.3.1" - globals "^11.1.0" - -"@babel/types@^7.22.5", "@babel/types@^7.23.6", "@babel/types@^7.27.1": - version "7.27.1" - resolved "https://registry.npmjs.org/@babel/types/-/types-7.27.1.tgz#9defc53c16fc899e46941fc6901a9eea1c9d8560" - integrity sha512-+EzkxvLNfiUeKMgy/3luqfsCWFRXLb7U6wNQTk60tovuckwB15B191tJWvpp4HjiQWdJkCxO3Wbvc6jlk3Xb2Q== - dependencies: - "@babel/helper-string-parser" "^7.27.1" - "@babel/helper-validator-identifier" "^7.27.1" - -"@emnapi/core@^1.4.3": - version "1.4.3" - resolved "https://registry.npmjs.org/@emnapi/core/-/core-1.4.3.tgz#9ac52d2d5aea958f67e52c40a065f51de59b77d6" - integrity sha512-4m62DuCE07lw01soJwPiBGC0nAww0Q+RY70VZ+n49yDIO13yyinhbWCeNnaob0lakDtWQzSdtNWzJeOJt2ma+g== - dependencies: - "@emnapi/wasi-threads" "1.0.2" - tslib "^2.4.0" - -"@emnapi/runtime@^1.4.3": - version "1.4.3" - resolved "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.4.3.tgz#c0564665c80dc81c448adac23f9dfbed6c838f7d" - integrity sha512-pBPWdu6MLKROBX05wSNKcNb++m5Er+KQ9QkB+WVM+pW2Kx9hoSrVTnu3BdkI5eBLZoKu/J6mW/B6i6bJB2ytXQ== - dependencies: - tslib "^2.4.0" - -"@emnapi/wasi-threads@1.0.2", "@emnapi/wasi-threads@^1.0.2": - version "1.0.2" - resolved "https://registry.npmjs.org/@emnapi/wasi-threads/-/wasi-threads-1.0.2.tgz#977f44f844eac7d6c138a415a123818c655f874c" - integrity sha512-5n3nTJblwRi8LlXkJ9eBzu+kZR8Yxcc7ubakyQTFzPMtIhFpUBRbsnc2Dv88IZDIbCDlBiWrknhB4Lsz7mg6BA== - dependencies: - tslib "^2.4.0" - -"@esbuild/aix-ppc64@0.25.4": - version "0.25.4" - resolved "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.25.4.tgz#830d6476cbbca0c005136af07303646b419f1162" - integrity sha512-1VCICWypeQKhVbE9oW/sJaAmjLxhVqacdkvPLEjwlttjfwENRSClS8EjBz0KzRyFSCPDIkuXW34Je/vk7zdB7Q== - -"@esbuild/android-arm64@0.25.4": - version "0.25.4" - resolved "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.25.4.tgz#d11d4fc299224e729e2190cacadbcc00e7a9fd67" - integrity sha512-bBy69pgfhMGtCnwpC/x5QhfxAz/cBgQ9enbtwjf6V9lnPI/hMyT9iWpR1arm0l3kttTr4L0KSLpKmLp/ilKS9A== - -"@esbuild/android-arm@0.25.4": - version "0.25.4" - resolved "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.25.4.tgz#5660bd25080553dd2a28438f2a401a29959bd9b1" - integrity sha512-QNdQEps7DfFwE3hXiU4BZeOV68HHzYwGd0Nthhd3uCkkEKK7/R6MTgM0P7H7FAs5pU/DIWsviMmEGxEoxIZ+ZQ== - -"@esbuild/android-x64@0.25.4": - version "0.25.4" - resolved "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.25.4.tgz#18ddde705bf984e8cd9efec54e199ac18bc7bee1" - integrity sha512-TVhdVtQIFuVpIIR282btcGC2oGQoSfZfmBdTip2anCaVYcqWlZXGcdcKIUklfX2wj0JklNYgz39OBqh2cqXvcQ== - -"@esbuild/darwin-arm64@0.25.4": - version "0.25.4" - resolved "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.25.4.tgz#b0b7fb55db8fc6f5de5a0207ae986eb9c4766e67" - integrity sha512-Y1giCfM4nlHDWEfSckMzeWNdQS31BQGs9/rouw6Ub91tkK79aIMTH3q9xHvzH8d0wDru5Ci0kWB8b3up/nl16g== - -"@esbuild/darwin-x64@0.25.4": - version "0.25.4" - resolved "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.25.4.tgz#e6813fdeba0bba356cb350a4b80543fbe66bf26f" - integrity sha512-CJsry8ZGM5VFVeyUYB3cdKpd/H69PYez4eJh1W/t38vzutdjEjtP7hB6eLKBoOdxcAlCtEYHzQ/PJ/oU9I4u0A== - -"@esbuild/freebsd-arm64@0.25.4": - version "0.25.4" - resolved "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.25.4.tgz#dc11a73d3ccdc308567b908b43c6698e850759be" - integrity sha512-yYq+39NlTRzU2XmoPW4l5Ifpl9fqSk0nAJYM/V/WUGPEFfek1epLHJIkTQM6bBs1swApjO5nWgvr843g6TjxuQ== - -"@esbuild/freebsd-x64@0.25.4": - version "0.25.4" - resolved "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.25.4.tgz#91da08db8bd1bff5f31924c57a81dab26e93a143" - integrity sha512-0FgvOJ6UUMflsHSPLzdfDnnBBVoCDtBTVyn/MrWloUNvq/5SFmh13l3dvgRPkDihRxb77Y17MbqbCAa2strMQQ== - -"@esbuild/linux-arm64@0.25.4": - version "0.25.4" - resolved "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.25.4.tgz#efc15e45c945a082708f9a9f73bfa8d4db49728a" - integrity sha512-+89UsQTfXdmjIvZS6nUnOOLoXnkUTB9hR5QAeLrQdzOSWZvNSAXAtcRDHWtqAUtAmv7ZM1WPOOeSxDzzzMogiQ== - -"@esbuild/linux-arm@0.25.4": - version "0.25.4" - resolved "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.25.4.tgz#9b93c3e54ac49a2ede6f906e705d5d906f6db9e8" - integrity sha512-kro4c0P85GMfFYqW4TWOpvmF8rFShbWGnrLqlzp4X1TNWjRY3JMYUfDCtOxPKOIY8B0WC8HN51hGP4I4hz4AaQ== - -"@esbuild/linux-ia32@0.25.4": - version "0.25.4" - resolved "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.25.4.tgz#be8ef2c3e1d99fca2d25c416b297d00360623596" - integrity sha512-yTEjoapy8UP3rv8dB0ip3AfMpRbyhSN3+hY8mo/i4QXFeDxmiYbEKp3ZRjBKcOP862Ua4b1PDfwlvbuwY7hIGQ== - -"@esbuild/linux-loong64@0.25.4": - version "0.25.4" - resolved "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.25.4.tgz#b0840a2707c3fc02eec288d3f9defa3827cd7a87" - integrity sha512-NeqqYkrcGzFwi6CGRGNMOjWGGSYOpqwCjS9fvaUlX5s3zwOtn1qwg1s2iE2svBe4Q/YOG1q6875lcAoQK/F4VA== - -"@esbuild/linux-mips64el@0.25.4": - version "0.25.4" - resolved "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.25.4.tgz#2a198e5a458c9f0e75881a4e63d26ba0cf9df39f" - integrity sha512-IcvTlF9dtLrfL/M8WgNI/qJYBENP3ekgsHbYUIzEzq5XJzzVEV/fXY9WFPfEEXmu3ck2qJP8LG/p3Q8f7Zc2Xg== - -"@esbuild/linux-ppc64@0.25.4": - version "0.25.4" - resolved "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.25.4.tgz#64f4ae0b923d7dd72fb860b9b22edb42007cf8f5" - integrity sha512-HOy0aLTJTVtoTeGZh4HSXaO6M95qu4k5lJcH4gxv56iaycfz1S8GO/5Jh6X4Y1YiI0h7cRyLi+HixMR+88swag== - -"@esbuild/linux-riscv64@0.25.4": - version "0.25.4" - resolved "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.25.4.tgz#fb2844b11fdddd39e29d291c7cf80f99b0d5158d" - integrity sha512-i8JUDAufpz9jOzo4yIShCTcXzS07vEgWzyX3NH2G7LEFVgrLEhjwL3ajFE4fZI3I4ZgiM7JH3GQ7ReObROvSUA== - -"@esbuild/linux-s390x@0.25.4": - version "0.25.4" - resolved "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.25.4.tgz#1466876e0aa3560c7673e63fdebc8278707bc750" - integrity sha512-jFnu+6UbLlzIjPQpWCNh5QtrcNfMLjgIavnwPQAfoGx4q17ocOU9MsQ2QVvFxwQoWpZT8DvTLooTvmOQXkO51g== - -"@esbuild/linux-x64@0.25.4": - version "0.25.4" - resolved "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.25.4.tgz#c10fde899455db7cba5f11b3bccfa0e41bf4d0cd" - integrity sha512-6e0cvXwzOnVWJHq+mskP8DNSrKBr1bULBvnFLpc1KY+d+irZSgZ02TGse5FsafKS5jg2e4pbvK6TPXaF/A6+CA== - -"@esbuild/netbsd-arm64@0.25.4": - version "0.25.4" - resolved "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.25.4.tgz#02e483fbcbe3f18f0b02612a941b77be76c111a4" - integrity sha512-vUnkBYxZW4hL/ie91hSqaSNjulOnYXE1VSLusnvHg2u3jewJBz3YzB9+oCw8DABeVqZGg94t9tyZFoHma8gWZQ== - -"@esbuild/netbsd-x64@0.25.4": - version "0.25.4" - resolved "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.25.4.tgz#ec401fb0b1ed0ac01d978564c5fc8634ed1dc2ed" - integrity sha512-XAg8pIQn5CzhOB8odIcAm42QsOfa98SBeKUdo4xa8OvX8LbMZqEtgeWE9P/Wxt7MlG2QqvjGths+nq48TrUiKw== - -"@esbuild/openbsd-arm64@0.25.4": - version "0.25.4" - resolved "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.25.4.tgz#f272c2f41cfea1d91b93d487a51b5c5ca7a8c8c4" - integrity sha512-Ct2WcFEANlFDtp1nVAXSNBPDxyU+j7+tId//iHXU2f/lN5AmO4zLyhDcpR5Cz1r08mVxzt3Jpyt4PmXQ1O6+7A== - -"@esbuild/openbsd-x64@0.25.4": - version "0.25.4" - resolved "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.25.4.tgz#2e25950bc10fa9db1e5c868e3d50c44f7c150fd7" - integrity sha512-xAGGhyOQ9Otm1Xu8NT1ifGLnA6M3sJxZ6ixylb+vIUVzvvd6GOALpwQrYrtlPouMqd/vSbgehz6HaVk4+7Afhw== - -"@esbuild/sunos-x64@0.25.4": - version "0.25.4" - resolved "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.25.4.tgz#cd596fa65a67b3b7adc5ecd52d9f5733832e1abd" - integrity sha512-Mw+tzy4pp6wZEK0+Lwr76pWLjrtjmJyUB23tHKqEDP74R3q95luY/bXqXZeYl4NYlvwOqoRKlInQialgCKy67Q== - -"@esbuild/win32-arm64@0.25.4": - version "0.25.4" - resolved "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.25.4.tgz#b4dbcb57b21eeaf8331e424c3999b89d8951dc88" - integrity sha512-AVUP428VQTSddguz9dO9ngb+E5aScyg7nOeJDrF1HPYu555gmza3bDGMPhmVXL8svDSoqPCsCPjb265yG/kLKQ== - -"@esbuild/win32-ia32@0.25.4": - version "0.25.4" - resolved "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.25.4.tgz#410842e5d66d4ece1757634e297a87635eb82f7a" - integrity sha512-i1sW+1i+oWvQzSgfRcxxG2k4I9n3O9NRqy8U+uugaT2Dy7kLO9Y7wI72haOahxceMX8hZAzgGou1FhndRldxRg== - -"@esbuild/win32-x64@0.25.4": - version "0.25.4" - resolved "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.25.4.tgz#0b17ec8a70b2385827d52314c1253160a0b9bacc" - integrity sha512-nOT2vZNw6hJ+z43oP1SPea/G/6AbN6X+bGNhNuq8NtRHy4wsMhw765IKLNmnjek7GvjWBYQ8Q5VBoYTFg9y1UQ== - -"@isaacs/cliui@^8.0.2": - version "8.0.2" - resolved "https://registry.npmjs.org/@isaacs/cliui/-/cliui-8.0.2.tgz#b37667b7bc181c168782259bab42474fbf52b550" - integrity sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA== - dependencies: - string-width "^5.1.2" - string-width-cjs "npm:string-width@^4.2.0" - strip-ansi "^7.0.1" - strip-ansi-cjs "npm:strip-ansi@^6.0.1" - wrap-ansi "^8.1.0" - wrap-ansi-cjs "npm:wrap-ansi@^7.0.0" - -"@isaacs/fs-minipass@^4.0.0": - version "4.0.1" - resolved "https://registry.npmjs.org/@isaacs/fs-minipass/-/fs-minipass-4.0.1.tgz#2d59ae3ab4b38fb4270bfa23d30f8e2e86c7fe32" - integrity sha512-wgm9Ehl2jpeqP3zw/7mo3kRHFp5MEDhqAdwy1fTGkHAwnkGOVsgpvQhL8B5n1qlb01jV3n/bI0ZfZp5lWA1k4w== - dependencies: - minipass "^7.0.4" - -"@jridgewell/gen-mapping@^0.3.5": - version "0.3.8" - resolved "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.8.tgz#4f0e06362e01362f823d348f1872b08f666d8142" - integrity sha512-imAbBGkb+ebQyxKgzv5Hu2nmROxoDOXHh80evxdoXNOrvAnVx7zimzc1Oo5h9RlfV4vPXaE2iM5pOFbvOCClWA== - dependencies: - "@jridgewell/set-array" "^1.2.1" - "@jridgewell/sourcemap-codec" "^1.4.10" - "@jridgewell/trace-mapping" "^0.3.24" - -"@jridgewell/resolve-uri@^3.1.0": - version "3.1.2" - resolved "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz#7a0ee601f60f99a20c7c7c5ff0c80388c1189bd6" - integrity sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw== - -"@jridgewell/set-array@^1.2.1": - version "1.2.1" - resolved "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.2.1.tgz#558fb6472ed16a4c850b889530e6b36438c49280" - integrity sha512-R8gLRTZeyp03ymzP/6Lil/28tGeGEzhx1q2k703KGWRAI1VdvPIXdG70VJc2pAMw3NA6JKL5hhFu1sJX0Mnn/A== - -"@jridgewell/sourcemap-codec@^1.4.10", "@jridgewell/sourcemap-codec@^1.4.14", "@jridgewell/sourcemap-codec@^1.5.0": - version "1.5.0" - resolved "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.0.tgz#3188bcb273a414b0d215fd22a58540b989b9409a" - integrity sha512-gv3ZRaISU3fjPAgNsriBRqGWQL6quFx04YMPW/zD8XMLsU32mhCCbfbO6KZFLjvYpCZ8zyDEgqsgf+PwPaM7GQ== - -"@jridgewell/trace-mapping@^0.3.24", "@jridgewell/trace-mapping@^0.3.25": - version "0.3.25" - resolved "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.25.tgz#15f190e98895f3fc23276ee14bc76b675c2e50f0" - integrity sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ== - dependencies: - "@jridgewell/resolve-uri" "^3.1.0" - "@jridgewell/sourcemap-codec" "^1.4.14" - -"@mjackson/node-fetch-server@^0.2.0": - version "0.2.0" - resolved "https://registry.npmjs.org/@mjackson/node-fetch-server/-/node-fetch-server-0.2.0.tgz#577c0c25d8aae9f69a97738b7b0d03d1471cdc49" - integrity sha512-EMlH1e30yzmTpGLQjlFmaDAjyOeZhng1/XCd7DExR8PNAnG/G1tyruZxEoUe11ClnwGhGrtsdnyyUx1frSzjng== - -"@napi-rs/wasm-runtime@^0.2.9": - version "0.2.10" - resolved "https://registry.npmjs.org/@napi-rs/wasm-runtime/-/wasm-runtime-0.2.10.tgz#f3b7109419c6670000b2401e0c778b98afc25f84" - integrity sha512-bCsCyeZEwVErsGmyPNSzwfwFn4OdxBj0mmv6hOFucB/k81Ojdu68RbZdxYsRQUPc9l6SU5F/cG+bXgWs3oUgsQ== - dependencies: - "@emnapi/core" "^1.4.3" - "@emnapi/runtime" "^1.4.3" - "@tybys/wasm-util" "^0.9.0" - -"@npmcli/git@^4.1.0": - version "4.1.0" - resolved "https://registry.npmjs.org/@npmcli/git/-/git-4.1.0.tgz#ab0ad3fd82bc4d8c1351b6c62f0fa56e8fe6afa6" - integrity sha512-9hwoB3gStVfa0N31ymBmrX+GuDGdVA/QWShZVqE0HK2Af+7QGGrCTbZia/SW0ImUTjTne7SP91qxDmtXvDHRPQ== - dependencies: - "@npmcli/promise-spawn" "^6.0.0" - lru-cache "^7.4.4" - npm-pick-manifest "^8.0.0" - proc-log "^3.0.0" - promise-inflight "^1.0.1" - promise-retry "^2.0.1" - semver "^7.3.5" - which "^3.0.0" - -"@npmcli/package-json@^4.0.1": - version "4.0.1" - resolved "https://registry.npmjs.org/@npmcli/package-json/-/package-json-4.0.1.tgz#1a07bf0e086b640500791f6bf245ff43cc27fa37" - integrity sha512-lRCEGdHZomFsURroh522YvA/2cVb9oPIJrjHanCJZkiasz1BzcnLr3tBJhlV7S86MBJBuAQ33is2D60YitZL2Q== - dependencies: - "@npmcli/git" "^4.1.0" - glob "^10.2.2" - hosted-git-info "^6.1.1" - json-parse-even-better-errors "^3.0.0" - normalize-package-data "^5.0.0" - proc-log "^3.0.0" - semver "^7.5.3" - -"@npmcli/promise-spawn@^6.0.0": - version "6.0.2" - resolved "https://registry.npmjs.org/@npmcli/promise-spawn/-/promise-spawn-6.0.2.tgz#c8bc4fa2bd0f01cb979d8798ba038f314cfa70f2" - integrity sha512-gGq0NJkIGSwdbUt4yhdF8ZrmkGKVz9vAdVzpOfnom+V8PLSmSOVhZwbNvZZS1EYcJN5hzzKBxmmVVAInM6HQLg== - dependencies: - which "^3.0.0" - -"@pkgjs/parseargs@^0.11.0": - version "0.11.0" - resolved "https://registry.npmjs.org/@pkgjs/parseargs/-/parseargs-0.11.0.tgz#a77ea742fab25775145434eb1d2328cf5013ac33" - integrity sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg== - -"@react-router/dev@^7.5.3": - version "7.6.0" - resolved "https://registry.npmjs.org/@react-router/dev/-/dev-7.6.0.tgz#9974e608be0398ee22282405992d0a62a3cc26ba" - integrity sha512-XSxEslex0ddJPxNNgdU1Eqmc9lsY/lhcLNCcRLAtlrOPyOz3Y8kIPpAf5T/U2AG3HGXFVBa9f8aQ7wXU3wTJSw== - dependencies: - "@babel/core" "^7.21.8" - "@babel/generator" "^7.21.5" - "@babel/parser" "^7.21.8" - "@babel/plugin-syntax-decorators" "^7.22.10" - "@babel/plugin-syntax-jsx" "^7.21.4" - "@babel/preset-typescript" "^7.21.5" - "@babel/traverse" "^7.23.2" - "@babel/types" "^7.22.5" - "@npmcli/package-json" "^4.0.1" - "@react-router/node" "7.6.0" - arg "^5.0.1" - babel-dead-code-elimination "^1.0.6" - chokidar "^4.0.0" - dedent "^1.5.3" - es-module-lexer "^1.3.1" - exit-hook "2.2.1" - fs-extra "^10.0.0" - jsesc "3.0.2" - lodash "^4.17.21" - pathe "^1.1.2" - picocolors "^1.1.1" - prettier "^2.7.1" - react-refresh "^0.14.0" - semver "^7.3.7" - set-cookie-parser "^2.6.0" - valibot "^0.41.0" - vite-node "3.0.0-beta.2" - -"@react-router/express@7.6.0": - version "7.6.0" - resolved "https://registry.npmjs.org/@react-router/express/-/express-7.6.0.tgz#1c9686e9154aab7b9411d4d1bcf4cc14145ebc94" - integrity sha512-nxSTCcTsVx94bXOI9JjG7Cg338myi8EdQWTOjA97v2ApX35wZm/ZDYos5MbrvZiMi0aB4KgAD62o4byNqF9Z1A== - dependencies: - "@react-router/node" "7.6.0" - -"@react-router/node@7.6.0", "@react-router/node@^7.5.3": - version "7.6.0" - resolved "https://registry.npmjs.org/@react-router/node/-/node-7.6.0.tgz#18c31c502f8004f2b8a1ca6ceebd0ac1e9307e12" - integrity sha512-agjDPUzisLdGJ7Q2lx/Z3OfdS2t1k6qv/nTvA45iahGsQJCMDvMqVoIi7iIULKQJwrn4HWjM9jqEp75+WsMOXg== - dependencies: - "@mjackson/node-fetch-server" "^0.2.0" - source-map-support "^0.5.21" - stream-slice "^0.1.2" - undici "^6.19.2" - -"@react-router/serve@^7.5.3": - version "7.6.0" - resolved "https://registry.npmjs.org/@react-router/serve/-/serve-7.6.0.tgz#ab0ff21cf80b3dbbb746fad1110d9d2903b24439" - integrity sha512-2O8ALEYgJfimvEdNRqMpnZb2N+DQ5UK/SKo9Xo3mTkt3no0rNTcNxzmhzD2tm92Q/HI7kHmMY1nBegNB2i1abA== - dependencies: - "@react-router/express" "7.6.0" - "@react-router/node" "7.6.0" - compression "^1.7.4" - express "^4.19.2" - get-port "5.1.1" - morgan "^1.10.0" - source-map-support "^0.5.21" - -"@rollup/rollup-android-arm-eabi@4.41.0": - version "4.41.0" - resolved "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.41.0.tgz#9145b38faf3fbfe3ec557130110e772f797335aa" - integrity sha512-KxN+zCjOYHGwCl4UCtSfZ6jrq/qi88JDUtiEFk8LELEHq2Egfc/FgW+jItZiOLRuQfb/3xJSgFuNPC9jzggX+A== - -"@rollup/rollup-android-arm64@4.41.0": - version "4.41.0" - resolved "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.41.0.tgz#d73d641c59e9d7827e5ce0af9dfbc168b95cce0f" - integrity sha512-yDvqx3lWlcugozax3DItKJI5j05B0d4Kvnjx+5mwiUpWramVvmAByYigMplaoAQ3pvdprGCTCE03eduqE/8mPQ== - -"@rollup/rollup-darwin-arm64@4.41.0": - version "4.41.0" - resolved "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.41.0.tgz#45d9d71d941117c98e7a5e77f60f0bc682d27e82" - integrity sha512-2KOU574vD3gzcPSjxO0eyR5iWlnxxtmW1F5CkNOHmMlueKNCQkxR6+ekgWyVnz6zaZihpUNkGxjsYrkTJKhkaw== - -"@rollup/rollup-darwin-x64@4.41.0": - version "4.41.0" - resolved "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.41.0.tgz#8d72fb5f81714cb43e90f263fb1674520cce3f2a" - integrity sha512-gE5ACNSxHcEZyP2BA9TuTakfZvULEW4YAOtxl/A/YDbIir/wPKukde0BNPlnBiP88ecaN4BJI2TtAd+HKuZPQQ== - -"@rollup/rollup-freebsd-arm64@4.41.0": - version "4.41.0" - resolved "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.41.0.tgz#a52b58852c9cec9255e382a2f335b08bc8c6111d" - integrity sha512-GSxU6r5HnWij7FoSo7cZg3l5GPg4HFLkzsFFh0N/b16q5buW1NAWuCJ+HMtIdUEi6XF0qH+hN0TEd78laRp7Dg== - -"@rollup/rollup-freebsd-x64@4.41.0": - version "4.41.0" - resolved "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.41.0.tgz#104511dc64612789ddda41d164ab07cdac84a6c1" - integrity sha512-KGiGKGDg8qLRyOWmk6IeiHJzsN/OYxO6nSbT0Vj4MwjS2XQy/5emsmtoqLAabqrohbgLWJ5GV3s/ljdrIr8Qjg== - -"@rollup/rollup-linux-arm-gnueabihf@4.41.0": - version "4.41.0" - resolved "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.41.0.tgz#643e3ad19c93903201fde89abd76baaee725e6c2" - integrity sha512-46OzWeqEVQyX3N2/QdiU/CMXYDH/lSHpgfBkuhl3igpZiaB3ZIfSjKuOnybFVBQzjsLwkus2mjaESy8H41SzvA== - -"@rollup/rollup-linux-arm-musleabihf@4.41.0": - version "4.41.0" - resolved "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.41.0.tgz#fdc6a595aec7b20c5bfdac81412028c56d734e63" - integrity sha512-lfgW3KtQP4YauqdPpcUZHPcqQXmTmH4nYU0cplNeW583CMkAGjtImw4PKli09NFi2iQgChk4e9erkwlfYem6Lg== - -"@rollup/rollup-linux-arm64-gnu@4.41.0": - version "4.41.0" - resolved "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.41.0.tgz#c28620bcd385496bdbbc24920b21f9fcca9ecbfa" - integrity sha512-nn8mEyzMbdEJzT7cwxgObuwviMx6kPRxzYiOl6o/o+ChQq23gfdlZcUNnt89lPhhz3BYsZ72rp0rxNqBSfqlqw== - -"@rollup/rollup-linux-arm64-musl@4.41.0": - version "4.41.0" - resolved "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.41.0.tgz#a6b71b1e8fa33bac9f65b6f879e8ed878035d120" - integrity sha512-l+QK99je2zUKGd31Gh+45c4pGDAqZSuWQiuRFCdHYC2CSiO47qUWsCcenrI6p22hvHZrDje9QjwSMAFL3iwXwQ== - -"@rollup/rollup-linux-loongarch64-gnu@4.41.0": - version "4.41.0" - resolved "https://registry.npmjs.org/@rollup/rollup-linux-loongarch64-gnu/-/rollup-linux-loongarch64-gnu-4.41.0.tgz#b06374601ce865a1110324b2f06db574d3a1b0e1" - integrity sha512-WbnJaxPv1gPIm6S8O/Wg+wfE/OzGSXlBMbOe4ie+zMyykMOeqmgD1BhPxZQuDqwUN+0T/xOFtL2RUWBspnZj3w== - -"@rollup/rollup-linux-powerpc64le-gnu@4.41.0": - version "4.41.0" - resolved "https://registry.npmjs.org/@rollup/rollup-linux-powerpc64le-gnu/-/rollup-linux-powerpc64le-gnu-4.41.0.tgz#8a2a1f6058c920889c2aff3753a20fead7a8cc26" - integrity sha512-eRDWR5t67/b2g8Q/S8XPi0YdbKcCs4WQ8vklNnUYLaSWF+Cbv2axZsp4jni6/j7eKvMLYCYdcsv8dcU+a6QNFg== - -"@rollup/rollup-linux-riscv64-gnu@4.41.0": - version "4.41.0" - resolved "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.41.0.tgz#8ef6f680d011b95a2f6546c6c31a37a33138035f" - integrity sha512-TWrZb6GF5jsEKG7T1IHwlLMDRy2f3DPqYldmIhnA2DVqvvhY2Ai184vZGgahRrg8k9UBWoSlHv+suRfTN7Ua4A== - -"@rollup/rollup-linux-riscv64-musl@4.41.0": - version "4.41.0" - resolved "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.41.0.tgz#9f4884c5955a7cd39b396f6e27aa59b3269988eb" - integrity sha512-ieQljaZKuJpmWvd8gW87ZmSFwid6AxMDk5bhONJ57U8zT77zpZ/TPKkU9HpnnFrM4zsgr4kiGuzbIbZTGi7u9A== - -"@rollup/rollup-linux-s390x-gnu@4.41.0": - version "4.41.0" - resolved "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.41.0.tgz#5619303cc51994e3df404a497f42c79dc5efd6eb" - integrity sha512-/L3pW48SxrWAlVsKCN0dGLB2bi8Nv8pr5S5ocSM+S0XCn5RCVCXqi8GVtHFsOBBCSeR+u9brV2zno5+mg3S4Aw== - -"@rollup/rollup-linux-x64-gnu@4.41.0": - version "4.41.0" - resolved "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.41.0.tgz#c3e42b66c04e25ad0f2a00beec42ede96ccc8983" - integrity sha512-XMLeKjyH8NsEDCRptf6LO8lJk23o9wvB+dJwcXMaH6ZQbbkHu2dbGIUindbMtRN6ux1xKi16iXWu6q9mu7gDhQ== - -"@rollup/rollup-linux-x64-musl@4.41.0": - version "4.41.0" - resolved "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.41.0.tgz#8d3452de42aa72fc5fc3e5ad1eb0b68030742a25" - integrity sha512-m/P7LycHZTvSQeXhFmgmdqEiTqSV80zn6xHaQ1JSqwCtD1YGtwEK515Qmy9DcB2HK4dOUVypQxvhVSy06cJPEg== - -"@rollup/rollup-win32-arm64-msvc@4.41.0": - version "4.41.0" - resolved "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.41.0.tgz#3b7bbd9f43f1c380061f306abce6f3f64de20306" - integrity sha512-4yodtcOrFHpbomJGVEqZ8fzD4kfBeCbpsUy5Pqk4RluXOdsWdjLnjhiKy2w3qzcASWd04fp52Xz7JKarVJ5BTg== - -"@rollup/rollup-win32-ia32-msvc@4.41.0": - version "4.41.0" - resolved "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.41.0.tgz#e27ef5c40bbec49fac3d4e4b1618fbe4597b40e5" - integrity sha512-tmazCrAsKzdkXssEc65zIE1oC6xPHwfy9d5Ta25SRCDOZS+I6RypVVShWALNuU9bxIfGA0aqrmzlzoM5wO5SPQ== - -"@rollup/rollup-win32-x64-msvc@4.41.0": - version "4.41.0" - resolved "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.41.0.tgz#b0b595ad4720259bbb81600750d26a655cac06be" - integrity sha512-h1J+Yzjo/X+0EAvR2kIXJDuTuyT7drc+t2ALY0nIcGPbTatNOf0VWdhEA2Z4AAjv6X1NJV7SYo5oCTYRJhSlVA== - -"@tailwindcss/node@4.1.7": - version "4.1.7" - resolved "https://registry.npmjs.org/@tailwindcss/node/-/node-4.1.7.tgz#6d2df06c6b84a6fd8255a535b4f537c5235a37ee" - integrity sha512-9rsOpdY9idRI2NH6CL4wORFY0+Q6fnx9XP9Ju+iq/0wJwGD5IByIgFmwVbyy4ymuyprj8Qh4ErxMKTUL4uNh3g== - dependencies: - "@ampproject/remapping" "^2.3.0" - enhanced-resolve "^5.18.1" - jiti "^2.4.2" - lightningcss "1.30.1" - magic-string "^0.30.17" - source-map-js "^1.2.1" - tailwindcss "4.1.7" - -"@tailwindcss/oxide-android-arm64@4.1.7": - version "4.1.7" - resolved "https://registry.npmjs.org/@tailwindcss/oxide-android-arm64/-/oxide-android-arm64-4.1.7.tgz#3b115097b64aff6487715304ebe0bb43d7a2d42a" - integrity sha512-IWA410JZ8fF7kACus6BrUwY2Z1t1hm0+ZWNEzykKmMNM09wQooOcN/VXr0p/WJdtHZ90PvJf2AIBS/Ceqx1emg== - -"@tailwindcss/oxide-darwin-arm64@4.1.7": - version "4.1.7" - resolved "https://registry.npmjs.org/@tailwindcss/oxide-darwin-arm64/-/oxide-darwin-arm64-4.1.7.tgz#6c9d8cd3cd631a2ac0dff58a766c958cff5b0046" - integrity sha512-81jUw9To7fimGGkuJ2W5h3/oGonTOZKZ8C2ghm/TTxbwvfSiFSDPd6/A/KE2N7Jp4mv3Ps9OFqg2fEKgZFfsvg== - -"@tailwindcss/oxide-darwin-x64@4.1.7": - version "4.1.7" - resolved "https://registry.npmjs.org/@tailwindcss/oxide-darwin-x64/-/oxide-darwin-x64-4.1.7.tgz#a022b5ef006570ac8bb92128ec7fc9cc2314bdd1" - integrity sha512-q77rWjEyGHV4PdDBtrzO0tgBBPlQWKY7wZK0cUok/HaGgbNKecegNxCGikuPJn5wFAlIywC3v+WMBt0PEBtwGw== - -"@tailwindcss/oxide-freebsd-x64@4.1.7": - version "4.1.7" - resolved "https://registry.npmjs.org/@tailwindcss/oxide-freebsd-x64/-/oxide-freebsd-x64-4.1.7.tgz#efc133e6065c3c6299a981caa9c5f426e1d60e5e" - integrity sha512-RfmdbbK6G6ptgF4qqbzoxmH+PKfP4KSVs7SRlTwcbRgBwezJkAO3Qta/7gDy10Q2DcUVkKxFLXUQO6J3CRvBGw== - -"@tailwindcss/oxide-linux-arm-gnueabihf@4.1.7": - version "4.1.7" - resolved "https://registry.npmjs.org/@tailwindcss/oxide-linux-arm-gnueabihf/-/oxide-linux-arm-gnueabihf-4.1.7.tgz#7a4dc1d9636c1b1e62936bd679a8867030dc0ad6" - integrity sha512-OZqsGvpwOa13lVd1z6JVwQXadEobmesxQ4AxhrwRiPuE04quvZHWn/LnihMg7/XkN+dTioXp/VMu/p6A5eZP3g== - -"@tailwindcss/oxide-linux-arm64-gnu@4.1.7": - version "4.1.7" - resolved "https://registry.npmjs.org/@tailwindcss/oxide-linux-arm64-gnu/-/oxide-linux-arm64-gnu-4.1.7.tgz#9a0da009c37d135fe983d1093bfb6d370fe926dc" - integrity sha512-voMvBTnJSfKecJxGkoeAyW/2XRToLZ227LxswLAwKY7YslG/Xkw9/tJNH+3IVh5bdYzYE7DfiaPbRkSHFxY1xA== - -"@tailwindcss/oxide-linux-arm64-musl@4.1.7": - version "4.1.7" - resolved "https://registry.npmjs.org/@tailwindcss/oxide-linux-arm64-musl/-/oxide-linux-arm64-musl-4.1.7.tgz#ef2398b48c426148c1b9949fdbdd86d364dca10d" - integrity sha512-PjGuNNmJeKHnP58M7XyjJyla8LPo+RmwHQpBI+W/OxqrwojyuCQ+GUtygu7jUqTEexejZHr/z3nBc/gTiXBj4A== - -"@tailwindcss/oxide-linux-x64-gnu@4.1.7": - version "4.1.7" - resolved "https://registry.npmjs.org/@tailwindcss/oxide-linux-x64-gnu/-/oxide-linux-x64-gnu-4.1.7.tgz#4d36aec4c4df87f8c332526bb6874b2c36230901" - integrity sha512-HMs+Va+ZR3gC3mLZE00gXxtBo3JoSQxtu9lobbZd+DmfkIxR54NO7Z+UQNPsa0P/ITn1TevtFxXTpsRU7qEvWg== - -"@tailwindcss/oxide-linux-x64-musl@4.1.7": - version "4.1.7" - resolved "https://registry.npmjs.org/@tailwindcss/oxide-linux-x64-musl/-/oxide-linux-x64-musl-4.1.7.tgz#6e3045edc70d5156089aba0cd4a9760caa53965c" - integrity sha512-MHZ6jyNlutdHH8rd+YTdr3QbXrHXqwIhHw9e7yXEBcQdluGwhpQY2Eku8UZK6ReLaWtQ4gijIv5QoM5eE+qlsA== - -"@tailwindcss/oxide-wasm32-wasi@4.1.7": - version "4.1.7" - resolved "https://registry.npmjs.org/@tailwindcss/oxide-wasm32-wasi/-/oxide-wasm32-wasi-4.1.7.tgz#79b41b0c8da6e2f1dc5b722fe43528aa324222d5" - integrity sha512-ANaSKt74ZRzE2TvJmUcbFQ8zS201cIPxUDm5qez5rLEwWkie2SkGtA4P+GPTj+u8N6JbPrC8MtY8RmJA35Oo+A== - dependencies: - "@emnapi/core" "^1.4.3" - "@emnapi/runtime" "^1.4.3" - "@emnapi/wasi-threads" "^1.0.2" - "@napi-rs/wasm-runtime" "^0.2.9" - "@tybys/wasm-util" "^0.9.0" - tslib "^2.8.0" - -"@tailwindcss/oxide-win32-arm64-msvc@4.1.7": - version "4.1.7" - resolved "https://registry.npmjs.org/@tailwindcss/oxide-win32-arm64-msvc/-/oxide-win32-arm64-msvc-4.1.7.tgz#6c695b870eb8a3f9a958d0afccc67b1b6ee7e78d" - integrity sha512-HUiSiXQ9gLJBAPCMVRk2RT1ZrBjto7WvqsPBwUrNK2BcdSxMnk19h4pjZjI7zgPhDxlAbJSumTC4ljeA9y0tEw== - -"@tailwindcss/oxide-win32-x64-msvc@4.1.7": - version "4.1.7" - resolved "https://registry.npmjs.org/@tailwindcss/oxide-win32-x64-msvc/-/oxide-win32-x64-msvc-4.1.7.tgz#012b2929f6f33ba720a48793e3dbabc34bb0212c" - integrity sha512-rYHGmvoHiLJ8hWucSfSOEmdCBIGZIq7SpkPRSqLsH2Ab2YUNgKeAPT1Fi2cx3+hnYOrAb0jp9cRyode3bBW4mQ== - -"@tailwindcss/oxide@4.1.7": - version "4.1.7" - resolved "https://registry.npmjs.org/@tailwindcss/oxide/-/oxide-4.1.7.tgz#d8370c4d524a3017a85d4f63c41ca2318770debd" - integrity sha512-5SF95Ctm9DFiUyjUPnDGkoKItPX/k+xifcQhcqX5RA85m50jw1pT/KzjdvlqxRja45Y52nR4MR9fD1JYd7f8NQ== - dependencies: - detect-libc "^2.0.4" - tar "^7.4.3" - optionalDependencies: - "@tailwindcss/oxide-android-arm64" "4.1.7" - "@tailwindcss/oxide-darwin-arm64" "4.1.7" - "@tailwindcss/oxide-darwin-x64" "4.1.7" - "@tailwindcss/oxide-freebsd-x64" "4.1.7" - "@tailwindcss/oxide-linux-arm-gnueabihf" "4.1.7" - "@tailwindcss/oxide-linux-arm64-gnu" "4.1.7" - "@tailwindcss/oxide-linux-arm64-musl" "4.1.7" - "@tailwindcss/oxide-linux-x64-gnu" "4.1.7" - "@tailwindcss/oxide-linux-x64-musl" "4.1.7" - "@tailwindcss/oxide-wasm32-wasi" "4.1.7" - "@tailwindcss/oxide-win32-arm64-msvc" "4.1.7" - "@tailwindcss/oxide-win32-x64-msvc" "4.1.7" - -"@tailwindcss/vite@^4.1.4": - version "4.1.7" - resolved "https://registry.npmjs.org/@tailwindcss/vite/-/vite-4.1.7.tgz#73fb55d2341982fb920f916e1ef6781099cf2df6" - integrity sha512-tYa2fO3zDe41I7WqijyVbRd8oWT0aEID1Eokz5hMT6wShLIHj3yvwj9XbfuloHP9glZ6H+aG2AN/+ZrxJ1Y5RQ== - dependencies: - "@tailwindcss/node" "4.1.7" - "@tailwindcss/oxide" "4.1.7" - tailwindcss "4.1.7" - -"@tybys/wasm-util@^0.9.0": - version "0.9.0" - resolved "https://registry.npmjs.org/@tybys/wasm-util/-/wasm-util-0.9.0.tgz#3e75eb00604c8d6db470bf18c37b7d984a0e3355" - integrity sha512-6+7nlbMVX/PVDCwaIQ8nTOPveOcFLSt8GcXdx8hD0bt39uWxYT88uXzqTd4fTvqta7oeUJqudepapKNt2DYJFw== - dependencies: - tslib "^2.4.0" - -"@types/estree@1.0.7": - version "1.0.7" - resolved "https://registry.npmjs.org/@types/estree/-/estree-1.0.7.tgz#4158d3105276773d5b7695cd4834b1722e4f37a8" - integrity sha512-w28IoSUCJpidD/TGviZwwMJckNESJZXFu7NBZ5YJ4mEUnNraUn9Pm8HSZm/jDF1pDWYKspWE7oVphigUPRakIQ== - -"@types/node@^20": - version "20.17.50" - resolved "https://registry.npmjs.org/@types/node/-/node-20.17.50.tgz#d2640af991fc839ba746f799516fc6a4e47ebe50" - integrity sha512-Mxiq0ULv/zo1OzOhwPqOA13I81CV/W3nvd3ChtQZRT5Cwz3cr0FKo/wMSsbTqL3EXpaBAEQhva2B8ByRkOIh9A== - dependencies: - undici-types "~6.19.2" - -"@types/react-dom@^19.1.2": - version "19.1.5" - resolved "https://registry.npmjs.org/@types/react-dom/-/react-dom-19.1.5.tgz#cdfe2c663742887372f54804b16e8dbc26bd794a" - integrity sha512-CMCjrWucUBZvohgZxkjd6S9h0nZxXjzus6yDfUb+xLxYM7VvjKNH1tQrE9GWLql1XoOP4/Ds3bwFqShHUYraGg== - -"@types/react@^19.1.2": - version "19.1.5" - resolved "https://registry.npmjs.org/@types/react/-/react-19.1.5.tgz#9feb3bdeb506d0c79d8533b6ebdcacdbcb4756db" - integrity sha512-piErsCVVbpMMT2r7wbawdZsq4xMvIAhQuac2gedQHysu1TZYEigE6pnFfgZT+/jQnrRuF5r+SHzuehFjfRjr4g== - dependencies: - csstype "^3.0.2" - -accepts@~1.3.8: - version "1.3.8" - resolved "https://registry.npmjs.org/accepts/-/accepts-1.3.8.tgz#0bf0be125b67014adcb0b0921e62db7bffe16b2e" - integrity sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw== - dependencies: - mime-types "~2.1.34" - negotiator "0.6.3" - -ansi-regex@^5.0.1: - version "5.0.1" - resolved "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz#082cb2c89c9fe8659a311a53bd6a4dc5301db304" - integrity sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ== - -ansi-regex@^6.0.1: - version "6.1.0" - resolved "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.1.0.tgz#95ec409c69619d6cb1b8b34f14b660ef28ebd654" - integrity sha512-7HSX4QQb4CspciLpVFwyRe79O3xsIZDDLER21kERQ71oaPodF8jL725AgJMFAYbooIqolJoRLuM81SpeUkpkvA== - -ansi-styles@^4.0.0: - version "4.3.0" - resolved "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz#edd803628ae71c04c85ae7a0906edad34b648937" - integrity sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg== - dependencies: - color-convert "^2.0.1" - -ansi-styles@^6.1.0: - version "6.2.1" - resolved "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.1.tgz#0e62320cf99c21afff3b3012192546aacbfb05c5" - integrity sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug== - -arg@^5.0.1: - version "5.0.2" - resolved "https://registry.npmjs.org/arg/-/arg-5.0.2.tgz#c81433cc427c92c4dcf4865142dbca6f15acd59c" - integrity sha512-PYjyFOLKQ9y57JvQ6QLo8dAgNqswh8M1RMJYdQduT6xbWSgK36P/Z/v+p888pM69jMMfS8Xd8F6I1kQ/I9HUGg== - -array-flatten@1.1.1: - version "1.1.1" - resolved "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz#9a5f699051b1e7073328f2a008968b64ea2955d2" - integrity sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg== - -babel-dead-code-elimination@^1.0.6: - version "1.0.10" - resolved "https://registry.npmjs.org/babel-dead-code-elimination/-/babel-dead-code-elimination-1.0.10.tgz#e230562b57bf72ff3de4639ac763ba54f15d37b0" - integrity sha512-DV5bdJZTzZ0zn0DC24v3jD7Mnidh6xhKa4GfKCbq3sfW8kaWhDdZjP3i81geA8T33tdYqWKw4D3fVv0CwEgKVA== - dependencies: - "@babel/core" "^7.23.7" - "@babel/parser" "^7.23.6" - "@babel/traverse" "^7.23.7" - "@babel/types" "^7.23.6" - -balanced-match@^1.0.0: - version "1.0.2" - resolved "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz#e83e3a7e3f300b34cb9d87f615fa0cbf357690ee" - integrity sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw== - -basic-auth@~2.0.1: - version "2.0.1" - resolved "https://registry.npmjs.org/basic-auth/-/basic-auth-2.0.1.tgz#b998279bf47ce38344b4f3cf916d4679bbf51e3a" - integrity sha512-NF+epuEdnUYVlGuhaxbbq+dvJttwLnGY+YixlXlME5KpQ5W3CnXA5cVTneY3SPbPDRkcjMbifrwmFYcClgOZeg== - dependencies: - safe-buffer "5.1.2" - -body-parser@1.20.3: - version "1.20.3" - resolved "https://registry.npmjs.org/body-parser/-/body-parser-1.20.3.tgz#1953431221c6fb5cd63c4b36d53fab0928e548c6" - integrity sha512-7rAxByjUMqQ3/bHJy7D6OGXvx/MMc4IqBn/X0fcM1QUcAItpZrBEYhWGem+tzXH90c+G01ypMcYJBO9Y30203g== - dependencies: - bytes "3.1.2" - content-type "~1.0.5" - debug "2.6.9" - depd "2.0.0" - destroy "1.2.0" - http-errors "2.0.0" - iconv-lite "0.4.24" - on-finished "2.4.1" - qs "6.13.0" - raw-body "2.5.2" - type-is "~1.6.18" - unpipe "1.0.0" - -brace-expansion@^2.0.1: - version "2.0.1" - resolved "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz#1edc459e0f0c548486ecf9fc99f2221364b9a0ae" - integrity sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA== - dependencies: - balanced-match "^1.0.0" - -browserslist@^4.24.0: - version "4.24.5" - resolved "https://registry.npmjs.org/browserslist/-/browserslist-4.24.5.tgz#aa0f5b8560fe81fde84c6dcb38f759bafba0e11b" - integrity sha512-FDToo4Wo82hIdgc1CQ+NQD0hEhmpPjrZ3hiUgwgOG6IuTdlpr8jdjyG24P6cNP1yJpTLzS5OcGgSw0xmDU1/Tw== - dependencies: - caniuse-lite "^1.0.30001716" - electron-to-chromium "^1.5.149" - node-releases "^2.0.19" - update-browserslist-db "^1.1.3" - -buffer-from@^1.0.0: - version "1.1.2" - resolved "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz#2b146a6fd72e80b4f55d255f35ed59a3a9a41bd5" - integrity sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ== - -bytes@3.1.2: - version "3.1.2" - resolved "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz#8b0beeb98605adf1b128fa4386403c009e0221a5" - integrity sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg== - -cac@^6.7.14: - version "6.7.14" - resolved "https://registry.npmjs.org/cac/-/cac-6.7.14.tgz#804e1e6f506ee363cb0e3ccbb09cad5dd9870959" - integrity sha512-b6Ilus+c3RrdDk+JhLKUAQfzzgLEPy6wcXqS7f/xe1EETvsDP6GORG7SFuOs6cID5YkqchW/LXZbX5bc8j7ZcQ== - -call-bind-apply-helpers@^1.0.1, call-bind-apply-helpers@^1.0.2: - version "1.0.2" - resolved "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz#4b5428c222be985d79c3d82657479dbe0b59b2d6" - integrity sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ== - dependencies: - es-errors "^1.3.0" - function-bind "^1.1.2" - -call-bound@^1.0.2: - version "1.0.4" - resolved "https://registry.npmjs.org/call-bound/-/call-bound-1.0.4.tgz#238de935d2a2a692928c538c7ccfa91067fd062a" - integrity sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg== - dependencies: - call-bind-apply-helpers "^1.0.2" - get-intrinsic "^1.3.0" - -caniuse-lite@^1.0.30001716: - version "1.0.30001718" - resolved "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001718.tgz#dae13a9c80d517c30c6197515a96131c194d8f82" - integrity sha512-AflseV1ahcSunK53NfEs9gFWgOEmzr0f+kaMFA4xiLZlr9Hzt7HxcSpIFcnNCUkz6R6dWKa54rUz3HUmI3nVcw== - -chokidar@^4.0.0: - version "4.0.3" - resolved "https://registry.npmjs.org/chokidar/-/chokidar-4.0.3.tgz#7be37a4c03c9aee1ecfe862a4a23b2c70c205d30" - integrity sha512-Qgzu8kfBvo+cA4962jnP1KkS6Dop5NS6g7R5LFYJr4b8Ub94PPQXUksCw9PvXoeXPRRddRNC5C1JQUR2SMGtnA== - dependencies: - readdirp "^4.0.1" - -chownr@^3.0.0: - version "3.0.0" - resolved "https://registry.npmjs.org/chownr/-/chownr-3.0.0.tgz#9855e64ecd240a9cc4267ce8a4aa5d24a1da15e4" - integrity sha512-+IxzY9BZOQd/XuYPRmrvEVjF/nqj5kgT4kEq7VofrDoM1MxoRjEWkrCC3EtLi59TVawxTAn+orJwFQcrqEN1+g== - -color-convert@^2.0.1: - version "2.0.1" - resolved "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz#72d3a68d598c9bdb3af2ad1e84f21d896abd4de3" - integrity sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ== - dependencies: - color-name "~1.1.4" - -color-name@~1.1.4: - version "1.1.4" - resolved "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz#c2a09a87acbde69543de6f63fa3995c826c536a2" - integrity sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA== - -compressible@~2.0.18: - version "2.0.18" - resolved "https://registry.npmjs.org/compressible/-/compressible-2.0.18.tgz#af53cca6b070d4c3c0750fbd77286a6d7cc46fba" - integrity sha512-AF3r7P5dWxL8MxyITRMlORQNaOA2IkAFaTr4k7BUumjPtRpGDTZpl0Pb1XCO6JeDCBdp126Cgs9sMxqSjgYyRg== - dependencies: - mime-db ">= 1.43.0 < 2" - -compression@^1.7.4: - version "1.8.0" - resolved "https://registry.npmjs.org/compression/-/compression-1.8.0.tgz#09420efc96e11a0f44f3a558de59e321364180f7" - integrity sha512-k6WLKfunuqCYD3t6AsuPGvQWaKwuLLh2/xHNcX4qE+vIfDNXpSqnrhwA7O53R7WVQUnt8dVAIW+YHr7xTgOgGA== - dependencies: - bytes "3.1.2" - compressible "~2.0.18" - debug "2.6.9" - negotiator "~0.6.4" - on-headers "~1.0.2" - safe-buffer "5.2.1" - vary "~1.1.2" - -content-disposition@0.5.4: - version "0.5.4" - resolved "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.4.tgz#8b82b4efac82512a02bb0b1dcec9d2c5e8eb5bfe" - integrity sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ== - dependencies: - safe-buffer "5.2.1" - -content-type@~1.0.4, content-type@~1.0.5: - version "1.0.5" - resolved "https://registry.npmjs.org/content-type/-/content-type-1.0.5.tgz#8b773162656d1d1086784c8f23a54ce6d73d7918" - integrity sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA== - -convert-source-map@^2.0.0: - version "2.0.0" - resolved "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz#4b560f649fc4e918dd0ab75cf4961e8bc882d82a" - integrity sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg== - -cookie-signature@1.0.6: - version "1.0.6" - resolved "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz#e303a882b342cc3ee8ca513a79999734dab3ae2c" - integrity sha512-QADzlaHc8icV8I7vbaJXJwod9HWYp8uCqf1xa4OfNu1T7JVxQIrUgOWtHdNDtPiywmFbiS12VjotIXLrKM3orQ== - -cookie@0.7.1: - version "0.7.1" - resolved "https://registry.npmjs.org/cookie/-/cookie-0.7.1.tgz#2f73c42142d5d5cf71310a74fc4ae61670e5dbc9" - integrity sha512-6DnInpx7SJ2AK3+CTUE/ZM0vWTUboZCegxhC2xiIydHR9jNuTAASBrfEpHhiGOZw/nX51bHt6YQl8jsGo4y/0w== - -cookie@^1.0.1: - version "1.0.2" - resolved "https://registry.npmjs.org/cookie/-/cookie-1.0.2.tgz#27360701532116bd3f1f9416929d176afe1e4610" - integrity sha512-9Kr/j4O16ISv8zBBhJoi4bXOYNTkFLOqSL3UDB0njXxCXNezjeyVrJyGOWtgfs/q2km1gwBcfH8q1yEGoMYunA== - -cross-spawn@^7.0.6: - version "7.0.6" - resolved "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz#8a58fe78f00dcd70c370451759dfbfaf03e8ee9f" - integrity sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA== - dependencies: - path-key "^3.1.0" - shebang-command "^2.0.0" - which "^2.0.1" - -csstype@^3.0.2: - version "3.1.3" - resolved "https://registry.npmjs.org/csstype/-/csstype-3.1.3.tgz#d80ff294d114fb0e6ac500fbf85b60137d7eff81" - integrity sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw== - -debug@2.6.9: - version "2.6.9" - resolved "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz#5d128515df134ff327e90a4c93f4e077a536341f" - integrity sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA== - dependencies: - ms "2.0.0" - -debug@^4.1.0, debug@^4.1.1, debug@^4.3.1, debug@^4.4.0: - version "4.4.1" - resolved "https://registry.npmjs.org/debug/-/debug-4.4.1.tgz#e5a8bc6cbc4c6cd3e64308b0693a3d4fa550189b" - integrity sha512-KcKCqiftBJcZr++7ykoDIEwSa3XWowTfNPo92BYxjXiyYEVrUQh2aLyhxBCwww+heortUFxEJYcRzosstTEBYQ== - dependencies: - ms "^2.1.3" - -dedent@^1.5.3: - version "1.6.0" - resolved "https://registry.npmjs.org/dedent/-/dedent-1.6.0.tgz#79d52d6389b1ffa67d2bcef59ba51847a9d503b2" - integrity sha512-F1Z+5UCFpmQUzJa11agbyPVMbpgT/qA3/SKyJ1jyBgm7dUcUEa8v9JwDkerSQXfakBwFljIxhOJqGkjUwZ9FSA== - -depd@2.0.0, depd@~2.0.0: - version "2.0.0" - resolved "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz#b696163cc757560d09cf22cc8fad1571b79e76df" - integrity sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw== - -destroy@1.2.0: - version "1.2.0" - resolved "https://registry.npmjs.org/destroy/-/destroy-1.2.0.tgz#4803735509ad8be552934c67df614f94e66fa015" - integrity sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg== - -detect-libc@^2.0.3, detect-libc@^2.0.4: - version "2.0.4" - resolved "https://registry.npmjs.org/detect-libc/-/detect-libc-2.0.4.tgz#f04715b8ba815e53b4d8109655b6508a6865a7e8" - integrity sha512-3UDv+G9CsCKO1WKMGw9fwq/SWJYbI0c5Y7LU1AXYoDdbhE2AHQ6N6Nb34sG8Fj7T5APy8qXDCKuuIHd1BR0tVA== - -dunder-proto@^1.0.1: - version "1.0.1" - resolved "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz#d7ae667e1dc83482f8b70fd0f6eefc50da30f58a" - integrity sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A== - dependencies: - call-bind-apply-helpers "^1.0.1" - es-errors "^1.3.0" - gopd "^1.2.0" - -eastasianwidth@^0.2.0: - version "0.2.0" - resolved "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz#696ce2ec0aa0e6ea93a397ffcf24aa7840c827cb" - integrity sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA== - -ee-first@1.1.1: - version "1.1.1" - resolved "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz#590c61156b0ae2f4f0255732a158b266bc56b21d" - integrity sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow== - -electron-to-chromium@^1.5.149: - version "1.5.155" - resolved "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.155.tgz#809dd0ae9ae1db87c358e0c0c17c09a2ffc432d1" - integrity sha512-ps5KcGGmwL8VaeJlvlDlu4fORQpv3+GIcF5I3f9tUKUlJ/wsysh6HU8P5L1XWRYeXfA0oJd4PyM8ds8zTFf6Ng== - -emoji-regex@^8.0.0: - version "8.0.0" - resolved "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz#e818fd69ce5ccfcb404594f842963bf53164cc37" - integrity sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A== - -emoji-regex@^9.2.2: - version "9.2.2" - resolved "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz#840c8803b0d8047f4ff0cf963176b32d4ef3ed72" - integrity sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg== - -encodeurl@~1.0.2: - version "1.0.2" - resolved "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz#ad3ff4c86ec2d029322f5a02c3a9a606c95b3f59" - integrity sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w== - -encodeurl@~2.0.0: - version "2.0.0" - resolved "https://registry.npmjs.org/encodeurl/-/encodeurl-2.0.0.tgz#7b8ea898077d7e409d3ac45474ea38eaf0857a58" - integrity sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg== - -enhanced-resolve@^5.18.1: - version "5.18.1" - resolved "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.18.1.tgz#728ab082f8b7b6836de51f1637aab5d3b9568faf" - integrity sha512-ZSW3ma5GkcQBIpwZTSRAI8N71Uuwgs93IezB7mf7R60tC8ZbJideoDNKjHn2O9KIlx6rkGTTEk1xUCK2E1Y2Yg== - dependencies: - graceful-fs "^4.2.4" - tapable "^2.2.0" - -err-code@^2.0.2: - version "2.0.3" - resolved "https://registry.npmjs.org/err-code/-/err-code-2.0.3.tgz#23c2f3b756ffdfc608d30e27c9a941024807e7f9" - integrity sha512-2bmlRpNKBxT/CRmPOlyISQpNj+qSeYvcym/uT0Jx2bMOlKLtSy1ZmLuVxSEKKyor/N5yhvp/ZiG1oE3DEYMSFA== - -es-define-property@^1.0.1: - version "1.0.1" - resolved "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz#983eb2f9a6724e9303f61addf011c72e09e0b0fa" - integrity sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g== - -es-errors@^1.3.0: - version "1.3.0" - resolved "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz#05f75a25dab98e4fb1dcd5e1472c0546d5057c8f" - integrity sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw== - -es-module-lexer@^1.3.1, es-module-lexer@^1.5.4: - version "1.7.0" - resolved "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-1.7.0.tgz#9159601561880a85f2734560a9099b2c31e5372a" - integrity sha512-jEQoCwk8hyb2AZziIOLhDqpm5+2ww5uIE6lkO/6jcOCusfk6LhMHpXXfBLXTZ7Ydyt0j4VoUQv6uGNYbdW+kBA== - -es-object-atoms@^1.0.0, es-object-atoms@^1.1.1: - version "1.1.1" - resolved "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.1.tgz#1c4f2c4837327597ce69d2ca190a7fdd172338c1" - integrity sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA== - dependencies: - es-errors "^1.3.0" - -esbuild@^0.25.0: - version "0.25.4" - resolved "https://registry.npmjs.org/esbuild/-/esbuild-0.25.4.tgz#bb9a16334d4ef2c33c7301a924b8b863351a0854" - integrity sha512-8pgjLUcUjcgDg+2Q4NYXnPbo/vncAY4UmyaCm0jZevERqCHZIaWwdJHkf8XQtu4AxSKCdvrUbT0XUr1IdZzI8Q== - optionalDependencies: - "@esbuild/aix-ppc64" "0.25.4" - "@esbuild/android-arm" "0.25.4" - "@esbuild/android-arm64" "0.25.4" - "@esbuild/android-x64" "0.25.4" - "@esbuild/darwin-arm64" "0.25.4" - "@esbuild/darwin-x64" "0.25.4" - "@esbuild/freebsd-arm64" "0.25.4" - "@esbuild/freebsd-x64" "0.25.4" - "@esbuild/linux-arm" "0.25.4" - "@esbuild/linux-arm64" "0.25.4" - "@esbuild/linux-ia32" "0.25.4" - "@esbuild/linux-loong64" "0.25.4" - "@esbuild/linux-mips64el" "0.25.4" - "@esbuild/linux-ppc64" "0.25.4" - "@esbuild/linux-riscv64" "0.25.4" - "@esbuild/linux-s390x" "0.25.4" - "@esbuild/linux-x64" "0.25.4" - "@esbuild/netbsd-arm64" "0.25.4" - "@esbuild/netbsd-x64" "0.25.4" - "@esbuild/openbsd-arm64" "0.25.4" - "@esbuild/openbsd-x64" "0.25.4" - "@esbuild/sunos-x64" "0.25.4" - "@esbuild/win32-arm64" "0.25.4" - "@esbuild/win32-ia32" "0.25.4" - "@esbuild/win32-x64" "0.25.4" - -escalade@^3.2.0: - version "3.2.0" - resolved "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz#011a3f69856ba189dffa7dc8fcce99d2a87903e5" - integrity sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA== - -escape-html@~1.0.3: - version "1.0.3" - resolved "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz#0258eae4d3d0c0974de1c169188ef0051d1d1988" - integrity sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow== - -etag@~1.8.1: - version "1.8.1" - resolved "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz#41ae2eeb65efa62268aebfea83ac7d79299b0887" - integrity sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg== - -exit-hook@2.2.1: - version "2.2.1" - resolved "https://registry.npmjs.org/exit-hook/-/exit-hook-2.2.1.tgz#007b2d92c6428eda2b76e7016a34351586934593" - integrity sha512-eNTPlAD67BmP31LDINZ3U7HSF8l57TxOY2PmBJ1shpCvpnxBF93mWCE8YHBnXs8qiUZJc9WDcWIeC3a2HIAMfw== - -express@^4.19.2: - version "4.21.2" - resolved "https://registry.npmjs.org/express/-/express-4.21.2.tgz#cf250e48362174ead6cea4a566abef0162c1ec32" - integrity sha512-28HqgMZAmih1Czt9ny7qr6ek2qddF4FclbMzwhCREB6OFfH+rXAnuNCwo1/wFvrtbgsQDb4kSbX9de9lFbrXnA== - dependencies: - accepts "~1.3.8" - array-flatten "1.1.1" - body-parser "1.20.3" - content-disposition "0.5.4" - content-type "~1.0.4" - cookie "0.7.1" - cookie-signature "1.0.6" - debug "2.6.9" - depd "2.0.0" - encodeurl "~2.0.0" - escape-html "~1.0.3" - etag "~1.8.1" - finalhandler "1.3.1" - fresh "0.5.2" - http-errors "2.0.0" - merge-descriptors "1.0.3" - methods "~1.1.2" - on-finished "2.4.1" - parseurl "~1.3.3" - path-to-regexp "0.1.12" - proxy-addr "~2.0.7" - qs "6.13.0" - range-parser "~1.2.1" - safe-buffer "5.2.1" - send "0.19.0" - serve-static "1.16.2" - setprototypeof "1.2.0" - statuses "2.0.1" - type-is "~1.6.18" - utils-merge "1.0.1" - vary "~1.1.2" - -fdir@^6.4.4: - version "6.4.4" - resolved "https://registry.npmjs.org/fdir/-/fdir-6.4.4.tgz#1cfcf86f875a883e19a8fab53622cfe992e8d2f9" - integrity sha512-1NZP+GK4GfuAv3PqKvxQRDMjdSRZjnkq7KfhlNrCNNlZ0ygQFpebfrnfnq/W7fpUnAv9aGWmY1zKx7FYL3gwhg== - -finalhandler@1.3.1: - version "1.3.1" - resolved "https://registry.npmjs.org/finalhandler/-/finalhandler-1.3.1.tgz#0c575f1d1d324ddd1da35ad7ece3df7d19088019" - integrity sha512-6BN9trH7bp3qvnrRyzsBz+g3lZxTNZTbVO2EV1CS0WIcDbawYVdYvGflME/9QP0h0pYlCDBCTjYa9nZzMDpyxQ== - dependencies: - debug "2.6.9" - encodeurl "~2.0.0" - escape-html "~1.0.3" - on-finished "2.4.1" - parseurl "~1.3.3" - statuses "2.0.1" - unpipe "~1.0.0" - -foreground-child@^3.1.0: - version "3.3.1" - resolved "https://registry.npmjs.org/foreground-child/-/foreground-child-3.3.1.tgz#32e8e9ed1b68a3497befb9ac2b6adf92a638576f" - integrity sha512-gIXjKqtFuWEgzFRJA9WCQeSJLZDjgJUOMCMzxtvFq/37KojM1BFGufqsCy0r4qSQmYLsZYMeyRqzIWOMup03sw== - dependencies: - cross-spawn "^7.0.6" - signal-exit "^4.0.1" - -forwarded@0.2.0: - version "0.2.0" - resolved "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz#2269936428aad4c15c7ebe9779a84bf0b2a81811" - integrity sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow== - -fresh@0.5.2: - version "0.5.2" - resolved "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz#3d8cadd90d976569fa835ab1f8e4b23a105605a7" - integrity sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q== - -fs-extra@^10.0.0: - version "10.1.0" - resolved "https://registry.npmjs.org/fs-extra/-/fs-extra-10.1.0.tgz#02873cfbc4084dde127eaa5f9905eef2325d1abf" - integrity sha512-oRXApq54ETRj4eMiFzGnHWGy+zo5raudjuxN0b8H7s/RU2oW0Wvsx9O0ACRN/kRq9E8Vu/ReskGB5o3ji+FzHQ== - dependencies: - graceful-fs "^4.2.0" - jsonfile "^6.0.1" - universalify "^2.0.0" - -fsevents@~2.3.2, fsevents@~2.3.3: - version "2.3.3" - resolved "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz#cac6407785d03675a2a5e1a5305c697b347d90d6" - integrity sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw== - -function-bind@^1.1.2: - version "1.1.2" - resolved "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz#2c02d864d97f3ea6c8830c464cbd11ab6eab7a1c" - integrity sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA== - -gensync@^1.0.0-beta.2: - version "1.0.0-beta.2" - resolved "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz#32a6ee76c3d7f52d46b2b1ae5d93fea8580a25e0" - integrity sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg== - -get-intrinsic@^1.2.5, get-intrinsic@^1.3.0: - version "1.3.0" - resolved "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz#743f0e3b6964a93a5491ed1bffaae054d7f98d01" - integrity sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ== - dependencies: - call-bind-apply-helpers "^1.0.2" - es-define-property "^1.0.1" - es-errors "^1.3.0" - es-object-atoms "^1.1.1" - function-bind "^1.1.2" - get-proto "^1.0.1" - gopd "^1.2.0" - has-symbols "^1.1.0" - hasown "^2.0.2" - math-intrinsics "^1.1.0" - -get-port@5.1.1: - version "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-proto@^1.0.1: - version "1.0.1" - resolved "https://registry.npmjs.org/get-proto/-/get-proto-1.0.1.tgz#150b3f2743869ef3e851ec0c49d15b1d14d00ee1" - integrity sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g== - dependencies: - dunder-proto "^1.0.1" - es-object-atoms "^1.0.0" - -glob@^10.2.2: - version "10.4.5" - resolved "https://registry.npmjs.org/glob/-/glob-10.4.5.tgz#f4d9f0b90ffdbab09c9d77f5f29b4262517b0956" - integrity sha512-7Bv8RF0k6xjo7d4A/PxYLbUCfb6c+Vpd2/mB2yRDlew7Jb5hEXiCD9ibfO7wpk8i4sevK6DFny9h7EYbM3/sHg== - dependencies: - foreground-child "^3.1.0" - jackspeak "^3.1.2" - minimatch "^9.0.4" - minipass "^7.1.2" - package-json-from-dist "^1.0.0" - path-scurry "^1.11.1" - -globals@^11.1.0: - version "11.12.0" - resolved "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz#ab8795338868a0babd8525758018c2a7eb95c42e" - integrity sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA== - -globrex@^0.1.2: - version "0.1.2" - resolved "https://registry.npmjs.org/globrex/-/globrex-0.1.2.tgz#dd5d9ec826232730cd6793a5e33a9302985e6098" - integrity sha512-uHJgbwAMwNFf5mLst7IWLNg14x1CkeqglJb/K3doi4dw6q2IvAAmM/Y81kevy83wP+Sst+nutFTYOGg3d1lsxg== - -gopd@^1.2.0: - version "1.2.0" - resolved "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz#89f56b8217bdbc8802bd299df6d7f1081d7e51a1" - integrity sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg== - -graceful-fs@^4.1.6, graceful-fs@^4.2.0, graceful-fs@^4.2.4: - version "4.2.11" - resolved "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz#4183e4e8bf08bb6e05bbb2f7d2e0c8f712ca40e3" - integrity sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ== - -has-symbols@^1.1.0: - version "1.1.0" - resolved "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz#fc9c6a783a084951d0b971fe1018de813707a338" - integrity sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ== - -hasown@^2.0.2: - version "2.0.2" - resolved "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz#003eaf91be7adc372e84ec59dc37252cedb80003" - integrity sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ== - dependencies: - function-bind "^1.1.2" - -hosted-git-info@^6.0.0, hosted-git-info@^6.1.1: - version "6.1.3" - resolved "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-6.1.3.tgz#2ee1a14a097a1236bddf8672c35b613c46c55946" - integrity sha512-HVJyzUrLIL1c0QmviVh5E8VGyUS7xCFPS6yydaVd1UegW+ibV/CohqTH9MkOLDp5o+rb82DMo77PTuc9F/8GKw== - dependencies: - lru-cache "^7.5.1" - -http-errors@2.0.0: - version "2.0.0" - resolved "https://registry.npmjs.org/http-errors/-/http-errors-2.0.0.tgz#b7774a1486ef73cf7667ac9ae0858c012c57b9d3" - integrity sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ== - dependencies: - depd "2.0.0" - inherits "2.0.4" - setprototypeof "1.2.0" - statuses "2.0.1" - toidentifier "1.0.1" - -iconv-lite@0.4.24: - version "0.4.24" - resolved "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz#2022b4b25fbddc21d2f524974a474aafe733908b" - integrity sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA== - dependencies: - safer-buffer ">= 2.1.2 < 3" - -inherits@2.0.4: - version "2.0.4" - resolved "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz#0fa2c64f932917c3433a0ded55363aae37416b7c" - integrity sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ== - -ipaddr.js@1.9.1: - version "1.9.1" - resolved "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz#bff38543eeb8984825079ff3a2a8e6cbd46781b3" - integrity sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g== - -is-core-module@^2.8.1: - version "2.16.1" - resolved "https://registry.npmjs.org/is-core-module/-/is-core-module-2.16.1.tgz#2a98801a849f43e2add644fbb6bc6229b19a4ef4" - integrity sha512-UfoeMA6fIJ8wTYFEUjelnaGI67v6+N7qXJEvQuIGa99l4xsCruSYOVSQ0uPANn4dAzm8lkYPaKLrrijLq7x23w== - dependencies: - hasown "^2.0.2" - -is-fullwidth-code-point@^3.0.0: - version "3.0.0" - resolved "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz#f116f8064fe90b3f7844a38997c0b75051269f1d" - integrity sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg== - -isbot@^5.1.27: - version "5.1.28" - resolved "https://registry.npmjs.org/isbot/-/isbot-5.1.28.tgz#a9a32e70c890cf19b76090971b1fccf6021a519b" - integrity sha512-qrOp4g3xj8YNse4biorv6O5ZShwsJM0trsoda4y7j/Su7ZtTTfVXFzbKkpgcSoDrHS8FcTuUwcU04YimZlZOxw== - -isexe@^2.0.0: - version "2.0.0" - resolved "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz#e8fbf374dc556ff8947a10dcb0572d633f2cfa10" - integrity sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw== - -jackspeak@^3.1.2: - version "3.4.3" - resolved "https://registry.npmjs.org/jackspeak/-/jackspeak-3.4.3.tgz#8833a9d89ab4acde6188942bd1c53b6390ed5a8a" - integrity sha512-OGlZQpz2yfahA/Rd1Y8Cd9SIEsqvXkLVoSw/cgwhnhFMDbsQFeZYoJJ7bIZBS9BcamUW96asq/npPWugM+RQBw== - dependencies: - "@isaacs/cliui" "^8.0.2" - optionalDependencies: - "@pkgjs/parseargs" "^0.11.0" - -jiti@^2.4.2: - version "2.4.2" - resolved "https://registry.npmjs.org/jiti/-/jiti-2.4.2.tgz#d19b7732ebb6116b06e2038da74a55366faef560" - integrity sha512-rg9zJN+G4n2nfJl5MW3BMygZX56zKPNVEYYqq7adpmMh4Jn2QNEwhvQlFy6jPVdcod7txZtKHWnyZiA3a0zP7A== - -js-tokens@^4.0.0: - version "4.0.0" - resolved "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz#19203fb59991df98e3a287050d4647cdeaf32499" - integrity sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ== - -jsesc@3.0.2: - version "3.0.2" - resolved "https://registry.npmjs.org/jsesc/-/jsesc-3.0.2.tgz#bb8b09a6597ba426425f2e4a07245c3d00b9343e" - integrity sha512-xKqzzWXDttJuOcawBt4KnKHHIf5oQ/Cxax+0PWFG+DFDgHNAdi+TXECADI+RYiFUMmx8792xsMbbgXj4CwnP4g== - -jsesc@^3.0.2: - version "3.1.0" - resolved "https://registry.npmjs.org/jsesc/-/jsesc-3.1.0.tgz#74d335a234f67ed19907fdadfac7ccf9d409825d" - integrity sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA== - -json-parse-even-better-errors@^3.0.0: - version "3.0.2" - resolved "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-3.0.2.tgz#b43d35e89c0f3be6b5fbbe9dc6c82467b30c28da" - integrity sha512-fi0NG4bPjCHunUJffmLd0gxssIgkNmArMvis4iNah6Owg1MCJjWhEcDLmsK6iGkJq3tHwbDkTlce70/tmXN4cQ== - -json5@^2.2.3: - version "2.2.3" - resolved "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz#78cd6f1a19bdc12b73db5ad0c61efd66c1e29283" - integrity sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg== - -jsonfile@^6.0.1: - version "6.1.0" - resolved "https://registry.npmjs.org/jsonfile/-/jsonfile-6.1.0.tgz#bc55b2634793c679ec6403094eb13698a6ec0aae" - integrity sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ== - dependencies: - universalify "^2.0.0" - optionalDependencies: - graceful-fs "^4.1.6" - -lightningcss-darwin-arm64@1.30.1: - version "1.30.1" - resolved "https://registry.npmjs.org/lightningcss-darwin-arm64/-/lightningcss-darwin-arm64-1.30.1.tgz#3d47ce5e221b9567c703950edf2529ca4a3700ae" - integrity sha512-c8JK7hyE65X1MHMN+Viq9n11RRC7hgin3HhYKhrMyaXflk5GVplZ60IxyoVtzILeKr+xAJwg6zK6sjTBJ0FKYQ== - -lightningcss-darwin-x64@1.30.1: - version "1.30.1" - resolved "https://registry.npmjs.org/lightningcss-darwin-x64/-/lightningcss-darwin-x64-1.30.1.tgz#e81105d3fd6330860c15fe860f64d39cff5fbd22" - integrity sha512-k1EvjakfumAQoTfcXUcHQZhSpLlkAuEkdMBsI/ivWw9hL+7FtilQc0Cy3hrx0AAQrVtQAbMI7YjCgYgvn37PzA== - -lightningcss-freebsd-x64@1.30.1: - version "1.30.1" - resolved "https://registry.npmjs.org/lightningcss-freebsd-x64/-/lightningcss-freebsd-x64-1.30.1.tgz#a0e732031083ff9d625c5db021d09eb085af8be4" - integrity sha512-kmW6UGCGg2PcyUE59K5r0kWfKPAVy4SltVeut+umLCFoJ53RdCUWxcRDzO1eTaxf/7Q2H7LTquFHPL5R+Gjyig== - -lightningcss-linux-arm-gnueabihf@1.30.1: - version "1.30.1" - resolved "https://registry.npmjs.org/lightningcss-linux-arm-gnueabihf/-/lightningcss-linux-arm-gnueabihf-1.30.1.tgz#1f5ecca6095528ddb649f9304ba2560c72474908" - integrity sha512-MjxUShl1v8pit+6D/zSPq9S9dQ2NPFSQwGvxBCYaBYLPlCWuPh9/t1MRS8iUaR8i+a6w7aps+B4N0S1TYP/R+Q== - -lightningcss-linux-arm64-gnu@1.30.1: - version "1.30.1" - resolved "https://registry.npmjs.org/lightningcss-linux-arm64-gnu/-/lightningcss-linux-arm64-gnu-1.30.1.tgz#eee7799726103bffff1e88993df726f6911ec009" - integrity sha512-gB72maP8rmrKsnKYy8XUuXi/4OctJiuQjcuqWNlJQ6jZiWqtPvqFziskH3hnajfvKB27ynbVCucKSm2rkQp4Bw== - -lightningcss-linux-arm64-musl@1.30.1: - version "1.30.1" - resolved "https://registry.npmjs.org/lightningcss-linux-arm64-musl/-/lightningcss-linux-arm64-musl-1.30.1.tgz#f2e4b53f42892feeef8f620cbb889f7c064a7dfe" - integrity sha512-jmUQVx4331m6LIX+0wUhBbmMX7TCfjF5FoOH6SD1CttzuYlGNVpA7QnrmLxrsub43ClTINfGSYyHe2HWeLl5CQ== - -lightningcss-linux-x64-gnu@1.30.1: - version "1.30.1" - resolved "https://registry.npmjs.org/lightningcss-linux-x64-gnu/-/lightningcss-linux-x64-gnu-1.30.1.tgz#2fc7096224bc000ebb97eea94aea248c5b0eb157" - integrity sha512-piWx3z4wN8J8z3+O5kO74+yr6ze/dKmPnI7vLqfSqI8bccaTGY5xiSGVIJBDd5K5BHlvVLpUB3S2YCfelyJ1bw== - -lightningcss-linux-x64-musl@1.30.1: - version "1.30.1" - resolved "https://registry.npmjs.org/lightningcss-linux-x64-musl/-/lightningcss-linux-x64-musl-1.30.1.tgz#66dca2b159fd819ea832c44895d07e5b31d75f26" - integrity sha512-rRomAK7eIkL+tHY0YPxbc5Dra2gXlI63HL+v1Pdi1a3sC+tJTcFrHX+E86sulgAXeI7rSzDYhPSeHHjqFhqfeQ== - -lightningcss-win32-arm64-msvc@1.30.1: - version "1.30.1" - resolved "https://registry.npmjs.org/lightningcss-win32-arm64-msvc/-/lightningcss-win32-arm64-msvc-1.30.1.tgz#7d8110a19d7c2d22bfdf2f2bb8be68e7d1b69039" - integrity sha512-mSL4rqPi4iXq5YVqzSsJgMVFENoa4nGTT/GjO2c0Yl9OuQfPsIfncvLrEW6RbbB24WtZ3xP/2CCmI3tNkNV4oA== - -lightningcss-win32-x64-msvc@1.30.1: - version "1.30.1" - resolved "https://registry.npmjs.org/lightningcss-win32-x64-msvc/-/lightningcss-win32-x64-msvc-1.30.1.tgz#fd7dd008ea98494b85d24b4bea016793f2e0e352" - integrity sha512-PVqXh48wh4T53F/1CCu8PIPCxLzWyCnn/9T5W1Jpmdy5h9Cwd+0YQS6/LwhHXSafuc61/xg9Lv5OrCby6a++jg== - -lightningcss@1.30.1: - version "1.30.1" - resolved "https://registry.npmjs.org/lightningcss/-/lightningcss-1.30.1.tgz#78e979c2d595bfcb90d2a8c0eb632fe6c5bfed5d" - integrity sha512-xi6IyHML+c9+Q3W0S4fCQJOym42pyurFiJUHEcEyHS0CeKzia4yZDEsLlqOFykxOdHpNy0NmvVO31vcSqAxJCg== - dependencies: - detect-libc "^2.0.3" - optionalDependencies: - lightningcss-darwin-arm64 "1.30.1" - lightningcss-darwin-x64 "1.30.1" - lightningcss-freebsd-x64 "1.30.1" - lightningcss-linux-arm-gnueabihf "1.30.1" - lightningcss-linux-arm64-gnu "1.30.1" - lightningcss-linux-arm64-musl "1.30.1" - lightningcss-linux-x64-gnu "1.30.1" - lightningcss-linux-x64-musl "1.30.1" - lightningcss-win32-arm64-msvc "1.30.1" - lightningcss-win32-x64-msvc "1.30.1" - -lodash@^4.17.21: - version "4.17.21" - resolved "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz#679591c564c3bffaae8454cf0b3df370c3d6911c" - integrity sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg== - -lru-cache@^10.2.0: - version "10.4.3" - resolved "https://registry.npmjs.org/lru-cache/-/lru-cache-10.4.3.tgz#410fc8a17b70e598013df257c2446b7f3383f119" - integrity sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ== - -lru-cache@^5.1.1: - version "5.1.1" - resolved "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz#1da27e6710271947695daf6848e847f01d84b920" - integrity sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w== - dependencies: - yallist "^3.0.2" - -lru-cache@^7.4.4, lru-cache@^7.5.1: - version "7.18.3" - resolved "https://registry.npmjs.org/lru-cache/-/lru-cache-7.18.3.tgz#f793896e0fd0e954a59dfdd82f0773808df6aa89" - integrity sha512-jumlc0BIUrS3qJGgIkWZsyfAM7NCWiBcCDhnd+3NNM5KbBmLTgHVfWBcg6W+rLUsIpzpERPsvwUP7CckAQSOoA== - -magic-string@^0.30.17: - version "0.30.17" - resolved "https://registry.npmjs.org/magic-string/-/magic-string-0.30.17.tgz#450a449673d2460e5bbcfba9a61916a1714c7453" - integrity sha512-sNPKHvyjVf7gyjwS4xGTaW/mCnF8wnjtifKBEhxfZ7E/S8tQ0rssrwGNn6q8JH/ohItJfSQp9mBtQYuTlH5QnA== - dependencies: - "@jridgewell/sourcemap-codec" "^1.5.0" - -math-intrinsics@^1.1.0: - version "1.1.0" - resolved "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz#a0dd74be81e2aa5c2f27e65ce283605ee4e2b7f9" - integrity sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g== - -media-typer@0.3.0: - version "0.3.0" - resolved "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz#8710d7af0aa626f8fffa1ce00168545263255748" - integrity sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ== - -merge-descriptors@1.0.3: - version "1.0.3" - resolved "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.3.tgz#d80319a65f3c7935351e5cfdac8f9318504dbed5" - integrity sha512-gaNvAS7TZ897/rVaZ0nMtAyxNyi/pdbjbAwUpFQpN70GqnVfOiXpeUUMKRBmzXaSQ8DdTX4/0ms62r2K+hE6mQ== - -methods@~1.1.2: - version "1.1.2" - resolved "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz#5529a4d67654134edcc5266656835b0f851afcee" - integrity sha512-iclAHeNqNm68zFtnZ0e+1L2yUIdvzNoauKU4WBA3VvH/vPFieF7qfRlwUZU+DA9P9bPXIS90ulxoUoCH23sV2w== - -mime-db@1.52.0: - version "1.52.0" - resolved "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz#bbabcdc02859f4987301c856e3387ce5ec43bf70" - integrity sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg== - -"mime-db@>= 1.43.0 < 2": - version "1.54.0" - resolved "https://registry.npmjs.org/mime-db/-/mime-db-1.54.0.tgz#cddb3ee4f9c64530dff640236661d42cb6a314f5" - integrity sha512-aU5EJuIN2WDemCcAp2vFBfp/m4EAhWJnUNSSw0ixs7/kXbd6Pg64EmwJkNdFhB8aWt1sH2CTXrLxo/iAGV3oPQ== - -mime-types@~2.1.24, mime-types@~2.1.34: - version "2.1.35" - resolved "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz#381a871b62a734450660ae3deee44813f70d959a" - integrity sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw== - dependencies: - mime-db "1.52.0" - -mime@1.6.0: - version "1.6.0" - resolved "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz#32cd9e5c64553bd58d19a568af452acff04981b1" - integrity sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg== - -minimatch@^9.0.4: - version "9.0.5" - resolved "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz#d74f9dd6b57d83d8e98cfb82133b03978bc929e5" - integrity sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow== - dependencies: - brace-expansion "^2.0.1" - -"minipass@^5.0.0 || ^6.0.2 || ^7.0.0", minipass@^7.0.4, minipass@^7.1.2: - version "7.1.2" - resolved "https://registry.npmjs.org/minipass/-/minipass-7.1.2.tgz#93a9626ce5e5e66bd4db86849e7515e92340a707" - integrity sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw== - -minizlib@^3.0.1: - version "3.0.2" - resolved "https://registry.npmjs.org/minizlib/-/minizlib-3.0.2.tgz#f33d638eb279f664439aa38dc5f91607468cb574" - integrity sha512-oG62iEk+CYt5Xj2YqI5Xi9xWUeZhDI8jjQmC5oThVH5JGCTgIjr7ciJDzC7MBzYd//WvR1OTmP5Q38Q8ShQtVA== - dependencies: - minipass "^7.1.2" - -mkdirp@^3.0.1: - version "3.0.1" - resolved "https://registry.npmjs.org/mkdirp/-/mkdirp-3.0.1.tgz#e44e4c5607fb279c168241713cc6e0fea9adcb50" - integrity sha512-+NsyUUAZDmo6YVHzL/stxSu3t9YS1iljliy3BSDrXJ/dkn1KYdmtZODGGjLcc9XLgVVpH4KshHB8XmZgMhaBXg== - -morgan@^1.10.0: - version "1.10.0" - resolved "https://registry.npmjs.org/morgan/-/morgan-1.10.0.tgz#091778abc1fc47cd3509824653dae1faab6b17d7" - integrity sha512-AbegBVI4sh6El+1gNwvD5YIck7nSA36weD7xvIxG4in80j/UoK8AEGaWnnz8v1GxonMCltmlNs5ZKbGvl9b1XQ== - dependencies: - basic-auth "~2.0.1" - debug "2.6.9" - depd "~2.0.0" - on-finished "~2.3.0" - on-headers "~1.0.2" - -ms@2.0.0: - version "2.0.0" - resolved "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz#5608aeadfc00be6c2901df5f9861788de0d597c8" - integrity sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A== - -ms@2.1.3, ms@^2.1.3: - version "2.1.3" - resolved "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz#574c8138ce1d2b5861f0b44579dbadd60c6615b2" - integrity sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA== - -nanoid@^3.3.8: - version "3.3.11" - resolved "https://registry.npmjs.org/nanoid/-/nanoid-3.3.11.tgz#4f4f112cefbe303202f2199838128936266d185b" - integrity sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w== - -negotiator@0.6.3: - version "0.6.3" - resolved "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz#58e323a72fedc0d6f9cd4d31fe49f51479590ccd" - integrity sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg== - -negotiator@~0.6.4: - version "0.6.4" - resolved "https://registry.npmjs.org/negotiator/-/negotiator-0.6.4.tgz#777948e2452651c570b712dd01c23e262713fff7" - integrity sha512-myRT3DiWPHqho5PrJaIRyaMv2kgYf0mUVgBNOYMuCH5Ki1yEiQaf/ZJuQ62nvpc44wL5WDbTX7yGJi1Neevw8w== - -node-releases@^2.0.19: - version "2.0.19" - resolved "https://registry.npmjs.org/node-releases/-/node-releases-2.0.19.tgz#9e445a52950951ec4d177d843af370b411caf314" - integrity sha512-xxOWJsBKtzAq7DY0J+DTzuz58K8e7sJbdgwkbMWQe8UYB6ekmsQ45q0M/tJDsGaZmbC+l7n57UV8Hl5tHxO9uw== - -normalize-package-data@^5.0.0: - version "5.0.0" - resolved "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-5.0.0.tgz#abcb8d7e724c40d88462b84982f7cbf6859b4588" - integrity sha512-h9iPVIfrVZ9wVYQnxFgtw1ugSvGEMOlyPWWtm8BMJhnwyEL/FLbYbTY3V3PpjI/BUK67n9PEWDu6eHzu1fB15Q== - dependencies: - hosted-git-info "^6.0.0" - is-core-module "^2.8.1" - semver "^7.3.5" - validate-npm-package-license "^3.0.4" - -npm-install-checks@^6.0.0: - version "6.3.0" - resolved "https://registry.npmjs.org/npm-install-checks/-/npm-install-checks-6.3.0.tgz#046552d8920e801fa9f919cad569545d60e826fe" - integrity sha512-W29RiK/xtpCGqn6f3ixfRYGk+zRyr+Ew9F2E20BfXxT5/euLdA/Nm7fO7OeTGuAmTs30cpgInyJ0cYe708YTZw== - dependencies: - semver "^7.1.1" - -npm-normalize-package-bin@^3.0.0: - version "3.0.1" - resolved "https://registry.npmjs.org/npm-normalize-package-bin/-/npm-normalize-package-bin-3.0.1.tgz#25447e32a9a7de1f51362c61a559233b89947832" - integrity sha512-dMxCf+zZ+3zeQZXKxmyuCKlIDPGuv8EF940xbkC4kQVDTtqoh6rJFO+JTKSA6/Rwi0getWmtuy4Itup0AMcaDQ== - -npm-package-arg@^10.0.0: - version "10.1.0" - resolved "https://registry.npmjs.org/npm-package-arg/-/npm-package-arg-10.1.0.tgz#827d1260a683806685d17193073cc152d3c7e9b1" - integrity sha512-uFyyCEmgBfZTtrKk/5xDfHp6+MdrqGotX/VoOyEEl3mBwiEE5FlBaePanazJSVMPT7vKepcjYBY2ztg9A3yPIA== - dependencies: - hosted-git-info "^6.0.0" - proc-log "^3.0.0" - semver "^7.3.5" - validate-npm-package-name "^5.0.0" - -npm-pick-manifest@^8.0.0: - version "8.0.2" - resolved "https://registry.npmjs.org/npm-pick-manifest/-/npm-pick-manifest-8.0.2.tgz#2159778d9c7360420c925c1a2287b5a884c713aa" - integrity sha512-1dKY+86/AIiq1tkKVD3l0WI+Gd3vkknVGAggsFeBkTvbhMQ1OND/LKkYv4JtXPKUJ8bOTCyLiqEg2P6QNdK+Gg== - dependencies: - npm-install-checks "^6.0.0" - npm-normalize-package-bin "^3.0.0" - npm-package-arg "^10.0.0" - semver "^7.3.5" - -object-inspect@^1.13.3: - version "1.13.4" - resolved "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.4.tgz#8375265e21bc20d0fa582c22e1b13485d6e00213" - integrity sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew== - -on-finished@2.4.1: - version "2.4.1" - resolved "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz#58c8c44116e54845ad57f14ab10b03533184ac3f" - integrity sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg== - dependencies: - ee-first "1.1.1" - -on-finished@~2.3.0: - version "2.3.0" - resolved "https://registry.npmjs.org/on-finished/-/on-finished-2.3.0.tgz#20f1336481b083cd75337992a16971aa2d906947" - integrity sha512-ikqdkGAAyf/X/gPhXGvfgAytDZtDbr+bkNUJ0N9h5MI/dmdgCs3l6hoHrcUv41sRKew3jIwrp4qQDXiK99Utww== - dependencies: - ee-first "1.1.1" - -on-headers@~1.0.2: - version "1.0.2" - resolved "https://registry.npmjs.org/on-headers/-/on-headers-1.0.2.tgz#772b0ae6aaa525c399e489adfad90c403eb3c28f" - integrity sha512-pZAE+FJLoyITytdqK0U5s+FIpjN0JP3OzFi/u8Rx+EV5/W+JTWGXG8xFzevE7AjBfDqHv/8vL8qQsIhHnqRkrA== - -package-json-from-dist@^1.0.0: - version "1.0.1" - resolved "https://registry.npmjs.org/package-json-from-dist/-/package-json-from-dist-1.0.1.tgz#4f1471a010827a86f94cfd9b0727e36d267de505" - integrity sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw== - -parseurl@~1.3.3: - version "1.3.3" - resolved "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz#9da19e7bee8d12dff0513ed5b76957793bc2e8d4" - integrity sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ== - -path-key@^3.1.0: - version "3.1.1" - resolved "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz#581f6ade658cbba65a0d3380de7753295054f375" - integrity sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q== - -path-scurry@^1.11.1: - version "1.11.1" - resolved "https://registry.npmjs.org/path-scurry/-/path-scurry-1.11.1.tgz#7960a668888594a0720b12a911d1a742ab9f11d2" - integrity sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA== - dependencies: - lru-cache "^10.2.0" - minipass "^5.0.0 || ^6.0.2 || ^7.0.0" - -path-to-regexp@0.1.12: - version "0.1.12" - resolved "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.12.tgz#d5e1a12e478a976d432ef3c58d534b9923164bb7" - integrity sha512-RA1GjUVMnvYFxuqovrEqZoxxW5NUZqbwKtYz/Tt7nXerk0LbLblQmrsgdeOxV5SFHf0UDggjS/bSeOZwt1pmEQ== - -pathe@^1.1.2: - version "1.1.2" - resolved "https://registry.npmjs.org/pathe/-/pathe-1.1.2.tgz#6c4cb47a945692e48a1ddd6e4094d170516437ec" - integrity sha512-whLdWMYL2TwI08hn8/ZqAbrVemu0LNaNNJZX73O6qaIdCTfXutsLhMkjdENX0qhsQ9uIimo4/aQOmXkoon2nDQ== - -picocolors@^1.1.1: - version "1.1.1" - resolved "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz#3d321af3eab939b083c8f929a1d12cda81c26b6b" - integrity sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA== - -picomatch@^4.0.2: - version "4.0.2" - resolved "https://registry.npmjs.org/picomatch/-/picomatch-4.0.2.tgz#77c742931e8f3b8820946c76cd0c1f13730d1dab" - integrity sha512-M7BAV6Rlcy5u+m6oPhAPFgJTzAioX/6B0DxyvDlo9l8+T3nLKbrczg2WLUyzd45L8RqfUMyGPzekbMvX2Ldkwg== - -postcss@^8.5.3: - version "8.5.3" - resolved "https://registry.npmjs.org/postcss/-/postcss-8.5.3.tgz#1463b6f1c7fb16fe258736cba29a2de35237eafb" - integrity sha512-dle9A3yYxlBSrt8Fu+IpjGT8SY8hN0mlaA6GY8t0P5PjIOZemULz/E2Bnm/2dcUOena75OTNkHI76uZBNUUq3A== - dependencies: - nanoid "^3.3.8" - picocolors "^1.1.1" - source-map-js "^1.2.1" - -prettier@^2.7.1: - version "2.8.8" - resolved "https://registry.npmjs.org/prettier/-/prettier-2.8.8.tgz#e8c5d7e98a4305ffe3de2e1fc4aca1a71c28b1da" - integrity sha512-tdN8qQGvNjw4CHbY+XXk0JgCXn9QiF21a55rBe5LJAU+kDyC4WQn4+awm2Xfk2lQMk5fKup9XgzTZtGkjBdP9Q== - -proc-log@^3.0.0: - version "3.0.0" - resolved "https://registry.npmjs.org/proc-log/-/proc-log-3.0.0.tgz#fb05ef83ccd64fd7b20bbe9c8c1070fc08338dd8" - integrity sha512-++Vn7NS4Xf9NacaU9Xq3URUuqZETPsf8L4j5/ckhaRYsfPeRyzGw+iDjFhV/Jr3uNmTvvddEJFWh5R1gRgUH8A== - -promise-inflight@^1.0.1: - version "1.0.1" - resolved "https://registry.npmjs.org/promise-inflight/-/promise-inflight-1.0.1.tgz#98472870bf228132fcbdd868129bad12c3c029e3" - integrity sha512-6zWPyEOFaQBJYcGMHBKTKJ3u6TBsnMFOIZSa6ce1e/ZrrsOlnHRHbabMjLiBYKp+n44X9eUI6VUPaukCXHuG4g== - -promise-retry@^2.0.1: - version "2.0.1" - resolved "https://registry.npmjs.org/promise-retry/-/promise-retry-2.0.1.tgz#ff747a13620ab57ba688f5fc67855410c370da22" - integrity sha512-y+WKFlBR8BGXnsNlIHFGPZmyDf3DFMoLhaflAnyZgV6rG6xu+JwesTo2Q9R6XwYmtmwAFCkAk3e35jEdoeh/3g== - dependencies: - err-code "^2.0.2" - retry "^0.12.0" - -proxy-addr@~2.0.7: - version "2.0.7" - resolved "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz#f19fe69ceab311eeb94b42e70e8c2070f9ba1025" - integrity sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg== - dependencies: - forwarded "0.2.0" - ipaddr.js "1.9.1" - -qs@6.13.0: - version "6.13.0" - resolved "https://registry.npmjs.org/qs/-/qs-6.13.0.tgz#6ca3bd58439f7e245655798997787b0d88a51906" - integrity sha512-+38qI9SOr8tfZ4QmJNplMUxqjbe7LKvvZgWdExBOmd+egZTtjLB67Gu0HRX3u/XOq7UU2Nx6nsjvS16Z9uwfpg== - dependencies: - side-channel "^1.0.6" - -range-parser@~1.2.1: - version "1.2.1" - resolved "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz#3cf37023d199e1c24d1a55b84800c2f3e6468031" - integrity sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg== - -raw-body@2.5.2: - version "2.5.2" - resolved "https://registry.npmjs.org/raw-body/-/raw-body-2.5.2.tgz#99febd83b90e08975087e8f1f9419a149366b68a" - integrity sha512-8zGqypfENjCIqGhgXToC8aB2r7YrBX+AQAfIPs/Mlk+BtPTztOvTS01NRW/3Eh60J+a48lt8qsCzirQ6loCVfA== - dependencies: - bytes "3.1.2" - http-errors "2.0.0" - iconv-lite "0.4.24" - unpipe "1.0.0" - -react-dom@^19.1.0: - version "19.1.0" - resolved "https://registry.npmjs.org/react-dom/-/react-dom-19.1.0.tgz#133558deca37fa1d682708df8904b25186793623" - integrity sha512-Xs1hdnE+DyKgeHJeJznQmYMIBG3TKIHJJT95Q58nHLSrElKlGQqDTR2HQ9fx5CN/Gk6Vh/kupBTDLU11/nDk/g== - dependencies: - scheduler "^0.26.0" - -react-refresh@^0.14.0: - version "0.14.2" - resolved "https://registry.npmjs.org/react-refresh/-/react-refresh-0.14.2.tgz#3833da01ce32da470f1f936b9d477da5c7028bf9" - integrity sha512-jCvmsr+1IUSMUyzOkRcvnVbX3ZYC6g9TDrDbFuFmRDq7PD4yaGbLKNQL6k2jnArV8hjYxh7hVhAZB6s9HDGpZA== - -react-router@^7.5.3: - version "7.6.0" - resolved "https://registry.npmjs.org/react-router/-/react-router-7.6.0.tgz#e2d0872d7bea8df79465a8bba9a20c87c32ce995" - integrity sha512-GGufuHIVCJDbnIAXP3P9Sxzq3UUsddG3rrI3ut1q6m0FI6vxVBF3JoPQ38+W/blslLH4a5Yutp8drkEpXoddGQ== - dependencies: - cookie "^1.0.1" - set-cookie-parser "^2.6.0" - -react@^19.1.0: - version "19.1.0" - resolved "https://registry.npmjs.org/react/-/react-19.1.0.tgz#926864b6c48da7627f004795d6cce50e90793b75" - integrity sha512-FS+XFBNvn3GTAWq26joslQgWNoFu08F4kl0J4CgdNKADkdSGXQyTCnKteIAJy96Br6YbpEU1LSzV5dYtjMkMDg== - -readdirp@^4.0.1: - version "4.1.2" - resolved "https://registry.npmjs.org/readdirp/-/readdirp-4.1.2.tgz#eb85801435fbf2a7ee58f19e0921b068fc69948d" - integrity sha512-GDhwkLfywWL2s6vEjyhri+eXmfH6j1L7JE27WhqLeYzoh/A3DBaYGEj2H/HFZCn/kMfim73FXxEJTw06WtxQwg== - -retry@^0.12.0: - version "0.12.0" - resolved "https://registry.npmjs.org/retry/-/retry-0.12.0.tgz#1b42a6266a21f07421d1b0b54b7dc167b01c013b" - integrity sha512-9LkiTwjUh6rT555DtE9rTX+BKByPfrMzEAtnlEtdEwr3Nkffwiihqe2bWADg+OQRjt9gl6ICdmB/ZFDCGAtSow== - -rollup@^4.34.9: - version "4.41.0" - resolved "https://registry.npmjs.org/rollup/-/rollup-4.41.0.tgz#17476835d2967759e3ffebe5823ed15fc4b7d13e" - integrity sha512-HqMFpUbWlf/tvcxBFNKnJyzc7Lk+XO3FGc3pbNBLqEbOz0gPLRgcrlS3UF4MfUrVlstOaP/q0kM6GVvi+LrLRg== - dependencies: - "@types/estree" "1.0.7" - optionalDependencies: - "@rollup/rollup-android-arm-eabi" "4.41.0" - "@rollup/rollup-android-arm64" "4.41.0" - "@rollup/rollup-darwin-arm64" "4.41.0" - "@rollup/rollup-darwin-x64" "4.41.0" - "@rollup/rollup-freebsd-arm64" "4.41.0" - "@rollup/rollup-freebsd-x64" "4.41.0" - "@rollup/rollup-linux-arm-gnueabihf" "4.41.0" - "@rollup/rollup-linux-arm-musleabihf" "4.41.0" - "@rollup/rollup-linux-arm64-gnu" "4.41.0" - "@rollup/rollup-linux-arm64-musl" "4.41.0" - "@rollup/rollup-linux-loongarch64-gnu" "4.41.0" - "@rollup/rollup-linux-powerpc64le-gnu" "4.41.0" - "@rollup/rollup-linux-riscv64-gnu" "4.41.0" - "@rollup/rollup-linux-riscv64-musl" "4.41.0" - "@rollup/rollup-linux-s390x-gnu" "4.41.0" - "@rollup/rollup-linux-x64-gnu" "4.41.0" - "@rollup/rollup-linux-x64-musl" "4.41.0" - "@rollup/rollup-win32-arm64-msvc" "4.41.0" - "@rollup/rollup-win32-ia32-msvc" "4.41.0" - "@rollup/rollup-win32-x64-msvc" "4.41.0" - fsevents "~2.3.2" - -safe-buffer@5.1.2: - version "5.1.2" - resolved "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz#991ec69d296e0313747d59bdfd2b745c35f8828d" - integrity sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g== - -safe-buffer@5.2.1: - version "5.2.1" - resolved "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz#1eaf9fa9bdb1fdd4ec75f58f9cdb4e6b7827eec6" - integrity sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ== - -"safer-buffer@>= 2.1.2 < 3": - version "2.1.2" - resolved "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz#44fa161b0187b9549dd84bb91802f9bd8385cd6a" - integrity sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg== - -scheduler@^0.26.0: - version "0.26.0" - resolved "https://registry.npmjs.org/scheduler/-/scheduler-0.26.0.tgz#4ce8a8c2a2095f13ea11bf9a445be50c555d6337" - integrity sha512-NlHwttCI/l5gCPR3D1nNXtWABUmBwvZpEQiD4IXSbIDq8BzLIK/7Ir5gTFSGZDUu37K5cMNp0hFtzO38sC7gWA== - -semver@^6.3.1: - version "6.3.1" - resolved "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz#556d2ef8689146e46dcea4bfdd095f3434dffcb4" - integrity sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA== - -semver@^7.1.1, semver@^7.3.5, semver@^7.3.7, semver@^7.5.3: - version "7.7.2" - resolved "https://registry.npmjs.org/semver/-/semver-7.7.2.tgz#67d99fdcd35cec21e6f8b87a7fd515a33f982b58" - integrity sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA== - -send@0.19.0: - version "0.19.0" - resolved "https://registry.npmjs.org/send/-/send-0.19.0.tgz#bbc5a388c8ea6c048967049dbeac0e4a3f09d7f8" - integrity sha512-dW41u5VfLXu8SJh5bwRmyYUbAoSB3c9uQh6L8h/KtsFREPWpbX1lrljJo186Jc4nmci/sGUZ9a0a0J2zgfq2hw== - dependencies: - debug "2.6.9" - depd "2.0.0" - destroy "1.2.0" - encodeurl "~1.0.2" - escape-html "~1.0.3" - etag "~1.8.1" - fresh "0.5.2" - http-errors "2.0.0" - mime "1.6.0" - ms "2.1.3" - on-finished "2.4.1" - range-parser "~1.2.1" - statuses "2.0.1" - -serve-static@1.16.2: - version "1.16.2" - resolved "https://registry.npmjs.org/serve-static/-/serve-static-1.16.2.tgz#b6a5343da47f6bdd2673848bf45754941e803296" - integrity sha512-VqpjJZKadQB/PEbEwvFdO43Ax5dFBZ2UECszz8bQ7pi7wt//PWe1P6MN7eCnjsatYtBT6EuiClbjSWP2WrIoTw== - dependencies: - encodeurl "~2.0.0" - escape-html "~1.0.3" - parseurl "~1.3.3" - send "0.19.0" - -set-cookie-parser@^2.6.0: - version "2.7.1" - resolved "https://registry.npmjs.org/set-cookie-parser/-/set-cookie-parser-2.7.1.tgz#3016f150072202dfbe90fadee053573cc89d2943" - integrity sha512-IOc8uWeOZgnb3ptbCURJWNjWUPcO3ZnTTdzsurqERrP6nPyv+paC55vJM0LpOlT2ne+Ix+9+CRG1MNLlyZ4GjQ== - -setprototypeof@1.2.0: - version "1.2.0" - resolved "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz#66c9a24a73f9fc28cbe66b09fed3d33dcaf1b424" - integrity sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw== - -shebang-command@^2.0.0: - version "2.0.0" - resolved "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz#ccd0af4f8835fbdc265b82461aaf0c36663f34ea" - integrity sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA== - dependencies: - shebang-regex "^3.0.0" - -shebang-regex@^3.0.0: - version "3.0.0" - resolved "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz#ae16f1644d873ecad843b0307b143362d4c42172" - integrity sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A== - -side-channel-list@^1.0.0: - version "1.0.0" - resolved "https://registry.npmjs.org/side-channel-list/-/side-channel-list-1.0.0.tgz#10cb5984263115d3b7a0e336591e290a830af8ad" - integrity sha512-FCLHtRD/gnpCiCHEiJLOwdmFP+wzCmDEkc9y7NsYxeF4u7Btsn1ZuwgwJGxImImHicJArLP4R0yX4c2KCrMrTA== - dependencies: - es-errors "^1.3.0" - object-inspect "^1.13.3" - -side-channel-map@^1.0.1: - version "1.0.1" - resolved "https://registry.npmjs.org/side-channel-map/-/side-channel-map-1.0.1.tgz#d6bb6b37902c6fef5174e5f533fab4c732a26f42" - integrity sha512-VCjCNfgMsby3tTdo02nbjtM/ewra6jPHmpThenkTYh8pG9ucZ/1P8So4u4FGBek/BjpOVsDCMoLA/iuBKIFXRA== - dependencies: - call-bound "^1.0.2" - es-errors "^1.3.0" - get-intrinsic "^1.2.5" - object-inspect "^1.13.3" - -side-channel-weakmap@^1.0.2: - version "1.0.2" - resolved "https://registry.npmjs.org/side-channel-weakmap/-/side-channel-weakmap-1.0.2.tgz#11dda19d5368e40ce9ec2bdc1fb0ecbc0790ecea" - integrity sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A== - dependencies: - call-bound "^1.0.2" - es-errors "^1.3.0" - get-intrinsic "^1.2.5" - object-inspect "^1.13.3" - side-channel-map "^1.0.1" - -side-channel@^1.0.6: - version "1.1.0" - resolved "https://registry.npmjs.org/side-channel/-/side-channel-1.1.0.tgz#c3fcff9c4da932784873335ec9765fa94ff66bc9" - integrity sha512-ZX99e6tRweoUXqR+VBrslhda51Nh5MTQwou5tnUDgbtyM0dBgmhEDtWGP/xbKn6hqfPRHujUNwz5fy/wbbhnpw== - dependencies: - es-errors "^1.3.0" - object-inspect "^1.13.3" - side-channel-list "^1.0.0" - side-channel-map "^1.0.1" - side-channel-weakmap "^1.0.2" - -signal-exit@^4.0.1: - version "4.1.0" - resolved "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz#952188c1cbd546070e2dd20d0f41c0ae0530cb04" - integrity sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw== - -source-map-js@^1.2.1: - version "1.2.1" - resolved "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz#1ce5650fddd87abc099eda37dcff024c2667ae46" - integrity sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA== - -source-map-support@^0.5.21: - version "0.5.21" - resolved "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.21.tgz#04fe7c7f9e1ed2d662233c28cb2b35b9f63f6e4f" - integrity sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w== - dependencies: - buffer-from "^1.0.0" - source-map "^0.6.0" - -source-map@^0.6.0: - version "0.6.1" - resolved "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz#74722af32e9614e9c287a8d0bbde48b5e2f1a263" - integrity sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g== - -spdx-correct@^3.0.0: - version "3.2.0" - resolved "https://registry.npmjs.org/spdx-correct/-/spdx-correct-3.2.0.tgz#4f5ab0668f0059e34f9c00dce331784a12de4e9c" - integrity sha512-kN9dJbvnySHULIluDHy32WHRUu3Og7B9sbY7tsFLctQkIqnMh3hErYgdMjTYuqmcXX+lK5T1lnUt3G7zNswmZA== - dependencies: - spdx-expression-parse "^3.0.0" - spdx-license-ids "^3.0.0" - -spdx-exceptions@^2.1.0: - version "2.5.0" - resolved "https://registry.npmjs.org/spdx-exceptions/-/spdx-exceptions-2.5.0.tgz#5d607d27fc806f66d7b64a766650fa890f04ed66" - integrity sha512-PiU42r+xO4UbUS1buo3LPJkjlO7430Xn5SVAhdpzzsPHsjbYVflnnFdATgabnLude+Cqu25p6N+g2lw/PFsa4w== - -spdx-expression-parse@^3.0.0: - version "3.0.1" - resolved "https://registry.npmjs.org/spdx-expression-parse/-/spdx-expression-parse-3.0.1.tgz#cf70f50482eefdc98e3ce0a6833e4a53ceeba679" - integrity sha512-cbqHunsQWnJNE6KhVSMsMeH5H/L9EpymbzqTQ3uLwNCLZ1Q481oWaofqH7nO6V07xlXwY6PhQdQ2IedWx/ZK4Q== - dependencies: - spdx-exceptions "^2.1.0" - spdx-license-ids "^3.0.0" - -spdx-license-ids@^3.0.0: - version "3.0.21" - resolved "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.21.tgz#6d6e980c9df2b6fc905343a3b2d702a6239536c3" - integrity sha512-Bvg/8F5XephndSK3JffaRqdT+gyhfqIPwDHpX80tJrF8QQRYMo8sNMeaZ2Dp5+jhwKnUmIOyFFQfHRkjJm5nXg== - -statuses@2.0.1: - version "2.0.1" - resolved "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz#55cb000ccf1d48728bd23c685a063998cf1a1b63" - integrity sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ== - -stream-slice@^0.1.2: - version "0.1.2" - resolved "https://registry.npmjs.org/stream-slice/-/stream-slice-0.1.2.tgz#2dc4f4e1b936fb13f3eb39a2def1932798d07a4b" - integrity sha512-QzQxpoacatkreL6jsxnVb7X5R/pGw9OUv2qWTYWnmLpg4NdN31snPy/f3TdQE1ZUXaThRvj1Zw4/OGg0ZkaLMA== - -"string-width-cjs@npm:string-width@^4.2.0", string-width@^4.1.0: - version "4.2.3" - resolved "https://registry.npmjs.org/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@^5.0.1, string-width@^5.1.2: - version "5.1.2" - resolved "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz#14f8daec6d81e7221d2a357e668cab73bdbca794" - integrity sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA== - dependencies: - eastasianwidth "^0.2.0" - emoji-regex "^9.2.2" - strip-ansi "^7.0.1" - -"strip-ansi-cjs@npm:strip-ansi@^6.0.1", strip-ansi@^6.0.0, strip-ansi@^6.0.1: - version "6.0.1" - resolved "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9" - integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A== - dependencies: - ansi-regex "^5.0.1" - -strip-ansi@^7.0.1: - version "7.1.0" - resolved "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz#d5b6568ca689d8561370b0707685d22434faff45" - integrity sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ== - dependencies: - ansi-regex "^6.0.1" - -tailwindcss@4.1.7, tailwindcss@^4.1.4: - version "4.1.7" - resolved "https://registry.npmjs.org/tailwindcss/-/tailwindcss-4.1.7.tgz#949f76d50667946ddd7291e0c7a4b5a7dfc9e765" - integrity sha512-kr1o/ErIdNhTz8uzAYL7TpaUuzKIE6QPQ4qmSdxnoX/lo+5wmUHQA6h3L5yIqEImSRnAAURDirLu/BgiXGPAhg== - -tapable@^2.2.0: - version "2.2.2" - resolved "https://registry.npmjs.org/tapable/-/tapable-2.2.2.tgz#ab4984340d30cb9989a490032f086dbb8b56d872" - integrity sha512-Re10+NauLTMCudc7T5WLFLAwDhQ0JWdrMK+9B2M8zR5hRExKmsRDCBA7/aV/pNJFltmBFO5BAMlQFi/vq3nKOg== - -tar@^7.4.3: - version "7.4.3" - resolved "https://registry.npmjs.org/tar/-/tar-7.4.3.tgz#88bbe9286a3fcd900e94592cda7a22b192e80571" - integrity sha512-5S7Va8hKfV7W5U6g3aYxXmlPoZVAwUMy9AOKyF2fVuZa2UD3qZjg578OrLRt8PcNN1PleVaL/5/yYATNL0ICUw== - dependencies: - "@isaacs/fs-minipass" "^4.0.0" - chownr "^3.0.0" - minipass "^7.1.2" - minizlib "^3.0.1" - mkdirp "^3.0.1" - yallist "^5.0.0" - -tinyglobby@^0.2.13: - version "0.2.13" - resolved "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.13.tgz#a0e46515ce6cbcd65331537e57484af5a7b2ff7e" - integrity sha512-mEwzpUgrLySlveBwEVDMKk5B57bhLPYovRfPAXD5gA/98Opn0rCDj3GtLwFvCvH5RK9uPCExUROW5NjDwvqkxw== - dependencies: - fdir "^6.4.4" - picomatch "^4.0.2" - -toidentifier@1.0.1: - version "1.0.1" - resolved "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz#3be34321a88a820ed1bd80dfaa33e479fbb8dd35" - integrity sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA== - -tsconfck@^3.0.3: - version "3.1.6" - resolved "https://registry.npmjs.org/tsconfck/-/tsconfck-3.1.6.tgz#da1f0b10d82237ac23422374b3fce1edb23c3ead" - integrity sha512-ks6Vjr/jEw0P1gmOVwutM3B7fWxoWBL2KRDb1JfqGVawBmO5UsvmWOQFGHBPl5yxYz4eERr19E6L7NMv+Fej4w== - -tslib@^2.4.0, tslib@^2.8.0: - version "2.8.1" - resolved "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz#612efe4ed235d567e8aba5f2a5fab70280ade83f" - integrity sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w== - -type-is@~1.6.18: - version "1.6.18" - resolved "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz#4e552cd05df09467dcbc4ef739de89f2cf37c131" - integrity sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g== - dependencies: - media-typer "0.3.0" - mime-types "~2.1.24" - -typescript@^5.8.3: - version "5.8.3" - resolved "https://registry.npmjs.org/typescript/-/typescript-5.8.3.tgz#92f8a3e5e3cf497356f4178c34cd65a7f5e8440e" - integrity sha512-p1diW6TqL9L07nNxvRMM7hMMw4c5XOo/1ibL4aAIGmSAt9slTE1Xgw5KWuof2uTOvCg9BY7ZRi+GaF+7sfgPeQ== - -undici-types@~6.19.2: - version "6.19.8" - resolved "https://registry.npmjs.org/undici-types/-/undici-types-6.19.8.tgz#35111c9d1437ab83a7cdc0abae2f26d88eda0a02" - integrity sha512-ve2KP6f/JnbPBFyobGHuerC9g1FYGn/F8n1LWTwNxCEzd6IfqTwUQcNXgEtmmQ6DlRrC1hrSrBnCZPokRrDHjw== - -undici@^6.19.2: - version "6.21.3" - resolved "https://registry.npmjs.org/undici/-/undici-6.21.3.tgz#185752ad92c3d0efe7a7d1f6854a50f83b552d7a" - integrity sha512-gBLkYIlEnSp8pFbT64yFgGE6UIB9tAkhukC23PmMDCe5Nd+cRqKxSjw5y54MK2AZMgZfJWMaNE4nYUHgi1XEOw== - -universalify@^2.0.0: - version "2.0.1" - resolved "https://registry.npmjs.org/universalify/-/universalify-2.0.1.tgz#168efc2180964e6386d061e094df61afe239b18d" - integrity sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw== - -unpipe@1.0.0, unpipe@~1.0.0: - version "1.0.0" - resolved "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz#b2bf4ee8514aae6165b4817829d21b2ef49904ec" - integrity sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ== - -update-browserslist-db@^1.1.3: - version "1.1.3" - resolved "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.1.3.tgz#348377dd245216f9e7060ff50b15a1b740b75420" - integrity sha512-UxhIZQ+QInVdunkDAaiazvvT/+fXL5Osr0JZlJulepYu6Jd7qJtDZjlur0emRlT71EN3ScPoE7gvsuIKKNavKw== - dependencies: - escalade "^3.2.0" - picocolors "^1.1.1" - -utils-merge@1.0.1: - version "1.0.1" - resolved "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz#9f95710f50a267947b2ccc124741c1028427e713" - integrity sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA== - -valibot@^0.41.0: - version "0.41.0" - resolved "https://registry.npmjs.org/valibot/-/valibot-0.41.0.tgz#5c2efd49c078e455f7862379365f6036f3cd9f96" - integrity sha512-igDBb8CTYr8YTQlOKgaN9nSS0Be7z+WRuaeYqGf3Cjz3aKmSnqEmYnkfVjzIuumGqfHpa3fLIvMEAfhrpqN8ng== - -validate-npm-package-license@^3.0.4: - version "3.0.4" - resolved "https://registry.npmjs.org/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz#fc91f6b9c7ba15c857f4cb2c5defeec39d4f410a" - integrity sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew== - dependencies: - spdx-correct "^3.0.0" - spdx-expression-parse "^3.0.0" - -validate-npm-package-name@^5.0.0: - version "5.0.1" - resolved "https://registry.npmjs.org/validate-npm-package-name/-/validate-npm-package-name-5.0.1.tgz#a316573e9b49f3ccd90dbb6eb52b3f06c6d604e8" - integrity sha512-OljLrQ9SQdOUqTaQxqL5dEfZWrXExyyWsozYlAWFawPVNuD83igl7uJD2RTkNMbniIYgt8l81eCJGIdQF7avLQ== - -vary@~1.1.2: - version "1.1.2" - resolved "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz#2299f02c6ded30d4a5961b0b9f74524a18f634fc" - integrity sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg== - -vite-node@3.0.0-beta.2: - version "3.0.0-beta.2" - resolved "https://registry.npmjs.org/vite-node/-/vite-node-3.0.0-beta.2.tgz#4208a6be384f9e7bba97570114d662ce9c957dc1" - integrity sha512-ofTf6cfRdL30Wbl9n/BX81EyIR5s4PReLmSurrxQ+koLaWUNOEo8E0lCM53OJkb8vpa2URM2nSrxZsIFyvY1rg== - dependencies: - cac "^6.7.14" - debug "^4.4.0" - es-module-lexer "^1.5.4" - pathe "^1.1.2" - vite "^5.0.0 || ^6.0.0" - -vite-tsconfig-paths@^5.1.4: - version "5.1.4" - resolved "https://registry.npmjs.org/vite-tsconfig-paths/-/vite-tsconfig-paths-5.1.4.tgz#d9a71106a7ff2c1c840c6f1708042f76a9212ed4" - integrity sha512-cYj0LRuLV2c2sMqhqhGpaO3LretdtMn/BVX4cPLanIZuwwrkVl+lK84E/miEXkCHWXuq65rhNN4rXsBcOB3S4w== - dependencies: - debug "^4.1.1" - globrex "^0.1.2" - tsconfck "^3.0.3" - -"vite@^5.0.0 || ^6.0.0", vite@^6.3.3: - version "6.3.5" - resolved "https://registry.npmjs.org/vite/-/vite-6.3.5.tgz#fec73879013c9c0128c8d284504c6d19410d12a3" - integrity sha512-cZn6NDFE7wdTpINgs++ZJ4N49W2vRp8LCKrn3Ob1kYNtOo21vfDoaV5GzBfLU4MovSAB8uNRm4jgzVQZ+mBzPQ== - dependencies: - esbuild "^0.25.0" - fdir "^6.4.4" - picomatch "^4.0.2" - postcss "^8.5.3" - rollup "^4.34.9" - tinyglobby "^0.2.13" - optionalDependencies: - fsevents "~2.3.3" - -which@^2.0.1: - version "2.0.2" - resolved "https://registry.npmjs.org/which/-/which-2.0.2.tgz#7c6a8dd0a636a0327e10b59c9286eee93f3f51b1" - integrity sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA== - dependencies: - isexe "^2.0.0" - -which@^3.0.0: - version "3.0.1" - resolved "https://registry.npmjs.org/which/-/which-3.0.1.tgz#89f1cd0c23f629a8105ffe69b8172791c87b4be1" - integrity sha512-XA1b62dzQzLfaEOSQFTCOd5KFf/1VSzZo7/7TUjnya6u0vGGKzU96UQBZTAThCb2j4/xjBAyii1OhRLJEivHvg== - dependencies: - isexe "^2.0.0" - -"wrap-ansi-cjs@npm:wrap-ansi@^7.0.0": - version "7.0.0" - resolved "https://registry.npmjs.org/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@^8.1.0: - version "8.1.0" - resolved "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-8.1.0.tgz#56dc22368ee570face1b49819975d9b9a5ead214" - integrity sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ== - dependencies: - ansi-styles "^6.1.0" - string-width "^5.0.1" - strip-ansi "^7.0.1" - -yallist@^3.0.2: - version "3.1.1" - resolved "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz#dbb7daf9bfd8bac9ab45ebf602b8cbad0d5d08fd" - integrity sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g== - -yallist@^5.0.0: - version "5.0.0" - resolved "https://registry.npmjs.org/yallist/-/yallist-5.0.0.tgz#00e2de443639ed0d78fd87de0d27469fbcffb533" - integrity sha512-YgvUTfwqyc7UXVMrB+SImsVYSmTS8X/tSrtdNZMImM+n7+QTriRXyXim0mBrTXNeqzVF0KWGgHPeiyViFFrNDw== diff --git a/dev-packages/node-integration-tests/package.json b/dev-packages/node-integration-tests/package.json index 5f5c80094fc5..013761ff78a8 100644 --- a/dev-packages/node-integration-tests/package.json +++ b/dev-packages/node-integration-tests/package.json @@ -61,6 +61,8 @@ "openai": "5.18.1", "pg": "8.16.0", "postgres": "^3.4.7", + "@prisma/client": "6.15.0", + "prisma": "6.15.0", "proxy": "^2.1.1", "redis-4": "npm:redis@^4.6.14", "reflect-metadata": "0.2.1", diff --git a/dev-packages/node-integration-tests/suites/tracing/knex/mysql2/docker-compose.yml b/dev-packages/node-integration-tests/suites/tracing/knex/mysql2/docker-compose.yml new file mode 100644 index 000000000000..788311e4e117 --- /dev/null +++ b/dev-packages/node-integration-tests/suites/tracing/knex/mysql2/docker-compose.yml @@ -0,0 +1,12 @@ +version: '3.9' + +services: + db_mysql2: + image: mysql:8 + restart: always + container_name: integration-tests-knex-mysql2 + ports: + - '3307:3306' + environment: + MYSQL_ROOT_PASSWORD: docker + MYSQL_DATABASE: tests diff --git a/dev-packages/node-integration-tests/suites/tracing/knex/instrument-withMysql2.mjs b/dev-packages/node-integration-tests/suites/tracing/knex/mysql2/instrument.mjs similarity index 100% rename from dev-packages/node-integration-tests/suites/tracing/knex/instrument-withMysql2.mjs rename to dev-packages/node-integration-tests/suites/tracing/knex/mysql2/instrument.mjs diff --git a/dev-packages/node-integration-tests/suites/tracing/knex/scenario-withMysql2.mjs b/dev-packages/node-integration-tests/suites/tracing/knex/mysql2/scenario.mjs similarity index 100% rename from dev-packages/node-integration-tests/suites/tracing/knex/scenario-withMysql2.mjs rename to dev-packages/node-integration-tests/suites/tracing/knex/mysql2/scenario.mjs diff --git a/dev-packages/node-integration-tests/suites/tracing/knex/mysql2/test.ts b/dev-packages/node-integration-tests/suites/tracing/knex/mysql2/test.ts new file mode 100644 index 000000000000..e8116293de09 --- /dev/null +++ b/dev-packages/node-integration-tests/suites/tracing/knex/mysql2/test.ts @@ -0,0 +1,73 @@ +import { describe, expect } from 'vitest'; +import { createEsmAndCjsTests } from '../../../../utils/runner'; + +describe('knex auto instrumentation', () => { + // Update this if another knex version is installed + const KNEX_VERSION = '2.5.1'; + + describe('with `mysql2` client', () => { + createEsmAndCjsTests(__dirname, 'scenario.mjs', 'instrument.mjs', (createRunner, test) => { + test('should auto-instrument `knex` package', { timeout: 60_000 }, async () => { + const EXPECTED_TRANSACTION = { + transaction: 'Test Transaction', + spans: expect.arrayContaining([ + expect.objectContaining({ + data: expect.objectContaining({ + 'knex.version': KNEX_VERSION, + 'db.system': 'mysql2', + 'db.name': 'tests', + 'db.user': 'root', + 'sentry.origin': 'auto.db.otel.knex', + 'sentry.op': 'db', + 'net.peer.name': 'localhost', + 'net.peer.port': 3307, + }), + status: 'ok', + description: + 'create table `User` (`id` int unsigned not null auto_increment primary key, `createdAt` timestamp(3) not null default CURRENT_TIMESTAMP(3), `email` text not null, `name` text not null)', + origin: 'auto.db.otel.knex', + }), + expect.objectContaining({ + data: expect.objectContaining({ + 'knex.version': KNEX_VERSION, + 'db.system': 'mysql2', + 'db.name': 'tests', + 'db.user': 'root', + 'sentry.origin': 'auto.db.otel.knex', + 'sentry.op': 'db', + 'net.peer.name': 'localhost', + 'net.peer.port': 3307, + }), + status: 'ok', + description: 'insert into `User` (`email`, `name`) values (?, ?)', + origin: 'auto.db.otel.knex', + }), + + expect.objectContaining({ + data: expect.objectContaining({ + 'knex.version': KNEX_VERSION, + 'db.operation': 'select', + 'db.sql.table': 'User', + 'db.system': 'mysql2', + 'db.name': 'tests', + 'db.statement': 'select * from `User`', + 'db.user': 'root', + 'sentry.origin': 'auto.db.otel.knex', + 'sentry.op': 'db', + }), + status: 'ok', + description: 'select * from `User`', + origin: 'auto.db.otel.knex', + }), + ]), + }; + + await createRunner() + .withDockerCompose({ workingDirectory: [__dirname], readyMatches: ['port: 3306'] }) + .expect({ transaction: EXPECTED_TRANSACTION }) + .start() + .completed(); + }); + }); + }); +}); diff --git a/dev-packages/node-integration-tests/suites/tracing/knex/docker-compose.yml b/dev-packages/node-integration-tests/suites/tracing/knex/pg/docker-compose.yml similarity index 55% rename from dev-packages/node-integration-tests/suites/tracing/knex/docker-compose.yml rename to dev-packages/node-integration-tests/suites/tracing/knex/pg/docker-compose.yml index 50eb7bc94237..e3edcd1d8d7b 100644 --- a/dev-packages/node-integration-tests/suites/tracing/knex/docker-compose.yml +++ b/dev-packages/node-integration-tests/suites/tracing/knex/pg/docker-compose.yml @@ -11,13 +11,3 @@ services: POSTGRES_USER: test POSTGRES_PASSWORD: test POSTGRES_DB: tests - - db_mysql2: - image: mysql:8 - restart: always - container_name: integration-tests-knex-mysql2 - ports: - - '3307:3306' - environment: - MYSQL_ROOT_PASSWORD: docker - MYSQL_DATABASE: tests diff --git a/dev-packages/node-integration-tests/suites/tracing/knex/instrument-withPg.mjs b/dev-packages/node-integration-tests/suites/tracing/knex/pg/instrument.mjs similarity index 100% rename from dev-packages/node-integration-tests/suites/tracing/knex/instrument-withPg.mjs rename to dev-packages/node-integration-tests/suites/tracing/knex/pg/instrument.mjs diff --git a/dev-packages/node-integration-tests/suites/tracing/knex/scenario-withPg.mjs b/dev-packages/node-integration-tests/suites/tracing/knex/pg/scenario.mjs similarity index 100% rename from dev-packages/node-integration-tests/suites/tracing/knex/scenario-withPg.mjs rename to dev-packages/node-integration-tests/suites/tracing/knex/pg/scenario.mjs diff --git a/dev-packages/node-integration-tests/suites/tracing/knex/pg/test.ts b/dev-packages/node-integration-tests/suites/tracing/knex/pg/test.ts new file mode 100644 index 000000000000..7c38381eb125 --- /dev/null +++ b/dev-packages/node-integration-tests/suites/tracing/knex/pg/test.ts @@ -0,0 +1,71 @@ +import { describe, expect } from 'vitest'; +import { createEsmAndCjsTests } from '../../../../utils/runner'; + +describe('knex auto instrumentation', () => { + // Update this if another knex version is installed + const KNEX_VERSION = '2.5.1'; + + describe('with `pg` client', () => { + createEsmAndCjsTests(__dirname, 'scenario.mjs', 'instrument.mjs', (createRunner, test) => { + test('should auto-instrument `knex` package', { timeout: 60_000 }, async () => { + const EXPECTED_TRANSACTION = { + transaction: 'Test Transaction', + spans: expect.arrayContaining([ + expect.objectContaining({ + data: expect.objectContaining({ + 'knex.version': KNEX_VERSION, + 'db.system': 'postgresql', + 'db.name': 'tests', + 'sentry.origin': 'auto.db.otel.knex', + 'sentry.op': 'db', + 'net.peer.name': 'localhost', + 'net.peer.port': 5445, + }), + status: 'ok', + description: + 'create table "User" ("id" serial primary key, "createdAt" timestamptz(3) not null default CURRENT_TIMESTAMP(3), "email" text not null, "name" text not null)', + origin: 'auto.db.otel.knex', + }), + expect.objectContaining({ + data: expect.objectContaining({ + 'knex.version': KNEX_VERSION, + 'db.system': 'postgresql', + 'db.name': 'tests', + 'sentry.origin': 'auto.db.otel.knex', + 'sentry.op': 'db', + 'net.peer.name': 'localhost', + 'net.peer.port': 5445, + }), + status: 'ok', + // In the knex-otel spans, the placeholders (e.g., `$1`) are replaced by a `?`. + description: 'insert into "User" ("email", "name") values (?, ?)', + origin: 'auto.db.otel.knex', + }), + + expect.objectContaining({ + data: expect.objectContaining({ + 'knex.version': KNEX_VERSION, + 'db.operation': 'select', + 'db.sql.table': 'User', + 'db.system': 'postgresql', + 'db.name': 'tests', + 'db.statement': 'select * from "User"', + 'sentry.origin': 'auto.db.otel.knex', + 'sentry.op': 'db', + }), + status: 'ok', + description: 'select * from "User"', + origin: 'auto.db.otel.knex', + }), + ]), + }; + + await createRunner() + .withDockerCompose({ workingDirectory: [__dirname], readyMatches: ['port 5432'] }) + .expect({ transaction: EXPECTED_TRANSACTION }) + .start() + .completed(); + }); + }); + }); +}); diff --git a/dev-packages/node-integration-tests/suites/tracing/knex/test.ts b/dev-packages/node-integration-tests/suites/tracing/knex/test.ts deleted file mode 100644 index f60ea76a1c54..000000000000 --- a/dev-packages/node-integration-tests/suites/tracing/knex/test.ts +++ /dev/null @@ -1,137 +0,0 @@ -import { describe, expect } from 'vitest'; -import { createEsmAndCjsTests } from '../../../utils/runner'; - -describe('knex auto instrumentation', () => { - // Update this if another knex version is installed - const KNEX_VERSION = '2.5.1'; - - describe('with `pg` client', () => { - createEsmAndCjsTests(__dirname, 'scenario-withPg.mjs', 'instrument-withPg.mjs', (createRunner, test) => { - test('should auto-instrument `knex` package', { timeout: 60_000 }, async () => { - const EXPECTED_TRANSACTION = { - transaction: 'Test Transaction', - spans: expect.arrayContaining([ - expect.objectContaining({ - data: expect.objectContaining({ - 'knex.version': KNEX_VERSION, - 'db.system': 'postgresql', - 'db.name': 'tests', - 'sentry.origin': 'auto.db.otel.knex', - 'sentry.op': 'db', - 'net.peer.name': 'localhost', - 'net.peer.port': 5445, - }), - status: 'ok', - description: - 'create table "User" ("id" serial primary key, "createdAt" timestamptz(3) not null default CURRENT_TIMESTAMP(3), "email" text not null, "name" text not null)', - origin: 'auto.db.otel.knex', - }), - expect.objectContaining({ - data: expect.objectContaining({ - 'knex.version': KNEX_VERSION, - 'db.system': 'postgresql', - 'db.name': 'tests', - 'sentry.origin': 'auto.db.otel.knex', - 'sentry.op': 'db', - 'net.peer.name': 'localhost', - 'net.peer.port': 5445, - }), - status: 'ok', - // In the knex-otel spans, the placeholders (e.g., `$1`) are replaced by a `?`. - description: 'insert into "User" ("email", "name") values (?, ?)', - origin: 'auto.db.otel.knex', - }), - - expect.objectContaining({ - data: expect.objectContaining({ - 'knex.version': KNEX_VERSION, - 'db.operation': 'select', - 'db.sql.table': 'User', - 'db.system': 'postgresql', - 'db.name': 'tests', - 'db.statement': 'select * from "User"', - 'sentry.origin': 'auto.db.otel.knex', - 'sentry.op': 'db', - }), - status: 'ok', - description: 'select * from "User"', - origin: 'auto.db.otel.knex', - }), - ]), - }; - - await createRunner() - .withDockerCompose({ workingDirectory: [__dirname], readyMatches: ['port 5432'] }) - .expect({ transaction: EXPECTED_TRANSACTION }) - .start() - .completed(); - }); - }); - }); - - describe('with `mysql2` client', () => { - createEsmAndCjsTests(__dirname, 'scenario-withMysql2.mjs', 'instrument-withMysql2.mjs', (createRunner, test) => { - test('should auto-instrument `knex` package', { timeout: 60_000 }, async () => { - const EXPECTED_TRANSACTION = { - transaction: 'Test Transaction', - spans: expect.arrayContaining([ - expect.objectContaining({ - data: expect.objectContaining({ - 'knex.version': KNEX_VERSION, - 'db.system': 'mysql2', - 'db.name': 'tests', - 'db.user': 'root', - 'sentry.origin': 'auto.db.otel.knex', - 'sentry.op': 'db', - 'net.peer.name': 'localhost', - 'net.peer.port': 3307, - }), - status: 'ok', - description: - 'create table `User` (`id` int unsigned not null auto_increment primary key, `createdAt` timestamp(3) not null default CURRENT_TIMESTAMP(3), `email` text not null, `name` text not null)', - origin: 'auto.db.otel.knex', - }), - expect.objectContaining({ - data: expect.objectContaining({ - 'knex.version': KNEX_VERSION, - 'db.system': 'mysql2', - 'db.name': 'tests', - 'db.user': 'root', - 'sentry.origin': 'auto.db.otel.knex', - 'sentry.op': 'db', - 'net.peer.name': 'localhost', - 'net.peer.port': 3307, - }), - status: 'ok', - description: 'insert into `User` (`email`, `name`) values (?, ?)', - origin: 'auto.db.otel.knex', - }), - - expect.objectContaining({ - data: expect.objectContaining({ - 'knex.version': KNEX_VERSION, - 'db.operation': 'select', - 'db.sql.table': 'User', - 'db.system': 'mysql2', - 'db.name': 'tests', - 'db.statement': 'select * from `User`', - 'db.user': 'root', - 'sentry.origin': 'auto.db.otel.knex', - 'sentry.op': 'db', - }), - status: 'ok', - description: 'select * from `User`', - origin: 'auto.db.otel.knex', - }), - ]), - }; - - await createRunner() - .withDockerCompose({ workingDirectory: [__dirname], readyMatches: ['port: 3306'] }) - .expect({ transaction: EXPECTED_TRANSACTION }) - .start() - .completed(); - }); - }); - }); -}); diff --git a/dev-packages/node-integration-tests/suites/tracing/prisma-orm-v5/instrument.mjs b/dev-packages/node-integration-tests/suites/tracing/prisma-orm-v5/instrument.mjs new file mode 100644 index 000000000000..46a27dd03b74 --- /dev/null +++ b/dev-packages/node-integration-tests/suites/tracing/prisma-orm-v5/instrument.mjs @@ -0,0 +1,9 @@ +import * as Sentry from '@sentry/node'; +import { loggingTransport } from '@sentry-internal/node-integration-tests'; + +Sentry.init({ + dsn: 'https://public@dsn.ingest.sentry.io/1337', + release: '1.0', + tracesSampleRate: 1.0, + transport: loggingTransport, +}); diff --git a/dev-packages/node-integration-tests/suites/tracing/prisma-orm-v5/package.json b/dev-packages/node-integration-tests/suites/tracing/prisma-orm-v5/package.json deleted file mode 100644 index d3dcaf9d1328..000000000000 --- a/dev-packages/node-integration-tests/suites/tracing/prisma-orm-v5/package.json +++ /dev/null @@ -1,21 +0,0 @@ -{ - "name": "sentry-prisma-test", - "version": "1.0.0", - "description": "", - "main": "index.js", - "engines": { - "node": ">=18" - }, - "scripts": { - "generate": "prisma generate", - "migrate": "prisma migrate dev -n sentry-test", - "setup": "run-s --silent generate migrate" - }, - "keywords": [], - "author": "", - "license": "ISC", - "dependencies": { - "@prisma/client": "5.22.0", - "prisma": "5.22.0" - } -} diff --git a/dev-packages/node-integration-tests/suites/tracing/prisma-orm-v5/scenario.js b/dev-packages/node-integration-tests/suites/tracing/prisma-orm-v5/scenario.mjs similarity index 64% rename from dev-packages/node-integration-tests/suites/tracing/prisma-orm-v5/scenario.js rename to dev-packages/node-integration-tests/suites/tracing/prisma-orm-v5/scenario.mjs index 767a6f27bdaa..631ca4a1cfb4 100644 --- a/dev-packages/node-integration-tests/suites/tracing/prisma-orm-v5/scenario.js +++ b/dev-packages/node-integration-tests/suites/tracing/prisma-orm-v5/scenario.mjs @@ -1,16 +1,6 @@ -const Sentry = require('@sentry/node'); -const { loggingTransport } = require('@sentry-internal/node-integration-tests'); - -Sentry.init({ - dsn: 'https://public@dsn.ingest.sentry.io/1337', - release: '1.0', - tracesSampleRate: 1.0, - transport: loggingTransport, - integrations: [Sentry.prismaIntegration()], -}); - -const { randomBytes } = require('crypto'); -const { PrismaClient } = require('@prisma/client'); +import { PrismaClient } from '@prisma/client'; +import * as Sentry from '@sentry/node'; +import { randomBytes } from 'crypto'; // Stop the process from exiting before the transaction is sent setInterval(() => {}, 1000); diff --git a/dev-packages/node-integration-tests/suites/tracing/prisma-orm-v5/test.ts b/dev-packages/node-integration-tests/suites/tracing/prisma-orm-v5/test.ts index b7788d58e372..74bdf4be4bd2 100644 --- a/dev-packages/node-integration-tests/suites/tracing/prisma-orm-v5/test.ts +++ b/dev-packages/node-integration-tests/suites/tracing/prisma-orm-v5/test.ts @@ -1,80 +1,136 @@ -import { afterAll, describe, expect, test } from 'vitest'; -import { cleanupChildProcesses, createRunner } from '../../../utils/runner'; +import { afterAll, describe, expect } from 'vitest'; +import { cleanupChildProcesses, createEsmAndCjsTests } from '../../../utils/runner'; afterAll(() => { cleanupChildProcesses(); }); describe('Prisma ORM v5 Tests', () => { - test('CJS - should instrument PostgreSQL queries from Prisma ORM', { timeout: 75_000 }, async () => { - await createRunner(__dirname, 'scenario.js') - .withDockerCompose({ - workingDirectory: [__dirname], - readyMatches: ['port 5432'], - setupCommand: 'yarn && yarn setup', - }) - .expect({ - transaction: transaction => { - expect(transaction.transaction).toBe('Test Transaction'); - const spans = transaction.spans || []; - expect(spans.length).toBeGreaterThanOrEqual(5); + createEsmAndCjsTests( + __dirname, + 'scenario.mjs', + 'instrument.mjs', + (createRunner, test, _mode, cwd) => { + test('should instrument PostgreSQL queries from Prisma ORM', { timeout: 75_000 }, async () => { + await createRunner() + .withDockerCompose({ + workingDirectory: [cwd], + readyMatches: ['port 5432'], + setupCommand: 'yarn prisma generate && yarn prisma migrate dev -n sentry-test', + }) + .expect({ + transaction: transaction => { + expect(transaction.transaction).toBe('Test Transaction'); + const spans = transaction.spans || []; + expect(spans.length).toBeGreaterThanOrEqual(5); - expect(spans).toContainEqual( - expect.objectContaining({ - data: { - method: 'create', - model: 'User', - name: 'User.create', - 'sentry.origin': 'auto.db.otel.prisma', - }, - description: 'prisma:client:operation', - status: 'ok', - }), - ); + expect(spans).toContainEqual( + expect.objectContaining({ + data: { + method: 'create', + model: 'User', + name: 'User.create', + 'sentry.origin': 'auto.db.otel.prisma', + }, + description: 'prisma:client:operation', + status: 'ok', + }), + ); - expect(spans).toContainEqual( - expect.objectContaining({ - data: { - 'sentry.origin': 'auto.db.otel.prisma', - }, - description: 'prisma:client:serialize', - status: 'ok', - }), - ); + expect(spans).toContainEqual( + expect.objectContaining({ + data: { + 'sentry.origin': 'auto.db.otel.prisma', + }, + description: 'prisma:client:serialize', + status: 'ok', + }), + ); - expect(spans).toContainEqual( - expect.objectContaining({ - data: { - 'sentry.origin': 'auto.db.otel.prisma', - }, - description: 'prisma:client:connect', - status: 'ok', - }), - ); - expect(spans).toContainEqual( - expect.objectContaining({ - data: { - method: 'findMany', - model: 'User', - name: 'User.findMany', - 'sentry.origin': 'auto.db.otel.prisma', - }, - description: 'prisma:client:operation', - status: 'ok', - }), - ); - expect(spans).toContainEqual( - expect.objectContaining({ - data: { - 'sentry.origin': 'auto.db.otel.prisma', - }, - description: 'prisma:client:serialize', - status: 'ok', - }), - ); - }, - }) - .start() - .completed(); - }); + expect(spans).toContainEqual( + expect.objectContaining({ + data: { + 'sentry.origin': 'auto.db.otel.prisma', + }, + description: 'prisma:client:connect', + status: 'ok', + }), + ); + expect(spans).toContainEqual( + expect.objectContaining({ + data: { + method: 'findMany', + model: 'User', + name: 'User.findMany', + 'sentry.origin': 'auto.db.otel.prisma', + }, + description: 'prisma:client:operation', + status: 'ok', + }), + ); + expect(spans).toContainEqual( + expect.objectContaining({ + data: { + 'sentry.origin': 'auto.db.otel.prisma', + }, + description: 'prisma:client:serialize', + status: 'ok', + }), + ); + expect(spans).toContainEqual( + expect.objectContaining({ + data: { + 'db.statement': expect.stringContaining('INSERT INTO'), + 'db.system': 'postgresql', + 'otel.kind': 'CLIENT', + 'sentry.op': 'db', + 'sentry.origin': 'auto.db.otel.prisma', + }, + op: 'db', + description: expect.stringContaining('INSERT INTO'), + status: 'ok', + }), + ); + expect(spans).toContainEqual( + expect.objectContaining({ + data: { + 'db.statement': expect.stringContaining('SELECT'), + 'db.system': 'postgresql', + 'otel.kind': 'CLIENT', + 'sentry.op': 'db', + 'sentry.origin': 'auto.db.otel.prisma', + }, + op: 'db', + description: expect.stringContaining('SELECT'), + status: 'ok', + }), + ); + expect(spans).toContainEqual( + expect.objectContaining({ + data: { + 'db.statement': expect.stringContaining('DELETE'), + 'db.system': 'postgresql', + 'otel.kind': 'CLIENT', + 'sentry.op': 'db', + 'sentry.origin': 'auto.db.otel.prisma', + }, + op: 'db', + description: expect.stringContaining('DELETE'), + status: 'ok', + }), + ); + }, + }) + .start() + .completed(); + }); + }, + { + additionalDependencies: { + '@prisma/client': '5.22.0', + prisma: '5.22.0', + }, + copyPaths: ['prisma', 'docker-compose.yml'], + }, + ); }); diff --git a/dev-packages/node-integration-tests/suites/tracing/prisma-orm-v5/yarn.lock b/dev-packages/node-integration-tests/suites/tracing/prisma-orm-v5/yarn.lock deleted file mode 100644 index 860aa032d6cc..000000000000 --- a/dev-packages/node-integration-tests/suites/tracing/prisma-orm-v5/yarn.lock +++ /dev/null @@ -1,58 +0,0 @@ -# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. -# yarn lockfile v1 - - -"@prisma/client@5.22.0": - version "5.22.0" - resolved "https://registry.yarnpkg.com/@prisma/client/-/client-5.22.0.tgz#da1ca9c133fbefe89e0da781c75e1c59da5f8802" - integrity sha512-M0SVXfyHnQREBKxCgyo7sffrKttwE6R8PMq330MIUF0pTwjUhLbW84pFDlf06B27XyCR++VtjugEnIHdr07SVA== - -"@prisma/debug@5.22.0": - version "5.22.0" - resolved "https://registry.yarnpkg.com/@prisma/debug/-/debug-5.22.0.tgz#58af56ed7f6f313df9fb1042b6224d3174bbf412" - integrity sha512-AUt44v3YJeggO2ZU5BkXI7M4hu9BF2zzH2iF2V5pyXT/lRTyWiElZ7It+bRH1EshoMRxHgpYg4VB6rCM+mG5jQ== - -"@prisma/engines-version@5.22.0-44.605197351a3c8bdd595af2d2a9bc3025bca48ea2": - version "5.22.0-44.605197351a3c8bdd595af2d2a9bc3025bca48ea2" - resolved "https://registry.yarnpkg.com/@prisma/engines-version/-/engines-version-5.22.0-44.605197351a3c8bdd595af2d2a9bc3025bca48ea2.tgz#d534dd7235c1ba5a23bacd5b92cc0ca3894c28f4" - integrity sha512-2PTmxFR2yHW/eB3uqWtcgRcgAbG1rwG9ZriSvQw+nnb7c4uCr3RAcGMb6/zfE88SKlC1Nj2ziUvc96Z379mHgQ== - -"@prisma/engines@5.22.0": - version "5.22.0" - resolved "https://registry.yarnpkg.com/@prisma/engines/-/engines-5.22.0.tgz#28f3f52a2812c990a8b66eb93a0987816a5b6d84" - integrity sha512-UNjfslWhAt06kVL3CjkuYpHAWSO6L4kDCVPegV6itt7nD1kSJavd3vhgAEhjglLJJKEdJ7oIqDJ+yHk6qO8gPA== - dependencies: - "@prisma/debug" "5.22.0" - "@prisma/engines-version" "5.22.0-44.605197351a3c8bdd595af2d2a9bc3025bca48ea2" - "@prisma/fetch-engine" "5.22.0" - "@prisma/get-platform" "5.22.0" - -"@prisma/fetch-engine@5.22.0": - version "5.22.0" - resolved "https://registry.yarnpkg.com/@prisma/fetch-engine/-/fetch-engine-5.22.0.tgz#4fb691b483a450c5548aac2f837b267dd50ef52e" - integrity sha512-bkrD/Mc2fSvkQBV5EpoFcZ87AvOgDxbG99488a5cexp5Ccny+UM6MAe/UFkUC0wLYD9+9befNOqGiIJhhq+HbA== - dependencies: - "@prisma/debug" "5.22.0" - "@prisma/engines-version" "5.22.0-44.605197351a3c8bdd595af2d2a9bc3025bca48ea2" - "@prisma/get-platform" "5.22.0" - -"@prisma/get-platform@5.22.0": - version "5.22.0" - resolved "https://registry.yarnpkg.com/@prisma/get-platform/-/get-platform-5.22.0.tgz#fc675bc9d12614ca2dade0506c9c4a77e7dddacd" - integrity sha512-pHhpQdr1UPFpt+zFfnPazhulaZYCUqeIcPpJViYoq9R+D/yw4fjE+CtnsnKzPYm0ddUbeXUzjGVGIRVgPDCk4Q== - dependencies: - "@prisma/debug" "5.22.0" - -fsevents@2.3.3: - version "2.3.3" - resolved "https://registry.yarnpkg.com/fsevents/-/fsevents-2.3.3.tgz#cac6407785d03675a2a5e1a5305c697b347d90d6" - integrity sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw== - -prisma@5.22.0: - version "5.22.0" - resolved "https://registry.yarnpkg.com/prisma/-/prisma-5.22.0.tgz#1f6717ff487cdef5f5799cc1010459920e2e6197" - integrity sha512-vtpjW3XuYCSnMsNVBjLMNkTj6OZbudcPPTPYHqX0CJfpcdWciI1dM8uHETwmDxxiqEwCIE6WvXucWUetJgfu/A== - dependencies: - "@prisma/engines" "5.22.0" - optionalDependencies: - fsevents "2.3.3" diff --git a/dev-packages/node-integration-tests/suites/tracing/prisma-orm-v6/instrument.mjs b/dev-packages/node-integration-tests/suites/tracing/prisma-orm-v6/instrument.mjs new file mode 100644 index 000000000000..46a27dd03b74 --- /dev/null +++ b/dev-packages/node-integration-tests/suites/tracing/prisma-orm-v6/instrument.mjs @@ -0,0 +1,9 @@ +import * as Sentry from '@sentry/node'; +import { loggingTransport } from '@sentry-internal/node-integration-tests'; + +Sentry.init({ + dsn: 'https://public@dsn.ingest.sentry.io/1337', + release: '1.0', + tracesSampleRate: 1.0, + transport: loggingTransport, +}); diff --git a/dev-packages/node-integration-tests/suites/tracing/prisma-orm-v6/package.json b/dev-packages/node-integration-tests/suites/tracing/prisma-orm-v6/package.json deleted file mode 100644 index dc062f1b9e3b..000000000000 --- a/dev-packages/node-integration-tests/suites/tracing/prisma-orm-v6/package.json +++ /dev/null @@ -1,21 +0,0 @@ -{ - "name": "sentry-prisma-test", - "version": "1.0.0", - "description": "", - "main": "index.js", - "engines": { - "node": ">=18" - }, - "scripts": { - "generate": "prisma generate", - "migrate": "prisma migrate dev -n sentry-test", - "setup": "run-s --silent generate migrate" - }, - "keywords": [], - "author": "", - "license": "ISC", - "dependencies": { - "@prisma/client": "6.2.1", - "prisma": "6.2.1" - } -} diff --git a/dev-packages/node-integration-tests/suites/tracing/prisma-orm-v6/scenario.js b/dev-packages/node-integration-tests/suites/tracing/prisma-orm-v6/scenario.mjs similarity index 64% rename from dev-packages/node-integration-tests/suites/tracing/prisma-orm-v6/scenario.js rename to dev-packages/node-integration-tests/suites/tracing/prisma-orm-v6/scenario.mjs index b6e3805fa595..89469a30a0c0 100644 --- a/dev-packages/node-integration-tests/suites/tracing/prisma-orm-v6/scenario.js +++ b/dev-packages/node-integration-tests/suites/tracing/prisma-orm-v6/scenario.mjs @@ -1,16 +1,6 @@ -const Sentry = require('@sentry/node'); -const { loggingTransport } = require('@sentry-internal/node-integration-tests'); - -Sentry.init({ - dsn: 'https://public@dsn.ingest.sentry.io/1337', - release: '1.0', - tracesSampleRate: 1.0, - transport: loggingTransport, - integrations: [Sentry.prismaIntegration()], -}); - -const { randomBytes } = require('crypto'); -const { PrismaClient } = require('@prisma/client'); +import { PrismaClient } from '@prisma/client'; +import * as Sentry from '@sentry/node'; +import { randomBytes } from 'crypto'; // Stop the process from exiting before the transaction is sent setInterval(() => {}, 1000); diff --git a/dev-packages/node-integration-tests/suites/tracing/prisma-orm-v6/test.ts b/dev-packages/node-integration-tests/suites/tracing/prisma-orm-v6/test.ts index 5e00a00966b7..07405a496fd0 100644 --- a/dev-packages/node-integration-tests/suites/tracing/prisma-orm-v6/test.ts +++ b/dev-packages/node-integration-tests/suites/tracing/prisma-orm-v6/test.ts @@ -1,96 +1,106 @@ import type { SpanJSON } from '@sentry/core'; -import { afterAll, describe, expect, test } from 'vitest'; -import { cleanupChildProcesses, createRunner } from '../../../utils/runner'; +import { afterAll, describe, expect } from 'vitest'; +import { cleanupChildProcesses, createEsmAndCjsTests } from '../../../utils/runner'; afterAll(() => { cleanupChildProcesses(); }); describe('Prisma ORM v6 Tests', () => { - test('CJS - should instrument PostgreSQL queries from Prisma ORM', { timeout: 75_000 }, async () => { - await createRunner(__dirname, 'scenario.js') - .withDockerCompose({ - workingDirectory: [__dirname], - readyMatches: ['port 5432'], - setupCommand: 'yarn && yarn setup', - }) - .expect({ - transaction: transaction => { - expect(transaction.transaction).toBe('Test Transaction'); + createEsmAndCjsTests( + __dirname, + 'scenario.mjs', + 'instrument.mjs', + (createRunner, test, _mode, cwd) => { + test('should instrument PostgreSQL queries from Prisma ORM', { timeout: 75_000 }, async () => { + await createRunner() + .withDockerCompose({ + workingDirectory: [cwd], + readyMatches: ['port 5432'], + setupCommand: `yarn prisma generate --schema ${cwd}/prisma/schema.prisma && yarn prisma migrate dev -n sentry-test --schema ${cwd}/prisma/schema.prisma`, + }) + .expect({ + transaction: transaction => { + expect(transaction.transaction).toBe('Test Transaction'); - const spans = transaction.spans || []; - expect(spans.length).toBeGreaterThanOrEqual(5); + const spans = transaction.spans || []; + expect(spans.length).toBeGreaterThanOrEqual(5); - function expectPrismaSpanToIncludeSpanWith(span: Partial) { - expect(spans).toContainEqual( - expect.objectContaining({ - ...span, - data: { - ...span.data, - 'sentry.origin': 'auto.db.otel.prisma', - }, - status: 'ok', - }), - ); - } + function expectPrismaSpanToIncludeSpanWith(span: Partial) { + expect(spans).toContainEqual( + expect.objectContaining({ + ...span, + data: { + ...span.data, + 'sentry.origin': 'auto.db.otel.prisma', + }, + status: 'ok', + }), + ); + } - expectPrismaSpanToIncludeSpanWith({ - description: 'prisma:client:detect_platform', - }); + expectPrismaSpanToIncludeSpanWith({ + description: 'prisma:client:detect_platform', + }); - expectPrismaSpanToIncludeSpanWith({ - description: 'prisma:client:load_engine', - }); + expectPrismaSpanToIncludeSpanWith({ + description: 'prisma:client:load_engine', + }); - expectPrismaSpanToIncludeSpanWith({ - description: 'prisma:client:operation', - data: { - method: 'create', - model: 'User', - name: 'User.create', - }, - }); + expectPrismaSpanToIncludeSpanWith({ + description: 'prisma:client:operation', + data: { + method: 'create', + model: 'User', + name: 'User.create', + }, + }); - expectPrismaSpanToIncludeSpanWith({ - description: 'prisma:client:serialize', - }); + expectPrismaSpanToIncludeSpanWith({ + description: 'prisma:client:serialize', + }); - expectPrismaSpanToIncludeSpanWith({ - description: 'prisma:client:connect', - }); + expectPrismaSpanToIncludeSpanWith({ + description: 'prisma:client:connect', + }); - expectPrismaSpanToIncludeSpanWith({ - description: 'prisma:engine:connect', - }); + expectPrismaSpanToIncludeSpanWith({ + description: 'prisma:engine:connect', + }); - expectPrismaSpanToIncludeSpanWith({ - description: 'prisma:engine:query', - }); + expectPrismaSpanToIncludeSpanWith({ + description: 'prisma:engine:query', + }); - expectPrismaSpanToIncludeSpanWith({ - data: { - 'sentry.op': 'db', - 'db.query.text': - 'SELECT "public"."User"."id", "public"."User"."createdAt", "public"."User"."email", "public"."User"."name" FROM "public"."User" WHERE 1=1 OFFSET $1', - 'db.system': 'postgresql', - 'otel.kind': 'CLIENT', - }, - description: - 'SELECT "public"."User"."id", "public"."User"."createdAt", "public"."User"."email", "public"."User"."name" FROM "public"."User" WHERE 1=1 OFFSET $1', - }); + expectPrismaSpanToIncludeSpanWith({ + data: { + 'sentry.op': 'db', + 'db.query.text': + 'SELECT "public"."User"."id", "public"."User"."createdAt", "public"."User"."email", "public"."User"."name" FROM "public"."User" WHERE 1=1 OFFSET $1', + 'db.system': 'postgresql', + 'otel.kind': 'CLIENT', + }, + description: + 'SELECT "public"."User"."id", "public"."User"."createdAt", "public"."User"."email", "public"."User"."name" FROM "public"."User" WHERE 1=1 OFFSET $1', + }); - expectPrismaSpanToIncludeSpanWith({ - data: { - 'sentry.op': 'db', - 'db.query.text': 'DELETE FROM "public"."User" WHERE "public"."User"."email"::text LIKE $1', - 'db.system': 'postgresql', - 'otel.kind': 'CLIENT', + expectPrismaSpanToIncludeSpanWith({ + data: { + 'sentry.op': 'db', + 'db.query.text': 'DELETE FROM "public"."User" WHERE "public"."User"."email"::text LIKE $1', + 'db.system': 'postgresql', + 'otel.kind': 'CLIENT', + }, + description: 'DELETE FROM "public"."User" WHERE "public"."User"."email"::text LIKE $1', + }); }, - description: 'DELETE FROM "public"."User" WHERE "public"."User"."email"::text LIKE $1', - }); - }, - }) - .start() - .completed(); - }); + }) + .start() + .completed(); + }); + }, + { + copyPaths: ['prisma', 'docker-compose.yml'], + }, + ); }); diff --git a/dev-packages/node-integration-tests/suites/tracing/prisma-orm-v6/yarn.lock b/dev-packages/node-integration-tests/suites/tracing/prisma-orm-v6/yarn.lock deleted file mode 100644 index fbdff7a9505d..000000000000 --- a/dev-packages/node-integration-tests/suites/tracing/prisma-orm-v6/yarn.lock +++ /dev/null @@ -1,58 +0,0 @@ -# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. -# yarn lockfile v1 - - -"@prisma/client@6.2.1": - version "6.2.1" - resolved "https://registry.yarnpkg.com/@prisma/client/-/client-6.2.1.tgz#3d7d0c8669bba490247e1ffff67b93a516bd789f" - integrity sha512-msKY2iRLISN8t5X0Tj7hU0UWet1u0KuxSPHWuf3IRkB4J95mCvGpyQBfQ6ufcmvKNOMQSq90O2iUmJEN2e5fiA== - -"@prisma/debug@6.2.1": - version "6.2.1" - resolved "https://registry.yarnpkg.com/@prisma/debug/-/debug-6.2.1.tgz#887719967c4942d125262e48f6c47c45d17c1f61" - integrity sha512-0KItvt39CmQxWkEw6oW+RQMD6RZ43SJWgEUnzxN8VC9ixMysa7MzZCZf22LCK5DSooiLNf8vM3LHZm/I/Ni7bQ== - -"@prisma/engines-version@6.2.0-14.4123509d24aa4dede1e864b46351bf2790323b69": - version "6.2.0-14.4123509d24aa4dede1e864b46351bf2790323b69" - resolved "https://registry.yarnpkg.com/@prisma/engines-version/-/engines-version-6.2.0-14.4123509d24aa4dede1e864b46351bf2790323b69.tgz#b84ce3fab44bfa13a22669da02752330b61745b2" - integrity sha512-7tw1qs/9GWSX6qbZs4He09TOTg1ff3gYsB3ubaVNN0Pp1zLm9NC5C5MZShtkz7TyQjx7blhpknB7HwEhlG+PrQ== - -"@prisma/engines@6.2.1": - version "6.2.1" - resolved "https://registry.yarnpkg.com/@prisma/engines/-/engines-6.2.1.tgz#14ef56bb780f02871a728667161d997a14aedb69" - integrity sha512-lTBNLJBCxVT9iP5I7Mn6GlwqAxTpS5qMERrhebkUhtXpGVkBNd/jHnNJBZQW4kGDCKaQg/r2vlJYkzOHnAb7ZQ== - dependencies: - "@prisma/debug" "6.2.1" - "@prisma/engines-version" "6.2.0-14.4123509d24aa4dede1e864b46351bf2790323b69" - "@prisma/fetch-engine" "6.2.1" - "@prisma/get-platform" "6.2.1" - -"@prisma/fetch-engine@6.2.1": - version "6.2.1" - resolved "https://registry.yarnpkg.com/@prisma/fetch-engine/-/fetch-engine-6.2.1.tgz#cd7eb7428a407105e0f3761dba536aefd41fc7f7" - integrity sha512-OO7O9d6Mrx2F9i+Gu1LW+DGXXyUFkP7OE5aj9iBfA/2jjDXEJjqa9X0ZmM9NZNo8Uo7ql6zKm6yjDcbAcRrw1A== - dependencies: - "@prisma/debug" "6.2.1" - "@prisma/engines-version" "6.2.0-14.4123509d24aa4dede1e864b46351bf2790323b69" - "@prisma/get-platform" "6.2.1" - -"@prisma/get-platform@6.2.1": - version "6.2.1" - resolved "https://registry.yarnpkg.com/@prisma/get-platform/-/get-platform-6.2.1.tgz#34313cd0ee3587798ad33a7b57b6342dc8e66426" - integrity sha512-zp53yvroPl5m5/gXYLz7tGCNG33bhG+JYCm74ohxOq1pPnrL47VQYFfF3RbTZ7TzGWCrR3EtoiYMywUBw7UK6Q== - dependencies: - "@prisma/debug" "6.2.1" - -fsevents@2.3.3: - version "2.3.3" - resolved "https://registry.yarnpkg.com/fsevents/-/fsevents-2.3.3.tgz#cac6407785d03675a2a5e1a5305c697b347d90d6" - integrity sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw== - -prisma@6.2.1: - version "6.2.1" - resolved "https://registry.yarnpkg.com/prisma/-/prisma-6.2.1.tgz#457b210326d66d0e6f583cc6f9cd2819b984408f" - integrity sha512-hhyM0H13pQleQ+br4CkzGizS5I0oInoeTw3JfLw1BRZduBSQxPILlJLwi+46wZzj9Je7ndyQEMGw/n5cN2fknA== - dependencies: - "@prisma/engines" "6.2.1" - optionalDependencies: - fsevents "2.3.3" diff --git a/dev-packages/node-integration-tests/utils/runner.ts b/dev-packages/node-integration-tests/utils/runner.ts index 46c031c2c786..02f0c112c412 100644 --- a/dev-packages/node-integration-tests/utils/runner.ts +++ b/dev-packages/node-integration-tests/utils/runner.ts @@ -14,7 +14,7 @@ import type { import { normalize } from '@sentry/core'; import { createBasicSentryServer } from '@sentry-internal/test-utils'; import { execSync, spawn, spawnSync } from 'child_process'; -import { existsSync, mkdirSync, readFileSync, rmSync, writeFileSync } from 'fs'; +import { cpSync, existsSync, mkdirSync, readFileSync, rmSync, writeFileSync } from 'fs'; import { basename, join } from 'path'; import { inspect } from 'util'; import { afterAll, beforeAll, describe, test } from 'vitest'; @@ -104,7 +104,11 @@ async function runDockerCompose(options: DockerOptions): Promise { child.stdout.removeAllListeners(); clearTimeout(timeout); if (options.setupCommand) { - execSync(options.setupCommand, { cwd, stdio: 'inherit' }); + try { + execSync(options.setupCommand, { cwd, stdio: 'inherit' }); + } catch (e) { + log('Error running docker setup command', e); + } } resolve(close); } @@ -174,11 +178,20 @@ export function createEsmAndCjsTests( createTestRunner: () => ReturnType, testFn: typeof test | typeof test.fails, mode: 'esm' | 'cjs', + cwd: string, ) => void, - // `additionalDependencies` to install in a tmp dir for the esm and cjs tests - // This could be used to override packages that live in the parent package.json for the specific run of the test - // e.g. `{ ai: '^5.0.0' }` to test Vercel AI v5 - options?: { failsOnCjs?: boolean; failsOnEsm?: boolean; additionalDependencies?: Record }, + options?: { + failsOnCjs?: boolean; + failsOnEsm?: boolean; + /** + * `additionalDependencies` to install in a tmp dir for the esm and cjs tests + * This could be used to override packages that live in the parent package.json for the specific run of the test + * e.g. `{ ai: '^5.0.0' }` to test Vercel AI v5 + */ + additionalDependencies?: Record; + /** Copy these files/dirs into the tmp dir. */ + copyPaths?: string[]; + }, ): void { const mjsScenarioPath = join(cwd, scenarioPath); const mjsInstrumentPath = join(cwd, instrumentPath); @@ -213,6 +226,13 @@ export function createEsmAndCjsTests( convertEsmFileToCjs(esmScenarioPathForRun, cjsScenarioPath); convertEsmFileToCjs(esmInstrumentPathForRun, cjsInstrumentPath); + // Copy any additional files/dirs into tmp dir + if (options?.copyPaths) { + for (const path of options.copyPaths) { + cpSync(join(cwd, path), join(tmpDirPath, path), { recursive: true }); + } + } + // Create a minimal package.json with requested dependencies (if any) and install them const additionalDependencies = options?.additionalDependencies ?? {}; if (Object.keys(additionalDependencies).length > 0) { @@ -278,12 +298,18 @@ export function createEsmAndCjsTests( () => createRunner(esmScenarioPathForRun).withFlags('--import', esmInstrumentPathForRun), esmTestFn, 'esm', + tmpDirPath, ); }); const cjsTestFn = options?.failsOnCjs ? test.fails : test; describe('cjs', () => { - callback(() => createRunner(cjsScenarioPath).withFlags('--require', cjsInstrumentPath), cjsTestFn, 'cjs'); + callback( + () => createRunner(cjsScenarioPath).withFlags('--require', cjsInstrumentPath), + cjsTestFn, + 'cjs', + tmpDirPath, + ); }); // Create tmp directory @@ -293,6 +319,9 @@ export function createEsmAndCjsTests( // Clean up the tmp directory after both esm and cjs suites have run afterAll(() => { + // First do cleanup! + cleanupChildProcesses(); + try { rmSync(tmpDirPath, { recursive: true, force: true }); } catch { diff --git a/packages/node/src/integrations/tracing/prisma.ts b/packages/node/src/integrations/tracing/prisma.ts index bc845362b6bb..503e8c1908a4 100644 --- a/packages/node/src/integrations/tracing/prisma.ts +++ b/packages/node/src/integrations/tracing/prisma.ts @@ -1,4 +1,7 @@ +import type { Link, Tracer } from '@opentelemetry/api'; +import { context, SpanKind, trace, TraceFlags } from '@opentelemetry/api'; import type { Instrumentation } from '@opentelemetry/instrumentation'; +import type { IdGenerator } from '@opentelemetry/sdk-trace-base'; import { PrismaInstrumentation } from '@prisma/instrumentation'; import { consoleSandbox, defineIntegration, SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN, spanToJSON } from '@sentry/core'; import { generateInstrumentOnce } from '@sentry/node-core'; @@ -9,6 +12,27 @@ const INTEGRATION_NAME = 'Prisma'; type CompatibilityLayerTraceHelper = PrismaV5TracingHelper & PrismaV6TracingHelper; +// Vendored in from @prisma/instrumentation v5: +type V5EngineSpanEvent = { + span: boolean; + spans: V5EngineSpan[]; +}; + +type V5EngineSpanKind = 'client' | 'internal'; + +type V5EngineSpan = { + span: boolean; + name: string; + trace_id: string; + span_id: string; + parent_span_id: string; + start_time: [number, number]; + end_time: [number, number]; + attributes?: Record; + links?: { trace_id: string; span_id: string }[]; + kind: V5EngineSpanKind; +}; + function isPrismaV6TracingHelper(helper: unknown): helper is PrismaV6TracingHelper { return !!helper && typeof helper === 'object' && 'dispatchEngineSpans' in helper; } @@ -25,6 +49,17 @@ function getPrismaTracingHelper(): unknown | undefined { return prismaTracingHelper; } +type TracerWithIdGenerator = Tracer & { + _idGenerator?: IdGenerator; +}; + +interface PrismaOptions { + /** + * @deprecated This is no longer used, v5 works out of the box. + */ + prismaInstrumentation?: Instrumentation; +} + class SentryPrismaInteropInstrumentation extends PrismaInstrumentation { public constructor() { super(); @@ -35,38 +70,104 @@ class SentryPrismaInteropInstrumentation extends PrismaInstrumentation { // The PrismaIntegration (super class) defines a global variable `global["PRISMA_INSTRUMENTATION"]` when `enable()` is called. This global variable holds a "TracingHelper" which Prisma uses internally to create tracing data. It's their way of not depending on OTEL with their main package. The sucky thing is, prisma broke the interface of the tracing helper with the v6 major update. This means that if you use Prisma 5 with the v6 instrumentation (or vice versa) Prisma just blows up, because tries to call methods on the helper that no longer exist. // Because we actually want to use the v6 instrumentation and not blow up in Prisma 5 user's faces, what we're doing here is backfilling the v5 method (`createEngineSpan`) with a noop so that no longer crashes when it attempts to call that function. - // We still won't fully emit all the spans, but this could potentially be implemented in the future. const prismaTracingHelper = getPrismaTracingHelper(); - let emittedWarning = false; - if (isPrismaV6TracingHelper(prismaTracingHelper)) { - (prismaTracingHelper as CompatibilityLayerTraceHelper).createEngineSpan = () => { - consoleSandbox(() => { - if (!emittedWarning) { - emittedWarning = true; + // Inspired & adjusted from https://github.com/prisma/prisma/tree/5.22.0/packages/instrumentation + (prismaTracingHelper as CompatibilityLayerTraceHelper).createEngineSpan = ( + engineSpanEvent: V5EngineSpanEvent, + ) => { + const tracer = trace.getTracer('prismaV5Compatibility') as TracerWithIdGenerator; + + // Prisma v5 relies on being able to create spans with a specific span & trace ID + // this is no longer possible in OTEL v2, there is no public API to do this anymore + // So in order to kind of hack this possibility, we rely on the internal `_idGenerator` property + // This is used to generate the random IDs, and we overwrite this temporarily to generate static IDs + // This is flawed and may not work, e.g. if the code is bundled and the private property is renamed + // in such cases, these spans will not be captured and some Prisma spans will be missing + const initialIdGenerator = tracer._idGenerator; + + if (!initialIdGenerator) { + consoleSandbox(() => { // eslint-disable-next-line no-console console.warn( - '[Sentry] The Sentry SDK supports tracing with Prisma version 5 only with limited capabilities. For full tracing capabilities pass `prismaInstrumentation` for version 5 to the Sentry `prismaIntegration`. Read more: https://docs.sentry.io/platforms/javascript/guides/node/configuration/integrations/prisma/', + '[Sentry] Could not find _idGenerator on tracer, skipping Prisma v5 compatibility - some Prisma spans may be missing!', ); - } - }); + }); + + return; + } + + try { + engineSpanEvent.spans.forEach(engineSpan => { + const kind = engineSpanKindToOTELSpanKind(engineSpan.kind); + + const parentSpanId = engineSpan.parent_span_id; + const spanId = engineSpan.span_id; + const traceId = engineSpan.trace_id; + + const links: Link[] | undefined = engineSpan.links?.map(link => { + return { + context: { + traceId: link.trace_id, + spanId: link.span_id, + traceFlags: TraceFlags.SAMPLED, + }, + }; + }); + + const ctx = trace.setSpanContext(context.active(), { + traceId, + spanId: parentSpanId, + traceFlags: TraceFlags.SAMPLED, + }); + + context.with(ctx, () => { + const temporaryIdGenerator: IdGenerator = { + generateTraceId: () => { + return traceId; + }, + generateSpanId: () => { + return spanId; + }, + }; + + tracer._idGenerator = temporaryIdGenerator; + + const span = tracer.startSpan(engineSpan.name, { + kind, + links, + startTime: engineSpan.start_time, + attributes: engineSpan.attributes, + }); + + span.end(engineSpan.end_time); + + tracer._idGenerator = initialIdGenerator; + }); + }); + } finally { + // Ensure we always restore this at the end, even if something errors + tracer._idGenerator = initialIdGenerator; + } }; } } } -export const instrumentPrisma = generateInstrumentOnce<{ prismaInstrumentation?: Instrumentation }>( - INTEGRATION_NAME, - options => { - // Use a passed instrumentation instance to support older Prisma versions - if (options?.prismaInstrumentation) { - return options.prismaInstrumentation; - } +function engineSpanKindToOTELSpanKind(engineSpanKind: V5EngineSpanKind): SpanKind { + switch (engineSpanKind) { + case 'client': + return SpanKind.CLIENT; + case 'internal': + default: // Other span kinds aren't currently supported + return SpanKind.INTERNAL; + } +} - return new SentryPrismaInteropInstrumentation(); - }, -); +export const instrumentPrisma = generateInstrumentOnce(INTEGRATION_NAME, _options => { + return new SentryPrismaInteropInstrumentation(); +}); /** * Adds Sentry tracing instrumentation for the [prisma](https://www.npmjs.com/package/prisma) library. @@ -100,49 +201,36 @@ export const instrumentPrisma = generateInstrumentOnce<{ prismaInstrumentation?: * } * ``` */ -export const prismaIntegration = defineIntegration( - ({ - prismaInstrumentation, - }: { - /** - * Overrides the instrumentation used by the Sentry SDK with the passed in instrumentation instance. - * - * NOTE: By default, the Sentry SDK uses the Prisma v6 instrumentation. Use this option if you need performance instrumentation different Prisma versions. - * - * For more information refer to the documentation of `prismaIntegration()` or see https://docs.sentry.io/platforms/javascript/guides/node/configuration/integrations/prisma/ - */ - prismaInstrumentation?: Instrumentation; - } = {}) => { - return { - name: INTEGRATION_NAME, - setupOnce() { - instrumentPrisma({ prismaInstrumentation }); - }, - setup(client) { - // If no tracing helper exists, we skip any work here - // this means that prisma is not being used - if (!getPrismaTracingHelper()) { - return; +export const prismaIntegration = defineIntegration((_options?: PrismaOptions) => { + return { + name: INTEGRATION_NAME, + setupOnce() { + instrumentPrisma(); + }, + setup(client) { + // If no tracing helper exists, we skip any work here + // this means that prisma is not being used + if (!getPrismaTracingHelper()) { + return; + } + + client.on('spanStart', span => { + const spanJSON = spanToJSON(span); + if (spanJSON.description?.startsWith('prisma:')) { + span.setAttribute(SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN, 'auto.db.otel.prisma'); + } + + // Make sure we use the query text as the span name, for ex. SELECT * FROM "User" WHERE "id" = $1 + if (spanJSON.description === 'prisma:engine:db_query' && spanJSON.data['db.query.text']) { + span.updateName(spanJSON.data['db.query.text'] as string); } - client.on('spanStart', span => { - const spanJSON = spanToJSON(span); - if (spanJSON.description?.startsWith('prisma:')) { - span.setAttribute(SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN, 'auto.db.otel.prisma'); - } - - // Make sure we use the query text as the span name, for ex. SELECT * FROM "User" WHERE "id" = $1 - if (spanJSON.description === 'prisma:engine:db_query' && spanJSON.data['db.query.text']) { - span.updateName(spanJSON.data['db.query.text'] as string); - } - - // In Prisma v5.22+, the `db.system` attribute is automatically set - // On older versions, this is missing, so we add it here - if (spanJSON.description === 'prisma:engine:db_query' && !spanJSON.data['db.system']) { - span.setAttribute('db.system', 'prisma'); - } - }); - }, - }; - }, -); + // In Prisma v5.22+, the `db.system` attribute is automatically set + // On older versions, this is missing, so we add it here + if (spanJSON.description === 'prisma:engine:db_query' && !spanJSON.data['db.system']) { + span.setAttribute('db.system', 'prisma'); + } + }); + }, + }; +}); diff --git a/yarn.lock b/yarn.lock index 82fff6f849f2..f1bedd816167 100644 --- a/yarn.lock +++ b/yarn.lock @@ -6304,6 +6304,57 @@ resolved "https://registry.yarnpkg.com/@poppinss/exception/-/exception-1.2.2.tgz#8d30d42e126c54fe84e997433e4dcac610090743" integrity sha512-m7bpKCD4QMlFCjA/nKTs23fuvoVFoA83brRKmObCUNmi/9tVu8Ve3w4YQAnJu4q3Tjf5fr685HYIC/IA2zHRSg== +"@prisma/client@6.15.0": + version "6.15.0" + resolved "https://registry.yarnpkg.com/@prisma/client/-/client-6.15.0.tgz#c4166aa8492878c842e63c515853d7a4125e6168" + integrity sha512-wR2LXUbOH4cL/WToatI/Y2c7uzni76oNFND7+23ypLllBmIS8e3ZHhO+nud9iXSXKFt1SoM3fTZvHawg63emZw== + +"@prisma/config@6.15.0": + version "6.15.0" + resolved "https://registry.yarnpkg.com/@prisma/config/-/config-6.15.0.tgz#b9a4fd87620baae3ce71015ee4ad632995838d89" + integrity sha512-KMEoec9b2u6zX0EbSEx/dRpx1oNLjqJEBZYyK0S3TTIbZ7GEGoVyGyFRk4C72+A38cuPLbfQGQvgOD+gBErKlA== + dependencies: + c12 "3.1.0" + deepmerge-ts "7.1.5" + effect "3.16.12" + empathic "2.0.0" + +"@prisma/debug@6.15.0": + version "6.15.0" + resolved "https://registry.yarnpkg.com/@prisma/debug/-/debug-6.15.0.tgz#8595f8ae91eadd20d926398449b8602c74e36bbf" + integrity sha512-y7cSeLuQmyt+A3hstAs6tsuAiVXSnw9T55ra77z0nbNkA8Lcq9rNcQg6PI00by/+WnE/aMRJ/W7sZWn2cgIy1g== + +"@prisma/engines-version@6.15.0-5.85179d7826409ee107a6ba334b5e305ae3fba9fb": + version "6.15.0-5.85179d7826409ee107a6ba334b5e305ae3fba9fb" + resolved "https://registry.yarnpkg.com/@prisma/engines-version/-/engines-version-6.15.0-5.85179d7826409ee107a6ba334b5e305ae3fba9fb.tgz#718b17890287447360107bef3675487d8cb0ae15" + integrity sha512-a/46aK5j6L3ePwilZYEgYDPrhBQ/n4gYjLxT5YncUTJJNRnTCVjPF86QdzUOLRdYjCLfhtZp9aum90W0J+trrg== + +"@prisma/engines@6.15.0": + version "6.15.0" + resolved "https://registry.yarnpkg.com/@prisma/engines/-/engines-6.15.0.tgz#77a220e40f4ed3345ef27407f8f0323e80debea8" + integrity sha512-opITiR5ddFJ1N2iqa7mkRlohCZqVSsHhRcc29QXeldMljOf4FSellLT0J5goVb64EzRTKcIDeIsJBgmilNcKxA== + dependencies: + "@prisma/debug" "6.15.0" + "@prisma/engines-version" "6.15.0-5.85179d7826409ee107a6ba334b5e305ae3fba9fb" + "@prisma/fetch-engine" "6.15.0" + "@prisma/get-platform" "6.15.0" + +"@prisma/fetch-engine@6.15.0": + version "6.15.0" + resolved "https://registry.yarnpkg.com/@prisma/fetch-engine/-/fetch-engine-6.15.0.tgz#ec7e57030789ed42a87bf5b66be4db6e612412e9" + integrity sha512-xcT5f6b+OWBq6vTUnRCc7qL+Im570CtwvgSj+0MTSGA1o9UDSKZ/WANvwtiRXdbYWECpyC3CukoG3A04VTAPHw== + dependencies: + "@prisma/debug" "6.15.0" + "@prisma/engines-version" "6.15.0-5.85179d7826409ee107a6ba334b5e305ae3fba9fb" + "@prisma/get-platform" "6.15.0" + +"@prisma/get-platform@6.15.0": + version "6.15.0" + resolved "https://registry.yarnpkg.com/@prisma/get-platform/-/get-platform-6.15.0.tgz#86a114534a534090b931801b83356b0bbe676f29" + integrity sha512-Jbb+Xbxyp05NSR1x2epabetHiXvpO8tdN2YNoWoA/ZsbYyxxu/CO/ROBauIFuMXs3Ti+W7N7SJtWsHGaWte9Rg== + dependencies: + "@prisma/debug" "6.15.0" + "@prisma/instrumentation@6.15.0": version "6.15.0" resolved "https://registry.yarnpkg.com/@prisma/instrumentation/-/instrumentation-6.15.0.tgz#40b066dc6b1ea621aa5ae0fd6d54319550b7d8c9" @@ -7651,6 +7702,11 @@ resolved "https://registry.yarnpkg.com/@speed-highlight/core/-/core-1.2.7.tgz#eeaa7c1e7198559abbb98e4acbc93d108d35f2d3" integrity sha512-0dxmVj4gxg3Jg879kvFS/msl4s9F3T9UXC1InxgOf7t5NvcPD97u/WTA5vL/IxWHMn7qSxBozqrnnE2wvl1m8g== +"@standard-schema/spec@^1.0.0": + version "1.0.0" + resolved "https://registry.yarnpkg.com/@standard-schema/spec/-/spec-1.0.0.tgz#f193b73dc316c4170f2e82a881da0f550d551b9c" + integrity sha512-m2bOd0f2RT9k8QJx1JN85cZYyH1RqFBdlwtkSlf4tBDYLCiiZnv1fIIwacK6cqwXavOydf0NPToMQgpKq+dVlA== + "@supabase/auth-js@2.69.1": version "2.69.1" resolved "https://registry.yarnpkg.com/@supabase/auth-js/-/auth-js-2.69.1.tgz#fcf310d24dfab823ffbf22191e6ceaef933360d8" @@ -12131,6 +12187,24 @@ bytes@3.1.2, bytes@^3.1.2: resolved "https://registry.yarnpkg.com/bytes/-/bytes-3.1.2.tgz#8b0beeb98605adf1b128fa4386403c009e0221a5" integrity sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg== +c12@3.1.0, c12@^3.0.4: + version "3.1.0" + resolved "https://registry.yarnpkg.com/c12/-/c12-3.1.0.tgz#9e237970e1d3b74ebae51d25945cb59664c12c89" + integrity sha512-uWoS8OU1MEIsOv8p/5a82c3H31LsWVR5qiyXVfBNOzfffjUWtPnhAb4BYI2uG2HfGmZmFjCtui5XNWaps+iFuw== + dependencies: + chokidar "^4.0.3" + confbox "^0.2.2" + defu "^6.1.4" + dotenv "^16.6.1" + exsolve "^1.0.7" + giget "^2.0.0" + jiti "^2.4.2" + ohash "^2.0.11" + pathe "^2.0.3" + perfect-debounce "^1.0.0" + pkg-types "^2.2.0" + rc9 "^2.1.2" + c12@^1.11.2: version "1.11.2" resolved "https://registry.yarnpkg.com/c12/-/c12-1.11.2.tgz#f8a1e30c10f4b273894a1bcb6944f76c15b56717" @@ -12149,24 +12223,6 @@ c12@^1.11.2: pkg-types "^1.2.0" rc9 "^2.1.2" -c12@^3.0.4: - version "3.0.4" - resolved "https://registry.yarnpkg.com/c12/-/c12-3.0.4.tgz#2d65d9ba8f6958bd88f65013f54e15140332099b" - integrity sha512-t5FaZTYbbCtvxuZq9xxIruYydrAGsJ+8UdP0pZzMiK2xl/gNiSOy0OxhLzHUEEb0m1QXYqfzfvyIFEmz/g9lqg== - dependencies: - chokidar "^4.0.3" - confbox "^0.2.2" - defu "^6.1.4" - dotenv "^16.5.0" - exsolve "^1.0.5" - giget "^2.0.0" - jiti "^2.4.2" - ohash "^2.0.11" - pathe "^2.0.3" - perfect-debounce "^1.0.0" - pkg-types "^2.1.0" - rc9 "^2.1.2" - cac@^6.7.14: version "6.7.14" resolved "https://registry.yarnpkg.com/cac/-/cac-6.7.14.tgz#804e1e6f506ee363cb0e3ccbb09cad5dd9870959" @@ -13900,6 +13956,11 @@ deep-is@^0.1.3: resolved "https://registry.yarnpkg.com/deep-is/-/deep-is-0.1.3.tgz#b369d6fb5dbc13eecf524f91b070feedc357cf34" integrity sha1-s2nW+128E+7PUk+RsHD+7cNXzzQ= +deepmerge-ts@7.1.5: + version "7.1.5" + resolved "https://registry.yarnpkg.com/deepmerge-ts/-/deepmerge-ts-7.1.5.tgz#ff818564007f5c150808d2b7b732cac83aa415ab" + integrity sha512-HOJkrhaYsweh+W+e74Yn7YStZOilkoPb6fycpwNLKzSPtruFs48nYis0zy5yJz1+ktUhHxoRDJ27RQAWLIJVJw== + deepmerge@^4.2.2, deepmerge@^4.3.1: version "4.3.1" resolved "https://registry.yarnpkg.com/deepmerge/-/deepmerge-4.3.1.tgz#44b5f2147cd3b00d4b56137685966f26fd25dd4a" @@ -14470,10 +14531,10 @@ dotenv@16.0.3: resolved "https://registry.yarnpkg.com/dotenv/-/dotenv-16.0.3.tgz#115aec42bac5053db3c456db30cc243a5a836a07" integrity sha512-7GO6HghkA5fYG9TYnNxi14/7K9f5occMlp3zXAuSxn7CKCxt9xbNWG7yF8hTCSUchlfWSe3uLmlPfigevRItzQ== -dotenv@^16.3.1, dotenv@^16.4.5, dotenv@^16.5.0: - version "16.6.0" - resolved "https://registry.yarnpkg.com/dotenv/-/dotenv-16.6.0.tgz#b96bd4e7c2043ba5f51cbe1b8f9347850c864850" - integrity sha512-Omf1L8paOy2VJhILjyhrhqwLIdstqm1BvcDPKg4NGAlkwEu9ODyrFbvk8UymUOMCT+HXo31jg1lArIrVAAhuGA== +dotenv@^16.3.1, dotenv@^16.4.5, dotenv@^16.6.1: + version "16.6.1" + resolved "https://registry.yarnpkg.com/dotenv/-/dotenv-16.6.1.tgz#773f0e69527a8315c7285d5ee73c4459d20a8020" + integrity sha512-uBq4egWHTcTt33a72vpSG0z3HnPuIl6NqYcTrKEg2azoEyl2hpW0zqlxysq2pK9HlDIHyHyakeYaYnSAwd8bow== dotenv@~10.0.0: version "10.0.0" @@ -14553,6 +14614,14 @@ ee-first@1.1.1: resolved "https://registry.yarnpkg.com/ee-first/-/ee-first-1.1.1.tgz#590c61156b0ae2f4f0255732a158b266bc56b21d" integrity sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0= +effect@3.16.12: + version "3.16.12" + resolved "https://registry.yarnpkg.com/effect/-/effect-3.16.12.tgz#3762f745846cfa4905512e397e17f683438addbe" + integrity sha512-N39iBk0K71F9nb442TLbTkjl24FLUzuvx2i1I2RsEAQsdAdUTuUoW0vlfUXgkMTUOnYqKnWcFfqw4hK4Pw27hg== + dependencies: + "@standard-schema/spec" "^1.0.0" + fast-check "^3.23.1" + ejs@^3.1.7: version "3.1.8" resolved "https://registry.yarnpkg.com/ejs/-/ejs-3.1.8.tgz#758d32910c78047585c7ef1f92f9ee041c1c190b" @@ -15127,6 +15196,11 @@ emojis-list@^3.0.0: resolved "https://registry.yarnpkg.com/emojis-list/-/emojis-list-3.0.0.tgz#5570662046ad29e2e916e71aae260abdff4f6a78" integrity sha512-/kyM18EfinwXZbno9FyUGeFh87KC8HRQBQGildHZbEuRyWFOmv1U10o9BBp8XVZDVNNuQKyIGIu5ZYAAXJ0V2Q== +empathic@2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/empathic/-/empathic-2.0.0.tgz#71d3c2b94fad49532ef98a6c34be0386659f6131" + integrity sha512-i6UzDscO/XfAcNYD75CfICkmfLedpyPDdozrLMmQc5ORaQcdMoc21OnlEylMIqI7U8eniKrPMxxtj8k0vhmJhA== + enabled@2.0.x: version "2.0.0" resolved "https://registry.yarnpkg.com/enabled/-/enabled-2.0.0.tgz#f9dd92ec2d6f4bbc0d5d1e64e21d61cd4665e7c2" @@ -16531,7 +16605,7 @@ express@5.1.0: type-is "^2.0.1" vary "^1.1.2" -exsolve@^1.0.4, exsolve@^1.0.5, exsolve@^1.0.7: +exsolve@^1.0.4, exsolve@^1.0.7: version "1.0.7" resolved "https://registry.yarnpkg.com/exsolve/-/exsolve-1.0.7.tgz#3b74e4c7ca5c5f9a19c3626ca857309fa99f9e9e" integrity sha512-VO5fQUzZtI6C+vx4w/4BWJpg3s/5l+6pRQEHzFRM8WFi4XffSP1Z+4qi7GbjWbvRQEbdIco5mIMq+zX4rPuLrw== @@ -16612,6 +16686,13 @@ fake-indexeddb@^4.0.1: dependencies: realistic-structured-clone "^3.0.0" +fast-check@^3.23.1: + version "3.23.2" + resolved "https://registry.yarnpkg.com/fast-check/-/fast-check-3.23.2.tgz#0129f1eb7e4f500f58e8290edc83c670e4a574a2" + integrity sha512-h5+1OzzfCC3Ef7VbtKdcv7zsstUQwUDlYpUTvjeUsJAssPgLn7QzbboPtL5ro04Mq0rPOsMzl7q5hIbRs2wD1A== + dependencies: + pure-rand "^6.1.0" + fast-deep-equal@^3.1.1, fast-deep-equal@^3.1.3: version "3.1.3" resolved "https://registry.yarnpkg.com/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz#3a7d56b559d6cbc3eb512325244e619a65c6c525" @@ -24746,10 +24827,10 @@ pkg-types@^1.0.3, pkg-types@^1.1.3, pkg-types@^1.2.0, pkg-types@^1.3.0: mlly "^1.7.4" pathe "^2.0.1" -pkg-types@^2.0.0, pkg-types@^2.0.1, pkg-types@^2.1.0: - version "2.1.1" - resolved "https://registry.yarnpkg.com/pkg-types/-/pkg-types-2.1.1.tgz#b8b209b2e269af62f55ba51f0648b540039dc6ca" - integrity sha512-eY0QFb6eSwc9+0d/5D2lFFUq+A3n3QNGSy/X2Nvp+6MfzGw2u6EbA7S80actgjY1lkvvI0pqB+a4hioMh443Ew== +pkg-types@^2.0.0, pkg-types@^2.0.1, pkg-types@^2.1.0, pkg-types@^2.2.0: + version "2.3.0" + resolved "https://registry.yarnpkg.com/pkg-types/-/pkg-types-2.3.0.tgz#037f2c19bd5402966ff6810e32706558cb5b5726" + integrity sha512-SIqCzDRg0s9npO5XQ3tNZioRY1uK06lA41ynBC1YmFTmnY6FjUjVt6s4LoADmwoig1qqD0oK8h1p/8mlMx8Oig== dependencies: confbox "^0.2.2" exsolve "^1.0.7" @@ -25593,6 +25674,14 @@ printf@^0.6.1: resolved "https://registry.yarnpkg.com/printf/-/printf-0.6.1.tgz#b9afa3d3b55b7f2e8b1715272479fc756ed88650" integrity sha512-is0ctgGdPJ5951KulgfzvHGwJtZ5ck8l042vRkV6jrkpBzTmb/lueTqguWHy2JfVA+RY6gFVlaZgUS0j7S/dsw== +prisma@6.15.0: + version "6.15.0" + resolved "https://registry.yarnpkg.com/prisma/-/prisma-6.15.0.tgz#198f95a4468aba8f3c95b58bf3400f65daeb8942" + integrity sha512-E6RCgOt+kUVtjtZgLQDBJ6md2tDItLJNExwI0XJeBc1FKL+Vwb+ovxXxuok9r8oBgsOXBA33fGDuE/0qDdCWqQ== + dependencies: + "@prisma/config" "6.15.0" + "@prisma/engines" "6.15.0" + prismjs@^1.29.0: version "1.29.0" resolved "https://registry.yarnpkg.com/prismjs/-/prismjs-1.29.0.tgz#f113555a8fa9b57c35e637bba27509dcf802dd12" @@ -25791,6 +25880,11 @@ pupa@^2.1.1: dependencies: escape-goat "^2.0.0" +pure-rand@^6.1.0: + version "6.1.0" + resolved "https://registry.yarnpkg.com/pure-rand/-/pure-rand-6.1.0.tgz#d173cf23258231976ccbdb05247c9787957604f2" + integrity sha512-bVWawvoZoBYpp6yIoQtQXHZjmz35RSVHnUOTefl8Vcjr8snTPY1wnpSPMWekcFwbxI6gtmT7rSYPFvz71ldiOA== + qs@6.13.0: version "6.13.0" resolved "https://registry.yarnpkg.com/qs/-/qs-6.13.0.tgz#6ca3bd58439f7e245655798997787b0d88a51906" From 71880da59c25b08e17a6cd8a7fcd08976ba2dc92 Mon Sep 17 00:00:00 2001 From: Sigrid Huemer <32902192+s1gr1d@users.noreply.github.com> Date: Thu, 11 Sep 2025 13:25:46 +0200 Subject: [PATCH 11/61] fix(cloudflare): Initialize once per workflow run and preserve scope for `step.do` (#17582) Previously, our Cloudflare Workflows instrumentation created a new Sentry client inside every `step.do`. This resets the tracing context: a custom span started with `startSpan` around multiple `step.do` would finish under a different client/scope than its children. This change initializes Sentry once per workflow run and preserves the active scope across steps. We capture the current scope in `step.do` and pass it to `startSpan`. A new test was added to check that a `step.do` span becomes a child of a surrounding custom span. fixes https://github.com/getsentry/sentry-javascript/issues/17419 ref (for being able to test this): https://github.com/cloudflare/workerd/issues/5030 Multiple `step.do` with a surrounding `startSpan` call will now result in this: image --- packages/cloudflare/src/workflows.ts | 104 +++++++++++----------- packages/cloudflare/test/workflow.test.ts | 54 +++++++++-- 2 files changed, 101 insertions(+), 57 deletions(-) diff --git a/packages/cloudflare/src/workflows.ts b/packages/cloudflare/src/workflows.ts index 7ca31d00bbd5..97726e9b9270 100644 --- a/packages/cloudflare/src/workflows.ts +++ b/packages/cloudflare/src/workflows.ts @@ -2,6 +2,7 @@ import type { PropagationContext } from '@sentry/core'; import { captureException, flush, + getCurrentScope, SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN, SEMANTIC_ATTRIBUTE_SENTRY_SOURCE, startSpan, @@ -56,29 +57,6 @@ async function propagationContextFromInstanceId(instanceId: string): Promise( - instanceId: string, - options: CloudflareOptions, - callback: () => V, -): Promise { - setAsyncLocalStorageAsyncContextStrategy(); - - return withIsolationScope(async isolationScope => { - const client = init({ ...options, enableDedupe: false }); - isolationScope.setClient(client); - - addCloudResourceContext(isolationScope); - - return withScope(async scope => { - const propagationContext = await propagationContextFromInstanceId(instanceId); - scope.setPropagationContext(propagationContext); - - // eslint-disable-next-line no-return-await - return await callback(); - }); - }); -} - class WrappedWorkflowStep implements WorkflowStep { public constructor( private _instanceId: string, @@ -98,38 +76,40 @@ class WrappedWorkflowStep implements WorkflowStep { configOrCallback: WorkflowStepConfig | (() => Promise), maybeCallback?: () => Promise, ): Promise { + // Capture the current scope, so parent span (e.g., a startSpan surrounding step.do) is preserved + const scopeForStep = getCurrentScope(); + const userCallback = (maybeCallback || configOrCallback) as () => Promise; const config = typeof configOrCallback === 'function' ? undefined : configOrCallback; const instrumentedCallback: () => Promise = async () => { - return workflowStepWithSentry(this._instanceId, this._options, async () => { - return startSpan( - { - op: 'function.step.do', - name, - attributes: { - 'cloudflare.workflow.timeout': config?.timeout, - 'cloudflare.workflow.retries.backoff': config?.retries?.backoff, - 'cloudflare.workflow.retries.delay': config?.retries?.delay, - 'cloudflare.workflow.retries.limit': config?.retries?.limit, - [SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: 'auto.faas.cloudflare.workflow', - [SEMANTIC_ATTRIBUTE_SENTRY_SOURCE]: 'task', - }, - }, - async span => { - try { - const result = await userCallback(); - span.setStatus({ code: 1 }); - return result; - } catch (error) { - captureException(error, { mechanism: { handled: true, type: 'cloudflare' } }); - throw error; - } finally { - this._ctx.waitUntil(flush(2000)); - } + return startSpan( + { + op: 'function.step.do', + name, + scope: scopeForStep, + attributes: { + 'cloudflare.workflow.timeout': config?.timeout, + 'cloudflare.workflow.retries.backoff': config?.retries?.backoff, + 'cloudflare.workflow.retries.delay': config?.retries?.delay, + 'cloudflare.workflow.retries.limit': config?.retries?.limit, + [SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: 'auto.faas.cloudflare.workflow', + [SEMANTIC_ATTRIBUTE_SENTRY_SOURCE]: 'task', }, - ); - }); + }, + async span => { + try { + const result = await userCallback(); + span.setStatus({ code: 1 }); + return result; + } catch (error) { + captureException(error, { mechanism: { handled: true, type: 'cloudflare' } }); + throw error; + } finally { + this._ctx.waitUntil(flush(2000)); + } + }, + ); }; return config ? this._step.do(name, config, instrumentedCallback) : this._step.do(name, instrumentedCallback); @@ -183,7 +163,29 @@ export function instrumentWorkflowWithSentry< get(obj, prop, receiver) { if (prop === 'run') { return async function (event: WorkflowEvent

, step: WorkflowStep): Promise { - return obj.run.call(obj, event, new WrappedWorkflowStep(event.instanceId, ctx, options, step)); + setAsyncLocalStorageAsyncContextStrategy(); + + return withIsolationScope(async isolationScope => { + const client = init({ ...options, enableDedupe: false }); + isolationScope.setClient(client); + + addCloudResourceContext(isolationScope); + + return withScope(async scope => { + const propagationContext = await propagationContextFromInstanceId(event.instanceId); + scope.setPropagationContext(propagationContext); + + try { + return await obj.run.call( + obj, + event, + new WrappedWorkflowStep(event.instanceId, ctx, options, step), + ); + } finally { + ctx.waitUntil(flush(2000)); + } + }); + }); }; } return Reflect.get(obj, prop, receiver); diff --git a/packages/cloudflare/test/workflow.test.ts b/packages/cloudflare/test/workflow.test.ts index c403023fb525..e1a6c87e5279 100644 --- a/packages/cloudflare/test/workflow.test.ts +++ b/packages/cloudflare/test/workflow.test.ts @@ -1,4 +1,5 @@ /* eslint-disable @typescript-eslint/unbound-method */ +import { startSpan } from '@sentry/core'; import type { WorkflowEvent, WorkflowStep, WorkflowStepConfig } from 'cloudflare:workers'; import { beforeEach, describe, expect, test, vi } from 'vitest'; import { deterministicTraceIdFromInstanceId, instrumentWorkflowWithSentry } from '../src/workflows'; @@ -96,7 +97,8 @@ describe.skipIf(NODE_MAJOR_VERSION < 20)('workflows', () => { expect(mockStep.do).toHaveBeenCalledTimes(1); expect(mockStep.do).toHaveBeenCalledWith('first step', expect.any(Function)); - expect(mockContext.waitUntil).toHaveBeenCalledTimes(1); + // We flush after the step.do and at the end of the run + expect(mockContext.waitUntil).toHaveBeenCalledTimes(2); expect(mockContext.waitUntil).toHaveBeenCalledWith(expect.any(Promise)); expect(mockTransport.send).toHaveBeenCalledTimes(1); expect(mockTransport.send).toHaveBeenCalledWith([ @@ -161,7 +163,8 @@ describe.skipIf(NODE_MAJOR_VERSION < 20)('workflows', () => { expect(mockStep.do).toHaveBeenCalledTimes(1); expect(mockStep.do).toHaveBeenCalledWith('first step', expect.any(Function)); - expect(mockContext.waitUntil).toHaveBeenCalledTimes(1); + // We flush after the step.do and at the end of the run + expect(mockContext.waitUntil).toHaveBeenCalledTimes(2); expect(mockContext.waitUntil).toHaveBeenCalledWith(expect.any(Promise)); expect(mockTransport.send).toHaveBeenCalledTimes(1); expect(mockTransport.send).toHaveBeenCalledWith([ @@ -232,8 +235,10 @@ describe.skipIf(NODE_MAJOR_VERSION < 20)('workflows', () => { expect(mockStep.do).toHaveBeenCalledTimes(1); expect(mockStep.do).toHaveBeenCalledWith('sometimes error step', expect.any(Function)); - expect(mockContext.waitUntil).toHaveBeenCalledTimes(2); + // One flush for the error transaction, one for the retry success, one at end of run + expect(mockContext.waitUntil).toHaveBeenCalledTimes(3); expect(mockContext.waitUntil).toHaveBeenCalledWith(expect.any(Promise)); + // error event + failed transaction + successful retry transaction expect(mockTransport.send).toHaveBeenCalledTimes(3); // First we should get the error event @@ -376,11 +381,11 @@ describe.skipIf(NODE_MAJOR_VERSION < 20)('workflows', () => { expect(mockStep.do).toHaveBeenCalledTimes(1); expect(mockStep.do).toHaveBeenCalledWith('sometimes error step', expect.any(Function)); - expect(mockContext.waitUntil).toHaveBeenCalledTimes(2); + // One flush for the error event and one at end of run + expect(mockContext.waitUntil).toHaveBeenCalledTimes(3); expect(mockContext.waitUntil).toHaveBeenCalledWith(expect.any(Promise)); - // We should get the error event and then nothing else. No transactions - // should be sent + // We should get the error event and then nothing else. No transactions should be sent expect(mockTransport.send).toHaveBeenCalledTimes(1); expect(mockTransport.send).toHaveBeenCalledWith([ @@ -421,4 +426,41 @@ describe.skipIf(NODE_MAJOR_VERSION < 20)('workflows', () => { ], ]); }); + + test('Step.do span becomes child of surrounding custom span', async () => { + class ParentChildWorkflow { + constructor(_ctx: ExecutionContext, _env: unknown) {} + + async run(_event: Readonly>, step: WorkflowStep): Promise { + await startSpan({ name: 'custom span' }, async () => { + await step.do('first step', async () => { + return { files: ['a'] }; + }); + }); + } + } + + const TestWorkflowInstrumented = instrumentWorkflowWithSentry(getSentryOptions, ParentChildWorkflow as any); + const workflow = new TestWorkflowInstrumented(mockContext, {}) as ParentChildWorkflow; + const event = { payload: {}, timestamp: new Date(), instanceId: INSTANCE_ID }; + await workflow.run(event, mockStep); + + // Flush after step.do and at end of run + expect(mockContext.waitUntil).toHaveBeenCalledTimes(2); + expect(mockTransport.send).toHaveBeenCalledTimes(1); + + const sendArg = mockTransport.send.mock.calls[0]![0] as any; + const items = sendArg[1] as any[]; + const rootSpanItem = items.find(i => i[0].type === 'transaction'); + expect(rootSpanItem).toBeDefined(); + const rootSpan = rootSpanItem[1]; + + expect(rootSpan.transaction).toBe('custom span'); + const rootSpanId = rootSpan.contexts.trace.span_id; + + // Child span for the step.do with the custom span as parent + const stepSpan = rootSpan.spans.find((s: any) => s.description === 'first step' && s.op === 'function.step.do'); + expect(stepSpan).toBeDefined(); + expect(stepSpan.parent_span_id).toBe(rootSpanId); + }); }); From 15e22cd0b1760990fba047bfc792bbf6dd23cb77 Mon Sep 17 00:00:00 2001 From: Onur Temizkan Date: Thu, 11 Sep 2025 14:17:28 +0100 Subject: [PATCH 12/61] ref(node): Inline types imported from `shimmer` (#17597) Unblocks OTEL upgrades, where `shimmer` is not a sub-dependency anymore. --- .../tracing/firebase/otel/patches/firestore.ts | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/packages/node/src/integrations/tracing/firebase/otel/patches/firestore.ts b/packages/node/src/integrations/tracing/firebase/otel/patches/firestore.ts index b450be959b69..cc28aded8e8c 100644 --- a/packages/node/src/integrations/tracing/firebase/otel/patches/firestore.ts +++ b/packages/node/src/integrations/tracing/firebase/otel/patches/firestore.ts @@ -16,7 +16,6 @@ import { ATTR_SERVER_PORT, } from '@opentelemetry/semantic-conventions'; import type { SpanAttributes } from '@sentry/core'; -import type { unwrap as shimmerUnwrap, wrap as shimmerWrap } from 'shimmer'; import type { FirebaseInstrumentation } from '../firebaseInstrumentation'; import type { AddDocType, @@ -37,6 +36,11 @@ import type { WithFieldValue, } from '../types'; +// Inline minimal types used from `shimmer` to avoid importing shimmer's types directly. +// We only need the shape for `wrap` and `unwrap` used in this file. +type ShimmerWrap = (target: any, name: string, wrapper: (...args: any[]) => any) => void; +type ShimmerUnwrap = (target: any, name: string) => void; + /** * * @param tracer - Opentelemetry Tracer @@ -47,8 +51,8 @@ import type { export function patchFirestore( tracer: Tracer, firestoreSupportedVersions: string[], - wrap: typeof shimmerWrap, - unwrap: typeof shimmerUnwrap, + wrap: ShimmerWrap, + unwrap: ShimmerUnwrap, config: FirebaseInstrumentationConfig, ): InstrumentationNodeModuleDefinition { // eslint-disable-next-line @typescript-eslint/no-empty-function @@ -102,8 +106,8 @@ export function patchFirestore( function wrapMethods( // eslint-disable-next-line @typescript-eslint/no-explicit-any moduleExports: any, - wrap: typeof shimmerWrap, - unwrap: typeof shimmerUnwrap, + wrap: ShimmerWrap, + unwrap: ShimmerUnwrap, tracer: Tracer, firestoreSpanCreationHook: FirestoreSpanCreationHook, // eslint-disable-next-line @typescript-eslint/no-explicit-any @@ -121,7 +125,7 @@ function wrapMethods( function unwrapMethods( // eslint-disable-next-line @typescript-eslint/no-explicit-any moduleExports: any, - unwrap: typeof shimmerUnwrap, + unwrap: ShimmerUnwrap, // eslint-disable-next-line @typescript-eslint/no-explicit-any ): any { for (const method of ['addDoc', 'getDocs', 'setDoc', 'deleteDoc']) { From f742746cb989f76b760876574fae72b2c6171deb Mon Sep 17 00:00:00 2001 From: Lukas Stracke Date: Thu, 11 Sep 2025 15:20:24 +0200 Subject: [PATCH 13/61] ref(node-core): Add `mechanism` to cron instrumentations (#17544) - **ref(node-core): Add `mechanism` to cron instrumentations** - **adjust tests** ref #17260 --- .../suites/cron/cron/test.ts | 10 +++++++++- .../suites/cron/node-cron/test.ts | 10 +++++++++- .../suites/cron/node-schedule/test.ts | 10 +++++++++- packages/node-core/src/cron/cron.ts | 14 ++++++++++++-- packages/node-core/src/cron/node-cron.ts | 7 ++++++- 5 files changed, 45 insertions(+), 6 deletions(-) diff --git a/dev-packages/node-core-integration-tests/suites/cron/cron/test.ts b/dev-packages/node-core-integration-tests/suites/cron/cron/test.ts index 8b9fdfd5c593..c7d0ca5239fc 100644 --- a/dev-packages/node-core-integration-tests/suites/cron/cron/test.ts +++ b/dev-packages/node-core-integration-tests/suites/cron/cron/test.ts @@ -69,7 +69,15 @@ test('cron instrumentation', async () => { }) .expect({ event: { - exception: { values: [{ type: 'Error', value: 'Error in cron job' }] }, + exception: { + values: [ + { + type: 'Error', + value: 'Error in cron job', + mechanism: { type: 'auto.function.cron.instrumentCron', handled: false }, + }, + ], + }, }, }) .start() diff --git a/dev-packages/node-core-integration-tests/suites/cron/node-cron/test.ts b/dev-packages/node-core-integration-tests/suites/cron/node-cron/test.ts index 1c5fa515e208..fb3e7bacbf45 100644 --- a/dev-packages/node-core-integration-tests/suites/cron/node-cron/test.ts +++ b/dev-packages/node-core-integration-tests/suites/cron/node-cron/test.ts @@ -69,7 +69,15 @@ test('node-cron instrumentation', async () => { }) .expect({ event: { - exception: { values: [{ type: 'Error', value: 'Error in cron job' }] }, + exception: { + values: [ + { + type: 'Error', + value: 'Error in cron job', + mechanism: { type: 'auto.function.node-cron.instrumentNodeCron', handled: false }, + }, + ], + }, }, }) .start() diff --git a/dev-packages/node-core-integration-tests/suites/cron/node-schedule/test.ts b/dev-packages/node-core-integration-tests/suites/cron/node-schedule/test.ts index a2019253203f..19d2d0b4e339 100644 --- a/dev-packages/node-core-integration-tests/suites/cron/node-schedule/test.ts +++ b/dev-packages/node-core-integration-tests/suites/cron/node-schedule/test.ts @@ -69,7 +69,15 @@ test('node-schedule instrumentation', async () => { }) .expect({ event: { - exception: { values: [{ type: 'Error', value: 'Error in cron job' }] }, + exception: { + values: [ + { + type: 'Error', + value: 'Error in cron job', + mechanism: { type: 'onunhandledrejection', handled: false }, + }, + ], + }, }, }) .start() diff --git a/packages/node-core/src/cron/cron.ts b/packages/node-core/src/cron/cron.ts index ce6225ced2fa..2c764c877c46 100644 --- a/packages/node-core/src/cron/cron.ts +++ b/packages/node-core/src/cron/cron.ts @@ -99,7 +99,12 @@ export function instrumentCron(lib: T & CronJobConstructor, monitorSlug: stri try { await onTick(context, onComplete); } catch (e) { - captureException(e); + captureException(e, { + mechanism: { + handled: false, + type: 'auto.function.cron.instrumentCron', + }, + }); throw e; } }, @@ -136,7 +141,12 @@ export function instrumentCron(lib: T & CronJobConstructor, monitorSlug: stri try { await onTick(context, onComplete); } catch (e) { - captureException(e); + captureException(e, { + mechanism: { + handled: false, + type: 'auto.function.cron.instrumentCron', + }, + }); throw e; } }, diff --git a/packages/node-core/src/cron/node-cron.ts b/packages/node-core/src/cron/node-cron.ts index 5b9e48900287..a2374b06d4b5 100644 --- a/packages/node-core/src/cron/node-cron.ts +++ b/packages/node-core/src/cron/node-cron.ts @@ -53,7 +53,12 @@ export function instrumentNodeCron(lib: Partial & T): T { try { return await callback(); } catch (e) { - captureException(e); + captureException(e, { + mechanism: { + handled: false, + type: 'auto.function.node-cron.instrumentNodeCron', + }, + }); throw e; } }, From 0527ee39ce8fe6ab65228df9d60f03f53234ba90 Mon Sep 17 00:00:00 2001 From: Rola Abuhasna Date: Thu, 11 Sep 2025 15:47:30 +0200 Subject: [PATCH 14/61] fix(node): Preserve synchronous return behavior for streamText and other methods for AI (#17580) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Vercel AI integration was incorrectly wrapping all patched methods in an async function, causing synchronous methods like streamText to return Promises when they should return synchronous objects. ``` // Before const result = streamText({model: "gpt-5"}); console.log(result); ``` ``` // After const result = streamText({model: "gpt-5"}); console.log(result); // DefaultStreamTextResult { ... } ✅ ``` Resolves: https://github.com/getsentry/sentry-javascript/issues/17553 --- .../tracing/vercelai/instrumentation.ts | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/packages/node/src/integrations/tracing/vercelai/instrumentation.ts b/packages/node/src/integrations/tracing/vercelai/instrumentation.ts index 0b66f7e80919..bf32d417c42e 100644 --- a/packages/node/src/integrations/tracing/vercelai/instrumentation.ts +++ b/packages/node/src/integrations/tracing/vercelai/instrumentation.ts @@ -9,6 +9,7 @@ import { getActiveSpan, getCurrentScope, handleCallbackErrors, + isThenable, SDK_VERSION, withScope, } from '@sentry/core'; @@ -71,7 +72,7 @@ function isToolError(obj: unknown): obj is ToolError { * Check for tool errors in the result and capture them * Tool errors are not rejected in Vercel V5, it is added as metadata to the result content */ -function checkResultForToolErrors(result: unknown | Promise): void { +function checkResultForToolErrors(result: unknown): void { if (typeof result !== 'object' || result === null || !('content' in result)) { return; } @@ -236,13 +237,18 @@ export class SentryVercelAiInstrumentation extends InstrumentationBase { }; return handleCallbackErrors( - async () => { + () => { // @ts-expect-error we know that the method exists - const result = await originalMethod.apply(this, args); + const result = originalMethod.apply(this, args); - // Tool errors are not rejected in Vercel V5, it is added as metadata to the result content - checkResultForToolErrors(result); + if (isThenable(result)) { + // check for tool errors when the promise resolves, keep the original promise identity + result.then(checkResultForToolErrors, () => {}); + return result; + } + // check for tool errors when the result is synchronous + checkResultForToolErrors(result); return result; }, error => { From 891d790741e10f7cdfe43ee14cbdd6a15a260b9f Mon Sep 17 00:00:00 2001 From: Lukas Stracke Date: Thu, 11 Sep 2025 16:28:31 +0200 Subject: [PATCH 15/61] ref(node-core): Add more specific `mechanism.type` to worker thread errors from `childProcessIntegration` (#17578) Sets a more specific mechanism type. The main problem here is that I didn't find a suiting category to be used with trace origin. So I decided to go with a similar format but the category doesn't match an existing cat. ref https://github.com/getsentry/sentry-javascript/issues/17260 --- .../node-core-integration-tests/suites/child-process/test.ts | 2 +- .../node-integration-tests/suites/child-process/test.ts | 2 +- packages/node-core/src/integrations/childProcess.ts | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/dev-packages/node-core-integration-tests/suites/child-process/test.ts b/dev-packages/node-core-integration-tests/suites/child-process/test.ts index 798cd48d86d0..1d04772c351e 100644 --- a/dev-packages/node-core-integration-tests/suites/child-process/test.ts +++ b/dev-packages/node-core-integration-tests/suites/child-process/test.ts @@ -10,7 +10,7 @@ const WORKER_EVENT: Event = { type: 'Error', value: 'Test error', mechanism: { - type: 'instrument', + type: 'auto.child_process.worker_thread', handled: false, data: { threadId: expect.any(String), diff --git a/dev-packages/node-integration-tests/suites/child-process/test.ts b/dev-packages/node-integration-tests/suites/child-process/test.ts index 798cd48d86d0..1d04772c351e 100644 --- a/dev-packages/node-integration-tests/suites/child-process/test.ts +++ b/dev-packages/node-integration-tests/suites/child-process/test.ts @@ -10,7 +10,7 @@ const WORKER_EVENT: Event = { type: 'Error', value: 'Test error', mechanism: { - type: 'instrument', + type: 'auto.child_process.worker_thread', handled: false, data: { threadId: expect.any(String), diff --git a/packages/node-core/src/integrations/childProcess.ts b/packages/node-core/src/integrations/childProcess.ts index 6fc6046c7e2f..0e3fcfe4633a 100644 --- a/packages/node-core/src/integrations/childProcess.ts +++ b/packages/node-core/src/integrations/childProcess.ts @@ -99,7 +99,7 @@ function captureWorkerThreadEvents(worker: Worker, options: Options): void { .on('error', error => { if (options.captureWorkerErrors !== false) { captureException(error, { - mechanism: { type: 'instrument', handled: false, data: { threadId: String(threadId) } }, + mechanism: { type: 'auto.child_process.worker_thread', handled: false, data: { threadId: String(threadId) } }, }); } else { addBreadcrumb({ From 7b98893ec19eceb64f229c45b010d08c0b120f80 Mon Sep 17 00:00:00 2001 From: Lukas Stracke Date: Thu, 11 Sep 2025 17:07:49 +0200 Subject: [PATCH 16/61] ref(nuxt): Add and adjust `mechanism.type` in error events (#17599) This PR adjusts/adds the `mechanism.type` field to errors caught from our Nuxt SDK. The name now follows the trace origin as well as possible. --- .../tests/errors.client.test.ts | 4 ++ .../tests/errors.server.test.ts | 42 +++++++++++++++---- .../nuxt-3-min/tests/errors.client.test.ts | 4 ++ .../nuxt-3-min/tests/errors.server.test.ts | 42 +++++++++++++++---- .../tests/errors.client.test.ts | 4 ++ .../tests/errors.server.test.ts | 42 +++++++++++++++---- .../nuxt-3/tests/errors.client.test.ts | 5 +++ .../nuxt-3/tests/errors.server.test.ts | 42 +++++++++++++++---- .../nuxt-4/tests/errors.client.test.ts | 5 +++ .../nuxt-4/tests/errors.server.test.ts | 42 +++++++++++++++---- .../src/runtime/hooks/captureErrorHook.ts | 2 +- packages/nuxt/src/runtime/utils.ts | 2 +- packages/nuxt/test/runtime/utils.test.ts | 8 ++-- 13 files changed, 198 insertions(+), 46 deletions(-) diff --git a/dev-packages/e2e-tests/test-applications/nuxt-3-dynamic-import/tests/errors.client.test.ts b/dev-packages/e2e-tests/test-applications/nuxt-3-dynamic-import/tests/errors.client.test.ts index f4ca86413598..123b6f9a332a 100644 --- a/dev-packages/e2e-tests/test-applications/nuxt-3-dynamic-import/tests/errors.client.test.ts +++ b/dev-packages/e2e-tests/test-applications/nuxt-3-dynamic-import/tests/errors.client.test.ts @@ -21,6 +21,7 @@ test.describe('client-side errors', async () => { value: 'Error thrown from Nuxt-3 E2E test app', mechanism: { handled: false, + type: 'auto.function.nuxt.vue-error', }, }, ], @@ -49,6 +50,7 @@ test.describe('client-side errors', async () => { value: 'Error thrown from Param Route Button', mechanism: { handled: false, + type: 'auto.function.nuxt.vue-error', }, }, ], @@ -82,6 +84,7 @@ test.describe('client-side errors', async () => { value: 'Error thrown from Nuxt-3 E2E test app', mechanism: { handled: false, + type: 'auto.function.nuxt.vue-error', }, }, ], @@ -96,6 +99,7 @@ test.describe('client-side errors', async () => { value: 'Another Error thrown from Nuxt-3 E2E test app', mechanism: { handled: false, + type: 'auto.function.nuxt.vue-error', }, }, ], diff --git a/dev-packages/e2e-tests/test-applications/nuxt-3-dynamic-import/tests/errors.server.test.ts b/dev-packages/e2e-tests/test-applications/nuxt-3-dynamic-import/tests/errors.server.test.ts index b18da9ba0a3b..53d89e5b937f 100644 --- a/dev-packages/e2e-tests/test-applications/nuxt-3-dynamic-import/tests/errors.server.test.ts +++ b/dev-packages/e2e-tests/test-applications/nuxt-3-dynamic-import/tests/errors.server.test.ts @@ -14,10 +14,23 @@ test.describe('server-side errors', async () => { expect(error.transaction).toEqual('GET /api/server-error'); - const exception = error.exception.values[0]; - expect(exception.type).toEqual('Error'); - expect(exception.value).toEqual('Nuxt 3 Server error'); - expect(exception.mechanism.handled).toBe(false); + const exception0 = error.exception.values[0]; + const exception1 = error.exception.values[1]; + + expect(exception0.type).toEqual('Error'); + expect(exception0.value).toEqual('Nuxt 3 Server error'); + expect(exception0.mechanism).toEqual({ + handled: false, + type: 'auto.function.nuxt.nitro', + exception_id: 1, + parent_id: 0, + source: 'cause', + }); + + expect(exception1.type).toEqual('Error'); + expect(exception1.value).toEqual('Nuxt 3 Server error'); + // TODO: This isn't correct but requires adjustment in the core SDK + expect(exception1.mechanism).toEqual({ handled: true, type: 'generic', exception_id: 0 }); }); test('captures api fetch error (fetched on click) with parametrized route', async ({ page }) => { @@ -32,9 +45,22 @@ test.describe('server-side errors', async () => { expect(error.transaction).toEqual('GET /api/param-error/1234'); - const exception = error.exception.values[0]; - expect(exception.type).toEqual('Error'); - expect(exception.value).toEqual('Nuxt 3 Param Server error'); - expect(exception.mechanism.handled).toBe(false); + const exception0 = error.exception.values[0]; + const exception1 = error.exception.values[1]; + + expect(exception0.type).toEqual('Error'); + expect(exception0.value).toEqual('Nuxt 3 Param Server error'); + expect(exception0.mechanism).toEqual({ + handled: false, + type: 'auto.function.nuxt.nitro', + exception_id: 1, + parent_id: 0, + source: 'cause', + }); + + expect(exception1.type).toEqual('Error'); + expect(exception1.value).toEqual('Nuxt 3 Param Server error'); + // TODO: This isn't correct but requires adjustment in the core SDK + expect(exception1.mechanism).toEqual({ handled: true, type: 'generic', exception_id: 0 }); }); }); diff --git a/dev-packages/e2e-tests/test-applications/nuxt-3-min/tests/errors.client.test.ts b/dev-packages/e2e-tests/test-applications/nuxt-3-min/tests/errors.client.test.ts index 0de0c3dd498a..e9ef67ef75b6 100644 --- a/dev-packages/e2e-tests/test-applications/nuxt-3-min/tests/errors.client.test.ts +++ b/dev-packages/e2e-tests/test-applications/nuxt-3-min/tests/errors.client.test.ts @@ -21,6 +21,7 @@ test.describe('client-side errors', async () => { value: 'Error thrown from Nuxt-3-min E2E test app', mechanism: { handled: false, + type: 'auto.function.nuxt.vue-error', }, }, ], @@ -49,6 +50,7 @@ test.describe('client-side errors', async () => { value: 'Error thrown from Param Route Button', mechanism: { handled: false, + type: 'auto.function.nuxt.vue-error', }, }, ], @@ -82,6 +84,7 @@ test.describe('client-side errors', async () => { value: 'Error thrown from Nuxt-3-min E2E test app', mechanism: { handled: false, + type: 'auto.function.nuxt.vue-error', }, }, ], @@ -96,6 +99,7 @@ test.describe('client-side errors', async () => { value: 'Another Error thrown from Nuxt-3-min E2E test app', mechanism: { handled: false, + type: 'auto.function.nuxt.vue-error', }, }, ], diff --git a/dev-packages/e2e-tests/test-applications/nuxt-3-min/tests/errors.server.test.ts b/dev-packages/e2e-tests/test-applications/nuxt-3-min/tests/errors.server.test.ts index 40904b51b993..8b182fec69c7 100644 --- a/dev-packages/e2e-tests/test-applications/nuxt-3-min/tests/errors.server.test.ts +++ b/dev-packages/e2e-tests/test-applications/nuxt-3-min/tests/errors.server.test.ts @@ -14,10 +14,23 @@ test.describe('server-side errors', async () => { expect(error.transaction).toEqual('GET /api/server-error'); - const exception = error.exception.values[0]; - expect(exception.type).toEqual('Error'); - expect(exception.value).toEqual('Nuxt 3 Server error'); - expect(exception.mechanism.handled).toBe(false); + const exception0 = error.exception.values[0]; + const exception1 = error.exception.values[1]; + + expect(exception0.type).toEqual('Error'); + expect(exception0.value).toEqual('Nuxt 3 Server error'); + expect(exception0.mechanism).toEqual({ + handled: false, + type: 'auto.function.nuxt.nitro', + exception_id: 1, + parent_id: 0, + source: 'cause', + }); + + // TODO: This isn't correct but requires adjustment in the core SDK + expect(exception1.type).toEqual('Error'); + expect(exception1.value).toEqual('Nuxt 3 Server error'); + expect(exception1.mechanism).toEqual({ handled: true, type: 'generic', exception_id: 0 }); }); test('captures api fetch error (fetched on click) with parametrized route', async ({ page }) => { @@ -32,9 +45,22 @@ test.describe('server-side errors', async () => { expect(error.transaction).toEqual('GET /api/param-error/1234'); - const exception = error.exception.values[0]; - expect(exception.type).toEqual('Error'); - expect(exception.value).toEqual('Nuxt 3 Param Server error'); - expect(exception.mechanism.handled).toBe(false); + const exception0 = error.exception.values[0]; + const exception1 = error.exception.values[1]; + + expect(exception0.type).toEqual('Error'); + expect(exception0.value).toEqual('Nuxt 3 Param Server error'); + expect(exception0.mechanism).toEqual({ + handled: false, + type: 'auto.function.nuxt.nitro', + exception_id: 1, + parent_id: 0, + source: 'cause', + }); + + // TODO: This isn't correct but requires adjustment in the core SDK + expect(exception1.type).toEqual('Error'); + expect(exception1.value).toEqual('Nuxt 3 Param Server error'); + expect(exception1.mechanism).toEqual({ handled: true, type: 'generic', exception_id: 0 }); }); }); diff --git a/dev-packages/e2e-tests/test-applications/nuxt-3-top-level-import/tests/errors.client.test.ts b/dev-packages/e2e-tests/test-applications/nuxt-3-top-level-import/tests/errors.client.test.ts index 445a62e54b2a..ff99eb1b192f 100644 --- a/dev-packages/e2e-tests/test-applications/nuxt-3-top-level-import/tests/errors.client.test.ts +++ b/dev-packages/e2e-tests/test-applications/nuxt-3-top-level-import/tests/errors.client.test.ts @@ -21,6 +21,7 @@ test.describe('client-side errors', async () => { value: 'Error thrown from Nuxt-3 E2E test app', mechanism: { handled: false, + type: 'auto.function.nuxt.vue-error', }, }, ], @@ -49,6 +50,7 @@ test.describe('client-side errors', async () => { value: 'Error thrown from Param Route Button', mechanism: { handled: false, + type: 'auto.function.nuxt.vue-error', }, }, ], @@ -82,6 +84,7 @@ test.describe('client-side errors', async () => { value: 'Error thrown from Nuxt-3 E2E test app', mechanism: { handled: false, + type: 'auto.function.nuxt.vue-error', }, }, ], @@ -96,6 +99,7 @@ test.describe('client-side errors', async () => { value: 'Another Error thrown from Nuxt-3 E2E test app', mechanism: { handled: false, + type: 'auto.function.nuxt.vue-error', }, }, ], diff --git a/dev-packages/e2e-tests/test-applications/nuxt-3-top-level-import/tests/errors.server.test.ts b/dev-packages/e2e-tests/test-applications/nuxt-3-top-level-import/tests/errors.server.test.ts index 551a33ee1fc1..d7aafde49d55 100644 --- a/dev-packages/e2e-tests/test-applications/nuxt-3-top-level-import/tests/errors.server.test.ts +++ b/dev-packages/e2e-tests/test-applications/nuxt-3-top-level-import/tests/errors.server.test.ts @@ -19,10 +19,23 @@ test.describe('server-side errors', async () => { expect(error.transaction).toEqual('GET /api/server-error'); - const exception = error.exception.values[0]; - expect(exception.type).toEqual('Error'); - expect(exception.value).toEqual('Nuxt 3 Server error'); - expect(exception.mechanism.handled).toBe(false); + const exception0 = error.exception.values[0]; + const exception1 = error.exception.values[1]; + + expect(exception0.type).toEqual('Error'); + expect(exception0.value).toEqual('Nuxt 3 Server error'); + expect(exception0.mechanism).toEqual({ + handled: false, + type: 'auto.function.nuxt.nitro', + exception_id: 1, + parent_id: 0, + source: 'cause', + }); + + expect(exception1.type).toEqual('Error'); + expect(exception1.value).toEqual('Nuxt 3 Server error'); + // TODO: This isn't correct but requires adjustment in the core SDK + expect(exception1.mechanism).toEqual({ handled: true, type: 'generic', exception_id: 0 }); expect(error.tags?.['my-isolated-tag']).toBe(true); expect(error.tags?.['my-global-scope-isolated-tag']).not.toBeDefined(); @@ -63,9 +76,22 @@ test.describe('server-side errors', async () => { expect(error.transaction).toEqual('GET /api/param-error/1234'); - const exception = error.exception.values[0]; - expect(exception.type).toEqual('Error'); - expect(exception.value).toEqual('Nuxt 3 Param Server error'); - expect(exception.mechanism.handled).toBe(false); + const exception0 = error.exception.values[0]; + const exception1 = error.exception.values[1]; + + expect(exception0.type).toEqual('Error'); + expect(exception0.value).toEqual('Nuxt 3 Param Server error'); + expect(exception0.mechanism).toEqual({ + handled: false, + type: 'auto.function.nuxt.nitro', + exception_id: 1, + parent_id: 0, + source: 'cause', + }); + + expect(exception1.type).toEqual('Error'); + expect(exception1.value).toEqual('Nuxt 3 Param Server error'); + // TODO: This isn't correct but requires adjustment in the core SDK + expect(exception1.mechanism).toEqual({ handled: true, type: 'generic', exception_id: 0 }); }); }); diff --git a/dev-packages/e2e-tests/test-applications/nuxt-3/tests/errors.client.test.ts b/dev-packages/e2e-tests/test-applications/nuxt-3/tests/errors.client.test.ts index d94f649f78d1..11e4a5a367d4 100644 --- a/dev-packages/e2e-tests/test-applications/nuxt-3/tests/errors.client.test.ts +++ b/dev-packages/e2e-tests/test-applications/nuxt-3/tests/errors.client.test.ts @@ -21,6 +21,7 @@ test.describe('client-side errors', async () => { value: 'Error thrown from Nuxt-3 E2E test app', mechanism: { handled: false, + type: 'auto.function.nuxt.vue-error', }, }, ], @@ -62,6 +63,7 @@ test.describe('client-side errors', async () => { value: 'Error thrown in Error Boundary', mechanism: { handled: false, + type: 'auto.function.nuxt.vue-error', }, }, ], @@ -90,6 +92,7 @@ test.describe('client-side errors', async () => { value: 'Error thrown from Param Route Button', mechanism: { handled: false, + type: 'auto.function.nuxt.vue-error', }, }, ], @@ -123,6 +126,7 @@ test.describe('client-side errors', async () => { value: 'Error thrown from Nuxt-3 E2E test app', mechanism: { handled: false, + type: 'auto.function.nuxt.vue-error', }, }, ], @@ -137,6 +141,7 @@ test.describe('client-side errors', async () => { value: 'Another Error thrown from Nuxt-3 E2E test app', mechanism: { handled: false, + type: 'auto.function.nuxt.vue-error', }, }, ], diff --git a/dev-packages/e2e-tests/test-applications/nuxt-3/tests/errors.server.test.ts b/dev-packages/e2e-tests/test-applications/nuxt-3/tests/errors.server.test.ts index b62654b60c47..338ee6e3f78a 100644 --- a/dev-packages/e2e-tests/test-applications/nuxt-3/tests/errors.server.test.ts +++ b/dev-packages/e2e-tests/test-applications/nuxt-3/tests/errors.server.test.ts @@ -14,10 +14,23 @@ test.describe('server-side errors', async () => { expect(error.transaction).toEqual('GET /api/server-error'); - const exception = error.exception.values[0]; - expect(exception.type).toEqual('Error'); - expect(exception.value).toEqual('Nuxt 3 Server error'); - expect(exception.mechanism.handled).toBe(false); + const exception0 = error.exception.values[0]; + const exception1 = error.exception.values[1]; + + expect(exception0.type).toEqual('Error'); + expect(exception0.value).toEqual('Nuxt 3 Server error'); + expect(exception0.mechanism).toEqual({ + handled: false, + type: 'chained', + exception_id: 1, + parent_id: 0, + source: 'cause', + }); + + expect(exception1.type).toEqual('Error'); + expect(exception1.value).toEqual('Nuxt 3 Server error'); + // TODO: This isn't correct but requires adjustment in the core SDK + expect(exception1.mechanism).toEqual({ handled: true, type: 'generic', exception_id: 0 }); }); test('captures api fetch error (fetched on click) with parametrized route', async ({ page }) => { @@ -32,9 +45,22 @@ test.describe('server-side errors', async () => { expect(error.transaction).toEqual('GET /api/param-error/1234'); - const exception = error.exception.values[0]; - expect(exception.type).toEqual('Error'); - expect(exception.value).toEqual('Nuxt 3 Param Server error'); - expect(exception.mechanism.handled).toBe(false); + const exception0 = error.exception.values[0]; + const exception1 = error.exception.values[1]; + + expect(exception0.type).toEqual('Error'); + expect(exception0.value).toEqual('Nuxt 3 Param Server error'); + expect(exception0.mechanism).toEqual({ + handled: false, + type: 'chained', + exception_id: 1, + parent_id: 0, + source: 'cause', + }); + + expect(exception1.type).toEqual('Error'); + expect(exception1.value).toEqual('Nuxt 3 Param Server error'); + // TODO: This isn't correct but requires adjustment in the core SDK + expect(exception1.mechanism).toEqual({ handled: true, type: 'generic', exception_id: 0 }); }); }); diff --git a/dev-packages/e2e-tests/test-applications/nuxt-4/tests/errors.client.test.ts b/dev-packages/e2e-tests/test-applications/nuxt-4/tests/errors.client.test.ts index 6694ae851df1..2aed77dbfed6 100644 --- a/dev-packages/e2e-tests/test-applications/nuxt-4/tests/errors.client.test.ts +++ b/dev-packages/e2e-tests/test-applications/nuxt-4/tests/errors.client.test.ts @@ -21,6 +21,7 @@ test.describe('client-side errors', async () => { value: 'Error thrown from Nuxt-4 E2E test app', mechanism: { handled: false, + type: 'auto.function.nuxt.vue-error', }, }, ], @@ -62,6 +63,7 @@ test.describe('client-side errors', async () => { value: 'Error thrown in Error Boundary', mechanism: { handled: false, + type: 'auto.function.nuxt.vue-error', }, }, ], @@ -90,6 +92,7 @@ test.describe('client-side errors', async () => { value: 'Error thrown from Param Route Button', mechanism: { handled: false, + type: 'auto.function.nuxt.vue-error', }, }, ], @@ -123,6 +126,7 @@ test.describe('client-side errors', async () => { value: 'Error thrown from Nuxt-4 E2E test app', mechanism: { handled: false, + type: 'auto.function.nuxt.vue-error', }, }, ], @@ -137,6 +141,7 @@ test.describe('client-side errors', async () => { value: 'Another Error thrown from Nuxt-4 E2E test app', mechanism: { handled: false, + type: 'auto.function.nuxt.vue-error', }, }, ], diff --git a/dev-packages/e2e-tests/test-applications/nuxt-4/tests/errors.server.test.ts b/dev-packages/e2e-tests/test-applications/nuxt-4/tests/errors.server.test.ts index 1d593cb09caf..62933f29dd48 100644 --- a/dev-packages/e2e-tests/test-applications/nuxt-4/tests/errors.server.test.ts +++ b/dev-packages/e2e-tests/test-applications/nuxt-4/tests/errors.server.test.ts @@ -14,10 +14,23 @@ test.describe('server-side errors', async () => { expect(error.transaction).toEqual('GET /api/server-error'); - const exception = error.exception.values[0]; - expect(exception.type).toEqual('Error'); - expect(exception.value).toEqual('Nuxt 4 Server error'); - expect(exception.mechanism.handled).toBe(false); + const exception0 = error.exception.values[0]; + const exception1 = error.exception.values[1]; + + expect(exception0.type).toEqual('Error'); + expect(exception0.value).toEqual('Nuxt 4 Server error'); + expect(exception0.mechanism).toEqual({ + handled: false, + type: 'auto.function.nuxt.nitro', + exception_id: 1, + parent_id: 0, + source: 'cause', + }); + + expect(exception1.type).toEqual('Error'); + expect(exception1.value).toEqual('Nuxt 4 Server error'); + // TODO: This isn't correct but requires adjustment in the core SDK + expect(exception1.mechanism).toEqual({ handled: true, type: 'generic', exception_id: 0 }); }); test('captures api fetch error (fetched on click) with parametrized route', async ({ page }) => { @@ -32,9 +45,22 @@ test.describe('server-side errors', async () => { expect(error.transaction).toEqual('GET /api/param-error/1234'); - const exception = error.exception.values[0]; - expect(exception.type).toEqual('Error'); - expect(exception.value).toEqual('Nuxt 4 Param Server error'); - expect(exception.mechanism.handled).toBe(false); + const exception0 = error.exception.values[0]; + const exception1 = error.exception.values[1]; + + expect(exception0.type).toEqual('Error'); + expect(exception0.value).toEqual('Nuxt 4 Param Server error'); + expect(exception0.mechanism).toEqual({ + handled: false, + type: 'auto.function.nuxt.nitro', + exception_id: 1, + parent_id: 0, + source: 'cause', + }); + + expect(exception1.type).toEqual('Error'); + expect(exception1.value).toEqual('Nuxt 4 Param Server error'); + // TODO: This isn't correct but requires adjustment in the core SDK + expect(exception1.mechanism).toEqual({ handled: true, type: 'generic', exception_id: 0 }); }); }); diff --git a/packages/nuxt/src/runtime/hooks/captureErrorHook.ts b/packages/nuxt/src/runtime/hooks/captureErrorHook.ts index 7a27b7e6e4c6..9c2bc1011277 100644 --- a/packages/nuxt/src/runtime/hooks/captureErrorHook.ts +++ b/packages/nuxt/src/runtime/hooks/captureErrorHook.ts @@ -40,7 +40,7 @@ export async function sentryCaptureErrorHook(error: Error, errorContext: Capture captureException(error, { captureContext: { contexts: { nuxt: structuredContext } }, - mechanism: { handled: false }, + mechanism: { handled: false, type: 'auto.function.nuxt.nitro' }, }); await flushIfServerless(); diff --git a/packages/nuxt/src/runtime/utils.ts b/packages/nuxt/src/runtime/utils.ts index 29abbe23ec62..ce8069c58cdb 100644 --- a/packages/nuxt/src/runtime/utils.ts +++ b/packages/nuxt/src/runtime/utils.ts @@ -81,7 +81,7 @@ export function reportNuxtError(options: { setTimeout(() => { captureException(error, { captureContext: { contexts: { nuxt: metadata } }, - mechanism: { handled: false }, + mechanism: { handled: false, type: `auto.function.nuxt.${instance ? 'vue' : 'app'}-error` }, }); }); } diff --git a/packages/nuxt/test/runtime/utils.test.ts b/packages/nuxt/test/runtime/utils.test.ts index 9c3dd568d67a..70226ee12c86 100644 --- a/packages/nuxt/test/runtime/utils.test.ts +++ b/packages/nuxt/test/runtime/utils.test.ts @@ -119,7 +119,7 @@ describe('reportNuxtError', () => { expect(captureException).toHaveBeenCalledWith(mockError, { captureContext: { contexts: { nuxt: { info: undefined } } }, - mechanism: { handled: false }, + mechanism: { handled: false, type: 'auto.function.nuxt.app-error' }, }); }); @@ -129,7 +129,7 @@ describe('reportNuxtError', () => { expect(captureException).toHaveBeenCalledWith(mockError, { captureContext: { contexts: { nuxt: { info: undefined, propsData: { foo: 'bar' } } } }, - mechanism: { handled: false }, + mechanism: { handled: false, type: 'auto.function.nuxt.vue-error' }, }); }); @@ -141,7 +141,7 @@ describe('reportNuxtError', () => { expect(captureException).toHaveBeenCalledWith(mockError, { captureContext: { contexts: { nuxt: { info: undefined } } }, - mechanism: { handled: false }, + mechanism: { handled: false, type: 'auto.function.nuxt.vue-error' }, }); }); @@ -151,7 +151,7 @@ describe('reportNuxtError', () => { expect(captureException).toHaveBeenCalledWith(mockError, { captureContext: { contexts: { nuxt: { info: 'Some info' } } }, - mechanism: { handled: false }, + mechanism: { handled: false, type: 'auto.function.nuxt.app-error' }, }); }); }); From 636b2a572668888609bef8fd97b7f66ddd551a64 Mon Sep 17 00:00:00 2001 From: Martin Sonnberger Date: Thu, 11 Sep 2025 18:07:00 +0200 Subject: [PATCH 17/61] chore(aws): Update README.md (#17601) --- packages/aws-serverless/README.md | 30 +++++++++++++----------------- 1 file changed, 13 insertions(+), 17 deletions(-) diff --git a/packages/aws-serverless/README.md b/packages/aws-serverless/README.md index 9109b8e059b0..353b702fbcb9 100644 --- a/packages/aws-serverless/README.md +++ b/packages/aws-serverless/README.md @@ -4,21 +4,17 @@

-# Official Sentry SDK for Serverless environments +# Official Sentry SDK for AWS Lambda ## Links -- [Official SDK Docs](https://docs.sentry.io/) +- [Official SDK Docs](https://docs.sentry.io/platforms/javascript/guides/aws-lambda) ## General -This package is a wrapper around `@sentry/node`, with added functionality related to various Serverless solutions. All +This package is a wrapper around `@sentry/node`, with added functionality related to AWS Lambda. All methods available in `@sentry/node` can be imported from `@sentry/aws-serverless`. -Currently supported environment: - -### AWS Lambda - To use this SDK, call `Sentry.init(options)` at the very beginning of your JavaScript file. ```javascript @@ -30,14 +26,14 @@ Sentry.init({ }); // async (recommended) -exports.handler = Sentry.wrapHandler(async (event, context) => { +export const handler = async (event, context) => { throw new Error('oh, hello there!'); -}); +}; // sync -exports.handler = Sentry.wrapHandler((event, context, callback) => { +export const handler = (event, context, callback) => { throw new Error('oh, hello there!'); -}); +}; ``` If you also want to trace performance of all the incoming requests and also outgoing AWS service requests, just set the @@ -46,21 +42,21 @@ If you also want to trace performance of all the incoming requests and also outg ```javascript import * as Sentry from '@sentry/aws-serverless'; -Sentry.AWSLambda.init({ +Sentry.init({ dsn: '__DSN__', tracesSampleRate: 1.0, }); ``` -#### Integrate Sentry using internal extension +#### Integrate Sentry using the Sentry Lambda layer -Another and much simpler way to integrate Sentry to your AWS Lambda function is to add an official layer. +Another much simpler way to integrate Sentry to your AWS Lambda function is to add the official layer. 1. Choose Layers -> Add Layer. -2. Specify an ARN: `arn:aws:lambda:us-west-1:TODO:layer:TODO:VERSION`. +2. Specify an ARN: `arn:aws:lambda:us-west-1:943013980633:layer:SentryNodeServerlessSDKv10:19`. Get the latest ARN from the [docs](https://docs.sentry.io/platforms/javascript/guides/aws-lambda/install/layer). 3. Go to Environment variables and add: - - `NODE_OPTIONS`: `-r @sentry/aws-serverless/build/npm/cjs/awslambda-auto`. + - `NODE_OPTIONS`: `--import @sentry/aws-serverless/awslambda-auto`. - `SENTRY_DSN`: `your dsn`. - `SENTRY_TRACES_SAMPLE_RATE`: a number between 0 and 1 representing the chance a transaction is sent to Sentry. For more information, see - [docs](https://docs.sentry.io/platforms/node/guides/aws-lambda/configuration/options/#tracesSampleRate). + [docs](https://docs.sentry.io/platforms/javascript/guides/aws-lambda/configuration/options/#tracesSampleRate). From 4f38b4b652eb11d42eb34b8d3893b7440ac92817 Mon Sep 17 00:00:00 2001 From: Andrei <168741329+andreiborza@users.noreply.github.com> Date: Thu, 11 Sep 2025 18:40:28 +0200 Subject: [PATCH 18/61] fix(nextjs): Add edge polyfills for nextjs-13 in dev mode (#17488) This issue is only observed for Nextjs 13 and specifically for apps that don't use the app router. Similarly to what we do in vercel-edge, we polyfill `performance`. This is only done for dev mode as it's not an issue otherwise. Note: I tried to reproduce this via our existing e2e test apps but they're all using the app router so it's not manifesting there. Closes: #17343 --- .../test-applications/nextjs-13/middleware.ts | 6 + .../tests/server/cjs-api-endpoints.test.ts | 6 +- .../server/wrapApiHandlerWithSentry.test.ts | 6 +- packages/nextjs/.eslintrc.js | 6 + packages/nextjs/rollup.npm.config.mjs | 15 +++ .../nextjs/src/config/polyfills/perf_hooks.js | 26 ++++ packages/nextjs/src/config/types.ts | 1 + packages/nextjs/src/config/webpack.ts | 27 +++- packages/nextjs/test/config/fixtures.ts | 8 +- .../webpack/constructWebpackConfig.test.ts | 121 ++++++++++++++++++ 10 files changed, 216 insertions(+), 6 deletions(-) create mode 100644 dev-packages/e2e-tests/test-applications/nextjs-13/middleware.ts create mode 100644 packages/nextjs/src/config/polyfills/perf_hooks.js diff --git a/dev-packages/e2e-tests/test-applications/nextjs-13/middleware.ts b/dev-packages/e2e-tests/test-applications/nextjs-13/middleware.ts new file mode 100644 index 000000000000..b2117419c10f --- /dev/null +++ b/dev-packages/e2e-tests/test-applications/nextjs-13/middleware.ts @@ -0,0 +1,6 @@ +import { NextResponse } from 'next/server'; + +export function middleware() { + // Basic middleware to ensure that the build works with edge runtime + return NextResponse.next(); +} diff --git a/dev-packages/e2e-tests/test-applications/nextjs-13/tests/server/cjs-api-endpoints.test.ts b/dev-packages/e2e-tests/test-applications/nextjs-13/tests/server/cjs-api-endpoints.test.ts index 42b2e3727bd6..28cc91e9b879 100644 --- a/dev-packages/e2e-tests/test-applications/nextjs-13/tests/server/cjs-api-endpoints.test.ts +++ b/dev-packages/e2e-tests/test-applications/nextjs-13/tests/server/cjs-api-endpoints.test.ts @@ -11,7 +11,8 @@ test('should create a transaction for a CJS pages router API endpoint', async ({ const transactionPromise = waitForTransaction('nextjs-13', async transactionEvent => { return ( transactionEvent.transaction === 'GET /api/cjs-api-endpoint' && - transactionEvent.contexts?.trace?.op === 'http.server' + transactionEvent.contexts?.trace?.op === 'http.server' && + transactionEvent.transaction_info?.source === 'route' ); }); @@ -73,7 +74,8 @@ test('should not mess up require statements in CJS API endpoints', async ({ requ const transactionPromise = waitForTransaction('nextjs-13', async transactionEvent => { return ( transactionEvent.transaction === 'GET /api/cjs-api-endpoint-with-require' && - transactionEvent.contexts?.trace?.op === 'http.server' + transactionEvent.contexts?.trace?.op === 'http.server' && + transactionEvent.transaction_info?.source === 'route' ); }); diff --git a/dev-packages/e2e-tests/test-applications/nextjs-13/tests/server/wrapApiHandlerWithSentry.test.ts b/dev-packages/e2e-tests/test-applications/nextjs-13/tests/server/wrapApiHandlerWithSentry.test.ts index 3bcc1bbbea92..798ea3409089 100644 --- a/dev-packages/e2e-tests/test-applications/nextjs-13/tests/server/wrapApiHandlerWithSentry.test.ts +++ b/dev-packages/e2e-tests/test-applications/nextjs-13/tests/server/wrapApiHandlerWithSentry.test.ts @@ -22,7 +22,11 @@ const cases = [ cases.forEach(({ name, url, transactionName }) => { test(`Should capture transactions for routes with various shapes (${name})`, async ({ request }) => { const transactionEventPromise = waitForTransaction('nextjs-13', transactionEvent => { - return transactionEvent.transaction === transactionName && transactionEvent.contexts?.trace?.op === 'http.server'; + return ( + transactionEvent.transaction === transactionName && + transactionEvent.contexts?.trace?.op === 'http.server' && + transactionEvent.transaction_info?.source === 'route' + ); }); request.get(url).catch(() => { diff --git a/packages/nextjs/.eslintrc.js b/packages/nextjs/.eslintrc.js index d255f74ce829..1f0ae547d4e0 100644 --- a/packages/nextjs/.eslintrc.js +++ b/packages/nextjs/.eslintrc.js @@ -21,5 +21,11 @@ module.exports = { 'import/no-extraneous-dependencies': 'off', }, }, + { + files: ['src/config/polyfills/perf_hooks.js'], + globals: { + globalThis: 'readonly', + }, + }, ], }; diff --git a/packages/nextjs/rollup.npm.config.mjs b/packages/nextjs/rollup.npm.config.mjs index 3582cf4574ef..89271a21e9d3 100644 --- a/packages/nextjs/rollup.npm.config.mjs +++ b/packages/nextjs/rollup.npm.config.mjs @@ -88,5 +88,20 @@ export default [ }, }), ), + ...makeNPMConfigVariants( + makeBaseNPMConfig({ + entrypoints: ['src/config/polyfills/perf_hooks.js'], + + packageSpecificConfig: { + output: { + // Preserve the original file structure (i.e., so that everything is still relative to `src`) + entryFileNames: 'config/polyfills/[name].js', + + // make it so Rollup calms down about the fact that we're combining default and named exports + exports: 'named', + }, + }, + }), + ), ...makeOtelLoaders('./build', 'sentry-node'), ]; diff --git a/packages/nextjs/src/config/polyfills/perf_hooks.js b/packages/nextjs/src/config/polyfills/perf_hooks.js new file mode 100644 index 000000000000..1a0ce4a2af76 --- /dev/null +++ b/packages/nextjs/src/config/polyfills/perf_hooks.js @@ -0,0 +1,26 @@ +// Polyfill for Node.js perf_hooks module in edge runtime +// This mirrors the polyfill from packages/vercel-edge/rollup.npm.config.mjs +const __sentry__timeOrigin = Date.now(); + +// Ensure performance global is available +if (typeof globalThis !== 'undefined' && globalThis.performance === undefined) { + globalThis.performance = { + timeOrigin: __sentry__timeOrigin, + now: function () { + return Date.now() - __sentry__timeOrigin; + }, + }; +} + +// Export the performance object for perf_hooks compatibility +export const performance = globalThis.performance || { + timeOrigin: __sentry__timeOrigin, + now: function () { + return Date.now() - __sentry__timeOrigin; + }, +}; + +// Default export for CommonJS compatibility +export default { + performance, +}; diff --git a/packages/nextjs/src/config/types.ts b/packages/nextjs/src/config/types.ts index 1ca5eaa6bab0..55f080fb433e 100644 --- a/packages/nextjs/src/config/types.ts +++ b/packages/nextjs/src/config/types.ts @@ -581,6 +581,7 @@ export type BuildContext = { webpack: { version: string; DefinePlugin: new (values: Record) => WebpackPluginInstance; + ProvidePlugin: new (values: Record) => WebpackPluginInstance; }; // eslint-disable-next-line @typescript-eslint/no-explicit-any defaultLoaders: any; // needed for type tests (test:types) diff --git a/packages/nextjs/src/config/webpack.ts b/packages/nextjs/src/config/webpack.ts index b336e1e2ee9b..8741efe81194 100644 --- a/packages/nextjs/src/config/webpack.ts +++ b/packages/nextjs/src/config/webpack.ts @@ -60,6 +60,8 @@ export function constructWebpackConfigFunction( const pageExtensions = userNextConfig.pageExtensions || ['tsx', 'ts', 'jsx', 'js']; const dotPrefixedPageExtensions = pageExtensions.map(ext => `.${ext}`); const pageExtensionRegex = pageExtensions.map(escapeStringForRegex).join('|'); + const nextVersion = nextJsVersion || getNextjsVersion(); + const { major } = parseSemver(nextVersion || ''); // We add `.ts` and `.js` back in because `pageExtensions` might not be relevant to the instrumentation file // e.g. user's setting `.mdx`. In that case we still want to default look up @@ -70,8 +72,6 @@ export function constructWebpackConfigFunction( warnAboutDeprecatedConfigFiles(projectDir, instrumentationFile, runtime); } if (runtime === 'server') { - const nextJsVersion = getNextjsVersion(); - const { major } = parseSemver(nextJsVersion || ''); // was added in v15 (https://github.com/vercel/next.js/pull/67539) if (major && major >= 15) { warnAboutMissingOnRequestErrorHandler(instrumentationFile); @@ -103,6 +103,11 @@ export function constructWebpackConfigFunction( addOtelWarningIgnoreRule(newConfig); + // Add edge runtime polyfills when building for edge in dev mode + if (major && major === 13 && runtime === 'edge' && isDev) { + addEdgeRuntimePolyfills(newConfig, buildContext); + } + let pagesDirPath: string | undefined; const maybePagesDirPath = path.join(projectDir, 'pages'); const maybeSrcPagesDirPath = path.join(projectDir, 'src', 'pages'); @@ -865,6 +870,24 @@ function addOtelWarningIgnoreRule(newConfig: WebpackConfigObjectWithModuleRules) } } +function addEdgeRuntimePolyfills(newConfig: WebpackConfigObjectWithModuleRules, buildContext: BuildContext): void { + // Use ProvidePlugin to inject performance global only when accessed + newConfig.plugins = newConfig.plugins || []; + newConfig.plugins.push( + new buildContext.webpack.ProvidePlugin({ + performance: [path.resolve(__dirname, 'polyfills', 'perf_hooks.js'), 'performance'], + }), + ); + + // Add module resolution aliases for problematic Node.js modules in edge runtime + newConfig.resolve = newConfig.resolve || {}; + newConfig.resolve.alias = { + ...newConfig.resolve.alias, + // Redirect perf_hooks imports to a polyfilled version + perf_hooks: path.resolve(__dirname, 'polyfills', 'perf_hooks.js'), + }; +} + function _getModules(projectDir: string): Record { try { const packageJson = path.join(projectDir, 'package.json'); diff --git a/packages/nextjs/test/config/fixtures.ts b/packages/nextjs/test/config/fixtures.ts index a3c4feb0123b..5f5d4a2d3504 100644 --- a/packages/nextjs/test/config/fixtures.ts +++ b/packages/nextjs/test/config/fixtures.ts @@ -99,7 +99,13 @@ export function getBuildContext( distDir: '.next', ...materializedNextConfig, } as NextConfigObject, - webpack: { version: webpackVersion, DefinePlugin: class {} as any }, + webpack: { + version: webpackVersion, + DefinePlugin: class {} as any, + ProvidePlugin: class { + constructor(public definitions: Record) {} + } as any, + }, defaultLoaders: true, totalPages: 2, isServer: buildTarget === 'server' || buildTarget === 'edge', diff --git a/packages/nextjs/test/config/webpack/constructWebpackConfig.test.ts b/packages/nextjs/test/config/webpack/constructWebpackConfig.test.ts index 3a8e86b94e29..7371d35c859a 100644 --- a/packages/nextjs/test/config/webpack/constructWebpackConfig.test.ts +++ b/packages/nextjs/test/config/webpack/constructWebpackConfig.test.ts @@ -2,11 +2,13 @@ import '../mocks'; import * as core from '@sentry/core'; import { describe, expect, it, vi } from 'vitest'; +import * as util from '../../../src/config/util'; import * as getWebpackPluginOptionsModule from '../../../src/config/webpackPluginOptions'; import { CLIENT_SDK_CONFIG_FILE, clientBuildContext, clientWebpackConfig, + edgeBuildContext, exportedNextConfig, serverBuildContext, serverWebpackConfig, @@ -185,4 +187,123 @@ describe('constructWebpackConfigFunction()', () => { }); }); }); + + describe('edge runtime polyfills', () => { + it('adds polyfills only for edge runtime in dev mode on Next.js 13', async () => { + // Mock Next.js version 13 - polyfills should be added + vi.spyOn(util, 'getNextjsVersion').mockReturnValue('13.0.0'); + + // Test edge runtime in dev mode with Next.js 13 - should add polyfills + const edgeDevBuildContext = { ...edgeBuildContext, dev: true }; + const edgeDevConfig = await materializeFinalWebpackConfig({ + exportedNextConfig, + incomingWebpackConfig: serverWebpackConfig, + incomingWebpackBuildContext: edgeDevBuildContext, + }); + + const edgeProvidePlugin = edgeDevConfig.plugins?.find(plugin => plugin.constructor.name === 'ProvidePlugin'); + expect(edgeProvidePlugin).toBeDefined(); + expect(edgeDevConfig.resolve?.alias?.perf_hooks).toMatch(/perf_hooks\.js$/); + + vi.restoreAllMocks(); + }); + + it('does NOT add polyfills for edge runtime in prod mode even on Next.js 13', async () => { + // Mock Next.js version 13 - but prod mode should still not add polyfills + vi.spyOn(util, 'getNextjsVersion').mockReturnValue('13.0.0'); + + // Test edge runtime in prod mode - should NOT add polyfills + const edgeProdBuildContext = { ...edgeBuildContext, dev: false }; + const edgeProdConfig = await materializeFinalWebpackConfig({ + exportedNextConfig, + incomingWebpackConfig: serverWebpackConfig, + incomingWebpackBuildContext: edgeProdBuildContext, + }); + + const edgeProdProvidePlugin = edgeProdConfig.plugins?.find(plugin => plugin.constructor.name === 'ProvidePlugin'); + expect(edgeProdProvidePlugin).toBeUndefined(); + + vi.restoreAllMocks(); + }); + + it('does NOT add polyfills for server runtime even on Next.js 13', async () => { + // Mock Next.js version 13 + vi.spyOn(util, 'getNextjsVersion').mockReturnValue('13.0.0'); + + // Test server runtime in dev mode - should NOT add polyfills + const serverDevBuildContext = { ...serverBuildContext, dev: true }; + const serverDevConfig = await materializeFinalWebpackConfig({ + exportedNextConfig, + incomingWebpackConfig: serverWebpackConfig, + incomingWebpackBuildContext: serverDevBuildContext, + }); + + const serverProvidePlugin = serverDevConfig.plugins?.find(plugin => plugin.constructor.name === 'ProvidePlugin'); + expect(serverProvidePlugin).toBeUndefined(); + + vi.restoreAllMocks(); + }); + + it('does NOT add polyfills for client runtime even on Next.js 13', async () => { + // Mock Next.js version 13 + vi.spyOn(util, 'getNextjsVersion').mockReturnValue('13.0.0'); + + // Test client runtime in dev mode - should NOT add polyfills + const clientDevBuildContext = { ...clientBuildContext, dev: true }; + const clientDevConfig = await materializeFinalWebpackConfig({ + exportedNextConfig, + incomingWebpackConfig: clientWebpackConfig, + incomingWebpackBuildContext: clientDevBuildContext, + }); + + const clientProvidePlugin = clientDevConfig.plugins?.find(plugin => plugin.constructor.name === 'ProvidePlugin'); + expect(clientProvidePlugin).toBeUndefined(); + + vi.restoreAllMocks(); + }); + + it('does NOT add polyfills for edge runtime in dev mode on Next.js versions other than 13', async () => { + const edgeDevBuildContext = { ...edgeBuildContext, dev: true }; + + // Test with Next.js 12 - should NOT add polyfills + vi.spyOn(util, 'getNextjsVersion').mockReturnValue('12.3.0'); + const edgeConfigV12 = await materializeFinalWebpackConfig({ + exportedNextConfig, + incomingWebpackConfig: serverWebpackConfig, + incomingWebpackBuildContext: edgeDevBuildContext, + }); + expect(edgeConfigV12.plugins?.find(plugin => plugin.constructor.name === 'ProvidePlugin')).toBeUndefined(); + vi.restoreAllMocks(); + + // Test with Next.js 14 - should NOT add polyfills + vi.spyOn(util, 'getNextjsVersion').mockReturnValue('14.0.0'); + const edgeConfigV14 = await materializeFinalWebpackConfig({ + exportedNextConfig, + incomingWebpackConfig: serverWebpackConfig, + incomingWebpackBuildContext: edgeDevBuildContext, + }); + expect(edgeConfigV14.plugins?.find(plugin => plugin.constructor.name === 'ProvidePlugin')).toBeUndefined(); + vi.restoreAllMocks(); + + // Test with Next.js 15 - should NOT add polyfills + vi.spyOn(util, 'getNextjsVersion').mockReturnValue('15.0.0'); + const edgeConfigV15 = await materializeFinalWebpackConfig({ + exportedNextConfig, + incomingWebpackConfig: serverWebpackConfig, + incomingWebpackBuildContext: edgeDevBuildContext, + }); + expect(edgeConfigV15.plugins?.find(plugin => plugin.constructor.name === 'ProvidePlugin')).toBeUndefined(); + vi.restoreAllMocks(); + + // Test with undefined Next.js version - should NOT add polyfills + vi.spyOn(util, 'getNextjsVersion').mockReturnValue(undefined); + const edgeConfigUndefined = await materializeFinalWebpackConfig({ + exportedNextConfig, + incomingWebpackConfig: serverWebpackConfig, + incomingWebpackBuildContext: edgeDevBuildContext, + }); + expect(edgeConfigUndefined.plugins?.find(plugin => plugin.constructor.name === 'ProvidePlugin')).toBeUndefined(); + vi.restoreAllMocks(); + }); + }); }); From 63a679731ce10599d221ff970e6b1a7163107629 Mon Sep 17 00:00:00 2001 From: Lukas Stracke Date: Fri, 12 Sep 2025 08:36:07 +0200 Subject: [PATCH 19/61] ref(core): Simplify `linkedErrors` mechanism logic (#17600) --- packages/core/src/utils/aggregate-errors.ts | 9 +++------ .../test/lib/utils/aggregate-errors.test.ts | 20 +++++++++++++++++++ 2 files changed, 23 insertions(+), 6 deletions(-) diff --git a/packages/core/src/utils/aggregate-errors.ts b/packages/core/src/utils/aggregate-errors.ts index f472e5b9968b..16d100d60c09 100644 --- a/packages/core/src/utils/aggregate-errors.ts +++ b/packages/core/src/utils/aggregate-errors.ts @@ -99,10 +99,9 @@ function aggregateExceptionsFromError( } function applyExceptionGroupFieldsForParentException(exception: Exception, exceptionId: number): void { - // Don't know if this default makes sense. The protocol requires us to set these values so we pick *some* default. - exception.mechanism = exception.mechanism || { type: 'generic', handled: true }; - exception.mechanism = { + handled: true, + type: 'auto.core.linked_errors', ...exception.mechanism, ...(exception.type === 'AggregateError' && { is_exception_group: true }), exception_id: exceptionId, @@ -115,10 +114,8 @@ function applyExceptionGroupFieldsForChildException( exceptionId: number, parentId: number | undefined, ): void { - // Don't know if this default makes sense. The protocol requires us to set these values so we pick *some* default. - exception.mechanism = exception.mechanism || { type: 'generic', handled: true }; - exception.mechanism = { + handled: true, ...exception.mechanism, type: 'chained', source, diff --git a/packages/core/test/lib/utils/aggregate-errors.test.ts b/packages/core/test/lib/utils/aggregate-errors.test.ts index 01ede6d9186a..fa11fd22f7c8 100644 --- a/packages/core/test/lib/utils/aggregate-errors.test.ts +++ b/packages/core/test/lib/utils/aggregate-errors.test.ts @@ -160,6 +160,26 @@ describe('applyAggregateErrorsToEvent()', () => { expect(event.exception?.values?.[event.exception.values.length - 1]?.mechanism?.type).toBe('instrument'); }); + test('should assign a defualt mechanism type for the root exception', () => { + const fakeAggregateError = new FakeAggregateError( + [new Error('Nested Error 1'), new Error('Nested Error 2')], + 'Root Error', + ); + + const exceptionFromError = (_stackParser: StackParser, ex: Error): Exception => { + return { value: ex.message, type: ex.name }; + }; + + const event: Event = { exception: { values: [exceptionFromError(stackParser, fakeAggregateError)] } }; + const eventHint: EventHint = { originalException: fakeAggregateError }; + + applyAggregateErrorsToEvent(exceptionFromError, stackParser, 'cause', 100, event, eventHint); + + expect(event.exception?.values?.[event.exception.values.length - 1]?.mechanism?.type).toBe( + 'auto.core.linked_errors', + ); + }); + test('should recursively walk mixed errors (Aggregate errors and based on `key`)', () => { const chainedError: ExtendedError = new Error('Nested Error 3'); chainedError.cause = new Error('Nested Error 4'); From 7647e7295483d801daf594bea5807abb830bcab5 Mon Sep 17 00:00:00 2001 From: Lukas Stracke Date: Fri, 12 Sep 2025 08:36:35 +0200 Subject: [PATCH 20/61] ref(react): Add mechanism to `reactErrorHandler` and adjust mechanism in `ErrorBoundary` (#17602) Both mechanisms now follow the trace origin naming scheme. Decided to make the `handled` value of `reactErrorHandler` depend on the definedness of the passed callback. --- .../react-19/tests/errors.test.ts | 28 +++++++++++ packages/react/src/error.ts | 7 ++- packages/react/src/errorboundary.tsx | 4 +- packages/react/test/error.test.ts | 47 ++++++++++++++++++- packages/react/test/errorboundary.test.tsx | 10 ++-- 5 files changed, 86 insertions(+), 10 deletions(-) diff --git a/dev-packages/e2e-tests/test-applications/react-19/tests/errors.test.ts b/dev-packages/e2e-tests/test-applications/react-19/tests/errors.test.ts index 46e19b11a2ac..496c47e417d2 100644 --- a/dev-packages/e2e-tests/test-applications/react-19/tests/errors.test.ts +++ b/dev-packages/e2e-tests/test-applications/react-19/tests/errors.test.ts @@ -19,6 +19,20 @@ test('Catches errors caught by error boundary', async ({ page }) => { expect(errorEvent.exception?.values).toHaveLength(2); expect(errorEvent.exception?.values?.[0]?.value).toBe('caught error'); + expect(errorEvent.exception?.values?.[0]?.mechanism).toEqual({ + type: 'auto.function.react.error_handler', + handled: true, // true because a callback was provided + exception_id: 1, + parent_id: 0, + source: 'cause', + }); + + expect(errorEvent.exception?.values?.[1]?.value).toBe('caught error'); + expect(errorEvent.exception?.values?.[1]?.mechanism).toEqual({ + type: 'generic', + handled: true, // true because a callback was provided + exception_id: 0, + }); }); test('Catches errors uncaught by error boundary', async ({ page }) => { @@ -39,4 +53,18 @@ test('Catches errors uncaught by error boundary', async ({ page }) => { expect(errorEvent.exception?.values).toHaveLength(2); expect(errorEvent.exception?.values?.[0]?.value).toBe('uncaught error'); + expect(errorEvent.exception?.values?.[0]?.mechanism).toEqual({ + type: 'auto.function.react.error_handler', + handled: true, // true because a callback was provided + exception_id: 1, + parent_id: 0, + source: 'cause', + }); + + expect(errorEvent.exception?.values?.[1]?.value).toBe('uncaught error'); + expect(errorEvent.exception?.values?.[1]?.mechanism).toEqual({ + type: 'generic', + handled: true, // true because a callback was provided + exception_id: 0, + }); }); diff --git a/packages/react/src/error.ts b/packages/react/src/error.ts index ca5ccd8b2698..c10cebe4ecf4 100644 --- a/packages/react/src/error.ts +++ b/packages/react/src/error.ts @@ -95,8 +95,11 @@ export function reactErrorHandler( ): (error: any, errorInfo: ErrorInfo) => void { // eslint-disable-next-line @typescript-eslint/no-explicit-any return (error: any, errorInfo: ErrorInfo) => { - const eventId = captureReactException(error, errorInfo); - if (callback) { + const hasCallback = !!callback; + const eventId = captureReactException(error, errorInfo, { + mechanism: { handled: hasCallback, type: 'auto.function.react.error_handler' }, + }); + if (hasCallback) { callback(error, errorInfo, eventId); } }; diff --git a/packages/react/src/errorboundary.tsx b/packages/react/src/errorboundary.tsx index f37afe961042..6377ae9c8399 100644 --- a/packages/react/src/errorboundary.tsx +++ b/packages/react/src/errorboundary.tsx @@ -123,7 +123,9 @@ class ErrorBoundary extends React.Component { test.each([ @@ -13,3 +14,45 @@ describe('isAtLeastReact17', () => { expect(isAtLeastReact17(input)).toBe(output); }); }); + +describe('reactErrorHandler', () => { + const captureException = vi.spyOn(SentryBrowser, 'captureException'); + + beforeEach(() => { + captureException.mockClear(); + }); + + it('captures errors as unhandled when no callback is provided', () => { + const error = new Error('test error'); + const errorInfo = { componentStack: 'component stack' }; + + const handler = reactErrorHandler(); + + handler(error, errorInfo); + + expect(captureException).toHaveBeenCalledTimes(1); + expect(captureException).toHaveBeenCalledWith(error, { + mechanism: { handled: false, type: 'auto.function.react.error_handler' }, + }); + }); + + it('captures errors as handled when a callback is provided', () => { + captureException.mockReturnValueOnce('custom-event-id'); + + const error = new Error('test error'); + const errorInfo = { componentStack: 'component stack' }; + + const callback = vi.fn(); + const handler = reactErrorHandler(callback); + + handler(error, errorInfo); + + expect(captureException).toHaveBeenCalledTimes(1); + expect(captureException).toHaveBeenCalledWith(error, { + mechanism: { handled: true, type: 'auto.function.react.error_handler' }, + }); + + expect(callback).toHaveBeenCalledTimes(1); + expect(callback).toHaveBeenCalledWith(error, errorInfo, 'custom-event-id'); + }); +}); diff --git a/packages/react/test/errorboundary.test.tsx b/packages/react/test/errorboundary.test.tsx index 5e731cc86b49..3d2198e81dc9 100644 --- a/packages/react/test/errorboundary.test.tsx +++ b/packages/react/test/errorboundary.test.tsx @@ -385,7 +385,7 @@ describe('ErrorBoundary', () => { expect(mockCaptureException).toHaveBeenCalledTimes(1); expect(mockCaptureException).toHaveBeenLastCalledWith(expect.any(Error), { - mechanism: { handled: true }, + mechanism: { handled: true, type: 'auto.function.react.error_boundary' }, }); expect(scopeSetContextSpy).toHaveBeenCalledTimes(1); @@ -444,7 +444,7 @@ describe('ErrorBoundary', () => { expect(mockCaptureException).toHaveBeenCalledTimes(1); expect(mockCaptureException).toHaveBeenLastCalledWith('bam', { - mechanism: { handled: true }, + mechanism: { handled: true, type: 'auto.function.react.error_boundary' }, }); expect(scopeSetContextSpy).toHaveBeenCalledTimes(1); @@ -483,7 +483,7 @@ describe('ErrorBoundary', () => { expect(mockCaptureException).toHaveBeenCalledTimes(1); expect(mockCaptureException).toHaveBeenLastCalledWith(expect.any(Error), { - mechanism: { handled: true }, + mechanism: { handled: true, type: 'auto.function.react.error_boundary' }, }); expect(scopeSetContextSpy).toHaveBeenCalledTimes(1); @@ -527,7 +527,7 @@ describe('ErrorBoundary', () => { expect(mockCaptureException).toHaveBeenCalledTimes(1); expect(mockCaptureException).toHaveBeenLastCalledWith(expect.any(Error), { - mechanism: { handled: true }, + mechanism: { handled: true, type: 'auto.function.react.error_boundary' }, }); expect(scopeSetContextSpy).toHaveBeenCalledTimes(1); @@ -695,7 +695,7 @@ describe('ErrorBoundary', () => { expect(mockCaptureException).toHaveBeenCalledTimes(1); expect(mockCaptureException).toHaveBeenLastCalledWith(expect.any(Object), { - mechanism: { handled: expected }, + mechanism: { handled: expected, type: 'auto.function.react.error_boundary' }, }); expect(scopeSetContextSpy).toHaveBeenCalledTimes(1); From 1917899f18c35d4e50e06b77154f0d4ab84fc5a6 Mon Sep 17 00:00:00 2001 From: Lukas Stracke Date: Fri, 12 Sep 2025 09:33:00 +0200 Subject: [PATCH 21/61] test(angular): Bump TS version to 5.9.0 in Angular 20 e2e test (#17605) Angular 21 will only support TS 5.9.0 or greater. This breaking change was introduced in the [`next.3` preview release](https://github.com/angular/angular/releases/tag/21.0.0-next.3) and was flagged in our Angular canary test. Bumping the TS version should be safe here since it will be required by NG21 and still fits into the [compatible range of NG20](https://angular.dev/reference/versions). closes https://github.com/getsentry/sentry-javascript/issues/17603 --- .../test-applications/angular-20/package.json | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/dev-packages/e2e-tests/test-applications/angular-20/package.json b/dev-packages/e2e-tests/test-applications/angular-20/package.json index 73f22787cd85..5488a1fef56c 100644 --- a/dev-packages/e2e-tests/test-applications/angular-20/package.json +++ b/dev-packages/e2e-tests/test-applications/angular-20/package.json @@ -44,9 +44,17 @@ "karma-coverage": "~2.2.0", "karma-jasmine": "~5.1.0", "karma-jasmine-html-reporter": "~2.1.0", - "typescript": "~5.8.3" + "typescript": "~5.9.0" }, "volta": { "extends": "../../package.json" + }, + "sentryTest": { + "optionalVariants": [ + { + "build-command": "pnpm test:build-canary", + "label": "angular (canary)" + } + ] } } From 47342f35863f2d33be77ea7d2d9f84bc3232967c Mon Sep 17 00:00:00 2001 From: Lukas Stracke Date: Fri, 12 Sep 2025 10:11:35 +0200 Subject: [PATCH 22/61] ref(node): Add mechanism to errors captured via connect and koa integrations (#17579) Adds mechanism following the trace origin naming scheme closes https://github.com/getsentry/sentry-javascript/issues/17261 ref: https://github.com/getsentry/sentry-javascript/issues/17212 --- .../test-applications/node-connect/tests/errors.test.ts | 8 +++++++- .../test-applications/node-koa/tests/errors.test.ts | 8 +++++++- packages/node/src/integrations/tracing/connect.ts | 7 ++++++- packages/node/src/integrations/tracing/koa.ts | 7 ++++++- 4 files changed, 26 insertions(+), 4 deletions(-) diff --git a/dev-packages/e2e-tests/test-applications/node-connect/tests/errors.test.ts b/dev-packages/e2e-tests/test-applications/node-connect/tests/errors.test.ts index 2aa569915eb4..c8d7b3123482 100644 --- a/dev-packages/e2e-tests/test-applications/node-connect/tests/errors.test.ts +++ b/dev-packages/e2e-tests/test-applications/node-connect/tests/errors.test.ts @@ -11,7 +11,13 @@ test('Sends correct error event', async ({ baseURL }) => { const errorEvent = await errorEventPromise; expect(errorEvent.exception?.values).toHaveLength(1); - expect(errorEvent.exception?.values?.[0]?.value).toBe('This is an exception'); + const exception = errorEvent.exception?.values?.[0]; + expect(exception?.value).toBe('This is an exception'); + + expect(exception?.mechanism).toEqual({ + type: 'auto.middleware.connect', + handled: false, + }); expect(errorEvent.request).toEqual({ method: 'GET', diff --git a/dev-packages/e2e-tests/test-applications/node-koa/tests/errors.test.ts b/dev-packages/e2e-tests/test-applications/node-koa/tests/errors.test.ts index cadf29fbda90..dd0dfa4a084d 100644 --- a/dev-packages/e2e-tests/test-applications/node-koa/tests/errors.test.ts +++ b/dev-packages/e2e-tests/test-applications/node-koa/tests/errors.test.ts @@ -11,7 +11,13 @@ test('Sends correct error event', async ({ baseURL }) => { const errorEvent = await errorEventPromise; expect(errorEvent.exception?.values).toHaveLength(1); - expect(errorEvent.exception?.values?.[0]?.value).toBe('This is an exception with id 123'); + const exception = errorEvent.exception?.values?.[0]; + + expect(exception?.value).toBe('This is an exception with id 123'); + expect(exception?.mechanism).toEqual({ + type: 'auto.middleware.koa', + handled: false, + }); expect(errorEvent.request).toEqual({ method: 'GET', diff --git a/packages/node/src/integrations/tracing/connect.ts b/packages/node/src/integrations/tracing/connect.ts index c2ab3716bd33..3fd7d10b28fb 100644 --- a/packages/node/src/integrations/tracing/connect.ts +++ b/packages/node/src/integrations/tracing/connect.ts @@ -48,7 +48,12 @@ export const connectIntegration = defineIntegration(_connectIntegration); // eslint-disable-next-line @typescript-eslint/no-explicit-any function connectErrorMiddleware(err: any, req: any, res: any, next: any): void { - captureException(err); + captureException(err, { + mechanism: { + handled: false, + type: 'auto.middleware.connect', + }, + }); next(err); } diff --git a/packages/node/src/integrations/tracing/koa.ts b/packages/node/src/integrations/tracing/koa.ts index 487f471a9a12..7125479e91e0 100644 --- a/packages/node/src/integrations/tracing/koa.ts +++ b/packages/node/src/integrations/tracing/koa.ts @@ -134,7 +134,12 @@ export const setupKoaErrorHandler = (app: { use: (arg0: (ctx: any, next: any) => try { await next(); } catch (error) { - captureException(error); + captureException(error, { + mechanism: { + handled: false, + type: 'auto.middleware.koa', + }, + }); throw error; } }); From bd0671dad994cf8573521e0dac3b8ec6bc922907 Mon Sep 17 00:00:00 2001 From: Sigrid Huemer <32902192+s1gr1d@users.noreply.github.com> Date: Fri, 12 Sep 2025 10:44:47 +0200 Subject: [PATCH 23/61] feat(sveltekit): Align build time options with shared type (#17413) Closes https://github.com/getsentry/sentry-javascript/issues/17065 --- .../buildTimeOptionsBase.ts | 7 + .../sveltekit/src/vite/sentryVitePlugins.ts | 63 ++++- packages/sveltekit/src/vite/types.ts | 159 +++++------ .../test/vite/sentrySvelteKitPlugins.test.ts | 260 ++++++++++++++++++ 4 files changed, 388 insertions(+), 101 deletions(-) diff --git a/packages/core/src/build-time-plugins/buildTimeOptionsBase.ts b/packages/core/src/build-time-plugins/buildTimeOptionsBase.ts index 5c40d53174c1..e43160cc03ab 100644 --- a/packages/core/src/build-time-plugins/buildTimeOptionsBase.ts +++ b/packages/core/src/build-time-plugins/buildTimeOptionsBase.ts @@ -257,6 +257,13 @@ interface ReleaseOptions { */ name?: string; + /** + * Whether the plugin should inject release information into the build for the SDK to pick it up when sending events (recommended). + * + * @default true + */ + inject?: boolean; + /** * Whether to create a new release. * diff --git a/packages/sveltekit/src/vite/sentryVitePlugins.ts b/packages/sveltekit/src/vite/sentryVitePlugins.ts index 61b388a94cf2..56493e071a55 100644 --- a/packages/sveltekit/src/vite/sentryVitePlugins.ts +++ b/packages/sveltekit/src/vite/sentryVitePlugins.ts @@ -91,31 +91,80 @@ export function generateVitePluginOptions( }; } + // todo(v11): remove deprecated options (Also from options type) + // Source Maps if (svelteKitPluginOptions.autoUploadSourceMaps && process.env.NODE_ENV !== 'development') { - const { unstable_sentryVitePluginOptions, ...sourceMapsUploadOptions } = - svelteKitPluginOptions.sourceMapsUploadOptions || {}; + const { + // eslint-disable-next-line deprecation/deprecation + unstable_sentryVitePluginOptions: deprecated_unstableSourceMapUploadOptions, + ...deprecatedSourceMapUploadOptions + // eslint-disable-next-line deprecation/deprecation + } = svelteKitPluginOptions.sourceMapsUploadOptions || {}; + + const { + // eslint-disable-next-line @typescript-eslint/no-unused-vars,deprecation/deprecation + sourceMapsUploadOptions: _filtered1, + // eslint-disable-next-line @typescript-eslint/no-unused-vars + unstable_sentryVitePluginOptions: _filtered2, + // eslint-disable-next-line @typescript-eslint/no-unused-vars + autoUploadSourceMaps: _filtered3, + // eslint-disable-next-line @typescript-eslint/no-unused-vars + autoInstrument: _filtered4, + sentryUrl, + ...newSvelteKitPluginOptions + } = svelteKitPluginOptions; + + const { unstable_sentryVitePluginOptions } = svelteKitPluginOptions; sentryVitePluginsOptions = { ...(sentryVitePluginsOptions ? sentryVitePluginsOptions : {}), - ...sourceMapsUploadOptions, + ...deprecatedSourceMapUploadOptions, + ...newSvelteKitPluginOptions, + + url: sentryUrl, + + ...deprecated_unstableSourceMapUploadOptions, ...unstable_sentryVitePluginOptions, + adapter: svelteKitPluginOptions.adapter, // override the plugin's debug flag with the one from the top-level options debug: svelteKitPluginOptions.debug, }; - if (sentryVitePluginsOptions.sourcemaps) { + // Handle sourcemaps options - merge deprecated and new, with new taking precedence + if ( + // eslint-disable-next-line deprecation/deprecation + deprecatedSourceMapUploadOptions.sourcemaps || + svelteKitPluginOptions.sourcemaps || + deprecated_unstableSourceMapUploadOptions?.sourcemaps || + unstable_sentryVitePluginOptions?.sourcemaps + ) { sentryVitePluginsOptions.sourcemaps = { - ...sourceMapsUploadOptions?.sourcemaps, + // eslint-disable-next-line deprecation/deprecation + ...deprecatedSourceMapUploadOptions.sourcemaps, + ...svelteKitPluginOptions.sourcemaps, + // Also handle nested deprecated options from unstable plugin options + ...deprecated_unstableSourceMapUploadOptions?.sourcemaps, ...unstable_sentryVitePluginOptions?.sourcemaps, }; } - if (sentryVitePluginsOptions.release) { + // Handle release options - merge deprecated and new, with new taking precedence + if ( + // eslint-disable-next-line deprecation/deprecation + deprecatedSourceMapUploadOptions.release || + svelteKitPluginOptions.release || + deprecated_unstableSourceMapUploadOptions?.release || + unstable_sentryVitePluginOptions?.release + ) { sentryVitePluginsOptions.release = { - ...sourceMapsUploadOptions?.release, + // eslint-disable-next-line deprecation/deprecation + ...deprecatedSourceMapUploadOptions.release, + ...svelteKitPluginOptions.release, + // Also handle nested deprecated options from unstable plugin options + ...deprecated_unstableSourceMapUploadOptions?.release, ...unstable_sentryVitePluginOptions?.release, }; } diff --git a/packages/sveltekit/src/vite/types.ts b/packages/sveltekit/src/vite/types.ts index 4267ce378bb1..5f623cc876ad 100644 --- a/packages/sveltekit/src/vite/types.ts +++ b/packages/sveltekit/src/vite/types.ts @@ -1,3 +1,4 @@ +import type { BuildTimeOptionsBase, UnstableVitePluginOptions } from '@sentry/core'; import type { SentryVitePluginOptions } from '@sentry/vite-plugin'; import type { AutoInstrumentSelection } from './autoInstrument'; import type { SupportedSvelteKitAdapters } from './detectAdapter'; @@ -19,18 +20,24 @@ type SourceMapsUploadOptions = { * * To create an auth token, follow this guide: * @see https://docs.sentry.io/product/accounts/auth-tokens/#organization-auth-tokens + * + * @deprecated Use option `authToken` instead of `sourceMapsUploadOptions.authToken` */ authToken?: string; /** * The organization slug of your Sentry organization. * Instead of specifying this option, you can also set the `SENTRY_ORG` environment variable. + * + * @deprecated Use option `org` instead of `sourceMapsUploadOptions.org` */ org?: string; /** * The project slug of your Sentry project. * Instead of specifying this option, you can also set the `SENTRY_PROJECT` environment variable. + * + * @deprecated Use option `project` instead of `sourceMapsUploadOptions.project` */ project?: string; @@ -39,11 +46,13 @@ type SourceMapsUploadOptions = { * It will not collect any sensitive or user-specific data. * * @default true + * @deprecated Use option `telemetry` instead of `sourceMapsUploadOptions.telemetry` */ telemetry?: boolean; /** * Options related to sourcemaps + * @deprecated Use `sourcemaps` instead of `sourceMapsUploadOptions.sourcemaps` */ sourcemaps?: { /** @@ -55,6 +64,7 @@ type SourceMapsUploadOptions = { * * The globbing patterns must follow the implementation of the `glob` package. * @see https://www.npmjs.com/package/glob#glob-primer + * @deprecated Use `sourcemaps.assets` instead of `sourceMapsUploadOptions.sourcemaps.assets` */ assets?: string | Array; @@ -65,6 +75,8 @@ type SourceMapsUploadOptions = { * or the default value for `assets` are uploaded. * * The globbing patterns follow the implementation of the glob package. (https://www.npmjs.com/package/glob) + * + * @deprecated Use `sourcemaps.ignore` instead of `sourceMapsUploadOptions.sourcemaps.ignore` */ ignore?: string | Array; @@ -75,6 +87,8 @@ type SourceMapsUploadOptions = { * @default [] - By default no files are deleted. * * The globbing patterns follow the implementation of the glob package. (https://www.npmjs.com/package/glob) + * + * @deprecated Use `sourcemaps.filesToDeleteAfterUpload` instead of `sourceMapsUploadOptions.sourcemaps.filesToDeleteAfterUpload` */ filesToDeleteAfterUpload?: string | Array; }; @@ -83,6 +97,8 @@ type SourceMapsUploadOptions = { * Options related to managing the Sentry releases for a build. * * Note: Managing releases is optional and not required for uploading source maps. + * + * @deprecated Use `release` instead of `sourceMapsUploadOptions.release` */ release?: { /** @@ -94,6 +110,8 @@ type SourceMapsUploadOptions = { * access to git CLI and for the root directory to be a valid repository). * * If you didn't provide a value and the plugin can't automatically detect one, no release will be created. + * + * @deprecated Use `release.name` instead of `sourceMapsUploadOptions.release.name` */ name?: string; @@ -102,12 +120,16 @@ type SourceMapsUploadOptions = { * sending events. * * Defaults to `true`. + * + * @deprecated Use `release.inject` instead of `sourceMapsUploadOptions.release.inject` */ inject?: boolean; }; /** * The URL of the Sentry instance to upload the source maps to. + * + * @deprecated Use `sentryUrl` instead of `sourceMapsUploadOptions.url` */ url?: string; @@ -123,105 +145,54 @@ type SourceMapsUploadOptions = { * changes can occur at any time within a major SDK version. * * Furthermore, some options are untested with SvelteKit specifically. Use with caution. - */ - unstable_sentryVitePluginOptions?: Partial; -}; - -type BundleSizeOptimizationOptions = { - /** - * If set to `true`, the plugin will attempt to tree-shake (remove) any debugging code within the Sentry SDK. - * Note that the success of this depends on tree shaking being enabled in your build tooling. * - * Setting this option to `true` will disable features like the SDK's `debug` option. - */ - excludeDebugStatements?: boolean; - - /** - * If set to true, the plugin will try to tree-shake tracing statements out. - * Note that the success of this depends on tree shaking generally being enabled in your build. - * Attention: DO NOT enable this when you're using any performance monitoring-related SDK features (e.g. Sentry.startSpan()). + * @deprecated Use `unstable_sentryVitePluginOptions` instead of `sourceMapsUploadOptions.unstable_sentryVitePluginOptions` */ - excludeTracing?: boolean; - - /** - * If set to `true`, the plugin will attempt to tree-shake (remove) code related to the Sentry SDK's Session Replay Shadow DOM recording functionality. - * Note that the success of this depends on tree shaking being enabled in your build tooling. - * - * This option is safe to be used when you do not want to capture any Shadow DOM activity via Sentry Session Replay. - */ - excludeReplayShadowDom?: boolean; - - /** - * If set to `true`, the plugin will attempt to tree-shake (remove) code related to the Sentry SDK's Session Replay `iframe` recording functionality. - * Note that the success of this depends on tree shaking being enabled in your build tooling. - * - * You can safely do this when you do not want to capture any `iframe` activity via Sentry Session Replay. - */ - excludeReplayIframe?: boolean; - - /** - * If set to `true`, the plugin will attempt to tree-shake (remove) code related to the Sentry SDK's Session Replay's Compression Web Worker. - * Note that the success of this depends on tree shaking being enabled in your build tooling. - * - * **Notice:** You should only do use this option if you manually host a compression worker and configure it in your Sentry Session Replay integration config via the `workerUrl` option. - */ - excludeReplayWorker?: boolean; + unstable_sentryVitePluginOptions?: Partial; }; /** Options for the Sentry SvelteKit plugin */ -export type SentrySvelteKitPluginOptions = { - /** - * If this flag is `true`, the Sentry plugins will log some useful debug information. - * @default false. - */ - debug?: boolean; - - /** - * The Sentry plugin will automatically instrument certain parts of your SvelteKit application at build time. - * Set this option to `false` to disable this behavior or what is intrumented by passing an object. - * - * Auto instrumentation includes: - * - Universal `load` functions in `+page.(js|ts)` files - * - Server-only `load` functions in `+page.server.(js|ts)` files - * - * @default true (meaning, the plugin will instrument all of the above) - */ - autoInstrument?: boolean | AutoInstrumentSelection; - - /** - * Specify which SvelteKit adapter you're using. - * By default, the SDK will attempt auto-detect the used adapter at build time and apply the - * correct config for source maps upload or auto-instrumentation. - * - * Currently, the SDK supports the following adapters: - * - node (@sveltejs/adapter-node) - * - auto (@sveltejs/adapter-auto) only Vercel - * - vercel (@sveltejs/adapter-auto) only Serverless functions, no edge runtime - * - * Set this option, if the SDK detects the wrong adapter or you want to use an adapter - * that is not in this list. If you specify 'other', you'll most likely need to configure - * source maps upload yourself. - * - * @default {} the SDK attempts to auto-detect the used adapter at build time - */ - adapter?: SupportedSvelteKitAdapters; +export type SentrySvelteKitPluginOptions = BuildTimeOptionsBase & + UnstableVitePluginOptions> & { + /** + * The Sentry plugin will automatically instrument certain parts of your SvelteKit application at build time. + * Set this option to `false` to disable this behavior or what is intrumented by passing an object. + * + * Auto instrumentation includes: + * - Universal `load` functions in `+page.(js|ts)` files + * - Server-only `load` functions in `+page.server.(js|ts)` files + * + * @default true (meaning, the plugin will instrument all of the above) + */ + autoInstrument?: boolean | AutoInstrumentSelection; - /** - * Options for the Sentry Vite plugin to customize bundle size optimizations. - * - * These options are always read from the `sentryAstro` integration. - * Do not define them in the `sentry.client.config.(js|ts)` or `sentry.server.config.(js|ts)` files. - */ - bundleSizeOptimizations?: BundleSizeOptimizationOptions; + /** + * Specify which SvelteKit adapter you're using. + * By default, the SDK will attempt auto-detect the used adapter at build time and apply the + * correct config for source maps upload or auto-instrumentation. + * + * Currently, the SDK supports the following adapters: + * - node (@sveltejs/adapter-node) + * - auto (@sveltejs/adapter-auto) only Vercel + * - vercel (@sveltejs/adapter-auto) only Serverless functions, no edge runtime + * + * Set this option, if the SDK detects the wrong adapter or you want to use an adapter + * that is not in this list. If you specify 'other', you'll most likely need to configure + * source maps upload yourself. + * + * @default {} the SDK attempts to auto-detect the used adapter at build time + */ + adapter?: SupportedSvelteKitAdapters; - /** - * If this flag is `true`, the Sentry plugins will automatically upload source maps to Sentry. - * @default true`. - */ - autoUploadSourceMaps?: boolean; + /** + * If this flag is `true`, the Sentry plugins will automatically upload source maps to Sentry. + * @default true`. + */ + autoUploadSourceMaps?: boolean; - /** - * Options related to source maps upload to Sentry - */ - sourceMapsUploadOptions?: SourceMapsUploadOptions; -}; + /** * Options related to source maps upload to Sentry + * + * @deprecated This option was deprecated as it adds unnecessary nesting. Put the options one level higher to the root-level of the Sentry Svelte plugin options. + */ + sourceMapsUploadOptions?: SourceMapsUploadOptions; + }; diff --git a/packages/sveltekit/test/vite/sentrySvelteKitPlugins.test.ts b/packages/sveltekit/test/vite/sentrySvelteKitPlugins.test.ts index fa8df96f03a6..eef008fca73d 100644 --- a/packages/sveltekit/test/vite/sentrySvelteKitPlugins.test.ts +++ b/packages/sveltekit/test/vite/sentrySvelteKitPlugins.test.ts @@ -30,6 +30,7 @@ function getSentrySvelteKitPlugins(options?: Parameters[ authToken: 'token', org: 'org', project: 'project', + // eslint-disable-next-line deprecation/deprecation ...options?.sourceMapsUploadOptions, }, ...options, @@ -389,4 +390,263 @@ describe('generateVitePluginOptions', () => { process.env.NODE_ENV = originalEnv; }); + + it.each([ + { + testName: 'org setting precedence', + options: { + autoUploadSourceMaps: true, + org: 'root-org', + sourceMapsUploadOptions: { + org: 'deprecated-org', + unstable_sentryVitePluginOptions: { + org: 'unstable-org', + }, + }, + unstable_sentryVitePluginOptions: { + org: 'new-unstable-org', + }, + }, + expectedOrg: 'new-unstable-org', + }, + { + testName: 'project setting precedence', + options: { + autoUploadSourceMaps: true, + project: 'root-project', + sourceMapsUploadOptions: { + project: 'deprecated-project', + unstable_sentryVitePluginOptions: { + project: 'unstable-project', + }, + }, + unstable_sentryVitePluginOptions: { + project: 'new-unstable-project', + }, + }, + expectedProject: 'new-unstable-project', + }, + { + testName: 'authToken setting precedence', + options: { + autoUploadSourceMaps: true, + authToken: 'root-token', + sourceMapsUploadOptions: { + authToken: 'deprecated-token', + unstable_sentryVitePluginOptions: { + authToken: 'unstable-token', + }, + }, + unstable_sentryVitePluginOptions: { + authToken: 'new-unstable-token', + }, + }, + expectedAuthToken: 'new-unstable-token', + }, + { + testName: 'telemetry setting precedence', + options: { + autoUploadSourceMaps: true, + telemetry: true, + sourceMapsUploadOptions: { + telemetry: false, + unstable_sentryVitePluginOptions: { + telemetry: true, + }, + }, + unstable_sentryVitePluginOptions: { + telemetry: false, + }, + }, + expectedTelemetry: false, + }, + { + testName: 'url setting precedence', + options: { + autoUploadSourceMaps: true, + sentryUrl: 'https://root.sentry.io', + sourceMapsUploadOptions: { + url: 'https://deprecated.sentry.io', + unstable_sentryVitePluginOptions: { + url: 'https://unstable.sentry.io', + }, + }, + unstable_sentryVitePluginOptions: { + url: 'https://new-unstable.sentry.io', + }, + }, + expectedUrl: 'https://new-unstable.sentry.io', + }, + ])( + 'should use correct $testName', + ({ options, expectedOrg, expectedProject, expectedAuthToken, expectedTelemetry, expectedUrl }) => { + const result = generateVitePluginOptions(options as SentrySvelteKitPluginOptions); + + if (expectedOrg !== undefined) { + expect(result?.org).toBe(expectedOrg); + } + if (expectedProject !== undefined) { + expect(result?.project).toBe(expectedProject); + } + if (expectedAuthToken !== undefined) { + expect(result?.authToken).toBe(expectedAuthToken); + } + if (expectedTelemetry !== undefined) { + expect(result?.telemetry).toBe(expectedTelemetry); + } + if (expectedUrl !== undefined) { + expect(result?.url).toBe(expectedUrl); + } + }, + ); + + it('should handle sourcemap settings with correct order of overrides', () => { + const options: SentrySvelteKitPluginOptions = { + autoUploadSourceMaps: true, + sourcemaps: { + assets: ['root/*.js'], + ignore: ['root/ignore/*.js'], + filesToDeleteAfterUpload: ['root/delete/*.js'], + }, + sourceMapsUploadOptions: { + sourcemaps: { + assets: ['deprecated/*.js'], + ignore: ['deprecated/ignore/*.js'], + filesToDeleteAfterUpload: ['deprecated/delete/*.js'], + }, + unstable_sentryVitePluginOptions: { + sourcemaps: { + assets: ['unstable/*.js'], + ignore: ['unstable/ignore/*.js'], + }, + }, + }, + unstable_sentryVitePluginOptions: { + sourcemaps: { + assets: ['new-unstable/*.js'], + filesToDeleteAfterUpload: ['new-unstable/delete/*.js'], + }, + }, + }; + + const result = generateVitePluginOptions(options); + + expect(result?.sourcemaps).toEqual({ + assets: ['new-unstable/*.js'], // new unstable takes precedence + ignore: ['unstable/ignore/*.js'], // from deprecated unstable (not overridden by new unstable) + filesToDeleteAfterUpload: ['new-unstable/delete/*.js'], // new unstable takes precedence + }); + }); + + it('should handle release settings with correct order of overrides', () => { + const newReleaseOptions = { + name: 'root-release', + inject: true, + }; + const newUnstableReleaseOptions = { + name: 'new-unstable-release', + deploy: { + env: 'production', + }, + }; + + const options: SentrySvelteKitPluginOptions = { + autoUploadSourceMaps: true, + release: newReleaseOptions, + sourceMapsUploadOptions: { + release: { + name: 'deprecated-release', + inject: false, + }, + unstable_sentryVitePluginOptions: { + release: { name: 'deprecated-unstable-release', setCommits: { auto: true } }, + }, + }, + unstable_sentryVitePluginOptions: { release: newUnstableReleaseOptions }, + }; + + const result = generateVitePluginOptions(options); + + expect(result?.release).toEqual({ + name: newUnstableReleaseOptions.name, + inject: newReleaseOptions.inject, + setCommits: { + auto: true, // from deprecated unstable (not overridden) + }, + deploy: { + env: 'production', // from new unstable + }, + }); + }); + + it('should handle complex override scenario with all settings', () => { + const options: SentrySvelteKitPluginOptions = { + autoUploadSourceMaps: true, + org: 'root-org', + project: 'root-project', + authToken: 'root-token', + telemetry: true, + sentryUrl: 'https://root.sentry.io', + debug: false, + sourcemaps: { + assets: ['root/*.js'], + }, + release: { + name: 'root-1.0.0', + }, + sourceMapsUploadOptions: { + org: 'deprecated-org', + project: 'deprecated-project', + authToken: 'deprecated-token', + telemetry: false, + url: 'https://deprecated.sentry.io', + sourcemaps: { + assets: ['deprecated/*.js'], + ignore: ['deprecated/ignore/*.js'], + }, + release: { + name: 'deprecated-1.0.0', + inject: false, + }, + unstable_sentryVitePluginOptions: { + org: 'old-unstable-org', + sourcemaps: { + assets: ['old-unstable/*.js'], + }, + }, + }, + unstable_sentryVitePluginOptions: { + org: 'new-unstable-org', + authToken: 'new-unstable-token', + sourcemaps: { + assets: ['new-unstable/*.js'], + filesToDeleteAfterUpload: ['new-unstable/delete/*.js'], + }, + release: { + name: 'new-unstable-1.0.0', + }, + }, + }; + + const result = generateVitePluginOptions(options); + + expect(result).toEqual({ + org: 'new-unstable-org', + project: 'root-project', + authToken: 'new-unstable-token', + telemetry: true, + url: 'https://root.sentry.io', + sourcemaps: { + assets: ['new-unstable/*.js'], + ignore: ['deprecated/ignore/*.js'], + filesToDeleteAfterUpload: ['new-unstable/delete/*.js'], + }, + release: { + name: 'new-unstable-1.0.0', + inject: false, + }, + adapter: undefined, + debug: false, + }); + }); }); From e083772ed6dd674a9ce549a500a5df47fa3491c0 Mon Sep 17 00:00:00 2001 From: Lukas Stracke Date: Fri, 12 Sep 2025 11:08:11 +0200 Subject: [PATCH 24/61] ref(replay-internal): Add mechanism to error caught by `replayIntegration` in debug mode (#17606) Decided to go with `handled: true` since we handle the error but only optionally send it to sentry for debug purposes closes #17265 --- packages/replay-internal/src/util/logger.ts | 7 ++++++- .../test/integration/sendReplayEvent.test.ts | 7 ++++++- .../replay-internal/test/unit/util/logger.test.ts | 14 ++++++++++++-- 3 files changed, 24 insertions(+), 4 deletions(-) diff --git a/packages/replay-internal/src/util/logger.ts b/packages/replay-internal/src/util/logger.ts index 89ded3c69468..dfdd44b18472 100644 --- a/packages/replay-internal/src/util/logger.ts +++ b/packages/replay-internal/src/util/logger.ts @@ -74,7 +74,12 @@ function makeReplayDebugLogger(): ReplayDebugLogger { coreDebug.error(PREFIX, error); if (_capture) { - captureException(error); + captureException(error, { + mechanism: { + handled: true, + type: 'auto.function.replay.debug', + }, + }); } else if (_trace) { // No need for a breadcrumb if `_capture` is enabled since it should be // captured as an exception diff --git a/packages/replay-internal/test/integration/sendReplayEvent.test.ts b/packages/replay-internal/test/integration/sendReplayEvent.test.ts index 3b6c9b5764e1..1e870a8f577b 100644 --- a/packages/replay-internal/test/integration/sendReplayEvent.test.ts +++ b/packages/replay-internal/test/integration/sendReplayEvent.test.ts @@ -399,7 +399,12 @@ describe('Integration | sendReplayEvent', () => { expect(spyHandleException).toHaveBeenCalledTimes(5); const expectedError = new Error('Unable to send Replay - max retries exceeded'); (expectedError as any).cause = new Error('Something bad happened'); - expect(spyHandleException).toHaveBeenLastCalledWith(expectedError); + expect(spyHandleException).toHaveBeenLastCalledWith(expectedError, { + mechanism: { + handled: true, + type: 'auto.function.replay.debug', + }, + }); const spyHandleExceptionCall = spyHandleException.mock.calls; expect(spyHandleExceptionCall[spyHandleExceptionCall.length - 1][0]?.cause.message).toEqual( diff --git a/packages/replay-internal/test/unit/util/logger.test.ts b/packages/replay-internal/test/unit/util/logger.test.ts index a52512dabed8..a3fc85099525 100644 --- a/packages/replay-internal/test/unit/util/logger.test.ts +++ b/packages/replay-internal/test/unit/util/logger.test.ts @@ -52,7 +52,12 @@ describe('logger', () => { const err = new Error('An error'); debug.exception(err, 'a message'); if (captureExceptions) { - expect(mockCaptureException).toHaveBeenCalledWith(err); + expect(mockCaptureException).toHaveBeenCalledWith(err, { + mechanism: { + handled: true, + type: 'auto.function.replay.debug', + }, + }); } expect(mockLogError).toHaveBeenCalledWith('[Replay] ', 'a message'); expect(mockLogError).toHaveBeenLastCalledWith('[Replay] ', err); @@ -75,7 +80,12 @@ describe('logger', () => { const err = new Error('An error'); debug.exception(err); if (captureExceptions) { - expect(mockCaptureException).toHaveBeenCalledWith(err); + expect(mockCaptureException).toHaveBeenCalledWith(err, { + mechanism: { + handled: true, + type: 'auto.function.replay.debug', + }, + }); expect(mockAddBreadcrumb).not.toHaveBeenCalled(); } expect(mockLogError).toHaveBeenCalledTimes(1); From a4ffb1947364b0a93d36f259d9640fdd0bf979ba Mon Sep 17 00:00:00 2001 From: Lukas Stracke Date: Fri, 12 Sep 2025 11:25:43 +0200 Subject: [PATCH 25/61] test(react-router): Unflake `flushIfServerless` test (#17610) This test flaked before because we used real timers and apparently `setTimeout(_, 10)` sometimes caused an absolute delay <10 ms. Let's use fake timers instead which should(?) ensure that this works correctly in the test fixture. closes https://github.com/getsentry/sentry-javascript/issues/17611 --- .../test/server/createSentryHandleError.test.ts | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/packages/react-router/test/server/createSentryHandleError.test.ts b/packages/react-router/test/server/createSentryHandleError.test.ts index 0cad5d2a04ba..dddcf2eba86c 100644 --- a/packages/react-router/test/server/createSentryHandleError.test.ts +++ b/packages/react-router/test/server/createSentryHandleError.test.ts @@ -156,7 +156,8 @@ describe('createSentryHandleError', () => { }); describe('flushIfServerless behavior', () => { - it('should wait for flushIfServerless to complete', async () => { + it('waits for flushIfServerless to complete', async () => { + vi.useFakeTimers(); const handleError = createSentryHandleError({}); let resolveFlush: () => void; @@ -172,7 +173,8 @@ describe('createSentryHandleError', () => { const handleErrorPromise = handleError(mockError, mockArgs); - setTimeout(() => resolveFlush(), 10); + vi.advanceTimersByTime(10); + resolveFlush!(); await handleErrorPromise; const endTime = Date.now(); From 8077201f64ec86e75b9f131e6414d7305564737a Mon Sep 17 00:00:00 2001 From: Lukas Stracke Date: Fri, 12 Sep 2025 11:40:50 +0200 Subject: [PATCH 26/61] ref(aws-severless): Slightly adjust aws-serverless mechanism type (#17614) should have used snake_case instead of kebab-case --- .../test-applications/aws-serverless/tests/layer.test.ts | 4 ++-- packages/aws-serverless/src/integration/awslambda.ts | 2 +- packages/aws-serverless/src/sdk.ts | 8 ++++---- packages/aws-serverless/test/sdk.test.ts | 4 ++-- 4 files changed, 9 insertions(+), 9 deletions(-) diff --git a/dev-packages/e2e-tests/test-applications/aws-serverless/tests/layer.test.ts b/dev-packages/e2e-tests/test-applications/aws-serverless/tests/layer.test.ts index 0439aba5f53c..6ec76124140d 100644 --- a/dev-packages/e2e-tests/test-applications/aws-serverless/tests/layer.test.ts +++ b/dev-packages/e2e-tests/test-applications/aws-serverless/tests/layer.test.ts @@ -160,7 +160,7 @@ test.describe('Lambda layer', () => { type: 'Error', value: 'test', mechanism: { - type: 'auto.function.aws-serverless.otel', + type: 'auto.function.aws_serverless.otel', handled: false, }, }), @@ -188,7 +188,7 @@ test.describe('Lambda layer', () => { type: 'Error', value: 'test esm', mechanism: { - type: 'auto.function.aws-serverless.otel', + type: 'auto.function.aws_serverless.otel', handled: false, }, }), diff --git a/packages/aws-serverless/src/integration/awslambda.ts b/packages/aws-serverless/src/integration/awslambda.ts index c459fc8e25e8..2eaa1fd17354 100644 --- a/packages/aws-serverless/src/integration/awslambda.ts +++ b/packages/aws-serverless/src/integration/awslambda.ts @@ -29,7 +29,7 @@ export const instrumentAwsLambda = generateInstrumentOnce( }, responseHook(_span, { err }) { if (err) { - captureException(err, scope => markEventUnhandled(scope, 'auto.function.aws-serverless.otel')); + captureException(err, scope => markEventUnhandled(scope, 'auto.function.aws_serverless.otel')); } }, }; diff --git a/packages/aws-serverless/src/sdk.ts b/packages/aws-serverless/src/sdk.ts index fd647d3a3376..b7ac8927813c 100644 --- a/packages/aws-serverless/src/sdk.ts +++ b/packages/aws-serverless/src/sdk.ts @@ -235,13 +235,13 @@ export function wrapHandler( if (options.captureAllSettledReasons && Array.isArray(rv) && isPromiseAllSettledResult(rv)) { const reasons = getRejectedReasons(rv); reasons.forEach(exception => { - captureException(exception, scope => markEventUnhandled(scope, 'auto.function.aws-serverless.promise')); + captureException(exception, scope => markEventUnhandled(scope, 'auto.function.aws_serverless.promise')); }); } } catch (e) { // Errors should already captured in the instrumentation's `responseHook`, // we capture them here just to be safe. Double captures are deduplicated by the SDK. - captureException(e, scope => markEventUnhandled(scope, 'auto.function.aws-serverless.handler')); + captureException(e, scope => markEventUnhandled(scope, 'auto.function.aws_serverless.handler')); throw e; } finally { clearTimeout(timeoutWarningTimer); @@ -281,14 +281,14 @@ function wrapStreamingHandler( enhanceScopeWithEnvironmentData(scope, context, startTime); responseStream.on('error', error => { - captureException(error, scope => markEventUnhandled(scope, 'auto.function.aws-serverless.stream')); + captureException(error, scope => markEventUnhandled(scope, 'auto.function.aws_serverless.stream')); }); return await handler(event, responseStream, context); } catch (e) { // Errors should already captured in the instrumentation's `responseHook`, // we capture them here just to be safe. Double captures are deduplicated by the SDK. - captureException(e, scope => markEventUnhandled(scope, 'auto.function.aws-serverless.handler')); + captureException(e, scope => markEventUnhandled(scope, 'auto.function.aws_serverless.handler')); throw e; } finally { if (timeoutWarningTimer) { diff --git a/packages/aws-serverless/test/sdk.test.ts b/packages/aws-serverless/test/sdk.test.ts index 3bf2c42b8fd2..d2047274d744 100644 --- a/packages/aws-serverless/test/sdk.test.ts +++ b/packages/aws-serverless/test/sdk.test.ts @@ -533,7 +533,7 @@ describe('AWSLambda', () => { // @ts-expect-error just mocking around... expect(evtProcessor!(event).exception.values[0]?.mechanism).toEqual({ handled: false, - type: 'auto.function.aws-serverless.handler', + type: 'auto.function.aws_serverless.handler', }); } }); @@ -576,7 +576,7 @@ describe('AWSLambda', () => { // @ts-expect-error just mocking around... expect(evtProcessor(event).exception.values[0]?.mechanism).toEqual({ handled: false, - type: 'auto.function.aws-serverless.handler', + type: 'auto.function.aws_serverless.handler', }); } }); From cf849ab241655612c209263321ef10bf4bc62ec9 Mon Sep 17 00:00:00 2001 From: Lukas Stracke Date: Fri, 12 Sep 2025 12:02:38 +0200 Subject: [PATCH 27/61] ref(solid): Add `mechanism` to error captured by `withSentryErrorBoundary` (#17607) Adds a mechanism following the trace origin naming conventions. Decided to go with `handled: true` since AFAICT, users always have to provide a fallback (as opposed to react ErrorBoundary where this is optional) closes #17264 --- .../tests/errorboundary.test.ts | 6 +++--- .../solid/tests/errorboundary.test.ts | 6 +++--- .../tests/errorboundary.test.ts | 6 +++--- .../tests/errorboundary.test.ts | 6 +++--- .../tests/errorboundary.test.ts | 6 +++--- .../solidstart/tests/errorboundary.test.ts | 6 +++--- packages/solid/src/errorboundary.ts | 7 ++++++- packages/solid/test/errorboundary.test.tsx | 21 ++++++++++++++++--- .../test/client/errorboundary.test.tsx | 21 ++++++++++++++++--- .../test/server/errorboundary.test.tsx | 21 ++++++++++++++++--- 10 files changed, 78 insertions(+), 28 deletions(-) diff --git a/dev-packages/e2e-tests/test-applications/solid-solidrouter/tests/errorboundary.test.ts b/dev-packages/e2e-tests/test-applications/solid-solidrouter/tests/errorboundary.test.ts index 287642424850..14396feb2334 100644 --- a/dev-packages/e2e-tests/test-applications/solid-solidrouter/tests/errorboundary.test.ts +++ b/dev-packages/e2e-tests/test-applications/solid-solidrouter/tests/errorboundary.test.ts @@ -15,7 +15,7 @@ test('captures an exception', async ({ page }) => { type: 'ReferenceError', value: 'NonExistentComponent is not defined', mechanism: { - type: 'generic', + type: 'auto.function.solid.error_boundary', handled: true, }, }, @@ -39,7 +39,7 @@ test('captures a second exception after resetting the boundary', async ({ page } type: 'ReferenceError', value: 'NonExistentComponent is not defined', mechanism: { - type: 'generic', + type: 'auto.function.solid.error_boundary', handled: true, }, }, @@ -64,7 +64,7 @@ test('captures a second exception after resetting the boundary', async ({ page } type: 'ReferenceError', value: 'NonExistentComponent is not defined', mechanism: { - type: 'generic', + type: 'auto.function.solid.error_boundary', handled: true, }, }, diff --git a/dev-packages/e2e-tests/test-applications/solid/tests/errorboundary.test.ts b/dev-packages/e2e-tests/test-applications/solid/tests/errorboundary.test.ts index d192ed9cfcfd..896426e1765c 100644 --- a/dev-packages/e2e-tests/test-applications/solid/tests/errorboundary.test.ts +++ b/dev-packages/e2e-tests/test-applications/solid/tests/errorboundary.test.ts @@ -22,7 +22,7 @@ test('captures an exception', async ({ page }) => { type: 'Error', value: 'Error 1 thrown from Sentry ErrorBoundary in Solid E2E test app', mechanism: { - type: 'generic', + type: 'auto.function.solid.error_boundary', handled: true, }, }, @@ -53,7 +53,7 @@ test('captures a second exception after resetting the boundary', async ({ page } type: 'Error', value: 'Error 1 thrown from Sentry ErrorBoundary in Solid E2E test app', mechanism: { - type: 'generic', + type: 'auto.function.solid.error_boundary', handled: true, }, }, @@ -82,7 +82,7 @@ test('captures a second exception after resetting the boundary', async ({ page } type: 'Error', value: 'Error 2 thrown from Sentry ErrorBoundary in Solid E2E test app', mechanism: { - type: 'generic', + type: 'auto.function.solid.error_boundary', handled: true, }, }, diff --git a/dev-packages/e2e-tests/test-applications/solidstart-dynamic-import/tests/errorboundary.test.ts b/dev-packages/e2e-tests/test-applications/solidstart-dynamic-import/tests/errorboundary.test.ts index 599b5c121455..63b46a5870ec 100644 --- a/dev-packages/e2e-tests/test-applications/solidstart-dynamic-import/tests/errorboundary.test.ts +++ b/dev-packages/e2e-tests/test-applications/solidstart-dynamic-import/tests/errorboundary.test.ts @@ -23,7 +23,7 @@ test('captures an exception', async ({ page }) => { type: 'Error', value: 'Error 1 thrown from Sentry ErrorBoundary in Solid Start E2E test app', mechanism: { - type: 'generic', + type: 'auto.function.solid.error_boundary', handled: true, }, }, @@ -53,7 +53,7 @@ test('captures a second exception after resetting the boundary', async ({ page } type: 'Error', value: 'Error 1 thrown from Sentry ErrorBoundary in Solid Start E2E test app', mechanism: { - type: 'generic', + type: 'auto.function.solid.error_boundary', handled: true, }, }, @@ -81,7 +81,7 @@ test('captures a second exception after resetting the boundary', async ({ page } type: 'Error', value: 'Error 2 thrown from Sentry ErrorBoundary in Solid Start E2E test app', mechanism: { - type: 'generic', + type: 'auto.function.solid.error_boundary', handled: true, }, }, diff --git a/dev-packages/e2e-tests/test-applications/solidstart-spa/tests/errorboundary.test.ts b/dev-packages/e2e-tests/test-applications/solidstart-spa/tests/errorboundary.test.ts index 692e19f54393..4cf6eccd4b71 100644 --- a/dev-packages/e2e-tests/test-applications/solidstart-spa/tests/errorboundary.test.ts +++ b/dev-packages/e2e-tests/test-applications/solidstart-spa/tests/errorboundary.test.ts @@ -21,7 +21,7 @@ test('captures an exception', async ({ page }) => { type: 'Error', value: 'Error 1 thrown from Sentry ErrorBoundary in Solid Start E2E test app', mechanism: { - type: 'generic', + type: 'auto.function.solid.error_boundary', handled: true, }, }, @@ -51,7 +51,7 @@ test('captures a second exception after resetting the boundary', async ({ page } type: 'Error', value: 'Error 1 thrown from Sentry ErrorBoundary in Solid Start E2E test app', mechanism: { - type: 'generic', + type: 'auto.function.solid.error_boundary', handled: true, }, }, @@ -79,7 +79,7 @@ test('captures a second exception after resetting the boundary', async ({ page } type: 'Error', value: 'Error 2 thrown from Sentry ErrorBoundary in Solid Start E2E test app', mechanism: { - type: 'generic', + type: 'auto.function.solid.error_boundary', handled: true, }, }, diff --git a/dev-packages/e2e-tests/test-applications/solidstart-top-level-import/tests/errorboundary.test.ts b/dev-packages/e2e-tests/test-applications/solidstart-top-level-import/tests/errorboundary.test.ts index 49f50f882b50..a362fc76de84 100644 --- a/dev-packages/e2e-tests/test-applications/solidstart-top-level-import/tests/errorboundary.test.ts +++ b/dev-packages/e2e-tests/test-applications/solidstart-top-level-import/tests/errorboundary.test.ts @@ -21,7 +21,7 @@ test('captures an exception', async ({ page }) => { type: 'Error', value: 'Error 1 thrown from Sentry ErrorBoundary in Solid Start E2E test app', mechanism: { - type: 'generic', + type: 'auto.function.solid.error_boundary', handled: true, }, }, @@ -51,7 +51,7 @@ test('captures a second exception after resetting the boundary', async ({ page } type: 'Error', value: 'Error 1 thrown from Sentry ErrorBoundary in Solid Start E2E test app', mechanism: { - type: 'generic', + type: 'auto.function.solid.error_boundary', handled: true, }, }, @@ -79,7 +79,7 @@ test('captures a second exception after resetting the boundary', async ({ page } type: 'Error', value: 'Error 2 thrown from Sentry ErrorBoundary in Solid Start E2E test app', mechanism: { - type: 'generic', + type: 'auto.function.solid.error_boundary', handled: true, }, }, 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 index b709760aab94..9b169d6abda4 100644 --- a/dev-packages/e2e-tests/test-applications/solidstart/tests/errorboundary.test.ts +++ b/dev-packages/e2e-tests/test-applications/solidstart/tests/errorboundary.test.ts @@ -21,7 +21,7 @@ test('captures an exception', async ({ page }) => { type: 'Error', value: 'Error 1 thrown from Sentry ErrorBoundary in Solid Start E2E test app', mechanism: { - type: 'generic', + type: 'auto.function.solid.error_boundary', handled: true, }, }, @@ -51,7 +51,7 @@ test('captures a second exception after resetting the boundary', async ({ page } type: 'Error', value: 'Error 1 thrown from Sentry ErrorBoundary in Solid Start E2E test app', mechanism: { - type: 'generic', + type: 'auto.function.solid.error_boundary', handled: true, }, }, @@ -79,7 +79,7 @@ test('captures a second exception after resetting the boundary', async ({ page } type: 'Error', value: 'Error 2 thrown from Sentry ErrorBoundary in Solid Start E2E test app', mechanism: { - type: 'generic', + type: 'auto.function.solid.error_boundary', handled: true, }, }, diff --git a/packages/solid/src/errorboundary.ts b/packages/solid/src/errorboundary.ts index df75f9da80f9..aaf99c3f23b2 100644 --- a/packages/solid/src/errorboundary.ts +++ b/packages/solid/src/errorboundary.ts @@ -17,7 +17,12 @@ export function withSentryErrorBoundary(ErrorBoundary: Component void): JSX.Element => { - captureException(error); + captureException(error, { + mechanism: { + handled: true, // handled because user has to provide a fallback + type: 'auto.function.solid.error_boundary', + }, + }); const f = local.fallback; return typeof f === 'function' ? f(error, reset) : f; diff --git a/packages/solid/test/errorboundary.test.tsx b/packages/solid/test/errorboundary.test.tsx index 0ba1a2602bfe..53539e9b0d8e 100644 --- a/packages/solid/test/errorboundary.test.tsx +++ b/packages/solid/test/errorboundary.test.tsx @@ -49,7 +49,12 @@ describe('withSentryErrorBoundary', () => { )); expect(mockCaptureException).toHaveBeenCalledTimes(1); - expect(mockCaptureException).toHaveBeenLastCalledWith(new ReferenceError('NonExistentComponent is not defined')); + expect(mockCaptureException).toHaveBeenLastCalledWith(new ReferenceError('NonExistentComponent is not defined'), { + mechanism: { + handled: true, + type: 'auto.function.solid.error_boundary', + }, + }); }); it('renders the fallback component', async () => { @@ -89,13 +94,23 @@ describe('withSentryErrorBoundary', () => { )); expect(mockCaptureException).toHaveBeenCalledTimes(1); - expect(mockCaptureException).toHaveBeenNthCalledWith(1, new ReferenceError('NonExistentComponent is not defined')); + expect(mockCaptureException).toHaveBeenNthCalledWith(1, new ReferenceError('NonExistentComponent is not defined'), { + mechanism: { + handled: true, + type: 'auto.function.solid.error_boundary', + }, + }); const button = await findByRole('button'); await user.click(button); expect(mockCaptureException).toHaveBeenCalledTimes(2); - expect(mockCaptureException).toHaveBeenNthCalledWith(2, new ReferenceError('NonExistentComponent is not defined')); + expect(mockCaptureException).toHaveBeenNthCalledWith(2, new ReferenceError('NonExistentComponent is not defined'), { + mechanism: { + handled: true, + type: 'auto.function.solid.error_boundary', + }, + }); }); it('renders children when there is no error', async () => { diff --git a/packages/solidstart/test/client/errorboundary.test.tsx b/packages/solidstart/test/client/errorboundary.test.tsx index f1fc2c943ac1..099ab1f4bf2a 100644 --- a/packages/solidstart/test/client/errorboundary.test.tsx +++ b/packages/solidstart/test/client/errorboundary.test.tsx @@ -49,7 +49,12 @@ describe('withSentryErrorBoundary', () => { )); expect(mockCaptureException).toHaveBeenCalledTimes(1); - expect(mockCaptureException).toHaveBeenLastCalledWith(new ReferenceError('NonExistentComponent is not defined')); + expect(mockCaptureException).toHaveBeenLastCalledWith(new ReferenceError('NonExistentComponent is not defined'), { + mechanism: { + handled: true, + type: 'auto.function.solid.error_boundary', + }, + }); }); it('renders the fallback component', async () => { @@ -89,13 +94,23 @@ describe('withSentryErrorBoundary', () => { )); expect(mockCaptureException).toHaveBeenCalledTimes(1); - expect(mockCaptureException).toHaveBeenNthCalledWith(1, new ReferenceError('NonExistentComponent is not defined')); + expect(mockCaptureException).toHaveBeenNthCalledWith(1, new ReferenceError('NonExistentComponent is not defined'), { + mechanism: { + handled: true, + type: 'auto.function.solid.error_boundary', + }, + }); const button = await findByRole('button'); await user.click(button); expect(mockCaptureException).toHaveBeenCalledTimes(2); - expect(mockCaptureException).toHaveBeenNthCalledWith(2, new ReferenceError('NonExistentComponent is not defined')); + expect(mockCaptureException).toHaveBeenNthCalledWith(2, new ReferenceError('NonExistentComponent is not defined'), { + mechanism: { + handled: true, + type: 'auto.function.solid.error_boundary', + }, + }); }); it('renders children when there is no error', async () => { diff --git a/packages/solidstart/test/server/errorboundary.test.tsx b/packages/solidstart/test/server/errorboundary.test.tsx index afa2d44e98d7..3ed39bbfea13 100644 --- a/packages/solidstart/test/server/errorboundary.test.tsx +++ b/packages/solidstart/test/server/errorboundary.test.tsx @@ -49,7 +49,12 @@ describe('withSentryErrorBoundary', () => { )); expect(mockCaptureException).toHaveBeenCalledTimes(1); - expect(mockCaptureException).toHaveBeenLastCalledWith(new ReferenceError('NonExistentComponent is not defined')); + expect(mockCaptureException).toHaveBeenLastCalledWith(new ReferenceError('NonExistentComponent is not defined'), { + mechanism: { + handled: true, + type: 'auto.function.solid.error_boundary', + }, + }); }); it('renders the fallback component', async () => { @@ -89,13 +94,23 @@ describe('withSentryErrorBoundary', () => { )); expect(mockCaptureException).toHaveBeenCalledTimes(1); - expect(mockCaptureException).toHaveBeenNthCalledWith(1, new ReferenceError('NonExistentComponent is not defined')); + expect(mockCaptureException).toHaveBeenNthCalledWith(1, new ReferenceError('NonExistentComponent is not defined'), { + mechanism: { + handled: true, + type: 'auto.function.solid.error_boundary', + }, + }); const button = await findByRole('button'); await user.click(button); expect(mockCaptureException).toHaveBeenCalledTimes(2); - expect(mockCaptureException).toHaveBeenNthCalledWith(2, new ReferenceError('NonExistentComponent is not defined')); + expect(mockCaptureException).toHaveBeenNthCalledWith(2, new ReferenceError('NonExistentComponent is not defined'), { + mechanism: { + handled: true, + type: 'auto.function.solid.error_boundary', + }, + }); }); it('renders children when there is no error', async () => { From af5752c8c5d4b98a9732ebb264000f6810b06f64 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 12 Sep 2025 13:05:57 +0200 Subject: [PATCH 28/61] chore(deps): bump next from 14.2.29 to 14.2.32 in /dev-packages/e2e-tests/test-applications/nextjs-orpc (#17494) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Bumps [next](https://github.com/vercel/next.js) from 14.2.29 to 14.2.32.
Release notes

Sourced from next's releases.

v14.2.32

[!NOTE]
This release is backporting bug fixes. It does not include all pending features/changes on canary.

Core Changes

  • fix router handling when setting a location response header #82588

Credits

Huge thanks to @​ztanner for helping!

v14.2.31

[!NOTE]
This release is backporting bug fixes. It does not include all pending features/changes on canary.

Core Changes

  • fix(next/image): improve and simplify detect-content-type (#82179)
  • fix(next/image): fix image-optimizer.ts headers (#82178)

Credits

Huge thanks to @​styfle and @​ztanner for helping!

Commits
  • 89ee561 v14.2.32
  • 6a974ad [backport v14]: fix router handling when setting a location response header (...
  • 55f7662 v14.2.31
  • 5dd68a5 [backport v14]: fix(next/image): improve and simplify detect-content-type (#8...
  • bcc7c65 [backport v14]: fix(next/image): fix image-optimizer.ts headers (#82114) (#82...
  • 243072b v14.2.30
  • f523d4a [backport]: config.allowedDevOrigins (#80410)
  • See full diff in compare view

[![Dependabot compatibility score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=next&package-manager=npm_and_yarn&previous-version=14.2.29&new-version=14.2.32)](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores) Dependabot will resolve any conflicts with this PR as long as you don't alter it yourself. You can also trigger a rebase manually by commenting `@dependabot rebase`. [//]: # (dependabot-automerge-start) [//]: # (dependabot-automerge-end) ---
Dependabot commands and options
You can trigger Dependabot actions by commenting on this PR: - `@dependabot rebase` will rebase this PR - `@dependabot recreate` will recreate this PR, overwriting any edits that have been made to it - `@dependabot merge` will merge this PR after your CI passes on it - `@dependabot squash and merge` will squash and merge this PR after your CI passes on it - `@dependabot cancel merge` will cancel a previously requested merge and block automerging - `@dependabot reopen` will reopen this PR if it is closed - `@dependabot close` will close this PR and stop Dependabot recreating it. You can achieve the same result by closing it manually - `@dependabot show ignore conditions` will show all of the ignore conditions of the specified dependency - `@dependabot ignore this major version` will close this PR and stop Dependabot creating any more for this major version (unless you reopen the PR or upgrade to it yourself) - `@dependabot ignore this minor version` will close this PR and stop Dependabot creating any more for this minor version (unless you reopen the PR or upgrade to it yourself) - `@dependabot ignore this dependency` will close this PR and stop Dependabot creating any more for this dependency (unless you reopen the PR or upgrade to it yourself) You can disable automated security fix PRs for this repo from the [Security Alerts page](https://github.com/getsentry/sentry-javascript/network/alerts).
Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .../e2e-tests/test-applications/nextjs-orpc/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dev-packages/e2e-tests/test-applications/nextjs-orpc/package.json b/dev-packages/e2e-tests/test-applications/nextjs-orpc/package.json index f8c9e58f06d1..0a15260db4e8 100644 --- a/dev-packages/e2e-tests/test-applications/nextjs-orpc/package.json +++ b/dev-packages/e2e-tests/test-applications/nextjs-orpc/package.json @@ -19,7 +19,7 @@ "@sentry/nextjs": "latest || *", "@orpc/server": "latest", "@orpc/client": "latest", - "next": "14.2.29", + "next": "14.2.32", "react": "18.3.1", "react-dom": "18.3.1", "server-only": "^0.0.1" From d67b60d57f7c21210790fdac5092b6ce9d8f6b8a Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 12 Sep 2025 14:21:18 +0200 Subject: [PATCH 29/61] chore(deps): bump next from 14.2.25 to 14.2.32 in /dev-packages/e2e-tests/test-applications/nextjs-pages-dir (#17620) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Bumps [next](https://github.com/vercel/next.js) from 14.2.25 to 14.2.32.
Release notes

Sourced from next's releases.

v14.2.32

[!NOTE]
This release is backporting bug fixes. It does not include all pending features/changes on canary.

Core Changes

  • fix router handling when setting a location response header #82588

Credits

Huge thanks to @​ztanner for helping!

v14.2.31

[!NOTE]
This release is backporting bug fixes. It does not include all pending features/changes on canary.

Core Changes

  • fix(next/image): improve and simplify detect-content-type (#82179)
  • fix(next/image): fix image-optimizer.ts headers (#82178)

Credits

Huge thanks to @​styfle and @​ztanner for helping!

Commits

[![Dependabot compatibility score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=next&package-manager=npm_and_yarn&previous-version=14.2.25&new-version=14.2.32)](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores) Dependabot will resolve any conflicts with this PR as long as you don't alter it yourself. You can also trigger a rebase manually by commenting `@dependabot rebase`. [//]: # (dependabot-automerge-start) [//]: # (dependabot-automerge-end) ---
Dependabot commands and options
You can trigger Dependabot actions by commenting on this PR: - `@dependabot rebase` will rebase this PR - `@dependabot recreate` will recreate this PR, overwriting any edits that have been made to it - `@dependabot merge` will merge this PR after your CI passes on it - `@dependabot squash and merge` will squash and merge this PR after your CI passes on it - `@dependabot cancel merge` will cancel a previously requested merge and block automerging - `@dependabot reopen` will reopen this PR if it is closed - `@dependabot close` will close this PR and stop Dependabot recreating it. You can achieve the same result by closing it manually - `@dependabot show ignore conditions` will show all of the ignore conditions of the specified dependency - `@dependabot ignore this major version` will close this PR and stop Dependabot creating any more for this major version (unless you reopen the PR or upgrade to it yourself) - `@dependabot ignore this minor version` will close this PR and stop Dependabot creating any more for this minor version (unless you reopen the PR or upgrade to it yourself) - `@dependabot ignore this dependency` will close this PR and stop Dependabot creating any more for this dependency (unless you reopen the PR or upgrade to it yourself) You can disable automated security fix PRs for this repo from the [Security Alerts page](https://github.com/getsentry/sentry-javascript/network/alerts).
Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .../e2e-tests/test-applications/nextjs-pages-dir/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dev-packages/e2e-tests/test-applications/nextjs-pages-dir/package.json b/dev-packages/e2e-tests/test-applications/nextjs-pages-dir/package.json index 233ceb802536..0ef1d9bbcac7 100644 --- a/dev-packages/e2e-tests/test-applications/nextjs-pages-dir/package.json +++ b/dev-packages/e2e-tests/test-applications/nextjs-pages-dir/package.json @@ -20,7 +20,7 @@ "@types/node": "^18.19.1", "@types/react": "18.0.26", "@types/react-dom": "18.0.9", - "next": "14.2.25", + "next": "14.2.32", "react": "18.2.0", "react-dom": "18.2.0", "typescript": "~5.0.0" From 9914924a67fc9d4b5c3f49433b48d6afc7c6f17f Mon Sep 17 00:00:00 2001 From: Lukas Stracke Date: Fri, 12 Sep 2025 14:54:50 +0200 Subject: [PATCH 30/61] ref(bun): Adjust `mechanism` of errors captured in Bun.serve (#17616) uses the same mechanism type as the span active when captureException is called closes #17615 --- packages/bun/src/integrations/bunserver.ts | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/packages/bun/src/integrations/bunserver.ts b/packages/bun/src/integrations/bunserver.ts index 9c235b8bc97c..4a079f488474 100644 --- a/packages/bun/src/integrations/bunserver.ts +++ b/packages/bun/src/integrations/bunserver.ts @@ -246,11 +246,8 @@ function wrapRequestHandler( } catch (e) { captureException(e, { mechanism: { - type: 'bun', + type: 'auto.http.bun.serve', handled: false, - data: { - function: 'serve', - }, }, }); throw e; From d5f0ac37f4e61d52de3437a0213dd81ab7f565de Mon Sep 17 00:00:00 2001 From: Lukas Stracke Date: Fri, 12 Sep 2025 14:56:11 +0200 Subject: [PATCH 31/61] ref(core): Adjust MCP server error event `mechanism` (#17622) This PR adjusts the mechanism type we set to be in line with the trace origin naming scheme. ref https://github.com/getsentry/sentry-javascript/issues/17212 --- .../core/src/integrations/mcp-server/errorCapture.ts | 2 +- .../mcp-server/mcpServerErrorCapture.test.ts | 12 ++++++------ 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/packages/core/src/integrations/mcp-server/errorCapture.ts b/packages/core/src/integrations/mcp-server/errorCapture.ts index 544d61cf71ad..2ba401c93d08 100644 --- a/packages/core/src/integrations/mcp-server/errorCapture.ts +++ b/packages/core/src/integrations/mcp-server/errorCapture.ts @@ -36,7 +36,7 @@ export function captureError(error: Error, errorType?: McpErrorType, extraData?: captureException(error, { mechanism: { - type: 'mcp_server', + type: 'auto.ai.mcp_server', handled: false, data: { error_type: errorType || 'handler_execution', diff --git a/packages/core/test/lib/integrations/mcp-server/mcpServerErrorCapture.test.ts b/packages/core/test/lib/integrations/mcp-server/mcpServerErrorCapture.test.ts index bd5af090b0d9..210b303b29bf 100644 --- a/packages/core/test/lib/integrations/mcp-server/mcpServerErrorCapture.test.ts +++ b/packages/core/test/lib/integrations/mcp-server/mcpServerErrorCapture.test.ts @@ -24,7 +24,7 @@ describe('MCP Server Error Capture', () => { expect(captureExceptionSpy).toHaveBeenCalledWith(error, { mechanism: { - type: 'mcp_server', + type: 'auto.ai.mcp_server', handled: false, data: { error_type: 'tool_execution', @@ -40,7 +40,7 @@ describe('MCP Server Error Capture', () => { expect(captureExceptionSpy).toHaveBeenCalledWith(error, { mechanism: { - type: 'mcp_server', + type: 'auto.ai.mcp_server', handled: false, data: { error_type: 'transport', @@ -56,7 +56,7 @@ describe('MCP Server Error Capture', () => { expect(captureExceptionSpy).toHaveBeenCalledWith(error, { mechanism: { - type: 'mcp_server', + type: 'auto.ai.mcp_server', handled: false, data: { error_type: 'protocol', @@ -72,7 +72,7 @@ describe('MCP Server Error Capture', () => { expect(captureExceptionSpy).toHaveBeenCalledWith(error, { mechanism: { - type: 'mcp_server', + type: 'auto.ai.mcp_server', handled: false, data: { error_type: 'validation', @@ -88,7 +88,7 @@ describe('MCP Server Error Capture', () => { expect(captureExceptionSpy).toHaveBeenCalledWith(error, { mechanism: { - type: 'mcp_server', + type: 'auto.ai.mcp_server', handled: false, data: { error_type: 'timeout', @@ -104,7 +104,7 @@ describe('MCP Server Error Capture', () => { expect(captureExceptionSpy).toHaveBeenCalledWith(error, { mechanism: { - type: 'mcp_server', + type: 'auto.ai.mcp_server', handled: false, data: { error_type: 'tool_execution', From 6e11be974a34afa45c94147f0233b2316166a573 Mon Sep 17 00:00:00 2001 From: Lukas Stracke Date: Fri, 12 Sep 2025 16:44:52 +0200 Subject: [PATCH 32/61] ref(nextjs): Set more specific event `mechanism`s (#17543) exception mechanism now follows the trace origin naming scheme. Also added a couple of test assertions for tests expecting errors. closes #17258 --- .../nextjs-13/tests/client/click-error.test.ts | 12 +++++++++++- .../tests/server/getServerSideProps.test.ts | 4 ++-- .../tests/server/pages-router-api-endpoints.test.ts | 2 +- .../tests/server/server-component-error.test.ts | 2 +- .../nextjs-15/tests/nested-rsc-error.test.ts | 5 +++++ .../nextjs-15/tests/streaming-rsc-error.test.ts | 5 +++++ .../nextjs-app-dir/tests/client-errors.test.ts | 11 +++++++++++ .../nextjs-app-dir/tests/edge.test.ts | 5 +++++ .../nextjs-orpc/tests/orpc-error.test.ts | 6 ++++++ .../nextjs-pages-dir/tests/pages-ssr-errors.test.ts | 5 +++++ .../nextjs-t3/tests/trpc-error.test.ts | 8 ++++++++ .../nextjs-turbo/tests/app-router/rsc-error.test.ts | 4 ++++ packages/nextjs/src/common/captureRequestError.ts | 1 + .../common/pages-router-instrumentation/_error.ts | 2 +- .../wrapApiHandlerWithSentry.ts | 2 +- .../wrapPageComponentWithSentry.ts | 2 ++ packages/nextjs/src/common/utils/wrapperUtils.ts | 8 ++++++-- .../src/common/withServerActionInstrumentation.ts | 3 +++ .../src/common/wrapGenerationFunctionWithSentry.ts | 4 ++++ .../nextjs/src/common/wrapMiddlewareWithSentry.ts | 4 ++-- .../nextjs/src/common/wrapRouteHandlerWithSentry.ts | 1 + .../src/common/wrapServerComponentWithSentry.ts | 3 ++- packages/nextjs/src/edge/wrapApiHandlerWithSentry.ts | 4 ++-- 23 files changed, 89 insertions(+), 14 deletions(-) diff --git a/dev-packages/e2e-tests/test-applications/nextjs-13/tests/client/click-error.test.ts b/dev-packages/e2e-tests/test-applications/nextjs-13/tests/client/click-error.test.ts index a9fbcdb69a45..481e7264c62d 100644 --- a/dev-packages/e2e-tests/test-applications/nextjs-13/tests/client/click-error.test.ts +++ b/dev-packages/e2e-tests/test-applications/nextjs-13/tests/client/click-error.test.ts @@ -13,8 +13,18 @@ test('should send error for faulty click handlers', async ({ page }) => { expect(errorEvent).toBeDefined(); - const frames = errorEvent?.exception?.values?.[0]?.stacktrace?.frames; + const exception = errorEvent?.exception?.values?.[0]; + + expect(exception?.mechanism).toEqual({ + type: 'auto.browser.browserapierrors.addEventListener', + handled: false, + data: { + handler: expect.any(String), // the handler name varies in CI and locally + target: 'EventTarget', + }, + }); + const frames = exception?.stacktrace?.frames; await test.step('error should have a non-url-encoded top frame in route with parameter', () => { if (process.env.TEST_ENV === 'development') { // In dev mode we want to check local source mapping diff --git a/dev-packages/e2e-tests/test-applications/nextjs-13/tests/server/getServerSideProps.test.ts b/dev-packages/e2e-tests/test-applications/nextjs-13/tests/server/getServerSideProps.test.ts index 60f9842ca20e..3338d8e28595 100644 --- a/dev-packages/e2e-tests/test-applications/nextjs-13/tests/server/getServerSideProps.test.ts +++ b/dev-packages/e2e-tests/test-applications/nextjs-13/tests/server/getServerSideProps.test.ts @@ -23,7 +23,7 @@ test('Should report an error event for errors thrown in getServerSideProps', asy exception: { values: [ { - mechanism: { handled: false, type: 'generic' }, + mechanism: { handled: false, type: 'auto.function.nextjs.wrapped' }, type: 'Error', value: 'getServerSideProps Error', stacktrace: { @@ -110,7 +110,7 @@ test('Should report an error event for errors thrown in getServerSideProps in pa exception: { values: [ { - mechanism: { handled: false, type: 'generic' }, + mechanism: { handled: false, type: 'auto.function.nextjs.wrapped' }, type: 'Error', value: 'custom page extension error', stacktrace: { diff --git a/dev-packages/e2e-tests/test-applications/nextjs-13/tests/server/pages-router-api-endpoints.test.ts b/dev-packages/e2e-tests/test-applications/nextjs-13/tests/server/pages-router-api-endpoints.test.ts index 36592b56fb94..9f5ff5db8434 100644 --- a/dev-packages/e2e-tests/test-applications/nextjs-13/tests/server/pages-router-api-endpoints.test.ts +++ b/dev-packages/e2e-tests/test-applications/nextjs-13/tests/server/pages-router-api-endpoints.test.ts @@ -30,7 +30,7 @@ test('Should report an error event for errors thrown in pages router api routes' function: 'withSentry', }, handled: false, - type: 'instrument', + type: 'auto.http.nextjs.api_handler', }, stacktrace: { frames: expect.arrayContaining([]) }, type: 'Error', diff --git a/dev-packages/e2e-tests/test-applications/nextjs-13/tests/server/server-component-error.test.ts b/dev-packages/e2e-tests/test-applications/nextjs-13/tests/server/server-component-error.test.ts index 937d7c8da6c0..c485c02716a3 100644 --- a/dev-packages/e2e-tests/test-applications/nextjs-13/tests/server/server-component-error.test.ts +++ b/dev-packages/e2e-tests/test-applications/nextjs-13/tests/server/server-component-error.test.ts @@ -20,7 +20,7 @@ test('Should capture an error thrown in a server component', async ({ page }) => exception: { values: [ { - mechanism: { handled: false, type: 'generic' }, + mechanism: { handled: false, type: 'auto.function.nextjs.server_component' }, type: 'Error', value: 'RSC error', }, 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 index 863e5de111a2..68d03a00d601 100644 --- 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 @@ -41,4 +41,9 @@ test('Should capture errors from nested server components when `Sentry.captureRe router_path: '/nested-rsc-error/[param]', request_path: '/nested-rsc-error/123', }); + + expect(errorEvent.exception?.values?.[0]?.mechanism).toEqual({ + handled: false, + type: 'auto.function.nextjs.on_request_error', + }); }); 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 index 0a20e97be74a..6133d0fa29b1 100644 --- 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 @@ -41,4 +41,9 @@ test('Should capture errors for crashing streaming promises in server components router_path: '/streaming-rsc-error/[param]', request_path: '/streaming-rsc-error/123', }); + + expect(errorEvent.exception?.values?.[0]?.mechanism).toEqual({ + handled: false, + type: 'auto.function.nextjs.on_request_error', + }); }); diff --git a/dev-packages/e2e-tests/test-applications/nextjs-app-dir/tests/client-errors.test.ts b/dev-packages/e2e-tests/test-applications/nextjs-app-dir/tests/client-errors.test.ts index 091c8fc1e46c..580368f4b9a1 100644 --- a/dev-packages/e2e-tests/test-applications/nextjs-app-dir/tests/client-errors.test.ts +++ b/dev-packages/e2e-tests/test-applications/nextjs-app-dir/tests/client-errors.test.ts @@ -35,4 +35,15 @@ test('Sends a client-side exception to Sentry', async ({ page }) => { trace_id: expect.stringMatching(/[a-f0-9]{32}/), span_id: expect.stringMatching(/[a-f0-9]{16}/), }); + + expect(errorEvent.exception?.values?.[0]?.mechanism).toEqual({ + handled: false, + type: nextjsMajor >= 15 ? 'auto.browser.global_handlers.onerror' : 'auto.browser.browserapierrors.addEventListener', + ...(nextjsMajor < 15 && { + data: { + handler: expect.any(String), // the handler name varies in CI and locally + target: 'EventTarget', + }, + }), + }); }); diff --git a/dev-packages/e2e-tests/test-applications/nextjs-app-dir/tests/edge.test.ts b/dev-packages/e2e-tests/test-applications/nextjs-app-dir/tests/edge.test.ts index 934cfa2e472d..a19fe3402951 100644 --- a/dev-packages/e2e-tests/test-applications/nextjs-app-dir/tests/edge.test.ts +++ b/dev-packages/e2e-tests/test-applications/nextjs-app-dir/tests/edge.test.ts @@ -19,6 +19,11 @@ test('Should record exceptions for faulty edge server components', async ({ page expect(errorEvent.tags?.['my-global-scope-isolated-tag']).not.toBeDefined(); expect(errorEvent.transaction).toBe(`Page Server Component (/edge-server-components/error)`); + + expect(errorEvent.exception?.values?.[0]?.mechanism).toEqual({ + handled: false, + type: 'auto.function.nextjs.server_component', + }); }); test('Should record transaction for edge server components', async ({ page }) => { diff --git a/dev-packages/e2e-tests/test-applications/nextjs-orpc/tests/orpc-error.test.ts b/dev-packages/e2e-tests/test-applications/nextjs-orpc/tests/orpc-error.test.ts index 8a9f371972c0..a503533b6f00 100644 --- a/dev-packages/e2e-tests/test-applications/nextjs-orpc/tests/orpc-error.test.ts +++ b/dev-packages/e2e-tests/test-applications/nextjs-orpc/tests/orpc-error.test.ts @@ -19,4 +19,10 @@ test('should capture orpc error', async ({ page }) => { }), ], }); + + // orpc errors are captured manually by the orpc middleware (user-land) + expect(orpcError.exception?.values?.[0]?.mechanism).toEqual({ + handled: true, + type: 'generic', + }); }); diff --git a/dev-packages/e2e-tests/test-applications/nextjs-pages-dir/tests/pages-ssr-errors.test.ts b/dev-packages/e2e-tests/test-applications/nextjs-pages-dir/tests/pages-ssr-errors.test.ts index c3925f52ba48..e5539244570f 100644 --- a/dev-packages/e2e-tests/test-applications/nextjs-pages-dir/tests/pages-ssr-errors.test.ts +++ b/dev-packages/e2e-tests/test-applications/nextjs-pages-dir/tests/pages-ssr-errors.test.ts @@ -43,4 +43,9 @@ test('Will capture error for SSR rendering error with a connected trace (Functio // TODO(lforst): Reuse SSR request span isolation scope to fix the following two assertions // expect(ssrTransaction.tags?.['my-isolated-tag']).toBe(true); // expect(ssrTransaction.tags?.['my-global-scope-isolated-tag']).not.toBeDefined(); + + expect(errorEvent.exception?.values?.[0]?.mechanism).toEqual({ + handled: false, + type: 'auto.function.nextjs.page_function', + }); }); diff --git a/dev-packages/e2e-tests/test-applications/nextjs-t3/tests/trpc-error.test.ts b/dev-packages/e2e-tests/test-applications/nextjs-t3/tests/trpc-error.test.ts index d3e175e0558b..2a7af1b14d52 100644 --- a/dev-packages/e2e-tests/test-applications/nextjs-t3/tests/trpc-error.test.ts +++ b/dev-packages/e2e-tests/test-applications/nextjs-t3/tests/trpc-error.test.ts @@ -16,6 +16,14 @@ test('should capture error with trpc context', async ({ page }) => { expect(trpcError.contexts?.trpc?.procedure_type).toEqual('mutation'); expect(trpcError.contexts?.trpc?.procedure_path).toBe('post.throwError'); expect(trpcError.contexts?.trpc?.input).toEqual({ name: 'I love dogs' }); + + expect(trpcError.exception?.values?.[0]?.mechanism).toEqual({ + handled: false, + type: 'auto.rpc.trpc.middleware', + exception_id: 1, + parent_id: 0, + source: 'cause', + }); }); test('should create transaction with trpc input for error', async ({ page }) => { diff --git a/dev-packages/e2e-tests/test-applications/nextjs-turbo/tests/app-router/rsc-error.test.ts b/dev-packages/e2e-tests/test-applications/nextjs-turbo/tests/app-router/rsc-error.test.ts index 604faae7ea59..d9c94de2d2be 100644 --- a/dev-packages/e2e-tests/test-applications/nextjs-turbo/tests/app-router/rsc-error.test.ts +++ b/dev-packages/e2e-tests/test-applications/nextjs-turbo/tests/app-router/rsc-error.test.ts @@ -11,4 +11,8 @@ test('Should capture errors from server components', async ({ page }) => { const errorEvent = await errorEventPromise; expect(errorEvent).toBeDefined(); + expect(errorEvent.exception?.values?.[0]?.mechanism).toEqual({ + handled: false, + type: 'auto.function.nextjs.on_request_error', + }); }); diff --git a/packages/nextjs/src/common/captureRequestError.ts b/packages/nextjs/src/common/captureRequestError.ts index fec9d46d0e65..6fd2e83d3188 100644 --- a/packages/nextjs/src/common/captureRequestError.ts +++ b/packages/nextjs/src/common/captureRequestError.ts @@ -38,6 +38,7 @@ export function captureRequestError(error: unknown, request: RequestInfo, errorC captureException(error, { mechanism: { handled: false, + type: 'auto.function.nextjs.on_request_error', }, }); diff --git a/packages/nextjs/src/common/pages-router-instrumentation/_error.ts b/packages/nextjs/src/common/pages-router-instrumentation/_error.ts index b33c648839fa..354fbe0438e2 100644 --- a/packages/nextjs/src/common/pages-router-instrumentation/_error.ts +++ b/packages/nextjs/src/common/pages-router-instrumentation/_error.ts @@ -45,7 +45,7 @@ export async function captureUnderscoreErrorException(contextOrProps: ContextOrP // is what passing a string to `captureException` will wind up doing) captureException(err || `_error.js called with falsy error (${err})`, { mechanism: { - type: 'instrument', + type: 'auto.function.nextjs.underscore_error', handled: false, data: { function: '_error.getInitialProps', diff --git a/packages/nextjs/src/common/pages-router-instrumentation/wrapApiHandlerWithSentry.ts b/packages/nextjs/src/common/pages-router-instrumentation/wrapApiHandlerWithSentry.ts index a0e4112404cd..4cf8fde751fb 100644 --- a/packages/nextjs/src/common/pages-router-instrumentation/wrapApiHandlerWithSentry.ts +++ b/packages/nextjs/src/common/pages-router-instrumentation/wrapApiHandlerWithSentry.ts @@ -112,7 +112,7 @@ export function wrapApiHandlerWithSentry(apiHandler: NextApiHandler, parameteriz captureException(objectifiedErr, { mechanism: { - type: 'instrument', + type: 'auto.http.nextjs.api_handler', handled: false, data: { wrapped_handler: wrappingTarget.name, diff --git a/packages/nextjs/src/common/pages-router-instrumentation/wrapPageComponentWithSentry.ts b/packages/nextjs/src/common/pages-router-instrumentation/wrapPageComponentWithSentry.ts index b08bdad5e9ab..693341024726 100644 --- a/packages/nextjs/src/common/pages-router-instrumentation/wrapPageComponentWithSentry.ts +++ b/packages/nextjs/src/common/pages-router-instrumentation/wrapPageComponentWithSentry.ts @@ -48,6 +48,7 @@ export function wrapPageComponentWithSentry(pageComponent: FunctionComponent | C captureException(e, { mechanism: { handled: false, + type: 'auto.function.nextjs.page_class', }, }); throw e; @@ -77,6 +78,7 @@ export function wrapPageComponentWithSentry(pageComponent: FunctionComponent | C captureException(e, { mechanism: { handled: false, + type: 'auto.function.nextjs.page_function', }, }); throw e; diff --git a/packages/nextjs/src/common/utils/wrapperUtils.ts b/packages/nextjs/src/common/utils/wrapperUtils.ts index 23b960c857cf..5bde1f76f0d4 100644 --- a/packages/nextjs/src/common/utils/wrapperUtils.ts +++ b/packages/nextjs/src/common/utils/wrapperUtils.ts @@ -25,7 +25,11 @@ export function withErrorInstrumentation any>( return await origFunction.apply(this, origFunctionArguments); } catch (e) { // TODO: Extract error logic from `withSentry` in here or create a new wrapper with said logic or something like that. - captureException(e, { mechanism: { handled: false } }); + captureException(e, { + // TODO: check if origFunction.name actually returns the correct name or minified garbage + // in this case, we can add another argument to this wrapper with the respective function name + mechanism: { handled: false, type: 'auto.function.nextjs.wrapped', data: { function: origFunction.name } }, + }); throw e; } }; @@ -99,7 +103,7 @@ export async function callDataFetcherTraced Promis try { return await origFunction(...origFunctionArgs); } catch (e) { - captureException(e, { mechanism: { handled: false } }); + captureException(e, { mechanism: { handled: false, type: 'auto.function.nextjs.data_fetcher' } }); throw e; } } diff --git a/packages/nextjs/src/common/withServerActionInstrumentation.ts b/packages/nextjs/src/common/withServerActionInstrumentation.ts index e3cc2831d5e4..f5a1170cc44b 100644 --- a/packages/nextjs/src/common/withServerActionInstrumentation.ts +++ b/packages/nextjs/src/common/withServerActionInstrumentation.ts @@ -7,6 +7,7 @@ import { getClient, getIsolationScope, handleCallbackErrors, + SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN, SEMANTIC_ATTRIBUTE_SENTRY_SOURCE, SPAN_STATUS_ERROR, startSpan, @@ -116,6 +117,7 @@ async function withServerActionInstrumentationImplementation { @@ -130,6 +132,7 @@ async function withServerActionInstrumentationImplementation a captureException(err, { mechanism: { handled: false, + type: 'auto.function.nextjs.generation_function', + data: { + function: generationFunctionIdentifier, + }, }, }); } diff --git a/packages/nextjs/src/common/wrapMiddlewareWithSentry.ts b/packages/nextjs/src/common/wrapMiddlewareWithSentry.ts index bd84fb4195b7..ab05fbd5e944 100644 --- a/packages/nextjs/src/common/wrapMiddlewareWithSentry.ts +++ b/packages/nextjs/src/common/wrapMiddlewareWithSentry.ts @@ -98,7 +98,7 @@ export function wrapMiddlewareWithSentry( op: 'http.server.middleware', attributes: { [SEMANTIC_ATTRIBUTE_SENTRY_SOURCE]: spanSource, - [SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: 'auto.function.nextjs.wrapMiddlewareWithSentry', + [SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: 'auto.function.nextjs.wrap_middleware', ...headerAttributes, }, }, @@ -108,7 +108,7 @@ export function wrapMiddlewareWithSentry( error => { captureException(error, { mechanism: { - type: 'instrument', + type: 'auto.function.nextjs.wrap_middleware', handled: false, }, }); diff --git a/packages/nextjs/src/common/wrapRouteHandlerWithSentry.ts b/packages/nextjs/src/common/wrapRouteHandlerWithSentry.ts index e10d51321a0e..54858a9bdae2 100644 --- a/packages/nextjs/src/common/wrapRouteHandlerWithSentry.ts +++ b/packages/nextjs/src/common/wrapRouteHandlerWithSentry.ts @@ -96,6 +96,7 @@ export function wrapRouteHandlerWithSentry any>( captureException(error, { mechanism: { handled: false, + type: 'auto.function.nextjs.route_handler', }, }); } diff --git a/packages/nextjs/src/common/wrapServerComponentWithSentry.ts b/packages/nextjs/src/common/wrapServerComponentWithSentry.ts index f0f8e9df8717..d25225a149f9 100644 --- a/packages/nextjs/src/common/wrapServerComponentWithSentry.ts +++ b/packages/nextjs/src/common/wrapServerComponentWithSentry.ts @@ -114,7 +114,7 @@ export function wrapServerComponentWithSentry any> name: `${componentType} Server Component (${componentRoute})`, attributes: { [SEMANTIC_ATTRIBUTE_SENTRY_SOURCE]: 'component', - [SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: 'auto.function.nextjs', + [SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: 'auto.function.nextjs.server_component', 'sentry.nextjs.ssr.function.type': componentType, 'sentry.nextjs.ssr.function.route': componentRoute, }, @@ -136,6 +136,7 @@ export function wrapServerComponentWithSentry any> captureException(error, { mechanism: { handled: false, + type: 'auto.function.nextjs.server_component', }, }); } diff --git a/packages/nextjs/src/edge/wrapApiHandlerWithSentry.ts b/packages/nextjs/src/edge/wrapApiHandlerWithSentry.ts index 6002981cfcf4..735caf8d7788 100644 --- a/packages/nextjs/src/edge/wrapApiHandlerWithSentry.ts +++ b/packages/nextjs/src/edge/wrapApiHandlerWithSentry.ts @@ -79,7 +79,7 @@ export function wrapApiHandlerWithSentry( op: op, attributes: { [SEMANTIC_ATTRIBUTE_SENTRY_SOURCE]: 'route', - [SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: 'auto.function.nextjs.wrapApiHandlerWithSentry', + [SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: 'auto.function.nextjs.wrap_api_handler', ...headerAttributes, }, }, @@ -89,7 +89,7 @@ export function wrapApiHandlerWithSentry( error => { captureException(error, { mechanism: { - type: 'instrument', + type: 'auto.function.nextjs.wrap_api_handler', handled: false, }, }); From 5a3d572c0d20e9d5066a433450d939b2511efe66 Mon Sep 17 00:00:00 2001 From: Abhijeet Prasad Date: Fri, 12 Sep 2025 10:51:05 -0400 Subject: [PATCH 33/61] feat(core): Add Consola integration (#17435) resolves https://github.com/getsentry/sentry-javascript/issues/16659 This PR adds support for [`consola`](https://www.npmjs.com/package/consola) so that we can send logs from consola to Sentry. ```ts // importing from browser also works - consola is isomorphic import * as Sentry from "@sentry/node"; // Create a Sentry reporter for consola const sentryReporter = Sentry.createConsolaReporter(); // Add the reporter to consola consola.addReporter(sentryReporter); // Test with arguments formatting consola.info('Message with args:', 'hello', 123, { key: 'value' }, [1, 2, 3]); consola.error('Error with object:', new Error('Test error')); ``` --- .../node-integration-tests/package.json | 1 + .../suites/consola/subject-args.ts | 37 ++ .../suites/consola/subject-custom-levels.ts | 30 ++ .../suites/consola/subject-levels.ts | 32 ++ .../suites/consola/subject-tags.ts | 32 ++ .../suites/consola/subject-types.ts | 42 ++ .../suites/consola/subject.ts | 32 ++ .../suites/consola/test.ts | 494 ++++++++++++++++++ packages/astro/src/index.server.ts | 1 + packages/aws-serverless/src/index.ts | 1 + packages/browser/src/index.ts | 1 + packages/bun/src/index.ts | 1 + packages/cloudflare/src/index.ts | 1 + packages/core/src/index.ts | 1 + packages/core/src/integrations/consola.ts | 312 +++++++++++ packages/core/src/logs/console-integration.ts | 24 +- packages/core/src/logs/utils.ts | 39 ++ .../test/lib/integrations/consola.test.ts | 303 +++++++++++ packages/google-cloud-serverless/src/index.ts | 1 + packages/node-core/src/index.ts | 1 + packages/node/src/index.ts | 1 + packages/remix/src/server/index.ts | 1 + packages/solidstart/src/server/index.ts | 1 + packages/sveltekit/src/server/index.ts | 1 + packages/vercel-edge/src/index.ts | 1 + 25 files changed, 1368 insertions(+), 23 deletions(-) create mode 100644 dev-packages/node-integration-tests/suites/consola/subject-args.ts create mode 100644 dev-packages/node-integration-tests/suites/consola/subject-custom-levels.ts create mode 100644 dev-packages/node-integration-tests/suites/consola/subject-levels.ts create mode 100644 dev-packages/node-integration-tests/suites/consola/subject-tags.ts create mode 100644 dev-packages/node-integration-tests/suites/consola/subject-types.ts create mode 100644 dev-packages/node-integration-tests/suites/consola/subject.ts create mode 100644 dev-packages/node-integration-tests/suites/consola/test.ts create mode 100644 packages/core/src/integrations/consola.ts create mode 100644 packages/core/src/logs/utils.ts create mode 100644 packages/core/test/lib/integrations/consola.test.ts diff --git a/dev-packages/node-integration-tests/package.json b/dev-packages/node-integration-tests/package.json index 013761ff78a8..799338890030 100644 --- a/dev-packages/node-integration-tests/package.json +++ b/dev-packages/node-integration-tests/package.json @@ -39,6 +39,7 @@ "apollo-server": "^3.11.1", "body-parser": "^1.20.3", "connect": "^3.7.0", + "consola": "^3.2.3", "cors": "^2.8.5", "cron": "^3.1.6", "dataloader": "2.2.2", diff --git a/dev-packages/node-integration-tests/suites/consola/subject-args.ts b/dev-packages/node-integration-tests/suites/consola/subject-args.ts new file mode 100644 index 000000000000..11b39d1588cb --- /dev/null +++ b/dev-packages/node-integration-tests/suites/consola/subject-args.ts @@ -0,0 +1,37 @@ +import * as Sentry from '@sentry/node'; +import { loggingTransport } from '@sentry-internal/node-integration-tests'; +import { consola } from 'consola'; + +Sentry.init({ + dsn: 'https://public@dsn.ingest.sentry.io/1337', + release: '1.0.0', + environment: 'test', + enableLogs: true, + transport: loggingTransport, +}); + +async function run(): Promise { + // Set consola level to capture all logs including debug and trace + consola.level = 5; + + // Create a Sentry reporter for consola + const sentryReporter = Sentry.createConsolaReporter(); + + // Add the reporter to consola + consola.addReporter(sentryReporter); + + // Test with arguments formatting + consola.info('Message with args:', 'hello', 123, { key: 'value' }, [1, 2, 3]); + consola.log({ + type: 'debug', + message: 'Debug message', + userId: 12345, + sessionId: 'abc-123-def', + customData: { nested: 'value', count: 42 }, + }); + + await Sentry.flush(); +} + +// eslint-disable-next-line @typescript-eslint/no-floating-promises +void run(); diff --git a/dev-packages/node-integration-tests/suites/consola/subject-custom-levels.ts b/dev-packages/node-integration-tests/suites/consola/subject-custom-levels.ts new file mode 100644 index 000000000000..3f3690f7d110 --- /dev/null +++ b/dev-packages/node-integration-tests/suites/consola/subject-custom-levels.ts @@ -0,0 +1,30 @@ +import * as Sentry from '@sentry/node'; +import { loggingTransport } from '@sentry-internal/node-integration-tests'; +import { consola } from 'consola'; + +Sentry.init({ + dsn: 'https://public@dsn.ingest.sentry.io/1337', + release: '1.0.0', + environment: 'test', + enableLogs: true, + transport: loggingTransport, +}); + +async function run(): Promise { + // Test custom levels filtering + const customReporter = Sentry.createConsolaReporter({ + levels: ['error', 'warn'], // Only capture errors and warnings + }); + + // Add the custom reporter to consola + consola.addReporter(customReporter); + + consola.info('This should not be captured'); + consola.warn('This should be captured'); + consola.error('This should also be captured'); + + await Sentry.flush(); +} + +// eslint-disable-next-line @typescript-eslint/no-floating-promises +void run(); diff --git a/dev-packages/node-integration-tests/suites/consola/subject-levels.ts b/dev-packages/node-integration-tests/suites/consola/subject-levels.ts new file mode 100644 index 000000000000..02052febfb65 --- /dev/null +++ b/dev-packages/node-integration-tests/suites/consola/subject-levels.ts @@ -0,0 +1,32 @@ +import * as Sentry from '@sentry/node'; +import { loggingTransport } from '@sentry-internal/node-integration-tests'; +import { consola } from 'consola'; + +Sentry.init({ + dsn: 'https://public@dsn.ingest.sentry.io/1337', + release: '1.0.0', + environment: 'test', + enableLogs: true, + transport: loggingTransport, +}); + +async function run(): Promise { + // Set consola level to capture all logs including debug and trace + consola.level = 5; + + // Create a Sentry reporter for consola + const sentryReporter = Sentry.createConsolaReporter(); + + // Add the reporter to consola + consola.addReporter(sentryReporter); + + // Test level-based logging - test some basic level mappings by using different log methods + consola.fatal('Fatal level message'); + consola.warn('Warning level message'); + consola.info('Info level message'); + + await Sentry.flush(); +} + +// eslint-disable-next-line @typescript-eslint/no-floating-promises +void run(); diff --git a/dev-packages/node-integration-tests/suites/consola/subject-tags.ts b/dev-packages/node-integration-tests/suites/consola/subject-tags.ts new file mode 100644 index 000000000000..ca458a68a068 --- /dev/null +++ b/dev-packages/node-integration-tests/suites/consola/subject-tags.ts @@ -0,0 +1,32 @@ +import * as Sentry from '@sentry/node'; +import { loggingTransport } from '@sentry-internal/node-integration-tests'; +import { consola } from 'consola'; + +Sentry.init({ + dsn: 'https://public@dsn.ingest.sentry.io/1337', + release: '1.0.0', + environment: 'test', + enableLogs: true, + transport: loggingTransport, +}); + +async function run(): Promise { + // Set consola level to capture all logs including debug and trace + consola.level = 5; + + // Create a Sentry reporter for consola + const sentryReporter = Sentry.createConsolaReporter(); + + // Add the reporter to consola + consola.addReporter(sentryReporter); + + // Test with scoped logger (tags) + const taggedLogger = consola.withTag('api'); + taggedLogger.info('Tagged info message'); + taggedLogger.error('Tagged error message'); + + await Sentry.flush(); +} + +// eslint-disable-next-line @typescript-eslint/no-floating-promises +void run(); diff --git a/dev-packages/node-integration-tests/suites/consola/subject-types.ts b/dev-packages/node-integration-tests/suites/consola/subject-types.ts new file mode 100644 index 000000000000..fc63c38cf614 --- /dev/null +++ b/dev-packages/node-integration-tests/suites/consola/subject-types.ts @@ -0,0 +1,42 @@ +import * as Sentry from '@sentry/node'; +import { loggingTransport } from '@sentry-internal/node-integration-tests'; +import { consola } from 'consola'; + +Sentry.init({ + dsn: 'https://public@dsn.ingest.sentry.io/1337', + release: '1.0.0', + environment: 'test', + enableLogs: true, + transport: loggingTransport, +}); + +async function run(): Promise { + // Set consola level to capture all logs including debug, trace, and verbose + consola.level = Number.POSITIVE_INFINITY; + + // Create a Sentry reporter for consola + const sentryReporter = Sentry.createConsolaReporter(); + + // Add the reporter to consola + consola.addReporter(sentryReporter); + + // Test basic logging with different types + consola.info('Test info message'); + consola.error('Test error message'); + consola.warn('Test warn message'); + + // Test different consola log types + consola.success('Test success message'); + consola.fail('Test fail message'); + consola.ready('Test ready message'); + consola.start('Test start message'); + consola.box('Test box message'); + consola.verbose('Test verbose message'); + consola.debug('Test debug message'); + consola.trace('Test trace message'); + + await Sentry.flush(); +} + +// eslint-disable-next-line @typescript-eslint/no-floating-promises +void run(); diff --git a/dev-packages/node-integration-tests/suites/consola/subject.ts b/dev-packages/node-integration-tests/suites/consola/subject.ts new file mode 100644 index 000000000000..834aac57d993 --- /dev/null +++ b/dev-packages/node-integration-tests/suites/consola/subject.ts @@ -0,0 +1,32 @@ +import * as Sentry from '@sentry/node'; +import { loggingTransport } from '@sentry-internal/node-integration-tests'; +import { consola } from 'consola'; + +Sentry.init({ + dsn: 'https://public@dsn.ingest.sentry.io/1337', + release: '1.0.0', + environment: 'test', + enableLogs: true, + transport: loggingTransport, +}); + +async function run(): Promise { + // Set consola level to capture all logs including debug and trace + consola.level = 5; + + // Create a Sentry reporter for consola + const sentryReporter = Sentry.createConsolaReporter(); + + // Add the reporter to consola + consola.addReporter(sentryReporter); + + // Test basic logging with different types + consola.info('Test info message'); + consola.error('Test error message'); + consola.warn('Test warn message'); + + await Sentry.flush(); +} + +// eslint-disable-next-line @typescript-eslint/no-floating-promises +void run(); diff --git a/dev-packages/node-integration-tests/suites/consola/test.ts b/dev-packages/node-integration-tests/suites/consola/test.ts new file mode 100644 index 000000000000..cf396e319c51 --- /dev/null +++ b/dev-packages/node-integration-tests/suites/consola/test.ts @@ -0,0 +1,494 @@ +import { afterAll, describe, expect, test } from 'vitest'; +import { cleanupChildProcesses, createRunner } from '../../utils/runner'; + +describe('consola integration', () => { + afterAll(() => { + cleanupChildProcesses(); + }); + + test('should capture consola logs with default levels', async () => { + const runner = createRunner(__dirname, 'subject.ts') + .expect({ + log: { + items: [ + { + timestamp: expect.any(Number), + level: 'info', + body: 'Test info message', + severity_number: expect.any(Number), + trace_id: expect.any(String), + attributes: { + 'sentry.origin': { value: 'auto.logging.consola', type: 'string' }, + 'sentry.release': { value: '1.0.0', type: 'string' }, + 'sentry.environment': { value: 'test', type: 'string' }, + 'sentry.sdk.name': { value: 'sentry.javascript.node', type: 'string' }, + 'sentry.sdk.version': { value: expect.any(String), type: 'string' }, + 'server.address': { value: expect.any(String), type: 'string' }, + 'consola.type': { value: 'info', type: 'string' }, + 'consola.level': { value: 3, type: 'integer' }, + }, + }, + { + timestamp: expect.any(Number), + level: 'error', + body: 'Test error message', + severity_number: expect.any(Number), + trace_id: expect.any(String), + attributes: { + 'sentry.origin': { value: 'auto.logging.consola', type: 'string' }, + 'sentry.release': { value: '1.0.0', type: 'string' }, + 'sentry.environment': { value: 'test', type: 'string' }, + 'sentry.sdk.name': { value: 'sentry.javascript.node', type: 'string' }, + 'sentry.sdk.version': { value: expect.any(String), type: 'string' }, + 'server.address': { value: expect.any(String), type: 'string' }, + 'consola.type': { value: 'error', type: 'string' }, + 'consola.level': { value: 0, type: 'integer' }, + }, + }, + { + timestamp: expect.any(Number), + level: 'warn', + body: 'Test warn message', + severity_number: expect.any(Number), + trace_id: expect.any(String), + attributes: { + 'sentry.origin': { value: 'auto.logging.consola', type: 'string' }, + 'sentry.release': { value: '1.0.0', type: 'string' }, + 'sentry.environment': { value: 'test', type: 'string' }, + 'sentry.sdk.name': { value: 'sentry.javascript.node', type: 'string' }, + 'sentry.sdk.version': { value: expect.any(String), type: 'string' }, + 'server.address': { value: expect.any(String), type: 'string' }, + 'consola.type': { value: 'warn', type: 'string' }, + 'consola.level': { value: 1, type: 'integer' }, + }, + }, + ], + }, + }) + .start(); + + await runner.completed(); + }); + + test('should capture different consola log types', async () => { + const runner = createRunner(__dirname, 'subject-types.ts') + .expect({ + log: { + items: [ + // Basic logs from default test + { + timestamp: expect.any(Number), + level: 'info', + body: 'Test info message', + severity_number: expect.any(Number), + trace_id: expect.any(String), + attributes: { + 'sentry.origin': { value: 'auto.logging.consola', type: 'string' }, + 'sentry.release': { value: '1.0.0', type: 'string' }, + 'sentry.environment': { value: 'test', type: 'string' }, + 'sentry.sdk.name': { value: 'sentry.javascript.node', type: 'string' }, + 'sentry.sdk.version': { value: expect.any(String), type: 'string' }, + 'server.address': { value: expect.any(String), type: 'string' }, + 'consola.type': { value: 'info', type: 'string' }, + 'consola.level': { value: 3, type: 'integer' }, + }, + }, + { + timestamp: expect.any(Number), + level: 'error', + body: 'Test error message', + severity_number: expect.any(Number), + trace_id: expect.any(String), + attributes: { + 'sentry.origin': { value: 'auto.logging.consola', type: 'string' }, + 'sentry.release': { value: '1.0.0', type: 'string' }, + 'sentry.environment': { value: 'test', type: 'string' }, + 'sentry.sdk.name': { value: 'sentry.javascript.node', type: 'string' }, + 'sentry.sdk.version': { value: expect.any(String), type: 'string' }, + 'server.address': { value: expect.any(String), type: 'string' }, + 'consola.type': { value: 'error', type: 'string' }, + 'consola.level': { value: 0, type: 'integer' }, + }, + }, + { + timestamp: expect.any(Number), + level: 'warn', + body: 'Test warn message', + severity_number: expect.any(Number), + trace_id: expect.any(String), + attributes: { + 'sentry.origin': { value: 'auto.logging.consola', type: 'string' }, + 'sentry.release': { value: '1.0.0', type: 'string' }, + 'sentry.environment': { value: 'test', type: 'string' }, + 'sentry.sdk.name': { value: 'sentry.javascript.node', type: 'string' }, + 'sentry.sdk.version': { value: expect.any(String), type: 'string' }, + 'server.address': { value: expect.any(String), type: 'string' }, + 'consola.type': { value: 'warn', type: 'string' }, + 'consola.level': { value: 1, type: 'integer' }, + }, + }, + // Consola-specific log types + { + timestamp: expect.any(Number), + level: 'info', + body: 'Test success message', + severity_number: expect.any(Number), + trace_id: expect.any(String), + attributes: { + 'sentry.origin': { value: 'auto.logging.consola', type: 'string' }, + 'sentry.release': { value: '1.0.0', type: 'string' }, + 'sentry.environment': { value: 'test', type: 'string' }, + 'sentry.sdk.name': { value: 'sentry.javascript.node', type: 'string' }, + 'sentry.sdk.version': { value: expect.any(String), type: 'string' }, + 'server.address': { value: expect.any(String), type: 'string' }, + 'consola.type': { value: 'success', type: 'string' }, + 'consola.level': { value: 3, type: 'integer' }, + }, + }, + { + timestamp: expect.any(Number), + level: 'error', + body: 'Test fail message', + severity_number: expect.any(Number), + trace_id: expect.any(String), + attributes: { + 'sentry.origin': { value: 'auto.logging.consola', type: 'string' }, + 'sentry.release': { value: '1.0.0', type: 'string' }, + 'sentry.environment': { value: 'test', type: 'string' }, + 'sentry.sdk.name': { value: 'sentry.javascript.node', type: 'string' }, + 'sentry.sdk.version': { value: expect.any(String), type: 'string' }, + 'server.address': { value: expect.any(String), type: 'string' }, + 'consola.type': { value: 'fail', type: 'string' }, + 'consola.level': { value: 3, type: 'integer' }, + }, + }, + { + timestamp: expect.any(Number), + level: 'info', + body: 'Test ready message', + severity_number: expect.any(Number), + trace_id: expect.any(String), + attributes: { + 'sentry.origin': { value: 'auto.logging.consola', type: 'string' }, + 'sentry.release': { value: '1.0.0', type: 'string' }, + 'sentry.environment': { value: 'test', type: 'string' }, + 'sentry.sdk.name': { value: 'sentry.javascript.node', type: 'string' }, + 'sentry.sdk.version': { value: expect.any(String), type: 'string' }, + 'server.address': { value: expect.any(String), type: 'string' }, + 'consola.type': { value: 'ready', type: 'string' }, + 'consola.level': { value: 3, type: 'integer' }, + }, + }, + { + timestamp: expect.any(Number), + level: 'info', + body: 'Test start message', + severity_number: expect.any(Number), + trace_id: expect.any(String), + attributes: { + 'sentry.origin': { value: 'auto.logging.consola', type: 'string' }, + 'sentry.release': { value: '1.0.0', type: 'string' }, + 'sentry.environment': { value: 'test', type: 'string' }, + 'sentry.sdk.name': { value: 'sentry.javascript.node', type: 'string' }, + 'sentry.sdk.version': { value: expect.any(String), type: 'string' }, + 'server.address': { value: expect.any(String), type: 'string' }, + 'consola.type': { value: 'start', type: 'string' }, + 'consola.level': { value: 3, type: 'integer' }, + }, + }, + { + timestamp: expect.any(Number), + level: 'info', + body: 'Test box message', + severity_number: expect.any(Number), + trace_id: expect.any(String), + attributes: { + 'sentry.origin': { value: 'auto.logging.consola', type: 'string' }, + 'sentry.release': { value: '1.0.0', type: 'string' }, + 'sentry.environment': { value: 'test', type: 'string' }, + 'sentry.sdk.name': { value: 'sentry.javascript.node', type: 'string' }, + 'sentry.sdk.version': { value: expect.any(String), type: 'string' }, + 'server.address': { value: expect.any(String), type: 'string' }, + 'consola.type': { value: 'box', type: 'string' }, + 'consola.level': { value: 3, type: 'integer' }, + }, + }, + { + timestamp: expect.any(Number), + level: 'debug', + body: 'Test verbose message', + severity_number: expect.any(Number), + trace_id: expect.any(String), + attributes: { + 'sentry.origin': { value: 'auto.logging.consola', type: 'string' }, + 'sentry.release': { value: '1.0.0', type: 'string' }, + 'sentry.environment': { value: 'test', type: 'string' }, + 'sentry.sdk.name': { value: 'sentry.javascript.node', type: 'string' }, + 'sentry.sdk.version': { value: expect.any(String), type: 'string' }, + 'server.address': { value: expect.any(String), type: 'string' }, + 'consola.type': { value: 'verbose', type: 'string' }, + }, + }, + { + timestamp: expect.any(Number), + level: 'debug', + body: 'Test debug message', + severity_number: expect.any(Number), + trace_id: expect.any(String), + attributes: { + 'sentry.origin': { value: 'auto.logging.consola', type: 'string' }, + 'sentry.release': { value: '1.0.0', type: 'string' }, + 'sentry.environment': { value: 'test', type: 'string' }, + 'sentry.sdk.name': { value: 'sentry.javascript.node', type: 'string' }, + 'sentry.sdk.version': { value: expect.any(String), type: 'string' }, + 'server.address': { value: expect.any(String), type: 'string' }, + 'consola.type': { value: 'debug', type: 'string' }, + 'consola.level': { value: 4, type: 'integer' }, + }, + }, + { + timestamp: expect.any(Number), + level: 'trace', + body: 'Test trace message', + severity_number: expect.any(Number), + trace_id: expect.any(String), + attributes: { + 'sentry.origin': { value: 'auto.logging.consola', type: 'string' }, + 'sentry.release': { value: '1.0.0', type: 'string' }, + 'sentry.environment': { value: 'test', type: 'string' }, + 'sentry.sdk.name': { value: 'sentry.javascript.node', type: 'string' }, + 'sentry.sdk.version': { value: expect.any(String), type: 'string' }, + 'server.address': { value: expect.any(String), type: 'string' }, + 'consola.type': { value: 'trace', type: 'string' }, + 'consola.level': { value: 5, type: 'integer' }, + }, + }, + ], + }, + }) + .start(); + + await runner.completed(); + }); + + test('should capture consola logs with arguments formatting', async () => { + const runner = createRunner(__dirname, 'subject-args.ts') + .expect({ + log: { + items: [ + { + timestamp: expect.any(Number), + level: 'info', + body: 'Message with args: hello 123 {"key":"value"} [1,2,3]', + severity_number: expect.any(Number), + trace_id: expect.any(String), + attributes: { + 'sentry.origin': { value: 'auto.logging.consola', type: 'string' }, + 'sentry.release': { value: '1.0.0', type: 'string' }, + 'sentry.environment': { value: 'test', type: 'string' }, + 'sentry.sdk.name': { value: 'sentry.javascript.node', type: 'string' }, + 'sentry.sdk.version': { value: expect.any(String), type: 'string' }, + 'server.address': { value: expect.any(String), type: 'string' }, + 'consola.type': { value: 'info', type: 'string' }, + 'consola.level': { value: 3, type: 'integer' }, + }, + }, + { + timestamp: expect.any(Number), + level: 'debug', + body: 'Debug message', + severity_number: expect.any(Number), + trace_id: expect.any(String), + attributes: { + 'sentry.origin': { value: 'auto.logging.consola', type: 'string' }, + 'sentry.release': { value: '1.0.0', type: 'string' }, + 'sentry.environment': { value: 'test', type: 'string' }, + 'sentry.sdk.name': { value: 'sentry.javascript.node', type: 'string' }, + 'sentry.sdk.version': { value: expect.any(String), type: 'string' }, + 'server.address': { value: expect.any(String), type: 'string' }, + 'consola.type': { value: 'debug', type: 'string' }, + 'consola.level': { value: 2, type: 'integer' }, + customData: { + value: '{"nested":"value","count":42}', + type: 'string', + }, + sessionId: { + value: 'abc-123-def', + type: 'string', + }, + userId: { + value: 12345, + type: 'integer', + }, + }, + }, + ], + }, + }) + .start(); + + await runner.completed(); + }); + + test('should capture consola logs with tags', async () => { + const runner = createRunner(__dirname, 'subject-tags.ts') + .expect({ + log: { + items: [ + { + timestamp: expect.any(Number), + level: 'info', + body: 'Tagged info message', + severity_number: expect.any(Number), + trace_id: expect.any(String), + attributes: { + 'sentry.origin': { value: 'auto.logging.consola', type: 'string' }, + 'sentry.release': { value: '1.0.0', type: 'string' }, + 'sentry.environment': { value: 'test', type: 'string' }, + 'sentry.sdk.name': { value: 'sentry.javascript.node', type: 'string' }, + 'sentry.sdk.version': { value: expect.any(String), type: 'string' }, + 'server.address': { value: expect.any(String), type: 'string' }, + 'consola.tag': { value: 'api', type: 'string' }, + 'consola.type': { value: 'info', type: 'string' }, + 'consola.level': { value: 3, type: 'integer' }, + }, + }, + { + timestamp: expect.any(Number), + level: 'error', + body: 'Tagged error message', + severity_number: expect.any(Number), + trace_id: expect.any(String), + attributes: { + 'sentry.origin': { value: 'auto.logging.consola', type: 'string' }, + 'sentry.release': { value: '1.0.0', type: 'string' }, + 'sentry.environment': { value: 'test', type: 'string' }, + 'sentry.sdk.name': { value: 'sentry.javascript.node', type: 'string' }, + 'sentry.sdk.version': { value: expect.any(String), type: 'string' }, + 'server.address': { value: expect.any(String), type: 'string' }, + 'consola.tag': { value: 'api', type: 'string' }, + 'consola.type': { value: 'error', type: 'string' }, + 'consola.level': { value: 0, type: 'integer' }, + }, + }, + ], + }, + }) + .start(); + + await runner.completed(); + }); + + test('should respect custom level filtering', async () => { + const runner = createRunner(__dirname, 'subject-custom-levels.ts') + .expect({ + log: { + items: [ + // Should capture the warn message + { + timestamp: expect.any(Number), + level: 'warn', + body: 'This should be captured', + severity_number: expect.any(Number), + trace_id: expect.any(String), + attributes: { + 'sentry.origin': { value: 'auto.logging.consola', type: 'string' }, + 'sentry.release': { value: '1.0.0', type: 'string' }, + 'sentry.environment': { value: 'test', type: 'string' }, + 'sentry.sdk.name': { value: 'sentry.javascript.node', type: 'string' }, + 'sentry.sdk.version': { value: expect.any(String), type: 'string' }, + 'server.address': { value: expect.any(String), type: 'string' }, + 'consola.type': { value: 'warn', type: 'string' }, + 'consola.level': { value: 1, type: 'integer' }, + }, + }, + // Should capture the error message + { + timestamp: expect.any(Number), + level: 'error', + body: 'This should also be captured', + severity_number: expect.any(Number), + trace_id: expect.any(String), + attributes: { + 'sentry.origin': { value: 'auto.logging.consola', type: 'string' }, + 'sentry.release': { value: '1.0.0', type: 'string' }, + 'sentry.environment': { value: 'test', type: 'string' }, + 'sentry.sdk.name': { value: 'sentry.javascript.node', type: 'string' }, + 'sentry.sdk.version': { value: expect.any(String), type: 'string' }, + 'server.address': { value: expect.any(String), type: 'string' }, + 'consola.type': { value: 'error', type: 'string' }, + 'consola.level': { value: 0, type: 'integer' }, + }, + }, + ], + }, + }) + .start(); + + await runner.completed(); + }); + + test('should capture different consola level methods', async () => { + const runner = createRunner(__dirname, 'subject-levels.ts') + .expect({ + log: { + items: [ + { + timestamp: expect.any(Number), + level: 'fatal', + body: 'Fatal level message', + severity_number: expect.any(Number), + trace_id: expect.any(String), + attributes: { + 'sentry.origin': { value: 'auto.logging.consola', type: 'string' }, + 'sentry.release': { value: '1.0.0', type: 'string' }, + 'sentry.environment': { value: 'test', type: 'string' }, + 'sentry.sdk.name': { value: 'sentry.javascript.node', type: 'string' }, + 'sentry.sdk.version': { value: expect.any(String), type: 'string' }, + 'server.address': { value: expect.any(String), type: 'string' }, + 'consola.type': { value: 'fatal', type: 'string' }, + 'consola.level': { value: 0, type: 'integer' }, + }, + }, + { + timestamp: expect.any(Number), + level: 'warn', + body: 'Warning level message', + severity_number: expect.any(Number), + trace_id: expect.any(String), + attributes: { + 'sentry.origin': { value: 'auto.logging.consola', type: 'string' }, + 'sentry.release': { value: '1.0.0', type: 'string' }, + 'sentry.environment': { value: 'test', type: 'string' }, + 'sentry.sdk.name': { value: 'sentry.javascript.node', type: 'string' }, + 'sentry.sdk.version': { value: expect.any(String), type: 'string' }, + 'server.address': { value: expect.any(String), type: 'string' }, + 'consola.type': { value: 'warn', type: 'string' }, + 'consola.level': { value: 1, type: 'integer' }, + }, + }, + { + timestamp: expect.any(Number), + level: 'info', + body: 'Info level message', + severity_number: expect.any(Number), + trace_id: expect.any(String), + attributes: { + 'sentry.origin': { value: 'auto.logging.consola', type: 'string' }, + 'sentry.release': { value: '1.0.0', type: 'string' }, + 'sentry.environment': { value: 'test', type: 'string' }, + 'sentry.sdk.name': { value: 'sentry.javascript.node', type: 'string' }, + 'sentry.sdk.version': { value: expect.any(String), type: 'string' }, + 'server.address': { value: expect.any(String), type: 'string' }, + 'consola.type': { value: 'info', type: 'string' }, + 'consola.level': { value: 3, type: 'integer' }, + }, + }, + ], + }, + }) + .start(); + + await runner.completed(); + }); +}); diff --git a/packages/astro/src/index.server.ts b/packages/astro/src/index.server.ts index c91d98725d9b..5abf8d51633d 100644 --- a/packages/astro/src/index.server.ts +++ b/packages/astro/src/index.server.ts @@ -142,6 +142,7 @@ export { profiler, logger, consoleLoggingIntegration, + createConsolaReporter, wrapMcpServerWithSentry, NODE_VERSION, featureFlagsIntegration, diff --git a/packages/aws-serverless/src/index.ts b/packages/aws-serverless/src/index.ts index a041e0a7231f..541f8a97a410 100644 --- a/packages/aws-serverless/src/index.ts +++ b/packages/aws-serverless/src/index.ts @@ -128,6 +128,7 @@ export { vercelAIIntegration, logger, consoleLoggingIntegration, + createConsolaReporter, wrapMcpServerWithSentry, NODE_VERSION, featureFlagsIntegration, diff --git a/packages/browser/src/index.ts b/packages/browser/src/index.ts index 0bc523506454..35de77f611d0 100644 --- a/packages/browser/src/index.ts +++ b/packages/browser/src/index.ts @@ -16,6 +16,7 @@ export { extraErrorDataIntegration, rewriteFramesIntegration, consoleLoggingIntegration, + createConsolaReporter, } from '@sentry/core'; export { replayIntegration, getReplay } from '@sentry-internal/replay'; diff --git a/packages/bun/src/index.ts b/packages/bun/src/index.ts index e0ce86b1bd23..bc5bf37c0de4 100644 --- a/packages/bun/src/index.ts +++ b/packages/bun/src/index.ts @@ -146,6 +146,7 @@ export { vercelAIIntegration, logger, consoleLoggingIntegration, + createConsolaReporter, createSentryWinstonTransport, wrapMcpServerWithSentry, featureFlagsIntegration, diff --git a/packages/cloudflare/src/index.ts b/packages/cloudflare/src/index.ts index 5272d475572d..db0e0aab98a7 100644 --- a/packages/cloudflare/src/index.ts +++ b/packages/cloudflare/src/index.ts @@ -94,6 +94,7 @@ export { updateSpanName, wrapMcpServerWithSentry, consoleLoggingIntegration, + createConsolaReporter, featureFlagsIntegration, } from '@sentry/core'; diff --git a/packages/core/src/index.ts b/packages/core/src/index.ts index d54ac9d435f2..339dc92454b0 100644 --- a/packages/core/src/index.ts +++ b/packages/core/src/index.ts @@ -124,6 +124,7 @@ export { captureFeedback } from './feedback'; export type { ReportDialogOptions } from './report-dialog'; export { _INTERNAL_captureLog, _INTERNAL_flushLogsBuffer, _INTERNAL_captureSerializedLog } from './logs/exports'; export { consoleLoggingIntegration } from './logs/console-integration'; +export { createConsolaReporter } from './integrations/consola'; export { addVercelAiProcessors } from './utils/vercel-ai'; export { _INTERNAL_getSpanForToolCallId, _INTERNAL_cleanupToolCallSpan } from './utils/vercel-ai/utils'; export { instrumentOpenAiClient } from './utils/openai'; diff --git a/packages/core/src/integrations/consola.ts b/packages/core/src/integrations/consola.ts new file mode 100644 index 000000000000..cdae9efa17dc --- /dev/null +++ b/packages/core/src/integrations/consola.ts @@ -0,0 +1,312 @@ +import type { Client } from '../client'; +import { getClient } from '../currentScopes'; +import { _INTERNAL_captureLog } from '../logs/exports'; +import { formatConsoleArgs } from '../logs/utils'; +import type { LogSeverityLevel } from '../types-hoist/log'; + +/** + * Options for the Sentry Consola reporter. + */ +interface ConsolaReporterOptions { + /** + * Use this option to filter which levels should be captured. By default, all levels are captured. + * + * @example + * ```ts + * const sentryReporter = Sentry.createConsolaReporter({ + * // Only capture error and warn logs + * levels: ['error', 'warn'], + * }); + * consola.addReporter(sentryReporter); + * ``` + */ + levels?: Array; + + /** + * Optionally provide a specific Sentry client instance to use for capturing logs. + * If not provided, the current client will be retrieved using `getClient()`. + * + * This is useful when you want to use specific client options for log normalization + * or when working with multiple client instances. + * + * @example + * ```ts + * const sentryReporter = Sentry.createConsolaReporter({ + * client: myCustomClient, + * }); + * ``` + */ + client?: Client; +} + +export interface ConsolaReporter { + log: (logObj: ConsolaLogObject) => void; +} + +/** + * Represents a log object that Consola reporters receive. + * + * This interface matches the structure of log objects passed to Consola reporters. + * See: https://github.com/unjs/consola#custom-reporters + * + * @example + * ```ts + * const reporter = { + * log(logObj: ConsolaLogObject) { + * console.log(`[${logObj.type}] ${logObj.message || logObj.args?.join(' ')}`); + * } + * }; + * consola.addReporter(reporter); + * ``` + */ +export interface ConsolaLogObject { + /** + * Allows additional custom properties to be set on the log object. + * These properties will be captured as log attributes with a 'consola.' prefix. + * + * @example + * ```ts + * const reporter = Sentry.createConsolaReporter(); + * reporter.log({ + * type: 'info', + * message: 'User action', + * userId: 123, + * sessionId: 'abc-123' + * }); + * // Will create attributes: consola.userId and consola.sessionId + * ``` + */ + [key: string]: unknown; + + /** + * The numeric log level (0-5) or null. + * + * Consola log levels: + * - 0: Fatal and Error + * - 1: Warnings + * - 2: Normal logs + * - 3: Informational logs, success, fail, ready, start, box, ... + * - 4: Debug logs + * - 5: Trace logs + * - null: Some special types like 'verbose' + * + * See: https://github.com/unjs/consola/blob/main/README.md#log-level + */ + level?: number | null; + + /** + * The log type/method name (e.g., 'error', 'warn', 'info', 'debug', 'trace', 'success', 'fail', etc.). + * + * Consola built-in types include: + * - Standard: silent, fatal, error, warn, log, info, success, fail, ready, start, box, debug, trace, verbose + * - Custom types can also be defined + * + * See: https://github.com/unjs/consola/blob/main/README.md#log-types + */ + type?: string; + + /** + * An optional tag/scope for the log entry. + * + * Tags are created using `consola.withTag('scope')` and help categorize logs. + * + * @example + * ```ts + * const scopedLogger = consola.withTag('auth'); + * scopedLogger.info('User logged in'); // tag will be 'auth' + * ``` + * + * See: https://github.com/unjs/consola/blob/main/README.md#withtagtag + */ + tag?: string; + + /** + * The raw arguments passed to the log method. + * + * When `message` is not provided, these args are typically formatted into the final message. + * + * @example + * ```ts + * consola.info('Hello', 'world', { user: 'john' }); + * // args = ['Hello', 'world', { user: 'john' }] + * ``` + */ + args?: unknown[]; + + /** + * The timestamp when the log was created. + * + * This is automatically set by Consola when the log is created. + */ + date?: Date; + + /** + * The formatted log message. + * + * When provided, this is the final formatted message. When not provided, + * the message should be constructed from the `args` array. + */ + message?: string; +} + +const DEFAULT_CAPTURED_LEVELS: Array = ['trace', 'debug', 'info', 'warn', 'error', 'fatal']; + +/** + * Creates a new Sentry reporter for Consola that forwards logs to Sentry. Requires the `enableLogs` option to be enabled. + * + * **Note: This integration supports Consola v3.x only.** The reporter interface and log object structure + * may differ in other versions of Consola. + * + * @param options - Configuration options for the reporter. + * @returns A Consola reporter that can be added to consola instances. + * + * @example + * ```ts + * import * as Sentry from '@sentry/node'; + * import { consola } from 'consola'; + * + * Sentry.init({ + * enableLogs: true, + * }); + * + * const sentryReporter = Sentry.createConsolaReporter({ + * // Optional: filter levels to capture + * levels: ['error', 'warn', 'info'], + * }); + * + * consola.addReporter(sentryReporter); + * + * // Now consola logs will be captured by Sentry + * consola.info('This will be sent to Sentry'); + * consola.error('This error will also be sent to Sentry'); + * ``` + */ +export function createConsolaReporter(options: ConsolaReporterOptions = {}): ConsolaReporter { + const levels = new Set(options.levels ?? DEFAULT_CAPTURED_LEVELS); + const providedClient = options.client; + + return { + log(logObj: ConsolaLogObject) { + // eslint-disable-next-line @typescript-eslint/no-unused-vars + const { type, level, message: consolaMessage, args, tag, date: _date, ...attributes } = logObj; + + // Get client - use provided client or current client + const client = providedClient || getClient(); + if (!client) { + return; + } + + // Determine the log severity level + const logSeverityLevel = getLogSeverityLevel(type, level); + + // Early exit if this level should not be captured + if (!levels.has(logSeverityLevel)) { + return; + } + + const { normalizeDepth = 3, normalizeMaxBreadth = 1_000 } = client.getOptions(); + + // Format the log message using the same approach as consola's basic reporter + const messageParts = []; + if (consolaMessage) { + messageParts.push(consolaMessage); + } + if (args && args.length > 0) { + messageParts.push(formatConsoleArgs(args, normalizeDepth, normalizeMaxBreadth)); + } + const message = messageParts.join(' '); + + // Build attributes + attributes['sentry.origin'] = 'auto.logging.consola'; + + if (tag) { + attributes['consola.tag'] = tag; + } + + if (type) { + attributes['consola.type'] = type; + } + + // Only add level if it's a valid number (not null/undefined) + if (level != null && typeof level === 'number') { + attributes['consola.level'] = level; + } + + _INTERNAL_captureLog({ + level: logSeverityLevel, + message, + attributes, + }); + }, + }; +} + +// Mapping from consola log types to Sentry log severity levels +const CONSOLA_TYPE_TO_LOG_SEVERITY_LEVEL_MAP: Record = { + // Consola built-in types + silent: 'trace', + fatal: 'fatal', + error: 'error', + warn: 'warn', + log: 'info', + info: 'info', + success: 'info', + fail: 'error', + ready: 'info', + start: 'info', + box: 'info', + debug: 'debug', + trace: 'trace', + verbose: 'debug', + // Custom types that might exist + critical: 'fatal', + notice: 'info', +}; + +// Mapping from consola log levels (numbers) to Sentry log severity levels +const CONSOLA_LEVEL_TO_LOG_SEVERITY_LEVEL_MAP: Record = { + 0: 'fatal', // Fatal and Error + 1: 'warn', // Warnings + 2: 'info', // Normal logs + 3: 'info', // Informational logs, success, fail, ready, start, ... + 4: 'debug', // Debug logs + 5: 'trace', // Trace logs +}; + +/** + * Determines the log severity level from Consola type and level. + * + * @param type - The Consola log type (e.g., 'error', 'warn', 'info') + * @param level - The Consola numeric log level (0-5) or null for some types like 'verbose' + * @returns The corresponding Sentry log severity level + */ +function getLogSeverityLevel(type?: string, level?: number | null): LogSeverityLevel { + // Handle special case for verbose logs (level can be null with infinite level in Consola) + if (type === 'verbose') { + return 'debug'; + } + + // Handle silent logs - these should be at trace level + if (type === 'silent') { + return 'trace'; + } + + // First try to map by type (more specific) + if (type) { + const mappedLevel = CONSOLA_TYPE_TO_LOG_SEVERITY_LEVEL_MAP[type]; + if (mappedLevel) { + return mappedLevel; + } + } + + // Fallback to level mapping (handle null level) + if (typeof level === 'number') { + const mappedLevel = CONSOLA_LEVEL_TO_LOG_SEVERITY_LEVEL_MAP[level]; + if (mappedLevel) { + return mappedLevel; + } + } + + // Default fallback + return 'info'; +} diff --git a/packages/core/src/logs/console-integration.ts b/packages/core/src/logs/console-integration.ts index a69d406a659c..6c967499800f 100644 --- a/packages/core/src/logs/console-integration.ts +++ b/packages/core/src/logs/console-integration.ts @@ -6,21 +6,13 @@ import { SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN } from '../semanticAttributes'; import type { ConsoleLevel } from '../types-hoist/instrument'; import type { IntegrationFn } from '../types-hoist/integration'; import { CONSOLE_LEVELS, debug } from '../utils/debug-logger'; -import { isPrimitive } from '../utils/is'; -import { normalize } from '../utils/normalize'; -import { GLOBAL_OBJ } from '../utils/worldwide'; import { _INTERNAL_captureLog } from './exports'; +import { formatConsoleArgs } from './utils'; interface CaptureConsoleOptions { levels: ConsoleLevel[]; } -type GlobalObjectWithUtil = typeof GLOBAL_OBJ & { - util: { - format: (...args: unknown[]) => string; - }; -}; - const INTEGRATION_NAME = 'ConsoleLogs'; const DEFAULT_ATTRIBUTES = { @@ -89,17 +81,3 @@ const _consoleLoggingIntegration = ((options: Partial = { * ``` */ export const consoleLoggingIntegration = defineIntegration(_consoleLoggingIntegration); - -function formatConsoleArgs(values: unknown[], normalizeDepth: number, normalizeMaxBreadth: number): string { - return 'util' in GLOBAL_OBJ && typeof (GLOBAL_OBJ as GlobalObjectWithUtil).util.format === 'function' - ? (GLOBAL_OBJ as GlobalObjectWithUtil).util.format(...values) - : safeJoinConsoleArgs(values, normalizeDepth, normalizeMaxBreadth); -} - -function safeJoinConsoleArgs(values: unknown[], normalizeDepth: number, normalizeMaxBreadth: number): string { - return values - .map(value => - isPrimitive(value) ? String(value) : JSON.stringify(normalize(value, normalizeDepth, normalizeMaxBreadth)), - ) - .join(' '); -} diff --git a/packages/core/src/logs/utils.ts b/packages/core/src/logs/utils.ts new file mode 100644 index 000000000000..c30bfd75530b --- /dev/null +++ b/packages/core/src/logs/utils.ts @@ -0,0 +1,39 @@ +import { isPrimitive } from '../utils/is'; +import { normalize } from '../utils/normalize'; +import { GLOBAL_OBJ } from '../utils/worldwide'; + +type GlobalObjectWithUtil = typeof GLOBAL_OBJ & { + util: { + format: (...args: unknown[]) => string; + }; +}; + +/** + * Formats the given values into a string. + * + * @param values - The values to format. + * @param normalizeDepth - The depth to normalize the values. + * @param normalizeMaxBreadth - The max breadth to normalize the values. + * @returns The formatted string. + */ +export function formatConsoleArgs(values: unknown[], normalizeDepth: number, normalizeMaxBreadth: number): string { + return 'util' in GLOBAL_OBJ && typeof (GLOBAL_OBJ as GlobalObjectWithUtil).util.format === 'function' + ? (GLOBAL_OBJ as GlobalObjectWithUtil).util.format(...values) + : safeJoinConsoleArgs(values, normalizeDepth, normalizeMaxBreadth); +} + +/** + * Joins the given values into a string. + * + * @param values - The values to join. + * @param normalizeDepth - The depth to normalize the values. + * @param normalizeMaxBreadth - The max breadth to normalize the values. + * @returns The joined string. + */ +export function safeJoinConsoleArgs(values: unknown[], normalizeDepth: number, normalizeMaxBreadth: number): string { + return values + .map(value => + isPrimitive(value) ? String(value) : JSON.stringify(normalize(value, normalizeDepth, normalizeMaxBreadth)), + ) + .join(' '); +} diff --git a/packages/core/test/lib/integrations/consola.test.ts b/packages/core/test/lib/integrations/consola.test.ts new file mode 100644 index 000000000000..66830fb75e06 --- /dev/null +++ b/packages/core/test/lib/integrations/consola.test.ts @@ -0,0 +1,303 @@ +import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'; +import { getClient } from '../../../src/currentScopes'; +import { createConsolaReporter } from '../../../src/integrations/consola'; +import { _INTERNAL_captureLog } from '../../../src/logs/exports'; +import { formatConsoleArgs } from '../../../src/logs/utils'; +import { getDefaultTestClientOptions, TestClient } from '../../mocks/client'; + +// Mock dependencies +vi.mock('../../../src/logs/exports', () => ({ + _INTERNAL_captureLog: vi.fn(), +})); + +vi.mock('../../../src/logs/utils', async actual => ({ + formatConsoleArgs: vi.fn(((await actual()) as any).formatConsoleArgs), +})); + +vi.mock('../../../src/currentScopes', () => ({ + getClient: vi.fn(), +})); + +describe('createConsolaReporter', () => { + let mockClient: TestClient; + + beforeEach(() => { + vi.clearAllMocks(); + + // Create a test client with enableLogs: true + mockClient = new TestClient({ + ...getDefaultTestClientOptions({ dsn: 'https://username@domain/123' }), + enableLogs: true, + normalizeDepth: 3, + normalizeMaxBreadth: 1000, + }); + + vi.mocked(getClient).mockReturnValue(mockClient); + }); + + afterEach(() => { + vi.restoreAllMocks(); + }); + + describe('reporter creation', () => { + it('should create a reporter with log function', () => { + const reporter = createConsolaReporter(); + + expect(reporter).toEqual({ + log: expect.any(Function), + }); + }); + }); + + describe('log capturing', () => { + let sentryReporter: any; + + beforeEach(() => { + sentryReporter = createConsolaReporter(); + }); + + it('should capture error logs', () => { + const logObj = { + type: 'error', + level: 0, + message: 'This is an error', + tag: 'test', + date: new Date('2023-01-01T00:00:00.000Z'), + }; + + sentryReporter.log(logObj); + + expect(_INTERNAL_captureLog).toHaveBeenCalledWith({ + level: 'error', + message: 'This is an error', + attributes: { + 'sentry.origin': 'auto.logging.consola', + 'consola.tag': 'test', + 'consola.type': 'error', + 'consola.level': 0, + }, + }); + }); + + it('should capture warn logs', () => { + const logObj = { + type: 'warn', + message: 'This is a warning', + }; + + sentryReporter.log(logObj); + + expect(_INTERNAL_captureLog).toHaveBeenCalledWith({ + level: 'warn', + message: 'This is a warning', + attributes: { + 'sentry.origin': 'auto.logging.consola', + 'consola.type': 'warn', + }, + }); + }); + + it('should capture info logs', () => { + const logObj = { + type: 'info', + message: 'This is info', + }; + + sentryReporter.log(logObj); + + expect(_INTERNAL_captureLog).toHaveBeenCalledWith({ + level: 'info', + message: 'This is info', + attributes: { + 'sentry.origin': 'auto.logging.consola', + 'consola.type': 'info', + }, + }); + }); + + it('should capture debug logs', () => { + const logObj = { + type: 'debug', + message: 'Debug message', + }; + + sentryReporter.log(logObj); + + expect(_INTERNAL_captureLog).toHaveBeenCalledWith({ + level: 'debug', + message: 'Debug message', + attributes: { + 'sentry.origin': 'auto.logging.consola', + 'consola.type': 'debug', + }, + }); + }); + + it('should capture trace logs', () => { + const logObj = { + type: 'trace', + message: 'Trace message', + }; + + sentryReporter.log(logObj); + + expect(_INTERNAL_captureLog).toHaveBeenCalledWith({ + level: 'trace', + message: 'Trace message', + attributes: { + 'sentry.origin': 'auto.logging.consola', + 'consola.type': 'trace', + }, + }); + }); + + it('should capture fatal logs', () => { + const logObj = { + type: 'fatal', + message: 'Fatal error', + }; + + sentryReporter.log(logObj); + + expect(_INTERNAL_captureLog).toHaveBeenCalledWith({ + level: 'fatal', + message: 'Fatal error', + attributes: { + 'sentry.origin': 'auto.logging.consola', + 'consola.type': 'fatal', + }, + }); + }); + + it('should format message from args when message is not provided', () => { + const logObj = { + type: 'info', + args: ['Hello', 'world', 123, { key: 'value' }], + }; + + sentryReporter.log(logObj); + + expect(formatConsoleArgs).toHaveBeenCalledWith(['Hello', 'world', 123, { key: 'value' }], 3, 1000); + expect(_INTERNAL_captureLog).toHaveBeenCalledWith({ + level: 'info', + message: 'Hello world 123 {"key":"value"}', + attributes: { + 'sentry.origin': 'auto.logging.consola', + 'consola.type': 'info', + }, + }); + }); + + it('should handle args with unparseable objects', () => { + const circular: any = {}; + circular.self = circular; + + const logObj = { + type: 'info', + args: ['Message', circular], + }; + + sentryReporter.log(logObj); + + expect(_INTERNAL_captureLog).toHaveBeenCalledWith({ + level: 'info', + message: 'Message {"self":"[Circular ~]"}', + attributes: { + 'sentry.origin': 'auto.logging.consola', + 'consola.type': 'info', + }, + }); + }); + + it('should map consola levels to sentry levels when type is not provided', () => { + const logObj = { + level: 0, // Fatal level + message: 'Fatal message', + }; + + sentryReporter.log(logObj); + + expect(_INTERNAL_captureLog).toHaveBeenCalledWith({ + level: 'fatal', + message: 'Fatal message', + attributes: { + 'sentry.origin': 'auto.logging.consola', + 'consola.level': 0, + }, + }); + }); + + it('should map various consola types correctly', () => { + const testCases = [ + { type: 'success', expectedLevel: 'info' }, + { type: 'fail', expectedLevel: 'error' }, + { type: 'ready', expectedLevel: 'info' }, + { type: 'start', expectedLevel: 'info' }, + { type: 'verbose', expectedLevel: 'debug' }, + { type: 'log', expectedLevel: 'info' }, + { type: 'silent', expectedLevel: 'trace' }, + ]; + + testCases.forEach(({ type, expectedLevel }) => { + vi.clearAllMocks(); + + sentryReporter.log({ + type, + message: `Test ${type} message`, + }); + + expect(_INTERNAL_captureLog).toHaveBeenCalledWith({ + level: expectedLevel, + message: `Test ${type} message`, + attributes: { + 'sentry.origin': 'auto.logging.consola', + 'consola.type': type, + }, + }); + }); + }); + }); + + describe('level filtering', () => { + it('should only capture specified levels', () => { + const filteredReporter = createConsolaReporter({ + levels: ['error', 'warn'], + }); + + // Should capture error + filteredReporter.log({ + type: 'error', + message: 'Error message', + }); + expect(_INTERNAL_captureLog).toHaveBeenCalledTimes(1); + + // Should capture warn + filteredReporter.log({ + type: 'warn', + message: 'Warn message', + }); + expect(_INTERNAL_captureLog).toHaveBeenCalledTimes(2); + + // Should not capture info + filteredReporter.log({ + type: 'info', + message: 'Info message', + }); + expect(_INTERNAL_captureLog).toHaveBeenCalledTimes(2); + }); + + it('should use default levels when none specified', () => { + const defaultReporter = createConsolaReporter(); + + // Should capture all default levels + ['trace', 'debug', 'info', 'warn', 'error', 'fatal'].forEach(type => { + defaultReporter.log({ + type, + message: `${type} message`, + }); + }); + + expect(_INTERNAL_captureLog).toHaveBeenCalledTimes(6); + }); + }); +}); diff --git a/packages/google-cloud-serverless/src/index.ts b/packages/google-cloud-serverless/src/index.ts index 83292ab11e46..e8042e4260a8 100644 --- a/packages/google-cloud-serverless/src/index.ts +++ b/packages/google-cloud-serverless/src/index.ts @@ -128,6 +128,7 @@ export { vercelAIIntegration, logger, consoleLoggingIntegration, + createConsolaReporter, wrapMcpServerWithSentry, NODE_VERSION, featureFlagsIntegration, diff --git a/packages/node-core/src/index.ts b/packages/node-core/src/index.ts index 57d8e2ece0ae..87f96f09ab8e 100644 --- a/packages/node-core/src/index.ts +++ b/packages/node-core/src/index.ts @@ -127,6 +127,7 @@ export { zodErrorsIntegration, profiler, consoleLoggingIntegration, + createConsolaReporter, consoleIntegration, wrapMcpServerWithSentry, featureFlagsIntegration, diff --git a/packages/node/src/index.ts b/packages/node/src/index.ts index f510ca733d19..84603db7e575 100644 --- a/packages/node/src/index.ts +++ b/packages/node/src/index.ts @@ -127,6 +127,7 @@ export { zodErrorsIntegration, profiler, consoleLoggingIntegration, + createConsolaReporter, consoleIntegration, wrapMcpServerWithSentry, featureFlagsIntegration, diff --git a/packages/remix/src/server/index.ts b/packages/remix/src/server/index.ts index 9a785f1d144c..ef25e4b703e6 100644 --- a/packages/remix/src/server/index.ts +++ b/packages/remix/src/server/index.ts @@ -118,6 +118,7 @@ export { zodErrorsIntegration, logger, consoleLoggingIntegration, + createConsolaReporter, createSentryWinstonTransport, } from '@sentry/node'; diff --git a/packages/solidstart/src/server/index.ts b/packages/solidstart/src/server/index.ts index 17e7eaa9ef50..d1ad987da56f 100644 --- a/packages/solidstart/src/server/index.ts +++ b/packages/solidstart/src/server/index.ts @@ -123,6 +123,7 @@ export { zodErrorsIntegration, logger, consoleLoggingIntegration, + createConsolaReporter, createSentryWinstonTransport, } from '@sentry/node'; diff --git a/packages/sveltekit/src/server/index.ts b/packages/sveltekit/src/server/index.ts index d287331df14d..0613d6397f96 100644 --- a/packages/sveltekit/src/server/index.ts +++ b/packages/sveltekit/src/server/index.ts @@ -125,6 +125,7 @@ export { zodErrorsIntegration, logger, consoleLoggingIntegration, + createConsolaReporter, createSentryWinstonTransport, vercelAIIntegration, } from '@sentry/node'; diff --git a/packages/vercel-edge/src/index.ts b/packages/vercel-edge/src/index.ts index 374e5d8ef606..3e432c06c1fd 100644 --- a/packages/vercel-edge/src/index.ts +++ b/packages/vercel-edge/src/index.ts @@ -93,6 +93,7 @@ export { spanToBaggageHeader, wrapMcpServerWithSentry, consoleLoggingIntegration, + createConsolaReporter, featureFlagsIntegration, } from '@sentry/core'; From abb9337af73d8db62ba2ca933e7dbccb57d7a7be Mon Sep 17 00:00:00 2001 From: Abhijeet Prasad Date: Fri, 12 Sep 2025 11:05:02 -0400 Subject: [PATCH 34/61] feat(node-core): Remove experimental note from winston api (#17626) Logs are GA now, so we can remove this note from the `createSentryWinstonTransport` method. --- packages/node-core/src/integrations/winston.ts | 3 --- 1 file changed, 3 deletions(-) diff --git a/packages/node-core/src/integrations/winston.ts b/packages/node-core/src/integrations/winston.ts index a58a3ea31ad0..63e208920914 100644 --- a/packages/node-core/src/integrations/winston.ts +++ b/packages/node-core/src/integrations/winston.ts @@ -35,9 +35,6 @@ interface WinstonTransportOptions { * @param TransportClass - The Winston transport class to extend. * @returns The extended transport class. * - * @experimental This method will experience breaking changes. This is not yet part of - * the stable Sentry SDK API and can be changed or removed without warning. - * * @example * ```ts * const winston = require('winston'); From 50e9471ee0cd0ac6d3b842a88faae51241c1c8d8 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 12 Sep 2025 17:13:08 +0200 Subject: [PATCH 35/61] chore(deps): bump next from 14.2.25 to 14.2.32 in /dev-packages/e2e-tests/test-applications/nextjs-app-dir (#17627) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Bumps [next](https://github.com/vercel/next.js) from 14.2.25 to 14.2.32.
Release notes

Sourced from next's releases.

v14.2.32

[!NOTE]
This release is backporting bug fixes. It does not include all pending features/changes on canary.

Core Changes

  • fix router handling when setting a location response header #82588

Credits

Huge thanks to @​ztanner for helping!

v14.2.31

[!NOTE]
This release is backporting bug fixes. It does not include all pending features/changes on canary.

Core Changes

  • fix(next/image): improve and simplify detect-content-type (#82179)
  • fix(next/image): fix image-optimizer.ts headers (#82178)

Credits

Huge thanks to @​styfle and @​ztanner for helping!

Commits

[![Dependabot compatibility score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=next&package-manager=npm_and_yarn&previous-version=14.2.25&new-version=14.2.32)](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores) Dependabot will resolve any conflicts with this PR as long as you don't alter it yourself. You can also trigger a rebase manually by commenting `@dependabot rebase`. [//]: # (dependabot-automerge-start) [//]: # (dependabot-automerge-end) ---
Dependabot commands and options
You can trigger Dependabot actions by commenting on this PR: - `@dependabot rebase` will rebase this PR - `@dependabot recreate` will recreate this PR, overwriting any edits that have been made to it - `@dependabot merge` will merge this PR after your CI passes on it - `@dependabot squash and merge` will squash and merge this PR after your CI passes on it - `@dependabot cancel merge` will cancel a previously requested merge and block automerging - `@dependabot reopen` will reopen this PR if it is closed - `@dependabot close` will close this PR and stop Dependabot recreating it. You can achieve the same result by closing it manually - `@dependabot show ignore conditions` will show all of the ignore conditions of the specified dependency - `@dependabot ignore this major version` will close this PR and stop Dependabot creating any more for this major version (unless you reopen the PR or upgrade to it yourself) - `@dependabot ignore this minor version` will close this PR and stop Dependabot creating any more for this minor version (unless you reopen the PR or upgrade to it yourself) - `@dependabot ignore this dependency` will close this PR and stop Dependabot creating any more for this dependency (unless you reopen the PR or upgrade to it yourself) You can disable automated security fix PRs for this repo from the [Security Alerts page](https://github.com/getsentry/sentry-javascript/network/alerts).
Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .../e2e-tests/test-applications/nextjs-app-dir/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dev-packages/e2e-tests/test-applications/nextjs-app-dir/package.json b/dev-packages/e2e-tests/test-applications/nextjs-app-dir/package.json index e37e4a0c8ca3..2ac1965e180a 100644 --- a/dev-packages/e2e-tests/test-applications/nextjs-app-dir/package.json +++ b/dev-packages/e2e-tests/test-applications/nextjs-app-dir/package.json @@ -20,7 +20,7 @@ "@types/node": "^18.19.1", "@types/react": "18.0.26", "@types/react-dom": "18.0.9", - "next": "14.2.25", + "next": "14.2.32", "react": "18.2.0", "react-dom": "18.2.0", "typescript": "~5.0.0" From d198721444831de053619683f3d237a33fbcea8c Mon Sep 17 00:00:00 2001 From: Lukas Stracke Date: Fri, 12 Sep 2025 17:23:05 +0200 Subject: [PATCH 36/61] ref(angular): Adjust ErrorHandler event mechanism (#17608) Adjusts the mechanism of errors captured in Angular to follow the trace origin naming scheme closes https://github.com/getsentry/sentry-javascript/issues/17609 --- .../angular-17/tests/errors.test.ts | 4 +- .../angular-18/tests/errors.test.ts | 4 +- .../angular-19/tests/errors.test.ts | 4 +- .../angular-20/tests/errors.test.ts | 4 +- packages/angular/src/errorhandler.ts | 2 +- packages/angular/test/errorhandler.test.ts | 40 ++++++++++++++----- 6 files changed, 38 insertions(+), 20 deletions(-) diff --git a/dev-packages/e2e-tests/test-applications/angular-17/tests/errors.test.ts b/dev-packages/e2e-tests/test-applications/angular-17/tests/errors.test.ts index e6bd5ad180a9..391086555708 100644 --- a/dev-packages/e2e-tests/test-applications/angular-17/tests/errors.test.ts +++ b/dev-packages/e2e-tests/test-applications/angular-17/tests/errors.test.ts @@ -19,7 +19,7 @@ test('sends an error', async ({ page }) => { type: 'Error', value: 'Error thrown from Angular 17 E2E test app', mechanism: { - type: 'angular', + type: 'auto.function.angular.error_handler', handled: false, }, }, @@ -54,7 +54,7 @@ test('assigns the correct transaction value after a navigation', async ({ page } type: 'Error', value: 'Error thrown from user page', mechanism: { - type: 'angular', + type: 'auto.function.angular.error_handler', handled: false, }, }, diff --git a/dev-packages/e2e-tests/test-applications/angular-18/tests/errors.test.ts b/dev-packages/e2e-tests/test-applications/angular-18/tests/errors.test.ts index 36d23bd077a5..98d4ce9d1169 100644 --- a/dev-packages/e2e-tests/test-applications/angular-18/tests/errors.test.ts +++ b/dev-packages/e2e-tests/test-applications/angular-18/tests/errors.test.ts @@ -19,7 +19,7 @@ test('sends an error', async ({ page }) => { type: 'Error', value: 'Error thrown from Angular 18 E2E test app', mechanism: { - type: 'angular', + type: 'auto.function.angular.error_handler', handled: false, }, }, @@ -54,7 +54,7 @@ test('assigns the correct transaction value after a navigation', async ({ page } type: 'Error', value: 'Error thrown from user page', mechanism: { - type: 'angular', + type: 'auto.function.angular.error_handler', handled: false, }, }, diff --git a/dev-packages/e2e-tests/test-applications/angular-19/tests/errors.test.ts b/dev-packages/e2e-tests/test-applications/angular-19/tests/errors.test.ts index c6e8bc43abed..7fa706c00504 100644 --- a/dev-packages/e2e-tests/test-applications/angular-19/tests/errors.test.ts +++ b/dev-packages/e2e-tests/test-applications/angular-19/tests/errors.test.ts @@ -19,7 +19,7 @@ test('sends an error', async ({ page }) => { type: 'Error', value: 'Error thrown from Angular 18 E2E test app', mechanism: { - type: 'angular', + type: 'auto.function.angular.error_handler', handled: false, }, }, @@ -54,7 +54,7 @@ test('assigns the correct transaction value after a navigation', async ({ page } type: 'Error', value: 'Error thrown from user page', mechanism: { - type: 'angular', + type: 'auto.function.angular.error_handler', handled: false, }, }, diff --git a/dev-packages/e2e-tests/test-applications/angular-20/tests/errors.test.ts b/dev-packages/e2e-tests/test-applications/angular-20/tests/errors.test.ts index 98ae26e195cc..a6e12b6a79c4 100644 --- a/dev-packages/e2e-tests/test-applications/angular-20/tests/errors.test.ts +++ b/dev-packages/e2e-tests/test-applications/angular-20/tests/errors.test.ts @@ -19,7 +19,7 @@ test('sends an error', async ({ page }) => { type: 'Error', value: 'Error thrown from Angular 20 E2E test app', mechanism: { - type: 'angular', + type: 'auto.function.angular.error_handler', handled: false, }, }, @@ -54,7 +54,7 @@ test('assigns the correct transaction value after a navigation', async ({ page } type: 'Error', value: 'Error thrown from user page', mechanism: { - type: 'angular', + type: 'auto.function.angular.error_handler', handled: false, }, }, diff --git a/packages/angular/src/errorhandler.ts b/packages/angular/src/errorhandler.ts index 31f945b08731..ceb05c0b9e9f 100644 --- a/packages/angular/src/errorhandler.ts +++ b/packages/angular/src/errorhandler.ts @@ -111,7 +111,7 @@ class SentryErrorHandler implements AngularErrorHandler, OnDestroy { // Capture handled exception and send it to Sentry. const eventId = runOutsideAngular(() => Sentry.captureException(extractedError, { - mechanism: { type: 'angular', handled: false }, + mechanism: { type: 'auto.function.angular.error_handler', handled: false }, }), ); diff --git a/packages/angular/test/errorhandler.test.ts b/packages/angular/test/errorhandler.test.ts index af532c12d9ae..09b6d69d3337 100644 --- a/packages/angular/test/errorhandler.test.ts +++ b/packages/angular/test/errorhandler.test.ts @@ -1,7 +1,7 @@ import { HttpErrorResponse } from '@angular/common/http'; import * as SentryBrowser from '@sentry/browser'; import type { Client, Event } from '@sentry/core'; -import { vi } from 'vitest'; +import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'; import { createErrorHandler, SentryErrorHandler } from '../src/errorhandler'; const captureExceptionSpy = vi.spyOn(SentryBrowser, 'captureException'); @@ -9,7 +9,7 @@ const captureExceptionSpy = vi.spyOn(SentryBrowser, 'captureException'); vi.spyOn(console, 'error').mockImplementation(() => {}); const captureExceptionEventHint = { - mechanism: { handled: false, type: 'angular' }, + mechanism: { handled: false, type: 'auto.function.angular.error_handler' }, }; class CustomError extends Error { @@ -71,7 +71,9 @@ describe('SentryErrorHandler', () => { createErrorHandler().handleError(str); expect(captureExceptionSpy).toHaveBeenCalledTimes(1); - expect(captureExceptionSpy).toHaveBeenCalledWith(str, { mechanism: { handled: false, type: 'angular' } }); + expect(captureExceptionSpy).toHaveBeenCalledWith(str, { + mechanism: { handled: false, type: 'auto.function.angular.error_handler' }, + }); }); it('extracts an empty Error', () => { @@ -79,7 +81,9 @@ describe('SentryErrorHandler', () => { createErrorHandler().handleError(err); expect(captureExceptionSpy).toHaveBeenCalledTimes(1); - expect(captureExceptionSpy).toHaveBeenCalledWith(err, { mechanism: { handled: false, type: 'angular' } }); + expect(captureExceptionSpy).toHaveBeenCalledWith(err, { + mechanism: { handled: false, type: 'auto.function.angular.error_handler' }, + }); }); it('extracts a non-empty Error', () => { @@ -88,7 +92,9 @@ describe('SentryErrorHandler', () => { createErrorHandler().handleError(err); expect(captureExceptionSpy).toHaveBeenCalledTimes(1); - expect(captureExceptionSpy).toHaveBeenCalledWith(err, { mechanism: { handled: false, type: 'angular' } }); + expect(captureExceptionSpy).toHaveBeenCalledWith(err, { + mechanism: { handled: false, type: 'auto.function.angular.error_handler' }, + }); }); it('extracts an error-like object without stack', () => { @@ -169,7 +175,9 @@ describe('SentryErrorHandler', () => { createErrorHandler().handleError(err); expect(captureExceptionSpy).toHaveBeenCalledTimes(1); - expect(captureExceptionSpy).toHaveBeenCalledWith(err, { mechanism: { handled: false, type: 'angular' } }); + expect(captureExceptionSpy).toHaveBeenCalledWith(err, { + mechanism: { handled: false, type: 'auto.function.angular.error_handler' }, + }); }); it('extracts an instance of class not extending Error but that has an error-like shape', () => { @@ -178,7 +186,9 @@ describe('SentryErrorHandler', () => { createErrorHandler().handleError(err); expect(captureExceptionSpy).toHaveBeenCalledTimes(1); - expect(captureExceptionSpy).toHaveBeenCalledWith(err, { mechanism: { handled: false, type: 'angular' } }); + expect(captureExceptionSpy).toHaveBeenCalledWith(err, { + mechanism: { handled: false, type: 'auto.function.angular.error_handler' }, + }); }); it('extracts an instance of a class that does not extend Error and does not have an error-like shape', () => { @@ -251,7 +261,9 @@ describe('SentryErrorHandler', () => { createErrorHandler().handleError(err); expect(captureExceptionSpy).toHaveBeenCalledTimes(1); - expect(captureExceptionSpy).toHaveBeenCalledWith(ngErr, { mechanism: { handled: false, type: 'angular' } }); + expect(captureExceptionSpy).toHaveBeenCalledWith(ngErr, { + mechanism: { handled: false, type: 'auto.function.angular.error_handler' }, + }); }); it('extracts an `HttpErrorResponse` with `Error`', () => { @@ -261,7 +273,9 @@ describe('SentryErrorHandler', () => { createErrorHandler().handleError(err); expect(captureExceptionSpy).toHaveBeenCalledTimes(1); - expect(captureExceptionSpy).toHaveBeenCalledWith(httpErr, { mechanism: { handled: false, type: 'angular' } }); + expect(captureExceptionSpy).toHaveBeenCalledWith(httpErr, { + mechanism: { handled: false, type: 'auto.function.angular.error_handler' }, + }); }); it('extracts an `HttpErrorResponse` with `ErrorEvent`', () => { @@ -431,7 +445,9 @@ describe('SentryErrorHandler', () => { createErrorHandler().handleError(err); expect(captureExceptionSpy).toHaveBeenCalledTimes(1); - expect(captureExceptionSpy).toHaveBeenCalledWith(err, { mechanism: { handled: false, type: 'angular' } }); + expect(captureExceptionSpy).toHaveBeenCalledWith(err, { + mechanism: { handled: false, type: 'auto.function.angular.error_handler' }, + }); }); it('extracts an `HttpErrorResponse` with an instance of class not extending Error but that has an error-like shape', () => { @@ -441,7 +457,9 @@ describe('SentryErrorHandler', () => { createErrorHandler().handleError(err); expect(captureExceptionSpy).toHaveBeenCalledTimes(1); - expect(captureExceptionSpy).toHaveBeenCalledWith(innerErr, { mechanism: { handled: false, type: 'angular' } }); + expect(captureExceptionSpy).toHaveBeenCalledWith(innerErr, { + mechanism: { handled: false, type: 'auto.function.angular.error_handler' }, + }); }); it('extracts an `HttpErrorResponse` with an instance of a class that does not extend Error and does not have an error-like shape', () => { From cd706efe6b85dc18acab29555bbc78c4c39682f1 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 12 Sep 2025 17:27:42 +0200 Subject: [PATCH 37/61] chore(deps): bump next from 14.2.30 to 14.2.32 in /dev-packages/e2e-tests/test-applications/nextjs-14 (#17628) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Bumps [next](https://github.com/vercel/next.js) from 14.2.30 to 14.2.32.
Release notes

Sourced from next's releases.

v14.2.32

[!NOTE]
This release is backporting bug fixes. It does not include all pending features/changes on canary.

Core Changes

  • fix router handling when setting a location response header #82588

Credits

Huge thanks to @​ztanner for helping!

v14.2.31

[!NOTE]
This release is backporting bug fixes. It does not include all pending features/changes on canary.

Core Changes

  • fix(next/image): improve and simplify detect-content-type (#82179)
  • fix(next/image): fix image-optimizer.ts headers (#82178)

Credits

Huge thanks to @​styfle and @​ztanner for helping!

Commits
  • 89ee561 v14.2.32
  • 6a974ad [backport v14]: fix router handling when setting a location response header (...
  • 55f7662 v14.2.31
  • 5dd68a5 [backport v14]: fix(next/image): improve and simplify detect-content-type (#8...
  • bcc7c65 [backport v14]: fix(next/image): fix image-optimizer.ts headers (#82114) (#82...
  • See full diff in compare view

[![Dependabot compatibility score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=next&package-manager=npm_and_yarn&previous-version=14.2.30&new-version=14.2.32)](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores) Dependabot will resolve any conflicts with this PR as long as you don't alter it yourself. You can also trigger a rebase manually by commenting `@dependabot rebase`. [//]: # (dependabot-automerge-start) [//]: # (dependabot-automerge-end) ---
Dependabot commands and options
You can trigger Dependabot actions by commenting on this PR: - `@dependabot rebase` will rebase this PR - `@dependabot recreate` will recreate this PR, overwriting any edits that have been made to it - `@dependabot merge` will merge this PR after your CI passes on it - `@dependabot squash and merge` will squash and merge this PR after your CI passes on it - `@dependabot cancel merge` will cancel a previously requested merge and block automerging - `@dependabot reopen` will reopen this PR if it is closed - `@dependabot close` will close this PR and stop Dependabot recreating it. You can achieve the same result by closing it manually - `@dependabot show ignore conditions` will show all of the ignore conditions of the specified dependency - `@dependabot ignore this major version` will close this PR and stop Dependabot creating any more for this major version (unless you reopen the PR or upgrade to it yourself) - `@dependabot ignore this minor version` will close this PR and stop Dependabot creating any more for this minor version (unless you reopen the PR or upgrade to it yourself) - `@dependabot ignore this dependency` will close this PR and stop Dependabot creating any more for this dependency (unless you reopen the PR or upgrade to it yourself) You can disable automated security fix PRs for this repo from the [Security Alerts page](https://github.com/getsentry/sentry-javascript/network/alerts).
Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- dev-packages/e2e-tests/test-applications/nextjs-14/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dev-packages/e2e-tests/test-applications/nextjs-14/package.json b/dev-packages/e2e-tests/test-applications/nextjs-14/package.json index f2dcfee9d43b..fc6b31591bfa 100644 --- a/dev-packages/e2e-tests/test-applications/nextjs-14/package.json +++ b/dev-packages/e2e-tests/test-applications/nextjs-14/package.json @@ -17,7 +17,7 @@ "@types/node": "^18.19.1", "@types/react": "18.0.26", "@types/react-dom": "18.0.9", - "next": "14.2.30", + "next": "14.2.32", "react": "18.2.0", "react-dom": "18.2.0", "typescript": "~5.0.0" From 7c41f03e1aeac2f70b22107c2691d23271c3ee28 Mon Sep 17 00:00:00 2001 From: Lukas Stracke Date: Fri, 12 Sep 2025 17:34:09 +0200 Subject: [PATCH 38/61] feat(browser): Add timing and status atttributes to resource spans (#17562) Adds a few attributes to `resource.*` spans for request timing and status information. Most importantly: - `http.request.time_to_first_byte` [from `responseStart`](https://developer.mozilla.org/en-US/docs/Web/API/PerformanceResourceTiming/responseStart) which can be [interpreted as TTFB](https://web.dev/articles/ttfb#measure-resource-requests) for resource requests. This was requested via via https://github.com/getsentry/sentry/issues/63739. - `http.response.status_code` the status code of the resource request. Requested in #16805 and #10995 To get these attributes, I adjusted the already existing `resourceTimingToSpanAttributes` a bit: - Moved it from browser to browser-utils because resource spans logic is located in browser-utils (this is safe, it never was exported from browser) - Changed the signature to return an object instead of an array of attributes. This is more ergonomical to use in both callsites and should reduce bundle size slightly - Added `http.request.redirect_end`, `http.request.worker_start` attributes which are stable timing values available on `PerformanceResourceTiming` but were previously missing - Also added to conventions: https://github.com/getsentry/sentry-conventions/pull/130 - Added `http.request.time_to_first_byte`. Decided to add this attribute because `http.request.response_start` cannot be directly used as TTFB as its value is an absolute time stamp of `responseStart`. Instead the TTFB attribute is the relative response start attribute converted to seconds (happy to change to ms if reviewers prefer). - Also added to conventions: https://github.com/getsentry/sentry-conventions/pull/131 Other consequences: - `http.client` spans now also have the three additional attributes Remarks: - Not super happy about us always defaulting to `0` in case a value is not present. But decided to leave this as-is to avoid any behaviour change for http.client spans. --- .size-limit.js | 2 +- .../http-timings/test.ts | 5 +- .../metrics/pageload-resource-spans/test.ts | 50 +++- packages/browser-utils/src/index.ts | 2 + .../src/metrics/browserMetrics.ts | 93 +++--- .../src/metrics/resourceTiming.ts | 60 ++++ .../test/browser/browserMetrics.test.ts | 112 ++++++- .../metrics/elementTiming.test.ts | 6 +- .../{instrument => }/metrics/inpt.test.ts | 6 +- .../test/metrics/resourceTiming.test.ts} | 283 ++++++++++-------- packages/browser/src/tracing/request.ts | 5 +- .../browser/src/tracing/resource-timing.ts | 41 --- 12 files changed, 434 insertions(+), 231 deletions(-) create mode 100644 packages/browser-utils/src/metrics/resourceTiming.ts rename packages/browser-utils/test/{instrument => }/metrics/elementTiming.test.ts (98%) rename packages/browser-utils/test/{instrument => }/metrics/inpt.test.ts (95%) rename packages/{browser/test/tracing/resource-timing.test.ts => browser-utils/test/metrics/resourceTiming.test.ts} (60%) delete mode 100644 packages/browser/src/tracing/resource-timing.ts diff --git a/.size-limit.js b/.size-limit.js index 8eef4950f00d..32d5d19e1495 100644 --- a/.size-limit.js +++ b/.size-limit.js @@ -135,7 +135,7 @@ module.exports = [ path: 'packages/vue/build/esm/index.js', import: createImport('init', 'browserTracingIntegration'), gzip: true, - limit: '42 KB', + limit: '43 KB', }, // Svelte SDK (ESM) { diff --git a/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/http-timings/test.ts b/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/http-timings/test.ts index f47552f5e9b6..633be5f570b5 100644 --- a/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/http-timings/test.ts +++ b/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/http-timings/test.ts @@ -3,7 +3,7 @@ import type { Event } from '@sentry/core'; import { sentryTest } from '../../../../utils/fixtures'; import { getMultipleSentryEnvelopeRequests, shouldSkipTracingTest } from '../../../../utils/helpers'; -sentryTest('should create fetch spans with http timing @firefox', async ({ browserName, getLocalTestUrl, page }) => { +sentryTest('creates fetch spans with http timing', async ({ browserName, getLocalTestUrl, page }) => { const supportedBrowsers = ['chromium', 'firefox']; if (shouldSkipTracingTest() || !supportedBrowsers.includes(browserName)) { @@ -40,6 +40,8 @@ sentryTest('should create fetch spans with http timing @firefox', async ({ brows trace_id: tracingEvent.contexts?.trace?.trace_id, data: expect.objectContaining({ 'http.request.redirect_start': expect.any(Number), + 'http.request.redirect_end': expect.any(Number), + 'http.request.worker_start': expect.any(Number), 'http.request.fetch_start': expect.any(Number), 'http.request.domain_lookup_start': expect.any(Number), 'http.request.domain_lookup_end': expect.any(Number), @@ -49,6 +51,7 @@ sentryTest('should create fetch spans with http timing @firefox', async ({ brows 'http.request.request_start': expect.any(Number), 'http.request.response_start': expect.any(Number), 'http.request.response_end': expect.any(Number), + 'http.request.time_to_first_byte': expect.any(Number), 'network.protocol.version': expect.any(String), }), }), diff --git a/dev-packages/browser-integration-tests/suites/tracing/metrics/pageload-resource-spans/test.ts b/dev-packages/browser-integration-tests/suites/tracing/metrics/pageload-resource-spans/test.ts index 38c7e61ff541..f748c339ce14 100644 --- a/dev-packages/browser-integration-tests/suites/tracing/metrics/pageload-resource-spans/test.ts +++ b/dev-packages/browser-integration-tests/suites/tracing/metrics/pageload-resource-spans/test.ts @@ -4,7 +4,7 @@ import { type Event, SEMANTIC_ATTRIBUTE_SENTRY_OP, SEMANTIC_ATTRIBUTE_SENTRY_ORI import { sentryTest } from '../../../../utils/fixtures'; import { getFirstSentryEnvelopeRequest, shouldSkipTracingTest } from '../../../../utils/helpers'; -sentryTest('should add resource spans to pageload transaction', async ({ getLocalTestUrl, page, browserName }) => { +sentryTest('adds resource spans to pageload transaction', async ({ getLocalTestUrl, page, browserName }) => { if (shouldSkipTracingTest()) { sentryTest.skip(); } @@ -74,6 +74,19 @@ sentryTest('should add resource spans to pageload transaction', async ({ getLoca 'http.decoded_response_content_length': expect.any(Number), 'http.response_content_length': expect.any(Number), 'http.response_transfer_size': expect.any(Number), + 'http.request.connect_start': expect.any(Number), + 'http.request.connection_end': expect.any(Number), + 'http.request.domain_lookup_end': expect.any(Number), + 'http.request.domain_lookup_start': expect.any(Number), + 'http.request.fetch_start': expect.any(Number), + 'http.request.redirect_end': expect.any(Number), + 'http.request.redirect_start': expect.any(Number), + 'http.request.request_start': expect.any(Number), + 'http.request.secure_connection_start': expect.any(Number), + 'http.request.worker_start': expect.any(Number), + 'http.request.response_end': expect.any(Number), + 'http.request.response_start': expect.any(Number), + 'http.request.time_to_first_byte': expect.any(Number), 'network.protocol.name': '', 'network.protocol.version': 'unknown', [SEMANTIC_ATTRIBUTE_SENTRY_OP]: 'resource.img', @@ -82,6 +95,7 @@ sentryTest('should add resource spans to pageload transaction', async ({ getLoca 'url.same_origin': false, 'url.scheme': 'https', ...(!isWebkitRun && { + 'http.response.status_code': expect.any(Number), 'resource.render_blocking_status': 'non-blocking', 'http.response_delivery_type': '', }), @@ -96,11 +110,30 @@ sentryTest('should add resource spans to pageload transaction', async ({ getLoca trace_id: traceId, }); + // range check: TTFB must be >0 (at least in this case) and it's reasonable to + // assume <10 seconds. This also tests that we're reporting TTFB in seconds. + const imgSpanTtfb = imgSpan?.data['http.request.time_to_first_byte']; + expect(imgSpanTtfb).toBeGreaterThan(0); + expect(imgSpanTtfb).toBeLessThan(10); + expect(linkSpan).toEqual({ data: { 'http.decoded_response_content_length': expect.any(Number), 'http.response_content_length': expect.any(Number), 'http.response_transfer_size': expect.any(Number), + 'http.request.connect_start': expect.any(Number), + 'http.request.connection_end': expect.any(Number), + 'http.request.domain_lookup_end': expect.any(Number), + 'http.request.domain_lookup_start': expect.any(Number), + 'http.request.fetch_start': expect.any(Number), + 'http.request.redirect_end': expect.any(Number), + 'http.request.redirect_start': expect.any(Number), + 'http.request.request_start': expect.any(Number), + 'http.request.secure_connection_start': expect.any(Number), + 'http.request.worker_start': expect.any(Number), + 'http.request.response_end': expect.any(Number), + 'http.request.response_start': expect.any(Number), + 'http.request.time_to_first_byte': expect.any(Number), 'network.protocol.name': '', 'network.protocol.version': 'unknown', [SEMANTIC_ATTRIBUTE_SENTRY_OP]: 'resource.link', @@ -109,6 +142,7 @@ sentryTest('should add resource spans to pageload transaction', async ({ getLoca 'url.same_origin': false, 'url.scheme': 'https', ...(!isWebkitRun && { + 'http.response.status_code': expect.any(Number), 'resource.render_blocking_status': 'non-blocking', 'http.response_delivery_type': '', }), @@ -128,6 +162,19 @@ sentryTest('should add resource spans to pageload transaction', async ({ getLoca 'http.decoded_response_content_length': expect.any(Number), 'http.response_content_length': expect.any(Number), 'http.response_transfer_size': expect.any(Number), + 'http.request.connection_end': expect.any(Number), + 'http.request.connect_start': expect.any(Number), + 'http.request.domain_lookup_end': expect.any(Number), + 'http.request.domain_lookup_start': expect.any(Number), + 'http.request.fetch_start': expect.any(Number), + 'http.request.redirect_end': expect.any(Number), + 'http.request.redirect_start': expect.any(Number), + 'http.request.request_start': expect.any(Number), + 'http.request.secure_connection_start': expect.any(Number), + 'http.request.worker_start': expect.any(Number), + 'http.request.response_end': expect.any(Number), + 'http.request.response_start': expect.any(Number), + 'http.request.time_to_first_byte': expect.any(Number), 'network.protocol.name': '', 'network.protocol.version': 'unknown', 'sentry.op': 'resource.script', @@ -136,6 +183,7 @@ sentryTest('should add resource spans to pageload transaction', async ({ getLoca 'url.same_origin': false, 'url.scheme': 'https', ...(!isWebkitRun && { + 'http.response.status_code': expect.any(Number), 'resource.render_blocking_status': 'non-blocking', 'http.response_delivery_type': '', }), diff --git a/packages/browser-utils/src/index.ts b/packages/browser-utils/src/index.ts index bf6605f3f399..accf3cb3a278 100644 --- a/packages/browser-utils/src/index.ts +++ b/packages/browser-utils/src/index.ts @@ -30,4 +30,6 @@ export { addXhrInstrumentationHandler, SENTRY_XHR_DATA_KEY } from './instrument/ export { getBodyString, getFetchRequestArgBody, serializeFormData } from './networkUtils'; +export { resourceTimingToSpanAttributes } from './metrics/resourceTiming'; + export type { FetchHint, NetworkMetaWarning, XhrHint } from './types'; diff --git a/packages/browser-utils/src/metrics/browserMetrics.ts b/packages/browser-utils/src/metrics/browserMetrics.ts index 870558ada39d..8b1592408e8a 100644 --- a/packages/browser-utils/src/metrics/browserMetrics.ts +++ b/packages/browser-utils/src/metrics/browserMetrics.ts @@ -22,13 +22,8 @@ import { addTtfbInstrumentationHandler, } from './instrument'; import { trackLcpAsStandaloneSpan } from './lcp'; -import { - extractNetworkProtocol, - getBrowserPerformanceAPI, - isMeasurementValue, - msToSec, - startAndEndSpan, -} from './utils'; +import { resourceTimingToSpanAttributes } from './resourceTiming'; +import { getBrowserPerformanceAPI, isMeasurementValue, msToSec, startAndEndSpan } from './utils'; import { getActivationStart } from './web-vitals/lib/getActivationStart'; import { getNavigationEntry } from './web-vitals/lib/getNavigationEntry'; import { getVisibilityWatcher } from './web-vitals/lib/getVisibilityWatcher'; @@ -637,7 +632,7 @@ export function _addResourceSpans( startTime: number, duration: number, timeOrigin: number, - ignoreResourceSpans?: Array, + ignoredResourceSpanOps?: Array, ): void { // we already instrument based on fetch and xhr, so we don't need to // duplicate spans here. @@ -646,31 +641,15 @@ export function _addResourceSpans( } const op = entry.initiatorType ? `resource.${entry.initiatorType}` : 'resource.other'; - if (ignoreResourceSpans?.includes(op)) { + if (ignoredResourceSpanOps?.includes(op)) { return; } - const parsedUrl = parseUrl(resourceUrl); - const attributes: SpanAttributes = { [SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: 'auto.resource.browser.metrics', }; - setResourceEntrySizeData(attributes, entry, 'transferSize', 'http.response_transfer_size'); - setResourceEntrySizeData(attributes, entry, 'encodedBodySize', 'http.response_content_length'); - setResourceEntrySizeData(attributes, entry, 'decodedBodySize', 'http.decoded_response_content_length'); - - // `deliveryType` is experimental and does not exist everywhere - const deliveryType = (entry as { deliveryType?: 'cache' | 'navigational-prefetch' | '' }).deliveryType; - if (deliveryType != null) { - attributes['http.response_delivery_type'] = deliveryType; - } - // Types do not reflect this property yet - const renderBlockingStatus = (entry as { renderBlockingStatus?: 'render-blocking' | 'non-render-blocking' }) - .renderBlockingStatus; - if (renderBlockingStatus) { - attributes['resource.render_blocking_status'] = renderBlockingStatus; - } + const parsedUrl = parseUrl(resourceUrl); if (parsedUrl.protocol) { attributes['url.scheme'] = parsedUrl.protocol.split(':').pop(); // the protocol returned by parseUrl includes a :, but OTEL spec does not, so we remove it. @@ -682,13 +661,22 @@ export function _addResourceSpans( attributes['url.same_origin'] = resourceUrl.includes(WINDOW.location.origin); - // Checking for only `undefined` and `null` is intentional because it's - // valid for `nextHopProtocol` to be an empty string. - if (entry.nextHopProtocol != null) { - const { name, version } = extractNetworkProtocol(entry.nextHopProtocol); - attributes['network.protocol.name'] = name; - attributes['network.protocol.version'] = version; - } + _setResourceRequestAttributes(entry, attributes, [ + // https://developer.mozilla.org/en-US/docs/Web/API/PerformanceResourceTiming/responseStatus + ['responseStatus', 'http.response.status_code'], + + ['transferSize', 'http.response_transfer_size'], + ['encodedBodySize', 'http.response_content_length'], + ['decodedBodySize', 'http.decoded_response_content_length'], + + // https://developer.mozilla.org/en-US/docs/Web/API/PerformanceResourceTiming/renderBlockingStatus + ['renderBlockingStatus', 'resource.render_blocking_status'], + + // https://developer.mozilla.org/en-US/docs/Web/API/PerformanceResourceTiming/deliveryType + ['deliveryType', 'http.response_delivery_type'], + ]); + + const attributesWithResourceTiming: SpanAttributes = { ...attributes, ...resourceTimingToSpanAttributes(entry) }; const startTimestamp = timeOrigin + startTime; const endTimestamp = startTimestamp + duration; @@ -696,7 +684,7 @@ export function _addResourceSpans( startAndEndSpan(span, startTimestamp, endTimestamp, { name: resourceUrl.replace(WINDOW.location.origin, ''), op, - attributes, + attributes: attributesWithResourceTiming, }); } @@ -776,16 +764,37 @@ function _setWebVitalAttributes(span: Span, options: AddPerformanceEntriesOption } } -function setResourceEntrySizeData( +type ExperimentalResourceTimingProperty = + | 'renderBlockingStatus' + | 'deliveryType' + // For some reason, TS during build, errors on `responseStatus` not being a property of + // PerformanceResourceTiming while it actually is. Hence, we're adding it here. + // Perhaps because response status is not yet available in Webkit/Safari. + // https://developer.mozilla.org/en-US/docs/Web/API/PerformanceResourceTiming/responseStatus + | 'responseStatus'; + +/** + * Use this to set any attributes we can take directly form the PerformanceResourceTiming entry. + * + * This is just a mapping function for entry->attribute to keep bundle-size minimal. + * Experimental properties are also accepted (see {@link ExperimentalResourceTimingProperty}). + * Assumes that all entry properties might be undefined for browser-specific differences. + * Only accepts string and number values for now and also sets 0-values. + */ +export function _setResourceRequestAttributes( + entry: Partial & Partial>, attributes: SpanAttributes, - entry: PerformanceResourceTiming, - key: keyof Pick, - dataKey: 'http.response_transfer_size' | 'http.response_content_length' | 'http.decoded_response_content_length', + properties: [keyof PerformanceResourceTiming | ExperimentalResourceTimingProperty, string][], ): void { - const entryVal = entry[key]; - if (entryVal != null && entryVal < MAX_INT_AS_BYTES) { - attributes[dataKey] = entryVal; - } + properties.forEach(([entryKey, attributeKey]) => { + const entryVal = entry[entryKey]; + if ( + entryVal != null && + ((typeof entryVal === 'number' && entryVal < MAX_INT_AS_BYTES) || typeof entryVal === 'string') + ) { + attributes[attributeKey] = entryVal; + } + }); } /** diff --git a/packages/browser-utils/src/metrics/resourceTiming.ts b/packages/browser-utils/src/metrics/resourceTiming.ts new file mode 100644 index 000000000000..fe613355c55d --- /dev/null +++ b/packages/browser-utils/src/metrics/resourceTiming.ts @@ -0,0 +1,60 @@ +import type { SpanAttributes } from '@sentry/core'; +import { browserPerformanceTimeOrigin } from '@sentry/core'; +import { extractNetworkProtocol, getBrowserPerformanceAPI } from './utils'; + +function getAbsoluteTime(time = 0): number { + return ((browserPerformanceTimeOrigin() || performance.timeOrigin) + time) / 1000; +} + +/** + * Converts a PerformanceResourceTiming entry to span data for the resource span. Most importantly, + * it converts the timing values from timestamps relative to the `performance.timeOrigin` to absolute timestamps + * in seconds. + * + * @see https://developer.mozilla.org/en-US/docs/Web/API/PerformanceResourceTiming#timestamps + * + * @param resourceTiming + * @returns An array where the first element is the attribute name and the second element is the attribute value. + */ +export function resourceTimingToSpanAttributes(resourceTiming: PerformanceResourceTiming): SpanAttributes { + const timingSpanData: SpanAttributes = {}; + // Checking for only `undefined` and `null` is intentional because it's + // valid for `nextHopProtocol` to be an empty string. + if (resourceTiming.nextHopProtocol != undefined) { + const { name, version } = extractNetworkProtocol(resourceTiming.nextHopProtocol); + timingSpanData['network.protocol.version'] = version; + timingSpanData['network.protocol.name'] = name; + } + + if (!(browserPerformanceTimeOrigin() || getBrowserPerformanceAPI()?.timeOrigin)) { + return timingSpanData; + } + + return { + ...timingSpanData, + + 'http.request.redirect_start': getAbsoluteTime(resourceTiming.redirectStart), + 'http.request.redirect_end': getAbsoluteTime(resourceTiming.redirectEnd), + + 'http.request.worker_start': getAbsoluteTime(resourceTiming.workerStart), + + 'http.request.fetch_start': getAbsoluteTime(resourceTiming.fetchStart), + + 'http.request.domain_lookup_start': getAbsoluteTime(resourceTiming.domainLookupStart), + 'http.request.domain_lookup_end': getAbsoluteTime(resourceTiming.domainLookupEnd), + + 'http.request.connect_start': getAbsoluteTime(resourceTiming.connectStart), + 'http.request.secure_connection_start': getAbsoluteTime(resourceTiming.secureConnectionStart), + 'http.request.connection_end': getAbsoluteTime(resourceTiming.connectEnd), + + 'http.request.request_start': getAbsoluteTime(resourceTiming.requestStart), + + 'http.request.response_start': getAbsoluteTime(resourceTiming.responseStart), + 'http.request.response_end': getAbsoluteTime(resourceTiming.responseEnd), + + // For TTFB we actually want the relative time from timeOrigin to responseStart + // This way, TTFB always measures the "first page load" experience. + // see: https://web.dev/articles/ttfb#measure-resource-requests + 'http.request.time_to_first_byte': (resourceTiming.responseStart ?? 0) / 1000, + }; +} diff --git a/packages/browser-utils/test/browser/browserMetrics.test.ts b/packages/browser-utils/test/browser/browserMetrics.test.ts index 50dcfd65a528..c717bb81ca0b 100644 --- a/packages/browser-utils/test/browser/browserMetrics.test.ts +++ b/packages/browser-utils/test/browser/browserMetrics.test.ts @@ -1,4 +1,4 @@ -import type { Span } from '@sentry/core'; +import type { Span, SpanAttributes } from '@sentry/core'; import { getClient, getCurrentScope, @@ -10,7 +10,12 @@ import { spanToJSON, } from '@sentry/core'; import { afterAll, beforeAll, beforeEach, describe, expect, it } from 'vitest'; -import { _addMeasureSpans, _addNavigationSpans, _addResourceSpans } from '../../src/metrics/browserMetrics'; +import { + _addMeasureSpans, + _addNavigationSpans, + _addResourceSpans, + _setResourceRequestAttributes, +} from '../../src/metrics/browserMetrics'; import { WINDOW } from '../../src/types'; import { getDefaultClientOptions, TestClient } from '../utils/TestClient'; @@ -289,6 +294,19 @@ describe('_addResourceSpans', () => { ['url.same_origin']: true, ['network.protocol.name']: 'http', ['network.protocol.version']: '1.1', + 'http.request.connect_start': expect.any(Number), + 'http.request.connection_end': expect.any(Number), + 'http.request.domain_lookup_end': expect.any(Number), + 'http.request.domain_lookup_start': expect.any(Number), + 'http.request.fetch_start': expect.any(Number), + 'http.request.redirect_end': expect.any(Number), + 'http.request.redirect_start': expect.any(Number), + 'http.request.request_start': expect.any(Number), + 'http.request.response_end': expect.any(Number), + 'http.request.response_start': expect.any(Number), + 'http.request.secure_connection_start': expect.any(Number), + 'http.request.time_to_first_byte': 0, + 'http.request.worker_start': expect.any(Number), }, }), ); @@ -404,7 +422,7 @@ describe('_addResourceSpans', () => { expect(spans).toHaveLength(1); expect(spanToJSON(spans[0]!)).toEqual( expect.objectContaining({ - data: { + data: expect.objectContaining({ [SEMANTIC_ATTRIBUTE_SENTRY_OP]: 'resource.css', [SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: 'auto.resource.browser.metrics', ['http.decoded_response_content_length']: entry.decodedBodySize, @@ -416,7 +434,7 @@ describe('_addResourceSpans', () => { ['url.same_origin']: true, ['network.protocol.name']: 'http', ['network.protocol.version']: '2', - }, + }), }), ); }); @@ -441,7 +459,7 @@ describe('_addResourceSpans', () => { expect(spans).toHaveLength(1); expect(spanToJSON(spans[0]!)).toEqual( expect.objectContaining({ - data: { + data: expect.objectContaining({ [SEMANTIC_ATTRIBUTE_SENTRY_OP]: 'resource.css', [SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: 'auto.resource.browser.metrics', 'server.address': 'example.com', @@ -449,7 +467,7 @@ describe('_addResourceSpans', () => { 'url.scheme': 'https', ['network.protocol.name']: 'http', ['network.protocol.version']: '3', - }, + }), description: '/assets/to/css', timestamp: 468, op: 'resource.css', @@ -489,6 +507,19 @@ describe('_addResourceSpans', () => { 'url.scheme': 'https', ['network.protocol.name']: 'http', ['network.protocol.version']: '3', + 'http.request.connect_start': expect.any(Number), + 'http.request.connection_end': expect.any(Number), + 'http.request.domain_lookup_end': expect.any(Number), + 'http.request.domain_lookup_start': expect.any(Number), + 'http.request.fetch_start': expect.any(Number), + 'http.request.redirect_end': expect.any(Number), + 'http.request.redirect_start': expect.any(Number), + 'http.request.request_start': expect.any(Number), + 'http.request.response_end': expect.any(Number), + 'http.request.response_start': expect.any(Number), + 'http.request.secure_connection_start': expect.any(Number), + 'http.request.time_to_first_byte': 0, + 'http.request.worker_start': expect.any(Number), }, description: '/assets/to/css', timestamp: 468, @@ -709,6 +740,75 @@ describe('_addNavigationSpans', () => { }); }); +describe('_setResourceRequestAttributes', () => { + it('sets resource request attributes', () => { + const attributes: SpanAttributes = {}; + + const entry = mockPerformanceResourceTiming({ + transferSize: 0, + deliveryType: 'cache', + renderBlockingStatus: 'non-blocking', + responseStatus: 200, + redirectStart: 100, + responseStart: 200, + }); + + _setResourceRequestAttributes(entry, attributes, [ + ['transferSize', 'http.response_transfer_size'], + ['deliveryType', 'http.response_delivery_type'], + ['renderBlockingStatus', 'resource.render_blocking_status'], + ['responseStatus', 'http.response.status_code'], + ['redirectStart', 'http.request.redirect_start'], + ['responseStart', 'http.response.start'], + ]); + + expect(attributes).toEqual({ + 'http.response_transfer_size': 0, + 'http.request.redirect_start': 100, + 'http.response.start': 200, + 'http.response.status_code': 200, + 'http.response_delivery_type': 'cache', + 'resource.render_blocking_status': 'non-blocking', + }); + }); + + it("doesn't set other attributes", () => { + const attributes: SpanAttributes = {}; + + const entry = mockPerformanceResourceTiming({ + transferSize: 0, + deliveryType: 'cache', + renderBlockingStatus: 'non-blocking', + }); + + _setResourceRequestAttributes(entry, attributes, [['transferSize', 'http.response_transfer_size']]); + + expect(attributes).toEqual({ + 'http.response_transfer_size': 0, + }); + }); + + it("doesn't set non-primitive or undefined values", () => { + const attributes: SpanAttributes = {}; + + const entry = mockPerformanceResourceTiming({ + transferSize: undefined, + // @ts-expect-error null is invalid but let's test it anyway + deliveryType: null, + // @ts-expect-error object is invalid but let's test it anyway + renderBlockingStatus: { blocking: 'non-blocking' }, + }); + + _setResourceRequestAttributes(entry, attributes, [ + ['transferSize', 'http.response_transfer_size'], + ['deliveryType', 'http.response_delivery_type'], + ['renderBlockingStatus', 'resource.render_blocking_status'], + ]); + + expect(attributes).toEqual({}); + }); +}); + const setGlobalLocation = (location: Location) => { // @ts-expect-error need to delete this in order to set to new value delete WINDOW.location; diff --git a/packages/browser-utils/test/instrument/metrics/elementTiming.test.ts b/packages/browser-utils/test/metrics/elementTiming.test.ts similarity index 98% rename from packages/browser-utils/test/instrument/metrics/elementTiming.test.ts rename to packages/browser-utils/test/metrics/elementTiming.test.ts index 04456ceadc44..14431415873b 100644 --- a/packages/browser-utils/test/instrument/metrics/elementTiming.test.ts +++ b/packages/browser-utils/test/metrics/elementTiming.test.ts @@ -1,8 +1,8 @@ import * as sentryCore from '@sentry/core'; import { beforeEach, describe, expect, it, vi } from 'vitest'; -import { _onElementTiming, startTrackingElementTiming } from '../../../src/metrics/elementTiming'; -import * as browserMetricsInstrumentation from '../../../src/metrics/instrument'; -import * as browserMetricsUtils from '../../../src/metrics/utils'; +import { _onElementTiming, startTrackingElementTiming } from '../../src/metrics/elementTiming'; +import * as browserMetricsInstrumentation from '../../src/metrics/instrument'; +import * as browserMetricsUtils from '../../src/metrics/utils'; describe('_onElementTiming', () => { const spanEndSpy = vi.fn(); diff --git a/packages/browser-utils/test/instrument/metrics/inpt.test.ts b/packages/browser-utils/test/metrics/inpt.test.ts similarity index 95% rename from packages/browser-utils/test/instrument/metrics/inpt.test.ts rename to packages/browser-utils/test/metrics/inpt.test.ts index 437ae650d0fe..bfa44b17a5b4 100644 --- a/packages/browser-utils/test/instrument/metrics/inpt.test.ts +++ b/packages/browser-utils/test/metrics/inpt.test.ts @@ -1,8 +1,8 @@ import { afterEach } from 'node:test'; import { describe, expect, it, vi } from 'vitest'; -import { _onInp, _trackINP } from '../../../src/metrics/inp'; -import * as instrument from '../../../src/metrics/instrument'; -import * as utils from '../../../src/metrics/utils'; +import { _onInp, _trackINP } from '../../src/metrics/inp'; +import * as instrument from '../../src/metrics/instrument'; +import * as utils from '../../src/metrics/utils'; describe('_trackINP', () => { const addInpInstrumentationHandler = vi.spyOn(instrument, 'addInpInstrumentationHandler'); diff --git a/packages/browser/test/tracing/resource-timing.test.ts b/packages/browser-utils/test/metrics/resourceTiming.test.ts similarity index 60% rename from packages/browser/test/tracing/resource-timing.test.ts rename to packages/browser-utils/test/metrics/resourceTiming.test.ts index c3ad8b9c0c85..881a7075441e 100644 --- a/packages/browser/test/tracing/resource-timing.test.ts +++ b/packages/browser-utils/test/metrics/resourceTiming.test.ts @@ -1,8 +1,8 @@ import * as utils from '@sentry/core'; -import * as browserUtils from '@sentry-internal/browser-utils'; import type { MockInstance } from 'vitest'; import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'; -import { resourceTimingToSpanAttributes } from '../../src/tracing/resource-timing'; +import { resourceTimingToSpanAttributes } from '../../src/metrics/resourceTiming'; +import * as browserMetricsUtils from '../../src/metrics/utils'; describe('resourceTimingToSpanAttributes', () => { let browserPerformanceTimeOriginSpy: MockInstance; @@ -11,7 +11,7 @@ describe('resourceTimingToSpanAttributes', () => { beforeEach(() => { vi.clearAllMocks(); browserPerformanceTimeOriginSpy = vi.spyOn(utils, 'browserPerformanceTimeOrigin'); - extractNetworkProtocolSpy = vi.spyOn(browserUtils, 'extractNetworkProtocol'); + extractNetworkProtocolSpy = vi.spyOn(browserMetricsUtils, 'extractNetworkProtocol'); }); afterEach(() => { @@ -48,7 +48,7 @@ describe('resourceTimingToSpanAttributes', () => { }; describe('with network protocol information', () => { - it('should extract network protocol when nextHopProtocol is available', () => { + it('extracts network protocol when nextHopProtocol is available', () => { const mockResourceTiming = createMockResourceTiming({ nextHopProtocol: 'h2', }); @@ -70,16 +70,16 @@ describe('resourceTimingToSpanAttributes', () => { const result = resourceTimingToSpanAttributes(mockResourceTiming); expect(extractNetworkProtocolSpy).toHaveBeenCalledWith('h2'); - expect(result).toEqual([ - ['network.protocol.version', '2.0'], - ['network.protocol.name', 'http'], - ]); + expect(result).toEqual({ + 'network.protocol.version': '2.0', + 'network.protocol.name': 'http', + }); // Restore global performance global.performance = originalPerformance; }); - it('should handle different network protocols', () => { + it('handles different network protocols', () => { const mockResourceTiming = createMockResourceTiming({ nextHopProtocol: 'http/1.1', }); @@ -101,16 +101,16 @@ describe('resourceTimingToSpanAttributes', () => { const result = resourceTimingToSpanAttributes(mockResourceTiming); expect(extractNetworkProtocolSpy).toHaveBeenCalledWith('http/1.1'); - expect(result).toEqual([ - ['network.protocol.version', '1.1'], - ['network.protocol.name', 'http'], - ]); + expect(result).toEqual({ + 'network.protocol.version': '1.1', + 'network.protocol.name': 'http', + }); // Restore global performance global.performance = originalPerformance; }); - it('should extract network protocol even when nextHopProtocol is empty', () => { + it('extracts network protocol even when nextHopProtocol is empty', () => { const mockResourceTiming = createMockResourceTiming({ nextHopProtocol: '', }); @@ -132,16 +132,16 @@ describe('resourceTimingToSpanAttributes', () => { const result = resourceTimingToSpanAttributes(mockResourceTiming); expect(extractNetworkProtocolSpy).toHaveBeenCalledWith(''); - expect(result).toEqual([ - ['network.protocol.version', 'unknown'], - ['network.protocol.name', ''], - ]); + expect(result).toEqual({ + 'network.protocol.version': 'unknown', + 'network.protocol.name': '', + }); // Restore global performance global.performance = originalPerformance; }); - it('should not extract network protocol when nextHopProtocol is undefined', () => { + it("doesn't extract network protocol when nextHopProtocol is undefined", () => { const mockResourceTiming = createMockResourceTiming({ nextHopProtocol: undefined as any, }); @@ -158,7 +158,7 @@ describe('resourceTimingToSpanAttributes', () => { const result = resourceTimingToSpanAttributes(mockResourceTiming); expect(extractNetworkProtocolSpy).not.toHaveBeenCalled(); - expect(result).toEqual([]); + expect(result).toEqual({}); // Restore global performance global.performance = originalPerformance; @@ -166,7 +166,7 @@ describe('resourceTimingToSpanAttributes', () => { }); describe('without browserPerformanceTimeOrigin', () => { - it('should return only network protocol data when browserPerformanceTimeOrigin is not available', () => { + it('returns only network protocol data when browserPerformanceTimeOrigin is not available', () => { const mockResourceTiming = createMockResourceTiming({ nextHopProtocol: 'h2', }); @@ -187,16 +187,16 @@ describe('resourceTimingToSpanAttributes', () => { const result = resourceTimingToSpanAttributes(mockResourceTiming); - expect(result).toEqual([ - ['network.protocol.version', '2.0'], - ['network.protocol.name', 'http'], - ]); + expect(result).toEqual({ + 'network.protocol.version': '2.0', + 'network.protocol.name': 'http', + }); // Restore global performance global.performance = originalPerformance; }); - it('should return network protocol attributes even when empty string and no browserPerformanceTimeOrigin', () => { + it('returns network protocol attributes even when empty string and no browserPerformanceTimeOrigin', () => { const mockResourceTiming = createMockResourceTiming({ nextHopProtocol: '', }); @@ -217,10 +217,10 @@ describe('resourceTimingToSpanAttributes', () => { const result = resourceTimingToSpanAttributes(mockResourceTiming); - expect(result).toEqual([ - ['network.protocol.version', 'unknown'], - ['network.protocol.name', ''], - ]); + expect(result).toEqual({ + 'network.protocol.version': 'unknown', + 'network.protocol.name': '', + }); // Restore global performance global.performance = originalPerformance; @@ -232,10 +232,12 @@ describe('resourceTimingToSpanAttributes', () => { browserPerformanceTimeOriginSpy.mockReturnValue(1000000); // 1 second in milliseconds }); - it('should include all timing attributes when browserPerformanceTimeOrigin is available', () => { + it('includes all timing attributes when browserPerformanceTimeOrigin is available', () => { const mockResourceTiming = createMockResourceTiming({ nextHopProtocol: 'h2', redirectStart: 10, + redirectEnd: 20, + workerStart: 22, fetchStart: 25, domainLookupStart: 30, domainLookupEnd: 35, @@ -254,23 +256,26 @@ describe('resourceTimingToSpanAttributes', () => { const result = resourceTimingToSpanAttributes(mockResourceTiming); - expect(result).toEqual([ - ['network.protocol.version', '2.0'], - ['network.protocol.name', 'http'], - ['http.request.redirect_start', 1000.01], // (1000000 + 10) / 1000 - ['http.request.fetch_start', 1000.025], // (1000000 + 25) / 1000 - ['http.request.domain_lookup_start', 1000.03], // (1000000 + 30) / 1000 - ['http.request.domain_lookup_end', 1000.035], // (1000000 + 35) / 1000 - ['http.request.connect_start', 1000.04], // (1000000 + 40) / 1000 - ['http.request.secure_connection_start', 1000.045], // (1000000 + 45) / 1000 - ['http.request.connection_end', 1000.05], // (1000000 + 50) / 1000 - ['http.request.request_start', 1000.055], // (1000000 + 55) / 1000 - ['http.request.response_start', 1000.15], // (1000000 + 150) / 1000 - ['http.request.response_end', 1000.2], // (1000000 + 200) / 1000 - ]); + expect(result).toEqual({ + 'network.protocol.version': '2.0', + 'network.protocol.name': 'http', + 'http.request.redirect_start': 1000.01, // (1000000 + 10) / 1000 + 'http.request.redirect_end': 1000.02, // (1000000 + 20) / 1000 + 'http.request.worker_start': 1000.022, // (1000000 + 22) / 1000 + 'http.request.fetch_start': 1000.025, // (1000000 + 25) / 1000 + 'http.request.domain_lookup_start': 1000.03, // (1000000 + 30) / 1000 + 'http.request.domain_lookup_end': 1000.035, // (1000000 + 35) / 1000 + 'http.request.connect_start': 1000.04, // (1000000 + 40) / 1000 + 'http.request.secure_connection_start': 1000.045, // (1000000 + 45) / 1000 + 'http.request.connection_end': 1000.05, // (1000000 + 50) / 1000 + 'http.request.request_start': 1000.055, // (1000000 + 55) / 1000 + 'http.request.response_start': 1000.15, // (1000000 + 150) / 1000 + 'http.request.response_end': 1000.2, // (1000000 + 200) / 1000 + 'http.request.time_to_first_byte': 0.15, // 150 / 1000 + }); }); - it('should handle zero timing values', () => { + it('handles zero timing values', () => { extractNetworkProtocolSpy.mockReturnValue({ name: '', version: 'unknown', @@ -292,23 +297,26 @@ describe('resourceTimingToSpanAttributes', () => { const result = resourceTimingToSpanAttributes(mockResourceTiming); - expect(result).toEqual([ - ['network.protocol.version', 'unknown'], - ['network.protocol.name', ''], - ['http.request.redirect_start', 1000], // (1000000 + 0) / 1000 - ['http.request.fetch_start', 1000], - ['http.request.domain_lookup_start', 1000], - ['http.request.domain_lookup_end', 1000], - ['http.request.connect_start', 1000], - ['http.request.secure_connection_start', 1000], - ['http.request.connection_end', 1000], - ['http.request.request_start', 1000], - ['http.request.response_start', 1000], - ['http.request.response_end', 1000], - ]); + expect(result).toEqual({ + 'network.protocol.version': 'unknown', + 'network.protocol.name': '', + 'http.request.redirect_start': 1000, // (1000000 + 0) / 1000 + 'http.request.redirect_end': 1000.02, + 'http.request.worker_start': 1000, + 'http.request.fetch_start': 1000, + 'http.request.domain_lookup_start': 1000, + 'http.request.domain_lookup_end': 1000, + 'http.request.connect_start': 1000, + 'http.request.secure_connection_start': 1000, + 'http.request.connection_end': 1000, + 'http.request.request_start': 1000, + 'http.request.response_start': 1000, + 'http.request.response_end': 1000, + 'http.request.time_to_first_byte': 0, + }); }); - it('should combine network protocol and timing attributes', () => { + it('combines network protocol and timing attributes', () => { const mockResourceTiming = createMockResourceTiming({ nextHopProtocol: 'http/1.1', redirectStart: 5, @@ -330,25 +338,28 @@ describe('resourceTimingToSpanAttributes', () => { const result = resourceTimingToSpanAttributes(mockResourceTiming); - expect(result).toEqual([ - ['network.protocol.version', '1.1'], - ['network.protocol.name', 'http'], - ['http.request.redirect_start', 1000.005], - ['http.request.fetch_start', 1000.01], - ['http.request.domain_lookup_start', 1000.015], - ['http.request.domain_lookup_end', 1000.02], - ['http.request.connect_start', 1000.025], - ['http.request.secure_connection_start', 1000.03], - ['http.request.connection_end', 1000.035], - ['http.request.request_start', 1000.04], - ['http.request.response_start', 1000.08], - ['http.request.response_end', 1000.1], - ]); + expect(result).toEqual({ + 'network.protocol.version': '1.1', + 'network.protocol.name': 'http', + 'http.request.redirect_start': 1000.005, + 'http.request.redirect_end': 1000.02, + 'http.request.worker_start': 1000, + 'http.request.fetch_start': 1000.01, + 'http.request.domain_lookup_start': 1000.015, + 'http.request.domain_lookup_end': 1000.02, + 'http.request.connect_start': 1000.025, + 'http.request.secure_connection_start': 1000.03, + 'http.request.connection_end': 1000.035, + 'http.request.request_start': 1000.04, + 'http.request.response_start': 1000.08, + 'http.request.response_end': 1000.1, + 'http.request.time_to_first_byte': 0.08, + }); }); }); describe('fallback to performance.timeOrigin', () => { - it('should use performance.timeOrigin when browserPerformanceTimeOrigin returns null', () => { + it('uses performance.timeOrigin when browserPerformanceTimeOrigin returns null', () => { // Mock browserPerformanceTimeOrigin to return null for the main check browserPerformanceTimeOriginSpy.mockReturnValue(null); @@ -374,13 +385,13 @@ describe('resourceTimingToSpanAttributes', () => { const result = resourceTimingToSpanAttributes(mockResourceTiming); // When browserPerformanceTimeOrigin returns null, function returns early with only network protocol attributes - expect(result).toEqual([ - ['network.protocol.version', 'unknown'], - ['network.protocol.name', ''], - ]); + expect(result).toEqual({ + 'network.protocol.version': 'unknown', + 'network.protocol.name': '', + }); }); - it('should use performance.timeOrigin fallback in getAbsoluteTime when available', () => { + it('uses performance.timeOrigin fallback in getAbsoluteTime when available', () => { // Mock browserPerformanceTimeOrigin to return 500000 for the main check browserPerformanceTimeOriginSpy.mockReturnValue(500000); @@ -392,6 +403,8 @@ describe('resourceTimingToSpanAttributes', () => { const mockResourceTiming = createMockResourceTiming({ nextHopProtocol: '', redirectStart: 20, + redirectEnd: 30, + workerStart: 35, fetchStart: 40, domainLookupStart: 60, domainLookupEnd: 80, @@ -405,23 +418,26 @@ describe('resourceTimingToSpanAttributes', () => { const result = resourceTimingToSpanAttributes(mockResourceTiming); - expect(result).toEqual([ - ['network.protocol.version', 'unknown'], - ['network.protocol.name', ''], - ['http.request.redirect_start', 500.02], // (500000 + 20) / 1000 - ['http.request.fetch_start', 500.04], // (500000 + 40) / 1000 - ['http.request.domain_lookup_start', 500.06], // (500000 + 60) / 1000 - ['http.request.domain_lookup_end', 500.08], // (500000 + 80) / 1000 - ['http.request.connect_start', 500.1], // (500000 + 100) / 1000 - ['http.request.secure_connection_start', 500.12], // (500000 + 120) / 1000 - ['http.request.connection_end', 500.14], // (500000 + 140) / 1000 - ['http.request.request_start', 500.16], // (500000 + 160) / 1000 - ['http.request.response_start', 500.3], // (500000 + 300) / 1000 - ['http.request.response_end', 500.4], // (500000 + 400) / 1000 - ]); + expect(result).toEqual({ + 'network.protocol.version': 'unknown', + 'network.protocol.name': '', + 'http.request.redirect_start': 500.02, // (500000 + 20) / 1000 + 'http.request.redirect_end': 500.03, // (500000 + 30) / 1000 + 'http.request.worker_start': 500.035, // (500000 + 35) / 1000 + 'http.request.fetch_start': 500.04, // (500000 + 40) / 1000 + 'http.request.domain_lookup_start': 500.06, // (500000 + 60) / 1000 + 'http.request.domain_lookup_end': 500.08, // (500000 + 80) / 1000 + 'http.request.connect_start': 500.1, // (500000 + 100) / 1000 + 'http.request.secure_connection_start': 500.12, // (500000 + 120) / 1000 + 'http.request.connection_end': 500.14, // (500000 + 140) / 1000 + 'http.request.request_start': 500.16, // (500000 + 160) / 1000 + 'http.request.response_start': 500.3, // (500000 + 300) / 1000 + 'http.request.response_end': 500.4, // (500000 + 400) / 1000 + 'http.request.time_to_first_byte': 0.3, // 300 / 1000 + }); }); - it('should handle case when neither browserPerformanceTimeOrigin nor performance.timeOrigin is available', () => { + it('handles case when neither browserPerformanceTimeOrigin nor performance.timeOrigin is available', () => { browserPerformanceTimeOriginSpy.mockReturnValue(null); extractNetworkProtocolSpy.mockReturnValue({ @@ -443,10 +459,10 @@ describe('resourceTimingToSpanAttributes', () => { const result = resourceTimingToSpanAttributes(mockResourceTiming); // When neither timing source is available, should return network protocol attributes for empty string - expect(result).toEqual([ - ['network.protocol.version', 'unknown'], - ['network.protocol.name', ''], - ]); + expect(result).toEqual({ + 'network.protocol.version': 'unknown', + 'network.protocol.name': '', + }); // Restore global performance global.performance = originalPerformance; @@ -454,7 +470,7 @@ describe('resourceTimingToSpanAttributes', () => { }); describe('edge cases', () => { - it('should handle undefined timing values', () => { + it('handles undefined timing values', () => { browserPerformanceTimeOriginSpy.mockReturnValue(1000000); extractNetworkProtocolSpy.mockReturnValue({ @@ -466,6 +482,7 @@ describe('resourceTimingToSpanAttributes', () => { nextHopProtocol: '', redirectStart: undefined as any, fetchStart: undefined as any, + workerStart: undefined as any, domainLookupStart: undefined as any, domainLookupEnd: undefined as any, connectStart: undefined as any, @@ -478,23 +495,26 @@ describe('resourceTimingToSpanAttributes', () => { const result = resourceTimingToSpanAttributes(mockResourceTiming); - expect(result).toEqual([ - ['network.protocol.version', 'unknown'], - ['network.protocol.name', ''], - ['http.request.redirect_start', 1000], // (1000000 + 0) / 1000 - ['http.request.fetch_start', 1000], - ['http.request.domain_lookup_start', 1000], - ['http.request.domain_lookup_end', 1000], - ['http.request.connect_start', 1000], - ['http.request.secure_connection_start', 1000], - ['http.request.connection_end', 1000], - ['http.request.request_start', 1000], - ['http.request.response_start', 1000], - ['http.request.response_end', 1000], - ]); + expect(result).toEqual({ + 'network.protocol.version': 'unknown', + 'network.protocol.name': '', + 'http.request.redirect_start': 1000, // (1000000 + 0) / 1000 + 'http.request.redirect_end': 1000.02, + 'http.request.worker_start': 1000, + 'http.request.fetch_start': 1000, + 'http.request.domain_lookup_start': 1000, + 'http.request.domain_lookup_end': 1000, + 'http.request.connect_start': 1000, + 'http.request.secure_connection_start': 1000, + 'http.request.connection_end': 1000, + 'http.request.request_start': 1000, + 'http.request.response_start': 1000, + 'http.request.response_end': 1000, + 'http.request.time_to_first_byte': 0, + }); }); - it('should handle very large timing values', () => { + it('handles very large timing values', () => { browserPerformanceTimeOriginSpy.mockReturnValue(1000000); extractNetworkProtocolSpy.mockReturnValue({ @@ -518,20 +538,23 @@ describe('resourceTimingToSpanAttributes', () => { const result = resourceTimingToSpanAttributes(mockResourceTiming); - expect(result).toEqual([ - ['network.protocol.version', 'unknown'], - ['network.protocol.name', ''], - ['http.request.redirect_start', 1999.999], // (1000000 + 999999) / 1000 - ['http.request.fetch_start', 1999.999], - ['http.request.domain_lookup_start', 1999.999], - ['http.request.domain_lookup_end', 1999.999], - ['http.request.connect_start', 1999.999], - ['http.request.secure_connection_start', 1999.999], - ['http.request.connection_end', 1999.999], - ['http.request.request_start', 1999.999], - ['http.request.response_start', 1999.999], - ['http.request.response_end', 1999.999], - ]); + expect(result).toEqual({ + 'network.protocol.version': 'unknown', + 'network.protocol.name': '', + 'http.request.redirect_start': 1999.999, // (1000000 + 999999) / 1000 + 'http.request.redirect_end': 1000.02, + 'http.request.worker_start': 1000, + 'http.request.fetch_start': 1999.999, + 'http.request.domain_lookup_start': 1999.999, + 'http.request.domain_lookup_end': 1999.999, + 'http.request.connect_start': 1999.999, + 'http.request.secure_connection_start': 1999.999, + 'http.request.connection_end': 1999.999, + 'http.request.request_start': 1999.999, + 'http.request.response_start': 1999.999, + 'http.request.response_end': 1999.999, + 'http.request.time_to_first_byte': 999.999, + }); }); }); }); diff --git a/packages/browser/src/tracing/request.ts b/packages/browser/src/tracing/request.ts index d046793b42a1..1216c93ec9d3 100644 --- a/packages/browser/src/tracing/request.ts +++ b/packages/browser/src/tracing/request.ts @@ -22,11 +22,11 @@ import type { XhrHint } from '@sentry-internal/browser-utils'; import { addPerformanceInstrumentationHandler, addXhrInstrumentationHandler, + resourceTimingToSpanAttributes, SENTRY_XHR_DATA_KEY, } from '@sentry-internal/browser-utils'; import type { BrowserClient } from '../client'; import { WINDOW } from '../helpers'; -import { resourceTimingToSpanAttributes } from './resource-timing'; /** Options for Request Instrumentation */ export interface RequestInstrumentationOptions { @@ -249,8 +249,7 @@ function addHTTPTimings(span: Span): void { const cleanup = addPerformanceInstrumentationHandler('resource', ({ entries }) => { entries.forEach(entry => { if (isPerformanceResourceTiming(entry) && entry.name.endsWith(url)) { - const spanAttributes = resourceTimingToSpanAttributes(entry); - spanAttributes.forEach(attributeArray => span.setAttribute(...attributeArray)); + span.setAttributes(resourceTimingToSpanAttributes(entry)); // In the next tick, clean this handler up // We have to wait here because otherwise this cleans itself up before it is fully done setTimeout(cleanup); diff --git a/packages/browser/src/tracing/resource-timing.ts b/packages/browser/src/tracing/resource-timing.ts deleted file mode 100644 index c741a7e91016..000000000000 --- a/packages/browser/src/tracing/resource-timing.ts +++ /dev/null @@ -1,41 +0,0 @@ -import type { Span } from '@sentry/core'; -import { browserPerformanceTimeOrigin } from '@sentry/core'; -import { extractNetworkProtocol } from '@sentry-internal/browser-utils'; - -function getAbsoluteTime(time: number = 0): number { - return ((browserPerformanceTimeOrigin() || performance.timeOrigin) + time) / 1000; -} - -/** - * Converts a PerformanceResourceTiming entry to span data for the resource span. - * - * @param resourceTiming - * @returns An array where the first element is the attribute name and the second element is the attribute value. - */ -export function resourceTimingToSpanAttributes( - resourceTiming: PerformanceResourceTiming, -): Array> { - const timingSpanData: Array> = []; - // Checking for only `undefined` and `null` is intentional because it's - // valid for `nextHopProtocol` to be an empty string. - if (resourceTiming.nextHopProtocol != undefined) { - const { name, version } = extractNetworkProtocol(resourceTiming.nextHopProtocol); - timingSpanData.push(['network.protocol.version', version], ['network.protocol.name', name]); - } - if (!browserPerformanceTimeOrigin()) { - return timingSpanData; - } - return [ - ...timingSpanData, - ['http.request.redirect_start', getAbsoluteTime(resourceTiming.redirectStart)], - ['http.request.fetch_start', getAbsoluteTime(resourceTiming.fetchStart)], - ['http.request.domain_lookup_start', getAbsoluteTime(resourceTiming.domainLookupStart)], - ['http.request.domain_lookup_end', getAbsoluteTime(resourceTiming.domainLookupEnd)], - ['http.request.connect_start', getAbsoluteTime(resourceTiming.connectStart)], - ['http.request.secure_connection_start', getAbsoluteTime(resourceTiming.secureConnectionStart)], - ['http.request.connection_end', getAbsoluteTime(resourceTiming.connectEnd)], - ['http.request.request_start', getAbsoluteTime(resourceTiming.requestStart)], - ['http.request.response_start', getAbsoluteTime(resourceTiming.responseStart)], - ['http.request.response_end', getAbsoluteTime(resourceTiming.responseEnd)], - ]; -} From e1c43674ed9203adfe224ffc1ccb975ebc5fba2c Mon Sep 17 00:00:00 2001 From: Lukas Stracke Date: Fri, 12 Sep 2025 17:40:59 +0200 Subject: [PATCH 39/61] ref(remix): Adjust event mechanism of `captureRemixServerException` (#17629) Mechanism type now follows the trace origin naming scheme ref: https://github.com/getsentry/sentry-javascript/issues/17212 --- packages/remix/src/server/errors.ts | 2 +- .../test/server/instrumentation/action.test.ts | 16 ++++++++-------- .../test/server/instrumentation/loader.test.ts | 6 +++--- .../test/server/instrumentation/ssr.test.ts | 2 +- 4 files changed, 13 insertions(+), 13 deletions(-) diff --git a/packages/remix/src/server/errors.ts b/packages/remix/src/server/errors.ts index def6092717c8..e622e743b74c 100644 --- a/packages/remix/src/server/errors.ts +++ b/packages/remix/src/server/errors.ts @@ -65,7 +65,7 @@ export async function captureRemixServerException(err: unknown, name: string, re scope.addEventProcessor(event => { addExceptionMechanism(event, { - type: 'instrument', + type: 'auto.function.remix.server', handled: false, data: { function: name, diff --git a/packages/remix/test/integration/test/server/instrumentation/action.test.ts b/packages/remix/test/integration/test/server/instrumentation/action.test.ts index bca38429ee29..d511368ba707 100644 --- a/packages/remix/test/integration/test/server/instrumentation/action.test.ts +++ b/packages/remix/test/integration/test/server/instrumentation/action.test.ts @@ -90,7 +90,7 @@ describe('Remix API Actions', () => { function: 'remix.server.handleError', }, handled: false, - type: 'instrument', + type: 'auto.function.remix.server', }, }, ], @@ -185,7 +185,7 @@ describe('Remix API Actions', () => { function: 'remix.server.handleError', }, handled: false, - type: 'instrument', + type: 'auto.function.remix.server', }, }, ], @@ -232,7 +232,7 @@ describe('Remix API Actions', () => { function: 'action', }, handled: false, - type: 'instrument', + type: 'auto.function.remix.server', }, }, ], @@ -279,7 +279,7 @@ describe('Remix API Actions', () => { function: 'action', }, handled: false, - type: 'instrument', + type: 'auto.function.remix.server', }, }, ], @@ -326,7 +326,7 @@ describe('Remix API Actions', () => { function: 'action', }, handled: false, - type: 'instrument', + type: 'auto.function.remix.server', }, }, ], @@ -373,7 +373,7 @@ describe('Remix API Actions', () => { function: 'action', }, handled: false, - type: 'instrument', + type: 'auto.function.remix.server', }, }, ], @@ -420,7 +420,7 @@ describe('Remix API Actions', () => { function: 'remix.server.handleError', }, handled: false, - type: 'instrument', + type: 'auto.function.remix.server', }, }, ], @@ -467,7 +467,7 @@ describe('Remix API Actions', () => { function: 'remix.server.handleError', }, handled: false, - type: 'instrument', + type: 'auto.function.remix.server', }, }, ], diff --git a/packages/remix/test/integration/test/server/instrumentation/loader.test.ts b/packages/remix/test/integration/test/server/instrumentation/loader.test.ts index c90c23d135c8..75bcadf29b0c 100644 --- a/packages/remix/test/integration/test/server/instrumentation/loader.test.ts +++ b/packages/remix/test/integration/test/server/instrumentation/loader.test.ts @@ -35,7 +35,7 @@ describe('Remix API Loaders', () => { function: 'remix.server.handleError', }, handled: false, - type: 'instrument', + type: 'auto.function.remix.server', }, }, ], @@ -77,7 +77,7 @@ describe('Remix API Loaders', () => { function: 'loader', }, handled: false, - type: 'instrument', + type: 'auto.function.remix.server', }, }, ], @@ -155,7 +155,7 @@ describe('Remix API Loaders', () => { function: 'remix.server.handleError', }, handled: false, - type: 'instrument', + type: 'auto.function.remix.server', }, }, ], diff --git a/packages/remix/test/integration/test/server/instrumentation/ssr.test.ts b/packages/remix/test/integration/test/server/instrumentation/ssr.test.ts index 1b2a97a99b55..4f5965fac31f 100644 --- a/packages/remix/test/integration/test/server/instrumentation/ssr.test.ts +++ b/packages/remix/test/integration/test/server/instrumentation/ssr.test.ts @@ -36,7 +36,7 @@ describe('Server Side Rendering', () => { function: 'remix.server.handleError', }, handled: false, - type: 'instrument', + type: 'auto.function.remix.server', }, }, ], From c587947088cdfd12a2a8bc8e67834f983f4287c2 Mon Sep 17 00:00:00 2001 From: Lukas Stracke Date: Mon, 15 Sep 2025 10:07:38 +0200 Subject: [PATCH 40/61] ref(astro): Adjust `mechanism` on error events captured by astro middleware (#17613) mechanism type now follows the trace origin naming scheme closes #17612 --- .../astro-4/tests/errors.server.test.ts | 10 ++-------- .../astro-5/tests/errors.server.test.ts | 10 ++-------- packages/astro/src/server/middleware.ts | 5 +---- packages/astro/test/server/middleware.test.ts | 4 ++-- 4 files changed, 7 insertions(+), 22 deletions(-) diff --git a/dev-packages/e2e-tests/test-applications/astro-4/tests/errors.server.test.ts b/dev-packages/e2e-tests/test-applications/astro-4/tests/errors.server.test.ts index 3faacdb30090..f3b0e9a189de 100644 --- a/dev-packages/e2e-tests/test-applications/astro-4/tests/errors.server.test.ts +++ b/dev-packages/e2e-tests/test-applications/astro-4/tests/errors.server.test.ts @@ -47,11 +47,8 @@ test.describe('server-side errors', () => { values: [ { mechanism: { - data: { - function: 'astroMiddleware', - }, handled: false, - type: 'astro', + type: 'auto.middleware.astro', }, stacktrace: expect.any(Object), type: 'TypeError', @@ -136,11 +133,8 @@ test.describe('server-side errors', () => { values: [ { mechanism: { - data: { - function: 'astroMiddleware', - }, handled: false, - type: 'astro', + type: 'auto.middleware.astro', }, stacktrace: expect.any(Object), type: 'Error', diff --git a/dev-packages/e2e-tests/test-applications/astro-5/tests/errors.server.test.ts b/dev-packages/e2e-tests/test-applications/astro-5/tests/errors.server.test.ts index d6a9514da1d1..77d515ad2a24 100644 --- a/dev-packages/e2e-tests/test-applications/astro-5/tests/errors.server.test.ts +++ b/dev-packages/e2e-tests/test-applications/astro-5/tests/errors.server.test.ts @@ -47,11 +47,8 @@ test.describe('server-side errors', () => { values: [ { mechanism: { - data: { - function: 'astroMiddleware', - }, handled: false, - type: 'astro', + type: 'auto.middleware.astro', }, stacktrace: expect.any(Object), type: 'TypeError', @@ -136,11 +133,8 @@ test.describe('server-side errors', () => { values: [ { mechanism: { - data: { - function: 'astroMiddleware', - }, handled: false, - type: 'astro', + type: 'auto.middleware.astro', }, stacktrace: expect.any(Object), type: 'Error', diff --git a/packages/astro/src/server/middleware.ts b/packages/astro/src/server/middleware.ts index 61f7913cf1b1..a12c25ff6045 100644 --- a/packages/astro/src/server/middleware.ts +++ b/packages/astro/src/server/middleware.ts @@ -50,11 +50,8 @@ function sendErrorToSentry(e: unknown): unknown { captureException(objectifiedErr, { mechanism: { - type: 'astro', + type: 'auto.middleware.astro', handled: false, - data: { - function: 'astroMiddleware', - }, }, }); diff --git a/packages/astro/test/server/middleware.test.ts b/packages/astro/test/server/middleware.test.ts index 03933582c846..10e314268264 100644 --- a/packages/astro/test/server/middleware.test.ts +++ b/packages/astro/test/server/middleware.test.ts @@ -168,7 +168,7 @@ describe('sentryMiddleware', () => { await expect(async () => middleware(ctx, next)).rejects.toThrowError(); expect(captureExceptionSpy).toHaveBeenCalledWith(error, { - mechanism: { handled: false, type: 'astro', data: { function: 'astroMiddleware' } }, + mechanism: { handled: false, type: 'auto.middleware.astro' }, }); }); @@ -205,7 +205,7 @@ describe('sentryMiddleware', () => { await expect(() => resultFromNext!.text()).rejects.toThrowError(); expect(captureExceptionSpy).toHaveBeenCalledWith(error, { - mechanism: { handled: false, type: 'astro', data: { function: 'astroMiddleware' } }, + mechanism: { handled: false, type: 'auto.middleware.astro' }, }); }); From 4b562bcbf0a81494627ab2c5a153ddbddb2d89ae Mon Sep 17 00:00:00 2001 From: Lukas Stracke Date: Mon, 15 Sep 2025 11:33:37 +0200 Subject: [PATCH 41/61] chore: Add changelog note about mechanism changes (#17632) As suggested in https://github.com/getsentry/sentry-javascript/pull/17613#pullrequestreview-3223271017, this PR adds a note to the changelog about https://github.com/getsentry/sentry-javascript/issues/17212. Decided to put the individual PRs into a `
` element because the list is really long and 99% not relevant for users. --- CHANGELOG.md | 39 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 39 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index ea474e56355d..2e273e367111 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,45 @@ ## Unreleased +### Important Changes + +- **ref: Add and Adjust error event `mechanism` values** + + This release includes a variety of changes aimed at setting the `mechanism` field on errors captured automatically by the Sentry SDKs. [The intention](https://github.com/getsentry/sentry-javascript/issues/17212) is to clearly mark which instrumentation captured an error. In addition, some instrumentations previously did not yet annotate the error as handled or unhandled which this series of PRs corrects as well. + +
+ Relevant PRs + +
+ + Released in `10.12.0`: + - ref(angular): Adjust ErrorHandler event mechanism ([#17608](https://github.com/getsentry/sentry-javascript/pull/17608)) + - ref(astro): Adjust `mechanism` on error events captured by astro middleware ([#17613](https://github.com/getsentry/sentry-javascript/pull/17613)) + - ref(aws-severless): Slightly adjust aws-serverless mechanism type ([#17614](https://github.com/getsentry/sentry-javascript/pull/17614)) + - ref(bun): Adjust `mechanism` of errors captured in Bun.serve ([#17616](https://github.com/getsentry/sentry-javascript/pull/17616)) + - ref(core): Adjust MCP server error event `mechanism` ([#17622](https://github.com/getsentry/sentry-javascript/pull/17622)) + - ref(core): Simplify `linkedErrors` mechanism logic ([#17600](https://github.com/getsentry/sentry-javascript/pull/17600)) + - ref(nextjs): Set more specific event `mechanism`s ([#17543](https://github.com/getsentry/sentry-javascript/pull/17543)) + - ref(node-core): Add `mechanism` to cron instrumentations ([#17544](https://github.com/getsentry/sentry-javascript/pull/17544)) + - ref(node-core): Add more specific `mechanism.type` to worker thread errors from `childProcessIntegration` ([#17578](https://github.com/getsentry/sentry-javascript/pull/17578)) + - ref(node): Add mechanism to errors captured via connect and koa integrations ([#17579](https://github.com/getsentry/sentry-javascript/pull/17579)) + - ref(nuxt): Add and adjust `mechanism.type` in error events ([#17599](https://github.com/getsentry/sentry-javascript/pull/17599)) + - ref(react): Add mechanism to `reactErrorHandler` and adjust mechanism in `ErrorBoundary` ([#17602](https://github.com/getsentry/sentry-javascript/pull/17602)) + - ref(remix): Adjust event mechanism of `captureRemixServerException` ([#17629](https://github.com/getsentry/sentry-javascript/pull/17629)) + - ref(replay-internal): Add mechanism to error caught by `replayIntegration` in debug mode ([#17606](https://github.com/getsentry/sentry-javascript/pull/17606)) + - ref(solid): Add `mechanism` to error captured by `withSentryErrorBoundary` ([#17607](https://github.com/getsentry/sentry-javascript/pull/17607)) + +
+ + Released in `10.11.0`: + - ref(browser): Add more specific `mechanism.type` to errors captured by `httpClientIntegration` ([#17254](https://github.com/getsentry/sentry-javascript/pull/17254)) + - ref(browser): Set more descriptive `mechanism.type` in `browserApiErrorsIntergation` ([#17251](https://github.com/getsentry/sentry-javascript/pull/17251)) + - ref(core): Add `mechanism.type` to `trpcMiddleware` errors ([#17287](https://github.com/getsentry/sentry-javascript/pull/17287)) + - ref(core): Add more specific event `mechanism`s and span origins to `openAiIntegration` ([#17288](https://github.com/getsentry/sentry-javascript/pull/17288)) + - ref(nestjs): Add `mechanism` to captured errors ([#17312](https://github.com/getsentry/sentry-javascript/pull/17312)) + +
+ - "You miss 100 percent of the chances you don't take. — Wayne Gretzky" — Michael Scott ### Important Changes From 31dc645e4d7fdb72a2857b4d9090741ec59d1228 Mon Sep 17 00:00:00 2001 From: Lukas Stracke Date: Mon, 15 Sep 2025 12:03:46 +0200 Subject: [PATCH 42/61] ref(node): Adjust mechanism of `express`, `hapi` and `fastify` error handlers (#17623) Adjusts the already set mechanisms to follow the trace origin naming scheme ref #https://github.com/getsentry/sentry-javascript/issues/17212 --- CHANGELOG.md | 1 + .../test-applications/node-express/tests/errors.test.ts | 7 ++++++- .../test-applications/node-fastify-5/tests/errors.test.ts | 7 ++++++- .../test-applications/node-hapi/tests/errors.test.ts | 7 ++++++- .../suites/express/handle-error-scope-data-loss/test.ts | 6 +++--- .../suites/express/handle-error-tracesSampleRate-0/test.ts | 2 +- .../express/handle-error-tracesSampleRate-unset/test.ts | 2 +- .../suites/express/handle-error/test.ts | 2 +- packages/node/src/integrations/tracing/express.ts | 2 +- packages/node/src/integrations/tracing/fastify/index.ts | 2 +- packages/node/src/integrations/tracing/hapi/index.ts | 5 +---- 11 files changed, 28 insertions(+), 15 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 2e273e367111..7cbb0281ac31 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -21,6 +21,7 @@ - ref(core): Adjust MCP server error event `mechanism` ([#17622](https://github.com/getsentry/sentry-javascript/pull/17622)) - ref(core): Simplify `linkedErrors` mechanism logic ([#17600](https://github.com/getsentry/sentry-javascript/pull/17600)) - ref(nextjs): Set more specific event `mechanism`s ([#17543](https://github.com/getsentry/sentry-javascript/pull/17543)) + - ref(node): Adjust mechanism of express, hapi and fastify error handlers ([#17623](https://github.com/getsentry/sentry-javascript/pull/17623)) - ref(node-core): Add `mechanism` to cron instrumentations ([#17544](https://github.com/getsentry/sentry-javascript/pull/17544)) - ref(node-core): Add more specific `mechanism.type` to worker thread errors from `childProcessIntegration` ([#17578](https://github.com/getsentry/sentry-javascript/pull/17578)) - ref(node): Add mechanism to errors captured via connect and koa integrations ([#17579](https://github.com/getsentry/sentry-javascript/pull/17579)) diff --git a/dev-packages/e2e-tests/test-applications/node-express/tests/errors.test.ts b/dev-packages/e2e-tests/test-applications/node-express/tests/errors.test.ts index 00f9f413906f..3a3c821a927d 100644 --- a/dev-packages/e2e-tests/test-applications/node-express/tests/errors.test.ts +++ b/dev-packages/e2e-tests/test-applications/node-express/tests/errors.test.ts @@ -11,7 +11,12 @@ test('Sends correct error event', async ({ baseURL }) => { const errorEvent = await errorEventPromise; expect(errorEvent.exception?.values).toHaveLength(1); - expect(errorEvent.exception?.values?.[0]?.value).toBe('This is an exception with id 123'); + const exception = errorEvent.exception?.values?.[0]; + expect(exception?.value).toBe('This is an exception with id 123'); + expect(exception?.mechanism).toEqual({ + type: 'auto.middleware.express', + handled: false, + }); expect(errorEvent.request).toMatchObject({ method: 'GET', diff --git a/dev-packages/e2e-tests/test-applications/node-fastify-5/tests/errors.test.ts b/dev-packages/e2e-tests/test-applications/node-fastify-5/tests/errors.test.ts index cf1428b7a34a..a25f990748ea 100644 --- a/dev-packages/e2e-tests/test-applications/node-fastify-5/tests/errors.test.ts +++ b/dev-packages/e2e-tests/test-applications/node-fastify-5/tests/errors.test.ts @@ -11,7 +11,12 @@ test('Sends correct error event', async ({ baseURL }) => { const errorEvent = await errorEventPromise; expect(errorEvent.exception?.values).toHaveLength(1); - expect(errorEvent.exception?.values?.[0]?.value).toBe('This is an exception with id 123'); + const exception = errorEvent.exception?.values?.[0]; + expect(exception?.value).toBe('This is an exception with id 123'); + expect(exception?.mechanism).toEqual({ + type: 'auto.function.fastify', + handled: false, + }); expect(errorEvent.request).toEqual({ method: 'GET', diff --git a/dev-packages/e2e-tests/test-applications/node-hapi/tests/errors.test.ts b/dev-packages/e2e-tests/test-applications/node-hapi/tests/errors.test.ts index 4f2fba52aa95..39edc8bcde0e 100644 --- a/dev-packages/e2e-tests/test-applications/node-hapi/tests/errors.test.ts +++ b/dev-packages/e2e-tests/test-applications/node-hapi/tests/errors.test.ts @@ -22,7 +22,12 @@ test('Sends thrown error to Sentry', async ({ baseURL }) => { }); expect(errorEvent.exception?.values).toHaveLength(1); - expect(errorEvent.exception?.values?.[0]?.value).toBe('This is an error'); + const exception = errorEvent.exception?.values?.[0]; + expect(exception?.value).toBe('This is an error'); + expect(exception?.mechanism).toEqual({ + type: 'auto.function.hapi', + handled: false, + }); expect(errorEvent.request).toEqual({ method: 'GET', diff --git a/dev-packages/node-integration-tests/suites/express/handle-error-scope-data-loss/test.ts b/dev-packages/node-integration-tests/suites/express/handle-error-scope-data-loss/test.ts index 61291f86320d..cb5d20760c00 100644 --- a/dev-packages/node-integration-tests/suites/express/handle-error-scope-data-loss/test.ts +++ b/dev-packages/node-integration-tests/suites/express/handle-error-scope-data-loss/test.ts @@ -22,7 +22,7 @@ test('withScope scope is NOT applied to thrown error caught by global handler', values: [ { mechanism: { - type: 'middleware', + type: 'auto.middleware.express', handled: false, }, type: 'Error', @@ -61,7 +61,7 @@ test('http requestisolation scope is applied to thrown error caught by global ha values: [ { mechanism: { - type: 'middleware', + type: 'auto.middleware.express', handled: false, }, type: 'Error', @@ -109,7 +109,7 @@ test('withIsolationScope scope is NOT applied to thrown error caught by global h values: [ { mechanism: { - type: 'middleware', + type: 'auto.middleware.express', handled: false, }, type: 'Error', diff --git a/dev-packages/node-integration-tests/suites/express/handle-error-tracesSampleRate-0/test.ts b/dev-packages/node-integration-tests/suites/express/handle-error-tracesSampleRate-0/test.ts index 1f434ebc0971..b8cc2cf53d34 100644 --- a/dev-packages/node-integration-tests/suites/express/handle-error-tracesSampleRate-0/test.ts +++ b/dev-packages/node-integration-tests/suites/express/handle-error-tracesSampleRate-0/test.ts @@ -13,7 +13,7 @@ test('should capture and send Express controller error with txn name if tracesSa values: [ { mechanism: { - type: 'middleware', + type: 'auto.middleware.express', handled: false, }, type: 'Error', diff --git a/dev-packages/node-integration-tests/suites/express/handle-error-tracesSampleRate-unset/test.ts b/dev-packages/node-integration-tests/suites/express/handle-error-tracesSampleRate-unset/test.ts index 4f0dcaae1411..a5f9a1332866 100644 --- a/dev-packages/node-integration-tests/suites/express/handle-error-tracesSampleRate-unset/test.ts +++ b/dev-packages/node-integration-tests/suites/express/handle-error-tracesSampleRate-unset/test.ts @@ -14,7 +14,7 @@ test('should capture and send Express controller error if tracesSampleRate is no values: [ { mechanism: { - type: 'middleware', + type: 'auto.middleware.express', handled: false, }, type: 'Error', diff --git a/dev-packages/node-integration-tests/suites/express/handle-error/test.ts b/dev-packages/node-integration-tests/suites/express/handle-error/test.ts index 0db624160959..4e702360242f 100644 --- a/dev-packages/node-integration-tests/suites/express/handle-error/test.ts +++ b/dev-packages/node-integration-tests/suites/express/handle-error/test.ts @@ -18,7 +18,7 @@ test('should capture and send Express controller error with txn name if tracesSa values: [ { mechanism: { - type: 'middleware', + type: 'auto.middleware.express', handled: false, }, type: 'Error', diff --git a/packages/node/src/integrations/tracing/express.ts b/packages/node/src/integrations/tracing/express.ts index fed0a65cfd0b..e645cc24d31e 100644 --- a/packages/node/src/integrations/tracing/express.ts +++ b/packages/node/src/integrations/tracing/express.ts @@ -130,7 +130,7 @@ export function expressErrorHandler(options?: ExpressHandlerOptions): ExpressErr const shouldHandleError = options?.shouldHandleError || defaultShouldHandleError; if (shouldHandleError(error)) { - const eventId = captureException(error, { mechanism: { type: 'middleware', handled: false } }); + const eventId = captureException(error, { mechanism: { type: 'auto.middleware.express', handled: false } }); (res as { sentry?: string }).sentry = eventId; } diff --git a/packages/node/src/integrations/tracing/fastify/index.ts b/packages/node/src/integrations/tracing/fastify/index.ts index 65d783eb8be7..084c50a6957e 100644 --- a/packages/node/src/integrations/tracing/fastify/index.ts +++ b/packages/node/src/integrations/tracing/fastify/index.ts @@ -132,7 +132,7 @@ function handleFastifyError( } if (shouldHandleError(error, request, reply)) { - captureException(error, { mechanism: { handled: false, type: 'fastify' } }); + captureException(error, { mechanism: { handled: false, type: 'auto.function.fastify' } }); } } diff --git a/packages/node/src/integrations/tracing/hapi/index.ts b/packages/node/src/integrations/tracing/hapi/index.ts index eaa8d9c56ad9..a8a64c0b0714 100644 --- a/packages/node/src/integrations/tracing/hapi/index.ts +++ b/packages/node/src/integrations/tracing/hapi/index.ts @@ -54,11 +54,8 @@ function isErrorEvent(event: unknown): event is RequestEvent { function sendErrorToSentry(errorData: object): void { captureException(errorData, { mechanism: { - type: 'hapi', + type: 'auto.function.hapi', handled: false, - data: { - function: 'hapiErrorPlugin', - }, }, }); } From 28d183a52a5b93c3957a40d878f29ca3adc03867 Mon Sep 17 00:00:00 2001 From: Sukbin Lee <83221664+dolbin-prime@users.noreply.github.com> Date: Mon, 15 Sep 2025 19:43:10 +0900 Subject: [PATCH 43/61] feat(core): Export `TracesSamplerSamplingContext` type (#17523) In https://github.com/getsentry/sentry-javascript/pull/15277, `TracesSamplerSamplingContext` is added to and exported from [types-hoist], but is not being exported from [index.ts]. I'm following the [instruction], and I want to inject `(samplingContext: TracesSamplerSamplingContext) => number | boolean` to a function that is wrapping `Sentry.init`, but I can't because `TracesSamplerSamplingContext` is not recognized :( [types-hoist]: https://github.com/getsentry/sentry-javascript/blob/14eba5676149317f2e2b31143a0590256d32c094/packages/core/src/types-hoist/samplingcontext.ts#L47 [index.ts]: https://github.com/getsentry/sentry-javascript/blob/14eba5676149317f2e2b31143a0590256d32c094/packages/core/src/index.ts#L403 [instruction]: https://docs.sentry.io/platforms/javascript/guides/react/tracing/configure-sampling/#sampling-function-tracessampler --- packages/core/src/index.ts | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/packages/core/src/index.ts b/packages/core/src/index.ts index 339dc92454b0..b971aa8b43a3 100644 --- a/packages/core/src/index.ts +++ b/packages/core/src/index.ts @@ -410,7 +410,11 @@ export type { Stacktrace, StackParser, StackLineParser, StackLineParserFn } from export type { PropagationContext, TracePropagationTargets, SerializedTraceData } from './types-hoist/tracing'; export type { StartSpanOptions } from './types-hoist/startSpanOptions'; export type { TraceparentData, TransactionSource } from './types-hoist/transaction'; -export type { CustomSamplingContext, SamplingContext } from './types-hoist/samplingcontext'; +export type { + TracesSamplerSamplingContext, + CustomSamplingContext, + SamplingContext, +} from './types-hoist/samplingcontext'; export type { DurationUnit, InformationUnit, From 566929be79f229bf83d85f53e5293535bc0460c2 Mon Sep 17 00:00:00 2001 From: Lukas Stracke Date: Mon, 15 Sep 2025 13:40:52 +0200 Subject: [PATCH 44/61] ref(core): Adjust `mechanism` in `captureConsoleIntegration` (#17633) Sets a more specific mechanism type, following the Trace Origin naming scheme. ref https://github.com/getsentry/sentry-javascript/issues/17212 --- CHANGELOG.md | 1 + .../captureConsole-attachStackTrace/test.ts | 12 ++++++------ .../suites/integrations/captureConsole/test.ts | 2 +- packages/core/src/integrations/captureconsole.ts | 2 +- .../test/lib/integrations/captureconsole.test.ts | 6 +++--- 5 files changed, 12 insertions(+), 11 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 7cbb0281ac31..0f6f1ceb6de2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -18,6 +18,7 @@ - ref(astro): Adjust `mechanism` on error events captured by astro middleware ([#17613](https://github.com/getsentry/sentry-javascript/pull/17613)) - ref(aws-severless): Slightly adjust aws-serverless mechanism type ([#17614](https://github.com/getsentry/sentry-javascript/pull/17614)) - ref(bun): Adjust `mechanism` of errors captured in Bun.serve ([#17616](https://github.com/getsentry/sentry-javascript/pull/17616)) + - ref(core): Adjust `mechanism` in `captureConsoleIntegration` ([#17633](https://github.com/getsentry/sentry-javascript/pull/17633)) - ref(core): Adjust MCP server error event `mechanism` ([#17622](https://github.com/getsentry/sentry-javascript/pull/17622)) - ref(core): Simplify `linkedErrors` mechanism logic ([#17600](https://github.com/getsentry/sentry-javascript/pull/17600)) - ref(nextjs): Set more specific event `mechanism`s ([#17543](https://github.com/getsentry/sentry-javascript/pull/17543)) diff --git a/dev-packages/browser-integration-tests/suites/integrations/captureConsole-attachStackTrace/test.ts b/dev-packages/browser-integration-tests/suites/integrations/captureConsole-attachStackTrace/test.ts index fc8c1b378e7f..ff15c17832c6 100644 --- a/dev-packages/browser-integration-tests/suites/integrations/captureConsole-attachStackTrace/test.ts +++ b/dev-packages/browser-integration-tests/suites/integrations/captureConsole-attachStackTrace/test.ts @@ -37,7 +37,7 @@ sentryTest( expect(logEvent?.exception?.values![0]).toMatchObject({ mechanism: { handled: true, - type: 'console', + type: 'auto.core.capture_console', synthetic: true, }, value: 'console log', @@ -59,7 +59,7 @@ sentryTest( expect(warnEvent?.exception?.values![0]).toMatchObject({ mechanism: { handled: true, - type: 'console', + type: 'auto.core.capture_console', synthetic: true, }, value: 'console warn', @@ -81,7 +81,7 @@ sentryTest( expect(infoEvent?.exception?.values![0]).toMatchObject({ mechanism: { handled: true, - type: 'console', + type: 'auto.core.capture_console', synthetic: true, }, value: 'console info', @@ -103,7 +103,7 @@ sentryTest( expect(errorEvent?.exception?.values![0]).toMatchObject({ mechanism: { handled: true, - type: 'console', + type: 'auto.core.capture_console', synthetic: true, }, value: 'console error', @@ -125,7 +125,7 @@ sentryTest( expect(traceEvent?.exception?.values![0]).toMatchObject({ mechanism: { handled: true, - type: 'console', + type: 'auto.core.capture_console', synthetic: true, }, value: 'console trace', @@ -153,7 +153,7 @@ sentryTest( expect(errorWithErrorEvent?.exception?.values?.[0].value).toBe('console error with error object'); expect(errorWithErrorEvent?.exception?.values?.[0].mechanism).toEqual({ handled: true, - type: 'console', + type: 'auto.core.capture_console', }); expect(traceWithErrorEvent).toEqual( expect.objectContaining({ diff --git a/dev-packages/browser-integration-tests/suites/integrations/captureConsole/test.ts b/dev-packages/browser-integration-tests/suites/integrations/captureConsole/test.ts index 06d989cc4433..8e7e6aaa58e3 100644 --- a/dev-packages/browser-integration-tests/suites/integrations/captureConsole/test.ts +++ b/dev-packages/browser-integration-tests/suites/integrations/captureConsole/test.ts @@ -96,7 +96,7 @@ sentryTest('it captures console messages correctly', async ({ getLocalTestUrl, p expect(errorWithErrorEvent?.exception?.values?.[0].value).toBe('console error with error object'); expect(errorWithErrorEvent?.exception?.values?.[0].mechanism).toEqual({ handled: true, - type: 'console', + type: 'auto.core.capture_console', }); expect(traceWithErrorEvent).toEqual( expect.objectContaining({ diff --git a/packages/core/src/integrations/captureconsole.ts b/packages/core/src/integrations/captureconsole.ts index e491efedd83f..d5d34bd554aa 100644 --- a/packages/core/src/integrations/captureconsole.ts +++ b/packages/core/src/integrations/captureconsole.ts @@ -65,7 +65,7 @@ function consoleHandler(args: unknown[], level: string, handled: boolean): void addExceptionMechanism(event, { handled, - type: 'console', + type: 'auto.core.capture_console', }); return event; diff --git a/packages/core/test/lib/integrations/captureconsole.test.ts b/packages/core/test/lib/integrations/captureconsole.test.ts index 2fe971710a07..8da7c6570d74 100644 --- a/packages/core/test/lib/integrations/captureconsole.test.ts +++ b/packages/core/test/lib/integrations/captureconsole.test.ts @@ -328,7 +328,7 @@ describe('CaptureConsole setup', () => { expect(someEvent.exception?.values?.[0]?.mechanism).toEqual({ handled: true, - type: 'console', + type: 'auto.core.capture_console', }); }); @@ -352,7 +352,7 @@ describe('CaptureConsole setup', () => { expect(someEvent.exception?.values?.[0]?.mechanism).toEqual({ handled: true, - type: 'console', + type: 'auto.core.capture_console', }); }); @@ -376,7 +376,7 @@ describe('CaptureConsole setup', () => { expect(someEvent.exception?.values?.[0]?.mechanism).toEqual({ handled: false, - type: 'console', + type: 'auto.core.capture_console', }); }); }); From ce46ffccfc21b308ea64e3baf1a4a0c1a228f799 Mon Sep 17 00:00:00 2001 From: Lukas Stracke Date: Mon, 15 Sep 2025 14:25:04 +0200 Subject: [PATCH 45/61] ref(vue): Adjust `mechanism` in Vue error handler (#17647) Event mechanism type now follows the trace origin naming scheme closes #17640 --- CHANGELOG.md | 1 + .../vue-3/tests/errors.test.ts | 4 ++-- packages/vue/src/errorhandler.ts | 2 +- packages/vue/test/errorHandler.test.ts | 18 +++++++++++++----- 4 files changed, 17 insertions(+), 8 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 0f6f1ceb6de2..b470228dcc7b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -31,6 +31,7 @@ - ref(remix): Adjust event mechanism of `captureRemixServerException` ([#17629](https://github.com/getsentry/sentry-javascript/pull/17629)) - ref(replay-internal): Add mechanism to error caught by `replayIntegration` in debug mode ([#17606](https://github.com/getsentry/sentry-javascript/pull/17606)) - ref(solid): Add `mechanism` to error captured by `withSentryErrorBoundary` ([#17607](https://github.com/getsentry/sentry-javascript/pull/17607)) + - ref(vue): Adjust mechanism in Vue error handler ([#17647](https://github.com/getsentry/sentry-javascript/pull/17647))
diff --git a/dev-packages/e2e-tests/test-applications/vue-3/tests/errors.test.ts b/dev-packages/e2e-tests/test-applications/vue-3/tests/errors.test.ts index b86e56eb4b83..118476aeb485 100644 --- a/dev-packages/e2e-tests/test-applications/vue-3/tests/errors.test.ts +++ b/dev-packages/e2e-tests/test-applications/vue-3/tests/errors.test.ts @@ -19,7 +19,7 @@ test('sends an error', async ({ page }) => { type: 'Error', value: 'This is a Vue test error', mechanism: { - type: 'vue', + type: 'auto.function.vue.error_handler', handled: false, }, }, @@ -47,7 +47,7 @@ test('sends an error with a parameterized transaction name', async ({ page }) => type: 'Error', value: 'This is a Vue test error', mechanism: { - type: 'vue', + type: 'auto.function.vue.error_handler', handled: false, }, }, diff --git a/packages/vue/src/errorhandler.ts b/packages/vue/src/errorhandler.ts index f8ca3b9a2c9f..c18a51492c4a 100644 --- a/packages/vue/src/errorhandler.ts +++ b/packages/vue/src/errorhandler.ts @@ -30,7 +30,7 @@ export const attachErrorHandler = (app: Vue, options: VueOptions): void => { setTimeout(() => { captureException(error, { captureContext: { contexts: { vue: metadata } }, - mechanism: { handled: !!originalErrorHandler, type: 'vue' }, + mechanism: { handled: !!originalErrorHandler, type: 'auto.function.vue.error_handler' }, }); }); diff --git a/packages/vue/test/errorHandler.test.ts b/packages/vue/test/errorHandler.test.ts index 9e8e7572ba54..54fd80133017 100644 --- a/packages/vue/test/errorHandler.test.ts +++ b/packages/vue/test/errorHandler.test.ts @@ -26,7 +26,9 @@ describe('attachErrorHandler', () => { // assert t.expect.errorToHaveBeenCaptured().withoutProps(); - t.expect.errorToHaveBeenCaptured().withMechanismMetadata({ handled: false, type: 'vue' }); + t.expect + .errorToHaveBeenCaptured() + .withMechanismMetadata({ handled: false, type: 'auto.function.vue.error_handler' }); }); }); @@ -47,7 +49,9 @@ describe('attachErrorHandler', () => { // assert t.expect.errorToHaveBeenCaptured().withoutProps(); - t.expect.errorToHaveBeenCaptured().withMechanismMetadata({ handled: false, type: 'vue' }); + t.expect + .errorToHaveBeenCaptured() + .withMechanismMetadata({ handled: false, type: 'auto.function.vue.error_handler' }); }); }); @@ -146,7 +150,9 @@ describe('attachErrorHandler', () => { vi.runAllTimers(); // assert - t.expect.errorToHaveBeenCaptured().withMechanismMetadata({ handled: false, type: 'vue' }); + t.expect + .errorToHaveBeenCaptured() + .withMechanismMetadata({ handled: false, type: 'auto.function.vue.error_handler' }); }); it('should mark error as handled and properly delegate to error handler', () => { @@ -167,7 +173,9 @@ describe('attachErrorHandler', () => { // assert t.expect.errorHandlerSpy.toHaveBeenCalledWith(expect.any(Error), vm, 'stub-lifecycle-hook'); - t.expect.errorToHaveBeenCaptured().withMechanismMetadata({ handled: true, type: 'vue' }); + t.expect + .errorToHaveBeenCaptured() + .withMechanismMetadata({ handled: true, type: 'auto.function.vue.error_handler' }); }); }); }); @@ -305,7 +313,7 @@ const testHarness = ({ withoutProps: () => { expect(contexts).not.toHaveProperty('vue.propsData'); }, - withMechanismMetadata: (mechanism: { handled: boolean; type: 'vue' }) => { + withMechanismMetadata: (mechanism: { handled: boolean; type: 'auto.function.vue.error_handler' }) => { expect(mechanismMetadata).toEqual(mechanism); }, }; From a271c6db9b50eb33f7203174e3439a84ae767d47 Mon Sep 17 00:00:00 2001 From: Francesco Gringl-Novy Date: Mon, 15 Sep 2025 15:30:20 +0200 Subject: [PATCH 46/61] ref(core): Streamline event processor handling (#17634) Avoid re-creating many sync promises where not needed. We used to wrap every result of an event processor in a sync promise, no matter if needed or not. This is not really necessary, likely having a (very minor) perf impact but also making the stacktraces etc. more complicated then neccessary. With this PR, we only wrap the overall result in a sync promise once, if needed, and otherwise just run the event processors directly. Somewhat inspired by looking into this for https://github.com/getsentry/sentry-javascript/issues/17592, but likely does not actually fix this. --- packages/core/src/eventProcessors.ts | 47 ++++++---- packages/core/test/lib/client.test.ts | 119 ++++++++++++++++++++++++-- 2 files changed, 142 insertions(+), 24 deletions(-) diff --git a/packages/core/src/eventProcessors.ts b/packages/core/src/eventProcessors.ts index 3bebe71b9316..ca41e5ee2a03 100644 --- a/packages/core/src/eventProcessors.ts +++ b/packages/core/src/eventProcessors.ts @@ -3,7 +3,7 @@ import type { Event, EventHint } from './types-hoist/event'; import type { EventProcessor } from './types-hoist/eventprocessor'; import { debug } from './utils/debug-logger'; import { isThenable } from './utils/is'; -import { SyncPromise } from './utils/syncpromise'; +import { rejectedSyncPromise, resolvedSyncPromise } from './utils/syncpromise'; /** * Process an array of event processors, returning the processed event (or `null` if the event was dropped). @@ -14,24 +14,33 @@ export function notifyEventProcessors( hint: EventHint, index: number = 0, ): PromiseLike { - return new SyncPromise((resolve, reject) => { - const processor = processors[index]; - if (event === null || typeof processor !== 'function') { - resolve(event); - } else { - const result = processor({ ...event }, hint) as Event | null; + try { + const result = _notifyEventProcessors(event, hint, processors, index); + return isThenable(result) ? result : resolvedSyncPromise(result); + } catch (error) { + return rejectedSyncPromise(error); + } +} + +function _notifyEventProcessors( + event: Event | null, + hint: EventHint, + processors: EventProcessor[], + index: number, +): Event | null | PromiseLike { + const processor = processors[index]; + + if (!event || !processor) { + return event; + } + + const result = processor({ ...event }, hint); + + DEBUG_BUILD && result === null && debug.log(`Event processor "${processor.id || '?'}" dropped event`); - DEBUG_BUILD && processor.id && result === null && debug.log(`Event processor "${processor.id}" dropped event`); + if (isThenable(result)) { + return result.then(final => _notifyEventProcessors(final, hint, processors, index + 1)); + } - if (isThenable(result)) { - void result - .then(final => notifyEventProcessors(processors, final, hint, index + 1).then(resolve)) - .then(null, reject); - } else { - void notifyEventProcessors(processors, result, hint, index + 1) - .then(resolve) - .then(null, reject); - } - } - }); + return _notifyEventProcessors(result, hint, processors, index + 1); } diff --git a/packages/core/test/lib/client.test.ts b/packages/core/test/lib/client.test.ts index 6a7d0af22857..cb44d7212945 100644 --- a/packages/core/test/lib/client.test.ts +++ b/packages/core/test/lib/client.test.ts @@ -1833,15 +1833,13 @@ describe('Client', () => { }); }); - test('event processor sends an event and logs when it crashes', () => { - expect.assertions(3); - + test('event processor sends an event and logs when it crashes synchronously', () => { const options = getDefaultTestClientOptions({ dsn: PUBLIC_DSN }); const client = new TestClient(options); const captureExceptionSpy = vi.spyOn(client, 'captureException'); const loggerWarnSpy = vi.spyOn(debugLoggerModule.debug, 'warn'); const scope = new Scope(); - const exception = new Error('sorry'); + const exception = new Error('sorry 1'); scope.addEventProcessor(() => { throw exception; }); @@ -1850,7 +1848,43 @@ describe('Client', () => { expect(TestClient.instance!.event!.exception!.values![0]).toStrictEqual({ type: 'Error', - value: 'sorry', + value: 'sorry 1', + mechanism: { type: 'internal', handled: false }, + }); + expect(captureExceptionSpy).toBeCalledWith(exception, { + data: { + __sentry__: true, + }, + originalException: exception, + mechanism: { type: 'internal', handled: false }, + }); + expect(loggerWarnSpy).toBeCalledWith( + `Event processing pipeline threw an error, original event will not be sent. Details have been sent as a new event.\nReason: ${exception}`, + ); + }); + + test('event processor sends an event and logs when it crashes asynchronously', async () => { + vi.useFakeTimers(); + + const options = getDefaultTestClientOptions({ dsn: PUBLIC_DSN }); + const client = new TestClient(options); + const captureExceptionSpy = vi.spyOn(client, 'captureException'); + const loggerWarnSpy = vi.spyOn(debugLoggerModule.debug, 'warn'); + const scope = new Scope(); + const exception = new Error('sorry 2'); + scope.addEventProcessor(() => { + return new Promise((_resolve, reject) => { + reject(exception); + }); + }); + + client.captureEvent({ message: 'hello' }, {}, scope); + + await vi.runOnlyPendingTimersAsync(); + + expect(TestClient.instance!.event!.exception!.values![0]).toStrictEqual({ + type: 'Error', + value: 'sorry 2', mechanism: { type: 'internal', handled: false }, }); expect(captureExceptionSpy).toBeCalledWith(exception, { @@ -1865,6 +1899,81 @@ describe('Client', () => { ); }); + test('event processor sends an event and logs when it crashes synchronously in processor chain', () => { + const options = getDefaultTestClientOptions({ dsn: PUBLIC_DSN }); + const client = new TestClient(options); + const captureExceptionSpy = vi.spyOn(client, 'captureException'); + const scope = new Scope(); + const exception = new Error('sorry 3'); + + const processor1 = vi.fn(event => { + return event; + }); + const processor2 = vi.fn(() => { + throw exception; + }); + const processor3 = vi.fn(event => { + return event; + }); + + scope.addEventProcessor(processor1); + scope.addEventProcessor(processor2); + scope.addEventProcessor(processor3); + + client.captureEvent({ message: 'hello' }, {}, scope); + + expect(processor1).toHaveBeenCalledTimes(1); + expect(processor2).toHaveBeenCalledTimes(1); + expect(processor3).toHaveBeenCalledTimes(0); + + expect(captureExceptionSpy).toBeCalledWith(exception, { + data: { + __sentry__: true, + }, + originalException: exception, + mechanism: { type: 'internal', handled: false }, + }); + }); + + test('event processor sends an event and logs when it crashes asynchronously in processor chain', async () => { + vi.useFakeTimers(); + + const options = getDefaultTestClientOptions({ dsn: PUBLIC_DSN }); + const client = new TestClient(options); + const captureExceptionSpy = vi.spyOn(client, 'captureException'); + const scope = new Scope(); + const exception = new Error('sorry 4'); + + const processor1 = vi.fn(async event => { + return event; + }); + const processor2 = vi.fn(async () => { + throw exception; + }); + const processor3 = vi.fn(event => { + return event; + }); + + scope.addEventProcessor(processor1); + scope.addEventProcessor(processor2); + scope.addEventProcessor(processor3); + + client.captureEvent({ message: 'hello' }, {}, scope); + await vi.runOnlyPendingTimersAsync(); + + expect(processor1).toHaveBeenCalledTimes(1); + expect(processor2).toHaveBeenCalledTimes(1); + expect(processor3).toHaveBeenCalledTimes(0); + + expect(captureExceptionSpy).toBeCalledWith(exception, { + data: { + __sentry__: true, + }, + originalException: exception, + mechanism: { type: 'internal', handled: false }, + }); + }); + test('records events dropped due to `sampleRate` option', () => { expect.assertions(1); From 34b35975ad21904eedd505a93d7beb326d38bfd7 Mon Sep 17 00:00:00 2001 From: Sigrid Huemer <32902192+s1gr1d@users.noreply.github.com> Date: Mon, 15 Sep 2025 15:51:04 +0200 Subject: [PATCH 47/61] fix(browser): Always start navigation as root span (#17648) fixes https://github.com/getsentry/sentry-javascript/issues/17450 --- .../interactions-navigation-click/init.js | 13 +++++++ .../interactions-navigation-click/subject.js | 7 ++++ .../template.html | 9 +++++ .../interactions-navigation-click/test.ts | 39 +++++++++++++++++++ .../src/tracing/browserTracingIntegration.ts | 3 ++ 5 files changed, 71 insertions(+) create mode 100644 dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/interactions-navigation-click/init.js create mode 100644 dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/interactions-navigation-click/subject.js create mode 100644 dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/interactions-navigation-click/template.html create mode 100644 dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/interactions-navigation-click/test.ts diff --git a/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/interactions-navigation-click/init.js b/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/interactions-navigation-click/init.js new file mode 100644 index 000000000000..63ce47b4c91c --- /dev/null +++ b/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/interactions-navigation-click/init.js @@ -0,0 +1,13 @@ +import * as Sentry from '@sentry/browser'; + +window.Sentry = Sentry; + +Sentry.init({ + dsn: 'https://public@dsn.ingest.sentry.io/1337', + tracesSampleRate: 1, + integrations: [ + Sentry.browserTracingIntegration({ + _experiments: { enableInteractions: true }, + }), + ], +}); diff --git a/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/interactions-navigation-click/subject.js b/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/interactions-navigation-click/subject.js new file mode 100644 index 000000000000..f45d2c974586 --- /dev/null +++ b/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/interactions-navigation-click/subject.js @@ -0,0 +1,7 @@ +// Clicking the navigate button will push a new history state, triggering navigation +document.querySelector('[data-test-id=navigate-button]').addEventListener('click', () => { + const loc = window.location; + const url = loc.href.includes('#nav') ? loc.pathname : `${loc.pathname}#nav`; + + history.pushState({}, '', url); +}); diff --git a/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/interactions-navigation-click/template.html b/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/interactions-navigation-click/template.html new file mode 100644 index 000000000000..789445ddfdd4 --- /dev/null +++ b/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/interactions-navigation-click/template.html @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/interactions-navigation-click/test.ts b/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/interactions-navigation-click/test.ts new file mode 100644 index 000000000000..4931aaa4b172 --- /dev/null +++ b/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/interactions-navigation-click/test.ts @@ -0,0 +1,39 @@ +import { expect } from '@playwright/test'; +import { sentryTest } from '../../../../utils/fixtures'; +import { envelopeRequestParser, shouldSkipTracingTest, waitForTransactionRequest } from '../../../../utils/helpers'; + +sentryTest( + 'click-triggered navigation should produce a root navigation transaction', + async ({ getLocalTestUrl, page }) => { + if (shouldSkipTracingTest()) { + sentryTest.skip(); + } + + const url = await getLocalTestUrl({ testDir: __dirname }); + + await page.goto(url); + await waitForTransactionRequest(page); // "pageload" root span + + const interactionRequestPromise = waitForTransactionRequest( + page, + evt => evt.contexts?.trace?.op === 'ui.action.click', + ); + const navigationRequestPromise = waitForTransactionRequest(page, evt => evt.contexts?.trace?.op === 'navigation'); + + await page.locator('[data-test-id=navigate-button]').click(); + + const interactionEvent = envelopeRequestParser(await interactionRequestPromise); + const navigationEvent = envelopeRequestParser(await navigationRequestPromise); + + // Navigation is root span, not a child span on the interaction + expect(interactionEvent.contexts?.trace?.op).toBe('ui.action.click'); + expect(navigationEvent.contexts?.trace?.op).toBe('navigation'); + + expect(interactionEvent.contexts?.trace?.trace_id).not.toEqual(navigationEvent.contexts?.trace?.trace_id); + + // does not contain a child navigation span + const interactionSpans = interactionEvent.spans || []; + const hasNavigationChild = interactionSpans.some(span => span.op === 'navigation' || span.op === 'http.server'); + expect(hasNavigationChild).toBeFalsy(); + }, +); diff --git a/packages/browser/src/tracing/browserTracingIntegration.ts b/packages/browser/src/tracing/browserTracingIntegration.ts index edec1b1617b6..305b1e0322a0 100644 --- a/packages/browser/src/tracing/browserTracingIntegration.ts +++ b/packages/browser/src/tracing/browserTracingIntegration.ts @@ -536,6 +536,9 @@ export const browserTracingIntegration = ((_options: Partial Date: Mon, 15 Sep 2025 16:23:44 +0200 Subject: [PATCH 48/61] chore(deps): bump hono from 4.7.10 to 4.9.7 in /dev-packages/e2e-tests/test-applications/cloudflare-hono (#17630) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Bumps [hono](https://github.com/honojs/hono) from 4.7.10 to 4.9.7.
Release notes

Sourced from hono's releases.

v4.9.7

Security

  • Fixed an issue in the bodyLimit middleware where the body size limit could be bypassed when both Content-Length and Transfer-Encoding headers were present. If you are using this middleware, please update immediately. Security Advisory

What's Changed

New Contributors

Full Changelog: https://github.com/honojs/hono/compare/v4.9.6...v4.9.7

v4.9.6

Security

Fixed a bug in URL path parsing (getPath) that could cause path confusion under malformed requests.

If you rely on reverse proxies (e.g. Nginx) for ACLs or restrict access to endpoints like /admin, please update immediately.

See advisory for details: GHSA-9hp6-4448-45g2

What's Changed

Full Changelog: https://github.com/honojs/hono/compare/v4.9.5...v4.9.6

v4.9.5

What's Changed

New Contributors

Full Changelog: https://github.com/honojs/hono/compare/v4.9.4...v4.9.5

v4.9.4

What's Changed

... (truncated)

Commits

[![Dependabot compatibility score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=hono&package-manager=npm_and_yarn&previous-version=4.7.10&new-version=4.9.7)](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores) Dependabot will resolve any conflicts with this PR as long as you don't alter it yourself. You can also trigger a rebase manually by commenting `@dependabot rebase`. [//]: # (dependabot-automerge-start) [//]: # (dependabot-automerge-end) ---
Dependabot commands and options
You can trigger Dependabot actions by commenting on this PR: - `@dependabot rebase` will rebase this PR - `@dependabot recreate` will recreate this PR, overwriting any edits that have been made to it - `@dependabot merge` will merge this PR after your CI passes on it - `@dependabot squash and merge` will squash and merge this PR after your CI passes on it - `@dependabot cancel merge` will cancel a previously requested merge and block automerging - `@dependabot reopen` will reopen this PR if it is closed - `@dependabot close` will close this PR and stop Dependabot recreating it. You can achieve the same result by closing it manually - `@dependabot show ignore conditions` will show all of the ignore conditions of the specified dependency - `@dependabot ignore this major version` will close this PR and stop Dependabot creating any more for this major version (unless you reopen the PR or upgrade to it yourself) - `@dependabot ignore this minor version` will close this PR and stop Dependabot creating any more for this minor version (unless you reopen the PR or upgrade to it yourself) - `@dependabot ignore this dependency` will close this PR and stop Dependabot creating any more for this dependency (unless you reopen the PR or upgrade to it yourself) You can disable automated security fix PRs for this repo from the [Security Alerts page](https://github.com/getsentry/sentry-javascript/network/alerts).
Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .../e2e-tests/test-applications/cloudflare-hono/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dev-packages/e2e-tests/test-applications/cloudflare-hono/package.json b/dev-packages/e2e-tests/test-applications/cloudflare-hono/package.json index 4329f837613a..a8fe024e9405 100644 --- a/dev-packages/e2e-tests/test-applications/cloudflare-hono/package.json +++ b/dev-packages/e2e-tests/test-applications/cloudflare-hono/package.json @@ -12,7 +12,7 @@ }, "dependencies": { "@sentry/cloudflare": "latest || *", - "hono": "4.7.10" + "hono": "4.9.7" }, "devDependencies": { "@cloudflare/vitest-pool-workers": "^0.8.31", From 73a2388b1e9b11d351ae105324b40d5e879b1ba0 Mon Sep 17 00:00:00 2001 From: Lukas Stracke Date: Tue, 16 Sep 2025 09:28:50 +0200 Subject: [PATCH 49/61] ref(sveltekit): Adjust `mechanism` of error events (#17646) mechanism type now follows the trace origin naming scheme closes #17639 --- CHANGELOG.md | 1 + .../sveltekit-2/tests/errors.server.test.ts | 4 +--- packages/sveltekit/src/client/handleError.ts | 2 +- packages/sveltekit/src/client/load.ts | 5 +---- packages/sveltekit/src/server-common/handleError.ts | 2 +- packages/sveltekit/src/server-common/serverRoute.ts | 2 +- packages/sveltekit/src/server-common/utils.ts | 7 ++----- packages/sveltekit/test/client/handleError.test.ts | 10 ++++------ packages/sveltekit/test/client/load.test.ts | 2 +- packages/sveltekit/test/server-common/handle.test.ts | 2 +- .../sveltekit/test/server-common/handleError.test.ts | 10 ++++------ packages/sveltekit/test/server-common/load.test.ts | 2 +- .../sveltekit/test/server-common/serverRoute.test.ts | 7 +++++-- 13 files changed, 24 insertions(+), 32 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index b470228dcc7b..5d1c10346e25 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -31,6 +31,7 @@ - ref(remix): Adjust event mechanism of `captureRemixServerException` ([#17629](https://github.com/getsentry/sentry-javascript/pull/17629)) - ref(replay-internal): Add mechanism to error caught by `replayIntegration` in debug mode ([#17606](https://github.com/getsentry/sentry-javascript/pull/17606)) - ref(solid): Add `mechanism` to error captured by `withSentryErrorBoundary` ([#17607](https://github.com/getsentry/sentry-javascript/pull/17607)) + - ref(sveltekit): Adjust `mechanism` of error events ([#17646](https://github.com/getsentry/sentry-javascript/pull/17646)) - ref(vue): Adjust mechanism in Vue error handler ([#17647](https://github.com/getsentry/sentry-javascript/pull/17647))
diff --git a/dev-packages/e2e-tests/test-applications/sveltekit-2/tests/errors.server.test.ts b/dev-packages/e2e-tests/test-applications/sveltekit-2/tests/errors.server.test.ts index fd2e58e9c2a3..0d78c9c7d649 100644 --- a/dev-packages/e2e-tests/test-applications/sveltekit-2/tests/errors.server.test.ts +++ b/dev-packages/e2e-tests/test-applications/sveltekit-2/tests/errors.server.test.ts @@ -73,9 +73,7 @@ test.describe('server-side errors', () => { value: "'HttpError' captured as exception with keys: body, status", mechanism: { handled: false, - data: { - function: 'serverRoute', - }, + type: 'auto.function.sveltekit.server_route', }, stacktrace: { frames: expect.any(Array) }, }, diff --git a/packages/sveltekit/src/client/handleError.ts b/packages/sveltekit/src/client/handleError.ts index 14a0a5b4b9cd..d6c50b3519a9 100644 --- a/packages/sveltekit/src/client/handleError.ts +++ b/packages/sveltekit/src/client/handleError.ts @@ -38,7 +38,7 @@ export function handleErrorWithSentry(handleError?: HandleClientError): HandleCl captureException(input.error, { mechanism: { - type: 'sveltekit', + type: 'auto.function.sveltekit.handle_error', handled: !!handleError, }, }); diff --git a/packages/sveltekit/src/client/load.ts b/packages/sveltekit/src/client/load.ts index bf8cf7cb058e..216d2c373547 100644 --- a/packages/sveltekit/src/client/load.ts +++ b/packages/sveltekit/src/client/load.ts @@ -29,11 +29,8 @@ function sendErrorToSentry(e: unknown): unknown { captureException(objectifiedErr, { mechanism: { - type: 'sveltekit', + type: 'auto.function.sveltekit.load', handled: false, - data: { - function: 'load', - }, }, }); diff --git a/packages/sveltekit/src/server-common/handleError.ts b/packages/sveltekit/src/server-common/handleError.ts index 0ca6597ea864..4a7bce331622 100644 --- a/packages/sveltekit/src/server-common/handleError.ts +++ b/packages/sveltekit/src/server-common/handleError.ts @@ -36,7 +36,7 @@ export function handleErrorWithSentry(handleError?: HandleServerError): HandleSe captureException(input.error, { mechanism: { - type: 'sveltekit', + type: 'auto.function.sveltekit.handle_error', handled: !!handleError, }, }); diff --git a/packages/sveltekit/src/server-common/serverRoute.ts b/packages/sveltekit/src/server-common/serverRoute.ts index d09233cb3633..3b98ce270f2d 100644 --- a/packages/sveltekit/src/server-common/serverRoute.ts +++ b/packages/sveltekit/src/server-common/serverRoute.ts @@ -62,7 +62,7 @@ export function wrapServerRouteWithSentry( () => wrappingTarget.apply(thisArg, args), ); } catch (e) { - sendErrorToSentry(e, 'serverRoute'); + sendErrorToSentry(e, 'server_route'); throw e; } finally { await flushIfServerless(); diff --git a/packages/sveltekit/src/server-common/utils.ts b/packages/sveltekit/src/server-common/utils.ts index b861bf758697..6fc67dc60612 100644 --- a/packages/sveltekit/src/server-common/utils.ts +++ b/packages/sveltekit/src/server-common/utils.ts @@ -23,7 +23,7 @@ export function getTracePropagationData(event: RequestEvent): { sentryTrace: str * * @returns an objectified version of @param e */ -export function sendErrorToSentry(e: unknown, handlerFn: 'handle' | 'load' | 'serverRoute'): object { +export function sendErrorToSentry(e: unknown, handlerFn: 'handle' | 'load' | 'server_route'): object { // In case we have a primitive, wrap it in the equivalent wrapper class (string -> String, etc.) so that we can // store a seen flag on it. const objectifiedErr = objectify(e); @@ -42,11 +42,8 @@ export function sendErrorToSentry(e: unknown, handlerFn: 'handle' | 'load' | 'se captureException(objectifiedErr, { mechanism: { - type: 'sveltekit', + type: `auto.function.sveltekit.${handlerFn}`, handled: false, - data: { - function: handlerFn, - }, }, }); diff --git a/packages/sveltekit/test/client/handleError.test.ts b/packages/sveltekit/test/client/handleError.test.ts index 810acd865aa2..3511ff62f83b 100644 --- a/packages/sveltekit/test/client/handleError.test.ts +++ b/packages/sveltekit/test/client/handleError.test.ts @@ -21,10 +21,6 @@ const navigationEvent: NavigationEvent = { url: new URL('http://example.org/users/123'), }; -const captureExceptionEventHint = { - mechanism: { handled: false, type: 'sveltekit' }, -}; - const consoleErrorSpy = vi.spyOn(console, 'error').mockImplementation(_ => {}); describe('handleError (client)', () => { @@ -42,7 +38,9 @@ describe('handleError (client)', () => { expect(returnVal).not.toBeDefined(); expect(mockCaptureException).toHaveBeenCalledTimes(1); - expect(mockCaptureException).toHaveBeenCalledWith(mockError, captureExceptionEventHint); + expect(mockCaptureException).toHaveBeenCalledWith(mockError, { + mechanism: { handled: false, type: 'auto.function.sveltekit.handle_error' }, + }); // The default handler logs the error to the console expect(consoleErrorSpy).toHaveBeenCalledTimes(1); }); @@ -56,7 +54,7 @@ describe('handleError (client)', () => { expect(returnVal.message).toEqual('Whoops!'); expect(mockCaptureException).toHaveBeenCalledTimes(1); expect(mockCaptureException).toHaveBeenCalledWith(mockError, { - mechanism: { handled: true, type: 'sveltekit' }, + mechanism: { handled: true, type: 'auto.function.sveltekit.handle_error' }, }); // Check that the default handler wasn't invoked diff --git a/packages/sveltekit/test/client/load.test.ts b/packages/sveltekit/test/client/load.test.ts index 465167265d49..b7d0b58ffe93 100644 --- a/packages/sveltekit/test/client/load.test.ts +++ b/packages/sveltekit/test/client/load.test.ts @@ -154,7 +154,7 @@ describe('wrapLoadWithSentry', () => { expect(mockCaptureException).toHaveBeenCalledTimes(1); expect(mockCaptureException).toHaveBeenCalledWith(expect.any(Error), { - mechanism: { handled: false, type: 'sveltekit', data: { function: 'load' } }, + mechanism: { handled: false, type: 'auto.function.sveltekit.load' }, }); }); diff --git a/packages/sveltekit/test/server-common/handle.test.ts b/packages/sveltekit/test/server-common/handle.test.ts index db1e1fe4811f..fdb4a1103d90 100644 --- a/packages/sveltekit/test/server-common/handle.test.ts +++ b/packages/sveltekit/test/server-common/handle.test.ts @@ -317,7 +317,7 @@ describe('sentryHandle', () => { } catch (e) { expect(mockCaptureException).toBeCalledTimes(1); expect(mockCaptureException).toBeCalledWith(expect.any(Error), { - mechanism: { handled: false, type: 'sveltekit', data: { function: 'handle' } }, + mechanism: { handled: false, type: 'auto.function.sveltekit.handle' }, }); } }); diff --git a/packages/sveltekit/test/server-common/handleError.test.ts b/packages/sveltekit/test/server-common/handleError.test.ts index 6b4b3af992d1..30fa7e78c137 100644 --- a/packages/sveltekit/test/server-common/handleError.test.ts +++ b/packages/sveltekit/test/server-common/handleError.test.ts @@ -5,10 +5,6 @@ import { handleErrorWithSentry } from '../../src/server-common/handleError'; const mockCaptureException = vi.spyOn(SentryCore, 'captureException').mockImplementation(() => 'xx'); -const captureExceptionEventHint = { - mechanism: { handled: false, type: 'sveltekit' }, -}; - function handleError(_input: { error: unknown; event: RequestEvent }): ReturnType { return { message: 'Whoops!', @@ -73,7 +69,9 @@ describe('handleError (server)', () => { expect(returnVal).not.toBeDefined(); expect(mockCaptureException).toHaveBeenCalledTimes(1); - expect(mockCaptureException).toHaveBeenCalledWith(mockError, captureExceptionEventHint); + expect(mockCaptureException).toHaveBeenCalledWith(mockError, { + mechanism: { handled: false, type: 'auto.function.sveltekit.handle_error' }, + }); // The default handler logs the error to the console expect(consoleErrorSpy).toHaveBeenCalledTimes(1); }); @@ -91,7 +89,7 @@ describe('handleError (server)', () => { expect(returnVal.message).toEqual('Whoops!'); expect(mockCaptureException).toHaveBeenCalledTimes(1); expect(mockCaptureException).toHaveBeenCalledWith(mockError, { - mechanism: { handled: true, type: 'sveltekit' }, + mechanism: { handled: true, type: 'auto.function.sveltekit.handle_error' }, }); // Check that the default handler wasn't invoked expect(consoleErrorSpy).toHaveBeenCalledTimes(0); diff --git a/packages/sveltekit/test/server-common/load.test.ts b/packages/sveltekit/test/server-common/load.test.ts index c3200d9a7927..4ec2cf6967c2 100644 --- a/packages/sveltekit/test/server-common/load.test.ts +++ b/packages/sveltekit/test/server-common/load.test.ts @@ -150,7 +150,7 @@ describe.each([ expect(mockCaptureException).toHaveBeenCalledTimes(1); expect(mockCaptureException).toHaveBeenCalledWith(expect.any(Error), { - mechanism: { handled: false, type: 'sveltekit', data: { function: 'load' } }, + mechanism: { handled: false, type: 'auto.function.sveltekit.load' }, }); }); }); diff --git a/packages/sveltekit/test/server-common/serverRoute.test.ts b/packages/sveltekit/test/server-common/serverRoute.test.ts index 046c3673a8c7..2ec3ef2cf2d2 100644 --- a/packages/sveltekit/test/server-common/serverRoute.test.ts +++ b/packages/sveltekit/test/server-common/serverRoute.test.ts @@ -85,7 +85,7 @@ describe('wrapServerRouteWithSentry', () => { }).rejects.toThrowError('Server Route Error'); expect(captureExceptionSpy).toHaveBeenCalledWith(error, { - mechanism: { type: 'sveltekit', handled: false, data: { function: 'serverRoute' } }, + mechanism: { type: 'auto.function.sveltekit.server_route', handled: false }, }); }); @@ -101,7 +101,10 @@ describe('wrapServerRouteWithSentry', () => { expect(captureExceptionSpy).toHaveBeenCalledWith( { body: { message: `error(${status}) error` }, status }, { - mechanism: { type: 'sveltekit', handled: false, data: { function: 'serverRoute' } }, + mechanism: { + type: 'auto.function.sveltekit.server_route', + handled: false, + }, }, ); }); From 7200e639ed4ee0e8429d1b4804e70d72515049ed Mon Sep 17 00:00:00 2001 From: Lukas Stracke Date: Tue, 16 Sep 2025 10:23:22 +0200 Subject: [PATCH 50/61] ref(deno): Adjust `mechanism` of errors caught by `globalHandlersIntegration` (#17635) Now follows the same trace origin-esque naming scheme as the [browser version](https://github.com/getsentry/sentry-javascript/blob/4b562bcbf0a81494627ab2c5a153ddbddb2d89ae/packages/browser/src/integrations/globalhandlers.ts#L101) of `globalHandlersIntegration`. ref https://github.com/getsentry/sentry-javascript/issues/17212 --- CHANGELOG.md | 1 + packages/deno/src/integrations/globalhandlers.ts | 4 ++-- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 5d1c10346e25..643d0eaa3654 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -21,6 +21,7 @@ - ref(core): Adjust `mechanism` in `captureConsoleIntegration` ([#17633](https://github.com/getsentry/sentry-javascript/pull/17633)) - ref(core): Adjust MCP server error event `mechanism` ([#17622](https://github.com/getsentry/sentry-javascript/pull/17622)) - ref(core): Simplify `linkedErrors` mechanism logic ([#17600](https://github.com/getsentry/sentry-javascript/pull/17600)) + - ref(deno): Adjust `mechanism` of errors caught by `globalHandlersIntegration` ([#17635](https://github.com/getsentry/sentry-javascript/pull/17635)) - ref(nextjs): Set more specific event `mechanism`s ([#17543](https://github.com/getsentry/sentry-javascript/pull/17543)) - ref(node): Adjust mechanism of express, hapi and fastify error handlers ([#17623](https://github.com/getsentry/sentry-javascript/pull/17623)) - ref(node-core): Add `mechanism` to cron instrumentations ([#17544](https://github.com/getsentry/sentry-javascript/pull/17544)) diff --git a/packages/deno/src/integrations/globalhandlers.ts b/packages/deno/src/integrations/globalhandlers.ts index 0e3639a7472d..ae66a28068c4 100644 --- a/packages/deno/src/integrations/globalhandlers.ts +++ b/packages/deno/src/integrations/globalhandlers.ts @@ -61,7 +61,7 @@ function installGlobalErrorHandler(client: Client): void { originalException: error, mechanism: { handled: false, - type: 'error', + type: 'auto.deno.global_handlers.error', }, }); @@ -110,7 +110,7 @@ function installGlobalUnhandledRejectionHandler(client: Client): void { originalException: error, mechanism: { handled: false, - type: 'unhandledrejection', + type: 'auto.deno.global_handlers.unhandledrejection', }, }); From f2e621a612aad6850870ba460edc34cf65649738 Mon Sep 17 00:00:00 2001 From: Lukas Stracke Date: Tue, 16 Sep 2025 10:25:26 +0200 Subject: [PATCH 51/61] ref(node-core): Adjust `mechanism` of `onUnhandledRejection` and `onUnhandledException` integrations (#17636) mechanism type now follows the trace origin-esque naming, similarly to global handlers [in browser](https://github.com/getsentry/sentry-javascript/blob/4b562bcbf0a81494627ab2c5a153ddbddb2d89ae/packages/browser/src/integrations/globalhandlers.ts#L101) ref https://github.com/getsentry/sentry-javascript/issues/17212 --- CHANGELOG.md | 1 + .../suites/cron/node-schedule/test.ts | 2 +- .../public-api/OnUncaughtException/basic.js | 9 +++++++ .../public-api/OnUncaughtException/test.ts | 27 +++++++++++++++++++ .../onUnhandledRejectionIntegration/test.ts | 4 +-- .../public-api/OnUncaughtException/basic.js | 9 +++++++ .../public-api/OnUncaughtException/test.ts | 27 +++++++++++++++++++ .../onUnhandledRejectionIntegration/test.ts | 4 +-- .../lib/integrations/eventFilters.test.ts | 2 +- .../src/integrations/onuncaughtexception.ts | 2 +- .../src/integrations/onunhandledrejection.ts | 2 +- 11 files changed, 81 insertions(+), 8 deletions(-) create mode 100644 dev-packages/node-core-integration-tests/suites/public-api/OnUncaughtException/basic.js create mode 100644 dev-packages/node-integration-tests/suites/public-api/OnUncaughtException/basic.js diff --git a/CHANGELOG.md b/CHANGELOG.md index 643d0eaa3654..2799383addf1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -26,6 +26,7 @@ - ref(node): Adjust mechanism of express, hapi and fastify error handlers ([#17623](https://github.com/getsentry/sentry-javascript/pull/17623)) - ref(node-core): Add `mechanism` to cron instrumentations ([#17544](https://github.com/getsentry/sentry-javascript/pull/17544)) - ref(node-core): Add more specific `mechanism.type` to worker thread errors from `childProcessIntegration` ([#17578](https://github.com/getsentry/sentry-javascript/pull/17578)) + - ref(node-core): Adjust `mechanism` of `onUnhandledRejection` and `onUnhandledException` integrations ([#17636](https://github.com/getsentry/sentry-javascript/pull/17636)) - ref(node): Add mechanism to errors captured via connect and koa integrations ([#17579](https://github.com/getsentry/sentry-javascript/pull/17579)) - ref(nuxt): Add and adjust `mechanism.type` in error events ([#17599](https://github.com/getsentry/sentry-javascript/pull/17599)) - ref(react): Add mechanism to `reactErrorHandler` and adjust mechanism in `ErrorBoundary` ([#17602](https://github.com/getsentry/sentry-javascript/pull/17602)) diff --git a/dev-packages/node-core-integration-tests/suites/cron/node-schedule/test.ts b/dev-packages/node-core-integration-tests/suites/cron/node-schedule/test.ts index 19d2d0b4e339..285e41cee304 100644 --- a/dev-packages/node-core-integration-tests/suites/cron/node-schedule/test.ts +++ b/dev-packages/node-core-integration-tests/suites/cron/node-schedule/test.ts @@ -74,7 +74,7 @@ test('node-schedule instrumentation', async () => { { type: 'Error', value: 'Error in cron job', - mechanism: { type: 'onunhandledrejection', handled: false }, + mechanism: { type: 'auto.node.onunhandledrejection', handled: false }, }, ], }, diff --git a/dev-packages/node-core-integration-tests/suites/public-api/OnUncaughtException/basic.js b/dev-packages/node-core-integration-tests/suites/public-api/OnUncaughtException/basic.js new file mode 100644 index 000000000000..656a5b19220b --- /dev/null +++ b/dev-packages/node-core-integration-tests/suites/public-api/OnUncaughtException/basic.js @@ -0,0 +1,9 @@ +const Sentry = require('@sentry/node'); +const { loggingTransport } = require('@sentry-internal/node-integration-tests'); + +Sentry.init({ + dsn: 'https://public@dsn.ingest.sentry.io/1337', + transport: loggingTransport, +}); + +throw new Error('foo'); diff --git a/dev-packages/node-core-integration-tests/suites/public-api/OnUncaughtException/test.ts b/dev-packages/node-core-integration-tests/suites/public-api/OnUncaughtException/test.ts index d27b08f152be..5a35991bfd4b 100644 --- a/dev-packages/node-core-integration-tests/suites/public-api/OnUncaughtException/test.ts +++ b/dev-packages/node-core-integration-tests/suites/public-api/OnUncaughtException/test.ts @@ -1,6 +1,7 @@ import * as childProcess from 'child_process'; import * as path from 'path'; import { describe, expect, test } from 'vitest'; +import { createRunner } from '../../../utils/runner'; describe('OnUncaughtException integration', () => { test('should close process on uncaught error with no additional listeners registered', () => @@ -74,4 +75,30 @@ describe('OnUncaughtException integration', () => { }); })); }); + + test('sets correct event mechanism', async () => { + await createRunner(__dirname, 'basic.js') + .expect({ + event: { + level: 'fatal', + exception: { + values: [ + { + type: 'Error', + value: 'foo', + mechanism: { + type: 'auto.node.onuncaughtexception', + handled: false, + }, + stacktrace: { + frames: expect.any(Array), + }, + }, + ], + }, + }, + }) + .start() + .completed(); + }); }); diff --git a/dev-packages/node-core-integration-tests/suites/public-api/onUnhandledRejectionIntegration/test.ts b/dev-packages/node-core-integration-tests/suites/public-api/onUnhandledRejectionIntegration/test.ts index 2f4a22c835a4..8ee873a4f017 100644 --- a/dev-packages/node-core-integration-tests/suites/public-api/onUnhandledRejectionIntegration/test.ts +++ b/dev-packages/node-core-integration-tests/suites/public-api/onUnhandledRejectionIntegration/test.ts @@ -83,7 +83,7 @@ test rejection`); type: 'Error', value: 'test rejection', mechanism: { - type: 'onunhandledrejection', + type: 'auto.node.onunhandledrejection', handled: false, }, stacktrace: { @@ -109,7 +109,7 @@ test rejection`); type: 'Error', value: 'test rejection', mechanism: { - type: 'onunhandledrejection', + type: 'auto.node.onunhandledrejection', handled: false, }, stacktrace: { diff --git a/dev-packages/node-integration-tests/suites/public-api/OnUncaughtException/basic.js b/dev-packages/node-integration-tests/suites/public-api/OnUncaughtException/basic.js new file mode 100644 index 000000000000..656a5b19220b --- /dev/null +++ b/dev-packages/node-integration-tests/suites/public-api/OnUncaughtException/basic.js @@ -0,0 +1,9 @@ +const Sentry = require('@sentry/node'); +const { loggingTransport } = require('@sentry-internal/node-integration-tests'); + +Sentry.init({ + dsn: 'https://public@dsn.ingest.sentry.io/1337', + transport: loggingTransport, +}); + +throw new Error('foo'); diff --git a/dev-packages/node-integration-tests/suites/public-api/OnUncaughtException/test.ts b/dev-packages/node-integration-tests/suites/public-api/OnUncaughtException/test.ts index d27b08f152be..5a35991bfd4b 100644 --- a/dev-packages/node-integration-tests/suites/public-api/OnUncaughtException/test.ts +++ b/dev-packages/node-integration-tests/suites/public-api/OnUncaughtException/test.ts @@ -1,6 +1,7 @@ import * as childProcess from 'child_process'; import * as path from 'path'; import { describe, expect, test } from 'vitest'; +import { createRunner } from '../../../utils/runner'; describe('OnUncaughtException integration', () => { test('should close process on uncaught error with no additional listeners registered', () => @@ -74,4 +75,30 @@ describe('OnUncaughtException integration', () => { }); })); }); + + test('sets correct event mechanism', async () => { + await createRunner(__dirname, 'basic.js') + .expect({ + event: { + level: 'fatal', + exception: { + values: [ + { + type: 'Error', + value: 'foo', + mechanism: { + type: 'auto.node.onuncaughtexception', + handled: false, + }, + stacktrace: { + frames: expect.any(Array), + }, + }, + ], + }, + }, + }) + .start() + .completed(); + }); }); diff --git a/dev-packages/node-integration-tests/suites/public-api/onUnhandledRejectionIntegration/test.ts b/dev-packages/node-integration-tests/suites/public-api/onUnhandledRejectionIntegration/test.ts index 468e66a058ca..d3c8b4d599ff 100644 --- a/dev-packages/node-integration-tests/suites/public-api/onUnhandledRejectionIntegration/test.ts +++ b/dev-packages/node-integration-tests/suites/public-api/onUnhandledRejectionIntegration/test.ts @@ -84,7 +84,7 @@ test rejection`); type: 'Error', value: 'test rejection', mechanism: { - type: 'onunhandledrejection', + type: 'auto.node.onunhandledrejection', handled: false, }, stacktrace: { @@ -110,7 +110,7 @@ test rejection`); type: 'Error', value: 'test rejection', mechanism: { - type: 'onunhandledrejection', + type: 'auto.node.onunhandledrejection', handled: false, }, stacktrace: { diff --git a/packages/core/test/lib/integrations/eventFilters.test.ts b/packages/core/test/lib/integrations/eventFilters.test.ts index d78b59ede112..a107efe17719 100644 --- a/packages/core/test/lib/integrations/eventFilters.test.ts +++ b/packages/core/test/lib/integrations/eventFilters.test.ts @@ -256,7 +256,7 @@ const USELESS_EXCEPTION_EVENT: Event = { values: [ {}, { - mechanism: { type: 'onunhandledrejection', handled: false }, + mechanism: { type: 'auto.node.onunhandledrejection', handled: false }, }, ], }, diff --git a/packages/node-core/src/integrations/onuncaughtexception.ts b/packages/node-core/src/integrations/onuncaughtexception.ts index 045e2e9f7736..41c4bf96917d 100644 --- a/packages/node-core/src/integrations/onuncaughtexception.ts +++ b/packages/node-core/src/integrations/onuncaughtexception.ts @@ -108,7 +108,7 @@ export function makeErrorHandler(client: NodeClient, options: OnUncaughtExceptio }, mechanism: { handled: false, - type: 'onuncaughtexception', + type: 'auto.node.onuncaughtexception', }, }); } diff --git a/packages/node-core/src/integrations/onunhandledrejection.ts b/packages/node-core/src/integrations/onunhandledrejection.ts index a11d5c3cf7b0..dbddb2a4c396 100644 --- a/packages/node-core/src/integrations/onunhandledrejection.ts +++ b/packages/node-core/src/integrations/onunhandledrejection.ts @@ -69,7 +69,7 @@ export function makeUnhandledPromiseHandler( }, mechanism: { handled: false, - type: 'onunhandledrejection', + type: 'auto.node.onunhandledrejection', }, }); }); From 18340940ba5b49829ffd4e0e5216bffc41c56c87 Mon Sep 17 00:00:00 2001 From: Lukas Stracke Date: Tue, 16 Sep 2025 10:42:57 +0200 Subject: [PATCH 52/61] ref(cloudflare): Adjust event `mechanism`s and durable object origin (#17618) - Adjust the event mechanism to use the same value as the surrounding span's origin - Adjust the durable object span origin because it didn't follow the same patter as the other origins closes #17617 --- CHANGELOG.md | 1 + .../suites/basic/test.ts | 2 +- .../suites/tracing/durableobject/test.ts | 4 ++-- .../cloudflare-workers/playwright.config.ts | 1 + .../cloudflare-workers/tests/index.test.ts | 12 +++++++----- packages/cloudflare/src/durableobject.ts | 12 ++++++------ packages/cloudflare/src/handler.ts | 10 +++++----- packages/cloudflare/src/request.ts | 4 ++-- packages/cloudflare/src/workflows.ts | 2 +- packages/cloudflare/test/handler.test.ts | 10 +++++----- packages/cloudflare/test/request.test.ts | 2 +- packages/cloudflare/test/workflow.test.ts | 2 +- 12 files changed, 33 insertions(+), 29 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 2799383addf1..a19d000debf2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -18,6 +18,7 @@ - ref(astro): Adjust `mechanism` on error events captured by astro middleware ([#17613](https://github.com/getsentry/sentry-javascript/pull/17613)) - ref(aws-severless): Slightly adjust aws-serverless mechanism type ([#17614](https://github.com/getsentry/sentry-javascript/pull/17614)) - ref(bun): Adjust `mechanism` of errors captured in Bun.serve ([#17616](https://github.com/getsentry/sentry-javascript/pull/17616)) + - ref(cloudflare): Adjust event `mechanisms` and durable object origin ([#17618](https://github.com/getsentry/sentry-javascript/pull/17618)) - ref(core): Adjust `mechanism` in `captureConsoleIntegration` ([#17633](https://github.com/getsentry/sentry-javascript/pull/17633)) - ref(core): Adjust MCP server error event `mechanism` ([#17622](https://github.com/getsentry/sentry-javascript/pull/17622)) - ref(core): Simplify `linkedErrors` mechanism logic ([#17600](https://github.com/getsentry/sentry-javascript/pull/17600)) diff --git a/dev-packages/cloudflare-integration-tests/suites/basic/test.ts b/dev-packages/cloudflare-integration-tests/suites/basic/test.ts index 1bd7b4bac094..b785e6e37fd1 100644 --- a/dev-packages/cloudflare-integration-tests/suites/basic/test.ts +++ b/dev-packages/cloudflare-integration-tests/suites/basic/test.ts @@ -15,7 +15,7 @@ it('Basic error in fetch handler', async () => { stacktrace: { frames: expect.any(Array), }, - mechanism: { type: 'cloudflare', handled: false }, + mechanism: { type: 'auto.http.cloudflare', handled: false }, }, ], }, diff --git a/dev-packages/cloudflare-integration-tests/suites/tracing/durableobject/test.ts b/dev-packages/cloudflare-integration-tests/suites/tracing/durableobject/test.ts index cfb6841004a9..a9daae21480f 100644 --- a/dev-packages/cloudflare-integration-tests/suites/tracing/durableobject/test.ts +++ b/dev-packages/cloudflare-integration-tests/suites/tracing/durableobject/test.ts @@ -12,9 +12,9 @@ it('traces a durable object method', async () => { op: 'rpc', data: expect.objectContaining({ 'sentry.op': 'rpc', - 'sentry.origin': 'auto.faas.cloudflare_durableobjects', + 'sentry.origin': 'auto.faas.cloudflare.durable_object', }), - origin: 'auto.faas.cloudflare_durableobjects', + origin: 'auto.faas.cloudflare.durable_object', }), }), transaction: 'sayHello', diff --git a/dev-packages/e2e-tests/test-applications/cloudflare-workers/playwright.config.ts b/dev-packages/e2e-tests/test-applications/cloudflare-workers/playwright.config.ts index c69c955fafd8..73abbd951b90 100644 --- a/dev-packages/e2e-tests/test-applications/cloudflare-workers/playwright.config.ts +++ b/dev-packages/e2e-tests/test-applications/cloudflare-workers/playwright.config.ts @@ -15,6 +15,7 @@ const config = getPlaywrightConfig( { // This comes with the risk of tests leaking into each other but the tests run quite slow so we should parallelize workers: '100%', + retries: 0, }, ); diff --git a/dev-packages/e2e-tests/test-applications/cloudflare-workers/tests/index.test.ts b/dev-packages/e2e-tests/test-applications/cloudflare-workers/tests/index.test.ts index 31cc02b8c5e8..df8e655416fb 100644 --- a/dev-packages/e2e-tests/test-applications/cloudflare-workers/tests/index.test.ts +++ b/dev-packages/e2e-tests/test-applications/cloudflare-workers/tests/index.test.ts @@ -10,7 +10,7 @@ test('Index page', async ({ baseURL }) => { test("worker's withSentry", async ({ baseURL }) => { const eventWaiter = waitForError('cloudflare-workers', event => { - return event.exception?.values?.[0]?.mechanism?.type === 'cloudflare'; + return event.exception?.values?.[0]?.mechanism?.type === 'auto.http.cloudflare'; }); const response = await fetch(`${baseURL}/throwException`); expect(response.status).toBe(500); @@ -20,25 +20,27 @@ test("worker's withSentry", async ({ baseURL }) => { test('RPC method which throws an exception to be logged to sentry', async ({ baseURL }) => { const eventWaiter = waitForError('cloudflare-workers', event => { - return event.exception?.values?.[0]?.mechanism?.type === 'cloudflare_durableobject'; + return event.exception?.values?.[0]?.mechanism?.type === 'auto.faas.cloudflare.durable_object'; }); const response = await fetch(`${baseURL}/rpc/throwException`); expect(response.status).toBe(500); const event = await eventWaiter; expect(event.exception?.values?.[0]?.value).toBe('Should be recorded in Sentry.'); }); + test("Request processed by DurableObject's fetch is recorded", async ({ baseURL }) => { const eventWaiter = waitForError('cloudflare-workers', event => { - return event.exception?.values?.[0]?.mechanism?.type === 'cloudflare_durableobject'; + return event.exception?.values?.[0]?.mechanism?.type === 'auto.faas.cloudflare.durable_object'; }); const response = await fetch(`${baseURL}/pass-to-object/throwException`); expect(response.status).toBe(500); const event = await eventWaiter; expect(event.exception?.values?.[0]?.value).toBe('Should be recorded in Sentry.'); }); + test('Websocket.webSocketMessage', async ({ baseURL }) => { const eventWaiter = waitForError('cloudflare-workers', event => { - return event.exception?.values?.[0]?.mechanism?.type === 'cloudflare_durableobject'; + return event.exception?.values?.[0]?.mechanism?.type === 'auto.faas.cloudflare.durable_object'; }); const url = new URL('/pass-to-object/ws', baseURL); url.protocol = url.protocol.replace('http', 'ws'); @@ -53,7 +55,7 @@ test('Websocket.webSocketMessage', async ({ baseURL }) => { test('Websocket.webSocketClose', async ({ baseURL }) => { const eventWaiter = waitForError('cloudflare-workers', event => { - return event.exception?.values?.[0]?.mechanism?.type === 'cloudflare_durableobject'; + return event.exception?.values?.[0]?.mechanism?.type === 'auto.faas.cloudflare.durable_object'; }); const url = new URL('/pass-to-object/ws', baseURL); url.protocol = url.protocol.replace('http', 'ws'); diff --git a/packages/cloudflare/src/durableobject.ts b/packages/cloudflare/src/durableobject.ts index bda7a9aa3538..0f139a80ccd0 100644 --- a/packages/cloudflare/src/durableobject.ts +++ b/packages/cloudflare/src/durableobject.ts @@ -79,7 +79,7 @@ function wrapMethodWithSentry( (e: unknown) => { captureException(e, { mechanism: { - type: 'cloudflare_durableobject', + type: 'auto.faas.cloudflare.durable_object', handled: false, }, }); @@ -94,7 +94,7 @@ function wrapMethodWithSentry( } catch (e) { captureException(e, { mechanism: { - type: 'cloudflare_durableobject', + type: 'auto.faas.cloudflare.durable_object', handled: false, }, }); @@ -106,7 +106,7 @@ function wrapMethodWithSentry( const attributes = wrapperOptions.spanOp ? { [SEMANTIC_ATTRIBUTE_SENTRY_OP]: wrapperOptions.spanOp, - [SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: 'auto.faas.cloudflare_durableobjects', + [SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: 'auto.faas.cloudflare.durable_object', } : {}; @@ -123,7 +123,7 @@ function wrapMethodWithSentry( (e: unknown) => { captureException(e, { mechanism: { - type: 'cloudflare_durableobject', + type: 'auto.faas.cloudflare.durable_object', handled: false, }, }); @@ -138,7 +138,7 @@ function wrapMethodWithSentry( } catch (e) { captureException(e, { mechanism: { - type: 'cloudflare_durableobject', + type: 'auto.faas.cloudflare.durable_object', handled: false, }, }); @@ -243,7 +243,7 @@ export function instrumentDurableObjectWithSentry< (_, error) => captureException(error, { mechanism: { - type: 'cloudflare_durableobject_websocket', + type: 'auto.faas.cloudflare.durable_object_websocket', handled: false, }, }), diff --git a/packages/cloudflare/src/handler.ts b/packages/cloudflare/src/handler.ts index 354233154a0b..a6e5983902c6 100644 --- a/packages/cloudflare/src/handler.ts +++ b/packages/cloudflare/src/handler.ts @@ -59,7 +59,7 @@ export function withSentry); } catch (e) { - captureException(e, { mechanism: { handled: false, type: 'cloudflare' } }); + captureException(e, { mechanism: { handled: false, type: 'auto.faas.cloudflare.scheduled' } }); throw e; } finally { waitUntil(flush(2000)); @@ -138,7 +138,7 @@ export function withSentry); } catch (e) { - captureException(e, { mechanism: { handled: false, type: 'cloudflare' } }); + captureException(e, { mechanism: { handled: false, type: 'auto.faas.cloudflare.email' } }); throw e; } finally { waitUntil(flush(2000)); @@ -188,7 +188,7 @@ export function withSentry); } catch (e) { - captureException(e, { mechanism: { handled: false, type: 'cloudflare' } }); + captureException(e, { mechanism: { handled: false, type: 'auto.faas.cloudflare.queue' } }); throw e; } finally { waitUntil(flush(2000)); @@ -220,7 +220,7 @@ export function withSentry); } catch (e) { - captureException(e, { mechanism: { handled: false, type: 'cloudflare' } }); + captureException(e, { mechanism: { handled: false, type: 'auto.faas.cloudflare.tail' } }); throw e; } finally { waitUntil(flush(2000)); diff --git a/packages/cloudflare/src/request.ts b/packages/cloudflare/src/request.ts index 45fe548696ab..5c97562d9fde 100644 --- a/packages/cloudflare/src/request.ts +++ b/packages/cloudflare/src/request.ts @@ -84,7 +84,7 @@ export function wrapRequestHandler( return await handler(); } catch (e) { if (captureErrors) { - captureException(e, { mechanism: { handled: false, type: 'cloudflare' } }); + captureException(e, { mechanism: { handled: false, type: 'auto.http.cloudflare' } }); } throw e; } finally { @@ -110,7 +110,7 @@ export function wrapRequestHandler( return res; } catch (e) { if (captureErrors) { - captureException(e, { mechanism: { handled: false, type: 'cloudflare' } }); + captureException(e, { mechanism: { handled: false, type: 'auto.http.cloudflare' } }); } throw e; } finally { diff --git a/packages/cloudflare/src/workflows.ts b/packages/cloudflare/src/workflows.ts index 97726e9b9270..336df2abe301 100644 --- a/packages/cloudflare/src/workflows.ts +++ b/packages/cloudflare/src/workflows.ts @@ -103,7 +103,7 @@ class WrappedWorkflowStep implements WorkflowStep { span.setStatus({ code: 1 }); return result; } catch (error) { - captureException(error, { mechanism: { handled: true, type: 'cloudflare' } }); + captureException(error, { mechanism: { handled: true, type: 'auto.faas.cloudflare.workflow' } }); throw error; } finally { this._ctx.waitUntil(flush(2000)); diff --git a/packages/cloudflare/test/handler.test.ts b/packages/cloudflare/test/handler.test.ts index ddd4b0010ec0..97e93199ea31 100644 --- a/packages/cloudflare/test/handler.test.ts +++ b/packages/cloudflare/test/handler.test.ts @@ -305,7 +305,7 @@ describe('withSentry', () => { expect(captureExceptionSpy).toHaveBeenCalledTimes(1); expect(captureExceptionSpy).toHaveBeenLastCalledWith(error, { - mechanism: { handled: false, type: 'cloudflare' }, + mechanism: { handled: false, type: 'auto.faas.cloudflare.scheduled' }, }); }); @@ -545,7 +545,7 @@ describe('withSentry', () => { expect(captureExceptionSpy).toHaveBeenCalledTimes(1); expect(captureExceptionSpy).toHaveBeenLastCalledWith(error, { - mechanism: { handled: false, type: 'cloudflare' }, + mechanism: { handled: false, type: 'auto.faas.cloudflare.email' }, }); }); @@ -784,7 +784,7 @@ describe('withSentry', () => { expect(captureExceptionSpy).toHaveBeenCalledTimes(1); expect(captureExceptionSpy).toHaveBeenLastCalledWith(error, { - mechanism: { handled: false, type: 'cloudflare' }, + mechanism: { handled: false, type: 'auto.faas.cloudflare.queue' }, }); }); @@ -1027,7 +1027,7 @@ describe('withSentry', () => { expect(captureExceptionSpy).toHaveBeenCalledTimes(1); expect(captureExceptionSpy).toHaveBeenLastCalledWith(error, { - mechanism: { handled: false, type: 'cloudflare' }, + mechanism: { handled: false, type: 'auto.faas.cloudflare.tail' }, }); }); @@ -1102,7 +1102,7 @@ describe('withSentry', () => { expect(captureExceptionSpy).toHaveBeenCalledTimes(1); expect(captureExceptionSpy).toHaveBeenLastCalledWith(error, { - mechanism: { handled: false, type: 'cloudflare' }, + mechanism: { handled: false, type: 'auto.faas.cloudflare.error_handler' }, }); expect(errorHandlerResponse?.status).toBe(500); }); diff --git a/packages/cloudflare/test/request.test.ts b/packages/cloudflare/test/request.test.ts index ad323e3c5b5a..d6d0de5824a1 100644 --- a/packages/cloudflare/test/request.test.ts +++ b/packages/cloudflare/test/request.test.ts @@ -192,7 +192,7 @@ describe('withSentry', () => { expect(captureExceptionSpy).toHaveBeenCalledTimes(1); expect(captureExceptionSpy).toHaveBeenLastCalledWith(error, { - mechanism: { handled: false, type: 'cloudflare' }, + mechanism: { handled: false, type: 'auto.http.cloudflare' }, }); }); diff --git a/packages/cloudflare/test/workflow.test.ts b/packages/cloudflare/test/workflow.test.ts index e1a6c87e5279..2ca2ccd28e46 100644 --- a/packages/cloudflare/test/workflow.test.ts +++ b/packages/cloudflare/test/workflow.test.ts @@ -272,7 +272,7 @@ describe.skipIf(NODE_MAJOR_VERSION < 20)('workflows', () => { expect.objectContaining({ type: 'Error', value: 'Test error', - mechanism: { type: 'cloudflare', handled: true }, + mechanism: { type: 'auto.faas.cloudflare.workflow', handled: true }, }), ], }, From 702cc87913bbd87343c5da2f77b7b06bb12e5a59 Mon Sep 17 00:00:00 2001 From: Lukas Stracke Date: Tue, 16 Sep 2025 10:43:25 +0200 Subject: [PATCH 53/61] ref(solidstart): Adjust event `mechanism` in `withServerActionInstrumentation` (#17637) mechanism type now follows the trace origin naming scheme closes #17638 --- CHANGELOG.md | 1 + .../solidstart-dynamic-import/tests/errors.server.test.ts | 2 +- .../solidstart-spa/tests/errors.server.test.ts | 2 +- .../solidstart-top-level-import/tests/errors.server.test.ts | 2 +- .../test-applications/solidstart/tests/errors.server.test.ts | 2 +- .../solidstart/src/server/withServerActionInstrumentation.ts | 2 +- .../test/server/withServerActionInstrumentation.test.ts | 4 +++- 7 files changed, 9 insertions(+), 6 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index a19d000debf2..8cd1f67d0526 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -34,6 +34,7 @@ - ref(remix): Adjust event mechanism of `captureRemixServerException` ([#17629](https://github.com/getsentry/sentry-javascript/pull/17629)) - ref(replay-internal): Add mechanism to error caught by `replayIntegration` in debug mode ([#17606](https://github.com/getsentry/sentry-javascript/pull/17606)) - ref(solid): Add `mechanism` to error captured by `withSentryErrorBoundary` ([#17607](https://github.com/getsentry/sentry-javascript/pull/17607)) + - ref(solidstart): Adjust event mechanism in withServerActionInstrumentation ([#17637](https://github.com/getsentry/sentry-javascript/pull/17637)) - ref(sveltekit): Adjust `mechanism` of error events ([#17646](https://github.com/getsentry/sentry-javascript/pull/17646)) - ref(vue): Adjust mechanism in Vue error handler ([#17647](https://github.com/getsentry/sentry-javascript/pull/17647)) diff --git a/dev-packages/e2e-tests/test-applications/solidstart-dynamic-import/tests/errors.server.test.ts b/dev-packages/e2e-tests/test-applications/solidstart-dynamic-import/tests/errors.server.test.ts index 7ef5cd0e07de..72761a94fdbc 100644 --- a/dev-packages/e2e-tests/test-applications/solidstart-dynamic-import/tests/errors.server.test.ts +++ b/dev-packages/e2e-tests/test-applications/solidstart-dynamic-import/tests/errors.server.test.ts @@ -18,7 +18,7 @@ test.describe('server-side errors', () => { type: 'Error', value: 'Error thrown from Solid Start E2E test app server route', mechanism: { - type: 'solidstart', + type: 'auto.function.solidstart', handled: false, }, }, diff --git a/dev-packages/e2e-tests/test-applications/solidstart-spa/tests/errors.server.test.ts b/dev-packages/e2e-tests/test-applications/solidstart-spa/tests/errors.server.test.ts index be5c635fdd9e..0ccbbd61e2ae 100644 --- a/dev-packages/e2e-tests/test-applications/solidstart-spa/tests/errors.server.test.ts +++ b/dev-packages/e2e-tests/test-applications/solidstart-spa/tests/errors.server.test.ts @@ -18,7 +18,7 @@ test.describe('server-side errors', () => { type: 'Error', value: 'Error thrown from Solid Start E2E test app server route', mechanism: { - type: 'solidstart', + type: 'auto.function.solidstart', handled: false, }, }, diff --git a/dev-packages/e2e-tests/test-applications/solidstart-top-level-import/tests/errors.server.test.ts b/dev-packages/e2e-tests/test-applications/solidstart-top-level-import/tests/errors.server.test.ts index 682dd34e10f9..5d866efffe0e 100644 --- a/dev-packages/e2e-tests/test-applications/solidstart-top-level-import/tests/errors.server.test.ts +++ b/dev-packages/e2e-tests/test-applications/solidstart-top-level-import/tests/errors.server.test.ts @@ -18,7 +18,7 @@ test.describe('server-side errors', () => { type: 'Error', value: 'Error thrown from Solid Start E2E test app server route', mechanism: { - type: 'solidstart', + type: 'auto.function.solidstart', handled: false, }, }, diff --git a/dev-packages/e2e-tests/test-applications/solidstart/tests/errors.server.test.ts b/dev-packages/e2e-tests/test-applications/solidstart/tests/errors.server.test.ts index ccd0a802fbb2..7bf86b364da9 100644 --- a/dev-packages/e2e-tests/test-applications/solidstart/tests/errors.server.test.ts +++ b/dev-packages/e2e-tests/test-applications/solidstart/tests/errors.server.test.ts @@ -18,7 +18,7 @@ test.describe('server-side errors', () => { type: 'Error', value: 'Error thrown from Solid Start E2E test app server route', mechanism: { - type: 'solidstart', + type: 'auto.function.solidstart', handled: false, }, }, diff --git a/packages/solidstart/src/server/withServerActionInstrumentation.ts b/packages/solidstart/src/server/withServerActionInstrumentation.ts index c5c726614279..753188f805c3 100644 --- a/packages/solidstart/src/server/withServerActionInstrumentation.ts +++ b/packages/solidstart/src/server/withServerActionInstrumentation.ts @@ -48,7 +48,7 @@ export async function withServerActionInstrumentation { await expect(res).rejects.toThrow(); expect(mockCaptureException).toHaveBeenCalledTimes(1); - expect(mockCaptureException).toHaveBeenCalledWith(error, { mechanism: { handled: false, type: 'solidstart' } }); + expect(mockCaptureException).toHaveBeenCalledWith(error, { + mechanism: { handled: false, type: 'auto.function.solidstart' }, + }); }); it("doesn't call captureException for thrown redirects", async () => { From 89279215ffb0dd6da37ea438aa2eb8ebaef856fa Mon Sep 17 00:00:00 2001 From: 0xbad0c0d3 <0xbad0c0d3@gmail.com> Date: Tue, 16 Sep 2025 11:47:12 +0300 Subject: [PATCH 54/61] feat(cloudflare): Introduce lock instrumentation for `context.waitUntil` to prevent multiple instrumentation (#17539) fixes #17514 P.S.: It would be better to avoid "side effects" of instrumentation process. But it will require more changes and deeply tested. --------- Co-authored-by: cod1k --- packages/cloudflare/src/client.ts | 2 +- packages/cloudflare/src/durableobject.ts | 5 +- packages/cloudflare/src/flush.ts | 38 ------ packages/cloudflare/src/handler.ts | 21 +++- packages/cloudflare/src/sdk.ts | 2 +- .../src/utils/copyExecutionContext.ts | 47 +++++++ packages/cloudflare/src/utils/flushLock.ts | 57 +++++++++ .../src/utils/makePromiseResolver.ts | 26 ++++ .../test/copy-execution-context.test.ts | 47 +++++++ .../cloudflare/test/durableobject.test.ts | 14 +-- packages/cloudflare/test/flush-lock.test.ts | 28 +++++ packages/cloudflare/test/flush.test.ts | 30 ----- packages/cloudflare/test/handler.test.ts | 115 +++++++++--------- packages/cloudflare/test/request.test.ts | 26 ++-- 14 files changed, 300 insertions(+), 158 deletions(-) delete mode 100644 packages/cloudflare/src/flush.ts create mode 100644 packages/cloudflare/src/utils/copyExecutionContext.ts create mode 100644 packages/cloudflare/src/utils/flushLock.ts create mode 100644 packages/cloudflare/src/utils/makePromiseResolver.ts create mode 100644 packages/cloudflare/test/copy-execution-context.test.ts create mode 100644 packages/cloudflare/test/flush-lock.test.ts delete mode 100644 packages/cloudflare/test/flush.test.ts diff --git a/packages/cloudflare/src/client.ts b/packages/cloudflare/src/client.ts index 2de5147d3d5a..6da36cc1721c 100644 --- a/packages/cloudflare/src/client.ts +++ b/packages/cloudflare/src/client.ts @@ -1,7 +1,7 @@ import type { ClientOptions, Options, ServerRuntimeClientOptions } from '@sentry/core'; import { applySdkMetadata, ServerRuntimeClient } from '@sentry/core'; -import type { makeFlushLock } from './flush'; import type { CloudflareTransportOptions } from './transport'; +import type { makeFlushLock } from './utils/flushLock'; /** * The Sentry Cloudflare SDK Client. diff --git a/packages/cloudflare/src/durableobject.ts b/packages/cloudflare/src/durableobject.ts index 0f139a80ccd0..7fbb0592970a 100644 --- a/packages/cloudflare/src/durableobject.ts +++ b/packages/cloudflare/src/durableobject.ts @@ -18,6 +18,7 @@ import { isInstrumented, markAsInstrumented } from './instrument'; import { getFinalOptions } from './options'; import { wrapRequestHandler } from './request'; import { init } from './sdk'; +import { copyExecutionContext } from './utils/copyExecutionContext'; type MethodWrapperOptions = { spanName?: string; @@ -192,9 +193,11 @@ export function instrumentDurableObjectWithSentry< C extends new (state: DurableObjectState, env: E) => T, >(optionsCallback: (env: E) => CloudflareOptions, DurableObjectClass: C): C { return new Proxy(DurableObjectClass, { - construct(target, [context, env]) { + construct(target, [ctx, env]) { setAsyncLocalStorageAsyncContextStrategy(); + const context = copyExecutionContext(ctx); + const options = getFinalOptions(optionsCallback(env), env); const obj = new target(context, env); diff --git a/packages/cloudflare/src/flush.ts b/packages/cloudflare/src/flush.ts deleted file mode 100644 index f38c805d0f8b..000000000000 --- a/packages/cloudflare/src/flush.ts +++ /dev/null @@ -1,38 +0,0 @@ -import type { ExecutionContext } from '@cloudflare/workers-types'; - -type FlushLock = { - readonly ready: Promise; - readonly finalize: () => Promise; -}; - -/** - * Enhances the given execution context by wrapping its `waitUntil` method with a proxy - * to monitor pending tasks, and provides a flusher function to ensure all tasks - * have been completed before executing any subsequent logic. - * - * @param {ExecutionContext} context - The execution context to be enhanced. If no context is provided, the function returns undefined. - * @return {FlushLock} Returns a flusher function if a valid context is provided, otherwise undefined. - */ -export function makeFlushLock(context: ExecutionContext): FlushLock { - let resolveAllDone: () => void = () => undefined; - const allDone = new Promise(res => { - resolveAllDone = res; - }); - let pending = 0; - const originalWaitUntil = context.waitUntil.bind(context) as typeof context.waitUntil; - context.waitUntil = promise => { - pending++; - return originalWaitUntil( - promise.finally(() => { - if (--pending === 0) resolveAllDone(); - }), - ); - }; - return Object.freeze({ - ready: allDone, - finalize: () => { - if (pending === 0) resolveAllDone(); - return allDone; - }, - }); -} diff --git a/packages/cloudflare/src/handler.ts b/packages/cloudflare/src/handler.ts index a6e5983902c6..0275e01e1777 100644 --- a/packages/cloudflare/src/handler.ts +++ b/packages/cloudflare/src/handler.ts @@ -14,6 +14,7 @@ import { getFinalOptions } from './options'; import { wrapRequestHandler } from './request'; import { addCloudResourceContext } from './scope-utils'; import { init } from './sdk'; +import { copyExecutionContext } from './utils/copyExecutionContext'; /** * Wrapper for Cloudflare handlers. @@ -37,9 +38,11 @@ export function withSentry>) { - const [request, env, context] = args; + const [request, env, ctx] = args; const options = getFinalOptions(optionsCallback(env), env); + const context = copyExecutionContext(ctx); + args[2] = context; return wrapRequestHandler({ options, request, context }, () => target.apply(thisArg, args)); }, @@ -71,7 +74,9 @@ export function withSentry>) { - const [event, env, context] = args; + const [event, env, ctx] = args; + const context = copyExecutionContext(ctx); + args[2] = context; return withIsolationScope(isolationScope => { const options = getFinalOptions(optionsCallback(env), env); const waitUntil = context.waitUntil.bind(context); @@ -114,7 +119,9 @@ export function withSentry>) { - const [emailMessage, env, context] = args; + const [emailMessage, env, ctx] = args; + const context = copyExecutionContext(ctx); + args[2] = context; return withIsolationScope(isolationScope => { const options = getFinalOptions(optionsCallback(env), env); const waitUntil = context.waitUntil.bind(context); @@ -155,7 +162,9 @@ export function withSentry>) { - const [batch, env, context] = args; + const [batch, env, ctx] = args; + const context = copyExecutionContext(ctx); + args[2] = context; return withIsolationScope(isolationScope => { const options = getFinalOptions(optionsCallback(env), env); @@ -205,7 +214,9 @@ export function withSentry>) { - const [, env, context] = args; + const [, env, ctx] = args; + const context = copyExecutionContext(ctx); + args[2] = context; return withIsolationScope(async isolationScope => { const options = getFinalOptions(optionsCallback(env), env); diff --git a/packages/cloudflare/src/sdk.ts b/packages/cloudflare/src/sdk.ts index 9d4fb8d749ae..416ae47f312f 100644 --- a/packages/cloudflare/src/sdk.ts +++ b/packages/cloudflare/src/sdk.ts @@ -12,10 +12,10 @@ import { } from '@sentry/core'; import type { CloudflareClientOptions, CloudflareOptions } from './client'; import { CloudflareClient } from './client'; -import { makeFlushLock } from './flush'; import { fetchIntegration } from './integrations/fetch'; import { setupOpenTelemetryTracer } from './opentelemetry/tracer'; import { makeCloudflareTransport } from './transport'; +import { makeFlushLock } from './utils/flushLock'; import { defaultStackParser } from './vendor/stacktrace'; /** Get the default integrations for the Cloudflare SDK. */ diff --git a/packages/cloudflare/src/utils/copyExecutionContext.ts b/packages/cloudflare/src/utils/copyExecutionContext.ts new file mode 100644 index 000000000000..737a622d7a1c --- /dev/null +++ b/packages/cloudflare/src/utils/copyExecutionContext.ts @@ -0,0 +1,47 @@ +import { type DurableObjectState, type ExecutionContext } from '@cloudflare/workers-types'; + +const kBound = Symbol.for('kBound'); + +const defaultPropertyOptions: PropertyDescriptor = { + enumerable: true, + configurable: true, + writable: true, +}; + +/** + * Clones the given execution context by creating a shallow copy while ensuring the binding of specific methods. + * + * @param {ExecutionContext|DurableObjectState|void} ctx - The execution context to clone. Can be void. + * @return {ExecutionContext|DurableObjectState|void} A cloned execution context with bound methods, or the original void value if no context was provided. + */ +export function copyExecutionContext(ctx: T): T { + if (!ctx) return ctx; + return Object.create(ctx, { + waitUntil: { ...defaultPropertyOptions, value: copyAndBindMethod(ctx, 'waitUntil') }, + ...('passThroughOnException' in ctx && { + passThroughOnException: { ...defaultPropertyOptions, value: copyAndBindMethod(ctx, 'passThroughOnException') }, + }), + }); +} + +/** + * Copies a method from the given object and ensures the copied method remains bound to the original object's context. + * + * @param {object} obj - The object containing the method to be copied and bound. + * @param {string|symbol} method - The key of the method within the object to be copied and bound. + * @return {Function} - The copied and bound method, or the original property if it is not a function. + */ +function copyAndBindMethod(obj: T, method: K): T[K] { + const methodImpl = obj[method]; + if (typeof methodImpl !== 'function') return methodImpl; + if ((methodImpl as T[K] & { [kBound]?: boolean })[kBound]) return methodImpl; + const bound = methodImpl.bind(obj); + + return new Proxy(bound, { + get: (target, prop, receiver) => { + if (kBound === prop) return true; + if ('bind' === prop) return () => receiver; + return Reflect.get(target, prop, receiver); + }, + }); +} diff --git a/packages/cloudflare/src/utils/flushLock.ts b/packages/cloudflare/src/utils/flushLock.ts new file mode 100644 index 000000000000..dbb8d3666749 --- /dev/null +++ b/packages/cloudflare/src/utils/flushLock.ts @@ -0,0 +1,57 @@ +import type { ExecutionContext } from '@cloudflare/workers-types'; +import { createPromiseResolver } from './makePromiseResolver'; + +type FlushLock = { + readonly ready: Promise; + readonly finalize: () => Promise; +}; +type MaybeLockable = T & { [kFlushLock]?: FlushLock }; + +const kFlushLock = Symbol.for('kFlushLock'); + +function getInstrumentedLock(o: MaybeLockable): FlushLock | undefined { + return o[kFlushLock]; +} + +function storeInstrumentedLock(o: MaybeLockable, lock: FlushLock): void { + o[kFlushLock] = lock; +} + +/** + * Enhances the given execution context by wrapping its `waitUntil` method with a proxy + * to monitor pending tasks and provides a flusher function to ensure all tasks + * have been completed before executing any subsequent logic. + * + * @param {ExecutionContext} context - The execution context to be enhanced. If no context is provided, the function returns undefined. + * @return {FlushLock} Returns a flusher function if a valid context is provided, otherwise undefined. + */ +export function makeFlushLock(context: ExecutionContext): FlushLock { + // eslint-disable-next-line @typescript-eslint/unbound-method + let lock = getInstrumentedLock(context.waitUntil); + if (lock) { + // It is fine to return the same lock multiple times because this means the context has already been instrumented. + return lock; + } + let pending = 0; + const originalWaitUntil = context.waitUntil.bind(context) as typeof context.waitUntil; + const { promise, resolve } = createPromiseResolver(); + const hijackedWaitUntil: typeof originalWaitUntil = promise => { + pending++; + return originalWaitUntil( + promise.finally(() => { + if (--pending === 0) resolve(); + }), + ); + }; + lock = Object.freeze({ + ready: promise, + finalize: () => { + if (pending === 0) resolve(); + return promise; + }, + }) as FlushLock; + storeInstrumentedLock(hijackedWaitUntil, lock); + context.waitUntil = hijackedWaitUntil; + + return lock; +} diff --git a/packages/cloudflare/src/utils/makePromiseResolver.ts b/packages/cloudflare/src/utils/makePromiseResolver.ts new file mode 100644 index 000000000000..f772951406b1 --- /dev/null +++ b/packages/cloudflare/src/utils/makePromiseResolver.ts @@ -0,0 +1,26 @@ +type PromiseWithResolvers = { + readonly promise: Promise; + readonly resolve: (value?: T | PromiseLike) => void; + readonly reject: (reason?: E) => void; +}; +/** + * Creates an object containing a promise, along with its corresponding resolve and reject functions. + * + * This method provides a convenient way to create a promise and access its resolvers externally. + * + * @template T - The type of the resolved value of the promise. + * @template E - The type of the rejected value of the promise. Defaults to `unknown`. + * @return {PromiseWithResolvers} An object containing the promise and its resolve and reject functions. + */ +export function createPromiseResolver(): PromiseWithResolvers { + if ('withResolvers' in Promise && typeof Promise.withResolvers === 'function') { + return Promise.withResolvers(); + } + let resolve; + let reject; + const promise = new Promise((res, rej) => { + resolve = res; + reject = rej; + }); + return { promise, resolve, reject } as unknown as PromiseWithResolvers; +} diff --git a/packages/cloudflare/test/copy-execution-context.test.ts b/packages/cloudflare/test/copy-execution-context.test.ts new file mode 100644 index 000000000000..61757cf1f355 --- /dev/null +++ b/packages/cloudflare/test/copy-execution-context.test.ts @@ -0,0 +1,47 @@ +import { type ExecutionContext } from '@cloudflare/workers-types'; +import { type Mocked, describe, expect, it, vi } from 'vitest'; +import { copyExecutionContext } from '../src/utils/copyExecutionContext'; + +describe('Copy of the execution context', () => { + describe.for(['waitUntil', 'passThroughOnException'])('%s', method => { + it('Was not bound more than once', async () => { + const context = makeExecutionContextMock(); + const copy = copyExecutionContext(context); + const copy_of_copy = copyExecutionContext(copy); + + expect(copy[method]).toBe(copy_of_copy[method]); + }); + it('Copied method is bound to the original', async () => { + const context = makeExecutionContextMock(); + const copy = copyExecutionContext(context); + + expect(copy[method]()).toBe(context); + }); + it('Copied method "rebind" prevention', async () => { + const context = makeExecutionContextMock(); + const copy = copyExecutionContext(context); + expect(copy[method].bind('test')).toBe(copy[method]); + }); + }); + + it('No side effects', async () => { + const context = makeExecutionContextMock(); + expect(() => copyExecutionContext(Object.freeze(context))).not.toThrow( + /Cannot define property \w+, object is not extensible/, + ); + }); + it('Respects symbols', async () => { + const s = Symbol('test'); + const context = makeExecutionContextMock(); + context[s] = {}; + const copy = copyExecutionContext(context); + expect(copy[s]).toBe(context[s]); + }); +}); + +function makeExecutionContextMock() { + return { + waitUntil: vi.fn().mockReturnThis(), + passThroughOnException: vi.fn().mockReturnThis(), + } as unknown as Mocked; +} diff --git a/packages/cloudflare/test/durableobject.test.ts b/packages/cloudflare/test/durableobject.test.ts index ce794dc7fb69..4c4d5c63bfd3 100644 --- a/packages/cloudflare/test/durableobject.test.ts +++ b/packages/cloudflare/test/durableobject.test.ts @@ -1,8 +1,9 @@ import type { ExecutionContext } from '@cloudflare/workers-types'; import * as SentryCore from '@sentry/core'; -import { afterEach, describe, expect, it, onTestFinished, vi } from 'vitest'; +import { afterEach, describe, expect, it, vi } from 'vitest'; import { instrumentDurableObjectWithSentry } from '../src'; import { isInstrumented } from '../src/instrument'; +import { createPromiseResolver } from '../src/utils/makePromiseResolver'; describe('instrumentDurableObjectWithSentry', () => { afterEach(() => { @@ -122,15 +123,13 @@ describe('instrumentDurableObjectWithSentry', () => { }); it('flush performs after all waitUntil promises are finished', async () => { - vi.useFakeTimers(); - onTestFinished(() => { - vi.useRealTimers(); - }); const flush = vi.spyOn(SentryCore.Client.prototype, 'flush'); const waitUntil = vi.fn(); + const { promise, resolve } = createPromiseResolver(); + process.nextTick(resolve); const testClass = vi.fn(context => ({ fetch: () => { - context.waitUntil(new Promise(res => setTimeout(res))); + context.waitUntil(promise); return new Response('test'); }, })); @@ -142,8 +141,7 @@ describe('instrumentDurableObjectWithSentry', () => { expect(() => dObject.fetch(new Request('https://example.com'))).not.toThrow(); expect(flush).not.toBeCalled(); expect(waitUntil).toHaveBeenCalledOnce(); - vi.advanceTimersToNextTimer(); - await Promise.all(waitUntil.mock.calls.map(([p]) => p)); + await Promise.all(waitUntil.mock.calls.map(call => call[0])); expect(flush).toBeCalled(); }); diff --git a/packages/cloudflare/test/flush-lock.test.ts b/packages/cloudflare/test/flush-lock.test.ts new file mode 100644 index 000000000000..cc40fd3a7e03 --- /dev/null +++ b/packages/cloudflare/test/flush-lock.test.ts @@ -0,0 +1,28 @@ +import { type ExecutionContext } from '@cloudflare/workers-types'; +import { describe, expect, it, vi } from 'vitest'; +import { makeFlushLock } from '../src/utils/flushLock'; +import { createPromiseResolver } from '../src/utils/makePromiseResolver'; + +describe('Flush buffer test', () => { + const mockExecutionContext: ExecutionContext = { + waitUntil: vi.fn(), + passThroughOnException: vi.fn(), + props: null, + }; + it('should flush buffer immediately if no waitUntil were called', async () => { + const { finalize } = makeFlushLock(mockExecutionContext); + await expect(finalize()).resolves.toBeUndefined(); + }); + it('waitUntil should not be wrapped mose than once', () => { + expect(makeFlushLock(mockExecutionContext), 'Execution context wrapped twice').toBe( + makeFlushLock(mockExecutionContext), + ); + }); + it('should flush buffer only after all waitUntil were finished', async () => { + const { promise, resolve } = createPromiseResolver(); + const lock = makeFlushLock(mockExecutionContext); + mockExecutionContext.waitUntil(promise); + process.nextTick(resolve); + await expect(lock.finalize()).resolves.toBeUndefined(); + }); +}); diff --git a/packages/cloudflare/test/flush.test.ts b/packages/cloudflare/test/flush.test.ts deleted file mode 100644 index 34714711c682..000000000000 --- a/packages/cloudflare/test/flush.test.ts +++ /dev/null @@ -1,30 +0,0 @@ -import { type ExecutionContext } from '@cloudflare/workers-types'; -import { describe, expect, it, onTestFinished, vi } from 'vitest'; -import { makeFlushLock } from '../src/flush'; - -describe('Flush buffer test', () => { - const waitUntilPromises: Promise[] = []; - const mockExecutionContext: ExecutionContext = { - waitUntil: vi.fn(prmise => { - waitUntilPromises.push(prmise); - }), - passThroughOnException: vi.fn(), - }; - it('should flush buffer immediately if no waitUntil were called', async () => { - const { finalize } = makeFlushLock(mockExecutionContext); - await expect(finalize()).resolves.toBeUndefined(); - }); - it('should flush buffer only after all waitUntil were finished', async () => { - vi.useFakeTimers(); - onTestFinished(() => { - vi.useRealTimers(); - }); - const task = new Promise(resolve => setTimeout(resolve, 100)); - const lock = makeFlushLock(mockExecutionContext); - mockExecutionContext.waitUntil(task); - void lock.finalize(); - vi.advanceTimersToNextTimer(); - await Promise.all(waitUntilPromises); - await expect(lock.ready).resolves.toBeUndefined(); - }); -}); diff --git a/packages/cloudflare/test/handler.test.ts b/packages/cloudflare/test/handler.test.ts index 97e93199ea31..930e508cfddb 100644 --- a/packages/cloudflare/test/handler.test.ts +++ b/packages/cloudflare/test/handler.test.ts @@ -10,10 +10,11 @@ import type { } from '@cloudflare/workers-types'; import type { Event } from '@sentry/core'; import * as SentryCore from '@sentry/core'; -import { beforeEach, describe, expect, onTestFinished, test, vi } from 'vitest'; +import { beforeEach, describe, expect, test, vi } from 'vitest'; import { CloudflareClient } from '../src/client'; import { withSentry } from '../src/handler'; import { markAsInstrumented } from '../src/instrument'; +import { createPromiseResolver } from '../src/utils/makePromiseResolver'; // Custom type for hono-like apps (cloudflare handlers) that include errorHandler and onError type HonoLikeApp = ExportedHandler< @@ -30,8 +31,29 @@ const MOCK_ENV = { SENTRY_RELEASE: '1.1.1', }; -function addDelayedWaitUntil(context: ExecutionContext) { - context.waitUntil(new Promise(resolve => setTimeout(() => resolve()))); +function makeWaitUntilAndTask< + M extends ReturnType, + T extends M & { + readonly ready: Promise; + readonly task: { readonly promise: Promise; readonly resolve: M }; + }, +>(): T { + const waitUntil = vi.fn(); + const { promise, resolve } = createPromiseResolver(); + const resolver = vi.fn().mockImplementation(resolve).mockName('waitUntil.ready'); + Object.defineProperties(waitUntil, { + ready: { + get: () => Promise.all(waitUntil.mock.calls.map(call => call[0])).then(() => resolver), + enumerable: true, + configurable: true, + }, + task: { + value: { promise, resolve: resolver }, + enumerable: true, + configurable: true, + }, + }); + return waitUntil as T; } describe('withSentry', () => { @@ -135,27 +157,22 @@ describe('withSentry', () => { test('flush must be called when all waitUntil are done', async () => { const flush = vi.spyOn(SentryCore.Client.prototype, 'flush'); - vi.useFakeTimers(); - onTestFinished(() => { - vi.useRealTimers(); - }); + const waitUntil = makeWaitUntilAndTask(); + process.nextTick(waitUntil.task.resolve); const handler = { - fetch(_request, _env, _context) { - addDelayedWaitUntil(_context); + fetch(...args) { + args[2].waitUntil(waitUntil.task.promise); return new Response('test'); }, } satisfies ExportedHandler; const wrappedHandler = withSentry(vi.fn(), handler); - const waits: Promise[] = []; - const waitUntil = vi.fn(promise => waits.push(promise)); await wrappedHandler.fetch?.(new Request('https://example.com'), MOCK_ENV, { waitUntil, } as unknown as ExecutionContext); expect(flush).not.toBeCalled(); - expect(waitUntil).toBeCalled(); - vi.advanceTimersToNextTimer().runAllTimers(); - await Promise.all(waits); + const ready = await waitUntil.ready; + expect(flush).toHaveBeenCalledAfter(ready); expect(flush).toHaveBeenCalledOnce(); }); }); @@ -375,28 +392,23 @@ describe('withSentry', () => { test('flush must be called when all waitUntil are done', async () => { const flush = vi.spyOn(SentryCore.Client.prototype, 'flush'); - vi.useFakeTimers(); - onTestFinished(() => { - vi.useRealTimers(); - }); + const waitUntil = makeWaitUntilAndTask(); + process.nextTick(waitUntil.task.resolve); const handler = { - scheduled(_controller, _env, _context) { - addDelayedWaitUntil(_context); + scheduled(...args) { + args[2].waitUntil(waitUntil.task.promise); return; }, } satisfies ExportedHandler; const wrappedHandler = withSentry(vi.fn(), handler); - const waits: Promise[] = []; - const waitUntil = vi.fn(promise => waits.push(promise)); await wrappedHandler.scheduled?.(createMockScheduledController(), MOCK_ENV, { waitUntil, } as unknown as ExecutionContext); expect(flush).not.toBeCalled(); - expect(waitUntil).toBeCalled(); - vi.advanceTimersToNextTimer().runAllTimers(); - await Promise.all(waits); + await waitUntil.ready; expect(flush).toHaveBeenCalledOnce(); + expect(flush).toHaveBeenCalledAfter(waitUntil); }); }); @@ -614,28 +626,24 @@ describe('withSentry', () => { test('flush must be called when all waitUntil are done', async () => { const flush = vi.spyOn(SentryCore.Client.prototype, 'flush'); - vi.useFakeTimers(); - onTestFinished(() => { - vi.useRealTimers(); - }); + const waitUntil = makeWaitUntilAndTask(); + + process.nextTick(waitUntil.task.resolve); const handler = { - email(_controller, _env, _context) { - addDelayedWaitUntil(_context); + email(...args) { + args[2].waitUntil(waitUntil.task.promise); return; }, } satisfies ExportedHandler; const wrappedHandler = withSentry(vi.fn(), handler); - const waits: Promise[] = []; - const waitUntil = vi.fn(promise => waits.push(promise)); await wrappedHandler.email?.(createMockEmailMessage(), MOCK_ENV, { waitUntil, } as unknown as ExecutionContext); expect(flush).not.toBeCalled(); - expect(waitUntil).toBeCalled(); - vi.advanceTimersToNextTimer().runAllTimers(); - await Promise.all(waits); + const ready = await waitUntil.ready; expect(flush).toHaveBeenCalledOnce(); + expect(flush).toHaveBeenCalledAfter(ready); }); }); @@ -857,27 +865,22 @@ describe('withSentry', () => { test('flush must be called when all waitUntil are done', async () => { const flush = vi.spyOn(SentryCore.Client.prototype, 'flush'); - vi.useFakeTimers(); - onTestFinished(() => { - vi.useRealTimers(); - }); + const waitUntil = makeWaitUntilAndTask(); + process.nextTick(waitUntil.task.resolve); const handler = { - queue(_controller, _env, _context) { - addDelayedWaitUntil(_context); + queue(...args) { + args[2].waitUntil(waitUntil.task.promise); return; }, } satisfies ExportedHandler; const wrappedHandler = withSentry(vi.fn(), handler); - const waits: Promise[] = []; - const waitUntil = vi.fn(promise => waits.push(promise)); await wrappedHandler.queue?.(createMockQueueBatch(), MOCK_ENV, { waitUntil, } as unknown as ExecutionContext); expect(flush).not.toBeCalled(); - expect(waitUntil).toBeCalled(); - vi.advanceTimersToNextTimer().runAllTimers(); - await Promise.all(waits); + + expect(flush).toHaveBeenCalledAfter(await waitUntil.ready); expect(flush).toHaveBeenCalledOnce(); }); }); @@ -1054,29 +1057,23 @@ describe('withSentry', () => { test('flush must be called when all waitUntil are done', async () => { const flush = vi.spyOn(SentryCore.Client.prototype, 'flush'); - vi.useFakeTimers(); - onTestFinished(() => { - vi.useRealTimers(); - flush.mockRestore(); - }); + const waitUntil = makeWaitUntilAndTask(); + process.nextTick(waitUntil.task.resolve); const handler = { - tail(_controller, _env, _context) { - addDelayedWaitUntil(_context); + tail(...args) { + args[2].waitUntil(waitUntil.task.promise); return; }, } satisfies ExportedHandler; const wrappedHandler = withSentry(vi.fn(), handler); - const waits: Promise[] = []; - const waitUntil = vi.fn(promise => waits.push(promise)); await wrappedHandler.tail?.(createMockTailEvent(), MOCK_ENV, { waitUntil, } as unknown as ExecutionContext); expect(flush).not.toBeCalled(); - expect(waitUntil).toBeCalled(); - vi.advanceTimersToNextTimer().runAllTimers(); - await Promise.all(waits); + const ready = await waitUntil.ready; expect(flush).toHaveBeenCalledOnce(); + expect(flush).toHaveBeenCalledAfter(ready); }); }); diff --git a/packages/cloudflare/test/request.test.ts b/packages/cloudflare/test/request.test.ts index d6d0de5824a1..eaf06f8f7f3e 100644 --- a/packages/cloudflare/test/request.test.ts +++ b/packages/cloudflare/test/request.test.ts @@ -4,20 +4,17 @@ import type { ExecutionContext } from '@cloudflare/workers-types'; import type { Event } from '@sentry/core'; import * as SentryCore from '@sentry/core'; -import { beforeAll, beforeEach, describe, expect, onTestFinished, test, vi } from 'vitest'; +import { beforeAll, beforeEach, describe, expect, test, vi } from 'vitest'; import { setAsyncLocalStorageAsyncContextStrategy } from '../src/async'; import type { CloudflareOptions } from '../src/client'; import { CloudflareClient } from '../src/client'; import { wrapRequestHandler } from '../src/request'; +import { createPromiseResolver } from '../src/utils/makePromiseResolver'; const MOCK_OPTIONS: CloudflareOptions = { dsn: 'https://public@dsn.ingest.sentry.io/1337', }; -function addDelayedWaitUntil(context: ExecutionContext) { - context.waitUntil(new Promise(resolve => setTimeout(() => resolve()))); -} - describe('withSentry', () => { beforeAll(() => { setAsyncLocalStorageAsyncContextStrategy(); @@ -70,25 +67,24 @@ describe('withSentry', () => { test('flush must be called when all waitUntil are done', async () => { const flush = vi.spyOn(SentryCore.Client.prototype, 'flush'); - vi.useFakeTimers(); - onTestFinished(() => { - vi.useRealTimers(); - }); - const waits: Promise[] = []; - const waitUntil = vi.fn(promise => waits.push(promise)); + const waitUntil = vi.fn(); const context = { waitUntil, } as unknown as ExecutionContext; + const { promise, resolve } = createPromiseResolver(); + const resolved = vi.fn(() => resolve()); + process.nextTick(resolved); + await wrapRequestHandler({ options: MOCK_OPTIONS, request: new Request('https://example.com'), context }, () => { - addDelayedWaitUntil(context); + context.waitUntil(promise); return new Response('test'); }); expect(flush).not.toBeCalled(); expect(waitUntil).toBeCalled(); - vi.advanceTimersToNextTimerAsync().then(() => vi.runAllTimers()); - await Promise.all(waits); + await Promise.all(waitUntil.mock.calls.map(call => call[0])); + expect(flush).toHaveBeenCalledAfter(resolved); expect(flush).toHaveBeenCalledOnce(); }); @@ -337,5 +333,5 @@ function createMockExecutionContext(): ExecutionContext { return { waitUntil: vi.fn(), passThroughOnException: vi.fn(), - }; + } as unknown as ExecutionContext; } From 08c30f8fcc7e142e68bd1f91e9ebc4d2be4becc5 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 16 Sep 2025 10:59:06 +0200 Subject: [PATCH 55/61] chore(deps): bump next from 14.2.29 to 14.2.32 in /dev-packages/e2e-tests/test-applications/nextjs-t3 (#17656) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Bumps [next](https://github.com/vercel/next.js) from 14.2.29 to 14.2.32.
Release notes

Sourced from next's releases.

v14.2.32

[!NOTE]
This release is backporting bug fixes. It does not include all pending features/changes on canary.

Core Changes

  • fix router handling when setting a location response header #82588

Credits

Huge thanks to @​ztanner for helping!

v14.2.31

[!NOTE]
This release is backporting bug fixes. It does not include all pending features/changes on canary.

Core Changes

  • fix(next/image): improve and simplify detect-content-type (#82179)
  • fix(next/image): fix image-optimizer.ts headers (#82178)

Credits

Huge thanks to @​styfle and @​ztanner for helping!

Commits
  • 89ee561 v14.2.32
  • 6a974ad [backport v14]: fix router handling when setting a location response header (...
  • 55f7662 v14.2.31
  • 5dd68a5 [backport v14]: fix(next/image): improve and simplify detect-content-type (#8...
  • bcc7c65 [backport v14]: fix(next/image): fix image-optimizer.ts headers (#82114) (#82...
  • 243072b v14.2.30
  • f523d4a [backport]: config.allowedDevOrigins (#80410)
  • See full diff in compare view

[![Dependabot compatibility score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=next&package-manager=npm_and_yarn&previous-version=14.2.29&new-version=14.2.32)](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores) Dependabot will resolve any conflicts with this PR as long as you don't alter it yourself. You can also trigger a rebase manually by commenting `@dependabot rebase`. [//]: # (dependabot-automerge-start) [//]: # (dependabot-automerge-end) ---
Dependabot commands and options
You can trigger Dependabot actions by commenting on this PR: - `@dependabot rebase` will rebase this PR - `@dependabot recreate` will recreate this PR, overwriting any edits that have been made to it - `@dependabot merge` will merge this PR after your CI passes on it - `@dependabot squash and merge` will squash and merge this PR after your CI passes on it - `@dependabot cancel merge` will cancel a previously requested merge and block automerging - `@dependabot reopen` will reopen this PR if it is closed - `@dependabot close` will close this PR and stop Dependabot recreating it. You can achieve the same result by closing it manually - `@dependabot show ignore conditions` will show all of the ignore conditions of the specified dependency - `@dependabot ignore this major version` will close this PR and stop Dependabot creating any more for this major version (unless you reopen the PR or upgrade to it yourself) - `@dependabot ignore this minor version` will close this PR and stop Dependabot creating any more for this minor version (unless you reopen the PR or upgrade to it yourself) - `@dependabot ignore this dependency` will close this PR and stop Dependabot creating any more for this dependency (unless you reopen the PR or upgrade to it yourself) You can disable automated security fix PRs for this repo from the [Security Alerts page](https://github.com/getsentry/sentry-javascript/network/alerts).
Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- dev-packages/e2e-tests/test-applications/nextjs-t3/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dev-packages/e2e-tests/test-applications/nextjs-t3/package.json b/dev-packages/e2e-tests/test-applications/nextjs-t3/package.json index af4d92497ad9..f6e3613b379d 100644 --- a/dev-packages/e2e-tests/test-applications/nextjs-t3/package.json +++ b/dev-packages/e2e-tests/test-applications/nextjs-t3/package.json @@ -20,7 +20,7 @@ "@trpc/client": "~11.3.0", "@trpc/react-query": "~11.3.0", "@trpc/server": "~11.3.0", - "next": "14.2.29", + "next": "14.2.32", "react": "18.3.1", "react-dom": "18.3.1", "server-only": "^0.0.1", From 108ed16e662f7e05998111d08f6153bb9ac3d9a3 Mon Sep 17 00:00:00 2001 From: Lukas Stracke Date: Tue, 16 Sep 2025 12:50:42 +0200 Subject: [PATCH 56/61] test(sveltekit): Pin E2E test app SvelteKit versions and fix failing tests (#17665) - pin all SvelteKit versions used in our e2e tests - adjust one of them to use 2.41.0 (the most recent version) and adjust the test to the bugfix in this version --- .../sveltekit-2-kit-tracing/package.json | 2 +- .../test-applications/sveltekit-2-svelte-5/package.json | 2 +- .../sveltekit-2-svelte-5/tests/performance.test.ts | 8 ++++---- .../e2e-tests/test-applications/sveltekit-2/package.json | 2 +- 4 files changed, 7 insertions(+), 7 deletions(-) diff --git a/dev-packages/e2e-tests/test-applications/sveltekit-2-kit-tracing/package.json b/dev-packages/e2e-tests/test-applications/sveltekit-2-kit-tracing/package.json index 03e5441733da..2b6184b8cb28 100644 --- a/dev-packages/e2e-tests/test-applications/sveltekit-2-kit-tracing/package.json +++ b/dev-packages/e2e-tests/test-applications/sveltekit-2-kit-tracing/package.json @@ -22,7 +22,7 @@ "@playwright/test": "~1.53.2", "@sentry-internal/test-utils": "link:../../../test-utils", "@sveltejs/adapter-node": "^5.3.1", - "@sveltejs/kit": "^2.31.0", + "@sveltejs/kit": "2.31.0", "@sveltejs/vite-plugin-svelte": "^6.1.3", "svelte": "^5.38.3", "svelte-check": "^4.3.1", diff --git a/dev-packages/e2e-tests/test-applications/sveltekit-2-svelte-5/package.json b/dev-packages/e2e-tests/test-applications/sveltekit-2-svelte-5/package.json index 689934c1011f..3c5dcfd8f279 100644 --- a/dev-packages/e2e-tests/test-applications/sveltekit-2-svelte-5/package.json +++ b/dev-packages/e2e-tests/test-applications/sveltekit-2-svelte-5/package.json @@ -22,7 +22,7 @@ "@playwright/test": "~1.53.2", "@sentry-internal/test-utils": "link:../../../test-utils", "@sveltejs/adapter-auto": "^3.0.0", - "@sveltejs/kit": "^2.21.3", + "@sveltejs/kit": "2.41.0", "@sveltejs/vite-plugin-svelte": "^3.0.0", "svelte": "^5.0.0-next.115", "svelte-check": "^3.6.0", diff --git a/dev-packages/e2e-tests/test-applications/sveltekit-2-svelte-5/tests/performance.test.ts b/dev-packages/e2e-tests/test-applications/sveltekit-2-svelte-5/tests/performance.test.ts index 95855d8f8e81..6a29276f4dc8 100644 --- a/dev-packages/e2e-tests/test-applications/sveltekit-2-svelte-5/tests/performance.test.ts +++ b/dev-packages/e2e-tests/test-applications/sveltekit-2-svelte-5/tests/performance.test.ts @@ -316,7 +316,7 @@ test.describe('performance events', () => { 'sentry.origin': 'auto.navigation.sveltekit', 'sentry.op': 'navigation', 'sentry.source': 'route', - 'sentry.sveltekit.navigation.type': 'goto', + 'sentry.sveltekit.navigation.type': 'link', 'sentry.sveltekit.navigation.from': '/', 'sentry.sveltekit.navigation.to': '/redirect2', 'sentry.sample_rate': 1, @@ -337,7 +337,7 @@ test.describe('performance events', () => { 'sentry.origin': 'auto.ui.sveltekit', 'sentry.sveltekit.navigation.from': '/', 'sentry.sveltekit.navigation.to': '/redirect2', - 'sentry.sveltekit.navigation.type': 'goto', + 'sentry.sveltekit.navigation.type': 'link', }, }); @@ -353,7 +353,7 @@ test.describe('performance events', () => { 'sentry.origin': 'auto.navigation.sveltekit', 'sentry.op': 'navigation', 'sentry.source': 'route', - 'sentry.sveltekit.navigation.type': 'goto', + 'sentry.sveltekit.navigation.type': 'link', 'sentry.sveltekit.navigation.from': '/', 'sentry.sveltekit.navigation.to': '/users/[id]', 'sentry.sample_rate': 1, @@ -374,7 +374,7 @@ test.describe('performance events', () => { 'sentry.origin': 'auto.ui.sveltekit', 'sentry.sveltekit.navigation.from': '/', 'sentry.sveltekit.navigation.to': '/users/[id]', - 'sentry.sveltekit.navigation.type': 'goto', + 'sentry.sveltekit.navigation.type': 'link', }, }); }); diff --git a/dev-packages/e2e-tests/test-applications/sveltekit-2/package.json b/dev-packages/e2e-tests/test-applications/sveltekit-2/package.json index 03b4b1dad959..1f73b47bc10b 100644 --- a/dev-packages/e2e-tests/test-applications/sveltekit-2/package.json +++ b/dev-packages/e2e-tests/test-applications/sveltekit-2/package.json @@ -22,7 +22,7 @@ "@sentry-internal/test-utils": "link:../../../test-utils", "@sveltejs/adapter-auto": "^3.0.0", "@sveltejs/adapter-node": "^2.0.0", - "@sveltejs/kit": "^2.21.3", + "@sveltejs/kit": "2.21.3", "@sveltejs/vite-plugin-svelte": "^3.0.0", "svelte": "^4.2.8", "svelte-check": "^3.6.0", From 41cec5bd5000e793a63ec476b9ba9964591d695d Mon Sep 17 00:00:00 2001 From: Francesco Gringl-Novy Date: Tue, 16 Sep 2025 13:12:24 +0200 Subject: [PATCH 57/61] test: Adjust node test runner to avoid timeouts (#17663) This PR hopefully helps unflake some tests, e.g. https://github.com/getsentry/sentry-javascript/issues/17658, by: 1. Using async instead of sync methods for the test runner (hopefully a bit more performant) 2. increasing timeout for the beforeAll callback --- .../node-integration-tests/utils/runner.ts | 86 +++++++++---------- 1 file changed, 40 insertions(+), 46 deletions(-) diff --git a/dev-packages/node-integration-tests/utils/runner.ts b/dev-packages/node-integration-tests/utils/runner.ts index 02f0c112c412..dfe27dc5a4a5 100644 --- a/dev-packages/node-integration-tests/utils/runner.ts +++ b/dev-packages/node-integration-tests/utils/runner.ts @@ -13,10 +13,11 @@ import type { } from '@sentry/core'; import { normalize } from '@sentry/core'; import { createBasicSentryServer } from '@sentry-internal/test-utils'; -import { execSync, spawn, spawnSync } from 'child_process'; -import { cpSync, existsSync, mkdirSync, readFileSync, rmSync, writeFileSync } from 'fs'; +import { exec, execSync, spawn, spawnSync } from 'child_process'; +import { existsSync } from 'fs'; +import { cp, mkdir, readFile, rm, writeFile } from 'fs/promises'; import { basename, join } from 'path'; -import { inspect } from 'util'; +import { inspect, promisify } from 'util'; import { afterAll, beforeAll, describe, test } from 'vitest'; import { assertEnvelopeHeader, @@ -29,6 +30,8 @@ import { assertSentryTransaction, } from './assertions'; +const execPromise = promisify(exec); + const CLEANUP_STEPS = new Set(); export function cleanupChildProcesses(): void { @@ -215,21 +218,21 @@ export function createEsmAndCjsTests( const cjsScenarioPath = join(tmpDirPath, esmScenarioBasename.replace('.mjs', '.cjs')); const cjsInstrumentPath = join(tmpDirPath, esmInstrumentBasename.replace('.mjs', '.cjs')); - function createTmpDir(): void { - mkdirSync(tmpDirPath); + async function createTmpDir(): Promise { + await mkdir(tmpDirPath); // Copy ESM files as-is into tmp dir - writeFileSync(esmScenarioPathForRun, readFileSync(mjsScenarioPath, 'utf8')); - writeFileSync(esmInstrumentPathForRun, readFileSync(mjsInstrumentPath, 'utf8')); + await writeFile(esmScenarioPathForRun, await readFile(mjsScenarioPath, 'utf8')); + await writeFile(esmInstrumentPathForRun, await readFile(mjsInstrumentPath, 'utf8')); // Pre-create CJS converted files inside tmp dir - convertEsmFileToCjs(esmScenarioPathForRun, cjsScenarioPath); - convertEsmFileToCjs(esmInstrumentPathForRun, cjsInstrumentPath); + await convertEsmFileToCjs(esmScenarioPathForRun, cjsScenarioPath); + await convertEsmFileToCjs(esmInstrumentPathForRun, cjsInstrumentPath); // Copy any additional files/dirs into tmp dir if (options?.copyPaths) { for (const path of options.copyPaths) { - cpSync(join(cwd, path), join(tmpDirPath, path), { recursive: true }); + await cp(join(cwd, path), join(tmpDirPath, path), { recursive: true }); } } @@ -243,7 +246,7 @@ export function createEsmAndCjsTests( dependencies: additionalDependencies, } as const; - writeFileSync(join(tmpDirPath, 'package.json'), JSON.stringify(packageJson, null, 2)); + await writeFile(join(tmpDirPath, 'package.json'), JSON.stringify(packageJson, null, 2)); try { const deps = Object.entries(additionalDependencies).map(([name, range]) => { @@ -254,33 +257,24 @@ export function createEsmAndCjsTests( }); if (deps.length > 0) { - // Prefer npm for temp installs to avoid Yarn engine strictness; see https://github.com/vercel/ai/issues/7777 - // We rely on the generated package.json dependencies and run a plain install. - const result = spawnSync('npm', ['install', '--silent', '--no-audit', '--no-fund'], { - cwd: tmpDirPath, - encoding: 'utf8', - }); - - if (process.env.DEBUG) { - // eslint-disable-next-line no-console - console.log('[additionalDependencies via npm]', deps.join(' ')); - // eslint-disable-next-line no-console - console.log('[npm stdout]', result.stdout); - // eslint-disable-next-line no-console - console.log('[npm stderr]', result.stderr); - } + try { + // Prefer npm for temp installs to avoid Yarn engine strictness; see https://github.com/vercel/ai/issues/7777 + // We rely on the generated package.json dependencies and run a plain install. + const { stdout, stderr } = await execPromise('npm install --silent --no-audit --no-fund', { + cwd: tmpDirPath, + encoding: 'utf8', + }); - if (result.error) { - throw new Error( - `Failed to install additionalDependencies in tmp dir ${tmpDirPath}: ${result.error.message}`, - ); - } - if (typeof result.status === 'number' && result.status !== 0) { - throw new Error( - `Failed to install additionalDependencies in tmp dir ${tmpDirPath} (exit ${result.status}):\n${ - result.stderr || result.stdout || '(no output)' - }`, - ); + if (process.env.DEBUG) { + // eslint-disable-next-line no-console + console.log('[additionalDependencies via npm]', deps.join(' ')); + // eslint-disable-next-line no-console + console.log('[npm stdout]', stdout); + // eslint-disable-next-line no-console + console.log('[npm stderr]', stderr); + } + } catch (error) { + throw new Error(`Failed to install additionalDependencies in tmp dir ${tmpDirPath}: ${error}`); } } } catch (e) { @@ -313,31 +307,31 @@ export function createEsmAndCjsTests( }); // Create tmp directory - beforeAll(() => { - createTmpDir(); - }); + beforeAll(async () => { + await createTmpDir(); + }, 60_000); // Clean up the tmp directory after both esm and cjs suites have run - afterAll(() => { + afterAll(async () => { // First do cleanup! cleanupChildProcesses(); try { - rmSync(tmpDirPath, { recursive: true, force: true }); + await rm(tmpDirPath, { recursive: true, force: true }); } catch { if (process.env.DEBUG) { // eslint-disable-next-line no-console console.error(`Failed to remove tmp dir: ${tmpDirPath}`); } } - }); + }, 30_000); }); } -function convertEsmFileToCjs(inputPath: string, outputPath: string): void { - const cjsFileContent = readFileSync(inputPath, 'utf8'); +async function convertEsmFileToCjs(inputPath: string, outputPath: string): Promise { + const cjsFileContent = await readFile(inputPath, 'utf8'); const cjsFileContentConverted = convertEsmToCjs(cjsFileContent); - writeFileSync(outputPath, cjsFileContentConverted); + return writeFile(outputPath, cjsFileContentConverted); } /** Creates a test runner */ From 57b0656cc03ef1bd5f4ca0e28c0ce9ace313813a Mon Sep 17 00:00:00 2001 From: Francesco Gringl-Novy Date: Tue, 16 Sep 2025 13:28:10 +0200 Subject: [PATCH 58/61] ref(browser): Improve `fetchTransport` error handling (#17661) This changes our fetch transport a bit, making use of async functions and slightly adjusting error handling: 1. Technically there was a bug if fetch is not available, as we would keep increasing the `pendingBodySize`/`pendingCount` and never decrease it. 2. We had some dedicated error messages for edge cases that are IMHO not necessary - we can safe a few bytes by just using default error messages there. 3. No need to use sync promise here, all of this is async anyhow. Extracted this out of https://github.com/getsentry/sentry-javascript/pull/17641 --- packages/browser/src/transports/fetch.ts | 36 ++++++++----------- .../browser/test/transports/fetch.test.ts | 25 ++++++++++--- 2 files changed, 36 insertions(+), 25 deletions(-) diff --git a/packages/browser/src/transports/fetch.ts b/packages/browser/src/transports/fetch.ts index d21ae82486ec..f690f015f281 100644 --- a/packages/browser/src/transports/fetch.ts +++ b/packages/browser/src/transports/fetch.ts @@ -1,5 +1,5 @@ import type { Transport, TransportMakeRequestResponse, TransportRequest } from '@sentry/core'; -import { createTransport, rejectedSyncPromise } from '@sentry/core'; +import { createTransport } from '@sentry/core'; import { clearCachedImplementation, getNativeImplementation } from '@sentry-internal/browser-utils'; import type { WINDOW } from '../helpers'; import type { BrowserTransportOptions } from './types'; @@ -9,12 +9,12 @@ import type { BrowserTransportOptions } from './types'; */ export function makeFetchTransport( options: BrowserTransportOptions, - nativeFetch: typeof WINDOW.fetch | undefined = getNativeImplementation('fetch'), + nativeFetch: typeof WINDOW.fetch = getNativeImplementation('fetch'), ): Transport { let pendingBodySize = 0; let pendingCount = 0; - function makeRequest(request: TransportRequest): PromiseLike { + async function makeRequest(request: TransportRequest): Promise { const requestSize = request.body.length; pendingBodySize += requestSize; pendingCount++; @@ -39,29 +39,23 @@ export function makeFetchTransport( ...options.fetchOptions, }; - if (!nativeFetch) { - clearCachedImplementation('fetch'); - return rejectedSyncPromise('No fetch implementation available'); - } - try { - // Note: We do not need to suppress tracing here, becasue we are using the native fetch, instead of our wrapped one. - return nativeFetch(options.url, requestOptions).then(response => { - pendingBodySize -= requestSize; - pendingCount--; - return { - statusCode: response.status, - headers: { - 'x-sentry-rate-limits': response.headers.get('X-Sentry-Rate-Limits'), - 'retry-after': response.headers.get('Retry-After'), - }, - }; - }); + // Note: We do not need to suppress tracing here, because we are using the native fetch, instead of our wrapped one. + const response = await nativeFetch(options.url, requestOptions); + + return { + statusCode: response.status, + headers: { + 'x-sentry-rate-limits': response.headers.get('X-Sentry-Rate-Limits'), + 'retry-after': response.headers.get('Retry-After'), + }, + }; } catch (e) { clearCachedImplementation('fetch'); + throw e; + } finally { pendingBodySize -= requestSize; pendingCount--; - return rejectedSyncPromise(e); } } diff --git a/packages/browser/test/transports/fetch.test.ts b/packages/browser/test/transports/fetch.test.ts index cf8f8810531f..d330fe886d5f 100644 --- a/packages/browser/test/transports/fetch.test.ts +++ b/packages/browser/test/transports/fetch.test.ts @@ -1,7 +1,7 @@ import type { EventEnvelope, EventItem } from '@sentry/core'; import { createEnvelope, serializeEnvelope } from '@sentry/core'; import type { Mock } from 'vitest'; -import { describe, expect, it, vi } from 'vitest'; +import { afterEach, describe, expect, it, vi } from 'vitest'; import { makeFetchTransport } from '../../src/transports/fetch'; import type { BrowserTransportOptions } from '../../src/transports/types'; @@ -29,7 +29,11 @@ class Headers { } } -describe('NewFetchTransport', () => { +describe('fetchTransport', () => { + afterEach(() => { + vi.resetAllMocks(); + }); + it('calls fetch with the given URL', async () => { const mockFetch = vi.fn(() => Promise.resolve({ @@ -102,15 +106,28 @@ describe('NewFetchTransport', () => { }); }); - it('handles when `getNativetypeof window.fetchementation` is undefined', async () => { + it('handles when native fetch implementation returns undefined', async () => { const mockFetch = vi.fn(() => undefined) as unknown as typeof window.fetch; const transport = makeFetchTransport(DEFAULT_FETCH_TRANSPORT_OPTIONS, mockFetch); expect(mockFetch).toHaveBeenCalledTimes(0); - await expect(() => transport.send(ERROR_ENVELOPE)).not.toThrow(); + await expect(() => transport.send(ERROR_ENVELOPE)).rejects.toThrow( + "Cannot read properties of undefined (reading 'status')", + ); expect(mockFetch).toHaveBeenCalledTimes(1); }); + it('handles when native fetch implementation is undefined', async () => { + vi.mock('@sentry-internal/browser-utils', async importOriginal => ({ + ...(await importOriginal()), + getNativeImplementation: () => undefined, + })); + + const transport = makeFetchTransport(DEFAULT_FETCH_TRANSPORT_OPTIONS); + + await expect(() => transport.send(ERROR_ENVELOPE)).rejects.toThrow('nativeFetch is not a function'); + }); + it('correctly sets keepalive flag', async () => { const mockFetch = vi.fn(() => Promise.resolve({ From e8d255f5fe5934e6e561e3ad331ecff8cfbd4ca1 Mon Sep 17 00:00:00 2001 From: Lukas Stracke Date: Tue, 16 Sep 2025 13:46:23 +0200 Subject: [PATCH 59/61] Revert "feat(cloudflare): Introduce lock instrumentation for `context.waitUntil` to prevent multiple instrumentation (#17539)" (#17666) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This PR reverts #17539 because it causes e2e test fails on PRs as well as on `develop` and release branches. It seems like when our e2e tests expect errors in websocket, another unrelated error is thrown: ``` ✘ 5 [chromium] › tests/index.test.ts:41:5 › Websocket.webSocketMessage (13ms) [WebServer] ✘ [ERROR] Uncaught TypeError: Illegal invocation: function called with incorrect `this` reference. See https://developers.cloudflare.com/workers/observability/errors/#illegal-invocation-errors for details. Error [WebServer] [WebServer] at fetch (file:///private/var/folders/63/gxb4ks2n7wbf_7bbwz86cgsc0000gn/T/sentry-e2e-tests-cloudflare-workers-fU9D9e/src/index.ts:32:18) [WebServer] at null. (file:///private/var/folders/63/gxb4ks2n7wbf_7bbwz86cgsc0000gn/T/sentry-e2e-tests-cloudflare-workers-fU9D9e/node_modules/.pnpm/@sentry+cloudflare@10.11.0_@cloudflare+workers-types@4.20250913.0/node_modules/@sentry/cloudflare/src/durableobject.ts:219:23) [WebServer] at null. (file:///private/var/folders/63/gxb4ks2n7wbf_7bbwz86cgsc0000gn/T/sentry-e2e-tests-cloudflare-workers-fU9D9e/node_modules/.pnpm/@sentry+cloudflare@10.11.0_@cloudflare+workers-types@4.20250913.0/node_modules/@sentry/cloudflare/src/request.ts:108:33) [WebServer] at handleCallbackErrors.status.status (file:///private/var/folders/63/gxb4ks2n7wbf_7bbwz86cgsc0000gn/T/sentry-e2e-tests-cloudflare-workers-fU9D9e/node_modules/.pnpm/@sentry+core@10.11.0/node_modules/@sentry/core/src/tracing/trace.ts:77:15) [WebServer] at handleCallbackErrors (file:///private/var/folders/63/gxb4ks2n7wbf_7bbwz86cgsc0000gn/T/sentry-e2e-tests-cloudflare-workers-fU9D9e/node_modules/.pnpm/@sentry+core@10.11.0/node_modules/@sentry/core/src/utils/handleCallbackErrors.ts:20:26) [WebServer] at null. (file:///private/var/folders/63/gxb4ks2n7wbf_7bbwz86cgsc0000gn/T/sentry-e2e-tests-cloudflare-workers-fU9D9e/node_modules/.pnpm/@sentry+core@10.11.0/node_modules/@sentry/core/src/tracing/trace.ts:76:14) [WebServer] at null. (file:///private/var/folders/63/gxb4ks2n7wbf_7bbwz86cgsc0000gn/T/sentry-e2e-tests-cloudflare-workers-fU9D9e/node_modules/.pnpm/@sentry+core@10.11.0/node_modules/@sentry/core/src/tracing/trace.ts:530:100) [WebServer] at null. (file:///private/var/folders/63/gxb4ks2n7wbf_7bbwz86cgsc0000gn/T/sentry-e2e-tests-cloudflare-workers-fU9D9e/node_modules/.pnpm/@sentry+core@10.11.0/node_modules/@sentry/core/src/tracing/trace.ts:60:12) [WebServer] at null. (file:///private/var/folders/63/gxb4ks2n7wbf_7bbwz86cgsc0000gn/T/sentry-e2e-tests-cloudflare-workers-fU9D9e/node_modules/.pnpm/@sentry+cloudflare@10.11.0_@cloudflare+workers-types@4.20250913.0/node_modules/@sentry/cloudflare/src/async.ts:41:14) [WebServer] at withScope (file:///private/var/folders/63/gxb4ks2n7wbf_7bbwz86cgsc0000gn/T/sentry-e2e-tests-cloudflare-workers-fU9D9e/node_modules/.pnpm/@sentry+cloudflare@10.11.0_@cloudflare+workers-types@4.20250913.0/node_modules/@sentry/cloudflare/src/async.ts:40:25) [WebServer] at withScope (file:///private/var/folders/63/gxb4ks2n7wbf_7bbwz86cgsc0000gn/T/sentry-e2e-tests-cloudflare-workers-fU9D9e/node_modules/.pnpm/@sentry+core@10.11.0/node_modules/@sentry/core/src/currentScopes.ts:59:18) [WebServer] at startSpan (file:///private/var/folders/63/gxb4ks2n7wbf_7bbwz86cgsc0000gn/T/sentry-e2e-tests-cloudflare-workers-fU9D9e/node_modules/.pnpm/@sentry+core@10.11.0/node_modules/@sentry/core/src/tracing/trace.ts:56:10) [WebServer] at null. (file:///private/var/folders/63/gxb4ks2n7wbf_7bbwz86cgsc0000gn/T/sentry-e2e-tests-cloudflare-workers-fU9D9e/node_modules/.pnpm/@sentry+cloudflare@10.11.0_@cloudflare+workers-types@4.20250913.0/node_modules/@sentry/cloudflare/src/request.ts:101:16) [WebServer] at null. (file:///private/var/folders/63/gxb4ks2n7wbf_7bbwz86cgsc0000gn/T/sentry-e2e-tests-cloudflare-workers-fU9D9e/node_modules/.pnpm/@sentry+core@10.11.0/node_modules/@sentry/core/src/tracing/trace.ts:229:12) [WebServer] at null. (file:///private/var/folders/63/gxb4ks2n7wbf_7bbwz86cgsc0000gn/T/sentry-e2e-tests-cloudflare-workers-fU9D9e/node_modules/.pnpm/@sentry+cloudflare@10.11.0_@cloudflare+workers-types@4.20250913.0/node_modules/@sentry/cloudflare/src/async.ts:41:14) [WebServer] at withScope (file:///private/var/folders/63/gxb4ks2n7wbf_7bbwz86cgsc0000gn/T/sentry-e2e-tests-cloudflare-workers-fU9D9e/node_modules/.pnpm/@sentry+cloudflare@10.11.0_@cloudflare+workers-types@4.20250913.0/node_modules/@sentry/cloudflare/src/async.ts:40:25) [WebServer] at withScope (file:///private/var/folders/63/gxb4ks2n7wbf_7bbwz86cgsc0000gn/T/sentry-e2e-tests-cloudflare-workers-fU9D9e/node_modules/.pnpm/@sentry+core@10.11.0/node_modules/@sentry/core/src/currentScopes.ts:65:14) [WebServer] at continueTrace (file:///private/var/folders/63/gxb4ks2n7wbf_7bbwz86cgsc0000gn/T/sentry-e2e-tests-cloudflare-workers-fU9D9e/node_modules/.pnpm/@sentry+core@10.11.0/node_modules/@sentry/core/src/tracing/trace.ts:226:10) [WebServer] at null. (file:///private/var/folders/63/gxb4ks2n7wbf_7bbwz86cgsc0000gn/T/sentry-e2e-tests-cloudflare-workers-fU9D9e/node_modules/.pnpm/@sentry+cloudflare@10.11.0_@cloudflare+workers-types@4.20250913.0/node_modules/@sentry/cloudflare/src/request.ts:95:12) [WebServer] at null. (file:///private/var/folders/63/gxb4ks2n7wbf_7bbwz86cgsc0000gn/T/sentry-e2e-tests-cloudflare-workers-fU9D9e/node_modules/.pnpm/@sentry+cloudflare@10.11.0_@cloudflare+workers-types@4.20250913.0/node_modules/@sentry/cloudflare/src/async.ts:56:14) [WebServer] at withIsolationScope (file:///private/var/folders/63/gxb4ks2n7wbf_7bbwz86cgsc0000gn/T/sentry-e2e-tests-cloudflare-workers-fU9D9e/node_modules/.pnpm/@sentry+cloudflare@10.11.0_@cloudflare+workers-types@4.20250913.0/node_modules/@sentry/cloudflare/src/async.ts:55:25) [WebServer] at withIsolationScope (file:///private/var/folders/63/gxb4ks2n7wbf_7bbwz86cgsc0000gn/T/sentry-e2e-tests-cloudflare-workers-fU9D9e/node_modules/.pnpm/@sentry+core@10.11.0/node_modules/@sentry/core/src/currentScopes.ts:114:14) [WebServer] at wrapRequestHandler (file:///private/var/folders/63/gxb4ks2n7wbf_7bbwz86cgsc0000gn/T/sentry-e2e-tests-cloudflare-workers-fU9D9e/node_modules/.pnpm/@sentry+cloudflare@10.11.0_@cloudflare+workers-types@4.20250913.0/node_modules/@sentry/cloudflare/src/request.ts:43:10) [WebServer] at apply (file:///private/var/folders/63/gxb4ks2n7wbf_7bbwz86cgsc0000gn/T/sentry-e2e-tests-cloudflare-workers-fU9D9e/node_modules/.pnpm/@sentry+cloudflare@10.11.0_@cloudflare+workers-types@4.20250913.0/node_modules/@sentry/cloudflare/src/durableobject.ts:218:20) [WebServer] [WebServer] ``` The SDK catches this error instead but the tests fail because they expect the other error. Not sure what in #17539 caused this but for now let's unblock ourselves first and follow up with a fix in a new attempt (cc @0xbad0c0d3 ) --- .../cloudflare-workers/tests/index.test.ts | 6 +- packages/cloudflare/src/client.ts | 2 +- packages/cloudflare/src/durableobject.ts | 5 +- packages/cloudflare/src/flush.ts | 38 ++++++ packages/cloudflare/src/handler.ts | 21 +--- packages/cloudflare/src/sdk.ts | 2 +- .../src/utils/copyExecutionContext.ts | 47 ------- packages/cloudflare/src/utils/flushLock.ts | 57 --------- .../src/utils/makePromiseResolver.ts | 26 ---- .../test/copy-execution-context.test.ts | 47 ------- .../cloudflare/test/durableobject.test.ts | 14 ++- packages/cloudflare/test/flush-lock.test.ts | 28 ----- packages/cloudflare/test/flush.test.ts | 30 +++++ packages/cloudflare/test/handler.test.ts | 115 +++++++++--------- packages/cloudflare/test/request.test.ts | 26 ++-- 15 files changed, 162 insertions(+), 302 deletions(-) create mode 100644 packages/cloudflare/src/flush.ts delete mode 100644 packages/cloudflare/src/utils/copyExecutionContext.ts delete mode 100644 packages/cloudflare/src/utils/flushLock.ts delete mode 100644 packages/cloudflare/src/utils/makePromiseResolver.ts delete mode 100644 packages/cloudflare/test/copy-execution-context.test.ts delete mode 100644 packages/cloudflare/test/flush-lock.test.ts create mode 100644 packages/cloudflare/test/flush.test.ts diff --git a/dev-packages/e2e-tests/test-applications/cloudflare-workers/tests/index.test.ts b/dev-packages/e2e-tests/test-applications/cloudflare-workers/tests/index.test.ts index df8e655416fb..ad63c1a0d307 100644 --- a/dev-packages/e2e-tests/test-applications/cloudflare-workers/tests/index.test.ts +++ b/dev-packages/e2e-tests/test-applications/cloudflare-workers/tests/index.test.ts @@ -40,7 +40,7 @@ test("Request processed by DurableObject's fetch is recorded", async ({ baseURL test('Websocket.webSocketMessage', async ({ baseURL }) => { const eventWaiter = waitForError('cloudflare-workers', event => { - return event.exception?.values?.[0]?.mechanism?.type === 'auto.faas.cloudflare.durable_object'; + return !!event.exception?.values?.[0]; }); const url = new URL('/pass-to-object/ws', baseURL); url.protocol = url.protocol.replace('http', 'ws'); @@ -51,11 +51,12 @@ test('Websocket.webSocketMessage', async ({ baseURL }) => { const event = await eventWaiter; socket.close(); expect(event.exception?.values?.[0]?.value).toBe('Should be recorded in Sentry: webSocketMessage'); + expect(event.exception?.values?.[0]?.mechanism?.type).toBe('auto.faas.cloudflare.durable_object'); }); test('Websocket.webSocketClose', async ({ baseURL }) => { const eventWaiter = waitForError('cloudflare-workers', event => { - return event.exception?.values?.[0]?.mechanism?.type === 'auto.faas.cloudflare.durable_object'; + return !!event.exception?.values?.[0]; }); const url = new URL('/pass-to-object/ws', baseURL); url.protocol = url.protocol.replace('http', 'ws'); @@ -66,4 +67,5 @@ test('Websocket.webSocketClose', async ({ baseURL }) => { }); const event = await eventWaiter; expect(event.exception?.values?.[0]?.value).toBe('Should be recorded in Sentry: webSocketClose'); + expect(event.exception?.values?.[0]?.mechanism?.type).toBe('auto.faas.cloudflare.durable_object'); }); diff --git a/packages/cloudflare/src/client.ts b/packages/cloudflare/src/client.ts index 6da36cc1721c..2de5147d3d5a 100644 --- a/packages/cloudflare/src/client.ts +++ b/packages/cloudflare/src/client.ts @@ -1,7 +1,7 @@ import type { ClientOptions, Options, ServerRuntimeClientOptions } from '@sentry/core'; import { applySdkMetadata, ServerRuntimeClient } from '@sentry/core'; +import type { makeFlushLock } from './flush'; import type { CloudflareTransportOptions } from './transport'; -import type { makeFlushLock } from './utils/flushLock'; /** * The Sentry Cloudflare SDK Client. diff --git a/packages/cloudflare/src/durableobject.ts b/packages/cloudflare/src/durableobject.ts index 7fbb0592970a..0f139a80ccd0 100644 --- a/packages/cloudflare/src/durableobject.ts +++ b/packages/cloudflare/src/durableobject.ts @@ -18,7 +18,6 @@ import { isInstrumented, markAsInstrumented } from './instrument'; import { getFinalOptions } from './options'; import { wrapRequestHandler } from './request'; import { init } from './sdk'; -import { copyExecutionContext } from './utils/copyExecutionContext'; type MethodWrapperOptions = { spanName?: string; @@ -193,11 +192,9 @@ export function instrumentDurableObjectWithSentry< C extends new (state: DurableObjectState, env: E) => T, >(optionsCallback: (env: E) => CloudflareOptions, DurableObjectClass: C): C { return new Proxy(DurableObjectClass, { - construct(target, [ctx, env]) { + construct(target, [context, env]) { setAsyncLocalStorageAsyncContextStrategy(); - const context = copyExecutionContext(ctx); - const options = getFinalOptions(optionsCallback(env), env); const obj = new target(context, env); diff --git a/packages/cloudflare/src/flush.ts b/packages/cloudflare/src/flush.ts new file mode 100644 index 000000000000..f38c805d0f8b --- /dev/null +++ b/packages/cloudflare/src/flush.ts @@ -0,0 +1,38 @@ +import type { ExecutionContext } from '@cloudflare/workers-types'; + +type FlushLock = { + readonly ready: Promise; + readonly finalize: () => Promise; +}; + +/** + * Enhances the given execution context by wrapping its `waitUntil` method with a proxy + * to monitor pending tasks, and provides a flusher function to ensure all tasks + * have been completed before executing any subsequent logic. + * + * @param {ExecutionContext} context - The execution context to be enhanced. If no context is provided, the function returns undefined. + * @return {FlushLock} Returns a flusher function if a valid context is provided, otherwise undefined. + */ +export function makeFlushLock(context: ExecutionContext): FlushLock { + let resolveAllDone: () => void = () => undefined; + const allDone = new Promise(res => { + resolveAllDone = res; + }); + let pending = 0; + const originalWaitUntil = context.waitUntil.bind(context) as typeof context.waitUntil; + context.waitUntil = promise => { + pending++; + return originalWaitUntil( + promise.finally(() => { + if (--pending === 0) resolveAllDone(); + }), + ); + }; + return Object.freeze({ + ready: allDone, + finalize: () => { + if (pending === 0) resolveAllDone(); + return allDone; + }, + }); +} diff --git a/packages/cloudflare/src/handler.ts b/packages/cloudflare/src/handler.ts index 0275e01e1777..a6e5983902c6 100644 --- a/packages/cloudflare/src/handler.ts +++ b/packages/cloudflare/src/handler.ts @@ -14,7 +14,6 @@ import { getFinalOptions } from './options'; import { wrapRequestHandler } from './request'; import { addCloudResourceContext } from './scope-utils'; import { init } from './sdk'; -import { copyExecutionContext } from './utils/copyExecutionContext'; /** * Wrapper for Cloudflare handlers. @@ -38,11 +37,9 @@ export function withSentry>) { - const [request, env, ctx] = args; + const [request, env, context] = args; const options = getFinalOptions(optionsCallback(env), env); - const context = copyExecutionContext(ctx); - args[2] = context; return wrapRequestHandler({ options, request, context }, () => target.apply(thisArg, args)); }, @@ -74,9 +71,7 @@ export function withSentry>) { - const [event, env, ctx] = args; - const context = copyExecutionContext(ctx); - args[2] = context; + const [event, env, context] = args; return withIsolationScope(isolationScope => { const options = getFinalOptions(optionsCallback(env), env); const waitUntil = context.waitUntil.bind(context); @@ -119,9 +114,7 @@ export function withSentry>) { - const [emailMessage, env, ctx] = args; - const context = copyExecutionContext(ctx); - args[2] = context; + const [emailMessage, env, context] = args; return withIsolationScope(isolationScope => { const options = getFinalOptions(optionsCallback(env), env); const waitUntil = context.waitUntil.bind(context); @@ -162,9 +155,7 @@ export function withSentry>) { - const [batch, env, ctx] = args; - const context = copyExecutionContext(ctx); - args[2] = context; + const [batch, env, context] = args; return withIsolationScope(isolationScope => { const options = getFinalOptions(optionsCallback(env), env); @@ -214,9 +205,7 @@ export function withSentry>) { - const [, env, ctx] = args; - const context = copyExecutionContext(ctx); - args[2] = context; + const [, env, context] = args; return withIsolationScope(async isolationScope => { const options = getFinalOptions(optionsCallback(env), env); diff --git a/packages/cloudflare/src/sdk.ts b/packages/cloudflare/src/sdk.ts index 416ae47f312f..9d4fb8d749ae 100644 --- a/packages/cloudflare/src/sdk.ts +++ b/packages/cloudflare/src/sdk.ts @@ -12,10 +12,10 @@ import { } from '@sentry/core'; import type { CloudflareClientOptions, CloudflareOptions } from './client'; import { CloudflareClient } from './client'; +import { makeFlushLock } from './flush'; import { fetchIntegration } from './integrations/fetch'; import { setupOpenTelemetryTracer } from './opentelemetry/tracer'; import { makeCloudflareTransport } from './transport'; -import { makeFlushLock } from './utils/flushLock'; import { defaultStackParser } from './vendor/stacktrace'; /** Get the default integrations for the Cloudflare SDK. */ diff --git a/packages/cloudflare/src/utils/copyExecutionContext.ts b/packages/cloudflare/src/utils/copyExecutionContext.ts deleted file mode 100644 index 737a622d7a1c..000000000000 --- a/packages/cloudflare/src/utils/copyExecutionContext.ts +++ /dev/null @@ -1,47 +0,0 @@ -import { type DurableObjectState, type ExecutionContext } from '@cloudflare/workers-types'; - -const kBound = Symbol.for('kBound'); - -const defaultPropertyOptions: PropertyDescriptor = { - enumerable: true, - configurable: true, - writable: true, -}; - -/** - * Clones the given execution context by creating a shallow copy while ensuring the binding of specific methods. - * - * @param {ExecutionContext|DurableObjectState|void} ctx - The execution context to clone. Can be void. - * @return {ExecutionContext|DurableObjectState|void} A cloned execution context with bound methods, or the original void value if no context was provided. - */ -export function copyExecutionContext(ctx: T): T { - if (!ctx) return ctx; - return Object.create(ctx, { - waitUntil: { ...defaultPropertyOptions, value: copyAndBindMethod(ctx, 'waitUntil') }, - ...('passThroughOnException' in ctx && { - passThroughOnException: { ...defaultPropertyOptions, value: copyAndBindMethod(ctx, 'passThroughOnException') }, - }), - }); -} - -/** - * Copies a method from the given object and ensures the copied method remains bound to the original object's context. - * - * @param {object} obj - The object containing the method to be copied and bound. - * @param {string|symbol} method - The key of the method within the object to be copied and bound. - * @return {Function} - The copied and bound method, or the original property if it is not a function. - */ -function copyAndBindMethod(obj: T, method: K): T[K] { - const methodImpl = obj[method]; - if (typeof methodImpl !== 'function') return methodImpl; - if ((methodImpl as T[K] & { [kBound]?: boolean })[kBound]) return methodImpl; - const bound = methodImpl.bind(obj); - - return new Proxy(bound, { - get: (target, prop, receiver) => { - if (kBound === prop) return true; - if ('bind' === prop) return () => receiver; - return Reflect.get(target, prop, receiver); - }, - }); -} diff --git a/packages/cloudflare/src/utils/flushLock.ts b/packages/cloudflare/src/utils/flushLock.ts deleted file mode 100644 index dbb8d3666749..000000000000 --- a/packages/cloudflare/src/utils/flushLock.ts +++ /dev/null @@ -1,57 +0,0 @@ -import type { ExecutionContext } from '@cloudflare/workers-types'; -import { createPromiseResolver } from './makePromiseResolver'; - -type FlushLock = { - readonly ready: Promise; - readonly finalize: () => Promise; -}; -type MaybeLockable = T & { [kFlushLock]?: FlushLock }; - -const kFlushLock = Symbol.for('kFlushLock'); - -function getInstrumentedLock(o: MaybeLockable): FlushLock | undefined { - return o[kFlushLock]; -} - -function storeInstrumentedLock(o: MaybeLockable, lock: FlushLock): void { - o[kFlushLock] = lock; -} - -/** - * Enhances the given execution context by wrapping its `waitUntil` method with a proxy - * to monitor pending tasks and provides a flusher function to ensure all tasks - * have been completed before executing any subsequent logic. - * - * @param {ExecutionContext} context - The execution context to be enhanced. If no context is provided, the function returns undefined. - * @return {FlushLock} Returns a flusher function if a valid context is provided, otherwise undefined. - */ -export function makeFlushLock(context: ExecutionContext): FlushLock { - // eslint-disable-next-line @typescript-eslint/unbound-method - let lock = getInstrumentedLock(context.waitUntil); - if (lock) { - // It is fine to return the same lock multiple times because this means the context has already been instrumented. - return lock; - } - let pending = 0; - const originalWaitUntil = context.waitUntil.bind(context) as typeof context.waitUntil; - const { promise, resolve } = createPromiseResolver(); - const hijackedWaitUntil: typeof originalWaitUntil = promise => { - pending++; - return originalWaitUntil( - promise.finally(() => { - if (--pending === 0) resolve(); - }), - ); - }; - lock = Object.freeze({ - ready: promise, - finalize: () => { - if (pending === 0) resolve(); - return promise; - }, - }) as FlushLock; - storeInstrumentedLock(hijackedWaitUntil, lock); - context.waitUntil = hijackedWaitUntil; - - return lock; -} diff --git a/packages/cloudflare/src/utils/makePromiseResolver.ts b/packages/cloudflare/src/utils/makePromiseResolver.ts deleted file mode 100644 index f772951406b1..000000000000 --- a/packages/cloudflare/src/utils/makePromiseResolver.ts +++ /dev/null @@ -1,26 +0,0 @@ -type PromiseWithResolvers = { - readonly promise: Promise; - readonly resolve: (value?: T | PromiseLike) => void; - readonly reject: (reason?: E) => void; -}; -/** - * Creates an object containing a promise, along with its corresponding resolve and reject functions. - * - * This method provides a convenient way to create a promise and access its resolvers externally. - * - * @template T - The type of the resolved value of the promise. - * @template E - The type of the rejected value of the promise. Defaults to `unknown`. - * @return {PromiseWithResolvers} An object containing the promise and its resolve and reject functions. - */ -export function createPromiseResolver(): PromiseWithResolvers { - if ('withResolvers' in Promise && typeof Promise.withResolvers === 'function') { - return Promise.withResolvers(); - } - let resolve; - let reject; - const promise = new Promise((res, rej) => { - resolve = res; - reject = rej; - }); - return { promise, resolve, reject } as unknown as PromiseWithResolvers; -} diff --git a/packages/cloudflare/test/copy-execution-context.test.ts b/packages/cloudflare/test/copy-execution-context.test.ts deleted file mode 100644 index 61757cf1f355..000000000000 --- a/packages/cloudflare/test/copy-execution-context.test.ts +++ /dev/null @@ -1,47 +0,0 @@ -import { type ExecutionContext } from '@cloudflare/workers-types'; -import { type Mocked, describe, expect, it, vi } from 'vitest'; -import { copyExecutionContext } from '../src/utils/copyExecutionContext'; - -describe('Copy of the execution context', () => { - describe.for(['waitUntil', 'passThroughOnException'])('%s', method => { - it('Was not bound more than once', async () => { - const context = makeExecutionContextMock(); - const copy = copyExecutionContext(context); - const copy_of_copy = copyExecutionContext(copy); - - expect(copy[method]).toBe(copy_of_copy[method]); - }); - it('Copied method is bound to the original', async () => { - const context = makeExecutionContextMock(); - const copy = copyExecutionContext(context); - - expect(copy[method]()).toBe(context); - }); - it('Copied method "rebind" prevention', async () => { - const context = makeExecutionContextMock(); - const copy = copyExecutionContext(context); - expect(copy[method].bind('test')).toBe(copy[method]); - }); - }); - - it('No side effects', async () => { - const context = makeExecutionContextMock(); - expect(() => copyExecutionContext(Object.freeze(context))).not.toThrow( - /Cannot define property \w+, object is not extensible/, - ); - }); - it('Respects symbols', async () => { - const s = Symbol('test'); - const context = makeExecutionContextMock(); - context[s] = {}; - const copy = copyExecutionContext(context); - expect(copy[s]).toBe(context[s]); - }); -}); - -function makeExecutionContextMock() { - return { - waitUntil: vi.fn().mockReturnThis(), - passThroughOnException: vi.fn().mockReturnThis(), - } as unknown as Mocked; -} diff --git a/packages/cloudflare/test/durableobject.test.ts b/packages/cloudflare/test/durableobject.test.ts index 4c4d5c63bfd3..ce794dc7fb69 100644 --- a/packages/cloudflare/test/durableobject.test.ts +++ b/packages/cloudflare/test/durableobject.test.ts @@ -1,9 +1,8 @@ import type { ExecutionContext } from '@cloudflare/workers-types'; import * as SentryCore from '@sentry/core'; -import { afterEach, describe, expect, it, vi } from 'vitest'; +import { afterEach, describe, expect, it, onTestFinished, vi } from 'vitest'; import { instrumentDurableObjectWithSentry } from '../src'; import { isInstrumented } from '../src/instrument'; -import { createPromiseResolver } from '../src/utils/makePromiseResolver'; describe('instrumentDurableObjectWithSentry', () => { afterEach(() => { @@ -123,13 +122,15 @@ describe('instrumentDurableObjectWithSentry', () => { }); it('flush performs after all waitUntil promises are finished', async () => { + vi.useFakeTimers(); + onTestFinished(() => { + vi.useRealTimers(); + }); const flush = vi.spyOn(SentryCore.Client.prototype, 'flush'); const waitUntil = vi.fn(); - const { promise, resolve } = createPromiseResolver(); - process.nextTick(resolve); const testClass = vi.fn(context => ({ fetch: () => { - context.waitUntil(promise); + context.waitUntil(new Promise(res => setTimeout(res))); return new Response('test'); }, })); @@ -141,7 +142,8 @@ describe('instrumentDurableObjectWithSentry', () => { expect(() => dObject.fetch(new Request('https://example.com'))).not.toThrow(); expect(flush).not.toBeCalled(); expect(waitUntil).toHaveBeenCalledOnce(); - await Promise.all(waitUntil.mock.calls.map(call => call[0])); + vi.advanceTimersToNextTimer(); + await Promise.all(waitUntil.mock.calls.map(([p]) => p)); expect(flush).toBeCalled(); }); diff --git a/packages/cloudflare/test/flush-lock.test.ts b/packages/cloudflare/test/flush-lock.test.ts deleted file mode 100644 index cc40fd3a7e03..000000000000 --- a/packages/cloudflare/test/flush-lock.test.ts +++ /dev/null @@ -1,28 +0,0 @@ -import { type ExecutionContext } from '@cloudflare/workers-types'; -import { describe, expect, it, vi } from 'vitest'; -import { makeFlushLock } from '../src/utils/flushLock'; -import { createPromiseResolver } from '../src/utils/makePromiseResolver'; - -describe('Flush buffer test', () => { - const mockExecutionContext: ExecutionContext = { - waitUntil: vi.fn(), - passThroughOnException: vi.fn(), - props: null, - }; - it('should flush buffer immediately if no waitUntil were called', async () => { - const { finalize } = makeFlushLock(mockExecutionContext); - await expect(finalize()).resolves.toBeUndefined(); - }); - it('waitUntil should not be wrapped mose than once', () => { - expect(makeFlushLock(mockExecutionContext), 'Execution context wrapped twice').toBe( - makeFlushLock(mockExecutionContext), - ); - }); - it('should flush buffer only after all waitUntil were finished', async () => { - const { promise, resolve } = createPromiseResolver(); - const lock = makeFlushLock(mockExecutionContext); - mockExecutionContext.waitUntil(promise); - process.nextTick(resolve); - await expect(lock.finalize()).resolves.toBeUndefined(); - }); -}); diff --git a/packages/cloudflare/test/flush.test.ts b/packages/cloudflare/test/flush.test.ts new file mode 100644 index 000000000000..34714711c682 --- /dev/null +++ b/packages/cloudflare/test/flush.test.ts @@ -0,0 +1,30 @@ +import { type ExecutionContext } from '@cloudflare/workers-types'; +import { describe, expect, it, onTestFinished, vi } from 'vitest'; +import { makeFlushLock } from '../src/flush'; + +describe('Flush buffer test', () => { + const waitUntilPromises: Promise[] = []; + const mockExecutionContext: ExecutionContext = { + waitUntil: vi.fn(prmise => { + waitUntilPromises.push(prmise); + }), + passThroughOnException: vi.fn(), + }; + it('should flush buffer immediately if no waitUntil were called', async () => { + const { finalize } = makeFlushLock(mockExecutionContext); + await expect(finalize()).resolves.toBeUndefined(); + }); + it('should flush buffer only after all waitUntil were finished', async () => { + vi.useFakeTimers(); + onTestFinished(() => { + vi.useRealTimers(); + }); + const task = new Promise(resolve => setTimeout(resolve, 100)); + const lock = makeFlushLock(mockExecutionContext); + mockExecutionContext.waitUntil(task); + void lock.finalize(); + vi.advanceTimersToNextTimer(); + await Promise.all(waitUntilPromises); + await expect(lock.ready).resolves.toBeUndefined(); + }); +}); diff --git a/packages/cloudflare/test/handler.test.ts b/packages/cloudflare/test/handler.test.ts index 930e508cfddb..97e93199ea31 100644 --- a/packages/cloudflare/test/handler.test.ts +++ b/packages/cloudflare/test/handler.test.ts @@ -10,11 +10,10 @@ import type { } from '@cloudflare/workers-types'; import type { Event } from '@sentry/core'; import * as SentryCore from '@sentry/core'; -import { beforeEach, describe, expect, test, vi } from 'vitest'; +import { beforeEach, describe, expect, onTestFinished, test, vi } from 'vitest'; import { CloudflareClient } from '../src/client'; import { withSentry } from '../src/handler'; import { markAsInstrumented } from '../src/instrument'; -import { createPromiseResolver } from '../src/utils/makePromiseResolver'; // Custom type for hono-like apps (cloudflare handlers) that include errorHandler and onError type HonoLikeApp = ExportedHandler< @@ -31,29 +30,8 @@ const MOCK_ENV = { SENTRY_RELEASE: '1.1.1', }; -function makeWaitUntilAndTask< - M extends ReturnType, - T extends M & { - readonly ready: Promise; - readonly task: { readonly promise: Promise; readonly resolve: M }; - }, ->(): T { - const waitUntil = vi.fn(); - const { promise, resolve } = createPromiseResolver(); - const resolver = vi.fn().mockImplementation(resolve).mockName('waitUntil.ready'); - Object.defineProperties(waitUntil, { - ready: { - get: () => Promise.all(waitUntil.mock.calls.map(call => call[0])).then(() => resolver), - enumerable: true, - configurable: true, - }, - task: { - value: { promise, resolve: resolver }, - enumerable: true, - configurable: true, - }, - }); - return waitUntil as T; +function addDelayedWaitUntil(context: ExecutionContext) { + context.waitUntil(new Promise(resolve => setTimeout(() => resolve()))); } describe('withSentry', () => { @@ -157,22 +135,27 @@ describe('withSentry', () => { test('flush must be called when all waitUntil are done', async () => { const flush = vi.spyOn(SentryCore.Client.prototype, 'flush'); - const waitUntil = makeWaitUntilAndTask(); - process.nextTick(waitUntil.task.resolve); + vi.useFakeTimers(); + onTestFinished(() => { + vi.useRealTimers(); + }); const handler = { - fetch(...args) { - args[2].waitUntil(waitUntil.task.promise); + fetch(_request, _env, _context) { + addDelayedWaitUntil(_context); return new Response('test'); }, } satisfies ExportedHandler; const wrappedHandler = withSentry(vi.fn(), handler); + const waits: Promise[] = []; + const waitUntil = vi.fn(promise => waits.push(promise)); await wrappedHandler.fetch?.(new Request('https://example.com'), MOCK_ENV, { waitUntil, } as unknown as ExecutionContext); expect(flush).not.toBeCalled(); - const ready = await waitUntil.ready; - expect(flush).toHaveBeenCalledAfter(ready); + expect(waitUntil).toBeCalled(); + vi.advanceTimersToNextTimer().runAllTimers(); + await Promise.all(waits); expect(flush).toHaveBeenCalledOnce(); }); }); @@ -392,23 +375,28 @@ describe('withSentry', () => { test('flush must be called when all waitUntil are done', async () => { const flush = vi.spyOn(SentryCore.Client.prototype, 'flush'); - const waitUntil = makeWaitUntilAndTask(); - process.nextTick(waitUntil.task.resolve); + vi.useFakeTimers(); + onTestFinished(() => { + vi.useRealTimers(); + }); const handler = { - scheduled(...args) { - args[2].waitUntil(waitUntil.task.promise); + scheduled(_controller, _env, _context) { + addDelayedWaitUntil(_context); return; }, } satisfies ExportedHandler; const wrappedHandler = withSentry(vi.fn(), handler); + const waits: Promise[] = []; + const waitUntil = vi.fn(promise => waits.push(promise)); await wrappedHandler.scheduled?.(createMockScheduledController(), MOCK_ENV, { waitUntil, } as unknown as ExecutionContext); expect(flush).not.toBeCalled(); - await waitUntil.ready; + expect(waitUntil).toBeCalled(); + vi.advanceTimersToNextTimer().runAllTimers(); + await Promise.all(waits); expect(flush).toHaveBeenCalledOnce(); - expect(flush).toHaveBeenCalledAfter(waitUntil); }); }); @@ -626,24 +614,28 @@ describe('withSentry', () => { test('flush must be called when all waitUntil are done', async () => { const flush = vi.spyOn(SentryCore.Client.prototype, 'flush'); - const waitUntil = makeWaitUntilAndTask(); - - process.nextTick(waitUntil.task.resolve); + vi.useFakeTimers(); + onTestFinished(() => { + vi.useRealTimers(); + }); const handler = { - email(...args) { - args[2].waitUntil(waitUntil.task.promise); + email(_controller, _env, _context) { + addDelayedWaitUntil(_context); return; }, } satisfies ExportedHandler; const wrappedHandler = withSentry(vi.fn(), handler); + const waits: Promise[] = []; + const waitUntil = vi.fn(promise => waits.push(promise)); await wrappedHandler.email?.(createMockEmailMessage(), MOCK_ENV, { waitUntil, } as unknown as ExecutionContext); expect(flush).not.toBeCalled(); - const ready = await waitUntil.ready; + expect(waitUntil).toBeCalled(); + vi.advanceTimersToNextTimer().runAllTimers(); + await Promise.all(waits); expect(flush).toHaveBeenCalledOnce(); - expect(flush).toHaveBeenCalledAfter(ready); }); }); @@ -865,22 +857,27 @@ describe('withSentry', () => { test('flush must be called when all waitUntil are done', async () => { const flush = vi.spyOn(SentryCore.Client.prototype, 'flush'); - const waitUntil = makeWaitUntilAndTask(); - process.nextTick(waitUntil.task.resolve); + vi.useFakeTimers(); + onTestFinished(() => { + vi.useRealTimers(); + }); const handler = { - queue(...args) { - args[2].waitUntil(waitUntil.task.promise); + queue(_controller, _env, _context) { + addDelayedWaitUntil(_context); return; }, } satisfies ExportedHandler; const wrappedHandler = withSentry(vi.fn(), handler); + const waits: Promise[] = []; + const waitUntil = vi.fn(promise => waits.push(promise)); await wrappedHandler.queue?.(createMockQueueBatch(), MOCK_ENV, { waitUntil, } as unknown as ExecutionContext); expect(flush).not.toBeCalled(); - - expect(flush).toHaveBeenCalledAfter(await waitUntil.ready); + expect(waitUntil).toBeCalled(); + vi.advanceTimersToNextTimer().runAllTimers(); + await Promise.all(waits); expect(flush).toHaveBeenCalledOnce(); }); }); @@ -1057,23 +1054,29 @@ describe('withSentry', () => { test('flush must be called when all waitUntil are done', async () => { const flush = vi.spyOn(SentryCore.Client.prototype, 'flush'); - const waitUntil = makeWaitUntilAndTask(); - process.nextTick(waitUntil.task.resolve); + vi.useFakeTimers(); + onTestFinished(() => { + vi.useRealTimers(); + flush.mockRestore(); + }); const handler = { - tail(...args) { - args[2].waitUntil(waitUntil.task.promise); + tail(_controller, _env, _context) { + addDelayedWaitUntil(_context); return; }, } satisfies ExportedHandler; const wrappedHandler = withSentry(vi.fn(), handler); + const waits: Promise[] = []; + const waitUntil = vi.fn(promise => waits.push(promise)); await wrappedHandler.tail?.(createMockTailEvent(), MOCK_ENV, { waitUntil, } as unknown as ExecutionContext); expect(flush).not.toBeCalled(); - const ready = await waitUntil.ready; + expect(waitUntil).toBeCalled(); + vi.advanceTimersToNextTimer().runAllTimers(); + await Promise.all(waits); expect(flush).toHaveBeenCalledOnce(); - expect(flush).toHaveBeenCalledAfter(ready); }); }); diff --git a/packages/cloudflare/test/request.test.ts b/packages/cloudflare/test/request.test.ts index eaf06f8f7f3e..d6d0de5824a1 100644 --- a/packages/cloudflare/test/request.test.ts +++ b/packages/cloudflare/test/request.test.ts @@ -4,17 +4,20 @@ import type { ExecutionContext } from '@cloudflare/workers-types'; import type { Event } from '@sentry/core'; import * as SentryCore from '@sentry/core'; -import { beforeAll, beforeEach, describe, expect, test, vi } from 'vitest'; +import { beforeAll, beforeEach, describe, expect, onTestFinished, test, vi } from 'vitest'; import { setAsyncLocalStorageAsyncContextStrategy } from '../src/async'; import type { CloudflareOptions } from '../src/client'; import { CloudflareClient } from '../src/client'; import { wrapRequestHandler } from '../src/request'; -import { createPromiseResolver } from '../src/utils/makePromiseResolver'; const MOCK_OPTIONS: CloudflareOptions = { dsn: 'https://public@dsn.ingest.sentry.io/1337', }; +function addDelayedWaitUntil(context: ExecutionContext) { + context.waitUntil(new Promise(resolve => setTimeout(() => resolve()))); +} + describe('withSentry', () => { beforeAll(() => { setAsyncLocalStorageAsyncContextStrategy(); @@ -67,24 +70,25 @@ describe('withSentry', () => { test('flush must be called when all waitUntil are done', async () => { const flush = vi.spyOn(SentryCore.Client.prototype, 'flush'); - const waitUntil = vi.fn(); + vi.useFakeTimers(); + onTestFinished(() => { + vi.useRealTimers(); + }); + const waits: Promise[] = []; + const waitUntil = vi.fn(promise => waits.push(promise)); const context = { waitUntil, } as unknown as ExecutionContext; - const { promise, resolve } = createPromiseResolver(); - const resolved = vi.fn(() => resolve()); - process.nextTick(resolved); - await wrapRequestHandler({ options: MOCK_OPTIONS, request: new Request('https://example.com'), context }, () => { - context.waitUntil(promise); + addDelayedWaitUntil(context); return new Response('test'); }); expect(flush).not.toBeCalled(); expect(waitUntil).toBeCalled(); - await Promise.all(waitUntil.mock.calls.map(call => call[0])); - expect(flush).toHaveBeenCalledAfter(resolved); + vi.advanceTimersToNextTimerAsync().then(() => vi.runAllTimers()); + await Promise.all(waits); expect(flush).toHaveBeenCalledOnce(); }); @@ -333,5 +337,5 @@ function createMockExecutionContext(): ExecutionContext { return { waitUntil: vi.fn(), passThroughOnException: vi.fn(), - } as unknown as ExecutionContext; + }; } From dd172035a1480f1d2712d86dd4db4a6df8d8f6a6 Mon Sep 17 00:00:00 2001 From: Francesco Gringl-Novy Date: Tue, 16 Sep 2025 14:47:08 +0200 Subject: [PATCH 60/61] fix(core): Fix error handling when sending envelopes (#17662) I noticed that we actually handled errors in `sendEnvelope` incorrectly - we would resolve this function with the rejection reason, if sending fails. this does not match the type of `TransportMakeRequestResponse`, you could actually get something like this out (and the tests actually incorrectly tested this): ```js // transport.send() rejects with "fetch does not exist" const res = await client.sendEnvelope(envelope); // res --> "fetch does not exist" (string) ``` This PR fixes this to instead resolve with an empty object (which matches the expected return type). Extracted this out of https://github.com/getsentry/sentry-javascript/pull/17641 because it is actually a bug/fix. --- packages/core/src/client.ts | 10 ++++------ packages/core/test/lib/client.test.ts | 2 +- .../src/coreHandlers/handleAfterSendEvent.ts | 5 ++--- 3 files changed, 7 insertions(+), 10 deletions(-) diff --git a/packages/core/src/client.ts b/packages/core/src/client.ts index 88fab9107874..75a0d5ed49d2 100644 --- a/packages/core/src/client.ts +++ b/packages/core/src/client.ts @@ -415,10 +415,9 @@ export abstract class Client { env = addItemToEnvelope(env, createAttachmentEnvelopeItem(attachment)); } - const promise = this.sendEnvelope(env); - if (promise) { - promise.then(sendResponse => this.emit('afterSendEvent', event, sendResponse), null); - } + // sendEnvelope should not throw + // eslint-disable-next-line @typescript-eslint/no-floating-promises + this.sendEnvelope(env).then(sendResponse => this.emit('afterSendEvent', event, sendResponse)); } /** @@ -879,12 +878,11 @@ export abstract class Client { if (this._isEnabled() && this._transport) { return this._transport.send(envelope).then(null, reason => { DEBUG_BUILD && debug.error('Error while sending envelope:', reason); - return reason; + return {}; }); } DEBUG_BUILD && debug.error('Transport disabled'); - return resolvedSyncPromise({}); } diff --git a/packages/core/test/lib/client.test.ts b/packages/core/test/lib/client.test.ts index cb44d7212945..b7767a7e6c58 100644 --- a/packages/core/test/lib/client.test.ts +++ b/packages/core/test/lib/client.test.ts @@ -2268,7 +2268,7 @@ describe('Client', () => { expect(mockSend).toBeCalledTimes(1); expect(callback).toBeCalledTimes(1); - expect(callback).toBeCalledWith(errorEvent, 'send error'); + expect(callback).toBeCalledWith(errorEvent, {}); }); it('passes the response to the hook', async () => { diff --git a/packages/replay-internal/src/coreHandlers/handleAfterSendEvent.ts b/packages/replay-internal/src/coreHandlers/handleAfterSendEvent.ts index 827fc6f5b99b..4df1b62532ac 100644 --- a/packages/replay-internal/src/coreHandlers/handleAfterSendEvent.ts +++ b/packages/replay-internal/src/coreHandlers/handleAfterSendEvent.ts @@ -14,11 +14,10 @@ export function handleAfterSendEvent(replay: ReplayContainer): AfterSendEventCal return; } - const statusCode = sendResponse?.statusCode; + const statusCode = sendResponse.statusCode; // We only want to do stuff on successful error sending, otherwise you get error replays without errors attached - // If not using the base transport, we allow `undefined` response (as a custom transport may not implement this correctly yet) - // If we do use the base transport, we skip if we encountered an non-OK status code + // We skip if we encountered an non-OK status code if (!statusCode || statusCode < 200 || statusCode >= 300) { return; } From c076017685ae127618a231ee137dd98133cc60de Mon Sep 17 00:00:00 2001 From: Andrei Borza Date: Tue, 16 Sep 2025 11:01:29 +0200 Subject: [PATCH 61/61] meta(changelog): Update changelog for 10.12.0 --- CHANGELOG.md | 49 +++++++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 45 insertions(+), 4 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 8cd1f67d0526..6ec252166664 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,10 @@ ## Unreleased +- "You miss 100 percent of the chances you don't take. — Wayne Gretzky" — Michael Scott + +# 10.12.0 + ### Important Changes - **ref: Add and Adjust error event `mechanism` values** @@ -49,10 +53,6 @@
-- "You miss 100 percent of the chances you don't take. — Wayne Gretzky" — Michael Scott - -### Important Changes - - **feat(node) Ensure `prismaIntegration` works with Prisma 5 ([#17595](https://github.com/getsentry/sentry-javascript/pull/17595))** We used to require to pass in the v5 version of `@prisma/instrumentation` into `prismaIntegration({ prismaInstrumentation: new PrismaInstrumentation() })`, if you wanted to get full instrumentation for Prisma v5. However, it turns out this does not work on v10 of the SDK anymore, because `@prisma/instrumentation@5` requires OTEL v1. @@ -91,6 +91,47 @@ With this release, we dropped the requirement to configure anything to get v5 su - @opentelemetry/instrumentation-undici bumped to ^0.15.0 - @prisma/instrumentation bumped to 6.15.0 +### Other Changes + +- feat(browser): Add timing and status atttributes to resource spans ([#17562](https://github.com/getsentry/sentry-javascript/pull/17562)) +- feat(cloudflare,vercel-edge): Add support for Anthropic AI instrumentation ([#17571](https://github.com/getsentry/sentry-javascript/pull/17571)) +- feat(core): Add Consola integration ([#17435](https://github.com/getsentry/sentry-javascript/pull/17435)) +- feat(deps): Update OpenTelemetry dependencies ([#17569](https://github.com/getsentry/sentry-javascript/pull/17569)) +- feat(core): Export `TracesSamplerSamplingContext` type ([#17523](https://github.com/getsentry/sentry-javascript/pull/17523)) +- feat(deno): Add OpenTelemetry support and vercelAI integration ([#17445](https://github.com/getsentry/sentry-javascript/pull/17445)) +- feat(node-core): Remove experimental note from winston api ([#17626](https://github.com/getsentry/sentry-javascript/pull/17626)) +- feat(node): Ensure `prismaIntegration` works with Prisma v5 ([#17595](https://github.com/getsentry/sentry-javascript/pull/17595)) +- feat(node): Tidy existing ESM loader hook ([#17566](https://github.com/getsentry/sentry-javascript/pull/17566)) +- feat(sveltekit): Align build time options with shared type ([#17413](https://github.com/getsentry/sentry-javascript/pull/17413)) +- fix(core): Fix error handling when sending envelopes ([#17662](https://github.com/getsentry/sentry-javascript/pull/17662)) +- fix(browser): Always start navigation as root span ([#17648](https://github.com/getsentry/sentry-javascript/pull/17648)) +- fix(browser): Ensure propagated `parentSpanId` stays consistent during trace in TwP mode ([#17526](https://github.com/getsentry/sentry-javascript/pull/17526)) +- fix(cloudflare): Initialize once per workflow run and preserve scope for `step.do` ([#17582](https://github.com/getsentry/sentry-javascript/pull/17582)) +- fix(nextjs): Add edge polyfills for nextjs-13 in dev mode ([#17488](https://github.com/getsentry/sentry-javascript/pull/17488)) +- fix(nitro): Support nested `_platform` properties in Nitro 2.11.7+ ([#17596](https://github.com/getsentry/sentry-javascript/pull/17596)) +- fix(node): Preserve synchronous return behavior for streamText and other methods for AI ([#17580](https://github.com/getsentry/sentry-javascript/pull/17580)) +- ref(node): Inline types imported from `shimmer` ([#17597](https://github.com/getsentry/sentry-javascript/pull/17597)) - ref(nuxt): Add and adjust `mechanism.type` in error events ([#17599](https://github.com/getsentry/sentry-javascript/pull/17599)) +- ref(browser): Improve `fetchTransport` error handling ([#17661](https://github.com/getsentry/sentry-javascript/pull/17661)) + +
+ Internal Changes + +- chore: Add changelog note about mechanism changes ([#17632](https://github.com/getsentry/sentry-javascript/pull/17632)) +- chore(aws): Update README.md ([#17601](https://github.com/getsentry/sentry-javascript/pull/17601)) +- chore(deps): bump hono from 4.7.10 to 4.9.7 in /dev-packages/e2e-tests/test-applications/cloudflare-hono ([#17630](https://github.com/getsentry/sentry-javascript/pull/17630)) +- chore(deps): bump next from 14.2.25 to 14.2.32 in /dev-packages/e2e-tests/test-applications/nextjs-app-dir ([#17627](https://github.com/getsentry/sentry-javascript/pull/17627)) +- chore(deps): bump next from 14.2.25 to 14.2.32 in /dev-packages/e2e-tests/test-applications/nextjs-pages-dir ([#17620](https://github.com/getsentry/sentry-javascript/pull/17620)) +- chore(deps): bump next from 14.2.29 to 14.2.32 in /dev-packages/e2e-tests/test-applications/nextjs-orpc ([#17494](https://github.com/getsentry/sentry-javascript/pull/17494)) +- chore(deps): bump next from 14.2.30 to 14.2.32 in /dev-packages/e2e-tests/test-applications/nextjs-14 ([#17628](https://github.com/getsentry/sentry-javascript/pull/17628)) +- chore(repo): Rename `.claude/settings.local.json` to `.claude/settings.json` ([#17591](https://github.com/getsentry/sentry-javascript/pull/17591)) +- docs(issue-template): Add note about prioritization ([#17590](https://github.com/getsentry/sentry-javascript/pull/17590)) +- ref(core): Streamline event processor handling ([#17634](https://github.com/getsentry/sentry-javascript/pull/17634)) +- test(angular): Bump TS version to 5.9.0 in Angular 20 e2e test ([#17605](https://github.com/getsentry/sentry-javascript/pull/17605)) +- test(nextjs): Remove Next 13 and pin Next 14 canary and latest tests ([#17577](https://github.com/getsentry/sentry-javascript/pull/17577)) +- test(react-router): Unflake `flushIfServerless` test ([#17610](https://github.com/getsentry/sentry-javascript/pull/17610)) + +
+ ## 10.11.0 ### Important Changes