From e818fcba2aa704c547cc878818137b3f814edb37 Mon Sep 17 00:00:00 2001 From: Abdelrahman Awad Date: Thu, 16 Apr 2026 14:25:25 -0400 Subject: [PATCH 01/41] fix(core): handle stateless MCP wrapper transport correlation (#20293) This PR handles stateless MCP wrappers by using the connected transport instance as the stable MCP correlation key across wrapper layers. I verified the issue and the fix by adding a regression test for stateless wrapper transports where the request path runs on the inner transport and the response path runs on the wrapper. Closes #20290 --------- Co-authored-by: GPT-5 --- .../src/integrations/mcp-server/transport.ts | 18 ++-- .../transportInstrumentation.test.ts | 89 +++++++++++++++++++ 2 files changed, 98 insertions(+), 9 deletions(-) diff --git a/packages/core/src/integrations/mcp-server/transport.ts b/packages/core/src/integrations/mcp-server/transport.ts index 4b04a78f43b0..49b141964fa9 100644 --- a/packages/core/src/integrations/mcp-server/transport.ts +++ b/packages/core/src/integrations/mcp-server/transport.ts @@ -43,7 +43,7 @@ export function wrapTransportOnMessage(transport: MCPTransport, options: Resolve if (isInitialize) { try { initSessionData = extractSessionDataFromInitializeRequest(message); - storeSessionDataForTransport(this, initSessionData); + storeSessionDataForTransport(transport, initSessionData); } catch { // noop } @@ -52,7 +52,7 @@ export function wrapTransportOnMessage(transport: MCPTransport, options: Resolve const isolationScope = getIsolationScope().clone(); return withIsolationScope(isolationScope, () => { - const spanConfig = buildMcpServerSpanConfig(message, this, extra as ExtraHandlerData, options); + const spanConfig = buildMcpServerSpanConfig(message, transport, extra as ExtraHandlerData, options); const span = startInactiveSpan(spanConfig); // For initialize requests, add client info directly to span (works even for stateless transports) @@ -65,7 +65,7 @@ export function wrapTransportOnMessage(transport: MCPTransport, options: Resolve }); } - storeSpanForRequest(this, message.id, span, message.method); + storeSpanForRequest(transport, message.id, span, message.method); return withActiveSpan(span, () => { return (originalOnMessage as (...args: unknown[]) => unknown).call(this, message, extra); @@ -74,7 +74,7 @@ export function wrapTransportOnMessage(transport: MCPTransport, options: Resolve } if (isJsonRpcNotification(message)) { - return createMcpNotificationSpan(message, this, extra as ExtraHandlerData, options, () => { + return createMcpNotificationSpan(message, transport, extra as ExtraHandlerData, options, () => { return (originalOnMessage as (...args: unknown[]) => unknown).call(this, message, extra); }); } @@ -99,7 +99,7 @@ export function wrapTransportSend(transport: MCPTransport, options: ResolvedMcpO const [message] = args; if (isJsonRpcNotification(message)) { - return createMcpOutgoingNotificationSpan(message, this, options, () => { + return createMcpOutgoingNotificationSpan(message, transport, options, () => { return (originalSend as (...args: unknown[]) => unknown).call(this, ...args); }); } @@ -114,14 +114,14 @@ export function wrapTransportSend(transport: MCPTransport, options: ResolvedMcpO if (message.result.protocolVersion || message.result.serverInfo) { try { const serverData = extractSessionDataFromInitializeResponse(message.result); - updateSessionDataForTransport(this, serverData); + updateSessionDataForTransport(transport, serverData); } catch { // noop } } } - completeSpanWithResults(this, message.id, message.result, options, !!message.error); + completeSpanWithResults(transport, message.id, message.result, options, !!message.error); } } @@ -139,8 +139,8 @@ export function wrapTransportOnClose(transport: MCPTransport): void { if (transport.onclose) { fill(transport, 'onclose', originalOnClose => { return function (this: MCPTransport, ...args: unknown[]) { - cleanupPendingSpansForTransport(this); - cleanupSessionDataForTransport(this); + cleanupPendingSpansForTransport(transport); + cleanupSessionDataForTransport(transport); return (originalOnClose as (...args: unknown[]) => unknown).call(this, ...args); }; }); diff --git a/packages/core/test/lib/integrations/mcp-server/transportInstrumentation.test.ts b/packages/core/test/lib/integrations/mcp-server/transportInstrumentation.test.ts index 71590fd17712..d8cc546393ec 100644 --- a/packages/core/test/lib/integrations/mcp-server/transportInstrumentation.test.ts +++ b/packages/core/test/lib/integrations/mcp-server/transportInstrumentation.test.ts @@ -880,6 +880,95 @@ describe('MCP Server Transport Instrumentation', () => { expect(mockSpan.end).toHaveBeenCalled(); }); + it('should correlate spans correctly for stateless wrapper transports', async () => { + const { wrapper, inner } = createMockWrapperTransport('stateless-wrapper-session'); + inner.sessionId = undefined; + + const mockMcpServer = createMockMcpServer(); + const wrappedMcpServer = wrapMcpServerWithSentry(mockMcpServer); + + await wrappedMcpServer.connect(wrapper); + + const mockSpan = { setAttributes: vi.fn(), end: vi.fn() }; + startInactiveSpanSpy.mockReturnValue(mockSpan as any); + + inner.onmessage?.call( + inner, + { + jsonrpc: '2.0', + method: 'tools/call', + id: 'stateless-wrapper-req-1', + params: { name: 'test-tool' }, + }, + {}, + ); + + await wrapper.send({ + jsonrpc: '2.0', + id: 'stateless-wrapper-req-1', + result: { content: [{ type: 'text', text: 'success' }] }, + }); + + expect(mockSpan.end).toHaveBeenCalled(); + }); + + it('should preserve session metadata for later stateless wrapper spans', async () => { + const { wrapper, inner } = createMockWrapperTransport('stateless-wrapper-session'); + inner.sessionId = undefined; + + const mockMcpServer = createMockMcpServer(); + const wrappedMcpServer = wrapMcpServerWithSentry(mockMcpServer); + + await wrappedMcpServer.connect(wrapper); + + inner.onmessage?.call( + inner, + { + jsonrpc: '2.0', + method: 'initialize', + id: 'init-stateless', + params: { + protocolVersion: '2025-06-18', + clientInfo: { name: 'test-client', version: '1.0.0' }, + }, + }, + {}, + ); + + await wrapper.send({ + jsonrpc: '2.0', + id: 'init-stateless', + result: { + protocolVersion: '2025-06-18', + serverInfo: { name: 'test-server', version: '2.0.0' }, + capabilities: {}, + }, + }); + + inner.onmessage?.call( + inner, + { + jsonrpc: '2.0', + method: 'tools/call', + id: 'stateless-wrapper-req-2', + params: { name: 'test-tool' }, + }, + {}, + ); + + expect(startInactiveSpanSpy).toHaveBeenCalledWith( + expect.objectContaining({ + attributes: expect.objectContaining({ + 'mcp.client.name': 'test-client', + 'mcp.client.version': '1.0.0', + 'mcp.protocol.version': '2025-06-18', + 'mcp.server.name': 'test-server', + 'mcp.server.version': '2.0.0', + }), + }), + ); + }); + it('should handle initialize request/response with wrapper transport', async () => { const { wrapper } = createMockWrapperTransport('init-wrapper-session'); const mockMcpServer = createMockMcpServer(); From 104b64ea5aabb76f11c2c4663b2fc0fb036caaf3 Mon Sep 17 00:00:00 2001 From: Abdelrahman Awad Date: Thu, 16 Apr 2026 14:44:51 -0400 Subject: [PATCH 02/41] fix(browser): filter implausible LCP values (#20338) ## Summary - add an LCP plausibility guard to drop implausible browser-reported values above 60 seconds - apply the guard to both pageload LCP measurements and standalone LCP spans - add focused unit tests for valid and invalid LCP values --------- Co-authored-by: GPT-5 --- .../src/metrics/browserMetrics.ts | 4 +- packages/browser-utils/src/metrics/lcp.ts | 15 ++- .../src/metrics/webVitalSpans.ts | 7 +- .../browser-utils/test/metrics/lcp.test.ts | 105 ++++++++++++++++++ .../test/metrics/webVitalSpans.test.ts | 10 +- 5 files changed, 136 insertions(+), 5 deletions(-) create mode 100644 packages/browser-utils/test/metrics/lcp.test.ts diff --git a/packages/browser-utils/src/metrics/browserMetrics.ts b/packages/browser-utils/src/metrics/browserMetrics.ts index 9a00ab322e16..76e853eef5d3 100644 --- a/packages/browser-utils/src/metrics/browserMetrics.ts +++ b/packages/browser-utils/src/metrics/browserMetrics.ts @@ -22,7 +22,7 @@ import { addTtfbInstrumentationHandler, type PerformanceLongAnimationFrameTiming, } from './instrument'; -import { trackLcpAsStandaloneSpan } from './lcp'; +import { isValidLcpMetric, trackLcpAsStandaloneSpan } from './lcp'; import { resourceTimingToSpanAttributes } from './resourceTiming'; import { getBrowserPerformanceAPI, isMeasurementValue, msToSec, startAndEndSpan } from './utils'; import { getActivationStart } from './web-vitals/lib/getActivationStart'; @@ -283,7 +283,7 @@ function _trackCLS(): () => void { function _trackLCP(): () => void { return addLcpInstrumentationHandler(({ metric }) => { const entry = metric.entries[metric.entries.length - 1]; - if (!entry) { + if (!entry || !isValidLcpMetric(metric.value)) { return; } diff --git a/packages/browser-utils/src/metrics/lcp.ts b/packages/browser-utils/src/metrics/lcp.ts index a6410ac08580..c11f6bd63cbc 100644 --- a/packages/browser-utils/src/metrics/lcp.ts +++ b/packages/browser-utils/src/metrics/lcp.ts @@ -15,6 +15,15 @@ import { addLcpInstrumentationHandler } from './instrument'; import type { WebVitalReportEvent } from './utils'; import { listenForWebVitalReportEvents, msToSec, startStandaloneWebVitalSpan, supportsWebVital } from './utils'; +/** + * 60 seconds is the maximum for a plausible LCP value. + */ +export const MAX_PLAUSIBLE_LCP_DURATION = 60_000; + +export function isValidLcpMetric(lcpValue: number | undefined): lcpValue is number { + return lcpValue != null && lcpValue > 0 && lcpValue <= MAX_PLAUSIBLE_LCP_DURATION; +} + /** * Starts tracking the Largest Contentful Paint on the current page and collects the value once * @@ -34,7 +43,7 @@ export function trackLcpAsStandaloneSpan(client: Client): void { const cleanupLcpHandler = addLcpInstrumentationHandler(({ metric }) => { const entry = metric.entries[metric.entries.length - 1] as LargestContentfulPaint | undefined; - if (!entry) { + if (!entry || !isValidLcpMetric(metric.value)) { return; } standaloneLcpValue = metric.value; @@ -56,6 +65,10 @@ export function _sendStandaloneLcpSpan( pageloadSpanId: string, reportEvent: WebVitalReportEvent, ) { + if (!isValidLcpMetric(lcpValue)) { + return; + } + DEBUG_BUILD && debug.log(`Sending LCP span (${lcpValue})`); const startTime = msToSec((browserPerformanceTimeOrigin() || 0) + (entry?.startTime || 0)); diff --git a/packages/browser-utils/src/metrics/webVitalSpans.ts b/packages/browser-utils/src/metrics/webVitalSpans.ts index b342b653df97..6f6d8de3901e 100644 --- a/packages/browser-utils/src/metrics/webVitalSpans.ts +++ b/packages/browser-utils/src/metrics/webVitalSpans.ts @@ -18,6 +18,7 @@ import { WINDOW } from '../types'; import { getCachedInteractionContext, INP_ENTRY_MAP, MAX_PLAUSIBLE_INP_DURATION } from './inp'; import type { InstrumentationHandlerCallback } from './instrument'; import { addClsInstrumentationHandler, addInpInstrumentationHandler, addLcpInstrumentationHandler } from './instrument'; +import { isValidLcpMetric } from './lcp'; import type { WebVitalReportEvent } from './utils'; import { getBrowserPerformanceAPI, listenForWebVitalReportEvents, msToSec, supportsWebVital } from './utils'; import type { PerformanceEventTiming } from './instrument'; @@ -121,7 +122,7 @@ export function trackLcpAsSpan(client: Client): void { const cleanupLcpHandler = addLcpInstrumentationHandler(({ metric }) => { const entry = metric.entries[metric.entries.length - 1] as LargestContentfulPaint | undefined; - if (!entry) { + if (!entry || !isValidLcpMetric(metric.value)) { return; } lcpValue = metric.value; @@ -143,6 +144,10 @@ export function _sendLcpSpan( pageloadSpan?: Span, reportEvent?: WebVitalReportEvent, ): void { + if (!isValidLcpMetric(lcpValue)) { + return; + } + DEBUG_BUILD && debug.log(`Sending LCP span (${lcpValue})`); const performanceTimeOrigin = browserPerformanceTimeOrigin() || 0; diff --git a/packages/browser-utils/test/metrics/lcp.test.ts b/packages/browser-utils/test/metrics/lcp.test.ts new file mode 100644 index 000000000000..634b9652f816 --- /dev/null +++ b/packages/browser-utils/test/metrics/lcp.test.ts @@ -0,0 +1,105 @@ +import * as SentryCore from '@sentry/core'; +import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'; +import { _sendStandaloneLcpSpan, isValidLcpMetric, MAX_PLAUSIBLE_LCP_DURATION } from '../../src/metrics/lcp'; +import * as WebVitalUtils from '../../src/metrics/utils'; + +vi.mock('@sentry/core', async () => { + const actual = await vi.importActual('@sentry/core'); + return { + ...actual, + browserPerformanceTimeOrigin: vi.fn(), + getCurrentScope: vi.fn(), + htmlTreeAsString: vi.fn(), + }; +}); + +describe('isValidLcpMetric', () => { + it('returns true for plausible lcp values', () => { + expect(isValidLcpMetric(1)).toBe(true); + expect(isValidLcpMetric(2_500)).toBe(true); + expect(isValidLcpMetric(MAX_PLAUSIBLE_LCP_DURATION)).toBe(true); + }); + + it('returns false for implausible lcp values', () => { + expect(isValidLcpMetric(undefined)).toBe(false); + expect(isValidLcpMetric(0)).toBe(false); + expect(isValidLcpMetric(-1)).toBe(false); + expect(isValidLcpMetric(MAX_PLAUSIBLE_LCP_DURATION + 1)).toBe(false); + }); +}); + +describe('_sendStandaloneLcpSpan', () => { + const mockSpan = { + addEvent: vi.fn(), + end: vi.fn(), + }; + + const mockScope = { + getScopeData: vi.fn().mockReturnValue({ + transactionName: 'test-transaction', + }), + }; + + beforeEach(() => { + vi.mocked(SentryCore.getCurrentScope).mockReturnValue(mockScope as any); + vi.mocked(SentryCore.browserPerformanceTimeOrigin).mockReturnValue(1000); + vi.mocked(SentryCore.htmlTreeAsString).mockImplementation((node: any) => `<${node?.tagName || 'div'}>`); + vi.spyOn(WebVitalUtils, 'startStandaloneWebVitalSpan').mockReturnValue(mockSpan as any); + }); + + afterEach(() => { + vi.clearAllMocks(); + }); + + it('sends a standalone lcp span with entry data', () => { + const lcpValue = 1_234; + const mockEntry: LargestContentfulPaint = { + name: 'largest-contentful-paint', + entryType: 'largest-contentful-paint', + startTime: 100, + duration: 0, + id: 'image', + url: 'https://example.com/image.png', + size: 1234, + loadTime: 95, + renderTime: 100, + element: { tagName: 'img' } as Element, + toJSON: vi.fn(), + }; + + _sendStandaloneLcpSpan(lcpValue, mockEntry, '123', 'navigation'); + + expect(WebVitalUtils.startStandaloneWebVitalSpan).toHaveBeenCalledWith({ + name: '', + transaction: 'test-transaction', + attributes: { + 'sentry.origin': 'auto.http.browser.lcp', + 'sentry.op': 'ui.webvital.lcp', + 'sentry.exclusive_time': 0, + 'sentry.pageload.span_id': '123', + 'sentry.report_event': 'navigation', + 'lcp.element': '', + 'lcp.id': 'image', + 'lcp.url': 'https://example.com/image.png', + 'lcp.loadTime': 95, + 'lcp.renderTime': 100, + 'lcp.size': 1234, + }, + startTime: 1.1, + }); + + expect(mockSpan.addEvent).toHaveBeenCalledWith('lcp', { + 'sentry.measurement_unit': 'millisecond', + 'sentry.measurement_value': lcpValue, + }); + expect(mockSpan.end).toHaveBeenCalledWith(1.1); + }); + + it('does not send a standalone lcp span for implausibly large values', () => { + _sendStandaloneLcpSpan(MAX_PLAUSIBLE_LCP_DURATION + 1, undefined, '123', 'pagehide'); + + expect(WebVitalUtils.startStandaloneWebVitalSpan).not.toHaveBeenCalled(); + expect(mockSpan.addEvent).not.toHaveBeenCalled(); + expect(mockSpan.end).not.toHaveBeenCalled(); + }); +}); diff --git a/packages/browser-utils/test/metrics/webVitalSpans.test.ts b/packages/browser-utils/test/metrics/webVitalSpans.test.ts index 733891370fda..8b9325895e85 100644 --- a/packages/browser-utils/test/metrics/webVitalSpans.test.ts +++ b/packages/browser-utils/test/metrics/webVitalSpans.test.ts @@ -1,6 +1,7 @@ import * as SentryCore from '@sentry/core'; import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'; import * as inpModule from '../../src/metrics/inp'; +import { MAX_PLAUSIBLE_LCP_DURATION } from '../../src/metrics/lcp'; import { _emitWebVitalSpan, _sendClsSpan, _sendInpSpan, _sendLcpSpan } from '../../src/metrics/webVitalSpans'; vi.mock('@sentry/core', async () => { @@ -262,7 +263,7 @@ describe('_sendLcpSpan', () => { }); it('sends a streamed LCP span without entry data', () => { - _sendLcpSpan(0, undefined); + _sendLcpSpan(250, undefined); expect(SentryCore.startInactiveSpan).toHaveBeenCalledWith( expect.objectContaining({ @@ -271,6 +272,13 @@ describe('_sendLcpSpan', () => { }), ); }); + + it('drops implausible LCP values', () => { + _sendLcpSpan(0, undefined); + _sendLcpSpan(MAX_PLAUSIBLE_LCP_DURATION + 1, undefined); + + expect(SentryCore.startInactiveSpan).not.toHaveBeenCalled(); + }); }); describe('_sendClsSpan', () => { From 361fbf38944472bc7ee378c53e489bf17e7e3491 Mon Sep 17 00:00:00 2001 From: Charly Gomez Date: Fri, 17 Apr 2026 08:34:48 +0200 Subject: [PATCH 03/41] test(nextjs): Unskip blocked cf tests (#20356) This was broken and blocked upstream. closes https://github.com/getsentry/sentry-javascript/issues/19485 --- .../nextjs-16-cf-workers/package.json | 4 ++++ .../tests/cloudflare-runtime.test.ts | 3 +-- .../nextjs-16-cf-workers/tests/isr-routes.test.ts | 15 +++++---------- .../tests/parameterized-routes.test.ts | 12 ++++-------- .../tests/prefetch-spans.test.ts | 3 +-- .../tests/streaming-rsc-error.test.ts | 3 +-- 6 files changed, 16 insertions(+), 24 deletions(-) diff --git a/dev-packages/e2e-tests/test-applications/nextjs-16-cf-workers/package.json b/dev-packages/e2e-tests/test-applications/nextjs-16-cf-workers/package.json index 5be3e1b9a9d2..d55b0059d71d 100644 --- a/dev-packages/e2e-tests/test-applications/nextjs-16-cf-workers/package.json +++ b/dev-packages/e2e-tests/test-applications/nextjs-16-cf-workers/package.json @@ -43,6 +43,10 @@ { "build-command": "pnpm test:build-latest", "label": "nextjs-16-cf-workers (latest)" + }, + { + "build-command": "pnpm test:build-canary", + "label": "nextjs-16-cf-workers (canary)" } ] } diff --git a/dev-packages/e2e-tests/test-applications/nextjs-16-cf-workers/tests/cloudflare-runtime.test.ts b/dev-packages/e2e-tests/test-applications/nextjs-16-cf-workers/tests/cloudflare-runtime.test.ts index 8b6349a97e5f..cba53fa1970d 100644 --- a/dev-packages/e2e-tests/test-applications/nextjs-16-cf-workers/tests/cloudflare-runtime.test.ts +++ b/dev-packages/e2e-tests/test-applications/nextjs-16-cf-workers/tests/cloudflare-runtime.test.ts @@ -1,8 +1,7 @@ import { expect, test } from '@playwright/test'; import { waitForError } from '@sentry-internal/test-utils'; -// TODO(https://github.com/opennextjs/opennextjs-cloudflare/issues/1141): Unskip once opennext supports prefetch-hints.json -test.describe.skip('Cloudflare Runtime', () => { +test.describe('Cloudflare Runtime', () => { test('Should report cloudflare as the runtime in API route error events', async ({ request }) => { const errorEventPromise = waitForError('nextjs-16-cf-workers', errorEvent => { return !!errorEvent?.exception?.values?.some(value => diff --git a/dev-packages/e2e-tests/test-applications/nextjs-16-cf-workers/tests/isr-routes.test.ts b/dev-packages/e2e-tests/test-applications/nextjs-16-cf-workers/tests/isr-routes.test.ts index 1ff2d2b1cabb..b42d2cd61b93 100644 --- a/dev-packages/e2e-tests/test-applications/nextjs-16-cf-workers/tests/isr-routes.test.ts +++ b/dev-packages/e2e-tests/test-applications/nextjs-16-cf-workers/tests/isr-routes.test.ts @@ -1,8 +1,7 @@ import { expect, test } from '@playwright/test'; import { waitForTransaction } from '@sentry-internal/test-utils'; -// TODO(https://github.com/opennextjs/opennextjs-cloudflare/issues/1141): Unskip once opennext supports prefetch-hints.json -test.skip('should remove sentry-trace and baggage meta tags on ISR dynamic route page load', async ({ page }) => { +test('should remove sentry-trace and baggage meta tags on ISR dynamic route page load', async ({ page }) => { // Navigate to ISR page await page.goto('/isr-test/laptop'); @@ -14,8 +13,7 @@ test.skip('should remove sentry-trace and baggage meta tags on ISR dynamic route await expect(page.locator('meta[name="baggage"]')).toHaveCount(0); }); -// TODO(https://github.com/opennextjs/opennextjs-cloudflare/issues/1141): Unskip once opennext supports prefetch-hints.json -test.skip('should remove sentry-trace and baggage meta tags on ISR static route', async ({ page }) => { +test('should remove sentry-trace and baggage meta tags on ISR static route', async ({ page }) => { // Navigate to ISR static page await page.goto('/isr-test/static'); @@ -27,8 +25,7 @@ test.skip('should remove sentry-trace and baggage meta tags on ISR static route' await expect(page.locator('meta[name="baggage"]')).toHaveCount(0); }); -// TODO(https://github.com/opennextjs/opennextjs-cloudflare/issues/1141): Unskip once opennext supports prefetch-hints.json -test.skip('should remove meta tags for different ISR dynamic route values', async ({ page }) => { +test('should remove meta tags for different ISR dynamic route values', async ({ page }) => { // Test with 'phone' (one of the pre-generated static params) await page.goto('/isr-test/phone'); await expect(page.locator('#isr-product-id')).toHaveText('phone'); @@ -44,8 +41,7 @@ test.skip('should remove meta tags for different ISR dynamic route values', asyn await expect(page.locator('meta[name="baggage"]')).toHaveCount(0); }); -// TODO(https://github.com/opennextjs/opennextjs-cloudflare/issues/1141): Unskip once opennext supports prefetch-hints.json -test.skip('should create unique transactions for ISR pages on each visit', async ({ page }) => { +test('should create unique transactions for ISR pages on each visit', async ({ page }) => { const traceIds: string[] = []; // Load the same ISR page 5 times to ensure cached HTML meta tags are consistently removed @@ -75,8 +71,7 @@ test.skip('should create unique transactions for ISR pages on each visit', async expect(uniqueTraceIds.size).toBe(5); }); -// TODO(https://github.com/opennextjs/opennextjs-cloudflare/issues/1141): Unskip once opennext supports prefetch-hints.json -test.skip('ISR route should be identified correctly in the route manifest', async ({ page }) => { +test('ISR route should be identified correctly in the route manifest', async ({ page }) => { const transactionPromise = waitForTransaction('nextjs-16-cf-workers', async transactionEvent => { return transactionEvent.transaction === '/isr-test/:product' && transactionEvent.contexts?.trace?.op === 'pageload'; }); diff --git a/dev-packages/e2e-tests/test-applications/nextjs-16-cf-workers/tests/parameterized-routes.test.ts b/dev-packages/e2e-tests/test-applications/nextjs-16-cf-workers/tests/parameterized-routes.test.ts index 30faebe69548..b3ba64bb55c8 100644 --- a/dev-packages/e2e-tests/test-applications/nextjs-16-cf-workers/tests/parameterized-routes.test.ts +++ b/dev-packages/e2e-tests/test-applications/nextjs-16-cf-workers/tests/parameterized-routes.test.ts @@ -1,8 +1,7 @@ import { expect, test } from '@playwright/test'; import { waitForTransaction } from '@sentry-internal/test-utils'; -// TODO(https://github.com/opennextjs/opennextjs-cloudflare/issues/1141): Unskip once opennext supports prefetch-hints.json -test.skip('should create a parameterized transaction when the `app` directory is used', async ({ page }) => { +test('should create a parameterized transaction when the `app` directory is used', async ({ page }) => { const transactionPromise = waitForTransaction('nextjs-16-cf-workers', async transactionEvent => { return ( transactionEvent.transaction === '/parameterized/:one' && transactionEvent.contexts?.trace?.op === 'pageload' @@ -41,8 +40,7 @@ test.skip('should create a parameterized transaction when the `app` directory is }); }); -// TODO(https://github.com/opennextjs/opennextjs-cloudflare/issues/1141): Unskip once opennext supports prefetch-hints.json -test.skip('should create a static transaction when the `app` directory is used and the route is not parameterized', async ({ +test('should create a static transaction when the `app` directory is used and the route is not parameterized', async ({ page, }) => { const transactionPromise = waitForTransaction('nextjs-16-cf-workers', async transactionEvent => { @@ -83,8 +81,7 @@ test.skip('should create a static transaction when the `app` directory is used a }); }); -// TODO(https://github.com/opennextjs/opennextjs-cloudflare/issues/1141): Unskip once opennext supports prefetch-hints.json -test.skip('should create a partially parameterized transaction when the `app` directory is used', async ({ page }) => { +test('should create a partially parameterized transaction when the `app` directory is used', async ({ page }) => { const transactionPromise = waitForTransaction('nextjs-16-cf-workers', async transactionEvent => { return ( transactionEvent.transaction === '/parameterized/:one/beep' && transactionEvent.contexts?.trace?.op === 'pageload' @@ -123,8 +120,7 @@ test.skip('should create a partially parameterized transaction when the `app` di }); }); -// TODO(https://github.com/opennextjs/opennextjs-cloudflare/issues/1141): Unskip once opennext supports prefetch-hints.json -test.skip('should create a nested parameterized transaction when the `app` directory is used.', async ({ page }) => { +test('should create a nested parameterized transaction when the `app` directory is used.', async ({ page }) => { const transactionPromise = waitForTransaction('nextjs-16-cf-workers', async transactionEvent => { return ( transactionEvent.transaction === '/parameterized/:one/beep/:two' && diff --git a/dev-packages/e2e-tests/test-applications/nextjs-16-cf-workers/tests/prefetch-spans.test.ts b/dev-packages/e2e-tests/test-applications/nextjs-16-cf-workers/tests/prefetch-spans.test.ts index 59ec6d504382..f48158a54697 100644 --- a/dev-packages/e2e-tests/test-applications/nextjs-16-cf-workers/tests/prefetch-spans.test.ts +++ b/dev-packages/e2e-tests/test-applications/nextjs-16-cf-workers/tests/prefetch-spans.test.ts @@ -2,8 +2,7 @@ import { expect, test } from '@playwright/test'; import { waitForTransaction } from '@sentry-internal/test-utils'; import { isDevMode } from './isDevMode'; -// TODO(https://github.com/opennextjs/opennextjs-cloudflare/issues/1141): Unskip once opennext supports prefetch-hints.json -test.skip('Prefetch client spans should have a http.request.prefetch attribute', async ({ page }) => { +test('Prefetch client spans should have a http.request.prefetch attribute', async ({ page }) => { test.skip(isDevMode, "Prefetch requests don't have the prefetch header in dev mode"); const pageloadTransactionPromise = waitForTransaction('nextjs-16-cf-workers', async transactionEvent => { diff --git a/dev-packages/e2e-tests/test-applications/nextjs-16-cf-workers/tests/streaming-rsc-error.test.ts b/dev-packages/e2e-tests/test-applications/nextjs-16-cf-workers/tests/streaming-rsc-error.test.ts index 38cb628cb9ce..ba42d9fadbb9 100644 --- a/dev-packages/e2e-tests/test-applications/nextjs-16-cf-workers/tests/streaming-rsc-error.test.ts +++ b/dev-packages/e2e-tests/test-applications/nextjs-16-cf-workers/tests/streaming-rsc-error.test.ts @@ -1,8 +1,7 @@ import { expect, test } from '@playwright/test'; import { waitForError, waitForTransaction } from '@sentry-internal/test-utils'; -// TODO(https://github.com/opennextjs/opennextjs-cloudflare/issues/1141): Unskip once opennext supports prefetch-hints.json -test.skip('Should capture errors for crashing streaming promises in server components when `Sentry.captureRequestError` is added to the `onRequestError` hook', async ({ +test('Should capture errors for crashing streaming promises in server components when `Sentry.captureRequestError` is added to the `onRequestError` hook', async ({ page, }) => { const errorEventPromise = waitForError('nextjs-16-cf-workers', errorEvent => { From 2137d1adee37d189f40f2bebcddff070e28a0ea0 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 17 Apr 2026 08:54:36 +0200 Subject: [PATCH 04/41] feat(deps): Bump protobufjs from 7.5.4 to 7.5.5 (#20372) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Bumps [protobufjs](https://github.com/protobufjs/protobuf.js) from 7.5.4 to 7.5.5.
Changelog

Sourced from protobufjs's changelog.

Changelog

8.0.1 (2026-03-11)

Bug Fixes

  • bump protobufjs dependency version for cli package (#2128) (549b05e)
  • correct json syntax in tsconfig.json (#2120) (8065625)
  • descriptor: guard oneof index for non-Type parents (#2122) (1cac5cf)
  • do not allow setting proto in Message constructor (#2126) (f05e3c3)
  • filter invalid characters from the type name (#2127) (535df44)

8.0.0 (2025-12-16)

⚠ BREAKING CHANGES

  • add Edition 2024 Support (#2060)

Features

Commits
Maintainer changes

This version was pushed to npm by fenster, a new releaser for protobufjs since your current version.


[![Dependabot compatibility score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=protobufjs&package-manager=npm_and_yarn&previous-version=7.5.4&new-version=7.5.5)](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores) Dependabot will resolve any conflicts with this PR as long as you don't alter it yourself. You can also trigger a rebase manually by commenting `@dependabot rebase`. [//]: # (dependabot-automerge-start) [//]: # (dependabot-automerge-end) ---
Dependabot commands and options
You can trigger Dependabot actions by commenting on this PR: - `@dependabot rebase` will rebase this PR - `@dependabot recreate` will recreate this PR, overwriting any edits that have been made to it - `@dependabot show ignore conditions` will show all of the ignore conditions of the specified dependency - `@dependabot ignore this major version` will close this PR and stop Dependabot creating any more for this major version (unless you reopen the PR or upgrade to it yourself) - `@dependabot ignore this minor version` will close this PR and stop Dependabot creating any more for this minor version (unless you reopen the PR or upgrade to it yourself) - `@dependabot ignore this dependency` will close this PR and stop Dependabot creating any more for this dependency (unless you reopen the PR or upgrade to it yourself) You can disable automated security fix PRs for this repo from the [Security Alerts page](https://github.com/getsentry/sentry-javascript/network/alerts).
Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- yarn.lock | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/yarn.lock b/yarn.lock index a718a01b0bf2..8a75ed3d29e6 100644 --- a/yarn.lock +++ b/yarn.lock @@ -25352,9 +25352,9 @@ property-information@^7.0.0: integrity sha512-TwEZ+X+yCJmYfL7TPUOcvBZ4QfoT5YenQiJuX//0th53DE6w0xxLEtfK3iyryQFddXuvkIk51EEgrJQ0WJkOmQ== protobufjs@^7.0.0: - version "7.5.4" - resolved "https://registry.yarnpkg.com/protobufjs/-/protobufjs-7.5.4.tgz#885d31fe9c4b37f25d1bb600da30b1c5b37d286a" - integrity sha512-CvexbZtbov6jW2eXAvLukXjXUW1TzFaivC46BpWc/3BpcCysb5Vffu+B3XHMm8lVEuy2Mm4XGex8hBSg1yapPg== + version "7.5.5" + resolved "https://registry.yarnpkg.com/protobufjs/-/protobufjs-7.5.5.tgz#b7089ca4410374c75150baf277353ef76db69f96" + integrity sha512-3wY1AxV+VBNW8Yypfd1yQY9pXnqTAN+KwQxL8iYm3/BjKYMNg4i0owhEe26PWDOMaIrzeeF98Lqd5NGz4omiIg== dependencies: "@protobufjs/aspromise" "^1.1.2" "@protobufjs/base64" "^1.1.2" From 313cf8291b67b2abcc090fe6f8b88aef14df2363 Mon Sep 17 00:00:00 2001 From: Francesco Gringl-Novy Date: Fri, 17 Apr 2026 09:18:16 +0200 Subject: [PATCH 05/41] chore(tests): Use verdaccio as node process instead of docker image (#20336) Extracted out from https://github.com/getsentry/sentry-javascript/pull/20270 This rewrites how we run verdaccio to run this as node process instead of via docker. For this, I pinned the latest verdaccio version in the e2e package, and run it via node APIs as a detached process. This also means no more docker requirement to run this stuff locally. Works both with `yarn test:run xxx` for local usage as well as with `yarn test:prepare` for CI usage. --- .../actions/install-dependencies/action.yml | 2 +- .gitignore | 3 + dev-packages/e2e-tests/lib/constants.ts | 4 - dev-packages/e2e-tests/lib/publishPackages.ts | 33 +- dev-packages/e2e-tests/package.json | 4 +- dev-packages/e2e-tests/prepare.ts | 13 +- dev-packages/e2e-tests/registrySetup.ts | 158 ++- dev-packages/e2e-tests/run.ts | 25 +- .../e2e-tests/verdaccio-config/config.yaml | 6 +- dev-packages/e2e-tests/verdaccio-runner.mjs | 26 + package.json | 4 +- yarn.lock | 1175 ++++++++++++++--- 12 files changed, 1207 insertions(+), 246 deletions(-) delete mode 100644 dev-packages/e2e-tests/lib/constants.ts create mode 100644 dev-packages/e2e-tests/verdaccio-runner.mjs diff --git a/.github/actions/install-dependencies/action.yml b/.github/actions/install-dependencies/action.yml index cfa664b1d219..a862cd355af2 100644 --- a/.github/actions/install-dependencies/action.yml +++ b/.github/actions/install-dependencies/action.yml @@ -15,7 +15,7 @@ runs: shell: bash - name: Check dependency cache - uses: actions/cache@v4 + uses: actions/cache@v5 id: cache_dependencies with: path: ${{ env.CACHED_DEPENDENCY_PATHS }} diff --git a/.gitignore b/.gitignore index 464a09a9980e..d872a9adc27a 100644 --- a/.gitignore +++ b/.gitignore @@ -39,6 +39,9 @@ local.log .rpt2_cache +# verdaccio local registry (e2e tests) +dev-packages/e2e-tests/verdaccio-config/storage/ + lint-results.json trace.zip diff --git a/dev-packages/e2e-tests/lib/constants.ts b/dev-packages/e2e-tests/lib/constants.ts deleted file mode 100644 index b5476ba4614d..000000000000 --- a/dev-packages/e2e-tests/lib/constants.ts +++ /dev/null @@ -1,4 +0,0 @@ -export const TEST_REGISTRY_CONTAINER_NAME = 'verdaccio-e2e-test-registry'; -export const DEFAULT_BUILD_TIMEOUT_SECONDS = 60 * 5; -export const DEFAULT_TEST_TIMEOUT_SECONDS = 60 * 2; -export const VERDACCIO_VERSION = '5.22.1'; diff --git a/dev-packages/e2e-tests/lib/publishPackages.ts b/dev-packages/e2e-tests/lib/publishPackages.ts index 5aed3a76e77a..8b0681d5d1f6 100644 --- a/dev-packages/e2e-tests/lib/publishPackages.ts +++ b/dev-packages/e2e-tests/lib/publishPackages.ts @@ -1,15 +1,34 @@ /* eslint-disable no-console */ -import * as childProcess from 'child_process'; +import { spawn } from 'child_process'; import { readFileSync } from 'fs'; import { globSync } from 'glob'; import * as path from 'path'; const repositoryRoot = path.resolve(__dirname, '../../..'); +function npmPublish(tarballPath: string, npmrc: string): Promise { + return new Promise((resolve, reject) => { + const child = spawn('npm', ['--userconfig', npmrc, 'publish', tarballPath], { + cwd: repositoryRoot, + stdio: 'inherit', + }); + + child.on('error', reject); + child.on('close', code => { + if (code === 0) { + resolve(); + } else { + reject(new Error(`Error publishing tarball ${tarballPath}`)); + } + }); + }); +} + /** * Publishes all built Sentry package tarballs to the local Verdaccio test registry. + * Uses async `npm publish` so an in-process Verdaccio can still handle HTTP on the event loop. */ -export function publishPackages(): void { +export async function publishPackages(): Promise { const version = (JSON.parse(readFileSync(path.join(__dirname, '../package.json'), 'utf8')) as { version: string }) .version; @@ -28,14 +47,6 @@ export function publishPackages(): void { for (const tarballPath of packageTarballPaths) { console.log(`Publishing tarball ${tarballPath} ...`); - const result = childProcess.spawnSync('npm', ['--userconfig', npmrc, 'publish', tarballPath], { - cwd: repositoryRoot, - encoding: 'utf8', - stdio: 'inherit', - }); - - if (result.status !== 0) { - throw new Error(`Error publishing tarball ${tarballPath}`); - } + await npmPublish(tarballPath, npmrc); } } diff --git a/dev-packages/e2e-tests/package.json b/dev-packages/e2e-tests/package.json index 827dde43679f..ac1f7ddf23f7 100644 --- a/dev-packages/e2e-tests/package.json +++ b/dev-packages/e2e-tests/package.json @@ -13,7 +13,8 @@ "test:validate-test-app-setups": "ts-node validate-test-app-setups.ts", "test:prepare": "ts-node prepare.ts", "test:validate": "run-s test:validate-configuration test:validate-test-app-setups", - "clean": "rimraf tmp node_modules && yarn clean:test-applications && yarn clean:pnpm", + "clean:verdaccio": "sh -c 'pkill -f verdaccio-runner.mjs 2>/dev/null || true'", + "clean": "yarn clean:verdaccio && rimraf tmp node_modules verdaccio-config/storage && yarn clean:test-applications && yarn clean:pnpm", "ci:build-matrix": "ts-node ./lib/getTestMatrix.ts", "ci:build-matrix-optional": "ts-node ./lib/getTestMatrix.ts --optional=true", "ci:copy-to-temp": "ts-node ./ciCopyToTemp.ts", @@ -28,6 +29,7 @@ "glob": "^13.0.6", "rimraf": "^6.1.3", "ts-node": "10.9.2", + "verdaccio": "6.5.0", "yaml": "2.8.3" }, "volta": { diff --git a/dev-packages/e2e-tests/prepare.ts b/dev-packages/e2e-tests/prepare.ts index d887ef310502..f0bcca7ade5f 100644 --- a/dev-packages/e2e-tests/prepare.ts +++ b/dev-packages/e2e-tests/prepare.ts @@ -6,13 +6,10 @@ async function run(): Promise { // Load environment variables from .env file locally dotenv.config(); - try { - registrySetup(); - } catch (error) { - console.error(error); - process.exit(1); - } + await registrySetup({ daemonize: true }); } -// eslint-disable-next-line @typescript-eslint/no-floating-promises -run(); +run().catch(error => { + console.error(error); + process.exit(1); +}); diff --git a/dev-packages/e2e-tests/registrySetup.ts b/dev-packages/e2e-tests/registrySetup.ts index 6c521e619f76..fade430b6b55 100644 --- a/dev-packages/e2e-tests/registrySetup.ts +++ b/dev-packages/e2e-tests/registrySetup.ts @@ -1,50 +1,140 @@ /* eslint-disable no-console */ -import * as childProcess from 'child_process'; -import { TEST_REGISTRY_CONTAINER_NAME, VERDACCIO_VERSION } from './lib/constants'; +import { spawn, spawnSync, type ChildProcess } from 'child_process'; +import * as fs from 'fs'; +import * as http from 'http'; +import * as path from 'path'; import { publishPackages } from './lib/publishPackages'; -// https://docs.github.com/en/actions/using-workflows/workflow-commands-for-github-actions#grouping-log-lines -function groupCIOutput(groupTitle: string, fn: () => void): void { +const VERDACCIO_PORT = 4873; + +let verdaccioChild: ChildProcess | undefined; + +export interface RegistrySetupOptions { + /** + * When true, Verdaccio is spawned detached with stdio disconnected from the parent, then + * the child is unref'd after a successful setup so the parent can exit while the registry + * keeps running (e.g. `yarn test:prepare` then installs against 127.0.0.1:4873). + */ + daemonize?: boolean; +} + +/** Stops any Verdaccio runner from a previous prepare/run so port 4873 is free. */ +function killStrayVerdaccioRunner(): void { + spawnSync('pkill', ['-f', 'verdaccio-runner.mjs'], { stdio: 'ignore' }); +} + +async function groupCIOutput(groupTitle: string, fn: () => void | Promise): Promise { if (process.env.CI) { console.log(`::group::${groupTitle}`); - fn(); - console.log('::endgroup::'); + try { + await Promise.resolve(fn()); + } finally { + console.log('::endgroup::'); + } } else { - fn(); + await Promise.resolve(fn()); } } -export function registrySetup(): void { - groupCIOutput('Test Registry Setup', () => { - // Stop test registry container (Verdaccio) if it was already running - childProcess.spawnSync('docker', ['stop', TEST_REGISTRY_CONTAINER_NAME], { stdio: 'ignore' }); - console.log('Stopped previously running test registry'); - - // Start test registry (Verdaccio) - const startRegistryProcessResult = childProcess.spawnSync( - 'docker', - [ - 'run', - '--detach', - '--rm', - '--name', - TEST_REGISTRY_CONTAINER_NAME, - '-p', - '4873:4873', - '-v', - `${__dirname}/verdaccio-config:/verdaccio/conf`, - `verdaccio/verdaccio:${VERDACCIO_VERSION}`, - ], - { encoding: 'utf8', stdio: 'inherit' }, - ); - - if (startRegistryProcessResult.status !== 0) { - throw new Error('Start Registry Process failed.'); +function waitUntilVerdaccioResponds(maxRetries: number = 60): Promise { + const pingUrl = `http://127.0.0.1:${VERDACCIO_PORT}/-/ping`; + + function tryOnce(): Promise { + return new Promise(resolve => { + const req = http.get(pingUrl, res => { + res.resume(); + resolve((res.statusCode ?? 0) > 0 && (res.statusCode ?? 500) < 500); + }); + req.on('error', () => resolve(false)); + req.setTimeout(2000, () => { + req.destroy(); + resolve(false); + }); + }); + } + + return (async () => { + for (let i = 0; i < maxRetries; i++) { + if (await tryOnce()) { + return; + } + await new Promise(r => setTimeout(r, 1000)); } + throw new Error('Verdaccio did not start in time.'); + })(); +} + +function startVerdaccioChild(configPath: string, port: number, daemonize: boolean): ChildProcess { + const runnerPath = path.join(__dirname, 'verdaccio-runner.mjs'); + const verbose = process.env.E2E_VERDACCIO_VERBOSE === '1'; + return spawn(process.execPath, [runnerPath, configPath, String(port)], { + detached: daemonize, + stdio: daemonize && !verbose ? 'ignore' : 'inherit', + }); +} + +async function stopVerdaccioChild(): Promise { + const child = verdaccioChild; + verdaccioChild = undefined; + if (!child || child.killed) { + return; + } + child.kill('SIGTERM'); + await new Promise(resolve => { + const timeoutId = setTimeout(resolve, 5000); + child.once('exit', () => { + clearTimeout(timeoutId); + resolve(); + }); + }); +} + +/** Drop the child handle so the parent process can exit; Verdaccio keeps running. */ +function detachVerdaccioRunner(): void { + const child = verdaccioChild; + verdaccioChild = undefined; + if (child && !child.killed) { + child.unref(); + } +} + +export async function registrySetup(options: RegistrySetupOptions = {}): Promise { + const { daemonize = false } = options; + await groupCIOutput('Test Registry Setup', async () => { + killStrayVerdaccioRunner(); + + const configPath = path.join(__dirname, 'verdaccio-config', 'config.yaml'); + const storagePath = path.join(__dirname, 'verdaccio-config', 'storage'); + + // Clear previous registry storage to ensure a fresh state + fs.rmSync(storagePath, { recursive: true, force: true }); - publishPackages(); + // Verdaccio runs in a child process so tarball uploads are not starved by the + // same Node event loop as ts-node (in-process runServer + npm publish could hang). + console.log('Starting Verdaccio...'); + + verdaccioChild = startVerdaccioChild(configPath, VERDACCIO_PORT, daemonize); + + try { + await waitUntilVerdaccioResponds(60); + console.log('Verdaccio is ready'); + + await publishPackages(); + } catch (error) { + await stopVerdaccioChild(); + throw error; + } }); + if (daemonize) { + detachVerdaccioRunner(); + } + console.log(''); console.log(''); } + +export async function registryCleanup(): Promise { + await stopVerdaccioChild(); + killStrayVerdaccioRunner(); +} diff --git a/dev-packages/e2e-tests/run.ts b/dev-packages/e2e-tests/run.ts index 443ccf806b73..8542b99c3708 100644 --- a/dev-packages/e2e-tests/run.ts +++ b/dev-packages/e2e-tests/run.ts @@ -6,7 +6,7 @@ import { sync as globSync } from 'glob'; import { tmpdir } from 'os'; import { join, resolve } from 'path'; import { copyToTemp } from './lib/copyToTemp'; -import { registrySetup } from './registrySetup'; +import { registryCleanup, registrySetup } from './registrySetup'; interface SentryTestVariant { 'build-command': string; @@ -184,14 +184,16 @@ async function run(): Promise { ...envVarsToInject, }; + const skipRegistry = !!process.env.SKIP_REGISTRY; + try { + if (!skipRegistry) { + await registrySetup(); + } + console.log('Cleaning test-applications...'); console.log(''); - if (!process.env.SKIP_REGISTRY) { - registrySetup(); - } - await asyncExec('pnpm clean:test-applications', { env, cwd: __dirname }); await asyncExec('pnpm cache delete "@sentry/*"', { env, cwd: __dirname }); @@ -247,11 +249,14 @@ async function run(): Promise { // clean up (although this is tmp, still nice to do) await rm(tmpDirPath, { recursive: true }); } - } catch (error) { - console.error(error); - process.exit(1); + } finally { + if (!skipRegistry) { + await registryCleanup(); + } } } -// eslint-disable-next-line @typescript-eslint/no-floating-promises -run(); +run().catch(error => { + console.error(error); + process.exit(1); +}); diff --git a/dev-packages/e2e-tests/verdaccio-config/config.yaml b/dev-packages/e2e-tests/verdaccio-config/config.yaml index 9d726fdf772f..d80ed2aa429f 100644 --- a/dev-packages/e2e-tests/verdaccio-config/config.yaml +++ b/dev-packages/e2e-tests/verdaccio-config/config.yaml @@ -8,13 +8,13 @@ # https://github.com/verdaccio/verdaccio/tree/master/conf # -# path to a directory with all packages -storage: /verdaccio/storage/data +# Repo-local storage (relative to this file). Absolute /verdaccio/... matches Docker-only templates and is not writable on typical dev machines. +storage: ./storage/data # https://verdaccio.org/docs/configuration#authentication auth: htpasswd: - file: /verdaccio/storage/htpasswd + file: ./storage/htpasswd # https://verdaccio.org/docs/configuration#uplinks # a list of other known repositories we can talk to diff --git a/dev-packages/e2e-tests/verdaccio-runner.mjs b/dev-packages/e2e-tests/verdaccio-runner.mjs new file mode 100644 index 000000000000..7de53e472d5a --- /dev/null +++ b/dev-packages/e2e-tests/verdaccio-runner.mjs @@ -0,0 +1,26 @@ +/* eslint-disable no-console */ +import { createRequire } from 'node:module'; + +const require = createRequire(import.meta.url); +const { runServer } = require('verdaccio'); + +const configPath = process.argv[2]; +const port = parseInt(process.argv[3], 10); + +if (!configPath || !Number.isFinite(port)) { + console.error('verdaccio-runner: expected argv'); + process.exit(1); +} + +try { + // runServer resolves to the Express app; binding errors are emitted on the + // http.Server returned by app.listen(), not on the app itself. + const app = await runServer(configPath, { listenArg: String(port) }); + await new Promise((resolve, reject) => { + const httpServer = app.listen(port, '127.0.0.1', () => resolve()); + httpServer.once('error', reject); + }); +} catch (err) { + console.error(err); + process.exit(1); +} diff --git a/package.json b/package.json index 3d211ca42067..511970b04c2f 100644 --- a/package.json +++ b/package.json @@ -158,7 +158,9 @@ "wide-align/string-width": "4.2.3", "cliui/wrap-ansi": "7.0.0", "sucrase": "getsentry/sucrase#es2020-polyfills", - "**/express/path-to-regexp": "0.1.12" + "**/express/path-to-regexp": "0.1.12", + "**/@verdaccio/local-storage-legacy/lodash": "4.17.23", + "**/@verdaccio/core/minimatch": "~7.4.9" }, "version": "0.0.0", "name": "sentry-javascript" diff --git a/yarn.lock b/yarn.lock index 8a75ed3d29e6..cc45a89c5703 100644 --- a/yarn.lock +++ b/yarn.lock @@ -3158,6 +3158,30 @@ resolved "https://registry.yarnpkg.com/@csstools/selector-specificity/-/selector-specificity-2.2.0.tgz#2cbcf822bf3764c9658c4d2e568bd0c0cb748016" integrity sha512-+OJ9konv95ClSTOJCmMZqpd5+YGsB2S+x6w3E1oaM8UuR5j8nTNHYSz8c9BEPGDOCMQYIEEGlVPj/VY64iTbGw== +"@cypress/request@3.0.10": + version "3.0.10" + resolved "https://registry.yarnpkg.com/@cypress/request/-/request-3.0.10.tgz#e09c695e8460a82acafe6cfaf089cf2ca06dc054" + integrity sha512-hauBrOdvu08vOsagkZ/Aju5XuiZx6ldsLfByg1htFeldhex+PeMrYauANzFsMJeAA0+dyPLbDoX2OYuvVoLDkQ== + dependencies: + aws-sign2 "~0.7.0" + aws4 "^1.8.0" + caseless "~0.12.0" + combined-stream "~1.0.6" + extend "~3.0.2" + forever-agent "~0.6.1" + form-data "~4.0.4" + http-signature "~1.4.0" + is-typedarray "~1.0.0" + isstream "~0.1.2" + json-stringify-safe "~5.0.1" + mime-types "~2.1.19" + performance-now "^2.1.0" + qs "~6.14.1" + safe-buffer "^5.1.2" + tough-cookie "^5.0.0" + tunnel-agent "^0.6.0" + uuid "^8.3.2" + "@dabh/diagnostics@^2.0.2": version "2.0.3" resolved "https://registry.yarnpkg.com/@dabh/diagnostics/-/diagnostics-2.0.3.tgz#7f7e97ee9a725dffc7808d93668cc984e1dc477a" @@ -6784,6 +6808,11 @@ "@parcel/watcher-win32-ia32" "2.5.1" "@parcel/watcher-win32-x64" "2.5.1" +"@pinojs/redact@^0.4.0": + version "0.4.0" + resolved "https://registry.yarnpkg.com/@pinojs/redact/-/redact-0.4.0.tgz#c3de060dd12640dcc838516aa2a6803cc7b2e9d6" + integrity sha512-k2ENnmBugE/rzQfEcdWHcCY+/FM3VLzH9cYEsbdsoqrvzAKRhUZeRNhAZvB8OitQJ1TBed3yqWtdjzS6wJKBwg== + "@pkgjs/parseargs@^0.11.0": version "0.11.0" resolved "https://registry.yarnpkg.com/@pkgjs/parseargs/-/parseargs-0.11.0.tgz#a77ea742fab25775145434eb1d2328cf5013ac33" @@ -7811,6 +7840,11 @@ resolved "https://registry.yarnpkg.com/@sinclair/typebox/-/typebox-0.34.48.tgz#75b0ead87e59e1adbd6dccdc42bad4fddee73b59" integrity sha512-kKJTNuK3AQOrgjjotVxMrCn1sUJwM76wMszfq1kdU4uYVJjvEWuFQ6HgvLt4Xz3fSmZlTOxJ/Ie13KnIcWQXFA== +"@sindresorhus/is@4.6.0": + version "4.6.0" + resolved "https://registry.yarnpkg.com/@sindresorhus/is/-/is-4.6.0.tgz#3c7c9c46e678feefe7a2e5bb609d3dbd665ffb3f" + integrity sha512-t09vSN3MdfsyCHoFcTRCH/iUtG7OJ0CsjzB8cjAmKc/va/kIgeDI/TxsigdncE/4be734m0cvIYwNaV4i2XqAw== + "@sindresorhus/is@^7.0.2": version "7.0.2" resolved "https://registry.yarnpkg.com/@sindresorhus/is/-/is-7.0.2.tgz#a0df078a8d29f9741503c5a9c302de474ec8564a" @@ -8659,6 +8693,13 @@ "@swc/counter" "^0.1.3" tslib "^2.4.0" +"@szmarczak/http-timer@4.0.6": + version "4.0.6" + resolved "https://registry.yarnpkg.com/@szmarczak/http-timer/-/http-timer-4.0.6.tgz#b4a914bb62e7c272d4e5989fe4440f812ab1d807" + integrity sha512-4BAffykYOgO+5nzBWYwE3W90sBgLJoUPRWWcL8wlyiM8IB8ipJz3UMJ9KXQd1RKQXpKp8Tutn80HZtWsu2u76w== + dependencies: + defer-to-connect "^2.0.0" + "@tanstack/directive-functions-plugin@1.121.21": version "1.121.21" resolved "https://registry.yarnpkg.com/@tanstack/directive-functions-plugin/-/directive-functions-plugin-1.121.21.tgz#a5b265d6eb265b981a7b3e41e73c1e082a0948bc" @@ -9725,6 +9766,13 @@ resolved "https://registry.yarnpkg.com/@types/resolve/-/resolve-1.20.2.tgz#97d26e00cd4a0423b4af620abecf3e6f442b7975" integrity sha512-60BCwRFOZCQhDncwQdxxeOEEkbc5dIMccYLwbxsS4TUNeVECQ/pBJ0j09mrHOl/JJvpRPGwO9SvE4nR2Nb/a4Q== +"@types/responselike@1.0.0": + version "1.0.0" + resolved "https://registry.yarnpkg.com/@types/responselike/-/responselike-1.0.0.tgz#251f4fe7d154d2bad125abe1b429b23afd262e29" + integrity sha512-85Y2BjiufFzaMIlvJDvTTB8Fxl2xfLo4HgmHzVBz08w4wDePCTjYw66PdrolO0kzli3yam/YCgRufyo1DdQVTA== + dependencies: + "@types/node" "*" + "@types/retry@0.12.0": version "0.12.0" resolved "https://registry.yarnpkg.com/@types/retry/-/retry-0.12.0.tgz#2b35eccfcee7d38cd72ad99232fbd58bffb3c84d" @@ -10102,6 +10150,208 @@ picomatch "^4.0.2" resolve-from "^5.0.0" +"@verdaccio/auth@8.0.0-next-8.37": + version "8.0.0-next-8.37" + resolved "https://registry.yarnpkg.com/@verdaccio/auth/-/auth-8.0.0-next-8.37.tgz#0113de30ff0c66284adde0ec99c045f991a97e44" + integrity sha512-wvKPnjDZReT0gSxntUbcOYl23m2mHeMT9a/uhRMdw3pbraSgozatnf3UuoTd6Uyfm3vn+HHAHqzudUn1+yD4rw== + dependencies: + "@verdaccio/config" "8.0.0-next-8.37" + "@verdaccio/core" "8.0.0-next-8.37" + "@verdaccio/loaders" "8.0.0-next-8.27" + "@verdaccio/signature" "8.0.0-next-8.29" + debug "4.4.3" + lodash "4.18.1" + verdaccio-htpasswd "13.0.0-next-8.37" + +"@verdaccio/config@8.0.0-next-8.37": + version "8.0.0-next-8.37" + resolved "https://registry.yarnpkg.com/@verdaccio/config/-/config-8.0.0-next-8.37.tgz#b1432470f243de75ed72c97237851f23e683f59a" + integrity sha512-SbmDMJpora293B+TDYfxJL5LEaFh7gdh0MmkPJCBkmGlRPmynTfHcQzVzAll3+IMYFkrf1zZtq/qlgorjaoFoQ== + dependencies: + "@verdaccio/core" "8.0.0-next-8.37" + debug "4.4.3" + js-yaml "4.1.1" + lodash "4.18.1" + +"@verdaccio/core@8.0.0-next-8.21": + version "8.0.0-next-8.21" + resolved "https://registry.yarnpkg.com/@verdaccio/core/-/core-8.0.0-next-8.21.tgz#7eaa7ca107cc2889d4c87401d9d96bc6365c8341" + integrity sha512-n3Y8cqf84cwXxUUdTTfEJc8fV55PONPKijCt2YaC0jilb5qp1ieB3d4brqTOdCdXuwkmnG2uLCiGpUd/RuSW0Q== + dependencies: + ajv "8.17.1" + http-errors "2.0.0" + http-status-codes "2.3.0" + minimatch "7.4.6" + process-warning "1.0.0" + semver "7.7.2" + +"@verdaccio/core@8.0.0-next-8.37": + version "8.0.0-next-8.37" + resolved "https://registry.yarnpkg.com/@verdaccio/core/-/core-8.0.0-next-8.37.tgz#4c9d3cf85a74e19e8673802de9c8f8548b27d93e" + integrity sha512-R8rDEa2mPjfHhEK2tWTMFnrfvyNmTd5ZrNz9X5/EiFVJPr/+oo9cTZkDXzY9+KREJUUIUFili4qynmBt0lw8nw== + dependencies: + ajv "8.18.0" + http-errors "2.0.1" + http-status-codes "2.3.0" + minimatch "7.4.9" + process-warning "1.0.0" + semver "7.7.4" + +"@verdaccio/file-locking@10.3.1": + version "10.3.1" + resolved "https://registry.yarnpkg.com/@verdaccio/file-locking/-/file-locking-10.3.1.tgz#cfc2436e0715954e0965f97dfcd87381d116f749" + integrity sha512-oqYLfv3Yg3mAgw9qhASBpjD50osj2AX4IwbkUtyuhhKGyoFU9eZdrbeW6tpnqUnj6yBMtAPm2eGD4BwQuX400g== + dependencies: + lockfile "1.0.4" + +"@verdaccio/file-locking@13.0.0-next-8.7": + version "13.0.0-next-8.7" + resolved "https://registry.yarnpkg.com/@verdaccio/file-locking/-/file-locking-13.0.0-next-8.7.tgz#0b1a93bf7c29572ac20abdc9d20f28c86c072e30" + integrity sha512-XL12Okp4YQd0ogYMyGc+JrqrtVC+76V5hUGCK+s/VluSFSZaJQiLs4MoUPsKfwGhqXHCAm1JcNaz4L5LoXNbjQ== + dependencies: + lockfile "1.0.4" + +"@verdaccio/hooks@8.0.0-next-8.37": + version "8.0.0-next-8.37" + resolved "https://registry.yarnpkg.com/@verdaccio/hooks/-/hooks-8.0.0-next-8.37.tgz#cd285957e7964c28fb77dc5c183905516655c122" + integrity sha512-n2t6fjXqSA+y402zO2Yh5UEe+rzMf1jhglj46MQf7IswCaST/SGLlJ5VCl6bU8LGbSr9FOz7BAtUXc64i3oCmA== + dependencies: + "@verdaccio/core" "8.0.0-next-8.37" + "@verdaccio/logger" "8.0.0-next-8.37" + debug "4.4.3" + got-cjs "12.5.4" + handlebars "4.7.9" + +"@verdaccio/loaders@8.0.0-next-8.27": + version "8.0.0-next-8.27" + resolved "https://registry.yarnpkg.com/@verdaccio/loaders/-/loaders-8.0.0-next-8.27.tgz#3c32ce116919039557ad3eb905694939b44885c2" + integrity sha512-bDfHHCDrOCSdskAKeKxPArUi5aGXtsxEpRvO8MzghX50g1zJnVzLF1KEbsEY9ScFqGZAVYtZTcutysA0VOQ0Rg== + dependencies: + "@verdaccio/core" "8.0.0-next-8.37" + debug "4.4.3" + lodash "4.18.1" + +"@verdaccio/local-storage-legacy@11.1.1": + version "11.1.1" + resolved "https://registry.yarnpkg.com/@verdaccio/local-storage-legacy/-/local-storage-legacy-11.1.1.tgz#86c73bc723e7e3d9c65953d6eb9e300ca0223653" + integrity sha512-P6ahH2W6/KqfJFKP+Eid7P134FHDLNvHa+i8KVgRVBeo2/IXb6FEANpM1mCVNvPANu0LCAmNJBOXweoUKViaoA== + dependencies: + "@verdaccio/core" "8.0.0-next-8.21" + "@verdaccio/file-locking" "10.3.1" + "@verdaccio/streams" "10.2.1" + async "3.2.6" + debug "4.4.1" + lodash "4.17.21" + lowdb "1.0.0" + mkdirp "1.0.4" + +"@verdaccio/logger-commons@8.0.0-next-8.37": + version "8.0.0-next-8.37" + resolved "https://registry.yarnpkg.com/@verdaccio/logger-commons/-/logger-commons-8.0.0-next-8.37.tgz#3fc1c2d244843189b52b498b8d3950d9e104b8c4" + integrity sha512-HVt7ttnKgioERB1lCc1UnqnJMJ8skAeivLe49uq3wEG3QtruQGCct5nosj+q1pjx8SaYpQA+qxs1+4UDddprVA== + dependencies: + "@verdaccio/core" "8.0.0-next-8.37" + "@verdaccio/logger-prettify" "8.0.0-next-8.5" + colorette "2.0.20" + debug "4.4.3" + +"@verdaccio/logger-prettify@8.0.0-next-8.5": + version "8.0.0-next-8.5" + resolved "https://registry.yarnpkg.com/@verdaccio/logger-prettify/-/logger-prettify-8.0.0-next-8.5.tgz#4f11de2aa1da5268c1e7a1fc4789d51f2179ff11" + integrity sha512-zCsvdKgUQx2mSu7fnDOkA2r2QX6yMmBDsXGmqXmoov/cZ89deREn0uC7xIX7/YEo8EspBoXiUGzqI+S+qUWPyw== + dependencies: + colorette "2.0.20" + dayjs "1.11.18" + lodash "4.18.1" + on-exit-leak-free "2.1.2" + pino-abstract-transport "1.2.0" + sonic-boom "3.8.1" + +"@verdaccio/logger@8.0.0-next-8.37": + version "8.0.0-next-8.37" + resolved "https://registry.yarnpkg.com/@verdaccio/logger/-/logger-8.0.0-next-8.37.tgz#51d6dededa058726e3b54b310781889bec4fc0c9" + integrity sha512-xG27C1FsbTsHhvhB3OpisVzHUyrE9NSVrzVHapPuOPd6X1DpnHZoZ+UYBAS0MSO1tGS+NEHW9GHL0XiroULggA== + dependencies: + "@verdaccio/logger-commons" "8.0.0-next-8.37" + pino "9.14.0" + +"@verdaccio/middleware@8.0.0-next-8.37": + version "8.0.0-next-8.37" + resolved "https://registry.yarnpkg.com/@verdaccio/middleware/-/middleware-8.0.0-next-8.37.tgz#b6730c3ef5eea444fb164e95e9ebdac44c28b6d7" + integrity sha512-8SqCdzKfANhaO/ZoSBBJHPlDWmXEJdq/1giV/ZKYtU4xVbBb/4ThKp2nNKk4s9+8S8XNNgX+7J5e/BgbIzzsEA== + dependencies: + "@verdaccio/config" "8.0.0-next-8.37" + "@verdaccio/core" "8.0.0-next-8.37" + "@verdaccio/url" "13.0.0-next-8.37" + debug "4.4.3" + express "4.22.1" + express-rate-limit "5.5.1" + lodash "4.18.1" + lru-cache "7.18.3" + +"@verdaccio/package-filter@13.0.0-next-8.5": + version "13.0.0-next-8.5" + resolved "https://registry.yarnpkg.com/@verdaccio/package-filter/-/package-filter-13.0.0-next-8.5.tgz#4f8a70016df578da3bb1e200f772409839fcafe9" + integrity sha512-+RZzVI/Yqjpoiv2SL3C0cxMC8ucU6j+YPdP/Bg/KJVqPbGNTn4Ol/fuGNhMJO6meIRS5ekW0PSrAvrDJ6E+JCA== + dependencies: + "@verdaccio/core" "8.0.0-next-8.37" + debug "4.4.3" + semver "7.7.4" + +"@verdaccio/search-indexer@8.0.0-next-8.6": + version "8.0.0-next-8.6" + resolved "https://registry.yarnpkg.com/@verdaccio/search-indexer/-/search-indexer-8.0.0-next-8.6.tgz#7609c18afc68f09b9800e3548d3bce295e5bd001" + integrity sha512-+vFkeqwXWlbpPO/vxC1N5Wbi8sSXYR5l9Z41fqQgSncaF/hfSKB/iHsid1psCusfsDPGuwEbm2usPEW0nDdRDg== + +"@verdaccio/signature@8.0.0-next-8.29": + version "8.0.0-next-8.29" + resolved "https://registry.yarnpkg.com/@verdaccio/signature/-/signature-8.0.0-next-8.29.tgz#e06a854bd9422c80e38884fc635432f9f95f5ac0" + integrity sha512-D1OyGi7+/5zXdbf78qdmL4wpf7iGN+RNDGB2TdLVosSrd+PWGrXqMJB3q2r/DJQ8J3724YVOJgNKiXjxV+Y1Aw== + dependencies: + "@verdaccio/config" "8.0.0-next-8.37" + "@verdaccio/core" "8.0.0-next-8.37" + debug "4.4.3" + jsonwebtoken "9.0.3" + +"@verdaccio/streams@10.2.1": + version "10.2.1" + resolved "https://registry.yarnpkg.com/@verdaccio/streams/-/streams-10.2.1.tgz#9443d24d4f17672b8f8c8e147690557918ed2bcb" + integrity sha512-OojIG/f7UYKxC4dYX8x5ax8QhRx1b8OYUAMz82rUottCuzrssX/4nn5QE7Ank0DUSX3C9l/HPthc4d9uKRJqJQ== + +"@verdaccio/tarball@13.0.0-next-8.37": + version "13.0.0-next-8.37" + resolved "https://registry.yarnpkg.com/@verdaccio/tarball/-/tarball-13.0.0-next-8.37.tgz#5cd9a8b015c3ac6dbb2e92a42e6ad96bc3ad33e4" + integrity sha512-Qxm6JQYEFfkeJd4YQII/2IAMiL++QnM6gqKXZbM5mNkAApyqx8ZbL1e9pe3aCDmBYs2mo0JGORCy3OaHZcsJGw== + dependencies: + "@verdaccio/core" "8.0.0-next-8.37" + "@verdaccio/url" "13.0.0-next-8.37" + debug "4.4.3" + gunzip-maybe "1.4.2" + tar-stream "3.1.7" + +"@verdaccio/ui-theme@9.0.0-next-9.10": + version "9.0.0-next-9.10" + resolved "https://registry.yarnpkg.com/@verdaccio/ui-theme/-/ui-theme-9.0.0-next-9.10.tgz#9825385187a2ba090c22e4c39cdc1f1b9c300ddc" + integrity sha512-sJRuAGKHklQU8bLdcXjvzfajXk9VqyBcFUKHAAyWXAeIKs04/lTgCHB6nFcEm0WDzlXk8CMAQuo/kcjNkg7qyw== + +"@verdaccio/url@13.0.0-next-8.37": + version "13.0.0-next-8.37" + resolved "https://registry.yarnpkg.com/@verdaccio/url/-/url-13.0.0-next-8.37.tgz#2cf602feac7ac2f86cdef59207f4a7f1efa3f855" + integrity sha512-Gtv5oDgVwGPGxzuXaIJLbmL8YkBKW2UZwDsrnbDoWRt1nWLtiOp4Vui1VISTqX7A9BB50YslLEgNLcPd2qRE+w== + dependencies: + "@verdaccio/core" "8.0.0-next-8.37" + debug "4.4.3" + validator "13.15.26" + +"@verdaccio/utils@8.1.0-next-8.37": + version "8.1.0-next-8.37" + resolved "https://registry.yarnpkg.com/@verdaccio/utils/-/utils-8.1.0-next-8.37.tgz#ad6eb2be2722ef8b46d735581b33c4eabbc27663" + integrity sha512-wfwn3z5M+w2KOV+xJFVv8tM8aOB4Ok5emfBDrDHrHMPDJ/fn3dEo6HoOra654PJ+zNwbTiMDvE5oAg/PLtnsUw== + dependencies: + "@verdaccio/core" "8.0.0-next-8.37" + lodash "4.18.1" + minimatch "7.4.9" + "@vinxi/listhen@^1.5.6": version "1.5.6" resolved "https://registry.yarnpkg.com/@vinxi/listhen/-/listhen-1.5.6.tgz#78a01eb6d92016b646f3b468433e7bbb8c9957ab" @@ -10796,6 +11046,14 @@ 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.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== + dependencies: + jsonparse "^1.2.0" + through ">=2.2.7 <3" + abab@^2.0.6: version "2.0.6" resolved "https://registry.yarnpkg.com/abab/-/abab-2.0.6.tgz#41b80f2c871d19686216b82309231cfd3cb3d291" @@ -10826,7 +11084,7 @@ accepts@^2.0.0: mime-types "^3.0.0" negotiator "^1.0.0" -accepts@~1.3.4, accepts@~1.3.5, accepts@~1.3.8: +accepts@~1.3.4, accepts@~1.3.8: version "1.3.8" resolved "https://registry.yarnpkg.com/accepts/-/accepts-1.3.8.tgz#0bf0be125b67014adcb0b0921e62db7bffe16b2e" integrity sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw== @@ -10969,6 +11227,26 @@ ajv@8.11.0: require-from-string "^2.0.2" uri-js "^4.2.2" +ajv@8.17.1: + version "8.17.1" + resolved "https://registry.yarnpkg.com/ajv/-/ajv-8.17.1.tgz#37d9a5c776af6bc92d7f4f9510eba4c0a60d11a6" + integrity sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g== + dependencies: + fast-deep-equal "^3.1.3" + fast-uri "^3.0.1" + json-schema-traverse "^1.0.0" + require-from-string "^2.0.2" + +ajv@8.18.0, ajv@^8.0.0, ajv@^8.10.0, ajv@^8.9.0: + version "8.18.0" + resolved "https://registry.yarnpkg.com/ajv/-/ajv-8.18.0.tgz#8864186b6738d003eb3a933172bb3833e10cefbc" + integrity sha512-PlXPeEWMXMZ7sPYOHqmDyCJzcfNrUr3fGNKtezX14ykXOEIvyK81d+qydx89KY5O71FKMPaQ2vBfBFI5NHR63A== + dependencies: + fast-deep-equal "^3.1.3" + fast-uri "^3.0.1" + json-schema-traverse "^1.0.0" + require-from-string "^2.0.2" + ajv@^6.11.0, ajv@^6.12.4, ajv@^6.12.5: version "6.12.6" resolved "https://registry.yarnpkg.com/ajv/-/ajv-6.12.6.tgz#baf5a62e802b07d977034586f8c3baf5adf26df4" @@ -10979,16 +11257,6 @@ ajv@^6.11.0, ajv@^6.12.4, ajv@^6.12.5: json-schema-traverse "^0.4.1" uri-js "^4.2.2" -ajv@^8.0.0, ajv@^8.10.0, ajv@^8.9.0: - version "8.17.1" - resolved "https://registry.yarnpkg.com/ajv/-/ajv-8.17.1.tgz#37d9a5c776af6bc92d7f4f9510eba4c0a60d11a6" - integrity sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g== - dependencies: - fast-deep-equal "^3.1.3" - fast-uri "^3.0.1" - json-schema-traverse "^1.0.0" - require-from-string "^2.0.2" - amd-name-resolver@^1.3.1: version "1.3.1" resolved "https://registry.yarnpkg.com/amd-name-resolver/-/amd-name-resolver-1.3.1.tgz#ffe71c683c6e7191fc4ae1bb3aaed15abea135d9" @@ -11136,6 +11404,11 @@ anymatch@^3.1.1, anymatch@^3.1.3, anymatch@~3.1.2: normalize-path "^3.0.0" picomatch "^2.0.4" +apache-md5@1.1.8: + version "1.1.8" + resolved "https://registry.yarnpkg.com/apache-md5/-/apache-md5-1.1.8.tgz#ea79c6feb03abfed42b2830dde06f75df5e3bbd9" + integrity sha512-FCAJojipPn0bXjuEpjOOOMN8FZDkxfWWp4JGN9mifU2IhxvKyXZYqpzPHdnTSUpmPDy+tsslB6Z1g+Vg6nVbYA== + app-module-path@^2.2.0: version "2.2.0" resolved "https://registry.yarnpkg.com/app-module-path/-/app-module-path-2.2.0.tgz#641aa55dfb7d6a6f0a8141c4b9c0aa50b6c24dd5" @@ -11385,11 +11658,23 @@ arrify@^2.0.0, arrify@^2.0.1: resolved "https://registry.yarnpkg.com/arrify/-/arrify-2.0.1.tgz#c9655e9331e0abcd588d2a7cad7e9956f66701fa" integrity sha512-3duEwti880xqi4eAMN8AyR4a0ByT90zoYdLlevfrvU43vb0YZwZVfxOgxWrLXXXpyugL0hNZc9G6BiB5B3nUug== +asn1@~0.2.3: + version "0.2.6" + resolved "https://registry.yarnpkg.com/asn1/-/asn1-0.2.6.tgz#0d3a7bb6e64e02a90c0303b31f292868ea09a08d" + integrity sha512-ix/FxPn0MDjeyJ7i/yoHGFt/EX6LyNbxSEhPPXODPL+KB0VPk86UYfL0lMdy+KCnv+fmvIzySwaK5COwqVbWTQ== + dependencies: + safer-buffer "~2.1.0" + assert-never@^1.2.1: version "1.2.1" resolved "https://registry.yarnpkg.com/assert-never/-/assert-never-1.2.1.tgz#11f0e363bf146205fb08193b5c7b90f4d1cf44fe" integrity sha512-TaTivMB6pYI1kXwrFlEhLeGfOqoDNdTxjCdwRfFFkEA30Eu+k48W34nlok2EYWJfFFzqaEmichdNM7th6M5HNw== +assert-plus@1.0.0, assert-plus@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/assert-plus/-/assert-plus-1.0.0.tgz#f12e0f3c5d77b0b1cdd9146942e4e96c1e4dd525" + integrity sha512-NfJ4UzBCcQGLDlQq7nHxH+tv3kyZ0hHQqF5BO6J7tNJeP5do1llPr8dZ8zHonfhAu0PHAdMkSo+8o0wxg9lZWw== + assertion-error@^2.0.1: version "2.0.1" resolved "https://registry.yarnpkg.com/assertion-error/-/assertion-error-2.0.1.tgz#f641a196b335690b1070bf00b6e7593fec190bf7" @@ -11578,6 +11863,11 @@ async-sema@^3.1.1: resolved "https://registry.yarnpkg.com/async-sema/-/async-sema-3.1.1.tgz#e527c08758a0f8f6f9f15f799a173ff3c40ea808" integrity sha512-tLRNUXati5MFePdAk8dw7Qt7DpxPB60ofAgn8WRhW6a2rcimZnYBP9oxHiv0OHy+Wz7kPMG+t4LGdt31+4EmGg== +async@3.2.6, async@^3.2.3, async@^3.2.4: + version "3.2.6" + resolved "https://registry.yarnpkg.com/async/-/async-3.2.6.tgz#1b0728e14929d51b85b449b7f06e27c1145e38ce" + integrity sha512-htCUDlxyyCLMgaM3xXg0C0LW2xqfuQ6p05pCEIsXuyQ+a1koYKTuBMzRNwmybfLgvJDMd0r1LTn4+E0Ti6C2AA== + async@^2.4.1, async@^2.6.4: version "2.6.4" resolved "https://registry.yarnpkg.com/async/-/async-2.6.4.tgz#706b7ff6084664cd7eae713f6f965433b5504221" @@ -11585,11 +11875,6 @@ async@^2.4.1, async@^2.6.4: dependencies: lodash "^4.17.14" -async@^3.2.3, async@^3.2.4: - version "3.2.5" - resolved "https://registry.yarnpkg.com/async/-/async-3.2.5.tgz#ebd52a8fdaf7a2289a24df399f8d8485c8a46b66" - integrity sha512-baNZyqaaLhyLVKm/DlvdW051MSgO6b8eVfIezl9E5PqWxFgzLm/wQntEW4zOytVburDEr0JlALEpdOFwvErLsg== - async@~0.2.9: version "0.2.10" resolved "https://registry.yarnpkg.com/async/-/async-0.2.10.tgz#b6bbe0b0674b9d719708ca38de8c237cb526c3d1" @@ -11633,11 +11918,21 @@ available-typed-arrays@^1.0.7: dependencies: possible-typed-array-names "^1.0.0" +aws-sign2@~0.7.0: + version "0.7.0" + resolved "https://registry.yarnpkg.com/aws-sign2/-/aws-sign2-0.7.0.tgz#b46e890934a9591f2d2f6f86d7e6a9f1b3fe76a8" + integrity sha512-08kcGqnYf/YmjoRhfxyu+CLxBjUtHLXLXX/vUfx9l2LYzG3c1m61nrpyFUZI6zeS+Li/wWMMidD9KgrqtGq3mA== + aws-ssl-profiles@^1.1.2: version "1.1.2" resolved "https://registry.yarnpkg.com/aws-ssl-profiles/-/aws-ssl-profiles-1.1.2.tgz#157dd77e9f19b1d123678e93f120e6f193022641" integrity sha512-NZKeq9AfyQvEeNlN0zSYAaWrmBffJh3IELMZfRpJVWgrpEbtEpnjvzqBPf+mxoI287JohRDoa+/nsfqqiZmF6g== +aws4@^1.8.0: + version "1.13.2" + resolved "https://registry.yarnpkg.com/aws4/-/aws4-1.13.2.tgz#0aa167216965ac9474ccfa83892cfb6b3e1e52ef" + integrity sha512-lHe62zvbTB5eEABUVi/AwVh0ZKY9rMMDhmm+eeyuuUQbQ3+J+fONVQOZyj+DdrvD4BY33uYniyRJ4UJIaSKAfw== + axios@1.15.0, axios@^1.12.0: version "1.15.0" resolved "https://registry.yarnpkg.com/axios/-/axios-1.15.0.tgz#0fcee91ef03d386514474904b27863b2c683bf4f" @@ -11998,6 +12293,18 @@ batch@0.6.1: resolved "https://registry.yarnpkg.com/batch/-/batch-0.6.1.tgz#dc34314f4e679318093fc760272525f94bf25c16" integrity sha1-3DQxT05nkxgJP8dgJyUl+UvyXBY= +bcrypt-pbkdf@^1.0.0: + version "1.0.2" + resolved "https://registry.yarnpkg.com/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.2.tgz#a4301d389b6a43f9b67ff3ca11a3f6637e360e9e" + integrity sha512-qeFIXtP4MSoi6NLqO12WfqARWWuCKi2Rn/9hJLEmtB5yTNr9DqFWkJRCf2qShWzPeAMRnOgCrq0sg/KLv5ES9w== + dependencies: + tweetnacl "^0.14.3" + +bcryptjs@2.4.3: + version "2.4.3" + resolved "https://registry.yarnpkg.com/bcryptjs/-/bcryptjs-2.4.3.tgz#9ab5627b93e60621ff7cdac5da9733027df1d0cb" + integrity sha512-V/Hy/X9Vt7f3BbPJEi8BdVFMByHi+jNXrYkW3huaybV/kQ0KJg0Y6PkEMbn+zeT+i+SiKZ/HMqJGIIt4LZDqNQ== + before-after-hook@^2.2.0: version "2.2.3" resolved "https://registry.yarnpkg.com/before-after-hook/-/before-after-hook-2.2.3.tgz#c51e809c81a4e354084422b9b26bad88249c517c" @@ -12247,10 +12554,10 @@ brace-expansion@^2.0.1, brace-expansion@^2.0.2: dependencies: balanced-match "^1.0.0" -brace-expansion@^5.0.2: - version "5.0.3" - resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-5.0.3.tgz#6a9c6c268f85b53959ec527aeafe0f7300258eef" - integrity sha512-fy6KJm2RawA5RcHkLa1z/ScpBeA762UF9KmZQxwIbDtRJrgLzM10depAiEQ+CXYcoiqW1/m96OAAoke2nE9EeA== +brace-expansion@^5.0.2, brace-expansion@^5.0.5: + version "5.0.5" + resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-5.0.5.tgz#dcc3a37116b79f3e1b46db994ced5d570e930fdb" + integrity sha512-VZznLgtwhn+Mact9tfiwx64fA9erHH/MCXEUfB/0bX/6Fz6ny5EGTXYltMocqg4xFAQZtnO3DHWWXi8RiuN7cQ== dependencies: balanced-match "^4.0.2" @@ -12709,6 +13016,13 @@ broccoli@^3.5.2: underscore.string "^3.2.2" watch-detector "^1.0.0" +browserify-zlib@^0.1.4: + version "0.1.4" + resolved "https://registry.yarnpkg.com/browserify-zlib/-/browserify-zlib-0.1.4.tgz#bb35f8a519f600e0fa6b8485241c979d0141fb2d" + integrity sha512-19OEpq7vWgsH6WkvkBJQDFvJS1uPcbFOQ4v9CU839dO+ZZXUZO6XpE6hNCqvlIIj+4fZvRiJ6DsAQ382GwiyTQ== + dependencies: + pako "~0.2.0" + browserslist@^4.0.0, browserslist@^4.14.5, browserslist@^4.20.0, browserslist@^4.21.3, browserslist@^4.21.4, browserslist@^4.23.0, browserslist@^4.24.0, browserslist@^4.27.0, browserslist@^4.28.1, browserslist@^4.9.1: version "4.28.1" resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-4.28.1.tgz#7f534594628c53c63101079e27e40de490456a95" @@ -12754,7 +13068,7 @@ buffer-crc32@~0.2.3: resolved "https://registry.yarnpkg.com/buffer-crc32/-/buffer-crc32-0.2.13.tgz#0d333e3f00eac50aa1454abd30ef8c2a5d9a7242" integrity sha1-DTM+PwDqxQqhRUq9MO+MKl2ackI= -buffer-equal-constant-time@1.0.1: +buffer-equal-constant-time@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/buffer-equal-constant-time/-/buffer-equal-constant-time-1.0.1.tgz#f8e71132f7ffe6e01a5c9697a4c6f3e48d5cc819" integrity sha1-+OcRMvf/5uAaXJaXpMbz5I1cyBk= @@ -12834,12 +13148,7 @@ bytes@1: resolved "https://registry.yarnpkg.com/bytes/-/bytes-1.0.0.tgz#3569ede8ba34315fab99c3e92cb04c7220de1fa8" integrity sha1-NWnt6Lo0MV+rmcPpLLBMciDeH6g= -bytes@3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/bytes/-/bytes-3.0.0.tgz#d32815404d689699f85a4ea4fa8755dd13a96048" - integrity sha1-0ygVQE1olpn4Wk6k+odV3ROpYEg= - -bytes@^3.1.2, bytes@~3.1.2: +bytes@3.1.2, bytes@^3.1.2, bytes@~3.1.2: version "3.1.2" resolved "https://registry.yarnpkg.com/bytes/-/bytes-3.1.2.tgz#8b0beeb98605adf1b128fa4386403c009e0221a5" integrity sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg== @@ -12948,6 +13257,24 @@ cache-base@^1.0.1: union-value "^1.0.0" unset-value "^1.0.0" +cacheable-lookup@6.1.0: + version "6.1.0" + resolved "https://registry.yarnpkg.com/cacheable-lookup/-/cacheable-lookup-6.1.0.tgz#0330a543471c61faa4e9035db583aad753b36385" + integrity sha512-KJ/Dmo1lDDhmW2XDPMo+9oiy/CeqosPguPCrgcVzKyZrL6pM1gU2GmPY/xo6OQPTUaA/c0kwHuywB4E6nmT9ww== + +cacheable-request@7.0.2: + version "7.0.2" + resolved "https://registry.yarnpkg.com/cacheable-request/-/cacheable-request-7.0.2.tgz#ea0d0b889364a25854757301ca12b2da77f91d27" + integrity sha512-pouW8/FmiPQbuGpkXQ9BAPv/Mo5xDGANgSNXzTzJ8DrKGuXOssM4wIQRjfanNRh3Yu5cfYPvcorqbhg2KIJtew== + dependencies: + clone-response "^1.0.2" + get-stream "^5.1.0" + http-cache-semantics "^4.0.0" + keyv "^4.0.0" + lowercase-keys "^2.0.0" + normalize-url "^6.0.1" + responselike "^2.0.0" + calculate-cache-key-for-tree@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/calculate-cache-key-for-tree/-/calculate-cache-key-for-tree-2.0.0.tgz#7ac57f149a4188eacb0a45b210689215d3fef8d6" @@ -13056,6 +13383,11 @@ cardinal@^1.0.0: ansicolors "~0.2.1" redeyed "~1.0.0" +caseless@~0.12.0: + version "0.12.0" + resolved "https://registry.yarnpkg.com/caseless/-/caseless-0.12.0.tgz#1b681c21ff84033c826543090689420d187151dc" + integrity sha512-4tYFyifaFfGacoiObjJegolkwSU4xQNGbVgUiNYVUxbQ2x2lUsFvY4hVgVzGiIe6WLOPqycWXA40l+PWsxthUw== + ccount@^2.0.0: version "2.0.1" resolved "https://registry.yarnpkg.com/ccount/-/ccount-2.0.1.tgz#17a3bf82302e0870d6da43a01311a8bc02a3ecf5" @@ -13332,6 +13664,13 @@ client-only@0.0.1, client-only@^0.0.1: resolved "https://registry.yarnpkg.com/client-only/-/client-only-0.0.1.tgz#38bba5d403c41ab150bff64a95c85013cf73bca1" integrity sha512-IV3Ou0jSMzZrd3pZ48nLkT9DA7Ag1pnPzaiQhpW7c3RbcqqzvzzVu+L8gfqMp/8IM2MQtSiqaCxrrcfu8I8rMA== +clipanion@4.0.0-rc.4: + version "4.0.0-rc.4" + resolved "https://registry.yarnpkg.com/clipanion/-/clipanion-4.0.0-rc.4.tgz#7191a940e47ef197e5f18c9cbbe419278b5f5903" + integrity sha512-CXkMQxU6s9GklO/1f714dkKBMu1lopS1WFF0B8o4AxPykR1hpozxSiUZ5ZUeBjfPgCWqbcNOtZVFhB8Lkfp1+Q== + dependencies: + typanion "^3.8.0" + clipboardy@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/clipboardy/-/clipboardy-4.0.0.tgz#e73ced93a76d19dd379ebf1f297565426dffdca1" @@ -13368,6 +13707,13 @@ clone-deep@^4.0.1: kind-of "^6.0.2" shallow-clone "^3.0.0" +clone-response@^1.0.2: + version "1.0.3" + resolved "https://registry.yarnpkg.com/clone-response/-/clone-response-1.0.3.tgz#af2032aa47816399cf5f0a1d0db902f517abb8c3" + integrity sha512-ROoL94jJH2dUVML2Y/5PEDNaSHgeOdSDicUyS7izcF63G6sTc/FTjLub4b8Il9S8S0beOfYt0TaA5qvFK+w0wA== + dependencies: + mimic-response "^1.0.0" + clone@^1.0.2: version "1.0.4" resolved "https://registry.yarnpkg.com/clone/-/clone-1.0.4.tgz#da309cc263df15994c688ca902179ca3c7cd7c7e" @@ -13470,7 +13816,7 @@ colorette@2.0.19: resolved "https://registry.yarnpkg.com/colorette/-/colorette-2.0.19.tgz#cdf044f47ad41a0f4b56b3a0d5b4e6e1a2d5a798" integrity sha512-3tlv/dIP7FWvj3BsbHrGLJ6l/oKh1O3TcgBqMn+yyCagOxc23fyzDS6HypQbgxWbkpDnf52p1LuR4eWDQ/K9WQ== -colorette@^2.0.10: +colorette@2.0.20, colorette@^2.0.10: version "2.0.20" resolved "https://registry.yarnpkg.com/colorette/-/colorette-2.0.20.tgz#9eb793e6833067f7235902fcd3b09917a000a95a" integrity sha512-IfEDxwoWIjkeXL1eXcDiow4UbKjhLdq6/EuSVR9GMN7KVH3r9gQ83e73hsz1Nd1T3ijd5xv1wcWRYO+D6kCI2w== @@ -13493,7 +13839,7 @@ colorspace@1.1.x: color "^3.1.3" text-hex "1.0.x" -combined-stream@^1.0.8: +combined-stream@^1.0.8, combined-stream@~1.0.6: version "1.0.8" resolved "https://registry.yarnpkg.com/combined-stream/-/combined-stream-1.0.8.tgz#c3d45a8b34fd730631a110a8a2520682b31d5a7f" integrity sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg== @@ -13598,24 +13944,24 @@ compress-commons@^6.0.2: normalize-path "^3.0.0" readable-stream "^4.0.0" -compressible@~2.0.16: +compressible@~2.0.18: version "2.0.18" resolved "https://registry.yarnpkg.com/compressible/-/compressible-2.0.18.tgz#af53cca6b070d4c3c0750fbd77286a6d7cc46fba" integrity sha512-AF3r7P5dWxL8MxyITRMlORQNaOA2IkAFaTr4k7BUumjPtRpGDTZpl0Pb1XCO6JeDCBdp126Cgs9sMxqSjgYyRg== dependencies: mime-db ">= 1.43.0 < 2" -compression@^1.7.4: - version "1.7.4" - resolved "https://registry.yarnpkg.com/compression/-/compression-1.7.4.tgz#95523eff170ca57c29a0ca41e6fe131f41e5bb8f" - integrity sha512-jaSIDzP9pZVS4ZfQ+TzvtiWhdpFhE2RDHz8QJkpX9SIpLq88VueF5jJw6t+6CUQcAoA6t+x89MLrWAqpfDE8iQ== +compression@1.8.1, compression@^1.7.4: + version "1.8.1" + resolved "https://registry.yarnpkg.com/compression/-/compression-1.8.1.tgz#4a45d909ac16509195a9a28bd91094889c180d79" + integrity sha512-9mAqGPHLakhCLeNyxPkK4xVo746zQ/czLH1Ky+vkitMnWfWZps8r0qXuwhwizagCRttsL4lfG4pIOvaWLpAP0w== dependencies: - accepts "~1.3.5" - bytes "3.0.0" - compressible "~2.0.16" + bytes "3.1.2" + compressible "~2.0.18" debug "2.6.9" - on-headers "~1.0.2" - safe-buffer "5.1.2" + negotiator "~0.6.4" + on-headers "~1.1.0" + safe-buffer "5.2.1" vary "~1.1.2" concat-map@0.0.1: @@ -13851,7 +14197,7 @@ core-object@^3.1.5: dependencies: chalk "^2.0.0" -core-util-is@~1.0.0: +core-util-is@1.0.2, core-util-is@~1.0.0: version "1.0.2" resolved "https://registry.yarnpkg.com/core-util-is/-/core-util-is-1.0.2.tgz#b5fd54220aa2bc5ab57aab7140c940754503c1a7" integrity sha1-tf1UIgqivFq1eqtxQMlAdUUDwac= @@ -14180,6 +14526,13 @@ dag-map@^2.0.2: resolved "https://registry.yarnpkg.com/dag-map/-/dag-map-2.0.2.tgz#9714b472de82a1843de2fba9b6876938cab44c68" integrity sha1-lxS0ct6CoYQ94vuptodpOMq0TGg= +dashdash@^1.12.0: + version "1.14.1" + resolved "https://registry.yarnpkg.com/dashdash/-/dashdash-1.14.1.tgz#853cfa0f7cbe2fed5de20326b8dd581035f6e2f0" + integrity sha512-jRFi8UDGo6j+odZiEpjazZaWqEal3w/basFjQHQEwVtZJGDpxbH1MeYluwCS8Xq5wmLJooDlMgvVarmWfGM44g== + dependencies: + assert-plus "^1.0.0" + data-uri-to-buffer@^3.0.1: version "3.0.1" resolved "https://registry.yarnpkg.com/data-uri-to-buffer/-/data-uri-to-buffer-3.0.1.tgz#594b8973938c5bc2c33046535785341abc4f3636" @@ -14239,6 +14592,11 @@ dax-sh@^0.43.0: "@deno/shim-deno" "~0.19.0" undici-types "^5.26" +dayjs@1.11.18: + version "1.11.18" + resolved "https://registry.yarnpkg.com/dayjs/-/dayjs-1.11.18.tgz#835fa712aac52ab9dec8b1494098774ed7070a11" + integrity sha512-zFBQ7WFRvVRhKcWoUh+ZA1g2HVgUbsZm9sbddh8EC5iv93sui8DVVz1Npvz+r6meo9VKfa8NyLWBsQK1VvIKPA== + db0@^0.3.4: version "0.3.4" resolved "https://registry.yarnpkg.com/db0/-/db0-0.3.4.tgz#fb109b0d9823ba1f787a4a3209fa1f3cf9ae9cf9" @@ -14256,7 +14614,7 @@ debug@2, debug@2.6.9, debug@^2.1.0, debug@^2.1.1, debug@^2.1.3, debug@^2.2.0, de dependencies: ms "2.0.0" -debug@4, debug@4.x, debug@^4, debug@^4.0.0, debug@^4.1.0, debug@^4.1.1, debug@^4.3.1, debug@^4.3.2, debug@^4.3.3, debug@^4.3.4, debug@^4.3.5, debug@^4.4.0, debug@^4.4.1, debug@^4.4.3, debug@~4.4.1: +debug@4, debug@4.4.3, debug@4.x, debug@^4, debug@^4.0.0, debug@^4.1.0, debug@^4.1.1, debug@^4.3.1, debug@^4.3.2, debug@^4.3.3, debug@^4.3.4, debug@^4.3.5, debug@^4.4.0, debug@^4.4.1, debug@^4.4.3, debug@~4.4.1: version "4.4.3" resolved "https://registry.yarnpkg.com/debug/-/debug-4.4.3.tgz#c6ae432d9bd9662582fce08709b038c58e9e3d6a" integrity sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA== @@ -14270,6 +14628,13 @@ debug@4.3.4: dependencies: ms "2.1.2" +debug@4.4.1: + version "4.4.1" + resolved "https://registry.yarnpkg.com/debug/-/debug-4.4.1.tgz#e5a8bc6cbc4c6cd3e64308b0693a3d4fa550189b" + integrity sha512-KcKCqiftBJcZr++7ykoDIEwSa3XWowTfNPo92BYxjXiyYEVrUQh2aLyhxBCwww+heortUFxEJYcRzosstTEBYQ== + dependencies: + ms "^2.1.3" + debug@^3.0.1, debug@^3.1.0, debug@^3.2.6, debug@^3.2.7: version "3.2.7" resolved "https://registry.yarnpkg.com/debug/-/debug-3.2.7.tgz#72580b7e9145fb39b6676f9c5e5fb100b934179a" @@ -14402,6 +14767,11 @@ defaults@^1.0.3: dependencies: clone "^1.0.2" +defer-to-connect@^2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/defer-to-connect/-/defer-to-connect-2.0.1.tgz#8016bdb4143e4632b77a3449c6236277de520587" + integrity sha512-4tvttepXG1VaYGrRibk5EwJd1t4udunSOVMdLSAL6mId1ix438oPwPZMALY41FCijukO1L0twNcGsdzS7dHgDg== + define-data-property@^1.0.1, define-data-property@^1.1.4: version "1.1.4" resolved "https://registry.yarnpkg.com/define-data-property/-/define-data-property-1.1.4.tgz#894dc141bb7d3060ae4366f6a0107e68fbe48c5e" @@ -14879,6 +15249,16 @@ duplexer@^0.1.2: resolved "https://registry.yarnpkg.com/duplexer/-/duplexer-0.1.2.tgz#3abe43aef3835f8ae077d136ddce0f276b0400e6" integrity sha512-jtD6YG370ZCIi/9GTaJKQxWTZD045+4R4hTk/x1UyoqadyJ9x9CgSi1RlVDQF8U2sxLLSnFkCaMihqljHIWgMg== +duplexify@^3.5.0, duplexify@^3.6.0: + version "3.7.1" + resolved "https://registry.yarnpkg.com/duplexify/-/duplexify-3.7.1.tgz#2a4df5317f6ccfd91f86d6fd25d8d8a103b88309" + integrity sha512-07z8uv2wMyS51kKhD1KsdXJg5WQ6t93RneqRxUHnskXVtlYYkLqM0gqStQZ3pj073g687jPCHrqNfCzawLYh5g== + dependencies: + end-of-stream "^1.0.0" + inherits "^2.0.1" + readable-stream "^2.0.0" + stream-shift "^1.0.0" + duplexify@^4.0.0, duplexify@^4.1.1: version "4.1.1" resolved "https://registry.yarnpkg.com/duplexify/-/duplexify-4.1.1.tgz#7027dc374f157b122a8ae08c2d3ea4d2d953aa61" @@ -14894,6 +15274,14 @@ eastasianwidth@^0.2.0: resolved "https://registry.yarnpkg.com/eastasianwidth/-/eastasianwidth-0.2.0.tgz#696ce2ec0aa0e6ea93a397ffcf24aa7840c827cb" integrity sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA== +ecc-jsbn@~0.1.1: + version "0.1.2" + resolved "https://registry.yarnpkg.com/ecc-jsbn/-/ecc-jsbn-0.1.2.tgz#3a83a904e54353287874c564b7549386849a98c9" + integrity sha512-eh9O+hwRHNbG4BLTjEl3nw044CkGm5X6LoaCf7LPp7UU8Qrt47JYNi6nPX8xjW97TKGKm1ouctg0QSpZe9qrnw== + dependencies: + jsbn "~0.1.0" + safer-buffer "^2.1.0" + ecdsa-sig-formatter@1.0.11, ecdsa-sig-formatter@^1.0.11: version "1.0.11" resolved "https://registry.yarnpkg.com/ecdsa-sig-formatter/-/ecdsa-sig-formatter-1.0.11.tgz#ae0f0fa2d85045ef14a817daa3ce9acd0489e5bf" @@ -15551,10 +15939,10 @@ encoding@^0.1.13: dependencies: iconv-lite "^0.6.2" -end-of-stream@^1.1.0, end-of-stream@^1.4.1: - version "1.4.4" - resolved "https://registry.yarnpkg.com/end-of-stream/-/end-of-stream-1.4.4.tgz#5ae64a5f45057baf3626ec14da0ca5e4b2431eb0" - integrity sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q== +end-of-stream@^1.0.0, end-of-stream@^1.1.0, end-of-stream@^1.4.1: + version "1.4.5" + resolved "https://registry.yarnpkg.com/end-of-stream/-/end-of-stream-1.4.5.tgz#7344d711dea40e0b74abc2ed49778743ccedb08c" + integrity sha512-ooEGc6HP26xXq/N+GCGOT0JKCLDGrq2bQUZrQ7gyrJiZANJ/8YDTxTpQBXGMn+WbIQXNVpyWymm7KYVICQnyOg== dependencies: once "^1.4.0" @@ -15638,6 +16026,11 @@ env-runner@^0.1.6: httpxy "^0.3.1" srvx "^0.11.9" +envinfo@7.21.0: + version "7.21.0" + resolved "https://registry.yarnpkg.com/envinfo/-/envinfo-7.21.0.tgz#04a251be79f92548541f37d13c8b6f22940c3bae" + integrity sha512-Lw7I8Zp5YKHFCXL7+Dz95g4CcbMEpgvqZNNq3AmlT5XAV6CgAAk6gyAMqn2zjw08K9BHfcNuKrMiCPLByGafow== + err-code@^2.0.2: version "2.0.3" resolved "https://registry.yarnpkg.com/err-code/-/err-code-2.0.3.tgz#23c2f3b756ffdfc608d30e27c9a941024807e7f9" @@ -16877,41 +17270,12 @@ expect-type@^1.2.1: resolved "https://registry.yarnpkg.com/expect-type/-/expect-type-1.2.1.tgz#af76d8b357cf5fa76c41c09dafb79c549e75f71f" integrity sha512-/kP8CAwxzLVEeFrMm4kMmy4CCDlpipyA7MYLVrdJIkV0fYF0UaigQHRsxHiuY/GEea+bh4KSv3TIlgr+2UL6bw== -express@5.2.1: - version "5.2.1" - resolved "https://registry.yarnpkg.com/express/-/express-5.2.1.tgz#8f21d15b6d327f92b4794ecf8cb08a72f956ac04" - integrity sha512-hIS4idWWai69NezIdRt2xFVofaF4j+6INOpJlVOLDO8zXGpUVEVzIYk12UUi2JzjEzWL3IOAxcTubgz9Po0yXw== - dependencies: - accepts "^2.0.0" - body-parser "^2.2.1" - content-disposition "^1.0.0" - content-type "^1.0.5" - cookie "^0.7.1" - cookie-signature "^1.2.1" - debug "^4.4.0" - depd "^2.0.0" - encodeurl "^2.0.0" - escape-html "^1.0.3" - etag "^1.8.1" - finalhandler "^2.1.0" - fresh "^2.0.0" - http-errors "^2.0.0" - merge-descriptors "^2.0.0" - mime-types "^3.0.0" - on-finished "^2.4.1" - once "^1.4.0" - parseurl "^1.3.3" - proxy-addr "^2.0.7" - qs "^6.14.0" - range-parser "^1.2.1" - router "^2.2.0" - send "^1.1.0" - serve-static "^2.2.0" - statuses "^2.0.1" - type-is "^2.0.1" - vary "^1.1.2" +express-rate-limit@5.5.1: + version "5.5.1" + resolved "https://registry.yarnpkg.com/express-rate-limit/-/express-rate-limit-5.5.1.tgz#110c23f6a65dfa96ab468eda95e71697bc6987a2" + integrity sha512-MTjE2eIbHv5DyfuFz4zLYWxpqVhEhkTiwFGuB74Q9CSou2WHO52nlE5y3Zlg6SIsiYUIPj6ifFxnkPz6O3sIUg== -express@^4.10.7, express@^4.17.3, express@^4.18.1, express@^4.21.2: +express@4.22.1, express@^4.10.7, express@^4.17.3, express@^4.18.1, express@^4.21.2: version "4.22.1" resolved "https://registry.yarnpkg.com/express/-/express-4.22.1.tgz#1de23a09745a4fffdb39247b344bb5eaff382069" integrity sha512-F2X8g9P1X7uCPZMA3MVf9wcTqlyNp7IhH5qPCI0izhaOIYXaW9L535tGA3qmjRzpH+bZczqq7hVKxTR4NWnu+g== @@ -16948,6 +17312,40 @@ express@^4.10.7, express@^4.17.3, express@^4.18.1, express@^4.21.2: utils-merge "1.0.1" vary "~1.1.2" +express@5.2.1: + version "5.2.1" + resolved "https://registry.yarnpkg.com/express/-/express-5.2.1.tgz#8f21d15b6d327f92b4794ecf8cb08a72f956ac04" + integrity sha512-hIS4idWWai69NezIdRt2xFVofaF4j+6INOpJlVOLDO8zXGpUVEVzIYk12UUi2JzjEzWL3IOAxcTubgz9Po0yXw== + dependencies: + accepts "^2.0.0" + body-parser "^2.2.1" + content-disposition "^1.0.0" + content-type "^1.0.5" + cookie "^0.7.1" + cookie-signature "^1.2.1" + debug "^4.4.0" + depd "^2.0.0" + encodeurl "^2.0.0" + escape-html "^1.0.3" + etag "^1.8.1" + finalhandler "^2.1.0" + fresh "^2.0.0" + http-errors "^2.0.0" + merge-descriptors "^2.0.0" + mime-types "^3.0.0" + on-finished "^2.4.1" + once "^1.4.0" + parseurl "^1.3.3" + proxy-addr "^2.0.7" + qs "^6.14.0" + range-parser "^1.2.1" + router "^2.2.0" + send "^1.1.0" + serve-static "^2.2.0" + statuses "^2.0.1" + type-is "^2.0.1" + vary "^1.1.2" + exsolve@^1.0.5, exsolve@^1.0.7, exsolve@^1.0.8: version "1.0.8" resolved "https://registry.yarnpkg.com/exsolve/-/exsolve-1.0.8.tgz#7f5e34da61cd1116deda5136e62292c096f50613" @@ -16968,7 +17366,7 @@ extend-shallow@^3.0.0, extend-shallow@^3.0.2: assign-symbols "^1.0.0" is-extendable "^1.0.1" -extend@^3.0.0, extend@^3.0.2: +extend@^3.0.0, extend@^3.0.2, extend@~3.0.2: version "3.0.2" resolved "https://registry.yarnpkg.com/extend/-/extend-3.0.2.tgz#f8b1136b4071fbd8eb140aff858b1019ec2915fa" integrity sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g== @@ -17011,6 +17409,16 @@ extract-stack@^2.0.0: resolved "https://registry.yarnpkg.com/extract-stack/-/extract-stack-2.0.0.tgz#11367bc865bfcd9bc0db3123e5edb57786f11f9b" integrity sha512-AEo4zm+TenK7zQorGK1f9mJ8L14hnTDi2ZQPR+Mub1NX8zimka1mXpV5LpH8x9HoUmFSHZCfLHqWvp0Y4FxxzQ== +extsprintf@1.3.0: + version "1.3.0" + resolved "https://registry.yarnpkg.com/extsprintf/-/extsprintf-1.3.0.tgz#96918440e3041a7a414f8c52e3c574eb3c3e1e05" + integrity sha512-11Ndz7Nv+mvAC1j0ktTa7fAb0vLyGGX+rMHNBYQviQDGU0Hw7lhctJANqbPhu9nV9/izT/IntTgZ7Im/9LJs9g== + +extsprintf@^1.2.0: + version "1.4.1" + resolved "https://registry.yarnpkg.com/extsprintf/-/extsprintf-1.4.1.tgz#8d172c064867f235c0c84a596806d279bf4bcc07" + integrity sha512-Wrk35e8ydCKDj/ArClo1VrPVmN8zph5V4AtHwIuHhvMXsKf73UT3BOD+azBIW+3wOJ4FhEH7zyaJCFvChjYvMA== + fake-indexeddb@^6.2.4: version "6.2.4" resolved "https://registry.yarnpkg.com/fake-indexeddb/-/fake-indexeddb-6.2.4.tgz#cf3860b6b37ddc3b33e7840be00a61ed094486a5" @@ -17518,7 +17926,17 @@ foreground-child@^3.1.0: cross-spawn "^7.0.6" signal-exit "^4.0.1" -form-data@^4.0.0, form-data@^4.0.4, form-data@^4.0.5: +forever-agent@~0.6.1: + version "0.6.1" + resolved "https://registry.yarnpkg.com/forever-agent/-/forever-agent-0.6.1.tgz#fbc71f0c41adeb37f96c577ad1ed42d8fdacca91" + integrity sha512-j0KLYPhm6zeac4lz3oJ3o65qvgQCcPubiyotZrXqEaG4hNagNYO8qdlUrX5vwqv9ohqeT/Z3j6+yW067yWWdUw== + +form-data-encoder@1.7.2: + version "1.7.2" + resolved "https://registry.yarnpkg.com/form-data-encoder/-/form-data-encoder-1.7.2.tgz#1f1ae3dccf58ed4690b86d87e4f57c654fbab040" + integrity sha512-qfqtYan3rxrnCk1VYaA4H+Ms9xdpPqvLZa6xmMgFvhO32x7/3J/ExcTd6qpxM0vH2GdMI+poehyBZvqfMTto8A== + +form-data@^4.0.0, form-data@^4.0.4, form-data@^4.0.5, form-data@~4.0.4: version "4.0.5" resolved "https://registry.yarnpkg.com/form-data/-/form-data-4.0.5.tgz#b49e48858045ff4cbf6b03e1805cebcad3679053" integrity sha512-8RipRLol37bNs2bhoV67fiTEvdTrbMUYcFTiy3+wuuOnUog2QBHCZWXDRijWQfAkhBj2Uf5UnVaiWwA5vdd82w== @@ -17903,14 +18321,14 @@ get-stream@^4.0.0: dependencies: pump "^3.0.0" -get-stream@^5.0.0: +get-stream@^5.0.0, get-stream@^5.1.0: version "5.2.0" resolved "https://registry.yarnpkg.com/get-stream/-/get-stream-5.2.0.tgz#4966a1795ee5ace65e706c4b7beb71257d6e22d3" integrity sha512-nBF+F1rAZVCu/p7rjzgA+Yb4lfYXrpl7a6VmJrU8wF9I1CKvP/QwPNZHnOlwbTkY6dvtFIzFMSyQXbLoTQPRpA== dependencies: pump "^3.0.0" -get-stream@^6.0.0: +get-stream@^6.0.0, get-stream@^6.0.1: version "6.0.1" resolved "https://registry.yarnpkg.com/get-stream/-/get-stream-6.0.1.tgz#a262d8eef67aced57c2852ad6167526a43cbf7b7" integrity sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg== @@ -17939,6 +18357,13 @@ getopts@2.3.0: resolved "https://registry.yarnpkg.com/getopts/-/getopts-2.3.0.tgz#71e5593284807e03e2427449d4f6712a268666f4" integrity sha512-5eDf9fuSXwxBL6q5HX+dhDj+dslFGWzU5thZ9kNKUkcPtaPdatmUFKwHFrLb/uf/WpA4BHET+AX3Scl56cAjpA== +getpass@^0.1.1: + version "0.1.7" + resolved "https://registry.yarnpkg.com/getpass/-/getpass-0.1.7.tgz#5eff8e3e684d569ae4cb2b1282604e8ba62149fa" + integrity sha512-0fzj9JxOLfJ+XGLhR8ze3unN0KZCgZwiSSDz168VERjK8Wl8kVSdcu2kspd4s4wtAa1y/qrVRiAA0WclVsu0ng== + dependencies: + assert-plus "^1.0.0" + giget@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/giget/-/giget-2.0.0.tgz#395fc934a43f9a7a29a29d55b99f23e30c14f195" @@ -18228,6 +18653,24 @@ gopd@^1.0.1, gopd@^1.2.0: resolved "https://registry.yarnpkg.com/gopd/-/gopd-1.2.0.tgz#89f56b8217bdbc8802bd299df6d7f1081d7e51a1" integrity sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg== +got-cjs@12.5.4: + version "12.5.4" + resolved "https://registry.yarnpkg.com/got-cjs/-/got-cjs-12.5.4.tgz#b46419c0e8e5fb5503b926941807408049ae2e11" + integrity sha512-Uas6lAsP8bRCt5WXGMhjFf/qEHTrm4v4qxGR02rLG2kdG9qedctvlkdwXVcDJ7Cs84X+r4dPU7vdwGjCaspXug== + dependencies: + "@sindresorhus/is" "4.6.0" + "@szmarczak/http-timer" "4.0.6" + "@types/responselike" "1.0.0" + cacheable-lookup "6.1.0" + cacheable-request "7.0.2" + decompress-response "^6.0.0" + form-data-encoder "1.7.2" + get-stream "^6.0.1" + http2-wrapper "^2.1.10" + lowercase-keys "2.0.0" + p-cancelable "2.1.1" + responselike "2.0.1" + graceful-fs@^4.1.2, graceful-fs@^4.1.3, graceful-fs@^4.1.5, graceful-fs@^4.1.6, graceful-fs@^4.2.0, graceful-fs@^4.2.11, graceful-fs@^4.2.4, graceful-fs@^4.2.6, graceful-fs@^4.2.9: version "4.2.11" resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.11.tgz#4183e4e8bf08bb6e05bbb2f7d2e0c8f712ca40e3" @@ -18292,6 +18735,18 @@ gtoken@^7.0.0: gaxios "^6.0.0" jws "^4.0.0" +gunzip-maybe@1.4.2: + version "1.4.2" + resolved "https://registry.yarnpkg.com/gunzip-maybe/-/gunzip-maybe-1.4.2.tgz#b913564ae3be0eda6f3de36464837a9cd94b98ac" + integrity sha512-4haO1M4mLO91PW57BMsDFf75UmwoRX0GkdD+Faw+Lr+r/OZrOCS0pIBwOL1xCKQqnQzbNFGgK2V2CpBUPeFNTw== + dependencies: + browserify-zlib "^0.1.4" + is-deflate "^1.0.0" + is-gzip "^1.0.0" + peek-stream "^1.1.0" + pumpify "^1.3.3" + through2 "^2.0.3" + gzip-size@^6.0.0: version "6.0.0" resolved "https://registry.yarnpkg.com/gzip-size/-/gzip-size-6.0.0.tgz#065367fd50c239c0671cbcbad5be3e2eeb10e462" @@ -18349,7 +18804,7 @@ handle-thing@^2.0.0: resolved "https://registry.yarnpkg.com/handle-thing/-/handle-thing-2.0.1.tgz#857f79ce359580c340d43081cc648970d0bb234e" integrity sha512-9Qn4yBxelxoh2Ow62nP+Ka/kMnOXRi8BXnRaUwezLNhqelnN49xKz4F/dPP8OYLxLxq6JDtZb2i9XznUQbNPTg== -handlebars@^4.0.4, handlebars@^4.3.1, handlebars@^4.7.3: +handlebars@4.7.9, handlebars@^4.0.4, handlebars@^4.3.1, handlebars@^4.7.3: version "4.7.9" resolved "https://registry.yarnpkg.com/handlebars/-/handlebars-4.7.9.tgz#6f139082ab58dc4e5a0e51efe7db5ae890d56a0f" integrity sha512-4E71E0rpOaQuJR2A3xDZ+GM1HyWYv1clR58tC8emQNeQe3RH7MAzSbat+V0wG78LQBo6m6bzSG/L4pBuCsgnUQ== @@ -18806,17 +19261,28 @@ htmlparser2@^6.1.0: domutils "^2.5.2" entities "^2.0.0" -http-cache-semantics@^4.1.0, 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-cache-semantics@^4.0.0, http-cache-semantics@^4.1.0, http-cache-semantics@^4.1.1: + version "4.2.0" + resolved "https://registry.yarnpkg.com/http-cache-semantics/-/http-cache-semantics-4.2.0.tgz#205f4db64f8562b76a4ff9235aa5279839a09dd5" + integrity sha512-dTxcvPXqPvXBQpq5dUr6mEMJX4oIEFv6bwom3FDwKRDsuIjjJGANqhBuoAn9c1RQJIdAKav33ED65E2ys+87QQ== http-deceiver@^1.2.7: version "1.2.7" resolved "https://registry.yarnpkg.com/http-deceiver/-/http-deceiver-1.2.7.tgz#fa7168944ab9a519d337cb0bec7284dc3e723d87" integrity sha1-+nFolEq5pRnTN8sL7HKE3D5yPYc= -http-errors@^2.0.0, http-errors@^2.0.1, http-errors@~2.0.0, http-errors@~2.0.1: +http-errors@2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/http-errors/-/http-errors-2.0.0.tgz#b7774a1486ef73cf7667ac9ae0858c012c57b9d3" + integrity sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ== + dependencies: + depd "2.0.0" + inherits "2.0.4" + setprototypeof "1.2.0" + statuses "2.0.1" + toidentifier "1.0.1" + +http-errors@2.0.1, http-errors@^2.0.0, http-errors@^2.0.1, http-errors@~2.0.0, http-errors@~2.0.1: version "2.0.1" resolved "https://registry.yarnpkg.com/http-errors/-/http-errors-2.0.1.tgz#36d2f65bc909c8790018dd36fb4d93da6caae06b" integrity sha512-4FbRdAX+bSdmo4AUFuS0WNiPz8NgFt+r8ThgNWmlrjQjt1Q7ZR9+zTlce2859x4KSXrwIsaeTqDoKQmtP8pLmQ== @@ -18893,6 +19359,20 @@ http-shutdown@^1.2.2: resolved "https://registry.yarnpkg.com/http-shutdown/-/http-shutdown-1.2.2.tgz#41bc78fc767637c4c95179bc492f312c0ae64c5f" integrity sha512-S9wWkJ/VSY9/k4qcjG318bqJNruzE4HySUhFYknwmu6LBP97KLLfwNf+n4V1BHurvFNkSKLFnK/RsuUnRTf9Vw== +http-signature@~1.4.0: + version "1.4.0" + resolved "https://registry.yarnpkg.com/http-signature/-/http-signature-1.4.0.tgz#dee5a9ba2bf49416abc544abd6d967f6a94c8c3f" + integrity sha512-G5akfn7eKbpDN+8nPS/cb57YeA1jLTVxjpCj7tmm3QKPdyDy7T+qSC40e9ptydSWvkwjSXw1VbkpyEm39ukeAg== + dependencies: + assert-plus "^1.0.0" + jsprim "^2.0.2" + sshpk "^1.18.0" + +http-status-codes@2.3.0: + version "2.3.0" + resolved "https://registry.yarnpkg.com/http-status-codes/-/http-status-codes-2.3.0.tgz#987fefb28c69f92a43aecc77feec2866349a8bfc" + integrity sha512-RJ8XvFvpPM/Dmc5SV+dC4y5PCeOhT3x1Hq0NU3rjGeg5a/CqlhZ7uudknPwZFz4aeAXDcbAyaeP7GAo9lvngtA== + http-terminator@^3.2.0: version "3.2.0" resolved "https://registry.yarnpkg.com/http-terminator/-/http-terminator-3.2.0.tgz#bc158d2694b733ca4fbf22a35065a81a609fb3e9" @@ -18903,6 +19383,14 @@ http-terminator@^3.2.0: roarr "^7.0.4" type-fest "^2.3.3" +http2-wrapper@^2.1.10: + version "2.2.1" + resolved "https://registry.yarnpkg.com/http2-wrapper/-/http2-wrapper-2.2.1.tgz#310968153dcdedb160d8b72114363ef5fce1f64a" + integrity sha512-V5nVw1PAOgfI3Lmeaj2Exmeg7fenjhRUgz1lPSezy1CuhPYbgQtbQj4jZfEAEMlaL+vupsvhjqCyjzob0yxsmQ== + dependencies: + quick-lru "^5.1.1" + resolve-alpn "^1.2.0" + https-proxy-agent@5.0.1, https-proxy-agent@^5.0.0, https-proxy-agent@^5.0.1: version "5.0.1" resolved "https://registry.yarnpkg.com/https-proxy-agent/-/https-proxy-agent-5.0.1.tgz#c59ef224a04fe8b754f3db0063a25ea30d0005d6" @@ -19108,7 +19596,7 @@ inflight@^1.0.4: once "^1.3.0" wrappy "1" -inherits@2, inherits@^2.0.1, inherits@^2.0.3, inherits@^2.0.4, inherits@~2.0.1, inherits@~2.0.3, inherits@~2.0.4: +inherits@2, inherits@2.0.4, inherits@^2.0.1, inherits@^2.0.3, inherits@^2.0.4, inherits@~2.0.1, inherits@~2.0.3, inherits@~2.0.4: version "2.0.4" resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.4.tgz#0fa2c64f932917c3433a0ded55363aae37416b7c" integrity sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ== @@ -19436,6 +19924,11 @@ is-date-object@^1.0.5, is-date-object@^1.1.0: call-bound "^1.0.2" has-tostringtag "^1.0.2" +is-deflate@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/is-deflate/-/is-deflate-1.0.0.tgz#c862901c3c161fb09dac7cdc7e784f80e98f2f14" + integrity sha512-YDoFpuZWu1VRXlsnlYMzKyVRITXj7Ej/V9gXQ2/pAe7X1J7M/RNOqaIYi6qUn+B7nGyB9pDXrv02dsB58d2ZAQ== + is-descriptor@^0.1.0: version "0.1.6" resolved "https://registry.yarnpkg.com/is-descriptor/-/is-descriptor-0.1.6.tgz#366d8240dde487ca51823b1ab9f07a10a78251ca" @@ -19521,6 +20014,11 @@ is-glob@^4.0.0, is-glob@^4.0.1, is-glob@^4.0.3, is-glob@~4.0.1: dependencies: is-extglob "^2.1.1" +is-gzip@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/is-gzip/-/is-gzip-1.0.0.tgz#6ca8b07b99c77998025900e555ced8ed80879a83" + integrity sha512-rcfALRIb1YewtnksfRIHGcIY93QnK8BIQ/2c9yDYcG/Y6+vRoJuTWBmmSEbyLLYtXm7q35pHOHbZFQBaLrhlWQ== + is-inside-container@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/is-inside-container/-/is-inside-container-1.0.0.tgz#e81fba699662eb31dbdaf26766a61d4814717ea4" @@ -19645,6 +20143,11 @@ is-potential-custom-element-name@^1.0.1: resolved "https://registry.yarnpkg.com/is-potential-custom-element-name/-/is-potential-custom-element-name-1.0.1.tgz#171ed6f19e3ac554394edf78caa05784a45bebb5" integrity sha512-bCYeRA2rVibKZd+s2625gGnGF/t7DSqDs4dP7CrLA1m7jKWz6pps0LpYLJN8Q64HtmPKJ1hrN3nzPNKFEKOUiQ== +is-promise@^2.1.0: + version "2.2.2" + resolved "https://registry.yarnpkg.com/is-promise/-/is-promise-2.2.2.tgz#39ab959ccbf9a774cf079f7b40c7a26f763135f1" + integrity sha512-+lP4/6lKUBfQjZ2pdxThZvLUAafmZb8OAxFb8XXtiQmS35INgr85hdOGoEs124ez1FCnZJt6jau/T+alh58QFQ== + is-promise@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/is-promise/-/is-promise-4.0.0.tgz#42ff9f84206c1991d26debf520dd5c01042dd2f3" @@ -19749,7 +20252,7 @@ is-typed-array@^1.1.13, is-typed-array@^1.1.14, is-typed-array@^1.1.15, is-typed dependencies: which-typed-array "^1.1.16" -is-typedarray@^1.0.0: +is-typedarray@^1.0.0, is-typedarray@~1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/is-typedarray/-/is-typedarray-1.0.0.tgz#e479c80858df0c1b11ddda6940f96011fcda4a9a" integrity sha1-5HnICFjfDBsR3dppQPlgEfzaSpo= @@ -19887,6 +20390,11 @@ isobject@^3.0.0, isobject@^3.0.1: resolved "https://registry.yarnpkg.com/isobject/-/isobject-3.0.1.tgz#4e431e92b11a9731636aa1f9c8d1ccbcfdab78df" integrity sha1-TkMekrEalzFjaqH5yNHMvP2reN8= +isstream@~0.1.2: + version "0.1.2" + resolved "https://registry.yarnpkg.com/isstream/-/isstream-0.1.2.tgz#47e63f7af55afa6f92e1500e690eb8b8529c099a" + integrity sha512-Yljz7ffyPbrLpLngrMtZ7NduUgVvi6wG9RJ9IUcyCd59YQ911PBJphODUcbOVbqYfxe1wuYf/LJ8PauMRwsM/g== + istanbul-lib-coverage@^3.0.0, istanbul-lib-coverage@^3.2.0, istanbul-lib-coverage@^3.2.2: version "3.2.2" resolved "https://registry.yarnpkg.com/istanbul-lib-coverage/-/istanbul-lib-coverage-3.2.2.tgz#2d166c4b0644d43a39f04bf6c2edd1e585f31756" @@ -20061,6 +20569,13 @@ js-tokens@^9.0.1: resolved "https://registry.yarnpkg.com/js-tokens/-/js-tokens-9.0.1.tgz#2ec43964658435296f6761b34e10671c2d9527f4" integrity sha512-mxa9E9ITFOt0ban3j6L5MpjwegGz6lBQmM1IJkWeBZGcMxto50+eWdjC/52xDbS2vy0k7vIMK0Fe2wfL9OQSpQ== +js-yaml@4.1.1, js-yaml@^4.1.0: + version "4.1.1" + resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-4.1.1.tgz" + integrity sha512-qQKT4zQxXl8lLwBtHMWwaTcGfFOZviOJet3Oy/xmGk2gZH677CJM9EvtfdSkgWcATZhj/55JZ0rmy3myCT5lsA== + dependencies: + argparse "^2.0.1" + js-yaml@^3.10.0, js-yaml@^3.13.0, js-yaml@^3.13.1, js-yaml@^3.2.5, js-yaml@^3.2.7: version "3.14.2" resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-3.14.2.tgz" @@ -20069,12 +20584,10 @@ js-yaml@^3.10.0, js-yaml@^3.13.0, js-yaml@^3.13.1, js-yaml@^3.2.5, js-yaml@^3.2. argparse "^1.0.7" esprima "^4.0.0" -js-yaml@^4.1.0: - version "4.1.1" - resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-4.1.1.tgz" - integrity sha512-qQKT4zQxXl8lLwBtHMWwaTcGfFOZviOJet3Oy/xmGk2gZH677CJM9EvtfdSkgWcATZhj/55JZ0rmy3myCT5lsA== - dependencies: - argparse "^2.0.1" +jsbn@~0.1.0: + version "0.1.1" + resolved "https://registry.yarnpkg.com/jsbn/-/jsbn-0.1.1.tgz#a5e654c2e5a2deb5f201d96cefbca80c0ef2f513" + integrity sha512-UVU9dibq2JcFWxQPA6KCqj5O42VOmAY3zQUfEKxU0KpTGXwNoCjkX1e13eHNvw/xPynt6pU0rZ1htjWTNTSXsg== jsdoc-type-pratt-parser@~4.1.0: version "4.1.0" @@ -20153,6 +20666,11 @@ json-bigint@^1.0.0: dependencies: bignumber.js "^9.0.0" +json-buffer@3.0.1: + version "3.0.1" + resolved "https://registry.yarnpkg.com/json-buffer/-/json-buffer-3.0.1.tgz#9338802a30d3b6605fbe0613e094008ca8c05a13" + integrity sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ== + json-parse-even-better-errors@^2.3.0, json-parse-even-better-errors@^2.3.1: version "2.3.1" resolved "https://registry.yarnpkg.com/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz#7c47805a94319928e05777405dc12e1f7a4ee02d" @@ -20181,7 +20699,7 @@ json-schema-traverse@^1.0.0: resolved "https://registry.yarnpkg.com/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz#ae7bcb3656ab77a73ba5c49bf654f38e6b6860e2" integrity sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug== -json-schema@^0.4.0: +json-schema@0.4.0, json-schema@^0.4.0: version "0.4.0" resolved "https://registry.yarnpkg.com/json-schema/-/json-schema-0.4.0.tgz#f7de4cf6efab838ebaeb3236474cbba5a1930ab5" integrity sha512-es94M3nTIfsEPisRafak+HDLfHXnKBhV3vU5eqPcS3flIWqcxJWgXHXiey3YrpaNsanY5ei1VoYEbOzijuq9BA== @@ -20198,7 +20716,7 @@ json-stable-stringify@^1.0.0, json-stable-stringify@^1.0.1: dependencies: jsonify "~0.0.0" -json-stringify-safe@^5.0.1: +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" integrity sha1-Epai1Y/UXxmg9s4B1lcB4sc1tus= @@ -20267,17 +20785,17 @@ jsonify@~0.0.0: resolved "https://registry.yarnpkg.com/jsonify/-/jsonify-0.0.0.tgz#2c74b6ee41d93ca51b7b5aaee8f503631d252a73" integrity sha1-LHS27kHZPKUbe1qu6PUDYx0lKnM= -jsonparse@^1.3.1: +jsonparse@^1.2.0, jsonparse@^1.3.1: version "1.3.1" resolved "https://registry.yarnpkg.com/jsonparse/-/jsonparse-1.3.1.tgz#3f4dae4a91fac315f71062f8521cc239f1366280" integrity sha512-POQXvpdL69+CluYsillJ7SUhKvytYjW9vG/GKpnf+xP8UWgYEM/RaMzHHofbALDiKbbP1W8UEYmgGl39WkPZsg== -jsonwebtoken@^9.0.0: - version "9.0.2" - resolved "https://registry.yarnpkg.com/jsonwebtoken/-/jsonwebtoken-9.0.2.tgz#65ff91f4abef1784697d40952bb1998c504caaf3" - integrity sha512-PRp66vJ865SSqOlgqS8hujT5U4AOgMfhrwYIuIhfKaoSCZcirrmASQr8CX7cUg+RMih+hgznrjp99o+W4pJLHQ== +jsonwebtoken@9.0.3, jsonwebtoken@^9.0.0: + version "9.0.3" + resolved "https://registry.yarnpkg.com/jsonwebtoken/-/jsonwebtoken-9.0.3.tgz#6cd57ab01e9b0ac07cb847d53d3c9b6ee31f7ae2" + integrity sha512-MT/xP0CrubFRNLNKvxJ2BYfy53Zkm++5bX9dtuPbqAeQpTVe0MQTFhao8+Cp//EmJp244xt6Drw/GVEGCUj40g== dependencies: - jws "^3.2.2" + jws "^4.0.1" lodash.includes "^4.3.0" lodash.isboolean "^3.0.3" lodash.isinteger "^4.0.4" @@ -20288,6 +20806,16 @@ jsonwebtoken@^9.0.0: ms "^2.1.1" semver "^7.5.4" +jsprim@^2.0.2: + version "2.0.2" + resolved "https://registry.yarnpkg.com/jsprim/-/jsprim-2.0.2.tgz#77ca23dbcd4135cd364800d22ff82c2185803d4d" + integrity sha512-gqXddjPqQ6G40VdnI6T6yObEC+pDNvyP95wdQhkWkg7crHH3km5qP1FsOXEkzEQwnz6gz5qGTn1c2Y52wP3OyQ== + dependencies: + assert-plus "1.0.0" + extsprintf "1.3.0" + json-schema "0.4.0" + verror "1.10.0" + "jsx-ast-utils@^2.4.1 || ^3.0.0": version "3.2.0" resolved "https://registry.yarnpkg.com/jsx-ast-utils/-/jsx-ast-utils-3.2.0.tgz#41108d2cec408c3453c1bbe8a4aae9e1e2bd8f82" @@ -20296,38 +20824,21 @@ jsonwebtoken@^9.0.0: array-includes "^3.1.2" object.assign "^4.1.2" -jwa@^1.4.1: - version "1.4.1" - resolved "https://registry.yarnpkg.com/jwa/-/jwa-1.4.1.tgz#743c32985cb9e98655530d53641b66c8645b039a" - integrity sha512-qiLX/xhEEFKUAJ6FiBMbes3w9ATzyk5W7Hvzpa/SLYdxNtng+gcurvrI7TbACjIXlsJyr05/S1oUhZrc63evQA== - dependencies: - buffer-equal-constant-time "1.0.1" - ecdsa-sig-formatter "1.0.11" - safe-buffer "^5.0.1" - -jwa@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/jwa/-/jwa-2.0.0.tgz#a7e9c3f29dae94027ebcaf49975c9345593410fc" - integrity sha512-jrZ2Qx916EA+fq9cEAeCROWPTfCwi1IVHqT2tapuqLEVVDKFDENFw1oL+MwrTvH6msKxsd1YTDVw6uKEcsrLEA== +jwa@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/jwa/-/jwa-2.0.1.tgz#bf8176d1ad0cd72e0f3f58338595a13e110bc804" + integrity sha512-hRF04fqJIP8Abbkq5NKGN0Bbr3JxlQ+qhZufXVr0DvujKy93ZCbXZMHDL4EOtodSbCWxOqR8MS1tXA5hwqCXDg== dependencies: - buffer-equal-constant-time "1.0.1" + buffer-equal-constant-time "^1.0.1" ecdsa-sig-formatter "1.0.11" safe-buffer "^5.0.1" -jws@^3.2.2: - version "3.2.2" - resolved "https://registry.yarnpkg.com/jws/-/jws-3.2.2.tgz#001099f3639468c9414000e99995fa52fb478304" - integrity sha512-YHlZCB6lMTllWDtSPHz/ZXTsi8S00usEV6v1tjq8tOUZzw7DpSDWVXjXDre6ed1w/pd495ODpHZYSdkRTsa0HA== - dependencies: - jwa "^1.4.1" - safe-buffer "^5.0.1" - -jws@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/jws/-/jws-4.0.0.tgz#2d4e8cf6a318ffaa12615e9dec7e86e6c97310f4" - integrity sha512-KDncfTmOZoOMTFG4mBlG0qUIOlc03fmzH+ru6RgYVZhPkyiy/92Owlt/8UEN+a4TXR1FQetfIpJE8ApdvdVxTg== +jws@^4.0.0, jws@^4.0.1: + version "4.0.1" + resolved "https://registry.yarnpkg.com/jws/-/jws-4.0.1.tgz#07edc1be8fac20e677b283ece261498bd38f0690" + integrity sha512-EKI/M/yqPncGUUh44xz0PxSidXFr/+r0pA70+gIYhjv+et7yxM+s29Y+VGDkovRofQem0fs7Uvf4+YmAdyRduA== dependencies: - jwa "^2.0.0" + jwa "^2.0.1" safe-buffer "^5.0.1" jwt-decode@^4.0.0: @@ -20352,6 +20863,13 @@ karma-source-map-support@1.4.0: dependencies: source-map-support "^0.5.5" +keyv@^4.0.0: + version "4.5.4" + resolved "https://registry.yarnpkg.com/keyv/-/keyv-4.5.4.tgz#a879a99e29452f942439f2a405e3af8b31d4de93" + integrity sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw== + dependencies: + json-buffer "3.0.1" + kind-of@^3.0.2, kind-of@^3.0.3, kind-of@^3.2.0: version "3.2.2" resolved "https://registry.yarnpkg.com/kind-of/-/kind-of-3.2.2.tgz#31ea21a734bab9bbb0f32466d893aea51e4a3c64" @@ -20687,6 +21205,13 @@ locate-path@^7.1.0: dependencies: p-locate "^6.0.0" +lockfile@1.0.4: + version "1.0.4" + resolved "https://registry.yarnpkg.com/lockfile/-/lockfile-1.0.4.tgz#07f819d25ae48f87e538e6578b6964a4981a5609" + integrity sha512-cvbTwETRfsFh4nHsL1eGWapU1XFi5Ot9E85sWAwia7Y7EgB7vfqcZhTKZ+l7hCGxSPoushMv5GKhT5PdLv03WA== + dependencies: + signal-exit "^3.0.2" + lodash._baseassign@^3.0.0: version "3.2.0" resolved "https://registry.yarnpkg.com/lodash._baseassign/-/lodash._baseassign-3.2.0.tgz#8c38a099500f215ad09e59f1722fd0c52bfe0a4e" @@ -20895,7 +21420,12 @@ lodash.uniq@^4.2.0, lodash.uniq@^4.5.0: resolved "https://registry.yarnpkg.com/lodash.uniq/-/lodash.uniq-4.5.0.tgz#d0225373aeb652adc1bc82e4945339a842754773" integrity sha1-0CJTc662Uq3BvILklFM5qEJ1R3M= -lodash@^4.17.12, lodash@^4.17.14, lodash@^4.17.15, lodash@^4.17.19, lodash@^4.17.20, lodash@^4.17.21, lodash@~4.17.21: +lodash@4, lodash@4.18.1, lodash@^4.17.12, lodash@^4.17.14, lodash@^4.17.15, lodash@^4.17.19, lodash@^4.17.20, lodash@^4.17.21: + version "4.18.1" + resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.18.1.tgz#ff2b66c1f6326d59513de2407bf881439812771c" + integrity sha512-dMInicTPVE8d1e5otfwmmjlxkZoUpiVLwyeTdUsi/Caj/gfzzblBcCE5sRHV/AsjuCmxWrte2TNGSYuCeCq+0Q== + +lodash@4.17.21, lodash@4.17.23, lodash@~4.17.21: version "4.17.23" resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.23.tgz#f113b0378386103be4f6893388c73d0bde7f2c5a" integrity sha512-LgVTMpQtIopCi79SJeDiP0TfWi5CNEc/L/aRdTh3yIvmZXTnheWpKjSZhnvMl8iXbC1tFg9gdHHDMLoV7CnG+w== @@ -20972,6 +21502,17 @@ loupe@^3.1.0, loupe@^3.1.4: resolved "https://registry.yarnpkg.com/loupe/-/loupe-3.1.4.tgz#784a0060545cb38778ffb19ccde44d7870d5fdd9" integrity sha512-wJzkKwJrheKtknCOKNEtDK4iqg/MxmZheEMtSTYvnzRdEYaZzmgH976nenp8WdJRdx5Vc1X/9MO0Oszl6ezeXg== +lowdb@1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/lowdb/-/lowdb-1.0.0.tgz#5243be6b22786ccce30e50c9a33eac36b20c8064" + integrity sha512-2+x8esE/Wb9SQ1F9IHaYWfsC9FIecLOPrK4g17FGEayjUWH172H6nwicRovGvSE2CPZouc2MCIqCI7h9d+GftQ== + dependencies: + graceful-fs "^4.1.3" + is-promise "^2.1.0" + lodash "4" + pify "^3.0.0" + steno "^0.4.1" + lower-case@^2.0.2: version "2.0.2" resolved "https://registry.yarnpkg.com/lower-case/-/lower-case-2.0.2.tgz#6fa237c63dbdc4a82ca0fd882e4722dc5e634e28" @@ -20979,6 +21520,11 @@ lower-case@^2.0.2: dependencies: tslib "^2.0.3" +lowercase-keys@2.0.0, lowercase-keys@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/lowercase-keys/-/lowercase-keys-2.0.0.tgz#2603e78b7b4b0006cbca2fbcc8a3202558ac9479" + integrity sha512-tqNXrS78oMOE73NMxK4EMLQsQowWf8jKooH9g7xPavRT706R6bkQJ6DY2Te7QukaZsulxa30wQ7bk0pm4XiHmA== + lru-cache@6.0.0, lru-cache@^6.0.0: version "6.0.0" resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-6.0.0.tgz#6d6fe6570ebd96aaf90fcad1dafa3b2566db3a94" @@ -20986,6 +21532,11 @@ lru-cache@6.0.0, lru-cache@^6.0.0: dependencies: yallist "^4.0.0" +lru-cache@7.18.3, lru-cache@^7.4.4, lru-cache@^7.5.1, lru-cache@^7.7.1: + version "7.18.3" + resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-7.18.3.tgz#f793896e0fd0e954a59dfdd82f0773808df6aa89" + integrity sha512-jumlc0BIUrS3qJGgIkWZsyfAM7NCWiBcCDhnd+3NNM5KbBmLTgHVfWBcg6W+rLUsIpzpERPsvwUP7CckAQSOoA== + lru-cache@^10.2.0: version "10.4.3" resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-10.4.3.tgz#410fc8a17b70e598013df257c2446b7f3383f119" @@ -21003,11 +21554,6 @@ lru-cache@^5.1.1: dependencies: yallist "^3.0.2" -lru-cache@^7.4.4, lru-cache@^7.5.1, lru-cache@^7.7.1: - version "7.18.3" - resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-7.18.3.tgz#f793896e0fd0e954a59dfdd82f0773808df6aa89" - integrity sha512-jumlc0BIUrS3qJGgIkWZsyfAM7NCWiBcCDhnd+3NNM5KbBmLTgHVfWBcg6W+rLUsIpzpERPsvwUP7CckAQSOoA== - lru-memoizer@2.3.0: version "2.3.0" resolved "https://registry.yarnpkg.com/lru-memoizer/-/lru-memoizer-2.3.0.tgz#ef0fbc021bceb666794b145eefac6be49dc47f31" @@ -21857,7 +22403,7 @@ mime-db@1.52.0: resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.54.0.tgz#cddb3ee4f9c64530dff640236661d42cb6a314f5" integrity sha512-aU5EJuIN2WDemCcAp2vFBfp/m4EAhWJnUNSSw0ixs7/kXbd6Pg64EmwJkNdFhB8aWt1sH2CTXrLxo/iAGV3oPQ== -mime-types@^2.1.12, mime-types@^2.1.18, mime-types@^2.1.26, mime-types@^2.1.27, mime-types@^2.1.31, mime-types@~2.1.17, mime-types@~2.1.24, mime-types@~2.1.34: +mime-types@^2.1.12, mime-types@^2.1.18, mime-types@^2.1.26, mime-types@^2.1.27, mime-types@^2.1.31, mime-types@~2.1.17, mime-types@~2.1.19, mime-types@~2.1.24, mime-types@~2.1.34: version "2.1.35" resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-2.1.35.tgz#381a871b62a734450660ae3deee44813f70d959a" integrity sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw== @@ -21876,7 +22422,7 @@ mime@1.6.0, mime@^1.4.1: resolved "https://registry.yarnpkg.com/mime/-/mime-1.6.0.tgz#32cd9e5c64553bd58d19a568af452acff04981b1" integrity sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg== -mime@^3.0.0: +mime@3.0.0, mime@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/mime/-/mime-3.0.0.tgz#b374550dca3a0c18443b0c950a6a58f1931cf7a7" integrity sha512-jSCU7/VB1loIWBZe14aEYHU/+1UMEHoaO7qxCOVJOw9GgH72VAWppxNcjU+x9a2k3GSIBXNKxXQFqRvvZ7vr3A== @@ -21906,6 +22452,11 @@ mimic-fn@^4.0.0: resolved "https://registry.yarnpkg.com/mimic-fn/-/mimic-fn-4.0.0.tgz#60a90550d5cb0b239cca65d893b1a53b29871ecc" integrity sha512-vqiC06CuhBTUdZH+RYl8sFrL096vA45Ok5ISO6sE/Mr1jRbGH4Csnhi8f3wKVl7x8mO4Au7Ir9D3Oyv1VYMFJw== +mimic-response@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/mimic-response/-/mimic-response-1.0.1.tgz#4923538878eef42063cb8a3e3b0798781487ab1b" + integrity sha512-j5EctnkH7amfV/q5Hgmoal1g2QHFJRraOtmx0JpIqkxhBhI/lJSl1nMpQ45hVarwNETOoWEimndZ4QK0RHxuxQ== + mimic-response@^3.1.0: version "3.1.0" resolved "https://registry.yarnpkg.com/mimic-response/-/mimic-response-3.1.0.tgz#2d1d59af9c1b129815accc2c46a022a5ce1fa3c9" @@ -21952,7 +22503,7 @@ minimalistic-assert@^1.0.0: resolved "https://registry.yarnpkg.com/minimalistic-assert/-/minimalistic-assert-1.0.1.tgz#2e194de044626d4a10e7f7fbc00ce73e83e4d5c7" integrity sha512-UtJcAD4yEaGtjPezWuO9wC4nwUnVH/8/Im3yEHQP4b67cXlD/Qr9hdITCU1xDbSEXg2XKNaP8jsReV7vQd00/A== -minimatch@10.1.1, minimatch@10.2.4, minimatch@^10.2.2, minimatch@^10.2.4: +minimatch@10.1.1, minimatch@10.2.4: version "10.2.4" resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-10.2.4.tgz#465b3accbd0218b8281f5301e27cedc697f96fde" integrity sha512-oRjTw/97aTBN0RHbYCdtF1MQfvusSIBQM0IZEgzl6426+8jSC0nF1a/GmnVLpfB9yyr6g6FTqWqiZVbxrtaCIg== @@ -21973,13 +22524,20 @@ minimatch@5.1.0, minimatch@5.1.9, minimatch@^5.0.1, minimatch@^5.1.0: dependencies: brace-expansion "^2.0.1" -minimatch@^7.4.1: +minimatch@7.4.6, minimatch@7.4.9, minimatch@^7.4.1, minimatch@~7.4.9: version "7.4.9" resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-7.4.9.tgz#ef35412b1b36261b78ef1b2f0db29b759bbcaf5d" integrity sha512-Brg/fp/iAVDOQoHxkuN5bEYhyQlZhxddI78yWsCbeEwTHXQjlNLtiJDUsp1GIptVqMI7/gkJMz4vVAc01mpoBw== dependencies: brace-expansion "^2.0.2" +minimatch@^10.2.2, minimatch@^10.2.4: + version "10.2.5" + resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-10.2.5.tgz#bd48687a0be38ed2961399105600f832095861d1" + integrity sha512-MULkVLfKGYDFYejP07QOurDLLQpcjk7Fw+7jXS2R2czRQzR56yHRveU5NDJEOviH+hETZKSkIk5c+T23GjFUMg== + dependencies: + brace-expansion "^5.0.5" + minimatch@^8.0.2: version "8.0.7" resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-8.0.7.tgz#954766e22da88a3e0a17ad93b58c15c9d8a579de" @@ -21988,11 +22546,11 @@ minimatch@^8.0.2: brace-expansion "^2.0.1" minimatch@^9.0.0, minimatch@^9.0.4: - version "9.0.8" - resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-9.0.8.tgz#bb3aa36d7b42ea77a93c44d5c1082b188112497c" - integrity sha512-reYkDYtj/b19TeqbNZCV4q9t+Yxylf/rYBsLb42SXJatTv4/ylq5lEiAmhA/IToxO7NI2UzNMghHoHuaqDkAjw== + version "9.0.9" + resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-9.0.9.tgz#9b0cb9fcb78087f6fd7eababe2511c4d3d60574e" + integrity sha512-OBwBN9AL4dqmETlpS2zasx+vTeWclWzkblfZk7KTA5j3jeOONz/tRCnZomUyvNg83wL5Zv9Ss6HMJXAgL8R2Yg== dependencies: - brace-expansion "^5.0.2" + brace-expansion "^2.0.2" minimist@^0.2.1: version "0.2.4" @@ -22114,6 +22672,11 @@ mkdirp-classic@^0.5.2, mkdirp-classic@^0.5.3: resolved "https://registry.yarnpkg.com/mkdirp-classic/-/mkdirp-classic-0.5.3.tgz#fa10c9115cc6d8865be221ba47ee9bed78601113" integrity sha512-gKLcREMhtuZRwRAfqP3RFW+TK4JqApVBtOIftVgjuABpAtpxhPGaDcfvbhNvD0B8iD1oUr/txX35NjcaY6Ns/A== +mkdirp@1.0.4, mkdirp@^1.0.3, mkdirp@^1.0.4: + version "1.0.4" + resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-1.0.4.tgz#3eb5ed62622756d79a5f0e2a221dfebad75c2f7e" + integrity sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw== + mkdirp@^0.5.0, mkdirp@^0.5.1, mkdirp@^0.5.5, mkdirp@^0.5.6: version "0.5.6" resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-0.5.6.tgz#7def03d2432dcae4ba1d611445c48396062255f6" @@ -22121,11 +22684,6 @@ mkdirp@^0.5.0, mkdirp@^0.5.1, mkdirp@^0.5.5, mkdirp@^0.5.6: dependencies: minimist "^1.2.6" -mkdirp@^1.0.3, mkdirp@^1.0.4: - version "1.0.4" - resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-1.0.4.tgz#3eb5ed62622756d79a5f0e2a221dfebad75c2f7e" - integrity sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw== - mkdirp@^3.0.1, mkdirp@~3.0.0: version "3.0.1" resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-3.0.1.tgz#e44e4c5607fb279c168241713cc6e0fea9adcb50" @@ -22508,11 +23066,16 @@ needle@^3.1.0: iconv-lite "^0.6.3" sax "^1.2.4" -negotiator@0.6.3, negotiator@^0.6.3: +negotiator@0.6.3: version "0.6.3" resolved "https://registry.yarnpkg.com/negotiator/-/negotiator-0.6.3.tgz#58e323a72fedc0d6f9cd4d31fe49f51479590ccd" integrity sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg== +negotiator@^0.6.3, negotiator@~0.6.4: + version "0.6.4" + resolved "https://registry.yarnpkg.com/negotiator/-/negotiator-0.6.4.tgz#777948e2452651c570b712dd01c23e262713fff7" + integrity sha512-myRT3DiWPHqho5PrJaIRyaMv2kgYf0mUVgBNOYMuCH5Ki1yEiQaf/ZJuQ62nvpc44wL5WDbTX7yGJi1Neevw8w== + negotiator@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/negotiator/-/negotiator-1.0.0.tgz#b6c91bb47172d69f93cfd7c357bbb529019b5f6a" @@ -22764,6 +23327,13 @@ node-fetch@^2.3.0, node-fetch@^2.6.1, node-fetch@^2.6.7, node-fetch@^2.6.9: dependencies: whatwg-url "^5.0.0" +node-fetch@cjs: + version "2.6.7" + resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-2.6.7.tgz#24de9fba827e3b4ae44dc8b20256a379160052ad" + integrity sha512-ZjMPFEfVx5j+y2yF35Kzx5sF7kDzxuDj6ziH4FFbOp87zKDZNx8yExJIb05OGF4Nlt9IHFIMBkRl41VdvcNdbQ== + dependencies: + whatwg-url "^5.0.0" + node-forge@^1, node-forge@^1.3.1: version "1.4.0" resolved "https://registry.yarnpkg.com/node-forge/-/node-forge-1.4.0.tgz#1c7b7d8bdc2d078739f58287d589d903a11b2fc2" @@ -22914,6 +23484,11 @@ normalize-path@^3.0.0, normalize-path@~3.0.0: resolved "https://registry.yarnpkg.com/normalize-path/-/normalize-path-3.0.0.tgz#0dcd69ff23a1c9b11fd0978316644a0388216a65" integrity sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA== +normalize-url@^6.0.1: + version "6.1.0" + resolved "https://registry.yarnpkg.com/normalize-url/-/normalize-url-6.1.0.tgz#40d0885b535deffe3f3147bec877d05fe4c5668a" + integrity sha512-DlL+XwOy3NxAQ8xuC0okPgK46iuVNAK01YN7RueYBqqFeGsBjV9XmCAzAdgt+667bCl5kPh9EqKKDwnaPG1I7A== + npm-bundled@^1.1.1: version "1.1.2" resolved "https://registry.yarnpkg.com/npm-bundled/-/npm-bundled-1.1.2.tgz#944c78789bd739035b70baa2ca5cc32b8d860bc1" @@ -23392,7 +23967,7 @@ on-change@^5.0.1: resolved "https://registry.yarnpkg.com/on-change/-/on-change-5.0.1.tgz#ced60d262211eee41043e7479515b4875d1744ef" integrity sha512-n7THCP7RkyReRSLkJb8kUWoNsxUIBxTkIp3JKno+sEz6o/9AJ3w3P9fzQkITEkMwyTKJjZciF3v/pVoouxZZMg== -on-exit-leak-free@^2.1.0: +on-exit-leak-free@2.1.2, on-exit-leak-free@^2.1.0: version "2.1.2" resolved "https://registry.yarnpkg.com/on-exit-leak-free/-/on-exit-leak-free-2.1.2.tgz#fed195c9ebddb7d9e4c3842f93f281ac8dadd3b8" integrity sha512-0eJJY6hXLGf1udHwfNftBqH+g73EU4B504nZeKpz1sYRKafAghwxEJunB2O7rDZkL4PGfsMVnTXZ2EjibbqcsA== @@ -23416,6 +23991,11 @@ on-headers@~1.0.2: resolved "https://registry.yarnpkg.com/on-headers/-/on-headers-1.0.2.tgz#772b0ae6aaa525c399e489adfad90c403eb3c28f" integrity sha512-pZAE+FJLoyITytdqK0U5s+FIpjN0JP3OzFi/u8Rx+EV5/W+JTWGXG8xFzevE7AjBfDqHv/8vL8qQsIhHnqRkrA== +on-headers@~1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/on-headers/-/on-headers-1.1.0.tgz#59da4f91c45f5f989c6e4bcedc5a3b0aed70ff65" + integrity sha512-737ZY3yNnXy37FHkQxPzt4UZ2UWPWiCZWLvFZ4fu5cueciegX0zGPnrlY6bwRg4FdQOe9YU8MkmJwGhoMybl8A== + once@^1.3.0, once@^1.3.1, once@^1.4.0: version "1.4.0" resolved "https://registry.yarnpkg.com/once/-/once-1.4.0.tgz#583b1aa775961d4b113ac17d9c50baef9dd76bd1" @@ -23701,6 +24281,11 @@ oxlint@^1.53.0: "@oxlint/binding-win32-ia32-msvc" "1.53.0" "@oxlint/binding-win32-x64-msvc" "1.53.0" +p-cancelable@2.1.1: + version "2.1.1" + resolved "https://registry.yarnpkg.com/p-cancelable/-/p-cancelable-2.1.1.tgz#aab7fbd416582fa32a3db49859c122487c5ed2cf" + integrity sha512-BZOr3nRQHOntUjTrH8+Lh54smKHoHyur8We1V8DSMVrl5A2malOOwuJRnKRDjSnkoeBh4at6BwEnb5I7Jl31wg== + p-defer@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/p-defer/-/p-defer-1.0.0.tgz#9f6eb182f6c9aa8cd743004a7d4f96b196b0fb0c" @@ -23903,6 +24488,11 @@ pako@^1.0.3: resolved "https://registry.yarnpkg.com/pako/-/pako-1.0.11.tgz#6c9599d340d54dfd3946380252a35705a6b992bf" integrity sha512-4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw== +pako@~0.2.0: + version "0.2.9" + resolved "https://registry.yarnpkg.com/pako/-/pako-0.2.9.tgz#f3f7522f4ef782348da8161bad9ecfd51bf83a75" + integrity sha512-NUcwaKxUxWrZLpDG+z/xZaCgQITkA/Dv4V/T6bw7VON6l1Xz/VnrBqrYjZQ12TamKHzITTfOEIYUj48y2KXImA== + param-case@^3.0.4: version "3.0.4" resolved "https://registry.yarnpkg.com/param-case/-/param-case-3.0.4.tgz#7d17fe4aa12bde34d4a77d91acfb6219caad01c5" @@ -24151,6 +24741,15 @@ pathval@^2.0.0: resolved "https://registry.yarnpkg.com/pathval/-/pathval-2.0.0.tgz#7e2550b422601d4f6b8e26f1301bc8f15a741a25" integrity sha512-vE7JKRyES09KiunauX7nd2Q9/L7lhok4smP9RZTDeD4MVs72Dp2qNFVz39Nz5a0FVEW0BJR6C0DYrq6unoziZA== +peek-stream@^1.1.0: + version "1.1.3" + resolved "https://registry.yarnpkg.com/peek-stream/-/peek-stream-1.1.3.tgz#3b35d84b7ccbbd262fff31dc10da56856ead6d67" + integrity sha512-FhJ+YbOSBb9/rIl2ZeE/QHEsWn7PqNYt8ARAY3kIgNGOk13g9FGyIY6JIl/xB/3TFRVoTv5as0l11weORrTekA== + dependencies: + buffer-from "^1.0.0" + duplexify "^3.5.0" + through2 "^2.0.3" + pend@~1.2.0: version "1.2.0" resolved "https://registry.yarnpkg.com/pend/-/pend-1.2.0.tgz#7a57eb550a6783f9115331fcf4663d5c8e007a50" @@ -24171,6 +24770,11 @@ perfect-debounce@^2.0.0, perfect-debounce@^2.1.0: resolved "https://registry.yarnpkg.com/perfect-debounce/-/perfect-debounce-2.1.0.tgz#e7078e38f231cb191855c3136a4423aef725d261" integrity sha512-LjgdTytVFXeUgtHZr9WYViYSM/g8MkcTPYDlPa3cDqMirHjKiSZPYd6DoL7pK8AJQr+uWkQvCjHNdiMqsrJs+g== +performance-now@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/performance-now/-/performance-now-2.1.0.tgz#6309f4e0e5fa913ec1c69307ae364b4b377c9e7b" + integrity sha512-7EAHlyLHI56VEIdK57uwHdHKIaAGbnXPiw0yWbarQZOKaKpvUIgW0jWRVLiatnM+XXlSwsanIBH/hzGMJulMow== + periscopic@^3.1.0: version "3.1.0" resolved "https://registry.yarnpkg.com/periscopic/-/periscopic-3.1.0.tgz#7e9037bf51c5855bd33b48928828db4afa79d97a" @@ -24279,6 +24883,11 @@ pify@^2.3.0: resolved "https://registry.yarnpkg.com/pify/-/pify-2.3.0.tgz#ed141a6ac043a849ea588498e7dca8b15330e90c" integrity sha1-7RQaasBDqEnqWISY59yosVMw6Qw= +pify@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/pify/-/pify-3.0.0.tgz#e5a4acd2c101fdf3d9a4d07f0dbc4db49dd28176" + integrity sha512-C3FsVNH1udSEX48gGX1xfvwTWfsYWj5U+8/uK15BGzIGrKoUpghX8hWZwa/OFnakBiiVNmBvemTJR5mcy7iPcg== + pify@^4.0.1: version "4.0.1" resolved "https://registry.yarnpkg.com/pify/-/pify-4.0.1.tgz#4b2cd25c50d598735c50292224fd8c6df41e3231" @@ -24296,6 +24905,14 @@ pinkie@^2.0.0: resolved "https://registry.yarnpkg.com/pinkie/-/pinkie-2.0.4.tgz#72556b80cfa0d48a974e80e77248e80ed4f7f870" integrity sha1-clVrgM+g1IqXToDnckjoDtT3+HA= +pino-abstract-transport@1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/pino-abstract-transport/-/pino-abstract-transport-1.2.0.tgz#97f9f2631931e242da531b5c66d3079c12c9d1b5" + integrity sha512-Guhh8EZfPCfH+PMXAb6rKOjGQEoy0xlAIn+irODG5kgfYV+BQ0rGYYWTIel3P5mmyXqkYkPmdIkywsn6QKUR1Q== + dependencies: + readable-stream "^4.0.0" + split2 "^4.0.0" + pino-abstract-transport@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/pino-abstract-transport/-/pino-abstract-transport-2.0.0.tgz#de241578406ac7b8a33ce0d77ae6e8a0b3b68a60" @@ -24342,6 +24959,23 @@ pino@9.10.0: sonic-boom "^4.0.1" thread-stream "^3.0.0" +pino@9.14.0: + version "9.14.0" + resolved "https://registry.yarnpkg.com/pino/-/pino-9.14.0.tgz#673d9711c2d1e64d18670c1ec05ef7ba14562556" + integrity sha512-8OEwKp5juEvb/MjpIc4hjqfgCNysrS94RIOMXYvpYCdm/jglrKEiAYmiumbmGhCvs+IcInsphYDFwqrjr7398w== + dependencies: + "@pinojs/redact" "^0.4.0" + atomic-sleep "^1.0.0" + on-exit-leak-free "^2.1.0" + pino-abstract-transport "^2.0.0" + pino-std-serializers "^7.0.0" + process-warning "^5.0.0" + quick-format-unescaped "^4.0.3" + real-require "^0.2.0" + safe-stable-stringify "^2.3.1" + sonic-boom "^4.0.1" + thread-stream "^3.0.0" + pirates@^4.0.1: version "4.0.6" resolved "https://registry.yarnpkg.com/pirates/-/pirates-4.0.6.tgz#3018ae32ecfcff6c29ba2267cbf21166ac1f36b9" @@ -25265,6 +25899,11 @@ process-relative-require@^1.0.0: dependencies: node-modules-path "^1.0.0" +process-warning@1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/process-warning/-/process-warning-1.0.0.tgz#980a0b25dc38cd6034181be4b7726d89066b4616" + integrity sha512-du4wfLyj4yCZq1VupnVSZmRsPJsNuxoDQFdCFHLaYiEbFBD7QE0a+I4D7hOxrVnh78QE/YipFAj9lXHiXocV+Q== + process-warning@^5.0.0: version "5.0.0" resolved "https://registry.yarnpkg.com/process-warning/-/process-warning-5.0.0.tgz#566e0bf79d1dff30a72d8bbbe9e8ecefe8d378d7" @@ -25411,6 +26050,14 @@ pstree.remy@^1.1.8: resolved "https://registry.yarnpkg.com/pstree.remy/-/pstree.remy-1.1.8.tgz#c242224f4a67c21f686839bbdb4ac282b8373d3a" integrity sha512-77DZwxQmxKnu3aR542U+X8FypNzbfJ+C5XQDk3uWjWxn6151aIMGthWYRXTqT1E5oJvg+ljaa2OJi+VfvCOQ8w== +pump@^2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/pump/-/pump-2.0.1.tgz#12399add6e4cf7526d973cbc8b5ce2e2908b3909" + integrity sha512-ruPMNRkN3MHP1cWJc9OWr+T/xDP0jhXYCLfJcBuX54hhfIBnaQmAUMfDcG4DM5UMWByBbJY69QSphm3jtDKIkA== + dependencies: + end-of-stream "^1.1.0" + once "^1.3.1" + pump@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/pump/-/pump-3.0.0.tgz#b4a2116815bde2f4e1ea602354e8c75565107a64" @@ -25419,6 +26066,15 @@ pump@^3.0.0: end-of-stream "^1.1.0" once "^1.3.1" +pumpify@^1.3.3: + version "1.5.1" + resolved "https://registry.yarnpkg.com/pumpify/-/pumpify-1.5.1.tgz#36513be246ab27570b1a374a5ce278bfd74370ce" + integrity sha512-oClZI37HvuUJJxSKKrC17bZ9Cu0ZYhEAGPsPUy9KlMUmv9dKX2o77RUmq7f3XjIxbwyGwYzbzQ1L2Ks8sIradQ== + dependencies: + duplexify "^3.6.0" + inherits "^2.0.3" + pump "^2.0.0" + punycode@^2.1.0, punycode@^2.1.1, punycode@^2.3.0, punycode@^2.3.1: version "2.3.1" resolved "https://registry.yarnpkg.com/punycode/-/punycode-2.3.1.tgz#027422e2faec0b25e1549c3e1bd8309b9133b6e5" @@ -25429,7 +26085,7 @@ pure-rand@^6.1.0: resolved "https://registry.yarnpkg.com/pure-rand/-/pure-rand-6.1.0.tgz#d173cf23258231976ccbdb05247c9787957604f2" integrity sha512-bVWawvoZoBYpp6yIoQtQXHZjmz35RSVHnUOTefl8Vcjr8snTPY1wnpSPMWekcFwbxI6gtmT7rSYPFvz71ldiOA== -qs@^6.14.0, qs@^6.14.1, qs@^6.4.0, qs@~6.14.0: +qs@^6.14.0, qs@^6.14.1, qs@^6.4.0, qs@~6.14.0, qs@~6.14.1: version "6.14.2" resolved "https://registry.yarnpkg.com/qs/-/qs-6.14.2.tgz#b5634cf9d9ad9898e31fba3504e866e8efb6798c" integrity sha512-V/yCWTTF7VJ9hIh18Ugr2zhJMP01MY7c5kh4J870L7imm6/DIzBsNLTXzMwUA3yZ5b/KBqLx8Kp3uRvd7xSe3Q== @@ -25464,6 +26120,11 @@ quick-format-unescaped@^4.0.3: resolved "https://registry.yarnpkg.com/quick-format-unescaped/-/quick-format-unescaped-4.0.4.tgz#93ef6dd8d3453cbc7970dd614fad4c5954d6b5a7" integrity sha512-tYC1Q1hgyRuHgloV/YXs2w15unPVh8qfu/qCTfhTYamaw7fyhumKa2yGpdSo87vY32rIclj+4fWYQXUMs9EHvg== +quick-lru@^5.1.1: + version "5.1.1" + resolved "https://registry.yarnpkg.com/quick-lru/-/quick-lru-5.1.1.tgz#366493e6b3e42a3a6885e2e99d18f80fb7a8c932" + integrity sha512-WuyALRjWPDGtt/wzJiadO5AXY+8hZ80hVpe6MyivgraREW751X3SbhRvG3eLKOYN+8VEvqLcf3wdnt44Z4S4SA== + quick-temp@^0.1.2, quick-temp@^0.1.3, quick-temp@^0.1.5, quick-temp@^0.1.8: version "0.1.8" resolved "https://registry.yarnpkg.com/quick-temp/-/quick-temp-0.1.8.tgz#bab02a242ab8fb0dd758a3c9776b32f9a5d94408" @@ -25738,7 +26399,7 @@ readable-stream@2.3.7: string_decoder "~1.1.1" util-deprecate "~1.0.1" -readable-stream@^2.0.1, readable-stream@^2.0.5, readable-stream@^2.3.5: +readable-stream@^2.0.0, readable-stream@^2.0.1, readable-stream@^2.0.5, readable-stream@^2.3.5, readable-stream@~2.3.6: version "2.3.8" resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-2.3.8.tgz#91125e8042bba1b9887f49345f6277027ce8be9b" integrity sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA== @@ -26213,6 +26874,11 @@ reselect@^4.0.0, reselect@^4.1.7: resolved "https://registry.yarnpkg.com/reselect/-/reselect-4.1.8.tgz#3f5dc671ea168dccdeb3e141236f69f02eaec524" integrity sha512-ab9EmR80F/zQTMNeneUr4cv+jSwPJgIlvEmVwLerwrWVbpLlBuls9XHzIeTFy4cegU2NHBp3va0LKOzU5qFEYQ== +resolve-alpn@^1.2.0: + version "1.2.1" + resolved "https://registry.yarnpkg.com/resolve-alpn/-/resolve-alpn-1.2.1.tgz#b7adbdac3546aaaec20b45e7d8265927072726f9" + integrity sha512-0a1F4l73/ZFZOakJnQ3FvkJ2+gSTQWz/r2KE5OdDY0TxPm5h4GkqkWWfM47T7HsbnOtcJVEF4epCVy6u7Q3K+g== + resolve-dependency-path@^4.0.1: version "4.0.1" resolved "https://registry.yarnpkg.com/resolve-dependency-path/-/resolve-dependency-path-4.0.1.tgz#1b9d43e5b62384301e26d040b9fce61ee5db60bd" @@ -26333,6 +26999,13 @@ resolve@^2.0.0-next.3: path-parse "^1.0.7" supports-preserve-symlinks-flag "^1.0.0" +responselike@2.0.1, responselike@^2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/responselike/-/responselike-2.0.1.tgz#9a0bc8fdc252f3fb1cca68b016591059ba1422bc" + integrity sha512-4gl03wn3hj1HP3yzgdI7d3lCkF95F21Pz4BPGvKHinyQzALR5CapwC8yIi0Rh58DEMQ/SguC03wFj2k0M/mHhw== + dependencies: + lowercase-keys "^2.0.0" + restore-cursor@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/restore-cursor/-/restore-cursor-2.0.0.tgz#9f7ee287f82fd326d4fd162923d62129eee0dfaf" @@ -26739,7 +27412,7 @@ safe-stable-stringify@^2.3.1, safe-stable-stringify@^2.4.1, safe-stable-stringif resolved "https://registry.yarnpkg.com/safe-stable-stringify/-/safe-stable-stringify-2.5.0.tgz#4ca2f8e385f2831c432a719b108a3bf7af42a1dd" integrity sha512-b3rppTKm9T+PsVCBEOUR46GWI7fdOs00VKZ1+9c1EWDaDMvjQc6tUwuFyIprgGgTcWoVHSKrU8H31ZHA2e0RHA== -"safer-buffer@>= 2.1.2 < 3", "safer-buffer@>= 2.1.2 < 3.0.0": +"safer-buffer@>= 2.1.2 < 3", "safer-buffer@>= 2.1.2 < 3.0.0", safer-buffer@^2.0.2, safer-buffer@^2.1.0, safer-buffer@~2.1.0: version "2.1.2" resolved "https://registry.yarnpkg.com/safer-buffer/-/safer-buffer-2.1.2.tgz#44fa161b0187b9549dd84bb91802f9bd8385cd6a" integrity sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg== @@ -26919,6 +27592,16 @@ semver@7.5.3: dependencies: lru-cache "^6.0.0" +semver@7.7.2: + version "7.7.2" + resolved "https://registry.yarnpkg.com/semver/-/semver-7.7.2.tgz#67d99fdcd35cec21e6f8b87a7fd515a33f982b58" + integrity sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA== + +semver@7.7.4, semver@^7.0.0, semver@^7.1.1, semver@^7.3.2, semver@^7.3.4, semver@^7.3.5, semver@^7.3.7, semver@^7.3.8, semver@^7.5.0, semver@^7.5.3, semver@^7.5.4, semver@^7.6.0, semver@^7.6.2, semver@^7.6.3, semver@^7.7.2, semver@^7.7.3, semver@^7.7.4: + version "7.7.4" + resolved "https://registry.yarnpkg.com/semver/-/semver-7.7.4.tgz#28464e36060e991fa7a11d0279d2d3f3b57a7e8a" + integrity sha512-vFKC2IEtQnVhpT78h1Yp8wzwrf8CM+MzKMHGJZfBtzhZNycRFnXsHk6E5TxIkkMsgNS7mdX3AGB7x2QM2di4lA== + semver@^5.3.0, semver@^5.4.1, semver@^5.5.0, semver@^5.6.0, semver@^5.7.1: version "5.7.2" resolved "https://registry.yarnpkg.com/semver/-/semver-5.7.2.tgz#48d55db737c3287cd4835e17fa13feace1c41ef8" @@ -26929,11 +27612,6 @@ semver@^6.0.0, semver@^6.1.0, semver@^6.1.1, semver@^6.1.2, semver@^6.3.0, semve resolved "https://registry.yarnpkg.com/semver/-/semver-6.3.1.tgz#556d2ef8689146e46dcea4bfdd095f3434dffcb4" integrity sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA== -semver@^7.0.0, semver@^7.1.1, semver@^7.3.2, semver@^7.3.4, semver@^7.3.5, semver@^7.3.7, semver@^7.3.8, semver@^7.5.0, semver@^7.5.3, semver@^7.5.4, semver@^7.6.0, semver@^7.6.2, semver@^7.6.3, semver@^7.7.2, semver@^7.7.3, semver@^7.7.4: - version "7.7.4" - resolved "https://registry.yarnpkg.com/semver/-/semver-7.7.4.tgz#28464e36060e991fa7a11d0279d2d3f3b57a7e8a" - integrity sha512-vFKC2IEtQnVhpT78h1Yp8wzwrf8CM+MzKMHGJZfBtzhZNycRFnXsHk6E5TxIkkMsgNS7mdX3AGB7x2QM2di4lA== - send@^1.1.0, send@^1.2.0: version "1.2.1" resolved "https://registry.yarnpkg.com/send/-/send-1.2.1.tgz#9eab743b874f3550f40a26867bf286ad60d3f3ed" @@ -27538,6 +28216,13 @@ solid-use@^0.8.0: resolved "https://registry.yarnpkg.com/solid-use/-/solid-use-0.8.0.tgz#d46258c45edb0f4c621285e0ad1aa6b6a674d79b" integrity sha512-YX+XmcKLvSx3bwMimMhFy40ZkDnShnUcEw6cW6fSscwKEgl1TG3GlgAvkBmQ3AeWjvQSd8+HGTr82ImsrjkkqA== +sonic-boom@3.8.1: + version "3.8.1" + resolved "https://registry.yarnpkg.com/sonic-boom/-/sonic-boom-3.8.1.tgz#d5ba8c4e26d6176c9a1d14d549d9ff579a163422" + integrity sha512-y4Z8LCDBuum+PBP3lSV7RHrXscqksve/bi0as7mhwVnBW+/wUqKT/2Kb7um8yqcFy0duYbbPxzt89Zy2nOCaxg== + dependencies: + atomic-sleep "^1.0.0" + sonic-boom@^4.0.1: version "4.2.0" resolved "https://registry.yarnpkg.com/sonic-boom/-/sonic-boom-4.2.0.tgz#e59a525f831210fa4ef1896428338641ac1c124d" @@ -27822,6 +28507,21 @@ srvx@^0.11.12, srvx@^0.11.2, srvx@^0.11.9: resolved "https://registry.yarnpkg.com/srvx/-/srvx-0.11.13.tgz#cc77a98cb9a459c34f75ee4345bd0eef9f613a54" integrity sha512-oknN6qduuMPafxKtHucUeG32Q963pjriA5g3/Bl05cwEsUe5VVbIU4qR9LrALHbipSCyBe+VmfDGGydqazDRkw== +sshpk@^1.18.0: + version "1.18.0" + resolved "https://registry.yarnpkg.com/sshpk/-/sshpk-1.18.0.tgz#1663e55cddf4d688b86a46b77f0d5fe363aba028" + integrity sha512-2p2KJZTSqQ/I3+HX42EpYOa2l3f8Erv8MWKsy2I9uf4wA7yFIkXRffYdsx86y6z4vHtV8u7g+pPlr8/4ouAxsQ== + dependencies: + asn1 "~0.2.3" + assert-plus "^1.0.0" + bcrypt-pbkdf "^1.0.0" + dashdash "^1.12.0" + ecc-jsbn "~0.1.1" + getpass "^0.1.1" + jsbn "~0.1.0" + safer-buffer "^2.0.2" + tweetnacl "~0.14.0" + ssri@^9.0.0: version "9.0.1" resolved "https://registry.yarnpkg.com/ssri/-/ssri-9.0.1.tgz#544d4c357a8d7b71a19700074b6883fcb4eae057" @@ -27871,6 +28571,11 @@ static-extend@^0.1.1: define-property "^0.2.5" object-copy "^0.1.0" +statuses@2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/statuses/-/statuses-2.0.1.tgz#55cb000ccf1d48728bd23c685a063998cf1a1b63" + integrity sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ== + "statuses@>= 1.4.0 < 2", statuses@~1.5.0: version "1.5.0" resolved "https://registry.yarnpkg.com/statuses/-/statuses-1.5.0.tgz#161c7dac177659fd9811f43771fa99381478628c" @@ -27893,6 +28598,13 @@ stdin-discarder@^0.1.0: dependencies: bl "^5.0.0" +steno@^0.4.1: + version "0.4.4" + resolved "https://registry.yarnpkg.com/steno/-/steno-0.4.4.tgz#071105bdfc286e6615c0403c27e9d7b5dcb855cb" + integrity sha512-EEHMVYHNXFHfGtgjNITnka0aHhiAlo93F7z2/Pwd+g0teG9CnM3JIINM7hVVB5/rhw9voufD7Wukwgtw2uqh6w== + dependencies: + graceful-fs "^4.1.3" + stop-iteration-iterator@^1.0.0, stop-iteration-iterator@^1.1.0: version "1.1.0" resolved "https://registry.yarnpkg.com/stop-iteration-iterator/-/stop-iteration-iterator-1.1.0.tgz#f481ff70a548f6124d0312c3aa14cbfa7aa542ad" @@ -28479,6 +29191,15 @@ tar-fs@^3.0.4: bare-fs "^4.0.1" bare-path "^3.0.0" +tar-stream@3.1.7, tar-stream@^3.0.0, tar-stream@^3.1.5, tar-stream@^3.1.7: + version "3.1.7" + resolved "https://registry.yarnpkg.com/tar-stream/-/tar-stream-3.1.7.tgz#24b3fb5eabada19fe7338ed6d26e5f7c482e792b" + integrity sha512-qJj60CXt7IU1Ffyc3NJMjh6EkuCFej46zUqJ4J7pqYlThyd9bO0XBTmcOIhSzZJVWfsLks0+nle/j538YAW9RQ== + dependencies: + b4a "^1.6.4" + fast-fifo "^1.2.0" + streamx "^2.15.0" + tar-stream@^2.1.4, tar-stream@~2.2.0: version "2.2.0" resolved "https://registry.yarnpkg.com/tar-stream/-/tar-stream-2.2.0.tgz#acad84c284136b060dc3faa64474aa9aebd77287" @@ -28490,15 +29211,6 @@ tar-stream@^2.1.4, tar-stream@~2.2.0: inherits "^2.0.3" readable-stream "^3.1.1" -tar-stream@^3.0.0, tar-stream@^3.1.5, tar-stream@^3.1.7: - version "3.1.7" - resolved "https://registry.yarnpkg.com/tar-stream/-/tar-stream-3.1.7.tgz#24b3fb5eabada19fe7338ed6d26e5f7c482e792b" - integrity sha512-qJj60CXt7IU1Ffyc3NJMjh6EkuCFej46zUqJ4J7pqYlThyd9bO0XBTmcOIhSzZJVWfsLks0+nle/j538YAW9RQ== - dependencies: - b4a "^1.6.4" - fast-fifo "^1.2.0" - streamx "^2.15.0" - tar@^6.1.11, tar@^6.1.2: version "6.2.1" resolved "https://registry.yarnpkg.com/tar/-/tar-6.2.1.tgz#717549c541bc3c2af15751bea94b1dd068d4b03a" @@ -28697,6 +29409,14 @@ throttleit@2.1.0: resolved "https://registry.yarnpkg.com/throttleit/-/throttleit-2.1.0.tgz#a7e4aa0bf4845a5bd10daa39ea0c783f631a07b4" integrity sha512-nt6AMGKW1p/70DF/hGBdJB57B8Tspmbp5gfJ8ilhLnt7kkr2ye7hzD6NVG8GGErk2HWF34igrL2CXmNIkzKqKw== +through2@^2.0.3: + version "2.0.5" + resolved "https://registry.yarnpkg.com/through2/-/through2-2.0.5.tgz#01c1e39eb31d07cb7d03a96a70823260b23132cd" + integrity sha512-/mrRod8xqpA+IHSLyGCQ2s8SPHiCDEeQJSep1jqLYeEUClOFG2Qsh+4FU6G9VeqpZnGW/Su8LQGc4YKni5rYSQ== + dependencies: + readable-stream "~2.3.6" + xtend "~4.0.1" + through2@^3.0.1: version "3.0.2" resolved "https://registry.yarnpkg.com/through2/-/through2-3.0.2.tgz#99f88931cfc761ec7678b41d5d7336b5b6a07bf4" @@ -28705,7 +29425,7 @@ through2@^3.0.1: inherits "^2.0.4" readable-stream "2 || 3" -through@^2.3.6: +"through@>=2.2.7 <3", through@^2.3.6: version "2.3.8" resolved "https://registry.yarnpkg.com/through/-/through-2.3.8.tgz#0dd4c9ffaabc357960b1b724115d7e0e86a2e1f5" integrity sha1-DdTJ/6q8NXlgsbckEV1+Doai4fU= @@ -28801,6 +29521,18 @@ tinyspy@^4.0.3: resolved "https://registry.yarnpkg.com/tinyspy/-/tinyspy-4.0.3.tgz#d1d0f0602f4c15f1aae083a34d6d0df3363b1b52" integrity sha512-t2T/WLB2WRgZ9EpE4jgPJ9w+i66UZfDc8wHh0xrwiRNN+UwH98GIJkTeZqX9rg0i0ptwzqW+uYeIF0T4F8LR7A== +tldts-core@^6.1.86: + version "6.1.86" + resolved "https://registry.yarnpkg.com/tldts-core/-/tldts-core-6.1.86.tgz#a93e6ed9d505cb54c542ce43feb14c73913265d8" + integrity sha512-Je6p7pkk+KMzMv2XXKmAE3McmolOQFdxkKw0R8EYNr7sELW46JqnNeTX8ybPiQgvg1ymCoF8LXs5fzFaZvJPTA== + +tldts@^6.1.32: + version "6.1.86" + resolved "https://registry.yarnpkg.com/tldts/-/tldts-6.1.86.tgz#087e0555b31b9725ee48ca7e77edc56115cd82f7" + integrity sha512-WMi/OQ2axVTf/ykqCQgXiIct+mSQDFdH2fkwhPwgEwvJ1kSzZRiinb0zF2Xb8u4+OqPChmyI6MEu4EezNJz+FQ== + dependencies: + tldts-core "^6.1.86" + tmp@0.0.28: version "0.0.28" resolved "https://registry.yarnpkg.com/tmp/-/tmp-0.0.28.tgz#172735b7f614ea7af39664fa84cf0de4e515d120" @@ -28873,7 +29605,7 @@ to-regex@^3.0.1, to-regex@^3.0.2: regex-not "^1.0.2" safe-regex "^1.1.0" -toidentifier@~1.0.1: +toidentifier@1.0.1, toidentifier@~1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/toidentifier/-/toidentifier-1.0.1.tgz#3be34321a88a820ed1bd80dfaa33e479fbb8dd35" integrity sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA== @@ -28909,6 +29641,13 @@ tough-cookie@^4.1.2: universalify "^0.2.0" url-parse "^1.5.3" +tough-cookie@^5.0.0: + version "5.1.2" + resolved "https://registry.yarnpkg.com/tough-cookie/-/tough-cookie-5.1.2.tgz#66d774b4a1d9e12dc75089725af3ac75ec31bed7" + integrity sha512-FVDYdxtnj0G6Qm/DhNPSb8Ju59ULcup3tuJxkFb5K8Bv2pUXILbf0xZWU8PX8Ov19OXljbUyveOFwRMwkXzO+A== + dependencies: + tldts "^6.1.32" + tr46@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/tr46/-/tr46-3.0.0.tgz#555c4e297a950617e8eeddef633c87d4d9d6cbf9" @@ -29094,6 +29833,16 @@ turbo-stream@2.4.1: resolved "https://registry.yarnpkg.com/turbo-stream/-/turbo-stream-2.4.1.tgz#c1a64397724084c09b0f6ea4a2c528d3f67931f9" integrity sha512-v8kOJXpG3WoTN/+at8vK7erSzo6nW6CIaeOvNOkHQVDajfz1ZVeSxCbc6tOH4hrGZW7VUCV0TOXd8CPzYnYkrw== +tweetnacl@^0.14.3, tweetnacl@~0.14.0: + version "0.14.5" + resolved "https://registry.yarnpkg.com/tweetnacl/-/tweetnacl-0.14.5.tgz#5ae68177f192d4456269d108afa93ff8743f4f64" + integrity sha512-KXXFFdAbFXY4geFIwoyNK+f5Z1b7swfXABfL7HXCmoIWMKU3dmS26672A4EeQtDzLKy7SXmfBu51JolvEKwtGA== + +typanion@^3.8.0: + version "3.14.0" + resolved "https://registry.yarnpkg.com/typanion/-/typanion-3.14.0.tgz#a766a91810ce8258033975733e836c43a2929b94" + integrity sha512-ZW/lVMRabETuYCd9O9ZvMhAh8GslSqaUjxmK/JLPCh6l73CvLBiuXswj/+7LdnWOgYsQ130FqLzFz5aGT4I3Ug== + type-check@^0.4.0, type-check@~0.4.0: version "0.4.0" resolved "https://registry.yarnpkg.com/type-check/-/type-check-0.4.0.tgz#07b8203bfa7056c0657050e3ccd2c37730bab8f1" @@ -29661,6 +30410,11 @@ universalify@^2.0.0: resolved "https://registry.yarnpkg.com/universalify/-/universalify-2.0.0.tgz#75a4984efedc4b08975c5aeb73f530d02df25717" integrity sha512-hAZsKq7Yy11Zu1DE0OzWjw7nnLZmJZYTDZZyEFHZdUhV8FkH5MCfoU1XMaxXovpyW5nq5scPqq0ZDP9Zyl04oQ== +unix-crypt-td-js@1.1.4: + version "1.1.4" + resolved "https://registry.yarnpkg.com/unix-crypt-td-js/-/unix-crypt-td-js-1.1.4.tgz#4912dfad1c8aeb7d20fa0a39e4c31918c1d5d5dd" + integrity sha512-8rMeVYWSIyccIJscb9NdCfZKSRBKYTeVnwmiRYT2ulE3qd1RaDQ0xQDP+rI3ccIWbhu/zuo5cgN8z73belNZgw== + unpipe@~1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/unpipe/-/unpipe-1.0.0.tgz#b2bf4ee8514aae6165b4817829d21b2ef49904ec" @@ -29981,6 +30735,11 @@ validate-peer-dependencies@^1.1.0: resolve-package-path "^3.1.0" semver "^7.3.2" +validator@13.15.26: + version "13.15.26" + resolved "https://registry.yarnpkg.com/validator/-/validator-13.15.26.tgz#36c3deeab30e97806a658728a155c66fcaa5b944" + integrity sha512-spH26xU080ydGggxRyR1Yhcbgx+j3y5jbNXk/8L+iRvdIEQ4uTRH2Sgf2dokud6Q4oAtsbNvJ1Ft+9xmm6IZcA== + value-equal@^0.2.0: version "0.2.1" resolved "https://registry.yarnpkg.com/value-equal/-/value-equal-0.2.1.tgz#c220a304361fce6994dbbedaa3c7e1a1b895871d" @@ -30001,6 +30760,76 @@ vary@^1, vary@^1.1.2, vary@~1.1.2: resolved "https://registry.yarnpkg.com/vary/-/vary-1.1.2.tgz#2299f02c6ded30d4a5961b0b9f74524a18f634fc" integrity sha1-IpnwLG3tMNSllhsLn3RSShj2NPw= +verdaccio-audit@13.0.0-next-8.37: + version "13.0.0-next-8.37" + resolved "https://registry.yarnpkg.com/verdaccio-audit/-/verdaccio-audit-13.0.0-next-8.37.tgz#ab1dce7983f688e6ce84ae9b04644a3fc774e606" + integrity sha512-ckn4xxNEkK5lflwb8a6xs2j6rVe//9sEH4rJHBqh2RelKYnFkxHbnN06gsdV2KtqSKDD9F4NE2UDA9SSs8E90w== + dependencies: + "@verdaccio/config" "8.0.0-next-8.37" + "@verdaccio/core" "8.0.0-next-8.37" + express "4.22.1" + https-proxy-agent "5.0.1" + node-fetch cjs + +verdaccio-htpasswd@13.0.0-next-8.37: + version "13.0.0-next-8.37" + resolved "https://registry.yarnpkg.com/verdaccio-htpasswd/-/verdaccio-htpasswd-13.0.0-next-8.37.tgz#7b2be17f6ca9c35726c0baeb5089379bf54f5c2c" + integrity sha512-25MjzPLJG8mfPe4jtR8+3B8PCfBl0D2DRDnsP+KJPn8yBbUiO/GJaw2dyGOiVA7ZTyAWKDnN0WvmmIs+Ibj4+g== + dependencies: + "@verdaccio/core" "8.0.0-next-8.37" + "@verdaccio/file-locking" "13.0.0-next-8.7" + apache-md5 "1.1.8" + bcryptjs "2.4.3" + debug "4.4.3" + http-errors "2.0.1" + unix-crypt-td-js "1.1.4" + +verdaccio@6.5.0: + version "6.5.0" + resolved "https://registry.yarnpkg.com/verdaccio/-/verdaccio-6.5.0.tgz#d27d48ad7a21ce8f361a078137d784aa731d2b46" + integrity sha512-PFsGVvSyS47ZAt58MiL+5hX0jexgv20rrhEL2qEF5wGV9OLP9Cbm67aN5+PJT3pdQyghGk2Yq4/SVNwCmB/oHA== + dependencies: + "@cypress/request" "3.0.10" + "@verdaccio/auth" "8.0.0-next-8.37" + "@verdaccio/config" "8.0.0-next-8.37" + "@verdaccio/core" "8.0.0-next-8.37" + "@verdaccio/hooks" "8.0.0-next-8.37" + "@verdaccio/loaders" "8.0.0-next-8.27" + "@verdaccio/local-storage-legacy" "11.1.1" + "@verdaccio/logger" "8.0.0-next-8.37" + "@verdaccio/middleware" "8.0.0-next-8.37" + "@verdaccio/package-filter" "13.0.0-next-8.5" + "@verdaccio/search-indexer" "8.0.0-next-8.6" + "@verdaccio/signature" "8.0.0-next-8.29" + "@verdaccio/streams" "10.2.1" + "@verdaccio/tarball" "13.0.0-next-8.37" + "@verdaccio/ui-theme" "9.0.0-next-9.10" + "@verdaccio/url" "13.0.0-next-8.37" + "@verdaccio/utils" "8.1.0-next-8.37" + JSONStream "1.3.5" + async "3.2.6" + clipanion "4.0.0-rc.4" + compression "1.8.1" + cors "2.8.6" + debug "4.4.3" + envinfo "7.21.0" + express "4.22.1" + lodash "4.18.1" + lru-cache "7.18.3" + mime "3.0.0" + semver "7.7.4" + verdaccio-audit "13.0.0-next-8.37" + verdaccio-htpasswd "13.0.0-next-8.37" + +verror@1.10.0: + version "1.10.0" + resolved "https://registry.yarnpkg.com/verror/-/verror-1.10.0.tgz#3a105ca17053af55d6e270c1f8288682e18da400" + integrity sha512-ZZKSmDAEFOijERBLkmYfJ+vmk3w+7hOLYDNkRCuRuMJGEmqYNCNLyBBFwWKVMhfwaEF3WOd0Zlw86U/WC/+nYw== + dependencies: + assert-plus "^1.0.0" + core-util-is "1.0.2" + extsprintf "^1.2.0" + vfile-location@^4.0.0: version "4.1.0" resolved "https://registry.yarnpkg.com/vfile-location/-/vfile-location-4.1.0.tgz#69df82fb9ef0a38d0d02b90dd84620e120050dd0" @@ -30997,7 +31826,7 @@ xmlchars@^2.2.0: resolved "https://registry.yarnpkg.com/xmlchars/-/xmlchars-2.2.0.tgz#060fe1bcb7f9c76fe2a17db86a9bc3ab894210cb" integrity sha512-JZnDKK8B0RCDw84FNdDAIpZK+JuJw+s7Lz8nksI7SIuU3UXJJslUthsi+uWBUYOwPFwW7W7PRLRfUKpxjtjFCw== -xtend@^4.0.0, xtend@^4.0.2: +xtend@^4.0.0, xtend@^4.0.2, xtend@~4.0.1: version "4.0.2" resolved "https://registry.yarnpkg.com/xtend/-/xtend-4.0.2.tgz#bb72779f5fa465186b1f438f674fa347fdb5db54" integrity sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ== From e9ab83a7db5742118135ee2d7e3b39b7daf29aef Mon Sep 17 00:00:00 2001 From: Lukas Stracke Date: Fri, 17 Apr 2026 15:14:38 +0200 Subject: [PATCH 06/41] feat(browser): Add support for streamed spans in `cultureContextIntegration` (#20352) This PR adds span processing support for the `cultureContextIntegration` plus a few integration API additions to make span processing on an integration level easier: - `Integration::processSegmentSpan` - `Integration::processSpan` - register a `processSegmentSpan` callback on `cultureContextIntegration` that adds the attributes. All attributes are already registered in sentry conventions: https://getsentry.github.io/sentry-conventions/attributes/culture/ - adds a span streaming-specific integration test in addition to the already existing error/event-based integration test closes https://github.com/getsentry/sentry-javascript/issues/20353 closes https://github.com/getsentry/sentry-javascript/issues/20354 --- .size-limit.js | 12 +++++------ .../cultureContext-streamed/init.js | 9 ++++++++ .../cultureContext-streamed/test.ts | 21 +++++++++++++++++++ .../public-api/startSpan/streamed/test.ts | 12 +++++++++++ .../interactions-streamed/test.ts | 12 +++++++++++ .../navigation-streamed/test.ts | 12 +++++++++++ .../pageload-streamed/test.ts | 12 +++++++++++ .../src/integrations/culturecontext.ts | 12 +++++++++++ packages/core/src/integration.ts | 10 +++++++++ packages/core/src/integrations/requestdata.ts | 1 - .../core/src/tracing/spans/captureSpan.ts | 4 +++- packages/core/src/types-hoist/integration.ts | 15 +++++++++++++ 12 files changed, 124 insertions(+), 8 deletions(-) create mode 100644 dev-packages/browser-integration-tests/suites/integrations/cultureContext-streamed/init.js create mode 100644 dev-packages/browser-integration-tests/suites/integrations/cultureContext-streamed/test.ts diff --git a/.size-limit.js b/.size-limit.js index 86f3ef5ed87d..718781bbd318 100644 --- a/.size-limit.js +++ b/.size-limit.js @@ -155,7 +155,7 @@ module.exports = [ import: createImport('init', 'ErrorBoundary', 'reactRouterV6BrowserTracingIntegration'), ignore: ['react/jsx-runtime'], gzip: true, - limit: '46 KB', + limit: '47 KB', }, // Vue SDK (ESM) { @@ -241,14 +241,14 @@ module.exports = [ path: createCDNPath('bundle.min.js'), gzip: false, brotli: false, - limit: '83.5 KB', + limit: '84 KB', }, { name: 'CDN Bundle (incl. Tracing) - uncompressed', path: createCDNPath('bundle.tracing.min.js'), gzip: false, brotli: false, - limit: '134 KB', + limit: '135 KB', }, { name: 'CDN Bundle (incl. Logs, Metrics) - uncompressed', @@ -269,14 +269,14 @@ module.exports = [ path: createCDNPath('bundle.replay.logs.metrics.min.js'), gzip: false, brotli: false, - limit: '211 KB', + limit: '212 KB', }, { name: 'CDN Bundle (incl. Tracing, Replay) - uncompressed', path: createCDNPath('bundle.tracing.replay.min.js'), gzip: false, brotli: false, - limit: '251 KB', + limit: '252 KB', }, { name: 'CDN Bundle (incl. Tracing, Replay, Logs, Metrics) - uncompressed', @@ -290,7 +290,7 @@ module.exports = [ path: createCDNPath('bundle.tracing.replay.feedback.min.js'), gzip: false, brotli: false, - limit: '264 KB', + limit: '265 KB', }, { name: 'CDN Bundle (incl. Tracing, Replay, Feedback, Logs, Metrics) - uncompressed', diff --git a/dev-packages/browser-integration-tests/suites/integrations/cultureContext-streamed/init.js b/dev-packages/browser-integration-tests/suites/integrations/cultureContext-streamed/init.js new file mode 100644 index 000000000000..c69a872adc77 --- /dev/null +++ b/dev-packages/browser-integration-tests/suites/integrations/cultureContext-streamed/init.js @@ -0,0 +1,9 @@ +import * as Sentry from '@sentry/browser'; + +window.Sentry = Sentry; + +Sentry.init({ + dsn: 'https://public@dsn.ingest.sentry.io/1337', + integrations: [Sentry.spanStreamingIntegration(), Sentry.browserTracingIntegration()], + tracesSampleRate: 1.0, +}); diff --git a/dev-packages/browser-integration-tests/suites/integrations/cultureContext-streamed/test.ts b/dev-packages/browser-integration-tests/suites/integrations/cultureContext-streamed/test.ts new file mode 100644 index 000000000000..43dee40f093b --- /dev/null +++ b/dev-packages/browser-integration-tests/suites/integrations/cultureContext-streamed/test.ts @@ -0,0 +1,21 @@ +import { expect } from '@playwright/test'; +import { sentryTest } from '../../../utils/fixtures'; +import { getSpanOp, waitForStreamedSpans } from '../../../utils/spanUtils'; +import { shouldSkipTracingTest, testingCdnBundle } from '../../../utils/helpers'; + +sentryTest('cultureContextIntegration captures locale, timezone, and calendar', async ({ getLocalTestUrl, page }) => { + sentryTest.skip(shouldSkipTracingTest() || testingCdnBundle()); + const url = await getLocalTestUrl({ testDir: __dirname }); + + const spansPromise = waitForStreamedSpans(page, spans => spans.some(s => getSpanOp(s) === 'pageload')); + + await page.goto(url); + + const spans = await spansPromise; + + const pageloadSpan = spans.find(s => getSpanOp(s) === 'pageload'); + + expect(pageloadSpan!.attributes?.['culture.locale']).toEqual({ type: 'string', value: expect.any(String) }); + expect(pageloadSpan!.attributes?.['culture.timezone']).toEqual({ type: 'string', value: expect.any(String) }); + expect(pageloadSpan!.attributes?.['culture.calendar']).toEqual({ type: 'string', value: expect.any(String) }); +}); diff --git a/dev-packages/browser-integration-tests/suites/public-api/startSpan/streamed/test.ts b/dev-packages/browser-integration-tests/suites/public-api/startSpan/streamed/test.ts index b5f8f41ab4b4..9ea2197fff85 100644 --- a/dev-packages/browser-integration-tests/suites/public-api/startSpan/streamed/test.ts +++ b/dev-packages/browser-integration-tests/suites/public-api/startSpan/streamed/test.ts @@ -167,6 +167,18 @@ sentryTest( }, { attributes: { + 'culture.calendar': { + type: 'string', + value: expect.any(String), + }, + 'culture.locale': { + type: 'string', + value: expect.any(String), + }, + 'culture.timezone': { + type: 'string', + value: expect.any(String), + }, [SEMANTIC_ATTRIBUTE_SENTRY_OP]: { type: 'string', value: 'test', diff --git a/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/interactions-streamed/test.ts b/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/interactions-streamed/test.ts index fd384d0d3ff9..546d80c133ac 100644 --- a/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/interactions-streamed/test.ts +++ b/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/interactions-streamed/test.ts @@ -40,6 +40,18 @@ sentryTest('captures streamed interaction span tree. @firefox', async ({ browser expect(interactionSegmentSpan).toEqual({ attributes: { + 'culture.calendar': { + type: 'string', + value: expect.any(String), + }, + 'culture.locale': { + type: 'string', + value: expect.any(String), + }, + 'culture.timezone': { + type: 'string', + value: expect.any(String), + }, [SEMANTIC_ATTRIBUTE_SENTRY_IDLE_SPAN_FINISH_REASON]: { type: 'string', value: 'idleTimeout', diff --git a/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/navigation-streamed/test.ts b/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/navigation-streamed/test.ts index 403fdd4fdc0a..b9922c178fd9 100644 --- a/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/navigation-streamed/test.ts +++ b/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/navigation-streamed/test.ts @@ -69,6 +69,18 @@ sentryTest('starts a streamed navigation span on page navigation', async ({ getL expect(navigationSpan).toEqual({ attributes: { + 'culture.calendar': { + type: 'string', + value: expect.any(String), + }, + 'culture.locale': { + type: 'string', + value: expect.any(String), + }, + 'culture.timezone': { + type: 'string', + value: expect.any(String), + }, 'network.connection.effective_type': { type: 'string', value: expect.any(String), diff --git a/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/pageload-streamed/test.ts b/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/pageload-streamed/test.ts index 86882134cab4..e89e2011b100 100644 --- a/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/pageload-streamed/test.ts +++ b/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/pageload-streamed/test.ts @@ -62,6 +62,18 @@ sentryTest( expect(pageloadSpan).toEqual({ attributes: { + 'culture.calendar': { + type: 'string', + value: expect.any(String), + }, + 'culture.locale': { + type: 'string', + value: expect.any(String), + }, + 'culture.timezone': { + type: 'string', + value: expect.any(String), + }, // formerly known as 'effectiveConnectionType' 'network.connection.effective_type': { type: 'string', diff --git a/packages/browser/src/integrations/culturecontext.ts b/packages/browser/src/integrations/culturecontext.ts index 486f5dcd012b..f2b705e3e9a9 100644 --- a/packages/browser/src/integrations/culturecontext.ts +++ b/packages/browser/src/integrations/culturecontext.ts @@ -17,6 +17,18 @@ const _cultureContextIntegration = (() => { }; } }, + processSegmentSpan(span) { + const culture = getCultureContext(); + + if (culture) { + span.attributes = { + 'culture.locale': culture.locale, + 'culture.timezone': culture.timezone, + 'culture.calendar': culture.calendar, + ...span.attributes, + }; + } + }, }; }) satisfies IntegrationFn; diff --git a/packages/core/src/integration.ts b/packages/core/src/integration.ts index b8e7240cf748..be8e2179bdf0 100644 --- a/packages/core/src/integration.ts +++ b/packages/core/src/integration.ts @@ -4,6 +4,7 @@ import { DEBUG_BUILD } from './debug-build'; import type { Event, EventHint } from './types-hoist/event'; import type { Integration, IntegrationFn } from './types-hoist/integration'; import type { CoreOptions } from './types-hoist/options'; +import type { StreamedSpanJSON } from './types-hoist/span'; import { debug } from './utils/debug-logger'; export const installedIntegrations: string[] = []; @@ -138,6 +139,15 @@ export function setupIntegration(client: Client, integration: Integration, integ client.addEventProcessor(processor); } + (['processSpan', 'processSegmentSpan'] as const).forEach(hook => { + const callback = integration[hook]; + if (typeof callback === 'function') { + // The cast is needed because TS can't resolve overloads when the discriminant is a union type. + // Both overloads have the same callback signature so this is safe. + client.on(hook as 'processSpan', (span: StreamedSpanJSON) => callback.call(integration, span, client)); + } + }); + DEBUG_BUILD && debug.log(`Integration installed: ${integration.name}`); } diff --git a/packages/core/src/integrations/requestdata.ts b/packages/core/src/integrations/requestdata.ts index 7fdd8cee1683..a72fbed70d7e 100644 --- a/packages/core/src/integrations/requestdata.ts +++ b/packages/core/src/integrations/requestdata.ts @@ -1,5 +1,4 @@ import { defineIntegration } from '../integration'; -import { hasSpanStreamingEnabled } from '../tracing/spans/hasSpanStreamingEnabled'; import type { Event } from '../types-hoist/event'; import type { IntegrationFn } from '../types-hoist/integration'; import type { RequestEventData } from '../types-hoist/request'; diff --git a/packages/core/src/tracing/spans/captureSpan.ts b/packages/core/src/tracing/spans/captureSpan.ts index 979c7b460af1..fe8bc31fcae7 100644 --- a/packages/core/src/tracing/spans/captureSpan.ts +++ b/packages/core/src/tracing/spans/captureSpan.ts @@ -54,10 +54,12 @@ export function captureSpan(span: Span, client: Client): SerializedStreamedSpanW if (spanJSON.is_segment) { applyScopeToSegmentSpan(spanJSON, finalScopeData); // Allow hook subscribers to mutate the segment span JSON + // This also invokes the `processSegmentSpan` hook of all integrations client.emit('processSegmentSpan', spanJSON); } - // Allow hook subscribers to mutate the span JSON + // This allows hook subscribers to mutate the span JSON + // This also invokes the `processSpan` hook of all integrations client.emit('processSpan', spanJSON); const { beforeSendSpan } = client.getOptions(); diff --git a/packages/core/src/types-hoist/integration.ts b/packages/core/src/types-hoist/integration.ts index fc80cf3f524a..2e3cb5d45723 100644 --- a/packages/core/src/types-hoist/integration.ts +++ b/packages/core/src/types-hoist/integration.ts @@ -1,5 +1,6 @@ import type { Client } from '../client'; import type { Event, EventHint } from './event'; +import type { StreamedSpanJSON } from './span'; /** Integration interface */ export interface Integration { @@ -50,6 +51,20 @@ export interface Integration { * This receives the client that the integration was installed for as third argument. */ processEvent?(event: Event, hint: EventHint, client: Client): Event | null | PromiseLike; + + /** + * An optional hook that allows modifications to a span. This hook runs after the span is ended, + * during `captureSpan` and before the span is passed to users' `beforeSendSpan` callback. + * Use this hook to modify a span in-place. + */ + processSpan?(span: StreamedSpanJSON, client: Client): void; + + /** + * An optional hook that allows modifications to a segment span. This hook runs after the segment span is ended, + * during `captureSpan` and before the segment span is passed to users' `beforeSendSpan` callback. + * Use this hook to modify a segment span in-place. + */ + processSegmentSpan?(span: StreamedSpanJSON, client: Client): void; } /** From 9ee1f7710e645c7ecd033fe718493bc6ba33731c Mon Sep 17 00:00:00 2001 From: Abdelrahman Awad Date: Fri, 17 Apr 2026 09:28:23 -0400 Subject: [PATCH 07/41] fix(browser): Enrich graphqlClient spans for relative URLs (#20370) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The `graphqlClientIntegration` didn’t enrich spans for GraphQL requests to relative URLs because the handler used `url.full` or `http.url` to identify the request URL. However, in `packages/core/src/fetch.ts:382-386`, the fetch instrumentation only sets `http.url` for non-relative URLs and never sets `url.full`. Therefore, for relative endpoints, `httpUrl` was `undefined`, failed the `isString` guard, and the enrichment bailed out silently. By adding`spanAttributes['url']` as a third option. Scoped to the graphqlClient integration, this ensures enrichment happens and it's only scoped to the gql integration. The alternative was to populate `http.url` (or `url.full`) for relative URLs in `getFetchSpanAttributes` which is dangerous because `http.url` is a span attribute many users filter, group, alert, and build dashboards on. With this change, the `endpoints` matcher now sees the relative path (e.g. `/graphql`) instead of an absolute URL. If a user configured `endpoints` with an absolute-URL regex and uses relative fetches, that pattern won't match the relative form. However this changes nothing today because it never worked before (was `undefined`). closes #20292 Co-authored-by: Claude Opus 4.7 (1M context) --- .../browser/src/integrations/graphqlClient.ts | 4 +- .../test/integrations/graphqlClient.test.ts | 113 ++++++++++++++++++ 2 files changed, 116 insertions(+), 1 deletion(-) diff --git a/packages/browser/src/integrations/graphqlClient.ts b/packages/browser/src/integrations/graphqlClient.ts index d256fa6b72e1..51a3fe939f23 100644 --- a/packages/browser/src/integrations/graphqlClient.ts +++ b/packages/browser/src/integrations/graphqlClient.ts @@ -67,7 +67,9 @@ function _updateSpanWithGraphQLData(client: Client, options: GraphQLClientOption return; } - const httpUrl = spanAttributes[SEMANTIC_ATTRIBUTE_URL_FULL] || spanAttributes['http.url']; + // Fall back to `url` because fetch instrumentation only sets `http.url` for absolute URLs; + // relative URLs end up only in `url` (see `getFetchSpanAttributes` in packages/core/src/fetch.ts). + const httpUrl = spanAttributes[SEMANTIC_ATTRIBUTE_URL_FULL] || spanAttributes['http.url'] || spanAttributes['url']; const httpMethod = spanAttributes[SEMANTIC_ATTRIBUTE_HTTP_REQUEST_METHOD] || spanAttributes['http.method']; if (!isString(httpUrl) || !isString(httpMethod)) { diff --git a/packages/browser/test/integrations/graphqlClient.test.ts b/packages/browser/test/integrations/graphqlClient.test.ts index a90b62ee13d5..1c4ab60d30f2 100644 --- a/packages/browser/test/integrations/graphqlClient.test.ts +++ b/packages/browser/test/integrations/graphqlClient.test.ts @@ -2,6 +2,8 @@ * @vitest-environment jsdom */ +import type { Client } from '@sentry/core'; +import { SentrySpan, spanToJSON } from '@sentry/core'; import type { FetchHint, XhrHint } from '@sentry-internal/browser-utils'; import { SENTRY_XHR_DATA_KEY } from '@sentry-internal/browser-utils'; import { describe, expect, test } from 'vitest'; @@ -9,6 +11,7 @@ import { _getGraphQLOperation, getGraphQLRequestPayload, getRequestPayloadXhrOrFetch, + graphqlClientIntegration, parseGraphQLQuery, } from '../../src/integrations/graphqlClient'; @@ -308,4 +311,114 @@ describe('GraphqlClient', () => { expect(_getGraphQLOperation(requestBody as any)).toBe('unknown'); }); }); + + describe('beforeOutgoingRequestSpan handler', () => { + function setupHandler(endpoints: Array): (span: SentrySpan, hint: FetchHint | XhrHint) => void { + let capturedListener: ((span: SentrySpan, hint: FetchHint | XhrHint) => void) | undefined; + const mockClient = { + on: (eventName: string, cb: (span: SentrySpan, hint: FetchHint | XhrHint) => void) => { + if (eventName === 'beforeOutgoingRequestSpan') { + capturedListener = cb; + } + }, + } as unknown as Client; + + const integration = graphqlClientIntegration({ endpoints }); + integration.setup?.(mockClient); + + if (!capturedListener) { + throw new Error('beforeOutgoingRequestSpan listener was not registered'); + } + return capturedListener; + } + + function makeFetchHint(url: string, body: unknown): FetchHint { + return { + input: [url, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(body) }], + response: new Response(null, { status: 200 }), + startTimestamp: Date.now(), + endTimestamp: Date.now() + 1, + }; + } + + const requestBody = { + query: 'query GetHello { hello }', + operationName: 'GetHello', + variables: {}, + extensions: {}, + }; + + test('enriches http.client span for absolute URLs (http.url attribute)', () => { + const handler = setupHandler([/\/graphql$/]); + const span = new SentrySpan({ + name: 'POST http://localhost:4000/graphql', + op: 'http.client', + attributes: { + 'http.method': 'POST', + 'http.url': 'http://localhost:4000/graphql', + url: 'http://localhost:4000/graphql', + }, + }); + + handler(span, makeFetchHint('http://localhost:4000/graphql', requestBody)); + + const json = spanToJSON(span); + expect(json.description).toBe('POST http://localhost:4000/graphql (query GetHello)'); + expect(json.data['graphql.document']).toBe(requestBody.query); + }); + + test('enriches http.client span for relative URLs (only url attribute)', () => { + const handler = setupHandler([/\/graphql$/]); + // Fetch instrumentation does NOT set http.url for relative URLs — only `url`. + const span = new SentrySpan({ + name: 'POST /graphql', + op: 'http.client', + attributes: { + 'http.method': 'POST', + url: '/graphql', + }, + }); + + handler(span, makeFetchHint('/graphql', requestBody)); + + const json = spanToJSON(span); + expect(json.description).toBe('POST /graphql (query GetHello)'); + expect(json.data['graphql.document']).toBe(requestBody.query); + }); + + test('does nothing when no URL attribute is present', () => { + const handler = setupHandler([/\/graphql$/]); + const span = new SentrySpan({ + name: 'POST', + op: 'http.client', + attributes: { + 'http.method': 'POST', + }, + }); + + handler(span, makeFetchHint('/graphql', requestBody)); + + const json = spanToJSON(span); + expect(json.description).toBe('POST'); + expect(json.data['graphql.document']).toBeUndefined(); + }); + + test('does nothing when span op is not http.client', () => { + const handler = setupHandler([/\/graphql$/]); + const span = new SentrySpan({ + name: 'custom span', + op: 'custom', + attributes: { + 'http.method': 'POST', + url: '/graphql', + }, + }); + + handler(span, makeFetchHint('/graphql', requestBody)); + + const json = spanToJSON(span); + expect(json.description).toBe('custom span'); + expect(json.data['graphql.document']).toBeUndefined(); + }); + }); }); From fa9fea2cf7babee0afad8ada19da80426616c662 Mon Sep 17 00:00:00 2001 From: Abdelrahman Awad Date: Fri, 17 Apr 2026 11:50:34 -0400 Subject: [PATCH 08/41] feat(opentelemetry): Add tracingChannel utility for context propagation (#20358) - Vendors and adapts [`otel-tracing-channel`](https://github.com/logaretm/otel-tracing-channel) into `@sentry/opentelemetry` - Provides a drop-in `tracingChannel()` wrapper around Node.js `TracingChannel` that automatically binds OTel context propagation via `bindStore` - Uses our public `getAsyncLocalStorageLookup()` API to access the ALS instead of relying on OTel private `_asyncLocalStorage` internals - `subscribe`/`unsubscribe` accept partial subscriber objects so callers only provide handlers they need The util is exposed in a subpath export because nextjs does not externalize `node:diagnostic_channels` and will crash. --------- Co-authored-by: Claude Opus 4.6 (1M context) --- packages/opentelemetry/package.json | 10 + packages/opentelemetry/rollup.npm.config.mjs | 3 + packages/opentelemetry/src/tracingChannel.ts | 92 +++++++ .../opentelemetry/test/tracingChannel.test.ts | 251 ++++++++++++++++++ 4 files changed, 356 insertions(+) create mode 100644 packages/opentelemetry/src/tracingChannel.ts create mode 100644 packages/opentelemetry/test/tracingChannel.test.ts diff --git a/packages/opentelemetry/package.json b/packages/opentelemetry/package.json index 122dc7b92f83..fa0484e51c6d 100644 --- a/packages/opentelemetry/package.json +++ b/packages/opentelemetry/package.json @@ -26,6 +26,16 @@ "types": "./build/types/index.d.ts", "default": "./build/cjs/index.js" } + }, + "./tracing-channel": { + "import": { + "types": "./build/types/tracingChannel.d.ts", + "default": "./build/esm/tracingChannel.js" + }, + "require": { + "types": "./build/types/tracingChannel.d.ts", + "default": "./build/cjs/tracingChannel.js" + } } }, "typesVersions": { diff --git a/packages/opentelemetry/rollup.npm.config.mjs b/packages/opentelemetry/rollup.npm.config.mjs index e015fea4935e..e6f5ecdd4871 100644 --- a/packages/opentelemetry/rollup.npm.config.mjs +++ b/packages/opentelemetry/rollup.npm.config.mjs @@ -2,6 +2,9 @@ import { makeBaseNPMConfig, makeNPMConfigVariants } from '@sentry-internal/rollu export default makeNPMConfigVariants( makeBaseNPMConfig({ + // `tracingChannel` is a Node.js-only subpath so `node:diagnostics_channel` + // isn't pulled into the main bundle (breaks edge/browser builds). + entrypoints: ['src/index.ts', 'src/tracingChannel.ts'], packageSpecificConfig: { output: { // set exports to 'named' or 'auto' so that rollup doesn't warn diff --git a/packages/opentelemetry/src/tracingChannel.ts b/packages/opentelemetry/src/tracingChannel.ts new file mode 100644 index 000000000000..984986b7cdcb --- /dev/null +++ b/packages/opentelemetry/src/tracingChannel.ts @@ -0,0 +1,92 @@ +/** + * Vendored and adapted from https://github.com/logaretm/otel-tracing-channel + * + * Creates a TracingChannel with proper OpenTelemetry context propagation + * using Node.js diagnostic_channel's `bindStore` mechanism. + */ +import type { TracingChannel, TracingChannelSubscribers } from 'node:diagnostics_channel'; +import { tracingChannel as nativeTracingChannel } from 'node:diagnostics_channel'; +import type { Span } from '@opentelemetry/api'; +import { context, trace } from '@opentelemetry/api'; +import { logger } from '@sentry/core'; +import type { SentryAsyncLocalStorageContextManager } from './asyncLocalStorageContextManager'; +import type { AsyncLocalStorageLookup } from './contextManager'; +import { DEBUG_BUILD } from './debug-build'; + +/** + * Transform function that creates a span from the channel data. + */ +export type OtelTracingChannelTransform = (data: TData) => Span; + +type WithSpan = TData & { _sentrySpan?: Span }; + +/** + * A TracingChannel whose `subscribe` / `unsubscribe` accept partial subscriber + * objects — you only need to provide handlers for the events you care about. + */ +export interface OtelTracingChannel< + TData extends object = object, + TDataWithSpan extends object = WithSpan, +> extends Omit, 'subscribe' | 'unsubscribe'> { + subscribe(subscribers: Partial>): void; + unsubscribe(subscribers: Partial>): void; +} + +interface ContextApi { + _getContextManager(): SentryAsyncLocalStorageContextManager; +} + +/** + * Creates a new tracing channel with proper OTel context propagation. + * + * When the channel's `tracePromise` / `traceSync` / `traceCallback` is called, + * the `transformStart` function runs inside `bindStore` so that: + * 1. A new span is created from the channel data. + * 2. The span is set on the OTel context stored in AsyncLocalStorage. + * 3. Downstream code (including Sentry's span processor) sees the correct parent. + * + * @param channelNameOrInstance - Either a channel name string or an existing TracingChannel instance. + * @param transformStart - Function that creates an OpenTelemetry span from the channel data. + * @returns The tracing channel with OTel context bound. + */ +export function tracingChannel( + channelNameOrInstance: string, + transformStart: OtelTracingChannelTransform, +): OtelTracingChannel> { + const channel = nativeTracingChannel, WithSpan>( + channelNameOrInstance, + ) as unknown as OtelTracingChannel>; + + let lookup: AsyncLocalStorageLookup | undefined; + try { + const contextManager = (context as unknown as ContextApi)._getContextManager(); + lookup = contextManager.getAsyncLocalStorageLookup(); + } catch { + // getAsyncLocalStorageLookup may not exist if using a non-Sentry context manager + } + + if (!lookup) { + DEBUG_BUILD && + logger.warn( + '[TracingChannel] Could not access OpenTelemetry AsyncLocalStorage, context propagation will not work.', + ); + return channel; + } + + const otelStorage = lookup.asyncLocalStorage; + + // Bind the start channel so that each trace invocation runs the transform + // and stores the resulting context (with span) in AsyncLocalStorage. + // @ts-expect-error bindStore types don't account for AsyncLocalStorage of a different generic type + channel.start.bindStore(otelStorage, (data: WithSpan) => { + const span = transformStart(data); + + // Store the span on data so downstream event handlers (asyncEnd, error, etc.) can access it. + data._sentrySpan = span; + + // Return the context with the span set — this is what gets stored in AsyncLocalStorage. + return trace.setSpan(context.active(), span); + }); + + return channel; +} diff --git a/packages/opentelemetry/test/tracingChannel.test.ts b/packages/opentelemetry/test/tracingChannel.test.ts new file mode 100644 index 000000000000..2b5f72327352 --- /dev/null +++ b/packages/opentelemetry/test/tracingChannel.test.ts @@ -0,0 +1,251 @@ +import { context, trace } from '@opentelemetry/api'; +import type { ReadableSpan } from '@opentelemetry/sdk-trace-base'; +import { type Span, spanToJSON } from '@sentry/core'; +import { afterEach, beforeEach, describe, expect, it } from 'vitest'; +import { startSpanManual } from '../src/trace'; +import { tracingChannel } from '../src/tracingChannel'; +import { getActiveSpan } from '../src/utils/getActiveSpan'; +import { getParentSpanId } from '../src/utils/getParentSpanId'; +import { cleanupOtel, mockSdkInit } from './helpers/mockSdkInit'; + +describe('tracingChannel', () => { + beforeEach(() => { + mockSdkInit({ tracesSampleRate: 1 }); + }); + + afterEach(async () => { + await cleanupOtel(); + }); + + it('sets the created span as the active span inside traceSync', () => { + const channel = tracingChannel<{ op: string }>('test:sync:active', data => { + return startSpanManual({ name: 'channel-span', op: data.op }, span => span); + }); + + channel.subscribe({ + end: data => { + data._sentrySpan?.end(); + }, + }); + + channel.traceSync( + () => { + const active = getActiveSpan(); + expect(active).toBeDefined(); + expect(spanToJSON(active!).op).toBe('test.op'); + }, + { op: 'test.op' }, + ); + }); + + it('sets the created span as the active span inside tracePromise', async () => { + const channel = tracingChannel<{ op: string }>('test:promise:active', data => { + return startSpanManual({ name: 'channel-span', op: data.op }, span => span); + }); + + channel.subscribe({ + asyncEnd: data => { + data._sentrySpan?.end(); + }, + }); + + await channel.tracePromise( + async () => { + const active = getActiveSpan(); + expect(active).toBeDefined(); + expect(spanToJSON(active!).op).toBe('test.op'); + }, + { op: 'test.op' }, + ); + }); + + it('creates correct parent-child relationship with nested tracing channels', () => { + const outerChannel = tracingChannel<{ name: string }>('test:nested:outer', data => { + return startSpanManual({ name: data.name, op: 'outer' }, span => span); + }); + + const innerChannel = tracingChannel<{ name: string }>('test:nested:inner', data => { + return startSpanManual({ name: data.name, op: 'inner' }, span => span); + }); + + outerChannel.subscribe({ + end: data => { + data._sentrySpan?.end(); + }, + }); + + innerChannel.subscribe({ + end: data => { + data._sentrySpan?.end(); + }, + }); + + let outerSpanId: string | undefined; + let innerParentSpanId: string | undefined; + + outerChannel.traceSync( + () => { + const outerSpan = getActiveSpan(); + outerSpanId = outerSpan?.spanContext().spanId; + + innerChannel.traceSync( + () => { + const innerSpan = getActiveSpan(); + innerParentSpanId = getParentSpanId(innerSpan as unknown as ReadableSpan); + }, + { name: 'inner-span' }, + ); + }, + { name: 'outer-span' }, + ); + + expect(outerSpanId).toBeDefined(); + expect(innerParentSpanId).toBe(outerSpanId); + }); + + it('creates correct parent-child relationship with nested async tracing channels', async () => { + const outerChannel = tracingChannel<{ name: string }>('test:nested-async:outer', data => { + return startSpanManual({ name: data.name, op: 'outer' }, span => span); + }); + + const innerChannel = tracingChannel<{ name: string }>('test:nested-async:inner', data => { + return startSpanManual({ name: data.name, op: 'inner' }, span => span); + }); + + outerChannel.subscribe({ + asyncEnd: data => { + data._sentrySpan?.end(); + }, + }); + + innerChannel.subscribe({ + asyncEnd: data => { + data._sentrySpan?.end(); + }, + }); + + let outerSpanId: string | undefined; + let innerParentSpanId: string | undefined; + + await outerChannel.tracePromise( + async () => { + const outerSpan = getActiveSpan(); + outerSpanId = outerSpan?.spanContext().spanId; + + await innerChannel.tracePromise( + async () => { + const innerSpan = getActiveSpan(); + innerParentSpanId = getParentSpanId(innerSpan as unknown as ReadableSpan); + }, + { name: 'inner-span' }, + ); + }, + { name: 'outer-span' }, + ); + + expect(outerSpanId).toBeDefined(); + expect(innerParentSpanId).toBe(outerSpanId); + }); + + it('creates correct parent when a tracing channel is nested inside startSpanManual', () => { + const channel = tracingChannel<{ name: string }>('test:inside-startspan', data => { + return startSpanManual({ name: data.name, op: 'channel' }, span => span); + }); + + channel.subscribe({ + end: data => { + data._sentrySpan?.end(); + }, + }); + + let manualSpanId: string | undefined; + let channelParentSpanId: string | undefined; + + startSpanManual({ name: 'manual-parent' }, parentSpan => { + manualSpanId = parentSpan.spanContext().spanId; + + channel.traceSync( + () => { + const channelSpan = getActiveSpan(); + channelParentSpanId = getParentSpanId(channelSpan as unknown as ReadableSpan); + }, + { name: 'channel-child' }, + ); + + parentSpan.end(); + }); + + expect(manualSpanId).toBeDefined(); + expect(channelParentSpanId).toBe(manualSpanId); + }); + + it('makes the channel span available on data.span', () => { + let spanFromData: unknown; + + const channel = tracingChannel<{ name: string }>('test:data-span', data => { + return startSpanManual({ name: data.name }, span => span); + }); + + channel.subscribe({ + end: data => { + spanFromData = data._sentrySpan; + data._sentrySpan?.end(); + }, + }); + + channel.traceSync(() => {}, { name: 'test-span' }); + + expect(spanFromData).toBeDefined(); + expect(spanToJSON(spanFromData as unknown as Span).description).toBe('test-span'); + }); + + it('shares the same trace ID across nested channels', () => { + const outerChannel = tracingChannel<{ name: string }>('test:trace-id:outer', data => { + return startSpanManual({ name: data.name }, span => span); + }); + + const innerChannel = tracingChannel<{ name: string }>('test:trace-id:inner', data => { + return startSpanManual({ name: data.name }, span => span); + }); + + outerChannel.subscribe({ end: data => data._sentrySpan?.end() }); + innerChannel.subscribe({ end: data => data._sentrySpan?.end() }); + + let outerTraceId: string | undefined; + let innerTraceId: string | undefined; + + outerChannel.traceSync( + () => { + outerTraceId = getActiveSpan()?.spanContext().traceId; + + innerChannel.traceSync( + () => { + innerTraceId = getActiveSpan()?.spanContext().traceId; + }, + { name: 'inner' }, + ); + }, + { name: 'outer' }, + ); + + expect(outerTraceId).toBeDefined(); + expect(innerTraceId).toBe(outerTraceId); + }); + + it('does not leak context outside of traceSync', () => { + const channel = tracingChannel<{ name: string }>('test:no-leak', data => { + return startSpanManual({ name: data.name }, span => span); + }); + + channel.subscribe({ end: data => data._sentrySpan?.end() }); + + const activeBefore = trace.getSpan(context.active()); + + channel.traceSync(() => {}, { name: 'scoped-span' }); + + const activeAfter = trace.getSpan(context.active()); + + expect(activeBefore).toBeUndefined(); + expect(activeAfter).toBeUndefined(); + }); +}); From d37d6e49062535ef81452cd337ec4340c34f4a6b Mon Sep 17 00:00:00 2001 From: Sigrid <32902192+s1gr1d@users.noreply.github.com> Date: Mon, 20 Apr 2026 11:22:55 +0200 Subject: [PATCH 09/41] feat(hono): Add `@sentry/hono/bun` for Bun runtime (#20355) Adds a Hono subexport for the Bun runtime Internal Linear issue: https://linear.app/getsentry/issue/JS-2038/hono-add-bun-support-3 --- dev-packages/bun-integration-tests/expect.ts | 8 +- .../suites/basic/test.ts | 2 +- .../suites/hono-sdk/index.ts | 31 ++++ .../suites/hono-sdk/test.ts | 131 +++++++++++++++ packages/hono/package.json | 14 ++ packages/hono/rollup.npm.config.mjs | 2 +- packages/hono/src/bun/middleware.ts | 28 ++++ packages/hono/src/bun/sdk.ts | 24 +++ packages/hono/src/cloudflare/middleware.ts | 15 +- packages/hono/src/index.bun.ts | 5 + packages/hono/src/node/sdk.ts | 18 +-- .../src/shared/buildFilteredIntegrations.ts | 29 ++++ packages/hono/test/bun/middleware.test.ts | 149 ++++++++++++++++++ .../hono/test/cloudflare/middleware.test.ts | 98 ------------ packages/hono/test/node/middleware.test.ts | 110 ------------- .../shared/buildFilteredIntegrations.test.ts | 149 ++++++++++++++++++ 16 files changed, 576 insertions(+), 237 deletions(-) create mode 100644 dev-packages/bun-integration-tests/suites/hono-sdk/index.ts create mode 100644 dev-packages/bun-integration-tests/suites/hono-sdk/test.ts create mode 100644 packages/hono/src/bun/middleware.ts create mode 100644 packages/hono/src/bun/sdk.ts create mode 100644 packages/hono/src/index.bun.ts create mode 100644 packages/hono/src/shared/buildFilteredIntegrations.ts create mode 100644 packages/hono/test/bun/middleware.test.ts create mode 100644 packages/hono/test/shared/buildFilteredIntegrations.test.ts diff --git a/dev-packages/bun-integration-tests/expect.ts b/dev-packages/bun-integration-tests/expect.ts index 599caaa9e5be..6f1add5ffeae 100644 --- a/dev-packages/bun-integration-tests/expect.ts +++ b/dev-packages/bun-integration-tests/expect.ts @@ -68,7 +68,11 @@ export function expectedEvent(event: Event, { sdk }: { sdk: 'bun' | 'hono' }): E export function eventEnvelope( event: Event, - { includeSampleRand = false, sdk = 'bun' }: { includeSampleRand?: boolean; sdk?: 'bun' | 'hono' } = {}, + { + includeSampleRand = false, + includeTransaction = true, + sdk = 'bun', + }: { includeSampleRand?: boolean; includeTransaction?: boolean; sdk?: 'bun' | 'hono' } = {}, ): Envelope { return [ { @@ -79,11 +83,13 @@ export function eventEnvelope( environment: event.environment || 'production', public_key: 'public', trace_id: UUID_MATCHER, + sample_rate: expect.any(String), sampled: expect.any(String), // release is auto-detected from GitHub CI env vars, so only expect it if we know it will be there ...(process.env.GITHUB_SHA ? { release: expect.any(String) } : {}), ...(includeSampleRand && { sample_rand: expect.stringMatching(/^[01](\.\d+)?$/) }), + ...(includeTransaction && { transaction: expect.any(String) }), }, }, [[{ type: 'event' }, expectedEvent(event, { sdk })]], diff --git a/dev-packages/bun-integration-tests/suites/basic/test.ts b/dev-packages/bun-integration-tests/suites/basic/test.ts index 673464f0c81a..c03a09535702 100644 --- a/dev-packages/bun-integration-tests/suites/basic/test.ts +++ b/dev-packages/bun-integration-tests/suites/basic/test.ts @@ -25,7 +25,7 @@ it('captures an error thrown in Bun.serve fetch handler', async ({ signal }) => url: expect.stringContaining('/error'), }), }, - { includeSampleRand: true }, + { includeSampleRand: true, includeTransaction: false }, ), ) .ignore('transaction') diff --git a/dev-packages/bun-integration-tests/suites/hono-sdk/index.ts b/dev-packages/bun-integration-tests/suites/hono-sdk/index.ts new file mode 100644 index 000000000000..075fc896618b --- /dev/null +++ b/dev-packages/bun-integration-tests/suites/hono-sdk/index.ts @@ -0,0 +1,31 @@ +import { sentry } from '@sentry/hono/bun'; +import { Hono } from 'hono'; + +const app = new Hono(); + +app.use( + sentry(app, { + dsn: process.env.SENTRY_DSN, + tracesSampleRate: 1.0, + }), +); + +app.get('/', c => { + return c.text('Hello from Hono on Bun!'); +}); + +app.get('/hello/:name', c => { + const name = c.req.param('name'); + return c.text(`Hello, ${name}!`); +}); + +app.get('/error/:param', () => { + throw new Error('Test error from Hono app'); +}); + +const server = Bun.serve({ + port: 0, + fetch: app.fetch, +}); + +process.send?.(JSON.stringify({ event: 'READY', port: server.port })); diff --git a/dev-packages/bun-integration-tests/suites/hono-sdk/test.ts b/dev-packages/bun-integration-tests/suites/hono-sdk/test.ts new file mode 100644 index 000000000000..62d5021fddb9 --- /dev/null +++ b/dev-packages/bun-integration-tests/suites/hono-sdk/test.ts @@ -0,0 +1,131 @@ +import { expect, it } from 'vitest'; +import { eventEnvelope, SHORT_UUID_MATCHER, UUID_MATCHER } from '../../expect'; +import { createRunner } from '../../runner'; + +it('Hono app captures parametrized errors (Hono SDK on Bun)', async ({ signal }) => { + const runner = createRunner(__dirname) + .expect(envelope => { + const [, envelopeItems] = envelope; + const [itemHeader, itemPayload] = envelopeItems[0]; + + expect(itemHeader.type).toBe('transaction'); + + expect(itemPayload).toMatchObject({ + type: 'transaction', + platform: 'node', + transaction: 'GET /error/:param', + transaction_info: { + source: 'route', + }, + contexts: { + trace: { + span_id: expect.any(String), + trace_id: expect.any(String), + op: 'http.server', + status: 'internal_error', + origin: 'auto.http.bun.serve', + }, + response: { + status_code: 500, + }, + }, + request: expect.objectContaining({ + method: 'GET', + url: expect.stringContaining('/error/param-123'), + }), + breadcrumbs: [ + { + timestamp: expect.any(Number), + category: 'console', + level: 'error', + message: 'Error: Test error from Hono app', + data: expect.objectContaining({ + logger: 'console', + arguments: [{ message: 'Test error from Hono app', name: 'Error', stack: expect.any(String) }], + }), + }, + ], + }); + }) + + .expect( + eventEnvelope( + { + level: 'error', + transaction: 'GET /error/:param', + exception: { + values: [ + { + type: 'Error', + value: 'Test error from Hono app', + stacktrace: { + frames: expect.any(Array), + }, + mechanism: { type: 'auto.http.hono.context_error', handled: false }, + }, + ], + }, + request: { + cookies: {}, + headers: expect.any(Object), + method: 'GET', + url: expect.stringContaining('/error/param-123'), + }, + breadcrumbs: [ + { + timestamp: expect.any(Number), + category: 'console', + level: 'error', + message: 'Error: Test error from Hono app', + data: expect.objectContaining({ + logger: 'console', + arguments: [{ message: 'Test error from Hono app', name: 'Error', stack: expect.any(String) }], + }), + }, + ], + }, + { sdk: 'hono', includeSampleRand: true, includeTransaction: true }, + ), + ) + .unordered() + .start(signal); + + await runner.makeRequest('get', '/error/param-123', { expectError: true }); + await runner.completed(); +}); + +it('Hono app captures parametrized route names on Bun', async ({ signal }) => { + const runner = createRunner(__dirname) + .expect(envelope => { + const [, envelopeItems] = envelope; + const [itemHeader, itemPayload] = envelopeItems[0]; + + expect(itemHeader.type).toBe('transaction'); + + expect(itemPayload).toMatchObject({ + type: 'transaction', + platform: 'node', + transaction: 'GET /hello/:name', + transaction_info: { + source: 'route', + }, + contexts: { + trace: { + span_id: SHORT_UUID_MATCHER, + trace_id: UUID_MATCHER, + op: 'http.server', + status: 'ok', + origin: 'auto.http.bun.serve', + }, + }, + request: expect.objectContaining({ + method: 'GET', + url: expect.stringContaining('/hello/world'), + }), + }); + }) + .start(signal); + + await runner.makeRequest('get', '/hello/world'); + await runner.completed(); +}); diff --git a/packages/hono/package.json b/packages/hono/package.json index c93820ebc20c..4c0057c74e02 100644 --- a/packages/hono/package.json +++ b/packages/hono/package.json @@ -46,6 +46,16 @@ "types": "./build/types/index.node.d.ts", "default": "./build/cjs/index.node.js" } + }, + "./bun": { + "import": { + "types": "./build/types/index.bun.d.ts", + "default": "./build/esm/index.bun.js" + }, + "require": { + "types": "./build/types/index.bun.d.ts", + "default": "./build/cjs/index.bun.js" + } } }, "typesVersions": { @@ -58,6 +68,9 @@ ], "build/types/index.node.d.ts": [ "build/types-ts3.8/index.node.d.ts" + ], + "build/types/index.bun.d.ts": [ + "build/types-ts3.8/index.bun.d.ts" ] } }, @@ -66,6 +79,7 @@ }, "dependencies": { "@opentelemetry/api": "^1.9.1", + "@sentry/bun": "10.49.0", "@sentry/cloudflare": "10.49.0", "@sentry/core": "10.49.0", "@sentry/node": "10.49.0" diff --git a/packages/hono/rollup.npm.config.mjs b/packages/hono/rollup.npm.config.mjs index a60ba1312cc9..2a03d7540bdc 100644 --- a/packages/hono/rollup.npm.config.mjs +++ b/packages/hono/rollup.npm.config.mjs @@ -1,7 +1,7 @@ import { makeBaseNPMConfig, makeNPMConfigVariants } from '@sentry-internal/rollup-utils'; const baseConfig = makeBaseNPMConfig({ - entrypoints: ['src/index.ts', 'src/index.cloudflare.ts', 'src/index.node.ts'], + entrypoints: ['src/index.ts', 'src/index.cloudflare.ts', 'src/index.node.ts', 'src/index.bun.ts'], packageSpecificConfig: { output: { preserveModulesRoot: 'src', diff --git a/packages/hono/src/bun/middleware.ts b/packages/hono/src/bun/middleware.ts new file mode 100644 index 000000000000..cbca87ea6b9e --- /dev/null +++ b/packages/hono/src/bun/middleware.ts @@ -0,0 +1,28 @@ +import { type BaseTransportOptions, debug, type Options } from '@sentry/core'; +import { init } from './sdk'; +import type { Hono, MiddlewareHandler } from 'hono'; +import { patchAppUse } from '../shared/patchAppUse'; +import { requestHandler, responseHandler } from '../shared/middlewareHandlers'; + +export interface HonoBunOptions extends Options {} + +/** + * Sentry middleware for Hono running in a Bun runtime environment. + */ +export const sentry = (app: Hono, options: HonoBunOptions | undefined = {}): MiddlewareHandler => { + const isDebug = options.debug; + + isDebug && debug.log('Initialized Sentry Hono middleware (Bun)'); + + init(options); + + patchAppUse(app); + + return async (context, next) => { + requestHandler(context); + + await next(); // Handler runs in between Request above ⤴ and Response below ⤵ + + responseHandler(context); + }; +}; diff --git a/packages/hono/src/bun/sdk.ts b/packages/hono/src/bun/sdk.ts new file mode 100644 index 000000000000..d30269058f4d --- /dev/null +++ b/packages/hono/src/bun/sdk.ts @@ -0,0 +1,24 @@ +import type { Client } from '@sentry/core'; +import { applySdkMetadata } from '@sentry/core'; +import { init as initBun } from '@sentry/bun'; +import type { HonoBunOptions } from './middleware'; +import { buildFilteredIntegrations } from '../shared/buildFilteredIntegrations'; + +/** + * Initializes Sentry for Hono running in a Bun runtime environment. + * + * In general, it is recommended to initialize Sentry via the `sentry()` middleware, as it sets up everything by default and calls `init` internally. + * + * When manually calling `init`, add the `honoIntegration` to the `integrations` array to set up the Hono integration. + */ +export function init(options: HonoBunOptions): Client | undefined { + applySdkMetadata(options, 'hono', ['hono', 'bun']); + + // Remove Hono from the SDK defaults to prevent double instrumentation: @sentry/bun + const filteredOptions: HonoBunOptions = { + ...options, + integrations: buildFilteredIntegrations(options.integrations, false), + }; + + return initBun(filteredOptions); +} diff --git a/packages/hono/src/cloudflare/middleware.ts b/packages/hono/src/cloudflare/middleware.ts index 1769bbd141a6..66151af2f87f 100644 --- a/packages/hono/src/cloudflare/middleware.ts +++ b/packages/hono/src/cloudflare/middleware.ts @@ -1,9 +1,9 @@ import { withSentry } from '@sentry/cloudflare'; -import { applySdkMetadata, type BaseTransportOptions, debug, getIntegrationsToSetup, type Options } from '@sentry/core'; +import { applySdkMetadata, type BaseTransportOptions, debug, type Options } from '@sentry/core'; import type { Env, Hono, MiddlewareHandler } from 'hono'; +import { buildFilteredIntegrations } from '../shared/buildFilteredIntegrations'; import { requestHandler, responseHandler } from '../shared/middlewareHandlers'; import { patchAppUse } from '../shared/patchAppUse'; -import { filterHonoIntegration } from '../shared/filterHonoIntegration'; export interface HonoCloudflareOptions extends Options {} @@ -22,20 +22,11 @@ export function sentry( honoOptions.debug && debug.log('Initialized Sentry Hono middleware (Cloudflare)'); - const { integrations: userIntegrations } = honoOptions; return { ...honoOptions, // Always filter out the Hono integration from defaults and user integrations. // The Hono integration is already set up by withSentry, so adding it again would cause capturing too early (in Cloudflare SDK) and non-parametrized URLs. - integrations: Array.isArray(userIntegrations) - ? defaults => - getIntegrationsToSetup({ - defaultIntegrations: defaults.filter(filterHonoIntegration), - integrations: userIntegrations.filter(filterHonoIntegration), - }) - : typeof userIntegrations === 'function' - ? defaults => userIntegrations(defaults).filter(filterHonoIntegration) - : defaults => defaults.filter(filterHonoIntegration), + integrations: buildFilteredIntegrations(honoOptions.integrations, true), }; }, // Cast needed because Hono exposes a narrower fetch signature than ExportedHandler diff --git a/packages/hono/src/index.bun.ts b/packages/hono/src/index.bun.ts new file mode 100644 index 000000000000..51fbac5fe01f --- /dev/null +++ b/packages/hono/src/index.bun.ts @@ -0,0 +1,5 @@ +export { sentry } from './bun/middleware'; + +export * from '@sentry/bun'; + +export { init } from './bun/sdk'; diff --git a/packages/hono/src/node/sdk.ts b/packages/hono/src/node/sdk.ts index ff71ffe55909..936cf612bb44 100644 --- a/packages/hono/src/node/sdk.ts +++ b/packages/hono/src/node/sdk.ts @@ -1,8 +1,8 @@ -import type { Client, Integration } from '@sentry/core'; -import { applySdkMetadata, getIntegrationsToSetup } from '@sentry/core'; +import type { Client } from '@sentry/core'; +import { applySdkMetadata } from '@sentry/core'; import { init as initNode } from '@sentry/node'; import type { HonoNodeOptions } from './middleware'; -import { filterHonoIntegration } from '../shared/filterHonoIntegration'; +import { buildFilteredIntegrations } from '../shared/buildFilteredIntegrations'; /** * Initializes Sentry for Hono running in a Node runtime environment. @@ -14,20 +14,10 @@ import { filterHonoIntegration } from '../shared/filterHonoIntegration'; export function init(options: HonoNodeOptions): Client | undefined { applySdkMetadata(options, 'hono', ['hono', 'node']); - const { integrations: userIntegrations } = options; - // Remove Hono from the SDK defaults to prevent double instrumentation: @sentry/node const filteredOptions: HonoNodeOptions = { ...options, - integrations: Array.isArray(userIntegrations) - ? (defaults: Integration[]) => - getIntegrationsToSetup({ - defaultIntegrations: defaults.filter(filterHonoIntegration), - integrations: userIntegrations, // user's explicit Hono integration is preserved - }) - : typeof userIntegrations === 'function' - ? (defaults: Integration[]) => userIntegrations(defaults.filter(filterHonoIntegration)) - : (defaults: Integration[]) => defaults.filter(filterHonoIntegration), + integrations: buildFilteredIntegrations(options.integrations, false), }; return initNode(filteredOptions); diff --git a/packages/hono/src/shared/buildFilteredIntegrations.ts b/packages/hono/src/shared/buildFilteredIntegrations.ts new file mode 100644 index 000000000000..ccb0fd28029f --- /dev/null +++ b/packages/hono/src/shared/buildFilteredIntegrations.ts @@ -0,0 +1,29 @@ +import type { Integration } from '@sentry/core'; +import { getIntegrationsToSetup } from '@sentry/core'; +import { filterHonoIntegration } from './filterHonoIntegration'; + +/** + * Builds an `integrations` callback that removes the default Hono integration + * to prevent double instrumentation. + */ +export function buildFilteredIntegrations( + userIntegrations: Integration[] | ((defaults: Integration[]) => Integration[]) | undefined, + filterUserIntegrations: boolean, +): (defaults: Integration[]) => Integration[] { + if (Array.isArray(userIntegrations)) { + const integrations = filterUserIntegrations ? userIntegrations.filter(filterHonoIntegration) : userIntegrations; + return (defaults: Integration[]) => + getIntegrationsToSetup({ + defaultIntegrations: defaults.filter(filterHonoIntegration), + integrations, + }); + } + + if (typeof userIntegrations === 'function') { + return filterUserIntegrations + ? (defaults: Integration[]) => userIntegrations(defaults).filter(filterHonoIntegration) + : (defaults: Integration[]) => userIntegrations(defaults.filter(filterHonoIntegration)); + } + + return (defaults: Integration[]) => defaults.filter(filterHonoIntegration); +} diff --git a/packages/hono/test/bun/middleware.test.ts b/packages/hono/test/bun/middleware.test.ts new file mode 100644 index 000000000000..f3fc82d3696f --- /dev/null +++ b/packages/hono/test/bun/middleware.test.ts @@ -0,0 +1,149 @@ +import * as SentryCore from '@sentry/core'; +import { SDK_VERSION } from '@sentry/core'; +import { Hono } from 'hono'; +import { beforeEach, describe, expect, it, type Mock, vi } from 'vitest'; +import { sentry } from '../../src/bun/middleware'; + +vi.mock('@sentry/bun', () => ({ + init: vi.fn(), +})); + +// eslint-disable-next-line @typescript-eslint/consistent-type-imports +const { init: initBunMock } = await vi.importMock('@sentry/bun'); + +vi.mock('@sentry/core', async () => { + const actual = await vi.importActual('@sentry/core'); + return { + ...actual, + // eslint-disable-next-line @typescript-eslint/ban-ts-comment + // @ts-ignore + applySdkMetadata: vi.fn(actual.applySdkMetadata), + }; +}); + +const applySdkMetadataMock = SentryCore.applySdkMetadata as Mock; + +describe('Hono Bun Middleware', () => { + beforeEach(() => { + vi.clearAllMocks(); + }); + + describe('sentry middleware', () => { + it('calls applySdkMetadata with "hono" and "bun"', () => { + const app = new Hono(); + const options = { + dsn: 'https://public@dsn.ingest.sentry.io/1337', + }; + + sentry(app, options); + + expect(applySdkMetadataMock).toHaveBeenCalledTimes(1); + expect(applySdkMetadataMock).toHaveBeenCalledWith(options, 'hono', ['hono', 'bun']); + }); + + it('calls init from @sentry/bun', () => { + const app = new Hono(); + const options = { + dsn: 'https://public@dsn.ingest.sentry.io/1337', + }; + + sentry(app, options); + + expect(initBunMock).toHaveBeenCalledTimes(1); + expect(initBunMock).toHaveBeenCalledWith( + expect.objectContaining({ + dsn: 'https://public@dsn.ingest.sentry.io/1337', + }), + ); + }); + + it('sets SDK metadata before calling Bun init', () => { + const app = new Hono(); + const options = { + dsn: 'https://public@dsn.ingest.sentry.io/1337', + }; + + sentry(app, options); + + const applySdkMetadataCallOrder = applySdkMetadataMock.mock.invocationCallOrder[0]; + const initBunCallOrder = (initBunMock as Mock).mock.invocationCallOrder[0]; + + expect(applySdkMetadataCallOrder).toBeLessThan(initBunCallOrder as number); + }); + + it('preserves all user options', () => { + const app = new Hono(); + const options = { + dsn: 'https://public@dsn.ingest.sentry.io/1337', + environment: 'production', + sampleRate: 0.5, + tracesSampleRate: 1.0, + debug: true, + }; + + sentry(app, options); + + expect(initBunMock).toHaveBeenCalledWith( + expect.objectContaining({ + dsn: 'https://public@dsn.ingest.sentry.io/1337', + environment: 'production', + sampleRate: 0.5, + tracesSampleRate: 1.0, + debug: true, + }), + ); + }); + + it('returns a middleware handler function', () => { + const app = new Hono(); + const options = { + dsn: 'https://public@dsn.ingest.sentry.io/1337', + }; + + const middleware = sentry(app, options); + + expect(middleware).toBeDefined(); + expect(typeof middleware).toBe('function'); + expect(middleware).toHaveLength(2); // Hono middleware takes (context, next) + }); + + it('returns an async middleware handler', () => { + const app = new Hono(); + const middleware = sentry(app, {}); + + expect(middleware.constructor.name).toBe('AsyncFunction'); + }); + + it('passes an integrations function to initBun (never a raw array)', () => { + const app = new Hono(); + sentry(app, { dsn: 'https://public@dsn.ingest.sentry.io/1337' }); + + const callArgs = (initBunMock as Mock).mock.calls[0]?.[0]; + expect(typeof callArgs.integrations).toBe('function'); + }); + + it('includes hono SDK metadata', () => { + const app = new Hono(); + const options = { + dsn: 'https://public@dsn.ingest.sentry.io/1337', + }; + + sentry(app, options); + + expect(initBunMock).toHaveBeenCalledWith( + expect.objectContaining({ + _metadata: expect.objectContaining({ + sdk: expect.objectContaining({ + name: 'sentry.javascript.hono', + version: SDK_VERSION, + packages: [ + { name: 'npm:@sentry/hono', version: SDK_VERSION }, + { name: 'npm:@sentry/bun', version: SDK_VERSION }, + ], + }), + }), + }), + ); + }); + }); +}); diff --git a/packages/hono/test/cloudflare/middleware.test.ts b/packages/hono/test/cloudflare/middleware.test.ts index ac512d41afee..46e13956ec4e 100644 --- a/packages/hono/test/cloudflare/middleware.test.ts +++ b/packages/hono/test/cloudflare/middleware.test.ts @@ -164,102 +164,4 @@ describe('Hono Cloudflare Middleware', () => { }); }); }); - - describe('filters Hono integration from user-provided integrations', () => { - const honoIntegration = { name: 'Hono' } as SentryCore.Integration; - const otherIntegration = { name: 'Other' } as SentryCore.Integration; - - const getIntegrationsResult = () => { - const optionsCallback = withSentryMock.mock.calls[0]?.[0]; - return optionsCallback().integrations; - }; - - it.each([ - ['filters Hono integration out', [honoIntegration, otherIntegration], [otherIntegration]], - ['keeps non-Hono integrations', [otherIntegration], [otherIntegration]], - ['returns empty array when only Hono integration provided', [honoIntegration], []], - ])('%s (array)', (_name, input, expected) => { - const app = new Hono(); - sentry(app, { integrations: input }); - - const integrationsFn = getIntegrationsResult() as ( - defaults: SentryCore.Integration[], - ) => SentryCore.Integration[]; - expect(integrationsFn([])).toEqual(expected); - }); - - it('filters Hono from defaults when user provides an array', () => { - const app = new Hono(); - sentry(app, { integrations: [otherIntegration] }); - - const integrationsFn = getIntegrationsResult() as ( - defaults: SentryCore.Integration[], - ) => SentryCore.Integration[]; - // Defaults (from Cloudflare) include Hono; result must exclude it and deduplicate (user + defaults overlap) - const defaultsWithHono = [honoIntegration, otherIntegration]; - expect(integrationsFn(defaultsWithHono)).toEqual([otherIntegration]); - }); - - it('deduplicates when user integrations overlap with defaults (by name)', () => { - const app = new Hono(); - const duplicateIntegration = { name: 'Other' } as SentryCore.Integration; - sentry(app, { integrations: [duplicateIntegration] }); - - const integrationsFn = getIntegrationsResult() as ( - defaults: SentryCore.Integration[], - ) => SentryCore.Integration[]; - const defaultsWithOverlap = [ - honoIntegration, - otherIntegration, // same name as duplicateIntegration - ]; - const result = integrationsFn(defaultsWithOverlap); - expect(result).toHaveLength(1); - expect(result[0]?.name).toBe('Other'); - }); - - it('filters Hono integration out of a function result', () => { - const app = new Hono(); - sentry(app, { integrations: () => [honoIntegration, otherIntegration] }); - - const integrationsFn = getIntegrationsResult() as unknown as ( - defaults: SentryCore.Integration[], - ) => SentryCore.Integration[]; - expect(integrationsFn([])).toEqual([otherIntegration]); - }); - - it('passes defaults through to the user-provided integrations function', () => { - const app = new Hono(); - const userFn = vi.fn((_defaults: SentryCore.Integration[]) => [otherIntegration]); - const defaults = [{ name: 'Default' } as SentryCore.Integration]; - - sentry(app, { integrations: userFn }); - - const integrationsFn = getIntegrationsResult() as unknown as ( - defaults: SentryCore.Integration[], - ) => SentryCore.Integration[]; - integrationsFn(defaults); - - expect(userFn).toHaveBeenCalledWith(defaults); - }); - - it('filters Hono integration returned by the user-provided integrations function', () => { - const app = new Hono(); - sentry(app, { integrations: (_defaults: SentryCore.Integration[]) => [honoIntegration] }); - - const integrationsFn = getIntegrationsResult() as unknown as ( - defaults: SentryCore.Integration[], - ) => SentryCore.Integration[]; - expect(integrationsFn([])).toEqual([]); - }); - - it('filters Hono integration from defaults when integrations is undefined', () => { - const app = new Hono(); - sentry(app, {}); - - const integrationsFn = getIntegrationsResult() as unknown as ( - defaults: SentryCore.Integration[], - ) => SentryCore.Integration[]; - expect(integrationsFn([honoIntegration, otherIntegration])).toEqual([otherIntegration]); - }); - }); }); diff --git a/packages/hono/test/node/middleware.test.ts b/packages/hono/test/node/middleware.test.ts index 1473daf98acc..b6561098ed8a 100644 --- a/packages/hono/test/node/middleware.test.ts +++ b/packages/hono/test/node/middleware.test.ts @@ -3,7 +3,6 @@ import { SDK_VERSION } from '@sentry/core'; import { Hono } from 'hono'; import { beforeEach, describe, expect, it, type Mock, vi } from 'vitest'; import { sentry } from '../../src/node/middleware'; -import type { Integration } from '@sentry/core'; vi.mock('@sentry/node', () => ({ init: vi.fn(), @@ -147,113 +146,4 @@ describe('Hono Node Middleware', () => { ); }); }); - - describe('Hono integration filtering', () => { - const honoIntegration = { name: 'Hono' } as Integration; - const otherIntegration = { name: 'Other' } as Integration; - - const getIntegrationsFn = (): ((defaults: Integration[]) => Integration[]) => { - const callArgs = (initNodeMock as Mock).mock.calls[0]?.[0]; - return callArgs.integrations as (defaults: Integration[]) => Integration[]; - }; - - describe('when integrations is an array', () => { - it('keeps a user-explicitly-provided Hono integration', () => { - const app = new Hono(); - sentry(app, { integrations: [honoIntegration, otherIntegration] }); - - const integrationsFn = getIntegrationsFn(); - const result = integrationsFn([]); - expect(result.map(i => i.name)).toContain('Hono'); - expect(result.map(i => i.name)).toContain('Other'); - }); - - it('keeps non-Hono user integrations', () => { - const app = new Hono(); - sentry(app, { integrations: [otherIntegration] }); - - const integrationsFn = getIntegrationsFn(); - expect(integrationsFn([])).toEqual([otherIntegration]); - }); - - it('preserves user-provided Hono even when defaults would also provide it', () => { - const app = new Hono(); - sentry(app, { integrations: [honoIntegration] }); - - const integrationsFn = getIntegrationsFn(); - // Defaults include Hono, but it should be filtered from defaults; user's copy is kept - const result = integrationsFn([honoIntegration, otherIntegration]); - expect(result.filter(i => i.name === 'Hono')).toHaveLength(1); - }); - - it('removes Hono from defaults when user does not explicitly provide it', () => { - const app = new Hono(); - sentry(app, { integrations: [otherIntegration] }); - - const integrationsFn = getIntegrationsFn(); - const defaultsWithHono = [honoIntegration, otherIntegration]; - const result = integrationsFn(defaultsWithHono); - expect(result.map(i => i.name)).not.toContain('Hono'); - }); - - it('deduplicates non-Hono integrations when user integrations overlap with defaults', () => { - const app = new Hono(); - const duplicateIntegration = { name: 'Other' } as Integration; - sentry(app, { integrations: [duplicateIntegration] }); - - const integrationsFn = getIntegrationsFn(); - const defaultsWithOverlap = [honoIntegration, otherIntegration]; - const result = integrationsFn(defaultsWithOverlap); - expect(result).toHaveLength(1); - expect(result[0]?.name).toBe('Other'); - }); - }); - - describe('when integrations is a function', () => { - it('passes defaults without Hono to the user function', () => { - const app = new Hono(); - const userFn = vi.fn((_defaults: Integration[]) => [otherIntegration]); - const defaultIntegration = { name: 'Default' } as Integration; - - sentry(app, { integrations: userFn }); - - const integrationsFn = getIntegrationsFn(); - integrationsFn([honoIntegration, defaultIntegration]); - - const receivedDefaults = userFn.mock.calls[0]?.[0] as Integration[]; - expect(receivedDefaults.map(i => i.name)).not.toContain('Hono'); - expect(receivedDefaults.map(i => i.name)).toContain('Default'); - }); - - it('preserves a Hono integration explicitly returned by the user function', () => { - const app = new Hono(); - sentry(app, { integrations: () => [honoIntegration, otherIntegration] }); - - const integrationsFn = getIntegrationsFn(); - const result = integrationsFn([]); - expect(result.map(i => i.name)).toContain('Hono'); - expect(result.map(i => i.name)).toContain('Other'); - }); - - it('does not include Hono when user function just returns defaults', () => { - const app = new Hono(); - sentry(app, { integrations: (defaults: Integration[]) => defaults }); - - const integrationsFn = getIntegrationsFn(); - const result = integrationsFn([honoIntegration, otherIntegration]); - expect(result.map(i => i.name)).not.toContain('Hono'); - expect(result.map(i => i.name)).toContain('Other'); - }); - }); - - describe('when integrations is undefined', () => { - it('removes Hono from defaults', () => { - const app = new Hono(); - sentry(app, {}); - - const integrationsFn = getIntegrationsFn(); - expect(integrationsFn([honoIntegration, otherIntegration])).toEqual([otherIntegration]); - }); - }); - }); }); diff --git a/packages/hono/test/shared/buildFilteredIntegrations.test.ts b/packages/hono/test/shared/buildFilteredIntegrations.test.ts new file mode 100644 index 000000000000..e2aec16d1119 --- /dev/null +++ b/packages/hono/test/shared/buildFilteredIntegrations.test.ts @@ -0,0 +1,149 @@ +import type { Integration } from '@sentry/core'; +import { describe, expect, it, vi } from 'vitest'; +import { buildFilteredIntegrations } from '../../src/shared/buildFilteredIntegrations'; + +const hono = { name: 'Hono' } as Integration; +const other = { name: 'Other' } as Integration; +const dflt = { name: 'Default' } as Integration; + +function names(integrations: Integration[]): string[] { + return integrations.map(i => i.name); +} + +describe('buildFilteredIntegrations', () => { + it.each([ + { label: 'array', input: [] as Integration[], filterUser: false }, + { label: 'function', input: () => [] as Integration[], filterUser: false }, + { label: 'undefined', input: undefined, filterUser: false }, + { label: 'array', input: [] as Integration[], filterUser: true }, + { label: 'function', input: () => [] as Integration[], filterUser: true }, + { label: 'undefined', input: undefined, filterUser: true }, + ])('returns a function when userIntegrations=$label, filterUserIntegrations=$filterUser', ({ input, filterUser }) => { + expect(typeof buildFilteredIntegrations(input, filterUser)).toBe('function'); + }); + + it.each([false, true])( + 'removes Hono from defaults when userIntegrations is undefined (filterUserIntegrations=%j)', + filterUser => { + const fn = buildFilteredIntegrations(undefined, filterUser); + expect(fn([hono, other])).toEqual([other]); + }, + ); + + it.each([false, true])( + 'deduplicates when user integrations overlap with defaults (filterUserIntegrations=%j)', + filterUser => { + const duplicate = { name: 'Other' } as Integration; + const fn = buildFilteredIntegrations([duplicate], filterUser); + const result = fn([hono, other]); + expect(result).toHaveLength(1); + expect(result[0]?.name).toBe('Other'); + }, + ); + + describe('filterUserIntegrations: false (Node / Bun)', () => { + describe('when userIntegrations is an array', () => { + it.each([ + { + scenario: 'removes Hono from defaults', + user: [other], + defaults: [hono, dflt], + includes: ['Other', 'Default'], + excludes: ['Hono'], + }, + { + scenario: 'preserves user-provided Hono', + user: [hono, other], + defaults: [], + includes: ['Hono', 'Other'], + excludes: [], + }, + ])('$scenario', ({ user, defaults, includes, excludes }) => { + const fn = buildFilteredIntegrations(user, false); + const result = names(fn(defaults)); + for (const name of includes) { + expect(result).toContain(name); + } + for (const name of excludes) { + expect(result).not.toContain(name); + } + }); + + it('preserves user-provided Hono even when defaults also include it', () => { + const fn = buildFilteredIntegrations([hono], false); + const result = fn([hono, other]); + expect(result.filter(i => i.name === 'Hono')).toHaveLength(1); + }); + }); + + describe('when userIntegrations is a function', () => { + it('filters Hono from defaults before passing to the user function', () => { + const userFn = vi.fn((_defaults: Integration[]) => [other]); + const fn = buildFilteredIntegrations(userFn, false); + fn([hono, dflt]); + + expect(userFn).toHaveBeenCalledWith([dflt]); + }); + + it('preserves Hono when explicitly returned by the user function', () => { + const fn = buildFilteredIntegrations(() => [hono, other], false); + expect(names(fn([]))).toEqual(['Hono', 'Other']); + }); + + it('excludes Hono when user function passes defaults through', () => { + const fn = buildFilteredIntegrations(defaults => defaults, false); + expect(names(fn([hono, other]))).toEqual(['Other']); + }); + }); + }); + + describe('filterUserIntegrations: true (Cloudflare)', () => { + describe('when userIntegrations is an array', () => { + it.each([ + { + scenario: 'removes Hono from both user array and defaults', + user: [hono, other], + defaults: [hono, dflt], + includes: ['Other', 'Default'], + excludes: ['Hono'], + }, + { + scenario: 'returns empty when only Hono is provided', + user: [hono], + defaults: [], + includes: [], + excludes: ['Hono'], + }, + { scenario: 'keeps non-Hono integrations', user: [other], defaults: [], includes: ['Other'], excludes: [] }, + ])('$scenario', ({ user, defaults, includes, excludes }) => { + const fn = buildFilteredIntegrations(user, true); + const result = names(fn(defaults)); + for (const name of includes) { + expect(result).toContain(name); + } + for (const name of excludes) { + expect(result).not.toContain(name); + } + }); + }); + + describe('when userIntegrations is a function', () => { + it('passes defaults through to the user function unfiltered', () => { + const userFn = vi.fn((_defaults: Integration[]) => [other]); + const defaults = [dflt]; + const fn = buildFilteredIntegrations(userFn, true); + fn(defaults); + + expect(userFn).toHaveBeenCalledWith(defaults); + }); + + it.each([ + { scenario: 'filters Hono from result', userFn: () => [hono, other], expected: [other] }, + { scenario: 'returns empty when user function only returns Hono', userFn: () => [hono], expected: [] }, + ])('$scenario', ({ userFn, expected }) => { + const fn = buildFilteredIntegrations(userFn, true); + expect(fn([])).toEqual(expected); + }); + }); + }); +}); From be1353786b2c4e3936b8334cb6117b3c0e55988c Mon Sep 17 00:00:00 2001 From: Lukas Stracke Date: Mon, 20 Apr 2026 13:43:32 +0200 Subject: [PATCH 10/41] feat(core): Emit `no_parent_span` client outcomes for discarded spans requiring a parent (#20350) This PR adds reporting a client discard outcome for spans we skip because they require a parent span to be active. This happens when `onlyIfParent: true` is set when starting spans, or in custom logic for `http.client` spans. With this PR, we should now get a much clearer picture of how many spans users are missing out on. In span streaming, we'll always emit them (see #17932) spans. closes https://linear.app/getsentry/issue/SDK-1123/add-client-report-outcome-for-dropped-spans-because-of-missing-parent --------- Co-authored-by: s1gr1d <32902192+s1gr1d@users.noreply.github.com> --- .size-limit.js | 4 +- .../init.js | 13 ++ .../subject.js | 1 + .../test.ts | 45 +++++++ .../no-parent-span-client-report/init.js | 10 ++ .../no-parent-span-client-report/subject.js | 1 + .../no-parent-span-client-report/test.ts | 45 +++++++ .../instrument.mjs | 11 ++ .../scenario.mjs | 2 + .../test.ts | 29 +++++ .../instrument.mjs | 10 ++ .../no-parent-span-client-report/scenario.mjs | 2 + .../no-parent-span-client-report/test.ts | 29 +++++ packages/browser/src/tracing/request.ts | 6 +- packages/core/src/fetch.ts | 7 +- packages/core/src/tracing/trace.ts | 23 +++- packages/core/src/types-hoist/clientreport.ts | 3 +- packages/core/test/lib/tracing/trace.test.ts | 56 ++++++++- packages/opentelemetry/src/sampler.ts | 1 + packages/opentelemetry/src/trace.ts | 16 ++- packages/opentelemetry/test/sampler.test.ts | 5 +- packages/opentelemetry/test/trace.test.ts | 111 +++++++++++++++++- 22 files changed, 405 insertions(+), 25 deletions(-) create mode 100644 dev-packages/browser-integration-tests/suites/tracing/no-parent-span-client-report-streamed/init.js create mode 100644 dev-packages/browser-integration-tests/suites/tracing/no-parent-span-client-report-streamed/subject.js create mode 100644 dev-packages/browser-integration-tests/suites/tracing/no-parent-span-client-report-streamed/test.ts create mode 100644 dev-packages/browser-integration-tests/suites/tracing/no-parent-span-client-report/init.js create mode 100644 dev-packages/browser-integration-tests/suites/tracing/no-parent-span-client-report/subject.js create mode 100644 dev-packages/browser-integration-tests/suites/tracing/no-parent-span-client-report/test.ts create mode 100644 dev-packages/node-integration-tests/suites/tracing/no-parent-span-client-report-streamed/instrument.mjs create mode 100644 dev-packages/node-integration-tests/suites/tracing/no-parent-span-client-report-streamed/scenario.mjs create mode 100644 dev-packages/node-integration-tests/suites/tracing/no-parent-span-client-report-streamed/test.ts create mode 100644 dev-packages/node-integration-tests/suites/tracing/no-parent-span-client-report/instrument.mjs create mode 100644 dev-packages/node-integration-tests/suites/tracing/no-parent-span-client-report/scenario.mjs create mode 100644 dev-packages/node-integration-tests/suites/tracing/no-parent-span-client-report/test.ts diff --git a/.size-limit.js b/.size-limit.js index 718781bbd318..c4b37635ecda 100644 --- a/.size-limit.js +++ b/.size-limit.js @@ -203,7 +203,7 @@ module.exports = [ name: 'CDN Bundle (incl. Tracing, Logs, Metrics)', path: createCDNPath('bundle.tracing.logs.metrics.min.js'), gzip: true, - limit: '46 KB', + limit: '47 KB', }, { name: 'CDN Bundle (incl. Replay, Logs, Metrics)', @@ -324,7 +324,7 @@ module.exports = [ import: createImport('init'), ignore: [...builtinModules, ...nodePrefixedBuiltinModules], gzip: true, - limit: '59 KB', + limit: '60 KB', }, // Node SDK (ESM) { diff --git a/dev-packages/browser-integration-tests/suites/tracing/no-parent-span-client-report-streamed/init.js b/dev-packages/browser-integration-tests/suites/tracing/no-parent-span-client-report-streamed/init.js new file mode 100644 index 000000000000..94db849f5fde --- /dev/null +++ b/dev-packages/browser-integration-tests/suites/tracing/no-parent-span-client-report-streamed/init.js @@ -0,0 +1,13 @@ +import * as Sentry from '@sentry/browser'; + +window.Sentry = Sentry; + +Sentry.init({ + dsn: 'https://public@dsn.ingest.sentry.io/1337', + integrations: [ + Sentry.browserTracingIntegration({ instrumentPageLoad: false, instrumentNavigation: false }), + Sentry.spanStreamingIntegration(), + ], + tracesSampleRate: 1, + sendClientReports: true, +}); diff --git a/dev-packages/browser-integration-tests/suites/tracing/no-parent-span-client-report-streamed/subject.js b/dev-packages/browser-integration-tests/suites/tracing/no-parent-span-client-report-streamed/subject.js new file mode 100644 index 000000000000..6ba8011d77ac --- /dev/null +++ b/dev-packages/browser-integration-tests/suites/tracing/no-parent-span-client-report-streamed/subject.js @@ -0,0 +1 @@ +fetch('http://sentry-test-site.example/api/test'); diff --git a/dev-packages/browser-integration-tests/suites/tracing/no-parent-span-client-report-streamed/test.ts b/dev-packages/browser-integration-tests/suites/tracing/no-parent-span-client-report-streamed/test.ts new file mode 100644 index 000000000000..2672d41c17fb --- /dev/null +++ b/dev-packages/browser-integration-tests/suites/tracing/no-parent-span-client-report-streamed/test.ts @@ -0,0 +1,45 @@ +import { expect } from '@playwright/test'; +import type { ClientReport } from '@sentry/core'; +import { sentryTest } from '../../../utils/fixtures'; +import { + envelopeRequestParser, + hidePage, + shouldSkipTracingTest, + testingCdnBundle, + waitForClientReportRequest, +} from '../../../utils/helpers'; + +sentryTest( + 'records no_parent_span client report for fetch requests without an active span', + async ({ getLocalTestUrl, page }) => { + sentryTest.skip(shouldSkipTracingTest() || testingCdnBundle()); + + await page.route('http://sentry-test-site.example/api/test', route => { + route.fulfill({ + status: 200, + body: 'ok', + headers: { 'Content-Type': 'text/plain' }, + }); + }); + + const url = await getLocalTestUrl({ testDir: __dirname }); + + const clientReportPromise = waitForClientReportRequest(page, report => { + return report.discarded_events.some(e => e.reason === 'no_parent_span'); + }); + + await page.goto(url); + + await hidePage(page); + + const clientReport = envelopeRequestParser(await clientReportPromise); + + expect(clientReport.discarded_events).toEqual([ + { + category: 'span', + quantity: 1, + reason: 'no_parent_span', + }, + ]); + }, +); diff --git a/dev-packages/browser-integration-tests/suites/tracing/no-parent-span-client-report/init.js b/dev-packages/browser-integration-tests/suites/tracing/no-parent-span-client-report/init.js new file mode 100644 index 000000000000..10e5ac1b84eb --- /dev/null +++ b/dev-packages/browser-integration-tests/suites/tracing/no-parent-span-client-report/init.js @@ -0,0 +1,10 @@ +import * as Sentry from '@sentry/browser'; + +window.Sentry = Sentry; + +Sentry.init({ + dsn: 'https://public@dsn.ingest.sentry.io/1337', + integrations: [Sentry.browserTracingIntegration({ instrumentPageLoad: false, instrumentNavigation: false })], + tracesSampleRate: 1, + sendClientReports: true, +}); diff --git a/dev-packages/browser-integration-tests/suites/tracing/no-parent-span-client-report/subject.js b/dev-packages/browser-integration-tests/suites/tracing/no-parent-span-client-report/subject.js new file mode 100644 index 000000000000..6ba8011d77ac --- /dev/null +++ b/dev-packages/browser-integration-tests/suites/tracing/no-parent-span-client-report/subject.js @@ -0,0 +1 @@ +fetch('http://sentry-test-site.example/api/test'); diff --git a/dev-packages/browser-integration-tests/suites/tracing/no-parent-span-client-report/test.ts b/dev-packages/browser-integration-tests/suites/tracing/no-parent-span-client-report/test.ts new file mode 100644 index 000000000000..2672d41c17fb --- /dev/null +++ b/dev-packages/browser-integration-tests/suites/tracing/no-parent-span-client-report/test.ts @@ -0,0 +1,45 @@ +import { expect } from '@playwright/test'; +import type { ClientReport } from '@sentry/core'; +import { sentryTest } from '../../../utils/fixtures'; +import { + envelopeRequestParser, + hidePage, + shouldSkipTracingTest, + testingCdnBundle, + waitForClientReportRequest, +} from '../../../utils/helpers'; + +sentryTest( + 'records no_parent_span client report for fetch requests without an active span', + async ({ getLocalTestUrl, page }) => { + sentryTest.skip(shouldSkipTracingTest() || testingCdnBundle()); + + await page.route('http://sentry-test-site.example/api/test', route => { + route.fulfill({ + status: 200, + body: 'ok', + headers: { 'Content-Type': 'text/plain' }, + }); + }); + + const url = await getLocalTestUrl({ testDir: __dirname }); + + const clientReportPromise = waitForClientReportRequest(page, report => { + return report.discarded_events.some(e => e.reason === 'no_parent_span'); + }); + + await page.goto(url); + + await hidePage(page); + + const clientReport = envelopeRequestParser(await clientReportPromise); + + expect(clientReport.discarded_events).toEqual([ + { + category: 'span', + quantity: 1, + reason: 'no_parent_span', + }, + ]); + }, +); diff --git a/dev-packages/node-integration-tests/suites/tracing/no-parent-span-client-report-streamed/instrument.mjs b/dev-packages/node-integration-tests/suites/tracing/no-parent-span-client-report-streamed/instrument.mjs new file mode 100644 index 000000000000..b56505ef5e2d --- /dev/null +++ b/dev-packages/node-integration-tests/suites/tracing/no-parent-span-client-report-streamed/instrument.mjs @@ -0,0 +1,11 @@ +import * as Sentry from '@sentry/node'; +import { loggingTransport } from '@sentry-internal/node-integration-tests'; + +Sentry.init({ + dsn: 'https://public@dsn.ingest.sentry.io/1337', + release: '1.0', + tracesSampleRate: 1.0, + transport: loggingTransport, + traceLifecycle: 'stream', + clientReportFlushInterval: 1_000, +}); diff --git a/dev-packages/node-integration-tests/suites/tracing/no-parent-span-client-report-streamed/scenario.mjs b/dev-packages/node-integration-tests/suites/tracing/no-parent-span-client-report-streamed/scenario.mjs new file mode 100644 index 000000000000..18afc6db5113 --- /dev/null +++ b/dev-packages/node-integration-tests/suites/tracing/no-parent-span-client-report-streamed/scenario.mjs @@ -0,0 +1,2 @@ +import http from 'http'; +http.get('http://localhost:9999/external', () => {}).on('error', () => {}); diff --git a/dev-packages/node-integration-tests/suites/tracing/no-parent-span-client-report-streamed/test.ts b/dev-packages/node-integration-tests/suites/tracing/no-parent-span-client-report-streamed/test.ts new file mode 100644 index 000000000000..2b987f92d755 --- /dev/null +++ b/dev-packages/node-integration-tests/suites/tracing/no-parent-span-client-report-streamed/test.ts @@ -0,0 +1,29 @@ +import { afterAll, describe, expect } from 'vitest'; +import { cleanupChildProcesses, createEsmAndCjsTests } from '../../../utils/runner'; + +describe('no_parent_span client report (streaming)', () => { + afterAll(() => { + cleanupChildProcesses(); + }); + + createEsmAndCjsTests(__dirname, 'scenario.mjs', 'instrument.mjs', (createRunner, test) => { + test('records no_parent_span outcome for http.client span without a local parent', async () => { + const runner = createRunner() + .unignore('client_report') + .expect({ + client_report: report => { + expect(report.discarded_events).toEqual([ + { + category: 'span', + quantity: 1, + reason: 'no_parent_span', + }, + ]); + }, + }) + .start(); + + await runner.completed(); + }); + }); +}); diff --git a/dev-packages/node-integration-tests/suites/tracing/no-parent-span-client-report/instrument.mjs b/dev-packages/node-integration-tests/suites/tracing/no-parent-span-client-report/instrument.mjs new file mode 100644 index 000000000000..3a69e61ceb90 --- /dev/null +++ b/dev-packages/node-integration-tests/suites/tracing/no-parent-span-client-report/instrument.mjs @@ -0,0 +1,10 @@ +import * as Sentry from '@sentry/node'; +import { loggingTransport } from '@sentry-internal/node-integration-tests'; + +Sentry.init({ + dsn: 'https://public@dsn.ingest.sentry.io/1337', + release: '1.0', + tracesSampleRate: 1.0, + transport: loggingTransport, + clientReportFlushInterval: 1_000, +}); diff --git a/dev-packages/node-integration-tests/suites/tracing/no-parent-span-client-report/scenario.mjs b/dev-packages/node-integration-tests/suites/tracing/no-parent-span-client-report/scenario.mjs new file mode 100644 index 000000000000..18afc6db5113 --- /dev/null +++ b/dev-packages/node-integration-tests/suites/tracing/no-parent-span-client-report/scenario.mjs @@ -0,0 +1,2 @@ +import http from 'http'; +http.get('http://localhost:9999/external', () => {}).on('error', () => {}); diff --git a/dev-packages/node-integration-tests/suites/tracing/no-parent-span-client-report/test.ts b/dev-packages/node-integration-tests/suites/tracing/no-parent-span-client-report/test.ts new file mode 100644 index 000000000000..699dec65ddcf --- /dev/null +++ b/dev-packages/node-integration-tests/suites/tracing/no-parent-span-client-report/test.ts @@ -0,0 +1,29 @@ +import { afterAll, describe, expect } from 'vitest'; +import { cleanupChildProcesses, createEsmAndCjsTests } from '../../../utils/runner'; + +describe('no_parent_span client report', () => { + afterAll(() => { + cleanupChildProcesses(); + }); + + createEsmAndCjsTests(__dirname, 'scenario.mjs', 'instrument.mjs', (createRunner, test) => { + test('records no_parent_span outcome for http.client span without a local parent', async () => { + const runner = createRunner() + .unignore('client_report') + .expect({ + client_report: report => { + expect(report.discarded_events).toEqual([ + { + category: 'span', + quantity: 1, + reason: 'no_parent_span', + }, + ]); + }, + }) + .start(); + + await runner.completed(); + }); + }); +}); diff --git a/packages/browser/src/tracing/request.ts b/packages/browser/src/tracing/request.ts index 6211cf72947a..b393f0585b5b 100644 --- a/packages/browser/src/tracing/request.ts +++ b/packages/browser/src/tracing/request.ts @@ -404,6 +404,7 @@ function xhrCallback( const urlForSpanName = stripDataUrlContent(stripUrlQueryAndFragment(url)); + const client = getClient(); const hasParent = !!getActiveSpan(); const span = @@ -424,6 +425,10 @@ function xhrCallback( }) : new SentryNonRecordingSpan(); + if (shouldCreateSpanResult && !hasParent) { + client?.recordDroppedEvent('no_parent_span', 'span'); + } + xhr.__sentry_xhr_span_id__ = span.spanContext().spanId; spans[xhr.__sentry_xhr_span_id__] = span; @@ -438,7 +443,6 @@ function xhrCallback( ); } - const client = getClient(); if (client) { client.emit('beforeOutgoingRequestSpan', span, handlerData as XhrHint); } diff --git a/packages/core/src/fetch.ts b/packages/core/src/fetch.ts index 21a89b15c825..c65f147613dc 100644 --- a/packages/core/src/fetch.ts +++ b/packages/core/src/fetch.ts @@ -108,6 +108,7 @@ export function instrumentFetchRequest( const { spanOrigin = 'auto.http.browser', propagateTraceparent = false } = typeof spanOriginOrOptions === 'object' ? spanOriginOrOptions : { spanOrigin: spanOriginOrOptions }; + const client = getClient(); const hasParent = !!getActiveSpan(); const span = @@ -115,6 +116,10 @@ export function instrumentFetchRequest( ? startInactiveSpan(getSpanStartOptions(url, method, spanOrigin)) : new SentryNonRecordingSpan(); + if (shouldCreateSpanResult && !hasParent) { + client?.recordDroppedEvent('no_parent_span', 'span'); + } + handlerData.fetchData.__span = span.spanContext().spanId; spans[span.spanContext().spanId] = span; @@ -141,8 +146,6 @@ export function instrumentFetchRequest( } } - const client = getClient(); - if (client) { const fetchHint = { input: handlerData.args, diff --git a/packages/core/src/tracing/trace.ts b/packages/core/src/tracing/trace.ts index 5d40a0a30ebe..08411722cedf 100644 --- a/packages/core/src/tracing/trace.ts +++ b/packages/core/src/tracing/trace.ts @@ -68,9 +68,10 @@ export function startSpan(options: StartSpanOptions, callback: (span: Span) = return wrapper(() => { const scope = getCurrentScope(); const parentSpan = getParentSpan(scope, customParentSpan); + const client = getClient(); - const shouldSkipSpan = options.onlyIfParent && !parentSpan; - const activeSpan = shouldSkipSpan + const missingRequiredParent = options.onlyIfParent && !parentSpan; + const activeSpan = missingRequiredParent ? new SentryNonRecordingSpan() : createChildOrRootSpan({ parentSpan, @@ -79,6 +80,10 @@ export function startSpan(options: StartSpanOptions, callback: (span: Span) = scope, }); + if (missingRequiredParent) { + client?.recordDroppedEvent('no_parent_span', 'span'); + } + // Ignored root spans still need to be set on scope so that `getActiveSpan()` returns them // and descendants are also non-recording. Ignored child spans don't need this because // the parent span is already on scope. @@ -132,8 +137,8 @@ export function startSpanManual(options: StartSpanOptions, callback: (span: S const scope = getCurrentScope(); const parentSpan = getParentSpan(scope, customParentSpan); - const shouldSkipSpan = options.onlyIfParent && !parentSpan; - const activeSpan = shouldSkipSpan + const missingRequiredParent = options.onlyIfParent && !parentSpan; + const activeSpan = missingRequiredParent ? new SentryNonRecordingSpan() : createChildOrRootSpan({ parentSpan, @@ -142,6 +147,10 @@ export function startSpanManual(options: StartSpanOptions, callback: (span: S scope, }); + if (missingRequiredParent) { + getClient()?.recordDroppedEvent('no_parent_span', 'span'); + } + // We don't set ignored child spans onto the scope because there likely is an active, // unignored span on the scope already. if (!_isIgnoredSpan(activeSpan) || !parentSpan) { @@ -195,10 +204,12 @@ export function startInactiveSpan(options: StartSpanOptions): Span { return wrapper(() => { const scope = getCurrentScope(); const parentSpan = getParentSpan(scope, customParentSpan); + const client = getClient(); - const shouldSkipSpan = options.onlyIfParent && !parentSpan; + const missingRequiredParent = options.onlyIfParent && !parentSpan; - if (shouldSkipSpan) { + if (missingRequiredParent) { + client?.recordDroppedEvent('no_parent_span', 'span'); return new SentryNonRecordingSpan(); } diff --git a/packages/core/src/types-hoist/clientreport.ts b/packages/core/src/types-hoist/clientreport.ts index 4c07f1956014..154b58c5705e 100644 --- a/packages/core/src/types-hoist/clientreport.ts +++ b/packages/core/src/types-hoist/clientreport.ts @@ -11,7 +11,8 @@ export type EventDropReason = | 'internal_sdk_error' | 'buffer_overflow' | 'ignored' - | 'invalid'; + | 'invalid' + | 'no_parent_span'; export type Outcome = { reason: EventDropReason; diff --git a/packages/core/test/lib/tracing/trace.test.ts b/packages/core/test/lib/tracing/trace.test.ts index 0481f7f42687..db449f9a8ede 100644 --- a/packages/core/test/lib/tracing/trace.test.ts +++ b/packages/core/test/lib/tracing/trace.test.ts @@ -572,7 +572,7 @@ describe('startSpan', () => { }); describe('onlyIfParent', () => { - it('starts a non recording span if there is no parent', () => { + it('starts a non recording span and records no_parent_span client report if there is no parent', () => { const spyOnDroppedEvent = vi.spyOn(client, 'recordDroppedEvent'); const span = startSpan({ name: 'test span', onlyIfParent: true }, span => { @@ -581,10 +581,13 @@ describe('startSpan', () => { expect(span).toBeDefined(); expect(span).toBeInstanceOf(SentryNonRecordingSpan); - expect(spyOnDroppedEvent).not.toHaveBeenCalled(); + expect(spyOnDroppedEvent).toHaveBeenCalledWith('no_parent_span', 'span'); + expect(spyOnDroppedEvent).toHaveBeenCalledTimes(1); }); it('creates a span if there is a parent', () => { + const spyOnDroppedEvent = vi.spyOn(client, 'recordDroppedEvent'); + const span = startSpan({ name: 'parent span' }, () => { const span = startSpan({ name: 'test span', onlyIfParent: true }, span => { return span; @@ -595,6 +598,17 @@ describe('startSpan', () => { expect(span).toBeDefined(); expect(span).toBeInstanceOf(SentrySpan); + expect(spyOnDroppedEvent).not.toHaveBeenCalledWith('no_parent_span', 'span'); + }); + + it('does not record no_parent_span client report when onlyIfParent is not set', () => { + const spyOnDroppedEvent = vi.spyOn(client, 'recordDroppedEvent'); + + startSpan({ name: 'root span without onlyIfParent' }, span => { + return span; + }); + + expect(spyOnDroppedEvent).not.toHaveBeenCalledWith('no_parent_span', 'span'); }); }); @@ -1189,15 +1203,21 @@ describe('startSpanManual', () => { }); describe('onlyIfParent', () => { - it('does not create a span if there is no parent', () => { + it('does not create a span and records no_parent_span client report if there is no parent', () => { + const spyOnDroppedEvent = vi.spyOn(client, 'recordDroppedEvent'); + const span = startSpanManual({ name: 'test span', onlyIfParent: true }, span => { return span; }); expect(span).toBeDefined(); expect(span).toBeInstanceOf(SentryNonRecordingSpan); + expect(spyOnDroppedEvent).toHaveBeenCalledWith('no_parent_span', 'span'); + expect(spyOnDroppedEvent).toHaveBeenCalledTimes(1); }); it('creates a span if there is a parent', () => { + const spyOnDroppedEvent = vi.spyOn(client, 'recordDroppedEvent'); + const span = startSpan({ name: 'parent span' }, () => { const span = startSpanManual({ name: 'test span', onlyIfParent: true }, span => { return span; @@ -1208,6 +1228,18 @@ describe('startSpanManual', () => { expect(span).toBeDefined(); expect(span).toBeInstanceOf(SentrySpan); + expect(spyOnDroppedEvent).not.toHaveBeenCalledWith('no_parent_span', 'span'); + }); + + it('does not record no_parent_span client report when onlyIfParent is not set', () => { + const spyOnDroppedEvent = vi.spyOn(client, 'recordDroppedEvent'); + + startSpanManual({ name: 'root span without onlyIfParent' }, span => { + span.end(); + return span; + }); + + expect(spyOnDroppedEvent).not.toHaveBeenCalledWith('no_parent_span', 'span'); }); }); @@ -1592,14 +1624,20 @@ describe('startInactiveSpan', () => { }); describe('onlyIfParent', () => { - it('does not create a span if there is no parent', () => { + it('does not create a span and records no_parent_span client report if there is no parent', () => { + const spyOnDroppedEvent = vi.spyOn(client, 'recordDroppedEvent'); + const span = startInactiveSpan({ name: 'test span', onlyIfParent: true }); expect(span).toBeDefined(); expect(span).toBeInstanceOf(SentryNonRecordingSpan); + expect(spyOnDroppedEvent).toHaveBeenCalledWith('no_parent_span', 'span'); + expect(spyOnDroppedEvent).toHaveBeenCalledTimes(1); }); it('creates a span if there is a parent', () => { + const spyOnDroppedEvent = vi.spyOn(client, 'recordDroppedEvent'); + const span = startSpan({ name: 'parent span' }, () => { const span = startInactiveSpan({ name: 'test span', onlyIfParent: true }); return span; @@ -1607,6 +1645,16 @@ describe('startInactiveSpan', () => { expect(span).toBeDefined(); expect(span).toBeInstanceOf(SentrySpan); + expect(spyOnDroppedEvent).not.toHaveBeenCalledWith('no_parent_span', 'span'); + }); + + it('does not record no_parent_span client report when onlyIfParent is not set', () => { + const spyOnDroppedEvent = vi.spyOn(client, 'recordDroppedEvent'); + + const span = startInactiveSpan({ name: 'root span without onlyIfParent' }); + span.end(); + + expect(spyOnDroppedEvent).not.toHaveBeenCalledWith('no_parent_span', 'span'); }); }); diff --git a/packages/opentelemetry/src/sampler.ts b/packages/opentelemetry/src/sampler.ts index 5c0c47423284..1e65e9d15d14 100644 --- a/packages/opentelemetry/src/sampler.ts +++ b/packages/opentelemetry/src/sampler.ts @@ -77,6 +77,7 @@ export class SentrySampler implements Sampler { // If we have a http.client span that has no local parent, we never want to sample it // but we want to leave downstream sampling decisions up to the server if (spanKind === SpanKind.CLIENT && maybeSpanHttpMethod && (!parentSpan || parentContext?.isRemote)) { + this._client.recordDroppedEvent('no_parent_span', 'span'); return wrapSamplingDecision({ decision: undefined, context, spanAttributes }); } diff --git a/packages/opentelemetry/src/trace.ts b/packages/opentelemetry/src/trace.ts index 7c9d09a169b9..b60bda367704 100644 --- a/packages/opentelemetry/src/trace.ts +++ b/packages/opentelemetry/src/trace.ts @@ -48,8 +48,12 @@ function _startSpan(options: OpenTelemetrySpanContext, callback: (span: Span) return wrapper(() => { const activeCtx = getContext(options.scope, options.forceTransaction); - const shouldSkipSpan = options.onlyIfParent && !trace.getSpan(activeCtx); - const ctx = shouldSkipSpan ? suppressTracing(activeCtx) : activeCtx; + const missingRequiredParent = options.onlyIfParent && !trace.getSpan(activeCtx); + const ctx = missingRequiredParent ? suppressTracing(activeCtx) : activeCtx; + + if (missingRequiredParent) { + getClient()?.recordDroppedEvent('no_parent_span', 'span'); + } const spanOptions = getSpanOptions(options); @@ -151,8 +155,12 @@ export function startInactiveSpan(options: OpenTelemetrySpanContext): Span { return wrapper(() => { const activeCtx = getContext(options.scope, options.forceTransaction); - const shouldSkipSpan = options.onlyIfParent && !trace.getSpan(activeCtx); - let ctx = shouldSkipSpan ? suppressTracing(activeCtx) : activeCtx; + const missingRequiredParent = options.onlyIfParent && !trace.getSpan(activeCtx); + let ctx = missingRequiredParent ? suppressTracing(activeCtx) : activeCtx; + + if (missingRequiredParent) { + getClient()?.recordDroppedEvent('no_parent_span', 'span'); + } const spanOptions = getSpanOptions(options); diff --git a/packages/opentelemetry/test/sampler.test.ts b/packages/opentelemetry/test/sampler.test.ts index 654d96be91c4..22fa724fa161 100644 --- a/packages/opentelemetry/test/sampler.test.ts +++ b/packages/opentelemetry/test/sampler.test.ts @@ -120,7 +120,7 @@ describe('SentrySampler', () => { spyOnDroppedEvent.mockReset(); }); - it('ignores local http client root spans', () => { + it('ignores local http client root spans and records no_parent_span client report', () => { const client = new TestClient(getDefaultTestClientOptions({ tracesSampleRate: 0 })); const spyOnDroppedEvent = vi.spyOn(client, 'recordDroppedEvent'); const sampler = new SentrySampler(client); @@ -139,7 +139,8 @@ describe('SentrySampler', () => { decision: SamplingDecision.NOT_RECORD, traceState: new TraceState(), }); - expect(spyOnDroppedEvent).toHaveBeenCalledTimes(0); + expect(spyOnDroppedEvent).toHaveBeenCalledTimes(1); + expect(spyOnDroppedEvent).toHaveBeenCalledWith('no_parent_span', 'span'); spyOnDroppedEvent.mockReset(); }); diff --git a/packages/opentelemetry/test/trace.test.ts b/packages/opentelemetry/test/trace.test.ts index a6a7f35ab76a..0aeacc5284ad 100644 --- a/packages/opentelemetry/test/trace.test.ts +++ b/packages/opentelemetry/test/trace.test.ts @@ -530,15 +530,23 @@ describe('trace', () => { // TODO: propagation scope is not picked up by spans... describe('onlyIfParent', () => { - it('does not create a span if there is no parent', () => { + it('does not create a span and records no_parent_span client report if there is no parent', () => { + const client = getClient()!; + const spyOnDroppedEvent = vi.spyOn(client, 'recordDroppedEvent'); + const span = startSpan({ name: 'test span', onlyIfParent: true }, span => { return span; }); expect(isSpan(span)).toBe(false); + expect(spyOnDroppedEvent).toHaveBeenCalledWith('no_parent_span', 'span'); + expect(spyOnDroppedEvent).toHaveBeenCalledTimes(1); }); it('creates a span if there is a parent', () => { + const client = getClient()!; + const spyOnDroppedEvent = vi.spyOn(client, 'recordDroppedEvent'); + const span = startSpan({ name: 'parent span' }, () => { const span = startSpan({ name: 'test span', onlyIfParent: true }, span => { return span; @@ -548,6 +556,33 @@ describe('trace', () => { }); expect(isSpan(span)).toBe(true); + expect(spyOnDroppedEvent).not.toHaveBeenCalledWith('no_parent_span', 'span'); + }); + + it('does not record no_parent_span client report when onlyIfParent is not set', () => { + const client = getClient()!; + const spyOnDroppedEvent = vi.spyOn(client, 'recordDroppedEvent'); + + context.with(ROOT_CONTEXT, () => { + startSpan({ name: 'root span without onlyIfParent' }, span => { + return span; + }); + }); + + expect(spyOnDroppedEvent).not.toHaveBeenCalledWith('no_parent_span', 'span'); + }); + + it('does not record no_parent_span client report when onlyIfParent is false even without a parent', () => { + const client = getClient()!; + const spyOnDroppedEvent = vi.spyOn(client, 'recordDroppedEvent'); + + context.with(ROOT_CONTEXT, () => { + startSpan({ name: 'root span', onlyIfParent: false }, span => { + return span; + }); + }); + + expect(spyOnDroppedEvent).not.toHaveBeenCalledWith('no_parent_span', 'span'); }); }); }); @@ -824,13 +859,21 @@ describe('trace', () => { }); describe('onlyIfParent', () => { - it('does not create a span if there is no parent', () => { + it('does not create a span and records no_parent_span client report if there is no parent', () => { + const client = getClient()!; + const spyOnDroppedEvent = vi.spyOn(client, 'recordDroppedEvent'); + const span = startInactiveSpan({ name: 'test span', onlyIfParent: true }); expect(isSpan(span)).toBe(false); + expect(spyOnDroppedEvent).toHaveBeenCalledWith('no_parent_span', 'span'); + expect(spyOnDroppedEvent).toHaveBeenCalledTimes(1); }); it('creates a span if there is a parent', () => { + const client = getClient()!; + const spyOnDroppedEvent = vi.spyOn(client, 'recordDroppedEvent'); + const span = startSpan({ name: 'parent span' }, () => { const span = startInactiveSpan({ name: 'test span', onlyIfParent: true }); @@ -838,6 +881,31 @@ describe('trace', () => { }); expect(isSpan(span)).toBe(true); + expect(spyOnDroppedEvent).not.toHaveBeenCalledWith('no_parent_span', 'span'); + }); + + it('does not record no_parent_span client report when onlyIfParent is not set', () => { + const client = getClient()!; + const spyOnDroppedEvent = vi.spyOn(client, 'recordDroppedEvent'); + + context.with(ROOT_CONTEXT, () => { + const span = startInactiveSpan({ name: 'root span without onlyIfParent' }); + span.end(); + }); + + expect(spyOnDroppedEvent).not.toHaveBeenCalledWith('no_parent_span', 'span'); + }); + + it('does not record no_parent_span client report when onlyIfParent is false even without a parent', () => { + const client = getClient()!; + const spyOnDroppedEvent = vi.spyOn(client, 'recordDroppedEvent'); + + context.with(ROOT_CONTEXT, () => { + const span = startInactiveSpan({ name: 'root span', onlyIfParent: false }); + span.end(); + }); + + expect(spyOnDroppedEvent).not.toHaveBeenCalledWith('no_parent_span', 'span'); }); }); @@ -1192,15 +1260,23 @@ describe('trace', () => { }); describe('onlyIfParent', () => { - it('does not create a span if there is no parent', () => { + it('does not create a span and records no_parent_span client report if there is no parent', () => { + const client = getClient()!; + const spyOnDroppedEvent = vi.spyOn(client, 'recordDroppedEvent'); + const span = startSpanManual({ name: 'test span', onlyIfParent: true }, span => { return span; }); expect(isSpan(span)).toBe(false); + expect(spyOnDroppedEvent).toHaveBeenCalledWith('no_parent_span', 'span'); + expect(spyOnDroppedEvent).toHaveBeenCalledTimes(1); }); it('creates a span if there is a parent', () => { + const client = getClient()!; + const spyOnDroppedEvent = vi.spyOn(client, 'recordDroppedEvent'); + const span = startSpan({ name: 'parent span' }, () => { const span = startSpanManual({ name: 'test span', onlyIfParent: true }, span => { return span; @@ -1210,6 +1286,35 @@ describe('trace', () => { }); expect(isSpan(span)).toBe(true); + expect(spyOnDroppedEvent).not.toHaveBeenCalledWith('no_parent_span', 'span'); + }); + + it('does not record no_parent_span client report when onlyIfParent is not set', () => { + const client = getClient()!; + const spyOnDroppedEvent = vi.spyOn(client, 'recordDroppedEvent'); + + context.with(ROOT_CONTEXT, () => { + startSpanManual({ name: 'root span without onlyIfParent' }, span => { + span.end(); + return span; + }); + }); + + expect(spyOnDroppedEvent).not.toHaveBeenCalledWith('no_parent_span', 'span'); + }); + + it('does not record no_parent_span client report when onlyIfParent is false even without a parent', () => { + const client = getClient()!; + const spyOnDroppedEvent = vi.spyOn(client, 'recordDroppedEvent'); + + context.with(ROOT_CONTEXT, () => { + startSpanManual({ name: 'root span', onlyIfParent: false }, span => { + span.end(); + return span; + }); + }); + + expect(spyOnDroppedEvent).not.toHaveBeenCalledWith('no_parent_span', 'span'); }); }); }); From 6284aff0acc6ec47a457b320c302a1e6c663e6bd Mon Sep 17 00:00:00 2001 From: Abdelrahman Awad Date: Mon, 20 Apr 2026 09:28:41 -0400 Subject: [PATCH 11/41] feat(replay): Add replayStart/replayEnd client lifecycle hooks (#20369) Expose replay lifecycle events on the client so external consumers can observe when recording starts and stops. This includes internal stops (session expiry, send errors, mutation limit, event buffer overflow) that today are invisible to wrapper libraries. Closes #20281. ## API ```ts getClient()?.on('replayStart', ({ sessionId, recordingMode }) => { // recordingMode: 'session' | 'buffer' }); getClient()?.on('replayEnd', ({ sessionId, reason }) => { // reason: 'manual' | 'sessionExpired' | 'sendError' | 'mutationLimit' // | 'eventBufferError' | 'eventBufferOverflow' }); ``` The typed `reason` union lets consumers distinguish a user-initiated `replay.stop()` from an internally triggered stop, so wrapper state can stay in sync with actual replay state. --------- Co-authored-by: Claude Opus 4.7 (1M context) --- packages/core/src/client.ts | 24 ++++ packages/core/src/index.ts | 9 +- packages/core/src/types-hoist/replay.ts | 34 ++++++ packages/replay-internal/src/integration.ts | 2 +- packages/replay-internal/src/replay.ts | 23 +++- packages/replay-internal/src/types/replay.ts | 11 +- packages/replay-internal/src/util/addEvent.ts | 2 +- .../test/integration/lifecycleHooks.test.ts | 109 ++++++++++++++++++ 8 files changed, 204 insertions(+), 10 deletions(-) create mode 100644 packages/replay-internal/test/integration/lifecycleHooks.test.ts diff --git a/packages/core/src/client.ts b/packages/core/src/client.ts index 6c3ca949f38e..00c12db06855 100644 --- a/packages/core/src/client.ts +++ b/packages/core/src/client.ts @@ -28,6 +28,7 @@ import type { Metric } from './types-hoist/metric'; import type { Primitive } from './types-hoist/misc'; import type { ClientOptions } from './types-hoist/options'; import type { ParameterizedString } from './types-hoist/parameterize'; +import type { ReplayEndEvent, ReplayStartEvent } from './types-hoist/replay'; import type { RequestEventData } from './types-hoist/request'; import type { SdkMetadata } from './types-hoist/sdkmetadata'; import type { Session, SessionAggregates } from './types-hoist/session'; @@ -726,6 +727,19 @@ export abstract class Client { */ public on(hook: 'openFeedbackWidget', callback: () => void): () => void; + /** + * A hook that is called when a replay session starts recording (either session or buffer mode). + * @returns {() => void} A function that, when executed, removes the registered callback. + */ + public on(hook: 'replayStart', callback: (event: ReplayStartEvent) => void): () => void; + + /** + * A hook that is called when a replay session stops recording, either manually or due to an + * internal condition such as `maxReplayDuration` expiry, send failure, or mutation limit. + * @returns {() => void} A function that, when executed, removes the registered callback. + */ + public on(hook: 'replayEnd', callback: (event: ReplayEndEvent) => void): () => void; + /** * A hook for the browser tracing integrations to trigger a span start for a page load. * @returns {() => void} A function that, when executed, removes the registered callback. @@ -1001,6 +1015,16 @@ export abstract class Client { */ public emit(hook: 'openFeedbackWidget'): void; + /** + * Fire a hook event when a replay session starts recording. + */ + public emit(hook: 'replayStart', event: ReplayStartEvent): void; + + /** + * Fire a hook event when a replay session stops recording. + */ + public emit(hook: 'replayEnd', event: ReplayEndEvent): void; + /** * Emit a hook event for browser tracing integrations to trigger a span start for a page load. */ diff --git a/packages/core/src/index.ts b/packages/core/src/index.ts index f4039244e550..38a1c2d4a5ab 100644 --- a/packages/core/src/index.ts +++ b/packages/core/src/index.ts @@ -441,7 +441,14 @@ export type { Profile, ProfileChunk, } from './types-hoist/profiling'; -export type { ReplayEvent, ReplayRecordingData, ReplayRecordingMode } from './types-hoist/replay'; +export type { + ReplayEndEvent, + ReplayEvent, + ReplayRecordingData, + ReplayRecordingMode, + ReplayStartEvent, + ReplayStopReason, +} from './types-hoist/replay'; export type { FeedbackEvent, FeedbackFormData, diff --git a/packages/core/src/types-hoist/replay.ts b/packages/core/src/types-hoist/replay.ts index 65641ce011bd..a23f548aa357 100644 --- a/packages/core/src/types-hoist/replay.ts +++ b/packages/core/src/types-hoist/replay.ts @@ -25,3 +25,37 @@ export type ReplayRecordingData = string | Uint8Array; * @hidden */ export type ReplayRecordingMode = 'session' | 'buffer'; + +/** + * Reason a replay recording stopped, passed to the `replayEnd` client hook. + * + * - `manual`: user called `replay.stop()`. + * - `sessionExpired`: session hit `maxReplayDuration` or the idle-expiry threshold. + * - `sendError`: a replay segment failed to send after retries. + * - `mutationLimit`: DOM mutation budget for the session was exhausted. + * - `eventBufferError`: the event buffer threw an unexpected error. + * - `eventBufferOverflow`: the event buffer ran out of space. + */ +export type ReplayStopReason = + | 'manual' + | 'sessionExpired' + | 'sendError' + | 'mutationLimit' + | 'eventBufferError' + | 'eventBufferOverflow'; + +/** + * Payload emitted on the `replayStart` client hook when a replay begins recording. + */ +export interface ReplayStartEvent { + sessionId: string; + recordingMode: ReplayRecordingMode; +} + +/** + * Payload emitted on the `replayEnd` client hook when a replay stops recording. + */ +export interface ReplayEndEvent { + sessionId?: string; + reason: ReplayStopReason; +} diff --git a/packages/replay-internal/src/integration.ts b/packages/replay-internal/src/integration.ts index a940ef746979..ec762eacd8dd 100644 --- a/packages/replay-internal/src/integration.ts +++ b/packages/replay-internal/src/integration.ts @@ -297,7 +297,7 @@ export class Replay implements Integration { return Promise.resolve(); } - return this._replay.stop({ forceFlush: this._replay.recordingMode === 'session' }); + return this._replay.stop({ forceFlush: this._replay.recordingMode === 'session', reason: 'manual' }); } /** diff --git a/packages/replay-internal/src/replay.ts b/packages/replay-internal/src/replay.ts index cab408ca9d5d..d80f47a6704b 100644 --- a/packages/replay-internal/src/replay.ts +++ b/packages/replay-internal/src/replay.ts @@ -1,5 +1,5 @@ /* eslint-disable max-lines */ // TODO: We might want to split this file up -import type { ReplayRecordingMode, Span } from '@sentry/core'; +import type { ReplayRecordingMode, ReplayStopReason, Span } from '@sentry/core'; import { getActiveSpan, getClient, getRootSpan, SEMANTIC_ATTRIBUTE_SENTRY_SOURCE, spanToJSON } from '@sentry/core'; import { EventType, record } from '@sentry-internal/rrweb'; import { @@ -495,7 +495,10 @@ export class ReplayContainer implements ReplayContainerInterface { * Currently, this needs to be manually called (e.g. for tests). Sentry SDK * does not support a teardown */ - public async stop({ forceFlush = false, reason }: { forceFlush?: boolean; reason?: string } = {}): Promise { + public async stop({ + forceFlush = false, + reason, + }: { forceFlush?: boolean; reason?: ReplayStopReason } = {}): Promise { if (!this._isEnabled) { return; } @@ -508,8 +511,11 @@ export class ReplayContainer implements ReplayContainerInterface { // breadcrumbs to trigger a flush (e.g. in `addUpdate()`) this.recordingMode = 'buffer'; + const stopReason: ReplayStopReason = reason ?? 'manual'; + getClient()?.emit('replayEnd', { sessionId: this.session?.id, reason: stopReason }); + try { - DEBUG_BUILD && debug.log(`Stopping Replay${reason ? ` triggered by ${reason}` : ''}`); + DEBUG_BUILD && debug.log(`Stopping Replay triggered by ${stopReason}`); resetReplayIdOnDynamicSamplingContext(); @@ -862,6 +868,13 @@ export class ReplayContainer implements ReplayContainerInterface { this._isEnabled = true; this._isPaused = false; + if (this.session) { + getClient()?.emit('replayStart', { + sessionId: this.session.id, + recordingMode: this.recordingMode, + }); + } + this.startRecording(); } @@ -926,7 +939,7 @@ export class ReplayContainer implements ReplayContainerInterface { if (!this._isEnabled) { return; } - await this.stop({ reason: 'refresh session' }); + await this.stop({ reason: 'sessionExpired' }); this.initializeSampling(session.id); } @@ -1212,7 +1225,7 @@ export class ReplayContainer implements ReplayContainerInterface { // In this case, we want to completely stop the replay - otherwise, we may get inconsistent segments // This should never reject // eslint-disable-next-line @typescript-eslint/no-floating-promises - this.stop({ reason: 'sendReplay' }); + this.stop({ reason: 'sendError' }); const client = getClient(); diff --git a/packages/replay-internal/src/types/replay.ts b/packages/replay-internal/src/types/replay.ts index 6f8d836611bb..95cfbdd849bf 100644 --- a/packages/replay-internal/src/types/replay.ts +++ b/packages/replay-internal/src/types/replay.ts @@ -1,4 +1,11 @@ -import type { Breadcrumb, ErrorEvent, ReplayRecordingData, ReplayRecordingMode, Span } from '@sentry/core'; +import type { + Breadcrumb, + ErrorEvent, + ReplayRecordingData, + ReplayRecordingMode, + ReplayStopReason, + Span, +} from '@sentry/core'; import type { SKIPPED, THROTTLED } from '../util/throttle'; import type { AllPerformanceEntry, AllPerformanceEntryData, ReplayPerformanceEntry } from './performance'; import type { ReplayFrameEvent } from './replayFrame'; @@ -507,7 +514,7 @@ export interface ReplayContainer { getContext(): InternalEventContext; initializeSampling(): void; start(): void; - stop(options?: { reason?: string; forceflush?: boolean }): Promise; + stop(options?: { reason?: ReplayStopReason; forceFlush?: boolean }): Promise; pause(): void; resume(): void; startRecording(): void; diff --git a/packages/replay-internal/src/util/addEvent.ts b/packages/replay-internal/src/util/addEvent.ts index 0cd76227379c..d7c11f5f0ab6 100644 --- a/packages/replay-internal/src/util/addEvent.ts +++ b/packages/replay-internal/src/util/addEvent.ts @@ -82,7 +82,7 @@ async function _addEvent( return await eventBuffer.addEvent(eventAfterPossibleCallback); } catch (error) { const isExceeded = error && error instanceof EventBufferSizeExceededError; - const reason = isExceeded ? 'addEventSizeExceeded' : 'addEvent'; + const reason = isExceeded ? 'eventBufferOverflow' : 'eventBufferError'; const client = getClient(); if (client) { diff --git a/packages/replay-internal/test/integration/lifecycleHooks.test.ts b/packages/replay-internal/test/integration/lifecycleHooks.test.ts new file mode 100644 index 000000000000..814e50491bfb --- /dev/null +++ b/packages/replay-internal/test/integration/lifecycleHooks.test.ts @@ -0,0 +1,109 @@ +/** + * @vitest-environment jsdom + */ + +import '../utils/mock-internal-setTimeout'; +import type { ReplayEndEvent, ReplayStartEvent } from '@sentry/core'; +import { getClient } from '@sentry/core'; +import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from 'vitest'; +import type { Replay } from '../../src/integration'; +import type { ReplayContainer } from '../../src/replay'; +import { BASE_TIMESTAMP } from '../index'; +import { resetSdkMock } from '../mocks/resetSdkMock'; + +describe('Integration | lifecycle hooks', () => { + let replay: ReplayContainer; + let integration: Replay; + let startEvents: ReplayStartEvent[]; + let endEvents: ReplayEndEvent[]; + let unsubscribes: Array<() => void>; + + beforeAll(() => { + vi.useFakeTimers(); + }); + + beforeEach(async () => { + ({ replay, integration } = await resetSdkMock({ + replayOptions: { stickySession: false }, + sentryOptions: { replaysSessionSampleRate: 0.0 }, + autoStart: false, + })); + + startEvents = []; + endEvents = []; + const client = getClient()!; + unsubscribes = [ + client.on('replayStart', event => startEvents.push(event)), + client.on('replayEnd', event => endEvents.push(event)), + ]; + + await vi.runAllTimersAsync(); + }); + + afterEach(async () => { + unsubscribes.forEach(off => off()); + await integration.stop(); + await vi.runAllTimersAsync(); + vi.setSystemTime(new Date(BASE_TIMESTAMP)); + }); + + it('fires replayStart with session mode when start() is called', () => { + integration.start(); + + expect(startEvents).toHaveLength(1); + expect(startEvents[0]).toEqual({ + sessionId: expect.any(String), + recordingMode: 'session', + }); + expect(startEvents[0]!.sessionId).toBe(replay.session!.id); + }); + + it('fires replayStart with buffer mode when startBuffering() is called', () => { + integration.startBuffering(); + + expect(startEvents).toHaveLength(1); + expect(startEvents[0]).toEqual({ + sessionId: expect.any(String), + recordingMode: 'buffer', + }); + }); + + it('fires replayEnd with reason "manual" when integration.stop() is called', async () => { + integration.start(); + const sessionId = replay.session!.id; + + await integration.stop(); + + expect(endEvents).toHaveLength(1); + expect(endEvents[0]).toEqual({ sessionId, reason: 'manual' }); + }); + + it('forwards the internal stop reason to replayEnd subscribers', async () => { + integration.start(); + const sessionId = replay.session!.id; + + await replay.stop({ reason: 'mutationLimit' }); + + expect(endEvents).toHaveLength(1); + expect(endEvents[0]).toEqual({ sessionId, reason: 'mutationLimit' }); + }); + + it('does not fire replayEnd twice when stop() is called while already stopped', async () => { + integration.start(); + + await replay.stop({ reason: 'sendError' }); + await replay.stop({ reason: 'sendError' }); + + expect(endEvents).toHaveLength(1); + expect(endEvents[0]!.reason).toBe('sendError'); + }); + + it('stops invoking callbacks after the returned unsubscribe is called', () => { + const [offStart] = unsubscribes; + offStart!(); + + integration.start(); + + expect(startEvents).toHaveLength(0); + }); +}); From 5d0d14531511fcd703438e072723ef98cd700ea3 Mon Sep 17 00:00:00 2001 From: Sigrid <32902192+s1gr1d@users.noreply.github.com> Date: Mon, 20 Apr 2026 15:50:00 +0200 Subject: [PATCH 12/41] test(node): Refactor integration tests for `honoIntegration` (#20397) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Each `test` run in the node-integration-tests runs a new node process so it should be avoided to have multiple `test` calls within one scenario. The tests for the `honoIntegration` in Node were especially long-running because there were 480 test cases running a `test`. This was now changed to just two `test` scenarios while running the test matrix within this one test. All 12 tests now pass in about 8 seconds. **Before:** 480 test cases (2 routes x 5 methods x 3 paths x 8 tests x 2 modes), each spawning a separate Node process. **After:** 12 test cases (6 per mode x 2 modes): 1. **Transaction tests** (1 test per mode) — loops through all 90 route/method/path/type combinations inside a single test, making 90 sequential requests against one running server process. 2. **500 error tests** (1 test per mode) — loops through all 30 combinations, ignoring transactions and asserting error events. 3. **4xx ignored error tests** (4 tests per mode) — tests each error sub-path (`/401`, `/402`, `/403`, `/does-not-exist`) with a single representative combination (`GET /sync`) instead of all 120 combinations. The 4xx error-filtering behavior (`statusCode >= 500` check in `defaultShouldHandleError`) is method/route/path-agnostic, so full matrix coverage isn't needed here. --- .../suites/tracing/hono/test.ts | 358 ++++++------------ 1 file changed, 122 insertions(+), 236 deletions(-) diff --git a/dev-packages/node-integration-tests/suites/tracing/hono/test.ts b/dev-packages/node-integration-tests/suites/tracing/hono/test.ts index 67d0ff8b56fb..484e7c948407 100644 --- a/dev-packages/node-integration-tests/suites/tracing/hono/test.ts +++ b/dev-packages/node-integration-tests/suites/tracing/hono/test.ts @@ -1,251 +1,137 @@ -import { afterAll, describe, expect } from 'vitest'; +import { afterAll, expect } from 'vitest'; import { cleanupChildProcesses, createEsmAndCjsTests } from '../../../utils/runner'; -describe('hono tracing', () => { - afterAll(() => { - cleanupChildProcesses(); +const ROUTES = ['/sync', '/async'] as const; +const METHODS = ['get', 'post', 'put', 'delete', 'patch'] as const; +const PATHS = ['/', '/all', '/on'] as const; + +type Method = (typeof METHODS)[number]; + +function verifyHonoSpan(name: string, type: 'middleware' | 'request_handler') { + return expect.objectContaining({ + data: expect.objectContaining({ + 'hono.name': name, + 'hono.type': type, + }), + description: name, + op: type === 'request_handler' ? 'request_handler.hono' : 'middleware.hono', + origin: 'auto.http.otel.hono', }); +} - createEsmAndCjsTests(__dirname, 'scenario.mjs', 'instrument.mjs', (createRunner, test) => { - describe.each(['/sync', '/async'] as const)('when using %s route', route => { - describe.each(['get', 'post', 'put', 'delete', 'patch'] as const)('when using %s method', method => { - describe.each(['/', '/all', '/on'])('when using %s path', path => { - test('should handle transaction', async () => { - const runner = createRunner() - .expect({ - transaction: { - transaction: `${method.toUpperCase()} ${route}${path === '/' ? '' : path}`, - spans: expect.arrayContaining([ - expect.objectContaining({ - data: expect.objectContaining({ - 'hono.name': 'sentryRequestMiddleware', - 'hono.type': 'middleware', - }), - description: 'sentryRequestMiddleware', - op: 'middleware.hono', - origin: 'auto.http.otel.hono', - }), - expect.objectContaining({ - data: expect.objectContaining({ - 'hono.name': 'sentryErrorMiddleware', - 'hono.type': 'middleware', - }), - description: 'sentryErrorMiddleware', - op: 'middleware.hono', - origin: 'auto.http.otel.hono', - }), - expect.objectContaining({ - data: expect.objectContaining({ - 'hono.name': 'global', - 'hono.type': 'middleware', - }), - description: 'global', - op: 'middleware.hono', - origin: 'auto.http.otel.hono', - }), - expect.objectContaining({ - data: expect.objectContaining({ - 'hono.name': 'base', - 'hono.type': 'middleware', - }), - description: 'base', - op: 'middleware.hono', - origin: 'auto.http.otel.hono', - }), - expect.objectContaining({ - data: expect.objectContaining({ - 'hono.name': `${route}${path === '/' ? '' : path}`, - 'hono.type': 'request_handler', - }), - description: `${route}${path === '/' ? '' : path}`, - op: 'request_handler.hono', - origin: 'auto.http.otel.hono', - }), - ]), - }, - }) - .start(); - runner.makeRequest(method, `${route}${path === '/' ? '' : path}`); - await runner.completed(); - }); +function baseSpans() { + return [ + verifyHonoSpan('sentryRequestMiddleware', 'middleware'), + verifyHonoSpan('sentryErrorMiddleware', 'middleware'), + verifyHonoSpan('global', 'middleware'), + verifyHonoSpan('base', 'middleware'), + ]; +} + +afterAll(() => { + cleanupChildProcesses(); +}); - test('should handle transaction with anonymous middleware', async () => { - const runner = createRunner() - .expect({ - transaction: { - transaction: `${method.toUpperCase()} ${route}${path === '/' ? '' : path}/middleware`, - spans: expect.arrayContaining([ - expect.objectContaining({ - data: expect.objectContaining({ - 'hono.name': 'sentryRequestMiddleware', - 'hono.type': 'middleware', - }), - description: 'sentryRequestMiddleware', - op: 'middleware.hono', - origin: 'auto.http.otel.hono', - }), - expect.objectContaining({ - data: expect.objectContaining({ - 'hono.name': 'sentryErrorMiddleware', - 'hono.type': 'middleware', - }), - description: 'sentryErrorMiddleware', - op: 'middleware.hono', - origin: 'auto.http.otel.hono', - }), - expect.objectContaining({ - data: expect.objectContaining({ - 'hono.name': 'global', - 'hono.type': 'middleware', - }), - description: 'global', - op: 'middleware.hono', - origin: 'auto.http.otel.hono', - }), - expect.objectContaining({ - data: expect.objectContaining({ - 'hono.name': 'base', - 'hono.type': 'middleware', - }), - description: 'base', - op: 'middleware.hono', - origin: 'auto.http.otel.hono', - }), - expect.objectContaining({ - data: expect.objectContaining({ - 'hono.name': 'anonymous', - 'hono.type': 'middleware', - }), - description: 'anonymous', - op: 'middleware.hono', - origin: 'auto.http.otel.hono', - }), - expect.objectContaining({ - data: expect.objectContaining({ - 'hono.name': `${route}${path === '/' ? '' : path}/middleware`, - 'hono.type': 'request_handler', - }), - description: `${route}${path === '/' ? '' : path}/middleware`, - op: 'request_handler.hono', - origin: 'auto.http.otel.hono', - }), - ]), - }, - }) - .start(); - runner.makeRequest(method, `${route}${path === '/' ? '' : path}/middleware`); - await runner.completed(); +createEsmAndCjsTests(__dirname, 'scenario.mjs', 'instrument.mjs', (createRunner, test) => { + test('should handle transactions for all route/method/path combinations', async () => { + const runner = createRunner(); + const requests: Array<{ method: Method; url: string }> = []; + + for (const route of ROUTES) { + for (const method of METHODS) { + for (const path of PATHS) { + const pathSuffix = path === '/' ? '' : path; + const fullPath = `${route}${pathSuffix}`; + + runner.expect({ + transaction: { + transaction: `${method.toUpperCase()} ${fullPath}`, + spans: expect.arrayContaining([...baseSpans(), verifyHonoSpan(fullPath, 'request_handler')]), + }, }); + requests.push({ method, url: fullPath }); - test('should handle transaction with separate middleware', async () => { - const runner = createRunner() - .expect({ - transaction: { - transaction: `${method.toUpperCase()} ${route}${path === '/' ? '' : path}/middleware/separately`, - spans: expect.arrayContaining([ - expect.objectContaining({ - data: expect.objectContaining({ - 'hono.name': 'sentryRequestMiddleware', - 'hono.type': 'middleware', - }), - description: 'sentryRequestMiddleware', - op: 'middleware.hono', - origin: 'auto.http.otel.hono', - }), - expect.objectContaining({ - data: expect.objectContaining({ - 'hono.name': 'sentryErrorMiddleware', - 'hono.type': 'middleware', - }), - description: 'sentryErrorMiddleware', - op: 'middleware.hono', - origin: 'auto.http.otel.hono', - }), - expect.objectContaining({ - data: expect.objectContaining({ - 'hono.name': 'global', - 'hono.type': 'middleware', - }), - description: 'global', - op: 'middleware.hono', - origin: 'auto.http.otel.hono', - }), - expect.objectContaining({ - data: expect.objectContaining({ - 'hono.name': 'base', - 'hono.type': 'middleware', - }), - description: 'base', - op: 'middleware.hono', - origin: 'auto.http.otel.hono', - }), - expect.objectContaining({ - data: expect.objectContaining({ - 'hono.name': 'anonymous', - 'hono.type': 'middleware', - }), - description: 'anonymous', - op: 'middleware.hono', - origin: 'auto.http.otel.hono', - }), - expect.objectContaining({ - data: expect.objectContaining({ - 'hono.name': `${route}${path === '/' ? '' : path}/middleware/separately`, - 'hono.type': 'request_handler', - }), - description: `${route}${path === '/' ? '' : path}/middleware/separately`, - op: 'request_handler.hono', - origin: 'auto.http.otel.hono', - }), - ]), - }, - }) - .start(); - runner.makeRequest(method, `${route}${path === '/' ? '' : path}/middleware/separately`); - await runner.completed(); + runner.expect({ + transaction: { + transaction: `${method.toUpperCase()} ${fullPath}/middleware`, + spans: expect.arrayContaining([ + ...baseSpans(), + verifyHonoSpan('anonymous', 'middleware'), + verifyHonoSpan(`${fullPath}/middleware`, 'request_handler'), + ]), + }, }); + requests.push({ method, url: `${fullPath}/middleware` }); - test('should handle returned errors for %s path', async () => { - const runner = createRunner() - .ignore('transaction') - .expect({ - event: { - exception: { - values: [ - { - mechanism: { - type: 'auto.middleware.hono', - handled: false, - }, - type: 'Error', - value: 'response 500', - }, - ], - }, - }, - }) - .start(); - runner.makeRequest(method, `${route}${path === '/' ? '' : path}/500`, { expectError: true }); - await runner.completed(); + runner.expect({ + transaction: { + transaction: `${method.toUpperCase()} ${fullPath}/middleware/separately`, + spans: expect.arrayContaining([ + ...baseSpans(), + verifyHonoSpan('anonymous', 'middleware'), + verifyHonoSpan(`${fullPath}/middleware/separately`, 'request_handler'), + ]), + }, }); + requests.push({ method, url: `${fullPath}/middleware/separately` }); + } + } + } + + const started = runner.start(); + for (const req of requests) { + await started.makeRequest(req.method, req.url); + } + await started.completed(); + }, 60_000); + + test('should capture 500 errors for all route/method/path combinations', async () => { + const runner = createRunner().ignore('transaction'); + const requests: Array<{ method: Method; url: string }> = []; - test.each(['/401', '/402', '/403', '/does-not-exist'])( - 'should ignores error %s path by default', - async (subPath: string) => { - const runner = createRunner() - .expect({ - transaction: { - transaction: `${method.toUpperCase()} ${route}`, + for (const route of ROUTES) { + for (const method of METHODS) { + for (const path of PATHS) { + const pathSuffix = path === '/' ? '' : path; + + runner.expect({ + event: { + exception: { + values: [ + { + mechanism: { + type: 'auto.middleware.hono', + handled: false, + }, + type: 'Error', + value: 'response 500', }, - }) - .start(); - runner.makeRequest(method, `${route}${path === '/' ? '' : path}${subPath}`, { expectError: true }); - runner.makeRequest(method, route); - await runner.completed(); + ], + }, }, - ); - }); - }); - }); + }); + requests.push({ method, url: `${route}${pathSuffix}/500` }); + } + } + } + + const started = runner.start(); + for (const req of requests) { + await started.makeRequest(req.method, req.url, { expectError: true }); + } + await started.completed(); + }, 60_000); + + test.each(['/401', '/402', '/403', '/does-not-exist'])('should not capture %s errors', async (subPath: string) => { + const runner = createRunner() + .expect({ + transaction: { + transaction: 'GET /sync', + }, + }) + .start(); + runner.makeRequest('get', `/sync${subPath}`, { expectError: true }); + runner.makeRequest('get', '/sync'); + await runner.completed(); }); }); From a87183e687797be2dcc30fd5f9d9af05be7d5815 Mon Sep 17 00:00:00 2001 From: Abdelrahman Awad Date: Mon, 20 Apr 2026 14:57:33 -0400 Subject: [PATCH 13/41] fix(node-core): Pass rejection reason instead of Promise as originalException (#20366) This PR passes the rejection reason to `captureException` as `originalException`. Previously it was passing the promise which was useless to users. This aligns Node with browser/onUncaughtException behavior as well. closes #20325 Co-authored-by: Claude Opus 4.7 (1M context) --- .../src/integrations/onunhandledrejection.ts | 4 +- .../integrations/onunhandledrejection.test.ts | 54 +++++++++++++++++++ 2 files changed, 56 insertions(+), 2 deletions(-) create mode 100644 packages/node-core/test/integrations/onunhandledrejection.test.ts diff --git a/packages/node-core/src/integrations/onunhandledrejection.ts b/packages/node-core/src/integrations/onunhandledrejection.ts index af40bacfda57..8e2483d6a8cb 100644 --- a/packages/node-core/src/integrations/onunhandledrejection.ts +++ b/packages/node-core/src/integrations/onunhandledrejection.ts @@ -85,7 +85,7 @@ export function makeUnhandledPromiseHandler( client: Client, options: OnUnhandledRejectionOptions, ): (reason: unknown, promise: unknown) => void { - return function sendUnhandledPromise(reason: unknown, promise: unknown): void { + return function sendUnhandledPromise(reason: unknown, _promise: unknown): void { // Only handle for the active client if (getClient() !== client) { return; @@ -109,7 +109,7 @@ export function makeUnhandledPromiseHandler( activeSpanWrapper(() => { captureException(reason, { - originalException: promise, + originalException: reason, captureContext: { extra: { unhandledPromiseRejection: true }, level, diff --git a/packages/node-core/test/integrations/onunhandledrejection.test.ts b/packages/node-core/test/integrations/onunhandledrejection.test.ts new file mode 100644 index 000000000000..1f2c2c3c2581 --- /dev/null +++ b/packages/node-core/test/integrations/onunhandledrejection.test.ts @@ -0,0 +1,54 @@ +import * as SentryCore from '@sentry/core'; +import type { Client } from '@sentry/core'; +import { afterEach, describe, expect, it, vi } from 'vitest'; +import { + makeUnhandledPromiseHandler, + onUnhandledRejectionIntegration, +} from '../../src/integrations/onunhandledrejection'; + +// don't log the test errors we're going to throw, so at a quick glance it doesn't look like the test itself has failed +global.console.warn = () => null; +global.console.error = () => null; + +describe('unhandled promises', () => { + afterEach(() => { + vi.restoreAllMocks(); + }); + + it('installs a global listener', () => { + const client = { getOptions: () => ({}) } as unknown as Client; + SentryCore.setCurrentClient(client); + + const beforeListeners = process.listeners('unhandledRejection').length; + + const integration = onUnhandledRejectionIntegration(); + integration.setup!(client); + + expect(process.listeners('unhandledRejection').length).toBe(beforeListeners + 1); + }); + + it('passes the rejection reason (not the promise) as originalException', () => { + const client = { getOptions: () => ({}) } as unknown as Client; + SentryCore.setCurrentClient(client); + + const reason = new Error('boom'); + const promise = Promise.reject(reason); + // swallow the rejection so it does not leak into the test runner + promise.catch(() => {}); + + const captureException = vi.spyOn(SentryCore, 'captureException').mockImplementation(() => 'test'); + + const handler = makeUnhandledPromiseHandler(client, { mode: 'warn', ignore: [] }); + handler(reason, promise); + + expect(captureException).toHaveBeenCalledTimes(1); + const [capturedReason, hint] = captureException.mock.calls[0]!; + expect(capturedReason).toBe(reason); + expect(hint?.originalException).toBe(reason); + expect(hint?.originalException).not.toBe(promise); + expect(hint?.mechanism).toEqual({ + handled: false, + type: 'auto.node.onunhandledrejection', + }); + }); +}); From 403391eeb2a5d5eaf5ec00a342385735ef9d7687 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan=20Peer=20St=C3=B6cklmair?= Date: Tue, 21 Apr 2026 11:30:14 +0200 Subject: [PATCH 14/41] chore: Update size-limit (#20412) Currently the size-limits are failing by ~40bytes on develop. --- .size-limit.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.size-limit.js b/.size-limit.js index c4b37635ecda..adef55219f86 100644 --- a/.size-limit.js +++ b/.size-limit.js @@ -221,7 +221,7 @@ module.exports = [ name: 'CDN Bundle (incl. Tracing, Replay, Logs, Metrics)', path: createCDNPath('bundle.tracing.replay.logs.metrics.min.js'), gzip: true, - limit: '83 KB', + limit: '84 KB', }, { name: 'CDN Bundle (incl. Tracing, Replay, Feedback)', @@ -283,7 +283,7 @@ module.exports = [ path: createCDNPath('bundle.tracing.replay.logs.metrics.min.js'), gzip: false, brotli: false, - limit: '255 KB', + limit: '256 KB', }, { name: 'CDN Bundle (incl. Tracing, Replay, Feedback) - uncompressed', @@ -297,7 +297,7 @@ module.exports = [ path: createCDNPath('bundle.tracing.replay.feedback.logs.metrics.min.js'), gzip: false, brotli: false, - limit: '268 KB', + limit: '269 KB', }, // Next.js SDK (ESM) { From fe4428e90e54df4b72c52a3f2f67b42439ac3473 Mon Sep 17 00:00:00 2001 From: Alexander Pantiukhov Date: Tue, 21 Apr 2026 13:01:55 +0200 Subject: [PATCH 15/41] fix(core): Correct GoogleGenAIIstrumentedMethod typo in type name Rename `GoogleGenAIIstrumentedMethod` to `GoogleGenAIInstrumentedMethod` (previously missing an 'n'). The misspelled name is kept as a deprecated type alias and is still re-exported from `@sentry/core`, so existing consumers continue to work without changes. The deprecated alias will be removed in the next major version. --- packages/core/src/index.ts | 4 +++- packages/core/src/tracing/google-genai/types.ts | 8 ++++++-- 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/packages/core/src/index.ts b/packages/core/src/index.ts index 38a1c2d4a5ab..c3f8c454e997 100644 --- a/packages/core/src/index.ts +++ b/packages/core/src/index.ts @@ -192,8 +192,10 @@ export type { GoogleGenAIClient, GoogleGenAIChat, GoogleGenAIOptions, - GoogleGenAIIstrumentedMethod, + GoogleGenAIInstrumentedMethod, } from './tracing/google-genai/types'; +// eslint-disable-next-line deprecation/deprecation +export type { GoogleGenAIIstrumentedMethod } from './tracing/google-genai/types'; export { SpanBuffer } from './tracing/spans/spanBuffer'; export { hasSpanStreamingEnabled } from './tracing/spans/hasSpanStreamingEnabled'; diff --git a/packages/core/src/tracing/google-genai/types.ts b/packages/core/src/tracing/google-genai/types.ts index 69f1e279fbd0..35ca728a4a60 100644 --- a/packages/core/src/tracing/google-genai/types.ts +++ b/packages/core/src/tracing/google-genai/types.ts @@ -186,10 +186,14 @@ export interface GoogleGenAIChat { sendMessageStream: (...args: unknown[]) => Promise>; } +export type GoogleGenAIInstrumentedMethod = keyof typeof GOOGLE_GENAI_METHOD_REGISTRY; + /** - * @deprecated This type is no longer used and will be removed in the next major version. + * @deprecated Use {@link GoogleGenAIInstrumentedMethod} instead. This alias + * preserves backwards compatibility with the misspelled name and will be + * removed in the next major version. */ -export type GoogleGenAIIstrumentedMethod = keyof typeof GOOGLE_GENAI_METHOD_REGISTRY; +export type GoogleGenAIIstrumentedMethod = GoogleGenAIInstrumentedMethod; // Export the response type for use in instrumentation export type GoogleGenAIResponse = GenerateContentResponse; From e4e6c016d058b04614cc182f757f43b2e458d8c2 Mon Sep 17 00:00:00 2001 From: Sigrid <32902192+s1gr1d@users.noreply.github.com> Date: Tue, 21 Apr 2026 15:06:14 +0200 Subject: [PATCH 16/41] test(hono): Add E2E test for Hono on Cloudflare, Node and Bun (#20406) Adds a minimal E2E test setup. More tests are added later on - to keep reviewing this easier. Adds a testing matrix where each test uses a different entry point. This way, only one E2E application needs to be added and we have an easier time keeping the tests and functionality in sync across runtimes. Closes https://github.com/getsentry/sentry-javascript/issues/20403 --- .github/workflows/build.yml | 4 +- .../test-applications/hono-4/.gitignore | 36 +++++++++++++ .../e2e-tests/test-applications/hono-4/.npmrc | 2 + .../test-applications/hono-4/package.json | 43 +++++++++++++++ .../hono-4/playwright.config.ts | 32 +++++++++++ .../test-applications/hono-4/src/entry.bun.ts | 24 +++++++++ .../hono-4/src/entry.cloudflare.ts | 18 +++++++ .../hono-4/src/entry.node.ts | 24 +++++++++ .../test-applications/hono-4/src/routes.ts | 24 +++++++++ .../hono-4/start-event-proxy.mjs | 6 +++ .../hono-4/tests/errors.test.ts | 54 +++++++++++++++++++ .../hono-4/tests/tracing.test.ts | 41 ++++++++++++++ .../test-applications/hono-4/tsconfig.json | 13 +++++ .../test-applications/hono-4/wrangler.jsonc | 7 +++ 14 files changed, 327 insertions(+), 1 deletion(-) create mode 100644 dev-packages/e2e-tests/test-applications/hono-4/.gitignore create mode 100644 dev-packages/e2e-tests/test-applications/hono-4/.npmrc create mode 100644 dev-packages/e2e-tests/test-applications/hono-4/package.json create mode 100644 dev-packages/e2e-tests/test-applications/hono-4/playwright.config.ts create mode 100644 dev-packages/e2e-tests/test-applications/hono-4/src/entry.bun.ts create mode 100644 dev-packages/e2e-tests/test-applications/hono-4/src/entry.cloudflare.ts create mode 100644 dev-packages/e2e-tests/test-applications/hono-4/src/entry.node.ts create mode 100644 dev-packages/e2e-tests/test-applications/hono-4/src/routes.ts create mode 100644 dev-packages/e2e-tests/test-applications/hono-4/start-event-proxy.mjs create mode 100644 dev-packages/e2e-tests/test-applications/hono-4/tests/errors.test.ts create mode 100644 dev-packages/e2e-tests/test-applications/hono-4/tests/tracing.test.ts create mode 100644 dev-packages/e2e-tests/test-applications/hono-4/tsconfig.json create mode 100644 dev-packages/e2e-tests/test-applications/hono-4/wrangler.jsonc diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 544bb7900008..d686f8293158 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -930,7 +930,9 @@ jobs: with: node-version-file: 'dev-packages/e2e-tests/test-applications/${{ matrix.test-application }}/package.json' - name: Set up Bun - if: contains(fromJSON('["node-exports-test-app","nextjs-16-bun", "elysia-bun"]'), matrix.test-application) + if: + contains(fromJSON('["node-exports-test-app","nextjs-16-bun", "elysia-bun", "hono-4"]'), + matrix.test-application) uses: oven-sh/setup-bun@v2 - name: Set up AWS SAM if: matrix.test-application == 'aws-serverless' diff --git a/dev-packages/e2e-tests/test-applications/hono-4/.gitignore b/dev-packages/e2e-tests/test-applications/hono-4/.gitignore new file mode 100644 index 000000000000..534f51704346 --- /dev/null +++ b/dev-packages/e2e-tests/test-applications/hono-4/.gitignore @@ -0,0 +1,36 @@ +# prod +dist/ + +# dev +.yarn/ +!.yarn/releases +.vscode/* +!.vscode/launch.json +!.vscode/*.code-snippets +.idea/workspace.xml +.idea/usage.statistics.xml +.idea/shelf + +# deps +node_modules/ +.wrangler + +# env +.env +.env.production +.dev.vars + +# logs +logs/ +*.log +npm-debug.log* +yarn-debug.log* +yarn-error.log* +pnpm-debug.log* +lerna-debug.log* + +# test +test-results + +# misc +.DS_Store diff --git a/dev-packages/e2e-tests/test-applications/hono-4/.npmrc b/dev-packages/e2e-tests/test-applications/hono-4/.npmrc new file mode 100644 index 000000000000..070f80f05092 --- /dev/null +++ b/dev-packages/e2e-tests/test-applications/hono-4/.npmrc @@ -0,0 +1,2 @@ +@sentry:registry=http://127.0.0.1:4873 +@sentry-internal:registry=http://127.0.0.1:4873 diff --git a/dev-packages/e2e-tests/test-applications/hono-4/package.json b/dev-packages/e2e-tests/test-applications/hono-4/package.json new file mode 100644 index 000000000000..117555f7241f --- /dev/null +++ b/dev-packages/e2e-tests/test-applications/hono-4/package.json @@ -0,0 +1,43 @@ +{ + "name": "hono-4", + "type": "module", + "version": "0.0.0", + "private": true, + "scripts": { + "dev:cf": "wrangler dev --var \"E2E_TEST_DSN:$E2E_TEST_DSN\" --log-level=$(test $CI && echo 'none' || echo 'log')", + "dev:node": "node --import tsx/esm --import @sentry/node/preload src/entry.node.ts", + "dev:bun": "bun src/entry.bun.ts", + "build": "wrangler deploy --dry-run", + "test:build": "pnpm install && pnpm build", + "test:assert": "TEST_ENV=production playwright test" + }, + "dependencies": { + "@sentry/hono": "latest || *", + "@sentry/node": "latest || *", + "@hono/node-server": "^1.19.10", + "hono": "^4.12.14" + }, + "devDependencies": { + "@playwright/test": "~1.56.0", + "@cloudflare/workers-types": "^4.20240725.0", + "@sentry-internal/test-utils": "link:../../../test-utils", + "tsx": "^4.20.3", + "typescript": "^5.5.2", + "wrangler": "^4.61.0" + }, + "volta": { + "extends": "../../package.json" + }, + "sentryTest": { + "variants": [ + { + "assert-command": "RUNTIME=node pnpm test:assert", + "label": "hono-4 (node)" + }, + { + "assert-command": "RUNTIME=bun pnpm test:assert", + "label": "hono-4 (bun)" + } + ] + } +} diff --git a/dev-packages/e2e-tests/test-applications/hono-4/playwright.config.ts b/dev-packages/e2e-tests/test-applications/hono-4/playwright.config.ts new file mode 100644 index 000000000000..74a21e10a349 --- /dev/null +++ b/dev-packages/e2e-tests/test-applications/hono-4/playwright.config.ts @@ -0,0 +1,32 @@ +import { getPlaywrightConfig } from '@sentry-internal/test-utils'; + +type Runtime = 'cloudflare' | 'node' | 'bun'; + +const RUNTIME = (process.env.RUNTIME || 'cloudflare') as Runtime; + +const testEnv = process.env.TEST_ENV; + +if (!testEnv) { + throw new Error('No test env defined'); +} + +const APP_PORT = 38787; + +const startCommands: Record = { + cloudflare: `pnpm dev:cf --port ${APP_PORT}`, + node: `pnpm dev:node`, + bun: `pnpm dev:bun`, +}; + +const config = getPlaywrightConfig( + { + startCommand: startCommands[RUNTIME], + port: APP_PORT, + }, + { + workers: '100%', + retries: 0, + }, +); + +export default config; diff --git a/dev-packages/e2e-tests/test-applications/hono-4/src/entry.bun.ts b/dev-packages/e2e-tests/test-applications/hono-4/src/entry.bun.ts new file mode 100644 index 000000000000..2a27d1adb8a9 --- /dev/null +++ b/dev-packages/e2e-tests/test-applications/hono-4/src/entry.bun.ts @@ -0,0 +1,24 @@ +import { Hono } from 'hono'; +import { sentry } from '@sentry/hono/bun'; +import { addRoutes } from './routes'; + +const app = new Hono<{ Bindings: { E2E_TEST_DSN: string } }>(); + +app.use( + // @ts-expect-error - Env is not yet in type + sentry(app, { + dsn: process.env.E2E_TEST_DSN, + environment: 'qa', + tracesSampleRate: 1.0, + tunnel: 'http://localhost:3031/', + }), +); + +addRoutes(app); + +const port = Number(process.env.PORT || 38787); + +export default { + port, + fetch: app.fetch, +}; diff --git a/dev-packages/e2e-tests/test-applications/hono-4/src/entry.cloudflare.ts b/dev-packages/e2e-tests/test-applications/hono-4/src/entry.cloudflare.ts new file mode 100644 index 000000000000..e348dde56226 --- /dev/null +++ b/dev-packages/e2e-tests/test-applications/hono-4/src/entry.cloudflare.ts @@ -0,0 +1,18 @@ +import { Hono } from 'hono'; +import { sentry } from '@sentry/hono/cloudflare'; +import { addRoutes } from './routes'; + +const app = new Hono<{ Bindings: { E2E_TEST_DSN: string } }>(); + +app.use( + sentry(app, env => ({ + dsn: env.E2E_TEST_DSN, + environment: 'qa', + tracesSampleRate: 1.0, + tunnel: 'http://localhost:3031/', + })), +); + +addRoutes(app); + +export default app; diff --git a/dev-packages/e2e-tests/test-applications/hono-4/src/entry.node.ts b/dev-packages/e2e-tests/test-applications/hono-4/src/entry.node.ts new file mode 100644 index 000000000000..eb2c669c6806 --- /dev/null +++ b/dev-packages/e2e-tests/test-applications/hono-4/src/entry.node.ts @@ -0,0 +1,24 @@ +import { Hono } from 'hono'; +import { sentry } from '@sentry/hono/node'; +import { serve } from '@hono/node-server'; +import { addRoutes } from './routes'; + +const app = new Hono<{ Bindings: { E2E_TEST_DSN: string } }>(); + +app.use( + // @ts-expect-error - Env is not yet in type + sentry(app, { + dsn: process.env.E2E_TEST_DSN, + environment: 'qa', + tracesSampleRate: 1.0, + tunnel: 'http://localhost:3031/', + }), +); + +addRoutes(app); + +const port = Number(process.env.PORT || 38787); + +serve({ fetch: app.fetch, port }, () => { + console.log(`Hono (Node) listening on port ${port}`); +}); diff --git a/dev-packages/e2e-tests/test-applications/hono-4/src/routes.ts b/dev-packages/e2e-tests/test-applications/hono-4/src/routes.ts new file mode 100644 index 000000000000..fbb273c7c425 --- /dev/null +++ b/dev-packages/e2e-tests/test-applications/hono-4/src/routes.ts @@ -0,0 +1,24 @@ +import type { Hono } from 'hono'; +import { HTTPException } from 'hono/http-exception'; + +export function addRoutes(app: Hono<{ Bindings: { E2E_TEST_DSN: string } }>): void { + app.get('/', c => { + return c.text('Hello Hono!'); + }); + + app.get('/test-param/:paramId', c => { + return c.json({ paramId: c.req.param('paramId') }); + }); + + app.get('/error/:cause', c => { + throw new Error('This is a test error for Sentry!', { + cause: c.req.param('cause'), + }); + }); + + app.get('/http-exception/:code', c => { + // oxlint-disable-next-line typescript/no-explicit-any + const code = Number(c.req.param('code')) as any; + throw new HTTPException(code, { message: `HTTPException ${code}` }); + }); +} diff --git a/dev-packages/e2e-tests/test-applications/hono-4/start-event-proxy.mjs b/dev-packages/e2e-tests/test-applications/hono-4/start-event-proxy.mjs new file mode 100644 index 000000000000..cd6f91b3455d --- /dev/null +++ b/dev-packages/e2e-tests/test-applications/hono-4/start-event-proxy.mjs @@ -0,0 +1,6 @@ +import { startEventProxyServer } from '@sentry-internal/test-utils'; + +startEventProxyServer({ + port: 3031, + proxyServerName: 'hono-4', +}); diff --git a/dev-packages/e2e-tests/test-applications/hono-4/tests/errors.test.ts b/dev-packages/e2e-tests/test-applications/hono-4/tests/errors.test.ts new file mode 100644 index 000000000000..e85958e8328b --- /dev/null +++ b/dev-packages/e2e-tests/test-applications/hono-4/tests/errors.test.ts @@ -0,0 +1,54 @@ +import { expect, test } from '@playwright/test'; +import { waitForError } from '@sentry-internal/test-utils'; + +const APP_NAME = 'hono-4'; + +test('captures error thrown in route handler', async ({ baseURL }) => { + const errorWaiter = waitForError(APP_NAME, event => { + return event.exception?.values?.[0]?.value === 'This is a test error for Sentry!'; + }); + + const response = await fetch(`${baseURL}/error/test-cause`); + expect(response.status).toBe(500); + + const event = await errorWaiter; + expect(event.exception?.values?.[0]?.value).toBe('This is a test error for Sentry!'); +}); + +test('captures HTTPException with 502 status', async ({ baseURL }) => { + const errorWaiter = waitForError(APP_NAME, event => { + return event.exception?.values?.[0]?.value === 'HTTPException 502'; + }); + + const response = await fetch(`${baseURL}/http-exception/502`); + expect(response.status).toBe(502); + + const event = await errorWaiter; + expect(event.exception?.values?.[0]?.value).toBe('HTTPException 502'); +}); + +// TODO: 401 and 404 HTTPExceptions should not be captured by Sentry by default, +// but currently they are. Fix the filtering and update these tests accordingly. +test('captures HTTPException with 401 status', async ({ baseURL }) => { + const errorWaiter = waitForError(APP_NAME, event => { + return event.exception?.values?.[0]?.value === 'HTTPException 401'; + }); + + const response = await fetch(`${baseURL}/http-exception/401`); + expect(response.status).toBe(401); + + const event = await errorWaiter; + expect(event.exception?.values?.[0]?.value).toBe('HTTPException 401'); +}); + +test('captures HTTPException with 404 status', async ({ baseURL }) => { + const errorWaiter = waitForError(APP_NAME, event => { + return event.exception?.values?.[0]?.value === 'HTTPException 404'; + }); + + const response = await fetch(`${baseURL}/http-exception/404`); + expect(response.status).toBe(404); + + const event = await errorWaiter; + expect(event.exception?.values?.[0]?.value).toBe('HTTPException 404'); +}); diff --git a/dev-packages/e2e-tests/test-applications/hono-4/tests/tracing.test.ts b/dev-packages/e2e-tests/test-applications/hono-4/tests/tracing.test.ts new file mode 100644 index 000000000000..58c73c6a8369 --- /dev/null +++ b/dev-packages/e2e-tests/test-applications/hono-4/tests/tracing.test.ts @@ -0,0 +1,41 @@ +import { expect, test } from '@playwright/test'; +import { waitForTransaction } from '@sentry-internal/test-utils'; + +const APP_NAME = 'hono-4'; + +test('sends a transaction for the index route', async ({ baseURL }) => { + const transactionWaiter = waitForTransaction(APP_NAME, event => { + return event.transaction === 'GET /'; + }); + + const response = await fetch(`${baseURL}/`); + expect(response.status).toBe(200); + + const transaction = await transactionWaiter; + expect(transaction.contexts?.trace?.op).toBe('http.server'); +}); + +test('sends a transaction for a parameterized route', async ({ baseURL }) => { + const transactionWaiter = waitForTransaction(APP_NAME, event => { + return event.transaction === 'GET /test-param/:paramId'; + }); + + const response = await fetch(`${baseURL}/test-param/123`); + expect(response.status).toBe(200); + + const transaction = await transactionWaiter; + expect(transaction.contexts?.trace?.op).toBe('http.server'); + expect(transaction.transaction).toBe('GET /test-param/:paramId'); +}); + +test('sends a transaction for a route that throws', async ({ baseURL }) => { + const transactionWaiter = waitForTransaction(APP_NAME, event => { + return event.transaction === 'GET /error/:cause'; + }); + + await fetch(`${baseURL}/error/test-cause`); + + const transaction = await transactionWaiter; + expect(transaction.contexts?.trace?.op).toBe('http.server'); + expect(transaction.contexts?.trace?.status).toBe('internal_error'); +}); diff --git a/dev-packages/e2e-tests/test-applications/hono-4/tsconfig.json b/dev-packages/e2e-tests/test-applications/hono-4/tsconfig.json new file mode 100644 index 000000000000..3c4abeff44d6 --- /dev/null +++ b/dev-packages/e2e-tests/test-applications/hono-4/tsconfig.json @@ -0,0 +1,13 @@ +{ + "compilerOptions": { + "target": "ESNext", + "module": "ESNext", + "moduleResolution": "Bundler", + "strict": true, + "skipLibCheck": true, + "lib": ["ESNext"], + "jsx": "react-jsx", + "jsxImportSource": "hono/jsx", + "types": ["@cloudflare/workers-types"] + } +} diff --git a/dev-packages/e2e-tests/test-applications/hono-4/wrangler.jsonc b/dev-packages/e2e-tests/test-applications/hono-4/wrangler.jsonc new file mode 100644 index 000000000000..d4344dfa198a --- /dev/null +++ b/dev-packages/e2e-tests/test-applications/hono-4/wrangler.jsonc @@ -0,0 +1,7 @@ +{ + "$schema": "node_modules/wrangler/config-schema.json", + "name": "hono-4", + "main": "src/entry.cloudflare.ts", + "compatibility_date": "2026-04-20", + "compatibility_flags": ["nodejs_compat"], +} From 69f0bbc5d84b4f7cabd4fd78dcce6236a36b2643 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan=20Peer=20St=C3=B6cklmair?= Date: Mon, 20 Apr 2026 08:57:05 +0200 Subject: [PATCH 17/41] test(effect): Rename effect e2e tests to a versioned folder (#20390) This PR is only renaming Effect v3 e2e tests, which had no versioning, to one with a version. This makes it easier in the future to distinguish between Effect v3 and v4 tests. So effectively this has changed: - `effect-node` -> `effect-3-node` - `effect-browser` -> `effect-3-browser` --- .../{effect-browser => effect-3-browser}/.gitignore | 0 .../{effect-browser => effect-3-browser}/.npmrc | 0 .../{effect-browser => effect-3-browser}/build.mjs | 0 .../{effect-browser => effect-3-browser}/package.json | 2 +- .../playwright.config.mjs | 0 .../public/index.html | 0 .../{effect-browser => effect-3-browser}/src/index.js | 0 .../start-event-proxy.mjs | 2 +- .../tests/errors.test.ts | 6 +++--- .../tests/logs.test.ts | 10 +++++----- .../tests/transactions.test.ts | 10 +++++----- .../{effect-browser => effect-3-browser}/tsconfig.json | 0 .../{effect-node => effect-3-node}/.gitignore | 0 .../{effect-node => effect-3-node}/.npmrc | 0 .../{effect-node => effect-3-node}/package.json | 2 +- .../playwright.config.mjs | 0 .../{effect-node => effect-3-node}/src/app.ts | 0 .../start-event-proxy.mjs | 2 +- .../tests/errors.test.ts | 8 ++++---- .../{effect-node => effect-3-node}/tests/logs.test.ts | 10 +++++----- .../tests/transactions.test.ts | 8 ++++---- .../{effect-node => effect-3-node}/tsconfig.json | 0 22 files changed, 30 insertions(+), 30 deletions(-) rename dev-packages/e2e-tests/test-applications/{effect-browser => effect-3-browser}/.gitignore (100%) rename dev-packages/e2e-tests/test-applications/{effect-browser => effect-3-browser}/.npmrc (100%) rename dev-packages/e2e-tests/test-applications/{effect-browser => effect-3-browser}/build.mjs (100%) rename dev-packages/e2e-tests/test-applications/{effect-browser => effect-3-browser}/package.json (96%) rename dev-packages/e2e-tests/test-applications/{effect-browser => effect-3-browser}/playwright.config.mjs (100%) rename dev-packages/e2e-tests/test-applications/{effect-browser => effect-3-browser}/public/index.html (100%) rename dev-packages/e2e-tests/test-applications/{effect-browser => effect-3-browser}/src/index.js (100%) rename dev-packages/e2e-tests/test-applications/{effect-browser => effect-3-browser}/start-event-proxy.mjs (74%) rename dev-packages/e2e-tests/test-applications/{effect-browser => effect-3-browser}/tests/errors.test.ts (87%) rename dev-packages/e2e-tests/test-applications/{effect-browser => effect-3-browser}/tests/logs.test.ts (90%) rename dev-packages/e2e-tests/test-applications/{effect-browser => effect-3-browser}/tests/transactions.test.ts (90%) rename dev-packages/e2e-tests/test-applications/{effect-browser => effect-3-browser}/tsconfig.json (100%) rename dev-packages/e2e-tests/test-applications/{effect-node => effect-3-node}/.gitignore (100%) rename dev-packages/e2e-tests/test-applications/{effect-node => effect-3-node}/.npmrc (100%) rename dev-packages/e2e-tests/test-applications/{effect-node => effect-3-node}/package.json (95%) rename dev-packages/e2e-tests/test-applications/{effect-node => effect-3-node}/playwright.config.mjs (100%) rename dev-packages/e2e-tests/test-applications/{effect-node => effect-3-node}/src/app.ts (100%) rename dev-packages/e2e-tests/test-applications/{effect-node => effect-3-node}/start-event-proxy.mjs (75%) rename dev-packages/e2e-tests/test-applications/{effect-node => effect-3-node}/tests/errors.test.ts (86%) rename dev-packages/e2e-tests/test-applications/{effect-node => effect-3-node}/tests/logs.test.ts (88%) rename dev-packages/e2e-tests/test-applications/{effect-node => effect-3-node}/tests/transactions.test.ts (88%) rename dev-packages/e2e-tests/test-applications/{effect-node => effect-3-node}/tsconfig.json (100%) diff --git a/dev-packages/e2e-tests/test-applications/effect-browser/.gitignore b/dev-packages/e2e-tests/test-applications/effect-3-browser/.gitignore similarity index 100% rename from dev-packages/e2e-tests/test-applications/effect-browser/.gitignore rename to dev-packages/e2e-tests/test-applications/effect-3-browser/.gitignore diff --git a/dev-packages/e2e-tests/test-applications/effect-browser/.npmrc b/dev-packages/e2e-tests/test-applications/effect-3-browser/.npmrc similarity index 100% rename from dev-packages/e2e-tests/test-applications/effect-browser/.npmrc rename to dev-packages/e2e-tests/test-applications/effect-3-browser/.npmrc diff --git a/dev-packages/e2e-tests/test-applications/effect-browser/build.mjs b/dev-packages/e2e-tests/test-applications/effect-3-browser/build.mjs similarity index 100% rename from dev-packages/e2e-tests/test-applications/effect-browser/build.mjs rename to dev-packages/e2e-tests/test-applications/effect-3-browser/build.mjs diff --git a/dev-packages/e2e-tests/test-applications/effect-browser/package.json b/dev-packages/e2e-tests/test-applications/effect-3-browser/package.json similarity index 96% rename from dev-packages/e2e-tests/test-applications/effect-browser/package.json rename to dev-packages/e2e-tests/test-applications/effect-3-browser/package.json index 6c2e7e63ced8..c8cfbb5b587d 100644 --- a/dev-packages/e2e-tests/test-applications/effect-browser/package.json +++ b/dev-packages/e2e-tests/test-applications/effect-3-browser/package.json @@ -1,5 +1,5 @@ { - "name": "effect-browser-test-app", + "name": "effect-3-browser-test-app", "version": "1.0.0", "private": true, "scripts": { diff --git a/dev-packages/e2e-tests/test-applications/effect-browser/playwright.config.mjs b/dev-packages/e2e-tests/test-applications/effect-3-browser/playwright.config.mjs similarity index 100% rename from dev-packages/e2e-tests/test-applications/effect-browser/playwright.config.mjs rename to dev-packages/e2e-tests/test-applications/effect-3-browser/playwright.config.mjs diff --git a/dev-packages/e2e-tests/test-applications/effect-browser/public/index.html b/dev-packages/e2e-tests/test-applications/effect-3-browser/public/index.html similarity index 100% rename from dev-packages/e2e-tests/test-applications/effect-browser/public/index.html rename to dev-packages/e2e-tests/test-applications/effect-3-browser/public/index.html diff --git a/dev-packages/e2e-tests/test-applications/effect-browser/src/index.js b/dev-packages/e2e-tests/test-applications/effect-3-browser/src/index.js similarity index 100% rename from dev-packages/e2e-tests/test-applications/effect-browser/src/index.js rename to dev-packages/e2e-tests/test-applications/effect-3-browser/src/index.js diff --git a/dev-packages/e2e-tests/test-applications/effect-browser/start-event-proxy.mjs b/dev-packages/e2e-tests/test-applications/effect-3-browser/start-event-proxy.mjs similarity index 74% rename from dev-packages/e2e-tests/test-applications/effect-browser/start-event-proxy.mjs rename to dev-packages/e2e-tests/test-applications/effect-3-browser/start-event-proxy.mjs index a86a1bd91404..6da20fa0890e 100644 --- a/dev-packages/e2e-tests/test-applications/effect-browser/start-event-proxy.mjs +++ b/dev-packages/e2e-tests/test-applications/effect-3-browser/start-event-proxy.mjs @@ -2,5 +2,5 @@ import { startEventProxyServer } from '@sentry-internal/test-utils'; startEventProxyServer({ port: 3031, - proxyServerName: 'effect-browser', + proxyServerName: 'effect-3-browser', }); diff --git a/dev-packages/e2e-tests/test-applications/effect-browser/tests/errors.test.ts b/dev-packages/e2e-tests/test-applications/effect-3-browser/tests/errors.test.ts similarity index 87% rename from dev-packages/e2e-tests/test-applications/effect-browser/tests/errors.test.ts rename to dev-packages/e2e-tests/test-applications/effect-3-browser/tests/errors.test.ts index 80589f683c28..bca922963ee1 100644 --- a/dev-packages/e2e-tests/test-applications/effect-browser/tests/errors.test.ts +++ b/dev-packages/e2e-tests/test-applications/effect-3-browser/tests/errors.test.ts @@ -2,7 +2,7 @@ import { expect, test } from '@playwright/test'; import { waitForError, waitForTransaction } from '@sentry-internal/test-utils'; test('captures an error', async ({ page }) => { - const errorEventPromise = waitForError('effect-browser', event => { + const errorEventPromise = waitForError('effect-3-browser', event => { return !event.type && event.exception?.values?.[0]?.value === 'I am an error!'; }); @@ -29,11 +29,11 @@ test('captures an error', async ({ page }) => { }); test('sets correct transactionName', async ({ page }) => { - const transactionPromise = waitForTransaction('effect-browser', async transactionEvent => { + const transactionPromise = waitForTransaction('effect-3-browser', async transactionEvent => { return !!transactionEvent?.transaction && transactionEvent.contexts?.trace?.op === 'pageload'; }); - const errorEventPromise = waitForError('effect-browser', event => { + const errorEventPromise = waitForError('effect-3-browser', event => { return !event.type && event.exception?.values?.[0]?.value === 'I am an error!'; }); diff --git a/dev-packages/e2e-tests/test-applications/effect-browser/tests/logs.test.ts b/dev-packages/e2e-tests/test-applications/effect-3-browser/tests/logs.test.ts similarity index 90% rename from dev-packages/e2e-tests/test-applications/effect-browser/tests/logs.test.ts rename to dev-packages/e2e-tests/test-applications/effect-3-browser/tests/logs.test.ts index f81bc249cbd8..7857b7f9a156 100644 --- a/dev-packages/e2e-tests/test-applications/effect-browser/tests/logs.test.ts +++ b/dev-packages/e2e-tests/test-applications/effect-3-browser/tests/logs.test.ts @@ -3,7 +3,7 @@ import { waitForEnvelopeItem } from '@sentry-internal/test-utils'; import type { SerializedLogContainer } from '@sentry/core'; test('should send Effect debug logs', async ({ page }) => { - const logEnvelopePromise = waitForEnvelopeItem('effect-browser', envelope => { + const logEnvelopePromise = waitForEnvelopeItem('effect-3-browser', envelope => { return ( envelope[0].type === 'log' && (envelope[1] as SerializedLogContainer).items.some( @@ -26,7 +26,7 @@ test('should send Effect debug logs', async ({ page }) => { }); test('should send Effect info logs', async ({ page }) => { - const logEnvelopePromise = waitForEnvelopeItem('effect-browser', envelope => { + const logEnvelopePromise = waitForEnvelopeItem('effect-3-browser', envelope => { return ( envelope[0].type === 'log' && (envelope[1] as SerializedLogContainer).items.some( @@ -49,7 +49,7 @@ test('should send Effect info logs', async ({ page }) => { }); test('should send Effect warning logs', async ({ page }) => { - const logEnvelopePromise = waitForEnvelopeItem('effect-browser', envelope => { + const logEnvelopePromise = waitForEnvelopeItem('effect-3-browser', envelope => { return ( envelope[0].type === 'log' && (envelope[1] as SerializedLogContainer).items.some( @@ -72,7 +72,7 @@ test('should send Effect warning logs', async ({ page }) => { }); test('should send Effect error logs', async ({ page }) => { - const logEnvelopePromise = waitForEnvelopeItem('effect-browser', envelope => { + const logEnvelopePromise = waitForEnvelopeItem('effect-3-browser', envelope => { return ( envelope[0].type === 'log' && (envelope[1] as SerializedLogContainer).items.some( @@ -95,7 +95,7 @@ test('should send Effect error logs', async ({ page }) => { }); test('should send Effect logs with context attributes', async ({ page }) => { - const logEnvelopePromise = waitForEnvelopeItem('effect-browser', envelope => { + const logEnvelopePromise = waitForEnvelopeItem('effect-3-browser', envelope => { return ( envelope[0].type === 'log' && (envelope[1] as SerializedLogContainer).items.some(item => item.body === 'Log with context') diff --git a/dev-packages/e2e-tests/test-applications/effect-browser/tests/transactions.test.ts b/dev-packages/e2e-tests/test-applications/effect-3-browser/tests/transactions.test.ts similarity index 90% rename from dev-packages/e2e-tests/test-applications/effect-browser/tests/transactions.test.ts rename to dev-packages/e2e-tests/test-applications/effect-3-browser/tests/transactions.test.ts index b7c60b488403..db2a1dc352a8 100644 --- a/dev-packages/e2e-tests/test-applications/effect-browser/tests/transactions.test.ts +++ b/dev-packages/e2e-tests/test-applications/effect-3-browser/tests/transactions.test.ts @@ -2,7 +2,7 @@ import { expect, test } from '@playwright/test'; import { waitForTransaction } from '@sentry-internal/test-utils'; test('captures a pageload transaction', async ({ page }) => { - const transactionPromise = waitForTransaction('effect-browser', async transactionEvent => { + const transactionPromise = waitForTransaction('effect-3-browser', async transactionEvent => { return !!transactionEvent?.transaction && transactionEvent.contexts?.trace?.op === 'pageload'; }); @@ -49,11 +49,11 @@ test('captures a pageload transaction', async ({ page }) => { }); test('captures a navigation transaction', async ({ page }) => { - const pageLoadTransactionPromise = waitForTransaction('effect-browser', async transactionEvent => { + const pageLoadTransactionPromise = waitForTransaction('effect-3-browser', async transactionEvent => { return !!transactionEvent?.transaction && transactionEvent.contexts?.trace?.op === 'pageload'; }); - const navigationTransactionPromise = waitForTransaction('effect-browser', async transactionEvent => { + const navigationTransactionPromise = waitForTransaction('effect-3-browser', async transactionEvent => { return !!transactionEvent?.transaction && transactionEvent.contexts?.trace?.op === 'navigation'; }); @@ -80,11 +80,11 @@ test('captures a navigation transaction', async ({ page }) => { }); test('captures Effect spans with correct parent-child structure', async ({ page }) => { - const pageloadPromise = waitForTransaction('effect-browser', transactionEvent => { + const pageloadPromise = waitForTransaction('effect-3-browser', transactionEvent => { return transactionEvent?.contexts?.trace?.op === 'pageload'; }); - const transactionPromise = waitForTransaction('effect-browser', transactionEvent => { + const transactionPromise = waitForTransaction('effect-3-browser', transactionEvent => { return ( transactionEvent?.contexts?.trace?.op === 'ui.action.click' && transactionEvent.spans?.some(span => span.description === 'custom-effect-span') diff --git a/dev-packages/e2e-tests/test-applications/effect-browser/tsconfig.json b/dev-packages/e2e-tests/test-applications/effect-3-browser/tsconfig.json similarity index 100% rename from dev-packages/e2e-tests/test-applications/effect-browser/tsconfig.json rename to dev-packages/e2e-tests/test-applications/effect-3-browser/tsconfig.json diff --git a/dev-packages/e2e-tests/test-applications/effect-node/.gitignore b/dev-packages/e2e-tests/test-applications/effect-3-node/.gitignore similarity index 100% rename from dev-packages/e2e-tests/test-applications/effect-node/.gitignore rename to dev-packages/e2e-tests/test-applications/effect-3-node/.gitignore diff --git a/dev-packages/e2e-tests/test-applications/effect-node/.npmrc b/dev-packages/e2e-tests/test-applications/effect-3-node/.npmrc similarity index 100% rename from dev-packages/e2e-tests/test-applications/effect-node/.npmrc rename to dev-packages/e2e-tests/test-applications/effect-3-node/.npmrc diff --git a/dev-packages/e2e-tests/test-applications/effect-node/package.json b/dev-packages/e2e-tests/test-applications/effect-3-node/package.json similarity index 95% rename from dev-packages/e2e-tests/test-applications/effect-node/package.json rename to dev-packages/e2e-tests/test-applications/effect-3-node/package.json index 621a017d3020..43f9bee85306 100644 --- a/dev-packages/e2e-tests/test-applications/effect-node/package.json +++ b/dev-packages/e2e-tests/test-applications/effect-3-node/package.json @@ -1,5 +1,5 @@ { - "name": "effect-node-app", + "name": "effect-3-node-app", "version": "1.0.0", "private": true, "type": "module", diff --git a/dev-packages/e2e-tests/test-applications/effect-node/playwright.config.mjs b/dev-packages/e2e-tests/test-applications/effect-3-node/playwright.config.mjs similarity index 100% rename from dev-packages/e2e-tests/test-applications/effect-node/playwright.config.mjs rename to dev-packages/e2e-tests/test-applications/effect-3-node/playwright.config.mjs diff --git a/dev-packages/e2e-tests/test-applications/effect-node/src/app.ts b/dev-packages/e2e-tests/test-applications/effect-3-node/src/app.ts similarity index 100% rename from dev-packages/e2e-tests/test-applications/effect-node/src/app.ts rename to dev-packages/e2e-tests/test-applications/effect-3-node/src/app.ts diff --git a/dev-packages/e2e-tests/test-applications/effect-node/start-event-proxy.mjs b/dev-packages/e2e-tests/test-applications/effect-3-node/start-event-proxy.mjs similarity index 75% rename from dev-packages/e2e-tests/test-applications/effect-node/start-event-proxy.mjs rename to dev-packages/e2e-tests/test-applications/effect-3-node/start-event-proxy.mjs index 41eb647958b7..d74e61dc653b 100644 --- a/dev-packages/e2e-tests/test-applications/effect-node/start-event-proxy.mjs +++ b/dev-packages/e2e-tests/test-applications/effect-3-node/start-event-proxy.mjs @@ -2,5 +2,5 @@ import { startEventProxyServer } from '@sentry-internal/test-utils'; startEventProxyServer({ port: 3031, - proxyServerName: 'effect-node', + proxyServerName: 'effect-3-node', }); diff --git a/dev-packages/e2e-tests/test-applications/effect-node/tests/errors.test.ts b/dev-packages/e2e-tests/test-applications/effect-3-node/tests/errors.test.ts similarity index 86% rename from dev-packages/e2e-tests/test-applications/effect-node/tests/errors.test.ts rename to dev-packages/e2e-tests/test-applications/effect-3-node/tests/errors.test.ts index 3b7da230c0e0..848ffcfb8117 100644 --- a/dev-packages/e2e-tests/test-applications/effect-node/tests/errors.test.ts +++ b/dev-packages/e2e-tests/test-applications/effect-3-node/tests/errors.test.ts @@ -2,7 +2,7 @@ import { expect, test } from '@playwright/test'; import { waitForError } from '@sentry-internal/test-utils'; test('Captures manually reported error', async ({ baseURL }) => { - const errorEventPromise = waitForError('effect-node', event => { + const errorEventPromise = waitForError('effect-3-node', event => { return !event.type && event.exception?.values?.[0]?.value === 'This is an error'; }); @@ -17,7 +17,7 @@ test('Captures manually reported error', async ({ baseURL }) => { }); test('Captures thrown exception', async ({ baseURL }) => { - const errorEventPromise = waitForError('effect-node', event => { + const errorEventPromise = waitForError('effect-3-node', event => { return !event.type && event.exception?.values?.[0]?.value === 'This is an exception with id 123'; }); @@ -30,7 +30,7 @@ test('Captures thrown exception', async ({ baseURL }) => { }); test('Captures Effect.fail as error', async ({ baseURL }) => { - const errorEventPromise = waitForError('effect-node', event => { + const errorEventPromise = waitForError('effect-3-node', event => { return !event.type && event.exception?.values?.[0]?.value === 'Effect failure'; }); @@ -43,7 +43,7 @@ test('Captures Effect.fail as error', async ({ baseURL }) => { }); test('Captures Effect.die as error', async ({ baseURL }) => { - const errorEventPromise = waitForError('effect-node', event => { + const errorEventPromise = waitForError('effect-3-node', event => { return !event.type && event.exception?.values?.[0]?.value?.includes('Effect defect'); }); diff --git a/dev-packages/e2e-tests/test-applications/effect-node/tests/logs.test.ts b/dev-packages/e2e-tests/test-applications/effect-3-node/tests/logs.test.ts similarity index 88% rename from dev-packages/e2e-tests/test-applications/effect-node/tests/logs.test.ts rename to dev-packages/e2e-tests/test-applications/effect-3-node/tests/logs.test.ts index 85f5840e14a8..2519f18722fd 100644 --- a/dev-packages/e2e-tests/test-applications/effect-node/tests/logs.test.ts +++ b/dev-packages/e2e-tests/test-applications/effect-3-node/tests/logs.test.ts @@ -3,7 +3,7 @@ import { waitForEnvelopeItem } from '@sentry-internal/test-utils'; import type { SerializedLogContainer } from '@sentry/core'; test('should send Effect debug logs', async ({ baseURL }) => { - const logEnvelopePromise = waitForEnvelopeItem('effect-node', envelope => { + const logEnvelopePromise = waitForEnvelopeItem('effect-3-node', envelope => { return ( envelope[0].type === 'log' && (envelope[1] as SerializedLogContainer).items.some( @@ -22,7 +22,7 @@ test('should send Effect debug logs', async ({ baseURL }) => { }); test('should send Effect info logs', async ({ baseURL }) => { - const logEnvelopePromise = waitForEnvelopeItem('effect-node', envelope => { + const logEnvelopePromise = waitForEnvelopeItem('effect-3-node', envelope => { return ( envelope[0].type === 'log' && (envelope[1] as SerializedLogContainer).items.some( @@ -41,7 +41,7 @@ test('should send Effect info logs', async ({ baseURL }) => { }); test('should send Effect warning logs', async ({ baseURL }) => { - const logEnvelopePromise = waitForEnvelopeItem('effect-node', envelope => { + const logEnvelopePromise = waitForEnvelopeItem('effect-3-node', envelope => { return ( envelope[0].type === 'log' && (envelope[1] as SerializedLogContainer).items.some( @@ -60,7 +60,7 @@ test('should send Effect warning logs', async ({ baseURL }) => { }); test('should send Effect error logs', async ({ baseURL }) => { - const logEnvelopePromise = waitForEnvelopeItem('effect-node', envelope => { + const logEnvelopePromise = waitForEnvelopeItem('effect-3-node', envelope => { return ( envelope[0].type === 'log' && (envelope[1] as SerializedLogContainer).items.some( @@ -79,7 +79,7 @@ test('should send Effect error logs', async ({ baseURL }) => { }); test('should send Effect logs with context attributes', async ({ baseURL }) => { - const logEnvelopePromise = waitForEnvelopeItem('effect-node', envelope => { + const logEnvelopePromise = waitForEnvelopeItem('effect-3-node', envelope => { return ( envelope[0].type === 'log' && (envelope[1] as SerializedLogContainer).items.some(item => item.body === 'Log with context') diff --git a/dev-packages/e2e-tests/test-applications/effect-node/tests/transactions.test.ts b/dev-packages/e2e-tests/test-applications/effect-3-node/tests/transactions.test.ts similarity index 88% rename from dev-packages/e2e-tests/test-applications/effect-node/tests/transactions.test.ts rename to dev-packages/e2e-tests/test-applications/effect-3-node/tests/transactions.test.ts index ed7a58fa28df..b9693b2af6df 100644 --- a/dev-packages/e2e-tests/test-applications/effect-node/tests/transactions.test.ts +++ b/dev-packages/e2e-tests/test-applications/effect-3-node/tests/transactions.test.ts @@ -2,7 +2,7 @@ import { expect, test } from '@playwright/test'; import { waitForTransaction } from '@sentry-internal/test-utils'; test('Sends an HTTP transaction', async ({ baseURL }) => { - const transactionEventPromise = waitForTransaction('effect-node', transactionEvent => { + const transactionEventPromise = waitForTransaction('effect-3-node', transactionEvent => { return transactionEvent?.transaction === 'http.server GET'; }); @@ -14,7 +14,7 @@ test('Sends an HTTP transaction', async ({ baseURL }) => { }); test('Sends transaction with manual Effect span', async ({ baseURL }) => { - const transactionEventPromise = waitForTransaction('effect-node', transactionEvent => { + const transactionEventPromise = waitForTransaction('effect-3-node', transactionEvent => { return ( transactionEvent?.transaction === 'http.server GET' && transactionEvent?.spans?.some(span => span.description === 'test-span') @@ -36,7 +36,7 @@ test('Sends transaction with manual Effect span', async ({ baseURL }) => { }); test('Sends Effect spans with correct parent-child structure', async ({ baseURL }) => { - const transactionEventPromise = waitForTransaction('effect-node', transactionEvent => { + const transactionEventPromise = waitForTransaction('effect-3-node', transactionEvent => { return ( transactionEvent?.transaction === 'http.server GET' && transactionEvent?.spans?.some(span => span.description === 'custom-effect-span') @@ -87,7 +87,7 @@ test('Sends Effect spans with correct parent-child structure', async ({ baseURL }); test('Sends transaction for error route', async ({ baseURL }) => { - const transactionEventPromise = waitForTransaction('effect-node', transactionEvent => { + const transactionEventPromise = waitForTransaction('effect-3-node', transactionEvent => { return transactionEvent?.transaction === 'http.server GET'; }); diff --git a/dev-packages/e2e-tests/test-applications/effect-node/tsconfig.json b/dev-packages/e2e-tests/test-applications/effect-3-node/tsconfig.json similarity index 100% rename from dev-packages/e2e-tests/test-applications/effect-node/tsconfig.json rename to dev-packages/e2e-tests/test-applications/effect-3-node/tsconfig.json From 57d60125d0fcad466f238fbf4659646e5e4b682f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan=20Peer=20St=C3=B6cklmair?= Date: Tue, 21 Apr 2026 09:44:35 +0200 Subject: [PATCH 18/41] feat(effect): Support v4 beta (#20394) This adds support to Effect v4, but also keeps the compatibility for v3. There is no way that we can unit test against v3, as the `devDependencies` need to use `effect@4` and an updated `@effect/vitest` version, which is not compatible with Effect v3 (this is added in The API for Effect v4 has changed a little, so there are safeguards to detect if it is v3 or v4 and uses the correct API. The good part is that for users nothing changed, so they still can use the same methods in their app as before (ofc, respecting the new Effect v4 API). Before (Effect v3): ```ts const SentryLive = Layer.mergeAll( Sentry.effectLayer({ dsn: '__DSN__', tracesSampleRate: 1.0, enableLogs: true, }), Layer.setTracer(Sentry.SentryEffectTracer), Logger.replace(Logger.defaultLogger, Sentry.SentryEffectLogger), Sentry.SentryEffectMetricsLayer, ); ``` After (Effect v4): ```js const SentryLive = Layer.mergeAll( Sentry.effectLayer({ dsn: '__DSN__', tracesSampleRate: 1.0, enableLogs: true, }), Layer.succeed(Tracer.Tracer, Sentry.SentryEffectTracer), Logger.layer([Sentry.SentryEffectLogger]), Sentry.SentryEffectMetricsLayer, ); ``` Both usages still work and are represented in the E2E tests. --- .../effect-4-browser/.gitignore | 28 +++ .../test-applications/effect-4-browser/.npmrc | 2 + .../effect-4-browser/build.mjs | 52 +++++ .../effect-4-browser/package.json | 43 ++++ .../effect-4-browser/playwright.config.mjs | 7 + .../effect-4-browser/public/index.html | 48 +++++ .../effect-4-browser/src/index.js | 96 +++++++++ .../effect-4-browser/start-event-proxy.mjs | 6 + .../effect-4-browser/tests/errors.test.ts | 56 +++++ .../effect-4-browser/tests/logs.test.ts | 116 ++++++++++ .../tests/transactions.test.ts | 120 +++++++++++ .../effect-4-browser/tsconfig.json | 19 ++ .../effect-4-node/.gitignore | 2 + .../test-applications/effect-4-node/.npmrc | 2 + .../effect-4-node/package.json | 29 +++ .../effect-4-node/playwright.config.mjs | 7 + .../effect-4-node/src/app.ts | 146 +++++++++++++ .../effect-4-node/start-event-proxy.mjs | 6 + .../effect-4-node/tests/errors.test.ts | 56 +++++ .../effect-4-node/tests/logs.test.ts | 96 +++++++++ .../effect-4-node/tests/transactions.test.ts | 99 +++++++++ .../effect-4-node/tsconfig.json | 14 ++ packages/effect/README.md | 48 ++++- packages/effect/package.json | 6 +- packages/effect/src/logger.ts | 24 ++- packages/effect/src/metrics.ts | 202 ++++++++++++------ packages/effect/src/tracer.ts | 106 ++++++++- packages/effect/test/layer.test.ts | 29 ++- packages/effect/test/logger.test.ts | 15 +- packages/effect/test/metrics.test.ts | 171 ++++++--------- packages/effect/test/tracer.test.ts | 45 ++-- yarn.lock | 142 ++++++++++-- 32 files changed, 1586 insertions(+), 252 deletions(-) create mode 100644 dev-packages/e2e-tests/test-applications/effect-4-browser/.gitignore create mode 100644 dev-packages/e2e-tests/test-applications/effect-4-browser/.npmrc create mode 100644 dev-packages/e2e-tests/test-applications/effect-4-browser/build.mjs create mode 100644 dev-packages/e2e-tests/test-applications/effect-4-browser/package.json create mode 100644 dev-packages/e2e-tests/test-applications/effect-4-browser/playwright.config.mjs create mode 100644 dev-packages/e2e-tests/test-applications/effect-4-browser/public/index.html create mode 100644 dev-packages/e2e-tests/test-applications/effect-4-browser/src/index.js create mode 100644 dev-packages/e2e-tests/test-applications/effect-4-browser/start-event-proxy.mjs create mode 100644 dev-packages/e2e-tests/test-applications/effect-4-browser/tests/errors.test.ts create mode 100644 dev-packages/e2e-tests/test-applications/effect-4-browser/tests/logs.test.ts create mode 100644 dev-packages/e2e-tests/test-applications/effect-4-browser/tests/transactions.test.ts create mode 100644 dev-packages/e2e-tests/test-applications/effect-4-browser/tsconfig.json create mode 100644 dev-packages/e2e-tests/test-applications/effect-4-node/.gitignore create mode 100644 dev-packages/e2e-tests/test-applications/effect-4-node/.npmrc create mode 100644 dev-packages/e2e-tests/test-applications/effect-4-node/package.json create mode 100644 dev-packages/e2e-tests/test-applications/effect-4-node/playwright.config.mjs create mode 100644 dev-packages/e2e-tests/test-applications/effect-4-node/src/app.ts create mode 100644 dev-packages/e2e-tests/test-applications/effect-4-node/start-event-proxy.mjs create mode 100644 dev-packages/e2e-tests/test-applications/effect-4-node/tests/errors.test.ts create mode 100644 dev-packages/e2e-tests/test-applications/effect-4-node/tests/logs.test.ts create mode 100644 dev-packages/e2e-tests/test-applications/effect-4-node/tests/transactions.test.ts create mode 100644 dev-packages/e2e-tests/test-applications/effect-4-node/tsconfig.json diff --git a/dev-packages/e2e-tests/test-applications/effect-4-browser/.gitignore b/dev-packages/e2e-tests/test-applications/effect-4-browser/.gitignore new file mode 100644 index 000000000000..bd66327c3b4a --- /dev/null +++ b/dev-packages/e2e-tests/test-applications/effect-4-browser/.gitignore @@ -0,0 +1,28 @@ +# dependencies +/node_modules +/.pnp +.pnp.js + +# testing +/coverage + +# production +/build +/dist + +# misc +.DS_Store +.env.local +.env.development.local +.env.test.local +.env.production.local + +npm-debug.log* +yarn-debug.log* +yarn-error.log* + +/test-results/ +/playwright-report/ +/playwright/.cache/ + +!*.d.ts diff --git a/dev-packages/e2e-tests/test-applications/effect-4-browser/.npmrc b/dev-packages/e2e-tests/test-applications/effect-4-browser/.npmrc new file mode 100644 index 000000000000..070f80f05092 --- /dev/null +++ b/dev-packages/e2e-tests/test-applications/effect-4-browser/.npmrc @@ -0,0 +1,2 @@ +@sentry:registry=http://127.0.0.1:4873 +@sentry-internal:registry=http://127.0.0.1:4873 diff --git a/dev-packages/e2e-tests/test-applications/effect-4-browser/build.mjs b/dev-packages/e2e-tests/test-applications/effect-4-browser/build.mjs new file mode 100644 index 000000000000..63c63597d4fe --- /dev/null +++ b/dev-packages/e2e-tests/test-applications/effect-4-browser/build.mjs @@ -0,0 +1,52 @@ +import * as path from 'path'; +import * as url from 'url'; +import HtmlWebpackPlugin from 'html-webpack-plugin'; +import TerserPlugin from 'terser-webpack-plugin'; +import webpack from 'webpack'; + +const __dirname = path.dirname(url.fileURLToPath(import.meta.url)); + +webpack( + { + entry: path.join(__dirname, 'src/index.js'), + output: { + path: path.join(__dirname, 'build'), + filename: 'app.js', + }, + optimization: { + minimize: true, + minimizer: [new TerserPlugin()], + }, + plugins: [ + new webpack.EnvironmentPlugin(['E2E_TEST_DSN']), + new HtmlWebpackPlugin({ + template: path.join(__dirname, 'public/index.html'), + }), + ], + performance: { + hints: false, + }, + mode: 'production', + }, + (err, stats) => { + if (err) { + console.error(err.stack || err); + if (err.details) { + console.error(err.details); + } + return; + } + + const info = stats.toJson(); + + if (stats.hasErrors()) { + console.error(info.errors); + process.exit(1); + } + + if (stats.hasWarnings()) { + console.warn(info.warnings); + process.exit(1); + } + }, +); diff --git a/dev-packages/e2e-tests/test-applications/effect-4-browser/package.json b/dev-packages/e2e-tests/test-applications/effect-4-browser/package.json new file mode 100644 index 000000000000..4baf797b1019 --- /dev/null +++ b/dev-packages/e2e-tests/test-applications/effect-4-browser/package.json @@ -0,0 +1,43 @@ +{ + "name": "effect-4-browser-test-app", + "version": "1.0.0", + "private": true, + "scripts": { + "start": "serve -s build", + "build": "node build.mjs", + "test": "playwright test", + "clean": "npx rimraf node_modules pnpm-lock.yaml", + "test:build": "pnpm install && pnpm build", + "test:assert": "pnpm test" + }, + "dependencies": { + "@sentry/effect": "latest || *", + "@types/node": "^18.19.1", + "effect": "^4.0.0-beta.50", + "typescript": "~5.0.0" + }, + "devDependencies": { + "@playwright/test": "~1.56.0", + "@sentry-internal/test-utils": "link:../../../test-utils", + "webpack": "^5.91.0", + "serve": "14.0.1", + "terser-webpack-plugin": "^5.3.10", + "html-webpack-plugin": "^5.6.0" + }, + "browserslist": { + "production": [ + ">0.2%", + "not dead", + "not op_mini all" + ], + "development": [ + "last 1 chrome version", + "last 1 firefox version", + "last 1 safari version" + ] + }, + "volta": { + "node": "22.15.0", + "extends": "../../package.json" + } +} diff --git a/dev-packages/e2e-tests/test-applications/effect-4-browser/playwright.config.mjs b/dev-packages/e2e-tests/test-applications/effect-4-browser/playwright.config.mjs new file mode 100644 index 000000000000..31f2b913b58b --- /dev/null +++ b/dev-packages/e2e-tests/test-applications/effect-4-browser/playwright.config.mjs @@ -0,0 +1,7 @@ +import { getPlaywrightConfig } from '@sentry-internal/test-utils'; + +const config = getPlaywrightConfig({ + startCommand: `pnpm start`, +}); + +export default config; diff --git a/dev-packages/e2e-tests/test-applications/effect-4-browser/public/index.html b/dev-packages/e2e-tests/test-applications/effect-4-browser/public/index.html new file mode 100644 index 000000000000..19d5c3d99a2f --- /dev/null +++ b/dev-packages/e2e-tests/test-applications/effect-4-browser/public/index.html @@ -0,0 +1,48 @@ + + + + + + Effect Browser App + + +

Effect Browser E2E Test

+ +
+
+

Error Tests

+ +
+ +
+

Effect Span Tests

+ + +
+ +
+

Effect Failure Tests

+ + +
+ + +
+ +
+

Log Tests

+ + +
+ + +
+ + +
+ + diff --git a/dev-packages/e2e-tests/test-applications/effect-4-browser/src/index.js b/dev-packages/e2e-tests/test-applications/effect-4-browser/src/index.js new file mode 100644 index 000000000000..1748b4200ce1 --- /dev/null +++ b/dev-packages/e2e-tests/test-applications/effect-4-browser/src/index.js @@ -0,0 +1,96 @@ +// @ts-check +import * as Sentry from '@sentry/effect'; +import * as Logger from 'effect/Logger'; +import * as Layer from 'effect/Layer'; +import * as ManagedRuntime from 'effect/ManagedRuntime'; +import * as Tracer from 'effect/Tracer'; +import * as References from 'effect/References'; +import * as Effect from 'effect/Effect'; + +const AppLayer = Layer.mergeAll( + Sentry.effectLayer({ + dsn: process.env.E2E_TEST_DSN, + integrations: [ + Sentry.browserTracingIntegration({ + _experiments: { enableInteractions: true }, + }), + ], + tracesSampleRate: 1.0, + release: 'e2e-test', + environment: 'qa', + tunnel: 'http://localhost:3031', + enableLogs: true, + }), + Logger.layer([Sentry.SentryEffectLogger]), + Layer.succeed(Tracer.Tracer, Sentry.SentryEffectTracer), + Layer.succeed(References.MinimumLogLevel, 'Debug'), +); + +// v4 pattern: ManagedRuntime creates a long-lived runtime from the layer +const runtime = ManagedRuntime.make(AppLayer); + +// Force layer to build immediately (synchronously) so Sentry initializes at page load +Effect.runSync(runtime.contextEffect); + +const runEffect = fn => runtime.runPromise(fn()); + +document.getElementById('exception-button')?.addEventListener('click', () => { + throw new Error('I am an error!'); +}); + +document.getElementById('effect-span-button')?.addEventListener('click', async () => { + await runEffect(() => + Effect.gen(function* () { + yield* Effect.sleep('50 millis'); + yield* Effect.sleep('25 millis').pipe(Effect.withSpan('nested-span')); + }).pipe(Effect.withSpan('custom-effect-span', { kind: 'internal' })), + ); + const el = document.getElementById('effect-span-result'); + if (el) el.textContent = 'Span sent!'; +}); + +document.getElementById('effect-fail-button')?.addEventListener('click', async () => { + try { + await runEffect(() => Effect.fail(new Error('Effect failure'))); + } catch { + const el = document.getElementById('effect-fail-result'); + if (el) el.textContent = 'Effect failed (expected)'; + } +}); + +document.getElementById('effect-die-button')?.addEventListener('click', async () => { + try { + await runEffect(() => Effect.die('Effect defect')); + } catch { + const el = document.getElementById('effect-die-result'); + if (el) el.textContent = 'Effect died (expected)'; + } +}); + +document.getElementById('log-button')?.addEventListener('click', async () => { + await runEffect(() => + Effect.gen(function* () { + yield* Effect.logDebug('Debug log from Effect'); + yield* Effect.logInfo('Info log from Effect'); + yield* Effect.logWarning('Warning log from Effect'); + yield* Effect.logError('Error log from Effect'); + }), + ); + const el = document.getElementById('log-result'); + if (el) el.textContent = 'Logs sent!'; +}); + +document.getElementById('log-context-button')?.addEventListener('click', async () => { + await runEffect(() => + Effect.logInfo('Log with context').pipe( + Effect.annotateLogs('userId', '12345'), + Effect.annotateLogs('action', 'test'), + ), + ); + const el = document.getElementById('log-context-result'); + if (el) el.textContent = 'Log with context sent!'; +}); + +document.getElementById('navigation-link')?.addEventListener('click', () => { + document.getElementById('navigation-target')?.scrollIntoView({ behavior: 'smooth' }); +}); diff --git a/dev-packages/e2e-tests/test-applications/effect-4-browser/start-event-proxy.mjs b/dev-packages/e2e-tests/test-applications/effect-4-browser/start-event-proxy.mjs new file mode 100644 index 000000000000..04374ed614c6 --- /dev/null +++ b/dev-packages/e2e-tests/test-applications/effect-4-browser/start-event-proxy.mjs @@ -0,0 +1,6 @@ +import { startEventProxyServer } from '@sentry-internal/test-utils'; + +startEventProxyServer({ + port: 3031, + proxyServerName: 'effect-4-browser', +}); diff --git a/dev-packages/e2e-tests/test-applications/effect-4-browser/tests/errors.test.ts b/dev-packages/e2e-tests/test-applications/effect-4-browser/tests/errors.test.ts new file mode 100644 index 000000000000..25b5762390ad --- /dev/null +++ b/dev-packages/e2e-tests/test-applications/effect-4-browser/tests/errors.test.ts @@ -0,0 +1,56 @@ +import { expect, test } from '@playwright/test'; +import { waitForError, waitForTransaction } from '@sentry-internal/test-utils'; + +test('captures an error', async ({ page }) => { + const errorEventPromise = waitForError('effect-4-browser', event => { + return !event.type && event.exception?.values?.[0]?.value === 'I am an error!'; + }); + + await page.goto('/'); + + const exceptionButton = page.locator('id=exception-button'); + await exceptionButton.click(); + + const errorEvent = await errorEventPromise; + + expect(errorEvent.exception?.values).toHaveLength(1); + expect(errorEvent.exception?.values?.[0]?.value).toBe('I am an error!'); + expect(errorEvent.transaction).toBe('/'); + + expect(errorEvent.request).toEqual({ + url: 'http://localhost:3030/', + headers: expect.any(Object), + }); + + expect(errorEvent.contexts?.trace).toEqual({ + trace_id: expect.stringMatching(/[a-f0-9]{32}/), + span_id: expect.stringMatching(/[a-f0-9]{16}/), + }); +}); + +test('sets correct transactionName', async ({ page }) => { + const transactionPromise = waitForTransaction('effect-4-browser', async transactionEvent => { + return !!transactionEvent?.transaction && transactionEvent.contexts?.trace?.op === 'pageload'; + }); + + const errorEventPromise = waitForError('effect-4-browser', event => { + return !event.type && event.exception?.values?.[0]?.value === 'I am an error!'; + }); + + await page.goto('/'); + const transactionEvent = await transactionPromise; + + const exceptionButton = page.locator('id=exception-button'); + await exceptionButton.click(); + + const errorEvent = await errorEventPromise; + + expect(errorEvent.exception?.values).toHaveLength(1); + expect(errorEvent.exception?.values?.[0]?.value).toBe('I am an error!'); + expect(errorEvent.transaction).toEqual('/'); + + expect(errorEvent.contexts?.trace).toEqual({ + trace_id: transactionEvent.contexts?.trace?.trace_id, + span_id: expect.not.stringContaining(transactionEvent.contexts?.trace?.span_id || ''), + }); +}); diff --git a/dev-packages/e2e-tests/test-applications/effect-4-browser/tests/logs.test.ts b/dev-packages/e2e-tests/test-applications/effect-4-browser/tests/logs.test.ts new file mode 100644 index 000000000000..1026ed4ceeca --- /dev/null +++ b/dev-packages/e2e-tests/test-applications/effect-4-browser/tests/logs.test.ts @@ -0,0 +1,116 @@ +import { expect, test } from '@playwright/test'; +import { waitForEnvelopeItem } from '@sentry-internal/test-utils'; +import type { SerializedLogContainer } from '@sentry/core'; + +test('should send Effect debug logs', async ({ page }) => { + const logEnvelopePromise = waitForEnvelopeItem('effect-4-browser', envelope => { + return ( + envelope[0].type === 'log' && + (envelope[1] as SerializedLogContainer).items.some( + item => item.level === 'debug' && item.body === 'Debug log from Effect', + ) + ); + }); + + await page.goto('/'); + const logButton = page.locator('id=log-button'); + await logButton.click(); + + await expect(page.locator('id=log-result')).toHaveText('Logs sent!'); + + const logEnvelope = await logEnvelopePromise; + const logs = (logEnvelope[1] as SerializedLogContainer).items; + const debugLog = logs.find(log => log.level === 'debug' && log.body === 'Debug log from Effect'); + expect(debugLog).toBeDefined(); + expect(debugLog?.level).toBe('debug'); +}); + +test('should send Effect info logs', async ({ page }) => { + const logEnvelopePromise = waitForEnvelopeItem('effect-4-browser', envelope => { + return ( + envelope[0].type === 'log' && + (envelope[1] as SerializedLogContainer).items.some( + item => item.level === 'info' && item.body === 'Info log from Effect', + ) + ); + }); + + await page.goto('/'); + const logButton = page.locator('id=log-button'); + await logButton.click(); + + await expect(page.locator('id=log-result')).toHaveText('Logs sent!'); + + const logEnvelope = await logEnvelopePromise; + const logs = (logEnvelope[1] as SerializedLogContainer).items; + const infoLog = logs.find(log => log.level === 'info' && log.body === 'Info log from Effect'); + expect(infoLog).toBeDefined(); + expect(infoLog?.level).toBe('info'); +}); + +test('should send Effect warning logs', async ({ page }) => { + const logEnvelopePromise = waitForEnvelopeItem('effect-4-browser', envelope => { + return ( + envelope[0].type === 'log' && + (envelope[1] as SerializedLogContainer).items.some( + item => item.level === 'warn' && item.body === 'Warning log from Effect', + ) + ); + }); + + await page.goto('/'); + const logButton = page.locator('id=log-button'); + await logButton.click(); + + await expect(page.locator('id=log-result')).toHaveText('Logs sent!'); + + const logEnvelope = await logEnvelopePromise; + const logs = (logEnvelope[1] as SerializedLogContainer).items; + const warnLog = logs.find(log => log.level === 'warn' && log.body === 'Warning log from Effect'); + expect(warnLog).toBeDefined(); + expect(warnLog?.level).toBe('warn'); +}); + +test('should send Effect error logs', async ({ page }) => { + const logEnvelopePromise = waitForEnvelopeItem('effect-4-browser', envelope => { + return ( + envelope[0].type === 'log' && + (envelope[1] as SerializedLogContainer).items.some( + item => item.level === 'error' && item.body === 'Error log from Effect', + ) + ); + }); + + await page.goto('/'); + const logButton = page.locator('id=log-button'); + await logButton.click(); + + await expect(page.locator('id=log-result')).toHaveText('Logs sent!'); + + const logEnvelope = await logEnvelopePromise; + const logs = (logEnvelope[1] as SerializedLogContainer).items; + const errorLog = logs.find(log => log.level === 'error' && log.body === 'Error log from Effect'); + expect(errorLog).toBeDefined(); + expect(errorLog?.level).toBe('error'); +}); + +test('should send Effect logs with context attributes', async ({ page }) => { + const logEnvelopePromise = waitForEnvelopeItem('effect-4-browser', envelope => { + return ( + envelope[0].type === 'log' && + (envelope[1] as SerializedLogContainer).items.some(item => item.body === 'Log with context') + ); + }); + + await page.goto('/'); + const logContextButton = page.locator('id=log-context-button'); + await logContextButton.click(); + + await expect(page.locator('id=log-context-result')).toHaveText('Log with context sent!'); + + const logEnvelope = await logEnvelopePromise; + const logs = (logEnvelope[1] as SerializedLogContainer).items; + const contextLog = logs.find(log => log.body === 'Log with context'); + expect(contextLog).toBeDefined(); + expect(contextLog?.level).toBe('info'); +}); diff --git a/dev-packages/e2e-tests/test-applications/effect-4-browser/tests/transactions.test.ts b/dev-packages/e2e-tests/test-applications/effect-4-browser/tests/transactions.test.ts new file mode 100644 index 000000000000..6bec97ca4d79 --- /dev/null +++ b/dev-packages/e2e-tests/test-applications/effect-4-browser/tests/transactions.test.ts @@ -0,0 +1,120 @@ +import { expect, test } from '@playwright/test'; +import { waitForTransaction } from '@sentry-internal/test-utils'; + +test('captures a pageload transaction', async ({ page }) => { + const transactionPromise = waitForTransaction('effect-4-browser', async transactionEvent => { + return !!transactionEvent?.transaction && transactionEvent.contexts?.trace?.op === 'pageload'; + }); + + await page.goto('/'); + + const pageLoadTransaction = await transactionPromise; + + expect(pageLoadTransaction).toMatchObject({ + contexts: { + trace: { + data: expect.objectContaining({ + 'sentry.idle_span_finish_reason': 'idleTimeout', + 'sentry.op': 'pageload', + 'sentry.origin': 'auto.pageload.browser', + 'sentry.sample_rate': 1, + 'sentry.source': 'url', + }), + op: 'pageload', + origin: 'auto.pageload.browser', + span_id: expect.stringMatching(/[a-f0-9]{16}/), + trace_id: expect.stringMatching(/[a-f0-9]{32}/), + }, + }, + environment: 'qa', + event_id: expect.stringMatching(/[a-f0-9]{32}/), + measurements: expect.any(Object), + platform: 'javascript', + release: 'e2e-test', + request: { + headers: { + 'User-Agent': expect.any(String), + }, + url: 'http://localhost:3030/', + }, + spans: expect.any(Array), + start_timestamp: expect.any(Number), + timestamp: expect.any(Number), + transaction: '/', + transaction_info: { + source: 'url', + }, + type: 'transaction', + }); +}); + +test('captures a navigation transaction', async ({ page }) => { + const pageLoadTransactionPromise = waitForTransaction('effect-4-browser', async transactionEvent => { + return !!transactionEvent?.transaction && transactionEvent.contexts?.trace?.op === 'pageload'; + }); + + const navigationTransactionPromise = waitForTransaction('effect-4-browser', async transactionEvent => { + return !!transactionEvent?.transaction && transactionEvent.contexts?.trace?.op === 'navigation'; + }); + + await page.goto('/'); + await pageLoadTransactionPromise; + + const linkElement = page.locator('id=navigation-link'); + await linkElement.click(); + + const navigationTransaction = await navigationTransactionPromise; + + expect(navigationTransaction).toMatchObject({ + contexts: { + trace: { + op: 'navigation', + origin: 'auto.navigation.browser', + }, + }, + transaction: '/', + transaction_info: { + source: 'url', + }, + }); +}); + +test('captures Effect spans with correct parent-child structure', async ({ page }) => { + const pageloadPromise = waitForTransaction('effect-4-browser', transactionEvent => { + return transactionEvent?.contexts?.trace?.op === 'pageload'; + }); + + const transactionPromise = waitForTransaction('effect-4-browser', transactionEvent => { + return ( + transactionEvent?.contexts?.trace?.op === 'ui.action.click' && + transactionEvent.spans?.some(span => span.description === 'custom-effect-span') + ); + }); + + await page.goto('/'); + await pageloadPromise; + + const effectSpanButton = page.locator('id=effect-span-button'); + await effectSpanButton.click(); + + await expect(page.locator('id=effect-span-result')).toHaveText('Span sent!'); + + const transactionEvent = await transactionPromise; + const spans = transactionEvent.spans || []; + + expect(spans).toContainEqual( + expect.objectContaining({ + description: 'custom-effect-span', + }), + ); + + expect(spans).toContainEqual( + expect.objectContaining({ + description: 'nested-span', + }), + ); + + const parentSpan = spans.find(s => s.description === 'custom-effect-span'); + const nestedSpan = spans.find(s => s.description === 'nested-span'); + expect(nestedSpan?.parent_span_id).toBe(parentSpan?.span_id); +}); diff --git a/dev-packages/e2e-tests/test-applications/effect-4-browser/tsconfig.json b/dev-packages/e2e-tests/test-applications/effect-4-browser/tsconfig.json new file mode 100644 index 000000000000..cb69f25b8d50 --- /dev/null +++ b/dev-packages/e2e-tests/test-applications/effect-4-browser/tsconfig.json @@ -0,0 +1,19 @@ +{ + "compilerOptions": { + "target": "es2018", + "lib": ["dom", "dom.iterable", "esnext"], + "allowJs": true, + "skipLibCheck": true, + "esModuleInterop": true, + "allowSyntheticDefaultImports": true, + "strict": true, + "forceConsistentCasingInFileNames": true, + "noFallthroughCasesInSwitch": true, + "module": "esnext", + "moduleResolution": "node", + "resolveJsonModule": true, + "isolatedModules": true, + "noEmit": true + }, + "include": ["src", "tests"] +} diff --git a/dev-packages/e2e-tests/test-applications/effect-4-node/.gitignore b/dev-packages/e2e-tests/test-applications/effect-4-node/.gitignore new file mode 100644 index 000000000000..f06235c460c2 --- /dev/null +++ b/dev-packages/e2e-tests/test-applications/effect-4-node/.gitignore @@ -0,0 +1,2 @@ +node_modules +dist diff --git a/dev-packages/e2e-tests/test-applications/effect-4-node/.npmrc b/dev-packages/e2e-tests/test-applications/effect-4-node/.npmrc new file mode 100644 index 000000000000..070f80f05092 --- /dev/null +++ b/dev-packages/e2e-tests/test-applications/effect-4-node/.npmrc @@ -0,0 +1,2 @@ +@sentry:registry=http://127.0.0.1:4873 +@sentry-internal:registry=http://127.0.0.1:4873 diff --git a/dev-packages/e2e-tests/test-applications/effect-4-node/package.json b/dev-packages/e2e-tests/test-applications/effect-4-node/package.json new file mode 100644 index 000000000000..31ebb8b1ba53 --- /dev/null +++ b/dev-packages/e2e-tests/test-applications/effect-4-node/package.json @@ -0,0 +1,29 @@ +{ + "name": "effect-4-node-app", + "version": "1.0.0", + "private": true, + "type": "module", + "scripts": { + "build": "tsc", + "start": "node dist/app.js", + "test": "playwright test", + "clean": "npx rimraf node_modules pnpm-lock.yaml", + "test:build": "pnpm install && pnpm build", + "test:assert": "pnpm test" + }, + "dependencies": { + "@effect/platform-node": "^4.0.0-beta.50", + "@sentry/effect": "latest || *", + "@types/node": "^18.19.1", + "effect": "^4.0.0-beta.50", + "typescript": "~5.0.0" + }, + "devDependencies": { + "@playwright/test": "~1.56.0", + "@sentry-internal/test-utils": "link:../../../test-utils" + }, + "volta": { + "node": "22.15.0", + "extends": "../../package.json" + } +} diff --git a/dev-packages/e2e-tests/test-applications/effect-4-node/playwright.config.mjs b/dev-packages/e2e-tests/test-applications/effect-4-node/playwright.config.mjs new file mode 100644 index 000000000000..31f2b913b58b --- /dev/null +++ b/dev-packages/e2e-tests/test-applications/effect-4-node/playwright.config.mjs @@ -0,0 +1,7 @@ +import { getPlaywrightConfig } from '@sentry-internal/test-utils'; + +const config = getPlaywrightConfig({ + startCommand: `pnpm start`, +}); + +export default config; diff --git a/dev-packages/e2e-tests/test-applications/effect-4-node/src/app.ts b/dev-packages/e2e-tests/test-applications/effect-4-node/src/app.ts new file mode 100644 index 000000000000..5ebfef33be77 --- /dev/null +++ b/dev-packages/e2e-tests/test-applications/effect-4-node/src/app.ts @@ -0,0 +1,146 @@ +import * as Sentry from '@sentry/effect'; +import { NodeHttpServer, NodeRuntime } from '@effect/platform-node'; +import * as Effect from 'effect/Effect'; +import * as Cause from 'effect/Cause'; +import * as Layer from 'effect/Layer'; +import * as Logger from 'effect/Logger'; +import * as Tracer from 'effect/Tracer'; +import * as References from 'effect/References'; +import { HttpRouter, HttpServerResponse } from 'effect/unstable/http'; +import { createServer } from 'http'; + +const SentryLive = Layer.mergeAll( + Sentry.effectLayer({ + dsn: process.env.E2E_TEST_DSN, + environment: 'qa', + debug: !!process.env.DEBUG, + tunnel: 'http://localhost:3031/', + tracesSampleRate: 1, + enableLogs: true, + }), + Logger.layer([Sentry.SentryEffectLogger]), + Layer.succeed(Tracer.Tracer, Sentry.SentryEffectTracer), + Layer.succeed(References.MinimumLogLevel, 'Debug'), +); + +const Routes = Layer.mergeAll( + HttpRouter.add('GET', '/test-success', HttpServerResponse.json({ version: 'v1' })), + + HttpRouter.add( + 'GET', + '/test-transaction', + Effect.gen(function* () { + yield* Effect.void.pipe(Effect.withSpan('test-span')); + return yield* HttpServerResponse.json({ status: 'ok' }); + }), + ), + + HttpRouter.add( + 'GET', + '/test-effect-span', + Effect.gen(function* () { + yield* Effect.gen(function* () { + yield* Effect.sleep('50 millis'); + yield* Effect.sleep('25 millis').pipe(Effect.withSpan('nested-span')); + }).pipe(Effect.withSpan('custom-effect-span', { kind: 'internal' })); + return yield* HttpServerResponse.json({ status: 'ok' }); + }), + ), + + HttpRouter.add( + 'GET', + '/test-error', + Effect.gen(function* () { + const exceptionId = Sentry.captureException(new Error('This is an error')); + yield* Effect.promise(() => Sentry.flush(2000)); + return yield* HttpServerResponse.json({ exceptionId }); + }), + ), + + HttpRouter.add( + 'GET', + '/test-exception/:id', + Effect.gen(function* () { + yield* Effect.sync(() => { + throw new Error('This is an exception with id 123'); + }); + return HttpServerResponse.empty(); + }).pipe( + Effect.catchCause(cause => { + const error = Cause.squash(cause); + Sentry.captureException(error); + return Effect.gen(function* () { + yield* Effect.promise(() => Sentry.flush(2000)); + return yield* HttpServerResponse.json({ error: String(error) }, { status: 500 }); + }); + }), + ), + ), + + HttpRouter.add( + 'GET', + '/test-effect-fail', + Effect.gen(function* () { + yield* Effect.fail(new Error('Effect failure')); + return HttpServerResponse.empty(); + }).pipe( + Effect.catchCause(cause => { + const error = Cause.squash(cause); + Sentry.captureException(error); + return Effect.gen(function* () { + yield* Effect.promise(() => Sentry.flush(2000)); + return yield* HttpServerResponse.json({ error: String(error) }, { status: 500 }); + }); + }), + ), + ), + + HttpRouter.add( + 'GET', + '/test-effect-die', + Effect.gen(function* () { + yield* Effect.die('Effect defect'); + return HttpServerResponse.empty(); + }).pipe( + Effect.catchCause(cause => { + const error = Cause.squash(cause); + Sentry.captureException(error); + return Effect.gen(function* () { + yield* Effect.promise(() => Sentry.flush(2000)); + return yield* HttpServerResponse.json({ error: String(error) }, { status: 500 }); + }); + }), + ), + ), + + HttpRouter.add( + 'GET', + '/test-log', + Effect.gen(function* () { + yield* Effect.logDebug('Debug log from Effect'); + yield* Effect.logInfo('Info log from Effect'); + yield* Effect.logWarning('Warning log from Effect'); + yield* Effect.logError('Error log from Effect'); + return yield* HttpServerResponse.json({ message: 'Logs sent' }); + }), + ), + + HttpRouter.add( + 'GET', + '/test-log-with-context', + Effect.gen(function* () { + yield* Effect.logInfo('Log with context').pipe( + Effect.annotateLogs('userId', '12345'), + Effect.annotateLogs('action', 'test'), + ); + return yield* HttpServerResponse.json({ message: 'Log with context sent' }); + }), + ), +); + +const HttpLive = HttpRouter.serve(Routes).pipe( + Layer.provide(NodeHttpServer.layer(() => createServer(), { port: 3030 })), + Layer.provide(SentryLive), +); + +NodeRuntime.runMain(Layer.launch(HttpLive)); diff --git a/dev-packages/e2e-tests/test-applications/effect-4-node/start-event-proxy.mjs b/dev-packages/e2e-tests/test-applications/effect-4-node/start-event-proxy.mjs new file mode 100644 index 000000000000..6874b711993a --- /dev/null +++ b/dev-packages/e2e-tests/test-applications/effect-4-node/start-event-proxy.mjs @@ -0,0 +1,6 @@ +import { startEventProxyServer } from '@sentry-internal/test-utils'; + +startEventProxyServer({ + port: 3031, + proxyServerName: 'effect-4-node', +}); diff --git a/dev-packages/e2e-tests/test-applications/effect-4-node/tests/errors.test.ts b/dev-packages/e2e-tests/test-applications/effect-4-node/tests/errors.test.ts new file mode 100644 index 000000000000..f4d01534e60f --- /dev/null +++ b/dev-packages/e2e-tests/test-applications/effect-4-node/tests/errors.test.ts @@ -0,0 +1,56 @@ +import { expect, test } from '@playwright/test'; +import { waitForError } from '@sentry-internal/test-utils'; + +test('Captures manually reported error', async ({ baseURL }) => { + const errorEventPromise = waitForError('effect-4-node', event => { + return !event.type && event.exception?.values?.[0]?.value === 'This is an error'; + }); + + const response = await fetch(`${baseURL}/test-error`); + const body = await response.json(); + + const errorEvent = await errorEventPromise; + + expect(body.exceptionId).toBeDefined(); + expect(errorEvent.exception?.values).toHaveLength(1); + expect(errorEvent.exception?.values?.[0]?.value).toBe('This is an error'); +}); + +test('Captures thrown exception', async ({ baseURL }) => { + const errorEventPromise = waitForError('effect-4-node', event => { + return !event.type && event.exception?.values?.[0]?.value === 'This is an exception with id 123'; + }); + + await fetch(`${baseURL}/test-exception/123`); + + const errorEvent = await errorEventPromise; + + expect(errorEvent.exception?.values).toHaveLength(1); + expect(errorEvent.exception?.values?.[0]?.value).toBe('This is an exception with id 123'); +}); + +test('Captures Effect.fail as error', async ({ baseURL }) => { + const errorEventPromise = waitForError('effect-4-node', event => { + return !event.type && event.exception?.values?.[0]?.value === 'Effect failure'; + }); + + await fetch(`${baseURL}/test-effect-fail`); + + const errorEvent = await errorEventPromise; + + expect(errorEvent.exception?.values).toHaveLength(1); + expect(errorEvent.exception?.values?.[0]?.value).toBe('Effect failure'); +}); + +test('Captures Effect.die as error', async ({ baseURL }) => { + const errorEventPromise = waitForError('effect-4-node', event => { + return !event.type && event.exception?.values?.[0]?.value?.includes('Effect defect'); + }); + + await fetch(`${baseURL}/test-effect-die`); + + const errorEvent = await errorEventPromise; + + expect(errorEvent.exception?.values).toHaveLength(1); + expect(errorEvent.exception?.values?.[0]?.value).toContain('Effect defect'); +}); diff --git a/dev-packages/e2e-tests/test-applications/effect-4-node/tests/logs.test.ts b/dev-packages/e2e-tests/test-applications/effect-4-node/tests/logs.test.ts new file mode 100644 index 000000000000..f7563576ad75 --- /dev/null +++ b/dev-packages/e2e-tests/test-applications/effect-4-node/tests/logs.test.ts @@ -0,0 +1,96 @@ +import { expect, test } from '@playwright/test'; +import { waitForEnvelopeItem } from '@sentry-internal/test-utils'; +import type { SerializedLogContainer } from '@sentry/core'; + +test('should send Effect debug logs', async ({ baseURL }) => { + const logEnvelopePromise = waitForEnvelopeItem('effect-4-node', envelope => { + return ( + envelope[0].type === 'log' && + (envelope[1] as SerializedLogContainer).items.some( + item => item.level === 'debug' && item.body === 'Debug log from Effect', + ) + ); + }); + + await fetch(`${baseURL}/test-log`); + + const logEnvelope = await logEnvelopePromise; + const logs = (logEnvelope[1] as SerializedLogContainer).items; + const debugLog = logs.find(log => log.level === 'debug' && log.body === 'Debug log from Effect'); + expect(debugLog).toBeDefined(); + expect(debugLog?.level).toBe('debug'); +}); + +test('should send Effect info logs', async ({ baseURL }) => { + const logEnvelopePromise = waitForEnvelopeItem('effect-4-node', envelope => { + return ( + envelope[0].type === 'log' && + (envelope[1] as SerializedLogContainer).items.some( + item => item.level === 'info' && item.body === 'Info log from Effect', + ) + ); + }); + + await fetch(`${baseURL}/test-log`); + + const logEnvelope = await logEnvelopePromise; + const logs = (logEnvelope[1] as SerializedLogContainer).items; + const infoLog = logs.find(log => log.level === 'info' && log.body === 'Info log from Effect'); + expect(infoLog).toBeDefined(); + expect(infoLog?.level).toBe('info'); +}); + +test('should send Effect warning logs', async ({ baseURL }) => { + const logEnvelopePromise = waitForEnvelopeItem('effect-4-node', envelope => { + return ( + envelope[0].type === 'log' && + (envelope[1] as SerializedLogContainer).items.some( + item => item.level === 'warn' && item.body === 'Warning log from Effect', + ) + ); + }); + + await fetch(`${baseURL}/test-log`); + + const logEnvelope = await logEnvelopePromise; + const logs = (logEnvelope[1] as SerializedLogContainer).items; + const warnLog = logs.find(log => log.level === 'warn' && log.body === 'Warning log from Effect'); + expect(warnLog).toBeDefined(); + expect(warnLog?.level).toBe('warn'); +}); + +test('should send Effect error logs', async ({ baseURL }) => { + const logEnvelopePromise = waitForEnvelopeItem('effect-4-node', envelope => { + return ( + envelope[0].type === 'log' && + (envelope[1] as SerializedLogContainer).items.some( + item => item.level === 'error' && item.body === 'Error log from Effect', + ) + ); + }); + + await fetch(`${baseURL}/test-log`); + + const logEnvelope = await logEnvelopePromise; + const logs = (logEnvelope[1] as SerializedLogContainer).items; + const errorLog = logs.find(log => log.level === 'error' && log.body === 'Error log from Effect'); + expect(errorLog).toBeDefined(); + expect(errorLog?.level).toBe('error'); +}); + +test('should send Effect logs with context attributes', async ({ baseURL }) => { + const logEnvelopePromise = waitForEnvelopeItem('effect-4-node', envelope => { + return ( + envelope[0].type === 'log' && + (envelope[1] as SerializedLogContainer).items.some(item => item.body === 'Log with context') + ); + }); + + await fetch(`${baseURL}/test-log-with-context`); + + const logEnvelope = await logEnvelopePromise; + const logs = (logEnvelope[1] as SerializedLogContainer).items; + const contextLog = logs.find(log => log.body === 'Log with context'); + expect(contextLog).toBeDefined(); + expect(contextLog?.level).toBe('info'); +}); diff --git a/dev-packages/e2e-tests/test-applications/effect-4-node/tests/transactions.test.ts b/dev-packages/e2e-tests/test-applications/effect-4-node/tests/transactions.test.ts new file mode 100644 index 000000000000..5aeaf9b2a8ba --- /dev/null +++ b/dev-packages/e2e-tests/test-applications/effect-4-node/tests/transactions.test.ts @@ -0,0 +1,99 @@ +import { expect, test } from '@playwright/test'; +import { waitForTransaction } from '@sentry-internal/test-utils'; + +test('Sends an HTTP transaction', async ({ baseURL }) => { + const transactionEventPromise = waitForTransaction('effect-4-node', transactionEvent => { + return transactionEvent?.transaction === 'http.server GET'; + }); + + await fetch(`${baseURL}/test-success`); + + const transactionEvent = await transactionEventPromise; + + expect(transactionEvent.transaction).toBe('http.server GET'); +}); + +test('Sends transaction with manual Effect span', async ({ baseURL }) => { + const transactionEventPromise = waitForTransaction('effect-4-node', transactionEvent => { + return ( + transactionEvent?.transaction === 'http.server GET' && + transactionEvent?.spans?.some(span => span.description === 'test-span') + ); + }); + + await fetch(`${baseURL}/test-transaction`); + + const transactionEvent = await transactionEventPromise; + + expect(transactionEvent.transaction).toBe('http.server GET'); + + const spans = transactionEvent.spans || []; + expect(spans).toEqual([ + expect.objectContaining({ + description: 'test-span', + }), + ]); +}); + +test('Sends Effect spans with correct parent-child structure', async ({ baseURL }) => { + const transactionEventPromise = waitForTransaction('effect-4-node', transactionEvent => { + return ( + transactionEvent?.transaction === 'http.server GET' && + transactionEvent?.spans?.some(span => span.description === 'custom-effect-span') + ); + }); + + await fetch(`${baseURL}/test-effect-span`); + + const transactionEvent = await transactionEventPromise; + + expect(transactionEvent.transaction).toBe('http.server GET'); + + expect(transactionEvent).toEqual( + expect.objectContaining({ + contexts: expect.objectContaining({ + trace: expect.objectContaining({ + origin: 'auto.http.effect', + }), + }), + spans: [ + expect.objectContaining({ + description: 'custom-effect-span', + origin: 'auto.function.effect', + }), + expect.objectContaining({ + description: 'nested-span', + origin: 'auto.function.effect', + }), + ], + sdk: expect.objectContaining({ + name: 'sentry.javascript.effect', + packages: [ + expect.objectContaining({ + name: 'npm:@sentry/effect', + }), + expect.objectContaining({ + name: 'npm:@sentry/node-light', + }), + ], + }), + }), + ); + + const parentSpan = transactionEvent.spans?.[0]?.span_id; + const nestedSpan = transactionEvent.spans?.[1]?.parent_span_id; + + expect(nestedSpan).toBe(parentSpan); +}); + +test('Sends transaction for error route', async ({ baseURL }) => { + const transactionEventPromise = waitForTransaction('effect-4-node', transactionEvent => { + return transactionEvent?.transaction === 'http.server GET'; + }); + + await fetch(`${baseURL}/test-error`); + + const transactionEvent = await transactionEventPromise; + + expect(transactionEvent.transaction).toBe('http.server GET'); +}); diff --git a/dev-packages/e2e-tests/test-applications/effect-4-node/tsconfig.json b/dev-packages/e2e-tests/test-applications/effect-4-node/tsconfig.json new file mode 100644 index 000000000000..2cc9aca23e0e --- /dev/null +++ b/dev-packages/e2e-tests/test-applications/effect-4-node/tsconfig.json @@ -0,0 +1,14 @@ +{ + "compilerOptions": { + "target": "ES2022", + "module": "NodeNext", + "moduleResolution": "NodeNext", + "outDir": "dist", + "rootDir": "src", + "strict": true, + "esModuleInterop": true, + "skipLibCheck": true, + "declaration": false + }, + "include": ["src"] +} diff --git a/packages/effect/README.md b/packages/effect/README.md index 78b2f6471dc0..bfe3c51ce8dc 100644 --- a/packages/effect/README.md +++ b/packages/effect/README.md @@ -6,11 +6,16 @@ > NOTICE: This package is in alpha state and may be subject to breaking changes. +`@sentry/effect` supports both Effect v3 and Effect v4 (beta). The integration +auto-detects the installed Effect version at runtime, but the layer composition +APIs differ between the two major versions, so the setup code is slightly +different. + ## Getting Started This SDK does not have docs yet. Stay tuned. -## Usage +## Usage with Effect v3 ```typescript import * as Sentry from '@sentry/effect/server'; @@ -33,16 +38,45 @@ const MainLive = HttpLive.pipe(Layer.provide(SentryLive)); MainLive.pipe(Layer.launch, NodeRuntime.runMain); ``` -The `effectLayer` function initializes Sentry. To enable Effect instrumentation, compose with: +## Usage with Effect v4 -- `Layer.setTracer(Sentry.SentryEffectTracer)` - Effect spans traced as Sentry spans -- `Logger.replace(Logger.defaultLogger, Sentry.SentryEffectLogger)` - Effect logs forwarded to Sentry -- `Sentry.SentryEffectMetricsLayer` - Effect metrics sent to Sentry +Effect v4 reorganized the `Tracer` and `Logger` layer APIs, so the wiring looks +slightly different. The `effectLayer`, `SentryEffectTracer`, +`SentryEffectLogger`, and `SentryEffectMetricsLayer` exports themselves are the +same. -## Links +```typescript +import * as Sentry from '@sentry/effect/server'; +import { NodeHttpServer, NodeRuntime } from '@effect/platform-node'; +import * as Layer from 'effect/Layer'; +import * as Logger from 'effect/Logger'; +import * as Tracer from 'effect/Tracer'; +import { HttpRouter } from 'effect/unstable/http'; +import { createServer } from 'http'; +import { Routes } from './Routes.js'; + +const SentryLive = Layer.mergeAll( + Sentry.effectLayer({ + dsn: '__DSN__', + tracesSampleRate: 1.0, + enableLogs: true, + }), + Layer.succeed(Tracer.Tracer, Sentry.SentryEffectTracer), + Logger.layer([Sentry.SentryEffectLogger]), + Sentry.SentryEffectMetricsLayer, +); - +const HttpLive = HttpRouter.serve(Routes).pipe( + Layer.provide(NodeHttpServer.layer(() => createServer(), { port: 3030 })), + Layer.provide(SentryLive), +); + +NodeRuntime.runMain(Layer.launch(HttpLive)); +``` + +## Links +- [Official SDK Docs](https://docs.sentry.io/platforms/javascript/guides/effect/) - [Sentry.io](https://sentry.io/?utm_source=github&utm_medium=npm_effect) - [Sentry Discord Server](https://discord.gg/Ww9hbqr) - [Stack Overflow](https://stackoverflow.com/questions/tagged/sentry) diff --git a/packages/effect/package.json b/packages/effect/package.json index 412f884eca1a..669630577640 100644 --- a/packages/effect/package.json +++ b/packages/effect/package.json @@ -62,7 +62,7 @@ "@sentry/node-core": "10.49.0" }, "peerDependencies": { - "effect": "^3.0.0" + "effect": "^3.0.0 || ^4.0.0-beta.50" }, "peerDependenciesMeta": { "effect": { @@ -70,8 +70,8 @@ } }, "devDependencies": { - "@effect/vitest": "^0.23.9", - "effect": "^3.21.0" + "@effect/vitest": "^4.0.0-beta.50", + "effect": "^4.0.0-beta.50" }, "scripts": { "build": "run-p build:transpile build:types", diff --git a/packages/effect/src/logger.ts b/packages/effect/src/logger.ts index 833f5b6b7e95..7421bebc0dbe 100644 --- a/packages/effect/src/logger.ts +++ b/packages/effect/src/logger.ts @@ -1,5 +1,20 @@ import { logger as sentryLogger } from '@sentry/core'; import * as Logger from 'effect/Logger'; +import type * as LogLevel from 'effect/LogLevel'; + +function getLogLevelTag(logLevel: LogLevel.LogLevel): LogLevel.LogLevel | 'Warning' { + // Effect v4: logLevel is a string literal directly + if (typeof logLevel === 'string') { + return logLevel; + } + + // Effect v3: logLevel has _tag property + if (logLevel && typeof logLevel === 'object' && '_tag' in logLevel) { + return (logLevel as { _tag: LogLevel.LogLevel })._tag; + } + + return 'Info'; +} /** * Effect Logger that sends logs to Sentry. @@ -15,14 +30,17 @@ export const SentryEffectLogger = Logger.make(({ logLevel, message }) => { msg = JSON.stringify(message); } - switch (logLevel._tag) { + const tag = getLogLevelTag(logLevel); + + switch (tag) { case 'Fatal': sentryLogger.fatal(msg); break; case 'Error': sentryLogger.error(msg); break; - case 'Warning': + case 'Warning': // Effect v3 + case 'Warn': // Effect v4 sentryLogger.warn(msg); break; case 'Info': @@ -38,6 +56,6 @@ export const SentryEffectLogger = Logger.make(({ logLevel, message }) => { case 'None': break; default: - logLevel satisfies never; + tag satisfies never; } }); diff --git a/packages/effect/src/metrics.ts b/packages/effect/src/metrics.ts index 82daf5e67a5d..764149009be5 100644 --- a/packages/effect/src/metrics.ts +++ b/packages/effect/src/metrics.ts @@ -1,66 +1,75 @@ import { metrics as sentryMetrics } from '@sentry/core'; +import * as Context from 'effect/Context'; import * as Effect from 'effect/Effect'; -import type * as Layer from 'effect/Layer'; -import { scopedDiscard } from 'effect/Layer'; +import * as Layer from 'effect/Layer'; import * as Metric from 'effect/Metric'; -import * as MetricKeyType from 'effect/MetricKeyType'; -import type * as MetricPair from 'effect/MetricPair'; -import * as MetricState from 'effect/MetricState'; import * as Schedule from 'effect/Schedule'; type MetricAttributes = Record; -function labelsToAttributes(labels: ReadonlyArray<{ key: string; value: string }>): MetricAttributes { - return labels.reduce((acc, label) => ({ ...acc, [label.key]: label.value }), {}); +// ============================================================================= +// Effect v3 Types (vendored - not exported from effect@3.x) +// ============================================================================= + +interface V3MetricLabel { + key: string; + value: string; } -function sendMetricToSentry(pair: MetricPair.MetricPair.Untyped): void { - const { metricKey, metricState } = pair; - const name = metricKey.name; - const attributes = labelsToAttributes(metricKey.tags); +interface V3MetricPair { + metricKey: { + name: string; + tags: ReadonlyArray; + keyType: { _tag: string }; + }; + metricState: { + count?: number | bigint; + value?: number; + sum?: number; + min?: number; + max?: number; + occurrences?: Map; + }; +} - if (MetricState.isCounterState(metricState)) { - const value = Number(metricState.count); - sentryMetrics.count(name, value, { attributes }); - } else if (MetricState.isGaugeState(metricState)) { - const value = Number(metricState.value); - sentryMetrics.gauge(name, value, { attributes }); - } else if (MetricState.isHistogramState(metricState)) { - sentryMetrics.gauge(`${name}.sum`, metricState.sum, { attributes }); - sentryMetrics.gauge(`${name}.count`, metricState.count, { attributes }); - sentryMetrics.gauge(`${name}.min`, metricState.min, { attributes }); - sentryMetrics.gauge(`${name}.max`, metricState.max, { attributes }); - } else if (MetricState.isSummaryState(metricState)) { - sentryMetrics.gauge(`${name}.sum`, metricState.sum, { attributes }); - sentryMetrics.gauge(`${name}.count`, metricState.count, { attributes }); - sentryMetrics.gauge(`${name}.min`, metricState.min, { attributes }); - sentryMetrics.gauge(`${name}.max`, metricState.max, { attributes }); - } else if (MetricState.isFrequencyState(metricState)) { - for (const [word, count] of metricState.occurrences) { - sentryMetrics.count(name, count, { - attributes: { ...attributes, word }, - }); - } - } +// Effect v3 `MetricState` implementations brand themselves with a `Symbol.for(...)` TypeId +// rather than a string `_tag`. We use these globally-registered symbols to classify state +// instances returned by `Metric.unsafeSnapshot()` without importing `effect/MetricState` +// (the module does not exist in Effect v4). +const V3_COUNTER_STATE_TYPE_ID = Symbol.for('effect/MetricState/Counter'); +const V3_GAUGE_STATE_TYPE_ID = Symbol.for('effect/MetricState/Gauge'); +const V3_HISTOGRAM_STATE_TYPE_ID = Symbol.for('effect/MetricState/Histogram'); +const V3_SUMMARY_STATE_TYPE_ID = Symbol.for('effect/MetricState/Summary'); +const V3_FREQUENCY_STATE_TYPE_ID = Symbol.for('effect/MetricState/Frequency'); + +function labelsToAttributes(labels: ReadonlyArray): MetricAttributes { + return labels.reduce((acc, label) => ({ ...acc, [label.key]: label.value }), {}); } -function getMetricId(pair: MetricPair.MetricPair.Untyped): string { +function getMetricIdV3(pair: V3MetricPair): string { const tags = pair.metricKey.tags.map(t => `${t.key}=${t.value}`).join(','); return `${pair.metricKey.name}:${tags}`; } -function sendDeltaMetricToSentry( - pair: MetricPair.MetricPair.Untyped, - previousCounterValues: Map, -): void { +function getMetricIdV4(snapshot: Metric.Metric.Snapshot): string { + const attrs = snapshot.attributes + ? Object.entries(snapshot.attributes) + .map(([k, v]) => `${k}=${v}`) + .join(',') + : ''; + return `${snapshot.id}:${attrs}`; +} + +function sendV3MetricToSentry(pair: V3MetricPair, previousCounterValues: Map): void { const { metricKey, metricState } = pair; const name = metricKey.name; const attributes = labelsToAttributes(metricKey.tags); - const metricId = getMetricId(pair); + const metricId = getMetricIdV3(pair); - if (MetricState.isCounterState(metricState)) { - const currentValue = Number(metricState.count); + const state = metricState as unknown as Record; + if (state[V3_COUNTER_STATE_TYPE_ID] !== undefined) { + const currentValue = Number(metricState.count); const previousValue = previousCounterValues.get(metricId) ?? 0; const delta = currentValue - previousValue; @@ -69,41 +78,92 @@ function sendDeltaMetricToSentry( } previousCounterValues.set(metricId, currentValue); - } else { - sendMetricToSentry(pair); + } else if (state[V3_GAUGE_STATE_TYPE_ID] !== undefined) { + const value = Number(metricState.value); + sentryMetrics.gauge(name, value, { attributes }); + } else if (state[V3_HISTOGRAM_STATE_TYPE_ID] !== undefined || state[V3_SUMMARY_STATE_TYPE_ID] !== undefined) { + sentryMetrics.gauge(`${name}.sum`, metricState.sum ?? 0, { attributes }); + sentryMetrics.gauge(`${name}.count`, Number(metricState.count ?? 0), { attributes }); + sentryMetrics.gauge(`${name}.min`, metricState.min ?? 0, { attributes }); + sentryMetrics.gauge(`${name}.max`, metricState.max ?? 0, { attributes }); + } else if (state[V3_FREQUENCY_STATE_TYPE_ID] !== undefined && metricState.occurrences) { + for (const [word, count] of metricState.occurrences) { + sentryMetrics.count(name, count, { + attributes: { ...attributes, word }, + }); + } } } -/** - * Flushes all Effect metrics to Sentry. - * @param previousCounterValues - Map tracking previous counter values for delta calculation - */ -function flushMetricsToSentry(previousCounterValues: Map): void { - const snapshot = Metric.unsafeSnapshot(); +function sendV4MetricToSentry(snapshot: Metric.Metric.Snapshot, previousCounterValues: Map): void { + const name = snapshot.id; + const attributes: MetricAttributes = snapshot.attributes ? { ...snapshot.attributes } : {}; + const metricId = getMetricIdV4(snapshot); + + switch (snapshot.type) { + case 'Counter': { + const currentValue = Number(snapshot.state.count); + const previousValue = previousCounterValues.get(metricId) ?? 0; + const delta = currentValue - previousValue; + + if (delta > 0) { + sentryMetrics.count(name, delta, { attributes }); + } - snapshot.forEach((pair: MetricPair.MetricPair.Untyped) => { - if (MetricKeyType.isCounterKey(pair.metricKey.keyType)) { - sendDeltaMetricToSentry(pair, previousCounterValues); - } else { - sendMetricToSentry(pair); + previousCounterValues.set(metricId, currentValue); + break; + } + case 'Gauge': { + const value = Number(snapshot.state.value); + sentryMetrics.gauge(name, value, { attributes }); + break; + } + case 'Histogram': + case 'Summary': { + sentryMetrics.gauge(`${name}.sum`, snapshot.state.sum ?? 0, { attributes }); + sentryMetrics.gauge(`${name}.count`, snapshot.state.count ?? 0, { attributes }); + sentryMetrics.gauge(`${name}.min`, snapshot.state.min ?? 0, { attributes }); + sentryMetrics.gauge(`${name}.max`, snapshot.state.max ?? 0, { attributes }); + break; + } + case 'Frequency': { + for (const [word, count] of snapshot.state.occurrences) { + sentryMetrics.count(name, count, { + attributes: { ...attributes, word }, + }); + } + break; } - }); + } } -/** - * Creates a metrics flusher with its own isolated state for delta tracking. - * Useful for testing scenarios where you need to control the lifecycle. - * @internal - */ -export function createMetricsFlusher(): { - flush: () => void; - clear: () => void; -} { - const previousCounterValues = new Map(); - return { - flush: () => flushMetricsToSentry(previousCounterValues), - clear: () => previousCounterValues.clear(), - }; +// ============================================================================= +// Effect v3 snapshot function type (vendored - not exported from effect@3.x) +// ============================================================================= + +type V3UnsafeSnapshotFn = () => ReadonlyArray; + +// Use bracket notation to avoid Webpack static analysis flagging missing exports +// This is important for Effect v3 compatibility. +const MetricModule = Metric; +const snapshotUnsafe = MetricModule['snapshotUnsafe'] as typeof Metric.snapshotUnsafe | undefined; +// @ts-expect-error - unsafeSnapshot is not exported from effect@3.x +const unsafeSnapshot = MetricModule['unsafeSnapshot'] as V3UnsafeSnapshotFn | undefined; + +function flushMetricsToSentry(previousCounterValues: Map): void { + if (snapshotUnsafe) { + // Effect v4 + const snapshots = snapshotUnsafe(Context.empty()); + for (const snapshot of snapshots) { + sendV4MetricToSentry(snapshot, previousCounterValues); + } + } else if (unsafeSnapshot) { + // Effect v3 + const snapshots = unsafeSnapshot(); + for (const pair of snapshots) { + sendV3MetricToSentry(pair, previousCounterValues); + } + } } function createMetricsReporterEffect(previousCounterValues: Map): Effect.Effect { @@ -120,7 +180,7 @@ function createMetricsReporterEffect(previousCounterValues: Map) * The layer manages its own state for delta counter calculations, * which is automatically cleaned up when the layer is finalized. */ -export const SentryEffectMetricsLayer: Layer.Layer = scopedDiscard( +export const SentryEffectMetricsLayer: Layer.Layer = Layer.effectDiscard( Effect.gen(function* () { const previousCounterValues = new Map(); diff --git a/packages/effect/src/tracer.ts b/packages/effect/src/tracer.ts index f755101e4417..a3149b8e7096 100644 --- a/packages/effect/src/tracer.ts +++ b/packages/effect/src/tracer.ts @@ -32,6 +32,46 @@ function isSentrySpan(span: EffectTracer.AnySpan): span is SentrySpanLike { return SENTRY_SPAN_SYMBOL in span; } +function getErrorMessage(exit: Exit.Exit): string | undefined { + if (!Exit.isFailure(exit)) { + return undefined; + } + + const cause = exit.cause as unknown; + + // Effect v4: cause.reasons is an array of Reason objects + if ( + cause && + typeof cause === 'object' && + 'reasons' in cause && + Array.isArray((cause as { reasons: unknown }).reasons) + ) { + const reasons = (cause as { reasons: Array<{ _tag?: string; error?: unknown; defect?: unknown }> }).reasons; + for (const reason of reasons) { + if (reason._tag === 'Fail' && reason.error !== undefined) { + return String(reason.error); + } + if (reason._tag === 'Die' && reason.defect !== undefined) { + return String(reason.defect); + } + } + return 'internal_error'; + } + + // Effect v3: cause has _tag directly + if (cause && typeof cause === 'object' && '_tag' in cause) { + const v3Cause = cause as { _tag: string; error?: unknown; defect?: unknown }; + if (v3Cause._tag === 'Fail') { + return String(v3Cause.error); + } + if (v3Cause._tag === 'Die') { + return String(v3Cause.defect); + } + } + + return 'internal_error'; +} + class SentrySpanWrapper implements SentrySpanLike { public readonly [SENTRY_SPAN_SYMBOL]: true; public readonly _tag: 'Span'; @@ -43,6 +83,7 @@ class SentrySpanWrapper implements SentrySpanLike { public readonly links: Array; public status: EffectTracer.SpanStatus; public readonly sentrySpan: Span; + public readonly annotations: Context.Context; public constructor( public readonly name: string, @@ -59,6 +100,7 @@ class SentrySpanWrapper implements SentrySpanLike { this.parent = parent; this.links = [...links]; this.sentrySpan = existingSpan; + this.annotations = context; const spanContext = this.sentrySpan.spanContext(); this.spanId = spanContext.spanId; @@ -96,9 +138,7 @@ class SentrySpanWrapper implements SentrySpanLike { } if (Exit.isFailure(exit)) { - const cause = exit.cause; - const message = - cause._tag === 'Fail' ? String(cause.error) : cause._tag === 'Die' ? String(cause.defect) : 'internal_error'; + const message = getErrorMessage(exit) ?? 'internal_error'; this.sentrySpan.setStatus({ code: 2, message }); } else { this.sentrySpan.setStatus({ code: 1 }); @@ -139,21 +179,71 @@ function createSentrySpan( return new SentrySpanWrapper(name, parent, context, links, startTime, kind, newSpan); } -const makeSentryTracer = (): EffectTracer.Tracer => - EffectTracer.make({ - span(name, parent, context, links, startTime, kind) { +// Check if we're running Effect v4 by checking the Exit/Cause structure +// In v4, causes have a 'reasons' array +// In v3, causes have '_tag' directly on the cause object +const isEffectV4 = (() => { + try { + const testExit = Exit.fail('test') as unknown as { cause?: unknown }; + const cause = testExit.cause; + // v4 causes have 'reasons' array, v3 causes have '_tag' directly + if (cause && typeof cause === 'object' && 'reasons' in cause) { + return true; + } + return false; + } catch { + return false; + } +})(); + +const makeSentryTracerV3 = (): EffectTracer.Tracer => { + // Effect v3 API: span(name, parent, context, links, startTime, kind) + return EffectTracer.make({ + span( + name: string, + parent: Option.Option, + context: Context.Context, + links: ReadonlyArray, + startTime: bigint, + kind: EffectTracer.SpanKind, + ) { return createSentrySpan(name, parent, context, links, startTime, kind); }, - context(execution, fiber) { + context(execution: () => unknown, fiber: { currentSpan?: EffectTracer.AnySpan }) { const currentSpan = fiber.currentSpan; if (currentSpan === undefined || !isSentrySpan(currentSpan)) { return execution(); } return withActiveSpan(currentSpan.sentrySpan, execution); }, + } as unknown as EffectTracer.Tracer); +}; + +const makeSentryTracerV4 = (): EffectTracer.Tracer => { + const EFFECT_EVALUATE = '~effect/Effect/evaluate' as const; + + return EffectTracer.make({ + span(options) { + return createSentrySpan( + options.name, + options.parent, + options.annotations, + options.links, + options.startTime, + options.kind, + ); + }, + context(primitive, fiber) { + const currentSpan = fiber.currentSpan; + if (currentSpan === undefined || !isSentrySpan(currentSpan)) { + return primitive[EFFECT_EVALUATE](fiber); + } + return withActiveSpan(currentSpan.sentrySpan, () => primitive[EFFECT_EVALUATE](fiber)); + }, }); +}; /** * Effect Layer that sets up the Sentry tracer for Effect spans. */ -export const SentryEffectTracer = makeSentryTracer(); +export const SentryEffectTracer = isEffectV4 ? makeSentryTracerV4() : makeSentryTracerV3(); diff --git a/packages/effect/test/layer.test.ts b/packages/effect/test/layer.test.ts index 1874fe9b0f53..255d751799d5 100644 --- a/packages/effect/test/layer.test.ts +++ b/packages/effect/test/layer.test.ts @@ -1,7 +1,8 @@ import { describe, expect, it } from '@effect/vitest'; import * as sentryCore from '@sentry/core'; import { getClient, getCurrentScope, getIsolationScope, SDK_VERSION } from '@sentry/core'; -import { Effect, Layer, Logger, LogLevel } from 'effect'; +import { Effect, Layer, Logger } from 'effect'; +import * as References from 'effect/References'; import { afterEach, beforeEach, vi } from 'vitest'; import * as sentryClient from '../src/index.client'; import * as sentryServer from '../src/index.server'; @@ -109,7 +110,7 @@ describe.each([ ), ); - it.effect('layer can be composed with tracer layer', () => + it.effect('layer can be composed with tracer', () => Effect.gen(function* () { const startInactiveSpanMock = vi.spyOn(sentryCore, 'startInactiveSpan'); @@ -120,32 +121,30 @@ describe.each([ expect(result).toBe(84); expect(startInactiveSpanMock).toHaveBeenCalledWith(expect.objectContaining({ name: 'computation' })); }).pipe( + Effect.withTracer(SentryEffectTracer), Effect.provide( - Layer.mergeAll( - effectLayer({ - dsn: TEST_DSN, - transport: getMockTransport(), - }), - Layer.setTracer(SentryEffectTracer), - ), + effectLayer({ + dsn: TEST_DSN, + transport: getMockTransport(), + }), ), ), ); - it.effect('layer can be composed with logger layer', () => + it.effect('layer can be composed with logger', () => Effect.gen(function* () { yield* Effect.logInfo('test log'); const result = yield* Effect.succeed('logged'); expect(result).toBe('logged'); }).pipe( + Effect.provideService(References.MinimumLogLevel, 'All'), Effect.provide( Layer.mergeAll( effectLayer({ dsn: TEST_DSN, transport: getMockTransport(), }), - Logger.replace(Logger.defaultLogger, SentryEffectLogger), - Logger.minimumLogLevel(LogLevel.All), + Logger.layer([SentryEffectLogger]), ), ), ), @@ -164,15 +163,15 @@ describe.each([ expect(result).toBe(84); expect(startInactiveSpanMock).toHaveBeenCalledWith(expect.objectContaining({ name: 'computation' })); }).pipe( + Effect.withTracer(SentryEffectTracer), + Effect.provideService(References.MinimumLogLevel, 'All'), Effect.provide( Layer.mergeAll( effectLayer({ dsn: TEST_DSN, transport: getMockTransport(), }), - Layer.setTracer(SentryEffectTracer), - Logger.replace(Logger.defaultLogger, SentryEffectLogger), - Logger.minimumLogLevel(LogLevel.All), + Logger.layer([SentryEffectLogger]), ), ), ), diff --git a/packages/effect/test/logger.test.ts b/packages/effect/test/logger.test.ts index c372784b483f..5069514fc2c7 100644 --- a/packages/effect/test/logger.test.ts +++ b/packages/effect/test/logger.test.ts @@ -1,6 +1,7 @@ import { describe, expect, it } from '@effect/vitest'; import * as sentryCore from '@sentry/core'; -import { Effect, Layer, Logger, LogLevel } from 'effect'; +import { Effect, Logger } from 'effect'; +import * as References from 'effect/References'; import { afterEach, vi } from 'vitest'; import { SentryEffectLogger } from '../src/logger'; @@ -25,10 +26,10 @@ describe('SentryEffectLogger', () => { vi.clearAllMocks(); }); - const loggerLayer = Layer.mergeAll( - Logger.replace(Logger.defaultLogger, SentryEffectLogger), - Logger.minimumLogLevel(LogLevel.All), - ); + const loggerLayer = Logger.layer([SentryEffectLogger]); + + const withAllLogLevels = (effect: Effect.Effect) => + Effect.provideService(effect, References.MinimumLogLevel, 'All'); it.effect('forwards fatal logs to Sentry', () => Effect.gen(function* () { @@ -62,14 +63,14 @@ describe('SentryEffectLogger', () => { Effect.gen(function* () { yield* Effect.logDebug('This is a debug message'); expect(sentryCore.logger.debug).toHaveBeenCalledWith('This is a debug message'); - }).pipe(Effect.provide(loggerLayer)), + }).pipe(withAllLogLevels, Effect.provide(loggerLayer)), ); it.effect('forwards trace logs to Sentry', () => Effect.gen(function* () { yield* Effect.logTrace('This is a trace message'); expect(sentryCore.logger.trace).toHaveBeenCalledWith('This is a trace message'); - }).pipe(Effect.provide(loggerLayer)), + }).pipe(withAllLogLevels, Effect.provide(loggerLayer)), ); it.effect('handles object messages by stringifying', () => diff --git a/packages/effect/test/metrics.test.ts b/packages/effect/test/metrics.test.ts index 8c2b092b967f..a8d5a9813fa9 100644 --- a/packages/effect/test/metrics.test.ts +++ b/packages/effect/test/metrics.test.ts @@ -1,8 +1,10 @@ import { describe, expect, it } from '@effect/vitest'; import * as sentryCore from '@sentry/core'; -import { Duration, Effect, Metric, MetricBoundaries, MetricLabel } from 'effect'; +import * as Context from 'effect/Context'; +import { Duration, Effect, Layer, Metric } from 'effect'; +import { TestClock } from 'effect/testing'; import { afterEach, beforeEach, vi } from 'vitest'; -import { createMetricsFlusher } from '../src/metrics'; +import { SentryEffectMetricsLayer } from '../src/metrics'; describe('SentryEffectMetricsLayer', () => { const mockCount = vi.fn(); @@ -24,12 +26,12 @@ describe('SentryEffectMetricsLayer', () => { Effect.gen(function* () { const counter = Metric.counter('test_counter'); - yield* Metric.increment(counter); - yield* Metric.increment(counter); - yield* Metric.incrementBy(counter, 5); + yield* Metric.update(counter, 1); + yield* Metric.update(counter, 1); + yield* Metric.update(counter, 5); - const snapshot = Metric.unsafeSnapshot(); - const counterMetric = snapshot.find(p => p.metricKey.name === 'test_counter'); + const snapshot = Metric.snapshotUnsafe(Context.empty()); + const counterMetric = snapshot.find(p => p.id === 'test_counter'); expect(counterMetric).toBeDefined(); }), @@ -39,10 +41,10 @@ describe('SentryEffectMetricsLayer', () => { Effect.gen(function* () { const gauge = Metric.gauge('test_gauge'); - yield* Metric.set(gauge, 42); + yield* Metric.update(gauge, 42); - const snapshot = Metric.unsafeSnapshot(); - const gaugeMetric = snapshot.find(p => p.metricKey.name === 'test_gauge'); + const snapshot = Metric.snapshotUnsafe(Context.empty()); + const gaugeMetric = snapshot.find(p => p.id === 'test_gauge'); expect(gaugeMetric).toBeDefined(); }), @@ -50,14 +52,16 @@ describe('SentryEffectMetricsLayer', () => { it.effect('creates histogram metrics', () => Effect.gen(function* () { - const histogram = Metric.histogram('test_histogram', MetricBoundaries.linear({ start: 0, width: 10, count: 10 })); + const histogram = Metric.histogram('test_histogram', { + boundaries: Metric.linearBoundaries({ start: 0, width: 10, count: 10 }), + }); yield* Metric.update(histogram, 5); yield* Metric.update(histogram, 15); yield* Metric.update(histogram, 25); - const snapshot = Metric.unsafeSnapshot(); - const histogramMetric = snapshot.find(p => p.metricKey.name === 'test_histogram'); + const snapshot = Metric.snapshotUnsafe(Context.empty()); + const histogramMetric = snapshot.find(p => p.id === 'test_histogram'); expect(histogramMetric).toBeDefined(); }), @@ -65,8 +69,7 @@ describe('SentryEffectMetricsLayer', () => { it.effect('creates summary metrics', () => Effect.gen(function* () { - const summary = Metric.summary({ - name: 'test_summary', + const summary = Metric.summary('test_summary', { maxAge: '1 minute', maxSize: 100, error: 0.01, @@ -77,8 +80,8 @@ describe('SentryEffectMetricsLayer', () => { yield* Metric.update(summary, 20); yield* Metric.update(summary, 30); - const snapshot = Metric.unsafeSnapshot(); - const summaryMetric = snapshot.find(p => p.metricKey.name === 'test_summary'); + const snapshot = Metric.snapshotUnsafe(Context.empty()); + const summaryMetric = snapshot.find(p => p.id === 'test_summary'); expect(summaryMetric).toBeDefined(); }), @@ -92,39 +95,41 @@ describe('SentryEffectMetricsLayer', () => { yield* Metric.update(frequency, 'bar'); yield* Metric.update(frequency, 'foo'); - const snapshot = Metric.unsafeSnapshot(); - const frequencyMetric = snapshot.find(p => p.metricKey.name === 'test_frequency'); + const snapshot = Metric.snapshotUnsafe(Context.empty()); + const frequencyMetric = snapshot.find(p => p.id === 'test_frequency'); expect(frequencyMetric).toBeDefined(); }), ); - it.effect('supports metrics with labels', () => + it.effect('supports metrics with attributes', () => Effect.gen(function* () { const counter = Metric.counter('labeled_counter').pipe( - Metric.taggedWithLabels([MetricLabel.make('env', 'test'), MetricLabel.make('service', 'my-service')]), + Metric.withAttributes({ env: 'test', service: 'my-service' }), ); - yield* Metric.increment(counter); + yield* Metric.update(counter, 1); - const snapshot = Metric.unsafeSnapshot(); - const labeledMetric = snapshot.find(p => p.metricKey.name === 'labeled_counter'); + const snapshot = Metric.snapshotUnsafe(Context.empty()); + const labeledMetric = snapshot.find(p => p.id === 'labeled_counter'); expect(labeledMetric).toBeDefined(); - const tags = labeledMetric?.metricKey.tags ?? []; - expect(tags.some(t => t.key === 'env' && t.value === 'test')).toBe(true); - expect(tags.some(t => t.key === 'service' && t.value === 'my-service')).toBe(true); + const attrs = labeledMetric?.attributes ?? {}; + expect(attrs['env']).toBe('test'); + expect(attrs['service']).toBe('my-service'); }), ); - it.effect('tracks Effect durations with timer metric', () => + it.effect('tracks Effect durations with histogram metric', () => Effect.gen(function* () { - const timer = Metric.timerWithBoundaries('operation_duration', [10, 50, 100, 500, 1000]); + const histogram = Metric.histogram('operation_duration', { + boundaries: Metric.linearBoundaries({ start: 10, width: 100, count: 10 }), + }); - yield* Effect.succeed('done').pipe(Metric.trackDuration(timer)); + yield* Metric.update(histogram, Duration.millis(50)); - const snapshot = Metric.unsafeSnapshot(); - const timerMetric = snapshot.find(p => p.metricKey.name === 'operation_duration'); + const snapshot = Metric.snapshotUnsafe(Context.empty()); + const timerMetric = snapshot.find(p => p.id === 'operation_duration'); expect(timerMetric).toBeDefined(); }), @@ -140,7 +145,7 @@ describe('SentryEffectMetricsLayer', () => { ); }); -describe('createMetricsFlusher', () => { +describe('SentryEffectMetricsLayer flushing', () => { const mockCount = vi.fn(); const mockGauge = vi.fn(); const mockDistribution = vi.fn(); @@ -156,58 +161,54 @@ describe('createMetricsFlusher', () => { vi.restoreAllMocks(); }); + const TestLayer = SentryEffectMetricsLayer.pipe(Layer.provideMerge(TestClock.layer())); + it.effect('sends counter metrics to Sentry', () => Effect.gen(function* () { - const flusher = createMetricsFlusher(); const counter = Metric.counter('flush_test_counter'); - yield* Metric.increment(counter); - yield* Metric.incrementBy(counter, 4); + yield* Metric.update(counter, 1); + yield* Metric.update(counter, 4); - flusher.flush(); + yield* TestClock.adjust('10 seconds'); expect(mockCount).toHaveBeenCalledWith('flush_test_counter', 5, { attributes: {} }); - }), + }).pipe(Effect.provide(TestLayer)), ); it.effect('sends gauge metrics to Sentry', () => Effect.gen(function* () { - const flusher = createMetricsFlusher(); const gauge = Metric.gauge('flush_test_gauge'); - yield* Metric.set(gauge, 42); + yield* Metric.update(gauge, 42); - flusher.flush(); + yield* TestClock.adjust('10 seconds'); expect(mockGauge).toHaveBeenCalledWith('flush_test_gauge', 42, { attributes: {} }); - }), + }).pipe(Effect.provide(TestLayer)), ); it.effect('sends histogram metrics to Sentry', () => Effect.gen(function* () { - const flusher = createMetricsFlusher(); - const histogram = Metric.histogram( - 'flush_test_histogram', - MetricBoundaries.linear({ start: 0, width: 10, count: 5 }), - ); + const histogram = Metric.histogram('flush_test_histogram', { + boundaries: Metric.linearBoundaries({ start: 0, width: 10, count: 5 }), + }); yield* Metric.update(histogram, 5); yield* Metric.update(histogram, 15); - flusher.flush(); + yield* TestClock.adjust('10 seconds'); expect(mockGauge).toHaveBeenCalledWith('flush_test_histogram.sum', expect.any(Number), { attributes: {} }); expect(mockGauge).toHaveBeenCalledWith('flush_test_histogram.count', expect.any(Number), { attributes: {} }); expect(mockGauge).toHaveBeenCalledWith('flush_test_histogram.min', expect.any(Number), { attributes: {} }); expect(mockGauge).toHaveBeenCalledWith('flush_test_histogram.max', expect.any(Number), { attributes: {} }); - }), + }).pipe(Effect.provide(TestLayer)), ); it.effect('sends summary metrics to Sentry', () => Effect.gen(function* () { - const flusher = createMetricsFlusher(); - const summary = Metric.summary({ - name: 'flush_test_summary', + const summary = Metric.summary('flush_test_summary', { maxAge: '1 minute', maxSize: 100, error: 0.01, @@ -218,104 +219,74 @@ describe('createMetricsFlusher', () => { yield* Metric.update(summary, 20); yield* Metric.update(summary, 30); - flusher.flush(); + yield* TestClock.adjust('10 seconds'); expect(mockGauge).toHaveBeenCalledWith('flush_test_summary.sum', 60, { attributes: {} }); expect(mockGauge).toHaveBeenCalledWith('flush_test_summary.count', 3, { attributes: {} }); expect(mockGauge).toHaveBeenCalledWith('flush_test_summary.min', 10, { attributes: {} }); expect(mockGauge).toHaveBeenCalledWith('flush_test_summary.max', 30, { attributes: {} }); - }), + }).pipe(Effect.provide(TestLayer)), ); it.effect('sends frequency metrics to Sentry', () => Effect.gen(function* () { - const flusher = createMetricsFlusher(); const frequency = Metric.frequency('flush_test_frequency'); yield* Metric.update(frequency, 'apple'); yield* Metric.update(frequency, 'banana'); yield* Metric.update(frequency, 'apple'); - flusher.flush(); + yield* TestClock.adjust('10 seconds'); expect(mockCount).toHaveBeenCalledWith('flush_test_frequency', 2, { attributes: { word: 'apple' } }); expect(mockCount).toHaveBeenCalledWith('flush_test_frequency', 1, { attributes: { word: 'banana' } }); - }), + }).pipe(Effect.provide(TestLayer)), ); - it.effect('sends metrics with labels as attributes to Sentry', () => + it.effect('sends metrics with attributes to Sentry', () => Effect.gen(function* () { - const flusher = createMetricsFlusher(); const gauge = Metric.gauge('flush_test_labeled_gauge').pipe( - Metric.taggedWithLabels([MetricLabel.make('env', 'production'), MetricLabel.make('region', 'us-east')]), + Metric.withAttributes({ env: 'production', region: 'us-east' }), ); - yield* Metric.set(gauge, 100); + yield* Metric.update(gauge, 100); - flusher.flush(); + yield* TestClock.adjust('10 seconds'); expect(mockGauge).toHaveBeenCalledWith('flush_test_labeled_gauge', 100, { attributes: { env: 'production', region: 'us-east' }, }); - }), + }).pipe(Effect.provide(TestLayer)), ); it.effect('sends counter delta values on subsequent flushes', () => Effect.gen(function* () { - const flusher = createMetricsFlusher(); const counter = Metric.counter('flush_test_delta_counter'); - yield* Metric.incrementBy(counter, 10); - flusher.flush(); + yield* Metric.update(counter, 10); + yield* TestClock.adjust('10 seconds'); mockCount.mockClear(); - yield* Metric.incrementBy(counter, 5); - flusher.flush(); + yield* Metric.update(counter, 5); + yield* TestClock.adjust('10 seconds'); expect(mockCount).toHaveBeenCalledWith('flush_test_delta_counter', 5, { attributes: {} }); - }), + }).pipe(Effect.provide(TestLayer)), ); it.effect('does not send counter when delta is zero', () => Effect.gen(function* () { - const flusher = createMetricsFlusher(); const counter = Metric.counter('flush_test_zero_delta'); - yield* Metric.incrementBy(counter, 10); - flusher.flush(); + yield* Metric.update(counter, 10); + yield* TestClock.adjust('10 seconds'); mockCount.mockClear(); - flusher.flush(); + yield* TestClock.adjust('10 seconds'); expect(mockCount).not.toHaveBeenCalledWith('flush_test_zero_delta', 0, { attributes: {} }); - }), + }).pipe(Effect.provide(TestLayer)), ); - - it.effect('clear() resets delta tracking state', () => - Effect.gen(function* () { - const flusher = createMetricsFlusher(); - const counter = Metric.counter('flush_test_clear_counter'); - - yield* Metric.incrementBy(counter, 10); - flusher.flush(); - - mockCount.mockClear(); - flusher.clear(); - - flusher.flush(); - - expect(mockCount).toHaveBeenCalledWith('flush_test_clear_counter', 10, { attributes: {} }); - }), - ); - - it('each flusher has isolated state', () => { - const flusher1 = createMetricsFlusher(); - const flusher2 = createMetricsFlusher(); - - expect(flusher1).not.toBe(flusher2); - expect(flusher1.flush).not.toBe(flusher2.flush); - expect(flusher1.clear).not.toBe(flusher2.clear); - }); }); diff --git a/packages/effect/test/tracer.test.ts b/packages/effect/test/tracer.test.ts index 9583e7d12c5b..81d8cae64f42 100644 --- a/packages/effect/test/tracer.test.ts +++ b/packages/effect/test/tracer.test.ts @@ -1,11 +1,11 @@ import { describe, expect, it } from '@effect/vitest'; import * as sentryCore from '@sentry/core'; import { SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN } from '@sentry/core'; -import { Effect, Layer } from 'effect'; +import { Effect } from 'effect'; import { afterEach, vi } from 'vitest'; import { SentryEffectTracer } from '../src/tracer'; -const TracerLayer = Layer.setTracer(SentryEffectTracer); +const withSentryTracer = (effect: Effect.Effect) => Effect.withTracer(effect, SentryEffectTracer); describe('SentryEffectTracer', () => { afterEach(() => { @@ -24,7 +24,7 @@ describe('SentryEffectTracer', () => { ); expect(capturedSpanName).toBe('effect-span-executed'); - }).pipe(Effect.provide(TracerLayer)), + }).pipe(withSentryTracer), ); it.effect('creates spans with correct attributes', () => @@ -32,7 +32,7 @@ describe('SentryEffectTracer', () => { const result = yield* Effect.withSpan('my-operation')(Effect.succeed('success')); expect(result).toBe('success'); - }).pipe(Effect.provide(TracerLayer)), + }).pipe(withSentryTracer), ); it.effect('handles nested spans', () => @@ -45,7 +45,7 @@ describe('SentryEffectTracer', () => { ); expect(result).toBe('outer-inner-result'); - }).pipe(Effect.provide(TracerLayer)), + }).pipe(withSentryTracer), ); it.effect('propagates span context through Effect fibers', () => @@ -62,27 +62,30 @@ describe('SentryEffectTracer', () => { ); expect(results).toEqual(['parent-start', 'child-1', 'child-2', 'parent-end']); - }).pipe(Effect.provide(TracerLayer)), + }).pipe(withSentryTracer), ); it.effect('handles span failures correctly', () => Effect.gen(function* () { const result = yield* Effect.withSpan('failing-span')(Effect.fail('expected-error')).pipe( - Effect.catchAll(e => Effect.succeed(`caught: ${e}`)), + Effect.catchCause(cause => { + const error = cause.reasons[0]?._tag === 'Fail' ? cause.reasons[0].error : 'unknown'; + return Effect.succeed(`caught: ${error}`); + }), ); expect(result).toBe('caught: expected-error'); - }).pipe(Effect.provide(TracerLayer)), + }).pipe(withSentryTracer), ); it.effect('handles span with defects (die)', () => Effect.gen(function* () { const result = yield* Effect.withSpan('defect-span')(Effect.die('defect-value')).pipe( - Effect.catchAllDefect(d => Effect.succeed(`caught-defect: ${d}`)), + Effect.catchDefect(d => Effect.succeed(`caught-defect: ${d}`)), ); expect(result).toBe('caught-defect: defect-value'); - }).pipe(Effect.provide(TracerLayer)), + }).pipe(withSentryTracer), ); it.effect('works with Effect.all for parallel operations', () => @@ -96,7 +99,7 @@ describe('SentryEffectTracer', () => { ); expect(results).toEqual([1, 2, 3]); - }).pipe(Effect.provide(TracerLayer)), + }).pipe(withSentryTracer), ); it.effect('supports span annotations', () => @@ -107,7 +110,7 @@ describe('SentryEffectTracer', () => { ); expect(result).toBe('annotated'); - }).pipe(Effect.provide(TracerLayer)), + }).pipe(withSentryTracer), ); it.effect('sets span status to ok on success', () => @@ -130,7 +133,7 @@ describe('SentryEffectTracer', () => { expect(setStatusCalls).toContainEqual({ code: 1 }); mockStartInactiveSpan.mockRestore(); - }).pipe(Effect.provide(TracerLayer)), + }).pipe(withSentryTracer), ); it.effect('sets span status to error on failure', () => @@ -148,12 +151,12 @@ describe('SentryEffectTracer', () => { } as unknown as sentryCore.Span; }); - yield* Effect.withSpan('error-span')(Effect.fail('test-error')).pipe(Effect.catchAll(() => Effect.void)); + yield* Effect.withSpan('error-span')(Effect.fail('test-error')).pipe(Effect.catchCause(() => Effect.void)); expect(setStatusCalls).toContainEqual({ code: 2, message: 'test-error' }); mockStartInactiveSpan.mockRestore(); - }).pipe(Effect.provide(TracerLayer)), + }).pipe(withSentryTracer), ); it.effect('sets span status to error on defect', () => @@ -171,12 +174,12 @@ describe('SentryEffectTracer', () => { } as unknown as sentryCore.Span; }); - yield* Effect.withSpan('defect-span')(Effect.die('fatal-defect')).pipe(Effect.catchAllDefect(() => Effect.void)); + yield* Effect.withSpan('defect-span')(Effect.die('fatal-defect')).pipe(Effect.catchDefect(() => Effect.void)); expect(setStatusCalls).toContainEqual({ code: 2, message: 'fatal-defect' }); mockStartInactiveSpan.mockRestore(); - }).pipe(Effect.provide(TracerLayer)), + }).pipe(withSentryTracer), ); it.effect('propagates Sentry span context via withActiveSpan', () => @@ -197,7 +200,7 @@ describe('SentryEffectTracer', () => { expect(withActiveSpanCalls.length).toBeGreaterThan(0); mockWithActiveSpan.mockRestore(); - }).pipe(Effect.provide(TracerLayer)), + }).pipe(withSentryTracer), ); it.effect('sets origin to auto.function.effect for regular spans', () => @@ -222,7 +225,7 @@ describe('SentryEffectTracer', () => { expect(capturedAttributes?.[SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]).toBe('auto.function.effect'); mockStartInactiveSpan.mockRestore(); - }).pipe(Effect.provide(TracerLayer)), + }).pipe(withSentryTracer), ); it.effect('sets origin to auto.http.effect for http.server spans', () => @@ -247,7 +250,7 @@ describe('SentryEffectTracer', () => { expect(capturedAttributes?.[SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]).toBe('auto.http.effect'); mockStartInactiveSpan.mockRestore(); - }).pipe(Effect.provide(TracerLayer)), + }).pipe(withSentryTracer), ); it.effect('sets origin to auto.http.effect for http.client spans', () => @@ -272,7 +275,7 @@ describe('SentryEffectTracer', () => { expect(capturedAttributes?.[SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]).toBe('auto.http.effect'); mockStartInactiveSpan.mockRestore(); - }).pipe(Effect.provide(TracerLayer)), + }).pipe(withSentryTracer), ); it.effect('can be used with Effect.withTracer', () => diff --git a/yarn.lock b/yarn.lock index cc45a89c5703..7731d582e748 100644 --- a/yarn.lock +++ b/yarn.lock @@ -3229,10 +3229,10 @@ dependencies: "@edge-runtime/primitives" "6.0.0" -"@effect/vitest@^0.23.9": - version "0.23.13" - resolved "https://registry.yarnpkg.com/@effect/vitest/-/vitest-0.23.13.tgz#17edf9d8e3443f080ff8fe93bd37b023612a07a4" - integrity sha512-F3x2phMXuVzqWexdcYp8v0z1qQHkKxp2UaHNbqZaEjPEp8FBz/iMwbi6iS/oIWzLfGF8XqdP8BGJptvGIJONNw== +"@effect/vitest@^4.0.0-beta.50": + version "4.0.0-beta.50" + resolved "https://registry.yarnpkg.com/@effect/vitest/-/vitest-4.0.0-beta.50.tgz#c3945b4a0206fa07160896b641445e16eb5d3214" + integrity sha512-bju/iCLZB8oHsVia1i6olo9ZntkZ5TrqmsINudFsRkZfHhu5UuTR3vjic29wykZpPXXONX1wKO0KZZCk+stcKg== "@ember-data/rfc395-data@^0.0.4": version "0.0.4" @@ -5314,6 +5314,36 @@ dependencies: sparse-bitfield "^3.0.3" +"@msgpackr-extract/msgpackr-extract-darwin-arm64@3.0.3": + version "3.0.3" + resolved "https://registry.yarnpkg.com/@msgpackr-extract/msgpackr-extract-darwin-arm64/-/msgpackr-extract-darwin-arm64-3.0.3.tgz#9edec61b22c3082018a79f6d1c30289ddf3d9d11" + integrity sha512-QZHtlVgbAdy2zAqNA9Gu1UpIuI8Xvsd1v8ic6B2pZmeFnFcMWiPLfWXh7TVw4eGEZ/C9TH281KwhVoeQUKbyjw== + +"@msgpackr-extract/msgpackr-extract-darwin-x64@3.0.3": + version "3.0.3" + resolved "https://registry.yarnpkg.com/@msgpackr-extract/msgpackr-extract-darwin-x64/-/msgpackr-extract-darwin-x64-3.0.3.tgz#33677a275204898ad8acbf62734fc4dc0b6a4855" + integrity sha512-mdzd3AVzYKuUmiWOQ8GNhl64/IoFGol569zNRdkLReh6LRLHOXxU4U8eq0JwaD8iFHdVGqSy4IjFL4reoWCDFw== + +"@msgpackr-extract/msgpackr-extract-linux-arm64@3.0.3": + version "3.0.3" + resolved "https://registry.yarnpkg.com/@msgpackr-extract/msgpackr-extract-linux-arm64/-/msgpackr-extract-linux-arm64-3.0.3.tgz#19edf7cdc2e7063ee328403c1d895a86dd28f4bb" + integrity sha512-YxQL+ax0XqBJDZiKimS2XQaf+2wDGVa1enVRGzEvLLVFeqa5kx2bWbtcSXgsxjQB7nRqqIGFIcLteF/sHeVtQg== + +"@msgpackr-extract/msgpackr-extract-linux-arm@3.0.3": + version "3.0.3" + resolved "https://registry.yarnpkg.com/@msgpackr-extract/msgpackr-extract-linux-arm/-/msgpackr-extract-linux-arm-3.0.3.tgz#94fb0543ba2e28766c3fc439cabbe0440ae70159" + integrity sha512-fg0uy/dG/nZEXfYilKoRe7yALaNmHoYeIoJuJ7KJ+YyU2bvY8vPv27f7UKhGRpY6euFYqEVhxCFZgAUNQBM3nw== + +"@msgpackr-extract/msgpackr-extract-linux-x64@3.0.3": + version "3.0.3" + resolved "https://registry.yarnpkg.com/@msgpackr-extract/msgpackr-extract-linux-x64/-/msgpackr-extract-linux-x64-3.0.3.tgz#4a0609ab5fe44d07c9c60a11e4484d3c38bbd6e3" + integrity sha512-cvwNfbP07pKUfq1uH+S6KJ7dT9K8WOE4ZiAcsrSes+UY55E/0jLYc+vq+DO7jlmqRb5zAggExKm0H7O/CBaesg== + +"@msgpackr-extract/msgpackr-extract-win32-x64@3.0.3": + version "3.0.3" + resolved "https://registry.yarnpkg.com/@msgpackr-extract/msgpackr-extract-win32-x64/-/msgpackr-extract-win32-x64-3.0.3.tgz#0aa5502d547b57abfc4ac492de68e2006e417242" + integrity sha512-x0fWaQtYp4E6sktbsdAqnehxDgEc/VwM7uLsRCYWaiGu0ykYdZPiS8zCWdnjHwyiumousxfBm4SO31eXqwEZhQ== + "@napi-rs/wasm-runtime@0.2.4": version "0.2.4" resolved "https://registry.yarnpkg.com/@napi-rs/wasm-runtime/-/wasm-runtime-0.2.4.tgz#d27788176f250d86e498081e3c5ff48a17606918" @@ -8563,10 +8593,10 @@ resolved "https://registry.yarnpkg.com/@speed-highlight/core/-/core-1.2.14.tgz#5d7fe87410d2d779bd0b7680f7a706466f363314" integrity sha512-G4ewlBNhUtlLvrJTb88d2mdy2KRijzs4UhnlrOSRT4bmjh/IqNElZa3zkrZ+TC47TwtlDWzVLFADljF1Ijp5hA== -"@standard-schema/spec@^1.0.0": - version "1.0.0" - resolved "https://registry.yarnpkg.com/@standard-schema/spec/-/spec-1.0.0.tgz#f193b73dc316c4170f2e82a881da0f550d551b9c" - integrity sha512-m2bOd0f2RT9k8QJx1JN85cZYyH1RqFBdlwtkSlf4tBDYLCiiZnv1fIIwacK6cqwXavOydf0NPToMQgpKq+dVlA== +"@standard-schema/spec@^1.0.0", "@standard-schema/spec@^1.1.0": + version "1.1.0" + resolved "https://registry.yarnpkg.com/@standard-schema/spec/-/spec-1.1.0.tgz#a79b55dbaf8604812f52d140b2c9ab41bc150bb8" + integrity sha512-l2aFy5jALhniG5HgqrD6jXLi/rUWrKvqN/qJx6yoJsgKhblVd+iqqU4RCXavm/jPityDo5TCvKMnpjKnOriy0w== "@supabase/auth-js@2.69.1": version "2.69.1" @@ -14912,7 +14942,7 @@ detect-libc@^1.0.3: resolved "https://registry.yarnpkg.com/detect-libc/-/detect-libc-1.0.3.tgz#fa137c4bd698edf55cd5cd02ac559f91a4c4ba9b" integrity sha512-pGjwhsmsp4kL2RTz08wcOlGN83otlqHeD/Z5T8GXZB+/YcpQ/dgo+lbU8ZsGxV0HIvqqxo9l7mqYwyYMD9bKDg== -detect-libc@^2.0.0, detect-libc@^2.0.2, detect-libc@^2.0.3, detect-libc@^2.0.4, detect-libc@^2.1.2: +detect-libc@^2.0.0, detect-libc@^2.0.1, detect-libc@^2.0.2, detect-libc@^2.0.3, detect-libc@^2.0.4, detect-libc@^2.1.2: version "2.1.2" resolved "https://registry.yarnpkg.com/detect-libc/-/detect-libc-2.1.2.tgz#689c5dcdc1900ef5583a4cb9f6d7b473742074ad" integrity sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ== @@ -15315,13 +15345,21 @@ effect@3.16.12: "@standard-schema/spec" "^1.0.0" fast-check "^3.23.1" -effect@^3.21.0: - version "3.21.0" - resolved "https://registry.yarnpkg.com/effect/-/effect-3.21.0.tgz#ce222ce8f785b9e63f104b9a4ead985e7965f2c0" - integrity sha512-PPN80qRokCd1f015IANNhrwOnLO7GrrMQfk4/lnZRE/8j7UPWrNNjPV0uBrZutI/nHzernbW+J0hdqQysHiSnQ== - dependencies: - "@standard-schema/spec" "^1.0.0" - fast-check "^3.23.1" +effect@^4.0.0-beta.50: + version "4.0.0-beta.50" + resolved "https://registry.yarnpkg.com/effect/-/effect-4.0.0-beta.50.tgz#c4fbc42adad53428242b8002390bde69b48feb0d" + integrity sha512-UsENighZms6LWDSnF/05F9JinDAewV3sGXHAt9M7+dL3VnoFZIwduFxXvmFc7QJm7iV1s7rB98hv1SD3ALA9qg== + dependencies: + "@standard-schema/spec" "^1.1.0" + fast-check "^4.6.0" + find-my-way-ts "^0.1.6" + ini "^6.0.0" + kubernetes-types "^1.30.0" + msgpackr "^1.11.9" + multipasta "^0.2.7" + toml "^4.1.1" + uuid "^13.0.0" + yaml "^2.8.3" ejs@^3.1.7: version "3.1.8" @@ -17431,6 +17469,13 @@ fast-check@^3.23.1: dependencies: pure-rand "^6.1.0" +fast-check@^4.6.0: + version "4.7.0" + resolved "https://registry.yarnpkg.com/fast-check/-/fast-check-4.7.0.tgz#36c0051b9c968965e8970e88e63eee946fe45f8f" + integrity sha512-NsZRtqvSSoCP0HbNjUD+r1JH8zqZalyp6gLY9e7OYs7NK9b6AHOs2baBFeBG7bVNsuoukh89x2Yg3rPsul8ziQ== + dependencies: + pure-rand "^8.0.0" + fast-content-type-parse@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/fast-content-type-parse/-/fast-content-type-parse-3.0.0.tgz#5590b6c807cc598be125e6740a9fde589d2b7afb" @@ -17756,6 +17801,11 @@ find-index@^1.1.0: resolved "https://registry.yarnpkg.com/find-index/-/find-index-1.1.1.tgz#4b221f8d46b7f8bea33d8faed953f3ca7a081cbc" integrity sha512-XYKutXMrIK99YMUPf91KX5QVJoG31/OsgftD6YoTPAObfQIxM4ziA9f0J1AsqKhJmo+IeaIPP0CFopTD4bdUBw== +find-my-way-ts@^0.1.6: + version "0.1.6" + resolved "https://registry.yarnpkg.com/find-my-way-ts/-/find-my-way-ts-0.1.6.tgz#37f7b8433d0f61e7fe7290772240b0c133b0ebf2" + integrity sha512-a85L9ZoXtNAey3Y6Z+eBWW658kO/MwR7zIafkIUPUMf3isZG0NCs2pjW2wtjxAKuJPxMAsHUIP4ZPGv0o5gyTA== + find-up@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/find-up/-/find-up-2.1.0.tgz#45d1b7e506c717ddd482775a2b77920a3c0c57a7" @@ -19626,6 +19676,11 @@ ini@^2.0.0: resolved "https://registry.yarnpkg.com/ini/-/ini-2.0.0.tgz#e5fd556ecdd5726be978fa1001862eacb0a94bc5" integrity sha512-7PnF4oN3CvZF23ADhA5wRaYEQpJ8qygSkbtTXWBeXWXmEVRXK+1ITciHWwHhsjv1TmW0MgacIv6hEi5pX5NQdA== +ini@^6.0.0: + version "6.0.0" + resolved "https://registry.yarnpkg.com/ini/-/ini-6.0.0.tgz#efc7642b276f6a37d22fdf56ef50889d7146bf30" + integrity sha512-IBTdIkzZNOpqm7q3dRqJvMaldXjDHWkEDfrwGEQTs5eaQMWV+djAhR+wahyNNMAa+qpbDUhBMVt4ZKNwpPm7xQ== + injection-js@^2.4.0: version "2.4.0" resolved "https://registry.yarnpkg.com/injection-js/-/injection-js-2.4.0.tgz#ebe8871b1a349f23294eaa751bbd8209a636e754" @@ -20934,6 +20989,11 @@ knitwork@^1.2.0, knitwork@^1.3.0: resolved "https://registry.yarnpkg.com/knitwork/-/knitwork-1.3.0.tgz#4a0d0b0d45378cac909ee1117481392522bd08a4" integrity sha512-4LqMNoONzR43B1W0ek0fhXMsDNW/zxa1NdFAVMY+k28pgZLovR4G3PB5MrpTxCy1QaZCqNoiaKPr5w5qZHfSNw== +kubernetes-types@^1.30.0: + version "1.30.0" + resolved "https://registry.yarnpkg.com/kubernetes-types/-/kubernetes-types-1.30.0.tgz#f686cacb08ffc5f7e89254899c2153c723420116" + integrity sha512-Dew1okvhM/SQcIa2rcgujNndZwU8VnSapDgdxlYoB84ZlpAD43U6KLAFqYo17ykSFGHNPrg0qry0bP+GJd9v7Q== + kuler@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/kuler/-/kuler-2.0.0.tgz#e2c570a3800388fb44407e851531c1d670b061b3" @@ -22908,6 +22968,27 @@ ms@2.1.3, ms@^2.0.0, ms@^2.1.1, ms@^2.1.3: resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.3.tgz#574c8138ce1d2b5861f0b44579dbadd60c6615b2" integrity sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA== +msgpackr-extract@^3.0.2: + version "3.0.3" + resolved "https://registry.yarnpkg.com/msgpackr-extract/-/msgpackr-extract-3.0.3.tgz#e9d87023de39ce714872f9e9504e3c1996d61012" + integrity sha512-P0efT1C9jIdVRefqjzOQ9Xml57zpOXnIuS+csaB4MdZbTdmGDLo8XhzBG1N7aO11gKDDkJvBLULeFTo46wwreA== + dependencies: + node-gyp-build-optional-packages "5.2.2" + optionalDependencies: + "@msgpackr-extract/msgpackr-extract-darwin-arm64" "3.0.3" + "@msgpackr-extract/msgpackr-extract-darwin-x64" "3.0.3" + "@msgpackr-extract/msgpackr-extract-linux-arm" "3.0.3" + "@msgpackr-extract/msgpackr-extract-linux-arm64" "3.0.3" + "@msgpackr-extract/msgpackr-extract-linux-x64" "3.0.3" + "@msgpackr-extract/msgpackr-extract-win32-x64" "3.0.3" + +msgpackr@^1.11.9: + version "1.11.9" + resolved "https://registry.yarnpkg.com/msgpackr/-/msgpackr-1.11.9.tgz#1aa99ed379a066374ac82b62f8ad70723bbd3a59" + integrity sha512-FkoAAyyA6HM8wL882EcEyFZ9s7hVADSwG9xrVx3dxxNQAtgADTrJoEWivID82Iv1zWDsv/OtbrrcZAzGzOMdNw== + optionalDependencies: + msgpackr-extract "^3.0.2" + multer@2.0.2: version "2.0.2" resolved "https://registry.yarnpkg.com/multer/-/multer-2.0.2.tgz#08a8aa8255865388c387aaf041426b0c87bf58dd" @@ -22929,6 +23010,11 @@ multicast-dns@^7.2.5: dns-packet "^5.2.2" thunky "^1.0.2" +multipasta@^0.2.7: + version "0.2.7" + resolved "https://registry.yarnpkg.com/multipasta/-/multipasta-0.2.7.tgz#fa8fb38be65eb951fa57cad9e8e758107946eee9" + integrity sha512-KPA58d68KgGil15oDqXjkUBEBYc00XvbPj5/X+dyzeo/lWm9Nc25pQRlf1D+gv4OpK7NM0J1odrbu9JNNGvynA== + mustache@^4.2.0: version "4.2.0" resolved "https://registry.yarnpkg.com/mustache/-/mustache-4.2.0.tgz#e5892324d60a12ec9c2a73359edca52972bf6f64" @@ -23339,6 +23425,13 @@ node-forge@^1, node-forge@^1.3.1: resolved "https://registry.yarnpkg.com/node-forge/-/node-forge-1.4.0.tgz#1c7b7d8bdc2d078739f58287d589d903a11b2fc2" integrity sha512-LarFH0+6VfriEhqMMcLX2F7SwSXeWwnEAJEsYm5QKWchiVYVvJyV9v7UDvUv+w5HO23ZpQTXDv/GxdDdMyOuoQ== +node-gyp-build-optional-packages@5.2.2: + version "5.2.2" + resolved "https://registry.yarnpkg.com/node-gyp-build-optional-packages/-/node-gyp-build-optional-packages-5.2.2.tgz#522f50c2d53134d7f3a76cd7255de4ab6c96a3a4" + integrity sha512-s+w+rBWnpTMwSFbaE0UXsRlg7hU4FjekKU4eyAih5T8nJuNZT1nNsskXpxmeqSK9UzkBl6UgRlnKc8hz8IEqOw== + dependencies: + detect-libc "^2.0.1" + node-gyp-build@^4.2.2: version "4.6.0" resolved "https://registry.yarnpkg.com/node-gyp-build/-/node-gyp-build-4.6.0.tgz#0c52e4cbf54bbd28b709820ef7b6a3c2d6209055" @@ -26085,6 +26178,11 @@ pure-rand@^6.1.0: resolved "https://registry.yarnpkg.com/pure-rand/-/pure-rand-6.1.0.tgz#d173cf23258231976ccbdb05247c9787957604f2" integrity sha512-bVWawvoZoBYpp6yIoQtQXHZjmz35RSVHnUOTefl8Vcjr8snTPY1wnpSPMWekcFwbxI6gtmT7rSYPFvz71ldiOA== +pure-rand@^8.0.0: + version "8.4.0" + resolved "https://registry.yarnpkg.com/pure-rand/-/pure-rand-8.4.0.tgz#1d9e26e9c0555486e08ae300d02796af8dec1cd0" + integrity sha512-IoM8YF/jY0hiugFo/wOWqfmarlE6J0wc6fDK1PhftMk7MGhVZl88sZimmqBBFomLOCSmcCCpsfj7wXASCpvK9A== + qs@^6.14.0, qs@^6.14.1, qs@^6.4.0, qs@~6.14.0, qs@~6.14.1: version "6.14.2" resolved "https://registry.yarnpkg.com/qs/-/qs-6.14.2.tgz#b5634cf9d9ad9898e31fba3504e866e8efb6798c" @@ -29619,6 +29717,11 @@ token-types@^6.1.1: "@tokenizer/token" "^0.3.0" ieee754 "^1.2.1" +toml@^4.1.1: + version "4.1.1" + resolved "https://registry.yarnpkg.com/toml/-/toml-4.1.1.tgz#ab8248d0403ba2c02ffcf8515b42f0dcf0d6d1b5" + integrity sha512-EBJnVBr3dTXdA89WVFoAIPUqkBjxPMwRqsfuo1r240tKFHXv3zgca4+NJib/h6TyvGF7vOawz0jGuryJCdNHrw== + totalist@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/totalist/-/totalist-3.0.0.tgz#4ef9c58c5f095255cdc3ff2a0a55091c57a3a1bd" @@ -30672,6 +30775,11 @@ uuid@^11.1.0: resolved "https://registry.yarnpkg.com/uuid/-/uuid-11.1.0.tgz#9549028be1753bb934fc96e2bca09bb4105ae912" integrity sha512-0/A9rDy9P7cJ+8w1c9WD9V//9Wj15Ce2MPz8Ri6032usz+NfePxx5AcN3bN+r6ZL6jEo066/yNYB3tn4pQEx+A== +uuid@^13.0.0: + version "13.0.0" + resolved "https://registry.yarnpkg.com/uuid/-/uuid-13.0.0.tgz#263dc341b19b4d755eb8fe36b78d95a6b65707e8" + integrity sha512-XQegIaBTVUjSHliKqcnFqYypAd4S+WCYt5NIeRs6w/UAry7z8Y9j5ZwRRL4kzq9U3sD6v+85er9FvkEaBpji2w== + uuid@^9.0.0, uuid@^9.0.1: version "9.0.1" resolved "https://registry.yarnpkg.com/uuid/-/uuid-9.0.1.tgz#e188d4c8853cc722220392c424cd637f32293f30" @@ -31880,7 +31988,7 @@ yam@^1.0.0: fs-extra "^4.0.2" lodash.merge "^4.6.0" -yaml@2.8.3, yaml@^2.6.0, yaml@^2.8.0: +yaml@2.8.3, yaml@^2.6.0, yaml@^2.8.0, yaml@^2.8.3: version "2.8.3" resolved "https://registry.yarnpkg.com/yaml/-/yaml-2.8.3.tgz#a0d6bd2efb3dd03c59370223701834e60409bd7d" integrity sha512-AvbaCLOO2Otw/lW5bmh9d/WEdcDFdQp2Z2ZUH3pX9U2ihyUY0nvLv7J6TrWowklRGPYbB/IuIMfYgxaCPg5Bpg== From 29604aa167a50fec1c3f3bbf6804c33e3d07e3ad Mon Sep 17 00:00:00 2001 From: Nicolas Hrubec Date: Tue, 21 Apr 2026 15:41:48 +0200 Subject: [PATCH 19/41] feat(core): Export `spanStreamingIntegration` from CDN bundles (#20400) - Export the integration from all CDN tracing bundles and shim for non-tracing bundles. - Remove the CDN test skips in the browser integration tests since this should work now. - Reserve `_streamed` in the terser mangler so `withStreamedSpan`'s marker survives minification in CDN bundles. - Some additional test changes were needed to make CI happy. Mostly cases where certain browsers don't emit some attributes but we check for strict equality with `toEqual`. Closes https://github.com/getsentry/sentry-javascript/issues/20273 --- .size-limit.js | 24 ++++++------- .../cultureContext-streamed/test.ts | 4 +-- .../beforeSendSpan-streamed/test.ts | 4 +-- .../public-api/startSpan/streamed/test.ts | 4 +-- .../backgroundtab-pageload-streamed/test.ts | 4 +-- .../http-timings-streamed/test.ts | 4 +-- .../interactions-streamed/test.ts | 6 ++-- .../consistent-sampling/default/test.ts | 6 ++-- .../consistent-sampling/meta-negative/test.ts | 3 +- .../meta-precedence/test.ts | 3 +- .../consistent-sampling/meta/test.ts | 6 ++-- .../tracesSampler-precedence/test.ts | 3 +- .../custom-trace/test.ts | 4 +-- .../linked-traces-streamed/default/test.ts | 6 ++-- .../interaction-spans/test.ts | 4 +-- .../linked-traces-streamed/meta/test.ts | 4 +-- .../negatively-sampled/test.ts | 4 +-- .../session-storage/test.ts | 4 +-- .../test.ts | 4 +-- .../test.ts | 4 +-- .../test.ts | 6 ++-- .../test.ts | 6 ++-- .../test.ts | 4 +-- .../long-tasks-disabled-streamed/test.ts | 4 +-- .../long-tasks-enabled-streamed/test.ts | 4 +-- .../navigation-streamed/test.ts | 26 +++++++------- .../pageload-streamed/test.ts | 34 ++++++++++--------- .../reportPageLoaded-streamed/default/test.ts | 4 +-- .../finalTimeout/test.ts | 4 +-- .../navigation/test.ts | 4 +-- .../ignoreSpans-streamed/child/test.ts | 3 +- .../ignoreSpans-streamed/segment/test.ts | 3 +- .../tracing/linking-addLink-streamed/test.ts | 6 ++-- .../web-vitals-cls-streamed-spans/test.ts | 4 +-- .../web-vitals-inp-streamed-spans/test.ts | 4 +-- .../web-vitals-lcp-streamed-spans/test.ts | 4 +-- .../metrics/web-vitals-ttfb-streamed/test.ts | 4 +-- .../test.ts | 3 +- .../tracing/request/fetch-streamed/test.ts | 10 ++++-- .../tracing/request/xhr-streamed/test.ts | 4 +-- .../setSpanActive-streamed/default/test.ts | 4 +-- .../nested-parentAlwaysRoot/test.ts | 4 +-- .../setSpanActive-streamed/nested/test.ts | 4 +-- .../navigation-streamed/test.ts | 13 ++++--- .../trace-lifetime/pageload-streamed/test.ts | 13 ++++--- .../startNewTrace-streamed/test.ts | 4 +-- .../rollup-utils/plugins/bundlePlugins.mjs | 2 ++ packages/browser/src/index.bundle.feedback.ts | 2 ++ .../browser/src/index.bundle.logs.metrics.ts | 2 ++ .../src/index.bundle.replay.feedback.ts | 2 ++ .../src/index.bundle.replay.logs.metrics.ts | 7 +++- packages/browser/src/index.bundle.replay.ts | 2 ++ .../src/index.bundle.tracing.logs.metrics.ts | 2 ++ ...le.tracing.replay.feedback.logs.metrics.ts | 2 ++ .../index.bundle.tracing.replay.feedback.ts | 2 ++ ...ndex.bundle.tracing.replay.logs.metrics.ts | 2 ++ .../src/index.bundle.tracing.replay.ts | 2 ++ packages/browser/src/index.bundle.tracing.ts | 2 ++ packages/browser/src/index.bundle.ts | 2 ++ .../test/index.bundle.feedback.test.ts | 2 ++ .../test/index.bundle.logs.metrics.test.ts | 2 ++ .../test/index.bundle.replay.feedback.test.ts | 2 ++ .../index.bundle.replay.logs.metrics.test.ts | 7 +++- .../browser/test/index.bundle.replay.test.ts | 2 ++ packages/browser/test/index.bundle.test.ts | 2 ++ .../index.bundle.tracing.logs.metrics.test.ts | 3 +- ...acing.replay.feedback.logs.metrics.test.ts | 8 ++++- ...dex.bundle.tracing.replay.feedback.test.ts | 8 ++++- ...bundle.tracing.replay.logs.metrics.test.ts | 3 +- .../test/index.bundle.tracing.replay.test.ts | 3 +- .../browser/test/index.bundle.tracing.test.ts | 3 +- .../integration-shims/src/SpanStreaming.ts | 17 ++++++++++ packages/integration-shims/src/index.ts | 1 + 73 files changed, 230 insertions(+), 152 deletions(-) create mode 100644 packages/integration-shims/src/SpanStreaming.ts diff --git a/.size-limit.js b/.size-limit.js index adef55219f86..cad516a0a49a 100644 --- a/.size-limit.js +++ b/.size-limit.js @@ -191,7 +191,7 @@ module.exports = [ name: 'CDN Bundle (incl. Tracing)', path: createCDNPath('bundle.tracing.min.js'), gzip: true, - limit: '45 KB', + limit: '46.5 KB', }, { name: 'CDN Bundle (incl. Logs, Metrics)', @@ -203,7 +203,7 @@ module.exports = [ name: 'CDN Bundle (incl. Tracing, Logs, Metrics)', path: createCDNPath('bundle.tracing.logs.metrics.min.js'), gzip: true, - limit: '47 KB', + limit: '47.5 KB', }, { name: 'CDN Bundle (incl. Replay, Logs, Metrics)', @@ -215,25 +215,25 @@ module.exports = [ name: 'CDN Bundle (incl. Tracing, Replay)', path: createCDNPath('bundle.tracing.replay.min.js'), gzip: true, - limit: '82 KB', + limit: '83.5 KB', }, { name: 'CDN Bundle (incl. Tracing, Replay, Logs, Metrics)', path: createCDNPath('bundle.tracing.replay.logs.metrics.min.js'), gzip: true, - limit: '84 KB', + limit: '84.5 KB', }, { name: 'CDN Bundle (incl. Tracing, Replay, Feedback)', path: createCDNPath('bundle.tracing.replay.feedback.min.js'), gzip: true, - limit: '88 KB', + limit: '89 KB', }, { name: 'CDN Bundle (incl. Tracing, Replay, Feedback, Logs, Metrics)', path: createCDNPath('bundle.tracing.replay.feedback.logs.metrics.min.js'), gzip: true, - limit: '89 KB', + limit: '90 KB', }, // browser CDN bundles (non-gzipped) { @@ -248,7 +248,7 @@ module.exports = [ path: createCDNPath('bundle.tracing.min.js'), gzip: false, brotli: false, - limit: '135 KB', + limit: '138 KB', }, { name: 'CDN Bundle (incl. Logs, Metrics) - uncompressed', @@ -262,7 +262,7 @@ module.exports = [ path: createCDNPath('bundle.tracing.logs.metrics.min.js'), gzip: false, brotli: false, - limit: '138 KB', + limit: '141.5 KB', }, { name: 'CDN Bundle (incl. Replay, Logs, Metrics) - uncompressed', @@ -276,28 +276,28 @@ module.exports = [ path: createCDNPath('bundle.tracing.replay.min.js'), gzip: false, brotli: false, - limit: '252 KB', + limit: '255.5 KB', }, { name: 'CDN Bundle (incl. Tracing, Replay, Logs, Metrics) - uncompressed', path: createCDNPath('bundle.tracing.replay.logs.metrics.min.js'), gzip: false, brotli: false, - limit: '256 KB', + limit: '258.5 KB', }, { name: 'CDN Bundle (incl. Tracing, Replay, Feedback) - uncompressed', path: createCDNPath('bundle.tracing.replay.feedback.min.js'), gzip: false, brotli: false, - limit: '265 KB', + limit: '268 KB', }, { name: 'CDN Bundle (incl. Tracing, Replay, Feedback, Logs, Metrics) - uncompressed', path: createCDNPath('bundle.tracing.replay.feedback.logs.metrics.min.js'), gzip: false, brotli: false, - limit: '269 KB', + limit: '271.5 KB', }, // Next.js SDK (ESM) { diff --git a/dev-packages/browser-integration-tests/suites/integrations/cultureContext-streamed/test.ts b/dev-packages/browser-integration-tests/suites/integrations/cultureContext-streamed/test.ts index 43dee40f093b..731ce2db6146 100644 --- a/dev-packages/browser-integration-tests/suites/integrations/cultureContext-streamed/test.ts +++ b/dev-packages/browser-integration-tests/suites/integrations/cultureContext-streamed/test.ts @@ -1,10 +1,10 @@ import { expect } from '@playwright/test'; import { sentryTest } from '../../../utils/fixtures'; import { getSpanOp, waitForStreamedSpans } from '../../../utils/spanUtils'; -import { shouldSkipTracingTest, testingCdnBundle } from '../../../utils/helpers'; +import { shouldSkipTracingTest } from '../../../utils/helpers'; sentryTest('cultureContextIntegration captures locale, timezone, and calendar', async ({ getLocalTestUrl, page }) => { - sentryTest.skip(shouldSkipTracingTest() || testingCdnBundle()); + sentryTest.skip(shouldSkipTracingTest()); const url = await getLocalTestUrl({ testDir: __dirname }); const spansPromise = waitForStreamedSpans(page, spans => spans.some(s => getSpanOp(s) === 'pageload')); diff --git a/dev-packages/browser-integration-tests/suites/public-api/beforeSendSpan-streamed/test.ts b/dev-packages/browser-integration-tests/suites/public-api/beforeSendSpan-streamed/test.ts index ce07297c0a04..be5d0feee840 100644 --- a/dev-packages/browser-integration-tests/suites/public-api/beforeSendSpan-streamed/test.ts +++ b/dev-packages/browser-integration-tests/suites/public-api/beforeSendSpan-streamed/test.ts @@ -1,10 +1,10 @@ import { expect } from '@playwright/test'; import { sentryTest } from '../../../utils/fixtures'; -import { shouldSkipTracingTest, testingCdnBundle } from '../../../utils/helpers'; +import { shouldSkipTracingTest } from '../../../utils/helpers'; import { getSpanOp, waitForStreamedSpan } from '../../../utils/spanUtils'; sentryTest('beforeSendSpan applies changes to streamed span', async ({ getLocalTestUrl, page }) => { - sentryTest.skip(shouldSkipTracingTest() || testingCdnBundle()); + sentryTest.skip(shouldSkipTracingTest()); const url = await getLocalTestUrl({ testDir: __dirname }); diff --git a/dev-packages/browser-integration-tests/suites/public-api/startSpan/streamed/test.ts b/dev-packages/browser-integration-tests/suites/public-api/startSpan/streamed/test.ts index 9ea2197fff85..7a70c832558f 100644 --- a/dev-packages/browser-integration-tests/suites/public-api/startSpan/streamed/test.ts +++ b/dev-packages/browser-integration-tests/suites/public-api/startSpan/streamed/test.ts @@ -11,13 +11,13 @@ import { SEMANTIC_ATTRIBUTE_SENTRY_SOURCE, } from '@sentry/core'; import { sentryTest } from '../../../../utils/fixtures'; -import { shouldSkipTracingTest, testingCdnBundle } from '../../../../utils/helpers'; +import { shouldSkipTracingTest } from '../../../../utils/helpers'; import { waitForStreamedSpanEnvelope } from '../../../../utils/spanUtils'; sentryTest( 'sends a streamed span envelope if spanStreamingIntegration is enabled', async ({ getLocalTestUrl, page }) => { - sentryTest.skip(shouldSkipTracingTest() || testingCdnBundle()); + sentryTest.skip(shouldSkipTracingTest()); const spanEnvelopePromise = waitForStreamedSpanEnvelope(page); diff --git a/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/backgroundtab-pageload-streamed/test.ts b/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/backgroundtab-pageload-streamed/test.ts index 10e58acb81ad..a851918b5438 100644 --- a/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/backgroundtab-pageload-streamed/test.ts +++ b/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/backgroundtab-pageload-streamed/test.ts @@ -1,10 +1,10 @@ import { expect } from '@playwright/test'; import { sentryTest } from '../../../../utils/fixtures'; -import { shouldSkipTracingTest, testingCdnBundle } from '../../../../utils/helpers'; +import { shouldSkipTracingTest } from '../../../../utils/helpers'; import { getSpanOp, waitForStreamedSpan } from '../../../../utils/spanUtils'; sentryTest('finishes streamed pageload span when the page goes background', async ({ getLocalTestUrl, page }) => { - sentryTest.skip(shouldSkipTracingTest() || testingCdnBundle()); + sentryTest.skip(shouldSkipTracingTest()); const url = await getLocalTestUrl({ testDir: __dirname }); const pageloadSpanPromise = waitForStreamedSpan(page, span => getSpanOp(span) === 'pageload'); diff --git a/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/http-timings-streamed/test.ts b/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/http-timings-streamed/test.ts index 25d4ac497992..30e32621edbd 100644 --- a/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/http-timings-streamed/test.ts +++ b/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/http-timings-streamed/test.ts @@ -1,6 +1,6 @@ import { expect } from '@playwright/test'; import { sentryTest } from '../../../../utils/fixtures'; -import { shouldSkipTracingTest, testingCdnBundle } from '../../../../utils/helpers'; +import { shouldSkipTracingTest } from '../../../../utils/helpers'; import { getSpanOp, waitForStreamedSpans } from '../../../../utils/spanUtils'; sentryTest( @@ -8,7 +8,7 @@ sentryTest( async ({ browserName, getLocalTestUrl, page }) => { const supportedBrowsers = ['chromium', 'firefox']; - sentryTest.skip(shouldSkipTracingTest() || !supportedBrowsers.includes(browserName) || testingCdnBundle()); + sentryTest.skip(shouldSkipTracingTest() || !supportedBrowsers.includes(browserName)); await page.route('http://sentry-test-site.example/*', async route => { const request = route.request(); diff --git a/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/interactions-streamed/test.ts b/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/interactions-streamed/test.ts index 546d80c133ac..f1b0882d2325 100644 --- a/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/interactions-streamed/test.ts +++ b/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/interactions-streamed/test.ts @@ -11,13 +11,13 @@ import { SEMANTIC_ATTRIBUTE_SENTRY_SOURCE, } from '@sentry/core'; import { sentryTest } from '../../../../utils/fixtures'; -import { shouldSkipTracingTest, testingCdnBundle } from '../../../../utils/helpers'; +import { shouldSkipTracingTest } from '../../../../utils/helpers'; import { getSpanOp, waitForStreamedSpan, waitForStreamedSpans } from '../../../../utils/spanUtils'; sentryTest('captures streamed interaction span tree. @firefox', async ({ browserName, getLocalTestUrl, page }) => { const supportedBrowsers = ['chromium', 'firefox']; - sentryTest.skip(shouldSkipTracingTest() || !supportedBrowsers.includes(browserName) || testingCdnBundle()); + sentryTest.skip(shouldSkipTracingTest() || !supportedBrowsers.includes(browserName)); const url = await getLocalTestUrl({ testDir: __dirname }); const interactionSpansPromise = waitForStreamedSpans(page, spans => @@ -99,7 +99,7 @@ sentryTest('captures streamed interaction span tree. @firefox', async ({ browser }); const loAFSpans = interactionSpanTree.filter(span => getSpanOp(span)?.startsWith('ui.long-animation-frame')); - expect(loAFSpans).toHaveLength(1); + expect(loAFSpans).toHaveLength(browserName === 'chromium' ? 1 : 0); const interactionSpan = interactionSpanTree.find(span => getSpanOp(span) === 'ui.interaction.click'); expect(interactionSpan).toEqual({ diff --git a/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/linked-traces-streamed/consistent-sampling/default/test.ts b/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/linked-traces-streamed/consistent-sampling/default/test.ts index a97e13a4890a..61c8fc3303dd 100644 --- a/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/linked-traces-streamed/consistent-sampling/default/test.ts +++ b/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/linked-traces-streamed/consistent-sampling/default/test.ts @@ -1,12 +1,12 @@ import { expect } from '@playwright/test'; import { extractTraceparentData, parseBaggageHeader, SEMANTIC_LINK_ATTRIBUTE_LINK_TYPE } from '@sentry/core'; import { sentryTest } from '../../../../../../utils/fixtures'; -import { shouldSkipTracingTest, testingCdnBundle, waitForTracingHeadersOnUrl } from '../../../../../../utils/helpers'; +import { shouldSkipTracingTest, waitForTracingHeadersOnUrl } from '../../../../../../utils/helpers'; import { getSpanOp, waitForStreamedSpanEnvelope } from '../../../../../../utils/spanUtils'; sentryTest.describe('When `consistentTraceSampling` is `true`', () => { sentryTest('continues sampling decision from initial pageload span', async ({ getLocalTestUrl, page }) => { - sentryTest.skip(shouldSkipTracingTest() || testingCdnBundle()); + sentryTest.skip(shouldSkipTracingTest()); const url = await getLocalTestUrl({ testDir: __dirname }); @@ -80,7 +80,7 @@ sentryTest.describe('When `consistentTraceSampling` is `true`', () => { }); sentryTest('Propagates continued sampling decision to outgoing requests', async ({ page, getLocalTestUrl }) => { - sentryTest.skip(shouldSkipTracingTest() || testingCdnBundle()); + sentryTest.skip(shouldSkipTracingTest()); const url = await getLocalTestUrl({ testDir: __dirname }); diff --git a/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/linked-traces-streamed/consistent-sampling/meta-negative/test.ts b/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/linked-traces-streamed/consistent-sampling/meta-negative/test.ts index ea50f09f2361..79cabe19b927 100644 --- a/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/linked-traces-streamed/consistent-sampling/meta-negative/test.ts +++ b/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/linked-traces-streamed/consistent-sampling/meta-negative/test.ts @@ -7,7 +7,6 @@ import { envelopeRequestParser, hidePage, shouldSkipTracingTest, - testingCdnBundle, waitForClientReportRequest, waitForTracingHeadersOnUrl, } from '../../../../../../utils/helpers'; @@ -21,7 +20,7 @@ sentryTest.describe('When `consistentTraceSampling` is `true` and page contains sentryTest( 'Continues negative sampling decision from meta tag across all traces and downstream propagations', async ({ getLocalTestUrl, page }) => { - sentryTest.skip(shouldSkipTracingTest() || testingCdnBundle()); + sentryTest.skip(shouldSkipTracingTest()); const url = await getLocalTestUrl({ testDir: __dirname }); diff --git a/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/linked-traces-streamed/consistent-sampling/meta-precedence/test.ts b/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/linked-traces-streamed/consistent-sampling/meta-precedence/test.ts index 367b48e70eda..cd64596f9bfd 100644 --- a/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/linked-traces-streamed/consistent-sampling/meta-precedence/test.ts +++ b/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/linked-traces-streamed/consistent-sampling/meta-precedence/test.ts @@ -6,7 +6,6 @@ import { envelopeRequestParser, hidePage, shouldSkipTracingTest, - testingCdnBundle, waitForClientReportRequest, waitForTracingHeadersOnUrl, } from '../../../../../../utils/helpers'; @@ -21,7 +20,7 @@ sentryTest.describe('When `consistentTraceSampling` is `true` and page contains sentryTest( 'meta tag decision has precedence over sampling decision from previous trace in session storage', async ({ getLocalTestUrl, page }) => { - sentryTest.skip(shouldSkipTracingTest() || testingCdnBundle()); + sentryTest.skip(shouldSkipTracingTest()); const url = await getLocalTestUrl({ testDir: __dirname }); diff --git a/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/linked-traces-streamed/consistent-sampling/meta/test.ts b/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/linked-traces-streamed/consistent-sampling/meta/test.ts index 08cee9111b8a..154fe167a4a1 100644 --- a/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/linked-traces-streamed/consistent-sampling/meta/test.ts +++ b/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/linked-traces-streamed/consistent-sampling/meta/test.ts @@ -5,7 +5,7 @@ import { SEMANTIC_ATTRIBUTE_SENTRY_PREVIOUS_TRACE_SAMPLE_RATE, } from '@sentry/core'; import { sentryTest } from '../../../../../../utils/fixtures'; -import { shouldSkipTracingTest, testingCdnBundle, waitForTracingHeadersOnUrl } from '../../../../../../utils/helpers'; +import { shouldSkipTracingTest, waitForTracingHeadersOnUrl } from '../../../../../../utils/helpers'; import { getSpanOp, waitForStreamedSpanEnvelope } from '../../../../../../utils/spanUtils'; const metaTagSampleRand = 0.051121; @@ -13,7 +13,7 @@ const metaTagSampleRate = 0.2; sentryTest.describe('When `consistentTraceSampling` is `true` and page contains tags', () => { sentryTest('Continues sampling decision across all traces from meta tag', async ({ getLocalTestUrl, page }) => { - sentryTest.skip(shouldSkipTracingTest() || testingCdnBundle()); + sentryTest.skip(shouldSkipTracingTest()); const url = await getLocalTestUrl({ testDir: __dirname }); @@ -96,7 +96,7 @@ sentryTest.describe('When `consistentTraceSampling` is `true` and page contains sentryTest( 'Propagates continued tag sampling decision to outgoing requests', async ({ page, getLocalTestUrl }) => { - sentryTest.skip(shouldSkipTracingTest() || testingCdnBundle()); + sentryTest.skip(shouldSkipTracingTest()); const url = await getLocalTestUrl({ testDir: __dirname }); diff --git a/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/linked-traces-streamed/consistent-sampling/tracesSampler-precedence/test.ts b/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/linked-traces-streamed/consistent-sampling/tracesSampler-precedence/test.ts index d661a4548e94..cd319e614c71 100644 --- a/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/linked-traces-streamed/consistent-sampling/tracesSampler-precedence/test.ts +++ b/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/linked-traces-streamed/consistent-sampling/tracesSampler-precedence/test.ts @@ -7,7 +7,6 @@ import { envelopeRequestParser, hidePage, shouldSkipTracingTest, - testingCdnBundle, waitForClientReportRequest, } from '../../../../../../utils/helpers'; import { getSpanOp, waitForStreamedSpanEnvelope } from '../../../../../../utils/spanUtils'; @@ -19,7 +18,7 @@ import { getSpanOp, waitForStreamedSpanEnvelope } from '../../../../../../utils/ */ sentryTest.describe('When `consistentTraceSampling` is `true`', () => { sentryTest('explicit sampling decisions in `tracesSampler` have precedence', async ({ getLocalTestUrl, page }) => { - sentryTest.skip(shouldSkipTracingTest() || testingCdnBundle()); + sentryTest.skip(shouldSkipTracingTest()); const url = await getLocalTestUrl({ testDir: __dirname }); diff --git a/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/linked-traces-streamed/custom-trace/test.ts b/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/linked-traces-streamed/custom-trace/test.ts index d6e45901f959..571f4a6d8b5e 100644 --- a/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/linked-traces-streamed/custom-trace/test.ts +++ b/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/linked-traces-streamed/custom-trace/test.ts @@ -1,11 +1,11 @@ import { expect } from '@playwright/test'; import { SEMANTIC_LINK_ATTRIBUTE_LINK_TYPE } from '@sentry/core'; import { sentryTest } from '../../../../../utils/fixtures'; -import { shouldSkipTracingTest, testingCdnBundle } from '../../../../../utils/helpers'; +import { shouldSkipTracingTest } from '../../../../../utils/helpers'; import { getSpanOp, waitForStreamedSpan } from '../../../../../utils/spanUtils'; sentryTest('manually started custom traces are linked correctly in the chain', async ({ getLocalTestUrl, page }) => { - sentryTest.skip(shouldSkipTracingTest() || testingCdnBundle()); + sentryTest.skip(shouldSkipTracingTest()); const url = await getLocalTestUrl({ testDir: __dirname }); diff --git a/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/linked-traces-streamed/default/test.ts b/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/linked-traces-streamed/default/test.ts index 80e500437f79..fd63b6358bdf 100644 --- a/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/linked-traces-streamed/default/test.ts +++ b/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/linked-traces-streamed/default/test.ts @@ -1,11 +1,11 @@ import { expect } from '@playwright/test'; import { SEMANTIC_LINK_ATTRIBUTE_LINK_TYPE } from '@sentry/core'; import { sentryTest } from '../../../../../utils/fixtures'; -import { shouldSkipTracingTest, testingCdnBundle } from '../../../../../utils/helpers'; +import { shouldSkipTracingTest } from '../../../../../utils/helpers'; import { getSpanOp, waitForStreamedSpan } from '../../../../../utils/spanUtils'; sentryTest("navigation spans link back to previous trace's root span", async ({ getLocalTestUrl, page }) => { - sentryTest.skip(shouldSkipTracingTest() || testingCdnBundle()); + sentryTest.skip(shouldSkipTracingTest()); const url = await getLocalTestUrl({ testDir: __dirname }); @@ -71,7 +71,7 @@ sentryTest("navigation spans link back to previous trace's root span", async ({ }); sentryTest("doesn't link between hard page reloads by default", async ({ getLocalTestUrl, page }) => { - sentryTest.skip(shouldSkipTracingTest() || testingCdnBundle()); + sentryTest.skip(shouldSkipTracingTest()); const url = await getLocalTestUrl({ testDir: __dirname }); diff --git a/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/linked-traces-streamed/interaction-spans/test.ts b/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/linked-traces-streamed/interaction-spans/test.ts index c34aba99dbdd..1e5666e116e8 100644 --- a/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/linked-traces-streamed/interaction-spans/test.ts +++ b/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/linked-traces-streamed/interaction-spans/test.ts @@ -1,7 +1,7 @@ import { expect } from '@playwright/test'; import { SEMANTIC_LINK_ATTRIBUTE_LINK_TYPE } from '@sentry/core'; import { sentryTest } from '../../../../../utils/fixtures'; -import { shouldSkipTracingTest, testingCdnBundle } from '../../../../../utils/helpers'; +import { shouldSkipTracingTest } from '../../../../../utils/helpers'; import { getSpanOp, waitForStreamedSpan } from '../../../../../utils/spanUtils'; /* @@ -13,7 +13,7 @@ import { getSpanOp, waitForStreamedSpan } from '../../../../../utils/spanUtils'; sentryTest( 'only the first root spans in the trace link back to the previous trace', async ({ getLocalTestUrl, page }) => { - sentryTest.skip(shouldSkipTracingTest() || testingCdnBundle()); + sentryTest.skip(shouldSkipTracingTest()); const url = await getLocalTestUrl({ testDir: __dirname }); diff --git a/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/linked-traces-streamed/meta/test.ts b/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/linked-traces-streamed/meta/test.ts index cbcc231593ea..141e5a9505dc 100644 --- a/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/linked-traces-streamed/meta/test.ts +++ b/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/linked-traces-streamed/meta/test.ts @@ -1,13 +1,13 @@ import { expect } from '@playwright/test'; import { SEMANTIC_LINK_ATTRIBUTE_LINK_TYPE } from '@sentry/core'; import { sentryTest } from '../../../../../utils/fixtures'; -import { shouldSkipTracingTest, testingCdnBundle } from '../../../../../utils/helpers'; +import { shouldSkipTracingTest } from '../../../../../utils/helpers'; import { getSpanOp, waitForStreamedSpan } from '../../../../../utils/spanUtils'; sentryTest( "links back to previous trace's local root span if continued from meta tags", async ({ getLocalTestUrl, page }) => { - sentryTest.skip(shouldSkipTracingTest() || testingCdnBundle()); + sentryTest.skip(shouldSkipTracingTest()); const url = await getLocalTestUrl({ testDir: __dirname }); diff --git a/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/linked-traces-streamed/negatively-sampled/test.ts b/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/linked-traces-streamed/negatively-sampled/test.ts index 06366eb9921a..0ff22e58a405 100644 --- a/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/linked-traces-streamed/negatively-sampled/test.ts +++ b/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/linked-traces-streamed/negatively-sampled/test.ts @@ -1,11 +1,11 @@ import { expect } from '@playwright/test'; import { SEMANTIC_LINK_ATTRIBUTE_LINK_TYPE } from '@sentry/core'; import { sentryTest } from '../../../../../utils/fixtures'; -import { shouldSkipTracingTest, testingCdnBundle } from '../../../../../utils/helpers'; +import { shouldSkipTracingTest } from '../../../../../utils/helpers'; import { getSpanOp, waitForStreamedSpan } from '../../../../../utils/spanUtils'; sentryTest('includes a span link to a previously negatively sampled span', async ({ getLocalTestUrl, page }) => { - sentryTest.skip(shouldSkipTracingTest() || testingCdnBundle()); + sentryTest.skip(shouldSkipTracingTest()); const url = await getLocalTestUrl({ testDir: __dirname }); diff --git a/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/linked-traces-streamed/session-storage/test.ts b/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/linked-traces-streamed/session-storage/test.ts index 96a5bbeacc6d..5ed1c8021d4a 100644 --- a/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/linked-traces-streamed/session-storage/test.ts +++ b/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/linked-traces-streamed/session-storage/test.ts @@ -1,11 +1,11 @@ import { expect } from '@playwright/test'; import { SEMANTIC_LINK_ATTRIBUTE_LINK_TYPE } from '@sentry/core'; import { sentryTest } from '../../../../../utils/fixtures'; -import { shouldSkipTracingTest, testingCdnBundle } from '../../../../../utils/helpers'; +import { shouldSkipTracingTest } from '../../../../../utils/helpers'; import { getSpanOp, waitForStreamedSpan } from '../../../../../utils/spanUtils'; sentryTest('adds link between hard page reloads when opting into sessionStorage', async ({ getLocalTestUrl, page }) => { - sentryTest.skip(shouldSkipTracingTest() || testingCdnBundle()); + sentryTest.skip(shouldSkipTracingTest()); const url = await getLocalTestUrl({ testDir: __dirname }); diff --git a/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/long-animation-frame-before-navigation-streamed/test.ts b/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/long-animation-frame-before-navigation-streamed/test.ts index 3054c1c84bcb..f20fe774264b 100644 --- a/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/long-animation-frame-before-navigation-streamed/test.ts +++ b/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/long-animation-frame-before-navigation-streamed/test.ts @@ -1,13 +1,13 @@ import { expect } from '@playwright/test'; import { sentryTest } from '../../../../utils/fixtures'; -import { shouldSkipTracingTest, testingCdnBundle } from '../../../../utils/helpers'; +import { shouldSkipTracingTest } from '../../../../utils/helpers'; import { getSpanOp, waitForStreamedSpans } from '../../../../utils/spanUtils'; sentryTest( "doesn't capture long animation frame that starts before a navigation.", async ({ browserName, getLocalTestUrl, page }) => { // Long animation frames only work on chrome - sentryTest.skip(shouldSkipTracingTest() || browserName !== 'chromium' || testingCdnBundle()); + sentryTest.skip(shouldSkipTracingTest() || browserName !== 'chromium'); const url = await getLocalTestUrl({ testDir: __dirname }); diff --git a/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/long-animation-frame-disabled-streamed/test.ts b/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/long-animation-frame-disabled-streamed/test.ts index 7ba1dddd0c90..e03474070bb0 100644 --- a/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/long-animation-frame-disabled-streamed/test.ts +++ b/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/long-animation-frame-disabled-streamed/test.ts @@ -1,14 +1,14 @@ import type { Route } from '@playwright/test'; import { expect } from '@playwright/test'; import { sentryTest } from '../../../../utils/fixtures'; -import { shouldSkipTracingTest, testingCdnBundle } from '../../../../utils/helpers'; +import { shouldSkipTracingTest } from '../../../../utils/helpers'; import { getSpanOp, waitForStreamedSpans } from '../../../../utils/spanUtils'; sentryTest( 'does not capture long animation frame when flag is disabled.', async ({ browserName, getLocalTestUrl, page }) => { // Long animation frames only work on chrome - sentryTest.skip(shouldSkipTracingTest() || browserName !== 'chromium' || testingCdnBundle()); + sentryTest.skip(shouldSkipTracingTest() || browserName !== 'chromium'); await page.route('**/path/to/script.js', (route: Route) => route.fulfill({ path: `${__dirname}/assets/script.js` }), diff --git a/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/long-animation-frame-enabled-streamed/test.ts b/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/long-animation-frame-enabled-streamed/test.ts index c1e7efa5e8d8..040b78a89e9d 100644 --- a/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/long-animation-frame-enabled-streamed/test.ts +++ b/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/long-animation-frame-enabled-streamed/test.ts @@ -3,14 +3,14 @@ import { expect } from '@playwright/test'; import { SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN } from '@sentry/browser'; import { SEMANTIC_ATTRIBUTE_SENTRY_OP } from '@sentry/core'; import { sentryTest } from '../../../../utils/fixtures'; -import { shouldSkipTracingTest, testingCdnBundle } from '../../../../utils/helpers'; +import { shouldSkipTracingTest } from '../../../../utils/helpers'; import { getSpanOp, waitForStreamedSpans } from '../../../../utils/spanUtils'; sentryTest( 'captures long animation frame span for top-level script.', async ({ browserName, getLocalTestUrl, page }) => { // Long animation frames only work on chrome - sentryTest.skip(shouldSkipTracingTest() || browserName !== 'chromium' || testingCdnBundle()); + sentryTest.skip(shouldSkipTracingTest() || browserName !== 'chromium'); await page.route('**/path/to/script.js', (route: Route) => route.fulfill({ path: `${__dirname}/assets/script.js` }), @@ -62,7 +62,7 @@ sentryTest( sentryTest('captures long animation frame span for event listener.', async ({ browserName, getLocalTestUrl, page }) => { // Long animation frames only work on chrome - sentryTest.skip(shouldSkipTracingTest() || browserName !== 'chromium' || testingCdnBundle()); + sentryTest.skip(shouldSkipTracingTest() || browserName !== 'chromium'); await page.route('**/path/to/script.js', (route: Route) => route.fulfill({ path: `${__dirname}/assets/script.js` })); diff --git a/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/long-tasks-and-animation-frame-enabled-streamed/test.ts b/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/long-tasks-and-animation-frame-enabled-streamed/test.ts index 4f9207fa1e34..2529c6e2f66d 100644 --- a/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/long-tasks-and-animation-frame-enabled-streamed/test.ts +++ b/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/long-tasks-and-animation-frame-enabled-streamed/test.ts @@ -3,14 +3,14 @@ import { expect } from '@playwright/test'; import { SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN } from '@sentry/browser'; import { SEMANTIC_ATTRIBUTE_SENTRY_OP } from '@sentry/core'; import { sentryTest } from '../../../../utils/fixtures'; -import { shouldSkipTracingTest, testingCdnBundle } from '../../../../utils/helpers'; +import { shouldSkipTracingTest } from '../../../../utils/helpers'; import { getSpanOp, waitForStreamedSpans } from '../../../../utils/spanUtils'; sentryTest( 'captures long animation frame span for top-level script.', async ({ browserName, getLocalTestUrl, page }) => { // Long animation frames only work on chrome - sentryTest.skip(shouldSkipTracingTest() || browserName !== 'chromium' || testingCdnBundle()); + sentryTest.skip(shouldSkipTracingTest() || browserName !== 'chromium'); // Long animation frame should take priority over long tasks @@ -64,7 +64,7 @@ sentryTest( sentryTest('captures long animation frame span for event listener.', async ({ browserName, getLocalTestUrl, page }) => { // Long animation frames only work on chrome - sentryTest.skip(shouldSkipTracingTest() || browserName !== 'chromium' || testingCdnBundle()); + sentryTest.skip(shouldSkipTracingTest() || browserName !== 'chromium'); await page.route('**/path/to/script.js', (route: Route) => route.fulfill({ path: `${__dirname}/assets/script.js` })); diff --git a/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/long-tasks-before-navigation-streamed/test.ts b/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/long-tasks-before-navigation-streamed/test.ts index 74ce32706584..3d8658cb9065 100644 --- a/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/long-tasks-before-navigation-streamed/test.ts +++ b/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/long-tasks-before-navigation-streamed/test.ts @@ -1,13 +1,13 @@ import { expect } from '@playwright/test'; import { sentryTest } from '../../../../utils/fixtures'; -import { shouldSkipTracingTest, testingCdnBundle } from '../../../../utils/helpers'; +import { shouldSkipTracingTest } from '../../../../utils/helpers'; import { getSpanOp, waitForStreamedSpans } from '../../../../utils/spanUtils'; sentryTest( "doesn't capture long task spans starting before a navigation in the navigation transaction", async ({ browserName, getLocalTestUrl, page }) => { // Long tasks only work on chrome - sentryTest.skip(shouldSkipTracingTest() || browserName !== 'chromium' || testingCdnBundle()); + sentryTest.skip(shouldSkipTracingTest() || browserName !== 'chromium'); const url = await getLocalTestUrl({ testDir: __dirname }); await page.route('**/path/to/script.js', route => route.fulfill({ path: `${__dirname}/assets/script.js` })); diff --git a/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/long-tasks-disabled-streamed/test.ts b/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/long-tasks-disabled-streamed/test.ts index 83600f5d4a6a..da7f44f05df7 100644 --- a/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/long-tasks-disabled-streamed/test.ts +++ b/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/long-tasks-disabled-streamed/test.ts @@ -1,12 +1,12 @@ import type { Route } from '@playwright/test'; import { expect } from '@playwright/test'; import { sentryTest } from '../../../../utils/fixtures'; -import { shouldSkipTracingTest, testingCdnBundle } from '../../../../utils/helpers'; +import { shouldSkipTracingTest } from '../../../../utils/helpers'; import { getSpanOp, waitForStreamedSpans } from '../../../../utils/spanUtils'; sentryTest("doesn't capture long task spans when flag is disabled.", async ({ browserName, getLocalTestUrl, page }) => { // Long tasks only work on chrome - sentryTest.skip(shouldSkipTracingTest() || browserName !== 'chromium' || testingCdnBundle()); + sentryTest.skip(shouldSkipTracingTest() || browserName !== 'chromium'); await page.route('**/path/to/script.js', (route: Route) => route.fulfill({ path: `${__dirname}/assets/script.js` })); diff --git a/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/long-tasks-enabled-streamed/test.ts b/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/long-tasks-enabled-streamed/test.ts index 8b73aa91dff6..e312078254d8 100644 --- a/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/long-tasks-enabled-streamed/test.ts +++ b/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/long-tasks-enabled-streamed/test.ts @@ -1,12 +1,12 @@ import type { Route } from '@playwright/test'; import { expect } from '@playwright/test'; import { sentryTest } from '../../../../utils/fixtures'; -import { shouldSkipTracingTest, testingCdnBundle } from '../../../../utils/helpers'; +import { shouldSkipTracingTest } from '../../../../utils/helpers'; import { getSpanOp, waitForStreamedSpans } from '../../../../utils/spanUtils'; sentryTest('captures long task.', async ({ browserName, getLocalTestUrl, page }) => { // Long tasks only work on chrome - sentryTest.skip(shouldSkipTracingTest() || browserName !== 'chromium' || testingCdnBundle()); + sentryTest.skip(shouldSkipTracingTest() || browserName !== 'chromium'); await page.route('**/path/to/script.js', (route: Route) => route.fulfill({ path: `${__dirname}/assets/script.js` })); diff --git a/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/navigation-streamed/test.ts b/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/navigation-streamed/test.ts index b9922c178fd9..520a3d330bb9 100644 --- a/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/navigation-streamed/test.ts +++ b/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/navigation-streamed/test.ts @@ -7,7 +7,7 @@ import { SEMANTIC_ATTRIBUTE_SENTRY_SOURCE, } from '@sentry/core'; import { sentryTest } from '../../../../utils/fixtures'; -import { shouldSkipTracingTest, testingCdnBundle } from '../../../../utils/helpers'; +import { shouldSkipTracingTest } from '../../../../utils/helpers'; import { getSpanOp, getSpansFromEnvelope, @@ -15,8 +15,8 @@ import { waitForStreamedSpanEnvelope, } from '../../../../utils/spanUtils'; -sentryTest('starts a streamed navigation span on page navigation', async ({ getLocalTestUrl, page }) => { - sentryTest.skip(shouldSkipTracingTest() || testingCdnBundle()); +sentryTest('starts a streamed navigation span on page navigation', async ({ browserName, getLocalTestUrl, page }) => { + sentryTest.skip(shouldSkipTracingTest()); const pageloadSpanPromise = waitForStreamedSpan(page, span => getSpanOp(span) === 'pageload'); const navigationSpanEnvelopePromise = waitForStreamedSpanEnvelope( @@ -81,18 +81,20 @@ sentryTest('starts a streamed navigation span on page navigation', async ({ getL type: 'string', value: expect.any(String), }, - 'network.connection.effective_type': { - type: 'string', - value: expect.any(String), - }, 'device.processor_count': { type: expect.stringMatching(/^(integer)|(double)$/), value: expect.any(Number), }, - 'network.connection.rtt': { - type: expect.stringMatching(/^(integer)|(double)$/), - value: expect.any(Number), - }, + ...(browserName !== 'webkit' && { + 'network.connection.effective_type': { + type: 'string', + value: expect.any(String), + }, + 'network.connection.rtt': { + type: expect.stringMatching(/^(integer)|(double)$/), + value: expect.any(Number), + }, + }), 'sentry.idle_span_finish_reason': { type: 'string', value: 'idleTimeout', @@ -162,7 +164,7 @@ sentryTest('starts a streamed navigation span on page navigation', async ({ getL }); sentryTest('handles pushState with full URL', async ({ getLocalTestUrl, page }) => { - sentryTest.skip(shouldSkipTracingTest() || testingCdnBundle()); + sentryTest.skip(shouldSkipTracingTest()); const url = await getLocalTestUrl({ testDir: __dirname }); diff --git a/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/pageload-streamed/test.ts b/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/pageload-streamed/test.ts index e89e2011b100..6b09fcd0097d 100644 --- a/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/pageload-streamed/test.ts +++ b/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/pageload-streamed/test.ts @@ -11,13 +11,13 @@ import { SEMANTIC_ATTRIBUTE_SENTRY_SOURCE, } from '@sentry/core'; import { sentryTest } from '../../../../utils/fixtures'; -import { shouldSkipTracingTest, testingCdnBundle } from '../../../../utils/helpers'; +import { shouldSkipTracingTest } from '../../../../utils/helpers'; import { getSpanOp, getSpansFromEnvelope, waitForStreamedSpanEnvelope } from '../../../../utils/spanUtils'; sentryTest( 'creates a pageload streamed span envelope with url as pageload span name source', - async ({ getLocalTestUrl, page }) => { - sentryTest.skip(shouldSkipTracingTest() || testingCdnBundle()); + async ({ browserName, getLocalTestUrl, page }) => { + sentryTest.skip(shouldSkipTracingTest()); const spanEnvelopePromise = waitForStreamedSpanEnvelope( page, @@ -74,11 +74,6 @@ sentryTest( type: 'string', value: expect.any(String), }, - // formerly known as 'effectiveConnectionType' - 'network.connection.effective_type': { - type: 'string', - value: expect.any(String), - }, // formerly known as 'hardwareConcurrency' 'device.processor_count': { type: expect.stringMatching(/^(integer)|(double)$/), @@ -92,18 +87,25 @@ sentryTest( type: expect.stringMatching(/^(integer)|(double)$/), value: expect.any(Number), }, - 'network.connection.rtt': { - type: expect.stringMatching(/^(integer)|(double)$/), - value: expect.any(Number), - }, 'browser.web_vital.ttfb.request_time': { type: expect.stringMatching(/^(integer)|(double)$/), value: expect.any(Number), }, - 'browser.web_vital.ttfb.value': { - type: expect.stringMatching(/^(integer)|(double)$/), - value: expect.any(Number), - }, + ...(browserName !== 'webkit' && { + // formerly known as 'effectiveConnectionType' + 'network.connection.effective_type': { + type: 'string', + value: expect.any(String), + }, + 'network.connection.rtt': { + type: expect.stringMatching(/^(integer)|(double)$/), + value: expect.any(Number), + }, + 'browser.web_vital.ttfb.value': { + type: expect.stringMatching(/^(integer)|(double)$/), + value: expect.any(Number), + }, + }), 'sentry.idle_span_finish_reason': { type: 'string', value: 'idleTimeout', diff --git a/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/reportPageLoaded-streamed/default/test.ts b/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/reportPageLoaded-streamed/default/test.ts index fb6fa3ab2393..25ea24e6cbfe 100644 --- a/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/reportPageLoaded-streamed/default/test.ts +++ b/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/reportPageLoaded-streamed/default/test.ts @@ -7,13 +7,13 @@ import { } from '@sentry/browser'; import { SEMANTIC_ATTRIBUTE_SENTRY_IDLE_SPAN_FINISH_REASON } from '@sentry/core'; import { sentryTest } from '../../../../../utils/fixtures'; -import { shouldSkipTracingTest, testingCdnBundle } from '../../../../../utils/helpers'; +import { shouldSkipTracingTest } from '../../../../../utils/helpers'; import { getSpanOp, waitForStreamedSpan } from '../../../../../utils/spanUtils'; sentryTest( 'waits for Sentry.reportPageLoaded() to be called when `enableReportPageLoaded` is true', async ({ getLocalTestUrl, page }) => { - sentryTest.skip(shouldSkipTracingTest() || testingCdnBundle()); + sentryTest.skip(shouldSkipTracingTest()); const url = await getLocalTestUrl({ testDir: __dirname }); diff --git a/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/reportPageLoaded-streamed/finalTimeout/test.ts b/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/reportPageLoaded-streamed/finalTimeout/test.ts index 79df6a902e45..74ac5aedf101 100644 --- a/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/reportPageLoaded-streamed/finalTimeout/test.ts +++ b/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/reportPageLoaded-streamed/finalTimeout/test.ts @@ -6,13 +6,13 @@ import { SEMANTIC_ATTRIBUTE_SENTRY_SOURCE, } from '@sentry/browser'; import { sentryTest } from '../../../../../utils/fixtures'; -import { shouldSkipTracingTest, testingCdnBundle } from '../../../../../utils/helpers'; +import { shouldSkipTracingTest } from '../../../../../utils/helpers'; import { getSpanOp, waitForStreamedSpan } from '../../../../../utils/spanUtils'; sentryTest( 'final timeout cancels the pageload span even if `enableReportPageLoaded` is true', async ({ getLocalTestUrl, page }) => { - sentryTest.skip(shouldSkipTracingTest() || testingCdnBundle()); + sentryTest.skip(shouldSkipTracingTest()); const url = await getLocalTestUrl({ testDir: __dirname }); diff --git a/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/reportPageLoaded-streamed/navigation/test.ts b/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/reportPageLoaded-streamed/navigation/test.ts index 77f138f34053..001e87ad31fc 100644 --- a/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/reportPageLoaded-streamed/navigation/test.ts +++ b/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/reportPageLoaded-streamed/navigation/test.ts @@ -6,13 +6,13 @@ import { SEMANTIC_ATTRIBUTE_SENTRY_SOURCE, } from '@sentry/browser'; import { sentryTest } from '../../../../../utils/fixtures'; -import { shouldSkipTracingTest, testingCdnBundle } from '../../../../../utils/helpers'; +import { shouldSkipTracingTest } from '../../../../../utils/helpers'; import { getSpanOp, waitForStreamedSpan } from '../../../../../utils/spanUtils'; sentryTest( 'starting a navigation span cancels the pageload span even if `enableReportPageLoaded` is true', async ({ getLocalTestUrl, page }) => { - sentryTest.skip(shouldSkipTracingTest() || testingCdnBundle()); + sentryTest.skip(shouldSkipTracingTest()); const url = await getLocalTestUrl({ testDir: __dirname }); diff --git a/dev-packages/browser-integration-tests/suites/tracing/ignoreSpans-streamed/child/test.ts b/dev-packages/browser-integration-tests/suites/tracing/ignoreSpans-streamed/child/test.ts index 967ef101092d..02e24fc409d3 100644 --- a/dev-packages/browser-integration-tests/suites/tracing/ignoreSpans-streamed/child/test.ts +++ b/dev-packages/browser-integration-tests/suites/tracing/ignoreSpans-streamed/child/test.ts @@ -4,14 +4,13 @@ import { envelopeRequestParser, hidePage, shouldSkipTracingTest, - testingCdnBundle, waitForClientReportRequest, } from '../../../../utils/helpers'; import { waitForStreamedSpans } from '../../../../utils/spanUtils'; import type { ClientReport } from '@sentry/core'; sentryTest('ignored child spans are dropped and their children are reparented', async ({ getLocalTestUrl, page }) => { - sentryTest.skip(shouldSkipTracingTest() || testingCdnBundle()); + sentryTest.skip(shouldSkipTracingTest()); const spansPromise = waitForStreamedSpans(page, spans => !!spans?.find(s => s.name === 'parent-span')); diff --git a/dev-packages/browser-integration-tests/suites/tracing/ignoreSpans-streamed/segment/test.ts b/dev-packages/browser-integration-tests/suites/tracing/ignoreSpans-streamed/segment/test.ts index 93042cc5469e..99f87d647d89 100644 --- a/dev-packages/browser-integration-tests/suites/tracing/ignoreSpans-streamed/segment/test.ts +++ b/dev-packages/browser-integration-tests/suites/tracing/ignoreSpans-streamed/segment/test.ts @@ -4,14 +4,13 @@ import { envelopeRequestParser, hidePage, shouldSkipTracingTest, - testingCdnBundle, waitForClientReportRequest, } from '../../../../utils/helpers'; import { observeStreamedSpan, waitForStreamedSpans } from '../../../../utils/spanUtils'; import type { ClientReport } from '@sentry/core'; sentryTest('ignored segment span drops entire trace', async ({ getLocalTestUrl, page }) => { - sentryTest.skip(shouldSkipTracingTest() || testingCdnBundle()); + sentryTest.skip(shouldSkipTracingTest()); const url = await getLocalTestUrl({ testDir: __dirname }); diff --git a/dev-packages/browser-integration-tests/suites/tracing/linking-addLink-streamed/test.ts b/dev-packages/browser-integration-tests/suites/tracing/linking-addLink-streamed/test.ts index dc35f0c8fcf1..bafa95aee03f 100644 --- a/dev-packages/browser-integration-tests/suites/tracing/linking-addLink-streamed/test.ts +++ b/dev-packages/browser-integration-tests/suites/tracing/linking-addLink-streamed/test.ts @@ -1,10 +1,10 @@ import { expect } from '@playwright/test'; import { sentryTest } from '../../../utils/fixtures'; -import { shouldSkipTracingTest, testingCdnBundle } from '../../../utils/helpers'; +import { shouldSkipTracingTest } from '../../../utils/helpers'; import { waitForStreamedSpan, waitForStreamedSpans } from '../../../utils/spanUtils'; sentryTest('links spans with addLink() in trace context', async ({ getLocalTestUrl, page }) => { - sentryTest.skip(shouldSkipTracingTest() || testingCdnBundle()); + sentryTest.skip(shouldSkipTracingTest()); const rootSpan1Promise = waitForStreamedSpan(page, s => s.name === 'rootSpan1' && !!s.is_segment); const rootSpan2Promise = waitForStreamedSpan(page, s => s.name === 'rootSpan2' && !!s.is_segment); @@ -29,7 +29,7 @@ sentryTest('links spans with addLink() in trace context', async ({ getLocalTestU }); sentryTest('links spans with addLink() in nested startSpan() calls', async ({ getLocalTestUrl, page }) => { - sentryTest.skip(shouldSkipTracingTest() || testingCdnBundle()); + sentryTest.skip(shouldSkipTracingTest()); const rootSpan1Promise = waitForStreamedSpan(page, s => s.name === 'rootSpan1' && !!s.is_segment); const rootSpan3SpansPromise = waitForStreamedSpans(page, spans => diff --git a/dev-packages/browser-integration-tests/suites/tracing/metrics/web-vitals-cls-streamed-spans/test.ts b/dev-packages/browser-integration-tests/suites/tracing/metrics/web-vitals-cls-streamed-spans/test.ts index 31ddd09977cb..409d79327a91 100644 --- a/dev-packages/browser-integration-tests/suites/tracing/metrics/web-vitals-cls-streamed-spans/test.ts +++ b/dev-packages/browser-integration-tests/suites/tracing/metrics/web-vitals-cls-streamed-spans/test.ts @@ -1,11 +1,11 @@ import type { Page } from '@playwright/test'; import { expect } from '@playwright/test'; import { sentryTest } from '../../../../utils/fixtures'; -import { hidePage, shouldSkipTracingTest, testingCdnBundle } from '../../../../utils/helpers'; +import { hidePage, shouldSkipTracingTest } from '../../../../utils/helpers'; import { getSpanOp, waitForStreamedSpan } from '../../../../utils/spanUtils'; sentryTest.beforeEach(async ({ browserName, page }) => { - if (shouldSkipTracingTest() || testingCdnBundle() || browserName !== 'chromium') { + if (shouldSkipTracingTest() || browserName !== 'chromium') { sentryTest.skip(); } diff --git a/dev-packages/browser-integration-tests/suites/tracing/metrics/web-vitals-inp-streamed-spans/test.ts b/dev-packages/browser-integration-tests/suites/tracing/metrics/web-vitals-inp-streamed-spans/test.ts index 30dd4f92dbfc..893be918553d 100644 --- a/dev-packages/browser-integration-tests/suites/tracing/metrics/web-vitals-inp-streamed-spans/test.ts +++ b/dev-packages/browser-integration-tests/suites/tracing/metrics/web-vitals-inp-streamed-spans/test.ts @@ -1,10 +1,10 @@ import { expect } from '@playwright/test'; import { sentryTest } from '../../../../utils/fixtures'; -import { hidePage, shouldSkipTracingTest, testingCdnBundle } from '../../../../utils/helpers'; +import { hidePage, shouldSkipTracingTest } from '../../../../utils/helpers'; import { getSpanOp, waitForStreamedSpan } from '../../../../utils/spanUtils'; sentryTest.beforeEach(async ({ browserName }) => { - if (shouldSkipTracingTest() || testingCdnBundle() || browserName !== 'chromium') { + if (shouldSkipTracingTest() || browserName !== 'chromium') { sentryTest.skip(); } }); diff --git a/dev-packages/browser-integration-tests/suites/tracing/metrics/web-vitals-lcp-streamed-spans/test.ts b/dev-packages/browser-integration-tests/suites/tracing/metrics/web-vitals-lcp-streamed-spans/test.ts index 1f71cb8d76a7..8cff98edfcd0 100644 --- a/dev-packages/browser-integration-tests/suites/tracing/metrics/web-vitals-lcp-streamed-spans/test.ts +++ b/dev-packages/browser-integration-tests/suites/tracing/metrics/web-vitals-lcp-streamed-spans/test.ts @@ -1,11 +1,11 @@ import type { Route } from '@playwright/test'; import { expect } from '@playwright/test'; import { sentryTest } from '../../../../utils/fixtures'; -import { hidePage, shouldSkipTracingTest, testingCdnBundle } from '../../../../utils/helpers'; +import { hidePage, shouldSkipTracingTest } from '../../../../utils/helpers'; import { getSpanOp, waitForStreamedSpan } from '../../../../utils/spanUtils'; sentryTest.beforeEach(async ({ browserName, page }) => { - if (shouldSkipTracingTest() || testingCdnBundle() || browserName !== 'chromium') { + if (shouldSkipTracingTest() || browserName !== 'chromium') { sentryTest.skip(); } diff --git a/dev-packages/browser-integration-tests/suites/tracing/metrics/web-vitals-ttfb-streamed/test.ts b/dev-packages/browser-integration-tests/suites/tracing/metrics/web-vitals-ttfb-streamed/test.ts index 73f37f07a291..d410b1eaa356 100644 --- a/dev-packages/browser-integration-tests/suites/tracing/metrics/web-vitals-ttfb-streamed/test.ts +++ b/dev-packages/browser-integration-tests/suites/tracing/metrics/web-vitals-ttfb-streamed/test.ts @@ -1,10 +1,10 @@ import { expect } from '@playwright/test'; import { sentryTest } from '../../../../utils/fixtures'; -import { hidePage, shouldSkipTracingTest, testingCdnBundle } from '../../../../utils/helpers'; +import { hidePage, shouldSkipTracingTest } from '../../../../utils/helpers'; import { getSpanOp, waitForStreamedSpan } from '../../../../utils/spanUtils'; sentryTest.beforeEach(async ({ page }) => { - if (shouldSkipTracingTest() || testingCdnBundle()) { + if (shouldSkipTracingTest()) { sentryTest.skip(); } diff --git a/dev-packages/browser-integration-tests/suites/tracing/no-parent-span-client-report-streamed/test.ts b/dev-packages/browser-integration-tests/suites/tracing/no-parent-span-client-report-streamed/test.ts index 2672d41c17fb..609df6f551a3 100644 --- a/dev-packages/browser-integration-tests/suites/tracing/no-parent-span-client-report-streamed/test.ts +++ b/dev-packages/browser-integration-tests/suites/tracing/no-parent-span-client-report-streamed/test.ts @@ -5,14 +5,13 @@ import { envelopeRequestParser, hidePage, shouldSkipTracingTest, - testingCdnBundle, waitForClientReportRequest, } from '../../../utils/helpers'; sentryTest( 'records no_parent_span client report for fetch requests without an active span', async ({ getLocalTestUrl, page }) => { - sentryTest.skip(shouldSkipTracingTest() || testingCdnBundle()); + sentryTest.skip(shouldSkipTracingTest()); await page.route('http://sentry-test-site.example/api/test', route => { route.fulfill({ diff --git a/dev-packages/browser-integration-tests/suites/tracing/request/fetch-streamed/test.ts b/dev-packages/browser-integration-tests/suites/tracing/request/fetch-streamed/test.ts index 201c3e4979f2..49865da46140 100644 --- a/dev-packages/browser-integration-tests/suites/tracing/request/fetch-streamed/test.ts +++ b/dev-packages/browser-integration-tests/suites/tracing/request/fetch-streamed/test.ts @@ -1,10 +1,10 @@ import { expect } from '@playwright/test'; import { sentryTest } from '../../../../utils/fixtures'; -import { shouldSkipTracingTest, testingCdnBundle } from '../../../../utils/helpers'; +import { shouldSkipTracingTest } from '../../../../utils/helpers'; import { getSpanOp, waitForStreamedSpans } from '../../../../utils/spanUtils'; sentryTest('creates spans for fetch requests', async ({ getLocalTestUrl, page }) => { - sentryTest.skip(shouldSkipTracingTest() || testingCdnBundle()); + sentryTest.skip(shouldSkipTracingTest()); await page.route('http://sentry-test-site.example/*', route => route.fulfill({ body: 'ok' })); @@ -19,7 +19,11 @@ sentryTest('creates spans for fetch requests', async ({ getLocalTestUrl, page }) const allSpans = await spansPromise; const pageloadSpan = allSpans.find(s => getSpanOp(s) === 'pageload'); - const requestSpans = allSpans.filter(s => getSpanOp(s) === 'http.client'); + const requestSpans = allSpans + .filter(s => getSpanOp(s) === 'http.client') + .sort((a, b) => + (a.attributes!['http.url']!.value as string).localeCompare(b.attributes!['http.url']!.value as string), + ); expect(requestSpans).toHaveLength(3); diff --git a/dev-packages/browser-integration-tests/suites/tracing/request/xhr-streamed/test.ts b/dev-packages/browser-integration-tests/suites/tracing/request/xhr-streamed/test.ts index d3f20fd36453..50442d6840ce 100644 --- a/dev-packages/browser-integration-tests/suites/tracing/request/xhr-streamed/test.ts +++ b/dev-packages/browser-integration-tests/suites/tracing/request/xhr-streamed/test.ts @@ -1,10 +1,10 @@ import { expect } from '@playwright/test'; import { sentryTest } from '../../../../utils/fixtures'; -import { shouldSkipTracingTest, testingCdnBundle } from '../../../../utils/helpers'; +import { shouldSkipTracingTest } from '../../../../utils/helpers'; import { getSpanOp, waitForStreamedSpans } from '../../../../utils/spanUtils'; sentryTest('creates spans for XHR requests', async ({ getLocalTestUrl, page }) => { - sentryTest.skip(shouldSkipTracingTest() || testingCdnBundle()); + sentryTest.skip(shouldSkipTracingTest()); await page.route('http://sentry-test-site.example/*', route => route.fulfill({ body: 'ok' })); diff --git a/dev-packages/browser-integration-tests/suites/tracing/setSpanActive-streamed/default/test.ts b/dev-packages/browser-integration-tests/suites/tracing/setSpanActive-streamed/default/test.ts index a144e171a93a..c9f48a4ebefd 100644 --- a/dev-packages/browser-integration-tests/suites/tracing/setSpanActive-streamed/default/test.ts +++ b/dev-packages/browser-integration-tests/suites/tracing/setSpanActive-streamed/default/test.ts @@ -1,10 +1,10 @@ import { expect } from '@playwright/test'; import { sentryTest } from '../../../../utils/fixtures'; -import { shouldSkipTracingTest, testingCdnBundle } from '../../../../utils/helpers'; +import { shouldSkipTracingTest } from '../../../../utils/helpers'; import { waitForStreamedSpans } from '../../../../utils/spanUtils'; sentryTest('sets an inactive span active and adds child spans to it', async ({ getLocalTestUrl, page }) => { - sentryTest.skip(shouldSkipTracingTest() || testingCdnBundle()); + sentryTest.skip(shouldSkipTracingTest()); const spansPromise = waitForStreamedSpans(page, spans => spans.some(s => s.name === 'checkout-flow' && s.is_segment)); diff --git a/dev-packages/browser-integration-tests/suites/tracing/setSpanActive-streamed/nested-parentAlwaysRoot/test.ts b/dev-packages/browser-integration-tests/suites/tracing/setSpanActive-streamed/nested-parentAlwaysRoot/test.ts index 8f5e54e1fba0..f0ce77d4e00c 100644 --- a/dev-packages/browser-integration-tests/suites/tracing/setSpanActive-streamed/nested-parentAlwaysRoot/test.ts +++ b/dev-packages/browser-integration-tests/suites/tracing/setSpanActive-streamed/nested-parentAlwaysRoot/test.ts @@ -1,12 +1,12 @@ import { expect } from '@playwright/test'; import { sentryTest } from '../../../../utils/fixtures'; -import { shouldSkipTracingTest, testingCdnBundle } from '../../../../utils/helpers'; +import { shouldSkipTracingTest } from '../../../../utils/helpers'; import { waitForStreamedSpans } from '../../../../utils/spanUtils'; sentryTest( 'nested calls to setActiveSpanInBrowser with parentSpanIsAlwaysRootSpan=false result in correct parenting', async ({ getLocalTestUrl, page }) => { - sentryTest.skip(shouldSkipTracingTest() || testingCdnBundle()); + sentryTest.skip(shouldSkipTracingTest()); const checkoutSpansPromise = waitForStreamedSpans(page, spans => spans.some(s => s.name === 'checkout-flow' && s.is_segment), diff --git a/dev-packages/browser-integration-tests/suites/tracing/setSpanActive-streamed/nested/test.ts b/dev-packages/browser-integration-tests/suites/tracing/setSpanActive-streamed/nested/test.ts index 1b04553090bc..37a76f81152d 100644 --- a/dev-packages/browser-integration-tests/suites/tracing/setSpanActive-streamed/nested/test.ts +++ b/dev-packages/browser-integration-tests/suites/tracing/setSpanActive-streamed/nested/test.ts @@ -1,12 +1,12 @@ import { expect } from '@playwright/test'; import { sentryTest } from '../../../../utils/fixtures'; -import { shouldSkipTracingTest, testingCdnBundle } from '../../../../utils/helpers'; +import { shouldSkipTracingTest } from '../../../../utils/helpers'; import { waitForStreamedSpans } from '../../../../utils/spanUtils'; sentryTest( 'nested calls to setActiveSpanInBrowser still parent to root span by default', async ({ getLocalTestUrl, page }) => { - sentryTest.skip(shouldSkipTracingTest() || testingCdnBundle()); + sentryTest.skip(shouldSkipTracingTest()); const checkoutSpansPromise = waitForStreamedSpans(page, spans => spans.some(s => s.name === 'checkout-flow' && s.is_segment), diff --git a/dev-packages/browser-integration-tests/suites/tracing/trace-lifetime/navigation-streamed/test.ts b/dev-packages/browser-integration-tests/suites/tracing/trace-lifetime/navigation-streamed/test.ts index 28f3e5039910..3c2cafaa0430 100644 --- a/dev-packages/browser-integration-tests/suites/tracing/trace-lifetime/navigation-streamed/test.ts +++ b/dev-packages/browser-integration-tests/suites/tracing/trace-lifetime/navigation-streamed/test.ts @@ -7,12 +7,11 @@ import { getFirstSentryEnvelopeRequest, shouldSkipFeedbackTest, shouldSkipTracingTest, - testingCdnBundle, } from '../../../../utils/helpers'; import { getSpanOp, waitForStreamedSpan, waitForStreamedSpanEnvelope } from '../../../../utils/spanUtils'; sentryTest('creates a new trace and sample_rand on each navigation', async ({ getLocalTestUrl, page }) => { - sentryTest.skip(shouldSkipTracingTest() || testingCdnBundle()); + sentryTest.skip(shouldSkipTracingTest()); const url = await getLocalTestUrl({ testDir: __dirname }); @@ -76,7 +75,7 @@ sentryTest('creates a new trace and sample_rand on each navigation', async ({ ge }); sentryTest('error after navigation has navigation traceId', async ({ getLocalTestUrl, page }) => { - sentryTest.skip(shouldSkipTracingTest() || testingCdnBundle()); + sentryTest.skip(shouldSkipTracingTest()); const url = await getLocalTestUrl({ testDir: __dirname }); @@ -138,7 +137,7 @@ sentryTest('error after navigation has navigation traceId', async ({ getLocalTes }); sentryTest('error during navigation has new navigation traceId', async ({ getLocalTestUrl, page }) => { - sentryTest.skip(shouldSkipTracingTest() || testingCdnBundle()); + sentryTest.skip(shouldSkipTracingTest()); const url = await getLocalTestUrl({ testDir: __dirname }); @@ -188,7 +187,7 @@ sentryTest('error during navigation has new navigation traceId', async ({ getLoc sentryTest( 'outgoing fetch request during navigation has navigation traceId in headers', async ({ getLocalTestUrl, page }) => { - sentryTest.skip(shouldSkipTracingTest() || testingCdnBundle()); + sentryTest.skip(shouldSkipTracingTest()); const url = await getLocalTestUrl({ testDir: __dirname }); @@ -232,7 +231,7 @@ sentryTest( sentryTest( 'outgoing XHR request during navigation has navigation traceId in headers', async ({ getLocalTestUrl, page }) => { - sentryTest.skip(shouldSkipTracingTest() || testingCdnBundle()); + sentryTest.skip(shouldSkipTracingTest()); const url = await getLocalTestUrl({ testDir: __dirname }); @@ -276,7 +275,7 @@ sentryTest( sentryTest( 'user feedback event after navigation has navigation traceId in headers', async ({ getLocalTestUrl, page }) => { - sentryTest.skip(shouldSkipTracingTest() || shouldSkipFeedbackTest() || testingCdnBundle()); + sentryTest.skip(shouldSkipTracingTest() || shouldSkipFeedbackTest()); const url = await getLocalTestUrl({ testDir: __dirname, handleLazyLoadedFeedback: true }); diff --git a/dev-packages/browser-integration-tests/suites/tracing/trace-lifetime/pageload-streamed/test.ts b/dev-packages/browser-integration-tests/suites/tracing/trace-lifetime/pageload-streamed/test.ts index 1b4458991559..646d4be6fa56 100644 --- a/dev-packages/browser-integration-tests/suites/tracing/trace-lifetime/pageload-streamed/test.ts +++ b/dev-packages/browser-integration-tests/suites/tracing/trace-lifetime/pageload-streamed/test.ts @@ -7,12 +7,11 @@ import { getFirstSentryEnvelopeRequest, shouldSkipFeedbackTest, shouldSkipTracingTest, - testingCdnBundle, } from '../../../../utils/helpers'; import { getSpanOp, waitForStreamedSpan, waitForStreamedSpanEnvelope } from '../../../../utils/spanUtils'; sentryTest('creates a new trace for a navigation after the initial pageload', async ({ getLocalTestUrl, page }) => { - sentryTest.skip(shouldSkipTracingTest() || testingCdnBundle()); + sentryTest.skip(shouldSkipTracingTest()); const pageloadSpanPromise = waitForStreamedSpan(page, span => getSpanOp(span) === 'pageload'); const navigationSpanPromise = waitForStreamedSpan(page, span => getSpanOp(span) === 'navigation'); @@ -41,7 +40,7 @@ sentryTest('creates a new trace for a navigation after the initial pageload', as }); sentryTest('error after pageload has pageload traceId', async ({ getLocalTestUrl, page }) => { - sentryTest.skip(shouldSkipTracingTest() || testingCdnBundle()); + sentryTest.skip(shouldSkipTracingTest()); const pageloadSpanPromise = waitForStreamedSpan(page, span => getSpanOp(span) === 'pageload'); @@ -83,7 +82,7 @@ sentryTest('error after pageload has pageload traceId', async ({ getLocalTestUrl }); sentryTest('error during pageload has pageload traceId', async ({ getLocalTestUrl, page }) => { - sentryTest.skip(shouldSkipTracingTest() || testingCdnBundle()); + sentryTest.skip(shouldSkipTracingTest()); const url = await getLocalTestUrl({ testDir: __dirname }); @@ -126,7 +125,7 @@ sentryTest('error during pageload has pageload traceId', async ({ getLocalTestUr sentryTest( 'outgoing fetch request during pageload has pageload traceId in headers', async ({ getLocalTestUrl, page }) => { - sentryTest.skip(shouldSkipTracingTest() || testingCdnBundle()); + sentryTest.skip(shouldSkipTracingTest()); const url = await getLocalTestUrl({ testDir: __dirname }); @@ -165,7 +164,7 @@ sentryTest( sentryTest( 'outgoing XHR request during pageload has pageload traceId in headers', async ({ getLocalTestUrl, page }) => { - sentryTest.skip(shouldSkipTracingTest() || testingCdnBundle()); + sentryTest.skip(shouldSkipTracingTest()); const url = await getLocalTestUrl({ testDir: __dirname }); @@ -202,7 +201,7 @@ sentryTest( ); sentryTest('user feedback event after pageload has pageload traceId in headers', async ({ getLocalTestUrl, page }) => { - sentryTest.skip(shouldSkipTracingTest() || shouldSkipFeedbackTest() || testingCdnBundle()); + sentryTest.skip(shouldSkipTracingTest() || shouldSkipFeedbackTest()); const url = await getLocalTestUrl({ testDir: __dirname, handleLazyLoadedFeedback: true }); diff --git a/dev-packages/browser-integration-tests/suites/tracing/trace-lifetime/startNewTrace-streamed/test.ts b/dev-packages/browser-integration-tests/suites/tracing/trace-lifetime/startNewTrace-streamed/test.ts index d294efcd2e3b..07ac4b0c8cd3 100644 --- a/dev-packages/browser-integration-tests/suites/tracing/trace-lifetime/startNewTrace-streamed/test.ts +++ b/dev-packages/browser-integration-tests/suites/tracing/trace-lifetime/startNewTrace-streamed/test.ts @@ -1,12 +1,12 @@ import { expect } from '@playwright/test'; import { sentryTest } from '../../../../utils/fixtures'; -import { shouldSkipTracingTest, testingCdnBundle } from '../../../../utils/helpers'; +import { shouldSkipTracingTest } from '../../../../utils/helpers'; import { getSpanOp, waitForStreamedSpan } from '../../../../utils/spanUtils'; sentryTest( 'creates a new trace if `startNewTrace` is called and leaves old trace valid outside the callback', async ({ getLocalTestUrl, page }) => { - sentryTest.skip(shouldSkipTracingTest() || testingCdnBundle()); + sentryTest.skip(shouldSkipTracingTest()); const url = await getLocalTestUrl({ testDir: __dirname }); diff --git a/dev-packages/rollup-utils/plugins/bundlePlugins.mjs b/dev-packages/rollup-utils/plugins/bundlePlugins.mjs index 9d6edd3157c0..782e5d36c443 100644 --- a/dev-packages/rollup-utils/plugins/bundlePlugins.mjs +++ b/dev-packages/rollup-utils/plugins/bundlePlugins.mjs @@ -136,6 +136,8 @@ export function makeTerserPlugin() { '_resolveFilename', // Set on e.g. the shim feedbackIntegration to be able to detect it '_isShim', + // Marker set by `withStreamedSpan()` to tag streamed `beforeSendSpan` callbacks + '_streamed', // This is used in metadata integration '_sentryModuleMetadata', ], diff --git a/packages/browser/src/index.bundle.feedback.ts b/packages/browser/src/index.bundle.feedback.ts index f8d2dfd14014..d722e7c1ea65 100644 --- a/packages/browser/src/index.bundle.feedback.ts +++ b/packages/browser/src/index.bundle.feedback.ts @@ -4,6 +4,7 @@ import { elementTimingIntegrationShim, loggerShim, replayIntegrationShim, + spanStreamingIntegrationShim, } from '@sentry-internal/integration-shims'; import { feedbackAsyncIntegration } from './feedbackAsync'; @@ -20,4 +21,5 @@ export { feedbackAsyncIntegration as feedbackAsyncIntegration, feedbackAsyncIntegration as feedbackIntegration, replayIntegrationShim as replayIntegration, + spanStreamingIntegrationShim as spanStreamingIntegration, }; diff --git a/packages/browser/src/index.bundle.logs.metrics.ts b/packages/browser/src/index.bundle.logs.metrics.ts index f03371dc40b8..415a56cf7cc6 100644 --- a/packages/browser/src/index.bundle.logs.metrics.ts +++ b/packages/browser/src/index.bundle.logs.metrics.ts @@ -2,6 +2,7 @@ import { browserTracingIntegrationShim, feedbackIntegrationShim, replayIntegrationShim, + spanStreamingIntegrationShim, } from '@sentry-internal/integration-shims'; export * from './index.bundle.base'; @@ -16,4 +17,5 @@ export { feedbackIntegrationShim as feedbackAsyncIntegration, feedbackIntegrationShim as feedbackIntegration, replayIntegrationShim as replayIntegration, + spanStreamingIntegrationShim as spanStreamingIntegration, }; diff --git a/packages/browser/src/index.bundle.replay.feedback.ts b/packages/browser/src/index.bundle.replay.feedback.ts index da307df3a951..6ad0bb2bd153 100644 --- a/packages/browser/src/index.bundle.replay.feedback.ts +++ b/packages/browser/src/index.bundle.replay.feedback.ts @@ -3,6 +3,7 @@ import { consoleLoggingIntegrationShim, elementTimingIntegrationShim, loggerShim, + spanStreamingIntegrationShim, } from '@sentry-internal/integration-shims'; import { feedbackAsyncIntegration } from './feedbackAsync'; @@ -18,6 +19,7 @@ export { elementTimingIntegrationShim as elementTimingIntegration, feedbackAsyncIntegration as feedbackAsyncIntegration, feedbackAsyncIntegration as feedbackIntegration, + spanStreamingIntegrationShim as spanStreamingIntegration, }; export { replayIntegration, getReplay } from '@sentry-internal/replay'; diff --git a/packages/browser/src/index.bundle.replay.logs.metrics.ts b/packages/browser/src/index.bundle.replay.logs.metrics.ts index 6ceb7623d77f..02938d0d7063 100644 --- a/packages/browser/src/index.bundle.replay.logs.metrics.ts +++ b/packages/browser/src/index.bundle.replay.logs.metrics.ts @@ -1,4 +1,8 @@ -import { browserTracingIntegrationShim, feedbackIntegrationShim } from '@sentry-internal/integration-shims'; +import { + browserTracingIntegrationShim, + feedbackIntegrationShim, + spanStreamingIntegrationShim, +} from '@sentry-internal/integration-shims'; export * from './index.bundle.base'; @@ -13,4 +17,5 @@ export { browserTracingIntegrationShim as browserTracingIntegration, feedbackIntegrationShim as feedbackAsyncIntegration, feedbackIntegrationShim as feedbackIntegration, + spanStreamingIntegrationShim as spanStreamingIntegration, }; diff --git a/packages/browser/src/index.bundle.replay.ts b/packages/browser/src/index.bundle.replay.ts index e305596f190c..e9ec7e99132e 100644 --- a/packages/browser/src/index.bundle.replay.ts +++ b/packages/browser/src/index.bundle.replay.ts @@ -4,6 +4,7 @@ import { elementTimingIntegrationShim, feedbackIntegrationShim, loggerShim, + spanStreamingIntegrationShim, } from '@sentry-internal/integration-shims'; export * from './index.bundle.base'; @@ -18,4 +19,5 @@ export { elementTimingIntegrationShim as elementTimingIntegration, feedbackIntegrationShim as feedbackAsyncIntegration, feedbackIntegrationShim as feedbackIntegration, + spanStreamingIntegrationShim as spanStreamingIntegration, }; diff --git a/packages/browser/src/index.bundle.tracing.logs.metrics.ts b/packages/browser/src/index.bundle.tracing.logs.metrics.ts index 0c5c4c0a81cd..19b8118a5c04 100644 --- a/packages/browser/src/index.bundle.tracing.logs.metrics.ts +++ b/packages/browser/src/index.bundle.tracing.logs.metrics.ts @@ -30,6 +30,8 @@ export { elementTimingIntegration } from '@sentry-internal/browser-utils'; export { reportPageLoaded } from './tracing/reportPageLoaded'; export { setActiveSpanInBrowser } from './tracing/setActiveSpan'; +export { spanStreamingIntegration } from './integrations/spanstreaming'; + export { feedbackIntegrationShim as feedbackAsyncIntegration, feedbackIntegrationShim as feedbackIntegration, diff --git a/packages/browser/src/index.bundle.tracing.replay.feedback.logs.metrics.ts b/packages/browser/src/index.bundle.tracing.replay.feedback.logs.metrics.ts index 5fb7c306cc87..5a531f6b33a9 100644 --- a/packages/browser/src/index.bundle.tracing.replay.feedback.logs.metrics.ts +++ b/packages/browser/src/index.bundle.tracing.replay.feedback.logs.metrics.ts @@ -30,6 +30,8 @@ export { elementTimingIntegration } from '@sentry-internal/browser-utils'; export { reportPageLoaded } from './tracing/reportPageLoaded'; export { setActiveSpanInBrowser } from './tracing/setActiveSpan'; +export { spanStreamingIntegration } from './integrations/spanstreaming'; + export { getFeedback, sendFeedback } from '@sentry-internal/feedback'; export { feedbackAsyncIntegration as feedbackAsyncIntegration, feedbackAsyncIntegration as feedbackIntegration }; diff --git a/packages/browser/src/index.bundle.tracing.replay.feedback.ts b/packages/browser/src/index.bundle.tracing.replay.feedback.ts index 9d9098b5be3d..47b43d48f376 100644 --- a/packages/browser/src/index.bundle.tracing.replay.feedback.ts +++ b/packages/browser/src/index.bundle.tracing.replay.feedback.ts @@ -36,6 +36,8 @@ export { setActiveSpanInBrowser } from './tracing/setActiveSpan'; export { reportPageLoaded } from './tracing/reportPageLoaded'; +export { spanStreamingIntegration } from './integrations/spanstreaming'; + export { getFeedback, sendFeedback } from '@sentry-internal/feedback'; export { feedbackAsyncIntegration as feedbackAsyncIntegration, feedbackAsyncIntegration as feedbackIntegration }; diff --git a/packages/browser/src/index.bundle.tracing.replay.logs.metrics.ts b/packages/browser/src/index.bundle.tracing.replay.logs.metrics.ts index a000d456360b..45c7299bf436 100644 --- a/packages/browser/src/index.bundle.tracing.replay.logs.metrics.ts +++ b/packages/browser/src/index.bundle.tracing.replay.logs.metrics.ts @@ -30,6 +30,8 @@ export { elementTimingIntegration } from '@sentry-internal/browser-utils'; export { reportPageLoaded } from './tracing/reportPageLoaded'; export { setActiveSpanInBrowser } from './tracing/setActiveSpan'; +export { spanStreamingIntegration } from './integrations/spanstreaming'; + export { feedbackIntegrationShim as feedbackAsyncIntegration, feedbackIntegrationShim as feedbackIntegration }; export { replayIntegration, getReplay } from '@sentry-internal/replay'; diff --git a/packages/browser/src/index.bundle.tracing.replay.ts b/packages/browser/src/index.bundle.tracing.replay.ts index 496aacf348b9..63eb9a81c24a 100644 --- a/packages/browser/src/index.bundle.tracing.replay.ts +++ b/packages/browser/src/index.bundle.tracing.replay.ts @@ -35,6 +35,8 @@ export { elementTimingIntegrationShim as elementTimingIntegration }; export { reportPageLoaded } from './tracing/reportPageLoaded'; export { setActiveSpanInBrowser } from './tracing/setActiveSpan'; +export { spanStreamingIntegration } from './integrations/spanstreaming'; + export { feedbackIntegrationShim as feedbackAsyncIntegration, feedbackIntegrationShim as feedbackIntegration }; export { replayIntegration, getReplay } from '@sentry-internal/replay'; diff --git a/packages/browser/src/index.bundle.tracing.ts b/packages/browser/src/index.bundle.tracing.ts index 64126c101189..a385ad4b0792 100644 --- a/packages/browser/src/index.bundle.tracing.ts +++ b/packages/browser/src/index.bundle.tracing.ts @@ -37,6 +37,8 @@ export { setActiveSpanInBrowser } from './tracing/setActiveSpan'; export { reportPageLoaded } from './tracing/reportPageLoaded'; +export { spanStreamingIntegration } from './integrations/spanstreaming'; + export { feedbackIntegrationShim as feedbackAsyncIntegration, feedbackIntegrationShim as feedbackIntegration, diff --git a/packages/browser/src/index.bundle.ts b/packages/browser/src/index.bundle.ts index 7dfcd30ad2ef..b168b7d0189f 100644 --- a/packages/browser/src/index.bundle.ts +++ b/packages/browser/src/index.bundle.ts @@ -5,6 +5,7 @@ import { feedbackIntegrationShim, loggerShim, replayIntegrationShim, + spanStreamingIntegrationShim, } from '@sentry-internal/integration-shims'; export * from './index.bundle.base'; @@ -18,4 +19,5 @@ export { feedbackIntegrationShim as feedbackAsyncIntegration, feedbackIntegrationShim as feedbackIntegration, replayIntegrationShim as replayIntegration, + spanStreamingIntegrationShim as spanStreamingIntegration, }; diff --git a/packages/browser/test/index.bundle.feedback.test.ts b/packages/browser/test/index.bundle.feedback.test.ts index 5b72e0566236..767db76fd3ce 100644 --- a/packages/browser/test/index.bundle.feedback.test.ts +++ b/packages/browser/test/index.bundle.feedback.test.ts @@ -3,6 +3,7 @@ import { consoleLoggingIntegrationShim, loggerShim, replayIntegrationShim, + spanStreamingIntegrationShim, } from '@sentry-internal/integration-shims'; import { describe, expect, it } from 'vitest'; import { feedbackAsyncIntegration } from '../src'; @@ -14,6 +15,7 @@ describe('index.bundle.feedback', () => { expect(FeedbackBundle.feedbackAsyncIntegration).toBe(feedbackAsyncIntegration); expect(FeedbackBundle.feedbackIntegration).toBe(feedbackAsyncIntegration); expect(FeedbackBundle.replayIntegration).toBe(replayIntegrationShim); + expect(FeedbackBundle.spanStreamingIntegration).toBe(spanStreamingIntegrationShim); expect(FeedbackBundle.logger).toBe(loggerShim); expect(FeedbackBundle.consoleLoggingIntegration).toBe(consoleLoggingIntegrationShim); diff --git a/packages/browser/test/index.bundle.logs.metrics.test.ts b/packages/browser/test/index.bundle.logs.metrics.test.ts index 98cf359a7266..7d450dc1ced0 100644 --- a/packages/browser/test/index.bundle.logs.metrics.test.ts +++ b/packages/browser/test/index.bundle.logs.metrics.test.ts @@ -1,4 +1,5 @@ import { logger as coreLogger, metrics as coreMetrics } from '@sentry/core'; +import { spanStreamingIntegrationShim } from '@sentry-internal/integration-shims'; import { describe, expect, it } from 'vitest'; import * as LogsMetricsBundle from '../src/index.bundle.logs.metrics'; @@ -6,5 +7,6 @@ describe('index.bundle.logs.metrics', () => { it('has correct exports', () => { expect(LogsMetricsBundle.logger).toBe(coreLogger); expect(LogsMetricsBundle.metrics).toBe(coreMetrics); + expect(LogsMetricsBundle.spanStreamingIntegration).toBe(spanStreamingIntegrationShim); }); }); diff --git a/packages/browser/test/index.bundle.replay.feedback.test.ts b/packages/browser/test/index.bundle.replay.feedback.test.ts index b92c2c41b731..9f65458e5824 100644 --- a/packages/browser/test/index.bundle.replay.feedback.test.ts +++ b/packages/browser/test/index.bundle.replay.feedback.test.ts @@ -2,6 +2,7 @@ import { browserTracingIntegrationShim, consoleLoggingIntegrationShim, loggerShim, + spanStreamingIntegrationShim, } from '@sentry-internal/integration-shims'; import { describe, expect, it } from 'vitest'; import { feedbackAsyncIntegration, replayIntegration } from '../src'; @@ -13,6 +14,7 @@ describe('index.bundle.replay.feedback', () => { expect(ReplayFeedbackBundle.feedbackAsyncIntegration).toBe(feedbackAsyncIntegration); expect(ReplayFeedbackBundle.feedbackIntegration).toBe(feedbackAsyncIntegration); expect(ReplayFeedbackBundle.replayIntegration).toBe(replayIntegration); + expect(ReplayFeedbackBundle.spanStreamingIntegration).toBe(spanStreamingIntegrationShim); expect(ReplayFeedbackBundle.logger).toBe(loggerShim); expect(ReplayFeedbackBundle.consoleLoggingIntegration).toBe(consoleLoggingIntegrationShim); diff --git a/packages/browser/test/index.bundle.replay.logs.metrics.test.ts b/packages/browser/test/index.bundle.replay.logs.metrics.test.ts index b031510282f4..d6bb995fae09 100644 --- a/packages/browser/test/index.bundle.replay.logs.metrics.test.ts +++ b/packages/browser/test/index.bundle.replay.logs.metrics.test.ts @@ -1,5 +1,9 @@ import { logger as coreLogger, metrics as coreMetrics } from '@sentry/core'; -import { browserTracingIntegrationShim, feedbackIntegrationShim } from '@sentry-internal/integration-shims'; +import { + browserTracingIntegrationShim, + feedbackIntegrationShim, + spanStreamingIntegrationShim, +} from '@sentry-internal/integration-shims'; import { describe, expect, it } from 'vitest'; import { replayIntegration } from '../src'; import * as ReplayLogsMetricsBundle from '../src/index.bundle.replay.logs.metrics'; @@ -10,6 +14,7 @@ describe('index.bundle.replay.logs.metrics', () => { expect(ReplayLogsMetricsBundle.feedbackAsyncIntegration).toBe(feedbackIntegrationShim); expect(ReplayLogsMetricsBundle.feedbackIntegration).toBe(feedbackIntegrationShim); expect(ReplayLogsMetricsBundle.replayIntegration).toBe(replayIntegration); + expect(ReplayLogsMetricsBundle.spanStreamingIntegration).toBe(spanStreamingIntegrationShim); expect(ReplayLogsMetricsBundle.logger).toBe(coreLogger); expect(ReplayLogsMetricsBundle.metrics).toBe(coreMetrics); diff --git a/packages/browser/test/index.bundle.replay.test.ts b/packages/browser/test/index.bundle.replay.test.ts index 2bfc2ffcf7fc..1d5dc86d274d 100644 --- a/packages/browser/test/index.bundle.replay.test.ts +++ b/packages/browser/test/index.bundle.replay.test.ts @@ -3,6 +3,7 @@ import { consoleLoggingIntegrationShim, feedbackIntegrationShim, loggerShim, + spanStreamingIntegrationShim, } from '@sentry-internal/integration-shims'; import { describe, expect, it } from 'vitest'; import { replayIntegration } from '../src'; @@ -14,6 +15,7 @@ describe('index.bundle.replay', () => { expect(ReplayBundle.feedbackAsyncIntegration).toBe(feedbackIntegrationShim); expect(ReplayBundle.feedbackIntegration).toBe(feedbackIntegrationShim); expect(ReplayBundle.replayIntegration).toBe(replayIntegration); + expect(ReplayBundle.spanStreamingIntegration).toBe(spanStreamingIntegrationShim); expect(ReplayBundle.logger).toBe(loggerShim); expect(ReplayBundle.consoleLoggingIntegration).toBe(consoleLoggingIntegrationShim); diff --git a/packages/browser/test/index.bundle.test.ts b/packages/browser/test/index.bundle.test.ts index 6b29fea23aeb..d099b5545fc7 100644 --- a/packages/browser/test/index.bundle.test.ts +++ b/packages/browser/test/index.bundle.test.ts @@ -4,6 +4,7 @@ import { feedbackIntegrationShim, loggerShim, replayIntegrationShim, + spanStreamingIntegrationShim, } from '@sentry-internal/integration-shims'; import { describe, expect, it } from 'vitest'; import * as Bundle from '../src/index.bundle'; @@ -14,6 +15,7 @@ describe('index.bundle', () => { expect(Bundle.feedbackAsyncIntegration).toBe(feedbackIntegrationShim); expect(Bundle.feedbackIntegration).toBe(feedbackIntegrationShim); expect(Bundle.replayIntegration).toBe(replayIntegrationShim); + expect(Bundle.spanStreamingIntegration).toBe(spanStreamingIntegrationShim); expect(Bundle.logger).toBe(loggerShim); expect(Bundle.consoleLoggingIntegration).toBe(consoleLoggingIntegrationShim); diff --git a/packages/browser/test/index.bundle.tracing.logs.metrics.test.ts b/packages/browser/test/index.bundle.tracing.logs.metrics.test.ts index 19b3701ebf77..483a4ae8a1f5 100644 --- a/packages/browser/test/index.bundle.tracing.logs.metrics.test.ts +++ b/packages/browser/test/index.bundle.tracing.logs.metrics.test.ts @@ -1,7 +1,7 @@ import { logger as coreLogger, metrics as coreMetrics } from '@sentry/core'; import { feedbackIntegrationShim, replayIntegrationShim } from '@sentry-internal/integration-shims'; import { describe, expect, it } from 'vitest'; -import { browserTracingIntegration } from '../src'; +import { browserTracingIntegration, spanStreamingIntegration } from '../src'; import * as TracingLogsMetricsBundle from '../src/index.bundle.tracing.logs.metrics'; describe('index.bundle.tracing.logs.metrics', () => { @@ -10,6 +10,7 @@ describe('index.bundle.tracing.logs.metrics', () => { expect(TracingLogsMetricsBundle.feedbackAsyncIntegration).toBe(feedbackIntegrationShim); expect(TracingLogsMetricsBundle.feedbackIntegration).toBe(feedbackIntegrationShim); expect(TracingLogsMetricsBundle.replayIntegration).toBe(replayIntegrationShim); + expect(TracingLogsMetricsBundle.spanStreamingIntegration).toBe(spanStreamingIntegration); expect(TracingLogsMetricsBundle.logger).toBe(coreLogger); expect(TracingLogsMetricsBundle.metrics).toBe(coreMetrics); diff --git a/packages/browser/test/index.bundle.tracing.replay.feedback.logs.metrics.test.ts b/packages/browser/test/index.bundle.tracing.replay.feedback.logs.metrics.test.ts index 4a7cac9f53d6..0c474b195bc8 100644 --- a/packages/browser/test/index.bundle.tracing.replay.feedback.logs.metrics.test.ts +++ b/packages/browser/test/index.bundle.tracing.replay.feedback.logs.metrics.test.ts @@ -1,6 +1,11 @@ import { logger as coreLogger, metrics as coreMetrics } from '@sentry/core'; import { describe, expect, it } from 'vitest'; -import { browserTracingIntegration, feedbackAsyncIntegration, replayIntegration } from '../src'; +import { + browserTracingIntegration, + feedbackAsyncIntegration, + replayIntegration, + spanStreamingIntegration, +} from '../src'; import * as TracingReplayFeedbackLogsMetricsBundle from '../src/index.bundle.tracing.replay.feedback.logs.metrics'; describe('index.bundle.tracing.replay.feedback.logs.metrics', () => { @@ -9,6 +14,7 @@ describe('index.bundle.tracing.replay.feedback.logs.metrics', () => { expect(TracingReplayFeedbackLogsMetricsBundle.feedbackAsyncIntegration).toBe(feedbackAsyncIntegration); expect(TracingReplayFeedbackLogsMetricsBundle.feedbackIntegration).toBe(feedbackAsyncIntegration); expect(TracingReplayFeedbackLogsMetricsBundle.replayIntegration).toBe(replayIntegration); + expect(TracingReplayFeedbackLogsMetricsBundle.spanStreamingIntegration).toBe(spanStreamingIntegration); expect(TracingReplayFeedbackLogsMetricsBundle.logger).toBe(coreLogger); expect(TracingReplayFeedbackLogsMetricsBundle.metrics).toBe(coreMetrics); diff --git a/packages/browser/test/index.bundle.tracing.replay.feedback.test.ts b/packages/browser/test/index.bundle.tracing.replay.feedback.test.ts index 1cb7a18fe8bd..fe60d079dc41 100644 --- a/packages/browser/test/index.bundle.tracing.replay.feedback.test.ts +++ b/packages/browser/test/index.bundle.tracing.replay.feedback.test.ts @@ -1,6 +1,11 @@ import { consoleLoggingIntegrationShim, loggerShim } from '@sentry-internal/integration-shims'; import { describe, expect, it } from 'vitest'; -import { browserTracingIntegration, feedbackAsyncIntegration, replayIntegration } from '../src'; +import { + browserTracingIntegration, + feedbackAsyncIntegration, + replayIntegration, + spanStreamingIntegration, +} from '../src'; import * as TracingReplayFeedbackBundle from '../src/index.bundle.tracing.replay.feedback'; describe('index.bundle.tracing.replay.feedback', () => { @@ -9,6 +14,7 @@ describe('index.bundle.tracing.replay.feedback', () => { expect(TracingReplayFeedbackBundle.feedbackAsyncIntegration).toBe(feedbackAsyncIntegration); expect(TracingReplayFeedbackBundle.feedbackIntegration).toBe(feedbackAsyncIntegration); expect(TracingReplayFeedbackBundle.replayIntegration).toBe(replayIntegration); + expect(TracingReplayFeedbackBundle.spanStreamingIntegration).toBe(spanStreamingIntegration); expect(TracingReplayFeedbackBundle.logger).toBe(loggerShim); expect(TracingReplayFeedbackBundle.consoleLoggingIntegration).toBe(consoleLoggingIntegrationShim); diff --git a/packages/browser/test/index.bundle.tracing.replay.logs.metrics.test.ts b/packages/browser/test/index.bundle.tracing.replay.logs.metrics.test.ts index b47c00f4b510..4848de24caea 100644 --- a/packages/browser/test/index.bundle.tracing.replay.logs.metrics.test.ts +++ b/packages/browser/test/index.bundle.tracing.replay.logs.metrics.test.ts @@ -1,7 +1,7 @@ import { logger as coreLogger, metrics as coreMetrics } from '@sentry/core'; import { feedbackIntegrationShim } from '@sentry-internal/integration-shims'; import { describe, expect, it } from 'vitest'; -import { browserTracingIntegration, replayIntegration } from '../src'; +import { browserTracingIntegration, replayIntegration, spanStreamingIntegration } from '../src'; import * as TracingReplayLogsMetricsBundle from '../src/index.bundle.tracing.replay.logs.metrics'; describe('index.bundle.tracing.replay.logs.metrics', () => { @@ -10,6 +10,7 @@ describe('index.bundle.tracing.replay.logs.metrics', () => { expect(TracingReplayLogsMetricsBundle.feedbackAsyncIntegration).toBe(feedbackIntegrationShim); expect(TracingReplayLogsMetricsBundle.feedbackIntegration).toBe(feedbackIntegrationShim); expect(TracingReplayLogsMetricsBundle.replayIntegration).toBe(replayIntegration); + expect(TracingReplayLogsMetricsBundle.spanStreamingIntegration).toBe(spanStreamingIntegration); expect(TracingReplayLogsMetricsBundle.logger).toBe(coreLogger); expect(TracingReplayLogsMetricsBundle.metrics).toBe(coreMetrics); diff --git a/packages/browser/test/index.bundle.tracing.replay.test.ts b/packages/browser/test/index.bundle.tracing.replay.test.ts index 90c82f32cb6b..1cdae8214a20 100644 --- a/packages/browser/test/index.bundle.tracing.replay.test.ts +++ b/packages/browser/test/index.bundle.tracing.replay.test.ts @@ -1,6 +1,6 @@ import { consoleLoggingIntegrationShim, feedbackIntegrationShim, loggerShim } from '@sentry-internal/integration-shims'; import { describe, expect, it } from 'vitest'; -import { browserTracingIntegration, replayIntegration } from '../src'; +import { browserTracingIntegration, replayIntegration, spanStreamingIntegration } from '../src'; import * as TracingReplayBundle from '../src/index.bundle.tracing.replay'; describe('index.bundle.tracing.replay', () => { @@ -9,6 +9,7 @@ describe('index.bundle.tracing.replay', () => { expect(TracingReplayBundle.feedbackAsyncIntegration).toBe(feedbackIntegrationShim); expect(TracingReplayBundle.feedbackIntegration).toBe(feedbackIntegrationShim); expect(TracingReplayBundle.replayIntegration).toBe(replayIntegration); + expect(TracingReplayBundle.spanStreamingIntegration).toBe(spanStreamingIntegration); expect(TracingReplayBundle.logger).toBe(loggerShim); expect(TracingReplayBundle.consoleLoggingIntegration).toBe(consoleLoggingIntegrationShim); diff --git a/packages/browser/test/index.bundle.tracing.test.ts b/packages/browser/test/index.bundle.tracing.test.ts index 2ec58fcb9e48..75fb658c860d 100644 --- a/packages/browser/test/index.bundle.tracing.test.ts +++ b/packages/browser/test/index.bundle.tracing.test.ts @@ -5,7 +5,7 @@ import { replayIntegrationShim, } from '@sentry-internal/integration-shims'; import { describe, expect, it } from 'vitest'; -import { browserTracingIntegration } from '../src'; +import { browserTracingIntegration, spanStreamingIntegration } from '../src'; import * as TracingBundle from '../src/index.bundle.tracing'; describe('index.bundle.tracing', () => { @@ -14,6 +14,7 @@ describe('index.bundle.tracing', () => { expect(TracingBundle.feedbackAsyncIntegration).toBe(feedbackIntegrationShim); expect(TracingBundle.feedbackIntegration).toBe(feedbackIntegrationShim); expect(TracingBundle.replayIntegration).toBe(replayIntegrationShim); + expect(TracingBundle.spanStreamingIntegration).toBe(spanStreamingIntegration); expect(TracingBundle.logger).toBe(loggerShim); expect(TracingBundle.consoleLoggingIntegration).toBe(consoleLoggingIntegrationShim); diff --git a/packages/integration-shims/src/SpanStreaming.ts b/packages/integration-shims/src/SpanStreaming.ts new file mode 100644 index 000000000000..7b445f086145 --- /dev/null +++ b/packages/integration-shims/src/SpanStreaming.ts @@ -0,0 +1,17 @@ +import { consoleSandbox, defineIntegration } from '@sentry/core'; + +/** + * This is a shim for the SpanStreaming integration. + * It is needed in order for the CDN bundles to continue working when users add/remove span streaming + * from it, without changing their config. This is necessary for the loader mechanism. + */ +export const spanStreamingIntegrationShim = defineIntegration(() => { + consoleSandbox(() => { + // eslint-disable-next-line no-console + console.warn('You are using spanStreamingIntegration() even though this bundle does not include tracing.'); + }); + + return { + name: 'SpanStreaming', + }; +}); diff --git a/packages/integration-shims/src/index.ts b/packages/integration-shims/src/index.ts index 4cabb8a5e36f..fa820ad55145 100644 --- a/packages/integration-shims/src/index.ts +++ b/packages/integration-shims/src/index.ts @@ -4,3 +4,4 @@ export { browserTracingIntegrationShim } from './BrowserTracing'; export { launchDarklyIntegrationShim, buildLaunchDarklyFlagUsedHandlerShim } from './launchDarkly'; export { elementTimingIntegrationShim } from './ElementTiming'; export { loggerShim, consoleLoggingIntegrationShim } from './logs'; +export { spanStreamingIntegrationShim } from './SpanStreaming'; From 617fede56fd4476cf64e99bd1af5665d1881d29c Mon Sep 17 00:00:00 2001 From: isaacs Date: Fri, 17 Apr 2026 12:35:28 +0200 Subject: [PATCH 20/41] chore: prevent test from creating zombie process (#20392) An interval is set to keep the scenario file running in the vercel/sigterm-flush integration test. Ensure that the interval is cleared once the SIGTERM signal arrives, so that the scenario process can exit normally. --- .../suites/vercel/sigterm-flush/scenario.ts | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/dev-packages/node-integration-tests/suites/vercel/sigterm-flush/scenario.ts b/dev-packages/node-integration-tests/suites/vercel/sigterm-flush/scenario.ts index 51e1b4d09ccf..21aa0deb1a1e 100644 --- a/dev-packages/node-integration-tests/suites/vercel/sigterm-flush/scenario.ts +++ b/dev-packages/node-integration-tests/suites/vercel/sigterm-flush/scenario.ts @@ -33,4 +33,7 @@ Sentry.captureMessage('SIGTERM flush message'); console.log('READY'); // Keep the process alive so the integration test can send SIGTERM. -setInterval(() => undefined, 1_000); +const interval = setInterval(() => undefined, 10_000); + +// allow graceful exit once the SIGTERM arrives. +process.once('SIGTERM', () => clearInterval(interval)); From 215c16ef152435c3219fb9e688d3c33b9d839521 Mon Sep 17 00:00:00 2001 From: Francesco Gringl-Novy Date: Wed, 22 Apr 2026 09:23:09 +0200 Subject: [PATCH 21/41] chore(e2e-tests): Use tarball symlinks for E2E tests instead of verdaccio (#20386) This PR completely removes verdaccio in favor of referencing the built tarballs directly. We rely on pnpm overrides which we inject into the test app to ensure that also transitive dependencies are correct. This is basically instant and reduces about 20s of verdaccio/registry preparation time we used to have before each e2e test app. ## How it works * We use a reference to a tarball for internal dependencies in our e2e tests. these are virtual files in /packed directory, e.g. `"@sentry/browser": "file:../../packed/sentry-browser-packed.tgz",` * `yarn test:prepare` will create/update those packed files with symlinks to the current tarballs (which have the version in their filename so we cannot just link to them directly consistently) * Additionally, we adjust the created test apps in the tmp dir by adding a `pnpm.overrides` section for all our apps pointing them to the packed folder, to ensure we get these as transitive dependencies as well Locally, you can continue to use `yarn test:run test-app-name` and it will do all of that under the hood, nothing else needed (except to generate the tarballs, as before). On CI I adjusted the places to run the necessary pieces in addition to ensure everything works as expected. ## Example generated test app It effectively creates a package.json for the test app that looks like this: ```json { "name": "node-hapi", "version": "1.0.0", "private": true, "scripts": { "build": "tsc", "start": "node src/app.js", "test": "playwright test", "clean": "npx rimraf node_modules pnpm-lock.yaml", "test:build": "pnpm install", "test:assert": "pnpm test" }, "dependencies": { "@hapi/boom": "10.0.1", "@hapi/hapi": "21.3.10", "@sentry/node": "file:../../packed/sentry-node-packed.tgz" }, "devDependencies": { "@playwright/test": "~1.56.0", "@sentry-internal/test-utils": "link:~/my-app/dev-packages/test-utils" }, "volta": { "extends": "~/my-app/dev-packages/e2e-tests/package.json" }, "pnpm": { "overrides": { "@sentry-internal/browser-utils": "file:~/my-app/dev-packages/e2e-tests/packed/sentry-internal-browser-utils-packed.tgz", "@sentry-internal/eslint-config-sdk": "file:~/my-app/dev-packages/e2e-tests/packed/sentry-internal-eslint-config-sdk-packed.tgz", "@sentry-internal/eslint-plugin-sdk": "file:~/my-app/dev-packages/e2e-tests/packed/sentry-internal-eslint-plugin-sdk-packed.tgz", "@sentry-internal/feedback": "file:~/my-app/dev-packages/e2e-tests/packed/sentry-internal-feedback-packed.tgz", "@sentry-internal/replay": "file:~/my-app/dev-packages/e2e-tests/packed/sentry-internal-replay-packed.tgz", "@sentry-internal/replay-canvas": "file:~/my-app/dev-packages/e2e-tests/packed/sentry-internal-replay-canvas-packed.tgz", "@sentry-internal/typescript": "file:~/my-app/dev-packages/e2e-tests/packed/sentry-internal-typescript-packed.tgz", "@sentry/angular": "file:~/my-app/dev-packages/e2e-tests/packed/sentry-angular-packed.tgz", "@sentry/astro": "file:~/my-app/dev-packages/e2e-tests/packed/sentry-astro-packed.tgz", "@sentry/aws-serverless": "file:~/my-app/dev-packages/e2e-tests/packed/sentry-aws-serverless-packed.tgz", "@sentry/browser": "file:~/my-app/dev-packages/e2e-tests/packed/sentry-browser-packed.tgz", "@sentry/bun": "file:~/my-app/dev-packages/e2e-tests/packed/sentry-bun-packed.tgz", "@sentry/cloudflare": "file:~/my-app/dev-packages/e2e-tests/packed/sentry-cloudflare-packed.tgz", "@sentry/core": "file:~/my-app/dev-packages/e2e-tests/packed/sentry-core-packed.tgz", "@sentry/deno": "file:~/my-app/dev-packages/e2e-tests/packed/sentry-deno-packed.tgz", "@sentry/effect": "file:~/my-app/dev-packages/e2e-tests/packed/sentry-effect-packed.tgz", "@sentry/elysia": "file:~/my-app/dev-packages/e2e-tests/packed/sentry-elysia-packed.tgz", "@sentry/ember": "file:~/my-app/dev-packages/e2e-tests/packed/sentry-ember-packed.tgz", "@sentry/gatsby": "file:~/my-app/dev-packages/e2e-tests/packed/sentry-gatsby-packed.tgz", "@sentry/google-cloud-serverless": "file:~/my-app/dev-packages/e2e-tests/packed/sentry-google-cloud-serverless-packed.tgz", "@sentry/hono": "file:~/my-app/dev-packages/e2e-tests/packed/sentry-hono-packed.tgz", "@sentry/nestjs": "file:~/my-app/dev-packages/e2e-tests/packed/sentry-nestjs-packed.tgz", "@sentry/nextjs": "file:~/my-app/dev-packages/e2e-tests/packed/sentry-nextjs-packed.tgz", "@sentry/node": "file:~/my-app/dev-packages/e2e-tests/packed/sentry-node-packed.tgz", "@sentry/node-core": "file:~/my-app/dev-packages/e2e-tests/packed/sentry-node-core-packed.tgz", "@sentry/node-native": "file:~/my-app/dev-packages/e2e-tests/packed/sentry-node-native-packed.tgz", "@sentry/nuxt": "file:~/my-app/dev-packages/e2e-tests/packed/sentry-nuxt-packed.tgz", "@sentry/opentelemetry": "file:~/my-app/dev-packages/e2e-tests/packed/sentry-opentelemetry-packed.tgz", "@sentry/profiling-node": "file:~/my-app/dev-packages/e2e-tests/packed/sentry-profiling-node-packed.tgz", "@sentry/react": "file:~/my-app/dev-packages/e2e-tests/packed/sentry-react-packed.tgz", "@sentry/react-router": "file:~/my-app/dev-packages/e2e-tests/packed/sentry-react-router-packed.tgz", "@sentry/remix": "file:~/my-app/dev-packages/e2e-tests/packed/sentry-remix-packed.tgz", "@sentry/solid": "file:~/my-app/dev-packages/e2e-tests/packed/sentry-solid-packed.tgz", "@sentry/solidstart": "file:~/my-app/dev-packages/e2e-tests/packed/sentry-solidstart-packed.tgz", "@sentry/svelte": "file:~/my-app/dev-packages/e2e-tests/packed/sentry-svelte-packed.tgz", "@sentry/sveltekit": "file:~/my-app/dev-packages/e2e-tests/packed/sentry-sveltekit-packed.tgz", "@sentry/tanstackstart": "file:~/my-app/dev-packages/e2e-tests/packed/sentry-tanstackstart-packed.tgz", "@sentry/tanstackstart-react": "file:~/my-app/dev-packages/e2e-tests/packed/sentry-tanstackstart-react-packed.tgz", "@sentry/types": "file:~/my-app/dev-packages/e2e-tests/packed/sentry-types-packed.tgz", "@sentry/vercel-edge": "file:~/my-app/dev-packages/e2e-tests/packed/sentry-vercel-edge-packed.tgz", "@sentry/vue": "file:~/my-app/dev-packages/e2e-tests/packed/sentry-vue-packed.tgz", "@sentry/wasm": "file:~/my-app/dev-packages/e2e-tests/packed/sentry-wasm-packed.tgz" } } } ``` then installs this normally with the regular registry. --- .agents/skills/e2e/SKILL.md | 2 +- .github/workflows/build.yml | 28 +- .github/workflows/canary.yml | 14 +- .gitignore | 3 - dev-packages/e2e-tests/.gitignore | 1 + dev-packages/e2e-tests/ciCopyToTemp.ts | 6 +- dev-packages/e2e-tests/ciPnpmOverrides.ts | 20 + dev-packages/e2e-tests/lib/copyToTemp.ts | 29 +- .../e2e-tests/lib/packedTarballUtils.ts | 60 + dev-packages/e2e-tests/lib/pnpmOverrides.ts | 43 + dev-packages/e2e-tests/lib/publishPackages.ts | 52 - .../lib/syncPackedTarballSymlinks.ts | 65 + dev-packages/e2e-tests/package.json | 14 +- dev-packages/e2e-tests/prepare.ts | 4 +- dev-packages/e2e-tests/registrySetup.ts | 140 --- dev-packages/e2e-tests/run.ts | 110 +- .../test-applications/angular-17/.npmrc | 2 - .../test-applications/angular-17/package.json | 4 +- .../test-applications/angular-18/.npmrc | 2 - .../test-applications/angular-18/package.json | 4 +- .../test-applications/angular-19/.npmrc | 2 - .../test-applications/angular-19/package.json | 4 +- .../test-applications/angular-20/.npmrc | 2 - .../test-applications/angular-20/package.json | 4 +- .../test-applications/angular-21/.npmrc | 2 - .../test-applications/angular-21/package.json | 4 +- .../test-applications/astro-4/.npmrc | 2 - .../test-applications/astro-4/package.json | 2 +- .../astro-5-cf-workers/.npmrc | 2 - .../astro-5-cf-workers/package.json | 4 +- .../test-applications/astro-5/.npmrc | 2 - .../test-applications/astro-5/package.json | 2 +- .../astro-6-cf-workers/.npmrc | 2 - .../astro-6-cf-workers/package.json | 4 +- .../test-applications/astro-6/.npmrc | 2 - .../test-applications/astro-6/package.json | 2 +- .../test-applications/aws-serverless/.npmrc | 2 - .../aws-serverless/package.json | 6 + .../test-applications/browser-mfe-vite/.npmrc | 2 - .../apps/mfe-header/package.json | 2 +- .../apps/mfe-one/package.json | 2 +- .../browser-mfe-vite/apps/shell/package.json | 4 +- .../browser-webworker-vite/.npmrc | 2 - .../browser-webworker-vite/package.json | 2 +- .../test-applications/cloudflare-hono/.npmrc | 2 - .../cloudflare-hono/package.json | 2 +- .../cloudflare-local-workers/.npmrc | 2 - .../cloudflare-local-workers/package.json | 2 +- .../test-applications/cloudflare-mcp/.npmrc | 2 - .../cloudflare-mcp/package.json | 2 +- .../cloudflare-workers/.npmrc | 2 - .../cloudflare-workers/package.json | 2 +- .../cloudflare-workersentrypoint/.npmrc | 2 - .../cloudflare-workersentrypoint/package.json | 2 +- .../test-applications/create-react-app/.npmrc | 2 - .../create-react-app/package.json | 2 +- .../create-remix-app-express-vite-dev/.npmrc | 2 - .../package.json | 2 +- .../create-remix-app-express/.npmrc | 2 - .../create-remix-app-express/package.json | 2 +- .../create-remix-app-v2-non-vite/.npmrc | 2 - .../create-remix-app-v2-non-vite/package.json | 2 +- .../create-remix-app-v2/.npmrc | 2 - .../create-remix-app-v2/package.json | 2 +- .../debug-id-sourcemaps/.npmrc | 2 - .../debug-id-sourcemaps/package.json | 2 +- .../test-applications/default-browser/.npmrc | 2 - .../default-browser/package.json | 2 +- .../test-applications/deno-streamed/.npmrc | 2 - .../deno-streamed/package.json | 2 +- .../e2e-tests/test-applications/deno/.npmrc | 2 - .../test-applications/deno/package.json | 2 +- .../test-applications/effect-3-browser/.npmrc | 2 - .../effect-3-browser/package.json | 2 +- .../test-applications/effect-4-node/.npmrc | 2 - .../effect-4-node/package.json | 2 +- .../test-applications/elysia-bun/.npmrc | 2 - .../test-applications/elysia-bun/package.json | 2 +- .../test-applications/elysia-node/.npmrc | 2 - .../elysia-node/package.json | 2 +- .../test-applications/ember-classic/.npmrc | 2 - .../ember-classic/package.json | 2 +- .../test-applications/ember-embroider/.npmrc | 2 - .../ember-embroider/package.json | 2 +- .../test-applications/generic-ts3.8/.npmrc | 2 - .../generic-ts3.8/package.json | 10 +- .../test-applications/generic-ts5.0/.npmrc | 2 - .../generic-ts5.0/package.json | 10 +- .../hydrogen-react-router-7/.npmrc | 2 - .../hydrogen-react-router-7/package.json | 4 +- .../test-applications/nestjs-11/.npmrc | 2 - .../test-applications/nestjs-11/package.json | 2 +- .../test-applications/nestjs-8/.npmrc | 2 - .../test-applications/nestjs-8/package.json | 2 +- .../nestjs-basic-with-graphql/.npmrc | 2 - .../nestjs-basic-with-graphql/package.json | 2 +- .../test-applications/nestjs-basic/.npmrc | 2 - .../nestjs-basic/package.json | 2 +- .../test-applications/nestjs-bullmq/.npmrc | 2 - .../nestjs-bullmq/package.json | 2 +- .../nestjs-distributed-tracing/.npmrc | 2 - .../nestjs-distributed-tracing/package.json | 2 +- .../test-applications/nestjs-fastify/.npmrc | 2 - .../nestjs-fastify/package.json | 2 +- .../test-applications/nestjs-graphql/.npmrc | 2 - .../nestjs-graphql/package.json | 2 +- .../nestjs-microservices/.npmrc | 2 - .../nestjs-microservices/package.json | 2 +- .../nestjs-websockets/package.json | 2 +- .../nestjs-with-submodules-decorator/.npmrc | 2 - .../package.json | 2 +- .../nestjs-with-submodules/.npmrc | 2 - .../nestjs-with-submodules/package.json | 2 +- .../test-applications/nextjs-13/.npmrc | 2 - .../test-applications/nextjs-13/package.json | 2 +- .../test-applications/nextjs-14/.npmrc | 2 - .../test-applications/nextjs-14/package.json | 4 +- .../nextjs-15-basepath/.npmrc | 4 - .../nextjs-15-basepath/package.json | 2 +- .../test-applications/nextjs-15-intl/.npmrc | 2 - .../nextjs-15-intl/package.json | 2 +- .../test-applications/nextjs-15-t3/.npmrc | 2 - .../nextjs-15-t3/package.json | 2 +- .../test-applications/nextjs-15/.npmrc | 4 - .../test-applications/nextjs-15/package.json | 2 +- .../test-applications/nextjs-16-bun/.npmrc | 4 - .../nextjs-16-bun/package.json | 4 +- .../nextjs-16-cacheComponents/.npmrc | 4 - .../nextjs-16-cacheComponents/package.json | 4 +- .../nextjs-16-cf-workers/.npmrc | 2 - .../nextjs-16-cf-workers/package.json | 4 +- .../nextjs-16-trailing-slash/.npmrc | 4 - .../nextjs-16-trailing-slash/package.json | 4 +- .../test-applications/nextjs-16-tunnel/.npmrc | 4 - .../nextjs-16-tunnel/package.json | 4 +- .../nextjs-16-userfeedback/.npmrc | 4 - .../nextjs-16-userfeedback/package.json | 2 +- .../test-applications/nextjs-16/.npmrc | 4 - .../test-applications/nextjs-16/package.json | 4 +- .../test-applications/nextjs-app-dir/.npmrc | 2 - .../nextjs-app-dir/package.json | 4 +- .../test-applications/nextjs-orpc/.npmrc | 2 - .../nextjs-orpc/package.json | 2 +- .../test-applications/nextjs-pages-dir/.npmrc | 2 - .../nextjs-pages-dir/package.json | 4 +- .../nextjs-sourcemaps/.npmrc | 2 - .../nextjs-sourcemaps/package.json | 2 +- .../test-applications/node-connect/.npmrc | 2 - .../node-connect/package.json | 2 +- .../.npmrc | 2 - .../package.json | 4 +- .../node-core-express-otel-v1-sdk-node/.npmrc | 2 - .../package.json | 4 +- .../node-core-express-otel-v1/.npmrc | 2 - .../node-core-express-otel-v1/package.json | 4 +- .../.npmrc | 2 - .../package.json | 4 +- .../node-core-express-otel-v2-sdk-node/.npmrc | 2 - .../package.json | 4 +- .../node-core-express-otel-v2/.npmrc | 2 - .../node-core-express-otel-v2/package.json | 4 +- .../node-core-light-express/.npmrc | 2 - .../node-core-light-express/package.json | 4 +- .../node-core-light-otlp/.npmrc | 2 - .../node-core-light-otlp/package.json | 4 +- .../node-exports-test-app/.npmrc | 2 - .../node-exports-test-app/package.json | 16 +- .../node-express-cjs-preload/.npmrc | 2 - .../node-express-cjs-preload/package.json | 4 +- .../node-express-esm-loader/.npmrc | 2 - .../node-express-esm-loader/package.json | 4 +- .../node-express-esm-preload/.npmrc | 2 - .../node-express-esm-preload/package.json | 4 +- .../node-express-esm-without-loader/.npmrc | 2 - .../package.json | 4 +- .../.npmrc | 2 - .../package.json | 2 +- .../node-express-mcp-v2/.npmrc | 2 - .../node-express-mcp-v2/package.json | 4 +- .../node-express-send-to-sentry/.npmrc | 2 - .../node-express-send-to-sentry/package.json | 2 +- .../test-applications/node-express-v5/.npmrc | 2 - .../node-express-v5/package.json | 4 +- .../test-applications/node-express/.npmrc | 2 - .../node-express/package.json | 4 +- .../test-applications/node-fastify-3/.npmrc | 2 - .../node-fastify-3/package.json | 2 +- .../test-applications/node-fastify-4/.npmrc | 2 - .../node-fastify-4/package.json | 2 +- .../test-applications/node-fastify-5/.npmrc | 2 - .../node-fastify-5/package.json | 2 +- .../test-applications/node-firebase/.npmrc | 2 - .../node-firebase/firestore-app/package.json | 2 +- .../node-firebase/functions/package.json | 2 +- .../test-applications/node-hapi/.npmrc | 2 - .../test-applications/node-hapi/package.json | 2 +- .../test-applications/node-koa/.npmrc | 2 - .../test-applications/node-koa/package.json | 2 +- .../node-otel-custom-sampler/.npmrc | 2 - .../node-otel-custom-sampler/package.json | 4 +- .../node-otel-sdk-node/.npmrc | 2 - .../node-otel-sdk-node/package.json | 2 +- .../node-otel-without-tracing/.npmrc | 2 - .../node-otel-without-tracing/package.json | 2 +- .../test-applications/node-otel/.npmrc | 2 - .../test-applications/node-otel/package.json | 2 +- .../node-profiling-cjs/.npmrc | 2 - .../node-profiling-cjs/package.json | 4 +- .../node-profiling-electron/.npmrc | 2 - .../node-profiling-electron/package.json | 4 +- .../node-profiling-esm/.npmrc | 2 - .../node-profiling-esm/package.json | 4 +- .../nuxt-3-dynamic-import/.npmrc | 2 - .../nuxt-3-dynamic-import/package.json | 2 +- .../test-applications/nuxt-3-min/.npmrc | 2 - .../test-applications/nuxt-3-min/package.json | 2 +- .../nuxt-3-top-level-import/.npmrc | 2 - .../nuxt-3-top-level-import/package.json | 4 +- .../e2e-tests/test-applications/nuxt-3/.npmrc | 2 - .../test-applications/nuxt-3/package.json | 4 +- .../e2e-tests/test-applications/nuxt-4/.npmrc | 2 - .../test-applications/nuxt-4/package.json | 2 +- .../e2e-tests/test-applications/nuxt-5/.npmrc | 2 - .../test-applications/nuxt-5/package.json | 2 +- .../test-applications/react-17/.npmrc | 2 - .../test-applications/react-17/package.json | 2 +- .../test-applications/react-19/.npmrc | 2 - .../test-applications/react-19/package.json | 2 +- .../react-create-browser-router/.npmrc | 2 - .../react-create-browser-router/package.json | 2 +- .../react-create-hash-router/.npmrc | 2 - .../react-create-hash-router/package.json | 2 +- .../react-create-memory-router/.npmrc | 2 - .../react-create-memory-router/package.json | 2 +- .../test-applications/react-router-5/.npmrc | 2 - .../react-router-5/package.json | 2 +- .../react-router-6-descendant-routes/.npmrc | 2 - .../package.json | 2 +- .../react-router-6-use-routes/.npmrc | 2 - .../react-router-6-use-routes/package.json | 2 +- .../test-applications/react-router-6/.npmrc | 2 - .../react-router-6/package.json | 2 +- .../react-router-7-cross-usage/.npmrc | 2 - .../react-router-7-cross-usage/package.json | 2 +- .../react-router-7-framework-custom/.npmrc | 2 - .../package.json | 2 +- .../.npmrc | 2 - .../package.json | 2 +- .../.npmrc | 2 - .../package.json | 2 +- .../.npmrc | 2 - .../package.json | 2 +- .../react-router-7-framework-spa/.npmrc | 2 - .../react-router-7-framework-spa/package.json | 2 +- .../react-router-7-framework/.npmrc | 2 - .../react-router-7-framework/package.json | 2 +- .../react-router-7-lazy-routes/.npmrc | 2 - .../react-router-7-lazy-routes/package.json | 2 +- .../react-router-7-spa/.npmrc | 2 - .../react-router-7-spa/package.json | 2 +- .../react-send-to-sentry/.npmrc | 2 - .../react-send-to-sentry/package.json | 2 +- .../test-applications/remix-hydrogen/.npmrc | 2 - .../remix-hydrogen/package.json | 4 +- .../remix-server-timing/.npmrc | 2 - .../remix-server-timing/package.json | 2 +- .../solid-tanstack-router/.npmrc | 2 - .../solid-tanstack-router/package.json | 2 +- .../e2e-tests/test-applications/solid/.npmrc | 2 - .../test-applications/solid/package.json | 2 +- .../solidstart-dynamic-import/.npmrc | 2 - .../solidstart-dynamic-import/package.json | 2 +- .../test-applications/solidstart-spa/.npmrc | 2 - .../solidstart-spa/package.json | 2 +- .../solidstart-top-level-import/.npmrc | 2 - .../solidstart-top-level-import/package.json | 2 +- .../test-applications/solidstart/.npmrc | 2 - .../test-applications/solidstart/package.json | 2 +- .../test-applications/supabase-nextjs/.npmrc | 2 - .../supabase-nextjs/package.json | 2 +- .../test-applications/svelte-5/.npmrc | 2 - .../test-applications/svelte-5/package.json | 2 +- .../sveltekit-2-kit-tracing/.npmrc | 2 - .../sveltekit-2-kit-tracing/package.json | 2 +- .../sveltekit-2-svelte-5/.npmrc | 2 - .../sveltekit-2-svelte-5/package.json | 2 +- .../sveltekit-2.5.0-twp/.npmrc | 2 - .../sveltekit-2.5.0-twp/package.json | 2 +- .../test-applications/sveltekit-2/.npmrc | 2 - .../sveltekit-2/package.json | 2 +- .../sveltekit-cloudflare-pages/.npmrc | 2 - .../sveltekit-cloudflare-pages/package.json | 2 +- .../test-applications/tanstack-router/.npmrc | 2 - .../tanstack-router/package.json | 2 +- .../tanstackstart-react/.npmrc | 2 - .../tanstackstart-react/package.json | 2 +- .../test-applications/tsx-express/.npmrc | 2 - .../tsx-express/package.json | 4 +- .../e2e-tests/test-applications/vue-3/.npmrc | 2 - .../test-applications/vue-3/package.json | 2 +- .../vue-tanstack-router/.npmrc | 2 - .../vue-tanstack-router/package.json | 2 +- .../test-applications/webpack-4/.npmrc | 2 - .../test-applications/webpack-4/package.json | 2 +- .../test-applications/webpack-5/.npmrc | 2 - .../test-applications/webpack-5/package.json | 2 +- dev-packages/e2e-tests/test-registry.npmrc | 6 - .../validate-packed-tarball-setup.ts | 42 + .../e2e-tests/validate-test-app-setups.ts | 29 - .../validate-verdaccio-configuration.ts | 45 - .../e2e-tests/verdaccio-config/config.yaml | 288 ----- dev-packages/e2e-tests/verdaccio-runner.mjs | 26 - .../scripts/buildLambdaLayer.ts | 3 + yarn.lock | 1077 ++--------------- 314 files changed, 677 insertions(+), 2142 deletions(-) create mode 100644 dev-packages/e2e-tests/ciPnpmOverrides.ts create mode 100644 dev-packages/e2e-tests/lib/packedTarballUtils.ts create mode 100644 dev-packages/e2e-tests/lib/pnpmOverrides.ts delete mode 100644 dev-packages/e2e-tests/lib/publishPackages.ts create mode 100644 dev-packages/e2e-tests/lib/syncPackedTarballSymlinks.ts delete mode 100644 dev-packages/e2e-tests/registrySetup.ts delete mode 100644 dev-packages/e2e-tests/test-applications/angular-17/.npmrc delete mode 100644 dev-packages/e2e-tests/test-applications/angular-18/.npmrc delete mode 100644 dev-packages/e2e-tests/test-applications/angular-19/.npmrc delete mode 100644 dev-packages/e2e-tests/test-applications/angular-20/.npmrc delete mode 100644 dev-packages/e2e-tests/test-applications/angular-21/.npmrc delete mode 100644 dev-packages/e2e-tests/test-applications/astro-4/.npmrc delete mode 100644 dev-packages/e2e-tests/test-applications/astro-5-cf-workers/.npmrc delete mode 100644 dev-packages/e2e-tests/test-applications/astro-5/.npmrc delete mode 100644 dev-packages/e2e-tests/test-applications/astro-6-cf-workers/.npmrc delete mode 100644 dev-packages/e2e-tests/test-applications/astro-6/.npmrc delete mode 100644 dev-packages/e2e-tests/test-applications/aws-serverless/.npmrc delete mode 100644 dev-packages/e2e-tests/test-applications/browser-mfe-vite/.npmrc delete mode 100644 dev-packages/e2e-tests/test-applications/browser-webworker-vite/.npmrc delete mode 100644 dev-packages/e2e-tests/test-applications/cloudflare-hono/.npmrc delete mode 100644 dev-packages/e2e-tests/test-applications/cloudflare-local-workers/.npmrc delete mode 100644 dev-packages/e2e-tests/test-applications/cloudflare-mcp/.npmrc delete mode 100644 dev-packages/e2e-tests/test-applications/cloudflare-workers/.npmrc delete mode 100644 dev-packages/e2e-tests/test-applications/cloudflare-workersentrypoint/.npmrc delete mode 100644 dev-packages/e2e-tests/test-applications/create-react-app/.npmrc delete mode 100644 dev-packages/e2e-tests/test-applications/create-remix-app-express-vite-dev/.npmrc delete mode 100644 dev-packages/e2e-tests/test-applications/create-remix-app-express/.npmrc delete mode 100644 dev-packages/e2e-tests/test-applications/create-remix-app-v2-non-vite/.npmrc delete mode 100644 dev-packages/e2e-tests/test-applications/create-remix-app-v2/.npmrc delete mode 100644 dev-packages/e2e-tests/test-applications/debug-id-sourcemaps/.npmrc delete mode 100644 dev-packages/e2e-tests/test-applications/default-browser/.npmrc delete mode 100644 dev-packages/e2e-tests/test-applications/deno-streamed/.npmrc delete mode 100644 dev-packages/e2e-tests/test-applications/deno/.npmrc delete mode 100644 dev-packages/e2e-tests/test-applications/effect-3-browser/.npmrc delete mode 100644 dev-packages/e2e-tests/test-applications/effect-4-node/.npmrc delete mode 100644 dev-packages/e2e-tests/test-applications/elysia-bun/.npmrc delete mode 100644 dev-packages/e2e-tests/test-applications/elysia-node/.npmrc delete mode 100644 dev-packages/e2e-tests/test-applications/ember-classic/.npmrc delete mode 100644 dev-packages/e2e-tests/test-applications/ember-embroider/.npmrc delete mode 100644 dev-packages/e2e-tests/test-applications/generic-ts3.8/.npmrc delete mode 100644 dev-packages/e2e-tests/test-applications/generic-ts5.0/.npmrc delete mode 100644 dev-packages/e2e-tests/test-applications/hydrogen-react-router-7/.npmrc delete mode 100644 dev-packages/e2e-tests/test-applications/nestjs-11/.npmrc delete mode 100644 dev-packages/e2e-tests/test-applications/nestjs-8/.npmrc delete mode 100644 dev-packages/e2e-tests/test-applications/nestjs-basic-with-graphql/.npmrc delete mode 100644 dev-packages/e2e-tests/test-applications/nestjs-basic/.npmrc delete mode 100644 dev-packages/e2e-tests/test-applications/nestjs-bullmq/.npmrc delete mode 100644 dev-packages/e2e-tests/test-applications/nestjs-distributed-tracing/.npmrc delete mode 100644 dev-packages/e2e-tests/test-applications/nestjs-fastify/.npmrc delete mode 100644 dev-packages/e2e-tests/test-applications/nestjs-graphql/.npmrc delete mode 100644 dev-packages/e2e-tests/test-applications/nestjs-microservices/.npmrc delete mode 100644 dev-packages/e2e-tests/test-applications/nestjs-with-submodules-decorator/.npmrc delete mode 100644 dev-packages/e2e-tests/test-applications/nestjs-with-submodules/.npmrc delete mode 100644 dev-packages/e2e-tests/test-applications/nextjs-13/.npmrc delete mode 100644 dev-packages/e2e-tests/test-applications/nextjs-14/.npmrc delete mode 100644 dev-packages/e2e-tests/test-applications/nextjs-15-basepath/.npmrc delete mode 100644 dev-packages/e2e-tests/test-applications/nextjs-15-intl/.npmrc delete mode 100644 dev-packages/e2e-tests/test-applications/nextjs-15-t3/.npmrc delete mode 100644 dev-packages/e2e-tests/test-applications/nextjs-15/.npmrc delete mode 100644 dev-packages/e2e-tests/test-applications/nextjs-16-bun/.npmrc delete mode 100644 dev-packages/e2e-tests/test-applications/nextjs-16-cacheComponents/.npmrc delete mode 100644 dev-packages/e2e-tests/test-applications/nextjs-16-cf-workers/.npmrc delete mode 100644 dev-packages/e2e-tests/test-applications/nextjs-16-trailing-slash/.npmrc delete mode 100644 dev-packages/e2e-tests/test-applications/nextjs-16-tunnel/.npmrc delete mode 100644 dev-packages/e2e-tests/test-applications/nextjs-16-userfeedback/.npmrc delete mode 100644 dev-packages/e2e-tests/test-applications/nextjs-16/.npmrc delete mode 100644 dev-packages/e2e-tests/test-applications/nextjs-app-dir/.npmrc delete mode 100644 dev-packages/e2e-tests/test-applications/nextjs-orpc/.npmrc delete mode 100644 dev-packages/e2e-tests/test-applications/nextjs-pages-dir/.npmrc delete mode 100644 dev-packages/e2e-tests/test-applications/nextjs-sourcemaps/.npmrc delete mode 100644 dev-packages/e2e-tests/test-applications/node-connect/.npmrc delete mode 100644 dev-packages/e2e-tests/test-applications/node-core-express-otel-v1-custom-sampler/.npmrc delete mode 100644 dev-packages/e2e-tests/test-applications/node-core-express-otel-v1-sdk-node/.npmrc delete mode 100644 dev-packages/e2e-tests/test-applications/node-core-express-otel-v1/.npmrc delete mode 100644 dev-packages/e2e-tests/test-applications/node-core-express-otel-v2-custom-sampler/.npmrc delete mode 100644 dev-packages/e2e-tests/test-applications/node-core-express-otel-v2-sdk-node/.npmrc delete mode 100644 dev-packages/e2e-tests/test-applications/node-core-express-otel-v2/.npmrc delete mode 100644 dev-packages/e2e-tests/test-applications/node-core-light-express/.npmrc delete mode 100644 dev-packages/e2e-tests/test-applications/node-core-light-otlp/.npmrc delete mode 100644 dev-packages/e2e-tests/test-applications/node-exports-test-app/.npmrc delete mode 100644 dev-packages/e2e-tests/test-applications/node-express-cjs-preload/.npmrc delete mode 100644 dev-packages/e2e-tests/test-applications/node-express-esm-loader/.npmrc delete mode 100644 dev-packages/e2e-tests/test-applications/node-express-esm-preload/.npmrc delete mode 100644 dev-packages/e2e-tests/test-applications/node-express-esm-without-loader/.npmrc delete mode 100644 dev-packages/e2e-tests/test-applications/node-express-incorrect-instrumentation/.npmrc delete mode 100644 dev-packages/e2e-tests/test-applications/node-express-mcp-v2/.npmrc delete mode 100644 dev-packages/e2e-tests/test-applications/node-express-send-to-sentry/.npmrc delete mode 100644 dev-packages/e2e-tests/test-applications/node-express-v5/.npmrc delete mode 100644 dev-packages/e2e-tests/test-applications/node-express/.npmrc delete mode 100644 dev-packages/e2e-tests/test-applications/node-fastify-3/.npmrc delete mode 100644 dev-packages/e2e-tests/test-applications/node-fastify-4/.npmrc delete mode 100644 dev-packages/e2e-tests/test-applications/node-fastify-5/.npmrc delete mode 100644 dev-packages/e2e-tests/test-applications/node-firebase/.npmrc delete mode 100644 dev-packages/e2e-tests/test-applications/node-hapi/.npmrc delete mode 100644 dev-packages/e2e-tests/test-applications/node-koa/.npmrc delete mode 100644 dev-packages/e2e-tests/test-applications/node-otel-custom-sampler/.npmrc delete mode 100644 dev-packages/e2e-tests/test-applications/node-otel-sdk-node/.npmrc delete mode 100644 dev-packages/e2e-tests/test-applications/node-otel-without-tracing/.npmrc delete mode 100644 dev-packages/e2e-tests/test-applications/node-otel/.npmrc delete mode 100644 dev-packages/e2e-tests/test-applications/node-profiling-cjs/.npmrc delete mode 100644 dev-packages/e2e-tests/test-applications/node-profiling-electron/.npmrc delete mode 100644 dev-packages/e2e-tests/test-applications/node-profiling-esm/.npmrc delete mode 100644 dev-packages/e2e-tests/test-applications/nuxt-3-dynamic-import/.npmrc delete mode 100644 dev-packages/e2e-tests/test-applications/nuxt-3-min/.npmrc delete mode 100644 dev-packages/e2e-tests/test-applications/nuxt-3-top-level-import/.npmrc delete mode 100644 dev-packages/e2e-tests/test-applications/nuxt-3/.npmrc delete mode 100644 dev-packages/e2e-tests/test-applications/nuxt-4/.npmrc delete mode 100644 dev-packages/e2e-tests/test-applications/nuxt-5/.npmrc delete mode 100644 dev-packages/e2e-tests/test-applications/react-17/.npmrc delete mode 100644 dev-packages/e2e-tests/test-applications/react-19/.npmrc delete mode 100644 dev-packages/e2e-tests/test-applications/react-create-browser-router/.npmrc delete mode 100644 dev-packages/e2e-tests/test-applications/react-create-hash-router/.npmrc delete mode 100644 dev-packages/e2e-tests/test-applications/react-create-memory-router/.npmrc delete mode 100644 dev-packages/e2e-tests/test-applications/react-router-5/.npmrc delete mode 100644 dev-packages/e2e-tests/test-applications/react-router-6-descendant-routes/.npmrc delete mode 100644 dev-packages/e2e-tests/test-applications/react-router-6-use-routes/.npmrc delete mode 100644 dev-packages/e2e-tests/test-applications/react-router-6/.npmrc delete mode 100644 dev-packages/e2e-tests/test-applications/react-router-7-cross-usage/.npmrc delete mode 100644 dev-packages/e2e-tests/test-applications/react-router-7-framework-custom/.npmrc delete mode 100644 dev-packages/e2e-tests/test-applications/react-router-7-framework-instrumentation/.npmrc delete mode 100644 dev-packages/e2e-tests/test-applications/react-router-7-framework-node-20-18/.npmrc delete mode 100644 dev-packages/e2e-tests/test-applications/react-router-7-framework-spa-node-20-18/.npmrc delete mode 100644 dev-packages/e2e-tests/test-applications/react-router-7-framework-spa/.npmrc delete mode 100644 dev-packages/e2e-tests/test-applications/react-router-7-framework/.npmrc delete mode 100644 dev-packages/e2e-tests/test-applications/react-router-7-lazy-routes/.npmrc delete mode 100644 dev-packages/e2e-tests/test-applications/react-router-7-spa/.npmrc delete mode 100644 dev-packages/e2e-tests/test-applications/react-send-to-sentry/.npmrc delete mode 100644 dev-packages/e2e-tests/test-applications/remix-hydrogen/.npmrc delete mode 100644 dev-packages/e2e-tests/test-applications/remix-server-timing/.npmrc delete mode 100644 dev-packages/e2e-tests/test-applications/solid-tanstack-router/.npmrc delete mode 100644 dev-packages/e2e-tests/test-applications/solid/.npmrc delete mode 100644 dev-packages/e2e-tests/test-applications/solidstart-dynamic-import/.npmrc delete mode 100644 dev-packages/e2e-tests/test-applications/solidstart-spa/.npmrc delete mode 100644 dev-packages/e2e-tests/test-applications/solidstart-top-level-import/.npmrc delete mode 100644 dev-packages/e2e-tests/test-applications/solidstart/.npmrc delete mode 100644 dev-packages/e2e-tests/test-applications/supabase-nextjs/.npmrc delete mode 100644 dev-packages/e2e-tests/test-applications/svelte-5/.npmrc delete mode 100644 dev-packages/e2e-tests/test-applications/sveltekit-2-kit-tracing/.npmrc delete mode 100644 dev-packages/e2e-tests/test-applications/sveltekit-2-svelte-5/.npmrc delete mode 100644 dev-packages/e2e-tests/test-applications/sveltekit-2.5.0-twp/.npmrc delete mode 100644 dev-packages/e2e-tests/test-applications/sveltekit-2/.npmrc delete mode 100644 dev-packages/e2e-tests/test-applications/sveltekit-cloudflare-pages/.npmrc delete mode 100644 dev-packages/e2e-tests/test-applications/tanstack-router/.npmrc delete mode 100644 dev-packages/e2e-tests/test-applications/tanstackstart-react/.npmrc delete mode 100644 dev-packages/e2e-tests/test-applications/tsx-express/.npmrc delete mode 100644 dev-packages/e2e-tests/test-applications/vue-3/.npmrc delete mode 100644 dev-packages/e2e-tests/test-applications/vue-tanstack-router/.npmrc delete mode 100644 dev-packages/e2e-tests/test-applications/webpack-4/.npmrc delete mode 100644 dev-packages/e2e-tests/test-applications/webpack-5/.npmrc delete mode 100644 dev-packages/e2e-tests/test-registry.npmrc create mode 100644 dev-packages/e2e-tests/validate-packed-tarball-setup.ts delete mode 100644 dev-packages/e2e-tests/validate-test-app-setups.ts delete mode 100644 dev-packages/e2e-tests/validate-verdaccio-configuration.ts delete mode 100644 dev-packages/e2e-tests/verdaccio-config/config.yaml delete mode 100644 dev-packages/e2e-tests/verdaccio-runner.mjs diff --git a/.agents/skills/e2e/SKILL.md b/.agents/skills/e2e/SKILL.md index 8c45d939a8cf..bc9f3efe42f4 100644 --- a/.agents/skills/e2e/SKILL.md +++ b/.agents/skills/e2e/SKILL.md @@ -116,7 +116,7 @@ All tests completed successfully. Your SDK changes work correctly with this test - **No tarballs found**: Run `yarn build && yarn build:tarball` at repository root - **Test app not found**: List available apps and ask user to clarify -- **Verdaccio not running**: Tests should start Verdaccio automatically, but if issues occur, check Docker +- **Packed tarballs missing**: Run `yarn build:tarball` at the repo root, then `yarn test:prepare` in `dev-packages/e2e-tests` - **Build failures**: Fix build errors before running tests ## Common Test Applications diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index d686f8293158..c92fb6b05838 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -961,18 +961,24 @@ jobs: if: steps.restore-tarball-cache.outputs.cache-hit != 'true' run: yarn build:tarball - - name: Validate Verdaccio - run: yarn test:validate + - name: Prepare e2e tests + run: yarn test:prepare working-directory: dev-packages/e2e-tests - - name: Prepare Verdaccio - run: yarn test:prepare + - name: Validate e2e tests setup + run: yarn test:validate working-directory: dev-packages/e2e-tests - name: Copy to temp run: yarn ci:copy-to-temp ./test-applications/${{ matrix.test-application }} ${{ runner.temp }}/test-application working-directory: dev-packages/e2e-tests + - name: Add pnpm overrides + run: + yarn ci:pnpm-overrides ${{ runner.temp }}/test-application ${{ github.workspace + }}/dev-packages/e2e-tests/packed + working-directory: dev-packages/e2e-tests + - name: Build E2E app working-directory: ${{ runner.temp }}/test-application timeout-minutes: 7 @@ -1071,18 +1077,24 @@ jobs: if: steps.restore-tarball-cache.outputs.cache-hit != 'true' run: yarn build:tarball - - name: Validate Verdaccio - run: yarn test:validate + - name: Prepare E2E tests + run: yarn test:prepare working-directory: dev-packages/e2e-tests - - name: Prepare Verdaccio - run: yarn test:prepare + - name: Validate test setup + run: yarn test:validate working-directory: dev-packages/e2e-tests - name: Copy to temp run: yarn ci:copy-to-temp ./test-applications/${{ matrix.test-application }} ${{ runner.temp }}/test-application working-directory: dev-packages/e2e-tests + - name: Add pnpm overrides + run: + yarn ci:pnpm-overrides ${{ runner.temp }}/test-application ${{ github.workspace + }}/dev-packages/e2e-tests/packed + working-directory: dev-packages/e2e-tests + - name: Build E2E app working-directory: ${{ runner.temp }}/test-application timeout-minutes: 7 diff --git a/.github/workflows/canary.yml b/.github/workflows/canary.yml index ac4e1df08841..e28d6988d9a1 100644 --- a/.github/workflows/canary.yml +++ b/.github/workflows/canary.yml @@ -140,18 +140,24 @@ jobs: path: ${{ env.CACHED_BUILD_PATHS }} key: canary-${{ env.HEAD_COMMIT }} - - name: Validate Verdaccio - run: yarn test:validate + - name: Prepare e2e tests + run: yarn test:prepare working-directory: dev-packages/e2e-tests - - name: Prepare Verdaccio - run: yarn test:prepare + - name: Validate test setup + run: yarn test:validate working-directory: dev-packages/e2e-tests - name: Copy to temp run: yarn ci:copy-to-temp ./test-applications/${{ matrix.test-application }} ${{ runner.temp }}/test-application working-directory: dev-packages/e2e-tests + - name: Add pnpm overrides + run: + yarn ci:pnpm-overrides ${{ runner.temp }}/test-application ${{ github.workspace + }}/dev-packages/e2e-tests/packed + working-directory: dev-packages/e2e-tests + - name: Build E2E app working-directory: ${{ runner.temp }}/test-application timeout-minutes: 7 diff --git a/.gitignore b/.gitignore index d872a9adc27a..464a09a9980e 100644 --- a/.gitignore +++ b/.gitignore @@ -39,9 +39,6 @@ local.log .rpt2_cache -# verdaccio local registry (e2e tests) -dev-packages/e2e-tests/verdaccio-config/storage/ - lint-results.json trace.zip diff --git a/dev-packages/e2e-tests/.gitignore b/dev-packages/e2e-tests/.gitignore index 2ce9dc2100a5..21181cb54143 100644 --- a/dev-packages/e2e-tests/.gitignore +++ b/dev-packages/e2e-tests/.gitignore @@ -4,3 +4,4 @@ tmp .tmp_build_stderr pnpm-lock.yaml .last-run.json +packed diff --git a/dev-packages/e2e-tests/ciCopyToTemp.ts b/dev-packages/e2e-tests/ciCopyToTemp.ts index 0ecd3999db4d..24c3629d8dce 100644 --- a/dev-packages/e2e-tests/ciCopyToTemp.ts +++ b/dev-packages/e2e-tests/ciCopyToTemp.ts @@ -15,5 +15,7 @@ async function run(): Promise { await copyToTemp(originalPath, tmpDirPath); } -// eslint-disable-next-line @typescript-eslint/no-floating-promises -run(); +run().catch(error => { + console.error(error); + process.exit(1); +}); diff --git a/dev-packages/e2e-tests/ciPnpmOverrides.ts b/dev-packages/e2e-tests/ciPnpmOverrides.ts new file mode 100644 index 000000000000..909a59fa2e9d --- /dev/null +++ b/dev-packages/e2e-tests/ciPnpmOverrides.ts @@ -0,0 +1,20 @@ +/* eslint-disable no-console */ + +import { addPnpmOverrides } from './lib/pnpmOverrides'; +import * as path from 'path'; + +async function run(): Promise { + const tmpDirPath = process.argv[2]; + const packedDirPath = process.argv[3]; + + if (!tmpDirPath || !packedDirPath) { + throw new Error('Tmp dir path and packed dir path are required'); + } + + await addPnpmOverrides(path.resolve(tmpDirPath), path.resolve(packedDirPath)); +} + +run().catch(error => { + console.error(error); + process.exit(1); +}); diff --git a/dev-packages/e2e-tests/lib/copyToTemp.ts b/dev-packages/e2e-tests/lib/copyToTemp.ts index 830ff76f6077..c45a5793e2fc 100644 --- a/dev-packages/e2e-tests/lib/copyToTemp.ts +++ b/dev-packages/e2e-tests/lib/copyToTemp.ts @@ -1,7 +1,7 @@ /* eslint-disable no-console */ import { readFileSync, writeFileSync } from 'fs'; import { cp } from 'fs/promises'; -import { join } from 'path'; +import { isAbsolute, join, resolve } from 'path'; export async function copyToTemp(originalPath: string, tmpDirPath: string): Promise { // copy files to tmp dir @@ -35,7 +35,7 @@ function fixPackageJson(cwd: string): void { const extendsPath = packageJson.volta.extends; // We add a virtual dir to ensure that the relative depth is consistent // dirPath is relative to ./../test-applications/xxx - const newPath = join(__dirname, 'virtual-dir/', extendsPath); + const newPath = resolve(__dirname, 'virtual-dir/', extendsPath); packageJson.volta.extends = newPath; console.log(`Fixed volta.extends to ${newPath}`); } else { @@ -45,17 +45,24 @@ function fixPackageJson(cwd: string): void { writeFileSync(packageJsonPath, JSON.stringify(packageJson, null, 2)); } -function fixFileLinkDependencies(dependencyObj: Record): void { +// Exported for pnpmOverrides as well +export function fixFileLinkDependencies(dependencyObj: Record): void { for (const [key, value] of Object.entries(dependencyObj)) { - if (value.startsWith('link:')) { - const dirPath = value.replace('link:', ''); - - // We add a virtual dir to ensure that the relative depth is consistent - // dirPath is relative to ./../test-applications/xxx - const newPath = join(__dirname, 'virtual-dir/', dirPath); + const prefix = value.startsWith('link:') ? 'link:' : value.startsWith('file:') ? 'file:' : null; + if (!prefix) { + continue; + } - dependencyObj[key] = `link:${newPath}`; - console.log(`Fixed ${key} dependency to ${newPath}`); + const dirPath = value.slice(prefix.length); + if (isAbsolute(dirPath)) { + continue; } + + // We add a virtual dir to ensure that the relative depth is consistent + // dirPath is relative to ./../test-applications/xxx + const absPath = resolve(__dirname, 'virtual-dir', dirPath); + + dependencyObj[key] = `${prefix}${absPath}`; + console.log(`Fixed ${key} dependency to ${absPath}`); } } diff --git a/dev-packages/e2e-tests/lib/packedTarballUtils.ts b/dev-packages/e2e-tests/lib/packedTarballUtils.ts new file mode 100644 index 000000000000..7469eb12f4a2 --- /dev/null +++ b/dev-packages/e2e-tests/lib/packedTarballUtils.ts @@ -0,0 +1,60 @@ +import * as path from 'path'; +import * as fs from 'fs'; +import { sync as globSync } from 'glob'; + +const E2E_TESTS_ROOT = path.resolve(__dirname, '..'); +const REPOSITORY_ROOT = path.resolve(E2E_TESTS_ROOT, '../..'); + +/** + * Workspace @sentry and @sentry-internal packages that have a built tarball for the E2E version. + * @returns The names of the published Sentry tarball packages. + */ +export function getPublishedSentryTarballPackageNames(): string[] { + const version = getE2eTestsPackageVersion(); + const names: string[] = []; + + for (const packageJsonPath of globSync('packages/*/package.json', { + cwd: REPOSITORY_ROOT, + absolute: true, + })) { + const pkg = readJson<{ name?: string }>(packageJsonPath); + const name = pkg.name; + if (!name || (!name.startsWith('@sentry/') && !name.startsWith('@sentry-internal/'))) { + continue; + } + const packageDir = path.dirname(packageJsonPath); + const tarball = path.join(packageDir, versionedTarballFilename(name, version)); + if (fs.existsSync(tarball)) { + names.push(name); + } + } + + return names.sort(); +} + +/** Stable symlink name in `packed/` (no version segment). */ +export function packedSymlinkFilename(packageName: string): string { + return `${npmPackBasename(packageName)}-packed.tgz`; +} + +/** + * Versioned tarball filename produced by `npm pack` in a package directory. + */ +export function versionedTarballFilename(packageName: string, version: string): string { + return `${npmPackBasename(packageName)}-${version}.tgz`; +} + +/** + * npm pack tarball basename (without version and .tgz), e.g. `@sentry/core` -> `sentry-core`. + */ +function npmPackBasename(packageName: string): string { + return packageName.replace(/^@/, '').replace(/\//g, '-'); +} + +function readJson(filePath: string): T { + return JSON.parse(fs.readFileSync(filePath, 'utf8')) as T; +} + +function getE2eTestsPackageVersion(): string { + return readJson<{ version: string }>(path.join(E2E_TESTS_ROOT, 'package.json')).version; +} diff --git a/dev-packages/e2e-tests/lib/pnpmOverrides.ts b/dev-packages/e2e-tests/lib/pnpmOverrides.ts new file mode 100644 index 000000000000..cf88b6c48f09 --- /dev/null +++ b/dev-packages/e2e-tests/lib/pnpmOverrides.ts @@ -0,0 +1,43 @@ +import { readFile, writeFile } from 'fs/promises'; +import * as path from 'path'; +import { getPublishedSentryTarballPackageNames, packedSymlinkFilename } from './packedTarballUtils'; +import { fixFileLinkDependencies } from './copyToTemp'; + +/** + * For a given temp test application directory, add pnpm.overrides to pin the internal Sentry packages to the packed tarballs. + * This is used to ensure that the test application uses the correct version of the internal Sentry packages. + * @param tmpDirPath - The temporary directory path of the test application. + * @param packedDirPath - The path to the packed tarballs. + * @param packageNames - The names of the internal Sentry packages to pin to the packed tarballs. + * @returns + */ +export async function addPnpmOverrides(tmpDirPath: string, packedDirPath: string): Promise { + const packageJsonPath = path.join(tmpDirPath, 'package.json'); + const packageJson = JSON.parse(await readFile(packageJsonPath, 'utf8')) as { + pnpm?: { overrides?: Record }; + }; + + const overrides: Record = {}; + + const packageNames = getPublishedSentryTarballPackageNames(); + + for (const packageName of packageNames) { + overrides[packageName] = `file:${packedDirPath}/${packedSymlinkFilename(packageName)}`; + } + + const fixedOverrides = packageJson.pnpm?.overrides ?? {}; + fixFileLinkDependencies(fixedOverrides); + + packageJson.pnpm = { + ...packageJson.pnpm, + overrides: { + ...overrides, + ...fixedOverrides, + }, + }; + + // oxlint-disable-next-line no-console + console.log(`Added ${packageNames.length} internal Sentry packages to pnpm.overrides`); + + await writeFile(packageJsonPath, JSON.stringify(packageJson, null, 2)); +} diff --git a/dev-packages/e2e-tests/lib/publishPackages.ts b/dev-packages/e2e-tests/lib/publishPackages.ts deleted file mode 100644 index 8b0681d5d1f6..000000000000 --- a/dev-packages/e2e-tests/lib/publishPackages.ts +++ /dev/null @@ -1,52 +0,0 @@ -/* eslint-disable no-console */ -import { spawn } from 'child_process'; -import { readFileSync } from 'fs'; -import { globSync } from 'glob'; -import * as path from 'path'; - -const repositoryRoot = path.resolve(__dirname, '../../..'); - -function npmPublish(tarballPath: string, npmrc: string): Promise { - return new Promise((resolve, reject) => { - const child = spawn('npm', ['--userconfig', npmrc, 'publish', tarballPath], { - cwd: repositoryRoot, - stdio: 'inherit', - }); - - child.on('error', reject); - child.on('close', code => { - if (code === 0) { - resolve(); - } else { - reject(new Error(`Error publishing tarball ${tarballPath}`)); - } - }); - }); -} - -/** - * Publishes all built Sentry package tarballs to the local Verdaccio test registry. - * Uses async `npm publish` so an in-process Verdaccio can still handle HTTP on the event loop. - */ -export async function publishPackages(): Promise { - const version = (JSON.parse(readFileSync(path.join(__dirname, '../package.json'), 'utf8')) as { version: string }) - .version; - - // Get absolute paths of all the packages we want to publish to the fake registry - // Only include the current versions, to avoid getting old tarballs published as well - const packageTarballPaths = globSync(`packages/*/sentry-*-${version}.tgz`, { - cwd: repositoryRoot, - absolute: true, - }); - - if (packageTarballPaths.length === 0) { - throw new Error(`No packages to publish for version ${version}, did you run "yarn build:tarballs"?`); - } - - const npmrc = path.join(__dirname, '../test-registry.npmrc'); - - for (const tarballPath of packageTarballPaths) { - console.log(`Publishing tarball ${tarballPath} ...`); - await npmPublish(tarballPath, npmrc); - } -} diff --git a/dev-packages/e2e-tests/lib/syncPackedTarballSymlinks.ts b/dev-packages/e2e-tests/lib/syncPackedTarballSymlinks.ts new file mode 100644 index 000000000000..ac6110ed7afd --- /dev/null +++ b/dev-packages/e2e-tests/lib/syncPackedTarballSymlinks.ts @@ -0,0 +1,65 @@ +/* eslint-disable no-console */ +import * as fs from 'fs'; +import * as path from 'path'; +import { sync as globSync } from 'glob'; +import { packedSymlinkFilename, versionedTarballFilename } from './packedTarballUtils'; + +const e2eTestsRoot = path.resolve(__dirname, '..'); +const repositoryRoot = path.resolve(e2eTestsRoot, '../..'); +const packedDir = path.join(e2eTestsRoot, 'packed'); + +function readJson(filePath: string): T { + return JSON.parse(fs.readFileSync(filePath, 'utf8')) as T; +} + +/** + * Ensures `packed/-packed.tgz` symlinks point at the current versioned tarballs under `packages/*`. + * Run after `yarn build:tarball` at the repo root (or from CI after restoring the tarball cache). + */ +export function syncPackedTarballSymlinks(): void { + const { version } = readJson<{ version: string }>(path.join(e2eTestsRoot, 'package.json')); + + fs.mkdirSync(packedDir, { recursive: true }); + + for (const entry of fs.readdirSync(packedDir, { withFileTypes: true })) { + if (!entry.name.endsWith('-packed.tgz')) { + continue; + } + fs.rmSync(path.join(packedDir, entry.name), { recursive: true, force: true }); + } + + const packageJsonPaths = globSync('packages/*/package.json', { + cwd: repositoryRoot, + absolute: true, + }); + + let linked = 0; + for (const packageJsonPath of packageJsonPaths) { + const pkg = readJson<{ name?: string }>(packageJsonPath); + const name = pkg.name; + if (!name || (!name.startsWith('@sentry/') && !name.startsWith('@sentry-internal/'))) { + continue; + } + + const packageDir = path.dirname(packageJsonPath); + const expectedTarball = path.join(packageDir, versionedTarballFilename(name, version)); + + if (!fs.existsSync(expectedTarball)) { + continue; + } + + const linkName = packedSymlinkFilename(name); + const linkPath = path.join(packedDir, linkName); + + fs.symlinkSync(expectedTarball, linkPath); + linked++; + } + + if (linked === 0) { + throw new Error( + `No packed tarballs found for version ${version} under packages/*/. Run "yarn build:tarball" at the repository root.`, + ); + } + + console.log(`Linked ${linked} tarball symlinks in ${path.relative(repositoryRoot, packedDir) || 'packed'}.`); +} diff --git a/dev-packages/e2e-tests/package.json b/dev-packages/e2e-tests/package.json index ac1f7ddf23f7..25d87ad10a6e 100644 --- a/dev-packages/e2e-tests/package.json +++ b/dev-packages/e2e-tests/package.json @@ -7,17 +7,15 @@ "lint:fix": "OXLINT_TSGOLINT_DANGEROUSLY_SUPPRESS_PROGRAM_DIAGNOSTICS=true oxlint . --fix --type-aware", "lint": "OXLINT_TSGOLINT_DANGEROUSLY_SUPPRESS_PROGRAM_DIAGNOSTICS=true oxlint . --type-aware", "lint:ts": "tsc --noEmit", - "test:e2e": "run-s test:validate-configuration test:validate-test-app-setups test:run", + "test:e2e": "run-s test:prepare test:validate test:run", "test:run": "ts-node run.ts", - "test:validate-configuration": "ts-node validate-verdaccio-configuration.ts", - "test:validate-test-app-setups": "ts-node validate-test-app-setups.ts", "test:prepare": "ts-node prepare.ts", - "test:validate": "run-s test:validate-configuration test:validate-test-app-setups", - "clean:verdaccio": "sh -c 'pkill -f verdaccio-runner.mjs 2>/dev/null || true'", - "clean": "yarn clean:verdaccio && rimraf tmp node_modules verdaccio-config/storage && yarn clean:test-applications && yarn clean:pnpm", + "test:validate": "ts-node validate-packed-tarball-setup.ts", + "clean": "rimraf tmp node_modules packed && yarn clean:test-applications && yarn clean:pnpm", "ci:build-matrix": "ts-node ./lib/getTestMatrix.ts", "ci:build-matrix-optional": "ts-node ./lib/getTestMatrix.ts --optional=true", "ci:copy-to-temp": "ts-node ./ciCopyToTemp.ts", + "ci:pnpm-overrides": "ts-node ./ciPnpmOverrides.ts", "clean:test-applications": "rimraf --glob test-applications/**/{node_modules,dist,build,.next,.nuxt,.sveltekit,.react-router,.astro,.output,pnpm-lock.yaml,.last-run.json,test-results,.angular,event-dumps}", "clean:pnpm": "pnpm store prune" }, @@ -28,9 +26,7 @@ "eslint-plugin-regexp": "^1.15.0", "glob": "^13.0.6", "rimraf": "^6.1.3", - "ts-node": "10.9.2", - "verdaccio": "6.5.0", - "yaml": "2.8.3" + "ts-node": "10.9.2" }, "volta": { "extends": "../../package.json" diff --git a/dev-packages/e2e-tests/prepare.ts b/dev-packages/e2e-tests/prepare.ts index f0bcca7ade5f..049b727b1b21 100644 --- a/dev-packages/e2e-tests/prepare.ts +++ b/dev-packages/e2e-tests/prepare.ts @@ -1,12 +1,12 @@ /* eslint-disable no-console */ import * as dotenv from 'dotenv'; -import { registrySetup } from './registrySetup'; +import { syncPackedTarballSymlinks } from './lib/syncPackedTarballSymlinks'; async function run(): Promise { // Load environment variables from .env file locally dotenv.config(); - await registrySetup({ daemonize: true }); + syncPackedTarballSymlinks(); } run().catch(error => { diff --git a/dev-packages/e2e-tests/registrySetup.ts b/dev-packages/e2e-tests/registrySetup.ts deleted file mode 100644 index fade430b6b55..000000000000 --- a/dev-packages/e2e-tests/registrySetup.ts +++ /dev/null @@ -1,140 +0,0 @@ -/* eslint-disable no-console */ -import { spawn, spawnSync, type ChildProcess } from 'child_process'; -import * as fs from 'fs'; -import * as http from 'http'; -import * as path from 'path'; -import { publishPackages } from './lib/publishPackages'; - -const VERDACCIO_PORT = 4873; - -let verdaccioChild: ChildProcess | undefined; - -export interface RegistrySetupOptions { - /** - * When true, Verdaccio is spawned detached with stdio disconnected from the parent, then - * the child is unref'd after a successful setup so the parent can exit while the registry - * keeps running (e.g. `yarn test:prepare` then installs against 127.0.0.1:4873). - */ - daemonize?: boolean; -} - -/** Stops any Verdaccio runner from a previous prepare/run so port 4873 is free. */ -function killStrayVerdaccioRunner(): void { - spawnSync('pkill', ['-f', 'verdaccio-runner.mjs'], { stdio: 'ignore' }); -} - -async function groupCIOutput(groupTitle: string, fn: () => void | Promise): Promise { - if (process.env.CI) { - console.log(`::group::${groupTitle}`); - try { - await Promise.resolve(fn()); - } finally { - console.log('::endgroup::'); - } - } else { - await Promise.resolve(fn()); - } -} - -function waitUntilVerdaccioResponds(maxRetries: number = 60): Promise { - const pingUrl = `http://127.0.0.1:${VERDACCIO_PORT}/-/ping`; - - function tryOnce(): Promise { - return new Promise(resolve => { - const req = http.get(pingUrl, res => { - res.resume(); - resolve((res.statusCode ?? 0) > 0 && (res.statusCode ?? 500) < 500); - }); - req.on('error', () => resolve(false)); - req.setTimeout(2000, () => { - req.destroy(); - resolve(false); - }); - }); - } - - return (async () => { - for (let i = 0; i < maxRetries; i++) { - if (await tryOnce()) { - return; - } - await new Promise(r => setTimeout(r, 1000)); - } - throw new Error('Verdaccio did not start in time.'); - })(); -} - -function startVerdaccioChild(configPath: string, port: number, daemonize: boolean): ChildProcess { - const runnerPath = path.join(__dirname, 'verdaccio-runner.mjs'); - const verbose = process.env.E2E_VERDACCIO_VERBOSE === '1'; - return spawn(process.execPath, [runnerPath, configPath, String(port)], { - detached: daemonize, - stdio: daemonize && !verbose ? 'ignore' : 'inherit', - }); -} - -async function stopVerdaccioChild(): Promise { - const child = verdaccioChild; - verdaccioChild = undefined; - if (!child || child.killed) { - return; - } - child.kill('SIGTERM'); - await new Promise(resolve => { - const timeoutId = setTimeout(resolve, 5000); - child.once('exit', () => { - clearTimeout(timeoutId); - resolve(); - }); - }); -} - -/** Drop the child handle so the parent process can exit; Verdaccio keeps running. */ -function detachVerdaccioRunner(): void { - const child = verdaccioChild; - verdaccioChild = undefined; - if (child && !child.killed) { - child.unref(); - } -} - -export async function registrySetup(options: RegistrySetupOptions = {}): Promise { - const { daemonize = false } = options; - await groupCIOutput('Test Registry Setup', async () => { - killStrayVerdaccioRunner(); - - const configPath = path.join(__dirname, 'verdaccio-config', 'config.yaml'); - const storagePath = path.join(__dirname, 'verdaccio-config', 'storage'); - - // Clear previous registry storage to ensure a fresh state - fs.rmSync(storagePath, { recursive: true, force: true }); - - // Verdaccio runs in a child process so tarball uploads are not starved by the - // same Node event loop as ts-node (in-process runServer + npm publish could hang). - console.log('Starting Verdaccio...'); - - verdaccioChild = startVerdaccioChild(configPath, VERDACCIO_PORT, daemonize); - - try { - await waitUntilVerdaccioResponds(60); - console.log('Verdaccio is ready'); - - await publishPackages(); - } catch (error) { - await stopVerdaccioChild(); - throw error; - } - }); - - if (daemonize) { - detachVerdaccioRunner(); - } - - console.log(''); - console.log(''); -} - -export async function registryCleanup(): Promise { - await stopVerdaccioChild(); - killStrayVerdaccioRunner(); -} diff --git a/dev-packages/e2e-tests/run.ts b/dev-packages/e2e-tests/run.ts index 8542b99c3708..c10934a81b8d 100644 --- a/dev-packages/e2e-tests/run.ts +++ b/dev-packages/e2e-tests/run.ts @@ -6,7 +6,8 @@ import { sync as globSync } from 'glob'; import { tmpdir } from 'os'; import { join, resolve } from 'path'; import { copyToTemp } from './lib/copyToTemp'; -import { registryCleanup, registrySetup } from './registrySetup'; +import { syncPackedTarballSymlinks } from './lib/syncPackedTarballSymlinks'; +import { addPnpmOverrides } from './lib/pnpmOverrides'; interface SentryTestVariant { 'build-command': string; @@ -184,75 +185,66 @@ async function run(): Promise { ...envVarsToInject, }; - const skipRegistry = !!process.env.SKIP_REGISTRY; + console.log('Syncing packed tarball symlinks...'); + console.log(''); - try { - if (!skipRegistry) { - await registrySetup(); - } + syncPackedTarballSymlinks(); - console.log('Cleaning test-applications...'); - console.log(''); + console.log('Cleaning test-applications...'); + console.log(''); - await asyncExec('pnpm clean:test-applications', { env, cwd: __dirname }); - await asyncExec('pnpm cache delete "@sentry/*"', { env, cwd: __dirname }); + await asyncExec('pnpm clean:test-applications', { env, cwd: __dirname }); + await asyncExec('pnpm cache delete "@sentry/*"', { env, cwd: __dirname }); - const testAppPaths = appName ? [appName.trim()] : globSync('*', { cwd: `${__dirname}/test-applications/` }); + const testAppPaths = appName ? [appName.trim()] : globSync('*', { cwd: `${__dirname}/test-applications/` }); - console.log(`Runnings tests for: ${testAppPaths.join(', ')}`); - console.log(''); + console.log(`Runnings tests for: ${testAppPaths.join(', ')}`); + console.log(''); - for (const testAppPath of testAppPaths) { - const originalPath = resolve('test-applications', testAppPath); - const tmpDirPath = await mkdtemp(join(tmpdir(), `sentry-e2e-tests-${appName}-`)); + const packedDirPath = resolve(__dirname, 'packed'); - await copyToTemp(originalPath, tmpDirPath); - const cwd = tmpDirPath; - // Resolve variant if needed - const { buildCommand, assertCommand, testLabel, matchedVariantLabel } = variantLabel - ? await getVariantBuildCommand(join(tmpDirPath, 'package.json'), variantLabel, testAppPath) - : { - buildCommand: 'pnpm test:build', - assertCommand: 'pnpm test:assert', - testLabel: testAppPath, - }; + for (const testAppPath of testAppPaths) { + const originalPath = resolve('test-applications', testAppPath); + const tmpDirPath = await mkdtemp(join(tmpdir(), `sentry-e2e-tests-${appName}-`)); - // Print which variant we're using if found - if (matchedVariantLabel) { - console.log(`\n\nUsing variant: "${matchedVariantLabel}"\n\n`); - } + await copyToTemp(originalPath, tmpDirPath); + await addPnpmOverrides(tmpDirPath, packedDirPath); - console.log(`Building ${testLabel} in ${tmpDirPath}...`); - await asyncExec(`volta run ${buildCommand}`, { env, cwd }); - - console.log(`Testing ${testLabel}...`); - // Pass command as a string to support shell features (env vars, operators like &&) - // This matches how buildCommand is handled for consistency - // Properly quote test flags to preserve spaces and special characters - const quotedTestFlags = testFlags.map(flag => { - // If flag contains spaces or special shell characters, quote it - if ( - flag.includes(' ') || - flag.includes('"') || - flag.includes("'") || - flag.includes('$') || - flag.includes('`') - ) { - // Escape single quotes and wrap in single quotes (safest for shell) - return `'${flag.replace(/'/g, "'\\''")}'`; - } - return flag; - }); - const testCommand = `volta run ${assertCommand}${quotedTestFlags.length > 0 ? ` ${quotedTestFlags.join(' ')}` : ''}`; - await asyncExec(testCommand, { env, cwd }); + const cwd = tmpDirPath; + // Resolve variant if needed + const { buildCommand, assertCommand, testLabel, matchedVariantLabel } = variantLabel + ? await getVariantBuildCommand(join(tmpDirPath, 'package.json'), variantLabel, testAppPath) + : { + buildCommand: 'pnpm test:build', + assertCommand: 'pnpm test:assert', + testLabel: testAppPath, + }; - // clean up (although this is tmp, still nice to do) - await rm(tmpDirPath, { recursive: true }); - } - } finally { - if (!skipRegistry) { - await registryCleanup(); + // Print which variant we're using if found + if (matchedVariantLabel) { + console.log(`\n\nUsing variant: "${matchedVariantLabel}"\n\n`); } + + console.log(`Building ${testLabel} in ${tmpDirPath}...`); + await asyncExec(`volta run ${buildCommand}`, { env, cwd }); + + console.log(`Testing ${testLabel}...`); + // Pass command as a string to support shell features (env vars, operators like &&) + // This matches how buildCommand is handled for consistency + // Properly quote test flags to preserve spaces and special characters + const quotedTestFlags = testFlags.map(flag => { + // If flag contains spaces or special shell characters, quote it + if (flag.includes(' ') || flag.includes('"') || flag.includes("'") || flag.includes('$') || flag.includes('`')) { + // Escape single quotes and wrap in single quotes (safest for shell) + return `'${flag.replace(/'/g, "'\\''")}'`; + } + return flag; + }); + const testCommand = `volta run ${assertCommand}${quotedTestFlags.length > 0 ? ` ${quotedTestFlags.join(' ')}` : ''}`; + await asyncExec(testCommand, { env, cwd }); + + // clean up (although this is tmp, still nice to do) + await rm(tmpDirPath, { recursive: true }); } } diff --git a/dev-packages/e2e-tests/test-applications/angular-17/.npmrc b/dev-packages/e2e-tests/test-applications/angular-17/.npmrc deleted file mode 100644 index 070f80f05092..000000000000 --- a/dev-packages/e2e-tests/test-applications/angular-17/.npmrc +++ /dev/null @@ -1,2 +0,0 @@ -@sentry:registry=http://127.0.0.1:4873 -@sentry-internal:registry=http://127.0.0.1:4873 diff --git a/dev-packages/e2e-tests/test-applications/angular-17/package.json b/dev-packages/e2e-tests/test-applications/angular-17/package.json index 0d27f73f94e8..8cd141645d15 100644 --- a/dev-packages/e2e-tests/test-applications/angular-17/package.json +++ b/dev-packages/e2e-tests/test-applications/angular-17/package.json @@ -23,7 +23,7 @@ "@angular/platform-browser": "^17.1.0", "@angular/platform-browser-dynamic": "^17.1.0", "@angular/router": "^17.1.0", - "@sentry/angular": "* || latest", + "@sentry/angular": "file:../../packed/sentry-angular-packed.tgz", "rxjs": "~7.8.0", "tslib": "^2.3.0", "zone.js": "~0.14.3" @@ -31,7 +31,7 @@ "devDependencies": { "@playwright/test": "~1.56.0", "@sentry-internal/test-utils": "link:../../../test-utils", - "@sentry/core": "latest || *", + "@sentry/core": "file:../../packed/sentry-core-packed.tgz", "@angular-devkit/build-angular": "^17.1.1", "@angular/cli": "^17.1.1", "@angular/compiler-cli": "^17.1.0", diff --git a/dev-packages/e2e-tests/test-applications/angular-18/.npmrc b/dev-packages/e2e-tests/test-applications/angular-18/.npmrc deleted file mode 100644 index 070f80f05092..000000000000 --- a/dev-packages/e2e-tests/test-applications/angular-18/.npmrc +++ /dev/null @@ -1,2 +0,0 @@ -@sentry:registry=http://127.0.0.1:4873 -@sentry-internal:registry=http://127.0.0.1:4873 diff --git a/dev-packages/e2e-tests/test-applications/angular-18/package.json b/dev-packages/e2e-tests/test-applications/angular-18/package.json index a32d3f5de99f..06cd30bc5ac5 100644 --- a/dev-packages/e2e-tests/test-applications/angular-18/package.json +++ b/dev-packages/e2e-tests/test-applications/angular-18/package.json @@ -23,7 +23,7 @@ "@angular/platform-browser": "^18.0.0", "@angular/platform-browser-dynamic": "^18.0.0", "@angular/router": "^18.0.0", - "@sentry/angular": "* || latest", + "@sentry/angular": "file:../../packed/sentry-angular-packed.tgz", "rxjs": "~7.8.0", "tslib": "^2.3.0", "zone.js": "~0.14.3" @@ -31,7 +31,7 @@ "devDependencies": { "@playwright/test": "~1.56.0", "@sentry-internal/test-utils": "link:../../../test-utils", - "@sentry/core": "latest || *", + "@sentry/core": "file:../../packed/sentry-core-packed.tgz", "@angular-devkit/build-angular": "^18.0.0", "@angular/cli": "^18.0.0", "@angular/compiler-cli": "^18.0.0", diff --git a/dev-packages/e2e-tests/test-applications/angular-19/.npmrc b/dev-packages/e2e-tests/test-applications/angular-19/.npmrc deleted file mode 100644 index 070f80f05092..000000000000 --- a/dev-packages/e2e-tests/test-applications/angular-19/.npmrc +++ /dev/null @@ -1,2 +0,0 @@ -@sentry:registry=http://127.0.0.1:4873 -@sentry-internal:registry=http://127.0.0.1:4873 diff --git a/dev-packages/e2e-tests/test-applications/angular-19/package.json b/dev-packages/e2e-tests/test-applications/angular-19/package.json index 1e02f440b0a9..2b6c81fb7a20 100644 --- a/dev-packages/e2e-tests/test-applications/angular-19/package.json +++ b/dev-packages/e2e-tests/test-applications/angular-19/package.json @@ -23,7 +23,7 @@ "@angular/platform-browser": "^19.0.0", "@angular/platform-browser-dynamic": "^19.0.0", "@angular/router": "^19.0.0", - "@sentry/angular": "* || latest", + "@sentry/angular": "file:../../packed/sentry-angular-packed.tgz", "rxjs": "~7.8.0", "tslib": "^2.3.0", "zone.js": "~0.15.0" @@ -34,7 +34,7 @@ "@angular/compiler-cli": "^19.0.0", "@playwright/test": "~1.56.0", "@sentry-internal/test-utils": "link:../../../test-utils", - "@sentry/core": "latest || *", + "@sentry/core": "file:../../packed/sentry-core-packed.tgz", "@types/jasmine": "~5.1.0", "http-server": "^14.1.1", "jasmine-core": "~5.4.0", diff --git a/dev-packages/e2e-tests/test-applications/angular-20/.npmrc b/dev-packages/e2e-tests/test-applications/angular-20/.npmrc deleted file mode 100644 index 070f80f05092..000000000000 --- a/dev-packages/e2e-tests/test-applications/angular-20/.npmrc +++ /dev/null @@ -1,2 +0,0 @@ -@sentry:registry=http://127.0.0.1:4873 -@sentry-internal:registry=http://127.0.0.1:4873 diff --git a/dev-packages/e2e-tests/test-applications/angular-20/package.json b/dev-packages/e2e-tests/test-applications/angular-20/package.json index fbf27580ed67..922964eed0c1 100644 --- a/dev-packages/e2e-tests/test-applications/angular-20/package.json +++ b/dev-packages/e2e-tests/test-applications/angular-20/package.json @@ -23,7 +23,7 @@ "@angular/platform-browser": "^20.0.0", "@angular/platform-browser-dynamic": "^20.0.0", "@angular/router": "^20.0.0", - "@sentry/angular": "* || latest", + "@sentry/angular": "file:../../packed/sentry-angular-packed.tgz", "rxjs": "~7.8.0", "tslib": "^2.3.0", "zone.js": "~0.15.0" @@ -34,7 +34,7 @@ "@angular/compiler-cli": "^20.0.0", "@playwright/test": "~1.56.0", "@sentry-internal/test-utils": "link:../../../test-utils", - "@sentry/core": "latest || *", + "@sentry/core": "file:../../packed/sentry-core-packed.tgz", "@types/jasmine": "~5.1.0", "http-server": "^14.1.1", "jasmine-core": "~5.4.0", diff --git a/dev-packages/e2e-tests/test-applications/angular-21/.npmrc b/dev-packages/e2e-tests/test-applications/angular-21/.npmrc deleted file mode 100644 index 070f80f05092..000000000000 --- a/dev-packages/e2e-tests/test-applications/angular-21/.npmrc +++ /dev/null @@ -1,2 +0,0 @@ -@sentry:registry=http://127.0.0.1:4873 -@sentry-internal:registry=http://127.0.0.1:4873 diff --git a/dev-packages/e2e-tests/test-applications/angular-21/package.json b/dev-packages/e2e-tests/test-applications/angular-21/package.json index 0558d370e2c5..b7401593849f 100644 --- a/dev-packages/e2e-tests/test-applications/angular-21/package.json +++ b/dev-packages/e2e-tests/test-applications/angular-21/package.json @@ -24,7 +24,7 @@ "@angular/platform-browser": "^21.0.0", "@angular/platform-browser-dynamic": "^21.0.0", "@angular/router": "^21.0.0", - "@sentry/angular": "* || latest", + "@sentry/angular": "file:../../packed/sentry-angular-packed.tgz", "rxjs": "~7.8.0", "tslib": "^2.3.0", "zone.js": "~0.15.0" @@ -35,7 +35,7 @@ "@angular/compiler-cli": "^21.0.0", "@playwright/test": "~1.56.0", "@sentry-internal/test-utils": "link:../../../test-utils", - "@sentry/core": "latest || *", + "@sentry/core": "file:../../packed/sentry-core-packed.tgz", "@types/jasmine": "~5.1.0", "http-server": "^14.1.1", "jasmine-core": "~5.4.0", diff --git a/dev-packages/e2e-tests/test-applications/astro-4/.npmrc b/dev-packages/e2e-tests/test-applications/astro-4/.npmrc deleted file mode 100644 index 070f80f05092..000000000000 --- a/dev-packages/e2e-tests/test-applications/astro-4/.npmrc +++ /dev/null @@ -1,2 +0,0 @@ -@sentry:registry=http://127.0.0.1:4873 -@sentry-internal:registry=http://127.0.0.1:4873 diff --git a/dev-packages/e2e-tests/test-applications/astro-4/package.json b/dev-packages/e2e-tests/test-applications/astro-4/package.json index 339f5fd18c7d..0afd444f6d3d 100644 --- a/dev-packages/e2e-tests/test-applications/astro-4/package.json +++ b/dev-packages/e2e-tests/test-applications/astro-4/package.json @@ -15,7 +15,7 @@ "@astrojs/check": "0.9.2", "@astrojs/node": "8.3.4", "@playwright/test": "~1.56.0", - "@sentry/astro": "* || latest", + "@sentry/astro": "file:../../packed/sentry-astro-packed.tgz", "@sentry-internal/test-utils": "link:../../../test-utils", "@spotlightjs/astro": "2.1.6", "astro": "4.16.19", diff --git a/dev-packages/e2e-tests/test-applications/astro-5-cf-workers/.npmrc b/dev-packages/e2e-tests/test-applications/astro-5-cf-workers/.npmrc deleted file mode 100644 index 070f80f05092..000000000000 --- a/dev-packages/e2e-tests/test-applications/astro-5-cf-workers/.npmrc +++ /dev/null @@ -1,2 +0,0 @@ -@sentry:registry=http://127.0.0.1:4873 -@sentry-internal:registry=http://127.0.0.1:4873 diff --git a/dev-packages/e2e-tests/test-applications/astro-5-cf-workers/package.json b/dev-packages/e2e-tests/test-applications/astro-5-cf-workers/package.json index 400ab6144248..fabe0ce2333b 100644 --- a/dev-packages/e2e-tests/test-applications/astro-5-cf-workers/package.json +++ b/dev-packages/e2e-tests/test-applications/astro-5-cf-workers/package.json @@ -13,8 +13,8 @@ "@astrojs/cloudflare": "^12.6.12", "@playwright/test": "~1.56.0", "@sentry-internal/test-utils": "link:../../../test-utils", - "@sentry/astro": "latest || *", - "@sentry/cloudflare": "latest || *", + "@sentry/astro": "file:../../packed/sentry-astro-packed.tgz", + "@sentry/cloudflare": "file:../../packed/sentry-cloudflare-packed.tgz", "astro": "^5.17.1" }, "devDependencies": { diff --git a/dev-packages/e2e-tests/test-applications/astro-5/.npmrc b/dev-packages/e2e-tests/test-applications/astro-5/.npmrc deleted file mode 100644 index 070f80f05092..000000000000 --- a/dev-packages/e2e-tests/test-applications/astro-5/.npmrc +++ /dev/null @@ -1,2 +0,0 @@ -@sentry:registry=http://127.0.0.1:4873 -@sentry-internal:registry=http://127.0.0.1:4873 diff --git a/dev-packages/e2e-tests/test-applications/astro-5/package.json b/dev-packages/e2e-tests/test-applications/astro-5/package.json index f42b22b3d07f..268ce9ed82ca 100644 --- a/dev-packages/e2e-tests/test-applications/astro-5/package.json +++ b/dev-packages/e2e-tests/test-applications/astro-5/package.json @@ -16,7 +16,7 @@ "@astrojs/node": "^9.0.0", "@playwright/test": "~1.56.0", "@sentry-internal/test-utils": "link:../../../test-utils", - "@sentry/astro": "latest || *", + "@sentry/astro": "file:../../packed/sentry-astro-packed.tgz", "astro": "^5.0.3" }, "pnpm": { diff --git a/dev-packages/e2e-tests/test-applications/astro-6-cf-workers/.npmrc b/dev-packages/e2e-tests/test-applications/astro-6-cf-workers/.npmrc deleted file mode 100644 index 070f80f05092..000000000000 --- a/dev-packages/e2e-tests/test-applications/astro-6-cf-workers/.npmrc +++ /dev/null @@ -1,2 +0,0 @@ -@sentry:registry=http://127.0.0.1:4873 -@sentry-internal:registry=http://127.0.0.1:4873 diff --git a/dev-packages/e2e-tests/test-applications/astro-6-cf-workers/package.json b/dev-packages/e2e-tests/test-applications/astro-6-cf-workers/package.json index 2e7cfa8e62dd..4869975f7519 100644 --- a/dev-packages/e2e-tests/test-applications/astro-6-cf-workers/package.json +++ b/dev-packages/e2e-tests/test-applications/astro-6-cf-workers/package.json @@ -16,8 +16,8 @@ "@astrojs/cloudflare": "^13.0.2", "@playwright/test": "~1.56.0", "@sentry-internal/test-utils": "link:../../../test-utils", - "@sentry/astro": "latest || *", - "@sentry/cloudflare": "latest || *", + "@sentry/astro": "file:../../packed/sentry-astro-packed.tgz", + "@sentry/cloudflare": "file:../../packed/sentry-cloudflare-packed.tgz", "astro": "^6.0.0", "wrangler": "^4.72.0" }, diff --git a/dev-packages/e2e-tests/test-applications/astro-6/.npmrc b/dev-packages/e2e-tests/test-applications/astro-6/.npmrc deleted file mode 100644 index 070f80f05092..000000000000 --- a/dev-packages/e2e-tests/test-applications/astro-6/.npmrc +++ /dev/null @@ -1,2 +0,0 @@ -@sentry:registry=http://127.0.0.1:4873 -@sentry-internal:registry=http://127.0.0.1:4873 diff --git a/dev-packages/e2e-tests/test-applications/astro-6/package.json b/dev-packages/e2e-tests/test-applications/astro-6/package.json index 050ea8980e06..37f61a774e32 100644 --- a/dev-packages/e2e-tests/test-applications/astro-6/package.json +++ b/dev-packages/e2e-tests/test-applications/astro-6/package.json @@ -15,7 +15,7 @@ "@astrojs/node": "^10.0.0", "@playwright/test": "~1.56.0", "@sentry-internal/test-utils": "link:../../../test-utils", - "@sentry/astro": "latest || *", + "@sentry/astro": "file:../../packed/sentry-astro-packed.tgz", "astro": "^6.0.6" }, "volta": { diff --git a/dev-packages/e2e-tests/test-applications/aws-serverless/.npmrc b/dev-packages/e2e-tests/test-applications/aws-serverless/.npmrc deleted file mode 100644 index 070f80f05092..000000000000 --- a/dev-packages/e2e-tests/test-applications/aws-serverless/.npmrc +++ /dev/null @@ -1,2 +0,0 @@ -@sentry:registry=http://127.0.0.1:4873 -@sentry-internal:registry=http://127.0.0.1:4873 diff --git a/dev-packages/e2e-tests/test-applications/aws-serverless/package.json b/dev-packages/e2e-tests/test-applications/aws-serverless/package.json index a3d164e15813..b417d8022667 100644 --- a/dev-packages/e2e-tests/test-applications/aws-serverless/package.json +++ b/dev-packages/e2e-tests/test-applications/aws-serverless/package.json @@ -25,6 +25,12 @@ "volta": { "extends": "../../package.json" }, + "//": "We need to override this here again to ensure this is not overwritten by the test runner", + "pnpm": { + "overrides": { + "@sentry/aws-serverless": "link:../../../../packages/aws-serverless/build/aws/dist-serverless/" + } + }, "sentryTest": { "variants": [ { diff --git a/dev-packages/e2e-tests/test-applications/browser-mfe-vite/.npmrc b/dev-packages/e2e-tests/test-applications/browser-mfe-vite/.npmrc deleted file mode 100644 index 070f80f05092..000000000000 --- a/dev-packages/e2e-tests/test-applications/browser-mfe-vite/.npmrc +++ /dev/null @@ -1,2 +0,0 @@ -@sentry:registry=http://127.0.0.1:4873 -@sentry-internal:registry=http://127.0.0.1:4873 diff --git a/dev-packages/e2e-tests/test-applications/browser-mfe-vite/apps/mfe-header/package.json b/dev-packages/e2e-tests/test-applications/browser-mfe-vite/apps/mfe-header/package.json index 04bac1dafd66..34ffe98a592f 100644 --- a/dev-packages/e2e-tests/test-applications/browser-mfe-vite/apps/mfe-header/package.json +++ b/dev-packages/e2e-tests/test-applications/browser-mfe-vite/apps/mfe-header/package.json @@ -8,7 +8,7 @@ "preview": "vite preview --port 3032" }, "dependencies": { - "@sentry/browser": "latest || *", + "@sentry/browser": "file:../../../../packed/sentry-browser-packed.tgz", "react": "^18.3.1", "react-dom": "^18.3.1" }, diff --git a/dev-packages/e2e-tests/test-applications/browser-mfe-vite/apps/mfe-one/package.json b/dev-packages/e2e-tests/test-applications/browser-mfe-vite/apps/mfe-one/package.json index 6db9d42e97ac..b286b158b47c 100644 --- a/dev-packages/e2e-tests/test-applications/browser-mfe-vite/apps/mfe-one/package.json +++ b/dev-packages/e2e-tests/test-applications/browser-mfe-vite/apps/mfe-one/package.json @@ -8,7 +8,7 @@ "preview": "vite preview --port 3033" }, "dependencies": { - "@sentry/browser": "latest || *", + "@sentry/browser": "file:../../../../packed/sentry-browser-packed.tgz", "react": "^18.3.1", "react-dom": "^18.3.1" }, diff --git a/dev-packages/e2e-tests/test-applications/browser-mfe-vite/apps/shell/package.json b/dev-packages/e2e-tests/test-applications/browser-mfe-vite/apps/shell/package.json index a3437e079f7f..778b8439a635 100644 --- a/dev-packages/e2e-tests/test-applications/browser-mfe-vite/apps/shell/package.json +++ b/dev-packages/e2e-tests/test-applications/browser-mfe-vite/apps/shell/package.json @@ -8,8 +8,8 @@ "preview": "vite preview --port 3030" }, "dependencies": { - "@sentry/browser": "latest || *", - "@sentry/react": "latest || *", + "@sentry/browser": "file:../../../../packed/sentry-browser-packed.tgz", + "@sentry/react": "file:../../../../packed/sentry-react-packed.tgz", "react": "^18.3.1", "react-dom": "^18.3.1" }, diff --git a/dev-packages/e2e-tests/test-applications/browser-webworker-vite/.npmrc b/dev-packages/e2e-tests/test-applications/browser-webworker-vite/.npmrc deleted file mode 100644 index 070f80f05092..000000000000 --- a/dev-packages/e2e-tests/test-applications/browser-webworker-vite/.npmrc +++ /dev/null @@ -1,2 +0,0 @@ -@sentry:registry=http://127.0.0.1:4873 -@sentry-internal:registry=http://127.0.0.1:4873 diff --git a/dev-packages/e2e-tests/test-applications/browser-webworker-vite/package.json b/dev-packages/e2e-tests/test-applications/browser-webworker-vite/package.json index f6aae0e57df3..1c391eb7cf5e 100644 --- a/dev-packages/e2e-tests/test-applications/browser-webworker-vite/package.json +++ b/dev-packages/e2e-tests/test-applications/browser-webworker-vite/package.json @@ -18,7 +18,7 @@ "vite": "^7.0.4" }, "dependencies": { - "@sentry/browser": "latest || *", + "@sentry/browser": "file:../../packed/sentry-browser-packed.tgz", "@sentry/vite-plugin": "^5.2.0" }, "volta": { diff --git a/dev-packages/e2e-tests/test-applications/cloudflare-hono/.npmrc b/dev-packages/e2e-tests/test-applications/cloudflare-hono/.npmrc deleted file mode 100644 index 070f80f05092..000000000000 --- a/dev-packages/e2e-tests/test-applications/cloudflare-hono/.npmrc +++ /dev/null @@ -1,2 +0,0 @@ -@sentry:registry=http://127.0.0.1:4873 -@sentry-internal:registry=http://127.0.0.1:4873 diff --git a/dev-packages/e2e-tests/test-applications/cloudflare-hono/package.json b/dev-packages/e2e-tests/test-applications/cloudflare-hono/package.json index 3d536e6fbabe..b3b8695148fb 100644 --- a/dev-packages/e2e-tests/test-applications/cloudflare-hono/package.json +++ b/dev-packages/e2e-tests/test-applications/cloudflare-hono/package.json @@ -11,7 +11,7 @@ "test:assert": "pnpm typecheck && vitest run ." }, "dependencies": { - "@sentry/cloudflare": "latest || *", + "@sentry/cloudflare": "file:../../packed/sentry-cloudflare-packed.tgz", "hono": "4.12.12" }, "devDependencies": { diff --git a/dev-packages/e2e-tests/test-applications/cloudflare-local-workers/.npmrc b/dev-packages/e2e-tests/test-applications/cloudflare-local-workers/.npmrc deleted file mode 100644 index 070f80f05092..000000000000 --- a/dev-packages/e2e-tests/test-applications/cloudflare-local-workers/.npmrc +++ /dev/null @@ -1,2 +0,0 @@ -@sentry:registry=http://127.0.0.1:4873 -@sentry-internal:registry=http://127.0.0.1:4873 diff --git a/dev-packages/e2e-tests/test-applications/cloudflare-local-workers/package.json b/dev-packages/e2e-tests/test-applications/cloudflare-local-workers/package.json index 160b8a9cdc03..7433244fc417 100644 --- a/dev-packages/e2e-tests/test-applications/cloudflare-local-workers/package.json +++ b/dev-packages/e2e-tests/test-applications/cloudflare-local-workers/package.json @@ -15,7 +15,7 @@ "test:dev": "TEST_ENV=development playwright test" }, "dependencies": { - "@sentry/cloudflare": "latest || *" + "@sentry/cloudflare": "file:../../packed/sentry-cloudflare-packed.tgz" }, "devDependencies": { "@playwright/test": "~1.56.0", diff --git a/dev-packages/e2e-tests/test-applications/cloudflare-mcp/.npmrc b/dev-packages/e2e-tests/test-applications/cloudflare-mcp/.npmrc deleted file mode 100644 index 070f80f05092..000000000000 --- a/dev-packages/e2e-tests/test-applications/cloudflare-mcp/.npmrc +++ /dev/null @@ -1,2 +0,0 @@ -@sentry:registry=http://127.0.0.1:4873 -@sentry-internal:registry=http://127.0.0.1:4873 diff --git a/dev-packages/e2e-tests/test-applications/cloudflare-mcp/package.json b/dev-packages/e2e-tests/test-applications/cloudflare-mcp/package.json index 37b3352bdcfc..bae4b4ffd272 100644 --- a/dev-packages/e2e-tests/test-applications/cloudflare-mcp/package.json +++ b/dev-packages/e2e-tests/test-applications/cloudflare-mcp/package.json @@ -16,7 +16,7 @@ }, "dependencies": { "@modelcontextprotocol/sdk": "^1.24.0", - "@sentry/cloudflare": "latest || *", + "@sentry/cloudflare": "file:../../packed/sentry-cloudflare-packed.tgz", "agents": "0.3.10", "zod": "^3.25.76" }, diff --git a/dev-packages/e2e-tests/test-applications/cloudflare-workers/.npmrc b/dev-packages/e2e-tests/test-applications/cloudflare-workers/.npmrc deleted file mode 100644 index 070f80f05092..000000000000 --- a/dev-packages/e2e-tests/test-applications/cloudflare-workers/.npmrc +++ /dev/null @@ -1,2 +0,0 @@ -@sentry:registry=http://127.0.0.1:4873 -@sentry-internal:registry=http://127.0.0.1:4873 diff --git a/dev-packages/e2e-tests/test-applications/cloudflare-workers/package.json b/dev-packages/e2e-tests/test-applications/cloudflare-workers/package.json index 344337612165..b8b028797805 100644 --- a/dev-packages/e2e-tests/test-applications/cloudflare-workers/package.json +++ b/dev-packages/e2e-tests/test-applications/cloudflare-workers/package.json @@ -15,7 +15,7 @@ "test:dev": "TEST_ENV=development playwright test" }, "dependencies": { - "@sentry/cloudflare": "latest || *" + "@sentry/cloudflare": "file:../../packed/sentry-cloudflare-packed.tgz" }, "devDependencies": { "@playwright/test": "~1.56.0", diff --git a/dev-packages/e2e-tests/test-applications/cloudflare-workersentrypoint/.npmrc b/dev-packages/e2e-tests/test-applications/cloudflare-workersentrypoint/.npmrc deleted file mode 100644 index 070f80f05092..000000000000 --- a/dev-packages/e2e-tests/test-applications/cloudflare-workersentrypoint/.npmrc +++ /dev/null @@ -1,2 +0,0 @@ -@sentry:registry=http://127.0.0.1:4873 -@sentry-internal:registry=http://127.0.0.1:4873 diff --git a/dev-packages/e2e-tests/test-applications/cloudflare-workersentrypoint/package.json b/dev-packages/e2e-tests/test-applications/cloudflare-workersentrypoint/package.json index a0a1b020df86..83ac7bce286d 100644 --- a/dev-packages/e2e-tests/test-applications/cloudflare-workersentrypoint/package.json +++ b/dev-packages/e2e-tests/test-applications/cloudflare-workersentrypoint/package.json @@ -15,7 +15,7 @@ "test:dev": "TEST_ENV=development playwright test" }, "dependencies": { - "@sentry/cloudflare": "latest || *" + "@sentry/cloudflare": "file:../../packed/sentry-cloudflare-packed.tgz" }, "devDependencies": { "@playwright/test": "~1.56.0", diff --git a/dev-packages/e2e-tests/test-applications/create-react-app/.npmrc b/dev-packages/e2e-tests/test-applications/create-react-app/.npmrc deleted file mode 100644 index 070f80f05092..000000000000 --- a/dev-packages/e2e-tests/test-applications/create-react-app/.npmrc +++ /dev/null @@ -1,2 +0,0 @@ -@sentry:registry=http://127.0.0.1:4873 -@sentry-internal:registry=http://127.0.0.1:4873 diff --git a/dev-packages/e2e-tests/test-applications/create-react-app/package.json b/dev-packages/e2e-tests/test-applications/create-react-app/package.json index 0c2bc337d396..30f745d60bc7 100644 --- a/dev-packages/e2e-tests/test-applications/create-react-app/package.json +++ b/dev-packages/e2e-tests/test-applications/create-react-app/package.json @@ -3,7 +3,7 @@ "version": "0.1.0", "private": true, "dependencies": { - "@sentry/react": "latest || *", + "@sentry/react": "file:../../packed/sentry-react-packed.tgz", "@types/node": "^18.19.1", "@types/react": "18.0.0", "@types/react-dom": "18.0.0", diff --git a/dev-packages/e2e-tests/test-applications/create-remix-app-express-vite-dev/.npmrc b/dev-packages/e2e-tests/test-applications/create-remix-app-express-vite-dev/.npmrc deleted file mode 100644 index 070f80f05092..000000000000 --- a/dev-packages/e2e-tests/test-applications/create-remix-app-express-vite-dev/.npmrc +++ /dev/null @@ -1,2 +0,0 @@ -@sentry:registry=http://127.0.0.1:4873 -@sentry-internal:registry=http://127.0.0.1:4873 diff --git a/dev-packages/e2e-tests/test-applications/create-remix-app-express-vite-dev/package.json b/dev-packages/e2e-tests/test-applications/create-remix-app-express-vite-dev/package.json index d495d14019b6..a413c5c31ba0 100644 --- a/dev-packages/e2e-tests/test-applications/create-remix-app-express-vite-dev/package.json +++ b/dev-packages/e2e-tests/test-applications/create-remix-app-express-vite-dev/package.json @@ -16,7 +16,7 @@ "@remix-run/express": "^2.17.4", "@remix-run/node": "^2.17.4", "@remix-run/react": "^2.17.4", - "@sentry/remix": "latest || *", + "@sentry/remix": "file:../../packed/sentry-remix-packed.tgz", "compression": "^1.7.4", "express": "^4.21.2", "isbot": "^4.1.0", diff --git a/dev-packages/e2e-tests/test-applications/create-remix-app-express/.npmrc b/dev-packages/e2e-tests/test-applications/create-remix-app-express/.npmrc deleted file mode 100644 index 070f80f05092..000000000000 --- a/dev-packages/e2e-tests/test-applications/create-remix-app-express/.npmrc +++ /dev/null @@ -1,2 +0,0 @@ -@sentry:registry=http://127.0.0.1:4873 -@sentry-internal:registry=http://127.0.0.1:4873 diff --git a/dev-packages/e2e-tests/test-applications/create-remix-app-express/package.json b/dev-packages/e2e-tests/test-applications/create-remix-app-express/package.json index 20788a102bc1..2dc4f42dd34d 100644 --- a/dev-packages/e2e-tests/test-applications/create-remix-app-express/package.json +++ b/dev-packages/e2e-tests/test-applications/create-remix-app-express/package.json @@ -17,7 +17,7 @@ "@remix-run/express": "^2.17.4", "@remix-run/node": "^2.17.4", "@remix-run/react": "^2.17.4", - "@sentry/remix": "latest || *", + "@sentry/remix": "file:../../packed/sentry-remix-packed.tgz", "compression": "^1.7.4", "cross-env": "^7.0.3", "express": "^4.21.2", diff --git a/dev-packages/e2e-tests/test-applications/create-remix-app-v2-non-vite/.npmrc b/dev-packages/e2e-tests/test-applications/create-remix-app-v2-non-vite/.npmrc deleted file mode 100644 index 070f80f05092..000000000000 --- a/dev-packages/e2e-tests/test-applications/create-remix-app-v2-non-vite/.npmrc +++ /dev/null @@ -1,2 +0,0 @@ -@sentry:registry=http://127.0.0.1:4873 -@sentry-internal:registry=http://127.0.0.1:4873 diff --git a/dev-packages/e2e-tests/test-applications/create-remix-app-v2-non-vite/package.json b/dev-packages/e2e-tests/test-applications/create-remix-app-v2-non-vite/package.json index 185099a6adfc..38a7e231ddf1 100644 --- a/dev-packages/e2e-tests/test-applications/create-remix-app-v2-non-vite/package.json +++ b/dev-packages/e2e-tests/test-applications/create-remix-app-v2-non-vite/package.json @@ -11,7 +11,7 @@ "test:assert": "pnpm playwright test" }, "dependencies": { - "@sentry/remix": "latest || *", + "@sentry/remix": "file:../../packed/sentry-remix-packed.tgz", "@remix-run/css-bundle": "2.17.4", "@remix-run/node": "2.17.4", "@remix-run/react": "2.17.4", diff --git a/dev-packages/e2e-tests/test-applications/create-remix-app-v2/.npmrc b/dev-packages/e2e-tests/test-applications/create-remix-app-v2/.npmrc deleted file mode 100644 index 070f80f05092..000000000000 --- a/dev-packages/e2e-tests/test-applications/create-remix-app-v2/.npmrc +++ /dev/null @@ -1,2 +0,0 @@ -@sentry:registry=http://127.0.0.1:4873 -@sentry-internal:registry=http://127.0.0.1:4873 diff --git a/dev-packages/e2e-tests/test-applications/create-remix-app-v2/package.json b/dev-packages/e2e-tests/test-applications/create-remix-app-v2/package.json index d31e86ff0cdc..fd57d2920d5a 100644 --- a/dev-packages/e2e-tests/test-applications/create-remix-app-v2/package.json +++ b/dev-packages/e2e-tests/test-applications/create-remix-app-v2/package.json @@ -11,7 +11,7 @@ "test:assert": "pnpm playwright test" }, "dependencies": { - "@sentry/remix": "latest || *", + "@sentry/remix": "file:../../packed/sentry-remix-packed.tgz", "@remix-run/css-bundle": "2.17.4", "@remix-run/node": "2.17.4", "@remix-run/react": "2.17.4", diff --git a/dev-packages/e2e-tests/test-applications/debug-id-sourcemaps/.npmrc b/dev-packages/e2e-tests/test-applications/debug-id-sourcemaps/.npmrc deleted file mode 100644 index 070f80f05092..000000000000 --- a/dev-packages/e2e-tests/test-applications/debug-id-sourcemaps/.npmrc +++ /dev/null @@ -1,2 +0,0 @@ -@sentry:registry=http://127.0.0.1:4873 -@sentry-internal:registry=http://127.0.0.1:4873 diff --git a/dev-packages/e2e-tests/test-applications/debug-id-sourcemaps/package.json b/dev-packages/e2e-tests/test-applications/debug-id-sourcemaps/package.json index 4a0d90942367..d7599f0e332d 100644 --- a/dev-packages/e2e-tests/test-applications/debug-id-sourcemaps/package.json +++ b/dev-packages/e2e-tests/test-applications/debug-id-sourcemaps/package.json @@ -10,7 +10,7 @@ "test:assert": "pnpm test" }, "dependencies": { - "@sentry/node": "latest || *" + "@sentry/node": "file:../../packed/sentry-node-packed.tgz" }, "devDependencies": { "rollup": "^4.35.0", diff --git a/dev-packages/e2e-tests/test-applications/default-browser/.npmrc b/dev-packages/e2e-tests/test-applications/default-browser/.npmrc deleted file mode 100644 index 070f80f05092..000000000000 --- a/dev-packages/e2e-tests/test-applications/default-browser/.npmrc +++ /dev/null @@ -1,2 +0,0 @@ -@sentry:registry=http://127.0.0.1:4873 -@sentry-internal:registry=http://127.0.0.1:4873 diff --git a/dev-packages/e2e-tests/test-applications/default-browser/package.json b/dev-packages/e2e-tests/test-applications/default-browser/package.json index f181008e0427..6f3298f53285 100644 --- a/dev-packages/e2e-tests/test-applications/default-browser/package.json +++ b/dev-packages/e2e-tests/test-applications/default-browser/package.json @@ -3,7 +3,7 @@ "version": "0.1.0", "private": true, "dependencies": { - "@sentry/browser": "latest || *", + "@sentry/browser": "file:../../packed/sentry-browser-packed.tgz", "@types/node": "^18.19.1", "typescript": "~5.0.0" }, diff --git a/dev-packages/e2e-tests/test-applications/deno-streamed/.npmrc b/dev-packages/e2e-tests/test-applications/deno-streamed/.npmrc deleted file mode 100644 index 070f80f05092..000000000000 --- a/dev-packages/e2e-tests/test-applications/deno-streamed/.npmrc +++ /dev/null @@ -1,2 +0,0 @@ -@sentry:registry=http://127.0.0.1:4873 -@sentry-internal:registry=http://127.0.0.1:4873 diff --git a/dev-packages/e2e-tests/test-applications/deno-streamed/package.json b/dev-packages/e2e-tests/test-applications/deno-streamed/package.json index 70a20db2de05..7bbaeaf631f8 100644 --- a/dev-packages/e2e-tests/test-applications/deno-streamed/package.json +++ b/dev-packages/e2e-tests/test-applications/deno-streamed/package.json @@ -10,7 +10,7 @@ "test:assert": "pnpm test" }, "dependencies": { - "@sentry/deno": "latest || *", + "@sentry/deno": "file:../../packed/sentry-deno-packed.tgz", "@opentelemetry/api": "^1.9.0", "ai": "^3.0.0", "zod": "^3.22.4" diff --git a/dev-packages/e2e-tests/test-applications/deno/.npmrc b/dev-packages/e2e-tests/test-applications/deno/.npmrc deleted file mode 100644 index 070f80f05092..000000000000 --- a/dev-packages/e2e-tests/test-applications/deno/.npmrc +++ /dev/null @@ -1,2 +0,0 @@ -@sentry:registry=http://127.0.0.1:4873 -@sentry-internal:registry=http://127.0.0.1:4873 diff --git a/dev-packages/e2e-tests/test-applications/deno/package.json b/dev-packages/e2e-tests/test-applications/deno/package.json index ff30a9304e53..d46cc08530c5 100644 --- a/dev-packages/e2e-tests/test-applications/deno/package.json +++ b/dev-packages/e2e-tests/test-applications/deno/package.json @@ -10,7 +10,7 @@ "test:assert": "pnpm test" }, "dependencies": { - "@sentry/deno": "latest || *", + "@sentry/deno": "file:../../packed/sentry-deno-packed.tgz", "@opentelemetry/api": "^1.9.0", "ai": "^3.0.0", "zod": "^3.22.4" diff --git a/dev-packages/e2e-tests/test-applications/effect-3-browser/.npmrc b/dev-packages/e2e-tests/test-applications/effect-3-browser/.npmrc deleted file mode 100644 index 070f80f05092..000000000000 --- a/dev-packages/e2e-tests/test-applications/effect-3-browser/.npmrc +++ /dev/null @@ -1,2 +0,0 @@ -@sentry:registry=http://127.0.0.1:4873 -@sentry-internal:registry=http://127.0.0.1:4873 diff --git a/dev-packages/e2e-tests/test-applications/effect-3-browser/package.json b/dev-packages/e2e-tests/test-applications/effect-3-browser/package.json index c8cfbb5b587d..284c8f5ef9b2 100644 --- a/dev-packages/e2e-tests/test-applications/effect-3-browser/package.json +++ b/dev-packages/e2e-tests/test-applications/effect-3-browser/package.json @@ -11,7 +11,7 @@ "test:assert": "pnpm test" }, "dependencies": { - "@sentry/effect": "latest || *", + "@sentry/effect": "file:../../packed/sentry-effect-packed.tgz", "@types/node": "^18.19.1", "effect": "^3.19.19", "typescript": "~5.0.0" diff --git a/dev-packages/e2e-tests/test-applications/effect-4-node/.npmrc b/dev-packages/e2e-tests/test-applications/effect-4-node/.npmrc deleted file mode 100644 index 070f80f05092..000000000000 --- a/dev-packages/e2e-tests/test-applications/effect-4-node/.npmrc +++ /dev/null @@ -1,2 +0,0 @@ -@sentry:registry=http://127.0.0.1:4873 -@sentry-internal:registry=http://127.0.0.1:4873 diff --git a/dev-packages/e2e-tests/test-applications/effect-4-node/package.json b/dev-packages/e2e-tests/test-applications/effect-4-node/package.json index 31ebb8b1ba53..528b5cc77339 100644 --- a/dev-packages/e2e-tests/test-applications/effect-4-node/package.json +++ b/dev-packages/e2e-tests/test-applications/effect-4-node/package.json @@ -13,7 +13,7 @@ }, "dependencies": { "@effect/platform-node": "^4.0.0-beta.50", - "@sentry/effect": "latest || *", + "@sentry/effect": "file:../../packed/sentry-effect-packed.tgz", "@types/node": "^18.19.1", "effect": "^4.0.0-beta.50", "typescript": "~5.0.0" diff --git a/dev-packages/e2e-tests/test-applications/elysia-bun/.npmrc b/dev-packages/e2e-tests/test-applications/elysia-bun/.npmrc deleted file mode 100644 index 070f80f05092..000000000000 --- a/dev-packages/e2e-tests/test-applications/elysia-bun/.npmrc +++ /dev/null @@ -1,2 +0,0 @@ -@sentry:registry=http://127.0.0.1:4873 -@sentry-internal:registry=http://127.0.0.1:4873 diff --git a/dev-packages/e2e-tests/test-applications/elysia-bun/package.json b/dev-packages/e2e-tests/test-applications/elysia-bun/package.json index 73689db97994..98cf84595882 100644 --- a/dev-packages/e2e-tests/test-applications/elysia-bun/package.json +++ b/dev-packages/e2e-tests/test-applications/elysia-bun/package.json @@ -11,7 +11,7 @@ "test:assert": "pnpm test" }, "dependencies": { - "@sentry/elysia": "latest || *", + "@sentry/elysia": "file:../../packed/sentry-elysia-packed.tgz", "elysia": "^1.4.0" }, "devDependencies": { diff --git a/dev-packages/e2e-tests/test-applications/elysia-node/.npmrc b/dev-packages/e2e-tests/test-applications/elysia-node/.npmrc deleted file mode 100644 index 070f80f05092..000000000000 --- a/dev-packages/e2e-tests/test-applications/elysia-node/.npmrc +++ /dev/null @@ -1,2 +0,0 @@ -@sentry:registry=http://127.0.0.1:4873 -@sentry-internal:registry=http://127.0.0.1:4873 diff --git a/dev-packages/e2e-tests/test-applications/elysia-node/package.json b/dev-packages/e2e-tests/test-applications/elysia-node/package.json index dda646fab480..fcd2a4e8f71b 100644 --- a/dev-packages/e2e-tests/test-applications/elysia-node/package.json +++ b/dev-packages/e2e-tests/test-applications/elysia-node/package.json @@ -11,7 +11,7 @@ "test:assert": "pnpm test" }, "dependencies": { - "@sentry/elysia": "latest || *", + "@sentry/elysia": "file:../../packed/sentry-elysia-packed.tgz", "elysia": "latest", "@elysiajs/node": "^1.4.5" }, diff --git a/dev-packages/e2e-tests/test-applications/ember-classic/.npmrc b/dev-packages/e2e-tests/test-applications/ember-classic/.npmrc deleted file mode 100644 index 070f80f05092..000000000000 --- a/dev-packages/e2e-tests/test-applications/ember-classic/.npmrc +++ /dev/null @@ -1,2 +0,0 @@ -@sentry:registry=http://127.0.0.1:4873 -@sentry-internal:registry=http://127.0.0.1:4873 diff --git a/dev-packages/e2e-tests/test-applications/ember-classic/package.json b/dev-packages/e2e-tests/test-applications/ember-classic/package.json index 3cb6a5c9d836..9f96912ce555 100644 --- a/dev-packages/e2e-tests/test-applications/ember-classic/package.json +++ b/dev-packages/e2e-tests/test-applications/ember-classic/package.json @@ -27,7 +27,7 @@ "@playwright/test": "~1.56.0", "@ember/string": "~3.1.1", "@sentry-internal/test-utils": "link:../../../test-utils", - "@sentry/ember": "latest || *", + "@sentry/ember": "file:../../packed/sentry-ember-packed.tgz", "@tsconfig/ember": "~3.0.6", "@tsconfig/node18": "18.2.4", "@types/ember": "~4.0.11", diff --git a/dev-packages/e2e-tests/test-applications/ember-embroider/.npmrc b/dev-packages/e2e-tests/test-applications/ember-embroider/.npmrc deleted file mode 100644 index 070f80f05092..000000000000 --- a/dev-packages/e2e-tests/test-applications/ember-embroider/.npmrc +++ /dev/null @@ -1,2 +0,0 @@ -@sentry:registry=http://127.0.0.1:4873 -@sentry-internal:registry=http://127.0.0.1:4873 diff --git a/dev-packages/e2e-tests/test-applications/ember-embroider/package.json b/dev-packages/e2e-tests/test-applications/ember-embroider/package.json index e451a5da9db7..7f0b611b2bc1 100644 --- a/dev-packages/e2e-tests/test-applications/ember-embroider/package.json +++ b/dev-packages/e2e-tests/test-applications/ember-embroider/package.json @@ -51,7 +51,7 @@ "tracked-built-ins": "^3.3.0", "webpack": "^5.91.0", "@playwright/test": "~1.56.0", - "@sentry/ember": "latest || *", + "@sentry/ember": "file:../../packed/sentry-ember-packed.tgz", "@sentry-internal/test-utils": "link:../../../test-utils", "@tsconfig/ember": "^3.0.6", "@types/node": "^18.19.1", diff --git a/dev-packages/e2e-tests/test-applications/generic-ts3.8/.npmrc b/dev-packages/e2e-tests/test-applications/generic-ts3.8/.npmrc deleted file mode 100644 index 070f80f05092..000000000000 --- a/dev-packages/e2e-tests/test-applications/generic-ts3.8/.npmrc +++ /dev/null @@ -1,2 +0,0 @@ -@sentry:registry=http://127.0.0.1:4873 -@sentry-internal:registry=http://127.0.0.1:4873 diff --git a/dev-packages/e2e-tests/test-applications/generic-ts3.8/package.json b/dev-packages/e2e-tests/test-applications/generic-ts3.8/package.json index fe0e0f6ec5f0..f27c55af0a0a 100644 --- a/dev-packages/e2e-tests/test-applications/generic-ts3.8/package.json +++ b/dev-packages/e2e-tests/test-applications/generic-ts3.8/package.json @@ -14,11 +14,11 @@ "@types/node": "^14.0.0" }, "dependencies": { - "@sentry/browser": "latest || *", - "@sentry/core": "latest || *", - "@sentry/node": "latest || *", - "@sentry-internal/replay": "latest || *", - "@sentry/wasm": "latest || *" + "@sentry/browser": "file:../../packed/sentry-browser-packed.tgz", + "@sentry/core": "file:../../packed/sentry-core-packed.tgz", + "@sentry/node": "file:../../packed/sentry-node-packed.tgz", + "@sentry-internal/replay": "file:../../packed/sentry-internal-replay-packed.tgz", + "@sentry/wasm": "file:../../packed/sentry-wasm-packed.tgz" }, "pnpm": { "overrides": { diff --git a/dev-packages/e2e-tests/test-applications/generic-ts5.0/.npmrc b/dev-packages/e2e-tests/test-applications/generic-ts5.0/.npmrc deleted file mode 100644 index 070f80f05092..000000000000 --- a/dev-packages/e2e-tests/test-applications/generic-ts5.0/.npmrc +++ /dev/null @@ -1,2 +0,0 @@ -@sentry:registry=http://127.0.0.1:4873 -@sentry-internal:registry=http://127.0.0.1:4873 diff --git a/dev-packages/e2e-tests/test-applications/generic-ts5.0/package.json b/dev-packages/e2e-tests/test-applications/generic-ts5.0/package.json index 1079d8f4c793..e60d1b19489b 100644 --- a/dev-packages/e2e-tests/test-applications/generic-ts5.0/package.json +++ b/dev-packages/e2e-tests/test-applications/generic-ts5.0/package.json @@ -14,11 +14,11 @@ "@types/node": "^18.19.1" }, "dependencies": { - "@sentry/browser": "latest || *", - "@sentry/core": "latest || *", - "@sentry/node": "latest || *", - "@sentry-internal/replay": "latest || *", - "@sentry/wasm": "latest || *" + "@sentry/browser": "file:../../packed/sentry-browser-packed.tgz", + "@sentry/core": "file:../../packed/sentry-core-packed.tgz", + "@sentry/node": "file:../../packed/sentry-node-packed.tgz", + "@sentry-internal/replay": "file:../../packed/sentry-internal-replay-packed.tgz", + "@sentry/wasm": "file:../../packed/sentry-wasm-packed.tgz" }, "volta": { "extends": "../../package.json" diff --git a/dev-packages/e2e-tests/test-applications/hydrogen-react-router-7/.npmrc b/dev-packages/e2e-tests/test-applications/hydrogen-react-router-7/.npmrc deleted file mode 100644 index 070f80f05092..000000000000 --- a/dev-packages/e2e-tests/test-applications/hydrogen-react-router-7/.npmrc +++ /dev/null @@ -1,2 +0,0 @@ -@sentry:registry=http://127.0.0.1:4873 -@sentry-internal:registry=http://127.0.0.1:4873 diff --git a/dev-packages/e2e-tests/test-applications/hydrogen-react-router-7/package.json b/dev-packages/e2e-tests/test-applications/hydrogen-react-router-7/package.json index 87c67b61f195..56789ec7cedb 100644 --- a/dev-packages/e2e-tests/test-applications/hydrogen-react-router-7/package.json +++ b/dev-packages/e2e-tests/test-applications/hydrogen-react-router-7/package.json @@ -14,8 +14,8 @@ "test:assert": "pnpm playwright test" }, "dependencies": { - "@sentry/cloudflare": "latest || *", - "@sentry/react-router": "latest || *", + "@sentry/cloudflare": "file:../../packed/sentry-cloudflare-packed.tgz", + "@sentry/react-router": "file:../../packed/sentry-react-router-packed.tgz", "@sentry/vite-plugin": "^5.2.0", "@shopify/hydrogen": "2025.5.0", "@shopify/remix-oxygen": "^3.0.0", diff --git a/dev-packages/e2e-tests/test-applications/nestjs-11/.npmrc b/dev-packages/e2e-tests/test-applications/nestjs-11/.npmrc deleted file mode 100644 index 070f80f05092..000000000000 --- a/dev-packages/e2e-tests/test-applications/nestjs-11/.npmrc +++ /dev/null @@ -1,2 +0,0 @@ -@sentry:registry=http://127.0.0.1:4873 -@sentry-internal:registry=http://127.0.0.1:4873 diff --git a/dev-packages/e2e-tests/test-applications/nestjs-11/package.json b/dev-packages/e2e-tests/test-applications/nestjs-11/package.json index 48e2525de321..2a230d9d5a68 100644 --- a/dev-packages/e2e-tests/test-applications/nestjs-11/package.json +++ b/dev-packages/e2e-tests/test-applications/nestjs-11/package.json @@ -20,7 +20,7 @@ "@nestjs/microservices": "^11.0.0", "@nestjs/schedule": "^5.0.0", "@nestjs/platform-express": "^11.0.0", - "@sentry/nestjs": "latest || *", + "@sentry/nestjs": "file:../../packed/sentry-nestjs-packed.tgz", "reflect-metadata": "^0.2.0", "rxjs": "^7.8.1" }, diff --git a/dev-packages/e2e-tests/test-applications/nestjs-8/.npmrc b/dev-packages/e2e-tests/test-applications/nestjs-8/.npmrc deleted file mode 100644 index 070f80f05092..000000000000 --- a/dev-packages/e2e-tests/test-applications/nestjs-8/.npmrc +++ /dev/null @@ -1,2 +0,0 @@ -@sentry:registry=http://127.0.0.1:4873 -@sentry-internal:registry=http://127.0.0.1:4873 diff --git a/dev-packages/e2e-tests/test-applications/nestjs-8/package.json b/dev-packages/e2e-tests/test-applications/nestjs-8/package.json index 4a21f67e908a..3bd765774d2e 100644 --- a/dev-packages/e2e-tests/test-applications/nestjs-8/package.json +++ b/dev-packages/e2e-tests/test-applications/nestjs-8/package.json @@ -19,7 +19,7 @@ "@nestjs/microservices": "^8.0.0", "@nestjs/schedule": "^4.1.0", "@nestjs/platform-express": "^8.0.0", - "@sentry/nestjs": "latest || *", + "@sentry/nestjs": "file:../../packed/sentry-nestjs-packed.tgz", "reflect-metadata": "^0.2.0", "rxjs": "^7.8.1" }, diff --git a/dev-packages/e2e-tests/test-applications/nestjs-basic-with-graphql/.npmrc b/dev-packages/e2e-tests/test-applications/nestjs-basic-with-graphql/.npmrc deleted file mode 100644 index 070f80f05092..000000000000 --- a/dev-packages/e2e-tests/test-applications/nestjs-basic-with-graphql/.npmrc +++ /dev/null @@ -1,2 +0,0 @@ -@sentry:registry=http://127.0.0.1:4873 -@sentry-internal:registry=http://127.0.0.1:4873 diff --git a/dev-packages/e2e-tests/test-applications/nestjs-basic-with-graphql/package.json b/dev-packages/e2e-tests/test-applications/nestjs-basic-with-graphql/package.json index 3128d2f7ae51..e429f8cbb328 100644 --- a/dev-packages/e2e-tests/test-applications/nestjs-basic-with-graphql/package.json +++ b/dev-packages/e2e-tests/test-applications/nestjs-basic-with-graphql/package.json @@ -20,7 +20,7 @@ "@nestjs/core": "^10.3.10", "@nestjs/graphql": "^12.2.0", "@nestjs/platform-express": "^10.3.10", - "@sentry/nestjs": "latest || *", + "@sentry/nestjs": "file:../../packed/sentry-nestjs-packed.tgz", "graphql": "^16.9.0", "reflect-metadata": "^0.1.13", "rxjs": "^7.8.1" diff --git a/dev-packages/e2e-tests/test-applications/nestjs-basic/.npmrc b/dev-packages/e2e-tests/test-applications/nestjs-basic/.npmrc deleted file mode 100644 index 070f80f05092..000000000000 --- a/dev-packages/e2e-tests/test-applications/nestjs-basic/.npmrc +++ /dev/null @@ -1,2 +0,0 @@ -@sentry:registry=http://127.0.0.1:4873 -@sentry-internal:registry=http://127.0.0.1:4873 diff --git a/dev-packages/e2e-tests/test-applications/nestjs-basic/package.json b/dev-packages/e2e-tests/test-applications/nestjs-basic/package.json index 6917e546a383..ebf0244bc276 100644 --- a/dev-packages/e2e-tests/test-applications/nestjs-basic/package.json +++ b/dev-packages/e2e-tests/test-applications/nestjs-basic/package.json @@ -19,7 +19,7 @@ "@nestjs/microservices": "^10.0.0", "@nestjs/schedule": "^4.1.0", "@nestjs/platform-express": "^10.0.0", - "@sentry/nestjs": "latest || *", + "@sentry/nestjs": "file:../../packed/sentry-nestjs-packed.tgz", "reflect-metadata": "^0.2.0", "axios": "1.15.0", "rxjs": "^7.8.1" diff --git a/dev-packages/e2e-tests/test-applications/nestjs-bullmq/.npmrc b/dev-packages/e2e-tests/test-applications/nestjs-bullmq/.npmrc deleted file mode 100644 index 070f80f05092..000000000000 --- a/dev-packages/e2e-tests/test-applications/nestjs-bullmq/.npmrc +++ /dev/null @@ -1,2 +0,0 @@ -@sentry:registry=http://127.0.0.1:4873 -@sentry-internal:registry=http://127.0.0.1:4873 diff --git a/dev-packages/e2e-tests/test-applications/nestjs-bullmq/package.json b/dev-packages/e2e-tests/test-applications/nestjs-bullmq/package.json index 77d8c024e021..c4cfcd118f53 100644 --- a/dev-packages/e2e-tests/test-applications/nestjs-bullmq/package.json +++ b/dev-packages/e2e-tests/test-applications/nestjs-bullmq/package.json @@ -18,7 +18,7 @@ "@nestjs/platform-express": "^11.0.0", "@nestjs/bullmq": "^11.0.0", "bullmq": "^5.0.0", - "@sentry/nestjs": "latest || *", + "@sentry/nestjs": "file:../../packed/sentry-nestjs-packed.tgz", "reflect-metadata": "^0.2.0", "rxjs": "^7.8.1" }, diff --git a/dev-packages/e2e-tests/test-applications/nestjs-distributed-tracing/.npmrc b/dev-packages/e2e-tests/test-applications/nestjs-distributed-tracing/.npmrc deleted file mode 100644 index 070f80f05092..000000000000 --- a/dev-packages/e2e-tests/test-applications/nestjs-distributed-tracing/.npmrc +++ /dev/null @@ -1,2 +0,0 @@ -@sentry:registry=http://127.0.0.1:4873 -@sentry-internal:registry=http://127.0.0.1:4873 diff --git a/dev-packages/e2e-tests/test-applications/nestjs-distributed-tracing/package.json b/dev-packages/e2e-tests/test-applications/nestjs-distributed-tracing/package.json index d15679556bad..c8fe82cff563 100644 --- a/dev-packages/e2e-tests/test-applications/nestjs-distributed-tracing/package.json +++ b/dev-packages/e2e-tests/test-applications/nestjs-distributed-tracing/package.json @@ -18,7 +18,7 @@ "@nestjs/core": "^10.0.0", "@nestjs/platform-express": "^10.0.0", "@nestjs/event-emitter": "^2.0.0", - "@sentry/nestjs": "latest || *", + "@sentry/nestjs": "file:../../packed/sentry-nestjs-packed.tgz", "reflect-metadata": "^0.2.0", "rxjs": "^7.8.1" }, diff --git a/dev-packages/e2e-tests/test-applications/nestjs-fastify/.npmrc b/dev-packages/e2e-tests/test-applications/nestjs-fastify/.npmrc deleted file mode 100644 index 070f80f05092..000000000000 --- a/dev-packages/e2e-tests/test-applications/nestjs-fastify/.npmrc +++ /dev/null @@ -1,2 +0,0 @@ -@sentry:registry=http://127.0.0.1:4873 -@sentry-internal:registry=http://127.0.0.1:4873 diff --git a/dev-packages/e2e-tests/test-applications/nestjs-fastify/package.json b/dev-packages/e2e-tests/test-applications/nestjs-fastify/package.json index d5cecac78725..720cfe158eae 100644 --- a/dev-packages/e2e-tests/test-applications/nestjs-fastify/package.json +++ b/dev-packages/e2e-tests/test-applications/nestjs-fastify/package.json @@ -19,7 +19,7 @@ "@nestjs/microservices": "^10.0.0", "@nestjs/schedule": "^4.1.0", "@nestjs/platform-fastify": "^10.0.0", - "@sentry/nestjs": "latest || *", + "@sentry/nestjs": "file:../../packed/sentry-nestjs-packed.tgz", "reflect-metadata": "^0.2.0", "rxjs": "^7.8.1", "fastify": "^4.28.1" diff --git a/dev-packages/e2e-tests/test-applications/nestjs-graphql/.npmrc b/dev-packages/e2e-tests/test-applications/nestjs-graphql/.npmrc deleted file mode 100644 index 070f80f05092..000000000000 --- a/dev-packages/e2e-tests/test-applications/nestjs-graphql/.npmrc +++ /dev/null @@ -1,2 +0,0 @@ -@sentry:registry=http://127.0.0.1:4873 -@sentry-internal:registry=http://127.0.0.1:4873 diff --git a/dev-packages/e2e-tests/test-applications/nestjs-graphql/package.json b/dev-packages/e2e-tests/test-applications/nestjs-graphql/package.json index be8ed9d58533..05a38d691807 100644 --- a/dev-packages/e2e-tests/test-applications/nestjs-graphql/package.json +++ b/dev-packages/e2e-tests/test-applications/nestjs-graphql/package.json @@ -20,7 +20,7 @@ "@nestjs/core": "^10.3.10", "@nestjs/graphql": "^12.2.0", "@nestjs/platform-express": "^10.3.10", - "@sentry/nestjs": "latest || *", + "@sentry/nestjs": "file:../../packed/sentry-nestjs-packed.tgz", "graphql": "^16.9.0", "reflect-metadata": "^0.1.13", "rxjs": "^7.8.1" diff --git a/dev-packages/e2e-tests/test-applications/nestjs-microservices/.npmrc b/dev-packages/e2e-tests/test-applications/nestjs-microservices/.npmrc deleted file mode 100644 index 070f80f05092..000000000000 --- a/dev-packages/e2e-tests/test-applications/nestjs-microservices/.npmrc +++ /dev/null @@ -1,2 +0,0 @@ -@sentry:registry=http://127.0.0.1:4873 -@sentry-internal:registry=http://127.0.0.1:4873 diff --git a/dev-packages/e2e-tests/test-applications/nestjs-microservices/package.json b/dev-packages/e2e-tests/test-applications/nestjs-microservices/package.json index ee3ca5ebf816..4bfc4eee7710 100644 --- a/dev-packages/e2e-tests/test-applications/nestjs-microservices/package.json +++ b/dev-packages/e2e-tests/test-applications/nestjs-microservices/package.json @@ -16,7 +16,7 @@ "@nestjs/core": "^11.0.0", "@nestjs/microservices": "^11.0.0", "@nestjs/platform-express": "^11.0.0", - "@sentry/nestjs": "latest || *", + "@sentry/nestjs": "file:../../packed/sentry-nestjs-packed.tgz", "reflect-metadata": "^0.2.0", "rxjs": "^7.8.1" }, diff --git a/dev-packages/e2e-tests/test-applications/nestjs-websockets/package.json b/dev-packages/e2e-tests/test-applications/nestjs-websockets/package.json index 6356b48b322f..c859d4e49791 100644 --- a/dev-packages/e2e-tests/test-applications/nestjs-websockets/package.json +++ b/dev-packages/e2e-tests/test-applications/nestjs-websockets/package.json @@ -16,7 +16,7 @@ "@nestjs/platform-express": "^11.0.0", "@nestjs/websockets": "^11.0.0", "@nestjs/platform-socket.io": "^11.0.0", - "@sentry/nestjs": "latest || *", + "@sentry/nestjs": "file:../../packed/sentry-nestjs-packed.tgz", "reflect-metadata": "^0.2.0", "rxjs": "^7.8.1" }, diff --git a/dev-packages/e2e-tests/test-applications/nestjs-with-submodules-decorator/.npmrc b/dev-packages/e2e-tests/test-applications/nestjs-with-submodules-decorator/.npmrc deleted file mode 100644 index 070f80f05092..000000000000 --- a/dev-packages/e2e-tests/test-applications/nestjs-with-submodules-decorator/.npmrc +++ /dev/null @@ -1,2 +0,0 @@ -@sentry:registry=http://127.0.0.1:4873 -@sentry-internal:registry=http://127.0.0.1:4873 diff --git a/dev-packages/e2e-tests/test-applications/nestjs-with-submodules-decorator/package.json b/dev-packages/e2e-tests/test-applications/nestjs-with-submodules-decorator/package.json index f19782b24a7d..35ce0bc009e1 100644 --- a/dev-packages/e2e-tests/test-applications/nestjs-with-submodules-decorator/package.json +++ b/dev-packages/e2e-tests/test-applications/nestjs-with-submodules-decorator/package.json @@ -17,7 +17,7 @@ "@nestjs/common": "^10.0.0", "@nestjs/core": "^10.0.0", "@nestjs/platform-express": "^10.0.0", - "@sentry/nestjs": "latest || *", + "@sentry/nestjs": "file:../../packed/sentry-nestjs-packed.tgz", "reflect-metadata": "^0.2.0", "rxjs": "^7.8.1" }, diff --git a/dev-packages/e2e-tests/test-applications/nestjs-with-submodules/.npmrc b/dev-packages/e2e-tests/test-applications/nestjs-with-submodules/.npmrc deleted file mode 100644 index 070f80f05092..000000000000 --- a/dev-packages/e2e-tests/test-applications/nestjs-with-submodules/.npmrc +++ /dev/null @@ -1,2 +0,0 @@ -@sentry:registry=http://127.0.0.1:4873 -@sentry-internal:registry=http://127.0.0.1:4873 diff --git a/dev-packages/e2e-tests/test-applications/nestjs-with-submodules/package.json b/dev-packages/e2e-tests/test-applications/nestjs-with-submodules/package.json index 297555d6802f..e9da4c97ae26 100644 --- a/dev-packages/e2e-tests/test-applications/nestjs-with-submodules/package.json +++ b/dev-packages/e2e-tests/test-applications/nestjs-with-submodules/package.json @@ -17,7 +17,7 @@ "@nestjs/common": "^10.0.0", "@nestjs/core": "^10.0.0", "@nestjs/platform-express": "^10.0.0", - "@sentry/nestjs": "latest || *", + "@sentry/nestjs": "file:../../packed/sentry-nestjs-packed.tgz", "reflect-metadata": "^0.2.0", "rxjs": "^7.8.1" }, diff --git a/dev-packages/e2e-tests/test-applications/nextjs-13/.npmrc b/dev-packages/e2e-tests/test-applications/nextjs-13/.npmrc deleted file mode 100644 index 070f80f05092..000000000000 --- a/dev-packages/e2e-tests/test-applications/nextjs-13/.npmrc +++ /dev/null @@ -1,2 +0,0 @@ -@sentry:registry=http://127.0.0.1:4873 -@sentry-internal:registry=http://127.0.0.1:4873 diff --git a/dev-packages/e2e-tests/test-applications/nextjs-13/package.json b/dev-packages/e2e-tests/test-applications/nextjs-13/package.json index 29270d71da6a..f6137db6843c 100644 --- a/dev-packages/e2e-tests/test-applications/nextjs-13/package.json +++ b/dev-packages/e2e-tests/test-applications/nextjs-13/package.json @@ -12,7 +12,7 @@ "test:assert": "pnpm test:prod && pnpm test:dev" }, "dependencies": { - "@sentry/nextjs": "latest || *", + "@sentry/nextjs": "file:../../packed/sentry-nextjs-packed.tgz", "@types/node": "^18.19.1", "@types/react": "18.0.26", "@types/react-dom": "18.0.9", diff --git a/dev-packages/e2e-tests/test-applications/nextjs-14/.npmrc b/dev-packages/e2e-tests/test-applications/nextjs-14/.npmrc deleted file mode 100644 index 070f80f05092..000000000000 --- a/dev-packages/e2e-tests/test-applications/nextjs-14/.npmrc +++ /dev/null @@ -1,2 +0,0 @@ -@sentry:registry=http://127.0.0.1:4873 -@sentry-internal:registry=http://127.0.0.1:4873 diff --git a/dev-packages/e2e-tests/test-applications/nextjs-14/package.json b/dev-packages/e2e-tests/test-applications/nextjs-14/package.json index 2388110ad775..09a928eddfa0 100644 --- a/dev-packages/e2e-tests/test-applications/nextjs-14/package.json +++ b/dev-packages/e2e-tests/test-applications/nextjs-14/package.json @@ -13,7 +13,7 @@ "test:assert": "pnpm test:prod && pnpm test:dev" }, "dependencies": { - "@sentry/nextjs": "latest || *", + "@sentry/nextjs": "file:../../packed/sentry-nextjs-packed.tgz", "@types/node": "^18.19.1", "@types/react": "18.0.26", "@types/react-dom": "18.0.9", @@ -25,7 +25,7 @@ "devDependencies": { "@playwright/test": "~1.56.0", "@sentry-internal/test-utils": "link:../../../test-utils", - "@sentry/core": "latest || *" + "@sentry/core": "file:../../packed/sentry-core-packed.tgz" }, "volta": { "extends": "../../package.json" diff --git a/dev-packages/e2e-tests/test-applications/nextjs-15-basepath/.npmrc b/dev-packages/e2e-tests/test-applications/nextjs-15-basepath/.npmrc deleted file mode 100644 index a3160f4de175..000000000000 --- a/dev-packages/e2e-tests/test-applications/nextjs-15-basepath/.npmrc +++ /dev/null @@ -1,4 +0,0 @@ -@sentry:registry=http://127.0.0.1:4873 -@sentry-internal:registry=http://127.0.0.1:4873 -public-hoist-pattern[]=*import-in-the-middle* -public-hoist-pattern[]=*require-in-the-middle* diff --git a/dev-packages/e2e-tests/test-applications/nextjs-15-basepath/package.json b/dev-packages/e2e-tests/test-applications/nextjs-15-basepath/package.json index 01d3747009c7..ce63c62aeefa 100644 --- a/dev-packages/e2e-tests/test-applications/nextjs-15-basepath/package.json +++ b/dev-packages/e2e-tests/test-applications/nextjs-15-basepath/package.json @@ -11,7 +11,7 @@ "test:assert": "pnpm test:prod && pnpm test:dev" }, "dependencies": { - "@sentry/nextjs": "latest || *", + "@sentry/nextjs": "file:../../packed/sentry-nextjs-packed.tgz", "@types/node": "^18.19.1", "@types/react": "18.0.26", "@types/react-dom": "18.0.9", diff --git a/dev-packages/e2e-tests/test-applications/nextjs-15-intl/.npmrc b/dev-packages/e2e-tests/test-applications/nextjs-15-intl/.npmrc deleted file mode 100644 index c6b3ef9b3eaa..000000000000 --- a/dev-packages/e2e-tests/test-applications/nextjs-15-intl/.npmrc +++ /dev/null @@ -1,2 +0,0 @@ -@sentry:registry=http://localhost:4873 -@sentry-internal:registry=http://localhost:4873 diff --git a/dev-packages/e2e-tests/test-applications/nextjs-15-intl/package.json b/dev-packages/e2e-tests/test-applications/nextjs-15-intl/package.json index ca609897ff4c..b0c7f2852e01 100644 --- a/dev-packages/e2e-tests/test-applications/nextjs-15-intl/package.json +++ b/dev-packages/e2e-tests/test-applications/nextjs-15-intl/package.json @@ -11,7 +11,7 @@ "test:assert": "pnpm test:prod && pnpm test:dev" }, "dependencies": { - "@sentry/nextjs": "latest || *", + "@sentry/nextjs": "file:../../packed/sentry-nextjs-packed.tgz", "@types/node": "^18.19.1", "@types/react": "18.0.26", "@types/react-dom": "18.0.9", diff --git a/dev-packages/e2e-tests/test-applications/nextjs-15-t3/.npmrc b/dev-packages/e2e-tests/test-applications/nextjs-15-t3/.npmrc deleted file mode 100644 index 070f80f05092..000000000000 --- a/dev-packages/e2e-tests/test-applications/nextjs-15-t3/.npmrc +++ /dev/null @@ -1,2 +0,0 @@ -@sentry:registry=http://127.0.0.1:4873 -@sentry-internal:registry=http://127.0.0.1:4873 diff --git a/dev-packages/e2e-tests/test-applications/nextjs-15-t3/package.json b/dev-packages/e2e-tests/test-applications/nextjs-15-t3/package.json index 380e2ce0f66f..24fde175baea 100644 --- a/dev-packages/e2e-tests/test-applications/nextjs-15-t3/package.json +++ b/dev-packages/e2e-tests/test-applications/nextjs-15-t3/package.json @@ -14,7 +14,7 @@ "test:assert": "pnpm test:prod && pnpm test:dev" }, "dependencies": { - "@sentry/nextjs": "latest || *", + "@sentry/nextjs": "file:../../packed/sentry-nextjs-packed.tgz", "@t3-oss/env-nextjs": "^0.12.0", "@tanstack/react-query": "^5.50.0", "@trpc/client": "~11.8.0", diff --git a/dev-packages/e2e-tests/test-applications/nextjs-15/.npmrc b/dev-packages/e2e-tests/test-applications/nextjs-15/.npmrc deleted file mode 100644 index a3160f4de175..000000000000 --- a/dev-packages/e2e-tests/test-applications/nextjs-15/.npmrc +++ /dev/null @@ -1,4 +0,0 @@ -@sentry:registry=http://127.0.0.1:4873 -@sentry-internal:registry=http://127.0.0.1:4873 -public-hoist-pattern[]=*import-in-the-middle* -public-hoist-pattern[]=*require-in-the-middle* diff --git a/dev-packages/e2e-tests/test-applications/nextjs-15/package.json b/dev-packages/e2e-tests/test-applications/nextjs-15/package.json index 9263605b5672..9e453cf0edf5 100644 --- a/dev-packages/e2e-tests/test-applications/nextjs-15/package.json +++ b/dev-packages/e2e-tests/test-applications/nextjs-15/package.json @@ -15,7 +15,7 @@ "test:assert": "pnpm test:prod && pnpm test:dev" }, "dependencies": { - "@sentry/nextjs": "latest || *", + "@sentry/nextjs": "file:../../packed/sentry-nextjs-packed.tgz", "@types/node": "^18.19.1", "@types/react": "18.0.26", "@types/react-dom": "18.0.9", diff --git a/dev-packages/e2e-tests/test-applications/nextjs-16-bun/.npmrc b/dev-packages/e2e-tests/test-applications/nextjs-16-bun/.npmrc deleted file mode 100644 index a3160f4de175..000000000000 --- a/dev-packages/e2e-tests/test-applications/nextjs-16-bun/.npmrc +++ /dev/null @@ -1,4 +0,0 @@ -@sentry:registry=http://127.0.0.1:4873 -@sentry-internal:registry=http://127.0.0.1:4873 -public-hoist-pattern[]=*import-in-the-middle* -public-hoist-pattern[]=*require-in-the-middle* diff --git a/dev-packages/e2e-tests/test-applications/nextjs-16-bun/package.json b/dev-packages/e2e-tests/test-applications/nextjs-16-bun/package.json index 75e51867a38c..deb955b58daf 100644 --- a/dev-packages/e2e-tests/test-applications/nextjs-16-bun/package.json +++ b/dev-packages/e2e-tests/test-applications/nextjs-16-bun/package.json @@ -12,8 +12,8 @@ "test:assert": "pnpm test:prod" }, "dependencies": { - "@sentry/nextjs": "latest || *", - "@sentry/core": "latest || *", + "@sentry/nextjs": "file:../../packed/sentry-nextjs-packed.tgz", + "@sentry/core": "file:../../packed/sentry-core-packed.tgz", "import-in-the-middle": "^2", "next": "16.1.7", "react": "19.1.0", diff --git a/dev-packages/e2e-tests/test-applications/nextjs-16-cacheComponents/.npmrc b/dev-packages/e2e-tests/test-applications/nextjs-16-cacheComponents/.npmrc deleted file mode 100644 index a3160f4de175..000000000000 --- a/dev-packages/e2e-tests/test-applications/nextjs-16-cacheComponents/.npmrc +++ /dev/null @@ -1,4 +0,0 @@ -@sentry:registry=http://127.0.0.1:4873 -@sentry-internal:registry=http://127.0.0.1:4873 -public-hoist-pattern[]=*import-in-the-middle* -public-hoist-pattern[]=*require-in-the-middle* diff --git a/dev-packages/e2e-tests/test-applications/nextjs-16-cacheComponents/package.json b/dev-packages/e2e-tests/test-applications/nextjs-16-cacheComponents/package.json index bc306ef7dab7..3f3907a77bed 100644 --- a/dev-packages/e2e-tests/test-applications/nextjs-16-cacheComponents/package.json +++ b/dev-packages/e2e-tests/test-applications/nextjs-16-cacheComponents/package.json @@ -23,8 +23,8 @@ "test:assert-webpack": "pnpm test:prod && pnpm test:dev-webpack" }, "dependencies": { - "@sentry/nextjs": "latest || *", - "@sentry/core": "latest || *", + "@sentry/nextjs": "file:../../packed/sentry-nextjs-packed.tgz", + "@sentry/core": "file:../../packed/sentry-core-packed.tgz", "import-in-the-middle": "^1", "next": "16.1.7", "react": "19.1.0", diff --git a/dev-packages/e2e-tests/test-applications/nextjs-16-cf-workers/.npmrc b/dev-packages/e2e-tests/test-applications/nextjs-16-cf-workers/.npmrc deleted file mode 100644 index 070f80f05092..000000000000 --- a/dev-packages/e2e-tests/test-applications/nextjs-16-cf-workers/.npmrc +++ /dev/null @@ -1,2 +0,0 @@ -@sentry:registry=http://127.0.0.1:4873 -@sentry-internal:registry=http://127.0.0.1:4873 diff --git a/dev-packages/e2e-tests/test-applications/nextjs-16-cf-workers/package.json b/dev-packages/e2e-tests/test-applications/nextjs-16-cf-workers/package.json index d55b0059d71d..14334483d116 100644 --- a/dev-packages/e2e-tests/test-applications/nextjs-16-cf-workers/package.json +++ b/dev-packages/e2e-tests/test-applications/nextjs-16-cf-workers/package.json @@ -18,8 +18,8 @@ }, "dependencies": { "@opennextjs/cloudflare": "^1.14.9", - "@sentry/nextjs": "latest || *", - "@sentry/core": "latest || *", + "@sentry/nextjs": "file:../../packed/sentry-nextjs-packed.tgz", + "@sentry/core": "file:../../packed/sentry-core-packed.tgz", "next": "16.2.3", "react": "19.1.0", "react-dom": "19.1.0" diff --git a/dev-packages/e2e-tests/test-applications/nextjs-16-trailing-slash/.npmrc b/dev-packages/e2e-tests/test-applications/nextjs-16-trailing-slash/.npmrc deleted file mode 100644 index a3160f4de175..000000000000 --- a/dev-packages/e2e-tests/test-applications/nextjs-16-trailing-slash/.npmrc +++ /dev/null @@ -1,4 +0,0 @@ -@sentry:registry=http://127.0.0.1:4873 -@sentry-internal:registry=http://127.0.0.1:4873 -public-hoist-pattern[]=*import-in-the-middle* -public-hoist-pattern[]=*require-in-the-middle* diff --git a/dev-packages/e2e-tests/test-applications/nextjs-16-trailing-slash/package.json b/dev-packages/e2e-tests/test-applications/nextjs-16-trailing-slash/package.json index ea0475e5ed61..03035b9ddb33 100644 --- a/dev-packages/e2e-tests/test-applications/nextjs-16-trailing-slash/package.json +++ b/dev-packages/e2e-tests/test-applications/nextjs-16-trailing-slash/package.json @@ -13,8 +13,8 @@ "test:assert": "pnpm test:prod" }, "dependencies": { - "@sentry/nextjs": "latest || *", - "@sentry/core": "latest || *", + "@sentry/nextjs": "file:../../packed/sentry-nextjs-packed.tgz", + "@sentry/core": "file:../../packed/sentry-core-packed.tgz", "import-in-the-middle": "^2", "next": "16.1.7", "react": "19.1.0", diff --git a/dev-packages/e2e-tests/test-applications/nextjs-16-tunnel/.npmrc b/dev-packages/e2e-tests/test-applications/nextjs-16-tunnel/.npmrc deleted file mode 100644 index a3160f4de175..000000000000 --- a/dev-packages/e2e-tests/test-applications/nextjs-16-tunnel/.npmrc +++ /dev/null @@ -1,4 +0,0 @@ -@sentry:registry=http://127.0.0.1:4873 -@sentry-internal:registry=http://127.0.0.1:4873 -public-hoist-pattern[]=*import-in-the-middle* -public-hoist-pattern[]=*require-in-the-middle* diff --git a/dev-packages/e2e-tests/test-applications/nextjs-16-tunnel/package.json b/dev-packages/e2e-tests/test-applications/nextjs-16-tunnel/package.json index 5a1fed010500..ee74ff6e9259 100644 --- a/dev-packages/e2e-tests/test-applications/nextjs-16-tunnel/package.json +++ b/dev-packages/e2e-tests/test-applications/nextjs-16-tunnel/package.json @@ -23,8 +23,8 @@ "test:assert-webpack": "pnpm test:prod && pnpm test:dev-webpack" }, "dependencies": { - "@sentry/nextjs": "latest || *", - "@sentry/core": "latest || *", + "@sentry/nextjs": "file:../../packed/sentry-nextjs-packed.tgz", + "@sentry/core": "file:../../packed/sentry-core-packed.tgz", "ai": "^3.0.0", "import-in-the-middle": "^1", "next": "16.1.7", diff --git a/dev-packages/e2e-tests/test-applications/nextjs-16-userfeedback/.npmrc b/dev-packages/e2e-tests/test-applications/nextjs-16-userfeedback/.npmrc deleted file mode 100644 index a3160f4de175..000000000000 --- a/dev-packages/e2e-tests/test-applications/nextjs-16-userfeedback/.npmrc +++ /dev/null @@ -1,4 +0,0 @@ -@sentry:registry=http://127.0.0.1:4873 -@sentry-internal:registry=http://127.0.0.1:4873 -public-hoist-pattern[]=*import-in-the-middle* -public-hoist-pattern[]=*require-in-the-middle* diff --git a/dev-packages/e2e-tests/test-applications/nextjs-16-userfeedback/package.json b/dev-packages/e2e-tests/test-applications/nextjs-16-userfeedback/package.json index 24101853350b..b30636cd3576 100644 --- a/dev-packages/e2e-tests/test-applications/nextjs-16-userfeedback/package.json +++ b/dev-packages/e2e-tests/test-applications/nextjs-16-userfeedback/package.json @@ -11,7 +11,7 @@ "test:assert": "pnpm test:prod" }, "dependencies": { - "@sentry/nextjs": "latest || *", + "@sentry/nextjs": "file:../../packed/sentry-nextjs-packed.tgz", "@types/node": "^20", "@types/react": "^19", "@types/react-dom": "^19", diff --git a/dev-packages/e2e-tests/test-applications/nextjs-16/.npmrc b/dev-packages/e2e-tests/test-applications/nextjs-16/.npmrc deleted file mode 100644 index a3160f4de175..000000000000 --- a/dev-packages/e2e-tests/test-applications/nextjs-16/.npmrc +++ /dev/null @@ -1,4 +0,0 @@ -@sentry:registry=http://127.0.0.1:4873 -@sentry-internal:registry=http://127.0.0.1:4873 -public-hoist-pattern[]=*import-in-the-middle* -public-hoist-pattern[]=*require-in-the-middle* diff --git a/dev-packages/e2e-tests/test-applications/nextjs-16/package.json b/dev-packages/e2e-tests/test-applications/nextjs-16/package.json index 4f90b2bc9fe8..1e417a48fd1f 100644 --- a/dev-packages/e2e-tests/test-applications/nextjs-16/package.json +++ b/dev-packages/e2e-tests/test-applications/nextjs-16/package.json @@ -23,8 +23,8 @@ "test:assert-webpack": "pnpm test:prod && pnpm test:dev-webpack" }, "dependencies": { - "@sentry/nextjs": "latest || *", - "@sentry/core": "latest || *", + "@sentry/nextjs": "file:../../packed/sentry-nextjs-packed.tgz", + "@sentry/core": "file:../../packed/sentry-core-packed.tgz", "@vercel/queue": "^0.1.3", "ai": "^3.0.0", "import-in-the-middle": "^2", diff --git a/dev-packages/e2e-tests/test-applications/nextjs-app-dir/.npmrc b/dev-packages/e2e-tests/test-applications/nextjs-app-dir/.npmrc deleted file mode 100644 index 070f80f05092..000000000000 --- a/dev-packages/e2e-tests/test-applications/nextjs-app-dir/.npmrc +++ /dev/null @@ -1,2 +0,0 @@ -@sentry:registry=http://127.0.0.1:4873 -@sentry-internal:registry=http://127.0.0.1:4873 diff --git a/dev-packages/e2e-tests/test-applications/nextjs-app-dir/package.json b/dev-packages/e2e-tests/test-applications/nextjs-app-dir/package.json index 83c0fac47610..cb7927e9b0d8 100644 --- a/dev-packages/e2e-tests/test-applications/nextjs-app-dir/package.json +++ b/dev-packages/e2e-tests/test-applications/nextjs-app-dir/package.json @@ -14,8 +14,8 @@ "test:assert": "pnpm test:test-build && pnpm test:prod && pnpm test:dev" }, "dependencies": { - "@sentry/nextjs": "latest || *", - "@sentry/core": "latest || *", + "@sentry/nextjs": "file:../../packed/sentry-nextjs-packed.tgz", + "@sentry/core": "file:../../packed/sentry-core-packed.tgz", "@types/node": "^18.19.1", "@types/react": "18.0.26", "@types/react-dom": "18.0.9", diff --git a/dev-packages/e2e-tests/test-applications/nextjs-orpc/.npmrc b/dev-packages/e2e-tests/test-applications/nextjs-orpc/.npmrc deleted file mode 100644 index 070f80f05092..000000000000 --- a/dev-packages/e2e-tests/test-applications/nextjs-orpc/.npmrc +++ /dev/null @@ -1,2 +0,0 @@ -@sentry:registry=http://127.0.0.1:4873 -@sentry-internal:registry=http://127.0.0.1:4873 diff --git a/dev-packages/e2e-tests/test-applications/nextjs-orpc/package.json b/dev-packages/e2e-tests/test-applications/nextjs-orpc/package.json index 7e32f562916a..21f835e2ecd4 100644 --- a/dev-packages/e2e-tests/test-applications/nextjs-orpc/package.json +++ b/dev-packages/e2e-tests/test-applications/nextjs-orpc/package.json @@ -16,7 +16,7 @@ "test:assert": "pnpm test:prod && pnpm test:dev" }, "dependencies": { - "@sentry/nextjs": "latest || *", + "@sentry/nextjs": "file:../../packed/sentry-nextjs-packed.tgz", "@orpc/server": "latest", "@orpc/client": "latest", "next": "14.2.35", diff --git a/dev-packages/e2e-tests/test-applications/nextjs-pages-dir/.npmrc b/dev-packages/e2e-tests/test-applications/nextjs-pages-dir/.npmrc deleted file mode 100644 index 070f80f05092..000000000000 --- a/dev-packages/e2e-tests/test-applications/nextjs-pages-dir/.npmrc +++ /dev/null @@ -1,2 +0,0 @@ -@sentry:registry=http://127.0.0.1:4873 -@sentry-internal:registry=http://127.0.0.1:4873 diff --git a/dev-packages/e2e-tests/test-applications/nextjs-pages-dir/package.json b/dev-packages/e2e-tests/test-applications/nextjs-pages-dir/package.json index eda574954224..f677e02dd954 100644 --- a/dev-packages/e2e-tests/test-applications/nextjs-pages-dir/package.json +++ b/dev-packages/e2e-tests/test-applications/nextjs-pages-dir/package.json @@ -15,8 +15,8 @@ "test:assert": "pnpm test:test-build && pnpm test:prod && pnpm test:dev" }, "dependencies": { - "@sentry/nextjs": "latest || *", - "@sentry/core": "latest || *", + "@sentry/nextjs": "file:../../packed/sentry-nextjs-packed.tgz", + "@sentry/core": "file:../../packed/sentry-core-packed.tgz", "@types/node": "^18.19.1", "@types/react": "18.0.26", "@types/react-dom": "18.0.9", diff --git a/dev-packages/e2e-tests/test-applications/nextjs-sourcemaps/.npmrc b/dev-packages/e2e-tests/test-applications/nextjs-sourcemaps/.npmrc deleted file mode 100644 index 070f80f05092..000000000000 --- a/dev-packages/e2e-tests/test-applications/nextjs-sourcemaps/.npmrc +++ /dev/null @@ -1,2 +0,0 @@ -@sentry:registry=http://127.0.0.1:4873 -@sentry-internal:registry=http://127.0.0.1:4873 diff --git a/dev-packages/e2e-tests/test-applications/nextjs-sourcemaps/package.json b/dev-packages/e2e-tests/test-applications/nextjs-sourcemaps/package.json index 16d2ef6d6050..9667f17865f1 100644 --- a/dev-packages/e2e-tests/test-applications/nextjs-sourcemaps/package.json +++ b/dev-packages/e2e-tests/test-applications/nextjs-sourcemaps/package.json @@ -9,7 +9,7 @@ "test:assert": "pnpm ts-node --script-mode assert-build.ts" }, "dependencies": { - "@sentry/nextjs": "latest || *", + "@sentry/nextjs": "file:../../packed/sentry-nextjs-packed.tgz", "next": "16.1.7", "react": "19.1.0", "react-dom": "19.1.0", diff --git a/dev-packages/e2e-tests/test-applications/node-connect/.npmrc b/dev-packages/e2e-tests/test-applications/node-connect/.npmrc deleted file mode 100644 index 070f80f05092..000000000000 --- a/dev-packages/e2e-tests/test-applications/node-connect/.npmrc +++ /dev/null @@ -1,2 +0,0 @@ -@sentry:registry=http://127.0.0.1:4873 -@sentry-internal:registry=http://127.0.0.1:4873 diff --git a/dev-packages/e2e-tests/test-applications/node-connect/package.json b/dev-packages/e2e-tests/test-applications/node-connect/package.json index db3979ac7a94..729cfbe6c095 100644 --- a/dev-packages/e2e-tests/test-applications/node-connect/package.json +++ b/dev-packages/e2e-tests/test-applications/node-connect/package.json @@ -11,7 +11,7 @@ "test:assert": "pnpm test" }, "dependencies": { - "@sentry/node": "latest || *", + "@sentry/node": "file:../../packed/sentry-node-packed.tgz", "@types/node": "^18.19.1", "@types/connect": "3.4.38", "connect": "3.7.0", diff --git a/dev-packages/e2e-tests/test-applications/node-core-express-otel-v1-custom-sampler/.npmrc b/dev-packages/e2e-tests/test-applications/node-core-express-otel-v1-custom-sampler/.npmrc deleted file mode 100644 index 070f80f05092..000000000000 --- a/dev-packages/e2e-tests/test-applications/node-core-express-otel-v1-custom-sampler/.npmrc +++ /dev/null @@ -1,2 +0,0 @@ -@sentry:registry=http://127.0.0.1:4873 -@sentry-internal:registry=http://127.0.0.1:4873 diff --git a/dev-packages/e2e-tests/test-applications/node-core-express-otel-v1-custom-sampler/package.json b/dev-packages/e2e-tests/test-applications/node-core-express-otel-v1-custom-sampler/package.json index 6f379575019b..160edce67c56 100644 --- a/dev-packages/e2e-tests/test-applications/node-core-express-otel-v1-custom-sampler/package.json +++ b/dev-packages/e2e-tests/test-applications/node-core-express-otel-v1-custom-sampler/package.json @@ -18,8 +18,8 @@ "@opentelemetry/resources": "^1.30.1", "@opentelemetry/sdk-trace-node": "^1.30.1", "@opentelemetry/semantic-conventions": "^1.30.0", - "@sentry/node-core": "latest || *", - "@sentry/opentelemetry": "latest || *", + "@sentry/node-core": "file:../../packed/sentry-node-core-packed.tgz", + "@sentry/opentelemetry": "file:../../packed/sentry-opentelemetry-packed.tgz", "@types/express": "4.17.17", "@types/node": "^18.19.1", "express": "^4.21.2", diff --git a/dev-packages/e2e-tests/test-applications/node-core-express-otel-v1-sdk-node/.npmrc b/dev-packages/e2e-tests/test-applications/node-core-express-otel-v1-sdk-node/.npmrc deleted file mode 100644 index 070f80f05092..000000000000 --- a/dev-packages/e2e-tests/test-applications/node-core-express-otel-v1-sdk-node/.npmrc +++ /dev/null @@ -1,2 +0,0 @@ -@sentry:registry=http://127.0.0.1:4873 -@sentry-internal:registry=http://127.0.0.1:4873 diff --git a/dev-packages/e2e-tests/test-applications/node-core-express-otel-v1-sdk-node/package.json b/dev-packages/e2e-tests/test-applications/node-core-express-otel-v1-sdk-node/package.json index b7d9b06647b3..0ac871787ede 100644 --- a/dev-packages/e2e-tests/test-applications/node-core-express-otel-v1-sdk-node/package.json +++ b/dev-packages/e2e-tests/test-applications/node-core-express-otel-v1-sdk-node/package.json @@ -20,8 +20,8 @@ "@opentelemetry/semantic-conventions": "^1.30.0", "@opentelemetry/sdk-node": "^0.57.2", "@opentelemetry/exporter-trace-otlp-http": "^0.57.2", - "@sentry/node-core": "latest || *", - "@sentry/opentelemetry": "latest || *", + "@sentry/node-core": "file:../../packed/sentry-node-core-packed.tgz", + "@sentry/opentelemetry": "file:../../packed/sentry-opentelemetry-packed.tgz", "@types/express": "4.17.17", "@types/node": "^18.19.1", "express": "^4.21.2", diff --git a/dev-packages/e2e-tests/test-applications/node-core-express-otel-v1/.npmrc b/dev-packages/e2e-tests/test-applications/node-core-express-otel-v1/.npmrc deleted file mode 100644 index 070f80f05092..000000000000 --- a/dev-packages/e2e-tests/test-applications/node-core-express-otel-v1/.npmrc +++ /dev/null @@ -1,2 +0,0 @@ -@sentry:registry=http://127.0.0.1:4873 -@sentry-internal:registry=http://127.0.0.1:4873 diff --git a/dev-packages/e2e-tests/test-applications/node-core-express-otel-v1/package.json b/dev-packages/e2e-tests/test-applications/node-core-express-otel-v1/package.json index 28d17064a5ff..7cfea6cc7052 100644 --- a/dev-packages/e2e-tests/test-applications/node-core-express-otel-v1/package.json +++ b/dev-packages/e2e-tests/test-applications/node-core-express-otel-v1/package.json @@ -11,8 +11,8 @@ "test:assert": "pnpm test" }, "dependencies": { - "@sentry/node-core": "latest || *", - "@sentry/opentelemetry": "latest || *", + "@sentry/node-core": "file:../../packed/sentry-node-core-packed.tgz", + "@sentry/opentelemetry": "file:../../packed/sentry-opentelemetry-packed.tgz", "@opentelemetry/api": "^1.9.0", "@opentelemetry/core": "^1.30.1", "@opentelemetry/instrumentation": "^0.57.1", diff --git a/dev-packages/e2e-tests/test-applications/node-core-express-otel-v2-custom-sampler/.npmrc b/dev-packages/e2e-tests/test-applications/node-core-express-otel-v2-custom-sampler/.npmrc deleted file mode 100644 index 070f80f05092..000000000000 --- a/dev-packages/e2e-tests/test-applications/node-core-express-otel-v2-custom-sampler/.npmrc +++ /dev/null @@ -1,2 +0,0 @@ -@sentry:registry=http://127.0.0.1:4873 -@sentry-internal:registry=http://127.0.0.1:4873 diff --git a/dev-packages/e2e-tests/test-applications/node-core-express-otel-v2-custom-sampler/package.json b/dev-packages/e2e-tests/test-applications/node-core-express-otel-v2-custom-sampler/package.json index f79c0894bfc9..b44b3a62911e 100644 --- a/dev-packages/e2e-tests/test-applications/node-core-express-otel-v2-custom-sampler/package.json +++ b/dev-packages/e2e-tests/test-applications/node-core-express-otel-v2-custom-sampler/package.json @@ -18,8 +18,8 @@ "@opentelemetry/resources": "^2.6.0", "@opentelemetry/sdk-trace-node": "^2.6.0", "@opentelemetry/semantic-conventions": "^1.40.0", - "@sentry/node-core": "latest || *", - "@sentry/opentelemetry": "latest || *", + "@sentry/node-core": "file:../../packed/sentry-node-core-packed.tgz", + "@sentry/opentelemetry": "file:../../packed/sentry-opentelemetry-packed.tgz", "@types/express": "4.17.17", "@types/node": "^18.19.1", "express": "^4.21.2", diff --git a/dev-packages/e2e-tests/test-applications/node-core-express-otel-v2-sdk-node/.npmrc b/dev-packages/e2e-tests/test-applications/node-core-express-otel-v2-sdk-node/.npmrc deleted file mode 100644 index 070f80f05092..000000000000 --- a/dev-packages/e2e-tests/test-applications/node-core-express-otel-v2-sdk-node/.npmrc +++ /dev/null @@ -1,2 +0,0 @@ -@sentry:registry=http://127.0.0.1:4873 -@sentry-internal:registry=http://127.0.0.1:4873 diff --git a/dev-packages/e2e-tests/test-applications/node-core-express-otel-v2-sdk-node/package.json b/dev-packages/e2e-tests/test-applications/node-core-express-otel-v2-sdk-node/package.json index dd294c205b32..8552a7990a2d 100644 --- a/dev-packages/e2e-tests/test-applications/node-core-express-otel-v2-sdk-node/package.json +++ b/dev-packages/e2e-tests/test-applications/node-core-express-otel-v2-sdk-node/package.json @@ -20,8 +20,8 @@ "@opentelemetry/semantic-conventions": "^1.40.0", "@opentelemetry/sdk-node": "^0.214.0", "@opentelemetry/exporter-trace-otlp-http": "^0.214.0", - "@sentry/node-core": "latest || *", - "@sentry/opentelemetry": "latest || *", + "@sentry/node-core": "file:../../packed/sentry-node-core-packed.tgz", + "@sentry/opentelemetry": "file:../../packed/sentry-opentelemetry-packed.tgz", "@types/express": "4.17.17", "@types/node": "^18.19.1", "express": "^4.21.2", diff --git a/dev-packages/e2e-tests/test-applications/node-core-express-otel-v2/.npmrc b/dev-packages/e2e-tests/test-applications/node-core-express-otel-v2/.npmrc deleted file mode 100644 index 070f80f05092..000000000000 --- a/dev-packages/e2e-tests/test-applications/node-core-express-otel-v2/.npmrc +++ /dev/null @@ -1,2 +0,0 @@ -@sentry:registry=http://127.0.0.1:4873 -@sentry-internal:registry=http://127.0.0.1:4873 diff --git a/dev-packages/e2e-tests/test-applications/node-core-express-otel-v2/package.json b/dev-packages/e2e-tests/test-applications/node-core-express-otel-v2/package.json index 7c1ea4377070..dc57fb2568f8 100644 --- a/dev-packages/e2e-tests/test-applications/node-core-express-otel-v2/package.json +++ b/dev-packages/e2e-tests/test-applications/node-core-express-otel-v2/package.json @@ -11,8 +11,8 @@ "test:assert": "pnpm test" }, "dependencies": { - "@sentry/node-core": "latest || *", - "@sentry/opentelemetry": "latest || *", + "@sentry/node-core": "file:../../packed/sentry-node-core-packed.tgz", + "@sentry/opentelemetry": "file:../../packed/sentry-opentelemetry-packed.tgz", "@opentelemetry/api": "^1.9.0", "@opentelemetry/core": "^2.6.0", "@opentelemetry/instrumentation": "^0.214.0", diff --git a/dev-packages/e2e-tests/test-applications/node-core-light-express/.npmrc b/dev-packages/e2e-tests/test-applications/node-core-light-express/.npmrc deleted file mode 100644 index 070f80f05092..000000000000 --- a/dev-packages/e2e-tests/test-applications/node-core-light-express/.npmrc +++ /dev/null @@ -1,2 +0,0 @@ -@sentry:registry=http://127.0.0.1:4873 -@sentry-internal:registry=http://127.0.0.1:4873 diff --git a/dev-packages/e2e-tests/test-applications/node-core-light-express/package.json b/dev-packages/e2e-tests/test-applications/node-core-light-express/package.json index d1902f528561..a78e5a1b24cb 100644 --- a/dev-packages/e2e-tests/test-applications/node-core-light-express/package.json +++ b/dev-packages/e2e-tests/test-applications/node-core-light-express/package.json @@ -12,7 +12,7 @@ "test:assert": "pnpm test" }, "dependencies": { - "@sentry/node-core": "latest || *", + "@sentry/node-core": "file:../../packed/sentry-node-core-packed.tgz", "@types/express": "^4.17.21", "@types/node": "^22.0.0", "express": "^4.21.2", @@ -21,7 +21,7 @@ "devDependencies": { "@playwright/test": "~1.56.0", "@sentry-internal/test-utils": "link:../../../test-utils", - "@sentry/core": "latest || *" + "@sentry/core": "file:../../packed/sentry-core-packed.tgz" }, "volta": { "node": "22.18.0" diff --git a/dev-packages/e2e-tests/test-applications/node-core-light-otlp/.npmrc b/dev-packages/e2e-tests/test-applications/node-core-light-otlp/.npmrc deleted file mode 100644 index 070f80f05092..000000000000 --- a/dev-packages/e2e-tests/test-applications/node-core-light-otlp/.npmrc +++ /dev/null @@ -1,2 +0,0 @@ -@sentry:registry=http://127.0.0.1:4873 -@sentry-internal:registry=http://127.0.0.1:4873 diff --git a/dev-packages/e2e-tests/test-applications/node-core-light-otlp/package.json b/dev-packages/e2e-tests/test-applications/node-core-light-otlp/package.json index fcf388cfaa89..f819e1685eb2 100644 --- a/dev-packages/e2e-tests/test-applications/node-core-light-otlp/package.json +++ b/dev-packages/e2e-tests/test-applications/node-core-light-otlp/package.json @@ -16,7 +16,7 @@ "@opentelemetry/exporter-trace-otlp-http": "^0.211.0", "@opentelemetry/sdk-trace-base": "^2.5.1", "@opentelemetry/sdk-trace-node": "^2.5.1", - "@sentry/node-core": "latest || *", + "@sentry/node-core": "file:../../packed/sentry-node-core-packed.tgz", "@types/express": "^4.17.21", "@types/node": "^22.0.0", "express": "^4.21.2", @@ -25,7 +25,7 @@ "devDependencies": { "@playwright/test": "~1.56.0", "@sentry-internal/test-utils": "link:../../../test-utils", - "@sentry/core": "latest || *" + "@sentry/core": "file:../../packed/sentry-core-packed.tgz" }, "volta": { "node": "22.18.0" diff --git a/dev-packages/e2e-tests/test-applications/node-exports-test-app/.npmrc b/dev-packages/e2e-tests/test-applications/node-exports-test-app/.npmrc deleted file mode 100644 index 070f80f05092..000000000000 --- a/dev-packages/e2e-tests/test-applications/node-exports-test-app/.npmrc +++ /dev/null @@ -1,2 +0,0 @@ -@sentry:registry=http://127.0.0.1:4873 -@sentry-internal:registry=http://127.0.0.1:4873 diff --git a/dev-packages/e2e-tests/test-applications/node-exports-test-app/package.json b/dev-packages/e2e-tests/test-applications/node-exports-test-app/package.json index adb4f189fe85..a836b2b618ba 100644 --- a/dev-packages/e2e-tests/test-applications/node-exports-test-app/package.json +++ b/dev-packages/e2e-tests/test-applications/node-exports-test-app/package.json @@ -12,14 +12,14 @@ "test:assert": "pnpm test" }, "dependencies": { - "@sentry/node": "latest || *", - "@sentry/sveltekit": "latest || *", - "@sentry/remix": "latest || *", - "@sentry/astro": "latest || *", - "@sentry/nextjs": "latest || *", - "@sentry/aws-serverless": "latest || *", - "@sentry/google-cloud-serverless": "latest || *", - "@sentry/bun": "latest || *", + "@sentry/node": "file:../../packed/sentry-node-packed.tgz", + "@sentry/sveltekit": "file:../../packed/sentry-sveltekit-packed.tgz", + "@sentry/remix": "file:../../packed/sentry-remix-packed.tgz", + "@sentry/astro": "file:../../packed/sentry-astro-packed.tgz", + "@sentry/nextjs": "file:../../packed/sentry-nextjs-packed.tgz", + "@sentry/aws-serverless": "file:../../packed/sentry-aws-serverless-packed.tgz", + "@sentry/google-cloud-serverless": "file:../../packed/sentry-google-cloud-serverless-packed.tgz", + "@sentry/bun": "file:../../packed/sentry-bun-packed.tgz", "@types/node": "^18.19.1", "typescript": "~5.0.0" }, diff --git a/dev-packages/e2e-tests/test-applications/node-express-cjs-preload/.npmrc b/dev-packages/e2e-tests/test-applications/node-express-cjs-preload/.npmrc deleted file mode 100644 index 070f80f05092..000000000000 --- a/dev-packages/e2e-tests/test-applications/node-express-cjs-preload/.npmrc +++ /dev/null @@ -1,2 +0,0 @@ -@sentry:registry=http://127.0.0.1:4873 -@sentry-internal:registry=http://127.0.0.1:4873 diff --git a/dev-packages/e2e-tests/test-applications/node-express-cjs-preload/package.json b/dev-packages/e2e-tests/test-applications/node-express-cjs-preload/package.json index 532e28769675..125372c4501a 100644 --- a/dev-packages/e2e-tests/test-applications/node-express-cjs-preload/package.json +++ b/dev-packages/e2e-tests/test-applications/node-express-cjs-preload/package.json @@ -9,8 +9,8 @@ "test:assert": "playwright test" }, "dependencies": { - "@sentry/node": "latest || *", - "@sentry/opentelemetry": "latest || *", + "@sentry/node": "file:../../packed/sentry-node-packed.tgz", + "@sentry/opentelemetry": "file:../../packed/sentry-opentelemetry-packed.tgz", "express": "^4.21.2" }, "devDependencies": { diff --git a/dev-packages/e2e-tests/test-applications/node-express-esm-loader/.npmrc b/dev-packages/e2e-tests/test-applications/node-express-esm-loader/.npmrc deleted file mode 100644 index 070f80f05092..000000000000 --- a/dev-packages/e2e-tests/test-applications/node-express-esm-loader/.npmrc +++ /dev/null @@ -1,2 +0,0 @@ -@sentry:registry=http://127.0.0.1:4873 -@sentry-internal:registry=http://127.0.0.1:4873 diff --git a/dev-packages/e2e-tests/test-applications/node-express-esm-loader/package.json b/dev-packages/e2e-tests/test-applications/node-express-esm-loader/package.json index b54247ea1292..a2d2c720e92a 100644 --- a/dev-packages/e2e-tests/test-applications/node-express-esm-loader/package.json +++ b/dev-packages/e2e-tests/test-applications/node-express-esm-loader/package.json @@ -9,8 +9,8 @@ "test:assert": "playwright test" }, "dependencies": { - "@sentry/node": "latest || *", - "@sentry/opentelemetry": "latest || *", + "@sentry/node": "file:../../packed/sentry-node-packed.tgz", + "@sentry/opentelemetry": "file:../../packed/sentry-opentelemetry-packed.tgz", "express": "^4.21.2" }, "devDependencies": { diff --git a/dev-packages/e2e-tests/test-applications/node-express-esm-preload/.npmrc b/dev-packages/e2e-tests/test-applications/node-express-esm-preload/.npmrc deleted file mode 100644 index 070f80f05092..000000000000 --- a/dev-packages/e2e-tests/test-applications/node-express-esm-preload/.npmrc +++ /dev/null @@ -1,2 +0,0 @@ -@sentry:registry=http://127.0.0.1:4873 -@sentry-internal:registry=http://127.0.0.1:4873 diff --git a/dev-packages/e2e-tests/test-applications/node-express-esm-preload/package.json b/dev-packages/e2e-tests/test-applications/node-express-esm-preload/package.json index 0ce974f5aa43..4a602b6bd304 100644 --- a/dev-packages/e2e-tests/test-applications/node-express-esm-preload/package.json +++ b/dev-packages/e2e-tests/test-applications/node-express-esm-preload/package.json @@ -9,8 +9,8 @@ "test:assert": "playwright test" }, "dependencies": { - "@sentry/node": "latest || *", - "@sentry/opentelemetry": "latest || *", + "@sentry/node": "file:../../packed/sentry-node-packed.tgz", + "@sentry/opentelemetry": "file:../../packed/sentry-opentelemetry-packed.tgz", "express": "^4.21.2" }, "devDependencies": { diff --git a/dev-packages/e2e-tests/test-applications/node-express-esm-without-loader/.npmrc b/dev-packages/e2e-tests/test-applications/node-express-esm-without-loader/.npmrc deleted file mode 100644 index 070f80f05092..000000000000 --- a/dev-packages/e2e-tests/test-applications/node-express-esm-without-loader/.npmrc +++ /dev/null @@ -1,2 +0,0 @@ -@sentry:registry=http://127.0.0.1:4873 -@sentry-internal:registry=http://127.0.0.1:4873 diff --git a/dev-packages/e2e-tests/test-applications/node-express-esm-without-loader/package.json b/dev-packages/e2e-tests/test-applications/node-express-esm-without-loader/package.json index a389962d4deb..f6fc9adf6108 100644 --- a/dev-packages/e2e-tests/test-applications/node-express-esm-without-loader/package.json +++ b/dev-packages/e2e-tests/test-applications/node-express-esm-without-loader/package.json @@ -9,8 +9,8 @@ "test:assert": "playwright test" }, "dependencies": { - "@sentry/node": "latest || *", - "@sentry/opentelemetry": "latest || *", + "@sentry/node": "file:../../packed/sentry-node-packed.tgz", + "@sentry/opentelemetry": "file:../../packed/sentry-opentelemetry-packed.tgz", "express": "^4.21.2" }, "devDependencies": { diff --git a/dev-packages/e2e-tests/test-applications/node-express-incorrect-instrumentation/.npmrc b/dev-packages/e2e-tests/test-applications/node-express-incorrect-instrumentation/.npmrc deleted file mode 100644 index 070f80f05092..000000000000 --- a/dev-packages/e2e-tests/test-applications/node-express-incorrect-instrumentation/.npmrc +++ /dev/null @@ -1,2 +0,0 @@ -@sentry:registry=http://127.0.0.1:4873 -@sentry-internal:registry=http://127.0.0.1:4873 diff --git a/dev-packages/e2e-tests/test-applications/node-express-incorrect-instrumentation/package.json b/dev-packages/e2e-tests/test-applications/node-express-incorrect-instrumentation/package.json index 1036f91cf4e9..84afe281c642 100644 --- a/dev-packages/e2e-tests/test-applications/node-express-incorrect-instrumentation/package.json +++ b/dev-packages/e2e-tests/test-applications/node-express-incorrect-instrumentation/package.json @@ -11,7 +11,7 @@ "test:assert": "pnpm test" }, "dependencies": { - "@sentry/node": "latest || *", + "@sentry/node": "file:../../packed/sentry-node-packed.tgz", "@trpc/server": "10.45.4", "@trpc/client": "10.45.4", "@types/express": "4.17.17", diff --git a/dev-packages/e2e-tests/test-applications/node-express-mcp-v2/.npmrc b/dev-packages/e2e-tests/test-applications/node-express-mcp-v2/.npmrc deleted file mode 100644 index 070f80f05092..000000000000 --- a/dev-packages/e2e-tests/test-applications/node-express-mcp-v2/.npmrc +++ /dev/null @@ -1,2 +0,0 @@ -@sentry:registry=http://127.0.0.1:4873 -@sentry-internal:registry=http://127.0.0.1:4873 diff --git a/dev-packages/e2e-tests/test-applications/node-express-mcp-v2/package.json b/dev-packages/e2e-tests/test-applications/node-express-mcp-v2/package.json index 6a5a293b956d..4460adbd034c 100644 --- a/dev-packages/e2e-tests/test-applications/node-express-mcp-v2/package.json +++ b/dev-packages/e2e-tests/test-applications/node-express-mcp-v2/package.json @@ -14,7 +14,7 @@ "@cfworker/json-schema": "^4.0.0", "@modelcontextprotocol/server": "2.0.0-alpha.2", "@modelcontextprotocol/node": "2.0.0-alpha.2", - "@sentry/node": "latest || *", + "@sentry/node": "file:../../packed/sentry-node-packed.tgz", "@types/express": "^4.17.21", "@types/node": "^18.19.1", "express": "^4.21.2", @@ -25,7 +25,7 @@ "@modelcontextprotocol/client": "2.0.0-alpha.2", "@playwright/test": "~1.56.0", "@sentry-internal/test-utils": "link:../../../test-utils", - "@sentry/core": "latest || *" + "@sentry/core": "file:../../packed/sentry-core-packed.tgz" }, "type": "module", "volta": { diff --git a/dev-packages/e2e-tests/test-applications/node-express-send-to-sentry/.npmrc b/dev-packages/e2e-tests/test-applications/node-express-send-to-sentry/.npmrc deleted file mode 100644 index 070f80f05092..000000000000 --- a/dev-packages/e2e-tests/test-applications/node-express-send-to-sentry/.npmrc +++ /dev/null @@ -1,2 +0,0 @@ -@sentry:registry=http://127.0.0.1:4873 -@sentry-internal:registry=http://127.0.0.1:4873 diff --git a/dev-packages/e2e-tests/test-applications/node-express-send-to-sentry/package.json b/dev-packages/e2e-tests/test-applications/node-express-send-to-sentry/package.json index cca96b3e49cb..e5ec85096dce 100644 --- a/dev-packages/e2e-tests/test-applications/node-express-send-to-sentry/package.json +++ b/dev-packages/e2e-tests/test-applications/node-express-send-to-sentry/package.json @@ -11,7 +11,7 @@ "test:assert": "pnpm test" }, "dependencies": { - "@sentry/node": "latest || *", + "@sentry/node": "file:../../packed/sentry-node-packed.tgz", "@types/express": "4.17.17", "@types/node": "^18.19.1", "express": "^4.21.2", diff --git a/dev-packages/e2e-tests/test-applications/node-express-v5/.npmrc b/dev-packages/e2e-tests/test-applications/node-express-v5/.npmrc deleted file mode 100644 index 070f80f05092..000000000000 --- a/dev-packages/e2e-tests/test-applications/node-express-v5/.npmrc +++ /dev/null @@ -1,2 +0,0 @@ -@sentry:registry=http://127.0.0.1:4873 -@sentry-internal:registry=http://127.0.0.1:4873 diff --git a/dev-packages/e2e-tests/test-applications/node-express-v5/package.json b/dev-packages/e2e-tests/test-applications/node-express-v5/package.json index a6dd5fab1fbf..cf33b86e8669 100644 --- a/dev-packages/e2e-tests/test-applications/node-express-v5/package.json +++ b/dev-packages/e2e-tests/test-applications/node-express-v5/package.json @@ -12,7 +12,7 @@ }, "dependencies": { "@modelcontextprotocol/sdk": "^1.26.0", - "@sentry/node": "latest || *", + "@sentry/node": "file:../../packed/sentry-node-packed.tgz", "@trpc/server": "10.45.4", "@trpc/client": "10.45.4", "@types/express": "^4.17.21", @@ -24,7 +24,7 @@ "devDependencies": { "@playwright/test": "~1.56.0", "@sentry-internal/test-utils": "link:../../../test-utils", - "@sentry/core": "latest || *" + "@sentry/core": "file:../../packed/sentry-core-packed.tgz" }, "resolutions": { "@types/qs": "6.9.17" diff --git a/dev-packages/e2e-tests/test-applications/node-express/.npmrc b/dev-packages/e2e-tests/test-applications/node-express/.npmrc deleted file mode 100644 index 070f80f05092..000000000000 --- a/dev-packages/e2e-tests/test-applications/node-express/.npmrc +++ /dev/null @@ -1,2 +0,0 @@ -@sentry:registry=http://127.0.0.1:4873 -@sentry-internal:registry=http://127.0.0.1:4873 diff --git a/dev-packages/e2e-tests/test-applications/node-express/package.json b/dev-packages/e2e-tests/test-applications/node-express/package.json index 6ab9eb2047b9..4d2ad1833a58 100644 --- a/dev-packages/e2e-tests/test-applications/node-express/package.json +++ b/dev-packages/e2e-tests/test-applications/node-express/package.json @@ -12,7 +12,7 @@ }, "dependencies": { "@modelcontextprotocol/sdk": "^1.26.0", - "@sentry/node": "latest || *", + "@sentry/node": "file:../../packed/sentry-node-packed.tgz", "@trpc/server": "10.45.4", "@trpc/client": "10.45.4", "@types/express": "^4.17.21", @@ -24,7 +24,7 @@ "devDependencies": { "@playwright/test": "~1.56.0", "@sentry-internal/test-utils": "link:../../../test-utils", - "@sentry/core": "latest || *" + "@sentry/core": "file:../../packed/sentry-core-packed.tgz" }, "resolutions": { "@types/qs": "6.9.17" diff --git a/dev-packages/e2e-tests/test-applications/node-fastify-3/.npmrc b/dev-packages/e2e-tests/test-applications/node-fastify-3/.npmrc deleted file mode 100644 index 070f80f05092..000000000000 --- a/dev-packages/e2e-tests/test-applications/node-fastify-3/.npmrc +++ /dev/null @@ -1,2 +0,0 @@ -@sentry:registry=http://127.0.0.1:4873 -@sentry-internal:registry=http://127.0.0.1:4873 diff --git a/dev-packages/e2e-tests/test-applications/node-fastify-3/package.json b/dev-packages/e2e-tests/test-applications/node-fastify-3/package.json index 663268c466a9..3fa36adbbbd5 100644 --- a/dev-packages/e2e-tests/test-applications/node-fastify-3/package.json +++ b/dev-packages/e2e-tests/test-applications/node-fastify-3/package.json @@ -13,7 +13,7 @@ "test:assert": "pnpm test && pnpm test:override" }, "dependencies": { - "@sentry/node": "latest || *", + "@sentry/node": "file:../../packed/sentry-node-packed.tgz", "@types/node": "^18.19.1", "fastify": "3.29.5", "typescript": "~5.0.0", diff --git a/dev-packages/e2e-tests/test-applications/node-fastify-4/.npmrc b/dev-packages/e2e-tests/test-applications/node-fastify-4/.npmrc deleted file mode 100644 index 070f80f05092..000000000000 --- a/dev-packages/e2e-tests/test-applications/node-fastify-4/.npmrc +++ /dev/null @@ -1,2 +0,0 @@ -@sentry:registry=http://127.0.0.1:4873 -@sentry-internal:registry=http://127.0.0.1:4873 diff --git a/dev-packages/e2e-tests/test-applications/node-fastify-4/package.json b/dev-packages/e2e-tests/test-applications/node-fastify-4/package.json index 389aa9ff677b..086ec85fac7a 100644 --- a/dev-packages/e2e-tests/test-applications/node-fastify-4/package.json +++ b/dev-packages/e2e-tests/test-applications/node-fastify-4/package.json @@ -13,7 +13,7 @@ "test:assert": "pnpm test && pnpm test:override" }, "dependencies": { - "@sentry/node": "latest || *", + "@sentry/node": "file:../../packed/sentry-node-packed.tgz", "@types/node": "^18.19.1", "fastify": "4.29.1", "typescript": "5.6.3", diff --git a/dev-packages/e2e-tests/test-applications/node-fastify-5/.npmrc b/dev-packages/e2e-tests/test-applications/node-fastify-5/.npmrc deleted file mode 100644 index 070f80f05092..000000000000 --- a/dev-packages/e2e-tests/test-applications/node-fastify-5/.npmrc +++ /dev/null @@ -1,2 +0,0 @@ -@sentry:registry=http://127.0.0.1:4873 -@sentry-internal:registry=http://127.0.0.1:4873 diff --git a/dev-packages/e2e-tests/test-applications/node-fastify-5/package.json b/dev-packages/e2e-tests/test-applications/node-fastify-5/package.json index a7a754d3f1a6..dc0fa7770c70 100644 --- a/dev-packages/e2e-tests/test-applications/node-fastify-5/package.json +++ b/dev-packages/e2e-tests/test-applications/node-fastify-5/package.json @@ -13,7 +13,7 @@ "test:assert": "pnpm test && pnpm test:override" }, "dependencies": { - "@sentry/node": "latest || *", + "@sentry/node": "file:../../packed/sentry-node-packed.tgz", "@types/node": "^18.19.1", "fastify": "^5.7.0", "typescript": "5.6.3", diff --git a/dev-packages/e2e-tests/test-applications/node-firebase/.npmrc b/dev-packages/e2e-tests/test-applications/node-firebase/.npmrc deleted file mode 100644 index 070f80f05092..000000000000 --- a/dev-packages/e2e-tests/test-applications/node-firebase/.npmrc +++ /dev/null @@ -1,2 +0,0 @@ -@sentry:registry=http://127.0.0.1:4873 -@sentry-internal:registry=http://127.0.0.1:4873 diff --git a/dev-packages/e2e-tests/test-applications/node-firebase/firestore-app/package.json b/dev-packages/e2e-tests/test-applications/node-firebase/firestore-app/package.json index e0e0614a23f8..0961acb488d2 100644 --- a/dev-packages/e2e-tests/test-applications/node-firebase/firestore-app/package.json +++ b/dev-packages/e2e-tests/test-applications/node-firebase/firestore-app/package.json @@ -8,7 +8,7 @@ }, "dependencies": { "@firebase/app": "^0.13.1", - "@sentry/node": "latest || *", + "@sentry/node": "file:../../../packed/sentry-node-packed.tgz", "express": "^4.21.2", "firebase": "^12.0.0" }, diff --git a/dev-packages/e2e-tests/test-applications/node-firebase/functions/package.json b/dev-packages/e2e-tests/test-applications/node-firebase/functions/package.json index c3be318b8c38..f179461322dd 100644 --- a/dev-packages/e2e-tests/test-applications/node-firebase/functions/package.json +++ b/dev-packages/e2e-tests/test-applications/node-firebase/functions/package.json @@ -11,7 +11,7 @@ "dependencies": { "firebase-admin": "^12.6.0", "firebase-functions": "^6.0.1", - "@sentry/node": "latest || *" + "@sentry/node": "file:../../../packed/sentry-node-packed.tgz" }, "devDependencies": { "typescript": "5.9.3" diff --git a/dev-packages/e2e-tests/test-applications/node-hapi/.npmrc b/dev-packages/e2e-tests/test-applications/node-hapi/.npmrc deleted file mode 100644 index 070f80f05092..000000000000 --- a/dev-packages/e2e-tests/test-applications/node-hapi/.npmrc +++ /dev/null @@ -1,2 +0,0 @@ -@sentry:registry=http://127.0.0.1:4873 -@sentry-internal:registry=http://127.0.0.1:4873 diff --git a/dev-packages/e2e-tests/test-applications/node-hapi/package.json b/dev-packages/e2e-tests/test-applications/node-hapi/package.json index b735268d901d..ae87544644bf 100644 --- a/dev-packages/e2e-tests/test-applications/node-hapi/package.json +++ b/dev-packages/e2e-tests/test-applications/node-hapi/package.json @@ -13,7 +13,7 @@ "dependencies": { "@hapi/boom": "10.0.1", "@hapi/hapi": "21.3.10", - "@sentry/node": "latest || *" + "@sentry/node": "file:../../packed/sentry-node-packed.tgz" }, "devDependencies": { "@playwright/test": "~1.56.0", diff --git a/dev-packages/e2e-tests/test-applications/node-koa/.npmrc b/dev-packages/e2e-tests/test-applications/node-koa/.npmrc deleted file mode 100644 index 070f80f05092..000000000000 --- a/dev-packages/e2e-tests/test-applications/node-koa/.npmrc +++ /dev/null @@ -1,2 +0,0 @@ -@sentry:registry=http://127.0.0.1:4873 -@sentry-internal:registry=http://127.0.0.1:4873 diff --git a/dev-packages/e2e-tests/test-applications/node-koa/package.json b/dev-packages/e2e-tests/test-applications/node-koa/package.json index 0d993990730b..f4ef47cd0940 100644 --- a/dev-packages/e2e-tests/test-applications/node-koa/package.json +++ b/dev-packages/e2e-tests/test-applications/node-koa/package.json @@ -12,7 +12,7 @@ "dependencies": { "@koa/bodyparser": "^5.1.1", "@koa/router": "^12.0.1", - "@sentry/node": "latest || *", + "@sentry/node": "file:../../packed/sentry-node-packed.tgz", "@types/node": "^18.19.1", "koa": "^2.15.2", "typescript": "~5.0.0" diff --git a/dev-packages/e2e-tests/test-applications/node-otel-custom-sampler/.npmrc b/dev-packages/e2e-tests/test-applications/node-otel-custom-sampler/.npmrc deleted file mode 100644 index 070f80f05092..000000000000 --- a/dev-packages/e2e-tests/test-applications/node-otel-custom-sampler/.npmrc +++ /dev/null @@ -1,2 +0,0 @@ -@sentry:registry=http://127.0.0.1:4873 -@sentry-internal:registry=http://127.0.0.1:4873 diff --git a/dev-packages/e2e-tests/test-applications/node-otel-custom-sampler/package.json b/dev-packages/e2e-tests/test-applications/node-otel-custom-sampler/package.json index 76fcf398380d..b26e3981e028 100644 --- a/dev-packages/e2e-tests/test-applications/node-otel-custom-sampler/package.json +++ b/dev-packages/e2e-tests/test-applications/node-otel-custom-sampler/package.json @@ -13,8 +13,8 @@ "dependencies": { "@opentelemetry/api": "^1.9.0", "@opentelemetry/sdk-trace-node": "^2.6.0", - "@sentry/node": "latest || *", - "@sentry/opentelemetry": "latest || *", + "@sentry/node": "file:../../packed/sentry-node-packed.tgz", + "@sentry/opentelemetry": "file:../../packed/sentry-opentelemetry-packed.tgz", "@types/express": "4.17.17", "@types/node": "^18.19.1", "express": "^4.21.2", diff --git a/dev-packages/e2e-tests/test-applications/node-otel-sdk-node/.npmrc b/dev-packages/e2e-tests/test-applications/node-otel-sdk-node/.npmrc deleted file mode 100644 index 070f80f05092..000000000000 --- a/dev-packages/e2e-tests/test-applications/node-otel-sdk-node/.npmrc +++ /dev/null @@ -1,2 +0,0 @@ -@sentry:registry=http://127.0.0.1:4873 -@sentry-internal:registry=http://127.0.0.1:4873 diff --git a/dev-packages/e2e-tests/test-applications/node-otel-sdk-node/package.json b/dev-packages/e2e-tests/test-applications/node-otel-sdk-node/package.json index f695309f00d7..38badec93335 100644 --- a/dev-packages/e2e-tests/test-applications/node-otel-sdk-node/package.json +++ b/dev-packages/e2e-tests/test-applications/node-otel-sdk-node/package.json @@ -13,7 +13,7 @@ "dependencies": { "@opentelemetry/sdk-node": "0.213.0", "@opentelemetry/exporter-trace-otlp-http": "0.213.0", - "@sentry/node": "latest || *", + "@sentry/node": "file:../../packed/sentry-node-packed.tgz", "@types/express": "4.17.17", "@types/node": "^18.19.1", "express": "^4.21.2", diff --git a/dev-packages/e2e-tests/test-applications/node-otel-without-tracing/.npmrc b/dev-packages/e2e-tests/test-applications/node-otel-without-tracing/.npmrc deleted file mode 100644 index 070f80f05092..000000000000 --- a/dev-packages/e2e-tests/test-applications/node-otel-without-tracing/.npmrc +++ /dev/null @@ -1,2 +0,0 @@ -@sentry:registry=http://127.0.0.1:4873 -@sentry-internal:registry=http://127.0.0.1:4873 diff --git a/dev-packages/e2e-tests/test-applications/node-otel-without-tracing/package.json b/dev-packages/e2e-tests/test-applications/node-otel-without-tracing/package.json index 8e5563fdb4ec..04a6a80a500c 100644 --- a/dev-packages/e2e-tests/test-applications/node-otel-without-tracing/package.json +++ b/dev-packages/e2e-tests/test-applications/node-otel-without-tracing/package.json @@ -17,7 +17,7 @@ "@opentelemetry/instrumentation-undici": "0.24.0", "@opentelemetry/instrumentation-http": "0.214.0", "@opentelemetry/instrumentation": "0.214.0", - "@sentry/node": "latest || *", + "@sentry/node": "file:../../packed/sentry-node-packed.tgz", "@types/express": "4.17.17", "@types/node": "^18.19.1", "express": "^4.21.2", diff --git a/dev-packages/e2e-tests/test-applications/node-otel/.npmrc b/dev-packages/e2e-tests/test-applications/node-otel/.npmrc deleted file mode 100644 index 070f80f05092..000000000000 --- a/dev-packages/e2e-tests/test-applications/node-otel/.npmrc +++ /dev/null @@ -1,2 +0,0 @@ -@sentry:registry=http://127.0.0.1:4873 -@sentry-internal:registry=http://127.0.0.1:4873 diff --git a/dev-packages/e2e-tests/test-applications/node-otel/package.json b/dev-packages/e2e-tests/test-applications/node-otel/package.json index ef7b17112108..274b141ff2a7 100644 --- a/dev-packages/e2e-tests/test-applications/node-otel/package.json +++ b/dev-packages/e2e-tests/test-applications/node-otel/package.json @@ -13,7 +13,7 @@ "dependencies": { "@opentelemetry/sdk-node": "0.213.0", "@opentelemetry/exporter-trace-otlp-http": "0.213.0", - "@sentry/node": "latest || *", + "@sentry/node": "file:../../packed/sentry-node-packed.tgz", "@types/express": "4.17.17", "@types/node": "^18.19.1", "express": "^4.21.2", diff --git a/dev-packages/e2e-tests/test-applications/node-profiling-cjs/.npmrc b/dev-packages/e2e-tests/test-applications/node-profiling-cjs/.npmrc deleted file mode 100644 index 949fbddc2343..000000000000 --- a/dev-packages/e2e-tests/test-applications/node-profiling-cjs/.npmrc +++ /dev/null @@ -1,2 +0,0 @@ -# @sentry:registry=http://127.0.0.1:4873 -# @sentry-internal:registry=http://127.0.0.1:4873 diff --git a/dev-packages/e2e-tests/test-applications/node-profiling-cjs/package.json b/dev-packages/e2e-tests/test-applications/node-profiling-cjs/package.json index d217090e80fb..b136ea49dd4c 100644 --- a/dev-packages/e2e-tests/test-applications/node-profiling-cjs/package.json +++ b/dev-packages/e2e-tests/test-applications/node-profiling-cjs/package.json @@ -11,8 +11,8 @@ }, "dependencies": { "@playwright/test": "~1.56.0", - "@sentry/node": "latest || *", - "@sentry/profiling-node": "latest || *", + "@sentry/node": "file:../../packed/sentry-node-packed.tgz", + "@sentry/profiling-node": "file:../../packed/sentry-profiling-node-packed.tgz", "@types/node": "^18.19.1", "esbuild": "0.25.0", "typescript": "^5.7.3" diff --git a/dev-packages/e2e-tests/test-applications/node-profiling-electron/.npmrc b/dev-packages/e2e-tests/test-applications/node-profiling-electron/.npmrc deleted file mode 100644 index 949fbddc2343..000000000000 --- a/dev-packages/e2e-tests/test-applications/node-profiling-electron/.npmrc +++ /dev/null @@ -1,2 +0,0 @@ -# @sentry:registry=http://127.0.0.1:4873 -# @sentry-internal:registry=http://127.0.0.1:4873 diff --git a/dev-packages/e2e-tests/test-applications/node-profiling-electron/package.json b/dev-packages/e2e-tests/test-applications/node-profiling-electron/package.json index 5e87a3ca8002..c8b6c167396b 100644 --- a/dev-packages/e2e-tests/test-applications/node-profiling-electron/package.json +++ b/dev-packages/e2e-tests/test-applications/node-profiling-electron/package.json @@ -11,8 +11,8 @@ "@electron/rebuild": "^3.7.0", "@playwright/test": "~1.56.0", "@sentry/electron": "latest || *", - "@sentry/node": "latest || *", - "@sentry/profiling-node": "latest || *", + "@sentry/node": "file:../../packed/sentry-node-packed.tgz", + "@sentry/profiling-node": "file:../../packed/sentry-profiling-node-packed.tgz", "electron": "^33.2.0" }, "volta": { diff --git a/dev-packages/e2e-tests/test-applications/node-profiling-esm/.npmrc b/dev-packages/e2e-tests/test-applications/node-profiling-esm/.npmrc deleted file mode 100644 index 949fbddc2343..000000000000 --- a/dev-packages/e2e-tests/test-applications/node-profiling-esm/.npmrc +++ /dev/null @@ -1,2 +0,0 @@ -# @sentry:registry=http://127.0.0.1:4873 -# @sentry-internal:registry=http://127.0.0.1:4873 diff --git a/dev-packages/e2e-tests/test-applications/node-profiling-esm/package.json b/dev-packages/e2e-tests/test-applications/node-profiling-esm/package.json index fb1f3bf055b8..c7e5c39b9807 100644 --- a/dev-packages/e2e-tests/test-applications/node-profiling-esm/package.json +++ b/dev-packages/e2e-tests/test-applications/node-profiling-esm/package.json @@ -11,8 +11,8 @@ }, "dependencies": { "@playwright/test": "~1.56.0", - "@sentry/node": "latest || *", - "@sentry/profiling-node": "latest || *", + "@sentry/node": "file:../../packed/sentry-node-packed.tgz", + "@sentry/profiling-node": "file:../../packed/sentry-profiling-node-packed.tgz", "@types/node": "^18.19.1", "esbuild": "0.25.0", "typescript": "^5.7.3" diff --git a/dev-packages/e2e-tests/test-applications/nuxt-3-dynamic-import/.npmrc b/dev-packages/e2e-tests/test-applications/nuxt-3-dynamic-import/.npmrc deleted file mode 100644 index 070f80f05092..000000000000 --- a/dev-packages/e2e-tests/test-applications/nuxt-3-dynamic-import/.npmrc +++ /dev/null @@ -1,2 +0,0 @@ -@sentry:registry=http://127.0.0.1:4873 -@sentry-internal:registry=http://127.0.0.1:4873 diff --git a/dev-packages/e2e-tests/test-applications/nuxt-3-dynamic-import/package.json b/dev-packages/e2e-tests/test-applications/nuxt-3-dynamic-import/package.json index e146587fd08e..a61e1da1bdcd 100644 --- a/dev-packages/e2e-tests/test-applications/nuxt-3-dynamic-import/package.json +++ b/dev-packages/e2e-tests/test-applications/nuxt-3-dynamic-import/package.json @@ -14,7 +14,7 @@ "test:assert": "pnpm test" }, "dependencies": { - "@sentry/nuxt": "latest || *", + "@sentry/nuxt": "file:../../packed/sentry-nuxt-packed.tgz", "nuxt": "^3.14.0" }, "devDependencies": { diff --git a/dev-packages/e2e-tests/test-applications/nuxt-3-min/.npmrc b/dev-packages/e2e-tests/test-applications/nuxt-3-min/.npmrc deleted file mode 100644 index 070f80f05092..000000000000 --- a/dev-packages/e2e-tests/test-applications/nuxt-3-min/.npmrc +++ /dev/null @@ -1,2 +0,0 @@ -@sentry:registry=http://127.0.0.1:4873 -@sentry-internal:registry=http://127.0.0.1:4873 diff --git a/dev-packages/e2e-tests/test-applications/nuxt-3-min/package.json b/dev-packages/e2e-tests/test-applications/nuxt-3-min/package.json index 8160b472f57e..73b0c59e8a24 100644 --- a/dev-packages/e2e-tests/test-applications/nuxt-3-min/package.json +++ b/dev-packages/e2e-tests/test-applications/nuxt-3-min/package.json @@ -16,7 +16,7 @@ "test:assert": "pnpm test" }, "dependencies": { - "@sentry/nuxt": "latest || *", + "@sentry/nuxt": "file:../../packed/sentry-nuxt-packed.tgz", "nuxt": "3.7.0", "vue": "3.3.4", "vue-router": "4.2.4" diff --git a/dev-packages/e2e-tests/test-applications/nuxt-3-top-level-import/.npmrc b/dev-packages/e2e-tests/test-applications/nuxt-3-top-level-import/.npmrc deleted file mode 100644 index 070f80f05092..000000000000 --- a/dev-packages/e2e-tests/test-applications/nuxt-3-top-level-import/.npmrc +++ /dev/null @@ -1,2 +0,0 @@ -@sentry:registry=http://127.0.0.1:4873 -@sentry-internal:registry=http://127.0.0.1:4873 diff --git a/dev-packages/e2e-tests/test-applications/nuxt-3-top-level-import/package.json b/dev-packages/e2e-tests/test-applications/nuxt-3-top-level-import/package.json index 9e6fedb17838..21acb5644735 100644 --- a/dev-packages/e2e-tests/test-applications/nuxt-3-top-level-import/package.json +++ b/dev-packages/e2e-tests/test-applications/nuxt-3-top-level-import/package.json @@ -14,8 +14,8 @@ "test:assert": "pnpm test" }, "dependencies": { - "@sentry/nuxt": "latest || *", - "@sentry/core": "latest || *", + "@sentry/nuxt": "file:../../packed/sentry-nuxt-packed.tgz", + "@sentry/core": "file:../../packed/sentry-core-packed.tgz", "nuxt": "^3.14.0" }, "devDependencies": { diff --git a/dev-packages/e2e-tests/test-applications/nuxt-3/.npmrc b/dev-packages/e2e-tests/test-applications/nuxt-3/.npmrc deleted file mode 100644 index 070f80f05092..000000000000 --- a/dev-packages/e2e-tests/test-applications/nuxt-3/.npmrc +++ /dev/null @@ -1,2 +0,0 @@ -@sentry:registry=http://127.0.0.1:4873 -@sentry-internal:registry=http://127.0.0.1:4873 diff --git a/dev-packages/e2e-tests/test-applications/nuxt-3/package.json b/dev-packages/e2e-tests/test-applications/nuxt-3/package.json index 2a2ab10334f1..b7481e044b3e 100644 --- a/dev-packages/e2e-tests/test-applications/nuxt-3/package.json +++ b/dev-packages/e2e-tests/test-applications/nuxt-3/package.json @@ -16,8 +16,8 @@ "test:assert": "pnpm test" }, "dependencies": { - "@sentry/nuxt": "latest || *", - "@sentry/core": "latest || *", + "@sentry/nuxt": "file:../../packed/sentry-nuxt-packed.tgz", + "@sentry/core": "file:../../packed/sentry-core-packed.tgz", "nuxt": "^3.14.0" }, "devDependencies": { diff --git a/dev-packages/e2e-tests/test-applications/nuxt-4/.npmrc b/dev-packages/e2e-tests/test-applications/nuxt-4/.npmrc deleted file mode 100644 index 070f80f05092..000000000000 --- a/dev-packages/e2e-tests/test-applications/nuxt-4/.npmrc +++ /dev/null @@ -1,2 +0,0 @@ -@sentry:registry=http://127.0.0.1:4873 -@sentry-internal:registry=http://127.0.0.1:4873 diff --git a/dev-packages/e2e-tests/test-applications/nuxt-4/package.json b/dev-packages/e2e-tests/test-applications/nuxt-4/package.json index 3f25ef7df0e4..02477111483d 100644 --- a/dev-packages/e2e-tests/test-applications/nuxt-4/package.json +++ b/dev-packages/e2e-tests/test-applications/nuxt-4/package.json @@ -19,7 +19,7 @@ }, "dependencies": { "@pinia/nuxt": "^0.5.5", - "@sentry/nuxt": "latest || *", + "@sentry/nuxt": "file:../../packed/sentry-nuxt-packed.tgz", "nuxt": "^4.1.2" }, "devDependencies": { diff --git a/dev-packages/e2e-tests/test-applications/nuxt-5/.npmrc b/dev-packages/e2e-tests/test-applications/nuxt-5/.npmrc deleted file mode 100644 index 070f80f05092..000000000000 --- a/dev-packages/e2e-tests/test-applications/nuxt-5/.npmrc +++ /dev/null @@ -1,2 +0,0 @@ -@sentry:registry=http://127.0.0.1:4873 -@sentry-internal:registry=http://127.0.0.1:4873 diff --git a/dev-packages/e2e-tests/test-applications/nuxt-5/package.json b/dev-packages/e2e-tests/test-applications/nuxt-5/package.json index aa8296bc3314..bff128f66ed9 100644 --- a/dev-packages/e2e-tests/test-applications/nuxt-5/package.json +++ b/dev-packages/e2e-tests/test-applications/nuxt-5/package.json @@ -23,7 +23,7 @@ ], "dependencies": { "@pinia/nuxt": "^0.11.3", - "@sentry/nuxt": "latest || *", + "@sentry/nuxt": "file:../../packed/sentry-nuxt-packed.tgz", "nitro": "latest", "nuxt": "npm:nuxt-nightly@5x" }, diff --git a/dev-packages/e2e-tests/test-applications/react-17/.npmrc b/dev-packages/e2e-tests/test-applications/react-17/.npmrc deleted file mode 100644 index 070f80f05092..000000000000 --- a/dev-packages/e2e-tests/test-applications/react-17/.npmrc +++ /dev/null @@ -1,2 +0,0 @@ -@sentry:registry=http://127.0.0.1:4873 -@sentry-internal:registry=http://127.0.0.1:4873 diff --git a/dev-packages/e2e-tests/test-applications/react-17/package.json b/dev-packages/e2e-tests/test-applications/react-17/package.json index 7c11ea63d039..1982fffe7a53 100644 --- a/dev-packages/e2e-tests/test-applications/react-17/package.json +++ b/dev-packages/e2e-tests/test-applications/react-17/package.json @@ -3,7 +3,7 @@ "version": "0.1.0", "private": true, "dependencies": { - "@sentry/react": "latest || *", + "@sentry/react": "file:../../packed/sentry-react-packed.tgz", "@types/react": "17.0.2", "@types/react-dom": "17.0.2", "react": "17.0.2", diff --git a/dev-packages/e2e-tests/test-applications/react-19/.npmrc b/dev-packages/e2e-tests/test-applications/react-19/.npmrc deleted file mode 100644 index 070f80f05092..000000000000 --- a/dev-packages/e2e-tests/test-applications/react-19/.npmrc +++ /dev/null @@ -1,2 +0,0 @@ -@sentry:registry=http://127.0.0.1:4873 -@sentry-internal:registry=http://127.0.0.1:4873 diff --git a/dev-packages/e2e-tests/test-applications/react-19/package.json b/dev-packages/e2e-tests/test-applications/react-19/package.json index 08ef823e2bfe..f3a776a03924 100644 --- a/dev-packages/e2e-tests/test-applications/react-19/package.json +++ b/dev-packages/e2e-tests/test-applications/react-19/package.json @@ -3,7 +3,7 @@ "version": "0.1.0", "private": true, "dependencies": { - "@sentry/react": "latest || *", + "@sentry/react": "file:../../packed/sentry-react-packed.tgz", "history": "4.9.0", "@types/history": "4.7.11", "@types/node": "^18.19.1", diff --git a/dev-packages/e2e-tests/test-applications/react-create-browser-router/.npmrc b/dev-packages/e2e-tests/test-applications/react-create-browser-router/.npmrc deleted file mode 100644 index 070f80f05092..000000000000 --- a/dev-packages/e2e-tests/test-applications/react-create-browser-router/.npmrc +++ /dev/null @@ -1,2 +0,0 @@ -@sentry:registry=http://127.0.0.1:4873 -@sentry-internal:registry=http://127.0.0.1:4873 diff --git a/dev-packages/e2e-tests/test-applications/react-create-browser-router/package.json b/dev-packages/e2e-tests/test-applications/react-create-browser-router/package.json index 8bb20583f5aa..5a0b7d847162 100644 --- a/dev-packages/e2e-tests/test-applications/react-create-browser-router/package.json +++ b/dev-packages/e2e-tests/test-applications/react-create-browser-router/package.json @@ -3,7 +3,7 @@ "version": "0.1.0", "private": true, "dependencies": { - "@sentry/react": "latest || *", + "@sentry/react": "file:../../packed/sentry-react-packed.tgz", "@types/node": "^18.19.1", "@types/react": "18.0.0", "@types/react-dom": "18.0.0", diff --git a/dev-packages/e2e-tests/test-applications/react-create-hash-router/.npmrc b/dev-packages/e2e-tests/test-applications/react-create-hash-router/.npmrc deleted file mode 100644 index 070f80f05092..000000000000 --- a/dev-packages/e2e-tests/test-applications/react-create-hash-router/.npmrc +++ /dev/null @@ -1,2 +0,0 @@ -@sentry:registry=http://127.0.0.1:4873 -@sentry-internal:registry=http://127.0.0.1:4873 diff --git a/dev-packages/e2e-tests/test-applications/react-create-hash-router/package.json b/dev-packages/e2e-tests/test-applications/react-create-hash-router/package.json index acbf8d00ef2d..dbe32bdcf506 100644 --- a/dev-packages/e2e-tests/test-applications/react-create-hash-router/package.json +++ b/dev-packages/e2e-tests/test-applications/react-create-hash-router/package.json @@ -3,7 +3,7 @@ "version": "0.1.0", "private": true, "dependencies": { - "@sentry/react": "latest || *", + "@sentry/react": "file:../../packed/sentry-react-packed.tgz", "@types/node": "^18.19.1", "@types/react": "18.0.0", "@types/react-dom": "18.0.0", diff --git a/dev-packages/e2e-tests/test-applications/react-create-memory-router/.npmrc b/dev-packages/e2e-tests/test-applications/react-create-memory-router/.npmrc deleted file mode 100644 index 070f80f05092..000000000000 --- a/dev-packages/e2e-tests/test-applications/react-create-memory-router/.npmrc +++ /dev/null @@ -1,2 +0,0 @@ -@sentry:registry=http://127.0.0.1:4873 -@sentry-internal:registry=http://127.0.0.1:4873 diff --git a/dev-packages/e2e-tests/test-applications/react-create-memory-router/package.json b/dev-packages/e2e-tests/test-applications/react-create-memory-router/package.json index 119fde33b8e8..c4415f3433ed 100644 --- a/dev-packages/e2e-tests/test-applications/react-create-memory-router/package.json +++ b/dev-packages/e2e-tests/test-applications/react-create-memory-router/package.json @@ -3,7 +3,7 @@ "version": "0.1.0", "private": true, "dependencies": { - "@sentry/react": "latest || *", + "@sentry/react": "file:../../packed/sentry-react-packed.tgz", "@types/node": "^18.19.1", "@types/react": "18.0.0", "@types/react-dom": "18.0.0", diff --git a/dev-packages/e2e-tests/test-applications/react-router-5/.npmrc b/dev-packages/e2e-tests/test-applications/react-router-5/.npmrc deleted file mode 100644 index 070f80f05092..000000000000 --- a/dev-packages/e2e-tests/test-applications/react-router-5/.npmrc +++ /dev/null @@ -1,2 +0,0 @@ -@sentry:registry=http://127.0.0.1:4873 -@sentry-internal:registry=http://127.0.0.1:4873 diff --git a/dev-packages/e2e-tests/test-applications/react-router-5/package.json b/dev-packages/e2e-tests/test-applications/react-router-5/package.json index 973d87e057e5..8dcb32a24721 100644 --- a/dev-packages/e2e-tests/test-applications/react-router-5/package.json +++ b/dev-packages/e2e-tests/test-applications/react-router-5/package.json @@ -3,7 +3,7 @@ "version": "0.1.0", "private": true, "dependencies": { - "@sentry/react": "latest || *", + "@sentry/react": "file:../../packed/sentry-react-packed.tgz", "history": "4.9.0", "@types/history": "4.7.11", "@types/node": "^18.19.1", diff --git a/dev-packages/e2e-tests/test-applications/react-router-6-descendant-routes/.npmrc b/dev-packages/e2e-tests/test-applications/react-router-6-descendant-routes/.npmrc deleted file mode 100644 index 070f80f05092..000000000000 --- a/dev-packages/e2e-tests/test-applications/react-router-6-descendant-routes/.npmrc +++ /dev/null @@ -1,2 +0,0 @@ -@sentry:registry=http://127.0.0.1:4873 -@sentry-internal:registry=http://127.0.0.1:4873 diff --git a/dev-packages/e2e-tests/test-applications/react-router-6-descendant-routes/package.json b/dev-packages/e2e-tests/test-applications/react-router-6-descendant-routes/package.json index 73b0eb2e0d8b..1457efba945e 100644 --- a/dev-packages/e2e-tests/test-applications/react-router-6-descendant-routes/package.json +++ b/dev-packages/e2e-tests/test-applications/react-router-6-descendant-routes/package.json @@ -3,7 +3,7 @@ "version": "0.1.0", "private": true, "dependencies": { - "@sentry/react": "latest || *", + "@sentry/react": "file:../../packed/sentry-react-packed.tgz", "@types/react": "18.0.0", "@types/react-dom": "18.0.0", "express": "^4.21.2", diff --git a/dev-packages/e2e-tests/test-applications/react-router-6-use-routes/.npmrc b/dev-packages/e2e-tests/test-applications/react-router-6-use-routes/.npmrc deleted file mode 100644 index 070f80f05092..000000000000 --- a/dev-packages/e2e-tests/test-applications/react-router-6-use-routes/.npmrc +++ /dev/null @@ -1,2 +0,0 @@ -@sentry:registry=http://127.0.0.1:4873 -@sentry-internal:registry=http://127.0.0.1:4873 diff --git a/dev-packages/e2e-tests/test-applications/react-router-6-use-routes/package.json b/dev-packages/e2e-tests/test-applications/react-router-6-use-routes/package.json index fe4494775753..128c4967554c 100644 --- a/dev-packages/e2e-tests/test-applications/react-router-6-use-routes/package.json +++ b/dev-packages/e2e-tests/test-applications/react-router-6-use-routes/package.json @@ -3,7 +3,7 @@ "version": "0.1.0", "private": true, "dependencies": { - "@sentry/react": "latest || *", + "@sentry/react": "file:../../packed/sentry-react-packed.tgz", "@types/react": "18.0.0", "@types/react-dom": "18.0.0", "react": "18.2.0", diff --git a/dev-packages/e2e-tests/test-applications/react-router-6/.npmrc b/dev-packages/e2e-tests/test-applications/react-router-6/.npmrc deleted file mode 100644 index 070f80f05092..000000000000 --- a/dev-packages/e2e-tests/test-applications/react-router-6/.npmrc +++ /dev/null @@ -1,2 +0,0 @@ -@sentry:registry=http://127.0.0.1:4873 -@sentry-internal:registry=http://127.0.0.1:4873 diff --git a/dev-packages/e2e-tests/test-applications/react-router-6/package.json b/dev-packages/e2e-tests/test-applications/react-router-6/package.json index 228705bb6493..2d84c95d58f1 100644 --- a/dev-packages/e2e-tests/test-applications/react-router-6/package.json +++ b/dev-packages/e2e-tests/test-applications/react-router-6/package.json @@ -3,7 +3,7 @@ "version": "0.1.0", "private": true, "dependencies": { - "@sentry/react": "latest || *", + "@sentry/react": "file:../../packed/sentry-react-packed.tgz", "@types/react": "18.0.0", "@types/react-dom": "18.0.0", "express": "^4.21.2", diff --git a/dev-packages/e2e-tests/test-applications/react-router-7-cross-usage/.npmrc b/dev-packages/e2e-tests/test-applications/react-router-7-cross-usage/.npmrc deleted file mode 100644 index 070f80f05092..000000000000 --- a/dev-packages/e2e-tests/test-applications/react-router-7-cross-usage/.npmrc +++ /dev/null @@ -1,2 +0,0 @@ -@sentry:registry=http://127.0.0.1:4873 -@sentry-internal:registry=http://127.0.0.1:4873 diff --git a/dev-packages/e2e-tests/test-applications/react-router-7-cross-usage/package.json b/dev-packages/e2e-tests/test-applications/react-router-7-cross-usage/package.json index 0c312ea52926..586fbccee112 100644 --- a/dev-packages/e2e-tests/test-applications/react-router-7-cross-usage/package.json +++ b/dev-packages/e2e-tests/test-applications/react-router-7-cross-usage/package.json @@ -3,7 +3,7 @@ "version": "0.1.0", "private": true, "dependencies": { - "@sentry/react": "latest || *", + "@sentry/react": "file:../../packed/sentry-react-packed.tgz", "@types/react": "18.0.0", "@types/react-dom": "18.0.0", "express": "^4.21.2", diff --git a/dev-packages/e2e-tests/test-applications/react-router-7-framework-custom/.npmrc b/dev-packages/e2e-tests/test-applications/react-router-7-framework-custom/.npmrc deleted file mode 100644 index 070f80f05092..000000000000 --- a/dev-packages/e2e-tests/test-applications/react-router-7-framework-custom/.npmrc +++ /dev/null @@ -1,2 +0,0 @@ -@sentry:registry=http://127.0.0.1:4873 -@sentry-internal:registry=http://127.0.0.1:4873 diff --git a/dev-packages/e2e-tests/test-applications/react-router-7-framework-custom/package.json b/dev-packages/e2e-tests/test-applications/react-router-7-framework-custom/package.json index 8a9d6ac5656d..20fdccf46f4c 100644 --- a/dev-packages/e2e-tests/test-applications/react-router-7-framework-custom/package.json +++ b/dev-packages/e2e-tests/test-applications/react-router-7-framework-custom/package.json @@ -9,7 +9,7 @@ "react-router": "^7.13.0", "@react-router/node": "^7.13.0", "@react-router/serve": "^7.13.0", - "@sentry/react-router": "latest || *", + "@sentry/react-router": "file:../../packed/sentry-react-router-packed.tgz", "isbot": "^5.1.17" }, "devDependencies": { diff --git a/dev-packages/e2e-tests/test-applications/react-router-7-framework-instrumentation/.npmrc b/dev-packages/e2e-tests/test-applications/react-router-7-framework-instrumentation/.npmrc deleted file mode 100644 index 070f80f05092..000000000000 --- a/dev-packages/e2e-tests/test-applications/react-router-7-framework-instrumentation/.npmrc +++ /dev/null @@ -1,2 +0,0 @@ -@sentry:registry=http://127.0.0.1:4873 -@sentry-internal:registry=http://127.0.0.1:4873 diff --git a/dev-packages/e2e-tests/test-applications/react-router-7-framework-instrumentation/package.json b/dev-packages/e2e-tests/test-applications/react-router-7-framework-instrumentation/package.json index 9666bf218893..b7e2fd8de655 100644 --- a/dev-packages/e2e-tests/test-applications/react-router-7-framework-instrumentation/package.json +++ b/dev-packages/e2e-tests/test-applications/react-router-7-framework-instrumentation/package.json @@ -6,7 +6,7 @@ "dependencies": { "@react-router/node": "latest", "@react-router/serve": "latest", - "@sentry/react-router": "latest || *", + "@sentry/react-router": "file:../../packed/sentry-react-router-packed.tgz", "isbot": "^5.1.17", "react": "^18.3.1", "react-dom": "^18.3.1", diff --git a/dev-packages/e2e-tests/test-applications/react-router-7-framework-node-20-18/.npmrc b/dev-packages/e2e-tests/test-applications/react-router-7-framework-node-20-18/.npmrc deleted file mode 100644 index 070f80f05092..000000000000 --- a/dev-packages/e2e-tests/test-applications/react-router-7-framework-node-20-18/.npmrc +++ /dev/null @@ -1,2 +0,0 @@ -@sentry:registry=http://127.0.0.1:4873 -@sentry-internal:registry=http://127.0.0.1:4873 diff --git a/dev-packages/e2e-tests/test-applications/react-router-7-framework-node-20-18/package.json b/dev-packages/e2e-tests/test-applications/react-router-7-framework-node-20-18/package.json index e69f23c0630a..65f4a96b0165 100644 --- a/dev-packages/e2e-tests/test-applications/react-router-7-framework-node-20-18/package.json +++ b/dev-packages/e2e-tests/test-applications/react-router-7-framework-node-20-18/package.json @@ -9,7 +9,7 @@ "react-router": "7.13.0", "@react-router/node": "7.13.0", "@react-router/serve": "7.13.0", - "@sentry/react-router": "latest || *", + "@sentry/react-router": "file:../../packed/sentry-react-router-packed.tgz", "isbot": "^5.1.17" }, "devDependencies": { diff --git a/dev-packages/e2e-tests/test-applications/react-router-7-framework-spa-node-20-18/.npmrc b/dev-packages/e2e-tests/test-applications/react-router-7-framework-spa-node-20-18/.npmrc deleted file mode 100644 index 070f80f05092..000000000000 --- a/dev-packages/e2e-tests/test-applications/react-router-7-framework-spa-node-20-18/.npmrc +++ /dev/null @@ -1,2 +0,0 @@ -@sentry:registry=http://127.0.0.1:4873 -@sentry-internal:registry=http://127.0.0.1:4873 diff --git a/dev-packages/e2e-tests/test-applications/react-router-7-framework-spa-node-20-18/package.json b/dev-packages/e2e-tests/test-applications/react-router-7-framework-spa-node-20-18/package.json index 663e85a53963..56c4b7d052d7 100644 --- a/dev-packages/e2e-tests/test-applications/react-router-7-framework-spa-node-20-18/package.json +++ b/dev-packages/e2e-tests/test-applications/react-router-7-framework-spa-node-20-18/package.json @@ -17,7 +17,7 @@ "test:assert": "pnpm test:ts &&pnpm test:prod" }, "dependencies": { - "@sentry/react-router": "latest || *", + "@sentry/react-router": "file:../../packed/sentry-react-router-packed.tgz", "@react-router/node": "7.13.0", "@react-router/serve": "7.13.0", "isbot": "^5.1.27", diff --git a/dev-packages/e2e-tests/test-applications/react-router-7-framework-spa/.npmrc b/dev-packages/e2e-tests/test-applications/react-router-7-framework-spa/.npmrc deleted file mode 100644 index 070f80f05092..000000000000 --- a/dev-packages/e2e-tests/test-applications/react-router-7-framework-spa/.npmrc +++ /dev/null @@ -1,2 +0,0 @@ -@sentry:registry=http://127.0.0.1:4873 -@sentry-internal:registry=http://127.0.0.1:4873 diff --git a/dev-packages/e2e-tests/test-applications/react-router-7-framework-spa/package.json b/dev-packages/e2e-tests/test-applications/react-router-7-framework-spa/package.json index e5c375174793..28f189bcd1f3 100644 --- a/dev-packages/e2e-tests/test-applications/react-router-7-framework-spa/package.json +++ b/dev-packages/e2e-tests/test-applications/react-router-7-framework-spa/package.json @@ -17,7 +17,7 @@ "test:assert": "pnpm test:ts &&pnpm test:prod" }, "dependencies": { - "@sentry/react-router": "latest || *", + "@sentry/react-router": "file:../../packed/sentry-react-router-packed.tgz", "@react-router/node": "^7.13.0", "@react-router/serve": "^7.13.0", "isbot": "^5.1.27", diff --git a/dev-packages/e2e-tests/test-applications/react-router-7-framework/.npmrc b/dev-packages/e2e-tests/test-applications/react-router-7-framework/.npmrc deleted file mode 100644 index 070f80f05092..000000000000 --- a/dev-packages/e2e-tests/test-applications/react-router-7-framework/.npmrc +++ /dev/null @@ -1,2 +0,0 @@ -@sentry:registry=http://127.0.0.1:4873 -@sentry-internal:registry=http://127.0.0.1:4873 diff --git a/dev-packages/e2e-tests/test-applications/react-router-7-framework/package.json b/dev-packages/e2e-tests/test-applications/react-router-7-framework/package.json index fbd49881f521..fde0e1699d6a 100644 --- a/dev-packages/e2e-tests/test-applications/react-router-7-framework/package.json +++ b/dev-packages/e2e-tests/test-applications/react-router-7-framework/package.json @@ -9,7 +9,7 @@ "react-router": "^7.13.0", "@react-router/node": "^7.13.0", "@react-router/serve": "^7.13.0", - "@sentry/react-router": "latest || *", + "@sentry/react-router": "file:../../packed/sentry-react-router-packed.tgz", "isbot": "^5.1.17" }, "devDependencies": { diff --git a/dev-packages/e2e-tests/test-applications/react-router-7-lazy-routes/.npmrc b/dev-packages/e2e-tests/test-applications/react-router-7-lazy-routes/.npmrc deleted file mode 100644 index 070f80f05092..000000000000 --- a/dev-packages/e2e-tests/test-applications/react-router-7-lazy-routes/.npmrc +++ /dev/null @@ -1,2 +0,0 @@ -@sentry:registry=http://127.0.0.1:4873 -@sentry-internal:registry=http://127.0.0.1:4873 diff --git a/dev-packages/e2e-tests/test-applications/react-router-7-lazy-routes/package.json b/dev-packages/e2e-tests/test-applications/react-router-7-lazy-routes/package.json index afd2a32834aa..9e649c11afbe 100644 --- a/dev-packages/e2e-tests/test-applications/react-router-7-lazy-routes/package.json +++ b/dev-packages/e2e-tests/test-applications/react-router-7-lazy-routes/package.json @@ -3,7 +3,7 @@ "version": "0.1.0", "private": true, "dependencies": { - "@sentry/react": "latest || *", + "@sentry/react": "file:../../packed/sentry-react-packed.tgz", "@types/react": "18.0.0", "@types/react-dom": "18.0.0", "express": "^4.21.2", diff --git a/dev-packages/e2e-tests/test-applications/react-router-7-spa/.npmrc b/dev-packages/e2e-tests/test-applications/react-router-7-spa/.npmrc deleted file mode 100644 index 070f80f05092..000000000000 --- a/dev-packages/e2e-tests/test-applications/react-router-7-spa/.npmrc +++ /dev/null @@ -1,2 +0,0 @@ -@sentry:registry=http://127.0.0.1:4873 -@sentry-internal:registry=http://127.0.0.1:4873 diff --git a/dev-packages/e2e-tests/test-applications/react-router-7-spa/package.json b/dev-packages/e2e-tests/test-applications/react-router-7-spa/package.json index 0cb3c988dfc2..c792359c5a3f 100644 --- a/dev-packages/e2e-tests/test-applications/react-router-7-spa/package.json +++ b/dev-packages/e2e-tests/test-applications/react-router-7-spa/package.json @@ -3,7 +3,7 @@ "version": "0.1.0", "private": true, "dependencies": { - "@sentry/react": "latest || *", + "@sentry/react": "file:../../packed/sentry-react-packed.tgz", "@types/react": "18.3.1", "@types/react-dom": "18.3.1", "react": "18.3.1", diff --git a/dev-packages/e2e-tests/test-applications/react-send-to-sentry/.npmrc b/dev-packages/e2e-tests/test-applications/react-send-to-sentry/.npmrc deleted file mode 100644 index 070f80f05092..000000000000 --- a/dev-packages/e2e-tests/test-applications/react-send-to-sentry/.npmrc +++ /dev/null @@ -1,2 +0,0 @@ -@sentry:registry=http://127.0.0.1:4873 -@sentry-internal:registry=http://127.0.0.1:4873 diff --git a/dev-packages/e2e-tests/test-applications/react-send-to-sentry/package.json b/dev-packages/e2e-tests/test-applications/react-send-to-sentry/package.json index a256cd9f29ae..b5958cefd6f7 100644 --- a/dev-packages/e2e-tests/test-applications/react-send-to-sentry/package.json +++ b/dev-packages/e2e-tests/test-applications/react-send-to-sentry/package.json @@ -3,7 +3,7 @@ "version": "0.1.0", "private": true, "dependencies": { - "@sentry/react": "latest || *", + "@sentry/react": "file:../../packed/sentry-react-packed.tgz", "@types/node": "^18.19.1", "@types/react": "18.0.0", "@types/react-dom": "18.0.0", diff --git a/dev-packages/e2e-tests/test-applications/remix-hydrogen/.npmrc b/dev-packages/e2e-tests/test-applications/remix-hydrogen/.npmrc deleted file mode 100644 index 070f80f05092..000000000000 --- a/dev-packages/e2e-tests/test-applications/remix-hydrogen/.npmrc +++ /dev/null @@ -1,2 +0,0 @@ -@sentry:registry=http://127.0.0.1:4873 -@sentry-internal:registry=http://127.0.0.1:4873 diff --git a/dev-packages/e2e-tests/test-applications/remix-hydrogen/package.json b/dev-packages/e2e-tests/test-applications/remix-hydrogen/package.json index 8a35edaca3fb..b51c2868f415 100644 --- a/dev-packages/e2e-tests/test-applications/remix-hydrogen/package.json +++ b/dev-packages/e2e-tests/test-applications/remix-hydrogen/package.json @@ -17,8 +17,8 @@ "@remix-run/react": "^2.17.4", "@remix-run/server-runtime": "^2.17.4", "@remix-run/cloudflare-pages": "^2.17.4", - "@sentry/cloudflare": "latest || *", - "@sentry/remix": "latest || *", + "@sentry/cloudflare": "file:../../packed/sentry-cloudflare-packed.tgz", + "@sentry/remix": "file:../../packed/sentry-remix-packed.tgz", "@sentry/vite-plugin": "^5.2.0", "@shopify/hydrogen": "2025.4.0", "@shopify/remix-oxygen": "2.0.10", diff --git a/dev-packages/e2e-tests/test-applications/remix-server-timing/.npmrc b/dev-packages/e2e-tests/test-applications/remix-server-timing/.npmrc deleted file mode 100644 index 070f80f05092..000000000000 --- a/dev-packages/e2e-tests/test-applications/remix-server-timing/.npmrc +++ /dev/null @@ -1,2 +0,0 @@ -@sentry:registry=http://127.0.0.1:4873 -@sentry-internal:registry=http://127.0.0.1:4873 diff --git a/dev-packages/e2e-tests/test-applications/remix-server-timing/package.json b/dev-packages/e2e-tests/test-applications/remix-server-timing/package.json index d31e86ff0cdc..fd57d2920d5a 100644 --- a/dev-packages/e2e-tests/test-applications/remix-server-timing/package.json +++ b/dev-packages/e2e-tests/test-applications/remix-server-timing/package.json @@ -11,7 +11,7 @@ "test:assert": "pnpm playwright test" }, "dependencies": { - "@sentry/remix": "latest || *", + "@sentry/remix": "file:../../packed/sentry-remix-packed.tgz", "@remix-run/css-bundle": "2.17.4", "@remix-run/node": "2.17.4", "@remix-run/react": "2.17.4", diff --git a/dev-packages/e2e-tests/test-applications/solid-tanstack-router/.npmrc b/dev-packages/e2e-tests/test-applications/solid-tanstack-router/.npmrc deleted file mode 100644 index 070f80f05092..000000000000 --- a/dev-packages/e2e-tests/test-applications/solid-tanstack-router/.npmrc +++ /dev/null @@ -1,2 +0,0 @@ -@sentry:registry=http://127.0.0.1:4873 -@sentry-internal:registry=http://127.0.0.1:4873 diff --git a/dev-packages/e2e-tests/test-applications/solid-tanstack-router/package.json b/dev-packages/e2e-tests/test-applications/solid-tanstack-router/package.json index 8082add342d1..04a2fd2adeec 100644 --- a/dev-packages/e2e-tests/test-applications/solid-tanstack-router/package.json +++ b/dev-packages/e2e-tests/test-applications/solid-tanstack-router/package.json @@ -13,7 +13,7 @@ "test:assert": "pnpm test:prod" }, "dependencies": { - "@sentry/solid": "latest || *", + "@sentry/solid": "file:../../packed/sentry-solid-packed.tgz", "@tailwindcss/vite": "^4.0.6", "@tanstack/solid-router": "^1.141.8", "@tanstack/solid-router-devtools": "^1.132.25", diff --git a/dev-packages/e2e-tests/test-applications/solid/.npmrc b/dev-packages/e2e-tests/test-applications/solid/.npmrc deleted file mode 100644 index 070f80f05092..000000000000 --- a/dev-packages/e2e-tests/test-applications/solid/.npmrc +++ /dev/null @@ -1,2 +0,0 @@ -@sentry:registry=http://127.0.0.1:4873 -@sentry-internal:registry=http://127.0.0.1:4873 diff --git a/dev-packages/e2e-tests/test-applications/solid/package.json b/dev-packages/e2e-tests/test-applications/solid/package.json index 3551001d5dd1..aa0506110761 100644 --- a/dev-packages/e2e-tests/test-applications/solid/package.json +++ b/dev-packages/e2e-tests/test-applications/solid/package.json @@ -25,7 +25,7 @@ }, "dependencies": { "solid-js": "^1.8.18", - "@sentry/solid": "latest || *" + "@sentry/solid": "file:../../packed/sentry-solid-packed.tgz" }, "volta": { "extends": "../../package.json" diff --git a/dev-packages/e2e-tests/test-applications/solidstart-dynamic-import/.npmrc b/dev-packages/e2e-tests/test-applications/solidstart-dynamic-import/.npmrc deleted file mode 100644 index 070f80f05092..000000000000 --- a/dev-packages/e2e-tests/test-applications/solidstart-dynamic-import/.npmrc +++ /dev/null @@ -1,2 +0,0 @@ -@sentry:registry=http://127.0.0.1:4873 -@sentry-internal:registry=http://127.0.0.1:4873 diff --git a/dev-packages/e2e-tests/test-applications/solidstart-dynamic-import/package.json b/dev-packages/e2e-tests/test-applications/solidstart-dynamic-import/package.json index 1dbca26fc50c..747162d0bd75 100644 --- a/dev-packages/e2e-tests/test-applications/solidstart-dynamic-import/package.json +++ b/dev-packages/e2e-tests/test-applications/solidstart-dynamic-import/package.json @@ -12,7 +12,7 @@ }, "type": "module", "dependencies": { - "@sentry/solidstart": "latest || *" + "@sentry/solidstart": "file:../../packed/sentry-solidstart-packed.tgz" }, "devDependencies": { "@playwright/test": "~1.56.0", diff --git a/dev-packages/e2e-tests/test-applications/solidstart-spa/.npmrc b/dev-packages/e2e-tests/test-applications/solidstart-spa/.npmrc deleted file mode 100644 index 070f80f05092..000000000000 --- a/dev-packages/e2e-tests/test-applications/solidstart-spa/.npmrc +++ /dev/null @@ -1,2 +0,0 @@ -@sentry:registry=http://127.0.0.1:4873 -@sentry-internal:registry=http://127.0.0.1:4873 diff --git a/dev-packages/e2e-tests/test-applications/solidstart-spa/package.json b/dev-packages/e2e-tests/test-applications/solidstart-spa/package.json index cc4549c12c47..a9d1d6b91da3 100644 --- a/dev-packages/e2e-tests/test-applications/solidstart-spa/package.json +++ b/dev-packages/e2e-tests/test-applications/solidstart-spa/package.json @@ -12,7 +12,7 @@ }, "type": "module", "dependencies": { - "@sentry/solidstart": "latest || *" + "@sentry/solidstart": "file:../../packed/sentry-solidstart-packed.tgz" }, "devDependencies": { "@playwright/test": "~1.56.0", diff --git a/dev-packages/e2e-tests/test-applications/solidstart-top-level-import/.npmrc b/dev-packages/e2e-tests/test-applications/solidstart-top-level-import/.npmrc deleted file mode 100644 index 070f80f05092..000000000000 --- a/dev-packages/e2e-tests/test-applications/solidstart-top-level-import/.npmrc +++ /dev/null @@ -1,2 +0,0 @@ -@sentry:registry=http://127.0.0.1:4873 -@sentry-internal:registry=http://127.0.0.1:4873 diff --git a/dev-packages/e2e-tests/test-applications/solidstart-top-level-import/package.json b/dev-packages/e2e-tests/test-applications/solidstart-top-level-import/package.json index 35a20e4e64a7..c97a130c92b1 100644 --- a/dev-packages/e2e-tests/test-applications/solidstart-top-level-import/package.json +++ b/dev-packages/e2e-tests/test-applications/solidstart-top-level-import/package.json @@ -12,7 +12,7 @@ }, "type": "module", "dependencies": { - "@sentry/solidstart": "latest || *" + "@sentry/solidstart": "file:../../packed/sentry-solidstart-packed.tgz" }, "devDependencies": { "@playwright/test": "~1.56.0", diff --git a/dev-packages/e2e-tests/test-applications/solidstart/.npmrc b/dev-packages/e2e-tests/test-applications/solidstart/.npmrc deleted file mode 100644 index 070f80f05092..000000000000 --- a/dev-packages/e2e-tests/test-applications/solidstart/.npmrc +++ /dev/null @@ -1,2 +0,0 @@ -@sentry:registry=http://127.0.0.1:4873 -@sentry-internal:registry=http://127.0.0.1:4873 diff --git a/dev-packages/e2e-tests/test-applications/solidstart/package.json b/dev-packages/e2e-tests/test-applications/solidstart/package.json index bb2f5b85134c..7e382b6dc54b 100644 --- a/dev-packages/e2e-tests/test-applications/solidstart/package.json +++ b/dev-packages/e2e-tests/test-applications/solidstart/package.json @@ -12,7 +12,7 @@ }, "type": "module", "dependencies": { - "@sentry/solidstart": "latest || *" + "@sentry/solidstart": "file:../../packed/sentry-solidstart-packed.tgz" }, "devDependencies": { "@playwright/test": "~1.56.0", diff --git a/dev-packages/e2e-tests/test-applications/supabase-nextjs/.npmrc b/dev-packages/e2e-tests/test-applications/supabase-nextjs/.npmrc deleted file mode 100644 index 070f80f05092..000000000000 --- a/dev-packages/e2e-tests/test-applications/supabase-nextjs/.npmrc +++ /dev/null @@ -1,2 +0,0 @@ -@sentry:registry=http://127.0.0.1:4873 -@sentry-internal:registry=http://127.0.0.1:4873 diff --git a/dev-packages/e2e-tests/test-applications/supabase-nextjs/package.json b/dev-packages/e2e-tests/test-applications/supabase-nextjs/package.json index cb84814fd29a..4b9d9062dca3 100644 --- a/dev-packages/e2e-tests/test-applications/supabase-nextjs/package.json +++ b/dev-packages/e2e-tests/test-applications/supabase-nextjs/package.json @@ -14,7 +14,7 @@ }, "dependencies": { "@next/font": "14.2.15", - "@sentry/nextjs": "latest || *", + "@sentry/nextjs": "file:../../packed/sentry-nextjs-packed.tgz", "@supabase/auth-helpers-react": "0.5.0", "@supabase/auth-ui-react": "0.4.7", "@supabase/supabase-js": "2.49.1", diff --git a/dev-packages/e2e-tests/test-applications/svelte-5/.npmrc b/dev-packages/e2e-tests/test-applications/svelte-5/.npmrc deleted file mode 100644 index 070f80f05092..000000000000 --- a/dev-packages/e2e-tests/test-applications/svelte-5/.npmrc +++ /dev/null @@ -1,2 +0,0 @@ -@sentry:registry=http://127.0.0.1:4873 -@sentry-internal:registry=http://127.0.0.1:4873 diff --git a/dev-packages/e2e-tests/test-applications/svelte-5/package.json b/dev-packages/e2e-tests/test-applications/svelte-5/package.json index 1cfa4f510219..48039e81d8bf 100644 --- a/dev-packages/e2e-tests/test-applications/svelte-5/package.json +++ b/dev-packages/e2e-tests/test-applications/svelte-5/package.json @@ -24,7 +24,7 @@ "vite": "^5.4.11" }, "dependencies": { - "@sentry/svelte": "latest || *" + "@sentry/svelte": "file:../../packed/sentry-svelte-packed.tgz" }, "volta": { "extends": "../../package.json" diff --git a/dev-packages/e2e-tests/test-applications/sveltekit-2-kit-tracing/.npmrc b/dev-packages/e2e-tests/test-applications/sveltekit-2-kit-tracing/.npmrc deleted file mode 100644 index 070f80f05092..000000000000 --- a/dev-packages/e2e-tests/test-applications/sveltekit-2-kit-tracing/.npmrc +++ /dev/null @@ -1,2 +0,0 @@ -@sentry:registry=http://127.0.0.1:4873 -@sentry-internal:registry=http://127.0.0.1:4873 diff --git a/dev-packages/e2e-tests/test-applications/sveltekit-2-kit-tracing/package.json b/dev-packages/e2e-tests/test-applications/sveltekit-2-kit-tracing/package.json index abfd19bfa4ae..162c148d3a86 100644 --- a/dev-packages/e2e-tests/test-applications/sveltekit-2-kit-tracing/package.json +++ b/dev-packages/e2e-tests/test-applications/sveltekit-2-kit-tracing/package.json @@ -15,7 +15,7 @@ "test:assert": "pnpm test:prod" }, "dependencies": { - "@sentry/sveltekit": "latest || *", + "@sentry/sveltekit": "file:../../packed/sentry-sveltekit-packed.tgz", "@spotlightjs/spotlight": "2.0.0-alpha.1" }, "devDependencies": { diff --git a/dev-packages/e2e-tests/test-applications/sveltekit-2-svelte-5/.npmrc b/dev-packages/e2e-tests/test-applications/sveltekit-2-svelte-5/.npmrc deleted file mode 100644 index 070f80f05092..000000000000 --- a/dev-packages/e2e-tests/test-applications/sveltekit-2-svelte-5/.npmrc +++ /dev/null @@ -1,2 +0,0 @@ -@sentry:registry=http://127.0.0.1:4873 -@sentry-internal:registry=http://127.0.0.1:4873 diff --git a/dev-packages/e2e-tests/test-applications/sveltekit-2-svelte-5/package.json b/dev-packages/e2e-tests/test-applications/sveltekit-2-svelte-5/package.json index 6b183ea3ca54..50fd974e98b9 100644 --- a/dev-packages/e2e-tests/test-applications/sveltekit-2-svelte-5/package.json +++ b/dev-packages/e2e-tests/test-applications/sveltekit-2-svelte-5/package.json @@ -15,7 +15,7 @@ "test:assert": "pnpm test:prod" }, "dependencies": { - "@sentry/sveltekit": "latest || *", + "@sentry/sveltekit": "file:../../packed/sentry-sveltekit-packed.tgz", "@spotlightjs/spotlight": "2.0.0-alpha.1" }, "devDependencies": { diff --git a/dev-packages/e2e-tests/test-applications/sveltekit-2.5.0-twp/.npmrc b/dev-packages/e2e-tests/test-applications/sveltekit-2.5.0-twp/.npmrc deleted file mode 100644 index 070f80f05092..000000000000 --- a/dev-packages/e2e-tests/test-applications/sveltekit-2.5.0-twp/.npmrc +++ /dev/null @@ -1,2 +0,0 @@ -@sentry:registry=http://127.0.0.1:4873 -@sentry-internal:registry=http://127.0.0.1:4873 diff --git a/dev-packages/e2e-tests/test-applications/sveltekit-2.5.0-twp/package.json b/dev-packages/e2e-tests/test-applications/sveltekit-2.5.0-twp/package.json index fc691d4acebf..23f059eaee43 100644 --- a/dev-packages/e2e-tests/test-applications/sveltekit-2.5.0-twp/package.json +++ b/dev-packages/e2e-tests/test-applications/sveltekit-2.5.0-twp/package.json @@ -15,7 +15,7 @@ "test:assert": "pnpm test:prod" }, "dependencies": { - "@sentry/sveltekit": "latest || *" + "@sentry/sveltekit": "file:../../packed/sentry-sveltekit-packed.tgz" }, "devDependencies": { "@playwright/test": "~1.56.0", diff --git a/dev-packages/e2e-tests/test-applications/sveltekit-2/.npmrc b/dev-packages/e2e-tests/test-applications/sveltekit-2/.npmrc deleted file mode 100644 index 070f80f05092..000000000000 --- a/dev-packages/e2e-tests/test-applications/sveltekit-2/.npmrc +++ /dev/null @@ -1,2 +0,0 @@ -@sentry:registry=http://127.0.0.1:4873 -@sentry-internal:registry=http://127.0.0.1:4873 diff --git a/dev-packages/e2e-tests/test-applications/sveltekit-2/package.json b/dev-packages/e2e-tests/test-applications/sveltekit-2/package.json index 667371981e5f..388ea1f26f35 100644 --- a/dev-packages/e2e-tests/test-applications/sveltekit-2/package.json +++ b/dev-packages/e2e-tests/test-applications/sveltekit-2/package.json @@ -15,7 +15,7 @@ "test:assert": "pnpm test:prod" }, "dependencies": { - "@sentry/sveltekit": "latest || *" + "@sentry/sveltekit": "file:../../packed/sentry-sveltekit-packed.tgz" }, "devDependencies": { "@playwright/test": "~1.56.0", diff --git a/dev-packages/e2e-tests/test-applications/sveltekit-cloudflare-pages/.npmrc b/dev-packages/e2e-tests/test-applications/sveltekit-cloudflare-pages/.npmrc deleted file mode 100644 index 070f80f05092..000000000000 --- a/dev-packages/e2e-tests/test-applications/sveltekit-cloudflare-pages/.npmrc +++ /dev/null @@ -1,2 +0,0 @@ -@sentry:registry=http://127.0.0.1:4873 -@sentry-internal:registry=http://127.0.0.1:4873 diff --git a/dev-packages/e2e-tests/test-applications/sveltekit-cloudflare-pages/package.json b/dev-packages/e2e-tests/test-applications/sveltekit-cloudflare-pages/package.json index e17a9b2bdabd..8081e11fe19f 100644 --- a/dev-packages/e2e-tests/test-applications/sveltekit-cloudflare-pages/package.json +++ b/dev-packages/e2e-tests/test-applications/sveltekit-cloudflare-pages/package.json @@ -15,7 +15,7 @@ "test:assert": "pnpm run test:e2e" }, "dependencies": { - "@sentry/sveltekit": "latest || *" + "@sentry/sveltekit": "file:../../packed/sentry-sveltekit-packed.tgz" }, "devDependencies": { "@playwright/test": "~1.56.0", diff --git a/dev-packages/e2e-tests/test-applications/tanstack-router/.npmrc b/dev-packages/e2e-tests/test-applications/tanstack-router/.npmrc deleted file mode 100644 index 070f80f05092..000000000000 --- a/dev-packages/e2e-tests/test-applications/tanstack-router/.npmrc +++ /dev/null @@ -1,2 +0,0 @@ -@sentry:registry=http://127.0.0.1:4873 -@sentry-internal:registry=http://127.0.0.1:4873 diff --git a/dev-packages/e2e-tests/test-applications/tanstack-router/package.json b/dev-packages/e2e-tests/test-applications/tanstack-router/package.json index edb4a6cd6707..65086e5b4953 100644 --- a/dev-packages/e2e-tests/test-applications/tanstack-router/package.json +++ b/dev-packages/e2e-tests/test-applications/tanstack-router/package.json @@ -12,7 +12,7 @@ "test:assert": "pnpm test" }, "dependencies": { - "@sentry/react": "latest || *", + "@sentry/react": "file:../../packed/sentry-react-packed.tgz", "@tanstack/react-router": "^1.64.0", "react": "^18.2.0", "react-dom": "^18.2.0" diff --git a/dev-packages/e2e-tests/test-applications/tanstackstart-react/.npmrc b/dev-packages/e2e-tests/test-applications/tanstackstart-react/.npmrc deleted file mode 100644 index 070f80f05092..000000000000 --- a/dev-packages/e2e-tests/test-applications/tanstackstart-react/.npmrc +++ /dev/null @@ -1,2 +0,0 @@ -@sentry:registry=http://127.0.0.1:4873 -@sentry-internal:registry=http://127.0.0.1:4873 diff --git a/dev-packages/e2e-tests/test-applications/tanstackstart-react/package.json b/dev-packages/e2e-tests/test-applications/tanstackstart-react/package.json index bcfb3279f684..6d431226dbfc 100644 --- a/dev-packages/e2e-tests/test-applications/tanstackstart-react/package.json +++ b/dev-packages/e2e-tests/test-applications/tanstackstart-react/package.json @@ -13,7 +13,7 @@ "test:assert": "pnpm test" }, "dependencies": { - "@sentry/tanstackstart-react": "latest || *", + "@sentry/tanstackstart-react": "file:../../packed/sentry-tanstackstart-react-packed.tgz", "@tanstack/react-start": "^1.136.0", "@tanstack/react-router": "^1.136.0", "react": "^19.2.0", diff --git a/dev-packages/e2e-tests/test-applications/tsx-express/.npmrc b/dev-packages/e2e-tests/test-applications/tsx-express/.npmrc deleted file mode 100644 index 070f80f05092..000000000000 --- a/dev-packages/e2e-tests/test-applications/tsx-express/.npmrc +++ /dev/null @@ -1,2 +0,0 @@ -@sentry:registry=http://127.0.0.1:4873 -@sentry-internal:registry=http://127.0.0.1:4873 diff --git a/dev-packages/e2e-tests/test-applications/tsx-express/package.json b/dev-packages/e2e-tests/test-applications/tsx-express/package.json index 3c8cedfca04e..7794d2c7ac52 100644 --- a/dev-packages/e2e-tests/test-applications/tsx-express/package.json +++ b/dev-packages/e2e-tests/test-applications/tsx-express/package.json @@ -11,8 +11,8 @@ }, "dependencies": { "@modelcontextprotocol/sdk": "^1.26.0", - "@sentry/core": "latest || *", - "@sentry/node": "latest || *", + "@sentry/core": "file:../../packed/sentry-core-packed.tgz", + "@sentry/node": "file:../../packed/sentry-node-packed.tgz", "@trpc/server": "10.45.4", "@trpc/client": "10.45.4", "@types/express": "^4.17.21", diff --git a/dev-packages/e2e-tests/test-applications/vue-3/.npmrc b/dev-packages/e2e-tests/test-applications/vue-3/.npmrc deleted file mode 100644 index 070f80f05092..000000000000 --- a/dev-packages/e2e-tests/test-applications/vue-3/.npmrc +++ /dev/null @@ -1,2 +0,0 @@ -@sentry:registry=http://127.0.0.1:4873 -@sentry-internal:registry=http://127.0.0.1:4873 diff --git a/dev-packages/e2e-tests/test-applications/vue-3/package.json b/dev-packages/e2e-tests/test-applications/vue-3/package.json index 603f2f0ffc31..243f4875add6 100644 --- a/dev-packages/e2e-tests/test-applications/vue-3/package.json +++ b/dev-packages/e2e-tests/test-applications/vue-3/package.json @@ -19,7 +19,7 @@ "test:print-version": "node -p \"'Vue version: ' + require('vue/package.json').version\"" }, "dependencies": { - "@sentry/vue": "latest || *", + "@sentry/vue": "file:../../packed/sentry-vue-packed.tgz", "pinia": "^3.0.0", "vue": "^3.4.15", "vue-router": "^4.2.5" diff --git a/dev-packages/e2e-tests/test-applications/vue-tanstack-router/.npmrc b/dev-packages/e2e-tests/test-applications/vue-tanstack-router/.npmrc deleted file mode 100644 index 070f80f05092..000000000000 --- a/dev-packages/e2e-tests/test-applications/vue-tanstack-router/.npmrc +++ /dev/null @@ -1,2 +0,0 @@ -@sentry:registry=http://127.0.0.1:4873 -@sentry-internal:registry=http://127.0.0.1:4873 diff --git a/dev-packages/e2e-tests/test-applications/vue-tanstack-router/package.json b/dev-packages/e2e-tests/test-applications/vue-tanstack-router/package.json index 448876ec6d2b..8f764e682cd9 100644 --- a/dev-packages/e2e-tests/test-applications/vue-tanstack-router/package.json +++ b/dev-packages/e2e-tests/test-applications/vue-tanstack-router/package.json @@ -13,7 +13,7 @@ "test:assert": "pnpm test:prod" }, "dependencies": { - "@sentry/vue": "latest || *", + "@sentry/vue": "file:../../packed/sentry-vue-packed.tgz", "@tanstack/vue-router": "^1.141.8", "vue": "^3.4.15" }, diff --git a/dev-packages/e2e-tests/test-applications/webpack-4/.npmrc b/dev-packages/e2e-tests/test-applications/webpack-4/.npmrc deleted file mode 100644 index 070f80f05092..000000000000 --- a/dev-packages/e2e-tests/test-applications/webpack-4/.npmrc +++ /dev/null @@ -1,2 +0,0 @@ -@sentry:registry=http://127.0.0.1:4873 -@sentry-internal:registry=http://127.0.0.1:4873 diff --git a/dev-packages/e2e-tests/test-applications/webpack-4/package.json b/dev-packages/e2e-tests/test-applications/webpack-4/package.json index d4f59a0d0511..367804adc700 100644 --- a/dev-packages/e2e-tests/test-applications/webpack-4/package.json +++ b/dev-packages/e2e-tests/test-applications/webpack-4/package.json @@ -10,7 +10,7 @@ "devDependencies": { "@playwright/test": "~1.56.0", "@sentry-internal/test-utils": "link:../../../test-utils", - "@sentry/browser": "latest || *", + "@sentry/browser": "file:../../packed/sentry-browser-packed.tgz", "babel-loader": "^8.0.0", "@babel/core": "^7.0.0", "@babel/preset-env": "^7.0.0", diff --git a/dev-packages/e2e-tests/test-applications/webpack-5/.npmrc b/dev-packages/e2e-tests/test-applications/webpack-5/.npmrc deleted file mode 100644 index 070f80f05092..000000000000 --- a/dev-packages/e2e-tests/test-applications/webpack-5/.npmrc +++ /dev/null @@ -1,2 +0,0 @@ -@sentry:registry=http://127.0.0.1:4873 -@sentry-internal:registry=http://127.0.0.1:4873 diff --git a/dev-packages/e2e-tests/test-applications/webpack-5/package.json b/dev-packages/e2e-tests/test-applications/webpack-5/package.json index 4378532be1b9..8d7f63c08f27 100644 --- a/dev-packages/e2e-tests/test-applications/webpack-5/package.json +++ b/dev-packages/e2e-tests/test-applications/webpack-5/package.json @@ -10,7 +10,7 @@ "devDependencies": { "@playwright/test": "~1.56.0", "@sentry-internal/test-utils": "link:../../../test-utils", - "@sentry/browser": "latest || *", + "@sentry/browser": "file:../../packed/sentry-browser-packed.tgz", "webpack": "^5.91.0", "terser-webpack-plugin": "^5.3.10", "html-webpack-plugin": "^5.6.0", diff --git a/dev-packages/e2e-tests/test-registry.npmrc b/dev-packages/e2e-tests/test-registry.npmrc deleted file mode 100644 index 97b9627a1642..000000000000 --- a/dev-packages/e2e-tests/test-registry.npmrc +++ /dev/null @@ -1,6 +0,0 @@ -@sentry:registry=http://127.0.0.1:4873 -@sentry-internal:registry=http://127.0.0.1:4873 -//127.0.0.1:4873/:_authToken=some-token - -# Do not notify about npm updates -update-notifier=false diff --git a/dev-packages/e2e-tests/validate-packed-tarball-setup.ts b/dev-packages/e2e-tests/validate-packed-tarball-setup.ts new file mode 100644 index 000000000000..a6cc966d56f0 --- /dev/null +++ b/dev-packages/e2e-tests/validate-packed-tarball-setup.ts @@ -0,0 +1,42 @@ +import * as assert from 'assert'; +import * as fs from 'fs'; +import { sync as globSync } from 'glob'; +import * as path from 'path'; + +const repositoryRoot = path.resolve(__dirname, '../..'); + +const e2ePkg = JSON.parse(fs.readFileSync(path.join(__dirname, 'package.json'), 'utf8')) as { version: string }; +const version = e2ePkg.version; + +const tarballPaths = globSync(`packages/*/sentry-*-${version}.tgz`, { + cwd: repositoryRoot, + absolute: true, +}); + +assert.ok( + tarballPaths.length > 0, + `No tarballs found for version ${version}. Run "yarn build:tarball" at the repository root.`, +); + +const symlinkPaths = globSync('packed/*-packed.tgz', { + cwd: __dirname, + absolute: true, +}); + +assert.ok( + symlinkPaths.length > 0, + 'No packed tarball symlinks found. Run "yarn test:prepare" in dev-packages/e2e-tests.', +); + +assert.strictEqual( + symlinkPaths.length, + tarballPaths.length, + `Tarball count (${tarballPaths.length}) does not match packed symlink count (${symlinkPaths.length}). Re-run "yarn sync:packed-tarballs".`, +); + +for (const symlinkPath of symlinkPaths) { + const st = fs.lstatSync(symlinkPath); + assert.ok(st.isSymbolicLink(), `Expected ${symlinkPath} to be a symlink.`); + const target = path.resolve(path.dirname(symlinkPath), fs.readlinkSync(symlinkPath)); + assert.ok(fs.existsSync(target), `Symlink ${symlinkPath} points to missing file: ${target}`); +} diff --git a/dev-packages/e2e-tests/validate-test-app-setups.ts b/dev-packages/e2e-tests/validate-test-app-setups.ts deleted file mode 100644 index edbbe047417f..000000000000 --- a/dev-packages/e2e-tests/validate-test-app-setups.ts +++ /dev/null @@ -1,29 +0,0 @@ -/* eslint-disable no-console */ -import * as fs from 'fs'; -import { globSync } from 'glob'; -import * as path from 'path'; - -const testRecipePaths = globSync('test-applications/*/test-recipe.json', { - cwd: __dirname, - absolute: true, -}); - -testRecipePaths.forEach(testRecipePath => { - const testAppPath = path.dirname(testRecipePath); - const npmrcPath = path.resolve(testAppPath, '.npmrc'); - - if (!fs.existsSync(npmrcPath)) { - console.log( - `No .npmrc found in test application "${testAppPath}". Please add a .npmrc to this test application that uses the fake test registry. (More info in dev-packages/e2e-tests/README.md)`, - ); - process.exit(1); - } - - const npmrcContents = fs.readFileSync(npmrcPath, 'utf-8'); - if (!npmrcContents.includes('http://localhost:4873')) { - console.log( - `.npmrc in test application "${testAppPath} doesn't contain a reference to the fake test registry at "http://localhost:4873". Please add a .npmrc to this test application that uses the fake test registry. (More info in dev-packages/e2e-tests/README.md)`, - ); - process.exit(1); - } -}); diff --git a/dev-packages/e2e-tests/validate-verdaccio-configuration.ts b/dev-packages/e2e-tests/validate-verdaccio-configuration.ts deleted file mode 100644 index 7bef179bd5a6..000000000000 --- a/dev-packages/e2e-tests/validate-verdaccio-configuration.ts +++ /dev/null @@ -1,45 +0,0 @@ -import * as assert from 'assert'; -import * as fs from 'fs'; -import { globSync } from 'glob'; -import * as path from 'path'; -import * as YAML from 'yaml'; - -/* - * This file is a quick automatic check to confirm that the packages in the Verdaccio configuration always match the - * packages we defined in our monorepo. This is to ensure that the E2E tests do not use the packages that live on NPM - * but the local ones instead. - */ - -const repositoryRoot = path.resolve(__dirname, '../..'); - -const verdaccioConfigContent = fs.readFileSync('./verdaccio-config/config.yaml', { encoding: 'utf8' }); -const verdaccioConfig = YAML.parse(verdaccioConfigContent); -// eslint-disable-next-line @typescript-eslint/no-unsafe-member-access -const sentryScopedPackagesInVerdaccioConfig = Object.keys(verdaccioConfig.packages).filter(packageName => - packageName.startsWith('@sentry/'), -); - -const packageJsonPaths = globSync('packages/*/package.json', { - cwd: repositoryRoot, - absolute: true, -}); -const packageJsons = packageJsonPaths.map(packageJsonPath => require(packageJsonPath)); -const sentryScopedPackageNames = packageJsons - // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access - .filter(packageJson => packageJson.name.startsWith('@sentry/')) - // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access - .map(packageJson => packageJson.name); - -const extraPackagesInVerdaccioConfig = sentryScopedPackagesInVerdaccioConfig.filter( - x => !sentryScopedPackageNames.includes(x), -); -const extraPackagesInMonoRepo = sentryScopedPackageNames.filter( - x => !sentryScopedPackagesInVerdaccioConfig.includes(x), -); - -assert.ok( - extraPackagesInVerdaccioConfig.length === 0 && extraPackagesInMonoRepo.length === 0, - `Packages in Verdaccio configuration do not match the "@sentry"-scoped packages in monorepo. Make sure they match!\nPackages missing in Verdaccio configuration: ${JSON.stringify( - extraPackagesInMonoRepo, - )}\nPackages missing in monorepo: ${JSON.stringify(extraPackagesInVerdaccioConfig)}`, -); diff --git a/dev-packages/e2e-tests/verdaccio-config/config.yaml b/dev-packages/e2e-tests/verdaccio-config/config.yaml deleted file mode 100644 index d80ed2aa429f..000000000000 --- a/dev-packages/e2e-tests/verdaccio-config/config.yaml +++ /dev/null @@ -1,288 +0,0 @@ -# Taken from https://github.com/babel/babel/blob/624c78d99e8f42b2543b8943ab1b62bd71cf12d8/scripts/integration-tests/verdaccio-config.yml - -# -# This is the default config file. It allows all users to do anything, -# so don't use it on production systems. -# -# Look here for more config file examples: -# https://github.com/verdaccio/verdaccio/tree/master/conf -# - -# Repo-local storage (relative to this file). Absolute /verdaccio/... matches Docker-only templates and is not writable on typical dev machines. -storage: ./storage/data - -# https://verdaccio.org/docs/configuration#authentication -auth: - htpasswd: - file: ./storage/htpasswd - -# https://verdaccio.org/docs/configuration#uplinks -# a list of other known repositories we can talk to -uplinks: - npmjs: - url: https://registry.npmjs.org/ - -# Learn how to protect your packages -# https://verdaccio.org/docs/protect-your-dependencies/ -# https://verdaccio.org/docs/configuration#packages -packages: - # To not use a proxy (e.g. npm) but instead use verdaccio for package hosting we need to define rules here without the - # `proxy` field. Sadly we can't use a wildcard like "@sentry/*" because we have some dependencies (@sentry/cli, - # @sentry/webpack-plugin) that fall under that wildcard but don't live in this repository. If we were to use that - # wildcard, we would get a 404 when attempting to install them, since they weren't uploaded to verdaccio, and also - # don't have a proxy configuration. - - '@sentry/angular': - access: $all - publish: $all - unpublish: $all - # proxy: npmjs # Don't proxy for E2E tests! - - '@sentry/astro': - access: $all - publish: $all - unpublish: $all - # proxy: npmjs # Don't proxy for E2E tests! - - '@sentry/browser': - access: $all - publish: $all - unpublish: $all - # proxy: npmjs # Don't proxy for E2E tests! - - '@sentry/bun': - access: $all - publish: $all - unpublish: $all - # proxy: npmjs # Don't proxy for E2E tests! - - '@sentry/core': - access: $all - publish: $all - unpublish: $all - # proxy: npmjs # Don't proxy for E2E tests! - - '@sentry/cloudflare': - access: $all - publish: $all - unpublish: $all - # proxy: npmjs # Don't proxy for E2E tests! - - '@sentry/deno': - access: $all - publish: $all - unpublish: $all - # proxy: npmjs # Don't proxy for E2E tests! - - '@sentry/effect': - access: $all - publish: $all - unpublish: $all - # proxy: npmjs # Don't proxy for E2E tests! - - '@sentry/elysia': - access: $all - publish: $all - unpublish: $all - # proxy: npmjs # Don't proxy for E2E tests! - - '@sentry/ember': - access: $all - publish: $all - unpublish: $all - # proxy: npmjs # Don't proxy for E2E tests! - - '@sentry/gatsby': - access: $all - publish: $all - unpublish: $all - # proxy: npmjs # Don't proxy for E2E tests! - - '@sentry/hono': - access: $all - publish: $all - unpublish: $all - # proxy: npmjs # Don't proxy for E2E tests! - - '@sentry/nestjs': - access: $all - publish: $all - unpublish: $all - # proxy: npmjs # Don't proxy for E2E tests! - - '@sentry/nextjs': - access: $all - publish: $all - unpublish: $all - # proxy: npmjs # Don't proxy for E2E tests! - - '@sentry/node': - access: $all - publish: $all - unpublish: $all - # proxy: npmjs # Don't proxy for E2E tests! - - '@sentry/node-core': - access: $all - publish: $all - unpublish: $all - # proxy: npmjs # Don't proxy for E2E tests! - - '@sentry/node-native': - access: $all - publish: $all - unpublish: $all - # proxy: npmjs # Don't proxy for E2E tests! - - '@sentry/opentelemetry': - access: $all - publish: $all - unpublish: $all - # proxy: npmjs # Don't proxy for E2E tests! - - '@sentry/profiling-node': - access: $all - publish: $all - unpublish: $all - # proxy: npmjs # Don't proxy for E2E tests! - - '@sentry/react': - access: $all - publish: $all - unpublish: $all - # proxy: npmjs # Don't proxy for E2E tests! - - '@sentry/react-router': - access: $all - publish: $all - unpublish: $all - # proxy: npmjs # Don't proxy for E2E tests! - - '@sentry/remix': - access: $all - publish: $all - unpublish: $all - # proxy: npmjs # Don't proxy for E2E tests! - - '@sentry/aws-serverless': - access: $all - publish: $all - unpublish: $all - # proxy: npmjs # Don't proxy for E2E tests! - - '@sentry/google-cloud-serverless': - access: $all - publish: $all - unpublish: $all - # proxy: npmjs # Don't proxy for E2E tests! - - '@sentry/solid': - access: $all - publish: $all - unpublish: $all - # proxy: npmjs # Don't proxy for E2E tests! - - '@sentry/solidstart': - access: $all - publish: $all - unpublish: $all - # proxy: npmjs # Don't proxy for E2E tests! - - '@sentry/svelte': - access: $all - publish: $all - unpublish: $all - # proxy: npmjs # Don't proxy for E2E tests! - - '@sentry/sveltekit': - access: $all - publish: $all - unpublish: $all - # proxy: npmjs # Don't proxy for E2E tests! - - '@sentry/tanstackstart': - access: $all - publish: $all - unpublish: $all - # proxy: npmjs # Don't proxy for E2E tests! - - '@sentry/tanstackstart-react': - access: $all - publish: $all - unpublish: $all - # proxy: npmjs # Don't proxy for E2E tests! - - '@sentry/types': - access: $all - publish: $all - unpublish: $all - # proxy: npmjs # Don't proxy for E2E tests! - - '@sentry/vercel-edge': - access: $all - publish: $all - unpublish: $all - # proxy: npmjs # Don't proxy for E2E tests! - - '@sentry/vue': - access: $all - publish: $all - unpublish: $all - # proxy: npmjs # Don't proxy for E2E tests! - - '@sentry/nuxt': - access: $all - publish: $all - unpublish: $all - # proxy: npmjs # Don't proxy for E2E tests! - - '@sentry/wasm': - access: $all - publish: $all - unpublish: $all - # proxy: npmjs # Don't proxy for E2E tests! - - '@sentry-internal/*': - access: $all - publish: $all - unpublish: $all - # proxy: npmjs # Don't proxy for E2E tests! - - '@*/*': - # scoped packages - access: $all - publish: $all - unpublish: $all - proxy: npmjs - - '**': - # allow all users (including non-authenticated users) to read and - # publish all packages - # - # you can specify usernames/groupnames (depending on your auth plugin) - # and three keywords: "$all", "$anonymous", "$authenticated" - access: $all - - # allow all known users to publish/publish packages - # (anyone can register by default, remember?) - publish: $all - unpublish: $all - proxy: npmjs - -# https://verdaccio.org/docs/configuration#server -# You can specify HTTP/1.1 server keep alive timeout in seconds for incoming connections. -# A value of 0 makes the http server behave similarly to Node.js versions prior to 8.0.0, which did not have a keep-alive timeout. -# WORKAROUND: Through given configuration you can workaround following issue https://github.com/verdaccio/verdaccio/issues/301. Set to 0 in case 60 is not enough. -server: - keepAliveTimeout: 60 - -middlewares: - audit: - enabled: false - -# https://verdaccio.org/docs/logger -# log settings -log: { type: stdout, format: pretty, level: http } -#experiments: -# # support for npm token command -# token: false diff --git a/dev-packages/e2e-tests/verdaccio-runner.mjs b/dev-packages/e2e-tests/verdaccio-runner.mjs deleted file mode 100644 index 7de53e472d5a..000000000000 --- a/dev-packages/e2e-tests/verdaccio-runner.mjs +++ /dev/null @@ -1,26 +0,0 @@ -/* eslint-disable no-console */ -import { createRequire } from 'node:module'; - -const require = createRequire(import.meta.url); -const { runServer } = require('verdaccio'); - -const configPath = process.argv[2]; -const port = parseInt(process.argv[3], 10); - -if (!configPath || !Number.isFinite(port)) { - console.error('verdaccio-runner: expected argv'); - process.exit(1); -} - -try { - // runServer resolves to the Express app; binding errors are emitted on the - // http.Server returned by app.listen(), not on the app itself. - const app = await runServer(configPath, { listenArg: String(port) }); - await new Promise((resolve, reject) => { - const httpServer = app.listen(port, '127.0.0.1', () => resolve()); - httpServer.once('error', reject); - }); -} catch (err) { - console.error(err); - process.exit(1); -} diff --git a/packages/aws-serverless/scripts/buildLambdaLayer.ts b/packages/aws-serverless/scripts/buildLambdaLayer.ts index cca3b739bf6b..520a456c63ce 100644 --- a/packages/aws-serverless/scripts/buildLambdaLayer.ts +++ b/packages/aws-serverless/scripts/buildLambdaLayer.ts @@ -73,6 +73,9 @@ async function buildLambdaLayer(): Promise { console.log(`Creating final layer zip file ${zipFilename}.`); // need to preserve the symlink above with -y run(`zip -r -y ${zipFilename} ${dirsToZip.join(' ')}`, { cwd: 'build/aws/dist-serverless' }); + + // Cleanup temporary installation files + fs.rmSync('build/aws/dist-serverless/nodejs/', { recursive: true, force: true }); } // eslint-disable-next-line @typescript-eslint/no-floating-promises diff --git a/yarn.lock b/yarn.lock index 7731d582e748..be8715b44049 100644 --- a/yarn.lock +++ b/yarn.lock @@ -3158,30 +3158,6 @@ resolved "https://registry.yarnpkg.com/@csstools/selector-specificity/-/selector-specificity-2.2.0.tgz#2cbcf822bf3764c9658c4d2e568bd0c0cb748016" integrity sha512-+OJ9konv95ClSTOJCmMZqpd5+YGsB2S+x6w3E1oaM8UuR5j8nTNHYSz8c9BEPGDOCMQYIEEGlVPj/VY64iTbGw== -"@cypress/request@3.0.10": - version "3.0.10" - resolved "https://registry.yarnpkg.com/@cypress/request/-/request-3.0.10.tgz#e09c695e8460a82acafe6cfaf089cf2ca06dc054" - integrity sha512-hauBrOdvu08vOsagkZ/Aju5XuiZx6ldsLfByg1htFeldhex+PeMrYauANzFsMJeAA0+dyPLbDoX2OYuvVoLDkQ== - dependencies: - aws-sign2 "~0.7.0" - aws4 "^1.8.0" - caseless "~0.12.0" - combined-stream "~1.0.6" - extend "~3.0.2" - forever-agent "~0.6.1" - form-data "~4.0.4" - http-signature "~1.4.0" - is-typedarray "~1.0.0" - isstream "~0.1.2" - json-stringify-safe "~5.0.1" - mime-types "~2.1.19" - performance-now "^2.1.0" - qs "~6.14.1" - safe-buffer "^5.1.2" - tough-cookie "^5.0.0" - tunnel-agent "^0.6.0" - uuid "^8.3.2" - "@dabh/diagnostics@^2.0.2": version "2.0.3" resolved "https://registry.yarnpkg.com/@dabh/diagnostics/-/diagnostics-2.0.3.tgz#7f7e97ee9a725dffc7808d93668cc984e1dc477a" @@ -6838,11 +6814,6 @@ "@parcel/watcher-win32-ia32" "2.5.1" "@parcel/watcher-win32-x64" "2.5.1" -"@pinojs/redact@^0.4.0": - version "0.4.0" - resolved "https://registry.yarnpkg.com/@pinojs/redact/-/redact-0.4.0.tgz#c3de060dd12640dcc838516aa2a6803cc7b2e9d6" - integrity sha512-k2ENnmBugE/rzQfEcdWHcCY+/FM3VLzH9cYEsbdsoqrvzAKRhUZeRNhAZvB8OitQJ1TBed3yqWtdjzS6wJKBwg== - "@pkgjs/parseargs@^0.11.0": version "0.11.0" resolved "https://registry.yarnpkg.com/@pkgjs/parseargs/-/parseargs-0.11.0.tgz#a77ea742fab25775145434eb1d2328cf5013ac33" @@ -7870,11 +7841,6 @@ resolved "https://registry.yarnpkg.com/@sinclair/typebox/-/typebox-0.34.48.tgz#75b0ead87e59e1adbd6dccdc42bad4fddee73b59" integrity sha512-kKJTNuK3AQOrgjjotVxMrCn1sUJwM76wMszfq1kdU4uYVJjvEWuFQ6HgvLt4Xz3fSmZlTOxJ/Ie13KnIcWQXFA== -"@sindresorhus/is@4.6.0": - version "4.6.0" - resolved "https://registry.yarnpkg.com/@sindresorhus/is/-/is-4.6.0.tgz#3c7c9c46e678feefe7a2e5bb609d3dbd665ffb3f" - integrity sha512-t09vSN3MdfsyCHoFcTRCH/iUtG7OJ0CsjzB8cjAmKc/va/kIgeDI/TxsigdncE/4be734m0cvIYwNaV4i2XqAw== - "@sindresorhus/is@^7.0.2": version "7.0.2" resolved "https://registry.yarnpkg.com/@sindresorhus/is/-/is-7.0.2.tgz#a0df078a8d29f9741503c5a9c302de474ec8564a" @@ -8723,13 +8689,6 @@ "@swc/counter" "^0.1.3" tslib "^2.4.0" -"@szmarczak/http-timer@4.0.6": - version "4.0.6" - resolved "https://registry.yarnpkg.com/@szmarczak/http-timer/-/http-timer-4.0.6.tgz#b4a914bb62e7c272d4e5989fe4440f812ab1d807" - integrity sha512-4BAffykYOgO+5nzBWYwE3W90sBgLJoUPRWWcL8wlyiM8IB8ipJz3UMJ9KXQd1RKQXpKp8Tutn80HZtWsu2u76w== - dependencies: - defer-to-connect "^2.0.0" - "@tanstack/directive-functions-plugin@1.121.21": version "1.121.21" resolved "https://registry.yarnpkg.com/@tanstack/directive-functions-plugin/-/directive-functions-plugin-1.121.21.tgz#a5b265d6eb265b981a7b3e41e73c1e082a0948bc" @@ -9796,13 +9755,6 @@ resolved "https://registry.yarnpkg.com/@types/resolve/-/resolve-1.20.2.tgz#97d26e00cd4a0423b4af620abecf3e6f442b7975" integrity sha512-60BCwRFOZCQhDncwQdxxeOEEkbc5dIMccYLwbxsS4TUNeVECQ/pBJ0j09mrHOl/JJvpRPGwO9SvE4nR2Nb/a4Q== -"@types/responselike@1.0.0": - version "1.0.0" - resolved "https://registry.yarnpkg.com/@types/responselike/-/responselike-1.0.0.tgz#251f4fe7d154d2bad125abe1b429b23afd262e29" - integrity sha512-85Y2BjiufFzaMIlvJDvTTB8Fxl2xfLo4HgmHzVBz08w4wDePCTjYw66PdrolO0kzli3yam/YCgRufyo1DdQVTA== - dependencies: - "@types/node" "*" - "@types/retry@0.12.0": version "0.12.0" resolved "https://registry.yarnpkg.com/@types/retry/-/retry-0.12.0.tgz#2b35eccfcee7d38cd72ad99232fbd58bffb3c84d" @@ -10180,208 +10132,6 @@ picomatch "^4.0.2" resolve-from "^5.0.0" -"@verdaccio/auth@8.0.0-next-8.37": - version "8.0.0-next-8.37" - resolved "https://registry.yarnpkg.com/@verdaccio/auth/-/auth-8.0.0-next-8.37.tgz#0113de30ff0c66284adde0ec99c045f991a97e44" - integrity sha512-wvKPnjDZReT0gSxntUbcOYl23m2mHeMT9a/uhRMdw3pbraSgozatnf3UuoTd6Uyfm3vn+HHAHqzudUn1+yD4rw== - dependencies: - "@verdaccio/config" "8.0.0-next-8.37" - "@verdaccio/core" "8.0.0-next-8.37" - "@verdaccio/loaders" "8.0.0-next-8.27" - "@verdaccio/signature" "8.0.0-next-8.29" - debug "4.4.3" - lodash "4.18.1" - verdaccio-htpasswd "13.0.0-next-8.37" - -"@verdaccio/config@8.0.0-next-8.37": - version "8.0.0-next-8.37" - resolved "https://registry.yarnpkg.com/@verdaccio/config/-/config-8.0.0-next-8.37.tgz#b1432470f243de75ed72c97237851f23e683f59a" - integrity sha512-SbmDMJpora293B+TDYfxJL5LEaFh7gdh0MmkPJCBkmGlRPmynTfHcQzVzAll3+IMYFkrf1zZtq/qlgorjaoFoQ== - dependencies: - "@verdaccio/core" "8.0.0-next-8.37" - debug "4.4.3" - js-yaml "4.1.1" - lodash "4.18.1" - -"@verdaccio/core@8.0.0-next-8.21": - version "8.0.0-next-8.21" - resolved "https://registry.yarnpkg.com/@verdaccio/core/-/core-8.0.0-next-8.21.tgz#7eaa7ca107cc2889d4c87401d9d96bc6365c8341" - integrity sha512-n3Y8cqf84cwXxUUdTTfEJc8fV55PONPKijCt2YaC0jilb5qp1ieB3d4brqTOdCdXuwkmnG2uLCiGpUd/RuSW0Q== - dependencies: - ajv "8.17.1" - http-errors "2.0.0" - http-status-codes "2.3.0" - minimatch "7.4.6" - process-warning "1.0.0" - semver "7.7.2" - -"@verdaccio/core@8.0.0-next-8.37": - version "8.0.0-next-8.37" - resolved "https://registry.yarnpkg.com/@verdaccio/core/-/core-8.0.0-next-8.37.tgz#4c9d3cf85a74e19e8673802de9c8f8548b27d93e" - integrity sha512-R8rDEa2mPjfHhEK2tWTMFnrfvyNmTd5ZrNz9X5/EiFVJPr/+oo9cTZkDXzY9+KREJUUIUFili4qynmBt0lw8nw== - dependencies: - ajv "8.18.0" - http-errors "2.0.1" - http-status-codes "2.3.0" - minimatch "7.4.9" - process-warning "1.0.0" - semver "7.7.4" - -"@verdaccio/file-locking@10.3.1": - version "10.3.1" - resolved "https://registry.yarnpkg.com/@verdaccio/file-locking/-/file-locking-10.3.1.tgz#cfc2436e0715954e0965f97dfcd87381d116f749" - integrity sha512-oqYLfv3Yg3mAgw9qhASBpjD50osj2AX4IwbkUtyuhhKGyoFU9eZdrbeW6tpnqUnj6yBMtAPm2eGD4BwQuX400g== - dependencies: - lockfile "1.0.4" - -"@verdaccio/file-locking@13.0.0-next-8.7": - version "13.0.0-next-8.7" - resolved "https://registry.yarnpkg.com/@verdaccio/file-locking/-/file-locking-13.0.0-next-8.7.tgz#0b1a93bf7c29572ac20abdc9d20f28c86c072e30" - integrity sha512-XL12Okp4YQd0ogYMyGc+JrqrtVC+76V5hUGCK+s/VluSFSZaJQiLs4MoUPsKfwGhqXHCAm1JcNaz4L5LoXNbjQ== - dependencies: - lockfile "1.0.4" - -"@verdaccio/hooks@8.0.0-next-8.37": - version "8.0.0-next-8.37" - resolved "https://registry.yarnpkg.com/@verdaccio/hooks/-/hooks-8.0.0-next-8.37.tgz#cd285957e7964c28fb77dc5c183905516655c122" - integrity sha512-n2t6fjXqSA+y402zO2Yh5UEe+rzMf1jhglj46MQf7IswCaST/SGLlJ5VCl6bU8LGbSr9FOz7BAtUXc64i3oCmA== - dependencies: - "@verdaccio/core" "8.0.0-next-8.37" - "@verdaccio/logger" "8.0.0-next-8.37" - debug "4.4.3" - got-cjs "12.5.4" - handlebars "4.7.9" - -"@verdaccio/loaders@8.0.0-next-8.27": - version "8.0.0-next-8.27" - resolved "https://registry.yarnpkg.com/@verdaccio/loaders/-/loaders-8.0.0-next-8.27.tgz#3c32ce116919039557ad3eb905694939b44885c2" - integrity sha512-bDfHHCDrOCSdskAKeKxPArUi5aGXtsxEpRvO8MzghX50g1zJnVzLF1KEbsEY9ScFqGZAVYtZTcutysA0VOQ0Rg== - dependencies: - "@verdaccio/core" "8.0.0-next-8.37" - debug "4.4.3" - lodash "4.18.1" - -"@verdaccio/local-storage-legacy@11.1.1": - version "11.1.1" - resolved "https://registry.yarnpkg.com/@verdaccio/local-storage-legacy/-/local-storage-legacy-11.1.1.tgz#86c73bc723e7e3d9c65953d6eb9e300ca0223653" - integrity sha512-P6ahH2W6/KqfJFKP+Eid7P134FHDLNvHa+i8KVgRVBeo2/IXb6FEANpM1mCVNvPANu0LCAmNJBOXweoUKViaoA== - dependencies: - "@verdaccio/core" "8.0.0-next-8.21" - "@verdaccio/file-locking" "10.3.1" - "@verdaccio/streams" "10.2.1" - async "3.2.6" - debug "4.4.1" - lodash "4.17.21" - lowdb "1.0.0" - mkdirp "1.0.4" - -"@verdaccio/logger-commons@8.0.0-next-8.37": - version "8.0.0-next-8.37" - resolved "https://registry.yarnpkg.com/@verdaccio/logger-commons/-/logger-commons-8.0.0-next-8.37.tgz#3fc1c2d244843189b52b498b8d3950d9e104b8c4" - integrity sha512-HVt7ttnKgioERB1lCc1UnqnJMJ8skAeivLe49uq3wEG3QtruQGCct5nosj+q1pjx8SaYpQA+qxs1+4UDddprVA== - dependencies: - "@verdaccio/core" "8.0.0-next-8.37" - "@verdaccio/logger-prettify" "8.0.0-next-8.5" - colorette "2.0.20" - debug "4.4.3" - -"@verdaccio/logger-prettify@8.0.0-next-8.5": - version "8.0.0-next-8.5" - resolved "https://registry.yarnpkg.com/@verdaccio/logger-prettify/-/logger-prettify-8.0.0-next-8.5.tgz#4f11de2aa1da5268c1e7a1fc4789d51f2179ff11" - integrity sha512-zCsvdKgUQx2mSu7fnDOkA2r2QX6yMmBDsXGmqXmoov/cZ89deREn0uC7xIX7/YEo8EspBoXiUGzqI+S+qUWPyw== - dependencies: - colorette "2.0.20" - dayjs "1.11.18" - lodash "4.18.1" - on-exit-leak-free "2.1.2" - pino-abstract-transport "1.2.0" - sonic-boom "3.8.1" - -"@verdaccio/logger@8.0.0-next-8.37": - version "8.0.0-next-8.37" - resolved "https://registry.yarnpkg.com/@verdaccio/logger/-/logger-8.0.0-next-8.37.tgz#51d6dededa058726e3b54b310781889bec4fc0c9" - integrity sha512-xG27C1FsbTsHhvhB3OpisVzHUyrE9NSVrzVHapPuOPd6X1DpnHZoZ+UYBAS0MSO1tGS+NEHW9GHL0XiroULggA== - dependencies: - "@verdaccio/logger-commons" "8.0.0-next-8.37" - pino "9.14.0" - -"@verdaccio/middleware@8.0.0-next-8.37": - version "8.0.0-next-8.37" - resolved "https://registry.yarnpkg.com/@verdaccio/middleware/-/middleware-8.0.0-next-8.37.tgz#b6730c3ef5eea444fb164e95e9ebdac44c28b6d7" - integrity sha512-8SqCdzKfANhaO/ZoSBBJHPlDWmXEJdq/1giV/ZKYtU4xVbBb/4ThKp2nNKk4s9+8S8XNNgX+7J5e/BgbIzzsEA== - dependencies: - "@verdaccio/config" "8.0.0-next-8.37" - "@verdaccio/core" "8.0.0-next-8.37" - "@verdaccio/url" "13.0.0-next-8.37" - debug "4.4.3" - express "4.22.1" - express-rate-limit "5.5.1" - lodash "4.18.1" - lru-cache "7.18.3" - -"@verdaccio/package-filter@13.0.0-next-8.5": - version "13.0.0-next-8.5" - resolved "https://registry.yarnpkg.com/@verdaccio/package-filter/-/package-filter-13.0.0-next-8.5.tgz#4f8a70016df578da3bb1e200f772409839fcafe9" - integrity sha512-+RZzVI/Yqjpoiv2SL3C0cxMC8ucU6j+YPdP/Bg/KJVqPbGNTn4Ol/fuGNhMJO6meIRS5ekW0PSrAvrDJ6E+JCA== - dependencies: - "@verdaccio/core" "8.0.0-next-8.37" - debug "4.4.3" - semver "7.7.4" - -"@verdaccio/search-indexer@8.0.0-next-8.6": - version "8.0.0-next-8.6" - resolved "https://registry.yarnpkg.com/@verdaccio/search-indexer/-/search-indexer-8.0.0-next-8.6.tgz#7609c18afc68f09b9800e3548d3bce295e5bd001" - integrity sha512-+vFkeqwXWlbpPO/vxC1N5Wbi8sSXYR5l9Z41fqQgSncaF/hfSKB/iHsid1psCusfsDPGuwEbm2usPEW0nDdRDg== - -"@verdaccio/signature@8.0.0-next-8.29": - version "8.0.0-next-8.29" - resolved "https://registry.yarnpkg.com/@verdaccio/signature/-/signature-8.0.0-next-8.29.tgz#e06a854bd9422c80e38884fc635432f9f95f5ac0" - integrity sha512-D1OyGi7+/5zXdbf78qdmL4wpf7iGN+RNDGB2TdLVosSrd+PWGrXqMJB3q2r/DJQ8J3724YVOJgNKiXjxV+Y1Aw== - dependencies: - "@verdaccio/config" "8.0.0-next-8.37" - "@verdaccio/core" "8.0.0-next-8.37" - debug "4.4.3" - jsonwebtoken "9.0.3" - -"@verdaccio/streams@10.2.1": - version "10.2.1" - resolved "https://registry.yarnpkg.com/@verdaccio/streams/-/streams-10.2.1.tgz#9443d24d4f17672b8f8c8e147690557918ed2bcb" - integrity sha512-OojIG/f7UYKxC4dYX8x5ax8QhRx1b8OYUAMz82rUottCuzrssX/4nn5QE7Ank0DUSX3C9l/HPthc4d9uKRJqJQ== - -"@verdaccio/tarball@13.0.0-next-8.37": - version "13.0.0-next-8.37" - resolved "https://registry.yarnpkg.com/@verdaccio/tarball/-/tarball-13.0.0-next-8.37.tgz#5cd9a8b015c3ac6dbb2e92a42e6ad96bc3ad33e4" - integrity sha512-Qxm6JQYEFfkeJd4YQII/2IAMiL++QnM6gqKXZbM5mNkAApyqx8ZbL1e9pe3aCDmBYs2mo0JGORCy3OaHZcsJGw== - dependencies: - "@verdaccio/core" "8.0.0-next-8.37" - "@verdaccio/url" "13.0.0-next-8.37" - debug "4.4.3" - gunzip-maybe "1.4.2" - tar-stream "3.1.7" - -"@verdaccio/ui-theme@9.0.0-next-9.10": - version "9.0.0-next-9.10" - resolved "https://registry.yarnpkg.com/@verdaccio/ui-theme/-/ui-theme-9.0.0-next-9.10.tgz#9825385187a2ba090c22e4c39cdc1f1b9c300ddc" - integrity sha512-sJRuAGKHklQU8bLdcXjvzfajXk9VqyBcFUKHAAyWXAeIKs04/lTgCHB6nFcEm0WDzlXk8CMAQuo/kcjNkg7qyw== - -"@verdaccio/url@13.0.0-next-8.37": - version "13.0.0-next-8.37" - resolved "https://registry.yarnpkg.com/@verdaccio/url/-/url-13.0.0-next-8.37.tgz#2cf602feac7ac2f86cdef59207f4a7f1efa3f855" - integrity sha512-Gtv5oDgVwGPGxzuXaIJLbmL8YkBKW2UZwDsrnbDoWRt1nWLtiOp4Vui1VISTqX7A9BB50YslLEgNLcPd2qRE+w== - dependencies: - "@verdaccio/core" "8.0.0-next-8.37" - debug "4.4.3" - validator "13.15.26" - -"@verdaccio/utils@8.1.0-next-8.37": - version "8.1.0-next-8.37" - resolved "https://registry.yarnpkg.com/@verdaccio/utils/-/utils-8.1.0-next-8.37.tgz#ad6eb2be2722ef8b46d735581b33c4eabbc27663" - integrity sha512-wfwn3z5M+w2KOV+xJFVv8tM8aOB4Ok5emfBDrDHrHMPDJ/fn3dEo6HoOra654PJ+zNwbTiMDvE5oAg/PLtnsUw== - dependencies: - "@verdaccio/core" "8.0.0-next-8.37" - lodash "4.18.1" - minimatch "7.4.9" - "@vinxi/listhen@^1.5.6": version "1.5.6" resolved "https://registry.yarnpkg.com/@vinxi/listhen/-/listhen-1.5.6.tgz#78a01eb6d92016b646f3b468433e7bbb8c9957ab" @@ -11076,14 +10826,6 @@ 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.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== - dependencies: - jsonparse "^1.2.0" - through ">=2.2.7 <3" - abab@^2.0.6: version "2.0.6" resolved "https://registry.yarnpkg.com/abab/-/abab-2.0.6.tgz#41b80f2c871d19686216b82309231cfd3cb3d291" @@ -11257,17 +10999,17 @@ ajv@8.11.0: require-from-string "^2.0.2" uri-js "^4.2.2" -ajv@8.17.1: - version "8.17.1" - resolved "https://registry.yarnpkg.com/ajv/-/ajv-8.17.1.tgz#37d9a5c776af6bc92d7f4f9510eba4c0a60d11a6" - integrity sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g== +ajv@^6.11.0, ajv@^6.12.4, ajv@^6.12.5: + version "6.12.6" + resolved "https://registry.yarnpkg.com/ajv/-/ajv-6.12.6.tgz#baf5a62e802b07d977034586f8c3baf5adf26df4" + integrity sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g== dependencies: - fast-deep-equal "^3.1.3" - fast-uri "^3.0.1" - json-schema-traverse "^1.0.0" - require-from-string "^2.0.2" + fast-deep-equal "^3.1.1" + fast-json-stable-stringify "^2.0.0" + json-schema-traverse "^0.4.1" + uri-js "^4.2.2" -ajv@8.18.0, ajv@^8.0.0, ajv@^8.10.0, ajv@^8.9.0: +ajv@^8.0.0, ajv@^8.10.0, ajv@^8.9.0: version "8.18.0" resolved "https://registry.yarnpkg.com/ajv/-/ajv-8.18.0.tgz#8864186b6738d003eb3a933172bb3833e10cefbc" integrity sha512-PlXPeEWMXMZ7sPYOHqmDyCJzcfNrUr3fGNKtezX14ykXOEIvyK81d+qydx89KY5O71FKMPaQ2vBfBFI5NHR63A== @@ -11277,16 +11019,6 @@ ajv@8.18.0, ajv@^8.0.0, ajv@^8.10.0, ajv@^8.9.0: json-schema-traverse "^1.0.0" require-from-string "^2.0.2" -ajv@^6.11.0, ajv@^6.12.4, ajv@^6.12.5: - version "6.12.6" - resolved "https://registry.yarnpkg.com/ajv/-/ajv-6.12.6.tgz#baf5a62e802b07d977034586f8c3baf5adf26df4" - integrity sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g== - dependencies: - fast-deep-equal "^3.1.1" - fast-json-stable-stringify "^2.0.0" - json-schema-traverse "^0.4.1" - uri-js "^4.2.2" - amd-name-resolver@^1.3.1: version "1.3.1" resolved "https://registry.yarnpkg.com/amd-name-resolver/-/amd-name-resolver-1.3.1.tgz#ffe71c683c6e7191fc4ae1bb3aaed15abea135d9" @@ -11434,11 +11166,6 @@ anymatch@^3.1.1, anymatch@^3.1.3, anymatch@~3.1.2: normalize-path "^3.0.0" picomatch "^2.0.4" -apache-md5@1.1.8: - version "1.1.8" - resolved "https://registry.yarnpkg.com/apache-md5/-/apache-md5-1.1.8.tgz#ea79c6feb03abfed42b2830dde06f75df5e3bbd9" - integrity sha512-FCAJojipPn0bXjuEpjOOOMN8FZDkxfWWp4JGN9mifU2IhxvKyXZYqpzPHdnTSUpmPDy+tsslB6Z1g+Vg6nVbYA== - app-module-path@^2.2.0: version "2.2.0" resolved "https://registry.yarnpkg.com/app-module-path/-/app-module-path-2.2.0.tgz#641aa55dfb7d6a6f0a8141c4b9c0aa50b6c24dd5" @@ -11688,23 +11415,11 @@ arrify@^2.0.0, arrify@^2.0.1: resolved "https://registry.yarnpkg.com/arrify/-/arrify-2.0.1.tgz#c9655e9331e0abcd588d2a7cad7e9956f66701fa" integrity sha512-3duEwti880xqi4eAMN8AyR4a0ByT90zoYdLlevfrvU43vb0YZwZVfxOgxWrLXXXpyugL0hNZc9G6BiB5B3nUug== -asn1@~0.2.3: - version "0.2.6" - resolved "https://registry.yarnpkg.com/asn1/-/asn1-0.2.6.tgz#0d3a7bb6e64e02a90c0303b31f292868ea09a08d" - integrity sha512-ix/FxPn0MDjeyJ7i/yoHGFt/EX6LyNbxSEhPPXODPL+KB0VPk86UYfL0lMdy+KCnv+fmvIzySwaK5COwqVbWTQ== - dependencies: - safer-buffer "~2.1.0" - assert-never@^1.2.1: version "1.2.1" resolved "https://registry.yarnpkg.com/assert-never/-/assert-never-1.2.1.tgz#11f0e363bf146205fb08193b5c7b90f4d1cf44fe" integrity sha512-TaTivMB6pYI1kXwrFlEhLeGfOqoDNdTxjCdwRfFFkEA30Eu+k48W34nlok2EYWJfFFzqaEmichdNM7th6M5HNw== -assert-plus@1.0.0, assert-plus@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/assert-plus/-/assert-plus-1.0.0.tgz#f12e0f3c5d77b0b1cdd9146942e4e96c1e4dd525" - integrity sha512-NfJ4UzBCcQGLDlQq7nHxH+tv3kyZ0hHQqF5BO6J7tNJeP5do1llPr8dZ8zHonfhAu0PHAdMkSo+8o0wxg9lZWw== - assertion-error@^2.0.1: version "2.0.1" resolved "https://registry.yarnpkg.com/assertion-error/-/assertion-error-2.0.1.tgz#f641a196b335690b1070bf00b6e7593fec190bf7" @@ -11893,11 +11608,6 @@ async-sema@^3.1.1: resolved "https://registry.yarnpkg.com/async-sema/-/async-sema-3.1.1.tgz#e527c08758a0f8f6f9f15f799a173ff3c40ea808" integrity sha512-tLRNUXati5MFePdAk8dw7Qt7DpxPB60ofAgn8WRhW6a2rcimZnYBP9oxHiv0OHy+Wz7kPMG+t4LGdt31+4EmGg== -async@3.2.6, async@^3.2.3, async@^3.2.4: - version "3.2.6" - resolved "https://registry.yarnpkg.com/async/-/async-3.2.6.tgz#1b0728e14929d51b85b449b7f06e27c1145e38ce" - integrity sha512-htCUDlxyyCLMgaM3xXg0C0LW2xqfuQ6p05pCEIsXuyQ+a1koYKTuBMzRNwmybfLgvJDMd0r1LTn4+E0Ti6C2AA== - async@^2.4.1, async@^2.6.4: version "2.6.4" resolved "https://registry.yarnpkg.com/async/-/async-2.6.4.tgz#706b7ff6084664cd7eae713f6f965433b5504221" @@ -11905,6 +11615,11 @@ async@^2.4.1, async@^2.6.4: dependencies: lodash "^4.17.14" +async@^3.2.3, async@^3.2.4: + version "3.2.6" + resolved "https://registry.yarnpkg.com/async/-/async-3.2.6.tgz#1b0728e14929d51b85b449b7f06e27c1145e38ce" + integrity sha512-htCUDlxyyCLMgaM3xXg0C0LW2xqfuQ6p05pCEIsXuyQ+a1koYKTuBMzRNwmybfLgvJDMd0r1LTn4+E0Ti6C2AA== + async@~0.2.9: version "0.2.10" resolved "https://registry.yarnpkg.com/async/-/async-0.2.10.tgz#b6bbe0b0674b9d719708ca38de8c237cb526c3d1" @@ -11948,21 +11663,11 @@ available-typed-arrays@^1.0.7: dependencies: possible-typed-array-names "^1.0.0" -aws-sign2@~0.7.0: - version "0.7.0" - resolved "https://registry.yarnpkg.com/aws-sign2/-/aws-sign2-0.7.0.tgz#b46e890934a9591f2d2f6f86d7e6a9f1b3fe76a8" - integrity sha512-08kcGqnYf/YmjoRhfxyu+CLxBjUtHLXLXX/vUfx9l2LYzG3c1m61nrpyFUZI6zeS+Li/wWMMidD9KgrqtGq3mA== - aws-ssl-profiles@^1.1.2: version "1.1.2" resolved "https://registry.yarnpkg.com/aws-ssl-profiles/-/aws-ssl-profiles-1.1.2.tgz#157dd77e9f19b1d123678e93f120e6f193022641" integrity sha512-NZKeq9AfyQvEeNlN0zSYAaWrmBffJh3IELMZfRpJVWgrpEbtEpnjvzqBPf+mxoI287JohRDoa+/nsfqqiZmF6g== -aws4@^1.8.0: - version "1.13.2" - resolved "https://registry.yarnpkg.com/aws4/-/aws4-1.13.2.tgz#0aa167216965ac9474ccfa83892cfb6b3e1e52ef" - integrity sha512-lHe62zvbTB5eEABUVi/AwVh0ZKY9rMMDhmm+eeyuuUQbQ3+J+fONVQOZyj+DdrvD4BY33uYniyRJ4UJIaSKAfw== - axios@1.15.0, axios@^1.12.0: version "1.15.0" resolved "https://registry.yarnpkg.com/axios/-/axios-1.15.0.tgz#0fcee91ef03d386514474904b27863b2c683bf4f" @@ -12323,18 +12028,6 @@ batch@0.6.1: resolved "https://registry.yarnpkg.com/batch/-/batch-0.6.1.tgz#dc34314f4e679318093fc760272525f94bf25c16" integrity sha1-3DQxT05nkxgJP8dgJyUl+UvyXBY= -bcrypt-pbkdf@^1.0.0: - version "1.0.2" - resolved "https://registry.yarnpkg.com/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.2.tgz#a4301d389b6a43f9b67ff3ca11a3f6637e360e9e" - integrity sha512-qeFIXtP4MSoi6NLqO12WfqARWWuCKi2Rn/9hJLEmtB5yTNr9DqFWkJRCf2qShWzPeAMRnOgCrq0sg/KLv5ES9w== - dependencies: - tweetnacl "^0.14.3" - -bcryptjs@2.4.3: - version "2.4.3" - resolved "https://registry.yarnpkg.com/bcryptjs/-/bcryptjs-2.4.3.tgz#9ab5627b93e60621ff7cdac5da9733027df1d0cb" - integrity sha512-V/Hy/X9Vt7f3BbPJEi8BdVFMByHi+jNXrYkW3huaybV/kQ0KJg0Y6PkEMbn+zeT+i+SiKZ/HMqJGIIt4LZDqNQ== - before-after-hook@^2.2.0: version "2.2.3" resolved "https://registry.yarnpkg.com/before-after-hook/-/before-after-hook-2.2.3.tgz#c51e809c81a4e354084422b9b26bad88249c517c" @@ -13046,13 +12739,6 @@ broccoli@^3.5.2: underscore.string "^3.2.2" watch-detector "^1.0.0" -browserify-zlib@^0.1.4: - version "0.1.4" - resolved "https://registry.yarnpkg.com/browserify-zlib/-/browserify-zlib-0.1.4.tgz#bb35f8a519f600e0fa6b8485241c979d0141fb2d" - integrity sha512-19OEpq7vWgsH6WkvkBJQDFvJS1uPcbFOQ4v9CU839dO+ZZXUZO6XpE6hNCqvlIIj+4fZvRiJ6DsAQ382GwiyTQ== - dependencies: - pako "~0.2.0" - browserslist@^4.0.0, browserslist@^4.14.5, browserslist@^4.20.0, browserslist@^4.21.3, browserslist@^4.21.4, browserslist@^4.23.0, browserslist@^4.24.0, browserslist@^4.27.0, browserslist@^4.28.1, browserslist@^4.9.1: version "4.28.1" resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-4.28.1.tgz#7f534594628c53c63101079e27e40de490456a95" @@ -13287,24 +12973,6 @@ cache-base@^1.0.1: union-value "^1.0.0" unset-value "^1.0.0" -cacheable-lookup@6.1.0: - version "6.1.0" - resolved "https://registry.yarnpkg.com/cacheable-lookup/-/cacheable-lookup-6.1.0.tgz#0330a543471c61faa4e9035db583aad753b36385" - integrity sha512-KJ/Dmo1lDDhmW2XDPMo+9oiy/CeqosPguPCrgcVzKyZrL6pM1gU2GmPY/xo6OQPTUaA/c0kwHuywB4E6nmT9ww== - -cacheable-request@7.0.2: - version "7.0.2" - resolved "https://registry.yarnpkg.com/cacheable-request/-/cacheable-request-7.0.2.tgz#ea0d0b889364a25854757301ca12b2da77f91d27" - integrity sha512-pouW8/FmiPQbuGpkXQ9BAPv/Mo5xDGANgSNXzTzJ8DrKGuXOssM4wIQRjfanNRh3Yu5cfYPvcorqbhg2KIJtew== - dependencies: - clone-response "^1.0.2" - get-stream "^5.1.0" - http-cache-semantics "^4.0.0" - keyv "^4.0.0" - lowercase-keys "^2.0.0" - normalize-url "^6.0.1" - responselike "^2.0.0" - calculate-cache-key-for-tree@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/calculate-cache-key-for-tree/-/calculate-cache-key-for-tree-2.0.0.tgz#7ac57f149a4188eacb0a45b210689215d3fef8d6" @@ -13413,11 +13081,6 @@ cardinal@^1.0.0: ansicolors "~0.2.1" redeyed "~1.0.0" -caseless@~0.12.0: - version "0.12.0" - resolved "https://registry.yarnpkg.com/caseless/-/caseless-0.12.0.tgz#1b681c21ff84033c826543090689420d187151dc" - integrity sha512-4tYFyifaFfGacoiObjJegolkwSU4xQNGbVgUiNYVUxbQ2x2lUsFvY4hVgVzGiIe6WLOPqycWXA40l+PWsxthUw== - ccount@^2.0.0: version "2.0.1" resolved "https://registry.yarnpkg.com/ccount/-/ccount-2.0.1.tgz#17a3bf82302e0870d6da43a01311a8bc02a3ecf5" @@ -13694,13 +13357,6 @@ client-only@0.0.1, client-only@^0.0.1: resolved "https://registry.yarnpkg.com/client-only/-/client-only-0.0.1.tgz#38bba5d403c41ab150bff64a95c85013cf73bca1" integrity sha512-IV3Ou0jSMzZrd3pZ48nLkT9DA7Ag1pnPzaiQhpW7c3RbcqqzvzzVu+L8gfqMp/8IM2MQtSiqaCxrrcfu8I8rMA== -clipanion@4.0.0-rc.4: - version "4.0.0-rc.4" - resolved "https://registry.yarnpkg.com/clipanion/-/clipanion-4.0.0-rc.4.tgz#7191a940e47ef197e5f18c9cbbe419278b5f5903" - integrity sha512-CXkMQxU6s9GklO/1f714dkKBMu1lopS1WFF0B8o4AxPykR1hpozxSiUZ5ZUeBjfPgCWqbcNOtZVFhB8Lkfp1+Q== - dependencies: - typanion "^3.8.0" - clipboardy@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/clipboardy/-/clipboardy-4.0.0.tgz#e73ced93a76d19dd379ebf1f297565426dffdca1" @@ -13737,13 +13393,6 @@ clone-deep@^4.0.1: kind-of "^6.0.2" shallow-clone "^3.0.0" -clone-response@^1.0.2: - version "1.0.3" - resolved "https://registry.yarnpkg.com/clone-response/-/clone-response-1.0.3.tgz#af2032aa47816399cf5f0a1d0db902f517abb8c3" - integrity sha512-ROoL94jJH2dUVML2Y/5PEDNaSHgeOdSDicUyS7izcF63G6sTc/FTjLub4b8Il9S8S0beOfYt0TaA5qvFK+w0wA== - dependencies: - mimic-response "^1.0.0" - clone@^1.0.2: version "1.0.4" resolved "https://registry.yarnpkg.com/clone/-/clone-1.0.4.tgz#da309cc263df15994c688ca902179ca3c7cd7c7e" @@ -13846,7 +13495,7 @@ colorette@2.0.19: resolved "https://registry.yarnpkg.com/colorette/-/colorette-2.0.19.tgz#cdf044f47ad41a0f4b56b3a0d5b4e6e1a2d5a798" integrity sha512-3tlv/dIP7FWvj3BsbHrGLJ6l/oKh1O3TcgBqMn+yyCagOxc23fyzDS6HypQbgxWbkpDnf52p1LuR4eWDQ/K9WQ== -colorette@2.0.20, colorette@^2.0.10: +colorette@^2.0.10: version "2.0.20" resolved "https://registry.yarnpkg.com/colorette/-/colorette-2.0.20.tgz#9eb793e6833067f7235902fcd3b09917a000a95a" integrity sha512-IfEDxwoWIjkeXL1eXcDiow4UbKjhLdq6/EuSVR9GMN7KVH3r9gQ83e73hsz1Nd1T3ijd5xv1wcWRYO+D6kCI2w== @@ -13869,7 +13518,7 @@ colorspace@1.1.x: color "^3.1.3" text-hex "1.0.x" -combined-stream@^1.0.8, combined-stream@~1.0.6: +combined-stream@^1.0.8: version "1.0.8" resolved "https://registry.yarnpkg.com/combined-stream/-/combined-stream-1.0.8.tgz#c3d45a8b34fd730631a110a8a2520682b31d5a7f" integrity sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg== @@ -13981,7 +13630,7 @@ compressible@~2.0.18: dependencies: mime-db ">= 1.43.0 < 2" -compression@1.8.1, compression@^1.7.4: +compression@^1.7.4: version "1.8.1" resolved "https://registry.yarnpkg.com/compression/-/compression-1.8.1.tgz#4a45d909ac16509195a9a28bd91094889c180d79" integrity sha512-9mAqGPHLakhCLeNyxPkK4xVo746zQ/czLH1Ky+vkitMnWfWZps8r0qXuwhwizagCRttsL4lfG4pIOvaWLpAP0w== @@ -14227,7 +13876,7 @@ core-object@^3.1.5: dependencies: chalk "^2.0.0" -core-util-is@1.0.2, core-util-is@~1.0.0: +core-util-is@~1.0.0: version "1.0.2" resolved "https://registry.yarnpkg.com/core-util-is/-/core-util-is-1.0.2.tgz#b5fd54220aa2bc5ab57aab7140c940754503c1a7" integrity sha1-tf1UIgqivFq1eqtxQMlAdUUDwac= @@ -14556,13 +14205,6 @@ dag-map@^2.0.2: resolved "https://registry.yarnpkg.com/dag-map/-/dag-map-2.0.2.tgz#9714b472de82a1843de2fba9b6876938cab44c68" integrity sha1-lxS0ct6CoYQ94vuptodpOMq0TGg= -dashdash@^1.12.0: - version "1.14.1" - resolved "https://registry.yarnpkg.com/dashdash/-/dashdash-1.14.1.tgz#853cfa0f7cbe2fed5de20326b8dd581035f6e2f0" - integrity sha512-jRFi8UDGo6j+odZiEpjazZaWqEal3w/basFjQHQEwVtZJGDpxbH1MeYluwCS8Xq5wmLJooDlMgvVarmWfGM44g== - dependencies: - assert-plus "^1.0.0" - data-uri-to-buffer@^3.0.1: version "3.0.1" resolved "https://registry.yarnpkg.com/data-uri-to-buffer/-/data-uri-to-buffer-3.0.1.tgz#594b8973938c5bc2c33046535785341abc4f3636" @@ -14622,11 +14264,6 @@ dax-sh@^0.43.0: "@deno/shim-deno" "~0.19.0" undici-types "^5.26" -dayjs@1.11.18: - version "1.11.18" - resolved "https://registry.yarnpkg.com/dayjs/-/dayjs-1.11.18.tgz#835fa712aac52ab9dec8b1494098774ed7070a11" - integrity sha512-zFBQ7WFRvVRhKcWoUh+ZA1g2HVgUbsZm9sbddh8EC5iv93sui8DVVz1Npvz+r6meo9VKfa8NyLWBsQK1VvIKPA== - db0@^0.3.4: version "0.3.4" resolved "https://registry.yarnpkg.com/db0/-/db0-0.3.4.tgz#fb109b0d9823ba1f787a4a3209fa1f3cf9ae9cf9" @@ -14644,7 +14281,7 @@ debug@2, debug@2.6.9, debug@^2.1.0, debug@^2.1.1, debug@^2.1.3, debug@^2.2.0, de dependencies: ms "2.0.0" -debug@4, debug@4.4.3, debug@4.x, debug@^4, debug@^4.0.0, debug@^4.1.0, debug@^4.1.1, debug@^4.3.1, debug@^4.3.2, debug@^4.3.3, debug@^4.3.4, debug@^4.3.5, debug@^4.4.0, debug@^4.4.1, debug@^4.4.3, debug@~4.4.1: +debug@4, debug@4.x, debug@^4, debug@^4.0.0, debug@^4.1.0, debug@^4.1.1, debug@^4.3.1, debug@^4.3.2, debug@^4.3.3, debug@^4.3.4, debug@^4.3.5, debug@^4.4.0, debug@^4.4.1, debug@^4.4.3, debug@~4.4.1: version "4.4.3" resolved "https://registry.yarnpkg.com/debug/-/debug-4.4.3.tgz#c6ae432d9bd9662582fce08709b038c58e9e3d6a" integrity sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA== @@ -14658,13 +14295,6 @@ debug@4.3.4: dependencies: ms "2.1.2" -debug@4.4.1: - version "4.4.1" - resolved "https://registry.yarnpkg.com/debug/-/debug-4.4.1.tgz#e5a8bc6cbc4c6cd3e64308b0693a3d4fa550189b" - integrity sha512-KcKCqiftBJcZr++7ykoDIEwSa3XWowTfNPo92BYxjXiyYEVrUQh2aLyhxBCwww+heortUFxEJYcRzosstTEBYQ== - dependencies: - ms "^2.1.3" - debug@^3.0.1, debug@^3.1.0, debug@^3.2.6, debug@^3.2.7: version "3.2.7" resolved "https://registry.yarnpkg.com/debug/-/debug-3.2.7.tgz#72580b7e9145fb39b6676f9c5e5fb100b934179a" @@ -14797,11 +14427,6 @@ defaults@^1.0.3: dependencies: clone "^1.0.2" -defer-to-connect@^2.0.0: - version "2.0.1" - resolved "https://registry.yarnpkg.com/defer-to-connect/-/defer-to-connect-2.0.1.tgz#8016bdb4143e4632b77a3449c6236277de520587" - integrity sha512-4tvttepXG1VaYGrRibk5EwJd1t4udunSOVMdLSAL6mId1ix438oPwPZMALY41FCijukO1L0twNcGsdzS7dHgDg== - define-data-property@^1.0.1, define-data-property@^1.1.4: version "1.1.4" resolved "https://registry.yarnpkg.com/define-data-property/-/define-data-property-1.1.4.tgz#894dc141bb7d3060ae4366f6a0107e68fbe48c5e" @@ -15279,16 +14904,6 @@ duplexer@^0.1.2: resolved "https://registry.yarnpkg.com/duplexer/-/duplexer-0.1.2.tgz#3abe43aef3835f8ae077d136ddce0f276b0400e6" integrity sha512-jtD6YG370ZCIi/9GTaJKQxWTZD045+4R4hTk/x1UyoqadyJ9x9CgSi1RlVDQF8U2sxLLSnFkCaMihqljHIWgMg== -duplexify@^3.5.0, duplexify@^3.6.0: - version "3.7.1" - resolved "https://registry.yarnpkg.com/duplexify/-/duplexify-3.7.1.tgz#2a4df5317f6ccfd91f86d6fd25d8d8a103b88309" - integrity sha512-07z8uv2wMyS51kKhD1KsdXJg5WQ6t93RneqRxUHnskXVtlYYkLqM0gqStQZ3pj073g687jPCHrqNfCzawLYh5g== - dependencies: - end-of-stream "^1.0.0" - inherits "^2.0.1" - readable-stream "^2.0.0" - stream-shift "^1.0.0" - duplexify@^4.0.0, duplexify@^4.1.1: version "4.1.1" resolved "https://registry.yarnpkg.com/duplexify/-/duplexify-4.1.1.tgz#7027dc374f157b122a8ae08c2d3ea4d2d953aa61" @@ -15304,14 +14919,6 @@ eastasianwidth@^0.2.0: resolved "https://registry.yarnpkg.com/eastasianwidth/-/eastasianwidth-0.2.0.tgz#696ce2ec0aa0e6ea93a397ffcf24aa7840c827cb" integrity sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA== -ecc-jsbn@~0.1.1: - version "0.1.2" - resolved "https://registry.yarnpkg.com/ecc-jsbn/-/ecc-jsbn-0.1.2.tgz#3a83a904e54353287874c564b7549386849a98c9" - integrity sha512-eh9O+hwRHNbG4BLTjEl3nw044CkGm5X6LoaCf7LPp7UU8Qrt47JYNi6nPX8xjW97TKGKm1ouctg0QSpZe9qrnw== - dependencies: - jsbn "~0.1.0" - safer-buffer "^2.1.0" - ecdsa-sig-formatter@1.0.11, ecdsa-sig-formatter@^1.0.11: version "1.0.11" resolved "https://registry.yarnpkg.com/ecdsa-sig-formatter/-/ecdsa-sig-formatter-1.0.11.tgz#ae0f0fa2d85045ef14a817daa3ce9acd0489e5bf" @@ -15977,7 +15584,7 @@ encoding@^0.1.13: dependencies: iconv-lite "^0.6.2" -end-of-stream@^1.0.0, end-of-stream@^1.1.0, end-of-stream@^1.4.1: +end-of-stream@^1.1.0, end-of-stream@^1.4.1: version "1.4.5" resolved "https://registry.yarnpkg.com/end-of-stream/-/end-of-stream-1.4.5.tgz#7344d711dea40e0b74abc2ed49778743ccedb08c" integrity sha512-ooEGc6HP26xXq/N+GCGOT0JKCLDGrq2bQUZrQ7gyrJiZANJ/8YDTxTpQBXGMn+WbIQXNVpyWymm7KYVICQnyOg== @@ -16064,11 +15671,6 @@ env-runner@^0.1.6: httpxy "^0.3.1" srvx "^0.11.9" -envinfo@7.21.0: - version "7.21.0" - resolved "https://registry.yarnpkg.com/envinfo/-/envinfo-7.21.0.tgz#04a251be79f92548541f37d13c8b6f22940c3bae" - integrity sha512-Lw7I8Zp5YKHFCXL7+Dz95g4CcbMEpgvqZNNq3AmlT5XAV6CgAAk6gyAMqn2zjw08K9BHfcNuKrMiCPLByGafow== - err-code@^2.0.2: version "2.0.3" resolved "https://registry.yarnpkg.com/err-code/-/err-code-2.0.3.tgz#23c2f3b756ffdfc608d30e27c9a941024807e7f9" @@ -17308,12 +16910,41 @@ expect-type@^1.2.1: resolved "https://registry.yarnpkg.com/expect-type/-/expect-type-1.2.1.tgz#af76d8b357cf5fa76c41c09dafb79c549e75f71f" integrity sha512-/kP8CAwxzLVEeFrMm4kMmy4CCDlpipyA7MYLVrdJIkV0fYF0UaigQHRsxHiuY/GEea+bh4KSv3TIlgr+2UL6bw== -express-rate-limit@5.5.1: - version "5.5.1" - resolved "https://registry.yarnpkg.com/express-rate-limit/-/express-rate-limit-5.5.1.tgz#110c23f6a65dfa96ab468eda95e71697bc6987a2" - integrity sha512-MTjE2eIbHv5DyfuFz4zLYWxpqVhEhkTiwFGuB74Q9CSou2WHO52nlE5y3Zlg6SIsiYUIPj6ifFxnkPz6O3sIUg== +express@5.2.1: + version "5.2.1" + resolved "https://registry.yarnpkg.com/express/-/express-5.2.1.tgz#8f21d15b6d327f92b4794ecf8cb08a72f956ac04" + integrity sha512-hIS4idWWai69NezIdRt2xFVofaF4j+6INOpJlVOLDO8zXGpUVEVzIYk12UUi2JzjEzWL3IOAxcTubgz9Po0yXw== + dependencies: + accepts "^2.0.0" + body-parser "^2.2.1" + content-disposition "^1.0.0" + content-type "^1.0.5" + cookie "^0.7.1" + cookie-signature "^1.2.1" + debug "^4.4.0" + depd "^2.0.0" + encodeurl "^2.0.0" + escape-html "^1.0.3" + etag "^1.8.1" + finalhandler "^2.1.0" + fresh "^2.0.0" + http-errors "^2.0.0" + merge-descriptors "^2.0.0" + mime-types "^3.0.0" + on-finished "^2.4.1" + once "^1.4.0" + parseurl "^1.3.3" + proxy-addr "^2.0.7" + qs "^6.14.0" + range-parser "^1.2.1" + router "^2.2.0" + send "^1.1.0" + serve-static "^2.2.0" + statuses "^2.0.1" + type-is "^2.0.1" + vary "^1.1.2" -express@4.22.1, express@^4.10.7, express@^4.17.3, express@^4.18.1, express@^4.21.2: +express@^4.10.7, express@^4.17.3, express@^4.18.1, express@^4.21.2: version "4.22.1" resolved "https://registry.yarnpkg.com/express/-/express-4.22.1.tgz#1de23a09745a4fffdb39247b344bb5eaff382069" integrity sha512-F2X8g9P1X7uCPZMA3MVf9wcTqlyNp7IhH5qPCI0izhaOIYXaW9L535tGA3qmjRzpH+bZczqq7hVKxTR4NWnu+g== @@ -17350,40 +16981,6 @@ express@4.22.1, express@^4.10.7, express@^4.17.3, express@^4.18.1, express@^4.21 utils-merge "1.0.1" vary "~1.1.2" -express@5.2.1: - version "5.2.1" - resolved "https://registry.yarnpkg.com/express/-/express-5.2.1.tgz#8f21d15b6d327f92b4794ecf8cb08a72f956ac04" - integrity sha512-hIS4idWWai69NezIdRt2xFVofaF4j+6INOpJlVOLDO8zXGpUVEVzIYk12UUi2JzjEzWL3IOAxcTubgz9Po0yXw== - dependencies: - accepts "^2.0.0" - body-parser "^2.2.1" - content-disposition "^1.0.0" - content-type "^1.0.5" - cookie "^0.7.1" - cookie-signature "^1.2.1" - debug "^4.4.0" - depd "^2.0.0" - encodeurl "^2.0.0" - escape-html "^1.0.3" - etag "^1.8.1" - finalhandler "^2.1.0" - fresh "^2.0.0" - http-errors "^2.0.0" - merge-descriptors "^2.0.0" - mime-types "^3.0.0" - on-finished "^2.4.1" - once "^1.4.0" - parseurl "^1.3.3" - proxy-addr "^2.0.7" - qs "^6.14.0" - range-parser "^1.2.1" - router "^2.2.0" - send "^1.1.0" - serve-static "^2.2.0" - statuses "^2.0.1" - type-is "^2.0.1" - vary "^1.1.2" - exsolve@^1.0.5, exsolve@^1.0.7, exsolve@^1.0.8: version "1.0.8" resolved "https://registry.yarnpkg.com/exsolve/-/exsolve-1.0.8.tgz#7f5e34da61cd1116deda5136e62292c096f50613" @@ -17404,7 +17001,7 @@ extend-shallow@^3.0.0, extend-shallow@^3.0.2: assign-symbols "^1.0.0" is-extendable "^1.0.1" -extend@^3.0.0, extend@^3.0.2, extend@~3.0.2: +extend@^3.0.0, extend@^3.0.2: version "3.0.2" resolved "https://registry.yarnpkg.com/extend/-/extend-3.0.2.tgz#f8b1136b4071fbd8eb140aff858b1019ec2915fa" integrity sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g== @@ -17447,16 +17044,6 @@ extract-stack@^2.0.0: resolved "https://registry.yarnpkg.com/extract-stack/-/extract-stack-2.0.0.tgz#11367bc865bfcd9bc0db3123e5edb57786f11f9b" integrity sha512-AEo4zm+TenK7zQorGK1f9mJ8L14hnTDi2ZQPR+Mub1NX8zimka1mXpV5LpH8x9HoUmFSHZCfLHqWvp0Y4FxxzQ== -extsprintf@1.3.0: - version "1.3.0" - resolved "https://registry.yarnpkg.com/extsprintf/-/extsprintf-1.3.0.tgz#96918440e3041a7a414f8c52e3c574eb3c3e1e05" - integrity sha512-11Ndz7Nv+mvAC1j0ktTa7fAb0vLyGGX+rMHNBYQviQDGU0Hw7lhctJANqbPhu9nV9/izT/IntTgZ7Im/9LJs9g== - -extsprintf@^1.2.0: - version "1.4.1" - resolved "https://registry.yarnpkg.com/extsprintf/-/extsprintf-1.4.1.tgz#8d172c064867f235c0c84a596806d279bf4bcc07" - integrity sha512-Wrk35e8ydCKDj/ArClo1VrPVmN8zph5V4AtHwIuHhvMXsKf73UT3BOD+azBIW+3wOJ4FhEH7zyaJCFvChjYvMA== - fake-indexeddb@^6.2.4: version "6.2.4" resolved "https://registry.yarnpkg.com/fake-indexeddb/-/fake-indexeddb-6.2.4.tgz#cf3860b6b37ddc3b33e7840be00a61ed094486a5" @@ -17976,17 +17563,7 @@ foreground-child@^3.1.0: cross-spawn "^7.0.6" 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" - integrity sha512-j0KLYPhm6zeac4lz3oJ3o65qvgQCcPubiyotZrXqEaG4hNagNYO8qdlUrX5vwqv9ohqeT/Z3j6+yW067yWWdUw== - -form-data-encoder@1.7.2: - version "1.7.2" - resolved "https://registry.yarnpkg.com/form-data-encoder/-/form-data-encoder-1.7.2.tgz#1f1ae3dccf58ed4690b86d87e4f57c654fbab040" - integrity sha512-qfqtYan3rxrnCk1VYaA4H+Ms9xdpPqvLZa6xmMgFvhO32x7/3J/ExcTd6qpxM0vH2GdMI+poehyBZvqfMTto8A== - -form-data@^4.0.0, form-data@^4.0.4, form-data@^4.0.5, form-data@~4.0.4: +form-data@^4.0.0, form-data@^4.0.4, form-data@^4.0.5: version "4.0.5" resolved "https://registry.yarnpkg.com/form-data/-/form-data-4.0.5.tgz#b49e48858045ff4cbf6b03e1805cebcad3679053" integrity sha512-8RipRLol37bNs2bhoV67fiTEvdTrbMUYcFTiy3+wuuOnUog2QBHCZWXDRijWQfAkhBj2Uf5UnVaiWwA5vdd82w== @@ -18371,14 +17948,14 @@ get-stream@^4.0.0: dependencies: pump "^3.0.0" -get-stream@^5.0.0, get-stream@^5.1.0: +get-stream@^5.0.0: version "5.2.0" resolved "https://registry.yarnpkg.com/get-stream/-/get-stream-5.2.0.tgz#4966a1795ee5ace65e706c4b7beb71257d6e22d3" integrity sha512-nBF+F1rAZVCu/p7rjzgA+Yb4lfYXrpl7a6VmJrU8wF9I1CKvP/QwPNZHnOlwbTkY6dvtFIzFMSyQXbLoTQPRpA== dependencies: pump "^3.0.0" -get-stream@^6.0.0, get-stream@^6.0.1: +get-stream@^6.0.0: version "6.0.1" resolved "https://registry.yarnpkg.com/get-stream/-/get-stream-6.0.1.tgz#a262d8eef67aced57c2852ad6167526a43cbf7b7" integrity sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg== @@ -18407,13 +17984,6 @@ getopts@2.3.0: resolved "https://registry.yarnpkg.com/getopts/-/getopts-2.3.0.tgz#71e5593284807e03e2427449d4f6712a268666f4" integrity sha512-5eDf9fuSXwxBL6q5HX+dhDj+dslFGWzU5thZ9kNKUkcPtaPdatmUFKwHFrLb/uf/WpA4BHET+AX3Scl56cAjpA== -getpass@^0.1.1: - version "0.1.7" - resolved "https://registry.yarnpkg.com/getpass/-/getpass-0.1.7.tgz#5eff8e3e684d569ae4cb2b1282604e8ba62149fa" - integrity sha512-0fzj9JxOLfJ+XGLhR8ze3unN0KZCgZwiSSDz168VERjK8Wl8kVSdcu2kspd4s4wtAa1y/qrVRiAA0WclVsu0ng== - dependencies: - assert-plus "^1.0.0" - giget@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/giget/-/giget-2.0.0.tgz#395fc934a43f9a7a29a29d55b99f23e30c14f195" @@ -18703,24 +18273,6 @@ gopd@^1.0.1, gopd@^1.2.0: resolved "https://registry.yarnpkg.com/gopd/-/gopd-1.2.0.tgz#89f56b8217bdbc8802bd299df6d7f1081d7e51a1" integrity sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg== -got-cjs@12.5.4: - version "12.5.4" - resolved "https://registry.yarnpkg.com/got-cjs/-/got-cjs-12.5.4.tgz#b46419c0e8e5fb5503b926941807408049ae2e11" - integrity sha512-Uas6lAsP8bRCt5WXGMhjFf/qEHTrm4v4qxGR02rLG2kdG9qedctvlkdwXVcDJ7Cs84X+r4dPU7vdwGjCaspXug== - dependencies: - "@sindresorhus/is" "4.6.0" - "@szmarczak/http-timer" "4.0.6" - "@types/responselike" "1.0.0" - cacheable-lookup "6.1.0" - cacheable-request "7.0.2" - decompress-response "^6.0.0" - form-data-encoder "1.7.2" - get-stream "^6.0.1" - http2-wrapper "^2.1.10" - lowercase-keys "2.0.0" - p-cancelable "2.1.1" - responselike "2.0.1" - graceful-fs@^4.1.2, graceful-fs@^4.1.3, graceful-fs@^4.1.5, graceful-fs@^4.1.6, graceful-fs@^4.2.0, graceful-fs@^4.2.11, graceful-fs@^4.2.4, graceful-fs@^4.2.6, graceful-fs@^4.2.9: version "4.2.11" resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.11.tgz#4183e4e8bf08bb6e05bbb2f7d2e0c8f712ca40e3" @@ -18785,18 +18337,6 @@ gtoken@^7.0.0: gaxios "^6.0.0" jws "^4.0.0" -gunzip-maybe@1.4.2: - version "1.4.2" - resolved "https://registry.yarnpkg.com/gunzip-maybe/-/gunzip-maybe-1.4.2.tgz#b913564ae3be0eda6f3de36464837a9cd94b98ac" - integrity sha512-4haO1M4mLO91PW57BMsDFf75UmwoRX0GkdD+Faw+Lr+r/OZrOCS0pIBwOL1xCKQqnQzbNFGgK2V2CpBUPeFNTw== - dependencies: - browserify-zlib "^0.1.4" - is-deflate "^1.0.0" - is-gzip "^1.0.0" - peek-stream "^1.1.0" - pumpify "^1.3.3" - through2 "^2.0.3" - gzip-size@^6.0.0: version "6.0.0" resolved "https://registry.yarnpkg.com/gzip-size/-/gzip-size-6.0.0.tgz#065367fd50c239c0671cbcbad5be3e2eeb10e462" @@ -18854,7 +18394,7 @@ handle-thing@^2.0.0: resolved "https://registry.yarnpkg.com/handle-thing/-/handle-thing-2.0.1.tgz#857f79ce359580c340d43081cc648970d0bb234e" integrity sha512-9Qn4yBxelxoh2Ow62nP+Ka/kMnOXRi8BXnRaUwezLNhqelnN49xKz4F/dPP8OYLxLxq6JDtZb2i9XznUQbNPTg== -handlebars@4.7.9, handlebars@^4.0.4, handlebars@^4.3.1, handlebars@^4.7.3: +handlebars@^4.0.4, handlebars@^4.3.1, handlebars@^4.7.3: version "4.7.9" resolved "https://registry.yarnpkg.com/handlebars/-/handlebars-4.7.9.tgz#6f139082ab58dc4e5a0e51efe7db5ae890d56a0f" integrity sha512-4E71E0rpOaQuJR2A3xDZ+GM1HyWYv1clR58tC8emQNeQe3RH7MAzSbat+V0wG78LQBo6m6bzSG/L4pBuCsgnUQ== @@ -19311,7 +18851,7 @@ htmlparser2@^6.1.0: domutils "^2.5.2" entities "^2.0.0" -http-cache-semantics@^4.0.0, http-cache-semantics@^4.1.0, http-cache-semantics@^4.1.1: +http-cache-semantics@^4.1.0, http-cache-semantics@^4.1.1: version "4.2.0" resolved "https://registry.yarnpkg.com/http-cache-semantics/-/http-cache-semantics-4.2.0.tgz#205f4db64f8562b76a4ff9235aa5279839a09dd5" integrity sha512-dTxcvPXqPvXBQpq5dUr6mEMJX4oIEFv6bwom3FDwKRDsuIjjJGANqhBuoAn9c1RQJIdAKav33ED65E2ys+87QQ== @@ -19321,18 +18861,7 @@ http-deceiver@^1.2.7: resolved "https://registry.yarnpkg.com/http-deceiver/-/http-deceiver-1.2.7.tgz#fa7168944ab9a519d337cb0bec7284dc3e723d87" integrity sha1-+nFolEq5pRnTN8sL7HKE3D5yPYc= -http-errors@2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/http-errors/-/http-errors-2.0.0.tgz#b7774a1486ef73cf7667ac9ae0858c012c57b9d3" - integrity sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ== - dependencies: - depd "2.0.0" - inherits "2.0.4" - setprototypeof "1.2.0" - statuses "2.0.1" - toidentifier "1.0.1" - -http-errors@2.0.1, http-errors@^2.0.0, http-errors@^2.0.1, http-errors@~2.0.0, http-errors@~2.0.1: +http-errors@^2.0.0, http-errors@^2.0.1, http-errors@~2.0.0, http-errors@~2.0.1: version "2.0.1" resolved "https://registry.yarnpkg.com/http-errors/-/http-errors-2.0.1.tgz#36d2f65bc909c8790018dd36fb4d93da6caae06b" integrity sha512-4FbRdAX+bSdmo4AUFuS0WNiPz8NgFt+r8ThgNWmlrjQjt1Q7ZR9+zTlce2859x4KSXrwIsaeTqDoKQmtP8pLmQ== @@ -19409,20 +18938,6 @@ http-shutdown@^1.2.2: resolved "https://registry.yarnpkg.com/http-shutdown/-/http-shutdown-1.2.2.tgz#41bc78fc767637c4c95179bc492f312c0ae64c5f" integrity sha512-S9wWkJ/VSY9/k4qcjG318bqJNruzE4HySUhFYknwmu6LBP97KLLfwNf+n4V1BHurvFNkSKLFnK/RsuUnRTf9Vw== -http-signature@~1.4.0: - version "1.4.0" - resolved "https://registry.yarnpkg.com/http-signature/-/http-signature-1.4.0.tgz#dee5a9ba2bf49416abc544abd6d967f6a94c8c3f" - integrity sha512-G5akfn7eKbpDN+8nPS/cb57YeA1jLTVxjpCj7tmm3QKPdyDy7T+qSC40e9ptydSWvkwjSXw1VbkpyEm39ukeAg== - dependencies: - assert-plus "^1.0.0" - jsprim "^2.0.2" - sshpk "^1.18.0" - -http-status-codes@2.3.0: - version "2.3.0" - resolved "https://registry.yarnpkg.com/http-status-codes/-/http-status-codes-2.3.0.tgz#987fefb28c69f92a43aecc77feec2866349a8bfc" - integrity sha512-RJ8XvFvpPM/Dmc5SV+dC4y5PCeOhT3x1Hq0NU3rjGeg5a/CqlhZ7uudknPwZFz4aeAXDcbAyaeP7GAo9lvngtA== - http-terminator@^3.2.0: version "3.2.0" resolved "https://registry.yarnpkg.com/http-terminator/-/http-terminator-3.2.0.tgz#bc158d2694b733ca4fbf22a35065a81a609fb3e9" @@ -19433,14 +18948,6 @@ http-terminator@^3.2.0: roarr "^7.0.4" type-fest "^2.3.3" -http2-wrapper@^2.1.10: - version "2.2.1" - resolved "https://registry.yarnpkg.com/http2-wrapper/-/http2-wrapper-2.2.1.tgz#310968153dcdedb160d8b72114363ef5fce1f64a" - integrity sha512-V5nVw1PAOgfI3Lmeaj2Exmeg7fenjhRUgz1lPSezy1CuhPYbgQtbQj4jZfEAEMlaL+vupsvhjqCyjzob0yxsmQ== - dependencies: - quick-lru "^5.1.1" - resolve-alpn "^1.2.0" - https-proxy-agent@5.0.1, https-proxy-agent@^5.0.0, https-proxy-agent@^5.0.1: version "5.0.1" resolved "https://registry.yarnpkg.com/https-proxy-agent/-/https-proxy-agent-5.0.1.tgz#c59ef224a04fe8b754f3db0063a25ea30d0005d6" @@ -19646,7 +19153,7 @@ inflight@^1.0.4: once "^1.3.0" wrappy "1" -inherits@2, inherits@2.0.4, inherits@^2.0.1, inherits@^2.0.3, inherits@^2.0.4, inherits@~2.0.1, inherits@~2.0.3, inherits@~2.0.4: +inherits@2, inherits@^2.0.1, inherits@^2.0.3, inherits@^2.0.4, inherits@~2.0.1, inherits@~2.0.3, inherits@~2.0.4: version "2.0.4" resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.4.tgz#0fa2c64f932917c3433a0ded55363aae37416b7c" integrity sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ== @@ -19979,11 +19486,6 @@ is-date-object@^1.0.5, is-date-object@^1.1.0: call-bound "^1.0.2" has-tostringtag "^1.0.2" -is-deflate@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/is-deflate/-/is-deflate-1.0.0.tgz#c862901c3c161fb09dac7cdc7e784f80e98f2f14" - integrity sha512-YDoFpuZWu1VRXlsnlYMzKyVRITXj7Ej/V9gXQ2/pAe7X1J7M/RNOqaIYi6qUn+B7nGyB9pDXrv02dsB58d2ZAQ== - is-descriptor@^0.1.0: version "0.1.6" resolved "https://registry.yarnpkg.com/is-descriptor/-/is-descriptor-0.1.6.tgz#366d8240dde487ca51823b1ab9f07a10a78251ca" @@ -20069,11 +19571,6 @@ is-glob@^4.0.0, is-glob@^4.0.1, is-glob@^4.0.3, is-glob@~4.0.1: dependencies: is-extglob "^2.1.1" -is-gzip@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/is-gzip/-/is-gzip-1.0.0.tgz#6ca8b07b99c77998025900e555ced8ed80879a83" - integrity sha512-rcfALRIb1YewtnksfRIHGcIY93QnK8BIQ/2c9yDYcG/Y6+vRoJuTWBmmSEbyLLYtXm7q35pHOHbZFQBaLrhlWQ== - is-inside-container@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/is-inside-container/-/is-inside-container-1.0.0.tgz#e81fba699662eb31dbdaf26766a61d4814717ea4" @@ -20198,11 +19695,6 @@ is-potential-custom-element-name@^1.0.1: resolved "https://registry.yarnpkg.com/is-potential-custom-element-name/-/is-potential-custom-element-name-1.0.1.tgz#171ed6f19e3ac554394edf78caa05784a45bebb5" integrity sha512-bCYeRA2rVibKZd+s2625gGnGF/t7DSqDs4dP7CrLA1m7jKWz6pps0LpYLJN8Q64HtmPKJ1hrN3nzPNKFEKOUiQ== -is-promise@^2.1.0: - version "2.2.2" - resolved "https://registry.yarnpkg.com/is-promise/-/is-promise-2.2.2.tgz#39ab959ccbf9a774cf079f7b40c7a26f763135f1" - integrity sha512-+lP4/6lKUBfQjZ2pdxThZvLUAafmZb8OAxFb8XXtiQmS35INgr85hdOGoEs124ez1FCnZJt6jau/T+alh58QFQ== - is-promise@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/is-promise/-/is-promise-4.0.0.tgz#42ff9f84206c1991d26debf520dd5c01042dd2f3" @@ -20307,7 +19799,7 @@ is-typed-array@^1.1.13, is-typed-array@^1.1.14, is-typed-array@^1.1.15, is-typed dependencies: which-typed-array "^1.1.16" -is-typedarray@^1.0.0, is-typedarray@~1.0.0: +is-typedarray@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/is-typedarray/-/is-typedarray-1.0.0.tgz#e479c80858df0c1b11ddda6940f96011fcda4a9a" integrity sha1-5HnICFjfDBsR3dppQPlgEfzaSpo= @@ -20445,11 +19937,6 @@ isobject@^3.0.0, isobject@^3.0.1: resolved "https://registry.yarnpkg.com/isobject/-/isobject-3.0.1.tgz#4e431e92b11a9731636aa1f9c8d1ccbcfdab78df" integrity sha1-TkMekrEalzFjaqH5yNHMvP2reN8= -isstream@~0.1.2: - version "0.1.2" - resolved "https://registry.yarnpkg.com/isstream/-/isstream-0.1.2.tgz#47e63f7af55afa6f92e1500e690eb8b8529c099a" - integrity sha512-Yljz7ffyPbrLpLngrMtZ7NduUgVvi6wG9RJ9IUcyCd59YQ911PBJphODUcbOVbqYfxe1wuYf/LJ8PauMRwsM/g== - istanbul-lib-coverage@^3.0.0, istanbul-lib-coverage@^3.2.0, istanbul-lib-coverage@^3.2.2: version "3.2.2" resolved "https://registry.yarnpkg.com/istanbul-lib-coverage/-/istanbul-lib-coverage-3.2.2.tgz#2d166c4b0644d43a39f04bf6c2edd1e585f31756" @@ -20624,13 +20111,6 @@ js-tokens@^9.0.1: resolved "https://registry.yarnpkg.com/js-tokens/-/js-tokens-9.0.1.tgz#2ec43964658435296f6761b34e10671c2d9527f4" integrity sha512-mxa9E9ITFOt0ban3j6L5MpjwegGz6lBQmM1IJkWeBZGcMxto50+eWdjC/52xDbS2vy0k7vIMK0Fe2wfL9OQSpQ== -js-yaml@4.1.1, js-yaml@^4.1.0: - version "4.1.1" - resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-4.1.1.tgz" - integrity sha512-qQKT4zQxXl8lLwBtHMWwaTcGfFOZviOJet3Oy/xmGk2gZH677CJM9EvtfdSkgWcATZhj/55JZ0rmy3myCT5lsA== - dependencies: - argparse "^2.0.1" - js-yaml@^3.10.0, js-yaml@^3.13.0, js-yaml@^3.13.1, js-yaml@^3.2.5, js-yaml@^3.2.7: version "3.14.2" resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-3.14.2.tgz" @@ -20639,10 +20119,12 @@ js-yaml@^3.10.0, js-yaml@^3.13.0, js-yaml@^3.13.1, js-yaml@^3.2.5, js-yaml@^3.2. argparse "^1.0.7" esprima "^4.0.0" -jsbn@~0.1.0: - version "0.1.1" - resolved "https://registry.yarnpkg.com/jsbn/-/jsbn-0.1.1.tgz#a5e654c2e5a2deb5f201d96cefbca80c0ef2f513" - integrity sha512-UVU9dibq2JcFWxQPA6KCqj5O42VOmAY3zQUfEKxU0KpTGXwNoCjkX1e13eHNvw/xPynt6pU0rZ1htjWTNTSXsg== +js-yaml@^4.1.0: + version "4.1.1" + resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-4.1.1.tgz" + integrity sha512-qQKT4zQxXl8lLwBtHMWwaTcGfFOZviOJet3Oy/xmGk2gZH677CJM9EvtfdSkgWcATZhj/55JZ0rmy3myCT5lsA== + dependencies: + argparse "^2.0.1" jsdoc-type-pratt-parser@~4.1.0: version "4.1.0" @@ -20721,11 +20203,6 @@ json-bigint@^1.0.0: dependencies: bignumber.js "^9.0.0" -json-buffer@3.0.1: - version "3.0.1" - resolved "https://registry.yarnpkg.com/json-buffer/-/json-buffer-3.0.1.tgz#9338802a30d3b6605fbe0613e094008ca8c05a13" - integrity sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ== - json-parse-even-better-errors@^2.3.0, json-parse-even-better-errors@^2.3.1: version "2.3.1" resolved "https://registry.yarnpkg.com/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz#7c47805a94319928e05777405dc12e1f7a4ee02d" @@ -20754,7 +20231,7 @@ json-schema-traverse@^1.0.0: resolved "https://registry.yarnpkg.com/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz#ae7bcb3656ab77a73ba5c49bf654f38e6b6860e2" integrity sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug== -json-schema@0.4.0, json-schema@^0.4.0: +json-schema@^0.4.0: version "0.4.0" resolved "https://registry.yarnpkg.com/json-schema/-/json-schema-0.4.0.tgz#f7de4cf6efab838ebaeb3236474cbba5a1930ab5" integrity sha512-es94M3nTIfsEPisRafak+HDLfHXnKBhV3vU5eqPcS3flIWqcxJWgXHXiey3YrpaNsanY5ei1VoYEbOzijuq9BA== @@ -20771,7 +20248,7 @@ json-stable-stringify@^1.0.0, json-stable-stringify@^1.0.1: dependencies: jsonify "~0.0.0" -json-stringify-safe@^5.0.1, 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" integrity sha1-Epai1Y/UXxmg9s4B1lcB4sc1tus= @@ -20840,12 +20317,12 @@ jsonify@~0.0.0: resolved "https://registry.yarnpkg.com/jsonify/-/jsonify-0.0.0.tgz#2c74b6ee41d93ca51b7b5aaee8f503631d252a73" integrity sha1-LHS27kHZPKUbe1qu6PUDYx0lKnM= -jsonparse@^1.2.0, jsonparse@^1.3.1: +jsonparse@^1.3.1: version "1.3.1" resolved "https://registry.yarnpkg.com/jsonparse/-/jsonparse-1.3.1.tgz#3f4dae4a91fac315f71062f8521cc239f1366280" integrity sha512-POQXvpdL69+CluYsillJ7SUhKvytYjW9vG/GKpnf+xP8UWgYEM/RaMzHHofbALDiKbbP1W8UEYmgGl39WkPZsg== -jsonwebtoken@9.0.3, jsonwebtoken@^9.0.0: +jsonwebtoken@^9.0.0: version "9.0.3" resolved "https://registry.yarnpkg.com/jsonwebtoken/-/jsonwebtoken-9.0.3.tgz#6cd57ab01e9b0ac07cb847d53d3c9b6ee31f7ae2" integrity sha512-MT/xP0CrubFRNLNKvxJ2BYfy53Zkm++5bX9dtuPbqAeQpTVe0MQTFhao8+Cp//EmJp244xt6Drw/GVEGCUj40g== @@ -20861,16 +20338,6 @@ jsonwebtoken@9.0.3, jsonwebtoken@^9.0.0: ms "^2.1.1" semver "^7.5.4" -jsprim@^2.0.2: - version "2.0.2" - resolved "https://registry.yarnpkg.com/jsprim/-/jsprim-2.0.2.tgz#77ca23dbcd4135cd364800d22ff82c2185803d4d" - integrity sha512-gqXddjPqQ6G40VdnI6T6yObEC+pDNvyP95wdQhkWkg7crHH3km5qP1FsOXEkzEQwnz6gz5qGTn1c2Y52wP3OyQ== - dependencies: - assert-plus "1.0.0" - extsprintf "1.3.0" - json-schema "0.4.0" - verror "1.10.0" - "jsx-ast-utils@^2.4.1 || ^3.0.0": version "3.2.0" resolved "https://registry.yarnpkg.com/jsx-ast-utils/-/jsx-ast-utils-3.2.0.tgz#41108d2cec408c3453c1bbe8a4aae9e1e2bd8f82" @@ -20918,13 +20385,6 @@ karma-source-map-support@1.4.0: dependencies: source-map-support "^0.5.5" -keyv@^4.0.0: - version "4.5.4" - resolved "https://registry.yarnpkg.com/keyv/-/keyv-4.5.4.tgz#a879a99e29452f942439f2a405e3af8b31d4de93" - integrity sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw== - dependencies: - json-buffer "3.0.1" - kind-of@^3.0.2, kind-of@^3.0.3, kind-of@^3.2.0: version "3.2.2" resolved "https://registry.yarnpkg.com/kind-of/-/kind-of-3.2.2.tgz#31ea21a734bab9bbb0f32466d893aea51e4a3c64" @@ -21265,13 +20725,6 @@ locate-path@^7.1.0: dependencies: p-locate "^6.0.0" -lockfile@1.0.4: - version "1.0.4" - resolved "https://registry.yarnpkg.com/lockfile/-/lockfile-1.0.4.tgz#07f819d25ae48f87e538e6578b6964a4981a5609" - integrity sha512-cvbTwETRfsFh4nHsL1eGWapU1XFi5Ot9E85sWAwia7Y7EgB7vfqcZhTKZ+l7hCGxSPoushMv5GKhT5PdLv03WA== - dependencies: - signal-exit "^3.0.2" - lodash._baseassign@^3.0.0: version "3.2.0" resolved "https://registry.yarnpkg.com/lodash._baseassign/-/lodash._baseassign-3.2.0.tgz#8c38a099500f215ad09e59f1722fd0c52bfe0a4e" @@ -21480,16 +20933,16 @@ lodash.uniq@^4.2.0, lodash.uniq@^4.5.0: resolved "https://registry.yarnpkg.com/lodash.uniq/-/lodash.uniq-4.5.0.tgz#d0225373aeb652adc1bc82e4945339a842754773" integrity sha1-0CJTc662Uq3BvILklFM5qEJ1R3M= -lodash@4, lodash@4.18.1, lodash@^4.17.12, lodash@^4.17.14, lodash@^4.17.15, lodash@^4.17.19, lodash@^4.17.20, lodash@^4.17.21: - version "4.18.1" - resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.18.1.tgz#ff2b66c1f6326d59513de2407bf881439812771c" - integrity sha512-dMInicTPVE8d1e5otfwmmjlxkZoUpiVLwyeTdUsi/Caj/gfzzblBcCE5sRHV/AsjuCmxWrte2TNGSYuCeCq+0Q== - -lodash@4.17.21, lodash@4.17.23, lodash@~4.17.21: +lodash@4.17.23, lodash@~4.17.21: version "4.17.23" resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.23.tgz#f113b0378386103be4f6893388c73d0bde7f2c5a" integrity sha512-LgVTMpQtIopCi79SJeDiP0TfWi5CNEc/L/aRdTh3yIvmZXTnheWpKjSZhnvMl8iXbC1tFg9gdHHDMLoV7CnG+w== +lodash@^4.17.12, lodash@^4.17.14, lodash@^4.17.15, lodash@^4.17.19, lodash@^4.17.20, lodash@^4.17.21: + version "4.18.1" + resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.18.1.tgz#ff2b66c1f6326d59513de2407bf881439812771c" + integrity sha512-dMInicTPVE8d1e5otfwmmjlxkZoUpiVLwyeTdUsi/Caj/gfzzblBcCE5sRHV/AsjuCmxWrte2TNGSYuCeCq+0Q== + log-symbols@^2.2.0: version "2.2.0" resolved "https://registry.yarnpkg.com/log-symbols/-/log-symbols-2.2.0.tgz#5740e1c5d6f0dfda4ad9323b5332107ef6b4c40a" @@ -21562,17 +21015,6 @@ loupe@^3.1.0, loupe@^3.1.4: resolved "https://registry.yarnpkg.com/loupe/-/loupe-3.1.4.tgz#784a0060545cb38778ffb19ccde44d7870d5fdd9" integrity sha512-wJzkKwJrheKtknCOKNEtDK4iqg/MxmZheEMtSTYvnzRdEYaZzmgH976nenp8WdJRdx5Vc1X/9MO0Oszl6ezeXg== -lowdb@1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/lowdb/-/lowdb-1.0.0.tgz#5243be6b22786ccce30e50c9a33eac36b20c8064" - integrity sha512-2+x8esE/Wb9SQ1F9IHaYWfsC9FIecLOPrK4g17FGEayjUWH172H6nwicRovGvSE2CPZouc2MCIqCI7h9d+GftQ== - dependencies: - graceful-fs "^4.1.3" - is-promise "^2.1.0" - lodash "4" - pify "^3.0.0" - steno "^0.4.1" - lower-case@^2.0.2: version "2.0.2" resolved "https://registry.yarnpkg.com/lower-case/-/lower-case-2.0.2.tgz#6fa237c63dbdc4a82ca0fd882e4722dc5e634e28" @@ -21580,11 +21022,6 @@ lower-case@^2.0.2: dependencies: tslib "^2.0.3" -lowercase-keys@2.0.0, lowercase-keys@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/lowercase-keys/-/lowercase-keys-2.0.0.tgz#2603e78b7b4b0006cbca2fbcc8a3202558ac9479" - integrity sha512-tqNXrS78oMOE73NMxK4EMLQsQowWf8jKooH9g7xPavRT706R6bkQJ6DY2Te7QukaZsulxa30wQ7bk0pm4XiHmA== - lru-cache@6.0.0, lru-cache@^6.0.0: version "6.0.0" resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-6.0.0.tgz#6d6fe6570ebd96aaf90fcad1dafa3b2566db3a94" @@ -21592,11 +21029,6 @@ lru-cache@6.0.0, lru-cache@^6.0.0: dependencies: yallist "^4.0.0" -lru-cache@7.18.3, lru-cache@^7.4.4, lru-cache@^7.5.1, lru-cache@^7.7.1: - version "7.18.3" - resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-7.18.3.tgz#f793896e0fd0e954a59dfdd82f0773808df6aa89" - integrity sha512-jumlc0BIUrS3qJGgIkWZsyfAM7NCWiBcCDhnd+3NNM5KbBmLTgHVfWBcg6W+rLUsIpzpERPsvwUP7CckAQSOoA== - lru-cache@^10.2.0: version "10.4.3" resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-10.4.3.tgz#410fc8a17b70e598013df257c2446b7f3383f119" @@ -21614,6 +21046,11 @@ lru-cache@^5.1.1: dependencies: yallist "^3.0.2" +lru-cache@^7.4.4, lru-cache@^7.5.1, lru-cache@^7.7.1: + version "7.18.3" + resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-7.18.3.tgz#f793896e0fd0e954a59dfdd82f0773808df6aa89" + integrity sha512-jumlc0BIUrS3qJGgIkWZsyfAM7NCWiBcCDhnd+3NNM5KbBmLTgHVfWBcg6W+rLUsIpzpERPsvwUP7CckAQSOoA== + lru-memoizer@2.3.0: version "2.3.0" resolved "https://registry.yarnpkg.com/lru-memoizer/-/lru-memoizer-2.3.0.tgz#ef0fbc021bceb666794b145eefac6be49dc47f31" @@ -22463,7 +21900,7 @@ mime-db@1.52.0: resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.54.0.tgz#cddb3ee4f9c64530dff640236661d42cb6a314f5" integrity sha512-aU5EJuIN2WDemCcAp2vFBfp/m4EAhWJnUNSSw0ixs7/kXbd6Pg64EmwJkNdFhB8aWt1sH2CTXrLxo/iAGV3oPQ== -mime-types@^2.1.12, mime-types@^2.1.18, mime-types@^2.1.26, mime-types@^2.1.27, mime-types@^2.1.31, mime-types@~2.1.17, mime-types@~2.1.19, mime-types@~2.1.24, mime-types@~2.1.34: +mime-types@^2.1.12, mime-types@^2.1.18, mime-types@^2.1.26, mime-types@^2.1.27, mime-types@^2.1.31, mime-types@~2.1.17, mime-types@~2.1.24, mime-types@~2.1.34: version "2.1.35" resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-2.1.35.tgz#381a871b62a734450660ae3deee44813f70d959a" integrity sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw== @@ -22482,7 +21919,7 @@ mime@1.6.0, mime@^1.4.1: resolved "https://registry.yarnpkg.com/mime/-/mime-1.6.0.tgz#32cd9e5c64553bd58d19a568af452acff04981b1" integrity sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg== -mime@3.0.0, mime@^3.0.0: +mime@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/mime/-/mime-3.0.0.tgz#b374550dca3a0c18443b0c950a6a58f1931cf7a7" integrity sha512-jSCU7/VB1loIWBZe14aEYHU/+1UMEHoaO7qxCOVJOw9GgH72VAWppxNcjU+x9a2k3GSIBXNKxXQFqRvvZ7vr3A== @@ -22512,11 +21949,6 @@ mimic-fn@^4.0.0: resolved "https://registry.yarnpkg.com/mimic-fn/-/mimic-fn-4.0.0.tgz#60a90550d5cb0b239cca65d893b1a53b29871ecc" integrity sha512-vqiC06CuhBTUdZH+RYl8sFrL096vA45Ok5ISO6sE/Mr1jRbGH4Csnhi8f3wKVl7x8mO4Au7Ir9D3Oyv1VYMFJw== -mimic-response@^1.0.0: - version "1.0.1" - resolved "https://registry.yarnpkg.com/mimic-response/-/mimic-response-1.0.1.tgz#4923538878eef42063cb8a3e3b0798781487ab1b" - integrity sha512-j5EctnkH7amfV/q5Hgmoal1g2QHFJRraOtmx0JpIqkxhBhI/lJSl1nMpQ45hVarwNETOoWEimndZ4QK0RHxuxQ== - mimic-response@^3.1.0: version "3.1.0" resolved "https://registry.yarnpkg.com/mimic-response/-/mimic-response-3.1.0.tgz#2d1d59af9c1b129815accc2c46a022a5ce1fa3c9" @@ -22584,13 +22016,6 @@ minimatch@5.1.0, minimatch@5.1.9, minimatch@^5.0.1, minimatch@^5.1.0: dependencies: brace-expansion "^2.0.1" -minimatch@7.4.6, minimatch@7.4.9, minimatch@^7.4.1, minimatch@~7.4.9: - version "7.4.9" - resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-7.4.9.tgz#ef35412b1b36261b78ef1b2f0db29b759bbcaf5d" - integrity sha512-Brg/fp/iAVDOQoHxkuN5bEYhyQlZhxddI78yWsCbeEwTHXQjlNLtiJDUsp1GIptVqMI7/gkJMz4vVAc01mpoBw== - dependencies: - brace-expansion "^2.0.2" - minimatch@^10.2.2, minimatch@^10.2.4: version "10.2.5" resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-10.2.5.tgz#bd48687a0be38ed2961399105600f832095861d1" @@ -22598,6 +22023,13 @@ minimatch@^10.2.2, minimatch@^10.2.4: dependencies: brace-expansion "^5.0.5" +minimatch@^7.4.1, minimatch@~7.4.9: + version "7.4.9" + resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-7.4.9.tgz#ef35412b1b36261b78ef1b2f0db29b759bbcaf5d" + integrity sha512-Brg/fp/iAVDOQoHxkuN5bEYhyQlZhxddI78yWsCbeEwTHXQjlNLtiJDUsp1GIptVqMI7/gkJMz4vVAc01mpoBw== + dependencies: + brace-expansion "^2.0.2" + minimatch@^8.0.2: version "8.0.7" resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-8.0.7.tgz#954766e22da88a3e0a17ad93b58c15c9d8a579de" @@ -22732,11 +22164,6 @@ mkdirp-classic@^0.5.2, mkdirp-classic@^0.5.3: resolved "https://registry.yarnpkg.com/mkdirp-classic/-/mkdirp-classic-0.5.3.tgz#fa10c9115cc6d8865be221ba47ee9bed78601113" integrity sha512-gKLcREMhtuZRwRAfqP3RFW+TK4JqApVBtOIftVgjuABpAtpxhPGaDcfvbhNvD0B8iD1oUr/txX35NjcaY6Ns/A== -mkdirp@1.0.4, mkdirp@^1.0.3, mkdirp@^1.0.4: - version "1.0.4" - resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-1.0.4.tgz#3eb5ed62622756d79a5f0e2a221dfebad75c2f7e" - integrity sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw== - mkdirp@^0.5.0, mkdirp@^0.5.1, mkdirp@^0.5.5, mkdirp@^0.5.6: version "0.5.6" resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-0.5.6.tgz#7def03d2432dcae4ba1d611445c48396062255f6" @@ -22744,6 +22171,11 @@ mkdirp@^0.5.0, mkdirp@^0.5.1, mkdirp@^0.5.5, mkdirp@^0.5.6: dependencies: minimist "^1.2.6" +mkdirp@^1.0.3, mkdirp@^1.0.4: + version "1.0.4" + resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-1.0.4.tgz#3eb5ed62622756d79a5f0e2a221dfebad75c2f7e" + integrity sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw== + mkdirp@^3.0.1, mkdirp@~3.0.0: version "3.0.1" resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-3.0.1.tgz#e44e4c5607fb279c168241713cc6e0fea9adcb50" @@ -23413,13 +22845,6 @@ node-fetch@^2.3.0, node-fetch@^2.6.1, node-fetch@^2.6.7, node-fetch@^2.6.9: dependencies: whatwg-url "^5.0.0" -node-fetch@cjs: - version "2.6.7" - resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-2.6.7.tgz#24de9fba827e3b4ae44dc8b20256a379160052ad" - integrity sha512-ZjMPFEfVx5j+y2yF35Kzx5sF7kDzxuDj6ziH4FFbOp87zKDZNx8yExJIb05OGF4Nlt9IHFIMBkRl41VdvcNdbQ== - dependencies: - whatwg-url "^5.0.0" - node-forge@^1, node-forge@^1.3.1: version "1.4.0" resolved "https://registry.yarnpkg.com/node-forge/-/node-forge-1.4.0.tgz#1c7b7d8bdc2d078739f58287d589d903a11b2fc2" @@ -23577,11 +23002,6 @@ normalize-path@^3.0.0, normalize-path@~3.0.0: resolved "https://registry.yarnpkg.com/normalize-path/-/normalize-path-3.0.0.tgz#0dcd69ff23a1c9b11fd0978316644a0388216a65" integrity sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA== -normalize-url@^6.0.1: - version "6.1.0" - resolved "https://registry.yarnpkg.com/normalize-url/-/normalize-url-6.1.0.tgz#40d0885b535deffe3f3147bec877d05fe4c5668a" - integrity sha512-DlL+XwOy3NxAQ8xuC0okPgK46iuVNAK01YN7RueYBqqFeGsBjV9XmCAzAdgt+667bCl5kPh9EqKKDwnaPG1I7A== - npm-bundled@^1.1.1: version "1.1.2" resolved "https://registry.yarnpkg.com/npm-bundled/-/npm-bundled-1.1.2.tgz#944c78789bd739035b70baa2ca5cc32b8d860bc1" @@ -24060,7 +23480,7 @@ on-change@^5.0.1: resolved "https://registry.yarnpkg.com/on-change/-/on-change-5.0.1.tgz#ced60d262211eee41043e7479515b4875d1744ef" integrity sha512-n7THCP7RkyReRSLkJb8kUWoNsxUIBxTkIp3JKno+sEz6o/9AJ3w3P9fzQkITEkMwyTKJjZciF3v/pVoouxZZMg== -on-exit-leak-free@2.1.2, on-exit-leak-free@^2.1.0: +on-exit-leak-free@^2.1.0: version "2.1.2" resolved "https://registry.yarnpkg.com/on-exit-leak-free/-/on-exit-leak-free-2.1.2.tgz#fed195c9ebddb7d9e4c3842f93f281ac8dadd3b8" integrity sha512-0eJJY6hXLGf1udHwfNftBqH+g73EU4B504nZeKpz1sYRKafAghwxEJunB2O7rDZkL4PGfsMVnTXZ2EjibbqcsA== @@ -24374,11 +23794,6 @@ oxlint@^1.53.0: "@oxlint/binding-win32-ia32-msvc" "1.53.0" "@oxlint/binding-win32-x64-msvc" "1.53.0" -p-cancelable@2.1.1: - version "2.1.1" - resolved "https://registry.yarnpkg.com/p-cancelable/-/p-cancelable-2.1.1.tgz#aab7fbd416582fa32a3db49859c122487c5ed2cf" - integrity sha512-BZOr3nRQHOntUjTrH8+Lh54smKHoHyur8We1V8DSMVrl5A2malOOwuJRnKRDjSnkoeBh4at6BwEnb5I7Jl31wg== - p-defer@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/p-defer/-/p-defer-1.0.0.tgz#9f6eb182f6c9aa8cd743004a7d4f96b196b0fb0c" @@ -24581,11 +23996,6 @@ pako@^1.0.3: resolved "https://registry.yarnpkg.com/pako/-/pako-1.0.11.tgz#6c9599d340d54dfd3946380252a35705a6b992bf" integrity sha512-4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw== -pako@~0.2.0: - version "0.2.9" - resolved "https://registry.yarnpkg.com/pako/-/pako-0.2.9.tgz#f3f7522f4ef782348da8161bad9ecfd51bf83a75" - integrity sha512-NUcwaKxUxWrZLpDG+z/xZaCgQITkA/Dv4V/T6bw7VON6l1Xz/VnrBqrYjZQ12TamKHzITTfOEIYUj48y2KXImA== - param-case@^3.0.4: version "3.0.4" resolved "https://registry.yarnpkg.com/param-case/-/param-case-3.0.4.tgz#7d17fe4aa12bde34d4a77d91acfb6219caad01c5" @@ -24834,15 +24244,6 @@ pathval@^2.0.0: resolved "https://registry.yarnpkg.com/pathval/-/pathval-2.0.0.tgz#7e2550b422601d4f6b8e26f1301bc8f15a741a25" integrity sha512-vE7JKRyES09KiunauX7nd2Q9/L7lhok4smP9RZTDeD4MVs72Dp2qNFVz39Nz5a0FVEW0BJR6C0DYrq6unoziZA== -peek-stream@^1.1.0: - version "1.1.3" - resolved "https://registry.yarnpkg.com/peek-stream/-/peek-stream-1.1.3.tgz#3b35d84b7ccbbd262fff31dc10da56856ead6d67" - integrity sha512-FhJ+YbOSBb9/rIl2ZeE/QHEsWn7PqNYt8ARAY3kIgNGOk13g9FGyIY6JIl/xB/3TFRVoTv5as0l11weORrTekA== - dependencies: - buffer-from "^1.0.0" - duplexify "^3.5.0" - through2 "^2.0.3" - pend@~1.2.0: version "1.2.0" resolved "https://registry.yarnpkg.com/pend/-/pend-1.2.0.tgz#7a57eb550a6783f9115331fcf4663d5c8e007a50" @@ -24863,11 +24264,6 @@ perfect-debounce@^2.0.0, perfect-debounce@^2.1.0: resolved "https://registry.yarnpkg.com/perfect-debounce/-/perfect-debounce-2.1.0.tgz#e7078e38f231cb191855c3136a4423aef725d261" integrity sha512-LjgdTytVFXeUgtHZr9WYViYSM/g8MkcTPYDlPa3cDqMirHjKiSZPYd6DoL7pK8AJQr+uWkQvCjHNdiMqsrJs+g== -performance-now@^2.1.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/performance-now/-/performance-now-2.1.0.tgz#6309f4e0e5fa913ec1c69307ae364b4b377c9e7b" - integrity sha512-7EAHlyLHI56VEIdK57uwHdHKIaAGbnXPiw0yWbarQZOKaKpvUIgW0jWRVLiatnM+XXlSwsanIBH/hzGMJulMow== - periscopic@^3.1.0: version "3.1.0" resolved "https://registry.yarnpkg.com/periscopic/-/periscopic-3.1.0.tgz#7e9037bf51c5855bd33b48928828db4afa79d97a" @@ -24976,11 +24372,6 @@ pify@^2.3.0: resolved "https://registry.yarnpkg.com/pify/-/pify-2.3.0.tgz#ed141a6ac043a849ea588498e7dca8b15330e90c" integrity sha1-7RQaasBDqEnqWISY59yosVMw6Qw= -pify@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/pify/-/pify-3.0.0.tgz#e5a4acd2c101fdf3d9a4d07f0dbc4db49dd28176" - integrity sha512-C3FsVNH1udSEX48gGX1xfvwTWfsYWj5U+8/uK15BGzIGrKoUpghX8hWZwa/OFnakBiiVNmBvemTJR5mcy7iPcg== - pify@^4.0.1: version "4.0.1" resolved "https://registry.yarnpkg.com/pify/-/pify-4.0.1.tgz#4b2cd25c50d598735c50292224fd8c6df41e3231" @@ -24998,14 +24389,6 @@ pinkie@^2.0.0: resolved "https://registry.yarnpkg.com/pinkie/-/pinkie-2.0.4.tgz#72556b80cfa0d48a974e80e77248e80ed4f7f870" integrity sha1-clVrgM+g1IqXToDnckjoDtT3+HA= -pino-abstract-transport@1.2.0: - version "1.2.0" - resolved "https://registry.yarnpkg.com/pino-abstract-transport/-/pino-abstract-transport-1.2.0.tgz#97f9f2631931e242da531b5c66d3079c12c9d1b5" - integrity sha512-Guhh8EZfPCfH+PMXAb6rKOjGQEoy0xlAIn+irODG5kgfYV+BQ0rGYYWTIel3P5mmyXqkYkPmdIkywsn6QKUR1Q== - dependencies: - readable-stream "^4.0.0" - split2 "^4.0.0" - pino-abstract-transport@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/pino-abstract-transport/-/pino-abstract-transport-2.0.0.tgz#de241578406ac7b8a33ce0d77ae6e8a0b3b68a60" @@ -25052,23 +24435,6 @@ pino@9.10.0: sonic-boom "^4.0.1" thread-stream "^3.0.0" -pino@9.14.0: - version "9.14.0" - resolved "https://registry.yarnpkg.com/pino/-/pino-9.14.0.tgz#673d9711c2d1e64d18670c1ec05ef7ba14562556" - integrity sha512-8OEwKp5juEvb/MjpIc4hjqfgCNysrS94RIOMXYvpYCdm/jglrKEiAYmiumbmGhCvs+IcInsphYDFwqrjr7398w== - dependencies: - "@pinojs/redact" "^0.4.0" - atomic-sleep "^1.0.0" - on-exit-leak-free "^2.1.0" - pino-abstract-transport "^2.0.0" - pino-std-serializers "^7.0.0" - process-warning "^5.0.0" - quick-format-unescaped "^4.0.3" - real-require "^0.2.0" - safe-stable-stringify "^2.3.1" - sonic-boom "^4.0.1" - thread-stream "^3.0.0" - pirates@^4.0.1: version "4.0.6" resolved "https://registry.yarnpkg.com/pirates/-/pirates-4.0.6.tgz#3018ae32ecfcff6c29ba2267cbf21166ac1f36b9" @@ -25992,11 +25358,6 @@ process-relative-require@^1.0.0: dependencies: node-modules-path "^1.0.0" -process-warning@1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/process-warning/-/process-warning-1.0.0.tgz#980a0b25dc38cd6034181be4b7726d89066b4616" - integrity sha512-du4wfLyj4yCZq1VupnVSZmRsPJsNuxoDQFdCFHLaYiEbFBD7QE0a+I4D7hOxrVnh78QE/YipFAj9lXHiXocV+Q== - process-warning@^5.0.0: version "5.0.0" resolved "https://registry.yarnpkg.com/process-warning/-/process-warning-5.0.0.tgz#566e0bf79d1dff30a72d8bbbe9e8ecefe8d378d7" @@ -26143,14 +25504,6 @@ pstree.remy@^1.1.8: resolved "https://registry.yarnpkg.com/pstree.remy/-/pstree.remy-1.1.8.tgz#c242224f4a67c21f686839bbdb4ac282b8373d3a" integrity sha512-77DZwxQmxKnu3aR542U+X8FypNzbfJ+C5XQDk3uWjWxn6151aIMGthWYRXTqT1E5oJvg+ljaa2OJi+VfvCOQ8w== -pump@^2.0.0: - version "2.0.1" - resolved "https://registry.yarnpkg.com/pump/-/pump-2.0.1.tgz#12399add6e4cf7526d973cbc8b5ce2e2908b3909" - integrity sha512-ruPMNRkN3MHP1cWJc9OWr+T/xDP0jhXYCLfJcBuX54hhfIBnaQmAUMfDcG4DM5UMWByBbJY69QSphm3jtDKIkA== - dependencies: - end-of-stream "^1.1.0" - once "^1.3.1" - pump@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/pump/-/pump-3.0.0.tgz#b4a2116815bde2f4e1ea602354e8c75565107a64" @@ -26159,15 +25512,6 @@ pump@^3.0.0: end-of-stream "^1.1.0" once "^1.3.1" -pumpify@^1.3.3: - version "1.5.1" - resolved "https://registry.yarnpkg.com/pumpify/-/pumpify-1.5.1.tgz#36513be246ab27570b1a374a5ce278bfd74370ce" - integrity sha512-oClZI37HvuUJJxSKKrC17bZ9Cu0ZYhEAGPsPUy9KlMUmv9dKX2o77RUmq7f3XjIxbwyGwYzbzQ1L2Ks8sIradQ== - dependencies: - duplexify "^3.6.0" - inherits "^2.0.3" - pump "^2.0.0" - punycode@^2.1.0, punycode@^2.1.1, punycode@^2.3.0, punycode@^2.3.1: version "2.3.1" resolved "https://registry.yarnpkg.com/punycode/-/punycode-2.3.1.tgz#027422e2faec0b25e1549c3e1bd8309b9133b6e5" @@ -26183,7 +25527,7 @@ pure-rand@^8.0.0: resolved "https://registry.yarnpkg.com/pure-rand/-/pure-rand-8.4.0.tgz#1d9e26e9c0555486e08ae300d02796af8dec1cd0" integrity sha512-IoM8YF/jY0hiugFo/wOWqfmarlE6J0wc6fDK1PhftMk7MGhVZl88sZimmqBBFomLOCSmcCCpsfj7wXASCpvK9A== -qs@^6.14.0, qs@^6.14.1, qs@^6.4.0, qs@~6.14.0, qs@~6.14.1: +qs@^6.14.0, qs@^6.14.1, qs@^6.4.0, qs@~6.14.0: version "6.14.2" resolved "https://registry.yarnpkg.com/qs/-/qs-6.14.2.tgz#b5634cf9d9ad9898e31fba3504e866e8efb6798c" integrity sha512-V/yCWTTF7VJ9hIh18Ugr2zhJMP01MY7c5kh4J870L7imm6/DIzBsNLTXzMwUA3yZ5b/KBqLx8Kp3uRvd7xSe3Q== @@ -26218,11 +25562,6 @@ quick-format-unescaped@^4.0.3: resolved "https://registry.yarnpkg.com/quick-format-unescaped/-/quick-format-unescaped-4.0.4.tgz#93ef6dd8d3453cbc7970dd614fad4c5954d6b5a7" integrity sha512-tYC1Q1hgyRuHgloV/YXs2w15unPVh8qfu/qCTfhTYamaw7fyhumKa2yGpdSo87vY32rIclj+4fWYQXUMs9EHvg== -quick-lru@^5.1.1: - version "5.1.1" - resolved "https://registry.yarnpkg.com/quick-lru/-/quick-lru-5.1.1.tgz#366493e6b3e42a3a6885e2e99d18f80fb7a8c932" - integrity sha512-WuyALRjWPDGtt/wzJiadO5AXY+8hZ80hVpe6MyivgraREW751X3SbhRvG3eLKOYN+8VEvqLcf3wdnt44Z4S4SA== - quick-temp@^0.1.2, quick-temp@^0.1.3, quick-temp@^0.1.5, quick-temp@^0.1.8: version "0.1.8" resolved "https://registry.yarnpkg.com/quick-temp/-/quick-temp-0.1.8.tgz#bab02a242ab8fb0dd758a3c9776b32f9a5d94408" @@ -26497,7 +25836,7 @@ readable-stream@2.3.7: string_decoder "~1.1.1" util-deprecate "~1.0.1" -readable-stream@^2.0.0, readable-stream@^2.0.1, readable-stream@^2.0.5, readable-stream@^2.3.5, readable-stream@~2.3.6: +readable-stream@^2.0.1, readable-stream@^2.0.5, readable-stream@^2.3.5: version "2.3.8" resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-2.3.8.tgz#91125e8042bba1b9887f49345f6277027ce8be9b" integrity sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA== @@ -26972,11 +26311,6 @@ reselect@^4.0.0, reselect@^4.1.7: resolved "https://registry.yarnpkg.com/reselect/-/reselect-4.1.8.tgz#3f5dc671ea168dccdeb3e141236f69f02eaec524" integrity sha512-ab9EmR80F/zQTMNeneUr4cv+jSwPJgIlvEmVwLerwrWVbpLlBuls9XHzIeTFy4cegU2NHBp3va0LKOzU5qFEYQ== -resolve-alpn@^1.2.0: - version "1.2.1" - resolved "https://registry.yarnpkg.com/resolve-alpn/-/resolve-alpn-1.2.1.tgz#b7adbdac3546aaaec20b45e7d8265927072726f9" - integrity sha512-0a1F4l73/ZFZOakJnQ3FvkJ2+gSTQWz/r2KE5OdDY0TxPm5h4GkqkWWfM47T7HsbnOtcJVEF4epCVy6u7Q3K+g== - resolve-dependency-path@^4.0.1: version "4.0.1" resolved "https://registry.yarnpkg.com/resolve-dependency-path/-/resolve-dependency-path-4.0.1.tgz#1b9d43e5b62384301e26d040b9fce61ee5db60bd" @@ -27097,13 +26431,6 @@ resolve@^2.0.0-next.3: path-parse "^1.0.7" supports-preserve-symlinks-flag "^1.0.0" -responselike@2.0.1, responselike@^2.0.0: - version "2.0.1" - resolved "https://registry.yarnpkg.com/responselike/-/responselike-2.0.1.tgz#9a0bc8fdc252f3fb1cca68b016591059ba1422bc" - integrity sha512-4gl03wn3hj1HP3yzgdI7d3lCkF95F21Pz4BPGvKHinyQzALR5CapwC8yIi0Rh58DEMQ/SguC03wFj2k0M/mHhw== - dependencies: - lowercase-keys "^2.0.0" - restore-cursor@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/restore-cursor/-/restore-cursor-2.0.0.tgz#9f7ee287f82fd326d4fd162923d62129eee0dfaf" @@ -27510,7 +26837,7 @@ safe-stable-stringify@^2.3.1, safe-stable-stringify@^2.4.1, safe-stable-stringif resolved "https://registry.yarnpkg.com/safe-stable-stringify/-/safe-stable-stringify-2.5.0.tgz#4ca2f8e385f2831c432a719b108a3bf7af42a1dd" integrity sha512-b3rppTKm9T+PsVCBEOUR46GWI7fdOs00VKZ1+9c1EWDaDMvjQc6tUwuFyIprgGgTcWoVHSKrU8H31ZHA2e0RHA== -"safer-buffer@>= 2.1.2 < 3", "safer-buffer@>= 2.1.2 < 3.0.0", safer-buffer@^2.0.2, safer-buffer@^2.1.0, safer-buffer@~2.1.0: +"safer-buffer@>= 2.1.2 < 3", "safer-buffer@>= 2.1.2 < 3.0.0": version "2.1.2" resolved "https://registry.yarnpkg.com/safer-buffer/-/safer-buffer-2.1.2.tgz#44fa161b0187b9549dd84bb91802f9bd8385cd6a" integrity sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg== @@ -27690,16 +27017,6 @@ semver@7.5.3: dependencies: lru-cache "^6.0.0" -semver@7.7.2: - version "7.7.2" - resolved "https://registry.yarnpkg.com/semver/-/semver-7.7.2.tgz#67d99fdcd35cec21e6f8b87a7fd515a33f982b58" - integrity sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA== - -semver@7.7.4, semver@^7.0.0, semver@^7.1.1, semver@^7.3.2, semver@^7.3.4, semver@^7.3.5, semver@^7.3.7, semver@^7.3.8, semver@^7.5.0, semver@^7.5.3, semver@^7.5.4, semver@^7.6.0, semver@^7.6.2, semver@^7.6.3, semver@^7.7.2, semver@^7.7.3, semver@^7.7.4: - version "7.7.4" - resolved "https://registry.yarnpkg.com/semver/-/semver-7.7.4.tgz#28464e36060e991fa7a11d0279d2d3f3b57a7e8a" - integrity sha512-vFKC2IEtQnVhpT78h1Yp8wzwrf8CM+MzKMHGJZfBtzhZNycRFnXsHk6E5TxIkkMsgNS7mdX3AGB7x2QM2di4lA== - semver@^5.3.0, semver@^5.4.1, semver@^5.5.0, semver@^5.6.0, semver@^5.7.1: version "5.7.2" resolved "https://registry.yarnpkg.com/semver/-/semver-5.7.2.tgz#48d55db737c3287cd4835e17fa13feace1c41ef8" @@ -27710,6 +27027,11 @@ semver@^6.0.0, semver@^6.1.0, semver@^6.1.1, semver@^6.1.2, semver@^6.3.0, semve resolved "https://registry.yarnpkg.com/semver/-/semver-6.3.1.tgz#556d2ef8689146e46dcea4bfdd095f3434dffcb4" integrity sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA== +semver@^7.0.0, semver@^7.1.1, semver@^7.3.2, semver@^7.3.4, semver@^7.3.5, semver@^7.3.7, semver@^7.3.8, semver@^7.5.0, semver@^7.5.3, semver@^7.5.4, semver@^7.6.0, semver@^7.6.2, semver@^7.6.3, semver@^7.7.2, semver@^7.7.3, semver@^7.7.4: + version "7.7.4" + resolved "https://registry.yarnpkg.com/semver/-/semver-7.7.4.tgz#28464e36060e991fa7a11d0279d2d3f3b57a7e8a" + integrity sha512-vFKC2IEtQnVhpT78h1Yp8wzwrf8CM+MzKMHGJZfBtzhZNycRFnXsHk6E5TxIkkMsgNS7mdX3AGB7x2QM2di4lA== + send@^1.1.0, send@^1.2.0: version "1.2.1" resolved "https://registry.yarnpkg.com/send/-/send-1.2.1.tgz#9eab743b874f3550f40a26867bf286ad60d3f3ed" @@ -28314,13 +27636,6 @@ solid-use@^0.8.0: resolved "https://registry.yarnpkg.com/solid-use/-/solid-use-0.8.0.tgz#d46258c45edb0f4c621285e0ad1aa6b6a674d79b" integrity sha512-YX+XmcKLvSx3bwMimMhFy40ZkDnShnUcEw6cW6fSscwKEgl1TG3GlgAvkBmQ3AeWjvQSd8+HGTr82ImsrjkkqA== -sonic-boom@3.8.1: - version "3.8.1" - resolved "https://registry.yarnpkg.com/sonic-boom/-/sonic-boom-3.8.1.tgz#d5ba8c4e26d6176c9a1d14d549d9ff579a163422" - integrity sha512-y4Z8LCDBuum+PBP3lSV7RHrXscqksve/bi0as7mhwVnBW+/wUqKT/2Kb7um8yqcFy0duYbbPxzt89Zy2nOCaxg== - dependencies: - atomic-sleep "^1.0.0" - sonic-boom@^4.0.1: version "4.2.0" resolved "https://registry.yarnpkg.com/sonic-boom/-/sonic-boom-4.2.0.tgz#e59a525f831210fa4ef1896428338641ac1c124d" @@ -28605,21 +27920,6 @@ srvx@^0.11.12, srvx@^0.11.2, srvx@^0.11.9: resolved "https://registry.yarnpkg.com/srvx/-/srvx-0.11.13.tgz#cc77a98cb9a459c34f75ee4345bd0eef9f613a54" integrity sha512-oknN6qduuMPafxKtHucUeG32Q963pjriA5g3/Bl05cwEsUe5VVbIU4qR9LrALHbipSCyBe+VmfDGGydqazDRkw== -sshpk@^1.18.0: - version "1.18.0" - resolved "https://registry.yarnpkg.com/sshpk/-/sshpk-1.18.0.tgz#1663e55cddf4d688b86a46b77f0d5fe363aba028" - integrity sha512-2p2KJZTSqQ/I3+HX42EpYOa2l3f8Erv8MWKsy2I9uf4wA7yFIkXRffYdsx86y6z4vHtV8u7g+pPlr8/4ouAxsQ== - dependencies: - asn1 "~0.2.3" - assert-plus "^1.0.0" - bcrypt-pbkdf "^1.0.0" - dashdash "^1.12.0" - ecc-jsbn "~0.1.1" - getpass "^0.1.1" - jsbn "~0.1.0" - safer-buffer "^2.0.2" - tweetnacl "~0.14.0" - ssri@^9.0.0: version "9.0.1" resolved "https://registry.yarnpkg.com/ssri/-/ssri-9.0.1.tgz#544d4c357a8d7b71a19700074b6883fcb4eae057" @@ -28669,11 +27969,6 @@ static-extend@^0.1.1: define-property "^0.2.5" object-copy "^0.1.0" -statuses@2.0.1: - version "2.0.1" - resolved "https://registry.yarnpkg.com/statuses/-/statuses-2.0.1.tgz#55cb000ccf1d48728bd23c685a063998cf1a1b63" - integrity sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ== - "statuses@>= 1.4.0 < 2", statuses@~1.5.0: version "1.5.0" resolved "https://registry.yarnpkg.com/statuses/-/statuses-1.5.0.tgz#161c7dac177659fd9811f43771fa99381478628c" @@ -28696,13 +27991,6 @@ stdin-discarder@^0.1.0: dependencies: bl "^5.0.0" -steno@^0.4.1: - version "0.4.4" - resolved "https://registry.yarnpkg.com/steno/-/steno-0.4.4.tgz#071105bdfc286e6615c0403c27e9d7b5dcb855cb" - integrity sha512-EEHMVYHNXFHfGtgjNITnka0aHhiAlo93F7z2/Pwd+g0teG9CnM3JIINM7hVVB5/rhw9voufD7Wukwgtw2uqh6w== - dependencies: - graceful-fs "^4.1.3" - stop-iteration-iterator@^1.0.0, stop-iteration-iterator@^1.1.0: version "1.1.0" resolved "https://registry.yarnpkg.com/stop-iteration-iterator/-/stop-iteration-iterator-1.1.0.tgz#f481ff70a548f6124d0312c3aa14cbfa7aa542ad" @@ -29289,15 +28577,6 @@ tar-fs@^3.0.4: bare-fs "^4.0.1" bare-path "^3.0.0" -tar-stream@3.1.7, tar-stream@^3.0.0, tar-stream@^3.1.5, tar-stream@^3.1.7: - version "3.1.7" - resolved "https://registry.yarnpkg.com/tar-stream/-/tar-stream-3.1.7.tgz#24b3fb5eabada19fe7338ed6d26e5f7c482e792b" - integrity sha512-qJj60CXt7IU1Ffyc3NJMjh6EkuCFej46zUqJ4J7pqYlThyd9bO0XBTmcOIhSzZJVWfsLks0+nle/j538YAW9RQ== - dependencies: - b4a "^1.6.4" - fast-fifo "^1.2.0" - streamx "^2.15.0" - tar-stream@^2.1.4, tar-stream@~2.2.0: version "2.2.0" resolved "https://registry.yarnpkg.com/tar-stream/-/tar-stream-2.2.0.tgz#acad84c284136b060dc3faa64474aa9aebd77287" @@ -29309,6 +28588,15 @@ tar-stream@^2.1.4, tar-stream@~2.2.0: inherits "^2.0.3" readable-stream "^3.1.1" +tar-stream@^3.0.0, tar-stream@^3.1.5, tar-stream@^3.1.7: + version "3.1.7" + resolved "https://registry.yarnpkg.com/tar-stream/-/tar-stream-3.1.7.tgz#24b3fb5eabada19fe7338ed6d26e5f7c482e792b" + integrity sha512-qJj60CXt7IU1Ffyc3NJMjh6EkuCFej46zUqJ4J7pqYlThyd9bO0XBTmcOIhSzZJVWfsLks0+nle/j538YAW9RQ== + dependencies: + b4a "^1.6.4" + fast-fifo "^1.2.0" + streamx "^2.15.0" + tar@^6.1.11, tar@^6.1.2: version "6.2.1" resolved "https://registry.yarnpkg.com/tar/-/tar-6.2.1.tgz#717549c541bc3c2af15751bea94b1dd068d4b03a" @@ -29507,14 +28795,6 @@ throttleit@2.1.0: resolved "https://registry.yarnpkg.com/throttleit/-/throttleit-2.1.0.tgz#a7e4aa0bf4845a5bd10daa39ea0c783f631a07b4" integrity sha512-nt6AMGKW1p/70DF/hGBdJB57B8Tspmbp5gfJ8ilhLnt7kkr2ye7hzD6NVG8GGErk2HWF34igrL2CXmNIkzKqKw== -through2@^2.0.3: - version "2.0.5" - resolved "https://registry.yarnpkg.com/through2/-/through2-2.0.5.tgz#01c1e39eb31d07cb7d03a96a70823260b23132cd" - integrity sha512-/mrRod8xqpA+IHSLyGCQ2s8SPHiCDEeQJSep1jqLYeEUClOFG2Qsh+4FU6G9VeqpZnGW/Su8LQGc4YKni5rYSQ== - dependencies: - readable-stream "~2.3.6" - xtend "~4.0.1" - through2@^3.0.1: version "3.0.2" resolved "https://registry.yarnpkg.com/through2/-/through2-3.0.2.tgz#99f88931cfc761ec7678b41d5d7336b5b6a07bf4" @@ -29523,7 +28803,7 @@ through2@^3.0.1: inherits "^2.0.4" readable-stream "2 || 3" -"through@>=2.2.7 <3", through@^2.3.6: +through@^2.3.6: version "2.3.8" resolved "https://registry.yarnpkg.com/through/-/through-2.3.8.tgz#0dd4c9ffaabc357960b1b724115d7e0e86a2e1f5" integrity sha1-DdTJ/6q8NXlgsbckEV1+Doai4fU= @@ -29619,18 +28899,6 @@ tinyspy@^4.0.3: resolved "https://registry.yarnpkg.com/tinyspy/-/tinyspy-4.0.3.tgz#d1d0f0602f4c15f1aae083a34d6d0df3363b1b52" integrity sha512-t2T/WLB2WRgZ9EpE4jgPJ9w+i66UZfDc8wHh0xrwiRNN+UwH98GIJkTeZqX9rg0i0ptwzqW+uYeIF0T4F8LR7A== -tldts-core@^6.1.86: - version "6.1.86" - resolved "https://registry.yarnpkg.com/tldts-core/-/tldts-core-6.1.86.tgz#a93e6ed9d505cb54c542ce43feb14c73913265d8" - integrity sha512-Je6p7pkk+KMzMv2XXKmAE3McmolOQFdxkKw0R8EYNr7sELW46JqnNeTX8ybPiQgvg1ymCoF8LXs5fzFaZvJPTA== - -tldts@^6.1.32: - version "6.1.86" - resolved "https://registry.yarnpkg.com/tldts/-/tldts-6.1.86.tgz#087e0555b31b9725ee48ca7e77edc56115cd82f7" - integrity sha512-WMi/OQ2axVTf/ykqCQgXiIct+mSQDFdH2fkwhPwgEwvJ1kSzZRiinb0zF2Xb8u4+OqPChmyI6MEu4EezNJz+FQ== - dependencies: - tldts-core "^6.1.86" - tmp@0.0.28: version "0.0.28" resolved "https://registry.yarnpkg.com/tmp/-/tmp-0.0.28.tgz#172735b7f614ea7af39664fa84cf0de4e515d120" @@ -29703,7 +28971,7 @@ to-regex@^3.0.1, to-regex@^3.0.2: regex-not "^1.0.2" safe-regex "^1.1.0" -toidentifier@1.0.1, toidentifier@~1.0.1: +toidentifier@~1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/toidentifier/-/toidentifier-1.0.1.tgz#3be34321a88a820ed1bd80dfaa33e479fbb8dd35" integrity sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA== @@ -29744,13 +29012,6 @@ tough-cookie@^4.1.2: universalify "^0.2.0" url-parse "^1.5.3" -tough-cookie@^5.0.0: - version "5.1.2" - resolved "https://registry.yarnpkg.com/tough-cookie/-/tough-cookie-5.1.2.tgz#66d774b4a1d9e12dc75089725af3ac75ec31bed7" - integrity sha512-FVDYdxtnj0G6Qm/DhNPSb8Ju59ULcup3tuJxkFb5K8Bv2pUXILbf0xZWU8PX8Ov19OXljbUyveOFwRMwkXzO+A== - dependencies: - tldts "^6.1.32" - tr46@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/tr46/-/tr46-3.0.0.tgz#555c4e297a950617e8eeddef633c87d4d9d6cbf9" @@ -29936,16 +29197,6 @@ turbo-stream@2.4.1: resolved "https://registry.yarnpkg.com/turbo-stream/-/turbo-stream-2.4.1.tgz#c1a64397724084c09b0f6ea4a2c528d3f67931f9" integrity sha512-v8kOJXpG3WoTN/+at8vK7erSzo6nW6CIaeOvNOkHQVDajfz1ZVeSxCbc6tOH4hrGZW7VUCV0TOXd8CPzYnYkrw== -tweetnacl@^0.14.3, tweetnacl@~0.14.0: - version "0.14.5" - resolved "https://registry.yarnpkg.com/tweetnacl/-/tweetnacl-0.14.5.tgz#5ae68177f192d4456269d108afa93ff8743f4f64" - integrity sha512-KXXFFdAbFXY4geFIwoyNK+f5Z1b7swfXABfL7HXCmoIWMKU3dmS26672A4EeQtDzLKy7SXmfBu51JolvEKwtGA== - -typanion@^3.8.0: - version "3.14.0" - resolved "https://registry.yarnpkg.com/typanion/-/typanion-3.14.0.tgz#a766a91810ce8258033975733e836c43a2929b94" - integrity sha512-ZW/lVMRabETuYCd9O9ZvMhAh8GslSqaUjxmK/JLPCh6l73CvLBiuXswj/+7LdnWOgYsQ130FqLzFz5aGT4I3Ug== - type-check@^0.4.0, type-check@~0.4.0: version "0.4.0" resolved "https://registry.yarnpkg.com/type-check/-/type-check-0.4.0.tgz#07b8203bfa7056c0657050e3ccd2c37730bab8f1" @@ -30513,11 +29764,6 @@ universalify@^2.0.0: resolved "https://registry.yarnpkg.com/universalify/-/universalify-2.0.0.tgz#75a4984efedc4b08975c5aeb73f530d02df25717" integrity sha512-hAZsKq7Yy11Zu1DE0OzWjw7nnLZmJZYTDZZyEFHZdUhV8FkH5MCfoU1XMaxXovpyW5nq5scPqq0ZDP9Zyl04oQ== -unix-crypt-td-js@1.1.4: - version "1.1.4" - resolved "https://registry.yarnpkg.com/unix-crypt-td-js/-/unix-crypt-td-js-1.1.4.tgz#4912dfad1c8aeb7d20fa0a39e4c31918c1d5d5dd" - integrity sha512-8rMeVYWSIyccIJscb9NdCfZKSRBKYTeVnwmiRYT2ulE3qd1RaDQ0xQDP+rI3ccIWbhu/zuo5cgN8z73belNZgw== - unpipe@~1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/unpipe/-/unpipe-1.0.0.tgz#b2bf4ee8514aae6165b4817829d21b2ef49904ec" @@ -30843,11 +30089,6 @@ validate-peer-dependencies@^1.1.0: resolve-package-path "^3.1.0" semver "^7.3.2" -validator@13.15.26: - version "13.15.26" - resolved "https://registry.yarnpkg.com/validator/-/validator-13.15.26.tgz#36c3deeab30e97806a658728a155c66fcaa5b944" - integrity sha512-spH26xU080ydGggxRyR1Yhcbgx+j3y5jbNXk/8L+iRvdIEQ4uTRH2Sgf2dokud6Q4oAtsbNvJ1Ft+9xmm6IZcA== - value-equal@^0.2.0: version "0.2.1" resolved "https://registry.yarnpkg.com/value-equal/-/value-equal-0.2.1.tgz#c220a304361fce6994dbbedaa3c7e1a1b895871d" @@ -30868,76 +30109,6 @@ vary@^1, vary@^1.1.2, vary@~1.1.2: resolved "https://registry.yarnpkg.com/vary/-/vary-1.1.2.tgz#2299f02c6ded30d4a5961b0b9f74524a18f634fc" integrity sha1-IpnwLG3tMNSllhsLn3RSShj2NPw= -verdaccio-audit@13.0.0-next-8.37: - version "13.0.0-next-8.37" - resolved "https://registry.yarnpkg.com/verdaccio-audit/-/verdaccio-audit-13.0.0-next-8.37.tgz#ab1dce7983f688e6ce84ae9b04644a3fc774e606" - integrity sha512-ckn4xxNEkK5lflwb8a6xs2j6rVe//9sEH4rJHBqh2RelKYnFkxHbnN06gsdV2KtqSKDD9F4NE2UDA9SSs8E90w== - dependencies: - "@verdaccio/config" "8.0.0-next-8.37" - "@verdaccio/core" "8.0.0-next-8.37" - express "4.22.1" - https-proxy-agent "5.0.1" - node-fetch cjs - -verdaccio-htpasswd@13.0.0-next-8.37: - version "13.0.0-next-8.37" - resolved "https://registry.yarnpkg.com/verdaccio-htpasswd/-/verdaccio-htpasswd-13.0.0-next-8.37.tgz#7b2be17f6ca9c35726c0baeb5089379bf54f5c2c" - integrity sha512-25MjzPLJG8mfPe4jtR8+3B8PCfBl0D2DRDnsP+KJPn8yBbUiO/GJaw2dyGOiVA7ZTyAWKDnN0WvmmIs+Ibj4+g== - dependencies: - "@verdaccio/core" "8.0.0-next-8.37" - "@verdaccio/file-locking" "13.0.0-next-8.7" - apache-md5 "1.1.8" - bcryptjs "2.4.3" - debug "4.4.3" - http-errors "2.0.1" - unix-crypt-td-js "1.1.4" - -verdaccio@6.5.0: - version "6.5.0" - resolved "https://registry.yarnpkg.com/verdaccio/-/verdaccio-6.5.0.tgz#d27d48ad7a21ce8f361a078137d784aa731d2b46" - integrity sha512-PFsGVvSyS47ZAt58MiL+5hX0jexgv20rrhEL2qEF5wGV9OLP9Cbm67aN5+PJT3pdQyghGk2Yq4/SVNwCmB/oHA== - dependencies: - "@cypress/request" "3.0.10" - "@verdaccio/auth" "8.0.0-next-8.37" - "@verdaccio/config" "8.0.0-next-8.37" - "@verdaccio/core" "8.0.0-next-8.37" - "@verdaccio/hooks" "8.0.0-next-8.37" - "@verdaccio/loaders" "8.0.0-next-8.27" - "@verdaccio/local-storage-legacy" "11.1.1" - "@verdaccio/logger" "8.0.0-next-8.37" - "@verdaccio/middleware" "8.0.0-next-8.37" - "@verdaccio/package-filter" "13.0.0-next-8.5" - "@verdaccio/search-indexer" "8.0.0-next-8.6" - "@verdaccio/signature" "8.0.0-next-8.29" - "@verdaccio/streams" "10.2.1" - "@verdaccio/tarball" "13.0.0-next-8.37" - "@verdaccio/ui-theme" "9.0.0-next-9.10" - "@verdaccio/url" "13.0.0-next-8.37" - "@verdaccio/utils" "8.1.0-next-8.37" - JSONStream "1.3.5" - async "3.2.6" - clipanion "4.0.0-rc.4" - compression "1.8.1" - cors "2.8.6" - debug "4.4.3" - envinfo "7.21.0" - express "4.22.1" - lodash "4.18.1" - lru-cache "7.18.3" - mime "3.0.0" - semver "7.7.4" - verdaccio-audit "13.0.0-next-8.37" - verdaccio-htpasswd "13.0.0-next-8.37" - -verror@1.10.0: - version "1.10.0" - resolved "https://registry.yarnpkg.com/verror/-/verror-1.10.0.tgz#3a105ca17053af55d6e270c1f8288682e18da400" - integrity sha512-ZZKSmDAEFOijERBLkmYfJ+vmk3w+7hOLYDNkRCuRuMJGEmqYNCNLyBBFwWKVMhfwaEF3WOd0Zlw86U/WC/+nYw== - dependencies: - assert-plus "^1.0.0" - core-util-is "1.0.2" - extsprintf "^1.2.0" - vfile-location@^4.0.0: version "4.1.0" resolved "https://registry.yarnpkg.com/vfile-location/-/vfile-location-4.1.0.tgz#69df82fb9ef0a38d0d02b90dd84620e120050dd0" @@ -31934,7 +31105,7 @@ xmlchars@^2.2.0: resolved "https://registry.yarnpkg.com/xmlchars/-/xmlchars-2.2.0.tgz#060fe1bcb7f9c76fe2a17db86a9bc3ab894210cb" integrity sha512-JZnDKK8B0RCDw84FNdDAIpZK+JuJw+s7Lz8nksI7SIuU3UXJJslUthsi+uWBUYOwPFwW7W7PRLRfUKpxjtjFCw== -xtend@^4.0.0, xtend@^4.0.2, xtend@~4.0.1: +xtend@^4.0.0, xtend@^4.0.2: version "4.0.2" resolved "https://registry.yarnpkg.com/xtend/-/xtend-4.0.2.tgz#bb72779f5fa465186b1f438f674fa347fdb5db54" integrity sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ== @@ -31988,16 +31159,16 @@ yam@^1.0.0: fs-extra "^4.0.2" lodash.merge "^4.6.0" -yaml@2.8.3, yaml@^2.6.0, yaml@^2.8.0, yaml@^2.8.3: - version "2.8.3" - resolved "https://registry.yarnpkg.com/yaml/-/yaml-2.8.3.tgz#a0d6bd2efb3dd03c59370223701834e60409bd7d" - integrity sha512-AvbaCLOO2Otw/lW5bmh9d/WEdcDFdQp2Z2ZUH3pX9U2ihyUY0nvLv7J6TrWowklRGPYbB/IuIMfYgxaCPg5Bpg== - yaml@^1.10.0: version "1.10.2" resolved "https://registry.yarnpkg.com/yaml/-/yaml-1.10.2.tgz#2301c5ffbf12b467de8da2333a459e29e7920e4b" integrity sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg== +yaml@^2.6.0, yaml@^2.8.0, yaml@^2.8.3: + version "2.8.3" + resolved "https://registry.yarnpkg.com/yaml/-/yaml-2.8.3.tgz#a0d6bd2efb3dd03c59370223701834e60409bd7d" + integrity sha512-AvbaCLOO2Otw/lW5bmh9d/WEdcDFdQp2Z2ZUH3pX9U2ihyUY0nvLv7J6TrWowklRGPYbB/IuIMfYgxaCPg5Bpg== + yargs-parser@21.1.1, yargs-parser@^21.0.0, yargs-parser@^21.1.1: version "21.1.1" resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-21.1.1.tgz#9096bceebf990d21bb31fa9516e0ede294a77d35" From ceadf2f06f9288472425754db3b2284b3f8c5617 Mon Sep 17 00:00:00 2001 From: Francesco Gringl-Novy Date: Wed, 22 Apr 2026 10:23:12 +0200 Subject: [PATCH 22/41] ref(node): Vendor undici instrumentation (#20190) ref https://github.com/getsentry/sentry-javascript/issues/20165 This vendors the instrumentation in with no logic changes as a first step. Here we can likely do a lot of cleanup and possibly already refactor this away from using an OTEL instrumentation base at all even in v10, as this is fully diagnostics channel based already. --- .oxlintrc.base.json | 13 + .../src/integrations/node-fetch/types.ts | 7 +- .../src/utils/outgoingFetchRequest.ts | 37 +- packages/node/package.json | 1 - .../{node-fetch.ts => node-fetch/index.ts} | 6 +- .../node-fetch/vendored/internal-types.ts | 52 ++ .../integrations/node-fetch/vendored/types.ts | 92 +++ .../node-fetch/vendored/undici.ts | 522 ++++++++++++++++++ yarn.lock | 11 +- 9 files changed, 721 insertions(+), 20 deletions(-) rename packages/node/src/integrations/{node-fetch.ts => node-fetch/index.ts} (96%) create mode 100644 packages/node/src/integrations/node-fetch/vendored/internal-types.ts create mode 100644 packages/node/src/integrations/node-fetch/vendored/types.ts create mode 100644 packages/node/src/integrations/node-fetch/vendored/undici.ts diff --git a/.oxlintrc.base.json b/.oxlintrc.base.json index 96122ee95b3e..91ba709d0e7f 100644 --- a/.oxlintrc.base.json +++ b/.oxlintrc.base.json @@ -117,6 +117,19 @@ "max-lines": "off" } }, + { + "files": ["**/integrations/node-fetch/vendored/**/*.ts"], + "rules": { + "typescript/consistent-type-imports": "off", + "typescript/no-unnecessary-type-assertion": "off", + "typescript/no-unsafe-member-access": "off", + "typescript/no-explicit-any": "off", + "typescript/prefer-for-of": "off", + "max-lines": "off", + "complexity": "off", + "no-param-reassign": "off" + } + }, { "files": [ "**/scenarios/**", diff --git a/packages/node-core/src/integrations/node-fetch/types.ts b/packages/node-core/src/integrations/node-fetch/types.ts index 0139dadde413..26ed710f2474 100644 --- a/packages/node-core/src/integrations/node-fetch/types.ts +++ b/packages/node-core/src/integrations/node-fetch/types.ts @@ -15,7 +15,8 @@ */ /** - * Vendored from https://github.com/open-telemetry/opentelemetry-js-contrib/blob/28e209a9da36bc4e1f8c2b0db7360170ed46cb80/plugins/node/instrumentation-undici/src/types.ts + * Aligned with upstream Undici request shape; see `packages/node/.../node-fetch/vendored/types.ts` + * (vendored from `@opentelemetry/instrumentation-undici`). */ export interface UndiciRequest { @@ -24,9 +25,9 @@ export interface UndiciRequest { path: string; /** * Serialized string of headers in the form `name: value\r\n` for v5 - * Array of strings v6 + * Array of strings `[key1, value1, ...]` for v6 (values may be `string | string[]`) */ - headers: string | string[]; + headers: string | (string | string[])[]; /** * Helper method to add headers (from v6) */ diff --git a/packages/node-core/src/utils/outgoingFetchRequest.ts b/packages/node-core/src/utils/outgoingFetchRequest.ts index cad20496e478..85edd6a73b58 100644 --- a/packages/node-core/src/utils/outgoingFetchRequest.ts +++ b/packages/node-core/src/utils/outgoingFetchRequest.ts @@ -42,7 +42,13 @@ export function addTracePropagationHeadersToFetchRequest( const { 'sentry-trace': sentryTrace, baggage, traceparent } = addedHeaders; - const requestHeaders = Array.isArray(request.headers) ? request.headers : stringToArrayHeaders(request.headers); + // Undici can expose headers either as a raw string (v5-style) or as a flat array of pairs (v6-style). + // In the array form, even indices are header names and odd indices are values; in undici v6 a value + // may be `string | string[]` when a header has multiple values. The helpers below (_deduplicateArrayHeader, + // push, etc.) expect each value slot to be a single string, so we normalize array headers first. + const requestHeaders: string[] = Array.isArray(request.headers) + ? normalizeUndiciHeaderPairs(request.headers) + : stringToArrayHeaders(request.headers); // OTel's UndiciInstrumentation calls propagation.inject() which unconditionally // appends headers to the request. When the user also sets headers via getTraceData(), @@ -84,12 +90,37 @@ export function addTracePropagationHeadersToFetchRequest( } } - if (!Array.isArray(request.headers)) { - // For original string request headers, we need to write them back to the request + if (Array.isArray(request.headers)) { + // Replace contents in place so we keep the same array reference undici/fetch still holds. + // `requestHeaders` is already normalized (string pairs only); splice writes them back. + request.headers.splice(0, request.headers.length, ...requestHeaders); + } else { request.headers = arrayToStringHeaders(requestHeaders); } } +/** + * Convert undici’s header array into `[name, value, name, value, ...]` where every value is a string. + * + * Undici v6 uses this shape: `[k1, v1, k2, v2, ...]`. Types allow each `v` to be `string | string[]` when + * that header has multiple values. Sentry’s dedupe/merge helpers expect one string per value slot, so + * multi-value arrays are joined with `', '`. Missing value slots become `''`. + */ +function normalizeUndiciHeaderPairs(headers: (string | string[])[]): string[] { + const out: string[] = []; + for (let i = 0; i < headers.length; i++) { + const entry = headers[i]; + if (i % 2 === 0) { + // Header name (should always be a string; coerce defensively). + out.push(typeof entry === 'string' ? entry : String(entry)); + } else { + // Header value: flatten `string[]` to a single string for downstream string-only helpers. + out.push(Array.isArray(entry) ? entry.join(', ') : (entry ?? '')); + } + } + return out; +} + function stringToArrayHeaders(requestHeaders: string): string[] { const headersArray = requestHeaders.split('\r\n'); const headers: string[] = []; diff --git a/packages/node/package.json b/packages/node/package.json index d1c5cb5bbdc7..051f8366862f 100644 --- a/packages/node/package.json +++ b/packages/node/package.json @@ -88,7 +88,6 @@ "@opentelemetry/instrumentation-pg": "0.66.0", "@opentelemetry/instrumentation-redis": "0.62.0", "@opentelemetry/instrumentation-tedious": "0.33.0", - "@opentelemetry/instrumentation-undici": "0.24.0", "@opentelemetry/sdk-trace-base": "^2.6.1", "@opentelemetry/semantic-conventions": "^1.40.0", "@prisma/instrumentation": "7.6.0", diff --git a/packages/node/src/integrations/node-fetch.ts b/packages/node/src/integrations/node-fetch/index.ts similarity index 96% rename from packages/node/src/integrations/node-fetch.ts rename to packages/node/src/integrations/node-fetch/index.ts index 74bfff2dab47..2aa277b211c4 100644 --- a/packages/node/src/integrations/node-fetch.ts +++ b/packages/node/src/integrations/node-fetch/index.ts @@ -1,5 +1,5 @@ -import type { UndiciInstrumentationConfig } from '@opentelemetry/instrumentation-undici'; -import { UndiciInstrumentation } from '@opentelemetry/instrumentation-undici'; +import type { UndiciInstrumentationConfig } from './vendored/types'; +import { UndiciInstrumentation } from './vendored/undici'; import type { IntegrationFn } from '@sentry/core'; import { defineIntegration, @@ -12,7 +12,7 @@ import { } from '@sentry/core'; import type { NodeClient } from '@sentry/node-core'; import { generateInstrumentOnce, SentryNodeFetchInstrumentation } from '@sentry/node-core'; -import type { NodeClientOptions } from '../types'; +import type { NodeClientOptions } from '../../types'; const INTEGRATION_NAME = 'NodeFetch'; diff --git a/packages/node/src/integrations/node-fetch/vendored/internal-types.ts b/packages/node/src/integrations/node-fetch/vendored/internal-types.ts new file mode 100644 index 000000000000..cde91d1e139c --- /dev/null +++ b/packages/node/src/integrations/node-fetch/vendored/internal-types.ts @@ -0,0 +1,52 @@ +/* + * Copyright The OpenTelemetry Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * NOTICE from the Sentry authors: + * - Vendored from: https://github.com/open-telemetry/opentelemetry-js-contrib/tree/ed97091c9890dd18e52759f2ea98e9d7593b3ae4/packages/instrumentation-undici + * - Upstream version: @opentelemetry/instrumentation-undici@0.24.0 + * - Tracking issue: https://github.com/getsentry/sentry-javascript/issues/20165 + */ +/* eslint-disable -- vendored @opentelemetry/instrumentation-undici (#20165) */ + +import type { UndiciRequest, UndiciResponse } from './types'; + +export interface ListenerRecord { + name: string; + unsubscribe: () => void; +} + +export interface RequestMessage { + request: UndiciRequest; +} + +export interface RequestHeadersMessage { + request: UndiciRequest; + socket: any; +} + +export interface ResponseHeadersMessage { + request: UndiciRequest; + response: UndiciResponse; +} + +export interface RequestTrailersMessage { + request: UndiciRequest; + response: UndiciResponse; +} + +export interface RequestErrorMessage { + request: UndiciRequest; + error: Error; +} diff --git a/packages/node/src/integrations/node-fetch/vendored/types.ts b/packages/node/src/integrations/node-fetch/vendored/types.ts new file mode 100644 index 000000000000..f7c7d46c014a --- /dev/null +++ b/packages/node/src/integrations/node-fetch/vendored/types.ts @@ -0,0 +1,92 @@ +/* + * Copyright The OpenTelemetry Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * NOTICE from the Sentry authors: + * - Vendored from: https://github.com/open-telemetry/opentelemetry-js-contrib/tree/ed97091c9890dd18e52759f2ea98e9d7593b3ae4/packages/instrumentation-undici + * - Upstream version: @opentelemetry/instrumentation-undici@0.24.0 + * - Tracking issue: https://github.com/getsentry/sentry-javascript/issues/20165 + */ +/* eslint-disable -- vendored @opentelemetry/instrumentation-undici (#20165) */ + +import type { InstrumentationConfig } from '@opentelemetry/instrumentation'; +import type { Attributes, Span } from '@opentelemetry/api'; + +export interface UndiciRequest { + origin: string; + method: string; + path: string; + /** + * Serialized string of headers in the form `name: value\r\n` for v5 + * Array of strings `[key1, value1, key2, value2]`, where values are + * `string | string[]` for v6 + */ + headers: string | (string | string[])[]; + /** + * Helper method to add headers (from v6) + */ + addHeader: (name: string, value: string) => void; + throwOnError: boolean; + completed: boolean; + aborted: boolean; + idempotent: boolean; + contentLength: number | null; + contentType: string | null; + body: any; +} + +export interface UndiciResponse { + headers: Buffer[]; + statusCode: number; + statusText: string; +} + +export interface IgnoreRequestFunction { + (request: T): boolean; +} + +export interface RequestHookFunction { + (span: Span, request: T): void; +} + +export interface ResponseHookFunction { + (span: Span, info: { request: RequestType; response: ResponseType }): void; +} + +export interface StartSpanHookFunction { + (request: T): Attributes; +} + +// This package will instrument HTTP requests made through `undici` or `fetch` global API +// so it seems logical to have similar options than the HTTP instrumentation +export interface UndiciInstrumentationConfig< + RequestType = UndiciRequest, + ResponseType = UndiciResponse, +> extends InstrumentationConfig { + /** Not trace all outgoing requests that matched with custom function */ + ignoreRequestHook?: IgnoreRequestFunction; + /** Function for adding custom attributes before request is handled */ + requestHook?: RequestHookFunction; + /** Function called once response headers have been received */ + responseHook?: ResponseHookFunction; + /** Function for adding custom attributes before a span is started */ + startSpanHook?: StartSpanHookFunction; + /** Require parent to create span for outgoing requests */ + requireParentforSpans?: boolean; + /** Map the following HTTP headers to span attributes. */ + headersToSpanAttributes?: { + requestHeaders?: string[]; + responseHeaders?: string[]; + }; +} diff --git a/packages/node/src/integrations/node-fetch/vendored/undici.ts b/packages/node/src/integrations/node-fetch/vendored/undici.ts new file mode 100644 index 000000000000..55e09d7c4d53 --- /dev/null +++ b/packages/node/src/integrations/node-fetch/vendored/undici.ts @@ -0,0 +1,522 @@ +/* + * Copyright The OpenTelemetry Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * NOTICE from the Sentry authors: + * - Vendored from: https://github.com/open-telemetry/opentelemetry-js-contrib/tree/ed97091c9890dd18e52759f2ea98e9d7593b3ae4/packages/instrumentation-undici + * - Upstream version: @opentelemetry/instrumentation-undici@0.24.0 + * - Tracking issue: https://github.com/getsentry/sentry-javascript/issues/20165 + * - Minor TypeScript strictness adjustments for this repository's compiler settings + */ +/* eslint-disable -- vendored @opentelemetry/instrumentation-undici (#20165) */ + +import * as diagch from 'diagnostics_channel'; +import { URL } from 'url'; + +import { InstrumentationBase, safeExecuteInTheMiddle } from '@opentelemetry/instrumentation'; +import type { Attributes, Histogram, HrTime, Span } from '@opentelemetry/api'; +import { + context, + INVALID_SPAN_CONTEXT, + propagation, + SpanKind, + SpanStatusCode, + trace, + ValueType, +} from '@opentelemetry/api'; +import { hrTime, hrTimeDuration, hrTimeToMilliseconds } from '@opentelemetry/core'; +import { + ATTR_ERROR_TYPE, + ATTR_HTTP_REQUEST_METHOD, + ATTR_HTTP_REQUEST_METHOD_ORIGINAL, + ATTR_HTTP_RESPONSE_STATUS_CODE, + ATTR_NETWORK_PEER_ADDRESS, + ATTR_NETWORK_PEER_PORT, + ATTR_SERVER_ADDRESS, + ATTR_SERVER_PORT, + ATTR_URL_FULL, + ATTR_URL_PATH, + ATTR_URL_QUERY, + ATTR_URL_SCHEME, + ATTR_USER_AGENT_ORIGINAL, + METRIC_HTTP_CLIENT_REQUEST_DURATION, +} from '@opentelemetry/semantic-conventions'; + +import type { + ListenerRecord, + RequestHeadersMessage, + RequestMessage, + RequestTrailersMessage, + ResponseHeadersMessage, +} from './internal-types'; +import type { UndiciInstrumentationConfig, UndiciRequest } from './types'; + +import { SDK_VERSION } from '@sentry/core'; + +interface InstrumentationRecord { + span: Span; + attributes: Attributes; + startTime: HrTime; +} + +const PACKAGE_NAME = '@sentry/instrumentation-undici'; + +// A combination of https://github.com/elastic/apm-agent-nodejs and +// https://github.com/gadget-inc/opentelemetry-instrumentations/blob/main/packages/opentelemetry-instrumentation-undici/src/index.ts +export class UndiciInstrumentation extends InstrumentationBase { + // Keep ref to avoid https://github.com/nodejs/node/issues/42170 bug and for + // unsubscribing. + declare private _channelSubs: Array; + private _recordFromReq = new WeakMap(); + + declare private _httpClientDurationHistogram: Histogram; + + constructor(config: UndiciInstrumentationConfig = {}) { + super(PACKAGE_NAME, SDK_VERSION, config); + } + + // No need to instrument files/modules + protected override init() { + return undefined; + } + + override disable(): void { + super.disable(); + this._channelSubs.forEach(sub => sub.unsubscribe()); + this._channelSubs.length = 0; + } + + override enable(): void { + // "enabled" handling is currently a bit messy with InstrumentationBase. + // If constructed with `{enabled: false}`, this `.enable()` is still called, + // and `this.getConfig().enabled !== this.isEnabled()`, creating confusion. + // + // For now, this class will setup for instrumenting if `.enable()` is + // called, but use `this.getConfig().enabled` to determine if + // instrumentation should be generated. This covers the more likely common + // case of config being given a construction time, rather than later via + // `instance.enable()`, `.disable()`, or `.setConfig()` calls. + super.enable(); + + // This method is called by the super-class constructor before ours is + // called. So we need to ensure the property is initalized. + this._channelSubs = this._channelSubs || []; + + // Avoid to duplicate subscriptions + if (this._channelSubs.length > 0) { + return; + } + + this.subscribeToChannel('undici:request:create', this.onRequestCreated.bind(this)); + this.subscribeToChannel('undici:client:sendHeaders', this.onRequestHeaders.bind(this)); + this.subscribeToChannel('undici:request:headers', this.onResponseHeaders.bind(this)); + this.subscribeToChannel('undici:request:trailers', this.onDone.bind(this)); + this.subscribeToChannel('undici:request:error', this.onError.bind(this)); + } + + protected override _updateMetricInstruments() { + this._httpClientDurationHistogram = this.meter.createHistogram(METRIC_HTTP_CLIENT_REQUEST_DURATION, { + description: 'Measures the duration of outbound HTTP requests.', + unit: 's', + valueType: ValueType.DOUBLE, + advice: { + explicitBucketBoundaries: [0.005, 0.01, 0.025, 0.05, 0.075, 0.1, 0.25, 0.5, 0.75, 1, 2.5, 5, 7.5, 10], + }, + }); + } + + private subscribeToChannel(diagnosticChannel: string, onMessage: (message: any, name: string | symbol) => void) { + // `diagnostics_channel` had a ref counting bug until v18.19.0. + // https://github.com/nodejs/node/pull/47520 + const [major = 0, minor = 0] = process.version + .replace('v', '') + .split('.') + .map(n => Number(n)); + const useNewSubscribe = major > 18 || (major === 18 && minor >= 19); + + let unsubscribe: () => void; + if (useNewSubscribe) { + diagch.subscribe?.(diagnosticChannel, onMessage); + unsubscribe = () => diagch.unsubscribe?.(diagnosticChannel, onMessage); + } else { + const channel = diagch.channel(diagnosticChannel); + channel.subscribe(onMessage); + unsubscribe = () => channel.unsubscribe(onMessage); + } + + this._channelSubs.push({ + name: diagnosticChannel, + unsubscribe, + }); + } + + private parseRequestHeaders(request: UndiciRequest) { + const result = new Map(); + + if (Array.isArray(request.headers)) { + // headers are an array [k1, v2, k2, v2] (undici v6+) + // values could be string or a string[] for multiple values + for (let i = 0; i < request.headers.length; i += 2) { + const key = request.headers[i]; + const value = request.headers[i + 1]; + + // Key should always be a string, but the types don't know that, and let's be safe + if (typeof key === 'string' && value !== undefined) { + result.set(key.toLowerCase(), value); + } + } + } else if (typeof request.headers === 'string') { + // headers are a raw string (undici v5) + // headers could be repeated in several lines for multiple values + const headers = request.headers.split('\r\n'); + for (const line of headers) { + if (!line) { + continue; + } + const colonIndex = line.indexOf(':'); + if (colonIndex === -1) { + // Invalid header? Probably this can't happen, but again let's be safe. + continue; + } + const key = line.substring(0, colonIndex).toLowerCase(); + const value = line.substring(colonIndex + 1).trim(); + const allValues = result.get(key); + + if (allValues && Array.isArray(allValues)) { + allValues.push(value); + } else if (allValues) { + result.set(key, [allValues, value]); + } else { + result.set(key, value); + } + } + } + return result; + } + + // This is the 1st message we receive for each request (fired after request creation). Here we will + // create the span and populate some atttributes, then link the span to the request for further + // span processing + private onRequestCreated({ request }: RequestMessage): void { + // Ignore if: + // - instrumentation is disabled + // - ignored by config + // - method is 'CONNECT' + const config = this.getConfig(); + const enabled = config.enabled !== false; + const shouldIgnoreReq = safeExecuteInTheMiddle( + () => !enabled || request.method === 'CONNECT' || config.ignoreRequestHook?.(request), + e => e && this._diag.error('caught ignoreRequestHook error: ', e), + true, + ); + + if (shouldIgnoreReq) { + return; + } + + const startTime = hrTime(); + let requestUrl; + try { + requestUrl = new URL(request.path, request.origin); + } catch (err) { + this._diag.warn('could not determine url.full:', err); + // Skip instrumenting this request. + return; + } + const urlScheme = requestUrl.protocol.replace(':', ''); + const requestMethod = this.getRequestMethod(request.method); + const attributes: Attributes = { + [ATTR_HTTP_REQUEST_METHOD]: requestMethod, + [ATTR_HTTP_REQUEST_METHOD_ORIGINAL]: request.method, + [ATTR_URL_FULL]: requestUrl.toString(), + [ATTR_URL_PATH]: requestUrl.pathname, + [ATTR_URL_QUERY]: requestUrl.search, + [ATTR_URL_SCHEME]: urlScheme, + }; + + const schemePorts: Record = { https: '443', http: '80' }; + const serverAddress = requestUrl.hostname; + const serverPort = requestUrl.port || schemePorts[urlScheme]; + + attributes[ATTR_SERVER_ADDRESS] = serverAddress; + if (serverPort && !isNaN(Number(serverPort))) { + attributes[ATTR_SERVER_PORT] = Number(serverPort); + } + + // Get user agent from headers + const headersMap = this.parseRequestHeaders(request); + const userAgentValues = headersMap.get('user-agent'); + + if (userAgentValues) { + // NOTE: having multiple user agents is not expected so + // we're going to take last one like `curl` does + // ref: https://curl.se/docs/manpage.html#-A + const userAgent = Array.isArray(userAgentValues) ? userAgentValues[userAgentValues.length - 1] : userAgentValues; + attributes[ATTR_USER_AGENT_ORIGINAL] = userAgent; + } + + // Get attributes from the hook if present + const hookAttributes = safeExecuteInTheMiddle( + () => config.startSpanHook?.(request), + e => e && this._diag.error('caught startSpanHook error: ', e), + true, + ); + if (hookAttributes) { + Object.entries(hookAttributes).forEach(([key, val]) => { + attributes[key] = val; + }); + } + + // Check if parent span is required via config and: + // - if a parent is required but not present, we use a `NoopSpan` to still + // propagate context without recording it. + // - create a span otherwise + const activeCtx = context.active(); + const currentSpan = trace.getSpan(activeCtx); + let span: Span; + + if (config.requireParentforSpans && (!currentSpan || !trace.isSpanContextValid(currentSpan.spanContext()))) { + span = trace.wrapSpanContext(INVALID_SPAN_CONTEXT); + } else { + span = this.tracer.startSpan( + requestMethod === '_OTHER' ? 'HTTP' : requestMethod, + { + kind: SpanKind.CLIENT, + attributes: attributes, + }, + activeCtx, + ); + } + + // Execute the request hook if defined + safeExecuteInTheMiddle( + () => config.requestHook?.(span, request), + e => e && this._diag.error('caught requestHook error: ', e), + true, + ); + + // Context propagation goes last so no hook can tamper + // the propagation headers + const requestContext = trace.setSpan(context.active(), span); + const addedHeaders: Record = {}; + propagation.inject(requestContext, addedHeaders); + + const headerEntries = Object.entries(addedHeaders); + + for (let i = 0; i < headerEntries.length; i++) { + const pair = headerEntries[i]; + if (!pair) { + continue; + } + const [k, v] = pair; + + if (typeof request.addHeader === 'function') { + request.addHeader(k, v); + } else if (typeof request.headers === 'string') { + request.headers += `${k}: ${v}\r\n`; + } else if (Array.isArray(request.headers)) { + // undici@6.11.0 accidentally, briefly removed `request.addHeader()`. + request.headers.push(k, v); + } + } + this._recordFromReq.set(request, { span, attributes, startTime }); + } + + // This is the 2nd message we receive for each request. It is fired when connection with + // the remote is established and about to send the first byte. Here we do have info about the + // remote address and port so we can populate some `network.*` attributes into the span + private onRequestHeaders({ request, socket }: RequestHeadersMessage): void { + const record = this._recordFromReq.get(request); + + if (!record) { + return; + } + + const config = this.getConfig(); + const { span } = record; + const { remoteAddress, remotePort } = socket; + const spanAttributes: Attributes = { + [ATTR_NETWORK_PEER_ADDRESS]: remoteAddress, + [ATTR_NETWORK_PEER_PORT]: remotePort, + }; + + // After hooks have been processed (which may modify request headers) + // we can collect the headers based on the configuration + if (config.headersToSpanAttributes?.requestHeaders) { + const headersToAttribs = new Set(config.headersToSpanAttributes.requestHeaders.map(n => n.toLowerCase())); + const headersMap = this.parseRequestHeaders(request); + + for (const [name, value] of headersMap.entries()) { + if (headersToAttribs.has(name)) { + const attrValue = Array.isArray(value) ? value : [value]; + spanAttributes[`http.request.header.${name}`] = attrValue; + } + } + } + + span.setAttributes(spanAttributes); + } + + // This is the 3rd message we get for each request and it's fired when the server + // headers are received, body may not be accessible yet. + // From the response headers we can set the status and content length + private onResponseHeaders({ request, response }: ResponseHeadersMessage): void { + const record = this._recordFromReq.get(request); + + if (!record) { + return; + } + + const { span, attributes } = record; + const spanAttributes: Attributes = { + [ATTR_HTTP_RESPONSE_STATUS_CODE]: response.statusCode, + }; + + const config = this.getConfig(); + + // Execute the response hook if defined + safeExecuteInTheMiddle( + () => config.responseHook?.(span, { request, response }), + e => e && this._diag.error('caught responseHook error: ', e), + true, + ); + + if (config.headersToSpanAttributes?.responseHeaders) { + const headersToAttribs = new Set(); + config.headersToSpanAttributes?.responseHeaders.forEach(name => headersToAttribs.add(name.toLowerCase())); + + for (let idx = 0; idx < response.headers.length; idx = idx + 2) { + const nameBuf = response.headers[idx]; + const valueBuf = response.headers[idx + 1]; + if (nameBuf === undefined || valueBuf === undefined) { + continue; + } + const name = nameBuf.toString().toLowerCase(); + const value = valueBuf; + + if (headersToAttribs.has(name)) { + const attrName = `http.response.header.${name}`; + if (!Object.prototype.hasOwnProperty.call(spanAttributes, attrName)) { + spanAttributes[attrName] = [value.toString()]; + } else { + (spanAttributes[attrName] as string[]).push(value.toString()); + } + } + } + } + + span.setAttributes(spanAttributes); + span.setStatus({ + code: response.statusCode >= 400 ? SpanStatusCode.ERROR : SpanStatusCode.UNSET, + }); + record.attributes = Object.assign(attributes, spanAttributes); + } + + // This is the last event we receive if the request went without any errors + private onDone({ request }: RequestTrailersMessage): void { + const record = this._recordFromReq.get(request); + + if (!record) { + return; + } + + const { span, attributes, startTime } = record; + + // End the span + span.end(); + this._recordFromReq.delete(request); + + // Record metrics + this.recordRequestDuration(attributes, startTime); + } + + // This is the event we get when something is wrong in the request like + // - invalid options when calling `fetch` global API or any undici method for request + // - connectivity errors such as unreachable host + // - requests aborted through an `AbortController.signal` + // NOTE: server errors are considered valid responses and it's the lib consumer + // who should deal with that. + private onError({ request, error }: any): void { + const record = this._recordFromReq.get(request); + + if (!record) { + return; + } + + const { span, attributes, startTime } = record; + + // NOTE: in `undici@6.3.0` when request aborted the error type changes from + // a custom error (`RequestAbortedError`) to a built-in `DOMException` carrying + // some differences: + // - `code` is from DOMEXception (ABORT_ERR: 20) + // - `message` changes + // - stacktrace is smaller and contains node internal frames + span.recordException(error); + span.setStatus({ + code: SpanStatusCode.ERROR, + message: error.message, + }); + span.end(); + this._recordFromReq.delete(request); + + // Record metrics (with the error) + attributes[ATTR_ERROR_TYPE] = error.message; + this.recordRequestDuration(attributes, startTime); + } + + private recordRequestDuration(attributes: Attributes, startTime: HrTime) { + // Time to record metrics + const metricsAttributes: Attributes = {}; + // Get the attribs already in span attributes + const keysToCopy = [ + ATTR_HTTP_RESPONSE_STATUS_CODE, + ATTR_HTTP_REQUEST_METHOD, + ATTR_SERVER_ADDRESS, + ATTR_SERVER_PORT, + ATTR_URL_SCHEME, + ATTR_ERROR_TYPE, + ]; + keysToCopy.forEach(key => { + if (key in attributes) { + metricsAttributes[key] = attributes[key]; + } + }); + + // Take the duration and record it + const durationSeconds = hrTimeToMilliseconds(hrTimeDuration(startTime, hrTime())) / 1000; + this._httpClientDurationHistogram.record(durationSeconds, metricsAttributes); + } + + private getRequestMethod(original: string): string { + const knownMethods = { + CONNECT: true, + OPTIONS: true, + HEAD: true, + GET: true, + POST: true, + PUT: true, + PATCH: true, + DELETE: true, + TRACE: true, + // QUERY from https://datatracker.ietf.org/doc/draft-ietf-httpbis-safe-method-w-body/ + QUERY: true, + }; + + if (original.toUpperCase() in knownMethods) { + return original.toUpperCase(); + } + + return '_OTHER'; + } +} diff --git a/yarn.lock b/yarn.lock index be8715b44049..d655cea5359d 100644 --- a/yarn.lock +++ b/yarn.lock @@ -6291,15 +6291,6 @@ "@opentelemetry/semantic-conventions" "^1.33.0" "@types/tedious" "^4.0.14" -"@opentelemetry/instrumentation-undici@0.24.0": - version "0.24.0" - resolved "https://registry.yarnpkg.com/@opentelemetry/instrumentation-undici/-/instrumentation-undici-0.24.0.tgz#6ad41245012742899294edf65aa79fd190369094" - integrity sha512-oKzZ3uvqP17sV0EsoQcJgjEfIp0kiZRbYu/eD8p13Cbahumf8lb/xpYeNr/hfAJ4owzEtIDcGIjprfLcYbIKBQ== - dependencies: - "@opentelemetry/core" "^2.0.0" - "@opentelemetry/instrumentation" "^0.214.0" - "@opentelemetry/semantic-conventions" "^1.24.0" - "@opentelemetry/instrumentation@0.214.0", "@opentelemetry/instrumentation@^0.214.0": version "0.214.0" resolved "https://registry.yarnpkg.com/@opentelemetry/instrumentation/-/instrumentation-0.214.0.tgz#2649e8a29a8c4748bc583d35281c80632f046e25" @@ -6388,7 +6379,7 @@ "@opentelemetry/resources" "2.6.1" "@opentelemetry/semantic-conventions" "^1.29.0" -"@opentelemetry/semantic-conventions@^1.24.0", "@opentelemetry/semantic-conventions@^1.27.0", "@opentelemetry/semantic-conventions@^1.28.0", "@opentelemetry/semantic-conventions@^1.29.0", "@opentelemetry/semantic-conventions@^1.30.0", "@opentelemetry/semantic-conventions@^1.33.0", "@opentelemetry/semantic-conventions@^1.33.1", "@opentelemetry/semantic-conventions@^1.34.0", "@opentelemetry/semantic-conventions@^1.36.0", "@opentelemetry/semantic-conventions@^1.40.0": +"@opentelemetry/semantic-conventions@^1.27.0", "@opentelemetry/semantic-conventions@^1.28.0", "@opentelemetry/semantic-conventions@^1.29.0", "@opentelemetry/semantic-conventions@^1.30.0", "@opentelemetry/semantic-conventions@^1.33.0", "@opentelemetry/semantic-conventions@^1.33.1", "@opentelemetry/semantic-conventions@^1.34.0", "@opentelemetry/semantic-conventions@^1.36.0", "@opentelemetry/semantic-conventions@^1.40.0": version "1.40.0" resolved "https://registry.yarnpkg.com/@opentelemetry/semantic-conventions/-/semantic-conventions-1.40.0.tgz#10b2944ca559386590683392022a897eefd011d3" integrity sha512-cifvXDhcqMwwTlTK04GBNeIe7yyo28Mfby85QXFe1Yk8nmi36Ab/5UQwptOx84SsoGNRg+EVSjwzfSZMy6pmlw== From 738bd420bfd9ecae56e26285bc007fc8f34e1566 Mon Sep 17 00:00:00 2001 From: Sigrid <32902192+s1gr1d@users.noreply.github.com> Date: Wed, 22 Apr 2026 10:48:08 +0200 Subject: [PATCH 23/41] feat(hono): Add runtime packages as optional peer dependencies (#20423) It's not needed to have all runtime-related dependencies installed. People should explicitly install the package needed for the respective runtime. Ref https://github.com/getsentry/sentry-javascript/issues/15260 --- .../test-applications/hono-4/package.json | 2 + packages/hono/README.md | 67 +++++++++++++++++-- packages/hono/package.json | 17 +++-- 3 files changed, 78 insertions(+), 8 deletions(-) diff --git a/dev-packages/e2e-tests/test-applications/hono-4/package.json b/dev-packages/e2e-tests/test-applications/hono-4/package.json index 117555f7241f..53519a1bd80c 100644 --- a/dev-packages/e2e-tests/test-applications/hono-4/package.json +++ b/dev-packages/e2e-tests/test-applications/hono-4/package.json @@ -12,6 +12,8 @@ "test:assert": "TEST_ENV=production playwright test" }, "dependencies": { + "@sentry/bun": "latest || *", + "@sentry/cloudflare": "latest || *", "@sentry/hono": "latest || *", "@sentry/node": "latest || *", "@hono/node-server": "^1.19.10", diff --git a/packages/hono/README.md b/packages/hono/README.md index c359536c656e..236a4133bf67 100644 --- a/packages/hono/README.md +++ b/packages/hono/README.md @@ -29,7 +29,18 @@ npm install @sentry/hono ## Setup (Cloudflare Workers) -### 1. Enable Node.js compatibility +### 1. Install Peer Dependency + +Additionally to `@sentry/hono`, install the `@sentry/cloudflare` package: + +```bashbash +npm install --save @sentry/cloudflare +``` + +Make sure the installed version always stays in sync. The `@sentry/cloudflare` package is a required peer dependency when using `@sentry/hono/cloudflare`. +You won't import `@sentry/cloudflare` directly in your code, but it needs to be installed in your project. + +### 2. Enable Node.js compatibility Set the `nodejs_compat` compatibility flag in your `wrangler.jsonc`/`wrangler.toml` config. This is because the SDK needs access to the `AsyncLocalStorage` API to work correctly. @@ -43,7 +54,7 @@ Set the `nodejs_compat` compatibility flag in your `wrangler.jsonc`/`wrangler.to compatibility_flags = ["nodejs_compat"] ``` -### 2. Initialize Sentry in your Hono app +### 3. Initialize Sentry in your Hono app Initialize the Sentry Hono middleware as early as possible in your app: @@ -85,7 +96,18 @@ export default app; ## Setup (Node) -### 1. Initialize Sentry in your Hono app +### 1. Install Peer Dependency + +Additionally to `@sentry/hono`, install the `@sentry/node` package: + +```bashbash +npm install --save @sentry/node +``` + +Make sure the installed version always stays in sync. The `@sentry/node` package is a required peer dependency when using `@sentry/hono/node`. +You won't import `@sentry/node` directly in your code, but it needs to be installed in your project. + +### 2. Initialize Sentry in your Hono app Initialize the Sentry Hono middleware as early as possible in your app: @@ -109,7 +131,7 @@ app.use( serve(app); ``` -### 2. Add `preload` script to start command +### 3. Add `preload` script to start command To ensure that Sentry can capture spans from third-party libraries (e.g. database clients) used in your Hono app, Sentry needs to wrap these libraries as early as possible. @@ -126,3 +148,40 @@ NODE_OPTIONS="--import @sentry/node/preload" ``` Read more about this preload script in the docs: https://docs.sentry.io/platforms/javascript/guides/hono/install/late-initialization/#late-initialization-with-esm + +## Setup (Bun) + +### 1. Install Peer Dependency + +Additionally to `@sentry/hono`, install the `@sentry/bun` package: + +```bashbash +npm install --save @sentry/bun +``` + +Make sure the installed version always stays in sync. The `@sentry/bun` package is a required peer dependency when using `@sentry/hono/bun`. +You won't import `@sentry/bun` directly in your code, but it needs to be installed in your project. + +### 2. Initialize Sentry in your Hono app + +Initialize the Sentry Hono middleware as early as possible in your app: + +```ts +import { Hono } from 'hono'; +import { serve } from '@hono/node-server'; +import { sentry } from '@sentry/hono/bun'; + +const app = new Hono(); + +// Initialize Sentry middleware right after creating the app +app.use( + sentry(app, { + dsn: '__DSN__', // or process.env.SENTRY_DSN or Bun.env.SENTRY_DSN + tracesSampleRate: 1.0, + }), +); + +// ... your routes and other middleware + +serve(app); +``` diff --git a/packages/hono/package.json b/packages/hono/package.json index 4c0057c74e02..6f26fcaea8ab 100644 --- a/packages/hono/package.json +++ b/packages/hono/package.json @@ -79,18 +79,27 @@ }, "dependencies": { "@opentelemetry/api": "^1.9.1", - "@sentry/bun": "10.49.0", - "@sentry/cloudflare": "10.49.0", - "@sentry/core": "10.49.0", - "@sentry/node": "10.49.0" + "@sentry/core": "10.49.0" }, "peerDependencies": { "@cloudflare/workers-types": "^4.x", + "@sentry/bun": "10.49.0", + "@sentry/cloudflare": "10.49.0", + "@sentry/node": "10.49.0", "hono": "^4.x" }, "peerDependenciesMeta": { "@cloudflare/workers-types": { "optional": true + }, + "@sentry/bun": { + "optional": true + }, + "@sentry/cloudflare": { + "optional": true + }, + "@sentry/node": { + "optional": true } }, "devDependencies": { From 4d8baeaadcf9a9db348a37f81e8d1dee23ff195a Mon Sep 17 00:00:00 2001 From: Sigrid <32902192+s1gr1d@users.noreply.github.com> Date: Wed, 22 Apr 2026 10:53:48 +0200 Subject: [PATCH 24/41] fix(console): Re-patch console in AWS Lambda runtimes (#20337) On AWS Lambda, the Node.js runtime replaces `console.*` methods [with its own loggers](https://github.com/aws/aws-lambda-nodejs-runtime-interface-client/blob/9ab0fac53c768d6d8901dca9788c729d8eba94ec/src/logging/log-patch.ts#L77-L83). This means Sentry's console instrumentation gets silently overwritten, and integrations like `consoleLoggingIntegration` stop capturing console output entirely. This PR fixes that by introducing a `defineProperty`-based patching strategy for Lambda environments. Instead of simply assigning a wrapper to `console.log` (which Lambda can overwrite), we define a getter/setter on the console property. When the Lambda runtime assigns its logger, the setter intercepts it, stores the new function as the underlying delegate, and keeps Sentry's wrapper in place. The handler continues to fire, and the Lambda logger still gets called underneath (I checked that manually - the log is still shown in the CloudWatch logs). This behavior is guarded behind `process.env.LAMBDA_TASK_ROOT`, so non-Lambda environments continue to use the existing `fill()`-based patching with zero behavioral change. If `defineProperty` fails for any reason, it falls back to `fill()`. The setter also handles `consoleSandbox` correctly (recognizes when it restores the original method and allows it through), and defers to other Sentry wrappers by checking for `__sentry_original__`. Closes https://github.com/getsentry/sentry-javascript/issues/18238 --- packages/core/src/instrument/console.ts | 3 +- .../core/test/lib/instrument/console.test.ts | 22 ++ packages/nextjs/src/index.types.ts | 1 + packages/node-core/src/common-exports.ts | 2 +- .../node-core/src/integrations/console.ts | 127 +++++++++ packages/node-core/src/light/sdk.ts | 2 +- packages/node-core/src/sdk/index.ts | 2 +- .../test/integrations/console.test.ts | 259 ++++++++++++++++++ packages/node/src/index.ts | 2 +- 9 files changed, 414 insertions(+), 6 deletions(-) create mode 100644 packages/core/test/lib/instrument/console.test.ts create mode 100644 packages/node-core/src/integrations/console.ts create mode 100644 packages/node-core/test/integrations/console.test.ts diff --git a/packages/core/src/instrument/console.ts b/packages/core/src/instrument/console.ts index e96de345d202..cecf1e5cad8a 100644 --- a/packages/core/src/instrument/console.ts +++ b/packages/core/src/instrument/console.ts @@ -32,8 +32,7 @@ function instrumentConsole(): void { originalConsoleMethods[level] = originalConsoleMethod; return function (...args: any[]): void { - const handlerData: HandlerDataConsole = { args, level }; - triggerHandlers('console', handlerData); + triggerHandlers('console', { args, level } as HandlerDataConsole); const log = originalConsoleMethods[level]; log?.apply(GLOBAL_OBJ.console, args); diff --git a/packages/core/test/lib/instrument/console.test.ts b/packages/core/test/lib/instrument/console.test.ts new file mode 100644 index 000000000000..2499a231712d --- /dev/null +++ b/packages/core/test/lib/instrument/console.test.ts @@ -0,0 +1,22 @@ +import { describe, expect, it, vi } from 'vitest'; +import { addConsoleInstrumentationHandler } from '../../../src/instrument/console'; +import { GLOBAL_OBJ } from '../../../src/utils/worldwide'; + +describe('addConsoleInstrumentationHandler', () => { + it.each(['log', 'warn', 'error', 'debug', 'info'] as const)( + 'calls registered handler when console.%s is called', + level => { + const handler = vi.fn(); + addConsoleInstrumentationHandler(handler); + + GLOBAL_OBJ.console[level]('test message'); + + expect(handler).toHaveBeenCalledWith(expect.objectContaining({ args: ['test message'], level })); + }, + ); + + it('calls through to the underlying console method without throwing', () => { + addConsoleInstrumentationHandler(vi.fn()); + expect(() => GLOBAL_OBJ.console.log('hello')).not.toThrow(); + }); +}); diff --git a/packages/nextjs/src/index.types.ts b/packages/nextjs/src/index.types.ts index 2ae03ae3f204..341e583e90d1 100644 --- a/packages/nextjs/src/index.types.ts +++ b/packages/nextjs/src/index.types.ts @@ -23,6 +23,7 @@ export declare function init( export declare const linkedErrorsIntegration: typeof clientSdk.linkedErrorsIntegration; export declare const contextLinesIntegration: typeof clientSdk.contextLinesIntegration; +export declare const consoleIntegration: typeof serverSdk.consoleIntegration; export declare const spanStreamingIntegration: typeof clientSdk.spanStreamingIntegration; export declare const withStreamedSpan: typeof clientSdk.withStreamedSpan; diff --git a/packages/node-core/src/common-exports.ts b/packages/node-core/src/common-exports.ts index 1c724d2c29f6..b2f1deee7f4a 100644 --- a/packages/node-core/src/common-exports.ts +++ b/packages/node-core/src/common-exports.ts @@ -27,6 +27,7 @@ export { systemErrorIntegration } from './integrations/systemError'; export { childProcessIntegration } from './integrations/childProcess'; export { createSentryWinstonTransport } from './integrations/winston'; export { pinoIntegration } from './integrations/pino'; +export { consoleIntegration } from './integrations/console'; // SDK utilities export { getSentryRelease, defaultStackParser } from './sdk/api'; @@ -117,7 +118,6 @@ export { profiler, consoleLoggingIntegration, createConsolaReporter, - consoleIntegration, wrapMcpServerWithSentry, featureFlagsIntegration, spanStreamingIntegration, diff --git a/packages/node-core/src/integrations/console.ts b/packages/node-core/src/integrations/console.ts new file mode 100644 index 000000000000..d958e00bdf12 --- /dev/null +++ b/packages/node-core/src/integrations/console.ts @@ -0,0 +1,127 @@ +/* eslint-disable @typescript-eslint/no-explicit-any */ +import type { ConsoleLevel, HandlerDataConsole, WrappedFunction } from '@sentry/core'; +import { + CONSOLE_LEVELS, + GLOBAL_OBJ, + consoleIntegration as coreConsoleIntegration, + defineIntegration, + fill, + markFunctionWrapped, + maybeInstrument, + originalConsoleMethods, + triggerHandlers, +} from '@sentry/core'; + +interface ConsoleIntegrationOptions { + levels: ConsoleLevel[]; +} + +/** + * Node-specific console integration that captures breadcrumbs and handles + * the AWS Lambda runtime replacing console methods after our patch. + * + * In Lambda, console methods are patched via `Object.defineProperty` so that + * external replacements (by the Lambda runtime) are absorbed as the delegate + * while our wrapper stays in place. Outside Lambda, this delegates entirely + * to the core `consoleIntegration` which uses the simpler `fill`-based patch. + */ +export const consoleIntegration = defineIntegration((options: Partial = {}) => { + return { + name: 'Console', + setup(client) { + if (process.env.LAMBDA_TASK_ROOT) { + maybeInstrument('console', instrumentConsoleLambda); + } + + // Delegate breadcrumb handling to the core console integration. + const core = coreConsoleIntegration(options); + core.setup?.(client); + }, + }; +}); + +function instrumentConsoleLambda(): void { + const consoleObj = GLOBAL_OBJ?.console; + if (!consoleObj) { + return; + } + + CONSOLE_LEVELS.forEach((level: ConsoleLevel) => { + if (level in consoleObj) { + patchWithDefineProperty(consoleObj, level); + } + }); +} + +function patchWithDefineProperty(consoleObj: Console, level: ConsoleLevel): void { + const nativeMethod = consoleObj[level] as (...args: unknown[]) => void; + originalConsoleMethods[level] = nativeMethod; + + let delegate: Function = nativeMethod; + let savedDelegate: Function | undefined; + let isExecuting = false; + + const wrapper = function (...args: any[]): void { + if (isExecuting) { + // Re-entrant call: a third party captured `wrapper` via the getter and calls it from inside their replacement. We must + // use `nativeMethod` (not `delegate`) to break the cycle, and we intentionally skip `triggerHandlers` to avoid duplicate + // breadcrumbs. The outer invocation already triggered the handlers for this console call. + nativeMethod.apply(consoleObj, args); + return; + } + isExecuting = true; + try { + triggerHandlers('console', { args, level } as HandlerDataConsole); + delegate.apply(consoleObj, args); + } finally { + isExecuting = false; + } + }; + markFunctionWrapped(wrapper as unknown as WrappedFunction, nativeMethod as unknown as WrappedFunction); + + // consoleSandbox reads originalConsoleMethods[level] to temporarily bypass instrumentation. We replace it with a distinct reference (.bind creates a + // new function identity) so the setter can tell apart "consoleSandbox bypass" from "external code restoring a native method captured before Sentry init." + const sandboxBypass = nativeMethod.bind(consoleObj); + originalConsoleMethods[level] = sandboxBypass; + + try { + let current: any = wrapper; + + Object.defineProperty(consoleObj, level, { + configurable: true, + enumerable: true, + get() { + return current; + }, + set(newValue) { + if (newValue === wrapper) { + // consoleSandbox restoring the wrapper: recover the saved delegate. + if (savedDelegate !== undefined) { + delegate = savedDelegate; + savedDelegate = undefined; + } + current = wrapper; + } else if (newValue === sandboxBypass) { + // consoleSandbox entering bypass: save delegate, let getter return sandboxBypass directly so calls skip the wrapper entirely. + savedDelegate = delegate; + current = sandboxBypass; + } else if (typeof newValue === 'function' && !(newValue as WrappedFunction).__sentry_original__) { + delegate = newValue; + current = wrapper; + } else { + current = newValue; + } + }, + }); + } catch { + // Fall back to fill-based patching if defineProperty fails + fill(consoleObj, level, function (originalConsoleMethod: () => any): Function { + originalConsoleMethods[level] = originalConsoleMethod; + + return function (this: Console, ...args: any[]): void { + triggerHandlers('console', { args, level } as HandlerDataConsole); + originalConsoleMethods[level]?.apply(this, args); + }; + }); + } +} diff --git a/packages/node-core/src/light/sdk.ts b/packages/node-core/src/light/sdk.ts index 77b62e9ab2f9..1d57da67a0ab 100644 --- a/packages/node-core/src/light/sdk.ts +++ b/packages/node-core/src/light/sdk.ts @@ -1,7 +1,6 @@ import type { Integration, Options } from '@sentry/core'; import { applySdkMetadata, - consoleIntegration, consoleSandbox, debug, envToBool, @@ -25,6 +24,7 @@ import { onUncaughtExceptionIntegration } from '../integrations/onuncaughtexcept import { onUnhandledRejectionIntegration } from '../integrations/onunhandledrejection'; import { processSessionIntegration } from '../integrations/processSession'; import { INTEGRATION_NAME as SPOTLIGHT_INTEGRATION_NAME, spotlightIntegration } from '../integrations/spotlight'; +import { consoleIntegration } from '../integrations/console'; import { systemErrorIntegration } from '../integrations/systemError'; import { defaultStackParser, getSentryRelease } from '../sdk/api'; import { makeNodeTransport } from '../transports'; diff --git a/packages/node-core/src/sdk/index.ts b/packages/node-core/src/sdk/index.ts index 5ae840e6e976..52271ee62363 100644 --- a/packages/node-core/src/sdk/index.ts +++ b/packages/node-core/src/sdk/index.ts @@ -1,7 +1,6 @@ import type { Integration, Options } from '@sentry/core'; import { applySdkMetadata, - consoleIntegration, consoleSandbox, conversationIdIntegration, debug, @@ -35,6 +34,7 @@ import { onUncaughtExceptionIntegration } from '../integrations/onuncaughtexcept import { onUnhandledRejectionIntegration } from '../integrations/onunhandledrejection'; import { processSessionIntegration } from '../integrations/processSession'; import { INTEGRATION_NAME as SPOTLIGHT_INTEGRATION_NAME, spotlightIntegration } from '../integrations/spotlight'; +import { consoleIntegration } from '../integrations/console'; import { systemErrorIntegration } from '../integrations/systemError'; import { makeNodeTransport } from '../transports'; import type { NodeClientOptions, NodeOptions } from '../types'; diff --git a/packages/node-core/test/integrations/console.test.ts b/packages/node-core/test/integrations/console.test.ts new file mode 100644 index 000000000000..0355fe2d076b --- /dev/null +++ b/packages/node-core/test/integrations/console.test.ts @@ -0,0 +1,259 @@ +// Set LAMBDA_TASK_ROOT before any imports so consoleIntegration uses patchWithDefineProperty +process.env.LAMBDA_TASK_ROOT = '/var/task'; + +import { afterAll, describe, expect, it, vi } from 'vitest'; +import type { WrappedFunction } from '@sentry/core'; +import { + addConsoleInstrumentationHandler, + consoleSandbox, + markFunctionWrapped, + originalConsoleMethods, + GLOBAL_OBJ, +} from '@sentry/core'; +import { consoleIntegration } from '../../src/integrations/console'; + +// Capture the real native method before any patches are installed. +// This simulates external code doing `const log = console.log` before Sentry init. +// oxlint-disable-next-line no-console +const nativeConsoleLog = console.log; + +afterAll(() => { + delete process.env.LAMBDA_TASK_ROOT; +}); + +describe('consoleIntegration in Lambda (patchWithDefineProperty)', () => { + it('calls registered handler when console.log is called', () => { + const handler = vi.fn(); + // Setup the integration so it calls maybeInstrument with the Lambda strategy + consoleIntegration().setup?.({ on: vi.fn() } as any); + + addConsoleInstrumentationHandler(handler); + + GLOBAL_OBJ.console.log('test'); + + expect(handler).toHaveBeenCalledWith(expect.objectContaining({ args: ['test'], level: 'log' })); + }); + + describe('external replacement (e.g. Lambda runtime overwriting console)', () => { + it('keeps firing the handler after console.log is replaced externally', () => { + const handler = vi.fn(); + addConsoleInstrumentationHandler(handler); + + GLOBAL_OBJ.console.log = vi.fn(); + handler.mockClear(); + + GLOBAL_OBJ.console.log('after replacement'); + + expect(handler).toHaveBeenCalledWith(expect.objectContaining({ args: ['after replacement'], level: 'log' })); + }); + + it('calls the external replacement as the underlying method', () => { + addConsoleInstrumentationHandler(vi.fn()); + + const lambdaLogger = vi.fn(); + GLOBAL_OBJ.console.log = lambdaLogger; + + GLOBAL_OBJ.console.log('hello'); + + expect(lambdaLogger).toHaveBeenCalledWith('hello'); + }); + + it('always delegates to the latest replacement', () => { + addConsoleInstrumentationHandler(vi.fn()); + + const first = vi.fn(); + const second = vi.fn(); + + GLOBAL_OBJ.console.log = first; + GLOBAL_OBJ.console.log = second; + + GLOBAL_OBJ.console.log('latest'); + + expect(first).not.toHaveBeenCalled(); + expect(second).toHaveBeenCalledWith('latest'); + }); + + it('does not mutate originalConsoleMethods (kept safe for consoleSandbox)', () => { + addConsoleInstrumentationHandler(vi.fn()); + + const nativeLog = originalConsoleMethods.log; + GLOBAL_OBJ.console.log = vi.fn(); + + expect(originalConsoleMethods.log).toBe(nativeLog); + }); + }); + + describe('__sentry_original__ detection', () => { + it('accepts a function with __sentry_original__ without re-wrapping', () => { + const handler = vi.fn(); + addConsoleInstrumentationHandler(handler); + + const otherWrapper = vi.fn(); + markFunctionWrapped(otherWrapper as unknown as WrappedFunction, vi.fn() as unknown as WrappedFunction); + + GLOBAL_OBJ.console.log = otherWrapper; + + expect(GLOBAL_OBJ.console.log).toBe(otherWrapper); + }); + + it('does not fire our handler when a __sentry_original__ wrapper is installed', () => { + const handler = vi.fn(); + addConsoleInstrumentationHandler(handler); + + const otherWrapper = vi.fn(); + markFunctionWrapped(otherWrapper as unknown as WrappedFunction, vi.fn() as unknown as WrappedFunction); + + GLOBAL_OBJ.console.log = otherWrapper; + handler.mockClear(); + + GLOBAL_OBJ.console.log('via other wrapper'); + + expect(handler).not.toHaveBeenCalled(); + expect(otherWrapper).toHaveBeenCalledWith('via other wrapper'); + }); + + it('re-wraps a plain function without __sentry_original__', () => { + const handler = vi.fn(); + addConsoleInstrumentationHandler(handler); + + GLOBAL_OBJ.console.log = vi.fn(); + handler.mockClear(); + + GLOBAL_OBJ.console.log('plain'); + + expect(handler).toHaveBeenCalledWith(expect.objectContaining({ args: ['plain'], level: 'log' })); + }); + }); + + describe('consoleSandbox interaction', () => { + it('does not fire the handler inside consoleSandbox', () => { + const handler = vi.fn(); + addConsoleInstrumentationHandler(handler); + handler.mockClear(); + + consoleSandbox(() => { + GLOBAL_OBJ.console.log('sandbox message'); + }); + + expect(handler).not.toHaveBeenCalled(); + }); + + it('resumes firing the handler after consoleSandbox returns', () => { + const handler = vi.fn(); + addConsoleInstrumentationHandler(handler); + + consoleSandbox(() => { + GLOBAL_OBJ.console.log('inside sandbox'); + }); + handler.mockClear(); + + GLOBAL_OBJ.console.log('after sandbox'); + + expect(handler).toHaveBeenCalledWith(expect.objectContaining({ args: ['after sandbox'], level: 'log' })); + expect(handler).not.toHaveBeenCalledWith(expect.objectContaining({ args: ['inside sandbox'], level: 'log' })); + }); + + it('does not fire the handler inside consoleSandbox after a Lambda-style replacement', () => { + const handler = vi.fn(); + addConsoleInstrumentationHandler(handler); + + GLOBAL_OBJ.console.log = vi.fn(); + handler.mockClear(); + + consoleSandbox(() => { + GLOBAL_OBJ.console.log('sandbox after lambda'); + }); + + expect(handler).not.toHaveBeenCalled(); + }); + }); + + describe('third-party capture-and-call wrapping', () => { + it('does not cause infinite recursion when a third party wraps console with the capture pattern', () => { + const handler = vi.fn(); + addConsoleInstrumentationHandler(handler); + handler.mockClear(); + + const prevLog = GLOBAL_OBJ.console.log; + const thirdPartyExtra = vi.fn(); + GLOBAL_OBJ.console.log = (...args: any[]) => { + prevLog(...args); + thirdPartyExtra(...args); + }; + + expect(() => GLOBAL_OBJ.console.log('should not overflow')).not.toThrow(); + + expect(thirdPartyExtra).toHaveBeenCalledWith('should not overflow'); + expect(handler).toHaveBeenCalledTimes(1); + expect(handler).toHaveBeenCalledWith(expect.objectContaining({ args: ['should not overflow'], level: 'log' })); + }); + + it('fires the handler exactly once on re-entrant calls', () => { + const handler = vi.fn(); + addConsoleInstrumentationHandler(handler); + handler.mockClear(); + + const callOrder: string[] = []; + + const prevLog = GLOBAL_OBJ.console.log; + GLOBAL_OBJ.console.log = (...args: any[]) => { + callOrder.push('delegate-before-prev'); + prevLog(...args); + callOrder.push('delegate-after-prev'); + }; + + handler.mockImplementation(() => { + callOrder.push('handler'); + }); + + GLOBAL_OBJ.console.log('re-entrant test'); + + // The handler fires exactly once — on the first (outer) entry. + // The re-entrant call through prev() must NOT trigger it a second time. + expect(handler).toHaveBeenCalledTimes(1); + + // Verify the full call order: + // 1. wrapper enters → triggerHandlers → handler fires + // 2. wrapper calls consoleDelegate (third-party fn) + // 3. third-party fn calls prev() → re-enters wrapper → nativeMethod (no handler) + // 4. third-party fn continues after prev() + expect(callOrder).toEqual(['handler', 'delegate-before-prev', 'delegate-after-prev']); + }); + + it('consoleSandbox still bypasses the handler after third-party wrapping', () => { + const handler = vi.fn(); + addConsoleInstrumentationHandler(handler); + + const prevLog = GLOBAL_OBJ.console.log; + GLOBAL_OBJ.console.log = (...args: any[]) => { + prevLog(...args); + }; + handler.mockClear(); + + consoleSandbox(() => { + GLOBAL_OBJ.console.log('should bypass'); + }); + + expect(handler).not.toHaveBeenCalled(); + }); + + it('keeps firing the handler when console.log is set back to the original native method', () => { + const handler = vi.fn(); + addConsoleInstrumentationHandler(handler); + + // Simulate Lambda-style replacement + GLOBAL_OBJ.console.log = vi.fn(); + handler.mockClear(); + + // Simulate external code restoring a native method reference it captured + // before Sentry init — this should NOT clobber the wrapper. + GLOBAL_OBJ.console.log = nativeConsoleLog; + + GLOBAL_OBJ.console.log('after restore to original'); + + expect(handler).toHaveBeenCalledWith( + expect.objectContaining({ args: ['after restore to original'], level: 'log' }), + ); + }); + }); +}); diff --git a/packages/node/src/index.ts b/packages/node/src/index.ts index ce9458079980..3bd5e1edba1c 100644 --- a/packages/node/src/index.ts +++ b/packages/node/src/index.ts @@ -137,7 +137,6 @@ export { profiler, consoleLoggingIntegration, createConsolaReporter, - consoleIntegration, wrapMcpServerWithSentry, featureFlagsIntegration, spanStreamingIntegration, @@ -192,6 +191,7 @@ export { processSessionIntegration, nodeRuntimeMetricsIntegration, type NodeRuntimeMetricsOptions, + consoleIntegration, pinoIntegration, createSentryWinstonTransport, SentryContextManager, From 7b9ce980cfda501a18deb7f69b81a10c1c129428 Mon Sep 17 00:00:00 2001 From: Sigrid <32902192+s1gr1d@users.noreply.github.com> Date: Wed, 22 Apr 2026 10:54:21 +0200 Subject: [PATCH 25/41] docs(readme): Update usage instructions for binary scripts (#20426) Adds `--package` to binary scripts so the usage invokes the correct script. Closes https://github.com/getsentry/sentry-javascript/issues/20422 --- packages/profiling-node/README.md | 12 +++++++++--- packages/remix/README.md | 9 +++++++-- 2 files changed, 16 insertions(+), 5 deletions(-) diff --git a/packages/profiling-node/README.md b/packages/profiling-node/README.md index 962bc8e6834f..51e447640c14 100644 --- a/packages/profiling-node/README.md +++ b/packages/profiling-node/README.md @@ -239,13 +239,19 @@ the binaries will be copied. This is wasteful as you will likely only need one o runtime. To prune the other libraries, profiling-node ships with a small utility script that helps you prune unused binaries. The -script can be invoked via `sentry-prune-profiler-binaries`, use `--help` to see a list of available options or -`--dry-run` if you want it to log the binaries that would have been deleted. +script can be invoked via `sentry-prune-profiler-binaries`: + +```bash +npx --package=@sentry/profiling-node sentry-prune-profiler-binaries +``` + +Use `--help` to see a list of available options or `--dry-run` if you want it to log the binaries that would have been +deleted. Example of only preserving a binary to run node16 on linux x64 musl. ```bash -sentry-prune-profiler-binaries --target_dir_path=./dist --target_platform=linux --target_node=16 --target_stdlib=musl --target_arch=x64 +npx --package=@sentry/profiling-node sentry-prune-profiler-binaries --target_dir_path=./dist --target_platform=linux --target_node=16 --target_stdlib=musl --target_arch=x64 ``` Which will output something like diff --git a/packages/remix/README.md b/packages/remix/README.md index 9260def2b700..2589ed9f7e6b 100644 --- a/packages/remix/README.md +++ b/packages/remix/README.md @@ -122,8 +122,13 @@ Sentry.captureEvent({ The Remix SDK provides a script that automatically creates a release and uploads sourcemaps. To generate sourcemaps with Remix, you need to call `remix build` with the `--sourcemap` option. -On release, call `sentry-upload-sourcemaps` to upload source maps and create a release. To see more details on how to -use the command, call `sentry-upload-sourcemaps --help`. +On release, call `sentry-upload-sourcemaps` to upload source maps and create a release: + +```bash +npx --package=@sentry/remix sentry-upload-sourcemaps +``` + +To see more details on how to use the command, call `npx --package=@sentry/remix sentry-upload-sourcemaps --help`. For more advanced configuration, [directly use `sentry-cli` to upload source maps.](https://github.com/getsentry/sentry-cli). From a01d6a85476dd96da1a9c522cffc3688a97207f0 Mon Sep 17 00:00:00 2001 From: Nicolas Hrubec Date: Wed, 22 Apr 2026 10:59:21 +0200 Subject: [PATCH 26/41] test(node-core): Fix minute-boundary race in session-aggregate tests (#20437) The SDK buckets sessions by minute-rounded timestamp. When a test's sequential requests run across a minute boundary, the payload splits across two `aggregates` buckets and the existing single-bucket assertion fails. To resolve this, this PR replaces the single bucket check with a sum across all returned buckets to ensure the total count is correct even if events are split across buckets. The same pattern exists in our `node-core` and `node` integration tests. Closes #20283 Closes #20436 Co-authored-by: Claude Opus 4.7 (1M context) --- .../errored-session-aggregate/test.ts | 19 ++++++++++-------- .../sessions/exited-session-aggregate/test.ts | 18 ++++++++++------- .../crashed-session-aggregate/test.ts | 19 ++++++++++-------- .../errored-session-aggregate/test.ts | 20 ++++++++++--------- .../sessions/exited-session-aggregate/test.ts | 18 ++++++++++------- 5 files changed, 55 insertions(+), 39 deletions(-) diff --git a/dev-packages/node-core-integration-tests/suites/sessions/errored-session-aggregate/test.ts b/dev-packages/node-core-integration-tests/suites/sessions/errored-session-aggregate/test.ts index ba8110a62675..4ff4dea6d5cd 100644 --- a/dev-packages/node-core-integration-tests/suites/sessions/errored-session-aggregate/test.ts +++ b/dev-packages/node-core-integration-tests/suites/sessions/errored-session-aggregate/test.ts @@ -10,14 +10,17 @@ test('should aggregate successful, crashed and erroneous sessions', async () => .ignore('transaction', 'event') .unignore('sessions') .expect({ - sessions: { - aggregates: [ - { - started: expect.any(String), - exited: 2, - errored: 1, - }, - ], + sessions: agg => { + // Sessions are bucketed by minute; tolerate splits across a minute boundary by summing. + const totals = agg.aggregates.reduce( + (acc, b) => ({ + exited: acc.exited + (b.exited ?? 0), + errored: acc.errored + (b.errored ?? 0), + crashed: acc.crashed + (b.crashed ?? 0), + }), + { exited: 0, errored: 0, crashed: 0 }, + ); + expect(totals).toEqual({ exited: 2, errored: 1, crashed: 0 }); }, }) .start(); diff --git a/dev-packages/node-core-integration-tests/suites/sessions/exited-session-aggregate/test.ts b/dev-packages/node-core-integration-tests/suites/sessions/exited-session-aggregate/test.ts index 228ee9a98643..152861e87765 100644 --- a/dev-packages/node-core-integration-tests/suites/sessions/exited-session-aggregate/test.ts +++ b/dev-packages/node-core-integration-tests/suites/sessions/exited-session-aggregate/test.ts @@ -10,13 +10,17 @@ test('should aggregate successful sessions', async () => { .ignore('transaction', 'event') .unignore('sessions') .expect({ - sessions: { - aggregates: [ - { - started: expect.any(String), - exited: 3, - }, - ], + sessions: agg => { + // Sessions are bucketed by minute; tolerate splits across a minute boundary by summing. + const totals = agg.aggregates.reduce( + (acc, b) => ({ + exited: acc.exited + (b.exited ?? 0), + errored: acc.errored + (b.errored ?? 0), + crashed: acc.crashed + (b.crashed ?? 0), + }), + { exited: 0, errored: 0, crashed: 0 }, + ); + expect(totals).toEqual({ exited: 3, errored: 0, crashed: 0 }); }, }) .start(); diff --git a/dev-packages/node-integration-tests/suites/sessions/crashed-session-aggregate/test.ts b/dev-packages/node-integration-tests/suites/sessions/crashed-session-aggregate/test.ts index 712eeffcdeb3..ad8166e3163c 100644 --- a/dev-packages/node-integration-tests/suites/sessions/crashed-session-aggregate/test.ts +++ b/dev-packages/node-integration-tests/suites/sessions/crashed-session-aggregate/test.ts @@ -10,14 +10,17 @@ test('should aggregate successful and crashed sessions', async () => { .ignore('transaction', 'event') .unignore('sessions') .expect({ - sessions: { - aggregates: [ - { - started: expect.any(String), - exited: 2, - crashed: 1, - }, - ], + sessions: agg => { + // Sessions are bucketed by minute; tolerate splits across a minute boundary by summing. + const totals = agg.aggregates.reduce( + (acc, b) => ({ + exited: acc.exited + (b.exited ?? 0), + errored: acc.errored + (b.errored ?? 0), + crashed: acc.crashed + (b.crashed ?? 0), + }), + { exited: 0, errored: 0, crashed: 0 }, + ); + expect(totals).toEqual({ exited: 2, errored: 0, crashed: 1 }); }, }) .start(); diff --git a/dev-packages/node-integration-tests/suites/sessions/errored-session-aggregate/test.ts b/dev-packages/node-integration-tests/suites/sessions/errored-session-aggregate/test.ts index 4f35e6259697..d2c83f5d30fa 100644 --- a/dev-packages/node-integration-tests/suites/sessions/errored-session-aggregate/test.ts +++ b/dev-packages/node-integration-tests/suites/sessions/errored-session-aggregate/test.ts @@ -10,15 +10,17 @@ test('should aggregate successful, crashed and erroneous sessions', async () => .ignore('transaction', 'event') .unignore('sessions') .expect({ - sessions: { - aggregates: [ - { - started: expect.any(String), - exited: 1, - crashed: 1, - errored: 1, - }, - ], + sessions: agg => { + // Sessions are bucketed by minute; tolerate splits across a minute boundary by summing. + const totals = agg.aggregates.reduce( + (acc, b) => ({ + exited: acc.exited + (b.exited ?? 0), + errored: acc.errored + (b.errored ?? 0), + crashed: acc.crashed + (b.crashed ?? 0), + }), + { exited: 0, errored: 0, crashed: 0 }, + ); + expect(totals).toEqual({ exited: 1, errored: 1, crashed: 1 }); }, }) .start(); diff --git a/dev-packages/node-integration-tests/suites/sessions/exited-session-aggregate/test.ts b/dev-packages/node-integration-tests/suites/sessions/exited-session-aggregate/test.ts index 228ee9a98643..152861e87765 100644 --- a/dev-packages/node-integration-tests/suites/sessions/exited-session-aggregate/test.ts +++ b/dev-packages/node-integration-tests/suites/sessions/exited-session-aggregate/test.ts @@ -10,13 +10,17 @@ test('should aggregate successful sessions', async () => { .ignore('transaction', 'event') .unignore('sessions') .expect({ - sessions: { - aggregates: [ - { - started: expect.any(String), - exited: 3, - }, - ], + sessions: agg => { + // Sessions are bucketed by minute; tolerate splits across a minute boundary by summing. + const totals = agg.aggregates.reduce( + (acc, b) => ({ + exited: acc.exited + (b.exited ?? 0), + errored: acc.errored + (b.errored ?? 0), + crashed: acc.crashed + (b.crashed ?? 0), + }), + { exited: 0, errored: 0, crashed: 0 }, + ); + expect(totals).toEqual({ exited: 3, errored: 0, crashed: 0 }); }, }) .start(); From b0384179047d19afd8df54ff6e5e369797f0e7fc Mon Sep 17 00:00:00 2001 From: Sigrid <32902192+s1gr1d@users.noreply.github.com> Date: Wed, 22 Apr 2026 11:05:13 +0200 Subject: [PATCH 27/41] fix(hono): Remove undefined from options type (#20419) Removes `undefined` as users should always provide the options to at least provide the DSN. Reference https://github.com/getsentry/sentry-javascript/issues/15260 --- .../e2e-tests/test-applications/hono-4/src/entry.bun.ts | 3 +-- dev-packages/e2e-tests/test-applications/hono-4/src/routes.ts | 2 +- packages/hono/src/bun/middleware.ts | 2 +- packages/hono/src/node/middleware.ts | 2 +- 4 files changed, 4 insertions(+), 5 deletions(-) diff --git a/dev-packages/e2e-tests/test-applications/hono-4/src/entry.bun.ts b/dev-packages/e2e-tests/test-applications/hono-4/src/entry.bun.ts index 2a27d1adb8a9..e057eb78d4c5 100644 --- a/dev-packages/e2e-tests/test-applications/hono-4/src/entry.bun.ts +++ b/dev-packages/e2e-tests/test-applications/hono-4/src/entry.bun.ts @@ -2,10 +2,9 @@ import { Hono } from 'hono'; import { sentry } from '@sentry/hono/bun'; import { addRoutes } from './routes'; -const app = new Hono<{ Bindings: { E2E_TEST_DSN: string } }>(); +const app = new Hono(); app.use( - // @ts-expect-error - Env is not yet in type sentry(app, { dsn: process.env.E2E_TEST_DSN, environment: 'qa', diff --git a/dev-packages/e2e-tests/test-applications/hono-4/src/routes.ts b/dev-packages/e2e-tests/test-applications/hono-4/src/routes.ts index fbb273c7c425..a3272bc45ca3 100644 --- a/dev-packages/e2e-tests/test-applications/hono-4/src/routes.ts +++ b/dev-packages/e2e-tests/test-applications/hono-4/src/routes.ts @@ -1,7 +1,7 @@ import type { Hono } from 'hono'; import { HTTPException } from 'hono/http-exception'; -export function addRoutes(app: Hono<{ Bindings: { E2E_TEST_DSN: string } }>): void { +export function addRoutes(app: Hono<{ Bindings?: { E2E_TEST_DSN: string } }>): void { app.get('/', c => { return c.text('Hello Hono!'); }); diff --git a/packages/hono/src/bun/middleware.ts b/packages/hono/src/bun/middleware.ts index cbca87ea6b9e..fbcbffb15019 100644 --- a/packages/hono/src/bun/middleware.ts +++ b/packages/hono/src/bun/middleware.ts @@ -9,7 +9,7 @@ export interface HonoBunOptions extends Options {} /** * Sentry middleware for Hono running in a Bun runtime environment. */ -export const sentry = (app: Hono, options: HonoBunOptions | undefined = {}): MiddlewareHandler => { +export const sentry = (app: Hono, options: HonoBunOptions): MiddlewareHandler => { const isDebug = options.debug; isDebug && debug.log('Initialized Sentry Hono middleware (Bun)'); diff --git a/packages/hono/src/node/middleware.ts b/packages/hono/src/node/middleware.ts index 1dbca92d02e5..2a85575db0d8 100644 --- a/packages/hono/src/node/middleware.ts +++ b/packages/hono/src/node/middleware.ts @@ -9,7 +9,7 @@ export interface HonoNodeOptions extends Options {} /** * Sentry middleware for Hono running in a Node runtime environment. */ -export const sentry = (app: Hono, options: HonoNodeOptions | undefined = {}): MiddlewareHandler => { +export const sentry = (app: Hono, options: HonoNodeOptions): MiddlewareHandler => { const isDebug = options.debug; isDebug && debug.log('Initialized Sentry Hono middleware (Node)'); From b2033c0de7dd1d5dee82abcc9db470cf23f87e77 Mon Sep 17 00:00:00 2001 From: Sigrid <32902192+s1gr1d@users.noreply.github.com> Date: Wed, 22 Apr 2026 11:09:08 +0200 Subject: [PATCH 28/41] chore(lint): Remove lint warnings (#20413) --- packages/core/test/lib/utils/weakRef.test.ts | 2 +- packages/opentelemetry/test/utils/contextData.test.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/core/test/lib/utils/weakRef.test.ts b/packages/core/test/lib/utils/weakRef.test.ts index cf050ccf3d6e..36e4fcb6b8f3 100644 --- a/packages/core/test/lib/utils/weakRef.test.ts +++ b/packages/core/test/lib/utils/weakRef.test.ts @@ -1,4 +1,4 @@ -import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'; +import { describe, expect, it, vi } from 'vitest'; import { derefWeakRef, makeWeakRef, type MaybeWeakRef } from '../../../src/utils/weakRef'; describe('Unit | util | weakRef', () => { diff --git a/packages/opentelemetry/test/utils/contextData.test.ts b/packages/opentelemetry/test/utils/contextData.test.ts index 597b9fa2b637..0d04dc6556a5 100644 --- a/packages/opentelemetry/test/utils/contextData.test.ts +++ b/packages/opentelemetry/test/utils/contextData.test.ts @@ -1,6 +1,6 @@ import { ROOT_CONTEXT } from '@opentelemetry/api'; import { Scope } from '@sentry/core'; -import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'; +import { describe, expect, it } from 'vitest'; import { getContextFromScope, getScopesFromContext, From 6d2942c50226a791d762ea65b2f4ca2ca33222af Mon Sep 17 00:00:00 2001 From: Nicolas Hrubec Date: Wed, 22 Apr 2026 11:29:24 +0200 Subject: [PATCH 29/41] test(node): Use docker-compose healthchecks for service readiness (#20429) Replace the stdout `readyMatches` approach in the node-integration-tests runner with Docker native healthchecks + `docker compose up -d --wait`. The old approach matched log substrings like `'port 5432'`, which can fire too early when the server is not actually yet accepting connections resulting in flakes. Each `docker-compose.yml` now defines a proper healthcheck (e.g. pg_isready) and the runner blocks on `--wait` until every service reports healthy. Also renames the redis-cache container to avoid collision with redis. Closes https://github.com/getsentry/sentry-javascript/issues/20418 Closes https://github.com/getsentry/sentry-javascript/issues/20335 --------- Co-authored-by: Claude Opus 4.7 (1M context) --- .cursor/BUGBOT.md | 1 + .../suites/tracing/amqplib/docker-compose.yml | 6 ++ .../suites/tracing/amqplib/test.ts | 1 - .../suites/tracing/kafkajs/docker-compose.yml | 6 ++ .../suites/tracing/kafkajs/test.ts | 1 - .../tracing/knex/mysql2/docker-compose.yml | 6 ++ .../suites/tracing/knex/mysql2/test.ts | 2 +- .../suites/tracing/knex/pg/docker-compose.yml | 6 ++ .../suites/tracing/knex/pg/test.ts | 2 +- .../suites/tracing/mysql2/docker-compose.yml | 6 ++ .../suites/tracing/mysql2/test.ts | 2 +- .../tracing/postgres/docker-compose.yml | 6 ++ .../suites/tracing/postgres/test.ts | 3 - .../tracing/postgresjs/docker-compose.yml | 6 ++ .../suites/tracing/postgresjs/test.ts | 16 ++-- .../tracing/prisma-orm-v5/docker-compose.yml | 6 ++ .../suites/tracing/prisma-orm-v5/test.ts | 1 - .../tracing/prisma-orm-v6/docker-compose.yml | 6 ++ .../suites/tracing/prisma-orm-v6/test.ts | 1 - .../tracing/prisma-orm-v7/docker-compose.yml | 6 ++ .../suites/tracing/prisma-orm-v7/test.ts | 1 - .../tracing/redis-cache/docker-compose.yml | 8 +- .../suites/tracing/redis-cache/test.ts | 6 +- .../suites/tracing/redis/docker-compose.yml | 6 ++ .../suites/tracing/redis/test.ts | 2 +- .../suites/tracing/tedious/docker-compose.yml | 6 ++ .../suites/tracing/tedious/test.ts | 2 +- .../node-integration-tests/utils/runner.ts | 79 ++++++++----------- 28 files changed, 131 insertions(+), 69 deletions(-) diff --git a/.cursor/BUGBOT.md b/.cursor/BUGBOT.md index f4b4bd287271..c1a7811e5971 100644 --- a/.cursor/BUGBOT.md +++ b/.cursor/BUGBOT.md @@ -63,6 +63,7 @@ Do not flag the issues below if they appear in tests. - Race conditions when waiting on multiple requests. Ensure that waiting checks are unique enough and don't depend on a hard order when there's a chance that telemetry can be sent in arbitrary order. - Timeouts or sleeps in tests. Instead suggest concrete events or other signals to wait on. - Flag usage of `getFirstEnvelope*`, `getMultipleEnvelope*` or related test helpers. These are NOT reliable anymore. Instead suggest helpers like `waitForTransaction`, `waitForError`, `waitForSpans`, etc. +- Flag any new or modified `docker-compose.yml` under `dev-packages/node-integration-tests/suites/` or `dev-packages/node-core-integration-tests/suites/` where a service does not define a `healthcheck:`. The runner uses `docker compose up --wait` and relies on healthchecks to know when services are actually ready; without one the test will race the service's startup. ## Platform-safe code diff --git a/dev-packages/node-integration-tests/suites/tracing/amqplib/docker-compose.yml b/dev-packages/node-integration-tests/suites/tracing/amqplib/docker-compose.yml index c1127f097dbf..6d1468d3e04c 100644 --- a/dev-packages/node-integration-tests/suites/tracing/amqplib/docker-compose.yml +++ b/dev-packages/node-integration-tests/suites/tracing/amqplib/docker-compose.yml @@ -10,6 +10,12 @@ services: ports: - '5672:5672' - '15672:15672' + healthcheck: + test: ['CMD-SHELL', 'rabbitmq-diagnostics -q ping'] + interval: 2s + timeout: 10s + retries: 30 + start_period: 15s networks: default: diff --git a/dev-packages/node-integration-tests/suites/tracing/amqplib/test.ts b/dev-packages/node-integration-tests/suites/tracing/amqplib/test.ts index 0be272187f3f..62f2931daba5 100644 --- a/dev-packages/node-integration-tests/suites/tracing/amqplib/test.ts +++ b/dev-packages/node-integration-tests/suites/tracing/amqplib/test.ts @@ -34,7 +34,6 @@ describe('amqplib auto-instrumentation', () => { await createTestRunner() .withDockerCompose({ workingDirectory: [__dirname], - readyMatches: ['Time to start RabbitMQ'], }) .expect({ transaction: (transaction: TransactionEvent) => { diff --git a/dev-packages/node-integration-tests/suites/tracing/kafkajs/docker-compose.yml b/dev-packages/node-integration-tests/suites/tracing/kafkajs/docker-compose.yml index f744bfe6d50c..ab430a56ad00 100644 --- a/dev-packages/node-integration-tests/suites/tracing/kafkajs/docker-compose.yml +++ b/dev-packages/node-integration-tests/suites/tracing/kafkajs/docker-compose.yml @@ -5,3 +5,9 @@ services: container_name: integration-tests-kafka ports: - '9092:9092' + healthcheck: + test: ['CMD-SHELL', '/opt/kafka/bin/kafka-broker-api-versions.sh --bootstrap-server localhost:9092'] + interval: 2s + timeout: 5s + retries: 30 + start_period: 15s diff --git a/dev-packages/node-integration-tests/suites/tracing/kafkajs/test.ts b/dev-packages/node-integration-tests/suites/tracing/kafkajs/test.ts index 84e8d4a5612e..b7a996bddc53 100644 --- a/dev-packages/node-integration-tests/suites/tracing/kafkajs/test.ts +++ b/dev-packages/node-integration-tests/suites/tracing/kafkajs/test.ts @@ -16,7 +16,6 @@ describe('kafkajs', () => { await createRunner() .withDockerCompose({ workingDirectory: [__dirname], - readyMatches: ['9092'], }) .expect({ transaction: (transaction: TransactionEvent) => { diff --git a/dev-packages/node-integration-tests/suites/tracing/knex/mysql2/docker-compose.yml b/dev-packages/node-integration-tests/suites/tracing/knex/mysql2/docker-compose.yml index 788311e4e117..a57c8cb840f8 100644 --- a/dev-packages/node-integration-tests/suites/tracing/knex/mysql2/docker-compose.yml +++ b/dev-packages/node-integration-tests/suites/tracing/knex/mysql2/docker-compose.yml @@ -10,3 +10,9 @@ services: environment: MYSQL_ROOT_PASSWORD: docker MYSQL_DATABASE: tests + healthcheck: + test: ['CMD-SHELL', 'mysqladmin ping -h 127.0.0.1 -uroot -pdocker'] + interval: 2s + timeout: 3s + retries: 30 + start_period: 10s diff --git a/dev-packages/node-integration-tests/suites/tracing/knex/mysql2/test.ts b/dev-packages/node-integration-tests/suites/tracing/knex/mysql2/test.ts index e8116293de09..01db0bf6e88a 100644 --- a/dev-packages/node-integration-tests/suites/tracing/knex/mysql2/test.ts +++ b/dev-packages/node-integration-tests/suites/tracing/knex/mysql2/test.ts @@ -63,7 +63,7 @@ describe('knex auto instrumentation', () => { }; await createRunner() - .withDockerCompose({ workingDirectory: [__dirname], readyMatches: ['port: 3306'] }) + .withDockerCompose({ workingDirectory: [__dirname] }) .expect({ transaction: EXPECTED_TRANSACTION }) .start() .completed(); diff --git a/dev-packages/node-integration-tests/suites/tracing/knex/pg/docker-compose.yml b/dev-packages/node-integration-tests/suites/tracing/knex/pg/docker-compose.yml index e3edcd1d8d7b..28a916737317 100644 --- a/dev-packages/node-integration-tests/suites/tracing/knex/pg/docker-compose.yml +++ b/dev-packages/node-integration-tests/suites/tracing/knex/pg/docker-compose.yml @@ -11,3 +11,9 @@ services: POSTGRES_USER: test POSTGRES_PASSWORD: test POSTGRES_DB: tests + healthcheck: + test: ['CMD-SHELL', 'pg_isready -U test -d tests'] + interval: 2s + timeout: 3s + retries: 30 + start_period: 5s diff --git a/dev-packages/node-integration-tests/suites/tracing/knex/pg/test.ts b/dev-packages/node-integration-tests/suites/tracing/knex/pg/test.ts index 7c38381eb125..63af7b3628b5 100644 --- a/dev-packages/node-integration-tests/suites/tracing/knex/pg/test.ts +++ b/dev-packages/node-integration-tests/suites/tracing/knex/pg/test.ts @@ -61,7 +61,7 @@ describe('knex auto instrumentation', () => { }; await createRunner() - .withDockerCompose({ workingDirectory: [__dirname], readyMatches: ['port 5432'] }) + .withDockerCompose({ workingDirectory: [__dirname] }) .expect({ transaction: EXPECTED_TRANSACTION }) .start() .completed(); diff --git a/dev-packages/node-integration-tests/suites/tracing/mysql2/docker-compose.yml b/dev-packages/node-integration-tests/suites/tracing/mysql2/docker-compose.yml index 71ea54ad7e70..598c394ace5e 100644 --- a/dev-packages/node-integration-tests/suites/tracing/mysql2/docker-compose.yml +++ b/dev-packages/node-integration-tests/suites/tracing/mysql2/docker-compose.yml @@ -7,3 +7,9 @@ services: - '3306:3306' environment: MYSQL_ROOT_PASSWORD: password + healthcheck: + test: ['CMD-SHELL', 'mysqladmin ping -h 127.0.0.1 -uroot -ppassword'] + interval: 2s + timeout: 3s + retries: 30 + start_period: 10s diff --git a/dev-packages/node-integration-tests/suites/tracing/mysql2/test.ts b/dev-packages/node-integration-tests/suites/tracing/mysql2/test.ts index c1d680b9a52e..4c3078b922f7 100644 --- a/dev-packages/node-integration-tests/suites/tracing/mysql2/test.ts +++ b/dev-packages/node-integration-tests/suites/tracing/mysql2/test.ts @@ -34,7 +34,7 @@ describe('mysql2 auto instrumentation', () => { }; await createRunner(__dirname, 'scenario.js') - .withDockerCompose({ workingDirectory: [__dirname], readyMatches: ['port: 3306'] }) + .withDockerCompose({ workingDirectory: [__dirname] }) .expect({ transaction: EXPECTED_TRANSACTION }) .start() .completed(); diff --git a/dev-packages/node-integration-tests/suites/tracing/postgres/docker-compose.yml b/dev-packages/node-integration-tests/suites/tracing/postgres/docker-compose.yml index 51d9b86d028f..44425ae56188 100644 --- a/dev-packages/node-integration-tests/suites/tracing/postgres/docker-compose.yml +++ b/dev-packages/node-integration-tests/suites/tracing/postgres/docker-compose.yml @@ -11,3 +11,9 @@ services: POSTGRES_USER: test POSTGRES_PASSWORD: test POSTGRES_DB: tests + healthcheck: + test: ['CMD-SHELL', 'pg_isready -U test -d tests'] + interval: 2s + timeout: 3s + retries: 30 + start_period: 5s diff --git a/dev-packages/node-integration-tests/suites/tracing/postgres/test.ts b/dev-packages/node-integration-tests/suites/tracing/postgres/test.ts index 1fd03e92d0e2..98c42976498a 100644 --- a/dev-packages/node-integration-tests/suites/tracing/postgres/test.ts +++ b/dev-packages/node-integration-tests/suites/tracing/postgres/test.ts @@ -49,7 +49,6 @@ describe('postgres auto instrumentation', () => { await createRunner(__dirname, 'scenario.js') .withDockerCompose({ workingDirectory: [__dirname], - readyMatches: ['port 5432'], setupCommand: 'yarn', }) .expect({ transaction: EXPECTED_TRANSACTION }) @@ -61,7 +60,6 @@ describe('postgres auto instrumentation', () => { await createRunner(__dirname, 'scenario-ignoreConnect.js') .withDockerCompose({ workingDirectory: [__dirname], - readyMatches: ['port 5432'], setupCommand: 'yarn', }) .expect({ @@ -152,7 +150,6 @@ describe('postgres auto instrumentation', () => { await createRunner(__dirname, 'scenario-native.js') .withDockerCompose({ workingDirectory: [__dirname], - readyMatches: ['port 5432'], setupCommand: 'yarn', }) .expect({ transaction: EXPECTED_TRANSACTION }) diff --git a/dev-packages/node-integration-tests/suites/tracing/postgresjs/docker-compose.yml b/dev-packages/node-integration-tests/suites/tracing/postgresjs/docker-compose.yml index 301280106faa..f3afd85af9ab 100644 --- a/dev-packages/node-integration-tests/suites/tracing/postgresjs/docker-compose.yml +++ b/dev-packages/node-integration-tests/suites/tracing/postgresjs/docker-compose.yml @@ -11,3 +11,9 @@ services: POSTGRES_USER: test POSTGRES_PASSWORD: test POSTGRES_DB: test_db + healthcheck: + test: ['CMD-SHELL', 'pg_isready -U test -d test_db'] + interval: 2s + timeout: 3s + retries: 30 + start_period: 5s diff --git a/dev-packages/node-integration-tests/suites/tracing/postgresjs/test.ts b/dev-packages/node-integration-tests/suites/tracing/postgresjs/test.ts index 2dfbc020966b..f5f06208812e 100644 --- a/dev-packages/node-integration-tests/suites/tracing/postgresjs/test.ts +++ b/dev-packages/node-integration-tests/suites/tracing/postgresjs/test.ts @@ -218,7 +218,7 @@ describe('postgresjs auto instrumentation', () => { }; await createRunner(__dirname, 'scenario.js') - .withDockerCompose({ workingDirectory: [__dirname], readyMatches: ['port 5432'] }) + .withDockerCompose({ workingDirectory: [__dirname] }) .expect({ transaction: EXPECTED_TRANSACTION }) .expect({ event: EXPECTED_ERROR_EVENT }) .start() @@ -438,7 +438,7 @@ describe('postgresjs auto instrumentation', () => { await createRunner(__dirname, 'scenario.mjs') .withFlags('--import', `${__dirname}/instrument.mjs`) - .withDockerCompose({ workingDirectory: [__dirname], readyMatches: ['port 5432'] }) + .withDockerCompose({ workingDirectory: [__dirname] }) .expect({ transaction: EXPECTED_TRANSACTION }) .expect({ event: EXPECTED_ERROR_EVENT }) .start() @@ -532,7 +532,7 @@ describe('postgresjs auto instrumentation', () => { await createRunner(__dirname, 'scenario-requestHook.js') .withFlags('--require', `${__dirname}/instrument-requestHook.cjs`) - .withDockerCompose({ workingDirectory: [__dirname], readyMatches: ['port 5432'] }) + .withDockerCompose({ workingDirectory: [__dirname] }) .expect({ transaction: EXPECTED_TRANSACTION }) .start() .completed(); @@ -625,7 +625,7 @@ describe('postgresjs auto instrumentation', () => { await createRunner(__dirname, 'scenario-requestHook.mjs') .withFlags('--import', `${__dirname}/instrument-requestHook.mjs`) - .withDockerCompose({ workingDirectory: [__dirname], readyMatches: ['port 5432'] }) + .withDockerCompose({ workingDirectory: [__dirname] }) .expect({ transaction: EXPECTED_TRANSACTION }) .start() .completed(); @@ -706,7 +706,7 @@ describe('postgresjs auto instrumentation', () => { }; await createRunner(__dirname, 'scenario-url.cjs') - .withDockerCompose({ workingDirectory: [__dirname], readyMatches: ['port 5432'] }) + .withDockerCompose({ workingDirectory: [__dirname] }) .expect({ transaction: EXPECTED_TRANSACTION }) .start() .completed(); @@ -787,7 +787,7 @@ describe('postgresjs auto instrumentation', () => { await createRunner(__dirname, 'scenario-url.mjs') .withFlags('--import', `${__dirname}/instrument.mjs`) - .withDockerCompose({ workingDirectory: [__dirname], readyMatches: ['port 5432'] }) + .withDockerCompose({ workingDirectory: [__dirname] }) .expect({ transaction: EXPECTED_TRANSACTION }) .start() .completed(); @@ -866,7 +866,7 @@ describe('postgresjs auto instrumentation', () => { }; await createRunner(__dirname, 'scenario-unsafe.cjs') - .withDockerCompose({ workingDirectory: [__dirname], readyMatches: ['port 5432'] }) + .withDockerCompose({ workingDirectory: [__dirname] }) .expect({ transaction: EXPECTED_TRANSACTION }) .start() .completed(); @@ -946,7 +946,7 @@ describe('postgresjs auto instrumentation', () => { await createRunner(__dirname, 'scenario-unsafe.mjs') .withFlags('--import', `${__dirname}/instrument.mjs`) - .withDockerCompose({ workingDirectory: [__dirname], readyMatches: ['port 5432'] }) + .withDockerCompose({ workingDirectory: [__dirname] }) .expect({ transaction: EXPECTED_TRANSACTION }) .start() .completed(); diff --git a/dev-packages/node-integration-tests/suites/tracing/prisma-orm-v5/docker-compose.yml b/dev-packages/node-integration-tests/suites/tracing/prisma-orm-v5/docker-compose.yml index 37d45547b537..24bc212cac77 100644 --- a/dev-packages/node-integration-tests/suites/tracing/prisma-orm-v5/docker-compose.yml +++ b/dev-packages/node-integration-tests/suites/tracing/prisma-orm-v5/docker-compose.yml @@ -11,3 +11,9 @@ services: POSTGRES_USER: prisma POSTGRES_PASSWORD: prisma POSTGRES_DB: tests + healthcheck: + test: ['CMD-SHELL', 'pg_isready -U prisma -d tests'] + interval: 2s + timeout: 3s + retries: 30 + start_period: 5s diff --git a/dev-packages/node-integration-tests/suites/tracing/prisma-orm-v5/test.ts b/dev-packages/node-integration-tests/suites/tracing/prisma-orm-v5/test.ts index 74bdf4be4bd2..252ed938bf0d 100644 --- a/dev-packages/node-integration-tests/suites/tracing/prisma-orm-v5/test.ts +++ b/dev-packages/node-integration-tests/suites/tracing/prisma-orm-v5/test.ts @@ -15,7 +15,6 @@ describe('Prisma ORM v5 Tests', () => { await createRunner() .withDockerCompose({ workingDirectory: [cwd], - readyMatches: ['port 5432'], setupCommand: 'yarn prisma generate && yarn prisma migrate dev -n sentry-test', }) .expect({ diff --git a/dev-packages/node-integration-tests/suites/tracing/prisma-orm-v6/docker-compose.yml b/dev-packages/node-integration-tests/suites/tracing/prisma-orm-v6/docker-compose.yml index ddab7cb9c563..28f111fe3156 100644 --- a/dev-packages/node-integration-tests/suites/tracing/prisma-orm-v6/docker-compose.yml +++ b/dev-packages/node-integration-tests/suites/tracing/prisma-orm-v6/docker-compose.yml @@ -11,3 +11,9 @@ services: POSTGRES_USER: prisma POSTGRES_PASSWORD: prisma POSTGRES_DB: tests + healthcheck: + test: ['CMD-SHELL', 'pg_isready -U prisma -d tests'] + interval: 2s + timeout: 3s + retries: 30 + start_period: 5s diff --git a/dev-packages/node-integration-tests/suites/tracing/prisma-orm-v6/test.ts b/dev-packages/node-integration-tests/suites/tracing/prisma-orm-v6/test.ts index 07405a496fd0..b804adb10f71 100644 --- a/dev-packages/node-integration-tests/suites/tracing/prisma-orm-v6/test.ts +++ b/dev-packages/node-integration-tests/suites/tracing/prisma-orm-v6/test.ts @@ -16,7 +16,6 @@ describe('Prisma ORM v6 Tests', () => { await createRunner() .withDockerCompose({ workingDirectory: [cwd], - readyMatches: ['port 5432'], setupCommand: `yarn prisma generate --schema ${cwd}/prisma/schema.prisma && yarn prisma migrate dev -n sentry-test --schema ${cwd}/prisma/schema.prisma`, }) .expect({ diff --git a/dev-packages/node-integration-tests/suites/tracing/prisma-orm-v7/docker-compose.yml b/dev-packages/node-integration-tests/suites/tracing/prisma-orm-v7/docker-compose.yml index a56fd51cf7d9..117b5ef2c901 100644 --- a/dev-packages/node-integration-tests/suites/tracing/prisma-orm-v7/docker-compose.yml +++ b/dev-packages/node-integration-tests/suites/tracing/prisma-orm-v7/docker-compose.yml @@ -11,3 +11,9 @@ services: POSTGRES_USER: prisma POSTGRES_PASSWORD: prisma POSTGRES_DB: tests + healthcheck: + test: ['CMD-SHELL', 'pg_isready -U prisma -d tests'] + interval: 2s + timeout: 3s + retries: 30 + start_period: 5s diff --git a/dev-packages/node-integration-tests/suites/tracing/prisma-orm-v7/test.ts b/dev-packages/node-integration-tests/suites/tracing/prisma-orm-v7/test.ts index 5bb0158eee3c..f9fb22606772 100644 --- a/dev-packages/node-integration-tests/suites/tracing/prisma-orm-v7/test.ts +++ b/dev-packages/node-integration-tests/suites/tracing/prisma-orm-v7/test.ts @@ -17,7 +17,6 @@ conditionalTest({ min: 20 })('Prisma ORM v7 Tests', () => { await createRunner() .withDockerCompose({ workingDirectory: [cwd], - readyMatches: ['port 5432'], setupCommand: `yarn prisma generate --schema ${cwd}/prisma/schema.prisma && tsc -p ${cwd}/prisma/tsconfig.json && yarn prisma migrate dev -n sentry-test --schema ${cwd}/prisma/schema.prisma`, }) .expect({ diff --git a/dev-packages/node-integration-tests/suites/tracing/redis-cache/docker-compose.yml b/dev-packages/node-integration-tests/suites/tracing/redis-cache/docker-compose.yml index 164d5977e33d..ded08e8f62e5 100644 --- a/dev-packages/node-integration-tests/suites/tracing/redis-cache/docker-compose.yml +++ b/dev-packages/node-integration-tests/suites/tracing/redis-cache/docker-compose.yml @@ -4,6 +4,12 @@ services: db: image: redis:latest restart: always - container_name: integration-tests-redis + container_name: integration-tests-redis-cache ports: - '6379:6379' + healthcheck: + test: ['CMD-SHELL', 'redis-cli ping | grep -q PONG'] + interval: 2s + timeout: 3s + retries: 30 + start_period: 5s diff --git a/dev-packages/node-integration-tests/suites/tracing/redis-cache/test.ts b/dev-packages/node-integration-tests/suites/tracing/redis-cache/test.ts index e1aa0b9c1494..c27957c37b06 100644 --- a/dev-packages/node-integration-tests/suites/tracing/redis-cache/test.ts +++ b/dev-packages/node-integration-tests/suites/tracing/redis-cache/test.ts @@ -38,7 +38,7 @@ describe('redis cache auto instrumentation', () => { }; await createRunner(__dirname, 'scenario-ioredis.js') - .withDockerCompose({ workingDirectory: [__dirname], readyMatches: ['port=6379'] }) + .withDockerCompose({ workingDirectory: [__dirname] }) .expect({ transaction: EXPECTED_TRANSACTION }) .start() .completed(); @@ -137,7 +137,7 @@ describe('redis cache auto instrumentation', () => { }; await createRunner(__dirname, 'scenario-ioredis.js') - .withDockerCompose({ workingDirectory: [__dirname], readyMatches: ['port=6379'] }) + .withDockerCompose({ workingDirectory: [__dirname] }) .expect({ transaction: EXPECTED_TRANSACTION }) .start() .completed(); @@ -228,7 +228,7 @@ describe('redis cache auto instrumentation', () => { }; await createRunner(__dirname, 'scenario-redis-4.js') - .withDockerCompose({ workingDirectory: [__dirname], readyMatches: ['port=6379'] }) + .withDockerCompose({ workingDirectory: [__dirname] }) .expect({ transaction: EXPECTED_REDIS_CONNECT }) .expect({ transaction: EXPECTED_TRANSACTION }) .start() diff --git a/dev-packages/node-integration-tests/suites/tracing/redis/docker-compose.yml b/dev-packages/node-integration-tests/suites/tracing/redis/docker-compose.yml index 164d5977e33d..356302aa1f93 100644 --- a/dev-packages/node-integration-tests/suites/tracing/redis/docker-compose.yml +++ b/dev-packages/node-integration-tests/suites/tracing/redis/docker-compose.yml @@ -7,3 +7,9 @@ services: container_name: integration-tests-redis ports: - '6379:6379' + healthcheck: + test: ['CMD-SHELL', 'redis-cli ping | grep -q PONG'] + interval: 2s + timeout: 3s + retries: 30 + start_period: 5s diff --git a/dev-packages/node-integration-tests/suites/tracing/redis/test.ts b/dev-packages/node-integration-tests/suites/tracing/redis/test.ts index ec9cc0d93e84..7add18746f3a 100644 --- a/dev-packages/node-integration-tests/suites/tracing/redis/test.ts +++ b/dev-packages/node-integration-tests/suites/tracing/redis/test.ts @@ -43,7 +43,7 @@ describe('redis auto instrumentation', () => { }; await createRunner(__dirname, 'scenario-ioredis.js') - .withDockerCompose({ workingDirectory: [__dirname], readyMatches: ['port=6379'] }) + .withDockerCompose({ workingDirectory: [__dirname] }) .expect({ transaction: EXPECTED_TRANSACTION }) .start() .completed(); diff --git a/dev-packages/node-integration-tests/suites/tracing/tedious/docker-compose.yml b/dev-packages/node-integration-tests/suites/tracing/tedious/docker-compose.yml index 8e3604dca209..b77d05e2be20 100644 --- a/dev-packages/node-integration-tests/suites/tracing/tedious/docker-compose.yml +++ b/dev-packages/node-integration-tests/suites/tracing/tedious/docker-compose.yml @@ -10,3 +10,9 @@ services: environment: ACCEPT_EULA: 'Y' MSSQL_SA_PASSWORD: 'TESTing123' + healthcheck: + test: ['CMD-SHELL', '/opt/mssql-tools18/bin/sqlcmd -S localhost -U SA -P "TESTing123" -C -Q "SELECT 1"'] + interval: 2s + timeout: 3s + retries: 30 + start_period: 20s diff --git a/dev-packages/node-integration-tests/suites/tracing/tedious/test.ts b/dev-packages/node-integration-tests/suites/tracing/tedious/test.ts index 4b64611ac8f2..a8d45ad43877 100644 --- a/dev-packages/node-integration-tests/suites/tracing/tedious/test.ts +++ b/dev-packages/node-integration-tests/suites/tracing/tedious/test.ts @@ -42,7 +42,7 @@ describe.skip('tedious auto instrumentation', { timeout: 75_000 }, () => { }; await createRunner(__dirname, 'scenario.js') - .withDockerCompose({ workingDirectory: [__dirname], readyMatches: ['1433'] }) + .withDockerCompose({ workingDirectory: [__dirname] }) .expect({ transaction: EXPECTED_TRANSACTION }) .start() .completed(); diff --git a/dev-packages/node-integration-tests/utils/runner.ts b/dev-packages/node-integration-tests/utils/runner.ts index 7690fa40ee8b..89f96974c123 100644 --- a/dev-packages/node-integration-tests/utils/runner.ts +++ b/dev-packages/node-integration-tests/utils/runner.ts @@ -68,10 +68,6 @@ interface DockerOptions { * The working directory to run docker compose in */ workingDirectory: string[]; - /** - * The strings to look for in the output to know that the docker compose is ready for the test to be run - */ - readyMatches: string[]; /** * The command to run after docker compose is up */ @@ -79,56 +75,51 @@ interface DockerOptions { } /** - * Runs docker compose up and waits for the readyMatches to appear in the output + * Runs `docker compose up -d --wait`, which blocks until every service's + * healthcheck reports healthy. Each suite defines its healthcheck in its + * own docker-compose.yml. * * Returns a function that can be called to docker compose down */ async function runDockerCompose(options: DockerOptions): Promise { - return new Promise((resolve, reject) => { - const cwd = join(...options.workingDirectory); - const close = (): void => { - spawnSync('docker', ['compose', 'down', '--volumes'], { - cwd, - stdio: process.env.DEBUG ? 'inherit' : undefined, - }); - }; - - // ensure we're starting fresh - close(); - - const child = spawn('docker', ['compose', 'up'], { cwd }); + const cwd = join(...options.workingDirectory); + const close = (): void => { + spawnSync('docker', ['compose', 'down', '--volumes'], { + cwd, + stdio: process.env.DEBUG ? 'inherit' : undefined, + }); + }; - const timeout = setTimeout(() => { - close(); - reject(new Error('Timed out waiting for docker-compose')); - }, 75_000); + // ensure we're starting fresh + close(); - function newData(data: Buffer): void { - const text = data.toString('utf8'); + const result = spawnSync('docker', ['compose', 'up', '-d', '--wait'], { + cwd, + stdio: process.env.DEBUG ? 'inherit' : 'pipe', + }); - if (process.env.DEBUG) log(text); + if (result.status !== 0) { + const stderr = result.stderr?.toString() ?? ''; + const stdout = result.stdout?.toString() ?? ''; + // Surface container logs to make healthcheck failures easier to diagnose in CI + const logs = spawnSync('docker', ['compose', 'logs'], { cwd }).stdout?.toString() ?? ''; + close(); + throw new Error( + `docker compose up --wait failed (exit ${result.status})\n${stderr}${stdout}\n--- container logs ---\n${logs}`, + ); + } - for (const match of options.readyMatches) { - if (text.includes(match)) { - child.stdout.removeAllListeners(); - clearTimeout(timeout); - if (options.setupCommand) { - try { - // Prepend local node_modules/.bin to PATH so additionalDependencies binaries take precedence - const env = { ...process.env, PATH: `${cwd}/node_modules/.bin:${process.env.PATH}` }; - execSync(options.setupCommand, { cwd, stdio: 'inherit', env }); - } catch (e) { - log('Error running docker setup command', e); - } - } - resolve(close); - } - } + if (options.setupCommand) { + try { + // Prepend local node_modules/.bin to PATH so additionalDependencies binaries take precedence + const env = { ...process.env, PATH: `${cwd}/node_modules/.bin:${process.env.PATH}` }; + execSync(options.setupCommand, { cwd, stdio: 'inherit', env }); + } catch (e) { + log('Error running docker setup command', e); } + } - child.stdout.on('data', newData); - child.stderr.on('data', newData); - }); + return close; } type ExpectedEvent = Partial | ((event: Event) => void); From c2cf58f535efee8edaacf2a5bf61e5f765a39d0b Mon Sep 17 00:00:00 2001 From: Francesco Gringl-Novy Date: Wed, 22 Apr 2026 11:35:01 +0200 Subject: [PATCH 30/41] chore(test): Remove empty variant tests (#20443) I noticed we have two e2e tests with variants that have no build/assert command. This will essentially run the same test twice, once normally and once with a different label. So just removing the variant cuts down two e2e tests. --- .../test-applications/node-core-light-express/package.json | 7 ------- .../test-applications/node-core-light-otlp/package.json | 7 ------- 2 files changed, 14 deletions(-) diff --git a/dev-packages/e2e-tests/test-applications/node-core-light-express/package.json b/dev-packages/e2e-tests/test-applications/node-core-light-express/package.json index a78e5a1b24cb..83ca28556782 100644 --- a/dev-packages/e2e-tests/test-applications/node-core-light-express/package.json +++ b/dev-packages/e2e-tests/test-applications/node-core-light-express/package.json @@ -25,12 +25,5 @@ }, "volta": { "node": "22.18.0" - }, - "sentryTest": { - "variants": [ - { - "label": "node 22 (light mode, requires Node 22+ for diagnostics_channel)" - } - ] } } diff --git a/dev-packages/e2e-tests/test-applications/node-core-light-otlp/package.json b/dev-packages/e2e-tests/test-applications/node-core-light-otlp/package.json index f819e1685eb2..9a1f27147639 100644 --- a/dev-packages/e2e-tests/test-applications/node-core-light-otlp/package.json +++ b/dev-packages/e2e-tests/test-applications/node-core-light-otlp/package.json @@ -29,12 +29,5 @@ }, "volta": { "node": "22.18.0" - }, - "sentryTest": { - "variants": [ - { - "label": "node 22 (light mode + OTLP integration)" - } - ] } } From 27986663c932a07db2d5bbe1d994321a0561fc20 Mon Sep 17 00:00:00 2001 From: Copilot <198982749+Copilot@users.noreply.github.com> Date: Wed, 22 Apr 2026 11:52:36 +0200 Subject: [PATCH 31/41] test(browser): Fix flaky sessions route-lifecycle test + upgrade axios (#20197) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The `sessions/route-lifecycle` Playwright test flakes because `getMultipleSentryEnvelopeRequests` starts a 4000ms timeout **before** `page.goto()`. By the time page load + selector wait + 3 sequential clicks complete, the timeout can expire with only 3 of 4 expected `init` sessions captured. This PR replaces the timeout-based collection approach with sequential `waitForSession` calls — the same deterministic pattern already used in `start-session`, `initial-scope`, and `user` session tests: --------- Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com> Co-authored-by: Lms24 <8420481+Lms24@users.noreply.github.com> --- .../suites/sessions/route-lifecycle/test.ts | 27 ++++++++++--------- 1 file changed, 15 insertions(+), 12 deletions(-) diff --git a/dev-packages/browser-integration-tests/suites/sessions/route-lifecycle/test.ts b/dev-packages/browser-integration-tests/suites/sessions/route-lifecycle/test.ts index 1ccee9cd2728..05f10146f3f5 100644 --- a/dev-packages/browser-integration-tests/suites/sessions/route-lifecycle/test.ts +++ b/dev-packages/browser-integration-tests/suites/sessions/route-lifecycle/test.ts @@ -1,27 +1,30 @@ import { expect } from '@playwright/test'; -import type { SerializedSession } from '@sentry/core/src'; import { sentryTest } from '../../../utils/fixtures'; -import { getMultipleSentryEnvelopeRequests } from '../../../utils/helpers'; +import { waitForSession } from '../../../utils/helpers'; sentryTest( 'should start new sessions on pushState navigation with route lifecycle (default).', async ({ getLocalTestUrl, page }) => { const url = await getLocalTestUrl({ testDir: __dirname }); - const sessionsPromise = getMultipleSentryEnvelopeRequests(page, 10, { - url, - envelopeType: 'session', - timeout: 4000, - }); + const initSessionPromise = waitForSession(page, s => !!s.init && s.status === 'ok'); + await page.goto(url); + const initSession = await initSessionPromise; - await page.waitForSelector('#navigate'); - - await page.locator('#navigate').click(); + const session1Promise = waitForSession(page, s => !!s.init && s.status === 'ok' && s.sid !== initSession.sid); await page.locator('#navigate').click(); + const session1 = await session1Promise; + + const session2Promise = waitForSession(page, s => !!s.init && s.status === 'ok' && s.sid !== session1.sid); await page.locator('#navigate').click(); + const session2 = await session2Promise; - const startedSessions = (await sessionsPromise).filter(session => session.init); + const session3Promise = waitForSession(page, s => !!s.init && s.status === 'ok' && s.sid !== session2.sid); + await page.locator('#navigate').click(); + const session3 = await session3Promise; - expect(startedSessions.length).toBe(4); + // Verify we got 4 distinct init sessions (1 initial + 3 navigations) + const sids = new Set([initSession.sid, session1.sid, session2.sid, session3.sid]); + expect(sids.size).toBe(4); }, ); From 14719e3ce3b84ad9c3c8e0574fa29d68ab9a0235 Mon Sep 17 00:00:00 2001 From: Francesco Gringl-Novy Date: Wed, 22 Apr 2026 12:08:15 +0200 Subject: [PATCH 32/41] test(aws-serverless): Ensure aws-serverless E2E tests run locally (#20441) I ran into a bunch of issues trying to run this locally. This adjusts things slightly so that it runs locally, taking care of setting up sam consistently. Relevant changes: * Renamed the e2e test application to be consistent with app name * Streamlined how/when we use a dedicated node version. Now, by default we use node 20 and a respective docker image. We also run variants on CI for node 18 and node 22. * Added a minimal samconfig.toml because I noticed locally it was loudly complaining about this missing * Streamlined the invocation of `sam` to be a bit more consistent. * Made sam stuff compatibler with macos arm setups, this failed locally for me. * Added a nicer error message if sam is not installed, as you need to manually install this locally to run this test. --- .../aws-serverless/package.json | 17 ++++--- .../aws-serverless/pull-sam-image.sh | 8 ++-- .../aws-serverless/samconfig.toml | 10 +++++ .../aws-serverless/src/stack.ts | 16 +++++-- .../aws-serverless/start-event-proxy.mjs | 2 +- .../aws-serverless/tests/lambda-fixtures.ts | 45 ++++++++++++++----- .../aws-serverless/tests/layer.test.ts | 10 ++--- .../aws-serverless/tests/npm.test.ts | 4 +- 8 files changed, 75 insertions(+), 37 deletions(-) create mode 100644 dev-packages/e2e-tests/test-applications/aws-serverless/samconfig.toml diff --git a/dev-packages/e2e-tests/test-applications/aws-serverless/package.json b/dev-packages/e2e-tests/test-applications/aws-serverless/package.json index b417d8022667..84076d8393c2 100644 --- a/dev-packages/e2e-tests/test-applications/aws-serverless/package.json +++ b/dev-packages/e2e-tests/test-applications/aws-serverless/package.json @@ -1,12 +1,13 @@ { - "name": "aws-lambda-sam", + "name": "aws-serverless", "version": "1.0.0", "private": true, "type": "commonjs", "scripts": { "test": "playwright test", "clean": "npx rimraf node_modules pnpm-lock.yaml", - "test:build": "pnpm install && npx rimraf node_modules/@sentry/aws-serverless/nodejs", + "test:pull-sam-image": "./pull-sam-image.sh", + "test:build": "pnpm test:pull-sam-image && pnpm install && npx rimraf node_modules/@sentry/aws-serverless/nodejs", "test:assert": "pnpm test" }, "//": "We just need the @sentry/aws-serverless layer zip file, not the NPM package", @@ -15,12 +16,10 @@ "@playwright/test": "~1.56.0", "@sentry-internal/test-utils": "link:../../../test-utils", "@sentry/aws-serverless": "link:../../../../packages/aws-serverless/build/aws/dist-serverless/", - "@types/tmp": "^0.2.6", "aws-cdk-lib": "^2.210.0", "constructs": "^10.4.2", "glob": "^11.0.3", - "rimraf": "^5.0.10", - "tmp": "^0.2.5" + "rimraf": "^5.0.10" }, "volta": { "extends": "../../package.json" @@ -34,12 +33,12 @@ "sentryTest": { "variants": [ { - "build-command": "NODE_VERSION=20 ./pull-sam-image.sh && pnpm test:build", - "assert-command": "NODE_VERSION=20 pnpm test:assert", - "label": "aws-serverless (Node 20)" + "build-command": "NODE_VERSION=22 pnpm test:build", + "assert-command": "NODE_VERSION=22 pnpm test:assert", + "label": "aws-serverless (Node 22)" }, { - "build-command": "NODE_VERSION=18 ./pull-sam-image.sh && pnpm test:build", + "build-command": "NODE_VERSION=18 pnpm test:build", "assert-command": "NODE_VERSION=18 pnpm test:assert", "label": "aws-serverless (Node 18)" } diff --git a/dev-packages/e2e-tests/test-applications/aws-serverless/pull-sam-image.sh b/dev-packages/e2e-tests/test-applications/aws-serverless/pull-sam-image.sh index d6790c2c2c49..c9659547a4ea 100755 --- a/dev-packages/e2e-tests/test-applications/aws-serverless/pull-sam-image.sh +++ b/dev-packages/e2e-tests/test-applications/aws-serverless/pull-sam-image.sh @@ -1,13 +1,11 @@ #!/bin/bash -# Script to pull the correct Lambda docker image based on the NODE_VERSION environment variable. +# Pull the Lambda Node docker image for SAM local. NODE_VERSION should be the major only (e.g. 20). +# Defaults to 20 to match the repo's Volta Node major (see root package.json "volta.node"). set -e -if [[ -z "$NODE_VERSION" ]]; then - echo "Error: NODE_VERSION not set" - exit 1 -fi +NODE_VERSION="${NODE_VERSION:-20}" echo "Pulling Lambda Node $NODE_VERSION docker image..." docker pull "public.ecr.aws/lambda/nodejs:${NODE_VERSION}" diff --git a/dev-packages/e2e-tests/test-applications/aws-serverless/samconfig.toml b/dev-packages/e2e-tests/test-applications/aws-serverless/samconfig.toml new file mode 100644 index 000000000000..6771bf7d7900 --- /dev/null +++ b/dev-packages/e2e-tests/test-applications/aws-serverless/samconfig.toml @@ -0,0 +1,10 @@ +# SAM CLI expects this file in the project root; without it, `sam local start-lambda` logs +# OSError / missing file errors when run from the e2e temp directory. +# These values are placeholders — this app only uses `sam local`, not deploy. +version = 0.1 + +[default.deploy.parameters] +stack_name = "sentry-e2e-aws-serverless-local" +region = "us-east-1" +confirm_changeset = false +capabilities = "CAPABILITY_IAM" diff --git a/dev-packages/e2e-tests/test-applications/aws-serverless/src/stack.ts b/dev-packages/e2e-tests/test-applications/aws-serverless/src/stack.ts index 63463c914e1d..792bd4308c51 100644 --- a/dev-packages/e2e-tests/test-applications/aws-serverless/src/stack.ts +++ b/dev-packages/e2e-tests/test-applications/aws-serverless/src/stack.ts @@ -4,7 +4,7 @@ import * as path from 'node:path'; import * as fs from 'node:fs'; import * as os from 'node:os'; import * as dns from 'node:dns/promises'; -import { platform } from 'node:process'; +import { arch, platform } from 'node:process'; import { globSync } from 'glob'; import { execFileSync } from 'node:child_process'; @@ -12,9 +12,13 @@ const LAMBDA_FUNCTIONS_WITH_LAYER_DIR = './src/lambda-functions-layer'; const LAMBDA_FUNCTIONS_WITH_NPM_DIR = './src/lambda-functions-npm'; const LAMBDA_FUNCTION_TIMEOUT = 10; const LAYER_DIR = './node_modules/@sentry/aws-serverless/'; -const DEFAULT_NODE_VERSION = '22'; export const SAM_PORT = 3001; +/** Match SAM / Docker to this machine so Apple Silicon does not mix arm64 images with an x86_64 template default. */ +function samLambdaArchitecture(): 'arm64' | 'x86_64' { + return arch === 'arm64' ? 'arm64' : 'x86_64'; +} + function resolvePackagesDir(): string { // When running via the e2e test runner, tests are copied to a temp directory // so we need the workspace root passed via env var @@ -49,6 +53,7 @@ export class LocalLambdaStack extends Stack { properties: { ContentUri: path.join(LAYER_DIR, layerZipFile), CompatibleRuntimes: ['nodejs18.x', 'nodejs20.x', 'nodejs22.x'], + CompatibleArchitectures: [samLambdaArchitecture()], }, }); @@ -122,12 +127,17 @@ export class LocalLambdaStack extends Stack { execFileSync('npm', ['install', '--install-links', '--prefix', lambdaPath], { stdio: 'inherit' }); } + if (!process.env.NODE_VERSION) { + throw new Error('[LocalLambdaStack] NODE_VERSION is not set'); + } + new CfnResource(this, functionName, { type: 'AWS::Serverless::Function', properties: { + Architectures: [samLambdaArchitecture()], CodeUri: path.join(functionsDir, lambdaDir), Handler: 'index.handler', - Runtime: `nodejs${process.env.NODE_VERSION ?? DEFAULT_NODE_VERSION}.x`, + Runtime: `nodejs${process.env.NODE_VERSION}.x`, Timeout: LAMBDA_FUNCTION_TIMEOUT, Layers: addLayer ? [{ Ref: this.sentryLayer.logicalId }] : undefined, Environment: { diff --git a/dev-packages/e2e-tests/test-applications/aws-serverless/start-event-proxy.mjs b/dev-packages/e2e-tests/test-applications/aws-serverless/start-event-proxy.mjs index 196ae2471c69..4eff620fa17f 100644 --- a/dev-packages/e2e-tests/test-applications/aws-serverless/start-event-proxy.mjs +++ b/dev-packages/e2e-tests/test-applications/aws-serverless/start-event-proxy.mjs @@ -2,5 +2,5 @@ import { startEventProxyServer } from '@sentry-internal/test-utils'; startEventProxyServer({ port: 3031, - proxyServerName: 'aws-serverless-lambda-sam', + proxyServerName: 'aws-serverless', }); diff --git a/dev-packages/e2e-tests/test-applications/aws-serverless/tests/lambda-fixtures.ts b/dev-packages/e2e-tests/test-applications/aws-serverless/tests/lambda-fixtures.ts index 23aab3a7d683..4df52b322d26 100644 --- a/dev-packages/e2e-tests/test-applications/aws-serverless/tests/lambda-fixtures.ts +++ b/dev-packages/e2e-tests/test-applications/aws-serverless/tests/lambda-fixtures.ts @@ -1,14 +1,19 @@ import { test as base, expect } from '@playwright/test'; import { App } from 'aws-cdk-lib'; -import * as tmp from 'tmp'; import { LocalLambdaStack, SAM_PORT, getHostIp } from '../src/stack'; import { writeFileSync } from 'node:fs'; -import { spawn, execSync } from 'node:child_process'; +import { execSync, spawn } from 'node:child_process'; import { LambdaClient } from '@aws-sdk/client-lambda'; const DOCKER_NETWORK_NAME = 'lambda-test-network'; const SAM_TEMPLATE_FILE = 'sam.template.yml'; +/** Major Node for SAM `--invoke-image`; default matches root `package.json` `volta.node` and `pull-sam-image.sh`. */ +const DEFAULT_NODE_VERSION_MAJOR = '20'; + +const SAM_INSTALL_ERROR = + 'You need to install sam, e.g. run `brew install aws-sam-cli`. Ensure `sam` is on your PATH when running tests.'; + export { expect }; export const test = base.extend<{ testEnvironment: LocalLambdaStack; lambdaClient: LambdaClient }>({ @@ -16,6 +21,11 @@ export const test = base.extend<{ testEnvironment: LocalLambdaStack; lambdaClien async ({}, use) => { console.log('[testEnvironment fixture] Setting up AWS Lambda test infrastructure'); + const nodeVersionMajor = process.env.NODE_VERSION?.trim() || DEFAULT_NODE_VERSION_MAJOR; + process.env.NODE_VERSION = nodeVersionMajor; + + assertSamOnPath(); + execSync('docker network prune -f'); createDockerNetwork(); @@ -26,11 +36,6 @@ export const test = base.extend<{ testEnvironment: LocalLambdaStack; lambdaClien const template = app.synth().getStackByName('LocalLambdaStack').template; writeFileSync(SAM_TEMPLATE_FILE, JSON.stringify(template, null, 2)); - const debugLog = tmp.fileSync({ prefix: 'sentry_aws_lambda_tests_sam_debug', postfix: '.log' }); - if (!process.env.CI) { - console.log(`[test_environment fixture] Writing SAM debug log to: ${debugLog.name}`); - } - const args = [ 'local', 'start-lambda', @@ -42,16 +47,15 @@ export const test = base.extend<{ testEnvironment: LocalLambdaStack; lambdaClien '--docker-network', DOCKER_NETWORK_NAME, '--skip-pull-image', + '--invoke-image', + `public.ecr.aws/lambda/nodejs:${nodeVersionMajor}`, ]; - if (process.env.NODE_VERSION) { - args.push('--invoke-image', `public.ecr.aws/lambda/nodejs:${process.env.NODE_VERSION}`); - } - console.log(`[testEnvironment fixture] Running SAM with args: ${args.join(' ')}`); const samProcess = spawn('sam', args, { - stdio: process.env.CI ? 'inherit' : ['ignore', debugLog.fd, debugLog.fd], + stdio: process.env.DEBUG ? 'inherit' : 'ignore', + env: envForSamChild(), }); try { @@ -91,6 +95,23 @@ export const test = base.extend<{ testEnvironment: LocalLambdaStack; lambdaClien }, }); +/** Avoid forcing linux/amd64 on Apple Silicon when `DOCKER_DEFAULT_PLATFORM` is set globally. */ +function envForSamChild(): NodeJS.ProcessEnv { + const env = { ...process.env }; + if (process.arch === 'arm64') { + delete env.DOCKER_DEFAULT_PLATFORM; + } + return env; +} + +function assertSamOnPath(): void { + try { + execSync('sam --version', { encoding: 'utf-8', stdio: 'pipe' }); + } catch { + throw new Error(SAM_INSTALL_ERROR); + } +} + function createDockerNetwork() { try { execSync(`docker network create --driver bridge ${DOCKER_NETWORK_NAME}`); diff --git a/dev-packages/e2e-tests/test-applications/aws-serverless/tests/layer.test.ts b/dev-packages/e2e-tests/test-applications/aws-serverless/tests/layer.test.ts index 966ddf032218..fb3d45c5c188 100644 --- a/dev-packages/e2e-tests/test-applications/aws-serverless/tests/layer.test.ts +++ b/dev-packages/e2e-tests/test-applications/aws-serverless/tests/layer.test.ts @@ -4,7 +4,7 @@ import { test, expect } from './lambda-fixtures'; test.describe('Lambda layer', () => { test('tracing in CJS works', async ({ lambdaClient }) => { - const transactionEventPromise = waitForTransaction('aws-serverless-lambda-sam', transactionEvent => { + const transactionEventPromise = waitForTransaction('aws-serverless', transactionEvent => { return transactionEvent?.transaction === 'LayerTracingCjs'; }); @@ -72,7 +72,7 @@ test.describe('Lambda layer', () => { }); test('tracing in ESM works', async ({ lambdaClient }) => { - const transactionEventPromise = waitForTransaction('aws-serverless-lambda-sam', transactionEvent => { + const transactionEventPromise = waitForTransaction('aws-serverless', transactionEvent => { return transactionEvent?.transaction === 'LayerTracingEsm'; }); @@ -140,7 +140,7 @@ test.describe('Lambda layer', () => { }); test('capturing errors works', async ({ lambdaClient }) => { - const errorEventPromise = waitForError('aws-serverless-lambda-sam', errorEvent => { + const errorEventPromise = waitForError('aws-serverless', errorEvent => { return errorEvent?.exception?.values?.[0]?.value === 'test'; }); @@ -168,7 +168,7 @@ test.describe('Lambda layer', () => { }); test('capturing errors works in ESM', async ({ lambdaClient }) => { - const errorEventPromise = waitForError('aws-serverless-lambda-sam', errorEvent => { + const errorEventPromise = waitForError('aws-serverless', errorEvent => { return errorEvent?.exception?.values?.[0]?.value === 'test esm'; }); @@ -196,7 +196,7 @@ test.describe('Lambda layer', () => { }); test('streaming handlers work', async ({ lambdaClient }) => { - const transactionEventPromise = waitForTransaction('aws-serverless-lambda-sam', transactionEvent => { + const transactionEventPromise = waitForTransaction('aws-serverless', transactionEvent => { return transactionEvent?.transaction === 'LayerStreaming'; }); diff --git a/dev-packages/e2e-tests/test-applications/aws-serverless/tests/npm.test.ts b/dev-packages/e2e-tests/test-applications/aws-serverless/tests/npm.test.ts index e5b6ee1b9f32..943d5a2ab0f3 100644 --- a/dev-packages/e2e-tests/test-applications/aws-serverless/tests/npm.test.ts +++ b/dev-packages/e2e-tests/test-applications/aws-serverless/tests/npm.test.ts @@ -4,7 +4,7 @@ import { test, expect } from './lambda-fixtures'; test.describe('NPM package', () => { test('tracing in CJS works', async ({ lambdaClient }) => { - const transactionEventPromise = waitForTransaction('aws-serverless-lambda-sam', transactionEvent => { + const transactionEventPromise = waitForTransaction('aws-serverless', transactionEvent => { return transactionEvent?.transaction === 'NpmTracingCjs'; }); @@ -72,7 +72,7 @@ test.describe('NPM package', () => { }); test('tracing in ESM works', async ({ lambdaClient }) => { - const transactionEventPromise = waitForTransaction('aws-serverless-lambda-sam', transactionEvent => { + const transactionEventPromise = waitForTransaction('aws-serverless', transactionEvent => { return transactionEvent?.transaction === 'NpmTracingEsm'; }); From 7fb817edc27cca632ef10f433c1b4f11a16afef7 Mon Sep 17 00:00:00 2001 From: Nicolas Hrubec Date: Wed, 22 Apr 2026 14:19:34 +0200 Subject: [PATCH 33/41] test(nuxt): Fix flaky database error test (#20447) The test asserted on values[0], which flakes when the error arrives as a chained exception. Match by mechanism type instead, so the assertion holds regardless of ordering. Closes #20446 Co-authored-by: Claude Opus 4.7 (1M context) --- .../test-applications/nuxt-3/tests/database.test.ts | 12 ++++++++---- .../test-applications/nuxt-4/tests/database.test.ts | 12 ++++++++---- .../test-applications/nuxt-5/tests/database.test.ts | 12 ++++++++---- 3 files changed, 24 insertions(+), 12 deletions(-) diff --git a/dev-packages/e2e-tests/test-applications/nuxt-3/tests/database.test.ts b/dev-packages/e2e-tests/test-applications/nuxt-3/tests/database.test.ts index ecb0e32133db..f7635e6e06c9 100644 --- a/dev-packages/e2e-tests/test-applications/nuxt-3/tests/database.test.ts +++ b/dev-packages/e2e-tests/test-applications/nuxt-3/tests/database.test.ts @@ -128,7 +128,9 @@ test.describe('database integration', () => { test('captures database error and marks span as failed', async ({ request }) => { const errorPromise = waitForError('nuxt-3', errorEvent => { - return !!errorEvent?.exception?.values?.[0]?.value?.includes('no such table'); + return !!errorEvent?.exception?.values?.some( + value => value.mechanism?.type === 'auto.db.nuxt' && value.value?.includes('no such table'), + ); }); const transactionPromise = waitForTransaction('nuxt-3', transactionEvent => { @@ -141,9 +143,11 @@ test.describe('database integration', () => { const [error, transaction] = await Promise.all([errorPromise, transactionPromise]); - expect(error).toBeDefined(); - expect(error.exception?.values?.[0]?.value).toContain('no such table'); - expect(error.exception?.values?.[0]?.mechanism).toEqual({ + const dbException = error.exception?.values?.find(value => value.mechanism?.type === 'auto.db.nuxt'); + + expect(dbException).toBeDefined(); + expect(dbException?.value).toContain('no such table'); + expect(dbException?.mechanism).toEqual({ handled: false, type: 'auto.db.nuxt', }); diff --git a/dev-packages/e2e-tests/test-applications/nuxt-4/tests/database.test.ts b/dev-packages/e2e-tests/test-applications/nuxt-4/tests/database.test.ts index 9b9fdd892563..a74df938bc32 100644 --- a/dev-packages/e2e-tests/test-applications/nuxt-4/tests/database.test.ts +++ b/dev-packages/e2e-tests/test-applications/nuxt-4/tests/database.test.ts @@ -128,7 +128,9 @@ test.describe('database integration', () => { test('captures database error and marks span as failed', async ({ request }) => { const errorPromise = waitForError('nuxt-4', errorEvent => { - return !!errorEvent?.exception?.values?.[0]?.value?.includes('no such table'); + return !!errorEvent?.exception?.values?.some( + value => value.mechanism?.type === 'auto.db.nuxt' && value.value?.includes('no such table'), + ); }); const transactionPromise = waitForTransaction('nuxt-4', transactionEvent => { @@ -141,9 +143,11 @@ test.describe('database integration', () => { const [error, transaction] = await Promise.all([errorPromise, transactionPromise]); - expect(error).toBeDefined(); - expect(error.exception?.values?.[0]?.value).toContain('no such table'); - expect(error.exception?.values?.[0]?.mechanism).toEqual({ + const dbException = error.exception?.values?.find(value => value.mechanism?.type === 'auto.db.nuxt'); + + expect(dbException).toBeDefined(); + expect(dbException?.value).toContain('no such table'); + expect(dbException?.mechanism).toEqual({ handled: false, type: 'auto.db.nuxt', }); diff --git a/dev-packages/e2e-tests/test-applications/nuxt-5/tests/database.test.ts b/dev-packages/e2e-tests/test-applications/nuxt-5/tests/database.test.ts index 331b41d90ccf..ec7102449617 100644 --- a/dev-packages/e2e-tests/test-applications/nuxt-5/tests/database.test.ts +++ b/dev-packages/e2e-tests/test-applications/nuxt-5/tests/database.test.ts @@ -128,7 +128,9 @@ test.describe('database integration', () => { test('captures database error and marks span as failed', async ({ request }) => { const errorPromise = waitForError('nuxt-5', errorEvent => { - return !!errorEvent?.exception?.values?.[0]?.value?.includes('no such table'); + return !!errorEvent?.exception?.values?.some( + value => value.mechanism?.type === 'auto.db.nuxt' && value.value?.includes('no such table'), + ); }); const transactionPromise = waitForTransaction('nuxt-5', transactionEvent => { @@ -141,9 +143,11 @@ test.describe('database integration', () => { const [error, transaction] = await Promise.all([errorPromise, transactionPromise]); - expect(error).toBeDefined(); - expect(error.exception?.values?.[0]?.value).toContain('no such table'); - expect(error.exception?.values?.[0]?.mechanism).toEqual({ + const dbException = error.exception?.values?.find(value => value.mechanism?.type === 'auto.db.nuxt'); + + expect(dbException).toBeDefined(); + expect(dbException?.value).toContain('no such table'); + expect(dbException?.mechanism).toEqual({ handled: false, type: 'auto.db.nuxt', }); From ff23846e26bd4b3ec1dee15541d67813b858c6bd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan=20Peer=20St=C3=B6cklmair?= Date: Wed, 22 Apr 2026 15:09:28 +0200 Subject: [PATCH 34/41] chore: Ignore claude worktrees (#20440) When creating worktrees within Claude Code they are created within `.claude/worktrees`. That PR ignores this folder --- .gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitignore b/.gitignore index 464a09a9980e..741055ba1671 100644 --- a/.gitignore +++ b/.gitignore @@ -67,6 +67,7 @@ packages/**/*.junit.xml # Local Claude Code settings that should not be committed .claude/settings.local.json +.claude/worktrees # Triage report **/triage_report.md From a4c968647e500183f13f18a6874b686389b1ed1c Mon Sep 17 00:00:00 2001 From: Sigrid <32902192+s1gr1d@users.noreply.github.com> Date: Wed, 22 Apr 2026 17:16:06 +0200 Subject: [PATCH 35/41] test(hono): Add E2E tests for middleware spans (#20451) Adds tests for middleware spans. However, sub-app middlwares are not yet supported. Created an issue for that: https://github.com/getsentry/sentry-javascript/issues/20449 Reference: https://github.com/getsentry/sentry-javascript/issues/15260 --- .../hono-4/src/middleware.ts | 17 +++ .../src/route-groups/test-middleware.ts | 10 ++ .../test-applications/hono-4/src/routes.ts | 15 ++ .../hono-4/tests/middleware.test.ts | 143 ++++++++++++++++++ packages/hono/src/shared/patchAppUse.ts | 11 +- packages/hono/test/shared/patchAppUse.test.ts | 19 +++ 6 files changed, 213 insertions(+), 2 deletions(-) create mode 100644 dev-packages/e2e-tests/test-applications/hono-4/src/middleware.ts create mode 100644 dev-packages/e2e-tests/test-applications/hono-4/src/route-groups/test-middleware.ts create mode 100644 dev-packages/e2e-tests/test-applications/hono-4/tests/middleware.test.ts diff --git a/dev-packages/e2e-tests/test-applications/hono-4/src/middleware.ts b/dev-packages/e2e-tests/test-applications/hono-4/src/middleware.ts new file mode 100644 index 000000000000..cc7bfae9896d --- /dev/null +++ b/dev-packages/e2e-tests/test-applications/hono-4/src/middleware.ts @@ -0,0 +1,17 @@ +import type { MiddlewareHandler } from 'hono'; + +export const middlewareA: MiddlewareHandler = async function middlewareA(c, next) { + // Add some delay + await new Promise(resolve => setTimeout(resolve, 50)); + await next(); +}; + +export const middlewareB: MiddlewareHandler = async function middlewareB(_c, next) { + // Add some delay + await new Promise(resolve => setTimeout(resolve, 60)); + await next(); +}; + +export const failingMiddleware: MiddlewareHandler = async function failingMiddleware(_c, _next) { + throw new Error('Middleware error'); +}; diff --git a/dev-packages/e2e-tests/test-applications/hono-4/src/route-groups/test-middleware.ts b/dev-packages/e2e-tests/test-applications/hono-4/src/route-groups/test-middleware.ts new file mode 100644 index 000000000000..656fea319579 --- /dev/null +++ b/dev-packages/e2e-tests/test-applications/hono-4/src/route-groups/test-middleware.ts @@ -0,0 +1,10 @@ +import { Hono } from 'hono'; + +const testMiddleware = new Hono(); + +testMiddleware.get('/named', c => c.json({ middleware: 'named' })); +testMiddleware.get('/anonymous', c => c.json({ middleware: 'anonymous' })); +testMiddleware.get('/multi', c => c.json({ middleware: 'multi' })); +testMiddleware.get('/error', c => c.text('should not reach')); + +export { testMiddleware }; diff --git a/dev-packages/e2e-tests/test-applications/hono-4/src/routes.ts b/dev-packages/e2e-tests/test-applications/hono-4/src/routes.ts index a3272bc45ca3..65d30787de64 100644 --- a/dev-packages/e2e-tests/test-applications/hono-4/src/routes.ts +++ b/dev-packages/e2e-tests/test-applications/hono-4/src/routes.ts @@ -1,5 +1,7 @@ import type { Hono } from 'hono'; import { HTTPException } from 'hono/http-exception'; +import { testMiddleware } from './route-groups/test-middleware'; +import { middlewareA, middlewareB, failingMiddleware } from './middleware'; export function addRoutes(app: Hono<{ Bindings?: { E2E_TEST_DSN: string } }>): void { app.get('/', c => { @@ -21,4 +23,17 @@ export function addRoutes(app: Hono<{ Bindings?: { E2E_TEST_DSN: string } }>): v const code = Number(c.req.param('code')) as any; throw new HTTPException(code, { message: `HTTPException ${code}` }); }); + + // === Middleware === + // Middleware is registered on the main app (the patched instance) via `app.use()` + // TODO: In the future, we may want to support middleware registration on sub-apps (route groups) + app.use('/test-middleware/named/*', middlewareA); + app.use('/test-middleware/anonymous/*', async (c, next) => { + c.header('X-Custom', 'anonymous'); + await next(); + }); + app.use('/test-middleware/multi/*', middlewareA, middlewareB); + app.use('/test-middleware/error/*', failingMiddleware); + + app.route('/test-middleware', testMiddleware); } diff --git a/dev-packages/e2e-tests/test-applications/hono-4/tests/middleware.test.ts b/dev-packages/e2e-tests/test-applications/hono-4/tests/middleware.test.ts new file mode 100644 index 000000000000..a03398798756 --- /dev/null +++ b/dev-packages/e2e-tests/test-applications/hono-4/tests/middleware.test.ts @@ -0,0 +1,143 @@ +import { expect, test } from '@playwright/test'; +import { waitForError, waitForTransaction } from '@sentry-internal/test-utils'; +import { type SpanJSON } from '@sentry/core'; + +const APP_NAME = 'hono-4'; + +test('creates a span for named middleware', async ({ baseURL }) => { + const transactionPromise = waitForTransaction(APP_NAME, event => { + return event.contexts?.trace?.op === 'http.server' && event.transaction === 'GET /test-middleware/named'; + }); + + const response = await fetch(`${baseURL}/test-middleware/named`); + expect(response.status).toBe(200); + + const transaction = await transactionPromise; + const spans = transaction.spans || []; + + const middlewareSpan = spans.find( + (span: { description?: string; op?: string }) => + span.op === 'middleware.hono' && span.description === 'middlewareA', + ); + + expect(middlewareSpan).toEqual( + expect.objectContaining({ + description: 'middlewareA', + op: 'middleware.hono', + origin: 'auto.middleware.hono', + status: 'ok', + }), + ); + + // The middleware has a 50ms delay, so the span duration should be at least 50ms (0.05s) + // @ts-expect-error timestamp is defined + const durationMs = (middlewareSpan?.timestamp - middlewareSpan?.start_timestamp) * 1000; + expect(durationMs).toBeGreaterThanOrEqual(49); +}); + +test('creates a span for anonymous middleware', async ({ baseURL }) => { + const transactionPromise = waitForTransaction(APP_NAME, event => { + return event.contexts?.trace?.op === 'http.server' && event.transaction === 'GET /test-middleware/anonymous'; + }); + + const response = await fetch(`${baseURL}/test-middleware/anonymous`); + expect(response.status).toBe(200); + + const transaction = await transactionPromise; + const spans = transaction.spans || []; + + expect(spans).toContainEqual( + expect.objectContaining({ + description: '', + op: 'middleware.hono', + origin: 'auto.middleware.hono', + status: 'ok', + }), + ); +}); + +test('multiple middleware are sibling spans under the same parent', async ({ baseURL }) => { + const transactionPromise = waitForTransaction(APP_NAME, event => { + return event.contexts?.trace?.op === 'http.server' && event.transaction === 'GET /test-middleware/multi'; + }); + + const response = await fetch(`${baseURL}/test-middleware/multi`); + expect(response.status).toBe(200); + + const transaction = await transactionPromise; + const spans = transaction.spans || []; + + // Sort spans because they are in a different order in Node/Bun (OTel-based) + const middlewareSpans = spans + .filter((span: SpanJSON) => span.op === 'middleware.hono' && span.origin === 'auto.middleware.hono') + .sort((a, b) => (a.start_timestamp ?? 0) - (b.start_timestamp ?? 0)); + + expect(middlewareSpans).toHaveLength(2); + expect(middlewareSpans[0]?.description).toBe('middlewareA'); + expect(middlewareSpans[1]?.description).toBe('middlewareB'); + + // Both middleware spans share the same parent (siblings, not nested) + expect(middlewareSpans[0]?.parent_span_id).toBe(middlewareSpans[1]?.parent_span_id); + + // middlewareA has a 50ms delay, middlewareB has a 60ms delay + // @ts-expect-error timestamp is defined + const middlewareADuration = (middlewareSpans[0]?.timestamp - middlewareSpans[0]?.start_timestamp) * 1000; + // @ts-expect-error timestamp is defined + const middlewareBDuration = (middlewareSpans[1]?.timestamp - middlewareSpans[1]?.start_timestamp) * 1000; + expect(middlewareADuration).toBeGreaterThanOrEqual(49); + expect(middlewareBDuration).toBeGreaterThanOrEqual(59); +}); + +test('captures error thrown in middleware', async ({ baseURL }) => { + const errorPromise = waitForError(APP_NAME, event => { + return event.exception?.values?.[0]?.value === 'Middleware error'; + }); + + const response = await fetch(`${baseURL}/test-middleware/error`); + expect(response.status).toBe(500); + + const errorEvent = await errorPromise; + expect(errorEvent.exception?.values?.[0]?.value).toBe('Middleware error'); + expect(errorEvent.exception?.values?.[0]?.mechanism).toEqual( + expect.objectContaining({ + handled: false, + type: 'auto.middleware.hono', + }), + ); +}); + +test('sets error status on middleware span when middleware throws', async ({ baseURL }) => { + const transactionPromise = waitForTransaction(APP_NAME, event => { + return event.contexts?.trace?.op === 'http.server' && event.transaction === 'GET /test-middleware/error/*'; + }); + + await fetch(`${baseURL}/test-middleware/error`); + + const transaction = await transactionPromise; + const spans = transaction.spans || []; + + const failingSpan = spans.find( + (span: { description?: string; op?: string }) => + span.op === 'middleware.hono' && span.description === 'failingMiddleware', + ); + + expect(failingSpan).toBeDefined(); + expect(failingSpan?.status).toBe('internal_error'); + expect(failingSpan?.origin).toBe('auto.middleware.hono'); +}); + +test('includes request data on error events from middleware', async ({ baseURL }) => { + const errorPromise = waitForError(APP_NAME, event => { + return event.exception?.values?.[0]?.value === 'Middleware error'; + }); + + await fetch(`${baseURL}/test-middleware/error`); + + const errorEvent = await errorPromise; + expect(errorEvent.request).toEqual( + expect.objectContaining({ + method: 'GET', + url: expect.stringContaining('/test-middleware/error'), + }), + ); +}); diff --git a/packages/hono/src/shared/patchAppUse.ts b/packages/hono/src/shared/patchAppUse.ts index 28c3c49e7193..f4bb9205c0f6 100644 --- a/packages/hono/src/shared/patchAppUse.ts +++ b/packages/hono/src/shared/patchAppUse.ts @@ -1,5 +1,7 @@ import { captureException, + getActiveSpan, + getRootSpan, SEMANTIC_ATTRIBUTE_SENTRY_OP, SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN, SPAN_STATUS_ERROR, @@ -32,15 +34,20 @@ export function patchAppUse(app: Hono): void { /** * Wraps a Hono middleware handler so that its execution is traced as a Sentry span. - * Uses startInactiveSpan so that all middleware spans are siblings under the request/transaction - * (onion order: A → B → handler → B → A does not nest B under A in the trace). + * Explicitly parents each span under the root (transaction) span so that all middleware + * spans are siblings — even when OTel instrumentation introduces nested active contexts + * (onion order: A → B → handler → B → A would otherwise nest B under A). */ function wrapMiddlewareWithSpan(handler: MiddlewareHandler): MiddlewareHandler { return async function sentryTracedMiddleware(context, next) { + const activeSpan = getActiveSpan(); + const rootSpan = activeSpan ? getRootSpan(activeSpan) : undefined; + const span = startInactiveSpan({ name: handler.name || '', op: 'middleware.hono', onlyIfParent: true, + parentSpan: rootSpan, attributes: { [SEMANTIC_ATTRIBUTE_SENTRY_OP]: 'middleware.hono', [SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: MIDDLEWARE_ORIGIN, diff --git a/packages/hono/test/shared/patchAppUse.test.ts b/packages/hono/test/shared/patchAppUse.test.ts index 8f4e3bc0cc6c..0482d3569c84 100644 --- a/packages/hono/test/shared/patchAppUse.test.ts +++ b/packages/hono/test/shared/patchAppUse.test.ts @@ -155,4 +155,23 @@ describe('patchAppUse (middleware spans)', () => { expect(fakeApp._capturedThis).toBe(fakeApp); }); + + // todo: support sub-app (Hono route groups) patching in the future + it('does not wrap middleware on sub-apps (instance-level patching limitation)', async () => { + const app = new Hono(); + patchAppUse(app); + + // Route Grouping: https://hono.dev/docs/api/routing#grouping + const subApp = new Hono(); + subApp.use(async function subMiddleware(_c: unknown, next: () => Promise) { + await next(); + }); + subApp.get('/', () => new Response('sub')); + + app.route('/sub', subApp); + + await app.fetch(new Request('http://localhost/sub')); + + expect(startInactiveSpanMock).not.toHaveBeenCalledWith(expect.objectContaining({ name: 'subMiddleware' })); + }); }); From 7569b10524d8867423a285f6f50676cb195ddf31 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan=20Peer=20St=C3=B6cklmair?= Date: Wed, 22 Apr 2026 19:31:34 +0200 Subject: [PATCH 36/41] fix(cloudflare): Use TransformStream to keep track of streams (#20452) closes #20409 closes [JS-2233](https://linear.app/getsentry/issue/JS-2233/sentrycloudflare-root-httpserver-span-lost-on-streaming-responses) ## Problem The issue is that we had a `waitUntil?.(streamMonitor);` that waited until the stream was done. `streamMonitor` could [potentially live forever](https://developers.cloudflare.com/workflows/reference/limits/#wall-time-limits-by-invocation-type), while `waitUntil` has a limit of [30 seconds](https://developers.cloudflare.com/workers/runtime-apis/context/#waituntil) until it is getting cancelled. With the current approach we allow streams only being open for 30 seconds - then the `waitUntil` would cancel the request. This can only be reproduced when deployed, that is the reason why we didn't notice in our test cases. ## Solution By removing the `waitUntil` there is no hard limit, but we still have to wait until the stream is over in order to end our spans and flush accordingly. This can be achieved with [TransformStream](https://developers.cloudflare.com/workers/runtime-apis/streams/transformstream/), where we simply use the stream body from the client and pipe it through our transformer. With `flush` and `cancel` we know exactly when the stream would end or be cancelled - which is the only thing we need. There is btw no reason to add integration of E2E tests, as `miniflare` doesn't have this limitation and it couldn't be reproduced, so the tests would always succeed. The unit tests are kinda simulating that by checking if `waitUntil` is getting called or not. I also figured that the client isn't getting disposed and would leak memory - this PR is also fixing that (see screenshots). ## Some evidence repro: https://github.com/JPeer264/sentry-repros/tree/issue-20409 cloudflare logs before: image cloudflare logs after: image Sentry trace: https://sentry-sdks.sentry.io/explore/traces/trace/29a307b1272e48dbb3a87c270c487e5a/ Screenshot 2026-04-22 at 15 03 32 --- packages/cloudflare/src/request.ts | 54 +++-- .../worker/instrumentFetch.test.ts | 8 +- packages/cloudflare/test/request.test.ts | 184 ++++++++++++++++-- 3 files changed, 203 insertions(+), 43 deletions(-) diff --git a/packages/cloudflare/src/request.ts b/packages/cloudflare/src/request.ts index 05e870ff5d81..4fbdd9cf7fb4 100644 --- a/packages/cloudflare/src/request.ts +++ b/packages/cloudflare/src/request.ts @@ -129,40 +129,36 @@ export function wrapRequestHandler( const classification = classifyResponseStreaming(res); if (classification.isStreaming && res.body) { - // Streaming response detected - monitor consumption to keep span alive try { - const [clientStream, monitorStream] = res.body.tee(); - - // Monitor stream consumption and end span when complete - const streamMonitor = (async () => { - const reader = monitorStream.getReader(); - - try { - let done = false; - while (!done) { - const result = await reader.read(); - done = result.done; - } - } catch { - // Stream error or cancellation - will end span in finally - } finally { - reader.releaseLock(); - span.end(); - waitUntil?.(flushAndDispose(client)); - } - })(); - - // Keep worker alive until stream monitoring completes (otherwise span won't end) - waitUntil?.(streamMonitor); - - // Return response with client stream - return new Response(clientStream, { + let ended = false; + + const endSpanOnce = (): void => { + if (ended) return; + + ended = true; + span.end(); + waitUntil?.(flushAndDispose(client)); + }; + + const transform = new TransformStream({ + flush() { + // Source stream completed normally. + endSpanOnce(); + }, + cancel() { + // Client disconnected (or downstream cancelled). The `cancel` + // is being called while the response is still considered + // active, so this is a safe place to end the span. + endSpanOnce(); + }, + }); + + return new Response(res.body.pipeThrough(transform), { status: res.status, statusText: res.statusText, headers: res.headers, }); - } catch (_e) { - // tee() failed (e.g stream already locked) - fall back to non-streaming handling + } catch { span.end(); waitUntil?.(flushAndDispose(client)); return res; diff --git a/packages/cloudflare/test/instrumentations/worker/instrumentFetch.test.ts b/packages/cloudflare/test/instrumentations/worker/instrumentFetch.test.ts index 5486addb405c..2c8a734890fc 100644 --- a/packages/cloudflare/test/instrumentations/worker/instrumentFetch.test.ts +++ b/packages/cloudflare/test/instrumentations/worker/instrumentFetch.test.ts @@ -163,9 +163,11 @@ describe('instrumentFetch', () => { const wrappedHandler = withSentry(vi.fn(), handler); const waits: Promise[] = []; const waitUntil = vi.fn(promise => waits.push(promise)); - await wrappedHandler.fetch?.(new Request('https://example.com'), MOCK_ENV_WITHOUT_DSN, { - waitUntil, - } as unknown as ExecutionContext); + await wrappedHandler + .fetch?.(new Request('https://example.com'), MOCK_ENV_WITHOUT_DSN, { + waitUntil, + } as unknown as ExecutionContext) + .then(response => response.text()); expect(flush).not.toBeCalled(); expect(waitUntil).toBeCalled(); vi.advanceTimersToNextTimer().runAllTimers(); diff --git a/packages/cloudflare/test/request.test.ts b/packages/cloudflare/test/request.test.ts index 5160d8976e9b..28733ccfe651 100644 --- a/packages/cloudflare/test/request.test.ts +++ b/packages/cloudflare/test/request.test.ts @@ -14,6 +14,8 @@ const MOCK_OPTIONS: CloudflareOptions = { dsn: 'https://public@dsn.ingest.sentry.io/1337', }; +const NODE_MAJOR_VERSION = parseInt(process.versions.node.split('.')[0]!); + function addDelayedWaitUntil(context: ExecutionContext) { context.waitUntil(new Promise(resolve => setTimeout(() => resolve()))); } @@ -44,7 +46,7 @@ describe('withSentry', () => { await wrapRequestHandler( { options: MOCK_OPTIONS, request: new Request('https://example.com'), context }, () => new Response('test'), - ); + ).then(response => response.text()); expect(waitUntilSpy).toHaveBeenCalledTimes(1); expect(waitUntilSpy).toHaveBeenLastCalledWith(expect.any(Promise)); @@ -111,11 +113,8 @@ describe('withSentry', () => { await wrapRequestHandler({ options: MOCK_OPTIONS, request: new Request('https://example.com'), context }, () => { addDelayedWaitUntil(context); - const response = new Response('test'); - // Add Content-Length to skip probing - response.headers.set('content-length', '4'); - return response; - }); + return new Response('test'); + }).then(response => response.text()); expect(waitUntil).toBeCalled(); vi.advanceTimersToNextTimer().runAllTimers(); await Promise.all(waits); @@ -336,7 +335,7 @@ describe('withSentry', () => { SentryCore.captureMessage('sentry-trace'); return new Response('test'); }, - ); + ).then(response => response.text()); // Wait for async span end and transaction capture await new Promise(resolve => setTimeout(resolve, 50)); @@ -389,10 +388,8 @@ describe('flushAndDispose', () => { const flushSpy = vi.spyOn(SentryCore.Client.prototype, 'flush').mockResolvedValue(true); await wrapRequestHandler({ options: MOCK_OPTIONS, request: new Request('https://example.com'), context }, () => { - const response = new Response('test'); - response.headers.set('content-length', '4'); - return response; - }); + return new Response('test'); + }).then(response => response.text()); // Wait for all waitUntil promises to resolve await Promise.all(waits); @@ -518,6 +515,171 @@ describe('flushAndDispose', () => { disposeSpy.mockRestore(); }); + // Regression tests for https://github.com/getsentry/sentry-javascript/issues/20409 + // + // Pre-fix: streaming responses were observed via `body.tee()` + a long-running + // `waitUntil(streamMonitor)`. Cloudflare caps `waitUntil` at ~30s after the + // handler returns, so any stream taking longer than 30s to fully emit had the + // monitor cancelled before `span.end()` / `flushAndDispose()` ran — silently + // dropping the root `http.server` span. + // + // Post-fix: the body is piped through a passthrough `TransformStream`; the + // `flush` (normal completion) and `cancel` (client disconnect) callbacks fire + // while the response stream is still active (no waitUntil cap), so they can + // safely end the span and register `flushAndDispose` via a fresh `waitUntil` + // window. The contract guaranteed below: `waitUntil` is NOT called with any + // long-running stream-observation promise — only with `flushAndDispose`, and + // only after the response stream has finished (either by completion or cancel). + describe('regression #20409: streaming responses do not park stream observation in waitUntil', () => { + test('waitUntil is not called until streaming response is fully delivered', async () => { + const waits: Promise[] = []; + const waitUntil = vi.fn((promise: Promise) => waits.push(promise)); + const context = { waitUntil } as unknown as ExecutionContext; + + const flushSpy = vi.spyOn(SentryCore.Client.prototype, 'flush').mockResolvedValue(true); + const disposeSpy = vi.spyOn(CloudflareClient.prototype, 'dispose'); + + // Stream emits chunk1, then waits indefinitely until we open the gate + // before emitting chunk2 + closing. Models a long-running upstream + // (e.g. SSE / LLM streaming) whose body takes longer than the + // handler-return time to fully drain. + let releaseLastChunk!: () => void; + const lastChunkGate = new Promise(resolve => { + releaseLastChunk = resolve; + }); + + const stream = new ReadableStream({ + async start(controller) { + controller.enqueue(new TextEncoder().encode('chunk1')); + await lastChunkGate; + controller.enqueue(new TextEncoder().encode('chunk2')); + controller.close(); + }, + }); + + const result = await wrapRequestHandler( + { options: MOCK_OPTIONS, request: new Request('https://example.com'), context }, + () => new Response(stream, { headers: { 'content-type': 'text/event-stream' } }), + ); + + // Handler has returned, but the source stream has NOT closed yet. + // The pre-fix code would have already enqueued a long-running + // `waitUntil(streamMonitor)` task at this point. The post-fix code + // must not call waitUntil at all here. + expect(waitUntil).not.toHaveBeenCalled(); + + // Drain the response — Cloudflare would do this when forwarding to the client. + const reader = result.body!.getReader(); + await reader.read(); // chunk1 + // Source still hasn't closed — still no waitUntil. + expect(waitUntil).not.toHaveBeenCalled(); + + releaseLastChunk(); + await reader.read(); // chunk2 + await reader.read(); // done + reader.releaseLock(); + + // Stream completed → TransformStream `flush` fired → span ended → + // `flushAndDispose(client)` queued via waitUntil exactly once. + await Promise.all(waits); + expect(waitUntil).toHaveBeenCalledTimes(1); + expect(waitUntil).toHaveBeenLastCalledWith(expect.any(Promise)); + expect(flushSpy).toHaveBeenCalled(); + expect(disposeSpy).toHaveBeenCalled(); + + flushSpy.mockRestore(); + disposeSpy.mockRestore(); + }); + + // Node 18's TransformStream does not invoke the transformer's `cancel` hook + // when the downstream consumer cancels (WHATWG spec addition landed in Node 20). + // Cloudflare Workers run modern V8 where this works, so we only skip the + // test under Node 18. + test.skipIf(NODE_MAJOR_VERSION < 20)( + 'waitUntil is called once and dispose runs when client cancels mid-stream', + async () => { + const waits: Promise[] = []; + const waitUntil = vi.fn((promise: Promise) => waits.push(promise)); + const context = { waitUntil } as unknown as ExecutionContext; + + const flushSpy = vi.spyOn(SentryCore.Client.prototype, 'flush').mockResolvedValue(true); + const disposeSpy = vi.spyOn(CloudflareClient.prototype, 'dispose'); + + // Stream emits one chunk and then never closes — models an upstream + // that keeps emitting indefinitely. We then cancel the response from + // the consumer side to model a client disconnect. + let sourceCancelled = false; + const stream = new ReadableStream({ + start(controller) { + controller.enqueue(new TextEncoder().encode('chunk1')); + // intentionally don't close + }, + cancel() { + sourceCancelled = true; + }, + }); + + const result = await wrapRequestHandler( + { options: MOCK_OPTIONS, request: new Request('https://example.com'), context }, + () => new Response(stream, { headers: { 'content-type': 'text/event-stream' } }), + ); + + // Handler returned, source still open — no waitUntil yet. + expect(waitUntil).not.toHaveBeenCalled(); + + const reader = result.body!.getReader(); + await reader.read(); // chunk1 + await reader.cancel('client disconnected'); // simulates client disconnect + reader.releaseLock(); + + // TransformStream `cancel` fired → span ended → flushAndDispose queued. + await Promise.all(waits); + expect(waitUntil).toHaveBeenCalledTimes(1); + expect(waitUntil).toHaveBeenLastCalledWith(expect.any(Promise)); + expect(flushSpy).toHaveBeenCalled(); + expect(disposeSpy).toHaveBeenCalled(); + // pipeThrough should also propagate the cancel upstream to the source. + expect(sourceCancelled).toBe(true); + + flushSpy.mockRestore(); + disposeSpy.mockRestore(); + }, + ); + + test('waitUntil is called exactly once even if the response is consumed multiple times', async () => { + // Sanity: no matter how the response is drained, the TransformStream's + // flush callback must only end the span (and queue flushAndDispose) once. + const waits: Promise[] = []; + const waitUntil = vi.fn((promise: Promise) => waits.push(promise)); + const context = { waitUntil } as unknown as ExecutionContext; + + const flushSpy = vi.spyOn(SentryCore.Client.prototype, 'flush').mockResolvedValue(true); + const disposeSpy = vi.spyOn(CloudflareClient.prototype, 'dispose'); + + const stream = new ReadableStream({ + start(controller) { + controller.enqueue(new TextEncoder().encode('a')); + controller.enqueue(new TextEncoder().encode('b')); + controller.close(); + }, + }); + + const result = await wrapRequestHandler( + { options: MOCK_OPTIONS, request: new Request('https://example.com'), context }, + () => new Response(stream, { headers: { 'content-type': 'text/event-stream' } }), + ); + + const text = await result.text(); + expect(text).toBe('ab'); + + await Promise.all(waits); + expect(waitUntil).toHaveBeenCalledTimes(1); + + flushSpy.mockRestore(); + disposeSpy.mockRestore(); + }); + }); + test('dispose is NOT called for protocol upgrade responses (status 101)', async () => { const context = createMockExecutionContext(); const waits: Promise[] = []; From 4b4ac76db2cfca8e92cda9ec87b73ef2e950ebb5 Mon Sep 17 00:00:00 2001 From: Abdelrahman Awad Date: Wed, 22 Apr 2026 16:02:38 -0400 Subject: [PATCH 37/41] fix(node): Guard against null `httpVersion` in outgoing request span attributes (#20430) It appears that `httpVersion` can be `undefined` in some cases, and even tho the node types don't suggest that we do seem to treat it as such in other parts of the code so I assume we have run into this before. This PR guards against this specific usage here as this is the only place where it wasn't guarded. Fixes #20415 --------- Co-authored-by: Claude Opus 4.7 (1M context) Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- .../http/SentryHttpInstrumentation.ts | 10 +++- .../SentryHttpInstrumentation.test.ts | 48 +++++++++++++++++++ 2 files changed, 56 insertions(+), 2 deletions(-) create mode 100644 packages/node-core/test/integrations/SentryHttpInstrumentation.test.ts diff --git a/packages/node-core/src/integrations/http/SentryHttpInstrumentation.ts b/packages/node-core/src/integrations/http/SentryHttpInstrumentation.ts index b25d32138aa9..60cf7bbae9aa 100644 --- a/packages/node-core/src/integrations/http/SentryHttpInstrumentation.ts +++ b/packages/node-core/src/integrations/http/SentryHttpInstrumentation.ts @@ -456,10 +456,16 @@ function _getOutgoingRequestSpanData(request: http.ClientRequest): [string, Span ]; } -function _getOutgoingRequestEndedSpanData(response: http.IncomingMessage): SpanAttributes { +/** + * Exported for testing purposes. + */ +export function _getOutgoingRequestEndedSpanData(response: http.IncomingMessage): SpanAttributes { const { statusCode, statusMessage, httpVersion, socket } = response; - const transport = httpVersion.toUpperCase() !== 'QUIC' ? 'ip_tcp' : 'ip_udp'; + // httpVersion can be undefined in some cases and we seem to have encountered this before: + // https://github.com/getsentry/sentry-javascript/blob/ec8c8c64cde6001123db0199a8ca017b8863eac8/packages/node-core/src/integrations/http/httpServerSpansIntegration.ts#L158 + // see: #20415 + const transport = httpVersion?.toUpperCase() !== 'QUIC' ? 'ip_tcp' : 'ip_udp'; const additionalAttributes: SpanAttributes = { [ATTR_HTTP_RESPONSE_STATUS_CODE]: statusCode, diff --git a/packages/node-core/test/integrations/SentryHttpInstrumentation.test.ts b/packages/node-core/test/integrations/SentryHttpInstrumentation.test.ts new file mode 100644 index 000000000000..182abaa3663f --- /dev/null +++ b/packages/node-core/test/integrations/SentryHttpInstrumentation.test.ts @@ -0,0 +1,48 @@ +import type * as http from 'node:http'; +import { describe, expect, it } from 'vitest'; +import { _getOutgoingRequestEndedSpanData } from '../../src/integrations/http/SentryHttpInstrumentation'; + +function createResponse(overrides: Partial): http.IncomingMessage { + return { + statusCode: 200, + statusMessage: 'OK', + httpVersion: '1.1', + headers: {}, + socket: undefined, + ...overrides, + } as unknown as http.IncomingMessage; +} + +describe('_getOutgoingRequestEndedSpanData', () => { + it('sets ip_tcp transport for HTTP/1.1', () => { + const attributes = _getOutgoingRequestEndedSpanData(createResponse({ httpVersion: '1.1' })); + + expect(attributes['network.transport']).toBe('ip_tcp'); + expect(attributes['net.transport']).toBe('ip_tcp'); + expect(attributes['network.protocol.version']).toBe('1.1'); + expect(attributes['http.flavor']).toBe('1.1'); + }); + + it('sets ip_udp transport for QUIC', () => { + const attributes = _getOutgoingRequestEndedSpanData(createResponse({ httpVersion: 'QUIC' })); + + expect(attributes['network.transport']).toBe('ip_udp'); + expect(attributes['net.transport']).toBe('ip_udp'); + }); + + it('does not throw when httpVersion is null', () => { + expect(() => + _getOutgoingRequestEndedSpanData(createResponse({ httpVersion: null as unknown as string })), + ).not.toThrow(); + + const attributes = _getOutgoingRequestEndedSpanData(createResponse({ httpVersion: null as unknown as string })); + expect(attributes['network.transport']).toBe('ip_tcp'); + expect(attributes['net.transport']).toBe('ip_tcp'); + }); + + it('does not throw when httpVersion is undefined', () => { + expect(() => + _getOutgoingRequestEndedSpanData(createResponse({ httpVersion: undefined as unknown as string })), + ).not.toThrow(); + }); +}); From f97076ddc6f0aaab06c9b78f32078d282d6a87ab Mon Sep 17 00:00:00 2001 From: Francesco Gringl-Novy Date: Thu, 23 Apr 2026 09:50:18 +0200 Subject: [PATCH 38/41] chore(dev-deps): Bump nx from 22.5.0 to 22.6.5 (#20458) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## Summary Bumps `nx` from 22.5.0 (Feb 9) to 22.6.5 (Apr 10). This may (??) help with some cache restoration issues we have on CI, it's worth a try at least. ## Changelog (22.5.0 → 22.6.5) ### Potentially relevant to us - **⚠️ Vitest breaking change (22.6.0):** `reportsDirectory` is now resolved against workspace root instead of project root. We don't set a custom `reportsDirectory` in our vitest configs, so this should be a no-op. - **Batch executor fix (22.6.2):** Batch executor no longer errors on prematurely completed tasks — could improve reliability of our parallel builds. - **Prevent batch executor hang (22.6.5):** Fixes premature worker exit causing hangs — relevant since we use Nx's batch execution for build tasks. - **Infer extended tsconfig files as task inputs (22.6.2):** Tsconfig files referenced via `extends` are now automatically tracked as inputs. More accurate cache invalidation for our `build:types` tasks. - **Dangling symlinks handled during cache restore (22.6.0):** Could fix obscure cache restore failures in our yarn workspace setup. - **False positive loop detection with Bun resolved (22.6.0):** Relevant for `@sentry/bun` builds. - **Recursive FSEvents on macOS (22.6.0):** Fixes file watching to use recursive FSEvents instead of non-recursive kqueue — improves `build:watch` reliability on macOS dev machines. - **Shell metacharacters properly quoted in CLI args (22.6.1):** Fixes potential issues when task names or args contain special characters. - **Prevent DB corruption from concurrent initialization (22.6.1):** Fixes race conditions in Nx's task database. - **`windowsHide: true` on all child process spawns (22.6.1):** Cleaner CI on Windows (if applicable). - **TUI crash prevention (22.6.0, 22.6.2):** Multiple fixes for TUI panics/crashes. - **Misc perf fixes (22.6.5):** Multiple TUI performance improvements. - **Security: bumps axios to 1.15.0 (CVE-2026-25639), minimatch (CVE-2026-26996), ejs to 5.0.1, postcss-loader (yaml CVE)** ### Not relevant to us (for completeness) - Angular v21.2, ESLint v10, Vite 8 support - Gradle/Maven improvements - AI agent mode in `nx init` - `nx polygraph` command (cross-repo sessions) - Module federation fixes - React Native esbuild migration - Various `create-nx-workspace` improvements ## Test plan - [ ] CI build passes - [ ] `yarn build` works locally 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-authored-by: Claude Opus 4.6 (1M context) --- package.json | 2 +- yarn.lock | 177 +++++++++++++++++++++++---------------------------- 2 files changed, 80 insertions(+), 99 deletions(-) diff --git a/package.json b/package.json index 511970b04c2f..f455f42e3bbf 100644 --- a/package.json +++ b/package.json @@ -130,7 +130,7 @@ "madge": "8.0.0", "nodemon": "^3.1.10", "npm-run-all2": "^6.2.0", - "nx": "22.5.0", + "nx": "22.6.5", "oxfmt": "^0.38.0", "oxlint": "^1.53.0", "oxlint-tsgolint": "^0.16.0", diff --git a/yarn.lock b/yarn.lock index d655cea5359d..d95a0b67c008 100644 --- a/yarn.lock +++ b/yarn.lock @@ -5823,55 +5823,55 @@ consola "^2.15.0" node-fetch "^2.6.1" -"@nx/nx-darwin-arm64@22.5.0": - version "22.5.0" - resolved "https://registry.yarnpkg.com/@nx/nx-darwin-arm64/-/nx-darwin-arm64-22.5.0.tgz#290f2ed933b08284492307a6d079b887513dcbff" - integrity sha512-MHnzv6tzucvLsh4oS9FTepj+ct/o8/DPXrQow+9Jid7GSgY59xrDX/8CleJOrwL5lqKEyGW7vv8TR+4wGtEWTA== - -"@nx/nx-darwin-x64@22.5.0": - version "22.5.0" - resolved "https://registry.yarnpkg.com/@nx/nx-darwin-x64/-/nx-darwin-x64-22.5.0.tgz#6f1d554054c9255112dea61db3b25aa0276346a9" - integrity sha512-/0w43hbR5Kia0XeCDZHDt/18FHhpwQs+Y+8TO8/ZsF1RgCI0knJDCyJieYk1yEZAq6E8dStAJnuzxK9uvETs4A== - -"@nx/nx-freebsd-x64@22.5.0": - version "22.5.0" - resolved "https://registry.yarnpkg.com/@nx/nx-freebsd-x64/-/nx-freebsd-x64-22.5.0.tgz#0bd678ac05790fd19bbdf4cd98e6e08ce7b431c6" - integrity sha512-d4Pd1VFpD272R7kJTWm/Pj49BIz44GZ+QIVSfxlx3GWxyaPd25X9GBanUngL6qpactS+aLTwcoBmnSbZ4PEcEQ== - -"@nx/nx-linux-arm-gnueabihf@22.5.0": - version "22.5.0" - resolved "https://registry.yarnpkg.com/@nx/nx-linux-arm-gnueabihf/-/nx-linux-arm-gnueabihf-22.5.0.tgz#f0d8f30c77ec67a2e9f488088889aa5f7beb024d" - integrity sha512-cCyG23PikIlqE7I6s9j0aHJSqIxnpdOjFOXyRd224XmFyAB8tOyKl7vDD/WugcpAceos28i+Rgz4na189zm48A== - -"@nx/nx-linux-arm64-gnu@22.5.0": - version "22.5.0" - resolved "https://registry.yarnpkg.com/@nx/nx-linux-arm64-gnu/-/nx-linux-arm64-gnu-22.5.0.tgz#0103bc0bb74ca42a4108ab84ad6f21e97929050d" - integrity sha512-vkQw8737fpta6oVEEqskzwq+d0GeZkGhtyl+U3pAcuUcYTdqbsZaofSQACFnGfngsqpYmlJCWJGU5Te00qcPQw== - -"@nx/nx-linux-arm64-musl@22.5.0": - version "22.5.0" - resolved "https://registry.yarnpkg.com/@nx/nx-linux-arm64-musl/-/nx-linux-arm64-musl-22.5.0.tgz#8866ba44bcf2bd2420ab6a6532316029482f0f98" - integrity sha512-BkEsFBsnKrDK11N914rr5YKyIJwYoSVItJ7VzsQZIqAX0C7PdJeQ7KzqOGwoezbabdLmzFOBNg6s/o1ujoEYxw== - -"@nx/nx-linux-x64-gnu@22.5.0": - version "22.5.0" - resolved "https://registry.yarnpkg.com/@nx/nx-linux-x64-gnu/-/nx-linux-x64-gnu-22.5.0.tgz#0a27449cdaeba2d11d6673a42b6494a171d04210" - integrity sha512-Dsqoz4hWmqehMMm8oJY6Q0ckEUeeHz4+T/C8nHyDaaj/REKCSmqYf/+QV6f2Z5Up/CsQ/hoAsWYEhCHZ0tcSFg== - -"@nx/nx-linux-x64-musl@22.5.0": - version "22.5.0" - resolved "https://registry.yarnpkg.com/@nx/nx-linux-x64-musl/-/nx-linux-x64-musl-22.5.0.tgz#6263f05c1f984502f40d6f301fed89645f9fa0cc" - integrity sha512-Lcj/61BpsT85Qhm3hNTwQFrqGtsjLC+y4Kk21dh22d1/E5pOdVAwPXBuWrSPNo4lX+ESNoKmwxWjfgW3uoB05g== - -"@nx/nx-win32-arm64-msvc@22.5.0": - version "22.5.0" - resolved "https://registry.yarnpkg.com/@nx/nx-win32-arm64-msvc/-/nx-win32-arm64-msvc-22.5.0.tgz#0b75a32d592eda78740fff6fc5f2b259518e7f82" - integrity sha512-0DlnBDLvqNtseCyBBoBst0gwux+N91RBc4E41JDDcLcWpfntcwCQM39D6lA5qdma/0L7U0PUM7MYV9Q6igJMkQ== - -"@nx/nx-win32-x64-msvc@22.5.0": - version "22.5.0" - resolved "https://registry.yarnpkg.com/@nx/nx-win32-x64-msvc/-/nx-win32-x64-msvc-22.5.0.tgz#90b5b8230c37e2c6e09f1ed77169cabb79c4123f" - integrity sha512-kMMsU4PxKQ76NvmPFKT0/RlzRTiuUfuNWVJUmsWF1onVcBkXgQNKkmLcSJk3wGwML5/tHChjtlI7Hpo705Uv/g== +"@nx/nx-darwin-arm64@22.6.5": + version "22.6.5" + resolved "https://registry.yarnpkg.com/@nx/nx-darwin-arm64/-/nx-darwin-arm64-22.6.5.tgz#d1c88bc4b94c24b4d3e217dd52a0888ef0c63def" + integrity sha512-qT77Omkg5xQuL2+pDbneX2tI+XW5ZeayMylu7UUgK8OhTrAkJLKjpuYRH4xT5XBipxbDtlxmO3aLS3Ib1pKzJQ== + +"@nx/nx-darwin-x64@22.6.5": + version "22.6.5" + resolved "https://registry.yarnpkg.com/@nx/nx-darwin-x64/-/nx-darwin-x64-22.6.5.tgz#55b5e7c9137dbfea2acfd7043a58bdaca4f4e9df" + integrity sha512-9jICxb7vfJ56y/7Yuh3b/n1QJqWxO9xnXKYEs6SO8xPoW/KomVckILGc1C6RQSs6/3ixVJC7k1Dh1wm5tKPFrg== + +"@nx/nx-freebsd-x64@22.6.5": + version "22.6.5" + resolved "https://registry.yarnpkg.com/@nx/nx-freebsd-x64/-/nx-freebsd-x64-22.6.5.tgz#499e1fb013cf6fab217257d89405b9e19bf624a9" + integrity sha512-6B1wEKpqz5dI3AGMqttAVnA6M3DB/besAtuGyQiymK9ROlta1iuWgCcIYwcCQyhLn2Rx7vqj447KKcgCa8HlVw== + +"@nx/nx-linux-arm-gnueabihf@22.6.5": + version "22.6.5" + resolved "https://registry.yarnpkg.com/@nx/nx-linux-arm-gnueabihf/-/nx-linux-arm-gnueabihf-22.6.5.tgz#e492bd050aee479976c290fb5b23f290c284d665" + integrity sha512-xV50B8mnDPboct7JkAHftajI02s+8FszA8WTzhore+YGR+lEKHTLpucwGEaQuMlSdLplH7pQix4B4uK5pcMhZw== + +"@nx/nx-linux-arm64-gnu@22.6.5": + version "22.6.5" + resolved "https://registry.yarnpkg.com/@nx/nx-linux-arm64-gnu/-/nx-linux-arm64-gnu-22.6.5.tgz#4bdab184c1405e87680bbc49bb1ee79a3d8b0200" + integrity sha512-2JkWuMGj+HpW6oPAvU5VdAx1afTnEbiM10Y3YOrl3fipWV4BiP5VDx762QTrfCraP4hl6yqTgvTe7F9xaby+jQ== + +"@nx/nx-linux-arm64-musl@22.6.5": + version "22.6.5" + resolved "https://registry.yarnpkg.com/@nx/nx-linux-arm64-musl/-/nx-linux-arm64-musl-22.6.5.tgz#c1eb8ea0bb92cfbe50f3ccebdf931ce79d2052f6" + integrity sha512-Z/zMqFClnEyqDXouJKEPoWVhMQIif5F0YuECWBYjd3ZLwQsXGTItoh+6Wm3XF/nGMA2uLOHyTq/X7iFXQY3RzA== + +"@nx/nx-linux-x64-gnu@22.6.5": + version "22.6.5" + resolved "https://registry.yarnpkg.com/@nx/nx-linux-x64-gnu/-/nx-linux-x64-gnu-22.6.5.tgz#4f905688d30649c51035d71dca26a55666819cc2" + integrity sha512-FlotSyqNnaXSn0K+yWw+hRdYBwusABrPgKLyixfJIYRzsy+xPKN6pON6vZfqGwzuWF/9mEGReRz+iM8PiW0XSg== + +"@nx/nx-linux-x64-musl@22.6.5": + version "22.6.5" + resolved "https://registry.yarnpkg.com/@nx/nx-linux-x64-musl/-/nx-linux-x64-musl-22.6.5.tgz#94fed5da71aad67290ec10e35e30b9d065ed1c2e" + integrity sha512-RVOe2qcwhoIx6mxQURPjUfAW5SEOmT2gdhewvdcvX9ICq1hj5B2VarmkhTg0qroO7xiyqOqwq26mCzoV2I3NgQ== + +"@nx/nx-win32-arm64-msvc@22.6.5": + version "22.6.5" + resolved "https://registry.yarnpkg.com/@nx/nx-win32-arm64-msvc/-/nx-win32-arm64-msvc-22.6.5.tgz#60e3ad6379fbd6e9ae1d189d9803f156bcc29774" + integrity sha512-ZqurqI8VuYnsr2Kn4K4t+Gx6j/BZdf6qz/6Tv4A7XQQ6oNYVQgTqoNEFj+CCkVaIe6aIdCWpousFLqs+ZgBqYQ== + +"@nx/nx-win32-x64-msvc@22.6.5": + version "22.6.5" + resolved "https://registry.yarnpkg.com/@nx/nx-win32-x64-msvc/-/nx-win32-x64-msvc-22.6.5.tgz#d07d4d9d48c99f7b4c8c8a0dc2b14bf9239a3076" + integrity sha512-i2QFBJIuaYg9BHxrrnBV4O7W9rVL2k0pSIdk/rRp3EYJEU93iUng+qbZiY9wh1xvmXuUCE2G7TRd+8/SG/RFKg== "@octokit/auth-token@^2.4.4": version "2.5.0" @@ -11659,7 +11659,7 @@ aws-ssl-profiles@^1.1.2: resolved "https://registry.yarnpkg.com/aws-ssl-profiles/-/aws-ssl-profiles-1.1.2.tgz#157dd77e9f19b1d123678e93f120e6f193022641" integrity sha512-NZKeq9AfyQvEeNlN0zSYAaWrmBffJh3IELMZfRpJVWgrpEbtEpnjvzqBPf+mxoI287JohRDoa+/nsfqqiZmF6g== -axios@1.15.0, axios@^1.12.0: +axios@1.15.0: version "1.15.0" resolved "https://registry.yarnpkg.com/axios/-/axios-1.15.0.tgz#0fcee91ef03d386514474904b27863b2c683bf4f" integrity sha512-wWyJDlAatxk30ZJer+GeCWS209sA42X+N5jU2jy6oHTp7ufw8uzUTVFBX9+wTfAlhiJXGS0Bq7X6efruWjuK9Q== @@ -13123,7 +13123,7 @@ chalk@^3.0.0: ansi-styles "^4.1.0" supports-color "^7.1.0" -chalk@^4.0.0, chalk@^4.0.2, chalk@^4.1.0, chalk@^4.1.1, chalk@^4.1.2: +chalk@^4.0.0, chalk@^4.1.0, chalk@^4.1.1, chalk@^4.1.2: version "4.1.2" resolved "https://registry.yarnpkg.com/chalk/-/chalk-4.1.2.tgz#aac4e2b7734a740867aeb16bf02aad556a1e7a01" integrity sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA== @@ -14959,12 +14959,10 @@ effect@^4.0.0-beta.50: uuid "^13.0.0" yaml "^2.8.3" -ejs@^3.1.7: - version "3.1.8" - resolved "https://registry.yarnpkg.com/ejs/-/ejs-3.1.8.tgz#758d32910c78047585c7ef1f92f9ee041c1c190b" - integrity sha512-/sXZeMlhS0ArkfX2Aw780gJzXSMPnKjtspYZv+f3NiKLlubezAHDU5+9xz6gd3/NhG3txQCo6xlglmTS+oTGEQ== - dependencies: - jake "^10.8.5" +ejs@5.0.1: + version "5.0.1" + resolved "https://registry.yarnpkg.com/ejs/-/ejs-5.0.1.tgz#179523a437ed448543ad1b76ca4fb4c2e8950304" + integrity sha512-COqBPFMxuPTPspXl2DkVYaDS3HtrD1GpzOGkNTJ1IYkifq/r9h8SVEFrjA3D9/VJGOEoMQcrlhpntcSUrM8k6A== electron-to-chromium@^1.5.263: version "1.5.286" @@ -17265,13 +17263,6 @@ file-uri-to-path@1.0.0: resolved "https://registry.yarnpkg.com/file-uri-to-path/-/file-uri-to-path-1.0.0.tgz#553a7b8446ff6f684359c445f1e37a05dacc33dd" integrity sha512-0Zt+s3L7Vf1biwWZ29aARiVYLx7iMGnEUl9x33fbB/j3jR81u/O2LbqK+Bm1CDSNDKVtJ/YjwY7TUd5SkeLQLw== -filelist@^1.0.1: - version "1.0.4" - resolved "https://registry.yarnpkg.com/filelist/-/filelist-1.0.4.tgz#f78978a1e944775ff9e62e744424f215e58352b5" - integrity sha512-w1cEuf3S+DrLCQL7ET6kz+gmlJdbq9J7yXCSjK/OZCPA+qEN1WyF4ZAf0YYJa4/shHJra2t/d/r8SV4Ji+x+8Q== - dependencies: - minimatch "^5.0.1" - filesize@^10.0.5: version "10.1.6" resolved "https://registry.yarnpkg.com/filesize/-/filesize-10.1.6.tgz#31194da825ac58689c0bce3948f33ce83aabd361" @@ -20002,16 +19993,6 @@ jackspeak@^3.1.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" - integrity sha512-sVpxYeuAhWt0OTWITwT98oyV0GsXyMlXCF+3L1SuafBVUIr/uILGRB+NqwkzhgXKvoJpDIpQvqkUALgdmQsQxw== - dependencies: - async "^3.2.3" - chalk "^4.0.2" - filelist "^1.0.1" - minimatch "^3.0.4" - jest-diff@^29.7.0: version "29.7.0" resolved "https://registry.yarnpkg.com/jest-diff/-/jest-diff-29.7.0.tgz#017934a66ebb7ecf6f205e84699be10afd70458a" @@ -21986,7 +21967,7 @@ minimalistic-assert@^1.0.0: resolved "https://registry.yarnpkg.com/minimalistic-assert/-/minimalistic-assert-1.0.1.tgz#2e194de044626d4a10e7f7fbc00ce73e83e4d5c7" integrity sha512-UtJcAD4yEaGtjPezWuO9wC4nwUnVH/8/Im3yEHQP4b67cXlD/Qr9hdITCU1xDbSEXg2XKNaP8jsReV7vQd00/A== -minimatch@10.1.1, minimatch@10.2.4: +minimatch@10.2.4: version "10.2.4" resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-10.2.4.tgz#465b3accbd0218b8281f5301e27cedc697f96fde" integrity sha512-oRjTw/97aTBN0RHbYCdtF1MQfvusSIBQM0IZEgzl6426+8jSC0nF1a/GmnVLpfB9yyr6g6FTqWqiZVbxrtaCIg== @@ -22874,11 +22855,6 @@ node-int64@^0.4.0: resolved "https://registry.yarnpkg.com/node-int64/-/node-int64-0.4.0.tgz#87a9065cdb355d3182d8f94ce11188b825c68a3b" integrity sha1-h6kGXNs1XTGC2PlM4RGIuCXGijs= -node-machine-id@1.1.12: - version "1.1.12" - resolved "https://registry.yarnpkg.com/node-machine-id/-/node-machine-id-1.1.12.tgz#37904eee1e59b320bb9c5d6c0a59f3b469cb6267" - integrity sha512-QNABxbrPa3qEIfrE6GOJ7BYIuignnJw7iQ2YPbc3Nla1HzRJjXzZOiikfF8m7eAMfichLt3M4VgLOetqgDmgGQ== - node-mock-http@^1.0.0, node-mock-http@^1.0.4: version "1.0.4" resolved "https://registry.yarnpkg.com/node-mock-http/-/node-mock-http-1.0.4.tgz#21f2ab4ce2fe4fbe8a660d7c5195a1db85e042a4" @@ -23255,22 +23231,22 @@ nwsapi@^2.2.4: resolved "https://registry.yarnpkg.com/nwsapi/-/nwsapi-2.2.9.tgz#7f3303218372db2e9f27c27766bcfc59ae7e61c6" integrity sha512-2f3F0SEEer8bBu0dsNCFF50N0cTThV1nWFYcEYFZttdW0lDAoybv9cQoK7X7/68Z89S7FoRrVjP1LPX4XRf9vg== -nx@22.5.0: - version "22.5.0" - resolved "https://registry.yarnpkg.com/nx/-/nx-22.5.0.tgz#200898de49eec4df41eef5a44152721b6ee453df" - integrity sha512-GOHhDHXvuscD28Hpj1bP38oVrCgZ/+5UWjA8R/VkpbtkfMHgRZ0uHlfKLYXQAZIsjmTq7Tr+e4QchJt0e76n0w== +nx@22.6.5: + version "22.6.5" + resolved "https://registry.yarnpkg.com/nx/-/nx-22.6.5.tgz#71bfb4ccc35f2103397eb6ef251204256d0af541" + integrity sha512-VRKhDAt684dXNSz9MNjE7MekkCfQF41P2PSx5jEWQjDEP1Z4jFZbyeygWs5ZyOroG7/n0MoWAJTe6ftvIcBOAg== dependencies: "@napi-rs/wasm-runtime" "0.2.4" "@yarnpkg/lockfile" "^1.1.0" "@yarnpkg/parsers" "3.0.2" "@zkochan/js-yaml" "0.0.7" - axios "^1.12.0" + axios "1.15.0" cli-cursor "3.1.0" cli-spinners "2.6.1" cliui "^8.0.1" dotenv "~16.4.5" dotenv-expand "~11.0.6" - ejs "^3.1.7" + ejs "5.0.1" enquirer "~2.3.6" figures "3.2.0" flat "^5.0.2" @@ -23279,14 +23255,14 @@ nx@22.5.0: jest-diff "^30.0.2" jsonc-parser "3.2.0" lines-and-columns "2.0.3" - minimatch "10.1.1" - node-machine-id "1.1.12" + minimatch "10.2.4" npm-run-path "^4.0.1" open "^8.4.0" ora "5.3.0" picocolors "^1.1.0" resolve.exports "2.0.3" semver "^7.6.3" + smol-toml "1.6.1" string-width "^4.2.3" tar-stream "~2.2.0" tmp "~0.2.1" @@ -23297,16 +23273,16 @@ nx@22.5.0: yargs "^17.6.2" yargs-parser "21.1.1" optionalDependencies: - "@nx/nx-darwin-arm64" "22.5.0" - "@nx/nx-darwin-x64" "22.5.0" - "@nx/nx-freebsd-x64" "22.5.0" - "@nx/nx-linux-arm-gnueabihf" "22.5.0" - "@nx/nx-linux-arm64-gnu" "22.5.0" - "@nx/nx-linux-arm64-musl" "22.5.0" - "@nx/nx-linux-x64-gnu" "22.5.0" - "@nx/nx-linux-x64-musl" "22.5.0" - "@nx/nx-win32-arm64-msvc" "22.5.0" - "@nx/nx-win32-x64-msvc" "22.5.0" + "@nx/nx-darwin-arm64" "22.6.5" + "@nx/nx-darwin-x64" "22.6.5" + "@nx/nx-freebsd-x64" "22.6.5" + "@nx/nx-linux-arm-gnueabihf" "22.6.5" + "@nx/nx-linux-arm64-gnu" "22.6.5" + "@nx/nx-linux-arm64-musl" "22.6.5" + "@nx/nx-linux-x64-gnu" "22.6.5" + "@nx/nx-linux-x64-musl" "22.6.5" + "@nx/nx-win32-arm64-msvc" "22.6.5" + "@nx/nx-win32-x64-msvc" "22.6.5" nypm@^0.6.0, nypm@^0.6.2, nypm@^0.6.5: version "0.6.5" @@ -27511,6 +27487,11 @@ smob@^1.0.0: resolved "https://registry.yarnpkg.com/smob/-/smob-1.4.1.tgz#66270e7df6a7527664816c5b577a23f17ba6f5b5" integrity sha512-9LK+E7Hv5R9u4g4C3p+jjLstaLe11MDsL21UpYaCNmapvMkYhqCV4A/f/3gyH8QjMyh6l68q9xC85vihY9ahMQ== +smol-toml@1.6.1: + version "1.6.1" + resolved "https://registry.yarnpkg.com/smol-toml/-/smol-toml-1.6.1.tgz#4fceb5f7c4b86c2544024ef686e12ff0983465be" + integrity sha512-dWUG8F5sIIARXih1DTaQAX4SsiTXhInKf1buxdY9DIg4ZYPZK5nGM1VRIYmEbDbsHt7USo99xSLFu5Q1IqTmsg== + snake-case@^3.0.3: version "3.0.4" resolved "https://registry.yarnpkg.com/snake-case/-/snake-case-3.0.4.tgz#4f2bbd568e9935abdfd593f34c691dadb49c452c" From c741030c915e9529a8250724d3673077e4f93c7b Mon Sep 17 00:00:00 2001 From: Francesco Gringl-Novy Date: Thu, 23 Apr 2026 09:54:14 +0200 Subject: [PATCH 39/41] test(aws-serverless): Split npm & layer tests (#20442) This PR splits the aws-serverless test app into two, aws-serverless, which tests the npm package directly, and aws-serverless-layer, which tests using the layer. This allowed to make the test app easier to understand and follow and make the dependency graph clearer as well. I choose to run the layer in different node versions and the npm stuff only in node 20, IMHO that should be good enough, I don't think we need to also run the npm stuff in all the node versions, so we can reduce the number of test apps a bit. --- .github/workflows/build.yml | 2 +- .../aws-serverless-layer/package.json | 47 ++++++ .../aws-serverless-layer/playwright.config.ts | 5 + .../aws-serverless-layer/pull-sam-image.sh | 13 ++ .../aws-serverless-layer/samconfig.toml | 10 ++ .../src/lambda-functions-layer/Error/index.js | 0 .../lambda-functions-layer/ErrorEsm/index.mjs | 0 .../Streaming/index.mjs | 0 .../TracingCjs/index.js | 0 .../TracingEsm/index.mjs | 0 .../aws-serverless-layer/src/stack.ts | 124 ++++++++++++++++ .../start-event-proxy.mjs | 6 + .../tests/lambda-fixtures.ts | 137 ++++++++++++++++++ .../tests/layer.test.ts | 10 +- .../aws-serverless/package.json | 26 +--- .../aws-serverless/src/stack.ts | 117 +++++---------- 16 files changed, 388 insertions(+), 109 deletions(-) create mode 100644 dev-packages/e2e-tests/test-applications/aws-serverless-layer/package.json create mode 100644 dev-packages/e2e-tests/test-applications/aws-serverless-layer/playwright.config.ts create mode 100755 dev-packages/e2e-tests/test-applications/aws-serverless-layer/pull-sam-image.sh create mode 100644 dev-packages/e2e-tests/test-applications/aws-serverless-layer/samconfig.toml rename dev-packages/e2e-tests/test-applications/{aws-serverless => aws-serverless-layer}/src/lambda-functions-layer/Error/index.js (100%) rename dev-packages/e2e-tests/test-applications/{aws-serverless => aws-serverless-layer}/src/lambda-functions-layer/ErrorEsm/index.mjs (100%) rename dev-packages/e2e-tests/test-applications/{aws-serverless => aws-serverless-layer}/src/lambda-functions-layer/Streaming/index.mjs (100%) rename dev-packages/e2e-tests/test-applications/{aws-serverless => aws-serverless-layer}/src/lambda-functions-layer/TracingCjs/index.js (100%) rename dev-packages/e2e-tests/test-applications/{aws-serverless => aws-serverless-layer}/src/lambda-functions-layer/TracingEsm/index.mjs (100%) create mode 100644 dev-packages/e2e-tests/test-applications/aws-serverless-layer/src/stack.ts create mode 100644 dev-packages/e2e-tests/test-applications/aws-serverless-layer/start-event-proxy.mjs create mode 100644 dev-packages/e2e-tests/test-applications/aws-serverless-layer/tests/lambda-fixtures.ts rename dev-packages/e2e-tests/test-applications/{aws-serverless => aws-serverless-layer}/tests/layer.test.ts (96%) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index c92fb6b05838..add193a29d3b 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -935,7 +935,7 @@ jobs: matrix.test-application) uses: oven-sh/setup-bun@v2 - name: Set up AWS SAM - if: matrix.test-application == 'aws-serverless' + if: matrix.test-application == 'aws-serverless' || matrix.test-application == 'aws-serverless-layer' uses: aws-actions/setup-sam@v2 with: use-installer: true diff --git a/dev-packages/e2e-tests/test-applications/aws-serverless-layer/package.json b/dev-packages/e2e-tests/test-applications/aws-serverless-layer/package.json new file mode 100644 index 000000000000..16acf393e5d4 --- /dev/null +++ b/dev-packages/e2e-tests/test-applications/aws-serverless-layer/package.json @@ -0,0 +1,47 @@ +{ + "name": "aws-serverless-layer", + "version": "1.0.0", + "private": true, + "type": "commonjs", + "scripts": { + "test": "playwright test", + "clean": "npx rimraf node_modules pnpm-lock.yaml", + "test:pull-sam-image": "./pull-sam-image.sh", + "test:build": "pnpm test:pull-sam-image && pnpm install && npx rimraf node_modules/@sentry/aws-serverless/nodejs", + "test:assert": "pnpm test" + }, + "//": "We just need the @sentry/aws-serverless layer zip file, not the NPM package", + "devDependencies": { + "@aws-sdk/client-lambda": "^3.863.0", + "@playwright/test": "~1.56.0", + "@sentry-internal/test-utils": "link:../../../test-utils", + "@sentry/aws-serverless": "link:../../../../packages/aws-serverless/build/aws/dist-serverless/", + "aws-cdk-lib": "^2.210.0", + "constructs": "^10.4.2", + "glob": "^11.0.3", + "rimraf": "^5.0.10" + }, + "volta": { + "extends": "../../package.json" + }, + "//": "We need to override this here again to ensure this is not overwritten by the test runner", + "pnpm": { + "overrides": { + "@sentry/aws-serverless": "link:../../../../packages/aws-serverless/build/aws/dist-serverless/" + } + }, + "sentryTest": { + "variants": [ + { + "build-command": "NODE_VERSION=22 pnpm test:build", + "assert-command": "NODE_VERSION=22 pnpm test:assert", + "label": "aws-serverless-layer (Node 22)" + }, + { + "build-command": "NODE_VERSION=18 pnpm test:build", + "assert-command": "NODE_VERSION=18 pnpm test:assert", + "label": "aws-serverless-layer (Node 18)" + } + ] + } +} diff --git a/dev-packages/e2e-tests/test-applications/aws-serverless-layer/playwright.config.ts b/dev-packages/e2e-tests/test-applications/aws-serverless-layer/playwright.config.ts new file mode 100644 index 000000000000..e47333c66e76 --- /dev/null +++ b/dev-packages/e2e-tests/test-applications/aws-serverless-layer/playwright.config.ts @@ -0,0 +1,5 @@ +import { getPlaywrightConfig } from '@sentry-internal/test-utils'; + +export default getPlaywrightConfig(undefined, { + timeout: 60 * 1000 * 3, // 3 minutes +}); diff --git a/dev-packages/e2e-tests/test-applications/aws-serverless-layer/pull-sam-image.sh b/dev-packages/e2e-tests/test-applications/aws-serverless-layer/pull-sam-image.sh new file mode 100755 index 000000000000..c9659547a4ea --- /dev/null +++ b/dev-packages/e2e-tests/test-applications/aws-serverless-layer/pull-sam-image.sh @@ -0,0 +1,13 @@ +#!/bin/bash + +# Pull the Lambda Node docker image for SAM local. NODE_VERSION should be the major only (e.g. 20). +# Defaults to 20 to match the repo's Volta Node major (see root package.json "volta.node"). + +set -e + +NODE_VERSION="${NODE_VERSION:-20}" + +echo "Pulling Lambda Node $NODE_VERSION docker image..." +docker pull "public.ecr.aws/lambda/nodejs:${NODE_VERSION}" + +echo "Successfully pulled Lambda Node $NODE_VERSION docker image" diff --git a/dev-packages/e2e-tests/test-applications/aws-serverless-layer/samconfig.toml b/dev-packages/e2e-tests/test-applications/aws-serverless-layer/samconfig.toml new file mode 100644 index 000000000000..26f5a51c7a77 --- /dev/null +++ b/dev-packages/e2e-tests/test-applications/aws-serverless-layer/samconfig.toml @@ -0,0 +1,10 @@ +# SAM CLI expects this file in the project root; without it, `sam local start-lambda` logs +# OSError / missing file errors when run from the e2e temp directory. +# These values are placeholders — this app only uses `sam local`, not deploy. +version = 0.1 + +[default.deploy.parameters] +stack_name = "sentry-e2e-aws-serverless-layer-local" +region = "us-east-1" +confirm_changeset = false +capabilities = "CAPABILITY_IAM" diff --git a/dev-packages/e2e-tests/test-applications/aws-serverless/src/lambda-functions-layer/Error/index.js b/dev-packages/e2e-tests/test-applications/aws-serverless-layer/src/lambda-functions-layer/Error/index.js similarity index 100% rename from dev-packages/e2e-tests/test-applications/aws-serverless/src/lambda-functions-layer/Error/index.js rename to dev-packages/e2e-tests/test-applications/aws-serverless-layer/src/lambda-functions-layer/Error/index.js diff --git a/dev-packages/e2e-tests/test-applications/aws-serverless/src/lambda-functions-layer/ErrorEsm/index.mjs b/dev-packages/e2e-tests/test-applications/aws-serverless-layer/src/lambda-functions-layer/ErrorEsm/index.mjs similarity index 100% rename from dev-packages/e2e-tests/test-applications/aws-serverless/src/lambda-functions-layer/ErrorEsm/index.mjs rename to dev-packages/e2e-tests/test-applications/aws-serverless-layer/src/lambda-functions-layer/ErrorEsm/index.mjs diff --git a/dev-packages/e2e-tests/test-applications/aws-serverless/src/lambda-functions-layer/Streaming/index.mjs b/dev-packages/e2e-tests/test-applications/aws-serverless-layer/src/lambda-functions-layer/Streaming/index.mjs similarity index 100% rename from dev-packages/e2e-tests/test-applications/aws-serverless/src/lambda-functions-layer/Streaming/index.mjs rename to dev-packages/e2e-tests/test-applications/aws-serverless-layer/src/lambda-functions-layer/Streaming/index.mjs diff --git a/dev-packages/e2e-tests/test-applications/aws-serverless/src/lambda-functions-layer/TracingCjs/index.js b/dev-packages/e2e-tests/test-applications/aws-serverless-layer/src/lambda-functions-layer/TracingCjs/index.js similarity index 100% rename from dev-packages/e2e-tests/test-applications/aws-serverless/src/lambda-functions-layer/TracingCjs/index.js rename to dev-packages/e2e-tests/test-applications/aws-serverless-layer/src/lambda-functions-layer/TracingCjs/index.js diff --git a/dev-packages/e2e-tests/test-applications/aws-serverless/src/lambda-functions-layer/TracingEsm/index.mjs b/dev-packages/e2e-tests/test-applications/aws-serverless-layer/src/lambda-functions-layer/TracingEsm/index.mjs similarity index 100% rename from dev-packages/e2e-tests/test-applications/aws-serverless/src/lambda-functions-layer/TracingEsm/index.mjs rename to dev-packages/e2e-tests/test-applications/aws-serverless-layer/src/lambda-functions-layer/TracingEsm/index.mjs diff --git a/dev-packages/e2e-tests/test-applications/aws-serverless-layer/src/stack.ts b/dev-packages/e2e-tests/test-applications/aws-serverless-layer/src/stack.ts new file mode 100644 index 000000000000..8475ee0a328a --- /dev/null +++ b/dev-packages/e2e-tests/test-applications/aws-serverless-layer/src/stack.ts @@ -0,0 +1,124 @@ +import { Stack, CfnResource, StackProps } from 'aws-cdk-lib'; +import { Construct } from 'constructs'; +import * as path from 'node:path'; +import * as fs from 'node:fs'; +import * as os from 'node:os'; +import * as dns from 'node:dns/promises'; +import { arch, platform } from 'node:process'; +import { globSync } from 'glob'; + +const LAMBDA_FUNCTIONS_DIR = './src/lambda-functions-layer'; +const LAMBDA_FUNCTION_TIMEOUT = 10; +const LAYER_DIR = './node_modules/@sentry/aws-serverless/'; +export const SAM_PORT = 3001; + +/** Match SAM / Docker to this machine so Apple Silicon does not mix arm64 images with an x86_64 template default. */ +function samLambdaArchitecture(): 'arm64' | 'x86_64' { + return arch === 'arm64' ? 'arm64' : 'x86_64'; +} + +export class LocalLambdaStack extends Stack { + sentryLayer: CfnResource; + + constructor(scope: Construct, id: string, props: StackProps, hostIp: string) { + console.log('[LocalLambdaStack] Creating local SAM Lambda Stack'); + super(scope, id, props); + + this.templateOptions.templateFormatVersion = '2010-09-09'; + this.templateOptions.transforms = ['AWS::Serverless-2016-10-31']; + + console.log('[LocalLambdaStack] Add Sentry Lambda layer containing the Sentry SDK to the SAM stack'); + + const [layerZipFile] = globSync('sentry-node-serverless-*.zip', { cwd: LAYER_DIR }); + + if (!layerZipFile) { + throw new Error(`[LocalLambdaStack] Could not find sentry-node-serverless zip file in ${LAYER_DIR}`); + } + + this.sentryLayer = new CfnResource(this, 'SentryNodeServerlessSDK', { + type: 'AWS::Serverless::LayerVersion', + properties: { + ContentUri: path.join(LAYER_DIR, layerZipFile), + CompatibleRuntimes: ['nodejs18.x', 'nodejs20.x', 'nodejs22.x'], + CompatibleArchitectures: [samLambdaArchitecture()], + }, + }); + + const dsn = `http://public@${hostIp}:3031/1337`; + console.log(`[LocalLambdaStack] Using Sentry DSN: ${dsn}`); + + this.addLambdaFunctions(dsn); + } + + private addLambdaFunctions(dsn: string) { + console.log(`[LocalLambdaStack] Add all Lambda functions defined in ${LAMBDA_FUNCTIONS_DIR} to the SAM stack`); + + const lambdaDirs = fs + .readdirSync(LAMBDA_FUNCTIONS_DIR) + .filter(dir => fs.statSync(path.join(LAMBDA_FUNCTIONS_DIR, dir)).isDirectory()); + + for (const lambdaDir of lambdaDirs) { + const functionName = `Layer${lambdaDir}`; + + if (!process.env.NODE_VERSION) { + throw new Error('[LocalLambdaStack] NODE_VERSION is not set'); + } + + new CfnResource(this, functionName, { + type: 'AWS::Serverless::Function', + properties: { + Architectures: [samLambdaArchitecture()], + CodeUri: path.join(LAMBDA_FUNCTIONS_DIR, lambdaDir), + Handler: 'index.handler', + Runtime: `nodejs${process.env.NODE_VERSION}.x`, + Timeout: LAMBDA_FUNCTION_TIMEOUT, + Layers: [{ Ref: this.sentryLayer.logicalId }], + Environment: { + Variables: { + SENTRY_DSN: dsn, + SENTRY_TRACES_SAMPLE_RATE: 1.0, + SENTRY_DEBUG: true, + NODE_OPTIONS: `--import=@sentry/aws-serverless/awslambda-auto`, + }, + }, + }, + }); + + console.log(`[LocalLambdaStack] Added Lambda function: ${functionName}`); + } + } + + static async waitForStack(timeout = 60000, port = SAM_PORT) { + const startTime = Date.now(); + const maxWaitTime = timeout; + + while (Date.now() - startTime < maxWaitTime) { + try { + const response = await fetch(`http://127.0.0.1:${port}/`); + + if (response.ok || response.status === 404) { + console.log(`[LocalLambdaStack] SAM stack is ready`); + return; + } + } catch { + await new Promise(resolve => setTimeout(resolve, 1000)); + } + } + + throw new Error(`[LocalLambdaStack] Failed to start SAM stack after ${timeout}ms`); + } +} + +export async function getHostIp() { + if (process.env.GITHUB_ACTIONS) { + const host = await dns.lookup(os.hostname()); + return host.address; + } + + if (platform === 'darwin' || platform === 'win32') { + return 'host.docker.internal'; + } + + const host = await dns.lookup(os.hostname()); + return host.address; +} diff --git a/dev-packages/e2e-tests/test-applications/aws-serverless-layer/start-event-proxy.mjs b/dev-packages/e2e-tests/test-applications/aws-serverless-layer/start-event-proxy.mjs new file mode 100644 index 000000000000..dfc97cb7f3f3 --- /dev/null +++ b/dev-packages/e2e-tests/test-applications/aws-serverless-layer/start-event-proxy.mjs @@ -0,0 +1,6 @@ +import { startEventProxyServer } from '@sentry-internal/test-utils'; + +startEventProxyServer({ + port: 3031, + proxyServerName: 'aws-serverless-layer', +}); diff --git a/dev-packages/e2e-tests/test-applications/aws-serverless-layer/tests/lambda-fixtures.ts b/dev-packages/e2e-tests/test-applications/aws-serverless-layer/tests/lambda-fixtures.ts new file mode 100644 index 000000000000..4df52b322d26 --- /dev/null +++ b/dev-packages/e2e-tests/test-applications/aws-serverless-layer/tests/lambda-fixtures.ts @@ -0,0 +1,137 @@ +import { test as base, expect } from '@playwright/test'; +import { App } from 'aws-cdk-lib'; +import { LocalLambdaStack, SAM_PORT, getHostIp } from '../src/stack'; +import { writeFileSync } from 'node:fs'; +import { execSync, spawn } from 'node:child_process'; +import { LambdaClient } from '@aws-sdk/client-lambda'; + +const DOCKER_NETWORK_NAME = 'lambda-test-network'; +const SAM_TEMPLATE_FILE = 'sam.template.yml'; + +/** Major Node for SAM `--invoke-image`; default matches root `package.json` `volta.node` and `pull-sam-image.sh`. */ +const DEFAULT_NODE_VERSION_MAJOR = '20'; + +const SAM_INSTALL_ERROR = + 'You need to install sam, e.g. run `brew install aws-sam-cli`. Ensure `sam` is on your PATH when running tests.'; + +export { expect }; + +export const test = base.extend<{ testEnvironment: LocalLambdaStack; lambdaClient: LambdaClient }>({ + testEnvironment: [ + async ({}, use) => { + console.log('[testEnvironment fixture] Setting up AWS Lambda test infrastructure'); + + const nodeVersionMajor = process.env.NODE_VERSION?.trim() || DEFAULT_NODE_VERSION_MAJOR; + process.env.NODE_VERSION = nodeVersionMajor; + + assertSamOnPath(); + + execSync('docker network prune -f'); + createDockerNetwork(); + + const hostIp = await getHostIp(); + const app = new App(); + + const stack = new LocalLambdaStack(app, 'LocalLambdaStack', {}, hostIp); + const template = app.synth().getStackByName('LocalLambdaStack').template; + writeFileSync(SAM_TEMPLATE_FILE, JSON.stringify(template, null, 2)); + + const args = [ + 'local', + 'start-lambda', + '--debug', + '--template', + SAM_TEMPLATE_FILE, + '--warm-containers', + 'EAGER', + '--docker-network', + DOCKER_NETWORK_NAME, + '--skip-pull-image', + '--invoke-image', + `public.ecr.aws/lambda/nodejs:${nodeVersionMajor}`, + ]; + + console.log(`[testEnvironment fixture] Running SAM with args: ${args.join(' ')}`); + + const samProcess = spawn('sam', args, { + stdio: process.env.DEBUG ? 'inherit' : 'ignore', + env: envForSamChild(), + }); + + try { + await LocalLambdaStack.waitForStack(); + + await use(stack); + } finally { + console.log('[testEnvironment fixture] Tearing down AWS Lambda test infrastructure'); + + samProcess.kill('SIGTERM'); + await new Promise(resolve => { + samProcess.once('exit', resolve); + setTimeout(() => { + if (!samProcess.killed) { + samProcess.kill('SIGKILL'); + } + resolve(void 0); + }, 5000); + }); + + removeDockerNetwork(); + } + }, + { scope: 'worker', auto: true }, + ], + lambdaClient: async ({}, use) => { + const lambdaClient = new LambdaClient({ + endpoint: `http://127.0.0.1:${SAM_PORT}`, + region: 'us-east-1', + credentials: { + accessKeyId: 'dummy', + secretAccessKey: 'dummy', + }, + }); + + await use(lambdaClient); + }, +}); + +/** Avoid forcing linux/amd64 on Apple Silicon when `DOCKER_DEFAULT_PLATFORM` is set globally. */ +function envForSamChild(): NodeJS.ProcessEnv { + const env = { ...process.env }; + if (process.arch === 'arm64') { + delete env.DOCKER_DEFAULT_PLATFORM; + } + return env; +} + +function assertSamOnPath(): void { + try { + execSync('sam --version', { encoding: 'utf-8', stdio: 'pipe' }); + } catch { + throw new Error(SAM_INSTALL_ERROR); + } +} + +function createDockerNetwork() { + try { + execSync(`docker network create --driver bridge ${DOCKER_NETWORK_NAME}`); + } catch (error) { + const stderr = (error as { stderr?: Buffer }).stderr?.toString() ?? ''; + if (stderr.includes('already exists')) { + console.log(`[testEnvironment fixture] Reusing existing docker network ${DOCKER_NETWORK_NAME}`); + return; + } + throw error; + } +} + +function removeDockerNetwork() { + try { + execSync(`docker network rm ${DOCKER_NETWORK_NAME}`); + } catch (error) { + const stderr = (error as { stderr?: Buffer }).stderr?.toString() ?? ''; + if (!stderr.includes('No such network')) { + console.warn(`[testEnvironment fixture] Failed to remove docker network ${DOCKER_NETWORK_NAME}: ${stderr}`); + } + } +} diff --git a/dev-packages/e2e-tests/test-applications/aws-serverless/tests/layer.test.ts b/dev-packages/e2e-tests/test-applications/aws-serverless-layer/tests/layer.test.ts similarity index 96% rename from dev-packages/e2e-tests/test-applications/aws-serverless/tests/layer.test.ts rename to dev-packages/e2e-tests/test-applications/aws-serverless-layer/tests/layer.test.ts index fb3d45c5c188..c32dbfea7435 100644 --- a/dev-packages/e2e-tests/test-applications/aws-serverless/tests/layer.test.ts +++ b/dev-packages/e2e-tests/test-applications/aws-serverless-layer/tests/layer.test.ts @@ -4,7 +4,7 @@ import { test, expect } from './lambda-fixtures'; test.describe('Lambda layer', () => { test('tracing in CJS works', async ({ lambdaClient }) => { - const transactionEventPromise = waitForTransaction('aws-serverless', transactionEvent => { + const transactionEventPromise = waitForTransaction('aws-serverless-layer', transactionEvent => { return transactionEvent?.transaction === 'LayerTracingCjs'; }); @@ -72,7 +72,7 @@ test.describe('Lambda layer', () => { }); test('tracing in ESM works', async ({ lambdaClient }) => { - const transactionEventPromise = waitForTransaction('aws-serverless', transactionEvent => { + const transactionEventPromise = waitForTransaction('aws-serverless-layer', transactionEvent => { return transactionEvent?.transaction === 'LayerTracingEsm'; }); @@ -140,7 +140,7 @@ test.describe('Lambda layer', () => { }); test('capturing errors works', async ({ lambdaClient }) => { - const errorEventPromise = waitForError('aws-serverless', errorEvent => { + const errorEventPromise = waitForError('aws-serverless-layer', errorEvent => { return errorEvent?.exception?.values?.[0]?.value === 'test'; }); @@ -168,7 +168,7 @@ test.describe('Lambda layer', () => { }); test('capturing errors works in ESM', async ({ lambdaClient }) => { - const errorEventPromise = waitForError('aws-serverless', errorEvent => { + const errorEventPromise = waitForError('aws-serverless-layer', errorEvent => { return errorEvent?.exception?.values?.[0]?.value === 'test esm'; }); @@ -196,7 +196,7 @@ test.describe('Lambda layer', () => { }); test('streaming handlers work', async ({ lambdaClient }) => { - const transactionEventPromise = waitForTransaction('aws-serverless', transactionEvent => { + const transactionEventPromise = waitForTransaction('aws-serverless-layer', transactionEvent => { return transactionEvent?.transaction === 'LayerStreaming'; }); diff --git a/dev-packages/e2e-tests/test-applications/aws-serverless/package.json b/dev-packages/e2e-tests/test-applications/aws-serverless/package.json index 84076d8393c2..f74e7a670c50 100644 --- a/dev-packages/e2e-tests/test-applications/aws-serverless/package.json +++ b/dev-packages/e2e-tests/test-applications/aws-serverless/package.json @@ -7,41 +7,19 @@ "test": "playwright test", "clean": "npx rimraf node_modules pnpm-lock.yaml", "test:pull-sam-image": "./pull-sam-image.sh", - "test:build": "pnpm test:pull-sam-image && pnpm install && npx rimraf node_modules/@sentry/aws-serverless/nodejs", + "test:build": "pnpm test:pull-sam-image && pnpm install", "test:assert": "pnpm test" }, - "//": "We just need the @sentry/aws-serverless layer zip file, not the NPM package", "devDependencies": { "@aws-sdk/client-lambda": "^3.863.0", "@playwright/test": "~1.56.0", "@sentry-internal/test-utils": "link:../../../test-utils", - "@sentry/aws-serverless": "link:../../../../packages/aws-serverless/build/aws/dist-serverless/", + "@sentry/aws-serverless": "file:../../packed/sentry-aws-serverless-packed.tgz", "aws-cdk-lib": "^2.210.0", "constructs": "^10.4.2", - "glob": "^11.0.3", "rimraf": "^5.0.10" }, "volta": { "extends": "../../package.json" - }, - "//": "We need to override this here again to ensure this is not overwritten by the test runner", - "pnpm": { - "overrides": { - "@sentry/aws-serverless": "link:../../../../packages/aws-serverless/build/aws/dist-serverless/" - } - }, - "sentryTest": { - "variants": [ - { - "build-command": "NODE_VERSION=22 pnpm test:build", - "assert-command": "NODE_VERSION=22 pnpm test:assert", - "label": "aws-serverless (Node 22)" - }, - { - "build-command": "NODE_VERSION=18 pnpm test:build", - "assert-command": "NODE_VERSION=18 pnpm test:assert", - "label": "aws-serverless (Node 18)" - } - ] } } diff --git a/dev-packages/e2e-tests/test-applications/aws-serverless/src/stack.ts b/dev-packages/e2e-tests/test-applications/aws-serverless/src/stack.ts index 792bd4308c51..63ab8a533e70 100644 --- a/dev-packages/e2e-tests/test-applications/aws-serverless/src/stack.ts +++ b/dev-packages/e2e-tests/test-applications/aws-serverless/src/stack.ts @@ -5,13 +5,10 @@ import * as fs from 'node:fs'; import * as os from 'node:os'; import * as dns from 'node:dns/promises'; import { arch, platform } from 'node:process'; -import { globSync } from 'glob'; import { execFileSync } from 'node:child_process'; -const LAMBDA_FUNCTIONS_WITH_LAYER_DIR = './src/lambda-functions-layer'; -const LAMBDA_FUNCTIONS_WITH_NPM_DIR = './src/lambda-functions-npm'; +const LAMBDA_FUNCTIONS_DIR = './src/lambda-functions-npm'; const LAMBDA_FUNCTION_TIMEOUT = 10; -const LAYER_DIR = './node_modules/@sentry/aws-serverless/'; export const SAM_PORT = 3001; /** Match SAM / Docker to this machine so Apple Silicon does not mix arm64 images with an x86_64 template default. */ @@ -31,8 +28,6 @@ function resolvePackagesDir(): string { } export class LocalLambdaStack extends Stack { - sentryLayer: CfnResource; - constructor(scope: Construct, id: string, props: StackProps, hostIp: string) { console.log('[LocalLambdaStack] Creating local SAM Lambda Stack'); super(scope, id, props); @@ -40,92 +35,57 @@ export class LocalLambdaStack extends Stack { this.templateOptions.templateFormatVersion = '2010-09-09'; this.templateOptions.transforms = ['AWS::Serverless-2016-10-31']; - console.log('[LocalLambdaStack] Add Sentry Lambda layer containing the Sentry SDK to the SAM stack'); - - const [layerZipFile] = globSync('sentry-node-serverless-*.zip', { cwd: LAYER_DIR }); - - if (!layerZipFile) { - throw new Error(`[LocalLambdaStack] Could not find sentry-node-serverless zip file in ${LAYER_DIR}`); - } - - this.sentryLayer = new CfnResource(this, 'SentryNodeServerlessSDK', { - type: 'AWS::Serverless::LayerVersion', - properties: { - ContentUri: path.join(LAYER_DIR, layerZipFile), - CompatibleRuntimes: ['nodejs18.x', 'nodejs20.x', 'nodejs22.x'], - CompatibleArchitectures: [samLambdaArchitecture()], - }, - }); - const dsn = `http://public@${hostIp}:3031/1337`; console.log(`[LocalLambdaStack] Using Sentry DSN: ${dsn}`); - this.addLambdaFunctions({ functionsDir: LAMBDA_FUNCTIONS_WITH_LAYER_DIR, dsn, addLayer: true }); - this.addLambdaFunctions({ functionsDir: LAMBDA_FUNCTIONS_WITH_NPM_DIR, dsn, addLayer: false }); + this.addLambdaFunctions(dsn); } - private addLambdaFunctions({ - functionsDir, - dsn, - addLayer, - }: { - functionsDir: string; - dsn: string; - addLayer: boolean; - }) { - console.log(`[LocalLambdaStack] Add all Lambda functions defined in ${functionsDir} to the SAM stack`); + private addLambdaFunctions(dsn: string) { + console.log(`[LocalLambdaStack] Add all Lambda functions defined in ${LAMBDA_FUNCTIONS_DIR} to the SAM stack`); const lambdaDirs = fs - .readdirSync(functionsDir) - .filter(dir => fs.statSync(path.join(functionsDir, dir)).isDirectory()); + .readdirSync(LAMBDA_FUNCTIONS_DIR) + .filter(dir => fs.statSync(path.join(LAMBDA_FUNCTIONS_DIR, dir)).isDirectory()); for (const lambdaDir of lambdaDirs) { - const functionName = `${addLayer ? 'Layer' : 'Npm'}${lambdaDir}`; - - if (!addLayer) { - const lambdaPath = path.resolve(functionsDir, lambdaDir); - const packageLockPath = path.join(lambdaPath, 'package-lock.json'); - const nodeModulesPath = path.join(lambdaPath, 'node_modules'); - - // Point the dependency at the locally built packages so tests use the current workspace bits - // We need to link all @sentry/* packages that are dependencies of aws-serverless - // because otherwise npm will try to install them from the registry, where the current version is not yet published - const packagesToLink = ['aws-serverless', 'node', 'core', 'node-core', 'opentelemetry']; - const dependencies: Record = {}; - - const packagesDir = resolvePackagesDir(); - for (const pkgName of packagesToLink) { - const pkgDir = path.join(packagesDir, pkgName); - if (!fs.existsSync(pkgDir)) { - throw new Error( - `[LocalLambdaStack] Workspace package ${pkgName} not found at ${pkgDir}. Did you run the build?`, - ); - } - const relativePath = path.relative(lambdaPath, pkgDir); - dependencies[`@sentry/${pkgName}`] = `file:${relativePath.replace(/\\/g, '/')}`; + const functionName = `Npm${lambdaDir}`; + + const lambdaPath = path.resolve(LAMBDA_FUNCTIONS_DIR, lambdaDir); + const packageLockPath = path.join(lambdaPath, 'package-lock.json'); + const nodeModulesPath = path.join(lambdaPath, 'node_modules'); + + const packagesToLink = ['aws-serverless', 'node', 'core', 'node-core', 'opentelemetry']; + const dependencies: Record = {}; + + const packagesDir = resolvePackagesDir(); + for (const pkgName of packagesToLink) { + const pkgDir = path.join(packagesDir, pkgName); + if (!fs.existsSync(pkgDir)) { + throw new Error( + `[LocalLambdaStack] Workspace package ${pkgName} not found at ${pkgDir}. Did you run the build?`, + ); } + const relativePath = path.relative(lambdaPath, pkgDir); + dependencies[`@sentry/${pkgName}`] = `file:${relativePath.replace(/\\/g, '/')}`; + } - console.log(`[LocalLambdaStack] Install dependencies for ${functionName}`); + console.log(`[LocalLambdaStack] Install dependencies for ${functionName}`); - if (fs.existsSync(packageLockPath)) { - // Prevent stale lock files from pinning the published package version - fs.rmSync(packageLockPath); - } + if (fs.existsSync(packageLockPath)) { + fs.rmSync(packageLockPath); + } - if (fs.existsSync(nodeModulesPath)) { - // Ensure we reinstall from the workspace instead of reusing cached dependencies - fs.rmSync(nodeModulesPath, { recursive: true, force: true }); - } + if (fs.existsSync(nodeModulesPath)) { + fs.rmSync(nodeModulesPath, { recursive: true, force: true }); + } - const packageJson = { - dependencies, - }; + const packageJson = { + dependencies, + }; - fs.writeFileSync(path.join(lambdaPath, 'package.json'), JSON.stringify(packageJson, null, 2)); - // Use --install-links to copy files instead of creating symlinks for file: dependencies. - // Symlinks don't work inside the Docker container because the target paths don't exist there. - execFileSync('npm', ['install', '--install-links', '--prefix', lambdaPath], { stdio: 'inherit' }); - } + fs.writeFileSync(path.join(lambdaPath, 'package.json'), JSON.stringify(packageJson, null, 2)); + execFileSync('npm', ['install', '--install-links', '--prefix', lambdaPath], { stdio: 'inherit' }); if (!process.env.NODE_VERSION) { throw new Error('[LocalLambdaStack] NODE_VERSION is not set'); @@ -135,11 +95,10 @@ export class LocalLambdaStack extends Stack { type: 'AWS::Serverless::Function', properties: { Architectures: [samLambdaArchitecture()], - CodeUri: path.join(functionsDir, lambdaDir), + CodeUri: path.join(LAMBDA_FUNCTIONS_DIR, lambdaDir), Handler: 'index.handler', Runtime: `nodejs${process.env.NODE_VERSION}.x`, Timeout: LAMBDA_FUNCTION_TIMEOUT, - Layers: addLayer ? [{ Ref: this.sentryLayer.logicalId }] : undefined, Environment: { Variables: { SENTRY_DSN: dsn, From 39740da9e46de76f4b03bb7ae11849ea761dac14 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan=20Peer=20St=C3=B6cklmair?= Date: Thu, 23 Apr 2026 10:08:46 +0200 Subject: [PATCH 40/41] test(cloudflare): Use .makeRequestAndWaitForEnvelope to wait for envelopes (#20208) There was one flaky test, which got me a little deeper into the `runner.ts` logic. This test was only passing when it was running / finishing first. With the `shuffle` flag it was consistently failing, this is why this is added in this PR as well. Furthermore a random port will be created for each runner by setting `--port 0`, this just makes sure that when running `wrangler dev` in another tab, while running the tests, the local development has the default `:8787` port. `.makeRequestAndWaitForEnvelope` has been added that is making a request and then waits for an envelope, with that we make sure that flush has been called before going to the next request. The issue is that miniflare might cancel ongoing `waitUntil` calls (where we flush) --- .../cloudflare-integration-tests/runner.ts | 41 +++++++++++++++++++ .../public-api/startSpan-streamed/test.ts | 4 +- .../tracing/durableobject-spans/test.ts | 31 +++++++------- .../vite.config.mts | 3 ++ 4 files changed, 60 insertions(+), 19 deletions(-) diff --git a/dev-packages/cloudflare-integration-tests/runner.ts b/dev-packages/cloudflare-integration-tests/runner.ts index e0a48dd33ff5..542ffe82b802 100644 --- a/dev-packages/cloudflare-integration-tests/runner.ts +++ b/dev-packages/cloudflare-integration-tests/runner.ts @@ -50,6 +50,12 @@ type StartResult = { path: string, options?: { headers?: Record; data?: BodyInit; expectError?: boolean }, ): Promise; + makeRequestAndWaitForEnvelope( + method: 'get' | 'post', + path: string, + expected: Expected | Expected[], + options?: { headers?: Record; data?: BodyInit; expectError?: boolean }, + ): Promise; }; /** Creates a test runner */ @@ -108,6 +114,7 @@ export function createRunner(...paths: string[]) { const expectedEnvelopeCount = expectedEnvelopes.length; let envelopeCount = 0; + const envelopeWaiters: { expected: Expected; resolve: () => void; reject: (e: unknown) => void }[] = []; const { resolve: setWorkerPort, promise: workerPortPromise } = deferredPromise(); let child: ReturnType | undefined; let childSubWorker: ReturnType | undefined; @@ -120,6 +127,12 @@ export function createRunner(...paths: string[]) { } } + function waitForEnvelope(expected: Expected): Promise { + return new Promise((resolveWaiter, rejectWaiter) => { + envelopeWaiters.push({ expected, resolve: resolveWaiter, reject: rejectWaiter }); + }); + } + function assertEnvelopeMatches(expected: Expected, envelope: Envelope): void { if (typeof expected === 'function') { expected(envelope); @@ -137,6 +150,18 @@ export function createRunner(...paths: string[]) { return; } + // Check per-request waiters first (FIFO order) + if (envelopeWaiters.length > 0) { + const waiter = envelopeWaiters.shift()!; + try { + assertEnvelopeMatches(waiter.expected, envelope); + waiter.resolve(); + } catch (e) { + waiter.reject(e); + } + return; + } + try { if (unordered) { // find any matching expected envelope @@ -242,6 +267,10 @@ export function createRunner(...paths: string[]) { `SENTRY_DSN:http://public@localhost:${mockServerPort}/1337`, '--var', `SERVER_URL:${serverUrl}`, + '--port', + '0', + '--inspector-port', + '0', ...extraWranglerArgs, ], { stdio, signal }, @@ -304,6 +333,18 @@ export function createRunner(...paths: string[]) { return; } }, + makeRequestAndWaitForEnvelope: async function ( + method: 'get' | 'post', + path: string, + expected: Expected | Expected[], + options: { headers?: Record; data?: BodyInit; expectError?: boolean } = {}, + ): Promise { + const expectations = Array.isArray(expected) ? expected : [expected]; + const envelopePromises = expectations.map(e => waitForEnvelope(e)); + const result = await this.makeRequest(method, path, options); + await Promise.all(envelopePromises); + return result; + }, }; }, }; diff --git a/dev-packages/cloudflare-integration-tests/suites/public-api/startSpan-streamed/test.ts b/dev-packages/cloudflare-integration-tests/suites/public-api/startSpan-streamed/test.ts index 090142714d5b..d9c18431202b 100644 --- a/dev-packages/cloudflare-integration-tests/suites/public-api/startSpan-streamed/test.ts +++ b/dev-packages/cloudflare-integration-tests/suites/public-api/startSpan-streamed/test.ts @@ -197,7 +197,7 @@ it('sends a streamed span envelope with correct spans for a manually started spa }, 'url.port': { type: 'string', - value: '8787', + value: expect.stringMatching(/^\d{4,5}$/), }, 'url.scheme': { type: 'string', @@ -221,7 +221,7 @@ it('sends a streamed span envelope with correct spans for a manually started spa }, 'http.request.header.cf_connecting_ip': { type: 'string', - value: '::1', + value: expect.stringMatching(/^(::1|127\.0\.0\.1)$/), }, 'http.request.header.host': { type: 'string', diff --git a/dev-packages/cloudflare-integration-tests/suites/tracing/durableobject-spans/test.ts b/dev-packages/cloudflare-integration-tests/suites/tracing/durableobject-spans/test.ts index 1415950208cc..483b170936e4 100644 --- a/dev-packages/cloudflare-integration-tests/suites/tracing/durableobject-spans/test.ts +++ b/dev-packages/cloudflare-integration-tests/suites/tracing/durableobject-spans/test.ts @@ -25,15 +25,13 @@ it.skip('sends child spans on repeated Durable Object calls', async ({ signal }) // All 5 child spans should be present expect(transactionEvent.spans).toHaveLength(5); - expect(transactionEvent.spans).toEqual( - expect.arrayContaining([ - expect.objectContaining({ description: 'task-1', op: 'task' }), - expect.objectContaining({ description: 'task-2', op: 'task' }), - expect.objectContaining({ description: 'task-3', op: 'task' }), - expect.objectContaining({ description: 'task-4', op: 'task' }), - expect.objectContaining({ description: 'task-5', op: 'task' }), - ]), - ); + expect(transactionEvent.spans).toEqual([ + expect.objectContaining({ description: 'task-1', op: 'task' }), + expect.objectContaining({ description: 'task-2', op: 'task' }), + expect.objectContaining({ description: 'task-3', op: 'task' }), + expect.objectContaining({ description: 'task-4', op: 'task' }), + expect.objectContaining({ description: 'task-5', op: 'task' }), + ]); // All child spans share the root trace_id const rootTraceId = transactionEvent.contexts?.trace?.trace_id; @@ -43,13 +41,12 @@ it.skip('sends child spans on repeated Durable Object calls', async ({ signal }) } } - // Expect 5 transaction envelopes — one per call. - const runner = createRunner(__dirname).expectN(5, assertDoWorkEnvelope).start(signal); + const runner = createRunner(__dirname).start(signal); - await runner.makeRequest('get', '/'); - await runner.makeRequest('get', '/'); - await runner.makeRequest('get', '/'); - await runner.makeRequest('get', '/'); - await runner.makeRequest('get', '/'); - await runner.completed(); + // Each request waits for its envelope to be received and validated before proceeding. + await runner.makeRequestAndWaitForEnvelope('get', '/', assertDoWorkEnvelope); + await runner.makeRequestAndWaitForEnvelope('get', '/', assertDoWorkEnvelope); + await runner.makeRequestAndWaitForEnvelope('get', '/', assertDoWorkEnvelope); + await runner.makeRequestAndWaitForEnvelope('get', '/', assertDoWorkEnvelope); + await runner.makeRequestAndWaitForEnvelope('get', '/', assertDoWorkEnvelope); }); diff --git a/dev-packages/cloudflare-integration-tests/vite.config.mts b/dev-packages/cloudflare-integration-tests/vite.config.mts index a80bbbf63f32..0fdb560b8f11 100644 --- a/dev-packages/cloudflare-integration-tests/vite.config.mts +++ b/dev-packages/cloudflare-integration-tests/vite.config.mts @@ -28,6 +28,9 @@ export default defineConfig({ singleThread: true, }, }, + sequence: { + shuffle: true, + }, reporters: process.env.DEBUG ? ['default', { summary: false }] : process.env.GITHUB_ACTIONS From 7b584c40e3809c1e955e80f839ce2eaf29d73414 Mon Sep 17 00:00:00 2001 From: JPeer264 Date: Thu, 23 Apr 2026 11:06:25 +0200 Subject: [PATCH 41/41] meta(changelog): Update changelog for 10.50.0 --- CHANGELOG.md | 62 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 62 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 7ba7c69b14dd..dfcace55cda1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,68 @@ ## Unreleased +## 10.50.0 + +### Important Changes + +- **feat(effect): Support v4 beta ([#20394](https://github.com/getsentry/sentry-javascript/pull/20394))** + + The `@sentry/effect` integration now supports Effect v4 beta, enabling Sentry instrumentation for the latest Effect framework version. + Read more in the [Effect SDK readme](https://github.com/getsentry/sentry-javascript/blob/39740da9e46de76f4b03bb7ae11849ea761dac14/packages/effect/README.md). + +- **feat(hono): Add `@sentry/hono/bun` for Bun runtime ([#20355](https://github.com/getsentry/sentry-javascript/pull/20355))** + + A new `@sentry/hono/bun` entry point adds first-class support for running Hono applications instrumented with Sentry on the Bun runtime. + Read more in the [Hono SDK readme](https://github.com/getsentry/sentry-javascript/blob/39740da9e46de76f4b03bb7ae11849ea761dac14/packages/hono/README.md). + +- **feat(replay): Add replayStart/replayEnd client lifecycle hooks ([#20369](https://github.com/getsentry/sentry-javascript/pull/20369))** + + New `replayStart` and `replayEnd` client lifecycle hooks let you react to replay session start and end events in your application. + +### Other Changes + +- feat(core): Emit `no_parent_span` client outcomes for discarded spans requiring a parent ([#20350](https://github.com/getsentry/sentry-javascript/pull/20350)) +- feat(deps): Bump protobufjs from 7.5.4 to 7.5.5 ([#20372](https://github.com/getsentry/sentry-javascript/pull/20372)) +- feat(hono): Add runtime packages as optional peer dependencies ([#20423](https://github.com/getsentry/sentry-javascript/pull/20423)) +- feat(opentelemetry): Add tracingChannel utility for context propagation ([#20358](https://github.com/getsentry/sentry-javascript/pull/20358)) +- fix(browser): Enrich graphqlClient spans for relative URLs ([#20370](https://github.com/getsentry/sentry-javascript/pull/20370)) +- fix(browser): Filter implausible LCP values ([#20338](https://github.com/getsentry/sentry-javascript/pull/20338)) +- fix(cloudflare): Use TransformStream to keep track of streams ([#20452](https://github.com/getsentry/sentry-javascript/pull/20452)) +- fix(console): Re-patch console in AWS Lambda runtimes ([#20337](https://github.com/getsentry/sentry-javascript/pull/20337)) +- fix(core): Correct `GoogleGenAIIstrumentedMethod` typo in type name +- fix(core): Handle stateless MCP wrapper transport correlation ([#20293](https://github.com/getsentry/sentry-javascript/pull/20293)) +- fix(hono): Remove undefined from options type ([#20419](https://github.com/getsentry/sentry-javascript/pull/20419)) +- fix(node): Guard against null `httpVersion` in outgoing request span attributes ([#20430](https://github.com/getsentry/sentry-javascript/pull/20430)) +- fix(node-core): Pass rejection reason instead of Promise as originalException ([#20366](https://github.com/getsentry/sentry-javascript/pull/20366)) + +
+ Internal Changes + +- chore: Ignore claude worktrees ([#20440](https://github.com/getsentry/sentry-javascript/pull/20440)) +- chore: Prevent test from creating zombie process ([#20392](https://github.com/getsentry/sentry-javascript/pull/20392)) +- chore: Update size-limit ([#20412](https://github.com/getsentry/sentry-javascript/pull/20412)) +- chore(dev-deps): Bump nx from 22.5.0 to 22.6.5 ([#20458](https://github.com/getsentry/sentry-javascript/pull/20458)) +- chore(e2e-tests): Use tarball symlinks for E2E tests instead of verdaccio ([#20386](https://github.com/getsentry/sentry-javascript/pull/20386)) +- chore(lint): Remove lint warnings ([#20413](https://github.com/getsentry/sentry-javascript/pull/20413)) +- chore(test): Remove empty variant tests ([#20443](https://github.com/getsentry/sentry-javascript/pull/20443)) +- chore(tests): Use verdaccio as node process instead of docker image ([#20336](https://github.com/getsentry/sentry-javascript/pull/20336)) +- docs(readme): Update usage instructions for binary scripts ([#20426](https://github.com/getsentry/sentry-javascript/pull/20426)) +- ref(node): Vendor undici instrumentation ([#20190](https://github.com/getsentry/sentry-javascript/pull/20190)) +- test(aws-serverless): Ensure aws-serverless E2E tests run locally ([#20441](https://github.com/getsentry/sentry-javascript/pull/20441)) +- test(aws-serverless): Split npm & layer tests ([#20442](https://github.com/getsentry/sentry-javascript/pull/20442)) +- test(browser): Fix flaky sessions route-lifecycle test + upgrade axios ([#20197](https://github.com/getsentry/sentry-javascript/pull/20197)) +- test(cloudflare): Use `.makeRequestAndWaitForEnvelope` to wait for envelopes ([#20208](https://github.com/getsentry/sentry-javascript/pull/20208)) +- test(effect): Rename effect e2e tests to a versioned folder ([#20390](https://github.com/getsentry/sentry-javascript/pull/20390)) +- test(hono): Add E2E test for Hono on Cloudflare, Node and Bun ([#20406](https://github.com/getsentry/sentry-javascript/pull/20406)) +- test(hono): Add E2E tests for middleware spans ([#20451](https://github.com/getsentry/sentry-javascript/pull/20451)) +- test(nextjs): Unskip blocked cf tests ([#20356](https://github.com/getsentry/sentry-javascript/pull/20356)) +- test(node): Refactor integration tests for `honoIntegration` ([#20397](https://github.com/getsentry/sentry-javascript/pull/20397)) +- test(node): Use docker-compose healthchecks for service readiness ([#20429](https://github.com/getsentry/sentry-javascript/pull/20429)) +- test(node-core): Fix minute-boundary race in session-aggregate tests ([#20437](https://github.com/getsentry/sentry-javascript/pull/20437)) +- test(nuxt): Fix flaky database error test ([#20447](https://github.com/getsentry/sentry-javascript/pull/20447)) + +
+ ## 10.49.0 ### Important Changes