From 9cdd2d9ac681bc311e2439c15e083ade391cb435 Mon Sep 17 00:00:00 2001 From: Sigrid Huemer <32902192+s1gr1d@users.noreply.github.com> Date: Mon, 24 Mar 2025 13:15:57 +0100 Subject: [PATCH 01/28] feat(nextjs): Disable server webpack-handling for static builds (#15751) When static builds are enabled, Sentry should not change webpack configuration for the server/edge runtime as there is no server. closes https://github.com/getsentry/sentry-javascript/issues/12420 --- packages/nextjs/src/config/webpack.ts | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/packages/nextjs/src/config/webpack.ts b/packages/nextjs/src/config/webpack.ts index cababfa63002..9155b0cd2b60 100644 --- a/packages/nextjs/src/config/webpack.ts +++ b/packages/nextjs/src/config/webpack.ts @@ -349,9 +349,11 @@ export function constructWebpackConfigFunction( } } - // We don't want to do any webpack plugin stuff OR any source maps stuff in dev mode. + const isStaticExport = userNextConfig?.output === 'export'; + + // We don't want to do any webpack plugin stuff OR any source maps stuff in dev mode or for the server on static-only builds. // Symbolication for dev-mode errors is done elsewhere. - if (!isDev) { + if (!(isDev || (isStaticExport && isServer))) { // eslint-disable-next-line @typescript-eslint/no-explicit-any const { sentryWebpackPlugin } = loadModule<{ sentryWebpackPlugin: any }>('@sentry/webpack-plugin', module) ?? {}; From fa9376c71cce272eb721f637126c785dd17644cc Mon Sep 17 00:00:00 2001 From: Sigrid Huemer <32902192+s1gr1d@users.noreply.github.com> Date: Mon, 24 Mar 2025 13:19:52 +0100 Subject: [PATCH 02/28] fix(nuxt): Don't override Nuxt options if undefined (#15795) Sometimes, the build fails because of this error: ` ERROR Cannot set property options of # which has only a getter ` As `nuxt.options` are always defined anyway (it's also not optional in the `Nuxt` type), we can safely delete the assertion to `{}` which causes the error to dissapear. --- packages/nuxt/src/vite/sourceMaps.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/packages/nuxt/src/vite/sourceMaps.ts b/packages/nuxt/src/vite/sourceMaps.ts index 4f1e1184e637..1372db47889e 100644 --- a/packages/nuxt/src/vite/sourceMaps.ts +++ b/packages/nuxt/src/vite/sourceMaps.ts @@ -142,7 +142,6 @@ export function changeNuxtSourceMapSettings( nuxt: Nuxt, sentryModuleOptions: SentryNuxtModuleOptions, ): { client: UserSourceMapSetting; server: UserSourceMapSetting } { - nuxt.options = nuxt.options || {}; nuxt.options.sourcemap = nuxt.options.sourcemap ?? { server: undefined, client: undefined }; let previousUserSourceMapSetting: { client: UserSourceMapSetting; server: UserSourceMapSetting } = { From 34b69b30527a382a3dc6657306b045c4236b5766 Mon Sep 17 00:00:00 2001 From: Francesco Gringl-Novy Date: Mon, 24 Mar 2025 13:30:10 +0100 Subject: [PATCH 03/28] ref: Avoid `dropUndefinedKeys` for `spanToJSON` calls (#15792) We probably don't need this! --- packages/core/src/tracing/sentrySpan.ts | 5 ++--- packages/core/src/utils/spanUtils.ts | 4 ++-- .../core/test/lib/tracing/sentrySpan.test.ts | 20 ------------------- 3 files changed, 4 insertions(+), 25 deletions(-) diff --git a/packages/core/src/tracing/sentrySpan.ts b/packages/core/src/tracing/sentrySpan.ts index 6641e1e87296..f08d906dc1e0 100644 --- a/packages/core/src/tracing/sentrySpan.ts +++ b/packages/core/src/tracing/sentrySpan.ts @@ -26,7 +26,6 @@ import type { } from '../types-hoist'; import type { SpanLink } from '../types-hoist/link'; import { logger } from '../utils-hoist/logger'; -import { dropUndefinedKeys } from '../utils-hoist/object'; import { generateSpanId, generateTraceId } from '../utils-hoist/propagationContext'; import { timestampInSeconds } from '../utils-hoist/time'; import { @@ -223,7 +222,7 @@ export class SentrySpan implements Span { * use `spanToJSON(span)` instead. */ public getSpanJSON(): SpanJSON { - return dropUndefinedKeys({ + return { data: this._attributes, description: this._name, op: this._attributes[SEMANTIC_ATTRIBUTE_SENTRY_OP], @@ -240,7 +239,7 @@ export class SentrySpan implements Span { is_segment: (this._isStandaloneSpan && getRootSpan(this) === this) || undefined, segment_id: this._isStandaloneSpan ? getRootSpan(this).spanContext().spanId : undefined, links: convertSpanLinksForEnvelope(this._links), - }); + }; } /** @inheritdoc */ diff --git a/packages/core/src/utils/spanUtils.ts b/packages/core/src/utils/spanUtils.ts index 7cb19fbacf3c..0a46ba600e0c 100644 --- a/packages/core/src/utils/spanUtils.ts +++ b/packages/core/src/utils/spanUtils.ts @@ -147,7 +147,7 @@ export function spanToJSON(span: Span): SpanJSON { if (spanIsOpenTelemetrySdkTraceBaseSpan(span)) { const { attributes, startTime, name, endTime, parentSpanId, status, links } = span; - return dropUndefinedKeys({ + return { span_id, trace_id, data: attributes, @@ -160,7 +160,7 @@ export function spanToJSON(span: Span): SpanJSON { op: attributes[SEMANTIC_ATTRIBUTE_SENTRY_OP], origin: attributes[SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN] as SpanOrigin | undefined, links: convertSpanLinksForEnvelope(links), - }); + }; } // Finally, at least we have `spanContext()`.... diff --git a/packages/core/test/lib/tracing/sentrySpan.test.ts b/packages/core/test/lib/tracing/sentrySpan.test.ts index eda0246bda03..726f30790c5b 100644 --- a/packages/core/test/lib/tracing/sentrySpan.test.ts +++ b/packages/core/test/lib/tracing/sentrySpan.test.ts @@ -75,26 +75,6 @@ describe('SentrySpan', () => { expect(serialized).toHaveProperty('span_id', 'd'); expect(serialized).toHaveProperty('trace_id', 'c'); }); - - test('should drop all `undefined` values', () => { - const spanA = new SentrySpan({ traceId: 'a', spanId: 'b' }); - const spanB = new SentrySpan({ - parentSpanId: spanA.spanContext().spanId, - spanId: 'd', - traceId: 'c', - }); - const serialized = spanToJSON(spanB); - expect(serialized).toStrictEqual({ - start_timestamp: expect.any(Number), - parent_span_id: 'b', - span_id: 'd', - trace_id: 'c', - origin: 'manual', - data: { - 'sentry.origin': 'manual', - }, - }); - }); }); describe('end', () => { From 244c87f578fb816864b98e4f895cf430a156380f Mon Sep 17 00:00:00 2001 From: Francesco Gringl-Novy Date: Mon, 24 Mar 2025 13:57:08 +0100 Subject: [PATCH 04/28] ref(node): Avoid `dropUndefinedKeys` in Node SDK init (#15797) We can instead of `??` to get the same outcome. --- packages/node/src/sdk/index.ts | 49 +++++++++++----------------------- 1 file changed, 15 insertions(+), 34 deletions(-) diff --git a/packages/node/src/sdk/index.ts b/packages/node/src/sdk/index.ts index 140f99bdd254..d479407a4e67 100644 --- a/packages/node/src/sdk/index.ts +++ b/packages/node/src/sdk/index.ts @@ -1,7 +1,6 @@ import type { Integration, Options } from '@sentry/core'; import { consoleSandbox, - dropUndefinedKeys, functionToStringIntegration, getCurrentScope, getIntegrationsToSetup, @@ -201,50 +200,32 @@ function getClientOptions( getDefaultIntegrationsImpl: (options: Options) => Integration[], ): NodeClientOptions { const release = getRelease(options.release); - - if (options.spotlight == null) { - const spotlightEnv = envToBool(process.env.SENTRY_SPOTLIGHT, { strict: true }); - if (spotlightEnv == null) { - options.spotlight = process.env.SENTRY_SPOTLIGHT; - } else { - options.spotlight = spotlightEnv; - } - } - + const spotlight = + options.spotlight ?? envToBool(process.env.SENTRY_SPOTLIGHT, { strict: true }) ?? process.env.SENTRY_SPOTLIGHT; const tracesSampleRate = getTracesSampleRate(options.tracesSampleRate); - const baseOptions = dropUndefinedKeys({ - dsn: process.env.SENTRY_DSN, - environment: process.env.SENTRY_ENVIRONMENT, - sendClientReports: true, - }); - - const overwriteOptions = dropUndefinedKeys({ - release, - tracesSampleRate, - transport: options.transport || makeNodeTransport, - }); - const mergedOptions = { - ...baseOptions, ...options, - ...overwriteOptions, + dsn: options.dsn ?? process.env.SENTRY_DSN, + environment: options.environment ?? process.env.SENTRY_ENVIRONMENT, + sendClientReports: options.sendClientReports ?? true, + transport: options.transport ?? makeNodeTransport, + stackParser: stackParserFromStackParserOptions(options.stackParser || defaultStackParser), + release, + tracesSampleRate, + spotlight, }; - if (options.defaultIntegrations === undefined) { - options.defaultIntegrations = getDefaultIntegrationsImpl(mergedOptions); - } + const integrations = options.integrations; + const defaultIntegrations = options.defaultIntegrations ?? getDefaultIntegrationsImpl(mergedOptions); - const clientOptions: NodeClientOptions = { + return { ...mergedOptions, - stackParser: stackParserFromStackParserOptions(options.stackParser || defaultStackParser), integrations: getIntegrationsToSetup({ - defaultIntegrations: options.defaultIntegrations, - integrations: options.integrations, + defaultIntegrations, + integrations, }), }; - - return clientOptions; } function getRelease(release: NodeOptions['release']): string | undefined { From 4262d53fc7367f4c9c8e874e73b7210d5a90cf5b Mon Sep 17 00:00:00 2001 From: Francesco Gringl-Novy Date: Mon, 24 Mar 2025 14:08:58 +0100 Subject: [PATCH 05/28] ref: Stop using `dropUndefinedKeys` in SpanExporter (#15794) Another place to get rid of this, this is serialized afterwards and this should not really matter! --- packages/opentelemetry/src/spanExporter.ts | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/packages/opentelemetry/src/spanExporter.ts b/packages/opentelemetry/src/spanExporter.ts index e095ab60e805..90a3d890a959 100644 --- a/packages/opentelemetry/src/spanExporter.ts +++ b/packages/opentelemetry/src/spanExporter.ts @@ -19,7 +19,6 @@ import { SEMANTIC_ATTRIBUTE_SENTRY_SAMPLE_RATE, SEMANTIC_ATTRIBUTE_SENTRY_SOURCE, captureEvent, - dropUndefinedKeys, getCapturedScopesOnSpan, getDynamicSamplingContextFromSpan, getStatusMessage, @@ -274,7 +273,7 @@ export function createTransactionForOtelSpan(span: ReadableSpan): TransactionEve const statusCode = attributes[ATTR_HTTP_RESPONSE_STATUS_CODE]; const responseContext = typeof statusCode === 'number' ? { response: { status_code: statusCode } } : undefined; - const transactionEvent: TransactionEvent = dropUndefinedKeys({ + const transactionEvent: TransactionEvent = { contexts: { trace: traceContext, otel: { @@ -298,7 +297,7 @@ export function createTransactionForOtelSpan(span: ReadableSpan): TransactionEve source, }, }), - }); + }; return transactionEvent; } @@ -335,7 +334,7 @@ function createAndFinishSpanForOtelSpan(node: SpanNode, spans: SpanJSON[], sentS const status = mapStatus(span); - const spanJSON: SpanJSON = dropUndefinedKeys({ + const spanJSON: SpanJSON = { span_id, trace_id, data: allData, @@ -349,7 +348,7 @@ function createAndFinishSpanForOtelSpan(node: SpanNode, spans: SpanJSON[], sentS origin, measurements: timedEventsToMeasurements(span.events), links: convertSpanLinksForEnvelope(links), - }); + }; spans.push(spanJSON); From c622c8a5d1ff9fd6dc1eea1d2ce31913e0df131c Mon Sep 17 00:00:00 2001 From: Lukas Stracke Date: Mon, 24 Mar 2025 14:11:35 +0100 Subject: [PATCH 06/28] test(node-integration): Skip flaky tedious tests (#15798) This test now failed twice in a row for me and I've seen it fail in countless PRs. Skipping it for now but we should prioritize https://github.com/getsentry/sentry-javascript/issues/15579 to eventually unflake and unskip them again. --- .../node-integration-tests/suites/tracing/tedious/test.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/dev-packages/node-integration-tests/suites/tracing/tedious/test.ts b/dev-packages/node-integration-tests/suites/tracing/tedious/test.ts index 797ede718b39..9a9fa28b1022 100644 --- a/dev-packages/node-integration-tests/suites/tracing/tedious/test.ts +++ b/dev-packages/node-integration-tests/suites/tracing/tedious/test.ts @@ -1,7 +1,8 @@ import { afterAll, describe, expect, test } from 'vitest'; import { cleanupChildProcesses, createRunner } from '../../../utils/runner'; -describe('tedious auto instrumentation', {timeout: 75_000}, () => { +// eslint-disable-next-line @sentry-internal/sdk/no-skipped-tests +describe.skip('tedious auto instrumentation', { timeout: 75_000 }, () => { afterAll(() => { cleanupChildProcesses(); }); From 85e59d38e96cef98c66781e8b6f0de7f88e855ac Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 24 Mar 2025 13:34:28 +0000 Subject: [PATCH 07/28] chore(deps): bump nuxt from 3.7.0 to 3.16.0 in /dev-packages/e2e-tests/test-applications/nuxt-3-min (#15744) --- .../e2e-tests/test-applications/nuxt-3-min/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dev-packages/e2e-tests/test-applications/nuxt-3-min/package.json b/dev-packages/e2e-tests/test-applications/nuxt-3-min/package.json index 62133c9a85c3..bb75c9aa1818 100644 --- a/dev-packages/e2e-tests/test-applications/nuxt-3-min/package.json +++ b/dev-packages/e2e-tests/test-applications/nuxt-3-min/package.json @@ -17,7 +17,7 @@ }, "dependencies": { "@sentry/nuxt": "latest || *", - "nuxt": "3.7.0" + "nuxt": "3.16.0" }, "devDependencies": { "@playwright/test": "~1.50.0", From aeec85a8c0d2e593ae9746008e226520fd393976 Mon Sep 17 00:00:00 2001 From: Sigrid Huemer <32902192+s1gr1d@users.noreply.github.com> Date: Mon, 24 Mar 2025 14:51:53 +0100 Subject: [PATCH 08/28] test(nuxt): Reduce version in Nuxt min test (#15800) Reducing version for E2E test which tests the minimum supported version for Nuxt. Was bumped here: https://github.com/getsentry/sentry-javascript/pull/15744 --- .../e2e-tests/test-applications/nuxt-3-min/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dev-packages/e2e-tests/test-applications/nuxt-3-min/package.json b/dev-packages/e2e-tests/test-applications/nuxt-3-min/package.json index bb75c9aa1818..62133c9a85c3 100644 --- a/dev-packages/e2e-tests/test-applications/nuxt-3-min/package.json +++ b/dev-packages/e2e-tests/test-applications/nuxt-3-min/package.json @@ -17,7 +17,7 @@ }, "dependencies": { "@sentry/nuxt": "latest || *", - "nuxt": "3.16.0" + "nuxt": "3.7.0" }, "devDependencies": { "@playwright/test": "~1.50.0", From ef67fdcac4a2ae1ab18426794d9dfaae66655433 Mon Sep 17 00:00:00 2001 From: Abhijeet Prasad Date: Mon, 24 Mar 2025 12:20:19 -0400 Subject: [PATCH 09/28] test: Add browser integration test for AggregateErrors (#15723) While trying to debug https://github.com/getsentry/sentry-javascript/issues/15707 I added a new integration test. Figured we should merge this in for now. --- .../aggregateError/subject.js | 13 ++++ .../captureException/aggregateError/test.ts | 69 +++++++++++++++++++ 2 files changed, 82 insertions(+) create mode 100644 dev-packages/browser-integration-tests/suites/public-api/captureException/aggregateError/subject.js create mode 100644 dev-packages/browser-integration-tests/suites/public-api/captureException/aggregateError/test.ts diff --git a/dev-packages/browser-integration-tests/suites/public-api/captureException/aggregateError/subject.js b/dev-packages/browser-integration-tests/suites/public-api/captureException/aggregateError/subject.js new file mode 100644 index 000000000000..fb5bf2f2100e --- /dev/null +++ b/dev-packages/browser-integration-tests/suites/public-api/captureException/aggregateError/subject.js @@ -0,0 +1,13 @@ +try { + // Create an AggregateError with multiple error objects + const error1 = new Error('First error message'); + const error2 = new TypeError('Second error message'); + const error3 = new RangeError('Third error message'); + + // Create the AggregateError with these errors and a message + const aggregateError = new AggregateError([error1, error2, error3], 'Multiple errors occurred'); + + throw aggregateError; +} catch (err) { + Sentry.captureException(err); +} diff --git a/dev-packages/browser-integration-tests/suites/public-api/captureException/aggregateError/test.ts b/dev-packages/browser-integration-tests/suites/public-api/captureException/aggregateError/test.ts new file mode 100644 index 000000000000..41b1a09c74f3 --- /dev/null +++ b/dev-packages/browser-integration-tests/suites/public-api/captureException/aggregateError/test.ts @@ -0,0 +1,69 @@ +import { expect } from '@playwright/test'; + +import { sentryTest } from '../../../../utils/fixtures'; +import { envelopeRequestParser, waitForErrorRequestOnUrl } from '../../../../utils/helpers'; + +sentryTest('should capture an AggregateError with embedded errors', async ({ getLocalTestUrl, page }) => { + const url = await getLocalTestUrl({ testDir: __dirname }); + const req = await waitForErrorRequestOnUrl(page, url); + const eventData = envelopeRequestParser(req); + + expect(eventData.exception?.values).toHaveLength(4); // AggregateError + 3 embedded errors + + // Verify the embedded errors come first + expect(eventData.exception?.values?.[0]).toMatchObject({ + type: 'RangeError', + value: 'Third error message', + mechanism: { + type: 'chained', + handled: true, + source: expect.stringMatching(/^errors\[\d+\]$/), + exception_id: expect.any(Number), + }, + }); + + expect(eventData.exception?.values?.[1]).toMatchObject({ + type: 'TypeError', + value: 'Second error message', + mechanism: { + type: 'chained', + handled: true, + source: expect.stringMatching(/^errors\[\d+\]$/), + exception_id: expect.any(Number), + }, + }); + + expect(eventData.exception?.values?.[2]).toMatchObject({ + type: 'Error', + value: 'First error message', + mechanism: { + type: 'chained', + handled: true, + source: expect.stringMatching(/^errors\[\d+\]$/), + exception_id: expect.any(Number), + }, + }); + + // Verify the AggregateError comes last + expect(eventData.exception?.values?.[3]).toMatchObject({ + type: 'AggregateError', + value: 'Multiple errors occurred', + mechanism: { + type: 'generic', + handled: true, + is_exception_group: true, + exception_id: expect.any(Number), + }, + stacktrace: { + frames: expect.any(Array), + }, + }); + + // Get parent exception ID for reference checks + const parentId = eventData.exception?.values?.[3].mechanism?.exception_id; + + // Verify parent_id references match for all child errors + for (let i = 0; i < 3; i++) { + expect(eventData.exception?.values?.[i].mechanism?.parent_id).toBe(parentId); + } +}); From 38a499a77b4ad37b6105696ec2fb5fc93d26c017 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 24 Mar 2025 16:29:13 +0000 Subject: [PATCH 10/28] chore(deps): bump next from 14.0.2 to 14.2.25 in /dev-packages/e2e-tests/test-applications/nextjs-app-dir (#15799) --- .../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 f779d9ece306..f02e3c0138da 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 @@ -19,7 +19,7 @@ "@types/node": "^18.19.1", "@types/react": "18.0.26", "@types/react-dom": "18.0.9", - "next": "14.0.2", + "next": "14.2.25", "react": "18.2.0", "react-dom": "18.2.0", "typescript": "~5.0.0" From 2ed6fc20516f3c70c9f7380adf18fb10fa4138bf Mon Sep 17 00:00:00 2001 From: Abhijeet Prasad Date: Mon, 24 Mar 2025 13:21:33 -0400 Subject: [PATCH 11/28] feat(node): Add logging public APIs to Node SDKs (#15764) ref https://github.com/getsentry/sentry-javascript/issues/15526 Continuing off the work from https://github.com/getsentry/sentry-javascript/pull/15763, this PR adds the logging public API to the Node SDK. It also adds a basic weight-based flushing strategy to the SDK that is in the server-runtime client. The main file added was `log.ts`. In that file I've added logger methods for `trace`, `debug`, `info`, `warn`, `error`, `fatal` (the log severity levels) as well as an internal capture log helper all these methods call. Usage: ```js import * as Sentry from "@sentry/node"; Sentry.init({ dsn: "your-dsn-here", _experiments: { enableLogs: true // This is required to use the logging features } }); // Trace level (lowest severity) Sentry.logger.trace("This is a trace message", { userId: 123 }); // Debug level Sentry.logger.debug("This is a debug message", { component: "UserProfile" }); // Info level Sentry.logger.info("User %s logged in successfully", [123]); // Warning level Sentry.logger.warn("API response was slow", { responseTime: 2500 }); // Error level Sentry.logger.error("Failed to load user %s data", [123], { errorCode: 404 }); // Critical level Sentry.logger.critical("Database connection failed", { dbHost: "primary-db" }); // Fatal level (highest severity) Sentry.logger.fatal("Application is shutting down unexpectedly", { memory: "exhausted" }); ``` --- packages/astro/src/index.server.ts | 1 + packages/astro/src/index.types.ts | 3 + packages/aws-serverless/src/index.ts | 1 + packages/bun/src/index.ts | 1 + packages/core/src/client.ts | 13 + packages/core/src/logs/index.ts | 12 +- packages/core/src/server-runtime-client.ts | 68 ++++++ packages/core/src/types-hoist/log.ts | 2 +- packages/google-cloud-serverless/src/index.ts | 1 + packages/nextjs/src/index.types.ts | 2 + packages/node/src/index.ts | 4 + packages/node/src/log.ts | 223 ++++++++++++++++++ packages/node/test/log.test.ts | 130 ++++++++++ packages/nuxt/src/index.types.ts | 3 + packages/react-router/src/index.types.ts | 2 + packages/remix/src/index.types.ts | 2 + packages/remix/src/server/index.ts | 1 + packages/solidstart/src/index.types.ts | 2 + packages/solidstart/src/server/index.ts | 1 + packages/sveltekit/src/index.types.ts | 2 + packages/sveltekit/src/server/index.ts | 1 + .../tanstackstart-react/src/index.types.ts | 2 + 22 files changed, 470 insertions(+), 7 deletions(-) create mode 100644 packages/node/src/log.ts create mode 100644 packages/node/test/log.test.ts diff --git a/packages/astro/src/index.server.ts b/packages/astro/src/index.server.ts index 6f9647d0134e..6467affd841c 100644 --- a/packages/astro/src/index.server.ts +++ b/packages/astro/src/index.server.ts @@ -127,6 +127,7 @@ export { withScope, zodErrorsIntegration, profiler, + logger, } from '@sentry/node'; export { init } from './server/sdk'; diff --git a/packages/astro/src/index.types.ts b/packages/astro/src/index.types.ts index eeadf11fa3d5..ec6713098f11 100644 --- a/packages/astro/src/index.types.ts +++ b/packages/astro/src/index.types.ts @@ -10,6 +10,7 @@ import type { NodeOptions } from '@sentry/node'; import type { Client, Integration, Options, StackParser } from '@sentry/core'; import type * as clientSdk from './index.client'; +import type * as serverSdk from './index.server'; import sentryAstro from './index.server'; /** Initializes Sentry Astro SDK */ @@ -26,4 +27,6 @@ export declare function flush(timeout?: number | undefined): PromiseLike { */ public on(hook: 'close', callback: () => void): () => void; + /** + * A hook that is called before a log is captured + * + * @returns {() => void} A function that, when executed, removes the registered callback. + */ + public on(hook: 'beforeCaptureLog', callback: (log: Log) => void): () => void; + /** * Register a hook on this client. */ @@ -768,6 +776,11 @@ export abstract class Client { */ public emit(hook: 'close'): void; + /** + * Emit a hook event for client before capturing a log + */ + public emit(hook: 'beforeCaptureLog', log: Log): void; + /** * Emit a hook that was previously registered via `on()`. */ diff --git a/packages/core/src/logs/index.ts b/packages/core/src/logs/index.ts index 6c6c9b580ee9..56de1b0bdc15 100644 --- a/packages/core/src/logs/index.ts +++ b/packages/core/src/logs/index.ts @@ -110,14 +110,14 @@ export function _INTERNAL_captureLog(log: Log, client = getClient(), scope = get const logBuffer = CLIENT_TO_LOG_BUFFER_MAP.get(client); if (logBuffer === undefined) { CLIENT_TO_LOG_BUFFER_MAP.set(client, [serializedLog]); - // Every time we initialize a new log buffer, we start a new interval to flush the buffer - return; + } else { + logBuffer.push(serializedLog); + if (logBuffer.length > MAX_LOG_BUFFER_SIZE) { + _INTERNAL_flushLogsBuffer(client, logBuffer); + } } - logBuffer.push(serializedLog); - if (logBuffer.length > MAX_LOG_BUFFER_SIZE) { - _INTERNAL_flushLogsBuffer(client, logBuffer); - } + client.emit('beforeCaptureLog', log); } /** diff --git a/packages/core/src/server-runtime-client.ts b/packages/core/src/server-runtime-client.ts index 61db792b901e..9f160d7ee25e 100644 --- a/packages/core/src/server-runtime-client.ts +++ b/packages/core/src/server-runtime-client.ts @@ -4,8 +4,10 @@ import type { ClientOptions, Event, EventHint, + Log, MonitorConfig, ParameterizedString, + Primitive, SerializedCheckIn, SeverityLevel, } from './types-hoist'; @@ -20,6 +22,8 @@ import { eventFromMessage, eventFromUnknownInput } from './utils-hoist/eventbuil import { logger } from './utils-hoist/logger'; import { uuid4 } from './utils-hoist/misc'; import { resolvedSyncPromise } from './utils-hoist/syncpromise'; +import { _INTERNAL_flushLogsBuffer } from './logs'; +import { isPrimitive } from './utils-hoist'; export interface ServerRuntimeClientOptions extends ClientOptions { platform?: string; @@ -33,6 +37,8 @@ export interface ServerRuntimeClientOptions extends ClientOptions extends Client { + private _logWeight: number; + /** * Creates a new Edge SDK instance. * @param options Configuration options for this SDK. @@ -42,6 +48,26 @@ export class ServerRuntimeClient< registerSpanErrorInstrumentation(); super(options); + + this._logWeight = 0; + + // eslint-disable-next-line @typescript-eslint/no-this-alias + const client = this; + this.on('flush', () => { + _INTERNAL_flushLogsBuffer(client); + }); + + this.on('beforeCaptureLog', log => { + client._logWeight += estimateLogSizeInBytes(log); + + // We flush the logs buffer if it exceeds 0.8 MB + // The log weight is a rough estimate, so we flush way before + // the payload gets too big. + if (client._logWeight > 800_000) { + _INTERNAL_flushLogsBuffer(client); + client._logWeight = 0; + } + }); } /** @@ -196,3 +222,45 @@ function setCurrentRequestSessionErroredOrCrashed(eventHint?: EventHint): void { } } } + +/** + * Estimate the size of a log in bytes. + * + * @param log - The log to estimate the size of. + * @returns The estimated size of the log in bytes. + */ +function estimateLogSizeInBytes(log: Log): number { + let weight = 0; + + // Estimate byte size of 2 bytes per character. This is a rough estimate JS strings are stored as UTF-16. + if (log.message) { + weight += log.message.length * 2; + } + + if (log.attributes) { + Object.values(log.attributes).forEach(value => { + if (Array.isArray(value)) { + weight += value.length * estimatePrimitiveSizeInBytes(value[0]); + } else if (isPrimitive(value)) { + weight += estimatePrimitiveSizeInBytes(value); + } else { + // For objects values, we estimate the size of the object as 100 bytes + weight += 100; + } + }); + } + + return weight; +} + +function estimatePrimitiveSizeInBytes(value: Primitive): number { + if (typeof value === 'string') { + return value.length * 2; + } else if (typeof value === 'number') { + return 8; + } else if (typeof value === 'boolean') { + return 4; + } + + return 0; +} diff --git a/packages/core/src/types-hoist/log.ts b/packages/core/src/types-hoist/log.ts index a313b493306c..45172c44adc0 100644 --- a/packages/core/src/types-hoist/log.ts +++ b/packages/core/src/types-hoist/log.ts @@ -41,7 +41,7 @@ export interface Log { /** * Arbitrary structured data that stores information about the log - e.g., userId: 100. */ - attributes?: Record>; + attributes?: Record; /** * The severity number - generally higher severity are levels like 'error' and lower are levels like 'debug' diff --git a/packages/google-cloud-serverless/src/index.ts b/packages/google-cloud-serverless/src/index.ts index 9505ef6dd248..83a18f62c6df 100644 --- a/packages/google-cloud-serverless/src/index.ts +++ b/packages/google-cloud-serverless/src/index.ts @@ -113,6 +113,7 @@ export { amqplibIntegration, childProcessIntegration, vercelAIIntegration, + logger, } from '@sentry/node'; export { diff --git a/packages/nextjs/src/index.types.ts b/packages/nextjs/src/index.types.ts index 7b6173f9c8ea..f1b24da081f0 100644 --- a/packages/nextjs/src/index.types.ts +++ b/packages/nextjs/src/index.types.ts @@ -32,6 +32,8 @@ export declare const createReduxEnhancer: typeof clientSdk.createReduxEnhancer; export declare const showReportDialog: typeof clientSdk.showReportDialog; export declare const withErrorBoundary: typeof clientSdk.withErrorBoundary; +export declare const logger: typeof clientSdk.logger | typeof serverSdk.logger; + export { withSentryConfig } from './config'; /** diff --git a/packages/node/src/index.ts b/packages/node/src/index.ts index bdc8d6405217..decfbd578c68 100644 --- a/packages/node/src/index.ts +++ b/packages/node/src/index.ts @@ -150,3 +150,7 @@ export type { User, Span, } from '@sentry/core'; + +import * as logger from './log'; + +export { logger }; diff --git a/packages/node/src/log.ts b/packages/node/src/log.ts new file mode 100644 index 000000000000..9bad4895ceb6 --- /dev/null +++ b/packages/node/src/log.ts @@ -0,0 +1,223 @@ +import { format } from 'node:util'; + +import type { LogSeverityLevel, Log } from '@sentry/core'; +import { _INTERNAL_captureLog } from '@sentry/core'; + +type CaptureLogArgs = + | [message: string, attributes?: Log['attributes']] + | [messageTemplate: string, messageParams: Array, attributes?: Log['attributes']]; + +/** + * Capture a log with the given level. + * + * @param level - The level of the log. + * @param message - The message to log. + * @param attributes - Arbitrary structured data that stores information about the log - e.g., userId: 100. + */ +function captureLog(level: LogSeverityLevel, ...args: CaptureLogArgs): void { + const [messageOrMessageTemplate, paramsOrAttributes, maybeAttributes] = args; + if (Array.isArray(paramsOrAttributes)) { + const attributes = { ...maybeAttributes }; + attributes['sentry.message.template'] = messageOrMessageTemplate; + paramsOrAttributes.forEach((param, index) => { + attributes[`sentry.message.param.${index}`] = param; + }); + const message = format(messageOrMessageTemplate, ...paramsOrAttributes); + _INTERNAL_captureLog({ level, message, attributes }); + } else { + _INTERNAL_captureLog({ level, message: messageOrMessageTemplate, attributes: paramsOrAttributes }); + } +} + +/** + * @summary Capture a log with the `trace` level. Requires `_experiments.enableLogs` to be enabled. + * + * You can either pass a message and attributes or a message template, params and attributes. + * + * @example + * + * ``` + * Sentry.logger.trace('Starting database connection', { + * database: 'users', + * connectionId: 'conn_123' + * }); + * ``` + * + * @example With template strings + * + * ``` + * Sentry.logger.trace('Database connection %s established for %s', + * ['successful', 'users'], + * { connectionId: 'conn_123' } + * ); + * ``` + */ +export function trace(...args: CaptureLogArgs): void { + captureLog('trace', ...args); +} + +/** + * @summary Capture a log with the `debug` level. Requires `_experiments.enableLogs` to be enabled. + * + * You can either pass a message and attributes or a message template, params and attributes. + * + * @example + * + * ``` + * Sentry.logger.debug('Cache miss for user profile', { + * userId: 'user_123', + * cacheKey: 'profile:user_123' + * }); + * ``` + * + * @example With template strings + * + * ``` + * Sentry.logger.debug('Cache %s for %s: %s', + * ['miss', 'user profile', 'key not found'], + * { userId: 'user_123' } + * ); + * ``` + */ +export function debug(...args: CaptureLogArgs): void { + captureLog('debug', ...args); +} + +/** + * @summary Capture a log with the `info` level. Requires `_experiments.enableLogs` to be enabled. + * + * You can either pass a message and attributes or a message template, params and attributes. + * + * @example + * + * ``` + * Sentry.logger.info('User profile updated', { + * userId: 'user_123', + * updatedFields: ['email', 'preferences'] + * }); + * ``` + * + * @example With template strings + * + * ``` + * Sentry.logger.info('User %s updated their %s', + * ['John Doe', 'profile settings'], + * { userId: 'user_123' } + * ); + * ``` + */ +export function info(...args: CaptureLogArgs): void { + captureLog('info', ...args); +} + +/** + * @summary Capture a log with the `warn` level. Requires `_experiments.enableLogs` to be enabled. + * + * You can either pass a message and attributes or a message template, params and attributes. + * + * @example + * + * ``` + * Sentry.logger.warn('Rate limit approaching', { + * endpoint: '/api/users', + * currentRate: '95/100', + * resetTime: '2024-03-20T10:00:00Z' + * }); + * ``` + * + * @example With template strings + * + * ``` + * Sentry.logger.warn('Rate limit %s for %s: %s', + * ['approaching', '/api/users', '95/100 requests'], + * { resetTime: '2024-03-20T10:00:00Z' } + * ); + * ``` + */ +export function warn(...args: CaptureLogArgs): void { + captureLog('warn', ...args); +} + +/** + * @summary Capture a log with the `error` level. Requires `_experiments.enableLogs` to be enabled. + * + * You can either pass a message and attributes or a message template, params and attributes. + * + * @example + * + * ``` + * Sentry.logger.error('Failed to process payment', { + * orderId: 'order_123', + * errorCode: 'PAYMENT_FAILED', + * amount: 99.99 + * }); + * ``` + * + * @example With template strings + * + * ``` + * Sentry.logger.error('Payment processing failed for order %s: %s', + * ['order_123', 'insufficient funds'], + * { amount: 99.99 } + * ); + * ``` + */ +export function error(...args: CaptureLogArgs): void { + captureLog('error', ...args); +} + +/** + * @summary Capture a log with the `fatal` level. Requires `_experiments.enableLogs` to be enabled. + * + * You can either pass a message and attributes or a message template, params and attributes. + * + * @example + * + * ``` + * Sentry.logger.fatal('Database connection pool exhausted', { + * database: 'users', + * activeConnections: 100, + * maxConnections: 100 + * }); + * ``` + * + * @example With template strings + * + * ``` + * Sentry.logger.fatal('Database %s: %s connections active', + * ['connection pool exhausted', '100/100'], + * { database: 'users' } + * ); + * ``` + */ +export function fatal(...args: CaptureLogArgs): void { + captureLog('fatal', ...args); +} + +/** + * @summary Capture a log with the `critical` level. Requires `_experiments.enableLogs` to be enabled. + * + * You can either pass a message and attributes or a message template, params and attributes. + * + * @example + * + * ``` + * Sentry.logger.critical('Service health check failed', { + * service: 'payment-gateway', + * status: 'DOWN', + * lastHealthy: '2024-03-20T09:55:00Z' + * }); + * ``` + * + * @example With template strings + * + * ``` + * Sentry.logger.critical('Service %s is %s', + * ['payment-gateway', 'DOWN'], + * { lastHealthy: '2024-03-20T09:55:00Z' } + * ); + * ``` + */ +export function critical(...args: CaptureLogArgs): void { + captureLog('critical', ...args); +} diff --git a/packages/node/test/log.test.ts b/packages/node/test/log.test.ts new file mode 100644 index 000000000000..4064d7c1f3f1 --- /dev/null +++ b/packages/node/test/log.test.ts @@ -0,0 +1,130 @@ +import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'; +import * as sentryCore from '@sentry/core'; +import * as nodeLogger from '../src/log'; + +// Mock the core functions +vi.mock('@sentry/core', async () => { + const actual = await vi.importActual('@sentry/core'); + return { + ...actual, + _INTERNAL_captureLog: vi.fn(), + }; +}); + +describe('Node Logger', () => { + // Use the mocked function + const mockCaptureLog = vi.mocked(sentryCore._INTERNAL_captureLog); + + beforeEach(() => { + // Reset mocks + mockCaptureLog.mockClear(); + }); + + afterEach(() => { + vi.resetAllMocks(); + }); + + describe('Basic logging methods', () => { + it('should export all log methods', () => { + expect(nodeLogger.trace).toBeTypeOf('function'); + expect(nodeLogger.debug).toBeTypeOf('function'); + expect(nodeLogger.info).toBeTypeOf('function'); + expect(nodeLogger.warn).toBeTypeOf('function'); + expect(nodeLogger.error).toBeTypeOf('function'); + expect(nodeLogger.fatal).toBeTypeOf('function'); + expect(nodeLogger.critical).toBeTypeOf('function'); + }); + + it('should call _INTERNAL_captureLog with trace level', () => { + nodeLogger.trace('Test trace message', { key: 'value' }); + expect(mockCaptureLog).toHaveBeenCalledWith({ + level: 'trace', + message: 'Test trace message', + attributes: { key: 'value' }, + }); + }); + + it('should call _INTERNAL_captureLog with debug level', () => { + nodeLogger.debug('Test debug message', { key: 'value' }); + expect(mockCaptureLog).toHaveBeenCalledWith({ + level: 'debug', + message: 'Test debug message', + attributes: { key: 'value' }, + }); + }); + + it('should call _INTERNAL_captureLog with info level', () => { + nodeLogger.info('Test info message', { key: 'value' }); + expect(mockCaptureLog).toHaveBeenCalledWith({ + level: 'info', + message: 'Test info message', + attributes: { key: 'value' }, + }); + }); + + it('should call _INTERNAL_captureLog with warn level', () => { + nodeLogger.warn('Test warn message', { key: 'value' }); + expect(mockCaptureLog).toHaveBeenCalledWith({ + level: 'warn', + message: 'Test warn message', + attributes: { key: 'value' }, + }); + }); + + it('should call _INTERNAL_captureLog with error level', () => { + nodeLogger.error('Test error message', { key: 'value' }); + expect(mockCaptureLog).toHaveBeenCalledWith({ + level: 'error', + message: 'Test error message', + attributes: { key: 'value' }, + }); + }); + + it('should call _INTERNAL_captureLog with fatal level', () => { + nodeLogger.fatal('Test fatal message', { key: 'value' }); + expect(mockCaptureLog).toHaveBeenCalledWith({ + level: 'fatal', + message: 'Test fatal message', + attributes: { key: 'value' }, + }); + }); + + it('should call _INTERNAL_captureLog with critical level', () => { + nodeLogger.critical('Test critical message', { key: 'value' }); + expect(mockCaptureLog).toHaveBeenCalledWith({ + level: 'critical', + message: 'Test critical message', + attributes: { key: 'value' }, + }); + }); + }); + + describe('Template string logging', () => { + it('should handle template strings with parameters', () => { + nodeLogger.info('Hello %s, your balance is %d', ['John', 100], { userId: 123 }); + expect(mockCaptureLog).toHaveBeenCalledWith({ + level: 'info', + message: 'Hello John, your balance is 100', + attributes: { + userId: 123, + 'sentry.message.template': 'Hello %s, your balance is %d', + 'sentry.message.param.0': 'John', + 'sentry.message.param.1': 100, + }, + }); + }); + + it('should handle template strings without additional attributes', () => { + nodeLogger.debug('User %s logged in from %s', ['Alice', 'mobile']); + expect(mockCaptureLog).toHaveBeenCalledWith({ + level: 'debug', + message: 'User Alice logged in from mobile', + attributes: { + 'sentry.message.template': 'User %s logged in from %s', + 'sentry.message.param.0': 'Alice', + 'sentry.message.param.1': 'mobile', + }, + }); + }); + }); +}); diff --git a/packages/nuxt/src/index.types.ts b/packages/nuxt/src/index.types.ts index b1393a076029..24b21b8680ae 100644 --- a/packages/nuxt/src/index.types.ts +++ b/packages/nuxt/src/index.types.ts @@ -1,6 +1,7 @@ import type { Client, Integration, Options, StackParser } from '@sentry/core'; import type { SentryNuxtClientOptions, SentryNuxtServerOptions } from './common/types'; import type * as clientSdk from './index.client'; +import type * as serverSdk from './index.server'; // We export everything from both the client part of the SDK and from the server part. Some of the exports collide, // which is not allowed, unless we re-export the colliding exports in this file - which we do below. @@ -13,3 +14,5 @@ export declare const linkedErrorsIntegration: typeof clientSdk.linkedErrorsInteg export declare const contextLinesIntegration: typeof clientSdk.contextLinesIntegration; export declare const getDefaultIntegrations: (options: Options) => Integration[]; export declare const defaultStackParser: StackParser; + +export declare const logger: typeof clientSdk.logger | typeof serverSdk.logger; diff --git a/packages/react-router/src/index.types.ts b/packages/react-router/src/index.types.ts index 4d7fb425f5ee..cf62bf717794 100644 --- a/packages/react-router/src/index.types.ts +++ b/packages/react-router/src/index.types.ts @@ -15,3 +15,5 @@ export declare const contextLinesIntegration: typeof clientSdk.contextLinesInteg export declare const linkedErrorsIntegration: typeof clientSdk.linkedErrorsIntegration; export declare const defaultStackParser: StackParser; export declare const getDefaultIntegrations: (options: Options) => Integration[]; + +export declare const logger: typeof clientSdk.logger | typeof serverSdk.logger; diff --git a/packages/remix/src/index.types.ts b/packages/remix/src/index.types.ts index 763a2747f69e..2d1ae40da8e5 100644 --- a/packages/remix/src/index.types.ts +++ b/packages/remix/src/index.types.ts @@ -21,6 +21,8 @@ export declare const defaultStackParser: StackParser; export declare function captureRemixServerException(err: unknown, name: string, request: Request): Promise; +export declare const logger: typeof clientSdk.logger | typeof serverSdk.logger; + // This variable is not a runtime variable but just a type to tell typescript that the methods below can either come // from the client SDK or from the server SDK. TypeScript is smart enough to understand that these resolve to the same // methods from `@sentry/core`. diff --git a/packages/remix/src/server/index.ts b/packages/remix/src/server/index.ts index 4160a871d165..7809455ce3fa 100644 --- a/packages/remix/src/server/index.ts +++ b/packages/remix/src/server/index.ts @@ -112,6 +112,7 @@ export { withMonitor, withScope, zodErrorsIntegration, + logger, } from '@sentry/node'; // Keeping the `*` exports for backwards compatibility and types diff --git a/packages/solidstart/src/index.types.ts b/packages/solidstart/src/index.types.ts index 54a5ec6d6a3c..d243bd371241 100644 --- a/packages/solidstart/src/index.types.ts +++ b/packages/solidstart/src/index.types.ts @@ -22,3 +22,5 @@ export declare const defaultStackParser: StackParser; export declare function close(timeout?: number | undefined): PromiseLike; export declare function flush(timeout?: number | undefined): PromiseLike; export declare function lastEventId(): string | undefined; + +export declare const logger: typeof clientSdk.logger | typeof serverSdk.logger; diff --git a/packages/solidstart/src/server/index.ts b/packages/solidstart/src/server/index.ts index 948c3c746d0c..a36b7fbfaad1 100644 --- a/packages/solidstart/src/server/index.ts +++ b/packages/solidstart/src/server/index.ts @@ -115,6 +115,7 @@ export { withMonitor, withScope, zodErrorsIntegration, + logger, } from '@sentry/node'; // We can still leave this for the carrier init and type exports diff --git a/packages/sveltekit/src/index.types.ts b/packages/sveltekit/src/index.types.ts index bf2edbfb0a0f..161e7098de11 100644 --- a/packages/sveltekit/src/index.types.ts +++ b/packages/sveltekit/src/index.types.ts @@ -53,3 +53,5 @@ export declare function flush(timeout?: number | undefined): PromiseLike; + +export declare const logger: typeof clientSdk.logger | typeof serverSdk.logger; diff --git a/packages/sveltekit/src/server/index.ts b/packages/sveltekit/src/server/index.ts index f8844c1e264d..9df3ddd688c8 100644 --- a/packages/sveltekit/src/server/index.ts +++ b/packages/sveltekit/src/server/index.ts @@ -117,6 +117,7 @@ export { withMonitor, withScope, zodErrorsIntegration, + logger, } from '@sentry/node'; // We can still leave this for the carrier init and type exports diff --git a/packages/tanstackstart-react/src/index.types.ts b/packages/tanstackstart-react/src/index.types.ts index 84a987788d17..0e3bbca37bf9 100644 --- a/packages/tanstackstart-react/src/index.types.ts +++ b/packages/tanstackstart-react/src/index.types.ts @@ -25,3 +25,5 @@ export declare const ErrorBoundary: typeof clientSdk.ErrorBoundary; export declare const createReduxEnhancer: typeof clientSdk.createReduxEnhancer; export declare const showReportDialog: typeof clientSdk.showReportDialog; export declare const withErrorBoundary: typeof clientSdk.withErrorBoundary; + +export declare const logger: typeof clientSdk.logger | typeof serverSdk.logger; From 2e26182f20b1cae54d955413290b87ae3ea56082 Mon Sep 17 00:00:00 2001 From: Luca Forstner Date: Tue, 25 Mar 2025 14:24:25 +0100 Subject: [PATCH 12/28] feat(browser): Add `diagnoseSdkConnectivity()` function to programmatically detect possible connectivity issues (#15821) Resolves https://github.com/getsentry/sentry-javascript/issues/15780 Adds a function `diagnoseSdkConnectivity` that will resolve to various "error codes" for possible reasons why events might not land in Sentry. Originally I wanted to add an API to detect ad blockers so we can show a notification in the example pages that the wizard creates. Then I quickly thought of a different case why the SDK might not send data so I extended the API to also detect if the SDK wasn't initialized yet. We can extend this function at any point if we come up with more cases. --- packages/browser/src/diagnose-sdk.ts | 41 ++++++++++++++++++++++++++++ packages/browser/src/index.ts | 1 + 2 files changed, 42 insertions(+) create mode 100644 packages/browser/src/diagnose-sdk.ts diff --git a/packages/browser/src/diagnose-sdk.ts b/packages/browser/src/diagnose-sdk.ts new file mode 100644 index 000000000000..e2399b8103b0 --- /dev/null +++ b/packages/browser/src/diagnose-sdk.ts @@ -0,0 +1,41 @@ +import { getClient } from '@sentry/core'; + +/** + * A function to diagnose why the SDK might not be successfully sending data. + * + * Possible return values wrapped in a Promise: + * - `"no-client-active"` - There was no active client when the function was called. This possibly means that the SDK was not initialized yet. + * - `"sentry-unreachable"` - The Sentry SaaS servers were not reachable. This likely means that there is an ad blocker active on the page or that there are other connection issues. + * + * If the function doesn't detect an issue it resolves to `undefined`. + */ +export async function diagnoseSdkConnectivity(): Promise< + 'no-client-active' | 'sentry-unreachable' | 'no-dsn-configured' | void +> { + const client = getClient(); + + if (!client) { + return 'no-client-active'; + } + + if (!client.getDsn()) { + return 'no-dsn-configured'; + } + + try { + // If fetch throws, there is likely an ad blocker active or there are other connective issues. + await fetch( + // We want this to be as close as possible to an actual ingest URL so that ad blockers will actually block the request + // We are using the "sentry-sdks" org with id 447951 not to pollute any actual organizations. + 'https://o447951.ingest.sentry.io/api/1337/envelope/?sentry_version=7&sentry_key=1337&sentry_client=sentry.javascript.browser%2F1.33.7', + { + body: '{}', + method: 'POST', + mode: 'cors', + credentials: 'omit', + }, + ); + } catch { + return 'sentry-unreachable'; + } +} diff --git a/packages/browser/src/index.ts b/packages/browser/src/index.ts index 47bef0cd6dae..9103bbab99b5 100644 --- a/packages/browser/src/index.ts +++ b/packages/browser/src/index.ts @@ -71,3 +71,4 @@ export { launchDarklyIntegration, buildLaunchDarklyFlagUsedHandler } from './int export { openFeatureIntegration, OpenFeatureIntegrationHook } from './integrations/featureFlags/openfeature'; export { unleashIntegration } from './integrations/featureFlags/unleash'; export { statsigIntegration } from './integrations/featureFlags/statsig'; +export { diagnoseSdkConnectivity } from './diagnose-sdk'; From f2f21ee6a0e6c7ea5cfee21669a5b5f72fdbd3c2 Mon Sep 17 00:00:00 2001 From: Abhijeet Prasad Date: Tue, 25 Mar 2025 09:43:57 -0400 Subject: [PATCH 13/28] ref(browser-utils): Give parseUrl a better name (#15813) While working on https://github.com/getsentry/sentry-javascript/issues/15767 I ran into a `parseUrl` function defined in `packages/browser-utils/src/instrument/xhr.ts`. This usage is fine, but was a bit confusing, so I renamed the method and expanded the docstring. I also expanded the types a little bit. --- packages/browser-utils/src/instrument/xhr.ts | 27 +++++++++++++++----- 1 file changed, 20 insertions(+), 7 deletions(-) diff --git a/packages/browser-utils/src/instrument/xhr.ts b/packages/browser-utils/src/instrument/xhr.ts index cf44434ff4f8..c90467380c30 100644 --- a/packages/browser-utils/src/instrument/xhr.ts +++ b/packages/browser-utils/src/instrument/xhr.ts @@ -30,7 +30,13 @@ export function instrumentXHR(): void { // eslint-disable-next-line @typescript-eslint/unbound-method xhrproto.open = new Proxy(xhrproto.open, { - apply(originalOpen, xhrOpenThisArg: XMLHttpRequest & SentryWrappedXMLHttpRequest, xhrOpenArgArray) { + apply( + originalOpen, + xhrOpenThisArg: XMLHttpRequest & SentryWrappedXMLHttpRequest, + xhrOpenArgArray: + | [method: string, url: string | URL] + | [method: string, url: string | URL, async: boolean, username?: string | null, password?: string | null], + ) { // NOTE: If you are a Sentry user, and you are seeing this stack frame, // it means the error, that was caused by your XHR call did not // have a stack trace. If you are using HttpClient integration, @@ -43,7 +49,7 @@ export function instrumentXHR(): void { // open() should always be called with two or more arguments // But to be on the safe side, we actually validate this and bail out if we don't have a method & url const method = isString(xhrOpenArgArray[0]) ? xhrOpenArgArray[0].toUpperCase() : undefined; - const url = parseUrl(xhrOpenArgArray[1]); + const url = parseXhrUrlArg(xhrOpenArgArray[1]); if (!method || !url) { return originalOpen.apply(xhrOpenThisArg, xhrOpenArgArray); @@ -147,16 +153,23 @@ export function instrumentXHR(): void { }); } -function parseUrl(url: string | unknown): string | undefined { +/** + * Parses the URL argument of a XHR method to a string. + * + * See: https://developer.mozilla.org/en-US/docs/Web/API/XMLHttpRequest/open#url + * url: A string or any other object with a stringifier — including a URL object — that provides the URL of the resource to send the request to. + * + * @param url - The URL argument of an XHR method + * @returns The parsed URL string or undefined if the URL is invalid + */ +function parseXhrUrlArg(url: unknown): string | undefined { if (isString(url)) { return url; } try { - // url can be a string or URL - // but since URL is not available in IE11, we do not check for it, - // but simply assume it is an URL and return `toString()` from it (which returns the full URL) - // If that fails, we just return undefined + // If the passed in argument is not a string, it should have a `toString` method as a stringifier. + // If that fails, we just return undefined (like in IE11 where URL is not available) return (url as URL).toString(); } catch {} // eslint-disable-line no-empty From 9ca030d0e47b4827a473ce4e294c074e1da93830 Mon Sep 17 00:00:00 2001 From: Abhijeet Prasad Date: Tue, 25 Mar 2025 09:44:09 -0400 Subject: [PATCH 14/28] feat(core): Add support for `beforeSendLog` (#15814) ref https://github.com/getsentry/sentry-javascript/issues/15526 Adds support for `beforeSendLog`, currently in the `_experiments` options namespace. While adding `beforeSendLog`, I noticed the `beforeCaptureLog` was not placed correctly. This PR also fixes that, and introduces a `afterCaptureLog` that runs in the capturing lifecycle properly. --- packages/core/src/client.ts | 16 +++- packages/core/src/logs/index.ts | 16 +++- packages/core/src/server-runtime-client.ts | 2 +- packages/core/src/types-hoist/options.ts | 12 +++ packages/core/test/lib/log/index.test.ts | 91 ++++++++++++++++++++++ 5 files changed, 131 insertions(+), 6 deletions(-) diff --git a/packages/core/src/client.ts b/packages/core/src/client.ts index 07b5224e5928..cf7968804429 100644 --- a/packages/core/src/client.ts +++ b/packages/core/src/client.ts @@ -623,12 +623,19 @@ export abstract class Client { public on(hook: 'close', callback: () => void): () => void; /** - * A hook that is called before a log is captured + * A hook that is called before a log is captured. This hooks runs before `beforeSendLog` is fired. * * @returns {() => void} A function that, when executed, removes the registered callback. */ public on(hook: 'beforeCaptureLog', callback: (log: Log) => void): () => void; + /** + * A hook that is called after a log is captured + * + * @returns {() => void} A function that, when executed, removes the registered callback. + */ + public on(hook: 'afterCaptureLog', callback: (log: Log) => void): () => void; + /** * Register a hook on this client. */ @@ -777,10 +784,15 @@ export abstract class Client { public emit(hook: 'close'): void; /** - * Emit a hook event for client before capturing a log + * Emit a hook event for client before capturing a log. This hooks runs before `beforeSendLog` is fired. */ public emit(hook: 'beforeCaptureLog', log: Log): void; + /** + * Emit a hook event for client after capturing a log. + */ + public emit(hook: 'afterCaptureLog', log: Log): void; + /** * Emit a hook that was previously registered via `on()`. */ diff --git a/packages/core/src/logs/index.ts b/packages/core/src/logs/index.ts index 56de1b0bdc15..20b654e39532 100644 --- a/packages/core/src/logs/index.ts +++ b/packages/core/src/logs/index.ts @@ -62,18 +62,28 @@ export function logAttributeToSerializedLogAttribute(key: string, value: unknown * @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. */ -export function _INTERNAL_captureLog(log: Log, client = getClient(), scope = getCurrentScope()): void { +export function _INTERNAL_captureLog(beforeLog: Log, client = getClient(), scope = getCurrentScope()): void { if (!client) { DEBUG_BUILD && logger.warn('No client available to capture log.'); return; } const { _experiments, release, environment } = client.getOptions(); - if (!_experiments?.enableLogs) { + const { enableLogs = false, beforeSendLog } = _experiments ?? {}; + if (!enableLogs) { DEBUG_BUILD && logger.warn('logging option not enabled, log will not be captured.'); return; } + client.emit('beforeCaptureLog', beforeLog); + + const log = beforeSendLog ? beforeSendLog(beforeLog) : beforeLog; + if (!log) { + client.recordDroppedEvent('before_send', 'log_item', 1); + DEBUG_BUILD && logger.warn('beforeSendLog returned null, log will not be captured.'); + return; + } + const [, traceContext] = _getTraceInfoFromScope(client, scope); const { level, message, attributes, severityNumber } = log; @@ -117,7 +127,7 @@ export function _INTERNAL_captureLog(log: Log, client = getClient(), scope = get } } - client.emit('beforeCaptureLog', log); + client.emit('afterCaptureLog', log); } /** diff --git a/packages/core/src/server-runtime-client.ts b/packages/core/src/server-runtime-client.ts index 9f160d7ee25e..16af7b57f7c4 100644 --- a/packages/core/src/server-runtime-client.ts +++ b/packages/core/src/server-runtime-client.ts @@ -57,7 +57,7 @@ export class ServerRuntimeClient< _INTERNAL_flushLogsBuffer(client); }); - this.on('beforeCaptureLog', log => { + this.on('afterCaptureLog', log => { client._logWeight += estimateLogSizeInBytes(log); // We flush the logs buffer if it exceeds 0.8 MB diff --git a/packages/core/src/types-hoist/options.ts b/packages/core/src/types-hoist/options.ts index d0474b959fa9..51ccc7cdab79 100644 --- a/packages/core/src/types-hoist/options.ts +++ b/packages/core/src/types-hoist/options.ts @@ -2,6 +2,7 @@ import type { CaptureContext } from '../scope'; import type { Breadcrumb, BreadcrumbHint } from './breadcrumb'; import type { ErrorEvent, EventHint, TransactionEvent } from './event'; import type { Integration } from './integration'; +import type { Log } from './log'; import type { TracesSamplerSamplingContext } from './samplingcontext'; import type { SdkMetadata } from './sdkmetadata'; import type { SpanJSON } from './span'; @@ -188,6 +189,17 @@ export interface ClientOptions Log | null; }; /** diff --git a/packages/core/test/lib/log/index.test.ts b/packages/core/test/lib/log/index.test.ts index ab7cfe9bb4b4..22e76a99e528 100644 --- a/packages/core/test/lib/log/index.test.ts +++ b/packages/core/test/lib/log/index.test.ts @@ -8,6 +8,7 @@ import { import { TestClient, getDefaultTestClientOptions } from '../../mocks/client'; import * as loggerModule from '../../../src/utils-hoist/logger'; import { Scope } from '../../../src'; +import type { Log } from '../../../src/types-hoist/log'; const PUBLIC_DSN = 'https://username@domain/123'; @@ -187,4 +188,94 @@ describe('_INTERNAL_captureLog', () => { _INTERNAL_flushLogsBuffer(client); expect(mockSendEnvelope).not.toHaveBeenCalled(); }); + + it('processes logs through beforeSendLog when provided', () => { + const beforeSendLog = vi.fn().mockImplementation(log => ({ + ...log, + message: `Modified: ${log.message}`, + attributes: { ...log.attributes, processed: true }, + })); + + const options = getDefaultTestClientOptions({ + dsn: PUBLIC_DSN, + _experiments: { enableLogs: true, beforeSendLog }, + }); + const client = new TestClient(options); + + _INTERNAL_captureLog( + { + level: 'info', + message: 'original message', + attributes: { original: true }, + }, + client, + undefined, + ); + + expect(beforeSendLog).toHaveBeenCalledWith({ + level: 'info', + message: 'original message', + attributes: { original: true }, + }); + + const logBuffer = _INTERNAL_getLogBuffer(client); + expect(logBuffer).toBeDefined(); + expect(logBuffer?.[0]).toEqual( + expect.objectContaining({ + body: { + stringValue: 'Modified: original message', + }, + attributes: expect.arrayContaining([ + expect.objectContaining({ key: 'processed', value: { boolValue: true } }), + expect.objectContaining({ key: 'original', value: { boolValue: true } }), + ]), + }), + ); + }); + + it('drops logs when beforeSendLog returns null', () => { + const beforeSendLog = vi.fn().mockReturnValue(null); + const recordDroppedEventSpy = vi.spyOn(TestClient.prototype, 'recordDroppedEvent'); + const loggerWarnSpy = vi.spyOn(loggerModule.logger, 'warn').mockImplementation(() => undefined); + + const options = getDefaultTestClientOptions({ + dsn: PUBLIC_DSN, + _experiments: { enableLogs: true, beforeSendLog }, + }); + const client = new TestClient(options); + + _INTERNAL_captureLog( + { + level: 'info', + message: 'test message', + }, + client, + undefined, + ); + + expect(beforeSendLog).toHaveBeenCalled(); + expect(recordDroppedEventSpy).toHaveBeenCalledWith('before_send', 'log_item', 1); + expect(loggerWarnSpy).toHaveBeenCalledWith('beforeSendLog returned null, log will not be captured.'); + expect(_INTERNAL_getLogBuffer(client)).toBeUndefined(); + + recordDroppedEventSpy.mockRestore(); + loggerWarnSpy.mockRestore(); + }); + + it('emits beforeCaptureLog and afterCaptureLog events', () => { + const beforeCaptureLogSpy = vi.spyOn(TestClient.prototype, 'emit'); + const options = getDefaultTestClientOptions({ dsn: PUBLIC_DSN, _experiments: { enableLogs: true } }); + const client = new TestClient(options); + + const log: Log = { + level: 'info', + message: 'test message', + }; + + _INTERNAL_captureLog(log, client, undefined); + + expect(beforeCaptureLogSpy).toHaveBeenCalledWith('beforeCaptureLog', log); + expect(beforeCaptureLogSpy).toHaveBeenCalledWith('afterCaptureLog', log); + beforeCaptureLogSpy.mockRestore(); + }); }); From d6502182462ff8f96761e54df82c2529eb6e9a05 Mon Sep 17 00:00:00 2001 From: Abhijeet Prasad Date: Tue, 25 Mar 2025 10:12:48 -0400 Subject: [PATCH 15/28] feat(core): Add support for parameterizing logs (#15812) ref https://github.com/getsentry/sentry-javascript/issues/15526 This adds support for parameterizing logs via the existing `ParameterizedString` type and `parameterize` function exported from the SDK. This works for all usages of the logger, so browser and Node.js. Usage: ```js Sentry.logger.info(Sentry.logger.fmt`User ${user} updated profile picture`, { userId: 'user-123', imageSize: '2.5MB', timestamp: Date.now() }); ``` `fmt` is an alias to `Sentry.parameterize` that is exported from the `logger` namespace. To support this change, I changed the typing of `ParameterizedString` to accept `unknown[]` for `__sentry_template_values__`. This is broadening the type, so should not be a breaking change. [`logentry.params`](https://github.com/getsentry/relay/blob/a91f0c92860f88789ad6092ef5b1062aa3e34b80/relay-event-schema/src/protocol/logentry.rs#L51C27-L51C32) should accept all kinds of values, relay handles formatting them correctly. --- packages/browser/src/log.ts | 152 +++++++++++++++--- packages/browser/test/log.test.ts | 33 ++++ packages/core/src/index.ts | 2 +- packages/core/src/logs/index.ts | 10 +- packages/core/src/types-hoist/event.ts | 2 +- packages/core/src/types-hoist/log.ts | 4 +- packages/core/src/types-hoist/parameterize.ts | 2 +- packages/core/src/utils/parameterize.ts | 15 +- .../test/lib/{log => logs}/envelope.test.ts | 0 .../core/test/lib/{log => logs}/index.test.ts | 30 +++- packages/node/src/log.ts | 6 +- packages/node/test/log.test.ts | 25 +++ 12 files changed, 248 insertions(+), 33 deletions(-) rename packages/core/test/lib/{log => logs}/envelope.test.ts (100%) rename packages/core/test/lib/{log => logs}/index.test.ts (90%) diff --git a/packages/browser/src/log.ts b/packages/browser/src/log.ts index 4ae97fd27a94..69f8313a3378 100644 --- a/packages/browser/src/log.ts +++ b/packages/browser/src/log.ts @@ -1,4 +1,4 @@ -import type { LogSeverityLevel, Log, Client } from '@sentry/core'; +import type { LogSeverityLevel, Log, Client, ParameterizedString } from '@sentry/core'; import { getClient, _INTERNAL_captureLog, _INTERNAL_flushLogsBuffer } from '@sentry/core'; import { WINDOW } from './helpers'; @@ -59,7 +59,7 @@ function addFlushingListeners(client: Client): void { */ function captureLog( level: LogSeverityLevel, - message: string, + message: ParameterizedString, attributes?: Log['attributes'], severityNumber?: Log['severityNumber'], ): void { @@ -77,15 +77,28 @@ function captureLog( * @summary Capture a log with the `trace` level. Requires `_experiments.enableLogs` to be enabled. * * @param message - The message to log. - * @param attributes - Arbitrary structured data that stores information about the log - e.g., userId: 100. + * @param attributes - Arbitrary structured data that stores information about the log - e.g., { userId: 100, route: '/dashboard' }. * * @example * * ``` - * Sentry.logger.trace('Hello world', { userId: 100 }); + * Sentry.logger.trace('User clicked submit button', { + * buttonId: 'submit-form', + * formId: 'user-profile', + * timestamp: Date.now() + * }); + * ``` + * + * @example With template strings + * + * ``` + * Sentry.logger.trace(Sentry.logger.fmt`User ${user} navigated to ${page}`, { + * userId: '123', + * sessionId: 'abc-xyz' + * }); * ``` */ -export function trace(message: string, attributes?: Log['attributes']): void { +export function trace(message: ParameterizedString, attributes?: Log['attributes']): void { captureLog('trace', message, attributes); } @@ -93,15 +106,29 @@ export function trace(message: string, attributes?: Log['attributes']): void { * @summary Capture a log with the `debug` level. Requires `_experiments.enableLogs` to be enabled. * * @param message - The message to log. - * @param attributes - Arbitrary structured data that stores information about the log - e.g., userId: 100. + * @param attributes - Arbitrary structured data that stores information about the log - e.g., { component: 'Header', state: 'loading' }. * * @example * * ``` - * Sentry.logger.debug('Hello world', { userId: 100 }); + * Sentry.logger.debug('Component mounted', { + * component: 'UserProfile', + * props: { userId: 123 }, + * renderTime: 150 + * }); + * ``` + * + * @example With template strings + * + * ``` + * Sentry.logger.debug(Sentry.logger.fmt`API request to ${endpoint} failed`, { + * statusCode: 404, + * requestId: 'req-123', + * duration: 250 + * }); * ``` */ -export function debug(message: string, attributes?: Log['attributes']): void { +export function debug(message: ParameterizedString, attributes?: Log['attributes']): void { captureLog('debug', message, attributes); } @@ -109,15 +136,29 @@ export function debug(message: string, attributes?: Log['attributes']): void { * @summary Capture a log with the `info` level. Requires `_experiments.enableLogs` to be enabled. * * @param message - The message to log. - * @param attributes - Arbitrary structured data that stores information about the log - e.g., userId: 100. + * @param attributes - Arbitrary structured data that stores information about the log - e.g., { feature: 'checkout', status: 'completed' }. * * @example * * ``` - * Sentry.logger.info('Hello world', { userId: 100 }); + * Sentry.logger.info('User completed checkout', { + * orderId: 'order-123', + * amount: 99.99, + * paymentMethod: 'credit_card' + * }); + * ``` + * + * @example With template strings + * + * ``` + * Sentry.logger.info(Sentry.logger.fmt`User ${user} updated profile picture`, { + * userId: 'user-123', + * imageSize: '2.5MB', + * timestamp: Date.now() + * }); * ``` */ -export function info(message: string, attributes?: Log['attributes']): void { +export function info(message: ParameterizedString, attributes?: Log['attributes']): void { captureLog('info', message, attributes); } @@ -125,15 +166,30 @@ export function info(message: string, attributes?: Log['attributes']): void { * @summary Capture a log with the `warn` level. Requires `_experiments.enableLogs` to be enabled. * * @param message - The message to log. - * @param attributes - Arbitrary structured data that stores information about the log - e.g., userId: 100. + * @param attributes - Arbitrary structured data that stores information about the log - e.g., { browser: 'Chrome', version: '91.0' }. * * @example * * ``` - * Sentry.logger.warn('Hello world', { userId: 100 }); + * Sentry.logger.warn('Browser compatibility issue detected', { + * browser: 'Safari', + * version: '14.0', + * feature: 'WebRTC', + * fallback: 'enabled' + * }); + * ``` + * + * @example With template strings + * + * ``` + * Sentry.logger.warn(Sentry.logger.fmt`API endpoint ${endpoint} is deprecated`, { + * recommendedEndpoint: '/api/v2/users', + * sunsetDate: '2024-12-31', + * clientVersion: '1.2.3' + * }); * ``` */ -export function warn(message: string, attributes?: Log['attributes']): void { +export function warn(message: ParameterizedString, attributes?: Log['attributes']): void { captureLog('warn', message, attributes); } @@ -141,15 +197,31 @@ export function warn(message: string, attributes?: Log['attributes']): void { * @summary Capture a log with the `error` level. Requires `_experiments.enableLogs` to be enabled. * * @param message - The message to log. - * @param attributes - Arbitrary structured data that stores information about the log - e.g., userId: 100. + * @param attributes - Arbitrary structured data that stores information about the log - e.g., { error: 'NetworkError', url: '/api/data' }. * * @example * * ``` - * Sentry.logger.error('Hello world', { userId: 100 }); + * Sentry.logger.error('Failed to load user data', { + * error: 'NetworkError', + * url: '/api/users/123', + * statusCode: 500, + * retryCount: 3 + * }); + * ``` + * + * @example With template strings + * + * ``` + * Sentry.logger.error(Sentry.logger.fmt`Payment processing failed for order ${orderId}`, { + * error: 'InsufficientFunds', + * amount: 100.00, + * currency: 'USD', + * userId: 'user-456' + * }); * ``` */ -export function error(message: string, attributes?: Log['attributes']): void { +export function error(message: ParameterizedString, attributes?: Log['attributes']): void { captureLog('error', message, attributes); } @@ -157,15 +229,31 @@ export function error(message: string, attributes?: Log['attributes']): void { * @summary Capture a log with the `fatal` level. Requires `_experiments.enableLogs` to be enabled. * * @param message - The message to log. - * @param attributes - Arbitrary structured data that stores information about the log - e.g., userId: 100. + * @param attributes - Arbitrary structured data that stores information about the log - e.g., { appState: 'corrupted', sessionId: 'abc-123' }. * * @example * * ``` - * Sentry.logger.fatal('Hello world', { userId: 100 }); + * Sentry.logger.fatal('Application state corrupted', { + * lastKnownState: 'authenticated', + * sessionId: 'session-123', + * timestamp: Date.now(), + * recoveryAttempted: true + * }); + * ``` + * + * @example With template strings + * + * ``` + * Sentry.logger.fatal(Sentry.logger.fmt`Critical system failure in ${service}`, { + * service: 'payment-processor', + * errorCode: 'CRITICAL_FAILURE', + * affectedUsers: 150, + * timestamp: Date.now() + * }); * ``` */ -export function fatal(message: string, attributes?: Log['attributes']): void { +export function fatal(message: ParameterizedString, attributes?: Log['attributes']): void { captureLog('fatal', message, attributes); } @@ -173,14 +261,32 @@ export function fatal(message: string, attributes?: Log['attributes']): void { * @summary Capture a log with the `critical` level. Requires `_experiments.enableLogs` to be enabled. * * @param message - The message to log. - * @param attributes - Arbitrary structured data that stores information about the log - e.g., userId: 100. + * @param attributes - Arbitrary structured data that stores information about the log - e.g., { security: 'breach', severity: 'high' }. * * @example * * ``` - * Sentry.logger.critical('Hello world', { userId: 100 }); + * Sentry.logger.critical('Security breach detected', { + * type: 'unauthorized_access', + * user: '132123', + * endpoint: '/api/admin', + * timestamp: Date.now() + * }); + * ``` + * + * @example With template strings + * + * ``` + * Sentry.logger.critical(Sentry.logger.fmt`Multiple failed login attempts from user ${user}`, { + * attempts: 10, + * timeWindow: '5m', + * blocked: true, + * timestamp: Date.now() + * }); * ``` */ -export function critical(message: string, attributes?: Log['attributes']): void { +export function critical(message: ParameterizedString, attributes?: Log['attributes']): void { captureLog('critical', message, attributes); } + +export { fmt } from '@sentry/core'; diff --git a/packages/browser/test/log.test.ts b/packages/browser/test/log.test.ts index 582cc3b45d20..9dec17fe01e0 100644 --- a/packages/browser/test/log.test.ts +++ b/packages/browser/test/log.test.ts @@ -196,5 +196,38 @@ describe('Logger', () => { vi.advanceTimersByTime(2000); expect(mockFlushLogsBuffer).toHaveBeenCalledTimes(1); }); + + it('should handle parameterized strings with parameters', () => { + logger.info(logger.fmt`Hello ${'John'}, your balance is ${100}`, { userId: 123 }); + expect(mockCaptureLog).toHaveBeenCalledWith( + { + level: 'info', + message: expect.objectContaining({ + __sentry_template_string__: 'Hello %s, your balance is %s', + __sentry_template_values__: ['John', 100], + }), + attributes: { + userId: 123, + }, + }, + expect.any(Object), + undefined, + ); + }); + + it('should handle parameterized strings without additional attributes', () => { + logger.debug(logger.fmt`User ${'Alice'} logged in from ${'mobile'}`); + expect(mockCaptureLog).toHaveBeenCalledWith( + { + level: 'debug', + message: expect.objectContaining({ + __sentry_template_string__: 'User %s logged in from %s', + __sentry_template_values__: ['Alice', 'mobile'], + }), + }, + expect.any(Object), + undefined, + ); + }); }); }); diff --git a/packages/core/src/index.ts b/packages/core/src/index.ts index 292a9d5d9f6d..800df99e1c0e 100644 --- a/packages/core/src/index.ts +++ b/packages/core/src/index.ts @@ -66,7 +66,7 @@ export { hasTracingEnabled } from './utils/hasSpansEnabled'; export { hasSpansEnabled } from './utils/hasSpansEnabled'; export { isSentryRequestUrl } from './utils/isSentryRequestUrl'; export { handleCallbackErrors } from './utils/handleCallbackErrors'; -export { parameterize } from './utils/parameterize'; +export { parameterize, fmt } from './utils/parameterize'; export { addAutoIpAddressToSession, addAutoIpAddressToUser } from './utils/ipAddress'; export { convertSpanLinksForEnvelope, diff --git a/packages/core/src/logs/index.ts b/packages/core/src/logs/index.ts index 20b654e39532..fbe1b40493c3 100644 --- a/packages/core/src/logs/index.ts +++ b/packages/core/src/logs/index.ts @@ -5,7 +5,7 @@ import { DEBUG_BUILD } from '../debug-build'; import { SEVERITY_TEXT_TO_SEVERITY_NUMBER } from './constants'; import type { SerializedLogAttribute, SerializedOtelLog } from '../types-hoist'; import type { Log } from '../types-hoist/log'; -import { logger } from '../utils-hoist'; +import { isParameterizedString, logger } from '../utils-hoist'; import { _getSpanForScope } from '../utils/spanOnScope'; import { createOtelLogEnvelope } from './envelope'; @@ -100,6 +100,14 @@ export function _INTERNAL_captureLog(beforeLog: Log, client = getClient(), scope logAttributes.environment = environment; } + if (isParameterizedString(message)) { + const { __sentry_template_string__, __sentry_template_values__ = [] } = message; + logAttributes['sentry.message.template'] = __sentry_template_string__; + __sentry_template_values__.forEach((param, index) => { + logAttributes[`sentry.message.param.${index}`] = param; + }); + } + const span = _getSpanForScope(scope); if (span) { // Add the parent span ID to the log attributes for trace context diff --git a/packages/core/src/types-hoist/event.ts b/packages/core/src/types-hoist/event.ts index 5b4d87337236..a042edad2cda 100644 --- a/packages/core/src/types-hoist/event.ts +++ b/packages/core/src/types-hoist/event.ts @@ -22,7 +22,7 @@ export interface Event { message?: string; logentry?: { message?: string; - params?: string[]; + params?: unknown[]; }; timestamp?: number; start_timestamp?: number; diff --git a/packages/core/src/types-hoist/log.ts b/packages/core/src/types-hoist/log.ts index 45172c44adc0..844c9223dbfd 100644 --- a/packages/core/src/types-hoist/log.ts +++ b/packages/core/src/types-hoist/log.ts @@ -1,3 +1,5 @@ +import type { ParameterizedString } from './parameterize'; + export type LogSeverityLevel = 'trace' | 'debug' | 'info' | 'warn' | 'error' | 'fatal' | 'critical'; export type SerializedLogAttributeValueType = @@ -36,7 +38,7 @@ export interface Log { /** * The message to be logged - for example, 'hello world' would become a log like '[INFO] hello world' */ - message: string; + message: ParameterizedString; /** * Arbitrary structured data that stores information about the log - e.g., userId: 100. diff --git a/packages/core/src/types-hoist/parameterize.ts b/packages/core/src/types-hoist/parameterize.ts index a94daa3684db..8b62bfd483e9 100644 --- a/packages/core/src/types-hoist/parameterize.ts +++ b/packages/core/src/types-hoist/parameterize.ts @@ -1,4 +1,4 @@ export type ParameterizedString = string & { __sentry_template_string__?: string; - __sentry_template_values__?: string[]; + __sentry_template_values__?: unknown[]; }; diff --git a/packages/core/src/utils/parameterize.ts b/packages/core/src/utils/parameterize.ts index 392f4b70b444..2ada1f4ec2d9 100644 --- a/packages/core/src/utils/parameterize.ts +++ b/packages/core/src/utils/parameterize.ts @@ -5,13 +5,24 @@ import type { ParameterizedString } from '../types-hoist'; * For example: parameterize`This is a log statement with ${x} and ${y} params`, would return: * "__sentry_template_string__": 'This is a log statement with %s and %s params', * "__sentry_template_values__": ['first', 'second'] + * * @param strings An array of string values splitted between expressions * @param values Expressions extracted from template string - * @returns String with template information in __sentry_template_string__ and __sentry_template_values__ properties + * + * @returns A `ParameterizedString` object that can be passed into `captureMessage` or Sentry.logger.X methods. */ -export function parameterize(strings: TemplateStringsArray, ...values: string[]): ParameterizedString { +export function parameterize(strings: TemplateStringsArray, ...values: unknown[]): ParameterizedString { const formatted = new String(String.raw(strings, ...values)) as ParameterizedString; formatted.__sentry_template_string__ = strings.join('\x00').replace(/%/g, '%%').replace(/\0/g, '%s'); formatted.__sentry_template_values__ = values; return formatted; } + +/** + * Tagged template function which returns parameterized representation of the message. + * + * @param strings An array of string values splitted between expressions + * @param values Expressions extracted from template string + * @returns A `ParameterizedString` object that can be passed into `captureMessage` or Sentry.logger.X methods. + */ +export const fmt = parameterize; diff --git a/packages/core/test/lib/log/envelope.test.ts b/packages/core/test/lib/logs/envelope.test.ts similarity index 100% rename from packages/core/test/lib/log/envelope.test.ts rename to packages/core/test/lib/logs/envelope.test.ts diff --git a/packages/core/test/lib/log/index.test.ts b/packages/core/test/lib/logs/index.test.ts similarity index 90% rename from packages/core/test/lib/log/index.test.ts rename to packages/core/test/lib/logs/index.test.ts index 22e76a99e528..e2b7eba781d2 100644 --- a/packages/core/test/lib/log/index.test.ts +++ b/packages/core/test/lib/logs/index.test.ts @@ -7,7 +7,7 @@ import { } from '../../../src/logs'; import { TestClient, getDefaultTestClientOptions } from '../../mocks/client'; import * as loggerModule from '../../../src/utils-hoist/logger'; -import { Scope } from '../../../src'; +import { Scope, fmt } from '../../../src'; import type { Log } from '../../../src/types-hoist/log'; const PUBLIC_DSN = 'https://username@domain/123'; @@ -117,6 +117,7 @@ describe('_INTERNAL_captureLog', () => { expect(_INTERNAL_getLogBuffer(client)?.[0]).toEqual( expect.objectContaining({ traceId: '3d9355f71e9c444b81161599adac6e29', + severityNumber: 17, // error level maps to 17 }), ); }); @@ -189,6 +190,33 @@ describe('_INTERNAL_captureLog', () => { expect(mockSendEnvelope).not.toHaveBeenCalled(); }); + it('handles parameterized strings correctly', () => { + const options = getDefaultTestClientOptions({ dsn: PUBLIC_DSN, _experiments: { enableLogs: true } }); + const client = new TestClient(options); + + const parameterizedMessage = fmt`Hello ${'John'}, welcome to ${'Sentry'}`; + + _INTERNAL_captureLog({ level: 'info', message: parameterizedMessage }, client, undefined); + + const logAttributes = _INTERNAL_getLogBuffer(client)?.[0]?.attributes; + expect(logAttributes).toEqual( + expect.arrayContaining([ + expect.objectContaining({ + key: 'sentry.message.template', + value: { stringValue: 'Hello %s, welcome to %s' }, + }), + expect.objectContaining({ + key: 'sentry.message.param.0', + value: { stringValue: 'John' }, + }), + expect.objectContaining({ + key: 'sentry.message.param.1', + value: { stringValue: 'Sentry' }, + }), + ]), + ); + }); + it('processes logs through beforeSendLog when provided', () => { const beforeSendLog = vi.fn().mockImplementation(log => ({ ...log, diff --git a/packages/node/src/log.ts b/packages/node/src/log.ts index 9bad4895ceb6..be5fc934351c 100644 --- a/packages/node/src/log.ts +++ b/packages/node/src/log.ts @@ -1,10 +1,10 @@ import { format } from 'node:util'; -import type { LogSeverityLevel, Log } from '@sentry/core'; +import type { LogSeverityLevel, Log, ParameterizedString } from '@sentry/core'; import { _INTERNAL_captureLog } from '@sentry/core'; type CaptureLogArgs = - | [message: string, attributes?: Log['attributes']] + | [message: ParameterizedString, attributes?: Log['attributes']] | [messageTemplate: string, messageParams: Array, attributes?: Log['attributes']]; /** @@ -221,3 +221,5 @@ export function fatal(...args: CaptureLogArgs): void { export function critical(...args: CaptureLogArgs): void { captureLog('critical', ...args); } + +export { fmt } from '@sentry/core'; diff --git a/packages/node/test/log.test.ts b/packages/node/test/log.test.ts index 4064d7c1f3f1..150e040f5ec5 100644 --- a/packages/node/test/log.test.ts +++ b/packages/node/test/log.test.ts @@ -126,5 +126,30 @@ describe('Node Logger', () => { }, }); }); + + it('should handle parameterized strings with parameters', () => { + nodeLogger.info(nodeLogger.fmt`Hello ${'John'}, your balance is ${100}`, { userId: 123 }); + expect(mockCaptureLog).toHaveBeenCalledWith({ + level: 'info', + message: expect.objectContaining({ + __sentry_template_string__: 'Hello %s, your balance is %s', + __sentry_template_values__: ['John', 100], + }), + attributes: { + userId: 123, + }, + }); + }); + + it('should handle parameterized strings without additional attributes', () => { + nodeLogger.debug(nodeLogger.fmt`User ${'Alice'} logged in from ${'mobile'}`); + expect(mockCaptureLog).toHaveBeenCalledWith({ + level: 'debug', + message: expect.objectContaining({ + __sentry_template_string__: 'User %s logged in from %s', + __sentry_template_values__: ['Alice', 'mobile'], + }), + }); + }); }); }); From c276386538f198c7fab3fb4cc9d5f31323e9cbe7 Mon Sep 17 00:00:00 2001 From: Francesco Gringl-Novy Date: Tue, 25 Mar 2025 15:29:54 +0100 Subject: [PATCH 16/28] ref: Avoid using `SentryError` for PromiseBuffer control flow (#15822) This replaces usage of `SentryError` for promise buffer control flow. Instead, we can use a symbol and just check this directly, we do not even care about the message here. ref https://github.com/getsentry/sentry-javascript/issues/15725#issuecomment-2750892929 --- packages/cloudflare/src/transport.ts | 4 ++-- packages/cloudflare/test/transport.test.ts | 9 +++++++-- packages/core/src/transports/base.ts | 5 ++--- packages/core/src/utils-hoist/index.ts | 2 +- packages/core/src/utils-hoist/promisebuffer.ts | 5 +++-- packages/vercel-edge/src/transports/index.ts | 4 ++-- packages/vercel-edge/test/transports/index.test.ts | 9 +++++++-- 7 files changed, 24 insertions(+), 14 deletions(-) diff --git a/packages/cloudflare/src/transport.ts b/packages/cloudflare/src/transport.ts index 40b8549df9cd..108a0e915656 100644 --- a/packages/cloudflare/src/transport.ts +++ b/packages/cloudflare/src/transport.ts @@ -1,5 +1,5 @@ import type { BaseTransportOptions, Transport, TransportMakeRequestResponse, TransportRequest } from '@sentry/core'; -import { SentryError, createTransport, suppressTracing } from '@sentry/core'; +import { SENTRY_BUFFER_FULL_ERROR, createTransport, suppressTracing } from '@sentry/core'; export interface CloudflareTransportOptions extends BaseTransportOptions { /** Fetch API init parameters. */ @@ -38,7 +38,7 @@ export class IsolatedPromiseBuffer { */ public add(taskProducer: () => PromiseLike): PromiseLike { if (this._taskProducers.length >= this._bufferSize) { - return Promise.reject(new SentryError('Not adding Promise because buffer limit was reached.')); + return Promise.reject(SENTRY_BUFFER_FULL_ERROR); } this._taskProducers.push(taskProducer); diff --git a/packages/cloudflare/test/transport.test.ts b/packages/cloudflare/test/transport.test.ts index d3aca638eba1..d02ef18079be 100644 --- a/packages/cloudflare/test/transport.test.ts +++ b/packages/cloudflare/test/transport.test.ts @@ -1,4 +1,4 @@ -import { createEnvelope, serializeEnvelope } from '@sentry/core'; +import { SENTRY_BUFFER_FULL_ERROR, createEnvelope, serializeEnvelope } from '@sentry/core'; import type { EventEnvelope, EventItem } from '@sentry/core'; import { afterAll, describe, expect, it, vi } from 'vitest'; @@ -140,7 +140,12 @@ describe('IsolatedPromiseBuffer', () => { await ipb.add(task2); await ipb.add(task3); - await expect(ipb.add(task4)).rejects.toThrowError('Not adding Promise because buffer limit was reached.'); + try { + await ipb.add(task4); + throw new Error('Should not be called'); + } catch (error) { + expect(error).toBe(SENTRY_BUFFER_FULL_ERROR); + } }); it('should not throw when one of the tasks throws when drained', async () => { diff --git a/packages/core/src/transports/base.ts b/packages/core/src/transports/base.ts index 9296095428cf..2b7de82ba4e1 100644 --- a/packages/core/src/transports/base.ts +++ b/packages/core/src/transports/base.ts @@ -14,9 +14,8 @@ import { forEachEnvelopeItem, serializeEnvelope, } from '../utils-hoist/envelope'; -import { SentryError } from '../utils-hoist/error'; import { logger } from '../utils-hoist/logger'; -import { type PromiseBuffer, makePromiseBuffer } from '../utils-hoist/promisebuffer'; +import { type PromiseBuffer, makePromiseBuffer, SENTRY_BUFFER_FULL_ERROR } from '../utils-hoist/promisebuffer'; import { type RateLimits, isRateLimited, updateRateLimits } from '../utils-hoist/ratelimit'; import { resolvedSyncPromise } from '../utils-hoist/syncpromise'; @@ -85,7 +84,7 @@ export function createTransport( return buffer.add(requestTask).then( result => result, error => { - if (error instanceof SentryError) { + if (error === SENTRY_BUFFER_FULL_ERROR) { DEBUG_BUILD && logger.error('Skipped sending event because buffer is full.'); recordEnvelopeLoss('queue_overflow'); return resolvedSyncPromise({}); diff --git a/packages/core/src/utils-hoist/index.ts b/packages/core/src/utils-hoist/index.ts index 4f22928eff86..88801545f3d5 100644 --- a/packages/core/src/utils-hoist/index.ts +++ b/packages/core/src/utils-hoist/index.ts @@ -53,7 +53,7 @@ export { objectify, } from './object'; export { basename, dirname, isAbsolute, join, normalizePath, relative, resolve } from './path'; -export { makePromiseBuffer } from './promisebuffer'; +export { makePromiseBuffer, SENTRY_BUFFER_FULL_ERROR } from './promisebuffer'; export type { PromiseBuffer } from './promisebuffer'; export { severityLevelFromString } from './severity'; diff --git a/packages/core/src/utils-hoist/promisebuffer.ts b/packages/core/src/utils-hoist/promisebuffer.ts index 32b90e4d8519..d7eb82443fda 100644 --- a/packages/core/src/utils-hoist/promisebuffer.ts +++ b/packages/core/src/utils-hoist/promisebuffer.ts @@ -1,4 +1,3 @@ -import { SentryError } from './error'; import { SyncPromise, rejectedSyncPromise, resolvedSyncPromise } from './syncpromise'; export interface PromiseBuffer { @@ -9,6 +8,8 @@ export interface PromiseBuffer { drain(timeout?: number): PromiseLike; } +export const SENTRY_BUFFER_FULL_ERROR = Symbol.for('SentryBufferFullError'); + /** * Creates an new PromiseBuffer object with the specified limit * @param limit max number of promises that can be stored in the buffer @@ -42,7 +43,7 @@ export function makePromiseBuffer(limit?: number): PromiseBuffer { */ function add(taskProducer: () => PromiseLike): PromiseLike { if (!isReady()) { - return rejectedSyncPromise(new SentryError('Not adding Promise because buffer limit was reached.')); + return rejectedSyncPromise(SENTRY_BUFFER_FULL_ERROR); } // start the task and add its promise to the queue diff --git a/packages/vercel-edge/src/transports/index.ts b/packages/vercel-edge/src/transports/index.ts index e938477b3003..e1ce24c501c7 100644 --- a/packages/vercel-edge/src/transports/index.ts +++ b/packages/vercel-edge/src/transports/index.ts @@ -1,5 +1,5 @@ import type { BaseTransportOptions, Transport, TransportMakeRequestResponse, TransportRequest } from '@sentry/core'; -import { SentryError, createTransport, suppressTracing } from '@sentry/core'; +import { SENTRY_BUFFER_FULL_ERROR, createTransport, suppressTracing } from '@sentry/core'; export interface VercelEdgeTransportOptions extends BaseTransportOptions { /** Fetch API init parameters. */ @@ -38,7 +38,7 @@ export class IsolatedPromiseBuffer { */ public add(taskProducer: () => PromiseLike): PromiseLike { if (this._taskProducers.length >= this._bufferSize) { - return Promise.reject(new SentryError('Not adding Promise because buffer limit was reached.')); + return Promise.reject(SENTRY_BUFFER_FULL_ERROR); } this._taskProducers.push(taskProducer); diff --git a/packages/vercel-edge/test/transports/index.test.ts b/packages/vercel-edge/test/transports/index.test.ts index 4434a177d0f8..a7e6bd342fb5 100644 --- a/packages/vercel-edge/test/transports/index.test.ts +++ b/packages/vercel-edge/test/transports/index.test.ts @@ -1,6 +1,6 @@ import { afterAll, describe, expect, it, vi } from 'vitest'; -import { createEnvelope, serializeEnvelope } from '@sentry/core'; +import { SENTRY_BUFFER_FULL_ERROR, createEnvelope, serializeEnvelope } from '@sentry/core'; import type { EventEnvelope, EventItem } from '@sentry/core'; import type { VercelEdgeTransportOptions } from '../../src/transports'; @@ -139,7 +139,12 @@ describe('IsolatedPromiseBuffer', () => { await ipb.add(task2); await ipb.add(task3); - await expect(ipb.add(task4)).rejects.toThrowError('Not adding Promise because buffer limit was reached.'); + try { + await ipb.add(task4); + throw new Error('Should not be called'); + } catch (error) { + expect(error).toBe(SENTRY_BUFFER_FULL_ERROR); + } }); it('should not throw when one of the tasks throws when drained', async () => { From 7f1087d3fb6a2be0aa7427c49d37dd352dba07da Mon Sep 17 00:00:00 2001 From: Francesco Gringl-Novy Date: Tue, 25 Mar 2025 15:52:52 +0100 Subject: [PATCH 17/28] feat: Stop using `dropUndefinedKeys` (#15796) This removes the last remaining usages of `dropUndefinedKeys` in the SDK. It also deprecates the export from `@sentry/core` for future removal. --- .../server/plugins/customNitroErrorHandler.ts | 28 ++++++++--------- .../tracing/browserTracingIntegration.test.ts | 3 ++ .../src/tracing/dynamicSamplingContext.ts | 8 +++-- packages/core/src/utils-hoist/anr.ts | 5 ++-- packages/core/src/utils-hoist/envelope.ts | 5 ++-- packages/core/src/utils-hoist/index.ts | 1 + packages/core/src/utils-hoist/object.ts | 2 ++ packages/core/src/utils/request.ts | 5 ++-- packages/core/src/utils/spanUtils.ts | 10 +++---- packages/core/src/utils/transactionEvent.ts | 9 ++---- .../tracing/dynamicSamplingContext.test.ts | 4 +++ packages/core/test/utils-hoist/object.test.ts | 2 ++ packages/nuxt/src/runtime/utils.ts | 30 +++++++++---------- packages/replay-internal/src/integration.ts | 4 +-- .../sveltekit/src/vite/sentryVitePlugins.ts | 3 +- 15 files changed, 61 insertions(+), 58 deletions(-) diff --git a/dev-packages/e2e-tests/test-applications/nuxt-3/server/plugins/customNitroErrorHandler.ts b/dev-packages/e2e-tests/test-applications/nuxt-3/server/plugins/customNitroErrorHandler.ts index 2d9258936169..9ca836610f2f 100644 --- a/dev-packages/e2e-tests/test-applications/nuxt-3/server/plugins/customNitroErrorHandler.ts +++ b/dev-packages/e2e-tests/test-applications/nuxt-3/server/plugins/customNitroErrorHandler.ts @@ -1,4 +1,4 @@ -import { Context, GLOBAL_OBJ, dropUndefinedKeys, flush, logger, vercelWaitUntil } from '@sentry/core'; +import { Context, GLOBAL_OBJ, flush, logger, vercelWaitUntil } from '@sentry/core'; import * as SentryNode from '@sentry/node'; import { H3Error } from 'h3'; import type { CapturedErrorContext } from 'nitropack'; @@ -36,24 +36,22 @@ export default defineNitroPlugin(nitroApp => { }); function extractErrorContext(errorContext: CapturedErrorContext): Context { - const structuredContext: Context = { - method: undefined, - path: undefined, - tags: undefined, - }; + const ctx: Context = {}; - if (errorContext) { - if (errorContext.event) { - structuredContext.method = errorContext.event._method || undefined; - structuredContext.path = errorContext.event._path || undefined; - } + if (!errorContext) { + return ctx; + } - if (Array.isArray(errorContext.tags)) { - structuredContext.tags = errorContext.tags || undefined; - } + if (errorContext.event) { + ctx.method = errorContext.event._method; + ctx.path = errorContext.event._path; + } + + if (Array.isArray(errorContext.tags)) { + ctx.tags = errorContext.tags; } - return dropUndefinedKeys(structuredContext); + return ctx; } async function flushIfServerless(): Promise { diff --git a/packages/browser/test/tracing/browserTracingIntegration.test.ts b/packages/browser/test/tracing/browserTracingIntegration.test.ts index ee43935cd531..fe9005d47215 100644 --- a/packages/browser/test/tracing/browserTracingIntegration.test.ts +++ b/packages/browser/test/tracing/browserTracingIntegration.test.ts @@ -728,6 +728,7 @@ describe('browserTracingIntegration', () => { sampled: true, sampleRand: expect.any(Number), dsc: { + release: undefined, environment: 'production', public_key: 'examplePublicKey', sample_rate: '1', @@ -768,6 +769,7 @@ describe('browserTracingIntegration', () => { sampled: false, sampleRand: expect.any(Number), dsc: { + release: undefined, environment: 'production', public_key: 'examplePublicKey', sample_rate: '0', @@ -892,6 +894,7 @@ describe('browserTracingIntegration', () => { expect(dynamicSamplingContext).toBeDefined(); expect(dynamicSamplingContext).toStrictEqual({ + release: undefined, environment: 'production', public_key: 'examplePublicKey', sample_rate: '1', diff --git a/packages/core/src/tracing/dynamicSamplingContext.ts b/packages/core/src/tracing/dynamicSamplingContext.ts index 706684ffecab..12cf4ca11ca6 100644 --- a/packages/core/src/tracing/dynamicSamplingContext.ts +++ b/packages/core/src/tracing/dynamicSamplingContext.ts @@ -8,7 +8,7 @@ import { baggageHeaderToDynamicSamplingContext, dynamicSamplingContextToSentryBaggageHeader, } from '../utils-hoist/baggage'; -import { addNonEnumerableProperty, dropUndefinedKeys } from '../utils-hoist/object'; +import { addNonEnumerableProperty } from '../utils-hoist/object'; import { hasSpansEnabled } from '../utils/hasSpansEnabled'; import { getRootSpan, spanIsSampled, spanToJSON } from '../utils/spanUtils'; import { getCapturedScopesOnSpan } from './utils'; @@ -41,12 +41,14 @@ export function getDynamicSamplingContextFromClient(trace_id: string, client: Cl const { publicKey: public_key } = client.getDsn() || {}; - const dsc = dropUndefinedKeys({ + // Instead of conditionally adding non-undefined values, we add them and then remove them if needed + // otherwise, the order of baggage entries changes, which "breaks" a bunch of tests etc. + const dsc: DynamicSamplingContext = { environment: options.environment || DEFAULT_ENVIRONMENT, release: options.release, public_key, trace_id, - }) satisfies DynamicSamplingContext; + }; client.emit('createDsc', dsc); diff --git a/packages/core/src/utils-hoist/anr.ts b/packages/core/src/utils-hoist/anr.ts index 0602321117bf..84cce09bb030 100644 --- a/packages/core/src/utils-hoist/anr.ts +++ b/packages/core/src/utils-hoist/anr.ts @@ -1,6 +1,5 @@ import type { StackFrame } from '../types-hoist'; import { filenameIsInApp } from './node-stack-trace'; -import { dropUndefinedKeys } from './object'; import { UNKNOWN_FUNCTION } from './stacktrace'; type WatchdogReturn = { @@ -81,12 +80,12 @@ export function callFrameToStackFrame( const colno = frame.location.columnNumber ? frame.location.columnNumber + 1 : undefined; const lineno = frame.location.lineNumber ? frame.location.lineNumber + 1 : undefined; - return dropUndefinedKeys({ + return { filename, module: getModuleFromFilename(filename), function: frame.functionName || UNKNOWN_FUNCTION, colno, lineno, in_app: filename ? filenameIsInApp(filename) : undefined, - }); + }; } diff --git a/packages/core/src/utils-hoist/envelope.ts b/packages/core/src/utils-hoist/envelope.ts index 2593079d5db6..450706fb9c2e 100644 --- a/packages/core/src/utils-hoist/envelope.ts +++ b/packages/core/src/utils-hoist/envelope.ts @@ -18,7 +18,6 @@ import type { import { dsnToString } from './dsn'; import { normalize } from './normalize'; -import { dropUndefinedKeys } from './object'; import { GLOBAL_OBJ } from './worldwide'; /** @@ -196,13 +195,13 @@ export function createAttachmentEnvelopeItem(attachment: Attachment): Attachment const buffer = typeof attachment.data === 'string' ? encodeUTF8(attachment.data) : attachment.data; return [ - dropUndefinedKeys({ + { type: 'attachment', length: buffer.length, filename: attachment.filename, content_type: attachment.contentType, attachment_type: attachment.attachmentType, - }), + }, buffer, ]; } diff --git a/packages/core/src/utils-hoist/index.ts b/packages/core/src/utils-hoist/index.ts index 88801545f3d5..99272de04d05 100644 --- a/packages/core/src/utils-hoist/index.ts +++ b/packages/core/src/utils-hoist/index.ts @@ -45,6 +45,7 @@ export { normalize, normalizeToSize, normalizeUrlToBase } from './normalize'; export { addNonEnumerableProperty, convertToPlainObject, + // eslint-disable-next-line deprecation/deprecation dropUndefinedKeys, extractExceptionKeysForMessage, fill, diff --git a/packages/core/src/utils-hoist/object.ts b/packages/core/src/utils-hoist/object.ts index 7826e960d982..d1a1ba82aa99 100644 --- a/packages/core/src/utils-hoist/object.ts +++ b/packages/core/src/utils-hoist/object.ts @@ -210,6 +210,8 @@ export function extractExceptionKeysForMessage(exception: Record(inputValue: T): T { // This map keeps track of what already visited nodes map to. diff --git a/packages/core/src/utils/request.ts b/packages/core/src/utils/request.ts index 07907d8b9b4f..91a965484d8c 100644 --- a/packages/core/src/utils/request.ts +++ b/packages/core/src/utils/request.ts @@ -1,6 +1,5 @@ import type { PolymorphicRequest, RequestEventData } from '../types-hoist'; import type { WebFetchHeaders, WebFetchRequest } from '../types-hoist/webfetchapi'; -import { dropUndefinedKeys } from '../utils-hoist/object'; /** * Transforms a `Headers` object that implements the `Web Fetch API` (https://developer.mozilla.org/en-US/docs/Web/API/Headers) into a simple key-value dict. @@ -91,14 +90,14 @@ export function httpRequestToRequestData(request: { // This is non-standard, but may be set on e.g. Next.js or Express requests const cookies = (request as PolymorphicRequest).cookies; - return dropUndefinedKeys({ + return { url: absoluteUrl, method: request.method, query_string: extractQueryParamsFromUrl(url), headers: headersToDict(headers), cookies, data, - }); + }; } function getAbsoluteUrl({ diff --git a/packages/core/src/utils/spanUtils.ts b/packages/core/src/utils/spanUtils.ts index 0a46ba600e0c..cbfcde2a2576 100644 --- a/packages/core/src/utils/spanUtils.ts +++ b/packages/core/src/utils/spanUtils.ts @@ -21,7 +21,7 @@ import type { } from '../types-hoist'; import type { SpanLink, SpanLinkJSON } from '../types-hoist/link'; import { consoleSandbox } from '../utils-hoist/logger'; -import { addNonEnumerableProperty, dropUndefinedKeys } from '../utils-hoist/object'; +import { addNonEnumerableProperty } from '../utils-hoist/object'; import { generateSpanId } from '../utils-hoist/propagationContext'; import { timestampInSeconds } from '../utils-hoist/time'; import { generateSentryTraceHeader } from '../utils-hoist/tracing'; @@ -42,7 +42,7 @@ export function spanToTransactionTraceContext(span: Span): TraceContext { const { spanId: span_id, traceId: trace_id } = span.spanContext(); const { data, op, parent_span_id, status, origin, links } = spanToJSON(span); - return dropUndefinedKeys({ + return { parent_span_id, span_id, trace_id, @@ -51,7 +51,7 @@ export function spanToTransactionTraceContext(span: Span): TraceContext { status, origin, links, - }); + }; } /** @@ -67,11 +67,11 @@ export function spanToTraceContext(span: Span): TraceContext { const span_id = isRemote ? scope?.getPropagationContext().propagationSpanId || generateSpanId() : spanId; - return dropUndefinedKeys({ + return { parent_span_id, span_id, trace_id, - }); + }; } /** diff --git a/packages/core/src/utils/transactionEvent.ts b/packages/core/src/utils/transactionEvent.ts index 9ec233b4f078..195c31a66966 100644 --- a/packages/core/src/utils/transactionEvent.ts +++ b/packages/core/src/utils/transactionEvent.ts @@ -1,6 +1,5 @@ import { SEMANTIC_ATTRIBUTE_EXCLUSIVE_TIME, SEMANTIC_ATTRIBUTE_PROFILE_ID } from '../semanticAttributes'; import type { SpanJSON, TransactionEvent } from '../types-hoist'; -import { dropUndefinedKeys } from '../utils-hoist'; /** * Converts a transaction event to a span JSON object. @@ -8,7 +7,7 @@ import { dropUndefinedKeys } from '../utils-hoist'; export function convertTransactionEventToSpanJson(event: TransactionEvent): SpanJSON { const { trace_id, parent_span_id, span_id, status, origin, data, op } = event.contexts?.trace ?? {}; - return dropUndefinedKeys({ + return { data: data ?? {}, description: event.transaction, op, @@ -23,14 +22,14 @@ export function convertTransactionEventToSpanJson(event: TransactionEvent): Span exclusive_time: data?.[SEMANTIC_ATTRIBUTE_EXCLUSIVE_TIME] as number | undefined, measurements: event.measurements, is_segment: true, - }); + }; } /** * Converts a span JSON object to a transaction event. */ export function convertSpanJsonToTransactionEvent(span: SpanJSON): TransactionEvent { - const event: TransactionEvent = { + return { type: 'transaction', timestamp: span.timestamp, start_timestamp: span.start_timestamp, @@ -52,6 +51,4 @@ export function convertSpanJsonToTransactionEvent(span: SpanJSON): TransactionEv }, measurements: span.measurements, }; - - return dropUndefinedKeys(event); } diff --git a/packages/core/test/lib/tracing/dynamicSamplingContext.test.ts b/packages/core/test/lib/tracing/dynamicSamplingContext.test.ts index 23406908037c..2b7a030734bb 100644 --- a/packages/core/test/lib/tracing/dynamicSamplingContext.test.ts +++ b/packages/core/test/lib/tracing/dynamicSamplingContext.test.ts @@ -70,6 +70,7 @@ describe('getDynamicSamplingContextFromSpan', () => { const dynamicSamplingContext = getDynamicSamplingContextFromSpan(rootSpan); expect(dynamicSamplingContext).toStrictEqual({ + public_key: undefined, release: '1.0.1', environment: 'production', sampled: 'true', @@ -88,6 +89,7 @@ describe('getDynamicSamplingContextFromSpan', () => { const dynamicSamplingContext = getDynamicSamplingContextFromSpan(rootSpan); expect(dynamicSamplingContext).toStrictEqual({ + public_key: undefined, release: '1.0.1', environment: 'production', sampled: 'true', @@ -111,6 +113,7 @@ describe('getDynamicSamplingContextFromSpan', () => { const dynamicSamplingContext = getDynamicSamplingContextFromSpan(rootSpan); expect(dynamicSamplingContext).toStrictEqual({ + public_key: undefined, release: '1.0.1', environment: 'production', sampled: 'true', @@ -166,6 +169,7 @@ describe('getDynamicSamplingContextFromSpan', () => { const dynamicSamplingContext = getDynamicSamplingContextFromSpan(rootSpan); expect(dynamicSamplingContext).toStrictEqual({ + public_key: undefined, release: '1.0.1', environment: 'production', trace_id: expect.stringMatching(/^[a-f0-9]{32}$/), diff --git a/packages/core/test/utils-hoist/object.test.ts b/packages/core/test/utils-hoist/object.test.ts index e1c32f163193..a563c1298ccb 100644 --- a/packages/core/test/utils-hoist/object.test.ts +++ b/packages/core/test/utils-hoist/object.test.ts @@ -169,6 +169,7 @@ describe('extractExceptionKeysForMessage()', () => { }); }); +/* eslint-disable deprecation/deprecation */ describe('dropUndefinedKeys()', () => { test('simple case', () => { expect( @@ -314,6 +315,7 @@ describe('dropUndefinedKeys()', () => { expect(droppedChicken.lays[0] === droppedChicken).toBe(true); }); }); +/* eslint-enable deprecation/deprecation */ describe('objectify()', () => { describe('stringifies nullish values', () => { diff --git a/packages/nuxt/src/runtime/utils.ts b/packages/nuxt/src/runtime/utils.ts index 23bac2486b74..c6eb59807764 100644 --- a/packages/nuxt/src/runtime/utils.ts +++ b/packages/nuxt/src/runtime/utils.ts @@ -1,5 +1,5 @@ import type { ClientOptions, Context } from '@sentry/core'; -import { captureException, dropUndefinedKeys, getClient, getTraceMetaTags } from '@sentry/core'; +import { captureException, getClient, getTraceMetaTags } from '@sentry/core'; import type { VueOptions } from '@sentry/vue/src/types'; import type { CapturedErrorContext } from 'nitropack'; import type { NuxtRenderHTMLContext } from 'nuxt/app'; @@ -9,25 +9,23 @@ import type { ComponentPublicInstance } from 'vue'; * Extracts the relevant context information from the error context (H3Event in Nitro Error) * and created a structured context object. */ -export function extractErrorContext(errorContext: CapturedErrorContext): Context { - const structuredContext: Context = { - method: undefined, - path: undefined, - tags: undefined, - }; +export function extractErrorContext(errorContext: CapturedErrorContext | undefined): Context { + const ctx: Context = {}; - if (errorContext) { - if (errorContext.event) { - structuredContext.method = errorContext.event._method || undefined; - structuredContext.path = errorContext.event._path || undefined; - } + if (!errorContext) { + return ctx; + } - if (Array.isArray(errorContext.tags)) { - structuredContext.tags = errorContext.tags || undefined; - } + if (errorContext.event) { + ctx.method = errorContext.event._method; + ctx.path = errorContext.event._path; + } + + if (Array.isArray(errorContext.tags)) { + ctx.tags = errorContext.tags; } - return dropUndefinedKeys(structuredContext); + return ctx; } /** diff --git a/packages/replay-internal/src/integration.ts b/packages/replay-internal/src/integration.ts index 4ec1a357eac4..bc916ba591a8 100644 --- a/packages/replay-internal/src/integration.ts +++ b/packages/replay-internal/src/integration.ts @@ -1,5 +1,5 @@ import type { BrowserClientReplayOptions, Client, Integration, IntegrationFn, ReplayRecordingMode } from '@sentry/core'; -import { consoleSandbox, dropUndefinedKeys, isBrowser, parseSampleRate } from '@sentry/core'; +import { consoleSandbox, isBrowser, parseSampleRate } from '@sentry/core'; import { DEFAULT_FLUSH_MAX_DELAY, DEFAULT_FLUSH_MIN_DELAY, @@ -356,7 +356,7 @@ function loadReplayOptionsFromClient(initialOptions: InitialReplayPluginOptions, const finalOptions: ReplayPluginOptions = { sessionSampleRate: 0, errorSampleRate: 0, - ...dropUndefinedKeys(initialOptions), + ...initialOptions, }; const replaysSessionSampleRate = parseSampleRate(opt.replaysSessionSampleRate); diff --git a/packages/sveltekit/src/vite/sentryVitePlugins.ts b/packages/sveltekit/src/vite/sentryVitePlugins.ts index 60258f653400..4444ba9a6ab7 100644 --- a/packages/sveltekit/src/vite/sentryVitePlugins.ts +++ b/packages/sveltekit/src/vite/sentryVitePlugins.ts @@ -1,4 +1,3 @@ -import { dropUndefinedKeys } from '@sentry/core'; import type { Plugin } from 'vite'; import type { AutoInstrumentSelection } from './autoInstrument'; import { makeAutoInstrumentationPlugin } from './autoInstrument'; @@ -104,5 +103,5 @@ export function generateVitePluginOptions( } } - return dropUndefinedKeys(sentryVitePluginsOptions); + return sentryVitePluginsOptions; } From c14ab92c54fad2a408133d2e5dc8c23a0081717c Mon Sep 17 00:00:00 2001 From: Francesco Gringl-Novy Date: Tue, 25 Mar 2025 16:56:57 +0100 Subject: [PATCH 18/28] ref(core): Avoid using `SentryError` for event processing control flow (#15823) This PR replaces our usage of `SentryError` for control flow in our event processing. Instead, we now throw either an `InternalError` or `DoNotSendEventError` (which are just POJOs with a symbol, without a stackframe). These two also allow us to differentiate between these two use cases, which so far have been kind of combined via the log level, but are really different things - one is "expected"/configured by a user, the other is unexpected and more of a warning. I also removed the handling for `SentryError` from inbound filters, as this should then become unused. This also deprecates `SentryError`, it is no longer used and we can eventually remove it. ref https://github.com/getsentry/sentry-javascript/issues/15725#issuecomment-2750892929 --- packages/core/src/client.ts | 59 +++++++++++++++---- .../core/src/integrations/eventFilters.ts | 17 ------ packages/core/src/utils-hoist/error.ts | 5 +- packages/core/src/utils-hoist/index.ts | 1 + packages/core/test/lib/client.test.ts | 23 +++----- .../lib/integrations/eventFilters.test.ts | 27 --------- packages/core/test/utils-hoist/is.test.ts | 11 ---- .../test/utils/mapStatus.test.ts | 2 +- 8 files changed, 60 insertions(+), 85 deletions(-) diff --git a/packages/core/src/client.ts b/packages/core/src/client.ts index cf7968804429..6deea6ac38ce 100644 --- a/packages/core/src/client.ts +++ b/packages/core/src/client.ts @@ -53,7 +53,6 @@ import { import { createClientReportEnvelope } from './utils-hoist/clientreport'; import { dsnToString, makeDsn } from './utils-hoist/dsn'; import { addItemToEnvelope, createAttachmentEnvelopeItem } from './utils-hoist/envelope'; -import { SentryError } from './utils-hoist/error'; import { isParameterizedString, isPlainObject, isPrimitive, isThenable } from './utils-hoist/is'; import { logger } from './utils-hoist/logger'; import { checkOrSetAlreadyCaught, uuid4 } from './utils-hoist/misc'; @@ -69,6 +68,41 @@ import { _getSpanForScope } from './utils/spanOnScope'; const ALREADY_SEEN_ERROR = "Not capturing exception because it's already been captured."; const MISSING_RELEASE_FOR_SESSION_ERROR = 'Discarded session because of missing or non-string release'; +const INTERNAL_ERROR_SYMBOL = Symbol.for('SentryInternalError'); +const DO_NOT_SEND_EVENT_SYMBOL = Symbol.for('SentryDoNotSendEventError'); + +interface InternalError { + message: string; + [INTERNAL_ERROR_SYMBOL]: true; +} + +interface DoNotSendEventError { + message: string; + [DO_NOT_SEND_EVENT_SYMBOL]: true; +} + +function _makeInternalError(message: string): InternalError { + return { + message, + [INTERNAL_ERROR_SYMBOL]: true, + }; +} + +function _makeDoNotSendEventError(message: string): DoNotSendEventError { + return { + message, + [DO_NOT_SEND_EVENT_SYMBOL]: true, + }; +} + +function _isInternalError(error: unknown): error is InternalError { + return !!error && typeof error === 'object' && INTERNAL_ERROR_SYMBOL in error; +} + +function _isDoNotSendEventError(error: unknown): error is DoNotSendEventError { + return !!error && typeof error === 'object' && DO_NOT_SEND_EVENT_SYMBOL in error; +} + /** * Base implementation for all JavaScript SDK clients. * @@ -975,10 +1009,10 @@ export abstract class Client { }, reason => { if (DEBUG_BUILD) { - // If something's gone wrong, log the error as a warning. If it's just us having used a `SentryError` for - // control flow, log just the message (no stack) as a log-level log. - if (reason instanceof SentryError && reason.logLevel === 'log') { + if (_isDoNotSendEventError(reason)) { logger.log(reason.message); + } else if (_isInternalError(reason)) { + logger.warn(reason.message); } else { logger.warn(reason); } @@ -1022,9 +1056,8 @@ export abstract class Client { if (isError && typeof parsedSampleRate === 'number' && Math.random() > parsedSampleRate) { this.recordDroppedEvent('sample_rate', 'error'); return rejectedSyncPromise( - new SentryError( + _makeDoNotSendEventError( `Discarding event because it's not included in the random sample (sampling rate = ${sampleRate})`, - 'log', ), ); } @@ -1035,7 +1068,7 @@ export abstract class Client { .then(prepared => { if (prepared === null) { this.recordDroppedEvent('event_processor', dataCategory); - throw new SentryError('An event processor returned `null`, will not send event.', 'log'); + throw _makeDoNotSendEventError('An event processor returned `null`, will not send event.'); } const isInternalException = hint.data && (hint.data as { __sentry__: boolean }).__sentry__ === true; @@ -1055,7 +1088,7 @@ export abstract class Client { const spanCount = 1 + spans.length; this.recordDroppedEvent('before_send', 'span', spanCount); } - throw new SentryError(`${beforeSendLabel} returned \`null\`, will not send event.`, 'log'); + throw _makeDoNotSendEventError(`${beforeSendLabel} returned \`null\`, will not send event.`); } const session = currentScope.getSession() || isolationScope.getSession(); @@ -1089,7 +1122,7 @@ export abstract class Client { return processedEvent; }) .then(null, reason => { - if (reason instanceof SentryError) { + if (_isDoNotSendEventError(reason) || _isInternalError(reason)) { throw reason; } @@ -1099,7 +1132,7 @@ export abstract class Client { }, originalException: reason, }); - throw new SentryError( + throw _makeInternalError( `Event processing pipeline threw an error, original event will not be sent. Details have been sent as a new event.\nReason: ${reason}`, ); }); @@ -1205,16 +1238,16 @@ function _validateBeforeSendResult( return beforeSendResult.then( event => { if (!isPlainObject(event) && event !== null) { - throw new SentryError(invalidValueError); + throw _makeInternalError(invalidValueError); } return event; }, e => { - throw new SentryError(`${beforeSendLabel} rejected with ${e}`); + throw _makeInternalError(`${beforeSendLabel} rejected with ${e}`); }, ); } else if (!isPlainObject(beforeSendResult) && beforeSendResult !== null) { - throw new SentryError(invalidValueError); + throw _makeInternalError(invalidValueError); } return beforeSendResult; } diff --git a/packages/core/src/integrations/eventFilters.ts b/packages/core/src/integrations/eventFilters.ts index 95bce0d758c8..8b571b19fb75 100644 --- a/packages/core/src/integrations/eventFilters.ts +++ b/packages/core/src/integrations/eventFilters.ts @@ -102,19 +102,12 @@ function _mergeOptions( ...(internalOptions.disableErrorDefaults ? [] : DEFAULT_IGNORE_ERRORS), ], ignoreTransactions: [...(internalOptions.ignoreTransactions || []), ...(clientOptions.ignoreTransactions || [])], - ignoreInternal: internalOptions.ignoreInternal !== undefined ? internalOptions.ignoreInternal : true, }; } function _shouldDropEvent(event: Event, options: Partial): boolean { if (!event.type) { // Filter errors - - if (options.ignoreInternal && _isSentryError(event)) { - DEBUG_BUILD && - logger.warn(`Event dropped due to being internal Sentry Error.\nEvent: ${getEventDescription(event)}`); - return true; - } if (_isIgnoredError(event, options.ignoreErrors)) { DEBUG_BUILD && logger.warn( @@ -196,16 +189,6 @@ function _isAllowedUrl(event: Event, allowUrls?: Array): boolea return !url ? true : stringMatchesSomePattern(url, allowUrls); } -function _isSentryError(event: Event): boolean { - try { - // @ts-expect-error can't be a sentry error if undefined - return event.exception.values[0].type === 'SentryError'; - } catch (e) { - // ignore - } - return false; -} - function _getLastValidUrl(frames: StackFrame[] = []): string | null { for (let i = frames.length - 1; i >= 0; i--) { const frame = frames[i]; diff --git a/packages/core/src/utils-hoist/error.ts b/packages/core/src/utils-hoist/error.ts index 5ae28093a8bf..e15a52dcfc83 100644 --- a/packages/core/src/utils-hoist/error.ts +++ b/packages/core/src/utils-hoist/error.ts @@ -1,6 +1,9 @@ import type { ConsoleLevel } from '../types-hoist'; -/** An error emitted by Sentry SDKs and related utilities. */ +/** + * An error emitted by Sentry SDKs and related utilities. + * @deprecated This class is no longer used and will be removed in a future version. Use `Error` instead. + */ export class SentryError extends Error { public logLevel: ConsoleLevel; diff --git a/packages/core/src/utils-hoist/index.ts b/packages/core/src/utils-hoist/index.ts index 99272de04d05..990ad55fcc8e 100644 --- a/packages/core/src/utils-hoist/index.ts +++ b/packages/core/src/utils-hoist/index.ts @@ -2,6 +2,7 @@ export { applyAggregateErrorsToEvent } from './aggregate-errors'; export { getBreadcrumbLogLevelFromHttpStatusCode } from './breadcrumb-log-level'; export { getComponentName, getLocationHref, htmlTreeAsString } from './browser'; export { dsnFromString, dsnToString, makeDsn } from './dsn'; +// eslint-disable-next-line deprecation/deprecation export { SentryError } from './error'; export { GLOBAL_OBJ } from './worldwide'; export type { InternalGlobal } from './worldwide'; diff --git a/packages/core/test/lib/client.test.ts b/packages/core/test/lib/client.test.ts index 9f60226c0de7..03907285f6cb 100644 --- a/packages/core/test/lib/client.test.ts +++ b/packages/core/test/lib/client.test.ts @@ -1,7 +1,6 @@ import { afterEach, beforeEach, describe, expect, it, test, vi } from 'vitest'; import { Scope, - SentryError, SyncPromise, addBreadcrumb, dsnToString, @@ -533,7 +532,7 @@ describe('Client', () => { ); }); - test('it adds a trace context to all events xxx', () => { + test('it adds a trace context to all events', () => { expect.assertions(1); const options = getDefaultTestClientOptions({ dsn: PUBLIC_DSN }); @@ -1206,7 +1205,7 @@ describe('Client', () => { const options = getDefaultTestClientOptions({ dsn: PUBLIC_DSN, beforeSend }); const client = new TestClient(options); const captureExceptionSpy = vi.spyOn(client, 'captureException'); - const loggerWarnSpy = vi.spyOn(loggerModule.logger, 'log'); + const loggerLogSpy = vi.spyOn(loggerModule.logger, 'log'); client.captureEvent({ message: 'hello' }); @@ -1215,7 +1214,7 @@ describe('Client', () => { // This proves that the reason the event didn't send/didn't get set on the test client is not because there was an // error, but because `beforeSend` returned `null` expect(captureExceptionSpy).not.toBeCalled(); - expect(loggerWarnSpy).toBeCalledWith('before send for type `error` returned `null`, will not send event.'); + expect(loggerLogSpy).toBeCalledWith('before send for type `error` returned `null`, will not send event.'); }); test('calls `beforeSendTransaction` and discards the event', () => { @@ -1225,7 +1224,7 @@ describe('Client', () => { const options = getDefaultTestClientOptions({ dsn: PUBLIC_DSN, beforeSendTransaction }); const client = new TestClient(options); const captureExceptionSpy = vi.spyOn(client, 'captureException'); - const loggerWarnSpy = vi.spyOn(loggerModule.logger, 'log'); + const loggerLogSpy = vi.spyOn(loggerModule.logger, 'log'); client.captureEvent({ transaction: '/dogs/are/great', type: 'transaction' }); @@ -1234,7 +1233,7 @@ describe('Client', () => { // This proves that the reason the event didn't send/didn't get set on the test client is not because there was an // error, but because `beforeSendTransaction` returned `null` expect(captureExceptionSpy).not.toBeCalled(); - expect(loggerWarnSpy).toBeCalledWith('before send for type `transaction` returned `null`, will not send event.'); + expect(loggerLogSpy).toBeCalledWith('before send for type `transaction` returned `null`, will not send event.'); }); test('does not discard span and warn when returning null from `beforeSendSpan', () => { @@ -1293,9 +1292,7 @@ describe('Client', () => { expect(beforeSend).toHaveBeenCalled(); expect(TestClient.instance!.event).toBeUndefined(); - expect(loggerWarnSpy).toBeCalledWith( - new SentryError('before send for type `error` must return `null` or a valid event.'), - ); + expect(loggerWarnSpy).toBeCalledWith('before send for type `error` must return `null` or a valid event.'); } }); @@ -1314,9 +1311,7 @@ describe('Client', () => { expect(beforeSendTransaction).toHaveBeenCalled(); expect(TestClient.instance!.event).toBeUndefined(); - expect(loggerWarnSpy).toBeCalledWith( - new SentryError('before send for type `transaction` must return `null` or a valid event.'), - ); + expect(loggerWarnSpy).toBeCalledWith('before send for type `transaction` must return `null` or a valid event.'); } }); @@ -1688,9 +1683,7 @@ describe('Client', () => { originalException: exception, }); expect(loggerWarnSpy).toBeCalledWith( - new SentryError( - `Event processing pipeline threw an error, original event will not be sent. Details have been sent as a new event.\nReason: ${exception}`, - ), + `Event processing pipeline threw an error, original event will not be sent. Details have been sent as a new event.\nReason: ${exception}`, ); }); diff --git a/packages/core/test/lib/integrations/eventFilters.test.ts b/packages/core/test/lib/integrations/eventFilters.test.ts index 51a15f909f5d..924711ce8662 100644 --- a/packages/core/test/lib/integrations/eventFilters.test.ts +++ b/packages/core/test/lib/integrations/eventFilters.test.ts @@ -311,17 +311,6 @@ const EVENT_WITH_VALUE: Event = { }, }; -const SENTRY_EVENT: Event = { - exception: { - values: [ - { - type: 'SentryError', - value: 'something something server connection', - }, - ], - }, -}; - const SCRIPT_ERROR_EVENT: Event = { exception: { values: [ @@ -425,22 +414,6 @@ describe.each([ ['InboundFilters', inboundFiltersIntegration], ['EventFilters', eventFiltersIntegration], ])('%s', (_, integrationFn) => { - describe('_isSentryError', () => { - it('should work as expected', () => { - const eventProcessor = createEventFiltersEventProcessor(integrationFn); - expect(eventProcessor(MESSAGE_EVENT, {})).toBe(MESSAGE_EVENT); - expect(eventProcessor(EXCEPTION_EVENT, {})).toBe(EXCEPTION_EVENT); - expect(eventProcessor(SENTRY_EVENT, {})).toBe(null); - }); - - it('should be configurable', () => { - const eventProcessor = createEventFiltersEventProcessor(integrationFn, { ignoreInternal: false }); - expect(eventProcessor(MESSAGE_EVENT, {})).toBe(MESSAGE_EVENT); - expect(eventProcessor(EXCEPTION_EVENT, {})).toBe(EXCEPTION_EVENT); - expect(eventProcessor(SENTRY_EVENT, {})).toBe(SENTRY_EVENT); - }); - }); - describe('ignoreErrors', () => { it('string filter with partial match', () => { const eventProcessor = createEventFiltersEventProcessor(integrationFn, { diff --git a/packages/core/test/utils-hoist/is.test.ts b/packages/core/test/utils-hoist/is.test.ts index 09fac86fcf12..70a83eee5efd 100644 --- a/packages/core/test/utils-hoist/is.test.ts +++ b/packages/core/test/utils-hoist/is.test.ts @@ -14,16 +14,6 @@ import { supportsDOMError, supportsDOMException, supportsErrorEvent } from '../. import { resolvedSyncPromise } from '../../src/utils-hoist/syncpromise'; import { testOnlyIfNodeVersionAtLeast } from './testutils'; -class SentryError extends Error { - public name: string; - - public constructor(public message: string) { - super(message); - this.name = new.target.prototype.constructor.name; - Object.setPrototypeOf(this, new.target.prototype); - } -} - if (supportsDOMError()) { describe('isDOMError()', () => { test('should work as advertised', () => { @@ -47,7 +37,6 @@ describe('isError()', () => { test('should work as advertised', () => { expect(isError(new Error())).toEqual(true); expect(isError(new ReferenceError())).toEqual(true); - expect(isError(new SentryError('message'))).toEqual(true); expect(isError({})).toEqual(false); expect( isError({ diff --git a/packages/opentelemetry/test/utils/mapStatus.test.ts b/packages/opentelemetry/test/utils/mapStatus.test.ts index aa72cfb95b53..a6b5db847652 100644 --- a/packages/opentelemetry/test/utils/mapStatus.test.ts +++ b/packages/opentelemetry/test/utils/mapStatus.test.ts @@ -81,7 +81,7 @@ describe('mapStatus', () => { expect(actual).toEqual(expected); }); - it('works with string SEMATTRS_HTTP_STATUS_CODE xxx', () => { + it('works with string SEMATTRS_HTTP_STATUS_CODE', () => { const span = createSpan('test-span'); span.setStatus({ code: 0 }); // UNSET From c2d0b2c82246860d9a62f2fe1afbfa4618b05761 Mon Sep 17 00:00:00 2001 From: Abhijeet Prasad Date: Tue, 25 Mar 2025 12:57:15 -0400 Subject: [PATCH 19/28] fix: Remove critical log severity level (#15824) This isn't part of the spec, and will break ingestion if we allow things to be sent. --- packages/browser/src/log.ts | 32 ---------------------------- packages/browser/test/index.test.ts | 1 - packages/browser/test/log.test.ts | 15 ------------- packages/core/src/types-hoist/log.ts | 2 +- packages/node/src/log.ts | 28 ------------------------ packages/node/test/log.test.ts | 10 --------- 6 files changed, 1 insertion(+), 87 deletions(-) diff --git a/packages/browser/src/log.ts b/packages/browser/src/log.ts index 69f8313a3378..23322c168a67 100644 --- a/packages/browser/src/log.ts +++ b/packages/browser/src/log.ts @@ -257,36 +257,4 @@ export function fatal(message: ParameterizedString, attributes?: Log['attributes captureLog('fatal', message, attributes); } -/** - * @summary Capture a log with the `critical` level. Requires `_experiments.enableLogs` to be enabled. - * - * @param message - The message to log. - * @param attributes - Arbitrary structured data that stores information about the log - e.g., { security: 'breach', severity: 'high' }. - * - * @example - * - * ``` - * Sentry.logger.critical('Security breach detected', { - * type: 'unauthorized_access', - * user: '132123', - * endpoint: '/api/admin', - * timestamp: Date.now() - * }); - * ``` - * - * @example With template strings - * - * ``` - * Sentry.logger.critical(Sentry.logger.fmt`Multiple failed login attempts from user ${user}`, { - * attempts: 10, - * timeWindow: '5m', - * blocked: true, - * timestamp: Date.now() - * }); - * ``` - */ -export function critical(message: ParameterizedString, attributes?: Log['attributes']): void { - captureLog('critical', message, attributes); -} - export { fmt } from '@sentry/core'; diff --git a/packages/browser/test/index.test.ts b/packages/browser/test/index.test.ts index f6f66184509f..ad4d47b1bf04 100644 --- a/packages/browser/test/index.test.ts +++ b/packages/browser/test/index.test.ts @@ -334,7 +334,6 @@ describe('SentryBrowser', () => { expect(logger.warn).toBeDefined(); expect(logger.error).toBeDefined(); expect(logger.fatal).toBeDefined(); - expect(logger.critical).toBeDefined(); }); }); }); diff --git a/packages/browser/test/log.test.ts b/packages/browser/test/log.test.ts index 9dec17fe01e0..9cddc3ecfc71 100644 --- a/packages/browser/test/log.test.ts +++ b/packages/browser/test/log.test.ts @@ -64,7 +64,6 @@ describe('Logger', () => { expect(logger.warn).toBeTypeOf('function'); expect(logger.error).toBeTypeOf('function'); expect(logger.fatal).toBeTypeOf('function'); - expect(logger.critical).toBeTypeOf('function'); }); it('should call _INTERNAL_captureLog with trace level', () => { @@ -150,20 +149,6 @@ describe('Logger', () => { undefined, ); }); - - it('should call _INTERNAL_captureLog with critical level', () => { - logger.critical('Test critical message', { key: 'value' }); - expect(mockCaptureLog).toHaveBeenCalledWith( - { - level: 'critical', - message: 'Test critical message', - attributes: { key: 'value' }, - severityNumber: undefined, - }, - expect.any(Object), - undefined, - ); - }); }); describe('Automatic flushing', () => { diff --git a/packages/core/src/types-hoist/log.ts b/packages/core/src/types-hoist/log.ts index 844c9223dbfd..35e30ce42b65 100644 --- a/packages/core/src/types-hoist/log.ts +++ b/packages/core/src/types-hoist/log.ts @@ -1,6 +1,6 @@ import type { ParameterizedString } from './parameterize'; -export type LogSeverityLevel = 'trace' | 'debug' | 'info' | 'warn' | 'error' | 'fatal' | 'critical'; +export type LogSeverityLevel = 'trace' | 'debug' | 'info' | 'warn' | 'error' | 'fatal'; export type SerializedLogAttributeValueType = | { diff --git a/packages/node/src/log.ts b/packages/node/src/log.ts index be5fc934351c..2523b0f77bf8 100644 --- a/packages/node/src/log.ts +++ b/packages/node/src/log.ts @@ -194,32 +194,4 @@ export function fatal(...args: CaptureLogArgs): void { captureLog('fatal', ...args); } -/** - * @summary Capture a log with the `critical` level. Requires `_experiments.enableLogs` to be enabled. - * - * You can either pass a message and attributes or a message template, params and attributes. - * - * @example - * - * ``` - * Sentry.logger.critical('Service health check failed', { - * service: 'payment-gateway', - * status: 'DOWN', - * lastHealthy: '2024-03-20T09:55:00Z' - * }); - * ``` - * - * @example With template strings - * - * ``` - * Sentry.logger.critical('Service %s is %s', - * ['payment-gateway', 'DOWN'], - * { lastHealthy: '2024-03-20T09:55:00Z' } - * ); - * ``` - */ -export function critical(...args: CaptureLogArgs): void { - captureLog('critical', ...args); -} - export { fmt } from '@sentry/core'; diff --git a/packages/node/test/log.test.ts b/packages/node/test/log.test.ts index 150e040f5ec5..bd2e051ddf50 100644 --- a/packages/node/test/log.test.ts +++ b/packages/node/test/log.test.ts @@ -32,7 +32,6 @@ describe('Node Logger', () => { expect(nodeLogger.warn).toBeTypeOf('function'); expect(nodeLogger.error).toBeTypeOf('function'); expect(nodeLogger.fatal).toBeTypeOf('function'); - expect(nodeLogger.critical).toBeTypeOf('function'); }); it('should call _INTERNAL_captureLog with trace level', () => { @@ -88,15 +87,6 @@ describe('Node Logger', () => { attributes: { key: 'value' }, }); }); - - it('should call _INTERNAL_captureLog with critical level', () => { - nodeLogger.critical('Test critical message', { key: 'value' }); - expect(mockCaptureLog).toHaveBeenCalledWith({ - level: 'critical', - message: 'Test critical message', - attributes: { key: 'value' }, - }); - }); }); describe('Template string logging', () => { From 00a1018779ada58b73d1ee9080aca4a3e3424f28 Mon Sep 17 00:00:00 2001 From: Charly Gomez Date: Tue, 25 Mar 2025 18:01:28 +0100 Subject: [PATCH 20/28] chore(deps): Bump `rrweb` to `2.35.0` (#15825) --- packages/replay-canvas/package.json | 2 +- packages/replay-internal/package.json | 4 +-- yarn.lock | 35 ++++++++++++++++++++++++++- 3 files changed, 37 insertions(+), 4 deletions(-) diff --git a/packages/replay-canvas/package.json b/packages/replay-canvas/package.json index 261842a70f32..a65c2a2c2bfb 100644 --- a/packages/replay-canvas/package.json +++ b/packages/replay-canvas/package.json @@ -65,7 +65,7 @@ }, "homepage": "https://docs.sentry.io/platforms/javascript/session-replay/", "devDependencies": { - "@sentry-internal/rrweb": "2.34.0" + "@sentry-internal/rrweb": "2.35.0" }, "dependencies": { "@sentry-internal/replay": "9.9.0", diff --git a/packages/replay-internal/package.json b/packages/replay-internal/package.json index 44b7974c8942..aabcaf3c9dde 100644 --- a/packages/replay-internal/package.json +++ b/packages/replay-internal/package.json @@ -71,8 +71,8 @@ "devDependencies": { "@babel/core": "^7.17.5", "@sentry-internal/replay-worker": "9.9.0", - "@sentry-internal/rrweb": "2.34.0", - "@sentry-internal/rrweb-snapshot": "2.34.0", + "@sentry-internal/rrweb": "2.35.0", + "@sentry-internal/rrweb-snapshot": "2.35.0", "fflate": "0.8.2", "jest-matcher-utils": "^29.0.0", "jsdom-worker": "^0.2.1" diff --git a/yarn.lock b/yarn.lock index 73b52e4425eb..7f755028b1da 100644 --- a/yarn.lock +++ b/yarn.lock @@ -6660,11 +6660,23 @@ dependencies: "@sentry-internal/rrweb-snapshot" "2.34.0" +"@sentry-internal/rrdom@2.35.0": + version "2.35.0" + resolved "https://registry.yarnpkg.com/@sentry-internal/rrdom/-/rrdom-2.35.0.tgz#27dbdfe3249afb65a31f3b680cd0cc92ed2001dd" + integrity sha512-sWZjJpv7/Fu1po5ibzGUojWLMGn/GgqsayE8dqbwI6F2x5gMVYL0/yIk+9Qii0ei3Su3BybWHfftZs+5r2Bong== + dependencies: + "@sentry-internal/rrweb-snapshot" "2.35.0" + "@sentry-internal/rrweb-snapshot@2.34.0": version "2.34.0" resolved "https://registry.yarnpkg.com/@sentry-internal/rrweb-snapshot/-/rrweb-snapshot-2.34.0.tgz#79c2049b6c887e3c128d5fa80d6f745a61dd0e68" integrity sha512-9Tb8jwVufn5GLV0d/CTuoZWo2O06ZB+xWeTJdEkbtJ6PAmO/Q7GQI3uNIx0pfFEnXP+0Km8CKKxpwkEM0z2m6w== +"@sentry-internal/rrweb-snapshot@2.35.0": + version "2.35.0" + resolved "https://registry.yarnpkg.com/@sentry-internal/rrweb-snapshot/-/rrweb-snapshot-2.35.0.tgz#656f4716e3bdda151f122868f6f92d5f4224967c" + integrity sha512-CyERHnGWIkuCtw4xYJMoyDUv+5vj38HBd0upeEhKyYzjZ8rOttwsFjfZUBdotsP8O0/RVt9KIPRbSRESC1qSJw== + "@sentry-internal/rrweb-types@2.34.0": version "2.34.0" resolved "https://registry.yarnpkg.com/@sentry-internal/rrweb-types/-/rrweb-types-2.34.0.tgz#32b853d93d1d9a1ae1888b17d84b24e674fadee0" @@ -6673,6 +6685,14 @@ "@sentry-internal/rrweb-snapshot" "2.34.0" "@types/css-font-loading-module" "0.0.7" +"@sentry-internal/rrweb-types@2.35.0": + version "2.35.0" + resolved "https://registry.yarnpkg.com/@sentry-internal/rrweb-types/-/rrweb-types-2.35.0.tgz#b2e63879a23593505fc3e28aa811e718de71f15f" + integrity sha512-D0mu2bgtvYD8MGijZDSD+q3FC8fDVRvNJD4canKvI3Wy9/LHTPbJ6F4U544vp5VrdBGCYIf/cxuJwmyZDfl5RQ== + dependencies: + "@sentry-internal/rrweb-snapshot" "2.35.0" + "@types/css-font-loading-module" "0.0.7" + "@sentry-internal/rrweb@2.34.0": version "2.34.0" resolved "https://registry.yarnpkg.com/@sentry-internal/rrweb/-/rrweb-2.34.0.tgz#a32945504f1ba2ba60f2ebd7a17d2df5e1aa010d" @@ -6687,6 +6707,20 @@ fflate "^0.4.4" mitt "^3.0.0" +"@sentry-internal/rrweb@2.35.0": + version "2.35.0" + resolved "https://registry.yarnpkg.com/@sentry-internal/rrweb/-/rrweb-2.35.0.tgz#ae10b9aaf3ee379164ec52f1186ee053d369b0a3" + integrity sha512-Zy3bnzL9GY6SFTZ5x5YNxtkmIUiaLSppLA41xn6zc4UWSYI4DcA+M8OGxI4TiHkQVJhhjwBG1CevrLyrBxyEgA== + dependencies: + "@sentry-internal/rrdom" "2.35.0" + "@sentry-internal/rrweb-snapshot" "2.35.0" + "@sentry-internal/rrweb-types" "2.35.0" + "@types/css-font-loading-module" "0.0.7" + "@xstate/fsm" "^1.4.0" + base64-arraybuffer "^1.0.1" + fflate "^0.4.4" + mitt "^3.0.0" + "@sentry/babel-plugin-component-annotate@2.22.6": version "2.22.6" resolved "https://registry.yarnpkg.com/@sentry/babel-plugin-component-annotate/-/babel-plugin-component-annotate-2.22.6.tgz#829d6caf2c95c1c46108336de4e1049e6521435e" @@ -28153,7 +28187,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 12c5e73d96086fc7d032463af3006bcd46a94fce Mon Sep 17 00:00:00 2001 From: Tim Fish Date: Tue, 25 Mar 2025 17:15:02 -0600 Subject: [PATCH 21/28] test(google-serverless): Migrate to Vitest (#15567) Co-authored-by: Abhijeet Prasad --- .../google-cloud-serverless/jest.config.js | 1 - packages/google-cloud-serverless/package.json | 7 +- .../src/integrations/google-cloud-grpc.ts | 8 +- .../test/__mocks__/dns.ts | 2 - .../test/gcpfunction/cloud_event.test.ts | 18 +- .../test/gcpfunction/events.test.ts | 20 +- .../test/gcpfunction/http.test.ts | 29 +- .../integrations/google-cloud-grpc.test.ts | 290 +++++++++++------- .../integrations/google-cloud-http.test.ts | 15 +- .../google-cloud-serverless/test/sdk.test.ts | 11 +- .../tsconfig.test.json | 4 +- .../google-cloud-serverless/vite.config.ts | 8 + yarn.lock | 281 +++++++---------- 13 files changed, 345 insertions(+), 349 deletions(-) delete mode 100644 packages/google-cloud-serverless/jest.config.js delete mode 100644 packages/google-cloud-serverless/test/__mocks__/dns.ts create mode 100644 packages/google-cloud-serverless/vite.config.ts diff --git a/packages/google-cloud-serverless/jest.config.js b/packages/google-cloud-serverless/jest.config.js deleted file mode 100644 index 24f49ab59a4c..000000000000 --- a/packages/google-cloud-serverless/jest.config.js +++ /dev/null @@ -1 +0,0 @@ -module.exports = require('../../jest/jest.config.js'); diff --git a/packages/google-cloud-serverless/package.json b/packages/google-cloud-serverless/package.json index d5c4baddad56..3500292d0ef3 100644 --- a/packages/google-cloud-serverless/package.json +++ b/packages/google-cloud-serverless/package.json @@ -55,10 +55,7 @@ "devDependencies": { "@google-cloud/bigquery": "^5.3.0", "@google-cloud/common": "^3.4.1", - "@google-cloud/functions-framework": "^1.7.1", - "@google-cloud/pubsub": "^2.5.0", "@types/node": "^18.19.1", - "google-gax": "^2.9.0", "nock": "^13.5.5" }, "scripts": { @@ -77,8 +74,8 @@ "clean": "rimraf build coverage sentry-google-cloud-*.tgz", "fix": "eslint . --format stylish --fix", "lint": "eslint . --format stylish", - "test": "jest", - "test:watch": "jest --watch", + "test": "vitest run", + "test:watch": "vitest --watch", "yalc:publish": "yalc publish --push --sig" }, "volta": { diff --git a/packages/google-cloud-serverless/src/integrations/google-cloud-grpc.ts b/packages/google-cloud-serverless/src/integrations/google-cloud-grpc.ts index 7d4c49990af6..6261660b5f98 100644 --- a/packages/google-cloud-serverless/src/integrations/google-cloud-grpc.ts +++ b/packages/google-cloud-serverless/src/integrations/google-cloud-grpc.ts @@ -3,11 +3,11 @@ import type { Client, IntegrationFn } from '@sentry/core'; import { SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN, defineIntegration, fill, getClient } from '@sentry/core'; import { startInactiveSpan } from '@sentry/node'; -interface GrpcFunction extends CallableFunction { +export interface GrpcFunction extends CallableFunction { (...args: unknown[]): EventEmitter; } -interface GrpcFunctionObject extends GrpcFunction { +export interface GrpcFunctionObject extends GrpcFunction { requestStream: boolean; responseStream: boolean; originalName: string; @@ -21,7 +21,7 @@ interface CreateStubFunc extends CallableFunction { (createStub: unknown, options: StubOptions): PromiseLike; } -interface Stub { +export interface Stub { [key: string]: GrpcFunctionObject; } @@ -78,7 +78,7 @@ function wrapCreateStub(origCreate: CreateStubFunc): CreateStubFunc { } /** Patches the function in grpc stub to enable tracing */ -function fillGrpcFunction(stub: Stub, serviceIdentifier: string, methodName: string): void { +export function fillGrpcFunction(stub: Stub, serviceIdentifier: string, methodName: string): void { const funcObj = stub[methodName]; if (typeof funcObj !== 'function') { return; diff --git a/packages/google-cloud-serverless/test/__mocks__/dns.ts b/packages/google-cloud-serverless/test/__mocks__/dns.ts deleted file mode 100644 index d03aa8d3f84b..000000000000 --- a/packages/google-cloud-serverless/test/__mocks__/dns.ts +++ /dev/null @@ -1,2 +0,0 @@ -export const lookup = jest.fn(); -export const resolveTxt = jest.fn(); diff --git a/packages/google-cloud-serverless/test/gcpfunction/cloud_event.test.ts b/packages/google-cloud-serverless/test/gcpfunction/cloud_event.test.ts index 8bbc926643f7..34aad6f13adb 100644 --- a/packages/google-cloud-serverless/test/gcpfunction/cloud_event.test.ts +++ b/packages/google-cloud-serverless/test/gcpfunction/cloud_event.test.ts @@ -1,22 +1,24 @@ import { SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN, SEMANTIC_ATTRIBUTE_SENTRY_SOURCE } from '@sentry/core'; +import { describe, vi, beforeEach, test, expect } from 'vitest'; import { wrapCloudEventFunction } from '../../src/gcpfunction/cloud_events'; import type { CloudEventFunction, CloudEventFunctionWithCallback } from '../../src/gcpfunction/general'; -const mockStartSpanManual = jest.fn((...spanArgs) => ({ ...spanArgs })); -const mockFlush = jest.fn((...args) => Promise.resolve(args)); -const mockCaptureException = jest.fn(); +const mockStartSpanManual = vi.fn((...spanArgs) => ({ ...spanArgs })); +const mockFlush = vi.fn((...args) => Promise.resolve(args)); +const mockCaptureException = vi.fn(); const mockScope = { - setContext: jest.fn(), + setContext: vi.fn(), }; const mockSpan = { - end: jest.fn(), + end: vi.fn(), }; -jest.mock('@sentry/node', () => { - const original = jest.requireActual('@sentry/node'); +vi.mock('@sentry/node', async () => { + // eslint-disable-next-line @typescript-eslint/consistent-type-imports + const original = (await vi.importActual('@sentry/node')) as typeof import('@sentry/node'); return { ...original, startSpanManual: (...args: unknown[]) => { @@ -38,7 +40,7 @@ jest.mock('@sentry/node', () => { describe('wrapCloudEventFunction', () => { beforeEach(() => { - jest.clearAllMocks(); + vi.clearAllMocks(); }); function handleCloudEvent(fn: CloudEventFunctionWithCallback): Promise { diff --git a/packages/google-cloud-serverless/test/gcpfunction/events.test.ts b/packages/google-cloud-serverless/test/gcpfunction/events.test.ts index aad3d5ec1645..99274c714637 100644 --- a/packages/google-cloud-serverless/test/gcpfunction/events.test.ts +++ b/packages/google-cloud-serverless/test/gcpfunction/events.test.ts @@ -1,23 +1,25 @@ import { SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN, SEMANTIC_ATTRIBUTE_SENTRY_SOURCE } from '@sentry/core'; +import { describe, vi, beforeEach, test, expect } from 'vitest'; import type { Event } from '@sentry/core'; import { wrapEventFunction } from '../../src/gcpfunction/events'; import type { EventFunction, EventFunctionWithCallback } from '../../src/gcpfunction/general'; -const mockStartSpanManual = jest.fn((...spanArgs) => ({ ...spanArgs })); -const mockFlush = jest.fn((...args) => Promise.resolve(args)); -const mockCaptureException = jest.fn(); +const mockStartSpanManual = vi.fn((...spanArgs) => ({ ...spanArgs })); +const mockFlush = vi.fn((...args) => Promise.resolve(args)); +const mockCaptureException = vi.fn(); const mockScope = { - setContext: jest.fn(), + setContext: vi.fn(), }; const mockSpan = { - end: jest.fn(), + end: vi.fn(), }; -jest.mock('@sentry/node', () => { - const original = jest.requireActual('@sentry/node'); +vi.mock('@sentry/node', async () => { + // eslint-disable-next-line @typescript-eslint/consistent-type-imports + const original = (await vi.importActual('@sentry/node')) as typeof import('@sentry/node'); return { ...original, startSpanManual: (...args: unknown[]) => { @@ -39,7 +41,7 @@ jest.mock('@sentry/node', () => { describe('wrapEventFunction', () => { beforeEach(() => { - jest.clearAllMocks(); + vi.clearAllMocks(); }); function handleEvent(fn: EventFunctionWithCallback): Promise { @@ -238,7 +240,7 @@ describe('wrapEventFunction', () => { const scopeFunction = mockCaptureException.mock.calls[0][1]; const event: Event = { exception: { values: [{}] } }; let evtProcessor: ((e: Event) => Event) | undefined = undefined; - scopeFunction({ addEventProcessor: jest.fn().mockImplementation(proc => (evtProcessor = proc)) }); + scopeFunction({ addEventProcessor: vi.fn().mockImplementation(proc => (evtProcessor = proc)) }); expect(evtProcessor).toBeInstanceOf(Function); // @ts-expect-error just mocking around... diff --git a/packages/google-cloud-serverless/test/gcpfunction/http.test.ts b/packages/google-cloud-serverless/test/gcpfunction/http.test.ts index 7f456237ad9c..914c1baffed0 100644 --- a/packages/google-cloud-serverless/test/gcpfunction/http.test.ts +++ b/packages/google-cloud-serverless/test/gcpfunction/http.test.ts @@ -1,28 +1,27 @@ import type { Integration } from '@sentry/core'; - import { SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN, SEMANTIC_ATTRIBUTE_SENTRY_SOURCE } from '@sentry/core'; +import { describe, vi, beforeEach, test, expect, type MockInstance } from 'vitest'; import { wrapHttpFunction } from '../../src/gcpfunction/http'; - import type { HttpFunction, Request, Response } from '../../src/gcpfunction/general'; - import { init } from '../../src/sdk'; -const mockStartSpanManual = jest.fn((...spanArgs) => ({ ...spanArgs })); -const mockFlush = jest.fn((...args) => Promise.resolve(args)); -const mockCaptureException = jest.fn(); -const mockInit = jest.fn(); +const mockStartSpanManual = vi.fn((...spanArgs) => ({ ...spanArgs })); +const mockFlush = vi.fn((...args) => Promise.resolve(args)); +const mockCaptureException = vi.fn(); +const mockInit = vi.fn(); const mockScope = { - setSDKProcessingMetadata: jest.fn(), + setSDKProcessingMetadata: vi.fn(), }; const mockSpan = { - end: jest.fn(), + end: vi.fn(), }; -jest.mock('@sentry/node', () => { - const original = jest.requireActual('@sentry/node'); +vi.mock('@sentry/node', async () => { + // eslint-disable-next-line @typescript-eslint/consistent-type-imports + const original = (await vi.importActual('@sentry/node')) as typeof import('@sentry/node'); return { ...original, init: (options: unknown) => { @@ -47,7 +46,7 @@ jest.mock('@sentry/node', () => { describe('GCPFunction', () => { beforeEach(() => { - jest.clearAllMocks(); + vi.clearAllMocks(); }); async function handleHttp(fn: HttpFunction, trace_headers: { [key: string]: string } | null = null): Promise { @@ -146,7 +145,7 @@ describe('GCPFunction', () => { body: { foo: 'bar' }, } as Request; - const mockEnd = jest.fn(); + const mockEnd = vi.fn(); const response = { end: mockEnd } as unknown as Response; mockFlush.mockImplementationOnce(async () => { @@ -171,8 +170,8 @@ describe('GCPFunction', () => { await handleHttp(wrappedHandler); - const initOptions = (mockInit as unknown as jest.SpyInstance).mock.calls[0]; - const defaultIntegrations = initOptions[0]?.defaultIntegrations.map((i: Integration) => i.name); + const initOptions = (mockInit as unknown as MockInstance).mock.calls[0]; + const defaultIntegrations = initOptions?.[0]?.defaultIntegrations.map((i: Integration) => i.name); expect(defaultIntegrations).toContain('RequestData'); diff --git a/packages/google-cloud-serverless/test/integrations/google-cloud-grpc.test.ts b/packages/google-cloud-serverless/test/integrations/google-cloud-grpc.test.ts index 2e6c9039e075..af37bc71a7c5 100644 --- a/packages/google-cloud-serverless/test/integrations/google-cloud-grpc.test.ts +++ b/packages/google-cloud-serverless/test/integrations/google-cloud-grpc.test.ts @@ -1,25 +1,32 @@ -jest.mock('dns'); +import { vi, describe, beforeEach, test, expect } from 'vitest'; +import { NodeClient } from '@sentry/node'; +import { createTransport } from '@sentry/core'; +import { setCurrentClient, SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN } from '@sentry/core'; +import { googleCloudGrpcIntegration, fillGrpcFunction } from '../../src/integrations/google-cloud-grpc'; +import type { GrpcFunctionObject, Stub, GrpcFunction } from '../../src/integrations/google-cloud-grpc'; -import * as dns from 'dns'; -import { EventEmitter } from 'events'; -import * as fs from 'fs'; -import * as path from 'path'; -import { PubSub } from '@google-cloud/pubsub'; -import * as http2 from 'http2'; -import * as nock from 'nock'; +const mockSpanEnd = vi.fn(); +const mockStartInactiveSpan = vi.fn(); +const mockFill = vi.fn(); -import { SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN } from '@sentry/core'; -import { NodeClient, createTransport, setCurrentClient } from '@sentry/node'; -import { googleCloudGrpcIntegration } from '../../src/integrations/google-cloud-grpc'; +let mockClient: NodeClient; -const spyConnect = jest.spyOn(http2, 'connect'); - -const mockSpanEnd = jest.fn(); -const mockStartInactiveSpan = jest.fn(spanArgs => ({ ...spanArgs })); +vi.mock('@sentry/core', async () => { + const original = await vi.importActual('@sentry/core'); + return { + ...original, + fill: (obj: any, name: string, replacement: any) => { + mockFill(obj, name, replacement); + obj[name] = replacement(obj[name]); + }, + getClient: () => mockClient, + }; +}); -jest.mock('@sentry/node', () => { +vi.mock('@sentry/node', async () => { + const original = await vi.importActual('@sentry/node'); return { - ...jest.requireActual('@sentry/node'), + ...original, startInactiveSpan: (ctx: unknown) => { mockStartInactiveSpan(ctx); return { end: mockSpanEnd }; @@ -27,130 +34,177 @@ jest.mock('@sentry/node', () => { }; }); -/** Fake HTTP2 stream */ -class FakeStream extends EventEmitter { - public rstCode: number = 0; - close() { - this.emit('end'); - this.emit('close'); - } - end() {} - pause() {} - resume() {} - write(_data: Buffer, cb: CallableFunction) { - process.nextTick(cb, null); - } -} - -/** Fake HTTP2 session for GRPC */ -class FakeSession extends EventEmitter { - public socket: EventEmitter = new EventEmitter(); - public request: jest.Mock = jest.fn(); - ping() {} - mockRequest(fn: (stream: FakeStream) => void): FakeStream { - const stream = new FakeStream(); - this.request.mockImplementationOnce(() => { - process.nextTick(fn, stream); - return stream; - }); - return stream; - } - mockUnaryRequest(responseData: Buffer) { - this.mockRequest(stream => { - stream.emit( - 'response', - { ':status': 200, 'content-type': 'application/grpc', 'content-disposition': 'attachment' }, - 4, - ); - stream.emit('data', responseData); - stream.emit('trailers', { 'grpc-status': '0', 'content-disposition': 'attachment' }); - }); - } - close() { - this.emit('close'); - this.socket.emit('close'); - } - ref() {} - unref() {} +// Need to override mock because the integration loads google-gax as a CJS file +async function mock(mockedUri: string, stub: any) { + // @ts-expect-error we are using import on purpose + const { Module } = await import('module'); + + // @ts-expect-error test + Module._load_original = Module._load; + // @ts-expect-error test + Module._load = (uri, parent) => { + if (uri === mockedUri) return stub; + // @ts-expect-error test + return Module._load_original(uri, parent); + }; } -function mockHttp2Session(): FakeSession { - const session = new FakeSession(); - spyConnect.mockImplementationOnce(() => { - process.nextTick(() => session.emit('connect')); - return session as unknown as http2.ClientHttp2Session; - }); - return session; -} +vi.hoisted( + () => + void mock('google-gax', { + GrpcClient: { + prototype: { + createStub: vi.fn(), + }, + }, + }), +); describe('GoogleCloudGrpc tracing', () => { - const mockClient = new NodeClient({ - tracesSampleRate: 1.0, - integrations: [], - dsn: 'https://withAWSServices@domain/123', - transport: () => createTransport({ recordDroppedEvent: () => undefined }, _ => Promise.resolve({})), - stackParser: () => [], - }); + beforeEach(() => { + mockClient = new NodeClient({ + tracesSampleRate: 1.0, + integrations: [], + dsn: 'https://withAWSServices@domain/123', + transport: () => createTransport({ recordDroppedEvent: () => undefined }, _ => Promise.resolve({})), + stackParser: () => [], + }); - const integration = googleCloudGrpcIntegration(); - mockClient.addIntegration(integration); + const integration = googleCloudGrpcIntegration(); + mockClient.addIntegration(integration); + integration.setup?.(mockClient); - beforeEach(() => { - nock('https://www.googleapis.com').post('/oauth2/v4/token').reply(200, []); setCurrentClient(mockClient); mockSpanEnd.mockClear(); mockStartInactiveSpan.mockClear(); + mockFill.mockClear(); }); - afterAll(() => { - nock.restore(); - spyConnect.mockRestore(); - }); - - // We use google cloud pubsub as an example of grpc service for which we can trace requests. - describe('pubsub', () => { - // @ts-expect-error see "Why @ts-expect-error" note - const dnsLookup = dns.lookup as jest.Mock; - // @ts-expect-error see "Why @ts-expect-error" note - const resolveTxt = dns.resolveTxt as jest.Mock; - dnsLookup.mockImplementation((hostname, ...args) => { - expect(hostname).toEqual('pubsub.googleapis.com'); - process.nextTick(args[args.length - 1], null, [{ address: '0.0.0.0', family: 4 }]); - }); - resolveTxt.mockImplementation((hostname, cb) => { - expect(hostname).toEqual('pubsub.googleapis.com'); - process.nextTick(cb, null, []); + describe('setup', () => { + test('integration name is correct', () => { + const integration = googleCloudGrpcIntegration(); + expect(integration.name).toBe('GoogleCloudGrpc'); }); - const pubsub = new PubSub({ - credentials: { - client_email: 'client@email', - private_key: fs.readFileSync(path.resolve(__dirname, 'private.pem')).toString(), - }, - projectId: 'project-id', + test('setupOnce patches GrpcClient.createStub', () => { + const mockCreateStub = vi.fn(); + const mockGrpcClient = { + prototype: { + createStub: mockCreateStub, + }, + }; + + // eslint-disable-next-line @typescript-eslint/no-var-requires + require('google-gax').GrpcClient = mockGrpcClient; + + const integration = googleCloudGrpcIntegration(); + integration.setupOnce?.(); + expect(mockCreateStub).toBeDefined(); }); - afterEach(() => { - dnsLookup.mockReset(); - resolveTxt.mockReset(); + test('setupOnce throws when google-gax is not available and not optional', () => { + // eslint-disable-next-line @typescript-eslint/no-var-requires + require('google-gax').GrpcClient = undefined; + + const integration = googleCloudGrpcIntegration(); + expect(() => integration.setupOnce?.()).toThrow(); }); - afterAll(async () => { - await pubsub.close(); + test('setupOnce does not throw when google-gax is not available and optional', () => { + // eslint-disable-next-line @typescript-eslint/no-var-requires + require('google-gax').GrpcClient = undefined; + + const optionalIntegration = googleCloudGrpcIntegration({ optional: true }); + expect(() => optionalIntegration.setupOnce?.()).not.toThrow(); }); + }); - test('publish', async () => { - mockHttp2Session().mockUnaryRequest(Buffer.from('00000000120a1031363337303834313536363233383630', 'hex')); - const resp = await pubsub.topic('nicetopic').publish(Buffer.from('data')); - expect(resp).toEqual('1637084156623860'); - expect(mockStartInactiveSpan).toBeCalledWith({ - op: 'grpc.pubsub', + describe('fillGrpcFunction', () => { + test('patches unary call methods with tracing', () => { + const mockStub: Stub = { + unaryMethod: Object.assign(vi.fn(), { + requestStream: false, + responseStream: false, + originalName: 'unaryMethod', + } as GrpcFunctionObject), + }; + + const mockEventEmitter = { + on: vi.fn(), + }; + + (mockStub.unaryMethod as any).apply = vi.fn().mockReturnValue(mockEventEmitter); + + fillGrpcFunction(mockStub, 'test-service', 'unaryMethod'); + + const result = (mockStub.unaryMethod as GrpcFunction)(); + expect(result).toBe(mockEventEmitter); + expect(mockEventEmitter.on).toHaveBeenCalledWith('status', expect.any(Function)); + expect(mockStartInactiveSpan).toHaveBeenCalledWith({ + name: 'unary call unaryMethod', + onlyIfParent: true, + op: 'grpc.test-service', attributes: { [SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: 'auto.grpc.serverless', }, - name: 'unary call publish', - onlyIfParent: true, }); }); + + test('does not patch non-unary call methods', () => { + const mockStub: Stub = { + clientStreamMethod: Object.assign(vi.fn(), { + requestStream: true, + responseStream: false, + originalName: 'clientStreamMethod', + } as GrpcFunctionObject), + serverStreamMethod: Object.assign(vi.fn(), { + requestStream: false, + responseStream: true, + originalName: 'serverStreamMethod', + } as GrpcFunctionObject), + bidiStreamMethod: Object.assign(vi.fn(), { + requestStream: true, + responseStream: true, + originalName: 'bidiStreamMethod', + } as GrpcFunctionObject), + }; + + fillGrpcFunction(mockStub, 'test-service', 'clientStreamMethod'); + fillGrpcFunction(mockStub, 'test-service', 'serverStreamMethod'); + fillGrpcFunction(mockStub, 'test-service', 'bidiStreamMethod'); + + expect(mockStartInactiveSpan).not.toHaveBeenCalled(); + }); + + test('does not patch non-function properties', () => { + const mockStub: Stub = { + nonFunction: Object.assign(vi.fn(), { + requestStream: false, + responseStream: false, + originalName: 'nonFunction', + } as GrpcFunctionObject), + }; + + fillGrpcFunction(mockStub, 'test-service', 'nonFunction'); + expect(mockStartInactiveSpan).not.toHaveBeenCalled(); + }); + + test('does not patch methods when return value is not an EventEmitter', () => { + const mockStub: Stub = { + unaryMethod: Object.assign(vi.fn(), { + requestStream: false, + responseStream: false, + originalName: 'unaryMethod', + } as GrpcFunctionObject), + }; + + (mockStub.unaryMethod as any).apply = vi.fn().mockReturnValue({ notAnEventEmitter: true }); + + fillGrpcFunction(mockStub, 'test-service', 'unaryMethod'); + + const result = (mockStub.unaryMethod as GrpcFunction)(); + expect(result).toEqual({ notAnEventEmitter: true }); + expect(mockStartInactiveSpan).not.toHaveBeenCalled(); + }); }); }); diff --git a/packages/google-cloud-serverless/test/integrations/google-cloud-http.test.ts b/packages/google-cloud-serverless/test/integrations/google-cloud-http.test.ts index ca26d1069379..164d1c10dfc2 100644 --- a/packages/google-cloud-serverless/test/integrations/google-cloud-http.test.ts +++ b/packages/google-cloud-serverless/test/integrations/google-cloud-http.test.ts @@ -1,18 +1,23 @@ import * as fs from 'fs'; import * as path from 'path'; import { BigQuery } from '@google-cloud/bigquery'; -import * as nock from 'nock'; +// eslint-disable-next-line @typescript-eslint/ban-ts-comment +// @ts-ignore ESM/CJS interop issue +import nock from 'nock'; +import { describe, vi, beforeEach, test, expect, afterAll } from 'vitest'; import { SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN } from '@sentry/core'; import { NodeClient, createTransport, setCurrentClient } from '@sentry/node'; import { googleCloudHttpIntegration } from '../../src/integrations/google-cloud-http'; -const mockSpanEnd = jest.fn(); -const mockStartInactiveSpan = jest.fn(spanArgs => ({ ...spanArgs })); +const mockSpanEnd = vi.fn(); +const mockStartInactiveSpan = vi.fn(spanArgs => ({ ...spanArgs })); -jest.mock('@sentry/node', () => { +vi.mock('@sentry/node', async () => { + // eslint-disable-next-line @typescript-eslint/consistent-type-imports + const original = (await vi.importActual('@sentry/node')) as typeof import('@sentry/node'); return { - ...jest.requireActual('@sentry/node'), + ...original, startInactiveSpan: (ctx: unknown) => { mockStartInactiveSpan(ctx); return { end: mockSpanEnd }; diff --git a/packages/google-cloud-serverless/test/sdk.test.ts b/packages/google-cloud-serverless/test/sdk.test.ts index ee4a1fc6fa17..522915116e24 100644 --- a/packages/google-cloud-serverless/test/sdk.test.ts +++ b/packages/google-cloud-serverless/test/sdk.test.ts @@ -1,9 +1,12 @@ +import { vi, describe, beforeEach, test, expect } from 'vitest'; + import { init } from '../src/sdk'; -const mockInit = jest.fn(); +const mockInit = vi.fn(); -jest.mock('@sentry/node', () => { - const original = jest.requireActual('@sentry/node'); +vi.mock('@sentry/node', async () => { + // eslint-disable-next-line @typescript-eslint/consistent-type-imports + const original = (await vi.importActual('@sentry/node')) as typeof import('@sentry/node'); return { ...original, init: (options: unknown) => { @@ -14,7 +17,7 @@ jest.mock('@sentry/node', () => { describe('init()', () => { beforeEach(() => { - jest.clearAllMocks(); + vi.clearAllMocks(); }); test('calls Sentry.init with correct sdk info metadata', () => { diff --git a/packages/google-cloud-serverless/tsconfig.test.json b/packages/google-cloud-serverless/tsconfig.test.json index 87f6afa06b86..ca7dbeb3be94 100644 --- a/packages/google-cloud-serverless/tsconfig.test.json +++ b/packages/google-cloud-serverless/tsconfig.test.json @@ -1,11 +1,11 @@ { "extends": "./tsconfig.json", - "include": ["test/**/*"], + "include": ["test/**/*", "vite.config.ts"], "compilerOptions": { // should include all types from `./tsconfig.json` plus types for all test frameworks used - "types": ["node", "jest"] + "types": ["node"] // other package-specific, test-specific options } diff --git a/packages/google-cloud-serverless/vite.config.ts b/packages/google-cloud-serverless/vite.config.ts new file mode 100644 index 000000000000..f18ec92095bc --- /dev/null +++ b/packages/google-cloud-serverless/vite.config.ts @@ -0,0 +1,8 @@ +import baseConfig from '../../vite/vite.config'; + +export default { + ...baseConfig, + test: { + ...baseConfig.test, + }, +}; diff --git a/yarn.lock b/yarn.lock index 7f755028b1da..9e8d7830af5d 100644 --- a/yarn.lock +++ b/yarn.lock @@ -4065,16 +4065,6 @@ retry-request "^4.1.1" teeny-request "^7.0.0" -"@google-cloud/functions-framework@^1.7.1": - version "1.7.1" - resolved "https://registry.yarnpkg.com/@google-cloud/functions-framework/-/functions-framework-1.7.1.tgz#d29a27744a6eb2f95d840b86135b97b0d804a49e" - integrity sha512-jjG7nH94Thij97EPW2oQN28pVPRN3UEGcsCRi6RdaaiSyK32X40LN4WHntKVmQPBhqH+I0magHMk1pSb0McH2g== - dependencies: - body-parser "^1.18.3" - express "^4.16.4" - minimist "^1.2.0" - on-finished "^2.3.0" - "@google-cloud/paginator@^3.0.0": version "3.0.5" resolved "https://registry.yarnpkg.com/@google-cloud/paginator/-/paginator-3.0.5.tgz#9d6b96c421a89bd560c1bc2c197c7611ef21db6c" @@ -4083,11 +4073,6 @@ arrify "^2.0.0" extend "^3.0.2" -"@google-cloud/precise-date@^2.0.0": - version "2.0.3" - resolved "https://registry.yarnpkg.com/@google-cloud/precise-date/-/precise-date-2.0.3.tgz#14f6f28ce35dabf3882e7aeab1c9d51bd473faed" - integrity sha512-+SDJ3ZvGkF7hzo6BGa8ZqeK3F6Z4+S+KviC9oOK+XCs3tfMyJCh/4j93XIWINgMMDIh9BgEvlw4306VxlXIlYA== - "@google-cloud/projectify@^2.0.0": version "2.0.1" resolved "https://registry.yarnpkg.com/@google-cloud/projectify/-/projectify-2.0.1.tgz#13350ee609346435c795bbfe133a08dfeab78d65" @@ -4098,27 +4083,6 @@ resolved "https://registry.yarnpkg.com/@google-cloud/promisify/-/promisify-2.0.3.tgz#f934b5cdc939e3c7039ff62b9caaf59a9d89e3a8" integrity sha512-d4VSA86eL/AFTe5xtyZX+ePUjE8dIFu2T8zmdeNBSa5/kNgXPCx/o/wbFNHAGLJdGnk1vddRuMESD9HbOC8irw== -"@google-cloud/pubsub@^2.5.0": - version "2.10.0" - resolved "https://registry.yarnpkg.com/@google-cloud/pubsub/-/pubsub-2.10.0.tgz#5fbfa59c91b15e880bd0258b907d8f52e0509074" - integrity sha512-XM/Fc6/W/LYzGH2pnhGLDR5E6JNZFMfzyUFP5bWgC4FK1KqIZ4g6hrnCCO38G4JfH2i1IuSQuefPF7FrZZo9tw== - dependencies: - "@google-cloud/paginator" "^3.0.0" - "@google-cloud/precise-date" "^2.0.0" - "@google-cloud/projectify" "^2.0.0" - "@google-cloud/promisify" "^2.0.0" - "@opentelemetry/api" "^0.12.0" - "@opentelemetry/tracing" "^0.12.0" - "@types/duplexify" "^3.6.0" - "@types/long" "^4.0.0" - arrify "^2.0.0" - extend "^3.0.2" - google-auth-library "^7.0.0" - google-gax "^2.9.2" - is-stream-ended "^0.1.4" - lodash.snakecase "^4.1.1" - p-defer "^3.0.0" - "@graphql-tools/merge@8.3.1": version "8.3.1" resolved "https://registry.yarnpkg.com/@graphql-tools/merge/-/merge-8.3.1.tgz#06121942ad28982a14635dbc87b5d488a041d722" @@ -4179,23 +4143,6 @@ dependencies: tslib "^2.4.0" -"@grpc/grpc-js@~1.2.0": - version "1.2.12" - resolved "https://registry.yarnpkg.com/@grpc/grpc-js/-/grpc-js-1.2.12.tgz#0153f27512acf69184bb52c0a1035ca91d6c14b0" - integrity sha512-+gPCklP1eqIgrNPyzddYQdt9+GvZqPlLpIjIo+TveE+gbtp74VV1A2ju8ExeO8ma8f7MbpaGZx/KJPYVWL9eDw== - dependencies: - "@types/node" ">=12.12.47" - google-auth-library "^6.1.1" - semver "^6.2.0" - -"@grpc/proto-loader@^0.5.1": - version "0.5.6" - resolved "https://registry.yarnpkg.com/@grpc/proto-loader/-/proto-loader-0.5.6.tgz#1dea4b8a6412b05e2d58514d507137b63a52a98d" - integrity sha512-DT14xgw3PSzPxwS13auTEwxhMMOoz33DPUKNtmYK/QYbBSpLXJy78FGGs5yVoxVobEqPm4iW9MOIoz0A3bLTRQ== - dependencies: - lodash.camelcase "^4.3.0" - protobufjs "^6.8.6" - "@handlebars/parser@~2.0.0": version "2.0.0" resolved "https://registry.yarnpkg.com/@handlebars/parser/-/parser-2.0.0.tgz#5e8b7298f31ff8f7b260e6b7363c7e9ceed7d9c5" @@ -5661,23 +5608,11 @@ resolved "https://registry.yarnpkg.com/@opentelemetry/api/-/api-1.9.0.tgz#d03eba68273dc0f7509e2a3d5cba21eae10379fe" integrity sha512-3giAOQvZiH5F9bMlMiv8+GSPMeqg0dbaeo58/0SlA9sxSqZhnUtxzX9/2FzyhS9sWQf5S0GJE0AKBrFqjpeYcg== -"@opentelemetry/api@^0.12.0": - version "0.12.0" - resolved "https://registry.yarnpkg.com/@opentelemetry/api/-/api-0.12.0.tgz#0359c3926e8f16fdcd8c78f196bd1e9fc4e66777" - integrity sha512-Dn4vU5GlaBrIWzLpsM6xbJwKHdlpwBQ4Bd+cL9ofJP3hKT8jBXpBpribmyaqAzrajzzl2Yt8uTa9rFVLfjDAvw== - dependencies: - "@opentelemetry/context-base" "^0.12.0" - "@opentelemetry/context-async-hooks@^1.30.1": version "1.30.1" resolved "https://registry.yarnpkg.com/@opentelemetry/context-async-hooks/-/context-async-hooks-1.30.1.tgz#4f76280691a742597fd0bf682982126857622948" integrity sha512-s5vvxXPVdjqS3kTLKMeBMvop9hbWkwzBpu+mUO2M7sZtlkyDJGwFe33wRKnbaYDo8ExRVBIIdwIGrqpxHuKttA== -"@opentelemetry/context-base@^0.12.0": - version "0.12.0" - resolved "https://registry.yarnpkg.com/@opentelemetry/context-base/-/context-base-0.12.0.tgz#4906ae27359d3311e3dea1b63770a16f60848550" - integrity sha512-UXwSsXo3F3yZ1dIBOG9ID8v2r9e+bqLWoizCtTb8rXtwF+N5TM7hzzvQz72o3nBU+zrI/D5e+OqAYK8ZgDd3DA== - "@opentelemetry/core@1.30.1", "@opentelemetry/core@^1.1.0", "@opentelemetry/core@^1.26.0", "@opentelemetry/core@^1.30.1", "@opentelemetry/core@^1.8.0": version "1.30.1" resolved "https://registry.yarnpkg.com/@opentelemetry/core/-/core-1.30.1.tgz#a0b468bb396358df801881709ea38299fc30ab27" @@ -5685,15 +5620,6 @@ dependencies: "@opentelemetry/semantic-conventions" "1.28.0" -"@opentelemetry/core@^0.12.0": - version "0.12.0" - resolved "https://registry.yarnpkg.com/@opentelemetry/core/-/core-0.12.0.tgz#a888badc9a408fa1f13976a574e69d14be32488e" - integrity sha512-oLZIkmTNWTJXzo1eA4dGu/S7wOVtylsgnEsCmhSJGhrJVDXm1eW/aGuNs3DVBeuxp0ZvQLAul3/PThsC3YrnzA== - dependencies: - "@opentelemetry/api" "^0.12.0" - "@opentelemetry/context-base" "^0.12.0" - semver "^7.1.3" - "@opentelemetry/instrumentation-amqplib@^0.46.1": version "0.46.1" resolved "https://registry.yarnpkg.com/@opentelemetry/instrumentation-amqplib/-/instrumentation-amqplib-0.46.1.tgz#7101678488d0e942162ca85c9ac6e93e1f3e0008" @@ -5963,14 +5889,6 @@ "@opentelemetry/core" "1.30.1" "@opentelemetry/semantic-conventions" "1.28.0" -"@opentelemetry/resources@^0.12.0": - version "0.12.0" - resolved "https://registry.yarnpkg.com/@opentelemetry/resources/-/resources-0.12.0.tgz#5eb287c3032a2bebb2bb9f69b44bd160d2a7d591" - integrity sha512-8cYvIKB68cyupc7D6SWzkLtt13mbjgxMahL4JKCM6hWPyiGSJlPFEAey4XFXI5LLpPZRYTPHLVoLqI/xwCFZZA== - dependencies: - "@opentelemetry/api" "^0.12.0" - "@opentelemetry/core" "^0.12.0" - "@opentelemetry/sdk-trace-base@^1.30.1": version "1.30.1" resolved "https://registry.yarnpkg.com/@opentelemetry/sdk-trace-base/-/sdk-trace-base-1.30.1.tgz#41a42234096dc98e8f454d24551fc80b816feb34" @@ -5985,11 +5903,6 @@ resolved "https://registry.yarnpkg.com/@opentelemetry/semantic-conventions/-/semantic-conventions-1.28.0.tgz#337fb2bca0453d0726696e745f50064411f646d6" integrity sha512-lp4qAiMTD4sNWW4DbKLBkfiMZ4jbAboJIGOQr5DvciMRI494OapieI9qiODpOt0XBr1LjIDy1xAGAnVs5supTA== -"@opentelemetry/semantic-conventions@^0.12.0": - version "0.12.0" - resolved "https://registry.yarnpkg.com/@opentelemetry/semantic-conventions/-/semantic-conventions-0.12.0.tgz#7e392aecdbdbd5d737d3995998b120dc17589ab0" - integrity sha512-BuCcDW0uLNYYTns0/LwXkJ8lp8aDm7kpS+WunEmPAPRSCe6ciOYRvzn5reqJfX93rf+6A3U2SgrBnCTH+0qoQQ== - "@opentelemetry/semantic-conventions@^1.25.1", "@opentelemetry/semantic-conventions@^1.27.0", "@opentelemetry/semantic-conventions@^1.28.0", "@opentelemetry/semantic-conventions@^1.30.0": version "1.30.0" resolved "https://registry.yarnpkg.com/@opentelemetry/semantic-conventions/-/semantic-conventions-1.30.0.tgz#3a42c4c475482f2ec87c12aad98832dc0087dc9a" @@ -6002,17 +5915,6 @@ dependencies: "@opentelemetry/core" "^1.1.0" -"@opentelemetry/tracing@^0.12.0": - version "0.12.0" - resolved "https://registry.yarnpkg.com/@opentelemetry/tracing/-/tracing-0.12.0.tgz#769927721d417bfac85eef50c2af068bedce8873" - integrity sha512-2TUGhTGkhgnxTciHCNAILPSeyXageJewRqfP9wOrx65sKd/jgvNYoY8nYf4EVWVMirDOxKDsmYgUkjdQrwb2dg== - dependencies: - "@opentelemetry/api" "^0.12.0" - "@opentelemetry/context-base" "^0.12.0" - "@opentelemetry/core" "^0.12.0" - "@opentelemetry/resources" "^0.12.0" - "@opentelemetry/semantic-conventions" "^0.12.0" - "@parcel/watcher-android-arm64@2.5.0": version "2.5.0" resolved "https://registry.yarnpkg.com/@parcel/watcher-android-arm64/-/watcher-android-arm64-2.5.0.tgz#e32d3dda6647791ee930556aee206fcd5ea0fb7a" @@ -6541,96 +6443,191 @@ estree-walker "^2.0.2" picomatch "^2.3.1" +"@rollup/rollup-android-arm-eabi@4.34.8": + version "4.34.8" + resolved "https://registry.yarnpkg.com/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.34.8.tgz#731df27dfdb77189547bcef96ada7bf166bbb2fb" + integrity sha512-q217OSE8DTp8AFHuNHXo0Y86e1wtlfVrXiAlwkIvGRQv9zbc6mE3sjIVfwI8sYUyNxwOg0j/Vm1RKM04JcWLJw== + "@rollup/rollup-android-arm-eabi@4.35.0": version "4.35.0" resolved "https://registry.yarnpkg.com/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.35.0.tgz#e1d7700735f7e8de561ef7d1fa0362082a180c43" integrity sha512-uYQ2WfPaqz5QtVgMxfN6NpLD+no0MYHDBywl7itPYd3K5TjjSghNKmX8ic9S8NU8w81NVhJv/XojcHptRly7qQ== +"@rollup/rollup-android-arm64@4.34.8": + version "4.34.8" + resolved "https://registry.yarnpkg.com/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.34.8.tgz#4bea6db78e1f6927405df7fe0faf2f5095e01343" + integrity sha512-Gigjz7mNWaOL9wCggvoK3jEIUUbGul656opstjaUSGC3eT0BM7PofdAJaBfPFWWkXNVAXbaQtC99OCg4sJv70Q== + "@rollup/rollup-android-arm64@4.35.0": version "4.35.0" resolved "https://registry.yarnpkg.com/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.35.0.tgz#fa6cdfb1fc9e2c8e227a7f35d524d8f7f90cf4db" integrity sha512-FtKddj9XZudurLhdJnBl9fl6BwCJ3ky8riCXjEw3/UIbjmIY58ppWwPEvU3fNu+W7FUsAsB1CdH+7EQE6CXAPA== +"@rollup/rollup-darwin-arm64@4.34.8": + version "4.34.8" + resolved "https://registry.yarnpkg.com/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.34.8.tgz#a7aab77d44be3c44a20f946e10160f84e5450e7f" + integrity sha512-02rVdZ5tgdUNRxIUrFdcMBZQoaPMrxtwSb+/hOfBdqkatYHR3lZ2A2EGyHq2sGOd0Owk80oV3snlDASC24He3Q== + "@rollup/rollup-darwin-arm64@4.35.0": version "4.35.0" resolved "https://registry.yarnpkg.com/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.35.0.tgz#6da5a1ddc4f11d4a7ae85ab443824cb6bf614e30" integrity sha512-Uk+GjOJR6CY844/q6r5DR/6lkPFOw0hjfOIzVx22THJXMxktXG6CbejseJFznU8vHcEBLpiXKY3/6xc+cBm65Q== +"@rollup/rollup-darwin-x64@4.34.8": + version "4.34.8" + resolved "https://registry.yarnpkg.com/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.34.8.tgz#c572c024b57ee8ddd1b0851703ace9eb6cc0dd82" + integrity sha512-qIP/elwR/tq/dYRx3lgwK31jkZvMiD6qUtOycLhTzCvrjbZ3LjQnEM9rNhSGpbLXVJYQ3rq39A6Re0h9tU2ynw== + "@rollup/rollup-darwin-x64@4.35.0": version "4.35.0" resolved "https://registry.yarnpkg.com/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.35.0.tgz#25b74ce2d8d3f9ea8e119b01384d44a1c0a0d3ae" integrity sha512-3IrHjfAS6Vkp+5bISNQnPogRAW5GAV1n+bNCrDwXmfMHbPl5EhTmWtfmwlJxFRUCBZ+tZ/OxDyU08aF6NI/N5Q== +"@rollup/rollup-freebsd-arm64@4.34.8": + version "4.34.8" + resolved "https://registry.yarnpkg.com/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.34.8.tgz#cf74f8113b5a83098a5c026c165742277cbfb88b" + integrity sha512-IQNVXL9iY6NniYbTaOKdrlVP3XIqazBgJOVkddzJlqnCpRi/yAeSOa8PLcECFSQochzqApIOE1GHNu3pCz+BDA== + "@rollup/rollup-freebsd-arm64@4.35.0": version "4.35.0" resolved "https://registry.yarnpkg.com/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.35.0.tgz#be3d39e3441df5d6e187c83d158c60656c82e203" integrity sha512-sxjoD/6F9cDLSELuLNnY0fOrM9WA0KrM0vWm57XhrIMf5FGiN8D0l7fn+bpUeBSU7dCgPV2oX4zHAsAXyHFGcQ== +"@rollup/rollup-freebsd-x64@4.34.8": + version "4.34.8" + resolved "https://registry.yarnpkg.com/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.34.8.tgz#39561f3a2f201a4ad6a01425b1ff5928154ecd7c" + integrity sha512-TYXcHghgnCqYFiE3FT5QwXtOZqDj5GmaFNTNt3jNC+vh22dc/ukG2cG+pi75QO4kACohZzidsq7yKTKwq/Jq7Q== + "@rollup/rollup-freebsd-x64@4.35.0": version "4.35.0" resolved "https://registry.yarnpkg.com/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.35.0.tgz#cd932d3ec679711efd65ca25821fb318e25b7ce4" integrity sha512-2mpHCeRuD1u/2kruUiHSsnjWtHjqVbzhBkNVQ1aVD63CcexKVcQGwJ2g5VphOd84GvxfSvnnlEyBtQCE5hxVVw== +"@rollup/rollup-linux-arm-gnueabihf@4.34.8": + version "4.34.8" + resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.34.8.tgz#980d6061e373bfdaeb67925c46d2f8f9b3de537f" + integrity sha512-A4iphFGNkWRd+5m3VIGuqHnG3MVnqKe7Al57u9mwgbyZ2/xF9Jio72MaY7xxh+Y87VAHmGQr73qoKL9HPbXj1g== + "@rollup/rollup-linux-arm-gnueabihf@4.35.0": version "4.35.0" resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.35.0.tgz#d300b74c6f805474225632f185daaeae760ac2bb" integrity sha512-mrA0v3QMy6ZSvEuLs0dMxcO2LnaCONs1Z73GUDBHWbY8tFFocM6yl7YyMu7rz4zS81NDSqhrUuolyZXGi8TEqg== +"@rollup/rollup-linux-arm-musleabihf@4.34.8": + version "4.34.8" + resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.34.8.tgz#f91a90f30dc00d5a64ac2d9bbedc829cd3cfaa78" + integrity sha512-S0lqKLfTm5u+QTxlFiAnb2J/2dgQqRy/XvziPtDd1rKZFXHTyYLoVL58M/XFwDI01AQCDIevGLbQrMAtdyanpA== + "@rollup/rollup-linux-arm-musleabihf@4.35.0": version "4.35.0" resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.35.0.tgz#2caac622380f314c41934ed1e68ceaf6cc380cc3" integrity sha512-DnYhhzcvTAKNexIql8pFajr0PiDGrIsBYPRvCKlA5ixSS3uwo/CWNZxB09jhIapEIg945KOzcYEAGGSmTSpk7A== +"@rollup/rollup-linux-arm64-gnu@4.34.8": + version "4.34.8" + resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.34.8.tgz#fac700fa5c38bc13a0d5d34463133093da4c92a0" + integrity sha512-jpz9YOuPiSkL4G4pqKrus0pn9aYwpImGkosRKwNi+sJSkz+WU3anZe6hi73StLOQdfXYXC7hUfsQlTnjMd3s1A== + "@rollup/rollup-linux-arm64-gnu@4.35.0": version "4.35.0" resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.35.0.tgz#1ec841650b038cc15c194c26326483fd7ebff3e3" integrity sha512-uagpnH2M2g2b5iLsCTZ35CL1FgyuzzJQ8L9VtlJ+FckBXroTwNOaD0z0/UF+k5K3aNQjbm8LIVpxykUOQt1m/A== +"@rollup/rollup-linux-arm64-musl@4.34.8": + version "4.34.8" + resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.34.8.tgz#f50ecccf8c78841ff6df1706bc4782d7f62bf9c3" + integrity sha512-KdSfaROOUJXgTVxJNAZ3KwkRc5nggDk+06P6lgi1HLv1hskgvxHUKZ4xtwHkVYJ1Rep4GNo+uEfycCRRxht7+Q== + "@rollup/rollup-linux-arm64-musl@4.35.0": version "4.35.0" resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.35.0.tgz#2fc70a446d986e27f6101ea74e81746987f69150" integrity sha512-XQxVOCd6VJeHQA/7YcqyV0/88N6ysSVzRjJ9I9UA/xXpEsjvAgDTgH3wQYz5bmr7SPtVK2TsP2fQ2N9L4ukoUg== +"@rollup/rollup-linux-loongarch64-gnu@4.34.8": + version "4.34.8" + resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-loongarch64-gnu/-/rollup-linux-loongarch64-gnu-4.34.8.tgz#5869dc0b28242da6553e2b52af41374f4038cd6e" + integrity sha512-NyF4gcxwkMFRjgXBM6g2lkT58OWztZvw5KkV2K0qqSnUEqCVcqdh2jN4gQrTn/YUpAcNKyFHfoOZEer9nwo6uQ== + "@rollup/rollup-linux-loongarch64-gnu@4.35.0": version "4.35.0" resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-loongarch64-gnu/-/rollup-linux-loongarch64-gnu-4.35.0.tgz#561bd045cd9ce9e08c95f42e7a8688af8c93d764" integrity sha512-5pMT5PzfgwcXEwOaSrqVsz/LvjDZt+vQ8RT/70yhPU06PTuq8WaHhfT1LW+cdD7mW6i/J5/XIkX/1tCAkh1W6g== +"@rollup/rollup-linux-powerpc64le-gnu@4.34.8": + version "4.34.8" + resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-powerpc64le-gnu/-/rollup-linux-powerpc64le-gnu-4.34.8.tgz#5cdd9f851ce1bea33d6844a69f9574de335f20b1" + integrity sha512-LMJc999GkhGvktHU85zNTDImZVUCJ1z/MbAJTnviiWmmjyckP5aQsHtcujMjpNdMZPT2rQEDBlJfubhs3jsMfw== + "@rollup/rollup-linux-powerpc64le-gnu@4.35.0": version "4.35.0" resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-powerpc64le-gnu/-/rollup-linux-powerpc64le-gnu-4.35.0.tgz#45d849a0b33813f33fe5eba9f99e0ff15ab5caad" integrity sha512-c+zkcvbhbXF98f4CtEIP1EBA/lCic5xB0lToneZYvMeKu5Kamq3O8gqrxiYYLzlZH6E3Aq+TSW86E4ay8iD8EA== +"@rollup/rollup-linux-riscv64-gnu@4.34.8": + version "4.34.8" + resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.34.8.tgz#ef5dc37f4388f5253f0def43e1440ec012af204d" + integrity sha512-xAQCAHPj8nJq1PI3z8CIZzXuXCstquz7cIOL73HHdXiRcKk8Ywwqtx2wrIy23EcTn4aZ2fLJNBB8d0tQENPCmw== + "@rollup/rollup-linux-riscv64-gnu@4.35.0": version "4.35.0" resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.35.0.tgz#78dde3e6fcf5b5733a97d0a67482d768aa1e83a5" integrity sha512-s91fuAHdOwH/Tad2tzTtPX7UZyytHIRR6V4+2IGlV0Cej5rkG0R61SX4l4y9sh0JBibMiploZx3oHKPnQBKe4g== +"@rollup/rollup-linux-s390x-gnu@4.34.8": + version "4.34.8" + resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.34.8.tgz#7dbc3ccbcbcfb3e65be74538dfb6e8dd16178fde" + integrity sha512-DdePVk1NDEuc3fOe3dPPTb+rjMtuFw89gw6gVWxQFAuEqqSdDKnrwzZHrUYdac7A7dXl9Q2Vflxpme15gUWQFA== + "@rollup/rollup-linux-s390x-gnu@4.35.0": version "4.35.0" resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.35.0.tgz#2e34835020f9e03dfb411473a5c2a0e8a9c5037b" integrity sha512-hQRkPQPLYJZYGP+Hj4fR9dDBMIM7zrzJDWFEMPdTnTy95Ljnv0/4w/ixFw3pTBMEuuEuoqtBINYND4M7ujcuQw== +"@rollup/rollup-linux-x64-gnu@4.34.8": + version "4.34.8" + resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.34.8.tgz#5783fc0adcab7dc069692056e8ca8d83709855ce" + integrity sha512-8y7ED8gjxITUltTUEJLQdgpbPh1sUQ0kMTmufRF/Ns5tI9TNMNlhWtmPKKHCU0SilX+3MJkZ0zERYYGIVBYHIA== + "@rollup/rollup-linux-x64-gnu@4.35.0": version "4.35.0" resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.35.0.tgz#4f9774beddc6f4274df57ac99862eb23040de461" integrity sha512-Pim1T8rXOri+0HmV4CdKSGrqcBWX0d1HoPnQ0uw0bdp1aP5SdQVNBy8LjYncvnLgu3fnnCt17xjWGd4cqh8/hA== +"@rollup/rollup-linux-x64-musl@4.34.8": + version "4.34.8" + resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.34.8.tgz#00b6c29b298197a384e3c659910b47943003a678" + integrity sha512-SCXcP0ZpGFIe7Ge+McxY5zKxiEI5ra+GT3QRxL0pMMtxPfpyLAKleZODi1zdRHkz5/BhueUrYtYVgubqe9JBNQ== + "@rollup/rollup-linux-x64-musl@4.35.0": version "4.35.0" resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.35.0.tgz#dfcff2c1aed518b3d23ccffb49afb349d74fb608" integrity sha512-QysqXzYiDvQWfUiTm8XmJNO2zm9yC9P/2Gkrwg2dH9cxotQzunBHYr6jk4SujCTqnfGxduOmQcI7c2ryuW8XVg== +"@rollup/rollup-win32-arm64-msvc@4.34.8": + version "4.34.8" + resolved "https://registry.yarnpkg.com/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.34.8.tgz#cbfee01f1fe73791c35191a05397838520ca3cdd" + integrity sha512-YHYsgzZgFJzTRbth4h7Or0m5O74Yda+hLin0irAIobkLQFRQd1qWmnoVfwmKm9TXIZVAD0nZ+GEb2ICicLyCnQ== + "@rollup/rollup-win32-arm64-msvc@4.35.0": version "4.35.0" resolved "https://registry.yarnpkg.com/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.35.0.tgz#b0b37e2d77041e3aa772f519291309abf4c03a84" integrity sha512-OUOlGqPkVJCdJETKOCEf1mw848ZyJ5w50/rZ/3IBQVdLfR5jk/6Sr5m3iO2tdPgwo0x7VcncYuOvMhBWZq8ayg== +"@rollup/rollup-win32-ia32-msvc@4.34.8": + version "4.34.8" + resolved "https://registry.yarnpkg.com/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.34.8.tgz#95cdbdff48fe6c948abcf6a1d500b2bd5ce33f62" + integrity sha512-r3NRQrXkHr4uWy5TOjTpTYojR9XmF0j/RYgKCef+Ag46FWUTltm5ziticv8LdNsDMehjJ543x/+TJAek/xBA2w== + "@rollup/rollup-win32-ia32-msvc@4.35.0": version "4.35.0" resolved "https://registry.yarnpkg.com/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.35.0.tgz#5b5a40e44a743ddc0e06b8e1b3982f856dc9ce0a" integrity sha512-2/lsgejMrtwQe44glq7AFFHLfJBPafpsTa6JvP2NGef/ifOa4KBoglVf7AKN7EV9o32evBPRqfg96fEHzWo5kw== +"@rollup/rollup-win32-x64-msvc@4.34.8": + version "4.34.8" + resolved "https://registry.yarnpkg.com/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.34.8.tgz#4cdb2cfae69cdb7b1a3cc58778e820408075e928" + integrity sha512-U0FaE5O1BCpZSeE6gBl3c5ObhePQSfk9vDRToMmTkbhCOgW4jqvtS5LGyQ76L1fH8sM0keRp4uDTsbjiUyjk0g== + "@rollup/rollup-win32-x64-msvc@4.35.0": version "4.35.0" resolved "https://registry.yarnpkg.com/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.35.0.tgz#05f25dbc9981bee1ae6e713daab10397044a46ca" @@ -7898,13 +7895,6 @@ resolved "https://registry.yarnpkg.com/@types/diff-match-patch/-/diff-match-patch-1.0.36.tgz#dcef10a69d357fe9d43ac4ff2eca6b85dbf466af" integrity sha512-xFdR6tkm0MWvBfO8xXCSsinYxHcqkQUlcHeSpMC2ukzOb6lwQAfDmW+Qt0AvlGd8HpsS28qKsB+oPeJn9I39jg== -"@types/duplexify@^3.6.0": - version "3.6.0" - resolved "https://registry.yarnpkg.com/@types/duplexify/-/duplexify-3.6.0.tgz#dfc82b64bd3a2168f5bd26444af165bf0237dcd8" - integrity sha512-5zOA53RUlzN74bvrSGwjudssD9F3a797sDZQkiYpUOxW+WHaXTCPz4/d5Dgi6FKnOqZ2CpaTo0DhgIfsXAOE/A== - dependencies: - "@types/node" "*" - "@types/ember-resolver@5.0.13": version "5.0.13" resolved "https://registry.yarnpkg.com/@types/ember-resolver/-/ember-resolver-5.0.13.tgz#db66678076ca625ed80b629c09619ae85c1c1f7a" @@ -8294,7 +8284,7 @@ resolved "https://registry.yarnpkg.com/@types/json5/-/json5-0.0.29.tgz#ee28707ae94e11d2b827bcbe5270bcea7f3e71ee" integrity sha1-7ihweulOEdK4J7y+UnC86n8+ce4= -"@types/long@^4.0.0", "@types/long@^4.0.1": +"@types/long@^4.0.0": version "4.0.2" resolved "https://registry.yarnpkg.com/@types/long/-/long-4.0.2.tgz#b74129719fc8d11c01868010082d483b7545591a" integrity sha512-MqTGEo5bj5t157U6fA/BiDynNkn0YknVdh48CMPkTSpFTVmvao5UQmm7uEF6xBEo7qIMAlY/JSleYaE6VOdpaA== @@ -8392,7 +8382,7 @@ dependencies: "@types/node" "*" -"@types/node@*", "@types/node@>=10.0.0", "@types/node@>=12.12.47", "@types/node@>=13.7.0", "@types/node@>=18": +"@types/node@*", "@types/node@>=10.0.0", "@types/node@>=18": version "22.10.2" resolved "https://registry.yarnpkg.com/@types/node/-/node-22.10.2.tgz#a485426e6d1fdafc7b0d4c7b24e2c78182ddabb9" integrity sha512-Xxr6BBRCAOQixvonOye19wnzyDiUtTeqldOOmj3CkeblonbccA12PFwlufvRdrpjXxqnmUaeiU5EOA+7s5diUQ== @@ -11052,7 +11042,7 @@ bluebird@^3.4.6, bluebird@^3.7.2: resolved "https://registry.yarnpkg.com/bluebird/-/bluebird-3.7.2.tgz#9f229c15be272454ffa973ace0dbee79a1b0c36f" integrity sha512-XpNj6GDQzdfW+r2Wnn7xiSAd7TM3jzkxGXBGTtWKuSXv1xUV+azxAm8jdWZN06QTQk+2N2XB9jRDkvbmQmcRtg== -body-parser@1.20.3, body-parser@^1.18.3, body-parser@^1.19.0, body-parser@^1.20.3: +body-parser@1.20.3, body-parser@^1.19.0, body-parser@^1.20.3: version "1.20.3" resolved "https://registry.yarnpkg.com/body-parser/-/body-parser-1.20.3.tgz#1953431221c6fb5cd63c4b36d53fab0928e548c6" integrity sha512-7rAxByjUMqQ3/bHJy7D6OGXvx/MMc4IqBn/X0fcM1QUcAItpZrBEYhWGem+tzXH90c+G01ypMcYJBO9Y30203g== @@ -15955,7 +15945,7 @@ exponential-backoff@^3.1.1: resolved "https://registry.yarnpkg.com/exponential-backoff/-/exponential-backoff-3.1.1.tgz#64ac7526fe341ab18a39016cd22c787d01e00bf6" integrity sha512-dX7e/LHVJ6W3DE1MHWi9S1EYzDESENfLrYohG2G++ovZrYOkm4Knwa0mc1cn84xJOR4KEU0WSchhLbd0UklbHw== -express@4.21.1, express@^4.10.7, express@^4.16.4, express@^4.17.1, express@^4.17.3, express@^4.18.1, express@^4.21.1: +express@4.21.1, express@^4.10.7, express@^4.17.1, express@^4.17.3, express@^4.18.1, express@^4.21.1: version "4.21.1" resolved "https://registry.yarnpkg.com/express/-/express-4.21.1.tgz#9dae5dda832f16b4eec941a4e44aa89ec481b281" integrity sha512-YSFlK1Ee0/GC8QaO91tHcDxJiE/X4FbpAyQWkxAvG6AXCuR65YzK8ua6D9hvi/TzUfZMpc+BwuM1IPw8fmQBiQ== @@ -16147,7 +16137,7 @@ fast-sourcemap-concat@^2.1.0: source-map-url "^0.3.0" sourcemap-validator "^1.1.0" -fast-text-encoding@^1.0.0, fast-text-encoding@^1.0.3: +fast-text-encoding@^1.0.0: version "1.0.3" resolved "https://registry.yarnpkg.com/fast-text-encoding/-/fast-text-encoding-1.0.3.tgz#ec02ac8e01ab8a319af182dae2681213cfe9ce53" integrity sha512-dtm4QZH9nZtcDt8qJiOH9fcQd1NAgi+K1O2DbE6GG1PPCK/BWfOH3idCTRQ4ImXRUOyopDEgDEnVEE7Y/2Wrig== @@ -17266,22 +17256,7 @@ gonzales-pe@^4.3.0: dependencies: minimist "^1.2.5" -google-auth-library@^6.1.1: - version "6.1.6" - resolved "https://registry.yarnpkg.com/google-auth-library/-/google-auth-library-6.1.6.tgz#deacdcdb883d9ed6bac78bb5d79a078877fdf572" - integrity sha512-Q+ZjUEvLQj/lrVHF/IQwRo6p3s8Nc44Zk/DALsN+ac3T4HY/g/3rrufkgtl+nZ1TW7DNAw5cTChdVp4apUXVgQ== - dependencies: - arrify "^2.0.0" - base64-js "^1.3.0" - ecdsa-sig-formatter "^1.0.11" - fast-text-encoding "^1.0.0" - gaxios "^4.0.0" - gcp-metadata "^4.2.0" - gtoken "^5.0.4" - jws "^4.0.0" - lru-cache "^6.0.0" - -google-auth-library@^7.0.0, google-auth-library@^7.0.2: +google-auth-library@^7.0.2: version "7.0.3" resolved "https://registry.yarnpkg.com/google-auth-library/-/google-auth-library-7.0.3.tgz#a38e853722ac1a4f14a7ff4d170fd0b0bf37766b" integrity sha512-6wJNYqY1QUr5I2lWaUkkzOT2b9OCNhNQrdFOt/bsBbGb7T7NCdEvrBsXraUm+KTUGk2xGlQ7m9RgUd4Llcw8NQ== @@ -17296,23 +17271,6 @@ google-auth-library@^7.0.0, google-auth-library@^7.0.2: jws "^4.0.0" lru-cache "^6.0.0" -google-gax@^2.9.0, google-gax@^2.9.2: - version "2.11.2" - resolved "https://registry.yarnpkg.com/google-gax/-/google-gax-2.11.2.tgz#9ef7773b94aaa61c4588fb2408d62e8444995026" - integrity sha512-PNqXv7Oi5XBMgoMWVxLZHUidfMv7cPHrDSDXqLyEd6kY6pqFnVKC8jt2T1df4JPSc2+VLPdeo6L7X9mbdQG8Xw== - dependencies: - "@grpc/grpc-js" "~1.2.0" - "@grpc/proto-loader" "^0.5.1" - "@types/long" "^4.0.0" - abort-controller "^3.0.0" - duplexify "^4.0.0" - fast-text-encoding "^1.0.3" - google-auth-library "^7.0.2" - is-stream-ended "^0.1.4" - node-fetch "^2.6.1" - protobufjs "^6.10.2" - retry-request "^4.0.0" - google-p12-pem@^3.0.3: version "3.1.4" resolved "https://registry.yarnpkg.com/google-p12-pem/-/google-p12-pem-3.1.4.tgz#123f7b40da204de4ed1fbf2fd5be12c047fc8b3b" @@ -18925,11 +18883,6 @@ is-ssh@^1.4.0: dependencies: protocols "^2.0.1" -is-stream-ended@^0.1.4: - version "0.1.4" - resolved "https://registry.yarnpkg.com/is-stream-ended/-/is-stream-ended-0.1.4.tgz#f50224e95e06bce0e356d440a4827cd35b267eda" - integrity sha512-xj0XPvmr7bQFTvirqnFr50o0hQIh6ZItDqloxt5aJrR4NQsYeSsyFQERYGCAzfindAcnKjINnwEEgLx4IqVzQw== - is-stream@2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/is-stream/-/is-stream-2.0.0.tgz#bde9c32680d6fae04129d6ac9d921ce7815f78e3" @@ -20581,7 +20534,7 @@ lodash.assign@^3.2.0: lodash._createassigner "^3.0.0" lodash.keys "^3.0.0" -lodash.camelcase@^4.1.1, lodash.camelcase@^4.3.0: +lodash.camelcase@^4.1.1: version "4.3.0" resolved "https://registry.yarnpkg.com/lodash.camelcase/-/lodash.camelcase-4.3.0.tgz#b28aa6288a2b9fc651035c7711f65ab6190331a6" integrity sha1-soqmKIorn8ZRA1x3EfZathkDMaY= @@ -20715,11 +20668,6 @@ lodash.restparam@^3.0.0: resolved "https://registry.yarnpkg.com/lodash.restparam/-/lodash.restparam-3.6.1.tgz#936a4e309ef330a7645ed4145986c85ae5b20805" integrity sha1-k2pOMJ7zMKdkXtQUWYbIWuWyCAU= -lodash.snakecase@^4.1.1: - version "4.1.1" - resolved "https://registry.yarnpkg.com/lodash.snakecase/-/lodash.snakecase-4.1.1.tgz#39d714a35357147837aefd64b5dcbb16becd8f8d" - integrity sha1-OdcUo1NXFHg3rv1ktdy7Fr7Nj40= - lodash.sortby@^4.7.0: version "4.7.0" resolved "https://registry.yarnpkg.com/lodash.sortby/-/lodash.sortby-4.7.0.tgz#edd14c824e2cc9c1e0b0a1b42bb5210516a42438" @@ -23386,7 +23334,7 @@ ohash@^1.1.3, ohash@^1.1.4: resolved "https://registry.yarnpkg.com/ohash/-/ohash-1.1.4.tgz#ae8d83014ab81157d2c285abf7792e2995fadd72" integrity sha512-FlDryZAahJmEF3VR3w1KogSEdWX3WhA5GPakFx4J81kEAiHyLMpdLLElS8n8dfNadMgAne/MywcvmogzscVt4g== -on-finished@2.4.1, on-finished@^2.3.0: +on-finished@2.4.1: 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== @@ -25321,25 +25269,6 @@ property-information@^6.0.0: resolved "https://registry.yarnpkg.com/property-information/-/property-information-6.3.0.tgz#ba4a06ec6b4e1e90577df9931286953cdf4282c3" integrity sha512-gVNZ74nqhRMiIUYWGQdosYetaKc83x8oT41a0LlV3AAFCAZwCpg4vmGkq8t34+cUhp3cnM4XDiU/7xlgK7HGrg== -protobufjs@^6.10.2, protobufjs@^6.8.6: - version "6.11.4" - resolved "https://registry.yarnpkg.com/protobufjs/-/protobufjs-6.11.4.tgz#29a412c38bf70d89e537b6d02d904a6f448173aa" - integrity sha512-5kQWPaJHi1WoCpjTGszzQ32PG2F4+wRY6BmAT4Vfw56Q2FZ4YZzK20xUYQH4YkfehY1e6QSICrJquM6xXZNcrw== - dependencies: - "@protobufjs/aspromise" "^1.1.2" - "@protobufjs/base64" "^1.1.2" - "@protobufjs/codegen" "^2.0.4" - "@protobufjs/eventemitter" "^1.1.0" - "@protobufjs/fetch" "^1.1.0" - "@protobufjs/float" "^1.0.2" - "@protobufjs/inquire" "^1.1.0" - "@protobufjs/path" "^1.1.2" - "@protobufjs/pool" "^1.1.0" - "@protobufjs/utf8" "^1.1.0" - "@types/long" "^4.0.1" - "@types/node" ">=13.7.0" - long "^4.0.0" - protocols@^2.0.0, protocols@^2.0.1: version "2.0.1" resolved "https://registry.yarnpkg.com/protocols/-/protocols-2.0.1.tgz#8f155da3fc0f32644e83c5782c8e8212ccf70a86" @@ -26457,7 +26386,7 @@ retext@^8.1.0: retext-stringify "^3.0.0" unified "^10.0.0" -retry-request@^4.0.0, retry-request@^4.1.1: +retry-request@^4.1.1: version "4.1.3" resolved "https://registry.yarnpkg.com/retry-request/-/retry-request-4.1.3.tgz#d5f74daf261372cff58d08b0a1979b4d7cab0fde" integrity sha512-QnRZUpuPNgX0+D1xVxul6DbJ9slvo4Rm6iV/dn63e048MvGbUZiKySVt6Tenp04JqmchxjiLltGerOJys7kJYQ== @@ -26931,7 +26860,7 @@ semver@7.5.3: dependencies: lru-cache "^6.0.0" -semver@7.x, semver@^7.0.0, semver@^7.1.1, semver@^7.1.3, semver@^7.2.1, semver@^7.3.2, semver@^7.3.4, semver@^7.3.5, semver@^7.3.7, semver@^7.3.8, semver@^7.5.0, semver@^7.5.2, semver@^7.5.3, semver@^7.5.4, semver@^7.6.2, semver@^7.6.3: +semver@7.x, semver@^7.0.0, semver@^7.1.1, semver@^7.2.1, semver@^7.3.2, semver@^7.3.4, semver@^7.3.5, semver@^7.3.7, semver@^7.3.8, semver@^7.5.0, semver@^7.5.2, semver@^7.5.3, semver@^7.5.4, semver@^7.6.2, semver@^7.6.3: version "7.7.1" resolved "https://registry.yarnpkg.com/semver/-/semver-7.7.1.tgz#abd5098d82b18c6c81f6074ff2647fd3e7220c9f" integrity sha512-hlq8tAfn0m/61p4BVRcPzIGr6LKiMwo4VM6dGi6pt4qcRkmNzTcWq6eCEjEh+qXjkMDvPlOFFSGwQjoEa6gyMA== From c38315ea0cbb0faf300c388e6123316e2b57e9d6 Mon Sep 17 00:00:00 2001 From: Tim Fish Date: Tue, 25 Mar 2025 23:39:01 -0600 Subject: [PATCH 22/28] chore(jest): Final Jest cleanup (#15549) resolves https://github.com/getsentry/sentry-javascript/issues/11084 --- .eslintrc.js | 2 +- .gitignore | 2 - .vscode/launch.json | 29 - CONTRIBUTING.md | 17 - .../node-integration-tests/.eslintrc.js | 1 - dev-packages/node-integration-tests/README.md | 6 +- .../node-integration-tests/tsconfig.json | 2 +- jest/jest.config.js | 43 - package.json | 7 +- packages/core/test/utils-hoist/misc.test.ts | 2 - .../core/test/utils-hoist/normalize.test.ts | 2 +- packages/core/test/utils-hoist/object.test.ts | 11 +- packages/eslint-config-sdk/package.json | 1 - packages/eslint-config-sdk/src/base.js | 4 - packages/gatsby/tsconfig.plugin.json | 1 - packages/node/jest.config.js | 1 - packages/node/test/helpers/conditional.ts | 1 - packages/nuxt/tsconfig.test.json | 2 +- packages/react-router/tsconfig.test.json | 2 +- .../remix/test/integration/tsconfig.test.json | 2 +- packages/solidstart/tsconfig.test.json | 2 +- tsconfig-templates/tsconfig.test.json | 2 +- tsconfig.dev.json | 4 +- yarn.lock | 1333 ++--------------- 24 files changed, 121 insertions(+), 1358 deletions(-) delete mode 100644 jest/jest.config.js delete mode 100644 packages/node/jest.config.js diff --git a/.eslintrc.js b/.eslintrc.js index 53944e16d9dc..64452fdd7c15 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -38,7 +38,7 @@ module.exports = { }, }, { - files: ['jest/**/*.ts', 'scripts/**/*.ts'], + files: ['scripts/**/*.ts'], parserOptions: { project: ['tsconfig.dev.json'], }, diff --git a/.gitignore b/.gitignore index be853254d612..f784704ac31a 100644 --- a/.gitignore +++ b/.gitignore @@ -17,8 +17,6 @@ scratch/ # side effects of running AWS lambda layer zip action locally dist-serverless/ sentry-node-serverless-*.zip -# transpiled transformers -jest/transformers/*.js # node tarballs packages/*/sentry-*.tgz .nxcache diff --git a/.vscode/launch.json b/.vscode/launch.json index 409d7264a7e0..87d8002f6e7b 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -52,35 +52,6 @@ "internalConsoleOptions": "openOnSessionStart", "outputCapture": "std" }, - // Run a specific test file in watch mode (must have file in currently active tab when hitting the play button). - // NOTE: If you try to run this and VSCode complains that the command `shellCommand.execute` can't be found, go - // install the recommended extension Tasks Shell Input. - { - "name": "Debug unit tests - just open file", - "type": "node", - "cwd": "${workspaceFolder}/packages/${input:getPackageName}", - "request": "launch", - "program": "${workspaceFolder}/node_modules/.bin/jest", - "args": [ - "--watch", - // this runs one test at a time, rather than running them in parallel (necessary for debugging so that you know - // you're hitting a single test's breakpoints, in order) - "--runInBand", - // coverage messes up the source maps - "--coverage", - "false", - // remove this to run all package tests - "${relativeFile}" - ], - "sourceMaps": true, - "smartStep": true, - // otherwise it goes to the VSCode debug terminal, which prints the test output or console logs (depending on - // "outputCapture" option here; default is to show console logs), but not both - "console": "integratedTerminal", - // since we're not using it, don't automatically switch to it - "internalConsoleOptions": "neverOpen" - }, - // Run a specific test file in watch mode (must have file in currently active tab when hitting the play button). // NOTE: If you try to run this and VSCode complains that the command `shellCommand.execute` can't be found, go // install the recommended extension Tasks Shell Input. diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index b74d59693a18..8d486d6718c1 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -73,23 +73,6 @@ the tests in each location. Check out the `scripts` entry of the corresponding ` Note: you must run `yarn build` before `yarn test` will work. -## Debugging Tests - -If you run into trouble writing tests and need to debug one of them, you can do so using VSCode's debugger. - -0. If you don't already have it installed, install the Tasks Shell Input extension, which you'll find in the Extensions - tab in the sidebar as one of the recommended workspace extensions. - -1. Place breakpoints or `debugger` statements in the test or the underlying code wherever you'd like `jest` to pause. -2. Open the file containing the test in question, and make sure its tab is active (so you can see the file's contents). -3. Switch to the debugger in the sidebar and choose `Debug unit tests - just open file` from the dropdown. -4. Click the green "play" button to run the tests in the open file in watch mode. - -Pro tip: If any of your breakpoints are in code run by multiple tests, and you run the whole test file, you'll land on -those breakpoints over and over again, in the middle of tests you don't care about. To avoid this, replace the test's -initial `it` or `test` with `it.only` or `test.only`. That way, when you hit a breakpoint, you'll know you got there are -part of the buggy test. - ## Debug Build Flags Throughout the codebase, you will find a `__DEBUG_BUILD__` constant. This flag serves two purposes: diff --git a/dev-packages/node-integration-tests/.eslintrc.js b/dev-packages/node-integration-tests/.eslintrc.js index 51b7dfbb7ed3..8d5573907fb9 100644 --- a/dev-packages/node-integration-tests/.eslintrc.js +++ b/dev-packages/node-integration-tests/.eslintrc.js @@ -1,7 +1,6 @@ module.exports = { env: { node: true, - jest: true, }, extends: ['../../.eslintrc.js'], overrides: [ diff --git a/dev-packages/node-integration-tests/README.md b/dev-packages/node-integration-tests/README.md index 3f7abd7b5727..c920f05d5e31 100644 --- a/dev-packages/node-integration-tests/README.md +++ b/dev-packages/node-integration-tests/README.md @@ -23,7 +23,7 @@ folders containing test scenarios and assertions. `runServer` also accepts an optional `scenarioPath` argument for non-standard usage. `test.ts` is required for each test case, and contains the server runner logic, request interceptors for Sentry -requests, and assertions. Test server, interceptors and assertions are all run on the same Jest thread. +requests, and assertions. Test server, interceptors and assertions are all run on the same Vitest thread. ### Utilities @@ -40,12 +40,10 @@ Tests can be run locally with: `yarn test` -To run tests with Jest's watch mode: +To run tests with Vitest's watch mode: `yarn test:watch` To filter tests by their title: `yarn test -t "set different properties of a scope"` - -You can refer to [Jest documentation](https://jestjs.io/docs/cli) for other CLI options. diff --git a/dev-packages/node-integration-tests/tsconfig.json b/dev-packages/node-integration-tests/tsconfig.json index 9ef29d983a08..1cd6c0aec734 100644 --- a/dev-packages/node-integration-tests/tsconfig.json +++ b/dev-packages/node-integration-tests/tsconfig.json @@ -9,6 +9,6 @@ "lib": ["DOM", "ES2018"], // package-specific options "esModuleInterop": true, - "types": ["node", "jest"] + "types": ["node"] } } diff --git a/jest/jest.config.js b/jest/jest.config.js deleted file mode 100644 index 804519918182..000000000000 --- a/jest/jest.config.js +++ /dev/null @@ -1,43 +0,0 @@ -module.exports = { - // this is the package root, even when tests are being run at the repo level - rootDir: process.cwd(), - collectCoverage: true, - transform: { - '^.+\\.(ts|tsx)$': 'ts-jest', - }, - coverageDirectory: '/coverage', - moduleFileExtensions: ['js', 'ts', 'tsx'], - testMatch: ['/**/*.test.ts', '/**/*.test.tsx'], - moduleNameMapper: { - '^axios$': require.resolve('axios'), - '@opentelemetry/semantic-conventions/incubating': require.resolve('@opentelemetry/semantic-conventions/incubating'), - }, - globals: { - 'ts-jest': { - tsconfig: '/tsconfig.test.json', - diagnostics: { - // Ignore this warning for tests, we do not care about this - ignoreCodes: ['TS151001'], - }, - }, - __DEBUG_BUILD__: true, - }, - testPathIgnorePatterns: ['/build/', '/node_modules/'], - - // On CI, we do not need the pretty CLI output, as it makes logs harder to parse - ...(process.env.CI - ? { - coverageReporters: ['json', 'lcov', 'clover'], - reporters: [ - 'default', - [ - 'jest-junit', - { - outputName: 'jest.junit.xml', - classNameTemplate: '{filepath}', - }, - ], - ], - } - : {}), -}; diff --git a/package.json b/package.json index 138ed7d787de..8985b40af8d0 100644 --- a/package.json +++ b/package.json @@ -15,7 +15,7 @@ "circularDepCheck": "lerna run circularDepCheck", "clean": "run-s clean:build clean:caches", "clean:build": "lerna run clean", - "clean:caches": "yarn rimraf eslintcache .nxcache .nx && yarn jest --clearCache", + "clean:caches": "yarn rimraf eslintcache .nxcache .nx", "clean:deps": "lerna clean --yes && rm -rf node_modules && yarn", "clean:tarballs": "rimraf {packages,dev-packages}/*/*.tgz", "clean:watchman": "watchman watch-del \".\"", @@ -110,7 +110,6 @@ "@rollup/pluginutils": "^5.1.0", "@size-limit/file": "~11.1.6", "@size-limit/webpack": "~11.1.6", - "@types/jest": "^27.4.1", "@types/jsdom": "^21.1.6", "@types/node": "^18.19.1", "@vitest/coverage-v8": "^2.1.8", @@ -118,9 +117,6 @@ "downlevel-dts": "~0.11.0", "es-check": "^7.2.1", "eslint": "7.32.0", - "jest": "^27.5.1", - "jest-environment-node": "^27.5.1", - "jest-junit": "^16.0.0", "jsdom": "^21.1.2", "lerna": "7.1.1", "madge": "7.0.0", @@ -133,7 +129,6 @@ "rollup-plugin-license": "^3.3.1", "size-limit": "~11.1.6", "sucrase": "^3.35.0", - "ts-jest": "^27.1.4", "ts-node": "10.9.1", "typescript": "~5.0.0", "vitest": "^2.1.8", diff --git a/packages/core/test/utils-hoist/misc.test.ts b/packages/core/test/utils-hoist/misc.test.ts index ea2d03992fe8..0bcc317a017c 100644 --- a/packages/core/test/utils-hoist/misc.test.ts +++ b/packages/core/test/utils-hoist/misc.test.ts @@ -291,8 +291,6 @@ describe('checkOrSetAlreadyCaught()', () => { describe('uuid4 generation', () => { const uuid4Regex = /^[0-9A-F]{12}[4][0-9A-F]{3}[89AB][0-9A-F]{15}$/i; - // Jest messes with the global object, so there is no global crypto object in any node version - // For this reason we need to create our own crypto object for each test to cover all the code paths it('returns valid uuid v4 ids via Math.random', () => { for (let index = 0; index < 1_000; index++) { expect(uuid4()).toMatch(uuid4Regex); diff --git a/packages/core/test/utils-hoist/normalize.test.ts b/packages/core/test/utils-hoist/normalize.test.ts index 7fbecd8608e7..e08c7187c839 100644 --- a/packages/core/test/utils-hoist/normalize.test.ts +++ b/packages/core/test/utils-hoist/normalize.test.ts @@ -1,5 +1,5 @@ /** - * @jest-environment jsdom + * @vitest-environment jsdom */ import { describe, expect, test, vi } from 'vitest'; diff --git a/packages/core/test/utils-hoist/object.test.ts b/packages/core/test/utils-hoist/object.test.ts index a563c1298ccb..bda0a53e3a5c 100644 --- a/packages/core/test/utils-hoist/object.test.ts +++ b/packages/core/test/utils-hoist/object.test.ts @@ -1,5 +1,5 @@ /** - * @jest-environment jsdom + * @vitest-environment jsdom */ import type { WrappedFunction } from '../../src/types-hoist'; @@ -331,17 +331,10 @@ describe('objectify()', () => { }); describe('wraps other primitives with their respective object wrapper classes', () => { - // TODO: There's currently a bug in Jest - if you give it the `Boolean` class, it runs `typeof received === - // 'boolean'` but not `received instanceof Boolean` (the way it correctly does for other primitive wrappers, like - // `Number` and `String). (See https://github.com/facebook/jest/pull/11976.) Once that is fixed and we upgrade jest, - // we can comment the test below back in. (The tests for symbols and bigints are working only because our current - // version of jest is sufficiently old that they're not even considered in the relevant check and just fall to the - // default `instanceof` check jest uses for all unknown classes.) - it.each([ ['number', Number, 1121], ['string', String, 'Dogs are great!'], - // ["boolean", Boolean, true], + ['boolean', Boolean, true], ['symbol', Symbol, Symbol('Maisey')], ])('%s', (_caseName, wrapperClass, primitive) => { const objectifiedPrimitive = objectify(primitive); diff --git a/packages/eslint-config-sdk/package.json b/packages/eslint-config-sdk/package.json index 32b568606f29..2f0ce2b521e4 100644 --- a/packages/eslint-config-sdk/package.json +++ b/packages/eslint-config-sdk/package.json @@ -29,7 +29,6 @@ "eslint-config-prettier": "^6.11.0", "eslint-plugin-deprecation": "^1.5.0", "eslint-plugin-import": "^2.22.0", - "eslint-plugin-jest": "^27.2.2", "eslint-plugin-jsdoc": "^30.0.3", "eslint-plugin-simple-import-sort": "^5.0.3" }, diff --git a/packages/eslint-config-sdk/src/base.js b/packages/eslint-config-sdk/src/base.js index 8c11f26dd925..10e410aaa592 100644 --- a/packages/eslint-config-sdk/src/base.js +++ b/packages/eslint-config-sdk/src/base.js @@ -154,10 +154,6 @@ module.exports = { }, }, { - // Configuration for files in test directories - env: { - jest: true, - }, files: [ 'test.ts', '*.test.ts', diff --git a/packages/gatsby/tsconfig.plugin.json b/packages/gatsby/tsconfig.plugin.json index 88724d075f48..1eb512a6fa0b 100644 --- a/packages/gatsby/tsconfig.plugin.json +++ b/packages/gatsby/tsconfig.plugin.json @@ -5,7 +5,6 @@ "compilerOptions": { // should include all types from `./tsconfig.json` plus types for all test frameworks used - // "types": ["node", "jest"] "declaration": true, "declarationMap": false, "emitDeclarationOnly": true, diff --git a/packages/node/jest.config.js b/packages/node/jest.config.js deleted file mode 100644 index 24f49ab59a4c..000000000000 --- a/packages/node/jest.config.js +++ /dev/null @@ -1 +0,0 @@ -module.exports = require('../../jest/jest.config.js'); diff --git a/packages/node/test/helpers/conditional.ts b/packages/node/test/helpers/conditional.ts index 48df7d821da8..ceea11315db4 100644 --- a/packages/node/test/helpers/conditional.ts +++ b/packages/node/test/helpers/conditional.ts @@ -7,7 +7,6 @@ const NODE_VERSION = parseSemver(process.versions.node).major; * Returns`describe` or `describe.skip` depending on allowed major versions of Node. * * @param {{ min?: number; max?: number }} allowedVersion - * @return {*} {jest.Describe} */ export const conditionalTest = (allowedVersion: { min?: number; max?: number }) => { if (!NODE_VERSION) { diff --git a/packages/nuxt/tsconfig.test.json b/packages/nuxt/tsconfig.test.json index 3fbe012384ee..c41efeacd92f 100644 --- a/packages/nuxt/tsconfig.test.json +++ b/packages/nuxt/tsconfig.test.json @@ -5,6 +5,6 @@ "compilerOptions": { // should include all types from `./tsconfig.json` plus types for all test frameworks used - "types": ["node", "vitest/globals"] + "types": ["node"] } } diff --git a/packages/react-router/tsconfig.test.json b/packages/react-router/tsconfig.test.json index b3625cd0bd3f..65d74ebb843a 100644 --- a/packages/react-router/tsconfig.test.json +++ b/packages/react-router/tsconfig.test.json @@ -4,6 +4,6 @@ "include": ["test/**/*", "vite.config.ts"], "compilerOptions": { - "types": ["node", "vitest/globals"], + "types": ["node"], } } diff --git a/packages/remix/test/integration/tsconfig.test.json b/packages/remix/test/integration/tsconfig.test.json index 8ce7525d33fd..0bb16039ac48 100644 --- a/packages/remix/test/integration/tsconfig.test.json +++ b/packages/remix/test/integration/tsconfig.test.json @@ -4,6 +4,6 @@ "include": ["test/**/*"], "compilerOptions": { - "types": ["node", "vitest/globals"] + "types": ["node"] } } diff --git a/packages/solidstart/tsconfig.test.json b/packages/solidstart/tsconfig.test.json index 51c6c9c9b8ea..62b0f6844441 100644 --- a/packages/solidstart/tsconfig.test.json +++ b/packages/solidstart/tsconfig.test.json @@ -5,7 +5,7 @@ "compilerOptions": { // should include all types from `./tsconfig.json` plus types for all test frameworks used - "types": ["node", "vitest/globals", "vite/client", "@testing-library/jest-dom"], + "types": ["node", "@testing-library/jest-dom"], // other package-specific, test-specific options "jsx": "preserve", diff --git a/tsconfig-templates/tsconfig.test.json b/tsconfig-templates/tsconfig.test.json index af7e36ec0eda..1141567cdfec 100644 --- a/tsconfig-templates/tsconfig.test.json +++ b/tsconfig-templates/tsconfig.test.json @@ -5,7 +5,7 @@ "compilerOptions": { // should include all types from `./tsconfig.json` plus types for all test frameworks used - "types": ["jest"] + "types": [] // other package-specific, test-specific options } diff --git a/tsconfig.dev.json b/tsconfig.dev.json index 8fa9976f16af..a5728ba2f184 100644 --- a/tsconfig.dev.json +++ b/tsconfig.dev.json @@ -2,9 +2,9 @@ { "extends": "./tsconfig.json", - "include": ["**/scripts/**/*.ts", "jest/**/*.ts", "vite/**/*.ts"], + "include": ["**/scripts/**/*.ts", "vite/**/*.ts"], "compilerOptions": { - "types": ["node", "jest", "vitest/globals"] + "types": ["node"] } } diff --git a/yarn.lock b/yarn.lock index 9e8d7830af5d..88ef6070bd84 100644 --- a/yarn.lock +++ b/yarn.lock @@ -84,7 +84,7 @@ debug "^4.3.4" safe-buffer "~5.1.2" -"@adobe/css-tools@^4.0.1", "@adobe/css-tools@^4.3.2": +"@adobe/css-tools@^4.0.1": version "4.4.0" resolved "https://registry.yarnpkg.com/@adobe/css-tools/-/css-tools-4.4.0.tgz#728c484f4e10df03d5a3acd0d8adcbbebff8ad63" integrity sha512-Ff9+ksdQQB3rMncgqDK78uLznstjyfIf2Arnh22pW8kBpLs6rpKDwgnZT46hin5Hl1WzazzK64DOrhSwYpS7bQ== @@ -1273,7 +1273,7 @@ dependencies: "@babel/highlight" "^7.10.4" -"@babel/code-frame@^7.0.0", "@babel/code-frame@^7.10.4", "@babel/code-frame@^7.12.13", "@babel/code-frame@^7.18.6", "@babel/code-frame@^7.23.5", "@babel/code-frame@^7.24.2", "@babel/code-frame@^7.26.2": +"@babel/code-frame@^7.0.0", "@babel/code-frame@^7.10.4", "@babel/code-frame@^7.12.13", "@babel/code-frame@^7.18.6", "@babel/code-frame@^7.23.5", "@babel/code-frame@^7.24.2", "@babel/code-frame@^7.25.9", "@babel/code-frame@^7.26.0", "@babel/code-frame@^7.26.2": version "7.26.2" resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.26.2.tgz#4b5fab97d33338eff916235055f0ebc21e573a85" integrity sha512-RJlIHRueQgwWitWgF8OdFYGZX328Ax5BCemNGlqHfplnRT9ESi8JkFlvaVYbS+UubVY6dpv87Fs2u5M29iNFVQ== @@ -1282,7 +1282,7 @@ js-tokens "^4.0.0" picocolors "^1.0.0" -"@babel/compat-data@^7.17.7", "@babel/compat-data@^7.18.8", "@babel/compat-data@^7.20.5", "@babel/compat-data@^7.22.6", "@babel/compat-data@^7.24.4", "@babel/compat-data@^7.26.5": +"@babel/compat-data@^7.17.7", "@babel/compat-data@^7.18.8", "@babel/compat-data@^7.20.5", "@babel/compat-data@^7.22.6", "@babel/compat-data@^7.24.4", "@babel/compat-data@^7.26.5", "@babel/compat-data@^7.26.8": version "7.26.8" resolved "https://registry.yarnpkg.com/@babel/compat-data/-/compat-data-7.26.8.tgz#821c1d35641c355284d4a870b8a4a7b0c141e367" integrity sha512-oH5UPLMWR3L2wEFLnFJ1TZXqHufiTKAiLfqw5zkhS4dKXLJ10yVztfil/twG8EDTA4F/tvVNw9nOl4ZMslB8rQ== @@ -1308,7 +1308,7 @@ json5 "^2.2.1" semver "^6.3.0" -"@babel/core@^7.1.0", "@babel/core@^7.12.0", "@babel/core@^7.12.3", "@babel/core@^7.16.10", "@babel/core@^7.16.7", "@babel/core@^7.17.2", "@babel/core@^7.17.5", "@babel/core@^7.18.5", "@babel/core@^7.21.0", "@babel/core@^7.21.8", "@babel/core@^7.22.10", "@babel/core@^7.23.0", "@babel/core@^7.23.3", "@babel/core@^7.23.7", "@babel/core@^7.24.4", "@babel/core@^7.24.7", "@babel/core@^7.3.4", "@babel/core@^7.7.2", "@babel/core@^7.8.0": +"@babel/core@^7.12.0", "@babel/core@^7.12.3", "@babel/core@^7.16.10", "@babel/core@^7.16.7", "@babel/core@^7.17.2", "@babel/core@^7.17.5", "@babel/core@^7.18.5", "@babel/core@^7.21.0", "@babel/core@^7.21.8", "@babel/core@^7.22.10", "@babel/core@^7.23.0", "@babel/core@^7.23.3", "@babel/core@^7.23.7", "@babel/core@^7.24.4", "@babel/core@^7.24.7", "@babel/core@^7.3.4": version "7.26.9" resolved "https://registry.yarnpkg.com/@babel/core/-/core-7.26.9.tgz#71838542a4b1e49dfed353d7acbc6eb89f4a76f2" integrity sha512-lWBYIrF7qK5+GjY5Uy+/hEgp8OJWOD/rpy74GplYRhEauvbHDeFB8t5hPOZxCZ0Oxf4Cc36tK51/l3ymJysrKw== @@ -1338,7 +1338,7 @@ "@jridgewell/gen-mapping" "^0.3.2" jsesc "^2.5.1" -"@babel/generator@^7.18.10", "@babel/generator@^7.21.5", "@babel/generator@^7.22.10", "@babel/generator@^7.23.6", "@babel/generator@^7.26.9", "@babel/generator@^7.7.2": +"@babel/generator@^7.18.10", "@babel/generator@^7.21.5", "@babel/generator@^7.22.10", "@babel/generator@^7.23.6", "@babel/generator@^7.26.0", "@babel/generator@^7.26.3", "@babel/generator@^7.26.9": version "7.26.9" resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.26.9.tgz#75a9482ad3d0cc7188a537aa4910bc59db67cbca" integrity sha512-kEWdzjOAUMW4hAyrzJ0ZaTOu9OmpyDIQicIh0zg0EEcEkYXZb2TjtBhnHi2ViX7PKwZqF4xwqfAm299/QMP3lg== @@ -1370,12 +1370,12 @@ dependencies: "@babel/types" "^7.22.15" -"@babel/helper-compilation-targets@^7.12.0", "@babel/helper-compilation-targets@^7.17.7", "@babel/helper-compilation-targets@^7.18.9", "@babel/helper-compilation-targets@^7.20.7", "@babel/helper-compilation-targets@^7.22.6", "@babel/helper-compilation-targets@^7.23.6", "@babel/helper-compilation-targets@^7.26.5": - version "7.26.5" - resolved "https://registry.yarnpkg.com/@babel/helper-compilation-targets/-/helper-compilation-targets-7.26.5.tgz#75d92bb8d8d51301c0d49e52a65c9a7fe94514d8" - integrity sha512-IXuyn5EkouFJscIDuFF5EsiSolseme1s0CZB+QxVugqJLYmKdxI1VfIBOst0SUu4rnk2Z7kqTwmoO1lp3HIfnA== +"@babel/helper-compilation-targets@^7.12.0", "@babel/helper-compilation-targets@^7.17.7", "@babel/helper-compilation-targets@^7.18.9", "@babel/helper-compilation-targets@^7.20.7", "@babel/helper-compilation-targets@^7.22.6", "@babel/helper-compilation-targets@^7.23.6", "@babel/helper-compilation-targets@^7.25.9", "@babel/helper-compilation-targets@^7.26.5": + version "7.27.0" + resolved "https://registry.yarnpkg.com/@babel/helper-compilation-targets/-/helper-compilation-targets-7.27.0.tgz#de0c753b1cd1d9ab55d473c5a5cf7170f0a81880" + integrity sha512-LVk7fbXml0H2xH34dFzKQ7TDZ2G4/rVTOrq9V+icbbadjbVxxeFeDsNHv2SrZeWoA+6ZiTyWYWtScEIW07EAcA== dependencies: - "@babel/compat-data" "^7.26.5" + "@babel/compat-data" "^7.26.8" "@babel/helper-validator-option" "^7.25.9" browserslist "^4.24.0" lru-cache "^5.1.1" @@ -1556,13 +1556,13 @@ "@babel/template" "^7.22.15" "@babel/types" "^7.22.19" -"@babel/helpers@^7.18.9", "@babel/helpers@^7.26.9": - version "7.26.9" - resolved "https://registry.yarnpkg.com/@babel/helpers/-/helpers-7.26.9.tgz#28f3fb45252fc88ef2dc547c8a911c255fc9fef6" - integrity sha512-Mz/4+y8udxBKdmzt/UjPACs4G3j5SshJJEFFKxlCGPydG4JAHXxjWjAwjd09tf6oINvl1VfMJo+nB7H2YKQ0dA== +"@babel/helpers@^7.18.9", "@babel/helpers@^7.26.0", "@babel/helpers@^7.26.9": + version "7.27.0" + resolved "https://registry.yarnpkg.com/@babel/helpers/-/helpers-7.27.0.tgz#53d156098defa8243eab0f32fa17589075a1b808" + integrity sha512-U5eyP/CTFPuNE3qk+WZMxFkp/4zUzdceQlfzf7DdGdhp+Fezd7HD+i8Y24ZuTMKX3wQBld449jijbGq6OdGNQg== dependencies: - "@babel/template" "^7.26.9" - "@babel/types" "^7.26.9" + "@babel/template" "^7.27.0" + "@babel/types" "^7.27.0" "@babel/highlight@^7.10.4": version "7.24.7" @@ -1574,13 +1574,20 @@ js-tokens "^4.0.0" picocolors "^1.0.0" -"@babel/parser@7.26.9", "@babel/parser@^7.1.0", "@babel/parser@^7.14.7", "@babel/parser@^7.16.4", "@babel/parser@^7.18.10", "@babel/parser@^7.20.7", "@babel/parser@^7.21.8", "@babel/parser@^7.22.10", "@babel/parser@^7.22.16", "@babel/parser@^7.23.5", "@babel/parser@^7.23.6", "@babel/parser@^7.23.9", "@babel/parser@^7.25.3", "@babel/parser@^7.25.4", "@babel/parser@^7.25.6", "@babel/parser@^7.26.9", "@babel/parser@^7.4.5", "@babel/parser@^7.7.0": +"@babel/parser@7.26.9": version "7.26.9" resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.26.9.tgz#d9e78bee6dc80f9efd8f2349dcfbbcdace280fd5" integrity sha512-81NWa1njQblgZbQHxWHpxxCzNsa3ZwvFqpUg7P+NNUU6f3UU2jBEg4OlF/J6rl8+PQGh1q6/zWScd001YwcA5A== dependencies: "@babel/types" "^7.26.9" +"@babel/parser@^7.1.0", "@babel/parser@^7.14.7", "@babel/parser@^7.16.4", "@babel/parser@^7.18.10", "@babel/parser@^7.20.7", "@babel/parser@^7.21.8", "@babel/parser@^7.22.10", "@babel/parser@^7.22.16", "@babel/parser@^7.23.5", "@babel/parser@^7.23.6", "@babel/parser@^7.23.9", "@babel/parser@^7.25.3", "@babel/parser@^7.25.4", "@babel/parser@^7.25.6", "@babel/parser@^7.25.9", "@babel/parser@^7.26.0", "@babel/parser@^7.26.3", "@babel/parser@^7.26.9", "@babel/parser@^7.27.0", "@babel/parser@^7.4.5", "@babel/parser@^7.7.0": + version "7.27.0" + resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.27.0.tgz#3d7d6ee268e41d2600091cbd4e145ffee85a44ec" + integrity sha512-iaepho73/2Pz7w2eMS0Q5f83+0RKI7i4xmiYeBmDzfRVbQtTOG7Ts0S4HzJVsTMGI9keU8rNfuZr8DKfSt7Yyg== + dependencies: + "@babel/types" "^7.27.0" + "@babel/plugin-bugfix-firefox-class-in-computed-class-key@^7.24.4": version "7.24.4" resolved "https://registry.yarnpkg.com/@babel/plugin-bugfix-firefox-class-in-computed-class-key/-/plugin-bugfix-firefox-class-in-computed-class-key-7.24.4.tgz#6125f0158543fb4edf1c22f322f3db67f21cb3e1" @@ -1773,14 +1780,7 @@ dependencies: "@babel/helper-plugin-utils" "^7.8.0" -"@babel/plugin-syntax-bigint@^7.8.3": - version "7.8.3" - resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-bigint/-/plugin-syntax-bigint-7.8.3.tgz#4c9a6f669f5d0cdf1b90a1671e9a146be5300cea" - integrity sha512-wnTnFlG+YxQm3vDxpGE57Pj0srRU4sHE/mDkt1qv2YJJSeUAec2ma4WLUnUPeKjyrfntVwe/N6dCXpU+zL3Npg== - dependencies: - "@babel/helper-plugin-utils" "^7.8.0" - -"@babel/plugin-syntax-class-properties@^7.12.13", "@babel/plugin-syntax-class-properties@^7.8.3": +"@babel/plugin-syntax-class-properties@^7.12.13": version "7.12.13" resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-class-properties/-/plugin-syntax-class-properties-7.12.13.tgz#b5c987274c4a3a82b89714796931a6b53544ae10" integrity sha512-fm4idjKla0YahUNgFNLCB0qySdsoPiZP3iQE3rky0mBUtMZ23yDJ9SJdg6dXTSDnulOVqiF3Hgr9nbXvXTQZYA== @@ -1829,7 +1829,7 @@ dependencies: "@babel/helper-plugin-utils" "^7.24.7" -"@babel/plugin-syntax-import-meta@^7.10.4", "@babel/plugin-syntax-import-meta@^7.8.3": +"@babel/plugin-syntax-import-meta@^7.10.4": version "7.10.4" resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-import-meta/-/plugin-syntax-import-meta-7.10.4.tgz#ee601348c370fa334d2207be158777496521fd51" integrity sha512-Yqfm+XDx0+Prh3VSeEQCPU81yC+JWZ2pDPFSS4ZdpfZhp4MkFMaDC1UqseovEKwSUpnIL7+vK+Clp7bfh0iD7g== @@ -1850,7 +1850,7 @@ dependencies: "@babel/helper-plugin-utils" "^7.25.9" -"@babel/plugin-syntax-logical-assignment-operators@^7.10.4", "@babel/plugin-syntax-logical-assignment-operators@^7.8.3": +"@babel/plugin-syntax-logical-assignment-operators@^7.10.4": version "7.10.4" resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-logical-assignment-operators/-/plugin-syntax-logical-assignment-operators-7.10.4.tgz#ca91ef46303530448b906652bac2e9fe9941f699" integrity sha512-d8waShlpFDinQ5MtvGU9xDAOzKH47+FFoney2baFIoMr952hKOLp1HR7VszoZvOsV/4+RRszNY7D17ba0te0ig== @@ -1864,7 +1864,7 @@ dependencies: "@babel/helper-plugin-utils" "^7.8.0" -"@babel/plugin-syntax-numeric-separator@^7.10.4", "@babel/plugin-syntax-numeric-separator@^7.8.3": +"@babel/plugin-syntax-numeric-separator@^7.10.4": version "7.10.4" resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-numeric-separator/-/plugin-syntax-numeric-separator-7.10.4.tgz#b9b070b3e33570cd9fd07ba7fa91c0dd37b9af97" integrity sha512-9H6YdfkcK/uOnY/K7/aA2xpzaAgkQn37yzWUMRK7OaPOqOpGS1+n0H5hxT9AUw9EsSjPW8SVyMJwYRtWs3X3ug== @@ -1899,14 +1899,14 @@ dependencies: "@babel/helper-plugin-utils" "^7.14.5" -"@babel/plugin-syntax-top-level-await@^7.14.5", "@babel/plugin-syntax-top-level-await@^7.8.3": +"@babel/plugin-syntax-top-level-await@^7.14.5": version "7.14.5" resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-top-level-await/-/plugin-syntax-top-level-await-7.14.5.tgz#c1cfdadc35a646240001f06138247b741c34d94c" integrity sha512-hx++upLv5U1rgYfwe1xBQUhRmU41NEvpUvrp8jkrSCdvGSnM5/qdRMtylJ6PG5OFkBaHkbTAKTnd3/YyESRHFw== dependencies: "@babel/helper-plugin-utils" "^7.14.5" -"@babel/plugin-syntax-typescript@^7.2.0", "@babel/plugin-syntax-typescript@^7.25.9", "@babel/plugin-syntax-typescript@^7.7.2": +"@babel/plugin-syntax-typescript@^7.2.0", "@babel/plugin-syntax-typescript@^7.25.9": version "7.25.9" resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.25.9.tgz#67dda2b74da43727cf21d46cf9afef23f4365399" integrity sha512-hjMgRy5hb8uJJjUcdWunWVcoi9bGpJp8p5Ol1229PoN6aytsLwNMgmdftO23wnCLMfVmTwZDWMPNq/D1SY60JQ== @@ -2593,7 +2593,7 @@ dependencies: regenerator-runtime "^0.13.4" -"@babel/runtime@^7.1.2", "@babel/runtime@^7.12.5", "@babel/runtime@^7.14.0", "@babel/runtime@^7.8.4", "@babel/runtime@^7.9.2": +"@babel/runtime@^7.1.2", "@babel/runtime@^7.12.5", "@babel/runtime@^7.14.0", "@babel/runtime@^7.8.4": version "7.24.7" resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.24.7.tgz#f4f0d5530e8dbdf59b3451b9b3e594b6ba082e12" integrity sha512-UwgBRMjJP+xv857DCngvqXI3Iq6J4v0wXmwc6sapg+zyhbwmQX67LUEFrkK5tbyJ30jGuG3ZvWpBiB9LCy1kWw== @@ -2614,16 +2614,16 @@ "@babel/parser" "^7.18.10" "@babel/types" "^7.18.10" -"@babel/template@^7.18.10", "@babel/template@^7.22.15", "@babel/template@^7.23.9", "@babel/template@^7.24.0", "@babel/template@^7.24.7", "@babel/template@^7.26.9", "@babel/template@^7.3.3": - version "7.26.9" - resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.26.9.tgz#4577ad3ddf43d194528cff4e1fa6b232fa609bb2" - integrity sha512-qyRplbeIpNZhmzOysF/wFMuP9sctmh2cFzRAZOn1YapxBsE1i9bJIY586R/WBLfLcmcBlM8ROBiQURnnNy+zfA== +"@babel/template@^7.18.10", "@babel/template@^7.22.15", "@babel/template@^7.23.9", "@babel/template@^7.24.0", "@babel/template@^7.24.7", "@babel/template@^7.25.9", "@babel/template@^7.26.9", "@babel/template@^7.27.0": + version "7.27.0" + resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.27.0.tgz#b253e5406cc1df1c57dcd18f11760c2dbf40c0b4" + integrity sha512-2ncevenBqXI6qRMukPlXwHKHchC7RyMuu4xv5JBXRfOGVcTy1mXCD12qrp7Jsoxll1EV3+9sE4GugBVRjT2jFA== dependencies: "@babel/code-frame" "^7.26.2" - "@babel/parser" "^7.26.9" - "@babel/types" "^7.26.9" + "@babel/parser" "^7.27.0" + "@babel/types" "^7.27.0" -"@babel/traverse@^7.18.10", "@babel/traverse@^7.22.10", "@babel/traverse@^7.23.2", "@babel/traverse@^7.23.7", "@babel/traverse@^7.23.9", "@babel/traverse@^7.25.9", "@babel/traverse@^7.26.5", "@babel/traverse@^7.26.9", "@babel/traverse@^7.4.5", "@babel/traverse@^7.7.0", "@babel/traverse@^7.7.2": +"@babel/traverse@^7.18.10", "@babel/traverse@^7.22.10", "@babel/traverse@^7.23.2", "@babel/traverse@^7.23.7", "@babel/traverse@^7.23.9", "@babel/traverse@^7.25.9", "@babel/traverse@^7.26.5", "@babel/traverse@^7.26.9", "@babel/traverse@^7.4.5", "@babel/traverse@^7.7.0": version "7.26.9" resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.26.9.tgz#4398f2394ba66d05d988b2ad13c219a2c857461a" integrity sha512-ZYW7L+pL8ahU5fXmNbPF+iZFHCv5scFak7MZ9bwaRPLUhHh7QQEMjZUg0HevihoqCM5iSYHN61EyCoZvqC+bxg== @@ -2636,10 +2636,10 @@ debug "^4.3.1" globals "^11.1.0" -"@babel/types@^7.0.0", "@babel/types@^7.18.10", "@babel/types@^7.18.6", "@babel/types@^7.20.7", "@babel/types@^7.22.10", "@babel/types@^7.22.15", "@babel/types@^7.22.17", "@babel/types@^7.22.19", "@babel/types@^7.22.5", "@babel/types@^7.23.6", "@babel/types@^7.23.9", "@babel/types@^7.24.7", "@babel/types@^7.25.4", "@babel/types@^7.25.6", "@babel/types@^7.25.9", "@babel/types@^7.26.3", "@babel/types@^7.26.9", "@babel/types@^7.3.0", "@babel/types@^7.3.3", "@babel/types@^7.4.4", "@babel/types@^7.7.0", "@babel/types@^7.7.2": - version "7.26.9" - resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.26.9.tgz#08b43dec79ee8e682c2ac631c010bdcac54a21ce" - integrity sha512-Y3IR1cRnOxOCDvMmNiym7XpXQ93iGDDPHx+Zj+NM+rg0fBaShfQLkg+hKPaZCEvg5N/LeCo4+Rj/i3FuJsIQaw== +"@babel/types@^7.0.0", "@babel/types@^7.18.10", "@babel/types@^7.18.6", "@babel/types@^7.20.7", "@babel/types@^7.22.10", "@babel/types@^7.22.15", "@babel/types@^7.22.17", "@babel/types@^7.22.19", "@babel/types@^7.22.5", "@babel/types@^7.23.6", "@babel/types@^7.23.9", "@babel/types@^7.24.7", "@babel/types@^7.25.4", "@babel/types@^7.25.6", "@babel/types@^7.25.9", "@babel/types@^7.26.0", "@babel/types@^7.26.3", "@babel/types@^7.26.9", "@babel/types@^7.27.0", "@babel/types@^7.3.0", "@babel/types@^7.4.4", "@babel/types@^7.7.0", "@babel/types@^7.7.2": + version "7.27.0" + resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.27.0.tgz#ef9acb6b06c3173f6632d993ecb6d4ae470b4559" + integrity sha512-H45s8fVLYjbhFH62dIJ3WtmJ6RSPt/3DRO0ZcT2SUiYiQyz3BLVb9ADEnLl91m74aQPS3AzzeajZHYOalWe3bg== dependencies: "@babel/helper-string-parser" "^7.25.9" "@babel/helper-validator-identifier" "^7.25.9" @@ -3889,7 +3889,7 @@ resolved "https://registry.yarnpkg.com/@esbuild/win32-x64/-/win32-x64-0.24.2.tgz#34aa0b52d0fbb1a654b596acfa595f0c7b77a77b" integrity sha512-7VTgWzgMGvup6aSqDPLiW5zHaxYJGTO4OokMjIlrCtf+VpEL+cXKtCvg723iguPYI5oaUNdS+/V7OU2gvXVWEg== -"@eslint-community/eslint-utils@^4.1.2", "@eslint-community/eslint-utils@^4.2.0", "@eslint-community/eslint-utils@^4.4.0": +"@eslint-community/eslint-utils@^4.1.2", "@eslint-community/eslint-utils@^4.4.0": version "4.4.0" resolved "https://registry.yarnpkg.com/@eslint-community/eslint-utils/-/eslint-utils-4.4.0.tgz#a23514e8fb9af1269d5f7788aa556798d61c6b59" integrity sha512-1/sA4dwrzBAyeUoQ6oxahHKmrZvsnLCg4RfxW3ZFGGmQkSNQPFNLV9CUEFQP1x9EYXHTo5p6xdhZM1Ne9p/AfA== @@ -4450,114 +4450,6 @@ resolved "https://registry.yarnpkg.com/@istanbuljs/schema/-/schema-0.1.3.tgz#e45e384e4b8ec16bce2fd903af78450f6bf7ec98" integrity sha512-ZXRY4jNvVgSVQ8DL3LTcakaAtXwTVUxE81hslsyD2AtoXW/wVob10HkOJ1X/pAlcI7D+2YoZKg5do8G/w6RYgA== -"@jest/console@^27.5.1": - version "27.5.1" - resolved "https://registry.yarnpkg.com/@jest/console/-/console-27.5.1.tgz#260fe7239602fe5130a94f1aa386eff54b014bba" - integrity sha512-kZ/tNpS3NXn0mlXXXPNuDZnb4c0oZ20r4K5eemM2k30ZC3G0T02nXUvyhf5YdbXWHPEJLc9qGLxEZ216MdL+Zg== - dependencies: - "@jest/types" "^27.5.1" - "@types/node" "*" - chalk "^4.0.0" - jest-message-util "^27.5.1" - jest-util "^27.5.1" - slash "^3.0.0" - -"@jest/core@^27.5.1": - version "27.5.1" - resolved "https://registry.yarnpkg.com/@jest/core/-/core-27.5.1.tgz#267ac5f704e09dc52de2922cbf3af9edcd64b626" - integrity sha512-AK6/UTrvQD0Cd24NSqmIA6rKsu0tKIxfiCducZvqxYdmMisOYAsdItspT+fQDQYARPf8XgjAFZi0ogW2agH5nQ== - dependencies: - "@jest/console" "^27.5.1" - "@jest/reporters" "^27.5.1" - "@jest/test-result" "^27.5.1" - "@jest/transform" "^27.5.1" - "@jest/types" "^27.5.1" - "@types/node" "*" - ansi-escapes "^4.2.1" - chalk "^4.0.0" - emittery "^0.8.1" - exit "^0.1.2" - graceful-fs "^4.2.9" - jest-changed-files "^27.5.1" - jest-config "^27.5.1" - jest-haste-map "^27.5.1" - jest-message-util "^27.5.1" - jest-regex-util "^27.5.1" - jest-resolve "^27.5.1" - jest-resolve-dependencies "^27.5.1" - jest-runner "^27.5.1" - jest-runtime "^27.5.1" - jest-snapshot "^27.5.1" - jest-util "^27.5.1" - jest-validate "^27.5.1" - jest-watcher "^27.5.1" - micromatch "^4.0.4" - rimraf "^3.0.0" - slash "^3.0.0" - strip-ansi "^6.0.0" - -"@jest/environment@^27.5.1": - version "27.5.1" - resolved "https://registry.yarnpkg.com/@jest/environment/-/environment-27.5.1.tgz#d7425820511fe7158abbecc010140c3fd3be9c74" - integrity sha512-/WQjhPJe3/ghaol/4Bq480JKXV/Rfw8nQdN7f41fM8VDHLcxKXou6QyXAh3EFr9/bVG3x74z1NWDkP87EiY8gA== - dependencies: - "@jest/fake-timers" "^27.5.1" - "@jest/types" "^27.5.1" - "@types/node" "*" - jest-mock "^27.5.1" - -"@jest/fake-timers@^27.5.1": - version "27.5.1" - resolved "https://registry.yarnpkg.com/@jest/fake-timers/-/fake-timers-27.5.1.tgz#76979745ce0579c8a94a4678af7a748eda8ada74" - integrity sha512-/aPowoolwa07k7/oM3aASneNeBGCmGQsc3ugN4u6s4C/+s5M64MFo/+djTdiwcbQlRfFElGuDXWzaWj6QgKObQ== - dependencies: - "@jest/types" "^27.5.1" - "@sinonjs/fake-timers" "^8.0.1" - "@types/node" "*" - jest-message-util "^27.5.1" - jest-mock "^27.5.1" - jest-util "^27.5.1" - -"@jest/globals@^27.5.1": - version "27.5.1" - resolved "https://registry.yarnpkg.com/@jest/globals/-/globals-27.5.1.tgz#7ac06ce57ab966566c7963431cef458434601b2b" - integrity sha512-ZEJNB41OBQQgGzgyInAv0UUfDDj3upmHydjieSxFvTRuZElrx7tXg/uVQ5hYVEwiXs3+aMsAeEc9X7xiSKCm4Q== - dependencies: - "@jest/environment" "^27.5.1" - "@jest/types" "^27.5.1" - expect "^27.5.1" - -"@jest/reporters@^27.5.1": - version "27.5.1" - resolved "https://registry.yarnpkg.com/@jest/reporters/-/reporters-27.5.1.tgz#ceda7be96170b03c923c37987b64015812ffec04" - integrity sha512-cPXh9hWIlVJMQkVk84aIvXuBB4uQQmFqZiacloFuGiP3ah1sbCxCosidXFDfqG8+6fO1oR2dTJTlsOy4VFmUfw== - dependencies: - "@bcoe/v8-coverage" "^0.2.3" - "@jest/console" "^27.5.1" - "@jest/test-result" "^27.5.1" - "@jest/transform" "^27.5.1" - "@jest/types" "^27.5.1" - "@types/node" "*" - chalk "^4.0.0" - collect-v8-coverage "^1.0.0" - exit "^0.1.2" - glob "^7.1.2" - graceful-fs "^4.2.9" - istanbul-lib-coverage "^3.0.0" - istanbul-lib-instrument "^5.1.0" - istanbul-lib-report "^3.0.0" - istanbul-lib-source-maps "^4.0.0" - istanbul-reports "^3.1.3" - jest-haste-map "^27.5.1" - jest-resolve "^27.5.1" - jest-util "^27.5.1" - jest-worker "^27.5.1" - slash "^3.0.0" - source-map "^0.6.0" - string-length "^4.0.1" - terminal-link "^2.0.0" - v8-to-istanbul "^8.1.0" - "@jest/schemas@^29.6.3": version "29.6.3" resolved "https://registry.yarnpkg.com/@jest/schemas/-/schemas-29.6.3.tgz#430b5ce8a4e0044a7e3819663305a7b3091c8e03" @@ -4565,67 +4457,6 @@ dependencies: "@sinclair/typebox" "^0.27.8" -"@jest/source-map@^27.5.1": - version "27.5.1" - resolved "https://registry.yarnpkg.com/@jest/source-map/-/source-map-27.5.1.tgz#6608391e465add4205eae073b55e7f279e04e8cf" - integrity sha512-y9NIHUYF3PJRlHk98NdC/N1gl88BL08aQQgu4k4ZopQkCw9t9cV8mtl3TV8b/YCB8XaVTFrmUTAJvjsntDireg== - dependencies: - callsites "^3.0.0" - graceful-fs "^4.2.9" - source-map "^0.6.0" - -"@jest/test-result@^27.5.1": - version "27.5.1" - resolved "https://registry.yarnpkg.com/@jest/test-result/-/test-result-27.5.1.tgz#56a6585fa80f7cdab72b8c5fc2e871d03832f5bb" - integrity sha512-EW35l2RYFUcUQxFJz5Cv5MTOxlJIQs4I7gxzi2zVU7PJhOwfYq1MdC5nhSmYjX1gmMmLPvB3sIaC+BkcHRBfag== - dependencies: - "@jest/console" "^27.5.1" - "@jest/types" "^27.5.1" - "@types/istanbul-lib-coverage" "^2.0.0" - collect-v8-coverage "^1.0.0" - -"@jest/test-sequencer@^27.5.1": - version "27.5.1" - resolved "https://registry.yarnpkg.com/@jest/test-sequencer/-/test-sequencer-27.5.1.tgz#4057e0e9cea4439e544c6353c6affe58d095745b" - integrity sha512-LCheJF7WB2+9JuCS7VB/EmGIdQuhtqjRNI9A43idHv3E4KltCTsPsLxvdaubFHSYwY/fNjMWjl6vNRhDiN7vpQ== - dependencies: - "@jest/test-result" "^27.5.1" - graceful-fs "^4.2.9" - jest-haste-map "^27.5.1" - jest-runtime "^27.5.1" - -"@jest/transform@^27.5.1": - version "27.5.1" - resolved "https://registry.yarnpkg.com/@jest/transform/-/transform-27.5.1.tgz#6c3501dcc00c4c08915f292a600ece5ecfe1f409" - integrity sha512-ipON6WtYgl/1329g5AIJVbUuEh0wZVbdpGwC99Jw4LwuoBNS95MVphU6zOeD9pDkon+LLbFL7lOQRapbB8SCHw== - dependencies: - "@babel/core" "^7.1.0" - "@jest/types" "^27.5.1" - babel-plugin-istanbul "^6.1.1" - chalk "^4.0.0" - convert-source-map "^1.4.0" - fast-json-stable-stringify "^2.0.0" - graceful-fs "^4.2.9" - jest-haste-map "^27.5.1" - jest-regex-util "^27.5.1" - jest-util "^27.5.1" - micromatch "^4.0.4" - pirates "^4.0.4" - slash "^3.0.0" - source-map "^0.6.1" - write-file-atomic "^3.0.0" - -"@jest/types@^27.5.1": - version "27.5.1" - resolved "https://registry.yarnpkg.com/@jest/types/-/types-27.5.1.tgz#3c79ec4a8ba61c170bf937bcf9e98a9df175ec80" - integrity sha512-Cx46iJ9QpwQTjIdq5VJu2QTMMs3QlEjI0x1QbBP5W1+nMzyc2XmimiRR/CbX9TO0cPTeUlxWMOu8mslYsJ8DEw== - dependencies: - "@types/istanbul-lib-coverage" "^2.0.0" - "@types/istanbul-reports" "^3.0.0" - "@types/node" "*" - "@types/yargs" "^16.0.0" - chalk "^4.0.0" - "@josephg/resolvable@^1.0.0": version "1.0.1" resolved "https://registry.yarnpkg.com/@josephg/resolvable/-/resolvable-1.0.1.tgz#69bc4db754d79e1a2f17a650d3466e038d94a5eb" @@ -6443,191 +6274,96 @@ estree-walker "^2.0.2" picomatch "^2.3.1" -"@rollup/rollup-android-arm-eabi@4.34.8": - version "4.34.8" - resolved "https://registry.yarnpkg.com/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.34.8.tgz#731df27dfdb77189547bcef96ada7bf166bbb2fb" - integrity sha512-q217OSE8DTp8AFHuNHXo0Y86e1wtlfVrXiAlwkIvGRQv9zbc6mE3sjIVfwI8sYUyNxwOg0j/Vm1RKM04JcWLJw== - "@rollup/rollup-android-arm-eabi@4.35.0": version "4.35.0" resolved "https://registry.yarnpkg.com/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.35.0.tgz#e1d7700735f7e8de561ef7d1fa0362082a180c43" integrity sha512-uYQ2WfPaqz5QtVgMxfN6NpLD+no0MYHDBywl7itPYd3K5TjjSghNKmX8ic9S8NU8w81NVhJv/XojcHptRly7qQ== -"@rollup/rollup-android-arm64@4.34.8": - version "4.34.8" - resolved "https://registry.yarnpkg.com/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.34.8.tgz#4bea6db78e1f6927405df7fe0faf2f5095e01343" - integrity sha512-Gigjz7mNWaOL9wCggvoK3jEIUUbGul656opstjaUSGC3eT0BM7PofdAJaBfPFWWkXNVAXbaQtC99OCg4sJv70Q== - "@rollup/rollup-android-arm64@4.35.0": version "4.35.0" resolved "https://registry.yarnpkg.com/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.35.0.tgz#fa6cdfb1fc9e2c8e227a7f35d524d8f7f90cf4db" integrity sha512-FtKddj9XZudurLhdJnBl9fl6BwCJ3ky8riCXjEw3/UIbjmIY58ppWwPEvU3fNu+W7FUsAsB1CdH+7EQE6CXAPA== -"@rollup/rollup-darwin-arm64@4.34.8": - version "4.34.8" - resolved "https://registry.yarnpkg.com/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.34.8.tgz#a7aab77d44be3c44a20f946e10160f84e5450e7f" - integrity sha512-02rVdZ5tgdUNRxIUrFdcMBZQoaPMrxtwSb+/hOfBdqkatYHR3lZ2A2EGyHq2sGOd0Owk80oV3snlDASC24He3Q== - "@rollup/rollup-darwin-arm64@4.35.0": version "4.35.0" resolved "https://registry.yarnpkg.com/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.35.0.tgz#6da5a1ddc4f11d4a7ae85ab443824cb6bf614e30" integrity sha512-Uk+GjOJR6CY844/q6r5DR/6lkPFOw0hjfOIzVx22THJXMxktXG6CbejseJFznU8vHcEBLpiXKY3/6xc+cBm65Q== -"@rollup/rollup-darwin-x64@4.34.8": - version "4.34.8" - resolved "https://registry.yarnpkg.com/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.34.8.tgz#c572c024b57ee8ddd1b0851703ace9eb6cc0dd82" - integrity sha512-qIP/elwR/tq/dYRx3lgwK31jkZvMiD6qUtOycLhTzCvrjbZ3LjQnEM9rNhSGpbLXVJYQ3rq39A6Re0h9tU2ynw== - "@rollup/rollup-darwin-x64@4.35.0": version "4.35.0" resolved "https://registry.yarnpkg.com/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.35.0.tgz#25b74ce2d8d3f9ea8e119b01384d44a1c0a0d3ae" integrity sha512-3IrHjfAS6Vkp+5bISNQnPogRAW5GAV1n+bNCrDwXmfMHbPl5EhTmWtfmwlJxFRUCBZ+tZ/OxDyU08aF6NI/N5Q== -"@rollup/rollup-freebsd-arm64@4.34.8": - version "4.34.8" - resolved "https://registry.yarnpkg.com/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.34.8.tgz#cf74f8113b5a83098a5c026c165742277cbfb88b" - integrity sha512-IQNVXL9iY6NniYbTaOKdrlVP3XIqazBgJOVkddzJlqnCpRi/yAeSOa8PLcECFSQochzqApIOE1GHNu3pCz+BDA== - "@rollup/rollup-freebsd-arm64@4.35.0": version "4.35.0" resolved "https://registry.yarnpkg.com/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.35.0.tgz#be3d39e3441df5d6e187c83d158c60656c82e203" integrity sha512-sxjoD/6F9cDLSELuLNnY0fOrM9WA0KrM0vWm57XhrIMf5FGiN8D0l7fn+bpUeBSU7dCgPV2oX4zHAsAXyHFGcQ== -"@rollup/rollup-freebsd-x64@4.34.8": - version "4.34.8" - resolved "https://registry.yarnpkg.com/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.34.8.tgz#39561f3a2f201a4ad6a01425b1ff5928154ecd7c" - integrity sha512-TYXcHghgnCqYFiE3FT5QwXtOZqDj5GmaFNTNt3jNC+vh22dc/ukG2cG+pi75QO4kACohZzidsq7yKTKwq/Jq7Q== - "@rollup/rollup-freebsd-x64@4.35.0": version "4.35.0" resolved "https://registry.yarnpkg.com/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.35.0.tgz#cd932d3ec679711efd65ca25821fb318e25b7ce4" integrity sha512-2mpHCeRuD1u/2kruUiHSsnjWtHjqVbzhBkNVQ1aVD63CcexKVcQGwJ2g5VphOd84GvxfSvnnlEyBtQCE5hxVVw== -"@rollup/rollup-linux-arm-gnueabihf@4.34.8": - version "4.34.8" - resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.34.8.tgz#980d6061e373bfdaeb67925c46d2f8f9b3de537f" - integrity sha512-A4iphFGNkWRd+5m3VIGuqHnG3MVnqKe7Al57u9mwgbyZ2/xF9Jio72MaY7xxh+Y87VAHmGQr73qoKL9HPbXj1g== - "@rollup/rollup-linux-arm-gnueabihf@4.35.0": version "4.35.0" resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.35.0.tgz#d300b74c6f805474225632f185daaeae760ac2bb" integrity sha512-mrA0v3QMy6ZSvEuLs0dMxcO2LnaCONs1Z73GUDBHWbY8tFFocM6yl7YyMu7rz4zS81NDSqhrUuolyZXGi8TEqg== -"@rollup/rollup-linux-arm-musleabihf@4.34.8": - version "4.34.8" - resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.34.8.tgz#f91a90f30dc00d5a64ac2d9bbedc829cd3cfaa78" - integrity sha512-S0lqKLfTm5u+QTxlFiAnb2J/2dgQqRy/XvziPtDd1rKZFXHTyYLoVL58M/XFwDI01AQCDIevGLbQrMAtdyanpA== - "@rollup/rollup-linux-arm-musleabihf@4.35.0": version "4.35.0" resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.35.0.tgz#2caac622380f314c41934ed1e68ceaf6cc380cc3" integrity sha512-DnYhhzcvTAKNexIql8pFajr0PiDGrIsBYPRvCKlA5ixSS3uwo/CWNZxB09jhIapEIg945KOzcYEAGGSmTSpk7A== -"@rollup/rollup-linux-arm64-gnu@4.34.8": - version "4.34.8" - resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.34.8.tgz#fac700fa5c38bc13a0d5d34463133093da4c92a0" - integrity sha512-jpz9YOuPiSkL4G4pqKrus0pn9aYwpImGkosRKwNi+sJSkz+WU3anZe6hi73StLOQdfXYXC7hUfsQlTnjMd3s1A== - "@rollup/rollup-linux-arm64-gnu@4.35.0": version "4.35.0" resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.35.0.tgz#1ec841650b038cc15c194c26326483fd7ebff3e3" integrity sha512-uagpnH2M2g2b5iLsCTZ35CL1FgyuzzJQ8L9VtlJ+FckBXroTwNOaD0z0/UF+k5K3aNQjbm8LIVpxykUOQt1m/A== -"@rollup/rollup-linux-arm64-musl@4.34.8": - version "4.34.8" - resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.34.8.tgz#f50ecccf8c78841ff6df1706bc4782d7f62bf9c3" - integrity sha512-KdSfaROOUJXgTVxJNAZ3KwkRc5nggDk+06P6lgi1HLv1hskgvxHUKZ4xtwHkVYJ1Rep4GNo+uEfycCRRxht7+Q== - "@rollup/rollup-linux-arm64-musl@4.35.0": version "4.35.0" resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.35.0.tgz#2fc70a446d986e27f6101ea74e81746987f69150" integrity sha512-XQxVOCd6VJeHQA/7YcqyV0/88N6ysSVzRjJ9I9UA/xXpEsjvAgDTgH3wQYz5bmr7SPtVK2TsP2fQ2N9L4ukoUg== -"@rollup/rollup-linux-loongarch64-gnu@4.34.8": - version "4.34.8" - resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-loongarch64-gnu/-/rollup-linux-loongarch64-gnu-4.34.8.tgz#5869dc0b28242da6553e2b52af41374f4038cd6e" - integrity sha512-NyF4gcxwkMFRjgXBM6g2lkT58OWztZvw5KkV2K0qqSnUEqCVcqdh2jN4gQrTn/YUpAcNKyFHfoOZEer9nwo6uQ== - "@rollup/rollup-linux-loongarch64-gnu@4.35.0": version "4.35.0" resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-loongarch64-gnu/-/rollup-linux-loongarch64-gnu-4.35.0.tgz#561bd045cd9ce9e08c95f42e7a8688af8c93d764" integrity sha512-5pMT5PzfgwcXEwOaSrqVsz/LvjDZt+vQ8RT/70yhPU06PTuq8WaHhfT1LW+cdD7mW6i/J5/XIkX/1tCAkh1W6g== -"@rollup/rollup-linux-powerpc64le-gnu@4.34.8": - version "4.34.8" - resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-powerpc64le-gnu/-/rollup-linux-powerpc64le-gnu-4.34.8.tgz#5cdd9f851ce1bea33d6844a69f9574de335f20b1" - integrity sha512-LMJc999GkhGvktHU85zNTDImZVUCJ1z/MbAJTnviiWmmjyckP5aQsHtcujMjpNdMZPT2rQEDBlJfubhs3jsMfw== - "@rollup/rollup-linux-powerpc64le-gnu@4.35.0": version "4.35.0" resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-powerpc64le-gnu/-/rollup-linux-powerpc64le-gnu-4.35.0.tgz#45d849a0b33813f33fe5eba9f99e0ff15ab5caad" integrity sha512-c+zkcvbhbXF98f4CtEIP1EBA/lCic5xB0lToneZYvMeKu5Kamq3O8gqrxiYYLzlZH6E3Aq+TSW86E4ay8iD8EA== -"@rollup/rollup-linux-riscv64-gnu@4.34.8": - version "4.34.8" - resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.34.8.tgz#ef5dc37f4388f5253f0def43e1440ec012af204d" - integrity sha512-xAQCAHPj8nJq1PI3z8CIZzXuXCstquz7cIOL73HHdXiRcKk8Ywwqtx2wrIy23EcTn4aZ2fLJNBB8d0tQENPCmw== - "@rollup/rollup-linux-riscv64-gnu@4.35.0": version "4.35.0" resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.35.0.tgz#78dde3e6fcf5b5733a97d0a67482d768aa1e83a5" integrity sha512-s91fuAHdOwH/Tad2tzTtPX7UZyytHIRR6V4+2IGlV0Cej5rkG0R61SX4l4y9sh0JBibMiploZx3oHKPnQBKe4g== -"@rollup/rollup-linux-s390x-gnu@4.34.8": - version "4.34.8" - resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.34.8.tgz#7dbc3ccbcbcfb3e65be74538dfb6e8dd16178fde" - integrity sha512-DdePVk1NDEuc3fOe3dPPTb+rjMtuFw89gw6gVWxQFAuEqqSdDKnrwzZHrUYdac7A7dXl9Q2Vflxpme15gUWQFA== - "@rollup/rollup-linux-s390x-gnu@4.35.0": version "4.35.0" resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.35.0.tgz#2e34835020f9e03dfb411473a5c2a0e8a9c5037b" integrity sha512-hQRkPQPLYJZYGP+Hj4fR9dDBMIM7zrzJDWFEMPdTnTy95Ljnv0/4w/ixFw3pTBMEuuEuoqtBINYND4M7ujcuQw== -"@rollup/rollup-linux-x64-gnu@4.34.8": - version "4.34.8" - resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.34.8.tgz#5783fc0adcab7dc069692056e8ca8d83709855ce" - integrity sha512-8y7ED8gjxITUltTUEJLQdgpbPh1sUQ0kMTmufRF/Ns5tI9TNMNlhWtmPKKHCU0SilX+3MJkZ0zERYYGIVBYHIA== - "@rollup/rollup-linux-x64-gnu@4.35.0": version "4.35.0" resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.35.0.tgz#4f9774beddc6f4274df57ac99862eb23040de461" integrity sha512-Pim1T8rXOri+0HmV4CdKSGrqcBWX0d1HoPnQ0uw0bdp1aP5SdQVNBy8LjYncvnLgu3fnnCt17xjWGd4cqh8/hA== -"@rollup/rollup-linux-x64-musl@4.34.8": - version "4.34.8" - resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.34.8.tgz#00b6c29b298197a384e3c659910b47943003a678" - integrity sha512-SCXcP0ZpGFIe7Ge+McxY5zKxiEI5ra+GT3QRxL0pMMtxPfpyLAKleZODi1zdRHkz5/BhueUrYtYVgubqe9JBNQ== - "@rollup/rollup-linux-x64-musl@4.35.0": version "4.35.0" resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.35.0.tgz#dfcff2c1aed518b3d23ccffb49afb349d74fb608" integrity sha512-QysqXzYiDvQWfUiTm8XmJNO2zm9yC9P/2Gkrwg2dH9cxotQzunBHYr6jk4SujCTqnfGxduOmQcI7c2ryuW8XVg== -"@rollup/rollup-win32-arm64-msvc@4.34.8": - version "4.34.8" - resolved "https://registry.yarnpkg.com/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.34.8.tgz#cbfee01f1fe73791c35191a05397838520ca3cdd" - integrity sha512-YHYsgzZgFJzTRbth4h7Or0m5O74Yda+hLin0irAIobkLQFRQd1qWmnoVfwmKm9TXIZVAD0nZ+GEb2ICicLyCnQ== - "@rollup/rollup-win32-arm64-msvc@4.35.0": version "4.35.0" resolved "https://registry.yarnpkg.com/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.35.0.tgz#b0b37e2d77041e3aa772f519291309abf4c03a84" integrity sha512-OUOlGqPkVJCdJETKOCEf1mw848ZyJ5w50/rZ/3IBQVdLfR5jk/6Sr5m3iO2tdPgwo0x7VcncYuOvMhBWZq8ayg== -"@rollup/rollup-win32-ia32-msvc@4.34.8": - version "4.34.8" - resolved "https://registry.yarnpkg.com/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.34.8.tgz#95cdbdff48fe6c948abcf6a1d500b2bd5ce33f62" - integrity sha512-r3NRQrXkHr4uWy5TOjTpTYojR9XmF0j/RYgKCef+Ag46FWUTltm5ziticv8LdNsDMehjJ543x/+TJAek/xBA2w== - "@rollup/rollup-win32-ia32-msvc@4.35.0": version "4.35.0" resolved "https://registry.yarnpkg.com/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.35.0.tgz#5b5a40e44a743ddc0e06b8e1b3982f856dc9ce0a" integrity sha512-2/lsgejMrtwQe44glq7AFFHLfJBPafpsTa6JvP2NGef/ifOa4KBoglVf7AKN7EV9o32evBPRqfg96fEHzWo5kw== -"@rollup/rollup-win32-x64-msvc@4.34.8": - version "4.34.8" - resolved "https://registry.yarnpkg.com/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.34.8.tgz#4cdb2cfae69cdb7b1a3cc58778e820408075e928" - integrity sha512-U0FaE5O1BCpZSeE6gBl3c5ObhePQSfk9vDRToMmTkbhCOgW4jqvtS5LGyQ76L1fH8sM0keRp4uDTsbjiUyjk0g== - "@rollup/rollup-win32-x64-msvc@4.35.0": version "4.35.0" resolved "https://registry.yarnpkg.com/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.35.0.tgz#05f25dbc9981bee1ae6e713daab10397044a46ca" @@ -7023,13 +6759,6 @@ resolved "https://registry.yarnpkg.com/@sindresorhus/merge-streams/-/merge-streams-2.3.0.tgz#719df7fb41766bc143369eaa0dd56d8dc87c9958" integrity sha512-LtoMMhxAlorcGhmFYI+LhPgbPZCkgP6ra1YL604EeF6U98pLlQ3iWIGMdWSC+vWmPBWBNgmDBAhnAobLROJmwg== -"@sinonjs/commons@^1.7.0": - version "1.8.2" - resolved "https://registry.yarnpkg.com/@sinonjs/commons/-/commons-1.8.2.tgz#858f5c4b48d80778fde4b9d541f27edc0d56488b" - integrity sha512-sruwd86RJHdsVf/AtBoijDmUqJp3B6hF/DGC23C+JaegnDHaZyewCjoVGTdg3J0uz3Zs7NnIT05OBOmML72lQw== - dependencies: - type-detect "4.0.8" - "@sinonjs/commons@^3.0.1": version "3.0.1" resolved "https://registry.yarnpkg.com/@sinonjs/commons/-/commons-3.0.1.tgz#1029357e44ca901a615585f6d27738dbc89084cd" @@ -7044,13 +6773,6 @@ dependencies: "@sinonjs/commons" "^3.0.1" -"@sinonjs/fake-timers@^8.0.1": - version "8.1.0" - resolved "https://registry.yarnpkg.com/@sinonjs/fake-timers/-/fake-timers-8.1.0.tgz#3fdc2b6cb58935b21bfb8d1625eb1300484316e7" - integrity sha512-OAPJUAtgeINhh/TAlUID4QTs53Njm7xzddaVlEs/SXwgtiD1tW22zAB/W1wdqfrpmikgaWQ9Fw6Ws+hsiRm5Vg== - dependencies: - "@sinonjs/commons" "^1.7.0" - "@sinonjs/samsam@^8.0.1": version "8.0.2" resolved "https://registry.yarnpkg.com/@sinonjs/samsam/-/samsam-8.0.2.tgz#e4386bf668ff36c95949e55a38dc5f5892fc2689" @@ -7651,20 +7373,6 @@ lz-string "^1.5.0" pretty-format "^27.0.2" -"@testing-library/jest-dom@^6.4.5": - version "6.4.5" - resolved "https://registry.yarnpkg.com/@testing-library/jest-dom/-/jest-dom-6.4.5.tgz#badb40296477149136dabef32b572ddd3b56adf1" - integrity sha512-AguB9yvTXmCnySBP1lWjfNNUwpbElsaQ567lt2VdGqAdHtpieLgjmcVyv1q7PMIvLbgpDdkWV5Ydv3FEejyp2A== - dependencies: - "@adobe/css-tools" "^4.3.2" - "@babel/runtime" "^7.9.2" - aria-query "^5.0.0" - chalk "^3.0.0" - css.escape "^1.5.1" - dom-accessibility-api "^0.6.3" - lodash "^4.17.21" - redent "^3.0.0" - "@testing-library/react-hooks@^7.0.2": version "7.0.2" resolved "https://registry.yarnpkg.com/@testing-library/react-hooks/-/react-hooks-7.0.2.tgz#3388d07f562d91e7f2431a4a21b5186062ecfee0" @@ -7774,7 +7482,7 @@ resolved "https://registry.yarnpkg.com/@types/aws-lambda/-/aws-lambda-8.10.147.tgz#dc5c89aa32f47a9b35e52c32630545c83afa6f2f" integrity sha512-nD0Z9fNIZcxYX5Mai2CTmFD7wX7UldCkW2ezCF8D1T5hdiLsnTWDGRpfRYntU6VjTdLQjOvyszru7I1c1oCQew== -"@types/babel__core@^7.0.0", "@types/babel__core@^7.1.14", "@types/babel__core@^7.20.1", "@types/babel__core@^7.20.4": +"@types/babel__core@^7.20.1", "@types/babel__core@^7.20.4": version "7.20.5" resolved "https://registry.yarnpkg.com/@types/babel__core/-/babel__core-7.20.5.tgz#3df15f27ba85319caa07ba08d0721889bb39c017" integrity sha512-qoQprZvz5wQFJwMDqeseRXWv3rqMvhgpbXFfVyWhbx9X47POIA6i/+dXefEmZKoAgOaTdaIgNSMqMIU61yRyzA== @@ -7800,7 +7508,7 @@ "@babel/parser" "^7.1.0" "@babel/types" "^7.0.0" -"@types/babel__traverse@*", "@types/babel__traverse@^7.0.4", "@types/babel__traverse@^7.0.6": +"@types/babel__traverse@*": version "7.14.2" resolved "https://registry.yarnpkg.com/@types/babel__traverse/-/babel__traverse-7.14.2.tgz#ffcd470bbb3f8bf30481678fb5502278ca833a43" integrity sha512-K2waXdXBi2302XUdcHcR1jCeU0LL4TD9HRs/gk0N2Xvrht+G/BfJa4QObBQZfhMdxiCpV3COl5Nfq4uKTeTnJA== @@ -8165,13 +7873,6 @@ "@types/minimatch" "*" "@types/node" "*" -"@types/graceful-fs@^4.1.2": - version "4.1.5" - resolved "https://registry.yarnpkg.com/@types/graceful-fs/-/graceful-fs-4.1.5.tgz#21ffba0d98da4350db64891f92a9e5db3cdb4e15" - integrity sha512-anKkLmZZ+xm4p8JWBf4hElkM4XR+EZeA2M9BAkkTldmcyDY4mbdIJnRghDJH3Ov5ooY7/UAoENtmdMSkaAd7Cw== - dependencies: - "@types/node" "*" - "@types/hast@^2.0.0": version "2.3.6" resolved "https://registry.yarnpkg.com/@types/hast/-/hast-2.3.6.tgz#bb8b05602112a26d22868acb70c4b20984ec7086" @@ -8231,33 +7932,6 @@ dependencies: "@types/node" "*" -"@types/istanbul-lib-coverage@*", "@types/istanbul-lib-coverage@^2.0.0", "@types/istanbul-lib-coverage@^2.0.1": - version "2.0.4" - resolved "https://registry.yarnpkg.com/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.4.tgz#8467d4b3c087805d63580480890791277ce35c44" - integrity sha512-z/QT1XN4K4KYuslS23k62yDIDLwLFkzxOuMplDtObz0+y7VqJCaO2o+SPwHCvLFZh7xazvvoor2tA/hPz9ee7g== - -"@types/istanbul-lib-report@*": - version "3.0.0" - resolved "https://registry.yarnpkg.com/@types/istanbul-lib-report/-/istanbul-lib-report-3.0.0.tgz#c14c24f18ea8190c118ee7562b7ff99a36552686" - integrity sha512-plGgXAPfVKFoYfa9NpYDAkseG+g6Jr294RqeqcqDixSbU34MZVJRi/P+7Y8GDpzkEwLaGZZOpKIEmeVZNtKsrg== - dependencies: - "@types/istanbul-lib-coverage" "*" - -"@types/istanbul-reports@^3.0.0": - version "3.0.0" - resolved "https://registry.yarnpkg.com/@types/istanbul-reports/-/istanbul-reports-3.0.0.tgz#508b13aa344fa4976234e75dddcc34925737d821" - integrity sha512-nwKNbvnwJ2/mndE9ItP/zc2TCzw6uuodnF4EHYWD+gCQDVBuRQL5UzbZD0/ezy1iKsFU2ZQiDqg4M9dN4+wZgA== - dependencies: - "@types/istanbul-lib-report" "*" - -"@types/jest@^27.4.1": - version "27.4.1" - resolved "https://registry.yarnpkg.com/@types/jest/-/jest-27.4.1.tgz#185cbe2926eaaf9662d340cc02e548ce9e11ab6d" - integrity sha512-23iPJADSmicDVrWk+HT58LMJtzLAnB2AgIzplQuq/bSrGaxCrlvRFjGbXmamnnk/mAmCdLStiGqggu28ocUyiw== - dependencies: - jest-matcher-utils "^27.0.0" - pretty-format "^27.0.0" - "@types/jquery@*": version "3.5.5" resolved "https://registry.yarnpkg.com/@types/jquery/-/jquery-3.5.5.tgz#2c63f47c9c8d96693d272f5453602afd8338c903" @@ -8446,11 +8120,6 @@ pg-protocol "*" pg-types "^2.2.0" -"@types/prettier@^2.1.5": - version "2.4.4" - resolved "https://registry.yarnpkg.com/@types/prettier/-/prettier-2.4.4.tgz#5d9b63132df54d8909fce1c3f8ca260fdd693e17" - integrity sha512-ReVR2rLTV1kvtlWFyuot+d1pkpG2Fw/XKE3PDAdj57rbM97ttSp9JZ2UsP+2EHTylra9cUf6JA7tGwW1INzUrA== - "@types/prop-types@*": version "15.7.3" resolved "https://registry.yarnpkg.com/@types/prop-types/-/prop-types-15.7.3.tgz#2ab0d5da2e5815f94b0b9d4b95d1e5f243ab2ca7" @@ -8616,11 +8285,6 @@ dependencies: "@types/node" "*" -"@types/stack-utils@^2.0.0": - version "2.0.1" - resolved "https://registry.yarnpkg.com/@types/stack-utils/-/stack-utils-2.0.1.tgz#20f18294f797f2209b5f65c8e3b5c8e8261d127c" - integrity sha512-Hl219/BT5fLAaz6NDkSuhzasy49dwQS/DSdu4MdggFB8zcXv7vflBI3xp7FEmkmdDkBUI2bPUNeMttp2knYdxw== - "@types/symlink-or-copy@^1.2.0": version "1.2.0" resolved "https://registry.yarnpkg.com/@types/symlink-or-copy/-/symlink-or-copy-1.2.0.tgz#4151a81b4052c80bc2becbae09f3a9ec010a9c7a" @@ -8672,18 +8336,6 @@ dependencies: "@types/node" "*" -"@types/yargs-parser@*": - version "20.2.0" - resolved "https://registry.yarnpkg.com/@types/yargs-parser/-/yargs-parser-20.2.0.tgz#dd3e6699ba3237f0348cd085e4698780204842f9" - integrity sha512-37RSHht+gzzgYeobbG+KWryeAW8J33Nhr69cjTqSYymXVZEN9NbRYWoYlRtDhHKPVT1FyNKwaTPC1NynKZpzRA== - -"@types/yargs@^16.0.0": - version "16.0.4" - resolved "https://registry.yarnpkg.com/@types/yargs/-/yargs-16.0.4.tgz#26aad98dd2c2a38e421086ea9ad42b9e51642977" - integrity sha512-T8Yc9wt/5LbJyCaLiHPReJa0kApcIgJ7Bn735GjItUfh08Z1pJvu8QZqb9s+mMvKV6WUQRV7K2R46YbjMXTTJw== - dependencies: - "@types/yargs-parser" "*" - "@typescript-eslint/eslint-plugin@^5.48.0": version "5.48.0" resolved "https://registry.yarnpkg.com/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.48.0.tgz#54f8368d080eb384a455f60c2ee044e948a8ce67" @@ -8717,14 +8369,6 @@ "@typescript-eslint/types" "5.48.0" "@typescript-eslint/visitor-keys" "5.48.0" -"@typescript-eslint/scope-manager@5.62.0": - version "5.62.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/scope-manager/-/scope-manager-5.62.0.tgz#d9457ccc6a0b8d6b37d0eb252a23022478c5460c" - integrity sha512-VXuvVvZeQCQb5Zgf4HAxc04q5j+WrNAtNh9OwCsCgpKqESMTu3tF/jhZ3xG6T4NZwWl65Bg8KuS2uEvhSfLl0w== - dependencies: - "@typescript-eslint/types" "5.62.0" - "@typescript-eslint/visitor-keys" "5.62.0" - "@typescript-eslint/scope-manager@6.7.4": version "6.7.4" resolved "https://registry.yarnpkg.com/@typescript-eslint/scope-manager/-/scope-manager-6.7.4.tgz#a484a17aa219e96044db40813429eb7214d7b386" @@ -8771,19 +8415,6 @@ semver "^7.3.7" tsutils "^3.21.0" -"@typescript-eslint/typescript-estree@5.62.0", "@typescript-eslint/typescript-estree@^5.62.0": - version "5.62.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/typescript-estree/-/typescript-estree-5.62.0.tgz#7d17794b77fabcac615d6a48fb143330d962eb9b" - integrity sha512-CmcQ6uY7b9y694lKdRB8FEel7JbU/40iSAPomu++SjLMntB+2Leay2LO6i8VnJk58MtE9/nQSFIH6jpyRWyYzA== - dependencies: - "@typescript-eslint/types" "5.62.0" - "@typescript-eslint/visitor-keys" "5.62.0" - debug "^4.3.4" - globby "^11.1.0" - is-glob "^4.0.3" - semver "^7.3.7" - tsutils "^3.21.0" - "@typescript-eslint/typescript-estree@6.7.4": version "6.7.4" resolved "https://registry.yarnpkg.com/@typescript-eslint/typescript-estree/-/typescript-estree-6.7.4.tgz#f2baece09f7bb1df9296e32638b2e1130014ef1a" @@ -8797,6 +8428,19 @@ semver "^7.5.4" ts-api-utils "^1.0.1" +"@typescript-eslint/typescript-estree@^5.62.0": + version "5.62.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/typescript-estree/-/typescript-estree-5.62.0.tgz#7d17794b77fabcac615d6a48fb143330d962eb9b" + integrity sha512-CmcQ6uY7b9y694lKdRB8FEel7JbU/40iSAPomu++SjLMntB+2Leay2LO6i8VnJk58MtE9/nQSFIH6jpyRWyYzA== + dependencies: + "@typescript-eslint/types" "5.62.0" + "@typescript-eslint/visitor-keys" "5.62.0" + debug "^4.3.4" + globby "^11.1.0" + is-glob "^4.0.3" + semver "^7.3.7" + tsutils "^3.21.0" + "@typescript-eslint/utils@5.48.0": version "5.48.0" resolved "https://registry.yarnpkg.com/@typescript-eslint/utils/-/utils-5.48.0.tgz#eee926af2733f7156ad8d15e51791e42ce300273" @@ -8811,20 +8455,6 @@ eslint-utils "^3.0.0" semver "^7.3.7" -"@typescript-eslint/utils@^5.10.0": - version "5.62.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/utils/-/utils-5.62.0.tgz#141e809c71636e4a75daa39faed2fb5f4b10df86" - integrity sha512-n8oxjeb5aIbPFEtmQxQYOLI0i9n5ySBEY/ZEHHZqKQSFnxio1rv6dthascc9dLuwrL0RC5mPCxB7vnAVGAYWAQ== - dependencies: - "@eslint-community/eslint-utils" "^4.2.0" - "@types/json-schema" "^7.0.9" - "@types/semver" "^7.3.12" - "@typescript-eslint/scope-manager" "5.62.0" - "@typescript-eslint/types" "5.62.0" - "@typescript-eslint/typescript-estree" "5.62.0" - eslint-scope "^5.1.1" - semver "^7.3.7" - "@typescript-eslint/utils@^6.0.0": version "6.7.4" resolved "https://registry.yarnpkg.com/@typescript-eslint/utils/-/utils-6.7.4.tgz#2236f72b10e38277ee05ef06142522e1de470ff2" @@ -9624,7 +9254,7 @@ JSONStream@^1.3.5: jsonparse "^1.2.0" through ">=2.2.7 <3" -abab@^2.0.3, abab@^2.0.5, abab@^2.0.6: +abab@^2.0.6: version "2.0.6" resolved "https://registry.yarnpkg.com/abab/-/abab-2.0.6.tgz#41b80f2c871d19686216b82309231cfd3cb3d291" integrity sha512-j2afSsaIENvHZN2B8GOpF566vZ5WVk5opAiMTvWgaQT8DkbOqsTfvNAvHoRGU2zzP8cPoqys+xHTRDWW8L+/BA== @@ -9649,14 +9279,6 @@ accepts@^1.3.5, accepts@~1.3.4, accepts@~1.3.5, accepts@~1.3.8: mime-types "~2.1.34" negotiator "0.6.3" -acorn-globals@^6.0.0: - version "6.0.0" - resolved "https://registry.yarnpkg.com/acorn-globals/-/acorn-globals-6.0.0.tgz#46cdd39f0f8ff08a876619b55f5ac8a6dc770b45" - integrity sha512-ZQl7LOWaF5ePqqcX4hLuv/bLXYQNfNWw2c0/yX/TsPRKamzHcTGQnlCjHT3TsmkOUVEPS3crCxiPfdzE/Trlhg== - dependencies: - acorn "^7.1.1" - acorn-walk "^7.1.1" - acorn-globals@^7.0.0: version "7.0.1" resolved "https://registry.yarnpkg.com/acorn-globals/-/acorn-globals-7.0.1.tgz#0dbf05c44fa7c94332914c02066d5beff62c40c3" @@ -9692,11 +9314,6 @@ acorn-typescript@^1.4.3: resolved "https://registry.yarnpkg.com/acorn-typescript/-/acorn-typescript-1.4.13.tgz#5f851c8bdda0aa716ffdd5f6ac084df8acc6f5ea" integrity sha512-xsc9Xv0xlVfwp2o7sQ+GCQ1PgbkdcpWdTzrwXxO3xDMTAywVS3oXVOcOHuRjAPkS4P9b+yc/qNF15460v+jp4Q== -acorn-walk@^7.1.1: - version "7.2.0" - resolved "https://registry.yarnpkg.com/acorn-walk/-/acorn-walk-7.2.0.tgz#0de889a601203909b0fbe07b8938dc21d2e967bc" - integrity sha512-OPdCF6GsMIP+Az+aWfAAOEt2/+iVDKE7oy6lJ098aoe59oAmK76qV6Gw60SbZ8jHuG2wH058GF4pLFbYamYrVA== - acorn-walk@^8.0.0, acorn-walk@^8.0.2, acorn-walk@^8.1.1, acorn-walk@^8.2.0: version "8.3.3" resolved "https://registry.yarnpkg.com/acorn-walk/-/acorn-walk-8.3.3.tgz#9caeac29eefaa0c41e3d4c65137de4d6f34df43e" @@ -9714,12 +9331,12 @@ acorn@8.12.1: resolved "https://registry.yarnpkg.com/acorn/-/acorn-8.12.1.tgz#71616bdccbe25e27a54439e0046e89ca76df2248" integrity sha512-tcpGyI9zbizT9JbV6oYE477V6mTlXvvi0T0G3SNIYE2apm/G5huBa1+K89VGeovbg+jycCrfhl3ADxErOuO6Jg== -acorn@^7.1.1, acorn@^7.4.0: +acorn@^7.4.0: version "7.4.1" resolved "https://registry.yarnpkg.com/acorn/-/acorn-7.4.1.tgz#feaed255973d2e77555b83dbc08851a6c63520fa" integrity sha512-nQyp0o1/mNdbTO1PO6kHkwSrmgZ0MT/jCCpNiwbUjGoRN4dlBhqJtoQuCnEOKzgTVwg0ZWiCoQy6SxMebQVh8A== -acorn@^8.0.4, acorn@^8.1.0, acorn@^8.10.0, acorn@^8.11.0, acorn@^8.11.3, acorn@^8.12.1, acorn@^8.14.0, acorn@^8.2.4, acorn@^8.4.1, acorn@^8.5.0, acorn@^8.6.0, acorn@^8.7.0, acorn@^8.7.1, acorn@^8.8.0, acorn@^8.8.1, acorn@^8.8.2, acorn@^8.9.0: +acorn@^8.0.4, acorn@^8.1.0, acorn@^8.10.0, acorn@^8.11.0, acorn@^8.11.3, acorn@^8.12.1, acorn@^8.14.0, acorn@^8.4.1, acorn@^8.5.0, acorn@^8.6.0, acorn@^8.7.0, acorn@^8.7.1, acorn@^8.8.0, acorn@^8.8.1, acorn@^8.8.2, acorn@^8.9.0: version "8.14.0" resolved "https://registry.yarnpkg.com/acorn/-/acorn-8.14.0.tgz#063e2c70cac5fb4f6467f0b11152e04c682795b0" integrity sha512-cl669nCJTZBsL97OF4kUQm5g5hC2uihk0NxY3WENAC0TYdILVkAyHymAntgxGkl7K+t0cXIrH5siy5S4XkFycA== @@ -9964,7 +9581,7 @@ anymatch@^2.0.0: micromatch "^3.1.4" normalize-path "^2.1.1" -anymatch@^3.0.3, anymatch@^3.1.1, anymatch@^3.1.3, anymatch@~3.1.2: +anymatch@^3.1.1, anymatch@^3.1.3, anymatch@~3.1.2: version "3.1.3" resolved "https://registry.yarnpkg.com/anymatch/-/anymatch-3.1.3.tgz#790c58b19ba1720a84205b57c618d5ad8524973e" integrity sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw== @@ -10612,20 +10229,6 @@ babel-import-util@^3.0.0: resolved "https://registry.yarnpkg.com/babel-import-util/-/babel-import-util-3.0.0.tgz#5814c6a58e7b80e64156b48fdfd34d48e6e0b1df" integrity sha512-4YNPkuVsxAW5lnSTa6cn4Wk49RX6GAB6vX+M6LqEtN0YePqoFczv1/x0EyLK/o+4E1j9jEuYj5Su7IEPab5JHQ== -babel-jest@^27.5.1: - version "27.5.1" - resolved "https://registry.yarnpkg.com/babel-jest/-/babel-jest-27.5.1.tgz#a1bf8d61928edfefd21da27eb86a695bfd691444" - integrity sha512-cdQ5dXjGRd0IBRATiQ4mZGlGlRE8kJpjPOixdNRdT+m3UcNqmYWN6rK6nvtXYfY3D76cb8s/O1Ss8ea24PIwcg== - dependencies: - "@jest/transform" "^27.5.1" - "@jest/types" "^27.5.1" - "@types/babel__core" "^7.1.14" - babel-plugin-istanbul "^6.1.1" - babel-preset-jest "^27.5.1" - chalk "^4.0.0" - graceful-fs "^4.2.9" - slash "^3.0.0" - babel-loader@8.2.5, babel-loader@^8.0.6, babel-loader@^8.2.2: version "8.2.5" resolved "https://registry.yarnpkg.com/babel-loader/-/babel-loader-8.2.5.tgz#d45f585e654d5a5d90f5350a779d7647c5ed512e" @@ -10701,7 +10304,7 @@ babel-plugin-htmlbars-inline-precompile@^5.2.1, babel-plugin-htmlbars-inline-pre parse-static-imports "^1.1.0" string.prototype.matchall "^4.0.5" -babel-plugin-istanbul@6.1.1, babel-plugin-istanbul@^6.1.1: +babel-plugin-istanbul@6.1.1: version "6.1.1" resolved "https://registry.yarnpkg.com/babel-plugin-istanbul/-/babel-plugin-istanbul-6.1.1.tgz#fa88ec59232fd9b4e36dbbc540a8ec9a9b47da73" integrity sha512-Y1IQok9821cC9onCx5otgFfRm7Lm+I+wwxOx738M/WLPZ9Q42m4IG5W0FNX8WLL2gYMZo3JkuXIH2DOpWM+qwA== @@ -10712,16 +10315,6 @@ babel-plugin-istanbul@6.1.1, babel-plugin-istanbul@^6.1.1: istanbul-lib-instrument "^5.0.4" test-exclude "^6.0.0" -babel-plugin-jest-hoist@^27.5.1: - version "27.5.1" - resolved "https://registry.yarnpkg.com/babel-plugin-jest-hoist/-/babel-plugin-jest-hoist-27.5.1.tgz#9be98ecf28c331eb9f5df9c72d6f89deb8181c2e" - integrity sha512-50wCwD5EMNW4aRpOwtqzyZHIewTYNxLA4nhB+09d8BIssfNfzBRhkBIHiaPv1Si226TQSvp8gxAJm2iY2qs2hQ== - dependencies: - "@babel/template" "^7.3.3" - "@babel/types" "^7.3.3" - "@types/babel__core" "^7.0.0" - "@types/babel__traverse" "^7.0.6" - babel-plugin-jsx-dom-expressions@^0.37.20: version "0.37.21" resolved "https://registry.yarnpkg.com/babel-plugin-jsx-dom-expressions/-/babel-plugin-jsx-dom-expressions-0.37.21.tgz#8d63d09c183485f228a11f13cbdf1ff25e541a8e" @@ -10819,32 +10412,6 @@ babel-plugin-syntax-dynamic-import@^6.18.0: resolved "https://registry.yarnpkg.com/babel-plugin-syntax-dynamic-import/-/babel-plugin-syntax-dynamic-import-6.18.0.tgz#8d6a26229c83745a9982a441051572caa179b1da" integrity sha1-jWomIpyDdFqZgqRBBRVyyqF5sdo= -babel-preset-current-node-syntax@^1.0.0: - version "1.0.1" - resolved "https://registry.yarnpkg.com/babel-preset-current-node-syntax/-/babel-preset-current-node-syntax-1.0.1.tgz#b4399239b89b2a011f9ddbe3e4f401fc40cff73b" - integrity sha512-M7LQ0bxarkxQoN+vz5aJPsLBn77n8QgTFmo8WK0/44auK2xlCXrYcUxHFxgU7qW5Yzw/CjmLRK2uJzaCd7LvqQ== - dependencies: - "@babel/plugin-syntax-async-generators" "^7.8.4" - "@babel/plugin-syntax-bigint" "^7.8.3" - "@babel/plugin-syntax-class-properties" "^7.8.3" - "@babel/plugin-syntax-import-meta" "^7.8.3" - "@babel/plugin-syntax-json-strings" "^7.8.3" - "@babel/plugin-syntax-logical-assignment-operators" "^7.8.3" - "@babel/plugin-syntax-nullish-coalescing-operator" "^7.8.3" - "@babel/plugin-syntax-numeric-separator" "^7.8.3" - "@babel/plugin-syntax-object-rest-spread" "^7.8.3" - "@babel/plugin-syntax-optional-catch-binding" "^7.8.3" - "@babel/plugin-syntax-optional-chaining" "^7.8.3" - "@babel/plugin-syntax-top-level-await" "^7.8.3" - -babel-preset-jest@^27.5.1: - version "27.5.1" - resolved "https://registry.yarnpkg.com/babel-preset-jest/-/babel-preset-jest-27.5.1.tgz#91f10f58034cb7989cb4f962b69fa6eef6a6bc81" - integrity sha512-Nptf2FzlPCWYuJg41HBqXVT8ym6bXOevuCTbhxlUpjwtysGaIWFvDEjp4y+G7fl13FgOdjs7P/DmErqH7da0Ag== - dependencies: - babel-plugin-jest-hoist "^27.5.1" - babel-preset-current-node-syntax "^1.0.0" - babel-preset-solid@^1.8.4: version "1.8.17" resolved "https://registry.yarnpkg.com/babel-preset-solid/-/babel-preset-solid-1.8.17.tgz#8d55e8e2ee800be85527425e7943534f984dc815" @@ -11620,11 +11187,6 @@ broccoli@^3.5.2: underscore.string "^3.2.2" watch-detector "^1.0.0" -browser-process-hrtime@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/browser-process-hrtime/-/browser-process-hrtime-1.0.0.tgz#3c9b4b7d782c8121e56f10106d84c0d0ffc94626" - integrity sha512-9o5UecI3GhkpM6DrXr69PblIuWxPKk9Y0jHBRhdocZ2y7YECBFCsHm79Pr3OyR2AvjhDkabFJaDJMYRazHgsow== - browserify-zlib@^0.1.4: version "0.1.4" resolved "https://registry.yarnpkg.com/browserify-zlib/-/browserify-zlib-0.1.4.tgz#bb35f8a519f600e0fa6b8485241c979d0141fb2d" @@ -11642,13 +11204,6 @@ browserslist@^4.0.0, browserslist@^4.14.5, browserslist@^4.20.0, browserslist@^4 node-releases "^2.0.18" update-browserslist-db "^1.1.1" -bs-logger@0.x: - version "0.2.6" - resolved "https://registry.yarnpkg.com/bs-logger/-/bs-logger-0.2.6.tgz#eb7d365307a72cf974cc6cda76b68354ad336bd8" - integrity sha512-pd8DCoxmbgc7hyPKOvxtqNcjYoOsABPQdcCUjGp3d42VR2CX1ORhk2A87oqqu5R1kk+76nsxZupkmyd+MVtCog== - dependencies: - fast-json-stable-stringify "2.x" - bser@2.1.1: version "2.1.1" resolved "https://registry.yarnpkg.com/bser/-/bser-2.1.1.tgz#e6787da20ece9d07998533cfd9de6f5c38f4bc05" @@ -12079,11 +11634,6 @@ chalk@^5.0.0, chalk@^5.2.0, chalk@^5.3.0: resolved "https://registry.yarnpkg.com/chalk/-/chalk-5.3.0.tgz#67c20a7ebef70e7f3970a01f90fa210cb6860385" integrity sha512-dLitG79d+GV1Nb/VYcCDFivJeK1hiukt9QjRNVOsUtTy1rR1YJsmpGGTZ3qJos+uw7WmWF4wUwBd9jxjocFC2w== -char-regex@^1.0.2: - version "1.0.2" - resolved "https://registry.yarnpkg.com/char-regex/-/char-regex-1.0.2.tgz#d744358226217f981ed58f479b1d6bcc29545dcf" - integrity sha512-kWWXztvZ5SBQV+eRgKFeh8q5sLuZY2+8WUIzlxWVTg+oGwY14qylx1KbKzHd8P6ZYkAg0xyIDU9JMHhyJMZ1jw== - character-entities-html4@^2.0.0: version "2.1.0" resolved "https://registry.yarnpkg.com/character-entities-html4/-/character-entities-html4-2.1.0.tgz#1f1adb940c971a4b22ba39ddca6b618dc6e56b2b" @@ -12177,7 +11727,7 @@ citty@^0.1.2, citty@^0.1.5, citty@^0.1.6: dependencies: consola "^3.2.3" -cjs-module-lexer@^1.0.0, cjs-module-lexer@^1.2.2: +cjs-module-lexer@^1.2.2: version "1.2.3" resolved "https://registry.yarnpkg.com/cjs-module-lexer/-/cjs-module-lexer-1.2.3.tgz#6c370ab19f8a3394e318fe682686ec0ac684d107" integrity sha512-0TNiGstbQmCFwt4akjjBg5pLRTSyj/PkWQ1ZoO2zntmg9yLqSRxwEa4iCfQLGjqhiqBfOJa7W/E8wfGrTDmlZQ== @@ -12367,11 +11917,6 @@ cmd-shim@6.0.1: resolved "https://registry.yarnpkg.com/cmd-shim/-/cmd-shim-6.0.1.tgz#a65878080548e1dca760b3aea1e21ed05194da9d" integrity sha512-S9iI9y0nKR4hwEQsVWpyxld/6kRfGepGfzff83FcaiEBpmvlbA2nnGe7Cylgrx2f/p1P5S5wpRm9oL8z1PbS3Q== -co@^4.6.0: - version "4.6.0" - resolved "https://registry.yarnpkg.com/co/-/co-4.6.0.tgz#6ea6bdf3d853ae54ccb8e47bfa0bf3f9031fb184" - integrity sha1-bqa989hTrlTMuOR7+gvz+QMfsYQ= - code-red@^1.0.3: version "1.0.4" resolved "https://registry.yarnpkg.com/code-red/-/code-red-1.0.4.tgz#59ba5c9d1d320a4ef795bc10a28bd42bfebe3e35" @@ -12383,11 +11928,6 @@ code-red@^1.0.3: estree-walker "^3.0.3" periscopic "^3.1.0" -collect-v8-coverage@^1.0.0: - version "1.0.1" - resolved "https://registry.yarnpkg.com/collect-v8-coverage/-/collect-v8-coverage-1.0.1.tgz#cc2c8e94fc18bbdffe64d6534570c8a673b27f59" - integrity sha512-iBPtljfCNcTKNAto0KEtDfZ3qzjJvqE3aTGZsbhjSBlorqpXJlaWWtPO35D+ZImoC3KWejX64o+yPGxhWSTzfg== - collection-visit@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/collection-visit/-/collection-visit-1.0.0.tgz#4bc0373c164bc3291b4d368c829cf1a80a59dca0" @@ -12803,7 +12343,7 @@ conventional-recommended-bump@7.0.1: git-semver-tags "^5.0.0" meow "^8.1.2" -convert-source-map@^1.4.0, convert-source-map@^1.5.1, convert-source-map@^1.6.0, convert-source-map@^1.7.0: +convert-source-map@^1.5.1, convert-source-map@^1.7.0: version "1.7.0" resolved "https://registry.yarnpkg.com/convert-source-map/-/convert-source-map-1.7.0.tgz#17a2cb882d7f77d3490585e2ce6c524424a3a442" integrity sha512-4FJkXzKXEDB1snCFZlLP4gpC3JILicCpGbzG9f9G7tGqGCzETQ2hWPrcinA9oU4wtf2biUaEH5065UnMeR33oA== @@ -13147,11 +12687,6 @@ css-what@^6.0.1, css-what@^6.1.0: resolved "https://registry.yarnpkg.com/css-what/-/css-what-6.1.0.tgz#fb5effcf76f1ddea2c81bdfaa4de44e79bac70f4" integrity sha512-HTUrgRJ7r4dsZKU6GjmpfRK1O76h97Z8MfS1G0FozR+oF2kG6Vfe8JE6zwrkbxigziPHinCJ+gCPjA9EaBDtRw== -css.escape@^1.5.1: - version "1.5.1" - resolved "https://registry.yarnpkg.com/css.escape/-/css.escape-1.5.1.tgz#42e27d4fa04ae32f931a4b4d4191fa9cddee97cb" - integrity sha512-YUifsXXuknHlUsmlgyY0PKzgPOr7/FjCePfHNt0jxm83wHZi44VDMQ7/fGNkjY3/jV1MC+1CmZbaHzugyeRtpg== - cssdb@^7.0.0, cssdb@^7.1.0: version "7.11.2" resolved "https://registry.yarnpkg.com/cssdb/-/cssdb-7.11.2.tgz#127a2f5b946ee653361a5af5333ea85a39df5ae5" @@ -13223,23 +12758,6 @@ csso@^5.0.5: dependencies: css-tree "~2.2.0" -cssom@^0.4.4: - version "0.4.4" - resolved "https://registry.yarnpkg.com/cssom/-/cssom-0.4.4.tgz#5a66cf93d2d0b661d80bf6a44fb65f5c2e4e0a10" - integrity sha512-p3pvU7r1MyyqbTk+WbNJIgJjG2VmTIaB10rI93LzVPrmDJKkzKYMtxxyAvQXR/NS6otuzveI7+7BBq3SjBS2mw== - -cssom@~0.3.6: - version "0.3.8" - resolved "https://registry.yarnpkg.com/cssom/-/cssom-0.3.8.tgz#9f1276f5b2b463f2114d3f2c75250af8c1a36f4a" - integrity sha512-b0tGHbfegbhPJpxpiBPU2sCkigAqtM9O121le6bbOlgyV+NyGyCmVfJ6QW9eRjz8CpNfWEOYBIMIGRYkLwsIYg== - -cssstyle@^2.3.0: - version "2.3.0" - resolved "https://registry.yarnpkg.com/cssstyle/-/cssstyle-2.3.0.tgz#ff665a0ddbdc31864b09647f34163443d90b0852" - integrity sha512-AZL67abkUzIuvcHqk7c09cezpGNcxUxU4Ioi/05xHk4DQeTkWmGYftIE6ctU6AEt+Gn4n1lDStOtj7FKycP71A== - dependencies: - cssom "~0.3.6" - cssstyle@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/cssstyle/-/cssstyle-3.0.0.tgz#17ca9c87d26eac764bb8cfd00583cff21ce0277a" @@ -13282,15 +12800,6 @@ data-uri-to-buffer@^3.0.1: resolved "https://registry.yarnpkg.com/data-uri-to-buffer/-/data-uri-to-buffer-3.0.1.tgz#594b8973938c5bc2c33046535785341abc4f3636" integrity sha512-WboRycPNsVw3B3TL559F7kuBUM4d8CgMEvk6xEJlOp7OBPjt6G7z8WMWlD2rOFZLk6OYfFIUGsCOWzcQH9K2og== -data-urls@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/data-urls/-/data-urls-2.0.0.tgz#156485a72963a970f5d5821aaf642bef2bf2db9b" - integrity sha512-X5eWTSXO/BJmpdIKCRuKUgSCgAN0OwliVK3yPKbwIWU1Tdw5BRajxlzMidvh+gwko9AfQ9zIj52pzF91Q3YAvQ== - dependencies: - abab "^2.0.3" - whatwg-mimetype "^2.3.0" - whatwg-url "^8.0.0" - data-urls@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/data-urls/-/data-urls-4.0.0.tgz#333a454eca6f9a5b7b0f1013ff89074c3f522dd4" @@ -13385,7 +12894,7 @@ decamelize@^1.1.0: resolved "https://registry.yarnpkg.com/decamelize/-/decamelize-1.2.0.tgz#f6534d15148269b20352e7bee26f501f9a191290" integrity sha1-9lNNFRSCabIDUue+4m9QH5oZEpA= -decimal.js@^10.2.1, decimal.js@^10.4.3: +decimal.js@^10.4.3: version "10.4.3" resolved "https://registry.yarnpkg.com/decimal.js/-/decimal.js-10.4.3.tgz#1044092884d245d1b7f65725fa4ad4c6f781cc23" integrity sha512-VBBaLc1MgL5XpzgIP7ny5Z6Nx3UrRkIViUkPUdtl9aya5amy3De1gsUUSB1g3+3sExYNjCAsAznmukyxCb1GRA== @@ -13424,7 +12933,7 @@ decorator-transforms@^2.0.0: "@babel/plugin-syntax-decorators" "^7.23.3" babel-import-util "^3.0.0" -dedent@0.7.0, dedent@^0.7.0: +dedent@0.7.0: version "0.7.0" resolved "https://registry.yarnpkg.com/dedent/-/dedent-0.7.0.tgz#2495ddbaf6eb874abb0e1be9df22d2e5a544326c" integrity sha1-JJXduvbrh0q7Dhvp3yLS5aVEMmw= @@ -13678,7 +13187,7 @@ detect-libc@^2.0.0, detect-libc@^2.0.2: resolved "https://registry.yarnpkg.com/detect-libc/-/detect-libc-2.0.2.tgz#8ccf2ba9315350e1241b88d0ac3b0e1fbd99605d" integrity sha512-UX6sGumvvqSaXgdKGUsgZWqcUyIXZ/vZTrlRT/iobiKhGL0zL4d3osHj3uqllWJK+i+sixDS/3COVEOFbupFyw== -detect-newline@3.1.0, detect-newline@^3.0.0: +detect-newline@3.1.0: version "3.1.0" resolved "https://registry.yarnpkg.com/detect-newline/-/detect-newline-3.1.0.tgz#576f5dfc63ae1a192ff192d8ad3af6308991b651" integrity sha512-TLz+x/vEXm/Y7P7wn1EJFNLxYpUD4TgMosxY6fAVJUnJMbupHBOncxyWUG9OpTaH9EBD7uFI5LfEgmMOc54DsA== @@ -13780,11 +13289,6 @@ diff-match-patch@^1.0.5: resolved "https://registry.yarnpkg.com/diff-match-patch/-/diff-match-patch-1.0.5.tgz#abb584d5f10cd1196dfc55aa03701592ae3f7b37" integrity sha512-IayShXAgj/QMXgB0IWmKx+rOPuGMhqm5w6jvFxmVenXKIzRqTAAsbBPT3kWQeGANj3jGgvcvv4yK6SxqYmikgw== -diff-sequences@^27.5.1: - version "27.5.1" - resolved "https://registry.yarnpkg.com/diff-sequences/-/diff-sequences-27.5.1.tgz#eaecc0d327fd68c8d9672a1e64ab8dccb2ef5327" - integrity sha512-k1gCAXAsNgLwEL+Y8Wvl+M6oEFj5bgazfZULpS5CneoPPXRaCCW7dm+q21Ky2VEE5X+VeRDBVg1Pcvvsr4TtNQ== - diff-sequences@^29.6.3: version "29.6.3" resolved "https://registry.yarnpkg.com/diff-sequences/-/diff-sequences-29.6.3.tgz#4deaf894d11407c51efc8418012f9e70b84ea921" @@ -13851,11 +13355,6 @@ dom-accessibility-api@^0.5.9: resolved "https://registry.yarnpkg.com/dom-accessibility-api/-/dom-accessibility-api-0.5.13.tgz#102ee5f25eacce09bdf1cfa5a298f86da473be4b" integrity sha512-R305kwb5CcMDIpSHUnLyIAp7SrSPBx6F0VfQFB3M75xVMHhXJJIdePYgbPPh1o57vCHNu5QztokWUPsLjWzFqw== -dom-accessibility-api@^0.6.3: - version "0.6.3" - resolved "https://registry.yarnpkg.com/dom-accessibility-api/-/dom-accessibility-api-0.6.3.tgz#993e925cc1d73f2c662e7d75dd5a5445259a8fd8" - integrity sha512-7ZgogeTnjuHbo+ct10G9Ffp0mif17idi0IyWNVA/wcwcm7NPOD/WEHVP3n7n3MhXqxoIYm8d6MuZohYWIZ4T3w== - dom-converter@^0.2.0: version "0.2.0" resolved "https://registry.yarnpkg.com/dom-converter/-/dom-converter-0.2.0.tgz#6721a9daee2e293682955b6afe416771627bb768" @@ -13898,13 +13397,6 @@ domexception@^1.0.1: dependencies: webidl-conversions "^4.0.2" -domexception@^2.0.1: - version "2.0.1" - resolved "https://registry.yarnpkg.com/domexception/-/domexception-2.0.1.tgz#fb44aefba793e1574b0af6aed2801d057529f304" - integrity sha512-yxJ2mFy/sibVQlu5qHjOkf9J3K6zgmCxgJ94u2EdvDOV09H+32LtRswEcUsmUWN72pVLOEnTSRaIVVzVQgS0dg== - dependencies: - webidl-conversions "^5.0.0" - domexception@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/domexception/-/domexception-4.0.0.tgz#4ad1be56ccadc86fc76d033353999a8037d03673" @@ -14617,11 +14109,6 @@ ember-template-recast@^6.1.3, ember-template-recast@^6.1.4: tmp "^0.2.1" workerpool "^6.4.0" -emittery@^0.8.1: - version "0.8.1" - resolved "https://registry.yarnpkg.com/emittery/-/emittery-0.8.1.tgz#bb23cc86d03b30aa75a7f734819dee2e1ba70860" - integrity sha512-uDfvUjVrfGJJhymx/kz6prltenw1u7WrCg1oa94zYY8xxVpLLUu045LAT0dhDZdXG58/EpPL/5kA180fQ/qudg== - emoji-regex@^10.2.1: version "10.3.0" resolved "https://registry.yarnpkg.com/emoji-regex/-/emoji-regex-10.3.0.tgz#76998b9268409eb3dae3de989254d456e70cfe23" @@ -15401,11 +14888,6 @@ escape-string-regexp@^1.0.2, escape-string-regexp@^1.0.5: resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz#1b61c0562190a8dff6ae3bb2cf0200ca130b86d4" integrity sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ= -escape-string-regexp@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-2.0.0.tgz#a30304e99daa32e23b2fd20f51babd07cffca344" - integrity sha512-UpzcLCXolUWcNu5HtVMHYdXJjArjsF9C0aNnquZYY4uW/Vu0miy5YoWvbV345HauVvcAUnpRuhMMcqTcGOY2+w== - escape-string-regexp@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz#14ba83a5d373e3d311e5afca29cf5bfad965bf34" @@ -15506,13 +14988,6 @@ eslint-plugin-import@^2.22.0: resolve "^1.17.0" tsconfig-paths "^3.9.0" -eslint-plugin-jest@^27.2.2: - version "27.2.2" - resolved "https://registry.yarnpkg.com/eslint-plugin-jest/-/eslint-plugin-jest-27.2.2.tgz#be4ded5f91905d9ec89aa8968d39c71f3b072c0c" - integrity sha512-euzbp06F934Z7UDl5ZUaRPLAc9MKjh0rMPERrHT7UhlCEwgb25kBj37TvMgWeHZVkR5I9CayswrpoaqZU1RImw== - dependencies: - "@typescript-eslint/utils" "^5.10.0" - eslint-plugin-jsdoc@^30.0.3: version "30.7.13" resolved "https://registry.yarnpkg.com/eslint-plugin-jsdoc/-/eslint-plugin-jsdoc-30.7.13.tgz#52e5c74fb806d3bbeb51d04a0c829508c3c6b563" @@ -15930,16 +15405,6 @@ expect-type@^1.1.0: resolved "https://registry.yarnpkg.com/expect-type/-/expect-type-1.1.0.tgz#a146e414250d13dfc49eafcfd1344a4060fa4c75" integrity sha512-bFi65yM+xZgk+u/KRIpekdSYkTB5W1pEf0Lt8Q8Msh7b+eQ7LXVtIB1Bkm4fvclDEL1b2CZkMhv2mOeF8tMdkA== -expect@^27.5.1: - version "27.5.1" - resolved "https://registry.yarnpkg.com/expect/-/expect-27.5.1.tgz#83ce59f1e5bdf5f9d2b94b61d2050db48f3fef74" - integrity sha512-E1q5hSUG2AmYQwQJ041nvgpkODHQvB+RKlB4IYdru6uJsyFTRyZAP463M+1lINorwbqAmUggi6+WwkD8lCS/Dw== - dependencies: - "@jest/types" "^27.5.1" - jest-get-type "^27.5.1" - jest-matcher-utils "^27.5.1" - jest-message-util "^27.5.1" - exponential-backoff@^3.1.1: version "3.1.1" resolved "https://registry.yarnpkg.com/exponential-backoff/-/exponential-backoff-3.1.1.tgz#64ac7526fe341ab18a39016cd22c787d01e00bf6" @@ -16079,7 +15544,7 @@ fast-glob@^3.0.3, fast-glob@^3.2.11, fast-glob@^3.2.7, fast-glob@^3.2.9, fast-gl merge2 "^1.3.0" micromatch "^4.0.4" -fast-json-stable-stringify@2.x, fast-json-stable-stringify@^2.0.0, fast-json-stable-stringify@^2.1.0: +fast-json-stable-stringify@^2.0.0, fast-json-stable-stringify@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz#874bf69c6f404c2b5d99c481341399fd55892633" integrity sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw== @@ -16514,15 +15979,6 @@ foreground-child@^3.1.0: cross-spawn "^7.0.0" signal-exit "^4.0.1" -form-data@^3.0.0: - version "3.0.1" - resolved "https://registry.yarnpkg.com/form-data/-/form-data-3.0.1.tgz#ebd53791b78356a99af9a300d4282c4d5eb9755f" - integrity sha512-RHkBKtLWUVwd7SqRIvCZMEvAMoGUp0XU+seQiZejj0COz3RI3hWP4sCv3gZWWLjJTd7rGwcsF5eKZGii0r/hbg== - dependencies: - asynckit "^0.4.0" - combined-stream "^1.0.8" - mime-types "^2.1.12" - form-data@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/form-data/-/form-data-4.0.0.tgz#93919daeaf361ee529584b9b31664dc12c9fa452" @@ -16710,7 +16166,7 @@ fsevents@2.3.2: resolved "https://registry.yarnpkg.com/fsevents/-/fsevents-2.3.2.tgz#8a526f78b8fdf4623b709e0b975c52c24c02fd1a" integrity sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA== -fsevents@^2.3.2, fsevents@~2.3.2, fsevents@~2.3.3: +fsevents@~2.3.2, 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== @@ -17104,7 +16560,7 @@ glob@^5.0.10: once "^1.3.0" path-is-absolute "^1.0.0" -glob@^7.0.0, glob@^7.0.4, glob@^7.1.1, glob@^7.1.2, glob@^7.1.3, glob@^7.1.4, glob@^7.1.6, glob@^7.2.3, glob@~7.2.0: +glob@^7.0.0, glob@^7.0.4, glob@^7.1.2, glob@^7.1.3, glob@^7.1.4, glob@^7.1.6, glob@^7.2.3, glob@~7.2.0: version "7.2.3" resolved "https://registry.yarnpkg.com/glob/-/glob-7.2.3.tgz#b8df0fb802bbfa8e89bd1d938b4e16578ed44f2b" integrity sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q== @@ -17875,13 +17331,6 @@ hpack.js@^2.1.6: readable-stream "^2.0.1" wbuf "^1.1.0" -html-encoding-sniffer@^2.0.1: - version "2.0.1" - resolved "https://registry.yarnpkg.com/html-encoding-sniffer/-/html-encoding-sniffer-2.0.1.tgz#42a6dc4fd33f00281176e8b23759ca4e4fa185f3" - integrity sha512-D5JbOMBIR/TVZkubHT+OyT2705QvogUW4IBn6nHd756OwieSF9aDYFj4dv6HHEVGYbHaLETa3WggZYWWMyy3ZQ== - dependencies: - whatwg-encoding "^1.0.5" - html-encoding-sniffer@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/html-encoding-sniffer/-/html-encoding-sniffer-3.0.0.tgz#2cb1a8cf0db52414776e5b2a7a04d5dd98158de9" @@ -17999,7 +17448,7 @@ http-parser-js@>=0.5.1: resolved "https://registry.yarnpkg.com/http-parser-js/-/http-parser-js-0.5.3.tgz#01d2709c79d41698bb01d4decc5e9da4e4a033d9" integrity sha512-t7hjvef/5HEK7RWTdUzVUhl8zkEu+LlaE0IYzdMuvbSDipxBRpOn4Uhw8ZyECEa808iVT8XCjzo6xmYt4CiLZg== -http-proxy-agent@^4.0.0, http-proxy-agent@^4.0.1: +http-proxy-agent@^4.0.0: version "4.0.1" resolved "https://registry.yarnpkg.com/http-proxy-agent/-/http-proxy-agent-4.0.1.tgz#8a8c8ef7f5932ccf953c296ca8291b95aa74aa3a" integrity sha512-k0zdNgqWTGA6aeIRVpvfVob4fL52dTfaehylg0Y4UvSySvOq/Y+BOyPrgpUrA7HylqvU8vIZGsRuXmspskV0Tg== @@ -18211,7 +17660,7 @@ import-lazy@^2.1.0: resolved "https://registry.yarnpkg.com/import-lazy/-/import-lazy-2.1.0.tgz#05698e3d45c88e8d7e9d92cb0584e77f096f3e43" integrity sha512-m7ZEHgtw69qOGw+jwxXkHlrlIPdTGkyh66zXZ1ajZbxkDBNjSY/LGbmjc7h0s2ELsUDTAhFr55TrPSSqJGPG0A== -import-local@3.1.0, import-local@^3.0.2: +import-local@3.1.0: version "3.1.0" resolved "https://registry.yarnpkg.com/import-local/-/import-local-3.1.0.tgz#b4479df8a5fd44f6cdce24070675676063c95cb4" integrity sha512-ASB07uLtnDs1o6EHjKpX34BKYDSqnFerfTOJL2HvMqF70LnxpjkzDB8J44oT9pu4AMPkQwf8jl6szgvNd2tRIg== @@ -18661,11 +18110,6 @@ is-fullwidth-code-point@^3.0.0: resolved "https://registry.yarnpkg.com/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz#f116f8064fe90b3f7844a38997c0b75051269f1d" integrity sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg== -is-generator-fn@^2.0.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/is-generator-fn/-/is-generator-fn-2.1.0.tgz#7d140adc389aaf3011a8f2a2a4cfa6faadffb118" - integrity sha512-cTIB4yPYL/Grw0EaSzASzg6bBy9gqCofvWN8okThAYIxKJZC+udlRAmGbM0XLeniEJSs8uEgHPGuHSe1XsOLSQ== - is-generator-function@^1.0.7: version "1.0.8" resolved "https://registry.yarnpkg.com/is-generator-function/-/is-generator-function-1.0.8.tgz#dfb5c2b120e02b0a8d9d2c6806cd5621aa922f7b" @@ -19090,7 +18534,7 @@ istanbul-lib-coverage@^3.0.0, istanbul-lib-coverage@^3.2.0, istanbul-lib-coverag resolved "https://registry.yarnpkg.com/istanbul-lib-coverage/-/istanbul-lib-coverage-3.2.2.tgz#2d166c4b0644d43a39f04bf6c2edd1e585f31756" integrity sha512-O8dpsF+r0WV/8MNRKfnmrtCWhuKjxrq2w+jpzBL5UZKTi2LeVWnWOmWRxFlesJONmc+wLAGvKQZEOanko0LFTg== -istanbul-lib-instrument@^5.0.4, istanbul-lib-instrument@^5.1.0: +istanbul-lib-instrument@^5.0.4: version "5.1.0" resolved "https://registry.yarnpkg.com/istanbul-lib-instrument/-/istanbul-lib-instrument-5.1.0.tgz#7b49198b657b27a730b8e9cb601f1e1bff24c59a" integrity sha512-czwUz525rkOFDJxfKK6mYfIs9zBKILyrZQxjz3ABhjQXhbhFsSbo1HW/BFcsDnfJYJWA6thRR5/TUY2qs5W99Q== @@ -19110,15 +18554,6 @@ istanbul-lib-report@^3.0.0, istanbul-lib-report@^3.0.1: make-dir "^4.0.0" supports-color "^7.1.0" -istanbul-lib-source-maps@^4.0.0: - version "4.0.1" - resolved "https://registry.yarnpkg.com/istanbul-lib-source-maps/-/istanbul-lib-source-maps-4.0.1.tgz#895f3a709fcfba34c6de5a42939022f3e4358551" - integrity sha512-n3s8EwkdFIJCG3BPKBYvskgXGoy88ARzvegkitk60NxRdwltLOTaH7CUiMRXvwYorl0Q712iEjcWB+fK/MrWVw== - dependencies: - debug "^4.1.1" - istanbul-lib-coverage "^3.0.0" - source-map "^0.6.1" - istanbul-lib-source-maps@^5.0.6: version "5.0.6" resolved "https://registry.yarnpkg.com/istanbul-lib-source-maps/-/istanbul-lib-source-maps-5.0.6.tgz#acaef948df7747c8eb5fbf1265cb980f6353a441" @@ -19128,7 +18563,7 @@ istanbul-lib-source-maps@^5.0.6: debug "^4.1.1" istanbul-lib-coverage "^3.0.0" -istanbul-reports@^3.1.3, istanbul-reports@^3.1.7: +istanbul-reports@^3.1.7: version "3.1.7" resolved "https://registry.yarnpkg.com/istanbul-reports/-/istanbul-reports-3.1.7.tgz#daed12b9e1dca518e15c056e1e537e741280fa0b" integrity sha512-BewmUXImeuRk2YY0PVbxgKAysvhRPUQE0h5QRM++nVWyubKGV0l8qQ5op8+B2DOmwSe63Jivj0BjkPQVf8fP5g== @@ -19185,88 +18620,6 @@ jake@^10.8.5: filelist "^1.0.1" minimatch "^3.0.4" -jest-changed-files@^27.5.1: - version "27.5.1" - resolved "https://registry.yarnpkg.com/jest-changed-files/-/jest-changed-files-27.5.1.tgz#a348aed00ec9bf671cc58a66fcbe7c3dfd6a68f5" - integrity sha512-buBLMiByfWGCoMsLLzGUUSpAmIAGnbR2KJoMN10ziLhOLvP4e0SlypHnAel8iqQXTrcbmfEY9sSqae5sgUsTvw== - dependencies: - "@jest/types" "^27.5.1" - execa "^5.0.0" - throat "^6.0.1" - -jest-circus@^27.5.1: - version "27.5.1" - resolved "https://registry.yarnpkg.com/jest-circus/-/jest-circus-27.5.1.tgz#37a5a4459b7bf4406e53d637b49d22c65d125ecc" - integrity sha512-D95R7x5UtlMA5iBYsOHFFbMD/GVA4R/Kdq15f7xYWUfWHBto9NYRsOvnSauTgdF+ogCpJ4tyKOXhUifxS65gdw== - dependencies: - "@jest/environment" "^27.5.1" - "@jest/test-result" "^27.5.1" - "@jest/types" "^27.5.1" - "@types/node" "*" - chalk "^4.0.0" - co "^4.6.0" - dedent "^0.7.0" - expect "^27.5.1" - is-generator-fn "^2.0.0" - jest-each "^27.5.1" - jest-matcher-utils "^27.5.1" - jest-message-util "^27.5.1" - jest-runtime "^27.5.1" - jest-snapshot "^27.5.1" - jest-util "^27.5.1" - pretty-format "^27.5.1" - slash "^3.0.0" - stack-utils "^2.0.3" - throat "^6.0.1" - -jest-cli@^27.5.1: - version "27.5.1" - resolved "https://registry.yarnpkg.com/jest-cli/-/jest-cli-27.5.1.tgz#278794a6e6458ea8029547e6c6cbf673bd30b145" - integrity sha512-Hc6HOOwYq4/74/c62dEE3r5elx8wjYqxY0r0G/nFrLDPMFRu6RA/u8qINOIkvhxG7mMQ5EJsOGfRpI8L6eFUVw== - dependencies: - "@jest/core" "^27.5.1" - "@jest/test-result" "^27.5.1" - "@jest/types" "^27.5.1" - chalk "^4.0.0" - exit "^0.1.2" - graceful-fs "^4.2.9" - import-local "^3.0.2" - jest-config "^27.5.1" - jest-util "^27.5.1" - jest-validate "^27.5.1" - prompts "^2.0.1" - yargs "^16.2.0" - -jest-config@^27.5.1: - version "27.5.1" - resolved "https://registry.yarnpkg.com/jest-config/-/jest-config-27.5.1.tgz#5c387de33dca3f99ad6357ddeccd91bf3a0e4a41" - integrity sha512-5sAsjm6tGdsVbW9ahcChPAFCk4IlkQUknH5AvKjuLTSlcO/wCZKyFdn7Rg0EkC+OGgWODEy2hDpWB1PgzH0JNA== - dependencies: - "@babel/core" "^7.8.0" - "@jest/test-sequencer" "^27.5.1" - "@jest/types" "^27.5.1" - babel-jest "^27.5.1" - chalk "^4.0.0" - ci-info "^3.2.0" - deepmerge "^4.2.2" - glob "^7.1.1" - graceful-fs "^4.2.9" - jest-circus "^27.5.1" - jest-environment-jsdom "^27.5.1" - jest-environment-node "^27.5.1" - jest-get-type "^27.5.1" - jest-jasmine2 "^27.5.1" - jest-regex-util "^27.5.1" - jest-resolve "^27.5.1" - jest-runner "^27.5.1" - jest-util "^27.5.1" - jest-validate "^27.5.1" - micromatch "^4.0.4" - parse-json "^5.2.0" - pretty-format "^27.5.1" - slash "^3.0.0" - strip-json-comments "^3.1.1" - "jest-diff@>=29.4.3 < 30", jest-diff@^29.7.0: version "29.7.0" resolved "https://registry.yarnpkg.com/jest-diff/-/jest-diff-29.7.0.tgz#017934a66ebb7ecf6f205e84699be10afd70458a" @@ -19277,140 +18630,11 @@ jest-config@^27.5.1: jest-get-type "^29.6.3" pretty-format "^29.7.0" -jest-diff@^27.5.1: - version "27.5.1" - resolved "https://registry.yarnpkg.com/jest-diff/-/jest-diff-27.5.1.tgz#a07f5011ac9e6643cf8a95a462b7b1ecf6680def" - integrity sha512-m0NvkX55LDt9T4mctTEgnZk3fmEg3NRYutvMPWM/0iPnkFj2wIeF45O1718cMSOFO1vINkqmxqD8vE37uTEbqw== - dependencies: - chalk "^4.0.0" - diff-sequences "^27.5.1" - jest-get-type "^27.5.1" - pretty-format "^27.5.1" - -jest-docblock@^27.5.1: - version "27.5.1" - resolved "https://registry.yarnpkg.com/jest-docblock/-/jest-docblock-27.5.1.tgz#14092f364a42c6108d42c33c8cf30e058e25f6c0" - integrity sha512-rl7hlABeTsRYxKiUfpHrQrG4e2obOiTQWfMEH3PxPjOtdsfLQO4ReWSZaQ7DETm4xu07rl4q/h4zcKXyU0/OzQ== - dependencies: - detect-newline "^3.0.0" - -jest-each@^27.5.1: - version "27.5.1" - resolved "https://registry.yarnpkg.com/jest-each/-/jest-each-27.5.1.tgz#5bc87016f45ed9507fed6e4702a5b468a5b2c44e" - integrity sha512-1Ff6p+FbhT/bXQnEouYy00bkNSY7OUpfIcmdl8vZ31A1UUaurOLPA8a8BbJOF2RDUElwJhmeaV7LnagI+5UwNQ== - dependencies: - "@jest/types" "^27.5.1" - chalk "^4.0.0" - jest-get-type "^27.5.1" - jest-util "^27.5.1" - pretty-format "^27.5.1" - -jest-environment-jsdom@^27.5.1: - version "27.5.1" - resolved "https://registry.yarnpkg.com/jest-environment-jsdom/-/jest-environment-jsdom-27.5.1.tgz#ea9ccd1fc610209655a77898f86b2b559516a546" - integrity sha512-TFBvkTC1Hnnnrka/fUb56atfDtJ9VMZ94JkjTbggl1PEpwrYtUBKMezB3inLmWqQsXYLcMwNoDQwoBTAvFfsfw== - dependencies: - "@jest/environment" "^27.5.1" - "@jest/fake-timers" "^27.5.1" - "@jest/types" "^27.5.1" - "@types/node" "*" - jest-mock "^27.5.1" - jest-util "^27.5.1" - jsdom "^16.6.0" - -jest-environment-node@^27.5.1: - version "27.5.1" - resolved "https://registry.yarnpkg.com/jest-environment-node/-/jest-environment-node-27.5.1.tgz#dedc2cfe52fab6b8f5714b4808aefa85357a365e" - integrity sha512-Jt4ZUnxdOsTGwSRAfKEnE6BcwsSPNOijjwifq5sDFSA2kesnXTvNqKHYgM0hDq3549Uf/KzdXNYn4wMZJPlFLw== - dependencies: - "@jest/environment" "^27.5.1" - "@jest/fake-timers" "^27.5.1" - "@jest/types" "^27.5.1" - "@types/node" "*" - jest-mock "^27.5.1" - jest-util "^27.5.1" - -jest-get-type@^27.5.1: - version "27.5.1" - resolved "https://registry.yarnpkg.com/jest-get-type/-/jest-get-type-27.5.1.tgz#3cd613c507b0f7ace013df407a1c1cd578bcb4f1" - integrity sha512-2KY95ksYSaK7DMBWQn6dQz3kqAf3BB64y2udeG+hv4KfSOb9qwcYQstTJc1KCbsix+wLZWZYN8t7nwX3GOBLRw== - jest-get-type@^29.6.3: version "29.6.3" resolved "https://registry.yarnpkg.com/jest-get-type/-/jest-get-type-29.6.3.tgz#36f499fdcea197c1045a127319c0481723908fd1" integrity sha512-zrteXnqYxfQh7l5FHyL38jL39di8H8rHoecLH3JNxH3BwOrBsNeabdap5e0I23lD4HHI8W5VFBZqG4Eaq5LNcw== -jest-haste-map@^27.5.1: - version "27.5.1" - resolved "https://registry.yarnpkg.com/jest-haste-map/-/jest-haste-map-27.5.1.tgz#9fd8bd7e7b4fa502d9c6164c5640512b4e811e7f" - integrity sha512-7GgkZ4Fw4NFbMSDSpZwXeBiIbx+t/46nJ2QitkOjvwPYyZmqttu2TDSimMHP1EkPOi4xUZAN1doE5Vd25H4Jng== - dependencies: - "@jest/types" "^27.5.1" - "@types/graceful-fs" "^4.1.2" - "@types/node" "*" - anymatch "^3.0.3" - fb-watchman "^2.0.0" - graceful-fs "^4.2.9" - jest-regex-util "^27.5.1" - jest-serializer "^27.5.1" - jest-util "^27.5.1" - jest-worker "^27.5.1" - micromatch "^4.0.4" - walker "^1.0.7" - optionalDependencies: - fsevents "^2.3.2" - -jest-jasmine2@^27.5.1: - version "27.5.1" - resolved "https://registry.yarnpkg.com/jest-jasmine2/-/jest-jasmine2-27.5.1.tgz#a037b0034ef49a9f3d71c4375a796f3b230d1ac4" - integrity sha512-jtq7VVyG8SqAorDpApwiJJImd0V2wv1xzdheGHRGyuT7gZm6gG47QEskOlzsN1PG/6WNaCo5pmwMHDf3AkG2pQ== - dependencies: - "@jest/environment" "^27.5.1" - "@jest/source-map" "^27.5.1" - "@jest/test-result" "^27.5.1" - "@jest/types" "^27.5.1" - "@types/node" "*" - chalk "^4.0.0" - co "^4.6.0" - expect "^27.5.1" - is-generator-fn "^2.0.0" - jest-each "^27.5.1" - jest-matcher-utils "^27.5.1" - jest-message-util "^27.5.1" - jest-runtime "^27.5.1" - jest-snapshot "^27.5.1" - jest-util "^27.5.1" - pretty-format "^27.5.1" - throat "^6.0.1" - -jest-junit@^16.0.0: - version "16.0.0" - resolved "https://registry.yarnpkg.com/jest-junit/-/jest-junit-16.0.0.tgz#d838e8c561cf9fdd7eb54f63020777eee4136785" - integrity sha512-A94mmw6NfJab4Fg/BlvVOUXzXgF0XIH6EmTgJ5NDPp4xoKq0Kr7sErb+4Xs9nZvu58pJojz5RFGpqnZYJTrRfQ== - dependencies: - mkdirp "^1.0.4" - strip-ansi "^6.0.1" - uuid "^8.3.2" - xml "^1.0.1" - -jest-leak-detector@^27.5.1: - version "27.5.1" - resolved "https://registry.yarnpkg.com/jest-leak-detector/-/jest-leak-detector-27.5.1.tgz#6ec9d54c3579dd6e3e66d70e3498adf80fde3fb8" - integrity sha512-POXfWAMvfU6WMUXftV4HolnJfnPOGEu10fscNCA76KBpRRhcMN2c8d3iT2pxQS3HLbA+5X4sOUPzYO2NUyIlHQ== - dependencies: - jest-get-type "^27.5.1" - pretty-format "^27.5.1" - -jest-matcher-utils@^27.0.0, jest-matcher-utils@^27.5.1: - version "27.5.1" - resolved "https://registry.yarnpkg.com/jest-matcher-utils/-/jest-matcher-utils-27.5.1.tgz#9c0cdbda8245bc22d2331729d1091308b40cf8ab" - integrity sha512-z2uTx/T6LBaCoNWNFWwChLBKYxTMcGBRjAt+2SbP929/Fflb9aa5LGma654Rz8z9HLxsrUaYzxE9T/EFIL/PAw== - dependencies: - chalk "^4.0.0" - jest-diff "^27.5.1" - jest-get-type "^27.5.1" - pretty-format "^27.5.1" - jest-matcher-utils@^29.0.0: version "29.7.0" resolved "https://registry.yarnpkg.com/jest-matcher-utils/-/jest-matcher-utils-29.7.0.tgz#ae8fec79ff249fd592ce80e3ee474e83a6c44f12" @@ -19421,193 +18645,7 @@ jest-matcher-utils@^29.0.0: jest-get-type "^29.6.3" pretty-format "^29.7.0" -jest-message-util@^27.5.1: - version "27.5.1" - resolved "https://registry.yarnpkg.com/jest-message-util/-/jest-message-util-27.5.1.tgz#bdda72806da10d9ed6425e12afff38cd1458b6cf" - integrity sha512-rMyFe1+jnyAAf+NHwTclDz0eAaLkVDdKVHHBFWsBWHnnh5YeJMNWWsv7AbFYXfK3oTqvL7VTWkhNLu1jX24D+g== - dependencies: - "@babel/code-frame" "^7.12.13" - "@jest/types" "^27.5.1" - "@types/stack-utils" "^2.0.0" - chalk "^4.0.0" - graceful-fs "^4.2.9" - micromatch "^4.0.4" - pretty-format "^27.5.1" - slash "^3.0.0" - stack-utils "^2.0.3" - -jest-mock@^27.5.1: - version "27.5.1" - resolved "https://registry.yarnpkg.com/jest-mock/-/jest-mock-27.5.1.tgz#19948336d49ef4d9c52021d34ac7b5f36ff967d6" - integrity sha512-K4jKbY1d4ENhbrG2zuPWaQBvDly+iZ2yAW+T1fATN78hc0sInwn7wZB8XtlNnvHug5RMwV897Xm4LqmPM4e2Og== - dependencies: - "@jest/types" "^27.5.1" - "@types/node" "*" - -jest-pnp-resolver@^1.2.2: - version "1.2.2" - resolved "https://registry.yarnpkg.com/jest-pnp-resolver/-/jest-pnp-resolver-1.2.2.tgz#b704ac0ae028a89108a4d040b3f919dfddc8e33c" - integrity sha512-olV41bKSMm8BdnuMsewT4jqlZ8+3TCARAXjZGT9jcoSnrfUnRCqnMoF9XEeoWjbzObpqF9dRhHQj0Xb9QdF6/w== - -jest-regex-util@^27.5.1: - version "27.5.1" - resolved "https://registry.yarnpkg.com/jest-regex-util/-/jest-regex-util-27.5.1.tgz#4da143f7e9fd1e542d4aa69617b38e4a78365b95" - integrity sha512-4bfKq2zie+x16okqDXjXn9ql2B0dScQu+vcwe4TvFVhkVyuWLqpZrZtXxLLWoXYgn0E87I6r6GRYHF7wFZBUvg== - -jest-resolve-dependencies@^27.5.1: - version "27.5.1" - resolved "https://registry.yarnpkg.com/jest-resolve-dependencies/-/jest-resolve-dependencies-27.5.1.tgz#d811ecc8305e731cc86dd79741ee98fed06f1da8" - integrity sha512-QQOOdY4PE39iawDn5rzbIePNigfe5B9Z91GDD1ae/xNDlu9kaat8QQ5EKnNmVWPV54hUdxCVwwj6YMgR2O7IOg== - dependencies: - "@jest/types" "^27.5.1" - jest-regex-util "^27.5.1" - jest-snapshot "^27.5.1" - -jest-resolve@^27.5.1: - version "27.5.1" - resolved "https://registry.yarnpkg.com/jest-resolve/-/jest-resolve-27.5.1.tgz#a2f1c5a0796ec18fe9eb1536ac3814c23617b384" - integrity sha512-FFDy8/9E6CV83IMbDpcjOhumAQPDyETnU2KZ1O98DwTnz8AOBsW/Xv3GySr1mOZdItLR+zDZ7I/UdTFbgSOVCw== - dependencies: - "@jest/types" "^27.5.1" - chalk "^4.0.0" - graceful-fs "^4.2.9" - jest-haste-map "^27.5.1" - jest-pnp-resolver "^1.2.2" - jest-util "^27.5.1" - jest-validate "^27.5.1" - resolve "^1.20.0" - resolve.exports "^1.1.0" - slash "^3.0.0" - -jest-runner@^27.5.1: - version "27.5.1" - resolved "https://registry.yarnpkg.com/jest-runner/-/jest-runner-27.5.1.tgz#071b27c1fa30d90540805c5645a0ec167c7b62e5" - integrity sha512-g4NPsM4mFCOwFKXO4p/H/kWGdJp9V8kURY2lX8Me2drgXqG7rrZAx5kv+5H7wtt/cdFIjhqYx1HrlqWHaOvDaQ== - dependencies: - "@jest/console" "^27.5.1" - "@jest/environment" "^27.5.1" - "@jest/test-result" "^27.5.1" - "@jest/transform" "^27.5.1" - "@jest/types" "^27.5.1" - "@types/node" "*" - chalk "^4.0.0" - emittery "^0.8.1" - graceful-fs "^4.2.9" - jest-docblock "^27.5.1" - jest-environment-jsdom "^27.5.1" - jest-environment-node "^27.5.1" - jest-haste-map "^27.5.1" - jest-leak-detector "^27.5.1" - jest-message-util "^27.5.1" - jest-resolve "^27.5.1" - jest-runtime "^27.5.1" - jest-util "^27.5.1" - jest-worker "^27.5.1" - source-map-support "^0.5.6" - throat "^6.0.1" - -jest-runtime@^27.5.1: - version "27.5.1" - resolved "https://registry.yarnpkg.com/jest-runtime/-/jest-runtime-27.5.1.tgz#4896003d7a334f7e8e4a53ba93fb9bcd3db0a1af" - integrity sha512-o7gxw3Gf+H2IGt8fv0RiyE1+r83FJBRruoA+FXrlHw6xEyBsU8ugA6IPfTdVyA0w8HClpbK+DGJxH59UrNMx8A== - dependencies: - "@jest/environment" "^27.5.1" - "@jest/fake-timers" "^27.5.1" - "@jest/globals" "^27.5.1" - "@jest/source-map" "^27.5.1" - "@jest/test-result" "^27.5.1" - "@jest/transform" "^27.5.1" - "@jest/types" "^27.5.1" - chalk "^4.0.0" - cjs-module-lexer "^1.0.0" - collect-v8-coverage "^1.0.0" - execa "^5.0.0" - glob "^7.1.3" - graceful-fs "^4.2.9" - jest-haste-map "^27.5.1" - jest-message-util "^27.5.1" - jest-mock "^27.5.1" - jest-regex-util "^27.5.1" - jest-resolve "^27.5.1" - jest-snapshot "^27.5.1" - jest-util "^27.5.1" - slash "^3.0.0" - strip-bom "^4.0.0" - -jest-serializer@^27.5.1: - version "27.5.1" - resolved "https://registry.yarnpkg.com/jest-serializer/-/jest-serializer-27.5.1.tgz#81438410a30ea66fd57ff730835123dea1fb1f64" - integrity sha512-jZCyo6iIxO1aqUxpuBlwTDMkzOAJS4a3eYz3YzgxxVQFwLeSA7Jfq5cbqCY+JLvTDrWirgusI/0KwxKMgrdf7w== - dependencies: - "@types/node" "*" - graceful-fs "^4.2.9" - -jest-snapshot@^27.5.1: - version "27.5.1" - resolved "https://registry.yarnpkg.com/jest-snapshot/-/jest-snapshot-27.5.1.tgz#b668d50d23d38054a51b42c4039cab59ae6eb6a1" - integrity sha512-yYykXI5a0I31xX67mgeLw1DZ0bJB+gpq5IpSuCAoyDi0+BhgU/RIrL+RTzDmkNTchvDFWKP8lp+w/42Z3us5sA== - dependencies: - "@babel/core" "^7.7.2" - "@babel/generator" "^7.7.2" - "@babel/plugin-syntax-typescript" "^7.7.2" - "@babel/traverse" "^7.7.2" - "@babel/types" "^7.0.0" - "@jest/transform" "^27.5.1" - "@jest/types" "^27.5.1" - "@types/babel__traverse" "^7.0.4" - "@types/prettier" "^2.1.5" - babel-preset-current-node-syntax "^1.0.0" - chalk "^4.0.0" - expect "^27.5.1" - graceful-fs "^4.2.9" - jest-diff "^27.5.1" - jest-get-type "^27.5.1" - jest-haste-map "^27.5.1" - jest-matcher-utils "^27.5.1" - jest-message-util "^27.5.1" - jest-util "^27.5.1" - natural-compare "^1.4.0" - pretty-format "^27.5.1" - semver "^7.3.2" - -jest-util@^27.0.0, jest-util@^27.5.1: - version "27.5.1" - resolved "https://registry.yarnpkg.com/jest-util/-/jest-util-27.5.1.tgz#3ba9771e8e31a0b85da48fe0b0891fb86c01c2f9" - integrity sha512-Kv2o/8jNvX1MQ0KGtw480E/w4fBCDOnH6+6DmeKi6LZUIlKA5kwY0YNdlzaWTiVgxqAqik11QyxDOKk543aKXw== - dependencies: - "@jest/types" "^27.5.1" - "@types/node" "*" - chalk "^4.0.0" - ci-info "^3.2.0" - graceful-fs "^4.2.9" - picomatch "^2.2.3" - -jest-validate@^27.5.1: - version "27.5.1" - resolved "https://registry.yarnpkg.com/jest-validate/-/jest-validate-27.5.1.tgz#9197d54dc0bdb52260b8db40b46ae668e04df067" - integrity sha512-thkNli0LYTmOI1tDB3FI1S1RTp/Bqyd9pTarJwL87OIBFuqEb5Apv5EaApEudYg4g86e3CT6kM0RowkhtEnCBQ== - dependencies: - "@jest/types" "^27.5.1" - camelcase "^6.2.0" - chalk "^4.0.0" - jest-get-type "^27.5.1" - leven "^3.1.0" - pretty-format "^27.5.1" - -jest-watcher@^27.5.1: - version "27.5.1" - resolved "https://registry.yarnpkg.com/jest-watcher/-/jest-watcher-27.5.1.tgz#71bd85fb9bde3a2c2ec4dc353437971c43c642a2" - integrity sha512-z676SuD6Z8o8qbmEGhoEUFOM1+jfEiL3DXHK/xgEiG2EyNYfFG60jluWcupY6dATjfEsKQuibReS1djInQnoVw== - dependencies: - "@jest/test-result" "^27.5.1" - "@jest/types" "^27.5.1" - "@types/node" "*" - ansi-escapes "^4.2.1" - chalk "^4.0.0" - jest-util "^27.5.1" - string-length "^4.0.1" - -jest-worker@^27.4.5, jest-worker@^27.5.1: +jest-worker@^27.4.5: version "27.5.1" resolved "https://registry.yarnpkg.com/jest-worker/-/jest-worker-27.5.1.tgz#8d146f0900e8973b106b6f73cc1e9a8cb86f8db0" integrity sha512-7vuh85V5cdDofPyxn58nrPjBktZo0u9x1g8WtjQol+jZDaE+fhN+cIvTj11GndBnMnyfrUOG1sZQxCdjKh+DKg== @@ -19616,15 +18654,6 @@ jest-worker@^27.4.5, jest-worker@^27.5.1: merge-stream "^2.0.0" supports-color "^8.0.0" -jest@^27.5.1: - version "27.5.1" - resolved "https://registry.yarnpkg.com/jest/-/jest-27.5.1.tgz#dadf33ba70a779be7a6fc33015843b51494f63fc" - integrity sha512-Yn0mADZB89zTtjkPJEXwrac3LHudkQMR+Paqa8uxJHCBr9agxztUifWCyiYrjhMPBoUVBjyny0I7XH6ozDr7QQ== - dependencies: - "@jest/core" "^27.5.1" - import-local "^3.0.2" - jest-cli "^27.5.1" - jiti@^1.19.3, jiti@^1.21.0, jiti@^1.21.6: version "1.21.6" resolved "https://registry.yarnpkg.com/jiti/-/jiti-1.21.6.tgz#6c7f7398dd4b3142767f9a168af2f317a428d268" @@ -19697,39 +18726,6 @@ jsdom-worker@^0.2.1: mitt "^1.1.3" uuid-v4 "^0.1.0" -jsdom@^16.6.0: - version "16.7.0" - resolved "https://registry.yarnpkg.com/jsdom/-/jsdom-16.7.0.tgz#918ae71965424b197c819f8183a754e18977b710" - integrity sha512-u9Smc2G1USStM+s/x1ru5Sxrl6mPYCbByG1U/hUmqaVsm4tbNyS7CicOSRyuGQYZhTu0h84qkZZQ/I+dzizSVw== - dependencies: - abab "^2.0.5" - acorn "^8.2.4" - acorn-globals "^6.0.0" - cssom "^0.4.4" - cssstyle "^2.3.0" - data-urls "^2.0.0" - decimal.js "^10.2.1" - domexception "^2.0.1" - escodegen "^2.0.0" - form-data "^3.0.0" - html-encoding-sniffer "^2.0.1" - http-proxy-agent "^4.0.1" - https-proxy-agent "^5.0.0" - is-potential-custom-element-name "^1.0.1" - nwsapi "^2.2.0" - parse5 "6.0.1" - saxes "^5.0.1" - symbol-tree "^3.2.4" - tough-cookie "^4.0.0" - w3c-hr-time "^1.0.2" - w3c-xmlserializer "^2.0.0" - webidl-conversions "^6.1.0" - whatwg-encoding "^1.0.5" - whatwg-mimetype "^2.3.0" - whatwg-url "^8.5.0" - ws "^7.4.6" - xml-name-validator "^3.0.0" - jsdom@^21.1.2: version "21.1.2" resolved "https://registry.yarnpkg.com/jsdom/-/jsdom-21.1.2.tgz#6433f751b8718248d646af1cdf6662dc8a1ca7f9" @@ -19841,11 +18837,6 @@ json-stringify-safe@^5.0.1: resolved "https://registry.yarnpkg.com/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz#1296a2d58fd45f19a0f6ce01d65701e2c735b6eb" integrity sha1-Epai1Y/UXxmg9s4B1lcB4sc1tus= -json5@2.x, json5@^2.1.2, json5@^2.2.1, json5@^2.2.2, 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== - json5@^0.5.1: version "0.5.1" resolved "https://registry.yarnpkg.com/json5/-/json5-0.5.1.tgz#1eade7acc012034ad84e2396767ead9fa5495821" @@ -19858,6 +18849,11 @@ json5@^1.0.1: dependencies: minimist "^1.2.0" +json5@^2.1.2, json5@^2.2.1, json5@^2.2.2, 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== + jsonc-parser@3.1.0: version "3.1.0" resolved "https://registry.yarnpkg.com/jsonc-parser/-/jsonc-parser-3.1.0.tgz#73b8f0e5c940b83d03476bc2e51a20ef0932615d" @@ -20252,11 +19248,6 @@ leven@2.1.0: resolved "https://registry.yarnpkg.com/leven/-/leven-2.1.0.tgz#c2e7a9f772094dee9d34202ae8acce4687875580" integrity sha512-nvVPLpIHUxCUoRLrFqTgSxXJ614d8AgQoWl7zPe/2VadE8+1dpU3LBhowRuBAcuwruWtOdD8oYC9jDNJjXDPyA== -leven@^3.1.0: - version "3.1.0" - resolved "https://registry.yarnpkg.com/leven/-/leven-3.1.0.tgz#77891de834064cccba82ae7842bb6b14a13ed7f2" - integrity sha512-qsda+H8jTaUaN/x5vzW2rzc+8Rw4TAQ/4KjB46IwK5VH+IlVeeeje/EoZRpiXvIqjFgK84QffqPztGI3VBLG1A== - levn@^0.4.1: version "0.4.1" resolved "https://registry.yarnpkg.com/levn/-/levn-0.4.1.tgz#ae4562c007473b932a6200d403268dd2fffc6ade" @@ -20643,7 +19634,7 @@ lodash.keys@^3.0.0: lodash.isarguments "^3.0.0" lodash.isarray "^3.0.0" -lodash.memoize@4.x, lodash.memoize@^4.1.2: +lodash.memoize@^4.1.2: version "4.1.2" resolved "https://registry.yarnpkg.com/lodash.memoize/-/lodash.memoize-4.1.2.tgz#bcc6c49a42a2840ed997f323eada5ecd182e0bfe" integrity sha1-vMbEmkKihA7Zl/Mj6tpezRguC/4= @@ -20965,7 +19956,7 @@ make-dir@^4.0.0: dependencies: semver "^7.5.3" -make-error@1.x, make-error@^1.1.1: +make-error@^1.1.1: version "1.3.6" resolved "https://registry.yarnpkg.com/make-error/-/make-error-1.3.6.tgz#2eb2e37ea9b67c4891f684a1394799af484cf7a2" integrity sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw== @@ -23142,7 +22133,7 @@ nuxt@^3.13.2: vue-devtools-stub "^0.1.0" vue-router "^4.4.5" -nwsapi@^2.2.0, nwsapi@^2.2.4: +nwsapi@^2.2.4: version "2.2.9" resolved "https://registry.yarnpkg.com/nwsapi/-/nwsapi-2.2.9.tgz#7f3303218372db2e9f27c27766bcfc59ae7e61c6" integrity sha512-2f3F0SEEer8bBu0dsNCFF50N0cTThV1nWFYcEYFZttdW0lDAoybv9cQoK7X7/68Z89S7FoRrVjP1LPX4XRf9vg== @@ -23867,7 +22858,7 @@ parse-json@^4.0.0: error-ex "^1.3.1" json-parse-better-errors "^1.0.1" -parse-json@^5.0.0, parse-json@^5.2.0: +parse-json@^5.0.0: version "5.2.0" resolved "https://registry.yarnpkg.com/parse-json/-/parse-json-5.2.0.tgz#c76fc66dee54231c962b22bcc8a72cf2f99753cd" integrity sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg== @@ -23942,7 +22933,7 @@ parse5-sax-parser@^6.0.1: dependencies: parse5 "^6.0.1" -parse5@6.0.1, parse5@^6.0.0, parse5@^6.0.1: +parse5@^6.0.0, parse5@^6.0.1: version "6.0.1" resolved "https://registry.yarnpkg.com/parse5/-/parse5-6.0.1.tgz#e1a1c085c569b3dc08321184f19a39cc27f7c30b" integrity sha512-Ofn/CTFzRGTTxwpNEs9PP93gXShHcTq255nzRYSKe8AkVpZY7e1fpmTfOyoIvjP5HG7Z2ZM7VS9PPhQGW2pOpw== @@ -24213,7 +23204,7 @@ picocolors@^1.0.0, picocolors@^1.0.1, picocolors@^1.1.0, picocolors@^1.1.1: resolved "https://registry.yarnpkg.com/picocolors/-/picocolors-1.1.1.tgz#3d321af3eab939b083c8f929a1d12cda81c26b6b" integrity sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA== -picomatch@^2.0.4, picomatch@^2.2.1, picomatch@^2.2.2, picomatch@^2.2.3, picomatch@^2.3.1: +picomatch@^2.0.4, picomatch@^2.2.1, picomatch@^2.2.2, picomatch@^2.3.1: version "2.3.1" resolved "https://registry.yarnpkg.com/picomatch/-/picomatch-2.3.1.tgz#3ba3833733646d9d3e4995946c1365a67fb07a42" integrity sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA== @@ -24260,7 +23251,7 @@ pinkie@^2.0.0: resolved "https://registry.yarnpkg.com/pinkie/-/pinkie-2.0.4.tgz#72556b80cfa0d48a974e80e77248e80ed4f7f870" integrity sha1-clVrgM+g1IqXToDnckjoDtT3+HA= -pirates@^4.0.1, pirates@^4.0.4: +pirates@^4.0.1: version "4.0.6" resolved "https://registry.yarnpkg.com/pirates/-/pirates-4.0.6.tgz#3018ae32ecfcff6c29ba2267cbf21166ac1f36b9" integrity sha512-saLsH7WeYYPiD25LDuLRRY/i+6HaPYr6G1OUlN39otzkSTxKnubR9RTxS3/Kk50s1g2JTgFwWQDQyplC5/SHZg== @@ -25103,7 +24094,7 @@ pretty-error@^4.0.0: lodash "^4.17.20" renderkid "^3.0.0" -pretty-format@^27.0.0, pretty-format@^27.0.2, pretty-format@^27.5.1: +pretty-format@^27.0.2: version "27.5.1" resolved "https://registry.yarnpkg.com/pretty-format/-/pretty-format-27.5.1.tgz#2181879fdea51a7a5851fb39d920faa63f01d88e" integrity sha512-Qb1gy5OrP5+zDf2Bvnzdl3jsTf1qXVMazbvCoKhtKqVs4/YK4ozX4gKQJJVyNe+cajNPn0KoC0MC3FUmaHWEmQ== @@ -25226,7 +24217,7 @@ promise@^7.1.1: dependencies: asap "~2.0.3" -prompts@^2.0.1, prompts@^2.4.2: +prompts@^2.4.2: version "2.4.2" resolved "https://registry.yarnpkg.com/prompts/-/prompts-2.4.2.tgz#7b57e73b3a48029ad10ebd44f74b01722a4cb069" integrity sha512-NxNv/kLguCA7p3jE8oL2aEBsrJWgAakBpgmgK6lpPWV+WuOmY6r2/zbAVnP+T8bQlA0nzHXSJSJW0Hq7ylaD2Q== @@ -26275,11 +25266,6 @@ resolve-url@^0.2.1: resolved "https://registry.yarnpkg.com/resolve-url/-/resolve-url-0.2.1.tgz#2c637fe77c893afd2a663fe21aa9080068e2052a" integrity sha1-LGN/53yJOv0qZj/iGqkIAGjiBSo= -resolve.exports@^1.1.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/resolve.exports/-/resolve.exports-1.1.0.tgz#5ce842b94b05146c0e03076985d1d0e7e48c90c9" - integrity sha512-J1l+Zxxp4XK3LUDZ9m60LRJF/mAe4z6a4xyabPHk7pvK5t35dACV32iIjJDFeWZFfZlO29w6SZ67knR0tHzJtQ== - resolve.exports@^2.0.2: version "2.0.2" resolved "https://registry.yarnpkg.com/resolve.exports/-/resolve.exports-2.0.2.tgz#f8c934b8e6a13f539e38b7098e2e36134f01e800" @@ -26756,13 +25742,6 @@ sax@^1.2.4, sax@~1.2.4: resolved "https://registry.yarnpkg.com/sax/-/sax-1.2.4.tgz#2816234e2378bddc4e5354fab5caa895df7100d9" integrity sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw== -saxes@^5.0.1: - version "5.0.1" - resolved "https://registry.yarnpkg.com/saxes/-/saxes-5.0.1.tgz#eebab953fa3b7608dbe94e5dadb15c888fa6696d" - integrity sha512-5LBh1Tls8c9xgGjw3QrMwETmTMVk0oFgvrFSvWx62llR2hcEInrKNZ2GZCCuuy2lvWrdl5jhbpeqc5hRYKFOcw== - dependencies: - xmlchars "^2.2.0" - saxes@^6.0.0: version "6.0.0" resolved "https://registry.yarnpkg.com/saxes/-/saxes-6.0.0.tgz#fe5b4a4768df4f14a201b1ba6a65c1f3d9988cc5" @@ -26860,16 +25839,16 @@ semver@7.5.3: dependencies: lru-cache "^6.0.0" -semver@7.x, semver@^7.0.0, semver@^7.1.1, semver@^7.2.1, semver@^7.3.2, semver@^7.3.4, semver@^7.3.5, semver@^7.3.7, semver@^7.3.8, semver@^7.5.0, semver@^7.5.2, semver@^7.5.3, semver@^7.5.4, semver@^7.6.2, semver@^7.6.3: - version "7.7.1" - resolved "https://registry.yarnpkg.com/semver/-/semver-7.7.1.tgz#abd5098d82b18c6c81f6074ff2647fd3e7220c9f" - integrity sha512-hlq8tAfn0m/61p4BVRcPzIGr6LKiMwo4VM6dGi6pt4qcRkmNzTcWq6eCEjEh+qXjkMDvPlOFFSGwQjoEa6gyMA== - semver@^6.0.0, semver@^6.1.1, semver@^6.1.2, semver@^6.2.0, semver@^6.3.0, semver@^6.3.1: version "6.3.1" resolved "https://registry.yarnpkg.com/semver/-/semver-6.3.1.tgz#556d2ef8689146e46dcea4bfdd095f3434dffcb4" integrity sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA== +semver@^7.0.0, semver@^7.1.1, semver@^7.2.1, semver@^7.3.2, semver@^7.3.4, semver@^7.3.5, semver@^7.3.7, semver@^7.3.8, semver@^7.5.0, semver@^7.5.2, semver@^7.5.3, semver@^7.5.4, semver@^7.6.2, semver@^7.6.3: + version "7.7.1" + resolved "https://registry.yarnpkg.com/semver/-/semver-7.7.1.tgz#abd5098d82b18c6c81f6074ff2647fd3e7220c9f" + integrity sha512-hlq8tAfn0m/61p4BVRcPzIGr6LKiMwo4VM6dGi6pt4qcRkmNzTcWq6eCEjEh+qXjkMDvPlOFFSGwQjoEa6gyMA== + send@0.19.0: version "0.19.0" resolved "https://registry.yarnpkg.com/send/-/send-0.19.0.tgz#bbc5a388c8ea6c048967049dbeac0e4a3f09d7f8" @@ -27451,7 +26430,7 @@ source-map-resolve@^0.6.0: atob "^2.1.2" decode-uri-component "^0.2.0" -source-map-support@0.5.21, source-map-support@^0.5.21, source-map-support@^0.5.5, source-map-support@^0.5.6, source-map-support@~0.5.20: +source-map-support@0.5.21, source-map-support@^0.5.21, source-map-support@^0.5.5, source-map-support@~0.5.20: version "0.5.21" resolved "https://registry.yarnpkg.com/source-map-support/-/source-map-support-0.5.21.tgz#04fe7c7f9e1ed2d662233c28cb2b35b9f63f6e4f" integrity sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w== @@ -27679,13 +26658,6 @@ stack-trace@0.0.x: resolved "https://registry.yarnpkg.com/stack-trace/-/stack-trace-0.0.10.tgz#547c70b347e8d32b4e108ea1a2a159e5fdde19c0" integrity sha512-KGzahc7puUKkzyMt+IqAep+TVNbKP+k2Lmwhub39m1AsTSkaDutx56aDCo+HLDzf/D26BIHTJWNiTG1KAJiQCg== -stack-utils@^2.0.3: - version "2.0.5" - resolved "https://registry.yarnpkg.com/stack-utils/-/stack-utils-2.0.5.tgz#d25265fca995154659dbbfba3b49254778d2fdd5" - integrity sha512-xrQcmYhOsn/1kX+Vraq+7j4oE2j/6BFscZ0etmYg81xuM8Gq0022Pxb8+IqgOFUIaxHs0KaSb7T1+OegiNrNFA== - dependencies: - escape-string-regexp "^2.0.0" - stackback@0.0.2: version "0.0.2" resolved "https://registry.yarnpkg.com/stackback/-/stackback-0.0.2.tgz#1ac8a0d9483848d1695e418b6d031a3c3ce68e3b" @@ -27814,14 +26786,6 @@ strict-uri-encode@^1.0.0: resolved "https://registry.yarnpkg.com/strict-uri-encode/-/strict-uri-encode-1.1.0.tgz#279b225df1d582b1f54e65addd4352e18faa0713" integrity sha1-J5siXfHVgrH1TmWt3UNS4Y+qBxM= -string-length@^4.0.1: - version "4.0.2" - resolved "https://registry.yarnpkg.com/string-length/-/string-length-4.0.2.tgz#a8a8dc7bd5c1a82b9b3c8b87e125f66871b6e57a" - integrity sha512-+l6rNN5fYHNhZZy41RXsYptCjA2Igmq4EG7kZAYFQI1E1VTXarr6ZPXBg6eq7Y6eK4FEhY6AJlyuFIb/v/S0VQ== - dependencies: - char-regex "^1.0.2" - strip-ansi "^6.0.0" - string-similarity@^4.0.1: version "4.0.4" resolved "https://registry.yarnpkg.com/string-similarity/-/string-similarity-4.0.4.tgz#42d01ab0b34660ea8a018da8f56a3309bb8b2a5b" @@ -28152,7 +27116,7 @@ supports-color@^5.3.0, supports-color@^5.5.0: dependencies: has-flag "^3.0.0" -supports-color@^7.0.0, supports-color@^7.1.0, supports-color@^7.2.0: +supports-color@^7.1.0, supports-color@^7.2.0: version "7.2.0" resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-7.2.0.tgz#1b7dcdcb32b8138801b3e478ba6a51caa89648da" integrity sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw== @@ -28171,14 +27135,6 @@ supports-color@^9.4.0: resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-9.4.0.tgz#17bfcf686288f531db3dea3215510621ccb55954" integrity sha512-VL+lNrEoIXww1coLPOmiEmK/0sGigko5COxI09KzHc2VJXJsQ37UaQ+8quuxjDeA7+KnLGTWRyOXSLLR2Wb4jw== -supports-hyperlinks@^2.0.0: - version "2.2.0" - resolved "https://registry.yarnpkg.com/supports-hyperlinks/-/supports-hyperlinks-2.2.0.tgz#4f77b42488765891774b70c79babd87f9bd594bb" - integrity sha512-6sXEzV5+I5j8Bmq9/vUphGRM/RJNT9SCURJLjwfOg51heRtguGWDzcaBlgAzKhQa0EVNpPEKzQuBwZ8S8WaCeQ== - dependencies: - has-flag "^4.0.0" - supports-color "^7.0.0" - supports-preserve-symlinks-flag@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz#6eda4bd344a3c94aea376d4cc31bc77311039e09" @@ -28420,14 +27376,6 @@ temp@0.9.4: mkdirp "^0.5.1" rimraf "~2.6.2" -terminal-link@^2.0.0: - version "2.1.1" - resolved "https://registry.yarnpkg.com/terminal-link/-/terminal-link-2.1.1.tgz#14a64a27ab3c0df933ea546fba55f2d078edc994" - integrity sha512-un0FmiRUQNr5PJqy9kP7c40F5BOfpGlYTrxonDChEZB7pzZxRNp/bt+ymiy9/npwXya9KH99nJ/GXFIiUkYGFQ== - dependencies: - ansi-escapes "^4.2.1" - supports-hyperlinks "^2.0.0" - terracotta@^1.0.4: version "1.0.5" resolved "https://registry.yarnpkg.com/terracotta/-/terracotta-1.0.5.tgz#633d3aa630f686f383e2577d156c14d261f7eb4c" @@ -28549,11 +27497,6 @@ thenify-all@^1.0.0: dependencies: any-promise "^1.0.0" -throat@^6.0.1: - version "6.0.1" - resolved "https://registry.yarnpkg.com/throat/-/throat-6.0.1.tgz#d514fedad95740c12c2d7fc70ea863eb51ade375" - integrity sha512-8hmiGIJMDlwjg7dlJ4yKGLK8EsYqKgPWbG3b4wjJddKNwc7N7Dpn08Df4szr/sZdMVeOstrdYSsqzX6BYbcB+w== - throttleit@2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/throttleit/-/throttleit-2.1.0.tgz#a7e4aa0bf4845a5bd10daa39ea0c783f631a07b4" @@ -28753,7 +27696,7 @@ touch@^3.1.0: dependencies: nopt "~1.0.10" -tough-cookie@^4.0.0, tough-cookie@^4.1.2: +tough-cookie@^4.1.2: version "4.1.4" resolved "https://registry.yarnpkg.com/tough-cookie/-/tough-cookie-4.1.4.tgz#945f1461b45b5a8c76821c33ea49c3ac192c1b36" integrity sha512-Loo5UUvLD9ScZ6jh8beX1T6sO1w2/MpCRpEP7V280GKMVUQ0Jzar2U3UJPsrdbziLEMMhu3Ujnq//rhiFuIeag== @@ -28856,20 +27799,6 @@ ts-interface-checker@^0.1.9: resolved "https://registry.yarnpkg.com/ts-interface-checker/-/ts-interface-checker-0.1.13.tgz#784fd3d679722bc103b1b4b8030bcddb5db2a699" integrity sha512-Y/arvbn+rrz3JCKl9C4kVNfTfSm2/mEp5FSz5EsZSANGPSlQrpRI5M4PKF+mJnE52jOO90PnPSc3Ur3bTQw0gA== -ts-jest@^27.1.4: - version "27.1.4" - resolved "https://registry.yarnpkg.com/ts-jest/-/ts-jest-27.1.4.tgz#84d42cf0f4e7157a52e7c64b1492c46330943e00" - integrity sha512-qjkZlVPWVctAezwsOD1OPzbZ+k7zA5z3oxII4dGdZo5ggX/PL7kvwTM0pXTr10fAtbiVpJaL3bWd502zAhpgSQ== - dependencies: - bs-logger "0.x" - fast-json-stable-stringify "2.x" - jest-util "^27.0.0" - json5 "2.x" - lodash.memoize "4.x" - make-error "1.x" - semver "7.x" - yargs-parser "20.x" - ts-node@10.9.1: version "10.9.1" resolved "https://registry.yarnpkg.com/ts-node/-/ts-node-10.9.1.tgz#e73de9102958af9e1f0b168a6ff320e25adcff4b" @@ -29813,15 +28742,6 @@ v8-compile-cache@2.3.0, v8-compile-cache@^2.0.3, v8-compile-cache@^2.3.0: resolved "https://registry.yarnpkg.com/v8-compile-cache/-/v8-compile-cache-2.3.0.tgz#2de19618c66dc247dcfb6f99338035d8245a2cee" integrity sha512-l8lCEmLcLYZh4nbunNZvQCJc5pv7+RCwa8q/LdUx8u7lsWvPDKmpodJAJNwkAhJC//dFY48KuIEmjtd4RViDrA== -v8-to-istanbul@^8.1.0: - version "8.1.1" - resolved "https://registry.yarnpkg.com/v8-to-istanbul/-/v8-to-istanbul-8.1.1.tgz#77b752fd3975e31bbcef938f85e9bd1c7a8d60ed" - integrity sha512-FGtKtv3xIpR6BYhvgH8MI/y78oT7d8Au3ww4QIxymrCtZEh5b8gCw2siywE+puhEmuWKDtmfrvF5UlB298ut3w== - dependencies: - "@types/istanbul-lib-coverage" "^2.0.1" - convert-source-map "^1.6.0" - source-map "^0.7.3" - valibot@^0.41.0: version "0.41.0" resolved "https://registry.yarnpkg.com/valibot/-/valibot-0.41.0.tgz#5c2efd49c078e455f7862379365f6036f3cd9f96" @@ -30197,20 +29117,6 @@ vue@~3.2.41: "@vue/server-renderer" "3.2.45" "@vue/shared" "3.2.45" -w3c-hr-time@^1.0.2: - version "1.0.2" - resolved "https://registry.yarnpkg.com/w3c-hr-time/-/w3c-hr-time-1.0.2.tgz#0a89cdf5cc15822df9c360543676963e0cc308cd" - integrity sha512-z8P5DvDNjKDoFIHK7q8r8lackT6l+jo/Ye3HOle7l9nICP9lf1Ci25fy9vHd0JOWewkIFzXIEig3TdKT7JQ5fQ== - dependencies: - browser-process-hrtime "^1.0.0" - -w3c-xmlserializer@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/w3c-xmlserializer/-/w3c-xmlserializer-2.0.0.tgz#3e7104a05b75146cc60f564380b7f683acf1020a" - integrity sha512-4tzD0mF8iSiMiNs30BiLO3EpfGLZUT2MSX/G+o7ZywDzliWQ3OPtTZ0PTC3B3ca1UAf4cJMHB+2Bf56EriJuRA== - dependencies: - xml-name-validator "^3.0.0" - w3c-xmlserializer@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/w3c-xmlserializer/-/w3c-xmlserializer-4.0.0.tgz#aebdc84920d806222936e3cdce408e32488a3073" @@ -30260,7 +29166,7 @@ walkdir@^0.4.1: resolved "https://registry.yarnpkg.com/walkdir/-/walkdir-0.4.1.tgz#dc119f83f4421df52e3061e514228a2db20afa39" integrity sha512-3eBwRyEln6E1MSzcxcVpQIhRG8Q1jLvEqRmCZqS3dsfXEDR/AhOF4d+jHg1qvDCpYaVRZjENPQyrVxAkQqxPgQ== -walker@^1.0.7, walker@~1.0.5: +walker@~1.0.5: version "1.0.7" resolved "https://registry.yarnpkg.com/walker/-/walker-1.0.7.tgz#2f7f9b8fd10d677262b18a884e28d19618e028fb" integrity sha1-L3+bj9ENZ3JisYqITijRlhjgKPs= @@ -30334,11 +29240,6 @@ webidl-conversions@^4.0.2: resolved "https://registry.yarnpkg.com/webidl-conversions/-/webidl-conversions-4.0.2.tgz#a855980b1f0b6b359ba1d5d9fb39ae941faa63ad" integrity sha512-YQ+BmxuTgd6UXZW3+ICGfyqRyHXVlD5GtQr5+qjiNW7bF0cqrzX500HVXPBOvgXb5YnzDd+h0zqyv61KUD7+Sg== -webidl-conversions@^5.0.0: - version "5.0.0" - resolved "https://registry.yarnpkg.com/webidl-conversions/-/webidl-conversions-5.0.0.tgz#ae59c8a00b121543a2acc65c0434f57b0fc11aff" - integrity sha512-VlZwKPCkYKxQgeSbH5EyngOmRp7Ww7I9rQLERETtf5ofd9pGeswWiOtogpEO850jziPRarreGxn5QIiTqpb2wA== - webidl-conversions@^6.1.0: version "6.1.0" resolved "https://registry.yarnpkg.com/webidl-conversions/-/webidl-conversions-6.1.0.tgz#9111b4d7ea80acd40f5270d666621afa78b69514" @@ -30527,13 +29428,6 @@ websocket-extensions@>=0.1.1: resolved "https://registry.yarnpkg.com/websocket-extensions/-/websocket-extensions-0.1.4.tgz#7f8473bc839dfd87608adb95d7eb075211578a42" integrity sha512-OqedPIGOfsDlo31UNwYbCFMSaO9m9G/0faIHj5/dZFDMFqPTcx6UwqyOy3COEaEOg/9VsGIpdqn62W5KhoKSpg== -whatwg-encoding@^1.0.5: - version "1.0.5" - resolved "https://registry.yarnpkg.com/whatwg-encoding/-/whatwg-encoding-1.0.5.tgz#5abacf777c32166a51d085d6b4f3e7d27113ddb0" - integrity sha512-b5lim54JOPN9HtzvK9HFXvBma/rnfFeqsic0hSpjtDbVxR3dJKLc+KB4V6GgiGOvl7CY/KNh8rxSo9DKQrnUEw== - dependencies: - iconv-lite "0.4.24" - whatwg-encoding@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/whatwg-encoding/-/whatwg-encoding-2.0.0.tgz#e7635f597fd87020858626805a2729fa7698ac53" @@ -30546,11 +29440,6 @@ whatwg-fetch@>=0.10.0: resolved "https://registry.yarnpkg.com/whatwg-fetch/-/whatwg-fetch-3.6.2.tgz#dced24f37f2624ed0281725d51d0e2e3fe677f8c" integrity sha512-bJlen0FcuU/0EMLrdbJ7zOnW6ITZLrZMIarMUVmdKtsGvZna8vxKYaexICWPfZ8qwf9fzNq+UEIZrnSaApt6RA== -whatwg-mimetype@^2.3.0: - version "2.3.0" - resolved "https://registry.yarnpkg.com/whatwg-mimetype/-/whatwg-mimetype-2.3.0.tgz#3d4b1e0312d2079879f826aff18dbeeca5960fbf" - integrity sha512-M4yMwr6mAnQz76TbJm914+gPpB/nCwvZbJU28cUD6dR004SAxDLOOSUaB1JDRqLtaOV/vi0IC5lEAGFgrjGv/g== - whatwg-mimetype@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/whatwg-mimetype/-/whatwg-mimetype-3.0.0.tgz#5fa1a7623867ff1af6ca3dc72ad6b8a4208beba7" @@ -30580,7 +29469,7 @@ whatwg-url@^5.0.0: tr46 "~0.0.3" webidl-conversions "^3.0.0" -whatwg-url@^8.0.0, whatwg-url@^8.4.0, whatwg-url@^8.5.0: +whatwg-url@^8.4.0: version "8.7.0" resolved "https://registry.yarnpkg.com/whatwg-url/-/whatwg-url-8.7.0.tgz#656a78e510ff8f3937bc0bcbe9f5c0ac35941b77" integrity sha512-gAojqb/m9Q8a5IV96E3fHJM70AzCkgt4uXYX2O7EmuyOnLrViCQlsEBmF9UQIu3/aeAIp2U17rtbpZWNntQqdg== @@ -30877,7 +29766,7 @@ write-pkg@4.0.0: type-fest "^0.4.1" write-json-file "^3.2.0" -ws@^7.3.1, ws@^7.4.6: +ws@^7.3.1: version "7.5.10" resolved "https://registry.yarnpkg.com/ws/-/ws-7.5.10.tgz#58b5c20dc281633f6c19113f39b349bd8bd558d9" integrity sha512-+dbF1tHwZpXcbOJdVOkzLDxZP1ailvSxM6ZweXTegylPny803bFhA+vqBYw4s31NSAk4S2Qz+AKXK9a4wkdjcQ== @@ -30897,21 +29786,11 @@ xdg-basedir@^4.0.0: resolved "https://registry.yarnpkg.com/xdg-basedir/-/xdg-basedir-4.0.0.tgz#4bc8d9984403696225ef83a1573cbbcb4e79db13" integrity sha512-PSNhEJDejZYV7h50BohL09Er9VaIefr2LMAf3OEmpCkjOi34eYyQYAXUTjEQtZJTKcF0E2UKTh+osDLsgNim9Q== -xml-name-validator@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/xml-name-validator/-/xml-name-validator-3.0.0.tgz#6ae73e06de4d8c6e47f9fb181f78d648ad457c6a" - integrity sha512-A5CUptxDsvxKJEU3yO6DuWBSJz/qizqzJKOMIfUJHETbBw/sFaDxgd6fxm1ewUaM0jZ444Fc5vC5ROYurg/4Pw== - xml-name-validator@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/xml-name-validator/-/xml-name-validator-4.0.0.tgz#79a006e2e63149a8600f15430f0a4725d1524835" integrity sha512-ICP2e+jsHvAj2E2lIHxa5tjXRlKDJo4IdvPvCXbXQGdzSfmSpNVyIKMvoZHjDY9DP0zV17iI85o90vRFXNccRw== -xml@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/xml/-/xml-1.0.1.tgz#78ba72020029c5bc87b8a81a3cfcd74b4a2fc1e5" - integrity sha512-huCv9IH9Tcf95zuYCsQraZtWnJvBtLVE0QHMOs8bWyZAFZNDcYjsPq1nEx8jKA9y+Beo9v+7OBPRisQTjinQMw== - xmlchars@^2.2.0: version "2.2.0" resolved "https://registry.yarnpkg.com/xmlchars/-/xmlchars-2.2.0.tgz#060fe1bcb7f9c76fe2a17db86a9bc3ab894210cb" @@ -30999,16 +29878,16 @@ yargs-parser@20.2.4: resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-20.2.4.tgz#b42890f14566796f85ae8e3a25290d205f154a54" integrity sha512-WOkpgNhPTlE73h4VFAFsOnomJVaovO8VqLDzy5saChRBFQFBoMYirowyW+Q9HB4HFF4Z7VZTiG3iSzJJA29yRA== -yargs-parser@20.x, yargs-parser@^20.2.2, yargs-parser@^20.2.3: - version "20.2.9" - resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-20.2.9.tgz#2eb7dc3b0289718fc295f362753845c41a0c94ee" - integrity sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w== - yargs-parser@21.1.1, yargs-parser@^21.0.0, yargs-parser@^21.1.1: version "21.1.1" resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-21.1.1.tgz#9096bceebf990d21bb31fa9516e0ede294a77d35" integrity sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw== +yargs-parser@^20.2.2, yargs-parser@^20.2.3: + version "20.2.9" + resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-20.2.9.tgz#2eb7dc3b0289718fc295f362753845c41a0c94ee" + integrity sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w== + yargs@16.2.0, yargs@^16.1.1, yargs@^16.2.0: version "16.2.0" resolved "https://registry.yarnpkg.com/yargs/-/yargs-16.2.0.tgz#1c82bf0f6b6a66eafce7ef30e376f49a12477f66" From 05391d0b3aa05f0df8dc2c8b395dcfc8ac2d79cc Mon Sep 17 00:00:00 2001 From: Francesco Gringl-Novy Date: Wed, 26 Mar 2025 10:54:19 +0100 Subject: [PATCH 23/28] feat(node): Only add span listeners for instrumentation when used (#15802) Related to https://github.com/getsentry/sentry-javascript/issues/15725#issuecomment-2740015011, this PR is an idea to avoid registering `on('spanStart')` hooks for all the node libraries that do not have proper hooks, unless they are used. Today, for OTEL instrumentation that does not provide span hooks, we fall back to `client.on('spanStart')` to enhance/mutate spans emitted by the instrumentation. This PR improved this by only registering the `spanStart` client hook if we detect that the OTEL instrumentation is actually wrapping something. This avoids us calling a bunch of span callbacks each time a span is started, when it is not even needed. For this, a new `instrumentWhenWrapped` utility is added which can be passed an instrumentation. This works by monkey-patching `_wrap` on the instrumentation, and ensuring a callback is only called conditionally. Note: For some (e.g. connect, fastify) we register `spanStart` in the error handler, which is even better, so there we do not need this logic. --- .../node/src/integrations/tracing/connect.ts | 2 +- .../src/integrations/tracing/dataloader.ts | 44 +-- .../src/integrations/tracing/genericPool.ts | 29 +- .../node/src/integrations/tracing/knex.ts | 25 +- .../node/src/integrations/tracing/prisma.ts | 26 +- .../node/src/integrations/tracing/tedious.ts | 33 ++- .../integrations/tracing/vercelai/index.ts | 271 +++++++++--------- .../tracing/vercelai/instrumentation.ts | 22 +- packages/node/src/otel/instrument.ts | 61 +++- packages/node/test/utils/instrument.test.ts | 96 +++++++ 10 files changed, 403 insertions(+), 206 deletions(-) create mode 100644 packages/node/test/utils/instrument.test.ts diff --git a/packages/node/src/integrations/tracing/connect.ts b/packages/node/src/integrations/tracing/connect.ts index 8cfe2bb74050..ae7958c7da83 100644 --- a/packages/node/src/integrations/tracing/connect.ts +++ b/packages/node/src/integrations/tracing/connect.ts @@ -104,7 +104,7 @@ function addConnectSpanAttributes(span: Span): void { [SEMANTIC_ATTRIBUTE_SENTRY_OP]: `${type}.connect`, }); - // Also update the name, we don't need to "middleware - " prefix + // Also update the name, we don't need the "middleware - " prefix const name = attributes['connect.name']; if (typeof name === 'string') { span.updateName(name); diff --git a/packages/node/src/integrations/tracing/dataloader.ts b/packages/node/src/integrations/tracing/dataloader.ts index 6339515c6f28..3c887bb33fa5 100644 --- a/packages/node/src/integrations/tracing/dataloader.ts +++ b/packages/node/src/integrations/tracing/dataloader.ts @@ -6,7 +6,7 @@ import { spanToJSON, } from '@sentry/core'; import type { IntegrationFn } from '@sentry/core'; -import { generateInstrumentOnce } from '../../otel/instrument'; +import { instrumentWhenWrapped, generateInstrumentOnce } from '../../otel/instrument'; const INTEGRATION_NAME = 'Dataloader'; @@ -19,31 +19,37 @@ export const instrumentDataloader = generateInstrumentOnce( ); const _dataloaderIntegration = (() => { + let instrumentationWrappedCallback: undefined | ((callback: () => void) => void); + return { name: INTEGRATION_NAME, setupOnce() { - instrumentDataloader(); + const instrumentation = instrumentDataloader(); + instrumentationWrappedCallback = instrumentWhenWrapped(instrumentation); }, setup(client) { - client.on('spanStart', span => { - const spanJSON = spanToJSON(span); - if (spanJSON.description?.startsWith('dataloader')) { - span.setAttribute(SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN, 'auto.db.otel.dataloader'); - } + // This is called either immediately or when the instrumentation is wrapped + instrumentationWrappedCallback?.(() => { + client.on('spanStart', span => { + const spanJSON = spanToJSON(span); + if (spanJSON.description?.startsWith('dataloader')) { + span.setAttribute(SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN, 'auto.db.otel.dataloader'); + } - // These are all possible dataloader span descriptions - // Still checking for the future versions - // in case they add support for `clear` and `prime` - if ( - spanJSON.description === 'dataloader.load' || - spanJSON.description === 'dataloader.loadMany' || - spanJSON.description === 'dataloader.batch' - ) { - span.setAttribute(SEMANTIC_ATTRIBUTE_SENTRY_OP, 'cache.get'); - // TODO: We can try adding `key` to the `data` attribute upstream. - // Or alternatively, we can add `requestHook` to the dataloader instrumentation. - } + // These are all possible dataloader span descriptions + // Still checking for the future versions + // in case they add support for `clear` and `prime` + if ( + spanJSON.description === 'dataloader.load' || + spanJSON.description === 'dataloader.loadMany' || + spanJSON.description === 'dataloader.batch' + ) { + span.setAttribute(SEMANTIC_ATTRIBUTE_SENTRY_OP, 'cache.get'); + // TODO: We can try adding `key` to the `data` attribute upstream. + // Or alternatively, we can add `requestHook` to the dataloader instrumentation. + } + }); }); }, }; diff --git a/packages/node/src/integrations/tracing/genericPool.ts b/packages/node/src/integrations/tracing/genericPool.ts index 6c4169d66b99..d6f736c6f9a5 100644 --- a/packages/node/src/integrations/tracing/genericPool.ts +++ b/packages/node/src/integrations/tracing/genericPool.ts @@ -1,33 +1,38 @@ import { GenericPoolInstrumentation } from '@opentelemetry/instrumentation-generic-pool'; import { SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN, defineIntegration, spanToJSON } from '@sentry/core'; import type { IntegrationFn } from '@sentry/core'; -import { generateInstrumentOnce } from '../../otel/instrument'; +import { generateInstrumentOnce, instrumentWhenWrapped } from '../../otel/instrument'; const INTEGRATION_NAME = 'GenericPool'; export const instrumentGenericPool = generateInstrumentOnce(INTEGRATION_NAME, () => new GenericPoolInstrumentation({})); const _genericPoolIntegration = (() => { + let instrumentationWrappedCallback: undefined | ((callback: () => void) => void); + return { name: INTEGRATION_NAME, setupOnce() { - instrumentGenericPool(); + const instrumentation = instrumentGenericPool(); + instrumentationWrappedCallback = instrumentWhenWrapped(instrumentation); }, setup(client) { - client.on('spanStart', span => { - const spanJSON = spanToJSON(span); + instrumentationWrappedCallback?.(() => + client.on('spanStart', span => { + const spanJSON = spanToJSON(span); - const spanDescription = spanJSON.description; + const spanDescription = spanJSON.description; - // typo in emitted span for version <= 0.38.0 of @opentelemetry/instrumentation-generic-pool - const isGenericPoolSpan = - spanDescription === 'generic-pool.aquire' || spanDescription === 'generic-pool.acquire'; + // typo in emitted span for version <= 0.38.0 of @opentelemetry/instrumentation-generic-pool + const isGenericPoolSpan = + spanDescription === 'generic-pool.aquire' || spanDescription === 'generic-pool.acquire'; - if (isGenericPoolSpan) { - span.setAttribute(SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN, 'auto.db.otel.generic_pool'); - } - }); + if (isGenericPoolSpan) { + span.setAttribute(SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN, 'auto.db.otel.generic_pool'); + } + }), + ); }, }; }) satisfies IntegrationFn; diff --git a/packages/node/src/integrations/tracing/knex.ts b/packages/node/src/integrations/tracing/knex.ts index 86c728d115bd..da70d8b3c8b7 100644 --- a/packages/node/src/integrations/tracing/knex.ts +++ b/packages/node/src/integrations/tracing/knex.ts @@ -1,7 +1,7 @@ import { KnexInstrumentation } from '@opentelemetry/instrumentation-knex'; import { SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN, defineIntegration, spanToJSON } from '@sentry/core'; import type { IntegrationFn } from '@sentry/core'; -import { generateInstrumentOnce } from '../../otel/instrument'; +import { generateInstrumentOnce, instrumentWhenWrapped } from '../../otel/instrument'; const INTEGRATION_NAME = 'Knex'; @@ -11,21 +11,26 @@ export const instrumentKnex = generateInstrumentOnce( ); const _knexIntegration = (() => { + let instrumentationWrappedCallback: undefined | ((callback: () => void) => void); + return { name: INTEGRATION_NAME, setupOnce() { - instrumentKnex(); + const instrumentation = instrumentKnex(); + instrumentationWrappedCallback = instrumentWhenWrapped(instrumentation); }, setup(client) { - client.on('spanStart', span => { - const { data } = spanToJSON(span); - // knex.version is always set in the span data - // https://github.com/open-telemetry/opentelemetry-js-contrib/blob/0309caeafc44ac9cb13a3345b790b01b76d0497d/plugins/node/opentelemetry-instrumentation-knex/src/instrumentation.ts#L138 - if ('knex.version' in data) { - span.setAttribute(SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN, 'auto.db.otel.knex'); - } - }); + instrumentationWrappedCallback?.(() => + client.on('spanStart', span => { + const { data } = spanToJSON(span); + // knex.version is always set in the span data + // https://github.com/open-telemetry/opentelemetry-js-contrib/blob/0309caeafc44ac9cb13a3345b790b01b76d0497d/plugins/node/opentelemetry-instrumentation-knex/src/instrumentation.ts#L138 + if ('knex.version' in data) { + span.setAttribute(SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN, 'auto.db.otel.knex'); + } + }), + ); }, }; }) satisfies IntegrationFn; diff --git a/packages/node/src/integrations/tracing/prisma.ts b/packages/node/src/integrations/tracing/prisma.ts index 58516671c9a3..06b75f0c707e 100644 --- a/packages/node/src/integrations/tracing/prisma.ts +++ b/packages/node/src/integrations/tracing/prisma.ts @@ -19,6 +19,18 @@ function isPrismaV6TracingHelper(helper: unknown): helper is PrismaV6TracingHelp return !!helper && typeof helper === 'object' && 'dispatchEngineSpans' in helper; } +function getPrismaTracingHelper(): unknown | undefined { + const prismaInstrumentationObject = (globalThis as Record).PRISMA_INSTRUMENTATION; + const prismaTracingHelper = + prismaInstrumentationObject && + typeof prismaInstrumentationObject === 'object' && + 'helper' in prismaInstrumentationObject + ? prismaInstrumentationObject.helper + : undefined; + + return prismaTracingHelper; +} + class SentryPrismaInteropInstrumentation extends EsmInteropPrismaInstrumentation { public constructor() { super(); @@ -30,13 +42,7 @@ class SentryPrismaInteropInstrumentation extends EsmInteropPrismaInstrumentation // 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 prismaInstrumentationObject = (globalThis as Record).PRISMA_INSTRUMENTATION; - const prismaTracingHelper = - prismaInstrumentationObject && - typeof prismaInstrumentationObject === 'object' && - 'helper' in prismaInstrumentationObject - ? prismaInstrumentationObject.helper - : undefined; + const prismaTracingHelper = getPrismaTracingHelper(); let emittedWarning = false; @@ -119,6 +125,12 @@ export const prismaIntegration = defineIntegration( 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; + } + client.on('spanStart', span => { const spanJSON = spanToJSON(span); if (spanJSON.description?.startsWith('prisma:')) { diff --git a/packages/node/src/integrations/tracing/tedious.ts b/packages/node/src/integrations/tracing/tedious.ts index 644601986a7e..dd74c095a566 100644 --- a/packages/node/src/integrations/tracing/tedious.ts +++ b/packages/node/src/integrations/tracing/tedious.ts @@ -1,7 +1,7 @@ import { TediousInstrumentation } from '@opentelemetry/instrumentation-tedious'; import { SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN, defineIntegration, spanToJSON } from '@sentry/core'; import type { IntegrationFn } from '@sentry/core'; -import { generateInstrumentOnce } from '../../otel/instrument'; +import { generateInstrumentOnce, instrumentWhenWrapped } from '../../otel/instrument'; const TEDIUS_INSTRUMENTED_METHODS = new Set([ 'callProcedure', @@ -17,25 +17,30 @@ const INTEGRATION_NAME = 'Tedious'; export const instrumentTedious = generateInstrumentOnce(INTEGRATION_NAME, () => new TediousInstrumentation({})); const _tediousIntegration = (() => { + let instrumentationWrappedCallback: undefined | ((callback: () => void) => void); + return { name: INTEGRATION_NAME, setupOnce() { - instrumentTedious(); + const instrumentation = instrumentTedious(); + instrumentationWrappedCallback = instrumentWhenWrapped(instrumentation); }, setup(client) { - client.on('spanStart', span => { - const { description, data } = spanToJSON(span); - // Tedius integration always set a span name and `db.system` attribute to `mssql`. - if (!description || data['db.system'] !== 'mssql') { - return; - } - - const operation = description.split(' ')[0] || ''; - if (TEDIUS_INSTRUMENTED_METHODS.has(operation)) { - span.setAttribute(SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN, 'auto.db.otel.tedious'); - } - }); + instrumentationWrappedCallback?.(() => + client.on('spanStart', span => { + const { description, data } = spanToJSON(span); + // Tedius integration always set a span name and `db.system` attribute to `mssql`. + if (!description || data['db.system'] !== 'mssql') { + return; + } + + const operation = description.split(' ')[0] || ''; + if (TEDIUS_INSTRUMENTED_METHODS.has(operation)) { + span.setAttribute(SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN, 'auto.db.otel.tedious'); + } + }), + ); }, }; }) satisfies IntegrationFn; diff --git a/packages/node/src/integrations/tracing/vercelai/index.ts b/packages/node/src/integrations/tracing/vercelai/index.ts index a8198a8458ec..26bf57ed38b2 100644 --- a/packages/node/src/integrations/tracing/vercelai/index.ts +++ b/packages/node/src/integrations/tracing/vercelai/index.ts @@ -3,152 +3,153 @@ import { SEMANTIC_ATTRIBUTE_SENTRY_OP, defineIntegration, spanToJSON } from '@se import type { IntegrationFn } from '@sentry/core'; import { generateInstrumentOnce } from '../../../otel/instrument'; import { addOriginToSpan } from '../../../utils/addOriginToSpan'; -import { SentryVercelAiInstrumentation, sentryVercelAiPatched } from './instrumentation'; +import { SentryVercelAiInstrumentation } from './instrumentation'; const INTEGRATION_NAME = 'VercelAI'; export const instrumentVercelAi = generateInstrumentOnce(INTEGRATION_NAME, () => new SentryVercelAiInstrumentation({})); const _vercelAIIntegration = (() => { + let instrumentation: undefined | SentryVercelAiInstrumentation; + return { name: INTEGRATION_NAME, setupOnce() { - instrumentVercelAi(); - }, - processEvent(event) { - if (event.type === 'transaction' && event.spans?.length) { - for (const span of event.spans) { - const { data: attributes, description: name } = span; - - if (!name || span.origin !== 'auto.vercelai.otel') { - continue; - } - - if (attributes['ai.usage.completionTokens'] != undefined) { - attributes['ai.completion_tokens.used'] = attributes['ai.usage.completionTokens']; - } - if (attributes['ai.usage.promptTokens'] != undefined) { - attributes['ai.prompt_tokens.used'] = attributes['ai.usage.promptTokens']; - } - if ( - typeof attributes['ai.usage.completionTokens'] == 'number' && - typeof attributes['ai.usage.promptTokens'] == 'number' - ) { - attributes['ai.total_tokens.used'] = - attributes['ai.usage.completionTokens'] + attributes['ai.usage.promptTokens']; - } - } - } - - return event; + instrumentation = instrumentVercelAi(); }, setup(client) { - client.on('spanStart', span => { - if (!sentryVercelAiPatched) { - return; - } - - const { data: attributes, description: name } = spanToJSON(span); - - if (!name) { - return; - } - - // The id of the model - const aiModelId = attributes['ai.model.id']; - - // the provider of the model - const aiModelProvider = attributes['ai.model.provider']; - - // both of these must be defined for the integration to work - if (typeof aiModelId !== 'string' || typeof aiModelProvider !== 'string' || !aiModelId || !aiModelProvider) { - return; - } - - let isPipelineSpan = false; - - switch (name) { - case 'ai.generateText': { - span.setAttribute(SEMANTIC_ATTRIBUTE_SENTRY_OP, 'ai.pipeline.generateText'); - isPipelineSpan = true; - break; - } - case 'ai.generateText.doGenerate': { - span.setAttribute(SEMANTIC_ATTRIBUTE_SENTRY_OP, 'ai.run.doGenerate'); - break; - } - case 'ai.streamText': { - span.setAttribute(SEMANTIC_ATTRIBUTE_SENTRY_OP, 'ai.pipeline.streamText'); - isPipelineSpan = true; - break; - } - case 'ai.streamText.doStream': { - span.setAttribute(SEMANTIC_ATTRIBUTE_SENTRY_OP, 'ai.run.doStream'); - break; - } - case 'ai.generateObject': { - span.setAttribute(SEMANTIC_ATTRIBUTE_SENTRY_OP, 'ai.pipeline.generateObject'); - isPipelineSpan = true; - break; - } - case 'ai.generateObject.doGenerate': { - span.setAttribute(SEMANTIC_ATTRIBUTE_SENTRY_OP, 'ai.run.doGenerate'); - break; - } - case 'ai.streamObject': { - span.setAttribute(SEMANTIC_ATTRIBUTE_SENTRY_OP, 'ai.pipeline.streamObject'); - isPipelineSpan = true; - break; - } - case 'ai.streamObject.doStream': { - span.setAttribute(SEMANTIC_ATTRIBUTE_SENTRY_OP, 'ai.run.doStream'); - break; - } - case 'ai.embed': { - span.setAttribute(SEMANTIC_ATTRIBUTE_SENTRY_OP, 'ai.pipeline.embed'); - isPipelineSpan = true; - break; - } - case 'ai.embed.doEmbed': { - span.setAttribute(SEMANTIC_ATTRIBUTE_SENTRY_OP, 'ai.embeddings'); - break; - } - case 'ai.embedMany': { - span.setAttribute(SEMANTIC_ATTRIBUTE_SENTRY_OP, 'ai.pipeline.embedMany'); - isPipelineSpan = true; - break; - } - case 'ai.embedMany.doEmbed': { - span.setAttribute(SEMANTIC_ATTRIBUTE_SENTRY_OP, 'ai.embeddings'); - break; - } - case 'ai.toolCall': - case 'ai.stream.firstChunk': - case 'ai.stream.finish': - span.setAttribute(SEMANTIC_ATTRIBUTE_SENTRY_OP, 'ai.run'); - break; - } - - addOriginToSpan(span, 'auto.vercelai.otel'); - - const nameWthoutAi = name.replace('ai.', ''); - span.setAttribute('ai.pipeline.name', nameWthoutAi); - span.updateName(nameWthoutAi); - - // If a Telemetry name is set and it is a pipeline span, use that as the operation name - const functionId = attributes['ai.telemetry.functionId']; - if (functionId && typeof functionId === 'string' && isPipelineSpan) { - span.updateName(functionId); - span.setAttribute('ai.pipeline.name', functionId); - } - - if (attributes['ai.prompt']) { - span.setAttribute('ai.input_messages', attributes['ai.prompt']); - } - if (attributes['ai.model.id']) { - span.setAttribute('ai.model_id', attributes['ai.model.id']); - } - span.setAttribute('ai.streaming', name.includes('stream')); + instrumentation?.callWhenPatched(() => { + client.on('spanStart', span => { + const { data: attributes, description: name } = spanToJSON(span); + + if (!name) { + return; + } + + // The id of the model + const aiModelId = attributes['ai.model.id']; + + // the provider of the model + const aiModelProvider = attributes['ai.model.provider']; + + // both of these must be defined for the integration to work + if (typeof aiModelId !== 'string' || typeof aiModelProvider !== 'string' || !aiModelId || !aiModelProvider) { + return; + } + + let isPipelineSpan = false; + + switch (name) { + case 'ai.generateText': { + span.setAttribute(SEMANTIC_ATTRIBUTE_SENTRY_OP, 'ai.pipeline.generateText'); + isPipelineSpan = true; + break; + } + case 'ai.generateText.doGenerate': { + span.setAttribute(SEMANTIC_ATTRIBUTE_SENTRY_OP, 'ai.run.doGenerate'); + break; + } + case 'ai.streamText': { + span.setAttribute(SEMANTIC_ATTRIBUTE_SENTRY_OP, 'ai.pipeline.streamText'); + isPipelineSpan = true; + break; + } + case 'ai.streamText.doStream': { + span.setAttribute(SEMANTIC_ATTRIBUTE_SENTRY_OP, 'ai.run.doStream'); + break; + } + case 'ai.generateObject': { + span.setAttribute(SEMANTIC_ATTRIBUTE_SENTRY_OP, 'ai.pipeline.generateObject'); + isPipelineSpan = true; + break; + } + case 'ai.generateObject.doGenerate': { + span.setAttribute(SEMANTIC_ATTRIBUTE_SENTRY_OP, 'ai.run.doGenerate'); + break; + } + case 'ai.streamObject': { + span.setAttribute(SEMANTIC_ATTRIBUTE_SENTRY_OP, 'ai.pipeline.streamObject'); + isPipelineSpan = true; + break; + } + case 'ai.streamObject.doStream': { + span.setAttribute(SEMANTIC_ATTRIBUTE_SENTRY_OP, 'ai.run.doStream'); + break; + } + case 'ai.embed': { + span.setAttribute(SEMANTIC_ATTRIBUTE_SENTRY_OP, 'ai.pipeline.embed'); + isPipelineSpan = true; + break; + } + case 'ai.embed.doEmbed': { + span.setAttribute(SEMANTIC_ATTRIBUTE_SENTRY_OP, 'ai.embeddings'); + break; + } + case 'ai.embedMany': { + span.setAttribute(SEMANTIC_ATTRIBUTE_SENTRY_OP, 'ai.pipeline.embedMany'); + isPipelineSpan = true; + break; + } + case 'ai.embedMany.doEmbed': { + span.setAttribute(SEMANTIC_ATTRIBUTE_SENTRY_OP, 'ai.embeddings'); + break; + } + case 'ai.toolCall': + case 'ai.stream.firstChunk': + case 'ai.stream.finish': + span.setAttribute(SEMANTIC_ATTRIBUTE_SENTRY_OP, 'ai.run'); + break; + } + + addOriginToSpan(span, 'auto.vercelai.otel'); + + const nameWthoutAi = name.replace('ai.', ''); + span.setAttribute('ai.pipeline.name', nameWthoutAi); + span.updateName(nameWthoutAi); + + // If a Telemetry name is set and it is a pipeline span, use that as the operation name + const functionId = attributes['ai.telemetry.functionId']; + if (functionId && typeof functionId === 'string' && isPipelineSpan) { + span.updateName(functionId); + span.setAttribute('ai.pipeline.name', functionId); + } + + if (attributes['ai.prompt']) { + span.setAttribute('ai.input_messages', attributes['ai.prompt']); + } + if (attributes['ai.model.id']) { + span.setAttribute('ai.model_id', attributes['ai.model.id']); + } + span.setAttribute('ai.streaming', name.includes('stream')); + }); + + client.addEventProcessor(event => { + if (event.type === 'transaction' && event.spans?.length) { + for (const span of event.spans) { + const { data: attributes, description: name } = span; + + if (!name || span.origin !== 'auto.vercelai.otel') { + continue; + } + + if (attributes['ai.usage.completionTokens'] != undefined) { + attributes['ai.completion_tokens.used'] = attributes['ai.usage.completionTokens']; + } + if (attributes['ai.usage.promptTokens'] != undefined) { + attributes['ai.prompt_tokens.used'] = attributes['ai.usage.promptTokens']; + } + if ( + typeof attributes['ai.usage.completionTokens'] == 'number' && + typeof attributes['ai.usage.promptTokens'] == 'number' + ) { + attributes['ai.total_tokens.used'] = + attributes['ai.usage.completionTokens'] + attributes['ai.usage.promptTokens']; + } + } + } + + return event; + }); }); }, }; diff --git a/packages/node/src/integrations/tracing/vercelai/instrumentation.ts b/packages/node/src/integrations/tracing/vercelai/instrumentation.ts index 97721eaee15d..2cddd175c0d8 100644 --- a/packages/node/src/integrations/tracing/vercelai/instrumentation.ts +++ b/packages/node/src/integrations/tracing/vercelai/instrumentation.ts @@ -23,8 +23,6 @@ type MethodArgs = [MethodFirstArg, ...unknown[]]; type PatchedModuleExports = Record<(typeof INSTRUMENTED_METHODS)[number], (...args: MethodArgs) => unknown> & Record; -export let sentryVercelAiPatched = false; - /** * This detects is added by the Sentry Vercel AI Integration to detect if the integration should * be enabled. @@ -32,6 +30,9 @@ export let sentryVercelAiPatched = false; * It also patches the `ai` module to enable Vercel AI telemetry automatically for all methods. */ export class SentryVercelAiInstrumentation extends InstrumentationBase { + private _isPatched = false; + private _callbacks: (() => void)[] = []; + public constructor(config: InstrumentationConfig = {}) { super('@sentry/instrumentation-vercel-ai', SDK_VERSION, config); } @@ -44,11 +45,26 @@ export class SentryVercelAiInstrumentation extends InstrumentationBase { return module; } + /** + * Call the provided callback when the module is patched. + * If it has already been patched, the callback will be called immediately. + */ + public callWhenPatched(callback: () => void): void { + if (this._isPatched) { + callback(); + } else { + this._callbacks.push(callback); + } + } + /** * Patches module exports to enable Vercel AI telemetry. */ private _patch(moduleExports: PatchedModuleExports): unknown { - sentryVercelAiPatched = true; + this._isPatched = true; + + this._callbacks.forEach(callback => callback()); + this._callbacks = []; function generatePatch(name: string) { return (...args: MethodArgs) => { diff --git a/packages/node/src/otel/instrument.ts b/packages/node/src/otel/instrument.ts index c5bd7500a739..6f8b10db2ba7 100644 --- a/packages/node/src/otel/instrument.ts +++ b/packages/node/src/otel/instrument.ts @@ -7,19 +7,22 @@ export const INSTRUMENTED: Record = {}; * Instrument an OpenTelemetry instrumentation once. * This will skip running instrumentation again if it was already instrumented. */ -export function generateInstrumentOnce( +export function generateInstrumentOnce< + Options = unknown, + InstrumentationInstance extends Instrumentation = Instrumentation, +>( name: string, - creator: (options?: Options) => Instrumentation, -): ((options?: Options) => void) & { id: string } { + creator: (options?: Options) => InstrumentationInstance, +): ((options?: Options) => InstrumentationInstance) & { id: string } { return Object.assign( (options?: Options) => { - const instrumented = INSTRUMENTED[name]; + const instrumented = INSTRUMENTED[name] as InstrumentationInstance | undefined; if (instrumented) { // If options are provided, ensure we update them if (options) { instrumented.setConfig(options); } - return; + return instrumented; } const instrumentation = creator(options); @@ -28,7 +31,55 @@ export function generateInstrumentOnce( registerInstrumentations({ instrumentations: [instrumentation], }); + + return instrumentation; }, { id: name }, ); } + +/** + * Ensure a given callback is called when the instrumentation is actually wrapping something. + * This can be used to ensure some logic is only called when the instrumentation is actually active. + * + * This function returns a function that can be invoked with a callback. + * This callback will either be invoked immediately + * (e.g. if the instrumentation was already wrapped, or if _wrap could not be patched), + * or once the instrumentation is actually wrapping something. + * + * Make sure to call this function right after adding the instrumentation, otherwise it may be too late! + * The returned callback can be used any time, and also multiple times. + */ +export function instrumentWhenWrapped(instrumentation: T): (callback: () => void) => void { + let isWrapped = false; + let callbacks: (() => void)[] = []; + + if (!hasWrap(instrumentation)) { + isWrapped = true; + } else { + const originalWrap = instrumentation['_wrap']; + + instrumentation['_wrap'] = (...args: Parameters) => { + isWrapped = true; + callbacks.forEach(callback => callback()); + callbacks = []; + return originalWrap(...args); + }; + } + + const registerCallback = (callback: () => void): void => { + if (isWrapped) { + callback(); + } else { + callbacks.push(callback); + } + }; + + return registerCallback; +} + +function hasWrap( + instrumentation: T, +): instrumentation is T & { _wrap: (...args: unknown[]) => unknown } { + return typeof (instrumentation as T & { _wrap?: (...args: unknown[]) => unknown })['_wrap'] === 'function'; +} diff --git a/packages/node/test/utils/instrument.test.ts b/packages/node/test/utils/instrument.test.ts new file mode 100644 index 000000000000..3cc7fedb4d22 --- /dev/null +++ b/packages/node/test/utils/instrument.test.ts @@ -0,0 +1,96 @@ +import { describe, test, vi, expect } from 'vitest'; +import { instrumentWhenWrapped } from '../../src/otel/instrument'; + +describe('instrumentWhenWrapped', () => { + test('calls callback immediately when instrumentation has no _wrap method', () => { + const callback = vi.fn(); + const instrumentation = {} as any; + + const registerCallback = instrumentWhenWrapped(instrumentation); + registerCallback(callback); + + expect(callback).toHaveBeenCalledTimes(1); + }); + + test('calls callback when _wrap is called', () => { + const callback = vi.fn(); + const originalWrap = vi.fn(); + const instrumentation = { + _wrap: originalWrap, + } as any; + + const registerCallback = instrumentWhenWrapped(instrumentation); + registerCallback(callback); + + // Callback should not be called yet + expect(callback).not.toHaveBeenCalled(); + + // Call _wrap + instrumentation._wrap(); + + // Callback should be called once + expect(callback).toHaveBeenCalledTimes(1); + expect(originalWrap).toHaveBeenCalled(); + }); + + test('calls multiple callbacks when _wrap is called', () => { + const callback1 = vi.fn(); + const callback2 = vi.fn(); + const originalWrap = vi.fn(); + const instrumentation = { + _wrap: originalWrap, + } as any; + + const registerCallback = instrumentWhenWrapped(instrumentation); + registerCallback(callback1); + registerCallback(callback2); + + // Callbacks should not be called yet + expect(callback1).not.toHaveBeenCalled(); + expect(callback2).not.toHaveBeenCalled(); + + // Call _wrap + instrumentation._wrap(); + + // Both callbacks should be called once + expect(callback1).toHaveBeenCalledTimes(1); + expect(callback2).toHaveBeenCalledTimes(1); + expect(originalWrap).toHaveBeenCalled(); + }); + + test('calls callback immediately if already wrapped', () => { + const callback = vi.fn(); + const originalWrap = vi.fn(); + const instrumentation = { + _wrap: originalWrap, + } as any; + + const registerCallback = instrumentWhenWrapped(instrumentation); + + // Call _wrap first + instrumentation._wrap(); + + registerCallback(callback); + + // Callback should be called immediately + expect(callback).toHaveBeenCalledTimes(1); + expect(originalWrap).toHaveBeenCalled(); + }); + + test('passes through arguments to original _wrap', () => { + const callback = vi.fn(); + const originalWrap = vi.fn(); + const instrumentation = { + _wrap: originalWrap, + } as any; + + const registerCallback = instrumentWhenWrapped(instrumentation); + registerCallback(callback); + + // Call _wrap with arguments + const args = ['arg1', 'arg2']; + instrumentation._wrap(...args); + + expect(originalWrap).toHaveBeenCalledWith(...args); + }); +}); From 7191ebd4e27481c06ef53a83501d201edbd096a0 Mon Sep 17 00:00:00 2001 From: Luca Forstner Date: Wed, 26 Mar 2025 15:42:14 +0100 Subject: [PATCH 24/28] feat: Always truncate stored breadcrumb messages to 2kb (#15819) We had this logic in the console instrumentation before but it probably makes sense to truncate all breadcrumbs to some degree for memory consumption reasons. Acts as prework for https://github.com/getsentry/sentry-javascript/pull/15818 because if we don't truncate error messages they will end up as very big strings in breadcrumbs. --- packages/core/src/scope.ts | 5 ++++- packages/core/test/lib/scope.test.ts | 6 +++++ packages/node/src/integrations/console.ts | 3 +-- .../node/test/integration/console.test.ts | 22 ------------------- 4 files changed, 11 insertions(+), 25 deletions(-) diff --git a/packages/core/src/scope.ts b/packages/core/src/scope.ts index a302e5a14c34..d10b9dea08d6 100644 --- a/packages/core/src/scope.ts +++ b/packages/core/src/scope.ts @@ -24,6 +24,7 @@ import { isPlainObject } from './utils-hoist/is'; import { logger } from './utils-hoist/logger'; import { uuid4 } from './utils-hoist/misc'; import { generateTraceId } from './utils-hoist/propagationContext'; +import { truncate } from './utils-hoist/string'; import { dateTimestampInSeconds } from './utils-hoist/time'; import { merge } from './utils/merge'; import { _getSpanForScope, _setSpanForScope } from './utils/spanOnScope'; @@ -474,9 +475,11 @@ export class Scope { return this; } - const mergedBreadcrumb = { + const mergedBreadcrumb: Breadcrumb = { timestamp: dateTimestampInSeconds(), ...breadcrumb, + // Breadcrumb messages can theoretically be infinitely large and they're held in memory so we truncate them not to leak (too much) memory + message: breadcrumb.message ? truncate(breadcrumb.message, 2048) : breadcrumb.message, }; this._breadcrumbs.push(mergedBreadcrumb); diff --git a/packages/core/test/lib/scope.test.ts b/packages/core/test/lib/scope.test.ts index 4b27eff42824..87dcf9315ba9 100644 --- a/packages/core/test/lib/scope.test.ts +++ b/packages/core/test/lib/scope.test.ts @@ -186,6 +186,12 @@ describe('Scope', () => { expect(scope['_breadcrumbs']).toHaveLength(111); }); + test('addBreadcrumb will truncate the stored messages', () => { + const scope = new Scope(); + scope.addBreadcrumb({ message: 'A'.repeat(10_000) }); + expect(scope['_breadcrumbs'][0]?.message).toBe(`${'A'.repeat(2048)}...`); + }); + test('setLevel', () => { const scope = new Scope(); scope.setLevel('fatal'); diff --git a/packages/node/src/integrations/console.ts b/packages/node/src/integrations/console.ts index 5e5e6ac414d9..d1bb0463551e 100644 --- a/packages/node/src/integrations/console.ts +++ b/packages/node/src/integrations/console.ts @@ -5,7 +5,6 @@ import { defineIntegration, getClient, severityLevelFromString, - truncate, } from '@sentry/core'; const INTEGRATION_NAME = 'Console'; @@ -26,7 +25,7 @@ export const consoleIntegration = defineIntegration(() => { { category: 'console', level: severityLevelFromString(level), - message: truncate(util.format.apply(undefined, args), 2048), // 2KB + message: util.format.apply(undefined, args), }, { input: [...args], diff --git a/packages/node/test/integration/console.test.ts b/packages/node/test/integration/console.test.ts index 401dffd34819..691ccd4397ee 100644 --- a/packages/node/test/integration/console.test.ts +++ b/packages/node/test/integration/console.test.ts @@ -36,26 +36,4 @@ describe('Console integration', () => { }, ); }); - - it('should truncate breadcrumbs with more than 2 KB message size', () => { - consoleIntegration().setup?.(getClient() as NodeClient); - - const longMsg = 'A'.repeat(10_000); - - // eslint-disable-next-line no-console - console.log(longMsg); - - expect(addBreadcrumbSpy).toHaveBeenCalledTimes(1); - expect(addBreadcrumbSpy).toHaveBeenCalledWith( - { - category: 'console', - level: 'log', - message: `${'A'.repeat(2048)}...`, - }, - { - input: [longMsg], - level: 'log', - }, - ); - }); }); From b0a1b068d3288c556484dcd49aaa84dc1ed222c7 Mon Sep 17 00:00:00 2001 From: Abhijeet Prasad Date: Wed, 26 Mar 2025 11:35:48 -0400 Subject: [PATCH 25/28] test: Add browser and nodejs integration tests for logs (#15815) ref https://github.com/getsentry/sentry-javascript/issues/15526 --- .../suites/public-api/logger/init.js | 10 + .../suites/public-api/logger/subject.js | 19 + .../suites/public-api/logger/test.ts | 372 ++++++++++++++++++ .../utils/helpers.ts | 11 +- .../utils/assertions.ts | 7 + .../node-integration-tests/utils/runner.ts | 20 +- packages/core/src/types-hoist/index.ts | 2 + 7 files changed, 436 insertions(+), 5 deletions(-) create mode 100644 dev-packages/browser-integration-tests/suites/public-api/logger/init.js create mode 100644 dev-packages/browser-integration-tests/suites/public-api/logger/subject.js create mode 100644 dev-packages/browser-integration-tests/suites/public-api/logger/test.ts diff --git a/dev-packages/browser-integration-tests/suites/public-api/logger/init.js b/dev-packages/browser-integration-tests/suites/public-api/logger/init.js new file mode 100644 index 000000000000..27397e0f90ce --- /dev/null +++ b/dev-packages/browser-integration-tests/suites/public-api/logger/init.js @@ -0,0 +1,10 @@ +import * as Sentry from '@sentry/browser'; + +window.Sentry = Sentry; + +Sentry.init({ + dsn: 'https://public@dsn.ingest.sentry.io/1337', + _experiments: { + enableLogs: true, + }, +}); diff --git a/dev-packages/browser-integration-tests/suites/public-api/logger/subject.js b/dev-packages/browser-integration-tests/suites/public-api/logger/subject.js new file mode 100644 index 000000000000..e175ee4c9e27 --- /dev/null +++ b/dev-packages/browser-integration-tests/suites/public-api/logger/subject.js @@ -0,0 +1,19 @@ +Sentry.logger.trace('test trace'); +Sentry.logger.debug('test debug'); +Sentry.logger.info('test info'); +Sentry.logger.warn('test warn'); +Sentry.logger.error('test error'); +Sentry.logger.fatal('test fatal'); + +const formattedMessage = (message, stringArg, boolArg, numberArg) => { + return Sentry.logger.fmt`test ${message} ${stringArg} ${boolArg} ${numberArg}`; +}; + +Sentry.logger.trace(formattedMessage('trace', 'stringArg', false, 123)); +Sentry.logger.debug(formattedMessage('debug', 'stringArg', false, 123)); +Sentry.logger.info(formattedMessage('info', 'stringArg', false, 123)); +Sentry.logger.warn(formattedMessage('warn', 'stringArg', false, 123)); +Sentry.logger.error(formattedMessage('error', 'stringArg', false, 123)); +Sentry.logger.fatal(formattedMessage('fatal', 'stringArg', false, 123)); + +Sentry.flush(); diff --git a/dev-packages/browser-integration-tests/suites/public-api/logger/test.ts b/dev-packages/browser-integration-tests/suites/public-api/logger/test.ts new file mode 100644 index 000000000000..53a5f31ffcbb --- /dev/null +++ b/dev-packages/browser-integration-tests/suites/public-api/logger/test.ts @@ -0,0 +1,372 @@ +import { expect } from '@playwright/test'; +import type { OtelLogEnvelope } from '@sentry/core'; + +import { sentryTest } from '../../../utils/fixtures'; +import { getFirstSentryEnvelopeRequest, properFullEnvelopeRequestParser } from '../../../utils/helpers'; + +sentryTest('should capture all logging methods', async ({ getLocalTestUrl, page }) => { + const bundle = process.env.PW_BUNDLE || ''; + // Only run this for npm package exports + if (bundle.startsWith('bundle') || bundle.startsWith('loader')) { + sentryTest.skip(); + } + + const url = await getLocalTestUrl({ testDir: __dirname }); + + const event = await getFirstSentryEnvelopeRequest(page, url, properFullEnvelopeRequestParser); + const envelopeItems = event[1]; + + expect(envelopeItems[0]).toEqual([ + { + type: 'otel_log', + }, + { + severityText: 'trace', + body: { stringValue: 'test trace' }, + attributes: [], + timeUnixNano: expect.any(String), + traceId: expect.any(String), + severityNumber: 1, + }, + ]); + + expect(envelopeItems[1]).toEqual([ + { + type: 'otel_log', + }, + { + severityText: 'debug', + body: { stringValue: 'test debug' }, + attributes: [], + timeUnixNano: expect.any(String), + traceId: expect.any(String), + severityNumber: 5, + }, + ]); + + expect(envelopeItems[2]).toEqual([ + { + type: 'otel_log', + }, + { + severityText: 'info', + body: { stringValue: 'test info' }, + attributes: [], + timeUnixNano: expect.any(String), + traceId: expect.any(String), + severityNumber: 9, + }, + ]); + + expect(envelopeItems[3]).toEqual([ + { + type: 'otel_log', + }, + { + severityText: 'warn', + body: { stringValue: 'test warn' }, + attributes: [], + timeUnixNano: expect.any(String), + traceId: expect.any(String), + severityNumber: 13, + }, + ]); + + expect(envelopeItems[4]).toEqual([ + { + type: 'otel_log', + }, + { + severityText: 'error', + body: { stringValue: 'test error' }, + attributes: [], + timeUnixNano: expect.any(String), + traceId: expect.any(String), + severityNumber: 17, + }, + ]); + + expect(envelopeItems[5]).toEqual([ + { + type: 'otel_log', + }, + { + severityText: 'fatal', + body: { stringValue: 'test fatal' }, + attributes: [], + timeUnixNano: expect.any(String), + traceId: expect.any(String), + severityNumber: 21, + }, + ]); + + expect(envelopeItems[6]).toEqual([ + { + type: 'otel_log', + }, + { + severityText: 'trace', + body: { stringValue: 'test trace stringArg false 123' }, + attributes: [ + { + key: 'sentry.message.template', + value: { + stringValue: 'test %s %s %s %s', + }, + }, + { + key: 'sentry.message.param.0', + value: { + stringValue: 'trace', + }, + }, + { + key: 'sentry.message.param.1', + value: { + stringValue: 'stringArg', + }, + }, + { + key: 'sentry.message.param.2', + value: { + boolValue: false, + }, + }, + { + key: 'sentry.message.param.3', + value: { + doubleValue: 123, + }, + }, + ], + timeUnixNano: expect.any(String), + traceId: expect.any(String), + severityNumber: 1, + }, + ]); + + expect(envelopeItems[7]).toEqual([ + { + type: 'otel_log', + }, + { + severityText: 'debug', + body: { stringValue: 'test debug stringArg false 123' }, + attributes: [ + { + key: 'sentry.message.template', + value: { + stringValue: 'test %s %s %s %s', + }, + }, + { + key: 'sentry.message.param.0', + value: { + stringValue: 'debug', + }, + }, + { + key: 'sentry.message.param.1', + value: { + stringValue: 'stringArg', + }, + }, + { + key: 'sentry.message.param.2', + value: { + boolValue: false, + }, + }, + { + key: 'sentry.message.param.3', + value: { + doubleValue: 123, + }, + }, + ], + timeUnixNano: expect.any(String), + traceId: expect.any(String), + severityNumber: 5, + }, + ]); + + expect(envelopeItems[8]).toEqual([ + { + type: 'otel_log', + }, + { + severityText: 'info', + body: { stringValue: 'test info stringArg false 123' }, + attributes: [ + { + key: 'sentry.message.template', + value: { + stringValue: 'test %s %s %s %s', + }, + }, + { + key: 'sentry.message.param.0', + value: { + stringValue: 'info', + }, + }, + { + key: 'sentry.message.param.1', + value: { + stringValue: 'stringArg', + }, + }, + { + key: 'sentry.message.param.2', + value: { + boolValue: false, + }, + }, + { + key: 'sentry.message.param.3', + value: { + doubleValue: 123, + }, + }, + ], + timeUnixNano: expect.any(String), + traceId: expect.any(String), + severityNumber: 9, + }, + ]); + + expect(envelopeItems[9]).toEqual([ + { + type: 'otel_log', + }, + { + severityText: 'warn', + body: { stringValue: 'test warn stringArg false 123' }, + attributes: [ + { + key: 'sentry.message.template', + value: { + stringValue: 'test %s %s %s %s', + }, + }, + { + key: 'sentry.message.param.0', + value: { + stringValue: 'warn', + }, + }, + { + key: 'sentry.message.param.1', + value: { + stringValue: 'stringArg', + }, + }, + { + key: 'sentry.message.param.2', + value: { + boolValue: false, + }, + }, + { + key: 'sentry.message.param.3', + value: { + doubleValue: 123, + }, + }, + ], + timeUnixNano: expect.any(String), + traceId: expect.any(String), + severityNumber: 13, + }, + ]); + + expect(envelopeItems[10]).toEqual([ + { + type: 'otel_log', + }, + { + severityText: 'error', + body: { stringValue: 'test error stringArg false 123' }, + attributes: [ + { + key: 'sentry.message.template', + value: { + stringValue: 'test %s %s %s %s', + }, + }, + { + key: 'sentry.message.param.0', + value: { + stringValue: 'error', + }, + }, + { + key: 'sentry.message.param.1', + value: { + stringValue: 'stringArg', + }, + }, + { + key: 'sentry.message.param.2', + value: { + boolValue: false, + }, + }, + { + key: 'sentry.message.param.3', + value: { + doubleValue: 123, + }, + }, + ], + timeUnixNano: expect.any(String), + traceId: expect.any(String), + severityNumber: 17, + }, + ]); + + expect(envelopeItems[11]).toEqual([ + { + type: 'otel_log', + }, + { + severityText: 'fatal', + body: { stringValue: 'test fatal stringArg false 123' }, + attributes: [ + { + key: 'sentry.message.template', + value: { + stringValue: 'test %s %s %s %s', + }, + }, + { + key: 'sentry.message.param.0', + value: { + stringValue: 'fatal', + }, + }, + { + key: 'sentry.message.param.1', + value: { + stringValue: 'stringArg', + }, + }, + { + key: 'sentry.message.param.2', + value: { + boolValue: false, + }, + }, + { + key: 'sentry.message.param.3', + value: { + doubleValue: 123, + }, + }, + ], + timeUnixNano: expect.any(String), + traceId: expect.any(String), + severityNumber: 21, + }, + ]); +}); diff --git a/dev-packages/browser-integration-tests/utils/helpers.ts b/dev-packages/browser-integration-tests/utils/helpers.ts index 0d2b17cc0db9..d08ffccd7831 100644 --- a/dev-packages/browser-integration-tests/utils/helpers.ts +++ b/dev-packages/browser-integration-tests/utils/helpers.ts @@ -135,10 +135,13 @@ export const countEnvelopes = async ( page.on('request', requestHandler); - setTimeout(() => { - page.off('request', requestHandler); - resolve(reqCount); - }, options?.timeout || 1000); + setTimeout( + () => { + page.off('request', requestHandler); + resolve(reqCount); + }, + options?.timeout || 1000, + ); }); if (options?.url) { diff --git a/dev-packages/node-integration-tests/utils/assertions.ts b/dev-packages/node-integration-tests/utils/assertions.ts index 4197c4a70844..f3925dfd493e 100644 --- a/dev-packages/node-integration-tests/utils/assertions.ts +++ b/dev-packages/node-integration-tests/utils/assertions.ts @@ -4,6 +4,7 @@ import type { Envelope, Event, SerializedCheckIn, + SerializedOtelLog, SerializedSession, SessionAggregates, TransactionEvent, @@ -66,6 +67,12 @@ export function assertSentryClientReport(actual: ClientReport, expected: Partial }); } +export function assertSentryOtelLog(actual: SerializedOtelLog, expected: Partial): void { + expect(actual).toMatchObject({ + ...expected, + }); +} + export function assertEnvelopeHeader(actual: Envelope[0], expected: Partial): void { expect(actual).toEqual({ event_id: expect.any(String), diff --git a/dev-packages/node-integration-tests/utils/runner.ts b/dev-packages/node-integration-tests/utils/runner.ts index 1d77e80bf55c..436e03a4c330 100644 --- a/dev-packages/node-integration-tests/utils/runner.ts +++ b/dev-packages/node-integration-tests/utils/runner.ts @@ -10,6 +10,7 @@ import type { Event, EventEnvelope, SerializedCheckIn, + SerializedOtelLog, SerializedSession, SessionAggregates, TransactionEvent, @@ -20,6 +21,7 @@ import { assertSentryCheckIn, assertSentryClientReport, assertSentryEvent, + assertSentryOtelLog, assertSentrySession, assertSentrySessions, assertSentryTransaction, @@ -119,6 +121,7 @@ type ExpectedSession = Partial | ((event: SerializedSession) type ExpectedSessions = Partial | ((event: SessionAggregates) => void); type ExpectedCheckIn = Partial | ((event: SerializedCheckIn) => void); type ExpectedClientReport = Partial | ((event: ClientReport) => void); +type ExpectedOtelLog = Partial | ((event: SerializedOtelLog) => void); type Expected = | { @@ -138,13 +141,17 @@ type Expected = } | { client_report: ExpectedClientReport; + } + | { + otel_log: ExpectedOtelLog; }; type ExpectedEnvelopeHeader = | { event: Partial } | { transaction: Partial } | { session: Partial } - | { sessions: Partial }; + | { sessions: Partial } + | { otel_log: Partial }; type StartResult = { completed(): Promise; @@ -325,6 +332,9 @@ export function createRunner(...paths: string[]) { } else if ('client_report' in expected) { expectClientReport(item[1] as ClientReport, expected.client_report); expectCallbackCalled(); + } else if ('otel_log' in expected) { + expectOtelLog(item[1] as SerializedOtelLog, expected.otel_log); + expectCallbackCalled(); } else { throw new Error( `Unhandled expected envelope item type: ${JSON.stringify(expected)}\nItem: ${JSON.stringify(item)}`, @@ -547,3 +557,11 @@ function expectClientReport(item: ClientReport, expected: ExpectedClientReport): assertSentryClientReport(item, expected); } } + +function expectOtelLog(item: SerializedOtelLog, expected: ExpectedOtelLog): void { + if (typeof expected === 'function') { + expected(item); + } else { + assertSentryOtelLog(item, expected); + } +} diff --git a/packages/core/src/types-hoist/index.ts b/packages/core/src/types-hoist/index.ts index 1ca827f225a5..1659aeda9bc0 100644 --- a/packages/core/src/types-hoist/index.ts +++ b/packages/core/src/types-hoist/index.ts @@ -49,6 +49,8 @@ export type { ProfileChunkItem, SpanEnvelope, SpanItem, + OtelLogEnvelope, + OtelLogItem, } from './envelope'; export type { ExtendedError } from './error'; export type { Event, EventHint, EventType, ErrorEvent, TransactionEvent } from './event'; From 2284e81ecd035e165d0e9c5fd97312d89d10ec40 Mon Sep 17 00:00:00 2001 From: Luca Forstner Date: Wed, 26 Mar 2025 16:46:11 +0100 Subject: [PATCH 26/28] deps: Bump bundler plugins to `3.2.3` (#15829) --- packages/gatsby/package.json | 2 +- packages/nextjs/package.json | 2 +- packages/nuxt/package.json | 4 +- packages/react-router/package.json | 2 +- packages/sveltekit/package.json | 2 +- yarn.lock | 187 +++++++++-------------------- 6 files changed, 65 insertions(+), 134 deletions(-) diff --git a/packages/gatsby/package.json b/packages/gatsby/package.json index 590141d75264..57d49c028d5e 100644 --- a/packages/gatsby/package.json +++ b/packages/gatsby/package.json @@ -47,7 +47,7 @@ "dependencies": { "@sentry/core": "9.9.0", "@sentry/react": "9.9.0", - "@sentry/webpack-plugin": "3.2.2" + "@sentry/webpack-plugin": "3.2.3" }, "peerDependencies": { "gatsby": "^2.0.0 || ^3.0.0 || ^4.0.0 || ^5.0.0", diff --git a/packages/nextjs/package.json b/packages/nextjs/package.json index 09904a4611af..d933aec3811d 100644 --- a/packages/nextjs/package.json +++ b/packages/nextjs/package.json @@ -85,7 +85,7 @@ "@sentry/opentelemetry": "9.9.0", "@sentry/react": "9.9.0", "@sentry/vercel-edge": "9.9.0", - "@sentry/webpack-plugin": "3.2.2", + "@sentry/webpack-plugin": "3.2.3", "chalk": "3.0.0", "resolve": "1.22.8", "rollup": "4.35.0", diff --git a/packages/nuxt/package.json b/packages/nuxt/package.json index fce211d2d2e4..cda00dd68bbb 100644 --- a/packages/nuxt/package.json +++ b/packages/nuxt/package.json @@ -47,8 +47,8 @@ "@sentry/core": "9.9.0", "@sentry/node": "9.9.0", "@sentry/opentelemetry": "9.9.0", - "@sentry/rollup-plugin": "3.1.2", - "@sentry/vite-plugin": "2.22.6", + "@sentry/rollup-plugin": "3.2.3", + "@sentry/vite-plugin": "3.2.3", "@sentry/vue": "9.9.0" }, "devDependencies": { diff --git a/packages/react-router/package.json b/packages/react-router/package.json index 801c888aaef4..cb31ab4cb32b 100644 --- a/packages/react-router/package.json +++ b/packages/react-router/package.json @@ -38,7 +38,7 @@ "@sentry/cli": "^2.42.3", "@sentry/core": "9.9.0", "@sentry/node": "9.9.0", - "@sentry/vite-plugin": "^3.2.0", + "@sentry/vite-plugin": "^3.2.3", "glob": "11.0.1" }, "devDependencies": { diff --git a/packages/sveltekit/package.json b/packages/sveltekit/package.json index 9e145941bcbf..1cb03303ff49 100644 --- a/packages/sveltekit/package.json +++ b/packages/sveltekit/package.json @@ -50,7 +50,7 @@ "@sentry/node": "9.9.0", "@sentry/opentelemetry": "9.9.0", "@sentry/svelte": "9.9.0", - "@sentry/vite-plugin": "3.2.0", + "@sentry/vite-plugin": "3.2.3", "magic-string": "0.30.7", "recast": "0.23.11", "sorcery": "1.0.0" diff --git a/yarn.lock b/yarn.lock index 88ef6070bd84..521c455d0e64 100644 --- a/yarn.lock +++ b/yarn.lock @@ -84,10 +84,10 @@ debug "^4.3.4" safe-buffer "~5.1.2" -"@adobe/css-tools@^4.0.1": - version "4.4.0" - resolved "https://registry.yarnpkg.com/@adobe/css-tools/-/css-tools-4.4.0.tgz#728c484f4e10df03d5a3acd0d8adcbbebff8ad63" - integrity sha512-Ff9+ksdQQB3rMncgqDK78uLznstjyfIf2Arnh22pW8kBpLs6rpKDwgnZT46hin5Hl1WzazzK64DOrhSwYpS7bQ== +"@adobe/css-tools@^4.0.1", "@adobe/css-tools@^4.4.0": + version "4.4.2" + resolved "https://registry.yarnpkg.com/@adobe/css-tools/-/css-tools-4.4.2.tgz#c836b1bd81e6d62cd6cdf3ee4948bcdce8ea79c8" + integrity sha512-baYZExFpsdkBNuvGKTKWCwKH57HRZLVtycZS05WTQNVOiXVSeAki3nU35zlRbToeMW8aHlJfyS+1C4BOv27q0A== "@ai-sdk/provider-utils@2.0.2": version "2.0.2" @@ -1273,7 +1273,7 @@ dependencies: "@babel/highlight" "^7.10.4" -"@babel/code-frame@^7.0.0", "@babel/code-frame@^7.10.4", "@babel/code-frame@^7.12.13", "@babel/code-frame@^7.18.6", "@babel/code-frame@^7.23.5", "@babel/code-frame@^7.24.2", "@babel/code-frame@^7.25.9", "@babel/code-frame@^7.26.0", "@babel/code-frame@^7.26.2": +"@babel/code-frame@^7.0.0", "@babel/code-frame@^7.10.4", "@babel/code-frame@^7.12.13", "@babel/code-frame@^7.18.6", "@babel/code-frame@^7.23.5", "@babel/code-frame@^7.24.2", "@babel/code-frame@^7.26.2": version "7.26.2" resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.26.2.tgz#4b5fab97d33338eff916235055f0ebc21e573a85" integrity sha512-RJlIHRueQgwWitWgF8OdFYGZX328Ax5BCemNGlqHfplnRT9ESi8JkFlvaVYbS+UubVY6dpv87Fs2u5M29iNFVQ== @@ -1282,7 +1282,7 @@ js-tokens "^4.0.0" picocolors "^1.0.0" -"@babel/compat-data@^7.17.7", "@babel/compat-data@^7.18.8", "@babel/compat-data@^7.20.5", "@babel/compat-data@^7.22.6", "@babel/compat-data@^7.24.4", "@babel/compat-data@^7.26.5", "@babel/compat-data@^7.26.8": +"@babel/compat-data@^7.17.7", "@babel/compat-data@^7.18.8", "@babel/compat-data@^7.20.5", "@babel/compat-data@^7.22.6", "@babel/compat-data@^7.24.4", "@babel/compat-data@^7.26.8": version "7.26.8" resolved "https://registry.yarnpkg.com/@babel/compat-data/-/compat-data-7.26.8.tgz#821c1d35641c355284d4a870b8a4a7b0c141e367" integrity sha512-oH5UPLMWR3L2wEFLnFJ1TZXqHufiTKAiLfqw5zkhS4dKXLJ10yVztfil/twG8EDTA4F/tvVNw9nOl4ZMslB8rQ== @@ -1338,7 +1338,7 @@ "@jridgewell/gen-mapping" "^0.3.2" jsesc "^2.5.1" -"@babel/generator@^7.18.10", "@babel/generator@^7.21.5", "@babel/generator@^7.22.10", "@babel/generator@^7.23.6", "@babel/generator@^7.26.0", "@babel/generator@^7.26.3", "@babel/generator@^7.26.9": +"@babel/generator@^7.18.10", "@babel/generator@^7.21.5", "@babel/generator@^7.22.10", "@babel/generator@^7.23.6", "@babel/generator@^7.26.9": version "7.26.9" resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.26.9.tgz#75a9482ad3d0cc7188a537aa4910bc59db67cbca" integrity sha512-kEWdzjOAUMW4hAyrzJ0ZaTOu9OmpyDIQicIh0zg0EEcEkYXZb2TjtBhnHi2ViX7PKwZqF4xwqfAm299/QMP3lg== @@ -1370,7 +1370,7 @@ dependencies: "@babel/types" "^7.22.15" -"@babel/helper-compilation-targets@^7.12.0", "@babel/helper-compilation-targets@^7.17.7", "@babel/helper-compilation-targets@^7.18.9", "@babel/helper-compilation-targets@^7.20.7", "@babel/helper-compilation-targets@^7.22.6", "@babel/helper-compilation-targets@^7.23.6", "@babel/helper-compilation-targets@^7.25.9", "@babel/helper-compilation-targets@^7.26.5": +"@babel/helper-compilation-targets@^7.12.0", "@babel/helper-compilation-targets@^7.17.7", "@babel/helper-compilation-targets@^7.18.9", "@babel/helper-compilation-targets@^7.20.7", "@babel/helper-compilation-targets@^7.22.6", "@babel/helper-compilation-targets@^7.23.6", "@babel/helper-compilation-targets@^7.26.5": version "7.27.0" resolved "https://registry.yarnpkg.com/@babel/helper-compilation-targets/-/helper-compilation-targets-7.27.0.tgz#de0c753b1cd1d9ab55d473c5a5cf7170f0a81880" integrity sha512-LVk7fbXml0H2xH34dFzKQ7TDZ2G4/rVTOrq9V+icbbadjbVxxeFeDsNHv2SrZeWoA+6ZiTyWYWtScEIW07EAcA== @@ -1556,7 +1556,7 @@ "@babel/template" "^7.22.15" "@babel/types" "^7.22.19" -"@babel/helpers@^7.18.9", "@babel/helpers@^7.26.0", "@babel/helpers@^7.26.9": +"@babel/helpers@^7.18.9", "@babel/helpers@^7.26.9": version "7.27.0" resolved "https://registry.yarnpkg.com/@babel/helpers/-/helpers-7.27.0.tgz#53d156098defa8243eab0f32fa17589075a1b808" integrity sha512-U5eyP/CTFPuNE3qk+WZMxFkp/4zUzdceQlfzf7DdGdhp+Fezd7HD+i8Y24ZuTMKX3wQBld449jijbGq6OdGNQg== @@ -1581,7 +1581,7 @@ dependencies: "@babel/types" "^7.26.9" -"@babel/parser@^7.1.0", "@babel/parser@^7.14.7", "@babel/parser@^7.16.4", "@babel/parser@^7.18.10", "@babel/parser@^7.20.7", "@babel/parser@^7.21.8", "@babel/parser@^7.22.10", "@babel/parser@^7.22.16", "@babel/parser@^7.23.5", "@babel/parser@^7.23.6", "@babel/parser@^7.23.9", "@babel/parser@^7.25.3", "@babel/parser@^7.25.4", "@babel/parser@^7.25.6", "@babel/parser@^7.25.9", "@babel/parser@^7.26.0", "@babel/parser@^7.26.3", "@babel/parser@^7.26.9", "@babel/parser@^7.27.0", "@babel/parser@^7.4.5", "@babel/parser@^7.7.0": +"@babel/parser@^7.1.0", "@babel/parser@^7.14.7", "@babel/parser@^7.16.4", "@babel/parser@^7.18.10", "@babel/parser@^7.20.7", "@babel/parser@^7.21.8", "@babel/parser@^7.22.10", "@babel/parser@^7.22.16", "@babel/parser@^7.23.5", "@babel/parser@^7.23.6", "@babel/parser@^7.23.9", "@babel/parser@^7.25.3", "@babel/parser@^7.25.4", "@babel/parser@^7.25.6", "@babel/parser@^7.26.9", "@babel/parser@^7.27.0", "@babel/parser@^7.4.5", "@babel/parser@^7.7.0": version "7.27.0" resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.27.0.tgz#3d7d6ee268e41d2600091cbd4e145ffee85a44ec" integrity sha512-iaepho73/2Pz7w2eMS0Q5f83+0RKI7i4xmiYeBmDzfRVbQtTOG7Ts0S4HzJVsTMGI9keU8rNfuZr8DKfSt7Yyg== @@ -2614,7 +2614,7 @@ "@babel/parser" "^7.18.10" "@babel/types" "^7.18.10" -"@babel/template@^7.18.10", "@babel/template@^7.22.15", "@babel/template@^7.23.9", "@babel/template@^7.24.0", "@babel/template@^7.24.7", "@babel/template@^7.25.9", "@babel/template@^7.26.9", "@babel/template@^7.27.0": +"@babel/template@^7.18.10", "@babel/template@^7.22.15", "@babel/template@^7.23.9", "@babel/template@^7.24.0", "@babel/template@^7.24.7", "@babel/template@^7.26.9", "@babel/template@^7.27.0": version "7.27.0" resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.27.0.tgz#b253e5406cc1df1c57dcd18f11760c2dbf40c0b4" integrity sha512-2ncevenBqXI6qRMukPlXwHKHchC7RyMuu4xv5JBXRfOGVcTy1mXCD12qrp7Jsoxll1EV3+9sE4GugBVRjT2jFA== @@ -2636,7 +2636,7 @@ debug "^4.3.1" globals "^11.1.0" -"@babel/types@^7.0.0", "@babel/types@^7.18.10", "@babel/types@^7.18.6", "@babel/types@^7.20.7", "@babel/types@^7.22.10", "@babel/types@^7.22.15", "@babel/types@^7.22.17", "@babel/types@^7.22.19", "@babel/types@^7.22.5", "@babel/types@^7.23.6", "@babel/types@^7.23.9", "@babel/types@^7.24.7", "@babel/types@^7.25.4", "@babel/types@^7.25.6", "@babel/types@^7.25.9", "@babel/types@^7.26.0", "@babel/types@^7.26.3", "@babel/types@^7.26.9", "@babel/types@^7.27.0", "@babel/types@^7.3.0", "@babel/types@^7.4.4", "@babel/types@^7.7.0", "@babel/types@^7.7.2": +"@babel/types@^7.0.0", "@babel/types@^7.18.10", "@babel/types@^7.18.6", "@babel/types@^7.20.7", "@babel/types@^7.22.10", "@babel/types@^7.22.15", "@babel/types@^7.22.17", "@babel/types@^7.22.19", "@babel/types@^7.22.5", "@babel/types@^7.23.6", "@babel/types@^7.23.9", "@babel/types@^7.24.7", "@babel/types@^7.25.4", "@babel/types@^7.25.6", "@babel/types@^7.25.9", "@babel/types@^7.26.3", "@babel/types@^7.26.9", "@babel/types@^7.27.0", "@babel/types@^7.3.0", "@babel/types@^7.4.4", "@babel/types@^7.7.0", "@babel/types@^7.7.2": version "7.27.0" resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.27.0.tgz#ef9acb6b06c3173f6632d993ecb6d4ae470b4559" integrity sha512-H45s8fVLYjbhFH62dIJ3WtmJ6RSPt/3DRO0ZcT2SUiYiQyz3BLVb9ADEnLl91m74aQPS3AzzeajZHYOalWe3bg== @@ -6459,20 +6459,10 @@ resolved "https://registry.yarnpkg.com/@sentry/babel-plugin-component-annotate/-/babel-plugin-component-annotate-2.22.6.tgz#829d6caf2c95c1c46108336de4e1049e6521435e" integrity sha512-V2g1Y1I5eSe7dtUVMBvAJr8BaLRr4CLrgNgtPaZyMT4Rnps82SrZ5zqmEkLXPumlXhLUWR6qzoMNN2u+RXVXfQ== -"@sentry/babel-plugin-component-annotate@3.1.2": - version "3.1.2" - resolved "https://registry.yarnpkg.com/@sentry/babel-plugin-component-annotate/-/babel-plugin-component-annotate-3.1.2.tgz#5497ca5adbe775955e96c566511a0bed3ab0a3ce" - integrity sha512-5h2WXRJ6swKA0TwxHHryC8M2QyOfS9QhTAL6ElPfkEYe9HhJieXmxsDpyspbqAa26ccnCUcmwE5vL34jAjt4sQ== - -"@sentry/babel-plugin-component-annotate@3.2.0": - version "3.2.0" - resolved "https://registry.yarnpkg.com/@sentry/babel-plugin-component-annotate/-/babel-plugin-component-annotate-3.2.0.tgz#17c000cf6cc315bb620eddbd95c88dfb2471cfb9" - integrity sha512-Sg7nLRP1yiJYl/KdGGxYGbjvLq5rswyeB5yESgfWX34XUNZaFgmNvw4pU/QEKVeYgcPyOulgJ+y80ewujyffTA== - -"@sentry/babel-plugin-component-annotate@3.2.2": - version "3.2.2" - resolved "https://registry.yarnpkg.com/@sentry/babel-plugin-component-annotate/-/babel-plugin-component-annotate-3.2.2.tgz#0c5f26e417b8f524924fa4531b82ad5603216e90" - integrity sha512-D+SKQ266ra/wo87s9+UI/rKQi3qhGPCR8eSCDe0VJudhjHsqyNU+JJ5lnIGCgmZaWFTXgdBP/gdr1Iz1zqGs4Q== +"@sentry/babel-plugin-component-annotate@3.2.3": + version "3.2.3" + resolved "https://registry.yarnpkg.com/@sentry/babel-plugin-component-annotate/-/babel-plugin-component-annotate-3.2.3.tgz#2ec2ca459fe4087924dda5c54bd8a734fccba3bf" + integrity sha512-nbFvTHD7wOfxsqsbkyDVxxi1XMv6j54TW6i0vwLnzq3uwsSno/bs2Xzdc+mzXyMmji1b27kpOPiI2fKBi3XXVQ== "@sentry/bundler-plugin-core@2.22.6": version "2.22.6" @@ -6488,41 +6478,13 @@ magic-string "0.30.8" unplugin "1.0.1" -"@sentry/bundler-plugin-core@3.1.2": - version "3.1.2" - resolved "https://registry.yarnpkg.com/@sentry/bundler-plugin-core/-/bundler-plugin-core-3.1.2.tgz#29e4e686c5893b41a0d98a1bef6f0315a610bd59" - integrity sha512-lqOCvmOPzKiQenIMhmm5/mwCntwFy0dPZbVD28Dnr3MXpT1rIBg1HXjfnqQWFlMRbL9haSsWiY/TQyR/6b30YA== - dependencies: - "@babel/core" "^7.18.5" - "@sentry/babel-plugin-component-annotate" "3.1.2" - "@sentry/cli" "2.41.1" - dotenv "^16.3.1" - find-up "^5.0.0" - glob "^9.3.2" - magic-string "0.30.8" - unplugin "1.0.1" - -"@sentry/bundler-plugin-core@3.2.0": - version "3.2.0" - resolved "https://registry.yarnpkg.com/@sentry/bundler-plugin-core/-/bundler-plugin-core-3.2.0.tgz#023ec92530a35fbec7c7077b7a8be2e79f0f9dd5" - integrity sha512-Q/ogVylue3XaFawyIxzuiic+7Dp4w63eJtRtVH8VBebNURyJ/re4GVoP1QNGccE1R243tXY1y2GiwqiJkAONOg== - dependencies: - "@babel/core" "^7.18.5" - "@sentry/babel-plugin-component-annotate" "3.2.0" - "@sentry/cli" "2.41.1" - dotenv "^16.3.1" - find-up "^5.0.0" - glob "^9.3.2" - magic-string "0.30.8" - unplugin "1.0.1" - -"@sentry/bundler-plugin-core@3.2.2": - version "3.2.2" - resolved "https://registry.yarnpkg.com/@sentry/bundler-plugin-core/-/bundler-plugin-core-3.2.2.tgz#c9193b0c97acf0097fdb820d86eaaad9c9b6b2c4" - integrity sha512-YGrtmqQ2jMixccX2slVG/Lw7pCGJL3DGB3clmY9mO8QBEBIN3/gEANiHJVWwRidpUOS/0b7yVVGAdwZ87oPwTg== +"@sentry/bundler-plugin-core@3.2.3": + version "3.2.3" + resolved "https://registry.yarnpkg.com/@sentry/bundler-plugin-core/-/bundler-plugin-core-3.2.3.tgz#2d88a3bcae74299aa9969725fd80484847d2ff02" + integrity sha512-efg4F0ia1eO0GdWF9a/9tyKQaP5eHABBU8v/zFTmdw6WCPozFH+aRvfXOVSLGoII/sduRamW9BiUlE4NxrWPqQ== dependencies: "@babel/core" "^7.18.5" - "@sentry/babel-plugin-component-annotate" "3.2.2" + "@sentry/babel-plugin-component-annotate" "3.2.3" "@sentry/cli" "2.42.2" dotenv "^16.3.1" find-up "^5.0.0" @@ -6530,11 +6492,6 @@ magic-string "0.30.8" unplugin "1.0.1" -"@sentry/cli-darwin@2.41.1": - version "2.41.1" - resolved "https://registry.yarnpkg.com/@sentry/cli-darwin/-/cli-darwin-2.41.1.tgz#ca7e12bf1ad59bc2df35868ae98abc8869108efa" - integrity sha512-7pS3pu/SuhE6jOn3wptstAg6B5nUP878O6s+2svT7b5fKNfYUi/6NPK6dAveh2Ca0rwVq40TO4YFJabWMgTpdQ== - "@sentry/cli-darwin@2.42.2": version "2.42.2" resolved "https://registry.yarnpkg.com/@sentry/cli-darwin/-/cli-darwin-2.42.2.tgz#a32a4f226e717122b37d9969e8d4d0e14779f720" @@ -6545,11 +6502,6 @@ resolved "https://registry.yarnpkg.com/@sentry/cli-darwin/-/cli-darwin-2.42.3.tgz#34782c2bf889cec99794ec90287fec3836b1a691" integrity sha512-QGNXZ5c2kbjB3O37ep/uVfqTspHaHkH4kmoMPNJ6j21A1oYyJq5t/AX9JWsueysRwvn6Jc0K0+XyzYZ13z0vsQ== -"@sentry/cli-linux-arm64@2.41.1": - version "2.41.1" - resolved "https://registry.yarnpkg.com/@sentry/cli-linux-arm64/-/cli-linux-arm64-2.41.1.tgz#948e8af8290418b1562db3531db08e69e39d74bb" - integrity sha512-EzYCEnnENBnS5kpNW+2dBcrPZn1MVfywh2joGVQZTpmgDL5YFJ59VOd+K0XuEwqgFI8BSNI14KXZ75s4DD1/Vw== - "@sentry/cli-linux-arm64@2.42.2": version "2.42.2" resolved "https://registry.yarnpkg.com/@sentry/cli-linux-arm64/-/cli-linux-arm64-2.42.2.tgz#1c06c83ff21f51ec23acf5be3b1f8c7553bf86b1" @@ -6560,11 +6512,6 @@ resolved "https://registry.yarnpkg.com/@sentry/cli-linux-arm64/-/cli-linux-arm64-2.42.3.tgz#b722270c81fbdd7703a26451f505f82528d97116" integrity sha512-tRqWrmphK82G14KKFEouLdV8BdCpGsTuySZ8nzTqhoAtcjpWFaavX2/1UqKzPVYjkxwXc1npO3Q7qfZYW2HvjQ== -"@sentry/cli-linux-arm@2.41.1": - version "2.41.1" - resolved "https://registry.yarnpkg.com/@sentry/cli-linux-arm/-/cli-linux-arm-2.41.1.tgz#1e5fa971ae8dfb3ea5564c8503b4e635ae6aed8a" - integrity sha512-wNUvquD6qjOCczvuBGf9OiD29nuQ6yf8zzfyPJa5Bdx1QXuteKsKb6HBrMwuIR3liyuu0duzHd+H/+p1n541Hg== - "@sentry/cli-linux-arm@2.42.2": version "2.42.2" resolved "https://registry.yarnpkg.com/@sentry/cli-linux-arm/-/cli-linux-arm-2.42.2.tgz#00cadc359ae3c051efb3e63873c033c61dbd1ca1" @@ -6575,11 +6522,6 @@ resolved "https://registry.yarnpkg.com/@sentry/cli-linux-arm/-/cli-linux-arm-2.42.3.tgz#81373e1dd017a3d265a49172546063ebbcf69098" integrity sha512-tipumegAsKy9KLq6Bk87E8FqkKErReaNzdhoHCb081jkxQxpzKN/MQPMl9mA0XeKc4A7OUBM3vjhIk6uNi1R2g== -"@sentry/cli-linux-i686@2.41.1": - version "2.41.1" - resolved "https://registry.yarnpkg.com/@sentry/cli-linux-i686/-/cli-linux-i686-2.41.1.tgz#3f01aff314f2ad8fd761f3e6e807a5ec09ae4eb4" - integrity sha512-urpQCWrdYnSAsZY3udttuMV88wTJzKZL10xsrp7sjD/Hd+O6qSLVLkxebIlxts70jMLLFHYrQ2bkRg5kKuX6Fg== - "@sentry/cli-linux-i686@2.42.2": version "2.42.2" resolved "https://registry.yarnpkg.com/@sentry/cli-linux-i686/-/cli-linux-i686-2.42.2.tgz#3b817b715dd806c20dfbffd539725ad8089c310a" @@ -6590,11 +6532,6 @@ resolved "https://registry.yarnpkg.com/@sentry/cli-linux-i686/-/cli-linux-i686-2.42.3.tgz#8342a137ea57df0bb0136a57c3a21eddc1e28991" integrity sha512-pc4Kc7xTMNbUPiRLQ2UXcj2V2vbVmST0IyhOlVTmY0L3ZxMdiwTq7qgS/IcxI/CaCPyEZWplXnxlsa//mCMuYw== -"@sentry/cli-linux-x64@2.41.1": - version "2.41.1" - resolved "https://registry.yarnpkg.com/@sentry/cli-linux-x64/-/cli-linux-x64-2.41.1.tgz#30dbf966a4b4c1721ffccd901dfcb6f967db073d" - integrity sha512-ZqpYwHXAaK4MMEFlyaLYr6mJTmpy9qP6n30jGhLTW7kHKS3s6GPLCSlNmIfeClrInEt0963fM633ZRnXa04VPw== - "@sentry/cli-linux-x64@2.42.2": version "2.42.2" resolved "https://registry.yarnpkg.com/@sentry/cli-linux-x64/-/cli-linux-x64-2.42.2.tgz#ddf906bc3071cc79ce6e633eddcb76bb9068e688" @@ -6605,11 +6542,6 @@ resolved "https://registry.yarnpkg.com/@sentry/cli-linux-x64/-/cli-linux-x64-2.42.3.tgz#46c4331a46e9c5cee547dcc6c9e039fb8a45f331" integrity sha512-CZoG2swc38/RruLYtvMJNpcbvzqpcSDl+dUFJ3y6wIvMsE1IQL3zrb4xSh1Acf8Hi+GhlFAtoZpgH1tpWWZ/Zw== -"@sentry/cli-win32-i686@2.41.1": - version "2.41.1" - resolved "https://registry.yarnpkg.com/@sentry/cli-win32-i686/-/cli-win32-i686-2.41.1.tgz#f88eeb5d2d4ee46c38d8616ae1eb484108ea71c2" - integrity sha512-AuRimCeVsx99DIOr9cwdYBHk39tlmAuPDdy2r16iNzY0InXs4xOys4gGzM7N4vlFQvFkzuc778Su0HkfasgprA== - "@sentry/cli-win32-i686@2.42.2": version "2.42.2" resolved "https://registry.yarnpkg.com/@sentry/cli-win32-i686/-/cli-win32-i686-2.42.2.tgz#9036085c7c6ce455ad45fda411c55ff39c06eb95" @@ -6620,11 +6552,6 @@ resolved "https://registry.yarnpkg.com/@sentry/cli-win32-i686/-/cli-win32-i686-2.42.3.tgz#5fc97729eb895707808a757ec6d557fbbf4a44de" integrity sha512-geiPcfeuSj23N346xwrxFAuohIivK48NKQQYllVFAADYoYrSawSznVzRyOIheiOte1AIrDkpqzoT/5igl7ANEQ== -"@sentry/cli-win32-x64@2.41.1": - version "2.41.1" - resolved "https://registry.yarnpkg.com/@sentry/cli-win32-x64/-/cli-win32-x64-2.41.1.tgz#eefd95a2aa184adb464334e265b55a9142070f6f" - integrity sha512-6JcPvXGye61+wPp0xdzfc2YLE/Dcud8JdaK8VxLM3b/8+Em7E+UyliDu3uF8+YGUqizY5JYTd3fs17DC8DZhLw== - "@sentry/cli-win32-x64@2.42.2": version "2.42.2" resolved "https://registry.yarnpkg.com/@sentry/cli-win32-x64/-/cli-win32-x64-2.42.2.tgz#7d6464b63f32c9f97fff428f246b1f039b402233" @@ -6635,25 +6562,6 @@ resolved "https://registry.yarnpkg.com/@sentry/cli-win32-x64/-/cli-win32-x64-2.42.3.tgz#759f320d60dc1d923d8c8edc44945aca77813f67" integrity sha512-sG+phJ+3WUMx6gTrUd7UH+q0L6X1YjS57ovMMf3XYyE/WIF8c+uc+vZC/RB3O5l3vTTCXoePqHf8+9ulgp9dkA== -"@sentry/cli@2.41.1": - version "2.41.1" - resolved "https://registry.yarnpkg.com/@sentry/cli/-/cli-2.41.1.tgz#a9467ca3ff4acfcdedec1565c9ff726b93758d29" - integrity sha512-0GVmDiTV7R1492wkVY4bGcfC0fSmRmQjuxaaPI8CIV9B2VP9pBVCUizi1mevXaaE4I3fM60LI+XYrKFEneuVog== - dependencies: - https-proxy-agent "^5.0.0" - node-fetch "^2.6.7" - progress "^2.0.3" - proxy-from-env "^1.1.0" - which "^2.0.2" - optionalDependencies: - "@sentry/cli-darwin" "2.41.1" - "@sentry/cli-linux-arm" "2.41.1" - "@sentry/cli-linux-arm64" "2.41.1" - "@sentry/cli-linux-i686" "2.41.1" - "@sentry/cli-linux-x64" "2.41.1" - "@sentry/cli-win32-i686" "2.41.1" - "@sentry/cli-win32-x64" "2.41.1" - "@sentry/cli@2.42.2": version "2.42.2" resolved "https://registry.yarnpkg.com/@sentry/cli/-/cli-2.42.2.tgz#8173df4d057d600a9ef0cf1e9b42b0c6607b46e4" @@ -6692,12 +6600,12 @@ "@sentry/cli-win32-i686" "2.42.3" "@sentry/cli-win32-x64" "2.42.3" -"@sentry/rollup-plugin@3.1.2": - version "3.1.2" - resolved "https://registry.yarnpkg.com/@sentry/rollup-plugin/-/rollup-plugin-3.1.2.tgz#d1ed4eeb558e10260bf0e7f292f9ad6baf22a98c" - integrity sha512-CVUsfQkL8REOGuyaPX7BzccZoq+wce05gQW3dG4PcXNPQeKTPRpC89NLcCDJijJa08yvC0DF0wsWRhlFWM89kQ== +"@sentry/rollup-plugin@3.2.3": + version "3.2.3" + resolved "https://registry.yarnpkg.com/@sentry/rollup-plugin/-/rollup-plugin-3.2.3.tgz#df931a1858d8d4cbdb7714793f76eaf2e7f34b2d" + integrity sha512-00WQUYvJ/X5WlfI+MK/Wf+hXroafFw4kEae0/ToAYTnpOIp7twQ/ULBhsGmyzOLSzxnv3CtkhxbOz7sUPdmWvQ== dependencies: - "@sentry/bundler-plugin-core" "3.1.2" + "@sentry/bundler-plugin-core" "3.2.3" unplugin "1.0.1" "@sentry/vite-plugin@2.22.6", "@sentry/vite-plugin@^2.22.6": @@ -6708,20 +6616,20 @@ "@sentry/bundler-plugin-core" "2.22.6" unplugin "1.0.1" -"@sentry/vite-plugin@3.2.0", "@sentry/vite-plugin@^3.2.0": - version "3.2.0" - resolved "https://registry.yarnpkg.com/@sentry/vite-plugin/-/vite-plugin-3.2.0.tgz#0785b6e04e0aed8a4d6b57a433a2da11c14e6cd0" - integrity sha512-IVBoAzZmpoX9+mnmIMq2ndxlFPoWMuYSE5Mek5zOWpYh+GbPxvkrxvM+vg0HeLH4r5v9Tm0FWcEZDgDIZqtoSg== +"@sentry/vite-plugin@3.2.3", "@sentry/vite-plugin@^3.2.3": + version "3.2.3" + resolved "https://registry.yarnpkg.com/@sentry/vite-plugin/-/vite-plugin-3.2.3.tgz#3e51bcd7ee37b5b84177637df4d66dbed953a5f5" + integrity sha512-7znFCOi60tTr5LjVnsP9Bb1kXfYHUz2ylueBT6bY5pu9xO5C3D0wpSodY8X7AiqLKWOc5wPbHMSIxNOWp6in3g== dependencies: - "@sentry/bundler-plugin-core" "3.2.0" + "@sentry/bundler-plugin-core" "3.2.3" unplugin "1.0.1" -"@sentry/webpack-plugin@3.2.2": - version "3.2.2" - resolved "https://registry.yarnpkg.com/@sentry/webpack-plugin/-/webpack-plugin-3.2.2.tgz#716ab462279c25cea17490d02cb1d22b00f3f661" - integrity sha512-6OkVKNOjKk8P9j7oh6svZ+kEP1i9YIHBC2aGWL2XsgeZTIrMBxJAXtOf+qSrfMAxEtibSroGVOMQc/y3WJTQtg== +"@sentry/webpack-plugin@3.2.3": + version "3.2.3" + resolved "https://registry.yarnpkg.com/@sentry/webpack-plugin/-/webpack-plugin-3.2.3.tgz#80fbb39045dc54e81ca8df4aac1d4e61c2b729df" + integrity sha512-uBRJO54wOqZ2csi/JT0kLeNpjClypwYBsyrqKgDSp04N2oEnt1eZr4L4yS4kNShI/ZrkA5Q4VWuFk5cCB2f/ew== dependencies: - "@sentry/bundler-plugin-core" "3.2.2" + "@sentry/bundler-plugin-core" "3.2.3" unplugin "1.0.1" uuid "^9.0.0" @@ -7373,6 +7281,19 @@ lz-string "^1.5.0" pretty-format "^27.0.2" +"@testing-library/jest-dom@^6.4.5": + version "6.6.3" + resolved "https://registry.yarnpkg.com/@testing-library/jest-dom/-/jest-dom-6.6.3.tgz#26ba906cf928c0f8172e182c6fe214eb4f9f2bd2" + integrity sha512-IteBhl4XqYNkM54f4ejhLRJiZNqcSCoXUOG2CPK7qbD322KjQozM4kHQOfkG2oln9b9HTYqs+Sae8vBATubxxA== + dependencies: + "@adobe/css-tools" "^4.4.0" + aria-query "^5.0.0" + chalk "^3.0.0" + css.escape "^1.5.1" + dom-accessibility-api "^0.6.3" + lodash "^4.17.21" + redent "^3.0.0" + "@testing-library/react-hooks@^7.0.2": version "7.0.2" resolved "https://registry.yarnpkg.com/@testing-library/react-hooks/-/react-hooks-7.0.2.tgz#3388d07f562d91e7f2431a4a21b5186062ecfee0" @@ -12687,6 +12608,11 @@ css-what@^6.0.1, css-what@^6.1.0: resolved "https://registry.yarnpkg.com/css-what/-/css-what-6.1.0.tgz#fb5effcf76f1ddea2c81bdfaa4de44e79bac70f4" integrity sha512-HTUrgRJ7r4dsZKU6GjmpfRK1O76h97Z8MfS1G0FozR+oF2kG6Vfe8JE6zwrkbxigziPHinCJ+gCPjA9EaBDtRw== +css.escape@^1.5.1: + version "1.5.1" + resolved "https://registry.yarnpkg.com/css.escape/-/css.escape-1.5.1.tgz#42e27d4fa04ae32f931a4b4d4191fa9cddee97cb" + integrity sha512-YUifsXXuknHlUsmlgyY0PKzgPOr7/FjCePfHNt0jxm83wHZi44VDMQ7/fGNkjY3/jV1MC+1CmZbaHzugyeRtpg== + cssdb@^7.0.0, cssdb@^7.1.0: version "7.11.2" resolved "https://registry.yarnpkg.com/cssdb/-/cssdb-7.11.2.tgz#127a2f5b946ee653361a5af5333ea85a39df5ae5" @@ -13355,6 +13281,11 @@ dom-accessibility-api@^0.5.9: resolved "https://registry.yarnpkg.com/dom-accessibility-api/-/dom-accessibility-api-0.5.13.tgz#102ee5f25eacce09bdf1cfa5a298f86da473be4b" integrity sha512-R305kwb5CcMDIpSHUnLyIAp7SrSPBx6F0VfQFB3M75xVMHhXJJIdePYgbPPh1o57vCHNu5QztokWUPsLjWzFqw== +dom-accessibility-api@^0.6.3: + version "0.6.3" + resolved "https://registry.yarnpkg.com/dom-accessibility-api/-/dom-accessibility-api-0.6.3.tgz#993e925cc1d73f2c662e7d75dd5a5445259a8fd8" + integrity sha512-7ZgogeTnjuHbo+ct10G9Ffp0mif17idi0IyWNVA/wcwcm7NPOD/WEHVP3n7n3MhXqxoIYm8d6MuZohYWIZ4T3w== + dom-converter@^0.2.0: version "0.2.0" resolved "https://registry.yarnpkg.com/dom-converter/-/dom-converter-0.2.0.tgz#6721a9daee2e293682955b6afe416771627bb768" From ef2f35d5863ce5e581a36aac9ce4557a9ebe370c Mon Sep 17 00:00:00 2001 From: Francesco Gringl-Novy Date: Wed, 26 Mar 2025 17:17:49 +0100 Subject: [PATCH 27/28] ref(opentelemetry): Avoid sampling work for non-root spans (#15820) Related to https://github.com/getsentry/sentry-javascript/issues/15725#issuecomment-2742022649 When a span is not a root span, we do not need to do sampling work for it. By reordering stuff in the `shouldSample` function, we should be able to save a bunch of operations for the vast majority of (non-root) spans. I _think_ this should be just fine! --- packages/opentelemetry/src/sampler.ts | 129 +++++++++++++------------- 1 file changed, 62 insertions(+), 67 deletions(-) diff --git a/packages/opentelemetry/src/sampler.ts b/packages/opentelemetry/src/sampler.ts index 9c9889e2d2fa..20a04f9cc9e7 100644 --- a/packages/opentelemetry/src/sampler.ts +++ b/packages/opentelemetry/src/sampler.ts @@ -67,6 +67,17 @@ export class SentrySampler implements Sampler { } const parentSampled = parentSpan ? getParentSampled(parentSpan, traceId, spanName) : undefined; + const isRootSpan = !parentSpan || parentContext?.isRemote; + + // We only sample based on parameters (like tracesSampleRate or tracesSampler) for root spans (which is done in sampleSpan). + // Non-root-spans simply inherit the sampling decision from their parent. + if (!isRootSpan) { + return wrapSamplingDecision({ + decision: parentSampled ? SamplingDecision.RECORD_AND_SAMPLED : SamplingDecision.NOT_RECORD, + context, + spanAttributes, + }); + } // We want to pass the inferred name & attributes to the sampler method const { @@ -99,76 +110,60 @@ export class SentrySampler implements Sampler { return wrapSamplingDecision({ decision: undefined, context, spanAttributes }); } - const isRootSpan = !parentSpan || parentContext?.isRemote; + const { isolationScope } = getScopesFromContext(context) ?? {}; - // We only sample based on parameters (like tracesSampleRate or tracesSampler) for root spans (which is done in sampleSpan). - // Non-root-spans simply inherit the sampling decision from their parent. - if (isRootSpan) { - const { isolationScope } = getScopesFromContext(context) ?? {}; - - const dscString = parentContext?.traceState ? parentContext.traceState.get(SENTRY_TRACE_STATE_DSC) : undefined; - const dsc = dscString ? baggageHeaderToDynamicSamplingContext(dscString) : undefined; - - const sampleRand = parseSampleRate(dsc?.sample_rand) ?? Math.random(); - - const [sampled, sampleRate, localSampleRateWasApplied] = sampleSpan( - options, - { - name: inferredSpanName, - attributes: mergedAttributes, - normalizedRequest: isolationScope?.getScopeData().sdkProcessingMetadata.normalizedRequest, - parentSampled, - parentSampleRate: parseSampleRate(dsc?.sample_rate), - }, + const dscString = parentContext?.traceState ? parentContext.traceState.get(SENTRY_TRACE_STATE_DSC) : undefined; + const dsc = dscString ? baggageHeaderToDynamicSamplingContext(dscString) : undefined; + + const sampleRand = parseSampleRate(dsc?.sample_rand) ?? Math.random(); + + const [sampled, sampleRate, localSampleRateWasApplied] = sampleSpan( + options, + { + name: inferredSpanName, + attributes: mergedAttributes, + normalizedRequest: isolationScope?.getScopeData().sdkProcessingMetadata.normalizedRequest, + parentSampled, + parentSampleRate: parseSampleRate(dsc?.sample_rate), + }, + sampleRand, + ); + + const method = `${maybeSpanHttpMethod}`.toUpperCase(); + if (method === 'OPTIONS' || method === 'HEAD') { + DEBUG_BUILD && logger.log(`[Tracing] Not sampling span because HTTP method is '${method}' for ${spanName}`); + + return wrapSamplingDecision({ + decision: SamplingDecision.NOT_RECORD, + context, + spanAttributes, sampleRand, - ); - - const method = `${maybeSpanHttpMethod}`.toUpperCase(); - if (method === 'OPTIONS' || method === 'HEAD') { - DEBUG_BUILD && logger.log(`[Tracing] Not sampling span because HTTP method is '${method}' for ${spanName}`); - - return { - ...wrapSamplingDecision({ - decision: SamplingDecision.NOT_RECORD, - context, - spanAttributes, - sampleRand, - downstreamTraceSampleRate: 0, // we don't want to sample anything in the downstream trace either - }), - }; - } - - if ( - !sampled && - // We check for `parentSampled === undefined` because we only want to record client reports for spans that are trace roots (ie. when there was incoming trace) - parentSampled === undefined - ) { - DEBUG_BUILD && logger.log('[Tracing] Discarding root span because its trace was not chosen to be sampled.'); - this._client.recordDroppedEvent('sample_rate', 'transaction'); - } - - return { - ...wrapSamplingDecision({ - decision: sampled ? SamplingDecision.RECORD_AND_SAMPLED : SamplingDecision.NOT_RECORD, - context, - spanAttributes, - sampleRand, - downstreamTraceSampleRate: localSampleRateWasApplied ? sampleRate : undefined, - }), - attributes: { - // We set the sample rate on the span when a local sample rate was applied to better understand how traces were sampled in Sentry - [SEMANTIC_ATTRIBUTE_SENTRY_SAMPLE_RATE]: localSampleRateWasApplied ? sampleRate : undefined, - }, - }; - } else { - return { - ...wrapSamplingDecision({ - decision: parentSampled ? SamplingDecision.RECORD_AND_SAMPLED : SamplingDecision.NOT_RECORD, - context, - spanAttributes, - }), - }; + downstreamTraceSampleRate: 0, // we don't want to sample anything in the downstream trace either + }); + } + + if ( + !sampled && + // We check for `parentSampled === undefined` because we only want to record client reports for spans that are trace roots (ie. when there was incoming trace) + parentSampled === undefined + ) { + DEBUG_BUILD && logger.log('[Tracing] Discarding root span because its trace was not chosen to be sampled.'); + this._client.recordDroppedEvent('sample_rate', 'transaction'); } + + return { + ...wrapSamplingDecision({ + decision: sampled ? SamplingDecision.RECORD_AND_SAMPLED : SamplingDecision.NOT_RECORD, + context, + spanAttributes, + sampleRand, + downstreamTraceSampleRate: localSampleRateWasApplied ? sampleRate : undefined, + }), + attributes: { + // We set the sample rate on the span when a local sample rate was applied to better understand how traces were sampled in Sentry + [SEMANTIC_ATTRIBUTE_SENTRY_SAMPLE_RATE]: localSampleRateWasApplied ? sampleRate : undefined, + }, + }; } /** Returns the sampler name or short description with the configuration. */ From d430d967b01508751eed913693cb56a2375a8125 Mon Sep 17 00:00:00 2001 From: Abhijeet Prasad Date: Thu, 27 Mar 2025 11:22:03 -0400 Subject: [PATCH 28/28] meta(changelog): Update changelog for 9.10.0 --- CHANGELOG.md | 83 +++++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 82 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 7adb951938a1..38a12489d36e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,6 +10,87 @@ - "You miss 100 percent of the chances you don't take. — Wayne Gretzky" — Michael Scott +## 9.10.0 + +### Important Changes + +- **feat: Add support for logs** + + - feat(node): Add logging public APIs to Node SDKs ([#15764](https://github.com/getsentry/sentry-javascript/pull/15764)) + - feat(core): Add support for `beforeSendLog` ([#15814](https://github.com/getsentry/sentry-javascript/pull/15814)) + - feat(core): Add support for parameterizing logs ([#15812](https://github.com/getsentry/sentry-javascript/pull/15812)) + - fix: Remove critical log severity level ([#15824](https://github.com/getsentry/sentry-javascript/pull/15824)) + + All JavaScript SDKs other than `@sentry/cloudflare` and `@sentry/deno` now support sending logs via dedicated methods as part of Sentry's [upcoming logging product](https://github.com/getsentry/sentry/discussions/86804). + + Logging is gated by an experimental option, `_experiments.enableLogs`. + + ```js + Sentry.init({ + dsn: 'PUBLIC_DSN', + // `enableLogs` must be set to true to use the logging features + _experiments: { enableLogs: true }, + }); + + const { trace, debug, info, warn, error, fatal, fmt } = Sentry.logger; + + trace('Starting database connection', { database: 'users' }); + debug('Cache miss for user', { userId: 123 }); + error('Failed to process payment', { orderId: 'order_123', amount: 99.99 }); + fatal('Database connection pool exhausted', { database: 'users', activeConnections: 100 }); + + // Structured logging via the `fmt` helper function. When you use `fmt`, the string template and parameters are sent separately so they can be queried independently in Sentry. + + info(fmt(`Updated profile for user ${userId}`)); + warn(fmt(`Rate limit approaching for endpoint ${endpoint}. Requests: ${requests}, Limit: ${limit}`)); + ``` + + With server-side SDKs like `@sentry/node`, `@sentry/bun` or server-side of `@sentry/nextjs` or `@sentry/sveltekit`, you can do structured logging without needing the `fmt` helper function. + + ```js + const { info, warn } = Sentry.logger; + + info('User %s logged in successfully', [123]); + warn('Failed to load user %s data', [123], { errorCode: 404 }); + ``` + + To filter logs, or update them before they are sent to Sentry, you can use the `_experiments.beforeSendLog` option. + +- **feat(browser): Add `diagnoseSdkConnectivity()` function to programmatically detect possible connectivity issues ([#15821](https://github.com/getsentry/sentry-javascript/pull/15821))** + + The `diagnoseSdkConnectivity()` function can be used to programmatically detect possible connectivity issues with the Sentry SDK. + + ```js + const result = await Sentry.diagnoseSdkConnectivity(); + ``` + + The result will be an object with the following properties: + + - `"no-client-active"`: There was no active client when the function was called. This possibly means that the SDK was not initialized yet. + - `"sentry-unreachable"`: The Sentry SaaS servers were not reachable. This likely means that there is an ad blocker active on the page or that there are other connection issues. + - `undefined`: The SDK is working as expected. + +- **SDK Tracing Performance Improvements for Node SDKs** + + - feat: Stop using `dropUndefinedKeys` ([#15796](https://github.com/getsentry/sentry-javascript/pull/15796)) + - feat(node): Only add span listeners for instrumentation when used ([#15802](https://github.com/getsentry/sentry-javascript/pull/15802)) + - ref: Avoid `dropUndefinedKeys` for `spanToJSON` calls ([#15792](https://github.com/getsentry/sentry-javascript/pull/15792)) + - ref: Avoid using `SentryError` for PromiseBuffer control flow ([#15822](https://github.com/getsentry/sentry-javascript/pull/15822)) + - ref: Stop using `dropUndefinedKeys` in SpanExporter ([#15794](https://github.com/getsentry/sentry-javascript/pull/15794)) + - ref(core): Avoid using `SentryError` for event processing control flow ([#15823](https://github.com/getsentry/sentry-javascript/pull/15823)) + - ref(node): Avoid `dropUndefinedKeys` in Node SDK init ([#15797](https://github.com/getsentry/sentry-javascript/pull/15797)) + - ref(opentelemetry): Avoid sampling work for non-root spans ([#15820](https://github.com/getsentry/sentry-javascript/pull/15820)) + + We've been hard at work making performance improvements to the Sentry Node SDKs (`@sentry/node`, `@sentry/aws-serverless`, `@sentry/nestjs`, etc.). We've seen that upgrading from `9.7.0` to `9.10.0` leads to 30-40% improvement in request latency for HTTP web-server applications that use tracing with high sample rates. Non web-server applications and non-tracing applications will see smaller improvements. + +### Other Changes + +- chore(deps): Bump `rrweb` to `2.35.0` ([#15825](https://github.com/getsentry/sentry-javascript/pull/15825)) +- deps: Bump bundler plugins to `3.2.3` ([#15829](https://github.com/getsentry/sentry-javascript/pull/15829)) +- feat: Always truncate stored breadcrumb messages to 2kb ([#15819](https://github.com/getsentry/sentry-javascript/pull/15819)) +- feat(nextjs): Disable server webpack-handling for static builds ([#15751](https://github.com/getsentry/sentry-javascript/pull/15751)) +- fix(nuxt): Don't override Nuxt options if undefined ([#15795](https://github.com/getsentry/sentry-javascript/pull/15795)) + ## 9.9.0 ### Important Changes @@ -42,7 +123,7 @@ - **feat(browser): Add `logger.X` methods to browser SDK ([#15763](https://github.com/getsentry/sentry-javascript/pull/15763))** - For Sentry's [upcoming logging product](https://github.com/getsentry/sentry/discussions/86804), the SDK now supports sending logs via dedicated + For Sentry's [upcoming logging product](https://github.com/getsentry/sentry/discussions/86804), the SDK now supports sending logs via dedicated methods. ```js Sentry.init({