diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 0b254d24022b..a5523a861eee 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -179,7 +179,7 @@ jobs: key: ${{ steps.compute_lockfile_hash.outputs.hash }} - name: Install dependencies - if: steps.cache_dependencies.outputs.cache_hit != 'true' + if: steps.cache_dependencies.outputs.cache-hit != 'true' run: yarn install --ignore-engines --frozen-lockfile outputs: dependency_cache_key: ${{ steps.compute_lockfile_hash.outputs.hash }} @@ -661,7 +661,7 @@ jobs: needs: [job_get_metadata, job_build] if: needs.job_get_metadata.outputs.changed_node == 'true' || github.event_name != 'pull_request' runs-on: ubuntu-20.04 - timeout-minutes: 10 + timeout-minutes: 15 strategy: fail-fast: false matrix: @@ -738,11 +738,11 @@ jobs: github.actor != 'dependabot[bot]' needs: [job_get_metadata, job_build] runs-on: ubuntu-20.04 - timeout-minutes: 20 + timeout-minutes: 30 strategy: fail-fast: false matrix: - shard: [1, 2, 3] + shard: [1, 2, 3, 4] steps: - name: Check out current commit (${{ needs.job_get_metadata.outputs.commit_label }}) @@ -773,7 +773,7 @@ jobs: E2E_TEST_SENTRY_ORG_SLUG: 'sentry-javascript-sdks' E2E_TEST_SENTRY_TEST_PROJECT: 'sentry-javascript-e2e-tests' E2E_TEST_SHARD: ${{ matrix.shard }} - E2E_TEST_SHARD_AMOUNT: 3 + E2E_TEST_SHARD_AMOUNT: 4 run: | cd packages/e2e-tests yarn test:e2e diff --git a/.gitignore b/.gitignore index 8574a81de0a4..777b23658572 100644 --- a/.gitignore +++ b/.gitignore @@ -10,7 +10,6 @@ build/ dist/ coverage/ scratch/ -*.d.ts *.js.map *.pyc *.tsbuildinfo diff --git a/CHANGELOG.md b/CHANGELOG.md index f2b1e7168f88..8d53c9fa7fc4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,64 @@ - "You miss 100 percent of the chances you don't take. — Wayne Gretzky" — Michael Scott +## 7.58.0 + +### Important Changes + +- **Performance Monitoring not required for Distributed Tracing** + +This release adds support for [distributed tracing](https://docs.sentry.io/platforms/javascript/usage/distributed-tracing/) without requiring performance monitoring to be active on the JavaScript SDKs (browser and node). This means even if there is no sampled transaction/span, the SDK will still propagate traces to downstream services. Distributed Tracing can be configured with the `tracePropagationTargets` option, which controls what requests to attach the `sentry-trace` and `baggage` HTTP headers to (which is what propagates tracing information). + +```js +Sentry.init({ + tracePropagationTargets: ["third-party-site.com", /^https:\/\/yourserver\.io\/api/], +}); +``` + +- feat(tracing): Add tracing without performance to browser and client Sveltekit (#8458) +- feat(node): Add tracing without performance to Node http integration (#8450) +- feat(node): Add tracing without performance to Node Undici (#8449) +- feat(node): Populate propagation context using env variables (#8422) + +- **feat(core): Support `AggregateErrors` in `LinkedErrors` integration (#8463)** + +This release adds support for [`AggregateErrors`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/AggregateError). AggregateErrors are considered as Exception Groups by Sentry, and will be visualized and grouped differently. See the [Exception Groups Changelog Post](https://changelog.getsentry.com/announcements/exception-groups-now-supported-for-python-and-net) for more details. + +Exception Group support requires Self-Hosted Sentry [version 23.5.1](https://github.com/getsentry/self-hosted/releases/tag/23.5.1) or newer. + +- **feat(replay): Add a new option `networkDetailDenyUrls` (#8439)** + +This release adds a new option `networkDetailDenyUrls` to the `Replay` integration. This option allows you to specify a list of URLs that should not be captured by the `Replay` integration, which can be used alongside the existing `networkDetailAllowUrls` for finely grained control of which URLs should have network details captured. + +```js +Sentry.init({ + integrations: [ + new Sentry.Integrations.Replay({ + networkDetailDenyUrls: [/^http:\/\/example.com\/test$/], + }), + ], +}); +``` + +### Other Changes + +- feat(core): Add helpers to get module metadata from injected code (#8438) +- feat(core): Add sampling decision to trace envelope header (#8483) +- feat(node): Add trace context to checkin (#8503) +- feat(node): Export `getModule` for Electron SDK (#8488) +- feat(types): Allow `user.id` to be a number (#8330) +- fix(browser): Set anonymous `crossorigin` attribute on report dialog (#8424) +- fix(nextjs): Ignore `tunnelRoute` when doing static exports (#8471) +- fix(nextjs): Use `basePath` option for `tunnelRoute` (#8454) +- fix(node): Apply source context to linked errors even when it is uncached (#8453) +- fix(node): report errorMiddleware errors as unhandled (#8048) +- fix(react): Add support for `basename` option of `createBrowserRouter` (#8457) +- fix(remix): Add explicit `@sentry/node` exports. (#8509) +- fix(remix): Don't inject trace/baggage to `redirect` and `catch` responses (#8467) +- fix(replay): Adjust slow/multi click handling (#8380) + +Work in this release contributed by @mrdulin, @donaldxdonald & @ziyad-elabid-nw. Thank you for your contributions! + ## 7.57.0 ### Important Changes diff --git a/LICENSE b/LICENSE index 689ce9d5572b..293314012679 100644 --- a/LICENSE +++ b/LICENSE @@ -1,14 +1,21 @@ -Copyright (c) 2018 Sentry (https://sentry.io) and individual contributors. All rights reserved. +MIT License -Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated -documentation files (the "Software"), to deal in the Software without restriction, including without limitation the -rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit -persons to whom the Software is furnished to do so, subject to the following conditions: +Copyright (c) 2022 Functional Software, Inc. dba Sentry -The above copyright notice and this permission notice shall be included in all copies or substantial portions of the -Software. +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +of the Software, and to permit persons to whom the Software is furnished to do +so, subject to the following conditions: -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE -WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR -COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR -OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/lerna.json b/lerna.json index 90686ec37d8a..71aa98569aca 100644 --- a/lerna.json +++ b/lerna.json @@ -1,6 +1,5 @@ { "$schema": "node_modules/lerna/schemas/lerna-schema.json", "version": "7.57.0", - "npmClient": "yarn", - "useWorkspaces": true + "npmClient": "yarn" } diff --git a/nx.json b/nx.json index 2341174dd956..c25aeaa1f0e7 100644 --- a/nx.json +++ b/nx.json @@ -3,65 +3,35 @@ "default": { "runner": "nx/tasks-runners/default", "options": { - "cacheableOperations": [ - "build:bundle", - "build:transpile", - "build:types", - "lint:eslint", - "test:unit" - ], + "cacheableOperations": ["build:bundle", "build:transpile", "build:types", "lint:eslint", "test:unit"], "cacheDirectory": ".nxcache" } } }, "namedInputs": { "default": ["{projectRoot}/**/*", "sharedGlobals"], - "sharedGlobals": [ - "{workspaceRoot}/*.js", - "{workspaceRoot}/*.json" - ], - "production": [ - "default", - "!{projectRoot}/test/**/*", - "!{projectRoot}/**/*.md" - ] + "sharedGlobals": ["{workspaceRoot}/*.js", "{workspaceRoot}/*.json"], + "production": ["default", "!{projectRoot}/test/**/*", "!{projectRoot}/**/*.md"] }, "targetDefaults": { "build:bundle": { "inputs": ["production", "^production"], - "dependsOn": [ - "build:transpile" - ], - "outputs": [ - "{projectRoot}/build/bundles" - ] + "dependsOn": ["build:transpile"], + "outputs": ["{projectRoot}/build/bundles"] }, "build:tarball": { "inputs": ["production", "^production"], - "dependsOn": [ - "build:transpile", - "build:types" - ], + "dependsOn": ["build:transpile", "build:types"], "outputs": [] }, "build:transpile": { "inputs": ["production", "^production"], - "dependsOn": [ - "^build:transpile:uncached", - "^build:transpile", - "build:transpile:uncached" - ], - "outputs": [ - "{projectRoot}/build/npm", - "{projectRoot}/build/esm", - "{projectRoot}/build/cjs" - ] + "dependsOn": ["^build:transpile:uncached", "^build:transpile", "build:transpile:uncached"], + "outputs": ["{projectRoot}/build/npm", "{projectRoot}/build/esm", "{projectRoot}/build/cjs"] }, "build:types": { "inputs": ["production", "^production"], - "dependsOn": [ - "^build:types" - ], + "dependsOn": ["^build:types"], "outputs": [ "{projectRoot}/build/types", "{projectRoot}/build/types-ts3.8", @@ -78,5 +48,6 @@ "inputs": ["default"], "outputs": ["{projectRoot}/coverage"] } - } + }, + "$schema": "./node_modules/nx/schemas/nx-schema.json" } diff --git a/package.json b/package.json index feb5be7839d0..0d800adc7824 100644 --- a/package.json +++ b/package.json @@ -25,9 +25,9 @@ "lint:eslint": "lerna run lint:eslint", "validate:es5": "lerna run validate:es5", "postpublish": "lerna run --stream --concurrency 1 postpublish", - "test": "lerna run --ignore @sentry-internal/* test", - "test:unit": "lerna run --ignore @sentry-internal/* test:unit", - "test-ci-browser": "lerna run test --ignore \"@sentry/{node,opentelemetry-node,serverless,nextjs,remix,gatsby,sveltekit}\" --ignore @sentry-internal/*", + "test": "lerna run --ignore \"@sentry-internal/{browser-integration-tests,e2e-tests,integration-shims,node-integration-tests,overhead-metrics}\" test", + "test:unit": "lerna run --ignore \"@sentry-internal/{browser-integration-tests,e2e-tests,integration-shims,node-integration-tests,overhead-metrics}\" test:unit", + "test-ci-browser": "lerna run test --ignore \"@sentry/{node,opentelemetry-node,serverless,nextjs,remix,gatsby,sveltekit}\" --ignore \"@sentry-internal/{browser-integration-tests,e2e-tests,integration-shims,node-integration-tests,overhead-metrics}\"", "test-ci-node": "ts-node ./scripts/node-unit-tests.ts", "test:update-snapshots": "lerna run test:update-snapshots", "yalc:publish": "lerna run yalc:publish" @@ -98,7 +98,7 @@ "jsdom": "^19.0.0", "karma-browserstack-launcher": "^1.5.1", "karma-firefox-launcher": "^1.1.0", - "lerna": "6.5.0-alpha.2", + "lerna": "7.1.1", "madge": "4.0.2", "mocha": "^6.1.4", "nodemon": "^2.0.16", diff --git a/packages/angular-ivy/README.md b/packages/angular-ivy/README.md index f70931554059..9546888170e6 100644 --- a/packages/angular-ivy/README.md +++ b/packages/angular-ivy/README.md @@ -175,7 +175,7 @@ import { TraceModule } from '@sentry/angular-ivy'; export class AppModule {} ``` -Then inside your components template (keep in mind that directive name attribute is required): +Then, inside your component's template (keep in mind that the directive's name attribute is required): ```html diff --git a/packages/angular/README.md b/packages/angular/README.md index 9ec22b82313b..aa18724839a4 100644 --- a/packages/angular/README.md +++ b/packages/angular/README.md @@ -21,7 +21,7 @@ If you're using Angular 12 or newer, we recommend using `@sentry/angular-ivy` for native support with Angular's rendering engine Ivy. -This SDK stilll officially supports Angular 10-15. If you are using an older version of Angular and experience problems with the Angular SDK, we recommend downgrading the SDK to version 6.x. +This SDK still officially supports Angular 10-15. If you are using an older version of Angular and experience problems with the Angular SDK, we recommend downgrading the SDK to version 6.x. ## General @@ -175,7 +175,7 @@ import { TraceModule } from '@sentry/angular'; export class AppModule {} ``` -Then inside your components template (keep in mind that directive name attribute is required): +Then, inside your component's template (keep in mind that the directive's name attribute is required): ```html diff --git a/packages/angular/src/errorhandler.ts b/packages/angular/src/errorhandler.ts index 6940e0cc8cc1..d9b4db07ff9d 100644 --- a/packages/angular/src/errorhandler.ts +++ b/packages/angular/src/errorhandler.ts @@ -69,7 +69,6 @@ function isErrorOrErrorLikeObject(value: unknown): value is Error { const candidate = value as ErrorCandidate; return ( - isString(candidate.name) && isString(candidate.name) && isString(candidate.message) && (undefined === candidate.stack || isString(candidate.stack)) diff --git a/packages/angular/tsconfig.json b/packages/angular/tsconfig.json index ed785543d690..2f88305e15e5 100644 --- a/packages/angular/tsconfig.json +++ b/packages/angular/tsconfig.json @@ -5,6 +5,10 @@ "compilerOptions": { // package-specific options - "experimentalDecorators": true + "experimentalDecorators": true, + // Avoid loading all @types/... packages as they may not be TS 4.0 compatible + // We have no types packages here we directly depend on, + // if we ever add some we need to allowlist them here + "types": [] } } diff --git a/packages/browser-integration-tests/suites/replay/captureReplay/test.ts b/packages/browser-integration-tests/suites/replay/captureReplay/test.ts index 72cbc47efe99..421e725bb3e4 100644 --- a/packages/browser-integration-tests/suites/replay/captureReplay/test.ts +++ b/packages/browser-integration-tests/suites/replay/captureReplay/test.ts @@ -56,7 +56,6 @@ sentryTest('should capture replays (@sentry/browser export)', async ({ getLocalT version: SDK_VERSION, name: 'sentry.javascript.browser', }, - sdkProcessingMetadata: {}, request: { url: expect.stringContaining('/dist/index.html'), headers: { @@ -94,7 +93,6 @@ sentryTest('should capture replays (@sentry/browser export)', async ({ getLocalT version: SDK_VERSION, name: 'sentry.javascript.browser', }, - sdkProcessingMetadata: {}, request: { url: expect.stringContaining('/dist/index.html'), headers: { diff --git a/packages/browser-integration-tests/suites/replay/captureReplayFromReplayPackage/test.ts b/packages/browser-integration-tests/suites/replay/captureReplayFromReplayPackage/test.ts index 6caf1e4ea57c..c42bfc692018 100644 --- a/packages/browser-integration-tests/suites/replay/captureReplayFromReplayPackage/test.ts +++ b/packages/browser-integration-tests/suites/replay/captureReplayFromReplayPackage/test.ts @@ -56,7 +56,6 @@ sentryTest('should capture replays (@sentry/replay export)', async ({ getLocalTe version: SDK_VERSION, name: 'sentry.javascript.browser', }, - sdkProcessingMetadata: {}, request: { url: expect.stringContaining('/dist/index.html'), headers: { @@ -94,7 +93,6 @@ sentryTest('should capture replays (@sentry/replay export)', async ({ getLocalTe version: SDK_VERSION, name: 'sentry.javascript.browser', }, - sdkProcessingMetadata: {}, request: { url: expect.stringContaining('/dist/index.html'), headers: { diff --git a/packages/browser-integration-tests/suites/replay/dsc/test.ts b/packages/browser-integration-tests/suites/replay/dsc/test.ts index 54de1ee7db81..ffd2cf1877da 100644 --- a/packages/browser-integration-tests/suites/replay/dsc/test.ts +++ b/packages/browser-integration-tests/suites/replay/dsc/test.ts @@ -49,6 +49,7 @@ sentryTest( trace_id: expect.any(String), public_key: 'public', replay_id: replay.session?.id, + sampled: 'true', }); }, ); @@ -93,6 +94,7 @@ sentryTest( sample_rate: '1', trace_id: expect.any(String), public_key: 'public', + sampled: 'true', }); }, ); @@ -152,6 +154,7 @@ sentryTest( trace_id: expect.any(String), public_key: 'public', replay_id: replay.session?.id, + sampled: 'true', }); }, ); @@ -199,6 +202,7 @@ sentryTest( sample_rate: '1', trace_id: expect.any(String), public_key: 'public', + sampled: 'true', }); }, ); diff --git a/packages/browser-integration-tests/suites/replay/slowClick/multiClick/test.ts b/packages/browser-integration-tests/suites/replay/slowClick/multiClick/test.ts index 2d84eeb29ac3..82bc436f714e 100644 --- a/packages/browser-integration-tests/suites/replay/slowClick/multiClick/test.ts +++ b/packages/browser-integration-tests/suites/replay/slowClick/multiClick/test.ts @@ -1,7 +1,12 @@ import { expect } from '@playwright/test'; import { sentryTest } from '../../../../utils/fixtures'; -import { getCustomRecordingEvents, shouldSkipReplayTest, waitForReplayRequest } from '../../../../utils/replayHelpers'; +import { + getCustomRecordingEvents, + shouldSkipReplayTest, + waitForReplayRequest, + waitForReplayRequests, +} from '../../../../utils/replayHelpers'; sentryTest('captures multi click when not detecting slow click', async ({ getLocalTestUrl, page }) => { if (shouldSkipReplayTest()) { @@ -58,3 +63,97 @@ sentryTest('captures multi click when not detecting slow click', async ({ getLoc }, ]); }); + +sentryTest('captures multiple multi clicks', async ({ getLocalTestUrl, page, forceFlushReplay }) => { + if (shouldSkipReplayTest()) { + sentryTest.skip(); + } + + const reqPromise0 = waitForReplayRequest(page, 0); + + await page.route('https://dsn.ingest.sentry.io/**/*', route => { + return route.fulfill({ + status: 200, + contentType: 'application/json', + body: JSON.stringify({ id: 'test-id' }), + }); + }); + + const url = await getLocalTestUrl({ testDir: __dirname }); + + await page.goto(url); + await reqPromise0; + + let multiClickBreadcrumbCount = 0; + + const reqsPromise = waitForReplayRequests(page, (_event, res) => { + const { breadcrumbs } = getCustomRecordingEvents(res); + const count = breadcrumbs.filter(breadcrumb => breadcrumb.category === 'ui.multiClick').length; + + multiClickBreadcrumbCount += count; + + if (multiClickBreadcrumbCount === 2) { + return true; + } + + return false; + }); + + await page.click('#mutationButtonImmediately', { clickCount: 4 }); + await forceFlushReplay(); + + // Ensure we waited at least 1s, which is the threshold to create a new ui.click breadcrumb + await new Promise(resolve => setTimeout(resolve, 1001)); + + await page.click('#mutationButtonImmediately', { clickCount: 2 }); + await forceFlushReplay(); + + const responses = await reqsPromise; + + const slowClickBreadcrumbs = responses + .flatMap(res => getCustomRecordingEvents(res).breadcrumbs) + .filter(breadcrumb => breadcrumb.category === 'ui.multiClick'); + + expect(slowClickBreadcrumbs).toEqual([ + { + category: 'ui.multiClick', + type: 'default', + data: { + clickCount: 6, + metric: true, + node: { + attributes: { + id: 'mutationButtonImmediately', + }, + id: expect.any(Number), + tagName: 'button', + textContent: '******* ******** ***********', + }, + nodeId: expect.any(Number), + url: 'http://sentry-test.io/index.html', + }, + message: 'body > button#mutationButtonImmediately', + timestamp: expect.any(Number), + }, + { + category: 'ui.multiClick', + type: 'default', + data: { + clickCount: 2, + metric: true, + node: { + attributes: { + id: 'mutationButtonImmediately', + }, + id: expect.any(Number), + tagName: 'button', + textContent: '******* ******** ***********', + }, + nodeId: expect.any(Number), + url: 'http://sentry-test.io/index.html', + }, + message: 'body > button#mutationButtonImmediately', + timestamp: expect.any(Number), + }, + ]); +}); diff --git a/packages/browser-integration-tests/suites/tracing/envelope-header-transaction-name/test.ts b/packages/browser-integration-tests/suites/tracing/envelope-header-transaction-name/test.ts index bc94930e0be5..b244768f7f82 100644 --- a/packages/browser-integration-tests/suites/tracing/envelope-header-transaction-name/test.ts +++ b/packages/browser-integration-tests/suites/tracing/envelope-header-transaction-name/test.ts @@ -27,6 +27,7 @@ sentryTest( transaction: expect.stringContaining('/index.html'), trace_id: expect.any(String), public_key: 'public', + sampled: 'true', }); }, ); diff --git a/packages/browser-integration-tests/suites/tracing/envelope-header/test.ts b/packages/browser-integration-tests/suites/tracing/envelope-header/test.ts index b8df56e72f7c..71adc007385c 100644 --- a/packages/browser-integration-tests/suites/tracing/envelope-header/test.ts +++ b/packages/browser-integration-tests/suites/tracing/envelope-header/test.ts @@ -30,6 +30,7 @@ sentryTest( sample_rate: '1', trace_id: expect.any(String), public_key: 'public', + sampled: 'true', }); }, ); diff --git a/packages/browser-integration-tests/utils/replayEventTemplates.ts b/packages/browser-integration-tests/utils/replayEventTemplates.ts index d88fbd1bf0e5..46208b39af00 100644 --- a/packages/browser-integration-tests/utils/replayEventTemplates.ts +++ b/packages/browser-integration-tests/utils/replayEventTemplates.ts @@ -30,7 +30,6 @@ const DEFAULT_REPLAY_EVENT = { version: SDK_VERSION, name: 'sentry.javascript.browser', }, - sdkProcessingMetadata: {}, request: { url: expect.stringContaining('/dist/index.html'), headers: { diff --git a/packages/browser-integration-tests/utils/replayHelpers.ts b/packages/browser-integration-tests/utils/replayHelpers.ts index bf070b395cf6..cb05baffb62b 100644 --- a/packages/browser-integration-tests/utils/replayHelpers.ts +++ b/packages/browser-integration-tests/utils/replayHelpers.ts @@ -101,6 +101,46 @@ export function waitForReplayRequest( ); } +/** + * Wait until a callback returns true, collecting all replay responses along the way. + * This can be useful when you don't know if stuff will be in one or multiple replay requests. + */ +export function waitForReplayRequests( + page: Page, + callback: (event: ReplayEvent, res: Response) => boolean, + timeout?: number, +): Promise { + const responses: Response[] = []; + + return new Promise(resolve => { + void page.waitForResponse( + res => { + const req = res.request(); + + const event = getReplayEventFromRequest(req); + + if (!event) { + return false; + } + + responses.push(res); + + try { + if (callback(event, res)) { + resolve(responses); + return true; + } + + return false; + } catch { + return false; + } + }, + timeout ? { timeout } : undefined, + ); + }); +} + export function isReplayEvent(event: Event): event is ReplayEvent { return event.type === 'replay_event'; } diff --git a/packages/browser/src/integrations/linkederrors.ts b/packages/browser/src/integrations/linkederrors.ts index bb53d0210f96..08581fecb9b3 100644 --- a/packages/browser/src/integrations/linkederrors.ts +++ b/packages/browser/src/integrations/linkederrors.ts @@ -1,8 +1,6 @@ -import { addGlobalEventProcessor, getCurrentHub } from '@sentry/core'; -import type { Event, EventHint, Exception, ExtendedError, Integration, StackParser } from '@sentry/types'; -import { isInstanceOf } from '@sentry/utils'; +import type { Event, EventHint, EventProcessor, Hub, Integration } from '@sentry/types'; +import { applyAggregateErrorsToEvent } from '@sentry/utils'; -import type { BrowserClient } from '../client'; import { exceptionFromError } from '../eventbuilder'; const DEFAULT_KEY = 'cause'; @@ -46,49 +44,26 @@ export class LinkedErrors implements Integration { /** * @inheritDoc */ - public setupOnce(): void { - const client = getCurrentHub().getClient(); - if (!client) { - return; - } + public setupOnce(addGlobalEventProcessor: (callback: EventProcessor) => void, getCurrentHub: () => Hub): void { addGlobalEventProcessor((event: Event, hint?: EventHint) => { - const self = getCurrentHub().getIntegration(LinkedErrors); - return self ? _handler(client.getOptions().stackParser, self._key, self._limit, event, hint) : event; - }); - } -} + const hub = getCurrentHub(); + const client = hub.getClient(); + const self = hub.getIntegration(LinkedErrors); -/** - * @inheritDoc - */ -export function _handler( - parser: StackParser, - key: string, - limit: number, - event: Event, - hint?: EventHint, -): Event | null { - if (!event.exception || !event.exception.values || !hint || !isInstanceOf(hint.originalException, Error)) { - return event; - } - const linkedErrors = _walkErrorTree(parser, limit, hint.originalException as ExtendedError, key); - event.exception.values = [...linkedErrors, ...event.exception.values]; - return event; -} + if (!client || !self) { + return event; + } + + applyAggregateErrorsToEvent( + exceptionFromError, + client.getOptions().stackParser, + self._key, + self._limit, + event, + hint, + ); -/** - * JSDOC - */ -export function _walkErrorTree( - parser: StackParser, - limit: number, - error: ExtendedError, - key: string, - stack: Exception[] = [], -): Exception[] { - if (!isInstanceOf(error[key], Error) || stack.length + 1 >= limit) { - return stack; + return event; + }); } - const exception = exceptionFromError(parser, error[key]); - return _walkErrorTree(parser, limit, error[key], key, [exception, ...stack]); } diff --git a/packages/browser/src/sdk.ts b/packages/browser/src/sdk.ts index d58888260e3e..17fbfc159549 100644 --- a/packages/browser/src/sdk.ts +++ b/packages/browser/src/sdk.ts @@ -164,6 +164,7 @@ export function showReportDialog(options: ReportDialogOptions = {}, hub: Hub = g const script = WINDOW.document.createElement('script'); script.async = true; + script.crossOrigin = 'anonymous'; script.src = getReportDialogEndpoint(dsn, options); if (options.onLoad) { diff --git a/packages/browser/test/unit/integrations/linkederrors.test.ts b/packages/browser/test/unit/integrations/linkederrors.test.ts deleted file mode 100644 index 40738ca2af09..000000000000 --- a/packages/browser/test/unit/integrations/linkederrors.test.ts +++ /dev/null @@ -1,121 +0,0 @@ -import type { Event as SentryEvent, Exception, ExtendedError } from '@sentry/types'; - -import { BrowserClient } from '../../../src/client'; -import * as LinkedErrorsModule from '../../../src/integrations/linkederrors'; -import { defaultStackParser as parser } from '../../../src/stack-parsers'; -import { getDefaultBrowserClientOptions } from '../helper/browser-client-options'; - -type EventWithException = SentryEvent & { - exception: { - values: Exception[]; - }; -}; - -describe('LinkedErrors', () => { - describe('handler', () => { - it('should bail out if event does not contain exception', () => { - const event = { - message: 'foo', - }; - const result = LinkedErrorsModule._handler(parser, 'cause', 5, event); - expect(result).toEqual(event); - }); - - it('should bail out if event contains exception, but no hint', () => { - const event = { - exception: { - values: [], - }, - message: 'foo', - }; - const result = LinkedErrorsModule._handler(parser, 'cause', 5, event); - expect(result).toEqual(event); - }); - - it('should recursively walk error to find linked exceptions and assign them to the event', async () => { - const three: ExtendedError = new SyntaxError('three'); - - const two: ExtendedError = new TypeError('two'); - two.cause = three; - - const one: ExtendedError = new Error('one'); - one.cause = two; - - const originalException = one; - const options = getDefaultBrowserClientOptions({ stackParser: parser }); - const client = new BrowserClient(options); - return client.eventFromException(originalException).then(event => { - const result = LinkedErrorsModule._handler(parser, 'cause', 5, event, { - originalException, - }) as EventWithException; - - // It shouldn't include root exception, as it's already processed in the event by the main error handler - expect(result.exception.values.length).toBe(3); - expect(result.exception.values[0].type).toBe('SyntaxError'); - expect(result.exception.values[0].value).toBe('three'); - expect(result.exception.values[0].stacktrace).toHaveProperty('frames'); - expect(result.exception.values[1].type).toBe('TypeError'); - expect(result.exception.values[1].value).toBe('two'); - expect(result.exception.values[1].stacktrace).toHaveProperty('frames'); - expect(result.exception.values[2].type).toBe('Error'); - expect(result.exception.values[2].value).toBe('one'); - expect(result.exception.values[2].stacktrace).toHaveProperty('frames'); - }); - }); - - it('should allow to change walk key', async () => { - const three: ExtendedError = new SyntaxError('three'); - - const two: ExtendedError = new TypeError('two'); - two.reason = three; - - const one: ExtendedError = new Error('one'); - one.reason = two; - - const originalException = one; - const options = getDefaultBrowserClientOptions({ stackParser: parser }); - const client = new BrowserClient(options); - return client.eventFromException(originalException).then(event => { - const result = LinkedErrorsModule._handler(parser, 'reason', 5, event, { - originalException, - }) as EventWithException; - - expect(result.exception.values.length).toBe(3); - expect(result.exception.values[0].type).toBe('SyntaxError'); - expect(result.exception.values[0].value).toBe('three'); - expect(result.exception.values[0].stacktrace).toHaveProperty('frames'); - expect(result.exception.values[1].type).toBe('TypeError'); - expect(result.exception.values[1].value).toBe('two'); - expect(result.exception.values[1].stacktrace).toHaveProperty('frames'); - expect(result.exception.values[2].type).toBe('Error'); - expect(result.exception.values[2].value).toBe('one'); - expect(result.exception.values[2].stacktrace).toHaveProperty('frames'); - }); - }); - - it('should allow to change stack size limit', async () => { - const one: ExtendedError = new Error('one'); - const two: ExtendedError = new TypeError('two'); - const three: ExtendedError = new SyntaxError('three'); - one.cause = two; - two.cause = three; - - const options = getDefaultBrowserClientOptions({ stackParser: parser }); - const client = new BrowserClient(options); - const originalException = one; - return client.eventFromException(originalException).then(event => { - const result = LinkedErrorsModule._handler(parser, 'cause', 2, event, { - originalException, - }) as EventWithException; - - expect(result.exception.values.length).toBe(2); - expect(result.exception.values[0].type).toBe('TypeError'); - expect(result.exception.values[0].value).toBe('two'); - expect(result.exception.values[0].stacktrace).toHaveProperty('frames'); - expect(result.exception.values[1].type).toBe('Error'); - expect(result.exception.values[1].value).toBe('one'); - expect(result.exception.values[1].stacktrace).toHaveProperty('frames'); - }); - }); - }); -}); diff --git a/packages/core/src/baseclient.ts b/packages/core/src/baseclient.ts index 9a1312a131ac..52c4301f94ee 100644 --- a/packages/core/src/baseclient.ts +++ b/packages/core/src/baseclient.ts @@ -15,6 +15,7 @@ import type { Integration, IntegrationClass, Outcome, + PropagationContext, SdkMetadata, Session, SessionAggregates, @@ -46,6 +47,7 @@ import type { IntegrationIndex } from './integration'; import { setupIntegration, setupIntegrations } from './integration'; import type { Scope } from './scope'; import { updateSession } from './session'; +import { getDynamicSamplingContextFromClient } from './tracing/dynamicSamplingContext'; import { prepareEvent } from './utils/prepareEvent'; const ALREADY_SEEN_ERROR = "Not capturing exception because it's already been captured."; @@ -507,7 +509,36 @@ export abstract class BaseClient implements Client { if (!hint.integrations && integrations.length > 0) { hint.integrations = integrations; } - return prepareEvent(options, event, hint, scope); + return prepareEvent(options, event, hint, scope).then(evt => { + if (evt === null) { + return evt; + } + + // If a trace context is not set on the event, we use the propagationContext set on the event to + // generate a trace context. If the propagationContext does not have a dynamic sampling context, we + // also generate one for it. + const { propagationContext } = evt.sdkProcessingMetadata || {}; + const trace = evt.contexts && evt.contexts.trace; + if (!trace && propagationContext) { + const { traceId: trace_id, spanId, parentSpanId, dsc } = propagationContext as PropagationContext; + evt.contexts = { + trace: { + trace_id, + span_id: spanId, + parent_span_id: parentSpanId, + }, + ...evt.contexts, + }; + + const dynamicSamplingContext = dsc ? dsc : getDynamicSamplingContextFromClient(trace_id, this, scope); + + evt.sdkProcessingMetadata = { + dynamicSamplingContext, + ...evt.sdkProcessingMetadata, + }; + } + return evt; + }); } /** diff --git a/packages/core/src/checkin.ts b/packages/core/src/checkin.ts index 2417736bcdae..e7dd6c906f4c 100644 --- a/packages/core/src/checkin.ts +++ b/packages/core/src/checkin.ts @@ -1,26 +1,42 @@ -import type { CheckInEvelope, CheckInItem, DsnComponents, SdkMetadata, SerializedCheckIn } from '@sentry/types'; -import { createEnvelope, dsnToString } from '@sentry/utils'; +import type { + CheckInEvelope, + CheckInItem, + DsnComponents, + DynamicSamplingContext, + SdkMetadata, + SerializedCheckIn, +} from '@sentry/types'; +import { createEnvelope, dropUndefinedKeys, dsnToString } from '@sentry/utils'; /** * Create envelope from check in item. */ export function createCheckInEnvelope( checkIn: SerializedCheckIn, + dynamicSamplingContext?: Partial, metadata?: SdkMetadata, tunnel?: string, dsn?: DsnComponents, ): CheckInEvelope { const headers: CheckInEvelope[0] = { sent_at: new Date().toISOString(), - ...(metadata && - metadata.sdk && { - sdk: { - name: metadata.sdk.name, - version: metadata.sdk.version, - }, - }), - ...(!!tunnel && !!dsn && { dsn: dsnToString(dsn) }), }; + + if (metadata && metadata.sdk) { + headers.sdk = { + name: metadata.sdk.name, + version: metadata.sdk.version, + }; + } + + if (!!tunnel && !!dsn) { + headers.dsn = dsnToString(dsn); + } + + if (dynamicSamplingContext) { + headers.trace = dropUndefinedKeys(dynamicSamplingContext) as DynamicSamplingContext; + } + const item = createCheckInEnvelopeItem(checkIn); return createEnvelope(headers, [item]); } diff --git a/packages/core/src/exports.ts b/packages/core/src/exports.ts index ba283914fbbd..ad3d33013253 100644 --- a/packages/core/src/exports.ts +++ b/packages/core/src/exports.ts @@ -196,13 +196,15 @@ export function startTransaction( * to create a monitor automatically when sending a check in. */ export function captureCheckIn(checkIn: CheckIn, upsertMonitorConfig?: MonitorConfig): string { - const client = getCurrentHub().getClient(); + const hub = getCurrentHub(); + const scope = hub.getScope(); + const client = hub.getClient(); if (!client) { __DEBUG_BUILD__ && logger.warn('Cannot capture check-in. No client defined.'); } else if (!client.captureCheckIn) { __DEBUG_BUILD__ && logger.warn('Cannot capture check-in. Client does not support sending check-ins.'); } else { - return client.captureCheckIn(checkIn, upsertMonitorConfig); + return client.captureCheckIn(checkIn, upsertMonitorConfig, scope); } return uuid4(); diff --git a/packages/core/src/metadata.ts b/packages/core/src/metadata.ts new file mode 100644 index 000000000000..d1ebac6e90e5 --- /dev/null +++ b/packages/core/src/metadata.ts @@ -0,0 +1,97 @@ +import type { Event, StackParser } from '@sentry/types'; +import { GLOBAL_OBJ } from '@sentry/utils'; + +/** Keys are source filename/url, values are metadata objects. */ +// eslint-disable-next-line @typescript-eslint/no-explicit-any +const filenameMetadataMap = new Map(); +/** Set of stack strings that have already been parsed. */ +const parsedStacks = new Set(); + +function ensureMetadataStacksAreParsed(parser: StackParser): void { + if (!GLOBAL_OBJ._sentryModuleMetadata) { + return; + } + + for (const stack of Object.keys(GLOBAL_OBJ._sentryModuleMetadata)) { + const metadata = GLOBAL_OBJ._sentryModuleMetadata[stack]; + + if (parsedStacks.has(stack)) { + continue; + } + + // Ensure this stack doesn't get parsed again + parsedStacks.add(stack); + + const frames = parser(stack); + + // Go through the frames starting from the top of the stack and find the first one with a filename + for (const frame of frames.reverse()) { + if (frame.filename) { + // Save the metadata for this filename + filenameMetadataMap.set(frame.filename, metadata); + break; + } + } + } +} + +/** + * Retrieve metadata for a specific JavaScript file URL. + * + * Metadata is injected by the Sentry bundler plugins using the `_experiments.moduleMetadata` config option. + */ +// eslint-disable-next-line @typescript-eslint/no-explicit-any +export function getMetadataForUrl(parser: StackParser, filename: string): any | undefined { + ensureMetadataStacksAreParsed(parser); + return filenameMetadataMap.get(filename); +} + +/** + * Adds metadata to stack frames. + * + * Metadata is injected by the Sentry bundler plugins using the `_experiments.moduleMetadata` config option. + */ +export function addMetadataToStackFrames(parser: StackParser, event: Event): void { + try { + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion + event.exception!.values!.forEach(exception => { + if (!exception.stacktrace) { + return; + } + + for (const frame of exception.stacktrace.frames || []) { + if (!frame.filename) { + continue; + } + + const metadata = getMetadataForUrl(parser, frame.filename); + + if (metadata) { + frame.module_metadata = metadata; + } + } + }); + } catch (_) { + // To save bundle size we're just try catching here instead of checking for the existence of all the different objects. + } +} + +/** + * Strips metadata from stack frames. + */ +export function stripMetadataFromStackFrames(event: Event): void { + try { + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion + event.exception!.values!.forEach(exception => { + if (!exception.stacktrace) { + return; + } + + for (const frame of exception.stacktrace.frames || []) { + delete frame.module_metadata; + } + }); + } catch (_) { + // To save bundle size we're just try catching here instead of checking for the existence of all the different objects. + } +} diff --git a/packages/core/src/scope.ts b/packages/core/src/scope.ts index 40a1fa135417..b72defc56016 100644 --- a/packages/core/src/scope.ts +++ b/packages/core/src/scope.ts @@ -11,6 +11,7 @@ import type { Extra, Extras, Primitive, + PropagationContext, RequestSession, Scope as ScopeInterface, ScopeContext, @@ -29,6 +30,7 @@ import { isThenable, logger, SyncPromise, + uuid4, } from '@sentry/utils'; import { updateSession } from './session'; @@ -70,6 +72,9 @@ export class Scope implements ScopeInterface { /** Attachments */ protected _attachments: Attachment[]; + /** Propagation Context for distributed tracing */ + protected _propagationContext: PropagationContext; + /** * A place to stash data which is needed at some point in the SDK's event processing pipeline but which shouldn't get * sent to Sentry @@ -108,6 +113,7 @@ export class Scope implements ScopeInterface { this._extra = {}; this._contexts = {}; this._sdkProcessingMetadata = {}; + this._propagationContext = generatePropagationContext(); } /** @@ -131,6 +137,7 @@ export class Scope implements ScopeInterface { newScope._requestSession = scope._requestSession; newScope._attachments = [...scope._attachments]; newScope._sdkProcessingMetadata = { ...scope._sdkProcessingMetadata }; + newScope._propagationContext = { ...scope._propagationContext }; } return newScope; } @@ -347,6 +354,9 @@ export class Scope implements ScopeInterface { if (captureContext._requestSession) { this._requestSession = captureContext._requestSession; } + if (captureContext._propagationContext) { + this._propagationContext = captureContext._propagationContext; + } } else if (isPlainObject(captureContext)) { // eslint-disable-next-line no-param-reassign captureContext = captureContext as ScopeContext; @@ -365,6 +375,9 @@ export class Scope implements ScopeInterface { if (captureContext.requestSession) { this._requestSession = captureContext.requestSession; } + if (captureContext.propagationContext) { + this._propagationContext = captureContext.propagationContext; + } } return this; @@ -387,6 +400,7 @@ export class Scope implements ScopeInterface { this._session = undefined; this._notifyScopeListeners(); this._attachments = []; + this._propagationContext = generatePropagationContext(); return this; } @@ -500,7 +514,11 @@ export class Scope implements ScopeInterface { event.breadcrumbs = [...(event.breadcrumbs || []), ...this._breadcrumbs]; event.breadcrumbs = event.breadcrumbs.length > 0 ? event.breadcrumbs : undefined; - event.sdkProcessingMetadata = { ...event.sdkProcessingMetadata, ...this._sdkProcessingMetadata }; + event.sdkProcessingMetadata = { + ...event.sdkProcessingMetadata, + ...this._sdkProcessingMetadata, + propagationContext: this._propagationContext, + }; return this._notifyEventProcessors([...getGlobalEventProcessors(), ...this._eventProcessors], event, hint); } @@ -514,6 +532,21 @@ export class Scope implements ScopeInterface { return this; } + /** + * @inheritDoc + */ + public setPropagationContext(context: PropagationContext): this { + this._propagationContext = context; + return this; + } + + /** + * @inheritDoc + */ + public getPropagationContext(): PropagationContext { + return this._propagationContext; + } + /** * This will be called after {@link applyToEvent} is finished. */ @@ -598,3 +631,11 @@ function getGlobalEventProcessors(): EventProcessor[] { export function addGlobalEventProcessor(callback: EventProcessor): void { getGlobalEventProcessors().push(callback); } + +function generatePropagationContext(): PropagationContext { + return { + traceId: uuid4(), + spanId: uuid4().substring(16), + sampled: false, + }; +} diff --git a/packages/core/src/tracing/dynamicSamplingContext.ts b/packages/core/src/tracing/dynamicSamplingContext.ts new file mode 100644 index 000000000000..f8e8cd107c87 --- /dev/null +++ b/packages/core/src/tracing/dynamicSamplingContext.ts @@ -0,0 +1,32 @@ +import type { Client, DynamicSamplingContext, Scope } from '@sentry/types'; +import { dropUndefinedKeys } from '@sentry/utils'; + +import { DEFAULT_ENVIRONMENT } from '../constants'; + +/** + * Creates a dynamic sampling context from a client. + * + * Dispatchs the `createDsc` lifecycle hook as a side effect. + */ +export function getDynamicSamplingContextFromClient( + trace_id: string, + client: Client, + scope?: Scope, +): DynamicSamplingContext { + const options = client.getOptions(); + + const { publicKey: public_key } = client.getDsn() || {}; + const { segment: user_segment } = (scope && scope.getUser()) || {}; + + const dsc = dropUndefinedKeys({ + environment: options.environment || DEFAULT_ENVIRONMENT, + release: options.release, + user_segment, + public_key, + trace_id, + }) as DynamicSamplingContext; + + client.emit && client.emit('createDsc', dsc); + + return dsc; +} diff --git a/packages/core/src/tracing/index.ts b/packages/core/src/tracing/index.ts index a0d2716fda49..7a1f5336fe1c 100644 --- a/packages/core/src/tracing/index.ts +++ b/packages/core/src/tracing/index.ts @@ -7,3 +7,4 @@ export { extractTraceparentData, getActiveTransaction } from './utils'; export { SpanStatus } from './spanstatus'; export type { SpanStatusType } from './span'; export { trace } from './trace'; +export { getDynamicSamplingContextFromClient } from './dynamicSamplingContext'; diff --git a/packages/core/src/tracing/span.ts b/packages/core/src/tracing/span.ts index 59a8ed11d68a..617dc7eaa52c 100644 --- a/packages/core/src/tracing/span.ts +++ b/packages/core/src/tracing/span.ts @@ -7,7 +7,7 @@ import type { TraceContext, Transaction, } from '@sentry/types'; -import { dropUndefinedKeys, logger, timestampInSeconds, uuid4 } from '@sentry/utils'; +import { dropUndefinedKeys, generateSentryTraceHeader, logger, timestampInSeconds, uuid4 } from '@sentry/utils'; /** * Keeps track of finished spans for a given transaction @@ -265,11 +265,7 @@ export class Span implements SpanInterface { * @inheritDoc */ public toTraceparent(): string { - let sampledString = ''; - if (this.sampled !== undefined) { - sampledString = this.sampled ? '-1' : '-0'; - } - return `${this.traceId}-${this.spanId}${sampledString}`; + return generateSentryTraceHeader(this.traceId, this.spanId, this.sampled); } /** diff --git a/packages/core/src/tracing/transaction.ts b/packages/core/src/tracing/transaction.ts index d62f4c3b2833..71e6279eab80 100644 --- a/packages/core/src/tracing/transaction.ts +++ b/packages/core/src/tracing/transaction.ts @@ -11,9 +11,9 @@ import type { } from '@sentry/types'; import { dropUndefinedKeys, logger } from '@sentry/utils'; -import { DEFAULT_ENVIRONMENT } from '../constants'; import type { Hub } from '../hub'; import { getCurrentHub } from '../hub'; +import { getDynamicSamplingContextFromClient } from './dynamicSamplingContext'; import { Span as SpanClass, SpanRecorder } from './span'; /** JSDoc */ @@ -245,39 +245,32 @@ export class Transaction extends SpanClass implements TransactionInterface { return this._frozenDynamicSamplingContext; } - const hub: Hub = this._hub || getCurrentHub(); - const client = hub && hub.getClient(); + const hub = this._hub || getCurrentHub(); + const client = hub.getClient(); if (!client) return {}; - const { environment, release } = client.getOptions() || {}; - const { publicKey: public_key } = client.getDsn() || {}; + const scope = hub.getScope(); + const dsc = getDynamicSamplingContextFromClient(this.traceId, client, scope); const maybeSampleRate = this.metadata.sampleRate; - const sample_rate = maybeSampleRate !== undefined ? maybeSampleRate.toString() : undefined; - - const { segment: user_segment } = hub.getScope().getUser() || {}; + if (maybeSampleRate !== undefined) { + dsc.sample_rate = `${maybeSampleRate}`; + } + // We don't want to have a transaction name in the DSC if the source is "url" because URLs might contain PII const source = this.metadata.source; + if (source && source !== 'url') { + dsc.transaction = this.name; + } - // We don't want to have a transaction name in the DSC if the source is "url" because URLs might contain PII - const transaction = source && source !== 'url' ? this.name : undefined; - - const dsc = dropUndefinedKeys({ - environment: environment || DEFAULT_ENVIRONMENT, - release, - transaction, - user_segment, - public_key, - trace_id: this.traceId, - sample_rate, - }); + if (this.sampled !== undefined) { + dsc.sampled = String(this.sampled); + } // Uncomment if we want to make DSC immutable // this._frozenDynamicSamplingContext = dsc; - client.emit && client.emit('createDsc', dsc); - return dsc; } diff --git a/packages/core/test/lib/base.test.ts b/packages/core/test/lib/base.test.ts index aca8784ad511..162b53e4bb51 100644 --- a/packages/core/test/lib/base.test.ts +++ b/packages/core/test/lib/base.test.ts @@ -492,6 +492,28 @@ describe('BaseClient', () => { ); }); + test('it adds a trace context all events', () => { + expect.assertions(1); + + const options = getDefaultTestClientOptions({ dsn: PUBLIC_DSN }); + const client = new TestClient(options); + const scope = new Scope(); + + client.captureEvent({ message: 'message' }, { event_id: 'wat' }, scope); + + expect(TestClient.instance!.event!).toEqual( + expect.objectContaining({ + contexts: { + trace: { + parent_span_id: undefined, + span_id: expect.any(String), + trace_id: expect.any(String), + }, + }, + }), + ); + }); + test('adds `event_id` from hint if available', () => { expect.assertions(1); diff --git a/packages/core/test/lib/checkin.test.ts b/packages/core/test/lib/checkin.test.ts index 38a8fce56e95..5ae6bac6b5f3 100644 --- a/packages/core/test/lib/checkin.test.ts +++ b/packages/core/test/lib/checkin.test.ts @@ -10,6 +10,10 @@ describe('createCheckInEnvelope', () => { monitor_slug: 'b7645b8e-b47d-4398-be9a-d16b0dac31cb', status: 'in_progress', }, + { + trace_id: '86f39e84263a4de99c326acab3bfe3bd', + public_key: 'testPublicKey', + }, { sdk: { name: 'testSdkName', @@ -30,6 +34,10 @@ describe('createCheckInEnvelope', () => { name: 'testSdkName', version: 'testSdkVersion', }, + trace: { + trace_id: '86f39e84263a4de99c326acab3bfe3bd', + public_key: 'testPublicKey', + }, sent_at: expect.any(String), }); }); diff --git a/packages/core/test/lib/metadata.test.ts b/packages/core/test/lib/metadata.test.ts new file mode 100644 index 000000000000..64b3fd7310ad --- /dev/null +++ b/packages/core/test/lib/metadata.test.ts @@ -0,0 +1,97 @@ +import type { Event } from '@sentry/types'; +import { createStackParser, GLOBAL_OBJ, nodeStackLineParser } from '@sentry/utils'; + +import { addMetadataToStackFrames, getMetadataForUrl, stripMetadataFromStackFrames } from '../../src/metadata'; + +const parser = createStackParser(nodeStackLineParser()); + +const stack = new Error().stack || ''; + +const event: Event = { + exception: { + values: [ + { + stacktrace: { + frames: [ + { + filename: '', + function: 'new Promise', + }, + { + filename: '/tmp/utils.js', + function: 'Promise.then.completed', + lineno: 391, + colno: 28, + }, + { + filename: __filename, + function: 'Object.', + lineno: 9, + colno: 19, + }, + ], + }, + }, + ], + }, +}; + +describe('Metadata', () => { + beforeEach(() => { + GLOBAL_OBJ._sentryModuleMetadata = GLOBAL_OBJ._sentryModuleMetadata || {}; + GLOBAL_OBJ._sentryModuleMetadata[stack] = { team: 'frontend' }; + }); + + it('is parsed', () => { + const metadata = getMetadataForUrl(parser, __filename); + + expect(metadata).toEqual({ team: 'frontend' }); + }); + + it('is added and stripped from stack frames', () => { + addMetadataToStackFrames(parser, event); + + expect(event.exception?.values?.[0].stacktrace?.frames).toEqual([ + { + filename: '', + function: 'new Promise', + }, + { + filename: '/tmp/utils.js', + function: 'Promise.then.completed', + lineno: 391, + colno: 28, + }, + { + filename: __filename, + function: 'Object.', + lineno: 9, + colno: 19, + module_metadata: { + team: 'frontend', + }, + }, + ]); + + stripMetadataFromStackFrames(event); + + expect(event.exception?.values?.[0].stacktrace?.frames).toEqual([ + { + filename: '', + function: 'new Promise', + }, + { + filename: '/tmp/utils.js', + function: 'Promise.then.completed', + lineno: 391, + colno: 28, + }, + { + filename: __filename, + function: 'Object.', + lineno: 9, + colno: 19, + }, + ]); + }); +}); diff --git a/packages/e2e-tests/lib/types.ts b/packages/e2e-tests/lib/types.ts index a86c748ef6d0..f06094ce0426 100644 --- a/packages/e2e-tests/lib/types.ts +++ b/packages/e2e-tests/lib/types.ts @@ -47,4 +47,4 @@ export interface RecipeTestResult extends RecipeInstance { tests: TestResult[]; } -export type Env = Record; +export type Env = Record; diff --git a/packages/e2e-tests/lib/utils.ts b/packages/e2e-tests/lib/utils.ts index 0c786a34f520..ef6d65dc84dc 100644 --- a/packages/e2e-tests/lib/utils.ts +++ b/packages/e2e-tests/lib/utils.ts @@ -17,22 +17,50 @@ interface SpawnAsync { status: number | null; } -export function spawnAsync( - cmd: string, - options?: - | childProcess.SpawnOptionsWithoutStdio - | childProcess.SpawnOptionsWithStdioTuple, - input?: string, -): Promise { - const start = Date.now(); +interface SpawnOptions { + timeout?: number; + // eslint-disable-next-line @typescript-eslint/no-explicit-any + env?: any; + cwd?: string; +} + +export function spawnAsync(cmd: string, options?: SpawnOptions, input?: string): Promise { + const timeoutMs = options?.timeout || 60_000 * 5; return new Promise(resolve => { const cp = childProcess.spawn(cmd, { shell: true, ...options }); + // Ensure we properly time out after max. 5 min per command + let timeout: ReturnType | undefined = setTimeout(() => { + console.log(`Command "${cmd}" timed out after 5 minutes.`); + cp.kill(); + end(null, `ETDIMEDOUT: Process timed out after ${timeoutMs} ms.`); + }, timeoutMs); + const stderr: unknown[] = []; const stdout: string[] = []; let error: Error | undefined; + function end(status: number | null, errorMessage?: string): void { + // This means we already ended + if (!timeout) { + return; + } + + if (!error && errorMessage) { + error = new Error(errorMessage); + } + + clearTimeout(timeout); + timeout = undefined; + resolve({ + stdout: stdout.join(''), + stderr: stderr.join(''), + error: error || (status !== 0 ? new Error(`Process exited with status ${status}`) : undefined), + status, + }); + } + cp.stdout.on('data', data => { stdout.push(data ? (data as object).toString() : ''); }); @@ -46,19 +74,7 @@ export function spawnAsync( }); cp.on('close', status => { - const end = Date.now(); - - // We manually mark this as timed out if the process takes too long - if (!error && status === 1 && options?.timeout && end >= start + options.timeout) { - error = new Error(`ETDIMEDOUT: Process timed out after ${options.timeout} ms.`); - } - - resolve({ - stdout: stdout.join(''), - stderr: stderr.join(''), - error: error || (status !== 0 ? new Error(`Process exited with status ${status}`) : undefined), - status, - }); + end(status); }); if (input) { diff --git a/packages/e2e-tests/package.json b/packages/e2e-tests/package.json index 21a8f0e42552..c8b4473d24e4 100644 --- a/packages/e2e-tests/package.json +++ b/packages/e2e-tests/package.json @@ -10,9 +10,10 @@ "fix": "run-s fix:eslint fix:prettier", "fix:eslint": "eslint . --format stylish --fix", "fix:prettier": "prettier --config ../../.prettierrc.json --write . ", - "lint": "run-s lint:prettier lint:eslint", + "lint": "run-s lint:prettier lint:eslint lint:ts", "lint:eslint": "eslint . --format stylish", "lint:prettier": "prettier --config ../../.prettierrc.json --check .", + "lint:ts": "tsc --noEmit", "test:e2e": "run-s test:validate-configuration test:validate-test-app-setups test:run", "test:run": "ts-node run.ts", "test:validate-configuration": "ts-node validate-verdaccio-configuration.ts", diff --git a/packages/e2e-tests/run.ts b/packages/e2e-tests/run.ts index a062f51aab6c..658b4ffaa97f 100644 --- a/packages/e2e-tests/run.ts +++ b/packages/e2e-tests/run.ts @@ -17,6 +17,7 @@ async function run(): Promise { const envVarsToInject = { REACT_APP_E2E_TEST_DSN: process.env.E2E_TEST_DSN, + REMIX_APP_E2E_TEST_DSN: process.env.E2E_TEST_DSN, NEXT_PUBLIC_E2E_TEST_DSN: process.env.E2E_TEST_DSN, PUBLIC_E2E_TEST_DSN: process.env.E2E_TEST_DSN, BASE_PORT: '27496', // just some random port diff --git a/packages/e2e-tests/test-applications/create-remix-app/.eslintrc.js b/packages/e2e-tests/test-applications/create-remix-app/.eslintrc.js new file mode 100644 index 000000000000..f2faf1470fd8 --- /dev/null +++ b/packages/e2e-tests/test-applications/create-remix-app/.eslintrc.js @@ -0,0 +1,4 @@ +/** @type {import('eslint').Linter.Config} */ +module.exports = { + extends: ['@remix-run/eslint-config', '@remix-run/eslint-config/node'], +}; diff --git a/packages/e2e-tests/test-applications/create-remix-app/.gitignore b/packages/e2e-tests/test-applications/create-remix-app/.gitignore new file mode 100644 index 000000000000..3f7bf98da3e1 --- /dev/null +++ b/packages/e2e-tests/test-applications/create-remix-app/.gitignore @@ -0,0 +1,6 @@ +node_modules + +/.cache +/build +/public/build +.env diff --git a/packages/e2e-tests/test-applications/create-remix-app/.npmrc b/packages/e2e-tests/test-applications/create-remix-app/.npmrc new file mode 100644 index 000000000000..c6b3ef9b3eaa --- /dev/null +++ b/packages/e2e-tests/test-applications/create-remix-app/.npmrc @@ -0,0 +1,2 @@ +@sentry:registry=http://localhost:4873 +@sentry-internal:registry=http://localhost:4873 diff --git a/packages/e2e-tests/test-applications/create-remix-app/app/entry.client.tsx b/packages/e2e-tests/test-applications/create-remix-app/app/entry.client.tsx new file mode 100644 index 000000000000..edb18224d6c6 --- /dev/null +++ b/packages/e2e-tests/test-applications/create-remix-app/app/entry.client.tsx @@ -0,0 +1,34 @@ +/** + * By default, Remix will handle hydrating your app on the client for you. + * You are free to delete this file if you'd like to, but if you ever want it revealed again, you can run `npx remix reveal` ✨ + * For more information, see https://remix.run/file-conventions/entry.client + */ + +import { RemixBrowser, useLocation, useMatches } from '@remix-run/react'; +import { startTransition, StrictMode, useEffect } from 'react'; +import { hydrateRoot } from 'react-dom/client'; +import * as Sentry from '@sentry/remix'; + +Sentry.init({ + dsn: process.env.REMIX_APP_E2E_TEST_DSN, + integrations: [ + new Sentry.BrowserTracing({ + routingInstrumentation: Sentry.remixRouterInstrumentation(useEffect, useLocation, useMatches), + }), + new Sentry.Replay(), + ], + // Performance Monitoring + tracesSampleRate: 1.0, // Capture 100% of the transactions, reduce in production! + // Session Replay + replaysSessionSampleRate: 0.1, // This sets the sample rate at 10%. You may want to change it to 100% while in development and then sample at a lower rate in production. + replaysOnErrorSampleRate: 1.0, // If you're not already sampling the entire session, change the sample rate to 100% when sampling sessions where errors occur. +}); + +startTransition(() => { + hydrateRoot( + document, + + + , + ); +}); diff --git a/packages/e2e-tests/test-applications/create-remix-app/app/entry.server.tsx b/packages/e2e-tests/test-applications/create-remix-app/app/entry.server.tsx new file mode 100644 index 000000000000..c9975e940e21 --- /dev/null +++ b/packages/e2e-tests/test-applications/create-remix-app/app/entry.server.tsx @@ -0,0 +1,110 @@ +/** + * By default, Remix will handle generating the HTTP Response for you. + * You are free to delete this file if you'd like to, but if you ever want it revealed again, you can run `npx remix reveal` ✨ + * For more information, see https://remix.run/file-conventions/entry.server + */ + +import { PassThrough } from 'node:stream'; + +import type { AppLoadContext, EntryContext } from '@remix-run/node'; +import { Response } from '@remix-run/node'; +import { RemixServer } from '@remix-run/react'; +import isbot from 'isbot'; +import { renderToPipeableStream } from 'react-dom/server'; +import * as Sentry from '@sentry/remix'; + +const ABORT_DELAY = 5_000; + +Sentry.init({ + dsn: process.env.REMIX_APP_E2E_TEST_DSN, + // Performance Monitoring + tracesSampleRate: 1.0, // Capture 100% of the transactions, reduce in production! +}); + +export default function handleRequest( + request: Request, + responseStatusCode: number, + responseHeaders: Headers, + remixContext: EntryContext, + loadContext: AppLoadContext, +) { + return isbot(request.headers.get('user-agent')) + ? handleBotRequest(request, responseStatusCode, responseHeaders, remixContext) + : handleBrowserRequest(request, responseStatusCode, responseHeaders, remixContext); +} + +function handleBotRequest( + request: Request, + responseStatusCode: number, + responseHeaders: Headers, + remixContext: EntryContext, +) { + return new Promise((resolve, reject) => { + const { pipe, abort } = renderToPipeableStream( + , + { + onAllReady() { + const body = new PassThrough(); + + responseHeaders.set('Content-Type', 'text/html'); + + resolve( + new Response(body, { + headers: responseHeaders, + status: responseStatusCode, + }), + ); + + pipe(body); + }, + onShellError(error: unknown) { + reject(error); + }, + onError(error: unknown) { + responseStatusCode = 500; + console.error(error); + }, + }, + ); + + setTimeout(abort, ABORT_DELAY); + }); +} + +function handleBrowserRequest( + request: Request, + responseStatusCode: number, + responseHeaders: Headers, + remixContext: EntryContext, +) { + return new Promise((resolve, reject) => { + const { pipe, abort } = renderToPipeableStream( + , + { + onShellReady() { + const body = new PassThrough(); + + responseHeaders.set('Content-Type', 'text/html'); + + resolve( + new Response(body, { + headers: responseHeaders, + status: responseStatusCode, + }), + ); + + pipe(body); + }, + onShellError(error: unknown) { + reject(error); + }, + onError(error: unknown) { + console.error(error); + responseStatusCode = 500; + }, + }, + ); + + setTimeout(abort, ABORT_DELAY); + }); +} diff --git a/packages/e2e-tests/test-applications/create-remix-app/app/root.tsx b/packages/e2e-tests/test-applications/create-remix-app/app/root.tsx new file mode 100644 index 000000000000..0f3d1a16e8a8 --- /dev/null +++ b/packages/e2e-tests/test-applications/create-remix-app/app/root.tsx @@ -0,0 +1,27 @@ +import { cssBundleHref } from '@remix-run/css-bundle'; +import type { LinksFunction } from '@remix-run/node'; +import { Links, LiveReload, Meta, Outlet, Scripts, ScrollRestoration } from '@remix-run/react'; +import { withSentry } from '@sentry/remix'; + +export const links: LinksFunction = () => [...(cssBundleHref ? [{ rel: 'stylesheet', href: cssBundleHref }] : [])]; + +function App() { + return ( + + + + + + + + + + + + + + + ); +} + +export default withSentry(App); diff --git a/packages/e2e-tests/test-applications/create-remix-app/app/routes/_index.tsx b/packages/e2e-tests/test-applications/create-remix-app/app/routes/_index.tsx new file mode 100644 index 000000000000..e279d69b17b3 --- /dev/null +++ b/packages/e2e-tests/test-applications/create-remix-app/app/routes/_index.tsx @@ -0,0 +1,30 @@ +import type { V2_MetaFunction } from '@remix-run/node'; + +export const meta: V2_MetaFunction = () => { + return [{ title: 'New Remix App' }, { name: 'description', content: 'Welcome to Remix!' }]; +}; + +export default function Index() { + return ( + + ); +} diff --git a/packages/e2e-tests/test-applications/create-remix-app/package.json b/packages/e2e-tests/test-applications/create-remix-app/package.json new file mode 100644 index 000000000000..080bc7de0446 --- /dev/null +++ b/packages/e2e-tests/test-applications/create-remix-app/package.json @@ -0,0 +1,31 @@ +{ + "private": true, + "sideEffects": false, + "scripts": { + "build": "remix build", + "dev": "remix dev", + "start": "remix-serve build", + "typecheck": "tsc" + }, + "dependencies": { + "@sentry/remix": "*", + "@remix-run/css-bundle": "^1.16.1", + "@remix-run/node": "^1.16.1", + "@remix-run/react": "^1.16.1", + "@remix-run/serve": "^1.16.1", + "isbot": "^3.6.8", + "react": "^18.2.0", + "react-dom": "^18.2.0" + }, + "devDependencies": { + "@remix-run/dev": "^1.16.1", + "@remix-run/eslint-config": "^1.16.1", + "@types/react": "^18.0.35", + "@types/react-dom": "^18.0.11", + "eslint": "^8.38.0", + "typescript": "^5.0.4" + }, + "engines": { + "node": ">=14" + } +} diff --git a/packages/e2e-tests/test-applications/create-remix-app/remix.config.js b/packages/e2e-tests/test-applications/create-remix-app/remix.config.js new file mode 100644 index 000000000000..1203199a8d5f --- /dev/null +++ b/packages/e2e-tests/test-applications/create-remix-app/remix.config.js @@ -0,0 +1,15 @@ +/** @type {import('@remix-run/dev').AppConfig} */ +module.exports = { + ignoredRouteFiles: ['**/.*'], + // appDirectory: "app", + // assetsBuildDirectory: "public/build", + // serverBuildPath: "build/index.js", + // publicPath: "/build/", + serverModuleFormat: 'cjs', + future: { + v2_errorBoundary: true, + v2_meta: true, + v2_normalizeFormMethod: true, + v2_routeConvention: true, + }, +}; diff --git a/packages/e2e-tests/test-applications/create-remix-app/test-recipe.json b/packages/e2e-tests/test-applications/create-remix-app/test-recipe.json new file mode 100644 index 000000000000..dc16b21af54c --- /dev/null +++ b/packages/e2e-tests/test-applications/create-remix-app/test-recipe.json @@ -0,0 +1,6 @@ +{ + "$schema": "../../test-recipe-schema.json", + "testApplicationName": "create-remix-app", + "buildCommand": "pnpm install && pnpm build", + "tests": [] +} diff --git a/packages/e2e-tests/test-applications/create-remix-app/tsconfig.json b/packages/e2e-tests/test-applications/create-remix-app/tsconfig.json new file mode 100644 index 000000000000..20f8a386a6c4 --- /dev/null +++ b/packages/e2e-tests/test-applications/create-remix-app/tsconfig.json @@ -0,0 +1,22 @@ +{ + "include": ["remix.env.d.ts", "**/*.ts", "**/*.tsx"], + "compilerOptions": { + "lib": ["DOM", "DOM.Iterable", "ES2019"], + "isolatedModules": true, + "esModuleInterop": true, + "jsx": "react-jsx", + "moduleResolution": "node", + "resolveJsonModule": true, + "target": "ES2019", + "strict": true, + "allowJs": true, + "forceConsistentCasingInFileNames": true, + "baseUrl": ".", + "paths": { + "~/*": ["./app/*"] + }, + + // Remix takes care of building everything in `remix build`. + "noEmit": true + } +} diff --git a/packages/e2e-tests/tsconfig.json b/packages/e2e-tests/tsconfig.json index 2e051315981a..026669f596c0 100644 --- a/packages/e2e-tests/tsconfig.json +++ b/packages/e2e-tests/tsconfig.json @@ -1,5 +1,6 @@ { "extends": "../../tsconfig.json", + "exclude": ["node_modules", "test-applications"], "compilerOptions": { "types": ["node"] } diff --git a/packages/gatsby/.gitignore b/packages/gatsby/.gitignore new file mode 100644 index 000000000000..c318d0bec1a6 --- /dev/null +++ b/packages/gatsby/.gitignore @@ -0,0 +1,2 @@ +gatsby-browser.d.ts +gatsby-node.d.ts diff --git a/packages/hub/test/scope.test.ts b/packages/hub/test/scope.test.ts index 6571cd3122b4..982cba766a87 100644 --- a/packages/hub/test/scope.test.ts +++ b/packages/hub/test/scope.test.ts @@ -12,6 +12,21 @@ describe('Scope', () => { GLOBAL_OBJ.__SENTRY__.globalEventProcessors = undefined; }); + describe('init', () => { + test('it creates a propagation context', () => { + const scope = new Scope(); + + // @ts-ignore asserting on private properties + expect(scope._propagationContext).toEqual({ + traceId: expect.any(String), + spanId: expect.any(String), + sampled: false, + dsc: undefined, + parentSpanId: undefined, + }); + }); + }); + describe('attributes modification', () => { test('setFingerprint', () => { const scope = new Scope(); @@ -137,6 +152,22 @@ describe('Scope', () => { expect((scope as any)._sdkProcessingMetadata.dogs).toEqual('are great!'); }); + test('set and get propagation context', () => { + const scope = new Scope(); + const oldPropagationContext = scope.getPropagationContext(); + scope.setPropagationContext({ + traceId: '86f39e84263a4de99c326acab3bfe3bd', + spanId: '6e0c63257de34c92', + sampled: true, + }); + expect(scope.getPropagationContext()).not.toEqual(oldPropagationContext); + expect(scope.getPropagationContext()).toEqual({ + traceId: '86f39e84263a4de99c326acab3bfe3bd', + spanId: '6e0c63257de34c92', + sampled: true, + }); + }); + test('chaining', () => { const scope = new Scope(); scope.setLevel('fatal').setUser({ id: '1' }); @@ -193,6 +224,14 @@ describe('Scope', () => { expect(parentScope.getRequestSession()).toEqual({ status: 'ok' }); expect(scope.getRequestSession()).toEqual({ status: 'ok' }); }); + + test('should clone propagation context', () => { + const parentScope = new Scope(); + const scope = Scope.clone(parentScope); + + // @ts-ignore accessing private property for test + expect(scope._propagationContext).toEqual(parentScope._propagationContext); + }); }); describe('applyToEvent', () => { @@ -220,7 +259,11 @@ describe('Scope', () => { expect(processedEvent!.transaction).toEqual('/abc'); expect(processedEvent!.breadcrumbs![0]).toHaveProperty('message', 'test'); expect(processedEvent!.contexts).toEqual({ os: { id: '1' } }); - expect(processedEvent!.sdkProcessingMetadata).toEqual({ dogs: 'are great!' }); + expect(processedEvent!.sdkProcessingMetadata).toEqual({ + dogs: 'are great!', + // @ts-expect-error accessing private property for test + propagationContext: scope._propagationContext, + }); }); }); @@ -339,7 +382,7 @@ describe('Scope', () => { scope.setSpan(span); const event: Event = { contexts: { - trace: { a: 'c' }, + trace: { a: 'c' } as any, }, }; return scope.applyToEvent(event).then(processedEvent => { @@ -383,6 +426,8 @@ describe('Scope', () => { test('clear', () => { const scope = new Scope(); + // @ts-expect-error accessing private property + const oldPropagationContext = scope._propagationContext; scope.setExtra('a', 2); scope.setTag('a', 'b'); scope.setUser({ id: '1' }); @@ -393,6 +438,14 @@ describe('Scope', () => { scope.clear(); expect((scope as any)._extra).toEqual({}); expect((scope as any)._requestSession).toEqual(undefined); + // @ts-expect-error accessing private property + expect(scope._propagationContext).toEqual({ + traceId: expect.any(String), + spanId: expect.any(String), + sampled: false, + }); + // @ts-expect-error accessing private property + expect(scope._propagationContext).not.toEqual(oldPropagationContext); }); test('clearBreadcrumbs', () => { @@ -486,6 +539,8 @@ describe('Scope', () => { expect(updatedScope._level).toEqual('warning'); expect(updatedScope._fingerprint).toEqual(['bar']); expect(updatedScope._requestSession.status).toEqual('ok'); + // @ts-ignore accessing private property for test + expect(updatedScope._propagationContext).toEqual(localScope._propagationContext); }); test('given an empty instance of Scope, it should preserve all the original scope data', () => { @@ -518,7 +573,13 @@ describe('Scope', () => { tags: { bar: '3', baz: '4' }, user: { id: '42' }, requestSession: { status: 'errored' as RequestSessionStatus }, + propagationContext: { + traceId: '8949daf83f4a4a70bee4c1eb9ab242ed', + spanId: 'a024ad8fea82680e', + sampled: true, + }, }; + const updatedScope = scope.update(localAttributes) as any; expect(updatedScope._tags).toEqual({ @@ -540,6 +601,11 @@ describe('Scope', () => { expect(updatedScope._level).toEqual('warning'); expect(updatedScope._fingerprint).toEqual(['bar']); expect(updatedScope._requestSession).toEqual({ status: 'errored' }); + expect(updatedScope._propagationContext).toEqual({ + traceId: '8949daf83f4a4a70bee4c1eb9ab242ed', + spanId: 'a024ad8fea82680e', + sampled: true, + }); }); }); diff --git a/packages/nextjs/src/client/performance.ts b/packages/nextjs/src/client/performance.ts index 0dd0b109408f..fd5f979a08b0 100644 --- a/packages/nextjs/src/client/performance.ts +++ b/packages/nextjs/src/client/performance.ts @@ -1,12 +1,7 @@ import { getCurrentHub } from '@sentry/core'; import { WINDOW } from '@sentry/react'; -import type { Primitive, TraceparentData, Transaction, TransactionContext, TransactionSource } from '@sentry/types'; -import { - baggageHeaderToDynamicSamplingContext, - extractTraceparentData, - logger, - stripUrlQueryAndFragment, -} from '@sentry/utils'; +import type { Primitive, Transaction, TransactionContext, TransactionSource } from '@sentry/types'; +import { logger, stripUrlQueryAndFragment, tracingContextFromHeaders } from '@sentry/utils'; import type { NEXT_DATA as NextData } from 'next/dist/next-server/lib/utils'; import { default as Router } from 'next/router'; import type { ParsedUrlQuery } from 'querystring'; @@ -37,9 +32,9 @@ interface SentryEnhancedNextData extends NextData { interface NextDataTagInfo { route?: string; - traceParentData?: TraceparentData; - baggage?: string; params?: ParsedUrlQuery; + sentryTrace?: string; + baggage?: string; } /** @@ -83,13 +78,8 @@ function extractNextDataTagInformation(): NextDataTagInfo { nextDataTagInfo.params = query; if (props && props.pageProps) { - if (props.pageProps._sentryBaggage) { - nextDataTagInfo.baggage = props.pageProps._sentryBaggage; - } - - if (props.pageProps._sentryTraceData) { - nextDataTagInfo.traceParentData = extractTraceparentData(props.pageProps._sentryTraceData); - } + nextDataTagInfo.sentryTrace = props.pageProps._sentryTraceData; + nextDataTagInfo.baggage = props.pageProps._sentryBaggage; } return nextDataTagInfo; @@ -121,22 +111,26 @@ export function nextRouterInstrumentation( startTransactionOnPageLoad: boolean = true, startTransactionOnLocationChange: boolean = true, ): void { - const { route, traceParentData, baggage, params } = extractNextDataTagInformation(); + const { route, params, sentryTrace, baggage } = extractNextDataTagInformation(); + const { traceparentData, dynamicSamplingContext, propagationContext } = tracingContextFromHeaders( + sentryTrace, + baggage, + ); + + getCurrentHub().getScope().setPropagationContext(propagationContext); prevLocationName = route || globalObject.location.pathname; if (startTransactionOnPageLoad) { const source = route ? 'route' : 'url'; - const dynamicSamplingContext = baggageHeaderToDynamicSamplingContext(baggage); - activeTransaction = startTransactionCb({ name: prevLocationName, op: 'pageload', tags: DEFAULT_TAGS, ...(params && client && client.getOptions().sendDefaultPii && { data: params }), - ...traceParentData, + ...traceparentData, metadata: { source, - dynamicSamplingContext: traceParentData && !dynamicSamplingContext ? {} : dynamicSamplingContext, + dynamicSamplingContext: traceparentData && !dynamicSamplingContext ? {} : dynamicSamplingContext, }, }); } diff --git a/packages/nextjs/src/config/types.ts b/packages/nextjs/src/config/types.ts index 28f70d62dc05..5bab88555056 100644 --- a/packages/nextjs/src/config/types.ts +++ b/packages/nextjs/src/config/types.ts @@ -49,6 +49,8 @@ export type NextConfigObject = { publicRuntimeConfig?: { [key: string]: unknown }; // File extensions that count as pages in the `pages/` directory pageExtensions?: string[]; + // Whether Next.js should do a static export + output?: string; // Paths to reroute when requested rewrites?: () => Promise< | NextRewrite[] diff --git a/packages/nextjs/src/config/webpack.ts b/packages/nextjs/src/config/webpack.ts index bb5ef56ce4ad..fd5b36f9a789 100644 --- a/packages/nextjs/src/config/webpack.ts +++ b/packages/nextjs/src/config/webpack.ts @@ -865,7 +865,10 @@ function addValueInjectionLoader( const isomorphicValues = { // `rewritesTunnel` set by the user in Next.js config - __sentryRewritesTunnelPath__: userSentryOptions.tunnelRoute, + __sentryRewritesTunnelPath__: + userSentryOptions.tunnelRoute !== undefined && userNextConfig.output !== 'export' + ? `${userNextConfig.basePath ?? ''}${userSentryOptions.tunnelRoute}` + : undefined, // The webpack plugin's release injection breaks the `app` directory so we inject the release manually here instead. // Having a release defined in dev-mode spams releases in Sentry so we only set one in non-dev mode diff --git a/packages/nextjs/src/config/withSentryConfig.ts b/packages/nextjs/src/config/withSentryConfig.ts index c35e069eea1e..86f8c5a67031 100644 --- a/packages/nextjs/src/config/withSentryConfig.ts +++ b/packages/nextjs/src/config/withSentryConfig.ts @@ -8,6 +8,8 @@ import type { } from './types'; import { constructWebpackConfigFunction } from './webpack'; +let showedExportModeTunnelWarning = false; + /** * Add Sentry options to the config to be exported from the user's `next.config.js` file. * @@ -48,7 +50,17 @@ function getFinalConfigObject( delete incomingUserNextConfigObject.sentry; if (userSentryOptions?.tunnelRoute) { - setUpTunnelRewriteRules(incomingUserNextConfigObject, userSentryOptions.tunnelRoute); + if (incomingUserNextConfigObject.output === 'export') { + if (!showedExportModeTunnelWarning) { + showedExportModeTunnelWarning = true; + // eslint-disable-next-line no-console + console.warn( + '[@sentry/nextjs] The Sentry Next.js SDK `tunnelRoute` option will not work in combination with Next.js static exports. The `tunnelRoute` option uses serverside features that cannot be accessed in export mode. If you still want to tunnel Sentry events, set up your own tunnel: https://docs.sentry.io/platforms/javascript/troubleshooting/#using-the-tunnel-option', + ); + } + } else { + setUpTunnelRewriteRules(incomingUserNextConfigObject, userSentryOptions.tunnelRoute); + } } return { diff --git a/packages/nextjs/src/edge/edgeclient.ts b/packages/nextjs/src/edge/edgeclient.ts index ce13d6666448..39b795c81a53 100644 --- a/packages/nextjs/src/edge/edgeclient.ts +++ b/packages/nextjs/src/edge/edgeclient.ts @@ -1,14 +1,22 @@ import type { Scope } from '@sentry/core'; -import { addTracingExtensions, BaseClient, createCheckInEnvelope, SDK_VERSION } from '@sentry/core'; +import { + addTracingExtensions, + BaseClient, + createCheckInEnvelope, + getDynamicSamplingContextFromClient, + SDK_VERSION, +} from '@sentry/core'; import type { CheckIn, ClientOptions, + DynamicSamplingContext, Event, EventHint, MonitorConfig, SerializedCheckIn, Severity, SeverityLevel, + TraceContext, } from '@sentry/types'; import { logger, uuid4 } from '@sentry/utils'; @@ -72,7 +80,7 @@ export class EdgeClient extends BaseClient { * @param upsertMonitorConfig An optional object that describes a monitor config. Use this if you want * to create a monitor automatically when sending a check in. */ - public captureCheckIn(checkIn: CheckIn, monitorConfig?: MonitorConfig): string { + public captureCheckIn(checkIn: CheckIn, monitorConfig?: MonitorConfig, scope?: Scope): string { const id = checkIn.status !== 'in_progress' && checkIn.checkInId ? checkIn.checkInId : uuid4(); if (!this._isEnabled()) { __DEBUG_BUILD__ && logger.warn('SDK not enabled, will not capture checkin.'); @@ -103,7 +111,20 @@ export class EdgeClient extends BaseClient { }; } - const envelope = createCheckInEnvelope(serializedCheckIn, this.getSdkMetadata(), tunnel, this.getDsn()); + const [dynamicSamplingContext, traceContext] = this._getTraceInfoFromScope(scope); + if (traceContext) { + serializedCheckIn.contexts = { + trace: traceContext, + }; + } + + const envelope = createCheckInEnvelope( + serializedCheckIn, + dynamicSamplingContext, + this.getSdkMetadata(), + tunnel, + this.getDsn(), + ); __DEBUG_BUILD__ && logger.info('Sending checkin:', checkIn.monitorSlug, checkIn.status); void this._sendEnvelope(envelope); @@ -124,4 +145,30 @@ export class EdgeClient extends BaseClient { event.server_name = event.server_name || process.env.SENTRY_NAME; return super._prepareEvent(event, hint, scope); } + + /** Extract trace information from scope */ + private _getTraceInfoFromScope( + scope: Scope | undefined, + ): [dynamicSamplingContext: Partial | undefined, traceContext: TraceContext | undefined] { + if (!scope) { + return [undefined, undefined]; + } + + const span = scope.getSpan(); + if (span) { + return [span?.transaction?.getDynamicSamplingContext(), span?.getTraceContext()]; + } + + const { traceId, spanId, parentSpanId, dsc } = scope.getPropagationContext(); + const traceContext: TraceContext = { + trace_id: traceId, + span_id: spanId, + parent_span_id: parentSpanId, + }; + if (dsc) { + return [dsc, traceContext]; + } + + return [getDynamicSamplingContextFromClient(traceId, this, scope), traceContext]; + } } diff --git a/packages/nextjs/src/edge/utils/edgeWrapperUtils.ts b/packages/nextjs/src/edge/utils/edgeWrapperUtils.ts index 384074e317bd..256499c97185 100644 --- a/packages/nextjs/src/edge/utils/edgeWrapperUtils.ts +++ b/packages/nextjs/src/edge/utils/edgeWrapperUtils.ts @@ -1,12 +1,6 @@ import { captureException, getCurrentHub, hasTracingEnabled, startTransaction } from '@sentry/core'; import type { Span } from '@sentry/types'; -import { - addExceptionMechanism, - baggageHeaderToDynamicSamplingContext, - extractTraceparentData, - logger, - objectify, -} from '@sentry/utils'; +import { addExceptionMechanism, logger, objectify, tracingContextFromHeaders } from '@sentry/utils'; import type { EdgeRouteHandler } from '../types'; import { flush } from './flush'; @@ -32,17 +26,17 @@ export function withEdgeWrapping( op: options.spanOp, }); } else if (req instanceof Request) { - // If there is a trace header set, extract the data from it (parentSpanId, traceId, and sampling decision) - let traceparentData; - - const sentryTraceHeader = req.headers.get('sentry-trace'); - if (sentryTraceHeader) { - traceparentData = extractTraceparentData(sentryTraceHeader); - __DEBUG_BUILD__ && logger.log(`[Tracing] Continuing trace ${traceparentData?.traceId}.`); + const sentryTrace = req.headers.get('sentry-trace') || ''; + const baggage = req.headers.get('baggage'); + const { traceparentData, dynamicSamplingContext, propagationContext } = tracingContextFromHeaders( + sentryTrace, + baggage, + ); + currentScope.setPropagationContext(propagationContext); + if (traceparentData) { + __DEBUG_BUILD__ && logger.log(`[Tracing] Continuing trace ${traceparentData.traceId}.`); } - const dynamicSamplingContext = baggageHeaderToDynamicSamplingContext(req.headers.get('baggage')); - span = startTransaction({ name: options.spanDescription, op: options.spanOp, diff --git a/packages/nextjs/src/server/utils/wrapperUtils.ts b/packages/nextjs/src/server/utils/wrapperUtils.ts index c0fb4037c58b..a3cf13736900 100644 --- a/packages/nextjs/src/server/utils/wrapperUtils.ts +++ b/packages/nextjs/src/server/utils/wrapperUtils.ts @@ -6,7 +6,7 @@ import { startTransaction, } from '@sentry/core'; import type { Span, Transaction } from '@sentry/types'; -import { baggageHeaderToDynamicSamplingContext, extractTraceparentData } from '@sentry/utils'; +import { isString, tracingContextFromHeaders } from '@sentry/utils'; import type { IncomingMessage, ServerResponse } from 'http'; import { platformSupportsStreaming } from './platformSupportsStreaming'; @@ -38,6 +38,7 @@ function setTransactionOnRequest(transaction: Transaction, req: IncomingMessage) * * Note: This function turns the wrapped function into an asynchronous one. */ +// eslint-disable-next-line @typescript-eslint/no-explicit-any export function withErrorInstrumentation any>( origFunction: F, ): (...params: Parameters) => Promise> { @@ -66,6 +67,7 @@ export function withErrorInstrumentation any>( * @param options Options providing details for the created transaction and span. * @returns what the data fetching method call returned. */ +// eslint-disable-next-line @typescript-eslint/no-explicit-any export function withTracedServerSideDataFetcher Promise | any>( origDataFetcher: F, req: IncomingMessage, @@ -86,12 +88,14 @@ export function withTracedServerSideDataFetcher Pr const previousSpan: Span | undefined = getTransactionFromRequest(req) ?? scope.getSpan(); let dataFetcherSpan; - const sentryTraceHeader = req.headers['sentry-trace']; - const rawBaggageString = req.headers && req.headers.baggage; - const traceparentData = - typeof sentryTraceHeader === 'string' ? extractTraceparentData(sentryTraceHeader) : undefined; - - const dynamicSamplingContext = baggageHeaderToDynamicSamplingContext(rawBaggageString); + const sentryTrace = + req.headers && isString(req.headers['sentry-trace']) ? req.headers['sentry-trace'] : undefined; + const baggage = req.headers?.baggage; + const { traceparentData, dynamicSamplingContext, propagationContext } = tracingContextFromHeaders( + sentryTrace, + baggage, + ); + scope.setPropagationContext(propagationContext); if (platformSupportsStreaming()) { let spanToContinue: Span; diff --git a/packages/nextjs/src/server/wrapApiHandlerWithSentry.ts b/packages/nextjs/src/server/wrapApiHandlerWithSentry.ts index 9aecbd9a6c6b..524e29fa0ca0 100644 --- a/packages/nextjs/src/server/wrapApiHandlerWithSentry.ts +++ b/packages/nextjs/src/server/wrapApiHandlerWithSentry.ts @@ -3,12 +3,11 @@ import { captureException, startTransaction } from '@sentry/node'; import type { Transaction } from '@sentry/types'; import { addExceptionMechanism, - baggageHeaderToDynamicSamplingContext, - extractTraceparentData, isString, logger, objectify, stripUrlQueryAndFragment, + tracingContextFromHeaders, } from '@sentry/utils'; import type { AugmentedNextApiRequest, AugmentedNextApiResponse, NextApiHandler } from './types'; @@ -73,15 +72,18 @@ export function withSentry(apiHandler: NextApiHandler, parameterizedRoute?: stri currentScope.setSDKProcessingMetadata({ request: req }); if (hasTracingEnabled(options) && options?.instrumenter === 'sentry') { - // If there is a trace header set, extract the data from it (parentSpanId, traceId, and sampling decision) - let traceparentData; - if (req.headers && isString(req.headers['sentry-trace'])) { - traceparentData = extractTraceparentData(req.headers['sentry-trace']); - __DEBUG_BUILD__ && logger.log(`[Tracing] Continuing trace ${traceparentData?.traceId}.`); - } + const sentryTrace = + req.headers && isString(req.headers['sentry-trace']) ? req.headers['sentry-trace'] : undefined; + const baggage = req.headers?.baggage; + const { traceparentData, dynamicSamplingContext, propagationContext } = tracingContextFromHeaders( + sentryTrace, + baggage, + ); + hub.getScope().setPropagationContext(propagationContext); - const baggageHeader = req.headers && req.headers.baggage; - const dynamicSamplingContext = baggageHeaderToDynamicSamplingContext(baggageHeader); + if (__DEBUG_BUILD__ && traceparentData) { + logger.log(`[Tracing] Continuing trace ${traceparentData.traceId}.`); + } // prefer the parameterized route, if we have it (which we will if we've auto-wrapped the route handler) let reqPath = parameterizedRoute; diff --git a/packages/nextjs/src/server/wrapServerComponentWithSentry.ts b/packages/nextjs/src/server/wrapServerComponentWithSentry.ts index 8b0f1d8f2d79..81f030fa824c 100644 --- a/packages/nextjs/src/server/wrapServerComponentWithSentry.ts +++ b/packages/nextjs/src/server/wrapServerComponentWithSentry.ts @@ -5,7 +5,7 @@ import { runWithAsyncContext, startTransaction, } from '@sentry/core'; -import { baggageHeaderToDynamicSamplingContext, extractTraceparentData } from '@sentry/utils'; +import { tracingContextFromHeaders } from '@sentry/utils'; import { isNotFoundNavigationError, isRedirectNavigationError } from '../common/nextNavigationErrorUtils'; import type { ServerComponentContext } from '../common/types'; @@ -13,6 +13,7 @@ import type { ServerComponentContext } from '../common/types'; /** * Wraps an `app` directory server component with Sentry error instrumentation. */ +// eslint-disable-next-line @typescript-eslint/no-explicit-any export function wrapServerComponentWithSentry any>( appDirComponent: F, context: ServerComponentContext, @@ -28,14 +29,15 @@ export function wrapServerComponentWithSentry any> apply: (originalFunction, thisArg, args) => { return runWithAsyncContext(() => { const hub = getCurrentHub(); + const currentScope = hub.getScope(); let maybePromiseResult; - const traceparentData = context.sentryTraceHeader - ? extractTraceparentData(context.sentryTraceHeader) - : undefined; - - const dynamicSamplingContext = baggageHeaderToDynamicSamplingContext(context.baggageHeader); + const { traceparentData, dynamicSamplingContext, propagationContext } = tracingContextFromHeaders( + context.sentryTraceHeader, + context.baggageHeader, + ); + currentScope.setPropagationContext(propagationContext); const transaction = startTransaction({ op: 'function.nextjs', @@ -48,7 +50,6 @@ export function wrapServerComponentWithSentry any> }, }); - const currentScope = hub.getScope(); if (currentScope) { currentScope.setSpan(transaction); } diff --git a/packages/nextjs/test/buildProcess/testApp/yarn.lock b/packages/nextjs/test/buildProcess/testApp/yarn.lock index 6d3ceaa61296..cbe67329d3d6 100644 --- a/packages/nextjs/test/buildProcess/testApp/yarn.lock +++ b/packages/nextjs/test/buildProcess/testApp/yarn.lock @@ -2,6 +2,11 @@ # yarn lockfile v1 +"@jridgewell/sourcemap-codec@^1.4.13": + version "1.4.15" + resolved "https://registry.yarnpkg.com/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.15.tgz#d7c6e6755c78567a951e04ab52ef0fd26de59f32" + integrity sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg== + "@mapbox/node-pre-gyp@^1.0.5": version "1.0.10" resolved "https://registry.yarnpkg.com/@mapbox/node-pre-gyp/-/node-pre-gyp-1.0.10.tgz#8e6735ccebbb1581e5a7e652244cadc8a844d03c" @@ -87,36 +92,48 @@ resolved "https://registry.yarnpkg.com/@next/swc-win32-x64-msvc/-/swc-win32-x64-msvc-13.0.1.tgz#469dde61519f6a310874af93ee5969f1d5ff6d03" integrity sha512-t/0G33t/6VGWZUGCOT7rG42qqvf/x+MrFp1CU+8CN6PrjSSL57R5bqkXfubV9t4eCEnUxVP+5Hn3MoEXEebtEw== -"@rollup/plugin-sucrase@4.0.4": - version "4.0.4" - resolved "https://registry.yarnpkg.com/@rollup/plugin-sucrase/-/plugin-sucrase-4.0.4.tgz#0a3b3d97cdc239ec3399f5a10711f751e9f95d98" - integrity sha512-YH4J8yoJb5EVnLhAwWxYAQNh2SJOR+SdZ6XdgoKEv6Kxm33riYkM8MlMaggN87UoISP52qAFyZ5ey56wu6umGg== - dependencies: - "@rollup/pluginutils" "^4.1.1" - sucrase "^3.20.0" - -"@rollup/plugin-virtual@3.0.0": - version "3.0.0" - resolved "https://registry.yarnpkg.com/@rollup/plugin-virtual/-/plugin-virtual-3.0.0.tgz#8c3f54b4ab4b267d9cd3dcbaedc58d4fd1deddca" - integrity sha512-K9KORe1myM62o0lKkNR4MmCxjwuAXsZEtIHpaILfv4kILXTOrXt/R2ha7PzMcCHPYdnkWPiBZK8ed4Zr3Ll5lQ== +"@rollup/plugin-commonjs@24.0.0": + version "24.0.0" + resolved "https://registry.yarnpkg.com/@rollup/plugin-commonjs/-/plugin-commonjs-24.0.0.tgz#fb7cf4a6029f07ec42b25daa535c75b05a43f75c" + integrity sha512-0w0wyykzdyRRPHOb0cQt14mIBLujfAv6GgP6g8nvg/iBxEm112t3YPPq+Buqe2+imvElTka+bjNlJ/gB56TD8g== + dependencies: + "@rollup/pluginutils" "^5.0.1" + commondir "^1.0.1" + estree-walker "^2.0.2" + glob "^8.0.3" + is-reference "1.2.1" + magic-string "^0.27.0" + +"@rollup/pluginutils@^5.0.1": + version "5.0.2" + resolved "https://registry.yarnpkg.com/@rollup/pluginutils/-/pluginutils-5.0.2.tgz#012b8f53c71e4f6f9cb317e311df1404f56e7a33" + integrity sha512-pTd9rIsP92h+B6wWwFbW8RkZv4hiR/xKsqre4SIuAOaOEQRxi0lqLke9k2/7WegC85GgUs9pjmOjCUi3In4vwA== + dependencies: + "@types/estree" "^1.0.0" + estree-walker "^2.0.2" + picomatch "^2.3.1" -"@rollup/pluginutils@^4.1.1": - version "4.2.1" - resolved "https://registry.yarnpkg.com/@rollup/pluginutils/-/pluginutils-4.2.1.tgz#e6c6c3aba0744edce3fb2074922d3776c0af2a6d" - integrity sha512-iKnFXr7NkdZAIHiIWE+BX5ULi/ucVFYWD6TbAV+rZctiRTY2PL6tsIKhoIOaoskiWAkgu+VsbXgUVDNLHf+InQ== +"@sentry-internal/tracing@7.57.0": + version "7.57.0" + resolved "https://registry.yarnpkg.com/@sentry-internal/tracing/-/tracing-7.57.0.tgz#cb761931b635f8f24c84be0eecfacb8516b20551" + integrity sha512-tpViyDd8AhQGYYhI94xi2aaDopXOPfL2Apwrtb3qirWkomIQ2K86W1mPmkce+B0cFOnW2Dxv/ZTFKz6ghjK75A== dependencies: - estree-walker "^2.0.1" - picomatch "^2.2.2" + "@sentry/core" "7.57.0" + "@sentry/types" "7.57.0" + "@sentry/utils" "7.57.0" + tslib "^2.4.1 || ^1.9.3" -"@sentry/browser@7.20.0": - version "7.20.0" - resolved "https://registry.yarnpkg.com/@sentry/browser/-/browser-7.20.0.tgz#e917cab48d3bba2fe8a2e3a8987b0bd9b9149db8" - integrity sha512-L84CdB7DPQ2ohVcWh/KivemndWSZyXRvBZBr+tHFlQchzcaZZ/8lIPvjwvb8OJhzhecDq6JCAyUxaZwyItdyAg== +"@sentry/browser@7.57.0": + version "7.57.0" + resolved "https://registry.yarnpkg.com/@sentry/browser/-/browser-7.57.0.tgz#6e724c9eac680dba99ced0fdf81be8d1e3b3bceb" + integrity sha512-E0HaYYlaqHFiIRZXxcvOO8Odvlt+TR1vFFXzqUWXPOvDRxURglTOCQ3EN/u6bxtAGJ6y/Zc2obgihTtypuel/w== dependencies: - "@sentry/core" "7.20.0" - "@sentry/types" "7.20.0" - "@sentry/utils" "7.20.0" - tslib "^1.9.3" + "@sentry-internal/tracing" "7.57.0" + "@sentry/core" "7.57.0" + "@sentry/replay" "7.57.0" + "@sentry/types" "7.57.0" + "@sentry/utils" "7.57.0" + tslib "^2.4.1 || ^1.9.3" "@sentry/cli@^1.74.6": version "1.74.6" @@ -131,88 +148,87 @@ proxy-from-env "^1.1.0" which "^2.0.2" -"@sentry/core@7.20.0": - version "7.20.0" - resolved "https://registry.yarnpkg.com/@sentry/core/-/core-7.20.0.tgz#7becd848c982d5422627f69c4061d5914946c54a" - integrity sha512-8dIHk8niyEyVayUQpgECHnV2p444nPBjIyuQrtkdTxL7sBLC5+Y0DhRjxg9cJyZe/bZnXVerGkgcA7niKW4W8A== +"@sentry/core@7.57.0": + version "7.57.0" + resolved "https://registry.yarnpkg.com/@sentry/core/-/core-7.57.0.tgz#65093d739c04f320a54395a21be955fcbe326acb" + integrity sha512-l014NudPH0vQlzybtXajPxYFfs9w762NoarjObC3gu76D1jzBBFzhdRelkGpDbSLNTIsKhEDDRpgAjBWJ9icfw== dependencies: - "@sentry/types" "7.20.0" - "@sentry/utils" "7.20.0" - tslib "^1.9.3" + "@sentry/types" "7.57.0" + "@sentry/utils" "7.57.0" + tslib "^2.4.1 || ^1.9.3" -"@sentry/integrations@7.20.0": - version "7.20.0" - resolved "https://registry.yarnpkg.com/@sentry/integrations/-/integrations-7.20.0.tgz#78fc544847c5516acb86fc507d0bfdaedb295fd8" - integrity sha512-wT00rtdKiVOZm/2fGEotIQ2ItCU9xTODz3KveQLq2X2nI5Jg4opkDOY2NorEnuOfBNg6BncBxaC8TG8ujvRyfw== +"@sentry/integrations@7.57.0": + version "7.57.0" + resolved "https://registry.yarnpkg.com/@sentry/integrations/-/integrations-7.57.0.tgz#298085b3a2fe862cc70bc7f2143aa0fbc617322c" + integrity sha512-C3WZo5AGI2L0dj+mIjeZpdAwDEG2nDYvZbTzq5J9hVoHFdP3t7fOWBHSPkSFVtTdMaJrv+82aKnUefVCeAjxGg== dependencies: - "@sentry/types" "7.20.0" - "@sentry/utils" "7.20.0" + "@sentry/types" "7.57.0" + "@sentry/utils" "7.57.0" localforage "^1.8.1" - tslib "^1.9.3" + tslib "^2.4.1 || ^1.9.3" "@sentry/nextjs@file:../../..": - version "7.20.0" - dependencies: - "@rollup/plugin-sucrase" "4.0.4" - "@rollup/plugin-virtual" "3.0.0" - "@sentry/core" "7.20.0" - "@sentry/integrations" "7.20.0" - "@sentry/node" "7.20.0" - "@sentry/react" "7.20.0" - "@sentry/tracing" "7.20.0" - "@sentry/types" "7.20.0" - "@sentry/utils" "7.20.0" + version "7.57.0" + dependencies: + "@rollup/plugin-commonjs" "24.0.0" + "@sentry/core" "7.57.0" + "@sentry/integrations" "7.57.0" + "@sentry/node" "7.57.0" + "@sentry/react" "7.57.0" + "@sentry/types" "7.57.0" + "@sentry/utils" "7.57.0" "@sentry/webpack-plugin" "1.20.0" chalk "3.0.0" rollup "2.78.0" - tslib "^1.9.3" - -"@sentry/node@7.20.0": - version "7.20.0" - resolved "https://registry.yarnpkg.com/@sentry/node/-/node-7.20.0.tgz#a32e37b2c93607e8b8d7f3741f60418ec2d99ddb" - integrity sha512-aB0VSueCiWPg53DIevnKNYSu2OGsPweWO1eKhMhu1uJNbL+ZYAFRczdcLdkWCqa38X5jjqO82GKuIZYpXUGX+A== - dependencies: - "@sentry/core" "7.20.0" - "@sentry/types" "7.20.0" - "@sentry/utils" "7.20.0" + stacktrace-parser "^0.1.10" + tslib "^2.4.1 || ^1.9.3" + +"@sentry/node@7.57.0": + version "7.57.0" + resolved "https://registry.yarnpkg.com/@sentry/node/-/node-7.57.0.tgz#31052f5988ed4496d7f3ff925240cf9b02d09941" + integrity sha512-63mjyUVM6sfJFVQ5TGVRVGUsoEfESl5ABzIW1W0s9gUiQPaG8SOdaQJglb2VNrkMYxnRHgD8Q9LUh/qcmUyPGw== + dependencies: + "@sentry-internal/tracing" "7.57.0" + "@sentry/core" "7.57.0" + "@sentry/types" "7.57.0" + "@sentry/utils" "7.57.0" cookie "^0.4.1" https-proxy-agent "^5.0.0" lru_map "^0.3.3" - tslib "^1.9.3" + tslib "^2.4.1 || ^1.9.3" -"@sentry/react@7.20.0": - version "7.20.0" - resolved "https://registry.yarnpkg.com/@sentry/react/-/react-7.20.0.tgz#41e1edb5a7330bf15ab06e0c0b3e0be7cf36a7d3" - integrity sha512-hXPObzl4I7TgeCGEb3b03yLI7zF/oYQ5NoGz65fmhUainOGuW+S8KSyCWmAvaHXZ1cZao+sLfQSxl29sVVQyww== +"@sentry/react@7.57.0": + version "7.57.0" + resolved "https://registry.yarnpkg.com/@sentry/react/-/react-7.57.0.tgz#cf91f0115bcd2a8306d6c8a39d8e8b53d4b21814" + integrity sha512-XGNTjIoCG3naSmCU8qObd+y+CqAB6NQkGWOp2yyBwp2inyKF2ehJvDh6bIQloBYq2TmOJDa4NfXdMrkilxaLFQ== dependencies: - "@sentry/browser" "7.20.0" - "@sentry/types" "7.20.0" - "@sentry/utils" "7.20.0" + "@sentry/browser" "7.57.0" + "@sentry/types" "7.57.0" + "@sentry/utils" "7.57.0" hoist-non-react-statics "^3.3.2" - tslib "^1.9.3" + tslib "^2.4.1 || ^1.9.3" -"@sentry/tracing@7.20.0": - version "7.20.0" - resolved "https://registry.yarnpkg.com/@sentry/tracing/-/tracing-7.20.0.tgz#346bf72bc5574ac78de43bd8ab6f6027f8b64cbd" - integrity sha512-qg3sMvjuMQl/NEaF8I2IpvUcJ4HGGVIwEqqqZ6hkeHXIKt02p6f+nls45pVhluMiNHAaQJ+vefMTUc3E1yZwDA== +"@sentry/replay@7.57.0": + version "7.57.0" + resolved "https://registry.yarnpkg.com/@sentry/replay/-/replay-7.57.0.tgz#c8f7eae7b7edc9d32c3d2955b337f3b3c76dff39" + integrity sha512-pN4ryNS3J5EYbkXvR+O/+hseAJha7XDl8mPFtK0OGTHG10JzCi4tQJazblHQdpb5QBaMMPCeZ+isyfoQLDNXnw== dependencies: - "@sentry/core" "7.20.0" - "@sentry/types" "7.20.0" - "@sentry/utils" "7.20.0" - tslib "^1.9.3" + "@sentry/core" "7.57.0" + "@sentry/types" "7.57.0" + "@sentry/utils" "7.57.0" -"@sentry/types@7.20.0": - version "7.20.0" - resolved "https://registry.yarnpkg.com/@sentry/types/-/types-7.20.0.tgz#ace232e00407355dccedf65e27471635b4331265" - integrity sha512-x17ddduGWqW95neBFVvxzmInb5WXVw+2PcNASHXpGFhi7v2gz2a7/w2CcIKxsqODNnc+z/k1t0Y+uy9B6aH6ag== +"@sentry/types@7.57.0": + version "7.57.0" + resolved "https://registry.yarnpkg.com/@sentry/types/-/types-7.57.0.tgz#4fdb80cbd49ba034dd8d9be0c0005a016d5db3ce" + integrity sha512-D7ifoUfxuVCUyktIr5Gc+jXUbtcUMmfHdTtTbf1XCZHua5mJceK9wtl3YCg3eq/HK2Ppd52BKnTzEcS5ZKQM+w== -"@sentry/utils@7.20.0": - version "7.20.0" - resolved "https://registry.yarnpkg.com/@sentry/utils/-/utils-7.20.0.tgz#cca09e2256b64bfb41a78c89af3c88fcc7ca84c2" - integrity sha512-4lc122TFgkaCAvoPRy+uc5vgOCumTa/2nPkzCSxVsezQs+ebHxyMJQK7GWBLI6P+EzKfEjlgyMzRWaPJ3iJatA== +"@sentry/utils@7.57.0": + version "7.57.0" + resolved "https://registry.yarnpkg.com/@sentry/utils/-/utils-7.57.0.tgz#8253c6fcf35138b4c424234b8da1596e11b98ad8" + integrity sha512-YXrkMCiNklqkXctn4mKYkrzNCf/dfVcRUQrkXjeBC+PHXbcpPyaJgInNvztR7Skl8lE3JPGPN4v5XhLxK1bUUg== dependencies: - "@sentry/types" "7.20.0" - tslib "^1.9.3" + "@sentry/types" "7.57.0" + tslib "^2.4.1 || ^1.9.3" "@sentry/webpack-plugin@1.20.0": version "1.20.0" @@ -229,6 +245,11 @@ dependencies: tslib "^2.4.0" +"@types/estree@*", "@types/estree@^1.0.0": + version "1.0.1" + resolved "https://registry.yarnpkg.com/@types/estree/-/estree-1.0.1.tgz#aa22750962f3bf0e79d753d3cc067f010c95f194" + integrity sha512-LG4opVs2ANWZ1TJoKc937iMmNstM/d0ae1vNbnBvBhqCSezgVUOzcLCqbI5elV8Vy6WKwKjaqR+zO9VKirBBCA== + "@vercel/nft@latest": version "0.22.1" resolved "https://registry.yarnpkg.com/@vercel/nft/-/nft-0.22.1.tgz#0d91d2a21e3a7f0b23ce1550da9870eac4942828" @@ -280,11 +301,6 @@ ansi-styles@^4.1.0: dependencies: color-convert "^2.0.1" -any-promise@^1.0.0: - version "1.3.0" - resolved "https://registry.yarnpkg.com/any-promise/-/any-promise-1.3.0.tgz#abc6afeedcea52e809cdc0376aed3ce39635d17f" - integrity sha512-7UvmKalWRt1wgjL1RrGxoSJW/0QZFIegpeGvZG9kjp8vrRu55XTHbwnqq2GpXm9uLbcuhxm3IqX9OB4MZR1b2A== - aproba@^1.0.3: version "1.2.0" resolved "https://registry.yarnpkg.com/aproba/-/aproba-1.2.0.tgz#6802e6264efd18c790a1b0d517f0f2627bf2c94a" @@ -336,6 +352,13 @@ brace-expansion@^1.1.7: balanced-match "^1.0.0" concat-map "0.0.1" +brace-expansion@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-2.0.1.tgz#1edc459e0f0c548486ecf9fc99f2221364b9a0ae" + integrity sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA== + dependencies: + balanced-match "^1.0.0" + braces@^3.0.2: version "3.0.2" resolved "https://registry.yarnpkg.com/braces/-/braces-3.0.2.tgz#3454e1a462ee8d599e236df336cd9ea4f8afe107" @@ -388,10 +411,10 @@ color-support@^1.1.2: resolved "https://registry.yarnpkg.com/color-support/-/color-support-1.1.3.tgz#93834379a1cc9a0c61f82f52f0d04322251bd5a2" integrity sha512-qiBjkpbMLO/HL68y+lh4q0/O1MZFj2RX6X/KmMa3+gJD3z+WwI1ZzDHysvqHGS3mP6mznPckpXmw1nI9cJjyRg== -commander@^4.0.0: - version "4.1.1" - resolved "https://registry.yarnpkg.com/commander/-/commander-4.1.1.tgz#9fd602bd936294e9e9ef46a3f4d6964044b18068" - integrity sha512-NOKm8xhkzAjzFx8B2v5OAHT+u5pRQc2UCa2Vq9jYL/31o2wi9mxBA7LIFs3sV5VSC49z6pEhfbMULvShKj26WA== +commondir@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/commondir/-/commondir-1.0.1.tgz#ddd800da0c66127393cca5950ea968a3aaf1253b" + integrity sha512-W9pAhw0ja1Edb5GVdIF1mjZw/ASI0AlShXM83UUGe2DVr5TdAPEA1OA8m/g8zWp9x6On7gqufY+FatDbC3MDQg== concat-map@0.0.1: version "0.0.1" @@ -435,7 +458,7 @@ emoji-regex@^8.0.0: resolved "https://registry.yarnpkg.com/emoji-regex/-/emoji-regex-8.0.0.tgz#e818fd69ce5ccfcb404594f842963bf53164cc37" integrity sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A== -estree-walker@2.0.2, estree-walker@^2.0.1: +estree-walker@2.0.2, estree-walker@^2.0.2: version "2.0.2" resolved "https://registry.yarnpkg.com/estree-walker/-/estree-walker-2.0.2.tgz#52f010178c2a4c117a7757cfe942adb7d2da4cac" integrity sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w== @@ -503,29 +526,28 @@ gauge@~2.7.3: strip-ansi "^3.0.1" wide-align "^1.1.0" -glob@7.1.6: - version "7.1.6" - resolved "https://registry.yarnpkg.com/glob/-/glob-7.1.6.tgz#141f33b81a7c2492e125594307480c46679278a6" - integrity sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA== +glob@^7.1.3: + version "7.2.3" + resolved "https://registry.yarnpkg.com/glob/-/glob-7.2.3.tgz#b8df0fb802bbfa8e89bd1d938b4e16578ed44f2b" + integrity sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q== dependencies: fs.realpath "^1.0.0" inflight "^1.0.4" inherits "2" - minimatch "^3.0.4" + minimatch "^3.1.1" once "^1.3.0" path-is-absolute "^1.0.0" -glob@^7.1.3: - version "7.2.3" - resolved "https://registry.yarnpkg.com/glob/-/glob-7.2.3.tgz#b8df0fb802bbfa8e89bd1d938b4e16578ed44f2b" - integrity sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q== +glob@^8.0.3: + version "8.1.0" + resolved "https://registry.yarnpkg.com/glob/-/glob-8.1.0.tgz#d388f656593ef708ee3e34640fdfb99a9fd1c33e" + integrity sha512-r8hpEjiQEYlF2QU0df3dS+nxxSIreXQS1qRhMJM0Q5NDdR386C7jb7Hwwod8Fgiuex+k0GFjgft18yvxm5XoCQ== dependencies: fs.realpath "^1.0.0" inflight "^1.0.4" inherits "2" - minimatch "^3.1.1" + minimatch "^5.0.1" once "^1.3.0" - path-is-absolute "^1.0.0" graceful-fs@^4.2.9: version "4.2.10" @@ -592,6 +614,13 @@ is-number@^7.0.0: resolved "https://registry.yarnpkg.com/is-number/-/is-number-7.0.0.tgz#7535345b896734d5f80c4d06c50955527a14f12b" integrity sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng== +is-reference@1.2.1: + version "1.2.1" + resolved "https://registry.yarnpkg.com/is-reference/-/is-reference-1.2.1.tgz#8b2dac0b371f4bc994fdeaba9eb542d03002d0b7" + integrity sha512-U82MsXXiFIrjCK4otLT+o2NA2Cd2g5MLoOVXUZjIOhLurrRxpEXzI8O0KZHr3IjLvlAH1kTPYSuqer5T9ZVBKQ== + dependencies: + "@types/estree" "*" + isarray@~1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/isarray/-/isarray-1.0.0.tgz#bb935d48582cba168c06834957a54a3e07124f11" @@ -614,11 +643,6 @@ lie@3.1.1: dependencies: immediate "~3.0.5" -lines-and-columns@^1.1.6: - version "1.2.4" - resolved "https://registry.yarnpkg.com/lines-and-columns/-/lines-and-columns-1.2.4.tgz#eca284f75d2965079309dc0ad9255abb2ebc1632" - integrity sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg== - localforage@^1.8.1: version "1.10.0" resolved "https://registry.yarnpkg.com/localforage/-/localforage-1.10.0.tgz#5c465dc5f62b2807c3a84c0c6a1b1b3212781dd4" @@ -645,6 +669,13 @@ lru_map@^0.3.3: resolved "https://registry.yarnpkg.com/lru_map/-/lru_map-0.3.3.tgz#b5c8351b9464cbd750335a79650a0ec0e56118dd" integrity sha512-Pn9cox5CsMYngeDbmChANltQl+5pi6XmTrraMSzhPmMBbmgcxmqWry0U3PGapCU1yB4/LqCcom7qhHZiF/jGfQ== +magic-string@^0.27.0: + version "0.27.0" + resolved "https://registry.yarnpkg.com/magic-string/-/magic-string-0.27.0.tgz#e4a3413b4bab6d98d2becffd48b4a257effdbbf3" + integrity sha512-8UnnX2PeRAPZuN12svgR9j7M1uWMovg/CEnIwIG0LFkXSJJe4PdfUGiTGl8V9bsBHFUtfVINcSyYxd7q+kx9fA== + dependencies: + "@jridgewell/sourcemap-codec" "^1.4.13" + make-dir@^3.1.0: version "3.1.0" resolved "https://registry.yarnpkg.com/make-dir/-/make-dir-3.1.0.tgz#415e967046b3a7f1d185277d84aa58203726a13f" @@ -660,13 +691,20 @@ micromatch@^4.0.2: braces "^3.0.2" picomatch "^2.3.1" -minimatch@^3.0.4, minimatch@^3.1.1: +minimatch@^3.1.1: version "3.1.2" resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.1.2.tgz#19cd194bfd3e428f049a70817c038d89ab4be35b" integrity sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw== dependencies: brace-expansion "^1.1.7" +minimatch@^5.0.1: + version "5.1.6" + resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-5.1.6.tgz#1cfcb8cf5522ea69952cd2af95ae09477f122a96" + integrity sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g== + dependencies: + brace-expansion "^2.0.1" + minimist@^1.2.6: version "1.2.7" resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.7.tgz#daa1c4d91f507390437c6a8bc01078e7000c4d18" @@ -704,15 +742,6 @@ ms@2.1.2: resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.2.tgz#d09d1f357b443f493382a8eb3ccd183872ae6009" integrity sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w== -mz@^2.7.0: - version "2.7.0" - resolved "https://registry.yarnpkg.com/mz/-/mz-2.7.0.tgz#95008057a56cafadc2bc63dde7f9ff6955948e32" - integrity sha512-z81GNO7nnYMEhrGh9LeymoE4+Yr0Wn5McHIZMK5cfQCl+NDX08sCZgUc9/6MHni9IWuFLm1Z3HTCXu2z9fN62Q== - dependencies: - any-promise "^1.0.0" - object-assign "^4.0.1" - thenify-all "^1.0.0" - nanoid@^3.3.4: version "3.3.4" resolved "https://registry.yarnpkg.com/nanoid/-/nanoid-3.3.4.tgz#730b67e3cd09e2deacf03c027c81c9d9dbc5e8ab" @@ -788,7 +817,7 @@ number-is-nan@^1.0.0: resolved "https://registry.yarnpkg.com/number-is-nan/-/number-is-nan-1.0.1.tgz#097b602b53422a522c1afb8790318336941a011d" integrity sha512-4jbtZXNAsfZbAHiiqjLPBiCl16dES1zI4Hpzzxw61Tk+loF+sBDBKx1ICKKKwIqQ7M0mFn1TmkN7euSncWgHiQ== -object-assign@^4.0.1, object-assign@^4.1.0, object-assign@^4.1.1: +object-assign@^4.1.0, object-assign@^4.1.1: version "4.1.1" resolved "https://registry.yarnpkg.com/object-assign/-/object-assign-4.1.1.tgz#2109adc7965887cfc05cbbd442cac8bfbb360863" integrity sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg== @@ -810,16 +839,11 @@ picocolors@^1.0.0: resolved "https://registry.yarnpkg.com/picocolors/-/picocolors-1.0.0.tgz#cb5bdc74ff3f51892236eaf79d68bc44564ab81c" integrity sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ== -picomatch@^2.2.2, picomatch@^2.3.1: +picomatch@^2.3.1: version "2.3.1" resolved "https://registry.yarnpkg.com/picomatch/-/picomatch-2.3.1.tgz#3ba3833733646d9d3e4995946c1365a67fb07a42" integrity sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA== -pirates@^4.0.1: - version "4.0.5" - resolved "https://registry.yarnpkg.com/pirates/-/pirates-4.0.5.tgz#feec352ea5c3268fb23a37c702ab1699f35a5f3b" - integrity sha512-8V9+HQPupnaXMA23c5hvl69zXvTwTzyAYasnkb0Tts4XvO4CliqONMOnvlq26rkhLC3nWDFBJf73LU1e1VZLaQ== - postcss@8.4.14: version "8.4.14" resolved "https://registry.yarnpkg.com/postcss/-/postcss-8.4.14.tgz#ee9274d5622b4858c1007a74d76e42e56fd21caf" @@ -930,14 +954,14 @@ scheduler@^0.23.0: loose-envify "^1.1.0" semver@^6.0.0: - version "6.3.0" - resolved "https://registry.yarnpkg.com/semver/-/semver-6.3.0.tgz#ee0a64c8af5e8ceea67687b133761e1becbd1d3d" - integrity sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw== + version "6.3.1" + resolved "https://registry.yarnpkg.com/semver/-/semver-6.3.1.tgz#556d2ef8689146e46dcea4bfdd095f3434dffcb4" + integrity sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA== semver@^7.3.5: - version "7.3.8" - resolved "https://registry.yarnpkg.com/semver/-/semver-7.3.8.tgz#07a78feafb3f7b32347d725e33de7e2a2df67798" - integrity sha512-NB1ctGL5rlHrPJtFDVIVzTyQylMLu9N9VICA6HSFJo8MCGVTMW6gfpicwKmmK/dAjTOrqu5l63JJOpDSrAis3A== + version "7.5.4" + resolved "https://registry.yarnpkg.com/semver/-/semver-7.5.4.tgz#483986ec4ed38e1c6c48c34894a9182dbff68a6e" + integrity sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA== dependencies: lru-cache "^6.0.0" @@ -956,6 +980,13 @@ source-map-js@^1.0.2: resolved "https://registry.yarnpkg.com/source-map-js/-/source-map-js-1.0.2.tgz#adbc361d9c62df380125e7f161f71c826f1e490c" integrity sha512-R0XvVJ9WusLiqTCEiGCmICCMplcCkIwwR11mOSD9CR5u+IXYdiseeEuXCVAjS54zqwkLcPNnmU4OeJ6tUrWhDw== +stacktrace-parser@^0.1.10: + version "0.1.10" + resolved "https://registry.yarnpkg.com/stacktrace-parser/-/stacktrace-parser-0.1.10.tgz#29fb0cae4e0d0b85155879402857a1639eb6051a" + integrity sha512-KJP1OCML99+8fhOHxwwzyWrlUuVX5GQ0ZpJTd1DFXhdkrvg1szxfHhawXUZ3g9TkXORQd4/WG68jMlQZ2p8wlg== + dependencies: + type-fest "^0.7.1" + string-width@^1.0.1: version "1.0.2" resolved "https://registry.yarnpkg.com/string-width/-/string-width-1.0.2.tgz#118bdf5b8cdc51a2a7e70d211e07e2b0b9b107d3" @@ -1009,18 +1040,6 @@ styled-jsx@5.1.0: dependencies: client-only "0.0.1" -sucrase@^3.20.0: - version "3.29.0" - resolved "https://registry.yarnpkg.com/sucrase/-/sucrase-3.29.0.tgz#3207c5bc1b980fdae1e539df3f8a8a518236da7d" - integrity sha512-bZPAuGA5SdFHuzqIhTAqt9fvNEo9rESqXIG3oiKdF8K4UmkQxC4KlNL3lVyAErXp+mPvUqZ5l13qx6TrDIGf3A== - dependencies: - commander "^4.0.0" - glob "7.1.6" - lines-and-columns "^1.1.6" - mz "^2.7.0" - pirates "^4.0.1" - ts-interface-checker "^0.1.9" - supports-color@^7.1.0: version "7.2.0" resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-7.2.0.tgz#1b7dcdcb32b8138801b3e478ba6a51caa89648da" @@ -1040,20 +1059,6 @@ tar@^6.1.11: mkdirp "^1.0.3" yallist "^4.0.0" -thenify-all@^1.0.0: - version "1.6.0" - resolved "https://registry.yarnpkg.com/thenify-all/-/thenify-all-1.6.0.tgz#1a1918d402d8fc3f98fbf234db0bcc8cc10e9726" - integrity sha512-RNxQH/qI8/t3thXJDwcstUO4zeqo64+Uy/+sNVRBx4Xn2OX+OZ9oP+iJnNFqplFra2ZUVeKCSa2oVWi3T4uVmA== - dependencies: - thenify ">= 3.1.0 < 4" - -"thenify@>= 3.1.0 < 4": - version "3.3.1" - resolved "https://registry.yarnpkg.com/thenify/-/thenify-3.3.1.tgz#8932e686a4066038a016dd9e2ca46add9838a95f" - integrity sha512-RVZSIV5IG10Hk3enotrhvz0T9em6cyHBLkH/YAZuKqd8hRkKhSfCGIcP2KUY0EPxndzANBmNllzWPwak+bheSw== - dependencies: - any-promise "^1.0.0" - to-regex-range@^5.0.1: version "5.0.1" resolved "https://registry.yarnpkg.com/to-regex-range/-/to-regex-range-5.0.1.tgz#1648c44aae7c8d988a326018ed72f5b4dd0392e4" @@ -1066,21 +1071,21 @@ tr46@~0.0.3: resolved "https://registry.yarnpkg.com/tr46/-/tr46-0.0.3.tgz#8184fd347dac9cdc185992f3a6622e14b9d9ab6a" integrity sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw== -ts-interface-checker@^0.1.9: - version "0.1.13" - resolved "https://registry.yarnpkg.com/ts-interface-checker/-/ts-interface-checker-0.1.13.tgz#784fd3d679722bc103b1b4b8030bcddb5db2a699" - integrity sha512-Y/arvbn+rrz3JCKl9C4kVNfTfSm2/mEp5FSz5EsZSANGPSlQrpRI5M4PKF+mJnE52jOO90PnPSc3Ur3bTQw0gA== - -tslib@^1.9.3: - version "1.14.1" - resolved "https://registry.yarnpkg.com/tslib/-/tslib-1.14.1.tgz#cf2d38bdc34a134bcaf1091c41f6619e2f672d00" - integrity sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg== - tslib@^2.4.0: version "2.4.1" resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.4.1.tgz#0d0bfbaac2880b91e22df0768e55be9753a5b17e" integrity sha512-tGyy4dAjRIEwI7BzsB0lynWgOpfqjUdq91XXAlIWD2OwKBH7oCl/GZG/HT4BOHrTlPMOASlMQ7veyTqpmRcrNA== +"tslib@^2.4.1 || ^1.9.3": + version "2.6.0" + resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.6.0.tgz#b295854684dbda164e181d259a22cd779dcd7bc3" + integrity sha512-7At1WUettjcSRHXCyYtTselblcHl9PJFFVKiCAy/bY97+BPZXSQ2wbq0P9s8tK2G7dFQfNnlJnPAiArVBVBsfA== + +type-fest@^0.7.1: + version "0.7.1" + resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.7.1.tgz#8dda65feaf03ed78f0a3f9678f1869147f7c5c48" + integrity sha512-Ne2YiiGN8bmrmJJEuTWTLJR32nh/JdL1+PSicowtNb0WFpn59GK8/lfD61bVtzguz7b3PBt74nxpv/Pw5po5Rg== + use-sync-external-store@1.2.0: version "1.2.0" resolved "https://registry.yarnpkg.com/use-sync-external-store/-/use-sync-external-store-1.2.0.tgz#7dbefd6ef3fe4e767a0cf5d7287aacfb5846928a" diff --git a/packages/nextjs/test/integration/sentry.client.config.js b/packages/nextjs/test/integration/sentry.client.config.js index e8da7a90914b..dbb6e760ea8d 100644 --- a/packages/nextjs/test/integration/sentry.client.config.js +++ b/packages/nextjs/test/integration/sentry.client.config.js @@ -3,7 +3,7 @@ import { Integrations } from '@sentry/tracing'; Sentry.init({ dsn: 'https://public@dsn.ingest.sentry.io/1337', - tracesSampleRate: 1, + tracesSampler: () => true, debug: process.env.SDK_DEBUG, integrations: [ diff --git a/packages/nextjs/test/integration/sentry.edge.config.js b/packages/nextjs/test/integration/sentry.edge.config.js index 8d83922b637d..36600e702048 100644 --- a/packages/nextjs/test/integration/sentry.edge.config.js +++ b/packages/nextjs/test/integration/sentry.edge.config.js @@ -2,6 +2,7 @@ import * as Sentry from '@sentry/nextjs'; Sentry.init({ dsn: 'https://public@dsn.ingest.sentry.io/1337', - tracesSampleRate: 1, + tracesSampleRate: 1.0, + tracePropagationTargets: ['http://example.com'], debug: process.env.SDK_DEBUG, }); diff --git a/packages/nextjs/test/integration/sentry.server.config.js b/packages/nextjs/test/integration/sentry.server.config.js index da0759f4475e..54c5db73a1a2 100644 --- a/packages/nextjs/test/integration/sentry.server.config.js +++ b/packages/nextjs/test/integration/sentry.server.config.js @@ -2,7 +2,8 @@ import * as Sentry from '@sentry/nextjs'; Sentry.init({ dsn: 'https://public@dsn.ingest.sentry.io/1337', - tracesSampleRate: 1, + tracesSampleRate: 1.0, + tracePropagationTargets: ['http://example.com'], debug: process.env.SDK_DEBUG, integrations: defaults => [ diff --git a/packages/node-integration-tests/suites/express/handle-error/test.ts b/packages/node-integration-tests/suites/express/handle-error/test.ts index e8be4081203c..d9eb59a72785 100644 --- a/packages/node-integration-tests/suites/express/handle-error/test.ts +++ b/packages/node-integration-tests/suites/express/handle-error/test.ts @@ -11,8 +11,8 @@ test('should capture and send Express controller error.', async () => { values: [ { mechanism: { - type: 'generic', - handled: true, + type: 'middleware', + handled: false, }, type: 'Error', value: 'test_error', diff --git a/packages/node-integration-tests/suites/express/sentry-trace/baggage-header-assign/test.ts b/packages/node-integration-tests/suites/express/sentry-trace/baggage-header-assign/test.ts index 65aea16c60a1..79b98772fe39 100644 --- a/packages/node-integration-tests/suites/express/sentry-trace/baggage-header-assign/test.ts +++ b/packages/node-integration-tests/suites/express/sentry-trace/baggage-header-assign/test.ts @@ -76,8 +76,8 @@ test('Should populate and propagate sentry baggage if sentry-trace header does n test_data: { host: 'somewhere.not.sentry', // TraceId changes, hence we only expect that the string contains the traceid key - baggage: expect.stringContaining( - 'sentry-environment=prod,sentry-release=1.0,sentry-transaction=GET%20%2Ftest%2Fexpress,sentry-public_key=public,sentry-trace_id=', + baggage: expect.stringMatching( + /sentry-environment=prod,sentry-release=1.0,sentry-public_key=public,sentry-trace_id=[\S]*,sentry-sample_rate=1,sentry-transaction=GET%20%2Ftest%2Fexpress/, ), }, }); @@ -95,8 +95,8 @@ test('Should populate Sentry and ignore 3rd party content if sentry-trace header test_data: { host: 'somewhere.not.sentry', // TraceId changes, hence we only expect that the string contains the traceid key - baggage: expect.stringContaining( - 'sentry-environment=prod,sentry-release=1.0,sentry-transaction=GET%20%2Ftest%2Fexpress,sentry-public_key=public,sentry-trace_id=', + baggage: expect.stringMatching( + /sentry-environment=prod,sentry-release=1.0,sentry-public_key=public,sentry-trace_id=[\S]*,sentry-sample_rate=1,sentry-transaction=GET%20%2Ftest%2Fexpress/, ), }, }); diff --git a/packages/node-integration-tests/suites/express/sentry-trace/baggage-header-out/server.ts b/packages/node-integration-tests/suites/express/sentry-trace/baggage-header-out/server.ts index f582288f8314..750f49b40210 100644 --- a/packages/node-integration-tests/suites/express/sentry-trace/baggage-header-out/server.ts +++ b/packages/node-integration-tests/suites/express/sentry-trace/baggage-header-out/server.ts @@ -12,6 +12,7 @@ Sentry.init({ dsn: 'https://public@dsn.ingest.sentry.io/1337', release: '1.0', environment: 'prod', + tracePropagationTargets: [/^(?!.*express).*$/], // eslint-disable-next-line deprecation/deprecation integrations: [new Sentry.Integrations.Http({ tracing: true }), new Tracing.Integrations.Express({ app })], tracesSampleRate: 1.0, diff --git a/packages/node-integration-tests/suites/express/sentry-trace/baggage-header-out/test.ts b/packages/node-integration-tests/suites/express/sentry-trace/baggage-header-out/test.ts index 71defa704bba..4a3303af9863 100644 --- a/packages/node-integration-tests/suites/express/sentry-trace/baggage-header-out/test.ts +++ b/packages/node-integration-tests/suites/express/sentry-trace/baggage-header-out/test.ts @@ -13,8 +13,9 @@ test('should attach a `baggage` header to an outgoing request.', async () => { test_data: { host: 'somewhere.not.sentry', baggage: - 'sentry-environment=prod,sentry-release=1.0,sentry-transaction=GET%20%2Ftest%2Fexpress,sentry-user_segment=SegmentA' + - ',sentry-public_key=public,sentry-trace_id=86f39e84263a4de99c326acab3bfe3bd,sentry-sample_rate=1', + 'sentry-environment=prod,sentry-release=1.0,sentry-user_segment=SegmentA,sentry-public_key=public' + + ',sentry-trace_id=86f39e84263a4de99c326acab3bfe3bd,sentry-sample_rate=1,sentry-transaction=GET%20%2Ftest%2Fexpress' + + ',sentry-sampled=true', }, }); }); diff --git a/packages/node-integration-tests/suites/express/sentry-trace/baggage-other-vendors-with-sentry-entries/server.ts b/packages/node-integration-tests/suites/express/sentry-trace/baggage-other-vendors-with-sentry-entries/server.ts index 6674e2a1bb77..93738d4c0a12 100644 --- a/packages/node-integration-tests/suites/express/sentry-trace/baggage-other-vendors-with-sentry-entries/server.ts +++ b/packages/node-integration-tests/suites/express/sentry-trace/baggage-other-vendors-with-sentry-entries/server.ts @@ -12,6 +12,8 @@ Sentry.init({ dsn: 'https://public@dsn.ingest.sentry.io/1337', release: '1.0', environment: 'prod', + // disable requests to /express + tracePropagationTargets: [/^(?!.*express).*$/], // eslint-disable-next-line deprecation/deprecation integrations: [new Sentry.Integrations.Http({ tracing: true }), new Tracing.Integrations.Express({ app })], tracesSampleRate: 1.0, diff --git a/packages/node-integration-tests/suites/express/sentry-trace/baggage-other-vendors-with-sentry-entries/test.ts b/packages/node-integration-tests/suites/express/sentry-trace/baggage-other-vendors-with-sentry-entries/test.ts index 0c2c2d39c606..c20354944a6d 100644 --- a/packages/node-integration-tests/suites/express/sentry-trace/baggage-other-vendors-with-sentry-entries/test.ts +++ b/packages/node-integration-tests/suites/express/sentry-trace/baggage-other-vendors-with-sentry-entries/test.ts @@ -35,7 +35,7 @@ test('should ignore sentry-values in `baggage` header of a third party vendor an baggage: [ 'other=vendor,foo=bar,third=party,sentry-release=9.9.9,sentry-environment=staging,sentry-sample_rate=0.54,last=item', expect.stringMatching( - /sentry-environment=prod,sentry-release=1\.0,sentry-transaction=GET%20%2Ftest%2Fexpress,sentry-public_key=public,sentry-trace_id=[0-9a-f]{32},sentry-sample_rate=1/, + /sentry-environment=prod,sentry-release=1\.0,sentry-public_key=public,sentry-trace_id=[0-9a-f]{32},sentry-sample_rate=1,sentry-transaction=GET%20%2Ftest%2Fexpress/, ), ], }, diff --git a/packages/node-integration-tests/suites/express/sentry-trace/baggage-other-vendors/server.ts b/packages/node-integration-tests/suites/express/sentry-trace/baggage-other-vendors/server.ts index 86cfa033b79e..867bb0e6131e 100644 --- a/packages/node-integration-tests/suites/express/sentry-trace/baggage-other-vendors/server.ts +++ b/packages/node-integration-tests/suites/express/sentry-trace/baggage-other-vendors/server.ts @@ -12,6 +12,8 @@ Sentry.init({ dsn: 'https://public@dsn.ingest.sentry.io/1337', release: '1.0', environment: 'prod', + // disable requests to /express + tracePropagationTargets: [/^(?!.*express).*$/], // eslint-disable-next-line deprecation/deprecation integrations: [new Sentry.Integrations.Http({ tracing: true }), new Tracing.Integrations.Express({ app })], tracesSampleRate: 1.0, diff --git a/packages/node-integration-tests/suites/express/sentry-trace/baggage-transaction-name/server.ts b/packages/node-integration-tests/suites/express/sentry-trace/baggage-transaction-name/server.ts index 78e24ffa9f10..21738f3b3fb8 100644 --- a/packages/node-integration-tests/suites/express/sentry-trace/baggage-transaction-name/server.ts +++ b/packages/node-integration-tests/suites/express/sentry-trace/baggage-transaction-name/server.ts @@ -12,6 +12,8 @@ Sentry.init({ dsn: 'https://public@dsn.ingest.sentry.io/1337', release: '1.0', environment: 'prod', + // disable requests to /express + tracePropagationTargets: [/^(?!.*express).*$/], // eslint-disable-next-line deprecation/deprecation integrations: [new Sentry.Integrations.Http({ tracing: true }), new Tracing.Integrations.Express({ app })], tracesSampleRate: 1.0, diff --git a/packages/node-integration-tests/suites/express/sentry-trace/server.ts b/packages/node-integration-tests/suites/express/sentry-trace/server.ts index 76bb399e3bc0..1cfc02648f02 100644 --- a/packages/node-integration-tests/suites/express/sentry-trace/server.ts +++ b/packages/node-integration-tests/suites/express/sentry-trace/server.ts @@ -12,6 +12,7 @@ Sentry.init({ dsn: 'https://public@dsn.ingest.sentry.io/1337', release: '1.0', environment: 'prod', + tracePropagationTargets: [/^(?!.*express).*$/], // eslint-disable-next-line deprecation/deprecation integrations: [new Sentry.Integrations.Http({ tracing: true }), new Tracing.Integrations.Express({ app })], tracesSampleRate: 1.0, diff --git a/packages/node-integration-tests/suites/express/tracing/server.ts b/packages/node-integration-tests/suites/express/tracing/server.ts index e857621ad22e..1c56a81fef98 100644 --- a/packages/node-integration-tests/suites/express/tracing/server.ts +++ b/packages/node-integration-tests/suites/express/tracing/server.ts @@ -7,6 +7,8 @@ const app = express(); Sentry.init({ dsn: 'https://public@dsn.ingest.sentry.io/1337', release: '1.0', + // disable attaching headers to /test/* endpoints + tracePropagationTargets: [/^(?!.*test).*$/], integrations: [new Sentry.Integrations.Http({ tracing: true }), new Sentry.Integrations.Express({ app })], tracesSampleRate: 1.0, }); diff --git a/packages/node-integration-tests/utils/index.ts b/packages/node-integration-tests/utils/index.ts index cde2cb745cd9..88120853ee69 100644 --- a/packages/node-integration-tests/utils/index.ts +++ b/packages/node-integration-tests/utils/index.ts @@ -202,11 +202,11 @@ export class TestEnv { */ public async getAPIResponse( url?: string, - headers?: Record, + headers: Record = {}, endServer: boolean = true, ): Promise { try { - const { data } = await axios.get(url || this.url, { headers: headers || {} }); + const { data } = await axios.get(url || this.url, { headers }); return data; } finally { await Sentry.flush(); diff --git a/packages/node/src/client.ts b/packages/node/src/client.ts index ccdbeda279e1..50af36448046 100644 --- a/packages/node/src/client.ts +++ b/packages/node/src/client.ts @@ -1,13 +1,22 @@ import type { Scope } from '@sentry/core'; -import { addTracingExtensions, BaseClient, createCheckInEnvelope, SDK_VERSION, SessionFlusher } from '@sentry/core'; +import { + addTracingExtensions, + BaseClient, + createCheckInEnvelope, + getDynamicSamplingContextFromClient, + SDK_VERSION, + SessionFlusher, +} from '@sentry/core'; import type { CheckIn, + DynamicSamplingContext, Event, EventHint, MonitorConfig, SerializedCheckIn, Severity, SeverityLevel, + TraceContext, } from '@sentry/types'; import { logger, resolvedSyncPromise, uuid4 } from '@sentry/utils'; import * as os from 'os'; @@ -154,7 +163,7 @@ export class NodeClient extends BaseClient { * to create a monitor automatically when sending a check in. * @returns A string representing the id of the check in. */ - public captureCheckIn(checkIn: CheckIn, monitorConfig?: MonitorConfig): string { + public captureCheckIn(checkIn: CheckIn, monitorConfig?: MonitorConfig, scope?: Scope): string { const id = checkIn.status !== 'in_progress' && checkIn.checkInId ? checkIn.checkInId : uuid4(); if (!this._isEnabled()) { __DEBUG_BUILD__ && logger.warn('SDK not enabled, will not capture checkin.'); @@ -185,7 +194,20 @@ export class NodeClient extends BaseClient { }; } - const envelope = createCheckInEnvelope(serializedCheckIn, this.getSdkMetadata(), tunnel, this.getDsn()); + const [dynamicSamplingContext, traceContext] = this._getTraceInfoFromScope(scope); + if (traceContext) { + serializedCheckIn.contexts = { + trace: traceContext, + }; + } + + const envelope = createCheckInEnvelope( + serializedCheckIn, + dynamicSamplingContext, + this.getSdkMetadata(), + tunnel, + this.getDsn(), + ); __DEBUG_BUILD__ && logger.info('Sending checkin:', checkIn.monitorSlug, checkIn.status); void this._sendEnvelope(envelope); @@ -220,4 +242,30 @@ export class NodeClient extends BaseClient { this._sessionFlusher.incrementSessionStatusCount(); } } + + /** Extract trace information from scope */ + private _getTraceInfoFromScope( + scope: Scope | undefined, + ): [dynamicSamplingContext: Partial | undefined, traceContext: TraceContext | undefined] { + if (!scope) { + return [undefined, undefined]; + } + + const span = scope.getSpan(); + if (span) { + return [span?.transaction?.getDynamicSamplingContext(), span?.getTraceContext()]; + } + + const { traceId, spanId, parentSpanId, dsc } = scope.getPropagationContext(); + const traceContext: TraceContext = { + trace_id: traceId, + span_id: spanId, + parent_span_id: parentSpanId, + }; + if (dsc) { + return [dsc, traceContext]; + } + + return [getDynamicSamplingContextFromClient(traceId, this, scope), traceContext]; + } } diff --git a/packages/node/src/handlers.ts b/packages/node/src/handlers.ts index 95a4cfe65e38..6d2545c3bf9d 100644 --- a/packages/node/src/handlers.ts +++ b/packages/node/src/handlers.ts @@ -10,14 +10,14 @@ import { import type { Span } from '@sentry/types'; import type { AddRequestDataToEventOptions } from '@sentry/utils'; import { + addExceptionMechanism, addRequestDataToTransaction, - baggageHeaderToDynamicSamplingContext, dropUndefinedKeys, extractPathForTransaction, - extractTraceparentData, isString, logger, normalize, + tracingContextFromHeaders, } from '@sentry/utils'; import type * as http from 'http'; @@ -62,11 +62,13 @@ export function tracingHandler(): ( return next(); } - // If there is a trace header set, we extract the data from it (parentSpanId, traceId, and sampling decision) - const traceparentData = - req.headers && isString(req.headers['sentry-trace']) && extractTraceparentData(req.headers['sentry-trace']); - const incomingBaggageHeaders = req.headers?.baggage; - const dynamicSamplingContext = baggageHeaderToDynamicSamplingContext(incomingBaggageHeaders); + const sentryTrace = req.headers && isString(req.headers['sentry-trace']) ? req.headers['sentry-trace'] : undefined; + const baggage = req.headers?.baggage; + const { traceparentData, dynamicSamplingContext, propagationContext } = tracingContextFromHeaders( + sentryTrace, + baggage, + ); + hub.getScope().setPropagationContext(propagationContext); const [name, source] = extractPathForTransaction(req, { path: true, method: true }); const transaction = startTransaction( @@ -304,6 +306,11 @@ export function errorHandler(options?: { } } + _scope.addEventProcessor(event => { + addExceptionMechanism(event, { type: 'middleware', handled: false }); + return event; + }); + const eventId = captureException(error); // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access (res as any).sentry = eventId; diff --git a/packages/node/src/index.ts b/packages/node/src/index.ts index 0e57936a02a9..1dd778e21199 100644 --- a/packages/node/src/index.ts +++ b/packages/node/src/index.ts @@ -60,6 +60,7 @@ export { makeNodeTransport } from './transports'; export { defaultIntegrations, init, defaultStackParser, lastEventId, flush, close, getSentryRelease } from './sdk'; export { addRequestDataToEvent, DEFAULT_USER_INCLUDES, extractRequestData } from './requestdata'; export { deepReadDirSync } from './utils'; +export { getModuleFromFilename } from './module'; import { Integrations as CoreIntegrations } from '@sentry/core'; diff --git a/packages/node/src/integrations/http.ts b/packages/node/src/integrations/http.ts index 54d761861348..b210500e90e0 100644 --- a/packages/node/src/integrations/http.ts +++ b/packages/node/src/integrations/http.ts @@ -1,14 +1,26 @@ import type { Hub } from '@sentry/core'; -import { getCurrentHub } from '@sentry/core'; -import type { EventProcessor, Integration, SanitizedRequestData, Span, TracePropagationTargets } from '@sentry/types'; -import { dynamicSamplingContextToSentryBaggageHeader, fill, logger, stringMatchesSomePattern } from '@sentry/utils'; +import { getCurrentHub, getDynamicSamplingContextFromClient } from '@sentry/core'; +import type { + DynamicSamplingContext, + EventProcessor, + Integration, + SanitizedRequestData, + TracePropagationTargets, +} from '@sentry/types'; +import { + dynamicSamplingContextToSentryBaggageHeader, + fill, + generateSentryTraceHeader, + logger, + stringMatchesSomePattern, +} from '@sentry/utils'; import type * as http from 'http'; import type * as https from 'https'; import { LRUMap } from 'lru_map'; import type { NodeClient } from '../client'; import { NODE_VERSION } from '../nodeVersion'; -import type { RequestMethod, RequestMethodArgs } from './utils/http'; +import type { RequestMethod, RequestMethodArgs, RequestOptions } from './utils/http'; import { cleanSpanDescription, extractRawUrl, extractUrl, isSentryRequest, normalizeRequestArgs } from './utils/http'; interface TracingOptions { @@ -96,13 +108,20 @@ export class Http implements Integration { return; } - // TODO (v8): `tracePropagationTargets` and `shouldCreateSpanForRequest` will be removed from clientOptions - // and we will no longer have to do this optional merge, we can just pass `this._tracing` directly. - const tracingOptions = this._tracing ? { ...clientOptions, ...this._tracing } : undefined; + const shouldCreateSpanForRequest = + // eslint-disable-next-line deprecation/deprecation + this._tracing?.shouldCreateSpanForRequest || clientOptions?.shouldCreateSpanForRequest; + // eslint-disable-next-line deprecation/deprecation + const tracePropagationTargets = clientOptions?.tracePropagationTargets || this._tracing?.tracePropagationTargets; // eslint-disable-next-line @typescript-eslint/no-var-requires const httpModule = require('http'); - const wrappedHttpHandlerMaker = _createWrappedRequestMethodFactory(this._breadcrumbs, tracingOptions, httpModule); + const wrappedHttpHandlerMaker = _createWrappedRequestMethodFactory( + httpModule, + this._breadcrumbs, + shouldCreateSpanForRequest, + tracePropagationTargets, + ); fill(httpModule, 'get', wrappedHttpHandlerMaker); fill(httpModule, 'request', wrappedHttpHandlerMaker); @@ -113,9 +132,10 @@ export class Http implements Integration { // eslint-disable-next-line @typescript-eslint/no-var-requires const httpsModule = require('https'); const wrappedHttpsHandlerMaker = _createWrappedRequestMethodFactory( - this._breadcrumbs, - tracingOptions, httpsModule, + this._breadcrumbs, + shouldCreateSpanForRequest, + tracePropagationTargets, ); fill(httpsModule, 'get', wrappedHttpsHandlerMaker); fill(httpsModule, 'request', wrappedHttpsHandlerMaker); @@ -138,16 +158,17 @@ type WrappedRequestMethodFactory = (original: OriginalRequestMethod) => WrappedR * @returns A function which accepts the exiting handler and returns a wrapped handler */ function _createWrappedRequestMethodFactory( - breadcrumbsEnabled: boolean, - tracingOptions: TracingOptions | undefined, httpModule: typeof http | typeof https, + breadcrumbsEnabled: boolean, + shouldCreateSpanForRequest: ((url: string) => boolean) | undefined, + tracePropagationTargets: TracePropagationTargets | undefined, ): WrappedRequestMethodFactory { // We're caching results so we don't have to recompute regexp every time we create a request. const createSpanUrlMap = new LRUMap(100); const headersUrlMap = new LRUMap(100); const shouldCreateSpan = (url: string): boolean => { - if (tracingOptions?.shouldCreateSpanForRequest === undefined) { + if (shouldCreateSpanForRequest === undefined) { return true; } @@ -156,14 +177,13 @@ function _createWrappedRequestMethodFactory( return cachedDecision; } - const decision = tracingOptions.shouldCreateSpanForRequest(url); + const decision = shouldCreateSpanForRequest(url); createSpanUrlMap.set(url, decision); return decision; }; const shouldAttachTraceData = (url: string): boolean => { - // eslint-disable-next-line deprecation/deprecation - if (tracingOptions?.tracePropagationTargets === undefined) { + if (tracePropagationTargets === undefined) { return true; } @@ -172,12 +192,41 @@ function _createWrappedRequestMethodFactory( return cachedDecision; } - // eslint-disable-next-line deprecation/deprecation - const decision = stringMatchesSomePattern(url, tracingOptions.tracePropagationTargets); + const decision = stringMatchesSomePattern(url, tracePropagationTargets); headersUrlMap.set(url, decision); return decision; }; + /** + * Captures Breadcrumb based on provided request/response pair + */ + function addRequestBreadcrumb( + event: string, + requestSpanData: SanitizedRequestData, + req: http.ClientRequest, + res?: http.IncomingMessage, + ): void { + if (!getCurrentHub().getIntegration(Http)) { + return; + } + + getCurrentHub().addBreadcrumb( + { + category: 'http', + data: { + status_code: res && res.statusCode, + ...requestSpanData, + }, + type: 'http', + }, + { + event, + request: req, + response: res, + }, + ); + } + return function wrappedRequestMethodFactory(originalRequestMethod: OriginalRequestMethod): WrappedRequestMethod { return function wrappedMethod(this: unknown, ...args: RequestMethodArgs): http.ClientRequest { const requestArgs = normalizeRequestArgs(httpModule, args); @@ -191,74 +240,38 @@ function _createWrappedRequestMethodFactory( return originalRequestMethod.apply(httpModule, requestArgs); } - let requestSpan: Span | undefined; - const parentSpan = getCurrentHub().getScope().getSpan(); - - const method = requestOptions.method || 'GET'; - const requestSpanData: SanitizedRequestData = { - url: requestUrl, - 'http.method': method, - }; - if (requestOptions.hash) { - // strip leading "#" - requestSpanData['http.fragment'] = requestOptions.hash.substring(1); - } - if (requestOptions.search) { - // strip leading "?" - requestSpanData['http.query'] = requestOptions.search.substring(1); - } + const hub = getCurrentHub(); + const scope = hub.getScope(); + const parentSpan = scope.getSpan(); - if (tracingOptions && shouldCreateSpan(rawRequestUrl)) { - if (parentSpan) { - requestSpan = parentSpan.startChild({ - description: `${method} ${requestSpanData.url}`, + const data = getRequestSpanData(requestUrl, requestOptions); + + const requestSpan = shouldCreateSpan(rawRequestUrl) + ? parentSpan?.startChild({ op: 'http.client', - data: requestSpanData, - }); - - if (shouldAttachTraceData(rawRequestUrl)) { - const sentryTraceHeader = requestSpan.toTraceparent(); - __DEBUG_BUILD__ && - logger.log( - `[Tracing] Adding sentry-trace header ${sentryTraceHeader} to outgoing request to "${requestUrl}": `, - ); - - requestOptions.headers = { - ...requestOptions.headers, - 'sentry-trace': sentryTraceHeader, - }; - - if (parentSpan.transaction) { - const dynamicSamplingContext = parentSpan.transaction.getDynamicSamplingContext(); - const sentryBaggageHeader = dynamicSamplingContextToSentryBaggageHeader(dynamicSamplingContext); - - let newBaggageHeaderField; - if (!requestOptions.headers || !requestOptions.headers.baggage) { - newBaggageHeaderField = sentryBaggageHeader; - } else if (!sentryBaggageHeader) { - newBaggageHeaderField = requestOptions.headers.baggage; - } else if (Array.isArray(requestOptions.headers.baggage)) { - newBaggageHeaderField = [...requestOptions.headers.baggage, sentryBaggageHeader]; - } else { - // Type-cast explanation: - // Technically this the following could be of type `(number | string)[]` but for the sake of simplicity - // we say this is undefined behaviour, since it would not be baggage spec conform if the user did this. - newBaggageHeaderField = [requestOptions.headers.baggage, sentryBaggageHeader] as string[]; - } - - requestOptions.headers = { - ...requestOptions.headers, - // Setting a hader to `undefined` will crash in node so we only set the baggage header when it's defined - ...(newBaggageHeaderField && { baggage: newBaggageHeaderField }), - }; - } - } else { - __DEBUG_BUILD__ && - logger.log( - `[Tracing] Not adding sentry-trace header to outgoing request (${requestUrl}) due to mismatching tracePropagationTargets option.`, - ); - } + description: `${data['http.method']} ${data.url}`, + data, + }) + : undefined; + + if (shouldAttachTraceData(rawRequestUrl)) { + if (requestSpan) { + const sentryTraceHeader = requestSpan.toTraceparent(); + const dynamicSamplingContext = requestSpan?.transaction?.getDynamicSamplingContext(); + addHeadersToRequestOptions(requestOptions, requestUrl, sentryTraceHeader, dynamicSamplingContext); + } else { + const client = hub.getClient(); + const { traceId, sampled, dsc } = scope.getPropagationContext(); + const sentryTraceHeader = generateSentryTraceHeader(traceId, undefined, sampled); + const dynamicSamplingContext = + dsc || (client ? getDynamicSamplingContextFromClient(traceId, client, scope) : undefined); + addHeadersToRequestOptions(requestOptions, requestUrl, sentryTraceHeader, dynamicSamplingContext); } + } else { + __DEBUG_BUILD__ && + logger.log( + `[Tracing] Not adding sentry-trace header to outgoing request (${requestUrl}) due to mismatching tracePropagationTargets option.`, + ); } // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access @@ -268,7 +281,7 @@ function _createWrappedRequestMethodFactory( // eslint-disable-next-line @typescript-eslint/no-this-alias const req = this; if (breadcrumbsEnabled) { - addRequestBreadcrumb('response', requestSpanData, req, res); + addRequestBreadcrumb('response', data, req, res); } if (requestSpan) { if (res.statusCode) { @@ -283,7 +296,7 @@ function _createWrappedRequestMethodFactory( const req = this; if (breadcrumbsEnabled) { - addRequestBreadcrumb('error', requestSpanData, req); + addRequestBreadcrumb('error', data, req); } if (requestSpan) { requestSpan.setHttpStatus(500); @@ -295,32 +308,55 @@ function _createWrappedRequestMethodFactory( }; } -/** - * Captures Breadcrumb based on provided request/response pair - */ -function addRequestBreadcrumb( - event: string, - requestSpanData: SanitizedRequestData, - req: http.ClientRequest, - res?: http.IncomingMessage, +function addHeadersToRequestOptions( + requestOptions: RequestOptions, + requestUrl: string, + sentryTraceHeader: string, + dynamicSamplingContext: Partial | undefined, ): void { - if (!getCurrentHub().getIntegration(Http)) { - return; + __DEBUG_BUILD__ && + logger.log(`[Tracing] Adding sentry-trace header ${sentryTraceHeader} to outgoing request to "${requestUrl}": `); + const sentryBaggage = dynamicSamplingContextToSentryBaggageHeader(dynamicSamplingContext); + const sentryBaggageHeader = normalizeBaggageHeader(requestOptions, sentryBaggage); + requestOptions.headers = { + ...requestOptions.headers, + 'sentry-trace': sentryTraceHeader, + // Setting a header to `undefined` will crash in node so we only set the baggage header when it's defined + ...(sentryBaggageHeader && { baggage: sentryBaggageHeader }), + }; +} + +function getRequestSpanData(requestUrl: string, requestOptions: RequestOptions): SanitizedRequestData { + const method = requestOptions.method || 'GET'; + const data: SanitizedRequestData = { + url: requestUrl, + 'http.method': method, + }; + if (requestOptions.hash) { + // strip leading "#" + data['http.fragment'] = requestOptions.hash.substring(1); + } + if (requestOptions.search) { + // strip leading "?" + data['http.query'] = requestOptions.search.substring(1); } + return data; +} - getCurrentHub().addBreadcrumb( - { - category: 'http', - data: { - status_code: res && res.statusCode, - ...requestSpanData, - }, - type: 'http', - }, - { - event, - request: req, - response: res, - }, - ); +function normalizeBaggageHeader( + requestOptions: RequestOptions, + sentryBaggageHeader: string | undefined, +): string | string[] | undefined { + if (!requestOptions.headers || !requestOptions.headers.baggage) { + return sentryBaggageHeader; + } else if (!sentryBaggageHeader) { + return requestOptions.headers.baggage as string | string[]; + } else if (Array.isArray(requestOptions.headers.baggage)) { + return [...requestOptions.headers.baggage, sentryBaggageHeader]; + } + + // Type-cast explanation: + // Technically this the following could be of type `(number | string)[]` but for the sake of simplicity + // we say this is undefined behaviour, since it would not be baggage spec conform if the user did this. + return [requestOptions.headers.baggage, sentryBaggageHeader] as string[]; } diff --git a/packages/node/src/integrations/linkederrors.ts b/packages/node/src/integrations/linkederrors.ts index e2894a128be5..d6a130ce5d54 100644 --- a/packages/node/src/integrations/linkederrors.ts +++ b/packages/node/src/integrations/linkederrors.ts @@ -1,8 +1,6 @@ -import { addGlobalEventProcessor, getCurrentHub } from '@sentry/core'; -import type { Event, EventHint, Exception, ExtendedError, Integration, StackParser } from '@sentry/types'; -import { isInstanceOf, resolvedSyncPromise, SyncPromise } from '@sentry/utils'; +import type { Event, EventHint, EventProcessor, Hub, Integration } from '@sentry/types'; +import { applyAggregateErrorsToEvent } from '@sentry/utils'; -import type { NodeClient } from '../client'; import { exceptionFromError } from '../eventbuilder'; import { ContextLines } from './contextlines'; @@ -42,68 +40,33 @@ export class LinkedErrors implements Integration { /** * @inheritDoc */ - public setupOnce(): void { - addGlobalEventProcessor(async (event: Event, hint: EventHint) => { + public setupOnce(addGlobalEventProcessor: (callback: EventProcessor) => void, getCurrentHub: () => Hub): void { + addGlobalEventProcessor(async (event: Event, hint?: EventHint) => { const hub = getCurrentHub(); + const client = hub.getClient(); const self = hub.getIntegration(LinkedErrors); - const client = hub.getClient(); - if (client && self && self._handler && typeof self._handler === 'function') { - await self._handler(client.getOptions().stackParser, event, hint); - } - return event; - }); - } - /** - * @inheritDoc - */ - private _handler(stackParser: StackParser, event: Event, hint: EventHint): PromiseLike { - if (!event.exception || !event.exception.values || !isInstanceOf(hint.originalException, Error)) { - return resolvedSyncPromise(event); - } - - return new SyncPromise(resolve => { - void this._walkErrorTree(stackParser, hint.originalException as Error, this._key) - .then((linkedErrors: Exception[]) => { - if (event && event.exception && event.exception.values) { - event.exception.values = [...linkedErrors, ...event.exception.values]; - } - resolve(event); - }) - .then(null, () => { - resolve(event); - }); - }); - } - - /** - * @inheritDoc - */ - private async _walkErrorTree( - stackParser: StackParser, - error: ExtendedError, - key: string, - stack: Exception[] = [], - ): Promise { - if (!isInstanceOf(error[key], Error) || stack.length + 1 >= this._limit) { - return Promise.resolve(stack); - } + if (!client || !self) { + return event; + } - const exception = exceptionFromError(stackParser, error[key]); + applyAggregateErrorsToEvent( + exceptionFromError, + client.getOptions().stackParser, + self._key, + self._limit, + event, + hint, + ); - // If the ContextLines integration is enabled, we add source code context to linked errors - // because we can't guarantee the order that integrations are run. - const contextLines = getCurrentHub().getIntegration(ContextLines); - if (contextLines && exception.stacktrace?.frames) { - await contextLines.addSourceContextToFrames(exception.stacktrace.frames); - } + // If the ContextLines integration is enabled, we add source code context to linked errors + // because we can't guarantee the order that integrations are run. + const contextLines = getCurrentHub().getIntegration(ContextLines); + if (contextLines) { + await contextLines.addSourceContext(event); + } - return new Promise((resolve, reject) => { - void this._walkErrorTree(stackParser, error[key], key, [exception, ...stack]) - .then(resolve) - .then(null, () => { - reject(); - }); + return event; }); } } diff --git a/packages/node/src/integrations/undici/index.ts b/packages/node/src/integrations/undici/index.ts index 38f920283b7e..0c69dec37d3f 100644 --- a/packages/node/src/integrations/undici/index.ts +++ b/packages/node/src/integrations/undici/index.ts @@ -1,8 +1,9 @@ -import type { Hub } from '@sentry/core'; -import type { EventProcessor, Integration } from '@sentry/types'; +import { getCurrentHub, getDynamicSamplingContextFromClient } from '@sentry/core'; +import type { EventProcessor, Integration, Span } from '@sentry/types'; import { dynamicRequire, dynamicSamplingContextToSentryBaggageHeader, + generateSentryTraceHeader, getSanitizedUrlString, parseUrl, stringMatchesSomePattern, @@ -12,7 +13,13 @@ import { LRUMap } from 'lru_map'; import type { NodeClient } from '../../client'; import { NODE_VERSION } from '../../nodeVersion'; import { isSentryRequest } from '../utils/http'; -import type { DiagnosticsChannel, RequestCreateMessage, RequestEndMessage, RequestErrorMessage } from './types'; +import type { + DiagnosticsChannel, + RequestCreateMessage, + RequestEndMessage, + RequestErrorMessage, + RequestWithSentry, +} from './types'; export enum ChannelName { // https://github.com/nodejs/undici/blob/e6fc80f809d1217814c044f52ed40ef13f21e43c/docs/api/DiagnosticsChannel.md#undicirequestcreate @@ -81,7 +88,7 @@ export class Undici implements Integration { /** * @inheritDoc */ - public setupOnce(_addGlobalEventProcessor: (callback: EventProcessor) => void, getCurrentHub: () => Hub): void { + public setupOnce(_addGlobalEventProcessor: (callback: EventProcessor) => void): void { // Requires Node 16+ to use the diagnostics_channel API. if (NODE_VERSION.major && NODE_VERSION.major < 16) { return; @@ -99,169 +106,205 @@ export class Undici implements Integration { return; } - const shouldCreateSpan = (url: string): boolean => { - if (this._options.shouldCreateSpanForRequest === undefined) { + // https://github.com/nodejs/undici/blob/e6fc80f809d1217814c044f52ed40ef13f21e43c/docs/api/DiagnosticsChannel.md + ds.subscribe(ChannelName.RequestCreate, this._onRequestCreate); + ds.subscribe(ChannelName.RequestEnd, this._onRequestEnd); + ds.subscribe(ChannelName.RequestError, this._onRequestError); + } + + /** Helper that wraps shouldCreateSpanForRequest option */ + private _shouldCreateSpan(url: string): boolean { + if (this._options.shouldCreateSpanForRequest === undefined) { + return true; + } + + const cachedDecision = this._createSpanUrlMap.get(url); + if (cachedDecision !== undefined) { + return cachedDecision; + } + + const decision = this._options.shouldCreateSpanForRequest(url); + this._createSpanUrlMap.set(url, decision); + return decision; + } + + private _onRequestCreate = (message: unknown): void => { + const hub = getCurrentHub(); + if (!hub.getIntegration(Undici)) { + return; + } + + const { request } = message as RequestCreateMessage; + + const stringUrl = request.origin ? request.origin.toString() + request.path : request.path; + + if (isSentryRequest(stringUrl) || request.__sentry_span__ !== undefined) { + return; + } + + const client = hub.getClient(); + if (!client) { + return; + } + + const clientOptions = client.getOptions(); + const scope = hub.getScope(); + + const parentSpan = scope.getSpan(); + + const span = this._shouldCreateSpan(stringUrl) ? createRequestSpan(parentSpan, request, stringUrl) : undefined; + if (span) { + request.__sentry_span__ = span; + } + + const shouldAttachTraceData = (url: string): boolean => { + if (clientOptions.tracePropagationTargets === undefined) { return true; } - const cachedDecision = this._createSpanUrlMap.get(url); + const cachedDecision = this._headersUrlMap.get(url); if (cachedDecision !== undefined) { return cachedDecision; } - const decision = this._options.shouldCreateSpanForRequest(url); - this._createSpanUrlMap.set(url, decision); + const decision = stringMatchesSomePattern(url, clientOptions.tracePropagationTargets); + this._headersUrlMap.set(url, decision); return decision; }; - // https://github.com/nodejs/undici/blob/e6fc80f809d1217814c044f52ed40ef13f21e43c/docs/api/DiagnosticsChannel.md - ds.subscribe(ChannelName.RequestCreate, message => { - const hub = getCurrentHub(); - if (!hub.getIntegration(Undici)) { - return; + if (shouldAttachTraceData(stringUrl)) { + if (span) { + const dynamicSamplingContext = span?.transaction?.getDynamicSamplingContext(); + const sentryBaggageHeader = dynamicSamplingContextToSentryBaggageHeader(dynamicSamplingContext); + + setHeadersOnRequest(request, span.toTraceparent(), sentryBaggageHeader); + } else { + const { traceId, sampled, dsc } = scope.getPropagationContext(); + const sentryTrace = generateSentryTraceHeader(traceId, undefined, sampled); + const dynamicSamplingContext = dsc || getDynamicSamplingContextFromClient(traceId, client, scope); + const sentryBaggageHeader = dynamicSamplingContextToSentryBaggageHeader(dynamicSamplingContext); + setHeadersOnRequest(request, sentryTrace, sentryBaggageHeader); } + } + }; - const { request } = message as RequestCreateMessage; + private _onRequestEnd = (message: unknown): void => { + const hub = getCurrentHub(); + if (!hub.getIntegration(Undici)) { + return; + } - const stringUrl = request.origin ? request.origin.toString() + request.path : request.path; - const url = parseUrl(stringUrl); + const { request, response } = message as RequestEndMessage; - if (isSentryRequest(stringUrl) || request.__sentry__ !== undefined) { - return; - } + const stringUrl = request.origin ? request.origin.toString() + request.path : request.path; - const client = hub.getClient(); - const scope = hub.getScope(); - - const activeSpan = scope.getSpan(); - - if (activeSpan && client) { - const clientOptions = client.getOptions(); - - if (shouldCreateSpan(stringUrl)) { - const method = request.method || 'GET'; - const data: Record = { - 'http.method': method, - }; - if (url.search) { - data['http.query'] = url.search; - } - if (url.hash) { - data['http.fragment'] = url.hash; - } - const span = activeSpan.startChild({ - op: 'http.client', - description: `${method} ${getSanitizedUrlString(url)}`, - data, - }); - request.__sentry__ = span; - - const shouldAttachTraceData = (url: string): boolean => { - if (clientOptions.tracePropagationTargets === undefined) { - return true; - } - - const cachedDecision = this._headersUrlMap.get(url); - if (cachedDecision !== undefined) { - return cachedDecision; - } - - const decision = stringMatchesSomePattern(url, clientOptions.tracePropagationTargets); - this._headersUrlMap.set(url, decision); - return decision; - }; - - if (shouldAttachTraceData(stringUrl)) { - request.addHeader('sentry-trace', span.toTraceparent()); - if (span.transaction) { - const dynamicSamplingContext = span.transaction.getDynamicSamplingContext(); - const sentryBaggageHeader = dynamicSamplingContextToSentryBaggageHeader(dynamicSamplingContext); - if (sentryBaggageHeader) { - request.addHeader('baggage', sentryBaggageHeader); - } - } - } - } - } - }); + if (isSentryRequest(stringUrl)) { + return; + } - ds.subscribe(ChannelName.RequestEnd, message => { - const hub = getCurrentHub(); - if (!hub.getIntegration(Undici)) { - return; - } + const span = request.__sentry_span__; + if (span) { + span.setHttpStatus(response.statusCode); + span.finish(); + } - const { request, response } = message as RequestEndMessage; + if (this._options.breadcrumbs) { + hub.addBreadcrumb( + { + category: 'http', + data: { + method: request.method, + status_code: response.statusCode, + url: stringUrl, + }, + type: 'http', + }, + { + event: 'response', + request, + response, + }, + ); + } + }; - const stringUrl = request.origin ? request.origin.toString() + request.path : request.path; + private _onRequestError = (message: unknown): void => { + const hub = getCurrentHub(); + if (!hub.getIntegration(Undici)) { + return; + } - if (isSentryRequest(stringUrl)) { - return; - } + const { request } = message as RequestErrorMessage; - const span = request.__sentry__; - if (span) { - span.setHttpStatus(response.statusCode); - span.finish(); - } + const stringUrl = request.origin ? request.origin.toString() + request.path : request.path; - if (this._options.breadcrumbs) { - hub.addBreadcrumb( - { - category: 'http', - data: { - method: request.method, - status_code: response.statusCode, - url: stringUrl, - }, - type: 'http', - }, - { - event: 'response', - request, - response, - }, - ); - } - }); + if (isSentryRequest(stringUrl)) { + return; + } - ds.subscribe(ChannelName.RequestError, message => { - const hub = getCurrentHub(); - if (!hub.getIntegration(Undici)) { - return; - } + const span = request.__sentry_span__; + if (span) { + span.setStatus('internal_error'); + span.finish(); + } - const { request } = message as RequestErrorMessage; + if (this._options.breadcrumbs) { + hub.addBreadcrumb( + { + category: 'http', + data: { + method: request.method, + url: stringUrl, + }, + level: 'error', + type: 'http', + }, + { + event: 'error', + request, + }, + ); + } + }; +} - const stringUrl = request.origin ? request.origin.toString() + request.path : request.path; +function setHeadersOnRequest( + request: RequestWithSentry, + sentryTrace: string, + sentryBaggageHeader: string | undefined, +): void { + if (request.__sentry_has_headers__) { + return; + } - if (isSentryRequest(stringUrl)) { - return; - } + request.addHeader('sentry-trace', sentryTrace); + if (sentryBaggageHeader) { + request.addHeader('baggage', sentryBaggageHeader); + } - const span = request.__sentry__; - if (span) { - span.setStatus('internal_error'); - span.finish(); - } + request.__sentry_has_headers__ = true; +} - if (this._options.breadcrumbs) { - hub.addBreadcrumb( - { - category: 'http', - data: { - method: request.method, - url: stringUrl, - }, - level: 'error', - type: 'http', - }, - { - event: 'error', - request, - }, - ); - } - }); +function createRequestSpan( + activeSpan: Span | undefined, + request: RequestWithSentry, + stringUrl: string, +): Span | undefined { + const url = parseUrl(stringUrl); + + const method = request.method || 'GET'; + const data: Record = { + 'http.method': method, + }; + if (url.search) { + data['http.query'] = url.search; + } + if (url.hash) { + data['http.fragment'] = url.hash; } + return activeSpan?.startChild({ + op: 'http.client', + description: `${method} ${getSanitizedUrlString(url)}`, + data, + }); } diff --git a/packages/node/src/integrations/undici/types.ts b/packages/node/src/integrations/undici/types.ts index c2d2db125195..f56e708f456c 100644 --- a/packages/node/src/integrations/undici/types.ts +++ b/packages/node/src/integrations/undici/types.ts @@ -234,7 +234,8 @@ export interface UndiciResponse { } export interface RequestWithSentry extends UndiciRequest { - __sentry__?: Span; + __sentry_span__?: Span; + __sentry_has_headers__?: boolean; } export interface RequestCreateMessage { diff --git a/packages/node/src/module.ts b/packages/node/src/module.ts index 595ee09bbe49..44bff87a02d2 100644 --- a/packages/node/src/module.ts +++ b/packages/node/src/module.ts @@ -10,7 +10,7 @@ function normalizeWindowsPath(path: string): string { } /** Gets the module from a filename */ -export function getModule( +export function getModuleFromFilename( filename: string | undefined, normalizeWindowsPathSeparator: boolean = isWindowsPlatform, ): string | undefined { diff --git a/packages/node/src/sdk.ts b/packages/node/src/sdk.ts index 9e55fd4b5a84..06be10d848b6 100644 --- a/packages/node/src/sdk.ts +++ b/packages/node/src/sdk.ts @@ -13,6 +13,7 @@ import { logger, nodeStackLineParser, stackParserFromStackParserOptions, + tracingContextFromHeaders, } from '@sentry/utils'; import { setNodeAsyncContextStrategy } from './async'; @@ -30,7 +31,7 @@ import { RequestData, Undici, } from './integrations'; -import { getModule } from './module'; +import { getModuleFromFilename } from './module'; import { makeNodeTransport } from './transports'; import type { NodeClientOptions, NodeOptions } from './types'; @@ -129,8 +130,9 @@ export function init(options: NodeOptions = {}): void { options.dsn = process.env.SENTRY_DSN; } - if (options.tracesSampleRate === undefined && process.env.SENTRY_TRACES_SAMPLE_RATE) { - const tracesSampleRate = parseFloat(process.env.SENTRY_TRACES_SAMPLE_RATE); + const sentryTracesSampleRate = process.env.SENTRY_TRACES_SAMPLE_RATE; + if (options.tracesSampleRate === undefined && sentryTracesSampleRate) { + const tracesSampleRate = parseFloat(sentryTracesSampleRate); if (isFinite(tracesSampleRate)) { options.tracesSampleRate = tracesSampleRate; } @@ -171,6 +173,8 @@ export function init(options: NodeOptions = {}): void { if (options.autoSessionTracking) { startSessionTracking(); } + + updateScopeFromEnvVariables(); } /** @@ -263,7 +267,7 @@ export function getSentryRelease(fallback?: string): string | undefined { } /** Node.js stack parser */ -export const defaultStackParser: StackParser = createStackParser(nodeStackLineParser(getModule)); +export const defaultStackParser: StackParser = createStackParser(nodeStackLineParser(getModuleFromFilename)); /** * Enable automatic Session Tracking for the node process. @@ -285,3 +289,19 @@ function startSessionTracking(): void { if (session && !terminalStates.includes(session.status)) hub.endSession(); }); } + +/** + * Update scope and propagation context based on environmental variables. + * + * See https://github.com/getsentry/rfcs/blob/main/text/0071-continue-trace-over-process-boundaries.md + * for more details. + */ +function updateScopeFromEnvVariables(): void { + const sentryUseEnvironment = (process.env.SENTRY_USE_ENVIRONMENT || '').toLowerCase(); + if (!['false', 'n', 'no', 'off', '0'].includes(sentryUseEnvironment)) { + const sentryTraceEnv = process.env.SENTRY_TRACE; + const baggageEnv = process.env.SENTRY_BAGGAGE; + const { propagationContext } = tracingContextFromHeaders(sentryTraceEnv, baggageEnv); + getCurrentHub().getScope().setPropagationContext(propagationContext); + } +} diff --git a/packages/node/test/async/domain.test.ts b/packages/node/test/async/domain.test.ts index 84c3362b9882..8794a938ce50 100644 --- a/packages/node/test/async/domain.test.ts +++ b/packages/node/test/async/domain.test.ts @@ -1,5 +1,5 @@ -import { getCurrentHub, Hub, runWithAsyncContext, setAsyncContextStrategy } from '@sentry/core'; -import * as domain from 'domain'; +import type { Hub } from '@sentry/core'; +import { getCurrentHub, runWithAsyncContext, setAsyncContextStrategy } from '@sentry/core'; import { setDomainAsyncContextStrategy } from '../../src/async/domain'; @@ -9,13 +9,6 @@ describe('domains', () => { setAsyncContextStrategy(undefined); }); - test('without domain', () => { - // @ts-ignore property active does not exist on domain - expect(domain.active).toBeFalsy(); - const hub = getCurrentHub(); - expect(hub).toEqual(new Hub()); - }); - test('hub scope inheritance', () => { setDomainAsyncContextStrategy(); diff --git a/packages/node/test/async/hooks.test.ts b/packages/node/test/async/hooks.test.ts index 8c4c5decab76..a08271230579 100644 --- a/packages/node/test/async/hooks.test.ts +++ b/packages/node/test/async/hooks.test.ts @@ -1,4 +1,5 @@ -import { getCurrentHub, Hub, runWithAsyncContext, setAsyncContextStrategy } from '@sentry/core'; +import type { Hub } from '@sentry/core'; +import { getCurrentHub, runWithAsyncContext, setAsyncContextStrategy } from '@sentry/core'; import { setHooksAsyncContextStrategy } from '../../src/async/hooks'; import { conditionalTest } from '../utils'; @@ -9,11 +10,6 @@ conditionalTest({ min: 12 })('async_hooks', () => { setAsyncContextStrategy(undefined); }); - test('without context', () => { - const hub = getCurrentHub(); - expect(hub).toEqual(new Hub()); - }); - test('without strategy hubs should be equal', () => { runWithAsyncContext(() => { const hub1 = getCurrentHub(); diff --git a/packages/node/test/context-lines.test.ts b/packages/node/test/context-lines.test.ts index b469796214b1..cfdd44e8b840 100644 --- a/packages/node/test/context-lines.test.ts +++ b/packages/node/test/context-lines.test.ts @@ -107,7 +107,8 @@ describe('ContextLines', () => { expect(readFileSpy).toHaveBeenCalledTimes(0); }); }); - test.only('does not attempt to readfile multiple times if it fails', async () => { + + test('does not attempt to readfile multiple times if it fails', async () => { expect.assertions(1); contextLines = new ContextLines({}); diff --git a/packages/node/test/handlers.test.ts b/packages/node/test/handlers.test.ts index 298d61cf1aac..d464342fe396 100644 --- a/packages/node/test/handlers.test.ts +++ b/packages/node/test/handlers.test.ts @@ -1,7 +1,7 @@ import type { Hub } from '@sentry/core'; import * as sentryCore from '@sentry/core'; import { setAsyncContextStrategy, Transaction } from '@sentry/core'; -import type { Event } from '@sentry/types'; +import type { Event, PropagationContext } from '@sentry/types'; import { SentryError } from '@sentry/utils'; import * as http from 'http'; @@ -209,6 +209,11 @@ describe('tracingHandler', () => { jest.restoreAllMocks(); }); + function getPropagationContext(): PropagationContext { + // @ts-expect-error accesing private property for test + return hub.getScope()._propagationContext; + } + it('creates a transaction when handling a request', () => { const startTransaction = jest.spyOn(sentryCore, 'startTransaction'); @@ -251,6 +256,13 @@ describe('tracingHandler', () => { const transaction = (res as any).__sentry_transaction; + expect(getPropagationContext()).toEqual({ + traceId: '12312012123120121231201212312012', + parentSpanId: '1121201211212012', + spanId: expect.any(String), + sampled: false, + }); + // since we have no tracesSampler defined, the default behavior (inherit if possible) applies expect(transaction.traceId).toEqual('12312012123120121231201212312012'); expect(transaction.parentSpanId).toEqual('1121201211212012'); @@ -260,18 +272,26 @@ describe('tracingHandler', () => { it("pulls parent's data from tracing and baggage headers on the request", () => { req.headers = { - 'sentry-trace': '12312012123120121231201212312012-1121201211212012-0', + 'sentry-trace': '12312012123120121231201212312012-1121201211212012-1', baggage: 'sentry-version=1.0,sentry-environment=production', }; sentryTracingMiddleware(req, res, next); + expect(getPropagationContext()).toEqual({ + traceId: '12312012123120121231201212312012', + parentSpanId: '1121201211212012', + spanId: expect.any(String), + sampled: true, + dsc: { version: '1.0', environment: 'production' }, + }); + const transaction = (res as any).__sentry_transaction; // since we have no tracesSampler defined, the default behavior (inherit if possible) applies expect(transaction.traceId).toEqual('12312012123120121231201212312012'); expect(transaction.parentSpanId).toEqual('1121201211212012'); - expect(transaction.sampled).toEqual(false); + expect(transaction.sampled).toEqual(true); expect(transaction.metadata?.dynamicSamplingContext).toStrictEqual({ version: '1.0', environment: 'production' }); }); @@ -283,6 +303,8 @@ describe('tracingHandler', () => { sentryTracingMiddleware(req, res, next); + expect(getPropagationContext().dsc).toEqual({ version: '1.0', environment: 'production' }); + const transaction = (res as any).__sentry_transaction; expect(transaction.metadata?.dynamicSamplingContext).toStrictEqual({ version: '1.0', environment: 'production' }); }); diff --git a/packages/node/test/index.test.ts b/packages/node/test/index.test.ts index ab9bd41adaf9..60b37b44acb1 100644 --- a/packages/node/test/index.test.ts +++ b/packages/node/test/index.test.ts @@ -488,4 +488,47 @@ describe('SentryNode initialization', () => { expect(instrumenter).toEqual('otel'); }); }); + + describe('propagation context', () => { + beforeEach(() => { + process.env.SENTRY_TRACE = '12312012123120121231201212312012-1121201211212012-0'; + process.env.SENTRY_BAGGAGE = 'sentry-release=1.0.0,sentry-environment=production'; + + getCurrentHub().getScope().clear(); + }); + + afterEach(() => { + delete process.env.SENTRY_TRACE; + delete process.env.SENTRY_BAGGAGE; + }); + + it('reads from environmental variables', () => { + init({ dsn }); + + // @ts-expect-error accessing private method for test + expect(getCurrentHub().getScope()._propagationContext).toEqual({ + traceId: '12312012123120121231201212312012', + parentSpanId: '1121201211212012', + spanId: expect.any(String), + sampled: false, + dsc: { + release: '1.0.0', + environment: 'production', + }, + }); + }); + + it.each(['false', 'False', 'FALSE', 'n', 'no', 'No', 'NO', 'off', 'Off', 'OFF', '0'])( + 'does not read from environmental variable if SENTRY_USE_ENVIRONMENT is set to %s', + useEnvValue => { + process.env.SENTRY_USE_ENVIRONMENT = useEnvValue; + init({ dsn }); + + // @ts-expect-error accessing private method for test + expect(getCurrentHub().getScope()._propagationContext.traceId).not.toEqual('12312012123120121231201212312012'); + + delete process.env.SENTRY_USE_ENVIRONMENT; + }, + ); + }); }); diff --git a/packages/node/test/integrations/http.test.ts b/packages/node/test/integrations/http.test.ts index 3f5a87d15363..5cbea24a9b01 100644 --- a/packages/node/test/integrations/http.test.ts +++ b/packages/node/test/integrations/http.test.ts @@ -54,6 +54,21 @@ describe('tracing', () => { return transaction; } + function getHub(customOptions: Partial = {}) { + const options = getDefaultNodeClientOptions({ + dsn: 'https://dogsarebadatkeepingsecrets@squirrelchasers.ingest.sentry.io/12312012', + tracesSampleRate: 1.0, + integrations: [new HttpIntegration({ tracing: true })], + release: '1.0.0', + environment: 'production', + ...customOptions, + }); + const hub = new Hub(new NodeClient(options)); + jest.spyOn(sentryCore, 'getCurrentHub').mockReturnValue(hub); + + return hub; + } + it("creates a span for each outgoing non-sentry request when there's a transaction on the scope", () => { nock('http://dogs.are.great').get('/').reply(200); @@ -114,9 +129,10 @@ describe('tracing', () => { const baggageHeader = request.getHeader('baggage') as string; expect(baggageHeader).toEqual( - 'sentry-environment=production,sentry-release=1.0.0,sentry-transaction=dogpark,' + + 'sentry-environment=production,sentry-release=1.0.0,' + 'sentry-user_segment=segmentA,sentry-public_key=dogsarebadatkeepingsecrets,' + - 'sentry-trace_id=12312012123120121231201212312012,sentry-sample_rate=1', + 'sentry-trace_id=12312012123120121231201212312012,sentry-sample_rate=1,' + + 'sentry-transaction=dogpark,sentry-sampled=true', ); }); @@ -128,10 +144,10 @@ describe('tracing', () => { const request = http.get({ host: 'http://dogs.are.great/', headers: { baggage: 'dog=great' } }); const baggageHeader = request.getHeader('baggage') as string; - expect(baggageHeader).toEqual([ - 'dog=great', - 'sentry-environment=production,sentry-release=1.0.0,sentry-transaction=dogpark,sentry-user_segment=segmentA,sentry-public_key=dogsarebadatkeepingsecrets,sentry-trace_id=12312012123120121231201212312012,sentry-sample_rate=1', - ]); + expect(baggageHeader[0]).toEqual('dog=great'); + expect(baggageHeader[1]).toEqual( + 'sentry-environment=production,sentry-release=1.0.0,sentry-user_segment=segmentA,sentry-public_key=dogsarebadatkeepingsecrets,sentry-trace_id=12312012123120121231201212312012,sentry-sample_rate=1,sentry-transaction=dogpark,sentry-sampled=true', + ); }); it('adds the transaction name to the the baggage header if a valid transaction source is set', async () => { @@ -144,7 +160,7 @@ describe('tracing', () => { expect(baggageHeader).toEqual([ 'dog=great', - 'sentry-environment=production,sentry-release=1.0.0,sentry-transaction=dogpark,sentry-user_segment=segmentA,sentry-public_key=dogsarebadatkeepingsecrets,sentry-trace_id=12312012123120121231201212312012,sentry-sample_rate=1', + 'sentry-environment=production,sentry-release=1.0.0,sentry-user_segment=segmentA,sentry-public_key=dogsarebadatkeepingsecrets,sentry-trace_id=12312012123120121231201212312012,sentry-sample_rate=1,sentry-transaction=dogpark,sentry-sampled=true', ]); }); @@ -158,10 +174,55 @@ describe('tracing', () => { expect(baggageHeader).toEqual([ 'dog=great', - 'sentry-environment=production,sentry-release=1.0.0,sentry-user_segment=segmentA,sentry-public_key=dogsarebadatkeepingsecrets,sentry-trace_id=12312012123120121231201212312012,sentry-sample_rate=1', + 'sentry-environment=production,sentry-release=1.0.0,sentry-user_segment=segmentA,sentry-public_key=dogsarebadatkeepingsecrets,sentry-trace_id=12312012123120121231201212312012,sentry-sample_rate=1,sentry-sampled=true', ]); }); + it('generates and uses propagation context to attach baggage and sentry-trace header', async () => { + nock('http://dogs.are.great').get('/').reply(200); + + const request = http.get('http://dogs.are.great/'); + const sentryTraceHeader = request.getHeader('sentry-trace') as string; + const baggageHeader = request.getHeader('baggage') as string; + + const parts = sentryTraceHeader.split('-'); + expect(parts.length).toEqual(3); + expect(parts[0]).toEqual('12312012123120121231201212312012'); + expect(parts[1]).toEqual(expect.any(String)); + expect(parts[2]).toEqual('1'); + + expect(baggageHeader).toEqual( + 'sentry-environment=production,sentry-release=1.0.0,sentry-user_segment=segmentA,sentry-public_key=dogsarebadatkeepingsecrets,sentry-trace_id=12312012123120121231201212312012,sentry-sample_rate=1,sentry-sampled=true', + ); + }); + + it('uses incoming propagation context to attach baggage and sentry-trace', async () => { + nock('http://dogs.are.great').get('/').reply(200); + + const hub = getHub(); + hub.getScope().setPropagationContext({ + traceId: '86f39e84263a4de99c326acab3bfe3bd', + spanId: '86f39e84263a4de9', + sampled: true, + dsc: { + trace_id: '86f39e84263a4de99c326acab3bfe3bd', + public_key: 'test-public-key', + }, + }); + + const request = http.get('http://dogs.are.great/'); + const sentryTraceHeader = request.getHeader('sentry-trace') as string; + const baggageHeader = request.getHeader('baggage') as string; + + const parts = sentryTraceHeader.split('-'); + expect(parts.length).toEqual(3); + expect(parts[0]).toEqual('86f39e84263a4de99c326acab3bfe3bd'); + expect(parts[1]).toEqual(expect.any(String)); + expect(parts[2]).toEqual('1'); + + expect(baggageHeader).toEqual('sentry-trace_id=86f39e84263a4de99c326acab3bfe3bd,sentry-public_key=test-public-key'); + }); + it("doesn't attach the sentry-trace header to outgoing sentry requests", () => { nock('http://squirrelchasers.ingest.sentry.io').get('/api/12312012/store/').reply(200); @@ -270,9 +331,8 @@ describe('tracing', () => { return transaction; } - // TODO (v8): These can be removed once we remove these properties from client options describe('as client options', () => { - it("doesn't create span if shouldCreateSpanForRequest returns false", () => { + it('creates span with propagation context if shouldCreateSpanForRequest returns false', () => { const url = 'http://dogs.are.great/api/v1/index/'; nock(url).get(/.*/).reply(200); @@ -295,8 +355,15 @@ describe('tracing', () => { expect(httpSpans.length).toBe(0); // And headers are not attached without span creation - expect(request.getHeader('sentry-trace')).toBeUndefined(); - expect(request.getHeader('baggage')).toBeUndefined(); + expect(request.getHeader('sentry-trace')).toBeDefined(); + expect(request.getHeader('baggage')).toBeDefined(); + + const propagationContext = hub.getScope().getPropagationContext(); + + expect((request.getHeader('sentry-trace') as string).includes(propagationContext.traceId)).toBe(true); + expect(request.getHeader('baggage')).toEqual( + `sentry-environment=production,sentry-release=1.0.0,sentry-public_key=dogsarebadatkeepingsecrets,sentry-trace_id=${propagationContext.traceId}`, + ); }); it.each([ @@ -366,7 +433,7 @@ describe('tracing', () => { }); describe('as Http integration constructor options', () => { - it("doesn't create span if shouldCreateSpanForRequest returns false", () => { + it('creates span with propagation context if shouldCreateSpanForRequest returns false', () => { const url = 'http://dogs.are.great/api/v1/index/'; nock(url).get(/.*/).reply(200); @@ -393,8 +460,15 @@ describe('tracing', () => { expect(httpSpans.length).toBe(0); // And headers are not attached without span creation - expect(request.getHeader('sentry-trace')).toBeUndefined(); - expect(request.getHeader('baggage')).toBeUndefined(); + expect(request.getHeader('sentry-trace')).toBeDefined(); + expect(request.getHeader('baggage')).toBeDefined(); + + const propagationContext = hub.getScope().getPropagationContext(); + + expect((request.getHeader('sentry-trace') as string).includes(propagationContext.traceId)).toBe(true); + expect(request.getHeader('baggage')).toEqual( + `sentry-environment=production,sentry-release=1.0.0,sentry-public_key=dogsarebadatkeepingsecrets,sentry-trace_id=${propagationContext.traceId}`, + ); }); it.each([ diff --git a/packages/node/test/integrations/linkederrors.test.ts b/packages/node/test/integrations/linkederrors.test.ts deleted file mode 100644 index 6582c0f6e9e5..000000000000 --- a/packages/node/test/integrations/linkederrors.test.ts +++ /dev/null @@ -1,165 +0,0 @@ -import type { ExtendedError } from '@sentry/types'; - -import type { Event } from '../../src'; -import { NodeClient } from '../../src'; -import { LinkedErrors } from '../../src/integrations/linkederrors'; -import { defaultStackParser as stackParser } from '../../src/sdk'; -import { getDefaultNodeClientOptions } from '../helper/node-client-options'; - -let linkedErrors: any; - -describe('LinkedErrors', () => { - beforeEach(() => { - linkedErrors = new LinkedErrors(); - }); - - describe('handler', () => { - it('should bail out if event doesnt contain exception', async () => { - expect.assertions(2); - const spy = jest.spyOn(linkedErrors, '_walkErrorTree'); - const event = { - message: 'foo', - }; - return linkedErrors._handler(stackParser, event, {}).then((result: any) => { - expect(spy.mock.calls.length).toEqual(0); - expect(result).toEqual(event); - }); - }); - - it('should bail out if event contains exception, but no hint', async () => { - expect.assertions(2); - const spy = jest.spyOn(linkedErrors, '_walkErrorTree'); - const one = new Error('originalException'); - const options = getDefaultNodeClientOptions({ stackParser }); - const client = new NodeClient(options); - let event: Event | undefined; - return client - .eventFromException(one) - .then(eventFromException => { - event = eventFromException; - return linkedErrors._handler(stackParser, eventFromException, {}); - }) - .then(result => { - expect(spy.mock.calls.length).toEqual(0); - expect(result).toEqual(event); - }); - }); - - it('should call walkErrorTree if event contains exception and hint with originalException', async () => { - expect.assertions(1); - const spy = jest.spyOn(linkedErrors, '_walkErrorTree').mockImplementation( - async () => - new Promise<[]>(resolve => { - resolve([]); - }), - ); - const one = new Error('originalException'); - const options = getDefaultNodeClientOptions({ stackParser }); - const client = new NodeClient(options); - return client.eventFromException(one).then(event => - linkedErrors - ._handler(stackParser, event, { - originalException: one, - }) - .then((_: any) => { - expect(spy.mock.calls.length).toEqual(1); - }), - ); - }); - - it('should recursively walk error to find linked exceptions and assign them to the event', async () => { - expect.assertions(10); - const one: ExtendedError = new Error('one'); - const two: ExtendedError = new TypeError('two'); - const three: ExtendedError = new SyntaxError('three'); - one.cause = two; - two.cause = three; - - const options = getDefaultNodeClientOptions({ stackParser }); - const client = new NodeClient(options); - return client.eventFromException(one).then(event => - linkedErrors - ._handler(stackParser, event, { - originalException: one, - }) - .then((result: any) => { - expect(result.exception.values.length).toEqual(3); - expect(result.exception.values[0].type).toEqual('SyntaxError'); - expect(result.exception.values[0].value).toEqual('three'); - expect(result.exception.values[0].stacktrace).toHaveProperty('frames'); - expect(result.exception.values[1].type).toEqual('TypeError'); - expect(result.exception.values[1].value).toEqual('two'); - expect(result.exception.values[1].stacktrace).toHaveProperty('frames'); - expect(result.exception.values[2].type).toEqual('Error'); - expect(result.exception.values[2].value).toEqual('one'); - expect(result.exception.values[2].stacktrace).toHaveProperty('frames'); - }), - ); - }); - - it('should allow to change walk key', async () => { - expect.assertions(10); - linkedErrors = new LinkedErrors({ - key: 'reason', - }); - - const one: ExtendedError = new Error('one'); - const two: ExtendedError = new TypeError('two'); - const three: ExtendedError = new SyntaxError('three'); - one.reason = two; - two.reason = three; - - const options = getDefaultNodeClientOptions({ stackParser }); - const client = new NodeClient(options); - return client.eventFromException(one).then(event => - linkedErrors - ._handler(stackParser, event, { - originalException: one, - }) - .then((result: any) => { - expect(result.exception.values.length).toEqual(3); - expect(result.exception.values[0].type).toEqual('SyntaxError'); - expect(result.exception.values[0].value).toEqual('three'); - expect(result.exception.values[0].stacktrace).toHaveProperty('frames'); - expect(result.exception.values[1].type).toEqual('TypeError'); - expect(result.exception.values[1].value).toEqual('two'); - expect(result.exception.values[1].stacktrace).toHaveProperty('frames'); - expect(result.exception.values[2].type).toEqual('Error'); - expect(result.exception.values[2].value).toEqual('one'); - expect(result.exception.values[2].stacktrace).toHaveProperty('frames'); - }), - ); - }); - - it('should allow to change stack size limit', async () => { - expect.assertions(7); - linkedErrors = new LinkedErrors({ - limit: 2, - }); - - const one: ExtendedError = new Error('one'); - const two: ExtendedError = new TypeError('two'); - const three: ExtendedError = new SyntaxError('three'); - one.cause = two; - two.cause = three; - - const options = getDefaultNodeClientOptions({ stackParser }); - const client = new NodeClient(options); - return client.eventFromException(one).then(event => - linkedErrors - ._handler(stackParser, event, { - originalException: one, - }) - .then((result: any) => { - expect(result.exception.values.length).toEqual(2); - expect(result.exception.values[0].type).toEqual('TypeError'); - expect(result.exception.values[0].value).toEqual('two'); - expect(result.exception.values[0].stacktrace).toHaveProperty('frames'); - expect(result.exception.values[1].type).toEqual('Error'); - expect(result.exception.values[1].value).toEqual('one'); - expect(result.exception.values[1].stacktrace).toHaveProperty('frames'); - }), - ); - }); - }); -}); diff --git a/packages/node/test/integrations/undici.test.ts b/packages/node/test/integrations/undici.test.ts index 46756cbe88cd..b30ac92c0695 100644 --- a/packages/node/test/integrations/undici.test.ts +++ b/packages/node/test/integrations/undici.test.ts @@ -1,5 +1,5 @@ import type { Transaction } from '@sentry/core'; -import { Hub, makeMain } from '@sentry/core'; +import { Hub, makeMain, runWithAsyncContext } from '@sentry/core'; import * as http from 'http'; import type { fetch as FetchType } from 'undici'; @@ -15,8 +15,8 @@ let hub: Hub; let fetch: typeof FetchType; beforeAll(async () => { - await setupTestServer(); try { + await setupTestServer(); // need to conditionally require `undici` because it's not available in Node 10 // eslint-disable-next-line @typescript-eslint/no-var-requires fetch = require('undici').fetch; @@ -28,7 +28,7 @@ beforeAll(async () => { const DEFAULT_OPTIONS = getDefaultNodeClientOptions({ dsn: SENTRY_DSN, - tracesSampleRate: 1, + tracesSampler: () => true, integrations: [new Undici()], }); @@ -51,10 +51,10 @@ conditionalTest({ min: 16 })('Undici integration', () => { it.each([ [ 'simple url', - 'http://localhost:18099', + 'http://localhost:18100', undefined, { - description: 'GET http://localhost:18099/', + description: 'GET http://localhost:18100/', op: 'http.client', data: expect.objectContaining({ 'http.method': 'GET', @@ -63,10 +63,10 @@ conditionalTest({ min: 16 })('Undici integration', () => { ], [ 'url with query', - 'http://localhost:18099?foo=bar', + 'http://localhost:18100?foo=bar', undefined, { - description: 'GET http://localhost:18099/', + description: 'GET http://localhost:18100/', op: 'http.client', data: expect.objectContaining({ 'http.method': 'GET', @@ -76,10 +76,10 @@ conditionalTest({ min: 16 })('Undici integration', () => { ], [ 'url with POST method', - 'http://localhost:18099', + 'http://localhost:18100', { method: 'POST' }, { - description: 'POST http://localhost:18099/', + description: 'POST http://localhost:18100/', data: expect.objectContaining({ 'http.method': 'POST', }), @@ -87,10 +87,10 @@ conditionalTest({ min: 16 })('Undici integration', () => { ], [ 'url with POST method', - 'http://localhost:18099', + 'http://localhost:18100', { method: 'POST' }, { - description: 'POST http://localhost:18099/', + description: 'POST http://localhost:18100/', data: expect.objectContaining({ 'http.method': 'POST', }), @@ -98,10 +98,10 @@ conditionalTest({ min: 16 })('Undici integration', () => { ], [ 'url with GET as default', - 'http://localhost:18099', + 'http://localhost:18100', { method: undefined }, { - description: 'GET http://localhost:18099/', + description: 'GET http://localhost:18100/', }, ], ])('creates a span with a %s', async (_: string, request, requestInit, expected) => { @@ -180,54 +180,86 @@ conditionalTest({ min: 16 })('Undici integration', () => { const transaction = hub.startTransaction({ name: 'test-transaction' }) as Transaction; hub.getScope().setSpan(transaction); - const undoPatch = patchUndici(hub, { shouldCreateSpanForRequest: url => url.includes('yes') }); + const undoPatch = patchUndici({ shouldCreateSpanForRequest: url => url.includes('yes') }); - await fetch('http://localhost:18099/no', { method: 'POST' }); + await fetch('http://localhost:18100/no', { method: 'POST' }); expect(transaction.spanRecorder?.spans.length).toBe(1); - await fetch('http://localhost:18099/yes', { method: 'POST' }); + await fetch('http://localhost:18100/yes', { method: 'POST' }); expect(transaction.spanRecorder?.spans.length).toBe(2); undoPatch(); }); - it('attaches the sentry trace and baggage headers', async () => { - const transaction = hub.startTransaction({ name: 'test-transaction' }) as Transaction; - hub.getScope().setSpan(transaction); + // This flakes on CI for some reason: https://github.com/getsentry/sentry-javascript/pull/8449 + it.skip('attaches the sentry trace and baggage headers if there is an active span', async () => { + expect.assertions(3); - await fetch('http://localhost:18099', { method: 'POST' }); + await runWithAsyncContext(async () => { + const transaction = hub.startTransaction({ name: 'test-transaction' }) as Transaction; + hub.getScope().setSpan(transaction); - expect(transaction.spanRecorder?.spans.length).toBe(2); - const span = transaction.spanRecorder?.spans[1]; + await fetch('http://localhost:18100', { method: 'POST' }); + + expect(transaction.spanRecorder?.spans.length).toBe(2); + const span = transaction.spanRecorder?.spans[1]; + + expect(requestHeaders['sentry-trace']).toEqual(span?.toTraceparent()); + expect(requestHeaders['baggage']).toEqual( + `sentry-environment=production,sentry-public_key=0,sentry-trace_id=${transaction.traceId},sentry-sample_rate=1,sentry-transaction=test-transaction`, + ); + }); + }); + + // This flakes on CI for some reason: https://github.com/getsentry/sentry-javascript/pull/8449 + it.skip('attaches the sentry trace and baggage headers if there is no active span', async () => { + const scope = hub.getScope(); - expect(requestHeaders['sentry-trace']).toEqual(span?.toTraceparent()); + await fetch('http://localhost:18100', { method: 'POST' }); + + const propagationContext = scope.getPropagationContext(); + + expect(requestHeaders['sentry-trace'].includes(propagationContext.traceId)).toBe(true); expect(requestHeaders['baggage']).toEqual( - `sentry-environment=production,sentry-transaction=test-transaction,sentry-public_key=0,sentry-trace_id=${transaction.traceId},sentry-sample_rate=1`, + `sentry-environment=production,sentry-public_key=0,sentry-trace_id=${propagationContext.traceId},sentry-sample_rate=1,sentry-transaction=test-transaction,sentry-sampled=true`, ); }); - it('does not attach headers if `shouldCreateSpanForRequest` does not create a span', async () => { + // This flakes on CI for some reason: https://github.com/getsentry/sentry-javascript/pull/8449 + it.skip('attaches headers if `shouldCreateSpanForRequest` does not create a span using propagation context', async () => { const transaction = hub.startTransaction({ name: 'test-transaction' }) as Transaction; - hub.getScope().setSpan(transaction); + const scope = hub.getScope(); + const propagationContext = scope.getPropagationContext(); - const undoPatch = patchUndici(hub, { shouldCreateSpanForRequest: url => url.includes('yes') }); + scope.setSpan(transaction); - await fetch('http://localhost:18099/no', { method: 'POST' }); + const undoPatch = patchUndici({ shouldCreateSpanForRequest: url => url.includes('yes') }); - expect(requestHeaders['sentry-trace']).toBeUndefined(); - expect(requestHeaders['baggage']).toBeUndefined(); + await fetch('http://localhost:18100/no', { method: 'POST' }); + + expect(requestHeaders['sentry-trace']).toBeDefined(); + expect(requestHeaders['baggage']).toBeDefined(); + + expect(requestHeaders['sentry-trace'].includes(propagationContext.traceId)).toBe(true); + const firstSpanId = requestHeaders['sentry-trace'].split('-')[1]; - await fetch('http://localhost:18099/yes', { method: 'POST' }); + await fetch('http://localhost:18100/yes', { method: 'POST' }); expect(requestHeaders['sentry-trace']).toBeDefined(); expect(requestHeaders['baggage']).toBeDefined(); + expect(requestHeaders['sentry-trace'].includes(propagationContext.traceId)).toBe(false); + + const secondSpanId = requestHeaders['sentry-trace'].split('-')[1]; + expect(firstSpanId).not.toBe(secondSpanId); + undoPatch(); }); - it('uses tracePropagationTargets', async () => { + // This flakes on CI for some reason: https://github.com/getsentry/sentry-javascript/pull/8449 + it.skip('uses tracePropagationTargets', async () => { const transaction = hub.startTransaction({ name: 'test-transaction' }) as Transaction; hub.getScope().setSpan(transaction); @@ -236,14 +268,14 @@ conditionalTest({ min: 16 })('Undici integration', () => { expect(transaction.spanRecorder?.spans.length).toBe(1); - await fetch('http://localhost:18099/no', { method: 'POST' }); + await fetch('http://localhost:18100/no', { method: 'POST' }); expect(transaction.spanRecorder?.spans.length).toBe(2); expect(requestHeaders['sentry-trace']).toBeUndefined(); expect(requestHeaders['baggage']).toBeUndefined(); - await fetch('http://localhost:18099/yes', { method: 'POST' }); + await fetch('http://localhost:18100/yes', { method: 'POST' }); expect(transaction.spanRecorder?.spans.length).toBe(3); @@ -262,7 +294,7 @@ conditionalTest({ min: 16 })('Undici integration', () => { data: { method: 'POST', status_code: 200, - url: 'http://localhost:18099/', + url: 'http://localhost:18100/', }, type: 'http', timestamp: expect.any(Number), @@ -272,7 +304,7 @@ conditionalTest({ min: 16 })('Undici integration', () => { }); hub.bindClient(client); - await fetch('http://localhost:18099', { method: 'POST' }); + await fetch('http://localhost:18100', { method: 'POST' }); }); it('adds a breadcrumb on errored request', async () => { @@ -306,9 +338,9 @@ conditionalTest({ min: 16 })('Undici integration', () => { it('does not add a breadcrumb if disabled', async () => { expect.assertions(0); - const undoPatch = patchUndici(hub, { breadcrumbs: false }); + const undoPatch = patchUndici({ breadcrumbs: false }); - await fetch('http://localhost:18099', { method: 'POST' }); + await fetch('http://localhost:18100', { method: 'POST' }); undoPatch(); }); @@ -351,37 +383,34 @@ function setupTestServer() { res.end(); // also terminate socket because keepalive hangs connection a bit - res.connection.end(); + res.connection?.end(); }); - testServer.listen(18099, 'localhost'); + testServer?.listen(18100); return new Promise(resolve => { testServer?.on('listening', resolve); }); } -function patchUndici(hub: Hub, userOptions: Partial): () => void { - let options: any = {}; - const client = hub.getClient(); - if (client) { - const undici = client.getIntegration(Undici); - if (undici) { - // @ts-ignore need to access private property - options = { ...undici._options }; - // @ts-ignore need to access private property - undici._options = Object.assign(undici._options, userOptions); - } +function patchUndici(userOptions: Partial): () => void { + try { + const undici = hub.getClient()!.getIntegration(Undici); + // @ts-ignore need to access private property + options = { ...undici._options }; + // @ts-ignore need to access private property + undici._options = Object.assign(undici._options, userOptions); + } catch (_) { + throw new Error('Could not undo patching of undici'); } return () => { - const client = hub.getClient(); - if (client) { - const undici = client.getIntegration(Undici); - if (undici) { - // @ts-ignore need to access private property - undici._options = { ...options }; - } + try { + const undici = hub.getClient()!.getIntegration(Undici); + // @ts-expect-error Need to override readonly property + undici!['_options'] = { ...options }; + } catch (_) { + throw new Error('Could not undo patching of undici'); } }; } diff --git a/packages/node/test/manual/apm-transaction/main.js b/packages/node/test/manual/apm-transaction/main.js deleted file mode 100644 index 0584177e8b10..000000000000 --- a/packages/node/test/manual/apm-transaction/main.js +++ /dev/null @@ -1,113 +0,0 @@ -const http = require('http'); -const express = require('express'); -const app = express(); -const Sentry = require('../../../build/cjs'); - -Sentry.init({ - debug: true, - dsn: 'http://test@example.com/1337', - beforeSend(event) { - console.log(JSON.stringify(event, null, 2)); - return null; - }, -}); - -function hasSentryTraceHeader(req) { - return req.headers && typeof req.headers['sentry-trace'] === 'string'; -} - -class Tracing { - static start() { - return (req, _res, next) => { - const transaction = `${req.method.toUpperCase()} ${req.originalUrl}`; - const span = hasSentryTraceHeader(req) - ? Span.fromTraceparent(req.headers['sentry-trace'], { - transaction, - }) - : Sentry.startTransaction({ - name: transaction, - }); - - Sentry.getCurrentHub().configureScope(scope => { - scope.setSpan(span); - }); - - next(); - }; - } - - static finish() { - return (_req, _res, next) => { - const scope = Sentry.getCurrentHub().getScope(); - if (!scope) { - return next(); - } - const span = scope.getSpan(); - if (!span) { - return next(); - } - Sentry.getCurrentHub().finishSpan(span); - next(); - }; - } -} - -async function databaseCall(_query) { - const span = Sentry.getCurrentHub().startSpan({ - op: 'db', - }); - - return new Promise(resolve => { - setTimeout(() => { - Sentry.getCurrentHub().finishSpan(span); - resolve('http://whatever.com/raw'); - }, Math.random() * 100); - }); -} - -async function httpCall(_url) { - const span = Sentry.getCurrentHub().startSpan({ - op: 'http', - }); - - return new Promise(resolve => { - setTimeout(() => { - Sentry.getCurrentHub().finishSpan(span); - resolve('httpCall'); - }, Math.random() * 100); - }); -} - -async function encodeData(_data) { - const span = Sentry.getCurrentHub().startSpan({ - op: 'encode', - }); - - return new Promise(resolve => { - setTimeout(() => { - Sentry.getCurrentHub().finishSpan(span); - resolve('encodedData'); - }, Math.random() * 100); - }); -} - -app.use(Sentry.Handlers.requestHandler()); -app.use(Tracing.start()); - -app.get('/trace', async (_req, res, next) => { - const url = await databaseCall('SELECT url FROM queue WHERE processed = 0 LIMIT 1'); - const raw = await httpCall(url); - const encoded = await encodeData(raw); - res.status(200).send(encoded); - next(); -}); - -app.use(Tracing.finish()); -app.use(Sentry.Handlers.errorHandler()); - -const server = app.listen(0, () => { - const port = server.address().port; - http.get(`http://localhost:${port}/trace`, res => { - server.close(); - }); -}); diff --git a/packages/node/test/manual/webpack-async-context/yarn.lock b/packages/node/test/manual/webpack-async-context/yarn.lock index 79d15b238de4..984f2cbbf55e 100644 --- a/packages/node/test/manual/webpack-async-context/yarn.lock +++ b/packages/node/test/manual/webpack-async-context/yarn.lock @@ -2112,9 +2112,9 @@ schema-utils@^1.0.0: ajv-keywords "^3.1.0" semver@^5.5.0, semver@^5.6.0: - version "5.7.1" - resolved "https://registry.yarnpkg.com/semver/-/semver-5.7.1.tgz#a954f931aeba508d307bbf069eff0c01c96116f7" - integrity sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ== + version "5.7.2" + resolved "https://registry.yarnpkg.com/semver/-/semver-5.7.2.tgz#48d55db737c3287cd4835e17fa13feace1c41ef8" + integrity sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g== serialize-javascript@^2.1.2: version "2.1.2" diff --git a/packages/node/test/module.test.ts b/packages/node/test/module.test.ts index 39f1e9758f46..e27f1482ff90 100644 --- a/packages/node/test/module.test.ts +++ b/packages/node/test/module.test.ts @@ -1,4 +1,4 @@ -import { getModule } from '../src/module'; +import { getModuleFromFilename } from '../src/module'; function withFilename(fn: () => void, filename: string) { const prevFilename = require.main?.filename; @@ -15,16 +15,16 @@ function withFilename(fn: () => void, filename: string) { } } -describe('getModule', () => { +describe('getModuleFromFilename', () => { test('Windows', () => { withFilename(() => { - expect(getModule('C:\\Users\\users\\Tim\\Desktop\\node_modules\\module.js', true)).toEqual('module'); + expect(getModuleFromFilename('C:\\Users\\users\\Tim\\Desktop\\node_modules\\module.js', true)).toEqual('module'); }, 'C:\\Users\\Tim\\app.js'); }); test('POSIX', () => { withFilename(() => { - expect(getModule('/Users/users/Tim/Desktop/node_modules/module.js')).toEqual('module'); + expect(getModuleFromFilename('/Users/users/Tim/Desktop/node_modules/module.js')).toEqual('module'); }, '/Users/Tim/app.js'); }); }); diff --git a/packages/opentelemetry-node/test/propagator.test.ts b/packages/opentelemetry-node/test/propagator.test.ts index d5222e3103d4..a70ecb051663 100644 --- a/packages/opentelemetry-node/test/propagator.test.ts +++ b/packages/opentelemetry-node/test/propagator.test.ts @@ -85,7 +85,7 @@ describe('SentryPropagator', () => { spanId: '6e0c63257de34c92', sampled: true, }, - 'sentry-environment=production,sentry-release=1.0.0,sentry-transaction=sampled-transaction,sentry-public_key=abc,sentry-trace_id=d4cda95b652f4a1592b449d5929fda1b', + 'sentry-environment=production,sentry-release=1.0.0,sentry-public_key=abc,sentry-trace_id=d4cda95b652f4a1592b449d5929fda1b,sentry-transaction=sampled-transaction,sentry-sampled=true', 'd4cda95b652f4a1592b449d5929fda1b-6e0c63257de34c92-1', ], [ @@ -101,7 +101,7 @@ describe('SentryPropagator', () => { spanId: '6e0c63257de34c92', sampled: false, }, - 'sentry-environment=production,sentry-release=1.0.0,sentry-transaction=not-sampled-transaction,sentry-public_key=abc,sentry-trace_id=d4cda95b652f4a1592b449d5929fda1b', + 'sentry-environment=production,sentry-release=1.0.0,sentry-public_key=abc,sentry-trace_id=d4cda95b652f4a1592b449d5929fda1b,sentry-transaction=not-sampled-transaction,sentry-sampled=false', 'd4cda95b652f4a1592b449d5929fda1b-6e0c63257de34c92-0', ], [ @@ -161,7 +161,7 @@ describe('SentryPropagator', () => { const baggage = propagation.createBaggage({ foo: { value: 'bar' } }); propagator.inject(propagation.setBaggage(context, baggage), carrier, defaultTextMapSetter); expect(carrier[SENTRY_BAGGAGE_HEADER]).toBe( - 'foo=bar,sentry-environment=production,sentry-release=1.0.0,sentry-transaction=sampled-transaction,sentry-public_key=abc,sentry-trace_id=d4cda95b652f4a1592b449d5929fda1b', + 'foo=bar,sentry-environment=production,sentry-release=1.0.0,sentry-public_key=abc,sentry-trace_id=d4cda95b652f4a1592b449d5929fda1b,sentry-transaction=sampled-transaction,sentry-sampled=true', ); }); @@ -232,7 +232,7 @@ describe('SentryPropagator', () => { it('sets defined dynamic sampling context on context', () => { const baggage = - 'sentry-environment=production,sentry-release=1.0.0,sentry-transaction=dsc-transaction,sentry-public_key=abc,sentry-trace_id=d4cda95b652f4a1592b449d5929fda1b'; + 'sentry-environment=production,sentry-release=1.0.0,sentry-public_key=abc,sentry-trace_id=d4cda95b652f4a1592b449d5929fda1b,sentry-transaction=dsc-transaction'; carrier[SENTRY_BAGGAGE_HEADER] = baggage; const context = propagator.extract(ROOT_CONTEXT, carrier, defaultTextMapGetter); expect(context.getValue(SENTRY_DYNAMIC_SAMPLING_CONTEXT_KEY)).toEqual({ diff --git a/packages/react/src/reactrouterv6.tsx b/packages/react/src/reactrouterv6.tsx index bc917ff12508..161c7dd5fb3b 100644 --- a/packages/react/src/reactrouterv6.tsx +++ b/packages/react/src/reactrouterv6.tsx @@ -119,8 +119,15 @@ function getNormalizedName( return [location.pathname, 'url']; } -function updatePageloadTransaction(location: Location, routes: RouteObject[], matches?: AgnosticDataRouteMatch): void { - const branches = Array.isArray(matches) ? matches : (_matchRoutes(routes, location) as unknown as RouteMatch[]); +function updatePageloadTransaction( + location: Location, + routes: RouteObject[], + matches?: AgnosticDataRouteMatch, + basename?: string, +): void { + const branches = Array.isArray(matches) + ? matches + : (_matchRoutes(routes, location, basename) as unknown as RouteMatch[]); if (activeTransaction && branches) { activeTransaction.setName(...getNormalizedName(routes, location, branches)); @@ -132,8 +139,9 @@ function handleNavigation( routes: RouteObject[], navigationType: Action, matches?: AgnosticDataRouteMatch, + basename?: string, ): void { - const branches = Array.isArray(matches) ? matches : _matchRoutes(routes, location); + const branches = Array.isArray(matches) ? matches : _matchRoutes(routes, location, basename); if (_startTransactionOnLocationChange && (navigationType === 'PUSH' || navigationType === 'POP') && branches) { if (activeTransaction) { @@ -254,15 +262,17 @@ export function wrapCreateBrowserRouter< TRouter extends Router = Router, >(createRouterFunction: CreateRouterFunction): CreateRouterFunction { // `opts` for createBrowserHistory and createMemoryHistory are different, but also not relevant for us at the moment. + // `basename` is the only option that is relevant for us, and it is the same for all. // eslint-disable-next-line @typescript-eslint/no-explicit-any - return function (routes: RouteObject[], opts?: any): TRouter { + return function (routes: RouteObject[], opts?: Record & { basename?: string }): TRouter { const router = createRouterFunction(routes, opts); + const basename = opts && opts.basename; // The initial load ends when `createBrowserRouter` is called. // This is the earliest convenient time to update the transaction name. // Callbacks to `router.subscribe` are not called for the initial load. if (router.state.historyAction === 'POP' && activeTransaction) { - updatePageloadTransaction(router.state.location, routes); + updatePageloadTransaction(router.state.location, routes, undefined, basename); } router.subscribe((state: RouterState) => { @@ -273,7 +283,7 @@ export function wrapCreateBrowserRouter< (state.historyAction === 'PUSH' || state.historyAction === 'POP') && activeTransaction ) { - handleNavigation(location, routes, state.historyAction); + handleNavigation(location, routes, state.historyAction, undefined, basename); } }); diff --git a/packages/react/src/types.ts b/packages/react/src/types.ts index 409a0a33c23d..fb0652acb036 100644 --- a/packages/react/src/types.ts +++ b/packages/react/src/types.ts @@ -75,7 +75,11 @@ export type UseNavigationType = () => Action; export type RouteObjectArrayAlias = any; export type RouteMatchAlias = any; export type CreateRoutesFromChildren = (children: JSX.Element[]) => RouteObjectArrayAlias; -export type MatchRoutes = (routes: RouteObjectArrayAlias, location: Location) => RouteMatchAlias[] | null; +export type MatchRoutes = ( + routes: RouteObjectArrayAlias, + location: Location, + basename?: string, +) => RouteMatchAlias[] | null; // Types for react-router >= 6.4.2 export type ShouldRevalidateFunction = (args: any) => boolean; @@ -203,7 +207,7 @@ export declare enum HistoryAction { export interface RouterState { historyAction: Action | HistoryAction | any; - location: any; + location: Location; } export interface Router { state: TState; diff --git a/packages/react/test/reactrouterv6.4.test.tsx b/packages/react/test/reactrouterv6.4.test.tsx index a01da694ce2f..e6bfc67b9bcf 100644 --- a/packages/react/test/reactrouterv6.4.test.tsx +++ b/packages/react/test/reactrouterv6.4.test.tsx @@ -265,5 +265,44 @@ describe('React Router v6.4', () => { expect(mockStartTransaction).toHaveBeenCalledTimes(1); expect(mockSetName).toHaveBeenLastCalledWith('/about/:page', 'route'); }); + + it('works with `basename` option', () => { + const [mockStartTransaction] = createInstrumentation(); + const sentryCreateBrowserRouter = wrapCreateBrowserRouter(createMemoryRouter as CreateRouterFunction); + + const router = sentryCreateBrowserRouter( + [ + { + path: '/', + element: , + }, + { + path: 'about', + element:
About
, + children: [ + { + path: 'us', + element:
Us
, + }, + ], + }, + ], + { + initialEntries: ['/app'], + basename: '/app', + }, + ); + + // @ts-ignore router is fine + render(); + + expect(mockStartTransaction).toHaveBeenCalledTimes(2); + expect(mockStartTransaction).toHaveBeenLastCalledWith({ + name: '/app/about/us', + op: 'navigation', + tags: { 'routing.instrumentation': 'react-router-v6' }, + metadata: { source: 'url' }, + }); + }); }); }); diff --git a/packages/remix/src/index.server.ts b/packages/remix/src/index.server.ts index 21cfc20b17ab..91839a809993 100644 --- a/packages/remix/src/index.server.ts +++ b/packages/remix/src/index.server.ts @@ -6,9 +6,56 @@ import { instrumentServer } from './utils/instrumentServer'; import { buildMetadata } from './utils/metadata'; import type { RemixOptions } from './utils/remixOptions'; +// We need to explicitly export @sentry/node as they end up under `default` in ESM builds +// See: https://github.com/getsentry/sentry-javascript/issues/8474 +export { + addGlobalEventProcessor, + addBreadcrumb, + captureCheckIn, + captureException, + captureEvent, + captureMessage, + configureScope, + createTransport, + extractTraceparentData, + getActiveTransaction, + getHubFromCarrier, + getCurrentHub, + Hub, + makeMain, + Scope, + startTransaction, + SDK_VERSION, + setContext, + setExtra, + setExtras, + setTag, + setTags, + setUser, + spanStatusfromHttpCode, + trace, + withScope, + autoDiscoverNodePerformanceMonitoringIntegrations, + makeNodeTransport, + defaultIntegrations, + defaultStackParser, + lastEventId, + flush, + close, + getSentryRelease, + addRequestDataToEvent, + DEFAULT_USER_INCLUDES, + extractRequestData, + deepReadDirSync, + Integrations, + Handlers, +} from '@sentry/node'; + +// Keeping the `*` exports for backwards compatibility and types +export * from '@sentry/node'; + export { ErrorBoundary, withErrorBoundary } from '@sentry/react'; export { remixRouterInstrumentation, withSentry } from './performance/client'; -export * from '@sentry/node'; export { wrapExpressCreateRequestHandler } from './utils/serverAdapters/express'; function sdkAlreadyInitialized(): boolean { diff --git a/packages/remix/src/utils/instrumentServer.ts b/packages/remix/src/utils/instrumentServer.ts index 5e0f300bf82e..64dea4cfb92b 100644 --- a/packages/remix/src/utils/instrumentServer.ts +++ b/packages/remix/src/utils/instrumentServer.ts @@ -5,13 +5,12 @@ import { captureException, getCurrentHub } from '@sentry/node'; import type { Transaction, TransactionSource, WrappedFunction } from '@sentry/types'; import { addExceptionMechanism, - baggageHeaderToDynamicSamplingContext, dynamicSamplingContextToSentryBaggageHeader, - extractTraceparentData, fill, isNodeEnv, loadModule, logger, + tracingContextFromHeaders, } from '@sentry/utils'; import type { @@ -237,18 +236,26 @@ function makeWrappedRootLoader(origLoader: DataFunction): DataFunction { }; } - // Note: `redirect` and `catch` responses do not have bodies to extract - if (isResponse(res) && !isRedirectResponse(res) && !isCatchResponse(res)) { - const data = await extractData(res); - - if (typeof data === 'object') { - return json( - { ...data, ...traceAndBaggage }, - { headers: res.headers, statusText: res.statusText, status: res.status }, - ); - } else { - __DEBUG_BUILD__ && logger.warn('Skipping injection of trace and baggage as the response body is not an object'); + if (isResponse(res)) { + // Note: `redirect` and `catch` responses do not have bodies to extract. + // We skip injection of trace and baggage in those cases. + // For `redirect`, a valid internal redirection target will have the trace and baggage injected. + if (isRedirectResponse(res) || isCatchResponse(res)) { + __DEBUG_BUILD__ && logger.warn('Skipping injection of trace and baggage as the response does not have a body'); return res; + } else { + const data = await extractData(res); + + if (typeof data === 'object') { + return json( + { ...data, ...traceAndBaggage }, + { headers: res.headers, statusText: res.statusText, status: res.status }, + ); + } else { + __DEBUG_BUILD__ && + logger.warn('Skipping injection of trace and baggage as the response body is not an object'); + return res; + } } } @@ -290,9 +297,11 @@ export function startRequestHandlerTransaction( method: string; }, ): Transaction { - // If there is a trace header set, we extract the data from it (parentSpanId, traceId, and sampling decision) - const traceparentData = extractTraceparentData(request.headers['sentry-trace']); - const dynamicSamplingContext = baggageHeaderToDynamicSamplingContext(request.headers.baggage); + const { traceparentData, dynamicSamplingContext, propagationContext } = tracingContextFromHeaders( + request.headers['sentry-trace'], + request.headers.baggage, + ); + hub.getScope().setPropagationContext(propagationContext); const transaction = hub.startTransaction({ name, diff --git a/packages/remix/test/integration/app_v1/entry.server.tsx b/packages/remix/test/integration/app_v1/entry.server.tsx index ae879492e236..d48f2644fac4 100644 --- a/packages/remix/test/integration/app_v1/entry.server.tsx +++ b/packages/remix/test/integration/app_v1/entry.server.tsx @@ -6,6 +6,7 @@ import * as Sentry from '@sentry/remix'; Sentry.init({ dsn: 'https://public@dsn.ingest.sentry.io/1337', tracesSampleRate: 1, + tracePropagationTargets: ['example.org'], // Disabling to test series of envelopes deterministically. autoSessionTracking: false, }); diff --git a/packages/remix/test/integration/app_v1/root.tsx b/packages/remix/test/integration/app_v1/root.tsx index 1e716237343c..ee9b6ceddb78 100644 --- a/packages/remix/test/integration/app_v1/root.tsx +++ b/packages/remix/test/integration/app_v1/root.tsx @@ -34,6 +34,10 @@ export const loader: LoaderFunction = async ({ request }) => { throw redirect('/?type=plain'); case 'returnRedirect': return redirect('/?type=plain'); + case 'throwRedirectToExternal': + throw redirect('https://example.com'); + case 'returnRedirectToExternal': + return redirect('https://example.com'); default: { return {}; } diff --git a/packages/remix/test/integration/app_v2/entry.server.tsx b/packages/remix/test/integration/app_v2/entry.server.tsx index ae879492e236..d48f2644fac4 100644 --- a/packages/remix/test/integration/app_v2/entry.server.tsx +++ b/packages/remix/test/integration/app_v2/entry.server.tsx @@ -6,6 +6,7 @@ import * as Sentry from '@sentry/remix'; Sentry.init({ dsn: 'https://public@dsn.ingest.sentry.io/1337', tracesSampleRate: 1, + tracePropagationTargets: ['example.org'], // Disabling to test series of envelopes deterministically. autoSessionTracking: false, }); diff --git a/packages/remix/test/integration/app_v2/root.tsx b/packages/remix/test/integration/app_v2/root.tsx index faf075951d69..5af1f8cc7a1a 100644 --- a/packages/remix/test/integration/app_v2/root.tsx +++ b/packages/remix/test/integration/app_v2/root.tsx @@ -34,6 +34,10 @@ export const loader: LoaderFunction = async ({ request }) => { throw redirect('/?type=plain'); case 'returnRedirect': return redirect('/?type=plain'); + case 'throwRedirectToExternal': + throw redirect('https://example.com'); + case 'returnRedirectToExternal': + return redirect('https://example.com'); default: { return {}; } diff --git a/packages/remix/test/integration/test/client/meta-tags.test.ts b/packages/remix/test/integration/test/client/meta-tags.test.ts index f956787b35c6..6d0a0cd06307 100644 --- a/packages/remix/test/integration/test/client/meta-tags.test.ts +++ b/packages/remix/test/integration/test/client/meta-tags.test.ts @@ -2,7 +2,13 @@ import { test, expect } from '@playwright/test'; import { getFirstSentryEnvelopeRequest } from './utils/helpers'; import { Event } from '@sentry/types'; -test('should inject `sentry-trace` and `baggage` meta tags inside the root page.', async ({ page }) => { +test('should inject `sentry-trace` and `baggage` meta tags inside the root page.', async ({ page, browserName }) => { + // This test is flaky on firefox + // https://github.com/getsentry/sentry-javascript/issues/8398 + if (browserName === 'firefox') { + test.skip(); + } + await page.goto('/'); const sentryTraceTag = await page.$('meta[name="sentry-trace"]'); @@ -16,7 +22,16 @@ test('should inject `sentry-trace` and `baggage` meta tags inside the root page. expect(sentryBaggageContent).toEqual(expect.any(String)); }); -test('should inject `sentry-trace` and `baggage` meta tags inside a parameterized route.', async ({ page }) => { +test('should inject `sentry-trace` and `baggage` meta tags inside a parameterized route.', async ({ + page, + browserName, +}) => { + // This test is flaky on firefox + // https://github.com/getsentry/sentry-javascript/issues/8398 + if (browserName === 'firefox') { + test.skip(); + } + await page.goto('/loader-json-response/0'); const sentryTraceTag = await page.$('meta[name="sentry-trace"]'); @@ -30,7 +45,16 @@ test('should inject `sentry-trace` and `baggage` meta tags inside a parameterize expect(sentryBaggageContent).toEqual(expect.any(String)); }); -test('should send transactions with corresponding `sentry-trace` and `baggage` inside root page', async ({ page }) => { +test('should send transactions with corresponding `sentry-trace` and `baggage` inside root page', async ({ + page, + browserName, +}) => { + // This test is flaky on firefox + // https://github.com/getsentry/sentry-javascript/issues/8398 + if (browserName === 'firefox') { + test.skip(); + } + const envelope = await getFirstSentryEnvelopeRequest(page, '/'); const sentryTraceTag = await page.$('meta[name="sentry-trace"]'); @@ -47,7 +71,14 @@ test('should send transactions with corresponding `sentry-trace` and `baggage` i test('should send transactions with corresponding `sentry-trace` and `baggage` inside a parameterized route', async ({ page, + browserName, }) => { + // This test is flaky on firefox + // https://github.com/getsentry/sentry-javascript/issues/8398 + if (browserName === 'firefox') { + test.skip(); + } + const envelope = await getFirstSentryEnvelopeRequest(page, '/loader-json-response/0'); const sentryTraceTag = await page.$('meta[name="sentry-trace"]'); diff --git a/packages/remix/test/integration/test/client/root-loader.test.ts b/packages/remix/test/integration/test/client/root-loader.test.ts index 4bb5b41a82ce..c78ceb2aa411 100644 --- a/packages/remix/test/integration/test/client/root-loader.test.ts +++ b/packages/remix/test/integration/test/client/root-loader.test.ts @@ -125,6 +125,9 @@ test('should inject `sentry-trace` and `baggage` into root loader throwing a red }) => { await page.goto('/?type=throwRedirect'); + // We should be successfully redirected to the path. + expect(page.url()).toEqual(expect.stringContaining('/?type=plain')); + const { sentryTrace, sentryBaggage } = await extractTraceAndBaggageFromMeta(page); expect(sentryTrace).toEqual(expect.any(String)); @@ -138,11 +141,14 @@ test('should inject `sentry-trace` and `baggage` into root loader throwing a red }); }); -test('should inject `sentry-trace` and `baggage` into root loader returning a redirection to a plain object', async ({ +test('should inject `sentry-trace` and `baggage` into root loader returning a redirection to valid path.', async ({ page, }) => { await page.goto('/?type=returnRedirect'); + // We should be successfully redirected to the path. + expect(page.url()).toEqual(expect.stringContaining('/?type=plain')); + const { sentryTrace, sentryBaggage } = await extractTraceAndBaggageFromMeta(page); expect(sentryTrace).toEqual(expect.any(String)); @@ -155,3 +161,27 @@ test('should inject `sentry-trace` and `baggage` into root loader returning a re sentryBaggage: sentryBaggage, }); }); + +test('should return redirect to an external path with no baggage and trace injected.', async ({ page }) => { + await page.goto('/?type=returnRedirectToExternal'); + + // We should be successfully redirected to the external path. + expect(page.url()).toEqual(expect.stringContaining('https://example.com')); + + const { sentryTrace, sentryBaggage } = await extractTraceAndBaggageFromMeta(page); + + expect(sentryTrace).toBeUndefined(); + expect(sentryBaggage).toBeUndefined(); +}); + +test('should throw redirect to an external path with no baggage and trace injected.', async ({ page }) => { + await page.goto('/?type=throwRedirectToExternal'); + + // We should be successfully redirected to the external path. + expect(page.url()).toEqual(expect.stringContaining('https://example.com')); + + const { sentryTrace, sentryBaggage } = await extractTraceAndBaggageFromMeta(page); + + expect(sentryTrace).toBeUndefined(); + expect(sentryBaggage).toBeUndefined(); +}); diff --git a/packages/replay/src/constants.ts b/packages/replay/src/constants.ts index 699cefe8b784..86dc228c50bf 100644 --- a/packages/replay/src/constants.ts +++ b/packages/replay/src/constants.ts @@ -42,8 +42,6 @@ export const CONSOLE_ARG_MAX_SIZE = 5_000; export const SLOW_CLICK_THRESHOLD = 3_000; /* For scroll actions after a click, we only look for a very short time period to detect programmatic scrolling. */ export const SLOW_CLICK_SCROLL_TIMEOUT = 300; -/* Clicks in this time period are considered e.g. double/triple clicks. */ -export const MULTI_CLICK_TIMEOUT = 1_000; /** When encountering a total segment size exceeding this size, stop the replay (as we cannot properly ingest it). */ export const REPLAY_MAX_EVENT_BUFFER_SIZE = 20_000_000; // ~20MB diff --git a/packages/replay/src/coreHandlers/handleClick.ts b/packages/replay/src/coreHandlers/handleClick.ts index c5a65ddfdca0..5f7e46dbcbce 100644 --- a/packages/replay/src/coreHandlers/handleClick.ts +++ b/packages/replay/src/coreHandlers/handleClick.ts @@ -33,7 +33,6 @@ export class ClickDetector implements ReplayClickDetector { private _clicks: Click[] = []; private _teardown: undefined | (() => void); - private _multiClickTimeout: number; private _threshold: number; private _scollTimeout: number; private _timeout: number; @@ -51,7 +50,6 @@ export class ClickDetector implements ReplayClickDetector { ) { // We want everything in s, but options are in ms this._timeout = slowClickConfig.timeout / 1000; - this._multiClickTimeout = slowClickConfig.multiClickTimeout / 1000; this._threshold = slowClickConfig.threshold / 1000; this._scollTimeout = slowClickConfig.scrollTimeout / 1000; this._replay = replay; @@ -126,13 +124,6 @@ export class ClickDetector implements ReplayClickDetector { return; } - const click = this._getClick(node); - - if (click) { - // this means a click on the same element was captured in the last 1s, so we consider this a multi click - return; - } - const newClick: Click = { timestamp: breadcrumb.timestamp, clickBreadcrumb: breadcrumb, @@ -150,22 +141,14 @@ export class ClickDetector implements ReplayClickDetector { /** Count multiple clicks on elements. */ private _handleMultiClick(node: HTMLElement): void { - const click = this._getClick(node); - - if (!click) { - return; - } - - click.clickCount++; + this._getClicks(node).forEach(click => { + click.clickCount++; + }); } - /** Try to get an existing click on the given element. */ - private _getClick(node: HTMLElement): Click | undefined { - const now = nowInSeconds(); - - // Find any click on the same element in the last second - // If one exists, we consider this click as a double/triple/etc click - return this._clicks.find(click => click.node === node && now - click.timestamp < this._multiClickTimeout); + /** Get all pending clicks for a given node. */ + private _getClicks(node: HTMLElement): Click[] { + return this._clicks.filter(click => click.node === node); } /** Check the clicks that happened. */ @@ -182,13 +165,6 @@ export class ClickDetector implements ReplayClickDetector { click.scrollAfter = click.timestamp <= this._lastScroll ? this._lastScroll - click.timestamp : undefined; } - // If an action happens after the multi click threshold, we can skip waiting and handle the click right away - const actionTime = click.scrollAfter || click.mutationAfter || 0; - if (actionTime && actionTime >= this._multiClickTimeout) { - timedOutClicks.push(click); - return; - } - if (click.timestamp + this._timeout <= now) { timedOutClicks.push(click); } @@ -269,6 +245,10 @@ export class ClickDetector implements ReplayClickDetector { /** Schedule to check current clicks. */ private _scheduleCheckClicks(): void { + if (this._checkClickTimeout) { + clearTimeout(this._checkClickTimeout); + } + this._checkClickTimeout = setTimeout(() => this._checkClicks(), 1000); } } diff --git a/packages/replay/src/coreHandlers/handleNetworkBreadcrumbs.ts b/packages/replay/src/coreHandlers/handleNetworkBreadcrumbs.ts index 59324031e336..20fa00a633eb 100644 --- a/packages/replay/src/coreHandlers/handleNetworkBreadcrumbs.ts +++ b/packages/replay/src/coreHandlers/handleNetworkBreadcrumbs.ts @@ -31,13 +31,19 @@ export function handleNetworkBreadcrumbs(replay: ReplayContainer): void { try { const textEncoder = new TextEncoder(); - const { networkDetailAllowUrls, networkCaptureBodies, networkRequestHeaders, networkResponseHeaders } = - replay.getOptions(); + const { + networkDetailAllowUrls, + networkDetailDenyUrls, + networkCaptureBodies, + networkRequestHeaders, + networkResponseHeaders, + } = replay.getOptions(); const options: ExtendedNetworkBreadcrumbsOptions = { replay, textEncoder, networkDetailAllowUrls, + networkDetailDenyUrls, networkCaptureBodies, networkRequestHeaders, networkResponseHeaders, diff --git a/packages/replay/src/coreHandlers/util/fetchUtils.ts b/packages/replay/src/coreHandlers/util/fetchUtils.ts index 9411332d0197..491fe727b1b8 100644 --- a/packages/replay/src/coreHandlers/util/fetchUtils.ts +++ b/packages/replay/src/coreHandlers/util/fetchUtils.ts @@ -85,7 +85,8 @@ async function _prepareFetchData( response_body_size: responseBodySize, } = breadcrumb.data; - const captureDetails = urlMatches(url, options.networkDetailAllowUrls); + const captureDetails = + urlMatches(url, options.networkDetailAllowUrls) && !urlMatches(url, options.networkDetailDenyUrls); const request = captureDetails ? _getRequestInfo(options, hint.input, requestBodySize) diff --git a/packages/replay/src/coreHandlers/util/xhrUtils.ts b/packages/replay/src/coreHandlers/util/xhrUtils.ts index dfb1ccaf3ffc..638af2cbdb5c 100644 --- a/packages/replay/src/coreHandlers/util/xhrUtils.ts +++ b/packages/replay/src/coreHandlers/util/xhrUtils.ts @@ -78,7 +78,7 @@ function _prepareXhrData( return null; } - if (!urlMatches(url, options.networkDetailAllowUrls)) { + if (!urlMatches(url, options.networkDetailAllowUrls) || urlMatches(url, options.networkDetailDenyUrls)) { const request = buildSkippedNetworkRequestOrResponse(requestBodySize); const response = buildSkippedNetworkRequestOrResponse(responseBodySize); return { diff --git a/packages/replay/src/integration.ts b/packages/replay/src/integration.ts index 2baf117b5c38..2e795829894b 100644 --- a/packages/replay/src/integration.ts +++ b/packages/replay/src/integration.ts @@ -67,6 +67,7 @@ export class Replay implements Integration { slowClickIgnoreSelectors = [], networkDetailAllowUrls = [], + networkDetailDenyUrls = [], networkCaptureBodies = true, networkRequestHeaders = [], networkResponseHeaders = [], @@ -138,6 +139,7 @@ export class Replay implements Integration { slowClickTimeout, slowClickIgnoreSelectors, networkDetailAllowUrls, + networkDetailDenyUrls, networkCaptureBodies, networkRequestHeaders: _getMergedNetworkHeaders(networkRequestHeaders), networkResponseHeaders: _getMergedNetworkHeaders(networkResponseHeaders), diff --git a/packages/replay/src/replay.ts b/packages/replay/src/replay.ts index 8fab410a0c34..c72306239533 100644 --- a/packages/replay/src/replay.ts +++ b/packages/replay/src/replay.ts @@ -7,7 +7,6 @@ import { logger } from '@sentry/utils'; import { BUFFER_CHECKOUT_TIME, MAX_SESSION_LIFE, - MULTI_CLICK_TIMEOUT, SESSION_IDLE_EXPIRE_DURATION, SESSION_IDLE_PAUSE_DURATION, SLOW_CLICK_SCROLL_TIMEOUT, @@ -175,7 +174,6 @@ export class ReplayContainer implements ReplayContainerInterface { timeout: slowClickTimeout, scrollTimeout: SLOW_CLICK_SCROLL_TIMEOUT, ignoreSelector: slowClickIgnoreSelectors ? slowClickIgnoreSelectors.join(',') : '', - multiClickTimeout: MULTI_CLICK_TIMEOUT, } : undefined; diff --git a/packages/replay/src/types/replay.ts b/packages/replay/src/types/replay.ts index e2cfddd0f525..00cda5e3c6e7 100644 --- a/packages/replay/src/types/replay.ts +++ b/packages/replay/src/types/replay.ts @@ -70,23 +70,32 @@ export interface ReplayNetworkOptions { */ networkDetailAllowUrls: (string | RegExp)[]; + /** + * Deny request/response details for XHR/Fetch requests that match the given URLs. + * The URLs can be strings or regular expressions. + * When provided a string, we will deny any URL that contains the given string. + * You can use a Regex to handle exact matches or more complex matching. + * URLs matching these patterns will not have bodies & additional headers captured. + */ + networkDetailDenyUrls: (string | RegExp)[]; + /** * If request & response bodies should be captured. - * Only applies to URLs matched by `networkDetailAllowUrls`. + * Only applies to URLs matched by `networkDetailAllowUrls` and not matched by `networkDetailDenyUrls`. * Defaults to true. */ networkCaptureBodies: boolean; /** * Capture the following request headers, in addition to the default ones. - * Only applies to URLs matched by `networkDetailAllowUrls`. + * Only applies to URLs matched by `networkDetailAllowUrls` and not matched by `networkDetailDenyUrls`. * Any headers defined here will be captured in addition to the default headers. */ networkRequestHeaders: string[]; /** * Capture the following response headers, in addition to the default ones. - * Only applies to URLs matched by `networkDetailAllowUrls`. + * Only applies to URLs matched by `networkDetailAllowUrls` and not matched by `networkDetailDenyUrls`. * Any headers defined here will be captured in addition to the default headers. */ networkResponseHeaders: string[]; @@ -476,5 +485,4 @@ export interface SlowClickConfig { timeout: number; scrollTimeout: number; ignoreSelector: string; - multiClickTimeout: number; } diff --git a/packages/replay/src/util/sendReplayRequest.ts b/packages/replay/src/util/sendReplayRequest.ts index b6f49c0b9c9a..f0a01f0bf79a 100644 --- a/packages/replay/src/util/sendReplayRequest.ts +++ b/packages/replay/src/util/sendReplayRequest.ts @@ -93,6 +93,12 @@ export async function sendReplayRequest({ } */ + // Prevent this data (which, if it exists, was used in earlier steps in the processing pipeline) from being sent to + // sentry. (Note: Our use of this property comes and goes with whatever we might be debugging, whatever hacks we may + // have temporarily added, etc. Even if we don't happen to be using it at some point in the future, let's not get rid + // of this `delete`, lest we miss putting it back in the next time the property is in use.) + delete replayEvent.sdkProcessingMetadata; + const envelope = createReplayEnvelope(replayEvent, preparedRecordingData, dsn, client.getOptions().tunnel); let response: void | TransportMakeRequestResponse; diff --git a/packages/replay/test/unit/coreHandlers/handleClick.test.ts b/packages/replay/test/unit/coreHandlers/handleClick.test.ts index 8b06e4683656..f2980e60df69 100644 --- a/packages/replay/test/unit/coreHandlers/handleClick.test.ts +++ b/packages/replay/test/unit/coreHandlers/handleClick.test.ts @@ -26,7 +26,6 @@ describe('Unit | coreHandlers | handleClick', () => { timeout: 3_000, scrollTimeout: 200, ignoreSelector: '', - multiClickTimeout: 1_000, }, mockAddBreadcrumbEvent, ); @@ -72,113 +71,6 @@ describe('Unit | coreHandlers | handleClick', () => { expect(mockAddBreadcrumbEvent).toHaveBeenCalledTimes(1); }); - test('it groups multiple clicks together', async () => { - const replay = { - getCurrentRoute: () => 'test-route', - } as ReplayContainer; - - const mockAddBreadcrumbEvent = jest.fn(); - - const detector = new ClickDetector( - replay, - { - threshold: 1_000, - timeout: 3_000, - scrollTimeout: 200, - ignoreSelector: '', - multiClickTimeout: 1_000, - }, - mockAddBreadcrumbEvent, - ); - - const breadcrumb1: Breadcrumb = { - timestamp: BASE_TIMESTAMP / 1000, - data: { - nodeId: 1, - }, - }; - const breadcrumb2: Breadcrumb = { - timestamp: BASE_TIMESTAMP / 1000 + 0.2, - data: { - nodeId: 1, - }, - }; - const breadcrumb3: Breadcrumb = { - timestamp: BASE_TIMESTAMP / 1000 + 0.6, - data: { - nodeId: 1, - }, - }; - const breadcrumb4: Breadcrumb = { - timestamp: BASE_TIMESTAMP / 1000 + 2, - data: { - nodeId: 1, - }, - }; - const breadcrumb5: Breadcrumb = { - timestamp: BASE_TIMESTAMP / 1000 + 2.9, - data: { - nodeId: 1, - }, - }; - const node = document.createElement('button'); - detector.handleClick(breadcrumb1, node); - - detector.handleClick(breadcrumb2, node); - - detector.handleClick(breadcrumb3, node); - - expect(mockAddBreadcrumbEvent).toHaveBeenCalledTimes(0); - - jest.advanceTimersByTime(2_000); - - expect(mockAddBreadcrumbEvent).toHaveBeenCalledTimes(0); - - detector.handleClick(breadcrumb4, node); - detector.handleClick(breadcrumb5, node); - - jest.advanceTimersByTime(1_000); - - expect(mockAddBreadcrumbEvent).toHaveBeenCalledTimes(1); - expect(mockAddBreadcrumbEvent).toHaveBeenCalledWith(replay, { - category: 'ui.slowClickDetected', - type: 'default', - data: { - // count is not actually correct, because this is identified by a different click handler - clickCount: 1, - endReason: 'timeout', - nodeId: 1, - route: 'test-route', - timeAfterClickMs: 3000, - url: 'http://localhost/', - }, - message: undefined, - timestamp: expect.any(Number), - }); - - jest.advanceTimersByTime(2_000); - - expect(mockAddBreadcrumbEvent).toHaveBeenCalledTimes(2); - expect(mockAddBreadcrumbEvent).toHaveBeenCalledWith(replay, { - category: 'ui.slowClickDetected', - type: 'default', - data: { - // count is not actually correct, because this is identified by a different click handler - clickCount: 1, - endReason: 'timeout', - nodeId: 1, - route: 'test-route', - timeAfterClickMs: 3000, - url: 'http://localhost/', - }, - message: undefined, - timestamp: expect.any(Number), - }); - - jest.advanceTimersByTime(5_000); - expect(mockAddBreadcrumbEvent).toHaveBeenCalledTimes(2); - }); - test('it captures clicks on different elements', async () => { const replay = { getCurrentRoute: () => 'test-route', @@ -193,7 +85,6 @@ describe('Unit | coreHandlers | handleClick', () => { timeout: 3_000, scrollTimeout: 200, ignoreSelector: '', - multiClickTimeout: 1_000, }, mockAddBreadcrumbEvent, ); @@ -247,7 +138,6 @@ describe('Unit | coreHandlers | handleClick', () => { timeout: 3_000, scrollTimeout: 200, ignoreSelector: '', - multiClickTimeout: 1_000, }, mockAddBreadcrumbEvent, ); @@ -304,7 +194,6 @@ describe('Unit | coreHandlers | handleClick', () => { timeout: 3_000, scrollTimeout: 200, ignoreSelector: '', - multiClickTimeout: 1_000, }, mockAddBreadcrumbEvent, ); @@ -437,7 +326,6 @@ describe('Unit | coreHandlers | handleClick', () => { timeout: 3_000, scrollTimeout: 200, ignoreSelector: '', - multiClickTimeout: 1_000, }, mockAddBreadcrumbEvent, ); diff --git a/packages/replay/test/unit/coreHandlers/handleNetworkBreadcrumbs.test.ts b/packages/replay/test/unit/coreHandlers/handleNetworkBreadcrumbs.test.ts index 825c6e4eb452..c0ad9ef59b49 100644 --- a/packages/replay/test/unit/coreHandlers/handleNetworkBreadcrumbs.test.ts +++ b/packages/replay/test/unit/coreHandlers/handleNetworkBreadcrumbs.test.ts @@ -63,6 +63,7 @@ describe('Unit | coreHandlers | handleNetworkBreadcrumbs', () => { textEncoder: new TextEncoder(), replay: setupReplayContainer(), networkDetailAllowUrls: ['https://example.com'], + networkDetailDenyUrls: ['http://localhost:8080'], networkCaptureBodies: false, networkRequestHeaders: ['content-type', 'accept', 'x-custom-header'], networkResponseHeaders: ['content-type', 'accept', 'x-custom-header'], @@ -1382,5 +1383,166 @@ other-header: test`; ]); }); }); + + describe.each([ + ['exact string match', 'https://example.com/foo'], + ['partial string match', 'https://example.com/bar/what'], + ['exact regex match', 'http://example.com/exact'], + ['partial regex match', 'http://example.com/partial/string'], + ])('matching URL %s', (_label, url) => { + it('correctly deny URL for fetch request', async () => { + options.networkDetailDenyUrls = [ + 'https://example.com/foo', + 'com/bar', + /^http:\/\/example.com\/exact$/, + /^http:\/\/example.com\/partial/, + ]; + + const breadcrumb: Breadcrumb = { + category: 'fetch', + data: { + method: 'GET', + url, + status_code: 200, + }, + }; + + const mockResponse = getMockResponse('13', 'test response'); + + const hint: FetchBreadcrumbHint = { + input: ['GET', { body: 'test input' }], + response: mockResponse, + startTimestamp: BASE_TIMESTAMP + 1000, + endTimestamp: BASE_TIMESTAMP + 2000, + }; + beforeAddNetworkBreadcrumb(options, breadcrumb, hint); + + expect(breadcrumb).toEqual({ + category: 'fetch', + data: { + method: 'GET', + request_body_size: 10, + response_body_size: 13, + status_code: 200, + url, + }, + }); + + await waitForReplayEventBuffer(); + + expect((options.replay.eventBuffer as EventBufferArray).events).toEqual([ + { + data: { + payload: { + data: { + method: 'GET', + request: { + _meta: { + warnings: ['URL_SKIPPED'], + }, + headers: {}, + size: 10, + }, + response: { + _meta: { + warnings: ['URL_SKIPPED'], + }, + headers: {}, + size: 13, + }, + statusCode: 200, + }, + description: url, + endTimestamp: (BASE_TIMESTAMP + 2000) / 1000, + op: 'resource.fetch', + startTimestamp: (BASE_TIMESTAMP + 1000) / 1000, + }, + tag: 'performanceSpan', + }, + timestamp: (BASE_TIMESTAMP + 1000) / 1000, + type: 5, + }, + ]); + }); + + it('correctly deny URL for xhr request', async () => { + options.networkDetailDenyUrls = [ + 'https://example.com/foo', + 'com/bar', + /^http:\/\/example.com\/exact$/, + /^http:\/\/example.com\/partial/, + ]; + + const breadcrumb: Breadcrumb = { + category: 'xhr', + data: { + method: 'GET', + url, + status_code: 200, + }, + }; + const xhr = new XMLHttpRequest(); + Object.defineProperty(xhr, 'response', { + value: 'test response', + }); + Object.defineProperty(xhr, 'responseText', { + value: 'test response', + }); + const hint: XhrBreadcrumbHint = { + xhr, + input: 'test input', + startTimestamp: BASE_TIMESTAMP + 1000, + endTimestamp: BASE_TIMESTAMP + 2000, + }; + beforeAddNetworkBreadcrumb(options, breadcrumb, hint); + + expect(breadcrumb).toEqual({ + category: 'xhr', + data: { + method: 'GET', + request_body_size: 10, + response_body_size: 13, + status_code: 200, + url, + }, + }); + + await waitForReplayEventBuffer(); + + expect((options.replay.eventBuffer as EventBufferArray).events).toEqual([ + { + data: { + payload: { + data: { + method: 'GET', + request: { + _meta: { + warnings: ['URL_SKIPPED'], + }, + headers: {}, + size: 10, + }, + response: { + _meta: { + warnings: ['URL_SKIPPED'], + }, + headers: {}, + size: 13, + }, + statusCode: 200, + }, + description: url, + endTimestamp: (BASE_TIMESTAMP + 2000) / 1000, + op: 'resource.xhr', + startTimestamp: (BASE_TIMESTAMP + 1000) / 1000, + }, + tag: 'performanceSpan', + }, + timestamp: (BASE_TIMESTAMP + 1000) / 1000, + type: 5, + }, + ]); + }); + }); }); }); diff --git a/packages/replay/test/unit/util/prepareReplayEvent.test.ts b/packages/replay/test/unit/util/prepareReplayEvent.test.ts index b0f121e1256a..5ada534f65a0 100644 --- a/packages/replay/test/unit/util/prepareReplayEvent.test.ts +++ b/packages/replay/test/unit/util/prepareReplayEvent.test.ts @@ -82,7 +82,7 @@ describe('Unit | util | prepareReplayEvent', () => { name: 'sentry.javascript.testSdk', version: '1.0.0', }, - sdkProcessingMetadata: {}, + sdkProcessingMetadata: expect.any(Object), breadcrumbs: undefined, }); }); diff --git a/packages/replay/test/utils/setupReplayContainer.ts b/packages/replay/test/utils/setupReplayContainer.ts index 0237afddb538..e2a49052a799 100644 --- a/packages/replay/test/utils/setupReplayContainer.ts +++ b/packages/replay/test/utils/setupReplayContainer.ts @@ -12,6 +12,7 @@ const DEFAULT_OPTIONS = { useCompression: false, blockAllMedia: true, networkDetailAllowUrls: [], + networkDetailDenyUrls: [], networkCaptureBodies: true, networkRequestHeaders: [], networkResponseHeaders: [], diff --git a/packages/serverless/src/awslambda.ts b/packages/serverless/src/awslambda.ts index 888b57852327..b5cf41af5e90 100644 --- a/packages/serverless/src/awslambda.ts +++ b/packages/serverless/src/awslambda.ts @@ -3,7 +3,7 @@ import type { Scope } from '@sentry/node'; import * as Sentry from '@sentry/node'; import { captureException, captureMessage, flush, getCurrentHub, withScope } from '@sentry/node'; import type { Integration } from '@sentry/types'; -import { baggageHeaderToDynamicSamplingContext, extractTraceparentData, isString, logger } from '@sentry/utils'; +import { isString, logger, tracingContextFromHeaders } from '@sentry/utils'; // NOTE: I have no idea how to fix this right now, and don't want to waste more time, as it builds just fine — Kamil // eslint-disable-next-line import/no-unresolved import type { Context, Handler } from 'aws-lambda'; @@ -274,17 +274,20 @@ export function wrapHandler( }, timeoutWarningDelay) as unknown as NodeJS.Timeout; } - // Applying `sentry-trace` to context - let traceparentData; - const eventWithHeaders = event as { headers?: { [key: string]: string } }; - if (eventWithHeaders.headers && isString(eventWithHeaders.headers['sentry-trace'])) { - traceparentData = extractTraceparentData(eventWithHeaders.headers['sentry-trace']); - } + const hub = getCurrentHub(); - const baggageHeader = eventWithHeaders.headers && eventWithHeaders.headers.baggage; - const dynamicSamplingContext = baggageHeaderToDynamicSamplingContext(baggageHeader); + const eventWithHeaders = event as { headers?: { [key: string]: string } }; - const hub = getCurrentHub(); + const sentryTrace = + eventWithHeaders.headers && isString(eventWithHeaders.headers['sentry-trace']) + ? eventWithHeaders.headers['sentry-trace'] + : undefined; + const baggage = eventWithHeaders.headers?.baggage; + const { traceparentData, dynamicSamplingContext, propagationContext } = tracingContextFromHeaders( + sentryTrace, + baggage, + ); + hub.getScope().setPropagationContext(propagationContext); const transaction = hub.startTransaction({ name: context.functionName, diff --git a/packages/serverless/src/gcpfunction/http.ts b/packages/serverless/src/gcpfunction/http.ts index 9bc9052d179d..d3b24f5ba743 100644 --- a/packages/serverless/src/gcpfunction/http.ts +++ b/packages/serverless/src/gcpfunction/http.ts @@ -1,13 +1,6 @@ import type { AddRequestDataToEventOptions } from '@sentry/node'; import { captureException, flush, getCurrentHub } from '@sentry/node'; -import { - baggageHeaderToDynamicSamplingContext, - extractTraceparentData, - isString, - isThenable, - logger, - stripUrlQueryAndFragment, -} from '@sentry/utils'; +import { isString, isThenable, logger, stripUrlQueryAndFragment, tracingContextFromHeaders } from '@sentry/utils'; import { domainify, proxyFunction } from './../utils'; import type { HttpFunction, WrapperOptions } from './general'; @@ -68,21 +61,18 @@ function _wrapHttpFunction(fn: HttpFunction, wrapOptions: Partial { + const hub = getCurrentHub(); + const reqMethod = (req.method || '').toUpperCase(); const reqUrl = stripUrlQueryAndFragment(req.originalUrl || req.url || ''); - // Applying `sentry-trace` to context - let traceparentData; - const reqWithHeaders = req as { headers?: { [key: string]: string } }; - if (reqWithHeaders.headers && isString(reqWithHeaders.headers['sentry-trace'])) { - traceparentData = extractTraceparentData(reqWithHeaders.headers['sentry-trace']); - } - - const baggageHeader = reqWithHeaders.headers?.baggage; - - const dynamicSamplingContext = baggageHeaderToDynamicSamplingContext(baggageHeader); - - const hub = getCurrentHub(); + const sentryTrace = req.headers && isString(req.headers['sentry-trace']) ? req.headers['sentry-trace'] : undefined; + const baggage = req.headers?.baggage; + const { traceparentData, dynamicSamplingContext, propagationContext } = tracingContextFromHeaders( + sentryTrace, + baggage, + ); + hub.getScope().setPropagationContext(propagationContext); const transaction = hub.startTransaction({ name: `${reqMethod} ${reqUrl}`, diff --git a/packages/serverless/test/__mocks__/@sentry/node.ts b/packages/serverless/test/__mocks__/@sentry/node.ts index 355065317ea7..6da20c091780 100644 --- a/packages/serverless/test/__mocks__/@sentry/node.ts +++ b/packages/serverless/test/__mocks__/@sentry/node.ts @@ -22,6 +22,7 @@ export const fakeScope = { setSpan: jest.fn(), getTransaction: jest.fn(() => fakeTransaction), setSDKProcessingMetadata: jest.fn(), + setPropagationContext: jest.fn(), }; export const fakeSpan = { finish: jest.fn(), diff --git a/packages/sveltekit/src/client/load.ts b/packages/sveltekit/src/client/load.ts index e56d33b2e23c..8907994e8649 100644 --- a/packages/sveltekit/src/client/load.ts +++ b/packages/sveltekit/src/client/load.ts @@ -129,6 +129,8 @@ function instrumentSvelteKitFetch(originalFetch: SvelteKitFetch): SvelteKitFetch return originalFetch; } + const options = client.getOptions(); + const browserTracingIntegration = client.getIntegrationById('BrowserTracing') as BrowserTracing | undefined; const breadcrumbsIntegration = client.getIntegrationById('Breadcrumbs') as Breadcrumbs | undefined; @@ -147,7 +149,10 @@ function instrumentSvelteKitFetch(originalFetch: SvelteKitFetch): SvelteKitFetch const shouldAttachHeaders: (url: string) => boolean = url => { return ( !!shouldTraceFetch && - stringMatchesSomePattern(url, browserTracingOptions.tracePropagationTargets || ['localhost', /^\//]) + stringMatchesSomePattern( + url, + options.tracePropagationTargets || browserTracingOptions.tracePropagationTargets || ['localhost', /^\//], + ) ); }; @@ -177,20 +182,15 @@ function instrumentSvelteKitFetch(originalFetch: SvelteKitFetch): SvelteKitFetch }; const patchedInit: RequestInit = { ...init }; - const activeSpan = getCurrentHub().getScope().getSpan(); - const activeTransaction = activeSpan && activeSpan.transaction; - - const createSpan = activeTransaction && shouldCreateSpan(rawUrl); - const attachHeaders = createSpan && activeTransaction && shouldAttachHeaders(rawUrl); - - // only attach headers if we should create a span - if (attachHeaders) { - const dsc = activeTransaction.getDynamicSamplingContext(); + const hub = getCurrentHub(); + const scope = hub.getScope(); + const client = hub.getClient(); + if (client && shouldAttachHeaders(rawUrl)) { const headers = addTracingHeadersToFetchRequest( input as string | Request, - dsc, - activeSpan, + client, + scope, patchedInit as { headers: | { @@ -207,7 +207,7 @@ function instrumentSvelteKitFetch(originalFetch: SvelteKitFetch): SvelteKitFetch const patchedFetchArgs = [input, patchedInit]; - if (createSpan) { + if (shouldCreateSpan(rawUrl)) { fetchPromise = trace( { name: `${method} ${requestData.url}`, // this will become the description of the span diff --git a/packages/sveltekit/src/server/handle.ts b/packages/sveltekit/src/server/handle.ts index 6d41317cc044..5c90f42f4c9e 100644 --- a/packages/sveltekit/src/server/handle.ts +++ b/packages/sveltekit/src/server/handle.ts @@ -112,7 +112,8 @@ function instrumentHandle({ event, resolve }: Parameters[0], options: Se return resolve(event); } - const { traceparentData, dynamicSamplingContext } = getTracePropagationData(event); + const { dynamicSamplingContext, traceparentData, propagationContext } = getTracePropagationData(event); + getCurrentHub().getScope().setPropagationContext(propagationContext); return trace( { diff --git a/packages/sveltekit/src/server/load.ts b/packages/sveltekit/src/server/load.ts index c776dc639d3a..0ad28e1cb4eb 100644 --- a/packages/sveltekit/src/server/load.ts +++ b/packages/sveltekit/src/server/load.ts @@ -1,5 +1,5 @@ /* eslint-disable @sentry-internal/sdk/no-optional-chaining */ -import { trace } from '@sentry/core'; +import { getCurrentHub, trace } from '@sentry/core'; import { captureException } from '@sentry/node'; import type { TransactionContext } from '@sentry/types'; import { addExceptionMechanism, addNonEnumerableProperty, objectify } from '@sentry/utils'; @@ -121,7 +121,8 @@ export function wrapServerLoadWithSentry any>(origSe const routeId = event.route && event.route.id; - const { dynamicSamplingContext, traceparentData } = getTracePropagationData(event); + const { dynamicSamplingContext, traceparentData, propagationContext } = getTracePropagationData(event); + getCurrentHub().getScope().setPropagationContext(propagationContext); const traceLoadContext: TransactionContext = { op: 'function.sveltekit.server.load', diff --git a/packages/sveltekit/src/server/utils.ts b/packages/sveltekit/src/server/utils.ts index 8ef6acced314..1a9e1781643c 100644 --- a/packages/sveltekit/src/server/utils.ts +++ b/packages/sveltekit/src/server/utils.ts @@ -1,12 +1,5 @@ -import type { DynamicSamplingContext, StackFrame, TraceparentData } from '@sentry/types'; -import { - baggageHeaderToDynamicSamplingContext, - basename, - escapeStringForRegex, - extractTraceparentData, - GLOBAL_OBJ, - join, -} from '@sentry/utils'; +import type { StackFrame } from '@sentry/types'; +import { basename, escapeStringForRegex, GLOBAL_OBJ, join, tracingContextFromHeaders } from '@sentry/utils'; import type { RequestEvent } from '@sveltejs/kit'; import { WRAPPED_MODULE_SUFFIX } from '../vite/autoInstrument'; @@ -15,17 +8,13 @@ import type { GlobalWithSentryValues } from '../vite/injectGlobalValues'; /** * Takes a request event and extracts traceparent and DSC data * from the `sentry-trace` and `baggage` DSC headers. + * + * Sets propagation context as a side effect. */ -export function getTracePropagationData(event: RequestEvent): { - traceparentData?: TraceparentData; - dynamicSamplingContext?: Partial; -} { - const sentryTraceHeader = event.request.headers.get('sentry-trace'); +export function getTracePropagationData(event: RequestEvent): ReturnType { + const sentryTraceHeader = event.request.headers.get('sentry-trace') || ''; const baggageHeader = event.request.headers.get('baggage'); - const traceparentData = sentryTraceHeader ? extractTraceparentData(sentryTraceHeader) : undefined; - const dynamicSamplingContext = baggageHeaderToDynamicSamplingContext(baggageHeader); - - return { traceparentData, dynamicSamplingContext }; + return tracingContextFromHeaders(sentryTraceHeader, baggageHeader); } /** diff --git a/packages/sveltekit/test/client/load.test.ts b/packages/sveltekit/test/client/load.test.ts index 07608bb9845a..6373ea1ff571 100644 --- a/packages/sveltekit/test/client/load.test.ts +++ b/packages/sveltekit/test/client/load.test.ts @@ -61,6 +61,7 @@ const mockedGetIntegrationById = vi.fn(id => { const mockedGetClient = vi.fn(() => { return { getIntegrationById: mockedGetIntegrationById, + getOptions: () => ({}), }; }); @@ -77,6 +78,11 @@ vi.mock('@sentry/core', async () => { getClient: mockedGetClient, getScope: () => { return { + getPropagationContext: () => ({ + traceId: '1234567890abcdef1234567890abcdef', + spanId: '1234567890abcdef', + sampled: false, + }), getSpan: () => { return { transaction: { @@ -371,7 +377,7 @@ describe('wrapLoadWithSentry', () => { mockedBrowserTracing.options.traceFetch = true; }); - it("doesn't create a span nor propagate headers, if `shouldCreateSpanForRequest` returns false", async () => { + it("doesn't create a span if `shouldCreateSpanForRequest` returns false", async () => { mockedBrowserTracing.options.shouldCreateSpanForRequest = () => false; const wrappedLoad = wrapLoadWithSentry(load); @@ -391,10 +397,6 @@ describe('wrapLoadWithSentry', () => { expect.any(Function), ); - expect(mockedSveltekitFetch).toHaveBeenCalledWith( - ...[originalFetchArgs[0], originalFetchArgs.length === 2 ? originalFetchArgs[1] : {}], - ); - mockedBrowserTracing.options.shouldCreateSpanForRequest = () => true; }); diff --git a/packages/tracing-internal/src/browser/browsertracing.ts b/packages/tracing-internal/src/browser/browsertracing.ts index 4d1b1bca7c97..4be633821a1a 100644 --- a/packages/tracing-internal/src/browser/browsertracing.ts +++ b/packages/tracing-internal/src/browser/browsertracing.ts @@ -1,14 +1,8 @@ /* eslint-disable max-lines */ import type { Hub, IdleTransaction } from '@sentry/core'; -import { - addTracingExtensions, - extractTraceparentData, - getActiveTransaction, - startIdleTransaction, - TRACING_DEFAULTS, -} from '@sentry/core'; +import { addTracingExtensions, getActiveTransaction, startIdleTransaction, TRACING_DEFAULTS } from '@sentry/core'; import type { EventProcessor, Integration, Transaction, TransactionContext, TransactionSource } from '@sentry/types'; -import { baggageHeaderToDynamicSamplingContext, getDomElement, logger } from '@sentry/utils'; +import { getDomElement, logger, tracingContextFromHeaders } from '@sentry/utils'; import { registerBackgroundTabDetection } from './backgroundtab'; import { @@ -297,24 +291,25 @@ export class BrowserTracing implements Integration { return undefined; } + const hub = this._getCurrentHub(); + const { beforeNavigate, idleTimeout, finalTimeout, heartbeatInterval } = this.options; const isPageloadTransaction = context.op === 'pageload'; - const sentryTraceMetaTagValue = isPageloadTransaction ? getMetaContent('sentry-trace') : null; - const baggageMetaTagValue = isPageloadTransaction ? getMetaContent('baggage') : null; - - const traceParentData = sentryTraceMetaTagValue ? extractTraceparentData(sentryTraceMetaTagValue) : undefined; - const dynamicSamplingContext = baggageMetaTagValue - ? baggageHeaderToDynamicSamplingContext(baggageMetaTagValue) - : undefined; + const sentryTrace = isPageloadTransaction ? getMetaContent('sentry-trace') : ''; + const baggage = isPageloadTransaction ? getMetaContent('baggage') : ''; + const { traceparentData, dynamicSamplingContext, propagationContext } = tracingContextFromHeaders( + sentryTrace, + baggage, + ); const expandedContext: TransactionContext = { ...context, - ...traceParentData, + ...traceparentData, metadata: { ...context.metadata, - dynamicSamplingContext: traceParentData && !dynamicSamplingContext ? {} : dynamicSamplingContext, + dynamicSamplingContext: traceparentData && !dynamicSamplingContext ? {} : dynamicSamplingContext, }, trimEnd: true, }; @@ -341,7 +336,6 @@ export class BrowserTracing implements Integration { __DEBUG_BUILD__ && logger.log(`[Tracing] Starting ${finalContext.op} transaction on scope`); - const hub = this._getCurrentHub(); const { location } = WINDOW; const idleTransaction = startIdleTransaction( @@ -353,6 +347,24 @@ export class BrowserTracing implements Integration { { location }, // for use in the tracesSampler heartbeatInterval, ); + + const scope = hub.getScope(); + + // If it's a pageload and there is a meta tag set + // use the traceparentData as the propagation context + if (isPageloadTransaction && traceparentData) { + scope.setPropagationContext(propagationContext); + } else { + // Navigation transactions should set a new propagation context based on the + // created idle transaction. + scope.setPropagationContext({ + traceId: idleTransaction.traceId, + spanId: idleTransaction.spanId, + parentSpanId: idleTransaction.parentSpanId, + sampled: !!idleTransaction.sampled, + }); + } + idleTransaction.registerBeforeFinishCallback(transaction => { this._collectWebVitals(); addPerformanceEntries(transaction); @@ -424,11 +436,11 @@ export class BrowserTracing implements Integration { } /** Returns the value of a meta tag */ -export function getMetaContent(metaName: string): string | null { +export function getMetaContent(metaName: string): string | undefined { // Can't specify generic to `getDomElement` because tracing can be used // in a variety of environments, have to disable `no-unsafe-member-access` // as a result. const metaTag = getDomElement(`meta[name=${metaName}]`); // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access - return metaTag ? metaTag.getAttribute('content') : null; + return metaTag ? metaTag.getAttribute('content') : undefined; } diff --git a/packages/tracing-internal/src/browser/request.ts b/packages/tracing-internal/src/browser/request.ts index d7e397ae01ac..071b2146bb16 100644 --- a/packages/tracing-internal/src/browser/request.ts +++ b/packages/tracing-internal/src/browser/request.ts @@ -1,11 +1,12 @@ /* eslint-disable max-lines */ -import { getCurrentHub, hasTracingEnabled } from '@sentry/core'; -import type { DynamicSamplingContext, Span } from '@sentry/types'; +import { getCurrentHub, getDynamicSamplingContextFromClient, hasTracingEnabled } from '@sentry/core'; +import type { Client, Scope, Span } from '@sentry/types'; import { addInstrumentationHandler, BAGGAGE_HEADER_NAME, browserPerformanceTimeOrigin, dynamicSamplingContextToSentryBaggageHeader, + generateSentryTraceHeader, isInstanceOf, SENTRY_XHR_DATA_KEY, stringMatchesSomePattern, @@ -214,17 +215,19 @@ export function shouldAttachHeaders(url: string, tracePropagationTargets: (strin * * @returns Span if a span was created, otherwise void. */ -function fetchCallback( +export function fetchCallback( handlerData: FetchData, shouldCreateSpan: (url: string) => boolean, shouldAttachHeaders: (url: string) => boolean, spans: Record, -): Span | void { - if (!hasTracingEnabled() || !(handlerData.fetchData && shouldCreateSpan(handlerData.fetchData.url))) { - return; +): Span | undefined { + if (!hasTracingEnabled() || !handlerData.fetchData) { + return undefined; } - if (handlerData.endTimestamp) { + const shouldCreateSpanResult = shouldCreateSpan(handlerData.fetchData.url); + + if (handlerData.endTimestamp && shouldCreateSpanResult) { const spanId = handlerData.fetchData.__span; if (!spanId) return; @@ -251,27 +254,35 @@ function fetchCallback( // eslint-disable-next-line @typescript-eslint/no-dynamic-delete delete spans[spanId]; } - return; + return undefined; } - const currentSpan = getCurrentHub().getScope().getSpan(); - const activeTransaction = currentSpan && currentSpan.transaction; - - if (currentSpan && activeTransaction) { - const { method, url } = handlerData.fetchData; - const span = currentSpan.startChild({ - data: { - url, - type: 'fetch', - 'http.method': method, - }, - description: `${method} ${url}`, - op: 'http.client', - }); - + const hub = getCurrentHub(); + const scope = hub.getScope(); + const client = hub.getClient(); + const parentSpan = scope.getSpan(); + + const { method, url } = handlerData.fetchData; + + const span = + shouldCreateSpanResult && parentSpan + ? parentSpan.startChild({ + data: { + url, + type: 'fetch', + 'http.method': method, + }, + description: `${method} ${url}`, + op: 'http.client', + }) + : undefined; + + if (span) { handlerData.fetchData.__span = span.spanId; spans[span.spanId] = span; + } + if (shouldAttachHeaders(handlerData.fetchData.url) && client) { const request: string | Request = handlerData.args[0]; // In case the user hasn't set the second argument of a fetch call we default it to `{}`. @@ -280,16 +291,11 @@ function fetchCallback( // eslint-disable-next-line @typescript-eslint/no-explicit-any const options: { [key: string]: any } = handlerData.args[1]; - if (shouldAttachHeaders(handlerData.fetchData.url)) { - options.headers = addTracingHeadersToFetchRequest( - request, - activeTransaction.getDynamicSamplingContext(), - span, - options, - ); - } - return span; + // eslint-disable-next-line @typescript-eslint/no-explicit-any, @typescript-eslint/no-unsafe-member-access + options.headers = addTracingHeadersToFetchRequest(request, client, scope, options); } + + return span; } /** @@ -297,8 +303,8 @@ function fetchCallback( */ export function addTracingHeadersToFetchRequest( request: string | unknown, // unknown is actually type Request but we can't export DOM types from this package, - dynamicSamplingContext: Partial, - span: Span, + client: Client, + scope: Scope, options: { headers?: | { @@ -306,9 +312,21 @@ export function addTracingHeadersToFetchRequest( } | PolymorphicRequestHeaders; }, -): PolymorphicRequestHeaders { +): PolymorphicRequestHeaders | undefined { + const span = scope.getSpan(); + + const transaction = span && span.transaction; + + const { traceId, sampled, dsc } = scope.getPropagationContext(); + + const sentryTraceHeader = span ? span.toTraceparent() : generateSentryTraceHeader(traceId, undefined, sampled); + const dynamicSamplingContext = transaction + ? transaction.getDynamicSamplingContext() + : dsc + ? dsc + : getDynamicSamplingContextFromClient(traceId, client, scope); + const sentryBaggageHeader = dynamicSamplingContextToSentryBaggageHeader(dynamicSamplingContext); - const sentryTraceHeader = span.toTraceparent(); const headers = typeof Request !== 'undefined' && isInstanceOf(request, Request) ? (request as Request).headers : options.headers; @@ -364,25 +382,24 @@ export function addTracingHeadersToFetchRequest( * * @returns Span if a span was created, otherwise void. */ -function xhrCallback( +// eslint-disable-next-line complexity +export function xhrCallback( handlerData: XHRData, shouldCreateSpan: (url: string) => boolean, shouldAttachHeaders: (url: string) => boolean, spans: Record, -): Span | void { +): Span | undefined { const xhr = handlerData.xhr; const sentryXhrData = xhr && xhr[SENTRY_XHR_DATA_KEY]; - if ( - !hasTracingEnabled() || - (xhr && xhr.__sentry_own_request__) || - !(xhr && sentryXhrData && shouldCreateSpan(sentryXhrData.url)) - ) { - return; + if (!hasTracingEnabled() || (xhr && xhr.__sentry_own_request__) || !xhr || !sentryXhrData) { + return undefined; } + const shouldCreateSpanResult = shouldCreateSpan(sentryXhrData.url); + // check first if the request has finished and is tracked by an existing span which should now end - if (handlerData.endTimestamp) { + if (handlerData.endTimestamp && shouldCreateSpanResult) { const spanId = xhr.__sentry_xhr_span_id__; if (!spanId) return; @@ -394,45 +411,68 @@ function xhrCallback( // eslint-disable-next-line @typescript-eslint/no-dynamic-delete delete spans[spanId]; } - return; + return undefined; } - const currentSpan = getCurrentHub().getScope().getSpan(); - const activeTransaction = currentSpan && currentSpan.transaction; - - if (currentSpan && activeTransaction) { - const span = currentSpan.startChild({ - data: { - ...sentryXhrData.data, - type: 'xhr', - 'http.method': sentryXhrData.method, - url: sentryXhrData.url, - }, - description: `${sentryXhrData.method} ${sentryXhrData.url}`, - op: 'http.client', - }); - + const hub = getCurrentHub(); + const scope = hub.getScope(); + const parentSpan = scope.getSpan(); + + const span = + shouldCreateSpanResult && parentSpan + ? parentSpan.startChild({ + data: { + ...sentryXhrData.data, + type: 'xhr', + 'http.method': sentryXhrData.method, + url: sentryXhrData.url, + }, + description: `${sentryXhrData.method} ${sentryXhrData.url}`, + op: 'http.client', + }) + : undefined; + + if (span) { xhr.__sentry_xhr_span_id__ = span.spanId; spans[xhr.__sentry_xhr_span_id__] = span; + } - if (xhr.setRequestHeader && shouldAttachHeaders(sentryXhrData.url)) { - try { - xhr.setRequestHeader('sentry-trace', span.toTraceparent()); + if (xhr.setRequestHeader && shouldAttachHeaders(sentryXhrData.url)) { + if (span) { + const transaction = span && span.transaction; + const dynamicSamplingContext = transaction && transaction.getDynamicSamplingContext(); + const sentryBaggageHeader = dynamicSamplingContextToSentryBaggageHeader(dynamicSamplingContext); + setHeaderOnXhr(xhr, span.toTraceparent(), sentryBaggageHeader); + } else { + const client = hub.getClient(); + const { traceId, sampled, dsc } = scope.getPropagationContext(); + const sentryTraceHeader = generateSentryTraceHeader(traceId, undefined, sampled); + const dynamicSamplingContext = + dsc || (client ? getDynamicSamplingContextFromClient(traceId, client, scope) : undefined); + const sentryBaggageHeader = dynamicSamplingContextToSentryBaggageHeader(dynamicSamplingContext); + setHeaderOnXhr(xhr, sentryTraceHeader, sentryBaggageHeader); + } + } - const dynamicSamplingContext = activeTransaction.getDynamicSamplingContext(); - const sentryBaggageHeader = dynamicSamplingContextToSentryBaggageHeader(dynamicSamplingContext); + return span; +} - if (sentryBaggageHeader) { - // From MDN: "If this method is called several times with the same header, the values are merged into one single request header." - // We can therefore simply set a baggage header without checking what was there before - // https://developer.mozilla.org/en-US/docs/Web/API/XMLHttpRequest/setRequestHeader - xhr.setRequestHeader(BAGGAGE_HEADER_NAME, sentryBaggageHeader); - } - } catch (_) { - // Error: InvalidStateError: Failed to execute 'setRequestHeader' on 'XMLHttpRequest': The object's state must be OPENED. - } +function setHeaderOnXhr( + xhr: NonNullable, + sentryTraceHeader: string, + sentryBaggageHeader: string | undefined, +): void { + try { + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion + xhr.setRequestHeader!('sentry-trace', sentryTraceHeader); + if (sentryBaggageHeader) { + // From MDN: "If this method is called several times with the same header, the values are merged into one single request header." + // We can therefore simply set a baggage header without checking what was there before + // https://developer.mozilla.org/en-US/docs/Web/API/XMLHttpRequest/setRequestHeader + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion + xhr.setRequestHeader!(BAGGAGE_HEADER_NAME, sentryBaggageHeader); } - - return span; + } catch (_) { + // Error: InvalidStateError: Failed to execute 'setRequestHeader' on 'XMLHttpRequest': The object's state must be OPENED. } } diff --git a/packages/tracing-internal/test/browser/backgroundtab.test.ts b/packages/tracing-internal/test/browser/backgroundtab.test.ts index 436d63aab36c..5ce370e6690f 100644 --- a/packages/tracing-internal/test/browser/backgroundtab.test.ts +++ b/packages/tracing-internal/test/browser/backgroundtab.test.ts @@ -2,11 +2,11 @@ import { Hub, makeMain } from '@sentry/core'; import { JSDOM } from 'jsdom'; import { addExtensionMethods } from '../../../tracing/src'; -import { getDefaultBrowserClientOptions } from '../../../tracing/test/testutils'; +import { conditionalTest, getDefaultBrowserClientOptions } from '../../../tracing/test/testutils'; import { registerBackgroundTabDetection } from '../../src/browser/backgroundtab'; import { TestClient } from '../utils/TestClient'; -describe('registerBackgroundTabDetection', () => { +conditionalTest({ min: 10 })('registerBackgroundTabDetection', () => { let events: Record = {}; let hub: Hub; beforeEach(() => { diff --git a/packages/tracing-internal/test/browser/browsertracing.test.ts b/packages/tracing-internal/test/browser/browsertracing.test.ts index c7cec8a54735..0754afd65fc8 100644 --- a/packages/tracing-internal/test/browser/browsertracing.test.ts +++ b/packages/tracing-internal/test/browser/browsertracing.test.ts @@ -7,7 +7,7 @@ import { JSDOM } from 'jsdom'; import type { IdleTransaction } from '../../../tracing/src'; import { getActiveTransaction } from '../../../tracing/src'; -import { getDefaultBrowserClientOptions } from '../../../tracing/test/testutils'; +import { conditionalTest, getDefaultBrowserClientOptions } from '../../../tracing/test/testutils'; import type { BrowserTracingOptions } from '../../src/browser/browsertracing'; import { BrowserTracing, getMetaContent } from '../../src/browser/browsertracing'; import { defaultRequestInstrumentationOptions } from '../../src/browser/request'; @@ -58,7 +58,7 @@ beforeAll(() => { WINDOW.location = dom.window.location; }); -describe('BrowserTracing', () => { +conditionalTest({ min: 10 })('BrowserTracing', () => { let hub: Hub; beforeEach(() => { jest.useFakeTimers(); @@ -94,7 +94,6 @@ describe('BrowserTracing', () => { const browserTracing = createBrowserTracing(); expect(browserTracing.options).toEqual({ - _experiments: {}, enableLongTask: true, ...TRACING_DEFAULTS, markBackgroundTransactions: true, @@ -113,9 +112,6 @@ describe('BrowserTracing', () => { }); expect(browserTracing.options).toEqual({ - _experiments: { - enableLongTask: false, - }, enableLongTask: false, ...TRACING_DEFAULTS, markBackgroundTransactions: true, @@ -123,6 +119,9 @@ describe('BrowserTracing', () => { startTransactionOnLocationChange: true, startTransactionOnPageLoad: true, ...defaultRequestInstrumentationOptions, + _experiments: { + enableLongTask: false, + }, }); }); @@ -132,7 +131,6 @@ describe('BrowserTracing', () => { }); expect(browserTracing.options).toEqual({ - _experiments: {}, enableLongTask: false, ...TRACING_DEFAULTS, markBackgroundTransactions: true, @@ -248,6 +246,7 @@ describe('BrowserTracing', () => { traceFetch: true, traceXHR: true, tracePropagationTargets: ['something'], + _experiments: {}, }); }); @@ -261,6 +260,7 @@ describe('BrowserTracing', () => { }); expect(instrumentOutgoingRequestsMock).toHaveBeenCalledWith({ + _experiments: {}, traceFetch: true, traceXHR: true, tracePropagationTargets: ['something-else'], @@ -546,7 +546,7 @@ describe('BrowserTracing', () => { document.head.innerHTML = ''; const metaTagValue = getMetaContent('dogpark'); - expect(metaTagValue).toBe(null); + expect(metaTagValue).toBe(undefined); }); it('can pick the correct tag out of multiple options', () => { @@ -636,6 +636,7 @@ describe('BrowserTracing', () => { release: '1.0.0', environment: 'production', public_key: 'pubKey', + sampled: 'false', trace_id: expect.not.stringMatching('12312012123120121231201212312012'), }); }); diff --git a/packages/tracing-internal/test/browser/metrics/index.test.ts b/packages/tracing-internal/test/browser/metrics/index.test.ts index d325815bde31..c196fbd2467c 100644 --- a/packages/tracing-internal/test/browser/metrics/index.test.ts +++ b/packages/tracing-internal/test/browser/metrics/index.test.ts @@ -14,6 +14,7 @@ describe('_addMeasureSpans', () => { name: 'measure-1', duration: 10, startTime: 12, + detail: undefined, }; const timeOrigin = 100; diff --git a/packages/tracing-internal/test/browser/request.test.ts b/packages/tracing-internal/test/browser/request.test.ts index ee714e111a8b..9c8307e97fd7 100644 --- a/packages/tracing-internal/test/browser/request.test.ts +++ b/packages/tracing-internal/test/browser/request.test.ts @@ -98,9 +98,7 @@ describe('callbacks', () => { // each case is [shouldCreateSpanReturnValue, shouldAttachHeadersReturnValue, expectedSpan, expectedHeaderKeys] [true, true, expect.objectContaining(fetchSpan), ['sentry-trace', 'baggage']], [true, false, expect.objectContaining(fetchSpan), []], - // If there's no span then there's no parent span id to stick into a header, so no headers, even if there's a - // `tracingOrigins` match - [false, true, undefined, []], + [false, true, undefined, ['sentry-trace', 'baggage']], [false, false, undefined, []], ])( 'span creation/header attachment interaction - shouldCreateSpan: %s, shouldAttachHeaders: %s', @@ -240,6 +238,7 @@ describe('callbacks', () => { expect(finishedSpan.data).toEqual({ 'http.response_content_length': 123, 'http.method': 'GET', + 'http.response.status_code': 404, type: 'fetch', url: 'http://dogs.are.great/', }); @@ -283,9 +282,7 @@ describe('callbacks', () => { // each case is [shouldCreateSpanReturnValue, shouldAttachHeadersReturnValue, expectedSpan, expectedHeaderKeys] [true, true, expect.objectContaining(xhrSpan), ['sentry-trace', 'baggage']], [true, false, expect.objectContaining(xhrSpan), []], - // If there's no span then there's no parent span id to stick into a header, so no headers, even if there's a - // `tracingOrigins` match - [false, true, undefined, []], + [false, true, undefined, ['sentry-trace', 'baggage']], [false, false, undefined, []], ])( 'span creation/header attachment interaction - shouldCreateSpan: %s, shouldAttachHeaders: %s', diff --git a/packages/tracing-internal/test/browser/router.test.ts b/packages/tracing-internal/test/browser/router.test.ts index 65ce7e90af48..8ad414a97b80 100644 --- a/packages/tracing-internal/test/browser/router.test.ts +++ b/packages/tracing-internal/test/browser/router.test.ts @@ -1,6 +1,7 @@ import type { InstrumentHandlerCallback, InstrumentHandlerType } from '@sentry/utils'; import { JSDOM } from 'jsdom'; +import { conditionalTest } from '../../../tracing/test/testutils'; import { instrumentRoutingWithDefaults } from '../../src/browser/router'; let mockChangeHistory: ({ to, from }: { to: string; from?: string }) => void = () => undefined; @@ -16,7 +17,7 @@ jest.mock('@sentry/utils', () => { }; }); -describe('instrumentRoutingWithDefaults', () => { +conditionalTest({ min: 16 })('instrumentRoutingWithDefaults', () => { const mockFinish = jest.fn(); const customStartTransaction = jest.fn().mockReturnValue({ finish: mockFinish }); beforeEach(() => { @@ -46,6 +47,7 @@ describe('instrumentRoutingWithDefaults', () => { name: 'blank', op: 'pageload', metadata: { source: 'url' }, + startTimestamp: expect.any(Number), }); }); diff --git a/packages/tracing/test/testutils.ts b/packages/tracing/test/testutils.ts index 071ecdf5111a..484e3bf3cc35 100644 --- a/packages/tracing/test/testutils.ts +++ b/packages/tracing/test/testutils.ts @@ -1,6 +1,6 @@ import { createTransport } from '@sentry/browser'; import type { Client, ClientOptions } from '@sentry/types'; -import { GLOBAL_OBJ, resolvedSyncPromise } from '@sentry/utils'; +import { GLOBAL_OBJ, parseSemver, resolvedSyncPromise } from '@sentry/utils'; import { JSDOM } from 'jsdom'; /** @@ -58,6 +58,23 @@ export const testOnlyIfNodeVersionAtLeast = (minVersion: number): jest.It => { return it; }; +/** + * 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 }): jest.Describe => { + const NODE_VERSION = parseSemver(process.versions.node).major; + if (!NODE_VERSION) { + return describe.skip; + } + + return NODE_VERSION < (allowedVersion.min || -Infinity) || NODE_VERSION > (allowedVersion.max || Infinity) + ? describe.skip + : describe; +}; + export function getDefaultBrowserClientOptions(options: Partial = {}): ClientOptions { return { integrations: [], diff --git a/packages/tracing/test/utils.test.ts b/packages/tracing/test/utils.test.ts index 00d90e873c9e..94374ac58b23 100644 --- a/packages/tracing/test/utils.test.ts +++ b/packages/tracing/test/utils.test.ts @@ -77,6 +77,9 @@ describe('extractTraceparentData', () => { }); test('invalid', () => { + // undefined + expect(extractTraceparentData(undefined)).toBeUndefined(); + // empty string expect(extractTraceparentData('')).toBeUndefined(); diff --git a/packages/types/src/checkin.ts b/packages/types/src/checkin.ts index a316c0c7a375..b19ff7b78770 100644 --- a/packages/types/src/checkin.ts +++ b/packages/types/src/checkin.ts @@ -1,3 +1,5 @@ +import type { TraceContext } from './context'; + interface CrontabSchedule { type: 'crontab'; // The crontab schedule string, e.g. 0 * * * *. @@ -36,6 +38,9 @@ export interface SerializedCheckIn { // See: https://en.wikipedia.org/wiki/List_of_tz_database_time_zones timezone?: string; }; + contexts?: { + trace?: TraceContext; + }; } interface InProgressCheckIn { diff --git a/packages/types/src/client.ts b/packages/types/src/client.ts index 831b1c10e9f2..ac779fc3058e 100644 --- a/packages/types/src/client.ts +++ b/packages/types/src/client.ts @@ -74,9 +74,10 @@ export interface Client { * @param checkIn An object that describes a check in. * @param upsertMonitorConfig An optional object that describes a monitor config. Use this if you want * to create a monitor automatically when sending a check in. + * @param scope An optional scope containing event metadata. * @returns A string representing the id of the check in. */ - captureCheckIn?(checkIn: CheckIn, monitorConfig?: MonitorConfig): string; + captureCheckIn?(checkIn: CheckIn, monitorConfig?: MonitorConfig, scope?: Scope): string; /** Returns the current Dsn. */ getDsn(): DsnComponents | undefined; diff --git a/packages/types/src/context.ts b/packages/types/src/context.ts index bab969899cea..052d6f4a6523 100644 --- a/packages/types/src/context.ts +++ b/packages/types/src/context.ts @@ -8,6 +8,7 @@ export interface Contexts extends Record { os?: OsContext; culture?: CultureContext; response?: ResponseContext; + trace?: TraceContext; } export interface AppContext extends Record { diff --git a/packages/types/src/envelope.ts b/packages/types/src/envelope.ts index 3f3ebf999ef9..288453fc4fdb 100644 --- a/packages/types/src/envelope.ts +++ b/packages/types/src/envelope.ts @@ -20,6 +20,7 @@ export type DynamicSamplingContext = { transaction?: string; user_segment?: string; replay_id?: string; + sampled?: string; }; export type EnvelopeItemType = @@ -86,7 +87,7 @@ type ReplayRecordingItem = BaseEnvelopeItem Exception, + parser: StackParser, + key: string, + limit: number, + event: Event, + hint?: EventHint, +): void { + if (!event.exception || !event.exception.values || !hint || !isInstanceOf(hint.originalException, Error)) { + return; + } + + // Generally speaking the last item in `event.exception.values` is the exception originating from the original Error + const originalException: Exception | undefined = + event.exception.values.length > 0 ? event.exception.values[event.exception.values.length - 1] : undefined; + + // We only create exception grouping if there is an exception in the event. + if (originalException) { + event.exception.values = aggregateExceptionsFromError( + exceptionFromErrorImplementation, + parser, + limit, + hint.originalException as ExtendedError, + key, + event.exception.values, + originalException, + 0, + ); + } +} + +function aggregateExceptionsFromError( + exceptionFromErrorImplementation: (stackParser: StackParser, ex: Error) => Exception, + parser: StackParser, + limit: number, + error: ExtendedError, + key: string, + prevExceptions: Exception[], + exception: Exception, + exceptionId: number, +): Exception[] { + if (prevExceptions.length >= limit + 1) { + return prevExceptions; + } + + let newExceptions = [...prevExceptions]; + + if (isInstanceOf(error[key], Error)) { + applyExceptionGroupFieldsForParentException(exception, exceptionId); + const newException = exceptionFromErrorImplementation(parser, error[key]); + const newExceptionId = newExceptions.length; + applyExceptionGroupFieldsForChildException(newException, key, newExceptionId, exceptionId); + newExceptions = aggregateExceptionsFromError( + exceptionFromErrorImplementation, + parser, + limit, + error[key], + key, + [newException, ...newExceptions], + newException, + newExceptionId, + ); + } + + // This will create exception grouping for AggregateErrors + // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/AggregateError + if (Array.isArray(error.errors)) { + error.errors.forEach((childError, i) => { + if (isInstanceOf(childError, Error)) { + applyExceptionGroupFieldsForParentException(exception, exceptionId); + const newException = exceptionFromErrorImplementation(parser, childError); + const newExceptionId = newExceptions.length; + applyExceptionGroupFieldsForChildException(newException, `errors[${i}]`, newExceptionId, exceptionId); + newExceptions = aggregateExceptionsFromError( + exceptionFromErrorImplementation, + parser, + limit, + childError, + key, + [newException, ...newExceptions], + newException, + newExceptionId, + ); + } + }); + } + + return newExceptions; +} + +function applyExceptionGroupFieldsForParentException(exception: Exception, exceptionId: number): void { + // Don't know if this default makes sense. The protocol requires us to set these values so we pick *some* default. + exception.mechanism = exception.mechanism || { type: 'generic', handled: true }; + + exception.mechanism = { + ...exception.mechanism, + is_exception_group: true, + exception_id: exceptionId, + }; +} + +function applyExceptionGroupFieldsForChildException( + exception: Exception, + source: string, + exceptionId: number, + parentId: number | undefined, +): void { + // Don't know if this default makes sense. The protocol requires us to set these values so we pick *some* default. + exception.mechanism = exception.mechanism || { type: 'generic', handled: true }; + + exception.mechanism = { + ...exception.mechanism, + type: 'chained', + source, + exception_id: exceptionId, + parent_id: parentId, + }; +} diff --git a/packages/utils/src/baggage.ts b/packages/utils/src/baggage.ts index 406ee3adc819..674e7f60f7ef 100644 --- a/packages/utils/src/baggage.ts +++ b/packages/utils/src/baggage.ts @@ -83,8 +83,12 @@ export function baggageHeaderToDynamicSamplingContext( */ export function dynamicSamplingContextToSentryBaggageHeader( // this also takes undefined for convenience and bundle size in other places - dynamicSamplingContext: Partial, + dynamicSamplingContext?: Partial, ): string | undefined { + if (!dynamicSamplingContext) { + return undefined; + } + // Prefix all DSC keys with "sentry-" and put them into a new object const sentryPrefixedDSC = Object.entries(dynamicSamplingContext).reduce>( (acc, [dscKey, dscValue]) => { diff --git a/packages/utils/src/index.ts b/packages/utils/src/index.ts index 6b9426c22149..0464dbec25da 100644 --- a/packages/utils/src/index.ts +++ b/packages/utils/src/index.ts @@ -1,3 +1,4 @@ +export * from './aggregate-errors'; export * from './browser'; export * from './dsn'; export * from './error'; diff --git a/packages/utils/src/tracing.ts b/packages/utils/src/tracing.ts index 7eee5312ce6d..f879b856f9f2 100644 --- a/packages/utils/src/tracing.ts +++ b/packages/utils/src/tracing.ts @@ -1,4 +1,7 @@ -import type { TraceparentData } from '@sentry/types'; +import type { DynamicSamplingContext, PropagationContext, TraceparentData } from '@sentry/types'; + +import { baggageHeaderToDynamicSamplingContext } from './baggage'; +import { uuid4 } from './misc'; export const TRACEPARENT_REGEXP = new RegExp( '^[ \\t]*' + // whitespace @@ -15,11 +18,13 @@ export const TRACEPARENT_REGEXP = new RegExp( * * @returns Object containing data from the header, or undefined if traceparent string is malformed */ -export function extractTraceparentData(traceparent: string): TraceparentData | undefined { - const matches = traceparent.match(TRACEPARENT_REGEXP); +export function extractTraceparentData(traceparent?: string): TraceparentData | undefined { + if (!traceparent) { + return undefined; + } - if (!traceparent || !matches) { - // empty string or no matches is invalid traceparent data + const matches = traceparent.match(TRACEPARENT_REGEXP); + if (!matches) { return undefined; } @@ -36,3 +41,55 @@ export function extractTraceparentData(traceparent: string): TraceparentData | u parentSpanId: matches[2], }; } + +/** + * Create tracing context from incoming headers. + */ +export function tracingContextFromHeaders( + sentryTrace: Parameters[0], + baggage: Parameters[0], +): { + traceparentData: ReturnType; + dynamicSamplingContext: ReturnType; + propagationContext: PropagationContext; +} { + const traceparentData = extractTraceparentData(sentryTrace); + const dynamicSamplingContext = baggageHeaderToDynamicSamplingContext(baggage); + + const { traceId, parentSpanId, parentSampled } = traceparentData || {}; + + const propagationContext: PropagationContext = { + traceId: traceId || uuid4(), + spanId: uuid4().substring(16), + sampled: parentSampled === undefined ? false : parentSampled, + }; + + if (parentSpanId) { + propagationContext.parentSpanId = parentSpanId; + } + + if (dynamicSamplingContext) { + propagationContext.dsc = dynamicSamplingContext as DynamicSamplingContext; + } + + return { + traceparentData, + dynamicSamplingContext, + propagationContext, + }; +} + +/** + * Create sentry-trace header from span context values. + */ +export function generateSentryTraceHeader( + traceId: string = uuid4(), + spanId: string = uuid4().substring(16), + sampled?: boolean, +): string { + let sampledString = ''; + if (sampled !== undefined) { + sampledString = sampled ? '-1' : '-0'; + } + return `${traceId}-${spanId}${sampledString}`; +} diff --git a/packages/utils/src/worldwide.ts b/packages/utils/src/worldwide.ts index 37a6deb30851..12e098bf3bc7 100644 --- a/packages/utils/src/worldwide.ts +++ b/packages/utils/src/worldwide.ts @@ -55,6 +55,12 @@ export interface InternalGlobal { [key: string]: Function; }; }; + /** + * Raw module metadata that is injected by bundler plugins. + * + * Keys are `error.stack` strings, values are the metadata. + */ + _sentryModuleMetadata?: Record; } // The code below for 'isGlobalObj' and 'GLOBAL_OBJ' was copied from core-js before modification diff --git a/packages/utils/test/aggregate-errors.test.ts b/packages/utils/test/aggregate-errors.test.ts new file mode 100644 index 000000000000..9a42bba12858 --- /dev/null +++ b/packages/utils/test/aggregate-errors.test.ts @@ -0,0 +1,275 @@ +import type { Event, EventHint, Exception, ExtendedError, StackParser } from '@sentry/types'; + +import { applyAggregateErrorsToEvent, createStackParser } from '../src/index'; + +const stackParser = createStackParser([0, line => ({ filename: line })]); +const exceptionFromError = (_stackParser: StackParser, ex: Error): Exception => { + return { value: ex.message, mechanism: { type: 'instrument', handled: true } }; +}; + +describe('applyAggregateErrorsToEvent()', () => { + test('should not do anything if event does not contain an exception', () => { + const event: Event = { exception: undefined }; + const eventHint: EventHint = { originalException: new Error() }; + applyAggregateErrorsToEvent(exceptionFromError, stackParser, 'cause', 100, event, eventHint); + + // no changes + expect(event).toStrictEqual({ exception: undefined }); + }); + + test('should not do anything if event does not contain exception values', () => { + const event: Event = { exception: { values: undefined } }; + const eventHint: EventHint = { originalException: new Error() }; + applyAggregateErrorsToEvent(exceptionFromError, stackParser, 'cause', 100, event, eventHint); + + // no changes + expect(event).toStrictEqual({ exception: { values: undefined } }); + }); + + test('should not do anything if event does not contain an event hint', () => { + const event: Event = { exception: { values: [] } }; + applyAggregateErrorsToEvent(exceptionFromError, stackParser, 'cause', 100, event, undefined); + + // no changes + expect(event).toStrictEqual({ exception: { values: [] } }); + }); + + test('should not do anything if the event hint does not contain an original exception', () => { + const event: Event = { exception: { values: [] } }; + const eventHint: EventHint = { originalException: undefined }; + applyAggregateErrorsToEvent(exceptionFromError, stackParser, 'cause', 100, event, eventHint); + + // no changes + expect(event).toStrictEqual({ exception: { values: [] } }); + }); + + test('should recursively walk the original exception based on the `key` option and add them as exceptions to the event', () => { + const key = 'cause'; + const originalException: ExtendedError = new Error('Root Error'); + originalException[key] = new Error('Nested Error 1'); + originalException[key][key] = new Error('Nested Error 2'); + + const event: Event = { exception: { values: [exceptionFromError(stackParser, originalException)] } }; + const eventHint: EventHint = { originalException }; + + applyAggregateErrorsToEvent(exceptionFromError, stackParser, key, 100, event, eventHint); + expect(event).toStrictEqual({ + exception: { + values: [ + { + value: 'Nested Error 2', + mechanism: { + exception_id: 2, + handled: true, + parent_id: 1, + source: 'cause', + type: 'chained', + }, + }, + { + value: 'Nested Error 1', + mechanism: { + exception_id: 1, + handled: true, + parent_id: 0, + is_exception_group: true, + source: 'cause', + type: 'chained', + }, + }, + { + value: 'Root Error', + mechanism: { + exception_id: 0, + handled: true, + is_exception_group: true, + type: 'instrument', + }, + }, + ], + }, + }); + }); + + test('should not modify event if there are no attached errors', () => { + const originalException: ExtendedError = new Error('Some Error'); + + const event: Event = { exception: { values: [exceptionFromError(stackParser, originalException)] } }; + const eventHint: EventHint = { originalException }; + + applyAggregateErrorsToEvent(exceptionFromError, stackParser, 'cause', 100, event, eventHint); + + // no changes + expect(event).toStrictEqual({ exception: { values: [exceptionFromError(stackParser, originalException)] } }); + }); + + test('should allow to limit number of attached errors', () => { + const key = 'cause'; + const originalException: ExtendedError = new Error('Root Error'); + const event: Event = { exception: { values: [exceptionFromError(stackParser, originalException)] } }; + + let err = originalException; + for (let i = 0; i < 10; i++) { + const newErr = new Error('Nested Error!'); + err[key] = newErr; + err = newErr; + } + + const eventHint: EventHint = { originalException }; + applyAggregateErrorsToEvent(exceptionFromError, stackParser, key, 5, event, eventHint); + + // 6 -> one for original exception + 5 linked + expect(event.exception?.values).toHaveLength(5 + 1); + + // Last exception in list should be the root exception + expect(event.exception?.values?.[event.exception?.values.length - 1]).toStrictEqual({ + value: 'Root Error', + mechanism: { + exception_id: 0, + handled: true, + is_exception_group: true, + type: 'instrument', + }, + }); + }); + + test('should keep the original mechanism type for the root exception', () => { + const fakeAggregateError: ExtendedError = new Error('Root Error'); + fakeAggregateError.errors = [new Error('Nested Error 1'), new Error('Nested Error 2')]; + + const event: Event = { exception: { values: [exceptionFromError(stackParser, fakeAggregateError)] } }; + const eventHint: EventHint = { originalException: fakeAggregateError }; + + applyAggregateErrorsToEvent(exceptionFromError, stackParser, 'cause', 100, event, eventHint); + expect(event.exception?.values?.[event.exception.values.length - 1].mechanism?.type).toBe('instrument'); + }); + + test('should recursively walk mixed errors (Aggregate errors and based on `key`)', () => { + const chainedError: ExtendedError = new Error('Nested Error 3'); + chainedError.cause = new Error('Nested Error 4'); + const fakeAggregateError2: ExtendedError = new Error('AggregateError2'); + fakeAggregateError2.errors = [new Error('Nested Error 2'), chainedError]; + const fakeAggregateError1: ExtendedError = new Error('AggregateError1'); + fakeAggregateError1.errors = [new Error('Nested Error 1'), fakeAggregateError2]; + + const event: Event = { exception: { values: [exceptionFromError(stackParser, fakeAggregateError1)] } }; + const eventHint: EventHint = { originalException: fakeAggregateError1 }; + + applyAggregateErrorsToEvent(exceptionFromError, stackParser, 'cause', 100, event, eventHint); + expect(event).toStrictEqual({ + exception: { + values: [ + { + mechanism: { + exception_id: 5, + handled: true, + parent_id: 4, + source: 'cause', + type: 'chained', + }, + value: 'Nested Error 4', + }, + { + mechanism: { + exception_id: 4, + handled: true, + is_exception_group: true, + parent_id: 2, + source: 'errors[1]', + type: 'chained', + }, + value: 'Nested Error 3', + }, + { + mechanism: { + exception_id: 3, + handled: true, + parent_id: 2, + source: 'errors[0]', + type: 'chained', + }, + value: 'Nested Error 2', + }, + { + mechanism: { + exception_id: 2, + handled: true, + is_exception_group: true, + parent_id: 0, + source: 'errors[1]', + type: 'chained', + }, + value: 'AggregateError2', + }, + { + mechanism: { + exception_id: 1, + handled: true, + parent_id: 0, + source: 'errors[0]', + type: 'chained', + }, + value: 'Nested Error 1', + }, + { + mechanism: { + exception_id: 0, + handled: true, + is_exception_group: true, + type: 'instrument', + }, + value: 'AggregateError1', + }, + ], + }, + }); + }); + + test('should keep the original mechanism type for the root exception', () => { + const key = 'cause'; + const originalException: ExtendedError = new Error('Root Error'); + originalException[key] = new Error('Nested Error 1'); + originalException[key][key] = new Error('Nested Error 2'); + + const event: Event = { exception: { values: [exceptionFromError(stackParser, originalException)] } }; + const eventHint: EventHint = { originalException }; + + applyAggregateErrorsToEvent(exceptionFromError, stackParser, key, 100, event, eventHint); + expect(event).toStrictEqual({ + exception: { + values: [ + { + value: 'Nested Error 2', + mechanism: { + exception_id: 2, + handled: true, + parent_id: 1, + source: 'cause', + type: 'chained', + }, + }, + { + value: 'Nested Error 1', + mechanism: { + exception_id: 1, + handled: true, + parent_id: 0, + is_exception_group: true, + source: 'cause', + type: 'chained', + }, + }, + { + value: 'Root Error', + mechanism: { + exception_id: 0, + handled: true, + is_exception_group: true, + type: 'instrument', + }, + }, + ], + }, + }); + }); +}); diff --git a/packages/utils/test/baggage.test.ts b/packages/utils/test/baggage.test.ts index 8f848badf9de..539a34e44d9c 100644 --- a/packages/utils/test/baggage.test.ts +++ b/packages/utils/test/baggage.test.ts @@ -28,6 +28,7 @@ test.each([ }); test.each([ + [undefined, undefined], [{}, undefined], [{ release: 'abcdf' }, 'sentry-release=abcdf'], [{ release: 'abcdf', environment: '1234' }, 'sentry-release=abcdf,sentry-environment=1234'], diff --git a/scripts/node-unit-tests.ts b/scripts/node-unit-tests.ts index e5efc7c2ebd3..c32fa6376d38 100644 --- a/scripts/node-unit-tests.ts +++ b/scripts/node-unit-tests.ts @@ -22,6 +22,7 @@ const NODE_8_SKIP_TESTS_PACKAGES = [ '@sentry/nextjs', '@sentry/remix', '@sentry/sveltekit', + '@sentry-internal/replay-worker', ]; // We have to downgrade some of our dependencies in order to run tests in Node 8 and 10. @@ -34,7 +35,7 @@ const NODE_8_LEGACY_DEPENDENCIES = [ 'lerna@3.13.4', ]; -const NODE_10_SKIP_TESTS_PACKAGES = ['@sentry/remix', '@sentry/sveltekit']; +const NODE_10_SKIP_TESTS_PACKAGES = ['@sentry/remix', '@sentry/sveltekit', '@sentry-internal/replay-worker']; const NODE_10_LEGACY_DEPENDENCIES = ['jsdom@16.x', 'lerna@3.13.4']; const NODE_12_SKIP_TESTS_PACKAGES = ['@sentry/remix', '@sentry/sveltekit']; diff --git a/yarn.lock b/yarn.lock index 224622dc7189..8431c6928ee7 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2817,10 +2817,17 @@ resolved "https://registry.yarnpkg.com/@hutson/parse-repository-url/-/parse-repository-url-3.0.2.tgz#98c23c950a3d9b6c8f0daed06da6c3af06981340" integrity sha512-H9XAx3hc0BQHY6l+IFSWHDySypcXsvsuLhgYLUGywmJ5pswRVQJUHpOsobnLYp2ZUaUlKiKDrgWWhosOwAEM8Q== -"@isaacs/string-locale-compare@^1.1.0": - version "1.1.0" - resolved "https://registry.yarnpkg.com/@isaacs/string-locale-compare/-/string-locale-compare-1.1.0.tgz#291c227e93fd407a96ecd59879a35809120e432b" - integrity sha512-SQ7Kzhh9+D+ZW9MA0zkYv3VXhIDNx+LzM6EJ+/65I3QY+enU6Itte7E5XX7EWrqLW2FN4n06GWzBnPoC3th2aQ== +"@isaacs/cliui@^8.0.2": + version "8.0.2" + resolved "https://registry.yarnpkg.com/@isaacs/cliui/-/cliui-8.0.2.tgz#b37667b7bc181c168782259bab42474fbf52b550" + integrity sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA== + dependencies: + string-width "^5.1.2" + string-width-cjs "npm:string-width@^4.2.0" + strip-ansi "^7.0.1" + strip-ansi-cjs "npm:strip-ansi@^6.0.1" + wrap-ansi "^8.1.0" + wrap-ansi-cjs "npm:wrap-ansi@^7.0.0" "@istanbuljs/load-nyc-config@^1.0.0": version "1.1.0" @@ -2946,6 +2953,13 @@ terminal-link "^2.0.0" v8-to-istanbul "^8.1.0" +"@jest/schemas@^29.4.3": + version "29.4.3" + resolved "https://registry.yarnpkg.com/@jest/schemas/-/schemas-29.4.3.tgz#39cf1b8469afc40b6f5a2baaa146e332c4151788" + integrity sha512-VLYKXQmtmuEz6IxJsrZwzG9NvtkQsWNnWMsKxqWNu3+CnfzJQhp0WDDKWLVV9hLKr0l3SLLFRqcYHjhtyuDVxg== + dependencies: + "@sinclair/typebox" "^0.25.16" + "@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" @@ -3093,32 +3107,32 @@ resolved "https://registry.yarnpkg.com/@kwsites/promise-deferred/-/promise-deferred-1.1.1.tgz#8ace5259254426ccef57f3175bc64ed7095ed919" integrity sha512-GaHYm+c0O9MjZRu0ongGBRbinu8gVAMd2UZjji6jVmqKtZluZnptXGWhz1E8j8D2HJ3f/yMxKAUC0b+57wncIw== -"@lerna/child-process@6.5.0-alpha.2": - version "6.5.0-alpha.2" - resolved "https://registry.npmjs.org/@lerna/child-process/-/child-process-6.5.0-alpha.2.tgz#5ef9cc6c8e521fbb9f18b3031ca3e789345e7b56" - integrity sha512-lzRKVaI5J5p7CihAVSHaEN7uAMZScNNRwy2hoz6w+lM5vKiMmrc93yl3zjUo0BeyqB1i1b3ntUMMIX6UWGjvuA== +"@lerna/child-process@7.1.1": + version "7.1.1" + resolved "https://registry.yarnpkg.com/@lerna/child-process/-/child-process-7.1.1.tgz#60eddd6dc4b6ba0fd51851c78b6dbdc4e1614220" + integrity sha512-mR8PaTkckYPLmEBG2VsVsJq2UuzEvjXevOB1rKLKUZ/dPCGcottVhbiEzTxickc+s7Y/1dTTLn/1BKj3B1a5BA== dependencies: chalk "^4.1.0" execa "^5.0.0" strong-log-transformer "^2.1.0" -"@lerna/create@6.5.0-alpha.2": - version "6.5.0-alpha.2" - resolved "https://registry.npmjs.org/@lerna/create/-/create-6.5.0-alpha.2.tgz#8b4f681e049356a924056358ebd0b485519061d8" - integrity sha512-LSUv6/2yvU13odiZYpLsy3FQ39oyCinf+puFZ6waYMbVb0LBikbtNZ2YP4rAwyN91PRp5vTwtwqbls47NdHOvg== +"@lerna/create@7.1.1": + version "7.1.1" + resolved "https://registry.yarnpkg.com/@lerna/create/-/create-7.1.1.tgz#2af94afb01971c1b594c06347b6998607aefe5c4" + integrity sha512-1PY2OgwGxp7b91JzLKEhONVl69mCt1IyYEc6pzKy3Sv+UOdeK2QFq1SX/85hNOR3iitiyZ75bNWUTcBly1ZlZg== dependencies: - "@lerna/child-process" "6.5.0-alpha.2" - dedent "^0.7.0" - fs-extra "^9.1.0" - init-package-json "^3.0.2" + "@lerna/child-process" "7.1.1" + dedent "0.7.0" + fs-extra "^11.1.1" + init-package-json "5.0.0" npm-package-arg "8.1.1" p-reduce "^2.1.0" - pacote "^13.6.1" - pify "^5.0.0" + pacote "^15.2.0" + pify "5.0.0" semver "^7.3.4" slash "^3.0.0" validate-npm-package-license "^3.0.4" - validate-npm-package-name "^4.0.0" + validate-npm-package-name "5.0.0" yargs-parser "20.2.4" "@lint-todo/utils@^13.0.3": @@ -3201,46 +3215,6 @@ "@nodelib/fs.scandir" "2.1.4" fastq "^1.6.0" -"@npmcli/arborist@5.3.0": - version "5.3.0" - resolved "https://registry.yarnpkg.com/@npmcli/arborist/-/arborist-5.3.0.tgz#321d9424677bfc08569e98a5ac445ee781f32053" - integrity sha512-+rZ9zgL1lnbl8Xbb1NQdMjveOMwj4lIYfcDtyJHHi5x4X8jtR6m8SXooJMZy5vmFVZ8w7A2Bnd/oX9eTuU8w5A== - dependencies: - "@isaacs/string-locale-compare" "^1.1.0" - "@npmcli/installed-package-contents" "^1.0.7" - "@npmcli/map-workspaces" "^2.0.3" - "@npmcli/metavuln-calculator" "^3.0.1" - "@npmcli/move-file" "^2.0.0" - "@npmcli/name-from-folder" "^1.0.1" - "@npmcli/node-gyp" "^2.0.0" - "@npmcli/package-json" "^2.0.0" - "@npmcli/run-script" "^4.1.3" - bin-links "^3.0.0" - cacache "^16.0.6" - common-ancestor-path "^1.0.1" - json-parse-even-better-errors "^2.3.1" - json-stringify-nice "^1.1.4" - mkdirp "^1.0.4" - mkdirp-infer-owner "^2.0.0" - nopt "^5.0.0" - npm-install-checks "^5.0.0" - npm-package-arg "^9.0.0" - npm-pick-manifest "^7.0.0" - npm-registry-fetch "^13.0.0" - npmlog "^6.0.2" - pacote "^13.6.1" - parse-conflict-json "^2.0.1" - proc-log "^2.0.0" - promise-all-reject-late "^1.0.0" - promise-call-limit "^1.0.1" - read-package-json-fast "^2.0.2" - readdir-scoped-modules "^1.1.0" - rimraf "^3.0.2" - semver "^7.3.7" - ssri "^9.0.0" - treeverse "^2.0.0" - walk-up-path "^1.0.0" - "@npmcli/fs@^1.0.0": version "1.1.1" resolved "https://registry.yarnpkg.com/@npmcli/fs/-/fs-1.1.1.tgz#72f719fe935e687c56a4faecf3c03d06ba593257" @@ -3257,6 +3231,13 @@ "@gar/promisify" "^1.1.3" semver "^7.3.5" +"@npmcli/fs@^3.1.0": + version "3.1.0" + resolved "https://registry.yarnpkg.com/@npmcli/fs/-/fs-3.1.0.tgz#233d43a25a91d68c3a863ba0da6a3f00924a173e" + integrity sha512-7kZUAaLscfgbwBQRbvdMYaZOWyMEcPTH/tJjnyAWJ/dvvs9Ef+CERx/qJb9GExJpl1qipaDGn7KqHnFGGixd0w== + dependencies: + semver "^7.3.5" + "@npmcli/git@^2.1.0": version "2.1.0" resolved "https://registry.yarnpkg.com/@npmcli/git/-/git-2.1.0.tgz#2fbd77e147530247d37f325930d457b3ebe894f6" @@ -3271,22 +3252,21 @@ semver "^7.3.5" which "^2.0.2" -"@npmcli/git@^3.0.0": - version "3.0.2" - resolved "https://registry.yarnpkg.com/@npmcli/git/-/git-3.0.2.tgz#5c5de6b4d70474cf2d09af149ce42e4e1dacb931" - integrity sha512-CAcd08y3DWBJqJDpfuVL0uijlq5oaXaOJEKHKc4wqrjd00gkvTZB+nFuLn+doOOKddaQS9JfqtNoFCO2LCvA3w== +"@npmcli/git@^4.0.0": + version "4.1.0" + resolved "https://registry.yarnpkg.com/@npmcli/git/-/git-4.1.0.tgz#ab0ad3fd82bc4d8c1351b6c62f0fa56e8fe6afa6" + integrity sha512-9hwoB3gStVfa0N31ymBmrX+GuDGdVA/QWShZVqE0HK2Af+7QGGrCTbZia/SW0ImUTjTne7SP91qxDmtXvDHRPQ== dependencies: - "@npmcli/promise-spawn" "^3.0.0" + "@npmcli/promise-spawn" "^6.0.0" lru-cache "^7.4.4" - mkdirp "^1.0.4" - npm-pick-manifest "^7.0.0" - proc-log "^2.0.0" + npm-pick-manifest "^8.0.0" + proc-log "^3.0.0" promise-inflight "^1.0.1" promise-retry "^2.0.1" semver "^7.3.5" - which "^2.0.2" + which "^3.0.0" -"@npmcli/installed-package-contents@^1.0.6", "@npmcli/installed-package-contents@^1.0.7": +"@npmcli/installed-package-contents@^1.0.6": version "1.0.7" resolved "https://registry.yarnpkg.com/@npmcli/installed-package-contents/-/installed-package-contents-1.0.7.tgz#ab7408c6147911b970a8abe261ce512232a3f4fa" integrity sha512-9rufe0wnJusCQoLpV9ZPKIVP55itrM5BxOXs10DmdbRfgWtHy1LDyskbwRnBghuB0PrF7pNPOqREVtpz4HqzKw== @@ -3294,25 +3274,13 @@ npm-bundled "^1.1.1" npm-normalize-package-bin "^1.0.1" -"@npmcli/map-workspaces@^2.0.3": - version "2.0.4" - resolved "https://registry.yarnpkg.com/@npmcli/map-workspaces/-/map-workspaces-2.0.4.tgz#9e5e8ab655215a262aefabf139782b894e0504fc" - integrity sha512-bMo0aAfwhVwqoVM5UzX1DJnlvVvzDCHae821jv48L1EsrYwfOZChlqWYXEtto/+BkBXetPbEWgau++/brh4oVg== - dependencies: - "@npmcli/name-from-folder" "^1.0.1" - glob "^8.0.1" - minimatch "^5.0.1" - read-package-json-fast "^2.0.3" - -"@npmcli/metavuln-calculator@^3.0.1": - version "3.1.1" - resolved "https://registry.yarnpkg.com/@npmcli/metavuln-calculator/-/metavuln-calculator-3.1.1.tgz#9359bd72b400f8353f6a28a25c8457b562602622" - integrity sha512-n69ygIaqAedecLeVH3KnO39M6ZHiJ2dEv5A7DGvcqCB8q17BGUgW8QaanIkbWUo2aYGZqJaOORTLAlIvKjNDKA== +"@npmcli/installed-package-contents@^2.0.1": + version "2.0.2" + resolved "https://registry.yarnpkg.com/@npmcli/installed-package-contents/-/installed-package-contents-2.0.2.tgz#bfd817eccd9e8df200919e73f57f9e3d9e4f9e33" + integrity sha512-xACzLPhnfD51GKvTOOuNX2/V4G4mz9/1I2MfDoye9kBM3RYe5g2YbscsaGoTlaWqkxeiapBWyseULVKpSVHtKQ== dependencies: - cacache "^16.0.0" - json-parse-even-better-errors "^2.3.1" - pacote "^13.0.3" - semver "^7.3.5" + npm-bundled "^3.0.0" + npm-normalize-package-bin "^3.0.0" "@npmcli/move-file@^1.0.1": version "1.1.2" @@ -3330,27 +3298,15 @@ mkdirp "^1.0.4" rimraf "^3.0.2" -"@npmcli/name-from-folder@^1.0.1": - version "1.0.1" - resolved "https://registry.yarnpkg.com/@npmcli/name-from-folder/-/name-from-folder-1.0.1.tgz#77ecd0a4fcb772ba6fe927e2e2e155fbec2e6b1a" - integrity sha512-qq3oEfcLFwNfEYOQ8HLimRGKlD8WSeGEdtUa7hmzpR8Sa7haL1KVQrvgO6wqMjhWFFVjgtrh1gIxDz+P8sjUaA== - "@npmcli/node-gyp@^1.0.2": version "1.0.3" resolved "https://registry.yarnpkg.com/@npmcli/node-gyp/-/node-gyp-1.0.3.tgz#a912e637418ffc5f2db375e93b85837691a43a33" integrity sha512-fnkhw+fmX65kiLqk6E3BFLXNC26rUhK90zVwe2yncPliVT/Qos3xjhTLE59Df8KnPlcwIERXKVlU1bXoUQ+liA== -"@npmcli/node-gyp@^2.0.0": - version "2.0.0" - resolved "https://registry.yarnpkg.com/@npmcli/node-gyp/-/node-gyp-2.0.0.tgz#8c20e53e34e9078d18815c1d2dda6f2420d75e35" - integrity sha512-doNI35wIe3bBaEgrlPfdJPaCpUR89pJWep4Hq3aRdh6gKazIVWfs0jHttvSSoq47ZXgC7h73kDsUl8AoIQUB+A== - -"@npmcli/package-json@^2.0.0": - version "2.0.0" - resolved "https://registry.yarnpkg.com/@npmcli/package-json/-/package-json-2.0.0.tgz#3bbcf4677e21055adbe673d9f08c9f9cde942e4a" - integrity sha512-42jnZ6yl16GzjWSH7vtrmWyJDGVa/LXPdpN2rcUWolFjc9ON2N3uz0qdBbQACfmhuJZ2lbKYtmK5qx68ZPLHMA== - dependencies: - json-parse-even-better-errors "^2.3.1" +"@npmcli/node-gyp@^3.0.0": + version "3.0.0" + resolved "https://registry.yarnpkg.com/@npmcli/node-gyp/-/node-gyp-3.0.0.tgz#101b2d0490ef1aa20ed460e4c0813f0db560545a" + integrity sha512-gp8pRXC2oOxu0DUE1/M3bYtb1b3/DbJ5aM113+XJBgfXdussRAsX0YOrOhdd8WvnAR6auDBvJomGAkLKA5ydxA== "@npmcli/promise-spawn@^1.2.0", "@npmcli/promise-spawn@^1.3.2": version "1.3.2" @@ -3359,23 +3315,23 @@ dependencies: infer-owner "^1.0.4" -"@npmcli/promise-spawn@^3.0.0": - version "3.0.0" - resolved "https://registry.yarnpkg.com/@npmcli/promise-spawn/-/promise-spawn-3.0.0.tgz#53283b5f18f855c6925f23c24e67c911501ef573" - integrity sha512-s9SgS+p3a9Eohe68cSI3fi+hpcZUmXq5P7w0kMlAsWVtR7XbK3ptkZqKT2cK1zLDObJ3sR+8P59sJE0w/KTL1g== +"@npmcli/promise-spawn@^6.0.0", "@npmcli/promise-spawn@^6.0.1": + version "6.0.2" + resolved "https://registry.yarnpkg.com/@npmcli/promise-spawn/-/promise-spawn-6.0.2.tgz#c8bc4fa2bd0f01cb979d8798ba038f314cfa70f2" + integrity sha512-gGq0NJkIGSwdbUt4yhdF8ZrmkGKVz9vAdVzpOfnom+V8PLSmSOVhZwbNvZZS1EYcJN5hzzKBxmmVVAInM6HQLg== dependencies: - infer-owner "^1.0.4" + which "^3.0.0" -"@npmcli/run-script@4.1.7": - version "4.1.7" - resolved "https://registry.npmjs.org/@npmcli/run-script/-/run-script-4.1.7.tgz#b1a2f57568eb738e45e9ea3123fb054b400a86f7" - integrity sha512-WXr/MyM4tpKA4BotB81NccGAv8B48lNH0gRoILucbcAhTQXLCoi6HflMV3KdXubIqvP9SuLsFn68Z7r4jl+ppw== +"@npmcli/run-script@6.0.2", "@npmcli/run-script@^6.0.0": + version "6.0.2" + resolved "https://registry.yarnpkg.com/@npmcli/run-script/-/run-script-6.0.2.tgz#a25452d45ee7f7fb8c16dfaf9624423c0c0eb885" + integrity sha512-NCcr1uQo1k5U+SYlnIrbAh3cxy+OQT1VtqiAbxdymSlptbzBb62AjH2xXgjNCoP073hoa1CfCAcwoZ8k96C4nA== dependencies: - "@npmcli/node-gyp" "^2.0.0" - "@npmcli/promise-spawn" "^3.0.0" + "@npmcli/node-gyp" "^3.0.0" + "@npmcli/promise-spawn" "^6.0.0" node-gyp "^9.0.0" - read-package-json-fast "^2.0.3" - which "^2.0.2" + read-package-json-fast "^3.0.0" + which "^3.0.0" "@npmcli/run-script@^2.0.0": version "2.0.0" @@ -3387,41 +3343,81 @@ node-gyp "^8.2.0" read-package-json-fast "^2.0.1" -"@npmcli/run-script@^4.1.0", "@npmcli/run-script@^4.1.3": - version "4.2.1" - resolved "https://registry.yarnpkg.com/@npmcli/run-script/-/run-script-4.2.1.tgz#c07c5c71bc1c70a5f2a06b0d4da976641609b946" - integrity sha512-7dqywvVudPSrRCW5nTHpHgeWnbBtz8cFkOuKrecm6ih+oO9ciydhWt6OF7HlqupRRmB8Q/gECVdB9LMfToJbRg== +"@nrwl/devkit@16.4.1": + version "16.4.1" + resolved "https://registry.yarnpkg.com/@nrwl/devkit/-/devkit-16.4.1.tgz#3605e7f39cccdc47502838593579a1af6f22ae9c" + integrity sha512-kio+x1NonteK9Vxrgeai56+cDFkiWUl42YzLamNXORvICgVgGtcR7afdi9l7j9q2YPUuvtBos6T9YddS6YCb2g== dependencies: - "@npmcli/node-gyp" "^2.0.0" - "@npmcli/promise-spawn" "^3.0.0" - node-gyp "^9.0.0" - read-package-json-fast "^2.0.3" - which "^2.0.2" + "@nx/devkit" "16.4.1" -"@nrwl/cli@15.6.3": - version "15.6.3" - resolved "https://registry.npmjs.org/@nrwl/cli/-/cli-15.6.3.tgz#999531d6efb30afc39373bdcbd7e78254a3a3fd3" - integrity sha512-K4E0spofThZXMnhA6R8hkUTdfqmwSnUE2+DlD5Y3jqsvKTAgwF5U41IFkEouFZCf+dWjy0RA20bWoX48EVFtmQ== +"@nrwl/tao@16.4.1": + version "16.4.1" + resolved "https://registry.yarnpkg.com/@nrwl/tao/-/tao-16.4.1.tgz#4d060a73c2dfdbf00b66c922a2f1f7c30fffa048" + integrity sha512-aJqYxgz+PzyeuFrKj7jei8Xwq05JYLmq5o+4/Und+lkMZboqvVWz1ezwiMj9pzGoXz4td8b3sN1B+nwmORm3ZQ== dependencies: - nx "15.6.3" + nx "16.4.1" -"@nrwl/devkit@>=15.5.2 < 16": - version "15.6.3" - resolved "https://registry.npmjs.org/@nrwl/devkit/-/devkit-15.6.3.tgz#e4e96c53ba3304786a49034286c8511534b2b194" - integrity sha512-/JDvdzNxUM+C1PCZPCrvmFx+OfywqZdOq1GS9QR8C0VctTLG4D/SGSFD88O1SAdcbH/f1mMiBGfEYZYd23fghQ== +"@nx/devkit@16.4.1", "@nx/devkit@>=16.1.3 < 17": + version "16.4.1" + resolved "https://registry.yarnpkg.com/@nx/devkit/-/devkit-16.4.1.tgz#43b126704d5611b8f19418ade4a60a0fd1e695ff" + integrity sha512-N2oDaQQV9r6mnoPsAqKs8H+tsLdNptQms5AxgnhdEMYWH4ppmy6Zutg4h1qZWsbdqSyiLVuOqlPrPlzRM4EA4g== dependencies: - "@phenomnomnominal/tsquery" "4.1.1" + "@nrwl/devkit" "16.4.1" ejs "^3.1.7" ignore "^5.0.4" - semver "7.3.4" + semver "7.5.3" + tmp "~0.2.1" tslib "^2.3.0" -"@nrwl/tao@15.6.3": - version "15.6.3" - resolved "https://registry.npmjs.org/@nrwl/tao/-/tao-15.6.3.tgz#b24e11345375dea96bc386c60b9b1102a7584932" - integrity sha512-bDZbPIbU5Mf2BvX0q8GjPxrm1WkYyfW+gp7mLuuJth2sEpZiCr47mSwuGko/y4CKXvIX46VQcAS0pKQMKugXsg== - dependencies: - nx "15.6.3" +"@nx/nx-darwin-arm64@16.4.1": + version "16.4.1" + resolved "https://registry.yarnpkg.com/@nx/nx-darwin-arm64/-/nx-darwin-arm64-16.4.1.tgz#c7069c28972d19c0fa982819b9aa3e3af3637798" + integrity sha512-DkY51qgBlqgHwAVrK4k58tNZ1Uuqi4czA+aLs+J2OvC8W/4uRSajGPL4LWgdPWYe1zKxJvhFIFswchn8uQuaBw== + +"@nx/nx-darwin-x64@16.4.1": + version "16.4.1" + resolved "https://registry.yarnpkg.com/@nx/nx-darwin-x64/-/nx-darwin-x64-16.4.1.tgz#9d641ce027b2fa46c210229e7b133d5611318670" + integrity sha512-jMJz6wsCOl7n3x4lmiS7BbQZdGmKKsN1IUaLcJfxZjFN3YS8euO2bwO74trFkfNOdYG8KjFuw/+A62USYj4e+g== + +"@nx/nx-freebsd-x64@16.4.1": + version "16.4.1" + resolved "https://registry.yarnpkg.com/@nx/nx-freebsd-x64/-/nx-freebsd-x64-16.4.1.tgz#ee3806e1313327f58b4df624e703ba8fefb17711" + integrity sha512-8Ql2/g+WZOKnPC7x4IeW/vzIRi9K9BE6LvFGGMsTvqKJHurboGlPjBAAqo/wmgM+JPNivtzX+IsQQkcGQrFfLw== + +"@nx/nx-linux-arm-gnueabihf@16.4.1": + version "16.4.1" + resolved "https://registry.yarnpkg.com/@nx/nx-linux-arm-gnueabihf/-/nx-linux-arm-gnueabihf-16.4.1.tgz#8082e04b60350f9ca186911ca0ff431b32d141d2" + integrity sha512-EyK/q86FXO78oGcubBXlqdzCsrMBx+CgEyndS2IlvpGFXN3v2s3jE8v/RXWbPskJ6zJZytRvyMjTjxAnzjxb+A== + +"@nx/nx-linux-arm64-gnu@16.4.1": + version "16.4.1" + resolved "https://registry.yarnpkg.com/@nx/nx-linux-arm64-gnu/-/nx-linux-arm64-gnu-16.4.1.tgz#4931d04b842c066e8082cb39753d8dc01bb119d8" + integrity sha512-2YpmfnHahuFXD7DN4j/O+8hvV1P1oa+QxO+hxBfPdqU45YmOPSwbVEcTsjYmc++iG9xURpGaSu3hGmk5JR4OoQ== + +"@nx/nx-linux-arm64-musl@16.4.1": + version "16.4.1" + resolved "https://registry.yarnpkg.com/@nx/nx-linux-arm64-musl/-/nx-linux-arm64-musl-16.4.1.tgz#06cf22f9cb18e9ec1799b249a2b3af5734b8b767" + integrity sha512-+xDP3/veLSPaLFrp1lItZTK2rqpMEftOC+2TsRPQ1BwivGxBegerQYWgZxe6nfuBGrRD2xj8+aY4on5UfmYBJw== + +"@nx/nx-linux-x64-gnu@16.4.1": + version "16.4.1" + resolved "https://registry.yarnpkg.com/@nx/nx-linux-x64-gnu/-/nx-linux-x64-gnu-16.4.1.tgz#be5678fdfbfdb23a86a53f8fd18710a9aeb5b6e3" + integrity sha512-psbB+hTXffeeATO692To4zxz08XNUHNiHefZYVwT6hUWw+EsUAadnd3VimP9xoSzHyzvUk6raYPT783MySTzGg== + +"@nx/nx-linux-x64-musl@16.4.1": + version "16.4.1" + resolved "https://registry.yarnpkg.com/@nx/nx-linux-x64-musl/-/nx-linux-x64-musl-16.4.1.tgz#8ba537f29edfe9b548e40301fdc286c9aff145f2" + integrity sha512-pztJGR64NRygp675p/tkQIF2clIc9mxRVpVAaeIc1DoQTEpyeagqi6bTPwTTUdhDhTleqV6r3wOTL/3ImUrpng== + +"@nx/nx-win32-arm64-msvc@16.4.1": + version "16.4.1" + resolved "https://registry.yarnpkg.com/@nx/nx-win32-arm64-msvc/-/nx-win32-arm64-msvc-16.4.1.tgz#a601d766db76175e69d002d6dfcc859a9f5d777d" + integrity sha512-tmfVFZ5lKahYg16mUs7gwEJtlBkL9cEoc1Pf7cuFXHT+T7z5WhXoZ0q7VTyArf3gisK4fTmTAEEuUEK2MbQ2xA== + +"@nx/nx-win32-x64-msvc@16.4.1": + version "16.4.1" + resolved "https://registry.yarnpkg.com/@nx/nx-win32-x64-msvc/-/nx-win32-x64-msvc-16.4.1.tgz#d48b2b6f9762dcda5ae51b4f4ecd354479f42605" + integrity sha512-MAy719VC8hCPkYJ6j5Gl+s4pevWL0dxbzcXtQDstC0Y7XWPFmHS+CDgK8zHWfaN8mK6Sebv+nTQ+e/ptEu1+TA== "@octokit/auth-token@^3.0.0": version "3.0.2" @@ -3430,7 +3426,7 @@ dependencies: "@octokit/types" "^8.0.0" -"@octokit/core@^4.0.0", "@octokit/core@^4.1.0": +"@octokit/core@^4.1.0": version "4.2.0" resolved "https://registry.npmjs.org/@octokit/core/-/core-4.2.0.tgz#8c253ba9605aca605bc46187c34fcccae6a96648" integrity sha512-AgvDRUg3COpR82P7PBdGZF/NNqGmtMq2NiPqeSsDIeCfYFOZ9gddqWNQHnFdEUf+YwOj4aZYmJnlPp7OXmDIDg== @@ -3443,6 +3439,19 @@ before-after-hook "^2.2.0" universal-user-agent "^6.0.0" +"@octokit/core@^4.2.1": + version "4.2.4" + resolved "https://registry.yarnpkg.com/@octokit/core/-/core-4.2.4.tgz#d8769ec2b43ff37cc3ea89ec4681a20ba58ef907" + integrity sha512-rYKilwgzQ7/imScn3M9/pFfUf4I1AZEH3KhyJmtPdE2zfaXAn2mFfUy4FbKewzc2We5y/LlKLj36fWJLKC2SIQ== + dependencies: + "@octokit/auth-token" "^3.0.0" + "@octokit/graphql" "^5.0.0" + "@octokit/request" "^6.0.0" + "@octokit/request-error" "^3.0.0" + "@octokit/types" "^9.0.0" + before-after-hook "^2.2.0" + universal-user-agent "^6.0.0" + "@octokit/endpoint@^7.0.0": version "7.0.3" resolved "https://registry.yarnpkg.com/@octokit/endpoint/-/endpoint-7.0.3.tgz#0b96035673a9e3bedf8bab8f7335de424a2147ed" @@ -3461,11 +3470,6 @@ "@octokit/types" "^8.0.0" universal-user-agent "^6.0.0" -"@octokit/openapi-types@^12.11.0": - version "12.11.0" - resolved "https://registry.npmjs.org/@octokit/openapi-types/-/openapi-types-12.11.0.tgz#da5638d64f2b919bca89ce6602d059f1b52d3ef0" - integrity sha512-VsXyi8peyRq9PqIz/tpqiL2w3w80OgVMwBHltTml3LmVvXiphgeqmY9mvBw9Wu7e0QWk/fqD37ux8yP5uVekyQ== - "@octokit/openapi-types@^14.0.0": version "14.0.0" resolved "https://registry.yarnpkg.com/@octokit/openapi-types/-/openapi-types-14.0.0.tgz#949c5019028c93f189abbc2fb42f333290f7134a" @@ -3476,18 +3480,16 @@ resolved "https://registry.npmjs.org/@octokit/openapi-types/-/openapi-types-16.0.0.tgz#d92838a6cd9fb4639ca875ddb3437f1045cc625e" integrity sha512-JbFWOqTJVLHZSUUoF4FzAZKYtqdxWu9Z5m2QQnOyEa04fOFljvyh7D3GYKbfuaSWisqehImiVIMG4eyJeP5VEA== +"@octokit/openapi-types@^18.0.0": + version "18.0.0" + resolved "https://registry.yarnpkg.com/@octokit/openapi-types/-/openapi-types-18.0.0.tgz#f43d765b3c7533fd6fb88f3f25df079c24fccf69" + integrity sha512-V8GImKs3TeQRxRtXFpG2wl19V7444NIOTDF24AWuIbmNaNYOQMWRbjcGDXV5B+0n887fgDcuMNOmlul+k+oJtw== + "@octokit/plugin-enterprise-rest@6.0.1": version "6.0.1" resolved "https://registry.npmjs.org/@octokit/plugin-enterprise-rest/-/plugin-enterprise-rest-6.0.1.tgz#e07896739618dab8da7d4077c658003775f95437" integrity sha512-93uGjlhUD+iNg1iWhUENAtJata6w5nE+V4urXOAlIXdco6xNZtUSfYY8dzp3Udy74aqO/B5UZL80x/YMa5PKRw== -"@octokit/plugin-paginate-rest@^3.0.0": - version "3.1.0" - resolved "https://registry.npmjs.org/@octokit/plugin-paginate-rest/-/plugin-paginate-rest-3.1.0.tgz#86f8be759ce2d6d7c879a31490fd2f7410b731f0" - integrity sha512-+cfc40pMzWcLkoDcLb1KXqjX0jTGYXjKuQdFQDc6UAknISJHnZTiBqld6HDwRJvD4DsouDKrWXNbNV0lE/3AXA== - dependencies: - "@octokit/types" "^6.41.0" - "@octokit/plugin-paginate-rest@^6.0.0": version "6.0.0" resolved "https://registry.yarnpkg.com/@octokit/plugin-paginate-rest/-/plugin-paginate-rest-6.0.0.tgz#f34b5a7d9416019126042cd7d7b811e006c0d561" @@ -3495,19 +3497,19 @@ dependencies: "@octokit/types" "^9.0.0" +"@octokit/plugin-paginate-rest@^6.1.2": + version "6.1.2" + resolved "https://registry.yarnpkg.com/@octokit/plugin-paginate-rest/-/plugin-paginate-rest-6.1.2.tgz#f86456a7a1fe9e58fec6385a85cf1b34072341f8" + integrity sha512-qhrmtQeHU/IivxucOV1bbI/xZyC/iOBhclokv7Sut5vnejAIAEXVcGQeRpQlU39E0WwK9lNvJHphHri/DB6lbQ== + dependencies: + "@octokit/tsconfig" "^1.0.2" + "@octokit/types" "^9.2.3" + "@octokit/plugin-request-log@^1.0.4": version "1.0.4" resolved "https://registry.yarnpkg.com/@octokit/plugin-request-log/-/plugin-request-log-1.0.4.tgz#5e50ed7083a613816b1e4a28aeec5fb7f1462e85" integrity sha512-mLUsMkgP7K/cnFEw07kWqXGF5LKrOkD+lhCrKvPHXWDywAwuDUeDwWBpc69XK3pNX0uKiVt8g5z96PJ6z9xCFA== -"@octokit/plugin-rest-endpoint-methods@^6.0.0": - version "6.8.1" - resolved "https://registry.npmjs.org/@octokit/plugin-rest-endpoint-methods/-/plugin-rest-endpoint-methods-6.8.1.tgz#97391fda88949eb15f68dc291957ccbe1d3e8ad1" - integrity sha512-QrlaTm8Lyc/TbU7BL/8bO49vp+RZ6W3McxxmmQTgYxf2sWkO8ZKuj4dLhPNJD6VCUW1hetCmeIM0m6FTVpDiEg== - dependencies: - "@octokit/types" "^8.1.1" - deprecation "^2.3.1" - "@octokit/plugin-rest-endpoint-methods@^7.0.0": version "7.0.1" resolved "https://registry.yarnpkg.com/@octokit/plugin-rest-endpoint-methods/-/plugin-rest-endpoint-methods-7.0.1.tgz#f7ebe18144fd89460f98f35a587b056646e84502" @@ -3516,6 +3518,13 @@ "@octokit/types" "^9.0.0" deprecation "^2.3.1" +"@octokit/plugin-rest-endpoint-methods@^7.1.2": + version "7.2.3" + resolved "https://registry.yarnpkg.com/@octokit/plugin-rest-endpoint-methods/-/plugin-rest-endpoint-methods-7.2.3.tgz#37a84b171a6cb6658816c82c4082ac3512021797" + integrity sha512-I5Gml6kTAkzVlN7KCtjOM+Ruwe/rQppp0QU372K1GP7kNOYEKe8Xn5BW4sE62JAHdwpq95OQK/qGNyKQMUzVgA== + dependencies: + "@octokit/types" "^10.0.0" + "@octokit/request-error@^3.0.0": version "3.0.2" resolved "https://registry.yarnpkg.com/@octokit/request-error/-/request-error-3.0.2.tgz#f74c0f163d19463b87528efe877216c41d6deb0a" @@ -3537,15 +3546,15 @@ node-fetch "^2.6.7" universal-user-agent "^6.0.0" -"@octokit/rest@19.0.3": - version "19.0.3" - resolved "https://registry.npmjs.org/@octokit/rest/-/rest-19.0.3.tgz#b9a4e8dc8d53e030d611c053153ee6045f080f02" - integrity sha512-5arkTsnnRT7/sbI4fqgSJ35KiFaN7zQm0uQiQtivNQLI8RQx8EHwJCajcTUwmaCMNDg7tdCvqAnc7uvHHPxrtQ== +"@octokit/rest@19.0.11": + version "19.0.11" + resolved "https://registry.yarnpkg.com/@octokit/rest/-/rest-19.0.11.tgz#2ae01634fed4bd1fca5b642767205ed3fd36177c" + integrity sha512-m2a9VhaP5/tUw8FwfnW2ICXlXpLPIqxtg3XcAiGMLj/Xhw3RSBfZ8le/466ktO1Gcjr8oXudGnHhxV1TXJgFxw== dependencies: - "@octokit/core" "^4.0.0" - "@octokit/plugin-paginate-rest" "^3.0.0" + "@octokit/core" "^4.2.1" + "@octokit/plugin-paginate-rest" "^6.1.2" "@octokit/plugin-request-log" "^1.0.4" - "@octokit/plugin-rest-endpoint-methods" "^6.0.0" + "@octokit/plugin-rest-endpoint-methods" "^7.1.2" "@octokit/rest@^19.0.5": version "19.0.7" @@ -3557,12 +3566,17 @@ "@octokit/plugin-request-log" "^1.0.4" "@octokit/plugin-rest-endpoint-methods" "^7.0.0" -"@octokit/types@^6.41.0": - version "6.41.0" - resolved "https://registry.npmjs.org/@octokit/types/-/types-6.41.0.tgz#e58ef78d78596d2fb7df9c6259802464b5f84a04" - integrity sha512-eJ2jbzjdijiL3B4PrSQaSjuF2sPEQPVCPzBvTHJD9Nz+9dw2SGH4K4xeQJ77YfTq5bRQ+bD8wT11JbeDPmxmGg== +"@octokit/tsconfig@^1.0.2": + version "1.0.2" + resolved "https://registry.yarnpkg.com/@octokit/tsconfig/-/tsconfig-1.0.2.tgz#59b024d6f3c0ed82f00d08ead5b3750469125af7" + integrity sha512-I0vDR0rdtP8p2lGMzvsJzbhdOWy405HcGovrspJ8RRibHnyRgggUSNO5AIox5LmqiwmatHKYsvj6VGFHkqS7lA== + +"@octokit/types@^10.0.0": + version "10.0.0" + resolved "https://registry.yarnpkg.com/@octokit/types/-/types-10.0.0.tgz#7ee19c464ea4ada306c43f1a45d444000f419a4a" + integrity sha512-Vm8IddVmhCgU1fxC1eyinpwqzXPEYu0NrYzD3YZjlGjyftdLBTeqNblRC0jmJmgxbJIsQlyogVeGnrNaaMVzIg== dependencies: - "@octokit/openapi-types" "^12.11.0" + "@octokit/openapi-types" "^18.0.0" "@octokit/types@^8.0.0": version "8.0.0" @@ -3571,13 +3585,6 @@ dependencies: "@octokit/openapi-types" "^14.0.0" -"@octokit/types@^8.1.1": - version "8.2.1" - resolved "https://registry.npmjs.org/@octokit/types/-/types-8.2.1.tgz#a6de091ae68b5541f8d4fcf9a12e32836d4648aa" - integrity sha512-8oWMUji8be66q2B9PmEIUyQm00VPDPun07umUWSaCwxmeaquFBro4Hcc3ruVoDo3zkQyZBlRvhIMEYS3pBhanw== - dependencies: - "@octokit/openapi-types" "^14.0.0" - "@octokit/types@^9.0.0": version "9.0.0" resolved "https://registry.npmjs.org/@octokit/types/-/types-9.0.0.tgz#6050db04ddf4188ec92d60e4da1a2ce0633ff635" @@ -3585,6 +3592,13 @@ dependencies: "@octokit/openapi-types" "^16.0.0" +"@octokit/types@^9.2.3": + version "9.3.2" + resolved "https://registry.yarnpkg.com/@octokit/types/-/types-9.3.2.tgz#3f5f89903b69f6a2d196d78ec35f888c0013cac5" + integrity sha512-D4iHGTdAnEEVsB8fl95m1hiz7D5YiRdQ9b/OEb3BYRVwbLsGHcRVPz+u+BgRLNk0Q0/4iZCBqDN96j2XNxfXrA== + dependencies: + "@octokit/openapi-types" "^18.0.0" + "@opentelemetry/api@0.14.0": version "0.14.0" resolved "https://registry.yarnpkg.com/@opentelemetry/api/-/api-0.14.0.tgz#4e17d8d2f1da72b19374efa7b6526aa001267cae" @@ -3715,12 +3729,10 @@ node-addon-api "^3.2.1" node-gyp-build "^4.3.0" -"@phenomnomnominal/tsquery@4.1.1": - version "4.1.1" - resolved "https://registry.yarnpkg.com/@phenomnomnominal/tsquery/-/tsquery-4.1.1.tgz#42971b83590e9d853d024ddb04a18085a36518df" - integrity sha512-jjMmK1tnZbm1Jq5a7fBliM4gQwjxMU7TFoRNwIyzwlO+eHPRCFv/Nv+H/Gi1jc3WR7QURG8D5d0Tn12YGrUqBQ== - dependencies: - esquery "^1.0.1" +"@pkgjs/parseargs@^0.11.0": + version "0.11.0" + resolved "https://registry.yarnpkg.com/@pkgjs/parseargs/-/parseargs-0.11.0.tgz#a77ea742fab25775145434eb1d2328cf5013ac33" + integrity sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg== "@playwright/test@^1.31.1": version "1.31.1" @@ -4154,11 +4166,30 @@ "@sentry/cli" "^1.74.6" webpack-sources "^2.0.0 || ^3.0.0" +"@sigstore/protobuf-specs@^0.1.0": + version "0.1.0" + resolved "https://registry.yarnpkg.com/@sigstore/protobuf-specs/-/protobuf-specs-0.1.0.tgz#957cb64ea2f5ce527cc9cf02a096baeb0d2b99b4" + integrity sha512-a31EnjuIDSX8IXBUib3cYLDRlPMU36AWX4xS8ysLaNu4ZzUesDiPt83pgrW2X1YLMe5L2HbDyaKK5BrL4cNKaQ== + +"@sigstore/tuf@^1.0.0": + version "1.0.0" + resolved "https://registry.yarnpkg.com/@sigstore/tuf/-/tuf-1.0.0.tgz#13b69323e7bf8de458cd6c952c57acd1169772a5" + integrity sha512-bLzi9GeZgMCvjJeLUIfs8LJYCxrPRA8IXQkzUtaFKKVPTz0mucRyqFcV2U20yg9K+kYAD0YSitzGfRZCFLjdHQ== + dependencies: + "@sigstore/protobuf-specs" "^0.1.0" + make-fetch-happen "^11.0.1" + tuf-js "^1.1.3" + "@simple-dom/interface@^1.4.0": version "1.4.0" resolved "https://registry.yarnpkg.com/@simple-dom/interface/-/interface-1.4.0.tgz#e8feea579232017f89b0138e2726facda6fbb71f" integrity sha512-l5qumKFWU0S+4ZzMaLXFU8tQZsicHEMEyAxI5kDFGhJsRqDwe0a7/iPA/GdxlGyDKseQQAgIz5kzU7eXTrlSpA== +"@sinclair/typebox@^0.25.16": + version "0.25.24" + resolved "https://registry.yarnpkg.com/@sinclair/typebox/-/typebox-0.25.24.tgz#8c7688559979f7079aacaf31aa881c3aa410b718" + integrity sha512-XJfwUVUKDHF5ugKwIcxEgc9k8b7HbznCp6eUfWgu710hMPNIO4aw4/zB5RogDQz8nd6gyCDpU9O/m6qYEWY6yQ== + "@sindresorhus/is@^0.14.0": version "0.14.0" resolved "https://registry.yarnpkg.com/@sindresorhus/is/-/is-0.14.0.tgz#9fb3a3cf3132328151f353de4632e01e52102bea" @@ -4384,6 +4415,19 @@ resolved "https://registry.yarnpkg.com/@tsconfig/node16/-/node16-1.0.2.tgz#423c77877d0569db20e1fc80885ac4118314010e" integrity sha512-eZxlbI8GZscaGS7kkc/trHTT5xgrjH3/1n2JDwusC9iahPKWMRvRjJSAN5mCXviuTGQ/lHnhvv8Q1YTpnfz9gA== +"@tufjs/canonical-json@1.0.0": + version "1.0.0" + resolved "https://registry.yarnpkg.com/@tufjs/canonical-json/-/canonical-json-1.0.0.tgz#eade9fd1f537993bc1f0949f3aea276ecc4fab31" + integrity sha512-QTnf++uxunWvG2z3UFNzAoQPHxnSXOwtaI3iJ+AohhV+5vONuArPjJE7aPXPVXfXJsqrVbZBu9b81AJoSd09IQ== + +"@tufjs/models@1.0.4": + version "1.0.4" + resolved "https://registry.yarnpkg.com/@tufjs/models/-/models-1.0.4.tgz#5a689630f6b9dbda338d4b208019336562f176ef" + integrity sha512-qaGV9ltJP0EO25YfFUPhxRVK0evXFIAGicsVXuRim4Ed9cjPxYhNnNJ49SFmbeLgtxpslIkX317IgpfcHPVj/A== + dependencies: + "@tufjs/canonical-json" "1.0.0" + minimatch "^9.0.0" + "@types/accepts@^1.3.5": version "1.3.5" resolved "https://registry.yarnpkg.com/@types/accepts/-/accepts-1.3.5.tgz#c34bec115cfc746e04fe5a059df4ce7e7b391575" @@ -5793,10 +5837,10 @@ resolved "https://registry.yarnpkg.com/@yarnpkg/lockfile/-/lockfile-1.1.0.tgz#e77a97fbd345b76d83245edcd17d393b1b41fb31" integrity sha512-GpSwvyXOcOOlV70vbnzjj4fW5xW/FdUF6nQEt1ENy7m4ZCczi1+/buVUPAqmGfqznsORNFzUMjctTIp8a9tuCQ== -"@yarnpkg/parsers@^3.0.0-rc.18": - version "3.0.0-rc.31" - resolved "https://registry.yarnpkg.com/@yarnpkg/parsers/-/parsers-3.0.0-rc.31.tgz#fbcce77c3783b2be8a381edf70bea3182e0b8b16" - integrity sha512-7M67TPmTM5OmtoypK0KHV3vIY9z0v4qZ6zF7flH8THLgjGuoA7naop8pEfL9x5vCtid1PDC4A4COrcym4WAZpQ== +"@yarnpkg/parsers@3.0.0-rc.46": + version "3.0.0-rc.46" + resolved "https://registry.yarnpkg.com/@yarnpkg/parsers/-/parsers-3.0.0-rc.46.tgz#03f8363111efc0ea670e53b0282cd3ef62de4e01" + integrity sha512-aiATs7pSutzda/rq8fnuPwTglyVwjM22bNnK2ZgjrpAjQHSSl3lztd2f9evst1W/qnC58DRz7T7QndUDumAR4Q== dependencies: js-yaml "^3.10.0" tslib "^2.4.0" @@ -5813,7 +5857,7 @@ resolved "https://registry.yarnpkg.com/@zxing/text-encoding/-/text-encoding-0.9.0.tgz#fb50ffabc6c7c66a0c96b4c03e3d9be74864b70b" integrity sha512-U/4aVJ2mxI0aDNI8Uq0wEhMgY+u4CNtEb0om3+y3+niDAsoTCOB33UF0sxpzqzdqXLqmvc+vZyAt4O8pPdfkwA== -JSONStream@^1.0.4, JSONStream@^1.3.4: +JSONStream@^1.3.4, JSONStream@^1.3.5: version "1.3.5" resolved "https://registry.yarnpkg.com/JSONStream/-/JSONStream-1.3.5.tgz#3208c1f08d3a4d99261ab64f92302bc15e111ca0" integrity sha512-E+iruNOY8VV9s4JEbe1aNEm6MiszPRr/UfcHMz0TQh1BXSxHK+ASV1R6W4HpjBhSeS+54PIsAMCBmwD06LLsqQ== @@ -6148,7 +6192,7 @@ ansi-styles@^5.0.0: resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-5.2.0.tgz#07449690ad45777d1924ac2abb2fc8895dba836b" integrity sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA== -ansi-styles@^6.0.0: +ansi-styles@^6.0.0, ansi-styles@^6.1.0: version "6.2.1" resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-6.2.1.tgz#0e62320cf99c21afff3b3012192546aacbfb05c5" integrity sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug== @@ -6308,7 +6352,7 @@ aproba@^1.0.3, aproba@^1.1.1: resolved "https://registry.yarnpkg.com/aproba/-/aproba-1.2.0.tgz#6802e6264efd18c790a1b0d517f0f2627bf2c94a" integrity sha512-Y9J6ZjXtoYh8RnXVCMOU/ttDmk1aBjunq9vO0ta5x85WDQiQfUF9sIPBITdbiiIVcBo03Hi3jMxigBtsddlXRw== -"aproba@^1.0.3 || ^2.0.0", aproba@^2.0.0: +"aproba@^1.0.3 || ^2.0.0": version "2.0.0" resolved "https://registry.yarnpkg.com/aproba/-/aproba-2.0.0.tgz#52520b8ae5b569215b354efc0caa3fe1e45a8adc" integrity sha512-lYe4Gx7QT+MKGbDsA+Z+he/Wtef0BiwDOlK/XkBrdfsh9J/jPPXbX0tE9x9cl27Tmu5gg3QUbUrQYa/y+KOHPQ== @@ -7650,18 +7694,6 @@ bignumber.js@^9.0.0: resolved "https://registry.yarnpkg.com/bignumber.js/-/bignumber.js-9.0.1.tgz#8d7ba124c882bfd8e43260c67475518d0689e4e5" integrity sha512-IdZR9mh6ahOBv/hYGiXyVuyCetmGJhtYkqLBpTStdhEGjegpPlUawydyaF3pbIOFynJTpllEs+NP+CS9jKFLjA== -bin-links@^3.0.0: - version "3.0.3" - resolved "https://registry.yarnpkg.com/bin-links/-/bin-links-3.0.3.tgz#3842711ef3db2cd9f16a5f404a996a12db355a6e" - integrity sha512-zKdnMPWEdh4F5INR07/eBrodC7QrF5JKvqskjz/ZZRXg5YSAZIbn8zGhbhUrElzHBZ2fvEQdOU59RHcTG3GiwA== - dependencies: - cmd-shim "^5.0.0" - mkdirp-infer-owner "^2.0.0" - npm-normalize-package-bin "^2.0.0" - read-cmd-shim "^3.0.0" - rimraf "^3.0.0" - write-file-atomic "^4.0.0" - binary-extensions@^1.0.0: version "1.13.1" resolved "https://registry.yarnpkg.com/binary-extensions/-/binary-extensions-1.13.1.tgz#598afe54755b2868a5330d2aff9d4ebb53209b65" @@ -8554,10 +8586,10 @@ busboy@^1.6.0: dependencies: streamsearch "^1.1.0" -byte-size@7.0.0: - version "7.0.0" - resolved "https://registry.npmjs.org/byte-size/-/byte-size-7.0.0.tgz#36528cd1ca87d39bd9abd51f5715dc93b6ceb032" - integrity sha512-NNiBxKgxybMBtWdmvx7ZITJi4ZG+CYUgwOSZTfqB1qogkRHrhbQE/R2r5Fh94X+InN5MCYz6SvB/ejHMj/HbsQ== +byte-size@8.1.1: + version "8.1.1" + resolved "https://registry.yarnpkg.com/byte-size/-/byte-size-8.1.1.tgz#3424608c62d59de5bfda05d31e0313c6174842ae" + integrity sha512-tUkzZWK0M/qdoLEqikxBWe4kumyuwjl3HO6zHTr4yEI23EojPtLYXdG1+AQY7MN0cGyNDvEaJ8wiYQm6P2bPxg== bytes@1: version "1.0.0" @@ -8693,7 +8725,7 @@ cacache@^15.0.4, cacache@^15.0.5, cacache@^15.0.6, cacache@^15.2.0: tar "^6.0.2" unique-filename "^1.1.1" -cacache@^16.0.0, cacache@^16.0.6, cacache@^16.1.0: +cacache@^16.1.0: version "16.1.3" resolved "https://registry.yarnpkg.com/cacache/-/cacache-16.1.3.tgz#a02b9f34ecfaf9a78c9f4bc16fceb94d5d67a38e" integrity sha512-/+Emcj9DAXxX4cwlLmRI9c166RuL3w30zp4R7Joiv2cQTtTtA+jeuCAjH3ZlGnYS3tKENSrKhAzVVP9GVyzeYQ== @@ -8717,6 +8749,24 @@ cacache@^16.0.0, cacache@^16.0.6, cacache@^16.1.0: tar "^6.1.11" unique-filename "^2.0.0" +cacache@^17.0.0: + version "17.1.3" + resolved "https://registry.yarnpkg.com/cacache/-/cacache-17.1.3.tgz#c6ac23bec56516a7c0c52020fd48b4909d7c7044" + integrity sha512-jAdjGxmPxZh0IipMdR7fK/4sDSrHMLUV0+GvVUsjwyGNKHsh79kW/otg+GkbXwl6Uzvy9wsvHOX4nUoWldeZMg== + dependencies: + "@npmcli/fs" "^3.1.0" + fs-minipass "^3.0.0" + glob "^10.2.2" + lru-cache "^7.7.1" + minipass "^5.0.0" + minipass-collect "^1.0.2" + minipass-flush "^1.0.5" + minipass-pipeline "^1.2.4" + p-map "^4.0.0" + ssri "^10.0.0" + tar "^6.1.11" + unique-filename "^3.0.0" + cache-base@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/cache-base/-/cache-base-1.0.1.tgz#0a7f46416831c8b662ee36fe4e7c59d76f666ab2" @@ -9064,6 +9114,11 @@ ci-info@^3.2.0, ci-info@^3.3.2, ci-info@^3.4.0: resolved "https://registry.yarnpkg.com/ci-info/-/ci-info-3.5.0.tgz#bfac2a29263de4c829d806b1ab478e35091e171f" integrity sha512-yH4RezKOGlOhxkmhbeNuC4eYZKAUsEaGtBuBzDDP1eFUKiccDWzBABxBfOx31IDwDIXMTxWuwAxUGModvkbuVw== +ci-info@^3.6.1: + version "3.8.0" + resolved "https://registry.yarnpkg.com/ci-info/-/ci-info-3.8.0.tgz#81408265a5380c929f0bc665d62256628ce9ef91" + integrity sha512-eXTggHWSooYhq49F2opQhuHWgzucfF2YgODK4e1566GQs5BIfP30B0oenwBJHfWxAs2fyPB1s7Mg949zLf61Yw== + ci-job-number@^1.2.2: version "1.2.2" resolved "https://registry.yarnpkg.com/ci-job-number/-/ci-job-number-1.2.2.tgz#f4e5918fcaeeda95b604f214be7d7d4a961fe0c0" @@ -9266,12 +9321,10 @@ clone@^2.1.2: resolved "https://registry.yarnpkg.com/clone/-/clone-2.1.2.tgz#1b7f4b9f591f1e8f83670401600345a02887435f" integrity sha1-G39Ln1kfHo+DZwQBYANFoCiHQ18= -cmd-shim@5.0.0, cmd-shim@^5.0.0: - version "5.0.0" - resolved "https://registry.yarnpkg.com/cmd-shim/-/cmd-shim-5.0.0.tgz#8d0aaa1a6b0708630694c4dbde070ed94c707724" - integrity sha512-qkCtZ59BidfEwHltnJwkyVZn+XQojdAySM1D1gSeh11Z4pW1Kpolkyo53L5noc0nrxmIvyFwTmJRo4xs7FFLPw== - dependencies: - mkdirp-infer-owner "^2.0.0" +cmd-shim@6.0.1: + version "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" @@ -9477,11 +9530,6 @@ commenting@1.1.0: resolved "https://registry.yarnpkg.com/commenting/-/commenting-1.1.0.tgz#fae14345c6437b8554f30bc6aa6c1e1633033590" integrity sha512-YeNK4tavZwtH7jEgK1ZINXzLKm6DZdEMfsaaieOsCAN0S8vsY7UeuO3Q7d/M018EFgE+IeUAuBOKkFccBZsUZA== -common-ancestor-path@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/common-ancestor-path/-/common-ancestor-path-1.0.1.tgz#4f7d2d1394d91b7abdf51871c62f71eadb0182a7" - integrity sha512-L3sHRo1pXXEqX8VU28kfgUY+YGsk09hPqZiZmLacNib6XNTCM8ubYeT7ryXQw8asB1sKgcU5lkB7ONug08aB8w== - common-tags@^1.8.0: version "1.8.0" resolved "https://registry.yarnpkg.com/common-tags/-/common-tags-1.8.0.tgz#8e3153e542d4a39e9b10554434afaaf98956a937" @@ -9557,14 +9605,6 @@ concat-stream@^2.0.0: readable-stream "^3.0.2" typedarray "^0.0.6" -config-chain@1.1.12: - version "1.1.12" - resolved "https://registry.npmjs.org/config-chain/-/config-chain-1.1.12.tgz#0fde8d091200eb5e808caf25fe618c02f48e4efa" - integrity sha512-a1eOIcu8+7lUInge4Rpf/n4Krkf3Dd9lqhljRzII1/Zno/kRtUWnznPO3jOKBmTEktkt3fkxisUcivoj0ebzoA== - dependencies: - ini "^1.3.4" - proto-list "~1.2.1" - configstore@^5.0.1: version "5.0.1" resolved "https://registry.yarnpkg.com/configstore/-/configstore-5.0.1.tgz#d365021b5df4b98cdd187d6a3b0e3f6a7cc5ed96" @@ -9647,87 +9687,78 @@ continuable-cache@^0.3.1: resolved "https://registry.yarnpkg.com/continuable-cache/-/continuable-cache-0.3.1.tgz#bd727a7faed77e71ff3985ac93351a912733ad0f" integrity sha1-vXJ6f67XfnH/OYWskzUakSczrQ8= -conventional-changelog-angular@5.0.12: - version "5.0.12" - resolved "https://registry.npmjs.org/conventional-changelog-angular/-/conventional-changelog-angular-5.0.12.tgz#c979b8b921cbfe26402eb3da5bbfda02d865a2b9" - integrity sha512-5GLsbnkR/7A89RyHLvvoExbiGbd9xKdKqDTrArnPbOqBqG/2wIosu0fHwpeIRI8Tl94MhVNBXcLJZl92ZQ5USw== +conventional-changelog-angular@6.0.0: + version "6.0.0" + resolved "https://registry.yarnpkg.com/conventional-changelog-angular/-/conventional-changelog-angular-6.0.0.tgz#a9a9494c28b7165889144fd5b91573c4aa9ca541" + integrity sha512-6qLgrBF4gueoC7AFVHu51nHL9pF9FRjXrH+ceVf7WmAfH3gs+gEYOkvxhjMPjZu57I4AGUGoNTY8V7Hrgf1uqg== dependencies: compare-func "^2.0.0" - q "^1.5.1" -conventional-changelog-core@4.2.4: - version "4.2.4" - resolved "https://registry.npmjs.org/conventional-changelog-core/-/conventional-changelog-core-4.2.4.tgz#e50d047e8ebacf63fac3dc67bf918177001e1e9f" - integrity sha512-gDVS+zVJHE2v4SLc6B0sLsPiloR0ygU7HaDW14aNJE1v4SlqJPILPl/aJC7YdtRE4CybBf8gDwObBvKha8Xlyg== +conventional-changelog-core@5.0.1: + version "5.0.1" + resolved "https://registry.yarnpkg.com/conventional-changelog-core/-/conventional-changelog-core-5.0.1.tgz#3c331b155d5b9850f47b4760aeddfc983a92ad49" + integrity sha512-Rvi5pH+LvgsqGwZPZ3Cq/tz4ty7mjijhr3qR4m9IBXNbxGGYgTVVO+duXzz9aArmHxFtwZ+LRkrNIMDQzgoY4A== dependencies: add-stream "^1.0.0" - conventional-changelog-writer "^5.0.0" - conventional-commits-parser "^3.2.0" - dateformat "^3.0.0" - get-pkg-repo "^4.0.0" - git-raw-commits "^2.0.8" + conventional-changelog-writer "^6.0.0" + conventional-commits-parser "^4.0.0" + dateformat "^3.0.3" + get-pkg-repo "^4.2.1" + git-raw-commits "^3.0.0" git-remote-origin-url "^2.0.0" - git-semver-tags "^4.1.1" - lodash "^4.17.15" - normalize-package-data "^3.0.0" - q "^1.5.1" + git-semver-tags "^5.0.0" + normalize-package-data "^3.0.3" read-pkg "^3.0.0" read-pkg-up "^3.0.0" - through2 "^4.0.0" -conventional-changelog-preset-loader@^2.3.4: - version "2.3.4" - resolved "https://registry.yarnpkg.com/conventional-changelog-preset-loader/-/conventional-changelog-preset-loader-2.3.4.tgz#14a855abbffd59027fd602581f1f34d9862ea44c" - integrity sha512-GEKRWkrSAZeTq5+YjUZOYxdHq+ci4dNwHvpaBC3+ENalzFWuCWa9EZXSuZBpkr72sMdKB+1fyDV4takK1Lf58g== +conventional-changelog-preset-loader@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/conventional-changelog-preset-loader/-/conventional-changelog-preset-loader-3.0.0.tgz#14975ef759d22515d6eabae6396c2ae721d4c105" + integrity sha512-qy9XbdSLmVnwnvzEisjxdDiLA4OmV3o8db+Zdg4WiFw14fP3B6XNz98X0swPPpkTd/pc1K7+adKgEDM1JCUMiA== -conventional-changelog-writer@^5.0.0: - version "5.0.1" - resolved "https://registry.yarnpkg.com/conventional-changelog-writer/-/conventional-changelog-writer-5.0.1.tgz#e0757072f045fe03d91da6343c843029e702f359" - integrity sha512-5WsuKUfxW7suLblAbFnxAcrvf6r+0b7GvNaWUwUIk0bXMnENP/PEieGKVUQrjPqwPT4o3EPAASBXiY6iHooLOQ== +conventional-changelog-writer@^6.0.0: + version "6.0.0" + resolved "https://registry.yarnpkg.com/conventional-changelog-writer/-/conventional-changelog-writer-6.0.0.tgz#8c8dea0441c6e648c9b25bb784e750d02f8002d5" + integrity sha512-8PyWTnn7zBIt9l4hj4UusFs1TyG+9Ulu1zlOAc72L7Sdv9Hsc8E86ot7htY3HXCVhXHB/NO0pVGvZpwsyJvFfw== dependencies: - conventional-commits-filter "^2.0.7" - dateformat "^3.0.0" + conventional-commits-filter "^3.0.0" + dateformat "^3.0.3" handlebars "^4.7.7" json-stringify-safe "^5.0.1" - lodash "^4.17.15" - meow "^8.0.0" - semver "^6.0.0" - split "^1.0.0" - through2 "^4.0.0" + meow "^8.1.2" + semver "^6.3.0" + split "^1.0.1" -conventional-commits-filter@^2.0.7: - version "2.0.7" - resolved "https://registry.yarnpkg.com/conventional-commits-filter/-/conventional-commits-filter-2.0.7.tgz#f8d9b4f182fce00c9af7139da49365b136c8a0b3" - integrity sha512-ASS9SamOP4TbCClsRHxIHXRfcGCnIoQqkvAzCSbZzTFLfcTqJVugB0agRgsEELsqaeWgsXv513eS116wnlSSPA== +conventional-commits-filter@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/conventional-commits-filter/-/conventional-commits-filter-3.0.0.tgz#bf1113266151dd64c49cd269e3eb7d71d7015ee2" + integrity sha512-1ymej8b5LouPx9Ox0Dw/qAO2dVdfpRFq28e5Y0jJEU8ZrLdy0vOSkkIInwmxErFGhg6SALro60ZrwYFVTUDo4Q== dependencies: lodash.ismatch "^4.4.0" - modify-values "^1.0.0" + modify-values "^1.0.1" -conventional-commits-parser@^3.2.0: - version "3.2.4" - resolved "https://registry.yarnpkg.com/conventional-commits-parser/-/conventional-commits-parser-3.2.4.tgz#a7d3b77758a202a9b2293d2112a8d8052c740972" - integrity sha512-nK7sAtfi+QXbxHCYfhpZsfRtaitZLIA6889kFIouLvz6repszQDgxBu7wf2WbU+Dco7sAnNCJYERCwt54WPC2Q== +conventional-commits-parser@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/conventional-commits-parser/-/conventional-commits-parser-4.0.0.tgz#02ae1178a381304839bce7cea9da5f1b549ae505" + integrity sha512-WRv5j1FsVM5FISJkoYMR6tPk07fkKT0UodruX4je86V4owk451yjXAKzKAPOs9l7y59E2viHUS9eQ+dfUA9NSg== dependencies: - JSONStream "^1.0.4" + JSONStream "^1.3.5" is-text-path "^1.0.1" - lodash "^4.17.15" - meow "^8.0.0" - split2 "^3.0.0" - through2 "^4.0.0" + meow "^8.1.2" + split2 "^3.2.2" -conventional-recommended-bump@6.1.0: - version "6.1.0" - resolved "https://registry.npmjs.org/conventional-recommended-bump/-/conventional-recommended-bump-6.1.0.tgz#cfa623285d1de554012f2ffde70d9c8a22231f55" - integrity sha512-uiApbSiNGM/kkdL9GTOLAqC4hbptObFo4wW2QRyHsKciGAfQuLU1ShZ1BIVI/+K2BE/W1AWYQMCXAsv4dyKPaw== +conventional-recommended-bump@7.0.1: + version "7.0.1" + resolved "https://registry.yarnpkg.com/conventional-recommended-bump/-/conventional-recommended-bump-7.0.1.tgz#ec01f6c7f5d0e2491c2d89488b0d757393392424" + integrity sha512-Ft79FF4SlOFvX4PkwFDRnaNiIVX7YbmqGU0RwccUaiGvgp3S0a8ipR2/Qxk31vclDNM+GSdJOVs2KrsUCjblVA== dependencies: concat-stream "^2.0.0" - conventional-changelog-preset-loader "^2.3.4" - conventional-commits-filter "^2.0.7" - conventional-commits-parser "^3.2.0" - git-raw-commits "^2.0.8" - git-semver-tags "^4.1.1" - meow "^8.0.0" - q "^1.5.1" + conventional-changelog-preset-loader "^3.0.0" + conventional-commits-filter "^3.0.0" + conventional-commits-parser "^4.0.0" + git-raw-commits "^3.0.0" + git-semver-tags "^5.0.0" + meow "^8.1.2" convert-source-map@1.7.0, convert-source-map@^1.4.0, convert-source-map@^1.5.1, convert-source-map@^1.6.0, convert-source-map@^1.7.0: version "1.7.0" @@ -9879,17 +9910,6 @@ cors@^2.8.5, cors@~2.8.5: object-assign "^4" vary "^1" -cosmiconfig@7.0.0: - version "7.0.0" - resolved "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-7.0.0.tgz#ef9b44d773959cae63ddecd122de23853b60f8d3" - integrity sha512-pondGvTuVYDk++upghXJabWzL6Kxu6f26ljFw64Swq9v6sQPUL3EUlVDV56diOjpCayKihL6hVe8exIACU4XcA== - dependencies: - "@types/parse-json" "^4.0.0" - import-fresh "^3.2.1" - parse-json "^5.0.0" - path-type "^4.0.0" - yaml "^1.10.0" - cosmiconfig@^5.0.0: version "5.2.1" resolved "https://registry.yarnpkg.com/cosmiconfig/-/cosmiconfig-5.2.1.tgz#040f726809c591e77a17c0a3626ca45b4f168b1a" @@ -9911,6 +9931,16 @@ cosmiconfig@^7.0.0: path-type "^4.0.0" yaml "^1.10.0" +cosmiconfig@^8.2.0: + version "8.2.0" + resolved "https://registry.yarnpkg.com/cosmiconfig/-/cosmiconfig-8.2.0.tgz#f7d17c56a590856cd1e7cee98734dca272b0d8fd" + integrity sha512-3rTMnFJA1tCOPwRxtgF4wd7Ab2qvDbL8jX+3smjIbS4HlZBagTlpERbdN7iAbWlrfxE3M8c27kTwTawQ7st+OQ== + dependencies: + import-fresh "^3.2.1" + js-yaml "^4.1.0" + parse-json "^5.0.0" + path-type "^4.0.0" + create-ecdh@^4.0.0: version "4.0.4" resolved "https://registry.yarnpkg.com/create-ecdh/-/create-ecdh-4.0.4.tgz#d6e7f4bffa66736085a0762fd3a632684dabcc4e" @@ -10493,7 +10523,7 @@ dateformat@^1.0.6: get-stdin "^4.0.1" meow "^3.3.0" -dateformat@^3.0.0: +dateformat@^3.0.3: version "3.0.3" resolved "https://registry.yarnpkg.com/dateformat/-/dateformat-3.0.3.tgz#a6e37499a4d9a9cf85ef5872044d62901c9889ae" integrity sha512-jyCETtSl3VMZMWeRo7iY1FL19ges1t55hMo5yaam4Jrsm5EPL89UQkoQRyiI+Yf4k8r2ZpdngkV8hr1lIdjb3Q== @@ -10919,6 +10949,11 @@ diff-sequences@^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.4.3: + version "29.4.3" + resolved "https://registry.yarnpkg.com/diff-sequences/-/diff-sequences-29.4.3.tgz#9314bc1fabe09267ffeca9cbafc457d8499a13f2" + integrity sha512-ofrBgwpPhCD85kMKtE9RYFFq6OC1A89oW2vvgWZNCwxrUpRUILopY7lsYyMDSjc8g6U6aiO0Qubg6r4Wgt5ZnA== + diff@3.5.0, diff@^3.5.0: version "3.5.0" resolved "https://registry.yarnpkg.com/diff/-/diff-3.5.0.tgz#800c0dd1e0a8bfbc95835c202ad220fe317e5a12" @@ -11104,13 +11139,6 @@ dot-case@^3.0.4: no-case "^3.0.4" tslib "^2.0.3" -dot-prop@6.0.1: - version "6.0.1" - resolved "https://registry.npmjs.org/dot-prop/-/dot-prop-6.0.1.tgz#fc26b3cf142b9e59b74dbd39ed66ce620c681083" - integrity sha512-tE7ztYzXHIeyvc7N+hR3oi7FIbf/NIjVP9hmAt3yMXzrQ072/fpjGLx2GxNxGxUl5V73MEqYzioOMoVhGMJ5cA== - dependencies: - is-obj "^2.0.0" - dot-prop@^5.1.0, dot-prop@^5.2.0: version "5.3.0" resolved "https://registry.yarnpkg.com/dot-prop/-/dot-prop-5.3.0.tgz#90ccce708cd9cd82cc4dc8c3ddd9abdd55b20e88" @@ -12051,7 +12079,7 @@ env-paths@^2.2.0: resolved "https://registry.yarnpkg.com/env-paths/-/env-paths-2.2.1.tgz#420399d416ce1fbe9bc0a07c62fa68d67fd0f8f2" integrity sha512-+h1lkLKhZMTYjog1VEpJNG7NZJWcuc2DDk/qsqSTRRCOXiLjeQ1d1/udrUGhqMxUgAlwKNZ0cf2uqan5GLuS2A== -envinfo@^7.7.4: +envinfo@7.8.1: version "7.8.1" resolved "https://registry.yarnpkg.com/envinfo/-/envinfo-7.8.1.tgz#06377e3e5f4d379fea7ac592d5ad8927e0c4d475" integrity sha512-/o+BXHmB7ocbHEAs6F2EnG0ogybVVUdkRunTT2glZU9XAaGmhqskrvKwqXuDfNjEO0LZKWdejEEpnq8aM0tOaw== @@ -12652,7 +12680,7 @@ esprima@~3.0.0: resolved "https://registry.yarnpkg.com/esprima/-/esprima-3.0.0.tgz#53cf247acda77313e551c3aa2e73342d3fb4f7d9" integrity sha1-U88kes2ncxPlUcOqLnM0LT+099k= -esquery@^1.0.1, esquery@^1.4.0: +esquery@^1.4.0: version "1.4.0" resolved "https://registry.yarnpkg.com/esquery/-/esquery-1.4.0.tgz#2148ffc38b82e8c7057dfed48425b3e61f0f24a5" integrity sha512-cCDispWt5vHHtwMY2YrAQ4ibFkAL8RbH5YGBnZBc90MolvvfkkQcJro/aZiAQUlQ3qgrYS6D6v8Gc5G5CQsc9w== @@ -13499,6 +13527,14 @@ foreground-child@^2.0.0: cross-spawn "^7.0.0" signal-exit "^3.0.2" +foreground-child@^3.1.0: + version "3.1.1" + resolved "https://registry.yarnpkg.com/foreground-child/-/foreground-child-3.1.1.tgz#1d173e776d75d2772fed08efe4a0de1ea1b12d0d" + integrity sha512-TMKDUnIte6bfb5nWv7V/caI169OHgvwjb7V4WkeUvbQQdjr5rWKqHFiKWb/fcOwB+CzBT+qbWjvj+DVwRskpIg== + dependencies: + cross-spawn "^7.0.0" + signal-exit "^4.0.1" + forever-agent@~0.6.1: version "0.6.1" resolved "https://registry.yarnpkg.com/forever-agent/-/forever-agent-0.6.1.tgz#fbc71f0c41adeb37f96c577ad1ed42d8fdacca91" @@ -13591,16 +13627,6 @@ fs-extra@4.0.2: jsonfile "^4.0.0" universalify "^0.1.0" -fs-extra@9.1.0, fs-extra@^9.0.0, fs-extra@^9.0.1, fs-extra@^9.1.0: - version "9.1.0" - resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-9.1.0.tgz#5954460c764a8da2094ba3554bf839e6b9a7c86d" - integrity sha512-hcg3ZmepS30/7BSFqRvoo3DOMQu7IjqxO5nCDt+zM9XWjb33Wg7ziNT+Qvqbuc3+gWpzO02JubVyk2G4Zvo1OQ== - dependencies: - at-least-node "^1.0.0" - graceful-fs "^4.2.0" - jsonfile "^6.0.1" - universalify "^2.0.0" - fs-extra@^0.24.0: version "0.24.0" resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-0.24.0.tgz#d4e4342a96675cb7846633a6099249332b539952" @@ -13620,6 +13646,15 @@ fs-extra@^10.0.0, fs-extra@^10.1.0: jsonfile "^6.0.1" universalify "^2.0.0" +fs-extra@^11.1.1: + version "11.1.1" + resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-11.1.1.tgz#da69f7c39f3b002378b0954bb6ae7efdc0876e2d" + integrity sha512-MGIE4HOvQCeUCzmlHs0vXpih4ysz4wg9qiSAu6cd42lVwPbTM1TjV7RusoyQqMmk/95gdQZX72u+YW+c3eEpFQ== + dependencies: + graceful-fs "^4.2.0" + jsonfile "^6.0.1" + universalify "^2.0.0" + fs-extra@^4.0.2, fs-extra@^4.0.3: version "4.0.3" resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-4.0.3.tgz#0d852122e5bc5beb453fb028e9c0c9bf36340c94" @@ -13656,6 +13691,16 @@ fs-extra@^8.0.0, fs-extra@^8.0.1, fs-extra@^8.1.0: jsonfile "^4.0.0" universalify "^0.1.0" +fs-extra@^9.0.0, fs-extra@^9.0.1, fs-extra@^9.1.0: + version "9.1.0" + resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-9.1.0.tgz#5954460c764a8da2094ba3554bf839e6b9a7c86d" + integrity sha512-hcg3ZmepS30/7BSFqRvoo3DOMQu7IjqxO5nCDt+zM9XWjb33Wg7ziNT+Qvqbuc3+gWpzO02JubVyk2G4Zvo1OQ== + dependencies: + at-least-node "^1.0.0" + graceful-fs "^4.2.0" + jsonfile "^6.0.1" + universalify "^2.0.0" + fs-merger@^3.2.1: version "3.2.1" resolved "https://registry.yarnpkg.com/fs-merger/-/fs-merger-3.2.1.tgz#a225b11ae530426138294b8fbb19e82e3d4e0b3b" @@ -13681,6 +13726,13 @@ fs-minipass@^2.0.0, fs-minipass@^2.1.0: dependencies: minipass "^3.0.0" +fs-minipass@^3.0.0: + version "3.0.2" + resolved "https://registry.yarnpkg.com/fs-minipass/-/fs-minipass-3.0.2.tgz#5b383858efa8c1eb8c33b39e994f7e8555b8b3a3" + integrity sha512-2GAfyfoaCDRrM6jaOS3UsBts8yJ55VioXdWcOL7dK9zdAuKT71+WBA4ifnNYqVjYv+4SsPxjK0JT4yIIn4cA/g== + dependencies: + minipass "^5.0.0" + fs-monkey@^1.0.3: version "1.0.3" resolved "https://registry.yarnpkg.com/fs-monkey/-/fs-monkey-1.0.3.tgz#ae3ac92d53bb328efe0e9a1d9541f6ad8d48e2d3" @@ -13882,7 +13934,7 @@ get-package-type@^0.1.0: resolved "https://registry.yarnpkg.com/get-package-type/-/get-package-type-0.1.0.tgz#8de2d803cff44df3bc6c456e6668b36c3926e11a" integrity sha512-pjzuKtY64GYfWizNAJ0fr9VqttZkNiK2iS430LtIHzjBEr6bX8Am2zm4sW4Ro5wjWW5cAlRL1qAMTcXbjNAO2Q== -get-pkg-repo@^4.0.0: +get-pkg-repo@^4.2.1: version "4.2.1" resolved "https://registry.yarnpkg.com/get-pkg-repo/-/get-pkg-repo-4.2.1.tgz#75973e1c8050c73f48190c52047c4cee3acbf385" integrity sha512-2+QbHjFRfGB74v/pYWjd5OhU3TDIC2Gv/YKUTk/tCvAz0pkn/Mz6P3uByuBimLOcPvN2jYdScl3xGFSrx0jEcA== @@ -13961,16 +14013,14 @@ git-hooks-list@1.0.3: resolved "https://registry.yarnpkg.com/git-hooks-list/-/git-hooks-list-1.0.3.tgz#be5baaf78203ce342f2f844a9d2b03dba1b45156" integrity sha512-Y7wLWcrLUXwk2noSka166byGCvhMtDRpgHdzCno1UQv/n/Hegp++a2xBWJL1lJarnKD3SWaljD+0z1ztqxuKyQ== -git-raw-commits@^2.0.8: - version "2.0.11" - resolved "https://registry.yarnpkg.com/git-raw-commits/-/git-raw-commits-2.0.11.tgz#bc3576638071d18655e1cc60d7f524920008d723" - integrity sha512-VnctFhw+xfj8Va1xtfEqCUD2XDrbAPSJx+hSrE5K7fGdjZruW7XV+QOrN7LF/RJyvspRiD2I0asWsxFp0ya26A== +git-raw-commits@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/git-raw-commits/-/git-raw-commits-3.0.0.tgz#5432f053a9744f67e8db03dbc48add81252cfdeb" + integrity sha512-b5OHmZ3vAgGrDn/X0kS+9qCfNKWe4K/jFnhwzVWWg0/k5eLa3060tZShrRg8Dja5kPc+YjS0Gc6y7cRr44Lpjw== dependencies: dargs "^7.0.0" - lodash "^4.17.15" - meow "^8.0.0" - split2 "^3.0.0" - through2 "^4.0.0" + meow "^8.1.2" + split2 "^3.2.2" git-remote-origin-url@^2.0.0: version "2.0.0" @@ -13985,13 +14035,13 @@ git-repo-info@^2.1.1: resolved "https://registry.yarnpkg.com/git-repo-info/-/git-repo-info-2.1.1.tgz#220ffed8cbae74ef8a80e3052f2ccb5179aed058" integrity sha512-8aCohiDo4jwjOwma4FmYFd3i97urZulL8XL24nIPxuE+GZnfsAyy/g2Shqx6OjUiFKUXZM+Yy+KHnOmmA3FVcg== -git-semver-tags@^4.1.1: - version "4.1.1" - resolved "https://registry.yarnpkg.com/git-semver-tags/-/git-semver-tags-4.1.1.tgz#63191bcd809b0ec3e151ba4751c16c444e5b5780" - integrity sha512-OWyMt5zBe7xFs8vglMmhM9lRQzCWL3WjHtxNNfJTMngGym7pC1kh8sP6jevfydJ6LP3ZvGxfb6ABYgPUM0mtsA== +git-semver-tags@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/git-semver-tags/-/git-semver-tags-5.0.0.tgz#775ff55effae0b50b755448408de6cd56ce293e2" + integrity sha512-fZ+tmZ1O5aXW/T5nLzZLbxWAHdQTLLXalOECMNAmhoEQSfqZjtaeMjpsXH4C5qVhrICTkVQeQFujB1lKzIHljA== dependencies: - meow "^8.0.0" - semver "^6.0.0" + meow "^8.1.2" + semver "^6.3.0" git-up@^7.0.0: version "7.0.0" @@ -14123,6 +14173,17 @@ glob@9.3.2: minipass "^4.2.4" path-scurry "^1.6.1" +glob@^10.2.2: + version "10.3.1" + resolved "https://registry.yarnpkg.com/glob/-/glob-10.3.1.tgz#9789cb1b994515bedb811a6deca735b5c37d2bf4" + integrity sha512-9BKYcEeIs7QwlCYs+Y3GBvqAMISufUS0i2ELd11zpZjxI5V9iyRj0HgzB5/cLf2NY4vcYBTYzJ7GIui7j/4DOw== + dependencies: + foreground-child "^3.1.0" + jackspeak "^2.0.3" + minimatch "^9.0.1" + minipass "^5.0.0 || ^6.0.2" + path-scurry "^1.10.0" + glob@^5.0.10, glob@^5.0.15: version "5.0.15" resolved "https://registry.yarnpkg.com/glob/-/glob-5.0.15.tgz#1bc936b9e02f4a603fcc222ecf7633d30b8b93b1" @@ -14146,6 +14207,16 @@ glob@^7.0.0, glob@^7.0.3, glob@^7.0.4, glob@^7.0.5, glob@^7.1.1, glob@^7.1.2, gl once "^1.3.0" path-is-absolute "^1.0.0" +glob@^9.2.0: + version "9.3.5" + resolved "https://registry.yarnpkg.com/glob/-/glob-9.3.5.tgz#ca2ed8ca452781a3009685607fdf025a899dfe21" + integrity sha512-e1LleDykUz2Iu+MTYdkSsuWX8lvAjAcs0Xef0lNIu0S2wOAzuTxCJtcd9S3cijlwYF18EsU3rzb8jPVobxDh9Q== + dependencies: + fs.realpath "^1.0.0" + minimatch "^8.0.2" + minipass "^4.2.4" + path-scurry "^1.6.1" + global-dirs@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/global-dirs/-/global-dirs-3.0.0.tgz#70a76fe84ea315ab37b1f5576cbde7d48ef72686" @@ -14349,7 +14420,12 @@ got@^9.6.0: to-readable-stream "^1.0.0" url-parse-lax "^3.0.0" -graceful-fs@4.2.10, graceful-fs@^4.1.11, graceful-fs@^4.1.15, graceful-fs@^4.1.2, graceful-fs@^4.1.3, graceful-fs@^4.1.6, graceful-fs@^4.2.0, graceful-fs@^4.2.4, graceful-fs@^4.2.6, graceful-fs@^4.2.9: +graceful-fs@4.2.11: + version "4.2.11" + resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.11.tgz#4183e4e8bf08bb6e05bbb2f7d2e0c8f712ca40e3" + integrity sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ== + +graceful-fs@^4.1.11, graceful-fs@^4.1.15, graceful-fs@^4.1.2, graceful-fs@^4.1.3, graceful-fs@^4.1.6, graceful-fs@^4.2.0, graceful-fs@^4.2.4, graceful-fs@^4.2.6, graceful-fs@^4.2.9: version "4.2.10" resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.10.tgz#147d3a006da4ca3ce14728c7aefc287c367d7a6c" integrity sha512-9ByhssR2fPVsNZj478qUUbKfmL0+t5BDVyjShtyZZLiK7ZDAArFFfopyOTj0M05wE2tJPisA4iTnnXl2YoPvOA== @@ -14765,6 +14841,13 @@ hosted-git-info@^5.0.0: dependencies: lru-cache "^7.5.1" +hosted-git-info@^6.0.0: + version "6.1.1" + resolved "https://registry.yarnpkg.com/hosted-git-info/-/hosted-git-info-6.1.1.tgz#629442c7889a69c05de604d52996b74fe6f26d58" + integrity sha512-r0EI+HBMcXadMrugk0GCQ+6BQV39PiWAZVfq7oIckeGiN7sjRGyQxPdft3nQekFTCQbYxLBH+/axZMeH8UX6+w== + dependencies: + lru-cache "^7.5.1" + hpack.js@^2.1.6: version "2.1.6" resolved "https://registry.yarnpkg.com/hpack.js/-/hpack.js-2.1.6.tgz#87774c0949e513f42e84575b3c45681fade2a0b2" @@ -14858,6 +14941,11 @@ http-cache-semantics@^4.0.0, http-cache-semantics@^4.1.0: resolved "https://registry.yarnpkg.com/http-cache-semantics/-/http-cache-semantics-4.1.0.tgz#49e91c5cbf36c9b94bcfcd71c23d5249ec74e390" integrity sha512-carPklcUh7ROWRK7Cv27RPtdhYhUsela/ue5/jKzjegVvXDqM2ILE9Q2BGn9JZJh1g87cp56su/FgQSzcWS8cQ== +http-cache-semantics@^4.1.1: + version "4.1.1" + resolved "https://registry.yarnpkg.com/http-cache-semantics/-/http-cache-semantics-4.1.1.tgz#abe02fcb2985460bf0323be664436ec3476a6d5a" + integrity sha512-er295DKPVsV82j5kw1Gjt+ADA/XYHsajl82cGNQG2eyoPkvgUhX+nDIyelzhIWbbsXP39EHcI6l5tYs2FYqYXQ== + http-deceiver@^1.2.7: version "1.2.7" resolved "https://registry.yarnpkg.com/http-deceiver/-/http-deceiver-1.2.7.tgz#fa7168944ab9a519d337cb0bec7284dc3e723d87" @@ -15072,6 +15160,13 @@ ignore-walk@^5.0.1: dependencies: minimatch "^5.0.1" +ignore-walk@^6.0.0: + version "6.0.3" + resolved "https://registry.yarnpkg.com/ignore-walk/-/ignore-walk-6.0.3.tgz#0fcdb6decaccda35e308a7b0948645dd9523b7bb" + integrity sha512-C7FfFoTA+bI10qfeydT8aZbvr91vAEU+2W5BZUlzPec47oNb07SsOfwYrtxuvOYdUApPP/Qlh4DtAO51Ekk2QA== + dependencies: + minimatch "^9.0.0" + ignore@^4.0.6: version "4.0.6" resolved "https://registry.yarnpkg.com/ignore/-/ignore-4.0.6.tgz#750e3db5862087b4737ebac8207ffd1ef27b25fc" @@ -15132,6 +15227,14 @@ 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: + version "3.1.0" + resolved "https://registry.yarnpkg.com/import-local/-/import-local-3.1.0.tgz#b4479df8a5fd44f6cdce24070675676063c95cb4" + integrity sha512-ASB07uLtnDs1o6EHjKpX34BKYDSqnFerfTOJL2HvMqF70LnxpjkzDB8J44oT9pu4AMPkQwf8jl6szgvNd2tRIg== + dependencies: + pkg-dir "^4.2.0" + resolve-cwd "^3.0.0" + import-local@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/import-local/-/import-local-2.0.0.tgz#55070be38a5993cf18ef6db7e961f5bee5c5a09d" @@ -15140,14 +15243,6 @@ import-local@^2.0.0: pkg-dir "^3.0.0" resolve-cwd "^2.0.0" -import-local@^3.0.2: - version "3.1.0" - resolved "https://registry.yarnpkg.com/import-local/-/import-local-3.1.0.tgz#b4479df8a5fd44f6cdce24070675676063c95cb4" - integrity sha512-ASB07uLtnDs1o6EHjKpX34BKYDSqnFerfTOJL2HvMqF70LnxpjkzDB8J44oT9pu4AMPkQwf8jl6szgvNd2tRIg== - dependencies: - pkg-dir "^4.2.0" - resolve-cwd "^3.0.0" - imurmurhash@^0.1.4: version "0.1.4" resolved "https://registry.yarnpkg.com/imurmurhash/-/imurmurhash-0.1.4.tgz#9218b9b2b928a238b13dc4fb6b6d576f231453ea" @@ -15213,23 +15308,23 @@ ini@2.0.0, ini@^2.0.0: resolved "https://registry.yarnpkg.com/ini/-/ini-2.0.0.tgz#e5fd556ecdd5726be978fa1001862eacb0a94bc5" integrity sha512-7PnF4oN3CvZF23ADhA5wRaYEQpJ8qygSkbtTXWBeXWXmEVRXK+1ITciHWwHhsjv1TmW0MgacIv6hEi5pX5NQdA== -ini@^1.3.2, ini@^1.3.4, ini@~1.3.0: +ini@^1.3.2, ini@^1.3.4, ini@^1.3.8, ini@~1.3.0: version "1.3.8" resolved "https://registry.yarnpkg.com/ini/-/ini-1.3.8.tgz#a29da425b48806f34767a4efce397269af28432c" integrity sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew== -init-package-json@3.0.2, init-package-json@^3.0.2: - version "3.0.2" - resolved "https://registry.yarnpkg.com/init-package-json/-/init-package-json-3.0.2.tgz#f5bc9bac93f2bdc005778bc2271be642fecfcd69" - integrity sha512-YhlQPEjNFqlGdzrBfDNRLhvoSgX7iQRgSxgsNknRQ9ITXFT7UMfVMWhBTOh2Y+25lRnGrv5Xz8yZwQ3ACR6T3A== +init-package-json@5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/init-package-json/-/init-package-json-5.0.0.tgz#030cf0ea9c84cfc1b0dc2e898b45d171393e4b40" + integrity sha512-kBhlSheBfYmq3e0L1ii+VKe3zBTLL5lDCDWR+f9dLmEGSB3MqLlMlsolubSsyI88Bg6EA+BIMlomAnQ1SwgQBw== dependencies: - npm-package-arg "^9.0.1" - promzard "^0.3.0" - read "^1.0.7" - read-package-json "^5.0.0" + npm-package-arg "^10.0.0" + promzard "^1.0.0" + read "^2.0.0" + read-package-json "^6.0.0" semver "^7.3.5" validate-npm-package-license "^3.0.4" - validate-npm-package-name "^4.0.0" + validate-npm-package-name "^5.0.0" injection-js@^2.2.1, injection-js@^2.4.0: version "2.4.0" @@ -15481,7 +15576,14 @@ is-callable@^1.1.3, is-callable@^1.1.4, is-callable@^1.2.7: resolved "https://registry.yarnpkg.com/is-callable/-/is-callable-1.2.7.tgz#3bc2a85ea742d9e36205dcacdd72ca1fdc51b055" integrity sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA== -is-ci@2.0.0, is-ci@^2.0.0: +is-ci@3.0.1: + version "3.0.1" + resolved "https://registry.yarnpkg.com/is-ci/-/is-ci-3.0.1.tgz#db6ecbed1bd659c43dac0f45661e7674103d1867" + integrity sha512-ZYvCgrefwqoQ6yTyYUbQu64HsITZ3NfKX1lzaEYdkTDcfKzzCI/wthRRYKkdjHKFVgNiXKAKm65Zo1pk2as/QQ== + dependencies: + ci-info "^3.2.0" + +is-ci@^2.0.0: version "2.0.0" resolved "https://registry.npmjs.org/is-ci/-/is-ci-2.0.0.tgz#6bc6334181810e04b5c22b3d589fdca55026404c" integrity sha512-YfJT7rkpQB0updsdHLGWrvhBJfcfzNNawYDNIyQXJz0IViGf75O8EBPKSdvw2rF+LGCsX4FZ8tcr3b19LcZq4w== @@ -15507,6 +15609,13 @@ is-core-module@^2.2.0, is-core-module@^2.8.1, is-core-module@^2.9.0: dependencies: has "^1.0.3" +is-core-module@^2.5.0: + version "2.12.1" + resolved "https://registry.yarnpkg.com/is-core-module/-/is-core-module-2.12.1.tgz#0c0b6885b6f80011c71541ce15c8d66cf5a4f9fd" + integrity sha512-Q4ZuBAe2FUsKtyQJoQHlvP8OvBERxO3jEmy1I7hcRXcJBGGHFh/aJBswbXuS9sgrDH2QUO8ilkwNPHvHMd8clg== + dependencies: + has "^1.0.3" + is-data-descriptor@^0.1.4: version "0.1.4" resolved "https://registry.yarnpkg.com/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz#0b5ee648388e2c860282e793f1856fec3f301b56" @@ -15731,7 +15840,7 @@ is-path-inside@^3.0.2: resolved "https://registry.yarnpkg.com/is-path-inside/-/is-path-inside-3.0.3.tgz#d231362e53a07ff2b0e0ea7fed049161ffd16283" integrity sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ== -is-plain-obj@2.1.0, is-plain-obj@^2.0.0: +is-plain-obj@2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/is-plain-obj/-/is-plain-obj-2.1.0.tgz#45e42e37fccf1f40da8e5f76ee21515840c09287" integrity sha512-YWnfyRwxL/+SsrWYfOpUtz5b3YD+nyfkHvjbcanzk8zgyO4ASD67uVMRt8k5bM4lLMDnXfriRhOpemw+NfT1eA== @@ -16088,6 +16197,15 @@ isurl@^1.0.0-alpha5: has-to-string-tag-x "^1.2.0" is-object "^1.0.1" +jackspeak@^2.0.3: + version "2.2.1" + resolved "https://registry.yarnpkg.com/jackspeak/-/jackspeak-2.2.1.tgz#655e8cf025d872c9c03d3eb63e8f0c024fef16a6" + integrity sha512-MXbxovZ/Pm42f6cDIDkl3xpwv1AGwObKwfmjs2nQePiy85tP3fatofl3FC1aBsOtP/6fq5SbtgHwWcMsLP+bDw== + dependencies: + "@isaacs/cliui" "^8.0.2" + optionalDependencies: + "@pkgjs/parseargs" "^0.11.0" + jake@^10.8.5: version "10.8.5" resolved "https://registry.yarnpkg.com/jake/-/jake-10.8.5.tgz#f2183d2c59382cb274226034543b9c03b8164c46" @@ -16180,6 +16298,16 @@ jest-config@^27.5.1: slash "^3.0.0" strip-json-comments "^3.1.1" +"jest-diff@>=29.4.3 < 30": + version "29.5.0" + resolved "https://registry.yarnpkg.com/jest-diff/-/jest-diff-29.5.0.tgz#e0d83a58eb5451dcc1fa61b1c3ee4e8f5a290d63" + integrity sha512-LtxijLLZBduXnHSniy0WMdaHjmQnt3g5sa16W4p0HqukYTTsyTW3GD1q41TyGl5YFXj/5B2U6dlh5FM1LIMgxw== + dependencies: + chalk "^4.0.0" + diff-sequences "^29.4.3" + jest-get-type "^29.4.3" + pretty-format "^29.5.0" + jest-diff@^27.5.1: version "27.5.1" resolved "https://registry.yarnpkg.com/jest-diff/-/jest-diff-27.5.1.tgz#a07f5011ac9e6643cf8a95a462b7b1ecf6680def" @@ -16238,6 +16366,11 @@ jest-get-type@^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.4.3: + version "29.4.3" + resolved "https://registry.yarnpkg.com/jest-get-type/-/jest-get-type-29.4.3.tgz#1ab7a5207c995161100b5187159ca82dd48b3dd5" + integrity sha512-J5Xez4nRRMjk8emnTpWrlkyb9pfRQQanDrvWHhsR1+VUfbwxi30eVcZFlcdGInRibU4G5LwHXpI7IRHU0CY+gg== + 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" @@ -16721,6 +16854,11 @@ json-parse-even-better-errors@^2.3.0, json-parse-even-better-errors@^2.3.1: resolved "https://registry.yarnpkg.com/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz#7c47805a94319928e05777405dc12e1f7a4ee02d" integrity sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w== +json-parse-even-better-errors@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/json-parse-even-better-errors/-/json-parse-even-better-errors-3.0.0.tgz#2cb2ee33069a78870a0c7e3da560026b89669cf7" + integrity sha512-iZbGHafX/59r39gPwVPRBGw0QQKnA7tte5pSMrhWOW7swGsVvVTjmfyAV9pNqk8YGT7tRCdxRu8uzcgZwoDooA== + json-schema-traverse@^0.4.1: version "0.4.1" resolved "https://registry.yarnpkg.com/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz#69f6a87d9513ab8bb8fe63bdb0979c448e684660" @@ -16748,11 +16886,6 @@ json-stable-stringify@^1.0.0, json-stable-stringify@^1.0.1: dependencies: jsonify "~0.0.0" -json-stringify-nice@^1.1.4: - version "1.1.4" - resolved "https://registry.yarnpkg.com/json-stringify-nice/-/json-stringify-nice-1.1.4.tgz#2c937962b80181d3f317dd39aa323e14f5a60a67" - integrity sha512-5Z5RFW63yxReJ7vANgW6eZFGWaQvnPE3WNmZoOJrSkGju2etKA2L5rrOa1sm877TVTFt57A80BH1bArcmlLfPw== - json-stringify-safe@^5.0.1, json-stringify-safe@~5.0.1: version "5.0.1" resolved "https://registry.yarnpkg.com/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz#1296a2d58fd45f19a0f6ce01d65701e2c735b6eb" @@ -16851,16 +16984,6 @@ jsprim@^1.2.2: array-includes "^3.1.2" object.assign "^4.1.2" -just-diff-apply@^5.2.0: - version "5.4.1" - resolved "https://registry.yarnpkg.com/just-diff-apply/-/just-diff-apply-5.4.1.tgz#1debed059ad009863b4db0e8d8f333d743cdd83b" - integrity sha512-AAV5Jw7tsniWwih8Ly3fXxEZ06y+6p5TwQMsw0dzZ/wPKilzyDgdAnL0Ug4NNIquPUOh1vfFWEHbmXUqM5+o8g== - -just-diff@^5.0.1: - version "5.1.1" - resolved "https://registry.yarnpkg.com/just-diff/-/just-diff-5.1.1.tgz#8da6414342a5ed6d02ccd64f5586cbbed3146202" - integrity sha512-u8HXJ3HlNrTzY7zrYYKjNEfBlyjqhdBkoyTVdjtn7p02RJD5NvR8rIClzeGA7t+UYP1/7eAkWNLU0+P3QrEqKQ== - just-extend@^4.0.2: version "4.1.1" resolved "https://registry.yarnpkg.com/just-extend/-/just-extend-4.1.1.tgz#158f1fdb01f128c411dc8b286a7b4837b3545282" @@ -17167,47 +17290,47 @@ leek@0.0.24: lodash.assign "^3.2.0" rsvp "^3.0.21" -lerna@6.5.0-alpha.2: - version "6.5.0-alpha.2" - resolved "https://registry.npmjs.org/lerna/-/lerna-6.5.0-alpha.2.tgz#d7f2f338d7f681feea606ec851795a06d008b282" - integrity sha512-RTLl8XreCn05ja47EYvs2OOz89IZrKw0/DpSkavw/wC0jDoXosjqUNNcdYaEIAYafD3w5k9We2/GtpIIyBftOQ== +lerna@7.1.1: + version "7.1.1" + resolved "https://registry.yarnpkg.com/lerna/-/lerna-7.1.1.tgz#6703062e6c4ddefdaf41e8890e9200690924fd71" + integrity sha512-rjivAl3bYu2+lWOi90vy0tYFgwBYPMiNkR/DuEWZC08wle5dsbOZ/SlXeLk9+kzbF89Bt5P6p+qF78A2tJsWPA== dependencies: - "@lerna/child-process" "6.5.0-alpha.2" - "@lerna/create" "6.5.0-alpha.2" - "@npmcli/arborist" "5.3.0" - "@npmcli/run-script" "4.1.7" - "@nrwl/devkit" ">=15.5.2 < 16" + "@lerna/child-process" "7.1.1" + "@lerna/create" "7.1.1" + "@npmcli/run-script" "6.0.2" + "@nx/devkit" ">=16.1.3 < 17" "@octokit/plugin-enterprise-rest" "6.0.1" - "@octokit/rest" "19.0.3" - byte-size "7.0.0" + "@octokit/rest" "19.0.11" + byte-size "8.1.1" chalk "4.1.0" clone-deep "4.0.1" - cmd-shim "5.0.0" + cmd-shim "6.0.1" columnify "1.6.0" - config-chain "1.1.12" - conventional-changelog-angular "5.0.12" - conventional-changelog-core "4.2.4" - conventional-recommended-bump "6.1.0" - cosmiconfig "7.0.0" + conventional-changelog-angular "6.0.0" + conventional-changelog-core "5.0.1" + conventional-recommended-bump "7.0.1" + cosmiconfig "^8.2.0" dedent "0.7.0" - dot-prop "6.0.1" - envinfo "^7.7.4" + envinfo "7.8.1" execa "5.0.0" - fs-extra "9.1.0" + fs-extra "^11.1.1" get-port "5.1.1" get-stream "6.0.0" git-url-parse "13.1.0" glob-parent "5.1.2" globby "11.1.0" - graceful-fs "4.2.10" + graceful-fs "4.2.11" has-unicode "2.0.1" - import-local "^3.0.2" - init-package-json "3.0.2" + import-local "3.1.0" + ini "^1.3.8" + init-package-json "5.0.0" inquirer "^8.2.4" - is-ci "2.0.0" + is-ci "3.0.1" is-stream "2.0.0" - libnpmaccess "6.0.3" - libnpmpublish "6.0.4" + jest-diff ">=29.4.3 < 30" + js-yaml "4.1.0" + libnpmaccess "7.0.2" + libnpmpublish "7.3.0" load-json-file "6.2.0" make-dir "3.1.0" minimatch "3.0.5" @@ -17215,36 +17338,34 @@ lerna@6.5.0-alpha.2: node-fetch "2.6.7" npm-package-arg "8.1.1" npm-packlist "5.1.1" - npm-registry-fetch "13.3.0" + npm-registry-fetch "^14.0.5" npmlog "^6.0.2" - nx ">=15.5.2 < 16" + nx ">=16.1.3 < 17" p-map "4.0.0" p-map-series "2.1.0" p-pipe "3.1.0" p-queue "6.6.2" p-reduce "2.1.0" p-waterfall "2.1.1" - pacote "13.6.1" - path-exists "4.0.0" + pacote "^15.2.0" pify "5.0.0" - read-cmd-shim "3.0.0" - read-package-json "5.0.1" + read-cmd-shim "4.0.0" + read-package-json "6.0.4" resolve-from "5.0.0" - rimraf "^3.0.2" - semver "7.3.4" + rimraf "^4.4.1" + semver "^7.3.8" signal-exit "3.0.7" slash "3.0.0" - ssri "9.0.1" + ssri "^9.0.1" strong-log-transformer "2.1.0" tar "6.1.11" temp-dir "1.0.0" - typescript "^3 || ^4" - upath "^2.0.1" - uuid "8.3.2" + typescript ">=3 < 6" + upath "2.0.1" + uuid "^9.0.0" validate-npm-package-license "3.0.4" - validate-npm-package-name "4.0.0" - write-file-atomic "4.0.1" - write-json-file "4.3.0" + validate-npm-package-name "5.0.0" + write-file-atomic "5.0.1" write-pkg "4.0.0" yargs "16.2.0" yargs-parser "20.2.4" @@ -17344,26 +17465,27 @@ levn@~0.3.0: prelude-ls "~1.1.2" type-check "~0.3.2" -libnpmaccess@6.0.3: - version "6.0.3" - resolved "https://registry.npmjs.org/libnpmaccess/-/libnpmaccess-6.0.3.tgz#473cc3e4aadb2bc713419d92e45d23b070d8cded" - integrity sha512-4tkfUZprwvih2VUZYMozL7EMKgQ5q9VW2NtRyxWtQWlkLTAWHRklcAvBN49CVqEkhUw7vTX2fNgB5LzgUucgYg== - dependencies: - aproba "^2.0.0" - minipass "^3.1.1" - npm-package-arg "^9.0.1" - npm-registry-fetch "^13.0.0" - -libnpmpublish@6.0.4: - version "6.0.4" - resolved "https://registry.npmjs.org/libnpmpublish/-/libnpmpublish-6.0.4.tgz#adb41ec6b0c307d6f603746a4d929dcefb8f1a0b" - integrity sha512-lvAEYW8mB8QblL6Q/PI/wMzKNvIrF7Kpujf/4fGS/32a2i3jzUXi04TNyIBcK6dQJ34IgywfaKGh+Jq4HYPFmg== - dependencies: - normalize-package-data "^4.0.0" - npm-package-arg "^9.0.1" - npm-registry-fetch "^13.0.0" +libnpmaccess@7.0.2: + version "7.0.2" + resolved "https://registry.yarnpkg.com/libnpmaccess/-/libnpmaccess-7.0.2.tgz#7f056c8c933dd9c8ba771fa6493556b53c5aac52" + integrity sha512-vHBVMw1JFMTgEk15zRsJuSAg7QtGGHpUSEfnbcRL1/gTBag9iEfJbyjpDmdJmwMhvpoLoNBtdAUCdGnaP32hhw== + dependencies: + npm-package-arg "^10.1.0" + npm-registry-fetch "^14.0.3" + +libnpmpublish@7.3.0: + version "7.3.0" + resolved "https://registry.yarnpkg.com/libnpmpublish/-/libnpmpublish-7.3.0.tgz#2ceb2b36866d75a6cd7b4aa748808169f4d17e37" + integrity sha512-fHUxw5VJhZCNSls0KLNEG0mCD2PN1i14gH5elGOgiVnU3VgTcRahagYP2LKI1m0tFCJ+XrAm0zVYyF5RCbXzcg== + dependencies: + ci-info "^3.6.1" + normalize-package-data "^5.0.0" + npm-package-arg "^10.1.0" + npm-registry-fetch "^14.0.3" + proc-log "^3.0.0" semver "^7.3.7" - ssri "^9.0.0" + sigstore "^1.4.0" + ssri "^10.0.1" license-webpack-plugin@2.3.0: version "2.3.0" @@ -17929,6 +18051,11 @@ lru-cache@^9.0.0: resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-9.0.1.tgz#ac061ed291f8b9adaca2b085534bb1d3b61bef83" integrity sha512-C8QsKIN1UIXeOs3iWmiZ1lQY+EnKDojWd37fXy1aSbJvH4iSma1uy2OWuoB3m4SYRli5+CUjDv3Dij5DVoetmg== +"lru-cache@^9.1.1 || ^10.0.0": + version "10.0.0" + resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-10.0.0.tgz#b9e2a6a72a129d81ab317202d93c7691df727e61" + integrity sha512-svTf/fzsKHffP42sujkO/Rjs37BCIsQVRCeNYIm9WN8rgT7ffoUnRtZCqU+6BqcSBdv8gwJeTz8knJpgACeQMw== + lru_map@^0.3.3: version "0.3.3" resolved "https://registry.yarnpkg.com/lru_map/-/lru_map-0.3.3.tgz#b5c8351b9464cbd750335a79650a0ec0e56118dd" @@ -18036,7 +18163,7 @@ make-error@1.x, make-error@^1.1.1: resolved "https://registry.yarnpkg.com/make-error/-/make-error-1.3.6.tgz#2eb2e37ea9b67c4891f684a1394799af484cf7a2" integrity sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw== -make-fetch-happen@^10.0.3, make-fetch-happen@^10.0.6: +make-fetch-happen@^10.0.3: version "10.2.1" resolved "https://registry.yarnpkg.com/make-fetch-happen/-/make-fetch-happen-10.2.1.tgz#f5e3835c5e9817b617f2770870d9492d28678164" integrity sha512-NgOPbRiaQM10DYXvN3/hhGVI2M5MtITFryzBGxHM5p4wnFxsVCbxkrBrDsk+EZ5OB4jEOT7AjDxtdF+KVEFT7w== @@ -18058,6 +18185,27 @@ make-fetch-happen@^10.0.3, make-fetch-happen@^10.0.6: socks-proxy-agent "^7.0.0" ssri "^9.0.0" +make-fetch-happen@^11.0.0, make-fetch-happen@^11.0.1, make-fetch-happen@^11.1.1: + version "11.1.1" + resolved "https://registry.yarnpkg.com/make-fetch-happen/-/make-fetch-happen-11.1.1.tgz#85ceb98079584a9523d4bf71d32996e7e208549f" + integrity sha512-rLWS7GCSTcEujjVBs2YqG7Y4643u8ucvCJeSRqiLYhesrDuzeuFIk37xREzAsfQaqzl8b9rNCE4m6J8tvX4Q8w== + dependencies: + agentkeepalive "^4.2.1" + cacache "^17.0.0" + http-cache-semantics "^4.1.1" + http-proxy-agent "^5.0.0" + https-proxy-agent "^5.0.0" + is-lambda "^1.0.1" + lru-cache "^7.7.1" + minipass "^5.0.0" + minipass-fetch "^3.0.0" + minipass-flush "^1.0.5" + minipass-pipeline "^1.2.4" + negotiator "^0.6.3" + promise-retry "^2.0.1" + socks-proxy-agent "^7.0.0" + ssri "^10.0.0" + make-fetch-happen@^5.0.0: version "5.0.2" resolved "https://registry.yarnpkg.com/make-fetch-happen/-/make-fetch-happen-5.0.2.tgz#aa8387104f2687edca01c8687ee45013d02d19bd" @@ -18294,7 +18442,7 @@ meow@^3.3.0: redent "^1.0.0" trim-newlines "^1.0.0" -meow@^8.0.0: +meow@^8.1.2: version "8.1.2" resolved "https://registry.yarnpkg.com/meow/-/meow-8.1.2.tgz#bcbe45bda0ee1729d350c03cffc8395a36c4e897" integrity sha512-r85E3NdZ+mpYk1C6RjPFEMSE+s1iZMuHtsHAqY0DT3jZczl0diWUZ8g6oU7h0M9cD2EL+PzaYghhCLzR0ZNn5Q== @@ -18507,6 +18655,20 @@ minimatch@^7.4.1: dependencies: brace-expansion "^2.0.1" +minimatch@^8.0.2: + version "8.0.4" + resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-8.0.4.tgz#847c1b25c014d4e9a7f68aaf63dedd668a626229" + integrity sha512-W0Wvr9HyFXZRGIDgCicunpQ299OKXs9RgZfaukz4qAW/pJhcpUfupc9c+OObPOFueNy8VSrZgEmDtk6Kh4WzDA== + dependencies: + brace-expansion "^2.0.1" + +minimatch@^9.0.0, minimatch@^9.0.1: + version "9.0.2" + resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-9.0.2.tgz#397e387fff22f6795844d00badc903a3d5de7057" + integrity sha512-PZOT9g5v2ojiTL7r1xF6plNHLtOeTpSlDI007As2NlA2aYBMfVom17yqa6QzhmDP8QOhn7LjHTg7DFCVSSa6yg== + dependencies: + brace-expansion "^2.0.1" + minimatch@~3.0.4: version "3.0.8" resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.0.8.tgz#5e6a59bd11e2ab0de1cfb843eb2d82e546c321c1" @@ -18567,6 +18729,17 @@ minipass-fetch@^2.0.3: optionalDependencies: encoding "^0.1.13" +minipass-fetch@^3.0.0: + version "3.0.3" + resolved "https://registry.yarnpkg.com/minipass-fetch/-/minipass-fetch-3.0.3.tgz#d9df70085609864331b533c960fd4ffaa78d15ce" + integrity sha512-n5ITsTkDqYkYJZjcRWzZt9qnZKCT7nKCosJhHoj7S7zD+BP4jVbWs+odsniw5TA3E0sLomhTKOKjF86wf11PuQ== + dependencies: + minipass "^5.0.0" + minipass-sized "^1.0.3" + minizlib "^2.1.2" + optionalDependencies: + encoding "^0.1.13" + minipass-flush@^1.0.5: version "1.0.5" resolved "https://registry.yarnpkg.com/minipass-flush/-/minipass-flush-1.0.5.tgz#82e7135d7e89a50ffe64610a787953c4c4cbb373" @@ -18626,6 +18799,11 @@ minipass@^5.0.0: resolved "https://registry.yarnpkg.com/minipass/-/minipass-5.0.0.tgz#3e9788ffb90b694a5d0ec94479a45b5d8738133d" integrity sha512-3FnjYuehv9k6ovOEbyOswadCDPX1piCfhV8ncmYtHOjuPwylVWsghTLo7rabjC3Rx5xD4HDx8Wm1xnMF7S5qFQ== +"minipass@^5.0.0 || ^6.0.2": + version "6.0.2" + resolved "https://registry.yarnpkg.com/minipass/-/minipass-6.0.2.tgz#542844b6c4ce95b202c0995b0a471f1229de4c81" + integrity sha512-MzWSV5nYVT7mVyWCwn2o7JH13w2TBRmmSqSRCKzTw+lmft9X4z+3wjvs06Tzijo5z4W/kahUCDpRXTF+ZrmF/w== + minizlib@^1.3.3: version "1.3.3" resolved "https://registry.yarnpkg.com/minizlib/-/minizlib-1.3.3.tgz#2290de96818a34c29551c8a8d301216bd65a861d" @@ -18670,15 +18848,6 @@ mixin-deep@^1.2.0: for-in "^1.0.2" is-extendable "^1.0.1" -mkdirp-infer-owner@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/mkdirp-infer-owner/-/mkdirp-infer-owner-2.0.0.tgz#55d3b368e7d89065c38f32fd38e638f0ab61d316" - integrity sha512-sdqtiFt3lkOaYvTXSRIUjkIdPTcxgv5+fgqYE/5qgwdw12cOrAuzzgzvVExIkH/ul1oeHN3bCLOWSG3XOqbKKw== - dependencies: - chownr "^2.0.0" - infer-owner "^1.0.4" - mkdirp "^1.0.3" - mkdirp@0.5.4: version "0.5.4" resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-0.5.4.tgz#fd01504a6797ec5c9be81ff43d204961ed64a512" @@ -18742,7 +18911,7 @@ mocha@^6.1.4, mocha@^6.2.0: yargs-parser "13.1.2" yargs-unparser "1.6.0" -modify-values@^1.0.0: +modify-values@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/modify-values/-/modify-values-1.0.1.tgz#b3939fa605546474e3e3e3c63d64bd43b4ee6022" integrity sha512-xV2bxeN6F7oYjZWTe/YPAy6MN2M+sL4u/Rlm2AHCIVGfo2p1yGmBHQ6vHehl4bRTZBdHu3TSkWdYgkwpYzAGSw== @@ -18908,11 +19077,16 @@ mute-stream@0.0.7: resolved "https://registry.yarnpkg.com/mute-stream/-/mute-stream-0.0.7.tgz#3075ce93bc21b8fab43e1bc4da7e8115ed1e7bab" integrity sha1-MHXOk7whuPq0PhvE2n6BFe0ee6s= -mute-stream@0.0.8, mute-stream@~0.0.4: +mute-stream@0.0.8: version "0.0.8" resolved "https://registry.yarnpkg.com/mute-stream/-/mute-stream-0.0.8.tgz#1630c42b2251ff81e2a283de96a5497ea92e5e0d" integrity sha512-nnbWWOkoWyUsTjKrhgD0dcz22mdkSnpYqbEjIm2nhwhuxlSkpywJmBo8h0ZqJdkp73mb90SssHkN4rsRaBAfAA== +mute-stream@~1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/mute-stream/-/mute-stream-1.0.0.tgz#e31bd9fe62f0aed23520aa4324ea6671531e013e" + integrity sha512-avsJQhyd+680gKXyG/sQc0nXaC6rBkPOfyHYcFb9+hdkqQkR9bdnkJ0AMZhke0oesPqIO+mFFJ+IdBc7mst4IA== + mysql@^2.18.1: version "2.18.1" resolved "https://registry.yarnpkg.com/mysql/-/mysql-2.18.1.tgz#2254143855c5a8c73825e4522baf2ea021766717" @@ -19451,12 +19625,22 @@ normalize-package-data@^3.0.0: semver "^7.3.4" validate-npm-package-license "^3.0.1" -normalize-package-data@^4.0.0: - version "4.0.1" - resolved "https://registry.yarnpkg.com/normalize-package-data/-/normalize-package-data-4.0.1.tgz#b46b24e0616d06cadf9d5718b29b6d445a82a62c" - integrity sha512-EBk5QKKuocMJhB3BILuKhmaPjI8vNRSpIfO9woLC6NyHVkKKdVEdAO1mrT0ZfxNR1lKwCcTkuZfmGIFdizZ8Pg== +normalize-package-data@^3.0.3: + version "3.0.3" + resolved "https://registry.yarnpkg.com/normalize-package-data/-/normalize-package-data-3.0.3.tgz#dbcc3e2da59509a0983422884cd172eefdfa525e" + integrity sha512-p2W1sgqij3zMMyRC067Dg16bfzVH+w7hyegmpIvZ4JNjqtGOVAIvLmjBx3yP7YTe9vKJgkoNOPjwQGogDoMXFA== dependencies: - hosted-git-info "^5.0.0" + hosted-git-info "^4.0.1" + is-core-module "^2.5.0" + semver "^7.3.4" + validate-npm-package-license "^3.0.1" + +normalize-package-data@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/normalize-package-data/-/normalize-package-data-5.0.0.tgz#abcb8d7e724c40d88462b84982f7cbf6859b4588" + integrity sha512-h9iPVIfrVZ9wVYQnxFgtw1ugSvGEMOlyPWWtm8BMJhnwyEL/FLbYbTY3V3PpjI/BUK67n9PEWDu6eHzu1fB15Q== + dependencies: + hosted-git-info "^6.0.0" is-core-module "^2.8.1" semver "^7.3.5" validate-npm-package-license "^3.0.4" @@ -19526,12 +19710,12 @@ npm-bundled@^1.1.2: dependencies: npm-normalize-package-bin "^1.0.1" -npm-bundled@^2.0.0: - version "2.0.1" - resolved "https://registry.yarnpkg.com/npm-bundled/-/npm-bundled-2.0.1.tgz#94113f7eb342cd7a67de1e789f896b04d2c600f4" - integrity sha512-gZLxXdjEzE/+mOstGDqR6b0EkhJ+kM6fxM6vUuckuctuVPh80Q6pw/rSZj9s4Gex9GxWtIicO1pc8DB9KZWudw== +npm-bundled@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/npm-bundled/-/npm-bundled-3.0.0.tgz#7e8e2f8bb26b794265028491be60321a25a39db7" + integrity sha512-Vq0eyEQy+elFpzsKjMss9kxqb9tG3YHg4dsyWuUENuzvSUWe1TCnW/vV9FkhvBk/brEDoDiVd+M1Btosa6ImdQ== dependencies: - npm-normalize-package-bin "^2.0.0" + npm-normalize-package-bin "^3.0.0" npm-install-checks@^4.0.0: version "4.0.0" @@ -19540,10 +19724,10 @@ npm-install-checks@^4.0.0: dependencies: semver "^7.1.1" -npm-install-checks@^5.0.0: - version "5.0.0" - resolved "https://registry.yarnpkg.com/npm-install-checks/-/npm-install-checks-5.0.0.tgz#5ff27d209a4e3542b8ac6b0c1db6063506248234" - integrity sha512-65lUsMI8ztHCxFz5ckCEC44DRvEGdZX5usQFriauxHEwt7upv1FKaQEmAtU0YnOAdwuNWCmk64xYiQABNrEyLA== +npm-install-checks@^6.0.0: + version "6.1.1" + resolved "https://registry.yarnpkg.com/npm-install-checks/-/npm-install-checks-6.1.1.tgz#b459b621634d06546664207fde16810815808db1" + integrity sha512-dH3GmQL4vsPtld59cOn8uY0iOqRmqKvV+DLGwNXV/Q7MDgD2QfOADWd/mFXcIE5LVhYYGjA3baz6W9JneqnuCw== dependencies: semver "^7.1.1" @@ -19552,10 +19736,10 @@ npm-normalize-package-bin@^1.0.0, npm-normalize-package-bin@^1.0.1: resolved "https://registry.yarnpkg.com/npm-normalize-package-bin/-/npm-normalize-package-bin-1.0.1.tgz#6e79a41f23fd235c0623218228da7d9c23b8f6e2" integrity sha512-EPfafl6JL5/rU+ot6P3gRSCpPDW5VmIzX959Ob1+ySFUuuYHWHekXpwdUZcKP5C+DS4GEtdJluwBjnsNDl+fSA== -npm-normalize-package-bin@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/npm-normalize-package-bin/-/npm-normalize-package-bin-2.0.0.tgz#9447a1adaaf89d8ad0abe24c6c84ad614a675fff" - integrity sha512-awzfKUO7v0FscrSpRoogyNm0sajikhBWpU0QMrW09AMi9n1PoKU6WaIqUzuJSQnpciZZmJ/jMZ2Egfmb/9LiWQ== +npm-normalize-package-bin@^3.0.0: + version "3.0.1" + resolved "https://registry.yarnpkg.com/npm-normalize-package-bin/-/npm-normalize-package-bin-3.0.1.tgz#25447e32a9a7de1f51362c61a559233b89947832" + integrity sha512-dMxCf+zZ+3zeQZXKxmyuCKlIDPGuv8EF940xbkC4kQVDTtqoh6rJFO+JTKSA6/Rwi0getWmtuy4Itup0AMcaDQ== npm-package-arg@8.0.1: version "8.0.1" @@ -19584,6 +19768,16 @@ npm-package-arg@8.1.5, npm-package-arg@^8.0.0, npm-package-arg@^8.0.1, npm-packa semver "^7.3.4" validate-npm-package-name "^3.0.0" +npm-package-arg@^10.0.0, npm-package-arg@^10.1.0: + version "10.1.0" + resolved "https://registry.yarnpkg.com/npm-package-arg/-/npm-package-arg-10.1.0.tgz#827d1260a683806685d17193073cc152d3c7e9b1" + integrity sha512-uFyyCEmgBfZTtrKk/5xDfHp6+MdrqGotX/VoOyEEl3mBwiEE5FlBaePanazJSVMPT7vKepcjYBY2ztg9A3yPIA== + dependencies: + hosted-git-info "^6.0.0" + proc-log "^3.0.0" + semver "^7.3.5" + validate-npm-package-name "^5.0.0" + npm-package-arg@^6.0.0, npm-package-arg@^6.1.0: version "6.1.1" resolved "https://registry.yarnpkg.com/npm-package-arg/-/npm-package-arg-6.1.1.tgz#02168cb0a49a2b75bf988a28698de7b529df5cb7" @@ -19594,7 +19788,7 @@ npm-package-arg@^6.0.0, npm-package-arg@^6.1.0: semver "^5.6.0" validate-npm-package-name "^3.0.0" -npm-package-arg@^9.0.0, npm-package-arg@^9.0.1, npm-package-arg@^9.1.0: +npm-package-arg@^9.1.0: version "9.1.2" resolved "https://registry.yarnpkg.com/npm-package-arg/-/npm-package-arg-9.1.2.tgz#fc8acecb00235f42270dda446f36926ddd9ac2bc" integrity sha512-pzd9rLEx4TfNJkovvlBSLGhq31gGu2QDexFPWT19yCDh0JgnRhlBLNo5759N0AJmBk+kQ9Y/hXoLnlgFD+ukmg== @@ -19653,15 +19847,12 @@ npm-packlist@^3.0.0: npm-bundled "^1.1.1" npm-normalize-package-bin "^1.0.1" -npm-packlist@^5.1.0: - version "5.1.3" - resolved "https://registry.yarnpkg.com/npm-packlist/-/npm-packlist-5.1.3.tgz#69d253e6fd664b9058b85005905012e00e69274b" - integrity sha512-263/0NGrn32YFYi4J533qzrQ/krmmrWwhKkzwTuM4f/07ug51odoaNjUexxO4vxlzURHcmYMH1QjvHjsNDKLVg== +npm-packlist@^7.0.0: + version "7.0.4" + resolved "https://registry.yarnpkg.com/npm-packlist/-/npm-packlist-7.0.4.tgz#033bf74110eb74daf2910dc75144411999c5ff32" + integrity sha512-d6RGEuRrNS5/N84iglPivjaJPxhDbZmlbTwTDX2IbcRHG5bZCdtysYMhwiPvcF4GisXHGn7xsxv+GQ7T/02M5Q== dependencies: - glob "^8.0.1" - ignore-walk "^5.0.1" - npm-bundled "^2.0.0" - npm-normalize-package-bin "^2.0.0" + ignore-walk "^6.0.0" npm-pick-manifest@6.1.0: version "6.1.0" @@ -19691,29 +19882,16 @@ npm-pick-manifest@^3.0.0: npm-package-arg "^6.0.0" semver "^5.4.1" -npm-pick-manifest@^7.0.0: - version "7.0.2" - resolved "https://registry.yarnpkg.com/npm-pick-manifest/-/npm-pick-manifest-7.0.2.tgz#1d372b4e7ea7c6712316c0e99388a73ed3496e84" - integrity sha512-gk37SyRmlIjvTfcYl6RzDbSmS9Y4TOBXfsPnoYqTHARNgWbyDiCSMLUpmALDj4jjcTZpURiEfsSHJj9k7EV4Rw== +npm-pick-manifest@^8.0.0: + version "8.0.1" + resolved "https://registry.yarnpkg.com/npm-pick-manifest/-/npm-pick-manifest-8.0.1.tgz#c6acd97d1ad4c5dbb80eac7b386b03ffeb289e5f" + integrity sha512-mRtvlBjTsJvfCCdmPtiu2bdlx8d/KXtF7yNXNWe7G0Z36qWA9Ny5zXsI2PfBZEv7SXgoxTmNaTzGSbbzDZChoA== dependencies: - npm-install-checks "^5.0.0" - npm-normalize-package-bin "^2.0.0" - npm-package-arg "^9.0.0" + npm-install-checks "^6.0.0" + npm-normalize-package-bin "^3.0.0" + npm-package-arg "^10.0.0" semver "^7.3.5" -npm-registry-fetch@13.3.0: - version "13.3.0" - resolved "https://registry.npmjs.org/npm-registry-fetch/-/npm-registry-fetch-13.3.0.tgz#0ce10fa4a699a1e70685ecf41bbfb4150d74231b" - integrity sha512-10LJQ/1+VhKrZjIuY9I/+gQTvumqqlgnsCufoXETHAPFTS3+M+Z5CFhZRDHGavmJ6rOye3UvNga88vl8n1r6gg== - dependencies: - make-fetch-happen "^10.0.6" - minipass "^3.1.6" - minipass-fetch "^2.0.3" - minipass-json-stream "^1.0.1" - minizlib "^2.1.2" - npm-package-arg "^9.0.1" - proc-log "^2.0.0" - npm-registry-fetch@^11.0.0: version "11.0.0" resolved "https://registry.yarnpkg.com/npm-registry-fetch/-/npm-registry-fetch-11.0.0.tgz#68c1bb810c46542760d62a6a965f85a702d43a76" @@ -19726,18 +19904,18 @@ npm-registry-fetch@^11.0.0: minizlib "^2.0.0" npm-package-arg "^8.0.0" -npm-registry-fetch@^13.0.0, npm-registry-fetch@^13.0.1: - version "13.3.1" - resolved "https://registry.yarnpkg.com/npm-registry-fetch/-/npm-registry-fetch-13.3.1.tgz#bb078b5fa6c52774116ae501ba1af2a33166af7e" - integrity sha512-eukJPi++DKRTjSBRcDZSDDsGqRK3ehbxfFUcgaRd0Yp6kRwOwh2WVn0r+8rMB4nnuzvAk6rQVzl6K5CkYOmnvw== +npm-registry-fetch@^14.0.0, npm-registry-fetch@^14.0.3, npm-registry-fetch@^14.0.5: + version "14.0.5" + resolved "https://registry.yarnpkg.com/npm-registry-fetch/-/npm-registry-fetch-14.0.5.tgz#fe7169957ba4986a4853a650278ee02e568d115d" + integrity sha512-kIDMIo4aBm6xg7jOttupWZamsZRkAqMqwqqbVXnUqstY5+tapvv6bkH/qMR76jdgV+YljEUCyWx3hRYMrJiAgA== dependencies: - make-fetch-happen "^10.0.6" - minipass "^3.1.6" - minipass-fetch "^2.0.3" + make-fetch-happen "^11.0.0" + minipass "^5.0.0" + minipass-fetch "^3.0.0" minipass-json-stream "^1.0.1" minizlib "^2.1.2" - npm-package-arg "^9.0.1" - proc-log "^2.0.0" + npm-package-arg "^10.0.0" + proc-log "^3.0.0" npm-registry-fetch@^4.0.0: version "4.0.7" @@ -19842,16 +20020,15 @@ nwsapi@^2.2.0: resolved "https://registry.yarnpkg.com/nwsapi/-/nwsapi-2.2.0.tgz#204879a9e3d068ff2a55139c2c772780681a38b7" integrity sha512-h2AatdwYH+JHiZpv7pt/gSX1XoRGb7L/qSIeuqA6GwYoF9w1vP1cw42TO0aI2pNyshRK5893hNSl+1//vHK7hQ== -nx@15.6.3, "nx@>=15.5.2 < 16": - version "15.6.3" - resolved "https://registry.npmjs.org/nx/-/nx-15.6.3.tgz#900087bce38c6e5975660c23ebd41ead1bf54f98" - integrity sha512-3t0A0GPLNen1yPAyE+VGZ3nkAzZYb5nfXtAcx8SHBlKq4u42yBY3khBmP1y4Og3jhIwFIj7J7Npeh8ZKrthmYQ== +nx@16.4.1, "nx@>=16.1.3 < 17": + version "16.4.1" + resolved "https://registry.yarnpkg.com/nx/-/nx-16.4.1.tgz#3a8a58f78d6fbf2264639d401278ee16140d0b11" + integrity sha512-om1v2xu+e4/ibQ4PgnFfemwnfS4e9Biss3R0lx1d8GmaVRIJ/o4Ng0c6F5Uw9l/WMc0DyAnGBthKyrVAlHPs1A== dependencies: - "@nrwl/cli" "15.6.3" - "@nrwl/tao" "15.6.3" + "@nrwl/tao" "16.4.1" "@parcel/watcher" "2.0.4" "@yarnpkg/lockfile" "^1.1.0" - "@yarnpkg/parsers" "^3.0.0-rc.18" + "@yarnpkg/parsers" "3.0.0-rc.46" "@zkochan/js-yaml" "0.0.6" axios "^1.0.0" chalk "^4.1.0" @@ -19872,7 +20049,7 @@ nx@15.6.3, "nx@>=15.5.2 < 16": minimatch "3.0.5" npm-run-path "^4.0.1" open "^8.4.0" - semver "7.3.4" + semver "7.5.3" string-width "^4.2.3" strong-log-transformer "^2.1.0" tar-stream "~2.2.0" @@ -19882,6 +20059,17 @@ nx@15.6.3, "nx@>=15.5.2 < 16": v8-compile-cache "2.3.0" yargs "^17.6.2" yargs-parser "21.1.1" + optionalDependencies: + "@nx/nx-darwin-arm64" "16.4.1" + "@nx/nx-darwin-x64" "16.4.1" + "@nx/nx-freebsd-x64" "16.4.1" + "@nx/nx-linux-arm-gnueabihf" "16.4.1" + "@nx/nx-linux-arm64-gnu" "16.4.1" + "@nx/nx-linux-arm64-musl" "16.4.1" + "@nx/nx-linux-x64-gnu" "16.4.1" + "@nx/nx-linux-x64-musl" "16.4.1" + "@nx/nx-win32-arm64-msvc" "16.4.1" + "@nx/nx-win32-x64-msvc" "16.4.1" oauth-sign@~0.9.0: version "0.9.0" @@ -20431,33 +20619,6 @@ pacote@12.0.2: ssri "^8.0.1" tar "^6.1.0" -pacote@13.6.1: - version "13.6.1" - resolved "https://registry.npmjs.org/pacote/-/pacote-13.6.1.tgz#ac6cbd9032b4c16e5c1e0c60138dfe44e4cc589d" - integrity sha512-L+2BI1ougAPsFjXRyBhcKmfT016NscRFLv6Pz5EiNf1CCFJFU0pSKKQwsZTyAQB+sTuUL4TyFyp6J1Ork3dOqw== - dependencies: - "@npmcli/git" "^3.0.0" - "@npmcli/installed-package-contents" "^1.0.7" - "@npmcli/promise-spawn" "^3.0.0" - "@npmcli/run-script" "^4.1.0" - cacache "^16.0.0" - chownr "^2.0.0" - fs-minipass "^2.1.0" - infer-owner "^1.0.4" - minipass "^3.1.6" - mkdirp "^1.0.4" - npm-package-arg "^9.0.0" - npm-packlist "^5.1.0" - npm-pick-manifest "^7.0.0" - npm-registry-fetch "^13.0.1" - proc-log "^2.0.0" - promise-retry "^2.0.1" - read-package-json "^5.0.0" - read-package-json-fast "^2.0.3" - rimraf "^3.0.2" - ssri "^9.0.0" - tar "^6.1.11" - pacote@9.5.12: version "9.5.12" resolved "https://registry.yarnpkg.com/pacote/-/pacote-9.5.12.tgz#1e11dd7a8d736bcc36b375a9804d41bb0377bf66" @@ -20494,31 +20655,28 @@ pacote@9.5.12: unique-filename "^1.1.1" which "^1.3.1" -pacote@^13.0.3, pacote@^13.6.1: - version "13.6.2" - resolved "https://registry.yarnpkg.com/pacote/-/pacote-13.6.2.tgz#0d444ba3618ab3e5cd330b451c22967bbd0ca48a" - integrity sha512-Gu8fU3GsvOPkak2CkbojR7vjs3k3P9cA6uazKTHdsdV0gpCEQq2opelnEv30KRQWgVzP5Vd/5umjcedma3MKtg== - dependencies: - "@npmcli/git" "^3.0.0" - "@npmcli/installed-package-contents" "^1.0.7" - "@npmcli/promise-spawn" "^3.0.0" - "@npmcli/run-script" "^4.1.0" - cacache "^16.0.0" - chownr "^2.0.0" - fs-minipass "^2.1.0" - infer-owner "^1.0.4" - minipass "^3.1.6" - mkdirp "^1.0.4" - npm-package-arg "^9.0.0" - npm-packlist "^5.1.0" - npm-pick-manifest "^7.0.0" - npm-registry-fetch "^13.0.1" - proc-log "^2.0.0" +pacote@^15.2.0: + version "15.2.0" + resolved "https://registry.yarnpkg.com/pacote/-/pacote-15.2.0.tgz#0f0dfcc3e60c7b39121b2ac612bf8596e95344d3" + integrity sha512-rJVZeIwHTUta23sIZgEIM62WYwbmGbThdbnkt81ravBplQv+HjyroqnLRNH2+sLJHcGZmLRmhPwACqhfTcOmnA== + dependencies: + "@npmcli/git" "^4.0.0" + "@npmcli/installed-package-contents" "^2.0.1" + "@npmcli/promise-spawn" "^6.0.1" + "@npmcli/run-script" "^6.0.0" + cacache "^17.0.0" + fs-minipass "^3.0.0" + minipass "^5.0.0" + npm-package-arg "^10.0.0" + npm-packlist "^7.0.0" + npm-pick-manifest "^8.0.0" + npm-registry-fetch "^14.0.0" + proc-log "^3.0.0" promise-retry "^2.0.1" - read-package-json "^5.0.0" - read-package-json-fast "^2.0.3" - rimraf "^3.0.2" - ssri "^9.0.0" + read-package-json "^6.0.0" + read-package-json-fast "^3.0.0" + sigstore "^1.3.0" + ssri "^10.0.0" tar "^6.1.11" pad@^3.2.0: @@ -20573,15 +20731,6 @@ parse-asn1@^5.0.0, parse-asn1@^5.1.5: pbkdf2 "^3.0.3" safe-buffer "^5.1.1" -parse-conflict-json@^2.0.1: - version "2.0.2" - resolved "https://registry.yarnpkg.com/parse-conflict-json/-/parse-conflict-json-2.0.2.tgz#3d05bc8ffe07d39600dc6436c6aefe382033d323" - integrity sha512-jDbRGb00TAPFsKWCpZZOT93SxVP9nONOSgES3AevqRq/CHvavEBvKAjxX9p5Y5F0RZLxH9Ufd9+RwtCsa+lFDA== - dependencies: - json-parse-even-better-errors "^2.3.1" - just-diff "^5.0.1" - just-diff-apply "^5.2.0" - parse-json@^2.2.0: version "2.2.0" resolved "https://registry.yarnpkg.com/parse-json/-/parse-json-2.2.0.tgz#f480f40434ef80741f8469099f8dea18f55a4dc9" @@ -20701,11 +20850,6 @@ path-dirname@^1.0.0: resolved "https://registry.yarnpkg.com/path-dirname/-/path-dirname-1.0.2.tgz#cc33d24d525e099a5388c0336c6e32b9160609e0" integrity sha1-zDPSTVJeCZpTiMAzbG4yuRYGCeA= -path-exists@4.0.0, path-exists@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/path-exists/-/path-exists-4.0.0.tgz#513bdbe2d3b95d7762e8c1137efa195c6c61b5b3" - integrity sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w== - path-exists@^2.0.0: version "2.1.0" resolved "https://registry.yarnpkg.com/path-exists/-/path-exists-2.1.0.tgz#0feb6c64f0fc518d9a754dd5efb62c7022761f4b" @@ -20718,6 +20862,11 @@ path-exists@^3.0.0: resolved "https://registry.yarnpkg.com/path-exists/-/path-exists-3.0.0.tgz#ce0ebeaa5f78cb18925ea7d810d7b59b010fd515" integrity sha1-zg6+ql94yxiSXqfYENe1mwEP1RU= +path-exists@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/path-exists/-/path-exists-4.0.0.tgz#513bdbe2d3b95d7762e8c1137efa195c6c61b5b3" + integrity sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w== + path-exists@^5.0.0: version "5.0.0" resolved "https://registry.yarnpkg.com/path-exists/-/path-exists-5.0.0.tgz#a6aad9489200b21fab31e49cf09277e5116fb9e7" @@ -20765,6 +20914,14 @@ path-root@^0.1.1: dependencies: path-root-regex "^0.1.0" +path-scurry@^1.10.0: + version "1.10.0" + resolved "https://registry.yarnpkg.com/path-scurry/-/path-scurry-1.10.0.tgz#0ffbd4c1f7de9600f98a1405507d9f9acb438ab3" + integrity sha512-tZFEaRQbMLjwrsmidsGJ6wDMv0iazJWk6SfIKnY4Xru8auXgmJkOBa5DUbYFcFD2Rzk2+KDlIiF0GVXNCbgC7g== + dependencies: + lru-cache "^9.1.1 || ^10.0.0" + minipass "^5.0.0 || ^6.0.2" + path-scurry@^1.6.1: version "1.6.4" resolved "https://registry.yarnpkg.com/path-scurry/-/path-scurry-1.6.4.tgz#020a9449e5382a4acb684f9c7e1283bc5695de66" @@ -20927,7 +21084,7 @@ pidtree@^0.3.0: resolved "https://registry.yarnpkg.com/pidtree/-/pidtree-0.3.1.tgz#ef09ac2cc0533df1f3250ccf2c4d366b0d12114a" integrity sha512-qQbW94hLHEqCg7nhby4yRC7G2+jYHY4Rguc2bjw7Uug4GIJuu1tvf2uHaZv5Q8zdt+WKJ6qK1FOI6amaWUo5FA== -pify@5.0.0, pify@^5.0.0: +pify@5.0.0: version "5.0.0" resolved "https://registry.yarnpkg.com/pify/-/pify-5.0.0.tgz#1f5eca3f5e87ebec28cc6d54a0e4aaf00acc127f" integrity sha512-eW/gHNMlxdSP6dmG6uJip6FXN0EQBwm2clYYd8Wul42Cwu/DK8HEftzsapcNdYe2MfLiIwZqsDk2RDEsTE79hA== @@ -22158,6 +22315,15 @@ pretty-format@^27.0.0, pretty-format@^27.0.2, pretty-format@^27.5.1: ansi-styles "^5.0.0" react-is "^17.0.1" +pretty-format@^29.5.0: + version "29.5.0" + resolved "https://registry.yarnpkg.com/pretty-format/-/pretty-format-29.5.0.tgz#283134e74f70e2e3e7229336de0e4fce94ccde5a" + integrity sha512-V2mGkI31qdttvTFX7Mt4efOqHXqJWMu4/r66Xh3Z3BwZaPfPJgp6/gbwoujRpPUtfEF6AUUWx3Jim3GCw5g/Qw== + dependencies: + "@jest/schemas" "^29.4.3" + ansi-styles "^5.0.0" + react-is "^18.0.0" + pretty-ms@^7.0.0: version "7.0.1" resolved "https://registry.yarnpkg.com/pretty-ms/-/pretty-ms-7.0.1.tgz#7d903eaab281f7d8e03c66f867e239dc32fb73e8" @@ -22175,11 +22341,16 @@ private@^0.1.6, private@^0.1.8: resolved "https://registry.yarnpkg.com/private/-/private-0.1.8.tgz#2381edb3689f7a53d653190060fcf822d2f368ff" integrity sha512-VvivMrbvd2nKkiG38qjULzlc+4Vx4wm/whI9pQD35YrARNnhxeiRktSOhSukRLFNlzg6Br/cJPet5J/u19r/mg== -proc-log@^2.0.0, proc-log@^2.0.1: +proc-log@^2.0.1: version "2.0.1" resolved "https://registry.yarnpkg.com/proc-log/-/proc-log-2.0.1.tgz#8f3f69a1f608de27878f91f5c688b225391cb685" integrity sha512-Kcmo2FhfDTXdcbfDH76N7uBYHINxc/8GW7UAVuVP9I+Va3uHSerrnKV6dLooga/gh7GlgzuCCr/eoldnL1muGw== +proc-log@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/proc-log/-/proc-log-3.0.0.tgz#fb05ef83ccd64fd7b20bbe9c8c1070fc08338dd8" + integrity sha512-++Vn7NS4Xf9NacaU9Xq3URUuqZETPsf8L4j5/ckhaRYsfPeRyzGw+iDjFhV/Jr3uNmTvvddEJFWh5R1gRgUH8A== + process-nextick-args@~2.0.0: version "2.0.1" resolved "https://registry.yarnpkg.com/process-nextick-args/-/process-nextick-args-2.0.1.tgz#7820d9b16120cc55ca9ae7792680ae7dba6d7fe2" @@ -22202,16 +22373,6 @@ progress@^2.0.0, progress@^2.0.3: resolved "https://registry.yarnpkg.com/progress/-/progress-2.0.3.tgz#7e8cf8d8f5b8f239c1bc68beb4eb78567d572ef8" integrity sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA== -promise-all-reject-late@^1.0.0: - version "1.0.1" - resolved "https://registry.yarnpkg.com/promise-all-reject-late/-/promise-all-reject-late-1.0.1.tgz#f8ebf13483e5ca91ad809ccc2fcf25f26f8643c2" - integrity sha512-vuf0Lf0lOxyQREH7GDIOUMLS7kz+gs8i6B+Yi8dC68a2sychGrHTJYghMBD6k7eUcH0H5P73EckCA48xijWqXw== - -promise-call-limit@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/promise-call-limit/-/promise-call-limit-1.0.1.tgz#4bdee03aeb85674385ca934da7114e9bcd3c6e24" - integrity sha512-3+hgaa19jzCGLuSCbieeRsu5C2joKfYn8pY6JAuXFRVfF4IO+L7UPpFWNTeWT9pM7uhskvbPPd/oEOktCn317Q== - promise-inflight@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/promise-inflight/-/promise-inflight-1.0.1.tgz#98472870bf228132fcbdd868129bad12c3c029e3" @@ -22265,12 +22426,12 @@ prompts@^2.0.1: kleur "^3.0.3" sisteransi "^1.0.5" -promzard@^0.3.0: - version "0.3.0" - resolved "https://registry.yarnpkg.com/promzard/-/promzard-0.3.0.tgz#26a5d6ee8c7dee4cb12208305acfb93ba382a9ee" - integrity sha1-JqXW7ox97kyxIggwWs+5O6OCqe4= +promzard@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/promzard/-/promzard-1.0.0.tgz#3246f8e6c9895a77c0549cefb65828ac0f6c006b" + integrity sha512-KQVDEubSUHGSt5xLakaToDFrSoZhStB8dXLzk2xvwR67gJktrHFvpR63oZgHyK19WKbHFLXJqCPXdVR3aBP8Ig== dependencies: - read "1" + read "^2.0.0" prop-types@15.7.2: version "15.7.2" @@ -22304,11 +22465,6 @@ proper-lockfile@^4.1.2: retry "^0.12.0" signal-exit "^3.0.2" -proto-list@~1.2.1: - version "1.2.4" - resolved "https://registry.yarnpkg.com/proto-list/-/proto-list-1.2.4.tgz#212d5bfe1318306a420f6402b8e26ff39647a849" - integrity sha1-IS1b/hMYMGpCD2QCuOJv85ZHqEk= - protobufjs@^6.10.2, protobufjs@^6.8.6: version "6.11.3" resolved "https://registry.yarnpkg.com/protobufjs/-/protobufjs-6.11.3.tgz#637a527205a35caa4f3e2a9a4a13ddffe0e7af74" @@ -22434,7 +22590,7 @@ pupa@^2.1.1: dependencies: escape-goat "^2.0.0" -q@^1.1.2, q@^1.5.1, q@~1.5.0: +q@^1.1.2, q@~1.5.0: version "1.5.1" resolved "https://registry.yarnpkg.com/q/-/q-1.5.1.tgz#7e32f75b41381291d04611f1bf14109ac00651d7" integrity sha1-fjL3W0E4EpHQRhHxvxQQmsAGUdc= @@ -22622,6 +22778,11 @@ react-is@^17.0.1: resolved "https://registry.yarnpkg.com/react-is/-/react-is-17.0.2.tgz#e691d4a8e9c789365655539ab372762b0efb54f0" integrity sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w== +react-is@^18.0.0: + version "18.2.0" + resolved "https://registry.yarnpkg.com/react-is/-/react-is-18.2.0.tgz#199431eeaaa2e09f86427efbb4f1473edb47609b" + integrity sha512-xWGDIW6x921xtzPkhiULtthJHoJvBbF3q26fzloPCK0hsvxtPVelvftw3zjbHWSkR2km9Z+4uxbDDK/6Zw9B8w== + react-refresh@0.8.3: version "0.8.3" resolved "https://registry.yarnpkg.com/react-refresh/-/react-refresh-0.8.3.tgz#721d4657672d400c5e3c75d063c4a85fb2d5d68f" @@ -22706,17 +22867,12 @@ read-cache@^1.0.0: dependencies: pify "^2.3.0" -read-cmd-shim@3.0.0: - version "3.0.0" - resolved "https://registry.npmjs.org/read-cmd-shim/-/read-cmd-shim-3.0.0.tgz#62b8c638225c61e6cc607f8f4b779f3b8238f155" - integrity sha512-KQDVjGqhZk92PPNRj9ZEXEuqg8bUobSKRw+q0YQ3TKI5xkce7bUJobL4Z/OtiEbAAv70yEpYIXp4iQ9L8oPVog== - -read-cmd-shim@^3.0.0: - version "3.0.1" - resolved "https://registry.yarnpkg.com/read-cmd-shim/-/read-cmd-shim-3.0.1.tgz#868c235ec59d1de2db69e11aec885bc095aea087" - integrity sha512-kEmDUoYf/CDy8yZbLTmhB1X9kkjf9Q80PCNsDMb7ufrGd6zZSQA1+UyjrO+pZm5K/S4OXCWJeiIt1JA8kAsa6g== +read-cmd-shim@4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/read-cmd-shim/-/read-cmd-shim-4.0.0.tgz#640a08b473a49043e394ae0c7a34dd822c73b9bb" + integrity sha512-yILWifhaSEEytfXI76kB9xEEiG1AiozaCJZ83A87ytjRiN+jVibXjedjCRNjoZviinhG+4UkalO3mWTd8u5O0Q== -read-package-json-fast@^2.0.1, read-package-json-fast@^2.0.2, read-package-json-fast@^2.0.3: +read-package-json-fast@^2.0.1: version "2.0.3" resolved "https://registry.yarnpkg.com/read-package-json-fast/-/read-package-json-fast-2.0.3.tgz#323ca529630da82cb34b36cc0b996693c98c2b83" integrity sha512-W/BKtbL+dUjTuRL2vziuYhp76s5HZ9qQhd/dKfWIZveD0O40453QNyZhC0e63lqZrAQ4jiOapVoeJ7JrszenQQ== @@ -22724,15 +22880,23 @@ read-package-json-fast@^2.0.1, read-package-json-fast@^2.0.2, read-package-json- json-parse-even-better-errors "^2.3.0" npm-normalize-package-bin "^1.0.1" -read-package-json@5.0.1: - version "5.0.1" - resolved "https://registry.npmjs.org/read-package-json/-/read-package-json-5.0.1.tgz#1ed685d95ce258954596b13e2e0e76c7d0ab4c26" - integrity sha512-MALHuNgYWdGW3gKzuNMuYtcSSZbGQm94fAp16xt8VsYTLBjUSc55bLMKe6gzpWue0Tfi6CBgwCSdDAqutGDhMg== +read-package-json-fast@^3.0.0: + version "3.0.2" + resolved "https://registry.yarnpkg.com/read-package-json-fast/-/read-package-json-fast-3.0.2.tgz#394908a9725dc7a5f14e70c8e7556dff1d2b1049" + integrity sha512-0J+Msgym3vrLOUB3hzQCuZHII0xkNGCtz/HJH9xZshwv9DbDwkw1KaE3gx/e2J5rpEY5rtOy6cyhKOPrkP7FZw== dependencies: - glob "^8.0.1" - json-parse-even-better-errors "^2.3.1" - normalize-package-data "^4.0.0" - npm-normalize-package-bin "^1.0.1" + json-parse-even-better-errors "^3.0.0" + npm-normalize-package-bin "^3.0.0" + +read-package-json@6.0.4, read-package-json@^6.0.0: + version "6.0.4" + resolved "https://registry.yarnpkg.com/read-package-json/-/read-package-json-6.0.4.tgz#90318824ec456c287437ea79595f4c2854708836" + integrity sha512-AEtWXYfopBj2z5N5PbkAOeNHRPUg5q+Nen7QLxV8M2zJq1ym6/lCz3fYNTCXe19puu2d06jfHhrP7v/S2PtMMw== + dependencies: + glob "^10.2.2" + json-parse-even-better-errors "^3.0.0" + normalize-package-data "^5.0.0" + npm-normalize-package-bin "^3.0.0" read-package-json@^2.0.0: version "2.1.2" @@ -22744,16 +22908,6 @@ read-package-json@^2.0.0: normalize-package-data "^2.0.0" npm-normalize-package-bin "^1.0.0" -read-package-json@^5.0.0: - version "5.0.2" - resolved "https://registry.yarnpkg.com/read-package-json/-/read-package-json-5.0.2.tgz#b8779ccfd169f523b67208a89cc912e3f663f3fa" - integrity sha512-BSzugrt4kQ/Z0krro8zhTwV1Kd79ue25IhNN/VtHFy1mG/6Tluyi+msc0UpwaoQzxSHa28mntAjIZY6kEgfR9Q== - dependencies: - glob "^8.0.1" - json-parse-even-better-errors "^2.3.1" - normalize-package-data "^4.0.0" - npm-normalize-package-bin "^2.0.0" - read-package-tree@5.3.1: version "5.3.1" resolved "https://registry.yarnpkg.com/read-package-tree/-/read-package-tree-5.3.1.tgz#a32cb64c7f31eb8a6f31ef06f9cedf74068fe636" @@ -22841,12 +22995,12 @@ read-pkg@^5.0.0, read-pkg@^5.2.0: parse-json "^5.0.0" type-fest "^0.6.0" -read@1, read@^1.0.7: - version "1.0.7" - resolved "https://registry.yarnpkg.com/read/-/read-1.0.7.tgz#b3da19bd052431a97671d44a42634adf710b40c4" - integrity sha1-s9oZvQUkMal2cdRKQmNK33ELQMQ= +read@^2.0.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/read/-/read-2.1.0.tgz#69409372c54fe3381092bc363a00650b6ac37218" + integrity sha512-bvxi1QLJHcaywCAEsAk4DG3nVoqiY2Csps3qzWalhj5hFqRn1d/OixkFXtLO1PrgHUcAP0FNaSY/5GYNfENFFQ== dependencies: - mute-stream "~0.0.4" + mute-stream "~1.0.0" "readable-stream@1 || 2", readable-stream@2.3.7, readable-stream@^2.0.0, readable-stream@^2.0.1, readable-stream@^2.0.2, readable-stream@^2.0.6, readable-stream@^2.1.5, readable-stream@^2.2.2, readable-stream@^2.3.3, readable-stream@^2.3.5, readable-stream@^2.3.6, readable-stream@~2.3.6: version "2.3.7" @@ -22861,7 +23015,7 @@ read@1, read@^1.0.7: string_decoder "~1.1.1" util-deprecate "~1.0.1" -"readable-stream@2 || 3", readable-stream@3, readable-stream@^3.0.0, readable-stream@^3.0.2, readable-stream@^3.0.6, readable-stream@^3.1.1, readable-stream@^3.4.0, readable-stream@^3.5.0, readable-stream@^3.6.0: +"readable-stream@2 || 3", readable-stream@^3.0.0, readable-stream@^3.0.2, readable-stream@^3.0.6, readable-stream@^3.1.1, readable-stream@^3.4.0, readable-stream@^3.5.0, readable-stream@^3.6.0: version "3.6.0" resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-3.6.0.tgz#337bbda3adc0706bd3e024426a286d4b4b2c9198" integrity sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA== @@ -22880,7 +23034,7 @@ readable-stream@~1.0.2: isarray "0.0.1" string_decoder "~0.10.x" -readdir-scoped-modules@^1.0.0, readdir-scoped-modules@^1.1.0: +readdir-scoped-modules@^1.0.0: version "1.1.0" resolved "https://registry.yarnpkg.com/readdir-scoped-modules/-/readdir-scoped-modules-1.1.0.tgz#8d45407b4f870a0dcaebc0e28670d18e74514309" integrity sha512-asaikDeqAQg7JifRsZn1NJZXo9E+VwlyCfbkZhwyISinqk5zNS6266HS5kah6P0SaQKGF6SkNnZVHUzHFYxYDw== @@ -23566,6 +23720,13 @@ rimraf@^2.2.8, rimraf@^2.3.4, rimraf@^2.4.3, rimraf@^2.5.2, rimraf@^2.5.3, rimra dependencies: glob "^7.1.3" +rimraf@^4.4.1: + version "4.4.1" + resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-4.4.1.tgz#bd33364f67021c5b79e93d7f4fa0568c7c21b755" + integrity sha512-Gk8NlF062+T9CqNGn6h4tls3k6T1+/nXdOcSZVikNVtlRdYpA7wRJJMoXmuvOnLW844rPjdQ7JgXCYM6PPC/og== + dependencies: + glob "^9.2.0" + rimraf@~2.5.2: version "2.5.4" resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-2.5.4.tgz#96800093cbf1a0c86bd95b4625467535c29dfa04" @@ -23974,9 +24135,9 @@ semver-intersect@1.4.0: semver "^5.0.0" "semver@2 || 3 || 4 || 5", semver@^5.0.0, semver@^5.3.0, semver@^5.4.1, semver@^5.5.0, semver@^5.5.1, semver@^5.6.0, semver@^5.7.0, semver@^5.7.1: - version "5.7.1" - resolved "https://registry.yarnpkg.com/semver/-/semver-5.7.1.tgz#a954f931aeba508d307bbf069eff0c01c96116f7" - integrity sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ== + version "5.7.2" + resolved "https://registry.yarnpkg.com/semver/-/semver-5.7.2.tgz#48d55db737c3287cd4835e17fa13feace1c41ef8" + integrity sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g== semver@7.3.2: version "7.3.2" @@ -23997,17 +24158,24 @@ semver@7.3.5: 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: - version "7.3.8" - resolved "https://registry.yarnpkg.com/semver/-/semver-7.3.8.tgz#07a78feafb3f7b32347d725e33de7e2a2df67798" - integrity sha512-NB1ctGL5rlHrPJtFDVIVzTyQylMLu9N9VICA6HSFJo8MCGVTMW6gfpicwKmmK/dAjTOrqu5l63JJOpDSrAis3A== +semver@7.5.3: + version "7.5.3" + resolved "https://registry.yarnpkg.com/semver/-/semver-7.5.3.tgz#161ce8c2c6b4b3bdca6caadc9fa3317a4c4fe88e" + integrity sha512-QBlUtyVk/5EeHbi7X0fw6liDZc7BBmEaSYn01fMU1OUYbf6GPsbTtd8WmnqbI20SeycoHSeiybkE/q1Q+qlThQ== + 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: + version "7.5.4" + resolved "https://registry.yarnpkg.com/semver/-/semver-7.5.4.tgz#483986ec4ed38e1c6c48c34894a9182dbff68a6e" + integrity sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA== dependencies: lru-cache "^6.0.0" semver@^6.0.0, semver@^6.1.0, semver@^6.1.1, semver@^6.1.2, semver@^6.2.0, semver@^6.3.0: - version "6.3.0" - resolved "https://registry.yarnpkg.com/semver/-/semver-6.3.0.tgz#ee0a64c8af5e8ceea67687b133761e1becbd1d3d" - integrity sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw== + version "6.3.1" + resolved "https://registry.yarnpkg.com/semver/-/semver-6.3.1.tgz#556d2ef8689146e46dcea4bfdd095f3434dffcb4" + integrity sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA== send@0.18.0: version "0.18.0" @@ -24182,6 +24350,21 @@ signal-exit@3.0.7, signal-exit@^3.0.0, signal-exit@^3.0.2, signal-exit@^3.0.3, s resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-3.0.7.tgz#a9a1767f8af84155114eaabd73f99273c8f59ad9" integrity sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ== +signal-exit@^4.0.1: + version "4.0.2" + resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-4.0.2.tgz#ff55bb1d9ff2114c13b400688fa544ac63c36967" + integrity sha512-MY2/qGx4enyjprQnFaZsHib3Yadh3IXyV2C321GY0pjGfVBu4un0uDJkwgdxqO+Rdx8JMT8IfJIRwbYVz3Ob3Q== + +sigstore@^1.3.0, sigstore@^1.4.0: + version "1.6.0" + resolved "https://registry.yarnpkg.com/sigstore/-/sigstore-1.6.0.tgz#887a4007c6ee83f3ef3fd844be1a0840e849c301" + integrity sha512-QODKff/qW/TXOZI6V/Clqu74xnInAS6it05mufj4/fSewexLtfEntgLZZcBtUK44CDQyUE5TUXYy1ARYzlfG9g== + dependencies: + "@sigstore/protobuf-specs" "^0.1.0" + "@sigstore/tuf" "^1.0.0" + make-fetch-happen "^11.0.1" + tuf-js "^1.1.3" + silent-error@^1.0.0, silent-error@^1.0.1, silent-error@^1.1.0, silent-error@^1.1.1: version "1.1.1" resolved "https://registry.yarnpkg.com/silent-error/-/silent-error-1.1.1.tgz#f72af5b0d73682a2ba1778b7e32cd8aa7c2d8662" @@ -24489,13 +24672,6 @@ sort-keys@^2.0.0: dependencies: is-plain-obj "^1.0.0" -sort-keys@^4.0.0: - version "4.2.0" - resolved "https://registry.yarnpkg.com/sort-keys/-/sort-keys-4.2.0.tgz#6b7638cee42c506fff8c1cecde7376d21315be18" - integrity sha512-aUYIEU/UviqPgc8mHR6IW1EGxkAXpeRETYcrzg8cLAvUPZcpAlleSXHV2mY7G12GphSH6Gzv+4MMVSSkbdteHg== - dependencies: - is-plain-obj "^2.0.0" - sort-object-keys@^1.1.3: version "1.1.3" resolved "https://registry.yarnpkg.com/sort-object-keys/-/sort-object-keys-1.1.3.tgz#bff833fe85cab147b34742e45863453c1e190b45" @@ -24763,7 +24939,7 @@ split-string@^3.0.1, split-string@^3.0.2: dependencies: extend-shallow "^3.0.0" -split2@^3.0.0: +split2@^3.2.2: version "3.2.2" resolved "https://registry.yarnpkg.com/split2/-/split2-3.2.2.tgz#bf2cf2a37d838312c249c89206fd7a17dd12365f" integrity sha512-9NThjpgZnifTkJpzTZ7Eue85S49QwpNhZTq6GRJwObb6jnLFNGB7Qm73V5HewTROPyxD0C29xqmaI68bQtV+hg== @@ -24782,7 +24958,7 @@ split@0.3: dependencies: through "2" -split@^1.0.0: +split@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/split/-/split-1.0.1.tgz#605bd9be303aa59fb35f9229fbea0ddec9ea07d9" integrity sha512-mTyOoPbrivtXnwnIxZRFYRrPNtEFKlpB2fvjSnCQUiAA6qAZzqwna5envK4uk6OIeP17CsdF3rSBGYVBsU0Tkg== @@ -24824,12 +25000,12 @@ sshpk@^1.7.0: safer-buffer "^2.0.2" tweetnacl "~0.14.0" -ssri@9.0.1, ssri@^9.0.0: - version "9.0.1" - resolved "https://registry.yarnpkg.com/ssri/-/ssri-9.0.1.tgz#544d4c357a8d7b71a19700074b6883fcb4eae057" - integrity sha512-o57Wcn66jMQvfHG1FlYbWeZWW/dHZhJXjpIcTfXldXEk5nz5lStPo3mK0OJQfGR3RbZUlbISexbljkJzuEj/8Q== +ssri@^10.0.0, ssri@^10.0.1: + version "10.0.4" + resolved "https://registry.yarnpkg.com/ssri/-/ssri-10.0.4.tgz#5a20af378be586df139ddb2dfb3bf992cf0daba6" + integrity sha512-12+IR2CB2C28MMAw0Ncqwj5QbTcs0nGIhgJzYWzDkb21vWmfNI83KS4f3Ci6GI98WreIfG7o9UXp3C0qbpA8nQ== dependencies: - minipass "^3.1.1" + minipass "^5.0.0" ssri@^6.0.0, ssri@^6.0.1: version "6.0.2" @@ -24845,6 +25021,13 @@ ssri@^8.0.0, ssri@^8.0.1: dependencies: minipass "^3.1.1" +ssri@^9.0.0, ssri@^9.0.1: + version "9.0.1" + resolved "https://registry.yarnpkg.com/ssri/-/ssri-9.0.1.tgz#544d4c357a8d7b71a19700074b6883fcb4eae057" + integrity sha512-o57Wcn66jMQvfHG1FlYbWeZWW/dHZhJXjpIcTfXldXEk5nz5lStPo3mK0OJQfGR3RbZUlbISexbljkJzuEj/8Q== + dependencies: + minipass "^3.1.1" + stable@^0.1.8: version "0.1.8" resolved "https://registry.yarnpkg.com/stable/-/stable-0.1.8.tgz#836eb3c8382fe2936feaf544631017ce7d47a3cf" @@ -25028,6 +25211,15 @@ string-template@~0.2.1: resolved "https://registry.yarnpkg.com/string-template/-/string-template-0.2.1.tgz#42932e598a352d01fc22ec3367d9d84eec6c9add" integrity sha1-QpMuWYo1LQH8IuwzZ9nYTuxsmt0= +"string-width-cjs@npm:string-width@^4.2.0", "string-width@^1.0.2 || 2 || 3 || 4", string-width@^4.0.0, string-width@^4.1.0, string-width@^4.2.0, string-width@^4.2.2, string-width@^4.2.3: + version "4.2.3" + resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010" + integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g== + dependencies: + emoji-regex "^8.0.0" + is-fullwidth-code-point "^3.0.0" + strip-ansi "^6.0.1" + string-width@^1.0.1: version "1.0.2" resolved "https://registry.yarnpkg.com/string-width/-/string-width-1.0.2.tgz#118bdf5b8cdc51a2a7e70d211e07e2b0b9b107d3" @@ -25045,15 +25237,6 @@ string-width@^1.0.1: is-fullwidth-code-point "^2.0.0" strip-ansi "^4.0.0" -"string-width@^1.0.2 || 2 || 3 || 4", string-width@^4.0.0, string-width@^4.1.0, string-width@^4.2.0, string-width@^4.2.2, string-width@^4.2.3: - version "4.2.3" - resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010" - integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g== - dependencies: - emoji-regex "^8.0.0" - is-fullwidth-code-point "^3.0.0" - strip-ansi "^6.0.1" - string-width@^3.0.0, string-width@^3.1.0: version "3.1.0" resolved "https://registry.yarnpkg.com/string-width/-/string-width-3.1.0.tgz#22767be21b62af1081574306f69ac51b62203961" @@ -25063,7 +25246,7 @@ string-width@^3.0.0, string-width@^3.1.0: is-fullwidth-code-point "^2.0.0" strip-ansi "^5.1.0" -string-width@^5.0.0: +string-width@^5.0.0, string-width@^5.0.1, string-width@^5.1.2: version "5.1.2" resolved "https://registry.yarnpkg.com/string-width/-/string-width-5.1.2.tgz#14f8daec6d81e7221d2a357e668cab73bdbca794" integrity sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA== @@ -25141,6 +25324,13 @@ stringify-object@^3.2.1: is-obj "^1.0.1" is-regexp "^1.0.0" +"strip-ansi-cjs@npm:strip-ansi@^6.0.1", strip-ansi@^6.0.0, strip-ansi@^6.0.1: + version "6.0.1" + resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9" + integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A== + dependencies: + ansi-regex "^5.0.1" + strip-ansi@6.0.0: version "6.0.0" resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.0.tgz#0b1571dd7669ccd4f3e06e14ef1eed26225ae532" @@ -25169,13 +25359,6 @@ strip-ansi@^5.0.0, strip-ansi@^5.1.0, strip-ansi@^5.2.0: dependencies: ansi-regex "^4.1.0" -strip-ansi@^6.0.0, strip-ansi@^6.0.1: - version "6.0.1" - resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9" - integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A== - dependencies: - ansi-regex "^5.0.1" - strip-ansi@^7.0.1: version "7.0.1" resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-7.0.1.tgz#61740a08ce36b61e50e65653f07060d000975fb2" @@ -25888,13 +26071,6 @@ through2@^3.0.1: inherits "^2.0.4" readable-stream "2 || 3" -through2@^4.0.0: - version "4.0.2" - resolved "https://registry.yarnpkg.com/through2/-/through2-4.0.2.tgz#a7ce3ac2a7a8b0b966c80e7c49f0484c3b239764" - integrity sha512-iOqSav00cVxEEICeD7TjLB1sueEL+81Wpzp2bY17uZjZN0pWZPuo4suZ/61VujxmqSGFfgOcNuTZ85QJwNZQpw== - dependencies: - readable-stream "3" - through@2, "through@>=2.2.7 <3", through@^2.3.4, through@^2.3.6, through@~2.3, through@~2.3.1: version "2.3.8" resolved "https://registry.yarnpkg.com/through/-/through-2.3.8.tgz#0dd4c9ffaabc357960b1b724115d7e0e86a2e1f5" @@ -26156,11 +26332,6 @@ tree-sync@^2.0.0, tree-sync@^2.1.0: quick-temp "^0.1.5" walk-sync "^0.3.3" -treeverse@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/treeverse/-/treeverse-2.0.0.tgz#036dcef04bc3fd79a9b79a68d4da03e882d8a9ca" - integrity sha512-N5gJCkLu1aXccpOTtqV6ddSEi6ZmGkh3hjmbu1IjcavJK4qyOVQmi0myQKM7z5jVGmD68SJoliaVrMmVObhj6A== - trim-newlines@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/trim-newlines/-/trim-newlines-1.0.0.tgz#5887966bb582a4503a41eb524f7d35011815a613" @@ -26285,6 +26456,15 @@ tty-browserify@0.0.1, tty-browserify@^0.0.1: resolved "https://registry.yarnpkg.com/tty-browserify/-/tty-browserify-0.0.1.tgz#3f05251ee17904dfd0677546670db9651682b811" integrity sha512-C3TaO7K81YvjCgQH9Q1S3R3P3BtN3RIM8n+OvX4il1K1zgE8ZhI0op7kClgkxtutIE8hQrcrHBXvIheqKUUCxw== +tuf-js@^1.1.3: + version "1.1.7" + resolved "https://registry.yarnpkg.com/tuf-js/-/tuf-js-1.1.7.tgz#21b7ae92a9373015be77dfe0cb282a80ec3bbe43" + integrity sha512-i3P9Kgw3ytjELUfpuKVDNBJvk4u5bXL6gskv572mcevPbSKCV3zt3djhmlEQ65yERjIbOSncy7U4cQJaB1CBCg== + dependencies: + "@tufjs/models" "1.0.4" + debug "^4.3.4" + make-fetch-happen "^11.1.1" + tunnel-agent@^0.6.0: version "0.6.0" resolved "https://registry.yarnpkg.com/tunnel-agent/-/tunnel-agent-0.6.0.tgz#27a5dea06b36b04a0a9966774b290868f0fc40fd" @@ -26424,11 +26604,16 @@ typescript@4.3.5: resolved "https://registry.yarnpkg.com/typescript/-/typescript-4.3.5.tgz#4d1c37cc16e893973c45a06886b7113234f119f4" integrity sha512-DqQgihaQ9cUrskJo9kIyW/+g0Vxsk8cDtZ52a3NGh0YNTfpUSArXSohyUGnvbPazEPLu398C0UxmKSOrPumUzA== -typescript@4.9.5, "typescript@^3 || ^4": +typescript@4.9.5: version "4.9.5" resolved "https://registry.yarnpkg.com/typescript/-/typescript-4.9.5.tgz#095979f9bcc0d09da324d58d03ce8f8374cbe65a" integrity sha512-1FXk9E2Hm+QzZQ7z+McJiHL4NW1F2EzMu9Nq9i3zAaGqibafqYwCVU6WyWAuyQRRzOlxou8xZSyXLEN8oKj24g== +"typescript@>=3 < 6": + version "5.1.6" + resolved "https://registry.yarnpkg.com/typescript/-/typescript-5.1.6.tgz#02f8ac202b6dad2c0dd5e0913745b47a37998274" + integrity sha512-zaWCozRZ6DLEWAWFrVDz1H6FVXzUSfTy5FUMWsQlU8Ym5JP9eO4xkTIROFCQvhQf61z6O/G6ugw3SgAnvvm+HA== + typescript@^3.9.5, typescript@^3.9.7: version "3.9.10" resolved "https://registry.yarnpkg.com/typescript/-/typescript-3.9.10.tgz#70f3910ac7a51ed6bef79da7800690b19bf778b8" @@ -26577,6 +26762,13 @@ unique-filename@^2.0.0: dependencies: unique-slug "^3.0.0" +unique-filename@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/unique-filename/-/unique-filename-3.0.0.tgz#48ba7a5a16849f5080d26c760c86cf5cf05770ea" + integrity sha512-afXhuC55wkAmZ0P18QsVE6kp8JaxrEokN2HGIoIVv2ijHQd419H0+6EigAFcIzXeMIkcIkNBpB3L/DXB3cTS/g== + dependencies: + unique-slug "^4.0.0" + unique-slug@^2.0.0: version "2.0.2" resolved "https://registry.yarnpkg.com/unique-slug/-/unique-slug-2.0.2.tgz#baabce91083fc64e945b0f3ad613e264f7cd4e6c" @@ -26591,6 +26783,13 @@ unique-slug@^3.0.0: dependencies: imurmurhash "^0.1.4" +unique-slug@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/unique-slug/-/unique-slug-4.0.0.tgz#6bae6bb16be91351badd24cdce741f892a6532e3" + integrity sha512-WrcA6AyEfqDX5bWige/4NQfPZMtASNVxdmWR76WESYQVAACSgWcR6e9i0mofqqBxYFtL4oAxPIptY73/0YE1DQ== + dependencies: + imurmurhash "^0.1.4" + unique-string@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/unique-string/-/unique-string-2.0.0.tgz#39c6451f81afb2749de2b233e3f7c5e8843bd89d" @@ -26657,16 +26856,16 @@ untildify@^2.1.0: dependencies: os-homedir "^1.0.0" +upath@2.0.1, upath@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/upath/-/upath-2.0.1.tgz#50c73dea68d6f6b990f51d279ce6081665d61a8b" + integrity sha512-1uEe95xksV1O0CYKXo8vQvN1JEbtJp7lb7C5U9HMsIp6IVwntkH/oNUzyVNQSd4S1sYk2FpSSW44FqMc8qee5w== + upath@^1.1.1: version "1.2.0" resolved "https://registry.yarnpkg.com/upath/-/upath-1.2.0.tgz#8f66dbcd55a883acdae4408af8b035a5044c1894" integrity sha512-aZwGpamFO61g3OlfT7OQCHqhGnW43ieH9WZeP7QxN/G/jS4jfqUkZxoryvJgVPEcrl5NL/ggHsSmLMHuH64Lhg== -upath@^2.0.1: - version "2.0.1" - resolved "https://registry.yarnpkg.com/upath/-/upath-2.0.1.tgz#50c73dea68d6f6b990f51d279ce6081665d61a8b" - integrity sha512-1uEe95xksV1O0CYKXo8vQvN1JEbtJp7lb7C5U9HMsIp6IVwntkH/oNUzyVNQSd4S1sYk2FpSSW44FqMc8qee5w== - update-browserslist-db@^1.0.10, update-browserslist-db@^1.0.9: version "1.0.10" resolved "https://registry.yarnpkg.com/update-browserslist-db/-/update-browserslist-db-1.0.10.tgz#0f54b876545726f17d00cd9a2561e6dade943ff3" @@ -26912,10 +27111,10 @@ validate-npm-package-license@3.0.4, validate-npm-package-license@^3.0.1, validat spdx-correct "^3.0.0" spdx-expression-parse "^3.0.0" -validate-npm-package-name@4.0.0, validate-npm-package-name@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/validate-npm-package-name/-/validate-npm-package-name-4.0.0.tgz#fe8f1c50ac20afdb86f177da85b3600f0ac0d747" - integrity sha512-mzR0L8ZDktZjpX4OB46KT+56MAhl4EIazWP/+G/HPGuvfdaqg4YsCdtOm6U9+LOFyYDoh4dpnpxZRB9MQQns5Q== +validate-npm-package-name@5.0.0, validate-npm-package-name@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/validate-npm-package-name/-/validate-npm-package-name-5.0.0.tgz#f16afd48318e6f90a1ec101377fa0384cfc8c713" + integrity sha512-YuKoXDAhBYxY7SfOKxHBDoSyENFeW5VvIIQp2TGQuit8gpK6MnWaQelBKxso72DoxTZfZdcP3W90LqpSkgPzLQ== dependencies: builtins "^5.0.0" @@ -26926,6 +27125,13 @@ validate-npm-package-name@^3.0.0: dependencies: builtins "^1.0.3" +validate-npm-package-name@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/validate-npm-package-name/-/validate-npm-package-name-4.0.0.tgz#fe8f1c50ac20afdb86f177da85b3600f0ac0d747" + integrity sha512-mzR0L8ZDktZjpX4OB46KT+56MAhl4EIazWP/+G/HPGuvfdaqg4YsCdtOm6U9+LOFyYDoh4dpnpxZRB9MQQns5Q== + dependencies: + builtins "^5.0.0" + validate-peer-dependencies@^1.1.0: version "1.2.0" resolved "https://registry.yarnpkg.com/validate-peer-dependencies/-/validate-peer-dependencies-1.2.0.tgz#22aab93c514f4fda457d36c80685e8b1160d2036" @@ -27144,11 +27350,6 @@ walk-sync@^3.0.0: matcher-collection "^2.0.1" minimatch "^3.0.4" -walk-up-path@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/walk-up-path/-/walk-up-path-1.0.0.tgz#d4745e893dd5fd0dbb58dd0a4c6a33d9c9fec53e" - integrity sha512-hwj/qMDUEjCU5h0xr90KGCf0tg0/LgJbmOWgrWKYlcJZM7XvquvUJZ0G/HMGr7F7OQMOUuPHWP9JpriinkAlkg== - walkdir@^0.4.1: version "0.4.1" resolved "https://registry.yarnpkg.com/walkdir/-/walkdir-0.4.1.tgz#dc119f83f4421df52e3061e514228a2db20afa39" @@ -27705,6 +27906,13 @@ which@^2.0.1, which@^2.0.2: dependencies: isexe "^2.0.0" +which@^3.0.0: + version "3.0.1" + resolved "https://registry.yarnpkg.com/which/-/which-3.0.1.tgz#89f1cd0c23f629a8105ffe69b8172791c87b4be1" + integrity sha512-XA1b62dzQzLfaEOSQFTCOd5KFf/1VSzZo7/7TUjnya6u0vGGKzU96UQBZTAThCb2j4/xjBAyii1OhRLJEivHvg== + dependencies: + isexe "^2.0.0" + why-is-node-running@^2.2.2: version "2.2.2" resolved "https://registry.yarnpkg.com/why-is-node-running/-/why-is-node-running-2.2.2.tgz#4185b2b4699117819e7154594271e7e344c9973e" @@ -27813,6 +28021,15 @@ workerpool@^6.1.5, workerpool@^6.2.1: resolved "https://registry.yarnpkg.com/workerpool/-/workerpool-6.2.1.tgz#46fc150c17d826b86a008e5a4508656777e9c343" integrity sha512-ILEIE97kDZvF9Wb9f6h5aXK4swSlKGUcOEGiIYb2OOu/IrDU9iwj0fD//SsA6E5ibwJxpEvhullJY4Sl4GcpAw== +"wrap-ansi-cjs@npm:wrap-ansi@^7.0.0", wrap-ansi@^7.0.0: + version "7.0.0" + resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-7.0.0.tgz#67e145cff510a6a6984bdf1152911d69d2eb9e43" + integrity sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q== + dependencies: + ansi-styles "^4.0.0" + string-width "^4.1.0" + strip-ansi "^6.0.0" + wrap-ansi@^5.1.0: version "5.1.0" resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-5.1.0.tgz#1fd1f67235d5b6d0fee781056001bfb694c03b09" @@ -27831,27 +28048,27 @@ wrap-ansi@^6.2.0: string-width "^4.1.0" strip-ansi "^6.0.0" -wrap-ansi@^7.0.0: - version "7.0.0" - resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-7.0.0.tgz#67e145cff510a6a6984bdf1152911d69d2eb9e43" - integrity sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q== +wrap-ansi@^8.1.0: + version "8.1.0" + resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-8.1.0.tgz#56dc22368ee570face1b49819975d9b9a5ead214" + integrity sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ== dependencies: - ansi-styles "^4.0.0" - string-width "^4.1.0" - strip-ansi "^6.0.0" + ansi-styles "^6.1.0" + string-width "^5.0.1" + strip-ansi "^7.0.1" wrappy@1: version "1.0.2" resolved "https://registry.yarnpkg.com/wrappy/-/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f" integrity sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8= -write-file-atomic@4.0.1: - version "4.0.1" - resolved "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-4.0.1.tgz#9faa33a964c1c85ff6f849b80b42a88c2c537c8f" - integrity sha512-nSKUxgAbyioruk6hU87QzVbY279oYT6uiwgDoujth2ju4mJ+TZau7SQBhtbTmUyuNYTuXnSyRn66FV0+eCgcrQ== +write-file-atomic@5.0.1: + version "5.0.1" + resolved "https://registry.yarnpkg.com/write-file-atomic/-/write-file-atomic-5.0.1.tgz#68df4717c55c6fa4281a7860b4c2ba0a6d2b11e7" + integrity sha512-+QU2zd6OTD8XWIJCbffaiQeH9U73qIqafo1x6V1snCWYGJf6cVE0cDR4D8xRzcEnfI21IFrUPzPGtcPf8AC+Rw== dependencies: imurmurhash "^0.1.4" - signal-exit "^3.0.7" + signal-exit "^4.0.1" write-file-atomic@^2.4.2: version "2.4.3" @@ -27872,26 +28089,6 @@ write-file-atomic@^3.0.0: signal-exit "^3.0.2" typedarray-to-buffer "^3.1.5" -write-file-atomic@^4.0.0: - version "4.0.2" - resolved "https://registry.yarnpkg.com/write-file-atomic/-/write-file-atomic-4.0.2.tgz#a9df01ae5b77858a027fd2e80768ee433555fcfd" - integrity sha512-7KxauUdBmSdWnmpaGFg+ppNjKF8uNLry8LyzjauQDOVONfFLNKrKvQOxZ/VuTIcS/gge/YNahf5RIIQWTSarlg== - dependencies: - imurmurhash "^0.1.4" - signal-exit "^3.0.7" - -write-json-file@4.3.0: - version "4.3.0" - resolved "https://registry.npmjs.org/write-json-file/-/write-json-file-4.3.0.tgz#908493d6fd23225344af324016e4ca8f702dd12d" - integrity sha512-PxiShnxf0IlnQuMYOPPhPkhExoCQuTUNPOa/2JWCYTmBquU9njyyDuwRKN26IZBlp4yn1nt+Agh2HOOBl+55HQ== - dependencies: - detect-indent "^6.0.0" - graceful-fs "^4.1.15" - is-plain-obj "^2.0.0" - make-dir "^3.0.0" - sort-keys "^4.0.0" - write-file-atomic "^3.0.0" - write-json-file@^3.2.0: version "3.2.0" resolved "https://registry.yarnpkg.com/write-json-file/-/write-json-file-3.2.0.tgz#65bbdc9ecd8a1458e15952770ccbadfcff5fe62a"