From 10fe77f8635f9f3d43865256887528765cef4e7b Mon Sep 17 00:00:00 2001 From: Abhijeet Prasad Date: Wed, 6 Apr 2022 21:11:49 -0400 Subject: [PATCH 001/204] ref(tracing): Remove references to @sentry/apm (#4845) Removes all references to `@sentry/apm`, a deprecated package we do not use anymore. --- packages/integrations/src/vue.ts | 58 ++++---------------- packages/react/src/profiler.tsx | 90 +++----------------------------- 2 files changed, 16 insertions(+), 132 deletions(-) diff --git a/packages/integrations/src/vue.ts b/packages/integrations/src/vue.ts index 1b1cf3caa63a..12c55f53df32 100644 --- a/packages/integrations/src/vue.ts +++ b/packages/integrations/src/vue.ts @@ -5,15 +5,6 @@ import { basename, getGlobalObject, logger, timestampWithMs } from '@sentry/util import { IS_DEBUG_BUILD } from './flags'; -/** - * Used to extract Tracing integration from the current client, - * without the need to import `Tracing` itself from the @sentry/apm package. - * @deprecated as @sentry/tracing should be used over @sentry/apm. - */ -const TRACING_GETTER = { - id: 'Tracing', -} as any as IntegrationClass; - /** * Used to extract BrowserTracing integration from @sentry/tracing */ @@ -150,7 +141,6 @@ export class Vue implements Integration { private readonly _componentsCache: { [key: string]: string } = {}; private _rootSpan?: Span; private _rootSpanTimer?: ReturnType; - private _tracingActivity?: number; /** * @inheritDoc @@ -260,30 +250,12 @@ export class Vue implements Integration { vm.$once(`hook:${hook}`, () => { // Create an activity on the first event call. There'll be no second call, as rootSpan will be in place, // thus new event handler won't be attached. - - // We do this whole dance with `TRACING_GETTER` to prevent `@sentry/apm` from becoming a peerDependency. - // We also need to ask for the `.constructor`, as `pushActivity` and `popActivity` are static, not instance methods. - /* eslint-disable @typescript-eslint/no-unsafe-member-access */ - // eslint-disable-next-line deprecation/deprecation - const tracingIntegration = getCurrentHub().getIntegration(TRACING_GETTER); - if (tracingIntegration) { - this._tracingActivity = (tracingIntegration as any).constructor.pushActivity('Vue Application Render'); - const transaction = (tracingIntegration as any).constructor.getTransaction(); - if (transaction) { - this._rootSpan = transaction.startChild({ - description: 'Application Render', - op: VUE_OP, - }); - } - // Use functionality from @sentry/tracing - } else { - const activeTransaction = getActiveTransaction(getCurrentHub()); - if (activeTransaction) { - this._rootSpan = activeTransaction.startChild({ - description: 'Application Render', - op: VUE_OP, - }); - } + const activeTransaction = getActiveTransaction(getCurrentHub()); + if (activeTransaction) { + this._rootSpan = activeTransaction.startChild({ + description: 'Application Render', + op: VUE_OP, + }); } /* eslint-enable @typescript-eslint/no-unsafe-member-access */ }); @@ -349,24 +321,13 @@ export class Vue implements Integration { }; /** Finish top-level span and activity with a debounce configured using `timeout` option */ - private _finishRootSpan(timestamp: number, getCurrentHub: () => Hub): void { + private _finishRootSpan(timestamp: number, _getCurrentHub: () => Hub): void { if (this._rootSpanTimer) { clearTimeout(this._rootSpanTimer); } this._rootSpanTimer = setTimeout(() => { - if (this._tracingActivity) { - // We do this whole dance with `TRACING_GETTER` to prevent `@sentry/apm` from becoming a peerDependency. - // We also need to ask for the `.constructor`, as `pushActivity` and `popActivity` are static, not instance methods. - // eslint-disable-next-line deprecation/deprecation - const tracingIntegration = getCurrentHub().getIntegration(TRACING_GETTER); - if (tracingIntegration) { - // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access - (tracingIntegration as any).constructor.popActivity(this._tracingActivity); - } - } - - // We should always finish the span, only should pop activity if using @sentry/apm + // We should always finish the span if (this._rootSpan) { this._rootSpan.finish(timestamp); } @@ -379,8 +340,7 @@ export class Vue implements Integration { this._options.Vue.mixin({ beforeCreate(this: ViewModel): void { - // eslint-disable-next-line deprecation/deprecation - if (getCurrentHub().getIntegration(TRACING_GETTER) || getCurrentHub().getIntegration(BROWSER_TRACING_GETTER)) { + if (getCurrentHub().getIntegration(BROWSER_TRACING_GETTER)) { // `this` points to currently rendered component applyTracingHooks(this, getCurrentHub); } else { diff --git a/packages/react/src/profiler.tsx b/packages/react/src/profiler.tsx index acf764347c69..c52d503eb92b 100644 --- a/packages/react/src/profiler.tsx +++ b/packages/react/src/profiler.tsx @@ -1,7 +1,7 @@ /* eslint-disable @typescript-eslint/no-unsafe-member-access */ /* eslint-disable @typescript-eslint/no-explicit-any */ import { getCurrentHub, Hub } from '@sentry/browser'; -import { Integration, IntegrationClass, Span, Transaction } from '@sentry/types'; +import { Span, Transaction } from '@sentry/types'; import { timestampWithMs } from '@sentry/utils'; import hoistNonReactStatics from 'hoist-non-react-statics'; import * as React from 'react'; @@ -10,66 +10,6 @@ import { REACT_MOUNT_OP, REACT_RENDER_OP, REACT_UPDATE_OP } from './constants'; export const UNKNOWN_COMPONENT = 'unknown'; -const TRACING_GETTER = ({ - id: 'Tracing', -} as any) as IntegrationClass; - -let globalTracingIntegration: Integration | null = null; -/** @deprecated remove when @sentry/apm no longer used */ -const getTracingIntegration = (): Integration | null => { - if (globalTracingIntegration) { - return globalTracingIntegration; - } - - globalTracingIntegration = getCurrentHub().getIntegration(TRACING_GETTER); - return globalTracingIntegration; -}; - -/** - * pushActivity creates an new react activity. - * Is a no-op if Tracing integration is not valid - * @param name displayName of component that started activity - * @deprecated remove when @sentry/apm no longer used - */ -function pushActivity(name: string, op: string): number | null { - if (globalTracingIntegration === null) { - return null; - } - - return (globalTracingIntegration as any).constructor.pushActivity(name, { - description: `<${name}>`, - op, - }); -} - -/** - * popActivity removes a React activity. - * Is a no-op if Tracing integration is not valid. - * @param activity id of activity that is being popped - * @deprecated remove when @sentry/apm no longer used - */ -function popActivity(activity: number | null): void { - if (activity === null || globalTracingIntegration === null) { - return; - } - - (globalTracingIntegration as any).constructor.popActivity(activity); -} - -/** - * Obtain a span given an activity id. - * Is a no-op if Tracing integration is not valid. - * @param activity activity id associated with obtained span - * @deprecated remove when @sentry/apm no longer used - */ -function getActivitySpan(activity: number | null): Span | undefined { - if (activity === null || globalTracingIntegration === null) { - return undefined; - } - - return (globalTracingIntegration as any).constructor.getActivitySpan(activity) as Span | undefined; -} - export type ProfilerProps = { // The name of the component being profiled. name: string; @@ -95,9 +35,6 @@ class Profiler extends React.Component { */ protected _mountSpan: Span | undefined = undefined; - // The activity representing how long it takes to mount a component. - private _mountActivity: number | null = null; - // eslint-disable-next-line @typescript-eslint/member-ordering public static defaultProps: Partial = { disabled: false, @@ -113,19 +50,12 @@ class Profiler extends React.Component { return; } - // If they are using @sentry/apm, we need to push/pop activities - // eslint-disable-next-line deprecation/deprecation - if (getTracingIntegration()) { - // eslint-disable-next-line deprecation/deprecation - this._mountActivity = pushActivity(name, REACT_MOUNT_OP); - } else { - const activeTransaction = getActiveTransaction(); - if (activeTransaction) { - this._mountSpan = activeTransaction.startChild({ - description: `<${name}>`, - op: REACT_MOUNT_OP, - }); - } + const activeTransaction = getActiveTransaction(); + if (activeTransaction) { + this._mountSpan = activeTransaction.startChild({ + description: `<${name}>`, + op: REACT_MOUNT_OP, + }); } } @@ -133,12 +63,6 @@ class Profiler extends React.Component { public componentDidMount(): void { if (this._mountSpan) { this._mountSpan.finish(); - } else { - // eslint-disable-next-line deprecation/deprecation - this._mountSpan = getActivitySpan(this._mountActivity); - // eslint-disable-next-line deprecation/deprecation - popActivity(this._mountActivity); - this._mountActivity = null; } } From f50139152188487536a45870de6040d8128b9acb Mon Sep 17 00:00:00 2001 From: Abhijeet Prasad Date: Wed, 6 Apr 2022 21:12:47 -0400 Subject: [PATCH 002/204] ref(core): Delete `API` class (#4848) Removes the deprecated `API` class. --- packages/core/src/api.ts | 69 +----------------------------- packages/core/src/index.ts | 2 - packages/core/test/lib/api.test.ts | 39 +++++++++++------ 3 files changed, 26 insertions(+), 84 deletions(-) diff --git a/packages/core/src/api.ts b/packages/core/src/api.ts index bace0ad07591..8d76cb135400 100644 --- a/packages/core/src/api.ts +++ b/packages/core/src/api.ts @@ -17,73 +17,6 @@ export interface APIDetails { readonly tunnel?: string; } -/** - * Helper class to provide urls, headers and metadata that can be used to form - * different types of requests to Sentry endpoints. - * Supports both envelopes and regular event requests. - * - * @deprecated Please use APIDetails - **/ -export class API { - /** The DSN as passed to Sentry.init() */ - public dsn: DsnLike; - - /** Metadata about the SDK (name, version, etc) for inclusion in envelope headers */ - public metadata: SdkMetadata; - - /** The internally used Dsn object. */ - private readonly _dsnObject: DsnComponents; - - /** The envelope tunnel to use. */ - private readonly _tunnel?: string; - - /** Create a new instance of API */ - public constructor(dsn: DsnLike, metadata: SdkMetadata = {}, tunnel?: string) { - this.dsn = dsn; - this._dsnObject = makeDsn(dsn); - this.metadata = metadata; - this._tunnel = tunnel; - } - - /** Returns the Dsn object. */ - public getDsn(): DsnComponents { - return this._dsnObject; - } - - /** Does this transport force envelopes? */ - public forceEnvelope(): boolean { - return !!this._tunnel; - } - - /** Returns the prefix to construct Sentry ingestion API endpoints. */ - public getBaseApiEndpoint(): string { - return getBaseApiEndpoint(this._dsnObject); - } - - /** Returns the store endpoint URL. */ - public getStoreEndpoint(): string { - return getStoreEndpoint(this._dsnObject); - } - - /** - * Returns the store endpoint URL with auth in the query string. - * - * Sending auth as part of the query string and not as custom HTTP headers avoids CORS preflight requests. - */ - public getStoreEndpointWithUrlEncodedAuth(): string { - return getStoreEndpointWithUrlEncodedAuth(this._dsnObject); - } - - /** - * Returns the envelope endpoint URL with auth in the query string. - * - * Sending auth as part of the query string and not as custom HTTP headers avoids CORS preflight requests. - */ - public getEnvelopeEndpointWithUrlEncodedAuth(): string { - return getEnvelopeEndpointWithUrlEncodedAuth(this._dsnObject, this._tunnel); - } -} - /** Initializes API Details */ export function initAPIDetails(dsn: DsnLike, metadata?: SdkMetadata, tunnel?: string): APIDetails { return { @@ -117,7 +50,7 @@ function _encodedAuth(dsn: DsnComponents): string { } /** Returns the store endpoint URL. */ -function getStoreEndpoint(dsn: DsnComponents): string { +export function getStoreEndpoint(dsn: DsnComponents): string { return _getIngestEndpoint(dsn, 'store'); } diff --git a/packages/core/src/index.ts b/packages/core/src/index.ts index f09d804d3bed..b066724ce099 100644 --- a/packages/core/src/index.ts +++ b/packages/core/src/index.ts @@ -15,8 +15,6 @@ export { } from '@sentry/minimal'; export { addGlobalEventProcessor, getCurrentHub, getHubFromCarrier, Hub, makeMain, Scope, Session } from '@sentry/hub'; export { - // eslint-disable-next-line deprecation/deprecation - API, APIDetails, getEnvelopeEndpointWithUrlEncodedAuth, getStoreEndpointWithUrlEncodedAuth, diff --git a/packages/core/test/lib/api.test.ts b/packages/core/test/lib/api.test.ts index 14b44aed9602..af3a7bfb8ca3 100644 --- a/packages/core/test/lib/api.test.ts +++ b/packages/core/test/lib/api.test.ts @@ -1,27 +1,38 @@ /* eslint-disable deprecation/deprecation */ import { makeDsn } from '@sentry/utils'; -import { API, getReportDialogEndpoint, getRequestHeaders } from '../../src/api'; +import { + getEnvelopeEndpointWithUrlEncodedAuth, + getReportDialogEndpoint, + getRequestHeaders, + getStoreEndpoint, + getStoreEndpointWithUrlEncodedAuth, + initAPIDetails, +} from '../../src/api'; const ingestDsn = 'https://abc@xxxx.ingest.sentry.io:1234/subpath/123'; const dsnPublic = 'https://abc@sentry.io:1234/subpath/123'; const legacyDsn = 'https://abc:123@sentry.io:1234/subpath/123'; const tunnel = 'https://hello.com/world'; +const ingestDsnAPI = initAPIDetails(ingestDsn); +const dsnPublicAPI = initAPIDetails(dsnPublic); + describe('API', () => { test('getStoreEndpoint', () => { - expect(new API(dsnPublic).getStoreEndpointWithUrlEncodedAuth()).toEqual( + expect(getStoreEndpointWithUrlEncodedAuth(dsnPublicAPI.dsn)).toEqual( 'https://sentry.io:1234/subpath/api/123/store/?sentry_key=abc&sentry_version=7', ); - expect(new API(dsnPublic).getStoreEndpoint()).toEqual('https://sentry.io:1234/subpath/api/123/store/'); - expect(new API(ingestDsn).getStoreEndpoint()).toEqual('https://xxxx.ingest.sentry.io:1234/subpath/api/123/store/'); + expect(getStoreEndpoint(dsnPublicAPI.dsn)).toEqual('https://sentry.io:1234/subpath/api/123/store/'); + expect(getStoreEndpoint(ingestDsnAPI.dsn)).toEqual('https://xxxx.ingest.sentry.io:1234/subpath/api/123/store/'); }); test('getEnvelopeEndpoint', () => { - expect(new API(dsnPublic).getEnvelopeEndpointWithUrlEncodedAuth()).toEqual( + expect(getEnvelopeEndpointWithUrlEncodedAuth(dsnPublicAPI.dsn)).toEqual( 'https://sentry.io:1234/subpath/api/123/envelope/?sentry_key=abc&sentry_version=7', ); - expect(new API(dsnPublic, {}, tunnel).getEnvelopeEndpointWithUrlEncodedAuth()).toEqual(tunnel); + const dsnPublicAPIWithTunnel = initAPIDetails(dsnPublic, {}, tunnel); + expect(getEnvelopeEndpointWithUrlEncodedAuth(dsnPublicAPIWithTunnel.dsn, tunnel)).toEqual(tunnel); }); test('getRequestHeaders', () => { @@ -118,13 +129,13 @@ describe('API', () => { ); }); - test('getDsn', () => { - expect(new API(dsnPublic).getDsn().host).toEqual(makeDsn(dsnPublic).host); - expect(new API(dsnPublic).getDsn().path).toEqual(makeDsn(dsnPublic).path); - expect(new API(dsnPublic).getDsn().pass).toEqual(makeDsn(dsnPublic).pass); - expect(new API(dsnPublic).getDsn().port).toEqual(makeDsn(dsnPublic).port); - expect(new API(dsnPublic).getDsn().protocol).toEqual(makeDsn(dsnPublic).protocol); - expect(new API(dsnPublic).getDsn().projectId).toEqual(makeDsn(dsnPublic).projectId); - expect(new API(dsnPublic).getDsn().publicKey).toEqual(makeDsn(dsnPublic).publicKey); + test('initAPIDetails dsn', () => { + expect(dsnPublicAPI.dsn.host).toEqual(makeDsn(dsnPublic).host); + expect(dsnPublicAPI.dsn.path).toEqual(makeDsn(dsnPublic).path); + expect(dsnPublicAPI.dsn.pass).toEqual(makeDsn(dsnPublic).pass); + expect(dsnPublicAPI.dsn.port).toEqual(makeDsn(dsnPublic).port); + expect(dsnPublicAPI.dsn.protocol).toEqual(makeDsn(dsnPublic).protocol); + expect(dsnPublicAPI.dsn.projectId).toEqual(makeDsn(dsnPublic).projectId); + expect(dsnPublicAPI.dsn.publicKey).toEqual(makeDsn(dsnPublic).publicKey); }); }); From 1997ad5971a6397ddec9fe7d984855d08332ed5f Mon Sep 17 00:00:00 2001 From: Abhijeet Prasad Date: Wed, 6 Apr 2022 21:13:38 -0400 Subject: [PATCH 003/204] ref(tracing): Delete deprecated `startSpan` and `child` methods (#4849) Remove deprecated methods `startSpan` and `child`. These deprecated methods were removed in favour of `span.startChild`. --- packages/hub/src/hub.ts | 9 --------- packages/tracing/src/span.ts | 10 ---------- packages/types/src/hub.ts | 6 ------ packages/types/src/span.ts | 8 -------- 4 files changed, 33 deletions(-) diff --git a/packages/hub/src/hub.ts b/packages/hub/src/hub.ts index 67761953abfa..b6ebffc74866 100644 --- a/packages/hub/src/hub.ts +++ b/packages/hub/src/hub.ts @@ -14,8 +14,6 @@ import { Primitive, SessionContext, Severity, - Span, - SpanContext, Transaction, TransactionContext, User, @@ -385,13 +383,6 @@ export class Hub implements HubInterface { } } - /** - * @inheritDoc - */ - public startSpan(context: SpanContext): Span { - return this._callExtensionMethod('startSpan', context); - } - /** * @inheritDoc */ diff --git a/packages/tracing/src/span.ts b/packages/tracing/src/span.ts index 990f56ce981c..baece680f52c 100644 --- a/packages/tracing/src/span.ts +++ b/packages/tracing/src/span.ts @@ -149,16 +149,6 @@ export class Span implements SpanInterface { } } - /** - * @inheritDoc - * @deprecated - */ - public child( - spanContext?: Pick>, - ): Span { - return this.startChild(spanContext); - } - /** * @inheritDoc */ diff --git a/packages/types/src/hub.ts b/packages/types/src/hub.ts index c80ea1b51686..49452266fe9f 100644 --- a/packages/types/src/hub.ts +++ b/packages/types/src/hub.ts @@ -7,7 +7,6 @@ import { Primitive } from './misc'; import { Scope } from './scope'; import { Session, SessionContext } from './session'; import { Severity } from './severity'; -import { Span, SpanContext } from './span'; import { CustomSamplingContext, Transaction, TransactionContext } from './transaction'; import { User } from './user'; @@ -180,11 +179,6 @@ export interface Hub { /** Returns all trace headers that are currently on the top scope. */ traceHeaders(): { [key: string]: string }; - /** - * @deprecated No longer does anything. Use use {@link Transaction.startChild} instead. - */ - startSpan(context: SpanContext): Span; - /** * Starts a new `Transaction` and returns it. This is the entry point to manual tracing instrumentation. * diff --git a/packages/types/src/span.ts b/packages/types/src/span.ts index 09b69454835e..d26887a93f2a 100644 --- a/packages/types/src/span.ts +++ b/packages/types/src/span.ts @@ -128,14 +128,6 @@ export interface Span extends SpanContext { */ setHttpStatus(httpStatus: number): this; - /** - * Use {@link startChild} - * @deprecated - */ - child( - spanContext?: Pick>, - ): Span; - /** * Creates a new `Span` while setting the current `Span.id` as `parentSpanId`. * Also the `sampled` decision will be inherited. From 5f0d7c2538b7b37fdb5aabdbe1ae6dee81ad9624 Mon Sep 17 00:00:00 2001 From: Abhijeet Prasad Date: Wed, 6 Apr 2022 21:14:07 -0400 Subject: [PATCH 004/204] ref(core): Remove `whitelistUrls`/`blacklistUrls` (#4850) --- packages/browser/src/backend.ts | 6 ----- .../core/src/integrations/inboundfilters.ts | 23 ++----------------- 2 files changed, 2 insertions(+), 27 deletions(-) diff --git a/packages/browser/src/backend.ts b/packages/browser/src/backend.ts index 4cdef3d11047..89cf843d48ae 100644 --- a/packages/browser/src/backend.ts +++ b/packages/browser/src/backend.ts @@ -23,12 +23,6 @@ export interface BrowserOptions extends Options { * By default, all errors will be sent. */ denyUrls?: Array; - - /** @deprecated use {@link Options.allowUrls} instead. */ - whitelistUrls?: Array; - - /** @deprecated use {@link Options.denyUrls} instead. */ - blacklistUrls?: Array; } /** diff --git a/packages/core/src/integrations/inboundfilters.ts b/packages/core/src/integrations/inboundfilters.ts index ff6c616c442d..83d635ff3256 100644 --- a/packages/core/src/integrations/inboundfilters.ts +++ b/packages/core/src/integrations/inboundfilters.ts @@ -13,11 +13,6 @@ export interface InboundFiltersOptions { denyUrls: Array; ignoreErrors: Array; ignoreInternal: boolean; - - /** @deprecated use {@link InboundFiltersOptions.allowUrls} instead. */ - whitelistUrls: Array; - /** @deprecated use {@link InboundFiltersOptions.denyUrls} instead. */ - blacklistUrls: Array; } /** Inbound filters configurable by the user */ @@ -60,22 +55,8 @@ export function _mergeOptions( clientOptions: Partial = {}, ): Partial { return { - allowUrls: [ - // eslint-disable-next-line deprecation/deprecation - ...(internalOptions.whitelistUrls || []), - ...(internalOptions.allowUrls || []), - // eslint-disable-next-line deprecation/deprecation - ...(clientOptions.whitelistUrls || []), - ...(clientOptions.allowUrls || []), - ], - denyUrls: [ - // eslint-disable-next-line deprecation/deprecation - ...(internalOptions.blacklistUrls || []), - ...(internalOptions.denyUrls || []), - // eslint-disable-next-line deprecation/deprecation - ...(clientOptions.blacklistUrls || []), - ...(clientOptions.denyUrls || []), - ], + allowUrls: [...(internalOptions.allowUrls || []), ...(clientOptions.allowUrls || [])], + denyUrls: [...(internalOptions.denyUrls || []), ...(clientOptions.denyUrls || [])], ignoreErrors: [ ...(internalOptions.ignoreErrors || []), ...(clientOptions.ignoreErrors || []), From 67bbb65ea8f24865b0946368ccea8948f2101211 Mon Sep 17 00:00:00 2001 From: Abhijeet Prasad Date: Wed, 6 Apr 2022 21:14:50 -0400 Subject: [PATCH 005/204] ref(gatsby): Remove `Sentry` from window (#4857) --- packages/gatsby/gatsby-browser.js | 8 +++----- packages/gatsby/test/gatsby-browser.test.ts | 10 ---------- packages/gatsby/test/integration.test.tsx | 3 ++- 3 files changed, 5 insertions(+), 16 deletions(-) diff --git a/packages/gatsby/gatsby-browser.js b/packages/gatsby/gatsby-browser.js index 5cf1e3d26f80..598ce44ade08 100644 --- a/packages/gatsby/gatsby-browser.js +++ b/packages/gatsby/gatsby-browser.js @@ -6,12 +6,11 @@ exports.onClientEntry = function (_, pluginParams) { const areOptionsDefined = areSentryOptionsDefined(pluginParams); if (isIntialized) { - window.Sentry = Sentry; // For backwards compatibility if (areOptionsDefined) { console.warn( 'Sentry Logger [Warn]: The SDK was initialized in the Sentry config file, but options were found in the Gatsby config. ' + - 'These have been ignored, merge them to the Sentry config if you want to use them.\n' + - 'Learn more about the Gatsby SDK on https://docs.sentry.io/platforms/javascript/guides/gatsby/', + 'These have been ignored, merge them to the Sentry config if you want to use them.\n' + + 'Learn more about the Gatsby SDK on https://docs.sentry.io/platforms/javascript/guides/gatsby/', ); } return; @@ -20,7 +19,7 @@ exports.onClientEntry = function (_, pluginParams) { if (!areOptionsDefined) { console.error( 'Sentry Logger [Error]: No config for the Gatsby SDK was found.\n' + - 'Learn how to configure it on https://docs.sentry.io/platforms/javascript/guides/gatsby/', + 'Learn how to configure it on https://docs.sentry.io/platforms/javascript/guides/gatsby/', ); return; } @@ -32,7 +31,6 @@ exports.onClientEntry = function (_, pluginParams) { dsn: __SENTRY_DSN__, ...pluginParams, }); - window.Sentry = Sentry; // For backwards compatibility }; function isSentryInitialized() { diff --git a/packages/gatsby/test/gatsby-browser.test.ts b/packages/gatsby/test/gatsby-browser.test.ts index a3c98524a2fd..a2044dd2c8a9 100644 --- a/packages/gatsby/test/gatsby-browser.test.ts +++ b/packages/gatsby/test/gatsby-browser.test.ts @@ -36,10 +36,6 @@ describe('onClientEntry', () => { tracingAddExtensionMethods = jest.fn(); }); - afterEach(() => { - (window as any).Sentry = undefined; - }); - it.each([ [{}, ['dsn', 'release']], [{ key: 'value' }, ['dsn', 'release', 'key']], @@ -54,7 +50,6 @@ describe('onClientEntry', () => { describe('inits Sentry once', () => { afterEach(() => { - delete (window as any).Sentry; delete (window as any).__SENTRY__; (global.console.warn as jest.Mock).mockClear(); (global.console.error as jest.Mock).mockClear(); @@ -78,7 +73,6 @@ describe('onClientEntry', () => { // eslint-disable-next-line no-console expect(console.error).not.toHaveBeenCalled(); expect(sentryInit).not.toHaveBeenCalled(); - expect((window as any).Sentry).toBeDefined(); }); it('initialized in injected config, with pluginParams', () => { @@ -94,7 +88,6 @@ describe('onClientEntry', () => { // eslint-disable-next-line no-console expect(console.error).not.toHaveBeenCalled(); expect(sentryInit).not.toHaveBeenCalled(); - expect((window as any).Sentry).toBeDefined(); }); it('not initialized in injected config, without pluginParams', () => { @@ -108,7 +101,6 @@ describe('onClientEntry', () => { Learn how to configure it on https://docs.sentry.io/platforms/javascript/guides/gatsby/", ] `); - expect((window as any).Sentry).not.toBeDefined(); }); it('not initialized in injected config, with pluginParams', () => { @@ -125,7 +117,6 @@ describe('onClientEntry', () => { "release": "release", } `); - expect((window as any).Sentry).toBeDefined(); }); }); @@ -164,7 +155,6 @@ describe('onClientEntry', () => { it('does not run if plugin params are undefined', () => { onClientEntry(); expect(sentryInit).toHaveBeenCalledTimes(0); - expect((window as any).Sentry).toBeUndefined(); expect(tracingAddExtensionMethods).toHaveBeenCalledTimes(0); }); }); diff --git a/packages/gatsby/test/integration.test.tsx b/packages/gatsby/test/integration.test.tsx index e758aa90a79d..18c738ba2ac2 100644 --- a/packages/gatsby/test/integration.test.tsx +++ b/packages/gatsby/test/integration.test.tsx @@ -5,6 +5,7 @@ import { useEffect } from 'react'; import * as React from 'react'; import { onClientEntry } from '../gatsby-browser'; +import * as Sentry from '../src'; beforeAll(() => { (global as any).__SENTRY_RELEASE__ = '683f3a6ab819d47d23abfca9a914c81f0524d35b'; @@ -28,7 +29,7 @@ describe('useEffect', () => { function TestComponent() { useEffect(() => { const error = new Error('testing 123'); - (window as any).Sentry.captureException(error); + Sentry.captureException(error); }); return
Hello
; From c481f55483a361b0641cbb8731e137eb54bcad68 Mon Sep 17 00:00:00 2001 From: Abhijeet Prasad Date: Wed, 6 Apr 2022 21:15:35 -0400 Subject: [PATCH 006/204] ref(hub): Remove `getActiveDomain` (#4858) Removes `getActiveDomain` function and corresponding type. --- packages/hub/src/hub.ts | 23 ----------------------- packages/hub/src/index.ts | 4 ---- 2 files changed, 27 deletions(-) diff --git a/packages/hub/src/hub.ts b/packages/hub/src/hub.ts index b6ebffc74866..94c8e3ea91fe 100644 --- a/packages/hub/src/hub.ts +++ b/packages/hub/src/hub.ts @@ -80,15 +80,6 @@ export interface Carrier { }; } -/** - * @hidden - * @deprecated Can be removed once `Hub.getActiveDomain` is removed. - */ -export interface DomainAsCarrier extends Carrier { - // eslint-disable-next-line @typescript-eslint/no-explicit-any - members: { [key: string]: any }[]; -} - /** * @inheritDoc */ @@ -559,20 +550,6 @@ export function getCurrentHub(): Hub { return getHubFromCarrier(registry); } -/** - * Returns the active domain, if one exists - * @deprecated No longer used; remove in v7 - * @returns The domain, or undefined if there is no active domain - */ -// eslint-disable-next-line deprecation/deprecation -export function getActiveDomain(): DomainAsCarrier | undefined { - IS_DEBUG_BUILD && logger.warn('Function `getActiveDomain` is deprecated and will be removed in a future version.'); - - const sentry = getMainCarrier().__SENTRY__; - - return sentry && sentry.extensions && sentry.extensions.domain && sentry.extensions.domain.active; -} - /** * Try to read the hub from an active domain, and fallback to the registry if one doesn't exist * @returns discovered hub diff --git a/packages/hub/src/index.ts b/packages/hub/src/index.ts index 9c0a77625dc2..3d3b97fa239b 100644 --- a/packages/hub/src/index.ts +++ b/packages/hub/src/index.ts @@ -2,8 +2,6 @@ export { addGlobalEventProcessor, Scope } from './scope'; export { Session } from './session'; export { SessionFlusher } from './sessionflusher'; export { - // eslint-disable-next-line deprecation/deprecation - getActiveDomain, getCurrentHub, getHubFromCarrier, getMainCarrier, @@ -11,7 +9,5 @@ export { makeMain, setHubOnCarrier, Carrier, - // eslint-disable-next-line deprecation/deprecation - DomainAsCarrier, Layer, } from './hub'; From 8ed1e9e2b9487fbefb45c422e84df1d198c7ad31 Mon Sep 17 00:00:00 2001 From: Abhijeet Prasad Date: Wed, 6 Apr 2022 21:17:36 -0400 Subject: [PATCH 007/204] ref(types): Remove deprecated `user` dsn field (#4864) The `user` dsn component was renamed to `publicKey`. This PR removes that from the dsn field. --- packages/types/src/dsn.ts | 2 -- packages/utils/src/dsn.ts | 6 ------ 2 files changed, 8 deletions(-) diff --git a/packages/types/src/dsn.ts b/packages/types/src/dsn.ts index b21130802903..761c52889caf 100644 --- a/packages/types/src/dsn.ts +++ b/packages/types/src/dsn.ts @@ -5,8 +5,6 @@ export type DsnProtocol = 'http' | 'https'; export interface DsnComponents { /** Protocol used to connect to Sentry. */ protocol: DsnProtocol; - /** Public authorization key (deprecated, renamed to publicKey). */ - user?: string; /** Public authorization key. */ publicKey?: string; /** Private authorization key (deprecated, optional). */ diff --git a/packages/utils/src/dsn.ts b/packages/utils/src/dsn.ts index 0b58a7974821..5c500c5ec654 100644 --- a/packages/utils/src/dsn.ts +++ b/packages/utils/src/dsn.ts @@ -55,13 +55,7 @@ function dsnFromString(str: string): DsnComponents { } function dsnFromComponents(components: DsnComponents): DsnComponents { - // TODO this is for backwards compatibility, and can be removed in a future version - if ('user' in components && !('publicKey' in components)) { - components.publicKey = components.user; - } - return { - user: components.publicKey || '', protocol: components.protocol, publicKey: components.publicKey || '', pass: components.pass || '', From ba4561c550fda2f628991479c70abb14435f6bcc Mon Sep 17 00:00:00 2001 From: Abhijeet Prasad Date: Wed, 6 Apr 2022 21:18:08 -0400 Subject: [PATCH 008/204] ref(hub): Remove `setTransaction` scope method (#4865) --- packages/hub/src/scope.ts | 8 -------- 1 file changed, 8 deletions(-) diff --git a/packages/hub/src/scope.ts b/packages/hub/src/scope.ts index 9243cd409b66..09dd244734ee 100644 --- a/packages/hub/src/scope.ts +++ b/packages/hub/src/scope.ts @@ -223,14 +223,6 @@ export class Scope implements ScopeInterface { return this; } - /** - * Can be removed in major version. - * @deprecated in favor of {@link this.setTransactionName} - */ - public setTransaction(name?: string): this { - return this.setTransactionName(name); - } - /** * @inheritDoc */ From ebff7193b89dfa9a46b9dce1e005c5346015e702 Mon Sep 17 00:00:00 2001 From: Abhijeet Prasad Date: Wed, 6 Apr 2022 21:19:14 -0400 Subject: [PATCH 009/204] ref: Drop support for Node 6 (#4851) --- .github/workflows/build.yml | 2 +- packages/angular/package.json | 2 +- packages/browser/package.json | 2 +- packages/core/package.json | 2 +- packages/eslint-config-sdk/package.json | 2 +- packages/eslint-plugin-sdk/package.json | 2 +- packages/gatsby/package.json | 2 +- packages/hub/package.json | 2 +- packages/integrations/package.json | 2 +- packages/minimal/package.json | 2 +- packages/nextjs/package.json | 2 +- packages/node/package.json | 2 +- packages/node/test/integrations/http.test.ts | 4 +--- packages/react/package.json | 2 +- packages/tracing/package.json | 2 +- packages/types/package.json | 2 +- packages/utils/package.json | 2 +- packages/vue/package.json | 2 +- packages/wasm/package.json | 2 +- scripts/test.ts | 16 +--------------- 20 files changed, 20 insertions(+), 36 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index a8f026b2729f..368412a4515c 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -217,7 +217,7 @@ jobs: runs-on: ubuntu-latest strategy: matrix: - node: [6, 8, 10, 12, 14, 16] + node: [8, 10, 12, 14, 16] steps: - name: Check out current commit (${{ env.HEAD_COMMIT }}) uses: actions/checkout@v2 diff --git a/packages/angular/package.json b/packages/angular/package.json index ea99649e13eb..7a67fb0c4c8c 100644 --- a/packages/angular/package.json +++ b/packages/angular/package.json @@ -7,7 +7,7 @@ "author": "Sentry", "license": "MIT", "engines": { - "node": ">=6" + "node": ">=8" }, "main": "dist/index.js", "module": "esm/index.js", diff --git a/packages/browser/package.json b/packages/browser/package.json index 3ac30884c1f8..ab069d2d03df 100644 --- a/packages/browser/package.json +++ b/packages/browser/package.json @@ -7,7 +7,7 @@ "author": "Sentry", "license": "BSD-3-Clause", "engines": { - "node": ">=6" + "node": ">=8" }, "main": "build/npm/dist/index.js", "module": "build/npm/esm/index.js", diff --git a/packages/core/package.json b/packages/core/package.json index 7a1e0a8f38d6..db96208eea0d 100644 --- a/packages/core/package.json +++ b/packages/core/package.json @@ -7,7 +7,7 @@ "author": "Sentry", "license": "BSD-3-Clause", "engines": { - "node": ">=6" + "node": ">=8" }, "main": "build/dist/index.js", "module": "build/esm/index.js", diff --git a/packages/eslint-config-sdk/package.json b/packages/eslint-config-sdk/package.json index b9a467c926d2..f252027ee2c3 100644 --- a/packages/eslint-config-sdk/package.json +++ b/packages/eslint-config-sdk/package.json @@ -12,7 +12,7 @@ "sentry" ], "engines": { - "node": ">=6" + "node": ">=8" }, "main": "src/index.js", "publishConfig": { diff --git a/packages/eslint-plugin-sdk/package.json b/packages/eslint-plugin-sdk/package.json index 633b31552efa..ae823048fef2 100644 --- a/packages/eslint-plugin-sdk/package.json +++ b/packages/eslint-plugin-sdk/package.json @@ -12,7 +12,7 @@ "sentry" ], "engines": { - "node": ">=6" + "node": ">=8" }, "main": "src/index.js", "publishConfig": { diff --git a/packages/gatsby/package.json b/packages/gatsby/package.json index 1151153161fb..08289e6765ca 100644 --- a/packages/gatsby/package.json +++ b/packages/gatsby/package.json @@ -11,7 +11,7 @@ "gatsby-plugin" ], "engines": { - "node": ">=6" + "node": ">=8" }, "main": "build/dist/index.js", "module": "build/esm/index.js", diff --git a/packages/hub/package.json b/packages/hub/package.json index f194b7c7513d..39feacea908a 100644 --- a/packages/hub/package.json +++ b/packages/hub/package.json @@ -7,7 +7,7 @@ "author": "Sentry", "license": "BSD-3-Clause", "engines": { - "node": ">=6" + "node": ">=8" }, "main": "build/dist/index.js", "module": "build/esm/index.js", diff --git a/packages/integrations/package.json b/packages/integrations/package.json index 335ca2f3b9ab..41fc646e72e6 100644 --- a/packages/integrations/package.json +++ b/packages/integrations/package.json @@ -7,7 +7,7 @@ "author": "Sentry", "license": "BSD-3-Clause", "engines": { - "node": ">=6" + "node": ">=8" }, "publishConfig": { "access": "public" diff --git a/packages/minimal/package.json b/packages/minimal/package.json index 2ae39401d7b2..e91af98cf40f 100644 --- a/packages/minimal/package.json +++ b/packages/minimal/package.json @@ -7,7 +7,7 @@ "author": "Sentry", "license": "BSD-3-Clause", "engines": { - "node": ">=6" + "node": ">=8" }, "main": "build/dist/index.js", "module": "build/esm/index.js", diff --git a/packages/nextjs/package.json b/packages/nextjs/package.json index 760833113817..f5305887fb28 100644 --- a/packages/nextjs/package.json +++ b/packages/nextjs/package.json @@ -7,7 +7,7 @@ "author": "Sentry", "license": "MIT", "engines": { - "node": ">=6" + "node": ">=8" }, "main": "build/dist/index.server.js", "module": "build/esm/index.server.js", diff --git a/packages/node/package.json b/packages/node/package.json index f7c3e63b5f48..aa613076c62e 100644 --- a/packages/node/package.json +++ b/packages/node/package.json @@ -7,7 +7,7 @@ "author": "Sentry", "license": "BSD-3-Clause", "engines": { - "node": ">=6" + "node": ">=8" }, "main": "build/dist/index.js", "module": "build/esm/index.js", diff --git a/packages/node/test/integrations/http.test.ts b/packages/node/test/integrations/http.test.ts index c7a9c3e0a621..bc71b994c7a5 100644 --- a/packages/node/test/integrations/http.test.ts +++ b/packages/node/test/integrations/http.test.ts @@ -44,9 +44,7 @@ describe('tracing', () => { http.get('http://dogs.are.great/'); - // TODO: For some reason in node 6 two request spans are appearing. Once we stop testing against it, this can go - // back to being `toEqual()`. - expect(spans.length).toBeGreaterThanOrEqual(2); + expect(spans.length).toEqual(2); // our span is at index 1 because the transaction itself is at index 0 expect(spans[1].description).toEqual('GET http://dogs.are.great/'); diff --git a/packages/react/package.json b/packages/react/package.json index ace7013232e1..1fdda308f5a7 100644 --- a/packages/react/package.json +++ b/packages/react/package.json @@ -7,7 +7,7 @@ "author": "Sentry", "license": "BSD-3-Clause", "engines": { - "node": ">=6" + "node": ">=8" }, "main": "build/dist/index.js", "module": "build/esm/index.js", diff --git a/packages/tracing/package.json b/packages/tracing/package.json index 64f3e83b9cea..caa6bdf93121 100644 --- a/packages/tracing/package.json +++ b/packages/tracing/package.json @@ -7,7 +7,7 @@ "author": "Sentry", "license": "MIT", "engines": { - "node": ">=6" + "node": ">=8" }, "main": "build/npm/dist/index.js", "module": "build/npm/esm/index.js", diff --git a/packages/types/package.json b/packages/types/package.json index 84c43bb53198..d5d7c03cb6ad 100644 --- a/packages/types/package.json +++ b/packages/types/package.json @@ -7,7 +7,7 @@ "author": "Sentry", "license": "BSD-3-Clause", "engines": { - "node": ">=6" + "node": ">=8" }, "main": "build/dist/index.js", "module": "build/esm/index.js", diff --git a/packages/utils/package.json b/packages/utils/package.json index c104c67ba5ff..08525cc8a84a 100644 --- a/packages/utils/package.json +++ b/packages/utils/package.json @@ -7,7 +7,7 @@ "author": "Sentry", "license": "BSD-3-Clause", "engines": { - "node": ">=6" + "node": ">=8" }, "main": "build/dist/index.js", "module": "build/esm/index.js", diff --git a/packages/vue/package.json b/packages/vue/package.json index f14ae53120cd..1ff530963569 100644 --- a/packages/vue/package.json +++ b/packages/vue/package.json @@ -7,7 +7,7 @@ "author": "Sentry", "license": "MIT", "engines": { - "node": ">=6" + "node": ">=8" }, "main": "build/dist/index.js", "module": "build/esm/index.js", diff --git a/packages/wasm/package.json b/packages/wasm/package.json index c4e7ecd270a3..b0252e54f27b 100644 --- a/packages/wasm/package.json +++ b/packages/wasm/package.json @@ -7,7 +7,7 @@ "author": "Sentry", "license": "MIT", "engines": { - "node": ">=6" + "node": ">=8" }, "main": "build/npm/dist/index.js", "module": "build/npm/esm/index.js", diff --git a/scripts/test.ts b/scripts/test.ts index 6c2efd8a1b68..51a1dc06245e 100644 --- a/scripts/test.ts +++ b/scripts/test.ts @@ -11,21 +11,7 @@ function run(cmd: string, cwd: string = '') { const nodeMajorVersion = parseInt(process.version.split('.')[0].replace('v', ''), 10); -// control which packages we test on each version of node -if (nodeMajorVersion <= 6) { - // install legacy versions of packages whose current versions don't support node 6 - // ignoring engines and scripts lets us get away with having incompatible things installed for packages we're not testing - run('yarn add --dev --ignore-engines --ignore-scripts nock@10.x', 'packages/node'); - run('yarn add --dev --ignore-engines --ignore-scripts jsdom@11.x', 'packages/tracing'); - run('yarn add --dev --ignore-engines --ignore-scripts jsdom@11.x', 'packages/utils'); - - // only test against @sentry/node and its dependencies - node 6 is too old for anything else to work - const scope = ['@sentry/core', '@sentry/hub', '@sentry/minimal', '@sentry/node', '@sentry/utils', '@sentry/tracing'] - .map(dep => `--scope="${dep}"`) - .join(' '); - - run(`yarn test ${scope}`); -} else if (nodeMajorVersion <= 8) { +if (nodeMajorVersion <= 8) { // install legacy versions of packages whose current versions don't support node 8 // ignoring engines and scripts lets us get away with having incompatible things installed for packages we're not testing run('yarn add --dev --ignore-engines --ignore-scripts jsdom@15.x', 'packages/tracing'); From 5a050eeadb57c5489afb05f776d681ab69fe93ce Mon Sep 17 00:00:00 2001 From: Abhijeet Prasad Date: Wed, 6 Apr 2022 21:16:08 -0400 Subject: [PATCH 010/204] ref(tracing): Rename `registerRequestInstrumentation` -> `instrumentOutgoingRequests` (#4859) --- packages/tracing/src/index.ts | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/packages/tracing/src/index.ts b/packages/tracing/src/index.ts index 89e3b3ba92d5..dcfea00f422a 100644 --- a/packages/tracing/src/index.ts +++ b/packages/tracing/src/index.ts @@ -26,8 +26,7 @@ export { Span, SpanStatusType, spanStatusfromHttpCode } from './span'; export { SpanStatus } from './spanstatus'; export { Transaction } from './transaction'; export { - // TODO deprecate old name in v7 - instrumentOutgoingRequests as registerRequestInstrumentation, + instrumentOutgoingRequests, RequestInstrumentationOptions, defaultRequestInstrumentationOptions, } from './browser'; From bd509d0872ad9ef42f4b31a0d8a1748883460e36 Mon Sep 17 00:00:00 2001 From: Lukas Stracke Date: Thu, 7 Apr 2022 14:26:42 +0200 Subject: [PATCH 011/204] fix(test): Increase MongoMemoryServer creation timeout (#4881) Increases the creation timeout of `MongoMemoryServer` to temporarily fix Node integration test flakiness --- .../suites/tracing/auto-instrument/mongodb/test.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/node-integration-tests/suites/tracing/auto-instrument/mongodb/test.ts b/packages/node-integration-tests/suites/tracing/auto-instrument/mongodb/test.ts index d118a03261a5..dd5f88f860ca 100644 --- a/packages/node-integration-tests/suites/tracing/auto-instrument/mongodb/test.ts +++ b/packages/node-integration-tests/suites/tracing/auto-instrument/mongodb/test.ts @@ -8,7 +8,7 @@ conditionalTest({ min: 12 })('MongoDB Test', () => { beforeAll(async () => { mongoServer = await MongoMemoryServer.create(); process.env.MONGO_URL = mongoServer.getUri(); - }, 30000); + }, 40000); afterAll(async () => { await mongoServer.stop(); From 729aced0740ff0d2fbd15cc66a7ffc1b7eb59d87 Mon Sep 17 00:00:00 2001 From: Tim Fish Date: Thu, 7 Apr 2022 16:01:27 +0100 Subject: [PATCH 012/204] ref(node): Remove deprecated `frameContextLines` (#4884) This PR removes the previously deprecated `frameContextLines` from `NodeOptions`. --- packages/node/src/integrations/contextlines.ts | 13 ------------- packages/node/src/types.ts | 15 --------------- 2 files changed, 28 deletions(-) diff --git a/packages/node/src/integrations/contextlines.ts b/packages/node/src/integrations/contextlines.ts index e18768b2fe3d..cad196342f28 100644 --- a/packages/node/src/integrations/contextlines.ts +++ b/packages/node/src/integrations/contextlines.ts @@ -1,11 +1,8 @@ -import { getCurrentHub } from '@sentry/core'; import { Event, EventProcessor, Integration, StackFrame } from '@sentry/types'; import { addContextToFrame } from '@sentry/utils'; import { readFile } from 'fs'; import { LRUMap } from 'lru_map'; -import { NodeClient } from '../client'; - const FILE_CONTENT_CACHE = new LRUMap(100); const DEFAULT_LINES_OF_CONTEXT = 7; @@ -53,16 +50,6 @@ export class ContextLines implements Integration { /** Get's the number of context lines to add */ private get _contextLines(): number { - // This is only here to copy frameContextLines from init options if it hasn't - // been set via this integrations constructor. - // - // TODO: Remove on next major! - if (this._options.frameContextLines === undefined) { - const initOptions = getCurrentHub().getClient()?.getOptions(); - // eslint-disable-next-line deprecation/deprecation - this._options.frameContextLines = initOptions?.frameContextLines; - } - return this._options.frameContextLines !== undefined ? this._options.frameContextLines : DEFAULT_LINES_OF_CONTEXT; } diff --git a/packages/node/src/types.ts b/packages/node/src/types.ts index 6c6651bc7b88..055006a47e9e 100644 --- a/packages/node/src/types.ts +++ b/packages/node/src/types.ts @@ -20,21 +20,6 @@ export interface NodeOptions extends Options { /** HTTPS proxy certificates path */ caCerts?: string; - /** - * Sets the number of context lines for each frame when loading a file. - * - * @deprecated Context lines configuration has moved to the `ContextLines` integration, and can be used like this: - * - * ``` - * init({ - * dsn: '__DSN__', - * integrations: [new ContextLines({ frameContextLines: 10 })] - * }) - * ``` - * - * */ - frameContextLines?: number; - /** Callback that is executed when a fatal global error occurs. */ onFatalError?(error: Error): void; } From 0604aef18b653baf087107672daac5252be57ace Mon Sep 17 00:00:00 2001 From: Tim Fish Date: Thu, 7 Apr 2022 17:17:31 +0100 Subject: [PATCH 013/204] ref(browser): Remove top level eventbuilder exports (#4887) These exports were historically used in `@sentry/electron`, but are no longer being used by the Electron SDK or the React Native SDK, so they can be removed. --- packages/browser/src/exports.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/packages/browser/src/exports.ts b/packages/browser/src/exports.ts index 86ace0abbdd5..b4dd4374a7b3 100644 --- a/packages/browser/src/exports.ts +++ b/packages/browser/src/exports.ts @@ -44,6 +44,5 @@ export { export { BrowserOptions } from './backend'; export { BrowserClient } from './client'; export { injectReportDialog, ReportDialogOptions } from './helpers'; -export { eventFromException, eventFromMessage } from './eventbuilder'; export { defaultIntegrations, forceLoad, init, lastEventId, onLoad, showReportDialog, flush, close, wrap } from './sdk'; export { SDK_NAME } from './version'; From fc6aa54c3dd3dd241eeb9f4351d624a19fca41ea Mon Sep 17 00:00:00 2001 From: Katie Byers Date: Thu, 7 Apr 2022 16:20:52 -0700 Subject: [PATCH 014/204] ref(build): Update to TypeScript 3.8.3 (#4895) This updates the SDK to use Typescript 3.8.3, in order to be able to use type-only imports and exports[1]. (These are needed for us to turn on `isolatedModules`, which is in turn is needed for us to switch our build tool away from `tsc`[2], since no other tool understands the relationship between files.) As a result of this change, a few of the browser integration tests needed to be fixed so that all promises were explicitly awaited, a point about which the linter in 3.8 complains. This is a breaking change for anyone using TS 3.7.x (there's no one using TS < 3.7.x, since that's our current minimum). That said, though there are plenty of public projects on GH using TS 3.7 and `@sentry/xyz`, if you restrict it to projects using TS 3.7 and `@sentry/xyz` 6.x, all you get are forks of this very repo. Granted, this isn't every project ever, but it's likely decently representative of the fact that if you've upgraded our SDK, you've almost certainly upgraded TS as well. We're going to wait until v7 to release this change in any case, but that's an indication that it won't affect many people when we do. (See this commit's PR for links to searches demonstrating the points.) Note: Originally there was some thought of going farther, into TS 4.x, but we decided that for the foreseeable future, we're going to stick with 3.8.3. Though moving up to 4.x doesn't seem like it would affect many customers (repeating the same TS/sentry 6.x crossover search with TS 3.8 and 3.9 reveals a total of 5 projects), it does have the known side effect of replacing export statements which, after compilation, currently look like `exports.SdkInfo = types_1.SdkInfo;` with ones that look like `Object.defineProperty(exports, "SdkInfo", { enumerable: true, get: function () { return types_1.SdkInfo; } });`. (For those of you following along at home, that's a 3x character increase.) Though we might be able to engineer around this in order to avoid the inevitable substantial bundle size hit, attempting to do so is only worth it if there are features from 4.x that we desperately need. Given that we've agreed that right now there aren't, we'll stick with 3.8.3. [1] https://www.typescriptlang.org/docs/handbook/release-notes/typescript-3-8.html#type-only-imports-and-export [2] https://www.typescriptlang.org/tsconfig#isolatedModules --- package.json | 2 +- .../tracing/browsertracing/backgroundtab-pageload/test.ts | 2 +- .../suites/tracing/metrics/web-vitals-fid/test.ts | 4 ++-- .../suites/tracing/metrics/web-vitals-lcp/test.ts | 4 ++-- packages/typescript/package.json | 2 +- scripts/verify-packages-versions.js | 2 +- yarn.lock | 8 ++++---- 7 files changed, 12 insertions(+), 12 deletions(-) diff --git a/package.json b/package.json index 14d73feddc03..e1f42542b33d 100644 --- a/package.json +++ b/package.json @@ -89,7 +89,7 @@ "ts-node": "^8.10.2", "tslib": "^2.3.1", "typedoc": "^0.18.0", - "typescript": "3.7.5" + "typescript": "3.8.3" }, "resolutions": { "**/agent-base": "5", diff --git a/packages/integration-tests/suites/tracing/browsertracing/backgroundtab-pageload/test.ts b/packages/integration-tests/suites/tracing/browsertracing/backgroundtab-pageload/test.ts index 42787cbb4819..47f695b0440c 100644 --- a/packages/integration-tests/suites/tracing/browsertracing/backgroundtab-pageload/test.ts +++ b/packages/integration-tests/suites/tracing/browsertracing/backgroundtab-pageload/test.ts @@ -9,7 +9,7 @@ sentryTest('should finish pageload transaction when the page goes background', a await page.goto(url); - page.click('#go-background'); + void page.click('#go-background'); const pageloadTransaction = await getFirstSentryEnvelopeRequest(page); diff --git a/packages/integration-tests/suites/tracing/metrics/web-vitals-fid/test.ts b/packages/integration-tests/suites/tracing/metrics/web-vitals-fid/test.ts index ed3a86676867..0eaa60a75031 100644 --- a/packages/integration-tests/suites/tracing/metrics/web-vitals-fid/test.ts +++ b/packages/integration-tests/suites/tracing/metrics/web-vitals-fid/test.ts @@ -12,9 +12,9 @@ sentryTest('should capture a FID vital.', async ({ browserName, getLocalTestPath const url = await getLocalTestPath({ testDir: __dirname }); - page.goto(url); + await page.goto(url); // To trigger FID - page.click('#fid-btn'); + await page.click('#fid-btn'); const eventData = await getFirstSentryEnvelopeRequest(page); diff --git a/packages/integration-tests/suites/tracing/metrics/web-vitals-lcp/test.ts b/packages/integration-tests/suites/tracing/metrics/web-vitals-lcp/test.ts index 2eb554013280..32f86d3c54d1 100644 --- a/packages/integration-tests/suites/tracing/metrics/web-vitals-lcp/test.ts +++ b/packages/integration-tests/suites/tracing/metrics/web-vitals-lcp/test.ts @@ -14,10 +14,10 @@ sentryTest('should capture a LCP vital with element details.', async ({ browserN ); const url = await getLocalTestPath({ testDir: __dirname }); - page.goto(url); + await page.goto(url); // Force closure of LCP listener. - page.click('body'); + await page.click('body'); const eventData = await getFirstSentryEnvelopeRequest(page); expect(eventData.measurements).toBeDefined(); diff --git a/packages/typescript/package.json b/packages/typescript/package.json index 2156c40a9c2f..2b0ae182cd4c 100644 --- a/packages/typescript/package.json +++ b/packages/typescript/package.json @@ -16,7 +16,7 @@ }, "peerDependencies": { "tslint": "5.16.0", - "typescript": "3.7.5" + "typescript": "3.8.3" }, "scripts": { "link:yarn": "yarn link", diff --git a/scripts/verify-packages-versions.js b/scripts/verify-packages-versions.js index fb6a6e95ec7f..e0efc39311bc 100644 --- a/scripts/verify-packages-versions.js +++ b/scripts/verify-packages-versions.js @@ -1,6 +1,6 @@ const pkg = require('../package.json'); -const TYPESCRIPT_VERSION = '3.7.5'; +const TYPESCRIPT_VERSION = '3.8.3'; if (pkg.devDependencies.typescript !== TYPESCRIPT_VERSION) { console.error(` diff --git a/yarn.lock b/yarn.lock index 81cd3f64044a..26947533ab88 100644 --- a/yarn.lock +++ b/yarn.lock @@ -21523,10 +21523,10 @@ typescript-memoize@^1.0.1: resolved "https://registry.yarnpkg.com/typescript-memoize/-/typescript-memoize-1.0.1.tgz#0a8199aa28f6fe18517f6e9308ef7bfbe9a98d59" integrity sha512-oJNge1qUrOK37d5Y6Ly2txKeuelYVsFtNF6U9kXIN7juudcQaHJQg2MxLOy0CqtkW65rVDYuTCOjnSIVPd8z3w== -typescript@3.7.5: - version "3.7.5" - resolved "https://registry.yarnpkg.com/typescript/-/typescript-3.7.5.tgz#0692e21f65fd4108b9330238aac11dd2e177a1ae" - integrity sha512-/P5lkRXkWHNAbcJIiHPfRoKqyd7bsyCma1hZNUGfn20qm64T6ZBlrzprymeu918H+mB/0rIg2gGK/BXkhhYgBw== +typescript@3.8.3: + version "3.8.3" + resolved "https://registry.yarnpkg.com/typescript/-/typescript-3.8.3.tgz#409eb8544ea0335711205869ec458ab109ee1061" + integrity sha512-MYlEfn5VrLNsgudQTVJeNaQFUAI7DkhnOjdpAp4T+ku1TfQClewlbSuTVHiA+8skNBgaf02TL/kLOvig4y3G8w== typescript@^3.9.5, typescript@^3.9.7: version "3.9.9" From 804640ddde906daa0358e48a772bc31b70a00639 Mon Sep 17 00:00:00 2001 From: Luca Forstner Date: Fri, 8 Apr 2022 15:01:10 +0200 Subject: [PATCH 015/204] ref(bundles): Stop publishing CDN bundles on npm (#4901) --- .npmignore | 3 --- packages/minimal/.npmignore | 3 --- packages/node/.npmignore | 3 --- packages/tracing/.npmignore | 3 --- packages/types/.npmignore | 3 --- packages/utils/.npmignore | 3 --- packages/wasm/package.json | 2 +- scripts/prepack.ts | 21 --------------------- 8 files changed, 1 insertion(+), 40 deletions(-) diff --git a/.npmignore b/.npmignore index 4ab7cc4f278f..facb3a101f90 100644 --- a/.npmignore +++ b/.npmignore @@ -3,9 +3,6 @@ * -# TODO remove bundles (which in the tarball are inside `build`) in v7 -!/build/**/* - !/dist/**/* !/esm/**/* !/types/**/* diff --git a/packages/minimal/.npmignore b/packages/minimal/.npmignore index 4ab7cc4f278f..facb3a101f90 100644 --- a/packages/minimal/.npmignore +++ b/packages/minimal/.npmignore @@ -3,9 +3,6 @@ * -# TODO remove bundles (which in the tarball are inside `build`) in v7 -!/build/**/* - !/dist/**/* !/esm/**/* !/types/**/* diff --git a/packages/node/.npmignore b/packages/node/.npmignore index 4ab7cc4f278f..facb3a101f90 100644 --- a/packages/node/.npmignore +++ b/packages/node/.npmignore @@ -3,9 +3,6 @@ * -# TODO remove bundles (which in the tarball are inside `build`) in v7 -!/build/**/* - !/dist/**/* !/esm/**/* !/types/**/* diff --git a/packages/tracing/.npmignore b/packages/tracing/.npmignore index 4ab7cc4f278f..facb3a101f90 100644 --- a/packages/tracing/.npmignore +++ b/packages/tracing/.npmignore @@ -3,9 +3,6 @@ * -# TODO remove bundles (which in the tarball are inside `build`) in v7 -!/build/**/* - !/dist/**/* !/esm/**/* !/types/**/* diff --git a/packages/types/.npmignore b/packages/types/.npmignore index 4ab7cc4f278f..facb3a101f90 100644 --- a/packages/types/.npmignore +++ b/packages/types/.npmignore @@ -3,9 +3,6 @@ * -# TODO remove bundles (which in the tarball are inside `build`) in v7 -!/build/**/* - !/dist/**/* !/esm/**/* !/types/**/* diff --git a/packages/utils/.npmignore b/packages/utils/.npmignore index 4ab7cc4f278f..facb3a101f90 100644 --- a/packages/utils/.npmignore +++ b/packages/utils/.npmignore @@ -3,9 +3,6 @@ * -# TODO remove bundles (which in the tarball are inside `build`) in v7 -!/build/**/* - !/dist/**/* !/esm/**/* !/types/**/* diff --git a/packages/wasm/package.json b/packages/wasm/package.json index b0252e54f27b..0cd18104f8ce 100644 --- a/packages/wasm/package.json +++ b/packages/wasm/package.json @@ -43,7 +43,7 @@ "build:es5:watch": "yarn build:cjs:watch # *** backwards compatibility - remove in v7 ***", "build:esm:watch": "tsc -p tsconfig.esm.json --watch", "build:types:watch": "tsc -p tsconfig.types.json --watch", - "build:npm": "ts-node ../../scripts/prepack.ts --bundles --skipBundleCopy && npm pack ./build/npm", + "build:npm": "ts-node ../../scripts/prepack.ts --bundles && npm pack ./build/npm", "circularDepCheck": "madge --circular src/index.ts", "clean": "rimraf dist esm build coverage *.js.map *.d.ts", "fix": "run-s fix:eslint fix:prettier", diff --git a/scripts/prepack.ts b/scripts/prepack.ts index bac7a544a2f5..a7609a8087bd 100644 --- a/scripts/prepack.ts +++ b/scripts/prepack.ts @@ -7,7 +7,6 @@ */ import * as fs from 'fs'; -import * as fse from 'fs-extra'; import * as path from 'path'; const NPM_BUILD_DIR = 'build/npm'; @@ -49,26 +48,6 @@ ASSETS.forEach(asset => { } }); -// TODO remove in v7! Until then: -// copy CDN bundles into npm dir to temporarily keep bundles in npm tarball -// inside the tarball, they are located in `build/` -// for now, copy it by default, unless explicitly forbidden via an command line arg -const tmpCopyBundles = packageWithBundles && !process.argv.includes('--skipBundleCopy'); -if (tmpCopyBundles) { - const npmTmpBundlesPath = path.resolve(buildDir, 'build'); - const cdnBundlesPath = path.resolve('build', 'bundles'); - try { - if (!fs.existsSync(npmTmpBundlesPath)) { - fs.mkdirSync(npmTmpBundlesPath); - } - void fse.copy(cdnBundlesPath, npmTmpBundlesPath); - } catch (error) { - console.error(`Error while tmp copying CDN bundles to ${buildDir}`); - process.exit(1); - } -} -// end remove - // package.json modifications const packageJsonPath = path.resolve(buildDir, 'package.json'); // eslint-disable-next-line @typescript-eslint/no-var-requires From c64ca5e854462f12bf145820c6d0dfd3c5b2b868 Mon Sep 17 00:00:00 2001 From: Abhijeet Prasad Date: Fri, 8 Apr 2022 09:59:43 -0400 Subject: [PATCH 016/204] ref(types): Delete `RequestSessionStatus` enum (#4889) --- packages/types/src/requestsessionstatus.ts | 11 ----------- 1 file changed, 11 deletions(-) delete mode 100644 packages/types/src/requestsessionstatus.ts diff --git a/packages/types/src/requestsessionstatus.ts b/packages/types/src/requestsessionstatus.ts deleted file mode 100644 index b6b7ab9ee659..000000000000 --- a/packages/types/src/requestsessionstatus.ts +++ /dev/null @@ -1,11 +0,0 @@ -/** JSDoc - * @deprecated Use string literals - if you require type casting, cast to RequestSessionStatus type - */ -export enum RequestSessionStatus { - /** JSDoc */ - Ok = 'ok', - /** JSDoc */ - Errored = 'errored', - /** JSDoc */ - Crashed = 'crashed', -} From d096b021618ad916df908461c6edea4f5a4e81c3 Mon Sep 17 00:00:00 2001 From: Abhijeet Prasad Date: Fri, 8 Apr 2022 10:00:02 -0400 Subject: [PATCH 017/204] ref(types): Delete Status enum (#4891) --- packages/types/src/status.ts | 17 ----------------- 1 file changed, 17 deletions(-) delete mode 100644 packages/types/src/status.ts diff --git a/packages/types/src/status.ts b/packages/types/src/status.ts deleted file mode 100644 index a80cd00b97e7..000000000000 --- a/packages/types/src/status.ts +++ /dev/null @@ -1,17 +0,0 @@ -/** JSDoc - * @deprecated Use string literals - if you require type casting, cast to EventStatus type - */ -export enum Status { - /** The status could not be determined. */ - Unknown = 'unknown', - /** The event was skipped due to configuration or callbacks. */ - Skipped = 'skipped', - /** The event was sent to Sentry successfully. */ - Success = 'success', - /** The client is currently rate limited and will try again later. */ - RateLimit = 'rate_limit', - /** The event could not be processed. */ - Invalid = 'invalid', - /** A server-side error occurred during submission. */ - Failed = 'failed', -} From 9b1dfdfeae2948aee0e438a0e8f2298bc04b43b9 Mon Sep 17 00:00:00 2001 From: Lukas Stracke Date: Fri, 8 Apr 2022 17:12:20 +0200 Subject: [PATCH 018/204] ref(build): Rename `dist` directories to `cjs` (#4900) rename the dist directories in all build dirs to cjs. Hence, also the tarballs' structure changes which is why this PR also introduces a breaking change. Additional change: cleanup `yarn clean` commands by removing no longer existing directories --- .eslintrc.js | 11 ++++- .github/workflows/build.yml | 2 +- .gitignore | 2 +- .npmignore | 2 +- .vscode/launch.json | 49 +++++++------------ packages/angular/.npmignore | 2 +- packages/angular/package.json | 4 +- packages/angular/tsconfig.cjs.json | 2 +- packages/browser/package.json | 2 +- packages/browser/test/package/test-code.js | 4 +- packages/browser/tsconfig.cjs.json | 2 +- packages/core/package.json | 4 +- packages/core/tsconfig.cjs.json | 2 +- packages/gatsby/.npmignore | 2 +- packages/gatsby/package.json | 4 +- packages/gatsby/tsconfig.cjs.json | 2 +- packages/hub/package.json | 4 +- packages/hub/tsconfig.cjs.json | 2 +- .../integration-tests/utils/generatePlugin.ts | 4 +- packages/integrations/package.json | 4 +- packages/integrations/tsconfig.cjs.json | 2 +- packages/minimal/.npmignore | 2 +- packages/minimal/package.json | 4 +- packages/minimal/tsconfig.cjs.json | 2 +- packages/nextjs/package.json | 6 +-- packages/nextjs/tsconfig.cjs.json | 2 +- .../vercel/install-sentry-from-branch.sh | 2 +- packages/node/.npmignore | 2 +- packages/node/package.json | 4 +- .../node/test/manual/apm-transaction/main.js | 2 +- .../manual/express-scope-separation/start.js | 2 +- .../test/manual/memory-leak/context-memory.js | 2 +- .../manual/memory-leak/express-patient.js | 2 +- .../aggregates-disable-single-session.js | 2 +- .../caught-exception-errored-session.js | 2 +- .../errors-in-session-capped-to-one.js | 2 +- .../single-session/healthy-session.js | 2 +- .../terminal-state-sessions-sent-once.js | 2 +- .../uncaught-exception-crashed-session.js | 2 +- .../unhandled-rejection-crashed-session.js | 2 +- .../node/test/manual/webpack-domain/index.js | 2 +- packages/node/tsconfig.cjs.json | 2 +- packages/react/package.json | 4 +- packages/react/tsconfig.cjs.json | 2 +- packages/serverless/README.md | 2 +- packages/serverless/package.json | 4 +- .../scripts/build-awslambda-layer.js | 2 +- packages/serverless/tsconfig.cjs.json | 2 +- packages/tracing/.npmignore | 2 +- packages/tracing/package.json | 4 +- packages/tracing/tsconfig.cjs.json | 2 +- packages/types/.npmignore | 2 +- packages/types/package.json | 4 +- packages/types/tsconfig.cjs.json | 2 +- packages/utils/.npmignore | 2 +- packages/utils/package.json | 4 +- packages/utils/test/types/index.js | 2 +- packages/utils/tsconfig.cjs.json | 2 +- packages/vue/package.json | 4 +- packages/vue/tsconfig.cjs.json | 2 +- packages/wasm/package.json | 4 +- packages/wasm/tsconfig.cjs.json | 2 +- tsconfig-templates/tsconfig.cjs.json | 2 +- typedoc.js | 2 +- 64 files changed, 109 insertions(+), 111 deletions(-) diff --git a/.eslintrc.js b/.eslintrc.js index a081c38c7ade..da303c683c01 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -10,7 +10,16 @@ module.exports = { ecmaVersion: 2018, }, extends: ['@sentry-internal/sdk'], - ignorePatterns: ['coverage/**', 'build/**', 'dist/**', 'esm/**', 'examples/**', 'test/manual/**', 'types/**'], + ignorePatterns: [ + 'coverage/**', + 'build/**', + 'dist/**', + 'cjs/**', + 'esm/**', + 'examples/**', + 'test/manual/**', + 'types/**', + ], overrides: [ { files: ['*.ts', '*.tsx', '*.d.ts'], diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 368412a4515c..aec9e06c66dc 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -23,7 +23,7 @@ env: CACHED_BUILD_PATHS: | ${{ github.workspace }}/packages/**/build - ${{ github.workspace }}/packages/**/dist + ${{ github.workspace }}/packages/**/cjs ${{ github.workspace }}/packages/**/esm ${{ github.workspace }}/packages/ember/*.d.ts ${{ github.workspace }}/packages/ember/instance-initializers diff --git a/.gitignore b/.gitignore index 01098a94ef9f..4418a41432bc 100644 --- a/.gitignore +++ b/.gitignore @@ -5,7 +5,7 @@ package-lock.json # build and test build/ -packages/*/dist/ +packages/*/cjs/ packages/*/esm/ coverage/ scratch/ diff --git a/.npmignore b/.npmignore index facb3a101f90..cb864514088e 100644 --- a/.npmignore +++ b/.npmignore @@ -3,6 +3,6 @@ * -!/dist/**/* +!/cjs/**/* !/esm/**/* !/types/**/* diff --git a/.vscode/launch.json b/.vscode/launch.json index 9670683310d6..bef12bd786af 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -5,16 +5,16 @@ "version": "0.2.0", "inputs": [ { - "id": "getPackageName", - "type": "command", - "command": "shellCommand.execute", - "args": { - "command": "echo '${file}' | sed s/'.*sentry-javascript\\/packages\\/'// | grep --extended-regexp --only-matching --max-count 1 '[^\\/]+' | head -1", - "cwd": "${workspaceFolder}" , - // normally `input` commands bring up a selector for the user, but given that there should only be one - // choice here, this lets us skip the prompt - "useSingleResult": true - } + "id": "getPackageName", + "type": "command", + "command": "shellCommand.execute", + "args": { + "command": "echo '${file}' | sed s/'.*sentry-javascript\\/packages\\/'// | grep --extended-regexp --only-matching --max-count 1 '[^\\/]+' | head -1", + "cwd": "${workspaceFolder}", + // normally `input` commands bring up a selector for the user, but given that there should only be one + // choice here, this lets us skip the prompt + "useSingleResult": true + } } ], "configurations": [ @@ -25,18 +25,9 @@ "cwd": "${workspaceFolder}/packages/${input:getPackageName}", "request": "launch", "runtimeExecutable": "yarn", - "runtimeArgs": [ - "rollup", - "-c", - "${file}" - ], - "skipFiles": [ - "/**" - ], - "outFiles": [ - "${workspaceFolder}/**/*.js", - "!**/node_modules/**" - ], + "runtimeArgs": ["rollup", "-c", "${file}"], + "skipFiles": ["/**"], + "outFiles": ["${workspaceFolder}/**/*.js", "!**/node_modules/**"], "sourceMaps": true, "smartStep": true, "internalConsoleOptions": "openOnSessionStart", @@ -67,14 +58,13 @@ ], "sourceMaps": true, "smartStep": true, - // otherwise it goes to the VSCode debug terminal, which prints the test output or console logs (depending on - // "outputCapture" option here; default is to show console logs), but not both + // otherwise it goes to the VSCode debug terminal, which prints the test output or console logs (depending on + // "outputCapture" option here; default is to show console logs), but not both "console": "integratedTerminal", // since we're not using it, don't automatically switch to it - "internalConsoleOptions": "neverOpen", + "internalConsoleOptions": "neverOpen" }, - // @sentry/nextjs - Run a specific integration test file // Must have test file in currently active tab when hitting the play button, and must already have run `yarn` in test app directory { @@ -105,18 +95,17 @@ // this controls which files are sourcemapped "outFiles": [ // our SDK code - "${workspaceFolder}/**/dist/**/*.js", + "${workspaceFolder}/**/cjs/**/*.js", // the built test app "${workspaceFolder}/packages/nextjs/test/integration/.next/**/*.js", "!**/node_modules/**" ], "resolveSourceMapLocations": [ - "${workspaceFolder}/**/dist/**", + "${workspaceFolder}/**/cjs/**", "${workspaceFolder}/packages/nextjs/test/integration/.next/**", "!**/node_modules/**" ], "internalConsoleOptions": "openOnSessionStart" - - }, + } ] } diff --git a/packages/angular/.npmignore b/packages/angular/.npmignore index 8904efca5aea..75ee79933841 100644 --- a/packages/angular/.npmignore +++ b/packages/angular/.npmignore @@ -1,4 +1,4 @@ * -!/dist/**/* +!/cjs/**/* !/esm/**/* !/build/types/**/* diff --git a/packages/angular/package.json b/packages/angular/package.json index 7a67fb0c4c8c..5b5abb346c53 100644 --- a/packages/angular/package.json +++ b/packages/angular/package.json @@ -9,7 +9,7 @@ "engines": { "node": ">=8" }, - "main": "dist/index.js", + "main": "cjs/index.js", "module": "esm/index.js", "types": "build/types/index.d.ts", "publishConfig": { @@ -47,7 +47,7 @@ "build:types:watch": "tsc -p tsconfig.types.json --watch", "build:npm": "npm pack", "circularDepCheck": "madge --circular src/index.ts", - "clean": "rimraf dist esm build coverage", + "clean": "rimraf cjs esm build coverage", "fix": "run-s fix:eslint fix:prettier", "fix:eslint": "eslint . --format stylish --fix", "fix:prettier": "prettier --write \"{src,test,scripts}/**/*.ts\"", diff --git a/packages/angular/tsconfig.cjs.json b/packages/angular/tsconfig.cjs.json index abd80f77e1ff..4ec31d2ff68b 100644 --- a/packages/angular/tsconfig.cjs.json +++ b/packages/angular/tsconfig.cjs.json @@ -3,6 +3,6 @@ "compilerOptions": { "module": "commonjs", - "outDir": "dist" + "outDir": "cjs" } } diff --git a/packages/browser/package.json b/packages/browser/package.json index ab069d2d03df..d7a317f9370e 100644 --- a/packages/browser/package.json +++ b/packages/browser/package.json @@ -9,7 +9,7 @@ "engines": { "node": ">=8" }, - "main": "build/npm/dist/index.js", + "main": "build/npm/cjs/index.js", "module": "build/npm/esm/index.js", "types": "build/npm/types/index.d.ts", "publishConfig": { diff --git a/packages/browser/test/package/test-code.js b/packages/browser/test/package/test-code.js index e05a667db274..3a3811eebb89 100644 --- a/packages/browser/test/package/test-code.js +++ b/packages/browser/test/package/test-code.js @@ -1,6 +1,6 @@ /* eslint-disable no-console */ -const Sentry = require('../../build/npm/dist/index.js'); -const Integrations = require('../../../integrations/build/npm/dist/dedupe.js'); +const Sentry = require('../../build/npm/cjs/index.js'); +const Integrations = require('../../../integrations/build/npm/cjs/dedupe.js'); // Init Sentry.init({ diff --git a/packages/browser/tsconfig.cjs.json b/packages/browser/tsconfig.cjs.json index 6782dae5e453..0fa3132e7510 100644 --- a/packages/browser/tsconfig.cjs.json +++ b/packages/browser/tsconfig.cjs.json @@ -3,6 +3,6 @@ "compilerOptions": { "module": "commonjs", - "outDir": "build/npm/dist" + "outDir": "build/npm/cjs" } } diff --git a/packages/core/package.json b/packages/core/package.json index db96208eea0d..13af6977e228 100644 --- a/packages/core/package.json +++ b/packages/core/package.json @@ -9,7 +9,7 @@ "engines": { "node": ">=8" }, - "main": "build/dist/index.js", + "main": "build/cjs/index.js", "module": "build/esm/index.js", "types": "build/types/index.d.ts", "publishConfig": { @@ -37,7 +37,7 @@ "build:types:watch": "tsc -p tsconfig.types.json --watch", "build:npm": "ts-node ../../scripts/prepack.ts && npm pack ./build", "circularDepCheck": "madge --circular src/index.ts", - "clean": "rimraf dist esm build coverage", + "clean": "rimraf build coverage", "fix": "run-s fix:eslint fix:prettier", "fix:eslint": "eslint . --format stylish --fix", "fix:prettier": "prettier --write \"{src,test,scripts}/**/*.ts\"", diff --git a/packages/core/tsconfig.cjs.json b/packages/core/tsconfig.cjs.json index e3a918fc70af..c1edc81a9657 100644 --- a/packages/core/tsconfig.cjs.json +++ b/packages/core/tsconfig.cjs.json @@ -3,6 +3,6 @@ "compilerOptions": { "module": "commonjs", - "outDir": "build/dist" + "outDir": "build/cjs" } } diff --git a/packages/gatsby/.npmignore b/packages/gatsby/.npmignore index b8bb08da7374..66474694b223 100644 --- a/packages/gatsby/.npmignore +++ b/packages/gatsby/.npmignore @@ -3,7 +3,7 @@ * -!/dist/**/* +!/cjs/**/* !/esm/**/* !/types/**/* diff --git a/packages/gatsby/package.json b/packages/gatsby/package.json index 08289e6765ca..04d2530fdf24 100644 --- a/packages/gatsby/package.json +++ b/packages/gatsby/package.json @@ -13,7 +13,7 @@ "engines": { "node": ">=8" }, - "main": "build/dist/index.js", + "main": "build/cjs/index.js", "module": "build/esm/index.js", "types": "build/types/index.d.ts", "publishConfig": { @@ -48,7 +48,7 @@ "build:types:watch": "tsc -p tsconfig.types.json --watch", "build:npm": "ts-node ../../scripts/prepack.ts && npm pack ./build", "circularDepCheck": "madge --circular src/index.ts", - "clean": "rimraf dist esm build coverage", + "clean": "rimraf build coverage", "fix": "run-s fix:eslint fix:prettier", "fix:eslint": "eslint . --format stylish --fix", "fix:prettier": "prettier --write \"{src,test,scripts}/**/*.ts\"", diff --git a/packages/gatsby/tsconfig.cjs.json b/packages/gatsby/tsconfig.cjs.json index e3a918fc70af..c1edc81a9657 100644 --- a/packages/gatsby/tsconfig.cjs.json +++ b/packages/gatsby/tsconfig.cjs.json @@ -3,6 +3,6 @@ "compilerOptions": { "module": "commonjs", - "outDir": "build/dist" + "outDir": "build/cjs" } } diff --git a/packages/hub/package.json b/packages/hub/package.json index 39feacea908a..900e048cd735 100644 --- a/packages/hub/package.json +++ b/packages/hub/package.json @@ -9,7 +9,7 @@ "engines": { "node": ">=8" }, - "main": "build/dist/index.js", + "main": "build/cjs/index.js", "module": "build/esm/index.js", "types": "build/types/index.d.ts", "publishConfig": { @@ -35,7 +35,7 @@ "build:types:watch": "tsc -p tsconfig.types.json --watch", "build:npm": "ts-node ../../scripts/prepack.ts && npm pack ./build", "circularDepCheck": "madge --circular src/index.ts", - "clean": "rimraf dist esm coverage", + "clean": "rimraf build coverage", "fix": "run-s fix:eslint fix:prettier", "fix:eslint": "eslint . --format stylish --fix", "fix:prettier": "prettier --write \"{src,test,scripts}/**/*.ts\"", diff --git a/packages/hub/tsconfig.cjs.json b/packages/hub/tsconfig.cjs.json index e3a918fc70af..c1edc81a9657 100644 --- a/packages/hub/tsconfig.cjs.json +++ b/packages/hub/tsconfig.cjs.json @@ -3,6 +3,6 @@ "compilerOptions": { "module": "commonjs", - "outDir": "build/dist" + "outDir": "build/cjs" } } diff --git a/packages/integration-tests/utils/generatePlugin.ts b/packages/integration-tests/utils/generatePlugin.ts index 5163f81a668a..1a7ebefad0b7 100644 --- a/packages/integration-tests/utils/generatePlugin.ts +++ b/packages/integration-tests/utils/generatePlugin.ts @@ -17,7 +17,7 @@ const useBundle = bundleKey && !useCompiledModule; const BUNDLE_PATHS: Record> = { browser: { - cjs: 'build/npm/dist/index.js', + cjs: 'build/npm/cjs/index.js', esm: 'build/npm/esm/index.js', bundle_es5: 'build/bundles/bundle.js', bundle_es5_min: 'build/bundles/bundle.min.js', @@ -25,7 +25,7 @@ const BUNDLE_PATHS: Record> = { bundle_es6_min: 'build/bundles/bundle.es6.min.js', }, tracing: { - cjs: 'build/npm/dist/index.js', + cjs: 'build/npm/cjs/index.js', esm: 'build/npm/esm/index.js', bundle_es5: 'build/bundles/bundle.tracing.js', bundle_es5_min: 'build/bundles/bundle.tracing.min.js', diff --git a/packages/integrations/package.json b/packages/integrations/package.json index 41fc646e72e6..4cab47b436b2 100644 --- a/packages/integrations/package.json +++ b/packages/integrations/package.json @@ -12,7 +12,7 @@ "publishConfig": { "access": "public" }, - "main": "build/npm/dist/index.js", + "main": "build/npm/cjs/index.js", "module": "build/npm/esm/index.js", "types": "build/npm/types/index.d.ts", "dependencies": { @@ -40,7 +40,7 @@ "build:types:watch": "tsc -p tsconfig.types.json --watch", "build:npm": "ts-node ../../scripts/prepack.ts --bundles && npm pack ./build/npm", "circularDepCheck": "madge --circular src/index.ts", - "clean": "rimraf dist esm build coverage .rpt2_cache", + "clean": "rimraf build coverage .rpt2_cache", "fix": "run-s fix:eslint fix:prettier", "fix:eslint": "eslint . --format stylish --fix", "fix:prettier": "prettier --write \"{src,test,scripts}/**/*.ts\"", diff --git a/packages/integrations/tsconfig.cjs.json b/packages/integrations/tsconfig.cjs.json index 6782dae5e453..0fa3132e7510 100644 --- a/packages/integrations/tsconfig.cjs.json +++ b/packages/integrations/tsconfig.cjs.json @@ -3,6 +3,6 @@ "compilerOptions": { "module": "commonjs", - "outDir": "build/npm/dist" + "outDir": "build/npm/cjs" } } diff --git a/packages/minimal/.npmignore b/packages/minimal/.npmignore index facb3a101f90..cb864514088e 100644 --- a/packages/minimal/.npmignore +++ b/packages/minimal/.npmignore @@ -3,6 +3,6 @@ * -!/dist/**/* +!/cjs/**/* !/esm/**/* !/types/**/* diff --git a/packages/minimal/package.json b/packages/minimal/package.json index e91af98cf40f..e2e1b9286241 100644 --- a/packages/minimal/package.json +++ b/packages/minimal/package.json @@ -9,7 +9,7 @@ "engines": { "node": ">=8" }, - "main": "build/dist/index.js", + "main": "build/cjs/index.js", "module": "build/esm/index.js", "types": "build/types/index.d.ts", "publishConfig": { @@ -35,7 +35,7 @@ "build:types:watch": "tsc -p tsconfig.types.json --watch", "build:npm": "ts-node ../../scripts/prepack.ts && npm pack ./build", "circularDepCheck": "madge --circular src/index.ts", - "clean": "rimraf dist esm build coverage", + "clean": "rimraf build coverage", "fix": "run-s fix:eslint fix:prettier", "fix:eslint": "eslint . --format stylish --fix", "fix:prettier": "prettier --write \"{src,test,scripts}/**/*.ts\"", diff --git a/packages/minimal/tsconfig.cjs.json b/packages/minimal/tsconfig.cjs.json index e3a918fc70af..c1edc81a9657 100644 --- a/packages/minimal/tsconfig.cjs.json +++ b/packages/minimal/tsconfig.cjs.json @@ -3,6 +3,6 @@ "compilerOptions": { "module": "commonjs", - "outDir": "build/dist" + "outDir": "build/cjs" } } diff --git a/packages/nextjs/package.json b/packages/nextjs/package.json index f5305887fb28..569078be6d5d 100644 --- a/packages/nextjs/package.json +++ b/packages/nextjs/package.json @@ -9,7 +9,7 @@ "engines": { "node": ">=8" }, - "main": "build/dist/index.server.js", + "main": "build/cjs/index.server.js", "module": "build/esm/index.server.js", "browser": "build/esm/index.client.js", "types": "build/types/index.server.d.ts", @@ -57,7 +57,7 @@ "build:types:watch": "tsc -p tsconfig.types.json --watch", "build:npm": "ts-node ../../scripts/prepack.ts && npm pack ./build", "circularDepCheck": "madge --circular src/index.client.ts && madge --circular --exclude 'config/types\\.ts' src/index.server.ts # see https://github.com/pahen/madge/issues/306", - "clean": "rimraf dist esm build coverage *.js *.js.map *.d.ts", + "clean": "rimraf build coverage *.js *.js.map *.d.ts", "fix": "run-s fix:eslint fix:prettier", "fix:eslint": "eslint . --format stylish --fix", "fix:prettier": "prettier --write \"{src,test,scripts}/**/*.ts\"", @@ -97,7 +97,7 @@ } }, "sideEffects": [ - "./dist/index.server.js", + "./cjs/index.server.js", "./esm/index.server.js", "./src/index.server.ts" ] diff --git a/packages/nextjs/tsconfig.cjs.json b/packages/nextjs/tsconfig.cjs.json index e3a918fc70af..c1edc81a9657 100644 --- a/packages/nextjs/tsconfig.cjs.json +++ b/packages/nextjs/tsconfig.cjs.json @@ -3,6 +3,6 @@ "compilerOptions": { "module": "commonjs", - "outDir": "build/dist" + "outDir": "build/cjs" } } diff --git a/packages/nextjs/vercel/install-sentry-from-branch.sh b/packages/nextjs/vercel/install-sentry-from-branch.sh index af79d10ea04e..704601894308 100644 --- a/packages/nextjs/vercel/install-sentry-from-branch.sh +++ b/packages/nextjs/vercel/install-sentry-from-branch.sh @@ -28,7 +28,7 @@ yarn --prod false echo " " echo "BUILDING SDK" # We need to build es5 versions because `next.config.js` calls `require` on the SDK (to get `withSentryConfig`) and -# therefore it looks for `dist/index.js` +# therefore it looks for `cjs/index.js` yarn build:cjs # We need to build esm versions because that's what `next` actually uses when it builds the app yarn build:esm diff --git a/packages/node/.npmignore b/packages/node/.npmignore index facb3a101f90..cb864514088e 100644 --- a/packages/node/.npmignore +++ b/packages/node/.npmignore @@ -3,6 +3,6 @@ * -!/dist/**/* +!/cjs/**/* !/esm/**/* !/types/**/* diff --git a/packages/node/package.json b/packages/node/package.json index aa613076c62e..82e33852b768 100644 --- a/packages/node/package.json +++ b/packages/node/package.json @@ -9,7 +9,7 @@ "engines": { "node": ">=8" }, - "main": "build/dist/index.js", + "main": "build/cjs/index.js", "module": "build/esm/index.js", "types": "build/types/index.d.ts", "publishConfig": { @@ -48,7 +48,7 @@ "build:types:watch": "tsc -p tsconfig.types.json --watch", "build:npm": "ts-node ../../scripts/prepack.ts && npm pack ./build", "circularDepCheck": "madge --circular src/index.ts", - "clean": "rimraf dist esm build coverage", + "clean": "rimraf build coverage", "fix": "run-s fix:eslint fix:prettier", "fix:eslint": "eslint . --format stylish --fix", "fix:prettier": "prettier --write \"{src,test,scripts}/**/*.ts\"", diff --git a/packages/node/test/manual/apm-transaction/main.js b/packages/node/test/manual/apm-transaction/main.js index e34088b4eaaf..0584177e8b10 100644 --- a/packages/node/test/manual/apm-transaction/main.js +++ b/packages/node/test/manual/apm-transaction/main.js @@ -1,7 +1,7 @@ const http = require('http'); const express = require('express'); const app = express(); -const Sentry = require('../../../build/dist'); +const Sentry = require('../../../build/cjs'); Sentry.init({ debug: true, diff --git a/packages/node/test/manual/express-scope-separation/start.js b/packages/node/test/manual/express-scope-separation/start.js index 2e89fa14f4ec..9146c3ab2eba 100644 --- a/packages/node/test/manual/express-scope-separation/start.js +++ b/packages/node/test/manual/express-scope-separation/start.js @@ -1,7 +1,7 @@ const http = require('http'); const express = require('express'); const app = express(); -const Sentry = require('../../../build/dist'); +const Sentry = require('../../../build/cjs'); function assertTags(actual, expected) { if (JSON.stringify(actual) !== JSON.stringify(expected)) { diff --git a/packages/node/test/manual/memory-leak/context-memory.js b/packages/node/test/manual/memory-leak/context-memory.js index 9c63343c1fd8..6c124d542ce5 100644 --- a/packages/node/test/manual/memory-leak/context-memory.js +++ b/packages/node/test/manual/memory-leak/context-memory.js @@ -1,4 +1,4 @@ -const Sentry = require('../../../build/dist'); +const Sentry = require('../../../build/cjs'); Sentry.init({ dsn: 'https://public@app.getsentry.com/12345' }); diff --git a/packages/node/test/manual/memory-leak/express-patient.js b/packages/node/test/manual/memory-leak/express-patient.js index 90be8be575d4..7a677a442972 100644 --- a/packages/node/test/manual/memory-leak/express-patient.js +++ b/packages/node/test/manual/memory-leak/express-patient.js @@ -1,4 +1,4 @@ -const Sentry = require('../../../build/dist'); +const Sentry = require('../../../build/cjs'); Sentry.init({ dsn: 'https://public@app.getsentry.com/12345' }); diff --git a/packages/node/test/manual/release-health/session-aggregates/aggregates-disable-single-session.js b/packages/node/test/manual/release-health/session-aggregates/aggregates-disable-single-session.js index cedd795e9a57..0c536e0accae 100644 --- a/packages/node/test/manual/release-health/session-aggregates/aggregates-disable-single-session.js +++ b/packages/node/test/manual/release-health/session-aggregates/aggregates-disable-single-session.js @@ -1,7 +1,7 @@ const http = require('http'); const express = require('express'); const app = express(); -const Sentry = require('../../../../build/dist'); +const Sentry = require('../../../../build/cjs'); const { assertSessions, BaseDummyTransport } = require('../test-utils'); function cleanUpAndExitSuccessfully() { diff --git a/packages/node/test/manual/release-health/single-session/caught-exception-errored-session.js b/packages/node/test/manual/release-health/single-session/caught-exception-errored-session.js index 9f538a1ba993..db052b2bc508 100644 --- a/packages/node/test/manual/release-health/single-session/caught-exception-errored-session.js +++ b/packages/node/test/manual/release-health/single-session/caught-exception-errored-session.js @@ -1,4 +1,4 @@ -const Sentry = require('../../../../build/dist'); +const Sentry = require('../../../../build/cjs'); const { assertSessions, constructStrippedSessionObject, diff --git a/packages/node/test/manual/release-health/single-session/errors-in-session-capped-to-one.js b/packages/node/test/manual/release-health/single-session/errors-in-session-capped-to-one.js index 65c2bf05ef7f..983f10e9b294 100644 --- a/packages/node/test/manual/release-health/single-session/errors-in-session-capped-to-one.js +++ b/packages/node/test/manual/release-health/single-session/errors-in-session-capped-to-one.js @@ -1,4 +1,4 @@ -const Sentry = require('../../../../build/dist'); +const Sentry = require('../../../../build/cjs'); const { assertSessions, constructStrippedSessionObject, diff --git a/packages/node/test/manual/release-health/single-session/healthy-session.js b/packages/node/test/manual/release-health/single-session/healthy-session.js index 05712ae7dcc2..1906b8196bf5 100644 --- a/packages/node/test/manual/release-health/single-session/healthy-session.js +++ b/packages/node/test/manual/release-health/single-session/healthy-session.js @@ -1,4 +1,4 @@ -const Sentry = require('../../../../build/dist'); +const Sentry = require('../../../../build/cjs'); const { assertSessions, constructStrippedSessionObject, diff --git a/packages/node/test/manual/release-health/single-session/terminal-state-sessions-sent-once.js b/packages/node/test/manual/release-health/single-session/terminal-state-sessions-sent-once.js index 1fe58a972569..b2692957aa70 100644 --- a/packages/node/test/manual/release-health/single-session/terminal-state-sessions-sent-once.js +++ b/packages/node/test/manual/release-health/single-session/terminal-state-sessions-sent-once.js @@ -1,4 +1,4 @@ -const Sentry = require('../../../../build/dist'); +const Sentry = require('../../../../build/cjs'); const { assertSessions, constructStrippedSessionObject, diff --git a/packages/node/test/manual/release-health/single-session/uncaught-exception-crashed-session.js b/packages/node/test/manual/release-health/single-session/uncaught-exception-crashed-session.js index 8007b7d0ea4c..eaf247aa080a 100644 --- a/packages/node/test/manual/release-health/single-session/uncaught-exception-crashed-session.js +++ b/packages/node/test/manual/release-health/single-session/uncaught-exception-crashed-session.js @@ -1,4 +1,4 @@ -const Sentry = require('../../../../build/dist'); +const Sentry = require('../../../../build/cjs'); const { assertSessions, constructStrippedSessionObject, BaseDummyTransport } = require('../test-utils'); class DummyTransport extends BaseDummyTransport { diff --git a/packages/node/test/manual/release-health/single-session/unhandled-rejection-crashed-session.js b/packages/node/test/manual/release-health/single-session/unhandled-rejection-crashed-session.js index f24c557c878a..c5a1874024ba 100644 --- a/packages/node/test/manual/release-health/single-session/unhandled-rejection-crashed-session.js +++ b/packages/node/test/manual/release-health/single-session/unhandled-rejection-crashed-session.js @@ -1,4 +1,4 @@ -const Sentry = require('../../../../build/dist'); +const Sentry = require('../../../../build/cjs'); const { assertSessions, constructStrippedSessionObject, diff --git a/packages/node/test/manual/webpack-domain/index.js b/packages/node/test/manual/webpack-domain/index.js index e0479e0fec78..521699a6349a 100644 --- a/packages/node/test/manual/webpack-domain/index.js +++ b/packages/node/test/manual/webpack-domain/index.js @@ -1,4 +1,4 @@ -const Sentry = require('../../../build/dist'); +const Sentry = require('../../../build/cjs'); let remaining = 2; diff --git a/packages/node/tsconfig.cjs.json b/packages/node/tsconfig.cjs.json index e3a918fc70af..c1edc81a9657 100644 --- a/packages/node/tsconfig.cjs.json +++ b/packages/node/tsconfig.cjs.json @@ -3,6 +3,6 @@ "compilerOptions": { "module": "commonjs", - "outDir": "build/dist" + "outDir": "build/cjs" } } diff --git a/packages/react/package.json b/packages/react/package.json index 1fdda308f5a7..5be6217ff3ce 100644 --- a/packages/react/package.json +++ b/packages/react/package.json @@ -9,7 +9,7 @@ "engines": { "node": ">=8" }, - "main": "build/dist/index.js", + "main": "build/cjs/index.js", "module": "build/esm/index.js", "types": "build/types/index.d.ts", "publishConfig": { @@ -63,7 +63,7 @@ "build:types:watch": "tsc -p tsconfig.types.json --watch", "build:npm": "ts-node ../../scripts/prepack.ts && npm pack ./build", "circularDepCheck": "madge --circular src/index.ts", - "clean": "rimraf dist esm build coverage", + "clean": "rimraf build coverage", "fix": "run-s fix:eslint fix:prettier", "fix:eslint": "eslint . --format stylish --fix", "fix:prettier": "prettier --write \"{src,test,scripts}/**/*.ts\"", diff --git a/packages/react/tsconfig.cjs.json b/packages/react/tsconfig.cjs.json index e3a918fc70af..c1edc81a9657 100644 --- a/packages/react/tsconfig.cjs.json +++ b/packages/react/tsconfig.cjs.json @@ -3,6 +3,6 @@ "compilerOptions": { "module": "commonjs", - "outDir": "build/dist" + "outDir": "build/cjs" } } diff --git a/packages/serverless/README.md b/packages/serverless/README.md index 4b99dde9b424..e6f819f0cf66 100644 --- a/packages/serverless/README.md +++ b/packages/serverless/README.md @@ -60,7 +60,7 @@ Another and much simpler way to integrate Sentry to your AWS Lambda function is 1. Choose Layers -> Add Layer. 2. Specify an ARN: `arn:aws:lambda:us-west-1:TODO:layer:TODO:VERSION`. 3. Go to Environment variables and add: - - `NODE_OPTIONS`: `-r @sentry/serverless/dist/awslambda-auto`. + - `NODE_OPTIONS`: `-r @sentry/serverless/cjs/awslambda-auto`. - `SENTRY_DSN`: `your dsn`. - `SENTRY_TRACES_SAMPLE_RATE`: a number between 0 and 1 representing the chance a transaction is sent to Sentry. For more information, see [docs](https://docs.sentry.io/platforms/node/guides/aws-lambda/configuration/options/#tracesSampleRate). diff --git a/packages/serverless/package.json b/packages/serverless/package.json index 0200a629e706..4497263841a2 100644 --- a/packages/serverless/package.json +++ b/packages/serverless/package.json @@ -9,7 +9,7 @@ "engines": { "node": ">=10" }, - "main": "build/dist/index.js", + "main": "build/cjs/index.js", "module": "build/esm/index.js", "types": "build/types/index.d.ts", "publishConfig": { @@ -54,7 +54,7 @@ "build:types:watch": "tsc -p tsconfig.types.json --watch", "build:npm": "ts-node ../../scripts/prepack.ts && npm pack ./build", "circularDepCheck": "madge --circular src/index.ts", - "clean": "rimraf dist esm build dist-awslambda-layer coverage", + "clean": "rimraf build dist-awslambda-layer coverage", "fix": "run-s fix:eslint fix:prettier", "fix:eslint": "eslint . --format stylish --fix", "fix:prettier": "prettier --write \"{src,test,scripts}/**/*.ts\"", diff --git a/packages/serverless/scripts/build-awslambda-layer.js b/packages/serverless/scripts/build-awslambda-layer.js index 6031883e60b7..dfea6a0c3b73 100644 --- a/packages/serverless/scripts/build-awslambda-layer.js +++ b/packages/serverless/scripts/build-awslambda-layer.js @@ -69,7 +69,7 @@ async function collectPackages(cwd, packages = {}) { async function main() { const workDir = path.resolve(__dirname, '..'); // packages/serverless directory - const distRequirements = path.resolve(workDir, 'build', 'dist'); + const distRequirements = path.resolve(workDir, 'build', 'cjs'); if (!fs.existsSync(distRequirements)) { console.log(`The path ${distRequirements} must exist.`); return; diff --git a/packages/serverless/tsconfig.cjs.json b/packages/serverless/tsconfig.cjs.json index e3a918fc70af..c1edc81a9657 100644 --- a/packages/serverless/tsconfig.cjs.json +++ b/packages/serverless/tsconfig.cjs.json @@ -3,6 +3,6 @@ "compilerOptions": { "module": "commonjs", - "outDir": "build/dist" + "outDir": "build/cjs" } } diff --git a/packages/tracing/.npmignore b/packages/tracing/.npmignore index facb3a101f90..cb864514088e 100644 --- a/packages/tracing/.npmignore +++ b/packages/tracing/.npmignore @@ -3,6 +3,6 @@ * -!/dist/**/* +!/cjs/**/* !/esm/**/* !/types/**/* diff --git a/packages/tracing/package.json b/packages/tracing/package.json index caa6bdf93121..4f1048c4a1ea 100644 --- a/packages/tracing/package.json +++ b/packages/tracing/package.json @@ -9,7 +9,7 @@ "engines": { "node": ">=8" }, - "main": "build/npm/dist/index.js", + "main": "build/npm/cjs/index.js", "module": "build/npm/esm/index.js", "types": "build/npm/types/index.d.ts", "publishConfig": { @@ -44,7 +44,7 @@ "build:esm:watch": "tsc -p tsconfig.esm.json --watch", "build:types:watch": "tsc -p tsconfig.types.json --watch", "build:npm": "ts-node ../../scripts/prepack.ts --bundles && npm pack ./build/npm", - "clean": "rimraf dist esm build coverage", + "clean": "rimraf build coverage", "circularDepCheck": "madge --circular src/index.ts", "fix": "run-s fix:eslint fix:prettier", "fix:eslint": "eslint . --format stylish --fix", diff --git a/packages/tracing/tsconfig.cjs.json b/packages/tracing/tsconfig.cjs.json index 6782dae5e453..0fa3132e7510 100644 --- a/packages/tracing/tsconfig.cjs.json +++ b/packages/tracing/tsconfig.cjs.json @@ -3,6 +3,6 @@ "compilerOptions": { "module": "commonjs", - "outDir": "build/npm/dist" + "outDir": "build/npm/cjs" } } diff --git a/packages/types/.npmignore b/packages/types/.npmignore index facb3a101f90..cb864514088e 100644 --- a/packages/types/.npmignore +++ b/packages/types/.npmignore @@ -3,6 +3,6 @@ * -!/dist/**/* +!/cjs/**/* !/esm/**/* !/types/**/* diff --git a/packages/types/package.json b/packages/types/package.json index d5d7c03cb6ad..111d71701430 100644 --- a/packages/types/package.json +++ b/packages/types/package.json @@ -9,7 +9,7 @@ "engines": { "node": ">=8" }, - "main": "build/dist/index.js", + "main": "build/cjs/index.js", "module": "build/esm/index.js", "types": "build/types/index.d.ts", "publishConfig": { @@ -29,7 +29,7 @@ "build:esm:watch": "tsc -p tsconfig.esm.json --watch", "build:types:watch": "tsc -p tsconfig.types.json --watch", "build:npm": "ts-node ../../scripts/prepack.ts && npm pack ./build", - "clean": "rimraf dist esm build", + "clean": "rimraf build", "link:yarn": "yarn link", "lint": "run-s lint:prettier lint:eslint", "lint:eslint": "eslint . --cache --cache-location '../../eslintcache/' --format stylish", diff --git a/packages/types/tsconfig.cjs.json b/packages/types/tsconfig.cjs.json index e3a918fc70af..c1edc81a9657 100644 --- a/packages/types/tsconfig.cjs.json +++ b/packages/types/tsconfig.cjs.json @@ -3,6 +3,6 @@ "compilerOptions": { "module": "commonjs", - "outDir": "build/dist" + "outDir": "build/cjs" } } diff --git a/packages/utils/.npmignore b/packages/utils/.npmignore index facb3a101f90..cb864514088e 100644 --- a/packages/utils/.npmignore +++ b/packages/utils/.npmignore @@ -3,6 +3,6 @@ * -!/dist/**/* +!/cjs/**/* !/esm/**/* !/types/**/* diff --git a/packages/utils/package.json b/packages/utils/package.json index 08525cc8a84a..85cff26960c2 100644 --- a/packages/utils/package.json +++ b/packages/utils/package.json @@ -9,7 +9,7 @@ "engines": { "node": ">=8" }, - "main": "build/dist/index.js", + "main": "build/cjs/index.js", "module": "build/esm/index.js", "types": "build/types/index.d.ts", "publishConfig": { @@ -38,7 +38,7 @@ "build:types:watch": "tsc -p tsconfig.types.json --watch", "build:npm": "ts-node ../../scripts/prepack.ts && npm pack ./build", "circularDepCheck": "madge --circular src/index.ts", - "clean": "rimraf dist esm build coverage *.js *.js.map *.d.ts", + "clean": "rimraf build coverage *.js *.js.map *.d.ts", "fix": "run-s fix:eslint fix:prettier", "fix:eslint": "eslint . --format stylish --fix", "fix:prettier": "prettier --write \"{src,test,scripts}/**/*.ts\"", diff --git a/packages/utils/test/types/index.js b/packages/utils/test/types/index.js index a8f9e9360b68..7b6936691197 100644 --- a/packages/utils/test/types/index.js +++ b/packages/utils/test/types/index.js @@ -3,7 +3,7 @@ const path = require('path'); const testStrings = ['/// ']; -const paths = [path.join('./build/dist'), path.join('./build/esm')]; +const paths = [path.join('./build/cjs'), path.join('./build/esm')]; paths.forEach(dir => { if (!fs.existsSync(dir)) { diff --git a/packages/utils/tsconfig.cjs.json b/packages/utils/tsconfig.cjs.json index e3a918fc70af..c1edc81a9657 100644 --- a/packages/utils/tsconfig.cjs.json +++ b/packages/utils/tsconfig.cjs.json @@ -3,6 +3,6 @@ "compilerOptions": { "module": "commonjs", - "outDir": "build/dist" + "outDir": "build/cjs" } } diff --git a/packages/vue/package.json b/packages/vue/package.json index 1ff530963569..954b7b319059 100644 --- a/packages/vue/package.json +++ b/packages/vue/package.json @@ -9,7 +9,7 @@ "engines": { "node": ">=8" }, - "main": "build/dist/index.js", + "main": "build/cjs/index.js", "module": "build/esm/index.js", "types": "build/types/index.d.ts", "publishConfig": { @@ -46,7 +46,7 @@ "build:types:watch": "tsc -p tsconfig.types.json --watch", "build:npm": "ts-node ../../scripts/prepack.ts && npm pack ./build", "circularDepCheck": "madge --circular src/index.ts", - "clean": "rimraf dist esm build coverage", + "clean": "rimraf esm build coverage", "fix": "run-s fix:eslint fix:prettier", "fix:eslint": "eslint . --format stylish --fix", "fix:prettier": "prettier --write \"{src,test,scripts}/**/*.ts\"", diff --git a/packages/vue/tsconfig.cjs.json b/packages/vue/tsconfig.cjs.json index e3a918fc70af..c1edc81a9657 100644 --- a/packages/vue/tsconfig.cjs.json +++ b/packages/vue/tsconfig.cjs.json @@ -3,6 +3,6 @@ "compilerOptions": { "module": "commonjs", - "outDir": "build/dist" + "outDir": "build/cjs" } } diff --git a/packages/wasm/package.json b/packages/wasm/package.json index 0cd18104f8ce..14273e42a47b 100644 --- a/packages/wasm/package.json +++ b/packages/wasm/package.json @@ -9,7 +9,7 @@ "engines": { "node": ">=8" }, - "main": "build/npm/dist/index.js", + "main": "build/npm/cjs/index.js", "module": "build/npm/esm/index.js", "types": "build/npm/types/index.d.ts", "publishConfig": { @@ -45,7 +45,7 @@ "build:types:watch": "tsc -p tsconfig.types.json --watch", "build:npm": "ts-node ../../scripts/prepack.ts --bundles && npm pack ./build/npm", "circularDepCheck": "madge --circular src/index.ts", - "clean": "rimraf dist esm build coverage *.js.map *.d.ts", + "clean": "rimraf build coverage *.js.map *.d.ts", "fix": "run-s fix:eslint fix:prettier", "fix:eslint": "eslint . --format stylish --fix", "fix:prettier": "prettier --write \"{src,test,scripts}/**/*.ts\"", diff --git a/packages/wasm/tsconfig.cjs.json b/packages/wasm/tsconfig.cjs.json index 6782dae5e453..0fa3132e7510 100644 --- a/packages/wasm/tsconfig.cjs.json +++ b/packages/wasm/tsconfig.cjs.json @@ -3,6 +3,6 @@ "compilerOptions": { "module": "commonjs", - "outDir": "build/npm/dist" + "outDir": "build/npm/cjs" } } diff --git a/tsconfig-templates/tsconfig.cjs.json b/tsconfig-templates/tsconfig.cjs.json index abd80f77e1ff..4ec31d2ff68b 100644 --- a/tsconfig-templates/tsconfig.cjs.json +++ b/tsconfig-templates/tsconfig.cjs.json @@ -3,6 +3,6 @@ "compilerOptions": { "module": "commonjs", - "outDir": "dist" + "outDir": "cjs" } } diff --git a/typedoc.js b/typedoc.js index f6e0a7e2a805..4b1843c33aad 100644 --- a/typedoc.js +++ b/typedoc.js @@ -6,7 +6,7 @@ module.exports = { exclude: [ '**/test/**/*', '**/*.js', - '**/dist/**/*', + '**/cjs/**/*', '**/esm/**/*', '**/build/**/*', '**/packages/typescript/**/*', From 7f56b0f7a320c270b3d8d24b706743339fb6e33c Mon Sep 17 00:00:00 2001 From: Abhijeet Prasad Date: Fri, 8 Apr 2022 12:35:26 -0400 Subject: [PATCH 019/204] ref(integrations): Remove old `angular`, `ember`, and `vue` integrations (#4893) --- packages/integrations/src/angular.ts | 129 --------- packages/integrations/src/ember.ts | 73 ----- packages/integrations/src/index.ts | 3 - packages/integrations/src/vue.ts | 417 --------------------------- 4 files changed, 622 deletions(-) delete mode 100644 packages/integrations/src/angular.ts delete mode 100644 packages/integrations/src/ember.ts delete mode 100644 packages/integrations/src/vue.ts diff --git a/packages/integrations/src/angular.ts b/packages/integrations/src/angular.ts deleted file mode 100644 index dc71042700ff..000000000000 --- a/packages/integrations/src/angular.ts +++ /dev/null @@ -1,129 +0,0 @@ -import { Event, EventProcessor, Hub, Integration } from '@sentry/types'; -import { getGlobalObject, logger } from '@sentry/utils'; - -import { IS_DEBUG_BUILD } from './flags'; - -// See https://github.com/angular/angular.js/blob/v1.4.7/src/minErr.js -const angularPattern = /^\[((?:[$a-zA-Z0-9]+:)?(?:[$a-zA-Z0-9]+))\] (.*?)\n?(\S+)$/; - -/** - * AngularJS integration - * - * Provides an $exceptionHandler for AngularJS - */ -export class Angular implements Integration { - /** - * @inheritDoc - */ - public static id: string = 'AngularJS'; - - /** - * moduleName used in Angular's DI resolution algorithm - */ - public static moduleName: string = 'ngSentry'; - - /** - * @inheritDoc - */ - public name: string = Angular.id; - - /** - * Angular's instance - */ - // eslint-disable-next-line @typescript-eslint/no-explicit-any - private readonly _angular: any; - - /** - * ngSentry module instance - */ - // eslint-disable-next-line @typescript-eslint/no-explicit-any - private readonly _module: any; - - /** - * Returns current hub. - */ - private _getCurrentHub?: () => Hub; - - /** - * @inheritDoc - */ - // eslint-disable-next-line @typescript-eslint/no-explicit-any - public constructor(options: { angular?: any } = {}) { - IS_DEBUG_BUILD && logger.log('You are still using the Angular integration, consider moving to @sentry/angular'); - - // eslint-disable-next-line @typescript-eslint/no-explicit-any, @typescript-eslint/no-unsafe-member-access - this._angular = options.angular || getGlobalObject().angular; - - if (!this._angular) { - IS_DEBUG_BUILD && logger.error('AngularIntegration is missing an Angular instance'); - return; - } - - // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access - this._module = this._angular.module(Angular.moduleName, []); - } - - /** - * @inheritDoc - */ - public setupOnce(_: (callback: EventProcessor) => void, getCurrentHub: () => Hub): void { - if (!this._module) { - return; - } - - this._getCurrentHub = getCurrentHub; - - // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access - this._module.config([ - '$provide', - // eslint-disable-next-line @typescript-eslint/no-explicit-any - ($provide: any): void => { - // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access - $provide.decorator('$exceptionHandler', ['$delegate', this._$exceptionHandlerDecorator.bind(this)]); - }, - ]); - } - - /** - * Angular's exceptionHandler for Sentry integration - */ - // eslint-disable-next-line @typescript-eslint/no-explicit-any - private _$exceptionHandlerDecorator($delegate: any): any { - return (exception: Error, cause?: string): void => { - const hub = this._getCurrentHub && this._getCurrentHub(); - - if (hub && hub.getIntegration(Angular)) { - hub.withScope(scope => { - if (cause) { - scope.setExtra('cause', cause); - } - - scope.addEventProcessor((event: Event) => { - const ex = event.exception && event.exception.values && event.exception.values[0]; - - if (ex) { - const matches = angularPattern.exec(ex.value || ''); - - if (matches) { - // This type now becomes something like: $rootScope:inprog - ex.type = matches[1]; - ex.value = matches[2]; - event.message = `${ex.type}: ${ex.value}`; - // auto set a new tag specifically for the angular error url - event.extra = { - ...event.extra, - angularDocs: matches[3].substr(0, 250), - }; - } - } - - return event; - }); - - hub.captureException(exception); - }); - } - $delegate(exception, cause); - }; - } -} diff --git a/packages/integrations/src/ember.ts b/packages/integrations/src/ember.ts deleted file mode 100644 index 81bbd1a0036b..000000000000 --- a/packages/integrations/src/ember.ts +++ /dev/null @@ -1,73 +0,0 @@ -import { EventProcessor, Hub, Integration } from '@sentry/types'; -import { getGlobalObject, isInstanceOf, logger } from '@sentry/utils'; - -import { IS_DEBUG_BUILD } from './flags'; - -/** JSDoc */ -export class Ember implements Integration { - /** - * @inheritDoc - */ - public static id: string = 'Ember'; - - /** - * @inheritDoc - */ - public name: string = Ember.id; - - /** - * @inheritDoc - */ - // eslint-disable-next-line @typescript-eslint/naming-convention, @typescript-eslint/no-explicit-any - private readonly _Ember: any; - - /** - * @inheritDoc - */ - // eslint-disable-next-line @typescript-eslint/no-explicit-any - public constructor(options: { Ember?: any } = {}) { - // eslint-disable-next-line @typescript-eslint/no-explicit-any, @typescript-eslint/no-unsafe-member-access - this._Ember = options.Ember || getGlobalObject().Ember; - } - - /** - * @inheritDoc - */ - public setupOnce(_: (callback: EventProcessor) => void, getCurrentHub: () => Hub): void { - if (!this._Ember) { - IS_DEBUG_BUILD && logger.error('EmberIntegration is missing an Ember instance'); - return; - } - - /* eslint-disable @typescript-eslint/no-unsafe-member-access */ - const oldOnError = this._Ember.onerror; - - this._Ember.onerror = (error: Error): void => { - if (getCurrentHub().getIntegration(Ember)) { - getCurrentHub().captureException(error, { originalException: error }); - } - - if (typeof oldOnError === 'function') { - oldOnError.call(this._Ember, error); - } else if (this._Ember.testing) { - throw error; - } - }; - - // eslint-disable-next-line @typescript-eslint/no-explicit-any - this._Ember.RSVP.on('error', (reason: unknown): void => { - if (getCurrentHub().getIntegration(Ember)) { - getCurrentHub().withScope(scope => { - if (isInstanceOf(reason, Error)) { - scope.setExtra('context', 'Unhandled Promise error detected'); - getCurrentHub().captureException(reason, { originalException: reason as Error }); - } else { - scope.setExtra('reason', reason); - getCurrentHub().captureMessage('Unhandled Promise error detected'); - } - }); - } - }); - } - /* eslint-enable @typescript-eslint/no-unsafe-member-access */ -} diff --git a/packages/integrations/src/index.ts b/packages/integrations/src/index.ts index f1ba52e92026..9a2573ee5a44 100644 --- a/packages/integrations/src/index.ts +++ b/packages/integrations/src/index.ts @@ -1,12 +1,9 @@ -export { Angular } from './angular'; export { CaptureConsole } from './captureconsole'; export { Debug } from './debug'; export { Dedupe } from './dedupe'; -export { Ember } from './ember'; export { ExtraErrorData } from './extraerrordata'; export { Offline } from './offline'; export { ReportingObserver } from './reportingobserver'; export { RewriteFrames } from './rewriteframes'; export { SessionTiming } from './sessiontiming'; export { Transaction } from './transaction'; -export { Vue } from './vue'; diff --git a/packages/integrations/src/vue.ts b/packages/integrations/src/vue.ts deleted file mode 100644 index 12c55f53df32..000000000000 --- a/packages/integrations/src/vue.ts +++ /dev/null @@ -1,417 +0,0 @@ -/* eslint-disable max-lines */ -/* eslint-disable @typescript-eslint/no-explicit-any */ -import { EventProcessor, Hub, Integration, IntegrationClass, Scope, Span, Transaction } from '@sentry/types'; -import { basename, getGlobalObject, logger, timestampWithMs } from '@sentry/utils'; - -import { IS_DEBUG_BUILD } from './flags'; - -/** - * Used to extract BrowserTracing integration from @sentry/tracing - */ -const BROWSER_TRACING_GETTER = { - id: 'BrowserTracing', -} as any as IntegrationClass; - -const VUE_OP = 'ui.vue'; - -/** Global Vue object limited to the methods/attributes we require */ -interface VueInstance { - config: { - errorHandler?(error: Error, vm?: ViewModel, info?: string): void; - }; - util?: { - warn(...input: any): void; - }; - mixin(hooks: { [key: string]: () => void }): void; -} - -/** Representation of Vue component internals */ -interface ViewModel { - [key: string]: any; - // eslint-disable-next-line @typescript-eslint/ban-types - $root: object; - $options: { - [key: string]: any; - name?: string; - propsData?: { [key: string]: any }; - _componentTag?: string; - __file?: string; - $_sentryPerfHook?: boolean; - }; - $once(hook: string, cb: () => void): void; -} - -/** Vue Integration configuration */ -interface IntegrationOptions { - /** Vue instance to be used inside the integration */ - Vue: VueInstance; - - /** - * When set to `false`, Sentry will suppress reporting of all props data - * from your Vue components for privacy concerns. - */ - attachProps: boolean; - /** - * When set to `true`, original Vue's `logError` will be called as well. - * https://github.com/vuejs/vue/blob/c2b1cfe9ccd08835f2d99f6ce60f67b4de55187f/src/core/util/error.js#L38-L48 - */ - logErrors: boolean; - - /** - * When set to `true`, enables tracking of components lifecycle performance. - * It requires `Tracing` integration to be also enabled. - */ - tracing: boolean; - - /** {@link TracingOptions} */ - tracingOptions: TracingOptions; -} - -/** Vue specific configuration for Tracing Integration */ -interface TracingOptions { - /** - * Decides whether to track components by hooking into its lifecycle methods. - * Can be either set to `boolean` to enable/disable tracking for all of them. - * Or to an array of specific component names (case-sensitive). - */ - trackComponents: boolean | string[]; - /** How long to wait until the tracked root activity is marked as finished and sent of to Sentry */ - timeout: number; - /** - * List of hooks to keep track of during component lifecycle. - * Available hooks: 'activate' | 'create' | 'destroy' | 'mount' | 'update' - * Based on https://vuejs.org/v2/api/#Options-Lifecycle-Hooks - */ - hooks: Operation[]; -} - -/** Optional metadata attached to Sentry Event */ -interface Metadata { - [key: string]: any; - componentName?: string; - propsData?: { [key: string]: any }; - lifecycleHook?: string; -} - -// https://vuejs.org/v2/api/#Options-Lifecycle-Hooks -type Hook = - | 'activated' - | 'beforeCreate' - | 'beforeDestroy' - | 'beforeMount' - | 'beforeUpdate' - | 'created' - | 'deactivated' - | 'destroyed' - | 'mounted' - | 'updated'; - -type Operation = 'activate' | 'create' | 'destroy' | 'mount' | 'update'; - -// Mappings from operation to corresponding lifecycle hook. -const HOOKS: { [key in Operation]: Hook[] } = { - activate: ['activated', 'deactivated'], - create: ['beforeCreate', 'created'], - destroy: ['beforeDestroy', 'destroyed'], - mount: ['beforeMount', 'mounted'], - update: ['beforeUpdate', 'updated'], -}; - -const COMPONENT_NAME_REGEXP = /(?:^|[-_/])(\w)/g; -const ROOT_COMPONENT_NAME = 'root'; -const ANONYMOUS_COMPONENT_NAME = 'anonymous component'; - -/** JSDoc */ -export class Vue implements Integration { - /** - * @inheritDoc - */ - public static id: string = 'Vue'; - - /** - * @inheritDoc - */ - public name: string = Vue.id; - - private readonly _options: IntegrationOptions; - - /** - * Cache holding already processed component names - */ - private readonly _componentsCache: { [key: string]: string } = {}; - private _rootSpan?: Span; - private _rootSpanTimer?: ReturnType; - - /** - * @inheritDoc - */ - public constructor( - options: Partial & { tracingOptions: Partial }>, - ) { - IS_DEBUG_BUILD && logger.log('You are still using the Vue.js integration, consider moving to @sentry/vue'); - this._options = { - // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access - Vue: getGlobalObject().Vue, - attachProps: true, - logErrors: false, - tracing: false, - ...options, - tracingOptions: { - hooks: ['mount', 'update'], - timeout: 2000, - trackComponents: false, - ...options.tracingOptions, - }, - }; - } - - /** - * @inheritDoc - */ - public setupOnce(_: (callback: EventProcessor) => void, getCurrentHub: () => Hub): void { - if (!this._options.Vue) { - IS_DEBUG_BUILD && logger.error('Vue integration is missing a Vue instance'); - return; - } - - this._attachErrorHandler(getCurrentHub); - - if (this._options.tracing) { - this._startTracing(getCurrentHub); - } - } - - /** - * Extract component name from the ViewModel - */ - private _getComponentName(vm: ViewModel): string { - // Such level of granularity is most likely not necessary, but better safe than sorry. — Kamil - if (!vm) { - return ANONYMOUS_COMPONENT_NAME; - } - - if (vm.$root === vm) { - return ROOT_COMPONENT_NAME; - } - - if (!vm.$options) { - return ANONYMOUS_COMPONENT_NAME; - } - - if (vm.$options.name) { - return vm.$options.name; - } - - if (vm.$options._componentTag) { - return vm.$options._componentTag; - } - - // injected by vue-loader - if (vm.$options.__file) { - const unifiedFile = vm.$options.__file.replace(/^[a-zA-Z]:/, '').replace(/\\/g, '/'); - const filename = basename(unifiedFile, '.vue'); - return ( - this._componentsCache[filename] || - (this._componentsCache[filename] = filename.replace(COMPONENT_NAME_REGEXP, (_, c: string) => - c ? c.toUpperCase() : '', - )) - ); - } - - return ANONYMOUS_COMPONENT_NAME; - } - - /** Keep it as attribute function, to keep correct `this` binding inside the hooks callbacks */ - // eslint-disable-next-line @typescript-eslint/typedef - private readonly _applyTracingHooks = (vm: ViewModel, getCurrentHub: () => Hub): void => { - // Don't attach twice, just in case - if (vm.$options.$_sentryPerfHook) { - return; - } - vm.$options.$_sentryPerfHook = true; - - const name = this._getComponentName(vm); - const rootMount = name === ROOT_COMPONENT_NAME; - const spans: { [key: string]: Span } = {}; - - // Render hook starts after once event is emitted, - // but it ends before the second event of the same type. - // - // Because of this, we start measuring inside the first event, - // but finish it before it triggers, to skip the event emitter timing itself. - const rootHandler = (hook: Hook): void => { - const now = timestampWithMs(); - - // On the first handler call (before), it'll be undefined, as `$once` will add it in the future. - // However, on the second call (after), it'll be already in place. - if (this._rootSpan) { - this._finishRootSpan(now, getCurrentHub); - } else { - vm.$once(`hook:${hook}`, () => { - // Create an activity on the first event call. There'll be no second call, as rootSpan will be in place, - // thus new event handler won't be attached. - const activeTransaction = getActiveTransaction(getCurrentHub()); - if (activeTransaction) { - this._rootSpan = activeTransaction.startChild({ - description: 'Application Render', - op: VUE_OP, - }); - } - /* eslint-enable @typescript-eslint/no-unsafe-member-access */ - }); - } - }; - - const childHandler = (hook: Hook, operation: Operation): void => { - // Skip components that we don't want to track to minimize the noise and give a more granular control to the user - const shouldTrack = Array.isArray(this._options.tracingOptions.trackComponents) - ? this._options.tracingOptions.trackComponents.indexOf(name) > -1 - : this._options.tracingOptions.trackComponents; - - if (!this._rootSpan || !shouldTrack) { - return; - } - - const now = timestampWithMs(); - const span = spans[operation]; - - // On the first handler call (before), it'll be undefined, as `$once` will add it in the future. - // However, on the second call (after), it'll be already in place. - if (span) { - span.finish(); - this._finishRootSpan(now, getCurrentHub); - } else { - vm.$once(`hook:${hook}`, () => { - if (this._rootSpan) { - spans[operation] = this._rootSpan.startChild({ - description: `Vue <${name}>`, - op: `${VUE_OP}.${operation}`, - }); - } - }); - } - }; - - // Each component has it's own scope, so all activities are only related to one of them - this._options.tracingOptions.hooks.forEach(operation => { - // Retrieve corresponding hooks from Vue lifecycle. - // eg. mount => ['beforeMount', 'mounted'] - const internalHooks = HOOKS[operation]; - - if (!internalHooks) { - IS_DEBUG_BUILD && logger.warn(`Unknown hook: ${operation}`); - return; - } - - internalHooks.forEach(internalHook => { - const handler = rootMount - ? rootHandler.bind(this, internalHook) - : childHandler.bind(this, internalHook, operation); - const currentValue = vm.$options[internalHook]; - - if (Array.isArray(currentValue)) { - vm.$options[internalHook] = [handler, ...currentValue]; - } else if (typeof currentValue === 'function') { - vm.$options[internalHook] = [handler, currentValue]; - } else { - vm.$options[internalHook] = [handler]; - } - }); - }); - }; - - /** Finish top-level span and activity with a debounce configured using `timeout` option */ - private _finishRootSpan(timestamp: number, _getCurrentHub: () => Hub): void { - if (this._rootSpanTimer) { - clearTimeout(this._rootSpanTimer); - } - - this._rootSpanTimer = setTimeout(() => { - // We should always finish the span - if (this._rootSpan) { - this._rootSpan.finish(timestamp); - } - }, this._options.tracingOptions.timeout); - } - - /** Inject configured tracing hooks into Vue's component lifecycles */ - private _startTracing(getCurrentHub: () => Hub): void { - const applyTracingHooks = this._applyTracingHooks; - - this._options.Vue.mixin({ - beforeCreate(this: ViewModel): void { - if (getCurrentHub().getIntegration(BROWSER_TRACING_GETTER)) { - // `this` points to currently rendered component - applyTracingHooks(this, getCurrentHub); - } else { - IS_DEBUG_BUILD && - logger.error('Vue integration has tracing enabled, but Tracing integration is not configured'); - } - }, - }); - } - - /** Inject Sentry's handler into owns Vue's error handler */ - private _attachErrorHandler(getCurrentHub: () => Hub): void { - // eslint-disable-next-line @typescript-eslint/unbound-method - const currentErrorHandler = this._options.Vue.config.errorHandler; - - this._options.Vue.config.errorHandler = (error: Error, vm?: ViewModel, info?: string): void => { - const metadata: Metadata = {}; - - if (vm) { - try { - metadata.componentName = this._getComponentName(vm); - - if (this._options.attachProps) { - metadata.propsData = vm.$options.propsData; - } - } catch (_oO) { - IS_DEBUG_BUILD && logger.warn('Unable to extract metadata from Vue component.'); - } - } - - if (info) { - metadata.lifecycleHook = info; - } - - if (getCurrentHub().getIntegration(Vue)) { - // Capture exception in the next event loop, to make sure that all breadcrumbs are recorded in time. - setTimeout(() => { - getCurrentHub().withScope(scope => { - scope.setContext('vue', metadata); - getCurrentHub().captureException(error); - }); - }); - } - - if (typeof currentErrorHandler === 'function') { - currentErrorHandler.call(this._options.Vue, error, vm, info); - } - - if (this._options.logErrors) { - if (this._options.Vue.util) { - this._options.Vue.util.warn(`Error in ${info}: "${error && error.toString()}"`, vm); - } - // eslint-disable-next-line no-console - console.error(error); - } - }; - } -} - -interface HubType extends Hub { - getScope?(): Scope | undefined; -} - -/** Grabs active transaction off scope */ -export function getActiveTransaction(hub: HubType): T | undefined { - if (hub && hub.getScope) { - const scope = hub.getScope() as Scope; - if (scope) { - return scope.getTransaction() as T | undefined; - } - } - - return undefined; -} From e6149add0a0c336945fac0b01eadff5ee7a15435 Mon Sep 17 00:00:00 2001 From: Abhijeet Prasad Date: Fri, 8 Apr 2022 12:35:48 -0400 Subject: [PATCH 020/204] ref(types): Delete `SessionStatus` enum (#4890) --- packages/types/src/sessionstatus.ts | 13 ------------- 1 file changed, 13 deletions(-) delete mode 100644 packages/types/src/sessionstatus.ts diff --git a/packages/types/src/sessionstatus.ts b/packages/types/src/sessionstatus.ts deleted file mode 100644 index 339b4ea2f5e6..000000000000 --- a/packages/types/src/sessionstatus.ts +++ /dev/null @@ -1,13 +0,0 @@ -/** JSDoc - * @deprecated Use string literals - if you require type casting, cast to SessionStatus type - */ -export enum SessionStatus { - /** JSDoc */ - Ok = 'ok', - /** JSDoc */ - Exited = 'exited', - /** JSDoc */ - Crashed = 'crashed', - /** JSDoc */ - Abnormal = 'abnormal', -} From dcb1bb8cfca288593f7c271a4dc4e32b04f1b2b4 Mon Sep 17 00:00:00 2001 From: Abhijeet Prasad Date: Fri, 8 Apr 2022 14:04:04 -0400 Subject: [PATCH 021/204] meta: 7.0.0-alpha.0 changelog (#4892) --- CHANGELOG.md | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 3897e7bd58b4..a157a0ba3dab 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,28 @@ - "You miss 100 percent of the chances you don't take. — Wayne Gretzky" — Michael Scott +## 7.0.0-alpha.0 + +- **(breaking)** ref: Drop support for Node 6 (#4851) +- **(breaking)** ref(bundles): Stop publishing CDN bundles on npm (#4901) +- **(breaking)** ref(build): Rename `dist` directories to `cjs` (#4900) +- **(breaking)** ref(build): Update to TypeScript 3.8.3 (#4895) +- **(breaking)** ref(browser): Remove top level eventbuilder exports (#4887) +- **(breaking)** ref(core): Remove `whitelistUrls`/`blacklistUrls` (#4850) +- **(breaking)** ref(core): Delete `API` class (#4848) +- **(breaking)** ref(gatsby): Remove `Sentry` from window (#4857) +- **(breaking)** ref(hub): Remove `setTransaction` scope method (#4865) +- **(breaking)** ref(hub): Remove `getActiveDomain` (#4858) +- **(breaking)** ref(integrations): Remove old `angular`, `ember`, and `vue` integrations (#4893) +- **(breaking)** ref(node): Remove deprecated `frameContextLines` (#4884) +- **(breaking)** ref(tracing): Rename `registerRequestInstrumentation` -> `instrumentOutgoingRequests` (#4859) +- **(breaking)** ref(tracing): Delete deprecated `startSpan` and `child` methods (#4849) +- **(breaking)** ref(tracing): Remove references to `@sentry/apm` (#4845) +- **(breaking)** ref(types): Delete `SessionStatus` enum (#4890) +- **(breaking)** ref(types): Delete `RequestSessionStatus` enum (#4889) +- **(breaking)** ref(types): Remove deprecated `user` dsn field (#4864) +- **(breaking)** ref(types): Delete `Status` enum (#4891) + ## 6.19.7 - fix(react): Add children prop type to ErrorBoundary component (#4966) From 87e98944187621d2f2ccccc2e01652f2daaa7b5f Mon Sep 17 00:00:00 2001 From: Katie Byers Date: Sat, 9 Apr 2022 14:14:54 -0700 Subject: [PATCH 022/204] fix(build): Skip flags file when building integration bundles (#4906) In https://github.com/getsentry/sentry-javascript/pull/4842, a `flags.ts` file was added to each package to fix logger treeshaking when using webpack. Because of the way that webpack works, this file has to exist separately in each individual package, including in `@sentry/integrations`. It is not itself an integration, though, so we shouldn't be building a separate CDN bundle for it (let alone six versions of one). This fixes the build script so that we're no longer doing that. --- packages/integrations/scripts/buildBundles.sh | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/packages/integrations/scripts/buildBundles.sh b/packages/integrations/scripts/buildBundles.sh index 88c4887b0605..3e81a8e6e045 100644 --- a/packages/integrations/scripts/buildBundles.sh +++ b/packages/integrations/scripts/buildBundles.sh @@ -3,9 +3,10 @@ for filepath in ./src/*; do file=$(basename $filepath) - # the index file is only there for the purposes of npm builds - for the CDN we create a separate bundle for each - # integration - so we can skip it here - if [[ $file == "index.ts" ]]; then + # The index file is only there for the purposes of npm builds (for the CDN we create a separate bundle for each + # integration) and the flags file is just a helper for including or not including debug logging, whose contents gets + # incorporated into each of the individual integration bundles, so we can skip them both here. + if [[ $file == "index.ts" || $file == "flags.ts" ]]; then continue fi From bac2d91f2268cb846f476c8305272553417f815c Mon Sep 17 00:00:00 2001 From: Katie Byers Date: Mon, 11 Apr 2022 06:10:17 -0700 Subject: [PATCH 023/204] fix(dev): Standardize yarn `clean` scripts (#4909) Now that we have files like `rollup.config.js` and `jest.config.js` at the package root level, it's helpful if running `yarn clean` doesn't delete them. This fixes that problem, and also fixes a spot where the now-defunct package-top-level `esm` directory was still included in a `clean` script. --- packages/nextjs/package.json | 2 +- packages/utils/package.json | 2 +- packages/vue/package.json | 2 +- packages/wasm/package.json | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/packages/nextjs/package.json b/packages/nextjs/package.json index 569078be6d5d..d5dbb449e25b 100644 --- a/packages/nextjs/package.json +++ b/packages/nextjs/package.json @@ -57,7 +57,7 @@ "build:types:watch": "tsc -p tsconfig.types.json --watch", "build:npm": "ts-node ../../scripts/prepack.ts && npm pack ./build", "circularDepCheck": "madge --circular src/index.client.ts && madge --circular --exclude 'config/types\\.ts' src/index.server.ts # see https://github.com/pahen/madge/issues/306", - "clean": "rimraf build coverage *.js *.js.map *.d.ts", + "clean": "rimraf build coverage", "fix": "run-s fix:eslint fix:prettier", "fix:eslint": "eslint . --format stylish --fix", "fix:prettier": "prettier --write \"{src,test,scripts}/**/*.ts\"", diff --git a/packages/utils/package.json b/packages/utils/package.json index 85cff26960c2..c3c4edf28530 100644 --- a/packages/utils/package.json +++ b/packages/utils/package.json @@ -38,7 +38,7 @@ "build:types:watch": "tsc -p tsconfig.types.json --watch", "build:npm": "ts-node ../../scripts/prepack.ts && npm pack ./build", "circularDepCheck": "madge --circular src/index.ts", - "clean": "rimraf build coverage *.js *.js.map *.d.ts", + "clean": "rimraf build coverage", "fix": "run-s fix:eslint fix:prettier", "fix:eslint": "eslint . --format stylish --fix", "fix:prettier": "prettier --write \"{src,test,scripts}/**/*.ts\"", diff --git a/packages/vue/package.json b/packages/vue/package.json index 954b7b319059..5a25e9179224 100644 --- a/packages/vue/package.json +++ b/packages/vue/package.json @@ -46,7 +46,7 @@ "build:types:watch": "tsc -p tsconfig.types.json --watch", "build:npm": "ts-node ../../scripts/prepack.ts && npm pack ./build", "circularDepCheck": "madge --circular src/index.ts", - "clean": "rimraf esm build coverage", + "clean": "rimraf build coverage", "fix": "run-s fix:eslint fix:prettier", "fix:eslint": "eslint . --format stylish --fix", "fix:prettier": "prettier --write \"{src,test,scripts}/**/*.ts\"", diff --git a/packages/wasm/package.json b/packages/wasm/package.json index 14273e42a47b..a907afca91f7 100644 --- a/packages/wasm/package.json +++ b/packages/wasm/package.json @@ -45,7 +45,7 @@ "build:types:watch": "tsc -p tsconfig.types.json --watch", "build:npm": "ts-node ../../scripts/prepack.ts --bundles && npm pack ./build/npm", "circularDepCheck": "madge --circular src/index.ts", - "clean": "rimraf build coverage *.js.map *.d.ts", + "clean": "rimraf build coverage", "fix": "run-s fix:eslint fix:prettier", "fix:eslint": "eslint . --format stylish --fix", "fix:prettier": "prettier --write \"{src,test,scripts}/**/*.ts\"", From 62e71892404c965d430f1cb3d2fce1b10bae0e82 Mon Sep 17 00:00:00 2001 From: Katie Byers Date: Mon, 11 Apr 2022 06:11:39 -0700 Subject: [PATCH 024/204] chore(dev): Remove redundant `.gitignore` in `utils` package (#4905) The `.gitignore` file in the `utils` package isn't ignoring any current files which the main `.gitignore` (the one at the root level of the repo) isn't already ignoring, as evidenced by the fact that deleting the former doesn't cause anything to new to come to git's attention. What it will ignore, however, is future `.js` files (such as `rollup.config.js` and `jest.config.js`) which we don't want ignored (and which aren't ignored in any other package). This therefore removes the `.gitignore` in `utils`, in order to prevent that future problem. --- packages/utils/.gitignore | 5 ----- 1 file changed, 5 deletions(-) delete mode 100644 packages/utils/.gitignore diff --git a/packages/utils/.gitignore b/packages/utils/.gitignore deleted file mode 100644 index 5c5f1c199d85..000000000000 --- a/packages/utils/.gitignore +++ /dev/null @@ -1,5 +0,0 @@ -*.js.map -*.d.ts -*.js -!test/types/* -!.eslintrc.js From 53027de8ed188965f27408e0c527f850d61b9100 Mon Sep 17 00:00:00 2001 From: Katie Byers Date: Mon, 11 Apr 2022 06:49:41 -0700 Subject: [PATCH 025/204] ref(dev): Centralize jest config (#4907) Currently, we configure jest using a mix of `package.json` entries and `jest.config.js` files, and there's a great deal of repetition between configs. To make things DRYer and easier to update (as will happen in future PRs), this aims to fix that by creating a centralized jest config from which all others can inherit. To facilitate this inheritance, all config has been removed from `package.json` files and moved into `jest.config.js` files. This change was done in a few distinct stages: - Extracting the config which was identical across many packages into a central config file, and fixing a mistake they all contained, namely that they were using the regular tsconfig file rather than the test-specific one. - In the packages which were using exactly that config, creating a new `jest.config.js` file and inheriting directly from the central jest config with no changes. - In the packages whose config varied only slightly from the boilerplate config, creating a new `jest.config.js` file and inheriting from the central file with small changes. This also required adding `.tsx` files to the central config. - In the browser package, moving the existing `jest.config.js` for the unit tests to the repo root level and refactoring it to inherit from the central file. This also required specifying a coverage directory in the central config and modifying the browser package's yarn test commands. - In the node integration test package, refactoring the existing `jest.config.js` to inherit from the central file. This also required creating a test-specific tsconfig to match the one in other packages. - Finally, making a small optimization (narrowing the scope of where to look for tests) in the now universally-used central config. --- jest.config.js | 19 ++++++++++++++ packages/browser/jest.config.js | 7 +++++ packages/browser/package.json | 4 +-- packages/browser/test/unit/jest.config.js | 16 ------------ packages/core/jest.config.js | 1 + packages/core/package.json | 20 -------------- packages/gatsby/jest.config.js | 7 +++++ packages/gatsby/package.json | 26 ------------------- packages/hub/jest.config.js | 1 + packages/hub/package.json | 20 -------------- packages/integrations/jest.config.js | 1 + packages/integrations/package.json | 20 -------------- packages/minimal/jest.config.js | 1 + packages/minimal/package.json | 20 -------------- packages/nextjs/jest.config.js | 1 + packages/nextjs/package.json | 20 -------------- .../node-integration-tests/jest.config.js | 12 +++------ packages/node-integration-tests/tsconfig.json | 10 ++++--- .../node-integration-tests/tsconfig.test.json | 12 +++++++++ packages/node/jest.config.js | 1 + packages/node/package.json | 20 -------------- packages/react/jest.config.js | 6 +++++ packages/react/package.json | 23 ---------------- packages/serverless/jest.config.js | 1 + packages/serverless/package.json | 22 +--------------- packages/tracing/jest.config.js | 1 + packages/tracing/package.json | 20 -------------- packages/utils/jest.config.js | 1 + packages/utils/package.json | 20 -------------- packages/vue/jest.config.js | 6 +++++ packages/vue/package.json | 20 -------------- packages/wasm/jest.config.js | 3 +++ packages/wasm/package.json | 3 --- 33 files changed, 82 insertions(+), 283 deletions(-) create mode 100644 jest.config.js create mode 100644 packages/browser/jest.config.js delete mode 100644 packages/browser/test/unit/jest.config.js create mode 100644 packages/core/jest.config.js create mode 100644 packages/gatsby/jest.config.js create mode 100644 packages/hub/jest.config.js create mode 100644 packages/integrations/jest.config.js create mode 100644 packages/minimal/jest.config.js create mode 100644 packages/nextjs/jest.config.js create mode 100644 packages/node-integration-tests/tsconfig.test.json create mode 100644 packages/node/jest.config.js create mode 100644 packages/react/jest.config.js create mode 100644 packages/serverless/jest.config.js create mode 100644 packages/tracing/jest.config.js create mode 100644 packages/utils/jest.config.js create mode 100644 packages/vue/jest.config.js create mode 100644 packages/wasm/jest.config.js diff --git a/jest.config.js b/jest.config.js new file mode 100644 index 000000000000..411c1f269052 --- /dev/null +++ b/jest.config.js @@ -0,0 +1,19 @@ +module.exports = { + rootDir: process.cwd(), + collectCoverage: true, + transform: { + '^.+\\.ts$': 'ts-jest', + '^.+\\.tsx$': 'ts-jest', + }, + coverageDirectory: '/coverage', + moduleFileExtensions: ['js', 'ts', 'tsx'], + testEnvironment: 'node', + testMatch: ['/**/*.test.ts', '/**/*.test.tsx'], + globals: { + 'ts-jest': { + tsconfig: '/tsconfig.test.json', + diagnostics: false, + }, + }, + testPathIgnorePatterns: ['/build/', '/node_modules/'], +}; diff --git a/packages/browser/jest.config.js b/packages/browser/jest.config.js new file mode 100644 index 000000000000..06b833307dd8 --- /dev/null +++ b/packages/browser/jest.config.js @@ -0,0 +1,7 @@ +const baseConfig = require('../../jest.config.js'); + +module.exports = { + ...baseConfig, + testEnvironment: 'jsdom', + testMatch: ['/test/unit/**/*.test.ts'], +}; diff --git a/packages/browser/package.json b/packages/browser/package.json index d7a317f9370e..e8c72077676a 100644 --- a/packages/browser/package.json +++ b/packages/browser/package.json @@ -72,11 +72,11 @@ "size:check:es5": "cat build/bundles/bundle.min.js | gzip -9 | wc -c | awk '{$1=$1/1024; print \"ES5: \",$1,\"kB\";}'", "size:check:es6": "cat build/bundles/bundle.es6.min.js | gzip -9 | wc -c | awk '{$1=$1/1024; print \"ES6: \",$1,\"kB\";}'", "test": "run-s test:unit", - "test:unit": "jest --config test/unit/jest.config.js", + "test:unit": "jest", "test:integration": "test/integration/run.js", "test:integration:checkbrowsers": "node scripts/checkbrowsers.js", "test:package": "node test/package/npm-build.js && rm test/package/tmp.js", - "test:unit:watch": "jest --config test/unit/jest.config.js --watch", + "test:unit:watch": "jest --watch", "test:integration:watch": "test/integration/run.js --watch" }, "volta": { diff --git a/packages/browser/test/unit/jest.config.js b/packages/browser/test/unit/jest.config.js deleted file mode 100644 index 5495646bc33f..000000000000 --- a/packages/browser/test/unit/jest.config.js +++ /dev/null @@ -1,16 +0,0 @@ -module.exports = { - transform: { - '^.+\\.ts$': 'ts-jest', - }, - collectCoverage: true, - coverageDirectory: '../../coverage', - moduleFileExtensions: ['js', 'ts'], - testEnvironment: 'jsdom', - testMatch: ['**/*.test.ts'], - globals: { - 'ts-jest': { - tsConfig: '../../tsconfig.json', - diagnostics: false, - }, - }, -}; diff --git a/packages/core/jest.config.js b/packages/core/jest.config.js new file mode 100644 index 000000000000..58141f076dc4 --- /dev/null +++ b/packages/core/jest.config.js @@ -0,0 +1 @@ +module.exports = require('../../jest.config.js'); diff --git a/packages/core/package.json b/packages/core/package.json index 13af6977e228..bf127194994c 100644 --- a/packages/core/package.json +++ b/packages/core/package.json @@ -52,25 +52,5 @@ "volta": { "extends": "../../package.json" }, - "jest": { - "collectCoverage": true, - "transform": { - "^.+\\.ts$": "ts-jest" - }, - "moduleFileExtensions": [ - "js", - "ts" - ], - "testEnvironment": "node", - "testMatch": [ - "**/*.test.ts" - ], - "globals": { - "ts-jest": { - "tsConfig": "./tsconfig.json", - "diagnostics": false - } - } - }, "sideEffects": false } diff --git a/packages/gatsby/jest.config.js b/packages/gatsby/jest.config.js new file mode 100644 index 000000000000..6ea22d92d139 --- /dev/null +++ b/packages/gatsby/jest.config.js @@ -0,0 +1,7 @@ +const baseConfig = require('../../jest.config.js'); + +module.exports = { + ...baseConfig, + setupFiles: ['/test/setEnvVars.ts'], + testEnvironment: 'jsdom', +}; diff --git a/packages/gatsby/package.json b/packages/gatsby/package.json index 04d2530fdf24..4b5a25b74983 100644 --- a/packages/gatsby/package.json +++ b/packages/gatsby/package.json @@ -62,31 +62,5 @@ "volta": { "extends": "../../package.json" }, - "jest": { - "collectCoverage": true, - "transform": { - "^.+\\.ts$": "ts-jest", - "^.+\\.tsx$": "ts-jest" - }, - "moduleFileExtensions": [ - "js", - "ts", - "tsx" - ], - "testEnvironment": "jsdom", - "testMatch": [ - "**/*.test.ts", - "**/*.test.tsx" - ], - "globals": { - "ts-jest": { - "tsConfig": "./tsconfig.json", - "diagnostics": false - } - }, - "setupFiles": [ - "/test/setEnvVars.ts" - ] - }, "sideEffects": false } diff --git a/packages/hub/jest.config.js b/packages/hub/jest.config.js new file mode 100644 index 000000000000..58141f076dc4 --- /dev/null +++ b/packages/hub/jest.config.js @@ -0,0 +1 @@ +module.exports = require('../../jest.config.js'); diff --git a/packages/hub/package.json b/packages/hub/package.json index 900e048cd735..909cc63fcb12 100644 --- a/packages/hub/package.json +++ b/packages/hub/package.json @@ -49,25 +49,5 @@ "volta": { "extends": "../../package.json" }, - "jest": { - "collectCoverage": true, - "transform": { - "^.+\\.ts$": "ts-jest" - }, - "moduleFileExtensions": [ - "js", - "ts" - ], - "testEnvironment": "node", - "testMatch": [ - "**/*.test.ts" - ], - "globals": { - "ts-jest": { - "tsConfig": "./tsconfig.json", - "diagnostics": false - } - } - }, "sideEffects": false } diff --git a/packages/integrations/jest.config.js b/packages/integrations/jest.config.js new file mode 100644 index 000000000000..58141f076dc4 --- /dev/null +++ b/packages/integrations/jest.config.js @@ -0,0 +1 @@ +module.exports = require('../../jest.config.js'); diff --git a/packages/integrations/package.json b/packages/integrations/package.json index 4cab47b436b2..ceeba98954df 100644 --- a/packages/integrations/package.json +++ b/packages/integrations/package.json @@ -54,25 +54,5 @@ "volta": { "extends": "../../package.json" }, - "jest": { - "collectCoverage": true, - "transform": { - "^.+\\.ts$": "ts-jest" - }, - "moduleFileExtensions": [ - "js", - "ts" - ], - "testEnvironment": "node", - "testMatch": [ - "**/*.test.ts" - ], - "globals": { - "ts-jest": { - "tsConfig": "./tsconfig.json", - "diagnostics": false - } - } - }, "sideEffects": false } diff --git a/packages/minimal/jest.config.js b/packages/minimal/jest.config.js new file mode 100644 index 000000000000..58141f076dc4 --- /dev/null +++ b/packages/minimal/jest.config.js @@ -0,0 +1 @@ +module.exports = require('../../jest.config.js'); diff --git a/packages/minimal/package.json b/packages/minimal/package.json index e2e1b9286241..87d3bca84522 100644 --- a/packages/minimal/package.json +++ b/packages/minimal/package.json @@ -49,25 +49,5 @@ "volta": { "extends": "../../package.json" }, - "jest": { - "collectCoverage": true, - "transform": { - "^.+\\.ts$": "ts-jest" - }, - "moduleFileExtensions": [ - "js", - "ts" - ], - "testEnvironment": "node", - "testMatch": [ - "**/*.test.ts" - ], - "globals": { - "ts-jest": { - "tsConfig": "./tsconfig.json", - "diagnostics": false - } - } - }, "sideEffects": false } diff --git a/packages/nextjs/jest.config.js b/packages/nextjs/jest.config.js new file mode 100644 index 000000000000..58141f076dc4 --- /dev/null +++ b/packages/nextjs/jest.config.js @@ -0,0 +1 @@ +module.exports = require('../../jest.config.js'); diff --git a/packages/nextjs/package.json b/packages/nextjs/package.json index d5dbb449e25b..122a62f6983a 100644 --- a/packages/nextjs/package.json +++ b/packages/nextjs/package.json @@ -76,26 +76,6 @@ "volta": { "extends": "../../package.json" }, - "jest": { - "collectCoverage": true, - "transform": { - "^.+\\.ts$": "ts-jest" - }, - "moduleFileExtensions": [ - "js", - "ts" - ], - "testEnvironment": "node", - "testMatch": [ - "**/*.test.ts" - ], - "globals": { - "ts-jest": { - "tsConfig": "./tsconfig.json", - "diagnostics": false - } - } - }, "sideEffects": [ "./cjs/index.server.js", "./esm/index.server.js", diff --git a/packages/node-integration-tests/jest.config.js b/packages/node-integration-tests/jest.config.js index 8edea137a75b..617a25ea0817 100644 --- a/packages/node-integration-tests/jest.config.js +++ b/packages/node-integration-tests/jest.config.js @@ -1,10 +1,6 @@ -const config = { - transform: { - '^.+\\.ts$': 'ts-jest', - }, - testEnvironment: 'node', +const baseConfig = require('../../jest.config.js'); + +module.exports = { + ...baseConfig, testMatch: ['**/test.ts'], - moduleFileExtensions: ['js', 'ts'], }; - -module.exports = config; diff --git a/packages/node-integration-tests/tsconfig.json b/packages/node-integration-tests/tsconfig.json index 87d045dbc42d..c98602a3af23 100644 --- a/packages/node-integration-tests/tsconfig.json +++ b/packages/node-integration-tests/tsconfig.json @@ -1,9 +1,11 @@ { "extends": "../../tsconfig.json", + + "include": ["**/*.ts"], + "compilerOptions": { + // package-specific options "esModuleInterop": true, - "types": ["jest", "node"] - }, - "include": ["**/*.ts", "jest.config.js"], - "exclude": ["node_modules"] + "types": ["node"] + } } diff --git a/packages/node-integration-tests/tsconfig.test.json b/packages/node-integration-tests/tsconfig.test.json new file mode 100644 index 000000000000..b43ec254274b --- /dev/null +++ b/packages/node-integration-tests/tsconfig.test.json @@ -0,0 +1,12 @@ +{ + "extends": "./tsconfig.json", + + "include": ["**/*.ts"], + + "compilerOptions": { + // should include all types from `./tsconfig.json` plus types for all test frameworks used + "types": ["node", "jest"] + + // other package-specific, test-specific options + } +} diff --git a/packages/node/jest.config.js b/packages/node/jest.config.js new file mode 100644 index 000000000000..58141f076dc4 --- /dev/null +++ b/packages/node/jest.config.js @@ -0,0 +1 @@ +module.exports = require('../../jest.config.js'); diff --git a/packages/node/package.json b/packages/node/package.json index 82e33852b768..41dc9be47185 100644 --- a/packages/node/package.json +++ b/packages/node/package.json @@ -65,25 +65,5 @@ }, "volta": { "extends": "../../package.json" - }, - "jest": { - "collectCoverage": true, - "transform": { - "^.+\\.ts$": "ts-jest" - }, - "moduleFileExtensions": [ - "js", - "ts" - ], - "testEnvironment": "node", - "testMatch": [ - "**/*.test.ts" - ], - "globals": { - "ts-jest": { - "tsConfig": "./tsconfig.json", - "diagnostics": false - } - } } } diff --git a/packages/react/jest.config.js b/packages/react/jest.config.js new file mode 100644 index 000000000000..8baa8e01db0c --- /dev/null +++ b/packages/react/jest.config.js @@ -0,0 +1,6 @@ +const baseConfig = require('../../jest.config.js'); + +module.exports = { + ...baseConfig, + testEnvironment: 'jsdom', +}; diff --git a/packages/react/package.json b/packages/react/package.json index 5be6217ff3ce..5709b480521f 100644 --- a/packages/react/package.json +++ b/packages/react/package.json @@ -77,28 +77,5 @@ "volta": { "extends": "../../package.json" }, - "jest": { - "collectCoverage": true, - "transform": { - "^.+\\.ts$": "ts-jest", - "^.+\\.tsx$": "ts-jest" - }, - "moduleFileExtensions": [ - "js", - "ts", - "tsx" - ], - "testEnvironment": "jsdom", - "testMatch": [ - "**/*.test.ts", - "**/*.test.tsx" - ], - "globals": { - "ts-jest": { - "tsConfig": "./tsconfig.json", - "diagnostics": false - } - } - }, "sideEffects": false } diff --git a/packages/serverless/jest.config.js b/packages/serverless/jest.config.js new file mode 100644 index 000000000000..58141f076dc4 --- /dev/null +++ b/packages/serverless/jest.config.js @@ -0,0 +1 @@ +module.exports = require('../../jest.config.js'); diff --git a/packages/serverless/package.json b/packages/serverless/package.json index 4497263841a2..4c9f9ce90dbd 100644 --- a/packages/serverless/package.json +++ b/packages/serverless/package.json @@ -68,25 +68,5 @@ "volta": { "extends": "../../package.json" }, - "sideEffects": false, - "jest": { - "collectCoverage": true, - "transform": { - "^.+\\.ts$": "ts-jest" - }, - "moduleFileExtensions": [ - "js", - "ts" - ], - "testEnvironment": "node", - "testMatch": [ - "**/*.test.ts" - ], - "globals": { - "ts-jest": { - "tsConfig": "./tsconfig.json", - "diagnostics": false - } - } - } + "sideEffects": false } diff --git a/packages/tracing/jest.config.js b/packages/tracing/jest.config.js new file mode 100644 index 000000000000..58141f076dc4 --- /dev/null +++ b/packages/tracing/jest.config.js @@ -0,0 +1 @@ +module.exports = require('../../jest.config.js'); diff --git a/packages/tracing/package.json b/packages/tracing/package.json index 4f1048c4a1ea..472f7bca523e 100644 --- a/packages/tracing/package.json +++ b/packages/tracing/package.json @@ -59,26 +59,6 @@ "volta": { "extends": "../../package.json" }, - "jest": { - "collectCoverage": true, - "transform": { - "^.+\\.ts$": "ts-jest" - }, - "moduleFileExtensions": [ - "js", - "ts" - ], - "testEnvironment": "node", - "testMatch": [ - "**/*.test.ts" - ], - "globals": { - "ts-jest": { - "tsConfig": "./tsconfig.json", - "diagnostics": false - } - } - }, "sideEffects": [ "./dist/index.js", "./esm/index.js", diff --git a/packages/utils/jest.config.js b/packages/utils/jest.config.js new file mode 100644 index 000000000000..58141f076dc4 --- /dev/null +++ b/packages/utils/jest.config.js @@ -0,0 +1 @@ +module.exports = require('../../jest.config.js'); diff --git a/packages/utils/package.json b/packages/utils/package.json index c3c4edf28530..a0cd2360f65d 100644 --- a/packages/utils/package.json +++ b/packages/utils/package.json @@ -53,25 +53,5 @@ "volta": { "extends": "../../package.json" }, - "jest": { - "collectCoverage": true, - "transform": { - "^.+\\.ts$": "ts-jest" - }, - "moduleFileExtensions": [ - "js", - "ts" - ], - "testEnvironment": "node", - "testMatch": [ - "**/*.test.ts" - ], - "globals": { - "ts-jest": { - "tsConfig": "./tsconfig.json", - "diagnostics": false - } - } - }, "sideEffects": false } diff --git a/packages/vue/jest.config.js b/packages/vue/jest.config.js new file mode 100644 index 000000000000..8baa8e01db0c --- /dev/null +++ b/packages/vue/jest.config.js @@ -0,0 +1,6 @@ +const baseConfig = require('../../jest.config.js'); + +module.exports = { + ...baseConfig, + testEnvironment: 'jsdom', +}; diff --git a/packages/vue/package.json b/packages/vue/package.json index 5a25e9179224..dc627ee6e881 100644 --- a/packages/vue/package.json +++ b/packages/vue/package.json @@ -60,25 +60,5 @@ "volta": { "extends": "../../package.json" }, - "jest": { - "collectCoverage": true, - "transform": { - "^.+\\.ts$": "ts-jest" - }, - "moduleFileExtensions": [ - "js", - "ts" - ], - "testEnvironment": "jsdom", - "testMatch": [ - "**/*.test.ts" - ], - "globals": { - "ts-jest": { - "tsConfig": "./tsconfig.json", - "diagnostics": false - } - } - }, "sideEffects": false } diff --git a/packages/wasm/jest.config.js b/packages/wasm/jest.config.js new file mode 100644 index 000000000000..2762fa4ef2ff --- /dev/null +++ b/packages/wasm/jest.config.js @@ -0,0 +1,3 @@ +module.exports = { + preset: 'jest-puppeteer', +}; diff --git a/packages/wasm/package.json b/packages/wasm/package.json index a907afca91f7..2f3f73e175e9 100644 --- a/packages/wasm/package.json +++ b/packages/wasm/package.json @@ -59,8 +59,5 @@ "volta": { "extends": "../../package.json" }, - "jest": { - "preset": "jest-puppeteer" - }, "sideEffects": false } From f5a6ca86038820c67e4ddb695d594e612eabce5c Mon Sep 17 00:00:00 2001 From: getsentry-bot Date: Mon, 11 Apr 2022 13:54:26 +0000 Subject: [PATCH 026/204] release: 7.0.0-alpha.0 --- lerna.json | 2 +- packages/angular/package.json | 8 ++++---- packages/browser/package.json | 8 ++++---- packages/core/package.json | 10 +++++----- packages/core/src/version.ts | 2 +- packages/ember/package.json | 10 +++++----- packages/eslint-config-sdk/package.json | 6 +++--- packages/eslint-plugin-sdk/package.json | 2 +- packages/gatsby/package.json | 8 ++++---- packages/hub/package.json | 6 +++--- packages/integration-tests/package.json | 2 +- packages/integrations/package.json | 6 +++--- packages/minimal/package.json | 6 +++--- packages/nextjs/package.json | 18 +++++++++--------- packages/node-integration-tests/package.json | 2 +- packages/node/package.json | 10 +++++----- packages/react/package.json | 10 +++++----- packages/serverless/package.json | 12 ++++++------ packages/tracing/package.json | 12 ++++++------ packages/types/package.json | 2 +- packages/typescript/package.json | 2 +- packages/utils/package.json | 4 ++-- packages/vue/package.json | 12 ++++++------ packages/wasm/package.json | 6 +++--- 24 files changed, 83 insertions(+), 83 deletions(-) diff --git a/lerna.json b/lerna.json index 251bcc4fb2bc..ab5fdf259b84 100644 --- a/lerna.json +++ b/lerna.json @@ -1,6 +1,6 @@ { "lerna": "3.4.0", - "version": "6.19.7", + "version": "7.0.0-alpha.0", "packages": "packages/*", "npmClient": "yarn", "useWorkspaces": true diff --git a/packages/angular/package.json b/packages/angular/package.json index 5b5abb346c53..e92a3235d969 100644 --- a/packages/angular/package.json +++ b/packages/angular/package.json @@ -1,6 +1,6 @@ { "name": "@sentry/angular", - "version": "6.19.7", + "version": "7.0.0-alpha.0", "description": "Official Sentry SDK for Angular", "repository": "git://github.com/getsentry/sentry-javascript.git", "homepage": "https://github.com/getsentry/sentry-javascript/tree/master/packages/angular", @@ -21,9 +21,9 @@ "@angular/router": "10.x || 11.x || 12.x || 13.x" }, "dependencies": { - "@sentry/browser": "6.19.7", - "@sentry/types": "6.19.7", - "@sentry/utils": "6.19.7", + "@sentry/browser": "7.0.0-alpha.0", + "@sentry/types": "7.0.0-alpha.0", + "@sentry/utils": "7.0.0-alpha.0", "rxjs": "^6.6.0", "tslib": "^1.9.3" }, diff --git a/packages/browser/package.json b/packages/browser/package.json index e8c72077676a..d3a87d21d04a 100644 --- a/packages/browser/package.json +++ b/packages/browser/package.json @@ -1,6 +1,6 @@ { "name": "@sentry/browser", - "version": "6.19.7", + "version": "7.0.0-alpha.0", "description": "Official Sentry SDK for browsers", "repository": "git://github.com/getsentry/sentry-javascript.git", "homepage": "https://github.com/getsentry/sentry-javascript/tree/master/packages/browser", @@ -16,9 +16,9 @@ "access": "public" }, "dependencies": { - "@sentry/core": "6.19.7", - "@sentry/types": "6.19.7", - "@sentry/utils": "6.19.7", + "@sentry/core": "7.0.0-alpha.0", + "@sentry/types": "7.0.0-alpha.0", + "@sentry/utils": "7.0.0-alpha.0", "tslib": "^1.9.3" }, "devDependencies": { diff --git a/packages/core/package.json b/packages/core/package.json index bf127194994c..23ee48723424 100644 --- a/packages/core/package.json +++ b/packages/core/package.json @@ -1,6 +1,6 @@ { "name": "@sentry/core", - "version": "6.19.7", + "version": "7.0.0-alpha.0", "description": "Base implementation for all Sentry JavaScript SDKs", "repository": "git://github.com/getsentry/sentry-javascript.git", "homepage": "https://github.com/getsentry/sentry-javascript/tree/master/packages/core", @@ -16,10 +16,10 @@ "access": "public" }, "dependencies": { - "@sentry/hub": "6.19.7", - "@sentry/minimal": "6.19.7", - "@sentry/types": "6.19.7", - "@sentry/utils": "6.19.7", + "@sentry/hub": "7.0.0-alpha.0", + "@sentry/minimal": "7.0.0-alpha.0", + "@sentry/types": "7.0.0-alpha.0", + "@sentry/utils": "7.0.0-alpha.0", "tslib": "^1.9.3" }, "scripts": { diff --git a/packages/core/src/version.ts b/packages/core/src/version.ts index b774b10aabdc..485119673c97 100644 --- a/packages/core/src/version.ts +++ b/packages/core/src/version.ts @@ -1 +1 @@ -export const SDK_VERSION = '6.19.7'; +export const SDK_VERSION = '7.0.0-alpha.0'; diff --git a/packages/ember/package.json b/packages/ember/package.json index c4e58973cd53..3b22b69981b2 100644 --- a/packages/ember/package.json +++ b/packages/ember/package.json @@ -1,6 +1,6 @@ { "name": "@sentry/ember", - "version": "6.19.7", + "version": "7.0.0-alpha.0", "description": "Official Sentry SDK for Ember.js", "repository": "git://github.com/getsentry/sentry-javascript.git", "homepage": "https://github.com/getsentry/sentry-javascript/tree/master/packages/ember", @@ -29,10 +29,10 @@ }, "dependencies": { "@embroider/macros": "~0.47.2", - "@sentry/browser": "6.19.7", - "@sentry/tracing": "6.19.7", - "@sentry/types": "6.19.7", - "@sentry/utils": "6.19.7", + "@sentry/browser": "7.0.0-alpha.0", + "@sentry/tracing": "7.0.0-alpha.0", + "@sentry/types": "7.0.0-alpha.0", + "@sentry/utils": "7.0.0-alpha.0", "ember-auto-import": "~1.12.1 || ~2.2.0", "ember-cli-babel": "~7.26.6", "ember-cli-htmlbars": "^6.0.1", diff --git a/packages/eslint-config-sdk/package.json b/packages/eslint-config-sdk/package.json index f252027ee2c3..45a42c8472d0 100644 --- a/packages/eslint-config-sdk/package.json +++ b/packages/eslint-config-sdk/package.json @@ -1,6 +1,6 @@ { "name": "@sentry-internal/eslint-config-sdk", - "version": "6.19.7", + "version": "7.0.0-alpha.0", "description": "Official Sentry SDK eslint config", "repository": "git://github.com/getsentry/sentry-javascript.git", "homepage": "https://github.com/getsentry/sentry-javascript/tree/master/packages/eslint-config-sdk", @@ -19,8 +19,8 @@ "access": "public" }, "dependencies": { - "@sentry-internal/eslint-plugin-sdk": "6.19.7", - "@sentry-internal/typescript": "6.19.7", + "@sentry-internal/eslint-plugin-sdk": "7.0.0-alpha.0", + "@sentry-internal/typescript": "7.0.0-alpha.0", "@typescript-eslint/eslint-plugin": "^3.9.0", "@typescript-eslint/parser": "^3.9.0", "eslint-config-prettier": "^6.11.0", diff --git a/packages/eslint-plugin-sdk/package.json b/packages/eslint-plugin-sdk/package.json index ae823048fef2..938815cb33a3 100644 --- a/packages/eslint-plugin-sdk/package.json +++ b/packages/eslint-plugin-sdk/package.json @@ -1,6 +1,6 @@ { "name": "@sentry-internal/eslint-plugin-sdk", - "version": "6.19.7", + "version": "7.0.0-alpha.0", "description": "Official Sentry SDK eslint plugin", "repository": "git://github.com/getsentry/sentry-javascript.git", "homepage": "https://github.com/getsentry/sentry-javascript/tree/master/packages/eslint-plugin-sdk", diff --git a/packages/gatsby/package.json b/packages/gatsby/package.json index 4b5a25b74983..976ab9847663 100644 --- a/packages/gatsby/package.json +++ b/packages/gatsby/package.json @@ -1,6 +1,6 @@ { "name": "@sentry/gatsby", - "version": "6.19.7", + "version": "7.0.0-alpha.0", "description": "Official Sentry SDK for Gatsby.js", "repository": "git://github.com/getsentry/sentry-javascript.git", "homepage": "https://github.com/getsentry/sentry-javascript/tree/master/packages/gatsby", @@ -20,8 +20,8 @@ "access": "public" }, "dependencies": { - "@sentry/react": "6.19.7", - "@sentry/tracing": "6.19.7", + "@sentry/react": "7.0.0-alpha.0", + "@sentry/tracing": "7.0.0-alpha.0", "@sentry/webpack-plugin": "1.18.8" }, "peerDependencies": { @@ -29,7 +29,7 @@ "react": "15.x || 16.x || 17.x || 18.x" }, "devDependencies": { - "@sentry/types": "6.19.7", + "@sentry/types": "7.0.0-alpha.0", "@testing-library/react": "^13.0.0", "react": "^18.0.0" }, diff --git a/packages/hub/package.json b/packages/hub/package.json index 909cc63fcb12..8bb57aab02e2 100644 --- a/packages/hub/package.json +++ b/packages/hub/package.json @@ -1,6 +1,6 @@ { "name": "@sentry/hub", - "version": "6.19.7", + "version": "7.0.0-alpha.0", "description": "Sentry hub which handles global state managment.", "repository": "git://github.com/getsentry/sentry-javascript.git", "homepage": "https://github.com/getsentry/sentry-javascript/tree/master/packages/hub", @@ -16,8 +16,8 @@ "access": "public" }, "dependencies": { - "@sentry/types": "6.19.7", - "@sentry/utils": "6.19.7", + "@sentry/types": "7.0.0-alpha.0", + "@sentry/utils": "7.0.0-alpha.0", "tslib": "^1.9.3" }, "scripts": { diff --git a/packages/integration-tests/package.json b/packages/integration-tests/package.json index 3cec389dfaf3..5456ef0e19be 100644 --- a/packages/integration-tests/package.json +++ b/packages/integration-tests/package.json @@ -1,6 +1,6 @@ { "name": "@sentry-internal/browser-integration-tests", - "version": "6.19.7", + "version": "7.0.0-alpha.0", "main": "index.js", "license": "MIT", "engines": { diff --git a/packages/integrations/package.json b/packages/integrations/package.json index ceeba98954df..52f2c23ea924 100644 --- a/packages/integrations/package.json +++ b/packages/integrations/package.json @@ -1,6 +1,6 @@ { "name": "@sentry/integrations", - "version": "6.19.7", + "version": "7.0.0-alpha.0", "description": "Pluggable integrations that can be used to enhance JS SDKs", "repository": "git://github.com/getsentry/sentry-javascript.git", "homepage": "https://github.com/getsentry/sentry-javascript/tree/master/packages/integrations", @@ -16,8 +16,8 @@ "module": "build/npm/esm/index.js", "types": "build/npm/types/index.d.ts", "dependencies": { - "@sentry/types": "6.19.7", - "@sentry/utils": "6.19.7", + "@sentry/types": "7.0.0-alpha.0", + "@sentry/utils": "7.0.0-alpha.0", "localforage": "^1.8.1", "tslib": "^1.9.3" }, diff --git a/packages/minimal/package.json b/packages/minimal/package.json index 87d3bca84522..4154add1ca3d 100644 --- a/packages/minimal/package.json +++ b/packages/minimal/package.json @@ -1,6 +1,6 @@ { "name": "@sentry/minimal", - "version": "6.19.7", + "version": "7.0.0-alpha.0", "description": "Sentry minimal library that can be used in other packages", "repository": "git://github.com/getsentry/sentry-javascript.git", "homepage": "https://github.com/getsentry/sentry-javascript/tree/master/packages/minimal", @@ -16,8 +16,8 @@ "access": "public" }, "dependencies": { - "@sentry/hub": "6.19.7", - "@sentry/types": "6.19.7", + "@sentry/hub": "7.0.0-alpha.0", + "@sentry/types": "7.0.0-alpha.0", "tslib": "^1.9.3" }, "scripts": { diff --git a/packages/nextjs/package.json b/packages/nextjs/package.json index 122a62f6983a..5674ffc46956 100644 --- a/packages/nextjs/package.json +++ b/packages/nextjs/package.json @@ -1,6 +1,6 @@ { "name": "@sentry/nextjs", - "version": "6.19.7", + "version": "7.0.0-alpha.0", "description": "Official Sentry SDK for Next.js", "repository": "git://github.com/getsentry/sentry-javascript.git", "homepage": "https://github.com/getsentry/sentry-javascript/tree/master/packages/nextjs", @@ -17,18 +17,18 @@ "access": "public" }, "dependencies": { - "@sentry/core": "6.19.7", - "@sentry/hub": "6.19.7", - "@sentry/integrations": "6.19.7", - "@sentry/node": "6.19.7", - "@sentry/react": "6.19.7", - "@sentry/tracing": "6.19.7", - "@sentry/utils": "6.19.7", + "@sentry/core": "7.0.0-alpha.0", + "@sentry/hub": "7.0.0-alpha.0", + "@sentry/integrations": "7.0.0-alpha.0", + "@sentry/node": "7.0.0-alpha.0", + "@sentry/react": "7.0.0-alpha.0", + "@sentry/tracing": "7.0.0-alpha.0", + "@sentry/utils": "7.0.0-alpha.0", "@sentry/webpack-plugin": "1.18.8", "tslib": "^1.9.3" }, "devDependencies": { - "@sentry/types": "6.19.7", + "@sentry/types": "7.0.0-alpha.0", "@types/webpack": "^4.41.31", "next": "10.1.3" }, diff --git a/packages/node-integration-tests/package.json b/packages/node-integration-tests/package.json index e6014bd9a367..3f10098ba15b 100644 --- a/packages/node-integration-tests/package.json +++ b/packages/node-integration-tests/package.json @@ -1,6 +1,6 @@ { "name": "@sentry-internal/node-integration-tests", - "version": "6.19.7", + "version": "7.0.0-alpha.0", "license": "MIT", "engines": { "node": ">=10" diff --git a/packages/node/package.json b/packages/node/package.json index 41dc9be47185..f7a19037a029 100644 --- a/packages/node/package.json +++ b/packages/node/package.json @@ -1,6 +1,6 @@ { "name": "@sentry/node", - "version": "6.19.7", + "version": "7.0.0-alpha.0", "description": "Official Sentry SDK for Node.js", "repository": "git://github.com/getsentry/sentry-javascript.git", "homepage": "https://github.com/getsentry/sentry-javascript/tree/master/packages/node", @@ -16,10 +16,10 @@ "access": "public" }, "dependencies": { - "@sentry/core": "6.19.7", - "@sentry/hub": "6.19.7", - "@sentry/types": "6.19.7", - "@sentry/utils": "6.19.7", + "@sentry/core": "7.0.0-alpha.0", + "@sentry/hub": "7.0.0-alpha.0", + "@sentry/types": "7.0.0-alpha.0", + "@sentry/utils": "7.0.0-alpha.0", "cookie": "^0.4.1", "https-proxy-agent": "^5.0.0", "lru_map": "^0.3.3", diff --git a/packages/react/package.json b/packages/react/package.json index 5709b480521f..534283e00d07 100644 --- a/packages/react/package.json +++ b/packages/react/package.json @@ -1,6 +1,6 @@ { "name": "@sentry/react", - "version": "6.19.7", + "version": "7.0.0-alpha.0", "description": "Official Sentry SDK for React.js", "repository": "git://github.com/getsentry/sentry-javascript.git", "homepage": "https://github.com/getsentry/sentry-javascript/tree/master/packages/react", @@ -16,10 +16,10 @@ "access": "public" }, "dependencies": { - "@sentry/browser": "6.19.7", - "@sentry/minimal": "6.19.7", - "@sentry/types": "6.19.7", - "@sentry/utils": "6.19.7", + "@sentry/browser": "7.0.0-alpha.0", + "@sentry/minimal": "7.0.0-alpha.0", + "@sentry/types": "7.0.0-alpha.0", + "@sentry/utils": "7.0.0-alpha.0", "hoist-non-react-statics": "^3.3.2", "tslib": "^1.9.3" }, diff --git a/packages/serverless/package.json b/packages/serverless/package.json index 4c9f9ce90dbd..ac3374862764 100644 --- a/packages/serverless/package.json +++ b/packages/serverless/package.json @@ -1,6 +1,6 @@ { "name": "@sentry/serverless", - "version": "6.19.7", + "version": "7.0.0-alpha.0", "description": "Official Sentry SDK for various serverless solutions", "repository": "git://github.com/getsentry/sentry-javascript.git", "homepage": "https://github.com/getsentry/sentry-javascript/tree/master/packages/serverless", @@ -16,11 +16,11 @@ "access": "public" }, "dependencies": { - "@sentry/minimal": "6.19.7", - "@sentry/node": "6.19.7", - "@sentry/tracing": "6.19.7", - "@sentry/types": "6.19.7", - "@sentry/utils": "6.19.7", + "@sentry/minimal": "7.0.0-alpha.0", + "@sentry/node": "7.0.0-alpha.0", + "@sentry/tracing": "7.0.0-alpha.0", + "@sentry/types": "7.0.0-alpha.0", + "@sentry/utils": "7.0.0-alpha.0", "@types/aws-lambda": "^8.10.62", "@types/express": "^4.17.2", "tslib": "^1.9.3" diff --git a/packages/tracing/package.json b/packages/tracing/package.json index 472f7bca523e..d006f5dd25e7 100644 --- a/packages/tracing/package.json +++ b/packages/tracing/package.json @@ -1,6 +1,6 @@ { "name": "@sentry/tracing", - "version": "6.19.7", + "version": "7.0.0-alpha.0", "description": "Extensions for Sentry AM", "repository": "git://github.com/getsentry/sentry-javascript.git", "homepage": "https://github.com/getsentry/sentry-javascript/tree/master/packages/tracing", @@ -16,14 +16,14 @@ "access": "public" }, "dependencies": { - "@sentry/hub": "6.19.7", - "@sentry/minimal": "6.19.7", - "@sentry/types": "6.19.7", - "@sentry/utils": "6.19.7", + "@sentry/hub": "7.0.0-alpha.0", + "@sentry/minimal": "7.0.0-alpha.0", + "@sentry/types": "7.0.0-alpha.0", + "@sentry/utils": "7.0.0-alpha.0", "tslib": "^1.9.3" }, "devDependencies": { - "@sentry/browser": "6.19.7", + "@sentry/browser": "7.0.0-alpha.0", "@types/express": "^4.17.1", "@types/jsdom": "^16.2.3", "jsdom": "^16.2.2" diff --git a/packages/types/package.json b/packages/types/package.json index 111d71701430..2fc59e9a1535 100644 --- a/packages/types/package.json +++ b/packages/types/package.json @@ -1,6 +1,6 @@ { "name": "@sentry/types", - "version": "6.19.7", + "version": "7.0.0-alpha.0", "description": "Types for all Sentry JavaScript SDKs", "repository": "git://github.com/getsentry/sentry-javascript.git", "homepage": "https://github.com/getsentry/sentry-javascript/tree/master/packages/types", diff --git a/packages/typescript/package.json b/packages/typescript/package.json index 2b0ae182cd4c..338ab3906aef 100644 --- a/packages/typescript/package.json +++ b/packages/typescript/package.json @@ -1,6 +1,6 @@ { "name": "@sentry-internal/typescript", - "version": "6.19.7", + "version": "7.0.0-alpha.0", "description": "Typescript configuration used at Sentry", "repository": "git://github.com/getsentry/sentry-javascript.git", "homepage": "https://github.com/getsentry/sentry-javascript/tree/master/packages/typescript", diff --git a/packages/utils/package.json b/packages/utils/package.json index a0cd2360f65d..6d62d5602180 100644 --- a/packages/utils/package.json +++ b/packages/utils/package.json @@ -1,6 +1,6 @@ { "name": "@sentry/utils", - "version": "6.19.7", + "version": "7.0.0-alpha.0", "description": "Utilities for all Sentry JavaScript SDKs", "repository": "git://github.com/getsentry/sentry-javascript.git", "homepage": "https://github.com/getsentry/sentry-javascript/tree/master/packages/utils", @@ -16,7 +16,7 @@ "access": "public" }, "dependencies": { - "@sentry/types": "6.19.7", + "@sentry/types": "7.0.0-alpha.0", "tslib": "^1.9.3" }, "devDependencies": { diff --git a/packages/vue/package.json b/packages/vue/package.json index dc627ee6e881..fea81ff573a8 100644 --- a/packages/vue/package.json +++ b/packages/vue/package.json @@ -1,6 +1,6 @@ { "name": "@sentry/vue", - "version": "6.19.7", + "version": "7.0.0-alpha.0", "description": "Official Sentry SDK for Vue.js", "repository": "git://github.com/getsentry/sentry-javascript.git", "homepage": "https://github.com/getsentry/sentry-javascript/tree/master/packages/vue", @@ -16,11 +16,11 @@ "access": "public" }, "dependencies": { - "@sentry/browser": "6.19.7", - "@sentry/core": "6.19.7", - "@sentry/minimal": "6.19.7", - "@sentry/types": "6.19.7", - "@sentry/utils": "6.19.7", + "@sentry/browser": "7.0.0-alpha.0", + "@sentry/core": "7.0.0-alpha.0", + "@sentry/minimal": "7.0.0-alpha.0", + "@sentry/types": "7.0.0-alpha.0", + "@sentry/utils": "7.0.0-alpha.0", "tslib": "^1.9.3" }, "peerDependencies": { diff --git a/packages/wasm/package.json b/packages/wasm/package.json index 2f3f73e175e9..6cdf354f1f5e 100644 --- a/packages/wasm/package.json +++ b/packages/wasm/package.json @@ -1,6 +1,6 @@ { "name": "@sentry/wasm", - "version": "6.19.7", + "version": "7.0.0-alpha.0", "description": "Support for WASM.", "repository": "git://github.com/getsentry/sentry-javascript.git", "homepage": "https://github.com/getsentry/sentry-javascript/tree/master/packages/wasm", @@ -16,8 +16,8 @@ "access": "public" }, "dependencies": { - "@sentry/browser": "6.19.7", - "@sentry/types": "6.19.7", + "@sentry/browser": "7.0.0-alpha.0", + "@sentry/types": "7.0.0-alpha.0", "tslib": "^1.9.3" }, "devDependencies": { From b42472aba3c388ea88c0b581c0bd9e7a47abc71d Mon Sep 17 00:00:00 2001 From: Luca Forstner Date: Mon, 11 Apr 2022 20:19:29 +0200 Subject: [PATCH 027/204] feat(docs): Add migration guide for v7 (#4910) Documents: - ES6 for CJS files - Dropping Support for Node v6 - Removing Platform Integrations - New npm package structure - Deleting deprecations --- MIGRATION.md | 88 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 88 insertions(+) diff --git a/MIGRATION.md b/MIGRATION.md index b3acaa6695e8..5307b44a7597 100644 --- a/MIGRATION.md +++ b/MIGRATION.md @@ -1,3 +1,91 @@ +## Upgrading from 6.x to 7.x + +The main goal of version 7 is to reduce bundle size. This version is breaking because we removed deprecated APIs, upgraded our build tooling, and restructured npm package contents. +Below we will outline all the breaking changes you should consider when upgrading. + +### Dropping Support for Node.js v6 + +Node.js version 6 has reached end of life in April 2019. For Sentry JavaScript SDK version 7, we will no longer be supporting version 6 of Node.js. + +As far as SDK development goes, dropping support means no longer running integration tests for Node.js version 6, and also no longer handling edge cases specific to version 6. +Running the new SDK version on Node.js v6 is therefore highly discouraged. + +### Removal Of Old Platform Integrations From `@sentry/integrations` Package + +The following classes will be removed from the `@sentry/integrations` package and can no longer be used: + +- `Angular` +- `Ember` +- `Vue` + +These classes have been superseded and were moved into their own packages, `@sentry/angular`, `@sentry/ember`, and `@sentry/vue` in a previous version. +Refer to those packages if you want to integrate Sentry into your Angular, Ember, or Vue application. + +### Moving To ES6 For CommonJS Files + +From version 7 onwards, the CommonJS files in Sentry JavaScript SDK packages will use ES6. + +If you need to support Internet Explorer 11 or old Node.js versions, we recommend using a preprocessing tool like [Babel](https://babeljs.io/) to convert Sentry packages to ES5. + +### Restructuring Of Package Content + +Up until v6.x, we have published our packages on npm with the following structure: + +- `build` folder contained CDN bundles +- `dist` folder contained CommonJS files and TypeScript declarations +- `esm` folder contained ESM files and TypeScript declarations + +Moving forward the JavaScript SDK packages will generally have the following structure: + +- `cjs` folder contains CommonJS files +- `esm` folder contains ESM files +- `types` folder contains TypeScript declarations + +**CDN bundles of version 7 or higher will no longer be distributed through our npm package.** +This means that most third-party CDNs like [unpkg](https://unpkg.com/) or [jsDelivr](https://www.jsdelivr.com/) will also not provide them. + +If you depend on any specific files in a Sentry JavaScript npm package, you will most likely need to update their references. +For example, imports on `@sentry/browser/dist/client` will become `@sentry/browser/cjs/client`. +However, directly importing from specific files is discouraged. + +### Removing the `API` class from `@sentry/core` + +The internal `API` class was removed in favor of the `initAPIDetails` function and the `APIDetails` type. More details can be found in the [PR that deprecated this class](https://github.com/getsentry/sentry-javascript/pull/4281). To migrate, see the following example. + +```js +// New in v7: +import { + initAPIDetails, + getEnvelopeEndpointWithUrlEncodedAuth, + getStoreEndpointWithUrlEncodedAuth, +} from '@sentry/core'; + +const dsn = initAPIDetails(dsn, metadata, tunnel); +const dsn = api.dsn; +const storeEndpoint = getEnvelopeEndpointWithUrlEncodedAuth(api.dsn, api.tunnel); +const envelopeEndpoint = getStoreEndpointWithUrlEncodedAuth(api.dsn); + +// Before: +import { API } from '@sentry/core'; + +const api = new API(dsn, metadata, tunnel); +const dsn = api.getDsn(); +const storeEndpoint = api.getStoreEndpointWithUrlEncodedAuth(); +const envelopeEndpoint = api.getEnvelopeEndpointWithUrlEncodedAuth(); +``` + +### General API Changes + +For our efforts to reduce bundle size of the SDK we had to remove and refactor parts of the package which introduced a few changes to the API: + +- Remove support for deprecated `@sentry/apm` package. `@sentry/tracing` should be used instead. +- Remove deprecated `user` field from DSN. `publicKey` should be used instead. +- Remove deprecated `whitelistUrls` and `blacklistUrls` options from `Sentry.init`. They have been superseded by `allowUrls` and `denyUrls` specifically. See [our docs page on inclusive language](https://develop.sentry.dev/inclusion/) for more details. +- Gatsby SDK: Remove `Sentry` from `window` object. +- Remove deprecated `Status`, `SessionStatus`, and `RequestSessionStatus` enums. These were only part of an internal API. If you are using these enums, we encourage you to to look at [b177690d](https://github.com/getsentry/sentry-javascript/commit/b177690d89640aef2587039113c614672c07d2be), [5fc3147d](https://github.com/getsentry/sentry-javascript/commit/5fc3147dfaaf1a856d5923e4ba409479e87273be), and [f99bdd16](https://github.com/getsentry/sentry-javascript/commit/f99bdd16539bf6fac14eccf1a974a4988d586b28) to to see the changes we've made to our code as result. We generally recommend using string literals instead of the removed enums. +- Remove deprecated `getActiveDomain` method and `DomainAsCarrier` type from `@sentry/hub`. +- Rename `registerRequestInstrumentation` to `instrumentOutgoingRequests` in `@sentry/tracing`. + # Upgrading from 6.17.x to 6.18.0 Version 6.18.0 deprecates the `frameContextLines` top-level option for the Node SDK. This option will be removed in an upcoming major version. To migrate off of the top-level option, pass it instead to the new `ContextLines` integration. From 075d59b45f75f20a5f14b977bebbf9be1290196d Mon Sep 17 00:00:00 2001 From: Katie Byers Date: Mon, 11 Apr 2022 13:30:26 -0700 Subject: [PATCH 028/204] feat(browser): Add debugging hints to browser integration tests (#4914) Our old browser integration tests are difficult to debug. This adds a file of (hard-won) debugging tips to the test directory, to hopefully make it easier on the next person. While it's true that it contains a lot of very specific references (to functions, files, etc) and is therefore much more susceptible to becoming out of date, these tests aren't something we change often, and the consequences of such staleness are small. --- .../browser/test/integration/debugging.md | 29 +++++++++++++++++++ .../browser/test/integration/suites/shell.js | 2 +- 2 files changed, 30 insertions(+), 1 deletion(-) create mode 100644 packages/browser/test/integration/debugging.md diff --git a/packages/browser/test/integration/debugging.md b/packages/browser/test/integration/debugging.md new file mode 100644 index 000000000000..eb4849a48493 --- /dev/null +++ b/packages/browser/test/integration/debugging.md @@ -0,0 +1,29 @@ +### Debugging Hints + +These tests are hard to debug, because the testing system is somewhat complex, straightforward debugging doesn't work (see below), and the output of most `console.log` calls gets swallowed. Here, future debugger, are some tips to make it easier, to hopefully save you the hour(s) of trial and error it took to figure them out. + +- `suites/shell.js`: + - Remove the loader options from the `variants` array. + - Delete all of the placeholders of the form `{{ suites/something.js }}` except for the one you're interested in. It's not enough to comment them out, because they'll still exist in the file and get replaced by the test runner. Don't forget the one at the bottom of the file. + +- `suites/helpers.js`: + - Add `sandbox.contentWindow.console.log = (...args) => console.log(...args);` just before the return in `createSandbox()`. This will make it so that `console.log` statements come through to the terminal. (Yes, Karma theoretically has settings for that, but they don't seem to work. See https://github.com/karma-runner/karma-mocha/issues/47.) + +- `suites.yourTestFile.js`: + - Use `it.only` to only run the single test you're interested in. + +- Repo-level `rollup.config.js`: + - Comment out all bundle variants except whichever one `run.js` is turning into `artifacts/sdk.js`. + +- Browser-package-level `rollup.config.js`: + - Build only one of `es5` and `es6`. + +- Run `build:bundle:watch` in a separate terminal tab, so that when you add `console.log`s to the SDK, they get picked up. + +- Don't bother trying to copy one of our standard VSCode debug profiles, because it won't work, except to debug the testing system itself. (It will pause the node process running the tests, not the headless browser in which the tests themselves run.) + +- To make karma do verbose logging, run `export DEBUG=1`. To turn it off, run `unset DEBUG`. + +- To make the testing system do verbose logging, run `yarn test:integration --debug`. + +- To see exactly the files which are being run, comment out `rmdir('artifacts');` near the bottom of `run.js`. diff --git a/packages/browser/test/integration/suites/shell.js b/packages/browser/test/integration/suites/shell.js index 0b594f3d2eff..2ba7e2bd8c79 100644 --- a/packages/browser/test/integration/suites/shell.js +++ b/packages/browser/test/integration/suites/shell.js @@ -20,7 +20,7 @@ function runVariant(variant) { }); /** - * This part will be replaced by the test runner + * The test runner will replace each of these placeholders with the contents of the corresponding file. */ {{ suites/config.js }} // prettier-ignore {{ suites/api.js }} // prettier-ignore From 07d4ffd2d665e51874b1d0910a2f03cac1e94dd4 Mon Sep 17 00:00:00 2001 From: Tim Fish Date: Tue, 12 Apr 2022 00:11:39 +0100 Subject: [PATCH 029/204] ref(various): Remove usage of deprecated `event.stacktrace` (#4885) Removes usage of deprecated `event.stacktrace` and updated the event interface to remove the deprecated property. --- packages/browser/src/eventbuilder.ts | 7 +- packages/browser/src/integrations/dedupe.ts | 2 - .../browser/test/integration/suites/api.js | 4 +- .../test/integration/suites/breadcrumbs.js | 13 --- .../core/src/integrations/inboundfilters.ts | 3 - .../lib/integrations/inboundfilters.test.ts | 84 ++++++++++++------- packages/integrations/src/dedupe.ts | 2 - packages/integrations/src/rewriteframes.ts | 16 ---- packages/integrations/test/dedupe.test.ts | 40 +++++---- .../integrations/test/rewriteframes.test.ts | 26 ------ packages/node/src/eventbuilder.ts | 9 +- packages/types/src/event.ts | 2 - packages/wasm/src/index.ts | 3 - 13 files changed, 94 insertions(+), 117 deletions(-) diff --git a/packages/browser/src/eventbuilder.ts b/packages/browser/src/eventbuilder.ts index f999eb2854ca..d8acd41c12c0 100644 --- a/packages/browser/src/eventbuilder.ts +++ b/packages/browser/src/eventbuilder.ts @@ -74,7 +74,8 @@ export function eventFromPlainObject( if (syntheticException) { const frames = parseStackFrames(syntheticException); if (frames.length) { - event.stacktrace = { frames }; + // event.exception.values[0] has been set above + (event.exception as { values: Exception[] }).values[0].stacktrace = { frames }; } } @@ -273,7 +274,9 @@ export function eventFromString(input: string, syntheticException?: Error, attac if (attachStacktrace && syntheticException) { const frames = parseStackFrames(syntheticException); if (frames.length) { - event.stacktrace = { frames }; + event.exception = { + values: [{ value: input, stacktrace: { frames } }], + }; } } diff --git a/packages/browser/src/integrations/dedupe.ts b/packages/browser/src/integrations/dedupe.ts index 24e01d2f1226..c486c84c815e 100644 --- a/packages/browser/src/integrations/dedupe.ts +++ b/packages/browser/src/integrations/dedupe.ts @@ -198,8 +198,6 @@ function _getFramesFromEvent(event: Event): StackFrame[] | undefined { } catch (_oO) { return undefined; } - } else if (event.stacktrace) { - return event.stacktrace.frames; } return undefined; } diff --git a/packages/browser/test/integration/suites/api.js b/packages/browser/test/integration/suites/api.js index f51928c5bed5..be5de29abd0e 100644 --- a/packages/browser/test/integration/suites/api.js +++ b/packages/browser/test/integration/suites/api.js @@ -65,8 +65,8 @@ describe('API', function () { return runInSandbox(sandbox, function () { throwNonError(); }).then(function (summary) { - assert.isAtLeast(summary.events[0].stacktrace.frames.length, 1); - assert.isAtMost(summary.events[0].stacktrace.frames.length, 3); + assert.isAtLeast(summary.events[0].exception.values[0].stacktrace.frames.length, 1); + assert.isAtMost(summary.events[0].exception.values[0].stacktrace.frames.length, 3); }); }); diff --git a/packages/browser/test/integration/suites/breadcrumbs.js b/packages/browser/test/integration/suites/breadcrumbs.js index 434bcacc8b4a..5e5c2973efc7 100644 --- a/packages/browser/test/integration/suites/breadcrumbs.js +++ b/packages/browser/test/integration/suites/breadcrumbs.js @@ -234,8 +234,6 @@ describe('breadcrumbs', function () { assert.equal(summary.breadcrumbHints.length, 1); assert.equal(summary.breadcrumbHints[0].name, 'click'); assert.equal(summary.breadcrumbHints[0].event.target.tagName, 'INPUT'); - // There should be no expection, if there is one it means we threw it - assert.isUndefined(summary.events[0].exception); } }); }); @@ -265,9 +263,6 @@ describe('breadcrumbs', function () { assert.equal(summary.breadcrumbs[1].category, 'ui.input'); assert.equal(summary.breadcrumbs[1].message, 'body > form#foo-form > input[name="foo"]'); - - // There should be no expection, if there is one it means we threw it - assert.isUndefined(summary.events[0].exception); } }); }); @@ -288,8 +283,6 @@ describe('breadcrumbs', function () { // The async loader doesn't wrap event listeners, but we should receive the event without breadcrumbs assert.lengthOf(summary.events, 1); } else { - // There should be no expection, if there is one it means we threw it - assert.isUndefined(summary.events[0].exception); assert.equal(summary.breadcrumbs.length, 0); } }); @@ -309,8 +302,6 @@ describe('breadcrumbs', function () { // The async loader doesn't wrap event listeners, but we should receive the event without breadcrumbs assert.lengthOf(summary.events, 1); } else { - // There should be no expection, if there is one it means we threw it - assert.isUndefined(summary.events[0].exception); assert.equal(summary.breadcrumbs.length, 0); } }); @@ -472,7 +463,6 @@ describe('breadcrumbs', function () { assert.equal(summary.breadcrumbs[0].message, 'body > form#foo-form > input[name="foo"]'); assert.equal(summary.breadcrumbHints[0].global, false); assert.equal(summary.breadcrumbHints[1].global, false); - assert.isUndefined(summary.events[0].exception); } }); }); @@ -507,7 +497,6 @@ describe('breadcrumbs', function () { assert.equal(summary.breadcrumbs[0].message, 'body > form#foo-form > input[name="foo"]'); assert.equal(summary.breadcrumbHints[0].global, false); assert.equal(summary.breadcrumbHints[1].global, false); - assert.isUndefined(summary.events[0].exception); } }); }); @@ -538,7 +527,6 @@ describe('breadcrumbs', function () { assert.equal(summary.breadcrumbs[1].message, 'body > form#foo-form > div.contenteditable'); assert.equal(summary.breadcrumbHints[0].global, false); assert.equal(summary.breadcrumbHints[1].global, false); - assert.isUndefined(summary.events[0].exception); } }); }); @@ -706,7 +694,6 @@ describe('breadcrumbs', function () { assert.equal(summary.breadcrumbs.length, 2); assert.equal(summary.breadcrumbHints[0].global, true); assert.equal(summary.breadcrumbHints[1].global, true); - assert.isUndefined(summary.events[0].exception); } }); }); diff --git a/packages/core/src/integrations/inboundfilters.ts b/packages/core/src/integrations/inboundfilters.ts index 83d635ff3256..6151e32e5bbd 100644 --- a/packages/core/src/integrations/inboundfilters.ts +++ b/packages/core/src/integrations/inboundfilters.ts @@ -170,9 +170,6 @@ function _getLastValidUrl(frames: StackFrame[] = []): string | null { function _getEventFilterUrl(event: Event): string | null { try { - if (event.stacktrace) { - return _getLastValidUrl(event.stacktrace.frames); - } let frames; try { // @ts-ignore we only care about frames if the whole thing here is defined diff --git a/packages/core/test/lib/integrations/inboundfilters.test.ts b/packages/core/test/lib/integrations/inboundfilters.test.ts index fd6530cd75e3..74672a3f56e5 100644 --- a/packages/core/test/lib/integrations/inboundfilters.test.ts +++ b/packages/core/test/lib/integrations/inboundfilters.test.ts @@ -1,4 +1,4 @@ -import { EventProcessor } from '@sentry/types'; +import { Event, EventProcessor } from '@sentry/types'; import { InboundFilters, InboundFiltersOptions } from '../../../src/integrations/inboundfilters'; @@ -52,50 +52,68 @@ function createInboundFiltersEventProcessor( // Fixtures -const MESSAGE_EVENT = { +const MESSAGE_EVENT: Event = { message: 'captureMessage', }; -const MESSAGE_EVENT_2 = { +const MESSAGE_EVENT_2: Event = { message: 'captureMessageSomething', }; -const MESSAGE_EVENT_WITH_STACKTRACE = { +const MESSAGE_EVENT_WITH_STACKTRACE: Event = { message: 'wat', - stacktrace: { - // Frames are always in the reverse order, as this is how Sentry expect them to come. - // Frame that crashed is the last one, the one from awesome-analytics - frames: [ - { filename: 'https://our-side.com/js/bundle.js' }, - { filename: 'https://our-side.com/js/bundle.js' }, - { filename: 'https://awesome-analytics.io/some/file.js' }, + exception: { + values: [ + { + stacktrace: { + // Frames are always in the reverse order, as this is how Sentry expect them to come. + // Frame that crashed is the last one, the one from awesome-analytics + frames: [ + { filename: 'https://our-side.com/js/bundle.js' }, + { filename: 'https://our-side.com/js/bundle.js' }, + { filename: 'https://awesome-analytics.io/some/file.js' }, + ], + }, + }, ], }, }; -const MESSAGE_EVENT_WITH_ANON_LAST_FRAME = { +const MESSAGE_EVENT_WITH_ANON_LAST_FRAME: Event = { message: 'any', - stacktrace: { - frames: [ - { filename: 'https://our-side.com/js/bundle.js' }, - { filename: 'https://awesome-analytics.io/some/file.js' }, - { filename: '' }, + exception: { + values: [ + { + stacktrace: { + frames: [ + { filename: 'https://our-side.com/js/bundle.js' }, + { filename: 'https://awesome-analytics.io/some/file.js' }, + { filename: '' }, + ], + }, + }, ], }, }; -const MESSAGE_EVENT_WITH_NATIVE_LAST_FRAME = { +const MESSAGE_EVENT_WITH_NATIVE_LAST_FRAME: Event = { message: 'any', - stacktrace: { - frames: [ - { filename: 'https://our-side.com/js/bundle.js' }, - { filename: 'https://awesome-analytics.io/some/file.js' }, - { filename: '[native code]' }, + exception: { + values: [ + { + stacktrace: { + frames: [ + { filename: 'https://our-side.com/js/bundle.js' }, + { filename: 'https://awesome-analytics.io/some/file.js' }, + { filename: '[native code]' }, + ], + }, + }, ], }, }; -const EXCEPTION_EVENT = { +const EXCEPTION_EVENT: Event = { exception: { values: [ { @@ -106,7 +124,7 @@ const EXCEPTION_EVENT = { }, }; -const EXCEPTION_EVENT_WITH_FRAMES = { +const EXCEPTION_EVENT_WITH_FRAMES: Event = { exception: { values: [ { @@ -124,7 +142,7 @@ const EXCEPTION_EVENT_WITH_FRAMES = { }, }; -const SENTRY_EVENT = { +const SENTRY_EVENT: Event = { exception: { values: [ { @@ -135,7 +153,7 @@ const SENTRY_EVENT = { }, }; -const SCRIPT_ERROR_EVENT = { +const SCRIPT_ERROR_EVENT: Event = { exception: { values: [ { @@ -146,9 +164,15 @@ const SCRIPT_ERROR_EVENT = { }, }; -const MALFORMED_EVENT = { - stacktrace: { - frames: undefined, +const MALFORMED_EVENT: Event = { + exception: { + values: [ + { + stacktrace: { + frames: undefined, + }, + }, + ], }, }; diff --git a/packages/integrations/src/dedupe.ts b/packages/integrations/src/dedupe.ts index 2ce72a6b636c..644f3d41cd62 100644 --- a/packages/integrations/src/dedupe.ts +++ b/packages/integrations/src/dedupe.ts @@ -198,8 +198,6 @@ function _getFramesFromEvent(event: Event): StackFrame[] | undefined { } catch (_oO) { return undefined; } - } else if (event.stacktrace) { - return event.stacktrace.frames; } return undefined; } diff --git a/packages/integrations/src/rewriteframes.ts b/packages/integrations/src/rewriteframes.ts index 9eb95e54f6d3..7b1129e45032 100644 --- a/packages/integrations/src/rewriteframes.ts +++ b/packages/integrations/src/rewriteframes.ts @@ -61,10 +61,6 @@ export class RewriteFrames implements Integration { processedEvent = this._processExceptionsEvent(processedEvent); } - if (originalEvent.stacktrace) { - processedEvent = this._processStacktraceEvent(processedEvent); - } - return processedEvent; } @@ -110,18 +106,6 @@ export class RewriteFrames implements Integration { } } - /** JSDoc */ - private _processStacktraceEvent(event: Event): Event { - try { - return { - ...event, - stacktrace: this._processStacktrace(event.stacktrace), - }; - } catch (_oO) { - return event; - } - } - /** JSDoc */ private _processStacktrace(stacktrace?: Stacktrace): Stacktrace { return { diff --git a/packages/integrations/test/dedupe.test.ts b/packages/integrations/test/dedupe.test.ts index 46cac4d06320..c56ab59d5b82 100644 --- a/packages/integrations/test/dedupe.test.ts +++ b/packages/integrations/test/dedupe.test.ts @@ -1,3 +1,5 @@ +import { Event } from '@sentry/types'; + import { _shouldDropEvent } from '../src/dedupe'; /** JSDoc */ @@ -5,27 +7,34 @@ function clone(data: T): T { return JSON.parse(JSON.stringify(data)); } -const messageEvent = { +const messageEvent: Event = { fingerprint: ['MrSnuffles'], message: 'PickleRick', - stacktrace: { - frames: [ - { - colno: 1, - filename: 'filename.js', - function: 'function', - lineno: 1, - }, + exception: { + values: [ { - colno: 2, - filename: 'filename.js', - function: 'function', - lineno: 2, + value: 'PickleRick', + stacktrace: { + frames: [ + { + colno: 1, + filename: 'filename.js', + function: 'function', + lineno: 1, + }, + { + colno: 2, + filename: 'filename.js', + function: 'function', + lineno: 2, + }, + ], + }, }, ], }, }; -const exceptionEvent = { +const exceptionEvent: Event = { exception: { values: [ { @@ -64,13 +73,14 @@ describe('Dedupe', () => { const eventA = clone(messageEvent); const eventB = clone(messageEvent); eventB.message = 'EvilMorty'; + eventB.exception.values[0].value = 'EvilMorty'; expect(_shouldDropEvent(eventA, eventB)).toBe(false); }); it('should not drop if events have same messages, but different stacktraces', () => { const eventA = clone(messageEvent); const eventB = clone(messageEvent); - eventB.stacktrace.frames[0].colno = 1337; + eventB.exception.values[0].stacktrace.frames[0].colno = 1337; expect(_shouldDropEvent(eventA, eventB)).toBe(false); }); diff --git a/packages/integrations/test/rewriteframes.test.ts b/packages/integrations/test/rewriteframes.test.ts index 92346d99ebcb..ff18e16f753d 100644 --- a/packages/integrations/test/rewriteframes.test.ts +++ b/packages/integrations/test/rewriteframes.test.ts @@ -65,12 +65,6 @@ describe('RewriteFrames', () => { rewriteFrames = new RewriteFrames(); }); - it('transforms messageEvent frames', () => { - const event = rewriteFrames.process(messageEvent); - expect(event.stacktrace!.frames![0].filename).toEqual('app:///file1.js'); - expect(event.stacktrace!.frames![1].filename).toEqual('app:///file2.js'); - }); - it('transforms exceptionEvent frames', () => { const event = rewriteFrames.process(exceptionEvent); expect(event.exception!.values![0].stacktrace!.frames![0].filename).toEqual('app:///file1.js'); @@ -85,12 +79,6 @@ describe('RewriteFrames', () => { }); }); - it('transforms messageEvent frames', () => { - const event = rewriteFrames.process(messageEvent); - expect(event.stacktrace!.frames![0].filename).toEqual('foobar/file1.js'); - expect(event.stacktrace!.frames![1].filename).toEqual('foobar/file2.js'); - }); - it('transforms exceptionEvent frames', () => { const event = rewriteFrames.process(exceptionEvent); expect(event.exception!.values![0].stacktrace!.frames![0].filename).toEqual('foobar/file1.js'); @@ -117,12 +105,6 @@ describe('RewriteFrames', () => { }); }); - it('transforms messageEvent frames', () => { - const event = rewriteFrames.process(messageEvent); - expect(event.stacktrace!.frames![0].filename).toEqual('app:///src/app/file1.js'); - expect(event.stacktrace!.frames![1].filename).toEqual('app:///src/app/mo\\dule/file2.js'); - }); - it('transforms exceptionEvent frames', () => { const event = rewriteFrames.process(exceptionEvent); expect(event.exception!.values![0].stacktrace!.frames![0].filename).toEqual('app:///src/app/file1.js'); @@ -146,14 +128,6 @@ describe('RewriteFrames', () => { }); }); - it('transforms messageEvent frames', () => { - const event = rewriteFrames.process(messageEvent); - expect(event.stacktrace!.frames![0].filename).toEqual('/www/src/app/file1.js'); - expect(event.stacktrace!.frames![0].function).toEqual('whoops'); - expect(event.stacktrace!.frames![1].filename).toEqual('/www/src/app/mo\\dule/file2.js'); - expect(event.stacktrace!.frames![1].function).toEqual('whoops'); - }); - it('transforms exceptionEvent frames', () => { const event = rewriteFrames.process(exceptionEvent); expect(event.exception!.values![0].stacktrace!.frames![0].filename).toEqual('/www/src/app/file1.js'); diff --git a/packages/node/src/eventbuilder.ts b/packages/node/src/eventbuilder.ts index 0376f21a59c8..1287f2a47f48 100644 --- a/packages/node/src/eventbuilder.ts +++ b/packages/node/src/eventbuilder.ts @@ -105,7 +105,14 @@ export function eventFromMessage( if (attachStacktrace && hint && hint.syntheticException) { const frames = parseStackFrames(hint.syntheticException); if (frames.length) { - event.stacktrace = { frames }; + event.exception = { + values: [ + { + value: message, + stacktrace: { frames }, + }, + ], + }; } } diff --git a/packages/types/src/event.ts b/packages/types/src/event.ts index 0aa146f43848..6711322baab4 100644 --- a/packages/types/src/event.ts +++ b/packages/types/src/event.ts @@ -9,7 +9,6 @@ import { CaptureContext } from './scope'; import { SdkInfo } from './sdkinfo'; import { Severity } from './severity'; import { Span } from './span'; -import { Stacktrace } from './stacktrace'; import { Measurements } from './transaction'; import { User } from './user'; @@ -34,7 +33,6 @@ export interface Event { exception?: { values?: Exception[]; }; - stacktrace?: Stacktrace; breadcrumbs?: Breadcrumb[]; contexts?: Contexts; tags?: { [key: string]: Primitive }; diff --git a/packages/wasm/src/index.ts b/packages/wasm/src/index.ts index 14252305fa76..cc5e678ee123 100644 --- a/packages/wasm/src/index.ts +++ b/packages/wasm/src/index.ts @@ -58,9 +58,6 @@ export class Wasm implements Integration { } }); } - if (event.stacktrace?.frames) { - haveWasm = haveWasm || patchFrames(event.stacktrace.frames); - } if (haveWasm) { event.debug_meta = event.debug_meta || {}; From 5d38a7e315fe38a3942cb484d2de52030ee8f954 Mon Sep 17 00:00:00 2001 From: Katie Byers Date: Mon, 11 Apr 2022 16:47:44 -0700 Subject: [PATCH 030/204] chore(dev): Update `jest` and friends (#4897) This updates `jest`, `ts-jest`, and `jest-environment-node` to the latest versions, in order to facilitate code transformations during `ts-jest`'s on-the-fly compilation that will become necessary once we move to ES6. (More detail on this to come in the PR which actually introduces said transformation, but TL;DR the way we use and extend `global` is fine if it's a `var` (which it is in ES5 Land) but less fine if it's a `const` (which it becomes under ES6), and we need to fix that for tests to run.) It also updates `jsdom`. Together these updates meant that a larger number of packages needed to be downgraded in order for tests to run in node 8 and 10. This therefore also reworks the test script a bit to account for those changes. Finally, this removes the test environment from our main jest config, as its value has become the default in latest version of jest. --- .github/workflows/build.yml | 2 + jest.config.js | 1 - package.json | 10 +- packages/browser/package.json | 1 - packages/react/package.json | 1 - packages/tracing/package.json | 4 +- packages/utils/package.json | 3 +- packages/vue/package.json | 3 - scripts/test.ts | 69 +- yarn.lock | 1928 +++++++++++++++++++++------------ 10 files changed, 1304 insertions(+), 718 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index aec9e06c66dc..21d0fd5685f9 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -410,6 +410,8 @@ jobs: uses: actions/checkout@v2 - name: Set up Node uses: actions/setup-node@v1 + with: + node-version: '16' - name: Check dependency cache uses: actions/cache@v2 with: diff --git a/jest.config.js b/jest.config.js index 411c1f269052..208a643187f4 100644 --- a/jest.config.js +++ b/jest.config.js @@ -7,7 +7,6 @@ module.exports = { }, coverageDirectory: '/coverage', moduleFileExtensions: ['js', 'ts', 'tsx'], - testEnvironment: 'node', testMatch: ['/**/*.test.ts', '/**/*.test.tsx'], globals: { 'ts-jest': { diff --git a/package.json b/package.json index e1f42542b33d..7075a4f5f6fd 100644 --- a/package.json +++ b/package.json @@ -62,6 +62,7 @@ "@strictsoftware/typedoc-plugin-monorepo": "^0.3.1", "@types/chai": "^4.1.3", "@types/jest": "^24.0.11", + "@types/jsdom": "^16.2.3", "@types/mocha": "^5.2.0", "@types/node": "~10.17.0", "@types/sinon": "^7.0.11", @@ -69,7 +70,9 @@ "codecov": "^3.6.5", "deepmerge": "^4.2.2", "eslint": "7.32.0", - "jest": "^24.9.0", + "jest": "^27.5.1", + "jest-environment-node": "^27.5.1", + "jsdom": "^19.0.0", "karma-browserstack-launcher": "^1.5.1", "karma-firefox-launcher": "^1.1.0", "lerna": "3.13.4", @@ -85,15 +88,14 @@ "rollup-plugin-typescript2": "^0.31.2", "sinon": "^7.3.2", "size-limit": "^4.5.5", - "ts-jest": "^24.3.0", + "ts-jest": "^27.1.4", "ts-node": "^8.10.2", "tslib": "^2.3.1", "typedoc": "^0.18.0", "typescript": "3.8.3" }, "resolutions": { - "**/agent-base": "5", - "**/jest-environment-node": "24" + "**/agent-base": "5" }, "version": "0.0.0", "dependencies": {} diff --git a/packages/browser/package.json b/packages/browser/package.json index d3a87d21d04a..7f1c79235b59 100644 --- a/packages/browser/package.json +++ b/packages/browser/package.json @@ -26,7 +26,6 @@ "btoa": "^1.2.1", "chai": "^4.1.2", "chokidar": "^3.0.2", - "jsdom": "^15.0.0", "karma": "^6.3.16", "karma-chai": "^0.1.0", "karma-chrome-launcher": "^2.2.0", diff --git a/packages/react/package.json b/packages/react/package.json index 534283e00d07..7835071eccdc 100644 --- a/packages/react/package.json +++ b/packages/react/package.json @@ -40,7 +40,6 @@ "eslint-plugin-react-hooks": "^4.0.8", "history-4": "npm:history@4.6.0", "history-5": "npm:history@4.9.0", - "jsdom": "^16.2.2", "react": "^18.0.0", "react-dom": "^18.0.0", "react-router-3": "npm:react-router@3.2.0", diff --git a/packages/tracing/package.json b/packages/tracing/package.json index d006f5dd25e7..e9d01ff9c2a3 100644 --- a/packages/tracing/package.json +++ b/packages/tracing/package.json @@ -24,9 +24,7 @@ }, "devDependencies": { "@sentry/browser": "7.0.0-alpha.0", - "@types/express": "^4.17.1", - "@types/jsdom": "^16.2.3", - "jsdom": "^16.2.2" + "@types/express": "^4.17.1" }, "scripts": { "build": "run-p build:cjs build:esm build:types build:bundle && ts-node ../../scripts/prepack.ts --bundles #necessary for integration tests", diff --git a/packages/utils/package.json b/packages/utils/package.json index 6d62d5602180..ddce3d7933aa 100644 --- a/packages/utils/package.json +++ b/packages/utils/package.json @@ -20,8 +20,7 @@ "tslib": "^1.9.3" }, "devDependencies": { - "chai": "^4.1.2", - "jsdom": "^16.2.2" + "chai": "^4.1.2" }, "scripts": { "build": "run-p build:cjs build:esm build:types", diff --git a/packages/vue/package.json b/packages/vue/package.json index fea81ff573a8..b49beced0934 100644 --- a/packages/vue/package.json +++ b/packages/vue/package.json @@ -26,9 +26,6 @@ "peerDependencies": { "vue": "2.x || 3.x" }, - "devDependencies": { - "jsdom": "^16.2.2" - }, "scripts": { "build": "run-p build:cjs build:esm build:types", "build:bundle": "rollup --config", diff --git a/scripts/test.ts b/scripts/test.ts index 51a1dc06245e..02f440af1af9 100644 --- a/scripts/test.ts +++ b/scripts/test.ts @@ -11,28 +11,55 @@ function run(cmd: string, cwd: string = '') { const nodeMajorVersion = parseInt(process.version.split('.')[0].replace('v', ''), 10); -if (nodeMajorVersion <= 8) { - // install legacy versions of packages whose current versions don't support node 8 +// Ember tests require dependency changes for each set of tests, making them quite slow. To compensate for this, in CI +// we run them in a separate, parallel job. +let ignorePackages = ['@sentry/ember']; + +// install legacy versions of third-party packages whose current versions don't support node 8 or 10, and skip testing +// our own packages which don't support node 8 for various syntax or dependency reasons +if (nodeMajorVersion <= 10) { + let legacyDependencies; + + if (nodeMajorVersion === 8) { + legacyDependencies = [ + 'jsdom@15.x', + 'jest@25.x', + 'jest-environment-jsdom@25.x', + 'jest-environment-node@25.x', + 'ts-jest@25.x', + ]; + + ignorePackages = [ + ...ignorePackages, + '@sentry-internal/eslint-plugin-sdk', + '@sentry/react', + '@sentry/wasm', + '@sentry/gatsby', + '@sentry/serverless', + '@sentry/nextjs', + ]; + + // This is a hack, to deal the fact that the browser-based tests fail under Node 8, because of a conflict buried + // somewhere in the interaction between our current overall set of dependencies and the older versions of a small + // subset we're about to install below. Since they're browser-based, these tests are never going to be running in a + // node 8 environment in any case, so it's fine to skip them here. (In the long run, we should only run such tests + // against a single version of node, but in the short run, this at least allows us to not be blocked by the + // failures.) + run('rm -rf packages/tracing/test/browser'); + } + // Node 10 + else { + legacyDependencies = ['jsdom@16.x']; + } + + const legacyDepStr = legacyDependencies.join(' '); + // ignoring engines and scripts lets us get away with having incompatible things installed for packages we're not testing - run('yarn add --dev --ignore-engines --ignore-scripts jsdom@15.x', 'packages/tracing'); - run('yarn add --dev --ignore-engines --ignore-scripts jsdom@15.x', 'packages/utils'); - - // ember tests happen separately, and the rest fail on node 8 for various syntax or dependency reasons - const ignore = [ - '@sentry/ember', - '@sentry-internal/eslint-plugin-sdk', - '@sentry/react', - '@sentry/wasm', - '@sentry/gatsby', - '@sentry/serverless', - '@sentry/nextjs', - ] - .map(dep => `--ignore="${dep}"`) - .join(' '); - - run(`yarn test ${ignore}`); -} else { - run('yarn test --ignore="@sentry/ember"'); + run(`yarn add --dev --ignore-engines --ignore-scripts --ignore-workspace-root-check ${legacyDepStr}`); } +const ignoreFlags = ignorePackages.map(dep => `--ignore="${dep}"`).join(' '); + +run(`yarn test ${ignoreFlags}`); + process.exit(0); diff --git a/yarn.lock b/yarn.lock index 26947533ab88..7a8d5c41fb37 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2,6 +2,13 @@ # yarn lockfile v1 +"@ampproject/remapping@^2.1.0": + version "2.1.2" + resolved "https://registry.yarnpkg.com/@ampproject/remapping/-/remapping-2.1.2.tgz#4edca94973ded9630d20101cd8559cedb8d8bd34" + integrity sha512-hoyByceqwKirw7w3Z7gnIIZC3Wx3J484Y3L/cMpXFbr7d9ZQj2mODrirNzcJa+SM3UlpWXYvKV4RlRpFXlWgXg== + dependencies: + "@jridgewell/trace-mapping" "^0.3.0" + "@angular/common@^10.0.3": version "10.2.4" resolved "https://registry.yarnpkg.com/@angular/common/-/common-10.2.4.tgz#fb1772ea5780c96e00411900c54457f0cbcf401b" @@ -61,6 +68,11 @@ resolved "https://registry.yarnpkg.com/@babel/compat-data/-/compat-data-7.16.0.tgz#ea269d7f78deb3a7826c39a4048eecda541ebdaa" integrity sha512-DGjt2QZse5SGd9nfOSqO4WLJ8NN/oHkijbXbPrxuoJO3oIPJL3TciZs9FX+cOHNiY9E9l0opL8g7BmLe3T+9ew== +"@babel/compat-data@^7.17.7": + version "7.17.7" + resolved "https://registry.yarnpkg.com/@babel/compat-data/-/compat-data-7.17.7.tgz#078d8b833fbbcc95286613be8c716cef2b519fa2" + integrity sha512-p8pdE6j0a29TNGebNm7NzYZWB3xVZJBZ7XGs42uAKzQo8VQ3F0By/cQCtUEABwIqw5zo6WA4NbmxsfzADzMKnQ== + "@babel/core@^7.1.0", "@babel/core@^7.1.6", "@babel/core@^7.12.0", "@babel/core@^7.3.4": version "7.13.14" resolved "https://registry.yarnpkg.com/@babel/core/-/core-7.13.14.tgz#8e46ebbaca460a63497c797e574038ab04ae6d06" @@ -82,6 +94,27 @@ semver "^6.3.0" source-map "^0.5.0" +"@babel/core@^7.12.3", "@babel/core@^7.7.2", "@babel/core@^7.8.0": + version "7.17.9" + resolved "https://registry.yarnpkg.com/@babel/core/-/core-7.17.9.tgz#6bae81a06d95f4d0dec5bb9d74bbc1f58babdcfe" + integrity sha512-5ug+SfZCpDAkVp9SFIZAzlW18rlzsOcJGaetCjkySnrXXDUw9AR8cDUm1iByTmdWM6yxX6/zycaV76w3YTF2gw== + dependencies: + "@ampproject/remapping" "^2.1.0" + "@babel/code-frame" "^7.16.7" + "@babel/generator" "^7.17.9" + "@babel/helper-compilation-targets" "^7.17.7" + "@babel/helper-module-transforms" "^7.17.7" + "@babel/helpers" "^7.17.9" + "@babel/parser" "^7.17.9" + "@babel/template" "^7.16.7" + "@babel/traverse" "^7.17.9" + "@babel/types" "^7.17.0" + convert-source-map "^1.7.0" + debug "^4.1.0" + gensync "^1.0.0-beta.2" + json5 "^2.2.1" + semver "^6.3.0" + "@babel/core@^7.12.9": version "7.15.5" resolved "https://registry.yarnpkg.com/@babel/core/-/core-7.15.5.tgz#f8ed9ace730722544609f90c9bb49162dc3bf5b9" @@ -151,6 +184,15 @@ jsesc "^2.5.1" source-map "^0.5.0" +"@babel/generator@^7.17.9", "@babel/generator@^7.7.2": + version "7.17.9" + resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.17.9.tgz#f4af9fd38fa8de143c29fce3f71852406fc1e2fc" + integrity sha512-rAdDousTwxbIxbz5I7GEQ3lUip+xVCXooZNbsydCWs3xA7ZsYOv+CFRdzGxRX78BmQHu9B1Eso59AOZQOJDEdQ== + dependencies: + "@babel/types" "^7.17.0" + jsesc "^2.5.1" + source-map "^0.5.0" + "@babel/helper-annotate-as-pure@^7.12.13": version "7.12.13" resolved "https://registry.yarnpkg.com/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.12.13.tgz#0f58e86dfc4bb3b1fcd7db806570e177d439b6ab" @@ -200,6 +242,16 @@ browserslist "^4.17.5" semver "^6.3.0" +"@babel/helper-compilation-targets@^7.17.7": + version "7.17.7" + resolved "https://registry.yarnpkg.com/@babel/helper-compilation-targets/-/helper-compilation-targets-7.17.7.tgz#a3c2924f5e5f0379b356d4cfb313d1414dc30e46" + integrity sha512-UFzlz2jjd8kroj0hmCFV5zr+tQPi1dpC2cRsDV/3IEW8bJfCPrPpmcSN6ZS8RqIq4LXcmpipCQFPddyFA5Yc7w== + dependencies: + "@babel/compat-data" "^7.17.7" + "@babel/helper-validator-option" "^7.16.7" + browserslist "^4.17.5" + semver "^6.3.0" + "@babel/helper-create-class-features-plugin@^7.13.0", "@babel/helper-create-class-features-plugin@^7.5.5": version "7.13.11" resolved "https://registry.yarnpkg.com/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.13.11.tgz#30d30a005bca2c953f5653fc25091a492177f4f6" @@ -299,6 +351,14 @@ "@babel/template" "^7.16.7" "@babel/types" "^7.16.7" +"@babel/helper-function-name@^7.17.9": + version "7.17.9" + resolved "https://registry.yarnpkg.com/@babel/helper-function-name/-/helper-function-name-7.17.9.tgz#136fcd54bc1da82fcb47565cf16fd8e444b1ff12" + integrity sha512-7cRisGlVtiVqZ0MW0/yFB4atgpGLWEHUVYnb448hZK4x+vih0YO5UoS11XIYtZYqHd0dIPMdUSv8q5K4LdMnIg== + dependencies: + "@babel/template" "^7.16.7" + "@babel/types" "^7.17.0" + "@babel/helper-get-function-arity@^7.15.4": version "7.15.4" resolved "https://registry.yarnpkg.com/@babel/helper-get-function-arity/-/helper-get-function-arity-7.15.4.tgz#098818934a137fce78b536a3e015864be1e2879b" @@ -376,6 +436,13 @@ dependencies: "@babel/types" "^7.16.0" +"@babel/helper-module-imports@^7.16.7": + version "7.16.7" + resolved "https://registry.yarnpkg.com/@babel/helper-module-imports/-/helper-module-imports-7.16.7.tgz#25612a8091a999704461c8a222d0efec5d091437" + integrity sha512-LVtS6TqjJHFc+nYeITRo6VLXve70xmq7wPhWTqDJusJEgGmkAACWwMiTNrvfoQo6hEhFwAIixNkvB0jPXDL8Wg== + dependencies: + "@babel/types" "^7.16.7" + "@babel/helper-module-transforms@^7.13.0", "@babel/helper-module-transforms@^7.15.4": version "7.15.4" resolved "https://registry.yarnpkg.com/@babel/helper-module-transforms/-/helper-module-transforms-7.15.4.tgz#962cc629a7f7f9a082dd62d0307fa75fe8788d7c" @@ -404,6 +471,20 @@ "@babel/traverse" "^7.16.0" "@babel/types" "^7.16.0" +"@babel/helper-module-transforms@^7.17.7": + version "7.17.7" + resolved "https://registry.yarnpkg.com/@babel/helper-module-transforms/-/helper-module-transforms-7.17.7.tgz#3943c7f777139e7954a5355c815263741a9c1cbd" + integrity sha512-VmZD99F3gNTYB7fJRDTi+u6l/zxY0BE6OIxPSU7a50s6ZUQkHwSDmV92FfM+oCG0pZRVojGYhkR8I0OGeCVREw== + dependencies: + "@babel/helper-environment-visitor" "^7.16.7" + "@babel/helper-module-imports" "^7.16.7" + "@babel/helper-simple-access" "^7.17.7" + "@babel/helper-split-export-declaration" "^7.16.7" + "@babel/helper-validator-identifier" "^7.16.7" + "@babel/template" "^7.16.7" + "@babel/traverse" "^7.17.3" + "@babel/types" "^7.17.0" + "@babel/helper-optimise-call-expression@^7.12.13", "@babel/helper-optimise-call-expression@^7.15.4": version "7.15.4" resolved "https://registry.yarnpkg.com/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.15.4.tgz#f310a5121a3b9cc52d9ab19122bd729822dee171" @@ -489,6 +570,13 @@ dependencies: "@babel/types" "^7.16.0" +"@babel/helper-simple-access@^7.17.7": + version "7.17.7" + resolved "https://registry.yarnpkg.com/@babel/helper-simple-access/-/helper-simple-access-7.17.7.tgz#aaa473de92b7987c6dfa7ce9a7d9674724823367" + integrity sha512-txyMCGroZ96i+Pxr3Je3lzEJjqwaRC9buMUgtomcrLe5Nd0+fk1h0LLA+ixUF5OW7AhHuQ7Es1WcQJZmZsz2XA== + dependencies: + "@babel/types" "^7.17.0" + "@babel/helper-skip-transparent-expression-wrappers@^7.12.1": version "7.12.1" resolved "https://registry.yarnpkg.com/@babel/helper-skip-transparent-expression-wrappers/-/helper-skip-transparent-expression-wrappers-7.12.1.tgz#462dc63a7e435ade8468385c63d2b84cce4b3cbf" @@ -577,6 +665,15 @@ "@babel/traverse" "^7.15.4" "@babel/types" "^7.15.4" +"@babel/helpers@^7.17.9": + version "7.17.9" + resolved "https://registry.yarnpkg.com/@babel/helpers/-/helpers-7.17.9.tgz#b2af120821bfbe44f9907b1826e168e819375a1a" + integrity sha512-cPCt915ShDWUEzEp3+UNRktO2n6v49l5RSnG9M5pS24hA+2FAc5si+Pn1i4VVbQQ+jh+bIZhPFQOJOzbrOYY1Q== + dependencies: + "@babel/template" "^7.16.7" + "@babel/traverse" "^7.17.9" + "@babel/types" "^7.17.0" + "@babel/highlight@^7.10.4", "@babel/highlight@^7.14.5": version "7.14.5" resolved "https://registry.yarnpkg.com/@babel/highlight/-/highlight-7.14.5.tgz#6861a52f03966405001f6aa534a01a24d99e8cd9" @@ -609,6 +706,11 @@ resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.14.2.tgz#0c1680aa44ad4605b16cbdcc5c341a61bde9c746" integrity sha512-IoVDIHpsgE/fu7eXBeRWt8zLbDrSvD7H1gpomOkPpBoEN8KCruCqSDdqo8dddwQQrui30KSvQBaMUOJiuFu6QQ== +"@babel/parser@^7.14.7", "@babel/parser@^7.17.9": + version "7.17.9" + resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.17.9.tgz#9c94189a6062f0291418ca021077983058e171ef" + integrity sha512-vqUSBLP8dQHFPdPi9bc5GK9vRkYHJ49fsZdtoJ8EQ8ibpwk5rPKfvNIwChB0KVXcIjcepEBBd2VHC5r9Gy8ueg== + "@babel/parser@^7.15.4", "@babel/parser@^7.15.5": version "7.15.6" resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.15.6.tgz#043b9aa3c303c0722e5377fef9197f4cf1796549" @@ -833,7 +935,14 @@ dependencies: "@babel/helper-plugin-utils" "^7.8.0" -"@babel/plugin-syntax-class-properties@^7.12.13": +"@babel/plugin-syntax-bigint@^7.8.3": + version "7.8.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-bigint/-/plugin-syntax-bigint-7.8.3.tgz#4c9a6f669f5d0cdf1b90a1671e9a146be5300cea" + integrity sha512-wnTnFlG+YxQm3vDxpGE57Pj0srRU4sHE/mDkt1qv2YJJSeUAec2ma4WLUnUPeKjyrfntVwe/N6dCXpU+zL3Npg== + dependencies: + "@babel/helper-plugin-utils" "^7.8.0" + +"@babel/plugin-syntax-class-properties@^7.12.13", "@babel/plugin-syntax-class-properties@^7.8.3": version "7.12.13" resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-class-properties/-/plugin-syntax-class-properties-7.12.13.tgz#b5c987274c4a3a82b89714796931a6b53544ae10" integrity sha512-fm4idjKla0YahUNgFNLCB0qySdsoPiZP3iQE3rky0mBUtMZ23yDJ9SJdg6dXTSDnulOVqiF3Hgr9nbXvXTQZYA== @@ -861,6 +970,13 @@ dependencies: "@babel/helper-plugin-utils" "^7.8.3" +"@babel/plugin-syntax-import-meta@^7.8.3": + version "7.10.4" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-import-meta/-/plugin-syntax-import-meta-7.10.4.tgz#ee601348c370fa334d2207be158777496521fd51" + integrity sha512-Yqfm+XDx0+Prh3VSeEQCPU81yC+JWZ2pDPFSS4ZdpfZhp4MkFMaDC1UqseovEKwSUpnIL7+vK+Clp7bfh0iD7g== + dependencies: + "@babel/helper-plugin-utils" "^7.10.4" + "@babel/plugin-syntax-json-strings@^7.8.3": version "7.8.3" resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-json-strings/-/plugin-syntax-json-strings-7.8.3.tgz#01ca21b668cd8218c9e640cb6dd88c5412b2c96a" @@ -868,7 +984,7 @@ dependencies: "@babel/helper-plugin-utils" "^7.8.0" -"@babel/plugin-syntax-logical-assignment-operators@^7.10.4": +"@babel/plugin-syntax-logical-assignment-operators@^7.10.4", "@babel/plugin-syntax-logical-assignment-operators@^7.8.3": version "7.10.4" resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-logical-assignment-operators/-/plugin-syntax-logical-assignment-operators-7.10.4.tgz#ca91ef46303530448b906652bac2e9fe9941f699" integrity sha512-d8waShlpFDinQ5MtvGU9xDAOzKH47+FFoney2baFIoMr952hKOLp1HR7VszoZvOsV/4+RRszNY7D17ba0te0ig== @@ -882,14 +998,14 @@ dependencies: "@babel/helper-plugin-utils" "^7.8.0" -"@babel/plugin-syntax-numeric-separator@^7.10.4": +"@babel/plugin-syntax-numeric-separator@^7.10.4", "@babel/plugin-syntax-numeric-separator@^7.8.3": version "7.10.4" resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-numeric-separator/-/plugin-syntax-numeric-separator-7.10.4.tgz#b9b070b3e33570cd9fd07ba7fa91c0dd37b9af97" integrity sha512-9H6YdfkcK/uOnY/K7/aA2xpzaAgkQn37yzWUMRK7OaPOqOpGS1+n0H5hxT9AUw9EsSjPW8SVyMJwYRtWs3X3ug== dependencies: "@babel/helper-plugin-utils" "^7.10.4" -"@babel/plugin-syntax-object-rest-spread@^7.0.0", "@babel/plugin-syntax-object-rest-spread@^7.8.3": +"@babel/plugin-syntax-object-rest-spread@^7.8.3": version "7.8.3" resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-object-rest-spread/-/plugin-syntax-object-rest-spread-7.8.3.tgz#60e225edcbd98a640332a2e72dd3e66f1af55871" integrity sha512-XoqMijGZb9y3y2XskN+P1wUGiVwWZ5JmoDRwx5+3GmEplNyVM2s2Dg8ILFQm8rWM48orGy5YpI5Bl8U1y7ydlA== @@ -924,6 +1040,13 @@ dependencies: "@babel/helper-plugin-utils" "^7.12.13" +"@babel/plugin-syntax-top-level-await@^7.8.3": + version "7.14.5" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-top-level-await/-/plugin-syntax-top-level-await-7.14.5.tgz#c1cfdadc35a646240001f06138247b741c34d94c" + integrity sha512-hx++upLv5U1rgYfwe1xBQUhRmU41NEvpUvrp8jkrSCdvGSnM5/qdRMtylJ6PG5OFkBaHkbTAKTnd3/YyESRHFw== + dependencies: + "@babel/helper-plugin-utils" "^7.14.5" + "@babel/plugin-syntax-typescript@^7.12.13", "@babel/plugin-syntax-typescript@^7.2.0": version "7.12.13" resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.12.13.tgz#9dff111ca64154cef0f4dc52cf843d9f12ce4474" @@ -938,7 +1061,7 @@ dependencies: "@babel/helper-plugin-utils" "^7.14.5" -"@babel/plugin-syntax-typescript@^7.16.7": +"@babel/plugin-syntax-typescript@^7.16.7", "@babel/plugin-syntax-typescript@^7.7.2": version "7.16.7" resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.16.7.tgz#39c9b55ee153151990fb038651d58d3fd03f98f8" integrity sha512-YhUIJHHGkqPgEcMYkPCKTyGUdoGKWtopIycQyjJH8OjvRgOYsXsaKehLVPScKJWAULPxMa4N1vCe6szREFlZ7A== @@ -1417,7 +1540,7 @@ "@babel/parser" "^7.16.0" "@babel/types" "^7.16.0" -"@babel/template@^7.16.7": +"@babel/template@^7.16.7", "@babel/template@^7.3.3": version "7.16.7" resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.16.7.tgz#8d126c8701fde4d66b264b3eba3d96f07666d155" integrity sha512-I8j/x8kHUrbYRTUxXrrMbfCa7jxkE7tZre39x3kjr9hvI82cK1FfqLygotcWN5kdPGWcLdWMHpSBavse5tWw3w== @@ -1426,7 +1549,7 @@ "@babel/parser" "^7.16.7" "@babel/types" "^7.16.7" -"@babel/traverse@^7.1.0", "@babel/traverse@^7.1.6", "@babel/traverse@^7.13.0", "@babel/traverse@^7.13.13", "@babel/traverse@^7.4.3", "@babel/traverse@^7.4.5", "@babel/traverse@^7.7.0": +"@babel/traverse@^7.1.6", "@babel/traverse@^7.13.0", "@babel/traverse@^7.13.13", "@babel/traverse@^7.4.3", "@babel/traverse@^7.4.5", "@babel/traverse@^7.7.0": version "7.13.13" resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.13.13.tgz#39aa9c21aab69f74d948a486dd28a2dbdbf5114d" integrity sha512-CblEcwmXKR6eP43oQGG++0QMTtCjAsa3frUuzHoiIJWpaIIi8dwMyEFUJoXRLxagGqCK+jALRwIO+o3R9p/uUg== @@ -1486,6 +1609,22 @@ debug "^4.1.0" globals "^11.1.0" +"@babel/traverse@^7.17.3", "@babel/traverse@^7.17.9", "@babel/traverse@^7.7.2": + version "7.17.9" + resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.17.9.tgz#1f9b207435d9ae4a8ed6998b2b82300d83c37a0d" + integrity sha512-PQO8sDIJ8SIwipTPiR71kJQCKQYB5NGImbOviK8K+kg5xkNSYXLBupuX9QhatFowrsvo9Hj8WgArg3W7ijNAQw== + dependencies: + "@babel/code-frame" "^7.16.7" + "@babel/generator" "^7.17.9" + "@babel/helper-environment-visitor" "^7.16.7" + "@babel/helper-function-name" "^7.17.9" + "@babel/helper-hoist-variables" "^7.16.7" + "@babel/helper-split-export-declaration" "^7.16.7" + "@babel/parser" "^7.17.9" + "@babel/types" "^7.17.0" + debug "^4.1.0" + globals "^11.1.0" + "@babel/types@7.8.3": version "7.8.3" resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.8.3.tgz#5a383dffa5416db1b73dedffd311ffd0788fb31c" @@ -1528,6 +1667,19 @@ "@babel/helper-validator-identifier" "^7.16.7" to-fast-properties "^2.0.0" +"@babel/types@^7.17.0", "@babel/types@^7.3.3": + version "7.17.0" + resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.17.0.tgz#a826e368bccb6b3d84acd76acad5c0d87342390b" + integrity sha512-TmKSNO4D5rzhL5bjWFcVHHLETzfQ/AmbKpKPOSjlP0WoHZ6L911fgoOKY4Alp/emzG4cHJdyN49zpgkbXFEHHw== + dependencies: + "@babel/helper-validator-identifier" "^7.16.7" + to-fast-properties "^2.0.0" + +"@bcoe/v8-coverage@^0.2.3": + version "0.2.3" + resolved "https://registry.yarnpkg.com/@bcoe/v8-coverage/-/v8-coverage-0.2.3.tgz#75a2e8b51cb758a7553d6804a5932d7aace75c39" + integrity sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw== + "@cnakazawa/watch@^1.0.3": version "1.0.4" resolved "https://registry.yarnpkg.com/@cnakazawa/watch/-/watch-1.0.4.tgz#f864ae85004d0fcab6f50be9141c4da368d1656a" @@ -1953,7 +2105,23 @@ resolved "https://registry.yarnpkg.com/@humanwhocodes/object-schema/-/object-schema-1.2.1.tgz#b520529ec21d8e5945a1851dfd1c32e94e39ff45" integrity sha512-ZnQMnLV4e7hDlUvw8H+U8ASL02SS2Gn6+9Ac3wGGLIe7+je2AeAOxPY+izIPJDfFDb7eDjev0Us8MO1iFRN8hA== -"@jest/console@^24.7.1", "@jest/console@^24.9.0": +"@istanbuljs/load-nyc-config@^1.0.0": + version "1.1.0" + resolved "https://registry.yarnpkg.com/@istanbuljs/load-nyc-config/-/load-nyc-config-1.1.0.tgz#fd3db1d59ecf7cf121e80650bb86712f9b55eced" + integrity sha512-VjeHSlIzpv/NyD3N0YuHfXOPDIixcA1q2ZV98wsMqcYlPmv2n3Yb2lYP9XMElnaFVXg5A7YLTeLu6V84uQDjmQ== + dependencies: + camelcase "^5.3.1" + find-up "^4.1.0" + get-package-type "^0.1.0" + js-yaml "^3.13.1" + resolve-from "^5.0.0" + +"@istanbuljs/schema@^0.1.2": + version "0.1.3" + resolved "https://registry.yarnpkg.com/@istanbuljs/schema/-/schema-0.1.3.tgz#e45e384e4b8ec16bce2fd903af78450f6bf7ec98" + integrity sha512-ZXRY4jNvVgSVQ8DL3LTcakaAtXwTVUxE81hslsyD2AtoXW/wVob10HkOJ1X/pAlcI7D+2YoZKg5do8G/w6RYgA== + +"@jest/console@^24.9.0": version "24.9.0" resolved "https://registry.yarnpkg.com/@jest/console/-/console-24.9.0.tgz#79b1bc06fb74a8cfb01cbdedf945584b1b9707f0" integrity sha512-Zuj6b8TnKXi3q4ymac8EQfc3ea/uhLeCGThFqXeC8H9/raaH8ARPUTdId+XyGd03Z4In0/VjD2OYFcBF09fNLQ== @@ -1962,39 +2130,51 @@ chalk "^2.0.1" slash "^2.0.0" -"@jest/core@^24.9.0": - version "24.9.0" - resolved "https://registry.yarnpkg.com/@jest/core/-/core-24.9.0.tgz#2ceccd0b93181f9c4850e74f2a9ad43d351369c4" - integrity sha512-Fogg3s4wlAr1VX7q+rhV9RVnUv5tD7VuWfYy1+whMiWUrvl7U3QJSJyWcDio9Lq2prqYsZaeTv2Rz24pWGkJ2A== +"@jest/console@^27.5.1": + version "27.5.1" + resolved "https://registry.yarnpkg.com/@jest/console/-/console-27.5.1.tgz#260fe7239602fe5130a94f1aa386eff54b014bba" + integrity sha512-kZ/tNpS3NXn0mlXXXPNuDZnb4c0oZ20r4K5eemM2k30ZC3G0T02nXUvyhf5YdbXWHPEJLc9qGLxEZ216MdL+Zg== dependencies: - "@jest/console" "^24.7.1" - "@jest/reporters" "^24.9.0" - "@jest/test-result" "^24.9.0" - "@jest/transform" "^24.9.0" - "@jest/types" "^24.9.0" - ansi-escapes "^3.0.0" - chalk "^2.0.1" + "@jest/types" "^27.5.1" + "@types/node" "*" + chalk "^4.0.0" + jest-message-util "^27.5.1" + jest-util "^27.5.1" + slash "^3.0.0" + +"@jest/core@^27.5.1": + version "27.5.1" + resolved "https://registry.yarnpkg.com/@jest/core/-/core-27.5.1.tgz#267ac5f704e09dc52de2922cbf3af9edcd64b626" + integrity sha512-AK6/UTrvQD0Cd24NSqmIA6rKsu0tKIxfiCducZvqxYdmMisOYAsdItspT+fQDQYARPf8XgjAFZi0ogW2agH5nQ== + dependencies: + "@jest/console" "^27.5.1" + "@jest/reporters" "^27.5.1" + "@jest/test-result" "^27.5.1" + "@jest/transform" "^27.5.1" + "@jest/types" "^27.5.1" + "@types/node" "*" + ansi-escapes "^4.2.1" + chalk "^4.0.0" + emittery "^0.8.1" exit "^0.1.2" - graceful-fs "^4.1.15" - jest-changed-files "^24.9.0" - jest-config "^24.9.0" - jest-haste-map "^24.9.0" - jest-message-util "^24.9.0" - jest-regex-util "^24.3.0" - jest-resolve "^24.9.0" - jest-resolve-dependencies "^24.9.0" - jest-runner "^24.9.0" - jest-runtime "^24.9.0" - jest-snapshot "^24.9.0" - jest-util "^24.9.0" - jest-validate "^24.9.0" - jest-watcher "^24.9.0" - micromatch "^3.1.10" - p-each-series "^1.0.0" - realpath-native "^1.1.0" - rimraf "^2.5.4" - slash "^2.0.0" - strip-ansi "^5.0.0" + graceful-fs "^4.2.9" + jest-changed-files "^27.5.1" + jest-config "^27.5.1" + jest-haste-map "^27.5.1" + jest-message-util "^27.5.1" + jest-regex-util "^27.5.1" + jest-resolve "^27.5.1" + jest-resolve-dependencies "^27.5.1" + jest-runner "^27.5.1" + jest-runtime "^27.5.1" + jest-snapshot "^27.5.1" + jest-util "^27.5.1" + jest-validate "^27.5.1" + jest-watcher "^27.5.1" + micromatch "^4.0.4" + rimraf "^3.0.0" + slash "^3.0.0" + strip-ansi "^6.0.0" "@jest/environment@^24.9.0": version "24.9.0" @@ -2006,6 +2186,16 @@ "@jest/types" "^24.9.0" jest-mock "^24.9.0" +"@jest/environment@^27.5.1": + version "27.5.1" + resolved "https://registry.yarnpkg.com/@jest/environment/-/environment-27.5.1.tgz#d7425820511fe7158abbecc010140c3fd3be9c74" + integrity sha512-/WQjhPJe3/ghaol/4Bq480JKXV/Rfw8nQdN7f41fM8VDHLcxKXou6QyXAh3EFr9/bVG3x74z1NWDkP87EiY8gA== + dependencies: + "@jest/fake-timers" "^27.5.1" + "@jest/types" "^27.5.1" + "@types/node" "*" + jest-mock "^27.5.1" + "@jest/fake-timers@^24.9.0": version "24.9.0" resolved "https://registry.yarnpkg.com/@jest/fake-timers/-/fake-timers-24.9.0.tgz#ba3e6bf0eecd09a636049896434d306636540c93" @@ -2015,34 +2205,59 @@ jest-message-util "^24.9.0" jest-mock "^24.9.0" -"@jest/reporters@^24.9.0": - version "24.9.0" - resolved "https://registry.yarnpkg.com/@jest/reporters/-/reporters-24.9.0.tgz#86660eff8e2b9661d042a8e98a028b8d631a5b43" - integrity sha512-mu4X0yjaHrffOsWmVLzitKmmmWSQ3GGuefgNscUSWNiUNcEOSEQk9k3pERKEQVBb0Cnn88+UESIsZEMH3o88Gw== +"@jest/fake-timers@^27.5.1": + version "27.5.1" + resolved "https://registry.yarnpkg.com/@jest/fake-timers/-/fake-timers-27.5.1.tgz#76979745ce0579c8a94a4678af7a748eda8ada74" + integrity sha512-/aPowoolwa07k7/oM3aASneNeBGCmGQsc3ugN4u6s4C/+s5M64MFo/+djTdiwcbQlRfFElGuDXWzaWj6QgKObQ== dependencies: - "@jest/environment" "^24.9.0" - "@jest/test-result" "^24.9.0" - "@jest/transform" "^24.9.0" - "@jest/types" "^24.9.0" - chalk "^2.0.1" + "@jest/types" "^27.5.1" + "@sinonjs/fake-timers" "^8.0.1" + "@types/node" "*" + jest-message-util "^27.5.1" + jest-mock "^27.5.1" + jest-util "^27.5.1" + +"@jest/globals@^27.5.1": + version "27.5.1" + resolved "https://registry.yarnpkg.com/@jest/globals/-/globals-27.5.1.tgz#7ac06ce57ab966566c7963431cef458434601b2b" + integrity sha512-ZEJNB41OBQQgGzgyInAv0UUfDDj3upmHydjieSxFvTRuZElrx7tXg/uVQ5hYVEwiXs3+aMsAeEc9X7xiSKCm4Q== + dependencies: + "@jest/environment" "^27.5.1" + "@jest/types" "^27.5.1" + expect "^27.5.1" + +"@jest/reporters@^27.5.1": + version "27.5.1" + resolved "https://registry.yarnpkg.com/@jest/reporters/-/reporters-27.5.1.tgz#ceda7be96170b03c923c37987b64015812ffec04" + integrity sha512-cPXh9hWIlVJMQkVk84aIvXuBB4uQQmFqZiacloFuGiP3ah1sbCxCosidXFDfqG8+6fO1oR2dTJTlsOy4VFmUfw== + dependencies: + "@bcoe/v8-coverage" "^0.2.3" + "@jest/console" "^27.5.1" + "@jest/test-result" "^27.5.1" + "@jest/transform" "^27.5.1" + "@jest/types" "^27.5.1" + "@types/node" "*" + chalk "^4.0.0" + collect-v8-coverage "^1.0.0" exit "^0.1.2" glob "^7.1.2" - istanbul-lib-coverage "^2.0.2" - istanbul-lib-instrument "^3.0.1" - istanbul-lib-report "^2.0.4" - istanbul-lib-source-maps "^3.0.1" - istanbul-reports "^2.2.6" - jest-haste-map "^24.9.0" - jest-resolve "^24.9.0" - jest-runtime "^24.9.0" - jest-util "^24.9.0" - jest-worker "^24.6.0" - node-notifier "^5.4.2" - slash "^2.0.0" + graceful-fs "^4.2.9" + istanbul-lib-coverage "^3.0.0" + istanbul-lib-instrument "^5.1.0" + istanbul-lib-report "^3.0.0" + istanbul-lib-source-maps "^4.0.0" + istanbul-reports "^3.1.3" + jest-haste-map "^27.5.1" + jest-resolve "^27.5.1" + jest-util "^27.5.1" + jest-worker "^27.5.1" + slash "^3.0.0" source-map "^0.6.0" - string-length "^2.0.0" + string-length "^4.0.1" + terminal-link "^2.0.0" + v8-to-istanbul "^8.1.0" -"@jest/source-map@^24.3.0", "@jest/source-map@^24.9.0": +"@jest/source-map@^24.9.0": version "24.9.0" resolved "https://registry.yarnpkg.com/@jest/source-map/-/source-map-24.9.0.tgz#0e263a94430be4b41da683ccc1e6bffe2a191714" integrity sha512-/Xw7xGlsZb4MJzNDgB7PW5crou5JqWiBQaz6xyPd3ArOg2nfn/PunV8+olXbbEZzNl591o5rWKE9BRDaFAuIBg== @@ -2051,6 +2266,15 @@ graceful-fs "^4.1.15" source-map "^0.6.0" +"@jest/source-map@^27.5.1": + version "27.5.1" + resolved "https://registry.yarnpkg.com/@jest/source-map/-/source-map-27.5.1.tgz#6608391e465add4205eae073b55e7f279e04e8cf" + integrity sha512-y9NIHUYF3PJRlHk98NdC/N1gl88BL08aQQgu4k4ZopQkCw9t9cV8mtl3TV8b/YCB8XaVTFrmUTAJvjsntDireg== + dependencies: + callsites "^3.0.0" + graceful-fs "^4.2.9" + source-map "^0.6.0" + "@jest/test-result@^24.9.0": version "24.9.0" resolved "https://registry.yarnpkg.com/@jest/test-result/-/test-result-24.9.0.tgz#11796e8aa9dbf88ea025757b3152595ad06ba0ca" @@ -2060,15 +2284,25 @@ "@jest/types" "^24.9.0" "@types/istanbul-lib-coverage" "^2.0.0" -"@jest/test-sequencer@^24.9.0": - version "24.9.0" - resolved "https://registry.yarnpkg.com/@jest/test-sequencer/-/test-sequencer-24.9.0.tgz#f8f334f35b625a4f2f355f2fe7e6036dad2e6b31" - integrity sha512-6qqsU4o0kW1dvA95qfNog8v8gkRN9ph6Lz7r96IvZpHdNipP2cBcb07J1Z45mz/VIS01OHJ3pY8T5fUY38tg4A== +"@jest/test-result@^27.5.1": + version "27.5.1" + resolved "https://registry.yarnpkg.com/@jest/test-result/-/test-result-27.5.1.tgz#56a6585fa80f7cdab72b8c5fc2e871d03832f5bb" + integrity sha512-EW35l2RYFUcUQxFJz5Cv5MTOxlJIQs4I7gxzi2zVU7PJhOwfYq1MdC5nhSmYjX1gmMmLPvB3sIaC+BkcHRBfag== dependencies: - "@jest/test-result" "^24.9.0" - jest-haste-map "^24.9.0" - jest-runner "^24.9.0" - jest-runtime "^24.9.0" + "@jest/console" "^27.5.1" + "@jest/types" "^27.5.1" + "@types/istanbul-lib-coverage" "^2.0.0" + collect-v8-coverage "^1.0.0" + +"@jest/test-sequencer@^27.5.1": + version "27.5.1" + resolved "https://registry.yarnpkg.com/@jest/test-sequencer/-/test-sequencer-27.5.1.tgz#4057e0e9cea4439e544c6353c6affe58d095745b" + integrity sha512-LCheJF7WB2+9JuCS7VB/EmGIdQuhtqjRNI9A43idHv3E4KltCTsPsLxvdaubFHSYwY/fNjMWjl6vNRhDiN7vpQ== + dependencies: + "@jest/test-result" "^27.5.1" + graceful-fs "^4.2.9" + jest-haste-map "^27.5.1" + jest-runtime "^27.5.1" "@jest/transform@^24.9.0": version "24.9.0" @@ -2092,6 +2326,27 @@ source-map "^0.6.1" write-file-atomic "2.4.1" +"@jest/transform@^27.5.1": + version "27.5.1" + resolved "https://registry.yarnpkg.com/@jest/transform/-/transform-27.5.1.tgz#6c3501dcc00c4c08915f292a600ece5ecfe1f409" + integrity sha512-ipON6WtYgl/1329g5AIJVbUuEh0wZVbdpGwC99Jw4LwuoBNS95MVphU6zOeD9pDkon+LLbFL7lOQRapbB8SCHw== + dependencies: + "@babel/core" "^7.1.0" + "@jest/types" "^27.5.1" + babel-plugin-istanbul "^6.1.1" + chalk "^4.0.0" + convert-source-map "^1.4.0" + fast-json-stable-stringify "^2.0.0" + graceful-fs "^4.2.9" + jest-haste-map "^27.5.1" + jest-regex-util "^27.5.1" + jest-util "^27.5.1" + micromatch "^4.0.4" + pirates "^4.0.4" + slash "^3.0.0" + source-map "^0.6.1" + write-file-atomic "^3.0.0" + "@jest/types@>=24 <=26": version "26.6.2" resolved "https://registry.yarnpkg.com/@jest/types/-/types-26.6.2.tgz#bef5a532030e1d88a2f5a6d933f84e97226ed48e" @@ -2123,6 +2378,35 @@ "@types/yargs" "^16.0.0" chalk "^4.0.0" +"@jest/types@^27.5.1": + version "27.5.1" + resolved "https://registry.yarnpkg.com/@jest/types/-/types-27.5.1.tgz#3c79ec4a8ba61c170bf937bcf9e98a9df175ec80" + integrity sha512-Cx46iJ9QpwQTjIdq5VJu2QTMMs3QlEjI0x1QbBP5W1+nMzyc2XmimiRR/CbX9TO0cPTeUlxWMOu8mslYsJ8DEw== + dependencies: + "@types/istanbul-lib-coverage" "^2.0.0" + "@types/istanbul-reports" "^3.0.0" + "@types/node" "*" + "@types/yargs" "^16.0.0" + chalk "^4.0.0" + +"@jridgewell/resolve-uri@^3.0.3": + version "3.0.5" + resolved "https://registry.yarnpkg.com/@jridgewell/resolve-uri/-/resolve-uri-3.0.5.tgz#68eb521368db76d040a6315cdb24bf2483037b9c" + integrity sha512-VPeQ7+wH0itvQxnG+lIzWgkysKIr3L9sslimFW55rHMdGu/qCQ5z5h9zq4gI8uBtqkpHhsF4Z/OwExufUCThew== + +"@jridgewell/sourcemap-codec@^1.4.10": + version "1.4.11" + resolved "https://registry.yarnpkg.com/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.11.tgz#771a1d8d744eeb71b6adb35808e1a6c7b9b8c8ec" + integrity sha512-Fg32GrJo61m+VqYSdRSjRXMjQ06j8YIYfcTqndLYVAaHmroZHLJZCydsWBOTDqXS2v+mjxohBWEMfg97GXmYQg== + +"@jridgewell/trace-mapping@^0.3.0": + version "0.3.4" + resolved "https://registry.yarnpkg.com/@jridgewell/trace-mapping/-/trace-mapping-0.3.4.tgz#f6a0832dffd5b8a6aaa633b7d9f8e8e94c83a0c3" + integrity sha512-vFv9ttIedivx0ux3QSjhgtCVjPZd5l46ZOMDSCwnH1yUO2e964gO8LZGyv2QkqcgR6TnBU1v+1IFqmeoG+0UJQ== + dependencies: + "@jridgewell/resolve-uri" "^3.0.3" + "@jridgewell/sourcemap-codec" "^1.4.10" + "@lerna/add@3.13.3": version "3.13.3" resolved "https://registry.yarnpkg.com/@lerna/add/-/add-3.13.3.tgz#f4c1674839780e458f0426d4f7b6d0a77b9a2ae9" @@ -3173,6 +3457,13 @@ dependencies: "@sinonjs/commons" "^1.7.0" +"@sinonjs/fake-timers@^8.0.1": + version "8.1.0" + resolved "https://registry.yarnpkg.com/@sinonjs/fake-timers/-/fake-timers-8.1.0.tgz#3fdc2b6cb58935b21bfb8d1625eb1300484316e7" + integrity sha512-OAPJUAtgeINhh/TAlUID4QTs53Njm7xzddaVlEs/SXwgtiD1tW22zAB/W1wdqfrpmikgaWQ9Fw6Ws+hsiRm5Vg== + dependencies: + "@sinonjs/commons" "^1.7.0" + "@sinonjs/formatio@^3.2.1": version "3.2.2" resolved "https://registry.yarnpkg.com/@sinonjs/formatio/-/formatio-3.2.2.tgz#771c60dfa75ea7f2d68e3b94c7e888a78781372c" @@ -3287,6 +3578,11 @@ resolved "https://registry.yarnpkg.com/@tootallnate/once/-/once-1.1.2.tgz#ccb91445360179a04e7fe6aff78c00ffc1eeaf82" integrity sha512-RbzJvlNzmRq5c3O09UipeuXno4tA1FE6ikOjxZK0tuxVv3412l64l5t1W5pj4+rJq9vpkm/kwiR07aZXnsKPxw== +"@tootallnate/once@2": + version "2.0.0" + resolved "https://registry.yarnpkg.com/@tootallnate/once/-/once-2.0.0.tgz#f544a148d3ab35801c1f633a7441fd87c2e484bf" + integrity sha512-XCuKFP5PS55gnMVu3dty8KPatLqUoy/ZYzDzAGCQ8JNFCkLXzmI7vNHCR+XpbZaMWQK/vQubr7PkYq8g470J/A== + "@ts-type/package-dts@^1.0.58": version "1.0.58" resolved "https://registry.yarnpkg.com/@ts-type/package-dts/-/package-dts-1.0.58.tgz#75f6fdf5f1e8f262a5081b90346439b4c4bc8d01" @@ -3305,10 +3601,10 @@ resolved "https://registry.yarnpkg.com/@types/aws-lambda/-/aws-lambda-8.10.73.tgz#77773c9accb2cec26fcb7c6b510a555805604a53" integrity sha512-P+a6TRQbRnVQOIjWkmw6F23wiJcF+4Uniasbzx7NAXjLQCVGx/Z4VoMfit81/pxlmcXNxAMGuYPugn6CrJLilQ== -"@types/babel__core@^7.1.0": - version "7.1.16" - resolved "https://registry.yarnpkg.com/@types/babel__core/-/babel__core-7.1.16.tgz#bc12c74b7d65e82d29876b5d0baf5c625ac58702" - integrity sha512-EAEHtisTMM+KaKwfWdC3oyllIqswlznXCIVCt7/oRNrh+DhgT4UEBNC/jlADNjvw7UnfbcdkGQcPVZ1xYiLcrQ== +"@types/babel__core@^7.0.0", "@types/babel__core@^7.1.14": + version "7.1.19" + resolved "https://registry.yarnpkg.com/@types/babel__core/-/babel__core-7.1.19.tgz#7b497495b7d1b4812bdb9d02804d0576f43ee460" + integrity sha512-WEOTgRsbYkvA/KCsDwVEGkd7WAr1e3g31VHQ8zy5gul/V1qKullU/BU5I68X5v7V3GnB9eotmom4v5a5gjxorw== dependencies: "@babel/parser" "^7.1.0" "@babel/types" "^7.0.0" @@ -3338,6 +3634,13 @@ dependencies: "@babel/types" "^7.3.0" +"@types/babel__traverse@^7.0.4": + version "7.14.2" + resolved "https://registry.yarnpkg.com/@types/babel__traverse/-/babel__traverse-7.14.2.tgz#ffcd470bbb3f8bf30481678fb5502278ca833a43" + integrity sha512-K2waXdXBi2302XUdcHcR1jCeU0LL4TD9HRs/gk0N2Xvrht+G/BfJa4QObBQZfhMdxiCpV3COl5Nfq4uKTeTnJA== + dependencies: + "@babel/types" "^7.3.0" + "@types/body-parser@*": version "1.19.0" resolved "https://registry.yarnpkg.com/@types/body-parser/-/body-parser-1.19.0.tgz#0685b3c47eb3006ffed117cdd55164b61f80538f" @@ -3663,6 +3966,13 @@ "@types/minimatch" "*" "@types/node" "*" +"@types/graceful-fs@^4.1.2": + version "4.1.5" + resolved "https://registry.yarnpkg.com/@types/graceful-fs/-/graceful-fs-4.1.5.tgz#21ffba0d98da4350db64891f92a9e5db3cdb4e15" + integrity sha512-anKkLmZZ+xm4p8JWBf4hElkM4XR+EZeA2M9BAkkTldmcyDY4mbdIJnRghDJH3Ov5ooY7/UAoENtmdMSkaAd7Cw== + dependencies: + "@types/node" "*" + "@types/history-4@npm:@types/history@4.7.8", "@types/history-5@npm:@types/history@4.7.8", "@types/history@*": name "@types/history-4" version "4.7.8" @@ -3697,6 +4007,11 @@ resolved "https://registry.yarnpkg.com/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.3.tgz#4ba8ddb720221f432e443bd5f9117fd22cfd4762" integrity sha512-sz7iLqvVUg1gIedBOvlkxPlc8/uVzyS5OwGz1cKjXzkl3FpL3al0crU8YGU1WoHkxn0Wxbw5tyi6hvzJKNzFsw== +"@types/istanbul-lib-coverage@^2.0.1": + version "2.0.4" + resolved "https://registry.yarnpkg.com/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.4.tgz#8467d4b3c087805d63580480890791277ce35c44" + integrity sha512-z/QT1XN4K4KYuslS23k62yDIDLwLFkzxOuMplDtObz0+y7VqJCaO2o+SPwHCvLFZh7xazvvoor2tA/hPz9ee7g== + "@types/istanbul-lib-report@*": version "3.0.0" resolved "https://registry.yarnpkg.com/@types/istanbul-lib-report/-/istanbul-lib-report-3.0.0.tgz#c14c24f18ea8190c118ee7562b7ff99a36552686" @@ -3857,6 +4172,11 @@ pg-protocol "*" pg-types "^2.2.0" +"@types/prettier@^2.1.5": + version "2.4.4" + resolved "https://registry.yarnpkg.com/@types/prettier/-/prettier-2.4.4.tgz#5d9b63132df54d8909fce1c3f8ca260fdd693e17" + integrity sha512-ReVR2rLTV1kvtlWFyuot+d1pkpG2Fw/XKE3PDAdj57rbM97ttSp9JZ2UsP+2EHTylra9cUf6JA7tGwW1INzUrA== + "@types/prop-types@*": version "15.7.3" resolved "https://registry.yarnpkg.com/@types/prop-types/-/prop-types-15.7.3.tgz#2ab0d5da2e5815f94b0b9d4b95d1e5f243ab2ca7" @@ -4464,7 +4784,7 @@ JSONStream@^1.0.4, JSONStream@^1.3.4: jsonparse "^1.2.0" through ">=2.2.7 <3" -abab@^2.0.0, abab@^2.0.3, abab@^2.0.5: +abab@^2.0.3, abab@^2.0.5: version "2.0.5" resolved "https://registry.yarnpkg.com/abab/-/abab-2.0.5.tgz#c0b678fb32d60fc1219c784d6a826fe385aeb79a" integrity sha512-9IK9EadsbHo6jLWIpxpR6pL0sazTXV6+SQv25ZB+F7Bj9mJNaOc4nCRabwd5M/JwmUa8idz6Eci6eKfJryPs6Q== @@ -4502,14 +4822,6 @@ accepts@~1.3.8: mime-types "~2.1.34" negotiator "0.6.3" -acorn-globals@^4.1.0, acorn-globals@^4.3.2: - version "4.3.4" - resolved "https://registry.yarnpkg.com/acorn-globals/-/acorn-globals-4.3.4.tgz#9fa1926addc11c97308c4e66d7add0d40c3272e7" - integrity sha512-clfQEh21R+D0leSbUdWf3OcfqyaCSAQ8Ryq00bofSekfr9W8u1jyYZo6ir0xu9Gtcf7BjcHJpnbZH7JOCpP60A== - dependencies: - acorn "^6.0.1" - acorn-walk "^6.0.1" - acorn-globals@^6.0.0: version "6.0.0" resolved "https://registry.yarnpkg.com/acorn-globals/-/acorn-globals-6.0.0.tgz#46cdd39f0f8ff08a876619b55f5ac8a6dc770b45" @@ -4528,7 +4840,7 @@ acorn-jsx@^5.3.1: resolved "https://registry.yarnpkg.com/acorn-jsx/-/acorn-jsx-5.3.1.tgz#fc8661e11b7ac1539c47dbfea2e72b3af34d267b" integrity sha512-K0Ptm/47OKfQRpNQ2J/oIN/3QYiK6FwW+eJbILhsdxh2WTLdl+30o8aGdTbm5JbffpFFAg/g+zi1E+jvJha5ng== -acorn-walk@^6.0.1, acorn-walk@^6.1.1: +acorn-walk@^6.1.1: version "6.2.0" resolved "https://registry.yarnpkg.com/acorn-walk/-/acorn-walk-6.2.0.tgz#123cb8f3b84c2171f1f7fb252615b1c78a6b1a8c" integrity sha512-7evsyfH1cLOCdAzZAd43Cic04yKydNx0cF+7tiA19p1XnLLPU4dpCQOqpjqwokFe//vS0QqfqqjCS2JkiIs0cA== @@ -4543,26 +4855,26 @@ acorn-walk@^8.0.0: resolved "https://registry.yarnpkg.com/acorn-walk/-/acorn-walk-8.0.2.tgz#d4632bfc63fd93d0f15fd05ea0e984ffd3f5a8c3" integrity sha512-+bpA9MJsHdZ4bgfDcpk0ozQyhhVct7rzOmO0s1IIr0AGGgKBljss8n2zp11rRP2wid5VGeh04CgeKzgat5/25A== -acorn@^5.5.3: - version "5.7.4" - resolved "https://registry.yarnpkg.com/acorn/-/acorn-5.7.4.tgz#3e8d8a9947d0599a1796d10225d7432f4a4acf5e" - integrity sha512-1D++VG7BhrtvQpNbBzovKNc1FLGGEE/oGe7b9xJm/RFHMBeUaUGpluV9RLjZa47YFdPcDAenEYuq9pQPcMdLJg== - -acorn@^6.0.1, acorn@^6.0.5, acorn@^6.4.1: +acorn@^6.0.5, acorn@^6.4.1: version "6.4.2" resolved "https://registry.yarnpkg.com/acorn/-/acorn-6.4.2.tgz#35866fd710528e92de10cf06016498e47e39e1e6" integrity sha512-XtGIhXwF8YM8bJhGxG5kXgjkEuNGLTkoYqVE+KMR+aspr4KGYmKYg7yUe3KghyQ9yheNwLnjmzh/7+gfDBmHCQ== -acorn@^7.1.0, acorn@^7.1.1, acorn@^7.4.0: +acorn@^7.1.1, acorn@^7.4.0: version "7.4.1" resolved "https://registry.yarnpkg.com/acorn/-/acorn-7.4.1.tgz#feaed255973d2e77555b83dbc08851a6c63520fa" integrity sha512-nQyp0o1/mNdbTO1PO6kHkwSrmgZ0MT/jCCpNiwbUjGoRN4dlBhqJtoQuCnEOKzgTVwg0ZWiCoQy6SxMebQVh8A== -acorn@^8.0.4, acorn@^8.1.0: +acorn@^8.0.4: version "8.1.0" resolved "https://registry.yarnpkg.com/acorn/-/acorn-8.1.0.tgz#52311fd7037ae119cbb134309e901aa46295b3fe" integrity sha512-LWCF/Wn0nfHOmJ9rzQApGnxnvgfROzGilS8936rqN/lfcYkY9MYZzdMqN+2NJ4SlTc+m5HiSa+kNfDtI64dwUA== +acorn@^8.2.4, acorn@^8.5.0: + version "8.7.0" + resolved "https://registry.yarnpkg.com/acorn/-/acorn-8.7.0.tgz#90951fde0f8f09df93549481e5fc141445b791cf" + integrity sha512-V/LGr1APy+PXIwKebEWrkZPwoeoF+w1jiOBUmuxuiUIaOHtob8Qc9BTrYo7VuI5fR8tqsy+buA2WFooR5olqvQ== + acorn@^8.4.1: version "8.6.0" resolved "https://registry.yarnpkg.com/acorn/-/acorn-8.6.0.tgz#e3692ba0eb1a0c83eaa4f37f5fa7368dd7142895" @@ -4662,7 +4974,7 @@ ansi-colors@^4.1.1: resolved "https://registry.yarnpkg.com/ansi-colors/-/ansi-colors-4.1.1.tgz#cbb9ae256bf750af1eab344f229aa27fe94ba348" integrity sha512-JoX0apGbHaUJBNl6yF+p6JAFYZ666/hhCGKN5t9QFjbJQKUU/g8MNbFDbvfrgKXvI1QpZplPOnwIo99lX/AAmA== -ansi-escapes@^3.0.0, ansi-escapes@^3.2.0: +ansi-escapes@^3.2.0: version "3.2.0" resolved "https://registry.yarnpkg.com/ansi-escapes/-/ansi-escapes-3.2.0.tgz#8780b98ff9dbf5638152d1f1fe5c1d7b4442976b" integrity sha512-cBhpre4ma+U0T1oM5fXg7Dy1Jw7zzwv7lt/GoCpr+hDQJoYnKVPLL4dCvSEFMmQurOQvSrwT7SL/DAlhBI97RQ== @@ -4760,7 +5072,7 @@ anymatch@^2.0.0: micromatch "^3.1.4" normalize-path "^2.1.1" -anymatch@^3.0.0, anymatch@~3.1.1: +anymatch@^3.0.0, anymatch@^3.0.3, anymatch@~3.1.1: version "3.1.2" resolved "https://registry.yarnpkg.com/anymatch/-/anymatch-3.1.2.tgz#c0557c096af32f106198f4f4e2a383537e378716" integrity sha512-P43ePfOAIupkguHUycrc4qJ9kz8ZiuOUijaETwX7THt0Y/GNK7v0aa8rY816xWjZ7rJdA5XdMcpVFTKMq+RvWg== @@ -5022,11 +5334,6 @@ ast-types@0.13.3: resolved "https://registry.yarnpkg.com/ast-types/-/ast-types-0.13.3.tgz#50da3f28d17bdbc7969a3a2d83a0e4a72ae755a7" integrity sha512-XTZ7xGML849LkQP86sWdQzfhwbt3YwIO6MqbX9mUNYY98VKaaVZP7YNNm70IpwecbkkxmfC5IYAzOQ/2p29zRA== -astral-regex@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/astral-regex/-/astral-regex-1.0.0.tgz#6c8c3fb827dd43ee3918f27b82782ab7658a6fd9" - integrity sha512-+Ryf6g3BKoRc7jfp7ad8tM4TtMiaWvbF/1/sQcZPkkS7ag3D5nMBCe2UfOTONtAkaG0tO0ij3C5Lwmf1EiyjHg== - astral-regex@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/astral-regex/-/astral-regex-2.0.0.tgz#483143c567aeed4785759c0865786dc77d7d2e31" @@ -5063,11 +5370,6 @@ async-each@^1.0.1: resolved "https://registry.yarnpkg.com/async-each/-/async-each-1.0.3.tgz#b727dbf87d7651602f06f4d4ac387f47d91b0cbf" integrity sha512-z/WhQ5FPySLdvREByI2vZiTWwCnF0moMJ1hK9YQwDTHKh6I7/uSckMetoRGb5UBZPC1z0jlw+n/XCgjeH7y1AQ== -async-limiter@~1.0.0: - version "1.0.1" - resolved "https://registry.yarnpkg.com/async-limiter/-/async-limiter-1.0.1.tgz#dd379e94f0db8310b08291f9d64c3209766617fd" - integrity sha512-csOlWGAcRFJaI6m+F2WKdnMKr4HhdhFVBk0H/QbJFMCr+uO2kwohwXQPxw/9OCxp05r5ghVBFSyioixx3gfkNQ== - async-mutex@^0.3.2: version "0.3.2" resolved "https://registry.yarnpkg.com/async-mutex/-/async-mutex-0.3.2.tgz#1485eda5bda1b0ec7c8df1ac2e815757ad1831df" @@ -5342,18 +5644,19 @@ babel-import-util@^0.2.0: resolved "https://registry.yarnpkg.com/babel-import-util/-/babel-import-util-0.2.0.tgz#b468bb679919601a3570f9e317536c54f2862e23" integrity sha512-CtWYYHU/MgK88rxMrLfkD356dApswtR/kWZ/c6JifG1m10e7tBBrs/366dFzWMAoqYmG5/JSh+94tUSpIwh+ag== -babel-jest@^24.9.0: - version "24.9.0" - resolved "https://registry.yarnpkg.com/babel-jest/-/babel-jest-24.9.0.tgz#3fc327cb8467b89d14d7bc70e315104a783ccd54" - integrity sha512-ntuddfyiN+EhMw58PTNL1ph4C9rECiQXjI4nMMBKBaNjXvqLdkXpPRcMSr4iyBrJg/+wz9brFUD6RhOAT6r4Iw== - dependencies: - "@jest/transform" "^24.9.0" - "@jest/types" "^24.9.0" - "@types/babel__core" "^7.1.0" - babel-plugin-istanbul "^5.1.0" - babel-preset-jest "^24.9.0" - chalk "^2.4.2" - slash "^2.0.0" +babel-jest@^27.5.1: + version "27.5.1" + resolved "https://registry.yarnpkg.com/babel-jest/-/babel-jest-27.5.1.tgz#a1bf8d61928edfefd21da27eb86a695bfd691444" + integrity sha512-cdQ5dXjGRd0IBRATiQ4mZGlGlRE8kJpjPOixdNRdT+m3UcNqmYWN6rK6nvtXYfY3D76cb8s/O1Ss8ea24PIwcg== + dependencies: + "@jest/transform" "^27.5.1" + "@jest/types" "^27.5.1" + "@types/babel__core" "^7.1.14" + babel-plugin-istanbul "^6.1.1" + babel-preset-jest "^27.5.1" + chalk "^4.0.0" + graceful-fs "^4.2.9" + slash "^3.0.0" babel-loader@^8.0.6: version "8.2.2" @@ -5486,11 +5789,25 @@ babel-plugin-istanbul@^5.1.0: istanbul-lib-instrument "^3.3.0" test-exclude "^5.2.3" -babel-plugin-jest-hoist@^24.9.0: - version "24.9.0" - resolved "https://registry.yarnpkg.com/babel-plugin-jest-hoist/-/babel-plugin-jest-hoist-24.9.0.tgz#4f837091eb407e01447c8843cbec546d0002d756" - integrity sha512-2EMA2P8Vp7lG0RAzr4HXqtYwacfMErOuv1U3wrvxHX6rD1sV6xS3WXG3r8TRQ2r6w8OhvSdWt+z41hQNwNm3Xw== +babel-plugin-istanbul@^6.1.1: + version "6.1.1" + resolved "https://registry.yarnpkg.com/babel-plugin-istanbul/-/babel-plugin-istanbul-6.1.1.tgz#fa88ec59232fd9b4e36dbbc540a8ec9a9b47da73" + integrity sha512-Y1IQok9821cC9onCx5otgFfRm7Lm+I+wwxOx738M/WLPZ9Q42m4IG5W0FNX8WLL2gYMZo3JkuXIH2DOpWM+qwA== dependencies: + "@babel/helper-plugin-utils" "^7.0.0" + "@istanbuljs/load-nyc-config" "^1.0.0" + "@istanbuljs/schema" "^0.1.2" + istanbul-lib-instrument "^5.0.4" + test-exclude "^6.0.0" + +babel-plugin-jest-hoist@^27.5.1: + version "27.5.1" + resolved "https://registry.yarnpkg.com/babel-plugin-jest-hoist/-/babel-plugin-jest-hoist-27.5.1.tgz#9be98ecf28c331eb9f5df9c72d6f89deb8181c2e" + integrity sha512-50wCwD5EMNW4aRpOwtqzyZHIewTYNxLA4nhB+09d8BIssfNfzBRhkBIHiaPv1Si226TQSvp8gxAJm2iY2qs2hQ== + dependencies: + "@babel/template" "^7.3.3" + "@babel/types" "^7.3.3" + "@types/babel__core" "^7.0.0" "@types/babel__traverse" "^7.0.6" babel-plugin-module-resolver@^3.2.0: @@ -5796,6 +6113,24 @@ babel-polyfill@^6.26.0: core-js "^2.5.0" regenerator-runtime "^0.10.5" +babel-preset-current-node-syntax@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/babel-preset-current-node-syntax/-/babel-preset-current-node-syntax-1.0.1.tgz#b4399239b89b2a011f9ddbe3e4f401fc40cff73b" + integrity sha512-M7LQ0bxarkxQoN+vz5aJPsLBn77n8QgTFmo8WK0/44auK2xlCXrYcUxHFxgU7qW5Yzw/CjmLRK2uJzaCd7LvqQ== + dependencies: + "@babel/plugin-syntax-async-generators" "^7.8.4" + "@babel/plugin-syntax-bigint" "^7.8.3" + "@babel/plugin-syntax-class-properties" "^7.8.3" + "@babel/plugin-syntax-import-meta" "^7.8.3" + "@babel/plugin-syntax-json-strings" "^7.8.3" + "@babel/plugin-syntax-logical-assignment-operators" "^7.8.3" + "@babel/plugin-syntax-nullish-coalescing-operator" "^7.8.3" + "@babel/plugin-syntax-numeric-separator" "^7.8.3" + "@babel/plugin-syntax-object-rest-spread" "^7.8.3" + "@babel/plugin-syntax-optional-catch-binding" "^7.8.3" + "@babel/plugin-syntax-optional-chaining" "^7.8.3" + "@babel/plugin-syntax-top-level-await" "^7.8.3" + babel-preset-env@^1.7.0: version "1.7.0" resolved "https://registry.yarnpkg.com/babel-preset-env/-/babel-preset-env-1.7.0.tgz#dea79fa4ebeb883cd35dab07e260c1c9c04df77a" @@ -5832,13 +6167,13 @@ babel-preset-env@^1.7.0: invariant "^2.2.2" semver "^5.3.0" -babel-preset-jest@^24.9.0: - version "24.9.0" - resolved "https://registry.yarnpkg.com/babel-preset-jest/-/babel-preset-jest-24.9.0.tgz#192b521e2217fb1d1f67cf73f70c336650ad3cdc" - integrity sha512-izTUuhE4TMfTRPF92fFwD2QfdXaZW08qvWTFCI51V8rW5x00UuPgc3ajRoWofXOuxjfcOM5zzSYsQS3H8KGCAg== +babel-preset-jest@^27.5.1: + version "27.5.1" + resolved "https://registry.yarnpkg.com/babel-preset-jest/-/babel-preset-jest-27.5.1.tgz#91f10f58034cb7989cb4f962b69fa6eef6a6bc81" + integrity sha512-Nptf2FzlPCWYuJg41HBqXVT8ym6bXOevuCTbhxlUpjwtysGaIWFvDEjp4y+G7fl13FgOdjs7P/DmErqH7da0Ag== dependencies: - "@babel/plugin-syntax-object-rest-spread" "^7.0.0" - babel-plugin-jest-hoist "^24.9.0" + babel-plugin-jest-hoist "^27.5.1" + babel-preset-current-node-syntax "^1.0.0" babel-register@^6.26.0: version "6.26.0" @@ -6892,7 +7227,7 @@ buffer-equal-constant-time@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= -buffer-from@1.x, buffer-from@^1.0.0: +buffer-from@^1.0.0: version "1.1.1" resolved "https://registry.yarnpkg.com/buffer-from/-/buffer-from-1.1.1.tgz#32713bc028f75c02fdb710d7c7bcec1f2c6070ef" integrity sha512-MQcXEUbCKtEo7bhqEs6560Hyd4XaovZlO/k9V3hjVUF/zwW7KBVdSK4gIt/bzwS9MbR5qob+F5jusZsb0YQK2A== @@ -7262,6 +7597,11 @@ chalk@^4.0.0, chalk@^4.1.0: ansi-styles "^4.1.0" supports-color "^7.1.0" +char-regex@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/char-regex/-/char-regex-1.0.2.tgz#d744358226217f981ed58f479b1d6bcc29545dcf" + integrity sha512-kWWXztvZ5SBQV+eRgKFeh8q5sLuZY2+8WUIzlxWVTg+oGwY14qylx1KbKzHd8P6ZYkAg0xyIDU9JMHhyJMZ1jw== + chardet@^0.7.0: version "0.7.0" resolved "https://registry.yarnpkg.com/chardet/-/chardet-0.7.0.tgz#90094849f0937f2eedc2425d0d28a9e5f0cbad9e" @@ -7335,6 +7675,11 @@ ci-info@^2.0.0: resolved "https://registry.yarnpkg.com/ci-info/-/ci-info-2.0.0.tgz#67a9e964be31a51e15e5010d58e6f12834002f46" integrity sha512-5tK7EtrZ0N+OLFMthtqOj4fI2Jeb88C4CAZPu25LDVUgXJ0A3Js4PMGqrn0JU1W0Mh1/Z8wZzYPxqUrXeBboCQ== +ci-info@^3.2.0: + version "3.3.0" + resolved "https://registry.yarnpkg.com/ci-info/-/ci-info-3.3.0.tgz#b4ed1fb6818dea4803a55c623041f9165d2066b2" + integrity sha512-riT/3vI5YpVH6/qomlDnJow6TBee2PBKSEpx3O32EGPYbWGIRsIlGRms3Sm74wYE1JMo8RnO04Hb12+v1J5ICw== + ci-job-number@^1.2.2: version "1.2.2" resolved "https://registry.yarnpkg.com/ci-job-number/-/ci-job-number-1.2.2.tgz#f4e5918fcaeeda95b604f214be7d7d4a961fe0c0" @@ -7348,6 +7693,11 @@ cipher-base@^1.0.0, cipher-base@^1.0.1, cipher-base@^1.0.3: inherits "^2.0.1" safe-buffer "^5.0.1" +cjs-module-lexer@^1.0.0: + version "1.2.2" + resolved "https://registry.yarnpkg.com/cjs-module-lexer/-/cjs-module-lexer-1.2.2.tgz#9f84ba3244a512f3a54e5277e8eef4c489864e40" + integrity sha512-cOU9usZw8/dXIXKtwa8pM0OTJQuJkxMN6w30csNRUerHfeQ5R6U3kkU/FtJeIf3M202OHfY2U8ccInBG7/xogA== + class-utils@^0.3.5: version "0.3.6" resolved "https://registry.yarnpkg.com/class-utils/-/class-utils-0.3.6.tgz#f93369ae8b9a7ce02fd41faad0ca83033190c463" @@ -7550,6 +7900,11 @@ codecov@^3.6.5: teeny-request "6.0.1" urlgrey "0.4.4" +collect-v8-coverage@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/collect-v8-coverage/-/collect-v8-coverage-1.0.1.tgz#cc2c8e94fc18bbdffe64d6534570c8a673b27f59" + integrity sha512-iBPtljfCNcTKNAto0KEtDfZ3qzjJvqE3aTGZsbhjSBlorqpXJlaWWtPO35D+ZImoC3KWejX64o+yPGxhWSTzfg== + collection-visit@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/collection-visit/-/collection-visit-1.0.0.tgz#4bc0373c164bc3291b4d368c829cf1a80a59dca0" @@ -7631,7 +7986,7 @@ combine-source-map@^0.8.0: lodash.memoize "~3.0.3" source-map "~0.5.3" -combined-stream@^1.0.6, combined-stream@~1.0.6: +combined-stream@^1.0.6, 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== @@ -8122,7 +8477,7 @@ cross-spawn@^6.0.0, cross-spawn@^6.0.5: shebang-command "^1.2.0" which "^1.2.9" -cross-spawn@^7.0.0, cross-spawn@^7.0.1, cross-spawn@^7.0.2: +cross-spawn@^7.0.0, cross-spawn@^7.0.1, cross-spawn@^7.0.2, cross-spawn@^7.0.3: version "7.0.3" resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-7.0.3.tgz#f73a85b9d5d41d045551c177e2882d4ac85728a6" integrity sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w== @@ -8353,24 +8708,22 @@ csso@^4.0.2: dependencies: css-tree "^1.1.2" -cssom@0.3.x, "cssom@>= 0.3.2 < 0.4.0", cssom@~0.3.6: - version "0.3.8" - resolved "https://registry.yarnpkg.com/cssom/-/cssom-0.3.8.tgz#9f1276f5b2b463f2114d3f2c75250af8c1a36f4a" - integrity sha512-b0tGHbfegbhPJpxpiBPU2sCkigAqtM9O121le6bbOlgyV+NyGyCmVfJ6QW9eRjz8CpNfWEOYBIMIGRYkLwsIYg== - -cssom@^0.4.1, cssom@^0.4.4: +cssom@^0.4.4: version "0.4.4" resolved "https://registry.yarnpkg.com/cssom/-/cssom-0.4.4.tgz#5a66cf93d2d0b661d80bf6a44fb65f5c2e4e0a10" integrity sha512-p3pvU7r1MyyqbTk+WbNJIgJjG2VmTIaB10rI93LzVPrmDJKkzKYMtxxyAvQXR/NS6otuzveI7+7BBq3SjBS2mw== -cssstyle@^1.0.0: - version "1.4.0" - resolved "https://registry.yarnpkg.com/cssstyle/-/cssstyle-1.4.0.tgz#9d31328229d3c565c61e586b02041a28fccdccf1" - integrity sha512-GBrLZYZ4X4x6/QEoBnIrqb8B/f5l4+8me2dkom/j1Gtbxy0kBv6OGzKuAsGM75bkGwGAFkt56Iwg28S3XTZgSA== - dependencies: - cssom "0.3.x" +cssom@^0.5.0: + version "0.5.0" + resolved "https://registry.yarnpkg.com/cssom/-/cssom-0.5.0.tgz#d254fa92cd8b6fbd83811b9fbaed34663cc17c36" + integrity sha512-iKuQcq+NdHqlAcwUY0o/HL69XQrUaQdMjmStJ8JFmUaiiQErlhrmuigkg/CU4E2J0IyUKUrMAgl36TvN67MqTw== + +cssom@~0.3.6: + version "0.3.8" + resolved "https://registry.yarnpkg.com/cssom/-/cssom-0.3.8.tgz#9f1276f5b2b463f2114d3f2c75250af8c1a36f4a" + integrity sha512-b0tGHbfegbhPJpxpiBPU2sCkigAqtM9O121le6bbOlgyV+NyGyCmVfJ6QW9eRjz8CpNfWEOYBIMIGRYkLwsIYg== -cssstyle@^2.0.0, cssstyle@^2.3.0: +cssstyle@^2.3.0: version "2.3.0" resolved "https://registry.yarnpkg.com/cssstyle/-/cssstyle-2.3.0.tgz#ff665a0ddbdc31864b09647f34163443d90b0852" integrity sha512-AZL67abkUzIuvcHqk7c09cezpGNcxUxU4Ioi/05xHk4DQeTkWmGYftIE6ctU6AEt+Gn4n1lDStOtj7FKycP71A== @@ -8431,15 +8784,6 @@ data-uri-to-buffer@3.0.1: resolved "https://registry.yarnpkg.com/data-uri-to-buffer/-/data-uri-to-buffer-3.0.1.tgz#594b8973938c5bc2c33046535785341abc4f3636" integrity sha512-WboRycPNsVw3B3TL559F7kuBUM4d8CgMEvk6xEJlOp7OBPjt6G7z8WMWlD2rOFZLk6OYfFIUGsCOWzcQH9K2og== -data-urls@^1.0.0, data-urls@^1.1.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/data-urls/-/data-urls-1.1.0.tgz#15ee0582baa5e22bb59c77140da8f9c76963bbfe" - integrity sha512-YTWYI9se1P55u58gL5GkQHW4P6VJBJ5iBT+B5a7i2Tjadhv52paJG0qHX4A0OR6/t52odI64KP2YvFpkDOi3eQ== - dependencies: - abab "^2.0.0" - whatwg-mimetype "^2.2.0" - whatwg-url "^7.0.0" - data-urls@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/data-urls/-/data-urls-2.0.0.tgz#156485a72963a970f5d5821aaf642bef2bf2db9b" @@ -8449,6 +8793,15 @@ data-urls@^2.0.0: whatwg-mimetype "^2.3.0" whatwg-url "^8.0.0" +data-urls@^3.0.1: + version "3.0.1" + resolved "https://registry.yarnpkg.com/data-urls/-/data-urls-3.0.1.tgz#597fc2ae30f8bc4dbcf731fcd1b1954353afc6f8" + integrity sha512-Ds554NeT5Gennfoo9KN50Vh6tpgtvYEwraYjejXnyTpu1C7oXKxdFk75REooENHE8ndTVOJuv+BEs4/J/xcozw== + dependencies: + abab "^2.0.3" + whatwg-mimetype "^3.0.0" + whatwg-url "^10.0.0" + date-and-time@^0.14.2: version "0.14.2" resolved "https://registry.yarnpkg.com/date-and-time/-/date-and-time-0.14.2.tgz#a4266c3dead460f6c231fe9674e585908dac354e" @@ -8561,6 +8914,11 @@ decimal.js@^10.2.1: resolved "https://registry.yarnpkg.com/decimal.js/-/decimal.js-10.2.1.tgz#238ae7b0f0c793d3e3cea410108b35a2c01426a3" integrity sha512-KaL7+6Fw6i5A2XSnsbhm/6B+NuEA7TZ4vqxnd5tXz9sbKtrN9Srj8ab4vKVdK8YAqZO9P1kg45Y6YLoduPf+kw== +decimal.js@^10.3.1: + version "10.3.1" + resolved "https://registry.yarnpkg.com/decimal.js/-/decimal.js-10.3.1.tgz#d8c3a444a9c6774ba60ca6ad7261c3a94fd5e783" + integrity sha512-V0pfhfr8suzyPGOx3nmq4aHqabehUZn6Ch9kyFpV79TGDTWFmHqUqXdabR7QHqxzrYolF4+tVmJhUG4OURg5dQ== + decode-uri-component@^0.2.0: version "0.2.0" resolved "https://registry.yarnpkg.com/decode-uri-component/-/decode-uri-component-0.2.0.tgz#eb3913333458775cb84cd1a1fae062106bb87545" @@ -8724,16 +9082,11 @@ detect-indent@^6.0.0: resolved "https://registry.yarnpkg.com/detect-indent/-/detect-indent-6.0.0.tgz#0abd0f549f69fc6659a254fe96786186b6f528fd" integrity sha512-oSyFlqaTHCItVRGK5RmrmjB+CmaMOW7IaNA/kdxqhoa6d17j/5ce9O9eWXmV/KEdRwqpQA+Vqe8a8Bsybu4YnA== -detect-newline@3.1.0: +detect-newline@3.1.0, detect-newline@^3.0.0: version "3.1.0" resolved "https://registry.yarnpkg.com/detect-newline/-/detect-newline-3.1.0.tgz#576f5dfc63ae1a192ff192d8ad3af6308991b651" integrity sha512-TLz+x/vEXm/Y7P7wn1EJFNLxYpUD4TgMosxY6fAVJUnJMbupHBOncxyWUG9OpTaH9EBD7uFI5LfEgmMOc54DsA== -detect-newline@^2.1.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/detect-newline/-/detect-newline-2.1.0.tgz#f41f1c10be4b00e87b5f13da680759f2c5bfd3e2" - integrity sha1-9B8cEL5LAOh7XxPaaAdZ8sW/0+I= - detective-amd@^3.0.1: version "3.1.0" resolved "https://registry.yarnpkg.com/detective-amd/-/detective-amd-3.1.0.tgz#92daee3214a0ca4522646cf333cac90a3fca6373" @@ -8849,6 +9202,11 @@ diff-sequences@^27.4.0: resolved "https://registry.yarnpkg.com/diff-sequences/-/diff-sequences-27.4.0.tgz#d783920ad8d06ec718a060d00196dfef25b132a5" integrity sha512-YqiQzkrsmHMH5uuh8OdQFU9/ZpADnwzml8z0O5HvRNda+5UZsaX/xN+AAxfR2hWq1Y7HZnAzO9J5lJXOuDz2Ww== +diff-sequences@^27.5.1: + version "27.5.1" + resolved "https://registry.yarnpkg.com/diff-sequences/-/diff-sequences-27.5.1.tgz#eaecc0d327fd68c8d9672a1e64ab8dccb2ef5327" + integrity sha512-k1gCAXAsNgLwEL+Y8Wvl+M6oEFj5bgazfZULpS5CneoPPXRaCCW7dm+q21Ky2VEE5X+VeRDBVg1Pcvvsr4TtNQ== + diff@3.5.0, diff@^3.5.0: version "3.5.0" resolved "https://registry.yarnpkg.com/diff/-/diff-3.5.0.tgz#800c0dd1e0a8bfbc95835c202ad220fe317e5a12" @@ -8969,13 +9327,6 @@ domelementtype@^2.2.0: resolved "https://registry.yarnpkg.com/domelementtype/-/domelementtype-2.2.0.tgz#9a0b6c2782ed6a1c7323d42267183df9bd8b1d57" integrity sha512-DtBMo82pv1dFtUmHyr48beiuq792Sxohr+8Hm9zoxklYPfa6n0Z3Byjj2IV7bmr2IyqClnqEQhfgHJJ5QF0R5A== -domexception@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/domexception/-/domexception-1.0.1.tgz#937442644ca6a31261ef36e3ec677fe805582c90" - integrity sha512-raigMkn7CJNNo6Ihro1fzG7wr3fHuYVytzquZKX5n0yizGsTcYgzdIUwj1X9pK0VvjeihV+XiclP+DjwbsSKug== - dependencies: - webidl-conversions "^4.0.2" - domexception@^2.0.1: version "2.0.1" resolved "https://registry.yarnpkg.com/domexception/-/domexception-2.0.1.tgz#fb44aefba793e1574b0af6aed2801d057529f304" @@ -8983,6 +9334,13 @@ domexception@^2.0.1: dependencies: webidl-conversions "^5.0.0" +domexception@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/domexception/-/domexception-4.0.0.tgz#4ad1be56ccadc86fc76d033353999a8037d03673" + integrity sha512-A2is4PLG+eeSfoTMA95/s4pvAoSo2mKtiM5jlHkAVewmiO8ISFTFKZjH7UAM1Atli/OT/7JHOrJRJiMKUZKYBw== + dependencies: + webidl-conversions "^7.0.0" + domhandler@^4.0.0, domhandler@^4.2.0: version "4.3.0" resolved "https://registry.yarnpkg.com/domhandler/-/domhandler-4.3.0.tgz#16c658c626cf966967e306f966b431f77d4a5626" @@ -9823,6 +10181,11 @@ emit-function@0.0.2: resolved "https://registry.yarnpkg.com/emit-function/-/emit-function-0.0.2.tgz#e3a50b3d61be1bf8ca88b924bf713157a5bec124" integrity sha1-46ULPWG+G/jKiLkkv3ExV6W+wSQ= +emittery@^0.8.1: + version "0.8.1" + resolved "https://registry.yarnpkg.com/emittery/-/emittery-0.8.1.tgz#bb23cc86d03b30aa75a7f734819dee2e1ba70860" + integrity sha512-uDfvUjVrfGJJhymx/kz6prltenw1u7WrCg1oa94zYY8xxVpLLUu045LAT0dhDZdXG58/EpPL/5kA180fQ/qudg== + emoji-regex@^7.0.1: version "7.0.3" resolved "https://registry.yarnpkg.com/emoji-regex/-/emoji-regex-7.0.3.tgz#933a04052860c85e83c122479c4748a8e4c72156" @@ -10117,18 +10480,6 @@ escodegen@1.8.x: optionalDependencies: source-map "~0.2.0" -escodegen@^1.11.1, escodegen@^1.9.1: - version "1.14.3" - resolved "https://registry.yarnpkg.com/escodegen/-/escodegen-1.14.3.tgz#4e7b81fba61581dc97582ed78cab7f0e8d63f503" - integrity sha512-qFcX0XJkdg+PB3xjZZG/wKSuT1PnQWx57+TVSjIMmILd2yC/6ByYElPwJnslDsuWuSAp4AwJGumarAAmJch5Kw== - dependencies: - esprima "^4.0.1" - estraverse "^4.2.0" - esutils "^2.0.2" - optionator "^0.8.1" - optionalDependencies: - source-map "~0.6.1" - escodegen@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/escodegen/-/escodegen-2.0.0.tgz#5e32b12833e8aa8fa35e1bf0befa89380484c7dd" @@ -10390,7 +10741,7 @@ estraverse@^1.9.1: resolved "https://registry.yarnpkg.com/estraverse/-/estraverse-1.9.3.tgz#af67f2dc922582415950926091a4005d29c9bb44" integrity sha1-r2fy3JIlgkFZUJJgkaQAXSnJu0Q= -estraverse@^4.1.1, estraverse@^4.2.0: +estraverse@^4.1.1: version "4.3.0" resolved "https://registry.yarnpkg.com/estraverse/-/estraverse-4.3.0.tgz#398ad3f3c5a24948be7725e83d11a7de28cdbd1d" integrity sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw== @@ -10514,6 +10865,21 @@ execa@^4.0.0, execa@^4.1.0: signal-exit "^3.0.2" strip-final-newline "^2.0.0" +execa@^5.0.0: + version "5.1.1" + resolved "https://registry.yarnpkg.com/execa/-/execa-5.1.1.tgz#f80ad9cbf4298f7bd1d4c9555c21e93741c411dd" + integrity sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg== + dependencies: + cross-spawn "^7.0.3" + get-stream "^6.0.0" + human-signals "^2.1.0" + is-stream "^2.0.0" + merge-stream "^2.0.0" + npm-run-path "^4.0.1" + onetime "^5.1.2" + signal-exit "^3.0.3" + strip-final-newline "^2.0.0" + exists-sync@0.0.4: version "0.0.4" resolved "https://registry.yarnpkg.com/exists-sync/-/exists-sync-0.0.4.tgz#9744c2c428cc03b01060db454d4b12f0ef3c8879" @@ -10573,17 +10939,15 @@ expect@=27.2.5: jest-message-util "^27.2.5" jest-regex-util "^27.0.6" -expect@^24.9.0: - version "24.9.0" - resolved "https://registry.yarnpkg.com/expect/-/expect-24.9.0.tgz#b75165b4817074fa4a157794f46fe9f1ba15b6ca" - integrity sha512-wvVAx8XIol3Z5m9zvZXiyZOQ+sRJqNTIm6sGjdWlaZIeupQGO3WbYI+15D/AmEwZywL6wtJkbAbJtzkOfBuR0Q== +expect@^27.5.1: + version "27.5.1" + resolved "https://registry.yarnpkg.com/expect/-/expect-27.5.1.tgz#83ce59f1e5bdf5f9d2b94b61d2050db48f3fef74" + integrity sha512-E1q5hSUG2AmYQwQJ041nvgpkODHQvB+RKlB4IYdru6uJsyFTRyZAP463M+1lINorwbqAmUggi6+WwkD8lCS/Dw== dependencies: - "@jest/types" "^24.9.0" - ansi-styles "^3.2.0" - jest-get-type "^24.9.0" - jest-matcher-utils "^24.9.0" - jest-message-util "^24.9.0" - jest-regex-util "^24.9.0" + "@jest/types" "^27.5.1" + jest-get-type "^27.5.1" + jest-matcher-utils "^27.5.1" + jest-message-util "^27.5.1" express@^4.10.7, express@^4.16.4, express@^4.17.1: version "4.17.1" @@ -11190,6 +11554,24 @@ forever-agent@~0.6.1: resolved "https://registry.yarnpkg.com/forever-agent/-/forever-agent-0.6.1.tgz#fbc71f0c41adeb37f96c577ad1ed42d8fdacca91" integrity sha1-+8cfDEGt6zf5bFd60e1C2P2sypE= +form-data@^3.0.0: + version "3.0.1" + resolved "https://registry.yarnpkg.com/form-data/-/form-data-3.0.1.tgz#ebd53791b78356a99af9a300d4282c4d5eb9755f" + integrity sha512-RHkBKtLWUVwd7SqRIvCZMEvAMoGUp0XU+seQiZejj0COz3RI3hWP4sCv3gZWWLjJTd7rGwcsF5eKZGii0r/hbg== + dependencies: + asynckit "^0.4.0" + combined-stream "^1.0.8" + mime-types "^2.1.12" + +form-data@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/form-data/-/form-data-4.0.0.tgz#93919daeaf361ee529584b9b31664dc12c9fa452" + integrity sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww== + dependencies: + asynckit "^0.4.0" + combined-stream "^1.0.8" + mime-types "^2.1.12" + form-data@~2.3.2: version "2.3.3" resolved "https://registry.yarnpkg.com/form-data/-/form-data-2.3.3.tgz#dcce52c05f644f298c6a7ab936bd724ceffbf3a6" @@ -11398,7 +11780,7 @@ fsevents@^1.2.7: bindings "^1.5.0" nan "^2.12.1" -fsevents@~2.3.1, fsevents@~2.3.2: +fsevents@^2.3.2, fsevents@~2.3.1, fsevents@~2.3.2: version "2.3.2" resolved "https://registry.yarnpkg.com/fsevents/-/fsevents-2.3.2.tgz#8a526f78b8fdf4623b709e0b975c52c24c02fd1a" integrity sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA== @@ -11513,6 +11895,11 @@ get-own-enumerable-property-symbols@^3.0.0: resolved "https://registry.yarnpkg.com/get-own-enumerable-property-symbols/-/get-own-enumerable-property-symbols-3.0.2.tgz#b5fde77f22cbe35f390b4e089922c50bce6ef664" integrity sha512-I0UBV/XOz1XkIJHEUDMZAbzCThU/H8DxmSfmdGcKPnVhu2VfFqr34jr9777IyaTYvxjedWhqVIilEDsCdP5G6g== +get-package-type@^0.1.0: + version "0.1.0" + resolved "https://registry.yarnpkg.com/get-package-type/-/get-package-type-0.1.0.tgz#8de2d803cff44df3bc6c456e6668b36c3926e11a" + integrity sha512-pjzuKtY64GYfWizNAJ0fr9VqttZkNiK2iS430LtIHzjBEr6bX8Am2zm4sW4Ro5wjWW5cAlRL1qAMTcXbjNAO2Q== + get-pkg-repo@^1.0.0: version "1.4.0" resolved "https://registry.yarnpkg.com/get-pkg-repo/-/get-pkg-repo-1.4.0.tgz#c73b489c06d80cc5536c2c853f9e05232056972d" @@ -11967,6 +12354,11 @@ graceful-fs@^4.1.11, graceful-fs@^4.1.15, graceful-fs@^4.1.2, graceful-fs@^4.1.3 resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.9.tgz#041b05df45755e587a24942279b9d113146e1c96" integrity sha512-NtNxqUcXgpW2iMrfqSfR73Glt39K+BLwWsPs94yR63v45T0Wbej7eRmL5cWfwEgqXnmjQp3zaJTshdRW/qC2ZQ== +graceful-fs@^4.2.9: + version "4.2.10" + resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.10.tgz#147d3a006da4ca3ce14728c7aefc287c367d7a6c" + integrity sha512-9ByhssR2fPVsNZj478qUUbKfmL0+t5BDVyjShtyZZLiK7ZDAArFFfopyOTj0M05wE2tJPisA4iTnnXl2YoPvOA== + "graceful-readlink@>= 1.0.0": version "1.0.1" resolved "https://registry.yarnpkg.com/graceful-readlink/-/graceful-readlink-1.0.1.tgz#4cafad76bc62f02fa039b2f94e9a3dd3a391a725" @@ -12346,13 +12738,6 @@ html-comment-regex@^1.1.0: resolved "https://registry.yarnpkg.com/html-comment-regex/-/html-comment-regex-1.1.2.tgz#97d4688aeb5c81886a364faa0cad1dda14d433a7" integrity sha512-P+M65QY2JQ5Y0G9KKdlDpo0zK+/OHptU5AaBwUfAIDJZk1MYf32Frm84EcOytfJE0t5JvkAnKlmjsXDnWzCJmQ== -html-encoding-sniffer@^1.0.2: - version "1.0.2" - resolved "https://registry.yarnpkg.com/html-encoding-sniffer/-/html-encoding-sniffer-1.0.2.tgz#e70d84b94da53aa375e11fe3a351be6642ca46f8" - integrity sha512-71lZziiDnsuabfdYiUeWdCVyKuqwWi23L8YeIgV9jSSZHCtb6wB1BKWooH7L3tn4/FuZJMVWyNaIDr4RGmaSYw== - dependencies: - whatwg-encoding "^1.0.1" - html-encoding-sniffer@^2.0.1: version "2.0.1" resolved "https://registry.yarnpkg.com/html-encoding-sniffer/-/html-encoding-sniffer-2.0.1.tgz#42a6dc4fd33f00281176e8b23759ca4e4fa185f3" @@ -12360,6 +12745,13 @@ html-encoding-sniffer@^2.0.1: dependencies: whatwg-encoding "^1.0.5" +html-encoding-sniffer@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/html-encoding-sniffer/-/html-encoding-sniffer-3.0.0.tgz#2cb1a8cf0db52414776e5b2a7a04d5dd98158de9" + integrity sha512-oWv4T4yJ52iKrufjnyZPkrN0CH3QnrUqdB6In1g5Fe1mia8GmF36gnfNySxoZtxD5+NmYw1EElVXiBk93UeskA== + dependencies: + whatwg-encoding "^2.0.0" + html-escaper@^2.0.0: version "2.0.2" resolved "https://registry.yarnpkg.com/html-escaper/-/html-escaper-2.0.2.tgz#dfd60027da36a36dfcbe236262c00a5822681453" @@ -12460,7 +12852,7 @@ http-proxy-agent@^2.1.0: agent-base "4" debug "3.1.0" -http-proxy-agent@^4.0.0: +http-proxy-agent@^4.0.0, http-proxy-agent@^4.0.1: version "4.0.1" resolved "https://registry.yarnpkg.com/http-proxy-agent/-/http-proxy-agent-4.0.1.tgz#8a8c8ef7f5932ccf953c296ca8291b95aa74aa3a" integrity sha512-k0zdNgqWTGA6aeIRVpvfVob4fL52dTfaehylg0Y4UvSySvOq/Y+BOyPrgpUrA7HylqvU8vIZGsRuXmspskV0Tg== @@ -12469,6 +12861,15 @@ http-proxy-agent@^4.0.0: agent-base "6" debug "4" +http-proxy-agent@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/http-proxy-agent/-/http-proxy-agent-5.0.0.tgz#5129800203520d434f142bc78ff3c170800f2b43" + integrity sha512-n2hY8YdoRE1i7r6M0w9DIw5GgZN0G25P8zLCRQ8rjXtTU3vsNFBI/vWK/UIeE6g5MUUz6avwAPXmL6Fy9D/90w== + dependencies: + "@tootallnate/once" "2" + agent-base "6" + debug "4" + http-proxy@^1.13.1, http-proxy@^1.18.1: version "1.18.1" resolved "https://registry.yarnpkg.com/http-proxy/-/http-proxy-1.18.1.tgz#401541f0534884bbf95260334e72f88ee3976549" @@ -12526,6 +12927,11 @@ human-signals@^1.1.1: resolved "https://registry.yarnpkg.com/human-signals/-/human-signals-1.1.1.tgz#c5b1cd14f50aeae09ab6c59fe63ba3395fe4dfa3" integrity sha512-SEQu7vl8KjNL2eoGBLF3+wAjpsNfA9XMlXAYj/3EdaNfAlxKthD1xjEQfGOUhllCGGJVNY34bRr6lPINhNjyZw== +human-signals@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/human-signals/-/human-signals-2.1.0.tgz#dc91fcba42e4d06e4abaed33b3e7a3c02f514ea0" + integrity sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw== + humanize-ms@^1.2.1: version "1.2.1" resolved "https://registry.yarnpkg.com/humanize-ms/-/humanize-ms-1.2.1.tgz#c46e3159a293f6b896da29316d8b6fe8bb79bbed" @@ -12540,6 +12946,13 @@ iconv-lite@0.4.24, iconv-lite@^0.4.24: dependencies: safer-buffer ">= 2.1.2 < 3" +iconv-lite@0.6.3: + version "0.6.3" + resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.6.3.tgz#a52f80bf38da1952eb5c681790719871a1a72501" + integrity sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw== + dependencies: + safer-buffer ">= 2.1.2 < 3.0.0" + iconv-lite@^0.6.2: version "0.6.2" resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.6.2.tgz#ce13d1875b0c3a674bd6a04b7f76b01b1b6ded01" @@ -12618,13 +13031,13 @@ import-local@^1.0.0: pkg-dir "^2.0.0" resolve-cwd "^2.0.0" -import-local@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/import-local/-/import-local-2.0.0.tgz#55070be38a5993cf18ef6db7e961f5bee5c5a09d" - integrity sha512-b6s04m3O+s3CGSbqDIyP4R6aAwAeYlVq9+WUWep6iHa8ETRf9yei1U48C5MmfJmV9AiLYYBKPMq/W+/WRpQmCQ== +import-local@^3.0.2: + version "3.1.0" + resolved "https://registry.yarnpkg.com/import-local/-/import-local-3.1.0.tgz#b4479df8a5fd44f6cdce24070675676063c95cb4" + integrity sha512-ASB07uLtnDs1o6EHjKpX34BKYDSqnFerfTOJL2HvMqF70LnxpjkzDB8J44oT9pu4AMPkQwf8jl6szgvNd2tRIg== dependencies: - pkg-dir "^3.0.0" - resolve-cwd "^2.0.0" + pkg-dir "^4.2.0" + resolve-cwd "^3.0.0" imurmurhash@^0.1.4: version "0.1.4" @@ -12805,11 +13218,6 @@ invert-kv@^2.0.0: resolved "https://registry.yarnpkg.com/invert-kv/-/invert-kv-2.0.0.tgz#7393f5afa59ec9ff5f67a27620d11c226e3eec02" integrity sha512-wPVv/y/QQ/Uiirj/vh3oP+1Ww+AWehmi1g5fFWGPF6IpCBCDVrhgHRMvrLfdYcwDh3QJbGXDW4JAuzxElLSqKA== -ip-regex@^2.1.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/ip-regex/-/ip-regex-2.1.0.tgz#fa78bf5d2e6913c911ce9f819ee5146bb6d844e9" - integrity sha1-+ni/XS5pE8kRzp+BnuUUa7bYROk= - ip@1.1.5, ip@^1.1.5: version "1.1.5" resolved "https://registry.yarnpkg.com/ip/-/ip-1.1.5.tgz#bdded70114290828c0a039e72ef25f5aaec4354a" @@ -13144,10 +13552,10 @@ is-plain-object@^5.0.0: resolved "https://registry.yarnpkg.com/is-plain-object/-/is-plain-object-5.0.0.tgz#4427f50ab3429e9025ea7d52e9043a9ef4159344" integrity sha512-VRSzKkbMm5jMDoKLbltAkFQ5Qr7VDiTFGXxYFXXowVj387GeGNOCsOH6Msy00SGZ3Fp84b1Naa1psqgcCIEP5Q== -is-potential-custom-element-name@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/is-potential-custom-element-name/-/is-potential-custom-element-name-1.0.0.tgz#0c52e54bcca391bb2c494b21e8626d7336c6e397" - integrity sha1-DFLlS8yjkbssSUsh6GJtczbG45c= +is-potential-custom-element-name@^1.0.1: + version "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-redirect@^1.0.0: version "1.0.0" @@ -13389,12 +13797,17 @@ isstream@~0.1.2: resolved "https://registry.yarnpkg.com/isstream/-/isstream-0.1.2.tgz#47e63f7af55afa6f92e1500e690eb8b8529c099a" integrity sha1-R+Y/evVa+m+S4VAOaQ64uFKcCZo= -istanbul-lib-coverage@^2.0.2, istanbul-lib-coverage@^2.0.5: +istanbul-lib-coverage@^2.0.5: version "2.0.5" resolved "https://registry.yarnpkg.com/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.5.tgz#675f0ab69503fad4b1d849f736baaca803344f49" integrity sha512-8aXznuEPCJvGnMSRft4udDRDtb1V3pkQkMMI5LI+6HuQz5oQ4J2UFn1H82raA3qJtyOLkkwVqICBQkjnGtn5mA== -istanbul-lib-instrument@^3.0.1, istanbul-lib-instrument@^3.3.0: +istanbul-lib-coverage@^3.0.0, istanbul-lib-coverage@^3.2.0: + version "3.2.0" + resolved "https://registry.yarnpkg.com/istanbul-lib-coverage/-/istanbul-lib-coverage-3.2.0.tgz#189e7909d0a39fa5a3dfad5b03f71947770191d3" + integrity sha512-eOeJ5BHCmHYvQK7xt9GkdHuzuCGS1Y6g9Gvnx3Ym33fz/HpLRYxiS0wHNr+m/MBC8B647Xt608vCDEvhl9c6Mw== + +istanbul-lib-instrument@^3.3.0: version "3.3.0" resolved "https://registry.yarnpkg.com/istanbul-lib-instrument/-/istanbul-lib-instrument-3.3.0.tgz#a5f63d91f0bbc0c3e479ef4c5de027335ec6d630" integrity sha512-5nnIN4vo5xQZHdXno/YDXJ0G+I3dAm4XgzfSVTPLQpj/zAV2dV6Juy0yaf10/zrJOJeHoN3fraFe+XRq2bFVZA== @@ -13407,32 +13820,42 @@ istanbul-lib-instrument@^3.0.1, istanbul-lib-instrument@^3.3.0: istanbul-lib-coverage "^2.0.5" semver "^6.0.0" -istanbul-lib-report@^2.0.4: - version "2.0.8" - resolved "https://registry.yarnpkg.com/istanbul-lib-report/-/istanbul-lib-report-2.0.8.tgz#5a8113cd746d43c4889eba36ab10e7d50c9b4f33" - integrity sha512-fHBeG573EIihhAblwgxrSenp0Dby6tJMFR/HvlerBsrCTD5bkUuoNtn3gVh29ZCS824cGGBPn7Sg7cNk+2xUsQ== +istanbul-lib-instrument@^5.0.4, istanbul-lib-instrument@^5.1.0: + version "5.1.0" + resolved "https://registry.yarnpkg.com/istanbul-lib-instrument/-/istanbul-lib-instrument-5.1.0.tgz#7b49198b657b27a730b8e9cb601f1e1bff24c59a" + integrity sha512-czwUz525rkOFDJxfKK6mYfIs9zBKILyrZQxjz3ABhjQXhbhFsSbo1HW/BFcsDnfJYJWA6thRR5/TUY2qs5W99Q== dependencies: - istanbul-lib-coverage "^2.0.5" - make-dir "^2.1.0" - supports-color "^6.1.0" + "@babel/core" "^7.12.3" + "@babel/parser" "^7.14.7" + "@istanbuljs/schema" "^0.1.2" + istanbul-lib-coverage "^3.2.0" + semver "^6.3.0" -istanbul-lib-source-maps@^3.0.1: - version "3.0.6" - resolved "https://registry.yarnpkg.com/istanbul-lib-source-maps/-/istanbul-lib-source-maps-3.0.6.tgz#284997c48211752ec486253da97e3879defba8c8" - integrity sha512-R47KzMtDJH6X4/YW9XTx+jrLnZnscW4VpNN+1PViSYTejLVPWv7oov+Duf8YQSPyVRUvueQqz1TcsC6mooZTXw== +istanbul-lib-report@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/istanbul-lib-report/-/istanbul-lib-report-3.0.0.tgz#7518fe52ea44de372f460a76b5ecda9ffb73d8a6" + integrity sha512-wcdi+uAKzfiGT2abPpKZ0hSU1rGQjUQnLvtY5MpQ7QCTahD3VODhcu4wcfY1YtkGaDD5yuydOLINXsfbus9ROw== + dependencies: + istanbul-lib-coverage "^3.0.0" + make-dir "^3.0.0" + supports-color "^7.1.0" + +istanbul-lib-source-maps@^4.0.0: + version "4.0.1" + resolved "https://registry.yarnpkg.com/istanbul-lib-source-maps/-/istanbul-lib-source-maps-4.0.1.tgz#895f3a709fcfba34c6de5a42939022f3e4358551" + integrity sha512-n3s8EwkdFIJCG3BPKBYvskgXGoy88ARzvegkitk60NxRdwltLOTaH7CUiMRXvwYorl0Q712iEjcWB+fK/MrWVw== dependencies: debug "^4.1.1" - istanbul-lib-coverage "^2.0.5" - make-dir "^2.1.0" - rimraf "^2.6.3" + istanbul-lib-coverage "^3.0.0" source-map "^0.6.1" -istanbul-reports@^2.2.6: - version "2.2.7" - resolved "https://registry.yarnpkg.com/istanbul-reports/-/istanbul-reports-2.2.7.tgz#5d939f6237d7b48393cc0959eab40cd4fd056931" - integrity sha512-uu1F/L1o5Y6LzPVSVZXNOoD/KXpJue9aeLRd0sM9uMXfZvzomB0WxVamWb5ue8kA2vVWEmW7EG+A5n3f1kqHKg== +istanbul-reports@^3.1.3: + version "3.1.4" + resolved "https://registry.yarnpkg.com/istanbul-reports/-/istanbul-reports-3.1.4.tgz#1b6f068ecbc6c331040aab5741991273e609e40c" + integrity sha512-r1/DshN4KSE7xWEknZLLLLDn5CJybV3nw01VTkp6D5jzLuELlcbudfj/eSQFvrKsJuTVCGnePO7ho82Nw9zzfw== dependencies: html-escaper "^2.0.0" + istanbul-lib-report "^3.0.0" istanbul@0.4.5, istanbul@^0.4.0: version "0.4.5" @@ -13480,56 +13903,87 @@ isurl@^1.0.0-alpha5: has-to-string-tag-x "^1.2.0" is-object "^1.0.1" -jest-changed-files@^24.9.0: - version "24.9.0" - resolved "https://registry.yarnpkg.com/jest-changed-files/-/jest-changed-files-24.9.0.tgz#08d8c15eb79a7fa3fc98269bc14b451ee82f8039" - integrity sha512-6aTWpe2mHF0DhL28WjdkO8LyGjs3zItPET4bMSeXU6T3ub4FPMw+mcOcbdGXQOAfmLcxofD23/5Bl9Z4AkFwqg== +jest-changed-files@^27.5.1: + version "27.5.1" + resolved "https://registry.yarnpkg.com/jest-changed-files/-/jest-changed-files-27.5.1.tgz#a348aed00ec9bf671cc58a66fcbe7c3dfd6a68f5" + integrity sha512-buBLMiByfWGCoMsLLzGUUSpAmIAGnbR2KJoMN10ziLhOLvP4e0SlypHnAel8iqQXTrcbmfEY9sSqae5sgUsTvw== dependencies: - "@jest/types" "^24.9.0" - execa "^1.0.0" - throat "^4.0.0" + "@jest/types" "^27.5.1" + execa "^5.0.0" + throat "^6.0.1" -jest-cli@^24.9.0: - version "24.9.0" - resolved "https://registry.yarnpkg.com/jest-cli/-/jest-cli-24.9.0.tgz#ad2de62d07472d419c6abc301fc432b98b10d2af" - integrity sha512-+VLRKyitT3BWoMeSUIHRxV/2g8y9gw91Jh5z2UmXZzkZKpbC08CSehVxgHUwTpy+HwGcns/tqafQDJW7imYvGg== +jest-circus@^27.5.1: + version "27.5.1" + resolved "https://registry.yarnpkg.com/jest-circus/-/jest-circus-27.5.1.tgz#37a5a4459b7bf4406e53d637b49d22c65d125ecc" + integrity sha512-D95R7x5UtlMA5iBYsOHFFbMD/GVA4R/Kdq15f7xYWUfWHBto9NYRsOvnSauTgdF+ogCpJ4tyKOXhUifxS65gdw== dependencies: - "@jest/core" "^24.9.0" - "@jest/test-result" "^24.9.0" - "@jest/types" "^24.9.0" - chalk "^2.0.1" + "@jest/environment" "^27.5.1" + "@jest/test-result" "^27.5.1" + "@jest/types" "^27.5.1" + "@types/node" "*" + chalk "^4.0.0" + co "^4.6.0" + dedent "^0.7.0" + expect "^27.5.1" + is-generator-fn "^2.0.0" + jest-each "^27.5.1" + jest-matcher-utils "^27.5.1" + jest-message-util "^27.5.1" + jest-runtime "^27.5.1" + jest-snapshot "^27.5.1" + jest-util "^27.5.1" + pretty-format "^27.5.1" + slash "^3.0.0" + stack-utils "^2.0.3" + throat "^6.0.1" + +jest-cli@^27.5.1: + version "27.5.1" + resolved "https://registry.yarnpkg.com/jest-cli/-/jest-cli-27.5.1.tgz#278794a6e6458ea8029547e6c6cbf673bd30b145" + integrity sha512-Hc6HOOwYq4/74/c62dEE3r5elx8wjYqxY0r0G/nFrLDPMFRu6RA/u8qINOIkvhxG7mMQ5EJsOGfRpI8L6eFUVw== + dependencies: + "@jest/core" "^27.5.1" + "@jest/test-result" "^27.5.1" + "@jest/types" "^27.5.1" + chalk "^4.0.0" exit "^0.1.2" - import-local "^2.0.0" - is-ci "^2.0.0" - jest-config "^24.9.0" - jest-util "^24.9.0" - jest-validate "^24.9.0" + graceful-fs "^4.2.9" + import-local "^3.0.2" + jest-config "^27.5.1" + jest-util "^27.5.1" + jest-validate "^27.5.1" prompts "^2.0.1" - realpath-native "^1.1.0" - yargs "^13.3.0" + yargs "^16.2.0" -jest-config@^24.9.0: - version "24.9.0" - resolved "https://registry.yarnpkg.com/jest-config/-/jest-config-24.9.0.tgz#fb1bbc60c73a46af03590719efa4825e6e4dd1b5" - integrity sha512-RATtQJtVYQrp7fvWg6f5y3pEFj9I+H8sWw4aKxnDZ96mob5i5SD6ZEGWgMLXQ4LE8UurrjbdlLWdUeo+28QpfQ== +jest-config@^27.5.1: + version "27.5.1" + resolved "https://registry.yarnpkg.com/jest-config/-/jest-config-27.5.1.tgz#5c387de33dca3f99ad6357ddeccd91bf3a0e4a41" + integrity sha512-5sAsjm6tGdsVbW9ahcChPAFCk4IlkQUknH5AvKjuLTSlcO/wCZKyFdn7Rg0EkC+OGgWODEy2hDpWB1PgzH0JNA== dependencies: - "@babel/core" "^7.1.0" - "@jest/test-sequencer" "^24.9.0" - "@jest/types" "^24.9.0" - babel-jest "^24.9.0" - chalk "^2.0.1" + "@babel/core" "^7.8.0" + "@jest/test-sequencer" "^27.5.1" + "@jest/types" "^27.5.1" + babel-jest "^27.5.1" + chalk "^4.0.0" + ci-info "^3.2.0" + deepmerge "^4.2.2" glob "^7.1.1" - jest-environment-jsdom "^24.9.0" - jest-environment-node "^24.9.0" - jest-get-type "^24.9.0" - jest-jasmine2 "^24.9.0" - jest-regex-util "^24.3.0" - jest-resolve "^24.9.0" - jest-util "^24.9.0" - jest-validate "^24.9.0" - micromatch "^3.1.10" - pretty-format "^24.9.0" - realpath-native "^1.1.0" + graceful-fs "^4.2.9" + jest-circus "^27.5.1" + jest-environment-jsdom "^27.5.1" + jest-environment-node "^27.5.1" + jest-get-type "^27.5.1" + jest-jasmine2 "^27.5.1" + jest-regex-util "^27.5.1" + jest-resolve "^27.5.1" + jest-runner "^27.5.1" + jest-util "^27.5.1" + jest-validate "^27.5.1" + micromatch "^4.0.4" + parse-json "^5.2.0" + pretty-format "^27.5.1" + slash "^3.0.0" + strip-json-comments "^3.1.1" jest-dev-server@^4.4.0: version "4.4.0" @@ -13544,7 +13998,7 @@ jest-dev-server@^4.4.0: tree-kill "^1.2.2" wait-on "^3.3.0" -jest-diff@^24.3.0, jest-diff@^24.9.0: +jest-diff@^24.3.0: version "24.9.0" resolved "https://registry.yarnpkg.com/jest-diff/-/jest-diff-24.9.0.tgz#931b7d0d5778a1baf7452cb816e325e3724055da" integrity sha512-qMfrTs8AdJE2iqrTp0hzh7kTd2PQWrsFyj9tORoKmu32xjPjeE4NyjVRDz8ybYwqS2ik8N4hsIpiVTyFeo2lBQ== @@ -13564,37 +14018,48 @@ jest-diff@^27.2.5, jest-diff@^27.4.2: jest-get-type "^27.4.0" pretty-format "^27.4.2" -jest-docblock@^24.3.0: - version "24.9.0" - resolved "https://registry.yarnpkg.com/jest-docblock/-/jest-docblock-24.9.0.tgz#7970201802ba560e1c4092cc25cbedf5af5a8ce2" - integrity sha512-F1DjdpDMJMA1cN6He0FNYNZlo3yYmOtRUnktrT9Q37njYzC5WEaDdmbynIgy0L/IvXvvgsG8OsqhLPXTpfmZAA== +jest-diff@^27.5.1: + version "27.5.1" + resolved "https://registry.yarnpkg.com/jest-diff/-/jest-diff-27.5.1.tgz#a07f5011ac9e6643cf8a95a462b7b1ecf6680def" + integrity sha512-m0NvkX55LDt9T4mctTEgnZk3fmEg3NRYutvMPWM/0iPnkFj2wIeF45O1718cMSOFO1vINkqmxqD8vE37uTEbqw== dependencies: - detect-newline "^2.1.0" + chalk "^4.0.0" + diff-sequences "^27.5.1" + jest-get-type "^27.5.1" + pretty-format "^27.5.1" -jest-each@^24.9.0: - version "24.9.0" - resolved "https://registry.yarnpkg.com/jest-each/-/jest-each-24.9.0.tgz#eb2da602e2a610898dbc5f1f6df3ba86b55f8b05" - integrity sha512-ONi0R4BvW45cw8s2Lrx8YgbeXL1oCQ/wIDwmsM3CqM/nlblNCPmnC3IPQlMbRFZu3wKdQ2U8BqM6lh3LJ5Bsog== +jest-docblock@^27.5.1: + version "27.5.1" + resolved "https://registry.yarnpkg.com/jest-docblock/-/jest-docblock-27.5.1.tgz#14092f364a42c6108d42c33c8cf30e058e25f6c0" + integrity sha512-rl7hlABeTsRYxKiUfpHrQrG4e2obOiTQWfMEH3PxPjOtdsfLQO4ReWSZaQ7DETm4xu07rl4q/h4zcKXyU0/OzQ== dependencies: - "@jest/types" "^24.9.0" - chalk "^2.0.1" - jest-get-type "^24.9.0" - jest-util "^24.9.0" - pretty-format "^24.9.0" + detect-newline "^3.0.0" -jest-environment-jsdom@^24.9.0: - version "24.9.0" - resolved "https://registry.yarnpkg.com/jest-environment-jsdom/-/jest-environment-jsdom-24.9.0.tgz#4b0806c7fc94f95edb369a69cc2778eec2b7375b" - integrity sha512-Zv9FV9NBRzLuALXjvRijO2351DRQeLYXtpD4xNvfoVFw21IOKNhZAEUKcbiEtjTkm2GsJ3boMVgkaR7rN8qetA== +jest-each@^27.5.1: + version "27.5.1" + resolved "https://registry.yarnpkg.com/jest-each/-/jest-each-27.5.1.tgz#5bc87016f45ed9507fed6e4702a5b468a5b2c44e" + integrity sha512-1Ff6p+FbhT/bXQnEouYy00bkNSY7OUpfIcmdl8vZ31A1UUaurOLPA8a8BbJOF2RDUElwJhmeaV7LnagI+5UwNQ== dependencies: - "@jest/environment" "^24.9.0" - "@jest/fake-timers" "^24.9.0" - "@jest/types" "^24.9.0" - jest-mock "^24.9.0" - jest-util "^24.9.0" - jsdom "^11.5.1" + "@jest/types" "^27.5.1" + chalk "^4.0.0" + jest-get-type "^27.5.1" + jest-util "^27.5.1" + pretty-format "^27.5.1" + +jest-environment-jsdom@^27.5.1: + version "27.5.1" + resolved "https://registry.yarnpkg.com/jest-environment-jsdom/-/jest-environment-jsdom-27.5.1.tgz#ea9ccd1fc610209655a77898f86b2b559516a546" + integrity sha512-TFBvkTC1Hnnnrka/fUb56atfDtJ9VMZ94JkjTbggl1PEpwrYtUBKMezB3inLmWqQsXYLcMwNoDQwoBTAvFfsfw== + dependencies: + "@jest/environment" "^27.5.1" + "@jest/fake-timers" "^27.5.1" + "@jest/types" "^27.5.1" + "@types/node" "*" + jest-mock "^27.5.1" + jest-util "^27.5.1" + jsdom "^16.6.0" -jest-environment-node@24, "jest-environment-node@>=24 <=26", jest-environment-node@^24.9.0: +"jest-environment-node@>=24 <=26": version "24.9.0" resolved "https://registry.yarnpkg.com/jest-environment-node/-/jest-environment-node-24.9.0.tgz#333d2d2796f9687f2aeebf0742b519f33c1cbfd3" integrity sha512-6d4V2f4nxzIzwendo27Tr0aFm+IXWa0XEUnaH6nU0FMaozxovt+sfRvh4J47wL1OvF83I3SSTu0XK+i4Bqe7uA== @@ -13605,6 +14070,18 @@ jest-environment-node@24, "jest-environment-node@>=24 <=26", jest-environment-no jest-mock "^24.9.0" jest-util "^24.9.0" +jest-environment-node@^27.5.1: + version "27.5.1" + resolved "https://registry.yarnpkg.com/jest-environment-node/-/jest-environment-node-27.5.1.tgz#dedc2cfe52fab6b8f5714b4808aefa85357a365e" + integrity sha512-Jt4ZUnxdOsTGwSRAfKEnE6BcwsSPNOijjwifq5sDFSA2kesnXTvNqKHYgM0hDq3549Uf/KzdXNYn4wMZJPlFLw== + dependencies: + "@jest/environment" "^27.5.1" + "@jest/fake-timers" "^27.5.1" + "@jest/types" "^27.5.1" + "@types/node" "*" + jest-mock "^27.5.1" + jest-util "^27.5.1" + jest-environment-puppeteer@^4.4.0: version "4.4.0" resolved "https://registry.yarnpkg.com/jest-environment-puppeteer/-/jest-environment-puppeteer-4.4.0.tgz#d82a37e0e0c51b63cc6b15dea101d53967508860" @@ -13625,6 +14102,11 @@ jest-get-type@^27.0.6, jest-get-type@^27.4.0: resolved "https://registry.yarnpkg.com/jest-get-type/-/jest-get-type-27.4.0.tgz#7503d2663fffa431638337b3998d39c5e928e9b5" integrity sha512-tk9o+ld5TWq41DkK14L4wox4s2D9MtTpKaAVzXfr5CUKm5ZK2ExcaFE0qls2W71zE/6R2TxxrK9w2r6svAFDBQ== +jest-get-type@^27.5.1: + version "27.5.1" + resolved "https://registry.yarnpkg.com/jest-get-type/-/jest-get-type-27.5.1.tgz#3cd613c507b0f7ace013df407a1c1cd578bcb4f1" + integrity sha512-2KY95ksYSaK7DMBWQn6dQz3kqAf3BB64y2udeG+hv4KfSOb9qwcYQstTJc1KCbsix+wLZWZYN8t7nwX3GOBLRw== + jest-haste-map@^24.9.0: version "24.9.0" resolved "https://registry.yarnpkg.com/jest-haste-map/-/jest-haste-map-24.9.0.tgz#b38a5d64274934e21fa417ae9a9fbeb77ceaac7d" @@ -13644,35 +14126,56 @@ jest-haste-map@^24.9.0: optionalDependencies: fsevents "^1.2.7" -jest-jasmine2@^24.9.0: - version "24.9.0" - resolved "https://registry.yarnpkg.com/jest-jasmine2/-/jest-jasmine2-24.9.0.tgz#1f7b1bd3242c1774e62acabb3646d96afc3be6a0" - integrity sha512-Cq7vkAgaYKp+PsX+2/JbTarrk0DmNhsEtqBXNwUHkdlbrTBLtMJINADf2mf5FkowNsq8evbPc07/qFO0AdKTzw== +jest-haste-map@^27.5.1: + version "27.5.1" + resolved "https://registry.yarnpkg.com/jest-haste-map/-/jest-haste-map-27.5.1.tgz#9fd8bd7e7b4fa502d9c6164c5640512b4e811e7f" + integrity sha512-7GgkZ4Fw4NFbMSDSpZwXeBiIbx+t/46nJ2QitkOjvwPYyZmqttu2TDSimMHP1EkPOi4xUZAN1doE5Vd25H4Jng== dependencies: - "@babel/traverse" "^7.1.0" - "@jest/environment" "^24.9.0" - "@jest/test-result" "^24.9.0" - "@jest/types" "^24.9.0" - chalk "^2.0.1" + "@jest/types" "^27.5.1" + "@types/graceful-fs" "^4.1.2" + "@types/node" "*" + anymatch "^3.0.3" + fb-watchman "^2.0.0" + graceful-fs "^4.2.9" + jest-regex-util "^27.5.1" + jest-serializer "^27.5.1" + jest-util "^27.5.1" + jest-worker "^27.5.1" + micromatch "^4.0.4" + walker "^1.0.7" + optionalDependencies: + fsevents "^2.3.2" + +jest-jasmine2@^27.5.1: + version "27.5.1" + resolved "https://registry.yarnpkg.com/jest-jasmine2/-/jest-jasmine2-27.5.1.tgz#a037b0034ef49a9f3d71c4375a796f3b230d1ac4" + integrity sha512-jtq7VVyG8SqAorDpApwiJJImd0V2wv1xzdheGHRGyuT7gZm6gG47QEskOlzsN1PG/6WNaCo5pmwMHDf3AkG2pQ== + dependencies: + "@jest/environment" "^27.5.1" + "@jest/source-map" "^27.5.1" + "@jest/test-result" "^27.5.1" + "@jest/types" "^27.5.1" + "@types/node" "*" + chalk "^4.0.0" co "^4.6.0" - expect "^24.9.0" + expect "^27.5.1" is-generator-fn "^2.0.0" - jest-each "^24.9.0" - jest-matcher-utils "^24.9.0" - jest-message-util "^24.9.0" - jest-runtime "^24.9.0" - jest-snapshot "^24.9.0" - jest-util "^24.9.0" - pretty-format "^24.9.0" - throat "^4.0.0" - -jest-leak-detector@^24.9.0: - version "24.9.0" - resolved "https://registry.yarnpkg.com/jest-leak-detector/-/jest-leak-detector-24.9.0.tgz#b665dea7c77100c5c4f7dfcb153b65cf07dcf96a" - integrity sha512-tYkFIDsiKTGwb2FG1w8hX9V0aUb2ot8zY/2nFg087dUageonw1zrLMP4W6zsRO59dPkTSKie+D4rhMuP9nRmrA== + jest-each "^27.5.1" + jest-matcher-utils "^27.5.1" + jest-message-util "^27.5.1" + jest-runtime "^27.5.1" + jest-snapshot "^27.5.1" + jest-util "^27.5.1" + pretty-format "^27.5.1" + throat "^6.0.1" + +jest-leak-detector@^27.5.1: + version "27.5.1" + resolved "https://registry.yarnpkg.com/jest-leak-detector/-/jest-leak-detector-27.5.1.tgz#6ec9d54c3579dd6e3e66d70e3498adf80fde3fb8" + integrity sha512-POXfWAMvfU6WMUXftV4HolnJfnPOGEu10fscNCA76KBpRRhcMN2c8d3iT2pxQS3HLbA+5X4sOUPzYO2NUyIlHQ== dependencies: - jest-get-type "^24.9.0" - pretty-format "^24.9.0" + jest-get-type "^27.5.1" + pretty-format "^27.5.1" jest-matcher-utils@=27.2.5: version "27.2.5" @@ -13684,16 +14187,6 @@ jest-matcher-utils@=27.2.5: jest-get-type "^27.0.6" pretty-format "^27.2.5" -jest-matcher-utils@^24.9.0: - version "24.9.0" - resolved "https://registry.yarnpkg.com/jest-matcher-utils/-/jest-matcher-utils-24.9.0.tgz#f5b3661d5e628dffe6dd65251dfdae0e87c3a073" - integrity sha512-OZz2IXsu6eaiMAwe67c1T+5tUAtQyQx27/EMEkbFAGiw52tB9em+uGbzpcgYVpA8wl0hlxKPZxrly4CXU/GjHA== - dependencies: - chalk "^2.0.1" - jest-diff "^24.9.0" - jest-get-type "^24.9.0" - pretty-format "^24.9.0" - jest-matcher-utils@^27.2.5: version "27.4.2" resolved "https://registry.yarnpkg.com/jest-matcher-utils/-/jest-matcher-utils-27.4.2.tgz#d17c5038607978a255e0a9a5c32c24e984b6c60b" @@ -13704,6 +14197,16 @@ jest-matcher-utils@^27.2.5: jest-get-type "^27.4.0" pretty-format "^27.4.2" +jest-matcher-utils@^27.5.1: + version "27.5.1" + resolved "https://registry.yarnpkg.com/jest-matcher-utils/-/jest-matcher-utils-27.5.1.tgz#9c0cdbda8245bc22d2331729d1091308b40cf8ab" + integrity sha512-z2uTx/T6LBaCoNWNFWwChLBKYxTMcGBRjAt+2SbP929/Fflb9aa5LGma654Rz8z9HLxsrUaYzxE9T/EFIL/PAw== + dependencies: + chalk "^4.0.0" + jest-diff "^27.5.1" + jest-get-type "^27.5.1" + pretty-format "^27.5.1" + jest-message-util@^24.9.0: version "24.9.0" resolved "https://registry.yarnpkg.com/jest-message-util/-/jest-message-util-24.9.0.tgz#527f54a1e380f5e202a8d1149b0ec872f43119e3" @@ -13733,6 +14236,21 @@ jest-message-util@^27.2.5: slash "^3.0.0" stack-utils "^2.0.3" +jest-message-util@^27.5.1: + version "27.5.1" + resolved "https://registry.yarnpkg.com/jest-message-util/-/jest-message-util-27.5.1.tgz#bdda72806da10d9ed6425e12afff38cd1458b6cf" + integrity sha512-rMyFe1+jnyAAf+NHwTclDz0eAaLkVDdKVHHBFWsBWHnnh5YeJMNWWsv7AbFYXfK3oTqvL7VTWkhNLu1jX24D+g== + dependencies: + "@babel/code-frame" "^7.12.13" + "@jest/types" "^27.5.1" + "@types/stack-utils" "^2.0.0" + chalk "^4.0.0" + graceful-fs "^4.2.9" + micromatch "^4.0.4" + pretty-format "^27.5.1" + slash "^3.0.0" + stack-utils "^2.0.3" + jest-mock@^24.9.0: version "24.9.0" resolved "https://registry.yarnpkg.com/jest-mock/-/jest-mock-24.9.0.tgz#c22835541ee379b908673ad51087a2185c13f1c6" @@ -13740,7 +14258,15 @@ jest-mock@^24.9.0: dependencies: "@jest/types" "^24.9.0" -jest-pnp-resolver@^1.2.1: +jest-mock@^27.5.1: + version "27.5.1" + resolved "https://registry.yarnpkg.com/jest-mock/-/jest-mock-27.5.1.tgz#19948336d49ef4d9c52021d34ac7b5f36ff967d6" + integrity sha512-K4jKbY1d4ENhbrG2zuPWaQBvDly+iZ2yAW+T1fATN78hc0sInwn7wZB8XtlNnvHug5RMwV897Xm4LqmPM4e2Og== + dependencies: + "@jest/types" "^27.5.1" + "@types/node" "*" + +jest-pnp-resolver@^1.2.2: version "1.2.2" resolved "https://registry.yarnpkg.com/jest-pnp-resolver/-/jest-pnp-resolver-1.2.2.tgz#b704ac0ae028a89108a4d040b3f919dfddc8e33c" integrity sha512-olV41bKSMm8BdnuMsewT4jqlZ8+3TCARAXjZGT9jcoSnrfUnRCqnMoF9XEeoWjbzObpqF9dRhHQj0Xb9QdF6/w== @@ -13753,7 +14279,7 @@ jest-puppeteer@^4.4.0: expect-puppeteer "^4.4.0" jest-environment-puppeteer "^4.4.0" -jest-regex-util@^24.3.0, jest-regex-util@^24.9.0: +jest-regex-util@^24.9.0: version "24.9.0" resolved "https://registry.yarnpkg.com/jest-regex-util/-/jest-regex-util-24.9.0.tgz#c13fb3380bde22bf6575432c493ea8fe37965636" integrity sha512-05Cmb6CuxaA+Ys6fjr3PhvV3bGQmO+2p2La4hFbU+W5uOc479f7FdLXUWXw4pYMAhhSZIuKHwSXSu6CsSBAXQA== @@ -13763,103 +14289,131 @@ jest-regex-util@^27.0.6: resolved "https://registry.yarnpkg.com/jest-regex-util/-/jest-regex-util-27.4.0.tgz#e4c45b52653128843d07ad94aec34393ea14fbca" integrity sha512-WeCpMpNnqJYMQoOjm1nTtsgbR4XHAk1u00qDoNBQoykM280+/TmgA5Qh5giC1ecy6a5d4hbSsHzpBtu5yvlbEg== -jest-resolve-dependencies@^24.9.0: - version "24.9.0" - resolved "https://registry.yarnpkg.com/jest-resolve-dependencies/-/jest-resolve-dependencies-24.9.0.tgz#ad055198959c4cfba8a4f066c673a3f0786507ab" - integrity sha512-Fm7b6AlWnYhT0BXy4hXpactHIqER7erNgIsIozDXWl5dVm+k8XdGVe1oTg1JyaFnOxarMEbax3wyRJqGP2Pq+g== - dependencies: - "@jest/types" "^24.9.0" - jest-regex-util "^24.3.0" - jest-snapshot "^24.9.0" +jest-regex-util@^27.5.1: + version "27.5.1" + resolved "https://registry.yarnpkg.com/jest-regex-util/-/jest-regex-util-27.5.1.tgz#4da143f7e9fd1e542d4aa69617b38e4a78365b95" + integrity sha512-4bfKq2zie+x16okqDXjXn9ql2B0dScQu+vcwe4TvFVhkVyuWLqpZrZtXxLLWoXYgn0E87I6r6GRYHF7wFZBUvg== -jest-resolve@^24.9.0: - version "24.9.0" - resolved "https://registry.yarnpkg.com/jest-resolve/-/jest-resolve-24.9.0.tgz#dff04c7687af34c4dd7e524892d9cf77e5d17321" - integrity sha512-TaLeLVL1l08YFZAt3zaPtjiVvyy4oSA6CRe+0AFPPVX3Q/VI0giIWWoAvoS5L96vj9Dqxj4fB5p2qrHCmTU/MQ== +jest-resolve-dependencies@^27.5.1: + version "27.5.1" + resolved "https://registry.yarnpkg.com/jest-resolve-dependencies/-/jest-resolve-dependencies-27.5.1.tgz#d811ecc8305e731cc86dd79741ee98fed06f1da8" + integrity sha512-QQOOdY4PE39iawDn5rzbIePNigfe5B9Z91GDD1ae/xNDlu9kaat8QQ5EKnNmVWPV54hUdxCVwwj6YMgR2O7IOg== dependencies: - "@jest/types" "^24.9.0" - browser-resolve "^1.11.3" - chalk "^2.0.1" - jest-pnp-resolver "^1.2.1" - realpath-native "^1.1.0" + "@jest/types" "^27.5.1" + jest-regex-util "^27.5.1" + jest-snapshot "^27.5.1" -jest-runner@^24.9.0: - version "24.9.0" - resolved "https://registry.yarnpkg.com/jest-runner/-/jest-runner-24.9.0.tgz#574fafdbd54455c2b34b4bdf4365a23857fcdf42" - integrity sha512-KksJQyI3/0mhcfspnxxEOBueGrd5E4vV7ADQLT9ESaCzz02WnbdbKWIf5Mkaucoaj7obQckYPVX6JJhgUcoWWg== +jest-resolve@^27.5.1: + version "27.5.1" + resolved "https://registry.yarnpkg.com/jest-resolve/-/jest-resolve-27.5.1.tgz#a2f1c5a0796ec18fe9eb1536ac3814c23617b384" + integrity sha512-FFDy8/9E6CV83IMbDpcjOhumAQPDyETnU2KZ1O98DwTnz8AOBsW/Xv3GySr1mOZdItLR+zDZ7I/UdTFbgSOVCw== dependencies: - "@jest/console" "^24.7.1" - "@jest/environment" "^24.9.0" - "@jest/test-result" "^24.9.0" - "@jest/types" "^24.9.0" - chalk "^2.4.2" - exit "^0.1.2" - graceful-fs "^4.1.15" - jest-config "^24.9.0" - jest-docblock "^24.3.0" - jest-haste-map "^24.9.0" - jest-jasmine2 "^24.9.0" - jest-leak-detector "^24.9.0" - jest-message-util "^24.9.0" - jest-resolve "^24.9.0" - jest-runtime "^24.9.0" - jest-util "^24.9.0" - jest-worker "^24.6.0" + "@jest/types" "^27.5.1" + chalk "^4.0.0" + graceful-fs "^4.2.9" + jest-haste-map "^27.5.1" + jest-pnp-resolver "^1.2.2" + jest-util "^27.5.1" + jest-validate "^27.5.1" + resolve "^1.20.0" + resolve.exports "^1.1.0" + slash "^3.0.0" + +jest-runner@^27.5.1: + version "27.5.1" + resolved "https://registry.yarnpkg.com/jest-runner/-/jest-runner-27.5.1.tgz#071b27c1fa30d90540805c5645a0ec167c7b62e5" + integrity sha512-g4NPsM4mFCOwFKXO4p/H/kWGdJp9V8kURY2lX8Me2drgXqG7rrZAx5kv+5H7wtt/cdFIjhqYx1HrlqWHaOvDaQ== + dependencies: + "@jest/console" "^27.5.1" + "@jest/environment" "^27.5.1" + "@jest/test-result" "^27.5.1" + "@jest/transform" "^27.5.1" + "@jest/types" "^27.5.1" + "@types/node" "*" + chalk "^4.0.0" + emittery "^0.8.1" + graceful-fs "^4.2.9" + jest-docblock "^27.5.1" + jest-environment-jsdom "^27.5.1" + jest-environment-node "^27.5.1" + jest-haste-map "^27.5.1" + jest-leak-detector "^27.5.1" + jest-message-util "^27.5.1" + jest-resolve "^27.5.1" + jest-runtime "^27.5.1" + jest-util "^27.5.1" + jest-worker "^27.5.1" source-map-support "^0.5.6" - throat "^4.0.0" + throat "^6.0.1" -jest-runtime@^24.9.0: - version "24.9.0" - resolved "https://registry.yarnpkg.com/jest-runtime/-/jest-runtime-24.9.0.tgz#9f14583af6a4f7314a6a9d9f0226e1a781c8e4ac" - integrity sha512-8oNqgnmF3v2J6PVRM2Jfuj8oX3syKmaynlDMMKQ4iyzbQzIG6th5ub/lM2bCMTmoTKM3ykcUYI2Pw9xwNtjMnw== - dependencies: - "@jest/console" "^24.7.1" - "@jest/environment" "^24.9.0" - "@jest/source-map" "^24.3.0" - "@jest/transform" "^24.9.0" - "@jest/types" "^24.9.0" - "@types/yargs" "^13.0.0" - chalk "^2.0.1" - exit "^0.1.2" +jest-runtime@^27.5.1: + version "27.5.1" + resolved "https://registry.yarnpkg.com/jest-runtime/-/jest-runtime-27.5.1.tgz#4896003d7a334f7e8e4a53ba93fb9bcd3db0a1af" + integrity sha512-o7gxw3Gf+H2IGt8fv0RiyE1+r83FJBRruoA+FXrlHw6xEyBsU8ugA6IPfTdVyA0w8HClpbK+DGJxH59UrNMx8A== + dependencies: + "@jest/environment" "^27.5.1" + "@jest/fake-timers" "^27.5.1" + "@jest/globals" "^27.5.1" + "@jest/source-map" "^27.5.1" + "@jest/test-result" "^27.5.1" + "@jest/transform" "^27.5.1" + "@jest/types" "^27.5.1" + chalk "^4.0.0" + cjs-module-lexer "^1.0.0" + collect-v8-coverage "^1.0.0" + execa "^5.0.0" glob "^7.1.3" - graceful-fs "^4.1.15" - jest-config "^24.9.0" - jest-haste-map "^24.9.0" - jest-message-util "^24.9.0" - jest-mock "^24.9.0" - jest-regex-util "^24.3.0" - jest-resolve "^24.9.0" - jest-snapshot "^24.9.0" - jest-util "^24.9.0" - jest-validate "^24.9.0" - realpath-native "^1.1.0" - slash "^2.0.0" - strip-bom "^3.0.0" - yargs "^13.3.0" + graceful-fs "^4.2.9" + jest-haste-map "^27.5.1" + jest-message-util "^27.5.1" + jest-mock "^27.5.1" + jest-regex-util "^27.5.1" + jest-resolve "^27.5.1" + jest-snapshot "^27.5.1" + jest-util "^27.5.1" + slash "^3.0.0" + strip-bom "^4.0.0" jest-serializer@^24.9.0: version "24.9.0" resolved "https://registry.yarnpkg.com/jest-serializer/-/jest-serializer-24.9.0.tgz#e6d7d7ef96d31e8b9079a714754c5d5c58288e73" integrity sha512-DxYipDr8OvfrKH3Kel6NdED3OXxjvxXZ1uIY2I9OFbGg+vUkkg7AGvi65qbhbWNPvDckXmzMPbK3u3HaDO49bQ== -jest-snapshot@^24.9.0: - version "24.9.0" - resolved "https://registry.yarnpkg.com/jest-snapshot/-/jest-snapshot-24.9.0.tgz#ec8e9ca4f2ec0c5c87ae8f925cf97497b0e951ba" - integrity sha512-uI/rszGSs73xCM0l+up7O7a40o90cnrk429LOiK3aeTvfC0HHmldbd81/B7Ix81KSFe1lwkbl7GnBGG4UfuDew== +jest-serializer@^27.5.1: + version "27.5.1" + resolved "https://registry.yarnpkg.com/jest-serializer/-/jest-serializer-27.5.1.tgz#81438410a30ea66fd57ff730835123dea1fb1f64" + integrity sha512-jZCyo6iIxO1aqUxpuBlwTDMkzOAJS4a3eYz3YzgxxVQFwLeSA7Jfq5cbqCY+JLvTDrWirgusI/0KwxKMgrdf7w== dependencies: + "@types/node" "*" + graceful-fs "^4.2.9" + +jest-snapshot@^27.5.1: + version "27.5.1" + resolved "https://registry.yarnpkg.com/jest-snapshot/-/jest-snapshot-27.5.1.tgz#b668d50d23d38054a51b42c4039cab59ae6eb6a1" + integrity sha512-yYykXI5a0I31xX67mgeLw1DZ0bJB+gpq5IpSuCAoyDi0+BhgU/RIrL+RTzDmkNTchvDFWKP8lp+w/42Z3us5sA== + dependencies: + "@babel/core" "^7.7.2" + "@babel/generator" "^7.7.2" + "@babel/plugin-syntax-typescript" "^7.7.2" + "@babel/traverse" "^7.7.2" "@babel/types" "^7.0.0" - "@jest/types" "^24.9.0" - chalk "^2.0.1" - expect "^24.9.0" - jest-diff "^24.9.0" - jest-get-type "^24.9.0" - jest-matcher-utils "^24.9.0" - jest-message-util "^24.9.0" - jest-resolve "^24.9.0" - mkdirp "^0.5.1" + "@jest/transform" "^27.5.1" + "@jest/types" "^27.5.1" + "@types/babel__traverse" "^7.0.4" + "@types/prettier" "^2.1.5" + babel-preset-current-node-syntax "^1.0.0" + chalk "^4.0.0" + expect "^27.5.1" + graceful-fs "^4.2.9" + jest-diff "^27.5.1" + jest-get-type "^27.5.1" + jest-haste-map "^27.5.1" + jest-matcher-utils "^27.5.1" + jest-message-util "^27.5.1" + jest-util "^27.5.1" natural-compare "^1.4.0" - pretty-format "^24.9.0" - semver "^6.2.0" + pretty-format "^27.5.1" + semver "^7.3.2" jest-util@^24.9.0: version "24.9.0" @@ -13879,30 +14433,42 @@ jest-util@^24.9.0: slash "^2.0.0" source-map "^0.6.0" -jest-validate@^24.9.0: - version "24.9.0" - resolved "https://registry.yarnpkg.com/jest-validate/-/jest-validate-24.9.0.tgz#0775c55360d173cd854e40180756d4ff52def8ab" - integrity sha512-HPIt6C5ACwiqSiwi+OfSSHbK8sG7akG8eATl+IPKaeIjtPOeBUd/g3J7DghugzxrGjI93qS/+RPKe1H6PqvhRQ== +jest-util@^27.0.0, jest-util@^27.5.1: + version "27.5.1" + resolved "https://registry.yarnpkg.com/jest-util/-/jest-util-27.5.1.tgz#3ba9771e8e31a0b85da48fe0b0891fb86c01c2f9" + integrity sha512-Kv2o/8jNvX1MQ0KGtw480E/w4fBCDOnH6+6DmeKi6LZUIlKA5kwY0YNdlzaWTiVgxqAqik11QyxDOKk543aKXw== dependencies: - "@jest/types" "^24.9.0" - camelcase "^5.3.1" - chalk "^2.0.1" - jest-get-type "^24.9.0" + "@jest/types" "^27.5.1" + "@types/node" "*" + chalk "^4.0.0" + ci-info "^3.2.0" + graceful-fs "^4.2.9" + picomatch "^2.2.3" + +jest-validate@^27.5.1: + version "27.5.1" + resolved "https://registry.yarnpkg.com/jest-validate/-/jest-validate-27.5.1.tgz#9197d54dc0bdb52260b8db40b46ae668e04df067" + integrity sha512-thkNli0LYTmOI1tDB3FI1S1RTp/Bqyd9pTarJwL87OIBFuqEb5Apv5EaApEudYg4g86e3CT6kM0RowkhtEnCBQ== + dependencies: + "@jest/types" "^27.5.1" + camelcase "^6.2.0" + chalk "^4.0.0" + jest-get-type "^27.5.1" leven "^3.1.0" - pretty-format "^24.9.0" + pretty-format "^27.5.1" -jest-watcher@^24.9.0: - version "24.9.0" - resolved "https://registry.yarnpkg.com/jest-watcher/-/jest-watcher-24.9.0.tgz#4b56e5d1ceff005f5b88e528dc9afc8dd4ed2b3b" - integrity sha512-+/fLOfKPXXYJDYlks62/4R4GoT+GU1tYZed99JSCOsmzkkF7727RqKrjNAxtfO4YpGv11wybgRvCjR73lK2GZw== +jest-watcher@^27.5.1: + version "27.5.1" + resolved "https://registry.yarnpkg.com/jest-watcher/-/jest-watcher-27.5.1.tgz#71bd85fb9bde3a2c2ec4dc353437971c43c642a2" + integrity sha512-z676SuD6Z8o8qbmEGhoEUFOM1+jfEiL3DXHK/xgEiG2EyNYfFG60jluWcupY6dATjfEsKQuibReS1djInQnoVw== dependencies: - "@jest/test-result" "^24.9.0" - "@jest/types" "^24.9.0" - "@types/yargs" "^13.0.0" - ansi-escapes "^3.0.0" - chalk "^2.0.1" - jest-util "^24.9.0" - string-length "^2.0.0" + "@jest/test-result" "^27.5.1" + "@jest/types" "^27.5.1" + "@types/node" "*" + ansi-escapes "^4.2.1" + chalk "^4.0.0" + jest-util "^27.5.1" + string-length "^4.0.1" jest-worker@27.0.0-next.5: version "27.0.0-next.5" @@ -13913,7 +14479,7 @@ jest-worker@27.0.0-next.5: merge-stream "^2.0.0" supports-color "^8.0.0" -jest-worker@^24.6.0, jest-worker@^24.9.0: +jest-worker@^24.9.0: version "24.9.0" resolved "https://registry.yarnpkg.com/jest-worker/-/jest-worker-24.9.0.tgz#5dbfdb5b2d322e98567898238a9697bcce67b3e5" integrity sha512-51PE4haMSXcHohnSMdM42anbvZANYTqMrr52tVKPqqsPJMzoP6FYYDVqahX/HrAoKEKz3uUPzSvKs9A3qR4iVw== @@ -13939,13 +14505,23 @@ jest-worker@^27.0.6: merge-stream "^2.0.0" supports-color "^8.0.0" -jest@^24.9.0: - version "24.9.0" - resolved "https://registry.yarnpkg.com/jest/-/jest-24.9.0.tgz#987d290c05a08b52c56188c1002e368edb007171" - integrity sha512-YvkBL1Zm7d2B1+h5fHEOdyjCG+sGMz4f8D86/0HiqJ6MB4MnDc8FgP5vdWsGnemOQro7lnYo8UakZ3+5A0jxGw== +jest-worker@^27.5.1: + version "27.5.1" + resolved "https://registry.yarnpkg.com/jest-worker/-/jest-worker-27.5.1.tgz#8d146f0900e8973b106b6f73cc1e9a8cb86f8db0" + integrity sha512-7vuh85V5cdDofPyxn58nrPjBktZo0u9x1g8WtjQol+jZDaE+fhN+cIvTj11GndBnMnyfrUOG1sZQxCdjKh+DKg== + dependencies: + "@types/node" "*" + merge-stream "^2.0.0" + supports-color "^8.0.0" + +jest@^27.5.1: + version "27.5.1" + resolved "https://registry.yarnpkg.com/jest/-/jest-27.5.1.tgz#dadf33ba70a779be7a6fc33015843b51494f63fc" + integrity sha512-Yn0mADZB89zTtjkPJEXwrac3LHudkQMR+Paqa8uxJHCBr9agxztUifWCyiYrjhMPBoUVBjyny0I7XH6ozDr7QQ== dependencies: - import-local "^2.0.0" - jest-cli "^24.9.0" + "@jest/core" "^27.5.1" + import-local "^3.0.2" + jest-cli "^27.5.1" jmespath@0.15.0: version "0.15.0" @@ -14016,77 +14592,13 @@ jsdoctypeparser@^9.0.0: resolved "https://registry.yarnpkg.com/jsdoctypeparser/-/jsdoctypeparser-9.0.0.tgz#8c97e2fb69315eb274b0f01377eaa5c940bd7b26" integrity sha512-jrTA2jJIL6/DAEILBEh2/w9QxCuwmvNXIry39Ay/HVfhE3o2yVV0U44blYkqdHA/OKloJEqvJy0xU+GSdE2SIw== -jsdom@^11.5.1: - version "11.12.0" - resolved "https://registry.yarnpkg.com/jsdom/-/jsdom-11.12.0.tgz#1a80d40ddd378a1de59656e9e6dc5a3ba8657bc8" - integrity sha512-y8Px43oyiBM13Zc1z780FrfNLJCXTL40EWlty/LXUtcjykRBNgLlCjWXpfSPBl2iv+N7koQN+dvqszHZgT/Fjw== - dependencies: - abab "^2.0.0" - acorn "^5.5.3" - acorn-globals "^4.1.0" - array-equal "^1.0.0" - cssom ">= 0.3.2 < 0.4.0" - cssstyle "^1.0.0" - data-urls "^1.0.0" - domexception "^1.0.1" - escodegen "^1.9.1" - html-encoding-sniffer "^1.0.2" - left-pad "^1.3.0" - nwsapi "^2.0.7" - parse5 "4.0.0" - pn "^1.1.0" - request "^2.87.0" - request-promise-native "^1.0.5" - sax "^1.2.4" - symbol-tree "^3.2.2" - tough-cookie "^2.3.4" - w3c-hr-time "^1.0.1" - webidl-conversions "^4.0.2" - whatwg-encoding "^1.0.3" - whatwg-mimetype "^2.1.0" - whatwg-url "^6.4.1" - ws "^5.2.0" - xml-name-validator "^3.0.0" - -jsdom@^15.0.0: - version "15.2.1" - resolved "https://registry.yarnpkg.com/jsdom/-/jsdom-15.2.1.tgz#d2feb1aef7183f86be521b8c6833ff5296d07ec5" - integrity sha512-fAl1W0/7T2G5vURSyxBzrJ1LSdQn6Tr5UX/xD4PXDx/PDgwygedfW6El/KIj3xJ7FU61TTYnc/l/B7P49Eqt6g== - dependencies: - abab "^2.0.0" - acorn "^7.1.0" - acorn-globals "^4.3.2" - array-equal "^1.0.0" - cssom "^0.4.1" - cssstyle "^2.0.0" - data-urls "^1.1.0" - domexception "^1.0.1" - escodegen "^1.11.1" - html-encoding-sniffer "^1.0.2" - nwsapi "^2.2.0" - parse5 "5.1.0" - pn "^1.1.0" - request "^2.88.0" - request-promise-native "^1.0.7" - saxes "^3.1.9" - symbol-tree "^3.2.2" - tough-cookie "^3.0.1" - w3c-hr-time "^1.0.1" - w3c-xmlserializer "^1.1.2" - webidl-conversions "^4.0.2" - whatwg-encoding "^1.0.5" - whatwg-mimetype "^2.3.0" - whatwg-url "^7.0.0" - ws "^7.0.0" - xml-name-validator "^3.0.0" - -jsdom@^16.2.2: - version "16.5.2" - resolved "https://registry.yarnpkg.com/jsdom/-/jsdom-16.5.2.tgz#583fac89a0aea31dbf6237e7e4bedccd9beab472" - integrity sha512-JxNtPt9C1ut85boCbJmffaQ06NBnzkQY/MWO3YxPW8IWS38A26z+B1oBvA9LwKrytewdfymnhi4UNH3/RAgZrg== +jsdom@^16.6.0: + version "16.7.0" + resolved "https://registry.yarnpkg.com/jsdom/-/jsdom-16.7.0.tgz#918ae71965424b197c819f8183a754e18977b710" + integrity sha512-u9Smc2G1USStM+s/x1ru5Sxrl6mPYCbByG1U/hUmqaVsm4tbNyS7CicOSRyuGQYZhTu0h84qkZZQ/I+dzizSVw== dependencies: abab "^2.0.5" - acorn "^8.1.0" + acorn "^8.2.4" acorn-globals "^6.0.0" cssom "^0.4.4" cssstyle "^2.3.0" @@ -14094,12 +14606,13 @@ jsdom@^16.2.2: decimal.js "^10.2.1" domexception "^2.0.1" escodegen "^2.0.0" + form-data "^3.0.0" html-encoding-sniffer "^2.0.1" - is-potential-custom-element-name "^1.0.0" + http-proxy-agent "^4.0.1" + https-proxy-agent "^5.0.0" + is-potential-custom-element-name "^1.0.1" nwsapi "^2.2.0" parse5 "6.0.1" - request "^2.88.2" - request-promise-native "^1.0.9" saxes "^5.0.1" symbol-tree "^3.2.4" tough-cookie "^4.0.0" @@ -14109,9 +14622,42 @@ jsdom@^16.2.2: whatwg-encoding "^1.0.5" whatwg-mimetype "^2.3.0" whatwg-url "^8.5.0" - ws "^7.4.4" + ws "^7.4.6" xml-name-validator "^3.0.0" +jsdom@^19.0.0: + version "19.0.0" + resolved "https://registry.yarnpkg.com/jsdom/-/jsdom-19.0.0.tgz#93e67c149fe26816d38a849ea30ac93677e16b6a" + integrity sha512-RYAyjCbxy/vri/CfnjUWJQQtZ3LKlLnDqj+9XLNnJPgEGeirZs3hllKR20re8LUZ6o1b1X4Jat+Qd26zmP41+A== + dependencies: + abab "^2.0.5" + acorn "^8.5.0" + acorn-globals "^6.0.0" + cssom "^0.5.0" + cssstyle "^2.3.0" + data-urls "^3.0.1" + decimal.js "^10.3.1" + domexception "^4.0.0" + escodegen "^2.0.0" + form-data "^4.0.0" + html-encoding-sniffer "^3.0.0" + http-proxy-agent "^5.0.0" + https-proxy-agent "^5.0.0" + is-potential-custom-element-name "^1.0.1" + nwsapi "^2.2.0" + parse5 "6.0.1" + saxes "^5.0.1" + symbol-tree "^3.2.4" + tough-cookie "^4.0.0" + w3c-hr-time "^1.0.2" + w3c-xmlserializer "^3.0.0" + webidl-conversions "^7.0.0" + whatwg-encoding "^2.0.0" + whatwg-mimetype "^3.0.0" + whatwg-url "^10.0.0" + ws "^8.2.3" + xml-name-validator "^4.0.0" + jsesc@^1.3.0: version "1.3.0" resolved "https://registry.yarnpkg.com/jsesc/-/jsesc-1.3.0.tgz#46c3fec8c1892b12b0833db9bc7622176dbab34b" @@ -14205,6 +14751,11 @@ json5@^1.0.1: dependencies: minimist "^1.2.0" +json5@^2.2.1: + version "2.2.1" + resolved "https://registry.yarnpkg.com/json5/-/json5-2.2.1.tgz#655d50ed1e6f95ad1a3caababd2b0efda10b395c" + integrity sha512-1hqLFMSrGHRHxav9q9gNjJ5EXznIxGVO09xQRrwplcS8qs28pZ8s8hupZAmqDwZUmVZ2Qb2jnyPOWcDH8m8dlA== + jsonfile@^2.1.0: version "2.4.0" resolved "https://registry.yarnpkg.com/jsonfile/-/jsonfile-2.4.0.tgz#3736a2b428b87bbda0cc83b53fa3d633a35c2ae8" @@ -14528,11 +15079,6 @@ leek@0.0.24: lodash.assign "^3.2.0" rsvp "^3.0.21" -left-pad@^1.3.0: - version "1.3.0" - resolved "https://registry.yarnpkg.com/left-pad/-/left-pad-1.3.0.tgz#5b8a3a7765dfe001261dde915589e782f8c94d1e" - integrity sha512-XI5MPzVNApjAyhQzphX8BkmKsKUxD4LdyK24iZeQGinBN9yTQT3bFlCBy/aVx2HrNcqQGsdot8ghrjyrvMCoEA== - lerna@3.13.4: version "3.13.4" resolved "https://registry.yarnpkg.com/lerna/-/lerna-3.13.4.tgz#03026c11c5643f341fda42e4fb1882e2df35e6cb" @@ -15086,6 +15632,11 @@ lru-cache@^6.0.0: dependencies: yallist "^4.0.0" +lru-cache@^7.4.0: + version "7.8.0" + resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-7.8.0.tgz#649aaeb294a56297b5cbc5d70f198dcc5ebe5747" + integrity sha512-AmXqneQZL3KZMIgBpaPTeI6pfwh+xQ2vutMsyqOu1TBdEXFZgpG/80wuJ531w2ZN7TI0/oc8CPxzh/DKQudZqg== + lru_map@^0.3.3: version "0.3.3" resolved "https://registry.yarnpkg.com/lru_map/-/lru_map-0.3.3.tgz#b5c8351b9464cbd750335a79650a0ec0e56118dd" @@ -15148,7 +15699,7 @@ make-dir@^1.0.0: dependencies: pify "^3.0.0" -make-dir@^2.0.0, make-dir@^2.1.0: +make-dir@^2.0.0: version "2.1.0" resolved "https://registry.yarnpkg.com/make-dir/-/make-dir-2.1.0.tgz#5f0310e18b8be898cc07009295a30ae41e91e6f5" integrity sha512-LS9X+dc8KLxXCb8dni79fLIIUA5VyZoyjSMCwTluaXA0o27cCK0bhXkpgw+sTXVpPy/lSO57ilRixqk0vDmtRA== @@ -15679,7 +16230,7 @@ mkdirp@0.5.4: dependencies: minimist "^1.2.5" -mkdirp@0.5.x, mkdirp@0.x, mkdirp@^0.5.0, mkdirp@^0.5.1, mkdirp@^0.5.3, mkdirp@^0.5.5, mkdirp@~0.5.0, mkdirp@~0.5.1: +mkdirp@0.5.x, mkdirp@^0.5.0, mkdirp@^0.5.1, mkdirp@^0.5.3, mkdirp@^0.5.5, mkdirp@~0.5.0, mkdirp@~0.5.1: version "0.5.5" resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-0.5.5.tgz#d91cefd62d1436ca0f41620e251288d420099def" integrity sha512-NKmAlESf6jMGym1++R0Ra7wvhV+wFW63FaSOFPwRahvea0gMUcGUhVeAg/0BC0wiv9ih5NYPB1Wn1UEI1/L+xQ== @@ -16165,17 +16716,6 @@ node-modules-regexp@^1.0.0: resolved "https://registry.yarnpkg.com/node-modules-regexp/-/node-modules-regexp-1.0.0.tgz#8d9dbe28964a4ac5712e9131642107c71e90ec40" integrity sha1-jZ2+KJZKSsVxLpExZCEHxx6Q7EA= -node-notifier@^5.4.2: - version "5.4.5" - resolved "https://registry.yarnpkg.com/node-notifier/-/node-notifier-5.4.5.tgz#0cbc1a2b0f658493b4025775a13ad938e96091ef" - integrity sha512-tVbHs7DyTLtzOiN78izLA85zRqB9NvEXkAf014Vx3jtSvn/xBl6bR8ZYifj+dFcFrKI21huSQgJZ6ZtL3B4HfQ== - dependencies: - growly "^1.3.0" - is-wsl "^1.1.0" - semver "^5.5.0" - shellwords "^0.1.1" - which "^1.3.0" - node-notifier@^9.0.1: version "9.0.1" resolved "https://registry.yarnpkg.com/node-notifier/-/node-notifier-9.0.1.tgz#cea837f4c5e733936c7b9005e6545cea825d1af4" @@ -16390,7 +16930,7 @@ npm-run-path@^3.0.0: dependencies: path-key "^3.0.0" -npm-run-path@^4.0.0: +npm-run-path@^4.0.0, npm-run-path@^4.0.1: version "4.0.1" resolved "https://registry.yarnpkg.com/npm-run-path/-/npm-run-path-4.0.1.tgz#b7ecd1e5ed53da8e37a55e1c2269e0b97ed748ea" integrity sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw== @@ -16431,7 +16971,7 @@ number-is-nan@^1.0.0: resolved "https://registry.yarnpkg.com/number-is-nan/-/number-is-nan-1.0.1.tgz#097b602b53422a522c1afb8790318336941a011d" integrity sha1-CXtgK1NCKlIsGvuHkDGDNpQaAR0= -nwsapi@^2.0.7, nwsapi@^2.2.0: +nwsapi@^2.2.0: version "2.2.0" resolved "https://registry.yarnpkg.com/nwsapi/-/nwsapi-2.2.0.tgz#204879a9e3d068ff2a55139c2c772780681a38b7" integrity sha512-h2AatdwYH+JHiZpv7pt/gSX1XoRGb7L/qSIeuqA6GwYoF9w1vP1cw42TO0aI2pNyshRK5893hNSl+1//vHK7hQ== @@ -16587,7 +17127,7 @@ onetime@^2.0.0: dependencies: mimic-fn "^1.0.0" -onetime@^5.1.0: +onetime@^5.1.0, onetime@^5.1.2: version "5.1.2" resolved "https://registry.yarnpkg.com/onetime/-/onetime-5.1.2.tgz#d0e96ebb56b07476df1dd9c4806e5237985ca45e" integrity sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg== @@ -16729,13 +17269,6 @@ p-defer@^3.0.0: resolved "https://registry.yarnpkg.com/p-defer/-/p-defer-3.0.0.tgz#d1dceb4ee9b2b604b1d94ffec83760175d4e6f83" integrity sha512-ugZxsxmtTln604yeYd29EGrNhazN2lywetzpKhfmQjW/VJmhpDmWbiX+h0zL8V91R0UXkhb3KtPmyq9PZw3aYw== -p-each-series@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/p-each-series/-/p-each-series-1.0.0.tgz#930f3d12dd1f50e7434457a22cd6f04ac6ad7f71" - integrity sha1-kw89Et0fUOdDRFeiLNbwSsatf3E= - dependencies: - p-reduce "^1.0.0" - p-event@^4.1.0: version "4.2.0" resolved "https://registry.yarnpkg.com/p-event/-/p-event-4.2.0.tgz#af4b049c8acd91ae81083ebd1e6f5cae2044c1b5" @@ -16988,7 +17521,7 @@ parse-json@^4.0.0: error-ex "^1.3.1" json-parse-better-errors "^1.0.1" -parse-json@^5.0.0: +parse-json@^5.0.0, parse-json@^5.2.0: version "5.2.0" resolved "https://registry.yarnpkg.com/parse-json/-/parse-json-5.2.0.tgz#c76fc66dee54231c962b22bcc8a72cf2f99753cd" integrity sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg== @@ -17033,16 +17566,6 @@ parse-url@^5.0.0: parse-path "^4.0.0" protocols "^1.4.0" -parse5@4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/parse5/-/parse5-4.0.0.tgz#6d78656e3da8d78b4ec0b906f7c08ef1dfe3f608" - integrity sha512-VrZ7eOd3T1Fk4XWNXMgiGBK/z0MG48BWG2uQNU4I72fkQuKUTZpl+u9k+CxEG0twMVzSmXEEz12z5Fnw1jIQFA== - -parse5@5.1.0: - version "5.1.0" - resolved "https://registry.yarnpkg.com/parse5/-/parse5-5.1.0.tgz#c59341c9723f414c452975564c7c00a68d58acd2" - integrity sha512-fxNG2sQjHvlVAYmzBZS9YlDp6PTSSDwa98vkD4QgVDDCAo84z5X1t5XyJQ62ImdLXx5NdIIfihey6xpum9/gRQ== - parse5@6.0.1, parse5@^6.0.1: version "6.0.1" resolved "https://registry.yarnpkg.com/parse5/-/parse5-6.0.1.tgz#e1a1c085c569b3dc08321184f19a39cc27f7c30b" @@ -17342,6 +17865,11 @@ pirates@^4.0.1: dependencies: node-modules-regexp "^1.0.0" +pirates@^4.0.4: + version "4.0.5" + resolved "https://registry.yarnpkg.com/pirates/-/pirates-4.0.5.tgz#feec352ea5c3268fb23a37c702ab1699f35a5f3b" + integrity sha512-8V9+HQPupnaXMA23c5hvl69zXvTwTzyAYasnkb0Tts4XvO4CliqONMOnvlq26rkhLC3nWDFBJf73LU1e1VZLaQ== + pixelmatch@^5.2.1: version "5.2.1" resolved "https://registry.yarnpkg.com/pixelmatch/-/pixelmatch-5.2.1.tgz#9e4e4f4aa59648208a31310306a5bed5522b0d65" @@ -17440,11 +17968,6 @@ pluralize@^8.0.0: resolved "https://registry.yarnpkg.com/pluralize/-/pluralize-8.0.0.tgz#1a6fa16a38d12a1901e0320fa017051c539ce3b1" integrity sha512-Nc3IT5yHzflTfbjgqWcCPpo7DaKy4FnpB0l/zCAW0Tc7jxAiuqSxHasntB3D7887LSrA93kDJ9IXovxJYxyLCA== -pn@^1.1.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/pn/-/pn-1.1.0.tgz#e2f4cef0e219f463c179ab37463e4e1ecdccbafb" - integrity sha512-2qHaIQr2VLRFoxe2nASzsV6ef4yOOH+Fi9FBOVH6cqeSgUnoyySPZkxzLuzd+RYOQTRpROA0ztTMqxROKSb/nA== - pngjs@^4.0.1: version "4.0.1" resolved "https://registry.yarnpkg.com/pngjs/-/pngjs-4.0.1.tgz#f803869bb2fc1bfe1bf99aa4ec21c108117cfdbe" @@ -17910,7 +18433,7 @@ pretty-format@^24.9.0: ansi-styles "^3.2.0" react-is "^16.8.4" -pretty-format@^27.0.2: +pretty-format@^27.0.2, pretty-format@^27.5.1: version "27.5.1" resolved "https://registry.yarnpkg.com/pretty-format/-/pretty-format-27.5.1.tgz#2181879fdea51a7a5851fb39d920faa63f01d88e" integrity sha512-Qb1gy5OrP5+zDf2Bvnzdl3jsTf1qXVMazbvCoKhtKqVs4/YK4ozX4gKQJJVyNe+cajNPn0KoC0MC3FUmaHWEmQ== @@ -18896,23 +19419,7 @@ replace-in-file@^4.0.0: glob "^7.1.6" yargs "^15.0.2" -request-promise-core@1.1.4: - version "1.1.4" - resolved "https://registry.yarnpkg.com/request-promise-core/-/request-promise-core-1.1.4.tgz#3eedd4223208d419867b78ce815167d10593a22f" - integrity sha512-TTbAfBBRdWD7aNNOoVOBH4pN/KigV6LyapYNNlAPA8JwbovRti1E88m3sYAwsLi5ryhPKsE9APwnjFTgdUjTpw== - dependencies: - lodash "^4.17.19" - -request-promise-native@^1.0.5, request-promise-native@^1.0.7, request-promise-native@^1.0.9: - version "1.0.9" - resolved "https://registry.yarnpkg.com/request-promise-native/-/request-promise-native-1.0.9.tgz#e407120526a5efdc9a39b28a5679bf47b9d9dc28" - integrity sha512-wcW+sIUiWnKgNY0dqCpOZkUbF/I+YPi+f09JZIDa39Ec+q82CpSYniDp+ISgTTbKmnpJWASeJBPZmoxH84wt3g== - dependencies: - request-promise-core "1.1.4" - stealthy-require "^1.1.1" - tough-cookie "^2.3.3" - -request@^2.87.0, request@^2.88.0, request@^2.88.2: +request@^2.87.0, request@^2.88.0: version "2.88.2" resolved "https://registry.yarnpkg.com/request/-/request-2.88.2.tgz#d73c918731cb5a87da047e207234146f664d12b3" integrity sha512-MsvtOrfG9ZcrOwAW+Qi+F6HbD0CWXEh9ou77uOb7FM2WPhwT7smM833PzanhJLsgXjN89Ir6V2PczXNnMpwKhw== @@ -19003,6 +19510,13 @@ resolve-cwd@^2.0.0: dependencies: resolve-from "^3.0.0" +resolve-cwd@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/resolve-cwd/-/resolve-cwd-3.0.0.tgz#0f0075f1bb2544766cf73ba6a6e2adfebcb13f2d" + integrity sha512-OrZaX2Mb+rJCpH/6CpSqt9xFVpN++x01XnN2ie9g6P5/3xelLAkXWVADpdz1IHD/KFfEXyE6V0U01OQ3UO2rEg== + dependencies: + resolve-from "^5.0.0" + resolve-dependency-path@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/resolve-dependency-path/-/resolve-dependency-path-2.0.0.tgz#11700e340717b865d216c66cabeb4a2a3c696736" @@ -19034,6 +19548,11 @@ resolve-from@^4.0.0: resolved "https://registry.yarnpkg.com/resolve-from/-/resolve-from-4.0.0.tgz#4abcd852ad32dd7baabfe9b40e00a36db5f392e6" integrity sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g== +resolve-from@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/resolve-from/-/resolve-from-5.0.0.tgz#c35225843df8f776df21c57557bc087e9dfdfc69" + integrity sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw== + resolve-package-path@^1.0.11, resolve-package-path@^1.2.2, resolve-package-path@^1.2.6: version "1.2.7" resolved "https://registry.yarnpkg.com/resolve-package-path/-/resolve-package-path-1.2.7.tgz#2a7bc37ad96865e239330e3102c31322847e652e" @@ -19088,19 +19607,16 @@ resolve-url@^0.2.1: resolved "https://registry.yarnpkg.com/resolve-url/-/resolve-url-0.2.1.tgz#2c637fe77c893afd2a663fe21aa9080068e2052a" integrity sha1-LGN/53yJOv0qZj/iGqkIAGjiBSo= +resolve.exports@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/resolve.exports/-/resolve.exports-1.1.0.tgz#5ce842b94b05146c0e03076985d1d0e7e48c90c9" + integrity sha512-J1l+Zxxp4XK3LUDZ9m60LRJF/mAe4z6a4xyabPHk7pvK5t35dACV32iIjJDFeWZFfZlO29w6SZ67knR0tHzJtQ== + resolve@1.1.7, resolve@1.1.x: version "1.1.7" resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.1.7.tgz#203114d82ad2c5ed9e8e0411b3932875e889e97b" integrity sha1-IDEU2CrSxe2ejgQRs5ModeiJ6Xs= -resolve@1.x, resolve@^1.10.0, resolve@^1.10.1, resolve@^1.11.1, resolve@^1.12.0, resolve@^1.13.1, resolve@^1.14.2, resolve@^1.17.0, resolve@^1.19.0, resolve@^1.20.0, resolve@^1.3.3, resolve@^1.4.0, resolve@^1.5.0: - version "1.20.0" - resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.20.0.tgz#629a013fb3f70755d6f0b7935cc1c2c5378b1975" - integrity sha512-wENBPt4ySzg4ybFQW2TT1zMQucPK95HSh/nq2CFTZVOGut2+pQvSsgtda4d26YrYcr067wjbmzOG8byDPBX63A== - dependencies: - is-core-module "^2.2.0" - path-parse "^1.0.6" - resolve@^1.1.6: version "1.21.0" resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.21.0.tgz#b51adc97f3472e6a5cf4444d34bc9d6b9037591f" @@ -19110,6 +19626,14 @@ resolve@^1.1.6: path-parse "^1.0.7" supports-preserve-symlinks-flag "^1.0.0" +resolve@^1.10.0, resolve@^1.10.1, resolve@^1.11.1, resolve@^1.12.0, resolve@^1.13.1, resolve@^1.14.2, resolve@^1.17.0, resolve@^1.19.0, resolve@^1.20.0, resolve@^1.3.3, resolve@^1.4.0, resolve@^1.5.0: + version "1.20.0" + resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.20.0.tgz#629a013fb3f70755d6f0b7935cc1c2c5378b1975" + integrity sha512-wENBPt4ySzg4ybFQW2TT1zMQucPK95HSh/nq2CFTZVOGut2+pQvSsgtda4d26YrYcr067wjbmzOG8byDPBX63A== + dependencies: + is-core-module "^2.2.0" + path-parse "^1.0.6" + resolve@^2.0.0-next.3: version "2.0.0-next.3" resolved "https://registry.yarnpkg.com/resolve/-/resolve-2.0.0-next.3.tgz#d41016293d4a8586a39ca5d9b5f15cbea1f55e46" @@ -19370,18 +19894,11 @@ sax@1.2.1: resolved "https://registry.yarnpkg.com/sax/-/sax-1.2.1.tgz#7b8e656190b228e81a66aea748480d828cd2d37a" integrity sha1-e45lYZCyKOgaZq6nSEgNgozS03o= -sax@>=0.6.0, sax@^1.2.4, sax@~1.2.4: +sax@>=0.6.0, sax@~1.2.4: version "1.2.4" resolved "https://registry.yarnpkg.com/sax/-/sax-1.2.4.tgz#2816234e2378bddc4e5354fab5caa895df7100d9" integrity sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw== -saxes@^3.1.9: - version "3.1.11" - resolved "https://registry.yarnpkg.com/saxes/-/saxes-3.1.11.tgz#d59d1fd332ec92ad98a2e0b2ee644702384b1c5b" - integrity sha512-Ydydq3zC+WYDJK1+gRxRapLIED9PWeSuuS41wqyoRmzvhhh9nc+QQrVMKJYzJFULazeGhzSV0QleN2wD3boh2g== - dependencies: - xmlchars "^2.1.1" - saxes@^5.0.1: version "5.0.1" resolved "https://registry.yarnpkg.com/saxes/-/saxes-5.0.1.tgz#eebab953fa3b7608dbe94e5dadb15c888fa6696d" @@ -19432,7 +19949,7 @@ schema-utils@^3.1.0, schema-utils@^3.1.1: ajv "^6.12.5" ajv-keywords "^3.5.2" -"semver@2 || 3 || 4 || 5", "semver@2.x || 3.x || 4 || 5", semver@^5.1.0, semver@^5.3.0, semver@^5.4.1, semver@^5.5, semver@^5.5.0, semver@^5.5.1, semver@^5.6.0, semver@^5.7.0: +"semver@2 || 3 || 4 || 5", "semver@2.x || 3.x || 4 || 5", semver@^5.1.0, semver@^5.3.0, semver@^5.4.1, semver@^5.5.0, semver@^5.5.1, semver@^5.6.0, semver@^5.7.0: version "5.7.1" resolved "https://registry.yarnpkg.com/semver/-/semver-5.7.1.tgz#a954f931aeba508d307bbf069eff0c01c96116f7" integrity sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ== @@ -19449,6 +19966,13 @@ semver@7.3.4: dependencies: lru-cache "^6.0.0" +semver@7.x: + version "7.3.6" + resolved "https://registry.yarnpkg.com/semver/-/semver-7.3.6.tgz#5d73886fb9c0c6602e79440b97165c29581cbb2b" + integrity sha512-HZWqcgwLsjaX1HBD31msI/rXktuIhS+lWvdE4kN9z+8IVT4Itc7vqU2WvYsyD6/sjYCt4dEKH/m1M3dwI9CC5w== + dependencies: + lru-cache "^7.4.0" + semver@^6.0.0, semver@^6.1.0, semver@^6.1.1, semver@^6.1.2, semver@^6.2.0, semver@^6.3.0: version "6.3.0" resolved "https://registry.yarnpkg.com/semver/-/semver-6.3.0.tgz#ee0a64c8af5e8ceea67687b133761e1becbd1d3d" @@ -19648,6 +20172,11 @@ signal-exit@^3.0.0, signal-exit@^3.0.2: resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-3.0.3.tgz#a1410c2edd8f077b08b4e253c8eacfcaf057461c" integrity sha512-VUJ49FC8U1OxwZLxIbTTrDvLnf/6TDgxZcK8wxR8zs13xpx7xbG60ndBlhNrFi2EMuFRoeDoJO7wthSLq42EjA== +signal-exit@^3.0.3: + version "3.0.7" + resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-3.0.7.tgz#a9a1767f8af84155114eaabd73f99273c8f59ad9" + integrity sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ== + silent-error@^1.0.0, silent-error@^1.0.1, silent-error@^1.1.0, silent-error@^1.1.1: version "1.1.1" resolved "https://registry.yarnpkg.com/silent-error/-/silent-error-1.1.1.tgz#f72af5b0d73682a2ba1778b7e32cd8aa7c2d8662" @@ -20255,11 +20784,6 @@ static-extend@^0.1.1: resolved "https://registry.yarnpkg.com/statuses/-/statuses-1.5.0.tgz#161c7dac177659fd9811f43771fa99381478628c" integrity sha1-Fhx9rBd2Wf2YEfQ3cfqZOBR4Yow= -stealthy-require@^1.1.1: - version "1.1.1" - resolved "https://registry.yarnpkg.com/stealthy-require/-/stealthy-require-1.1.1.tgz#35b09875b4ff49f26a777e509b3090a3226bf24b" - integrity sha1-NbCYdbT/SfJqd35QmzCQoyJr8ks= - stream-browserify@3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/stream-browserify/-/stream-browserify-3.0.0.tgz#22b0a2850cdf6503e73085da1fc7b7d0c2122f2f" @@ -20366,13 +20890,13 @@ string-hash@1.1.3: resolved "https://registry.yarnpkg.com/string-hash/-/string-hash-1.1.3.tgz#e8aafc0ac1855b4666929ed7dd1275df5d6c811b" integrity sha1-6Kr8CsGFW0Zmkp7X3RJ1311sgRs= -string-length@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/string-length/-/string-length-2.0.0.tgz#d40dbb686a3ace960c1cffca562bf2c45f8363ed" - integrity sha1-1A27aGo6zpYMHP/KVivyxF+DY+0= +string-length@^4.0.1: + version "4.0.2" + resolved "https://registry.yarnpkg.com/string-length/-/string-length-4.0.2.tgz#a8a8dc7bd5c1a82b9b3c8b87e125f66871b6e57a" + integrity sha512-+l6rNN5fYHNhZZy41RXsYptCjA2Igmq4EG7kZAYFQI1E1VTXarr6ZPXBg6eq7Y6eK4FEhY6AJlyuFIb/v/S0VQ== dependencies: - astral-regex "^1.0.0" - strip-ansi "^4.0.0" + char-regex "^1.0.2" + strip-ansi "^6.0.0" string-template@~0.2.1: version "0.2.1" @@ -20707,6 +21231,14 @@ supports-color@^8.0.0: dependencies: has-flag "^4.0.0" +supports-hyperlinks@^2.0.0: + version "2.2.0" + resolved "https://registry.yarnpkg.com/supports-hyperlinks/-/supports-hyperlinks-2.2.0.tgz#4f77b42488765891774b70c79babd87f9bd594bb" + integrity sha512-6sXEzV5+I5j8Bmq9/vUphGRM/RJNT9SCURJLjwfOg51heRtguGWDzcaBlgAzKhQa0EVNpPEKzQuBwZ8S8WaCeQ== + dependencies: + has-flag "^4.0.0" + supports-color "^7.0.0" + supports-preserve-symlinks-flag@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz#6eda4bd344a3c94aea376d4cc31bc77311039e09" @@ -20736,7 +21268,7 @@ symbol-observable@^1.2.0: resolved "https://registry.yarnpkg.com/symbol-observable/-/symbol-observable-1.2.0.tgz#c22688aed4eab3cdc2dfeacbb561660560a00804" integrity sha512-e900nM8RRtGhlV36KGEU9k65K3mPb1WV70OdjfxlG2EAuM1noi/E/BaW/uMhL7bPEssK8QV57vN3esixjUvcXQ== -symbol-tree@^3.2.2, symbol-tree@^3.2.4: +symbol-tree@^3.2.4: version "3.2.4" resolved "https://registry.yarnpkg.com/symbol-tree/-/symbol-tree-3.2.4.tgz#430637d248ba77e078883951fb9aa0eed7c63fa2" integrity sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw== @@ -20897,6 +21429,14 @@ temp@~0.4.0: resolved "https://registry.yarnpkg.com/temp/-/temp-0.4.0.tgz#671ad63d57be0fe9d7294664b3fc400636678a60" integrity sha1-ZxrWPVe+D+nXKUZks/xABjZnimA= +terminal-link@^2.0.0: + version "2.1.1" + resolved "https://registry.yarnpkg.com/terminal-link/-/terminal-link-2.1.1.tgz#14a64a27ab3c0df933ea546fba55f2d078edc994" + integrity sha512-un0FmiRUQNr5PJqy9kP7c40F5BOfpGlYTrxonDChEZB7pzZxRNp/bt+ymiy9/npwXya9KH99nJ/GXFIiUkYGFQ== + dependencies: + ansi-escapes "^4.2.1" + supports-hyperlinks "^2.0.0" + terser-webpack-plugin@^1.4.3: version "1.4.5" resolved "https://registry.yarnpkg.com/terser-webpack-plugin/-/terser-webpack-plugin-1.4.5.tgz#a217aefaea330e734ffacb6120ec1fa312d6040b" @@ -20951,6 +21491,15 @@ test-exclude@^5.2.3: read-pkg-up "^4.0.0" require-main-filename "^2.0.0" +test-exclude@^6.0.0: + version "6.0.0" + resolved "https://registry.yarnpkg.com/test-exclude/-/test-exclude-6.0.0.tgz#04a8698661d805ea6fa293b6cb9e63ac044ef15e" + integrity sha512-cAGWPIyOHU6zlmg88jwm7VRyXnMN7iV68OGAbYDk/Mh/xC/pzVPlQtY6ngoIH/5/tciuhGfvESU8GrHrcxD56w== + dependencies: + "@istanbuljs/schema" "^0.1.2" + glob "^7.1.4" + minimatch "^3.0.4" + testem@^3.2.0: version "3.4.0" resolved "https://registry.yarnpkg.com/testem/-/testem-3.4.0.tgz#48ab6b98e96085eeddac1fb46337872b13e9e06c" @@ -21001,10 +21550,10 @@ text-table@^0.2.0: resolved "https://registry.yarnpkg.com/textextensions/-/textextensions-2.6.0.tgz#d7e4ab13fe54e32e08873be40d51b74229b00fc4" integrity sha512-49WtAWS+tcsy93dRt6P0P3AMD2m5PvXRhuEA0kaXos5ZLlujtYmpmFsB+QvWUSxE1ZsstmYXfQ7L40+EcQgpAQ== -throat@^4.0.0: - version "4.1.0" - resolved "https://registry.yarnpkg.com/throat/-/throat-4.1.0.tgz#89037cbc92c56ab18926e6ba4cbb200e15672a6a" - integrity sha1-iQN8vJLFarGJJua6TLsgDhVnKmo= +throat@^6.0.1: + version "6.0.1" + resolved "https://registry.yarnpkg.com/throat/-/throat-6.0.1.tgz#d514fedad95740c12c2d7fc70ea863eb51ade375" + integrity sha512-8hmiGIJMDlwjg7dlJ4yKGLK8EsYqKgPWbG3b4wjJddKNwc7N7Dpn08Df4szr/sZdMVeOstrdYSsqzX6BYbcB+w== through2@3.0.0: version "3.0.0" @@ -21199,23 +21748,6 @@ totalist@^1.0.0: resolved "https://registry.yarnpkg.com/totalist/-/totalist-1.1.0.tgz#a4d65a3e546517701e3e5c37a47a70ac97fe56df" integrity sha512-gduQwd1rOdDMGxFG1gEvhV88Oirdo2p+KjoYFU7k2g+i7n6AFFbDQ5kMPUsW0pNbfQsB/cwXvT1i4Bue0s9g5g== -tough-cookie@^2.3.3, tough-cookie@^2.3.4, tough-cookie@~2.5.0: - version "2.5.0" - resolved "https://registry.yarnpkg.com/tough-cookie/-/tough-cookie-2.5.0.tgz#cd9fb2a0aa1d5a12b473bd9fb96fa3dcff65ade2" - integrity sha512-nlLsUzgm1kfLXSXfRZMc1KLAugd4hqJHDTvc2hDIwS3mZAfMEuMbc03SujMF+GEcpaX/qboeycw6iO8JwVv2+g== - dependencies: - psl "^1.1.28" - punycode "^2.1.1" - -tough-cookie@^3.0.1: - version "3.0.1" - resolved "https://registry.yarnpkg.com/tough-cookie/-/tough-cookie-3.0.1.tgz#9df4f57e739c26930a018184887f4adb7dca73b2" - integrity sha512-yQyJ0u4pZsv9D4clxO69OEjLWYw+jbgspjTue4lTQZLfV0c5l1VmK2y1JK8E9ahdpltPOaAThPcp5nKPUgSnsg== - dependencies: - ip-regex "^2.1.0" - psl "^1.1.28" - punycode "^2.1.1" - tough-cookie@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/tough-cookie/-/tough-cookie-4.0.0.tgz#d822234eeca882f991f0f908824ad2622ddbece4" @@ -21225,6 +21757,14 @@ tough-cookie@^4.0.0: punycode "^2.1.1" universalify "^0.1.2" +tough-cookie@~2.5.0: + version "2.5.0" + resolved "https://registry.yarnpkg.com/tough-cookie/-/tough-cookie-2.5.0.tgz#cd9fb2a0aa1d5a12b473bd9fb96fa3dcff65ade2" + integrity sha512-nlLsUzgm1kfLXSXfRZMc1KLAugd4hqJHDTvc2hDIwS3mZAfMEuMbc03SujMF+GEcpaX/qboeycw6iO8JwVv2+g== + dependencies: + psl "^1.1.28" + punycode "^2.1.1" + tr46@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/tr46/-/tr46-1.0.1.tgz#a8b13fd6bfd2489519674ccde55ba3693b706d09" @@ -21239,6 +21779,13 @@ tr46@^2.0.2: dependencies: punycode "^2.1.1" +tr46@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/tr46/-/tr46-3.0.0.tgz#555c4e297a950617e8eeddef633c87d4d9d6cbf9" + integrity sha512-l7FvfAHlcmulp8kr+flpQZmVwtu7nfRV7NZujtN0OqES8EL4O4e0qqzL0DC5gAvx/ZC/9lk6rhcUwYvkBnBnYA== + dependencies: + punycode "^2.1.1" + tr46@~0.0.3: version "0.0.3" resolved "https://registry.yarnpkg.com/tr46/-/tr46-0.0.3.tgz#8184fd347dac9cdc185992f3a6622e14b9d9ab6a" @@ -21296,21 +21843,19 @@ trim-right@^1.0.1: resolved "https://registry.yarnpkg.com/trim-right/-/trim-right-1.0.1.tgz#cb2e1203067e0c8de1f614094b9fe45704ea6003" integrity sha1-yy4SAwZ+DI3h9hQJS5/kVwTqYAM= -ts-jest@^24.3.0: - version "24.3.0" - resolved "https://registry.yarnpkg.com/ts-jest/-/ts-jest-24.3.0.tgz#b97814e3eab359ea840a1ac112deae68aa440869" - integrity sha512-Hb94C/+QRIgjVZlJyiWwouYUF+siNJHJHknyspaOcZ+OQAIdFG/UrdQVXw/0B8Z3No34xkUXZJpOTy9alOWdVQ== +ts-jest@^27.1.4: + version "27.1.4" + resolved "https://registry.yarnpkg.com/ts-jest/-/ts-jest-27.1.4.tgz#84d42cf0f4e7157a52e7c64b1492c46330943e00" + integrity sha512-qjkZlVPWVctAezwsOD1OPzbZ+k7zA5z3oxII4dGdZo5ggX/PL7kvwTM0pXTr10fAtbiVpJaL3bWd502zAhpgSQ== dependencies: bs-logger "0.x" - buffer-from "1.x" fast-json-stable-stringify "2.x" + jest-util "^27.0.0" json5 "2.x" lodash.memoize "4.x" make-error "1.x" - mkdirp "0.x" - resolve "1.x" - semver "^5.5" - yargs-parser "10.x" + semver "7.x" + yargs-parser "20.x" ts-node@^8.10.2: version "8.10.2" @@ -21882,6 +22427,15 @@ v8-compile-cache@^2.0.3: resolved "https://registry.yarnpkg.com/v8-compile-cache/-/v8-compile-cache-2.3.0.tgz#2de19618c66dc247dcfb6f99338035d8245a2cee" integrity sha512-l8lCEmLcLYZh4nbunNZvQCJc5pv7+RCwa8q/LdUx8u7lsWvPDKmpodJAJNwkAhJC//dFY48KuIEmjtd4RViDrA== +v8-to-istanbul@^8.1.0: + version "8.1.1" + resolved "https://registry.yarnpkg.com/v8-to-istanbul/-/v8-to-istanbul-8.1.1.tgz#77b752fd3975e31bbcef938f85e9bd1c7a8d60ed" + integrity sha512-FGtKtv3xIpR6BYhvgH8MI/y78oT7d8Au3ww4QIxymrCtZEh5b8gCw2siywE+puhEmuWKDtmfrvF5UlB298ut3w== + dependencies: + "@types/istanbul-lib-coverage" "^2.0.1" + convert-source-map "^1.6.0" + source-map "^0.7.3" + validate-npm-package-license@^3.0.1, validate-npm-package-license@^3.0.3: version "3.0.4" resolved "https://registry.yarnpkg.com/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz#fc91f6b9c7ba15c857f4cb2c5defeec39d4f410a" @@ -21946,22 +22500,13 @@ void-elements@^2.0.0: resolved "https://registry.yarnpkg.com/void-elements/-/void-elements-2.0.1.tgz#c066afb582bb1cb4128d60ea92392e94d5e9dbec" integrity sha1-wGavtYK7HLQSjWDqkjkulNXp2+w= -w3c-hr-time@^1.0.1, w3c-hr-time@^1.0.2: +w3c-hr-time@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/w3c-hr-time/-/w3c-hr-time-1.0.2.tgz#0a89cdf5cc15822df9c360543676963e0cc308cd" integrity sha512-z8P5DvDNjKDoFIHK7q8r8lackT6l+jo/Ye3HOle7l9nICP9lf1Ci25fy9vHd0JOWewkIFzXIEig3TdKT7JQ5fQ== dependencies: browser-process-hrtime "^1.0.0" -w3c-xmlserializer@^1.1.2: - version "1.1.2" - resolved "https://registry.yarnpkg.com/w3c-xmlserializer/-/w3c-xmlserializer-1.1.2.tgz#30485ca7d70a6fd052420a3d12fd90e6339ce794" - integrity sha512-p10l/ayESzrBMYWRID6xbuCKh2Fp77+sA0doRuGn4tTIMrrZVeqfpKjXHY+oDh3K4nLdPgNwMTVP6Vp4pvqbNg== - dependencies: - domexception "^1.0.1" - webidl-conversions "^4.0.2" - xml-name-validator "^3.0.0" - w3c-xmlserializer@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/w3c-xmlserializer/-/w3c-xmlserializer-2.0.0.tgz#3e7104a05b75146cc60f564380b7f683acf1020a" @@ -21969,6 +22514,13 @@ w3c-xmlserializer@^2.0.0: dependencies: xml-name-validator "^3.0.0" +w3c-xmlserializer@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/w3c-xmlserializer/-/w3c-xmlserializer-3.0.0.tgz#06cdc3eefb7e4d0b20a560a5a3aeb0d2d9a65923" + integrity sha512-3WFqGEgSXIyGhOmAFtlicJNMjEps8b1MG31NCA0/vOF9+nKMUW1ckhi9cnNHmf88Rzw5V+dwIwsm2C7X8k9aQg== + dependencies: + xml-name-validator "^4.0.0" + wait-on@^3.3.0: version "3.3.0" resolved "https://registry.yarnpkg.com/wait-on/-/wait-on-3.3.0.tgz#9940981d047a72a9544a97b8b5fca45b2170a082" @@ -22114,6 +22666,11 @@ webidl-conversions@^6.1.0: resolved "https://registry.yarnpkg.com/webidl-conversions/-/webidl-conversions-6.1.0.tgz#9111b4d7ea80acd40f5270d666621afa78b69514" integrity sha512-qBIvFLGiBpLjfwmYAaHPXsn+ho5xZnGvyGvsarywGNc8VyQJUMHJ8OBKGGrPER0okBeMDaan4mNBlgBROxuI8w== +webidl-conversions@^7.0.0: + version "7.0.0" + resolved "https://registry.yarnpkg.com/webidl-conversions/-/webidl-conversions-7.0.0.tgz#256b4e1882be7debbf01d05f0aa2039778ea080a" + integrity sha512-VwddBukDzu71offAQR975unBIGqfKZpM+8ZX6ySk8nYhVoo5CYaZyzt3YBvYtRtO+aoGlqxPg/B87NGVZ/fu6g== + webpack-bundle-analyzer@^4.4.0: version "4.4.0" resolved "https://registry.yarnpkg.com/webpack-bundle-analyzer/-/webpack-bundle-analyzer-4.4.0.tgz#74013106e7e2b07cbd64f3a5ae847f7e814802c7" @@ -22215,23 +22772,43 @@ websocket-extensions@>=0.1.1: resolved "https://registry.yarnpkg.com/websocket-extensions/-/websocket-extensions-0.1.4.tgz#7f8473bc839dfd87608adb95d7eb075211578a42" integrity sha512-OqedPIGOfsDlo31UNwYbCFMSaO9m9G/0faIHj5/dZFDMFqPTcx6UwqyOy3COEaEOg/9VsGIpdqn62W5KhoKSpg== -whatwg-encoding@^1.0.1, whatwg-encoding@^1.0.3, whatwg-encoding@^1.0.5: +whatwg-encoding@^1.0.5: version "1.0.5" resolved "https://registry.yarnpkg.com/whatwg-encoding/-/whatwg-encoding-1.0.5.tgz#5abacf777c32166a51d085d6b4f3e7d27113ddb0" integrity sha512-b5lim54JOPN9HtzvK9HFXvBma/rnfFeqsic0hSpjtDbVxR3dJKLc+KB4V6GgiGOvl7CY/KNh8rxSo9DKQrnUEw== dependencies: iconv-lite "0.4.24" +whatwg-encoding@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/whatwg-encoding/-/whatwg-encoding-2.0.0.tgz#e7635f597fd87020858626805a2729fa7698ac53" + integrity sha512-p41ogyeMUrw3jWclHWTQg1k05DSVXPLcVxRTYsXUk+ZooOCZLcoYgPZ/HL/D/N+uQPOtcp1me1WhBEaX02mhWg== + dependencies: + iconv-lite "0.6.3" + whatwg-fetch@>=0.10.0: version "3.6.2" resolved "https://registry.yarnpkg.com/whatwg-fetch/-/whatwg-fetch-3.6.2.tgz#dced24f37f2624ed0281725d51d0e2e3fe677f8c" integrity sha512-bJlen0FcuU/0EMLrdbJ7zOnW6ITZLrZMIarMUVmdKtsGvZna8vxKYaexICWPfZ8qwf9fzNq+UEIZrnSaApt6RA== -whatwg-mimetype@^2.1.0, whatwg-mimetype@^2.2.0, whatwg-mimetype@^2.3.0: +whatwg-mimetype@^2.3.0: version "2.3.0" resolved "https://registry.yarnpkg.com/whatwg-mimetype/-/whatwg-mimetype-2.3.0.tgz#3d4b1e0312d2079879f826aff18dbeeca5960fbf" integrity sha512-M4yMwr6mAnQz76TbJm914+gPpB/nCwvZbJU28cUD6dR004SAxDLOOSUaB1JDRqLtaOV/vi0IC5lEAGFgrjGv/g== +whatwg-mimetype@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/whatwg-mimetype/-/whatwg-mimetype-3.0.0.tgz#5fa1a7623867ff1af6ca3dc72ad6b8a4208beba7" + integrity sha512-nt+N2dzIutVRxARx1nghPKGv1xHikU7HKdfafKkLNLindmPU/ch3U31NOCGGA/dmPcmb1VlofO0vnKAcsm0o/Q== + +whatwg-url@^10.0.0: + version "10.0.0" + resolved "https://registry.yarnpkg.com/whatwg-url/-/whatwg-url-10.0.0.tgz#37264f720b575b4a311bd4094ed8c760caaa05da" + integrity sha512-CLxxCmdUby142H5FZzn4D8ikO1cmypvXVQktsgosNy4a4BHrDHeciBBGZhb0bNoR5/MltoCatso+vFjjGx8t0w== + dependencies: + tr46 "^3.0.0" + webidl-conversions "^7.0.0" + whatwg-url@^5.0.0: version "5.0.0" resolved "https://registry.yarnpkg.com/whatwg-url/-/whatwg-url-5.0.0.tgz#966454e8765462e37644d3626f6742ce8b70965d" @@ -22240,15 +22817,6 @@ whatwg-url@^5.0.0: tr46 "~0.0.3" webidl-conversions "^3.0.0" -whatwg-url@^6.4.1: - version "6.5.0" - resolved "https://registry.yarnpkg.com/whatwg-url/-/whatwg-url-6.5.0.tgz#f2df02bff176fd65070df74ad5ccbb5a199965a8" - integrity sha512-rhRZRqx/TLJQWUpQ6bmrt2UV4f0HCQ463yQuONJqC6fO2VoEb1pTYddbe59SkYq87aoM5A3bdhMZiUiVws+fzQ== - dependencies: - lodash.sortby "^4.7.0" - tr46 "^1.0.1" - webidl-conversions "^4.0.2" - whatwg-url@^7.0.0: version "7.1.0" resolved "https://registry.yarnpkg.com/whatwg-url/-/whatwg-url-7.1.0.tgz#c2c492f1eca612988efd3d2266be1b9fc6170d06" @@ -22296,7 +22864,7 @@ which-typed-array@^1.1.2: has-symbols "^1.0.1" is-typed-array "^1.1.3" -which@1, which@1.3.1, which@^1.1.1, which@^1.2.1, which@^1.2.12, which@^1.2.14, which@^1.2.9, which@^1.3.0, which@^1.3.1: +which@1, which@1.3.1, which@^1.1.1, which@^1.2.1, which@^1.2.12, which@^1.2.14, which@^1.2.9, which@^1.3.1: version "1.3.1" resolved "https://registry.yarnpkg.com/which/-/which-1.3.1.tgz#a45043d54f5805316da8d62f9f50918d3da70b0a" integrity sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ== @@ -22460,14 +23028,7 @@ write-pkg@^3.1.0: sort-keys "^2.0.0" write-json-file "^2.2.0" -ws@^5.2.0: - version "5.2.3" - resolved "https://registry.yarnpkg.com/ws/-/ws-5.2.3.tgz#05541053414921bc29c63bee14b8b0dd50b07b3d" - integrity sha512-jZArVERrMsKUatIdnLzqvcfydI85dvd/Fp1u/VOpfdDWQ4c9qWXe+VIeAbQ5FrDwciAkr+lzofXLz3Kuf26AOA== - dependencies: - async-limiter "~1.0.0" - -ws@^7.0.0, ws@^7.2.3, ws@^7.3.1, ws@^7.4.4, ws@~7.4.2: +ws@^7.2.3, ws@^7.3.1, ws@~7.4.2: version "7.4.4" resolved "https://registry.yarnpkg.com/ws/-/ws-7.4.4.tgz#383bc9742cb202292c9077ceab6f6047b17f2d59" integrity sha512-Qm8k8ojNQIMx7S+Zp8u/uHOx7Qazv3Yv4q68MiWWWOJhiwG5W3x7iqmRtJo8xxrciZUY4vRxUTJCKuRnF28ZZw== @@ -22477,6 +23038,11 @@ ws@^7.4.6: resolved "https://registry.yarnpkg.com/ws/-/ws-7.5.6.tgz#e59fc509fb15ddfb65487ee9765c5a51dec5fe7b" integrity sha512-6GLgCqo2cy2A2rjCNFlxQS6ZljG/coZfZXclldI8FB/1G3CCI36Zd8xy2HrFVACi8tfk5XrgLQEk+P0Tnz9UcA== +ws@^8.2.3: + version "8.5.0" + resolved "https://registry.yarnpkg.com/ws/-/ws-8.5.0.tgz#bfb4be96600757fe5382de12c670dab984a1ed4f" + integrity sha512-BWX0SWVgLPzYwF8lTzEy1egjhS4S4OEAHfsO8o65WOVsrnSRGaSiUaa9e0ggGlkMTtBlmOpEXiie9RUcBO86qg== + ws@~8.2.3: version "8.2.3" resolved "https://registry.yarnpkg.com/ws/-/ws-8.2.3.tgz#63a56456db1b04367d0b721a0b80cae6d8becbba" @@ -22492,6 +23058,11 @@ xml-name-validator@^3.0.0: resolved "https://registry.yarnpkg.com/xml-name-validator/-/xml-name-validator-3.0.0.tgz#6ae73e06de4d8c6e47f9fb181f78d648ad457c6a" integrity sha512-A5CUptxDsvxKJEU3yO6DuWBSJz/qizqzJKOMIfUJHETbBw/sFaDxgd6fxm1ewUaM0jZ444Fc5vC5ROYurg/4Pw== +xml-name-validator@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/xml-name-validator/-/xml-name-validator-4.0.0.tgz#79a006e2e63149a8600f15430f0a4725d1524835" + integrity sha512-ICP2e+jsHvAj2E2lIHxa5tjXRlKDJo4IdvPvCXbXQGdzSfmSpNVyIKMvoZHjDY9DP0zV17iI85o90vRFXNccRw== + xml2js@0.4.19: version "0.4.19" resolved "https://registry.yarnpkg.com/xml2js/-/xml2js-0.4.19.tgz#686c20f213209e94abf0d1bcf1efaa291c7827a7" @@ -22505,7 +23076,7 @@ xmlbuilder@~9.0.1: resolved "https://registry.yarnpkg.com/xmlbuilder/-/xmlbuilder-9.0.7.tgz#132ee63d2ec5565c557e20f4c22df9aca686b10d" integrity sha1-Ey7mPS7FVlxVfiD0wi35rKaGsQ0= -xmlchars@^2.1.1, xmlchars@^2.2.0: +xmlchars@^2.2.0: version "2.2.0" resolved "https://registry.yarnpkg.com/xmlchars/-/xmlchars-2.2.0.tgz#060fe1bcb7f9c76fe2a17db86a9bc3ab894210cb" integrity sha512-JZnDKK8B0RCDw84FNdDAIpZK+JuJw+s7Lz8nksI7SIuU3UXJJslUthsi+uWBUYOwPFwW7W7PRLRfUKpxjtjFCw== @@ -22553,13 +23124,6 @@ yam@^1.0.0: fs-extra "^4.0.2" lodash.merge "^4.6.0" -yargs-parser@10.x: - version "10.1.0" - resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-10.1.0.tgz#7202265b89f7e9e9f2e5765e0fe735a905edbaa8" - integrity sha512-VCIyR1wJoEBZUqk5PA+oOBF6ypbwh5aNB3I50guxAL/quggdfs4TtNHQrSazFA3fYZ+tEqfs0zIGlv0c/rgjbQ== - dependencies: - camelcase "^4.1.0" - yargs-parser@13.1.2, yargs-parser@^13.1.2: version "13.1.2" resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-13.1.2.tgz#130f09702ebaeef2650d54ce6e3e5706f7a4fb38" @@ -22568,6 +23132,11 @@ yargs-parser@13.1.2, yargs-parser@^13.1.2: camelcase "^5.0.0" decamelize "^1.2.0" +yargs-parser@20.x, yargs-parser@^20.2.2: + version "20.2.9" + resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-20.2.9.tgz#2eb7dc3b0289718fc295f362753845c41a0c94ee" + integrity sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w== + yargs-parser@^11.1.1: version "11.1.1" resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-11.1.1.tgz#879a0865973bca9f6bab5cbdf3b1c67ec7d3bcf4" @@ -22584,11 +23153,6 @@ yargs-parser@^18.1.2: camelcase "^5.0.0" decamelize "^1.2.0" -yargs-parser@^20.2.2: - version "20.2.9" - resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-20.2.9.tgz#2eb7dc3b0289718fc295f362753845c41a0c94ee" - integrity sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w== - yargs-parser@^20.2.3: version "20.2.7" resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-20.2.7.tgz#61df85c113edfb5a7a4e36eb8aa60ef423cbc90a" @@ -22654,7 +23218,7 @@ yargs@^15.0.2, yargs@^15.4.1: y18n "^4.0.0" yargs-parser "^18.1.2" -yargs@^16.1.1: +yargs@^16.1.1, yargs@^16.2.0: version "16.2.0" resolved "https://registry.yarnpkg.com/yargs/-/yargs-16.2.0.tgz#1c82bf0f6b6a66eafce7ef30e376f49a12477f66" integrity sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw== From 05292b976b1de5c6d14e0d4a2b53f7ce8d554ab9 Mon Sep 17 00:00:00 2001 From: Katie Byers Date: Mon, 11 Apr 2022 17:03:23 -0700 Subject: [PATCH 031/204] fix(dev): Fix unit test debugging configuration (#4915) In https://github.com/getsentry/sentry-javascript/pull/4907, all of our jest config was pulled out of `package.json` and put into `jest.config.js` files. This fixes our VSCode debug profile for unit tests to point to the new config. --- .vscode/launch.json | 3 --- 1 file changed, 3 deletions(-) diff --git a/.vscode/launch.json b/.vscode/launch.json index bef12bd786af..2a1d7141b53c 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -47,9 +47,6 @@ // this runs one test at a time, rather than running them in parallel (necessary for debugging so that you know // you're hitting a single test's breakpoints, in order) "--runInBand", - // TODO: when we unify jest config, we may need to change this - "--config", - "${workspaceFolder}/packages/${input:getPackageName}/package.json", // coverage messes up the source maps "--coverage", "false", From a9cc619436354b6c292e9e139ace97958c493639 Mon Sep 17 00:00:00 2001 From: Lukas Stracke Date: Tue, 12 Apr 2022 10:12:48 +0200 Subject: [PATCH 032/204] ref: Port functionality from `Backend` to `Client` (#4911) port all the functionality from the Backend classes to the Client classes. This includes: * Backend (interface) * BaseBackend * BrowserBackend * NodeBackend * TestBackend Additionally, fix the unit and integration tests in Core to spy on TestClient instead of TestBackend. --- packages/browser/src/backend.ts | 2 + packages/browser/src/client.ts | 60 ++++++++- packages/browser/src/exports.ts | 1 + packages/browser/test/unit/backend.test.ts | 2 + packages/browser/test/unit/index.test.ts | 6 +- .../unit/integrations/linkederrors.test.ts | 3 + packages/core/src/baseclient.ts | 106 ++++++++++++++-- packages/core/src/index.ts | 1 + packages/core/test/lib/base.test.ts | 116 ++++++++++-------- packages/core/test/mocks/backend.ts | 9 +- packages/core/test/mocks/client.ts | 68 +++++++++- packages/node/src/client.ts | 67 +++++++++- packages/node/src/index.ts | 1 + packages/node/test/index.test.ts | 11 +- .../test/integrations/linkederrors.test.ts | 5 + packages/types/src/client.ts | 20 ++- 16 files changed, 394 insertions(+), 84 deletions(-) diff --git a/packages/browser/src/backend.ts b/packages/browser/src/backend.ts index 89cf843d48ae..2543c6ae99ad 100644 --- a/packages/browser/src/backend.ts +++ b/packages/browser/src/backend.ts @@ -8,6 +8,8 @@ import { FetchTransport, makeNewFetchTransport, makeNewXHRTransport, XHRTranspor /** * Configuration options for the Sentry Browser SDK. * @see BrowserClient for more information. + * + * TODO(v7): move to client */ export interface BrowserOptions extends Options { /** diff --git a/packages/browser/src/client.ts b/packages/browser/src/client.ts index 75aab49db77b..b64997c0a23b 100644 --- a/packages/browser/src/client.ts +++ b/packages/browser/src/client.ts @@ -1,17 +1,20 @@ -import { BaseClient, Scope, SDK_VERSION } from '@sentry/core'; -import { Event, EventHint } from '@sentry/types'; -import { getGlobalObject, logger } from '@sentry/utils'; +import { BaseClient, getEnvelopeEndpointWithUrlEncodedAuth, initAPIDetails, Scope, SDK_VERSION } from '@sentry/core'; +import { Event, EventHint, Severity, Transport, TransportOptions } from '@sentry/types'; +import { getGlobalObject, logger, supportsFetch } from '@sentry/utils'; import { BrowserBackend, BrowserOptions } from './backend'; +import { eventFromException, eventFromMessage } from './eventbuilder'; import { IS_DEBUG_BUILD } from './flags'; import { injectReportDialog, ReportDialogOptions } from './helpers'; import { Breadcrumbs } from './integrations'; +import { FetchTransport, makeNewFetchTransport, makeNewXHRTransport, XHRTransport } from './transports'; /** * The Sentry Browser SDK Client. * * @see BrowserOptions for documentation on configuration options. * @see SentryClient for usage documentation. + * TODO(v7): remove BrowserBackend */ export class BrowserClient extends BaseClient { /** @@ -32,6 +35,7 @@ export class BrowserClient extends BaseClient { version: SDK_VERSION, }; + // TODO(v7): remove BrowserBackend param super(BrowserBackend, options); } @@ -58,6 +62,20 @@ export class BrowserClient extends BaseClient { }); } + /** + * @inheritDoc + */ + public eventFromException(exception: unknown, hint?: EventHint): PromiseLike { + return eventFromException(exception, hint, this._options.attachStacktrace); + } + + /** + * @inheritDoc + */ + public eventFromMessage(message: string, level: Severity = Severity.Info, hint?: EventHint): PromiseLike { + return eventFromMessage(message, level, hint, this._options.attachStacktrace); + } + /** * @inheritDoc */ @@ -76,4 +94,40 @@ export class BrowserClient extends BaseClient { } super._sendEvent(event); } + + /** + * @inheritDoc + */ + protected _setupTransport(): Transport { + if (!this._options.dsn) { + // We return the noop transport here in case there is no Dsn. + return super._setupTransport(); + } + + const transportOptions: TransportOptions = { + ...this._options.transportOptions, + dsn: this._options.dsn, + tunnel: this._options.tunnel, + sendClientReports: this._options.sendClientReports, + _metadata: this._options._metadata, + }; + + const api = initAPIDetails(transportOptions.dsn, transportOptions._metadata, transportOptions.tunnel); + const url = getEnvelopeEndpointWithUrlEncodedAuth(api.dsn, api.tunnel); + + if (this._options.transport) { + return new this._options.transport(transportOptions); + } + if (supportsFetch()) { + const requestOptions: RequestInit = { ...transportOptions.fetchParameters }; + this._newTransport = makeNewFetchTransport({ requestOptions, url }); + return new FetchTransport(transportOptions); + } + + this._newTransport = makeNewXHRTransport({ + url, + headers: transportOptions.headers, + }); + return new XHRTransport(transportOptions); + } } diff --git a/packages/browser/src/exports.ts b/packages/browser/src/exports.ts index b4dd4374a7b3..2a62e8e46fbe 100644 --- a/packages/browser/src/exports.ts +++ b/packages/browser/src/exports.ts @@ -41,6 +41,7 @@ export { withScope, } from '@sentry/core'; +// TODO(v7): refactor to use client here! export { BrowserOptions } from './backend'; export { BrowserClient } from './client'; export { injectReportDialog, ReportDialogOptions } from './helpers'; diff --git a/packages/browser/test/unit/backend.test.ts b/packages/browser/test/unit/backend.test.ts index 71ffec4d75b1..83f95307a8c7 100644 --- a/packages/browser/test/unit/backend.test.ts +++ b/packages/browser/test/unit/backend.test.ts @@ -2,6 +2,8 @@ import { BrowserBackend } from '../../src/backend'; let backend: BrowserBackend; +// TODO(v7): remove when deleting Backend + describe('BrowserBackend', () => { describe('sendEvent()', () => { it('should use NoopTransport', () => { diff --git a/packages/browser/test/unit/index.test.ts b/packages/browser/test/unit/index.test.ts index 83c0053d3b66..707354fe7d1d 100644 --- a/packages/browser/test/unit/index.test.ts +++ b/packages/browser/test/unit/index.test.ts @@ -246,7 +246,7 @@ describe('SentryBrowser initialization', () => { it('should set SDK data when Sentry.init() is called', () => { init({ dsn }); - const sdkData = (getCurrentHub().getClient() as any)._backend._transport._api.metadata?.sdk; + const sdkData = (getCurrentHub().getClient() as any).getTransport()._api.metadata?.sdk; expect(sdkData.name).toBe('sentry.javascript.browser'); expect(sdkData.packages[0].name).toBe('npm:@sentry/browser'); @@ -257,7 +257,7 @@ describe('SentryBrowser initialization', () => { it('should set SDK data when instantiating a client directly', () => { const client = new BrowserClient({ dsn }); - const sdkData = (client as any)._backend._transport._api.metadata?.sdk; + const sdkData = (client as any).getTransport()._api.metadata?.sdk; expect(sdkData.name).toBe('sentry.javascript.browser'); expect(sdkData.packages[0].name).toBe('npm:@sentry/browser'); @@ -285,7 +285,7 @@ describe('SentryBrowser initialization', () => { }, }); - const sdkData = (getCurrentHub().getClient() as any)._backend._transport._api.metadata?.sdk; + const sdkData = (getCurrentHub().getClient() as any).getTransport()._api.metadata?.sdk; expect(sdkData.name).toBe('sentry.javascript.angular'); expect(sdkData.packages[0].name).toBe('npm:@sentry/angular'); diff --git a/packages/browser/test/unit/integrations/linkederrors.test.ts b/packages/browser/test/unit/integrations/linkederrors.test.ts index 1531aa6f77ed..35f54f4b2d87 100644 --- a/packages/browser/test/unit/integrations/linkederrors.test.ts +++ b/packages/browser/test/unit/integrations/linkederrors.test.ts @@ -34,6 +34,7 @@ describe('LinkedErrors', () => { one.cause = two; const originalException = one; + // TODO(v7): refactor to use client here! const backend = new BrowserBackend({}); return backend.eventFromException(originalException).then(event => { const result = LinkedErrorsModule._handler('cause', 5, event, { @@ -65,6 +66,7 @@ describe('LinkedErrors', () => { const originalException = one; const backend = new BrowserBackend({}); + // TODO(v7): refactor to use client here! return backend.eventFromException(originalException).then(event => { const result = LinkedErrorsModule._handler('reason', 5, event, { originalException, @@ -92,6 +94,7 @@ describe('LinkedErrors', () => { const backend = new BrowserBackend({}); const originalException = one; + // TODO(v7): refactor to use client here! return backend.eventFromException(originalException).then(event => { const result = LinkedErrorsModule._handler('cause', 2, event, { originalException, diff --git a/packages/core/src/baseclient.ts b/packages/core/src/baseclient.ts index b7fef7f2164f..59145508ca9d 100644 --- a/packages/core/src/baseclient.ts +++ b/packages/core/src/baseclient.ts @@ -28,15 +28,21 @@ import { uuid4, } from '@sentry/utils'; +import { initAPIDetails } from './api'; import { Backend, BackendClass } from './basebackend'; import { IS_DEBUG_BUILD } from './flags'; import { IntegrationIndex, setupIntegrations } from './integration'; +import { createEventEnvelope, createSessionEnvelope } from './request'; +import { NewTransport } from './transports/base'; +import { NoopTransport } from './transports/noop'; const ALREADY_SEEN_ERROR = "Not capturing exception because it's already been captured."; /** * Base implementation for all JavaScript SDK clients. * + * TODO(v7): refactor doc w.r.t. Backend + * * Call the constructor with the corresponding backend constructor and options * specific to the client subclass. To access these options later, use * {@link Client.getOptions}. Also, the Backend instance is available via @@ -71,6 +77,7 @@ export abstract class BaseClient implement * The backend used to physically interact in the environment. Usually, this * will correspond to the client. When composing SDKs, however, the Backend * from the root SDK will be used. + * TODO(v7): DELETE */ protected readonly _backend: B; @@ -86,6 +93,12 @@ export abstract class BaseClient implement /** Number of calls being processed */ protected _numProcessing: number = 0; + /** Cached transport used internally. */ + protected _transport: Transport; + + /** New v7 Transport that is initialized alongside the old one */ + protected _newTransport?: NewTransport; + /** * Initializes this client instance. * @@ -93,12 +106,17 @@ export abstract class BaseClient implement * @param options Options for the client. */ protected constructor(backendClass: BackendClass, options: O) { + // TODO(v7): Delete this._backend = new backendClass(options); this._options = options; if (options.dsn) { this._dsn = makeDsn(options.dsn); + } else { + IS_DEBUG_BUILD && logger.warn('No DSN provided, client will not do anything.'); } + + this._transport = this._setupTransport(); } /** @@ -115,8 +133,7 @@ export abstract class BaseClient implement let eventId: string | undefined = hint && hint.event_id; this._process( - this._getBackend() - .eventFromException(exception, hint) + this.eventFromException(exception, hint) .then(event => this._captureEvent(event, hint, scope)) .then(result => { eventId = result; @@ -133,8 +150,8 @@ export abstract class BaseClient implement let eventId: string | undefined = hint && hint.event_id; const promisedEvent = isPrimitive(message) - ? this._getBackend().eventFromMessage(String(message), level, hint) - : this._getBackend().eventFromException(message, hint); + ? this.eventFromMessage(String(message), level, hint) + : this.eventFromException(message, hint); this._process( promisedEvent @@ -204,7 +221,7 @@ export abstract class BaseClient implement * @inheritDoc */ public getTransport(): Transport { - return this._getBackend().getTransport(); + return this._transport; } /** @@ -249,6 +266,57 @@ export abstract class BaseClient implement } } + /** + * @inheritDoc + */ + public sendEvent(event: Event): void { + // TODO(v7): Remove the if-else + if ( + this._newTransport && + this._options.dsn && + this._options._experiments && + this._options._experiments.newTransport + ) { + const api = initAPIDetails(this._options.dsn, this._options._metadata, this._options.tunnel); + const env = createEventEnvelope(event, api); + void this._newTransport.send(env).then(null, reason => { + IS_DEBUG_BUILD && logger.error('Error while sending event:', reason); + }); + } else { + void this._transport.sendEvent(event).then(null, reason => { + IS_DEBUG_BUILD && logger.error('Error while sending event:', reason); + }); + } + } + + /** + * @inheritDoc + */ + public sendSession(session: Session): void { + if (!this._transport.sendSession) { + IS_DEBUG_BUILD && logger.warn("Dropping session because custom transport doesn't implement sendSession"); + return; + } + + // TODO(v7): Remove the if-else + if ( + this._newTransport && + this._options.dsn && + this._options._experiments && + this._options._experiments.newTransport + ) { + const api = initAPIDetails(this._options.dsn, this._options._metadata, this._options.tunnel); + const [env] = createSessionEnvelope(session, api); + void this._newTransport.send(env).then(null, reason => { + IS_DEBUG_BUILD && logger.error('Error while sending session:', reason); + }); + } else { + void this._transport.sendSession(session).then(null, reason => { + IS_DEBUG_BUILD && logger.error('Error while sending session:', reason); + }); + } + } + /** Updates existing session based on the provided event */ protected _updateSessionFromEvent(session: Session, event: Event): void { let crashed = false; @@ -283,8 +351,9 @@ export abstract class BaseClient implement } /** Deliver captured session to Sentry */ + // TODO(v7): should this be deleted? protected _sendSession(session: Session): void { - this._getBackend().sendSession(session); + this.sendSession(session); } /** @@ -317,7 +386,9 @@ export abstract class BaseClient implement }); } - /** Returns the current backend. */ + /** Returns the current backend. + * TODO(v7): DELETE + */ protected _getBackend(): B { return this._backend; } @@ -490,8 +561,9 @@ export abstract class BaseClient implement * Tells the backend to send this event * @param event The Sentry event to send */ + // TODO(v7): refactor: get rid of method? protected _sendEvent(event: Event): void { - this._getBackend().sendEvent(event); + this.sendEvent(event); } /** @@ -618,6 +690,24 @@ export abstract class BaseClient implement }, ); } + + /** + * Sets up the transport so it can be used later to send requests. + */ + protected _setupTransport(): Transport { + return new NoopTransport(); + } + + /** + * @inheritDoc + */ + // eslint-disable-next-line @typescript-eslint/no-explicit-any, @typescript-eslint/explicit-module-boundary-types + public abstract eventFromException(_exception: any, _hint?: EventHint): PromiseLike; + + /** + * @inheritDoc + */ + public abstract eventFromMessage(_message: string, _level?: Severity, _hint?: EventHint): PromiseLike; } /** diff --git a/packages/core/src/index.ts b/packages/core/src/index.ts index b066724ce099..77018c2b4437 100644 --- a/packages/core/src/index.ts +++ b/packages/core/src/index.ts @@ -23,6 +23,7 @@ export { getReportDialogEndpoint, } from './api'; export { BaseClient } from './baseclient'; +// TODO(v7): Delete! export { BackendClass, BaseBackend } from './basebackend'; export { eventToSentryRequest, sessionToSentryRequest } from './request'; export { initAndBind, ClientClass } from './sdk'; diff --git a/packages/core/test/lib/base.test.ts b/packages/core/test/lib/base.test.ts index 5c978b0ee530..8a26bd51f116 100644 --- a/packages/core/test/lib/base.test.ts +++ b/packages/core/test/lib/base.test.ts @@ -3,7 +3,7 @@ import { Event, Span, Transport } from '@sentry/types'; import { dsnToString, logger, SentryError, SyncPromise } from '@sentry/utils'; import * as integrationModule from '../../src/integration'; -import { TestBackend } from '../mocks/backend'; +import { NoopTransport } from '../../src/transports/noop'; import { TestClient } from '../mocks/client'; import { TestIntegration } from '../mocks/integration'; import { FakeTransport } from '../mocks/transport'; @@ -12,7 +12,7 @@ const PUBLIC_DSN = 'https://username@domain/123'; // eslint-disable-next-line no-var declare var global: any; -const backendEventFromException = jest.spyOn(TestBackend.prototype, 'eventFromException'); +const clientEventFromException = jest.spyOn(TestClient.prototype, 'eventFromException'); const clientProcess = jest.spyOn(TestClient.prototype as any, '_process'); jest.mock('@sentry/utils', () => { @@ -55,8 +55,8 @@ jest.mock('@sentry/utils', () => { describe('BaseClient', () => { beforeEach(() => { - TestBackend.sendEventCalled = undefined; - TestBackend.instance = undefined; + TestClient.sendEventCalled = undefined; + TestClient.instance = undefined; }); afterEach(() => { @@ -98,14 +98,24 @@ describe('BaseClient', () => { }); describe('getTransport()', () => { - test('returns the transport from backend', () => { + test('returns the transport from client', () => { expect.assertions(2); const options = { dsn: PUBLIC_DSN, transport: FakeTransport }; const client = new TestClient(options); expect(client.getTransport()).toBeInstanceOf(FakeTransport); - expect(TestBackend.instance!.getTransport()).toBe(client.getTransport()); + expect(TestClient.instance!.getTransport()).toBe(client.getTransport()); + }); + + test('retruns NoopTransport when no transport is passed', () => { + expect.assertions(2); + + const options = { dsn: PUBLIC_DSN }; + const client = new TestClient(options); + + expect(client.getTransport()).toBeInstanceOf(NoopTransport); + expect(TestClient.instance!.getTransport()).toBe(client.getTransport()); }); }); @@ -223,7 +233,7 @@ describe('BaseClient', () => { client.captureException(new Error('test exception')); - expect(TestBackend.instance!.event).toEqual( + expect(TestClient.instance!.event).toEqual( expect.objectContaining({ environment: 'production', event_id: '42', @@ -257,7 +267,7 @@ describe('BaseClient', () => { scope, ); - expect(TestBackend.instance!.event).toEqual( + expect(TestClient.instance!.event).toEqual( expect.objectContaining({ extra: { bar: 'wat', @@ -284,7 +294,7 @@ describe('BaseClient', () => { scope, ); - expect(TestBackend.instance!.event).toEqual( + expect(TestClient.instance!.event).toEqual( expect.objectContaining({ extra: { bar: 'wat', @@ -309,12 +319,12 @@ describe('BaseClient', () => { client.captureException(thrown); expect(thrown.__sentry_captured__).toBe(true); - expect(backendEventFromException).toHaveBeenCalledTimes(1); + expect(clientEventFromException).toHaveBeenCalledTimes(1); client.captureException(thrown); // `captureException` should bail right away this second time around and not get as far as calling this again - expect(backendEventFromException).toHaveBeenCalledTimes(1); + expect(clientEventFromException).toHaveBeenCalledTimes(1); }); }); @@ -324,7 +334,7 @@ describe('BaseClient', () => { client.captureMessage('test message'); - expect(TestBackend.instance!.event).toEqual( + expect(TestClient.instance!.event).toEqual( expect.objectContaining({ environment: 'production', event_id: '42', @@ -337,7 +347,7 @@ describe('BaseClient', () => { test('should call eventFromException if input to captureMessage is not a primitive', () => { const client = new TestClient({ dsn: PUBLIC_DSN }); - const spy = jest.spyOn(TestBackend.instance!, 'eventFromException'); + const spy = jest.spyOn(TestClient.instance!, 'eventFromException'); client.captureMessage('foo'); client.captureMessage(null as any); @@ -371,7 +381,7 @@ describe('BaseClient', () => { scope, ); - expect(TestBackend.instance!.event).toEqual( + expect(TestClient.instance!.event).toEqual( expect.objectContaining({ extra: { bar: 'wat', @@ -392,7 +402,7 @@ describe('BaseClient', () => { client.captureEvent({}, undefined, scope); - expect(TestBackend.instance!.event).toBeUndefined(); + expect(TestClient.instance!.event).toBeUndefined(); }); test('skips without a Dsn', () => { @@ -403,7 +413,7 @@ describe('BaseClient', () => { client.captureEvent({}, undefined, scope); - expect(TestBackend.instance!.event).toBeUndefined(); + expect(TestClient.instance!.event).toBeUndefined(); }); test.each([ @@ -443,8 +453,8 @@ describe('BaseClient', () => { client.captureEvent({ message: 'message' }, undefined, scope); - expect(TestBackend.instance!.event!.message).toBe('message'); - expect(TestBackend.instance!.event).toEqual( + expect(TestClient.instance!.event!.message).toBe('message'); + expect(TestClient.instance!.event).toEqual( expect.objectContaining({ environment: 'production', event_id: '42', @@ -462,8 +472,8 @@ describe('BaseClient', () => { client.captureEvent({ message: 'message', timestamp: 1234 }, undefined, scope); - expect(TestBackend.instance!.event!.message).toBe('message'); - expect(TestBackend.instance!.event).toEqual( + expect(TestClient.instance!.event!.message).toBe('message'); + expect(TestClient.instance!.event).toEqual( expect.objectContaining({ environment: 'production', event_id: '42', @@ -481,7 +491,7 @@ describe('BaseClient', () => { client.captureEvent({ message: 'message' }, { event_id: 'wat' }, scope); - expect(TestBackend.instance!.event!).toEqual( + expect(TestClient.instance!.event!).toEqual( expect.objectContaining({ environment: 'production', event_id: 'wat', @@ -501,7 +511,7 @@ describe('BaseClient', () => { client.captureEvent({ message: 'message' }, undefined, scope); - expect(TestBackend.instance!.event!).toEqual( + expect(TestClient.instance!.event!).toEqual( expect.objectContaining({ environment: 'production', event_id: '42', @@ -522,7 +532,7 @@ describe('BaseClient', () => { client.captureEvent({ message: 'message' }, undefined, scope); - expect(TestBackend.instance!.event!).toEqual( + expect(TestClient.instance!.event!).toEqual( expect.objectContaining({ environment: 'env', event_id: '42', @@ -543,7 +553,7 @@ describe('BaseClient', () => { client.captureEvent({ message: 'message' }, undefined, scope); - expect(TestBackend.instance!.event!).toEqual( + expect(TestClient.instance!.event!).toEqual( expect.objectContaining({ environment: undefined, event_id: '42', @@ -564,7 +574,7 @@ describe('BaseClient', () => { client.captureEvent({ message: 'message' }, undefined, scope); - expect(TestBackend.instance!.event!).toEqual( + expect(TestClient.instance!.event!).toEqual( expect.objectContaining({ environment: 'production', event_id: '42', @@ -584,10 +594,10 @@ describe('BaseClient', () => { client.captureEvent({ message: 'message' }, undefined, scope); - expect(TestBackend.instance!.event!).toHaveProperty('event_id', '42'); - expect(TestBackend.instance!.event!).toHaveProperty('message', 'message'); - expect(TestBackend.instance!.event!).toHaveProperty('breadcrumbs'); - expect(TestBackend.instance!.event!.breadcrumbs![0]).toHaveProperty('message', 'breadcrumb'); + expect(TestClient.instance!.event!).toHaveProperty('event_id', '42'); + expect(TestClient.instance!.event!).toHaveProperty('message', 'message'); + expect(TestClient.instance!.event!).toHaveProperty('breadcrumbs'); + expect(TestClient.instance!.event!.breadcrumbs![0]).toHaveProperty('message', 'breadcrumb'); }); test('limits previously saved breadcrumbs', () => { @@ -601,8 +611,8 @@ describe('BaseClient', () => { client.captureEvent({ message: 'message' }, undefined, scope); - expect(TestBackend.instance!.event!.breadcrumbs).toHaveLength(1); - expect(TestBackend.instance!.event!.breadcrumbs![0].message).toEqual('2'); + expect(TestClient.instance!.event!.breadcrumbs).toHaveLength(1); + expect(TestClient.instance!.event!.breadcrumbs![0].message).toEqual('2'); }); test('adds context data', () => { @@ -616,7 +626,7 @@ describe('BaseClient', () => { client.captureEvent({ message: 'message' }, undefined, scope); - expect(TestBackend.instance!.event!).toEqual( + expect(TestClient.instance!.event!).toEqual( expect.objectContaining({ environment: 'production', event_id: '42', @@ -638,7 +648,7 @@ describe('BaseClient', () => { client.captureEvent({ message: 'message' }, undefined, scope); - expect(TestBackend.instance!.event!).toEqual( + expect(TestClient.instance!.event!).toEqual( expect.objectContaining({ environment: 'production', event_id: '42', @@ -655,7 +665,7 @@ describe('BaseClient', () => { client.captureEvent({ message: 'message' }); - expect(TestBackend.instance!.event!.sdk).toEqual({ + expect(TestClient.instance!.event!.sdk).toEqual({ integrations: ['TestIntegration'], }); }); @@ -698,7 +708,7 @@ describe('BaseClient', () => { user: fourLevelsObject, }); - expect(TestBackend.instance!.event!).toEqual( + expect(TestClient.instance!.event!).toEqual( expect.objectContaining({ breadcrumbs: [normalizedBreadcrumb, normalizedBreadcrumb, normalizedBreadcrumb], contexts: normalizedObject, @@ -749,7 +759,7 @@ describe('BaseClient', () => { user: fourLevelsObject, }); - expect(TestBackend.instance!.event!).toEqual( + expect(TestClient.instance!.event!).toEqual( expect.objectContaining({ breadcrumbs: [normalizedBreadcrumb, normalizedBreadcrumb, normalizedBreadcrumb], contexts: normalizedObject, @@ -805,7 +815,7 @@ describe('BaseClient', () => { user: fourLevelsObject, }); - expect(TestBackend.instance!.event!).toEqual( + expect(TestClient.instance!.event!).toEqual( expect.objectContaining({ breadcrumbs: [normalizedBreadcrumb, normalizedBreadcrumb, normalizedBreadcrumb], contexts: normalizedObject, @@ -871,7 +881,7 @@ describe('BaseClient', () => { // event. The code can be restored to its original form (the commented-out line below) once that hack is // removed. See https://github.com/getsentry/sentry-javascript/pull/4425 and // https://github.com/getsentry/sentry-javascript/pull/4574 - const capturedEvent = TestBackend.instance!.event!; + const capturedEvent = TestClient.instance!.event!; if (capturedEvent.sdkProcessingMetadata?.normalizeDepth) { if (Object.keys(capturedEvent.sdkProcessingMetadata).length === 1) { delete capturedEvent.sdkProcessingMetadata; @@ -899,7 +909,7 @@ describe('BaseClient', () => { client.captureEvent({ message: 'hello' }); - expect(TestBackend.instance!.event!.message).toBe('hello'); + expect(TestClient.instance!.event!.message).toBe('hello'); }); test('calls beforeSend and uses the new one', () => { @@ -910,7 +920,7 @@ describe('BaseClient', () => { client.captureEvent({ message: 'hello' }); - expect(TestBackend.instance!.event!.message).toBe('changed1'); + expect(TestClient.instance!.event!.message).toBe('changed1'); }); test('calls beforeSend and discards the event', () => { @@ -923,7 +933,7 @@ describe('BaseClient', () => { client.captureEvent({ message: 'hello' }); - expect(TestBackend.instance!.event).toBeUndefined(); + expect(TestClient.instance!.event).toBeUndefined(); expect(captureExceptionSpy).not.toBeCalled(); expect(loggerErrorSpy).toBeCalledWith(new SentryError('`beforeSend` returned `null`, will not send event.')); }); @@ -940,7 +950,7 @@ describe('BaseClient', () => { client.captureEvent({ message: 'hello' }); - expect(TestBackend.instance!.event).toBeUndefined(); + expect(TestClient.instance!.event).toBeUndefined(); expect(loggerErrorSpy).toBeCalledWith( new SentryError('`beforeSend` method has to return `null` or a valid event.'), ); @@ -964,7 +974,7 @@ describe('BaseClient', () => { client.captureEvent({ message: 'hello' }); jest.runOnlyPendingTimers(); - TestBackend.sendEventCalled = (event: Event) => { + TestClient.sendEventCalled = (event: Event) => { expect(event.message).toBe('hello'); }; @@ -992,7 +1002,7 @@ describe('BaseClient', () => { client.captureEvent({ message: 'hello' }); jest.runOnlyPendingTimers(); - TestBackend.sendEventCalled = (event: Event) => { + TestClient.sendEventCalled = (event: Event) => { expect(event.message).toBe('changed2'); }; @@ -1020,7 +1030,7 @@ describe('BaseClient', () => { client.captureEvent({ message: 'hello' }); jest.runAllTimers(); - expect(TestBackend.instance!.event).toBeUndefined(); + expect(TestClient.instance!.event).toBeUndefined(); }); test('beforeSend gets access to a hint as a second argument', () => { @@ -1031,8 +1041,8 @@ describe('BaseClient', () => { client.captureEvent({ message: 'hello' }, { data: 'someRandomThing' }); - expect(TestBackend.instance!.event!.message).toBe('hello'); - expect((TestBackend.instance!.event! as any).data).toBe('someRandomThing'); + expect(TestClient.instance!.event!.message).toBe('hello'); + expect((TestClient.instance!.event! as any).data).toBe('someRandomThing'); }); test('beforeSend records dropped events', () => { @@ -1068,7 +1078,7 @@ describe('BaseClient', () => { client.captureEvent({ message: 'hello' }, {}, scope); - expect(TestBackend.instance!.event).toBeUndefined(); + expect(TestClient.instance!.event).toBeUndefined(); expect(captureExceptionSpy).not.toBeCalled(); expect(loggerErrorSpy).toBeCalledWith(new SentryError('An event processor returned null, will not send event.')); }); @@ -1108,7 +1118,7 @@ describe('BaseClient', () => { client.captureEvent({ message: 'hello' }, {}, scope); - expect(TestBackend.instance!.event!.exception!.values![0]).toStrictEqual({ type: 'Error', value: 'sorry' }); + expect(TestClient.instance!.event!.exception!.values![0]).toStrictEqual({ type: 'Error', value: 'sorry' }); expect(captureExceptionSpy).toBeCalledWith(exception, { data: { __sentry__: true, @@ -1249,7 +1259,7 @@ describe('BaseClient', () => { }); const delay = 300; - const spy = jest.spyOn(TestBackend.instance!, 'eventFromMessage'); + const spy = jest.spyOn(TestClient.instance!, 'eventFromMessage'); spy.mockImplementationOnce( (message, level) => new SyncPromise(resolve => { @@ -1317,7 +1327,7 @@ describe('BaseClient', () => { }); describe('captureSession()', () => { - test('sends sessions to the backend', () => { + test('sends sessions to the client', () => { expect.assertions(1); const client = new TestClient({ dsn: PUBLIC_DSN }); @@ -1325,7 +1335,7 @@ describe('BaseClient', () => { client.captureSession(session); - expect(TestBackend.instance!.session).toEqual(session); + expect(TestClient.instance!.session).toEqual(session); }); test('skips when disabled', () => { @@ -1336,7 +1346,7 @@ describe('BaseClient', () => { client.captureSession(session); - expect(TestBackend.instance!.session).toBeUndefined(); + expect(TestClient.instance!.session).toBeUndefined(); }); }); }); diff --git a/packages/core/test/mocks/backend.ts b/packages/core/test/mocks/backend.ts index 48ddb0d9cc0c..a201bc8c0b68 100644 --- a/packages/core/test/mocks/backend.ts +++ b/packages/core/test/mocks/backend.ts @@ -1,14 +1,11 @@ import { Session } from '@sentry/hub'; -import { Event, Options, Severity, Transport } from '@sentry/types'; +import { Event, Severity, Transport } from '@sentry/types'; import { resolvedSyncPromise } from '@sentry/utils'; import { BaseBackend } from '../../src/basebackend'; +import { TestOptions } from './client'; -export interface TestOptions extends Options { - test?: boolean; - mockInstallFailure?: boolean; - enableSend?: boolean; -} +// TODO: Delete whole file (?) export class TestBackend extends BaseBackend { public static instance?: TestBackend; diff --git a/packages/core/test/mocks/client.ts b/packages/core/test/mocks/client.ts index 810328818eae..8c02ca30f5f3 100644 --- a/packages/core/test/mocks/client.ts +++ b/packages/core/test/mocks/client.ts @@ -1,14 +1,80 @@ +import { Session } from '@sentry/hub'; +import { Event, Options, Severity, Transport } from '@sentry/types'; +import { resolvedSyncPromise } from '@sentry/utils'; + import { BaseClient } from '../../src/baseclient'; import { initAndBind } from '../../src/sdk'; -import { TestBackend, TestOptions } from './backend'; +import { TestBackend } from './backend'; +export interface TestOptions extends Options { + test?: boolean; + mockInstallFailure?: boolean; + enableSend?: boolean; +} +// TODO(v7): remove TestBackend export class TestClient extends BaseClient { public static instance?: TestClient; + public static sendEventCalled?: (event: Event) => void; + + public event?: Event; + public session?: Session; public constructor(options: TestOptions) { + // TODO(v7): remove TestBackend param super(TestBackend, options); TestClient.instance = this; } + + // eslint-disable-next-line @typescript-eslint/no-explicit-any, @typescript-eslint/explicit-module-boundary-types + public eventFromException(exception: any): PromiseLike { + return resolvedSyncPromise({ + exception: { + values: [ + { + /* eslint-disable @typescript-eslint/no-unsafe-member-access */ + type: exception.name, + value: exception.message, + /* eslint-enable @typescript-eslint/no-unsafe-member-access */ + }, + ], + }, + }); + } + + public eventFromMessage(message: string, level: Severity = Severity.Info): PromiseLike { + return resolvedSyncPromise({ message, level }); + } + + public sendEvent(event: Event): void { + this.event = event; + if (this._options.enableSend) { + super.sendEvent(event); + return; + } + // eslint-disable-next-line @typescript-eslint/no-unused-expressions + TestClient.sendEventCalled && TestClient.sendEventCalled(event); + } + + public sendSession(session: Session): void { + this.session = session; + } + + protected _setupTransport(): Transport { + if (!this._options.dsn) { + // We return the noop transport here in case there is no Dsn. + return super._setupTransport(); + } + + const transportOptions = this._options.transportOptions + ? this._options.transportOptions + : { dsn: this._options.dsn }; + + if (this._options.transport) { + return new this._options.transport(transportOptions); + } + + return super._setupTransport(); + } } export function init(options: TestOptions): void { diff --git a/packages/node/src/client.ts b/packages/node/src/client.ts index dde7f8941b74..0b2b585edc5d 100644 --- a/packages/node/src/client.ts +++ b/packages/node/src/client.ts @@ -1,10 +1,12 @@ -import { BaseClient, Scope, SDK_VERSION } from '@sentry/core'; +import { BaseClient, getEnvelopeEndpointWithUrlEncodedAuth, initAPIDetails, Scope, SDK_VERSION } from '@sentry/core'; import { SessionFlusher } from '@sentry/hub'; -import { Event, EventHint } from '@sentry/types'; -import { logger } from '@sentry/utils'; +import { Event, EventHint, Severity, Transport, TransportOptions } from '@sentry/types'; +import { logger, makeDsn, resolvedSyncPromise } from '@sentry/utils'; import { NodeBackend } from './backend'; +import { eventFromMessage, eventFromUnknownInput } from './eventbuilder'; import { IS_DEBUG_BUILD } from './flags'; +import { HTTPSTransport, HTTPTransport, makeNodeTransport } from './transports'; import { NodeOptions } from './types'; /** @@ -12,6 +14,8 @@ import { NodeOptions } from './types'; * * @see NodeOptions for documentation on configuration options. * @see SentryClient for usage documentation. + * + * TODO(v7): remove NodeBackend */ export class NodeClient extends BaseClient { protected _sessionFlusher: SessionFlusher | undefined; @@ -33,6 +37,7 @@ export class NodeClient extends BaseClient { version: SDK_VERSION, }; + // TODO(v7): remove NodeBackend param super(NodeBackend, options); } @@ -106,6 +111,21 @@ export class NodeClient extends BaseClient { } } + /** + * @inheritDoc + */ + // eslint-disable-next-line @typescript-eslint/no-explicit-any, @typescript-eslint/explicit-module-boundary-types + public eventFromException(exception: any, hint?: EventHint): PromiseLike { + return resolvedSyncPromise(eventFromUnknownInput(exception, hint)); + } + + /** + * @inheritDoc + */ + public eventFromMessage(message: string, level: Severity = Severity.Info, hint?: EventHint): PromiseLike { + return resolvedSyncPromise(eventFromMessage(message, level, hint, this._options.attachStacktrace)); + } + /** * @inheritDoc */ @@ -128,4 +148,45 @@ export class NodeClient extends BaseClient { this._sessionFlusher.incrementSessionStatusCount(); } } + + /** + * @inheritDoc + */ + protected _setupTransport(): Transport { + if (!this._options.dsn) { + // We return the noop transport here in case there is no Dsn. + return super._setupTransport(); + } + + const dsn = makeDsn(this._options.dsn); + + const transportOptions: TransportOptions = { + ...this._options.transportOptions, + ...(this._options.httpProxy && { httpProxy: this._options.httpProxy }), + ...(this._options.httpsProxy && { httpsProxy: this._options.httpsProxy }), + ...(this._options.caCerts && { caCerts: this._options.caCerts }), + dsn: this._options.dsn, + tunnel: this._options.tunnel, + _metadata: this._options._metadata, + }; + + if (this._options.transport) { + return new this._options.transport(transportOptions); + } + + const api = initAPIDetails(transportOptions.dsn, transportOptions._metadata, transportOptions.tunnel); + const url = getEnvelopeEndpointWithUrlEncodedAuth(api.dsn, api.tunnel); + + this._newTransport = makeNodeTransport({ + url, + headers: transportOptions.headers, + proxy: transportOptions.httpProxy, + caCerts: transportOptions.caCerts, + }); + + if (dsn.protocol === 'http') { + return new HTTPTransport(transportOptions); + } + return new HTTPSTransport(transportOptions); + } } diff --git a/packages/node/src/index.ts b/packages/node/src/index.ts index 9f65c3f1a1a8..48bb88a24872 100644 --- a/packages/node/src/index.ts +++ b/packages/node/src/index.ts @@ -42,6 +42,7 @@ export { } from '@sentry/core'; export { NodeOptions } from './types'; +// TODO(v7): delete! export { NodeBackend } from './backend'; export { NodeClient } from './client'; export { defaultIntegrations, init, lastEventId, flush, close, getSentryRelease } from './sdk'; diff --git a/packages/node/test/index.test.ts b/packages/node/test/index.test.ts index 0af38dae80fb..687465802a94 100644 --- a/packages/node/test/index.test.ts +++ b/packages/node/test/index.test.ts @@ -15,7 +15,6 @@ import { NodeClient, Scope, } from '../src'; -import { NodeBackend } from '../src/backend'; import { ContextLines, LinkedErrors } from '../src/integrations'; jest.mock('@sentry/core', () => { @@ -78,7 +77,7 @@ describe('SentryNode', () => { let s: jest.SpyInstance; beforeEach(() => { - s = jest.spyOn(NodeBackend.prototype, 'sendEvent').mockImplementation(async () => Promise.resolve({ code: 200 })); + s = jest.spyOn(NodeClient.prototype, 'sendEvent').mockImplementation(async () => Promise.resolve({ code: 200 })); }); afterEach(() => { @@ -107,7 +106,7 @@ describe('SentryNode', () => { let s: jest.SpyInstance; beforeEach(() => { - s = jest.spyOn(NodeBackend.prototype, 'sendEvent').mockImplementation(async () => Promise.resolve({ code: 200 })); + s = jest.spyOn(NodeClient.prototype, 'sendEvent').mockImplementation(async () => Promise.resolve({ code: 200 })); }); afterEach(() => { @@ -360,7 +359,7 @@ describe('SentryNode initialization', () => { init({ dsn }); // eslint-disable-next-line @typescript-eslint/no-explicit-any - const sdkData = (getCurrentHub().getClient() as any)._backend._transport._api.metadata?.sdk; + const sdkData = (getCurrentHub().getClient() as any).getTransport()._api.metadata?.sdk; expect(sdkData.name).toEqual('sentry.javascript.node'); expect(sdkData.packages[0].name).toEqual('npm:@sentry/node'); @@ -372,7 +371,7 @@ describe('SentryNode initialization', () => { const client = new NodeClient({ dsn }); // eslint-disable-next-line @typescript-eslint/no-explicit-any - const sdkData = (client as any)._backend._transport._api.metadata?.sdk; + const sdkData = (client as any).getTransport()._api.metadata?.sdk; expect(sdkData.name).toEqual('sentry.javascript.node'); expect(sdkData.packages[0].name).toEqual('npm:@sentry/node'); @@ -401,7 +400,7 @@ describe('SentryNode initialization', () => { }); // eslint-disable-next-line @typescript-eslint/no-explicit-any - const sdkData = (getCurrentHub().getClient() as any)._backend._transport._api.metadata?.sdk; + const sdkData = (getCurrentHub().getClient() as any).getTransport()._api.metadata?.sdk; expect(sdkData.name).toEqual('sentry.javascript.serverless'); expect(sdkData.packages[0].name).toEqual('npm:@sentry/serverless'); diff --git a/packages/node/test/integrations/linkederrors.test.ts b/packages/node/test/integrations/linkederrors.test.ts index 7dcaf077e4c6..8363bd177359 100644 --- a/packages/node/test/integrations/linkederrors.test.ts +++ b/packages/node/test/integrations/linkederrors.test.ts @@ -28,6 +28,7 @@ describe('LinkedErrors', () => { expect.assertions(2); const spy = jest.spyOn(linkedErrors, '_walkErrorTree'); const one = new Error('originalException'); + // TODO(v7): refactor to use client here! const backend = new NodeBackend({}); let event: Event | undefined; return backend @@ -52,6 +53,7 @@ describe('LinkedErrors', () => { ); const one = new Error('originalException'); const backend = new NodeBackend({}); + // TODO(v7): refactor to use client here! return backend.eventFromException(one).then(event => linkedErrors ._handler(event, { @@ -72,6 +74,7 @@ describe('LinkedErrors', () => { two.cause = three; const backend = new NodeBackend({}); + // TODO(v7): refactor to use client here! return backend.eventFromException(one).then(event => linkedErrors ._handler(event, { @@ -105,6 +108,7 @@ describe('LinkedErrors', () => { two.reason = three; const backend = new NodeBackend({}); + // TODO(v7): refactor to use client here! return backend.eventFromException(one).then(event => linkedErrors ._handler(event, { @@ -138,6 +142,7 @@ describe('LinkedErrors', () => { two.cause = three; const backend = new NodeBackend({}); + // TODO(v7): refactor to use client here! return backend.eventFromException(one).then(event => linkedErrors ._handler(event, { diff --git a/packages/types/src/client.ts b/packages/types/src/client.ts index 69f7298105b2..e11950487fa1 100644 --- a/packages/types/src/client.ts +++ b/packages/types/src/client.ts @@ -60,7 +60,12 @@ export interface Client { /** Returns the current options. */ getOptions(): O; - /** Returns clients transport. */ + /** + * Returns the transport that is used by the client. + * Please note that the transport gets lazy initialized so it will only be there once the first event has been sent. + * + * @returns The transport. + */ getTransport?(): Transport; /** @@ -88,4 +93,17 @@ export interface Client { /** This is an internal function to setup all integrations that should run on the client */ setupIntegrations(): void; + + /** Creates an {@link Event} from all inputs to `captureException` and non-primitive inputs to `captureMessage`. */ + // eslint-disable-next-line @typescript-eslint/no-explicit-any + eventFromException(exception: any, hint?: EventHint): PromiseLike; + + /** Creates an {@link Event} from primitive inputs to `captureMessage`. */ + eventFromMessage(message: string, level?: Severity, hint?: EventHint): PromiseLike; + + /** Submits the event to Sentry */ + sendEvent(event: Event): void; + + /** Submits the session to Sentry */ + sendSession(session: Session): void; } From 43cb4a17eb24f37aad2221a5d50bf95cd4753f7a Mon Sep 17 00:00:00 2001 From: Luca Forstner Date: Tue, 12 Apr 2022 10:54:43 +0200 Subject: [PATCH 033/204] feat(docs): Add section for renaming of CDN bundles (#4918) Co-authored-by: Lukas Stracke --- MIGRATION.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/MIGRATION.md b/MIGRATION.md index 5307b44a7597..4e5de472634d 100644 --- a/MIGRATION.md +++ b/MIGRATION.md @@ -27,6 +27,12 @@ From version 7 onwards, the CommonJS files in Sentry JavaScript SDK packages wil If you need to support Internet Explorer 11 or old Node.js versions, we recommend using a preprocessing tool like [Babel](https://babeljs.io/) to convert Sentry packages to ES5. +### Renaming Of CDN Bundles + +CDN bundles will be ES6 by default. Files that followed the naming scheme `bundle.es6.min.js` were renamed to `bundle.min.js` and any bundles using ES5 (files without `.es6`) turned into `bundle.es5.min.js`. + +See our [docs on CDN bundles](https://docs.sentry.io/platforms/javascript/install/cdn/) for more information. + ### Restructuring Of Package Content Up until v6.x, we have published our packages on npm with the following structure: From ea09e78d65a2c1240bfe9f4b0b2d32d55f5e15fa Mon Sep 17 00:00:00 2001 From: Lukas Stracke Date: Tue, 12 Apr 2022 13:35:33 +0200 Subject: [PATCH 034/204] ref(backend): Delete `Backend` classes (#4919) Delete the Backend classes. Specifically the following classes and files are removed: * `Backend (interface)` * `BaseBackend`* * `BrowserBackend` * `NodeBackend` * `TestBackend`* *`Options` interfaces in these files were moved to the respective Client files Additionally, * delete (the previously ported) `NoopTransport` test in packages/browser/test/unit/backend.test.ts. * adjust a few remaining Backend references in tests to use Client classes * adjust `BaseBackend` documentation to no longer include Backend information --- packages/browser/src/backend.ts | 83 --------- packages/browser/src/client.ts | 29 +++- packages/browser/src/exports.ts | 4 +- packages/browser/src/sdk.ts | 3 +- packages/browser/test/unit/backend.test.ts | 14 -- .../unit/integrations/linkederrors.test.ts | 17 +- packages/core/src/basebackend.ts | 161 ------------------ packages/core/src/baseclient.ts | 38 +---- packages/core/src/index.ts | 2 - packages/core/test/lib/base.test.ts | 2 +- packages/core/test/mocks/backend.ts | 72 -------- packages/core/test/mocks/client.ts | 7 +- packages/node/src/backend.ts | 69 -------- packages/node/src/client.ts | 8 +- packages/node/src/index.ts | 2 - .../test/integrations/linkederrors.test.ts | 28 ++- 16 files changed, 56 insertions(+), 483 deletions(-) delete mode 100644 packages/browser/src/backend.ts delete mode 100644 packages/browser/test/unit/backend.test.ts delete mode 100644 packages/core/src/basebackend.ts delete mode 100644 packages/core/test/mocks/backend.ts delete mode 100644 packages/node/src/backend.ts diff --git a/packages/browser/src/backend.ts b/packages/browser/src/backend.ts deleted file mode 100644 index 2543c6ae99ad..000000000000 --- a/packages/browser/src/backend.ts +++ /dev/null @@ -1,83 +0,0 @@ -import { BaseBackend, getEnvelopeEndpointWithUrlEncodedAuth, initAPIDetails } from '@sentry/core'; -import { Event, EventHint, Options, Severity, Transport, TransportOptions } from '@sentry/types'; -import { supportsFetch } from '@sentry/utils'; - -import { eventFromException, eventFromMessage } from './eventbuilder'; -import { FetchTransport, makeNewFetchTransport, makeNewXHRTransport, XHRTransport } from './transports'; - -/** - * Configuration options for the Sentry Browser SDK. - * @see BrowserClient for more information. - * - * TODO(v7): move to client - */ -export interface BrowserOptions extends Options { - /** - * A pattern for error URLs which should exclusively be sent to Sentry. - * This is the opposite of {@link Options.denyUrls}. - * By default, all errors will be sent. - */ - allowUrls?: Array; - - /** - * A pattern for error URLs which should not be sent to Sentry. - * To allow certain errors instead, use {@link Options.allowUrls}. - * By default, all errors will be sent. - */ - denyUrls?: Array; -} - -/** - * The Sentry Browser SDK Backend. - * @hidden - */ -export class BrowserBackend extends BaseBackend { - /** - * @inheritDoc - */ - public eventFromException(exception: unknown, hint?: EventHint): PromiseLike { - return eventFromException(exception, hint, this._options.attachStacktrace); - } - /** - * @inheritDoc - */ - public eventFromMessage(message: string, level: Severity = Severity.Info, hint?: EventHint): PromiseLike { - return eventFromMessage(message, level, hint, this._options.attachStacktrace); - } - - /** - * @inheritDoc - */ - protected _setupTransport(): Transport { - if (!this._options.dsn) { - // We return the noop transport here in case there is no Dsn. - return super._setupTransport(); - } - - const transportOptions: TransportOptions = { - ...this._options.transportOptions, - dsn: this._options.dsn, - tunnel: this._options.tunnel, - sendClientReports: this._options.sendClientReports, - _metadata: this._options._metadata, - }; - - const api = initAPIDetails(transportOptions.dsn, transportOptions._metadata, transportOptions.tunnel); - const url = getEnvelopeEndpointWithUrlEncodedAuth(api.dsn, api.tunnel); - - if (this._options.transport) { - return new this._options.transport(transportOptions); - } - if (supportsFetch()) { - const requestOptions: RequestInit = { ...transportOptions.fetchParameters }; - this._newTransport = makeNewFetchTransport({ requestOptions, url }); - return new FetchTransport(transportOptions); - } - - this._newTransport = makeNewXHRTransport({ - url, - headers: transportOptions.headers, - }); - return new XHRTransport(transportOptions); - } -} diff --git a/packages/browser/src/client.ts b/packages/browser/src/client.ts index b64997c0a23b..8265889e4967 100644 --- a/packages/browser/src/client.ts +++ b/packages/browser/src/client.ts @@ -1,22 +1,40 @@ import { BaseClient, getEnvelopeEndpointWithUrlEncodedAuth, initAPIDetails, Scope, SDK_VERSION } from '@sentry/core'; -import { Event, EventHint, Severity, Transport, TransportOptions } from '@sentry/types'; +import { Event, EventHint, Options, Severity, Transport, TransportOptions } from '@sentry/types'; import { getGlobalObject, logger, supportsFetch } from '@sentry/utils'; -import { BrowserBackend, BrowserOptions } from './backend'; import { eventFromException, eventFromMessage } from './eventbuilder'; import { IS_DEBUG_BUILD } from './flags'; import { injectReportDialog, ReportDialogOptions } from './helpers'; import { Breadcrumbs } from './integrations'; import { FetchTransport, makeNewFetchTransport, makeNewXHRTransport, XHRTransport } from './transports'; +/** + * Configuration options for the Sentry Browser SDK. + * @see BrowserClient for more information. + */ +export interface BrowserOptions extends Options { + /** + * A pattern for error URLs which should exclusively be sent to Sentry. + * This is the opposite of {@link Options.denyUrls}. + * By default, all errors will be sent. + */ + allowUrls?: Array; + + /** + * A pattern for error URLs which should not be sent to Sentry. + * To allow certain errors instead, use {@link Options.allowUrls}. + * By default, all errors will be sent. + */ + denyUrls?: Array; +} + /** * The Sentry Browser SDK Client. * * @see BrowserOptions for documentation on configuration options. * @see SentryClient for usage documentation. - * TODO(v7): remove BrowserBackend */ -export class BrowserClient extends BaseClient { +export class BrowserClient extends BaseClient { /** * Creates a new Browser SDK instance. * @@ -35,8 +53,7 @@ export class BrowserClient extends BaseClient { version: SDK_VERSION, }; - // TODO(v7): remove BrowserBackend param - super(BrowserBackend, options); + super(options); } /** diff --git a/packages/browser/src/exports.ts b/packages/browser/src/exports.ts index 2a62e8e46fbe..6204a25f614e 100644 --- a/packages/browser/src/exports.ts +++ b/packages/browser/src/exports.ts @@ -41,9 +41,7 @@ export { withScope, } from '@sentry/core'; -// TODO(v7): refactor to use client here! -export { BrowserOptions } from './backend'; -export { BrowserClient } from './client'; +export { BrowserClient, BrowserOptions } from './client'; export { injectReportDialog, ReportDialogOptions } from './helpers'; export { defaultIntegrations, forceLoad, init, lastEventId, onLoad, showReportDialog, flush, close, wrap } from './sdk'; export { SDK_NAME } from './version'; diff --git a/packages/browser/src/sdk.ts b/packages/browser/src/sdk.ts index 3f05a646c96f..44d66add18c3 100644 --- a/packages/browser/src/sdk.ts +++ b/packages/browser/src/sdk.ts @@ -2,8 +2,7 @@ import { getCurrentHub, initAndBind, Integrations as CoreIntegrations } from '@s import { Hub } from '@sentry/types'; import { addInstrumentationHandler, getGlobalObject, logger, resolvedSyncPromise } from '@sentry/utils'; -import { BrowserOptions } from './backend'; -import { BrowserClient } from './client'; +import { BrowserClient, BrowserOptions } from './client'; import { IS_DEBUG_BUILD } from './flags'; import { ReportDialogOptions, wrap as internalWrap } from './helpers'; import { Breadcrumbs, Dedupe, GlobalHandlers, LinkedErrors, TryCatch, UserAgent } from './integrations'; diff --git a/packages/browser/test/unit/backend.test.ts b/packages/browser/test/unit/backend.test.ts deleted file mode 100644 index 83f95307a8c7..000000000000 --- a/packages/browser/test/unit/backend.test.ts +++ /dev/null @@ -1,14 +0,0 @@ -import { BrowserBackend } from '../../src/backend'; - -let backend: BrowserBackend; - -// TODO(v7): remove when deleting Backend - -describe('BrowserBackend', () => { - describe('sendEvent()', () => { - it('should use NoopTransport', () => { - backend = new BrowserBackend({}); - expect(backend.getTransport().constructor.name).toBe('NoopTransport'); - }); - }); -}); diff --git a/packages/browser/test/unit/integrations/linkederrors.test.ts b/packages/browser/test/unit/integrations/linkederrors.test.ts index 35f54f4b2d87..8178741d1210 100644 --- a/packages/browser/test/unit/integrations/linkederrors.test.ts +++ b/packages/browser/test/unit/integrations/linkederrors.test.ts @@ -1,6 +1,6 @@ import { ExtendedError } from '@sentry/types'; -import { BrowserBackend } from '../../../src/backend'; +import { BrowserClient } from '../../../src/client'; import * as LinkedErrorsModule from '../../../src/integrations/linkederrors'; describe('LinkedErrors', () => { @@ -34,9 +34,8 @@ describe('LinkedErrors', () => { one.cause = two; const originalException = one; - // TODO(v7): refactor to use client here! - const backend = new BrowserBackend({}); - return backend.eventFromException(originalException).then(event => { + const client = new BrowserClient({}); + return client.eventFromException(originalException).then(event => { const result = LinkedErrorsModule._handler('cause', 5, event, { originalException, }); @@ -65,9 +64,8 @@ describe('LinkedErrors', () => { one.reason = two; const originalException = one; - const backend = new BrowserBackend({}); - // TODO(v7): refactor to use client here! - return backend.eventFromException(originalException).then(event => { + const client = new BrowserClient({}); + return client.eventFromException(originalException).then(event => { const result = LinkedErrorsModule._handler('reason', 5, event, { originalException, }); @@ -92,10 +90,9 @@ describe('LinkedErrors', () => { one.cause = two; two.cause = three; - const backend = new BrowserBackend({}); + const client = new BrowserClient({}); const originalException = one; - // TODO(v7): refactor to use client here! - return backend.eventFromException(originalException).then(event => { + return client.eventFromException(originalException).then(event => { const result = LinkedErrorsModule._handler('cause', 2, event, { originalException, }); diff --git a/packages/core/src/basebackend.ts b/packages/core/src/basebackend.ts deleted file mode 100644 index 92bdd4e7f65c..000000000000 --- a/packages/core/src/basebackend.ts +++ /dev/null @@ -1,161 +0,0 @@ -import { Event, EventHint, Options, Session, Severity, Transport } from '@sentry/types'; -import { logger, SentryError } from '@sentry/utils'; - -import { initAPIDetails } from './api'; -import { IS_DEBUG_BUILD } from './flags'; -import { createEventEnvelope, createSessionEnvelope } from './request'; -import { NewTransport } from './transports/base'; -import { NoopTransport } from './transports/noop'; - -/** - * Internal platform-dependent Sentry SDK Backend. - * - * While {@link Client} contains business logic specific to an SDK, the - * Backend offers platform specific implementations for low-level operations. - * These are persisting and loading information, sending events, and hooking - * into the environment. - * - * Backends receive a handle to the Client in their constructor. When a - * Backend automatically generates events, it must pass them to - * the Client for validation and processing first. - * - * Usually, the Client will be of corresponding type, e.g. NodeBackend - * receives NodeClient. However, higher-level SDKs can choose to instantiate - * multiple Backends and delegate tasks between them. In this case, an event - * generated by one backend might very well be sent by another one. - * - * The client also provides access to options via {@link Client.getOptions}. - * @hidden - */ -export interface Backend { - /** Creates an {@link Event} from all inputs to `captureException` and non-primitive inputs to `captureMessage`. */ - // eslint-disable-next-line @typescript-eslint/no-explicit-any - eventFromException(exception: any, hint?: EventHint): PromiseLike; - - /** Creates an {@link Event} from primitive inputs to `captureMessage`. */ - eventFromMessage(message: string, level?: Severity, hint?: EventHint): PromiseLike; - - /** Submits the event to Sentry */ - sendEvent(event: Event): void; - - /** Submits the session to Sentry */ - sendSession(session: Session): void; - - /** - * Returns the transport that is used by the backend. - * Please note that the transport gets lazy initialized so it will only be there once the first event has been sent. - * - * @returns The transport. - */ - getTransport(): Transport; -} - -/** - * A class object that can instantiate Backend objects. - * @hidden - */ -export type BackendClass = new (options: O) => B; - -/** - * This is the base implemention of a Backend. - * @hidden - */ -export abstract class BaseBackend implements Backend { - /** Options passed to the SDK. */ - protected readonly _options: O; - - /** Cached transport used internally. */ - protected _transport: Transport; - - /** New v7 Transport that is initialized alongside the old one */ - protected _newTransport?: NewTransport; - - /** Creates a new backend instance. */ - public constructor(options: O) { - this._options = options; - if (!this._options.dsn) { - IS_DEBUG_BUILD && logger.warn('No DSN provided, backend will not do anything.'); - } - this._transport = this._setupTransport(); - } - - /** - * @inheritDoc - */ - // eslint-disable-next-line @typescript-eslint/no-explicit-any, @typescript-eslint/explicit-module-boundary-types - public eventFromException(_exception: any, _hint?: EventHint): PromiseLike { - throw new SentryError('Backend has to implement `eventFromException` method'); - } - - /** - * @inheritDoc - */ - public eventFromMessage(_message: string, _level?: Severity, _hint?: EventHint): PromiseLike { - throw new SentryError('Backend has to implement `eventFromMessage` method'); - } - - /** - * @inheritDoc - */ - public sendEvent(event: Event): void { - // TODO(v7): Remove the if-else - if ( - this._newTransport && - this._options.dsn && - this._options._experiments && - this._options._experiments.newTransport - ) { - const api = initAPIDetails(this._options.dsn, this._options._metadata, this._options.tunnel); - const env = createEventEnvelope(event, api); - void this._newTransport.send(env).then(null, reason => { - IS_DEBUG_BUILD && logger.error('Error while sending event:', reason); - }); - } else { - void this._transport.sendEvent(event).then(null, reason => { - IS_DEBUG_BUILD && logger.error('Error while sending event:', reason); - }); - } - } - - /** - * @inheritDoc - */ - public sendSession(session: Session): void { - if (!this._transport.sendSession) { - IS_DEBUG_BUILD && logger.warn("Dropping session because custom transport doesn't implement sendSession"); - return; - } - - // TODO(v7): Remove the if-else - if ( - this._newTransport && - this._options.dsn && - this._options._experiments && - this._options._experiments.newTransport - ) { - const api = initAPIDetails(this._options.dsn, this._options._metadata, this._options.tunnel); - const [env] = createSessionEnvelope(session, api); - void this._newTransport.send(env).then(null, reason => { - IS_DEBUG_BUILD && logger.error('Error while sending session:', reason); - }); - } else { - void this._transport.sendSession(session).then(null, reason => { - IS_DEBUG_BUILD && logger.error('Error while sending session:', reason); - }); - } - } - - /** - * @inheritDoc - */ - public getTransport(): Transport { - return this._transport; - } - - /** - * Sets up the transport so it can be used later to send requests. - */ - protected _setupTransport(): Transport { - return new NoopTransport(); - } -} diff --git a/packages/core/src/baseclient.ts b/packages/core/src/baseclient.ts index 59145508ca9d..75712f62479b 100644 --- a/packages/core/src/baseclient.ts +++ b/packages/core/src/baseclient.ts @@ -29,7 +29,6 @@ import { } from '@sentry/utils'; import { initAPIDetails } from './api'; -import { Backend, BackendClass } from './basebackend'; import { IS_DEBUG_BUILD } from './flags'; import { IntegrationIndex, setupIntegrations } from './integration'; import { createEventEnvelope, createSessionEnvelope } from './request'; @@ -41,19 +40,16 @@ const ALREADY_SEEN_ERROR = "Not capturing exception because it's already been ca /** * Base implementation for all JavaScript SDK clients. * - * TODO(v7): refactor doc w.r.t. Backend - * - * Call the constructor with the corresponding backend constructor and options + * Call the constructor with the corresponding options * specific to the client subclass. To access these options later, use - * {@link Client.getOptions}. Also, the Backend instance is available via - * {@link Client.getBackend}. + * {@link Client.getOptions}. * * If a Dsn is specified in the options, it will be parsed and stored. Use * {@link Client.getDsn} to retrieve the Dsn at any moment. In case the Dsn is * invalid, the constructor will throw a {@link SentryException}. Note that * without a valid Dsn, the SDK will not send any events to Sentry. * - * Before sending an event via the backend, it is passed through + * Before sending an event, it is passed through * {@link BaseClient._prepareEvent} to add SDK information and scope data * (breadcrumbs and context). To add more custom information, override this * method and extend the resulting prepared event. @@ -64,23 +60,15 @@ const ALREADY_SEEN_ERROR = "Not capturing exception because it's already been ca * {@link Client.addBreadcrumb}. * * @example - * class NodeClient extends BaseClient { + * class NodeClient extends BaseClient { * public constructor(options: NodeOptions) { - * super(NodeBackend, options); + * super(options); * } * * // ... * } */ -export abstract class BaseClient implements Client { - /** - * The backend used to physically interact in the environment. Usually, this - * will correspond to the client. When composing SDKs, however, the Backend - * from the root SDK will be used. - * TODO(v7): DELETE - */ - protected readonly _backend: B; - +export abstract class BaseClient implements Client { /** Options passed to the SDK. */ protected readonly _options: O; @@ -102,12 +90,9 @@ export abstract class BaseClient implement /** * Initializes this client instance. * - * @param backendClass A constructor function to create the backend. * @param options Options for the client. */ - protected constructor(backendClass: BackendClass, options: O) { - // TODO(v7): Delete - this._backend = new backendClass(options); + protected constructor(options: O) { this._options = options; if (options.dsn) { @@ -386,13 +371,6 @@ export abstract class BaseClient implement }); } - /** Returns the current backend. - * TODO(v7): DELETE - */ - protected _getBackend(): B { - return this._backend; - } - /** Determines whether this SDK is enabled and a valid Dsn is present. */ protected _isEnabled(): boolean { return this.getOptions().enabled !== false && this._dsn !== undefined; @@ -558,7 +536,7 @@ export abstract class BaseClient implement } /** - * Tells the backend to send this event + * Sends the passed event * @param event The Sentry event to send */ // TODO(v7): refactor: get rid of method? diff --git a/packages/core/src/index.ts b/packages/core/src/index.ts index 77018c2b4437..e4143f098e4a 100644 --- a/packages/core/src/index.ts +++ b/packages/core/src/index.ts @@ -23,8 +23,6 @@ export { getReportDialogEndpoint, } from './api'; export { BaseClient } from './baseclient'; -// TODO(v7): Delete! -export { BackendClass, BaseBackend } from './basebackend'; export { eventToSentryRequest, sessionToSentryRequest } from './request'; export { initAndBind, ClientClass } from './sdk'; export { NoopTransport } from './transports/noop'; diff --git a/packages/core/test/lib/base.test.ts b/packages/core/test/lib/base.test.ts index 8a26bd51f116..6bb72ed99f16 100644 --- a/packages/core/test/lib/base.test.ts +++ b/packages/core/test/lib/base.test.ts @@ -898,7 +898,7 @@ describe('BaseClient', () => { } expect(capturedEvent).toEqual(normalizedTransaction); - // expect(TestBackend.instance!.event!).toEqual(normalizedTransaction); + // expect(TestClient.instance!.event!).toEqual(normalizedTransaction); }); test('calls beforeSend and uses original event without any changes', () => { diff --git a/packages/core/test/mocks/backend.ts b/packages/core/test/mocks/backend.ts deleted file mode 100644 index a201bc8c0b68..000000000000 --- a/packages/core/test/mocks/backend.ts +++ /dev/null @@ -1,72 +0,0 @@ -import { Session } from '@sentry/hub'; -import { Event, Severity, Transport } from '@sentry/types'; -import { resolvedSyncPromise } from '@sentry/utils'; - -import { BaseBackend } from '../../src/basebackend'; -import { TestOptions } from './client'; - -// TODO: Delete whole file (?) - -export class TestBackend extends BaseBackend { - public static instance?: TestBackend; - public static sendEventCalled?: (event: Event) => void; - - public event?: Event; - public session?: Session; - - public constructor(protected readonly _options: TestOptions) { - super(_options); - TestBackend.instance = this; - } - - // eslint-disable-next-line @typescript-eslint/no-explicit-any, @typescript-eslint/explicit-module-boundary-types - public eventFromException(exception: any): PromiseLike { - return resolvedSyncPromise({ - exception: { - values: [ - { - /* eslint-disable @typescript-eslint/no-unsafe-member-access */ - type: exception.name, - value: exception.message, - /* eslint-enable @typescript-eslint/no-unsafe-member-access */ - }, - ], - }, - }); - } - - public eventFromMessage(message: string, level: Severity = Severity.Info): PromiseLike { - return resolvedSyncPromise({ message, level }); - } - - public sendEvent(event: Event): void { - this.event = event; - if (this._options.enableSend) { - super.sendEvent(event); - return; - } - // eslint-disable-next-line @typescript-eslint/no-unused-expressions - TestBackend.sendEventCalled && TestBackend.sendEventCalled(event); - } - - public sendSession(session: Session): void { - this.session = session; - } - - protected _setupTransport(): Transport { - if (!this._options.dsn) { - // We return the noop transport here in case there is no Dsn. - return super._setupTransport(); - } - - const transportOptions = this._options.transportOptions - ? this._options.transportOptions - : { dsn: this._options.dsn }; - - if (this._options.transport) { - return new this._options.transport(transportOptions); - } - - return super._setupTransport(); - } -} diff --git a/packages/core/test/mocks/client.ts b/packages/core/test/mocks/client.ts index 8c02ca30f5f3..f399c6018122 100644 --- a/packages/core/test/mocks/client.ts +++ b/packages/core/test/mocks/client.ts @@ -4,15 +4,13 @@ import { resolvedSyncPromise } from '@sentry/utils'; import { BaseClient } from '../../src/baseclient'; import { initAndBind } from '../../src/sdk'; -import { TestBackend } from './backend'; export interface TestOptions extends Options { test?: boolean; mockInstallFailure?: boolean; enableSend?: boolean; } -// TODO(v7): remove TestBackend -export class TestClient extends BaseClient { +export class TestClient extends BaseClient { public static instance?: TestClient; public static sendEventCalled?: (event: Event) => void; @@ -20,8 +18,7 @@ export class TestClient extends BaseClient { public session?: Session; public constructor(options: TestOptions) { - // TODO(v7): remove TestBackend param - super(TestBackend, options); + super(options); TestClient.instance = this; } diff --git a/packages/node/src/backend.ts b/packages/node/src/backend.ts deleted file mode 100644 index f319673ebc18..000000000000 --- a/packages/node/src/backend.ts +++ /dev/null @@ -1,69 +0,0 @@ -import { BaseBackend, getEnvelopeEndpointWithUrlEncodedAuth, initAPIDetails } from '@sentry/core'; -import { Event, EventHint, Severity, Transport, TransportOptions } from '@sentry/types'; -import { makeDsn, resolvedSyncPromise } from '@sentry/utils'; - -import { eventFromMessage, eventFromUnknownInput } from './eventbuilder'; -import { HTTPSTransport, HTTPTransport, makeNodeTransport } from './transports'; -import { NodeOptions } from './types'; - -/** - * The Sentry Node SDK Backend. - * @hidden - */ -export class NodeBackend extends BaseBackend { - /** - * @inheritDoc - */ - // eslint-disable-next-line @typescript-eslint/no-explicit-any, @typescript-eslint/explicit-module-boundary-types - public eventFromException(exception: any, hint?: EventHint): PromiseLike { - return resolvedSyncPromise(eventFromUnknownInput(exception, hint)); - } - - /** - * @inheritDoc - */ - public eventFromMessage(message: string, level: Severity = Severity.Info, hint?: EventHint): PromiseLike { - return resolvedSyncPromise(eventFromMessage(message, level, hint, this._options.attachStacktrace)); - } - - /** - * @inheritDoc - */ - protected _setupTransport(): Transport { - if (!this._options.dsn) { - // We return the noop transport here in case there is no Dsn. - return super._setupTransport(); - } - - const dsn = makeDsn(this._options.dsn); - - const transportOptions: TransportOptions = { - ...this._options.transportOptions, - ...(this._options.httpProxy && { httpProxy: this._options.httpProxy }), - ...(this._options.httpsProxy && { httpsProxy: this._options.httpsProxy }), - ...(this._options.caCerts && { caCerts: this._options.caCerts }), - dsn: this._options.dsn, - tunnel: this._options.tunnel, - _metadata: this._options._metadata, - }; - - if (this._options.transport) { - return new this._options.transport(transportOptions); - } - - const api = initAPIDetails(transportOptions.dsn, transportOptions._metadata, transportOptions.tunnel); - const url = getEnvelopeEndpointWithUrlEncodedAuth(api.dsn, api.tunnel); - - this._newTransport = makeNodeTransport({ - url, - headers: transportOptions.headers, - proxy: transportOptions.httpProxy, - caCerts: transportOptions.caCerts, - }); - - if (dsn.protocol === 'http') { - return new HTTPTransport(transportOptions); - } - return new HTTPSTransport(transportOptions); - } -} diff --git a/packages/node/src/client.ts b/packages/node/src/client.ts index 0b2b585edc5d..4401f4016520 100644 --- a/packages/node/src/client.ts +++ b/packages/node/src/client.ts @@ -3,7 +3,6 @@ import { SessionFlusher } from '@sentry/hub'; import { Event, EventHint, Severity, Transport, TransportOptions } from '@sentry/types'; import { logger, makeDsn, resolvedSyncPromise } from '@sentry/utils'; -import { NodeBackend } from './backend'; import { eventFromMessage, eventFromUnknownInput } from './eventbuilder'; import { IS_DEBUG_BUILD } from './flags'; import { HTTPSTransport, HTTPTransport, makeNodeTransport } from './transports'; @@ -14,10 +13,8 @@ import { NodeOptions } from './types'; * * @see NodeOptions for documentation on configuration options. * @see SentryClient for usage documentation. - * - * TODO(v7): remove NodeBackend */ -export class NodeClient extends BaseClient { +export class NodeClient extends BaseClient { protected _sessionFlusher: SessionFlusher | undefined; /** @@ -37,8 +34,7 @@ export class NodeClient extends BaseClient { version: SDK_VERSION, }; - // TODO(v7): remove NodeBackend param - super(NodeBackend, options); + super(options); } /** diff --git a/packages/node/src/index.ts b/packages/node/src/index.ts index 48bb88a24872..591fd4ad911d 100644 --- a/packages/node/src/index.ts +++ b/packages/node/src/index.ts @@ -42,8 +42,6 @@ export { } from '@sentry/core'; export { NodeOptions } from './types'; -// TODO(v7): delete! -export { NodeBackend } from './backend'; export { NodeClient } from './client'; export { defaultIntegrations, init, lastEventId, flush, close, getSentryRelease } from './sdk'; export { deepReadDirSync } from './utils'; diff --git a/packages/node/test/integrations/linkederrors.test.ts b/packages/node/test/integrations/linkederrors.test.ts index 8363bd177359..47249d129716 100644 --- a/packages/node/test/integrations/linkederrors.test.ts +++ b/packages/node/test/integrations/linkederrors.test.ts @@ -1,7 +1,6 @@ import { ExtendedError } from '@sentry/types'; -import { Event } from '../../src'; -import { NodeBackend } from '../../src/backend'; +import { Event, NodeClient } from '../../src'; import { LinkedErrors } from '../../src/integrations/linkederrors'; let linkedErrors: any; @@ -28,10 +27,9 @@ describe('LinkedErrors', () => { expect.assertions(2); const spy = jest.spyOn(linkedErrors, '_walkErrorTree'); const one = new Error('originalException'); - // TODO(v7): refactor to use client here! - const backend = new NodeBackend({}); + const client = new NodeClient({}); let event: Event | undefined; - return backend + return client .eventFromException(one) .then(eventFromException => { event = eventFromException; @@ -52,9 +50,8 @@ describe('LinkedErrors', () => { }), ); const one = new Error('originalException'); - const backend = new NodeBackend({}); - // TODO(v7): refactor to use client here! - return backend.eventFromException(one).then(event => + const client = new NodeClient({}); + return client.eventFromException(one).then(event => linkedErrors ._handler(event, { originalException: one, @@ -73,9 +70,8 @@ describe('LinkedErrors', () => { one.cause = two; two.cause = three; - const backend = new NodeBackend({}); - // TODO(v7): refactor to use client here! - return backend.eventFromException(one).then(event => + const client = new NodeClient({}); + return client.eventFromException(one).then(event => linkedErrors ._handler(event, { originalException: one, @@ -107,9 +103,8 @@ describe('LinkedErrors', () => { one.reason = two; two.reason = three; - const backend = new NodeBackend({}); - // TODO(v7): refactor to use client here! - return backend.eventFromException(one).then(event => + const client = new NodeClient({}); + return client.eventFromException(one).then(event => linkedErrors ._handler(event, { originalException: one, @@ -141,9 +136,8 @@ describe('LinkedErrors', () => { one.cause = two; two.cause = three; - const backend = new NodeBackend({}); - // TODO(v7): refactor to use client here! - return backend.eventFromException(one).then(event => + const client = new NodeClient({}); + return client.eventFromException(one).then(event => linkedErrors ._handler(event, { originalException: one, From 2ffd78c5a88ef372bd48da5b607a858ebaf4644a Mon Sep 17 00:00:00 2001 From: Katie Byers Date: Tue, 12 Apr 2022 06:44:15 -0700 Subject: [PATCH 035/204] fix(wasm): Ensure wasm bundle exists before running wasm tests (#4916) The `@sentry/wasm` test suite requires both the browser bundle and the wasm bundle. Currently, before running the tests, we check to make sure the browser bundle exists, and build it if it's missing. We don't do the same for the wasm bundle, however. This fixes that, so that both bundles are guaranteed to exist before we try to run tests. --- packages/wasm/package.json | 2 +- .../{ensure-browser-bundle.js => ensure-bundles.js} | 9 +++++++++ 2 files changed, 10 insertions(+), 1 deletion(-) rename packages/wasm/test/scripts/{ensure-browser-bundle.js => ensure-bundles.js} (65%) diff --git a/packages/wasm/package.json b/packages/wasm/package.json index 6cdf354f1f5e..b8d70d1bd924 100644 --- a/packages/wasm/package.json +++ b/packages/wasm/package.json @@ -53,7 +53,7 @@ "lint": "run-s lint:prettier lint:eslint", "lint:eslint": "eslint . --cache --cache-location '../../eslintcache/' --format stylish", "lint:prettier": "prettier --check \"{src,test,scripts}/**/*.ts\"", - "test": "node test/scripts/ensure-browser-bundle.js && cross-env PORT=1337 jest", + "test": "node test/scripts/ensure-bundles.js && cross-env PORT=1337 jest", "test:watch": "jest --watch" }, "volta": { diff --git a/packages/wasm/test/scripts/ensure-browser-bundle.js b/packages/wasm/test/scripts/ensure-bundles.js similarity index 65% rename from packages/wasm/test/scripts/ensure-browser-bundle.js rename to packages/wasm/test/scripts/ensure-bundles.js index 2261eccaf84a..baa4f1e2a41c 100644 --- a/packages/wasm/test/scripts/ensure-browser-bundle.js +++ b/packages/wasm/test/scripts/ensure-bundles.js @@ -11,4 +11,13 @@ function ensureBrowserBundle() { } } +function ensureWasmBundle() { + if (!fs.existsSync('build/bundles/wasm.js')) { + // eslint-disable-next-line no-console + console.warn('\nWARNING: Missing wasm bundle. Bundle will be created before running wasm integration tests.'); + execSync('yarn build:bundle'); + } +} + ensureBrowserBundle(); +ensureWasmBundle(); From 6f09dd7dbbeaf82ce6a28870bf7e3fca699ce4b0 Mon Sep 17 00:00:00 2001 From: Tim Fish Date: Tue, 12 Apr 2022 16:23:17 +0100 Subject: [PATCH 036/204] feat: Expose configurable stack parser (#4902) Adds a `stackParser` option to `Options`: ```ts /** * A stack parser implementation or an array of stack line parsers * By default, a stack parser is supplied for all supported browsers */ stackParser?: StackParser | StackLineParser[]; ``` Whenever we want access to a `StackParser` to use with the functions in `eventbuilder` we call `stackParserFromOptions(options)`. This converts `StackLineParser[]` to `StackParser` and saves it back in the options so this conversion only occurs once. ### Added Exports `@sentry/node` - `nodeStackParser` `@sentry/browser` - `chromeStackParser` - `geckoStackParser` - `opera10StackParser` - `opera11StackParser` - `winjsStackParser` - `defaultStackParsers` --- packages/browser/src/client.ts | 12 +++- packages/browser/src/eventbuilder.ts | 65 +++++++++---------- packages/browser/src/exports.ts | 9 +++ .../src/integrations/globalhandlers.ts | 22 ++++--- .../browser/src/integrations/linkederrors.ts | 32 ++++++--- packages/browser/src/sdk.ts | 4 ++ packages/browser/src/stack-parsers.ts | 11 +++- .../unit/integrations/linkederrors.test.ts | 20 +++--- .../test/unit/tracekit/chromium.test.ts | 29 +++++---- .../test/unit/tracekit/firefox.test.ts | 23 ++++--- .../browser/test/unit/tracekit/ie.test.ts | 11 +++- .../browser/test/unit/tracekit/misc.test.ts | 7 +- .../browser/test/unit/tracekit/opera.test.ts | 13 ++-- .../test/unit/tracekit/react-native.test.ts | 15 +++-- .../browser/test/unit/tracekit/react.test.ts | 11 +++- .../browser/test/unit/tracekit/safari.test.ts | 25 ++++--- packages/node/src/client.ts | 8 ++- packages/node/src/eventbuilder.ts | 20 +++--- packages/node/src/index.ts | 1 + .../node/src/integrations/linkederrors.ts | 31 +++++---- packages/node/src/sdk.ts | 5 ++ packages/node/src/stack-parser.ts | 3 +- packages/node/test/context-lines.test.ts | 12 ++-- packages/node/test/index.test.ts | 13 ++++ .../test/integrations/linkederrors.test.ts | 26 ++++---- packages/node/test/stacktrace.test.ts | 33 ++++++---- packages/types/src/index.ts | 2 +- packages/types/src/options.ts | 7 ++ packages/types/src/stacktrace.ts | 4 ++ packages/utils/src/stacktrace.ts | 29 +++++++-- 30 files changed, 330 insertions(+), 173 deletions(-) diff --git a/packages/browser/src/client.ts b/packages/browser/src/client.ts index 8265889e4967..4ee24f0ceadf 100644 --- a/packages/browser/src/client.ts +++ b/packages/browser/src/client.ts @@ -1,6 +1,6 @@ import { BaseClient, getEnvelopeEndpointWithUrlEncodedAuth, initAPIDetails, Scope, SDK_VERSION } from '@sentry/core'; import { Event, EventHint, Options, Severity, Transport, TransportOptions } from '@sentry/types'; -import { getGlobalObject, logger, supportsFetch } from '@sentry/utils'; +import { getGlobalObject, logger, stackParserFromOptions, supportsFetch } from '@sentry/utils'; import { eventFromException, eventFromMessage } from './eventbuilder'; import { IS_DEBUG_BUILD } from './flags'; @@ -83,14 +83,20 @@ export class BrowserClient extends BaseClient { * @inheritDoc */ public eventFromException(exception: unknown, hint?: EventHint): PromiseLike { - return eventFromException(exception, hint, this._options.attachStacktrace); + return eventFromException(stackParserFromOptions(this._options), exception, hint, this._options.attachStacktrace); } /** * @inheritDoc */ public eventFromMessage(message: string, level: Severity = Severity.Info, hint?: EventHint): PromiseLike { - return eventFromMessage(message, level, hint, this._options.attachStacktrace); + return eventFromMessage( + stackParserFromOptions(this._options), + message, + level, + hint, + this._options.attachStacktrace, + ); } /** diff --git a/packages/browser/src/eventbuilder.ts b/packages/browser/src/eventbuilder.ts index d8acd41c12c0..6ed5181eefa1 100644 --- a/packages/browser/src/eventbuilder.ts +++ b/packages/browser/src/eventbuilder.ts @@ -1,8 +1,7 @@ -import { Event, EventHint, Exception, Severity, StackFrame } from '@sentry/types'; +import { Event, EventHint, Exception, Severity, StackFrame, StackParser } from '@sentry/types'; import { addExceptionMechanism, addExceptionTypeValue, - createStackParser, extractExceptionKeysForMessage, isDOMError, isDOMException, @@ -14,22 +13,12 @@ import { resolvedSyncPromise, } from '@sentry/utils'; -import { - chromeStackParser, - geckoStackParser, - opera10StackParser, - opera11StackParser, - winjsStackParser, -} from './stack-parsers'; - /** * This function creates an exception from an TraceKitStackTrace - * @param stacktrace TraceKitStackTrace that will be converted to an exception - * @hidden */ -export function exceptionFromError(ex: Error): Exception { +export function exceptionFromError(stackParser: StackParser, ex: Error): Exception { // Get the frames first since Opera can lose the stack if we touch anything else first - const frames = parseStackFrames(ex); + const frames = parseStackFrames(stackParser, ex); const exception: Exception = { type: ex && ex.name, @@ -51,6 +40,7 @@ export function exceptionFromError(ex: Error): Exception { * @hidden */ export function eventFromPlainObject( + stackParser: StackParser, exception: Record, syntheticException?: Error, isUnhandledRejection?: boolean, @@ -72,7 +62,7 @@ export function eventFromPlainObject( }; if (syntheticException) { - const frames = parseStackFrames(syntheticException); + const frames = parseStackFrames(stackParser, syntheticException); if (frames.length) { // event.exception.values[0] has been set above (event.exception as { values: Exception[] }).values[0].stacktrace = { frames }; @@ -85,16 +75,19 @@ export function eventFromPlainObject( /** * @hidden */ -export function eventFromError(ex: Error): Event { +export function eventFromError(stackParser: StackParser, ex: Error): Event { return { exception: { - values: [exceptionFromError(ex)], + values: [exceptionFromError(stackParser, ex)], }, }; } /** Parses stack frames from an error */ -export function parseStackFrames(ex: Error & { framesToPop?: number; stacktrace?: string }): StackFrame[] { +export function parseStackFrames( + stackParser: StackParser, + ex: Error & { framesToPop?: number; stacktrace?: string }, +): StackFrame[] { // Access and store the stacktrace property before doing ANYTHING // else to it because Opera is not very good at providing it // reliably in other circumstances. @@ -103,13 +96,7 @@ export function parseStackFrames(ex: Error & { framesToPop?: number; stacktrace? const popSize = getPopSize(ex); try { - return createStackParser( - opera10StackParser, - opera11StackParser, - chromeStackParser, - winjsStackParser, - geckoStackParser, - )(stacktrace, popSize); + return stackParser(stacktrace, popSize); } catch (e) { // no-empty } @@ -155,12 +142,13 @@ function extractMessage(ex: Error & { message: { error?: Error } }): string { * @hidden */ export function eventFromException( + stackParser: StackParser, exception: unknown, hint?: EventHint, attachStacktrace?: boolean, ): PromiseLike { const syntheticException = (hint && hint.syntheticException) || undefined; - const event = eventFromUnknownInput(exception, syntheticException, attachStacktrace); + const event = eventFromUnknownInput(stackParser, exception, syntheticException, attachStacktrace); addExceptionMechanism(event); // defaults to { type: 'generic', handled: true } event.level = Severity.Error; if (hint && hint.event_id) { @@ -174,13 +162,14 @@ export function eventFromException( * @hidden */ export function eventFromMessage( + stackParser: StackParser, message: string, level: Severity = Severity.Info, hint?: EventHint, attachStacktrace?: boolean, ): PromiseLike { const syntheticException = (hint && hint.syntheticException) || undefined; - const event = eventFromString(message, syntheticException, attachStacktrace); + const event = eventFromString(stackParser, message, syntheticException, attachStacktrace); event.level = level; if (hint && hint.event_id) { event.event_id = hint.event_id; @@ -192,6 +181,7 @@ export function eventFromMessage( * @hidden */ export function eventFromUnknownInput( + stackParser: StackParser, exception: unknown, syntheticException?: Error, attachStacktrace?: boolean, @@ -202,7 +192,7 @@ export function eventFromUnknownInput( if (isErrorEvent(exception as ErrorEvent) && (exception as ErrorEvent).error) { // If it is an ErrorEvent with `error` property, extract it to get actual Error const errorEvent = exception as ErrorEvent; - return eventFromError(errorEvent.error as Error); + return eventFromError(stackParser, errorEvent.error as Error); } // If it is a `DOMError` (which is a legacy API, but still supported in some browsers) then we just extract the name @@ -216,11 +206,11 @@ export function eventFromUnknownInput( const domException = exception as DOMException; if ('stack' in (exception as Error)) { - event = eventFromError(exception as Error); + event = eventFromError(stackParser, exception as Error); } else { const name = domException.name || (isDOMError(domException) ? 'DOMError' : 'DOMException'); const message = domException.message ? `${name}: ${domException.message}` : name; - event = eventFromString(message, syntheticException, attachStacktrace); + event = eventFromString(stackParser, message, syntheticException, attachStacktrace); addExceptionTypeValue(event, message); } if ('code' in domException) { @@ -231,14 +221,14 @@ export function eventFromUnknownInput( } if (isError(exception)) { // we have a real Error object, do nothing - return eventFromError(exception); + return eventFromError(stackParser, exception); } if (isPlainObject(exception) || isEvent(exception)) { // If it's a plain object or an instance of `Event` (the built-in JS kind, not this SDK's `Event` type), serialize // it manually. This will allow us to group events based on top-level keys which is much better than creating a new // group on any key/value change. const objectException = exception as Record; - event = eventFromPlainObject(objectException, syntheticException, isUnhandledRejection); + event = eventFromPlainObject(stackParser, objectException, syntheticException, isUnhandledRejection); addExceptionMechanism(event, { synthetic: true, }); @@ -254,7 +244,7 @@ export function eventFromUnknownInput( // - a plain Object // // So bail out and capture it as a simple message: - event = eventFromString(exception as string, syntheticException, attachStacktrace); + event = eventFromString(stackParser, exception as string, syntheticException, attachStacktrace); addExceptionTypeValue(event, `${exception}`, undefined); addExceptionMechanism(event, { synthetic: true, @@ -266,13 +256,18 @@ export function eventFromUnknownInput( /** * @hidden */ -export function eventFromString(input: string, syntheticException?: Error, attachStacktrace?: boolean): Event { +export function eventFromString( + stackParser: StackParser, + input: string, + syntheticException?: Error, + attachStacktrace?: boolean, +): Event { const event: Event = { message: input, }; if (attachStacktrace && syntheticException) { - const frames = parseStackFrames(syntheticException); + const frames = parseStackFrames(stackParser, syntheticException); if (frames.length) { event.exception = { values: [{ value: input, stacktrace: { frames } }], diff --git a/packages/browser/src/exports.ts b/packages/browser/src/exports.ts index 6204a25f614e..704096fae520 100644 --- a/packages/browser/src/exports.ts +++ b/packages/browser/src/exports.ts @@ -42,6 +42,15 @@ export { } from '@sentry/core'; export { BrowserClient, BrowserOptions } from './client'; + +export { + defaultStackParsers, + chromeStackParser, + geckoStackParser, + opera10StackParser, + opera11StackParser, + winjsStackParser, +} from './stack-parsers'; export { injectReportDialog, ReportDialogOptions } from './helpers'; export { defaultIntegrations, forceLoad, init, lastEventId, onLoad, showReportDialog, flush, close, wrap } from './sdk'; export { SDK_NAME } from './version'; diff --git a/packages/browser/src/integrations/globalhandlers.ts b/packages/browser/src/integrations/globalhandlers.ts index 19ee594afaf3..70966e4f7ec5 100644 --- a/packages/browser/src/integrations/globalhandlers.ts +++ b/packages/browser/src/integrations/globalhandlers.ts @@ -1,6 +1,6 @@ /* eslint-disable @typescript-eslint/no-unsafe-member-access */ import { getCurrentHub } from '@sentry/core'; -import { Event, EventHint, Hub, Integration, Primitive, Severity } from '@sentry/types'; +import { Event, EventHint, Hub, Integration, Primitive, Severity, StackParser } from '@sentry/types'; import { addExceptionMechanism, addInstrumentationHandler, @@ -9,8 +9,10 @@ import { isPrimitive, isString, logger, + stackParserFromOptions, } from '@sentry/utils'; +import { BrowserClient } from '../client'; import { eventFromUnknownInput } from '../eventbuilder'; import { IS_DEBUG_BUILD } from '../flags'; import { shouldIgnoreOnError } from '../helpers'; @@ -79,7 +81,7 @@ function _installGlobalOnErrorHandler(): void { 'error', // eslint-disable-next-line @typescript-eslint/no-explicit-any (data: { msg: any; url: any; line: any; column: any; error: any }) => { - const [hub, attachStacktrace] = getHubAndAttachStacktrace(); + const [hub, stackParser, attachStacktrace] = getHubAndOptions(); if (!hub.getIntegration(GlobalHandlers)) { return; } @@ -92,7 +94,7 @@ function _installGlobalOnErrorHandler(): void { error === undefined && isString(msg) ? _eventFromIncompleteOnError(msg, url, line, column) : _enhanceEventWithInitialFrame( - eventFromUnknownInput(error || msg, undefined, attachStacktrace, false), + eventFromUnknownInput(stackParser, error || msg, undefined, attachStacktrace, false), url, line, column, @@ -111,7 +113,7 @@ function _installGlobalOnUnhandledRejectionHandler(): void { 'unhandledrejection', // eslint-disable-next-line @typescript-eslint/no-explicit-any (e: any) => { - const [hub, attachStacktrace] = getHubAndAttachStacktrace(); + const [hub, stackParser, attachStacktrace] = getHubAndOptions(); if (!hub.getIntegration(GlobalHandlers)) { return; } @@ -142,7 +144,7 @@ function _installGlobalOnUnhandledRejectionHandler(): void { const event = isPrimitive(error) ? _eventFromRejectionWithPrimitive(error) - : eventFromUnknownInput(error, undefined, attachStacktrace, true); + : eventFromUnknownInput(stackParser, error, undefined, attachStacktrace, true); event.level = Severity.Error; @@ -250,9 +252,11 @@ function addMechanismAndCapture(hub: Hub, error: EventHint['originalException'], }); } -function getHubAndAttachStacktrace(): [Hub, boolean | undefined] { +function getHubAndOptions(): [Hub, StackParser, boolean | undefined] { const hub = getCurrentHub(); - const client = hub.getClient(); - const attachStacktrace = client && client.getOptions().attachStacktrace; - return [hub, attachStacktrace]; + const client = hub.getClient(); + const options = client?.getOptions(); + const parser = stackParserFromOptions(options); + const attachStacktrace = options?.attachStacktrace; + return [hub, parser, attachStacktrace]; } diff --git a/packages/browser/src/integrations/linkederrors.ts b/packages/browser/src/integrations/linkederrors.ts index fa197b48e681..1cc30c182950 100644 --- a/packages/browser/src/integrations/linkederrors.ts +++ b/packages/browser/src/integrations/linkederrors.ts @@ -1,7 +1,8 @@ import { addGlobalEventProcessor, getCurrentHub } from '@sentry/core'; -import { Event, EventHint, Exception, ExtendedError, Integration } from '@sentry/types'; -import { isInstanceOf } from '@sentry/utils'; +import { Event, EventHint, Exception, ExtendedError, Integration, StackParser } from '@sentry/types'; +import { isInstanceOf, stackParserFromOptions } from '@sentry/utils'; +import { BrowserClient } from '../client'; import { exceptionFromError } from '../eventbuilder'; const DEFAULT_KEY = 'cause'; @@ -46,9 +47,12 @@ export class LinkedErrors implements Integration { * @inheritDoc */ public setupOnce(): void { + const options = getCurrentHub().getClient()?.getOptions(); + const parser = stackParserFromOptions(options); + addGlobalEventProcessor((event: Event, hint?: EventHint) => { const self = getCurrentHub().getIntegration(LinkedErrors); - return self ? _handler(self._key, self._limit, event, hint) : event; + return self ? _handler(parser, self._key, self._limit, event, hint) : event; }); } } @@ -56,11 +60,17 @@ export class LinkedErrors implements Integration { /** * @inheritDoc */ -export function _handler(key: string, limit: number, event: Event, hint?: EventHint): Event | null { +export function _handler( + parser: StackParser, + key: string, + limit: number, + event: Event, + hint?: EventHint, +): Event | null { if (!event.exception || !event.exception.values || !hint || !isInstanceOf(hint.originalException, Error)) { return event; } - const linkedErrors = _walkErrorTree(limit, hint.originalException as ExtendedError, key); + const linkedErrors = _walkErrorTree(parser, limit, hint.originalException as ExtendedError, key); event.exception.values = [...linkedErrors, ...event.exception.values]; return event; } @@ -68,10 +78,16 @@ export function _handler(key: string, limit: number, event: Event, hint?: EventH /** * JSDOC */ -export function _walkErrorTree(limit: number, error: ExtendedError, key: string, stack: Exception[] = []): Exception[] { +export function _walkErrorTree( + parser: StackParser, + limit: number, + error: ExtendedError, + key: string, + stack: Exception[] = [], +): Exception[] { if (!isInstanceOf(error[key], Error) || stack.length + 1 >= limit) { return stack; } - const exception = exceptionFromError(error[key]); - return _walkErrorTree(limit, error[key], key, [exception, ...stack]); + const exception = exceptionFromError(parser, error[key]); + return _walkErrorTree(parser, limit, error[key], key, [exception, ...stack]); } diff --git a/packages/browser/src/sdk.ts b/packages/browser/src/sdk.ts index 44d66add18c3..60c53b364e9c 100644 --- a/packages/browser/src/sdk.ts +++ b/packages/browser/src/sdk.ts @@ -6,6 +6,7 @@ import { BrowserClient, BrowserOptions } from './client'; import { IS_DEBUG_BUILD } from './flags'; import { ReportDialogOptions, wrap as internalWrap } from './helpers'; import { Breadcrumbs, Dedupe, GlobalHandlers, LinkedErrors, TryCatch, UserAgent } from './integrations'; +import { defaultStackParsers } from './stack-parsers'; export const defaultIntegrations = [ new CoreIntegrations.InboundFilters(), @@ -92,6 +93,9 @@ export function init(options: BrowserOptions = {}): void { if (options.sendClientReports === undefined) { options.sendClientReports = true; } + if (options.stackParser === undefined) { + options.stackParser = defaultStackParsers; + } initAndBind(BrowserClient, options); diff --git a/packages/browser/src/stack-parsers.ts b/packages/browser/src/stack-parsers.ts index f54c1df803e9..5ee652001854 100644 --- a/packages/browser/src/stack-parsers.ts +++ b/packages/browser/src/stack-parsers.ts @@ -1,5 +1,4 @@ -import { StackFrame } from '@sentry/types'; -import { StackLineParser, StackLineParserFn } from '@sentry/utils'; +import { StackFrame, StackLineParser, StackLineParserFn } from '@sentry/types'; // global reference to slice const UNKNOWN_FUNCTION = '?'; @@ -131,6 +130,14 @@ const opera11: StackLineParserFn = line => { export const opera11StackParser: StackLineParser = [OPERA11_PRIORITY, opera11]; +export const defaultStackParsers = [ + chromeStackParser, + geckoStackParser, + opera10StackParser, + opera11StackParser, + winjsStackParser, +]; + /** * Safari web extensions, starting version unknown, can produce "frames-only" stacktraces. * What it means, is that instead of format like: diff --git a/packages/browser/test/unit/integrations/linkederrors.test.ts b/packages/browser/test/unit/integrations/linkederrors.test.ts index 8178741d1210..2589487dbcac 100644 --- a/packages/browser/test/unit/integrations/linkederrors.test.ts +++ b/packages/browser/test/unit/integrations/linkederrors.test.ts @@ -1,7 +1,11 @@ import { ExtendedError } from '@sentry/types'; +import { createStackParser } from '@sentry/utils'; import { BrowserClient } from '../../../src/client'; import * as LinkedErrorsModule from '../../../src/integrations/linkederrors'; +import { defaultStackParsers } from '../../../src/stack-parsers'; + +const parser = createStackParser(...defaultStackParsers); describe('LinkedErrors', () => { describe('handler', () => { @@ -9,7 +13,7 @@ describe('LinkedErrors', () => { const event = { message: 'foo', }; - const result = LinkedErrorsModule._handler('cause', 5, event); + const result = LinkedErrorsModule._handler(parser, 'cause', 5, event); expect(result).toEqual(event); }); @@ -20,7 +24,7 @@ describe('LinkedErrors', () => { }, message: 'foo', }; - const result = LinkedErrorsModule._handler('cause', 5, event); + const result = LinkedErrorsModule._handler(parser, 'cause', 5, event); expect(result).toEqual(event); }); @@ -34,9 +38,9 @@ describe('LinkedErrors', () => { one.cause = two; const originalException = one; - const client = new BrowserClient({}); + const client = new BrowserClient({ stackParser: parser }); return client.eventFromException(originalException).then(event => { - const result = LinkedErrorsModule._handler('cause', 5, event, { + const result = LinkedErrorsModule._handler(parser, 'cause', 5, event, { originalException, }); @@ -64,9 +68,9 @@ describe('LinkedErrors', () => { one.reason = two; const originalException = one; - const client = new BrowserClient({}); + const client = new BrowserClient({ stackParser: parser }); return client.eventFromException(originalException).then(event => { - const result = LinkedErrorsModule._handler('reason', 5, event, { + const result = LinkedErrorsModule._handler(parser, 'reason', 5, event, { originalException, }); @@ -90,10 +94,10 @@ describe('LinkedErrors', () => { one.cause = two; two.cause = three; - const client = new BrowserClient({}); + const client = new BrowserClient({ stackParser: parser }); const originalException = one; return client.eventFromException(originalException).then(event => { - const result = LinkedErrorsModule._handler('cause', 2, event, { + const result = LinkedErrorsModule._handler(parser, 'cause', 2, event, { originalException, }); diff --git a/packages/browser/test/unit/tracekit/chromium.test.ts b/packages/browser/test/unit/tracekit/chromium.test.ts index e40abffd8d1f..67189984563a 100644 --- a/packages/browser/test/unit/tracekit/chromium.test.ts +++ b/packages/browser/test/unit/tracekit/chromium.test.ts @@ -1,9 +1,14 @@ +import { createStackParser } from '@sentry/utils'; + import { exceptionFromError } from '../../../src/eventbuilder'; +import { defaultStackParsers } from '../../../src/stack-parsers'; + +const parser = createStackParser(...defaultStackParsers); describe('Tracekit - Chrome Tests', () => { it('should parse Chrome error with no location', () => { const NO_LOCATION = { message: 'foo', name: 'bar', stack: 'error\n at Array.forEach (native)' }; - const ex = exceptionFromError(NO_LOCATION); + const ex = exceptionFromError(parser, NO_LOCATION); expect(ex).toEqual({ value: 'foo', @@ -25,7 +30,7 @@ describe('Tracekit - Chrome Tests', () => { ' at http://path/to/file.js:24:4', }; - const ex = exceptionFromError(CHROME_15); + const ex = exceptionFromError(parser, CHROME_15); expect(ex).toEqual({ value: "Object # has no method 'undef'", @@ -52,7 +57,7 @@ describe('Tracekit - Chrome Tests', () => { ' at I.e.fn.(anonymous function) [as index] (http://localhost:8080/file.js:10:3651)', }; - const ex = exceptionFromError(CHROME_36); + const ex = exceptionFromError(parser, CHROME_36); expect(ex).toEqual({ value: 'Default error', @@ -98,7 +103,7 @@ describe('Tracekit - Chrome Tests', () => { ' at TESTTESTTEST.proxiedMethod(webpack:///./~/react-proxy/modules/createPrototypeProxy.js?:44:30)', }; - const ex = exceptionFromError(CHROME_XX_WEBPACK); + const ex = exceptionFromError(parser, CHROME_XX_WEBPACK); expect(ex).toEqual({ value: "Cannot read property 'error' of undefined", @@ -151,7 +156,7 @@ describe('Tracekit - Chrome Tests', () => { 'at http://localhost:8080/file.js:31:13\n', }; - const ex = exceptionFromError(CHROME_48_EVAL); + const ex = exceptionFromError(parser, CHROME_48_EVAL); expect(ex).toEqual({ value: 'message string', @@ -183,7 +188,7 @@ describe('Tracekit - Chrome Tests', () => { ' at n.handle (blob:http%3A//localhost%3A8080/abfc40e9-4742-44ed-9dcd-af8f99a29379:7:2863)', }; - const ex = exceptionFromError(CHROME_48_BLOB); + const ex = exceptionFromError(parser, CHROME_48_BLOB); expect(ex).toEqual({ value: 'Error: test', @@ -246,7 +251,7 @@ describe('Tracekit - Chrome Tests', () => { at examplescheme://examplehost/cd351f7250857e22ceaa.worker.js:70179:15`, }; - const ex = exceptionFromError(CHROMIUM_EMBEDDED_FRAMEWORK_CUSTOM_SCHEME); + const ex = exceptionFromError(parser, CHROMIUM_EMBEDDED_FRAMEWORK_CUSTOM_SCHEME); expect(ex).toEqual({ value: 'message string', @@ -276,7 +281,7 @@ describe('Tracekit - Chrome Tests', () => { at http://localhost:5000/test:24:7`, }; - const ex = exceptionFromError(CHROME73_NATIVE_CODE_EXCEPTION); + const ex = exceptionFromError(parser, CHROME73_NATIVE_CODE_EXCEPTION); expect(ex).toEqual({ value: 'test', @@ -309,7 +314,7 @@ describe('Tracekit - Chrome Tests', () => { at http://localhost:5000/:50:19`, }; - const ex = exceptionFromError(CHROME73_EVAL_EXCEPTION); + const ex = exceptionFromError(parser, CHROME73_EVAL_EXCEPTION); expect(ex).toEqual({ value: 'bad', @@ -342,7 +347,7 @@ describe('Tracekit - Chrome Tests', () => { at Global code (http://localhost:5000/test:24:7)`, }; - const ex = exceptionFromError(EDGE44_NATIVE_CODE_EXCEPTION); + const ex = exceptionFromError(parser, EDGE44_NATIVE_CODE_EXCEPTION); expect(ex).toEqual({ value: 'test', @@ -375,7 +380,7 @@ describe('Tracekit - Chrome Tests', () => { at Anonymous function (http://localhost:5000/:50:8)`, }; - const ex = exceptionFromError(EDGE44_EVAL_EXCEPTION); + const ex = exceptionFromError(parser, EDGE44_EVAL_EXCEPTION); expect(ex).toEqual({ value: 'aha', @@ -411,7 +416,7 @@ describe('Tracekit - Chrome Tests', () => { at TESTTESTTEST.someMethod (C:\\Users\\user\\path\\to\\file.js:295:108)`, }; - const ex = exceptionFromError(CHROME_ELECTRON_RENDERER); + const ex = exceptionFromError(parser, CHROME_ELECTRON_RENDERER); expect(ex).toEqual({ value: "Cannot read property 'error' of undefined", diff --git a/packages/browser/test/unit/tracekit/firefox.test.ts b/packages/browser/test/unit/tracekit/firefox.test.ts index a14fae1e38cc..87929568c857 100644 --- a/packages/browser/test/unit/tracekit/firefox.test.ts +++ b/packages/browser/test/unit/tracekit/firefox.test.ts @@ -1,4 +1,9 @@ +import { createStackParser } from '@sentry/utils'; + import { exceptionFromError } from '../../../src/eventbuilder'; +import { defaultStackParsers } from '../../../src/stack-parsers'; + +const parser = createStackParser(...defaultStackParsers); describe('Tracekit - Firefox Tests', () => { it('should parse Firefox 3 error', () => { @@ -18,7 +23,7 @@ describe('Tracekit - Firefox Tests', () => { '', }; - const ex = exceptionFromError(FIREFOX_3); + const ex = exceptionFromError(parser, FIREFOX_3); expect(ex).toEqual({ value: 'this.undef is not a function', @@ -54,7 +59,7 @@ describe('Tracekit - Firefox Tests', () => { '', }; - const ex = exceptionFromError(FIREFOX_7); + const ex = exceptionFromError(parser, FIREFOX_7); expect(ex).toEqual({ value: 'bar', @@ -86,7 +91,7 @@ describe('Tracekit - Firefox Tests', () => { lineNumber: 48, }; - const ex = exceptionFromError(FIREFOX_14); + const ex = exceptionFromError(parser, FIREFOX_14); expect(ex).toEqual({ value: 'x is null', @@ -115,7 +120,7 @@ describe('Tracekit - Firefox Tests', () => { columnNumber: 12, }; - const ex = exceptionFromError(FIREFOX_31); + const ex = exceptionFromError(parser, FIREFOX_31); expect(ex).toEqual({ value: 'Default error', @@ -150,7 +155,7 @@ describe('Tracekit - Firefox Tests', () => { result: 2147500037, }; - const ex = exceptionFromError(FIREFOX_44_NS_EXCEPTION); + const ex = exceptionFromError(parser, FIREFOX_44_NS_EXCEPTION); expect(ex).toEqual({ value: 'No error message', @@ -185,7 +190,7 @@ describe('Tracekit - Firefox Tests', () => { name: 'TypeError', }; - const ex = exceptionFromError(FIREFOX_50_RESOURCE_URL); + const ex = exceptionFromError(parser, FIREFOX_50_RESOURCE_URL); expect(ex).toEqual({ value: 'this.props.raw[this.state.dataSource].rows is undefined', @@ -233,7 +238,7 @@ describe('Tracekit - Firefox Tests', () => { '@http://localhost:8080/file.js:33:9', }; - const ex = exceptionFromError(FIREFOX_43_EVAL); + const ex = exceptionFromError(parser, FIREFOX_43_EVAL); expect(ex).toEqual({ value: 'message string', @@ -259,7 +264,7 @@ describe('Tracekit - Firefox Tests', () => { @http://localhost:5000/test:24:7`, }; - const stacktrace = exceptionFromError(FIREFOX66_NATIVE_CODE_EXCEPTION); + const stacktrace = exceptionFromError(parser, FIREFOX66_NATIVE_CODE_EXCEPTION); expect(stacktrace).toEqual({ value: 'test', @@ -289,7 +294,7 @@ describe('Tracekit - Firefox Tests', () => { @http://localhost:5000/:50:19`, }; - const stacktrace = exceptionFromError(FIREFOX66_EVAL_EXCEPTION); + const stacktrace = exceptionFromError(parser, FIREFOX66_EVAL_EXCEPTION); expect(stacktrace).toEqual({ value: 'aha', diff --git a/packages/browser/test/unit/tracekit/ie.test.ts b/packages/browser/test/unit/tracekit/ie.test.ts index cfd60ab2e6c4..9a796060f1cc 100644 --- a/packages/browser/test/unit/tracekit/ie.test.ts +++ b/packages/browser/test/unit/tracekit/ie.test.ts @@ -1,4 +1,9 @@ +import { createStackParser } from '@sentry/utils'; + import { exceptionFromError } from '../../../src/eventbuilder'; +import { defaultStackParsers } from '../../../src/stack-parsers'; + +const parser = createStackParser(...defaultStackParsers); describe('Tracekit - IE Tests', () => { it('should parse IE 10 error', () => { @@ -14,7 +19,7 @@ describe('Tracekit - IE Tests', () => { number: -2146823281, }; - const ex = exceptionFromError(IE_10); + const ex = exceptionFromError(parser, IE_10); // TODO: func should be normalized expect(ex).toEqual({ @@ -43,7 +48,7 @@ describe('Tracekit - IE Tests', () => { number: -2146823281, }; - const ex = exceptionFromError(IE_11); + const ex = exceptionFromError(parser, IE_11); // TODO: func should be normalized expect(ex).toEqual({ @@ -72,7 +77,7 @@ describe('Tracekit - IE Tests', () => { number: -2146823279, }; - const ex = exceptionFromError(IE_11_EVAL); + const ex = exceptionFromError(parser, IE_11_EVAL); expect(ex).toEqual({ value: "'getExceptionProps' is undefined", diff --git a/packages/browser/test/unit/tracekit/misc.test.ts b/packages/browser/test/unit/tracekit/misc.test.ts index 3aa59754cc9a..976f39e2449e 100644 --- a/packages/browser/test/unit/tracekit/misc.test.ts +++ b/packages/browser/test/unit/tracekit/misc.test.ts @@ -1,4 +1,9 @@ +import { createStackParser } from '@sentry/utils'; + import { exceptionFromError } from '../../../src/eventbuilder'; +import { defaultStackParsers } from '../../../src/stack-parsers'; + +const parser = createStackParser(...defaultStackParsers); describe('Tracekit - Misc Tests', () => { it('should parse PhantomJS 1.19 error', () => { @@ -11,7 +16,7 @@ describe('Tracekit - Misc Tests', () => { ' at foo (http://path/to/file.js:4283)\n' + ' at http://path/to/file.js:4287', }; - const ex = exceptionFromError(PHANTOMJS_1_19); + const ex = exceptionFromError(parser, PHANTOMJS_1_19); expect(ex).toEqual({ value: 'bar', diff --git a/packages/browser/test/unit/tracekit/opera.test.ts b/packages/browser/test/unit/tracekit/opera.test.ts index 472c4a55e2ca..a97675824e18 100644 --- a/packages/browser/test/unit/tracekit/opera.test.ts +++ b/packages/browser/test/unit/tracekit/opera.test.ts @@ -1,4 +1,9 @@ +import { createStackParser } from '@sentry/utils'; + import { exceptionFromError } from '../../../src/eventbuilder'; +import { defaultStackParsers } from '../../../src/stack-parsers'; + +const parser = createStackParser(...defaultStackParsers); describe('Tracekit - Opera Tests', () => { it('should parse Opera 10 error', () => { @@ -24,7 +29,7 @@ describe('Tracekit - Opera Tests', () => { '', }; - const ex = exceptionFromError(OPERA_10); + const ex = exceptionFromError(parser, OPERA_10); expect(ex).toEqual({ value: 'Statement on line 42: Type mismatch (usually non-object value supplied where object required)', @@ -70,7 +75,7 @@ describe('Tracekit - Opera Tests', () => { ' foo();', }; - const ex = exceptionFromError(OPERA_11); + const ex = exceptionFromError(parser, OPERA_11); expect(ex).toEqual({ value: "'this.undef' is not a function", @@ -107,7 +112,7 @@ describe('Tracekit - Opera Tests', () => { ' dumpException3();', }; - const ex = exceptionFromError(OPERA_12); + const ex = exceptionFromError(parser, OPERA_12); expect(ex).toEqual({ value: "Cannot convert 'x' to object", @@ -151,7 +156,7 @@ describe('Tracekit - Opera Tests', () => { ' at bar (http://path/to/file.js:108:168)', }; - const ex = exceptionFromError(OPERA_25); + const ex = exceptionFromError(parser, OPERA_25); expect(ex).toEqual({ value: "Cannot read property 'undef' of null", diff --git a/packages/browser/test/unit/tracekit/react-native.test.ts b/packages/browser/test/unit/tracekit/react-native.test.ts index 6935acd615fd..ac469a92246a 100644 --- a/packages/browser/test/unit/tracekit/react-native.test.ts +++ b/packages/browser/test/unit/tracekit/react-native.test.ts @@ -1,4 +1,9 @@ +import { createStackParser } from '@sentry/utils'; + import { exceptionFromError } from '../../../src/eventbuilder'; +import { defaultStackParsers } from '../../../src/stack-parsers'; + +const parser = createStackParser(...defaultStackParsers); describe('Tracekit - React Native Tests', () => { it('should parse exceptions for react-native-v8', () => { @@ -14,7 +19,7 @@ describe('Tracekit - React Native Tests', () => { at Object.y(index.android.bundle:93:571) at P(index.android.bundle:93:714)`, }; - const stacktrace = exceptionFromError(REACT_NATIVE_V8_EXCEPTION); + const stacktrace = exceptionFromError(parser, REACT_NATIVE_V8_EXCEPTION); expect(stacktrace).toEqual({ value: 'Manually triggered crash to test Sentry reporting', @@ -61,7 +66,7 @@ describe('Tracekit - React Native Tests', () => { p@/data/user/0/com.sentrytest/files/.expo-internal/bundle-613EDD44F3305B9D75D4679663900F2BCDDDC326F247CA3202A3A4219FD412D3:96:385 forEach@[native code]`, }; - const stacktrace = exceptionFromError(REACT_NATIVE_EXPO_EXCEPTION); + const stacktrace = exceptionFromError(parser, REACT_NATIVE_EXPO_EXCEPTION); expect(stacktrace).toEqual({ value: 'Test Error Expo', @@ -122,7 +127,7 @@ describe('Tracekit - React Native Tests', () => { 'at this(/home/username/sample-workspace/sampleapp.collect.react/node_modules/react-native/Libraries/Renderer/src/renderers/native/ReactNativeBaseComponent.js:74:41)\n', }; - const ex = exceptionFromError(ANDROID_REACT_NATIVE); + const ex = exceptionFromError(parser, ANDROID_REACT_NATIVE); expect(ex).toEqual({ value: 'Error: test', @@ -241,7 +246,7 @@ describe('Tracekit - React Native Tests', () => { '[native code]', }; - const ex = exceptionFromError(ANDROID_REACT_NATIVE_PROD); + const ex = exceptionFromError(parser, ANDROID_REACT_NATIVE_PROD); expect(ex).toEqual({ value: 'Error: test', @@ -352,7 +357,7 @@ describe('Tracekit - React Native Tests', () => { 'at value (address at index.android.bundle:1:32776)\n' + 'at value (address at index.android.bundle:1:31561)', }; - const ex = exceptionFromError(ANDROID_REACT_NATIVE_HERMES); + const ex = exceptionFromError(parser, ANDROID_REACT_NATIVE_HERMES); expect(ex).toEqual({ value: 'Error: lets throw!', diff --git a/packages/browser/test/unit/tracekit/react.test.ts b/packages/browser/test/unit/tracekit/react.test.ts index dba60cceab4f..55229b333403 100644 --- a/packages/browser/test/unit/tracekit/react.test.ts +++ b/packages/browser/test/unit/tracekit/react.test.ts @@ -1,4 +1,9 @@ +import { createStackParser } from '@sentry/utils'; + import { exceptionFromError } from '../../../src/eventbuilder'; +import { defaultStackParsers } from '../../../src/stack-parsers'; + +const parser = createStackParser(...defaultStackParsers); describe('Tracekit - React Tests', () => { it('should correctly parse Invariant Violation errors and use framesToPop to drop info message', () => { @@ -14,7 +19,7 @@ describe('Tracekit - React Tests', () => { at f (http://localhost:5000/:1:980)`, }; - const ex = exceptionFromError(REACT_INVARIANT_VIOLATION_EXCEPTION); + const ex = exceptionFromError(parser, REACT_INVARIANT_VIOLATION_EXCEPTION); expect(ex).toEqual({ value: @@ -61,7 +66,7 @@ describe('Tracekit - React Tests', () => { at f (http://localhost:5000/:1:980)`, }; - const ex = exceptionFromError(REACT_PRODUCTION_ERROR); + const ex = exceptionFromError(parser, REACT_PRODUCTION_ERROR); expect(ex).toEqual({ value: @@ -109,7 +114,7 @@ describe('Tracekit - React Tests', () => { at f (http://localhost:5000/:1:980)`, }; - const ex = exceptionFromError(REACT_PRODUCTION_ERROR); + const ex = exceptionFromError(parser, REACT_PRODUCTION_ERROR); expect(ex).toEqual({ value: diff --git a/packages/browser/test/unit/tracekit/safari.test.ts b/packages/browser/test/unit/tracekit/safari.test.ts index 6342899e1ca4..beff492a6c1d 100644 --- a/packages/browser/test/unit/tracekit/safari.test.ts +++ b/packages/browser/test/unit/tracekit/safari.test.ts @@ -1,4 +1,9 @@ +import { createStackParser } from '@sentry/utils'; + import { exceptionFromError } from '../../../src/eventbuilder'; +import { defaultStackParsers } from '../../../src/stack-parsers'; + +const parser = createStackParser(...defaultStackParsers); describe('Tracekit - Safari Tests', () => { it('should parse Safari 6 error', () => { @@ -14,7 +19,7 @@ describe('Tracekit - Safari Tests', () => { sourceURL: 'http://path/to/file.js', }; - const stackFrames = exceptionFromError(SAFARI_6); + const stackFrames = exceptionFromError(parser, SAFARI_6); expect(stackFrames).toEqual({ value: "'null' is not an object (evaluating 'x.undef')", @@ -40,7 +45,7 @@ describe('Tracekit - Safari Tests', () => { sourceURL: 'http://path/to/file.js', }; - const stackFrames = exceptionFromError(SAFARI_7); + const stackFrames = exceptionFromError(parser, SAFARI_7); expect(stackFrames).toEqual({ value: "'null' is not an object (evaluating 'x.undef')", @@ -66,7 +71,7 @@ describe('Tracekit - Safari Tests', () => { sourceURL: 'http://path/to/file.js', }; - const stackFrames = exceptionFromError(SAFARI_8); + const stackFrames = exceptionFromError(parser, SAFARI_8); expect(stackFrames).toEqual({ value: "null is not an object (evaluating 'x.undef')", @@ -96,7 +101,7 @@ describe('Tracekit - Safari Tests', () => { column: 18, }; - const stackFrames = exceptionFromError(SAFARI_8_EVAL); + const stackFrames = exceptionFromError(parser, SAFARI_8_EVAL); expect(stackFrames).toEqual({ value: "Can't find variable: getExceptionProps", @@ -121,7 +126,7 @@ describe('Tracekit - Safari Tests', () => { at safari-extension:(//3284871F-A480-4FFC-8BC4-3F362C752446/2665fee0/topee-content.js:3313:26)`, }; - const ex = exceptionFromError(SAFARI_EXTENSION_EXCEPTION); + const ex = exceptionFromError(parser, SAFARI_EXTENSION_EXCEPTION); expect(ex).toEqual({ value: 'wat', @@ -155,7 +160,7 @@ describe('Tracekit - Safari Tests', () => { safari-extension://com.grammarly.safari.extension.ext2-W8F64X92K3/ee7759dd/Grammarly.js:2:1588410 promiseReactionJob@[native code]`, }; - const ex = exceptionFromError(SAFARI_EXTENSION_EXCEPTION); + const ex = exceptionFromError(parser, SAFARI_EXTENSION_EXCEPTION); expect(ex).toEqual({ value: "undefined is not an object (evaluating 'e.groups.includes')", @@ -191,7 +196,7 @@ describe('Tracekit - Safari Tests', () => { at safari-web-extension:(//3284871F-A480-4FFC-8BC4-3F362C752446/2665fee0/topee-content.js:3313:26)`, }; - const ex = exceptionFromError(SAFARI_WEB_EXTENSION_EXCEPTION); + const ex = exceptionFromError(parser, SAFARI_WEB_EXTENSION_EXCEPTION); expect(ex).toEqual({ value: 'wat', @@ -225,7 +230,7 @@ describe('Tracekit - Safari Tests', () => { safari-web-extension://46434E60-F5BD-48A4-80C8-A422C5D16897/scripts/content-script.js:29:56027 promiseReactionJob@[native code]`, }; - const ex = exceptionFromError(SAFARI_EXTENSION_EXCEPTION); + const ex = exceptionFromError(parser, SAFARI_EXTENSION_EXCEPTION); expect(ex).toEqual({ value: "undefined is not an object (evaluating 'e.groups.includes')", @@ -263,7 +268,7 @@ describe('Tracekit - Safari Tests', () => { global code@http://localhost:5000/test:24:10`, }; - const ex = exceptionFromError(SAFARI12_NATIVE_CODE_EXCEPTION); + const ex = exceptionFromError(parser, SAFARI12_NATIVE_CODE_EXCEPTION); expect(ex).toEqual({ value: 'test', @@ -297,7 +302,7 @@ describe('Tracekit - Safari Tests', () => { http://localhost:5000/:50:29`, }; - const ex = exceptionFromError(SAFARI12_EVAL_EXCEPTION); + const ex = exceptionFromError(parser, SAFARI12_EVAL_EXCEPTION); expect(ex).toEqual({ value: 'aha', diff --git a/packages/node/src/client.ts b/packages/node/src/client.ts index 4401f4016520..81f808f5ef56 100644 --- a/packages/node/src/client.ts +++ b/packages/node/src/client.ts @@ -1,7 +1,7 @@ import { BaseClient, getEnvelopeEndpointWithUrlEncodedAuth, initAPIDetails, Scope, SDK_VERSION } from '@sentry/core'; import { SessionFlusher } from '@sentry/hub'; import { Event, EventHint, Severity, Transport, TransportOptions } from '@sentry/types'; -import { logger, makeDsn, resolvedSyncPromise } from '@sentry/utils'; +import { logger, makeDsn, resolvedSyncPromise, stackParserFromOptions } from '@sentry/utils'; import { eventFromMessage, eventFromUnknownInput } from './eventbuilder'; import { IS_DEBUG_BUILD } from './flags'; @@ -112,14 +112,16 @@ export class NodeClient extends BaseClient { */ // eslint-disable-next-line @typescript-eslint/no-explicit-any, @typescript-eslint/explicit-module-boundary-types public eventFromException(exception: any, hint?: EventHint): PromiseLike { - return resolvedSyncPromise(eventFromUnknownInput(exception, hint)); + return resolvedSyncPromise(eventFromUnknownInput(stackParserFromOptions(this._options), exception, hint)); } /** * @inheritDoc */ public eventFromMessage(message: string, level: Severity = Severity.Info, hint?: EventHint): PromiseLike { - return resolvedSyncPromise(eventFromMessage(message, level, hint, this._options.attachStacktrace)); + return resolvedSyncPromise( + eventFromMessage(stackParserFromOptions(this._options), message, level, hint, this._options.attachStacktrace), + ); } /** diff --git a/packages/node/src/eventbuilder.ts b/packages/node/src/eventbuilder.ts index 1287f2a47f48..e0b6b16261f0 100644 --- a/packages/node/src/eventbuilder.ts +++ b/packages/node/src/eventbuilder.ts @@ -1,34 +1,31 @@ import { getCurrentHub } from '@sentry/hub'; -import { Event, EventHint, Exception, Mechanism, Severity, StackFrame } from '@sentry/types'; +import { Event, EventHint, Exception, Mechanism, Severity, StackFrame, StackParser } from '@sentry/types'; import { addExceptionMechanism, addExceptionTypeValue, - createStackParser, extractExceptionKeysForMessage, isError, isPlainObject, normalizeToSize, } from '@sentry/utils'; -import { nodeStackParser } from './stack-parser'; - /** * Extracts stack frames from the error.stack string */ -export function parseStackFrames(error: Error): StackFrame[] { - return createStackParser(nodeStackParser)(error.stack || '', 1); +export function parseStackFrames(stackParser: StackParser, error: Error): StackFrame[] { + return stackParser(error.stack || '', 1); } /** * Extracts stack frames from the error and builds a Sentry Exception */ -export function exceptionFromError(error: Error): Exception { +export function exceptionFromError(stackParser: StackParser, error: Error): Exception { const exception: Exception = { type: error.name || error.constructor.name, value: error.message, }; - const frames = parseStackFrames(error); + const frames = parseStackFrames(stackParser, error); if (frames.length) { exception.stacktrace = { frames }; } @@ -40,7 +37,7 @@ export function exceptionFromError(error: Error): Exception { * Builds and Event from a Exception * @hidden */ -export function eventFromUnknownInput(exception: unknown, hint?: EventHint): Event { +export function eventFromUnknownInput(stackParser: StackParser, exception: unknown, hint?: EventHint): Event { // eslint-disable-next-line @typescript-eslint/no-explicit-any let ex: unknown = exception; const providedMechanism: Mechanism | undefined = @@ -73,7 +70,7 @@ export function eventFromUnknownInput(exception: unknown, hint?: EventHint): Eve const event = { exception: { - values: [exceptionFromError(ex as Error)], + values: [exceptionFromError(stackParser, ex as Error)], }, }; @@ -91,6 +88,7 @@ export function eventFromUnknownInput(exception: unknown, hint?: EventHint): Eve * @hidden */ export function eventFromMessage( + stackParser: StackParser, message: string, level: Severity = Severity.Info, hint?: EventHint, @@ -103,7 +101,7 @@ export function eventFromMessage( }; if (attachStacktrace && hint && hint.syntheticException) { - const frames = parseStackFrames(hint.syntheticException); + const frames = parseStackFrames(stackParser, hint.syntheticException); if (frames.length) { event.exception = { values: [ diff --git a/packages/node/src/index.ts b/packages/node/src/index.ts index 591fd4ad911d..d6a924a04c2e 100644 --- a/packages/node/src/index.ts +++ b/packages/node/src/index.ts @@ -46,6 +46,7 @@ export { NodeClient } from './client'; export { defaultIntegrations, init, lastEventId, flush, close, getSentryRelease } from './sdk'; export { deepReadDirSync } from './utils'; export { SDK_NAME } from './version'; +export { nodeStackParser } from './stack-parser'; import { Integrations as CoreIntegrations } from '@sentry/core'; import { getMainCarrier } from '@sentry/hub'; diff --git a/packages/node/src/integrations/linkederrors.ts b/packages/node/src/integrations/linkederrors.ts index 701400594971..c1dfd285db84 100644 --- a/packages/node/src/integrations/linkederrors.ts +++ b/packages/node/src/integrations/linkederrors.ts @@ -1,7 +1,8 @@ import { addGlobalEventProcessor, getCurrentHub } from '@sentry/core'; -import { Event, EventHint, Exception, ExtendedError, Integration } from '@sentry/types'; -import { isInstanceOf, resolvedSyncPromise, SyncPromise } from '@sentry/utils'; +import { Event, EventHint, Exception, ExtendedError, Integration, StackParser } from '@sentry/types'; +import { isInstanceOf, resolvedSyncPromise, stackParserFromOptions, SyncPromise } from '@sentry/utils'; +import { NodeClient } from '../client'; import { exceptionFromError } from '../eventbuilder'; import { ContextLines } from './contextlines'; @@ -42,12 +43,15 @@ export class LinkedErrors implements Integration { * @inheritDoc */ public setupOnce(): void { - addGlobalEventProcessor((event: Event, hint?: EventHint) => { - const self = getCurrentHub().getIntegration(LinkedErrors); + addGlobalEventProcessor(async (event: Event, hint?: EventHint) => { + const hub = getCurrentHub(); + const self = hub.getIntegration(LinkedErrors); + const stackParser = stackParserFromOptions(hub.getClient()?.getOptions()); + if (self) { - const handler = self._handler && self._handler.bind(self); - return typeof handler === 'function' ? handler(event, hint) : event; + await self._handler(stackParser, event, hint); } + return event; }); } @@ -55,13 +59,13 @@ export class LinkedErrors implements Integration { /** * @inheritDoc */ - private _handler(event: Event, hint?: EventHint): PromiseLike { + private _handler(stackParser: StackParser, event: Event, hint?: EventHint): PromiseLike { if (!event.exception || !event.exception.values || !hint || !isInstanceOf(hint.originalException, Error)) { return resolvedSyncPromise(event); } return new SyncPromise(resolve => { - void this._walkErrorTree(hint.originalException as Error, this._key) + void this._walkErrorTree(stackParser, hint.originalException as Error, this._key) .then((linkedErrors: Exception[]) => { if (event && event.exception && event.exception.values) { event.exception.values = [...linkedErrors, ...event.exception.values]; @@ -77,12 +81,17 @@ export class LinkedErrors implements Integration { /** * @inheritDoc */ - private async _walkErrorTree(error: ExtendedError, key: string, stack: Exception[] = []): Promise { + private async _walkErrorTree( + stackParser: StackParser, + error: ExtendedError, + key: string, + stack: Exception[] = [], + ): Promise { if (!isInstanceOf(error[key], Error) || stack.length + 1 >= this._limit) { return Promise.resolve(stack); } - const exception = exceptionFromError(error[key]); + const exception = exceptionFromError(stackParser, error[key]); // If the ContextLines integration is enabled, we add source code context to linked errors // because we can't guarantee the order that integrations are run. @@ -92,7 +101,7 @@ export class LinkedErrors implements Integration { } return new Promise((resolve, reject) => { - void this._walkErrorTree(error[key], key, [exception, ...stack]) + void this._walkErrorTree(stackParser, error[key], key, [exception, ...stack]) .then(resolve) .then(null, () => { reject(); diff --git a/packages/node/src/sdk.ts b/packages/node/src/sdk.ts index 7dd660a44048..095bd3114144 100644 --- a/packages/node/src/sdk.ts +++ b/packages/node/src/sdk.ts @@ -7,6 +7,7 @@ import * as domain from 'domain'; import { NodeClient } from './client'; import { IS_DEBUG_BUILD } from './flags'; import { Console, ContextLines, Http, LinkedErrors, OnUncaughtException, OnUnhandledRejection } from './integrations'; +import { nodeStackParser } from './stack-parser'; import { NodeOptions } from './types'; export const defaultIntegrations = [ @@ -120,6 +121,10 @@ export function init(options: NodeOptions = {}): void { options.autoSessionTracking = true; } + if (options.stackParser === undefined) { + options.stackParser = [nodeStackParser]; + } + // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access, @typescript-eslint/no-explicit-any if ((domain as any).active) { setHubOnCarrier(carrier, getCurrentHub()); diff --git a/packages/node/src/stack-parser.ts b/packages/node/src/stack-parser.ts index a37e38001da3..79a86e9018ee 100644 --- a/packages/node/src/stack-parser.ts +++ b/packages/node/src/stack-parser.ts @@ -1,4 +1,5 @@ -import { basename, dirname, StackLineParser, StackLineParserFn } from '@sentry/utils'; +import { StackLineParser, StackLineParserFn } from '@sentry/types'; +import { basename, dirname } from '@sentry/utils'; /** Gets the module */ function getModule(filename: string | undefined): string | undefined { diff --git a/packages/node/test/context-lines.test.ts b/packages/node/test/context-lines.test.ts index 821c95d7b6b0..55dce7be1615 100644 --- a/packages/node/test/context-lines.test.ts +++ b/packages/node/test/context-lines.test.ts @@ -1,10 +1,14 @@ import { StackFrame } from '@sentry/types'; +import { createStackParser } from '@sentry/utils'; import * as fs from 'fs'; import { parseStackFrames } from '../src/eventbuilder'; import { ContextLines, resetFileContentCache } from '../src/integrations/contextlines'; +import { nodeStackParser } from '../src/stack-parser'; import { getError } from './helper/error'; +const parser = createStackParser(nodeStackParser); + describe('ContextLines', () => { let readFileSpy: jest.SpyInstance; let contextLines: ContextLines; @@ -27,7 +31,7 @@ describe('ContextLines', () => { test('parseStack with same file', async () => { expect.assertions(1); - const frames = parseStackFrames(new Error('test')); + const frames = parseStackFrames(parser, new Error('test')); await addContext(Array.from(frames)); @@ -57,12 +61,12 @@ describe('ContextLines', () => { test('parseStack with adding different file', async () => { expect.assertions(1); - const frames = parseStackFrames(new Error('test')); + const frames = parseStackFrames(parser, new Error('test')); await addContext(frames); const numCalls = readFileSpy.mock.calls.length; - const parsedFrames = parseStackFrames(getError()); + const parsedFrames = parseStackFrames(parser, getError()); await addContext(parsedFrames); const newErrorCalls = readFileSpy.mock.calls.length; @@ -100,7 +104,7 @@ describe('ContextLines', () => { contextLines = new ContextLines({ frameContextLines: 0 }); expect.assertions(1); - const frames = parseStackFrames(new Error('test')); + const frames = parseStackFrames(parser, new Error('test')); await addContext(frames); expect(readFileSpy).toHaveBeenCalledTimes(0); diff --git a/packages/node/test/index.test.ts b/packages/node/test/index.test.ts index 687465802a94..af12a4b35994 100644 --- a/packages/node/test/index.test.ts +++ b/packages/node/test/index.test.ts @@ -1,6 +1,7 @@ import { initAndBind, SDK_VERSION } from '@sentry/core'; import { getMainCarrier } from '@sentry/hub'; import { Integration } from '@sentry/types'; +import { createStackParser } from '@sentry/utils'; import * as domain from 'domain'; import { @@ -16,6 +17,9 @@ import { Scope, } from '../src'; import { ContextLines, LinkedErrors } from '../src/integrations'; +import { nodeStackParser } from '../src/stack-parser'; + +const stackParser = createStackParser(nodeStackParser); jest.mock('@sentry/core', () => { const original = jest.requireActual('@sentry/core'); @@ -86,6 +90,7 @@ describe('SentryNode', () => { test('record auto breadcrumbs', done => { const client = new NodeClient({ + stackParser, beforeSend: (event: Event) => { // TODO: It should be 3, but we don't capture a breadcrumb // for our own captureMessage/captureException calls yet @@ -117,6 +122,7 @@ describe('SentryNode', () => { expect.assertions(6); getCurrentHub().bindClient( new NodeClient({ + stackParser, beforeSend: (event: Event) => { expect(event.tags).toEqual({ test: '1' }); expect(event.exception).not.toBeUndefined(); @@ -144,6 +150,7 @@ describe('SentryNode', () => { expect.assertions(6); getCurrentHub().bindClient( new NodeClient({ + stackParser, beforeSend: (event: Event) => { expect(event.tags).toEqual({ test: '1' }); expect(event.exception).not.toBeUndefined(); @@ -171,6 +178,7 @@ describe('SentryNode', () => { expect.assertions(10); getCurrentHub().bindClient( new NodeClient({ + stackParser, beforeSend: (event: Event) => { expect(event.tags).toEqual({ test: '1' }); expect(event.exception).not.toBeUndefined(); @@ -202,6 +210,7 @@ describe('SentryNode', () => { expect.assertions(15); getCurrentHub().bindClient( new NodeClient({ + stackParser, integrations: [new ContextLines(), new LinkedErrors()], beforeSend: (event: Event) => { expect(event.exception).not.toBeUndefined(); @@ -242,6 +251,7 @@ describe('SentryNode', () => { expect.assertions(2); getCurrentHub().bindClient( new NodeClient({ + stackParser, beforeSend: (event: Event) => { expect(event.message).toBe('test'); expect(event.exception).toBeUndefined(); @@ -258,6 +268,7 @@ describe('SentryNode', () => { expect.assertions(2); getCurrentHub().bindClient( new NodeClient({ + stackParser, beforeSend: (event: Event) => { expect(event.message).toBe('test event'); expect(event.exception).toBeUndefined(); @@ -274,6 +285,7 @@ describe('SentryNode', () => { const d = domain.create(); const client = new NodeClient({ + stackParser, beforeSend: (event: Event) => { expect(event.message).toBe('test domain'); expect(event.exception).toBeUndefined(); @@ -294,6 +306,7 @@ describe('SentryNode', () => { expect.assertions(1); getCurrentHub().bindClient( new NodeClient({ + stackParser, beforeSend: (event: Event) => { expect( event.exception!.values![0].stacktrace!.frames![ diff --git a/packages/node/test/integrations/linkederrors.test.ts b/packages/node/test/integrations/linkederrors.test.ts index 47249d129716..296384a5f222 100644 --- a/packages/node/test/integrations/linkederrors.test.ts +++ b/packages/node/test/integrations/linkederrors.test.ts @@ -1,7 +1,11 @@ import { ExtendedError } from '@sentry/types'; +import { createStackParser } from '@sentry/utils'; import { Event, NodeClient } from '../../src'; import { LinkedErrors } from '../../src/integrations/linkederrors'; +import { nodeStackParser } from '../../src/stack-parser'; + +const stackParser = createStackParser(nodeStackParser); let linkedErrors: any; @@ -17,7 +21,7 @@ describe('LinkedErrors', () => { const event = { message: 'foo', }; - return linkedErrors._handler(event).then((result: any) => { + return linkedErrors._handler(stackParser, event).then((result: any) => { expect(spy.mock.calls.length).toEqual(0); expect(result).toEqual(event); }); @@ -27,13 +31,13 @@ describe('LinkedErrors', () => { expect.assertions(2); const spy = jest.spyOn(linkedErrors, '_walkErrorTree'); const one = new Error('originalException'); - const client = new NodeClient({}); + const client = new NodeClient({ stackParser }); let event: Event | undefined; return client .eventFromException(one) .then(eventFromException => { event = eventFromException; - return linkedErrors._handler(eventFromException); + return linkedErrors._handler(stackParser, eventFromException); }) .then(result => { expect(spy.mock.calls.length).toEqual(0); @@ -50,10 +54,10 @@ describe('LinkedErrors', () => { }), ); const one = new Error('originalException'); - const client = new NodeClient({}); + const client = new NodeClient({ stackParser }); return client.eventFromException(one).then(event => linkedErrors - ._handler(event, { + ._handler(stackParser, event, { originalException: one, }) .then((_: any) => { @@ -70,10 +74,10 @@ describe('LinkedErrors', () => { one.cause = two; two.cause = three; - const client = new NodeClient({}); + const client = new NodeClient({ stackParser }); return client.eventFromException(one).then(event => linkedErrors - ._handler(event, { + ._handler(stackParser, event, { originalException: one, }) .then((result: any) => { @@ -103,10 +107,10 @@ describe('LinkedErrors', () => { one.reason = two; two.reason = three; - const client = new NodeClient({}); + const client = new NodeClient({ stackParser }); return client.eventFromException(one).then(event => linkedErrors - ._handler(event, { + ._handler(stackParser, event, { originalException: one, }) .then((result: any) => { @@ -136,10 +140,10 @@ describe('LinkedErrors', () => { one.cause = two; two.cause = three; - const client = new NodeClient({}); + const client = new NodeClient({ stackParser }); return client.eventFromException(one).then(event => linkedErrors - ._handler(event, { + ._handler(stackParser, event, { originalException: one, }) .then((result: any) => { diff --git a/packages/node/test/stacktrace.test.ts b/packages/node/test/stacktrace.test.ts index 656ba1a69a9b..ec5ef8b94800 100644 --- a/packages/node/test/stacktrace.test.ts +++ b/packages/node/test/stacktrace.test.ts @@ -10,7 +10,12 @@ * @license MIT */ +import { createStackParser } from '@sentry/utils'; + import { parseStackFrames } from '../src/eventbuilder'; +import { nodeStackParser } from '../src/stack-parser'; + +const stackParser = createStackParser(nodeStackParser); function testBasic() { return new Error('something went wrong'); @@ -26,17 +31,17 @@ function evalWrapper() { describe('Stack parsing', () => { test('test basic error', () => { - const frames = parseStackFrames(testBasic()); + const frames = parseStackFrames(stackParser, testBasic()); const last = frames.length - 1; expect(frames[last].filename).toEqual(__filename); expect(frames[last].function).toEqual('testBasic'); - expect(frames[last].lineno).toEqual(16); + expect(frames[last].lineno).toEqual(21); expect(frames[last].colno).toEqual(10); }); test('test error with wrapper', () => { - const frames = parseStackFrames(testWrapper()); + const frames = parseStackFrames(stackParser, testWrapper()); const last = frames.length - 1; expect(frames[last].function).toEqual('testBasic'); @@ -44,7 +49,7 @@ describe('Stack parsing', () => { }); test('test error with eval wrapper', () => { - const frames = parseStackFrames(evalWrapper()); + const frames = parseStackFrames(stackParser, evalWrapper()); const last = frames.length - 1; expect(frames[last].function).toEqual('testBasic'); @@ -59,7 +64,7 @@ describe('Stack parsing', () => { ' at [object Object].global.every [as _onTimeout] (/Users/hoitz/develop/test.coffee:36:3)\n' + ' at Timer.listOnTimeout [as ontimeout] (timers.js:110:15)\n'; - const frames = parseStackFrames(err); + const frames = parseStackFrames(stackParser, err); expect(frames).toEqual([ { @@ -83,7 +88,7 @@ describe('Stack parsing', () => { test('parses undefined stack', () => { const err = { stack: undefined }; - const trace = parseStackFrames(err as Error); + const trace = parseStackFrames(stackParser, err as Error); expect(trace).toEqual([]); }); @@ -97,7 +102,7 @@ describe('Stack parsing', () => { 'oh no' + ' at TestCase.run (/Users/felix/code/node-fast-or-slow/lib/test_case.js:61:8)\n'; - const frames = parseStackFrames(err); + const frames = parseStackFrames(stackParser, err); expect(frames).toEqual([ { @@ -126,7 +131,7 @@ describe('Stack parsing', () => { ' at Test.fn (/Users/felix/code/node-fast-or-slow/test/fast/example/test-example.js:6)\n' + ' at Test.run (/Users/felix/code/node-fast-or-slow/lib/test.js:45)'; - const frames = parseStackFrames(err); + const frames = parseStackFrames(stackParser, err); expect(frames).toEqual([ { @@ -157,7 +162,7 @@ describe('Stack parsing', () => { ' at Array.0 (native)\n' + ' at EventEmitter._tickCallback (node.js:126:26)'; - const frames = parseStackFrames(err); + const frames = parseStackFrames(stackParser, err); expect(frames).toEqual([ { @@ -212,7 +217,7 @@ describe('Stack parsing', () => { const err = new Error(); err.stack = 'AssertionError: true == false\n' + ' at /Users/felix/code/node-fast-or-slow/lib/test_case.js:80:10'; - const frames = parseStackFrames(err); + const frames = parseStackFrames(stackParser, err); expect(frames).toEqual([ { @@ -232,7 +237,7 @@ describe('Stack parsing', () => { 'AssertionError: true == false\nAnd some more shit\n' + ' at /Users/felix/code/node-fast-or-slow/lib/test_case.js:80:10'; - const frames = parseStackFrames(err); + const frames = parseStackFrames(stackParser, err); expect(frames).toEqual([ { @@ -252,7 +257,7 @@ describe('Stack parsing', () => { 'AssertionError: expected [] to be arguments\n' + ' at Assertion.prop.(anonymous function) (/Users/den/Projects/should.js/lib/should.js:60:14)\n'; - const frames = parseStackFrames(err); + const frames = parseStackFrames(stackParser, err); expect(frames).toEqual([ { @@ -273,7 +278,7 @@ describe('Stack parsing', () => { ' at Test.run (/Users/felix (something)/code/node-fast-or-slow/lib/test.js:45:10)\n' + ' at TestCase.run (/Users/felix (something)/code/node-fast-or-slow/lib/test_case.js:61:8)\n'; - const frames = parseStackFrames(err); + const frames = parseStackFrames(stackParser, err); expect(frames).toEqual([ { @@ -309,7 +314,7 @@ describe('Stack parsing', () => { ' at async onBatch (/code/node_modules/kafkajs/src/consumer/runner.js:326:9)\n' + ' at async /code/node_modules/kafkajs/src/consumer/runner.js:376:15\n'; - const frames = parseStackFrames(err); + const frames = parseStackFrames(stackParser, err); expect(frames).toEqual([ { diff --git a/packages/types/src/index.ts b/packages/types/src/index.ts index 13651b7cd78e..20856f52aa64 100644 --- a/packages/types/src/index.ts +++ b/packages/types/src/index.ts @@ -50,7 +50,7 @@ export { Severity } from './severity'; export { SeverityLevel, SeverityLevels } from './severity'; export { Span, SpanContext } from './span'; export { StackFrame } from './stackframe'; -export { Stacktrace } from './stacktrace'; +export { Stacktrace, StackParser, StackLineParser, StackLineParserFn } from './stacktrace'; export { CustomSamplingContext, Measurements, diff --git a/packages/types/src/options.ts b/packages/types/src/options.ts index 3fefd41ca137..a7306acdd76f 100644 --- a/packages/types/src/options.ts +++ b/packages/types/src/options.ts @@ -3,6 +3,7 @@ import { Event, EventHint } from './event'; import { Integration } from './integration'; import { CaptureContext } from './scope'; import { SdkMetadata } from './sdkmetadata'; +import { StackLineParser, StackParser } from './stacktrace'; import { SamplingContext } from './transaction'; import { Transport, TransportClass, TransportOptions } from './transport'; @@ -149,6 +150,12 @@ export interface Options { */ initialScope?: CaptureContext; + /** + * A stack parser implementation or an array of stack line parsers + * By default, a stack parser is supplied for all supported browsers + */ + stackParser?: StackParser | StackLineParser[]; + /** * Set of metadata about the SDK that can be internally used to enhance envelopes and events, * and provide additional data about every request. diff --git a/packages/types/src/stacktrace.ts b/packages/types/src/stacktrace.ts index 120a1b471af6..ae2f350f716b 100644 --- a/packages/types/src/stacktrace.ts +++ b/packages/types/src/stacktrace.ts @@ -5,3 +5,7 @@ export interface Stacktrace { frames?: StackFrame[]; frames_omitted?: [number, number]; } + +export type StackParser = (stack: string, skipFirst?: number) => StackFrame[]; +export type StackLineParserFn = (line: string) => StackFrame | undefined; +export type StackLineParser = [number, StackLineParserFn]; diff --git a/packages/utils/src/stacktrace.ts b/packages/utils/src/stacktrace.ts index 7c9dddeee298..15c5401c09bd 100644 --- a/packages/utils/src/stacktrace.ts +++ b/packages/utils/src/stacktrace.ts @@ -1,11 +1,7 @@ -import { StackFrame } from '@sentry/types'; +import { StackFrame, StackLineParser, StackParser } from '@sentry/types'; const STACKTRACE_LIMIT = 50; -export type StackParser = (stack: string, skipFirst?: number) => StackFrame[]; -export type StackLineParserFn = (line: string) => StackFrame | undefined; -export type StackLineParser = [number, StackLineParserFn]; - /** * Creates a stack parser with the supplied line parsers * @@ -34,6 +30,29 @@ export function createStackParser(...parsers: StackLineParser[]): StackParser { }; } +interface StackParserOptions { + stackParser?: StackParser | StackLineParser[]; +} + +/** + * Gets a stack parser implementation from options + * + * If options contains an array of line parsers, it is converted into a parser + */ +export function stackParserFromOptions(options: StackParserOptions | undefined): StackParser { + if (options) { + if (Array.isArray(options.stackParser)) { + options.stackParser = createStackParser(...options.stackParser); + } + + if (typeof options.stackParser === 'function') { + return options.stackParser; + } + } + + return _ => []; +} + /** * @hidden */ From 08f50c87fb31c9e3e77fc3ceca396cb79d518f31 Mon Sep 17 00:00:00 2001 From: Tim Fish Date: Tue, 12 Apr 2022 20:45:21 +0100 Subject: [PATCH 037/204] ref(browser): Remove stack parser support for Opera pre v15 (#4923) Opera v15 was released in 2013 and was based on Chromium. This PR removes the two Opera specific stack parsers that cater for versions before this from the defaults. Anyone still wanting to support these ancient browser versions can do the following: ```ts import { init, defaultStackParsers, opera10StackParser, opera11StackParser } from '@sentry/browser'; init({ dsn: '__DSN__', stackParser: [...defaultStackParsers, opera10StackParser, opera11StackParser] }) ``` --- packages/browser/src/stack-parsers.ts | 8 +------- packages/browser/test/unit/tracekit/opera.test.ts | 13 +++++++------ 2 files changed, 8 insertions(+), 13 deletions(-) diff --git a/packages/browser/src/stack-parsers.ts b/packages/browser/src/stack-parsers.ts index 5ee652001854..5d58e9865cd4 100644 --- a/packages/browser/src/stack-parsers.ts +++ b/packages/browser/src/stack-parsers.ts @@ -130,13 +130,7 @@ const opera11: StackLineParserFn = line => { export const opera11StackParser: StackLineParser = [OPERA11_PRIORITY, opera11]; -export const defaultStackParsers = [ - chromeStackParser, - geckoStackParser, - opera10StackParser, - opera11StackParser, - winjsStackParser, -]; +export const defaultStackParsers = [chromeStackParser, geckoStackParser, winjsStackParser]; /** * Safari web extensions, starting version unknown, can produce "frames-only" stacktraces. diff --git a/packages/browser/test/unit/tracekit/opera.test.ts b/packages/browser/test/unit/tracekit/opera.test.ts index a97675824e18..9084447d4e29 100644 --- a/packages/browser/test/unit/tracekit/opera.test.ts +++ b/packages/browser/test/unit/tracekit/opera.test.ts @@ -1,9 +1,10 @@ import { createStackParser } from '@sentry/utils'; import { exceptionFromError } from '../../../src/eventbuilder'; -import { defaultStackParsers } from '../../../src/stack-parsers'; +import { defaultStackParsers, opera10StackParser, opera11StackParser } from '../../../src/stack-parsers'; -const parser = createStackParser(...defaultStackParsers); +const operaParser = createStackParser(opera10StackParser, opera11StackParser); +const chromiumParser = createStackParser(...defaultStackParsers); describe('Tracekit - Opera Tests', () => { it('should parse Opera 10 error', () => { @@ -29,7 +30,7 @@ describe('Tracekit - Opera Tests', () => { '', }; - const ex = exceptionFromError(parser, OPERA_10); + const ex = exceptionFromError(operaParser, OPERA_10); expect(ex).toEqual({ value: 'Statement on line 42: Type mismatch (usually non-object value supplied where object required)', @@ -75,7 +76,7 @@ describe('Tracekit - Opera Tests', () => { ' foo();', }; - const ex = exceptionFromError(parser, OPERA_11); + const ex = exceptionFromError(operaParser, OPERA_11); expect(ex).toEqual({ value: "'this.undef' is not a function", @@ -112,7 +113,7 @@ describe('Tracekit - Opera Tests', () => { ' dumpException3();', }; - const ex = exceptionFromError(parser, OPERA_12); + const ex = exceptionFromError(operaParser, OPERA_12); expect(ex).toEqual({ value: "Cannot convert 'x' to object", @@ -156,7 +157,7 @@ describe('Tracekit - Opera Tests', () => { ' at bar (http://path/to/file.js:108:168)', }; - const ex = exceptionFromError(parser, OPERA_25); + const ex = exceptionFromError(chromiumParser, OPERA_25); expect(ex).toEqual({ value: "Cannot read property 'undef' of null", From 052263b32a8d4ae0fdf602dee34f14b46eaa892c Mon Sep 17 00:00:00 2001 From: Luca Forstner Date: Wed, 13 Apr 2022 15:31:56 +0200 Subject: [PATCH 038/204] ref(tracing): Update `setMeasurements` to only set a single measurement (#4920) --- .../setMeasurement/subject.js | 8 ++++ .../startTransaction/setMeasurement/test.ts | 18 ++++++++ packages/tracing/src/browser/metrics.ts | 43 ++++++++++++------- packages/tracing/src/transaction.ts | 10 +++-- packages/types/src/transaction.ts | 2 +- 5 files changed, 62 insertions(+), 19 deletions(-) create mode 100644 packages/integration-tests/suites/public-api/startTransaction/setMeasurement/subject.js create mode 100644 packages/integration-tests/suites/public-api/startTransaction/setMeasurement/test.ts diff --git a/packages/integration-tests/suites/public-api/startTransaction/setMeasurement/subject.js b/packages/integration-tests/suites/public-api/startTransaction/setMeasurement/subject.js new file mode 100644 index 000000000000..036e86201b18 --- /dev/null +++ b/packages/integration-tests/suites/public-api/startTransaction/setMeasurement/subject.js @@ -0,0 +1,8 @@ +const transaction = Sentry.startTransaction({ name: 'some_transaction' }); + +transaction.setMeasurement('metric.foo', 42, 'ms'); +transaction.setMeasurement('metric.bar', 1337, 'nanoseconds'); +transaction.setMeasurement('metric.baz', 99, 's'); +transaction.setMeasurement('metric.baz', 1); + +transaction.finish(); diff --git a/packages/integration-tests/suites/public-api/startTransaction/setMeasurement/test.ts b/packages/integration-tests/suites/public-api/startTransaction/setMeasurement/test.ts new file mode 100644 index 000000000000..e91231093bf3 --- /dev/null +++ b/packages/integration-tests/suites/public-api/startTransaction/setMeasurement/test.ts @@ -0,0 +1,18 @@ +import { expect } from '@playwright/test'; +import { Event } from '@sentry/types'; + +import { sentryTest } from '../../../../utils/fixtures'; +import { getFirstSentryEnvelopeRequest } from '../../../../utils/helpers'; + +sentryTest('should attach measurement to transaction', async ({ getLocalTestPath, page }) => { + const url = await getLocalTestPath({ testDir: __dirname }); + const event = await getFirstSentryEnvelopeRequest(page, url); + + expect(event.measurements?.['metric.foo'].value).toBe(42); + expect(event.measurements?.['metric.bar'].value).toBe(1337); + expect(event.measurements?.['metric.baz'].value).toBe(1); + + expect(event.measurements?.['metric.foo'].unit).toBe('ms'); + expect(event.measurements?.['metric.bar'].unit).toBe('nanoseconds'); + expect(event.measurements?.['metric.baz'].unit).toBe(''); +}); diff --git a/packages/tracing/src/browser/metrics.ts b/packages/tracing/src/browser/metrics.ts index 8386b608f247..dffc39abb088 100644 --- a/packages/tracing/src/browser/metrics.ts +++ b/packages/tracing/src/browser/metrics.ts @@ -79,14 +79,14 @@ export class MetricsInstrumentation { if (entry.name === 'first-paint' && shouldRecord) { IS_DEBUG_BUILD && logger.log('[Measurements] Adding FP'); - this._measurements['fp'] = { value: entry.startTime }; - this._measurements['mark.fp'] = { value: startTimestamp }; + this._measurements['fp'] = { value: entry.startTime, unit: 'millisecond' }; + this._measurements['mark.fp'] = { value: startTimestamp, unit: 'second' }; } if (entry.name === 'first-contentful-paint' && shouldRecord) { IS_DEBUG_BUILD && logger.log('[Measurements] Adding FCP'); - this._measurements['fcp'] = { value: entry.startTime }; - this._measurements['mark.fcp'] = { value: startTimestamp }; + this._measurements['fcp'] = { value: entry.startTime, unit: 'millisecond' }; + this._measurements['mark.fcp'] = { value: startTimestamp, unit: 'second' }; } break; @@ -115,12 +115,18 @@ export class MetricsInstrumentation { // start of the response in milliseconds if (typeof responseStartTimestamp === 'number') { IS_DEBUG_BUILD && logger.log('[Measurements] Adding TTFB'); - this._measurements['ttfb'] = { value: (responseStartTimestamp - transaction.startTimestamp) * 1000 }; + this._measurements['ttfb'] = { + value: (responseStartTimestamp - transaction.startTimestamp) * 1000, + unit: 'millisecond', + }; if (typeof requestStartTimestamp === 'number' && requestStartTimestamp <= responseStartTimestamp) { // Capture the time spent making the request and receiving the first byte of the response. // This is the time between the start of the request and the start of the response in milliseconds. - this._measurements['ttfb.requestTime'] = { value: (responseStartTimestamp - requestStartTimestamp) * 1000 }; + this._measurements['ttfb.requestTime'] = { + value: (responseStartTimestamp - requestStartTimestamp) * 1000, + unit: 'second', + }; } } @@ -162,7 +168,14 @@ export class MetricsInstrumentation { delete this._measurements.cls; } - transaction.setMeasurements(this._measurements); + Object.keys(this._measurements).forEach(measurementName => { + transaction.setMeasurement( + measurementName, + this._measurements[measurementName].value, + this._measurements[measurementName].unit, + ); + }); + tagMetricInfo(transaction, this._lcpEntry, this._clsEntry); transaction.setTag('sentry_reportAllChanges', this._reportAllChanges); } @@ -189,11 +202,11 @@ export class MetricsInstrumentation { } if (isMeasurementValue(connection.rtt)) { - this._measurements['connection.rtt'] = { value: connection.rtt as number }; + this._measurements['connection.rtt'] = { value: connection.rtt, unit: 'millisecond' }; } if (isMeasurementValue(connection.downlink)) { - this._measurements['connection.downlink'] = { value: connection.downlink as number }; + this._measurements['connection.downlink'] = { value: connection.downlink, unit: '' }; // unit is empty string for now, while relay doesn't support download speed units } } @@ -218,7 +231,7 @@ export class MetricsInstrumentation { } IS_DEBUG_BUILD && logger.log('[Measurements] Adding CLS'); - this._measurements['cls'] = { value: metric.value }; + this._measurements['cls'] = { value: metric.value, unit: 'millisecond' }; this._clsEntry = entry as LayoutShift; }); } @@ -234,8 +247,8 @@ export class MetricsInstrumentation { const timeOrigin = msToSec(browserPerformanceTimeOrigin as number); const startTime = msToSec(entry.startTime); IS_DEBUG_BUILD && logger.log('[Measurements] Adding LCP'); - this._measurements['lcp'] = { value: metric.value }; - this._measurements['mark.lcp'] = { value: timeOrigin + startTime }; + this._measurements['lcp'] = { value: metric.value, unit: 'millisecond' }; + this._measurements['mark.lcp'] = { value: timeOrigin + startTime, unit: 'second' }; this._lcpEntry = entry as LargestContentfulPaint; }, this._reportAllChanges); } @@ -251,8 +264,8 @@ export class MetricsInstrumentation { const timeOrigin = msToSec(browserPerformanceTimeOrigin as number); const startTime = msToSec(entry.startTime); IS_DEBUG_BUILD && logger.log('[Measurements] Adding FID'); - this._measurements['fid'] = { value: metric.value }; - this._measurements['mark.fid'] = { value: timeOrigin + startTime }; + this._measurements['fid'] = { value: metric.value, unit: 'millisecond' }; + this._measurements['mark.fid'] = { value: timeOrigin + startTime, unit: 'second' }; }); } } @@ -392,7 +405,7 @@ export function _startChild(transaction: Transaction, { startTimestamp, ...ctx } /** * Checks if a given value is a valid measurement value. */ -function isMeasurementValue(value: any): boolean { +function isMeasurementValue(value: unknown): value is number { return typeof value === 'number' && isFinite(value); } diff --git a/packages/tracing/src/transaction.ts b/packages/tracing/src/transaction.ts index 6cbda0e04223..5384f15a6105 100644 --- a/packages/tracing/src/transaction.ts +++ b/packages/tracing/src/transaction.ts @@ -68,11 +68,15 @@ export class Transaction extends SpanClass implements TransactionInterface { } /** - * Set observed measurements for this transaction. + * Set observed measurement for this transaction. + * + * @param name Name of the measurement + * @param value Value of the measurement + * @param unit Unit of the measurement. (Defaults to an empty string) * @hidden */ - public setMeasurements(measurements: Measurements): void { - this._measurements = { ...measurements }; + public setMeasurement(name: string, value: number, unit: string = ''): void { + this._measurements[name] = { value, unit }; } /** diff --git a/packages/types/src/transaction.ts b/packages/types/src/transaction.ts index d2a8744cd335..0367eafbb941 100644 --- a/packages/types/src/transaction.ts +++ b/packages/types/src/transaction.ts @@ -115,7 +115,7 @@ export interface SamplingContext extends CustomSamplingContext { request?: ExtractedNodeRequestData; } -export type Measurements = Record; +export type Measurements = Record; export type TransactionSamplingMethod = 'explicitly_set' | 'client_sampler' | 'client_rate' | 'inheritance'; From b35fed99cb08c66a119ea5666da50c84cb04332d Mon Sep 17 00:00:00 2001 From: Luca Forstner Date: Wed, 13 Apr 2022 16:45:14 +0200 Subject: [PATCH 039/204] feat(tracing): Make `setMeasurement` public API (#4933) --- packages/tracing/src/transaction.ts | 7 +------ packages/types/src/transaction.ts | 9 +++++++++ 2 files changed, 10 insertions(+), 6 deletions(-) diff --git a/packages/tracing/src/transaction.ts b/packages/tracing/src/transaction.ts index 5384f15a6105..91fa4d3c435b 100644 --- a/packages/tracing/src/transaction.ts +++ b/packages/tracing/src/transaction.ts @@ -68,12 +68,7 @@ export class Transaction extends SpanClass implements TransactionInterface { } /** - * Set observed measurement for this transaction. - * - * @param name Name of the measurement - * @param value Value of the measurement - * @param unit Unit of the measurement. (Defaults to an empty string) - * @hidden + * @inheritDoc */ public setMeasurement(name: string, value: number, unit: string = ''): void { this._measurements[name] = { value, unit }; diff --git a/packages/types/src/transaction.ts b/packages/types/src/transaction.ts index 0367eafbb941..95f8b447e19a 100644 --- a/packages/types/src/transaction.ts +++ b/packages/types/src/transaction.ts @@ -73,6 +73,15 @@ export interface Transaction extends TransactionContext, Span { */ setName(name: string): void; + /** + * Set observed measurement for this transaction. + * + * @param name Name of the measurement + * @param value Value of the measurement + * @param unit Unit of the measurement. (Defaults to an empty string) + */ + setMeasurement(name: string, value: number, unit: string): void; + /** Returns the current transaction properties as a `TransactionContext` */ toContext(): TransactionContext; From 254e40ee566680a42c3f49d380be7ba2d3cf02c7 Mon Sep 17 00:00:00 2001 From: Katie Byers Date: Wed, 13 Apr 2022 17:32:27 -0700 Subject: [PATCH 040/204] ref(types): Stop using `Severity` enum (#4926) Note: This and https://github.com/getsentry/sentry-javascript/pull/4896 are together a (slightly updated) repeat of https://github.com/getsentry/sentry-javascript/pull/4497, to get it onto the main v7 branch. Our original v7 plan called for deprecating and then removing the `Severity` enum which lives in `@sentry/types`, because enums transpile to a two-way lookup function with a fairly hefty footprint (see this SO answer[1] from one of the maintainers of TS). We therefore added a new `SeverityLevel` type, which is the union of all of `Severity`'s underlying values, and directed people to use that instead. Though we subsequently decided not to remove `Severity` (at least in v7), we agreed that we should stop using it internally. This implements that change. Key Changes: - `Severity` and `severityFromString` have been redeprecated. - The original plan to have `SeverityLevel` live in `@sentry/utils` has been reverted, and it now lives only in `@sentry/types`. - The internal `SeverityLevels` array on which we were basing `SeverityLevel` has been removed. While we lose the elegance of the derived type, we gain the ability to truly only export types from `@sentry/types`. - Wherever we accept a `Severity` value, we now also accept a `SeverityLevel`. - All internal uses of `Severity` values have been replaced with the equivalent string constants. - A new `severityLevelFromString` function has been introduced, and is now used in place of `SeverityFromString`. - The tests for `severityFromString` have been cleaned up and replaced with equivalent tests for `SeverityLevelFromString`. [1] https://stackoverflow.com/a/28818850 --- packages/browser/src/client.ts | 9 +++- packages/browser/src/eventbuilder.ts | 7 ++-- packages/browser/src/exports.ts | 4 +- .../browser/src/integrations/breadcrumbs.ts | 8 ++-- .../src/integrations/globalhandlers.ts | 6 +-- packages/core/src/baseclient.ts | 16 ++++++- packages/core/test/mocks/client.ts | 8 +++- packages/hub/src/hub.ts | 8 +++- packages/hub/src/scope.ts | 9 +++- packages/hub/test/scope.test.ts | 22 +++++----- packages/integrations/src/captureconsole.ts | 4 +- packages/minimal/src/index.ts | 7 +++- packages/minimal/test/lib/minimal.test.ts | 13 +++--- .../multiple_breadcrumbs/scenario.ts | 2 +- .../simple_breadcrumb/scenario.ts | 2 +- .../captureMessage/with_level/scenario.ts | 14 +++---- packages/node/src/client.ts | 9 +++- packages/node/src/eventbuilder.ts | 14 ++++++- packages/node/src/index.ts | 4 +- packages/node/src/integrations/console.ts | 4 +- .../src/integrations/onuncaughtexception.ts | 4 +- packages/serverless/src/awslambda.ts | 2 +- packages/tracing/src/index.bundle.ts | 4 +- packages/types/src/breadcrumb.ts | 5 ++- packages/types/src/client.ts | 17 ++++++-- packages/types/src/event.ts | 5 ++- packages/types/src/hub.ts | 9 +++- packages/types/src/index.ts | 4 +- packages/types/src/scope.ts | 12 ++++-- packages/types/src/severity.ts | 10 ++--- packages/utils/src/enums.ts | 2 - packages/utils/src/index.ts | 1 - packages/utils/src/severity.ts | 42 +++++++++++++------ packages/utils/test/severity.test.ts | 28 +++++-------- packages/vue/src/index.bundle.ts | 3 +- 35 files changed, 199 insertions(+), 119 deletions(-) delete mode 100644 packages/utils/src/enums.ts diff --git a/packages/browser/src/client.ts b/packages/browser/src/client.ts index 4ee24f0ceadf..5116df6104d1 100644 --- a/packages/browser/src/client.ts +++ b/packages/browser/src/client.ts @@ -1,5 +1,5 @@ import { BaseClient, getEnvelopeEndpointWithUrlEncodedAuth, initAPIDetails, Scope, SDK_VERSION } from '@sentry/core'; -import { Event, EventHint, Options, Severity, Transport, TransportOptions } from '@sentry/types'; +import { Event, EventHint, Options, Severity, SeverityLevel, Transport, TransportOptions } from '@sentry/types'; import { getGlobalObject, logger, stackParserFromOptions, supportsFetch } from '@sentry/utils'; import { eventFromException, eventFromMessage } from './eventbuilder'; @@ -89,7 +89,12 @@ export class BrowserClient extends BaseClient { /** * @inheritDoc */ - public eventFromMessage(message: string, level: Severity = Severity.Info, hint?: EventHint): PromiseLike { + public eventFromMessage( + message: string, + // eslint-disable-next-line deprecation/deprecation + level: Severity | SeverityLevel = 'info', + hint?: EventHint, + ): PromiseLike { return eventFromMessage( stackParserFromOptions(this._options), message, diff --git a/packages/browser/src/eventbuilder.ts b/packages/browser/src/eventbuilder.ts index 6ed5181eefa1..c3052c41dd48 100644 --- a/packages/browser/src/eventbuilder.ts +++ b/packages/browser/src/eventbuilder.ts @@ -1,4 +1,4 @@ -import { Event, EventHint, Exception, Severity, StackFrame, StackParser } from '@sentry/types'; +import { Event, EventHint, Exception, Severity, SeverityLevel, StackFrame, StackParser } from '@sentry/types'; import { addExceptionMechanism, addExceptionTypeValue, @@ -150,7 +150,7 @@ export function eventFromException( const syntheticException = (hint && hint.syntheticException) || undefined; const event = eventFromUnknownInput(stackParser, exception, syntheticException, attachStacktrace); addExceptionMechanism(event); // defaults to { type: 'generic', handled: true } - event.level = Severity.Error; + event.level = 'error'; if (hint && hint.event_id) { event.event_id = hint.event_id; } @@ -164,7 +164,8 @@ export function eventFromException( export function eventFromMessage( stackParser: StackParser, message: string, - level: Severity = Severity.Info, + // eslint-disable-next-line deprecation/deprecation + level: Severity | SeverityLevel = 'info', hint?: EventHint, attachStacktrace?: boolean, ): PromiseLike { diff --git a/packages/browser/src/exports.ts b/packages/browser/src/exports.ts index 704096fae520..1ee859e354d6 100644 --- a/packages/browser/src/exports.ts +++ b/packages/browser/src/exports.ts @@ -8,15 +8,15 @@ export { EventStatus, Exception, Response, + // eslint-disable-next-line deprecation/deprecation Severity, + SeverityLevel, StackFrame, Stacktrace, Thread, User, } from '@sentry/types'; -export { SeverityLevel } from '@sentry/utils'; - export { addGlobalEventProcessor, addBreadcrumb, diff --git a/packages/browser/src/integrations/breadcrumbs.ts b/packages/browser/src/integrations/breadcrumbs.ts index 15237f599b15..3cca50248458 100644 --- a/packages/browser/src/integrations/breadcrumbs.ts +++ b/packages/browser/src/integrations/breadcrumbs.ts @@ -1,7 +1,7 @@ /* eslint-disable @typescript-eslint/no-unsafe-member-access */ /* eslint-disable max-lines */ import { getCurrentHub } from '@sentry/core'; -import { Event, Integration, Severity } from '@sentry/types'; +import { Event, Integration } from '@sentry/types'; import { addInstrumentationHandler, getEventDescription, @@ -9,7 +9,7 @@ import { htmlTreeAsString, parseUrl, safeJoin, - severityFromString, + severityLevelFromString, } from '@sentry/utils'; /** JSDoc */ @@ -157,7 +157,7 @@ function _consoleBreadcrumb(handlerData: { [key: string]: any }): void { arguments: handlerData.args, logger: 'console', }, - level: severityFromString(handlerData.level), + level: severityLevelFromString(handlerData.level), message: safeJoin(handlerData.args, ' '), }; @@ -230,7 +230,7 @@ function _fetchBreadcrumb(handlerData: { [key: string]: any }): void { { category: 'fetch', data: handlerData.fetchData, - level: Severity.Error, + level: 'error', type: 'http', }, { diff --git a/packages/browser/src/integrations/globalhandlers.ts b/packages/browser/src/integrations/globalhandlers.ts index 70966e4f7ec5..61e56a533629 100644 --- a/packages/browser/src/integrations/globalhandlers.ts +++ b/packages/browser/src/integrations/globalhandlers.ts @@ -1,6 +1,6 @@ /* eslint-disable @typescript-eslint/no-unsafe-member-access */ import { getCurrentHub } from '@sentry/core'; -import { Event, EventHint, Hub, Integration, Primitive, Severity, StackParser } from '@sentry/types'; +import { Event, EventHint, Hub, Integration, Primitive, StackParser } from '@sentry/types'; import { addExceptionMechanism, addInstrumentationHandler, @@ -100,7 +100,7 @@ function _installGlobalOnErrorHandler(): void { column, ); - event.level = Severity.Error; + event.level = 'error'; addMechanismAndCapture(hub, error, event, 'onerror'); }, @@ -146,7 +146,7 @@ function _installGlobalOnUnhandledRejectionHandler(): void { ? _eventFromRejectionWithPrimitive(error) : eventFromUnknownInput(stackParser, error, undefined, attachStacktrace, true); - event.level = Severity.Error; + event.level = 'error'; addMechanismAndCapture(hub, error, event, 'onunhandledrejection'); return; diff --git a/packages/core/src/baseclient.ts b/packages/core/src/baseclient.ts index 75712f62479b..8486d5e03b9c 100644 --- a/packages/core/src/baseclient.ts +++ b/packages/core/src/baseclient.ts @@ -9,6 +9,7 @@ import { IntegrationClass, Options, Severity, + SeverityLevel, Transport, } from '@sentry/types'; import { @@ -131,7 +132,13 @@ export abstract class BaseClient implements Client { /** * @inheritDoc */ - public captureMessage(message: string, level?: Severity, hint?: EventHint, scope?: Scope): string | undefined { + public captureMessage( + message: string, + // eslint-disable-next-line deprecation/deprecation + level?: Severity | SeverityLevel, + hint?: EventHint, + scope?: Scope, + ): string | undefined { let eventId: string | undefined = hint && hint.event_id; const promisedEvent = isPrimitive(message) @@ -685,7 +692,12 @@ export abstract class BaseClient implements Client { /** * @inheritDoc */ - public abstract eventFromMessage(_message: string, _level?: Severity, _hint?: EventHint): PromiseLike; + public abstract eventFromMessage( + _message: string, + // eslint-disable-next-line deprecation/deprecation + _level?: Severity | SeverityLevel, + _hint?: EventHint, + ): PromiseLike; } /** diff --git a/packages/core/test/mocks/client.ts b/packages/core/test/mocks/client.ts index f399c6018122..36f053429e7f 100644 --- a/packages/core/test/mocks/client.ts +++ b/packages/core/test/mocks/client.ts @@ -1,5 +1,5 @@ import { Session } from '@sentry/hub'; -import { Event, Options, Severity, Transport } from '@sentry/types'; +import { Event, Options, Severity, SeverityLevel, Transport } from '@sentry/types'; import { resolvedSyncPromise } from '@sentry/utils'; import { BaseClient } from '../../src/baseclient'; @@ -38,7 +38,11 @@ export class TestClient extends BaseClient { }); } - public eventFromMessage(message: string, level: Severity = Severity.Info): PromiseLike { + public eventFromMessage( + message: string, + // eslint-disable-next-line deprecation/deprecation + level: Severity | SeverityLevel = 'info', + ): PromiseLike { return resolvedSyncPromise({ message, level }); } diff --git a/packages/hub/src/hub.ts b/packages/hub/src/hub.ts index 94c8e3ea91fe..74a810fafeea 100644 --- a/packages/hub/src/hub.ts +++ b/packages/hub/src/hub.ts @@ -14,6 +14,7 @@ import { Primitive, SessionContext, Severity, + SeverityLevel, Transaction, TransactionContext, User, @@ -213,7 +214,12 @@ export class Hub implements HubInterface { /** * @inheritDoc */ - public captureMessage(message: string, level?: Severity, hint?: EventHint): string { + public captureMessage( + message: string, + // eslint-disable-next-line deprecation/deprecation + level?: Severity | SeverityLevel, + hint?: EventHint, + ): string { const eventId = (this._lastEventId = hint && hint.event_id ? hint.event_id : uuid4()); let finalHint = hint; diff --git a/packages/hub/src/scope.ts b/packages/hub/src/scope.ts index 09dd244734ee..1733cf7f826f 100644 --- a/packages/hub/src/scope.ts +++ b/packages/hub/src/scope.ts @@ -14,6 +14,7 @@ import { Scope as ScopeInterface, ScopeContext, Severity, + SeverityLevel, Span, Transaction, User, @@ -61,7 +62,8 @@ export class Scope implements ScopeInterface { protected _fingerprint?: string[]; /** Severity */ - protected _level?: Severity; + // eslint-disable-next-line deprecation/deprecation + protected _level?: Severity | SeverityLevel; /** Transaction Name */ protected _transactionName?: string; @@ -208,7 +210,10 @@ export class Scope implements ScopeInterface { /** * @inheritDoc */ - public setLevel(level: Severity): this { + public setLevel( + // eslint-disable-next-line deprecation/deprecation + level: Severity | SeverityLevel, + ): this { this._level = level; this._notifyScopeListeners(); return this; diff --git a/packages/hub/test/scope.test.ts b/packages/hub/test/scope.test.ts index 62f2e112f7fe..7cca748fb6f8 100644 --- a/packages/hub/test/scope.test.ts +++ b/packages/hub/test/scope.test.ts @@ -1,4 +1,4 @@ -import { Event, EventHint, Severity } from '@sentry/types'; +import { Event, EventHint } from '@sentry/types'; import { getGlobalObject } from '@sentry/utils'; import { addGlobalEventProcessor, Scope } from '../src'; @@ -85,8 +85,8 @@ describe('Scope', () => { test('setLevel', () => { const scope = new Scope(); - scope.setLevel(Severity.Critical); - expect((scope as any)._level).toEqual(Severity.Critical); + scope.setLevel('critical'); + expect((scope as any)._level).toEqual('critical'); }); test('setTransactionName', () => { @@ -137,8 +137,8 @@ describe('Scope', () => { test('chaining', () => { const scope = new Scope(); - scope.setLevel(Severity.Critical).setUser({ id: '1' }); - expect((scope as any)._level).toEqual(Severity.Critical); + scope.setLevel('critical').setUser({ id: '1' }); + expect((scope as any)._level).toEqual('critical'); expect((scope as any)._user).toEqual({ id: '1' }); }); }); @@ -202,7 +202,7 @@ describe('Scope', () => { scope.setTag('a', 'b'); scope.setUser({ id: '1' }); scope.setFingerprint(['abcd']); - scope.setLevel(Severity.Warning); + scope.setLevel('warning'); scope.setTransactionName('/abc'); scope.addBreadcrumb({ message: 'test' }); scope.setContext('os', { id: '1' }); @@ -294,11 +294,11 @@ describe('Scope', () => { test('scope level should have priority over event level', () => { expect.assertions(1); const scope = new Scope(); - scope.setLevel(Severity.Warning); + scope.setLevel('warning'); const event: Event = {}; - event.level = Severity.Critical; + event.level = 'critical'; return scope.applyToEvent(event).then(processedEvent => { - expect(processedEvent!.level).toEqual(Severity.Warning); + expect(processedEvent!.level).toEqual('warning'); }); }); @@ -410,7 +410,7 @@ describe('Scope', () => { scope.setContext('foo', { id: '1' }); scope.setContext('bar', { id: '2' }); scope.setUser({ id: '1337' }); - scope.setLevel(Severity.Info); + scope.setLevel('info'); scope.setFingerprint(['foo']); scope.setRequestSession({ status: 'ok' }); }); @@ -458,7 +458,7 @@ describe('Scope', () => { localScope.setContext('bar', { id: '3' }); localScope.setContext('baz', { id: '4' }); localScope.setUser({ id: '42' }); - localScope.setLevel(Severity.Warning); + localScope.setLevel('warning'); localScope.setFingerprint(['bar']); (localScope as any)._requestSession = { status: 'ok' }; diff --git a/packages/integrations/src/captureconsole.ts b/packages/integrations/src/captureconsole.ts index 7224df50f286..7243520c0661 100644 --- a/packages/integrations/src/captureconsole.ts +++ b/packages/integrations/src/captureconsole.ts @@ -1,5 +1,5 @@ import { EventProcessor, Hub, Integration } from '@sentry/types'; -import { CONSOLE_LEVELS, fill, getGlobalObject, safeJoin, severityFromString } from '@sentry/utils'; +import { CONSOLE_LEVELS, fill, getGlobalObject, safeJoin, severityLevelFromString } from '@sentry/utils'; const global = getGlobalObject(); @@ -48,7 +48,7 @@ export class CaptureConsole implements Integration { if (hub.getIntegration(CaptureConsole)) { hub.withScope(scope => { - scope.setLevel(severityFromString(level)); + scope.setLevel(severityLevelFromString(level)); scope.setExtra('arguments', args); scope.addEventProcessor(event => { event.logger = 'console'; diff --git a/packages/minimal/src/index.ts b/packages/minimal/src/index.ts index 00a1cdd91d44..7cb0271786ab 100644 --- a/packages/minimal/src/index.ts +++ b/packages/minimal/src/index.ts @@ -8,6 +8,7 @@ import { Extras, Primitive, Severity, + SeverityLevel, Transaction, TransactionContext, User, @@ -52,7 +53,11 @@ export function captureException(exception: any, captureContext?: CaptureContext * @param Severity Define the level of the message. * @returns The generated eventId. */ -export function captureMessage(message: string, captureContext?: CaptureContext | Severity): string { +export function captureMessage( + message: string, + // eslint-disable-next-line deprecation/deprecation + captureContext?: CaptureContext | Severity | SeverityLevel, +): string { const syntheticException = new Error(message); // This is necessary to provide explicit scopes upgrade, without changing the original diff --git a/packages/minimal/test/lib/minimal.test.ts b/packages/minimal/test/lib/minimal.test.ts index 911c19ad1f68..48a117cd5c5d 100644 --- a/packages/minimal/test/lib/minimal.test.ts +++ b/packages/minimal/test/lib/minimal.test.ts @@ -1,5 +1,4 @@ import { getCurrentHub, getHubFromCarrier, Scope } from '@sentry/hub'; -import { Severity } from '@sentry/types'; import { _callOnClient, @@ -165,8 +164,8 @@ describe('Minimal', () => { const client: any = new TestClient({}); const scope = getCurrentHub().pushScope(); getCurrentHub().bindClient(client); - scope.setLevel(Severity.Warning); - expect(global.__SENTRY__.hub._stack[1].scope._level).toEqual(Severity.Warning); + scope.setLevel('warning'); + expect(global.__SENTRY__.hub._stack[1].scope._level).toEqual('warning'); }); }); @@ -245,16 +244,16 @@ describe('Minimal', () => { test('withScope', () => { withScope(scope => { - scope.setLevel(Severity.Warning); + scope.setLevel('warning'); scope.setFingerprint(['1']); withScope(scope2 => { - scope2.setLevel(Severity.Info); + scope2.setLevel('info'); scope2.setFingerprint(['2']); withScope(scope3 => { scope3.clear(); - expect(global.__SENTRY__.hub._stack[1].scope._level).toEqual(Severity.Warning); + expect(global.__SENTRY__.hub._stack[1].scope._level).toEqual('warning'); expect(global.__SENTRY__.hub._stack[1].scope._fingerprint).toEqual(['1']); - expect(global.__SENTRY__.hub._stack[2].scope._level).toEqual(Severity.Info); + expect(global.__SENTRY__.hub._stack[2].scope._level).toEqual('info'); expect(global.__SENTRY__.hub._stack[2].scope._fingerprint).toEqual(['2']); expect(global.__SENTRY__.hub._stack[3].scope._level).toBeUndefined(); }); diff --git a/packages/node-integration-tests/suites/public-api/addBreadcrumb/multiple_breadcrumbs/scenario.ts b/packages/node-integration-tests/suites/public-api/addBreadcrumb/multiple_breadcrumbs/scenario.ts index a94acf718b4b..993049500b08 100644 --- a/packages/node-integration-tests/suites/public-api/addBreadcrumb/multiple_breadcrumbs/scenario.ts +++ b/packages/node-integration-tests/suites/public-api/addBreadcrumb/multiple_breadcrumbs/scenario.ts @@ -8,7 +8,7 @@ Sentry.init({ Sentry.addBreadcrumb({ category: 'foo', message: 'bar', - level: Sentry.Severity.Critical, + level: 'critical', }); Sentry.addBreadcrumb({ diff --git a/packages/node-integration-tests/suites/public-api/addBreadcrumb/simple_breadcrumb/scenario.ts b/packages/node-integration-tests/suites/public-api/addBreadcrumb/simple_breadcrumb/scenario.ts index 108b79c26963..a31b33c68d54 100644 --- a/packages/node-integration-tests/suites/public-api/addBreadcrumb/simple_breadcrumb/scenario.ts +++ b/packages/node-integration-tests/suites/public-api/addBreadcrumb/simple_breadcrumb/scenario.ts @@ -8,7 +8,7 @@ Sentry.init({ Sentry.addBreadcrumb({ category: 'foo', message: 'bar', - level: Sentry.Severity.Critical, + level: 'critical', }); Sentry.captureMessage('test_simple'); diff --git a/packages/node-integration-tests/suites/public-api/captureMessage/with_level/scenario.ts b/packages/node-integration-tests/suites/public-api/captureMessage/with_level/scenario.ts index 61f8e2532ee6..32d46fa171fe 100644 --- a/packages/node-integration-tests/suites/public-api/captureMessage/with_level/scenario.ts +++ b/packages/node-integration-tests/suites/public-api/captureMessage/with_level/scenario.ts @@ -5,10 +5,10 @@ Sentry.init({ release: '1.0', }); -Sentry.captureMessage('debug_message', Sentry.Severity.Debug); -Sentry.captureMessage('info_message', Sentry.Severity.Info); -Sentry.captureMessage('warning_message', Sentry.Severity.Warning); -Sentry.captureMessage('error_message', Sentry.Severity.Error); -Sentry.captureMessage('fatal_message', Sentry.Severity.Fatal); -Sentry.captureMessage('critical_message', Sentry.Severity.Critical); -Sentry.captureMessage('log_message', Sentry.Severity.Log); +Sentry.captureMessage('debug_message', 'debug'); +Sentry.captureMessage('info_message', 'info'); +Sentry.captureMessage('warning_message', 'warning'); +Sentry.captureMessage('error_message', 'error'); +Sentry.captureMessage('fatal_message', 'fatal'); +Sentry.captureMessage('critical_message', 'critical'); +Sentry.captureMessage('log_message', 'log'); diff --git a/packages/node/src/client.ts b/packages/node/src/client.ts index 81f808f5ef56..6c4812b65980 100644 --- a/packages/node/src/client.ts +++ b/packages/node/src/client.ts @@ -1,6 +1,6 @@ import { BaseClient, getEnvelopeEndpointWithUrlEncodedAuth, initAPIDetails, Scope, SDK_VERSION } from '@sentry/core'; import { SessionFlusher } from '@sentry/hub'; -import { Event, EventHint, Severity, Transport, TransportOptions } from '@sentry/types'; +import { Event, EventHint, Severity, SeverityLevel, Transport, TransportOptions } from '@sentry/types'; import { logger, makeDsn, resolvedSyncPromise, stackParserFromOptions } from '@sentry/utils'; import { eventFromMessage, eventFromUnknownInput } from './eventbuilder'; @@ -118,7 +118,12 @@ export class NodeClient extends BaseClient { /** * @inheritDoc */ - public eventFromMessage(message: string, level: Severity = Severity.Info, hint?: EventHint): PromiseLike { + public eventFromMessage( + message: string, + // eslint-disable-next-line deprecation/deprecation + level: Severity | SeverityLevel = 'info', + hint?: EventHint, + ): PromiseLike { return resolvedSyncPromise( eventFromMessage(stackParserFromOptions(this._options), message, level, hint, this._options.attachStacktrace), ); diff --git a/packages/node/src/eventbuilder.ts b/packages/node/src/eventbuilder.ts index e0b6b16261f0..eead089793a0 100644 --- a/packages/node/src/eventbuilder.ts +++ b/packages/node/src/eventbuilder.ts @@ -1,5 +1,14 @@ import { getCurrentHub } from '@sentry/hub'; -import { Event, EventHint, Exception, Mechanism, Severity, StackFrame, StackParser } from '@sentry/types'; +import { + Event, + EventHint, + Exception, + Mechanism, + Severity, + SeverityLevel, + StackFrame, + StackParser, +} from '@sentry/types'; import { addExceptionMechanism, addExceptionTypeValue, @@ -90,7 +99,8 @@ export function eventFromUnknownInput(stackParser: StackParser, exception: unkno export function eventFromMessage( stackParser: StackParser, message: string, - level: Severity = Severity.Info, + // eslint-disable-next-line deprecation/deprecation + level: Severity | SeverityLevel = 'info', hint?: EventHint, attachStacktrace?: boolean, ): Event { diff --git a/packages/node/src/index.ts b/packages/node/src/index.ts index d6a924a04c2e..cb83bd07cdcf 100644 --- a/packages/node/src/index.ts +++ b/packages/node/src/index.ts @@ -8,15 +8,15 @@ export { EventStatus, Exception, Response, + // eslint-disable-next-line deprecation/deprecation Severity, + SeverityLevel, StackFrame, Stacktrace, Thread, User, } from '@sentry/types'; -export { SeverityLevel } from '@sentry/utils'; - export { addGlobalEventProcessor, addBreadcrumb, diff --git a/packages/node/src/integrations/console.ts b/packages/node/src/integrations/console.ts index a9542837fbcc..9b032c75cced 100644 --- a/packages/node/src/integrations/console.ts +++ b/packages/node/src/integrations/console.ts @@ -1,6 +1,6 @@ import { getCurrentHub } from '@sentry/core'; import { Integration } from '@sentry/types'; -import { fill, severityFromString } from '@sentry/utils'; +import { fill, severityLevelFromString } from '@sentry/utils'; import * as util from 'util'; /** Console module integration */ @@ -30,7 +30,7 @@ export class Console implements Integration { */ function createConsoleWrapper(level: string): (originalConsoleMethod: () => void) => void { return function consoleWrapper(originalConsoleMethod: () => void): () => void { - const sentryLevel = severityFromString(level); + const sentryLevel = severityLevelFromString(level); /* eslint-disable prefer-rest-params */ return function (this: typeof console): void { diff --git a/packages/node/src/integrations/onuncaughtexception.ts b/packages/node/src/integrations/onuncaughtexception.ts index 9c8d33913315..dfff26fa0675 100644 --- a/packages/node/src/integrations/onuncaughtexception.ts +++ b/packages/node/src/integrations/onuncaughtexception.ts @@ -1,5 +1,5 @@ import { getCurrentHub, Scope } from '@sentry/core'; -import { Integration, Severity } from '@sentry/types'; +import { Integration } from '@sentry/types'; import { logger } from '@sentry/utils'; import { NodeClient } from '../client'; @@ -78,7 +78,7 @@ export class OnUncaughtException implements Integration { if (hub.getIntegration(OnUncaughtException)) { hub.withScope((scope: Scope) => { - scope.setLevel(Severity.Fatal); + scope.setLevel('fatal'); hub.captureException(error, { originalException: error, data: { mechanism: { handled: false, type: 'onuncaughtexception' } }, diff --git a/packages/serverless/src/awslambda.ts b/packages/serverless/src/awslambda.ts index 3702ffc6a1fd..0b86f560a820 100644 --- a/packages/serverless/src/awslambda.ts +++ b/packages/serverless/src/awslambda.ts @@ -277,7 +277,7 @@ export function wrapHandler( timeoutWarningTimer = setTimeout(() => { withScope(scope => { scope.setTag('timeout', humanReadableTimeout); - captureMessage(`Possible function timeout: ${context.functionName}`, Sentry.Severity.Warning); + captureMessage(`Possible function timeout: ${context.functionName}`, 'warning'); }); }, timeoutWarningDelay); } diff --git a/packages/tracing/src/index.bundle.ts b/packages/tracing/src/index.bundle.ts index 3349dfbd06da..72ce4f3c19db 100644 --- a/packages/tracing/src/index.bundle.ts +++ b/packages/tracing/src/index.bundle.ts @@ -6,15 +6,15 @@ export { EventStatus, Exception, Response, + // eslint-disable-next-line deprecation/deprecation Severity, + SeverityLevel, StackFrame, Stacktrace, Thread, User, } from '@sentry/types'; -export { SeverityLevel } from '@sentry/utils'; - export { addGlobalEventProcessor, addBreadcrumb, diff --git a/packages/types/src/breadcrumb.ts b/packages/types/src/breadcrumb.ts index cab5ac68b3e1..34fe2dfcd16a 100644 --- a/packages/types/src/breadcrumb.ts +++ b/packages/types/src/breadcrumb.ts @@ -1,9 +1,10 @@ -import { Severity } from './severity'; +import { Severity, SeverityLevel } from './severity'; /** JSDoc */ export interface Breadcrumb { type?: string; - level?: Severity; + // eslint-disable-next-line deprecation/deprecation + level?: Severity | SeverityLevel; event_id?: string; category?: string; message?: string; diff --git a/packages/types/src/client.ts b/packages/types/src/client.ts index e11950487fa1..c3f2a9920258 100644 --- a/packages/types/src/client.ts +++ b/packages/types/src/client.ts @@ -4,7 +4,7 @@ import { Integration, IntegrationClass } from './integration'; import { Options } from './options'; import { Scope } from './scope'; import { Session } from './session'; -import { Severity } from './severity'; +import { Severity, SeverityLevel } from './severity'; import { Transport } from './transport'; /** @@ -36,7 +36,13 @@ export interface Client { * @param scope An optional scope containing event metadata. * @returns The event id */ - captureMessage(message: string, level?: Severity, hint?: EventHint, scope?: Scope): string | undefined; + captureMessage( + message: string, + // eslint-disable-next-line deprecation/deprecation + level?: Severity | SeverityLevel, + hint?: EventHint, + scope?: Scope, + ): string | undefined; /** * Captures a manually created event and sends it to Sentry. @@ -99,7 +105,12 @@ export interface Client { eventFromException(exception: any, hint?: EventHint): PromiseLike; /** Creates an {@link Event} from primitive inputs to `captureMessage`. */ - eventFromMessage(message: string, level?: Severity, hint?: EventHint): PromiseLike; + eventFromMessage( + message: string, + // eslint-disable-next-line deprecation/deprecation + level?: Severity | SeverityLevel, + hint?: EventHint, + ): PromiseLike; /** Submits the event to Sentry */ sendEvent(event: Event): void; diff --git a/packages/types/src/event.ts b/packages/types/src/event.ts index 6711322baab4..7cb3047ed4d5 100644 --- a/packages/types/src/event.ts +++ b/packages/types/src/event.ts @@ -7,7 +7,7 @@ import { Primitive } from './misc'; import { Request } from './request'; import { CaptureContext } from './scope'; import { SdkInfo } from './sdkinfo'; -import { Severity } from './severity'; +import { Severity, SeverityLevel } from './severity'; import { Span } from './span'; import { Measurements } from './transaction'; import { User } from './user'; @@ -18,7 +18,8 @@ export interface Event { message?: string; timestamp?: number; start_timestamp?: number; - level?: Severity; + // eslint-disable-next-line deprecation/deprecation + level?: Severity | SeverityLevel; platform?: string; logger?: string; server_name?: string; diff --git a/packages/types/src/hub.ts b/packages/types/src/hub.ts index 49452266fe9f..9a67968a4fc1 100644 --- a/packages/types/src/hub.ts +++ b/packages/types/src/hub.ts @@ -6,7 +6,7 @@ import { Integration, IntegrationClass } from './integration'; import { Primitive } from './misc'; import { Scope } from './scope'; import { Session, SessionContext } from './session'; -import { Severity } from './severity'; +import { Severity, SeverityLevel } from './severity'; import { CustomSamplingContext, Transaction, TransactionContext } from './transaction'; import { User } from './user'; @@ -87,7 +87,12 @@ export interface Hub { * @param hint May contain additional information about the original exception. * @returns The generated eventId. */ - captureMessage(message: string, level?: Severity, hint?: EventHint): string; + captureMessage( + message: string, + // eslint-disable-next-line deprecation/deprecation + level?: Severity | SeverityLevel, + hint?: EventHint, + ): string; /** * Captures a manually created event and sends it to Sentry. diff --git a/packages/types/src/index.ts b/packages/types/src/index.ts index 20856f52aa64..b104f2e85930 100644 --- a/packages/types/src/index.ts +++ b/packages/types/src/index.ts @@ -46,8 +46,8 @@ export { SessionFlusherLike, } from './session'; -export { Severity } from './severity'; -export { SeverityLevel, SeverityLevels } from './severity'; +// eslint-disable-next-line deprecation/deprecation +export { Severity, SeverityLevel } from './severity'; export { Span, SpanContext } from './span'; export { StackFrame } from './stackframe'; export { Stacktrace, StackParser, StackLineParser, StackLineParserFn } from './stacktrace'; diff --git a/packages/types/src/scope.ts b/packages/types/src/scope.ts index b46c47ce0759..c3ee56a2a763 100644 --- a/packages/types/src/scope.ts +++ b/packages/types/src/scope.ts @@ -4,7 +4,7 @@ import { EventProcessor } from './eventprocessor'; import { Extra, Extras } from './extra'; import { Primitive } from './misc'; import { RequestSession, Session } from './session'; -import { Severity } from './severity'; +import { Severity, SeverityLevel } from './severity'; import { Span } from './span'; import { Transaction } from './transaction'; import { User } from './user'; @@ -15,7 +15,8 @@ export type CaptureContext = Scope | Partial | ((scope: Scope) => /** JSDocs */ export interface ScopeContext { user: User; - level: Severity; + // eslint-disable-next-line deprecation/deprecation + level: Severity | SeverityLevel; extra: Extras; contexts: Contexts; tags: { [key: string]: Primitive }; @@ -79,9 +80,12 @@ export interface Scope { /** * Sets the level on the scope for future events. - * @param level string {@link Severity} + * @param level string {@link SeverityLevel} */ - setLevel(level: Severity): this; + setLevel( + // eslint-disable-next-line deprecation/deprecation + level: Severity | SeverityLevel, + ): this; /** * Sets the transaction name on the scope for future events. diff --git a/packages/types/src/severity.ts b/packages/types/src/severity.ts index 513c63c7dadb..ad96231cb013 100644 --- a/packages/types/src/severity.ts +++ b/packages/types/src/severity.ts @@ -1,5 +1,6 @@ /** - * TODO(v7): Remove this enum and replace with SeverityLevel + * @deprecated Please use a `SeverityLevel` string instead of the `Severity` enum. Acceptable values are 'fatal', + * 'critical', 'error', 'warning', 'log', 'info', and 'debug'. */ export enum Severity { /** JSDoc */ @@ -18,7 +19,6 @@ export enum Severity { Critical = 'critical', } -// TODO: in v7, these can disappear, because they now also exist in `@sentry/utils`. (Having them there rather than here -// is nice because then it enforces the idea that only types are exported from `@sentry/types`.) -export const SeverityLevels = ['fatal', 'error', 'warning', 'log', 'info', 'debug', 'critical'] as const; -export type SeverityLevel = typeof SeverityLevels[number]; +// Note: If this is ever changed, the `validSeverityLevels` array in `@sentry/utils` needs to be changed, also. (See +// note there for why we can't derive one from the other.) +export type SeverityLevel = 'fatal' | 'error' | 'warning' | 'log' | 'info' | 'debug' | 'critical'; diff --git a/packages/utils/src/enums.ts b/packages/utils/src/enums.ts deleted file mode 100644 index 998540a6677f..000000000000 --- a/packages/utils/src/enums.ts +++ /dev/null @@ -1,2 +0,0 @@ -export const SeverityLevels = ['fatal', 'error', 'warning', 'log', 'info', 'debug', 'critical'] as const; -export type SeverityLevel = typeof SeverityLevels[number]; diff --git a/packages/utils/src/index.ts b/packages/utils/src/index.ts index e4567790f7d3..4d33eeab55e3 100644 --- a/packages/utils/src/index.ts +++ b/packages/utils/src/index.ts @@ -1,7 +1,6 @@ export * from './async'; export * from './browser'; export * from './dsn'; -export * from './enums'; export * from './error'; export * from './global'; export * from './instrument'; diff --git a/packages/utils/src/severity.ts b/packages/utils/src/severity.ts index 034f7dcbeb99..ba2ad2822851 100644 --- a/packages/utils/src/severity.ts +++ b/packages/utils/src/severity.ts @@ -1,20 +1,36 @@ -import { Severity } from '@sentry/types'; +/* eslint-disable deprecation/deprecation */ +import { Severity, SeverityLevel } from '@sentry/types'; -import { SeverityLevel, SeverityLevels } from './enums'; +// Note: Ideally the `SeverityLevel` type would be derived from `validSeverityLevels`, but that would mean either +// +// a) moving `validSeverityLevels` to `@sentry/types`, +// b) moving the`SeverityLevel` type here, or +// c) importing `validSeverityLevels` from here into `@sentry/types`. +// +// Option A would make `@sentry/types` a runtime dependency of `@sentry/utils` (not good), and options B and C would +// create a circular dependency between `@sentry/types` and `@sentry/utils` (also not good). So a TODO accompanying the +// type, reminding anyone who changes it to change this list also, will have to do. + +export const validSeverityLevels = ['fatal', 'error', 'warning', 'log', 'info', 'debug', 'critical']; -function isSupportedSeverity(level: string): level is Severity { - return SeverityLevels.indexOf(level as SeverityLevel) !== -1; -} /** - * Converts a string-based level into a {@link Severity}. + * Converts a string-based level into a member of the deprecated {@link Severity} enum. * - * @param level string representation of Severity + * @deprecated `severityFromString` is deprecated. Please use `severityLevelFromString` instead. + * + * @param level String representation of Severity * @returns Severity */ -export function severityFromString(level: SeverityLevel | string): Severity { - if (level === 'warn') return Severity.Warning; - if (isSupportedSeverity(level)) { - return level; - } - return Severity.Log; +export function severityFromString(level: Severity | SeverityLevel | string): Severity { + return severityLevelFromString(level) as Severity; +} + +/** + * Converts a string-based level into a `SeverityLevel`, normalizing it along the way. + * + * @param level String representation of desired `SeverityLevel`. + * @returns The `SeverityLevel` corresponding to the given string, or 'log' if the string isn't a valid level. + */ +export function severityLevelFromString(level: SeverityLevel | string): SeverityLevel { + return (level === 'warn' ? 'warning' : validSeverityLevels.includes(level) ? level : 'log') as SeverityLevel; } diff --git a/packages/utils/test/severity.test.ts b/packages/utils/test/severity.test.ts index 7b41c92a2082..51f66e815288 100644 --- a/packages/utils/test/severity.test.ts +++ b/packages/utils/test/severity.test.ts @@ -1,23 +1,17 @@ -import { SeverityLevels } from '../src/enums'; -import { severityFromString } from '../src/severity'; +import { severityLevelFromString, validSeverityLevels } from '../src/severity'; -describe('severityFromString()', () => { - describe('normalize warn and warning', () => { - test('handles warn and warning', () => { - expect(severityFromString('warn')).toBe('warning'); - expect(severityFromString('warning')).toBe('warning'); - }); - test('handles warn and warning', () => { - expect(severityFromString('warn')).toBe('warning'); - expect(severityFromString('warning')).toBe('warning'); - }); +describe('severityLevelFromString()', () => { + test("converts 'warn' to 'warning'", () => { + expect(severityLevelFromString('warn')).toBe('warning'); }); - describe('default to log', () => { - expect(severityFromString('foo')).toBe('log'); + + test('defaults to log', () => { + expect(severityLevelFromString('foo')).toBe('log'); }); - describe('allows ', () => { - for (const level of SeverityLevels) { - expect(severityFromString(level)).toBe(level); + + test('acts as a pass-through for valid level strings', () => { + for (const level of validSeverityLevels) { + expect(severityLevelFromString(level)).toBe(level); } }); }); diff --git a/packages/vue/src/index.bundle.ts b/packages/vue/src/index.bundle.ts index 3b1c401a9af9..232ec67cd0c1 100644 --- a/packages/vue/src/index.bundle.ts +++ b/packages/vue/src/index.bundle.ts @@ -6,14 +6,13 @@ export { EventStatus, Exception, Response, + SeverityLevel, StackFrame, Stacktrace, Thread, User, } from '@sentry/types'; -export { SeverityLevel } from '@sentry/utils'; - export { BrowserClient, BrowserOptions, From e2143256a55f8607e3ef454858107ce7beb5d3ea Mon Sep 17 00:00:00 2001 From: Katie Byers Date: Wed, 13 Apr 2022 18:49:57 -0700 Subject: [PATCH 041/204] ref(build): Turn on `isolatedModules` TS option (#4896) Note: This and https://github.com/getsentry/sentry-javascript/pull/4926 are together a (slightly updated) repeat of https://github.com/getsentry/sentry-javascript/pull/4497, to get it onto the main v7 branch. This applies [the TypeScript `isolatedModules` setting[1] to all packages in the repo, which is necessary in order for us to use any compiler other than `tsc`. (All other compilers handle each file separately, without understanding the relationships between them the way `tsc` does, so we need TS to warn us if we're doing anything which would make that a problem). It also makes the second of two code changes necessary in order to be compatible with the `isolatedModules` flag: all re-exported types and interfaces (anything that will get stripped away when compiling from TS to JS) are now exported using `export type`. This lets non-`tsc` compilers know that the exported types are eligible for stripping, in spite of the fact that said compilers can't follow the export path backwards to see the types' implementation. (The other code change, eliminating our usage of the `Severity` enum, was split off into the PR linked above.) [1] https://www.typescriptlang.org/tsconfig#isolatedModules --- packages/angular/src/index.ts | 4 +- packages/browser/src/exports.ts | 10 +-- packages/core/src/index.ts | 22 +++--- packages/hub/src/index.ts | 13 +--- packages/nextjs/src/index.server.ts | 2 +- packages/node/src/index.ts | 5 +- packages/node/src/transports/index.ts | 4 +- .../serverless/src/gcpfunction/general.ts | 2 +- packages/tracing/src/browser/index.ts | 8 +-- packages/tracing/src/index.bundle.ts | 7 +- packages/tracing/src/index.ts | 11 ++- packages/types/src/index.ts | 70 +++++++++---------- packages/typescript/tsconfig.json | 1 + packages/vue/src/index.bundle.ts | 6 +- 14 files changed, 83 insertions(+), 82 deletions(-) diff --git a/packages/angular/src/index.ts b/packages/angular/src/index.ts index 4b282610a16c..b6f188a35d14 100644 --- a/packages/angular/src/index.ts +++ b/packages/angular/src/index.ts @@ -1,7 +1,9 @@ +export type { ErrorHandlerOptions } from './errorhandler'; + export * from '@sentry/browser'; export { init } from './sdk'; -export { createErrorHandler, ErrorHandlerOptions, SentryErrorHandler } from './errorhandler'; +export { createErrorHandler, SentryErrorHandler } from './errorhandler'; export { getActiveTransaction, // TODO `instrumentAngularRouting` is just an alias for `routingInstrumentation`; deprecate the latter at some point diff --git a/packages/browser/src/exports.ts b/packages/browser/src/exports.ts index 1ee859e354d6..1af8a7d8aaf5 100644 --- a/packages/browser/src/exports.ts +++ b/packages/browser/src/exports.ts @@ -1,4 +1,4 @@ -export { +export type { Breadcrumb, BreadcrumbHint, Request, @@ -17,6 +17,9 @@ export { User, } from '@sentry/types'; +export type { BrowserOptions } from './client'; +export type { ReportDialogOptions } from './helpers'; + export { addGlobalEventProcessor, addBreadcrumb, @@ -41,8 +44,7 @@ export { withScope, } from '@sentry/core'; -export { BrowserClient, BrowserOptions } from './client'; - +export { BrowserClient } from './client'; export { defaultStackParsers, chromeStackParser, @@ -51,6 +53,6 @@ export { opera11StackParser, winjsStackParser, } from './stack-parsers'; -export { injectReportDialog, ReportDialogOptions } from './helpers'; +export { injectReportDialog } from './helpers'; export { defaultIntegrations, forceLoad, init, lastEventId, onLoad, showReportDialog, flush, close, wrap } from './sdk'; export { SDK_NAME } from './version'; diff --git a/packages/core/src/index.ts b/packages/core/src/index.ts index e4143f098e4a..ccf2aa519775 100644 --- a/packages/core/src/index.ts +++ b/packages/core/src/index.ts @@ -1,3 +1,13 @@ +export type { APIDetails } from './api'; +export type { ClientClass } from './sdk'; +export type { + BaseTransportOptions, + NewTransport, + TransportMakeRequestResponse, + TransportRequest, + TransportRequestExecutor, +} from './transports/base'; + export { addBreadcrumb, captureException, @@ -15,7 +25,6 @@ export { } from '@sentry/minimal'; export { addGlobalEventProcessor, getCurrentHub, getHubFromCarrier, Hub, makeMain, Scope, Session } from '@sentry/hub'; export { - APIDetails, getEnvelopeEndpointWithUrlEncodedAuth, getStoreEndpointWithUrlEncodedAuth, getRequestHeaders, @@ -24,16 +33,9 @@ export { } from './api'; export { BaseClient } from './baseclient'; export { eventToSentryRequest, sessionToSentryRequest } from './request'; -export { initAndBind, ClientClass } from './sdk'; +export { initAndBind } from './sdk'; export { NoopTransport } from './transports/noop'; -export { - BaseTransportOptions, - createTransport, - NewTransport, - TransportMakeRequestResponse, - TransportRequest, - TransportRequestExecutor, -} from './transports/base'; +export { createTransport } from './transports/base'; export { SDK_VERSION } from './version'; import * as Integrations from './integrations'; diff --git a/packages/hub/src/index.ts b/packages/hub/src/index.ts index 3d3b97fa239b..16c99ec7bc89 100644 --- a/packages/hub/src/index.ts +++ b/packages/hub/src/index.ts @@ -1,13 +1,6 @@ +export type { Carrier, Layer } from './hub'; + export { addGlobalEventProcessor, Scope } from './scope'; export { Session } from './session'; export { SessionFlusher } from './sessionflusher'; -export { - getCurrentHub, - getHubFromCarrier, - getMainCarrier, - Hub, - makeMain, - setHubOnCarrier, - Carrier, - Layer, -} from './hub'; +export { getCurrentHub, getHubFromCarrier, getMainCarrier, Hub, makeMain, setHubOnCarrier } from './hub'; diff --git a/packages/nextjs/src/index.server.ts b/packages/nextjs/src/index.server.ts index a57ee89443a5..35ef814d97cd 100644 --- a/packages/nextjs/src/index.server.ts +++ b/packages/nextjs/src/index.server.ts @@ -135,8 +135,8 @@ function filterTransactions(event: Event): Event | null { return event.type === 'transaction' && event.transaction === '/404' ? null : event; } +export type { SentryWebpackPluginOptions } from './config/types'; export { withSentryConfig } from './config'; -export { SentryWebpackPluginOptions } from './config/types'; export { withSentry } from './utils/withSentry'; // Wrap various server methods to enable error monitoring and tracing. (Note: This only happens for non-Vercel diff --git a/packages/node/src/index.ts b/packages/node/src/index.ts index cb83bd07cdcf..0e8febd8a92a 100644 --- a/packages/node/src/index.ts +++ b/packages/node/src/index.ts @@ -1,4 +1,4 @@ -export { +export type { Breadcrumb, BreadcrumbHint, Request, @@ -17,6 +17,8 @@ export { User, } from '@sentry/types'; +export type { NodeOptions } from './types'; + export { addGlobalEventProcessor, addBreadcrumb, @@ -41,7 +43,6 @@ export { withScope, } from '@sentry/core'; -export { NodeOptions } from './types'; export { NodeClient } from './client'; export { defaultIntegrations, init, lastEventId, flush, close, getSentryRelease } from './sdk'; export { deepReadDirSync } from './utils'; diff --git a/packages/node/src/transports/index.ts b/packages/node/src/transports/index.ts index 2cabeee08d5f..958562933321 100644 --- a/packages/node/src/transports/index.ts +++ b/packages/node/src/transports/index.ts @@ -1,4 +1,6 @@ +export type { NodeTransportOptions } from './new'; + export { BaseTransport } from './base'; export { HTTPTransport } from './http'; export { HTTPSTransport } from './https'; -export { makeNodeTransport, NodeTransportOptions } from './new'; +export { makeNodeTransport } from './new'; diff --git a/packages/serverless/src/gcpfunction/general.ts b/packages/serverless/src/gcpfunction/general.ts index becad86b5b0f..4ba794958ec4 100644 --- a/packages/serverless/src/gcpfunction/general.ts +++ b/packages/serverless/src/gcpfunction/general.ts @@ -62,4 +62,4 @@ export function configureScopeWithContext(scope: Scope, context: Context): void scope.setContext('gcp.function.context', { ...context } as SentryContext); } -export { Request, Response }; +export type { Request, Response }; diff --git a/packages/tracing/src/browser/index.ts b/packages/tracing/src/browser/index.ts index dd022fe2b8ec..2f7a3fe0d522 100644 --- a/packages/tracing/src/browser/index.ts +++ b/packages/tracing/src/browser/index.ts @@ -1,6 +1,4 @@ +export type { RequestInstrumentationOptions } from './request'; + export { BrowserTracing } from './browsertracing'; -export { - instrumentOutgoingRequests, - RequestInstrumentationOptions, - defaultRequestInstrumentationOptions, -} from './request'; +export { instrumentOutgoingRequests, defaultRequestInstrumentationOptions } from './request'; diff --git a/packages/tracing/src/index.bundle.ts b/packages/tracing/src/index.bundle.ts index 72ce4f3c19db..56e35e0ceaaa 100644 --- a/packages/tracing/src/index.bundle.ts +++ b/packages/tracing/src/index.bundle.ts @@ -1,4 +1,4 @@ -export { +export type { Breadcrumb, Request, SdkInfo, @@ -15,6 +15,8 @@ export { User, } from '@sentry/types'; +export type { BrowserOptions, ReportDialogOptions } from '@sentry/browser'; + export { addGlobalEventProcessor, addBreadcrumb, @@ -37,8 +39,7 @@ export { withScope, } from '@sentry/browser'; -export { BrowserOptions } from '@sentry/browser'; -export { BrowserClient, ReportDialogOptions } from '@sentry/browser'; +export { BrowserClient } from '@sentry/browser'; export { defaultIntegrations, forceLoad, diff --git a/packages/tracing/src/index.ts b/packages/tracing/src/index.ts index dcfea00f422a..0085ad5f6b0a 100644 --- a/packages/tracing/src/index.ts +++ b/packages/tracing/src/index.ts @@ -1,6 +1,9 @@ import { addExtensionMethods } from './hubextensions'; import * as Integrations from './integrations'; +export type { RequestInstrumentationOptions } from './browser'; +export type { SpanStatusType } from './span'; + export { Integrations }; // This is already exported as part of `Integrations` above (and for the moment will remain so for @@ -21,15 +24,11 @@ export { Integrations }; // For an example of of the new usage of BrowserTracing, see @sentry/nextjs index.client.ts export { BrowserTracing } from './browser'; -export { Span, SpanStatusType, spanStatusfromHttpCode } from './span'; +export { Span, spanStatusfromHttpCode } from './span'; // eslint-disable-next-line deprecation/deprecation export { SpanStatus } from './spanstatus'; export { Transaction } from './transaction'; -export { - instrumentOutgoingRequests, - RequestInstrumentationOptions, - defaultRequestInstrumentationOptions, -} from './browser'; +export { instrumentOutgoingRequests, defaultRequestInstrumentationOptions } from './browser'; export { IdleTransaction } from './idletransaction'; export { startIdleTransaction } from './hubextensions'; diff --git a/packages/types/src/index.ts b/packages/types/src/index.ts index b104f2e85930..84430c1d3976 100644 --- a/packages/types/src/index.ts +++ b/packages/types/src/index.ts @@ -1,10 +1,10 @@ -export { Breadcrumb, BreadcrumbHint } from './breadcrumb'; -export { Client } from './client'; -export { ClientReport } from './clientreport'; -export { Context, Contexts } from './context'; -export { DsnComponents, DsnLike, DsnProtocol } from './dsn'; -export { DebugImage, DebugImageType, DebugMeta } from './debugMeta'; -export { +export type { Breadcrumb, BreadcrumbHint } from './breadcrumb'; +export type { Client } from './client'; +export type { ClientReport } from './clientreport'; +export type { Context, Contexts } from './context'; +export type { DsnComponents, DsnLike, DsnProtocol } from './dsn'; +export type { DebugImage, DebugImageType, DebugMeta } from './debugMeta'; +export type { AttachmentItem, BaseEnvelopeHeaders, BaseEnvelopeItemHeaders, @@ -17,25 +17,25 @@ export { SessionItem, UserFeedbackItem, } from './envelope'; -export { ExtendedError } from './error'; -export { Event, EventHint } from './event'; -export { EventStatus } from './eventstatus'; -export { EventProcessor } from './eventprocessor'; -export { Exception } from './exception'; -export { Extra, Extras } from './extra'; -export { Hub } from './hub'; -export { Integration, IntegrationClass } from './integration'; -export { Mechanism } from './mechanism'; -export { ExtractedNodeRequestData, Primitive, WorkerLocation } from './misc'; -export { Options } from './options'; -export { Package } from './package'; -export { QueryParams, Request, SentryRequest, SentryRequestType } from './request'; -export { Response } from './response'; -export { Runtime } from './runtime'; -export { CaptureContext, Scope, ScopeContext } from './scope'; -export { SdkInfo } from './sdkinfo'; -export { SdkMetadata } from './sdkmetadata'; -export { +export type { ExtendedError } from './error'; +export type { Event, EventHint } from './event'; +export type { EventStatus } from './eventstatus'; +export type { EventProcessor } from './eventprocessor'; +export type { Exception } from './exception'; +export type { Extra, Extras } from './extra'; +export type { Hub } from './hub'; +export type { Integration, IntegrationClass } from './integration'; +export type { Mechanism } from './mechanism'; +export type { ExtractedNodeRequestData, Primitive, WorkerLocation } from './misc'; +export type { Options } from './options'; +export type { Package } from './package'; +export type { QueryParams, Request, SentryRequest, SentryRequestType } from './request'; +export type { Response } from './response'; +export type { Runtime } from './runtime'; +export type { CaptureContext, Scope, ScopeContext } from './scope'; +export type { SdkInfo } from './sdkinfo'; +export type { SdkMetadata } from './sdkmetadata'; +export type { SessionAggregates, AggregationCounts, Session, @@ -47,11 +47,11 @@ export { } from './session'; // eslint-disable-next-line deprecation/deprecation -export { Severity, SeverityLevel } from './severity'; -export { Span, SpanContext } from './span'; -export { StackFrame } from './stackframe'; -export { Stacktrace, StackParser, StackLineParser, StackLineParserFn } from './stacktrace'; -export { +export type { Severity, SeverityLevel } from './severity'; +export type { Span, SpanContext } from './span'; +export type { StackFrame } from './stackframe'; +export type { Stacktrace, StackParser, StackLineParser, StackLineParserFn } from './stacktrace'; +export type { CustomSamplingContext, Measurements, SamplingContext, @@ -61,7 +61,7 @@ export { TransactionMetadata, TransactionSamplingMethod, } from './transaction'; -export { Thread } from './thread'; -export { Outcome, Transport, TransportOptions, TransportClass } from './transport'; -export { User, UserFeedback } from './user'; -export { WrappedFunction } from './wrappedfunction'; +export type { Thread } from './thread'; +export type { Outcome, Transport, TransportOptions, TransportClass } from './transport'; +export type { User, UserFeedback } from './user'; +export type { WrappedFunction } from './wrappedfunction'; diff --git a/packages/typescript/tsconfig.json b/packages/typescript/tsconfig.json index d6858094b969..3a427cc596c9 100644 --- a/packages/typescript/tsconfig.json +++ b/packages/typescript/tsconfig.json @@ -6,6 +6,7 @@ "downlevelIteration": true, "importHelpers": true, "inlineSources": true, + "isolatedModules": true, "lib": ["es6", "dom"], // "module": "commonjs", // implied by "target" : "es5" "moduleResolution": "node", diff --git a/packages/vue/src/index.bundle.ts b/packages/vue/src/index.bundle.ts index 232ec67cd0c1..c2b744280372 100644 --- a/packages/vue/src/index.bundle.ts +++ b/packages/vue/src/index.bundle.ts @@ -1,4 +1,4 @@ -export { +export type { Breadcrumb, Request, SdkInfo, @@ -13,9 +13,10 @@ export { User, } from '@sentry/types'; +export type { BrowserOptions, ReportDialogOptions } from '@sentry/browser'; + export { BrowserClient, - BrowserOptions, defaultIntegrations, forceLoad, lastEventId, @@ -24,7 +25,6 @@ export { flush, close, wrap, - ReportDialogOptions, addGlobalEventProcessor, addBreadcrumb, captureException, From 3c7faf910d48979ece6d0b7dafdbb13d5b9fe651 Mon Sep 17 00:00:00 2001 From: Katie Byers Date: Wed, 13 Apr 2022 18:55:43 -0700 Subject: [PATCH 042/204] chore(types): Update `@types/jest` (#4929) This updates `@types/jest` to the latest version, a change which was missed in the recent jest update. --- package.json | 2 +- yarn.lock | 67 +++++++++++++++------------------------------------- 2 files changed, 20 insertions(+), 49 deletions(-) diff --git a/package.json b/package.json index 7075a4f5f6fd..bf91547479aa 100644 --- a/package.json +++ b/package.json @@ -61,7 +61,7 @@ "@size-limit/preset-small-lib": "^4.5.5", "@strictsoftware/typedoc-plugin-monorepo": "^0.3.1", "@types/chai": "^4.1.3", - "@types/jest": "^24.0.11", + "@types/jest": "^27.4.1", "@types/jsdom": "^16.2.3", "@types/mocha": "^5.2.0", "@types/node": "~10.17.0", diff --git a/yarn.lock b/yarn.lock index 7a8d5c41fb37..482f2369ee5b 100644 --- a/yarn.lock +++ b/yarn.lock @@ -4043,12 +4043,13 @@ "@types/puppeteer" "*" jest-environment-node ">=24 <=26" -"@types/jest@^24.0.11": - version "24.9.1" - resolved "https://registry.yarnpkg.com/@types/jest/-/jest-24.9.1.tgz#02baf9573c78f1b9974a5f36778b366aa77bd534" - integrity sha512-Fb38HkXSVA4L8fGKEZ6le5bB8r6MRWlOCZbVuWZcmOMSCd2wCYOwN1ibj8daIoV9naq7aaOZjrLCoCMptKU/4Q== +"@types/jest@^27.4.1": + version "27.4.1" + resolved "https://registry.yarnpkg.com/@types/jest/-/jest-27.4.1.tgz#185cbe2926eaaf9662d340cc02e548ce9e11ab6d" + integrity sha512-23iPJADSmicDVrWk+HT58LMJtzLAnB2AgIzplQuq/bSrGaxCrlvRFjGbXmamnnk/mAmCdLStiGqggu28ocUyiw== dependencies: - jest-diff "^24.3.0" + jest-matcher-utils "^27.0.0" + pretty-format "^27.0.0" "@types/jquery@*": version "3.5.5" @@ -5001,7 +5002,7 @@ ansi-regex@^3.0.0: resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-3.0.0.tgz#ed0317c322064f79466c02966bddb605ab37d998" integrity sha1-7QMXwyIGT3lGbAKWa922Bas32Zg= -ansi-regex@^4.0.0, ansi-regex@^4.1.0: +ansi-regex@^4.1.0: version "4.1.0" resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-4.1.0.tgz#8b9f8f08cf1acb843756a839ca8c7e3168c51997" integrity sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg== @@ -9192,11 +9193,6 @@ di@^0.0.1: resolved "https://registry.yarnpkg.com/di/-/di-0.0.1.tgz#806649326ceaa7caa3306d75d985ea2748ba913c" integrity sha1-gGZJMmzqp8qjMG112YXqJ0i6kTw= -diff-sequences@^24.9.0: - version "24.9.0" - resolved "https://registry.yarnpkg.com/diff-sequences/-/diff-sequences-24.9.0.tgz#5715d6244e2aa65f48bba0bc972db0b0b11e95b5" - integrity sha512-Dj6Wk3tWyTE+Fo1rW8v0Xhwk80um6yFYKbuAxc9c3EZxIHFDYwbi34Uk42u1CdnIiVorvt4RmlSDjIPyzGC2ew== - diff-sequences@^27.4.0: version "27.4.0" resolved "https://registry.yarnpkg.com/diff-sequences/-/diff-sequences-27.4.0.tgz#d783920ad8d06ec718a060d00196dfef25b132a5" @@ -13998,16 +13994,6 @@ jest-dev-server@^4.4.0: tree-kill "^1.2.2" wait-on "^3.3.0" -jest-diff@^24.3.0: - version "24.9.0" - resolved "https://registry.yarnpkg.com/jest-diff/-/jest-diff-24.9.0.tgz#931b7d0d5778a1baf7452cb816e325e3724055da" - integrity sha512-qMfrTs8AdJE2iqrTp0hzh7kTd2PQWrsFyj9tORoKmu32xjPjeE4NyjVRDz8ybYwqS2ik8N4hsIpiVTyFeo2lBQ== - dependencies: - chalk "^2.0.1" - diff-sequences "^24.9.0" - jest-get-type "^24.9.0" - pretty-format "^24.9.0" - jest-diff@^27.2.5, jest-diff@^27.4.2: version "27.4.2" resolved "https://registry.yarnpkg.com/jest-diff/-/jest-diff-27.4.2.tgz#786b2a5211d854f848e2dcc1e324448e9481f36f" @@ -14092,11 +14078,6 @@ jest-environment-puppeteer@^4.4.0: jest-dev-server "^4.4.0" merge-deep "^3.0.2" -jest-get-type@^24.9.0: - version "24.9.0" - resolved "https://registry.yarnpkg.com/jest-get-type/-/jest-get-type-24.9.0.tgz#1684a0c8a50f2e4901b6644ae861f579eed2ef0e" - integrity sha512-lUseMzAley4LhIcpSP9Jf+fTrQ4a1yHQwLNeeVa2cEmbCGeoZAtYPOIv8JaxLD/sUpKxetKGP+gsHl8f8TSj8Q== - jest-get-type@^27.0.6, jest-get-type@^27.4.0: version "27.4.0" resolved "https://registry.yarnpkg.com/jest-get-type/-/jest-get-type-27.4.0.tgz#7503d2663fffa431638337b3998d39c5e928e9b5" @@ -14187,6 +14168,16 @@ jest-matcher-utils@=27.2.5: jest-get-type "^27.0.6" pretty-format "^27.2.5" +jest-matcher-utils@^27.0.0, jest-matcher-utils@^27.5.1: + version "27.5.1" + resolved "https://registry.yarnpkg.com/jest-matcher-utils/-/jest-matcher-utils-27.5.1.tgz#9c0cdbda8245bc22d2331729d1091308b40cf8ab" + integrity sha512-z2uTx/T6LBaCoNWNFWwChLBKYxTMcGBRjAt+2SbP929/Fflb9aa5LGma654Rz8z9HLxsrUaYzxE9T/EFIL/PAw== + dependencies: + chalk "^4.0.0" + jest-diff "^27.5.1" + jest-get-type "^27.5.1" + pretty-format "^27.5.1" + jest-matcher-utils@^27.2.5: version "27.4.2" resolved "https://registry.yarnpkg.com/jest-matcher-utils/-/jest-matcher-utils-27.4.2.tgz#d17c5038607978a255e0a9a5c32c24e984b6c60b" @@ -14197,16 +14188,6 @@ jest-matcher-utils@^27.2.5: jest-get-type "^27.4.0" pretty-format "^27.4.2" -jest-matcher-utils@^27.5.1: - version "27.5.1" - resolved "https://registry.yarnpkg.com/jest-matcher-utils/-/jest-matcher-utils-27.5.1.tgz#9c0cdbda8245bc22d2331729d1091308b40cf8ab" - integrity sha512-z2uTx/T6LBaCoNWNFWwChLBKYxTMcGBRjAt+2SbP929/Fflb9aa5LGma654Rz8z9HLxsrUaYzxE9T/EFIL/PAw== - dependencies: - chalk "^4.0.0" - jest-diff "^27.5.1" - jest-get-type "^27.5.1" - pretty-format "^27.5.1" - jest-message-util@^24.9.0: version "24.9.0" resolved "https://registry.yarnpkg.com/jest-message-util/-/jest-message-util-24.9.0.tgz#527f54a1e380f5e202a8d1149b0ec872f43119e3" @@ -18423,17 +18404,7 @@ pretty-error@^4.0.0: lodash "^4.17.20" renderkid "^3.0.0" -pretty-format@^24.9.0: - version "24.9.0" - resolved "https://registry.yarnpkg.com/pretty-format/-/pretty-format-24.9.0.tgz#12fac31b37019a4eea3c11aa9a959eb7628aa7c9" - integrity sha512-00ZMZUiHaJrNfk33guavqgvfJS30sLYf0f8+Srklv0AMPodGGHcoHgksZ3OThYnIvOd+8yMCn0YiEOogjlgsnA== - dependencies: - "@jest/types" "^24.9.0" - ansi-regex "^4.0.0" - ansi-styles "^3.2.0" - react-is "^16.8.4" - -pretty-format@^27.0.2, pretty-format@^27.5.1: +pretty-format@^27.0.0, pretty-format@^27.0.2, pretty-format@^27.5.1: version "27.5.1" resolved "https://registry.yarnpkg.com/pretty-format/-/pretty-format-27.5.1.tgz#2181879fdea51a7a5851fb39d920faa63f01d88e" integrity sha512-Qb1gy5OrP5+zDf2Bvnzdl3jsTf1qXVMazbvCoKhtKqVs4/YK4ozX4gKQJJVyNe+cajNPn0KoC0MC3FUmaHWEmQ== @@ -18918,7 +18889,7 @@ react-error-boundary@^3.1.0: dependencies: "@babel/runtime" "^7.12.5" -react-is@16.13.1, react-is@^16.6.0, react-is@^16.7.0, react-is@^16.8.1, react-is@^16.8.4: +react-is@16.13.1, react-is@^16.6.0, react-is@^16.7.0, react-is@^16.8.1: version "16.13.1" resolved "https://registry.yarnpkg.com/react-is/-/react-is-16.13.1.tgz#789729a4dc36de2999dc156dd6c1d9c18cea56a4" integrity sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ== From fa349149dead8ebcf5fa00b2f4e6795205e949a5 Mon Sep 17 00:00:00 2001 From: Lukas Stracke Date: Thu, 14 Apr 2022 13:12:36 +0200 Subject: [PATCH 043/204] ref: Inject Transports into Client (#4921) Changes the way we initialize Transports. Previously, the Transport was initialized within the Client constructor (by calling setupTransport()). Change this so that the Transport is initialized beforehand and then injected into the client. This is currently (i.e. with this PR) happening via two additional arguments in the Client class constructors. The reason for two arguments is that we're still using both, the old `Transport` classes and the `NewTransport` interface. In the future, `NewTransport` will replace `Transport` and (once the old Transport is removed) the transport is then passed into the Client constructor as a property of the options object. * add the injection logic * initialize the transports in Browser and Node and pass them to the `Browser/NodeClient` initialization * add client-external transport setup functions (extracted from setupTransport()) * add basic tests for these transport setup functions * delete the client-internal setupTransport methods * fixe a bunch of tests that are initializing clients --- packages/browser/src/client.ts | 47 +--- packages/browser/src/sdk.ts | 4 +- packages/browser/src/transports/index.ts | 2 + packages/browser/src/transports/setup.ts | 68 +++++ packages/browser/test/unit/index.test.ts | 97 ++++--- .../unit/integrations/linkederrors.test.ts | 10 +- .../test/unit/transports/setup.test.ts | 98 +++++++ packages/core/src/baseclient.ts | 25 +- packages/core/src/sdk.ts | 19 +- packages/core/src/transports/base.ts | 9 - packages/core/test/lib/base.test.ts | 248 +++++++++++------- packages/core/test/lib/sdk.test.ts | 20 +- packages/core/test/mocks/client.ts | 39 +-- packages/node/src/client.ts | 52 +--- packages/node/src/sdk.ts | 4 +- packages/node/src/transports/index.ts | 1 + packages/node/src/transports/setup.ts | 52 ++++ packages/node/test/client.test.ts | 39 ++- packages/node/test/handlers.test.ts | 31 ++- packages/node/test/index.test.ts | 244 +++++++++-------- packages/node/test/integrations/http.test.ts | 37 ++- .../test/integrations/linkederrors.test.ts | 16 +- packages/node/test/transports/setup.test.ts | 61 +++++ .../test/browser/backgroundtab.test.ts | 4 +- .../test/browser/browsertracing.test.ts | 10 +- packages/tracing/test/browser/request.test.ts | 4 +- packages/tracing/test/errors.test.ts | 4 +- packages/tracing/test/hub.test.ts | 132 ++++++---- packages/tracing/test/idletransaction.test.ts | 3 +- packages/tracing/test/span.test.ts | 24 +- 30 files changed, 889 insertions(+), 515 deletions(-) create mode 100644 packages/browser/src/transports/setup.ts create mode 100644 packages/browser/test/unit/transports/setup.test.ts create mode 100644 packages/node/src/transports/setup.ts create mode 100644 packages/node/test/transports/setup.test.ts diff --git a/packages/browser/src/client.ts b/packages/browser/src/client.ts index 5116df6104d1..8666eaebe8d5 100644 --- a/packages/browser/src/client.ts +++ b/packages/browser/src/client.ts @@ -1,12 +1,11 @@ -import { BaseClient, getEnvelopeEndpointWithUrlEncodedAuth, initAPIDetails, Scope, SDK_VERSION } from '@sentry/core'; -import { Event, EventHint, Options, Severity, SeverityLevel, Transport, TransportOptions } from '@sentry/types'; -import { getGlobalObject, logger, stackParserFromOptions, supportsFetch } from '@sentry/utils'; +import { BaseClient, NewTransport, Scope, SDK_VERSION } from '@sentry/core'; +import { Event, EventHint, Options, Severity, SeverityLevel, Transport } from '@sentry/types'; +import { getGlobalObject, logger, stackParserFromOptions } from '@sentry/utils'; import { eventFromException, eventFromMessage } from './eventbuilder'; import { IS_DEBUG_BUILD } from './flags'; import { injectReportDialog, ReportDialogOptions } from './helpers'; import { Breadcrumbs } from './integrations'; -import { FetchTransport, makeNewFetchTransport, makeNewXHRTransport, XHRTransport } from './transports'; /** * Configuration options for the Sentry Browser SDK. @@ -40,7 +39,7 @@ export class BrowserClient extends BaseClient { * * @param options Configuration options for this SDK. */ - public constructor(options: BrowserOptions = {}) { + public constructor(options: BrowserOptions = {}, transport: Transport, newTransport?: NewTransport) { options._metadata = options._metadata || {}; options._metadata.sdk = options._metadata.sdk || { name: 'sentry.javascript.browser', @@ -53,7 +52,7 @@ export class BrowserClient extends BaseClient { version: SDK_VERSION, }; - super(options); + super(options, transport, newTransport); } /** @@ -122,40 +121,4 @@ export class BrowserClient extends BaseClient { } super._sendEvent(event); } - - /** - * @inheritDoc - */ - protected _setupTransport(): Transport { - if (!this._options.dsn) { - // We return the noop transport here in case there is no Dsn. - return super._setupTransport(); - } - - const transportOptions: TransportOptions = { - ...this._options.transportOptions, - dsn: this._options.dsn, - tunnel: this._options.tunnel, - sendClientReports: this._options.sendClientReports, - _metadata: this._options._metadata, - }; - - const api = initAPIDetails(transportOptions.dsn, transportOptions._metadata, transportOptions.tunnel); - const url = getEnvelopeEndpointWithUrlEncodedAuth(api.dsn, api.tunnel); - - if (this._options.transport) { - return new this._options.transport(transportOptions); - } - if (supportsFetch()) { - const requestOptions: RequestInit = { ...transportOptions.fetchParameters }; - this._newTransport = makeNewFetchTransport({ requestOptions, url }); - return new FetchTransport(transportOptions); - } - - this._newTransport = makeNewXHRTransport({ - url, - headers: transportOptions.headers, - }); - return new XHRTransport(transportOptions); - } } diff --git a/packages/browser/src/sdk.ts b/packages/browser/src/sdk.ts index 60c53b364e9c..94ec87cb4279 100644 --- a/packages/browser/src/sdk.ts +++ b/packages/browser/src/sdk.ts @@ -7,6 +7,7 @@ import { IS_DEBUG_BUILD } from './flags'; import { ReportDialogOptions, wrap as internalWrap } from './helpers'; import { Breadcrumbs, Dedupe, GlobalHandlers, LinkedErrors, TryCatch, UserAgent } from './integrations'; import { defaultStackParsers } from './stack-parsers'; +import { setupBrowserTransport } from './transports/setup'; export const defaultIntegrations = [ new CoreIntegrations.InboundFilters(), @@ -97,7 +98,8 @@ export function init(options: BrowserOptions = {}): void { options.stackParser = defaultStackParsers; } - initAndBind(BrowserClient, options); + const { transport, newTransport } = setupBrowserTransport(options); + initAndBind(BrowserClient, options, transport, newTransport); if (options.autoSessionTracking) { startSessionTracking(); diff --git a/packages/browser/src/transports/index.ts b/packages/browser/src/transports/index.ts index 287e14e0ac50..31871a76d01c 100644 --- a/packages/browser/src/transports/index.ts +++ b/packages/browser/src/transports/index.ts @@ -4,3 +4,5 @@ export { XHRTransport } from './xhr'; export { makeNewFetchTransport } from './new-fetch'; export { makeNewXHRTransport } from './new-xhr'; + +export { setupBrowserTransport } from './setup'; diff --git a/packages/browser/src/transports/setup.ts b/packages/browser/src/transports/setup.ts new file mode 100644 index 000000000000..0af6aad90676 --- /dev/null +++ b/packages/browser/src/transports/setup.ts @@ -0,0 +1,68 @@ +import { + BaseTransportOptions, + getEnvelopeEndpointWithUrlEncodedAuth, + initAPIDetails, + NewTransport, + NoopTransport, +} from '@sentry/core'; +import { Transport, TransportOptions } from '@sentry/types'; +import { supportsFetch } from '@sentry/utils'; + +import { BrowserOptions } from '../client'; +import { FetchTransport } from './fetch'; +import { makeNewFetchTransport } from './new-fetch'; +import { makeNewXHRTransport } from './new-xhr'; +import { XHRTransport } from './xhr'; + +export interface BrowserTransportOptions extends BaseTransportOptions { + // options to pass into fetch request + fetchParams: Record; + headers?: Record; + sendClientReports?: boolean; +} + +/** + * Sets up Browser transports based on the passed `options`. If available, the returned + * transport will use the fetch API. In case fetch is not supported, an XMLHttpRequest + * based transport is created. + * + * @returns an object currently still containing both, the old `Transport` and + * `NewTransport` which will eventually replace `Transport`. Once this is replaced, + * this function will return a ready to use `NewTransport`. + */ +// TODO(v7): Adjust return value when NewTransport is the default +export function setupBrowserTransport(options: BrowserOptions): { transport: Transport; newTransport?: NewTransport } { + if (!options.dsn) { + // We return the noop transport here in case there is no Dsn. + return { transport: new NoopTransport() }; + } + + const transportOptions: TransportOptions = { + ...options.transportOptions, + dsn: options.dsn, + tunnel: options.tunnel, + sendClientReports: options.sendClientReports, + _metadata: options._metadata, + }; + + const api = initAPIDetails(transportOptions.dsn, transportOptions._metadata, transportOptions.tunnel); + const url = getEnvelopeEndpointWithUrlEncodedAuth(api.dsn, api.tunnel); + + if (options.transport) { + return { transport: new options.transport(transportOptions) }; + } + + if (supportsFetch()) { + const requestOptions: RequestInit = { ...transportOptions.fetchParameters }; + const newTransport = makeNewFetchTransport({ requestOptions, url }); + const fetchTransport = new FetchTransport(transportOptions); + return { transport: fetchTransport, newTransport }; + } + + const newTransport = makeNewXHRTransport({ + url, + headers: transportOptions.headers, + }); + const transport = new XHRTransport(transportOptions); + return { transport, newTransport }; +} diff --git a/packages/browser/test/unit/index.test.ts b/packages/browser/test/unit/index.test.ts index 707354fe7d1d..9371e60073c1 100644 --- a/packages/browser/test/unit/index.test.ts +++ b/packages/browser/test/unit/index.test.ts @@ -75,7 +75,7 @@ describe('SentryBrowser', () => { describe('showReportDialog', () => { describe('user', () => { const EX_USER = { email: 'test@example.com' }; - const client = new BrowserClient({ dsn }); + const client = new BrowserClient({ dsn }, new SimpleTransport({ dsn })); const reportDialogSpy = jest.spyOn(client, 'showReportDialog'); beforeEach(() => { @@ -140,30 +140,36 @@ describe('SentryBrowser', () => { it('should capture a message', done => { getCurrentHub().bindClient( - new BrowserClient({ - beforeSend: (event: Event): Event | null => { - expect(event.message).toBe('test'); - expect(event.exception).toBeUndefined(); - done(); - return event; + new BrowserClient( + { + beforeSend: (event: Event): Event | null => { + expect(event.message).toBe('test'); + expect(event.exception).toBeUndefined(); + done(); + return event; + }, + dsn, }, - dsn, - }), + new SimpleTransport({ dsn }), + ), ); captureMessage('test'); }); it('should capture an event', done => { getCurrentHub().bindClient( - new BrowserClient({ - beforeSend: (event: Event): Event | null => { - expect(event.message).toBe('event'); - expect(event.exception).toBeUndefined(); - done(); - return event; + new BrowserClient( + { + beforeSend: (event: Event): Event | null => { + expect(event.message).toBe('event'); + expect(event.exception).toBeUndefined(); + done(); + return event; + }, + dsn, }, - dsn, - }), + new SimpleTransport({ dsn }), + ), ); captureEvent({ message: 'event' }); }); @@ -171,11 +177,14 @@ describe('SentryBrowser', () => { it('should not dedupe an event on bound client', async () => { const localBeforeSend = jest.fn(); getCurrentHub().bindClient( - new BrowserClient({ - beforeSend: localBeforeSend, - dsn, - integrations: [], - }), + new BrowserClient( + { + beforeSend: localBeforeSend, + dsn, + integrations: [], + }, + new SimpleTransport({ dsn }), + ), ); captureMessage('event222'); @@ -189,11 +198,14 @@ describe('SentryBrowser', () => { it('should use inboundfilter rules of bound client', async () => { const localBeforeSend = jest.fn(); getCurrentHub().bindClient( - new BrowserClient({ - beforeSend: localBeforeSend, - dsn, - integrations: [new Integrations.InboundFilters({ ignoreErrors: ['capture'] })], - }), + new BrowserClient( + { + beforeSend: localBeforeSend, + dsn, + integrations: [new Integrations.InboundFilters({ ignoreErrors: ['capture'] })], + }, + new SimpleTransport({ dsn }), + ), ); captureMessage('capture'); @@ -248,16 +260,16 @@ describe('SentryBrowser initialization', () => { const sdkData = (getCurrentHub().getClient() as any).getTransport()._api.metadata?.sdk; - expect(sdkData.name).toBe('sentry.javascript.browser'); - expect(sdkData.packages[0].name).toBe('npm:@sentry/browser'); - expect(sdkData.packages[0].version).toBe(SDK_VERSION); - expect(sdkData.version).toBe(SDK_VERSION); + expect(sdkData?.name).toBe('sentry.javascript.browser'); + expect(sdkData?.packages[0].name).toBe('npm:@sentry/browser'); + expect(sdkData?.packages[0].version).toBe(SDK_VERSION); + expect(sdkData?.version).toBe(SDK_VERSION); }); it('should set SDK data when instantiating a client directly', () => { - const client = new BrowserClient({ dsn }); + const client = new BrowserClient({ dsn }, new SimpleTransport({ dsn })); - const sdkData = (client as any).getTransport()._api.metadata?.sdk; + const sdkData = (client.getTransport() as any)._api.metadata?.sdk; expect(sdkData.name).toBe('sentry.javascript.browser'); expect(sdkData.packages[0].name).toBe('npm:@sentry/browser'); @@ -298,15 +310,18 @@ describe('SentryBrowser initialization', () => { describe('wrap()', () => { it('should wrap and call function while capturing error', done => { getCurrentHub().bindClient( - new BrowserClient({ - beforeSend: (event: Event): Event | null => { - expect(event.exception!.values![0].type).toBe('TypeError'); - expect(event.exception!.values![0].value).toBe('mkey'); - done(); - return null; + new BrowserClient( + { + beforeSend: (event: Event): Event | null => { + expect(event.exception!.values![0].type).toBe('TypeError'); + expect(event.exception!.values![0].value).toBe('mkey'); + done(); + return null; + }, + dsn, }, - dsn, - }), + new SimpleTransport({ dsn }), + ), ); try { diff --git a/packages/browser/test/unit/integrations/linkederrors.test.ts b/packages/browser/test/unit/integrations/linkederrors.test.ts index 2589487dbcac..6650877ff39b 100644 --- a/packages/browser/test/unit/integrations/linkederrors.test.ts +++ b/packages/browser/test/unit/integrations/linkederrors.test.ts @@ -4,6 +4,7 @@ import { createStackParser } from '@sentry/utils'; import { BrowserClient } from '../../../src/client'; import * as LinkedErrorsModule from '../../../src/integrations/linkederrors'; import { defaultStackParsers } from '../../../src/stack-parsers'; +import { setupBrowserTransport } from '../../../src/transports'; const parser = createStackParser(...defaultStackParsers); @@ -38,7 +39,8 @@ describe('LinkedErrors', () => { one.cause = two; const originalException = one; - const client = new BrowserClient({ stackParser: parser }); + const options = { stackParser: parser }; + const client = new BrowserClient(options, setupBrowserTransport(options).transport); return client.eventFromException(originalException).then(event => { const result = LinkedErrorsModule._handler(parser, 'cause', 5, event, { originalException, @@ -68,7 +70,8 @@ describe('LinkedErrors', () => { one.reason = two; const originalException = one; - const client = new BrowserClient({ stackParser: parser }); + const options = { stackParser: parser }; + const client = new BrowserClient(options, setupBrowserTransport(options).transport); return client.eventFromException(originalException).then(event => { const result = LinkedErrorsModule._handler(parser, 'reason', 5, event, { originalException, @@ -94,7 +97,8 @@ describe('LinkedErrors', () => { one.cause = two; two.cause = three; - const client = new BrowserClient({ stackParser: parser }); + const options = { stackParser: parser }; + const client = new BrowserClient(options, setupBrowserTransport(options).transport); const originalException = one; return client.eventFromException(originalException).then(event => { const result = LinkedErrorsModule._handler(parser, 'cause', 2, event, { diff --git a/packages/browser/test/unit/transports/setup.test.ts b/packages/browser/test/unit/transports/setup.test.ts new file mode 100644 index 000000000000..2683a0619aea --- /dev/null +++ b/packages/browser/test/unit/transports/setup.test.ts @@ -0,0 +1,98 @@ +import { NoopTransport } from '@sentry/core'; + +import { + FetchTransport, + makeNewFetchTransport, + makeNewXHRTransport, + setupBrowserTransport, + XHRTransport, +} from '../../../src/transports'; +import { SimpleTransport } from '../mocks/simpletransport'; + +const DSN = 'https://username@domain/123'; + +let fetchSupported = true; + +jest.mock('@sentry/utils', () => { + const original = jest.requireActual('@sentry/utils'); + return { + ...original, + supportsFetch(): boolean { + return fetchSupported; + }, + getGlobalObject(): any { + return { + fetch: () => {}, + }; + }, + }; +}); + +jest.mock('../../../src/transports/new-fetch', () => { + const original = jest.requireActual('../../../src/transports/new-fetch'); + return { + ...original, + makeNewFetchTransport: jest.fn(() => ({ + send: () => Promise.resolve({ status: 'success' }), + flush: () => Promise.resolve(true), + })), + }; +}); + +jest.mock('../../../src/transports/new-xhr', () => { + const original = jest.requireActual('../../../src/transports/new-xhr'); + return { + ...original, + makeNewXHRTransport: jest.fn(() => ({ + send: () => Promise.resolve({ status: 'success' }), + flush: () => Promise.resolve(true), + })), + }; +}); + +describe('setupBrowserTransport', () => { + afterEach(() => jest.clearAllMocks()); + + afterAll(() => jest.resetAllMocks()); + + it('returns NoopTransport if no dsn is passed', () => { + const { transport, newTransport } = setupBrowserTransport({}); + + expect(transport).toBeDefined(); + expect(transport).toBeInstanceOf(NoopTransport); + expect(newTransport).toBeUndefined(); + }); + + it('returns the instantiated transport passed via the options', () => { + const options = { dsn: DSN, transport: SimpleTransport }; + const { transport, newTransport } = setupBrowserTransport(options); + + expect(transport).toBeDefined(); + expect(transport).toBeInstanceOf(SimpleTransport); + expect(newTransport).toBeUndefined(); + }); + + it('returns fetchTransports if fetch is supported', () => { + const options = { dsn: DSN }; + const { transport, newTransport } = setupBrowserTransport(options); + + expect(transport).toBeDefined(); + expect(transport).toBeInstanceOf(FetchTransport); + expect(newTransport).toBeDefined(); + expect(makeNewFetchTransport).toHaveBeenCalledTimes(1); + expect(makeNewXHRTransport).toHaveBeenCalledTimes(0); + }); + + it('returns xhrTransports if fetch is not supported', () => { + fetchSupported = false; + + const options = { dsn: DSN }; + const { transport, newTransport } = setupBrowserTransport(options); + + expect(transport).toBeDefined(); + expect(transport).toBeInstanceOf(XHRTransport); + expect(newTransport).toBeDefined(); + expect(makeNewFetchTransport).toHaveBeenCalledTimes(0); + expect(makeNewXHRTransport).toHaveBeenCalledTimes(1); + }); +}); diff --git a/packages/core/src/baseclient.ts b/packages/core/src/baseclient.ts index 8486d5e03b9c..2888e9853f5e 100644 --- a/packages/core/src/baseclient.ts +++ b/packages/core/src/baseclient.ts @@ -29,12 +29,11 @@ import { uuid4, } from '@sentry/utils'; -import { initAPIDetails } from './api'; +import { APIDetails, initAPIDetails } from './api'; import { IS_DEBUG_BUILD } from './flags'; import { IntegrationIndex, setupIntegrations } from './integration'; import { createEventEnvelope, createSessionEnvelope } from './request'; import { NewTransport } from './transports/base'; -import { NoopTransport } from './transports/noop'; const ALREADY_SEEN_ERROR = "Not capturing exception because it's already been captured."; @@ -92,8 +91,10 @@ export abstract class BaseClient implements Client { * Initializes this client instance. * * @param options Options for the client. + * @param transport The (old) Transport instance for the client to use (TODO(v7): remove) + * @param newTransport The NewTransport instance for the client to use */ - protected constructor(options: O) { + protected constructor(options: O, transport: Transport, newTransport?: NewTransport) { this._options = options; if (options.dsn) { @@ -102,7 +103,16 @@ export abstract class BaseClient implements Client { IS_DEBUG_BUILD && logger.warn('No DSN provided, client will not do anything.'); } - this._transport = this._setupTransport(); + // TODO(v7): remove old transport + this._transport = transport; + this._newTransport = newTransport; + + // TODO(v7): refactor this to keep metadata/api outside of transport. This hack is used to + // satisfy tests until we move to NewTransport where we have to revisit this. + (this._transport as unknown as { _api: Partial })._api = { + ...((this._transport as unknown as { _api: Partial })._api || {}), + metadata: options._metadata || {}, + }; } /** @@ -676,13 +686,6 @@ export abstract class BaseClient implements Client { ); } - /** - * Sets up the transport so it can be used later to send requests. - */ - protected _setupTransport(): Transport { - return new NoopTransport(); - } - /** * @inheritDoc */ diff --git a/packages/core/src/sdk.ts b/packages/core/src/sdk.ts index 381a54839a0a..97c8b349a235 100644 --- a/packages/core/src/sdk.ts +++ b/packages/core/src/sdk.ts @@ -1,11 +1,16 @@ import { getCurrentHub } from '@sentry/hub'; -import { Client, Options } from '@sentry/types'; +import { Client, Options, Transport } from '@sentry/types'; import { logger } from '@sentry/utils'; import { IS_DEBUG_BUILD } from './flags'; +import { NewTransport } from './transports/base'; /** A class object that can instantiate Client objects. */ -export type ClientClass = new (options: O) => F; +export type ClientClass = new ( + options: O, + transport: Transport, + newTransport?: NewTransport, +) => F; /** * Internal function to create a new SDK client instance. The client is @@ -14,7 +19,12 @@ export type ClientClass = new (options: O) * @param clientClass The client class to instantiate. * @param options Options to pass to the client. */ -export function initAndBind(clientClass: ClientClass, options: O): void { +export function initAndBind( + clientClass: ClientClass, + options: O, + transport: Transport, + newTransport?: NewTransport, +): void { if (options.debug === true) { if (IS_DEBUG_BUILD) { logger.enable(); @@ -29,6 +39,7 @@ export function initAndBind(clientClass: Cl if (scope) { scope.update(options.initialScope); } - const client = new clientClass(options); + + const client = new clientClass(options, transport, newTransport); hub.bindClient(client); } diff --git a/packages/core/src/transports/base.ts b/packages/core/src/transports/base.ts index 663a4c3c686f..787e175b9985 100644 --- a/packages/core/src/transports/base.ts +++ b/packages/core/src/transports/base.ts @@ -51,7 +51,6 @@ export type TransportResponse = { interface InternalBaseTransportOptions { bufferSize?: number; } - export interface BaseTransportOptions extends InternalBaseTransportOptions { // url to send the event // transport does not care about dsn specific - client should take care of @@ -59,14 +58,6 @@ export interface BaseTransportOptions extends InternalBaseTransportOptions { url: string; } -// TODO: Move into Browser Transport -export interface BrowserTransportOptions extends BaseTransportOptions { - // options to pass into fetch request - fetchParams: Record; - headers?: Record; - sendClientReports?: boolean; -} - export interface NewTransport { send(request: Envelope): PromiseLike; flush(timeout?: number): PromiseLike; diff --git a/packages/core/test/lib/base.test.ts b/packages/core/test/lib/base.test.ts index 6bb72ed99f16..72c0ef63c211 100644 --- a/packages/core/test/lib/base.test.ts +++ b/packages/core/test/lib/base.test.ts @@ -4,7 +4,7 @@ import { dsnToString, logger, SentryError, SyncPromise } from '@sentry/utils'; import * as integrationModule from '../../src/integration'; import { NoopTransport } from '../../src/transports/noop'; -import { TestClient } from '../mocks/client'; +import { setupTestTransport, TestClient } from '../mocks/client'; import { TestIntegration } from '../mocks/integration'; import { FakeTransport } from '../mocks/transport'; @@ -67,14 +67,16 @@ describe('BaseClient', () => { test('returns the Dsn', () => { expect.assertions(1); - const client = new TestClient({ dsn: PUBLIC_DSN }); + const options = { dsn: PUBLIC_DSN }; + const client = new TestClient(options, setupTestTransport(options).transport); expect(dsnToString(client.getDsn())).toBe(PUBLIC_DSN); }); test('allows missing Dsn', () => { expect.assertions(1); - const client = new TestClient({}); + const options = {}; + const client = new TestClient(options, setupTestTransport(options).transport); expect(client.getDsn()).toBeUndefined(); }); @@ -82,7 +84,8 @@ describe('BaseClient', () => { test('throws with invalid Dsn', () => { expect.assertions(1); - expect(() => new TestClient({ dsn: 'abc' })).toThrow(SentryError); + const options = { dsn: 'abc' }; + expect(() => new TestClient(options, setupTestTransport(options).transport)).toThrow(SentryError); }); }); @@ -91,7 +94,7 @@ describe('BaseClient', () => { expect.assertions(1); const options = { dsn: PUBLIC_DSN, test: true }; - const client = new TestClient(options); + const client = new TestClient(options, setupTestTransport(options).transport); expect(client.getOptions()).toEqual(options); }); @@ -102,7 +105,7 @@ describe('BaseClient', () => { expect.assertions(2); const options = { dsn: PUBLIC_DSN, transport: FakeTransport }; - const client = new TestClient(options); + const client = new TestClient(options, new FakeTransport()); expect(client.getTransport()).toBeInstanceOf(FakeTransport); expect(TestClient.instance!.getTransport()).toBe(client.getTransport()); @@ -112,7 +115,7 @@ describe('BaseClient', () => { expect.assertions(2); const options = { dsn: PUBLIC_DSN }; - const client = new TestClient(options); + const client = new TestClient(options, setupTestTransport(options).transport); expect(client.getTransport()).toBeInstanceOf(NoopTransport); expect(TestClient.instance!.getTransport()).toBe(client.getTransport()); @@ -123,7 +126,8 @@ describe('BaseClient', () => { test('adds a breadcrumb', () => { expect.assertions(1); - const client = new TestClient({}); + const options = {}; + const client = new TestClient(options, setupTestTransport(options).transport); const scope = new Scope(); const hub = new Hub(client, scope); @@ -136,7 +140,8 @@ describe('BaseClient', () => { test('adds a timestamp to new breadcrumbs', () => { expect.assertions(1); - const client = new TestClient({}); + const options = {}; + const client = new TestClient(options, setupTestTransport(options).transport); const scope = new Scope(); const hub = new Hub(client, scope); @@ -149,7 +154,8 @@ describe('BaseClient', () => { test('discards breadcrumbs beyond maxBreadcrumbs', () => { expect.assertions(2); - const client = new TestClient({ maxBreadcrumbs: 1 }); + const options = { maxBreadcrumbs: 1 }; + const client = new TestClient(options, setupTestTransport(options).transport); const scope = new Scope(); const hub = new Hub(client, scope); @@ -163,7 +169,8 @@ describe('BaseClient', () => { test('allows concurrent updates', () => { expect.assertions(1); - const client = new TestClient({}); + const options = {}; + const client = new TestClient(options, setupTestTransport(options).transport); const scope = new Scope(); const hub = new Hub(client, scope); @@ -177,7 +184,8 @@ describe('BaseClient', () => { expect.assertions(1); const beforeBreadcrumb = jest.fn(breadcrumb => breadcrumb); - const client = new TestClient({ beforeBreadcrumb }); + const options = { beforeBreadcrumb }; + const client = new TestClient(options, setupTestTransport(options).transport); const scope = new Scope(); const hub = new Hub(client, scope); @@ -190,7 +198,8 @@ describe('BaseClient', () => { expect.assertions(1); const beforeBreadcrumb = jest.fn(() => ({ message: 'changed' })); - const client = new TestClient({ beforeBreadcrumb }); + const options = { beforeBreadcrumb }; + const client = new TestClient(options, setupTestTransport(options).transport); const scope = new Scope(); const hub = new Hub(client, scope); @@ -203,7 +212,8 @@ describe('BaseClient', () => { expect.assertions(1); const beforeBreadcrumb = jest.fn(() => null); - const client = new TestClient({ beforeBreadcrumb }); + const options = { beforeBreadcrumb }; + const client = new TestClient(options, setupTestTransport(options).transport); const scope = new Scope(); const hub = new Hub(client, scope); @@ -216,7 +226,8 @@ describe('BaseClient', () => { expect.assertions(2); const beforeBreadcrumb = jest.fn((breadcrumb, hint) => ({ ...breadcrumb, data: hint.data })); - const client = new TestClient({ beforeBreadcrumb }); + const options = { beforeBreadcrumb }; + const client = new TestClient(options, setupTestTransport(options).transport); const scope = new Scope(); const hub = new Hub(client, scope); @@ -229,7 +240,8 @@ describe('BaseClient', () => { describe('captureException', () => { test('captures and sends exceptions', () => { - const client = new TestClient({ dsn: PUBLIC_DSN }); + const options = { dsn: PUBLIC_DSN }; + const client = new TestClient(options, setupTestTransport(options).transport); client.captureException(new Error('test exception')); @@ -251,7 +263,8 @@ describe('BaseClient', () => { }); test('allows for providing explicit scope', () => { - const client = new TestClient({ dsn: PUBLIC_DSN }); + const options = { dsn: PUBLIC_DSN }; + const client = new TestClient(options, setupTestTransport(options).transport); const scope = new Scope(); scope.setExtra('foo', 'wat'); @@ -278,7 +291,8 @@ describe('BaseClient', () => { }); test('allows for clearing data from existing scope if explicit one does so in a callback function', () => { - const client = new TestClient({ dsn: PUBLIC_DSN }); + const options = { dsn: PUBLIC_DSN }; + const client = new TestClient(options, setupTestTransport(options).transport); const scope = new Scope(); scope.setExtra('foo', 'wat'); @@ -312,7 +326,8 @@ describe('BaseClient', () => { // already-seen check to work . Any primitive which is passed without being wrapped will be captured each time it // is encountered, so this test doesn't apply. ])("doesn't capture the same exception twice - %s", (_name: string, thrown: any) => { - const client = new TestClient({ dsn: PUBLIC_DSN }); + const options = { dsn: PUBLIC_DSN }; + const client = new TestClient(options, setupTestTransport(options).transport); expect(thrown.__sentry_captured__).toBeUndefined(); @@ -330,7 +345,8 @@ describe('BaseClient', () => { describe('captureMessage', () => { test('captures and sends messages', () => { - const client = new TestClient({ dsn: PUBLIC_DSN }); + const options = { dsn: PUBLIC_DSN }; + const client = new TestClient(options, setupTestTransport(options).transport); client.captureMessage('test message'); @@ -346,7 +362,8 @@ describe('BaseClient', () => { }); test('should call eventFromException if input to captureMessage is not a primitive', () => { - const client = new TestClient({ dsn: PUBLIC_DSN }); + const options = { dsn: PUBLIC_DSN }; + const client = new TestClient(options, setupTestTransport(options).transport); const spy = jest.spyOn(TestClient.instance!, 'eventFromException'); client.captureMessage('foo'); @@ -364,7 +381,8 @@ describe('BaseClient', () => { }); test('allows for providing explicit scope', () => { - const client = new TestClient({ dsn: PUBLIC_DSN }); + const options = { dsn: PUBLIC_DSN }; + const client = new TestClient(options, setupTestTransport(options).transport); const scope = new Scope(); scope.setExtra('foo', 'wat'); @@ -397,7 +415,8 @@ describe('BaseClient', () => { test('skips when disabled', () => { expect.assertions(1); - const client = new TestClient({ enabled: false, dsn: PUBLIC_DSN }); + const options = { enabled: false, dsn: PUBLIC_DSN }; + const client = new TestClient(options, setupTestTransport(options).transport); const scope = new Scope(); client.captureEvent({}, undefined, scope); @@ -408,7 +427,8 @@ describe('BaseClient', () => { test('skips without a Dsn', () => { expect.assertions(1); - const client = new TestClient({}); + const options = {}; + const client = new TestClient(options, setupTestTransport(options).transport); const scope = new Scope(); client.captureEvent({}, undefined, scope); @@ -425,10 +445,11 @@ describe('BaseClient', () => { // already-seen check to work . Any primitive which is passed without being wrapped will be captured each time it // is encountered, so this test doesn't apply. ])("doesn't capture an event wrapping the same exception twice - %s", (_name: string, thrown: any) => { + const options = { dsn: PUBLIC_DSN }; // Note: this is the same test as in `describe(captureException)`, except with the exception already wrapped in a // hint and accompanying an event. Duplicated here because some methods skip `captureException` and go straight to // `captureEvent`. - const client = new TestClient({ dsn: PUBLIC_DSN }); + const client = new TestClient(options, setupTestTransport(options).transport); const event = { exception: { values: [{ type: 'Error', message: 'Will I get caught twice?' }] } }; const hint = { originalException: thrown }; @@ -448,7 +469,8 @@ describe('BaseClient', () => { test('sends an event', () => { expect.assertions(2); - const client = new TestClient({ dsn: PUBLIC_DSN }); + const options = { dsn: PUBLIC_DSN }; + const client = new TestClient(options, setupTestTransport(options).transport); const scope = new Scope(); client.captureEvent({ message: 'message' }, undefined, scope); @@ -467,7 +489,8 @@ describe('BaseClient', () => { test('does not overwrite existing timestamp', () => { expect.assertions(2); - const client = new TestClient({ dsn: PUBLIC_DSN }); + const options = { dsn: PUBLIC_DSN }; + const client = new TestClient(options, setupTestTransport(options).transport); const scope = new Scope(); client.captureEvent({ message: 'message', timestamp: 1234 }, undefined, scope); @@ -486,7 +509,8 @@ describe('BaseClient', () => { test('adds event_id from hint if available', () => { expect.assertions(1); - const client = new TestClient({ dsn: PUBLIC_DSN }); + const options = { dsn: PUBLIC_DSN }; + const client = new TestClient(options, setupTestTransport(options).transport); const scope = new Scope(); client.captureEvent({ message: 'message' }, { event_id: 'wat' }, scope); @@ -504,9 +528,10 @@ describe('BaseClient', () => { test('sets default environment to `production` if none provided', () => { expect.assertions(1); - const client = new TestClient({ + const options = { dsn: PUBLIC_DSN, - }); + }; + const client = new TestClient(options, setupTestTransport(options).transport); const scope = new Scope(); client.captureEvent({ message: 'message' }, undefined, scope); @@ -524,10 +549,11 @@ describe('BaseClient', () => { test('adds the configured environment', () => { expect.assertions(1); - const client = new TestClient({ + const options = { dsn: PUBLIC_DSN, environment: 'env', - }); + }; + const client = new TestClient(options, setupTestTransport(options).transport); const scope = new Scope(); client.captureEvent({ message: 'message' }, undefined, scope); @@ -545,10 +571,11 @@ describe('BaseClient', () => { test('allows for environment to be explicitly set to falsy value', () => { expect.assertions(1); - const client = new TestClient({ + const options = { dsn: PUBLIC_DSN, environment: undefined, - }); + }; + const client = new TestClient(options, setupTestTransport(options).transport); const scope = new Scope(); client.captureEvent({ message: 'message' }, undefined, scope); @@ -566,10 +593,11 @@ describe('BaseClient', () => { test('adds the configured release', () => { expect.assertions(1); - const client = new TestClient({ + const options = { dsn: PUBLIC_DSN, release: 'v1.0.0', - }); + }; + const client = new TestClient(options, setupTestTransport(options).transport); const scope = new Scope(); client.captureEvent({ message: 'message' }, undefined, scope); @@ -588,7 +616,8 @@ describe('BaseClient', () => { test('adds breadcrumbs', () => { expect.assertions(4); - const client = new TestClient({ dsn: PUBLIC_DSN }); + const options = { dsn: PUBLIC_DSN }; + const client = new TestClient(options, setupTestTransport(options).transport); const scope = new Scope(); scope.addBreadcrumb({ message: 'breadcrumb' }, 100); @@ -603,7 +632,8 @@ describe('BaseClient', () => { test('limits previously saved breadcrumbs', () => { expect.assertions(2); - const client = new TestClient({ dsn: PUBLIC_DSN, maxBreadcrumbs: 1 }); + const options = { dsn: PUBLIC_DSN, maxBreadcrumbs: 1 }; + const client = new TestClient(options, setupTestTransport(options).transport); const scope = new Scope(); const hub = new Hub(client, scope); hub.addBreadcrumb({ message: '1' }); @@ -618,7 +648,8 @@ describe('BaseClient', () => { test('adds context data', () => { expect.assertions(1); - const client = new TestClient({ dsn: PUBLIC_DSN }); + const options = { dsn: PUBLIC_DSN }; + const client = new TestClient(options, setupTestTransport(options).transport); const scope = new Scope(); scope.setExtra('b', 'b'); scope.setTag('a', 'a'); @@ -642,7 +673,8 @@ describe('BaseClient', () => { test('adds fingerprint', () => { expect.assertions(1); - const client = new TestClient({ dsn: PUBLIC_DSN }); + const options = { dsn: PUBLIC_DSN }; + const client = new TestClient(options, setupTestTransport(options).transport); const scope = new Scope(); scope.setFingerprint(['abcd']); @@ -660,7 +692,8 @@ describe('BaseClient', () => { }); test('adds installed integrations to sdk info', () => { - const client = new TestClient({ dsn: PUBLIC_DSN, integrations: [new TestIntegration()] }); + const options = { dsn: PUBLIC_DSN, integrations: [new TestIntegration()] }; + const client = new TestClient(options, setupTestTransport(options).transport); client.setupIntegrations(); client.captureEvent({ message: 'message' }); @@ -673,7 +706,8 @@ describe('BaseClient', () => { test('normalizes event with default depth of 3', () => { expect.assertions(1); - const client = new TestClient({ dsn: PUBLIC_DSN }); + const options = { dsn: PUBLIC_DSN }; + const client = new TestClient(options, setupTestTransport(options).transport); const fourLevelsObject = { a: { b: { @@ -724,10 +758,11 @@ describe('BaseClient', () => { test('normalization respects `normalizeDepth` option', () => { expect.assertions(1); - const client = new TestClient({ + const options = { dsn: PUBLIC_DSN, normalizeDepth: 2, - }); + }; + const client = new TestClient(options, setupTestTransport(options).transport); const fourLevelsObject = { a: { b: { @@ -775,10 +810,11 @@ describe('BaseClient', () => { test('skips normalization when `normalizeDepth: 0`', () => { expect.assertions(1); - const client = new TestClient({ + const options = { dsn: PUBLIC_DSN, normalizeDepth: 0, - }); + }; + const client = new TestClient(options, setupTestTransport(options).transport); const fourLevelsObject = { a: { b: { @@ -831,7 +867,8 @@ describe('BaseClient', () => { test('normalization applies to Transaction and Span consistently', () => { expect.assertions(1); - const client = new TestClient({ dsn: PUBLIC_DSN }); + const options = { dsn: PUBLIC_DSN }; + const client = new TestClient(options, setupTestTransport(options).transport); const transaction: Event = { contexts: { trace: { @@ -905,7 +942,8 @@ describe('BaseClient', () => { expect.assertions(1); const beforeSend = jest.fn(event => event); - const client = new TestClient({ dsn: PUBLIC_DSN, beforeSend }); + const options = { dsn: PUBLIC_DSN, beforeSend }; + const client = new TestClient(options, setupTestTransport(options).transport); client.captureEvent({ message: 'hello' }); @@ -916,7 +954,8 @@ describe('BaseClient', () => { expect.assertions(1); const beforeSend = jest.fn(() => ({ message: 'changed1' })); - const client = new TestClient({ dsn: PUBLIC_DSN, beforeSend }); + const options = { dsn: PUBLIC_DSN, beforeSend }; + const client = new TestClient(options, setupTestTransport(options).transport); client.captureEvent({ message: 'hello' }); @@ -927,7 +966,7 @@ describe('BaseClient', () => { expect.assertions(3); const beforeSend = jest.fn(() => null); - const client = new TestClient({ dsn: PUBLIC_DSN, beforeSend }); + const client = new TestClient({ dsn: PUBLIC_DSN, beforeSend }, setupTestTransport({ dsn: PUBLIC_DSN }).transport); const captureExceptionSpy = jest.spyOn(client, 'captureException'); const loggerErrorSpy = jest.spyOn(logger, 'error'); @@ -944,8 +983,9 @@ describe('BaseClient', () => { for (const val of invalidValues) { const beforeSend = jest.fn(() => val); + const options = { dsn: PUBLIC_DSN, beforeSend }; // @ts-ignore we need to test regular-js behavior - const client = new TestClient({ dsn: PUBLIC_DSN, beforeSend }); + const client = new TestClient(options, setupTestTransport({ dsn: PUBLIC_DSN }).transport); const loggerErrorSpy = jest.spyOn(logger, 'error'); client.captureEvent({ message: 'hello' }); @@ -969,7 +1009,8 @@ describe('BaseClient', () => { }, 1); }), ); - const client = new TestClient({ dsn: PUBLIC_DSN, beforeSend }); + const options = { dsn: PUBLIC_DSN, beforeSend }; + const client = new TestClient(options, setupTestTransport(options).transport); client.captureEvent({ message: 'hello' }); jest.runOnlyPendingTimers(); @@ -997,7 +1038,8 @@ describe('BaseClient', () => { }, 1); }), ); - const client = new TestClient({ dsn: PUBLIC_DSN, beforeSend }); + const options = { dsn: PUBLIC_DSN, beforeSend }; + const client = new TestClient(options, setupTestTransport(options).transport); client.captureEvent({ message: 'hello' }); jest.runOnlyPendingTimers(); @@ -1025,7 +1067,8 @@ describe('BaseClient', () => { }); }), ); - const client = new TestClient({ dsn: PUBLIC_DSN, beforeSend }); + const options = { dsn: PUBLIC_DSN, beforeSend }; + const client = new TestClient(options, setupTestTransport(options).transport); client.captureEvent({ message: 'hello' }); jest.runAllTimers(); @@ -1037,7 +1080,8 @@ describe('BaseClient', () => { expect.assertions(2); const beforeSend = jest.fn((event, hint) => ({ ...event, data: hint.data })); - const client = new TestClient({ dsn: PUBLIC_DSN, beforeSend }); + const options = { dsn: PUBLIC_DSN, beforeSend }; + const client = new TestClient(options, setupTestTransport(options).transport); client.captureEvent({ message: 'hello' }, { data: 'someRandomThing' }); @@ -1048,12 +1092,15 @@ describe('BaseClient', () => { test('beforeSend records dropped events', () => { expect.assertions(1); - const client = new TestClient({ - dsn: PUBLIC_DSN, - beforeSend() { - return null; + const client = new TestClient( + { + dsn: PUBLIC_DSN, + beforeSend() { + return null; + }, }, - }); + setupTestTransport({ dsn: PUBLIC_DSN }).transport, + ); const recordLostEventSpy = jest.fn(); jest.spyOn(client, 'getTransport').mockImplementationOnce( () => @@ -1070,7 +1117,7 @@ describe('BaseClient', () => { test('eventProcessor can drop the even when it returns null', () => { expect.assertions(3); - const client = new TestClient({ dsn: PUBLIC_DSN }); + const client = new TestClient({ dsn: PUBLIC_DSN }, setupTestTransport({ dsn: PUBLIC_DSN }).transport); const captureExceptionSpy = jest.spyOn(client, 'captureException'); const loggerErrorSpy = jest.spyOn(logger, 'error'); const scope = new Scope(); @@ -1086,7 +1133,8 @@ describe('BaseClient', () => { test('eventProcessor records dropped events', () => { expect.assertions(1); - const client = new TestClient({ dsn: PUBLIC_DSN }); + const options = { dsn: PUBLIC_DSN }; + const client = new TestClient(options, setupTestTransport(options).transport); const recordLostEventSpy = jest.fn(); jest.spyOn(client, 'getTransport').mockImplementationOnce( @@ -1107,7 +1155,8 @@ describe('BaseClient', () => { test('eventProcessor sends an event and logs when it crashes', () => { expect.assertions(3); - const client = new TestClient({ dsn: PUBLIC_DSN }); + const options = { dsn: PUBLIC_DSN }; + const client = new TestClient(options, setupTestTransport(options).transport); const captureExceptionSpy = jest.spyOn(client, 'captureException'); const loggerErrorSpy = jest.spyOn(logger, 'error'); const scope = new Scope(); @@ -1135,10 +1184,11 @@ describe('BaseClient', () => { test('records events dropped due to sampleRate', () => { expect.assertions(1); - const client = new TestClient({ + const options = { dsn: PUBLIC_DSN, sampleRate: 0, - }); + }; + const client = new TestClient(options, setupTestTransport(options).transport); const recordLostEventSpy = jest.fn(); jest.spyOn(client, 'getTransport').mockImplementationOnce( @@ -1161,10 +1211,11 @@ describe('BaseClient', () => { test('setup each one of them on setupIntegration call', () => { expect.assertions(2); - const client = new TestClient({ + const options = { dsn: PUBLIC_DSN, integrations: [new TestIntegration()], - }); + }; + const client = new TestClient(options, setupTestTransport(options).transport); client.setupIntegrations(); expect(Object.keys((client as any)._integrations).length).toBe(1); @@ -1174,9 +1225,10 @@ describe('BaseClient', () => { test('skips installation if DSN is not provided', () => { expect.assertions(2); - const client = new TestClient({ + const options = { integrations: [new TestIntegration()], - }); + }; + const client = new TestClient(options, setupTestTransport(options).transport); client.setupIntegrations(); expect(Object.keys((client as any)._integrations).length).toBe(0); @@ -1186,11 +1238,12 @@ describe('BaseClient', () => { test('skips installation if enabled is set to false', () => { expect.assertions(2); - const client = new TestClient({ + const options = { dsn: PUBLIC_DSN, enabled: false, integrations: [new TestIntegration()], - }); + }; + const client = new TestClient(options, setupTestTransport(options).transport); client.setupIntegrations(); expect(Object.keys((client as any)._integrations).length).toBe(0); @@ -1200,10 +1253,11 @@ describe('BaseClient', () => { test('skips installation if integrations are already installed', () => { expect.assertions(4); - const client = new TestClient({ + const options = { dsn: PUBLIC_DSN, integrations: [new TestIntegration()], - }); + }; + const client = new TestClient(options, setupTestTransport(options).transport); // note: not the `Client` method `setupIntegrations`, but the free-standing function which that method calls const setupIntegrationsHelper = jest.spyOn(integrationModule, 'setupIntegrations'); @@ -1226,11 +1280,14 @@ describe('BaseClient', () => { jest.useRealTimers(); expect.assertions(5); - const client = new TestClient({ - dsn: PUBLIC_DSN, - enableSend: true, - transport: FakeTransport, - }); + const client = new TestClient( + { + dsn: PUBLIC_DSN, + enableSend: true, + transport: FakeTransport, + }, + new FakeTransport(), + ); const delay = 1; const transportInstance = client.getTransport() as FakeTransport; @@ -1252,11 +1309,14 @@ describe('BaseClient', () => { jest.useRealTimers(); expect.assertions(5); - const client = new TestClient({ - dsn: PUBLIC_DSN, - enableSend: true, - transport: FakeTransport, - }); + const client = new TestClient( + { + dsn: PUBLIC_DSN, + enableSend: true, + transport: FakeTransport, + }, + new FakeTransport(), + ); const delay = 300; const spy = jest.spyOn(TestClient.instance!, 'eventFromMessage'); @@ -1288,11 +1348,14 @@ describe('BaseClient', () => { jest.useRealTimers(); expect.assertions(2); - const client = new TestClient({ - dsn: PUBLIC_DSN, - enableSend: true, - transport: FakeTransport, - }); + const client = new TestClient( + { + dsn: PUBLIC_DSN, + enableSend: true, + transport: FakeTransport, + }, + new FakeTransport(), + ); const delay = 1; const transportInstance = client.getTransport() as FakeTransport; @@ -1310,7 +1373,8 @@ describe('BaseClient', () => { jest.useRealTimers(); expect.assertions(3); - const client = new TestClient({ dsn: PUBLIC_DSN }); + const options = { dsn: PUBLIC_DSN }; + const client = new TestClient(options, setupTestTransport(options).transport); return Promise.all([ client.flush(1).then(() => { @@ -1330,7 +1394,8 @@ describe('BaseClient', () => { test('sends sessions to the client', () => { expect.assertions(1); - const client = new TestClient({ dsn: PUBLIC_DSN }); + const options = { dsn: PUBLIC_DSN }; + const client = new TestClient(options, setupTestTransport(options).transport); const session = new Session({ release: 'test' }); client.captureSession(session); @@ -1341,7 +1406,8 @@ describe('BaseClient', () => { test('skips when disabled', () => { expect.assertions(1); - const client = new TestClient({ enabled: false, dsn: PUBLIC_DSN }); + const options = { enabled: false, dsn: PUBLIC_DSN }; + const client = new TestClient(options, setupTestTransport(options).transport); const session = new Session({ release: 'test' }); client.captureSession(session); diff --git a/packages/core/test/lib/sdk.test.ts b/packages/core/test/lib/sdk.test.ts index 04f704d0698e..578361a97511 100644 --- a/packages/core/test/lib/sdk.test.ts +++ b/packages/core/test/lib/sdk.test.ts @@ -3,7 +3,7 @@ import { Client, Integration } from '@sentry/types'; import { installedIntegrations } from '../../src/integration'; import { initAndBind } from '../../src/sdk'; -import { TestClient } from '../mocks/client'; +import { setupTestTransport, TestClient, TestOptions } from '../mocks/client'; // eslint-disable-next-line no-var declare var global: any; @@ -55,7 +55,8 @@ describe('SDK', () => { new MockIntegration('MockIntegration 1'), new MockIntegration('MockIntegration 2'), ]; - initAndBind(TestClient, { dsn: PUBLIC_DSN, defaultIntegrations: DEFAULT_INTEGRATIONS }); + const options = { dsn: PUBLIC_DSN, defaultIntegrations: DEFAULT_INTEGRATIONS }; + initAndBind(TestClient, options, setupTestTransport(options).transport); expect((DEFAULT_INTEGRATIONS[0].setupOnce as jest.Mock).mock.calls.length).toBe(1); expect((DEFAULT_INTEGRATIONS[1].setupOnce as jest.Mock).mock.calls.length).toBe(1); }); @@ -65,7 +66,8 @@ describe('SDK', () => { new MockIntegration('MockIntegration 1'), new MockIntegration('MockIntegration 2'), ]; - initAndBind(TestClient, { dsn: PUBLIC_DSN, defaultIntegrations: false }); + const options: TestOptions = { dsn: PUBLIC_DSN, defaultIntegrations: false }; + initAndBind(TestClient, options, setupTestTransport(options).transport); expect((DEFAULT_INTEGRATIONS[0].setupOnce as jest.Mock).mock.calls.length).toBe(0); expect((DEFAULT_INTEGRATIONS[1].setupOnce as jest.Mock).mock.calls.length).toBe(0); }); @@ -75,7 +77,8 @@ describe('SDK', () => { new MockIntegration('MockIntegration 1'), new MockIntegration('MockIntegration 2'), ]; - initAndBind(TestClient, { dsn: PUBLIC_DSN, integrations }); + const options = { dsn: PUBLIC_DSN, integrations }; + initAndBind(TestClient, options, setupTestTransport(options).transport); expect((integrations[0].setupOnce as jest.Mock).mock.calls.length).toBe(1); expect((integrations[1].setupOnce as jest.Mock).mock.calls.length).toBe(1); }); @@ -89,7 +92,8 @@ describe('SDK', () => { new MockIntegration('MockIntegration 1'), new MockIntegration('MockIntegration 3'), ]; - initAndBind(TestClient, { dsn: PUBLIC_DSN, defaultIntegrations: DEFAULT_INTEGRATIONS, integrations }); + const options = { dsn: PUBLIC_DSN, defaultIntegrations: DEFAULT_INTEGRATIONS, integrations }; + initAndBind(TestClient, options, setupTestTransport(options).transport); // 'MockIntegration 1' should be overridden by the one with the same name provided through options expect((DEFAULT_INTEGRATIONS[0].setupOnce as jest.Mock).mock.calls.length).toBe(0); expect((DEFAULT_INTEGRATIONS[1].setupOnce as jest.Mock).mock.calls.length).toBe(1); @@ -103,12 +107,12 @@ describe('SDK', () => { new MockIntegration('MockIntegration 2'), ]; const newIntegration = new MockIntegration('MockIntegration 3'); - initAndBind(TestClient, { - // Take only the first one and add a new one to it + const options = { defaultIntegrations: DEFAULT_INTEGRATIONS, dsn: PUBLIC_DSN, integrations: (integrations: Integration[]) => integrations.slice(0, 1).concat(newIntegration), - }); + }; + initAndBind(TestClient, options, setupTestTransport(options).transport); expect((DEFAULT_INTEGRATIONS[0].setupOnce as jest.Mock).mock.calls.length).toBe(1); expect((newIntegration.setupOnce as jest.Mock).mock.calls.length).toBe(1); expect((DEFAULT_INTEGRATIONS[1].setupOnce as jest.Mock).mock.calls.length).toBe(0); diff --git a/packages/core/test/mocks/client.ts b/packages/core/test/mocks/client.ts index 36f053429e7f..2a49f9fea757 100644 --- a/packages/core/test/mocks/client.ts +++ b/packages/core/test/mocks/client.ts @@ -1,13 +1,16 @@ import { Session } from '@sentry/hub'; -import { Event, Options, Severity, SeverityLevel, Transport } from '@sentry/types'; +import { Event, Integration, Options, Severity, SeverityLevel, Transport } from '@sentry/types'; import { resolvedSyncPromise } from '@sentry/utils'; import { BaseClient } from '../../src/baseclient'; import { initAndBind } from '../../src/sdk'; +import { NewTransport } from '../../src/transports/base'; +import { NoopTransport } from '../../src/transports/noop'; export interface TestOptions extends Options { test?: boolean; mockInstallFailure?: boolean; enableSend?: boolean; + defaultIntegrations?: Integration[] | false; } export class TestClient extends BaseClient { @@ -17,8 +20,8 @@ export class TestClient extends BaseClient { public event?: Event; public session?: Session; - public constructor(options: TestOptions) { - super(options); + public constructor(options: TestOptions, transport: Transport, newTransport?: NewTransport) { + super(options, transport, newTransport); TestClient.instance = this; } @@ -59,25 +62,25 @@ export class TestClient extends BaseClient { public sendSession(session: Session): void { this.session = session; } +} - protected _setupTransport(): Transport { - if (!this._options.dsn) { - // We return the noop transport here in case there is no Dsn. - return super._setupTransport(); - } +export function init(options: TestOptions, transport: Transport, newTransport?: NewTransport): void { + initAndBind(TestClient, options, transport, newTransport); +} - const transportOptions = this._options.transportOptions - ? this._options.transportOptions - : { dsn: this._options.dsn }; +export function setupTestTransport(options: TestOptions): { transport: Transport; newTransport?: NewTransport } { + const noop = { transport: new NoopTransport() }; - if (this._options.transport) { - return new this._options.transport(transportOptions); - } + if (!options.dsn) { + // We return the noop transport here in case there is no Dsn. + return noop; + } + + const transportOptions = options.transportOptions ? options.transportOptions : { dsn: options.dsn }; - return super._setupTransport(); + if (options.transport) { + return { transport: new this._options.transport(transportOptions) }; } -} -export function init(options: TestOptions): void { - initAndBind(TestClient, options); + return noop; } diff --git a/packages/node/src/client.ts b/packages/node/src/client.ts index 6c4812b65980..e660c6c7b421 100644 --- a/packages/node/src/client.ts +++ b/packages/node/src/client.ts @@ -1,11 +1,10 @@ -import { BaseClient, getEnvelopeEndpointWithUrlEncodedAuth, initAPIDetails, Scope, SDK_VERSION } from '@sentry/core'; +import { BaseClient, NewTransport, Scope, SDK_VERSION } from '@sentry/core'; import { SessionFlusher } from '@sentry/hub'; -import { Event, EventHint, Severity, SeverityLevel, Transport, TransportOptions } from '@sentry/types'; -import { logger, makeDsn, resolvedSyncPromise, stackParserFromOptions } from '@sentry/utils'; +import { Event, EventHint, Severity, SeverityLevel, Transport } from '@sentry/types'; +import { logger, resolvedSyncPromise, stackParserFromOptions } from '@sentry/utils'; import { eventFromMessage, eventFromUnknownInput } from './eventbuilder'; import { IS_DEBUG_BUILD } from './flags'; -import { HTTPSTransport, HTTPTransport, makeNodeTransport } from './transports'; import { NodeOptions } from './types'; /** @@ -21,7 +20,7 @@ export class NodeClient extends BaseClient { * Creates a new Node SDK instance. * @param options Configuration options for this SDK. */ - public constructor(options: NodeOptions) { + public constructor(options: NodeOptions, transport: Transport, newTransport?: NewTransport) { options._metadata = options._metadata || {}; options._metadata.sdk = options._metadata.sdk || { name: 'sentry.javascript.node', @@ -34,7 +33,7 @@ export class NodeClient extends BaseClient { version: SDK_VERSION, }; - super(options); + super(options, transport, newTransport); } /** @@ -151,45 +150,4 @@ export class NodeClient extends BaseClient { this._sessionFlusher.incrementSessionStatusCount(); } } - - /** - * @inheritDoc - */ - protected _setupTransport(): Transport { - if (!this._options.dsn) { - // We return the noop transport here in case there is no Dsn. - return super._setupTransport(); - } - - const dsn = makeDsn(this._options.dsn); - - const transportOptions: TransportOptions = { - ...this._options.transportOptions, - ...(this._options.httpProxy && { httpProxy: this._options.httpProxy }), - ...(this._options.httpsProxy && { httpsProxy: this._options.httpsProxy }), - ...(this._options.caCerts && { caCerts: this._options.caCerts }), - dsn: this._options.dsn, - tunnel: this._options.tunnel, - _metadata: this._options._metadata, - }; - - if (this._options.transport) { - return new this._options.transport(transportOptions); - } - - const api = initAPIDetails(transportOptions.dsn, transportOptions._metadata, transportOptions.tunnel); - const url = getEnvelopeEndpointWithUrlEncodedAuth(api.dsn, api.tunnel); - - this._newTransport = makeNodeTransport({ - url, - headers: transportOptions.headers, - proxy: transportOptions.httpProxy, - caCerts: transportOptions.caCerts, - }); - - if (dsn.protocol === 'http') { - return new HTTPTransport(transportOptions); - } - return new HTTPSTransport(transportOptions); - } } diff --git a/packages/node/src/sdk.ts b/packages/node/src/sdk.ts index 095bd3114144..0b7fb9bd5671 100644 --- a/packages/node/src/sdk.ts +++ b/packages/node/src/sdk.ts @@ -8,6 +8,7 @@ import { NodeClient } from './client'; import { IS_DEBUG_BUILD } from './flags'; import { Console, ContextLines, Http, LinkedErrors, OnUncaughtException, OnUnhandledRejection } from './integrations'; import { nodeStackParser } from './stack-parser'; +import { setupNodeTransport } from './transports'; import { NodeOptions } from './types'; export const defaultIntegrations = [ @@ -130,7 +131,8 @@ export function init(options: NodeOptions = {}): void { setHubOnCarrier(carrier, getCurrentHub()); } - initAndBind(NodeClient, options); + const { transport, newTransport } = setupNodeTransport(options); + initAndBind(NodeClient, options, transport, newTransport); if (options.autoSessionTracking) { startSessionTracking(); diff --git a/packages/node/src/transports/index.ts b/packages/node/src/transports/index.ts index 958562933321..01877029269e 100644 --- a/packages/node/src/transports/index.ts +++ b/packages/node/src/transports/index.ts @@ -4,3 +4,4 @@ export { BaseTransport } from './base'; export { HTTPTransport } from './http'; export { HTTPSTransport } from './https'; export { makeNodeTransport } from './new'; +export { setupNodeTransport } from './setup'; diff --git a/packages/node/src/transports/setup.ts b/packages/node/src/transports/setup.ts new file mode 100644 index 000000000000..2cffd2d43ecd --- /dev/null +++ b/packages/node/src/transports/setup.ts @@ -0,0 +1,52 @@ +import { getEnvelopeEndpointWithUrlEncodedAuth, initAPIDetails, NewTransport, NoopTransport } from '@sentry/core'; +import { Transport, TransportOptions } from '@sentry/types'; +import { makeDsn } from '@sentry/utils'; + +import { NodeOptions } from '../types'; +import { HTTPSTransport, HTTPTransport, makeNodeTransport } from '.'; + +/** + * Sets up Node transport based on the passed `options`. + * + * @returns an object currently still containing both, the old `Transport` and + * `NewTransport` which will eventually replace `Transport`. Once this is replaced, + * this function will return a ready to use `NewTransport`. + */ +// TODO(v7): Adjust return value when NewTransport is the default +export function setupNodeTransport(options: NodeOptions): { transport: Transport; newTransport?: NewTransport } { + if (!options.dsn) { + // We return the noop transport here in case there is no Dsn. + return { transport: new NoopTransport() }; + } + + const dsn = makeDsn(options.dsn); + + const transportOptions: TransportOptions = { + ...options.transportOptions, + ...(options.httpProxy && { httpProxy: options.httpProxy }), + ...(options.httpsProxy && { httpsProxy: options.httpsProxy }), + ...(options.caCerts && { caCerts: options.caCerts }), + dsn: options.dsn, + tunnel: options.tunnel, + _metadata: options._metadata, + }; + + if (options.transport) { + return { transport: new options.transport(transportOptions) }; + } + + const api = initAPIDetails(transportOptions.dsn, transportOptions._metadata, transportOptions.tunnel); + const url = getEnvelopeEndpointWithUrlEncodedAuth(api.dsn, api.tunnel); + + const newTransport = makeNodeTransport({ + url, + headers: transportOptions.headers, + proxy: transportOptions.httpProxy, + caCerts: transportOptions.caCerts, + }); + + if (dsn.protocol === 'http') { + return { transport: new HTTPTransport(transportOptions), newTransport }; + } + return { transport: new HTTPSTransport(transportOptions), newTransport }; +} diff --git a/packages/node/test/client.test.ts b/packages/node/test/client.test.ts index f18f66c716af..1cddd0e85e3f 100644 --- a/packages/node/test/client.test.ts +++ b/packages/node/test/client.test.ts @@ -1,6 +1,7 @@ import { Scope, SessionFlusher } from '@sentry/hub'; import { NodeClient } from '../src'; +import { setupNodeTransport } from '../src/transports'; const PUBLIC_DSN = 'https://username@domain/123'; @@ -14,7 +15,8 @@ describe('NodeClient', () => { describe('captureException', () => { test('when autoSessionTracking is enabled, and requestHandler is not used -> requestStatus should not be set', () => { - client = new NodeClient({ dsn: PUBLIC_DSN, autoSessionTracking: true, release: '1.4' }); + const options = { dsn: PUBLIC_DSN, autoSessionTracking: true, release: '1.4' }; + client = new NodeClient(options, setupNodeTransport(options).transport); const scope = new Scope(); scope.setRequestSession({ status: 'ok' }); @@ -24,7 +26,8 @@ describe('NodeClient', () => { expect(requestSession!.status).toEqual('ok'); }); test('when autoSessionTracking is disabled -> requestStatus should not be set', () => { - client = new NodeClient({ dsn: PUBLIC_DSN, autoSessionTracking: false, release: '1.4' }); + const options = { dsn: PUBLIC_DSN, autoSessionTracking: false, release: '1.4' }; + client = new NodeClient(options, setupNodeTransport(options).transport); // It is required to initialise SessionFlusher to capture Session Aggregates (it is usually initialised // by the`requestHandler`) client.initSessionFlusher(); @@ -38,7 +41,8 @@ describe('NodeClient', () => { expect(requestSession!.status).toEqual('ok'); }); test('when autoSessionTracking is enabled + requestSession status is Crashed -> requestStatus should not be overridden', () => { - client = new NodeClient({ dsn: PUBLIC_DSN, autoSessionTracking: true, release: '1.4' }); + const options = { dsn: PUBLIC_DSN, autoSessionTracking: true, release: '1.4' }; + client = new NodeClient(options, setupNodeTransport(options).transport); // It is required to initialise SessionFlusher to capture Session Aggregates (it is usually initialised // by the`requestHandler`) client.initSessionFlusher(); @@ -52,7 +56,8 @@ describe('NodeClient', () => { expect(requestSession!.status).toEqual('crashed'); }); test('when autoSessionTracking is enabled + error occurs within request bounds -> requestStatus should be set to Errored', () => { - client = new NodeClient({ dsn: PUBLIC_DSN, autoSessionTracking: true, release: '1.4' }); + const options = { dsn: PUBLIC_DSN, autoSessionTracking: true, release: '1.4' }; + client = new NodeClient(options, setupNodeTransport(options).transport); // It is required to initialise SessionFlusher to capture Session Aggregates (it is usually initialised // by the`requestHandler`) client.initSessionFlusher(); @@ -66,7 +71,8 @@ describe('NodeClient', () => { expect(requestSession!.status).toEqual('errored'); }); test('when autoSessionTracking is enabled + error occurs outside of request bounds -> requestStatus should not be set to Errored', () => { - client = new NodeClient({ dsn: PUBLIC_DSN, autoSessionTracking: true, release: '1.4' }); + const options = { dsn: PUBLIC_DSN, autoSessionTracking: true, release: '1.4' }; + client = new NodeClient(options, setupNodeTransport(options).transport); // It is required to initialise SessionFlusher to capture Session Aggregates (it is usually initialised // by the`requestHandler`) client.initSessionFlusher(); @@ -82,7 +88,8 @@ describe('NodeClient', () => { describe('captureEvent()', () => { test('If autoSessionTracking is disabled, requestSession status should not be set', () => { - client = new NodeClient({ dsn: PUBLIC_DSN, autoSessionTracking: false, release: '1.4' }); + const options = { dsn: PUBLIC_DSN, autoSessionTracking: false, release: '1.4' }; + client = new NodeClient(options, setupNodeTransport(options).transport); // It is required to initialise SessionFlusher to capture Session Aggregates (it is usually initialised // by the`requestHandler`) client.initSessionFlusher(); @@ -100,7 +107,8 @@ describe('NodeClient', () => { }); test('When captureEvent is called with an exception, requestSession status should be set to Errored', () => { - client = new NodeClient({ dsn: PUBLIC_DSN, autoSessionTracking: true, release: '2.2' }); + const options = { dsn: PUBLIC_DSN, autoSessionTracking: true, release: '2.2' }; + client = new NodeClient(options, setupNodeTransport(options).transport); // It is required to initialise SessionFlusher to capture Session Aggregates (it is usually initialised // by the`requestHandler`) client.initSessionFlusher(); @@ -115,7 +123,8 @@ describe('NodeClient', () => { }); test('When captureEvent is called without an exception, requestSession status should not be set to Errored', () => { - client = new NodeClient({ dsn: PUBLIC_DSN, autoSessionTracking: true, release: '2.2' }); + const options = { dsn: PUBLIC_DSN, autoSessionTracking: true, release: '2.2' }; + client = new NodeClient(options, setupNodeTransport(options).transport); // It is required to initialise SessionFlusher to capture Session Aggregates (it is usually initialised // by the`requestHandler`) client.initSessionFlusher(); @@ -130,7 +139,8 @@ describe('NodeClient', () => { }); test('When captureEvent is called with an exception but outside of a request, then requestStatus should not be set', () => { - client = new NodeClient({ dsn: PUBLIC_DSN, autoSessionTracking: true, release: '2.2' }); + const options = { dsn: PUBLIC_DSN, autoSessionTracking: true, release: '2.2' }; + client = new NodeClient(options, setupNodeTransport(options).transport); // It is required to initialise SessionFlusher to capture Session Aggregates (it is usually initialised // by the`requestHandler`) client.initSessionFlusher(); @@ -147,7 +157,8 @@ describe('NodeClient', () => { }); test('When captureEvent is called with a transaction, then requestSession status should not be set', () => { - client = new NodeClient({ dsn: PUBLIC_DSN, autoSessionTracking: true, release: '1.3' }); + const options = { dsn: PUBLIC_DSN, autoSessionTracking: true, release: '1.3' }; + client = new NodeClient(options, setupNodeTransport(options).transport); // It is required to initialise SessionFlusher to capture Session Aggregates (it is usually initialised // by the`requestHandler`) client.initSessionFlusher(); @@ -161,7 +172,8 @@ describe('NodeClient', () => { }); test('When captureEvent is called with an exception but requestHandler is not used, then requestSession status should not be set', () => { - client = new NodeClient({ dsn: PUBLIC_DSN, autoSessionTracking: true, release: '1.3' }); + const options = { dsn: PUBLIC_DSN, autoSessionTracking: true, release: '1.3' }; + client = new NodeClient(options, setupNodeTransport(options).transport); const scope = new Scope(); scope.setRequestSession({ status: 'ok' }); @@ -180,11 +192,12 @@ describe('NodeClient', () => { describe('flush/close', () => { test('client close function disables _sessionFlusher', async () => { jest.useRealTimers(); - const client = new NodeClient({ + const options = { dsn: PUBLIC_DSN, autoSessionTracking: true, release: '1.1', - }); + }; + const client = new NodeClient(options, setupNodeTransport(options).transport); client.initSessionFlusher(); // Clearing interval is important here to ensure that the flush function later on is called by the `client.close()` // not due to the interval running every 60s diff --git a/packages/node/test/handlers.test.ts b/packages/node/test/handlers.test.ts index f0959de22132..d7d47fda053d 100644 --- a/packages/node/test/handlers.test.ts +++ b/packages/node/test/handlers.test.ts @@ -18,6 +18,7 @@ import { tracingHandler, } from '../src/handlers'; import * as SDK from '../src/sdk'; +import { setupNodeTransport } from '../src/transports'; describe('parseRequest', () => { let mockReq: { [key: string]: any }; @@ -223,7 +224,8 @@ describe('requestHandler', () => { }); it('autoSessionTracking is enabled, sets requestSession status to ok, when handling a request', () => { - client = new NodeClient({ autoSessionTracking: true, release: '1.2' }); + const options = { autoSessionTracking: true, release: '1.2' }; + client = new NodeClient(options, setupNodeTransport(options).transport); const hub = new Hub(client); jest.spyOn(sentryCore, 'getCurrentHub').mockReturnValue(hub); @@ -235,7 +237,8 @@ describe('requestHandler', () => { }); it('autoSessionTracking is disabled, does not set requestSession, when handling a request', () => { - client = new NodeClient({ autoSessionTracking: false, release: '1.2' }); + const options = { autoSessionTracking: false, release: '1.2' }; + client = new NodeClient(options, setupNodeTransport(options).transport); const hub = new Hub(client); jest.spyOn(sentryCore, 'getCurrentHub').mockReturnValue(hub); @@ -247,7 +250,8 @@ describe('requestHandler', () => { }); it('autoSessionTracking is enabled, calls _captureRequestSession, on response finish', done => { - client = new NodeClient({ autoSessionTracking: true, release: '1.2' }); + const options = { autoSessionTracking: true, release: '1.2' }; + client = new NodeClient(options, setupNodeTransport(options).transport); const hub = new Hub(client); jest.spyOn(sentryCore, 'getCurrentHub').mockReturnValue(hub); @@ -267,7 +271,8 @@ describe('requestHandler', () => { }); it('autoSessionTracking is disabled, does not call _captureRequestSession, on response finish', done => { - client = new NodeClient({ autoSessionTracking: false, release: '1.2' }); + const options = { autoSessionTracking: false, release: '1.2' }; + client = new NodeClient(options, setupNodeTransport(options).transport); const hub = new Hub(client); jest.spyOn(sentryCore, 'getCurrentHub').mockReturnValue(hub); @@ -367,7 +372,8 @@ describe('tracingHandler', () => { it('extracts request data for sampling context', () => { const tracesSampler = jest.fn(); - const hub = new Hub(new NodeClient({ tracesSampler })); + const options = { tracesSampler }; + const hub = new Hub(new NodeClient(options, setupNodeTransport(options).transport)); // we need to mock both of these because the tracing handler relies on `@sentry/core` while the sampler relies on // `@sentry/hub`, and mocking breaks the link between the two jest.spyOn(sentryCore, 'getCurrentHub').mockReturnValue(hub); @@ -389,7 +395,8 @@ describe('tracingHandler', () => { }); it('puts its transaction on the scope', () => { - const hub = new Hub(new NodeClient({ tracesSampleRate: 1.0 })); + const options = { tracesSampleRate: 1.0 }; + const hub = new Hub(new NodeClient(options, setupNodeTransport(options).transport)); // we need to mock both of these because the tracing handler relies on `@sentry/core` while the sampler relies on // `@sentry/hub`, and mocking breaks the link between the two jest.spyOn(sentryCore, 'getCurrentHub').mockReturnValue(hub); @@ -720,7 +727,8 @@ describe('errorHandler()', () => { jest.restoreAllMocks(); }); it('when autoSessionTracking is disabled, does not set requestSession status on Crash', () => { - client = new NodeClient({ autoSessionTracking: false, release: '3.3' }); + const options = { autoSessionTracking: false, release: '3.3' }; + client = new NodeClient(options, setupNodeTransport(options).transport); // It is required to initialise SessionFlusher to capture Session Aggregates (it is usually initialised // by the`requestHandler`) client.initSessionFlusher(); @@ -739,7 +747,8 @@ describe('errorHandler()', () => { }); it('autoSessionTracking is enabled + requestHandler is not used -> does not set requestSession status on Crash', () => { - client = new NodeClient({ autoSessionTracking: false, release: '3.3' }); + const options = { autoSessionTracking: false, release: '3.3' }; + client = new NodeClient(options, setupNodeTransport(options).transport); const scope = sentryCore.getCurrentHub().getScope(); const hub = new Hub(client); @@ -755,7 +764,8 @@ describe('errorHandler()', () => { }); it('when autoSessionTracking is enabled, should set requestSession status to Crashed when an unhandled error occurs within the bounds of a request', () => { - client = new NodeClient({ autoSessionTracking: true, release: '1.1' }); + const options = { autoSessionTracking: true, release: '1.1' }; + client = new NodeClient(options, setupNodeTransport(options).transport); // It is required to initialise SessionFlusher to capture Session Aggregates (it is usually initialised // by the`requestHandler`) client.initSessionFlusher(); @@ -773,7 +783,8 @@ describe('errorHandler()', () => { }); it('when autoSessionTracking is enabled, should not set requestSession status on Crash when it occurs outside the bounds of a request', () => { - client = new NodeClient({ autoSessionTracking: true, release: '2.2' }); + const options = { autoSessionTracking: true, release: '2.2' }; + client = new NodeClient(options, setupNodeTransport(options).transport); // It is required to initialise SessionFlusher to capture Session Aggregates (it is usually initialised // by the`requestHandler`) client.initSessionFlusher(); diff --git a/packages/node/test/index.test.ts b/packages/node/test/index.test.ts index af12a4b35994..02eaad72b2c0 100644 --- a/packages/node/test/index.test.ts +++ b/packages/node/test/index.test.ts @@ -18,6 +18,7 @@ import { } from '../src'; import { ContextLines, LinkedErrors } from '../src/integrations'; import { nodeStackParser } from '../src/stack-parser'; +import { setupNodeTransport } from '../src/transports'; const stackParser = createStackParser(nodeStackParser); @@ -89,8 +90,7 @@ describe('SentryNode', () => { }); test('record auto breadcrumbs', done => { - const client = new NodeClient({ - stackParser, + const options = { beforeSend: (event: Event) => { // TODO: It should be 3, but we don't capture a breadcrumb // for our own captureMessage/captureException calls yet @@ -99,7 +99,9 @@ describe('SentryNode', () => { return null; }, dsn, - }); + stackParser, + }; + const client = new NodeClient(options, setupNodeTransport(options).transport); getCurrentHub().bindClient(client); addBreadcrumb({ message: 'test1' }); addBreadcrumb({ message: 'test2' }); @@ -120,22 +122,21 @@ describe('SentryNode', () => { test('capture an exception', done => { expect.assertions(6); - getCurrentHub().bindClient( - new NodeClient({ - stackParser, - beforeSend: (event: Event) => { - expect(event.tags).toEqual({ test: '1' }); - expect(event.exception).not.toBeUndefined(); - expect(event.exception!.values![0]).not.toBeUndefined(); - expect(event.exception!.values![0].stacktrace!).not.toBeUndefined(); - expect(event.exception!.values![0].stacktrace!.frames![2]).not.toBeUndefined(); - expect(event.exception!.values![0].value).toEqual('test'); - done(); - return null; - }, - dsn, - }), - ); + const options = { + stackParser, + beforeSend: (event: Event) => { + expect(event.tags).toEqual({ test: '1' }); + expect(event.exception).not.toBeUndefined(); + expect(event.exception!.values![0]).not.toBeUndefined(); + expect(event.exception!.values![0].stacktrace!).not.toBeUndefined(); + expect(event.exception!.values![0].stacktrace!.frames![2]).not.toBeUndefined(); + expect(event.exception!.values![0].value).toEqual('test'); + done(); + return null; + }, + dsn, + }; + getCurrentHub().bindClient(new NodeClient(options, setupNodeTransport(options).transport)); configureScope((scope: Scope) => { scope.setTag('test', '1'); }); @@ -148,22 +149,21 @@ describe('SentryNode', () => { test('capture a string exception', done => { expect.assertions(6); - getCurrentHub().bindClient( - new NodeClient({ - stackParser, - beforeSend: (event: Event) => { - expect(event.tags).toEqual({ test: '1' }); - expect(event.exception).not.toBeUndefined(); - expect(event.exception!.values![0]).not.toBeUndefined(); - expect(event.exception!.values![0].stacktrace!).not.toBeUndefined(); - expect(event.exception!.values![0].stacktrace!.frames![2]).not.toBeUndefined(); - expect(event.exception!.values![0].value).toEqual('test string exception'); - done(); - return null; - }, - dsn, - }), - ); + const options = { + stackParser, + beforeSend: (event: Event) => { + expect(event.tags).toEqual({ test: '1' }); + expect(event.exception).not.toBeUndefined(); + expect(event.exception!.values![0]).not.toBeUndefined(); + expect(event.exception!.values![0].stacktrace!).not.toBeUndefined(); + expect(event.exception!.values![0].stacktrace!.frames![2]).not.toBeUndefined(); + expect(event.exception!.values![0].value).toEqual('test string exception'); + done(); + return null; + }, + dsn, + }; + getCurrentHub().bindClient(new NodeClient(options, setupNodeTransport(options).transport)); configureScope((scope: Scope) => { scope.setTag('test', '1'); }); @@ -176,26 +176,25 @@ describe('SentryNode', () => { test('capture an exception with pre/post context', done => { expect.assertions(10); - getCurrentHub().bindClient( - new NodeClient({ - stackParser, - beforeSend: (event: Event) => { - expect(event.tags).toEqual({ test: '1' }); - expect(event.exception).not.toBeUndefined(); - expect(event.exception!.values![0]).not.toBeUndefined(); - expect(event.exception!.values![0].stacktrace!).not.toBeUndefined(); - expect(event.exception!.values![0].stacktrace!.frames![1]).not.toBeUndefined(); - expect(event.exception!.values![0].stacktrace!.frames![1].pre_context).not.toBeUndefined(); - expect(event.exception!.values![0].stacktrace!.frames![1].post_context).not.toBeUndefined(); - expect(event.exception!.values![0].type).toBe('Error'); - expect(event.exception!.values![0].value).toBe('test'); - expect(event.exception!.values![0].stacktrace).toBeTruthy(); - done(); - return null; - }, - dsn, - }), - ); + const options = { + stackParser, + beforeSend: (event: Event) => { + expect(event.tags).toEqual({ test: '1' }); + expect(event.exception).not.toBeUndefined(); + expect(event.exception!.values![0]).not.toBeUndefined(); + expect(event.exception!.values![0].stacktrace!).not.toBeUndefined(); + expect(event.exception!.values![0].stacktrace!.frames![1]).not.toBeUndefined(); + expect(event.exception!.values![0].stacktrace!.frames![1].pre_context).not.toBeUndefined(); + expect(event.exception!.values![0].stacktrace!.frames![1].post_context).not.toBeUndefined(); + expect(event.exception!.values![0].type).toBe('Error'); + expect(event.exception!.values![0].value).toBe('test'); + expect(event.exception!.values![0].stacktrace).toBeTruthy(); + done(); + return null; + }, + dsn, + }; + getCurrentHub().bindClient(new NodeClient(options, setupNodeTransport(options).transport)); configureScope((scope: Scope) => { scope.setTag('test', '1'); }); @@ -208,33 +207,32 @@ describe('SentryNode', () => { test('capture a linked exception with pre/post context', done => { expect.assertions(15); - getCurrentHub().bindClient( - new NodeClient({ - stackParser, - integrations: [new ContextLines(), new LinkedErrors()], - beforeSend: (event: Event) => { - expect(event.exception).not.toBeUndefined(); - expect(event.exception!.values![1]).not.toBeUndefined(); - expect(event.exception!.values![1].stacktrace!).not.toBeUndefined(); - expect(event.exception!.values![1].stacktrace!.frames![1]).not.toBeUndefined(); - expect(event.exception!.values![1].stacktrace!.frames![1].pre_context).not.toBeUndefined(); - expect(event.exception!.values![1].stacktrace!.frames![1].post_context).not.toBeUndefined(); - expect(event.exception!.values![1].type).toBe('Error'); - expect(event.exception!.values![1].value).toBe('test'); - - expect(event.exception!.values![0]).not.toBeUndefined(); - expect(event.exception!.values![0].stacktrace!).not.toBeUndefined(); - expect(event.exception!.values![0].stacktrace!.frames![1]).not.toBeUndefined(); - expect(event.exception!.values![0].stacktrace!.frames![1].pre_context).not.toBeUndefined(); - expect(event.exception!.values![0].stacktrace!.frames![1].post_context).not.toBeUndefined(); - expect(event.exception!.values![0].type).toBe('Error'); - expect(event.exception!.values![0].value).toBe('cause'); - done(); - return null; - }, - dsn, - }), - ); + const options = { + stackParser, + integrations: [new ContextLines(), new LinkedErrors()], + beforeSend: (event: Event) => { + expect(event.exception).not.toBeUndefined(); + expect(event.exception!.values![1]).not.toBeUndefined(); + expect(event.exception!.values![1].stacktrace!).not.toBeUndefined(); + expect(event.exception!.values![1].stacktrace!.frames![1]).not.toBeUndefined(); + expect(event.exception!.values![1].stacktrace!.frames![1].pre_context).not.toBeUndefined(); + expect(event.exception!.values![1].stacktrace!.frames![1].post_context).not.toBeUndefined(); + expect(event.exception!.values![1].type).toBe('Error'); + expect(event.exception!.values![1].value).toBe('test'); + + expect(event.exception!.values![0]).not.toBeUndefined(); + expect(event.exception!.values![0].stacktrace!).not.toBeUndefined(); + expect(event.exception!.values![0].stacktrace!.frames![1]).not.toBeUndefined(); + expect(event.exception!.values![0].stacktrace!.frames![1].pre_context).not.toBeUndefined(); + expect(event.exception!.values![0].stacktrace!.frames![1].post_context).not.toBeUndefined(); + expect(event.exception!.values![0].type).toBe('Error'); + expect(event.exception!.values![0].value).toBe('cause'); + done(); + return null; + }, + dsn, + }; + getCurrentHub().bindClient(new NodeClient(options, setupNodeTransport(options).transport)); try { throw new Error('test'); } catch (e) { @@ -249,42 +247,40 @@ describe('SentryNode', () => { test('capture a message', done => { expect.assertions(2); - getCurrentHub().bindClient( - new NodeClient({ - stackParser, - beforeSend: (event: Event) => { - expect(event.message).toBe('test'); - expect(event.exception).toBeUndefined(); - done(); - return null; - }, - dsn, - }), - ); + const options = { + stackParser, + beforeSend: (event: Event) => { + expect(event.message).toBe('test'); + expect(event.exception).toBeUndefined(); + done(); + return null; + }, + dsn, + }; + getCurrentHub().bindClient(new NodeClient(options, setupNodeTransport(options).transport)); captureMessage('test'); }); test('capture an event', done => { expect.assertions(2); - getCurrentHub().bindClient( - new NodeClient({ - stackParser, - beforeSend: (event: Event) => { - expect(event.message).toBe('test event'); - expect(event.exception).toBeUndefined(); - done(); - return null; - }, - dsn, - }), - ); + const options = { + stackParser, + beforeSend: (event: Event) => { + expect(event.message).toBe('test event'); + expect(event.exception).toBeUndefined(); + done(); + return null; + }, + dsn, + }; + getCurrentHub().bindClient(new NodeClient(options, setupNodeTransport(options).transport)); captureEvent({ message: 'test event' }); }); test('capture an event in a domain', done => { const d = domain.create(); - const client = new NodeClient({ + const options = { stackParser, beforeSend: (event: Event) => { expect(event.message).toBe('test domain'); @@ -293,7 +289,8 @@ describe('SentryNode', () => { return null; }, dsn, - }); + }; + const client = new NodeClient(options, setupNodeTransport(options).transport); d.run(() => { getCurrentHub().bindClient(client); @@ -304,21 +301,19 @@ describe('SentryNode', () => { test('stacktrace order', done => { expect.assertions(1); - getCurrentHub().bindClient( - new NodeClient({ - stackParser, - beforeSend: (event: Event) => { - expect( - event.exception!.values![0].stacktrace!.frames![ - event.exception!.values![0].stacktrace!.frames!.length - 1 - ].function, - ).toEqual('testy'); - done(); - return null; - }, - dsn, - }), - ); + const options = { + stackParser, + beforeSend: (event: Event) => { + expect( + event.exception!.values![0].stacktrace!.frames![event.exception!.values![0].stacktrace!.frames!.length - 1] + .function, + ).toEqual('testy'); + done(); + return null; + }, + dsn, + }; + getCurrentHub().bindClient(new NodeClient(options, setupNodeTransport(options).transport)); try { // @ts-ignore allow function declarations in strict mode // eslint-disable-next-line no-inner-declarations @@ -381,7 +376,8 @@ describe('SentryNode initialization', () => { }); it('should set SDK data when instantiating a client directly', () => { - const client = new NodeClient({ dsn }); + const options = { dsn }; + const client = new NodeClient(options, setupNodeTransport(options).transport); // eslint-disable-next-line @typescript-eslint/no-explicit-any const sdkData = (client as any).getTransport()._api.metadata?.sdk; diff --git a/packages/node/test/integrations/http.test.ts b/packages/node/test/integrations/http.test.ts index bc71b994c7a5..5a05d79e3b1b 100644 --- a/packages/node/test/integrations/http.test.ts +++ b/packages/node/test/integrations/http.test.ts @@ -11,18 +11,18 @@ import * as nock from 'nock'; import { Breadcrumb } from '../../src'; import { NodeClient } from '../../src/client'; import { Http as HttpIntegration } from '../../src/integrations/http'; +import { setupNodeTransport } from '../../src/transports'; const NODE_VERSION = parseSemver(process.versions.node); describe('tracing', () => { function createTransactionOnScope() { - const hub = new Hub( - new NodeClient({ - dsn: 'https://dogsarebadatkeepingsecrets@squirrelchasers.ingest.sentry.io/12312012', - tracesSampleRate: 1.0, - integrations: [new HttpIntegration({ tracing: true })], - }), - ); + const options = { + dsn: 'https://dogsarebadatkeepingsecrets@squirrelchasers.ingest.sentry.io/12312012', + tracesSampleRate: 1.0, + integrations: [new HttpIntegration({ tracing: true })], + }; + const hub = new Hub(new NodeClient(options, setupNodeTransport(options).transport)); addExtensionMethods(); // we need to mock both of these because the tracing handler relies on `@sentry/core` while the sampler relies on @@ -97,18 +97,17 @@ describe('default protocols', () => { const p = new Promise(r => { resolve = r; }); - hub.bindClient( - new NodeClient({ - dsn: 'https://dogsarebadatkeepingsecrets@squirrelchasers.ingest.sentry.io/12312012', - integrations: [new HttpIntegration({ breadcrumbs: true })], - beforeBreadcrumb: (b: Breadcrumb) => { - if ((b.data?.url as string).includes(key)) { - resolve(b); - } - return b; - }, - }), - ); + const options = { + dsn: 'https://dogsarebadatkeepingsecrets@squirrelchasers.ingest.sentry.io/12312012', + integrations: [new HttpIntegration({ breadcrumbs: true })], + beforeBreadcrumb: (b: Breadcrumb) => { + if ((b.data?.url as string).includes(key)) { + resolve(b); + } + return b; + }, + }; + hub.bindClient(new NodeClient(options, setupNodeTransport(options).transport)); return p; } diff --git a/packages/node/test/integrations/linkederrors.test.ts b/packages/node/test/integrations/linkederrors.test.ts index 296384a5f222..2c2957bf6a69 100644 --- a/packages/node/test/integrations/linkederrors.test.ts +++ b/packages/node/test/integrations/linkederrors.test.ts @@ -4,6 +4,7 @@ import { createStackParser } from '@sentry/utils'; import { Event, NodeClient } from '../../src'; import { LinkedErrors } from '../../src/integrations/linkederrors'; import { nodeStackParser } from '../../src/stack-parser'; +import { setupNodeTransport } from '../../src/transports'; const stackParser = createStackParser(nodeStackParser); @@ -31,7 +32,8 @@ describe('LinkedErrors', () => { expect.assertions(2); const spy = jest.spyOn(linkedErrors, '_walkErrorTree'); const one = new Error('originalException'); - const client = new NodeClient({ stackParser }); + const options = { stackParser }; + const client = new NodeClient(options, setupNodeTransport(options).transport); let event: Event | undefined; return client .eventFromException(one) @@ -54,7 +56,8 @@ describe('LinkedErrors', () => { }), ); const one = new Error('originalException'); - const client = new NodeClient({ stackParser }); + const options = { stackParser }; + const client = new NodeClient(options, setupNodeTransport(options).transport); return client.eventFromException(one).then(event => linkedErrors ._handler(stackParser, event, { @@ -74,7 +77,8 @@ describe('LinkedErrors', () => { one.cause = two; two.cause = three; - const client = new NodeClient({ stackParser }); + const options = { stackParser }; + const client = new NodeClient(options, setupNodeTransport(options).transport); return client.eventFromException(one).then(event => linkedErrors ._handler(stackParser, event, { @@ -107,7 +111,8 @@ describe('LinkedErrors', () => { one.reason = two; two.reason = three; - const client = new NodeClient({ stackParser }); + const options = { stackParser }; + const client = new NodeClient(options, setupNodeTransport(options).transport); return client.eventFromException(one).then(event => linkedErrors ._handler(stackParser, event, { @@ -140,7 +145,8 @@ describe('LinkedErrors', () => { one.cause = two; two.cause = three; - const client = new NodeClient({ stackParser }); + const options = { stackParser }; + const client = new NodeClient(options, setupNodeTransport(options).transport); return client.eventFromException(one).then(event => linkedErrors ._handler(stackParser, event, { diff --git a/packages/node/test/transports/setup.test.ts b/packages/node/test/transports/setup.test.ts new file mode 100644 index 000000000000..6864ac89ea1e --- /dev/null +++ b/packages/node/test/transports/setup.test.ts @@ -0,0 +1,61 @@ +import { NoopTransport } from '@sentry/core'; +import { FakeTransport } from '@sentry/core/test/mocks/transport'; +import { HTTPSTransport, HTTPTransport, setupNodeTransport } from '@sentry/node/src/transports'; + +import { makeNodeTransport } from '../../src/transports/new'; + +jest.mock('../../src/transports/new', () => { + const original = jest.requireActual('../../src/transports/new'); + return { + ...original, + makeNodeTransport: jest.fn(() => ({ + send: () => Promise.resolve({ status: 'success' }), + flush: () => Promise.resolve(true), + })), + }; +}); + +const DSN = 'https://username@domain/123'; + +describe('setupNodeTransport', () => { + afterEach(() => jest.clearAllMocks()); + + afterAll(() => jest.resetAllMocks()); + + it('returns NoopTransport if no dsn is passed', () => { + const { transport, newTransport } = setupNodeTransport({}); + + expect(transport).toBeDefined(); + expect(transport).toBeInstanceOf(NoopTransport); + expect(newTransport).toBeUndefined(); + }); + + it('returns the instantiated transport passed via the options', () => { + const options = { dsn: DSN, transport: FakeTransport }; + const { transport, newTransport } = setupNodeTransport(options); + + expect(transport).toBeDefined(); + expect(transport).toBeInstanceOf(FakeTransport); + expect(newTransport).toBeUndefined(); + }); + + it('returns HTTPS transport as a default', () => { + const options = { dsn: DSN }; + const { transport, newTransport } = setupNodeTransport(options); + + expect(transport).toBeDefined(); + expect(transport).toBeInstanceOf(HTTPSTransport); + expect(newTransport).toBeDefined(); + expect(makeNodeTransport).toHaveBeenCalledTimes(1); + }); + + it('returns HTTP transport if specified in the dsn', () => { + const options = { dsn: 'http://username@domain/123' }; + const { transport, newTransport } = setupNodeTransport(options); + + expect(transport).toBeDefined(); + expect(transport).toBeInstanceOf(HTTPTransport); + expect(newTransport).toBeDefined(); + expect(makeNodeTransport).toHaveBeenCalledTimes(1); + }); +}); diff --git a/packages/tracing/test/browser/backgroundtab.test.ts b/packages/tracing/test/browser/backgroundtab.test.ts index e46c79695d20..440eb785a609 100644 --- a/packages/tracing/test/browser/backgroundtab.test.ts +++ b/packages/tracing/test/browser/backgroundtab.test.ts @@ -1,4 +1,5 @@ import { BrowserClient } from '@sentry/browser'; +import { setupBrowserTransport } from '@sentry/browser/src/transports'; import { Hub, makeMain } from '@sentry/hub'; import { JSDOM } from 'jsdom'; @@ -13,7 +14,8 @@ describe('registerBackgroundTabDetection', () => { // @ts-ignore need to override global document global.document = dom.window.document; - hub = new Hub(new BrowserClient({ tracesSampleRate: 1 })); + const options = { tracesSampleRate: 1 }; + hub = new Hub(new BrowserClient(options, setupBrowserTransport(options).transport)); makeMain(hub); // If we do not add extension methods, invoking hub.startTransaction returns undefined diff --git a/packages/tracing/test/browser/browsertracing.test.ts b/packages/tracing/test/browser/browsertracing.test.ts index 8db3cc10e41e..510befc29064 100644 --- a/packages/tracing/test/browser/browsertracing.test.ts +++ b/packages/tracing/test/browser/browsertracing.test.ts @@ -1,4 +1,5 @@ import { BrowserClient } from '@sentry/browser'; +import { setupBrowserTransport } from '@sentry/browser/src/transports'; import { Hub, makeMain } from '@sentry/hub'; import { getGlobalObject } from '@sentry/utils'; import { JSDOM } from 'jsdom'; @@ -51,7 +52,8 @@ describe('BrowserTracing', () => { let hub: Hub; beforeEach(() => { jest.useFakeTimers(); - hub = new Hub(new BrowserClient({ tracesSampleRate: 1 })); + const options = { tracesSampleRate: 1 }; + hub = new Hub(new BrowserClient(options, setupBrowserTransport(options).transport)); makeMain(hub); document.head.innerHTML = ''; @@ -472,7 +474,8 @@ describe('BrowserTracing', () => { getGlobalObject().location = dogParkLocation as any; const tracesSampler = jest.fn(); - hub.bindClient(new BrowserClient({ tracesSampler })); + const options = { tracesSampler }; + hub.bindClient(new BrowserClient(options, setupBrowserTransport(options).transport)); // setting up the BrowserTracing integration automatically starts a pageload transaction createBrowserTracing(true); @@ -488,7 +491,8 @@ describe('BrowserTracing', () => { getGlobalObject().location = dogParkLocation as any; const tracesSampler = jest.fn(); - hub.bindClient(new BrowserClient({ tracesSampler })); + const options = { tracesSampler }; + hub.bindClient(new BrowserClient(options, setupBrowserTransport(options).transport)); // setting up the BrowserTracing integration normally automatically starts a pageload transaction, but that's not // what we're testing here createBrowserTracing(true, { startTransactionOnPageLoad: false }); diff --git a/packages/tracing/test/browser/request.test.ts b/packages/tracing/test/browser/request.test.ts index 2ba1c906818a..a31aa10d5c8a 100644 --- a/packages/tracing/test/browser/request.test.ts +++ b/packages/tracing/test/browser/request.test.ts @@ -1,4 +1,5 @@ import { BrowserClient } from '@sentry/browser'; +import { setupBrowserTransport } from '@sentry/browser/src/transports'; import { Hub, makeMain } from '@sentry/hub'; import * as utils from '@sentry/utils'; @@ -72,7 +73,8 @@ describe('callbacks', () => { }; beforeAll(() => { - hub = new Hub(new BrowserClient({ tracesSampleRate: 1 })); + const options = { tracesSampleRate: 1 }; + hub = new Hub(new BrowserClient(options, setupBrowserTransport(options).transport)); makeMain(hub); }); diff --git a/packages/tracing/test/errors.test.ts b/packages/tracing/test/errors.test.ts index 43583b352ef1..ca042672b595 100644 --- a/packages/tracing/test/errors.test.ts +++ b/packages/tracing/test/errors.test.ts @@ -1,4 +1,5 @@ import { BrowserClient } from '@sentry/browser'; +import { setupBrowserTransport } from '@sentry/browser/src/transports'; import { Hub, makeMain } from '@sentry/hub'; import { registerErrorInstrumentation } from '../src/errors'; @@ -33,7 +34,8 @@ describe('registerErrorHandlers()', () => { let hub: Hub; beforeEach(() => { mockAddInstrumentationHandler.mockClear(); - hub = new Hub(new BrowserClient({ tracesSampleRate: 1 })); + const options = { tracesSampleRate: 1 }; + hub = new Hub(new BrowserClient(options, setupBrowserTransport(options).transport)); makeMain(hub); }); diff --git a/packages/tracing/test/hub.test.ts b/packages/tracing/test/hub.test.ts index 5e4c8a806cac..045d9fa96fb1 100644 --- a/packages/tracing/test/hub.test.ts +++ b/packages/tracing/test/hub.test.ts @@ -1,5 +1,6 @@ /* eslint-disable @typescript-eslint/unbound-method */ import { BrowserClient } from '@sentry/browser'; +import { setupBrowserTransport } from '@sentry/browser/src/transports'; import { Hub, makeMain } from '@sentry/hub'; import * as utilsModule from '@sentry/utils'; // for mocking import { logger } from '@sentry/utils'; @@ -32,7 +33,8 @@ describe('Hub', () => { describe('getTransaction()', () => { it('should find a transaction which has been set on the scope if sampled = true', () => { - const hub = new Hub(new BrowserClient({ tracesSampleRate: 1 })); + const options = { tracesSampleRate: 1 }; + const hub = new Hub(new BrowserClient(options, setupBrowserTransport(options).transport)); makeMain(hub); const transaction = hub.startTransaction({ name: 'dogpark' }); transaction.sampled = true; @@ -45,7 +47,8 @@ describe('Hub', () => { }); it('should find a transaction which has been set on the scope if sampled = false', () => { - const hub = new Hub(new BrowserClient({ tracesSampleRate: 1 })); + const options = { tracesSampleRate: 1 }; + const hub = new Hub(new BrowserClient(options, setupBrowserTransport(options).transport)); makeMain(hub); const transaction = hub.startTransaction({ name: 'dogpark', sampled: false }); @@ -57,7 +60,8 @@ describe('Hub', () => { }); it("should not find an open transaction if it's not on the scope", () => { - const hub = new Hub(new BrowserClient({ tracesSampleRate: 1 })); + const options = { tracesSampleRate: 1 }; + const hub = new Hub(new BrowserClient(options, setupBrowserTransport(options).transport)); makeMain(hub); hub.startTransaction({ name: 'dogpark' }); @@ -69,7 +73,8 @@ describe('Hub', () => { describe('default sample context', () => { it('should add transaction context data to default sample context', () => { const tracesSampler = jest.fn(); - const hub = new Hub(new BrowserClient({ tracesSampler })); + const options = { tracesSampler }; + const hub = new Hub(new BrowserClient(options, setupBrowserTransport(options).transport)); makeMain(hub); const transactionContext = { @@ -85,7 +90,8 @@ describe('Hub', () => { it("should add parent's sampling decision to default sample context", () => { const tracesSampler = jest.fn(); - const hub = new Hub(new BrowserClient({ tracesSampler })); + const options = { tracesSampler }; + const hub = new Hub(new BrowserClient(options, setupBrowserTransport(options).transport)); makeMain(hub); const parentSamplingDecsion = false; @@ -103,8 +109,9 @@ describe('Hub', () => { describe('sample()', () => { it('should set sampled = false when tracing is disabled', () => { + const options = {}; // neither tracesSampleRate nor tracesSampler is defined -> tracing disabled - const hub = new Hub(new BrowserClient({})); + const hub = new Hub(new BrowserClient(options, setupBrowserTransport(options).transport)); makeMain(hub); const transaction = hub.startTransaction({ name: 'dogpark' }); @@ -112,7 +119,8 @@ describe('Hub', () => { }); it('should set sampled = false if tracesSampleRate is 0', () => { - const hub = new Hub(new BrowserClient({ tracesSampleRate: 0 })); + const options = { tracesSampleRate: 0 }; + const hub = new Hub(new BrowserClient(options, setupBrowserTransport(options).transport)); makeMain(hub); const transaction = hub.startTransaction({ name: 'dogpark' }); @@ -120,7 +128,8 @@ describe('Hub', () => { }); it('should set sampled = true if tracesSampleRate is 1', () => { - const hub = new Hub(new BrowserClient({ tracesSampleRate: 1 })); + const options = { tracesSampleRate: 1 }; + const hub = new Hub(new BrowserClient(options, setupBrowserTransport(options).transport)); makeMain(hub); const transaction = hub.startTransaction({ name: 'dogpark' }); @@ -128,7 +137,8 @@ describe('Hub', () => { }); it('should set sampled = true if tracesSampleRate is 1 (without global hub)', () => { - const hub = new Hub(new BrowserClient({ tracesSampleRate: 1 })); + const options = { tracesSampleRate: 1 }; + const hub = new Hub(new BrowserClient(options, setupBrowserTransport(options).transport)); const transaction = hub.startTransaction({ name: 'dogpark' }); expect(transaction.sampled).toBe(true); @@ -136,7 +146,8 @@ describe('Hub', () => { it("should call tracesSampler if it's defined", () => { const tracesSampler = jest.fn(); - const hub = new Hub(new BrowserClient({ tracesSampler })); + const options = { tracesSampler }; + const hub = new Hub(new BrowserClient(options, setupBrowserTransport(options).transport)); makeMain(hub); hub.startTransaction({ name: 'dogpark' }); @@ -145,7 +156,8 @@ describe('Hub', () => { it('should set sampled = false if tracesSampler returns 0', () => { const tracesSampler = jest.fn().mockReturnValue(0); - const hub = new Hub(new BrowserClient({ tracesSampler })); + const options = { tracesSampler }; + const hub = new Hub(new BrowserClient(options, setupBrowserTransport(options).transport)); makeMain(hub); const transaction = hub.startTransaction({ name: 'dogpark' }); @@ -155,7 +167,8 @@ describe('Hub', () => { it('should set sampled = true if tracesSampler returns 1', () => { const tracesSampler = jest.fn().mockReturnValue(1); - const hub = new Hub(new BrowserClient({ tracesSampler })); + const options = { tracesSampler }; + const hub = new Hub(new BrowserClient(options, setupBrowserTransport(options).transport)); makeMain(hub); const transaction = hub.startTransaction({ name: 'dogpark' }); @@ -165,7 +178,8 @@ describe('Hub', () => { it('should set sampled = true if tracesSampler returns 1 (without global hub)', () => { const tracesSampler = jest.fn().mockReturnValue(1); - const hub = new Hub(new BrowserClient({ tracesSampler })); + const options = { tracesSampler }; + const hub = new Hub(new BrowserClient(options, setupBrowserTransport(options).transport)); const transaction = hub.startTransaction({ name: 'dogpark' }); expect(tracesSampler).toHaveBeenCalled(); @@ -175,7 +189,8 @@ describe('Hub', () => { it('should not try to override explicitly set positive sampling decision', () => { // so that the decision otherwise would be false const tracesSampler = jest.fn().mockReturnValue(0); - const hub = new Hub(new BrowserClient({ tracesSampler })); + const options = { tracesSampler }; + const hub = new Hub(new BrowserClient(options, setupBrowserTransport(options).transport)); makeMain(hub); const transaction = hub.startTransaction({ name: 'dogpark', sampled: true }); @@ -185,7 +200,8 @@ describe('Hub', () => { it('should not try to override explicitly set negative sampling decision', () => { // so that the decision otherwise would be true const tracesSampler = jest.fn().mockReturnValue(1); - const hub = new Hub(new BrowserClient({ tracesSampler })); + const options = { tracesSampler }; + const hub = new Hub(new BrowserClient(options, setupBrowserTransport(options).transport)); makeMain(hub); const transaction = hub.startTransaction({ name: 'dogpark', sampled: false }); @@ -195,7 +211,8 @@ describe('Hub', () => { it('should prefer tracesSampler to tracesSampleRate', () => { // make the two options do opposite things to prove precedence const tracesSampler = jest.fn().mockReturnValue(true); - const hub = new Hub(new BrowserClient({ tracesSampleRate: 0, tracesSampler })); + const options = { tracesSampleRate: 0, tracesSampler }; + const hub = new Hub(new BrowserClient(options, setupBrowserTransport(options).transport)); makeMain(hub); const transaction = hub.startTransaction({ name: 'dogpark' }); @@ -205,7 +222,8 @@ describe('Hub', () => { it('should tolerate tracesSampler returning a boolean', () => { const tracesSampler = jest.fn().mockReturnValue(true); - const hub = new Hub(new BrowserClient({ tracesSampler })); + const options = { tracesSampler }; + const hub = new Hub(new BrowserClient(options, setupBrowserTransport(options).transport)); makeMain(hub); const transaction = hub.startTransaction({ name: 'dogpark' }); @@ -215,7 +233,8 @@ describe('Hub', () => { it('should record sampling method when sampling decision is explicitly set', () => { const tracesSampler = jest.fn().mockReturnValue(0.1121); - const hub = new Hub(new BrowserClient({ tracesSampler })); + const options = { tracesSampler }; + const hub = new Hub(new BrowserClient(options, setupBrowserTransport(options).transport)); makeMain(hub); hub.startTransaction({ name: 'dogpark', sampled: true }); @@ -226,7 +245,8 @@ describe('Hub', () => { it('should record sampling method and rate when sampling decision comes from tracesSampler', () => { const tracesSampler = jest.fn().mockReturnValue(0.1121); - const hub = new Hub(new BrowserClient({ tracesSampler })); + const options = { tracesSampler }; + const hub = new Hub(new BrowserClient(options, setupBrowserTransport(options).transport)); makeMain(hub); hub.startTransaction({ name: 'dogpark' }); @@ -236,7 +256,8 @@ describe('Hub', () => { }); it('should record sampling method when sampling decision is inherited', () => { - const hub = new Hub(new BrowserClient({ tracesSampleRate: 0.1121 })); + const options = { tracesSampleRate: 0.1121 }; + const hub = new Hub(new BrowserClient(options, setupBrowserTransport(options).transport)); makeMain(hub); hub.startTransaction({ name: 'dogpark', parentSampled: true }); @@ -246,7 +267,8 @@ describe('Hub', () => { }); it('should record sampling method and rate when sampling decision comes from traceSampleRate', () => { - const hub = new Hub(new BrowserClient({ tracesSampleRate: 0.1121 })); + const options = { tracesSampleRate: 0.1121 }; + const hub = new Hub(new BrowserClient(options, setupBrowserTransport(options).transport)); makeMain(hub); hub.startTransaction({ name: 'dogpark' }); @@ -258,7 +280,8 @@ describe('Hub', () => { describe('isValidSampleRate()', () => { it("should reject tracesSampleRates which aren't numbers or booleans", () => { - const hub = new Hub(new BrowserClient({ tracesSampleRate: 'dogs!' as any })); + const options = { tracesSampleRate: 'dogs!' as any }; + const hub = new Hub(new BrowserClient(options, setupBrowserTransport(options).transport)); makeMain(hub); hub.startTransaction({ name: 'dogpark' }); @@ -266,7 +289,8 @@ describe('Hub', () => { }); it('should reject tracesSampleRates which are NaN', () => { - const hub = new Hub(new BrowserClient({ tracesSampleRate: 'dogs!' as any })); + const options = { tracesSampleRate: 'dogs!' as any }; + const hub = new Hub(new BrowserClient(options, setupBrowserTransport(options).transport)); makeMain(hub); hub.startTransaction({ name: 'dogpark' }); @@ -275,7 +299,8 @@ describe('Hub', () => { // the rate might be a boolean, but for our purposes, false is equivalent to 0 and true is equivalent to 1 it('should reject tracesSampleRates less than 0', () => { - const hub = new Hub(new BrowserClient({ tracesSampleRate: -26 })); + const options = { tracesSampleRate: -26 }; + const hub = new Hub(new BrowserClient(options, setupBrowserTransport(options).transport)); makeMain(hub); hub.startTransaction({ name: 'dogpark' }); @@ -284,7 +309,8 @@ describe('Hub', () => { // the rate might be a boolean, but for our purposes, false is equivalent to 0 and true is equivalent to 1 it('should reject tracesSampleRates greater than 1', () => { - const hub = new Hub(new BrowserClient({ tracesSampleRate: 26 })); + const options = { tracesSampleRate: 26 }; + const hub = new Hub(new BrowserClient(options, setupBrowserTransport(options).transport)); makeMain(hub); hub.startTransaction({ name: 'dogpark' }); @@ -293,7 +319,8 @@ describe('Hub', () => { it("should reject tracesSampler return values which aren't numbers or booleans", () => { const tracesSampler = jest.fn().mockReturnValue('dogs!'); - const hub = new Hub(new BrowserClient({ tracesSampler })); + const options = { tracesSampler }; + const hub = new Hub(new BrowserClient(options, setupBrowserTransport(options).transport)); makeMain(hub); hub.startTransaction({ name: 'dogpark' }); @@ -302,7 +329,8 @@ describe('Hub', () => { it('should reject tracesSampler return values which are NaN', () => { const tracesSampler = jest.fn().mockReturnValue(NaN); - const hub = new Hub(new BrowserClient({ tracesSampler })); + const options = { tracesSampler }; + const hub = new Hub(new BrowserClient(options, setupBrowserTransport(options).transport)); makeMain(hub); hub.startTransaction({ name: 'dogpark' }); @@ -312,7 +340,8 @@ describe('Hub', () => { // the rate might be a boolean, but for our purposes, false is equivalent to 0 and true is equivalent to 1 it('should reject tracesSampler return values less than 0', () => { const tracesSampler = jest.fn().mockReturnValue(-12); - const hub = new Hub(new BrowserClient({ tracesSampler })); + const options = { tracesSampler }; + const hub = new Hub(new BrowserClient(options, setupBrowserTransport(options).transport)); makeMain(hub); hub.startTransaction({ name: 'dogpark' }); @@ -322,7 +351,8 @@ describe('Hub', () => { // the rate might be a boolean, but for our purposes, false is equivalent to 0 and true is equivalent to 1 it('should reject tracesSampler return values greater than 1', () => { const tracesSampler = jest.fn().mockReturnValue(31); - const hub = new Hub(new BrowserClient({ tracesSampler })); + const options = { tracesSampler }; + const hub = new Hub(new BrowserClient(options, setupBrowserTransport(options).transport)); makeMain(hub); hub.startTransaction({ name: 'dogpark' }); @@ -331,7 +361,8 @@ describe('Hub', () => { }); it('should drop transactions with sampled = false', () => { - const client = new BrowserClient({ tracesSampleRate: 0 }); + const options = { tracesSampleRate: 0 }; + const client = new BrowserClient(options, setupBrowserTransport(options).transport); jest.spyOn(client, 'captureEvent'); const hub = new Hub(client); @@ -348,7 +379,8 @@ describe('Hub', () => { describe('sampling inheritance', () => { it('should propagate sampling decision to child spans', () => { - const hub = new Hub(new BrowserClient({ tracesSampleRate: Math.random() })); + const options = { tracesSampleRate: Math.random() }; + const hub = new Hub(new BrowserClient(options, setupBrowserTransport(options).transport)); makeMain(hub); const transaction = hub.startTransaction({ name: 'dogpark' }); const child = transaction.startChild({ op: 'ball.chase' }); @@ -360,13 +392,12 @@ describe('Hub', () => { testOnlyIfNodeVersionAtLeast(10)( 'should propagate positive sampling decision to child transactions in XHR header', async () => { - const hub = new Hub( - new BrowserClient({ - dsn: 'https://1231@dogs.are.great/1121', - tracesSampleRate: 1, - integrations: [new BrowserTracing()], - }), - ); + const options = { + dsn: 'https://1231@dogs.are.great/1121', + tracesSampleRate: 1, + integrations: [new BrowserTracing()], + }; + const hub = new Hub(new BrowserClient(options, setupBrowserTransport(options).transport)); makeMain(hub); const transaction = hub.startTransaction({ name: 'dogpark' }); @@ -402,13 +433,12 @@ describe('Hub', () => { testOnlyIfNodeVersionAtLeast(10)( 'should propagate negative sampling decision to child transactions in XHR header', async () => { - const hub = new Hub( - new BrowserClient({ - dsn: 'https://1231@dogs.are.great/1121', - tracesSampleRate: 1, - integrations: [new BrowserTracing()], - }), - ); + const options = { + dsn: 'https://1231@dogs.are.great/1121', + tracesSampleRate: 1, + integrations: [new BrowserTracing()], + }; + const hub = new Hub(new BrowserClient(options, setupBrowserTransport(options).transport)); makeMain(hub); const transaction = hub.startTransaction({ name: 'dogpark', sampled: false }); @@ -453,7 +483,8 @@ describe('Hub', () => { // sample rate), so make parent's decision the opposite to prove that inheritance takes precedence over // tracesSampleRate mathRandom.mockReturnValueOnce(1); - const hub = new Hub(new BrowserClient({ tracesSampleRate: 0.5 })); + const options = { tracesSampleRate: 0.5 }; + const hub = new Hub(new BrowserClient(options, setupBrowserTransport(options).transport)); makeMain(hub); const parentSamplingDecsion = true; @@ -467,9 +498,10 @@ describe('Hub', () => { }); it("should inherit parent's negative sampling decision if tracesSampler is undefined", () => { + const options = { tracesSampleRate: 1 }; // tracesSampleRate = 1 means every transaction should end up with sampled = true, so make parent's decision the // opposite to prove that inheritance takes precedence over tracesSampleRate - const hub = new Hub(new BrowserClient({ tracesSampleRate: 1 })); + const hub = new Hub(new BrowserClient(options, setupBrowserTransport(options).transport)); makeMain(hub); const parentSamplingDecsion = false; @@ -488,7 +520,8 @@ describe('Hub', () => { const tracesSampler = () => true; const parentSamplingDecsion = false; - const hub = new Hub(new BrowserClient({ tracesSampler })); + const options = { tracesSampler }; + const hub = new Hub(new BrowserClient(options, setupBrowserTransport(options).transport)); makeMain(hub); const transaction = hub.startTransaction({ @@ -506,7 +539,8 @@ describe('Hub', () => { const tracesSampler = () => false; const parentSamplingDecsion = true; - const hub = new Hub(new BrowserClient({ tracesSampler })); + const options = { tracesSampler }; + const hub = new Hub(new BrowserClient(options, setupBrowserTransport(options).transport)); makeMain(hub); const transaction = hub.startTransaction({ diff --git a/packages/tracing/test/idletransaction.test.ts b/packages/tracing/test/idletransaction.test.ts index b60fec726c54..f2565e1c7e5f 100644 --- a/packages/tracing/test/idletransaction.test.ts +++ b/packages/tracing/test/idletransaction.test.ts @@ -14,7 +14,8 @@ export class SimpleTransport extends Transports.BaseTransport {} const dsn = 'https://123@sentry.io/42'; let hub: Hub; beforeEach(() => { - hub = new Hub(new BrowserClient({ dsn, tracesSampleRate: 1, transport: SimpleTransport })); + const options = { dsn, tracesSampleRate: 1, transport: SimpleTransport }; + hub = new Hub(new BrowserClient(options, new SimpleTransport(options))); }); describe('IdleTransaction', () => { diff --git a/packages/tracing/test/span.test.ts b/packages/tracing/test/span.test.ts index 4b0887f2dda7..fe11fbf143ee 100644 --- a/packages/tracing/test/span.test.ts +++ b/packages/tracing/test/span.test.ts @@ -1,4 +1,5 @@ import { BrowserClient } from '@sentry/browser'; +import { setupBrowserTransport } from '@sentry/browser/src/transports'; import { Hub, makeMain, Scope } from '@sentry/hub'; import { Span, Transaction } from '../src'; @@ -9,7 +10,8 @@ describe('Span', () => { beforeEach(() => { const myScope = new Scope(); - hub = new Hub(new BrowserClient({ tracesSampleRate: 1 }), myScope); + const options = { tracesSampleRate: 1 }; + hub = new Hub(new BrowserClient(options, setupBrowserTransport(options).transport), myScope); makeMain(hub); }); @@ -216,12 +218,11 @@ describe('Span', () => { }); test('maxSpans correctly limits number of spans', () => { - const _hub = new Hub( - new BrowserClient({ - _experiments: { maxSpans: 3 }, - tracesSampleRate: 1, - }), - ); + const options = { + _experiments: { maxSpans: 3 }, + tracesSampleRate: 1, + }; + const _hub = new Hub(new BrowserClient(options, setupBrowserTransport(options).transport)); const spy = jest.spyOn(_hub as any, 'captureEvent') as any; const transaction = _hub.startTransaction({ name: 'test' }); for (let i = 0; i < 10; i++) { @@ -233,11 +234,10 @@ describe('Span', () => { }); test('no span recorder created if transaction.sampled is false', () => { - const _hub = new Hub( - new BrowserClient({ - tracesSampleRate: 1, - }), - ); + const options = { + tracesSampleRate: 1, + }; + const _hub = new Hub(new BrowserClient(options, setupBrowserTransport(options).transport)); const spy = jest.spyOn(_hub as any, 'captureEvent') as any; const transaction = _hub.startTransaction({ name: 'test', sampled: false }); for (let i = 0; i < 10; i++) { From b7c24946335d5c95fd51c0e5f06ec73b25a548d8 Mon Sep 17 00:00:00 2001 From: Lukas Stracke Date: Thu, 14 Apr 2022 13:54:20 +0200 Subject: [PATCH 044/204] feat(docs): Add `Backend` removal and `Transport` injection to migration docs (#4934) Add two small bullet points to the "General API changes" section, briefly explaining the removal of `Backend` and the injection of `Transport`s. The transport injection part needs to be revisited once we switch over to just using `NewTransport` as well as when we're passing the transports as `options` properties to the client constructors. --- MIGRATION.md | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/MIGRATION.md b/MIGRATION.md index 4e5de472634d..f6017755e902 100644 --- a/MIGRATION.md +++ b/MIGRATION.md @@ -91,6 +91,19 @@ For our efforts to reduce bundle size of the SDK we had to remove and refactor p - Remove deprecated `Status`, `SessionStatus`, and `RequestSessionStatus` enums. These were only part of an internal API. If you are using these enums, we encourage you to to look at [b177690d](https://github.com/getsentry/sentry-javascript/commit/b177690d89640aef2587039113c614672c07d2be), [5fc3147d](https://github.com/getsentry/sentry-javascript/commit/5fc3147dfaaf1a856d5923e4ba409479e87273be), and [f99bdd16](https://github.com/getsentry/sentry-javascript/commit/f99bdd16539bf6fac14eccf1a974a4988d586b28) to to see the changes we've made to our code as result. We generally recommend using string literals instead of the removed enums. - Remove deprecated `getActiveDomain` method and `DomainAsCarrier` type from `@sentry/hub`. - Rename `registerRequestInstrumentation` to `instrumentOutgoingRequests` in `@sentry/tracing`. +- Remove `Backend` and port its functionality into `Client` (see + [#4911](https://github.com/getsentry/sentry-javascript/pull/4911) and + [#4919](https://github.com/getsentry/sentry-javascript/pull/4919)). `Backend` was an unnecessary abstraction which is + not present in other Sentry SDKs. For the sake of reducing complexity, increasing consistency with other Sentry SDKs and + decreasing bundle-size, `Backend` was removed. + + +- Inject transport into client instead of initializing it in the client in `setupTransport` (see + [#4921](https://github.com/getsentry/sentry-javascript/pull/4921/)). If you are creating your own `Client` or + calling `initAndBind`, you will have to supply your desired transport. Either provide a custom one or call + `setupBrowserTransport` or `setupNodeTransport` for default transports, depending on your requirements. + + # Upgrading from 6.17.x to 6.18.0 From b15912a57e5bef7059f9db89bac0a1f585ee424d Mon Sep 17 00:00:00 2001 From: Lukas Stracke Date: Thu, 14 Apr 2022 14:29:43 +0200 Subject: [PATCH 045/204] feat(docs): Add enum removal and deprecation section to migration docs (#4938) list the removed enums in v7 and the ones we chose to keep deprecated until the next major release. Additionally, provide links to examples how to migrate to string literals for the public enums. Co-authored-by: Luca Forstner --- MIGRATION.md | 35 +++++++++++++++++++++++++++-------- 1 file changed, 27 insertions(+), 8 deletions(-) diff --git a/MIGRATION.md b/MIGRATION.md index f6017755e902..286cc2f671c4 100644 --- a/MIGRATION.md +++ b/MIGRATION.md @@ -1,16 +1,16 @@ -## Upgrading from 6.x to 7.x +# Upgrading from 6.x to 7.x The main goal of version 7 is to reduce bundle size. This version is breaking because we removed deprecated APIs, upgraded our build tooling, and restructured npm package contents. Below we will outline all the breaking changes you should consider when upgrading. -### Dropping Support for Node.js v6 +## Dropping Support for Node.js v6 Node.js version 6 has reached end of life in April 2019. For Sentry JavaScript SDK version 7, we will no longer be supporting version 6 of Node.js. As far as SDK development goes, dropping support means no longer running integration tests for Node.js version 6, and also no longer handling edge cases specific to version 6. Running the new SDK version on Node.js v6 is therefore highly discouraged. -### Removal Of Old Platform Integrations From `@sentry/integrations` Package +## Removal Of Old Platform Integrations From `@sentry/integrations` Package The following classes will be removed from the `@sentry/integrations` package and can no longer be used: @@ -21,19 +21,19 @@ The following classes will be removed from the `@sentry/integrations` package an These classes have been superseded and were moved into their own packages, `@sentry/angular`, `@sentry/ember`, and `@sentry/vue` in a previous version. Refer to those packages if you want to integrate Sentry into your Angular, Ember, or Vue application. -### Moving To ES6 For CommonJS Files +## Moving To ES6 For CommonJS Files From version 7 onwards, the CommonJS files in Sentry JavaScript SDK packages will use ES6. If you need to support Internet Explorer 11 or old Node.js versions, we recommend using a preprocessing tool like [Babel](https://babeljs.io/) to convert Sentry packages to ES5. -### Renaming Of CDN Bundles +## Renaming Of CDN Bundles CDN bundles will be ES6 by default. Files that followed the naming scheme `bundle.es6.min.js` were renamed to `bundle.min.js` and any bundles using ES5 (files without `.es6`) turned into `bundle.es5.min.js`. See our [docs on CDN bundles](https://docs.sentry.io/platforms/javascript/install/cdn/) for more information. -### Restructuring Of Package Content +## Restructuring Of Package Content Up until v6.x, we have published our packages on npm with the following structure: @@ -54,7 +54,7 @@ If you depend on any specific files in a Sentry JavaScript npm package, you will For example, imports on `@sentry/browser/dist/client` will become `@sentry/browser/cjs/client`. However, directly importing from specific files is discouraged. -### Removing the `API` class from `@sentry/core` +## Removing the `API` class from `@sentry/core` The internal `API` class was removed in favor of the `initAPIDetails` function and the `APIDetails` type. More details can be found in the [PR that deprecated this class](https://github.com/getsentry/sentry-javascript/pull/4281). To migrate, see the following example. @@ -80,7 +80,26 @@ const storeEndpoint = api.getStoreEndpointWithUrlEncodedAuth(); const envelopeEndpoint = api.getEnvelopeEndpointWithUrlEncodedAuth(); ``` -### General API Changes +## Enum changes + +Given that enums have a high bundle-size impact, our long term goal is to eventually remove all enums from the SDK in +favor of string literals. + +### Removed Enums +* The previously deprecated enum `Status` was removed (see [#4891](https://github.com/getsentry/sentry-javascript/pull/4891)). + [This example](#status) explains how to migrate. +* The previously deprecated internal-only enum `RequestSessionStatus` was removed (see + [#4889](https://github.com/getsentry/sentry-javascript/pull/4889)) in favor of string literals. +* The previously deprecated internal-only enum `SessionStatus` was removed (see + [#4890](https://github.com/getsentry/sentry-javascript/pull/4890)) in favor of string literals. + +### Deprecated Enums +The two enums `SpanStatus`, and `Severity` remain deprecated, as we decided to limit the number of high-impact breaking +changes in v7. They will be removed in the next major release which is why we strongly recommend moving to the +corresponding string literals. Here's how to adjust [`Severity`](#severity-severitylevel-and-severitylevels) and +[`SpanStatus`](#spanstatus). + +## General API Changes For our efforts to reduce bundle size of the SDK we had to remove and refactor parts of the package which introduced a few changes to the API: From ec5ebe9acca39531291b5158736bc8d45ab05c23 Mon Sep 17 00:00:00 2001 From: Luca Forstner Date: Thu, 14 Apr 2022 14:47:13 +0200 Subject: [PATCH 046/204] feat: Add `name` field to `EventProcessor` (#4932) --- packages/browser/src/integrations/dedupe.ts | 7 +++++-- .../core/src/integrations/inboundfilters.ts | 7 +++++-- packages/core/test/mocks/integration.ts | 20 +++++++++++-------- packages/hub/src/scope.ts | 18 +++++++++++++++-- packages/integrations/src/dedupe.ts | 7 +++++-- packages/integrations/src/offline.ts | 9 +++++++-- packages/nextjs/src/index.client.ts | 7 ++++++- packages/nextjs/src/index.server.ts | 12 ++++++----- packages/types/src/eventprocessor.ts | 5 ++++- 9 files changed, 67 insertions(+), 25 deletions(-) diff --git a/packages/browser/src/integrations/dedupe.ts b/packages/browser/src/integrations/dedupe.ts index c486c84c815e..48aa8411f268 100644 --- a/packages/browser/src/integrations/dedupe.ts +++ b/packages/browser/src/integrations/dedupe.ts @@ -24,7 +24,7 @@ export class Dedupe implements Integration { * @inheritDoc */ public setupOnce(addGlobalEventProcessor: (callback: EventProcessor) => void, getCurrentHub: () => Hub): void { - addGlobalEventProcessor((currentEvent: Event) => { + const eventProcessor: EventProcessor = currentEvent => { const self = getCurrentHub().getIntegration(Dedupe); if (self) { // Juuust in case something goes wrong @@ -40,7 +40,10 @@ export class Dedupe implements Integration { return (self._previousEvent = currentEvent); } return currentEvent; - }); + }; + + eventProcessor.id = this.name; + addGlobalEventProcessor(eventProcessor); } } diff --git a/packages/core/src/integrations/inboundfilters.ts b/packages/core/src/integrations/inboundfilters.ts index 6151e32e5bbd..0e9dc859a3f8 100644 --- a/packages/core/src/integrations/inboundfilters.ts +++ b/packages/core/src/integrations/inboundfilters.ts @@ -33,7 +33,7 @@ export class InboundFilters implements Integration { * @inheritDoc */ public setupOnce(addGlobalEventProcessor: (processor: EventProcessor) => void, getCurrentHub: () => Hub): void { - addGlobalEventProcessor((event: Event) => { + const eventProcess: EventProcessor = (event: Event) => { const hub = getCurrentHub(); if (hub) { const self = hub.getIntegration(InboundFilters); @@ -45,7 +45,10 @@ export class InboundFilters implements Integration { } } return event; - }); + }; + + eventProcess.id = this.name; + addGlobalEventProcessor(eventProcess); } } diff --git a/packages/core/test/mocks/integration.ts b/packages/core/test/mocks/integration.ts index 465fac64576a..8b95b5673af8 100644 --- a/packages/core/test/mocks/integration.ts +++ b/packages/core/test/mocks/integration.ts @@ -1,6 +1,6 @@ import { getCurrentHub } from '@sentry/hub'; import { configureScope } from '@sentry/minimal'; -import { Event, Integration } from '@sentry/types'; +import { Event, EventProcessor, Integration } from '@sentry/types'; export class TestIntegration implements Integration { public static id: string = 'TestIntegration'; @@ -8,14 +8,18 @@ export class TestIntegration implements Integration { public name: string = 'TestIntegration'; public setupOnce(): void { - configureScope(scope => { - scope.addEventProcessor((event: Event) => { - if (!getCurrentHub().getIntegration(TestIntegration)) { - return event; - } + const eventProcessor: EventProcessor = (event: Event) => { + if (!getCurrentHub().getIntegration(TestIntegration)) { + return event; + } + + return null; + }; - return null; - }); + eventProcessor.id = this.name; + + configureScope(scope => { + scope.addEventProcessor(eventProcessor); }); } } diff --git a/packages/hub/src/scope.ts b/packages/hub/src/scope.ts index 1733cf7f826f..271827519792 100644 --- a/packages/hub/src/scope.ts +++ b/packages/hub/src/scope.ts @@ -19,8 +19,16 @@ import { Transaction, User, } from '@sentry/types'; -import { dateTimestampInSeconds, getGlobalSingleton, isPlainObject, isThenable, SyncPromise } from '@sentry/utils'; - +import { + dateTimestampInSeconds, + getGlobalSingleton, + isPlainObject, + isThenable, + logger, + SyncPromise, +} from '@sentry/utils'; + +import { IS_DEBUG_BUILD } from './flags'; import { Session } from './session'; /** @@ -462,6 +470,12 @@ export class Scope implements ScopeInterface { resolve(event); } else { const result = processor({ ...event }, hint) as Event | null; + + IS_DEBUG_BUILD && + processor.id && + result === null && + logger.log(`Event processor "${processor.id}" dropped event`); + if (isThenable(result)) { void result .then(final => this._notifyEventProcessors(processors, final, hint, index + 1).then(resolve)) diff --git a/packages/integrations/src/dedupe.ts b/packages/integrations/src/dedupe.ts index 644f3d41cd62..a29223f6832b 100644 --- a/packages/integrations/src/dedupe.ts +++ b/packages/integrations/src/dedupe.ts @@ -24,7 +24,7 @@ export class Dedupe implements Integration { * @inheritDoc */ public setupOnce(addGlobalEventProcessor: (callback: EventProcessor) => void, getCurrentHub: () => Hub): void { - addGlobalEventProcessor((currentEvent: Event) => { + const eventProcessor: EventProcessor = currentEvent => { const self = getCurrentHub().getIntegration(Dedupe); if (self) { // Juuust in case something goes wrong @@ -40,7 +40,10 @@ export class Dedupe implements Integration { return (self._previousEvent = currentEvent); } return currentEvent; - }); + }; + + eventProcessor.id = this.name; + addGlobalEventProcessor(eventProcessor); } } diff --git a/packages/integrations/src/offline.ts b/packages/integrations/src/offline.ts index 0d2da0841805..78d8ead05dd4 100644 --- a/packages/integrations/src/offline.ts +++ b/packages/integrations/src/offline.ts @@ -80,10 +80,12 @@ export class Offline implements Integration { }); } - addGlobalEventProcessor((event: Event) => { + const eventProcessor: EventProcessor = event => { if (this.hub && this.hub.getIntegration(Offline)) { // cache if we are positively offline if ('navigator' in this.global && 'onLine' in this.global.navigator && !this.global.navigator.onLine) { + IS_DEBUG_BUILD && logger.log('Event dropped due to being a offline - caching instead'); + void this._cacheEvent(event) .then((_event: Event): Promise => this._enforceMaxEvents()) .catch((_error): void => { @@ -96,7 +98,10 @@ export class Offline implements Integration { } return event; - }); + }; + + eventProcessor.id = this.name; + addGlobalEventProcessor(eventProcessor); // if online now, send any events stored in a previous offline session if ('navigator' in this.global && 'onLine' in this.global.navigator && this.global.navigator.onLine) { diff --git a/packages/nextjs/src/index.client.ts b/packages/nextjs/src/index.client.ts index 817f8ad3ca56..05b4dc64a3f3 100644 --- a/packages/nextjs/src/index.client.ts +++ b/packages/nextjs/src/index.client.ts @@ -1,5 +1,6 @@ import { configureScope, init as reactInit, Integrations as BrowserIntegrations } from '@sentry/react'; import { BrowserTracing, defaultRequestInstrumentationOptions } from '@sentry/tracing'; +import { EventProcessor } from '@sentry/types'; import { nextRouterInstrumentation } from './performance/client'; import { buildMetadata } from './utils/metadata'; @@ -42,9 +43,13 @@ export function init(options: NextjsOptions): void { ...options, integrations, }); + configureScope(scope => { scope.setTag('runtime', 'browser'); - scope.addEventProcessor(event => (event.type === 'transaction' && event.transaction === '/404' ? null : event)); + const filterTransactions: EventProcessor = event => + event.type === 'transaction' && event.transaction === '/404' ? null : event; + filterTransactions.id = 'NextClient404Filter'; + scope.addEventProcessor(filterTransactions); }); } diff --git a/packages/nextjs/src/index.server.ts b/packages/nextjs/src/index.server.ts index 35ef814d97cd..8d34a989264a 100644 --- a/packages/nextjs/src/index.server.ts +++ b/packages/nextjs/src/index.server.ts @@ -2,7 +2,7 @@ import { Carrier, getHubFromCarrier, getMainCarrier } from '@sentry/hub'; import { RewriteFrames } from '@sentry/integrations'; import { configureScope, getCurrentHub, init as nodeInit, Integrations } from '@sentry/node'; import { hasTracingEnabled } from '@sentry/tracing'; -import { Event } from '@sentry/types'; +import { EventProcessor } from '@sentry/types'; import { escapeStringForRegex, logger } from '@sentry/utils'; import * as domainModule from 'domain'; import * as path from 'path'; @@ -71,6 +71,12 @@ export function init(options: NextjsOptions): void { nodeInit(options); + const filterTransactions: EventProcessor = event => { + return event.type === 'transaction' && event.transaction === '/404' ? null : event; + }; + + filterTransactions.id = 'NextServer404Filter'; + configureScope(scope => { scope.setTag('runtime', 'node'); if (isVercel) { @@ -131,10 +137,6 @@ function addServerIntegrations(options: NextjsOptions): void { } } -function filterTransactions(event: Event): Event | null { - return event.type === 'transaction' && event.transaction === '/404' ? null : event; -} - export type { SentryWebpackPluginOptions } from './config/types'; export { withSentryConfig } from './config'; export { withSentry } from './utils/withSentry'; diff --git a/packages/types/src/eventprocessor.ts b/packages/types/src/eventprocessor.ts index 13727ae2e797..22bd74f0ab73 100644 --- a/packages/types/src/eventprocessor.ts +++ b/packages/types/src/eventprocessor.ts @@ -6,4 +6,7 @@ import { Event, EventHint } from './event'; * Returning a PromiseLike will work just fine, but better be sure that you know what you are doing. * Event processing will be deferred until your Promise is resolved. */ -export type EventProcessor = (event: Event, hint?: EventHint) => PromiseLike | Event | null; +export interface EventProcessor { + id?: string; // This field can't be named "name" because functions already have this field natively + (event: Event, hint?: EventHint): PromiseLike | Event | null; +} From 362375c8541e7a6f06b6237ef4faf5325ffa1015 Mon Sep 17 00:00:00 2001 From: Abhijeet Prasad Date: Thu, 14 Apr 2022 09:09:52 -0400 Subject: [PATCH 047/204] meta: 7.0.0-alpha.1 changelog (#4939) --- CHANGELOG.md | 14 ++++++++++++++ MIGRATION.md | 3 +-- 2 files changed, 15 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index a157a0ba3dab..8d406b4fa7c8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,20 @@ - "You miss 100 percent of the chances you don't take. — Wayne Gretzky" — Michael Scott +## 7.0.0-alpha.1 + +- **(breaking)** ref: Inject Transports into Client (#4921) +- **(breaking)** ref: Port functionality from `Backend` to `Client` (#4911) +- **(breaking)** ref: Delete `Backend` classes (#4919) +- **(breaking)** ref(browser): Remove stack parser support for Opera pre v15 (#4923) +- **(breaking)** ref(various): Remove usage of deprecated `event.stacktrace` (#4885) +- feat: Add `name` field to `EventProcessor` (#4932) +- feat: Expose configurable stack parser (#4902) +- ref(build): Turn on `isolatedModules` TS option (#4896) +- feat(tracing): Make `setMeasurement` public API (#4933) +- ref(tracing): Update `setMeasurements` to only set a single measurement (#4920) +- ref(types): Stop using `Severity` enum (#4926) + ## 7.0.0-alpha.0 - **(breaking)** ref: Drop support for Node 6 (#4851) diff --git a/MIGRATION.md b/MIGRATION.md index 286cc2f671c4..a33feb194af3 100644 --- a/MIGRATION.md +++ b/MIGRATION.md @@ -121,8 +121,7 @@ For our efforts to reduce bundle size of the SDK we had to remove and refactor p [#4921](https://github.com/getsentry/sentry-javascript/pull/4921/)). If you are creating your own `Client` or calling `initAndBind`, you will have to supply your desired transport. Either provide a custom one or call `setupBrowserTransport` or `setupNodeTransport` for default transports, depending on your requirements. - - +- Remove support for Opera browser pre v15 # Upgrading from 6.17.x to 6.18.0 From 23f8d6e878ab0118c5a42069dbc96e58c7610d99 Mon Sep 17 00:00:00 2001 From: getsentry-bot Date: Thu, 14 Apr 2022 13:12:39 +0000 Subject: [PATCH 048/204] release: 7.0.0-alpha.1 --- lerna.json | 2 +- packages/angular/package.json | 8 ++++---- packages/browser/package.json | 8 ++++---- packages/core/package.json | 10 +++++----- packages/core/src/version.ts | 2 +- packages/ember/package.json | 10 +++++----- packages/eslint-config-sdk/package.json | 6 +++--- packages/eslint-plugin-sdk/package.json | 2 +- packages/gatsby/package.json | 8 ++++---- packages/hub/package.json | 6 +++--- packages/integration-tests/package.json | 2 +- packages/integrations/package.json | 6 +++--- packages/minimal/package.json | 6 +++--- packages/nextjs/package.json | 18 +++++++++--------- packages/node-integration-tests/package.json | 2 +- packages/node/package.json | 10 +++++----- packages/react/package.json | 10 +++++----- packages/serverless/package.json | 12 ++++++------ packages/tracing/package.json | 12 ++++++------ packages/types/package.json | 2 +- packages/typescript/package.json | 2 +- packages/utils/package.json | 4 ++-- packages/vue/package.json | 12 ++++++------ packages/wasm/package.json | 6 +++--- 24 files changed, 83 insertions(+), 83 deletions(-) diff --git a/lerna.json b/lerna.json index ab5fdf259b84..c99f1bd4f741 100644 --- a/lerna.json +++ b/lerna.json @@ -1,6 +1,6 @@ { "lerna": "3.4.0", - "version": "7.0.0-alpha.0", + "version": "7.0.0-alpha.1", "packages": "packages/*", "npmClient": "yarn", "useWorkspaces": true diff --git a/packages/angular/package.json b/packages/angular/package.json index e92a3235d969..971bd6a1bfb4 100644 --- a/packages/angular/package.json +++ b/packages/angular/package.json @@ -1,6 +1,6 @@ { "name": "@sentry/angular", - "version": "7.0.0-alpha.0", + "version": "7.0.0-alpha.1", "description": "Official Sentry SDK for Angular", "repository": "git://github.com/getsentry/sentry-javascript.git", "homepage": "https://github.com/getsentry/sentry-javascript/tree/master/packages/angular", @@ -21,9 +21,9 @@ "@angular/router": "10.x || 11.x || 12.x || 13.x" }, "dependencies": { - "@sentry/browser": "7.0.0-alpha.0", - "@sentry/types": "7.0.0-alpha.0", - "@sentry/utils": "7.0.0-alpha.0", + "@sentry/browser": "7.0.0-alpha.1", + "@sentry/types": "7.0.0-alpha.1", + "@sentry/utils": "7.0.0-alpha.1", "rxjs": "^6.6.0", "tslib": "^1.9.3" }, diff --git a/packages/browser/package.json b/packages/browser/package.json index 7f1c79235b59..8ed9132b407e 100644 --- a/packages/browser/package.json +++ b/packages/browser/package.json @@ -1,6 +1,6 @@ { "name": "@sentry/browser", - "version": "7.0.0-alpha.0", + "version": "7.0.0-alpha.1", "description": "Official Sentry SDK for browsers", "repository": "git://github.com/getsentry/sentry-javascript.git", "homepage": "https://github.com/getsentry/sentry-javascript/tree/master/packages/browser", @@ -16,9 +16,9 @@ "access": "public" }, "dependencies": { - "@sentry/core": "7.0.0-alpha.0", - "@sentry/types": "7.0.0-alpha.0", - "@sentry/utils": "7.0.0-alpha.0", + "@sentry/core": "7.0.0-alpha.1", + "@sentry/types": "7.0.0-alpha.1", + "@sentry/utils": "7.0.0-alpha.1", "tslib": "^1.9.3" }, "devDependencies": { diff --git a/packages/core/package.json b/packages/core/package.json index 23ee48723424..8a53ffca97e3 100644 --- a/packages/core/package.json +++ b/packages/core/package.json @@ -1,6 +1,6 @@ { "name": "@sentry/core", - "version": "7.0.0-alpha.0", + "version": "7.0.0-alpha.1", "description": "Base implementation for all Sentry JavaScript SDKs", "repository": "git://github.com/getsentry/sentry-javascript.git", "homepage": "https://github.com/getsentry/sentry-javascript/tree/master/packages/core", @@ -16,10 +16,10 @@ "access": "public" }, "dependencies": { - "@sentry/hub": "7.0.0-alpha.0", - "@sentry/minimal": "7.0.0-alpha.0", - "@sentry/types": "7.0.0-alpha.0", - "@sentry/utils": "7.0.0-alpha.0", + "@sentry/hub": "7.0.0-alpha.1", + "@sentry/minimal": "7.0.0-alpha.1", + "@sentry/types": "7.0.0-alpha.1", + "@sentry/utils": "7.0.0-alpha.1", "tslib": "^1.9.3" }, "scripts": { diff --git a/packages/core/src/version.ts b/packages/core/src/version.ts index 485119673c97..2dcfdae39507 100644 --- a/packages/core/src/version.ts +++ b/packages/core/src/version.ts @@ -1 +1 @@ -export const SDK_VERSION = '7.0.0-alpha.0'; +export const SDK_VERSION = '7.0.0-alpha.1'; diff --git a/packages/ember/package.json b/packages/ember/package.json index 3b22b69981b2..23d65a60b571 100644 --- a/packages/ember/package.json +++ b/packages/ember/package.json @@ -1,6 +1,6 @@ { "name": "@sentry/ember", - "version": "7.0.0-alpha.0", + "version": "7.0.0-alpha.1", "description": "Official Sentry SDK for Ember.js", "repository": "git://github.com/getsentry/sentry-javascript.git", "homepage": "https://github.com/getsentry/sentry-javascript/tree/master/packages/ember", @@ -29,10 +29,10 @@ }, "dependencies": { "@embroider/macros": "~0.47.2", - "@sentry/browser": "7.0.0-alpha.0", - "@sentry/tracing": "7.0.0-alpha.0", - "@sentry/types": "7.0.0-alpha.0", - "@sentry/utils": "7.0.0-alpha.0", + "@sentry/browser": "7.0.0-alpha.1", + "@sentry/tracing": "7.0.0-alpha.1", + "@sentry/types": "7.0.0-alpha.1", + "@sentry/utils": "7.0.0-alpha.1", "ember-auto-import": "~1.12.1 || ~2.2.0", "ember-cli-babel": "~7.26.6", "ember-cli-htmlbars": "^6.0.1", diff --git a/packages/eslint-config-sdk/package.json b/packages/eslint-config-sdk/package.json index 45a42c8472d0..844251a06f14 100644 --- a/packages/eslint-config-sdk/package.json +++ b/packages/eslint-config-sdk/package.json @@ -1,6 +1,6 @@ { "name": "@sentry-internal/eslint-config-sdk", - "version": "7.0.0-alpha.0", + "version": "7.0.0-alpha.1", "description": "Official Sentry SDK eslint config", "repository": "git://github.com/getsentry/sentry-javascript.git", "homepage": "https://github.com/getsentry/sentry-javascript/tree/master/packages/eslint-config-sdk", @@ -19,8 +19,8 @@ "access": "public" }, "dependencies": { - "@sentry-internal/eslint-plugin-sdk": "7.0.0-alpha.0", - "@sentry-internal/typescript": "7.0.0-alpha.0", + "@sentry-internal/eslint-plugin-sdk": "7.0.0-alpha.1", + "@sentry-internal/typescript": "7.0.0-alpha.1", "@typescript-eslint/eslint-plugin": "^3.9.0", "@typescript-eslint/parser": "^3.9.0", "eslint-config-prettier": "^6.11.0", diff --git a/packages/eslint-plugin-sdk/package.json b/packages/eslint-plugin-sdk/package.json index 938815cb33a3..65a439ba5555 100644 --- a/packages/eslint-plugin-sdk/package.json +++ b/packages/eslint-plugin-sdk/package.json @@ -1,6 +1,6 @@ { "name": "@sentry-internal/eslint-plugin-sdk", - "version": "7.0.0-alpha.0", + "version": "7.0.0-alpha.1", "description": "Official Sentry SDK eslint plugin", "repository": "git://github.com/getsentry/sentry-javascript.git", "homepage": "https://github.com/getsentry/sentry-javascript/tree/master/packages/eslint-plugin-sdk", diff --git a/packages/gatsby/package.json b/packages/gatsby/package.json index 976ab9847663..1bf607d15a4c 100644 --- a/packages/gatsby/package.json +++ b/packages/gatsby/package.json @@ -1,6 +1,6 @@ { "name": "@sentry/gatsby", - "version": "7.0.0-alpha.0", + "version": "7.0.0-alpha.1", "description": "Official Sentry SDK for Gatsby.js", "repository": "git://github.com/getsentry/sentry-javascript.git", "homepage": "https://github.com/getsentry/sentry-javascript/tree/master/packages/gatsby", @@ -20,8 +20,8 @@ "access": "public" }, "dependencies": { - "@sentry/react": "7.0.0-alpha.0", - "@sentry/tracing": "7.0.0-alpha.0", + "@sentry/react": "7.0.0-alpha.1", + "@sentry/tracing": "7.0.0-alpha.1", "@sentry/webpack-plugin": "1.18.8" }, "peerDependencies": { @@ -29,7 +29,7 @@ "react": "15.x || 16.x || 17.x || 18.x" }, "devDependencies": { - "@sentry/types": "7.0.0-alpha.0", + "@sentry/types": "7.0.0-alpha.1", "@testing-library/react": "^13.0.0", "react": "^18.0.0" }, diff --git a/packages/hub/package.json b/packages/hub/package.json index 8bb57aab02e2..13351981746c 100644 --- a/packages/hub/package.json +++ b/packages/hub/package.json @@ -1,6 +1,6 @@ { "name": "@sentry/hub", - "version": "7.0.0-alpha.0", + "version": "7.0.0-alpha.1", "description": "Sentry hub which handles global state managment.", "repository": "git://github.com/getsentry/sentry-javascript.git", "homepage": "https://github.com/getsentry/sentry-javascript/tree/master/packages/hub", @@ -16,8 +16,8 @@ "access": "public" }, "dependencies": { - "@sentry/types": "7.0.0-alpha.0", - "@sentry/utils": "7.0.0-alpha.0", + "@sentry/types": "7.0.0-alpha.1", + "@sentry/utils": "7.0.0-alpha.1", "tslib": "^1.9.3" }, "scripts": { diff --git a/packages/integration-tests/package.json b/packages/integration-tests/package.json index 5456ef0e19be..e501c87aca91 100644 --- a/packages/integration-tests/package.json +++ b/packages/integration-tests/package.json @@ -1,6 +1,6 @@ { "name": "@sentry-internal/browser-integration-tests", - "version": "7.0.0-alpha.0", + "version": "7.0.0-alpha.1", "main": "index.js", "license": "MIT", "engines": { diff --git a/packages/integrations/package.json b/packages/integrations/package.json index 52f2c23ea924..de3eb70ed7bd 100644 --- a/packages/integrations/package.json +++ b/packages/integrations/package.json @@ -1,6 +1,6 @@ { "name": "@sentry/integrations", - "version": "7.0.0-alpha.0", + "version": "7.0.0-alpha.1", "description": "Pluggable integrations that can be used to enhance JS SDKs", "repository": "git://github.com/getsentry/sentry-javascript.git", "homepage": "https://github.com/getsentry/sentry-javascript/tree/master/packages/integrations", @@ -16,8 +16,8 @@ "module": "build/npm/esm/index.js", "types": "build/npm/types/index.d.ts", "dependencies": { - "@sentry/types": "7.0.0-alpha.0", - "@sentry/utils": "7.0.0-alpha.0", + "@sentry/types": "7.0.0-alpha.1", + "@sentry/utils": "7.0.0-alpha.1", "localforage": "^1.8.1", "tslib": "^1.9.3" }, diff --git a/packages/minimal/package.json b/packages/minimal/package.json index 4154add1ca3d..3ca8889512a2 100644 --- a/packages/minimal/package.json +++ b/packages/minimal/package.json @@ -1,6 +1,6 @@ { "name": "@sentry/minimal", - "version": "7.0.0-alpha.0", + "version": "7.0.0-alpha.1", "description": "Sentry minimal library that can be used in other packages", "repository": "git://github.com/getsentry/sentry-javascript.git", "homepage": "https://github.com/getsentry/sentry-javascript/tree/master/packages/minimal", @@ -16,8 +16,8 @@ "access": "public" }, "dependencies": { - "@sentry/hub": "7.0.0-alpha.0", - "@sentry/types": "7.0.0-alpha.0", + "@sentry/hub": "7.0.0-alpha.1", + "@sentry/types": "7.0.0-alpha.1", "tslib": "^1.9.3" }, "scripts": { diff --git a/packages/nextjs/package.json b/packages/nextjs/package.json index 5674ffc46956..c4ca89888d3a 100644 --- a/packages/nextjs/package.json +++ b/packages/nextjs/package.json @@ -1,6 +1,6 @@ { "name": "@sentry/nextjs", - "version": "7.0.0-alpha.0", + "version": "7.0.0-alpha.1", "description": "Official Sentry SDK for Next.js", "repository": "git://github.com/getsentry/sentry-javascript.git", "homepage": "https://github.com/getsentry/sentry-javascript/tree/master/packages/nextjs", @@ -17,18 +17,18 @@ "access": "public" }, "dependencies": { - "@sentry/core": "7.0.0-alpha.0", - "@sentry/hub": "7.0.0-alpha.0", - "@sentry/integrations": "7.0.0-alpha.0", - "@sentry/node": "7.0.0-alpha.0", - "@sentry/react": "7.0.0-alpha.0", - "@sentry/tracing": "7.0.0-alpha.0", - "@sentry/utils": "7.0.0-alpha.0", + "@sentry/core": "7.0.0-alpha.1", + "@sentry/hub": "7.0.0-alpha.1", + "@sentry/integrations": "7.0.0-alpha.1", + "@sentry/node": "7.0.0-alpha.1", + "@sentry/react": "7.0.0-alpha.1", + "@sentry/tracing": "7.0.0-alpha.1", + "@sentry/utils": "7.0.0-alpha.1", "@sentry/webpack-plugin": "1.18.8", "tslib": "^1.9.3" }, "devDependencies": { - "@sentry/types": "7.0.0-alpha.0", + "@sentry/types": "7.0.0-alpha.1", "@types/webpack": "^4.41.31", "next": "10.1.3" }, diff --git a/packages/node-integration-tests/package.json b/packages/node-integration-tests/package.json index 3f10098ba15b..2e662c2bb2fa 100644 --- a/packages/node-integration-tests/package.json +++ b/packages/node-integration-tests/package.json @@ -1,6 +1,6 @@ { "name": "@sentry-internal/node-integration-tests", - "version": "7.0.0-alpha.0", + "version": "7.0.0-alpha.1", "license": "MIT", "engines": { "node": ">=10" diff --git a/packages/node/package.json b/packages/node/package.json index f7a19037a029..d0710af4fe4c 100644 --- a/packages/node/package.json +++ b/packages/node/package.json @@ -1,6 +1,6 @@ { "name": "@sentry/node", - "version": "7.0.0-alpha.0", + "version": "7.0.0-alpha.1", "description": "Official Sentry SDK for Node.js", "repository": "git://github.com/getsentry/sentry-javascript.git", "homepage": "https://github.com/getsentry/sentry-javascript/tree/master/packages/node", @@ -16,10 +16,10 @@ "access": "public" }, "dependencies": { - "@sentry/core": "7.0.0-alpha.0", - "@sentry/hub": "7.0.0-alpha.0", - "@sentry/types": "7.0.0-alpha.0", - "@sentry/utils": "7.0.0-alpha.0", + "@sentry/core": "7.0.0-alpha.1", + "@sentry/hub": "7.0.0-alpha.1", + "@sentry/types": "7.0.0-alpha.1", + "@sentry/utils": "7.0.0-alpha.1", "cookie": "^0.4.1", "https-proxy-agent": "^5.0.0", "lru_map": "^0.3.3", diff --git a/packages/react/package.json b/packages/react/package.json index 7835071eccdc..2fea1d64d5be 100644 --- a/packages/react/package.json +++ b/packages/react/package.json @@ -1,6 +1,6 @@ { "name": "@sentry/react", - "version": "7.0.0-alpha.0", + "version": "7.0.0-alpha.1", "description": "Official Sentry SDK for React.js", "repository": "git://github.com/getsentry/sentry-javascript.git", "homepage": "https://github.com/getsentry/sentry-javascript/tree/master/packages/react", @@ -16,10 +16,10 @@ "access": "public" }, "dependencies": { - "@sentry/browser": "7.0.0-alpha.0", - "@sentry/minimal": "7.0.0-alpha.0", - "@sentry/types": "7.0.0-alpha.0", - "@sentry/utils": "7.0.0-alpha.0", + "@sentry/browser": "7.0.0-alpha.1", + "@sentry/minimal": "7.0.0-alpha.1", + "@sentry/types": "7.0.0-alpha.1", + "@sentry/utils": "7.0.0-alpha.1", "hoist-non-react-statics": "^3.3.2", "tslib": "^1.9.3" }, diff --git a/packages/serverless/package.json b/packages/serverless/package.json index ac3374862764..d77177b66c1a 100644 --- a/packages/serverless/package.json +++ b/packages/serverless/package.json @@ -1,6 +1,6 @@ { "name": "@sentry/serverless", - "version": "7.0.0-alpha.0", + "version": "7.0.0-alpha.1", "description": "Official Sentry SDK for various serverless solutions", "repository": "git://github.com/getsentry/sentry-javascript.git", "homepage": "https://github.com/getsentry/sentry-javascript/tree/master/packages/serverless", @@ -16,11 +16,11 @@ "access": "public" }, "dependencies": { - "@sentry/minimal": "7.0.0-alpha.0", - "@sentry/node": "7.0.0-alpha.0", - "@sentry/tracing": "7.0.0-alpha.0", - "@sentry/types": "7.0.0-alpha.0", - "@sentry/utils": "7.0.0-alpha.0", + "@sentry/minimal": "7.0.0-alpha.1", + "@sentry/node": "7.0.0-alpha.1", + "@sentry/tracing": "7.0.0-alpha.1", + "@sentry/types": "7.0.0-alpha.1", + "@sentry/utils": "7.0.0-alpha.1", "@types/aws-lambda": "^8.10.62", "@types/express": "^4.17.2", "tslib": "^1.9.3" diff --git a/packages/tracing/package.json b/packages/tracing/package.json index e9d01ff9c2a3..788189a6a8f9 100644 --- a/packages/tracing/package.json +++ b/packages/tracing/package.json @@ -1,6 +1,6 @@ { "name": "@sentry/tracing", - "version": "7.0.0-alpha.0", + "version": "7.0.0-alpha.1", "description": "Extensions for Sentry AM", "repository": "git://github.com/getsentry/sentry-javascript.git", "homepage": "https://github.com/getsentry/sentry-javascript/tree/master/packages/tracing", @@ -16,14 +16,14 @@ "access": "public" }, "dependencies": { - "@sentry/hub": "7.0.0-alpha.0", - "@sentry/minimal": "7.0.0-alpha.0", - "@sentry/types": "7.0.0-alpha.0", - "@sentry/utils": "7.0.0-alpha.0", + "@sentry/hub": "7.0.0-alpha.1", + "@sentry/minimal": "7.0.0-alpha.1", + "@sentry/types": "7.0.0-alpha.1", + "@sentry/utils": "7.0.0-alpha.1", "tslib": "^1.9.3" }, "devDependencies": { - "@sentry/browser": "7.0.0-alpha.0", + "@sentry/browser": "7.0.0-alpha.1", "@types/express": "^4.17.1" }, "scripts": { diff --git a/packages/types/package.json b/packages/types/package.json index 2fc59e9a1535..5c1c37c1c159 100644 --- a/packages/types/package.json +++ b/packages/types/package.json @@ -1,6 +1,6 @@ { "name": "@sentry/types", - "version": "7.0.0-alpha.0", + "version": "7.0.0-alpha.1", "description": "Types for all Sentry JavaScript SDKs", "repository": "git://github.com/getsentry/sentry-javascript.git", "homepage": "https://github.com/getsentry/sentry-javascript/tree/master/packages/types", diff --git a/packages/typescript/package.json b/packages/typescript/package.json index 338ab3906aef..fe238ae810aa 100644 --- a/packages/typescript/package.json +++ b/packages/typescript/package.json @@ -1,6 +1,6 @@ { "name": "@sentry-internal/typescript", - "version": "7.0.0-alpha.0", + "version": "7.0.0-alpha.1", "description": "Typescript configuration used at Sentry", "repository": "git://github.com/getsentry/sentry-javascript.git", "homepage": "https://github.com/getsentry/sentry-javascript/tree/master/packages/typescript", diff --git a/packages/utils/package.json b/packages/utils/package.json index ddce3d7933aa..142ae23e6010 100644 --- a/packages/utils/package.json +++ b/packages/utils/package.json @@ -1,6 +1,6 @@ { "name": "@sentry/utils", - "version": "7.0.0-alpha.0", + "version": "7.0.0-alpha.1", "description": "Utilities for all Sentry JavaScript SDKs", "repository": "git://github.com/getsentry/sentry-javascript.git", "homepage": "https://github.com/getsentry/sentry-javascript/tree/master/packages/utils", @@ -16,7 +16,7 @@ "access": "public" }, "dependencies": { - "@sentry/types": "7.0.0-alpha.0", + "@sentry/types": "7.0.0-alpha.1", "tslib": "^1.9.3" }, "devDependencies": { diff --git a/packages/vue/package.json b/packages/vue/package.json index b49beced0934..29452f54ce4c 100644 --- a/packages/vue/package.json +++ b/packages/vue/package.json @@ -1,6 +1,6 @@ { "name": "@sentry/vue", - "version": "7.0.0-alpha.0", + "version": "7.0.0-alpha.1", "description": "Official Sentry SDK for Vue.js", "repository": "git://github.com/getsentry/sentry-javascript.git", "homepage": "https://github.com/getsentry/sentry-javascript/tree/master/packages/vue", @@ -16,11 +16,11 @@ "access": "public" }, "dependencies": { - "@sentry/browser": "7.0.0-alpha.0", - "@sentry/core": "7.0.0-alpha.0", - "@sentry/minimal": "7.0.0-alpha.0", - "@sentry/types": "7.0.0-alpha.0", - "@sentry/utils": "7.0.0-alpha.0", + "@sentry/browser": "7.0.0-alpha.1", + "@sentry/core": "7.0.0-alpha.1", + "@sentry/minimal": "7.0.0-alpha.1", + "@sentry/types": "7.0.0-alpha.1", + "@sentry/utils": "7.0.0-alpha.1", "tslib": "^1.9.3" }, "peerDependencies": { diff --git a/packages/wasm/package.json b/packages/wasm/package.json index b8d70d1bd924..8c850944a72f 100644 --- a/packages/wasm/package.json +++ b/packages/wasm/package.json @@ -1,6 +1,6 @@ { "name": "@sentry/wasm", - "version": "7.0.0-alpha.0", + "version": "7.0.0-alpha.1", "description": "Support for WASM.", "repository": "git://github.com/getsentry/sentry-javascript.git", "homepage": "https://github.com/getsentry/sentry-javascript/tree/master/packages/wasm", @@ -16,8 +16,8 @@ "access": "public" }, "dependencies": { - "@sentry/browser": "7.0.0-alpha.0", - "@sentry/types": "7.0.0-alpha.0", + "@sentry/browser": "7.0.0-alpha.1", + "@sentry/types": "7.0.0-alpha.1", "tslib": "^1.9.3" }, "devDependencies": { From e5f26a58d5e70467c98238bbc777189ffad393a4 Mon Sep 17 00:00:00 2001 From: Katie Byers Date: Thu, 14 Apr 2022 07:11:37 -0700 Subject: [PATCH 049/204] chore(gatsby): Create type declarations for gatsby plugin files (#4928) When creating a Gatsby plugin (which is what `@sentry/gatsby` actually is), there are certain files[1] which need to live at the package root level. Because they are written in JS (not TS) and don't live in `src/`, we have thus far more or less ignored them as far as type-ful processing goes. With the recent updates to TS, jest, and ts-jest, typechecking has now gotten stricter (especially in tests) and so now declaration files are needed for those gatsby-required, root-level files if we want to test them (which we do). This adds machinery to create, manage, and publish these declaration files. [1] https://www.gatsbyjs.com/docs/files-gatsby-looks-for-in-a-plugin/ --- .gitignore | 2 ++ packages/gatsby/.eslintrc.js | 5 ++--- packages/gatsby/.npmignore | 2 ++ packages/gatsby/package.json | 5 +++-- packages/gatsby/scripts/prepack.ts | 2 +- packages/gatsby/tsconfig.plugin.json | 17 +++++++++++++++++ 6 files changed, 27 insertions(+), 6 deletions(-) create mode 100644 packages/gatsby/tsconfig.plugin.json diff --git a/.gitignore b/.gitignore index 4418a41432bc..ae301fadfde7 100644 --- a/.gitignore +++ b/.gitignore @@ -40,3 +40,5 @@ tmp.js # eslint .eslintcache eslintcache/* + +*.d.ts diff --git a/packages/gatsby/.eslintrc.js b/packages/gatsby/.eslintrc.js index ab610a82206a..6aff306daa40 100644 --- a/packages/gatsby/.eslintrc.js +++ b/packages/gatsby/.eslintrc.js @@ -6,8 +6,7 @@ module.exports = { parserOptions: { jsx: true, }, - // ignoring the package-specific prepack script here b/c it is not - // covered by a `tsconfig` which makes eslint throw an error - ignorePatterns: ['scripts/prepack.ts'], + // ignore these because they're not covered by a `tsconfig`, which makes eslint throw an error + ignorePatterns: ['scripts/prepack.ts', 'gatsby-browser.d.ts', 'gatsby-node.d.ts'], extends: ['../../.eslintrc.js'], }; diff --git a/packages/gatsby/.npmignore b/packages/gatsby/.npmignore index 66474694b223..35348e6a718d 100644 --- a/packages/gatsby/.npmignore +++ b/packages/gatsby/.npmignore @@ -10,3 +10,5 @@ # Gatsby specific !gatsby-browser.js !gatsby-node.js +!gatsby-browser.d.ts +!gatsby-node.d.ts diff --git a/packages/gatsby/package.json b/packages/gatsby/package.json index 1bf607d15a4c..23cb258fb512 100644 --- a/packages/gatsby/package.json +++ b/packages/gatsby/package.json @@ -34,11 +34,12 @@ "react": "^18.0.0" }, "scripts": { - "build": "run-p build:cjs build:esm build:types", + "build": "run-p build:cjs build:esm build:types build:plugin", "build:cjs": "tsc -p tsconfig.cjs.json", "build:dev": "run-s build", "build:es5": "yarn build:cjs # *** backwards compatibility - remove in v7 ***", "build:esm": "tsc -p tsconfig.esm.json", + "build:plugin": "tsc -p tsconfig.plugin.json", "build:types": "tsc -p tsconfig.types.json", "build:watch": "run-p build:cjs:watch build:esm:watch build:types:watch", "build:cjs:watch": "tsc -p tsconfig.cjs.json --watch", @@ -48,7 +49,7 @@ "build:types:watch": "tsc -p tsconfig.types.json --watch", "build:npm": "ts-node ../../scripts/prepack.ts && npm pack ./build", "circularDepCheck": "madge --circular src/index.ts", - "clean": "rimraf build coverage", + "clean": "rimraf build coverage *.d.ts", "fix": "run-s fix:eslint fix:prettier", "fix:eslint": "eslint . --format stylish --fix", "fix:prettier": "prettier --write \"{src,test,scripts}/**/*.ts\"", diff --git a/packages/gatsby/scripts/prepack.ts b/packages/gatsby/scripts/prepack.ts index 5aa95909d70c..3f5a8e24d674 100644 --- a/packages/gatsby/scripts/prepack.ts +++ b/packages/gatsby/scripts/prepack.ts @@ -6,7 +6,7 @@ import * as fs from 'fs'; import * as path from 'path'; -const PACKAGE_ASSETS = ['gatsby-browser.js', 'gatsby-node.js']; +const PACKAGE_ASSETS = ['gatsby-browser.js', 'gatsby-browser.d.ts', 'gatsby-node.js', 'gatsby-node.d.ts']; export function prepack(buildDir: string): boolean { // copy package-specific assets to build dir diff --git a/packages/gatsby/tsconfig.plugin.json b/packages/gatsby/tsconfig.plugin.json new file mode 100644 index 000000000000..8a755642dd8b --- /dev/null +++ b/packages/gatsby/tsconfig.plugin.json @@ -0,0 +1,17 @@ +{ + "extends": "./tsconfig.json", + + "include": ["gatsby-browser.js", "gatsby-node.js"], + + "compilerOptions": { + // should include all types from `./tsconfig.json` plus types for all test frameworks used + // "types": ["node", "jest"] + "declaration": true, + "declarationMap": false, + "emitDeclarationOnly": true, + "allowJs": true, + "skipLibCheck": true + + // other package-specific, plugin-specific options + } +} From 92bf5d398f5879ed1898d80e44081dcfea939941 Mon Sep 17 00:00:00 2001 From: Abhijeet Prasad Date: Thu, 14 Apr 2022 11:10:19 -0400 Subject: [PATCH 050/204] chore(dev): Remove `tslint` (#4940) tslint has been deprecated for eslint: https://github.com/palantir/tslint#tslint. The SDK switched over to eslint in Aug 2020. Let's formally remove tslint support in our typescript package. This cleans up our dependencies and reduces confusion to new SDK devs. --- packages/typescript/.npmignore | 1 - packages/typescript/README.md | 8 --- packages/typescript/package.json | 6 +-- packages/typescript/tsconfig.json | 10 ---- packages/typescript/tslint.json | 88 ------------------------------- yarn.lock | 56 ++------------------ 6 files changed, 4 insertions(+), 165 deletions(-) delete mode 100644 packages/typescript/tslint.json diff --git a/packages/typescript/.npmignore b/packages/typescript/.npmignore index d59f27ed8132..457274275b7c 100644 --- a/packages/typescript/.npmignore +++ b/packages/typescript/.npmignore @@ -1,3 +1,2 @@ * !/tsconfig.json -!/tslint.json diff --git a/packages/typescript/README.md b/packages/typescript/README.md index e06503ddf511..aa32b9fdf48b 100644 --- a/packages/typescript/README.md +++ b/packages/typescript/README.md @@ -36,14 +36,6 @@ npm install --save-dev @sentry-internal/typescript Add the following config files to your project's root directory: -**tslint.json**: - -```json -{ - "extends": "@sentry-internal/typescript/tslint" -} -``` - **tsconfig.json**: ```json diff --git a/packages/typescript/package.json b/packages/typescript/package.json index fe238ae810aa..9a8a3ed28d4c 100644 --- a/packages/typescript/package.json +++ b/packages/typescript/package.json @@ -10,12 +10,8 @@ "publishConfig": { "access": "public" }, - "dependencies": { - "tslint-config-prettier": "^1.18.0", - "tslint-consistent-codestyle": "^1.15.1" - }, + "dependencies": {}, "peerDependencies": { - "tslint": "5.16.0", "typescript": "3.8.3" }, "scripts": { diff --git a/packages/typescript/tsconfig.json b/packages/typescript/tsconfig.json index 3a427cc596c9..49c2339c7f21 100644 --- a/packages/typescript/tsconfig.json +++ b/packages/typescript/tsconfig.json @@ -18,16 +18,6 @@ "noImplicitUseStrict": true, "noUnusedLocals": true, "noUnusedParameters": true, - "plugins": [ - { - "name": "typescript-tslint-plugin", - "configFile": "./tslint.json", - "alwaysShowRuleFailuresAsWarnings": false, - "ignoreDefinitionFiles": true, - "mockTypeScriptVersion": false, - "suppressWhileTypeErrorsPresent": false - } - ], "preserveWatchOutput": true, "pretty": true, "sourceMap": true, diff --git a/packages/typescript/tslint.json b/packages/typescript/tslint.json deleted file mode 100644 index a739adabaf6a..000000000000 --- a/packages/typescript/tslint.json +++ /dev/null @@ -1,88 +0,0 @@ -{ - "extends": ["tslint:all", "tslint-config-prettier", "tslint-consistent-codestyle"], - "rules": { - // This rule has side effects and must be disabled - "no-unused-variable": false, - - "no-submodule-imports": false, - "no-null-keyword": false, - // We don't want these - "newline-before-return": false, - "no-any": false, - "no-magic-numbers": false, - "no-parameter-properties": false, - "no-require-imports": false, - "prefer-function-over-method": [false], - "strict-boolean-expressions": false, - "no-inferrable-types": false, - "ban-ts-ignore": false, - "increment-decrement": false, - "promise-function-async": false, - "ban-types": [true, ["async", "Use Promises instead, as async/await adds a lot to bundle size."]], - // These are too strict in tslint:all - "comment-format": [true, "check-space"], - "completed-docs": [ - true, - { - "classes": { - "tags": { - "content": {}, - "existence": ["inheritDoc", "hidden"] - } - }, - "enums": { - "tags": { - "content": {}, - "existence": ["inheritDoc", "hidden"] - } - }, - "enum-members": { - "tags": { - "content": {}, - "existence": ["inheritDoc", "hidden"] - } - }, - "functions": { - "tags": { - "content": {}, - "existence": ["inheritDoc", "hidden"] - } - }, - "interfaces": { - "tags": { - "content": {}, - "existence": ["inheritDoc", "hidden"] - } - }, - "methods": { - "tags": { - "content": {}, - "existence": ["inheritDoc", "hidden"] - } - }, - "properties": { - "tags": { - "content": {}, - "existence": ["inheritDoc", "hidden"] - }, - "locations": "instance" - } - } - ], - "interface-name": [true, "never-prefix"], - "member-ordering": [true, "statics-first"], - "no-console": [true, "log"], - "only-arrow-functions": [true, "allow-named-functions"], - "typedef": [true, "call-signature", "parameter", "property-declaration", "member-variable-declaration"], - "variable-name": [true, "check-format", "allow-leading-underscore", "ban-keywords"], - "naming-convention": [ - true, - // This config will apply to properties AND methods. If you only need it for properties, use "property" instead of - // "member". - { "type": "member", "modifiers": "protected", "leadingUnderscore": "require" }, - { "type": "member", "modifiers": "private", "leadingUnderscore": "require" } - ], - // we cannot use Promises as they are not IE10-11 compatibile, so we had to create our own implementation - "await-promise": [true, "PromiseLike"] - } -} diff --git a/yarn.lock b/yarn.lock index 482f2369ee5b..711e8abfe0d4 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1785,25 +1785,6 @@ minimatch "^3.0.4" strip-json-comments "^3.1.1" -"@fimbul/bifrost@^0.21.0": - version "0.21.0" - resolved "https://registry.yarnpkg.com/@fimbul/bifrost/-/bifrost-0.21.0.tgz#d0fafa25938fda475657a6a1e407a21bbe02c74e" - integrity sha512-ou8VU+nTmOW1jeg+FT+sn+an/M0Xb9G16RucrfhjXGWv1Q97kCoM5CG9Qj7GYOSdu7km72k7nY83Eyr53Bkakg== - dependencies: - "@fimbul/ymir" "^0.21.0" - get-caller-file "^2.0.0" - tslib "^1.8.1" - tsutils "^3.5.0" - -"@fimbul/ymir@^0.21.0": - version "0.21.0" - resolved "https://registry.yarnpkg.com/@fimbul/ymir/-/ymir-0.21.0.tgz#8525726787aceeafd4e199472c0d795160b5d4a1" - integrity sha512-T/y7WqPsm4n3zhT08EpB5sfdm2Kvw3gurAxr2Lr5dQeLi8ZsMlNT/Jby+ZmuuAAd1PnXYzKp+2SXgIkQIIMCUg== - dependencies: - inversify "^5.0.0" - reflect-metadata "^0.1.12" - tslib "^1.8.1" - "@glimmer/component@~1.0.0": version "1.0.4" resolved "https://registry.yarnpkg.com/@glimmer/component/-/component-1.0.4.tgz#1c85a5181615a6647f6acfaaed68e28ad7e9626e" @@ -11860,7 +11841,7 @@ get-caller-file@^1.0.1: resolved "https://registry.yarnpkg.com/get-caller-file/-/get-caller-file-1.0.3.tgz#f978fa4c90d1dfe7ff2d6beda2a515e713bdcf4a" integrity sha512-3t6rVToeoZfYSGd8YoLFR2DJkiQrIiUrGcjvFX2mDw3bn6k2OtwHN0TNCLbBO+w8qTvimhDkv+LSscbJY1vE6w== -get-caller-file@^2.0.0, get-caller-file@^2.0.1, get-caller-file@^2.0.5: +get-caller-file@^2.0.1, get-caller-file@^2.0.5: version "2.0.5" resolved "https://registry.yarnpkg.com/get-caller-file/-/get-caller-file-2.0.5.tgz#4f94412a82db32f36e3b0b9741f8a97feb031f7e" integrity sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg== @@ -13204,11 +13185,6 @@ invariant@^2.2.1, invariant@^2.2.2, invariant@^2.2.4: dependencies: loose-envify "^1.0.0" -inversify@^5.0.0: - version "5.0.5" - resolved "https://registry.yarnpkg.com/inversify/-/inversify-5.0.5.tgz#bd1f8e6d8e0f739331acd8ba9bc954635aae0bbf" - integrity sha512-60QsfPz8NAU/GZqXu8hJ+BhNf/C/c+Hp0eDc6XMIJTxBiP36AQyyQKpBkOVTLWBFDQWYVHpbbEuIsHu9dLuJDA== - invert-kv@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/invert-kv/-/invert-kv-2.0.0.tgz#7393f5afa59ec9ff5f67a27620d11c226e3eec02" @@ -19185,11 +19161,6 @@ redux@^4.0.5: loose-envify "^1.4.0" symbol-observable "^1.2.0" -reflect-metadata@^0.1.12: - version "0.1.13" - resolved "https://registry.yarnpkg.com/reflect-metadata/-/reflect-metadata-0.1.13.tgz#67ae3ca57c972a2aa1642b10fe363fe32d49dc08" - integrity sha512-Ts1Y/anZELhSsjMcU605fU9RE4Oi3p5ORujwbIKXfWa+0Zxs510Qrmrce5/Jowq3cHSZSJqBjypxmHarc+vEWg== - regenerate-unicode-properties@^8.2.0: version "8.2.0" resolved "https://registry.yarnpkg.com/regenerate-unicode-properties/-/regenerate-unicode-properties-8.2.0.tgz#e5de7111d655e7ba60c057dbe9ff37c87e65cdec" @@ -21862,7 +21833,7 @@ tsconfig-paths@^3.9.0: minimist "^1.2.0" strip-bom "^3.0.0" -tslib@^1.10.0, tslib@^1.7.1, tslib@^1.8.1, tslib@^1.9.0, tslib@^1.9.3: +tslib@^1.10.0, tslib@^1.8.1, tslib@^1.9.0, tslib@^1.9.3: version "1.14.1" resolved "https://registry.yarnpkg.com/tslib/-/tslib-1.14.1.tgz#cf2d38bdc34a134bcaf1091c41f6619e2f672d00" integrity sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg== @@ -21877,28 +21848,7 @@ tslib@^2.3.0, tslib@^2.3.1: resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.3.1.tgz#e8a335add5ceae51aa261d32a490158ef042ef01" integrity sha512-77EbyPPpMz+FRFRuAFlWMtmgUWGe9UOG2Z25NqCwiIjRhOf5iKGuzSe5P2w1laq+FkRy4p+PCuVkJSGkzTEKVw== -tslint-config-prettier@^1.18.0: - version "1.18.0" - resolved "https://registry.yarnpkg.com/tslint-config-prettier/-/tslint-config-prettier-1.18.0.tgz#75f140bde947d35d8f0d238e0ebf809d64592c37" - integrity sha512-xPw9PgNPLG3iKRxmK7DWr+Ea/SzrvfHtjFt5LBl61gk2UBG/DB9kCXRjv+xyIU1rUtnayLeMUVJBcMX8Z17nDg== - -tslint-consistent-codestyle@^1.15.1: - version "1.16.0" - resolved "https://registry.yarnpkg.com/tslint-consistent-codestyle/-/tslint-consistent-codestyle-1.16.0.tgz#52348ea899a7e025b37cc6545751c6a566a19077" - integrity sha512-ebR/xHyMEuU36hGNOgCfjGBNYxBPixf0yU1Yoo6s3BrpBRFccjPOmIVaVvQsWAUAMdmfzHOCihVkcaMfimqvHw== - dependencies: - "@fimbul/bifrost" "^0.21.0" - tslib "^1.7.1" - tsutils "^2.29.0" - -tsutils@^2.29.0: - version "2.29.0" - resolved "https://registry.yarnpkg.com/tsutils/-/tsutils-2.29.0.tgz#32b488501467acbedd4b85498673a0812aca0b99" - integrity sha512-g5JVHCIJwzfISaXpXE1qvNalca5Jwob6FjI4AoPlqMusJ6ftFE7IkkFoMhVLRgK+4Kx3gkzb8UZK5t5yTTvEmA== - dependencies: - tslib "^1.8.1" - -tsutils@^3.0.0, tsutils@^3.17.1, tsutils@^3.5.0: +tsutils@^3.0.0, tsutils@^3.17.1: version "3.21.0" resolved "https://registry.yarnpkg.com/tsutils/-/tsutils-3.21.0.tgz#b48717d394cea6c1e096983eed58e9d61715b623" integrity sha512-mHKK3iUXL+3UF6xL5k0PEhKRUBKPBCv/+RkEOpjRWxxx27KKRBmmA60A9pgOUvMi8GKhRMPEmjBRPzs2W7O1OA== From e816c342135a9d8261c5f107abcf035489207f2d Mon Sep 17 00:00:00 2001 From: Onur Temizkan Date: Thu, 14 Apr 2022 18:11:07 +0300 Subject: [PATCH 051/204] ref(test): Switch to `mongodb-memory-server-global` (#4872) Switching to `mongodb-memory-server-global` which downloads MongoDB binaries on `postinstall` instead of first test run. This will fix test flakiness caused by timeouts. --- .github/workflows/build.yml | 1 + packages/node-integration-tests/package.json | 8 +++++++- .../suites/tracing/auto-instrument/mongodb/test.ts | 7 +++++-- yarn.lock | 6 +++--- 4 files changed, 16 insertions(+), 6 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 21d0fd5685f9..bfefea5392c5 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -18,6 +18,7 @@ env: ${{ github.workspace }}/node_modules ${{ github.workspace }}/packages/**/node_modules ~/.cache/ms-playwright/ + ~/.cache/mongodb-binaries/ # DEPENDENCY_CACHE_KEY: can't be set here because we don't have access to yarn.lock diff --git a/packages/node-integration-tests/package.json b/packages/node-integration-tests/package.json index 2e662c2bb2fa..3f09e8d64192 100644 --- a/packages/node-integration-tests/package.json +++ b/packages/node-integration-tests/package.json @@ -21,10 +21,16 @@ "cors": "^2.8.5", "express": "^4.17.3", "mongodb": "^3.7.3", - "mongodb-memory-server": "^7.6.3", + "mongodb-memory-server-global": "^7.6.3", "mysql": "^2.18.1", "nock": "^13.1.0", "pg": "^8.7.3", "portfinder": "^1.0.28" + }, + "config": { + "mongodbMemoryServer": { + "preferGlobalPath": true, + "runtimeDownload": false + } } } diff --git a/packages/node-integration-tests/suites/tracing/auto-instrument/mongodb/test.ts b/packages/node-integration-tests/suites/tracing/auto-instrument/mongodb/test.ts index dd5f88f860ca..76cadd1518dd 100644 --- a/packages/node-integration-tests/suites/tracing/auto-instrument/mongodb/test.ts +++ b/packages/node-integration-tests/suites/tracing/auto-instrument/mongodb/test.ts @@ -1,14 +1,17 @@ -import { MongoMemoryServer } from 'mongodb-memory-server'; +import { MongoMemoryServer } from 'mongodb-memory-server-global'; import { assertSentryTransaction, conditionalTest, getEnvelopeRequest, runServer } from '../../../../utils'; +// This test can take longer. +jest.setTimeout(15000); + conditionalTest({ min: 12 })('MongoDB Test', () => { let mongoServer: MongoMemoryServer; beforeAll(async () => { mongoServer = await MongoMemoryServer.create(); process.env.MONGO_URL = mongoServer.getUri(); - }, 40000); + }, 10000); afterAll(async () => { await mongoServer.stop(); diff --git a/yarn.lock b/yarn.lock index 711e8abfe0d4..69010b40b996 100644 --- a/yarn.lock +++ b/yarn.lock @@ -16286,10 +16286,10 @@ mongodb-memory-server-core@7.6.3: uuid "^8.3.1" yauzl "^2.10.0" -mongodb-memory-server@^7.6.3: +mongodb-memory-server-global@^7.6.3: version "7.6.3" - resolved "https://registry.yarnpkg.com/mongodb-memory-server/-/mongodb-memory-server-7.6.3.tgz#8b2827363ca16aaf250cba07f7a2b49e502735d4" - integrity sha512-yHDE9FGxOpSRUzitF9Qx3JjEgayCSJI3JOW2wgeBH/5PAsUdisy2nRxRiNwwLDooQ7tohllWCRTXlWqyarUEMQ== + resolved "https://registry.yarnpkg.com/mongodb-memory-server-global/-/mongodb-memory-server-global-7.6.3.tgz#ad662a640db254eea7927668834c26b665c13547" + integrity sha512-WLlMqkEasuanHjoxyMxlyvQ/HtJgq0eGyrfCXX6lTnY/26Zfs96W2daeWLOQ48VLInSOh2umBvE74Ykqj7gVyA== dependencies: mongodb-memory-server-core "7.6.3" tslib "^2.3.0" From 9cf57fd86266546f7c11fa37b9d93eccdcb3635d Mon Sep 17 00:00:00 2001 From: Onur Temizkan Date: Thu, 14 Apr 2022 18:20:24 +0300 Subject: [PATCH 052/204] fix(tests): Use non-delayed success endpoints to test `crashed` and `errored` cases (#4937) --- .../suites/sessions/crashed-session-aggregate/test.ts | 2 +- .../suites/sessions/errored-session-aggregate/test.ts | 2 +- packages/node-integration-tests/suites/sessions/server.ts | 3 +-- 3 files changed, 3 insertions(+), 4 deletions(-) diff --git a/packages/node-integration-tests/suites/sessions/crashed-session-aggregate/test.ts b/packages/node-integration-tests/suites/sessions/crashed-session-aggregate/test.ts index 59bb88d4aae7..28ce0ba822c0 100644 --- a/packages/node-integration-tests/suites/sessions/crashed-session-aggregate/test.ts +++ b/packages/node-integration-tests/suites/sessions/crashed-session-aggregate/test.ts @@ -8,7 +8,7 @@ test('should aggregate successful and crashed sessions', async () => { const envelope = await Promise.race([ getEnvelopeRequest(`${url}/success`), getEnvelopeRequest(`${url}/error_unhandled`), - getEnvelopeRequest(`${url}/success_slow`), + getEnvelopeRequest(`${url}/success_next`), ]); expect(envelope).toHaveLength(3); diff --git a/packages/node-integration-tests/suites/sessions/errored-session-aggregate/test.ts b/packages/node-integration-tests/suites/sessions/errored-session-aggregate/test.ts index e26cafa8a586..ad1cf46f2704 100644 --- a/packages/node-integration-tests/suites/sessions/errored-session-aggregate/test.ts +++ b/packages/node-integration-tests/suites/sessions/errored-session-aggregate/test.ts @@ -6,7 +6,7 @@ test('should aggregate successful, crashed and erroneous sessions', async () => const url = await runServer(__dirname, `${path.resolve(__dirname, '..')}/server.ts`); const envelope = await Promise.race([ - getEnvelopeRequest(`${url}/success_slow`), + getEnvelopeRequest(`${url}/success`), getEnvelopeRequest(`${url}/error_handled`), getEnvelopeRequest(`${url}/error_unhandled`), ]); diff --git a/packages/node-integration-tests/suites/sessions/server.ts b/packages/node-integration-tests/suites/sessions/server.ts index 89b9a0e521ea..a718c07d16c1 100644 --- a/packages/node-integration-tests/suites/sessions/server.ts +++ b/packages/node-integration-tests/suites/sessions/server.ts @@ -24,8 +24,7 @@ clearInterval(flusherIntervalId); // @ts-ignore: need access to `_intervalId` flusherIntervalId = flusher?._intervalId = setInterval(() => flusher?.flush(), 1000); -// @ts-ignore: need access to `_intervalId` again -setTimeout(() => clearInterval(flusherIntervalId), 3000); +setTimeout(() => clearInterval(flusherIntervalId), 2000); app.get('/test/success', (_req, res) => { res.send('Success!'); From 9f3253d2355936c4e4ee9e7d9b30f2c6ed13268d Mon Sep 17 00:00:00 2001 From: Lukas Stracke Date: Thu, 14 Apr 2022 17:25:57 +0200 Subject: [PATCH 053/204] ref(utils): Remove `forget` async utility function (#4941) remove the `forget()` function from Utils as it is a rather unnecessary abstraction around logging a rejected Promise via console.error and it bloats the bundle --- packages/browser/src/transports/utils.ts | 16 ++++++-------- .../src/integrations/utils/errorhandling.ts | 9 ++++---- packages/utils/src/async.ts | 12 ---------- packages/utils/src/index.ts | 1 - packages/utils/test/async.test.ts | 22 ------------------- 5 files changed, 12 insertions(+), 48 deletions(-) delete mode 100644 packages/utils/src/async.ts delete mode 100644 packages/utils/test/async.test.ts diff --git a/packages/browser/src/transports/utils.ts b/packages/browser/src/transports/utils.ts index 5fc614d6b86f..5a8e17b42019 100644 --- a/packages/browser/src/transports/utils.ts +++ b/packages/browser/src/transports/utils.ts @@ -1,4 +1,4 @@ -import { forget, getGlobalObject, isNativeFetch, logger, supportsFetch } from '@sentry/utils'; +import { getGlobalObject, isNativeFetch, logger, supportsFetch } from '@sentry/utils'; import { IS_DEBUG_BUILD } from '../flags'; @@ -98,13 +98,11 @@ export function sendReport(url: string, body: string): void { if (supportsFetch()) { const fetch = getNativeFetchImplementation(); - return forget( - fetch(url, { - body, - method: 'POST', - credentials: 'omit', - keepalive: true, - }), - ); + fetch(url, { + body, + method: 'POST', + credentials: 'omit', + keepalive: true, + }).then(null, error => logger.error(error)); } } diff --git a/packages/node/src/integrations/utils/errorhandling.ts b/packages/node/src/integrations/utils/errorhandling.ts index aa79eebcaee8..587840d040b5 100644 --- a/packages/node/src/integrations/utils/errorhandling.ts +++ b/packages/node/src/integrations/utils/errorhandling.ts @@ -1,5 +1,5 @@ import { getCurrentHub } from '@sentry/core'; -import { forget, logger } from '@sentry/utils'; +import { logger } from '@sentry/utils'; import { NodeClient } from '../../client'; import { IS_DEBUG_BUILD } from '../../flags'; @@ -24,12 +24,13 @@ export function logAndExitProcess(error: Error): void { const timeout = (options && options.shutdownTimeout && options.shutdownTimeout > 0 && options.shutdownTimeout) || DEFAULT_SHUTDOWN_TIMEOUT; - forget( - client.close(timeout).then((result: boolean) => { + client.close(timeout).then( + (result: boolean) => { if (!result) { IS_DEBUG_BUILD && logger.warn('We reached the timeout for emptying the request buffer, still exiting now!'); } global.process.exit(1); - }), + }, + error => logger.error(error), ); } diff --git a/packages/utils/src/async.ts b/packages/utils/src/async.ts deleted file mode 100644 index e811fe25c4a6..000000000000 --- a/packages/utils/src/async.ts +++ /dev/null @@ -1,12 +0,0 @@ -/** - * Consumes the promise and logs the error when it rejects. - * @param promise A promise to forget. - */ -// eslint-disable-next-line @typescript-eslint/no-explicit-any -export function forget(promise: PromiseLike): void { - void promise.then(null, e => { - // TODO: Use a better logging mechanism - // eslint-disable-next-line no-console - console.error(e); - }); -} diff --git a/packages/utils/src/index.ts b/packages/utils/src/index.ts index 4d33eeab55e3..0f004218c052 100644 --- a/packages/utils/src/index.ts +++ b/packages/utils/src/index.ts @@ -1,4 +1,3 @@ -export * from './async'; export * from './browser'; export * from './dsn'; export * from './error'; diff --git a/packages/utils/test/async.test.ts b/packages/utils/test/async.test.ts deleted file mode 100644 index d4edbeca7129..000000000000 --- a/packages/utils/test/async.test.ts +++ /dev/null @@ -1,22 +0,0 @@ -import { forget } from '../src/async'; - -describe('forget', () => { - const console = { - error: jest.fn(), - log: jest.fn(), - }; - - beforeEach(() => { - global.console = console as any as Console; - }); - - test('logs rejections to console.error', done => { - const error = new Error(); - forget(Promise.reject(error)); - - setImmediate(() => { - expect(console.error).toHaveBeenCalledWith(error); - done(); - }); - }); -}); From 1660047caa0d65e874a4320d1500c90bef599e30 Mon Sep 17 00:00:00 2001 From: Alberto Leal Date: Thu, 14 Apr 2022 12:41:34 -0400 Subject: [PATCH 054/204] feat(tracing): Add GB unit to device memory tag value (#4935) Since tag values are always strings, it'll be useful to attach units to the `deviceMemory` tag. Since `navigator.deviceMemory` are approximate in gigabytes, we can suffix the tag value with " GB". Reference: https://developer.mozilla.org/en-US/docs/Web/API/Navigator/deviceMemory --- packages/tracing/src/browser/metrics.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/tracing/src/browser/metrics.ts b/packages/tracing/src/browser/metrics.ts index dffc39abb088..97d28806096d 100644 --- a/packages/tracing/src/browser/metrics.ts +++ b/packages/tracing/src/browser/metrics.ts @@ -211,7 +211,7 @@ export class MetricsInstrumentation { } if (isMeasurementValue(navigator.deviceMemory)) { - transaction.setTag('deviceMemory', String(navigator.deviceMemory)); + transaction.setTag('deviceMemory', `${navigator.deviceMemory} GB`); } if (isMeasurementValue(navigator.hardwareConcurrency)) { From 3f43c39e8de03dc1d4c010bdad576a772a1e12ab Mon Sep 17 00:00:00 2001 From: Katie Byers Date: Thu, 14 Apr 2022 11:52:05 -0700 Subject: [PATCH 055/204] fix(build): Cache gatsby plugin types files in CI (#4944) This is a follow up to https://github.com/getsentry/sentry-javascript/pull/4928, which added the creation of types files for the code in the gatsby SDK which lives outside of `src/`. One change which was missed was to have GHA include these new files in the build cache, so that they are available when tests are run. This fixes that oversight. --- .github/workflows/build.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index bfefea5392c5..28895bde0cb1 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -28,6 +28,7 @@ env: ${{ github.workspace }}/packages/**/esm ${{ github.workspace }}/packages/ember/*.d.ts ${{ github.workspace }}/packages/ember/instance-initializers + ${{ github.workspace }}/packages/gatsby/*.d.ts ${{ github.workspace }}/packages/serverless/dist-awslambda-layer/*.zip BUILD_CACHE_KEY: ${{ github.event.inputs.commit || github.sha }} From d62c758c44ecdacb16a1d7ac121c01efff4c0dcf Mon Sep 17 00:00:00 2001 From: Katie Byers Date: Thu, 14 Apr 2022 13:30:23 -0700 Subject: [PATCH 056/204] fix(tests): Fix type errors in tests (#4908) In the process of updating our jest dependencies, a significant number of type errors in tests suddenly appeared. (It seems newer versions of `jest` et al are stricter than the ones we had been using.) This fixes those errors, and changes our jest config so that it will throw an error on any types problems in a test, so we'll know to fix them locally. (Still to do: Fix our configuration so that the linter will catch the errors, so we don't have to actually run the tests to find them.) --- jest.config.js | 1 - .../unit/integrations/linkederrors.test.ts | 14 ++- .../test/unit/mocks/simpletransport.ts | 1 + .../browser/test/unit/transports/base.test.ts | 6 +- .../test/unit/transports/new-xhr.test.ts | 10 +- packages/core/test/lib/base.test.ts | 2 +- packages/core/test/mocks/client.ts | 2 +- packages/gatsby/test/gatsby-browser.test.ts | 4 +- packages/gatsby/test/gatsby-node.test.ts | 6 +- packages/gatsby/test/setEnvVars.ts | 4 + packages/hub/test/global.test.ts | 4 + packages/hub/test/hub.test.ts | 92 ++++++++++++++----- packages/hub/test/scope.test.ts | 22 ++--- .../integrations/test/captureconsole.test.ts | 54 +++++------ packages/integrations/test/debug.test.ts | 1 + packages/integrations/test/dedupe.test.ts | 14 ++- .../integrations/test/extraerrordata.test.ts | 4 +- packages/integrations/test/offline.test.ts | 2 +- .../test/reportingobserver.test.ts | 45 ++++----- .../integrations/test/rewriteframes.test.ts | 6 -- packages/integrations/tsconfig.test.json | 2 +- packages/node/test/integrations/http.test.ts | 2 +- packages/node/test/transports/http.test.ts | 1 + packages/serverless/src/awslambda.ts | 2 +- packages/serverless/src/gcpfunction/http.ts | 2 +- .../serverless/test/google-cloud-http.test.ts | 1 + .../test/browser/browsertracing.test.ts | 4 +- packages/tracing/test/browser/metrics.test.ts | 12 +-- packages/tracing/test/browser/request.test.ts | 38 ++++---- packages/tracing/test/browser/router.test.ts | 3 +- packages/tracing/test/errors.test.ts | 15 +-- packages/tracing/test/idletransaction.test.ts | 8 +- packages/utils/src/instrument.ts | 4 +- packages/utils/test/clientreport.test.ts | 2 +- packages/utils/test/syncpromise.test.ts | 22 ++--- 35 files changed, 243 insertions(+), 169 deletions(-) diff --git a/jest.config.js b/jest.config.js index 208a643187f4..8b7b7f1f5b7c 100644 --- a/jest.config.js +++ b/jest.config.js @@ -11,7 +11,6 @@ module.exports = { globals: { 'ts-jest': { tsconfig: '/tsconfig.test.json', - diagnostics: false, }, }, testPathIgnorePatterns: ['/build/', '/node_modules/'], diff --git a/packages/browser/test/unit/integrations/linkederrors.test.ts b/packages/browser/test/unit/integrations/linkederrors.test.ts index 6650877ff39b..6ccd4b5975a3 100644 --- a/packages/browser/test/unit/integrations/linkederrors.test.ts +++ b/packages/browser/test/unit/integrations/linkederrors.test.ts @@ -1,4 +1,4 @@ -import { ExtendedError } from '@sentry/types'; +import { Event as SentryEvent, Exception, ExtendedError } from '@sentry/types'; import { createStackParser } from '@sentry/utils'; import { BrowserClient } from '../../../src/client'; @@ -8,6 +8,12 @@ import { setupBrowserTransport } from '../../../src/transports'; const parser = createStackParser(...defaultStackParsers); +type EventWithException = SentryEvent & { + exception: { + values: Exception[]; + }; +}; + describe('LinkedErrors', () => { describe('handler', () => { it('should bail out if event does not contain exception', () => { @@ -44,7 +50,7 @@ describe('LinkedErrors', () => { return client.eventFromException(originalException).then(event => { const result = LinkedErrorsModule._handler(parser, 'cause', 5, event, { originalException, - }); + }) as EventWithException; // It shouldn't include root exception, as it's already processed in the event by the main error handler expect(result.exception.values.length).toBe(3); @@ -75,7 +81,7 @@ describe('LinkedErrors', () => { return client.eventFromException(originalException).then(event => { const result = LinkedErrorsModule._handler(parser, 'reason', 5, event, { originalException, - }); + }) as EventWithException; expect(result.exception.values.length).toBe(3); expect(result.exception.values[0].type).toBe('SyntaxError'); @@ -103,7 +109,7 @@ describe('LinkedErrors', () => { return client.eventFromException(originalException).then(event => { const result = LinkedErrorsModule._handler(parser, 'cause', 2, event, { originalException, - }); + }) as EventWithException; expect(result.exception.values.length).toBe(2); expect(result.exception.values[0].type).toBe('TypeError'); diff --git a/packages/browser/test/unit/mocks/simpletransport.ts b/packages/browser/test/unit/mocks/simpletransport.ts index 7d0b0e9498df..398feb1a3e6a 100644 --- a/packages/browser/test/unit/mocks/simpletransport.ts +++ b/packages/browser/test/unit/mocks/simpletransport.ts @@ -3,6 +3,7 @@ import { eventStatusFromHttpCode, resolvedSyncPromise } from '@sentry/utils'; import { Event, Response } from '../../../src'; import { BaseTransport } from '../../../src/transports'; +// @ts-ignore It's okay that we're not implementing the `_sendRequest()` method because we don't use it in our tests export class SimpleTransport extends BaseTransport { public sendEvent(_: Event): PromiseLike { return this._buffer.add(() => diff --git a/packages/browser/test/unit/transports/base.test.ts b/packages/browser/test/unit/transports/base.test.ts index 2f9fc26d07be..9df498352c1e 100644 --- a/packages/browser/test/unit/transports/base.test.ts +++ b/packages/browser/test/unit/transports/base.test.ts @@ -3,6 +3,8 @@ import { BaseTransport } from '../../../src/transports/base'; const testDsn = 'https://123@sentry.io/42'; const envelopeEndpoint = 'https://sentry.io/api/42/envelope/?sentry_key=123&sentry_version=7'; +// @ts-ignore We're purposely not implementing the methods of the abstract `BaseTransport` class in order to be able to +// assert on what the class provides and what it leaves to the concrete class to implement class SimpleTransport extends BaseTransport {} describe('BaseTransport', () => { @@ -111,12 +113,12 @@ describe('BaseTransport', () => { }); }); - it('doesnt provide sendEvent() implementation', () => { + it('doesnt provide sendEvent() implementation', async () => { expect.assertions(1); const transport = new SimpleTransport({ dsn: testDsn }); try { - void transport.sendEvent({}); + await transport.sendEvent({}); } catch (e) { expect(e).toBeDefined(); } diff --git a/packages/browser/test/unit/transports/new-xhr.test.ts b/packages/browser/test/unit/transports/new-xhr.test.ts index 5b3bbda313c7..603b0f6037dc 100644 --- a/packages/browser/test/unit/transports/new-xhr.test.ts +++ b/packages/browser/test/unit/transports/new-xhr.test.ts @@ -27,7 +27,7 @@ function createXHRMock() { case 'Retry-After': return '10'; case `${retryAfterSeconds}`: - return; + return null; default: return `${retryAfterSeconds}:error:scope`; } @@ -57,7 +57,7 @@ describe('NewXHRTransport', () => { expect(xhrMock.setRequestHeader).toHaveBeenCalledTimes(0); expect(xhrMock.send).toHaveBeenCalledTimes(0); - await Promise.all([transport.send(ERROR_ENVELOPE), (xhrMock as XMLHttpRequest).onreadystatechange(null)]); + await Promise.all([transport.send(ERROR_ENVELOPE), (xhrMock as XMLHttpRequest).onreadystatechange!({} as Event)]); expect(xhrMock.open).toHaveBeenCalledTimes(1); expect(xhrMock.open).toHaveBeenCalledWith('POST', DEFAULT_XHR_TRANSPORT_OPTIONS.url); @@ -70,7 +70,7 @@ describe('NewXHRTransport', () => { const [res] = await Promise.all([ transport.send(ERROR_ENVELOPE), - (xhrMock as XMLHttpRequest).onreadystatechange(null), + (xhrMock as XMLHttpRequest).onreadystatechange!({} as Event), ]); expect(res).toBeDefined(); @@ -80,7 +80,7 @@ describe('NewXHRTransport', () => { it('sets rate limit response headers', async () => { const transport = makeNewXHRTransport(DEFAULT_XHR_TRANSPORT_OPTIONS); - await Promise.all([transport.send(ERROR_ENVELOPE), (xhrMock as XMLHttpRequest).onreadystatechange(null)]); + await Promise.all([transport.send(ERROR_ENVELOPE), (xhrMock as XMLHttpRequest).onreadystatechange!({} as Event)]); expect(xhrMock.getResponseHeader).toHaveBeenCalledTimes(2); expect(xhrMock.getResponseHeader).toHaveBeenCalledWith('X-Sentry-Rate-Limits'); @@ -99,7 +99,7 @@ describe('NewXHRTransport', () => { }; const transport = makeNewXHRTransport(options); - await Promise.all([transport.send(ERROR_ENVELOPE), (xhrMock as XMLHttpRequest).onreadystatechange(null)]); + await Promise.all([transport.send(ERROR_ENVELOPE), (xhrMock as XMLHttpRequest).onreadystatechange!({} as Event)]); expect(xhrMock.setRequestHeader).toHaveBeenCalledTimes(3); expect(xhrMock.setRequestHeader).toHaveBeenCalledWith('referrerPolicy', headers.referrerPolicy); diff --git a/packages/core/test/lib/base.test.ts b/packages/core/test/lib/base.test.ts index 72c0ef63c211..1bd2750e5378 100644 --- a/packages/core/test/lib/base.test.ts +++ b/packages/core/test/lib/base.test.ts @@ -69,7 +69,7 @@ describe('BaseClient', () => { const options = { dsn: PUBLIC_DSN }; const client = new TestClient(options, setupTestTransport(options).transport); - expect(dsnToString(client.getDsn())).toBe(PUBLIC_DSN); + expect(dsnToString(client.getDsn()!)).toBe(PUBLIC_DSN); }); test('allows missing Dsn', () => { diff --git a/packages/core/test/mocks/client.ts b/packages/core/test/mocks/client.ts index 2a49f9fea757..707513ff91b1 100644 --- a/packages/core/test/mocks/client.ts +++ b/packages/core/test/mocks/client.ts @@ -79,7 +79,7 @@ export function setupTestTransport(options: TestOptions): { transport: Transport const transportOptions = options.transportOptions ? options.transportOptions : { dsn: options.dsn }; if (options.transport) { - return { transport: new this._options.transport(transportOptions) }; + return { transport: new options.transport(transportOptions) }; } return noop; diff --git a/packages/gatsby/test/gatsby-browser.test.ts b/packages/gatsby/test/gatsby-browser.test.ts index a2044dd2c8a9..cfeb9d227a88 100644 --- a/packages/gatsby/test/gatsby-browser.test.ts +++ b/packages/gatsby/test/gatsby-browser.test.ts @@ -1,7 +1,7 @@ /* eslint-disable @typescript-eslint/no-var-requires */ /* eslint-disable @typescript-eslint/no-explicit-any */ -const { onClientEntry } = require('../gatsby-browser'); +import { onClientEntry } from '../gatsby-browser'; (global as any).__SENTRY_RELEASE__ = '683f3a6ab819d47d23abfca9a914c81f0524d35b'; (global as any).__SENTRY_DSN__ = 'https://examplePublicKey@o0.ingest.sentry.io/0'; @@ -153,7 +153,7 @@ describe('onClientEntry', () => { // Run this last to check for any test side effects it('does not run if plugin params are undefined', () => { - onClientEntry(); + onClientEntry(undefined, undefined); expect(sentryInit).toHaveBeenCalledTimes(0); expect(tracingAddExtensionMethods).toHaveBeenCalledTimes(0); }); diff --git a/packages/gatsby/test/gatsby-node.test.ts b/packages/gatsby/test/gatsby-node.test.ts index f09f8e5be781..8880c133b90d 100644 --- a/packages/gatsby/test/gatsby-node.test.ts +++ b/packages/gatsby/test/gatsby-node.test.ts @@ -1,6 +1,6 @@ /* eslint-disable @typescript-eslint/no-var-requires */ /* eslint-disable @typescript-eslint/no-explicit-any */ -const { onCreateWebpackConfig } = require('../gatsby-node'); +import { onCreateWebpackConfig } from '../gatsby-node'; describe('onCreateWebpackConfig', () => { it('sets a webpack config', () => { @@ -12,7 +12,9 @@ describe('onCreateWebpackConfig', () => { setWebpackConfig: jest.fn(), }; - onCreateWebpackConfig({ plugins, actions }); + const getConfig = jest.fn(); + + onCreateWebpackConfig({ plugins, actions, getConfig }); expect(plugins.define).toHaveBeenCalledTimes(1); expect(plugins.define).toHaveBeenLastCalledWith({ diff --git a/packages/gatsby/test/setEnvVars.ts b/packages/gatsby/test/setEnvVars.ts index c97579e924e7..bc9d45b9c84a 100644 --- a/packages/gatsby/test/setEnvVars.ts +++ b/packages/gatsby/test/setEnvVars.ts @@ -1,2 +1,6 @@ // eslint-disable-next-line @typescript-eslint/no-explicit-any, @typescript-eslint/no-unsafe-member-access process.env.SENTRY_RELEASE = '14abbb1678a2eb59d1a171ea33d630dd6c6eee70'; + +// This file needs to have an import or an export to count as a module, which is necessary when using +// the `isolatedModules` tsconfig option. +export const _ = ''; diff --git a/packages/hub/test/global.test.ts b/packages/hub/test/global.test.ts index ec63679d5592..8dc32acb7fc7 100644 --- a/packages/hub/test/global.test.ts +++ b/packages/hub/test/global.test.ts @@ -1,5 +1,9 @@ +import { getGlobalObject } from '@sentry/utils'; + import { getCurrentHub, getHubFromCarrier, Hub } from '../src'; +const global = getGlobalObject(); + describe('global', () => { test('getGlobalHub', () => { expect(getCurrentHub()).toBeTruthy(); diff --git a/packages/hub/test/hub.test.ts b/packages/hub/test/hub.test.ts index 98c8dcea0636..6754ac258669 100644 --- a/packages/hub/test/hub.test.ts +++ b/packages/hub/test/hub.test.ts @@ -1,4 +1,5 @@ -import { Event } from '@sentry/types'; +/* eslint-disable @typescript-eslint/unbound-method */ +import { Client, Event } from '@sentry/types'; import { getCurrentHub, Hub, Scope } from '../src'; @@ -15,7 +16,18 @@ function makeClient() { getIntegration: jest.fn(), setupIntegrations: jest.fn(), captureMessage: jest.fn(), - }; + } as unknown as Client; +} + +/** + * Return an array containing the arguments passed to the given mocked or spied-upon function. + * + * By default, the args passed to the first call of the function are returned, but it is also possible to retrieve the + * nth call by passing `callIndex`. If the function wasn't called, an error message is returned instead. + */ +function getPassedArgs(mock: (...args: any[]) => any, callIndex: number = 0): any[] { + const asMock = mock as jest.MockedFunction<(...args: any[]) => any>; + return asMock.mock.calls[callIndex] || ["Error: Function wasn't called."]; } describe('Hub', () => { @@ -102,7 +114,7 @@ describe('Hub', () => { }); }); - test('inherit processors', () => { + test('inherit processors', async () => { expect.assertions(1); const event: Event = { extra: { b: 3 }, @@ -210,34 +222,45 @@ describe('Hub', () => { test('simple', () => { const testClient = makeClient(); const hub = new Hub(testClient); + hub.captureException('a'); - expect(testClient.captureException).toHaveBeenCalled(); - expect(testClient.captureException.mock.calls[0][0]).toBe('a'); + const args = getPassedArgs(testClient.captureException); + + expect(args[0]).toBe('a'); }); test('should set event_id in hint', () => { const testClient = makeClient(); const hub = new Hub(testClient); + hub.captureException('a'); - expect(testClient.captureException.mock.calls[0][1].event_id).toBeTruthy(); + const args = getPassedArgs(testClient.captureException); + + expect(args[1].event_id).toBeTruthy(); }); test('should keep event_id from hint', () => { const testClient = makeClient(); const hub = new Hub(testClient); const id = Math.random().toString(); + hub.captureException('a', { event_id: id }); - expect(testClient.captureException.mock.calls[0][1].event_id).toBe(id); + const args = getPassedArgs(testClient.captureException); + + expect(args[1].event_id).toBe(id); }); test('should generate hint if not provided in the call', () => { const testClient = makeClient(); const hub = new Hub(testClient); const ex = new Error('foo'); + hub.captureException(ex); - expect(testClient.captureException.mock.calls[0][1].originalException).toBe(ex); - expect(testClient.captureException.mock.calls[0][1].syntheticException).toBeInstanceOf(Error); - expect(testClient.captureException.mock.calls[0][1].syntheticException.message).toBe('Sentry syntheticException'); + const args = getPassedArgs(testClient.captureException); + + expect(args[1].originalException).toBe(ex); + expect(args[1].syntheticException).toBeInstanceOf(Error); + expect(args[1].syntheticException.message).toBe('Sentry syntheticException'); }); }); @@ -245,32 +268,44 @@ describe('Hub', () => { test('simple', () => { const testClient = makeClient(); const hub = new Hub(testClient); + hub.captureMessage('a'); - expect(testClient.captureMessage.mock.calls[0][0]).toBe('a'); + const args = getPassedArgs(testClient.captureMessage); + + expect(args[0]).toBe('a'); }); test('should set event_id in hint', () => { const testClient = makeClient(); const hub = new Hub(testClient); + hub.captureMessage('a'); - expect(testClient.captureMessage.mock.calls[0][2].event_id).toBeTruthy(); + const args = getPassedArgs(testClient.captureMessage); + + expect(args[2].event_id).toBeTruthy(); }); test('should keep event_id from hint', () => { const testClient = makeClient(); const hub = new Hub(testClient); const id = Math.random().toString(); + hub.captureMessage('a', undefined, { event_id: id }); - expect(testClient.captureMessage.mock.calls[0][2].event_id).toBe(id); + const args = getPassedArgs(testClient.captureMessage); + + expect(args[2].event_id).toBe(id); }); test('should generate hint if not provided in the call', () => { const testClient = makeClient(); const hub = new Hub(testClient); + hub.captureMessage('foo'); - expect(testClient.captureMessage.mock.calls[0][2].originalException).toBe('foo'); - expect(testClient.captureMessage.mock.calls[0][2].syntheticException).toBeInstanceOf(Error); - expect(testClient.captureMessage.mock.calls[0][2].syntheticException.message).toBe('foo'); + const args = getPassedArgs(testClient.captureMessage); + + expect(args[2].originalException).toBe('foo'); + expect(args[2].syntheticException).toBeInstanceOf(Error); + expect(args[2].syntheticException.message).toBe('foo'); }); }); @@ -281,8 +316,11 @@ describe('Hub', () => { }; const testClient = makeClient(); const hub = new Hub(testClient); + hub.captureEvent(event); - expect(testClient.captureEvent.mock.calls[0][0]).toBe(event); + const args = getPassedArgs(testClient.captureEvent); + + expect(args[0]).toBe(event); }); test('should set event_id in hint', () => { @@ -291,8 +329,11 @@ describe('Hub', () => { }; const testClient = makeClient(); const hub = new Hub(testClient); + hub.captureEvent(event); - expect(testClient.captureEvent.mock.calls[0][1].event_id).toBeTruthy(); + const args = getPassedArgs(testClient.captureEvent); + + expect(args[1].event_id).toBeTruthy(); }); test('should keep event_id from hint', () => { @@ -302,8 +343,11 @@ describe('Hub', () => { const testClient = makeClient(); const hub = new Hub(testClient); const id = Math.random().toString(); + hub.captureEvent(event, { event_id: id }); - expect(testClient.captureEvent.mock.calls[0][1].event_id).toBe(id); + const args = getPassedArgs(testClient.captureEvent); + + expect(args[1].event_id).toBe(id); }); test('sets lastEventId', () => { @@ -312,8 +356,11 @@ describe('Hub', () => { }; const testClient = makeClient(); const hub = new Hub(testClient); + hub.captureEvent(event); - expect(testClient.captureEvent.mock.calls[0][1].event_id).toEqual(hub.lastEventId()); + const args = getPassedArgs(testClient.captureEvent); + + expect(args[1].event_id).toEqual(hub.lastEventId()); }); test('transactions do not set lastEventId', () => { @@ -323,8 +370,11 @@ describe('Hub', () => { }; const testClient = makeClient(); const hub = new Hub(testClient); + hub.captureEvent(event); - expect(testClient.captureEvent.mock.calls[0][1].event_id).not.toEqual(hub.lastEventId()); + const args = getPassedArgs(testClient.captureEvent); + + expect(args[1].event_id).not.toEqual(hub.lastEventId()); }); }); diff --git a/packages/hub/test/scope.test.ts b/packages/hub/test/scope.test.ts index 7cca748fb6f8..b734ac59fcf6 100644 --- a/packages/hub/test/scope.test.ts +++ b/packages/hub/test/scope.test.ts @@ -1,4 +1,4 @@ -import { Event, EventHint } from '@sentry/types'; +import { Event, EventHint, RequestSessionStatus } from '@sentry/types'; import { getGlobalObject } from '@sentry/utils'; import { addGlobalEventProcessor, Scope } from '../src'; @@ -194,7 +194,7 @@ describe('Scope', () => { }); describe('applyToEvent', () => { - test('basic usage', () => { + test('basic usage', async () => { expect.assertions(9); const scope = new Scope(); @@ -222,7 +222,7 @@ describe('Scope', () => { }); }); - test('merge with existing event data', () => { + test('merge with existing event data', async () => { expect.assertions(8); const scope = new Scope(); scope.setExtra('a', 2); @@ -291,7 +291,7 @@ describe('Scope', () => { }); }); - test('scope level should have priority over event level', () => { + test('scope level should have priority over event level', async () => { expect.assertions(1); const scope = new Scope(); scope.setLevel('warning'); @@ -302,7 +302,7 @@ describe('Scope', () => { }); }); - test('scope transaction should have priority over event transaction', () => { + test('scope transaction should have priority over event transaction', async () => { expect.assertions(1); const scope = new Scope(); scope.setTransactionName('/abc'); @@ -511,10 +511,10 @@ describe('Scope', () => { contexts: { bar: { id: '3' }, baz: { id: '4' } }, extra: { bar: '3', baz: '4' }, fingerprint: ['bar'], - level: 'warning', + level: 'warning' as const, tags: { bar: '3', baz: '4' }, user: { id: '42' }, - requestSession: { status: 'errored' }, + requestSession: { status: 'errored' as RequestSessionStatus }, }; const updatedScope = scope.update(localAttributes) as any; @@ -541,7 +541,7 @@ describe('Scope', () => { }); describe('addEventProcessor', () => { - test('should allow for basic event manipulation', () => { + test('should allow for basic event manipulation', async () => { expect.assertions(3); const event: Event = { extra: { b: 3 }, @@ -566,7 +566,7 @@ describe('Scope', () => { }); }); - test('should work alongside global event processors', () => { + test('should work alongside global event processors', async () => { expect.assertions(3); const event: Event = { extra: { b: 3 }, @@ -667,7 +667,7 @@ describe('Scope', () => { }); }); - test('should drop an event when any of processors return null', () => { + test('should drop an event when any of processors return null', async () => { expect.assertions(1); const event: Event = { extra: { b: 3 }, @@ -680,7 +680,7 @@ describe('Scope', () => { }); }); - test('should have an access to the EventHint', () => { + test('should have an access to the EventHint', async () => { expect.assertions(3); const event: Event = { extra: { b: 3 }, diff --git a/packages/integrations/test/captureconsole.test.ts b/packages/integrations/test/captureconsole.test.ts index bb6cce8110d3..966c76879522 100644 --- a/packages/integrations/test/captureconsole.test.ts +++ b/packages/integrations/test/captureconsole.test.ts @@ -1,4 +1,5 @@ -import { Event, Integration } from '@sentry/types'; +/* eslint-disable @typescript-eslint/unbound-method */ +import { Event, Hub, Integration } from '@sentry/types'; import { CaptureConsole } from '../src/captureconsole'; @@ -16,10 +17,11 @@ const mockHub = { captureException: jest.fn(), }; -const getMockHubWithIntegration = (integration: Integration) => ({ - ...mockHub, - getIntegration: jest.fn(() => integration), -}); +const getMockHubWithIntegration = (integration: Integration) => + ({ + ...mockHub, + getIntegration: jest.fn(() => integration), + } as unknown as Hub); // We're using this to un-monkey patch the console after each test. const originalConsole = Object.assign({}, global.console); @@ -36,7 +38,7 @@ describe('CaptureConsole setup', () => { const captureConsoleIntegration = new CaptureConsole({ levels: ['log', 'warn'] }); captureConsoleIntegration.setupOnce( () => undefined, - () => getMockHubWithIntegration(captureConsoleIntegration) as any, + () => getMockHubWithIntegration(captureConsoleIntegration), ); expect(global.console.error).toBe(originalConsole.error); // not monkey patched @@ -48,7 +50,7 @@ describe('CaptureConsole setup', () => { const captureConsoleIntegration = new CaptureConsole(); captureConsoleIntegration.setupOnce( () => undefined, - () => getMockHubWithIntegration(captureConsoleIntegration) as any, + () => getMockHubWithIntegration(captureConsoleIntegration), ); // expect a set of defined console levels to have been monkey patched @@ -68,7 +70,7 @@ describe('CaptureConsole setup', () => { const captureConsoleIntegration = new CaptureConsole({ levels: [] }); captureConsoleIntegration.setupOnce( () => undefined, - () => getMockHubWithIntegration(captureConsoleIntegration) as any, + () => getMockHubWithIntegration(captureConsoleIntegration), ); // expect the default set of console levels not to have been monkey patched @@ -93,7 +95,7 @@ describe('CaptureConsole setup', () => { const captureConsoleIntegration = new CaptureConsole(); captureConsoleIntegration.setupOnce( () => undefined, - () => getMockHubWithIntegration(captureConsoleIntegration) as any, + () => getMockHubWithIntegration(captureConsoleIntegration), ); }).not.toThrow(); @@ -105,7 +107,7 @@ describe('CaptureConsole setup', () => { const captureConsoleIntegration = new CaptureConsole({ levels: ['error'] }); captureConsoleIntegration.setupOnce( () => undefined, - () => getMockHubWithIntegration(captureConsoleIntegration) as any, + () => getMockHubWithIntegration(captureConsoleIntegration), ); // call a wrapped function @@ -119,7 +121,7 @@ describe('CaptureConsole setup', () => { const captureConsoleIntegration = new CaptureConsole({ levels: ['log'] }); captureConsoleIntegration.setupOnce( () => undefined, - () => getMockHubWithIntegration(captureConsoleIntegration) as any, + () => getMockHubWithIntegration(captureConsoleIntegration), ); // call a wrapped function @@ -135,7 +137,7 @@ describe('CaptureConsole setup', () => { const captureConsoleIntegration = new CaptureConsole({ levels: ['log'] }); captureConsoleIntegration.setupOnce( () => undefined, - () => getMockHubWithIntegration(captureConsoleIntegration) as any, + () => getMockHubWithIntegration(captureConsoleIntegration), ); // call a wrapped function @@ -154,7 +156,7 @@ describe('CaptureConsole setup', () => { const captureConsoleIntegration = new CaptureConsole({ levels: ['assert'] }); captureConsoleIntegration.setupOnce( () => undefined, - () => getMockHubWithIntegration(captureConsoleIntegration) as any, + () => getMockHubWithIntegration(captureConsoleIntegration), ); global.console.assert(1 + 1 === 3); @@ -168,7 +170,7 @@ describe('CaptureConsole setup', () => { const captureConsoleIntegration = new CaptureConsole({ levels: ['assert'] }); captureConsoleIntegration.setupOnce( () => undefined, - () => getMockHubWithIntegration(captureConsoleIntegration) as any, + () => getMockHubWithIntegration(captureConsoleIntegration), ); global.console.assert(1 + 1 === 3, 'expression is false'); @@ -182,7 +184,7 @@ describe('CaptureConsole setup', () => { const captureConsoleIntegration = new CaptureConsole({ levels: ['assert'] }); captureConsoleIntegration.setupOnce( () => undefined, - () => getMockHubWithIntegration(captureConsoleIntegration) as any, + () => getMockHubWithIntegration(captureConsoleIntegration), ); global.console.assert(1 + 1 === 2); @@ -192,7 +194,7 @@ describe('CaptureConsole setup', () => { const captureConsoleIntegration = new CaptureConsole({ levels: ['error'] }); captureConsoleIntegration.setupOnce( () => undefined, - () => getMockHubWithIntegration(captureConsoleIntegration) as any, + () => getMockHubWithIntegration(captureConsoleIntegration), ); const someError = new Error('some error'); @@ -206,7 +208,7 @@ describe('CaptureConsole setup', () => { const captureConsoleIntegration = new CaptureConsole(); captureConsoleIntegration.setupOnce( () => undefined, - () => getMockHubWithIntegration(captureConsoleIntegration) as any, + () => getMockHubWithIntegration(captureConsoleIntegration), ); const someError = new Error('some error'); @@ -220,7 +222,7 @@ describe('CaptureConsole setup', () => { const captureConsoleIntegration = new CaptureConsole(); captureConsoleIntegration.setupOnce( () => undefined, - () => getMockHubWithIntegration(captureConsoleIntegration) as any, + () => getMockHubWithIntegration(captureConsoleIntegration), ); global.console.error('some message'); @@ -233,7 +235,7 @@ describe('CaptureConsole setup', () => { const captureConsoleIntegration = new CaptureConsole({ levels: ['error'] }); captureConsoleIntegration.setupOnce( () => undefined, - () => getMockHubWithIntegration(captureConsoleIntegration) as any, + () => getMockHubWithIntegration(captureConsoleIntegration), ); global.console.error('some non-error message'); @@ -247,7 +249,7 @@ describe('CaptureConsole setup', () => { const captureConsoleIntegration = new CaptureConsole({ levels: ['info'] }); captureConsoleIntegration.setupOnce( () => undefined, - () => getMockHubWithIntegration(captureConsoleIntegration) as any, + () => getMockHubWithIntegration(captureConsoleIntegration), ); global.console.info('some message'); @@ -265,7 +267,7 @@ describe('CaptureConsole setup', () => { const captureConsoleIntegration = new CaptureConsole({ levels: ['log'] }); captureConsoleIntegration.setupOnce( () => undefined, - () => getMockHubWithIntegration(captureConsoleIntegration) as any, + () => getMockHubWithIntegration(captureConsoleIntegration), ); global.console.log('some message 1', 'some message 2'); @@ -281,11 +283,11 @@ describe('CaptureConsole setup', () => { const captureConsoleIntegration = new CaptureConsole({ levels: ['log', 'someNonExistingLevel', 'error'] }); captureConsoleIntegration.setupOnce( () => undefined, - () => getMockHubWithIntegration(captureConsoleIntegration) as any, + () => getMockHubWithIntegration(captureConsoleIntegration), ); // The provided level should not be created - expect(global.console['someNonExistingLevel']).toBeUndefined(); + expect((global.console as any)['someNonExistingLevel']).toBeUndefined(); // Ohter levels should be wrapped as expected expect(global.console.log).not.toBe(originalConsole.log); @@ -296,7 +298,7 @@ describe('CaptureConsole setup', () => { const captureConsoleIntegration = new CaptureConsole({ levels: ['log', 'error'] }); captureConsoleIntegration.setupOnce( () => undefined, - () => getMockHubWithIntegration(null) as any, // simulate not having the integration registered + () => getMockHubWithIntegration(null as any), // simulate not having the integration registered ); // Console should be wrapped @@ -310,12 +312,12 @@ describe('CaptureConsole setup', () => { it("should not crash when the original console methods don't exist at time of invocation", () => { const originalConsoleLog = global.console.log; - global.console.log = undefined; // don't `delete` here, otherwise `fill` won't wrap the function + global.console.log = undefined as any; // don't `delete` here, otherwise `fill` won't wrap the function const captureConsoleIntegration = new CaptureConsole({ levels: ['log'] }); captureConsoleIntegration.setupOnce( () => undefined, - () => getMockHubWithIntegration(captureConsoleIntegration) as any, + () => getMockHubWithIntegration(captureConsoleIntegration), ); expect(() => { diff --git a/packages/integrations/test/debug.test.ts b/packages/integrations/test/debug.test.ts index 5c2f84f5d1e6..768c9606a1fb 100644 --- a/packages/integrations/test/debug.test.ts +++ b/packages/integrations/test/debug.test.ts @@ -8,6 +8,7 @@ const mockGetCurrentHub = (getIntegrationResult: Integration) => ({ // Replace console log with a mock so we can check for invocations const mockConsoleLog = jest.fn(); +// eslint-disable-next-line @typescript-eslint/unbound-method const originalConsoleLog = global.console.log; global.console.log = mockConsoleLog; diff --git a/packages/integrations/test/dedupe.test.ts b/packages/integrations/test/dedupe.test.ts index c56ab59d5b82..8bc354ffa620 100644 --- a/packages/integrations/test/dedupe.test.ts +++ b/packages/integrations/test/dedupe.test.ts @@ -1,13 +1,21 @@ -import { Event } from '@sentry/types'; +import { Event as SentryEvent, Exception, StackFrame, Stacktrace } from '@sentry/types'; import { _shouldDropEvent } from '../src/dedupe'; +type EventWithException = SentryEvent & { + exception: { + values: ExceptionWithStacktrace[]; + }; +}; +type ExceptionWithStacktrace = Exception & { stacktrace: StacktraceWithFrames }; +type StacktraceWithFrames = Stacktrace & { frames: StackFrame[] }; + /** JSDoc */ function clone(data: T): T { return JSON.parse(JSON.stringify(data)); } -const messageEvent: Event = { +const messageEvent: EventWithException = { fingerprint: ['MrSnuffles'], message: 'PickleRick', exception: { @@ -34,7 +42,7 @@ const messageEvent: Event = { ], }, }; -const exceptionEvent: Event = { +const exceptionEvent: EventWithException = { exception: { values: [ { diff --git a/packages/integrations/test/extraerrordata.test.ts b/packages/integrations/test/extraerrordata.test.ts index ed76a2dc75ca..68e38720f761 100644 --- a/packages/integrations/test/extraerrordata.test.ts +++ b/packages/integrations/test/extraerrordata.test.ts @@ -71,7 +71,7 @@ describe('ExtraErrorData()', () => { event = { // @ts-ignore Allow contexts on event contexts: { - foo: 42, + foo: { bar: 42 }, }, }; const error = new TypeError('foo') as ExtendedError; @@ -85,7 +85,7 @@ describe('ExtraErrorData()', () => { TypeError: { baz: 42, }, - foo: 42, + foo: { bar: 42 }, }); }); diff --git a/packages/integrations/test/offline.test.ts b/packages/integrations/test/offline.test.ts index 1f728d835abd..9c4eb8ad2e36 100644 --- a/packages/integrations/test/offline.test.ts +++ b/packages/integrations/test/offline.test.ts @@ -169,7 +169,7 @@ function initIntegration(options: { maxStoredEvents?: number } = {}): void { jest.spyOn(utils, 'getGlobalObject').mockImplementation( () => ({ - addEventListener: (_windowEvent, callback) => { + addEventListener: (_windowEvent: any, callback: any) => { eventListeners.push(callback); }, navigator: { diff --git a/packages/integrations/test/reportingobserver.test.ts b/packages/integrations/test/reportingobserver.test.ts index 1d6417e8ccc7..91547d5572f8 100644 --- a/packages/integrations/test/reportingobserver.test.ts +++ b/packages/integrations/test/reportingobserver.test.ts @@ -1,4 +1,4 @@ -import { Integration } from '@sentry/types'; +import { Hub, Integration } from '@sentry/types'; import { ReportingObserver } from '../src/reportingobserver'; @@ -13,10 +13,11 @@ const mockHub = { captureMessage: jest.fn(), }; -const getMockHubWithIntegration = (integration: Integration) => ({ - ...mockHub, - getIntegration: jest.fn(() => integration), -}); +const getMockHubWithIntegration = (integration: Integration) => + ({ + ...mockHub, + getIntegration: jest.fn(() => integration), + } as unknown as Hub); const mockReportingObserverConstructor = jest.fn(); const mockObserve = jest.fn(); @@ -49,7 +50,7 @@ describe('ReportingObserver', () => { expect(() => { reportingObserverIntegration.setupOnce( () => undefined, - () => getMockHubWithIntegration(null) as any, + () => getMockHubWithIntegration(null as any), ); }).not.toThrow(); @@ -61,7 +62,7 @@ describe('ReportingObserver', () => { const reportingObserverIntegration = new ReportingObserver(); reportingObserverIntegration.setupOnce( () => undefined, - () => getMockHubWithIntegration(reportingObserverIntegration) as any, + () => getMockHubWithIntegration(reportingObserverIntegration), ); expect(mockReportingObserverConstructor).toHaveBeenCalledTimes(1); @@ -75,7 +76,7 @@ describe('ReportingObserver', () => { const reportingObserverIntegration = new ReportingObserver({ types: ['crash'] }); reportingObserverIntegration.setupOnce( () => undefined, - () => getMockHubWithIntegration(reportingObserverIntegration) as any, + () => getMockHubWithIntegration(reportingObserverIntegration), ); expect(mockReportingObserverConstructor).toHaveBeenCalledTimes(1); @@ -89,7 +90,7 @@ describe('ReportingObserver', () => { const reportingObserverIntegration = new ReportingObserver(); reportingObserverIntegration.setupOnce( () => undefined, - () => getMockHubWithIntegration(reportingObserverIntegration) as any, + () => getMockHubWithIntegration(reportingObserverIntegration), ); expect(mockReportingObserverConstructor).toHaveBeenCalledTimes(1); @@ -103,7 +104,7 @@ describe('ReportingObserver', () => { const reportingObserverIntegration = new ReportingObserver(); reportingObserverIntegration.setupOnce( () => undefined, - () => getMockHubWithIntegration(reportingObserverIntegration) as any, + () => getMockHubWithIntegration(reportingObserverIntegration), ); expect(mockObserve).toHaveBeenCalledTimes(1); @@ -115,7 +116,7 @@ describe('ReportingObserver', () => { const reportingObserverIntegration = new ReportingObserver(); reportingObserverIntegration.setupOnce( () => undefined, - () => getMockHubWithIntegration(null) as any, + () => getMockHubWithIntegration(null as any), ); expect(() => { @@ -129,7 +130,7 @@ describe('ReportingObserver', () => { const reportingObserverIntegration = new ReportingObserver(); reportingObserverIntegration.setupOnce( () => undefined, - () => getMockHubWithIntegration(reportingObserverIntegration) as any, + () => getMockHubWithIntegration(reportingObserverIntegration), ); reportingObserverIntegration.handler([ @@ -144,7 +145,7 @@ describe('ReportingObserver', () => { const reportingObserverIntegration = new ReportingObserver(); reportingObserverIntegration.setupOnce( () => undefined, - () => getMockHubWithIntegration(reportingObserverIntegration) as any, + () => getMockHubWithIntegration(reportingObserverIntegration), ); reportingObserverIntegration.handler([ @@ -160,7 +161,7 @@ describe('ReportingObserver', () => { const reportingObserverIntegration = new ReportingObserver(); reportingObserverIntegration.setupOnce( () => undefined, - () => getMockHubWithIntegration(reportingObserverIntegration) as any, + () => getMockHubWithIntegration(reportingObserverIntegration), ); const report1 = { type: 'crash', url: 'some url 1', body: { crashId: 'id1' } } as const; @@ -176,7 +177,7 @@ describe('ReportingObserver', () => { const reportingObserverIntegration = new ReportingObserver(); reportingObserverIntegration.setupOnce( () => undefined, - () => getMockHubWithIntegration(reportingObserverIntegration) as any, + () => getMockHubWithIntegration(reportingObserverIntegration), ); reportingObserverIntegration.handler([{ type: 'crash', url: 'some url' }]); @@ -188,7 +189,7 @@ describe('ReportingObserver', () => { const reportingObserverIntegration = new ReportingObserver(); reportingObserverIntegration.setupOnce( () => undefined, - () => getMockHubWithIntegration(reportingObserverIntegration) as any, + () => getMockHubWithIntegration(reportingObserverIntegration), ); const report = { @@ -207,7 +208,7 @@ describe('ReportingObserver', () => { const reportingObserverIntegration = new ReportingObserver(); reportingObserverIntegration.setupOnce( () => undefined, - () => getMockHubWithIntegration(reportingObserverIntegration) as any, + () => getMockHubWithIntegration(reportingObserverIntegration), ); const report = { @@ -225,7 +226,7 @@ describe('ReportingObserver', () => { const reportingObserverIntegration = new ReportingObserver(); reportingObserverIntegration.setupOnce( () => undefined, - () => getMockHubWithIntegration(reportingObserverIntegration) as any, + () => getMockHubWithIntegration(reportingObserverIntegration), ); const report = { @@ -243,7 +244,7 @@ describe('ReportingObserver', () => { const reportingObserverIntegration = new ReportingObserver(); reportingObserverIntegration.setupOnce( () => undefined, - () => getMockHubWithIntegration(reportingObserverIntegration) as any, + () => getMockHubWithIntegration(reportingObserverIntegration), ); const report = { @@ -260,7 +261,7 @@ describe('ReportingObserver', () => { const reportingObserverIntegration = new ReportingObserver(); reportingObserverIntegration.setupOnce( () => undefined, - () => getMockHubWithIntegration(reportingObserverIntegration) as any, + () => getMockHubWithIntegration(reportingObserverIntegration), ); const report = { type: 'crash', url: 'some url', body: { crashId: '', reason: '' } } as const; @@ -274,7 +275,7 @@ describe('ReportingObserver', () => { const reportingObserverIntegration = new ReportingObserver(); reportingObserverIntegration.setupOnce( () => undefined, - () => getMockHubWithIntegration(reportingObserverIntegration) as any, + () => getMockHubWithIntegration(reportingObserverIntegration), ); const report = { @@ -292,7 +293,7 @@ describe('ReportingObserver', () => { const reportingObserverIntegration = new ReportingObserver(); reportingObserverIntegration.setupOnce( () => undefined, - () => getMockHubWithIntegration(reportingObserverIntegration) as any, + () => getMockHubWithIntegration(reportingObserverIntegration), ); const report = { diff --git a/packages/integrations/test/rewriteframes.test.ts b/packages/integrations/test/rewriteframes.test.ts index ff18e16f753d..bbe0a157e44a 100644 --- a/packages/integrations/test/rewriteframes.test.ts +++ b/packages/integrations/test/rewriteframes.test.ts @@ -3,18 +3,12 @@ import { Event, StackFrame } from '@sentry/types'; import { RewriteFrames } from '../src/rewriteframes'; let rewriteFrames: RewriteFrames; -let messageEvent: Event; let exceptionEvent: Event; let windowsExceptionEvent: Event; let multipleStacktracesEvent: Event; describe('RewriteFrames', () => { beforeEach(() => { - messageEvent = { - stacktrace: { - frames: [{ filename: '/www/src/app/file1.js' }, { filename: '/www/src/app/mo\\dule/file2.js' }], - }, - }; exceptionEvent = { exception: { values: [ diff --git a/packages/integrations/tsconfig.test.json b/packages/integrations/tsconfig.test.json index af7e36ec0eda..87f6afa06b86 100644 --- a/packages/integrations/tsconfig.test.json +++ b/packages/integrations/tsconfig.test.json @@ -5,7 +5,7 @@ "compilerOptions": { // should include all types from `./tsconfig.json` plus types for all test frameworks used - "types": ["jest"] + "types": ["node", "jest"] // other package-specific, test-specific options } diff --git a/packages/node/test/integrations/http.test.ts b/packages/node/test/integrations/http.test.ts index 5a05d79e3b1b..f66f847cd298 100644 --- a/packages/node/test/integrations/http.test.ts +++ b/packages/node/test/integrations/http.test.ts @@ -168,7 +168,7 @@ describe('default protocols', () => { let nockProtocol = 'https'; const proxy = 'http://:3128'; - const agent = new HttpsProxyAgent(proxy); + const agent = HttpsProxyAgent(proxy); if (NODE_VERSION.major && NODE_VERSION.major < 9) { nockProtocol = 'http'; diff --git a/packages/node/test/transports/http.test.ts b/packages/node/test/transports/http.test.ts index e2e837fee2d3..8c7abfa2ade2 100644 --- a/packages/node/test/transports/http.test.ts +++ b/packages/node/test/transports/http.test.ts @@ -31,6 +31,7 @@ const sessionPayload: Session = { update: jest.fn(), close: jest.fn(), toJSON: jest.fn(), + ignoreDuration: false, }; const sessionsPayload: SessionAggregates = { attrs: { environment: 'test', release: '1.0' }, diff --git a/packages/serverless/src/awslambda.ts b/packages/serverless/src/awslambda.ts index 0b86f560a820..bac3e1412c8b 100644 --- a/packages/serverless/src/awslambda.ts +++ b/packages/serverless/src/awslambda.ts @@ -279,7 +279,7 @@ export function wrapHandler( scope.setTag('timeout', humanReadableTimeout); captureMessage(`Possible function timeout: ${context.functionName}`, 'warning'); }); - }, timeoutWarningDelay); + }, timeoutWarningDelay) as unknown as NodeJS.Timeout; } // Applying `sentry-trace` to context diff --git a/packages/serverless/src/gcpfunction/http.ts b/packages/serverless/src/gcpfunction/http.ts index 607656fb3dbd..2e274bad3491 100644 --- a/packages/serverless/src/gcpfunction/http.ts +++ b/packages/serverless/src/gcpfunction/http.ts @@ -86,7 +86,7 @@ function _wrapHttpFunction(fn: HttpFunction, wrapOptions: Partial void), encoding?: string | (() => void), cb?: () => void): void { + res.end = function (chunk?: any | (() => void), encoding?: string | (() => void), cb?: () => void): any { transaction.setHttpStatus(res.statusCode); transaction.finish(); diff --git a/packages/serverless/test/google-cloud-http.test.ts b/packages/serverless/test/google-cloud-http.test.ts index fced84730b00..7d785462a4d9 100644 --- a/packages/serverless/test/google-cloud-http.test.ts +++ b/packages/serverless/test/google-cloud-http.test.ts @@ -62,6 +62,7 @@ describe('GoogleCloudHttp tracing', () => { op: 'gcloud.http.bigquery', description: 'POST /jobs', }); + // @ts-ignore see "Why @ts-ignore" note expect(Sentry.fakeTransaction.startChild).toBeCalledWith({ op: 'gcloud.http.bigquery', description: expect.stringMatching(new RegExp('^GET /queries/.+')), diff --git a/packages/tracing/test/browser/browsertracing.test.ts b/packages/tracing/test/browser/browsertracing.test.ts index 510befc29064..dfa4e0436b8c 100644 --- a/packages/tracing/test/browser/browsertracing.test.ts +++ b/packages/tracing/test/browser/browsertracing.test.ts @@ -1,7 +1,7 @@ import { BrowserClient } from '@sentry/browser'; import { setupBrowserTransport } from '@sentry/browser/src/transports'; import { Hub, makeMain } from '@sentry/hub'; -import { getGlobalObject } from '@sentry/utils'; +import { getGlobalObject, InstrumentHandlerCallback, InstrumentHandlerType } from '@sentry/utils'; import { JSDOM } from 'jsdom'; import { @@ -24,7 +24,7 @@ jest.mock('@sentry/utils', () => { const actual = jest.requireActual('@sentry/utils'); return { ...actual, - addInstrumentationHandler: (type, callback): void => { + addInstrumentationHandler: (type: InstrumentHandlerType, callback: InstrumentHandlerCallback): void => { if (type === 'history') { // rather than actually add the navigation-change handler, grab a reference to it, so we can trigger it manually mockChangeHistory = callback; diff --git a/packages/tracing/test/browser/metrics.test.ts b/packages/tracing/test/browser/metrics.test.ts index 7133f7975221..eb79ff30df93 100644 --- a/packages/tracing/test/browser/metrics.test.ts +++ b/packages/tracing/test/browser/metrics.test.ts @@ -1,11 +1,5 @@ import { Span, Transaction } from '../../src'; -import { - _startChild, - addResourceSpans, - DEFAULT_METRICS_INSTR_OPTIONS, - MetricsInstrumentation, - ResourceEntry, -} from '../../src/browser/metrics'; +import { _startChild, addResourceSpans, MetricsInstrumentation, ResourceEntry } from '../../src/browser/metrics'; import { addDOMPropertiesToGlobal } from '../testutils'; // eslint-disable-next-line @typescript-eslint/no-explicit-any, no-var @@ -186,7 +180,7 @@ describe('MetricsInstrumentation', () => { jest.spyOn(MetricsInstrumentation.prototype as any, tracker), ); - new MetricsInstrumentation(DEFAULT_METRICS_INSTR_OPTIONS); + new MetricsInstrumentation(); trackers.forEach(tracker => expect(tracker).not.toBeCalled()); }); @@ -200,7 +194,7 @@ describe('MetricsInstrumentation', () => { const trackers = ['_trackCLS', '_trackLCP', '_trackFID'].map(tracker => jest.spyOn(MetricsInstrumentation.prototype as any, tracker), ); - new MetricsInstrumentation(DEFAULT_METRICS_INSTR_OPTIONS); + new MetricsInstrumentation(); global.process = backup; trackers.forEach(tracker => expect(tracker).toBeCalled()); diff --git a/packages/tracing/test/browser/request.test.ts b/packages/tracing/test/browser/request.test.ts index a31aa10d5c8a..65823e293351 100644 --- a/packages/tracing/test/browser/request.test.ts +++ b/packages/tracing/test/browser/request.test.ts @@ -4,7 +4,7 @@ import { Hub, makeMain } from '@sentry/hub'; import * as utils from '@sentry/utils'; import { Span, spanStatusfromHttpCode, Transaction } from '../../src'; -import { fetchCallback, FetchData, instrumentOutgoingRequests, xhrCallback, XHRData } from '../../src/browser/request'; +import { fetchCallback, FetchData, instrumentOutgoingRequests, xhrCallback } from '../../src/browser/request'; import { addExtensionMethods } from '../../src/hubextensions'; import * as tracingUtils from '../../src/utils'; @@ -56,7 +56,7 @@ describe('callbacks', () => { fetchData: { url: 'http://dogs.are.great/', method: 'GET' }, startTimestamp, }; - const xhrHandlerData: XHRData = { + const xhrHandlerData = { xhr: { __sentry_xhr__: { method: 'GET', @@ -130,17 +130,17 @@ describe('callbacks', () => { // triggered by request being sent fetchCallback(fetchHandlerData, alwaysCreateSpan, spans); - const newSpan = transaction.spanRecorder?.spans[1]; + const newSpan = transaction.spanRecorder?.spans[1] as Span; expect(newSpan).toBeDefined(); expect(newSpan).toBeInstanceOf(Span); - expect(newSpan!.data).toEqual({ + expect(newSpan.data).toEqual({ method: 'GET', type: 'fetch', url: 'http://dogs.are.great/', }); - expect(newSpan!.description).toBe('GET http://dogs.are.great/'); - expect(newSpan!.op).toBe('http.client'); + expect(newSpan.description).toBe('GET http://dogs.are.great/'); + expect(newSpan.op).toBe('http.client'); expect(fetchHandlerData.fetchData?.__span).toBeDefined(); const postRequestFetchHandlerData = { @@ -151,7 +151,7 @@ describe('callbacks', () => { // triggered by response coming back fetchCallback(postRequestFetchHandlerData, alwaysCreateSpan, spans); - expect(newSpan!.endTimestamp).toBeDefined(); + expect(newSpan.endTimestamp).toBeDefined(); }); it('sets response status on finish', () => { @@ -160,7 +160,7 @@ describe('callbacks', () => { // triggered by request being sent fetchCallback(fetchHandlerData, alwaysCreateSpan, spans); - const newSpan = transaction.spanRecorder?.spans[1]; + const newSpan = transaction.spanRecorder?.spans[1] as Span; expect(newSpan).toBeDefined(); @@ -173,7 +173,7 @@ describe('callbacks', () => { // triggered by response coming back fetchCallback(postRequestFetchHandlerData, alwaysCreateSpan, spans); - expect(newSpan!.status).toBe(spanStatusfromHttpCode(404)); + expect(newSpan.status).toBe(spanStatusfromHttpCode(404)); }); it('ignores response with no associated span', () => { @@ -238,18 +238,18 @@ describe('callbacks', () => { // triggered by request being sent xhrCallback(xhrHandlerData, alwaysCreateSpan, spans); - const newSpan = transaction.spanRecorder?.spans[1]; + const newSpan = transaction.spanRecorder?.spans[1] as Span; expect(newSpan).toBeInstanceOf(Span); - expect(newSpan!.data).toEqual({ + expect(newSpan.data).toEqual({ method: 'GET', type: 'xhr', url: 'http://dogs.are.great/', }); - expect(newSpan!.description).toBe('GET http://dogs.are.great/'); - expect(newSpan!.op).toBe('http.client'); - expect(xhrHandlerData.xhr!.__sentry_xhr_span_id__).toBeDefined(); - expect(xhrHandlerData.xhr!.__sentry_xhr_span_id__).toEqual(newSpan?.spanId); + expect(newSpan.description).toBe('GET http://dogs.are.great/'); + expect(newSpan.op).toBe('http.client'); + expect(xhrHandlerData.xhr.__sentry_xhr_span_id__).toBeDefined(); + expect(xhrHandlerData.xhr.__sentry_xhr_span_id__).toEqual(newSpan?.spanId); const postRequestXHRHandlerData = { ...xhrHandlerData, @@ -259,7 +259,7 @@ describe('callbacks', () => { // triggered by response coming back xhrCallback(postRequestXHRHandlerData, alwaysCreateSpan, spans); - expect(newSpan!.endTimestamp).toBeDefined(); + expect(newSpan.endTimestamp).toBeDefined(); }); it('sets response status on finish', () => { @@ -268,7 +268,7 @@ describe('callbacks', () => { // triggered by request being sent xhrCallback(xhrHandlerData, alwaysCreateSpan, spans); - const newSpan = transaction.spanRecorder?.spans[1]; + const newSpan = transaction.spanRecorder?.spans[1] as Span; expect(newSpan).toBeDefined(); @@ -276,12 +276,12 @@ describe('callbacks', () => { ...xhrHandlerData, endTimestamp, }; - postRequestXHRHandlerData.xhr!.__sentry_xhr__!.status_code = 404; + postRequestXHRHandlerData.xhr.__sentry_xhr__.status_code = 404; // triggered by response coming back xhrCallback(postRequestXHRHandlerData, alwaysCreateSpan, spans); - expect(newSpan!.status).toBe(spanStatusfromHttpCode(404)); + expect(newSpan.status).toBe(spanStatusfromHttpCode(404)); }); it('ignores response with no associated span', () => { diff --git a/packages/tracing/test/browser/router.test.ts b/packages/tracing/test/browser/router.test.ts index e340a81222fe..9d1ae86f4e01 100644 --- a/packages/tracing/test/browser/router.test.ts +++ b/packages/tracing/test/browser/router.test.ts @@ -1,3 +1,4 @@ +import { InstrumentHandlerCallback, InstrumentHandlerType } from '@sentry/utils'; import { JSDOM } from 'jsdom'; import { instrumentRoutingWithDefaults } from '../../src/browser/router'; @@ -8,7 +9,7 @@ jest.mock('@sentry/utils', () => { const actual = jest.requireActual('@sentry/utils'); return { ...actual, - addInstrumentationHandler: (type, callback): void => { + addInstrumentationHandler: (type: InstrumentHandlerType, callback: InstrumentHandlerCallback): void => { addInstrumentationHandlerType = type; mockChangeHistory = callback; }, diff --git a/packages/tracing/test/errors.test.ts b/packages/tracing/test/errors.test.ts index ca042672b595..c15825fd5480 100644 --- a/packages/tracing/test/errors.test.ts +++ b/packages/tracing/test/errors.test.ts @@ -1,18 +1,19 @@ import { BrowserClient } from '@sentry/browser'; import { setupBrowserTransport } from '@sentry/browser/src/transports'; import { Hub, makeMain } from '@sentry/hub'; +import { InstrumentHandlerCallback, InstrumentHandlerType } from '@sentry/utils'; import { registerErrorInstrumentation } from '../src/errors'; import { _addTracingExtensions } from '../src/hubextensions'; const mockAddInstrumentationHandler = jest.fn(); -let mockErrorCallback: () => void = () => undefined; -let mockUnhandledRejectionCallback: () => void = () => undefined; +let mockErrorCallback: InstrumentHandlerCallback = () => undefined; +let mockUnhandledRejectionCallback: InstrumentHandlerCallback = () => undefined; jest.mock('@sentry/utils', () => { const actual = jest.requireActual('@sentry/utils'); return { ...actual, - addInstrumentationHandler: (type, callback) => { + addInstrumentationHandler: (type: InstrumentHandlerType, callback: InstrumentHandlerCallback) => { if (type === 'error') { mockErrorCallback = callback; } @@ -55,10 +56,10 @@ describe('registerErrorHandlers()', () => { const transaction = hub.startTransaction({ name: 'test' }); expect(transaction.status).toBe(undefined); - mockErrorCallback(); + mockErrorCallback({}); expect(transaction.status).toBe(undefined); - mockUnhandledRejectionCallback(); + mockUnhandledRejectionCallback({}); expect(transaction.status).toBe(undefined); transaction.finish(); }); @@ -68,7 +69,7 @@ describe('registerErrorHandlers()', () => { const transaction = hub.startTransaction({ name: 'test' }); hub.configureScope(scope => scope.setSpan(transaction)); - mockErrorCallback(); + mockErrorCallback({}); expect(transaction.status).toBe('internal_error'); transaction.finish(); @@ -79,7 +80,7 @@ describe('registerErrorHandlers()', () => { const transaction = hub.startTransaction({ name: 'test' }); hub.configureScope(scope => scope.setSpan(transaction)); - mockUnhandledRejectionCallback(); + mockUnhandledRejectionCallback({}); expect(transaction.status).toBe('internal_error'); transaction.finish(); }); diff --git a/packages/tracing/test/idletransaction.test.ts b/packages/tracing/test/idletransaction.test.ts index f2565e1c7e5f..b8029c6b92d7 100644 --- a/packages/tracing/test/idletransaction.test.ts +++ b/packages/tracing/test/idletransaction.test.ts @@ -9,7 +9,9 @@ import { } from '../src/idletransaction'; import { Span } from '../src/span'; -export class SimpleTransport extends Transports.BaseTransport {} +// @ts-ignore It's okay that we're not implementing the methods of the abstract `BaseTransport` class, because it's not +// what we're testing here +class SimpleTransport extends Transports.BaseTransport {} const dsn = 'https://123@sentry.io/42'; let hub: Hub; @@ -167,9 +169,9 @@ describe('IdleTransaction', () => { it('should record dropped transactions', async () => { const transaction = new IdleTransaction({ name: 'foo', startTimestamp: 1234, sampled: false }, hub, 1000); - const transport = hub.getClient()?.getTransport(); + const transport = hub.getClient()!.getTransport!(); - const spy = jest.spyOn(transport!, 'recordLostEvent'); + const spy = jest.spyOn(transport, 'recordLostEvent'); transaction.initSpanRecorder(10); transaction.finish(transaction.startTimestamp + 10); diff --git a/packages/utils/src/instrument.ts b/packages/utils/src/instrument.ts index 30fcef290c19..a77e0c8222f3 100644 --- a/packages/utils/src/instrument.ts +++ b/packages/utils/src/instrument.ts @@ -13,7 +13,7 @@ import { supportsHistory, supportsNativeFetch } from './supports'; const global = getGlobalObject(); -type InstrumentHandlerType = +export type InstrumentHandlerType = | 'console' | 'dom' | 'fetch' @@ -22,7 +22,7 @@ type InstrumentHandlerType = | 'xhr' | 'error' | 'unhandledrejection'; -type InstrumentHandlerCallback = (data: any) => void; +export type InstrumentHandlerCallback = (data: any) => void; /** * Instrument native APIs to call handlers that can be used to create breadcrumbs, APM spans etc. diff --git a/packages/utils/test/clientreport.test.ts b/packages/utils/test/clientreport.test.ts index 8d98291c3a14..156ddb95008f 100644 --- a/packages/utils/test/clientreport.test.ts +++ b/packages/utils/test/clientreport.test.ts @@ -3,7 +3,7 @@ import { ClientReport } from '@sentry/types'; import { createClientReportEnvelope } from '../src/clientreport'; import { serializeEnvelope } from '../src/envelope'; -const DEFAULT_DISCARDED_EVENTS: Array = [ +const DEFAULT_DISCARDED_EVENTS: ClientReport['discarded_events'] = [ { reason: 'before_send', category: 'event', diff --git a/packages/utils/test/syncpromise.test.ts b/packages/utils/test/syncpromise.test.ts index 6164c666d967..cdb0a7c6e0e7 100644 --- a/packages/utils/test/syncpromise.test.ts +++ b/packages/utils/test/syncpromise.test.ts @@ -1,7 +1,7 @@ import { rejectedSyncPromise, resolvedSyncPromise, SyncPromise } from '../src/syncpromise'; describe('SyncPromise', () => { - test('simple', () => { + test('simple', async () => { expect.assertions(1); return new SyncPromise(resolve => { @@ -11,7 +11,7 @@ describe('SyncPromise', () => { }); }); - test('simple chaining', () => { + test('simple chaining', async () => { expect.assertions(1); return new SyncPromise(resolve => { @@ -94,7 +94,7 @@ describe('SyncPromise', () => { ); }); - test('simple static', () => { + test('simple static', async () => { expect.assertions(1); const p = resolvedSyncPromise(10); @@ -103,7 +103,7 @@ describe('SyncPromise', () => { }); }); - test('using new Promise internally', () => { + test('using new Promise internally', async () => { expect.assertions(2); return new SyncPromise(done => { @@ -120,7 +120,7 @@ describe('SyncPromise', () => { }); }); - test('with setTimeout', () => { + test('with setTimeout', async () => { jest.useFakeTimers(); expect.assertions(1); @@ -175,7 +175,7 @@ describe('SyncPromise', () => { expect(qp).toHaveProperty('_value'); }); - test('multiple then returning undefined', () => { + test('multiple then returning undefined', async () => { expect.assertions(3); return new SyncPromise(resolve => { @@ -192,7 +192,7 @@ describe('SyncPromise', () => { }); }); - test('multiple then returning different values', () => { + test('multiple then returning different values', async () => { expect.assertions(3); return new SyncPromise(resolve => { @@ -211,7 +211,7 @@ describe('SyncPromise', () => { }); }); - test('multiple then returning different SyncPromise', () => { + test('multiple then returning different SyncPromise', async () => { expect.assertions(2); return new SyncPromise(resolve => { @@ -228,7 +228,7 @@ describe('SyncPromise', () => { }); }); - test('reject immediatly and do not call then', () => { + test('reject immediatly and do not call then', async () => { expect.assertions(1); return new SyncPromise((_, reject) => { @@ -242,7 +242,7 @@ describe('SyncPromise', () => { }); }); - test('reject', () => { + test('reject', async () => { expect.assertions(1); return new SyncPromise((_, reject) => { @@ -252,7 +252,7 @@ describe('SyncPromise', () => { }); }); - test('rejecting after first then', () => { + test('rejecting after first then', async () => { expect.assertions(2); return new SyncPromise(resolve => { From 0d811bd74d9a82cab13d26390fab15bc3387b2bd Mon Sep 17 00:00:00 2001 From: Abhijeet Prasad Date: Mon, 18 Apr 2022 13:41:31 -0400 Subject: [PATCH 057/204] ref(node): Remove `raven-node` backward-compat code (#4942) Removes functionality around syncing context from domain. --- .../src/integrations/onunhandledrejection.ts | 37 ++++--------------- .../node/test/onunhandledrejection.test.ts | 10 ----- 2 files changed, 8 insertions(+), 39 deletions(-) diff --git a/packages/node/src/integrations/onunhandledrejection.ts b/packages/node/src/integrations/onunhandledrejection.ts index 19f733b1f908..2ba9a3d8f205 100644 --- a/packages/node/src/integrations/onunhandledrejection.ts +++ b/packages/node/src/integrations/onunhandledrejection.ts @@ -46,36 +46,15 @@ export class OnUnhandledRejection implements Integration { // eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types, @typescript-eslint/no-explicit-any public sendUnhandledPromise(reason: any, promise: any): void { const hub = getCurrentHub(); - - if (!hub.getIntegration(OnUnhandledRejection)) { - this._handleRejection(reason); - return; - } - - /* eslint-disable @typescript-eslint/no-unsafe-member-access */ - const context = (promise.domain && promise.domain.sentryContext) || {}; - - hub.withScope((scope: Scope) => { - scope.setExtra('unhandledPromiseRejection', true); - - // Preserve backwards compatibility with raven-node for now - if (context.user) { - scope.setUser(context.user); - } - if (context.tags) { - scope.setTags(context.tags); - } - if (context.extra) { - scope.setExtras(context.extra); - } - - hub.captureException(reason, { - originalException: promise, - data: { mechanism: { handled: false, type: 'onunhandledrejection' } }, + if (hub.getIntegration(OnUnhandledRejection)) { + hub.withScope((scope: Scope) => { + scope.setExtra('unhandledPromiseRejection', true); + hub.captureException(reason, { + originalException: promise, + data: { mechanism: { handled: false, type: 'onunhandledrejection' } }, + }); }); - }); - /* eslint-disable @typescript-eslint/no-unsafe-member-access */ - + } this._handleRejection(reason); } diff --git a/packages/node/test/onunhandledrejection.test.ts b/packages/node/test/onunhandledrejection.test.ts index 5d8d498079e5..8588c9edddd6 100644 --- a/packages/node/test/onunhandledrejection.test.ts +++ b/packages/node/test/onunhandledrejection.test.ts @@ -1,4 +1,3 @@ -import { Scope } from '@sentry/core'; import { Hub } from '@sentry/hub'; import { OnUnhandledRejection } from '../src/integrations/onunhandledrejection'; @@ -35,10 +34,6 @@ describe('unhandled promises', () => { }; const captureException = jest.spyOn(Hub.prototype, 'captureException'); - const setUser = jest.spyOn(Scope.prototype, 'setUser'); - const setExtra = jest.spyOn(Scope.prototype, 'setExtra'); - const setExtras = jest.spyOn(Scope.prototype, 'setExtras'); - const setTags = jest.spyOn(Scope.prototype, 'setTags'); integration.sendUnhandledPromise('bla', promise); @@ -46,10 +41,5 @@ describe('unhandled promises', () => { mechanism: { handled: false, type: 'onunhandledrejection' }, }); expect(captureException.mock.calls[0][0]).toBe('bla'); - expect(setUser.mock.calls[0][0]).toEqual({ id: 1 }); - expect(setExtra.mock.calls[0]).toEqual(['unhandledPromiseRejection', true]); - - expect(setExtras.mock.calls[0]).toEqual([{ extra: '1' }]); - expect(setTags.mock.calls[0]).toEqual([{ tag: '2' }]); }); }); From fe845d9e07153801eb9007e2c08988636d76eb94 Mon Sep 17 00:00:00 2001 From: Katie Byers Date: Tue, 19 Apr 2022 06:09:05 -0700 Subject: [PATCH 058/204] ref(build): Split up rollup config code (#4950) As part of the new build process, a fair amount of new rollup-related code is going to be added. To keep things from getting too unwieldy, this splits the existing code up into modules. It also makes two other small changes, one for consistency and one to differentiate the current rollup code (which is for building bundles) from the future rollup code (which will be for building npm packages): - All plugins are now generated through factory functions (`makeXXXPlugin`). - Both the `makeConfigVariants` function and the individual `rollup.config.js` files have been renamed to make it clear they're for creating bundles. For now all of the resulting modules live in a `rollup` folder at the top level of the repo. In the long run, these would be good candidates to go into an `@sentry-internal/dev-utils` package. --- .eslintrc.js | 2 +- packages/browser/package.json | 2 +- ...llup.config.js => rollup.bundle.config.js} | 4 +- ...llup.config.js => rollup.bundle.config.js} | 4 +- packages/integrations/scripts/buildBundles.sh | 2 +- packages/tracing/package.json | 2 +- ...llup.config.js => rollup.bundle.config.js} | 4 +- packages/vue/package.json | 2 +- ...llup.config.js => rollup.bundle.config.js} | 4 +- packages/wasm/package.json | 2 +- ...llup.config.js => rollup.bundle.config.js} | 4 +- rollup.config.js | 270 ------------------ rollup/bundleHelpers.js | 143 ++++++++++ rollup/index.js | 8 + rollup/plugins/bundlePlugins.js | 142 +++++++++ rollup/plugins/index.js | 1 + rollup/utils.js | 16 ++ 17 files changed, 326 insertions(+), 286 deletions(-) rename packages/browser/{rollup.config.js => rollup.bundle.config.js} (67%) rename packages/integrations/{rollup.config.js => rollup.bundle.config.js} (81%) rename packages/tracing/{rollup.config.js => rollup.bundle.config.js} (70%) rename packages/vue/{rollup.config.js => rollup.bundle.config.js} (55%) rename packages/wasm/{rollup.config.js => rollup.bundle.config.js} (55%) delete mode 100644 rollup.config.js create mode 100644 rollup/bundleHelpers.js create mode 100644 rollup/index.js create mode 100644 rollup/plugins/bundlePlugins.js create mode 100644 rollup/plugins/index.js create mode 100644 rollup/utils.js diff --git a/.eslintrc.js b/.eslintrc.js index da303c683c01..956f3681ba72 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -42,7 +42,7 @@ module.exports = { }, }, { - files: ['scenarios/**'], + files: ['scenarios/**', 'rollup/**'], parserOptions: { sourceType: 'module', }, diff --git a/packages/browser/package.json b/packages/browser/package.json index 8ed9132b407e..8f9eb0013e26 100644 --- a/packages/browser/package.json +++ b/packages/browser/package.json @@ -44,7 +44,7 @@ }, "scripts": { "build": "run-p build:cjs build:esm build:bundle build:types", - "build:bundle": "rollup --config", + "build:bundle": "rollup --config rollup.bundle.config.js", "build:cjs": "tsc -p tsconfig.cjs.json", "build:dev": "run-p build:cjs build:esm build:types", "build:es5": "yarn build:cjs # *** backwards compatibility - remove in v7 ***", diff --git a/packages/browser/rollup.config.js b/packages/browser/rollup.bundle.config.js similarity index 67% rename from packages/browser/rollup.config.js rename to packages/browser/rollup.bundle.config.js index d49892da07be..aec9560621b1 100644 --- a/packages/browser/rollup.config.js +++ b/packages/browser/rollup.bundle.config.js @@ -1,4 +1,4 @@ -import { makeBaseBundleConfig, makeConfigVariants } from '../../rollup.config'; +import { makeBaseBundleConfig, makeBundleConfigVariants } from '../../rollup/index.js'; const builds = []; @@ -11,7 +11,7 @@ const builds = []; outputFileBase: `bundles/bundle${jsVersion === 'es6' ? '.es6' : ''}`, }); - builds.push(...makeConfigVariants(baseBundleConfig)); + builds.push(...makeBundleConfigVariants(baseBundleConfig)); }); export default builds; diff --git a/packages/integrations/rollup.config.js b/packages/integrations/rollup.bundle.config.js similarity index 81% rename from packages/integrations/rollup.config.js rename to packages/integrations/rollup.bundle.config.js index 7b23d698dfa2..de984497b9a8 100644 --- a/packages/integrations/rollup.config.js +++ b/packages/integrations/rollup.bundle.config.js @@ -1,6 +1,6 @@ import commonjs from '@rollup/plugin-commonjs'; -import { insertAt, makeBaseBundleConfig, makeConfigVariants } from '../../rollup.config'; +import { insertAt, makeBaseBundleConfig, makeBundleConfigVariants } from '../../rollup/index.js'; const builds = []; @@ -19,6 +19,6 @@ const baseBundleConfig = makeBaseBundleConfig({ baseBundleConfig.plugins = insertAt(baseBundleConfig.plugins, -2, commonjs()); // this makes non-minified, minified, and minified-with-debug-logging versions of each bundle -builds.push(...makeConfigVariants(baseBundleConfig)); +builds.push(...makeBundleConfigVariants(baseBundleConfig)); export default builds; diff --git a/packages/integrations/scripts/buildBundles.sh b/packages/integrations/scripts/buildBundles.sh index 3e81a8e6e045..2b75b83ee670 100644 --- a/packages/integrations/scripts/buildBundles.sh +++ b/packages/integrations/scripts/buildBundles.sh @@ -11,7 +11,7 @@ for filepath in ./src/*; do fi # run the build for each integration - INTEGRATION_FILE=$file JS_VERSION=$js_version yarn --silent rollup -c rollup.config.js + INTEGRATION_FILE=$file JS_VERSION=$js_version yarn --silent rollup --config rollup.bundle.config.js done done diff --git a/packages/tracing/package.json b/packages/tracing/package.json index 788189a6a8f9..f9465746e0e0 100644 --- a/packages/tracing/package.json +++ b/packages/tracing/package.json @@ -28,7 +28,7 @@ }, "scripts": { "build": "run-p build:cjs build:esm build:types build:bundle && ts-node ../../scripts/prepack.ts --bundles #necessary for integration tests", - "build:bundle": "rollup --config", + "build:bundle": "rollup --config rollup.bundle.config.js", "build:cjs": "tsc -p tsconfig.cjs.json", "build:dev": "run-p build:cjs build:esm build:types", "build:es5": "yarn build:cjs # *** backwards compatibility - remove in v7 ***", diff --git a/packages/tracing/rollup.config.js b/packages/tracing/rollup.bundle.config.js similarity index 70% rename from packages/tracing/rollup.config.js rename to packages/tracing/rollup.bundle.config.js index 529ddea29a1b..66d79286f38c 100644 --- a/packages/tracing/rollup.config.js +++ b/packages/tracing/rollup.bundle.config.js @@ -1,4 +1,4 @@ -import { makeBaseBundleConfig, makeConfigVariants } from '../../rollup.config'; +import { makeBaseBundleConfig, makeBundleConfigVariants } from '../../rollup/index.js'; const builds = []; @@ -11,7 +11,7 @@ const builds = []; outputFileBase: `bundles/bundle.tracing${jsVersion === 'es6' ? '.es6' : ''}`, }); - builds.push(...makeConfigVariants(baseBundleConfig)); + builds.push(...makeBundleConfigVariants(baseBundleConfig)); }); export default builds; diff --git a/packages/vue/package.json b/packages/vue/package.json index 29452f54ce4c..b6eefc1ab6f3 100644 --- a/packages/vue/package.json +++ b/packages/vue/package.json @@ -28,7 +28,7 @@ }, "scripts": { "build": "run-p build:cjs build:esm build:types", - "build:bundle": "rollup --config", + "build:bundle": "rollup --config rollup.bundle.config.js", "build:cjs": "tsc -p tsconfig.cjs.json", "build:dev": "run-p build:cjs build:esm build:types", "build:es5": "yarn build:cjs # *** backwards compatibility - remove in v7 ***", diff --git a/packages/vue/rollup.config.js b/packages/vue/rollup.bundle.config.js similarity index 55% rename from packages/vue/rollup.config.js rename to packages/vue/rollup.bundle.config.js index 2185b2b716c5..745205cda85f 100644 --- a/packages/vue/rollup.config.js +++ b/packages/vue/rollup.bundle.config.js @@ -1,4 +1,4 @@ -import { makeBaseBundleConfig, makeConfigVariants } from '../../rollup.config'; +import { makeBaseBundleConfig, makeBundleConfigVariants } from '../../rollup/index.js'; const baseBundleConfig = makeBaseBundleConfig({ input: 'src/index.bundle.ts', @@ -8,4 +8,4 @@ const baseBundleConfig = makeBaseBundleConfig({ outputFileBase: 'bundle.vue', }); -export default makeConfigVariants(baseBundleConfig); +export default makeBundleConfigVariants(baseBundleConfig); diff --git a/packages/wasm/package.json b/packages/wasm/package.json index 8c850944a72f..671ddad9c5f2 100644 --- a/packages/wasm/package.json +++ b/packages/wasm/package.json @@ -30,7 +30,7 @@ }, "scripts": { "build": "run-p build:cjs build:esm build:bundle build:types", - "build:bundle": "rollup --config", + "build:bundle": "rollup --config rollup.bundle.config.js", "build:cjs": "tsc -p tsconfig.cjs.json", "build:dev": "run-p build:cjs build:esm build:types", "build:es5": "yarn build:cjs # *** backwards compatibility - remove in v7 ***", diff --git a/packages/wasm/rollup.config.js b/packages/wasm/rollup.bundle.config.js similarity index 55% rename from packages/wasm/rollup.config.js rename to packages/wasm/rollup.bundle.config.js index edccdbd9287a..265b557c76a7 100644 --- a/packages/wasm/rollup.config.js +++ b/packages/wasm/rollup.bundle.config.js @@ -1,4 +1,4 @@ -import { makeBaseBundleConfig, makeConfigVariants } from '../../rollup.config'; +import { makeBaseBundleConfig, makeBundleConfigVariants } from '../../rollup/index.js'; const baseBundleConfig = makeBaseBundleConfig({ input: 'src/index.ts', @@ -8,4 +8,4 @@ const baseBundleConfig = makeBaseBundleConfig({ outputFileBase: 'bundles/wasm', }); -export default makeConfigVariants(baseBundleConfig); +export default makeBundleConfigVariants(baseBundleConfig); diff --git a/rollup.config.js b/rollup.config.js deleted file mode 100644 index 041f269f1095..000000000000 --- a/rollup.config.js +++ /dev/null @@ -1,270 +0,0 @@ -/** - * Code for generating config used by individual packages' Rollup configs - */ - -import assert from 'assert'; - -import deepMerge from 'deepmerge'; -import license from 'rollup-plugin-license'; -import resolve from '@rollup/plugin-node-resolve'; -import replace from '@rollup/plugin-replace'; -import { terser } from 'rollup-plugin-terser'; -import typescript from 'rollup-plugin-typescript2'; - -Error.stackTraceLimit = Infinity; - -/** - * Helper functions to compensate for the fact that JS can't handle negative array indices very well - * - * TODO `insertAt` is only exported so the integrations config can inject the `commonjs` plugin, for localforage (used - * in the offline plugin). Once that's fixed to no longer be necessary, this can stop being exported. - */ -const getLastElement = array => { - return array[array.length - 1]; -}; -export const insertAt = (arr, index, ...insertees) => { - const newArr = [...arr]; - // Add 1 to the array length so that the inserted element ends up in the right spot with respect to the length of the - // new array (which will be one element longer), rather than that of the current array - const destinationIndex = index >= 0 ? index : arr.length + 1 + index; - newArr.splice(destinationIndex, 0, ...insertees); - return newArr; -}; - -/** - * Create a plugin to add an identification banner to the top of stand-alone bundles. - * - * @param title The title to use for the SDK, if not the package name - * @returns An instance of the `rollup-plugin-license` plugin - */ -function makeLicensePlugin(title) { - const commitHash = require('child_process').execSync('git rev-parse --short HEAD', { encoding: 'utf-8' }).trim(); - - return license({ - banner: { - content: `/*! <%= data.title %> <%= pkg.version %> (${commitHash}) | https://github.com/getsentry/sentry-javascript */`, - data: { title }, - }, - }); -} - -function makeIsDebugBuildPlugin(includeDebugging) { - return replace({ - // __SENTRY_DEBUG__ should be save to replace in any case, so no checks for assignments necessary - preventAssignment: false, - values: { - __SENTRY_DEBUG__: includeDebugging, - }, - }); -} - -// `terser` options reference: https://github.com/terser/terser#api-reference -// `rollup-plugin-terser` options reference: https://github.com/TrySound/rollup-plugin-terser#options -export const terserPlugin = terser({ - mangle: { - // captureExceptions and captureMessage are public API methods and they don't need to be listed here - // as mangler doesn't touch user-facing thing, however sentryWrapped is not, and it would be mangled into a minified version. - // We need those full names to correctly detect our internal frames for stripping. - // I listed all of them here just for the clarity sake, as they are all used in the frames manipulation process. - reserved: ['captureException', 'captureMessage', 'sentryWrapped'], - properties: { - regex: /^_[^_]/, - reserved: ['_experiments'], - }, - }, - output: { - comments: false, - }, -}); - -export function makeBaseBundleConfig(options) { - const { input, isAddOn, jsVersion, licenseTitle, outputFileBase } = options; - - const baseTSPluginOptions = { - tsconfig: 'tsconfig.esm.json', - tsconfigOverride: { - compilerOptions: { - declaration: false, - declarationMap: false, - paths: { - '@sentry/browser': ['../browser/src'], - '@sentry/core': ['../core/src'], - '@sentry/hub': ['../hub/src'], - '@sentry/minimal': ['../minimal/src'], - '@sentry/types': ['../types/src'], - '@sentry/utils': ['../utils/src'], - }, - baseUrl: '.', - }, - }, - include: ['*.ts+(|x)', '**/*.ts+(|x)', '../**/*.ts+(|x)'], - // the typescript plugin doesn't handle concurrency very well, so clean the cache between builds - // (see https://github.com/ezolenko/rollup-plugin-typescript2/issues/15) - clean: true, - // TODO: For the moment, the above issue seems to have stopped spamming the build with (non-blocking) errors, as it - // was originally. If it starts again, this will suppress that output. If we get to the end of the bundle revamp and - // it still seems okay, we can take this out entirely. - // verbosity: 0, - }; - - const typescriptPluginES5 = typescript( - deepMerge(baseTSPluginOptions, { - tsconfigOverride: { - compilerOptions: { - target: 'es5', - }, - }, - }), - ); - - const typescriptPluginES6 = typescript( - deepMerge(baseTSPluginOptions, { - tsconfigOverride: { - compilerOptions: { - target: 'es6', - }, - }, - }), - ); - - const nodeResolvePlugin = resolve(); - - const markAsBrowserBuildPlugin = replace({ - // don't replace `__placeholder__` where it's followed immediately by a single `=` (to prevent ending up - // with something of the form `let "replacementValue" = "some assigned value"`, which would cause a - // syntax error) - preventAssignment: true, - // the replacement to make - values: { - __SENTRY_BROWSER_BUNDLE__: true, - }, - }); - - const licensePlugin = makeLicensePlugin(licenseTitle); - - // used by `@sentry/browser`, `@sentry/tracing`, and `@sentry/vue` (bundles which are a full SDK in and of themselves) - const standAloneBundleConfig = { - output: { - format: 'iife', - name: 'Sentry', - }, - context: 'window', - }; - - // used by `@sentry/integrations` and `@sentry/wasm` (bundles which need to be combined with a stand-alone SDK bundle) - const addOnBundleConfig = { - // These output settings are designed to mimic an IIFE. We don't use Rollup's `iife` format because we don't want to - // attach this code to a new global variable, but rather inject it into the existing SDK's `Integrations` object. - output: { - format: 'cjs', - - // code to add before the CJS wrapper - banner: '(function (__window) {', - - // code to add just inside the CJS wrapper, before any of the wrapped code - intro: 'var exports = {};', - - // code to add after all of the wrapped code, but still inside the CJS wrapper - outro: () => - [ - '', - " // Add this module's exports to the global `Sentry.Integrations`", - ' __window.Sentry = __window.Sentry || {};', - ' __window.Sentry.Integrations = __window.Sentry.Integrations || {};', - ' for (var key in exports) {', - ' if (Object.prototype.hasOwnProperty.call(exports, key)) {', - ' __window.Sentry.Integrations[key] = exports[key];', - ' }', - ' }', - ].join('\n'), - - // code to add after the CJS wrapper - footer: '}(window));', - }, - }; - - // used by all bundles - const sharedBundleConfig = { - input, - output: { - // a file extension will be added to this base value when we specify either a minified or non-minified build - file: `build/${outputFileBase}`, - sourcemap: true, - strict: false, - esModule: false, - }, - plugins: [ - jsVersion.toLowerCase() === 'es5' ? typescriptPluginES5 : typescriptPluginES6, - markAsBrowserBuildPlugin, - nodeResolvePlugin, - licensePlugin, - ], - treeshake: 'smallest', - }; - - return deepMerge(sharedBundleConfig, isAddOn ? addOnBundleConfig : standAloneBundleConfig); -} - -/** - * Takes the CDN rollup config for a given package and produces three versions of it: - * - non-minified, including debug logging, - * - minified, including debug logging, - * - minified, with debug logging stripped - * - * @param baseConfig The rollup config shared by the entire package - * @returns An array of versions of that config - */ -export function makeConfigVariants(baseConfig) { - const configVariants = []; - - const { plugins } = baseConfig; - const includeDebuggingPlugin = makeIsDebugBuildPlugin(true); - const stripDebuggingPlugin = makeIsDebugBuildPlugin(false); - - // The license plugin has to be last, so it ends up after terser. Otherwise, terser will remove the license banner. - assert( - getLastElement(plugins).name === 'rollup-plugin-license', - `Last plugin in given options should be \`rollup-plugin-license\`. Found ${getLastElement(plugins).name}`, - ); - - // The additional options to use for each variant we're going to create - const variantSpecificConfigs = [ - { - output: { - file: `${baseConfig.output.file}.js`, - }, - plugins: insertAt(plugins, -2, includeDebuggingPlugin), - }, - // This variant isn't particularly helpful for an SDK user, as it strips logging while making no other minification - // changes, so by default we don't create it. It is however very useful when debugging rollup's treeshaking, so it's - // left here for that purpose. - // { - // output: { file: `${baseConfig.output.file}.no-debug.js`, - // }, - // plugins: insertAt(plugins, -2, stripDebuggingPlugin), - // }, - { - output: { - file: `${baseConfig.output.file}.min.js`, - }, - plugins: insertAt(plugins, -2, stripDebuggingPlugin, terserPlugin), - }, - { - output: { - file: `${baseConfig.output.file}.debug.min.js`, - }, - plugins: insertAt(plugins, -2, includeDebuggingPlugin, terserPlugin), - }, - ]; - - variantSpecificConfigs.forEach(variant => { - const mergedConfig = deepMerge(baseConfig, variant, { - // this makes it so that instead of concatenating the `plugin` properties of the two objects, the first value is - // just overwritten by the second value - arrayMerge: (first, second) => second, - }); - configVariants.push(mergedConfig); - }); - - return configVariants; -} diff --git a/rollup/bundleHelpers.js b/rollup/bundleHelpers.js new file mode 100644 index 000000000000..d4ddc0e706e2 --- /dev/null +++ b/rollup/bundleHelpers.js @@ -0,0 +1,143 @@ +/** + * Rollup config docs: https://rollupjs.org/guide/en/#big-list-of-options + */ + +import assert from 'assert'; + +import deepMerge from 'deepmerge'; + +import { + makeBrowserBuildPlugin, + makeIsDebugBuildPlugin, + makeLicensePlugin, + makeNodeResolvePlugin, + makeTerserPlugin, + makeTSPlugin, +} from './plugins/index.js'; +import { getLastElement, insertAt } from './utils.js'; + +export function makeBaseBundleConfig(options) { + const { input, isAddOn, jsVersion, licenseTitle, outputFileBase } = options; + + const nodeResolvePlugin = makeNodeResolvePlugin(); + const markAsBrowserBuildPlugin = makeBrowserBuildPlugin(true); + const licensePlugin = makeLicensePlugin(licenseTitle); + const tsPlugin = makeTSPlugin(jsVersion.toLowerCase()); + + // used by `@sentry/browser`, `@sentry/tracing`, and `@sentry/vue` (bundles which are a full SDK in and of themselves) + const standAloneBundleConfig = { + output: { + format: 'iife', + name: 'Sentry', + }, + context: 'window', + }; + + // used by `@sentry/integrations` and `@sentry/wasm` (bundles which need to be combined with a stand-alone SDK bundle) + const addOnBundleConfig = { + // These output settings are designed to mimic an IIFE. We don't use Rollup's `iife` format because we don't want to + // attach this code to a new global variable, but rather inject it into the existing SDK's `Integrations` object. + output: { + format: 'cjs', + + // code to add before the CJS wrapper + banner: '(function (__window) {', + + // code to add just inside the CJS wrapper, before any of the wrapped code + intro: 'var exports = {};', + + // code to add after all of the wrapped code, but still inside the CJS wrapper + outro: () => + [ + '', + " // Add this module's exports to the global `Sentry.Integrations`", + ' __window.Sentry = __window.Sentry || {};', + ' __window.Sentry.Integrations = __window.Sentry.Integrations || {};', + ' for (var key in exports) {', + ' if (Object.prototype.hasOwnProperty.call(exports, key)) {', + ' __window.Sentry.Integrations[key] = exports[key];', + ' }', + ' }', + ].join('\n'), + + // code to add after the CJS wrapper + footer: '}(window));', + }, + }; + + // used by all bundles + const sharedBundleConfig = { + input, + output: { + // a file extension will be added to this base value when we specify either a minified or non-minified build + file: `build/${outputFileBase}`, + sourcemap: true, + strict: false, + esModule: false, + }, + plugins: [tsPlugin, markAsBrowserBuildPlugin, nodeResolvePlugin, licensePlugin], + treeshake: 'smallest', + }; + + return deepMerge(sharedBundleConfig, isAddOn ? addOnBundleConfig : standAloneBundleConfig); +} + +/** + * Takes the CDN rollup config for a given package and produces three versions of it: + * - non-minified, including debug logging, + * - minified, including debug logging, + * - minified, with debug logging stripped + * + * @param baseConfig The rollup config shared by the entire package + * @returns An array of versions of that config + */ +export function makeBundleConfigVariants(baseConfig) { + const { plugins: baseConfigPlugins } = baseConfig; + const includeDebuggingPlugin = makeIsDebugBuildPlugin(true); + const stripDebuggingPlugin = makeIsDebugBuildPlugin(false); + const terserPlugin = makeTerserPlugin(); + + // The license plugin has to be last, so it ends up after terser. Otherwise, terser will remove the license banner. + assert( + getLastElement(baseConfigPlugins).name === 'rollup-plugin-license', + `Last plugin in given options should be \`rollup-plugin-license\`. Found ${getLastElement(baseConfigPlugins).name}`, + ); + + // The additional options to use for each variant we're going to create + const variantSpecificConfigs = [ + { + output: { + file: `${baseConfig.output.file}.js`, + }, + plugins: insertAt(baseConfigPlugins, -2, includeDebuggingPlugin), + }, + // This variant isn't particularly helpful for an SDK user, as it strips logging while making no other minification + // changes, so by default we don't create it. It is however very useful when debugging rollup's treeshaking, so it's + // left here for that purpose. + // { + // output: { file: `${baseConfig.output.file}.no-debug.js`, + // }, + // plugins: insertAt(plugins, -2, stripDebuggingPlugin), + // }, + { + output: { + file: `${baseConfig.output.file}.min.js`, + }, + plugins: insertAt(baseConfigPlugins, -2, stripDebuggingPlugin, terserPlugin), + }, + { + output: { + file: `${baseConfig.output.file}.debug.min.js`, + }, + plugins: insertAt(baseConfigPlugins, -2, includeDebuggingPlugin, terserPlugin), + }, + ]; + + return variantSpecificConfigs.map(variant => + deepMerge(baseConfig, variant, { + // this makes it so that instead of concatenating the `plugin` properties of the two objects, the first value is + // just overwritten by the second value + arrayMerge: (first, second) => second, + }), + ); +} diff --git a/rollup/index.js b/rollup/index.js new file mode 100644 index 000000000000..51baa9ba247c --- /dev/null +++ b/rollup/index.js @@ -0,0 +1,8 @@ +Error.stackTraceLimit = Infinity; + +// TODO Is this necessary? +import * as plugins from './plugins/index.js'; +export { plugins }; + +export * from './bundleHelpers.js'; +export { insertAt } from './utils.js'; diff --git a/rollup/plugins/bundlePlugins.js b/rollup/plugins/bundlePlugins.js new file mode 100644 index 000000000000..17c8d5977044 --- /dev/null +++ b/rollup/plugins/bundlePlugins.js @@ -0,0 +1,142 @@ +/** + * License plugin docs: https://github.com/mjeanroy/rollup-plugin-license + * Replace plugin docs: https://github.com/rollup/plugins/tree/master/packages/replace + * Resolve plugin docs: https://github.com/rollup/plugins/tree/master/packages/node-resolve + * Terser plugin docs: https://github.com/TrySound/rollup-plugin-terser#options + * Terser docs: https://github.com/terser/terser#api-reference + * Typescript plugin docs: https://github.com/ezolenko/rollup-plugin-typescript2 + */ + +import deepMerge from 'deepmerge'; +import license from 'rollup-plugin-license'; +import resolve from '@rollup/plugin-node-resolve'; +import replace from '@rollup/plugin-replace'; +import { terser } from 'rollup-plugin-terser'; +import typescript from 'rollup-plugin-typescript2'; + +/** + * Create a plugin to add an identification banner to the top of stand-alone bundles. + * + * @param title The title to use for the SDK, if not the package name + * @returns An instance of the `rollup-plugin-license` plugin + */ +export function makeLicensePlugin(title) { + const commitHash = require('child_process').execSync('git rev-parse --short HEAD', { encoding: 'utf-8' }).trim(); + + return license({ + banner: { + content: `/*! <%= data.title %> <%= pkg.version %> (${commitHash}) | https://github.com/getsentry/sentry-javascript */`, + data: { title }, + }, + }); +} + +/** + * Create a plugin to set the value of the `__SENTRY_DEBUG__` magic string. + * + * @param includeDebugging Whether or not the resulting build should include log statements + * @returns An instance of the `replace` plugin to do the replacement of the magic string with `true` or 'false` + */ +export function makeIsDebugBuildPlugin(includeDebugging) { + return replace({ + // __SENTRY_DEBUG__ should be save to replace in any case, so no checks for assignments necessary + preventAssignment: false, + values: { + __SENTRY_DEBUG__: includeDebugging, + }, + }); +} + +/** + * Create a plugin to set the value of the `__SENTRY_BROWSER_BUNDLE__` magic string. + * + * @param isBrowserBuild Whether or not the resulting build will be run in the browser + * @returns An instance of the `replace` plugin to do the replacement of the magic string with `true` or 'false` + */ +export function makeBrowserBuildPlugin(isBrowserBuild) { + return replace({ + // TODO This will be the default in the next version of the `replace` plugin + preventAssignment: true, + values: { + __SENTRY_BROWSER_BUNDLE__: isBrowserBuild, + }, + }); +} + +// `terser` options reference: https://github.com/terser/terser#api-reference +// `rollup-plugin-terser` options reference: https://github.com/TrySound/rollup-plugin-terser#options + +/** + * Create a plugin to perform minification using `terser`. + * + * @returns An instance of the `terser` plugin + */ +export function makeTerserPlugin() { + return terser({ + mangle: { + // `captureException` and `captureMessage` are public API methods and they don't need to be listed here, as the + // mangler won't touch user-facing things, but `sentryWrapped` is not user-facing, and would be mangled during + // minification. (We need it in its original form to correctly detect our internal frames for stripping.) All three + // are all listed here just for the clarity's sake, as they are all used in the frames manipulation process. + reserved: ['captureException', 'captureMessage', 'sentryWrapped'], + properties: { + // allow mangling of private field names... + regex: /^_[^_]/, + // ...except for `_experiments`, which we want to remain usable from the outside + reserved: ['_experiments'], + }, + }, + output: { + comments: false, + }, + }); +} + +/** + * Create a TypeScript plugin, which will down-compile if necessary, based on the given JS version. + * + * @param jsVersion Either `es5` or `es6` + * @returns An instance of the `typescript` plugin + */ +export function makeTSPlugin(jsVersion) { + const baseTSPluginOptions = { + tsconfig: 'tsconfig.esm.json', + tsconfigOverride: { + compilerOptions: { + declaration: false, + declarationMap: false, + paths: { + '@sentry/browser': ['../browser/src'], + '@sentry/core': ['../core/src'], + '@sentry/hub': ['../hub/src'], + '@sentry/minimal': ['../minimal/src'], + '@sentry/types': ['../types/src'], + '@sentry/utils': ['../utils/src'], + }, + baseUrl: '.', + }, + }, + include: ['*.ts+(|x)', '**/*.ts+(|x)', '../**/*.ts+(|x)'], + // the typescript plugin doesn't handle concurrency very well, so clean the cache between builds + // (see https://github.com/ezolenko/rollup-plugin-typescript2/issues/15) + clean: true, + // TODO: For the moment, the above issue seems to have stopped spamming the build with (non-blocking) errors, as it + // was originally. If it starts again, this will suppress that output. If we get to the end of the bundle revamp and + // it still seems okay, we can take this out entirely. + // verbosity: 0, + }; + + return typescript( + deepMerge(baseTSPluginOptions, { + tsconfigOverride: { + compilerOptions: { + target: jsVersion, + }, + }, + }), + ); +} + +// We don't pass this plugin any options, so no need to wrap it in another factory function, as `resolve` is itself +// already a factory function. +export { resolve as makeNodeResolvePlugin }; diff --git a/rollup/plugins/index.js b/rollup/plugins/index.js new file mode 100644 index 000000000000..bb05969c2caf --- /dev/null +++ b/rollup/plugins/index.js @@ -0,0 +1 @@ +export * from './bundlePlugins'; diff --git a/rollup/utils.js b/rollup/utils.js new file mode 100644 index 000000000000..00a97e991edf --- /dev/null +++ b/rollup/utils.js @@ -0,0 +1,16 @@ +/** + * Helper functions to compensate for the fact that JS can't handle negative array indices very well + */ + +export const getLastElement = array => { + return array[array.length - 1]; +}; + +export const insertAt = (arr, index, ...insertees) => { + const newArr = [...arr]; + // Add 1 to the array length so that the inserted element ends up in the right spot with respect to the length of the + // new array (which will be one element longer), rather than that of the current array + const destinationIndex = index >= 0 ? index : arr.length + 1 + index; + newArr.splice(destinationIndex, 0, ...insertees); + return newArr; +}; From 34d4ddbb5a52a3244e36f32df076be50e54b2eee Mon Sep 17 00:00:00 2001 From: Onur Temizkan Date: Tue, 19 Apr 2022 15:24:29 +0000 Subject: [PATCH 059/204] ref(tests): Suppress unconstructive listener and open handle warnings. (#4951) Removes warnings that we can't address at the moment from Jest logs. 1 - `MaxEventListener` warning is about a potential memory leak when more than 10 event listeners are assigned inside a single thread. As we're running all our integration tests on a single Jest thread, after 10th Sentry initialization, this warning starts polluting logs. Still, it's not great to have unregistered handles around. But we know it's limited to the number of test scenarios here. So IMO this workaround is safe to use for these tests. 2 - `detectOpenHandles` warns about uncleared intervals from `session` tests (where we're hackily replacing flush interval), which we are eventually clearing but can't set a reliable timeout, and we're also using `--forceExit` that does that on exit. --- packages/node-integration-tests/jest.config.js | 1 + packages/node-integration-tests/package.json | 2 +- packages/node-integration-tests/setup-tests.ts | 12 ++++++++++++ 3 files changed, 14 insertions(+), 1 deletion(-) create mode 100644 packages/node-integration-tests/setup-tests.ts diff --git a/packages/node-integration-tests/jest.config.js b/packages/node-integration-tests/jest.config.js index 617a25ea0817..81e7d8f63d18 100644 --- a/packages/node-integration-tests/jest.config.js +++ b/packages/node-integration-tests/jest.config.js @@ -1,6 +1,7 @@ const baseConfig = require('../../jest.config.js'); module.exports = { + globalSetup: '/setup-tests.ts', ...baseConfig, testMatch: ['**/test.ts'], }; diff --git a/packages/node-integration-tests/package.json b/packages/node-integration-tests/package.json index 3f09e8d64192..002031983f52 100644 --- a/packages/node-integration-tests/package.json +++ b/packages/node-integration-tests/package.json @@ -11,7 +11,7 @@ "lint:eslint": "eslint . --cache --cache-location '../../eslintcache/' --format stylish", "lint:prettier": "prettier --check \"{suites,utils}/**/*.ts\"", "type-check": "tsc", - "test": "jest --detectOpenHandles --runInBand --forceExit", + "test": "jest --runInBand --forceExit", "test:watch": "yarn test --watch" }, "dependencies": { diff --git a/packages/node-integration-tests/setup-tests.ts b/packages/node-integration-tests/setup-tests.ts new file mode 100644 index 000000000000..6f7bb2bec369 --- /dev/null +++ b/packages/node-integration-tests/setup-tests.ts @@ -0,0 +1,12 @@ +import EventEmitter from 'events'; + +const setup = async (): Promise => { + // Node warns about a potential memory leak + // when more than 10 event listeners are assigned inside a single thread. + // Initializing Sentry for each test triggers these warnings after 10th test inside Jest thread. + // As we know that it's not a memory leak and number of listeners are limited to the number of tests, + // removing the limit on listener count here. + EventEmitter.defaultMaxListeners = 0; +}; + +export default setup; From 5ed6066851619d4728037e9c8b6609af5125e242 Mon Sep 17 00:00:00 2001 From: Abhijeet Prasad Date: Tue, 19 Apr 2022 12:46:27 -0400 Subject: [PATCH 060/204] feat(core): Introduce separate client options (#4927) This PR works toward differentiating the options that are passed into `Sentry.init` and the options passed into clients. We want to make this differentiation to minimize internal state in the client, and instead rely on the max number of items being passed in to the client constructor. We do this by explicitly differentiating between the options that are configured in sdk init (`Options`) and the options that are passed into the client constructor (`ClientOptions`). --- packages/browser/src/client.ts | 25 ++- packages/browser/src/sdk.ts | 26 ++- packages/browser/src/transports/setup.ts | 5 +- .../unit/helper/browser-client-options.ts | 12 ++ packages/browser/test/unit/index.test.ts | 111 +++++------ .../unit/integrations/linkederrors.test.ts | 7 +- packages/browser/test/unit/sdk.test.ts | 124 +++++++++++++ .../test/unit/transports/setup.test.ts | 9 +- packages/core/src/baseclient.ts | 4 +- packages/core/src/index.ts | 1 + packages/core/src/integration.ts | 6 +- packages/core/src/sdk.ts | 6 +- packages/core/test/lib/base.test.ts | 174 ++++++++---------- packages/core/test/lib/sdk.test.ts | 63 +------ packages/core/test/mocks/client.ts | 22 ++- packages/node/src/client.ts | 8 +- packages/node/src/sdk.ts | 22 ++- packages/node/src/types.ts | 23 ++- packages/node/test/client.test.ts | 27 +-- packages/node/test/handlers.test.ts | 23 +-- .../node/test/helper/node-client-options.ts | 12 ++ packages/node/test/index.test.ts | 39 ++-- packages/node/test/integrations/http.test.ts | 11 +- .../test/integrations/linkederrors.test.ts | 11 +- packages/node/test/sdk.test.ts | 92 +++++++++ packages/node/test/transports/setup.test.ts | 3 +- packages/tracing/src/hubextensions.ts | 11 +- packages/tracing/src/utils.ts | 4 +- .../test/browser/backgroundtab.test.ts | 3 +- .../test/browser/browsertracing.test.ts | 7 +- packages/tracing/test/browser/request.test.ts | 3 +- packages/tracing/test/errors.test.ts | 3 +- packages/tracing/test/hub.test.ts | 79 ++++---- packages/tracing/test/idletransaction.test.ts | 5 +- packages/tracing/test/span.test.ts | 11 +- packages/types/src/client.ts | 4 +- packages/types/src/index.ts | 2 +- packages/types/src/options.ts | 136 ++++++++------ packages/utils/src/dsn.ts | 2 - 39 files changed, 685 insertions(+), 451 deletions(-) create mode 100644 packages/browser/test/unit/helper/browser-client-options.ts create mode 100644 packages/browser/test/unit/sdk.test.ts create mode 100644 packages/node/test/helper/node-client-options.ts create mode 100644 packages/node/test/sdk.test.ts diff --git a/packages/browser/src/client.ts b/packages/browser/src/client.ts index 8666eaebe8d5..a889d7f69d06 100644 --- a/packages/browser/src/client.ts +++ b/packages/browser/src/client.ts @@ -1,5 +1,5 @@ import { BaseClient, NewTransport, Scope, SDK_VERSION } from '@sentry/core'; -import { Event, EventHint, Options, Severity, SeverityLevel, Transport } from '@sentry/types'; +import { ClientOptions, Event, EventHint, Options, Severity, SeverityLevel, Transport } from '@sentry/types'; import { getGlobalObject, logger, stackParserFromOptions } from '@sentry/utils'; import { eventFromException, eventFromMessage } from './eventbuilder'; @@ -7,11 +7,7 @@ import { IS_DEBUG_BUILD } from './flags'; import { injectReportDialog, ReportDialogOptions } from './helpers'; import { Breadcrumbs } from './integrations'; -/** - * Configuration options for the Sentry Browser SDK. - * @see BrowserClient for more information. - */ -export interface BrowserOptions extends Options { +export interface BaseBrowserOptions { /** * A pattern for error URLs which should exclusively be sent to Sentry. * This is the opposite of {@link Options.denyUrls}. @@ -27,19 +23,31 @@ export interface BrowserOptions extends Options { denyUrls?: Array; } +/** + * Configuration options for the Sentry Browser SDK. + * @see @sentry/types Options for more information. + */ +export interface BrowserOptions extends Options, BaseBrowserOptions {} + +/** + * Configuration options for the Sentry Browser SDK Client class + * @see BrowserClient for more information. + */ +export interface BrowserClientOptions extends ClientOptions, BaseBrowserOptions {} + /** * The Sentry Browser SDK Client. * * @see BrowserOptions for documentation on configuration options. * @see SentryClient for usage documentation. */ -export class BrowserClient extends BaseClient { +export class BrowserClient extends BaseClient { /** * Creates a new Browser SDK instance. * * @param options Configuration options for this SDK. */ - public constructor(options: BrowserOptions = {}, transport: Transport, newTransport?: NewTransport) { + public constructor(options: BrowserClientOptions, transport: Transport, newTransport?: NewTransport) { options._metadata = options._metadata || {}; options._metadata.sdk = options._metadata.sdk || { name: 'sentry.javascript.browser', @@ -51,7 +59,6 @@ export class BrowserClient extends BaseClient { ], version: SDK_VERSION, }; - super(options, transport, newTransport); } diff --git a/packages/browser/src/sdk.ts b/packages/browser/src/sdk.ts index 94ec87cb4279..05d9d9c34462 100644 --- a/packages/browser/src/sdk.ts +++ b/packages/browser/src/sdk.ts @@ -1,12 +1,20 @@ -import { getCurrentHub, initAndBind, Integrations as CoreIntegrations } from '@sentry/core'; +import { getCurrentHub, getIntegrationsToSetup, initAndBind, Integrations as CoreIntegrations } from '@sentry/core'; import { Hub } from '@sentry/types'; -import { addInstrumentationHandler, getGlobalObject, logger, resolvedSyncPromise } from '@sentry/utils'; +import { + addInstrumentationHandler, + getGlobalObject, + logger, + resolvedSyncPromise, + stackParserFromOptions, + supportsFetch, +} from '@sentry/utils'; -import { BrowserClient, BrowserOptions } from './client'; +import { BrowserClient, BrowserClientOptions, BrowserOptions } from './client'; import { IS_DEBUG_BUILD } from './flags'; import { ReportDialogOptions, wrap as internalWrap } from './helpers'; import { Breadcrumbs, Dedupe, GlobalHandlers, LinkedErrors, TryCatch, UserAgent } from './integrations'; import { defaultStackParsers } from './stack-parsers'; +import { FetchTransport, XHRTransport } from './transports'; import { setupBrowserTransport } from './transports/setup'; export const defaultIntegrations = [ @@ -97,9 +105,17 @@ export function init(options: BrowserOptions = {}): void { if (options.stackParser === undefined) { options.stackParser = defaultStackParsers; } - const { transport, newTransport } = setupBrowserTransport(options); - initAndBind(BrowserClient, options, transport, newTransport); + + const clientOptions: BrowserClientOptions = { + ...options, + stackParser: stackParserFromOptions(options), + integrations: getIntegrationsToSetup(options), + // TODO(v7): get rid of transport being passed down below + transport: options.transport || (supportsFetch() ? FetchTransport : XHRTransport), + }; + + initAndBind(BrowserClient, clientOptions, transport, newTransport); if (options.autoSessionTracking) { startSessionTracking(); diff --git a/packages/browser/src/transports/setup.ts b/packages/browser/src/transports/setup.ts index 0af6aad90676..f72365e7dc94 100644 --- a/packages/browser/src/transports/setup.ts +++ b/packages/browser/src/transports/setup.ts @@ -31,7 +31,10 @@ export interface BrowserTransportOptions extends BaseTransportOptions { * this function will return a ready to use `NewTransport`. */ // TODO(v7): Adjust return value when NewTransport is the default -export function setupBrowserTransport(options: BrowserOptions): { transport: Transport; newTransport?: NewTransport } { +export function setupBrowserTransport(options: BrowserOptions): { + transport: Transport; + newTransport?: NewTransport; +} { if (!options.dsn) { // We return the noop transport here in case there is no Dsn. return { transport: new NoopTransport() }; diff --git a/packages/browser/test/unit/helper/browser-client-options.ts b/packages/browser/test/unit/helper/browser-client-options.ts new file mode 100644 index 000000000000..aa763a5de06b --- /dev/null +++ b/packages/browser/test/unit/helper/browser-client-options.ts @@ -0,0 +1,12 @@ +import { NoopTransport } from '@sentry/core'; + +import { BrowserClientOptions } from '../../../src/client'; + +export function getDefaultBrowserClientOptions(options: Partial = {}): BrowserClientOptions { + return { + integrations: [], + transport: NoopTransport, + stackParser: () => [], + ...options, + }; +} diff --git a/packages/browser/test/unit/index.test.ts b/packages/browser/test/unit/index.test.ts index 9371e60073c1..434dea98977a 100644 --- a/packages/browser/test/unit/index.test.ts +++ b/packages/browser/test/unit/index.test.ts @@ -16,6 +16,7 @@ import { showReportDialog, wrap, } from '../../src'; +import { getDefaultBrowserClientOptions } from './helper/browser-client-options'; import { SimpleTransport } from './mocks/simpletransport'; const dsn = 'https://53039209a22b4ec1bcc296a3c9fdecd6@sentry.io/4291'; @@ -75,7 +76,8 @@ describe('SentryBrowser', () => { describe('showReportDialog', () => { describe('user', () => { const EX_USER = { email: 'test@example.com' }; - const client = new BrowserClient({ dsn }, new SimpleTransport({ dsn })); + const options = getDefaultBrowserClientOptions({ dsn }); + const client = new BrowserClient(options, new SimpleTransport({ dsn })); const reportDialogSpy = jest.spyOn(client, 'showReportDialog'); beforeEach(() => { @@ -139,53 +141,41 @@ describe('SentryBrowser', () => { }); it('should capture a message', done => { - getCurrentHub().bindClient( - new BrowserClient( - { - beforeSend: (event: Event): Event | null => { - expect(event.message).toBe('test'); - expect(event.exception).toBeUndefined(); - done(); - return event; - }, - dsn, - }, - new SimpleTransport({ dsn }), - ), - ); + const options = getDefaultBrowserClientOptions({ + beforeSend: (event: Event): Event | null => { + expect(event.message).toBe('test'); + expect(event.exception).toBeUndefined(); + done(); + return event; + }, + dsn, + }); + getCurrentHub().bindClient(new BrowserClient(options, new SimpleTransport({ dsn }))); captureMessage('test'); }); it('should capture an event', done => { - getCurrentHub().bindClient( - new BrowserClient( - { - beforeSend: (event: Event): Event | null => { - expect(event.message).toBe('event'); - expect(event.exception).toBeUndefined(); - done(); - return event; - }, - dsn, - }, - new SimpleTransport({ dsn }), - ), - ); + const options = getDefaultBrowserClientOptions({ + beforeSend: (event: Event): Event | null => { + expect(event.message).toBe('event'); + expect(event.exception).toBeUndefined(); + done(); + return event; + }, + dsn, + }); + getCurrentHub().bindClient(new BrowserClient(options, new SimpleTransport({ dsn }))); captureEvent({ message: 'event' }); }); it('should not dedupe an event on bound client', async () => { const localBeforeSend = jest.fn(); - getCurrentHub().bindClient( - new BrowserClient( - { - beforeSend: localBeforeSend, - dsn, - integrations: [], - }, - new SimpleTransport({ dsn }), - ), - ); + const options = getDefaultBrowserClientOptions({ + beforeSend: localBeforeSend, + dsn, + integrations: [], + }); + getCurrentHub().bindClient(new BrowserClient(options, new SimpleTransport({ dsn }))); captureMessage('event222'); captureMessage('event222'); @@ -197,16 +187,12 @@ describe('SentryBrowser', () => { it('should use inboundfilter rules of bound client', async () => { const localBeforeSend = jest.fn(); - getCurrentHub().bindClient( - new BrowserClient( - { - beforeSend: localBeforeSend, - dsn, - integrations: [new Integrations.InboundFilters({ ignoreErrors: ['capture'] })], - }, - new SimpleTransport({ dsn }), - ), - ); + const options = getDefaultBrowserClientOptions({ + beforeSend: localBeforeSend, + dsn, + integrations: [new Integrations.InboundFilters({ ignoreErrors: ['capture'] })], + }); + getCurrentHub().bindClient(new BrowserClient(options, new SimpleTransport({ dsn }))); captureMessage('capture'); @@ -267,7 +253,8 @@ describe('SentryBrowser initialization', () => { }); it('should set SDK data when instantiating a client directly', () => { - const client = new BrowserClient({ dsn }, new SimpleTransport({ dsn })); + const options = getDefaultBrowserClientOptions({ dsn }); + const client = new BrowserClient(options, new SimpleTransport({ dsn })); const sdkData = (client.getTransport() as any)._api.metadata?.sdk; @@ -309,20 +296,16 @@ describe('SentryBrowser initialization', () => { describe('wrap()', () => { it('should wrap and call function while capturing error', done => { - getCurrentHub().bindClient( - new BrowserClient( - { - beforeSend: (event: Event): Event | null => { - expect(event.exception!.values![0].type).toBe('TypeError'); - expect(event.exception!.values![0].value).toBe('mkey'); - done(); - return null; - }, - dsn, - }, - new SimpleTransport({ dsn }), - ), - ); + const options = getDefaultBrowserClientOptions({ + beforeSend: (event: Event): Event | null => { + expect(event.exception!.values![0].type).toBe('TypeError'); + expect(event.exception!.values![0].value).toBe('mkey'); + done(); + return null; + }, + dsn, + }); + getCurrentHub().bindClient(new BrowserClient(options, new SimpleTransport({ dsn }))); try { wrap(() => { diff --git a/packages/browser/test/unit/integrations/linkederrors.test.ts b/packages/browser/test/unit/integrations/linkederrors.test.ts index 6ccd4b5975a3..4b862705bccc 100644 --- a/packages/browser/test/unit/integrations/linkederrors.test.ts +++ b/packages/browser/test/unit/integrations/linkederrors.test.ts @@ -5,6 +5,7 @@ import { BrowserClient } from '../../../src/client'; import * as LinkedErrorsModule from '../../../src/integrations/linkederrors'; import { defaultStackParsers } from '../../../src/stack-parsers'; import { setupBrowserTransport } from '../../../src/transports'; +import { getDefaultBrowserClientOptions } from '../helper/browser-client-options'; const parser = createStackParser(...defaultStackParsers); @@ -45,7 +46,7 @@ describe('LinkedErrors', () => { one.cause = two; const originalException = one; - const options = { stackParser: parser }; + const options = getDefaultBrowserClientOptions({ stackParser: parser }); const client = new BrowserClient(options, setupBrowserTransport(options).transport); return client.eventFromException(originalException).then(event => { const result = LinkedErrorsModule._handler(parser, 'cause', 5, event, { @@ -76,7 +77,7 @@ describe('LinkedErrors', () => { one.reason = two; const originalException = one; - const options = { stackParser: parser }; + const options = getDefaultBrowserClientOptions({ stackParser: parser }); const client = new BrowserClient(options, setupBrowserTransport(options).transport); return client.eventFromException(originalException).then(event => { const result = LinkedErrorsModule._handler(parser, 'reason', 5, event, { @@ -103,7 +104,7 @@ describe('LinkedErrors', () => { one.cause = two; two.cause = three; - const options = { stackParser: parser }; + const options = getDefaultBrowserClientOptions({ stackParser: parser }); const client = new BrowserClient(options, setupBrowserTransport(options).transport); const originalException = one; return client.eventFromException(originalException).then(event => { diff --git a/packages/browser/test/unit/sdk.test.ts b/packages/browser/test/unit/sdk.test.ts new file mode 100644 index 000000000000..8814329f16e9 --- /dev/null +++ b/packages/browser/test/unit/sdk.test.ts @@ -0,0 +1,124 @@ +/* eslint-disable @typescript-eslint/unbound-method */ +import { NoopTransport, Scope } from '@sentry/core'; +import { MockIntegration } from '@sentry/core/test/lib/sdk.test'; +import { Client, Integration } from '@sentry/types'; + +import { BrowserOptions } from '../../src'; +import { init } from '../../src/sdk'; +// eslint-disable-next-line no-var +declare var global: any; + +const PUBLIC_DSN = 'https://username@domain/123'; + +function getDefaultBrowserOptions(options: Partial = {}): BrowserOptions { + return { + integrations: [], + transport: NoopTransport, + stackParser: () => [], + ...options, + }; +} + +jest.mock('@sentry/hub', () => { + const original = jest.requireActual('@sentry/hub'); + return { + ...original, + getCurrentHub(): { + bindClient(client: Client): boolean; + getClient(): boolean; + getScope(): Scope; + } { + return { + getClient(): boolean { + return false; + }, + getScope(): Scope { + return new Scope(); + }, + bindClient(client: Client): boolean { + client.setupIntegrations(); + return true; + }, + }; + }, + }; +}); + +describe('init', () => { + beforeEach(() => { + jest.clearAllMocks(); + }); + + afterAll(() => { + jest.resetAllMocks(); + }); + + test('installs default integrations', () => { + const DEFAULT_INTEGRATIONS: Integration[] = [ + new MockIntegration('MockIntegration 0.1'), + new MockIntegration('MockIntegration 0.2'), + ]; + const options = getDefaultBrowserOptions({ dsn: PUBLIC_DSN, defaultIntegrations: DEFAULT_INTEGRATIONS }); + + init(options); + + expect(DEFAULT_INTEGRATIONS[0].setupOnce as jest.Mock).toHaveBeenCalledTimes(1); + expect(DEFAULT_INTEGRATIONS[1].setupOnce as jest.Mock).toHaveBeenCalledTimes(1); + }); + + test("doesn't install default integrations if told not to", () => { + const DEFAULT_INTEGRATIONS: Integration[] = [ + new MockIntegration('MockIntegration 0.3'), + new MockIntegration('MockIntegration 0.4'), + ]; + const options = getDefaultBrowserOptions({ dsn: PUBLIC_DSN, defaultIntegrations: false }); + init(options); + + expect(DEFAULT_INTEGRATIONS[0].setupOnce as jest.Mock).toHaveBeenCalledTimes(0); + expect(DEFAULT_INTEGRATIONS[1].setupOnce as jest.Mock).toHaveBeenCalledTimes(0); + }); + + it('installs merged default integrations, with overrides provided through options', () => { + const DEFAULT_INTEGRATIONS = [ + new MockIntegration('MockIntegration 1.1'), + new MockIntegration('MockIntegration 1.2'), + ]; + + const integrations = [new MockIntegration('MockIntegration 1.1'), new MockIntegration('MockIntegration 1.3')]; + const options = getDefaultBrowserOptions({ + dsn: PUBLIC_DSN, + defaultIntegrations: DEFAULT_INTEGRATIONS, + integrations, + }); + + init(options); + // 'MockIntegration 1' should be overridden by the one with the same name provided through options + expect(DEFAULT_INTEGRATIONS[0].setupOnce as jest.Mock).toHaveBeenCalledTimes(0); + expect(DEFAULT_INTEGRATIONS[1].setupOnce as jest.Mock).toHaveBeenCalledTimes(1); + expect(integrations[0].setupOnce as jest.Mock).toHaveBeenCalledTimes(1); + expect(integrations[1].setupOnce as jest.Mock).toHaveBeenCalledTimes(1); + }); + + it('installs integrations returned from a callback function', () => { + const DEFAULT_INTEGRATIONS = [ + new MockIntegration('MockIntegration 2.1'), + new MockIntegration('MockIntegration 2.2'), + ]; + + const newIntegration = new MockIntegration('MockIntegration 2.3'); + const options = getDefaultBrowserOptions({ + defaultIntegrations: DEFAULT_INTEGRATIONS, + dsn: PUBLIC_DSN, + integrations: (integrations: Integration[]) => { + const t = integrations.slice(0, 1).concat(newIntegration); + return t; + }, + }); + + init(options); + + expect(DEFAULT_INTEGRATIONS[0].setupOnce as jest.Mock).toHaveBeenCalledTimes(1); + expect(newIntegration.setupOnce as jest.Mock).toHaveBeenCalledTimes(1); + expect(DEFAULT_INTEGRATIONS[1].setupOnce as jest.Mock).toHaveBeenCalledTimes(0); + }); +}); diff --git a/packages/browser/test/unit/transports/setup.test.ts b/packages/browser/test/unit/transports/setup.test.ts index 2683a0619aea..41b361d684d5 100644 --- a/packages/browser/test/unit/transports/setup.test.ts +++ b/packages/browser/test/unit/transports/setup.test.ts @@ -7,6 +7,7 @@ import { setupBrowserTransport, XHRTransport, } from '../../../src/transports'; +import { getDefaultBrowserClientOptions } from '../helper/browser-client-options'; import { SimpleTransport } from '../mocks/simpletransport'; const DSN = 'https://username@domain/123'; @@ -64,7 +65,7 @@ describe('setupBrowserTransport', () => { }); it('returns the instantiated transport passed via the options', () => { - const options = { dsn: DSN, transport: SimpleTransport }; + const options = getDefaultBrowserClientOptions({ dsn: DSN, transport: SimpleTransport }); const { transport, newTransport } = setupBrowserTransport(options); expect(transport).toBeDefined(); @@ -73,7 +74,8 @@ describe('setupBrowserTransport', () => { }); it('returns fetchTransports if fetch is supported', () => { - const options = { dsn: DSN }; + const options = getDefaultBrowserClientOptions({ dsn: DSN }); + delete options.transport; const { transport, newTransport } = setupBrowserTransport(options); expect(transport).toBeDefined(); @@ -86,7 +88,8 @@ describe('setupBrowserTransport', () => { it('returns xhrTransports if fetch is not supported', () => { fetchSupported = false; - const options = { dsn: DSN }; + const options = getDefaultBrowserClientOptions({ dsn: DSN }); + delete options.transport; const { transport, newTransport } = setupBrowserTransport(options); expect(transport).toBeDefined(); diff --git a/packages/core/src/baseclient.ts b/packages/core/src/baseclient.ts index 2888e9853f5e..3d82d829cead 100644 --- a/packages/core/src/baseclient.ts +++ b/packages/core/src/baseclient.ts @@ -2,12 +2,12 @@ import { Scope, Session } from '@sentry/hub'; import { Client, + ClientOptions, DsnComponents, Event, EventHint, Integration, IntegrationClass, - Options, Severity, SeverityLevel, Transport, @@ -68,7 +68,7 @@ const ALREADY_SEEN_ERROR = "Not capturing exception because it's already been ca * // ... * } */ -export abstract class BaseClient implements Client { +export abstract class BaseClient implements Client { /** Options passed to the SDK. */ protected readonly _options: O; diff --git a/packages/core/src/index.ts b/packages/core/src/index.ts index ccf2aa519775..da68917c936e 100644 --- a/packages/core/src/index.ts +++ b/packages/core/src/index.ts @@ -37,6 +37,7 @@ export { initAndBind } from './sdk'; export { NoopTransport } from './transports/noop'; export { createTransport } from './transports/base'; export { SDK_VERSION } from './version'; +export { getIntegrationsToSetup } from './integration'; import * as Integrations from './integrations'; diff --git a/packages/core/src/integration.ts b/packages/core/src/integration.ts index cc694c815289..b4f35e7d5b1e 100644 --- a/packages/core/src/integration.ts +++ b/packages/core/src/integration.ts @@ -1,5 +1,5 @@ import { addGlobalEventProcessor, getCurrentHub } from '@sentry/hub'; -import { Integration, Options } from '@sentry/types'; +import { ClientOptions, Integration, Options } from '@sentry/types'; import { addNonEnumerableProperty, logger } from '@sentry/utils'; import { IS_DEBUG_BUILD } from './flags'; @@ -70,9 +70,9 @@ export function setupIntegration(integration: Integration): void { * @param integrations array of integration instances * @param withDefault should enable default integrations */ -export function setupIntegrations(options: O): IntegrationIndex { +export function setupIntegrations(options: O): IntegrationIndex { const integrations: IntegrationIndex = {}; - getIntegrationsToSetup(options).forEach(integration => { + options.integrations.forEach(integration => { integrations[integration.name] = integration; setupIntegration(integration); }); diff --git a/packages/core/src/sdk.ts b/packages/core/src/sdk.ts index 97c8b349a235..c7f7bb4916a3 100644 --- a/packages/core/src/sdk.ts +++ b/packages/core/src/sdk.ts @@ -1,12 +1,12 @@ import { getCurrentHub } from '@sentry/hub'; -import { Client, Options, Transport } from '@sentry/types'; +import { Client, ClientOptions, Transport } from '@sentry/types'; import { logger } from '@sentry/utils'; import { IS_DEBUG_BUILD } from './flags'; import { NewTransport } from './transports/base'; /** A class object that can instantiate Client objects. */ -export type ClientClass = new ( +export type ClientClass = new ( options: O, transport: Transport, newTransport?: NewTransport, @@ -19,7 +19,7 @@ export type ClientClass = new ( * @param clientClass The client class to instantiate. * @param options Options to pass to the client. */ -export function initAndBind( +export function initAndBind( clientClass: ClientClass, options: O, transport: Transport, diff --git a/packages/core/test/lib/base.test.ts b/packages/core/test/lib/base.test.ts index 1bd2750e5378..47ec8ae70266 100644 --- a/packages/core/test/lib/base.test.ts +++ b/packages/core/test/lib/base.test.ts @@ -4,7 +4,7 @@ import { dsnToString, logger, SentryError, SyncPromise } from '@sentry/utils'; import * as integrationModule from '../../src/integration'; import { NoopTransport } from '../../src/transports/noop'; -import { setupTestTransport, TestClient } from '../mocks/client'; +import { getDefaultTestClientOptions, setupTestTransport, TestClient } from '../mocks/client'; import { TestIntegration } from '../mocks/integration'; import { FakeTransport } from '../mocks/transport'; @@ -67,7 +67,7 @@ describe('BaseClient', () => { test('returns the Dsn', () => { expect.assertions(1); - const options = { dsn: PUBLIC_DSN }; + const options = getDefaultTestClientOptions({ dsn: PUBLIC_DSN }); const client = new TestClient(options, setupTestTransport(options).transport); expect(dsnToString(client.getDsn()!)).toBe(PUBLIC_DSN); }); @@ -75,7 +75,7 @@ describe('BaseClient', () => { test('allows missing Dsn', () => { expect.assertions(1); - const options = {}; + const options = getDefaultTestClientOptions(); const client = new TestClient(options, setupTestTransport(options).transport); expect(client.getDsn()).toBeUndefined(); @@ -84,7 +84,7 @@ describe('BaseClient', () => { test('throws with invalid Dsn', () => { expect.assertions(1); - const options = { dsn: 'abc' }; + const options = getDefaultTestClientOptions({ dsn: 'abc' }); expect(() => new TestClient(options, setupTestTransport(options).transport)).toThrow(SentryError); }); }); @@ -93,7 +93,7 @@ describe('BaseClient', () => { test('returns the options', () => { expect.assertions(1); - const options = { dsn: PUBLIC_DSN, test: true }; + const options = getDefaultTestClientOptions({ dsn: PUBLIC_DSN, test: true }); const client = new TestClient(options, setupTestTransport(options).transport); expect(client.getOptions()).toEqual(options); @@ -104,7 +104,7 @@ describe('BaseClient', () => { test('returns the transport from client', () => { expect.assertions(2); - const options = { dsn: PUBLIC_DSN, transport: FakeTransport }; + const options = getDefaultTestClientOptions({ dsn: PUBLIC_DSN, transport: FakeTransport }); const client = new TestClient(options, new FakeTransport()); expect(client.getTransport()).toBeInstanceOf(FakeTransport); @@ -114,7 +114,7 @@ describe('BaseClient', () => { test('retruns NoopTransport when no transport is passed', () => { expect.assertions(2); - const options = { dsn: PUBLIC_DSN }; + const options = getDefaultTestClientOptions({ dsn: PUBLIC_DSN }); const client = new TestClient(options, setupTestTransport(options).transport); expect(client.getTransport()).toBeInstanceOf(NoopTransport); @@ -126,7 +126,7 @@ describe('BaseClient', () => { test('adds a breadcrumb', () => { expect.assertions(1); - const options = {}; + const options = getDefaultTestClientOptions({}); const client = new TestClient(options, setupTestTransport(options).transport); const scope = new Scope(); const hub = new Hub(client, scope); @@ -140,7 +140,7 @@ describe('BaseClient', () => { test('adds a timestamp to new breadcrumbs', () => { expect.assertions(1); - const options = {}; + const options = getDefaultTestClientOptions({}); const client = new TestClient(options, setupTestTransport(options).transport); const scope = new Scope(); const hub = new Hub(client, scope); @@ -154,7 +154,7 @@ describe('BaseClient', () => { test('discards breadcrumbs beyond maxBreadcrumbs', () => { expect.assertions(2); - const options = { maxBreadcrumbs: 1 }; + const options = getDefaultTestClientOptions({ maxBreadcrumbs: 1 }); const client = new TestClient(options, setupTestTransport(options).transport); const scope = new Scope(); const hub = new Hub(client, scope); @@ -169,7 +169,7 @@ describe('BaseClient', () => { test('allows concurrent updates', () => { expect.assertions(1); - const options = {}; + const options = getDefaultTestClientOptions({}); const client = new TestClient(options, setupTestTransport(options).transport); const scope = new Scope(); const hub = new Hub(client, scope); @@ -184,7 +184,7 @@ describe('BaseClient', () => { expect.assertions(1); const beforeBreadcrumb = jest.fn(breadcrumb => breadcrumb); - const options = { beforeBreadcrumb }; + const options = getDefaultTestClientOptions({ beforeBreadcrumb }); const client = new TestClient(options, setupTestTransport(options).transport); const scope = new Scope(); const hub = new Hub(client, scope); @@ -198,7 +198,7 @@ describe('BaseClient', () => { expect.assertions(1); const beforeBreadcrumb = jest.fn(() => ({ message: 'changed' })); - const options = { beforeBreadcrumb }; + const options = getDefaultTestClientOptions({ beforeBreadcrumb }); const client = new TestClient(options, setupTestTransport(options).transport); const scope = new Scope(); const hub = new Hub(client, scope); @@ -212,7 +212,7 @@ describe('BaseClient', () => { expect.assertions(1); const beforeBreadcrumb = jest.fn(() => null); - const options = { beforeBreadcrumb }; + const options = getDefaultTestClientOptions({ beforeBreadcrumb }); const client = new TestClient(options, setupTestTransport(options).transport); const scope = new Scope(); const hub = new Hub(client, scope); @@ -226,7 +226,7 @@ describe('BaseClient', () => { expect.assertions(2); const beforeBreadcrumb = jest.fn((breadcrumb, hint) => ({ ...breadcrumb, data: hint.data })); - const options = { beforeBreadcrumb }; + const options = getDefaultTestClientOptions({ beforeBreadcrumb }); const client = new TestClient(options, setupTestTransport(options).transport); const scope = new Scope(); const hub = new Hub(client, scope); @@ -240,7 +240,7 @@ describe('BaseClient', () => { describe('captureException', () => { test('captures and sends exceptions', () => { - const options = { dsn: PUBLIC_DSN }; + const options = getDefaultTestClientOptions({ dsn: PUBLIC_DSN }); const client = new TestClient(options, setupTestTransport(options).transport); client.captureException(new Error('test exception')); @@ -263,7 +263,7 @@ describe('BaseClient', () => { }); test('allows for providing explicit scope', () => { - const options = { dsn: PUBLIC_DSN }; + const options = getDefaultTestClientOptions({ dsn: PUBLIC_DSN }); const client = new TestClient(options, setupTestTransport(options).transport); const scope = new Scope(); scope.setExtra('foo', 'wat'); @@ -291,7 +291,7 @@ describe('BaseClient', () => { }); test('allows for clearing data from existing scope if explicit one does so in a callback function', () => { - const options = { dsn: PUBLIC_DSN }; + const options = getDefaultTestClientOptions({ dsn: PUBLIC_DSN }); const client = new TestClient(options, setupTestTransport(options).transport); const scope = new Scope(); scope.setExtra('foo', 'wat'); @@ -326,7 +326,7 @@ describe('BaseClient', () => { // already-seen check to work . Any primitive which is passed without being wrapped will be captured each time it // is encountered, so this test doesn't apply. ])("doesn't capture the same exception twice - %s", (_name: string, thrown: any) => { - const options = { dsn: PUBLIC_DSN }; + const options = getDefaultTestClientOptions({ dsn: PUBLIC_DSN }); const client = new TestClient(options, setupTestTransport(options).transport); expect(thrown.__sentry_captured__).toBeUndefined(); @@ -345,7 +345,7 @@ describe('BaseClient', () => { describe('captureMessage', () => { test('captures and sends messages', () => { - const options = { dsn: PUBLIC_DSN }; + const options = getDefaultTestClientOptions({ dsn: PUBLIC_DSN }); const client = new TestClient(options, setupTestTransport(options).transport); client.captureMessage('test message'); @@ -362,7 +362,7 @@ describe('BaseClient', () => { }); test('should call eventFromException if input to captureMessage is not a primitive', () => { - const options = { dsn: PUBLIC_DSN }; + const options = getDefaultTestClientOptions({ dsn: PUBLIC_DSN }); const client = new TestClient(options, setupTestTransport(options).transport); const spy = jest.spyOn(TestClient.instance!, 'eventFromException'); @@ -381,7 +381,7 @@ describe('BaseClient', () => { }); test('allows for providing explicit scope', () => { - const options = { dsn: PUBLIC_DSN }; + const options = getDefaultTestClientOptions({ dsn: PUBLIC_DSN }); const client = new TestClient(options, setupTestTransport(options).transport); const scope = new Scope(); scope.setExtra('foo', 'wat'); @@ -415,7 +415,7 @@ describe('BaseClient', () => { test('skips when disabled', () => { expect.assertions(1); - const options = { enabled: false, dsn: PUBLIC_DSN }; + const options = getDefaultTestClientOptions({ enabled: false, dsn: PUBLIC_DSN }); const client = new TestClient(options, setupTestTransport(options).transport); const scope = new Scope(); @@ -427,7 +427,7 @@ describe('BaseClient', () => { test('skips without a Dsn', () => { expect.assertions(1); - const options = {}; + const options = getDefaultTestClientOptions({}); const client = new TestClient(options, setupTestTransport(options).transport); const scope = new Scope(); @@ -445,7 +445,7 @@ describe('BaseClient', () => { // already-seen check to work . Any primitive which is passed without being wrapped will be captured each time it // is encountered, so this test doesn't apply. ])("doesn't capture an event wrapping the same exception twice - %s", (_name: string, thrown: any) => { - const options = { dsn: PUBLIC_DSN }; + const options = getDefaultTestClientOptions({ dsn: PUBLIC_DSN }); // Note: this is the same test as in `describe(captureException)`, except with the exception already wrapped in a // hint and accompanying an event. Duplicated here because some methods skip `captureException` and go straight to // `captureEvent`. @@ -469,7 +469,7 @@ describe('BaseClient', () => { test('sends an event', () => { expect.assertions(2); - const options = { dsn: PUBLIC_DSN }; + const options = getDefaultTestClientOptions({ dsn: PUBLIC_DSN }); const client = new TestClient(options, setupTestTransport(options).transport); const scope = new Scope(); @@ -489,7 +489,7 @@ describe('BaseClient', () => { test('does not overwrite existing timestamp', () => { expect.assertions(2); - const options = { dsn: PUBLIC_DSN }; + const options = getDefaultTestClientOptions({ dsn: PUBLIC_DSN }); const client = new TestClient(options, setupTestTransport(options).transport); const scope = new Scope(); @@ -509,7 +509,7 @@ describe('BaseClient', () => { test('adds event_id from hint if available', () => { expect.assertions(1); - const options = { dsn: PUBLIC_DSN }; + const options = getDefaultTestClientOptions({ dsn: PUBLIC_DSN }); const client = new TestClient(options, setupTestTransport(options).transport); const scope = new Scope(); @@ -528,9 +528,7 @@ describe('BaseClient', () => { test('sets default environment to `production` if none provided', () => { expect.assertions(1); - const options = { - dsn: PUBLIC_DSN, - }; + const options = getDefaultTestClientOptions({ dsn: PUBLIC_DSN }); const client = new TestClient(options, setupTestTransport(options).transport); const scope = new Scope(); @@ -549,10 +547,7 @@ describe('BaseClient', () => { test('adds the configured environment', () => { expect.assertions(1); - const options = { - dsn: PUBLIC_DSN, - environment: 'env', - }; + const options = getDefaultTestClientOptions({ environment: 'env', dsn: PUBLIC_DSN }); const client = new TestClient(options, setupTestTransport(options).transport); const scope = new Scope(); @@ -571,10 +566,7 @@ describe('BaseClient', () => { test('allows for environment to be explicitly set to falsy value', () => { expect.assertions(1); - const options = { - dsn: PUBLIC_DSN, - environment: undefined, - }; + const options = getDefaultTestClientOptions({ dsn: PUBLIC_DSN, environment: undefined }); const client = new TestClient(options, setupTestTransport(options).transport); const scope = new Scope(); @@ -593,10 +585,7 @@ describe('BaseClient', () => { test('adds the configured release', () => { expect.assertions(1); - const options = { - dsn: PUBLIC_DSN, - release: 'v1.0.0', - }; + const options = getDefaultTestClientOptions({ dsn: PUBLIC_DSN, release: 'v1.0.0' }); const client = new TestClient(options, setupTestTransport(options).transport); const scope = new Scope(); @@ -616,7 +605,7 @@ describe('BaseClient', () => { test('adds breadcrumbs', () => { expect.assertions(4); - const options = { dsn: PUBLIC_DSN }; + const options = getDefaultTestClientOptions({ dsn: PUBLIC_DSN }); const client = new TestClient(options, setupTestTransport(options).transport); const scope = new Scope(); scope.addBreadcrumb({ message: 'breadcrumb' }, 100); @@ -632,7 +621,7 @@ describe('BaseClient', () => { test('limits previously saved breadcrumbs', () => { expect.assertions(2); - const options = { dsn: PUBLIC_DSN, maxBreadcrumbs: 1 }; + const options = getDefaultTestClientOptions({ dsn: PUBLIC_DSN, maxBreadcrumbs: 1 }); const client = new TestClient(options, setupTestTransport(options).transport); const scope = new Scope(); const hub = new Hub(client, scope); @@ -648,7 +637,7 @@ describe('BaseClient', () => { test('adds context data', () => { expect.assertions(1); - const options = { dsn: PUBLIC_DSN }; + const options = getDefaultTestClientOptions({ dsn: PUBLIC_DSN }); const client = new TestClient(options, setupTestTransport(options).transport); const scope = new Scope(); scope.setExtra('b', 'b'); @@ -673,7 +662,7 @@ describe('BaseClient', () => { test('adds fingerprint', () => { expect.assertions(1); - const options = { dsn: PUBLIC_DSN }; + const options = getDefaultTestClientOptions({ dsn: PUBLIC_DSN }); const client = new TestClient(options, setupTestTransport(options).transport); const scope = new Scope(); scope.setFingerprint(['abcd']); @@ -692,7 +681,7 @@ describe('BaseClient', () => { }); test('adds installed integrations to sdk info', () => { - const options = { dsn: PUBLIC_DSN, integrations: [new TestIntegration()] }; + const options = getDefaultTestClientOptions({ dsn: PUBLIC_DSN, integrations: [new TestIntegration()] }); const client = new TestClient(options, setupTestTransport(options).transport); client.setupIntegrations(); @@ -706,7 +695,7 @@ describe('BaseClient', () => { test('normalizes event with default depth of 3', () => { expect.assertions(1); - const options = { dsn: PUBLIC_DSN }; + const options = getDefaultTestClientOptions({ dsn: PUBLIC_DSN }); const client = new TestClient(options, setupTestTransport(options).transport); const fourLevelsObject = { a: { @@ -758,10 +747,7 @@ describe('BaseClient', () => { test('normalization respects `normalizeDepth` option', () => { expect.assertions(1); - const options = { - dsn: PUBLIC_DSN, - normalizeDepth: 2, - }; + const options = getDefaultTestClientOptions({ dsn: PUBLIC_DSN, normalizeDepth: 2 }); const client = new TestClient(options, setupTestTransport(options).transport); const fourLevelsObject = { a: { @@ -810,10 +796,7 @@ describe('BaseClient', () => { test('skips normalization when `normalizeDepth: 0`', () => { expect.assertions(1); - const options = { - dsn: PUBLIC_DSN, - normalizeDepth: 0, - }; + const options = getDefaultTestClientOptions({ dsn: PUBLIC_DSN, normalizeDepth: 0 }); const client = new TestClient(options, setupTestTransport(options).transport); const fourLevelsObject = { a: { @@ -867,7 +850,7 @@ describe('BaseClient', () => { test('normalization applies to Transaction and Span consistently', () => { expect.assertions(1); - const options = { dsn: PUBLIC_DSN }; + const options = getDefaultTestClientOptions({ dsn: PUBLIC_DSN }); const client = new TestClient(options, setupTestTransport(options).transport); const transaction: Event = { contexts: { @@ -942,7 +925,7 @@ describe('BaseClient', () => { expect.assertions(1); const beforeSend = jest.fn(event => event); - const options = { dsn: PUBLIC_DSN, beforeSend }; + const options = getDefaultTestClientOptions({ dsn: PUBLIC_DSN, beforeSend }); const client = new TestClient(options, setupTestTransport(options).transport); client.captureEvent({ message: 'hello' }); @@ -954,7 +937,7 @@ describe('BaseClient', () => { expect.assertions(1); const beforeSend = jest.fn(() => ({ message: 'changed1' })); - const options = { dsn: PUBLIC_DSN, beforeSend }; + const options = getDefaultTestClientOptions({ dsn: PUBLIC_DSN, beforeSend }); const client = new TestClient(options, setupTestTransport(options).transport); client.captureEvent({ message: 'hello' }); @@ -966,7 +949,8 @@ describe('BaseClient', () => { expect.assertions(3); const beforeSend = jest.fn(() => null); - const client = new TestClient({ dsn: PUBLIC_DSN, beforeSend }, setupTestTransport({ dsn: PUBLIC_DSN }).transport); + const options = getDefaultTestClientOptions({ dsn: PUBLIC_DSN, beforeSend }); + const client = new TestClient(options, setupTestTransport(options).transport); const captureExceptionSpy = jest.spyOn(client, 'captureException'); const loggerErrorSpy = jest.spyOn(logger, 'error'); @@ -983,9 +967,9 @@ describe('BaseClient', () => { for (const val of invalidValues) { const beforeSend = jest.fn(() => val); - const options = { dsn: PUBLIC_DSN, beforeSend }; // @ts-ignore we need to test regular-js behavior - const client = new TestClient(options, setupTestTransport({ dsn: PUBLIC_DSN }).transport); + const options = getDefaultTestClientOptions({ dsn: PUBLIC_DSN, beforeSend }); + const client = new TestClient(options, setupTestTransport(options).transport); const loggerErrorSpy = jest.spyOn(logger, 'error'); client.captureEvent({ message: 'hello' }); @@ -1009,7 +993,7 @@ describe('BaseClient', () => { }, 1); }), ); - const options = { dsn: PUBLIC_DSN, beforeSend }; + const options = getDefaultTestClientOptions({ dsn: PUBLIC_DSN, beforeSend }); const client = new TestClient(options, setupTestTransport(options).transport); client.captureEvent({ message: 'hello' }); @@ -1038,7 +1022,7 @@ describe('BaseClient', () => { }, 1); }), ); - const options = { dsn: PUBLIC_DSN, beforeSend }; + const options = getDefaultTestClientOptions({ dsn: PUBLIC_DSN, beforeSend }); const client = new TestClient(options, setupTestTransport(options).transport); client.captureEvent({ message: 'hello' }); @@ -1067,7 +1051,7 @@ describe('BaseClient', () => { }); }), ); - const options = { dsn: PUBLIC_DSN, beforeSend }; + const options = getDefaultTestClientOptions({ dsn: PUBLIC_DSN, beforeSend }); const client = new TestClient(options, setupTestTransport(options).transport); client.captureEvent({ message: 'hello' }); @@ -1080,7 +1064,7 @@ describe('BaseClient', () => { expect.assertions(2); const beforeSend = jest.fn((event, hint) => ({ ...event, data: hint.data })); - const options = { dsn: PUBLIC_DSN, beforeSend }; + const options = getDefaultTestClientOptions({ dsn: PUBLIC_DSN, beforeSend }); const client = new TestClient(options, setupTestTransport(options).transport); client.captureEvent({ message: 'hello' }, { data: 'someRandomThing' }); @@ -1093,13 +1077,13 @@ describe('BaseClient', () => { expect.assertions(1); const client = new TestClient( - { + getDefaultTestClientOptions({ dsn: PUBLIC_DSN, beforeSend() { return null; }, - }, - setupTestTransport({ dsn: PUBLIC_DSN }).transport, + }), + setupTestTransport(getDefaultTestClientOptions({ dsn: PUBLIC_DSN })).transport, ); const recordLostEventSpy = jest.fn(); jest.spyOn(client, 'getTransport').mockImplementationOnce( @@ -1117,7 +1101,10 @@ describe('BaseClient', () => { test('eventProcessor can drop the even when it returns null', () => { expect.assertions(3); - const client = new TestClient({ dsn: PUBLIC_DSN }, setupTestTransport({ dsn: PUBLIC_DSN }).transport); + const client = new TestClient( + getDefaultTestClientOptions({ dsn: PUBLIC_DSN }), + setupTestTransport(getDefaultTestClientOptions({ dsn: PUBLIC_DSN })).transport, + ); const captureExceptionSpy = jest.spyOn(client, 'captureException'); const loggerErrorSpy = jest.spyOn(logger, 'error'); const scope = new Scope(); @@ -1133,7 +1120,7 @@ describe('BaseClient', () => { test('eventProcessor records dropped events', () => { expect.assertions(1); - const options = { dsn: PUBLIC_DSN }; + const options = getDefaultTestClientOptions({ dsn: PUBLIC_DSN }); const client = new TestClient(options, setupTestTransport(options).transport); const recordLostEventSpy = jest.fn(); @@ -1155,7 +1142,7 @@ describe('BaseClient', () => { test('eventProcessor sends an event and logs when it crashes', () => { expect.assertions(3); - const options = { dsn: PUBLIC_DSN }; + const options = getDefaultTestClientOptions({ dsn: PUBLIC_DSN }); const client = new TestClient(options, setupTestTransport(options).transport); const captureExceptionSpy = jest.spyOn(client, 'captureException'); const loggerErrorSpy = jest.spyOn(logger, 'error'); @@ -1184,10 +1171,7 @@ describe('BaseClient', () => { test('records events dropped due to sampleRate', () => { expect.assertions(1); - const options = { - dsn: PUBLIC_DSN, - sampleRate: 0, - }; + const options = getDefaultTestClientOptions({ dsn: PUBLIC_DSN, sampleRate: 0 }); const client = new TestClient(options, setupTestTransport(options).transport); const recordLostEventSpy = jest.fn(); @@ -1211,10 +1195,7 @@ describe('BaseClient', () => { test('setup each one of them on setupIntegration call', () => { expect.assertions(2); - const options = { - dsn: PUBLIC_DSN, - integrations: [new TestIntegration()], - }; + const options = getDefaultTestClientOptions({ dsn: PUBLIC_DSN, integrations: [new TestIntegration()] }); const client = new TestClient(options, setupTestTransport(options).transport); client.setupIntegrations(); @@ -1225,9 +1206,7 @@ describe('BaseClient', () => { test('skips installation if DSN is not provided', () => { expect.assertions(2); - const options = { - integrations: [new TestIntegration()], - }; + const options = getDefaultTestClientOptions({ integrations: [new TestIntegration()] }); const client = new TestClient(options, setupTestTransport(options).transport); client.setupIntegrations(); @@ -1238,11 +1217,11 @@ describe('BaseClient', () => { test('skips installation if enabled is set to false', () => { expect.assertions(2); - const options = { + const options = getDefaultTestClientOptions({ dsn: PUBLIC_DSN, enabled: false, integrations: [new TestIntegration()], - }; + }); const client = new TestClient(options, setupTestTransport(options).transport); client.setupIntegrations(); @@ -1253,10 +1232,7 @@ describe('BaseClient', () => { test('skips installation if integrations are already installed', () => { expect.assertions(4); - const options = { - dsn: PUBLIC_DSN, - integrations: [new TestIntegration()], - }; + const options = getDefaultTestClientOptions({ dsn: PUBLIC_DSN, integrations: [new TestIntegration()] }); const client = new TestClient(options, setupTestTransport(options).transport); // note: not the `Client` method `setupIntegrations`, but the free-standing function which that method calls const setupIntegrationsHelper = jest.spyOn(integrationModule, 'setupIntegrations'); @@ -1281,11 +1257,11 @@ describe('BaseClient', () => { expect.assertions(5); const client = new TestClient( - { + getDefaultTestClientOptions({ dsn: PUBLIC_DSN, enableSend: true, transport: FakeTransport, - }, + }), new FakeTransport(), ); @@ -1310,11 +1286,11 @@ describe('BaseClient', () => { expect.assertions(5); const client = new TestClient( - { + getDefaultTestClientOptions({ dsn: PUBLIC_DSN, enableSend: true, transport: FakeTransport, - }, + }), new FakeTransport(), ); @@ -1349,11 +1325,11 @@ describe('BaseClient', () => { expect.assertions(2); const client = new TestClient( - { + getDefaultTestClientOptions({ dsn: PUBLIC_DSN, enableSend: true, transport: FakeTransport, - }, + }), new FakeTransport(), ); @@ -1373,7 +1349,7 @@ describe('BaseClient', () => { jest.useRealTimers(); expect.assertions(3); - const options = { dsn: PUBLIC_DSN }; + const options = getDefaultTestClientOptions({ dsn: PUBLIC_DSN }); const client = new TestClient(options, setupTestTransport(options).transport); return Promise.all([ @@ -1394,7 +1370,7 @@ describe('BaseClient', () => { test('sends sessions to the client', () => { expect.assertions(1); - const options = { dsn: PUBLIC_DSN }; + const options = getDefaultTestClientOptions({ dsn: PUBLIC_DSN }); const client = new TestClient(options, setupTestTransport(options).transport); const session = new Session({ release: 'test' }); @@ -1406,7 +1382,7 @@ describe('BaseClient', () => { test('skips when disabled', () => { expect.assertions(1); - const options = { enabled: false, dsn: PUBLIC_DSN }; + const options = getDefaultTestClientOptions({ enabled: false, dsn: PUBLIC_DSN }); const client = new TestClient(options, setupTestTransport(options).transport); const session = new Session({ release: 'test' }); diff --git a/packages/core/test/lib/sdk.test.ts b/packages/core/test/lib/sdk.test.ts index 578361a97511..7dd3229c5c7e 100644 --- a/packages/core/test/lib/sdk.test.ts +++ b/packages/core/test/lib/sdk.test.ts @@ -3,7 +3,7 @@ import { Client, Integration } from '@sentry/types'; import { installedIntegrations } from '../../src/integration'; import { initAndBind } from '../../src/sdk'; -import { setupTestTransport, TestClient, TestOptions } from '../mocks/client'; +import { getDefaultTestClientOptions, setupTestTransport, TestClient } from '../mocks/client'; // eslint-disable-next-line no-var declare var global: any; @@ -35,7 +35,7 @@ jest.mock('@sentry/hub', () => { }; }); -class MockIntegration implements Integration { +export class MockIntegration implements Integration { public name: string; public setupOnce: () => void = jest.fn(); public constructor(name: string) { @@ -50,72 +50,15 @@ describe('SDK', () => { }); describe('initAndBind', () => { - test('installs default integrations', () => { - const DEFAULT_INTEGRATIONS: Integration[] = [ - new MockIntegration('MockIntegration 1'), - new MockIntegration('MockIntegration 2'), - ]; - const options = { dsn: PUBLIC_DSN, defaultIntegrations: DEFAULT_INTEGRATIONS }; - initAndBind(TestClient, options, setupTestTransport(options).transport); - expect((DEFAULT_INTEGRATIONS[0].setupOnce as jest.Mock).mock.calls.length).toBe(1); - expect((DEFAULT_INTEGRATIONS[1].setupOnce as jest.Mock).mock.calls.length).toBe(1); - }); - - test("doesn't install default integrations if told not to", () => { - const DEFAULT_INTEGRATIONS: Integration[] = [ - new MockIntegration('MockIntegration 1'), - new MockIntegration('MockIntegration 2'), - ]; - const options: TestOptions = { dsn: PUBLIC_DSN, defaultIntegrations: false }; - initAndBind(TestClient, options, setupTestTransport(options).transport); - expect((DEFAULT_INTEGRATIONS[0].setupOnce as jest.Mock).mock.calls.length).toBe(0); - expect((DEFAULT_INTEGRATIONS[1].setupOnce as jest.Mock).mock.calls.length).toBe(0); - }); - test('installs integrations provided through options', () => { const integrations: Integration[] = [ new MockIntegration('MockIntegration 1'), new MockIntegration('MockIntegration 2'), ]; - const options = { dsn: PUBLIC_DSN, integrations }; - initAndBind(TestClient, options, setupTestTransport(options).transport); - expect((integrations[0].setupOnce as jest.Mock).mock.calls.length).toBe(1); - expect((integrations[1].setupOnce as jest.Mock).mock.calls.length).toBe(1); - }); - - test('installs merged default integrations, with overrides provided through options', () => { - const DEFAULT_INTEGRATIONS: Integration[] = [ - new MockIntegration('MockIntegration 1'), - new MockIntegration('MockIntegration 2'), - ]; - const integrations: Integration[] = [ - new MockIntegration('MockIntegration 1'), - new MockIntegration('MockIntegration 3'), - ]; - const options = { dsn: PUBLIC_DSN, defaultIntegrations: DEFAULT_INTEGRATIONS, integrations }; + const options = getDefaultTestClientOptions({ dsn: PUBLIC_DSN, integrations }); initAndBind(TestClient, options, setupTestTransport(options).transport); - // 'MockIntegration 1' should be overridden by the one with the same name provided through options - expect((DEFAULT_INTEGRATIONS[0].setupOnce as jest.Mock).mock.calls.length).toBe(0); - expect((DEFAULT_INTEGRATIONS[1].setupOnce as jest.Mock).mock.calls.length).toBe(1); expect((integrations[0].setupOnce as jest.Mock).mock.calls.length).toBe(1); expect((integrations[1].setupOnce as jest.Mock).mock.calls.length).toBe(1); }); - - test('installs integrations returned from a callback function', () => { - const DEFAULT_INTEGRATIONS: Integration[] = [ - new MockIntegration('MockIntegration 1'), - new MockIntegration('MockIntegration 2'), - ]; - const newIntegration = new MockIntegration('MockIntegration 3'); - const options = { - defaultIntegrations: DEFAULT_INTEGRATIONS, - dsn: PUBLIC_DSN, - integrations: (integrations: Integration[]) => integrations.slice(0, 1).concat(newIntegration), - }; - initAndBind(TestClient, options, setupTestTransport(options).transport); - expect((DEFAULT_INTEGRATIONS[0].setupOnce as jest.Mock).mock.calls.length).toBe(1); - expect((newIntegration.setupOnce as jest.Mock).mock.calls.length).toBe(1); - expect((DEFAULT_INTEGRATIONS[1].setupOnce as jest.Mock).mock.calls.length).toBe(0); - }); }); }); diff --git a/packages/core/test/mocks/client.ts b/packages/core/test/mocks/client.ts index 707513ff91b1..5778dcf5e193 100644 --- a/packages/core/test/mocks/client.ts +++ b/packages/core/test/mocks/client.ts @@ -1,26 +1,36 @@ import { Session } from '@sentry/hub'; -import { Event, Integration, Options, Severity, SeverityLevel, Transport } from '@sentry/types'; +import { ClientOptions, Event, Integration, Severity, SeverityLevel, Transport } from '@sentry/types'; import { resolvedSyncPromise } from '@sentry/utils'; import { BaseClient } from '../../src/baseclient'; import { initAndBind } from '../../src/sdk'; import { NewTransport } from '../../src/transports/base'; import { NoopTransport } from '../../src/transports/noop'; -export interface TestOptions extends Options { + +export function getDefaultTestClientOptions(options: Partial = {}): TestClientOptions { + return { + integrations: [], + transport: NoopTransport, + stackParser: () => [], + ...options, + }; +} + +export interface TestClientOptions extends ClientOptions { test?: boolean; mockInstallFailure?: boolean; enableSend?: boolean; defaultIntegrations?: Integration[] | false; } -export class TestClient extends BaseClient { +export class TestClient extends BaseClient { public static instance?: TestClient; public static sendEventCalled?: (event: Event) => void; public event?: Event; public session?: Session; - public constructor(options: TestOptions, transport: Transport, newTransport?: NewTransport) { + public constructor(options: TestClientOptions, transport: Transport, newTransport?: NewTransport) { super(options, transport, newTransport); TestClient.instance = this; } @@ -64,11 +74,11 @@ export class TestClient extends BaseClient { } } -export function init(options: TestOptions, transport: Transport, newTransport?: NewTransport): void { +export function init(options: TestClientOptions, transport: Transport, newTransport?: NewTransport): void { initAndBind(TestClient, options, transport, newTransport); } -export function setupTestTransport(options: TestOptions): { transport: Transport; newTransport?: NewTransport } { +export function setupTestTransport(options: TestClientOptions): { transport: Transport; newTransport?: NewTransport } { const noop = { transport: new NoopTransport() }; if (!options.dsn) { diff --git a/packages/node/src/client.ts b/packages/node/src/client.ts index e660c6c7b421..7864fd1b7ccc 100644 --- a/packages/node/src/client.ts +++ b/packages/node/src/client.ts @@ -5,22 +5,22 @@ import { logger, resolvedSyncPromise, stackParserFromOptions } from '@sentry/uti import { eventFromMessage, eventFromUnknownInput } from './eventbuilder'; import { IS_DEBUG_BUILD } from './flags'; -import { NodeOptions } from './types'; +import { NodeClientOptions } from './types'; /** * The Sentry Node SDK Client. * - * @see NodeOptions for documentation on configuration options. + * @see NodeClientOptions for documentation on configuration options. * @see SentryClient for usage documentation. */ -export class NodeClient extends BaseClient { +export class NodeClient extends BaseClient { protected _sessionFlusher: SessionFlusher | undefined; /** * Creates a new Node SDK instance. * @param options Configuration options for this SDK. */ - public constructor(options: NodeOptions, transport: Transport, newTransport?: NewTransport) { + public constructor(options: NodeClientOptions, transport: Transport, newTransport?: NewTransport) { options._metadata = options._metadata || {}; options._metadata.sdk = options._metadata.sdk || { name: 'sentry.javascript.node', diff --git a/packages/node/src/sdk.ts b/packages/node/src/sdk.ts index 0b7fb9bd5671..fa4b22a12066 100644 --- a/packages/node/src/sdk.ts +++ b/packages/node/src/sdk.ts @@ -1,15 +1,15 @@ -import { getCurrentHub, initAndBind, Integrations as CoreIntegrations } from '@sentry/core'; +import { getCurrentHub, getIntegrationsToSetup, initAndBind, Integrations as CoreIntegrations } from '@sentry/core'; import { getMainCarrier, setHubOnCarrier } from '@sentry/hub'; import { SessionStatus } from '@sentry/types'; -import { getGlobalObject, logger } from '@sentry/utils'; +import { getGlobalObject, logger, stackParserFromOptions } from '@sentry/utils'; import * as domain from 'domain'; import { NodeClient } from './client'; import { IS_DEBUG_BUILD } from './flags'; import { Console, ContextLines, Http, LinkedErrors, OnUncaughtException, OnUnhandledRejection } from './integrations'; import { nodeStackParser } from './stack-parser'; -import { setupNodeTransport } from './transports'; -import { NodeOptions } from './types'; +import { HTTPSTransport, HTTPTransport, setupNodeTransport } from './transports'; +import { NodeClientOptions, NodeOptions } from './types'; export const defaultIntegrations = [ // Common @@ -132,7 +132,17 @@ export function init(options: NodeOptions = {}): void { } const { transport, newTransport } = setupNodeTransport(options); - initAndBind(NodeClient, options, transport, newTransport); + + // TODO(v7): Refactor this to reduce the logic above + const clientOptions: NodeClientOptions = { + ...options, + stackParser: stackParserFromOptions(options), + integrations: getIntegrationsToSetup(options), + // TODO(v7): Fix me when we switch to new transports entirely. + transport: options.transport || (transport instanceof HTTPTransport ? HTTPTransport : HTTPSTransport), + }; + + initAndBind(NodeClient, clientOptions, transport, newTransport); if (options.autoSessionTracking) { startSessionTracking(); @@ -189,7 +199,7 @@ export function isAutoSessionTrackingEnabled(client?: NodeClient): boolean { if (client === undefined) { return false; } - const clientOptions: NodeOptions = client && client.getOptions(); + const clientOptions = client && client.getOptions(); if (clientOptions && clientOptions.autoSessionTracking !== undefined) { return clientOptions.autoSessionTracking; } diff --git a/packages/node/src/types.ts b/packages/node/src/types.ts index 055006a47e9e..a2311a4698f9 100644 --- a/packages/node/src/types.ts +++ b/packages/node/src/types.ts @@ -1,16 +1,9 @@ -import { Options } from '@sentry/types'; +import { ClientOptions, Options } from '@sentry/types'; -/** - * Configuration options for the Sentry Node SDK. - * @see NodeClient for more information. - */ -export interface NodeOptions extends Options { +export interface BaseNodeOptions { /** Sets an optional server name (device name) */ serverName?: string; - /** Maximum time in milliseconds to wait to drain the request queue, before the process is allowed to exit. */ - shutdownTimeout?: number; - /** Set a HTTP proxy that should be used for outbound requests. */ httpProxy?: string; @@ -23,3 +16,15 @@ export interface NodeOptions extends Options { /** Callback that is executed when a fatal global error occurs. */ onFatalError?(error: Error): void; } + +/** + * Configuration options for the Sentry Node SDK + * @see @sentry/types Options for more information. + */ +export interface NodeOptions extends Options, BaseNodeOptions {} + +/** + * Configuration options for the Sentry Node SDK Client class + * @see NodeClient for more information. + */ +export interface NodeClientOptions extends ClientOptions, BaseNodeOptions {} diff --git a/packages/node/test/client.test.ts b/packages/node/test/client.test.ts index 1cddd0e85e3f..bb3d13ca4122 100644 --- a/packages/node/test/client.test.ts +++ b/packages/node/test/client.test.ts @@ -2,6 +2,7 @@ import { Scope, SessionFlusher } from '@sentry/hub'; import { NodeClient } from '../src'; import { setupNodeTransport } from '../src/transports'; +import { getDefaultNodeClientOptions } from './helper/node-client-options'; const PUBLIC_DSN = 'https://username@domain/123'; @@ -15,7 +16,7 @@ describe('NodeClient', () => { describe('captureException', () => { test('when autoSessionTracking is enabled, and requestHandler is not used -> requestStatus should not be set', () => { - const options = { dsn: PUBLIC_DSN, autoSessionTracking: true, release: '1.4' }; + const options = getDefaultNodeClientOptions({ dsn: PUBLIC_DSN, autoSessionTracking: true, release: '1.4' }); client = new NodeClient(options, setupNodeTransport(options).transport); const scope = new Scope(); scope.setRequestSession({ status: 'ok' }); @@ -26,7 +27,7 @@ describe('NodeClient', () => { expect(requestSession!.status).toEqual('ok'); }); test('when autoSessionTracking is disabled -> requestStatus should not be set', () => { - const options = { dsn: PUBLIC_DSN, autoSessionTracking: false, release: '1.4' }; + const options = getDefaultNodeClientOptions({ dsn: PUBLIC_DSN, autoSessionTracking: false, release: '1.4' }); client = new NodeClient(options, setupNodeTransport(options).transport); // It is required to initialise SessionFlusher to capture Session Aggregates (it is usually initialised // by the`requestHandler`) @@ -41,7 +42,7 @@ describe('NodeClient', () => { expect(requestSession!.status).toEqual('ok'); }); test('when autoSessionTracking is enabled + requestSession status is Crashed -> requestStatus should not be overridden', () => { - const options = { dsn: PUBLIC_DSN, autoSessionTracking: true, release: '1.4' }; + const options = getDefaultNodeClientOptions({ dsn: PUBLIC_DSN, autoSessionTracking: true, release: '1.4' }); client = new NodeClient(options, setupNodeTransport(options).transport); // It is required to initialise SessionFlusher to capture Session Aggregates (it is usually initialised // by the`requestHandler`) @@ -56,7 +57,7 @@ describe('NodeClient', () => { expect(requestSession!.status).toEqual('crashed'); }); test('when autoSessionTracking is enabled + error occurs within request bounds -> requestStatus should be set to Errored', () => { - const options = { dsn: PUBLIC_DSN, autoSessionTracking: true, release: '1.4' }; + const options = getDefaultNodeClientOptions({ dsn: PUBLIC_DSN, autoSessionTracking: true, release: '1.4' }); client = new NodeClient(options, setupNodeTransport(options).transport); // It is required to initialise SessionFlusher to capture Session Aggregates (it is usually initialised // by the`requestHandler`) @@ -71,7 +72,7 @@ describe('NodeClient', () => { expect(requestSession!.status).toEqual('errored'); }); test('when autoSessionTracking is enabled + error occurs outside of request bounds -> requestStatus should not be set to Errored', () => { - const options = { dsn: PUBLIC_DSN, autoSessionTracking: true, release: '1.4' }; + const options = getDefaultNodeClientOptions({ dsn: PUBLIC_DSN, autoSessionTracking: true, release: '1.4' }); client = new NodeClient(options, setupNodeTransport(options).transport); // It is required to initialise SessionFlusher to capture Session Aggregates (it is usually initialised // by the`requestHandler`) @@ -88,7 +89,7 @@ describe('NodeClient', () => { describe('captureEvent()', () => { test('If autoSessionTracking is disabled, requestSession status should not be set', () => { - const options = { dsn: PUBLIC_DSN, autoSessionTracking: false, release: '1.4' }; + const options = getDefaultNodeClientOptions({ dsn: PUBLIC_DSN, autoSessionTracking: false, release: '1.4' }); client = new NodeClient(options, setupNodeTransport(options).transport); // It is required to initialise SessionFlusher to capture Session Aggregates (it is usually initialised // by the`requestHandler`) @@ -107,7 +108,7 @@ describe('NodeClient', () => { }); test('When captureEvent is called with an exception, requestSession status should be set to Errored', () => { - const options = { dsn: PUBLIC_DSN, autoSessionTracking: true, release: '2.2' }; + const options = getDefaultNodeClientOptions({ dsn: PUBLIC_DSN, autoSessionTracking: true, release: '2.2' }); client = new NodeClient(options, setupNodeTransport(options).transport); // It is required to initialise SessionFlusher to capture Session Aggregates (it is usually initialised // by the`requestHandler`) @@ -123,7 +124,7 @@ describe('NodeClient', () => { }); test('When captureEvent is called without an exception, requestSession status should not be set to Errored', () => { - const options = { dsn: PUBLIC_DSN, autoSessionTracking: true, release: '2.2' }; + const options = getDefaultNodeClientOptions({ dsn: PUBLIC_DSN, autoSessionTracking: true, release: '2.2' }); client = new NodeClient(options, setupNodeTransport(options).transport); // It is required to initialise SessionFlusher to capture Session Aggregates (it is usually initialised // by the`requestHandler`) @@ -139,7 +140,7 @@ describe('NodeClient', () => { }); test('When captureEvent is called with an exception but outside of a request, then requestStatus should not be set', () => { - const options = { dsn: PUBLIC_DSN, autoSessionTracking: true, release: '2.2' }; + const options = getDefaultNodeClientOptions({ dsn: PUBLIC_DSN, autoSessionTracking: true, release: '2.2' }); client = new NodeClient(options, setupNodeTransport(options).transport); // It is required to initialise SessionFlusher to capture Session Aggregates (it is usually initialised // by the`requestHandler`) @@ -157,7 +158,7 @@ describe('NodeClient', () => { }); test('When captureEvent is called with a transaction, then requestSession status should not be set', () => { - const options = { dsn: PUBLIC_DSN, autoSessionTracking: true, release: '1.3' }; + const options = getDefaultNodeClientOptions({ dsn: PUBLIC_DSN, autoSessionTracking: true, release: '1.3' }); client = new NodeClient(options, setupNodeTransport(options).transport); // It is required to initialise SessionFlusher to capture Session Aggregates (it is usually initialised // by the`requestHandler`) @@ -172,7 +173,7 @@ describe('NodeClient', () => { }); test('When captureEvent is called with an exception but requestHandler is not used, then requestSession status should not be set', () => { - const options = { dsn: PUBLIC_DSN, autoSessionTracking: true, release: '1.3' }; + const options = getDefaultNodeClientOptions({ dsn: PUBLIC_DSN, autoSessionTracking: true, release: '1.3' }); client = new NodeClient(options, setupNodeTransport(options).transport); const scope = new Scope(); @@ -192,11 +193,11 @@ describe('NodeClient', () => { describe('flush/close', () => { test('client close function disables _sessionFlusher', async () => { jest.useRealTimers(); - const options = { + const options = getDefaultNodeClientOptions({ dsn: PUBLIC_DSN, autoSessionTracking: true, release: '1.1', - }; + }); const client = new NodeClient(options, setupNodeTransport(options).transport); client.initSessionFlusher(); // Clearing interval is important here to ensure that the flush function later on is called by the `client.close()` diff --git a/packages/node/test/handlers.test.ts b/packages/node/test/handlers.test.ts index d7d47fda053d..b857e1b74bf5 100644 --- a/packages/node/test/handlers.test.ts +++ b/packages/node/test/handlers.test.ts @@ -1,6 +1,6 @@ import * as sentryCore from '@sentry/core'; -import { Hub } from '@sentry/hub'; import * as sentryHub from '@sentry/hub'; +import { Hub } from '@sentry/hub'; import { Transaction } from '@sentry/tracing'; import { Runtime } from '@sentry/types'; import { SentryError } from '@sentry/utils'; @@ -19,6 +19,7 @@ import { } from '../src/handlers'; import * as SDK from '../src/sdk'; import { setupNodeTransport } from '../src/transports'; +import { getDefaultNodeClientOptions } from './helper/node-client-options'; describe('parseRequest', () => { let mockReq: { [key: string]: any }; @@ -224,7 +225,7 @@ describe('requestHandler', () => { }); it('autoSessionTracking is enabled, sets requestSession status to ok, when handling a request', () => { - const options = { autoSessionTracking: true, release: '1.2' }; + const options = getDefaultNodeClientOptions({ autoSessionTracking: true, release: '1.2' }); client = new NodeClient(options, setupNodeTransport(options).transport); const hub = new Hub(client); @@ -237,7 +238,7 @@ describe('requestHandler', () => { }); it('autoSessionTracking is disabled, does not set requestSession, when handling a request', () => { - const options = { autoSessionTracking: false, release: '1.2' }; + const options = getDefaultNodeClientOptions({ autoSessionTracking: false, release: '1.2' }); client = new NodeClient(options, setupNodeTransport(options).transport); const hub = new Hub(client); @@ -250,7 +251,7 @@ describe('requestHandler', () => { }); it('autoSessionTracking is enabled, calls _captureRequestSession, on response finish', done => { - const options = { autoSessionTracking: true, release: '1.2' }; + const options = getDefaultNodeClientOptions({ autoSessionTracking: true, release: '1.2' }); client = new NodeClient(options, setupNodeTransport(options).transport); const hub = new Hub(client); @@ -271,7 +272,7 @@ describe('requestHandler', () => { }); it('autoSessionTracking is disabled, does not call _captureRequestSession, on response finish', done => { - const options = { autoSessionTracking: false, release: '1.2' }; + const options = getDefaultNodeClientOptions({ autoSessionTracking: false, release: '1.2' }); client = new NodeClient(options, setupNodeTransport(options).transport); const hub = new Hub(client); jest.spyOn(sentryCore, 'getCurrentHub').mockReturnValue(hub); @@ -372,7 +373,7 @@ describe('tracingHandler', () => { it('extracts request data for sampling context', () => { const tracesSampler = jest.fn(); - const options = { tracesSampler }; + const options = getDefaultNodeClientOptions({ tracesSampler }); const hub = new Hub(new NodeClient(options, setupNodeTransport(options).transport)); // we need to mock both of these because the tracing handler relies on `@sentry/core` while the sampler relies on // `@sentry/hub`, and mocking breaks the link between the two @@ -395,7 +396,7 @@ describe('tracingHandler', () => { }); it('puts its transaction on the scope', () => { - const options = { tracesSampleRate: 1.0 }; + const options = getDefaultNodeClientOptions({ tracesSampleRate: 1.0 }); const hub = new Hub(new NodeClient(options, setupNodeTransport(options).transport)); // we need to mock both of these because the tracing handler relies on `@sentry/core` while the sampler relies on // `@sentry/hub`, and mocking breaks the link between the two @@ -727,7 +728,7 @@ describe('errorHandler()', () => { jest.restoreAllMocks(); }); it('when autoSessionTracking is disabled, does not set requestSession status on Crash', () => { - const options = { autoSessionTracking: false, release: '3.3' }; + const options = getDefaultNodeClientOptions({ autoSessionTracking: false, release: '3.3' }); client = new NodeClient(options, setupNodeTransport(options).transport); // It is required to initialise SessionFlusher to capture Session Aggregates (it is usually initialised // by the`requestHandler`) @@ -747,7 +748,7 @@ describe('errorHandler()', () => { }); it('autoSessionTracking is enabled + requestHandler is not used -> does not set requestSession status on Crash', () => { - const options = { autoSessionTracking: false, release: '3.3' }; + const options = getDefaultNodeClientOptions({ autoSessionTracking: false, release: '3.3' }); client = new NodeClient(options, setupNodeTransport(options).transport); const scope = sentryCore.getCurrentHub().getScope(); @@ -764,7 +765,7 @@ describe('errorHandler()', () => { }); it('when autoSessionTracking is enabled, should set requestSession status to Crashed when an unhandled error occurs within the bounds of a request', () => { - const options = { autoSessionTracking: true, release: '1.1' }; + const options = getDefaultNodeClientOptions({ autoSessionTracking: true, release: '1.1' }); client = new NodeClient(options, setupNodeTransport(options).transport); // It is required to initialise SessionFlusher to capture Session Aggregates (it is usually initialised // by the`requestHandler`) @@ -783,7 +784,7 @@ describe('errorHandler()', () => { }); it('when autoSessionTracking is enabled, should not set requestSession status on Crash when it occurs outside the bounds of a request', () => { - const options = { autoSessionTracking: true, release: '2.2' }; + const options = getDefaultNodeClientOptions({ autoSessionTracking: true, release: '2.2' }); client = new NodeClient(options, setupNodeTransport(options).transport); // It is required to initialise SessionFlusher to capture Session Aggregates (it is usually initialised // by the`requestHandler`) diff --git a/packages/node/test/helper/node-client-options.ts b/packages/node/test/helper/node-client-options.ts new file mode 100644 index 000000000000..c2bb1d42a871 --- /dev/null +++ b/packages/node/test/helper/node-client-options.ts @@ -0,0 +1,12 @@ +import { NoopTransport } from '@sentry/core'; + +import { NodeClientOptions } from '../../src/types'; + +export function getDefaultNodeClientOptions(options: Partial = {}): NodeClientOptions { + return { + integrations: [], + transport: NoopTransport, + stackParser: () => [], + ...options, + }; +} diff --git a/packages/node/test/index.test.ts b/packages/node/test/index.test.ts index 02eaad72b2c0..2bdf8497097c 100644 --- a/packages/node/test/index.test.ts +++ b/packages/node/test/index.test.ts @@ -19,6 +19,7 @@ import { import { ContextLines, LinkedErrors } from '../src/integrations'; import { nodeStackParser } from '../src/stack-parser'; import { setupNodeTransport } from '../src/transports'; +import { getDefaultNodeClientOptions } from './helper/node-client-options'; const stackParser = createStackParser(nodeStackParser); @@ -90,7 +91,7 @@ describe('SentryNode', () => { }); test('record auto breadcrumbs', done => { - const options = { + const options = getDefaultNodeClientOptions({ beforeSend: (event: Event) => { // TODO: It should be 3, but we don't capture a breadcrumb // for our own captureMessage/captureException calls yet @@ -100,7 +101,7 @@ describe('SentryNode', () => { }, dsn, stackParser, - }; + }); const client = new NodeClient(options, setupNodeTransport(options).transport); getCurrentHub().bindClient(client); addBreadcrumb({ message: 'test1' }); @@ -122,7 +123,7 @@ describe('SentryNode', () => { test('capture an exception', done => { expect.assertions(6); - const options = { + const options = getDefaultNodeClientOptions({ stackParser, beforeSend: (event: Event) => { expect(event.tags).toEqual({ test: '1' }); @@ -135,7 +136,7 @@ describe('SentryNode', () => { return null; }, dsn, - }; + }); getCurrentHub().bindClient(new NodeClient(options, setupNodeTransport(options).transport)); configureScope((scope: Scope) => { scope.setTag('test', '1'); @@ -149,7 +150,7 @@ describe('SentryNode', () => { test('capture a string exception', done => { expect.assertions(6); - const options = { + const options = getDefaultNodeClientOptions({ stackParser, beforeSend: (event: Event) => { expect(event.tags).toEqual({ test: '1' }); @@ -162,7 +163,7 @@ describe('SentryNode', () => { return null; }, dsn, - }; + }); getCurrentHub().bindClient(new NodeClient(options, setupNodeTransport(options).transport)); configureScope((scope: Scope) => { scope.setTag('test', '1'); @@ -176,7 +177,7 @@ describe('SentryNode', () => { test('capture an exception with pre/post context', done => { expect.assertions(10); - const options = { + const options = getDefaultNodeClientOptions({ stackParser, beforeSend: (event: Event) => { expect(event.tags).toEqual({ test: '1' }); @@ -193,7 +194,7 @@ describe('SentryNode', () => { return null; }, dsn, - }; + }); getCurrentHub().bindClient(new NodeClient(options, setupNodeTransport(options).transport)); configureScope((scope: Scope) => { scope.setTag('test', '1'); @@ -207,7 +208,7 @@ describe('SentryNode', () => { test('capture a linked exception with pre/post context', done => { expect.assertions(15); - const options = { + const options = getDefaultNodeClientOptions({ stackParser, integrations: [new ContextLines(), new LinkedErrors()], beforeSend: (event: Event) => { @@ -231,7 +232,7 @@ describe('SentryNode', () => { return null; }, dsn, - }; + }); getCurrentHub().bindClient(new NodeClient(options, setupNodeTransport(options).transport)); try { throw new Error('test'); @@ -247,7 +248,7 @@ describe('SentryNode', () => { test('capture a message', done => { expect.assertions(2); - const options = { + const options = getDefaultNodeClientOptions({ stackParser, beforeSend: (event: Event) => { expect(event.message).toBe('test'); @@ -256,14 +257,14 @@ describe('SentryNode', () => { return null; }, dsn, - }; + }); getCurrentHub().bindClient(new NodeClient(options, setupNodeTransport(options).transport)); captureMessage('test'); }); test('capture an event', done => { expect.assertions(2); - const options = { + const options = getDefaultNodeClientOptions({ stackParser, beforeSend: (event: Event) => { expect(event.message).toBe('test event'); @@ -272,7 +273,7 @@ describe('SentryNode', () => { return null; }, dsn, - }; + }); getCurrentHub().bindClient(new NodeClient(options, setupNodeTransport(options).transport)); captureEvent({ message: 'test event' }); }); @@ -280,7 +281,7 @@ describe('SentryNode', () => { test('capture an event in a domain', done => { const d = domain.create(); - const options = { + const options = getDefaultNodeClientOptions({ stackParser, beforeSend: (event: Event) => { expect(event.message).toBe('test domain'); @@ -289,7 +290,7 @@ describe('SentryNode', () => { return null; }, dsn, - }; + }); const client = new NodeClient(options, setupNodeTransport(options).transport); d.run(() => { @@ -301,7 +302,7 @@ describe('SentryNode', () => { test('stacktrace order', done => { expect.assertions(1); - const options = { + const options = getDefaultNodeClientOptions({ stackParser, beforeSend: (event: Event) => { expect( @@ -312,7 +313,7 @@ describe('SentryNode', () => { return null; }, dsn, - }; + }); getCurrentHub().bindClient(new NodeClient(options, setupNodeTransport(options).transport)); try { // @ts-ignore allow function declarations in strict mode @@ -376,7 +377,7 @@ describe('SentryNode initialization', () => { }); it('should set SDK data when instantiating a client directly', () => { - const options = { dsn }; + const options = getDefaultNodeClientOptions({ dsn }); const client = new NodeClient(options, setupNodeTransport(options).transport); // eslint-disable-next-line @typescript-eslint/no-explicit-any diff --git a/packages/node/test/integrations/http.test.ts b/packages/node/test/integrations/http.test.ts index f66f847cd298..30a923524a3e 100644 --- a/packages/node/test/integrations/http.test.ts +++ b/packages/node/test/integrations/http.test.ts @@ -1,6 +1,6 @@ import * as sentryCore from '@sentry/core'; -import { Hub } from '@sentry/hub'; import * as hubModule from '@sentry/hub'; +import { Hub } from '@sentry/hub'; import { addExtensionMethods, Span, TRACEPARENT_REGEXP, Transaction } from '@sentry/tracing'; import { parseSemver } from '@sentry/utils'; import * as http from 'http'; @@ -12,16 +12,17 @@ import { Breadcrumb } from '../../src'; import { NodeClient } from '../../src/client'; import { Http as HttpIntegration } from '../../src/integrations/http'; import { setupNodeTransport } from '../../src/transports'; +import { getDefaultNodeClientOptions } from '../helper/node-client-options'; const NODE_VERSION = parseSemver(process.versions.node); describe('tracing', () => { function createTransactionOnScope() { - const options = { + const options = getDefaultNodeClientOptions({ dsn: 'https://dogsarebadatkeepingsecrets@squirrelchasers.ingest.sentry.io/12312012', tracesSampleRate: 1.0, integrations: [new HttpIntegration({ tracing: true })], - }; + }); const hub = new Hub(new NodeClient(options, setupNodeTransport(options).transport)); addExtensionMethods(); @@ -97,7 +98,7 @@ describe('default protocols', () => { const p = new Promise(r => { resolve = r; }); - const options = { + const options = getDefaultNodeClientOptions({ dsn: 'https://dogsarebadatkeepingsecrets@squirrelchasers.ingest.sentry.io/12312012', integrations: [new HttpIntegration({ breadcrumbs: true })], beforeBreadcrumb: (b: Breadcrumb) => { @@ -106,7 +107,7 @@ describe('default protocols', () => { } return b; }, - }; + }); hub.bindClient(new NodeClient(options, setupNodeTransport(options).transport)); return p; diff --git a/packages/node/test/integrations/linkederrors.test.ts b/packages/node/test/integrations/linkederrors.test.ts index 2c2957bf6a69..4d18707be904 100644 --- a/packages/node/test/integrations/linkederrors.test.ts +++ b/packages/node/test/integrations/linkederrors.test.ts @@ -5,6 +5,7 @@ import { Event, NodeClient } from '../../src'; import { LinkedErrors } from '../../src/integrations/linkederrors'; import { nodeStackParser } from '../../src/stack-parser'; import { setupNodeTransport } from '../../src/transports'; +import { getDefaultNodeClientOptions } from '../helper/node-client-options'; const stackParser = createStackParser(nodeStackParser); @@ -32,7 +33,7 @@ describe('LinkedErrors', () => { expect.assertions(2); const spy = jest.spyOn(linkedErrors, '_walkErrorTree'); const one = new Error('originalException'); - const options = { stackParser }; + const options = getDefaultNodeClientOptions({ stackParser }); const client = new NodeClient(options, setupNodeTransport(options).transport); let event: Event | undefined; return client @@ -56,7 +57,7 @@ describe('LinkedErrors', () => { }), ); const one = new Error('originalException'); - const options = { stackParser }; + const options = getDefaultNodeClientOptions({ stackParser }); const client = new NodeClient(options, setupNodeTransport(options).transport); return client.eventFromException(one).then(event => linkedErrors @@ -77,7 +78,7 @@ describe('LinkedErrors', () => { one.cause = two; two.cause = three; - const options = { stackParser }; + const options = getDefaultNodeClientOptions({ stackParser }); const client = new NodeClient(options, setupNodeTransport(options).transport); return client.eventFromException(one).then(event => linkedErrors @@ -111,7 +112,7 @@ describe('LinkedErrors', () => { one.reason = two; two.reason = three; - const options = { stackParser }; + const options = getDefaultNodeClientOptions({ stackParser }); const client = new NodeClient(options, setupNodeTransport(options).transport); return client.eventFromException(one).then(event => linkedErrors @@ -145,7 +146,7 @@ describe('LinkedErrors', () => { one.cause = two; two.cause = three; - const options = { stackParser }; + const options = getDefaultNodeClientOptions({ stackParser }); const client = new NodeClient(options, setupNodeTransport(options).transport); return client.eventFromException(one).then(event => linkedErrors diff --git a/packages/node/test/sdk.test.ts b/packages/node/test/sdk.test.ts new file mode 100644 index 000000000000..48e5accee439 --- /dev/null +++ b/packages/node/test/sdk.test.ts @@ -0,0 +1,92 @@ +import { Integration } from '@sentry/types'; + +import { init } from '../src/sdk'; +import * as sdk from '../src/sdk'; + +// eslint-disable-next-line no-var +declare var global: any; + +const PUBLIC_DSN = 'https://username@domain/123'; + +class MockIntegration implements Integration { + public name: string; + public setupOnce: jest.Mock = jest.fn(); + public constructor(name: string) { + this.name = name; + } +} + +const defaultIntegrationsBackup = sdk.defaultIntegrations; + +describe('init()', () => { + beforeEach(() => { + global.__SENTRY__ = {}; + }); + + afterEach(() => { + // @ts-ignore - Reset the default integrations of node sdk to original + sdk.defaultIntegrations = defaultIntegrationsBackup; + }); + + it("doesn't install default integrations if told not to", () => { + const mockDefaultIntegrations = [ + new MockIntegration('Mock integration 1.1'), + new MockIntegration('Mock integration 1.2'), + ]; + + // @ts-ignore - Replace default integrations with mock integrations, needs ts-ignore because imports are readonly + sdk.defaultIntegrations = mockDefaultIntegrations; + + init({ dsn: PUBLIC_DSN, defaultIntegrations: false }); + + expect(mockDefaultIntegrations[0].setupOnce as jest.Mock).toHaveBeenCalledTimes(0); + expect(mockDefaultIntegrations[1].setupOnce as jest.Mock).toHaveBeenCalledTimes(0); + }); + + it('installs merged default integrations, with overrides provided through options', () => { + const mockDefaultIntegrations = [ + new MockIntegration('Some mock integration 2.1'), + new MockIntegration('Some mock integration 2.2'), + ]; + + // @ts-ignore - Replace default integrations with mock integrations, needs ts-ignore because imports are readonly + sdk.defaultIntegrations = mockDefaultIntegrations; + + const mockIntegrations = [ + new MockIntegration('Some mock integration 2.1'), + new MockIntegration('Some mock integration 2.3'), + ]; + + init({ dsn: PUBLIC_DSN, integrations: mockIntegrations }); + + expect(mockDefaultIntegrations[0].setupOnce as jest.Mock).toHaveBeenCalledTimes(0); + expect(mockDefaultIntegrations[1].setupOnce as jest.Mock).toHaveBeenCalledTimes(1); + expect(mockIntegrations[0].setupOnce as jest.Mock).toHaveBeenCalledTimes(1); + expect(mockIntegrations[1].setupOnce as jest.Mock).toHaveBeenCalledTimes(1); + }); + + it('installs integrations returned from a callback function', () => { + const mockDefaultIntegrations = [ + new MockIntegration('Some mock integration 3.1'), + new MockIntegration('Some mock integration 3.2'), + ]; + + // @ts-ignore - Replace default integrations with mock integrations, needs ts-ignore because imports are readonly + sdk.defaultIntegrations = mockDefaultIntegrations; + + const newIntegration = new MockIntegration('Some mock integration 3.3'); + + init({ + dsn: PUBLIC_DSN, + integrations: integrations => { + const newIntegrations = [...integrations]; + newIntegrations[1] = newIntegration; + return newIntegrations; + }, + }); + + expect(mockDefaultIntegrations[0].setupOnce as jest.Mock).toHaveBeenCalledTimes(1); + expect(mockDefaultIntegrations[1].setupOnce as jest.Mock).toHaveBeenCalledTimes(0); + expect(newIntegration.setupOnce as jest.Mock).toHaveBeenCalledTimes(1); + }); +}); diff --git a/packages/node/test/transports/setup.test.ts b/packages/node/test/transports/setup.test.ts index 6864ac89ea1e..38f99a4c95f3 100644 --- a/packages/node/test/transports/setup.test.ts +++ b/packages/node/test/transports/setup.test.ts @@ -3,6 +3,7 @@ import { FakeTransport } from '@sentry/core/test/mocks/transport'; import { HTTPSTransport, HTTPTransport, setupNodeTransport } from '@sentry/node/src/transports'; import { makeNodeTransport } from '../../src/transports/new'; +import { getDefaultNodeClientOptions } from '../helper/node-client-options'; jest.mock('../../src/transports/new', () => { const original = jest.requireActual('../../src/transports/new'); @@ -31,7 +32,7 @@ describe('setupNodeTransport', () => { }); it('returns the instantiated transport passed via the options', () => { - const options = { dsn: DSN, transport: FakeTransport }; + const options = getDefaultNodeClientOptions({ dsn: DSN, transport: FakeTransport }); const { transport, newTransport } = setupNodeTransport(options); expect(transport).toBeDefined(); diff --git a/packages/tracing/src/hubextensions.ts b/packages/tracing/src/hubextensions.ts index bd38b270cc22..80bd7a5783c8 100644 --- a/packages/tracing/src/hubextensions.ts +++ b/packages/tracing/src/hubextensions.ts @@ -1,5 +1,6 @@ import { getMainCarrier, Hub } from '@sentry/hub'; import { + ClientOptions, CustomSamplingContext, Integration, IntegrationClass, @@ -41,7 +42,11 @@ function traceHeaders(this: Hub): { [key: string]: string } { * * @returns The given transaction with its `sampled` value set */ -function sample(transaction: T, options: Options, samplingContext: SamplingContext): T { +function sample( + transaction: T, + options: Pick, + samplingContext: SamplingContext, +): T { // nothing to do if tracing is not enabled if (!hasTracingEnabled(options)) { transaction.sampled = false; @@ -171,7 +176,7 @@ function _startTransaction( customSamplingContext?: CustomSamplingContext, ): Transaction { const client = this.getClient(); - const options = (client && client.getOptions()) || {}; + const options: Partial = (client && client.getOptions()) || {}; let transaction = new Transaction(transactionContext, this); transaction = sample(transaction, options, { @@ -196,7 +201,7 @@ export function startIdleTransaction( customSamplingContext?: CustomSamplingContext, ): IdleTransaction { const client = hub.getClient(); - const options = (client && client.getOptions()) || {}; + const options: Partial = (client && client.getOptions()) || {}; let transaction = new IdleTransaction(transactionContext, hub, idleTimeout, onScope); transaction = sample(transaction, options, { diff --git a/packages/tracing/src/utils.ts b/packages/tracing/src/utils.ts index 63598c00ac58..9d999df486fb 100644 --- a/packages/tracing/src/utils.ts +++ b/packages/tracing/src/utils.ts @@ -20,7 +20,9 @@ export { TRACEPARENT_REGEXP, extractTraceparentData } from '@sentry/utils'; * * Tracing is enabled when at least one of `tracesSampleRate` and `tracesSampler` is defined in the SDK config. */ -export function hasTracingEnabled(maybeOptions?: Options | undefined): boolean { +export function hasTracingEnabled( + maybeOptions?: Pick | undefined, +): boolean { const client = getCurrentHub().getClient(); const options = maybeOptions || (client && client.getOptions()); return !!options && ('tracesSampleRate' in options || 'tracesSampler' in options); diff --git a/packages/tracing/test/browser/backgroundtab.test.ts b/packages/tracing/test/browser/backgroundtab.test.ts index 440eb785a609..29612b410322 100644 --- a/packages/tracing/test/browser/backgroundtab.test.ts +++ b/packages/tracing/test/browser/backgroundtab.test.ts @@ -1,5 +1,6 @@ import { BrowserClient } from '@sentry/browser'; import { setupBrowserTransport } from '@sentry/browser/src/transports'; +import { getDefaultBrowserClientOptions } from '@sentry/browser/test/unit/helper/browser-client-options'; import { Hub, makeMain } from '@sentry/hub'; import { JSDOM } from 'jsdom'; @@ -14,7 +15,7 @@ describe('registerBackgroundTabDetection', () => { // @ts-ignore need to override global document global.document = dom.window.document; - const options = { tracesSampleRate: 1 }; + const options = getDefaultBrowserClientOptions({ tracesSampleRate: 1 }); hub = new Hub(new BrowserClient(options, setupBrowserTransport(options).transport)); makeMain(hub); diff --git a/packages/tracing/test/browser/browsertracing.test.ts b/packages/tracing/test/browser/browsertracing.test.ts index dfa4e0436b8c..76ccaf947271 100644 --- a/packages/tracing/test/browser/browsertracing.test.ts +++ b/packages/tracing/test/browser/browsertracing.test.ts @@ -1,5 +1,6 @@ import { BrowserClient } from '@sentry/browser'; import { setupBrowserTransport } from '@sentry/browser/src/transports'; +import { getDefaultBrowserClientOptions } from '@sentry/browser/test/unit/helper/browser-client-options'; import { Hub, makeMain } from '@sentry/hub'; import { getGlobalObject, InstrumentHandlerCallback, InstrumentHandlerType } from '@sentry/utils'; import { JSDOM } from 'jsdom'; @@ -52,7 +53,7 @@ describe('BrowserTracing', () => { let hub: Hub; beforeEach(() => { jest.useFakeTimers(); - const options = { tracesSampleRate: 1 }; + const options = getDefaultBrowserClientOptions({ tracesSampleRate: 1 }); hub = new Hub(new BrowserClient(options, setupBrowserTransport(options).transport)); makeMain(hub); document.head.innerHTML = ''; @@ -474,7 +475,7 @@ describe('BrowserTracing', () => { getGlobalObject().location = dogParkLocation as any; const tracesSampler = jest.fn(); - const options = { tracesSampler }; + const options = getDefaultBrowserClientOptions({ tracesSampler }); hub.bindClient(new BrowserClient(options, setupBrowserTransport(options).transport)); // setting up the BrowserTracing integration automatically starts a pageload transaction createBrowserTracing(true); @@ -491,7 +492,7 @@ describe('BrowserTracing', () => { getGlobalObject().location = dogParkLocation as any; const tracesSampler = jest.fn(); - const options = { tracesSampler }; + const options = getDefaultBrowserClientOptions({ tracesSampler }); hub.bindClient(new BrowserClient(options, setupBrowserTransport(options).transport)); // setting up the BrowserTracing integration normally automatically starts a pageload transaction, but that's not // what we're testing here diff --git a/packages/tracing/test/browser/request.test.ts b/packages/tracing/test/browser/request.test.ts index 65823e293351..4ead6fe5bb9b 100644 --- a/packages/tracing/test/browser/request.test.ts +++ b/packages/tracing/test/browser/request.test.ts @@ -1,5 +1,6 @@ import { BrowserClient } from '@sentry/browser'; import { setupBrowserTransport } from '@sentry/browser/src/transports'; +import { getDefaultBrowserClientOptions } from '@sentry/browser/test/unit/helper/browser-client-options'; import { Hub, makeMain } from '@sentry/hub'; import * as utils from '@sentry/utils'; @@ -73,7 +74,7 @@ describe('callbacks', () => { }; beforeAll(() => { - const options = { tracesSampleRate: 1 }; + const options = getDefaultBrowserClientOptions({ tracesSampleRate: 1 }); hub = new Hub(new BrowserClient(options, setupBrowserTransport(options).transport)); makeMain(hub); }); diff --git a/packages/tracing/test/errors.test.ts b/packages/tracing/test/errors.test.ts index c15825fd5480..8dbcb2454539 100644 --- a/packages/tracing/test/errors.test.ts +++ b/packages/tracing/test/errors.test.ts @@ -1,5 +1,6 @@ import { BrowserClient } from '@sentry/browser'; import { setupBrowserTransport } from '@sentry/browser/src/transports'; +import { NoopTransport } from '@sentry/core/src/transports/noop'; import { Hub, makeMain } from '@sentry/hub'; import { InstrumentHandlerCallback, InstrumentHandlerType } from '@sentry/utils'; @@ -35,7 +36,7 @@ describe('registerErrorHandlers()', () => { let hub: Hub; beforeEach(() => { mockAddInstrumentationHandler.mockClear(); - const options = { tracesSampleRate: 1 }; + const options = { tracesSampleRate: 1, transport: NoopTransport, integrations: [], stackParser: () => [] }; hub = new Hub(new BrowserClient(options, setupBrowserTransport(options).transport)); makeMain(hub); }); diff --git a/packages/tracing/test/hub.test.ts b/packages/tracing/test/hub.test.ts index 045d9fa96fb1..6df8bad175c1 100644 --- a/packages/tracing/test/hub.test.ts +++ b/packages/tracing/test/hub.test.ts @@ -1,6 +1,7 @@ /* eslint-disable @typescript-eslint/unbound-method */ import { BrowserClient } from '@sentry/browser'; import { setupBrowserTransport } from '@sentry/browser/src/transports'; +import { getDefaultBrowserClientOptions } from '@sentry/browser/test/unit/helper/browser-client-options'; import { Hub, makeMain } from '@sentry/hub'; import * as utilsModule from '@sentry/utils'; // for mocking import { logger } from '@sentry/utils'; @@ -33,7 +34,7 @@ describe('Hub', () => { describe('getTransaction()', () => { it('should find a transaction which has been set on the scope if sampled = true', () => { - const options = { tracesSampleRate: 1 }; + const options = getDefaultBrowserClientOptions({ tracesSampleRate: 1 }); const hub = new Hub(new BrowserClient(options, setupBrowserTransport(options).transport)); makeMain(hub); const transaction = hub.startTransaction({ name: 'dogpark' }); @@ -47,7 +48,7 @@ describe('Hub', () => { }); it('should find a transaction which has been set on the scope if sampled = false', () => { - const options = { tracesSampleRate: 1 }; + const options = getDefaultBrowserClientOptions({ tracesSampleRate: 1 }); const hub = new Hub(new BrowserClient(options, setupBrowserTransport(options).transport)); makeMain(hub); const transaction = hub.startTransaction({ name: 'dogpark', sampled: false }); @@ -60,7 +61,7 @@ describe('Hub', () => { }); it("should not find an open transaction if it's not on the scope", () => { - const options = { tracesSampleRate: 1 }; + const options = getDefaultBrowserClientOptions({ tracesSampleRate: 1 }); const hub = new Hub(new BrowserClient(options, setupBrowserTransport(options).transport)); makeMain(hub); hub.startTransaction({ name: 'dogpark' }); @@ -73,7 +74,7 @@ describe('Hub', () => { describe('default sample context', () => { it('should add transaction context data to default sample context', () => { const tracesSampler = jest.fn(); - const options = { tracesSampler }; + const options = getDefaultBrowserClientOptions({ tracesSampler }); const hub = new Hub(new BrowserClient(options, setupBrowserTransport(options).transport)); makeMain(hub); @@ -90,7 +91,7 @@ describe('Hub', () => { it("should add parent's sampling decision to default sample context", () => { const tracesSampler = jest.fn(); - const options = { tracesSampler }; + const options = getDefaultBrowserClientOptions({ tracesSampler }); const hub = new Hub(new BrowserClient(options, setupBrowserTransport(options).transport)); makeMain(hub); const parentSamplingDecsion = false; @@ -109,7 +110,7 @@ describe('Hub', () => { describe('sample()', () => { it('should set sampled = false when tracing is disabled', () => { - const options = {}; + const options = getDefaultBrowserClientOptions({}); // neither tracesSampleRate nor tracesSampler is defined -> tracing disabled const hub = new Hub(new BrowserClient(options, setupBrowserTransport(options).transport)); makeMain(hub); @@ -119,7 +120,7 @@ describe('Hub', () => { }); it('should set sampled = false if tracesSampleRate is 0', () => { - const options = { tracesSampleRate: 0 }; + const options = getDefaultBrowserClientOptions({ tracesSampleRate: 0 }); const hub = new Hub(new BrowserClient(options, setupBrowserTransport(options).transport)); makeMain(hub); const transaction = hub.startTransaction({ name: 'dogpark' }); @@ -128,7 +129,7 @@ describe('Hub', () => { }); it('should set sampled = true if tracesSampleRate is 1', () => { - const options = { tracesSampleRate: 1 }; + const options = getDefaultBrowserClientOptions({ tracesSampleRate: 1 }); const hub = new Hub(new BrowserClient(options, setupBrowserTransport(options).transport)); makeMain(hub); const transaction = hub.startTransaction({ name: 'dogpark' }); @@ -137,7 +138,7 @@ describe('Hub', () => { }); it('should set sampled = true if tracesSampleRate is 1 (without global hub)', () => { - const options = { tracesSampleRate: 1 }; + const options = getDefaultBrowserClientOptions({ tracesSampleRate: 1 }); const hub = new Hub(new BrowserClient(options, setupBrowserTransport(options).transport)); const transaction = hub.startTransaction({ name: 'dogpark' }); @@ -146,7 +147,7 @@ describe('Hub', () => { it("should call tracesSampler if it's defined", () => { const tracesSampler = jest.fn(); - const options = { tracesSampler }; + const options = getDefaultBrowserClientOptions({ tracesSampler }); const hub = new Hub(new BrowserClient(options, setupBrowserTransport(options).transport)); makeMain(hub); hub.startTransaction({ name: 'dogpark' }); @@ -156,7 +157,7 @@ describe('Hub', () => { it('should set sampled = false if tracesSampler returns 0', () => { const tracesSampler = jest.fn().mockReturnValue(0); - const options = { tracesSampler }; + const options = getDefaultBrowserClientOptions({ tracesSampler }); const hub = new Hub(new BrowserClient(options, setupBrowserTransport(options).transport)); makeMain(hub); const transaction = hub.startTransaction({ name: 'dogpark' }); @@ -167,7 +168,7 @@ describe('Hub', () => { it('should set sampled = true if tracesSampler returns 1', () => { const tracesSampler = jest.fn().mockReturnValue(1); - const options = { tracesSampler }; + const options = getDefaultBrowserClientOptions({ tracesSampler }); const hub = new Hub(new BrowserClient(options, setupBrowserTransport(options).transport)); makeMain(hub); const transaction = hub.startTransaction({ name: 'dogpark' }); @@ -178,7 +179,7 @@ describe('Hub', () => { it('should set sampled = true if tracesSampler returns 1 (without global hub)', () => { const tracesSampler = jest.fn().mockReturnValue(1); - const options = { tracesSampler }; + const options = getDefaultBrowserClientOptions({ tracesSampler }); const hub = new Hub(new BrowserClient(options, setupBrowserTransport(options).transport)); const transaction = hub.startTransaction({ name: 'dogpark' }); @@ -189,7 +190,7 @@ describe('Hub', () => { it('should not try to override explicitly set positive sampling decision', () => { // so that the decision otherwise would be false const tracesSampler = jest.fn().mockReturnValue(0); - const options = { tracesSampler }; + const options = getDefaultBrowserClientOptions({ tracesSampler }); const hub = new Hub(new BrowserClient(options, setupBrowserTransport(options).transport)); makeMain(hub); const transaction = hub.startTransaction({ name: 'dogpark', sampled: true }); @@ -200,7 +201,7 @@ describe('Hub', () => { it('should not try to override explicitly set negative sampling decision', () => { // so that the decision otherwise would be true const tracesSampler = jest.fn().mockReturnValue(1); - const options = { tracesSampler }; + const options = getDefaultBrowserClientOptions({ tracesSampler }); const hub = new Hub(new BrowserClient(options, setupBrowserTransport(options).transport)); makeMain(hub); const transaction = hub.startTransaction({ name: 'dogpark', sampled: false }); @@ -211,7 +212,7 @@ describe('Hub', () => { it('should prefer tracesSampler to tracesSampleRate', () => { // make the two options do opposite things to prove precedence const tracesSampler = jest.fn().mockReturnValue(true); - const options = { tracesSampleRate: 0, tracesSampler }; + const options = getDefaultBrowserClientOptions({ tracesSampleRate: 0, tracesSampler }); const hub = new Hub(new BrowserClient(options, setupBrowserTransport(options).transport)); makeMain(hub); const transaction = hub.startTransaction({ name: 'dogpark' }); @@ -222,7 +223,7 @@ describe('Hub', () => { it('should tolerate tracesSampler returning a boolean', () => { const tracesSampler = jest.fn().mockReturnValue(true); - const options = { tracesSampler }; + const options = getDefaultBrowserClientOptions({ tracesSampler }); const hub = new Hub(new BrowserClient(options, setupBrowserTransport(options).transport)); makeMain(hub); const transaction = hub.startTransaction({ name: 'dogpark' }); @@ -233,7 +234,7 @@ describe('Hub', () => { it('should record sampling method when sampling decision is explicitly set', () => { const tracesSampler = jest.fn().mockReturnValue(0.1121); - const options = { tracesSampler }; + const options = getDefaultBrowserClientOptions({ tracesSampler }); const hub = new Hub(new BrowserClient(options, setupBrowserTransport(options).transport)); makeMain(hub); hub.startTransaction({ name: 'dogpark', sampled: true }); @@ -245,7 +246,7 @@ describe('Hub', () => { it('should record sampling method and rate when sampling decision comes from tracesSampler', () => { const tracesSampler = jest.fn().mockReturnValue(0.1121); - const options = { tracesSampler }; + const options = getDefaultBrowserClientOptions({ tracesSampler }); const hub = new Hub(new BrowserClient(options, setupBrowserTransport(options).transport)); makeMain(hub); hub.startTransaction({ name: 'dogpark' }); @@ -256,7 +257,7 @@ describe('Hub', () => { }); it('should record sampling method when sampling decision is inherited', () => { - const options = { tracesSampleRate: 0.1121 }; + const options = getDefaultBrowserClientOptions({ tracesSampleRate: 0.1121 }); const hub = new Hub(new BrowserClient(options, setupBrowserTransport(options).transport)); makeMain(hub); hub.startTransaction({ name: 'dogpark', parentSampled: true }); @@ -267,7 +268,7 @@ describe('Hub', () => { }); it('should record sampling method and rate when sampling decision comes from traceSampleRate', () => { - const options = { tracesSampleRate: 0.1121 }; + const options = getDefaultBrowserClientOptions({ tracesSampleRate: 0.1121 }); const hub = new Hub(new BrowserClient(options, setupBrowserTransport(options).transport)); makeMain(hub); hub.startTransaction({ name: 'dogpark' }); @@ -280,7 +281,7 @@ describe('Hub', () => { describe('isValidSampleRate()', () => { it("should reject tracesSampleRates which aren't numbers or booleans", () => { - const options = { tracesSampleRate: 'dogs!' as any }; + const options = getDefaultBrowserClientOptions({ tracesSampleRate: 'dogs!' as any }); const hub = new Hub(new BrowserClient(options, setupBrowserTransport(options).transport)); makeMain(hub); hub.startTransaction({ name: 'dogpark' }); @@ -289,7 +290,7 @@ describe('Hub', () => { }); it('should reject tracesSampleRates which are NaN', () => { - const options = { tracesSampleRate: 'dogs!' as any }; + const options = getDefaultBrowserClientOptions({ tracesSampleRate: 'dogs!' as any }); const hub = new Hub(new BrowserClient(options, setupBrowserTransport(options).transport)); makeMain(hub); hub.startTransaction({ name: 'dogpark' }); @@ -299,7 +300,7 @@ describe('Hub', () => { // the rate might be a boolean, but for our purposes, false is equivalent to 0 and true is equivalent to 1 it('should reject tracesSampleRates less than 0', () => { - const options = { tracesSampleRate: -26 }; + const options = getDefaultBrowserClientOptions({ tracesSampleRate: -26 }); const hub = new Hub(new BrowserClient(options, setupBrowserTransport(options).transport)); makeMain(hub); hub.startTransaction({ name: 'dogpark' }); @@ -309,7 +310,7 @@ describe('Hub', () => { // the rate might be a boolean, but for our purposes, false is equivalent to 0 and true is equivalent to 1 it('should reject tracesSampleRates greater than 1', () => { - const options = { tracesSampleRate: 26 }; + const options = getDefaultBrowserClientOptions({ tracesSampleRate: 26 }); const hub = new Hub(new BrowserClient(options, setupBrowserTransport(options).transport)); makeMain(hub); hub.startTransaction({ name: 'dogpark' }); @@ -319,7 +320,7 @@ describe('Hub', () => { it("should reject tracesSampler return values which aren't numbers or booleans", () => { const tracesSampler = jest.fn().mockReturnValue('dogs!'); - const options = { tracesSampler }; + const options = getDefaultBrowserClientOptions({ tracesSampler }); const hub = new Hub(new BrowserClient(options, setupBrowserTransport(options).transport)); makeMain(hub); hub.startTransaction({ name: 'dogpark' }); @@ -329,7 +330,7 @@ describe('Hub', () => { it('should reject tracesSampler return values which are NaN', () => { const tracesSampler = jest.fn().mockReturnValue(NaN); - const options = { tracesSampler }; + const options = getDefaultBrowserClientOptions({ tracesSampler }); const hub = new Hub(new BrowserClient(options, setupBrowserTransport(options).transport)); makeMain(hub); hub.startTransaction({ name: 'dogpark' }); @@ -340,7 +341,7 @@ describe('Hub', () => { // the rate might be a boolean, but for our purposes, false is equivalent to 0 and true is equivalent to 1 it('should reject tracesSampler return values less than 0', () => { const tracesSampler = jest.fn().mockReturnValue(-12); - const options = { tracesSampler }; + const options = getDefaultBrowserClientOptions({ tracesSampler }); const hub = new Hub(new BrowserClient(options, setupBrowserTransport(options).transport)); makeMain(hub); hub.startTransaction({ name: 'dogpark' }); @@ -351,7 +352,7 @@ describe('Hub', () => { // the rate might be a boolean, but for our purposes, false is equivalent to 0 and true is equivalent to 1 it('should reject tracesSampler return values greater than 1', () => { const tracesSampler = jest.fn().mockReturnValue(31); - const options = { tracesSampler }; + const options = getDefaultBrowserClientOptions({ tracesSampler }); const hub = new Hub(new BrowserClient(options, setupBrowserTransport(options).transport)); makeMain(hub); hub.startTransaction({ name: 'dogpark' }); @@ -361,7 +362,7 @@ describe('Hub', () => { }); it('should drop transactions with sampled = false', () => { - const options = { tracesSampleRate: 0 }; + const options = getDefaultBrowserClientOptions({ tracesSampleRate: 0 }); const client = new BrowserClient(options, setupBrowserTransport(options).transport); jest.spyOn(client, 'captureEvent'); @@ -379,7 +380,7 @@ describe('Hub', () => { describe('sampling inheritance', () => { it('should propagate sampling decision to child spans', () => { - const options = { tracesSampleRate: Math.random() }; + const options = getDefaultBrowserClientOptions({ tracesSampleRate: Math.random() }); const hub = new Hub(new BrowserClient(options, setupBrowserTransport(options).transport)); makeMain(hub); const transaction = hub.startTransaction({ name: 'dogpark' }); @@ -392,11 +393,11 @@ describe('Hub', () => { testOnlyIfNodeVersionAtLeast(10)( 'should propagate positive sampling decision to child transactions in XHR header', async () => { - const options = { + const options = getDefaultBrowserClientOptions({ dsn: 'https://1231@dogs.are.great/1121', tracesSampleRate: 1, integrations: [new BrowserTracing()], - }; + }); const hub = new Hub(new BrowserClient(options, setupBrowserTransport(options).transport)); makeMain(hub); @@ -433,11 +434,11 @@ describe('Hub', () => { testOnlyIfNodeVersionAtLeast(10)( 'should propagate negative sampling decision to child transactions in XHR header', async () => { - const options = { + const options = getDefaultBrowserClientOptions({ dsn: 'https://1231@dogs.are.great/1121', tracesSampleRate: 1, integrations: [new BrowserTracing()], - }; + }); const hub = new Hub(new BrowserClient(options, setupBrowserTransport(options).transport)); makeMain(hub); @@ -483,7 +484,7 @@ describe('Hub', () => { // sample rate), so make parent's decision the opposite to prove that inheritance takes precedence over // tracesSampleRate mathRandom.mockReturnValueOnce(1); - const options = { tracesSampleRate: 0.5 }; + const options = getDefaultBrowserClientOptions({ tracesSampleRate: 0.5 }); const hub = new Hub(new BrowserClient(options, setupBrowserTransport(options).transport)); makeMain(hub); const parentSamplingDecsion = true; @@ -498,7 +499,7 @@ describe('Hub', () => { }); it("should inherit parent's negative sampling decision if tracesSampler is undefined", () => { - const options = { tracesSampleRate: 1 }; + const options = getDefaultBrowserClientOptions({ tracesSampleRate: 1 }); // tracesSampleRate = 1 means every transaction should end up with sampled = true, so make parent's decision the // opposite to prove that inheritance takes precedence over tracesSampleRate const hub = new Hub(new BrowserClient(options, setupBrowserTransport(options).transport)); @@ -520,7 +521,7 @@ describe('Hub', () => { const tracesSampler = () => true; const parentSamplingDecsion = false; - const options = { tracesSampler }; + const options = getDefaultBrowserClientOptions({ tracesSampler }); const hub = new Hub(new BrowserClient(options, setupBrowserTransport(options).transport)); makeMain(hub); @@ -539,7 +540,7 @@ describe('Hub', () => { const tracesSampler = () => false; const parentSamplingDecsion = true; - const options = { tracesSampler }; + const options = getDefaultBrowserClientOptions({ tracesSampler }); const hub = new Hub(new BrowserClient(options, setupBrowserTransport(options).transport)); makeMain(hub); diff --git a/packages/tracing/test/idletransaction.test.ts b/packages/tracing/test/idletransaction.test.ts index b8029c6b92d7..b040f4adbd9b 100644 --- a/packages/tracing/test/idletransaction.test.ts +++ b/packages/tracing/test/idletransaction.test.ts @@ -1,4 +1,5 @@ import { BrowserClient, Transports } from '@sentry/browser'; +import { getDefaultBrowserClientOptions } from '@sentry/browser/test/unit/helper/browser-client-options'; import { Hub } from '@sentry/hub'; import { @@ -16,8 +17,8 @@ class SimpleTransport extends Transports.BaseTransport {} const dsn = 'https://123@sentry.io/42'; let hub: Hub; beforeEach(() => { - const options = { dsn, tracesSampleRate: 1, transport: SimpleTransport }; - hub = new Hub(new BrowserClient(options, new SimpleTransport(options))); + const options = getDefaultBrowserClientOptions({ dsn, tracesSampleRate: 1, transport: SimpleTransport }); + hub = new Hub(new BrowserClient(options, new SimpleTransport({ dsn }))); }); describe('IdleTransaction', () => { diff --git a/packages/tracing/test/span.test.ts b/packages/tracing/test/span.test.ts index fe11fbf143ee..13f47c51ae7b 100644 --- a/packages/tracing/test/span.test.ts +++ b/packages/tracing/test/span.test.ts @@ -1,5 +1,6 @@ import { BrowserClient } from '@sentry/browser'; import { setupBrowserTransport } from '@sentry/browser/src/transports'; +import { getDefaultBrowserClientOptions } from '@sentry/browser/test/unit/helper/browser-client-options'; import { Hub, makeMain, Scope } from '@sentry/hub'; import { Span, Transaction } from '../src'; @@ -10,7 +11,7 @@ describe('Span', () => { beforeEach(() => { const myScope = new Scope(); - const options = { tracesSampleRate: 1 }; + const options = getDefaultBrowserClientOptions({ tracesSampleRate: 1 }); hub = new Hub(new BrowserClient(options, setupBrowserTransport(options).transport), myScope); makeMain(hub); }); @@ -218,10 +219,10 @@ describe('Span', () => { }); test('maxSpans correctly limits number of spans', () => { - const options = { + const options = getDefaultBrowserClientOptions({ _experiments: { maxSpans: 3 }, tracesSampleRate: 1, - }; + }); const _hub = new Hub(new BrowserClient(options, setupBrowserTransport(options).transport)); const spy = jest.spyOn(_hub as any, 'captureEvent') as any; const transaction = _hub.startTransaction({ name: 'test' }); @@ -234,9 +235,9 @@ describe('Span', () => { }); test('no span recorder created if transaction.sampled is false', () => { - const options = { + const options = getDefaultBrowserClientOptions({ tracesSampleRate: 1, - }; + }); const _hub = new Hub(new BrowserClient(options, setupBrowserTransport(options).transport)); const spy = jest.spyOn(_hub as any, 'captureEvent') as any; const transaction = _hub.startTransaction({ name: 'test', sampled: false }); diff --git a/packages/types/src/client.ts b/packages/types/src/client.ts index c3f2a9920258..f0cdc8b3a988 100644 --- a/packages/types/src/client.ts +++ b/packages/types/src/client.ts @@ -1,7 +1,7 @@ import { DsnComponents } from './dsn'; import { Event, EventHint } from './event'; import { Integration, IntegrationClass } from './integration'; -import { Options } from './options'; +import { ClientOptions } from './options'; import { Scope } from './scope'; import { Session } from './session'; import { Severity, SeverityLevel } from './severity'; @@ -16,7 +16,7 @@ import { Transport } from './transport'; * there will only be one instance during runtime. * */ -export interface Client { +export interface Client { /** * Captures an exception event and sends it to Sentry. * diff --git a/packages/types/src/index.ts b/packages/types/src/index.ts index 84430c1d3976..5fe4ced6375b 100644 --- a/packages/types/src/index.ts +++ b/packages/types/src/index.ts @@ -27,7 +27,7 @@ export type { Hub } from './hub'; export type { Integration, IntegrationClass } from './integration'; export type { Mechanism } from './mechanism'; export type { ExtractedNodeRequestData, Primitive, WorkerLocation } from './misc'; -export type { Options } from './options'; +export type { ClientOptions, Options } from './options'; export type { Package } from './package'; export type { QueryParams, Request, SentryRequest, SentryRequestType } from './request'; export type { Response } from './response'; diff --git a/packages/types/src/options.ts b/packages/types/src/options.ts index a7306acdd76f..8bd273752700 100644 --- a/packages/types/src/options.ts +++ b/packages/types/src/options.ts @@ -7,8 +7,7 @@ import { StackLineParser, StackParser } from './stacktrace'; import { SamplingContext } from './transaction'; import { Transport, TransportClass, TransportOptions } from './transport'; -/** Base configuration options for every SDK. */ -export interface Options { +export interface ClientOptions { /** * Enable debug functionality in the SDK itself */ @@ -20,6 +19,21 @@ export interface Options { */ enabled?: boolean; + /** Attaches stacktraces to pure capture message / log integrations */ + attachStacktrace?: boolean; + + /** + * A flag enabling Sessions Tracking feature. + * By default, Sessions Tracking is enabled. + */ + autoSessionTracking?: boolean; + + /** + * Send SDK Client Reports. + * By default, Client Reports are enabled. + */ + sendClientReports?: boolean; + /** * The Dsn used to connect to Sentry and identify the project. If omitted, the * SDK will not send any data to Sentry. @@ -27,29 +41,33 @@ export interface Options { dsn?: string; /** - * If this is set to false, default integrations will not be added, otherwise this will internally be set to the - * recommended default integrations. - * TODO: We should consider changing this to `boolean | Integration[]` + * The release identifier used when uploading respective source maps. Specify + * this value to allow Sentry to resolve the correct source maps when + * processing events. */ - defaultIntegrations?: false | Integration[]; + release?: string; + + /** The current environment of your application (e.g. "production"). */ + environment?: string; + + /** Sets the distribution for all events */ + dist?: string; /** * List of integrations that should be installed after SDK was initialized. - * Accepts either a list of integrations or a function that receives - * default integrations and returns a new, updated list. */ - integrations?: Integration[] | ((integrations: Integration[]) => Integration[]); + integrations: Integration[]; /** - * A pattern for error messages which should not be sent to Sentry. - * By default, all errors will be sent. + * Transport object that should be used to send events to Sentry */ - ignoreErrors?: Array; + transport: TransportClass; /** - * Transport object that should be used to send events to Sentry + * A stack parser implementation + * By default, a stack parser is supplied for all supported platforms */ - transport?: TransportClass; + stackParser: StackParser; /** * Options for the default transport that the SDK uses. @@ -57,24 +75,20 @@ export interface Options { transportOptions?: TransportOptions; /** - * A URL to an envelope tunnel endpoint. An envelope tunnel is an HTTP endpoint - * that accepts Sentry envelopes for forwarding. This can be used to force data - * through a custom server independent of the type of data. + * Sample rate to determine trace sampling. + * + * 0.0 = 0% chance of a given trace being sent (send no traces) 1.0 = 100% chance of a given trace being sent (send + * all traces) + * + * Tracing is enabled if either this or `tracesSampler` is defined. If both are defined, `tracesSampleRate` is + * ignored. */ - tunnel?: string; + tracesSampleRate?: number; /** - * The release identifier used when uploading respective source maps. Specify - * this value to allow Sentry to resolve the correct source maps when - * processing events. + * Initial data to populate scope. */ - release?: string; - - /** The current environment of your application (e.g. "production"). */ - environment?: string; - - /** Sets the distribution for all events */ - dist?: string; + initialScope?: CaptureContext; /** * The maximum number of breadcrumbs sent with events. Defaults to 100. @@ -85,9 +99,6 @@ export interface Options { /** A global sample rate to apply to all events (0 - 1). */ sampleRate?: number; - /** Attaches stacktraces to pure capture message / log integrations */ - attachStacktrace?: boolean; - /** Maximum number of chars a single value can have before it will be truncated. */ maxValueLength?: number; @@ -123,38 +134,17 @@ export interface Options { shutdownTimeout?: number; /** - * Sample rate to determine trace sampling. - * - * 0.0 = 0% chance of a given trace being sent (send no traces) 1.0 = 100% chance of a given trace being sent (send - * all traces) - * - * Tracing is enabled if either this or `tracesSampler` is defined. If both are defined, `tracesSampleRate` is - * ignored. - */ - tracesSampleRate?: number; - - /** - * A flag enabling Sessions Tracking feature. - * By default, Sessions Tracking is enabled. - */ - autoSessionTracking?: boolean; - - /** - * Send SDK Client Reports. - * By default, Client Reports are enabled. - */ - sendClientReports?: boolean; - - /** - * Initial data to populate scope. + * A pattern for error messages which should not be sent to Sentry. + * By default, all errors will be sent. */ - initialScope?: CaptureContext; + ignoreErrors?: Array; /** - * A stack parser implementation or an array of stack line parsers - * By default, a stack parser is supplied for all supported browsers + * A URL to an envelope tunnel endpoint. An envelope tunnel is an HTTP endpoint + * that accepts Sentry envelopes for forwarding. This can be used to force data + * through a custom server independent of the type of data. */ - stackParser?: StackParser | StackLineParser[]; + tunnel?: string; /** * Set of metadata about the SDK that can be internally used to enhance envelopes and events, @@ -210,3 +200,31 @@ export interface Options { */ beforeBreadcrumb?: (breadcrumb: Breadcrumb, hint?: BreadcrumbHint) => Breadcrumb | null; } + +/** Base configuration options for every SDK. */ +export interface Options extends Omit, 'integrations' | 'transport' | 'stackParser'> { + /** + * If this is set to false, default integrations will not be added, otherwise this will internally be set to the + * recommended default integrations. + * TODO: We should consider changing this to `boolean | Integration[]` + */ + defaultIntegrations?: false | Integration[]; + + /** + * List of integrations that should be installed after SDK was initialized. + * Accepts either a list of integrations or a function that receives + * default integrations and returns a new, updated list. + */ + integrations?: Integration[] | ((integrations: Integration[]) => Integration[]); + + /** + * Transport object that should be used to send events to Sentry + */ + transport?: TransportClass; + + /** + * A stack parser implementation or an array of stack line parsers + * By default, a stack parser is supplied for all supported browsers + */ + stackParser?: StackParser | StackLineParser[]; +} diff --git a/packages/utils/src/dsn.ts b/packages/utils/src/dsn.ts index 5c500c5ec654..56b864e2863b 100644 --- a/packages/utils/src/dsn.ts +++ b/packages/utils/src/dsn.ts @@ -98,8 +98,6 @@ function validateDsn(dsn: DsnComponents): boolean | void { /** The Sentry Dsn, identifying a Sentry instance and project. */ export function makeDsn(from: DsnLike): DsnComponents { const components = typeof from === 'string' ? dsnFromString(from) : dsnFromComponents(from); - validateDsn(components); - return components; } From 1cc0e8ab6b9bd386bab3f49d503463398c877e68 Mon Sep 17 00:00:00 2001 From: Katie Byers Date: Tue, 19 Apr 2022 18:27:26 -0700 Subject: [PATCH 061/204] fix(dev): Check out correct commit in manual GHA `Build & Test` workflow (#4954) Our `Build & Test` GHA workflow can be triggered manually, and when you trigger it, you can either pick the branch to run it on or give it a specific commit to test. The former works fine, but the latter does not - though it picks up the given commit and correctly puts it in the env, it doesn't actually use that value when checking out the commit, instead always relying on the default value. This fixes that by always specifying what value to use. --- .github/workflows/build.yml | 27 ++++++++++++++++++++++++++- 1 file changed, 26 insertions(+), 1 deletion(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 28895bde0cb1..0ff66932e93b 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -41,6 +41,8 @@ jobs: steps: - name: Check out current commit (${{ env.HEAD_COMMIT }}) uses: actions/checkout@v2 + with: + ref: ${{ env.HEAD_COMMIT }} - name: Set up Node uses: actions/setup-node@v1 # we use a hash of yarn.lock as our cache key, because if it hasn't changed, our dependencies haven't changed, @@ -68,6 +70,8 @@ jobs: steps: - name: Check out current commit (${{ env.HEAD_COMMIT }}) uses: actions/checkout@v2 + with: + ref: ${{ env.HEAD_COMMIT }} - name: Set up Node uses: actions/setup-node@v1 - name: Check dependency cache @@ -109,6 +113,8 @@ jobs: steps: - name: Check out current commit (${{ env.HEAD_COMMIT }}) uses: actions/checkout@v2 + with: + ref: ${{ env.HEAD_COMMIT }} - name: Set up Node uses: actions/setup-node@v1 with: @@ -139,6 +145,8 @@ jobs: steps: - name: Check out current commit (${{ env.HEAD_COMMIT }}) uses: actions/checkout@v2 + with: + ref: ${{ env.HEAD_COMMIT }} - name: Set up Node uses: actions/setup-node@v1 - name: Check dependency cache @@ -162,6 +170,8 @@ jobs: steps: - name: Check out current commit (${{ env.HEAD_COMMIT }}) uses: actions/checkout@v2 + with: + ref: ${{ env.HEAD_COMMIT }} - name: Set up Node uses: actions/setup-node@v1 - name: Check dependency cache @@ -186,6 +196,8 @@ jobs: steps: - name: Check out current commit (${{ github.sha }}) uses: actions/checkout@v2 + with: + ref: ${{ env.HEAD_COMMIT }} - name: Set up Node uses: actions/setup-node@v1 - name: Check dependency cache @@ -223,6 +235,8 @@ jobs: steps: - name: Check out current commit (${{ env.HEAD_COMMIT }}) uses: actions/checkout@v2 + with: + ref: ${{ env.HEAD_COMMIT }} - name: Set up Node uses: actions/setup-node@v1 with: @@ -256,6 +270,8 @@ jobs: steps: - name: Check out current commit (${{ env.HEAD_COMMIT }}) uses: actions/checkout@v2 + with: + ref: ${{ env.HEAD_COMMIT }} - name: Set up Node uses: actions/setup-node@v1 with: @@ -288,11 +304,12 @@ jobs: steps: - name: Check out current commit (${{ env.HEAD_COMMIT }}) uses: actions/checkout@v2 + with: + ref: ${{ env.HEAD_COMMIT }} # TODO: removing `fetch-depth` below seems to have no effect, and the commit which added it had no description, # so it's not clear why it's necessary. That said, right now ember tests are xfail, so it's a little hard to # tell if it's safe to remove. Once ember tests are fixed, let's try again with it turned off, and if all goes # well, we can pull it out. - with: fetch-depth: 0 - name: Set up Node uses: actions/setup-node@v1 @@ -343,6 +360,8 @@ jobs: steps: - name: Check out current commit (${{ env.HEAD_COMMIT }}) uses: actions/checkout@v2 + with: + ref: ${{ env.HEAD_COMMIT }} - name: Set up Node uses: actions/setup-node@v1 with: @@ -381,6 +400,8 @@ jobs: steps: - name: Check out current commit (${{ env.HEAD_COMMIT }}) uses: actions/checkout@v2 + with: + ref: ${{ env.HEAD_COMMIT }} - name: Set up Node uses: actions/setup-node@v1 - name: Check dependency cache @@ -410,6 +431,8 @@ jobs: steps: - name: Check out current commit (${ env.HEAD_COMMIT }}) uses: actions/checkout@v2 + with: + ref: ${{ env.HEAD_COMMIT }} - name: Set up Node uses: actions/setup-node@v1 with: @@ -445,6 +468,8 @@ jobs: steps: - name: Check out current commit (${{ github.sha }}) uses: actions/checkout@v2 + with: + ref: ${{ env.HEAD_COMMIT }} - name: Set up Node uses: actions/setup-node@v1 with: From 1b32e54d59c3f509d599c9e1857a8cb94eb9a20f Mon Sep 17 00:00:00 2001 From: Luca Forstner Date: Wed, 20 Apr 2022 11:33:57 +0200 Subject: [PATCH 062/204] ref(core): Simplify the setup logic of integrations (#4952) --- packages/core/src/baseclient.ts | 10 +++++--- packages/core/src/integration.ts | 39 +++++++++++++------------------- 2 files changed, 23 insertions(+), 26 deletions(-) diff --git a/packages/core/src/baseclient.ts b/packages/core/src/baseclient.ts index 3d82d829cead..314d3d6216a0 100644 --- a/packages/core/src/baseclient.ts +++ b/packages/core/src/baseclient.ts @@ -75,9 +75,12 @@ export abstract class BaseClient implements Client { /** The client Dsn, if specified in options. Without this Dsn, the SDK will be disabled. */ protected readonly _dsn?: DsnComponents; - /** Array of used integrations. */ + /** Array of set up integrations. */ protected _integrations: IntegrationIndex = {}; + /** Indicates whether this client's integrations have been set up. */ + protected _integrationsInitialized: boolean = false; + /** Number of calls being processed */ protected _numProcessing: number = 0; @@ -251,8 +254,9 @@ export abstract class BaseClient implements Client { * Sets up the integrations */ public setupIntegrations(): void { - if (this._isEnabled() && !this._integrations.initialized) { - this._integrations = setupIntegrations(this._options); + if (this._isEnabled() && !this._integrationsInitialized) { + this._integrations = setupIntegrations(this._options.integrations); + this._integrationsInitialized = true; } } diff --git a/packages/core/src/integration.ts b/packages/core/src/integration.ts index b4f35e7d5b1e..e45f4a7c09d7 100644 --- a/packages/core/src/integration.ts +++ b/packages/core/src/integration.ts @@ -1,6 +1,6 @@ import { addGlobalEventProcessor, getCurrentHub } from '@sentry/hub'; -import { ClientOptions, Integration, Options } from '@sentry/types'; -import { addNonEnumerableProperty, logger } from '@sentry/utils'; +import { Integration, Options } from '@sentry/types'; +import { logger } from '@sentry/utils'; import { IS_DEBUG_BUILD } from './flags'; @@ -9,7 +9,7 @@ export const installedIntegrations: string[] = []; /** Map of integrations assigned to a client */ export type IntegrationIndex = { [key: string]: Integration; -} & { initialized?: boolean }; +}; /** * @private @@ -54,31 +54,24 @@ export function getIntegrationsToSetup(options: Options): Integration[] { return integrations; } -/** Setup given integration */ -export function setupIntegration(integration: Integration): void { - if (installedIntegrations.indexOf(integration.name) !== -1) { - return; - } - integration.setupOnce(addGlobalEventProcessor, getCurrentHub); - installedIntegrations.push(integration.name); - IS_DEBUG_BUILD && logger.log(`Integration installed: ${integration.name}`); -} - /** * Given a list of integration instances this installs them all. When `withDefaults` is set to `true` then all default * integrations are added unless they were already provided before. * @param integrations array of integration instances * @param withDefault should enable default integrations */ -export function setupIntegrations(options: O): IntegrationIndex { - const integrations: IntegrationIndex = {}; - options.integrations.forEach(integration => { - integrations[integration.name] = integration; - setupIntegration(integration); +export function setupIntegrations(integrations: Integration[]): IntegrationIndex { + const integrationIndex: IntegrationIndex = {}; + + integrations.forEach(integration => { + integrationIndex[integration.name] = integration; + + if (installedIntegrations.indexOf(integration.name) === -1) { + integration.setupOnce(addGlobalEventProcessor, getCurrentHub); + installedIntegrations.push(integration.name); + IS_DEBUG_BUILD && logger.log(`Integration installed: ${integration.name}`); + } }); - // set the `initialized` flag so we don't run through the process again unecessarily; use `Object.defineProperty` - // because by default it creates a property which is nonenumerable, which we want since `initialized` shouldn't be - // considered a member of the index the way the actual integrations are - addNonEnumerableProperty(integrations, 'initialized', true); - return integrations; + + return integrationIndex; } From 03d978a867d9a8a981376811c42ecd7ca13623ae Mon Sep 17 00:00:00 2001 From: Luca Forstner Date: Thu, 21 Apr 2022 11:28:42 +0200 Subject: [PATCH 063/204] ref(build): Rename CDN bundles to be es6 per default (#4958) --- .size-limit.js | 12 ++++++------ packages/browser/rollup.bundle.config.js | 2 +- .../integration-tests/utils/generatePlugin.ts | 16 ++++++++-------- packages/integrations/rollup.bundle.config.js | 2 +- packages/tracing/rollup.bundle.config.js | 2 +- packages/vue/rollup.bundle.config.js | 2 +- packages/wasm/rollup.bundle.config.js | 2 +- 7 files changed, 19 insertions(+), 19 deletions(-) diff --git a/.size-limit.js b/.size-limit.js index 8c51fdb70b84..a8df3eb88e83 100644 --- a/.size-limit.js +++ b/.size-limit.js @@ -1,25 +1,25 @@ module.exports = [ { name: '@sentry/browser - ES5 CDN Bundle (gzipped + minified)', - path: 'packages/browser/build/bundles/bundle.min.js', + path: 'packages/browser/build/bundles/bundle.es5.min.js', gzip: true, limit: '100 KB', }, { name: '@sentry/browser - ES5 CDN Bundle (minified)', - path: 'packages/browser/build/bundles/bundle.min.js', + path: 'packages/browser/build/bundles/bundle.es5.min.js', gzip: false, limit: '120 KB', }, { name: '@sentry/browser - ES6 CDN Bundle (gzipped + minified)', - path: 'packages/browser/build/bundles/bundle.es6.min.js', + path: 'packages/browser/build/bundles/bundle.min.js', gzip: true, limit: '100 KB', }, { name: '@sentry/browser - ES6 CDN Bundle (minified)', - path: 'packages/browser/build/bundles/bundle.es6.min.js', + path: 'packages/browser/build/bundles/bundle.min.js', gzip: false, limit: '120 KB', }, @@ -53,13 +53,13 @@ module.exports = [ }, { name: '@sentry/browser + @sentry/tracing - ES5 CDN Bundle (gzipped + minified)', - path: 'packages/tracing/build/bundles/bundle.tracing.min.js', + path: 'packages/tracing/build/bundles/bundle.tracing.es5.min.js', gzip: true, limit: '100 KB', }, { name: '@sentry/browser + @sentry/tracing - ES6 CDN Bundle (gzipped + minified)', - path: 'packages/tracing/build/bundles/bundle.tracing.es6.min.js', + path: 'packages/tracing/build/bundles/bundle.tracing.min.js', gzip: true, limit: '100 KB', }, diff --git a/packages/browser/rollup.bundle.config.js b/packages/browser/rollup.bundle.config.js index aec9560621b1..04ce4b438bc3 100644 --- a/packages/browser/rollup.bundle.config.js +++ b/packages/browser/rollup.bundle.config.js @@ -8,7 +8,7 @@ const builds = []; isAddOn: false, jsVersion, licenseTitle: '@sentry/browser', - outputFileBase: `bundles/bundle${jsVersion === 'es6' ? '.es6' : ''}`, + outputFileBase: `bundles/bundle${jsVersion === 'es5' ? '.es5' : ''}`, }); builds.push(...makeBundleConfigVariants(baseBundleConfig)); diff --git a/packages/integration-tests/utils/generatePlugin.ts b/packages/integration-tests/utils/generatePlugin.ts index 1a7ebefad0b7..62a91b31dc9c 100644 --- a/packages/integration-tests/utils/generatePlugin.ts +++ b/packages/integration-tests/utils/generatePlugin.ts @@ -19,18 +19,18 @@ const BUNDLE_PATHS: Record> = { browser: { cjs: 'build/npm/cjs/index.js', esm: 'build/npm/esm/index.js', - bundle_es5: 'build/bundles/bundle.js', - bundle_es5_min: 'build/bundles/bundle.min.js', - bundle_es6: 'build/bundles/bundle.es6.js', - bundle_es6_min: 'build/bundles/bundle.es6.min.js', + bundle_es5: 'build/bundles/bundle.es5.js', + bundle_es5_min: 'build/bundles/bundle.es5.min.js', + bundle_es6: 'build/bundles/bundle.js', + bundle_es6_min: 'build/bundles/bundle.min.js', }, tracing: { cjs: 'build/npm/cjs/index.js', esm: 'build/npm/esm/index.js', - bundle_es5: 'build/bundles/bundle.tracing.js', - bundle_es5_min: 'build/bundles/bundle.tracing.min.js', - bundle_es6: 'build/bundles/bundle.tracing.es6.js', - bundle_es6_min: 'build/bundles/bundle.tracing.es6.min.js', + bundle_es5: 'build/bundles/bundle.tracing.es5.js', + bundle_es5_min: 'build/bundles/bundle.tracing.es5.min.js', + bundle_es6: 'build/bundles/bundle.tracing.js', + bundle_es6_min: 'build/bundles/bundle.tracing.min.js', }, }; diff --git a/packages/integrations/rollup.bundle.config.js b/packages/integrations/rollup.bundle.config.js index de984497b9a8..9549a07b8f94 100644 --- a/packages/integrations/rollup.bundle.config.js +++ b/packages/integrations/rollup.bundle.config.js @@ -12,7 +12,7 @@ const baseBundleConfig = makeBaseBundleConfig({ isAddOn: true, jsVersion, licenseTitle: '@sentry/integrations', - outputFileBase: `bundles/${file.replace('.ts', '')}${jsVersion === 'ES6' ? '.es6' : ''}`, + outputFileBase: `bundles/${file.replace('.ts', '')}${jsVersion === 'ES5' ? '.es5' : ''}`, }); // TODO We only need `commonjs` for localforage (used in the offline plugin). Once that's fixed, this can come out. diff --git a/packages/tracing/rollup.bundle.config.js b/packages/tracing/rollup.bundle.config.js index 66d79286f38c..091cb1f56958 100644 --- a/packages/tracing/rollup.bundle.config.js +++ b/packages/tracing/rollup.bundle.config.js @@ -8,7 +8,7 @@ const builds = []; isAddOn: false, jsVersion, licenseTitle: '@sentry/tracing & @sentry/browser', - outputFileBase: `bundles/bundle.tracing${jsVersion === 'es6' ? '.es6' : ''}`, + outputFileBase: `bundles/bundle.tracing${jsVersion === 'es5' ? '.es5' : ''}`, }); builds.push(...makeBundleConfigVariants(baseBundleConfig)); diff --git a/packages/vue/rollup.bundle.config.js b/packages/vue/rollup.bundle.config.js index 745205cda85f..41bf0e7f659d 100644 --- a/packages/vue/rollup.bundle.config.js +++ b/packages/vue/rollup.bundle.config.js @@ -3,7 +3,7 @@ import { makeBaseBundleConfig, makeBundleConfigVariants } from '../../rollup/ind const baseBundleConfig = makeBaseBundleConfig({ input: 'src/index.bundle.ts', isAddOn: false, - jsVersion: 'es5', + jsVersion: 'es6', licenseTitle: '@sentry/vue', outputFileBase: 'bundle.vue', }); diff --git a/packages/wasm/rollup.bundle.config.js b/packages/wasm/rollup.bundle.config.js index 265b557c76a7..e928d466049d 100644 --- a/packages/wasm/rollup.bundle.config.js +++ b/packages/wasm/rollup.bundle.config.js @@ -3,7 +3,7 @@ import { makeBaseBundleConfig, makeBundleConfigVariants } from '../../rollup/ind const baseBundleConfig = makeBaseBundleConfig({ input: 'src/index.ts', isAddOn: true, - jsVersion: 'es5', + jsVersion: 'es6', licenseTitle: '@sentry/wasm', outputFileBase: 'bundles/wasm', }); From 67cb07a313ccad2650454bf5a260e4f6e37e91a6 Mon Sep 17 00:00:00 2001 From: Lukas Stracke Date: Thu, 21 Apr 2022 16:43:20 +0200 Subject: [PATCH 064/204] fix(angular): Use Angular compiler to compile @sentry/angular (#4641) * switch from tsc to the Angular compiler to compile our Angular SDK. This is done via the Angular CLI which internally uses ngc and ng-packagr to build and package our SDK correctly so that the Angular runtime can process all SDK components (including TraceDirective) correctly. * important: This is a breaking change as it requires SDK users to use Angular >= 10. Currently, our SDK might support earlier Angular versions despite the fact that we only list Angular 10-13 as peer dependencies of the SDK. With this new compiler, applications using the Sentry Angular SDK on earlier versions than Angular 10 will not be able to compile correctly. * change some yarn scripts by making the Angular compiler the default builder --- packages/angular/angular.json | 27 + packages/angular/ng-package.json | 13 + packages/angular/package.json | 42 +- packages/angular/src/errorhandler.ts | 4 +- packages/angular/tsconfig.cjs.json | 8 - packages/angular/tsconfig.esm.json | 8 - packages/angular/tsconfig.ngc.json | 28 + packages/angular/tsconfig.types.json | 10 - yarn.lock | 3027 ++++++++++++++++++++++++-- 9 files changed, 2891 insertions(+), 276 deletions(-) create mode 100644 packages/angular/angular.json create mode 100644 packages/angular/ng-package.json delete mode 100644 packages/angular/tsconfig.cjs.json delete mode 100644 packages/angular/tsconfig.esm.json create mode 100644 packages/angular/tsconfig.ngc.json delete mode 100644 packages/angular/tsconfig.types.json diff --git a/packages/angular/angular.json b/packages/angular/angular.json new file mode 100644 index 000000000000..f771c0e78df0 --- /dev/null +++ b/packages/angular/angular.json @@ -0,0 +1,27 @@ +/* To learn more about this file see: https://angular.io/guide/workspace-config */ +{ + "$schema": "../../node_modules/@angular/cli/lib/config/schema.json", + "version": 1, // version of angular.json + "projects": { + "sentry-angular": { + "projectType": "library", + "root": ".", + "sourceRoot": "src", + "architect": { + "build": { + "builder": "@angular-devkit/build-angular:ng-packagr", + "options": { + "tsConfig": "tsconfig.ngc.json", + "project": "ng-package.json" + }, + "configurations": { + "production": { + "tsConfig": "tsconfig.ngc.json" + } + } + } + } + } + }, + "defaultProject": "sentry-angular" +} diff --git a/packages/angular/ng-package.json b/packages/angular/ng-package.json new file mode 100644 index 000000000000..88df70c1c7bd --- /dev/null +++ b/packages/angular/ng-package.json @@ -0,0 +1,13 @@ +{ + "$schema": "node_modules/ng-packagr/ng-package.schema.json", + "dest": "build", + "lib": { + "entryFile": "src/index.ts", + "umdModuleIds": { + "@sentry/browser": "Sentry", + "@sentry/utils": "Sentry.util" + } + }, + "whitelistedNonPeerDependencies": ["@sentry/browser", "@sentry/utils", "@sentry/types", "tslib"], + "assets": ["README.md", "LICENSE"] +} diff --git a/packages/angular/package.json b/packages/angular/package.json index 971bd6a1bfb4..2b1c8f69fd55 100644 --- a/packages/angular/package.json +++ b/packages/angular/package.json @@ -9,45 +9,43 @@ "engines": { "node": ">=8" }, - "main": "cjs/index.js", - "module": "esm/index.js", - "types": "build/types/index.d.ts", + "main": "build/bundles/sentry-angular.umd.js", + "module": "build/fesm2015/sentry-angular.js", "publishConfig": { "access": "public" }, "peerDependencies": { "@angular/common": "10.x || 11.x || 12.x || 13.x", "@angular/core": "10.x || 11.x || 12.x || 13.x", - "@angular/router": "10.x || 11.x || 12.x || 13.x" + "@angular/router": "10.x || 11.x || 12.x || 13.x", + "rxjs": "^6.5.5 || ^7.x" }, "dependencies": { "@sentry/browser": "7.0.0-alpha.1", "@sentry/types": "7.0.0-alpha.1", "@sentry/utils": "7.0.0-alpha.1", - "rxjs": "^6.6.0", - "tslib": "^1.9.3" + "tslib": "^2.0.0" }, "devDependencies": { - "@angular/common": "^10.0.3", - "@angular/core": "^10.0.3", - "@angular/router": "^10.0.3" + "@angular-devkit/build-angular": "~0.1002.4", + "@angular/cli": "^10.2.4", + "@angular/common": "~10.2.5", + "@angular/compiler": "^10.2.5", + "@angular/compiler-cli": "~10.2.5", + "@angular/core": "~10.2.5", + "@angular/router": "~10.2.5", + "ng-packagr": "^10.1.0", + "typescript": "~4.0.2" }, "scripts": { - "build": "run-p build:cjs build:esm build:types", - "build:cjs": "tsc -p tsconfig.cjs.json", + "build": "yarn build:ngc", + "build:ngc": "ng build --prod", "build:dev": "run-s build", - "build:es5": "yarn build:cjs # *** backwards compatibility - remove in v7 ***", - "build:esm": "tsc -p tsconfig.esm.json", - "build:types": "tsc -p tsconfig.types.json", - "build:watch": "run-p build:cjs:watch build:esm:watch build:types:watch", - "build:cjs:watch": "tsc -p tsconfig.cjs.json --watch", - "build:dev:watch": "run-s build:watch", - "build:es5:watch": "yarn build:cjs:watch # *** backwards compatibility - remove in v7 ***", - "build:esm:watch": "tsc -p tsconfig.esm.json --watch", - "build:types:watch": "tsc -p tsconfig.types.json --watch", - "build:npm": "npm pack", + "build:watch": "run-p build:ngc:watch", + "build:ngc:watch": "ng build --prod --watch", + "build:npm": "npm pack ./build", "circularDepCheck": "madge --circular src/index.ts", - "clean": "rimraf cjs esm build coverage", + "clean": "rimraf build coverage", "fix": "run-s fix:eslint fix:prettier", "fix:eslint": "eslint . --format stylish --fix", "fix:prettier": "prettier --write \"{src,test,scripts}/**/*.ts\"", diff --git a/packages/angular/src/errorhandler.ts b/packages/angular/src/errorhandler.ts index 4e9c0f7ed037..54449071ee89 100644 --- a/packages/angular/src/errorhandler.ts +++ b/packages/angular/src/errorhandler.ts @@ -1,5 +1,5 @@ import { HttpErrorResponse } from '@angular/common/http'; -import { ErrorHandler as AngularErrorHandler, Injectable } from '@angular/core'; +import { ErrorHandler as AngularErrorHandler, Inject, Injectable } from '@angular/core'; import * as Sentry from '@sentry/browser'; import { runOutsideAngular } from './zone'; @@ -26,7 +26,7 @@ export interface ErrorHandlerOptions { class SentryErrorHandler implements AngularErrorHandler { protected readonly _options: ErrorHandlerOptions; - public constructor(options?: ErrorHandlerOptions) { + public constructor(@Inject('errorHandlerOptions') options?: ErrorHandlerOptions) { this._options = { logErrors: true, ...options, diff --git a/packages/angular/tsconfig.cjs.json b/packages/angular/tsconfig.cjs.json deleted file mode 100644 index 4ec31d2ff68b..000000000000 --- a/packages/angular/tsconfig.cjs.json +++ /dev/null @@ -1,8 +0,0 @@ -{ - "extends": "./tsconfig.json", - - "compilerOptions": { - "module": "commonjs", - "outDir": "cjs" - } -} diff --git a/packages/angular/tsconfig.esm.json b/packages/angular/tsconfig.esm.json deleted file mode 100644 index b6ee3fa615c0..000000000000 --- a/packages/angular/tsconfig.esm.json +++ /dev/null @@ -1,8 +0,0 @@ -{ - "extends": "./tsconfig.json", - - "compilerOptions": { - "module": "es6", - "outDir": "esm" - } -} diff --git a/packages/angular/tsconfig.ngc.json b/packages/angular/tsconfig.ngc.json new file mode 100644 index 000000000000..9dd04ce14239 --- /dev/null +++ b/packages/angular/tsconfig.ngc.json @@ -0,0 +1,28 @@ +/* To learn more about this file see: https://angular.io/config/tsconfig. */ +// This tsconfig is used when building @sentry/angular with the Angular +// compiler and `ng-packagr`. It configures a production build conforming +// to the Angular Package Format (APF). +{ + "extends": "./tsconfig.json", + "compilerOptions": { + "target": "es2015", + "lib": ["dom", "es2015"], + "baseUrl": "./" + }, + "angularCompilerOptions": { + "skipTemplateCodegen": true, + "strictMetadataEmit": true, + "enableResourceInlining": true, + "strictInjectionParameters": true, + "strictInputAccessModifiers": true, + "strictTemplates": true, + // As per Angular 10, the recommendation from the library creation guide + // is to disable compilation for the Ivy rendering engine in production builds + // to ensure compatibility with Angular 10. + // For Angular 11-13 applications, ngcc and the Angular linker convert the compiled JS + // at application compile time into an Ivy-compatible version which is then further used in + // the build process. This ensures compatibility with newer Angular versions than the one + // that was used to initially compile the library (Angular 10 in our case). + "enableIvy": false + } +} diff --git a/packages/angular/tsconfig.types.json b/packages/angular/tsconfig.types.json deleted file mode 100644 index 65455f66bd75..000000000000 --- a/packages/angular/tsconfig.types.json +++ /dev/null @@ -1,10 +0,0 @@ -{ - "extends": "./tsconfig.json", - - "compilerOptions": { - "declaration": true, - "declarationMap": true, - "emitDeclarationOnly": true, - "outDir": "build/types" - } -} diff --git a/yarn.lock b/yarn.lock index 69010b40b996..ca4e027ca772 100644 --- a/yarn.lock +++ b/yarn.lock @@ -9,24 +9,195 @@ dependencies: "@jridgewell/trace-mapping" "^0.3.0" -"@angular/common@^10.0.3": +"@angular-devkit/architect@0.1002.4": + version "0.1002.4" + resolved "https://registry.yarnpkg.com/@angular-devkit/architect/-/architect-0.1002.4.tgz#2e1fa9c7a4718a4d0d101516ab0cc9cb653c5c57" + integrity sha512-Vrb2XSnvqj4RByqSWPeG/o9BSNX2DL3pxwQgLMrxofG8/+1VHQ2MsN/KTxBnEZtqeW4/l2QWTsQyzY5frJI69A== + dependencies: + "@angular-devkit/core" "10.2.4" + rxjs "6.6.2" + +"@angular-devkit/build-angular@~0.1002.4": + version "0.1002.4" + resolved "https://registry.yarnpkg.com/@angular-devkit/build-angular/-/build-angular-0.1002.4.tgz#6525c8ac8ec88d79aa34fdb4f224a914f9ea520f" + integrity sha512-0jo8fCbOyo1HGRDKBVzIzmGd3/Z+x5YP/9t1QHQrPTq9gRoVI+1vFgrKh7XApmGPa/S4bN6hows1wnGzTq5xJg== + dependencies: + "@angular-devkit/architect" "0.1002.4" + "@angular-devkit/build-optimizer" "0.1002.4" + "@angular-devkit/build-webpack" "0.1002.4" + "@angular-devkit/core" "10.2.4" + "@babel/core" "7.11.1" + "@babel/generator" "7.11.0" + "@babel/plugin-transform-runtime" "7.11.0" + "@babel/preset-env" "7.11.0" + "@babel/runtime" "7.11.2" + "@babel/template" "7.10.4" + "@jsdevtools/coverage-istanbul-loader" "3.0.5" + "@ngtools/webpack" "10.2.4" + autoprefixer "9.8.6" + babel-loader "8.1.0" + browserslist "^4.9.1" + cacache "15.0.5" + caniuse-lite "^1.0.30001032" + circular-dependency-plugin "5.2.0" + copy-webpack-plugin "6.0.3" + core-js "3.6.4" + css-loader "4.2.2" + cssnano "4.1.10" + file-loader "6.0.0" + find-cache-dir "3.3.1" + glob "7.1.6" + jest-worker "26.3.0" + karma-source-map-support "1.4.0" + less-loader "6.2.0" + license-webpack-plugin "2.3.0" + loader-utils "2.0.0" + mini-css-extract-plugin "0.10.0" + minimatch "3.0.4" + open "7.2.0" + parse5 "6.0.1" + parse5-htmlparser2-tree-adapter "6.0.1" + pnp-webpack-plugin "1.6.4" + postcss "7.0.32" + postcss-import "12.0.1" + postcss-loader "3.0.0" + raw-loader "4.0.1" + regenerator-runtime "0.13.7" + resolve-url-loader "3.1.2" + rimraf "3.0.2" + rollup "2.26.5" + rxjs "6.6.2" + sass "1.26.10" + sass-loader "10.0.1" + semver "7.3.2" + source-map "0.7.3" + source-map-loader "1.0.2" + source-map-support "0.5.19" + speed-measure-webpack-plugin "1.3.3" + style-loader "1.2.1" + stylus "0.54.8" + stylus-loader "3.0.2" + terser "5.3.0" + terser-webpack-plugin "4.1.0" + tree-kill "1.2.2" + webpack "4.44.1" + webpack-dev-middleware "3.7.2" + webpack-dev-server "3.11.0" + webpack-merge "4.2.2" + webpack-sources "1.4.3" + webpack-subresource-integrity "1.4.1" + worker-plugin "5.0.0" + +"@angular-devkit/build-optimizer@0.1002.4": + version "0.1002.4" + resolved "https://registry.yarnpkg.com/@angular-devkit/build-optimizer/-/build-optimizer-0.1002.4.tgz#ddaa74e7e79cdc915631ec212780d6b7e2650c4a" + integrity sha512-O705v4N+VCaeTnePYVHf+XZaPxU8eTWCx2mYvCmG0urHh1GCehb+vX1v332tTaC2uzMoH+RSg2Nh2apFX+pE0Q== + dependencies: + loader-utils "2.0.0" + source-map "0.7.3" + tslib "2.0.1" + typescript "4.0.2" + webpack-sources "1.4.3" + +"@angular-devkit/build-webpack@0.1002.4": + version "0.1002.4" + resolved "https://registry.yarnpkg.com/@angular-devkit/build-webpack/-/build-webpack-0.1002.4.tgz#ed3a88a5c0af8a96ac0a14fa59ce66d4b7b850ac" + integrity sha512-5K+hPWmWV1q0HKcvJrTjJ5ABKEQintJlMMaewfmDUDOfslpabtXtY3LF+18a2RBdktAtLpIxoVTX1j/dvotu+w== + dependencies: + "@angular-devkit/architect" "0.1002.4" + "@angular-devkit/core" "10.2.4" + rxjs "6.6.2" + +"@angular-devkit/core@10.2.4": version "10.2.4" - resolved "https://registry.yarnpkg.com/@angular/common/-/common-10.2.4.tgz#fb1772ea5780c96e00411900c54457f0cbcf401b" - integrity sha512-bBfsLJNDQaC2OI1mReDJuSZ/uBb7Pf3HVpRmlQKNIPllIxqX1hLH8I3Plodrns9m32JMJ6FMsQthcP0KMdRCJA== + resolved "https://registry.yarnpkg.com/@angular-devkit/core/-/core-10.2.4.tgz#b1f6b580406e4a497eeba54cf34013b88e36cb47" + integrity sha512-gnm/+Iyaa6Jt3E803bpTjkwDAIb0AhP9badaGwbx44+bhbNSE2WzOBmdsQrsxJXHAMEG9CGeBzeRd8XZtLACWg== dependencies: - tslib "^2.0.0" + ajv "6.12.4" + fast-json-stable-stringify "2.1.0" + magic-string "0.25.7" + rxjs "6.6.2" + source-map "0.7.3" -"@angular/core@^10.0.3": +"@angular-devkit/schematics@10.2.4": version "10.2.4" - resolved "https://registry.yarnpkg.com/@angular/core/-/core-10.2.4.tgz#1124061f8232d79fcff7508c9243ec5ec3fc00f3" - integrity sha512-5xpAvmZwD9nZ8eWx10urjibqEeePGEiFXVMEn3IaJWgfdOcMmeSoioW9JUllT3w85+DlNVWbRbhz0YfE9a4jyw== + resolved "https://registry.yarnpkg.com/@angular-devkit/schematics/-/schematics-10.2.4.tgz#6f8bc7c0a5c4ac101460a0f709df33782782e6ad" + integrity sha512-poBGWRwMgnnnmoZfwyOBcQMJm7U5y5XxnxvMsBJEyAQRxfQa+KLvcCfGWXqskNTyBdQFpy4kxmtCzRClkoEiKQ== dependencies: - tslib "^2.0.0" + "@angular-devkit/core" "10.2.4" + ora "5.0.0" + rxjs "6.6.2" -"@angular/router@^10.0.3": +"@angular/cli@^10.2.4": version "10.2.4" - resolved "https://registry.yarnpkg.com/@angular/router/-/router-10.2.4.tgz#0c6a680d8cbf8f5ce8b904636c8ee0e75765124d" - integrity sha512-y3xMwZHWS84fbm3FoU8vTAeXaTuPd4ZfmZ3dhkG9c1tkVq/jCmc6pkqNxjv3L1iPenKrvt2bFhh+wCs+bcUPhw== + resolved "https://registry.yarnpkg.com/@angular/cli/-/cli-10.2.4.tgz#f8899eee8f774cd805b1831a8f2f865024e9f4e1" + integrity sha512-S8xAJemX3zE/I/xi81DT6NuzfDwEAEtEeITHxrAH0AHE4kaUBy2O9bAopvYqMNzxs/XGqyxMv8vwYYpGax7EEQ== + dependencies: + "@angular-devkit/architect" "0.1002.4" + "@angular-devkit/core" "10.2.4" + "@angular-devkit/schematics" "10.2.4" + "@schematics/angular" "10.2.4" + "@schematics/update" "0.1002.4" + "@yarnpkg/lockfile" "1.1.0" + ansi-colors "4.1.1" + debug "4.1.1" + ini "1.3.6" + inquirer "7.3.3" + npm-package-arg "8.0.1" + npm-pick-manifest "6.1.0" + open "7.2.0" + pacote "9.5.12" + read-package-tree "5.3.1" + rimraf "3.0.2" + semver "7.3.2" + symbol-observable "1.2.0" + universal-analytics "0.4.23" + uuid "8.3.0" + +"@angular/common@~10.2.5": + version "10.2.5" + resolved "https://registry.yarnpkg.com/@angular/common/-/common-10.2.5.tgz#5313f530446998e2f7af2dc43611addcfa6fd1c1" + integrity sha512-553yf6ZUHNqT4XpOqbW7EKKMfX56u/8DkwYXuSv8MAKdl4/AW6gliFOEJGYo04JcKF7Knq3VPvGSCO9kupf0hg== + dependencies: + tslib "^2.0.0" + +"@angular/compiler-cli@~10.2.5": + version "10.2.5" + resolved "https://registry.yarnpkg.com/@angular/compiler-cli/-/compiler-cli-10.2.5.tgz#adb65bb9ecea14762a501226fde7760b73c3ab1e" + integrity sha512-xddSpKudoPidEebIW3x1CvQdx69WEmnFg4DneeQi/tit7mtAKYTJemzYZmP6abdSYhtxovL0bPX5LxYlrtuxIw== + dependencies: + canonical-path "1.0.0" + chokidar "^3.0.0" + convert-source-map "^1.5.1" + dependency-graph "^0.7.2" + fs-extra "4.0.2" + magic-string "^0.25.0" + minimist "^1.2.0" + reflect-metadata "^0.1.2" + semver "^6.3.0" + source-map "^0.6.1" + sourcemap-codec "^1.4.8" + tslib "^2.0.0" + yargs "^16.1.1" + +"@angular/compiler@^10.2.5": + version "10.2.5" + resolved "https://registry.yarnpkg.com/@angular/compiler/-/compiler-10.2.5.tgz#1ff8514fdd2c07ff3c265b960dc49af6376071c9" + integrity sha512-ddJiTPCoVBIGjFDYoYWDpmq3Zs8UKoWpzaeW4u+p17gWW54HwyT5XTxrgtbeUmaxIuRdL4/KT1lGHs9/9bwbCA== + dependencies: + tslib "^2.0.0" + +"@angular/core@~10.2.5": + version "10.2.5" + resolved "https://registry.yarnpkg.com/@angular/core/-/core-10.2.5.tgz#2050b0dbb180aa98c2ec46bba6d4827565ba2a2d" + integrity sha512-krhOKNTj5XE92Rk9ASX5KmgTF72j7qT2PLVxrGEVjuUKjBY2XaK3TV0Kotq9zI3qa9WgeCrP/Njn6jlKQCCAEQ== + dependencies: + tslib "^2.0.0" + +"@angular/router@~10.2.5": + version "10.2.5" + resolved "https://registry.yarnpkg.com/@angular/router/-/router-10.2.5.tgz#acc75a29ab0b54c8ebad7d2a896986a59d7d99ec" + integrity sha512-AtSMB/d4V+pw/FL4G/mWWoiJJtZ/075TqsGW7uEFKgxS6Gh2kalv6BTMlXVG5GO+2oU0lsuDvguq5E7Atbak3Q== dependencies: tslib "^2.0.0" @@ -58,6 +229,11 @@ dependencies: "@babel/highlight" "^7.16.7" +"@babel/compat-data@^7.11.0", "@babel/compat-data@^7.17.0", "@babel/compat-data@^7.17.7": + version "7.17.7" + resolved "https://registry.yarnpkg.com/@babel/compat-data/-/compat-data-7.17.7.tgz#078d8b833fbbcc95286613be8c716cef2b519fa2" + integrity sha512-p8pdE6j0a29TNGebNm7NzYZWB3xVZJBZ7XGs42uAKzQo8VQ3F0By/cQCtUEABwIqw5zo6WA4NbmxsfzADzMKnQ== + "@babel/compat-data@^7.13.0", "@babel/compat-data@^7.13.12", "@babel/compat-data@^7.13.8", "@babel/compat-data@^7.15.0": version "7.15.0" resolved "https://registry.yarnpkg.com/@babel/compat-data/-/compat-data-7.15.0.tgz#2dbaf8b85334796cafbb0f5793a90a2fc010b176" @@ -68,10 +244,27 @@ resolved "https://registry.yarnpkg.com/@babel/compat-data/-/compat-data-7.16.0.tgz#ea269d7f78deb3a7826c39a4048eecda541ebdaa" integrity sha512-DGjt2QZse5SGd9nfOSqO4WLJ8NN/oHkijbXbPrxuoJO3oIPJL3TciZs9FX+cOHNiY9E9l0opL8g7BmLe3T+9ew== -"@babel/compat-data@^7.17.7": - version "7.17.7" - resolved "https://registry.yarnpkg.com/@babel/compat-data/-/compat-data-7.17.7.tgz#078d8b833fbbcc95286613be8c716cef2b519fa2" - integrity sha512-p8pdE6j0a29TNGebNm7NzYZWB3xVZJBZ7XGs42uAKzQo8VQ3F0By/cQCtUEABwIqw5zo6WA4NbmxsfzADzMKnQ== +"@babel/core@7.11.1": + version "7.11.1" + resolved "https://registry.yarnpkg.com/@babel/core/-/core-7.11.1.tgz#2c55b604e73a40dc21b0e52650b11c65cf276643" + integrity sha512-XqF7F6FWQdKGGWAzGELL+aCO1p+lRY5Tj5/tbT3St1G8NaH70jhhDIKknIZaDans0OQBG5wRAldROLHSt44BgQ== + dependencies: + "@babel/code-frame" "^7.10.4" + "@babel/generator" "^7.11.0" + "@babel/helper-module-transforms" "^7.11.0" + "@babel/helpers" "^7.10.4" + "@babel/parser" "^7.11.1" + "@babel/template" "^7.10.4" + "@babel/traverse" "^7.11.0" + "@babel/types" "^7.11.0" + convert-source-map "^1.7.0" + debug "^4.1.0" + gensync "^1.0.0-beta.1" + json5 "^2.1.2" + lodash "^4.17.19" + resolve "^1.3.2" + semver "^5.4.1" + source-map "^0.5.0" "@babel/core@^7.1.0", "@babel/core@^7.1.6", "@babel/core@^7.12.0", "@babel/core@^7.3.4": version "7.13.14" @@ -94,7 +287,7 @@ semver "^6.3.0" source-map "^0.5.0" -"@babel/core@^7.12.3", "@babel/core@^7.7.2", "@babel/core@^7.8.0": +"@babel/core@^7.12.3", "@babel/core@^7.7.2", "@babel/core@^7.7.5", "@babel/core@^7.8.0": version "7.17.9" resolved "https://registry.yarnpkg.com/@babel/core/-/core-7.17.9.tgz#6bae81a06d95f4d0dec5bb9d74bbc1f58babdcfe" integrity sha512-5ug+SfZCpDAkVp9SFIZAzlW18rlzsOcJGaetCjkySnrXXDUw9AR8cDUm1iByTmdWM6yxX6/zycaV76w3YTF2gw== @@ -157,6 +350,24 @@ semver "^6.3.0" source-map "^0.5.0" +"@babel/generator@7.11.0": + version "7.11.0" + resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.11.0.tgz#4b90c78d8c12825024568cbe83ee6c9af193585c" + integrity sha512-fEm3Uzw7Mc9Xi//qU20cBKatTfs2aOtKqmvy/Vm7RkJEGFQ4xc9myCfbXxqK//ZS8MR/ciOHw6meGASJuKmDfQ== + dependencies: + "@babel/types" "^7.11.0" + jsesc "^2.5.1" + source-map "^0.5.0" + +"@babel/generator@^7.11.0", "@babel/generator@^7.17.9", "@babel/generator@^7.7.2": + version "7.17.9" + resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.17.9.tgz#f4af9fd38fa8de143c29fce3f71852406fc1e2fc" + integrity sha512-rAdDousTwxbIxbz5I7GEQ3lUip+xVCXooZNbsydCWs3xA7ZsYOv+CFRdzGxRX78BmQHu9B1Eso59AOZQOJDEdQ== + dependencies: + "@babel/types" "^7.17.0" + jsesc "^2.5.1" + source-map "^0.5.0" + "@babel/generator@^7.13.9", "@babel/generator@^7.16.0": version "7.16.0" resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.16.0.tgz#d40f3d1d5075e62d3500bccb67f3daa8a95265b2" @@ -184,15 +395,6 @@ jsesc "^2.5.1" source-map "^0.5.0" -"@babel/generator@^7.17.9", "@babel/generator@^7.7.2": - version "7.17.9" - resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.17.9.tgz#f4af9fd38fa8de143c29fce3f71852406fc1e2fc" - integrity sha512-rAdDousTwxbIxbz5I7GEQ3lUip+xVCXooZNbsydCWs3xA7ZsYOv+CFRdzGxRX78BmQHu9B1Eso59AOZQOJDEdQ== - dependencies: - "@babel/types" "^7.17.0" - jsesc "^2.5.1" - source-map "^0.5.0" - "@babel/helper-annotate-as-pure@^7.12.13": version "7.12.13" resolved "https://registry.yarnpkg.com/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.12.13.tgz#0f58e86dfc4bb3b1fcd7db806570e177d439b6ab" @@ -222,6 +424,24 @@ "@babel/helper-explode-assignable-expression" "^7.12.13" "@babel/types" "^7.12.13" +"@babel/helper-builder-binary-assignment-operator-visitor@^7.16.7": + version "7.16.7" + resolved "https://registry.yarnpkg.com/@babel/helper-builder-binary-assignment-operator-visitor/-/helper-builder-binary-assignment-operator-visitor-7.16.7.tgz#38d138561ea207f0f69eb1626a418e4f7e6a580b" + integrity sha512-C6FdbRaxYjwVu/geKW4ZeQ0Q31AftgRcdSnZ5/jsH6BzCJbtvXvhpfkbkThYSuutZA7nCXpPR6AD9zd1dprMkA== + dependencies: + "@babel/helper-explode-assignable-expression" "^7.16.7" + "@babel/types" "^7.16.7" + +"@babel/helper-compilation-targets@^7.10.4", "@babel/helper-compilation-targets@^7.16.7", "@babel/helper-compilation-targets@^7.17.7": + version "7.17.7" + resolved "https://registry.yarnpkg.com/@babel/helper-compilation-targets/-/helper-compilation-targets-7.17.7.tgz#a3c2924f5e5f0379b356d4cfb313d1414dc30e46" + integrity sha512-UFzlz2jjd8kroj0hmCFV5zr+tQPi1dpC2cRsDV/3IEW8bJfCPrPpmcSN6ZS8RqIq4LXcmpipCQFPddyFA5Yc7w== + dependencies: + "@babel/compat-data" "^7.17.7" + "@babel/helper-validator-option" "^7.16.7" + browserslist "^4.17.5" + semver "^6.3.0" + "@babel/helper-compilation-targets@^7.12.0", "@babel/helper-compilation-targets@^7.13.0", "@babel/helper-compilation-targets@^7.13.10", "@babel/helper-compilation-targets@^7.13.8", "@babel/helper-compilation-targets@^7.15.4": version "7.15.4" resolved "https://registry.yarnpkg.com/@babel/helper-compilation-targets/-/helper-compilation-targets-7.15.4.tgz#cf6d94f30fbefc139123e27dd6b02f65aeedb7b9" @@ -242,16 +462,6 @@ browserslist "^4.17.5" semver "^6.3.0" -"@babel/helper-compilation-targets@^7.17.7": - version "7.17.7" - resolved "https://registry.yarnpkg.com/@babel/helper-compilation-targets/-/helper-compilation-targets-7.17.7.tgz#a3c2924f5e5f0379b356d4cfb313d1414dc30e46" - integrity sha512-UFzlz2jjd8kroj0hmCFV5zr+tQPi1dpC2cRsDV/3IEW8bJfCPrPpmcSN6ZS8RqIq4LXcmpipCQFPddyFA5Yc7w== - dependencies: - "@babel/compat-data" "^7.17.7" - "@babel/helper-validator-option" "^7.16.7" - browserslist "^4.17.5" - semver "^6.3.0" - "@babel/helper-create-class-features-plugin@^7.13.0", "@babel/helper-create-class-features-plugin@^7.5.5": version "7.13.11" resolved "https://registry.yarnpkg.com/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.13.11.tgz#30d30a005bca2c953f5653fc25091a492177f4f6" @@ -275,6 +485,19 @@ "@babel/helper-replace-supers" "^7.16.0" "@babel/helper-split-export-declaration" "^7.16.0" +"@babel/helper-create-class-features-plugin@^7.16.10": + version "7.17.9" + resolved "https://registry.yarnpkg.com/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.17.9.tgz#71835d7fb9f38bd9f1378e40a4c0902fdc2ea49d" + integrity sha512-kUjip3gruz6AJKOq5i3nC6CoCEEF/oHH3cp6tOZhB+IyyyPyW0g1Gfsxn3mkk6S08pIA2y8GQh609v9G/5sHVQ== + dependencies: + "@babel/helper-annotate-as-pure" "^7.16.7" + "@babel/helper-environment-visitor" "^7.16.7" + "@babel/helper-function-name" "^7.17.9" + "@babel/helper-member-expression-to-functions" "^7.17.7" + "@babel/helper-optimise-call-expression" "^7.16.7" + "@babel/helper-replace-supers" "^7.16.7" + "@babel/helper-split-export-declaration" "^7.16.7" + "@babel/helper-create-class-features-plugin@^7.16.7": version "7.16.7" resolved "https://registry.yarnpkg.com/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.16.7.tgz#9c5b34b53a01f2097daf10678d65135c1b9f84ba" @@ -296,6 +519,14 @@ "@babel/helper-annotate-as-pure" "^7.12.13" regexpu-core "^4.7.1" +"@babel/helper-create-regexp-features-plugin@^7.16.7": + version "7.17.0" + resolved "https://registry.yarnpkg.com/@babel/helper-create-regexp-features-plugin/-/helper-create-regexp-features-plugin-7.17.0.tgz#1dcc7d40ba0c6b6b25618997c5dbfd310f186fe1" + integrity sha512-awO2So99wG6KnlE+TPs6rn83gCz5WlEePJDTnLEqbchMVrBeAujURVphRdigsk094VhvZehFoNOihSlcBjwsXA== + dependencies: + "@babel/helper-annotate-as-pure" "^7.16.7" + regexpu-core "^5.0.1" + "@babel/helper-define-polyfill-provider@^0.1.5": version "0.1.5" resolved "https://registry.yarnpkg.com/@babel/helper-define-polyfill-provider/-/helper-define-polyfill-provider-0.1.5.tgz#3c2f91b7971b9fc11fe779c945c014065dea340e" @@ -324,6 +555,13 @@ dependencies: "@babel/types" "^7.13.0" +"@babel/helper-explode-assignable-expression@^7.16.7": + version "7.16.7" + resolved "https://registry.yarnpkg.com/@babel/helper-explode-assignable-expression/-/helper-explode-assignable-expression-7.16.7.tgz#12a6d8522fdd834f194e868af6354e8650242b7a" + integrity sha512-KyUenhWMC8VrxzkGP0Jizjo4/Zx+1nNZhgocs+gLzyZyB8SHidhoq9KK/8Ato4anhwsivfkBLftky7gvzbZMtQ== + dependencies: + "@babel/types" "^7.16.7" + "@babel/helper-function-name@^7.12.13", "@babel/helper-function-name@^7.15.4": version "7.15.4" resolved "https://registry.yarnpkg.com/@babel/helper-function-name/-/helper-function-name-7.15.4.tgz#845744dafc4381a4a5fb6afa6c3d36f98a787ebc" @@ -422,6 +660,20 @@ dependencies: "@babel/types" "^7.16.7" +"@babel/helper-member-expression-to-functions@^7.17.7": + version "7.17.7" + resolved "https://registry.yarnpkg.com/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.17.7.tgz#a34013b57d8542a8c4ff8ba3f747c02452a4d8c4" + integrity sha512-thxXgnQ8qQ11W2wVUObIqDL4p148VMxkt5T/qpN5k2fboRyzFGFmKsTGViquyM5QHKUy48OZoca8kw4ajaDPyw== + dependencies: + "@babel/types" "^7.17.0" + +"@babel/helper-module-imports@^7.10.4", "@babel/helper-module-imports@^7.16.7": + version "7.16.7" + resolved "https://registry.yarnpkg.com/@babel/helper-module-imports/-/helper-module-imports-7.16.7.tgz#25612a8091a999704461c8a222d0efec5d091437" + integrity sha512-LVtS6TqjJHFc+nYeITRo6VLXve70xmq7wPhWTqDJusJEgGmkAACWwMiTNrvfoQo6hEhFwAIixNkvB0jPXDL8Wg== + dependencies: + "@babel/types" "^7.16.7" + "@babel/helper-module-imports@^7.12.13", "@babel/helper-module-imports@^7.15.4", "@babel/helper-module-imports@^7.8.3": version "7.15.4" resolved "https://registry.yarnpkg.com/@babel/helper-module-imports/-/helper-module-imports-7.15.4.tgz#e18007d230632dea19b47853b984476e7b4e103f" @@ -436,12 +688,19 @@ dependencies: "@babel/types" "^7.16.0" -"@babel/helper-module-imports@^7.16.7": - version "7.16.7" - resolved "https://registry.yarnpkg.com/@babel/helper-module-imports/-/helper-module-imports-7.16.7.tgz#25612a8091a999704461c8a222d0efec5d091437" - integrity sha512-LVtS6TqjJHFc+nYeITRo6VLXve70xmq7wPhWTqDJusJEgGmkAACWwMiTNrvfoQo6hEhFwAIixNkvB0jPXDL8Wg== +"@babel/helper-module-transforms@^7.11.0", "@babel/helper-module-transforms@^7.16.7", "@babel/helper-module-transforms@^7.17.7": + version "7.17.7" + resolved "https://registry.yarnpkg.com/@babel/helper-module-transforms/-/helper-module-transforms-7.17.7.tgz#3943c7f777139e7954a5355c815263741a9c1cbd" + integrity sha512-VmZD99F3gNTYB7fJRDTi+u6l/zxY0BE6OIxPSU7a50s6ZUQkHwSDmV92FfM+oCG0pZRVojGYhkR8I0OGeCVREw== dependencies: - "@babel/types" "^7.16.7" + "@babel/helper-environment-visitor" "^7.16.7" + "@babel/helper-module-imports" "^7.16.7" + "@babel/helper-simple-access" "^7.17.7" + "@babel/helper-split-export-declaration" "^7.16.7" + "@babel/helper-validator-identifier" "^7.16.7" + "@babel/template" "^7.16.7" + "@babel/traverse" "^7.17.3" + "@babel/types" "^7.17.0" "@babel/helper-module-transforms@^7.13.0", "@babel/helper-module-transforms@^7.15.4": version "7.15.4" @@ -471,20 +730,6 @@ "@babel/traverse" "^7.16.0" "@babel/types" "^7.16.0" -"@babel/helper-module-transforms@^7.17.7": - version "7.17.7" - resolved "https://registry.yarnpkg.com/@babel/helper-module-transforms/-/helper-module-transforms-7.17.7.tgz#3943c7f777139e7954a5355c815263741a9c1cbd" - integrity sha512-VmZD99F3gNTYB7fJRDTi+u6l/zxY0BE6OIxPSU7a50s6ZUQkHwSDmV92FfM+oCG0pZRVojGYhkR8I0OGeCVREw== - dependencies: - "@babel/helper-environment-visitor" "^7.16.7" - "@babel/helper-module-imports" "^7.16.7" - "@babel/helper-simple-access" "^7.17.7" - "@babel/helper-split-export-declaration" "^7.16.7" - "@babel/helper-validator-identifier" "^7.16.7" - "@babel/template" "^7.16.7" - "@babel/traverse" "^7.17.3" - "@babel/types" "^7.17.0" - "@babel/helper-optimise-call-expression@^7.12.13", "@babel/helper-optimise-call-expression@^7.15.4": version "7.15.4" resolved "https://registry.yarnpkg.com/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.15.4.tgz#f310a5121a3b9cc52d9ab19122bd729822dee171" @@ -525,6 +770,15 @@ "@babel/helper-wrap-function" "^7.13.0" "@babel/types" "^7.13.0" +"@babel/helper-remap-async-to-generator@^7.16.8": + version "7.16.8" + resolved "https://registry.yarnpkg.com/@babel/helper-remap-async-to-generator/-/helper-remap-async-to-generator-7.16.8.tgz#29ffaade68a367e2ed09c90901986918d25e57e3" + integrity sha512-fm0gH7Flb8H51LqJHy3HJ3wnE1+qtYR2A99K06ahwrawLdOFsCEWjZOrYricXJHoPSudNKxrMBUPEIPxiIIvBw== + dependencies: + "@babel/helper-annotate-as-pure" "^7.16.7" + "@babel/helper-wrap-function" "^7.16.8" + "@babel/types" "^7.16.8" + "@babel/helper-replace-supers@^7.12.13", "@babel/helper-replace-supers@^7.13.0", "@babel/helper-replace-supers@^7.15.4": version "7.15.4" resolved "https://registry.yarnpkg.com/@babel/helper-replace-supers/-/helper-replace-supers-7.15.4.tgz#52a8ab26ba918c7f6dee28628b07071ac7b7347a" @@ -647,6 +901,25 @@ "@babel/traverse" "^7.13.0" "@babel/types" "^7.13.0" +"@babel/helper-wrap-function@^7.16.8": + version "7.16.8" + resolved "https://registry.yarnpkg.com/@babel/helper-wrap-function/-/helper-wrap-function-7.16.8.tgz#58afda087c4cd235de92f7ceedebca2c41274200" + integrity sha512-8RpyRVIAW1RcDDGTA+GpPAwV22wXCfKOoM9bet6TLkGIFTkRQSkH1nMQ5Yet4MpoXe1ZwHPVtNasc2w0uZMqnw== + dependencies: + "@babel/helper-function-name" "^7.16.7" + "@babel/template" "^7.16.7" + "@babel/traverse" "^7.16.8" + "@babel/types" "^7.16.8" + +"@babel/helpers@^7.10.4", "@babel/helpers@^7.17.9": + version "7.17.9" + resolved "https://registry.yarnpkg.com/@babel/helpers/-/helpers-7.17.9.tgz#b2af120821bfbe44f9907b1826e168e819375a1a" + integrity sha512-cPCt915ShDWUEzEp3+UNRktO2n6v49l5RSnG9M5pS24hA+2FAc5si+Pn1i4VVbQQ+jh+bIZhPFQOJOzbrOYY1Q== + dependencies: + "@babel/template" "^7.16.7" + "@babel/traverse" "^7.17.9" + "@babel/types" "^7.17.0" + "@babel/helpers@^7.13.10", "@babel/helpers@^7.16.0": version "7.16.3" resolved "https://registry.yarnpkg.com/@babel/helpers/-/helpers-7.16.3.tgz#27fc64f40b996e7074dc73128c3e5c3e7f55c43c" @@ -665,15 +938,6 @@ "@babel/traverse" "^7.15.4" "@babel/types" "^7.15.4" -"@babel/helpers@^7.17.9": - version "7.17.9" - resolved "https://registry.yarnpkg.com/@babel/helpers/-/helpers-7.17.9.tgz#b2af120821bfbe44f9907b1826e168e819375a1a" - integrity sha512-cPCt915ShDWUEzEp3+UNRktO2n6v49l5RSnG9M5pS24hA+2FAc5si+Pn1i4VVbQQ+jh+bIZhPFQOJOzbrOYY1Q== - dependencies: - "@babel/template" "^7.16.7" - "@babel/traverse" "^7.17.9" - "@babel/types" "^7.17.0" - "@babel/highlight@^7.10.4", "@babel/highlight@^7.14.5": version "7.14.5" resolved "https://registry.yarnpkg.com/@babel/highlight/-/highlight-7.14.5.tgz#6861a52f03966405001f6aa534a01a24d99e8cd9" @@ -706,7 +970,7 @@ resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.14.2.tgz#0c1680aa44ad4605b16cbdcc5c341a61bde9c746" integrity sha512-IoVDIHpsgE/fu7eXBeRWt8zLbDrSvD7H1gpomOkPpBoEN8KCruCqSDdqo8dddwQQrui30KSvQBaMUOJiuFu6QQ== -"@babel/parser@^7.14.7", "@babel/parser@^7.17.9": +"@babel/parser@^7.10.4", "@babel/parser@^7.11.1", "@babel/parser@^7.14.7", "@babel/parser@^7.17.9": version "7.17.9" resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.17.9.tgz#9c94189a6062f0291418ca021077983058e171ef" integrity sha512-vqUSBLP8dQHFPdPi9bc5GK9vRkYHJ49fsZdtoJ8EQ8ibpwk5rPKfvNIwChB0KVXcIjcepEBBd2VHC5r9Gy8ueg== @@ -735,6 +999,15 @@ "@babel/helper-skip-transparent-expression-wrappers" "^7.12.1" "@babel/plugin-proposal-optional-chaining" "^7.13.12" +"@babel/plugin-proposal-async-generator-functions@^7.10.4": + version "7.16.8" + resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-async-generator-functions/-/plugin-proposal-async-generator-functions-7.16.8.tgz#3bdd1ebbe620804ea9416706cd67d60787504bc8" + integrity sha512-71YHIvMuiuqWJQkebWJtdhQTfd4Q4mF76q2IX37uZPkG9+olBxsX+rH1vkhFto4UeJZ9dPY2s+mDvhDm1u2BGQ== + dependencies: + "@babel/helper-plugin-utils" "^7.16.7" + "@babel/helper-remap-async-to-generator" "^7.16.8" + "@babel/plugin-syntax-async-generators" "^7.8.4" + "@babel/plugin-proposal-async-generator-functions@^7.13.8": version "7.13.8" resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-async-generator-functions/-/plugin-proposal-async-generator-functions-7.13.8.tgz#87aacb574b3bc4b5603f6fe41458d72a5a2ec4b1" @@ -752,6 +1025,14 @@ "@babel/helper-create-class-features-plugin" "^7.13.0" "@babel/helper-plugin-utils" "^7.13.0" +"@babel/plugin-proposal-class-properties@^7.10.4": + version "7.16.7" + resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-class-properties/-/plugin-proposal-class-properties-7.16.7.tgz#925cad7b3b1a2fcea7e59ecc8eb5954f961f91b0" + integrity sha512-IobU0Xme31ewjYOShSIqd/ZGM/r/cuOz2z0MDbNrhF5FW+ZVgi0f2lyeoj9KFPDOAqsYxmLWZte1WOwlvY9aww== + dependencies: + "@babel/helper-create-class-features-plugin" "^7.16.7" + "@babel/helper-plugin-utils" "^7.16.7" + "@babel/plugin-proposal-class-properties@^7.14.5": version "7.16.0" resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-class-properties/-/plugin-proposal-class-properties-7.16.0.tgz#c029618267ddebc7280fa286e0f8ca2a278a2d1a" @@ -769,6 +1050,14 @@ "@babel/helper-plugin-utils" "^7.13.0" "@babel/plugin-syntax-decorators" "^7.12.13" +"@babel/plugin-proposal-dynamic-import@^7.10.4": + version "7.16.7" + resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-dynamic-import/-/plugin-proposal-dynamic-import-7.16.7.tgz#c19c897eaa46b27634a00fee9fb7d829158704b2" + integrity sha512-I8SW9Ho3/8DRSdmDdH3gORdyUuYnk1m4cMxUAdu5oy4n3OfN8flDEH+d60iG7dUfi0KkYwSvoalHzzdRzpWHTg== + dependencies: + "@babel/helper-plugin-utils" "^7.16.7" + "@babel/plugin-syntax-dynamic-import" "^7.8.3" + "@babel/plugin-proposal-dynamic-import@^7.13.8": version "7.13.8" resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-dynamic-import/-/plugin-proposal-dynamic-import-7.13.8.tgz#876a1f6966e1dec332e8c9451afda3bebcdf2e1d" @@ -785,6 +1074,14 @@ "@babel/helper-plugin-utils" "^7.14.5" "@babel/plugin-syntax-dynamic-import" "^7.8.3" +"@babel/plugin-proposal-export-namespace-from@^7.10.4": + version "7.16.7" + resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-export-namespace-from/-/plugin-proposal-export-namespace-from-7.16.7.tgz#09de09df18445a5786a305681423ae63507a6163" + integrity sha512-ZxdtqDXLRGBL64ocZcs7ovt71L3jhC1RGSyR996svrCi3PYqHNkb3SwPJCs8RIzD86s+WPpt2S73+EHCGO+NUA== + dependencies: + "@babel/helper-plugin-utils" "^7.16.7" + "@babel/plugin-syntax-export-namespace-from" "^7.8.3" + "@babel/plugin-proposal-export-namespace-from@^7.12.13": version "7.12.13" resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-export-namespace-from/-/plugin-proposal-export-namespace-from-7.12.13.tgz#393be47a4acd03fa2af6e3cde9b06e33de1b446d" @@ -801,6 +1098,14 @@ "@babel/helper-plugin-utils" "^7.14.5" "@babel/plugin-syntax-export-namespace-from" "^7.8.3" +"@babel/plugin-proposal-json-strings@^7.10.4": + version "7.16.7" + resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-json-strings/-/plugin-proposal-json-strings-7.16.7.tgz#9732cb1d17d9a2626a08c5be25186c195b6fa6e8" + integrity sha512-lNZ3EEggsGY78JavgbHsK9u5P3pQaW7k4axlgFLYkMd7UBsiNahCITShLjNQschPyjtO6dADrL24757IdhBrsQ== + dependencies: + "@babel/helper-plugin-utils" "^7.16.7" + "@babel/plugin-syntax-json-strings" "^7.8.3" + "@babel/plugin-proposal-json-strings@^7.13.8": version "7.13.8" resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-json-strings/-/plugin-proposal-json-strings-7.13.8.tgz#bf1fb362547075afda3634ed31571c5901afef7b" @@ -809,6 +1114,14 @@ "@babel/helper-plugin-utils" "^7.13.0" "@babel/plugin-syntax-json-strings" "^7.8.3" +"@babel/plugin-proposal-logical-assignment-operators@^7.11.0": + version "7.16.7" + resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-logical-assignment-operators/-/plugin-proposal-logical-assignment-operators-7.16.7.tgz#be23c0ba74deec1922e639832904be0bea73cdea" + integrity sha512-K3XzyZJGQCr00+EtYtrDjmwX7o7PLK6U9bi1nCwkQioRFVUv6dJoxbQjtWVtP+bCPy82bONBKG8NPyQ4+i6yjg== + dependencies: + "@babel/helper-plugin-utils" "^7.16.7" + "@babel/plugin-syntax-logical-assignment-operators" "^7.10.4" + "@babel/plugin-proposal-logical-assignment-operators@^7.13.8": version "7.13.8" resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-logical-assignment-operators/-/plugin-proposal-logical-assignment-operators-7.13.8.tgz#93fa78d63857c40ce3c8c3315220fd00bfbb4e1a" @@ -825,6 +1138,14 @@ "@babel/helper-plugin-utils" "^7.14.5" "@babel/plugin-syntax-logical-assignment-operators" "^7.10.4" +"@babel/plugin-proposal-nullish-coalescing-operator@^7.10.4": + version "7.16.7" + resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-nullish-coalescing-operator/-/plugin-proposal-nullish-coalescing-operator-7.16.7.tgz#141fc20b6857e59459d430c850a0011e36561d99" + integrity sha512-aUOrYU3EVtjf62jQrCj63pYZ7k6vns2h/DQvHPWGmsJRYzWXZ6/AsfgpiRy6XiuIDADhJzP2Q9MwSMKauBQ+UQ== + dependencies: + "@babel/helper-plugin-utils" "^7.16.7" + "@babel/plugin-syntax-nullish-coalescing-operator" "^7.8.3" + "@babel/plugin-proposal-nullish-coalescing-operator@^7.13.8": version "7.13.8" resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-nullish-coalescing-operator/-/plugin-proposal-nullish-coalescing-operator-7.13.8.tgz#3730a31dafd3c10d8ccd10648ed80a2ac5472ef3" @@ -841,6 +1162,14 @@ "@babel/helper-plugin-utils" "^7.14.5" "@babel/plugin-syntax-nullish-coalescing-operator" "^7.8.3" +"@babel/plugin-proposal-numeric-separator@^7.10.4": + version "7.16.7" + resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-numeric-separator/-/plugin-proposal-numeric-separator-7.16.7.tgz#d6b69f4af63fb38b6ca2558442a7fb191236eba9" + integrity sha512-vQgPMknOIgiuVqbokToyXbkY/OmmjAzr/0lhSIbG/KmnzXPGwW/AdhdKpi+O4X/VkWiWjnkKOBiqJrTaC98VKw== + dependencies: + "@babel/helper-plugin-utils" "^7.16.7" + "@babel/plugin-syntax-numeric-separator" "^7.10.4" + "@babel/plugin-proposal-numeric-separator@^7.12.13": version "7.12.13" resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-numeric-separator/-/plugin-proposal-numeric-separator-7.12.13.tgz#bd9da3188e787b5120b4f9d465a8261ce67ed1db" @@ -857,6 +1186,17 @@ "@babel/helper-plugin-utils" "^7.14.5" "@babel/plugin-syntax-numeric-separator" "^7.10.4" +"@babel/plugin-proposal-object-rest-spread@^7.11.0": + version "7.17.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-object-rest-spread/-/plugin-proposal-object-rest-spread-7.17.3.tgz#d9eb649a54628a51701aef7e0ea3d17e2b9dd390" + integrity sha512-yuL5iQA/TbZn+RGAfxQXfi7CNLmKi1f8zInn4IgobuCWcAb7i+zj4TYzQ9l8cEzVyJ89PDGuqxK1xZpUDISesw== + dependencies: + "@babel/compat-data" "^7.17.0" + "@babel/helper-compilation-targets" "^7.16.7" + "@babel/helper-plugin-utils" "^7.16.7" + "@babel/plugin-syntax-object-rest-spread" "^7.8.3" + "@babel/plugin-transform-parameters" "^7.16.7" + "@babel/plugin-proposal-object-rest-spread@^7.13.8": version "7.13.8" resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-object-rest-spread/-/plugin-proposal-object-rest-spread-7.13.8.tgz#5d210a4d727d6ce3b18f9de82cc99a3964eed60a" @@ -868,6 +1208,14 @@ "@babel/plugin-syntax-object-rest-spread" "^7.8.3" "@babel/plugin-transform-parameters" "^7.13.0" +"@babel/plugin-proposal-optional-catch-binding@^7.10.4": + version "7.16.7" + resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-optional-catch-binding/-/plugin-proposal-optional-catch-binding-7.16.7.tgz#c623a430674ffc4ab732fd0a0ae7722b67cb74cf" + integrity sha512-eMOH/L4OvWSZAE1VkHbr1vckLG1WUcHGJSLqqQwl2GaUqG6QjddvrOaTUMNYiv77H5IKPMZ9U9P7EaHwvAShfA== + dependencies: + "@babel/helper-plugin-utils" "^7.16.7" + "@babel/plugin-syntax-optional-catch-binding" "^7.8.3" + "@babel/plugin-proposal-optional-catch-binding@^7.13.8": version "7.13.8" resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-optional-catch-binding/-/plugin-proposal-optional-catch-binding-7.13.8.tgz#3ad6bd5901506ea996fc31bdcf3ccfa2bed71107" @@ -876,6 +1224,15 @@ "@babel/helper-plugin-utils" "^7.13.0" "@babel/plugin-syntax-optional-catch-binding" "^7.8.3" +"@babel/plugin-proposal-optional-chaining@^7.11.0": + version "7.16.7" + resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-optional-chaining/-/plugin-proposal-optional-chaining-7.16.7.tgz#7cd629564724816c0e8a969535551f943c64c39a" + integrity sha512-eC3xy+ZrUcBtP7x+sq62Q/HYd674pPTb/77XZMb5wbDPGWIdUbSr4Agr052+zaUPSb+gGRnjxXfKFvx5iMJ+DA== + dependencies: + "@babel/helper-plugin-utils" "^7.16.7" + "@babel/helper-skip-transparent-expression-wrappers" "^7.16.0" + "@babel/plugin-syntax-optional-chaining" "^7.8.3" + "@babel/plugin-proposal-optional-chaining@^7.13.12": version "7.13.12" resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-optional-chaining/-/plugin-proposal-optional-chaining-7.13.12.tgz#ba9feb601d422e0adea6760c2bd6bbb7bfec4866" @@ -894,6 +1251,14 @@ "@babel/helper-skip-transparent-expression-wrappers" "^7.16.0" "@babel/plugin-syntax-optional-chaining" "^7.8.3" +"@babel/plugin-proposal-private-methods@^7.10.4": + version "7.16.11" + resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-private-methods/-/plugin-proposal-private-methods-7.16.11.tgz#e8df108288555ff259f4527dbe84813aac3a1c50" + integrity sha512-F/2uAkPlXDr8+BHpZvo19w3hLFKge+k75XUprE6jaqKxjGkSYcK+4c+bup5PdW/7W/Rpjwql7FTVEDW+fRAQsw== + dependencies: + "@babel/helper-create-class-features-plugin" "^7.16.10" + "@babel/helper-plugin-utils" "^7.16.7" + "@babel/plugin-proposal-private-methods@^7.13.0": version "7.13.0" resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-private-methods/-/plugin-proposal-private-methods-7.13.0.tgz#04bd4c6d40f6e6bbfa2f57e2d8094bad900ef787" @@ -920,6 +1285,14 @@ "@babel/helper-plugin-utils" "^7.14.5" "@babel/plugin-syntax-private-property-in-object" "^7.14.5" +"@babel/plugin-proposal-unicode-property-regex@^7.10.4": + version "7.16.7" + resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-unicode-property-regex/-/plugin-proposal-unicode-property-regex-7.16.7.tgz#635d18eb10c6214210ffc5ff4932552de08188a2" + integrity sha512-QRK0YI/40VLhNVGIjRNAAQkEHws0cswSdFFjpFyt943YmJIU1da9uW63Iu6NFV6CxTZW5eTDCrwZUstBWgp/Rg== + dependencies: + "@babel/helper-create-regexp-features-plugin" "^7.16.7" + "@babel/helper-plugin-utils" "^7.16.7" + "@babel/plugin-proposal-unicode-property-regex@^7.12.13", "@babel/plugin-proposal-unicode-property-regex@^7.4.4": version "7.12.13" resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-unicode-property-regex/-/plugin-proposal-unicode-property-regex-7.12.13.tgz#bebde51339be829c17aaaaced18641deb62b39ba" @@ -928,7 +1301,7 @@ "@babel/helper-create-regexp-features-plugin" "^7.12.13" "@babel/helper-plugin-utils" "^7.12.13" -"@babel/plugin-syntax-async-generators@^7.8.4": +"@babel/plugin-syntax-async-generators@^7.8.0", "@babel/plugin-syntax-async-generators@^7.8.4": version "7.8.4" resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-async-generators/-/plugin-syntax-async-generators-7.8.4.tgz#a983fb1aeb2ec3f6ed042a210f640e90e786fe0d" integrity sha512-tycmZxkGfZaxhMRbXlPXuVFpdWlXpir2W4AMhSJgRKzk/eDlIXOhb2LHWoLpDF7TEHylV5zNhykX6KAgHJmTNw== @@ -942,7 +1315,7 @@ dependencies: "@babel/helper-plugin-utils" "^7.8.0" -"@babel/plugin-syntax-class-properties@^7.12.13", "@babel/plugin-syntax-class-properties@^7.8.3": +"@babel/plugin-syntax-class-properties@^7.10.4", "@babel/plugin-syntax-class-properties@^7.12.13", "@babel/plugin-syntax-class-properties@^7.8.3": version "7.12.13" resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-class-properties/-/plugin-syntax-class-properties-7.12.13.tgz#b5c987274c4a3a82b89714796931a6b53544ae10" integrity sha512-fm4idjKla0YahUNgFNLCB0qySdsoPiZP3iQE3rky0mBUtMZ23yDJ9SJdg6dXTSDnulOVqiF3Hgr9nbXvXTQZYA== @@ -956,7 +1329,7 @@ dependencies: "@babel/helper-plugin-utils" "^7.12.13" -"@babel/plugin-syntax-dynamic-import@^7.8.3": +"@babel/plugin-syntax-dynamic-import@^7.8.0", "@babel/plugin-syntax-dynamic-import@^7.8.3": version "7.8.3" resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-dynamic-import/-/plugin-syntax-dynamic-import-7.8.3.tgz#62bf98b2da3cd21d626154fc96ee5b3cb68eacb3" integrity sha512-5gdGbFon+PszYzqs83S3E5mpi7/y/8M9eC90MRTZfduQOYW76ig6SOSPNe41IG5LoP3FGBn2N0RjVDSQiS94kQ== @@ -977,7 +1350,7 @@ dependencies: "@babel/helper-plugin-utils" "^7.10.4" -"@babel/plugin-syntax-json-strings@^7.8.3": +"@babel/plugin-syntax-json-strings@^7.8.0", "@babel/plugin-syntax-json-strings@^7.8.3": version "7.8.3" resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-json-strings/-/plugin-syntax-json-strings-7.8.3.tgz#01ca21b668cd8218c9e640cb6dd88c5412b2c96a" integrity sha512-lY6kdGpWHvjoe2vk4WrAapEuBR69EMxZl+RoGRhrFGNYVK8mOPAW8VfbT/ZgrFbXlDNiiaxQnAtgVCZ6jv30EA== @@ -991,7 +1364,7 @@ dependencies: "@babel/helper-plugin-utils" "^7.10.4" -"@babel/plugin-syntax-nullish-coalescing-operator@^7.8.3": +"@babel/plugin-syntax-nullish-coalescing-operator@^7.8.0", "@babel/plugin-syntax-nullish-coalescing-operator@^7.8.3": version "7.8.3" resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-nullish-coalescing-operator/-/plugin-syntax-nullish-coalescing-operator-7.8.3.tgz#167ed70368886081f74b5c36c65a88c03b66d1a9" integrity sha512-aSff4zPII1u2QD7y+F8oDsz19ew4IGEJg9SVW+bqwpwtfFleiQDMdzA/R+UlWDzfnHFCxxleFT0PMIrR36XLNQ== @@ -1005,21 +1378,21 @@ dependencies: "@babel/helper-plugin-utils" "^7.10.4" -"@babel/plugin-syntax-object-rest-spread@^7.8.3": +"@babel/plugin-syntax-object-rest-spread@^7.8.0", "@babel/plugin-syntax-object-rest-spread@^7.8.3": version "7.8.3" resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-object-rest-spread/-/plugin-syntax-object-rest-spread-7.8.3.tgz#60e225edcbd98a640332a2e72dd3e66f1af55871" integrity sha512-XoqMijGZb9y3y2XskN+P1wUGiVwWZ5JmoDRwx5+3GmEplNyVM2s2Dg8ILFQm8rWM48orGy5YpI5Bl8U1y7ydlA== dependencies: "@babel/helper-plugin-utils" "^7.8.0" -"@babel/plugin-syntax-optional-catch-binding@^7.8.3": +"@babel/plugin-syntax-optional-catch-binding@^7.8.0", "@babel/plugin-syntax-optional-catch-binding@^7.8.3": version "7.8.3" resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-optional-catch-binding/-/plugin-syntax-optional-catch-binding-7.8.3.tgz#6111a265bcfb020eb9efd0fdfd7d26402b9ed6c1" integrity sha512-6VPD0Pc1lpTqw0aKoeRTMiB+kWhAoT24PA+ksWSBrFtl5SIRVpZlwN3NNPQjehA2E/91FV3RjLWoVTglWcSV3Q== dependencies: "@babel/helper-plugin-utils" "^7.8.0" -"@babel/plugin-syntax-optional-chaining@^7.8.3": +"@babel/plugin-syntax-optional-chaining@^7.8.0", "@babel/plugin-syntax-optional-chaining@^7.8.3": version "7.8.3" resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-optional-chaining/-/plugin-syntax-optional-chaining-7.8.3.tgz#4f69c2ab95167e0180cd5336613f8c5788f7d48a" integrity sha512-KoK9ErH1MBlCPxV0VANkXW2/dw4vlbGDrFgz8bmUsBGYkFRcbRwMh6cIJubdPrkxRwuGdtCk0v/wPTKbQgBjkg== @@ -1033,6 +1406,13 @@ dependencies: "@babel/helper-plugin-utils" "^7.14.5" +"@babel/plugin-syntax-top-level-await@^7.10.4", "@babel/plugin-syntax-top-level-await@^7.8.3": + version "7.14.5" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-top-level-await/-/plugin-syntax-top-level-await-7.14.5.tgz#c1cfdadc35a646240001f06138247b741c34d94c" + integrity sha512-hx++upLv5U1rgYfwe1xBQUhRmU41NEvpUvrp8jkrSCdvGSnM5/qdRMtylJ6PG5OFkBaHkbTAKTnd3/YyESRHFw== + dependencies: + "@babel/helper-plugin-utils" "^7.14.5" + "@babel/plugin-syntax-top-level-await@^7.12.13": version "7.12.13" resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-top-level-await/-/plugin-syntax-top-level-await-7.12.13.tgz#c5f0fa6e249f5b739727f923540cf7a806130178" @@ -1040,13 +1420,6 @@ dependencies: "@babel/helper-plugin-utils" "^7.12.13" -"@babel/plugin-syntax-top-level-await@^7.8.3": - version "7.14.5" - resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-top-level-await/-/plugin-syntax-top-level-await-7.14.5.tgz#c1cfdadc35a646240001f06138247b741c34d94c" - integrity sha512-hx++upLv5U1rgYfwe1xBQUhRmU41NEvpUvrp8jkrSCdvGSnM5/qdRMtylJ6PG5OFkBaHkbTAKTnd3/YyESRHFw== - dependencies: - "@babel/helper-plugin-utils" "^7.14.5" - "@babel/plugin-syntax-typescript@^7.12.13", "@babel/plugin-syntax-typescript@^7.2.0": version "7.12.13" resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.12.13.tgz#9dff111ca64154cef0f4dc52cf843d9f12ce4474" @@ -1068,6 +1441,13 @@ dependencies: "@babel/helper-plugin-utils" "^7.16.7" +"@babel/plugin-transform-arrow-functions@^7.10.4": + version "7.16.7" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-arrow-functions/-/plugin-transform-arrow-functions-7.16.7.tgz#44125e653d94b98db76369de9c396dc14bef4154" + integrity sha512-9ffkFFMbvzTvv+7dTp/66xvZAWASuPD5Tl9LK3Z9vhOmANo6j94rik+5YMBt4CwHVMWLWpMsriIc2zsa3WW3xQ== + dependencies: + "@babel/helper-plugin-utils" "^7.16.7" + "@babel/plugin-transform-arrow-functions@^7.13.0": version "7.13.0" resolved "https://registry.yarnpkg.com/@babel/plugin-transform-arrow-functions/-/plugin-transform-arrow-functions-7.13.0.tgz#10a59bebad52d637a027afa692e8d5ceff5e3dae" @@ -1075,6 +1455,15 @@ dependencies: "@babel/helper-plugin-utils" "^7.13.0" +"@babel/plugin-transform-async-to-generator@^7.10.4": + version "7.16.8" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-async-to-generator/-/plugin-transform-async-to-generator-7.16.8.tgz#b83dff4b970cf41f1b819f8b49cc0cfbaa53a808" + integrity sha512-MtmUmTJQHCnyJVrScNzNlofQJ3dLFuobYn3mwOTKHnSCMtbNsqvF71GQmJfFjdrXSsAA7iysFmYWw4bXZ20hOg== + dependencies: + "@babel/helper-module-imports" "^7.16.7" + "@babel/helper-plugin-utils" "^7.16.7" + "@babel/helper-remap-async-to-generator" "^7.16.8" + "@babel/plugin-transform-async-to-generator@^7.13.0": version "7.13.0" resolved "https://registry.yarnpkg.com/@babel/plugin-transform-async-to-generator/-/plugin-transform-async-to-generator-7.13.0.tgz#8e112bf6771b82bf1e974e5e26806c5c99aa516f" @@ -1084,6 +1473,13 @@ "@babel/helper-plugin-utils" "^7.13.0" "@babel/helper-remap-async-to-generator" "^7.13.0" +"@babel/plugin-transform-block-scoped-functions@^7.10.4": + version "7.16.7" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-block-scoped-functions/-/plugin-transform-block-scoped-functions-7.16.7.tgz#4d0d57d9632ef6062cdf354bb717102ee042a620" + integrity sha512-JUuzlzmF40Z9cXyytcbZEZKckgrQzChbQJw/5PuEHYeqzCsvebDx0K0jWnIIVcmmDOAVctCgnYs0pMcrYj2zJg== + dependencies: + "@babel/helper-plugin-utils" "^7.16.7" + "@babel/plugin-transform-block-scoped-functions@^7.12.13": version "7.12.13" resolved "https://registry.yarnpkg.com/@babel/plugin-transform-block-scoped-functions/-/plugin-transform-block-scoped-functions-7.12.13.tgz#a9bf1836f2a39b4eb6cf09967739de29ea4bf4c4" @@ -1091,6 +1487,13 @@ dependencies: "@babel/helper-plugin-utils" "^7.12.13" +"@babel/plugin-transform-block-scoping@^7.10.4": + version "7.16.7" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-block-scoping/-/plugin-transform-block-scoping-7.16.7.tgz#f50664ab99ddeaee5bc681b8f3a6ea9d72ab4f87" + integrity sha512-ObZev2nxVAYA4bhyusELdo9hb3H+A56bxH3FZMbEImZFiEDYVHXQSJ1hQKFlDnlt8G9bBrCZ5ZpURZUrV4G5qQ== + dependencies: + "@babel/helper-plugin-utils" "^7.16.7" + "@babel/plugin-transform-block-scoping@^7.12.13", "@babel/plugin-transform-block-scoping@^7.8.3": version "7.12.13" resolved "https://registry.yarnpkg.com/@babel/plugin-transform-block-scoping/-/plugin-transform-block-scoping-7.12.13.tgz#f36e55076d06f41dfd78557ea039c1b581642e61" @@ -1098,6 +1501,20 @@ dependencies: "@babel/helper-plugin-utils" "^7.12.13" +"@babel/plugin-transform-classes@^7.10.4": + version "7.16.7" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-classes/-/plugin-transform-classes-7.16.7.tgz#8f4b9562850cd973de3b498f1218796eb181ce00" + integrity sha512-WY7og38SFAGYRe64BrjKf8OrE6ulEHtr5jEYaZMwox9KebgqPi67Zqz8K53EKk1fFEJgm96r32rkKZ3qA2nCWQ== + dependencies: + "@babel/helper-annotate-as-pure" "^7.16.7" + "@babel/helper-environment-visitor" "^7.16.7" + "@babel/helper-function-name" "^7.16.7" + "@babel/helper-optimise-call-expression" "^7.16.7" + "@babel/helper-plugin-utils" "^7.16.7" + "@babel/helper-replace-supers" "^7.16.7" + "@babel/helper-split-export-declaration" "^7.16.7" + globals "^11.1.0" + "@babel/plugin-transform-classes@^7.13.0": version "7.13.0" resolved "https://registry.yarnpkg.com/@babel/plugin-transform-classes/-/plugin-transform-classes-7.13.0.tgz#0265155075c42918bf4d3a4053134176ad9b533b" @@ -1111,6 +1528,13 @@ "@babel/helper-split-export-declaration" "^7.12.13" globals "^11.1.0" +"@babel/plugin-transform-computed-properties@^7.10.4": + version "7.16.7" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-computed-properties/-/plugin-transform-computed-properties-7.16.7.tgz#66dee12e46f61d2aae7a73710f591eb3df616470" + integrity sha512-gN72G9bcmenVILj//sv1zLNaPyYcOzUho2lIJBMh/iakJ9ygCo/hEF9cpGb61SCMEDxbbyBoVQxrt+bWKu5KGw== + dependencies: + "@babel/helper-plugin-utils" "^7.16.7" + "@babel/plugin-transform-computed-properties@^7.13.0": version "7.13.0" resolved "https://registry.yarnpkg.com/@babel/plugin-transform-computed-properties/-/plugin-transform-computed-properties-7.13.0.tgz#845c6e8b9bb55376b1fa0b92ef0bdc8ea06644ed" @@ -1118,6 +1542,13 @@ dependencies: "@babel/helper-plugin-utils" "^7.13.0" +"@babel/plugin-transform-destructuring@^7.10.4": + version "7.17.7" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-destructuring/-/plugin-transform-destructuring-7.17.7.tgz#49dc2675a7afa9a5e4c6bdee636061136c3408d1" + integrity sha512-XVh0r5yq9sLR4vZ6eVZe8FKfIcSgaTBxVBRSYokRj2qksf6QerYnTxz9/GTuKTH/n/HwLP7t6gtlybHetJ/6hQ== + dependencies: + "@babel/helper-plugin-utils" "^7.16.7" + "@babel/plugin-transform-destructuring@^7.13.0": version "7.13.0" resolved "https://registry.yarnpkg.com/@babel/plugin-transform-destructuring/-/plugin-transform-destructuring-7.13.0.tgz#c5dce270014d4e1ebb1d806116694c12b7028963" @@ -1125,6 +1556,14 @@ dependencies: "@babel/helper-plugin-utils" "^7.13.0" +"@babel/plugin-transform-dotall-regex@^7.10.4": + version "7.16.7" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-dotall-regex/-/plugin-transform-dotall-regex-7.16.7.tgz#6b2d67686fab15fb6a7fd4bd895d5982cfc81241" + integrity sha512-Lyttaao2SjZF6Pf4vk1dVKv8YypMpomAbygW+mU5cYP3S5cWTfCJjG8xV6CFdzGFlfWK81IjL9viiTvpb6G7gQ== + dependencies: + "@babel/helper-create-regexp-features-plugin" "^7.16.7" + "@babel/helper-plugin-utils" "^7.16.7" + "@babel/plugin-transform-dotall-regex@^7.12.13", "@babel/plugin-transform-dotall-regex@^7.4.4": version "7.12.13" resolved "https://registry.yarnpkg.com/@babel/plugin-transform-dotall-regex/-/plugin-transform-dotall-regex-7.12.13.tgz#3f1601cc29905bfcb67f53910f197aeafebb25ad" @@ -1133,6 +1572,13 @@ "@babel/helper-create-regexp-features-plugin" "^7.12.13" "@babel/helper-plugin-utils" "^7.12.13" +"@babel/plugin-transform-duplicate-keys@^7.10.4": + version "7.16.7" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-duplicate-keys/-/plugin-transform-duplicate-keys-7.16.7.tgz#2207e9ca8f82a0d36a5a67b6536e7ef8b08823c9" + integrity sha512-03DvpbRfvWIXyK0/6QiR1KMTWeT6OcQ7tbhjrXyFS02kjuX/mu5Bvnh5SDSWHxyawit2g5aWhKwI86EE7GUnTw== + dependencies: + "@babel/helper-plugin-utils" "^7.16.7" + "@babel/plugin-transform-duplicate-keys@^7.12.13": version "7.12.13" resolved "https://registry.yarnpkg.com/@babel/plugin-transform-duplicate-keys/-/plugin-transform-duplicate-keys-7.12.13.tgz#6f06b87a8b803fd928e54b81c258f0a0033904de" @@ -1140,6 +1586,14 @@ dependencies: "@babel/helper-plugin-utils" "^7.12.13" +"@babel/plugin-transform-exponentiation-operator@^7.10.4": + version "7.16.7" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-exponentiation-operator/-/plugin-transform-exponentiation-operator-7.16.7.tgz#efa9862ef97e9e9e5f653f6ddc7b665e8536fe9b" + integrity sha512-8UYLSlyLgRixQvlYH3J2ekXFHDFLQutdy7FfFAMm3CPZ6q9wHCwnUyiXpQCe3gVVnQlHc5nsuiEVziteRNTXEA== + dependencies: + "@babel/helper-builder-binary-assignment-operator-visitor" "^7.16.7" + "@babel/helper-plugin-utils" "^7.16.7" + "@babel/plugin-transform-exponentiation-operator@^7.12.13": version "7.12.13" resolved "https://registry.yarnpkg.com/@babel/plugin-transform-exponentiation-operator/-/plugin-transform-exponentiation-operator-7.12.13.tgz#4d52390b9a273e651e4aba6aee49ef40e80cd0a1" @@ -1148,6 +1602,13 @@ "@babel/helper-builder-binary-assignment-operator-visitor" "^7.12.13" "@babel/helper-plugin-utils" "^7.12.13" +"@babel/plugin-transform-for-of@^7.10.4": + version "7.16.7" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-for-of/-/plugin-transform-for-of-7.16.7.tgz#649d639d4617dff502a9a158c479b3b556728d8c" + integrity sha512-/QZm9W92Ptpw7sjI9Nx1mbcsWz33+l8kuMIQnDwgQBG5s3fAfQvkRjQ7NqXhtNcKOnPkdICmUHyCaWW06HCsqg== + dependencies: + "@babel/helper-plugin-utils" "^7.16.7" + "@babel/plugin-transform-for-of@^7.13.0": version "7.13.0" resolved "https://registry.yarnpkg.com/@babel/plugin-transform-for-of/-/plugin-transform-for-of-7.13.0.tgz#c799f881a8091ac26b54867a845c3e97d2696062" @@ -1155,6 +1616,15 @@ dependencies: "@babel/helper-plugin-utils" "^7.13.0" +"@babel/plugin-transform-function-name@^7.10.4": + version "7.16.7" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-function-name/-/plugin-transform-function-name-7.16.7.tgz#5ab34375c64d61d083d7d2f05c38d90b97ec65cf" + integrity sha512-SU/C68YVwTRxqWj5kgsbKINakGag0KTgq9f2iZEXdStoAbOzLHEBRYzImmA6yFo8YZhJVflvXmIHUO7GWHmxxA== + dependencies: + "@babel/helper-compilation-targets" "^7.16.7" + "@babel/helper-function-name" "^7.16.7" + "@babel/helper-plugin-utils" "^7.16.7" + "@babel/plugin-transform-function-name@^7.12.13": version "7.12.13" resolved "https://registry.yarnpkg.com/@babel/plugin-transform-function-name/-/plugin-transform-function-name-7.12.13.tgz#bb024452f9aaed861d374c8e7a24252ce3a50051" @@ -1163,6 +1633,13 @@ "@babel/helper-function-name" "^7.12.13" "@babel/helper-plugin-utils" "^7.12.13" +"@babel/plugin-transform-literals@^7.10.4": + version "7.16.7" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-literals/-/plugin-transform-literals-7.16.7.tgz#254c9618c5ff749e87cb0c0cef1a0a050c0bdab1" + integrity sha512-6tH8RTpTWI0s2sV6uq3e/C9wPo4PTqqZps4uF0kzQ9/xPLFQtipynvmT1g/dOfEJ+0EQsHhkQ/zyRId8J2b8zQ== + dependencies: + "@babel/helper-plugin-utils" "^7.16.7" + "@babel/plugin-transform-literals@^7.12.13": version "7.12.13" resolved "https://registry.yarnpkg.com/@babel/plugin-transform-literals/-/plugin-transform-literals-7.12.13.tgz#2ca45bafe4a820197cf315794a4d26560fe4bdb9" @@ -1170,6 +1647,13 @@ dependencies: "@babel/helper-plugin-utils" "^7.12.13" +"@babel/plugin-transform-member-expression-literals@^7.10.4": + version "7.16.7" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-member-expression-literals/-/plugin-transform-member-expression-literals-7.16.7.tgz#6e5dcf906ef8a098e630149d14c867dd28f92384" + integrity sha512-mBruRMbktKQwbxaJof32LT9KLy2f3gH+27a5XSuXo6h7R3vqltl0PgZ80C8ZMKw98Bf8bqt6BEVi3svOh2PzMw== + dependencies: + "@babel/helper-plugin-utils" "^7.16.7" + "@babel/plugin-transform-member-expression-literals@^7.12.13": version "7.12.13" resolved "https://registry.yarnpkg.com/@babel/plugin-transform-member-expression-literals/-/plugin-transform-member-expression-literals-7.12.13.tgz#5ffa66cd59b9e191314c9f1f803b938e8c081e40" @@ -1177,6 +1661,15 @@ dependencies: "@babel/helper-plugin-utils" "^7.12.13" +"@babel/plugin-transform-modules-amd@^7.10.4": + version "7.16.7" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-amd/-/plugin-transform-modules-amd-7.16.7.tgz#b28d323016a7daaae8609781d1f8c9da42b13186" + integrity sha512-KaaEtgBL7FKYwjJ/teH63oAmE3lP34N3kshz8mm4VMAw7U3PxjVwwUmxEFksbgsNUaO3wId9R2AVQYSEGRa2+g== + dependencies: + "@babel/helper-module-transforms" "^7.16.7" + "@babel/helper-plugin-utils" "^7.16.7" + babel-plugin-dynamic-import-node "^2.3.3" + "@babel/plugin-transform-modules-amd@^7.12.1", "@babel/plugin-transform-modules-amd@^7.13.0": version "7.13.0" resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-amd/-/plugin-transform-modules-amd-7.13.0.tgz#19f511d60e3d8753cc5a6d4e775d3a5184866cc3" @@ -1186,6 +1679,16 @@ "@babel/helper-plugin-utils" "^7.13.0" babel-plugin-dynamic-import-node "^2.3.3" +"@babel/plugin-transform-modules-commonjs@^7.10.4": + version "7.17.9" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.17.9.tgz#274be1a2087beec0254d4abd4d86e52442e1e5b6" + integrity sha512-2TBFd/r2I6VlYn0YRTz2JdazS+FoUuQ2rIFHoAxtyP/0G3D82SBLaRq9rnUkpqlLg03Byfl/+M32mpxjO6KaPw== + dependencies: + "@babel/helper-module-transforms" "^7.17.7" + "@babel/helper-plugin-utils" "^7.16.7" + "@babel/helper-simple-access" "^7.17.7" + babel-plugin-dynamic-import-node "^2.3.3" + "@babel/plugin-transform-modules-commonjs@^7.13.8": version "7.13.8" resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.13.8.tgz#7b01ad7c2dcf2275b06fa1781e00d13d420b3e1b" @@ -1206,6 +1709,17 @@ "@babel/helper-simple-access" "^7.16.0" babel-plugin-dynamic-import-node "^2.3.3" +"@babel/plugin-transform-modules-systemjs@^7.10.4": + version "7.17.8" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-systemjs/-/plugin-transform-modules-systemjs-7.17.8.tgz#81fd834024fae14ea78fbe34168b042f38703859" + integrity sha512-39reIkMTUVagzgA5x88zDYXPCMT6lcaRKs1+S9K6NKBPErbgO/w/kP8GlNQTC87b412ZTlmNgr3k2JrWgHH+Bw== + dependencies: + "@babel/helper-hoist-variables" "^7.16.7" + "@babel/helper-module-transforms" "^7.17.7" + "@babel/helper-plugin-utils" "^7.16.7" + "@babel/helper-validator-identifier" "^7.16.7" + babel-plugin-dynamic-import-node "^2.3.3" + "@babel/plugin-transform-modules-systemjs@^7.13.8": version "7.13.8" resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-systemjs/-/plugin-transform-modules-systemjs-7.13.8.tgz#6d066ee2bff3c7b3d60bf28dec169ad993831ae3" @@ -1217,6 +1731,14 @@ "@babel/helper-validator-identifier" "^7.12.11" babel-plugin-dynamic-import-node "^2.3.3" +"@babel/plugin-transform-modules-umd@^7.10.4": + version "7.16.7" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-umd/-/plugin-transform-modules-umd-7.16.7.tgz#23dad479fa585283dbd22215bff12719171e7618" + integrity sha512-EMh7uolsC8O4xhudF2F6wedbSHm1HHZ0C6aJ7K67zcDNidMzVcxWdGr+htW9n21klm+bOn+Rx4CBsAntZd3rEQ== + dependencies: + "@babel/helper-module-transforms" "^7.16.7" + "@babel/helper-plugin-utils" "^7.16.7" + "@babel/plugin-transform-modules-umd@^7.13.0": version "7.13.0" resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-umd/-/plugin-transform-modules-umd-7.13.0.tgz#8a3d96a97d199705b9fd021580082af81c06e70b" @@ -1225,6 +1747,13 @@ "@babel/helper-module-transforms" "^7.13.0" "@babel/helper-plugin-utils" "^7.13.0" +"@babel/plugin-transform-named-capturing-groups-regex@^7.10.4": + version "7.16.8" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-named-capturing-groups-regex/-/plugin-transform-named-capturing-groups-regex-7.16.8.tgz#7f860e0e40d844a02c9dcf9d84965e7dfd666252" + integrity sha512-j3Jw+n5PvpmhRR+mrgIh04puSANCk/T/UA3m3P1MjJkhlK906+ApHhDIqBQDdOgL/r1UYpz4GNclTXxyZrYGSw== + dependencies: + "@babel/helper-create-regexp-features-plugin" "^7.16.7" + "@babel/plugin-transform-named-capturing-groups-regex@^7.12.13": version "7.12.13" resolved "https://registry.yarnpkg.com/@babel/plugin-transform-named-capturing-groups-regex/-/plugin-transform-named-capturing-groups-regex-7.12.13.tgz#2213725a5f5bbbe364b50c3ba5998c9599c5c9d9" @@ -1232,6 +1761,13 @@ dependencies: "@babel/helper-create-regexp-features-plugin" "^7.12.13" +"@babel/plugin-transform-new-target@^7.10.4": + version "7.16.7" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-new-target/-/plugin-transform-new-target-7.16.7.tgz#9967d89a5c243818e0800fdad89db22c5f514244" + integrity sha512-xiLDzWNMfKoGOpc6t3U+etCE2yRnn3SM09BXqWPIZOBpL2gvVrBWUKnsJx0K/ADi5F5YC5f8APFfWrz25TdlGg== + dependencies: + "@babel/helper-plugin-utils" "^7.16.7" + "@babel/plugin-transform-new-target@^7.12.13": version "7.12.13" resolved "https://registry.yarnpkg.com/@babel/plugin-transform-new-target/-/plugin-transform-new-target-7.12.13.tgz#e22d8c3af24b150dd528cbd6e685e799bf1c351c" @@ -1246,6 +1782,14 @@ dependencies: "@babel/helper-plugin-utils" "^7.14.5" +"@babel/plugin-transform-object-super@^7.10.4": + version "7.16.7" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-object-super/-/plugin-transform-object-super-7.16.7.tgz#ac359cf8d32cf4354d27a46867999490b6c32a94" + integrity sha512-14J1feiQVWaGvRxj2WjyMuXS2jsBkgB3MdSN5HuC2G5nRspa5RK9COcs82Pwy5BuGcjb+fYaUj94mYcOj7rCvw== + dependencies: + "@babel/helper-plugin-utils" "^7.16.7" + "@babel/helper-replace-supers" "^7.16.7" + "@babel/plugin-transform-object-super@^7.12.13": version "7.12.13" resolved "https://registry.yarnpkg.com/@babel/plugin-transform-object-super/-/plugin-transform-object-super-7.12.13.tgz#b4416a2d63b8f7be314f3d349bd55a9c1b5171f7" @@ -1254,6 +1798,13 @@ "@babel/helper-plugin-utils" "^7.12.13" "@babel/helper-replace-supers" "^7.12.13" +"@babel/plugin-transform-parameters@^7.10.4", "@babel/plugin-transform-parameters@^7.16.7": + version "7.16.7" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-parameters/-/plugin-transform-parameters-7.16.7.tgz#a1721f55b99b736511cb7e0152f61f17688f331f" + integrity sha512-AT3MufQ7zZEhU2hwOA11axBnExW0Lszu4RL/tAlUJBuNoRak+wehQW8h6KcXOcgjY42fHtDxswuMhMjFEuv/aw== + dependencies: + "@babel/helper-plugin-utils" "^7.16.7" + "@babel/plugin-transform-parameters@^7.13.0": version "7.13.0" resolved "https://registry.yarnpkg.com/@babel/plugin-transform-parameters/-/plugin-transform-parameters-7.13.0.tgz#8fa7603e3097f9c0b7ca1a4821bc2fb52e9e5007" @@ -1261,6 +1812,13 @@ dependencies: "@babel/helper-plugin-utils" "^7.13.0" +"@babel/plugin-transform-property-literals@^7.10.4": + version "7.16.7" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-property-literals/-/plugin-transform-property-literals-7.16.7.tgz#2dadac85155436f22c696c4827730e0fe1057a55" + integrity sha512-z4FGr9NMGdoIl1RqavCqGG+ZuYjfZ/hkCIeuH6Do7tXmSm0ls11nYVSJqFEUOSJbDab5wC6lRE/w6YjVcr6Hqw== + dependencies: + "@babel/helper-plugin-utils" "^7.16.7" + "@babel/plugin-transform-property-literals@^7.12.13": version "7.12.13" resolved "https://registry.yarnpkg.com/@babel/plugin-transform-property-literals/-/plugin-transform-property-literals-7.12.13.tgz#4e6a9e37864d8f1b3bc0e2dce7bf8857db8b1a81" @@ -1268,6 +1826,13 @@ dependencies: "@babel/helper-plugin-utils" "^7.12.13" +"@babel/plugin-transform-regenerator@^7.10.4": + version "7.17.9" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-regenerator/-/plugin-transform-regenerator-7.17.9.tgz#0a33c3a61cf47f45ed3232903683a0afd2d3460c" + integrity sha512-Lc2TfbxR1HOyn/c6b4Y/b6NHoTb67n/IoWLxTu4kC7h4KQnWlhCq2S8Tx0t2SVvv5Uu87Hs+6JEJ5kt2tYGylQ== + dependencies: + regenerator-transform "^0.15.0" + "@babel/plugin-transform-regenerator@^7.12.13": version "7.12.13" resolved "https://registry.yarnpkg.com/@babel/plugin-transform-regenerator/-/plugin-transform-regenerator-7.12.13.tgz#b628bcc9c85260ac1aeb05b45bde25210194a2f5" @@ -1275,6 +1840,13 @@ dependencies: regenerator-transform "^0.14.2" +"@babel/plugin-transform-reserved-words@^7.10.4": + version "7.16.7" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-reserved-words/-/plugin-transform-reserved-words-7.16.7.tgz#1d798e078f7c5958eec952059c460b220a63f586" + integrity sha512-KQzzDnZ9hWQBjwi5lpY5v9shmm6IVG0U9pB18zvMu2i4H90xpT4gmqwPYsn8rObiadYe2M0gmgsiOIF5A/2rtg== + dependencies: + "@babel/helper-plugin-utils" "^7.16.7" + "@babel/plugin-transform-reserved-words@^7.12.13": version "7.12.13" resolved "https://registry.yarnpkg.com/@babel/plugin-transform-reserved-words/-/plugin-transform-reserved-words-7.12.13.tgz#7d9988d4f06e0fe697ea1d9803188aa18b472695" @@ -1282,6 +1854,16 @@ dependencies: "@babel/helper-plugin-utils" "^7.12.13" +"@babel/plugin-transform-runtime@7.11.0": + version "7.11.0" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-runtime/-/plugin-transform-runtime-7.11.0.tgz#e27f78eb36f19448636e05c33c90fd9ad9b8bccf" + integrity sha512-LFEsP+t3wkYBlis8w6/kmnd6Kb1dxTd+wGJ8MlxTGzQo//ehtqlVL4S9DNUa53+dtPSQobN2CXx4d81FqC58cw== + dependencies: + "@babel/helper-module-imports" "^7.10.4" + "@babel/helper-plugin-utils" "^7.10.4" + resolve "^1.8.1" + semver "^5.5.1" + "@babel/plugin-transform-runtime@^7.13.9": version "7.13.10" resolved "https://registry.yarnpkg.com/@babel/plugin-transform-runtime/-/plugin-transform-runtime-7.13.10.tgz#a1e40d22e2bf570c591c9c7e5ab42d6bf1e419e1" @@ -1294,6 +1876,13 @@ babel-plugin-polyfill-regenerator "^0.1.2" semver "^6.3.0" +"@babel/plugin-transform-shorthand-properties@^7.10.4": + version "7.16.7" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-shorthand-properties/-/plugin-transform-shorthand-properties-7.16.7.tgz#e8549ae4afcf8382f711794c0c7b6b934c5fbd2a" + integrity sha512-hah2+FEnoRoATdIb05IOXf+4GzXYTq75TVhIn1PewihbpyrNWUt2JbudKQOETWw6QpLe+AIUpJ5MVLYTQbeeUg== + dependencies: + "@babel/helper-plugin-utils" "^7.16.7" + "@babel/plugin-transform-shorthand-properties@^7.12.13": version "7.12.13" resolved "https://registry.yarnpkg.com/@babel/plugin-transform-shorthand-properties/-/plugin-transform-shorthand-properties-7.12.13.tgz#db755732b70c539d504c6390d9ce90fe64aff7ad" @@ -1301,6 +1890,14 @@ dependencies: "@babel/helper-plugin-utils" "^7.12.13" +"@babel/plugin-transform-spread@^7.11.0": + version "7.16.7" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-spread/-/plugin-transform-spread-7.16.7.tgz#a303e2122f9f12e0105daeedd0f30fb197d8ff44" + integrity sha512-+pjJpgAngb53L0iaA5gU/1MLXJIfXcYepLgXB3esVRf4fqmj8f2cxM3/FKaHsZms08hFQJkFccEWuIpm429TXg== + dependencies: + "@babel/helper-plugin-utils" "^7.16.7" + "@babel/helper-skip-transparent-expression-wrappers" "^7.16.0" + "@babel/plugin-transform-spread@^7.13.0": version "7.13.0" resolved "https://registry.yarnpkg.com/@babel/plugin-transform-spread/-/plugin-transform-spread-7.13.0.tgz#84887710e273c1815ace7ae459f6f42a5d31d5fd" @@ -1309,6 +1906,13 @@ "@babel/helper-plugin-utils" "^7.13.0" "@babel/helper-skip-transparent-expression-wrappers" "^7.12.1" +"@babel/plugin-transform-sticky-regex@^7.10.4": + version "7.16.7" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-sticky-regex/-/plugin-transform-sticky-regex-7.16.7.tgz#c84741d4f4a38072b9a1e2e3fd56d359552e8660" + integrity sha512-NJa0Bd/87QV5NZZzTuZG5BPJjLYadeSZ9fO6oOUoL4iQx+9EEuw/eEM92SrsT19Yc2jgB1u1hsjqDtH02c3Drw== + dependencies: + "@babel/helper-plugin-utils" "^7.16.7" + "@babel/plugin-transform-sticky-regex@^7.12.13": version "7.12.13" resolved "https://registry.yarnpkg.com/@babel/plugin-transform-sticky-regex/-/plugin-transform-sticky-regex-7.12.13.tgz#760ffd936face73f860ae646fb86ee82f3d06d1f" @@ -1316,6 +1920,13 @@ dependencies: "@babel/helper-plugin-utils" "^7.12.13" +"@babel/plugin-transform-template-literals@^7.10.4": + version "7.16.7" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-template-literals/-/plugin-transform-template-literals-7.16.7.tgz#f3d1c45d28967c8e80f53666fc9c3e50618217ab" + integrity sha512-VwbkDDUeenlIjmfNeDX/V0aWrQH2QiVyJtwymVQSzItFDTpxfyJh3EVaQiS0rIN/CqbLGr0VcGmuwyTdZtdIsA== + dependencies: + "@babel/helper-plugin-utils" "^7.16.7" + "@babel/plugin-transform-template-literals@^7.13.0": version "7.13.0" resolved "https://registry.yarnpkg.com/@babel/plugin-transform-template-literals/-/plugin-transform-template-literals-7.13.0.tgz#a36049127977ad94438dee7443598d1cefdf409d" @@ -1323,6 +1934,13 @@ dependencies: "@babel/helper-plugin-utils" "^7.13.0" +"@babel/plugin-transform-typeof-symbol@^7.10.4": + version "7.16.7" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-typeof-symbol/-/plugin-transform-typeof-symbol-7.16.7.tgz#9cdbe622582c21368bd482b660ba87d5545d4f7e" + integrity sha512-p2rOixCKRJzpg9JB4gjnG4gjWkWa89ZoYUnl9snJ1cWIcTH/hvxZqfO+WjG6T8DRBpctEol5jw1O5rA8gkCokQ== + dependencies: + "@babel/helper-plugin-utils" "^7.16.7" + "@babel/plugin-transform-typeof-symbol@^7.12.13": version "7.12.13" resolved "https://registry.yarnpkg.com/@babel/plugin-transform-typeof-symbol/-/plugin-transform-typeof-symbol-7.12.13.tgz#785dd67a1f2ea579d9c2be722de8c84cb85f5a7f" @@ -1374,6 +1992,13 @@ "@babel/helper-plugin-utils" "^7.0.0" "@babel/plugin-syntax-typescript" "^7.2.0" +"@babel/plugin-transform-unicode-escapes@^7.10.4": + version "7.16.7" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-unicode-escapes/-/plugin-transform-unicode-escapes-7.16.7.tgz#da8717de7b3287a2c6d659750c964f302b31ece3" + integrity sha512-TAV5IGahIz3yZ9/Hfv35TV2xEm+kaBDaZQCn2S/hG9/CZ0DktxJv9eKfPc7yYCvOYR4JGx1h8C+jcSOvgaaI/Q== + dependencies: + "@babel/helper-plugin-utils" "^7.16.7" + "@babel/plugin-transform-unicode-escapes@^7.12.13": version "7.12.13" resolved "https://registry.yarnpkg.com/@babel/plugin-transform-unicode-escapes/-/plugin-transform-unicode-escapes-7.12.13.tgz#840ced3b816d3b5127dd1d12dcedc5dead1a5e74" @@ -1381,6 +2006,14 @@ dependencies: "@babel/helper-plugin-utils" "^7.12.13" +"@babel/plugin-transform-unicode-regex@^7.10.4": + version "7.16.7" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-unicode-regex/-/plugin-transform-unicode-regex-7.16.7.tgz#0f7aa4a501198976e25e82702574c34cfebe9ef2" + integrity sha512-oC5tYYKw56HO75KZVLQ+R/Nl3Hro9kf8iG0hXoaHP7tjAyCpvqBiSNe6vGrZni1Z6MggmUOC6A7VP7AVmw225Q== + dependencies: + "@babel/helper-create-regexp-features-plugin" "^7.16.7" + "@babel/helper-plugin-utils" "^7.16.7" + "@babel/plugin-transform-unicode-regex@^7.12.13": version "7.12.13" resolved "https://registry.yarnpkg.com/@babel/plugin-transform-unicode-regex/-/plugin-transform-unicode-regex-7.12.13.tgz#b52521685804e155b1202e83fc188d34bb70f5ac" @@ -1397,6 +2030,80 @@ core-js "^2.6.5" regenerator-runtime "^0.13.4" +"@babel/preset-env@7.11.0": + version "7.11.0" + resolved "https://registry.yarnpkg.com/@babel/preset-env/-/preset-env-7.11.0.tgz#860ee38f2ce17ad60480c2021ba9689393efb796" + integrity sha512-2u1/k7rG/gTh02dylX2kL3S0IJNF+J6bfDSp4DI2Ma8QN6Y9x9pmAax59fsCk6QUQG0yqH47yJWA+u1I1LccAg== + dependencies: + "@babel/compat-data" "^7.11.0" + "@babel/helper-compilation-targets" "^7.10.4" + "@babel/helper-module-imports" "^7.10.4" + "@babel/helper-plugin-utils" "^7.10.4" + "@babel/plugin-proposal-async-generator-functions" "^7.10.4" + "@babel/plugin-proposal-class-properties" "^7.10.4" + "@babel/plugin-proposal-dynamic-import" "^7.10.4" + "@babel/plugin-proposal-export-namespace-from" "^7.10.4" + "@babel/plugin-proposal-json-strings" "^7.10.4" + "@babel/plugin-proposal-logical-assignment-operators" "^7.11.0" + "@babel/plugin-proposal-nullish-coalescing-operator" "^7.10.4" + "@babel/plugin-proposal-numeric-separator" "^7.10.4" + "@babel/plugin-proposal-object-rest-spread" "^7.11.0" + "@babel/plugin-proposal-optional-catch-binding" "^7.10.4" + "@babel/plugin-proposal-optional-chaining" "^7.11.0" + "@babel/plugin-proposal-private-methods" "^7.10.4" + "@babel/plugin-proposal-unicode-property-regex" "^7.10.4" + "@babel/plugin-syntax-async-generators" "^7.8.0" + "@babel/plugin-syntax-class-properties" "^7.10.4" + "@babel/plugin-syntax-dynamic-import" "^7.8.0" + "@babel/plugin-syntax-export-namespace-from" "^7.8.3" + "@babel/plugin-syntax-json-strings" "^7.8.0" + "@babel/plugin-syntax-logical-assignment-operators" "^7.10.4" + "@babel/plugin-syntax-nullish-coalescing-operator" "^7.8.0" + "@babel/plugin-syntax-numeric-separator" "^7.10.4" + "@babel/plugin-syntax-object-rest-spread" "^7.8.0" + "@babel/plugin-syntax-optional-catch-binding" "^7.8.0" + "@babel/plugin-syntax-optional-chaining" "^7.8.0" + "@babel/plugin-syntax-top-level-await" "^7.10.4" + "@babel/plugin-transform-arrow-functions" "^7.10.4" + "@babel/plugin-transform-async-to-generator" "^7.10.4" + "@babel/plugin-transform-block-scoped-functions" "^7.10.4" + "@babel/plugin-transform-block-scoping" "^7.10.4" + "@babel/plugin-transform-classes" "^7.10.4" + "@babel/plugin-transform-computed-properties" "^7.10.4" + "@babel/plugin-transform-destructuring" "^7.10.4" + "@babel/plugin-transform-dotall-regex" "^7.10.4" + "@babel/plugin-transform-duplicate-keys" "^7.10.4" + "@babel/plugin-transform-exponentiation-operator" "^7.10.4" + "@babel/plugin-transform-for-of" "^7.10.4" + "@babel/plugin-transform-function-name" "^7.10.4" + "@babel/plugin-transform-literals" "^7.10.4" + "@babel/plugin-transform-member-expression-literals" "^7.10.4" + "@babel/plugin-transform-modules-amd" "^7.10.4" + "@babel/plugin-transform-modules-commonjs" "^7.10.4" + "@babel/plugin-transform-modules-systemjs" "^7.10.4" + "@babel/plugin-transform-modules-umd" "^7.10.4" + "@babel/plugin-transform-named-capturing-groups-regex" "^7.10.4" + "@babel/plugin-transform-new-target" "^7.10.4" + "@babel/plugin-transform-object-super" "^7.10.4" + "@babel/plugin-transform-parameters" "^7.10.4" + "@babel/plugin-transform-property-literals" "^7.10.4" + "@babel/plugin-transform-regenerator" "^7.10.4" + "@babel/plugin-transform-reserved-words" "^7.10.4" + "@babel/plugin-transform-shorthand-properties" "^7.10.4" + "@babel/plugin-transform-spread" "^7.11.0" + "@babel/plugin-transform-sticky-regex" "^7.10.4" + "@babel/plugin-transform-template-literals" "^7.10.4" + "@babel/plugin-transform-typeof-symbol" "^7.10.4" + "@babel/plugin-transform-unicode-escapes" "^7.10.4" + "@babel/plugin-transform-unicode-regex" "^7.10.4" + "@babel/preset-modules" "^0.1.3" + "@babel/types" "^7.11.0" + browserslist "^4.12.0" + core-js-compat "^3.6.2" + invariant "^2.2.2" + levenary "^1.1.1" + semver "^5.5.0" + "@babel/preset-env@^7.10.2", "@babel/preset-env@^7.12.0": version "7.13.12" resolved "https://registry.yarnpkg.com/@babel/preset-env/-/preset-env-7.13.12.tgz#6dff470478290582ac282fb77780eadf32480237" @@ -1472,6 +2179,17 @@ core-js-compat "^3.9.0" semver "^6.3.0" +"@babel/preset-modules@^0.1.3": + version "0.1.5" + resolved "https://registry.yarnpkg.com/@babel/preset-modules/-/preset-modules-0.1.5.tgz#ef939d6e7f268827e1841638dc6ff95515e115d9" + integrity sha512-A57th6YRG7oR3cq/yt/Y84MvGgE0eJG2F1JLhKuyG+jFxEgrd/HAMJatiFtmOiZurz+0DkrvbheCLaV5f2JfjA== + dependencies: + "@babel/helper-plugin-utils" "^7.0.0" + "@babel/plugin-proposal-unicode-property-regex" "^7.4.4" + "@babel/plugin-transform-dotall-regex" "^7.4.4" + "@babel/types" "^7.4.4" + esutils "^2.0.2" + "@babel/preset-modules@^0.1.4": version "0.1.4" resolved "https://registry.yarnpkg.com/@babel/preset-modules/-/preset-modules-0.1.4.tgz#362f2b68c662842970fdb5e254ffc8fc1c2e415e" @@ -1501,6 +2219,13 @@ "@babel/helper-validator-option" "^7.16.7" "@babel/plugin-transform-typescript" "^7.16.7" +"@babel/runtime@7.11.2": + version "7.11.2" + resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.11.2.tgz#f549c13c754cc40b87644b9fa9f09a6a95fe0736" + integrity sha512-TeWkU52so0mPtDcaCTxNBI/IHiz0pZgr8VEFqXFtZWpYD08ZB6FaSwVAS8MKRQAP3bYKiVjwysOJgMFY28o6Tw== + dependencies: + regenerator-runtime "^0.13.4" + "@babel/runtime@7.12.18": version "7.12.18" resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.12.18.tgz#af137bd7e7d9705a412b3caaf991fe6aaa97831b" @@ -1522,6 +2247,24 @@ dependencies: regenerator-runtime "^0.13.4" +"@babel/template@7.10.4": + version "7.10.4" + resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.10.4.tgz#3251996c4200ebc71d1a8fc405fba940f36ba278" + integrity sha512-ZCjD27cGJFUB6nmCB1Enki3r+L5kJveX9pq1SvAUKoICy6CZ9yD8xO086YXdYhvNjBdnekm4ZnaP5yC8Cs/1tA== + dependencies: + "@babel/code-frame" "^7.10.4" + "@babel/parser" "^7.10.4" + "@babel/types" "^7.10.4" + +"@babel/template@^7.10.4", "@babel/template@^7.16.7", "@babel/template@^7.3.3": + version "7.16.7" + resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.16.7.tgz#8d126c8701fde4d66b264b3eba3d96f07666d155" + integrity sha512-I8j/x8kHUrbYRTUxXrrMbfCa7jxkE7tZre39x3kjr9hvI82cK1FfqLygotcWN5kdPGWcLdWMHpSBavse5tWw3w== + dependencies: + "@babel/code-frame" "^7.16.7" + "@babel/parser" "^7.16.7" + "@babel/types" "^7.16.7" + "@babel/template@^7.12.13", "@babel/template@^7.15.4", "@babel/template@^7.4.0": version "7.15.4" resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.15.4.tgz#51898d35dcf3faa670c4ee6afcfd517ee139f194" @@ -1540,15 +2283,6 @@ "@babel/parser" "^7.16.0" "@babel/types" "^7.16.0" -"@babel/template@^7.16.7", "@babel/template@^7.3.3": - version "7.16.7" - resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.16.7.tgz#8d126c8701fde4d66b264b3eba3d96f07666d155" - integrity sha512-I8j/x8kHUrbYRTUxXrrMbfCa7jxkE7tZre39x3kjr9hvI82cK1FfqLygotcWN5kdPGWcLdWMHpSBavse5tWw3w== - dependencies: - "@babel/code-frame" "^7.16.7" - "@babel/parser" "^7.16.7" - "@babel/types" "^7.16.7" - "@babel/traverse@^7.1.6", "@babel/traverse@^7.13.0", "@babel/traverse@^7.13.13", "@babel/traverse@^7.4.3", "@babel/traverse@^7.4.5", "@babel/traverse@^7.7.0": version "7.13.13" resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.13.13.tgz#39aa9c21aab69f74d948a486dd28a2dbdbf5114d" @@ -1563,6 +2297,22 @@ debug "^4.1.0" globals "^11.1.0" +"@babel/traverse@^7.11.0", "@babel/traverse@^7.16.8", "@babel/traverse@^7.17.3", "@babel/traverse@^7.17.9", "@babel/traverse@^7.7.2": + version "7.17.9" + resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.17.9.tgz#1f9b207435d9ae4a8ed6998b2b82300d83c37a0d" + integrity sha512-PQO8sDIJ8SIwipTPiR71kJQCKQYB5NGImbOviK8K+kg5xkNSYXLBupuX9QhatFowrsvo9Hj8WgArg3W7ijNAQw== + dependencies: + "@babel/code-frame" "^7.16.7" + "@babel/generator" "^7.17.9" + "@babel/helper-environment-visitor" "^7.16.7" + "@babel/helper-function-name" "^7.17.9" + "@babel/helper-hoist-variables" "^7.16.7" + "@babel/helper-split-export-declaration" "^7.16.7" + "@babel/parser" "^7.17.9" + "@babel/types" "^7.17.0" + debug "^4.1.0" + globals "^11.1.0" + "@babel/traverse@^7.15.4": version "7.15.4" resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.15.4.tgz#ff8510367a144bfbff552d9e18e28f3e2889c22d" @@ -1609,22 +2359,6 @@ debug "^4.1.0" globals "^11.1.0" -"@babel/traverse@^7.17.3", "@babel/traverse@^7.17.9", "@babel/traverse@^7.7.2": - version "7.17.9" - resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.17.9.tgz#1f9b207435d9ae4a8ed6998b2b82300d83c37a0d" - integrity sha512-PQO8sDIJ8SIwipTPiR71kJQCKQYB5NGImbOviK8K+kg5xkNSYXLBupuX9QhatFowrsvo9Hj8WgArg3W7ijNAQw== - dependencies: - "@babel/code-frame" "^7.16.7" - "@babel/generator" "^7.17.9" - "@babel/helper-environment-visitor" "^7.16.7" - "@babel/helper-function-name" "^7.17.9" - "@babel/helper-hoist-variables" "^7.16.7" - "@babel/helper-split-export-declaration" "^7.16.7" - "@babel/parser" "^7.17.9" - "@babel/types" "^7.17.0" - debug "^4.1.0" - globals "^11.1.0" - "@babel/types@7.8.3": version "7.8.3" resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.8.3.tgz#5a383dffa5416db1b73dedffd311ffd0788fb31c" @@ -1643,6 +2377,14 @@ lodash "^4.17.19" to-fast-properties "^2.0.0" +"@babel/types@^7.10.4", "@babel/types@^7.11.0", "@babel/types@^7.17.0", "@babel/types@^7.3.3": + version "7.17.0" + resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.17.0.tgz#a826e368bccb6b3d84acd76acad5c0d87342390b" + integrity sha512-TmKSNO4D5rzhL5bjWFcVHHLETzfQ/AmbKpKPOSjlP0WoHZ6L911fgoOKY4Alp/emzG4cHJdyN49zpgkbXFEHHw== + dependencies: + "@babel/helper-validator-identifier" "^7.16.7" + to-fast-properties "^2.0.0" + "@babel/types@^7.15.4": version "7.15.6" resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.15.6.tgz#99abdc48218b2881c058dd0a7ab05b99c9be758f" @@ -1667,14 +2409,6 @@ "@babel/helper-validator-identifier" "^7.16.7" to-fast-properties "^2.0.0" -"@babel/types@^7.17.0", "@babel/types@^7.3.3": - version "7.17.0" - resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.17.0.tgz#a826e368bccb6b3d84acd76acad5c0d87342390b" - integrity sha512-TmKSNO4D5rzhL5bjWFcVHHLETzfQ/AmbKpKPOSjlP0WoHZ6L911fgoOKY4Alp/emzG4cHJdyN49zpgkbXFEHHw== - dependencies: - "@babel/helper-validator-identifier" "^7.16.7" - to-fast-properties "^2.0.0" - "@bcoe/v8-coverage@^0.2.3": version "0.2.3" resolved "https://registry.yarnpkg.com/@bcoe/v8-coverage/-/v8-coverage-0.2.3.tgz#75a2e8b51cb758a7553d6804a5932d7aace75c39" @@ -1785,6 +2519,11 @@ minimatch "^3.0.4" strip-json-comments "^3.1.1" +"@gar/promisify@^1.0.1": + version "1.1.3" + resolved "https://registry.yarnpkg.com/@gar/promisify/-/promisify-1.1.3.tgz#555193ab2e3bb3b6adc3d551c9c030d9e860daf6" + integrity sha512-k2Ty1JcVojjJFwrg/ThKi2ujJ7XNLYaFGNB/bWT9wGR+oSMJHMa5w+CUq6p/pVrKeNNgA7pCqEcjSnHVoqJQFw== + "@glimmer/component@~1.0.0": version "1.0.4" resolved "https://registry.yarnpkg.com/@glimmer/component/-/component-1.0.4.tgz#1c85a5181615a6647f6acfaaed68e28ad7e9626e" @@ -2388,6 +3127,17 @@ "@jridgewell/resolve-uri" "^3.0.3" "@jridgewell/sourcemap-codec" "^1.4.10" +"@jsdevtools/coverage-istanbul-loader@3.0.5": + version "3.0.5" + resolved "https://registry.yarnpkg.com/@jsdevtools/coverage-istanbul-loader/-/coverage-istanbul-loader-3.0.5.tgz#2a4bc65d0271df8d4435982db4af35d81754ee26" + integrity sha512-EUCPEkaRPvmHjWAAZkWMT7JDzpw7FKB00WTISaiXsbNOd5hCHg77XLA8sLYLFDo1zepYLo2w7GstN8YBqRXZfA== + dependencies: + convert-source-map "^1.7.0" + istanbul-lib-instrument "^4.0.3" + loader-utils "^2.0.0" + merge-source-map "^1.1.0" + schema-utils "^2.7.0" + "@lerna/add@3.13.3": version "3.13.3" resolved "https://registry.yarnpkg.com/@lerna/add/-/add-3.13.3.tgz#f4c1674839780e458f0426d4f7b6d0a77b9a2ae9" @@ -3049,6 +3799,15 @@ resolved "https://registry.yarnpkg.com/@next/react-refresh-utils/-/react-refresh-utils-10.1.3.tgz#65b3e1b9846c02452787fde1d54ad9c54b506dbd" integrity sha512-P4GJZuLKfD/o42JvGZ/xP4Hxg68vd3NeZxOLqIuQKFjjaYgC2IrO+lE5PTwGmRkytjfprJC+9j7Jss/xQAS6QA== +"@ngtools/webpack@10.2.4": + version "10.2.4" + resolved "https://registry.yarnpkg.com/@ngtools/webpack/-/webpack-10.2.4.tgz#6060970d1de053b5c33f5dd649ffb9f34e6b74a7" + integrity sha512-7rnGrd0TlnAHwOSwvKjKuD+/vwPEP2aVwD9ZnvWYafQFpLYQj+9TYOBj+nbg2l4PCRx5ByYy7xPKnu88GX5/lw== + dependencies: + "@angular-devkit/core" "10.2.4" + enhanced-resolve "4.3.0" + webpack-sources "1.4.3" + "@nodelib/fs.scandir@2.1.4": version "2.1.4" resolved "https://registry.yarnpkg.com/@nodelib/fs.scandir/-/fs.scandir-2.1.4.tgz#d4b3549a5db5de2683e0c1071ab4f140904bbf69" @@ -3075,6 +3834,22 @@ "@nodelib/fs.scandir" "2.1.4" fastq "^1.6.0" +"@npmcli/fs@^1.0.0": + version "1.1.1" + resolved "https://registry.yarnpkg.com/@npmcli/fs/-/fs-1.1.1.tgz#72f719fe935e687c56a4faecf3c03d06ba593257" + integrity sha512-8KG5RD0GVP4ydEzRn/I4BNDuxDtqVbOdm8675T49OIG/NGhaK0pjPX7ZcDlvKYbA+ulvVK3ztfcF4uBdOxuJbQ== + dependencies: + "@gar/promisify" "^1.0.1" + semver "^7.3.5" + +"@npmcli/move-file@^1.0.1": + version "1.1.2" + resolved "https://registry.yarnpkg.com/@npmcli/move-file/-/move-file-1.1.2.tgz#1a82c3e372f7cae9253eb66d72543d6b8685c674" + integrity sha512-1SUf/Cg2GzGDyaf15aR9St9TWlb+XvbZXWpDx8YKs7MLzMH/BCeopv+y9vzrzgkfykCGuWOlSu3mZhj2+FQcrg== + dependencies: + mkdirp "^1.0.4" + rimraf "^3.0.2" + "@octokit/auth-token@^2.4.0": version "2.4.5" resolved "https://registry.yarnpkg.com/@octokit/auth-token/-/auth-token-2.4.5.tgz#568ccfb8cb46f36441fac094ce34f7a875b197f3" @@ -3345,6 +4120,19 @@ resolved "https://registry.yarnpkg.com/@protobufjs/utf8/-/utf8-1.1.0.tgz#a777360b5b39a1a2e5106f8e858f2fd2d060c570" integrity sha1-p3c2C1s5oaLlEG+OhY8v0tBgxXA= +"@rollup/plugin-commonjs@^15.0.0": + version "15.1.0" + resolved "https://registry.yarnpkg.com/@rollup/plugin-commonjs/-/plugin-commonjs-15.1.0.tgz#1e7d076c4f1b2abf7e65248570e555defc37c238" + integrity sha512-xCQqz4z/o0h2syQ7d9LskIMvBSH4PX5PjYdpSSvgS+pQik3WahkQVNWg3D8XJeYjZoVWnIUQYDghuEMRGrmQYQ== + dependencies: + "@rollup/pluginutils" "^3.1.0" + commondir "^1.0.1" + estree-walker "^2.0.1" + glob "^7.1.6" + is-reference "^1.2.1" + magic-string "^0.25.7" + resolve "^1.17.0" + "@rollup/plugin-commonjs@^21.0.1": version "21.0.1" resolved "https://registry.yarnpkg.com/@rollup/plugin-commonjs/-/plugin-commonjs-21.0.1.tgz#1e57c81ae1518e4df0954d681c642e7d94588fee" @@ -3358,6 +4146,13 @@ magic-string "^0.25.7" resolve "^1.17.0" +"@rollup/plugin-json@^4.0.0": + version "4.1.0" + resolved "https://registry.yarnpkg.com/@rollup/plugin-json/-/plugin-json-4.1.0.tgz#54e09867ae6963c593844d8bd7a9c718294496f3" + integrity sha512-yfLbTdNS6amI/2OpmbiBoW12vngr5NW2jCJVZSBEz+H5KfUJZ2M7sDjk0U6GOOdCWFVScShte29o9NezJ53TPw== + dependencies: + "@rollup/pluginutils" "^3.0.8" + "@rollup/plugin-node-resolve@^13.1.3": version "13.1.3" resolved "https://registry.yarnpkg.com/@rollup/plugin-node-resolve/-/plugin-node-resolve-13.1.3.tgz#2ed277fb3ad98745424c1d2ba152484508a92d79" @@ -3370,6 +4165,18 @@ is-module "^1.0.0" resolve "^1.19.0" +"@rollup/plugin-node-resolve@^9.0.0": + version "9.0.0" + resolved "https://registry.yarnpkg.com/@rollup/plugin-node-resolve/-/plugin-node-resolve-9.0.0.tgz#39bd0034ce9126b39c1699695f440b4b7d2b62e6" + integrity sha512-gPz+utFHLRrd41WMP13Jq5mqqzHL3OXrfj3/MkSyB6UBIcuNt9j60GCbarzMzdf1VHFpOxfQh/ez7wyadLMqkg== + dependencies: + "@rollup/pluginutils" "^3.1.0" + "@types/resolve" "1.17.1" + builtin-modules "^3.1.0" + deepmerge "^4.2.2" + is-module "^1.0.0" + resolve "^1.17.0" + "@rollup/plugin-replace@^3.0.1": version "3.0.1" resolved "https://registry.yarnpkg.com/@rollup/plugin-replace/-/plugin-replace-3.0.1.tgz#f774550f482091719e52e9f14f67ffc0046a883d" @@ -3378,7 +4185,7 @@ "@rollup/pluginutils" "^3.1.0" magic-string "^0.25.7" -"@rollup/pluginutils@^3.1.0": +"@rollup/pluginutils@^3.0.8", "@rollup/pluginutils@^3.0.9", "@rollup/pluginutils@^3.1.0": version "3.1.0" resolved "https://registry.yarnpkg.com/@rollup/pluginutils/-/pluginutils-3.1.0.tgz#706b4524ee6dc8b103b3c995533e5ad680c02b9b" integrity sha512-GksZ6pr6TpIjHm8h9lSQ8pi8BE9VeubNT0OMJ3B5uZJ8pz73NPiqOtCog/x2/QzM1ENChPKxMDhiQuRHsqc+lg== @@ -3395,6 +4202,29 @@ estree-walker "^2.0.1" picomatch "^2.2.2" +"@schematics/angular@10.2.4": + version "10.2.4" + resolved "https://registry.yarnpkg.com/@schematics/angular/-/angular-10.2.4.tgz#3b99b9da572b57381d221e2008804e6bb9c98b82" + integrity sha512-irU3cnamfd5Hgy1B6oY7oweApJHhVaD2oYPq0NfI+F14JalERO+DGO0Tq3MWmEGn32tLQPv9fwM5O8EElEp9pA== + dependencies: + "@angular-devkit/core" "10.2.4" + "@angular-devkit/schematics" "10.2.4" + jsonc-parser "2.3.0" + +"@schematics/update@0.1002.4": + version "0.1002.4" + resolved "https://registry.yarnpkg.com/@schematics/update/-/update-0.1002.4.tgz#e8f5ff82d308f72fd521abd88316f7b0ccd296c1" + integrity sha512-qnDn3SSMmolfzWpj8CTAoC/TSPe43azKPYLR5r76GkRvuUbwr/dQEj92wu59twjGcsmjF54qcG4fGaxMndUn3Q== + dependencies: + "@angular-devkit/core" "10.2.4" + "@angular-devkit/schematics" "10.2.4" + "@yarnpkg/lockfile" "1.1.0" + ini "1.3.6" + npm-package-arg "^8.0.0" + pacote "9.5.12" + semver "7.3.2" + semver-intersect "1.4.0" + "@sentry/cli@^1.73.0": version "1.73.0" resolved "https://registry.yarnpkg.com/@sentry/cli/-/cli-1.73.0.tgz#0d0bce913e0060ae192741c6693c57e50078c886" @@ -4341,6 +5171,15 @@ "@types/source-list-map" "*" source-map "^0.7.3" +"@types/webpack-sources@^0.1.5": + version "0.1.9" + resolved "https://registry.yarnpkg.com/@types/webpack-sources/-/webpack-sources-0.1.9.tgz#da69b06eb34f6432e6658acb5a6893c55d983920" + integrity sha512-bvzMnzqoK16PQIC8AYHNdW45eREJQMd6WG/msQWX5V2+vZmODCOPb4TJcbgRljTZZTwTM4wUMcsI8FftNA7new== + dependencies: + "@types/node" "*" + "@types/source-list-map" "*" + source-map "^0.6.1" + "@types/webpack@^4.41.31": version "4.41.31" resolved "https://registry.yarnpkg.com/@types/webpack/-/webpack-4.41.31.tgz#c35f252a3559ddf9c85c0d8b0b42019025e581aa" @@ -4758,6 +5597,11 @@ tslib "^2.3.1" upath2 "^3.1.12" +"@yarnpkg/lockfile@1.1.0": + version "1.1.0" + resolved "https://registry.yarnpkg.com/@yarnpkg/lockfile/-/lockfile-1.1.0.tgz#e77a97fbd345b76d83245edcd17d393b1b41fb31" + integrity sha512-GpSwvyXOcOOlV70vbnzjj4fW5xW/FdUF6nQEt1ENy7m4ZCczi1+/buVUPAqmGfqznsORNFzUMjctTIp8a9tuCQ== + JSONStream@^1.0.4, JSONStream@^1.3.4: version "1.3.5" resolved "https://registry.yarnpkg.com/JSONStream/-/JSONStream-1.3.5.tgz#3208c1f08d3a4d99261ab64f92302bc15e111ca0" @@ -4862,6 +5706,14 @@ acorn@^8.4.1: resolved "https://registry.yarnpkg.com/acorn/-/acorn-8.6.0.tgz#e3692ba0eb1a0c83eaa4f37f5fa7368dd7142895" integrity sha512-U1riIR+lBSNi3IbxtaHOIKdH8sLFv3NYfNv8sg7ZsNhcfl4HF2++BfqqrNAxoCLQW1iiylOj76ecnaUxz+z9yw== +adjust-sourcemap-loader@3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/adjust-sourcemap-loader/-/adjust-sourcemap-loader-3.0.0.tgz#5ae12fb5b7b1c585e80bbb5a63ec163a1a45e61e" + integrity sha512-YBrGyT2/uVQ/c6Rr+t6ZJXniY03YtHGMJQYal368burRGYKqhx9qGTWqcBU5s1CwYY9E/ri63RYyG1IacMZtqw== + dependencies: + loader-utils "^2.0.0" + regex-parser "^2.2.11" + after@0.8.2: version "0.8.2" resolved "https://registry.yarnpkg.com/after/-/after-0.8.2.tgz#fedb394f9f0e02aa9768e702bda23b505fae7e1f" @@ -4879,6 +5731,14 @@ agentkeepalive@^3.4.1: dependencies: humanize-ms "^1.2.1" +aggregate-error@^3.0.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/aggregate-error/-/aggregate-error-3.1.0.tgz#92670ff50f5359bdb7a3e0d40d0ec30c5737687a" + integrity sha512-4I7Td01quW/RpocfNayFdFVk1qSuoh0E7JrbRJ16nH01HhKFQ88INq9Sd+nd72zqRySlr9BmDA8xlEJ6vJMrYA== + dependencies: + clean-stack "^2.0.0" + indent-string "^4.0.0" + ajv-errors@^1.0.0: version "1.0.1" resolved "https://registry.yarnpkg.com/ajv-errors/-/ajv-errors-1.0.1.tgz#f35986aceb91afadec4102fbd85014950cefa64d" @@ -4889,6 +5749,16 @@ ajv-keywords@^3.1.0, ajv-keywords@^3.4.1, ajv-keywords@^3.5.2: resolved "https://registry.yarnpkg.com/ajv-keywords/-/ajv-keywords-3.5.2.tgz#31f29da5ab6e00d1c2d329acf7b5929614d5014d" integrity sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ== +ajv@6.12.4: + version "6.12.4" + resolved "https://registry.yarnpkg.com/ajv/-/ajv-6.12.4.tgz#0614facc4522127fa713445c6bfd3ebd376e2234" + integrity sha512-eienB2c9qVQs2KWexhkrdMLVDoIQCz5KSeLxwg9Lzk4DOfBtIK9PQwwufcsn1jjGuf9WZmqPMbGxOzfcuphJCQ== + 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" + ajv@^6.1.0, ajv@^6.10.0, ajv@^6.10.2, ajv@^6.12.3, ajv@^6.12.4, ajv@^6.12.5: version "6.12.6" resolved "https://registry.yarnpkg.com/ajv/-/ajv-6.12.6.tgz#baf5a62e802b07d977034586f8c3baf5adf26df4" @@ -4944,6 +5814,11 @@ ansi-colors@3.2.3: resolved "https://registry.yarnpkg.com/ansi-colors/-/ansi-colors-3.2.3.tgz#57d35b8686e851e2cc04c403f1c00203976a1813" integrity sha512-LEHHyuhlPY3TmuUYMh2oz89lTShfvgbmzaBcxve9t/9Wuy7Dwf4yoAKcND7KFT1HAQfqZ12qtc+DUrBMeKF9nw== +ansi-colors@4.1.1, ansi-colors@^4.1.1: + version "4.1.1" + resolved "https://registry.yarnpkg.com/ansi-colors/-/ansi-colors-4.1.1.tgz#cbb9ae256bf750af1eab344f229aa27fe94ba348" + integrity sha512-JoX0apGbHaUJBNl6yF+p6JAFYZ666/hhCGKN5t9QFjbJQKUU/g8MNbFDbvfrgKXvI1QpZplPOnwIo99lX/AAmA== + ansi-colors@^1.0.1: version "1.1.0" resolved "https://registry.yarnpkg.com/ansi-colors/-/ansi-colors-1.1.0.tgz#6374b4dd5d4718ff3ce27a671a3b1cad077132a9" @@ -4951,10 +5826,10 @@ ansi-colors@^1.0.1: dependencies: ansi-wrap "^0.1.0" -ansi-colors@^4.1.1: - version "4.1.1" - resolved "https://registry.yarnpkg.com/ansi-colors/-/ansi-colors-4.1.1.tgz#cbb9ae256bf750af1eab344f229aa27fe94ba348" - integrity sha512-JoX0apGbHaUJBNl6yF+p6JAFYZ666/hhCGKN5t9QFjbJQKUU/g8MNbFDbvfrgKXvI1QpZplPOnwIo99lX/AAmA== +ansi-colors@^3.0.0: + version "3.2.4" + resolved "https://registry.yarnpkg.com/ansi-colors/-/ansi-colors-3.2.4.tgz#e3a3da4bfbae6c86a9c285625de124a234026fbf" + integrity sha512-hHUXGagefjN2iRrID63xckIvotOXOojhQKWIPUZ4mNUZ9nLZW+7FMNoE1lOkEhNWYsx/7ysGIuJYCiMAA9FnrA== ansi-escapes@^3.2.0: version "3.2.0" @@ -4968,7 +5843,7 @@ ansi-escapes@^4.2.1: dependencies: type-fest "^0.21.3" -ansi-html@^0.0.7: +ansi-html@0.0.7, ansi-html@^0.0.7: version "0.0.7" resolved "https://registry.yarnpkg.com/ansi-html/-/ansi-html-0.0.7.tgz#813584021962a9e9e6fd039f940d12f56ca7859e" integrity sha1-gTWEAhliqenm/QOflA0S9WynhZ4= @@ -5054,7 +5929,7 @@ anymatch@^2.0.0: micromatch "^3.1.4" normalize-path "^2.1.1" -anymatch@^3.0.0, anymatch@^3.0.3, anymatch@~3.1.1: +anymatch@^3.0.0, anymatch@^3.0.3, anymatch@~3.1.1, anymatch@~3.1.2: version "3.1.2" resolved "https://registry.yarnpkg.com/anymatch/-/anymatch-3.1.2.tgz#c0557c096af32f106198f4f4e2a383537e378716" integrity sha512-P43ePfOAIupkguHUycrc4qJ9kz8ZiuOUijaETwX7THt0Y/GNK7v0aa8rY816xWjZ7rJdA5XdMcpVFTKMq+RvWg== @@ -5112,6 +5987,11 @@ aria-query@^5.0.0: resolved "https://registry.yarnpkg.com/aria-query/-/aria-query-5.0.0.tgz#210c21aaf469613ee8c9a62c7f86525e058db52c" integrity sha512-V+SM7AbUwJ+EBnB8+DXs0hPZHO0W6pqBcc0dW90OwtVG02PswOu/teuARoLQjdDOH+t9pJgGnW5/Qmouf3gPJg== +arity-n@^1.0.4: + version "1.0.4" + resolved "https://registry.yarnpkg.com/arity-n/-/arity-n-1.0.4.tgz#d9e76b11733e08569c0847ae7b39b2860b30b745" + integrity sha1-2edrEXM+CFacCEeuezmyhgswt0U= + arr-diff@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/arr-diff/-/arr-diff-4.0.0.tgz#d6461074febfec71e7e15235761a329a5dc7c520" @@ -5152,6 +6032,11 @@ array-flatten@1.1.1: resolved "https://registry.yarnpkg.com/array-flatten/-/array-flatten-1.1.1.tgz#9a5f699051b1e7073328f2a008968b64ea2955d2" integrity sha1-ml9pkFGx5wczKPKgCJaLZOopVdI= +array-flatten@^2.1.0: + version "2.1.2" + resolved "https://registry.yarnpkg.com/array-flatten/-/array-flatten-2.1.2.tgz#24ef80a28c1a893617e2149b0c6d0d788293b099" + integrity sha512-hNfzcOV8W4NdualtqBFPyVO+54DSJuZGY9qT4pRroB6S9e3iiido2ISIC5h9R2sPJ8H3FHCIiEnsv1lPXO3KtQ== + array-from@^2.1.1: version "2.1.1" resolved "https://registry.yarnpkg.com/array-from/-/array-from-2.1.1.tgz#cfe9d8c26628b9dc5aecc62a9f5d8f1f352c1195" @@ -5352,6 +6237,11 @@ async-each@^1.0.1: resolved "https://registry.yarnpkg.com/async-each/-/async-each-1.0.3.tgz#b727dbf87d7651602f06f4d4ac387f47d91b0cbf" integrity sha512-z/WhQ5FPySLdvREByI2vZiTWwCnF0moMJ1hK9YQwDTHKh6I7/uSckMetoRGb5UBZPC1z0jlw+n/XCgjeH7y1AQ== +async-limiter@~1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/async-limiter/-/async-limiter-1.0.1.tgz#dd379e94f0db8310b08291f9d64c3209766617fd" + integrity sha512-csOlWGAcRFJaI6m+F2WKdnMKr4HhdhFVBk0H/QbJFMCr+uO2kwohwXQPxw/9OCxp05r5ghVBFSyioixx3gfkNQ== + async-mutex@^0.3.2: version "0.3.2" resolved "https://registry.yarnpkg.com/async-mutex/-/async-mutex-0.3.2.tgz#1485eda5bda1b0ec7c8df1ac2e815757ad1831df" @@ -5416,6 +6306,32 @@ atob@^2.1.2: resolved "https://registry.yarnpkg.com/atob/-/atob-2.1.2.tgz#6d9517eb9e030d2436666651e86bd9f6f13533c9" integrity sha512-Wm6ukoaOGJi/73p/cl2GvLjTI5JM1k/O14isD73YML8StrH/7/lRFgmg8nICZgD3bZZvjwCGxtMOD3wWNAu8cg== +autoprefixer@9.8.6: + version "9.8.6" + resolved "https://registry.yarnpkg.com/autoprefixer/-/autoprefixer-9.8.6.tgz#3b73594ca1bf9266320c5acf1588d74dea74210f" + integrity sha512-XrvP4VVHdRBCdX1S3WXVD8+RyG9qeb1D5Sn1DeLiG2xfSpzellk5k54xbUERJ3M5DggQxes39UGOTP8CFrEGbg== + dependencies: + browserslist "^4.12.0" + caniuse-lite "^1.0.30001109" + colorette "^1.2.1" + normalize-range "^0.1.2" + num2fraction "^1.2.2" + postcss "^7.0.32" + postcss-value-parser "^4.1.0" + +autoprefixer@^9.6.5: + version "9.8.8" + resolved "https://registry.yarnpkg.com/autoprefixer/-/autoprefixer-9.8.8.tgz#fd4bd4595385fa6f06599de749a4d5f7a474957a" + integrity sha512-eM9d/swFopRt5gdJ7jrpCwgvEMIayITpojhkkSMRsFHYuH5bkSQ4p/9qTEHtmNudUZh22Tehu7I6CxAW0IXTKA== + dependencies: + browserslist "^4.12.0" + caniuse-lite "^1.0.30001109" + normalize-range "^0.1.2" + num2fraction "^1.2.2" + picocolors "^0.2.1" + postcss "^7.0.32" + postcss-value-parser "^4.1.0" + available-typed-arrays@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/available-typed-arrays/-/available-typed-arrays-1.0.2.tgz#6b098ca9d8039079ee3f77f7b783c4480ba513f5" @@ -5640,6 +6556,17 @@ babel-jest@^27.5.1: graceful-fs "^4.2.9" slash "^3.0.0" +babel-loader@8.1.0: + version "8.1.0" + resolved "https://registry.yarnpkg.com/babel-loader/-/babel-loader-8.1.0.tgz#c611d5112bd5209abe8b9fa84c3e4da25275f1c3" + integrity sha512-7q7nC1tYOrqvUrN3LQK4GwSk/TQorZSOlO9C+RZDZpODgyN4ZlCqE5q9cDsyWOliN+aU9B4JX01xK9eJXowJLw== + dependencies: + find-cache-dir "^2.1.0" + loader-utils "^1.4.0" + mkdirp "^0.5.3" + pify "^4.0.1" + schema-utils "^2.6.5" + babel-loader@^8.0.6: version "8.2.2" resolved "https://registry.yarnpkg.com/babel-loader/-/babel-loader-8.2.2.tgz#9363ce84c10c9a40e6c753748e1441b60c8a0b81" @@ -6276,6 +7203,11 @@ basic-auth@~2.0.1: dependencies: safe-buffer "5.1.2" +batch@0.6.1: + version "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" @@ -6414,6 +7346,18 @@ body@^5.1.0: raw-body "~1.1.0" safe-json-parse "~1.0.1" +bonjour@^3.5.0: + version "3.5.0" + resolved "https://registry.yarnpkg.com/bonjour/-/bonjour-3.5.0.tgz#8e890a183d8ee9a2393b3844c691a42bcf7bc9f5" + integrity sha1-jokKGD2O6aI5OzhExpGkK897yfU= + dependencies: + array-flatten "^2.1.0" + deep-equal "^1.0.1" + dns-equal "^1.0.0" + dns-txt "^2.0.2" + multicast-dns "^6.0.1" + multicast-dns-service-types "^1.1.0" + boolbase@^1.0.0, boolbase@~1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/boolbase/-/boolbase-1.0.0.tgz#68dff5fbe60c51eb37725ea9e3ed310dcc1e776e" @@ -7124,6 +8068,17 @@ browserslist@^4.0.0, browserslist@^4.16.3, browserslist@^4.16.6: escalade "^3.1.1" node-releases "^1.1.75" +browserslist@^4.12.0, browserslist@^4.20.2, browserslist@^4.7.0, browserslist@^4.9.1: + version "4.20.2" + resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-4.20.2.tgz#567b41508757ecd904dab4d1c646c612cd3d4f88" + integrity sha512-CQOBCqp/9pDvDbx3xfMi+86pr4KXIf2FDkTTdeuYw8OxS9t898LA1Khq57gtufFILXpfgsSx5woNgsBgvGjpsA== + dependencies: + caniuse-lite "^1.0.30001317" + electron-to-chromium "^1.4.84" + escalade "^3.1.1" + node-releases "^2.0.2" + picocolors "^1.0.0" + browserslist@^4.14.5: version "4.18.1" resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-4.18.1.tgz#60d3920f25b6860eb917c6c7b185576f4d8b017f" @@ -7214,6 +8169,11 @@ buffer-from@^1.0.0: resolved "https://registry.yarnpkg.com/buffer-from/-/buffer-from-1.1.1.tgz#32713bc028f75c02fdb710d7c7bcec1f2c6070ef" integrity sha512-MQcXEUbCKtEo7bhqEs6560Hyd4XaovZlO/k9V3hjVUF/zwW7KBVdSK4gIt/bzwS9MbR5qob+F5jusZsb0YQK2A== +buffer-indexof@^1.0.0: + version "1.1.1" + resolved "https://registry.yarnpkg.com/buffer-indexof/-/buffer-indexof-1.1.1.tgz#52fabcc6a606d1a00302802648ef68f639da268c" + integrity sha512-4/rOEg86jivtPTeOUUT61jJO1Ya1TrR/OkqCSZDyq84WJh3LuuiphBYJN+fm5xufIk4XAFcEwte/8WzC8If/1g== + buffer-writer@2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/buffer-writer/-/buffer-writer-2.0.0.tgz#ce7eb81a38f7829db09c873f2fbb792c0c98ec04" @@ -7294,6 +8254,29 @@ bytes@3.1.2: resolved "https://registry.yarnpkg.com/bytes/-/bytes-3.1.2.tgz#8b0beeb98605adf1b128fa4386403c009e0221a5" integrity sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg== +cacache@15.0.5: + version "15.0.5" + resolved "https://registry.yarnpkg.com/cacache/-/cacache-15.0.5.tgz#69162833da29170d6732334643c60e005f5f17d0" + integrity sha512-lloiL22n7sOjEEXdL8NAjTgv9a1u43xICE9/203qonkZUCj5X1UEWIdf2/Y0d6QcCtMzbKQyhrcDbdvlZTs/+A== + dependencies: + "@npmcli/move-file" "^1.0.1" + chownr "^2.0.0" + fs-minipass "^2.0.0" + glob "^7.1.4" + infer-owner "^1.0.4" + lru-cache "^6.0.0" + minipass "^3.1.1" + minipass-collect "^1.0.2" + minipass-flush "^1.0.5" + minipass-pipeline "^1.2.2" + mkdirp "^1.0.3" + p-map "^4.0.0" + promise-inflight "^1.0.1" + rimraf "^3.0.2" + ssri "^8.0.0" + tar "^6.0.2" + unique-filename "^1.1.1" + cacache@^11.3.3: version "11.3.3" resolved "https://registry.yarnpkg.com/cacache/-/cacache-11.3.3.tgz#8bd29df8c6a718a6ebd2d010da4d7972ae3bbadc" @@ -7335,6 +8318,30 @@ cacache@^12.0.0, cacache@^12.0.2: unique-filename "^1.1.1" y18n "^4.0.0" +cacache@^15.0.4, cacache@^15.0.5: + version "15.3.0" + resolved "https://registry.yarnpkg.com/cacache/-/cacache-15.3.0.tgz#dc85380fb2f556fe3dda4c719bfa0ec875a7f1eb" + integrity sha512-VVdYzXEn+cnbXpFgWs5hTT7OScegHVmLhJIR8Ufqk3iFD6A6j5iSX1KuBTfNEv4tdJWE2PzA6IVFtcLC7fN9wQ== + dependencies: + "@npmcli/fs" "^1.0.0" + "@npmcli/move-file" "^1.0.1" + chownr "^2.0.0" + fs-minipass "^2.0.0" + glob "^7.1.4" + infer-owner "^1.0.4" + lru-cache "^6.0.0" + minipass "^3.1.1" + minipass-collect "^1.0.2" + minipass-flush "^1.0.5" + minipass-pipeline "^1.2.2" + mkdirp "^1.0.3" + p-map "^4.0.0" + promise-inflight "^1.0.1" + rimraf "^3.0.2" + ssri "^8.0.1" + tar "^6.0.2" + unique-filename "^1.1.1" + cache-base@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/cache-base/-/cache-base-1.0.1.tgz#0a7f46416831c8b662ee36fe4e7c59d76f666ab2" @@ -7441,6 +8448,11 @@ camelcase-keys@^6.2.2: map-obj "^4.0.0" quick-lru "^4.0.1" +camelcase@5.3.1, camelcase@^5.0.0, camelcase@^5.3.1: + version "5.3.1" + resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-5.3.1.tgz#e3c9b31569e106811df242f715725a1f4c494320" + integrity sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg== + camelcase@^2.0.0: version "2.1.1" resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-2.1.1.tgz#7c1d16d679a1bbe59ca02cacecfb011e201f5a1f" @@ -7451,12 +8463,7 @@ camelcase@^4.1.0: resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-4.1.0.tgz#d545635be1e33c542649c69173e5de6acfae34dd" integrity sha1-1UVjW+HjPFQmScaRc+Xeas+uNN0= -camelcase@^5.0.0, camelcase@^5.3.1: - version "5.3.1" - resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-5.3.1.tgz#e3c9b31569e106811df242f715725a1f4c494320" - integrity sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg== - -camelcase@^6.1.0: +camelcase@^6.0.0, camelcase@^6.1.0: version "6.3.0" resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-6.3.0.tgz#5685b95eb209ac9c0c177467778c9c84df58ba9a" integrity sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA== @@ -7488,6 +8495,11 @@ caniuse-lite@^1.0.0, caniuse-lite@^1.0.30000844, caniuse-lite@^1.0.30001173, can resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001257.tgz#150aaf649a48bee531104cfeda57f92ce587f6e5" integrity sha512-JN49KplOgHSXpIsVSF+LUyhD8PUp6xPpAXeRrrcBh4KBeP7W864jHn6RvzJgDlrReyeVjMFJL3PLpPvKIxlIHA== +caniuse-lite@^1.0.30001032, caniuse-lite@^1.0.30001109, caniuse-lite@^1.0.30001317: + version "1.0.30001332" + resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001332.tgz#39476d3aa8d83ea76359c70302eafdd4a1d727dd" + integrity sha512-10T30NYOEQtN6C11YGg411yebhvpnC6Z102+B95eAsN0oB6KUs01ivE8u+G6FMIRtIrVlYXhL+LUwQ3/hXwDWw== + caniuse-lite@^1.0.30001274: version "1.0.30001279" resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001279.tgz#eb06818da481ef5096a3b3760f43e5382ed6b0ce" @@ -7498,6 +8510,11 @@ caniuse-lite@^1.0.30001280: resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001286.tgz#3e9debad420419618cfdf52dc9b6572b28a8fff6" integrity sha512-zaEMRH6xg8ESMi2eQ3R4eZ5qw/hJiVsO/HlLwniIwErij0JDr9P+8V4dtx1l+kLq6j3yy8l8W4fst1lBnat5wQ== +canonical-path@1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/canonical-path/-/canonical-path-1.0.0.tgz#fcb470c23958def85081856be7a86e904f180d1d" + integrity sha512-feylzsbDxi1gPZ1IjystzIQZagYYLvfKrSuygUCgf7z6x790VEzze5QEkdSV1U58RA7Hi0+v6fv4K54atOzATg== + capture-exit@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/capture-exit/-/capture-exit-2.0.0.tgz#fb953bfaebeb781f62898239dabb426d08a509a4" @@ -7616,6 +8633,21 @@ chokidar@3.5.1, chokidar@^3.0.2, chokidar@^3.3.1, chokidar@^3.4.1, chokidar@^3.5 optionalDependencies: fsevents "~2.3.1" +"chokidar@>=2.0.0 <4.0.0", "chokidar@>=3.0.0 <4.0.0", chokidar@^3.0.0, chokidar@^3.2.1: + version "3.5.3" + resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-3.5.3.tgz#1cf37c8707b932bd1af1ae22c0432e2acd1903bd" + integrity sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw== + dependencies: + anymatch "~3.1.2" + braces "~3.0.2" + glob-parent "~5.1.2" + is-binary-path "~2.1.0" + is-glob "~4.0.1" + normalize-path "~3.0.0" + readdirp "~3.6.0" + optionalDependencies: + fsevents "~2.3.2" + chokidar@^2.1.8: version "2.1.8" resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-2.1.8.tgz#804b3a7b6a99358c3c5c61e71d8728f041cff917" @@ -7640,6 +8672,11 @@ chownr@^1.1.1, chownr@^1.1.2, chownr@^1.1.4: resolved "https://registry.yarnpkg.com/chownr/-/chownr-1.1.4.tgz#6fc9d7b42d32a583596337666e7d08084da2cc6b" integrity sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg== +chownr@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/chownr/-/chownr-2.0.0.tgz#15bfbe53d2eab4cf70f18a8cd68ebe5b3cb1dece" + integrity sha512-bIomtDF5KGpdogkLd9VspvFzk9KfpyyGlS8YFVZl7TGPBHL5snIOnxeshwVgPteQ9b4Eydl+pVbIyE1DcvCWgQ== + chrome-trace-event@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/chrome-trace-event/-/chrome-trace-event-1.0.2.tgz#234090ee97c7d4ad1a2c4beae27505deffc608a4" @@ -7675,6 +8712,11 @@ cipher-base@^1.0.0, cipher-base@^1.0.1, cipher-base@^1.0.3: inherits "^2.0.1" safe-buffer "^5.0.1" +circular-dependency-plugin@5.2.0: + version "5.2.0" + resolved "https://registry.yarnpkg.com/circular-dependency-plugin/-/circular-dependency-plugin-5.2.0.tgz#e09dbc2dd3e2928442403e2d45b41cea06bc0a93" + integrity sha512-7p4Kn/gffhQaavNfyDFg7LS5S/UT1JAjyGd4UqR2+jzoYF02eDkj0Ec3+48TsIa4zghjLY87nQHIh/ecK9qLdw== + cjs-module-lexer@^1.0.0: version "1.2.2" resolved "https://registry.yarnpkg.com/cjs-module-lexer/-/cjs-module-lexer-1.2.2.tgz#9f84ba3244a512f3a54e5277e8eef4c489864e40" @@ -7724,7 +8766,7 @@ clean-css@^5.2.2: dependencies: source-map "~0.6.0" -clean-stack@^2.2.0: +clean-stack@^2.0.0, clean-stack@^2.2.0: version "2.2.0" resolved "https://registry.yarnpkg.com/clean-stack/-/clean-stack-2.2.0.tgz#ee8472dbb129e727b31e8a10a427dee9dfe4008b" integrity sha512-4diC9HaTE+KRAMWhDhrGOECgWZxoevMc5TlkObMqNSsVU62PYzXZ/SMTjzyGAFF1YusgxGcSWTEXBhp0CPwQ1A== @@ -7753,6 +8795,11 @@ cli-spinners@^2.0.0, cli-spinners@^2.5.0: resolved "https://registry.yarnpkg.com/cli-spinners/-/cli-spinners-2.6.0.tgz#36c7dc98fb6a9a76bd6238ec3f77e2425627e939" integrity sha512-t+4/y50K/+4xcCRosKkA7W4gTr1MySvLV0q+PxmG7FJ5g+66ChKurYjxBCjHggHH3HA5Hh9cy+lcUGWDqVH+4Q== +cli-spinners@^2.4.0: + version "2.6.1" + resolved "https://registry.yarnpkg.com/cli-spinners/-/cli-spinners-2.6.1.tgz#adc954ebe281c37a6319bfa401e6dd2488ffb70d" + integrity sha512-x/5fWmGMnbKQAaNwN+UZlV79qBLM9JFnJuJ03gIi5whrob0xV0ofNVHy9DhwGdsMJQc2OKv0oGmLzvaqvAVv+g== + cli-table3@^0.5.1: version "0.5.1" resolved "https://registry.yarnpkg.com/cli-table3/-/cli-table3-0.5.1.tgz#0252372d94dfc40dbd8df06005f48f31f656f202" @@ -8007,7 +9054,7 @@ commander@^5.1.0: resolved "https://registry.yarnpkg.com/commander/-/commander-5.1.0.tgz#46abbd1652f8e059bddaef99bbdcb2ad9cf179ae" integrity sha512-P0CysNDQ7rtVw4QIQtm+MRxV66vKFSvlsQvGYXZWR3qFU0jlMKHZZZgw8e+8DSah4UDKMqnknRDQz+xuQXQ/Zg== -commander@^6.2.0, commander@^6.2.1: +commander@^6.0.0, commander@^6.2.0, commander@^6.2.1: version "6.2.1" resolved "https://registry.yarnpkg.com/commander/-/commander-6.2.1.tgz#0792eb682dfbc325999bb2b84fddddba110ac73c" integrity sha512-U7VdrJFnJgo4xjrHpTzu0yrHPGImdsmD95ZlgYSEajAn2JKzDhDTPG9kBTefmObL2w/ngeZnilk+OV9CG3d7UA== @@ -8065,6 +9112,13 @@ component-inherit@0.0.3: resolved "https://registry.yarnpkg.com/component-inherit/-/component-inherit-0.0.3.tgz#645fc4adf58b72b649d5cae65135619db26ff143" integrity sha1-ZF/ErfWLcrZJ1crmUTVhnbJv8UM= +compose-function@3.0.3: + version "3.0.3" + resolved "https://registry.yarnpkg.com/compose-function/-/compose-function-3.0.3.tgz#9ed675f13cc54501d30950a486ff6a7ba3ab185f" + integrity sha1-ntZ18TzFRQHTCVCkhv9qe6OrGF8= + dependencies: + arity-n "^1.0.4" + compressible@^2.0.12, compressible@~2.0.16: version "2.0.18" resolved "https://registry.yarnpkg.com/compressible/-/compressible-2.0.18.tgz#af53cca6b070d4c3c0750fbd77286a6d7cc46fba" @@ -8130,6 +9184,11 @@ configstore@^5.0.0, configstore@^5.0.1: write-file-atomic "^3.0.0" xdg-basedir "^4.0.0" +connect-history-api-fallback@^1.6.0: + version "1.6.0" + resolved "https://registry.yarnpkg.com/connect-history-api-fallback/-/connect-history-api-fallback-1.6.0.tgz#8b32089359308d111115d81cad3fceab888f97bc" + integrity sha512-e54B99q/OUoH64zYYRf3HBP5z24G38h5D3qXu23JGRoigpX5Ss4r9ZnDk3g0Z8uQC2x2lPaJ+UlWBc1ZWBWdLg== + connect@^3.6.6, connect@^3.7.0: version "3.7.0" resolved "https://registry.yarnpkg.com/connect/-/connect-3.7.0.tgz#5d49348910caa5e07a01800b030d0c35f20484f8" @@ -8292,6 +9351,11 @@ convert-source-map@1.7.0, convert-source-map@^1.4.0, convert-source-map@^1.5.1, dependencies: safe-buffer "~5.1.1" +convert-source-map@^0.3.3: + version "0.3.5" + resolved "https://registry.yarnpkg.com/convert-source-map/-/convert-source-map-0.3.5.tgz#f1d802950af7dd2631a1febe0596550c86ab3190" + integrity sha1-8dgClQr33SYxof6+BZZVDIarMZA= + convert-source-map@~1.1.0: version "1.1.3" resolved "https://registry.yarnpkg.com/convert-source-map/-/convert-source-map-1.1.3.tgz#4829c877e9fe49b3161f3bf3673888e204699860" @@ -8317,6 +9381,13 @@ cookie@^0.4.1, cookie@~0.4.1: resolved "https://registry.yarnpkg.com/cookie/-/cookie-0.4.1.tgz#afd713fe26ebd21ba95ceb61f9a8116e50a537d1" integrity sha512-ZwrFkGJxUR3EIoXtO+yVE69Eb7KlixbaeAWfBQB9vVsNn/o+Yw69gBWSSDK825hQNdN+wF8zELf3dFNl/kxkUA== +copy-anything@^2.0.1: + version "2.0.6" + resolved "https://registry.yarnpkg.com/copy-anything/-/copy-anything-2.0.6.tgz#092454ea9584a7b7ad5573062b2a87f5900fc480" + integrity sha512-1j20GZTsvKNkc4BY3NpMOM8tt///wY3FpIzozTOFO2ffuZcV61nojHXVKIy3WM+7ADCy5FVhdZYHYDdgTU0yJw== + dependencies: + is-what "^3.14.1" + copy-concurrently@^1.0.0: version "1.0.5" resolved "https://registry.yarnpkg.com/copy-concurrently/-/copy-concurrently-1.0.5.tgz#92297398cae34937fcafd6ec8139c18051f0b5e0" @@ -8339,6 +9410,31 @@ copy-descriptor@^0.1.0: resolved "https://registry.yarnpkg.com/copy-descriptor/-/copy-descriptor-0.1.1.tgz#676f6eb3c39997c2ee1ac3a924fd6124748f578d" integrity sha1-Z29us8OZl8LuGsOpJP1hJHSPV40= +copy-webpack-plugin@6.0.3: + version "6.0.3" + resolved "https://registry.yarnpkg.com/copy-webpack-plugin/-/copy-webpack-plugin-6.0.3.tgz#2b3d2bfc6861b96432a65f0149720adbd902040b" + integrity sha512-q5m6Vz4elsuyVEIUXr7wJdIdePWTubsqVbEMvf1WQnHGv0Q+9yPRu7MtYFPt+GBOXRav9lvIINifTQ1vSCs+eA== + dependencies: + cacache "^15.0.4" + fast-glob "^3.2.4" + find-cache-dir "^3.3.1" + glob-parent "^5.1.1" + globby "^11.0.1" + loader-utils "^2.0.0" + normalize-path "^3.0.0" + p-limit "^3.0.1" + schema-utils "^2.7.0" + serialize-javascript "^4.0.0" + webpack-sources "^1.4.3" + +core-js-compat@^3.6.2: + version "3.22.1" + resolved "https://registry.yarnpkg.com/core-js-compat/-/core-js-compat-3.22.1.tgz#47b9c5e79efbf13935f637449fa1cdec8cd9515f" + integrity sha512-CWbNqTluLMvZg1cjsQUbGiCM91dobSHKfDIyCoxuqxthdjGuUlaMbCsSehP3CBiVvG0C7P6UIrC1v0hgFE75jw== + dependencies: + browserslist "^4.20.2" + semver "7.0.0" + core-js-compat@^3.8.1, core-js-compat@^3.9.0: version "3.9.1" resolved "https://registry.yarnpkg.com/core-js-compat/-/core-js-compat-3.9.1.tgz#4e572acfe90aff69d76d8c37759d21a5c59bb455" @@ -8347,6 +9443,11 @@ core-js-compat@^3.8.1, core-js-compat@^3.9.0: browserslist "^4.16.3" semver "7.0.0" +core-js@3.6.4: + version "3.6.4" + resolved "https://registry.yarnpkg.com/core-js/-/core-js-3.6.4.tgz#440a83536b458114b9cb2ac1580ba377dc470647" + integrity sha512-4paDGScNgZP2IXXilaffL9X7968RuvwlkK3xWtZRVqgd8SYNiVKRJvkFd1aqqEuPfN7E68ZHEp9hDj6lHj4Hyw== + core-js@^1.0.0: version "1.2.7" resolved "https://registry.yarnpkg.com/core-js/-/core-js-1.2.7.tgz#652294c14651db28fa93bd2d5ff2983a4f08c636" @@ -8503,6 +9604,24 @@ css-declaration-sorter@^4.0.1: postcss "^7.0.1" timsort "^0.3.0" +css-loader@4.2.2: + version "4.2.2" + resolved "https://registry.yarnpkg.com/css-loader/-/css-loader-4.2.2.tgz#b668b3488d566dc22ebcf9425c5f254a05808c89" + integrity sha512-omVGsTkZPVwVRpckeUnLshPp12KsmMSLqYxs12+RzM9jRR5Y+Idn/tBffjXRvOE+qW7if24cuceFJqYR5FmGBg== + dependencies: + camelcase "^6.0.0" + cssesc "^3.0.0" + icss-utils "^4.1.1" + loader-utils "^2.0.0" + postcss "^7.0.32" + postcss-modules-extract-imports "^2.0.0" + postcss-modules-local-by-default "^3.0.3" + postcss-modules-scope "^2.2.0" + postcss-modules-values "^3.0.0" + postcss-value-parser "^4.1.0" + schema-utils "^2.7.0" + semver "^7.3.2" + css-loader@^5.1.1: version "5.2.0" resolved "https://registry.yarnpkg.com/css-loader/-/css-loader-5.2.0.tgz#a9ecda190500863673ce4434033710404efbff00" @@ -8537,6 +9656,13 @@ css-loader@^5.2.0: schema-utils "^3.0.0" semver "^7.3.5" +css-parse@~2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/css-parse/-/css-parse-2.0.0.tgz#a468ee667c16d81ccf05c58c38d2a97c780dbfd4" + integrity sha1-pGjuZnwW2BzPBcWMONKpfHgNv9Q= + dependencies: + css "^2.0.0" + css-select-base-adapter@^0.1.1: version "0.1.1" resolved "https://registry.yarnpkg.com/css-select-base-adapter/-/css-select-base-adapter-0.1.1.tgz#3b2ff4972cc362ab88561507a95408a1432135d7" @@ -8594,6 +9720,16 @@ css.escape@1.5.1: resolved "https://registry.yarnpkg.com/css.escape/-/css.escape-1.5.1.tgz#42e27d4fa04ae32f931a4b4d4191fa9cddee97cb" integrity sha1-QuJ9T6BK4y+TGktNQZH6nN3ul8s= +css@^2.0.0: + version "2.2.4" + resolved "https://registry.yarnpkg.com/css/-/css-2.2.4.tgz#c646755c73971f2bba6a601e2cf2fd71b1298929" + integrity sha512-oUnjmWpy0niI3x/mPL8dVEI1l7MnG3+HHyRPHf+YFSbK+svOhXpmSOcDURUh2aOCgl2grzrOPt1nHLuCVFULLw== + dependencies: + inherits "^2.0.3" + source-map "^0.6.1" + source-map-resolve "^0.5.2" + urix "^0.1.0" + cssesc@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/cssesc/-/cssesc-3.0.0.tgz#37741919903b868565e1c09ea747445cd18983ee" @@ -8673,7 +9809,7 @@ cssnano-util-same-parent@^4.0.0: resolved "https://registry.yarnpkg.com/cssnano-util-same-parent/-/cssnano-util-same-parent-4.0.1.tgz#574082fb2859d2db433855835d9a8456ea18bbf3" integrity sha512-WcKx5OY+KoSIAxBW6UBBRay1U6vkYheCdjyVNDm85zt5K9mHoGOfsOsqIszfAqrQQFIIKgjh2+FDgIj/zsl21Q== -cssnano@^4.1.10: +cssnano@4.1.10, cssnano@^4.1.10: version "4.1.10" resolved "https://registry.yarnpkg.com/cssnano/-/cssnano-4.1.10.tgz#0ac41f0b13d13d465487e111b778d42da631b8b2" integrity sha512-5wny+F6H4/8RgNlaqab4ktc3e0/blKutmq8yNlBFXA//nSFFAqAngjNVRzUvCgYROULmZZUoosL/KSoZo5aUaQ== @@ -8717,6 +9853,11 @@ csstype@^3.0.2: resolved "https://registry.yarnpkg.com/csstype/-/csstype-3.0.7.tgz#2a5fb75e1015e84dd15692f71e89a1450290950b" integrity sha512-KxnUB0ZMlnUWCsx2Z8MUsr6qV6ja1w9ArPErJaJaF8a5SOWoHLIszeCTKGRGRgtLgYrs1E8CHkNSP1VZTTPc9g== +cuint@^0.2.2: + version "0.2.2" + resolved "https://registry.yarnpkg.com/cuint/-/cuint-0.2.2.tgz#408086d409550c2631155619e9fa7bcadc3b991b" + integrity sha1-QICG1AlVDCYxFVYZ6fp7ytw7mRs= + currently-unhandled@^0.4.1: version "0.4.1" resolved "https://registry.yarnpkg.com/currently-unhandled/-/currently-unhandled-0.4.1.tgz#988df33feab191ef799a61369dd76c17adf957ea" @@ -8742,6 +9883,14 @@ cyclist@^1.0.1: resolved "https://registry.yarnpkg.com/cyclist/-/cyclist-1.0.1.tgz#596e9698fd0c80e12038c2b82d6eb1b35b6224d9" integrity sha1-WW6WmP0MgOEgOMK4LW6xs1tiJNk= +d@1, d@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/d/-/d-1.0.1.tgz#8698095372d58dbee346ffd0c7093f99f8f9eb5a" + integrity sha512-m62ShEObQ39CfralilEQRjH6oAMtNCV1xJyEx5LpRYUVN+EviphDgUc/F3hnYbADmkiNs67Y+3ylmlG7Lnu+FA== + dependencies: + es5-ext "^0.10.50" + type "^1.0.1" + dag-map@^2.0.2: version "2.0.2" resolved "https://registry.yarnpkg.com/dag-map/-/dag-map-2.0.2.tgz#9714b472de82a1843de2fba9b6876938cab44c68" @@ -8845,7 +9994,14 @@ debug@4, debug@^4.0.0, debug@^4.0.1, debug@^4.1.0, debug@^4.1.1, debug@^4.3.1: dependencies: ms "2.1.2" -debug@^3.0.1, debug@^3.1.0, debug@^3.1.1, debug@^3.2.6: +debug@4.1.1, debug@~4.1.0: + version "4.1.1" + resolved "https://registry.yarnpkg.com/debug/-/debug-4.1.1.tgz#3b72260255109c6b589cee050f1d516139664791" + integrity sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw== + dependencies: + ms "^2.1.1" + +debug@^3.0.1, debug@^3.1.0, debug@^3.1.1, debug@^3.2.5, debug@^3.2.6: version "3.2.7" resolved "https://registry.yarnpkg.com/debug/-/debug-3.2.7.tgz#72580b7e9145fb39b6676f9c5e5fb100b934179a" integrity sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ== @@ -8866,13 +10022,6 @@ debug@^4.3.3, debug@~4.3.1, debug@~4.3.2: dependencies: ms "2.1.2" -debug@~4.1.0: - version "4.1.1" - resolved "https://registry.yarnpkg.com/debug/-/debug-4.1.1.tgz#3b72260255109c6b589cee050f1d516139664791" - integrity sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw== - dependencies: - ms "^2.1.1" - debuglog@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/debuglog/-/debuglog-1.0.1.tgz#aa24ffb9ac3df9a2351837cfb2d279360cd78492" @@ -8932,6 +10081,18 @@ deep-eql@^3.0.1: dependencies: type-detect "^4.0.0" +deep-equal@^1.0.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/deep-equal/-/deep-equal-1.1.1.tgz#b5c98c942ceffaf7cb051e24e1434a25a2e6076a" + integrity sha512-yd9c5AdiqVcR+JjcwUQb9DkhJc8ngNr0MahEBGvDiJw8puWab2yZlh+nkasOnZP+EGTAP6rRp2JzJhJZzvNF8g== + dependencies: + is-arguments "^1.0.4" + is-date-object "^1.0.1" + is-regex "^1.0.4" + object-is "^1.0.1" + object-keys "^1.1.1" + regexp.prototype.flags "^1.2.0" + deep-extend@^0.6.0: version "0.6.0" resolved "https://registry.yarnpkg.com/deep-extend/-/deep-extend-0.6.0.tgz#c4fa7c95404a17a9c3e8ca7e1537312b736330ac" @@ -8947,6 +10108,14 @@ deepmerge@^4.2.2: resolved "https://registry.yarnpkg.com/deepmerge/-/deepmerge-4.2.2.tgz#44d2ea3679b8f4d4ffba33f03d865fc1e7bf4955" integrity sha512-FJ3UgI4gIl+PHZm53knsuSFpE+nESMr7M4v9QcgB7S63Kj/6WqMiFQJpBBYz1Pt+66bZpP3Q7Lye0Oo9MPKEdg== +default-gateway@^4.2.0: + version "4.2.0" + resolved "https://registry.yarnpkg.com/default-gateway/-/default-gateway-4.2.0.tgz#167104c7500c2115f6dd69b0a536bb8ed720552b" + integrity sha512-h6sMrVB1VMWVrW13mSc6ia/DwYYw5MN6+exNu1OaJeFac5aSAvwM7lZ0NVfTABuSkQelr4h5oebg3KB1XPdjgA== + dependencies: + execa "^1.0.0" + ip-regex "^2.1.0" + defaults@^1.0.3: version "1.0.3" resolved "https://registry.yarnpkg.com/defaults/-/defaults-1.0.3.tgz#c656051e9817d9ff08ed881477f3fe4019f3ef7d" @@ -8988,6 +10157,19 @@ define-property@^2.0.2: is-descriptor "^1.0.2" isobject "^3.0.1" +del@^4.1.1: + version "4.1.1" + resolved "https://registry.yarnpkg.com/del/-/del-4.1.1.tgz#9e8f117222ea44a31ff3a156c049b99052a9f0b4" + integrity sha512-QwGuEUouP2kVwQenAsOof5Fv8K9t3D8Ca8NxcXKrIpEHjTXK5J2nXLdP+ALI1cgv8wj7KuwBhTwBkOZSJKM5XQ== + dependencies: + "@types/glob" "^7.1.1" + globby "^6.1.0" + is-path-cwd "^2.0.0" + is-path-in-cwd "^2.0.0" + p-map "^2.0.0" + pify "^4.0.1" + rimraf "^2.6.3" + delayed-stream@~1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/delayed-stream/-/delayed-stream-1.0.0.tgz#df3ae199acadfb7d440aaae0b29e2272b24ec619" @@ -9013,6 +10195,11 @@ depd@~2.0.0: resolved "https://registry.yarnpkg.com/depd/-/depd-2.0.0.tgz#b696163cc757560d09cf22cc8fad1571b79e76df" integrity sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw== +dependency-graph@^0.7.2: + version "0.7.2" + resolved "https://registry.yarnpkg.com/dependency-graph/-/dependency-graph-0.7.2.tgz#91db9de6eb72699209d88aea4c1fd5221cac1c49" + integrity sha512-KqtH4/EZdtdfWX0p6MGP9jljvxSY6msy/pRUD4jgNwVpv3v1QmNLlsB3LDSSUg79BRVSn7jI1QPRtArGABovAQ== + dependency-tree@^8.0.0: version "8.1.0" resolved "https://registry.yarnpkg.com/dependency-tree/-/dependency-tree-8.1.0.tgz#1b896a0418bd7ba3e6d55c39bb664452a001579f" @@ -9069,6 +10256,11 @@ detect-newline@3.1.0, detect-newline@^3.0.0: resolved "https://registry.yarnpkg.com/detect-newline/-/detect-newline-3.1.0.tgz#576f5dfc63ae1a192ff192d8ad3af6308991b651" integrity sha512-TLz+x/vEXm/Y7P7wn1EJFNLxYpUD4TgMosxY6fAVJUnJMbupHBOncxyWUG9OpTaH9EBD7uFI5LfEgmMOc54DsA== +detect-node@^2.0.4: + version "2.1.0" + resolved "https://registry.yarnpkg.com/detect-node/-/detect-node-2.1.0.tgz#c9c70775a49c3d03bc2c06d9a73be550f978f8b1" + integrity sha512-T0NIuQpnTvFDATNuHN5roPwSBG83rFsuO+MXXH9/3N1eFbn4wcPjttvjMLEPWJ0RGUYgQE7cGgS3tNxbqCGM7g== + detective-amd@^3.0.1: version "3.1.0" resolved "https://registry.yarnpkg.com/detective-amd/-/detective-amd-3.1.0.tgz#92daee3214a0ca4522646cf333cac90a3fca6373" @@ -9218,6 +10410,26 @@ dir-glob@^3.0.1: dependencies: path-type "^4.0.0" +dns-equal@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/dns-equal/-/dns-equal-1.0.0.tgz#b39e7f1da6eb0a75ba9c17324b34753c47e0654d" + integrity sha1-s55/HabrCnW6nBcySzR1PEfgZU0= + +dns-packet@^1.3.1: + version "1.3.4" + resolved "https://registry.yarnpkg.com/dns-packet/-/dns-packet-1.3.4.tgz#e3455065824a2507ba886c55a89963bb107dec6f" + integrity sha512-BQ6F4vycLXBvdrJZ6S3gZewt6rcrks9KBgM9vrhW+knGRqc8uEdT7fuCwloc7nny5xNoMJ17HGH0R/6fpo8ECA== + dependencies: + ip "^1.1.0" + safe-buffer "^5.0.1" + +dns-txt@^2.0.2: + version "2.0.2" + resolved "https://registry.yarnpkg.com/dns-txt/-/dns-txt-2.0.2.tgz#b91d806f5d27188e4ab3e7d107d881a1cc4642b6" + integrity sha1-uR2Ab10nGI5Ks+fRB9iBocxGQrY= + dependencies: + buffer-indexof "^1.0.0" + doctrine@1.5.0: version "1.5.0" resolved "https://registry.yarnpkg.com/doctrine/-/doctrine-1.5.0.tgz#379dce730f6166f76cefa4e6707a159b02c5a6fa" @@ -9447,6 +10659,11 @@ electron-to-chromium@^1.3.896: resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.4.14.tgz#b0aa41fbfbf2eff8c2c6f7a871c03075250f8956" integrity sha512-RsGkAN9JEAYMObS72kzUsPPcPGMqX1rBqGuXi9aa4TBKLzICoLf+DAAtd0fVFzrniJqYzpby47gthCUoObfs0Q== +electron-to-chromium@^1.4.84: + version "1.4.114" + resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.4.114.tgz#d85ec0808dd50b0cf6e6b262480ffd385f71c873" + integrity sha512-gRwLpVYWHGbERPU6o8pKfR168V6enWEXzZc6zQNNXbgJ7UJna+9qzAIHY94+9KOv71D/CH+QebLA9pChD2q8zA== + elliptic@^6.5.3: version "6.5.4" resolved "https://registry.yarnpkg.com/elliptic/-/elliptic-6.5.4.tgz#da37cebd31e79a1367e941b592ed1fbebd58abbb" @@ -10265,7 +11482,16 @@ engine.io@~6.1.0: engine.io-parser "~5.0.3" ws "~8.2.3" -enhanced-resolve@^4.5.0: +enhanced-resolve@4.3.0: + version "4.3.0" + resolved "https://registry.yarnpkg.com/enhanced-resolve/-/enhanced-resolve-4.3.0.tgz#3b806f3bfafc1ec7de69551ef93cca46c1704126" + integrity sha512-3e87LvavsdxyoCfGusJnrZ5G8SLPOFeHSNpZI/ATL9a5leXo2k0w6MKnbqhdBad9qTobSfB20Ld7UmgoNbAZkQ== + dependencies: + graceful-fs "^4.1.2" + memory-fs "^0.5.0" + tapable "^1.0.0" + +enhanced-resolve@^4.3.0, enhanced-resolve@^4.5.0: version "4.5.0" resolved "https://registry.yarnpkg.com/enhanced-resolve/-/enhanced-resolve-4.5.0.tgz#2f3cfd84dbe3b487f18f2db2ef1e064a571ca5ec" integrity sha512-Nv9m36S/vxpsI+Hc4/ZGRs0n9mXqSWGGq49zxb/cJfPAQMbUtttJAlNPS4AQzaBdw/pKskw5bMbekT/Y7W/Wlg== @@ -10332,7 +11558,7 @@ errlop@^2.0.0: resolved "https://registry.yarnpkg.com/errlop/-/errlop-2.2.0.tgz#1ff383f8f917ae328bebb802d6ca69666a42d21b" integrity sha512-e64Qj9+4aZzjzzFpZC7p5kmm/ccCrbLhAJplhsDXQFs87XTsXwOpH4s1Io2s90Tau/8r2j9f4l/thhDevRjzxw== -errno@^0.1.3, errno@~0.1.7: +errno@^0.1.1, errno@^0.1.3, errno@~0.1.7: version "0.1.8" resolved "https://registry.yarnpkg.com/errno/-/errno-0.1.8.tgz#8bb3e9c7d463be4976ff888f76b4809ebc2e811f" integrity sha512-dJ6oBr5SQ1VSd9qkk7ByRgb/1SH4JZjCHSW/mr63/QcXO9zLVxvJ6Oy13nio03rxpSnVDDjFor75SjVeZWPW/A== @@ -10415,11 +11641,37 @@ es-to-primitive@^1.2.1: is-date-object "^1.0.1" is-symbol "^1.0.2" +es5-ext@^0.10.35, es5-ext@^0.10.50: + version "0.10.60" + resolved "https://registry.yarnpkg.com/es5-ext/-/es5-ext-0.10.60.tgz#e8060a86472842b93019c31c34865012449883f4" + integrity sha512-jpKNXIt60htYG59/9FGf2PYT3pwMpnEbNKysU+k/4FGwyGtMotOvcZOuW+EmXXYASRqYSXQfGL5cVIthOTgbkg== + dependencies: + es6-iterator "^2.0.3" + es6-symbol "^3.1.3" + next-tick "^1.1.0" + +es6-iterator@2.0.3, es6-iterator@^2.0.3: + version "2.0.3" + resolved "https://registry.yarnpkg.com/es6-iterator/-/es6-iterator-2.0.3.tgz#a7de889141a05a94b0854403b2d0a0fbfa98f3b7" + integrity sha1-p96IkUGgWpSwhUQDstCg+/qY87c= + dependencies: + d "1" + es5-ext "^0.10.35" + es6-symbol "^3.1.1" + es6-object-assign@^1.1.0: version "1.1.0" resolved "https://registry.yarnpkg.com/es6-object-assign/-/es6-object-assign-1.1.0.tgz#c2c3582656247c39ea107cb1e6652b6f9f24523c" integrity sha1-wsNYJlYkfDnqEHyx5mUrb58kUjw= +es6-symbol@^3.1.1, es6-symbol@^3.1.3: + version "3.1.3" + resolved "https://registry.yarnpkg.com/es6-symbol/-/es6-symbol-3.1.3.tgz#bad5d3c1bcdac28269f4cb331e431c78ac705d18" + integrity sha512-NJ6Yn3FuDinBaBRWl/q5X/s4koRHBrgKAu+yGI6JCBeiu3qrcbJhwT2GeR/EXVfylRk8dpQVJoLEFhK+Mu31NA== + dependencies: + d "^1.0.1" + ext "^1.1.2" + escalade@^3.1.1: version "3.1.1" resolved "https://registry.yarnpkg.com/escalade/-/escalade-3.1.1.tgz#d8cfdc7000965c5a0174b4a82eaa5c0552742e40" @@ -10786,6 +12038,13 @@ events@^3.0.0, events@^3.2.0: resolved "https://registry.yarnpkg.com/events/-/events-3.3.0.tgz#31a95ad0a924e2d2c419a813aeb2c4e878ea7400" integrity sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q== +eventsource@^1.0.7: + version "1.1.0" + resolved "https://registry.yarnpkg.com/eventsource/-/eventsource-1.1.0.tgz#00e8ca7c92109e94b0ddf32dac677d841028cfaf" + integrity sha512-VSJjT5oCNrFvCS6igjzPAt5hBzQ2qPBFIbJ03zLI9SE0mxwZpMw6BfJrbFHm1a141AavMEB8JHmBhWAd66PfCg== + dependencies: + original "^1.0.0" + evp_bytestokey@^1.0.0, evp_bytestokey@^1.0.3: version "1.0.3" resolved "https://registry.yarnpkg.com/evp_bytestokey/-/evp_bytestokey-1.0.3.tgz#7fcbdb198dc71959432efe13842684e0525acb02" @@ -10998,6 +12257,13 @@ express@^4.17.3: utils-merge "1.0.1" vary "~1.1.2" +ext@^1.1.2: + version "1.6.0" + resolved "https://registry.yarnpkg.com/ext/-/ext-1.6.0.tgz#3871d50641e874cc172e2b53f919842d19db4c52" + integrity sha512-sdBImtzkq2HpkdRLtlLWDa6w4DX22ijZLKx8BMPUuKe1c5lbN6xwQDQCxSfxBQnHZ13ls/FH0MQZx/q/gr6FQg== + dependencies: + type "^2.5.0" + extend-shallow@^2.0.1: version "2.0.1" resolved "https://registry.yarnpkg.com/extend-shallow/-/extend-shallow-2.0.1.tgz#51af7d614ad9a9f610ea1bafbb989d6b1c56890f" @@ -11096,7 +12362,18 @@ fast-glob@^3.0.3, fast-glob@^3.1.1: micromatch "^4.0.2" picomatch "^2.2.1" -fast-json-stable-stringify@2.x, fast-json-stable-stringify@^2.0.0: +fast-glob@^3.2.4: + version "3.2.11" + resolved "https://registry.yarnpkg.com/fast-glob/-/fast-glob-3.2.11.tgz#a1172ad95ceb8a16e20caa5c5e56480e5129c1d9" + integrity sha512-xrO3+1bxSo3ZVHAnqzyuewYT6aMFHRAd4Kcs92MAonjwQZLsK9d0SF1IyQ3k5PoirxTW0Oe/RqFgMQ6TcNE5Ew== + dependencies: + "@nodelib/fs.stat" "^2.0.2" + "@nodelib/fs.walk" "^1.2.3" + glob-parent "^5.1.2" + merge2 "^1.3.0" + micromatch "^4.0.4" + +fast-json-stable-stringify@2.1.0, fast-json-stable-stringify@2.x, fast-json-stable-stringify@^2.0.0: version "2.1.0" resolved "https://registry.yarnpkg.com/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz#874bf69c6f404c2b5d99c481341399fd55892633" integrity sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw== @@ -11139,6 +12416,13 @@ fastq@^1.6.0: dependencies: reusify "^1.0.4" +faye-websocket@^0.10.0: + version "0.10.0" + resolved "https://registry.yarnpkg.com/faye-websocket/-/faye-websocket-0.10.0.tgz#4e492f8d04dfb6f89003507f6edbf2d501e7c6f4" + integrity sha1-TkkvjQTftviQA1B/btvy1QHnxvQ= + dependencies: + websocket-driver ">=0.5.1" + faye-websocket@^0.11.3: version "0.11.3" resolved "https://registry.yarnpkg.com/faye-websocket/-/faye-websocket-0.11.3.tgz#5c0e9a8968e8912c286639fde977a8b209f2508e" @@ -11146,6 +12430,13 @@ faye-websocket@^0.11.3: dependencies: websocket-driver ">=0.5.1" +faye-websocket@~0.11.1: + version "0.11.4" + resolved "https://registry.yarnpkg.com/faye-websocket/-/faye-websocket-0.11.4.tgz#7f0d9275cfdd86a1c963dc8b65fcc451edcbb1da" + integrity sha512-CzbClwlXAuiRQAlUyfqPgvPoNKTckTPGfwZV4ZdAhVcP2lh9KUxJg2b5GkE7XbjKQ3YJnQ9z6D9ntLAlB+tP8g== + dependencies: + websocket-driver ">=0.5.1" + fb-watchman@^2.0.0: version "2.0.1" resolved "https://registry.yarnpkg.com/fb-watchman/-/fb-watchman-2.0.1.tgz#fc84fb39d2709cf3ff6d743706157bb5708a8a85" @@ -11199,6 +12490,14 @@ file-entry-cache@^6.0.1: dependencies: flat-cache "^3.0.4" +file-loader@6.0.0: + version "6.0.0" + resolved "https://registry.yarnpkg.com/file-loader/-/file-loader-6.0.0.tgz#97bbfaab7a2460c07bcbd72d3a6922407f67649f" + integrity sha512-/aMOAYEFXDdjG0wytpTL5YQLfZnnTmLNjn+AIrJ/6HVnTfDqLsVKUUwkDf4I4kgex36BvjuXEn/TX9B/1ESyqQ== + dependencies: + loader-utils "^2.0.0" + schema-utils "^2.6.5" + file-loader@^6.2.0: version "6.2.0" resolved "https://registry.yarnpkg.com/file-loader/-/file-loader-6.2.0.tgz#baef7cf8e1840df325e4390b4484879480eebe4d" @@ -11319,6 +12618,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-parent-dir@^0.3.0: + version "0.3.1" + resolved "https://registry.yarnpkg.com/find-parent-dir/-/find-parent-dir-0.3.1.tgz#c5c385b96858c3351f95d446cab866cbf9f11125" + integrity sha512-o4UcykWV/XN9wm+jMEtWLPlV8RXCZnMhQI6F6OdHeSez7iiJWePw8ijOlskJZMsaQoGR/b7dH6lO02HhaTN7+A== + find-pkg@^0.1.2: version "0.1.2" resolved "https://registry.yarnpkg.com/find-pkg/-/find-pkg-0.1.2.tgz#1bdc22c06e36365532e2a248046854b9788da557" @@ -11610,6 +12914,15 @@ fs-exists-sync@^0.1.0: resolved "https://registry.yarnpkg.com/fs-exists-sync/-/fs-exists-sync-0.1.0.tgz#982d6893af918e72d08dec9e8673ff2b5a8d6add" integrity sha1-mC1ok6+RjnLQjeyehnP/K1qNat0= +fs-extra@4.0.2: + version "4.0.2" + resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-4.0.2.tgz#f91704c53d1b461f893452b0c307d9997647ab6b" + integrity sha1-+RcExT0bRh+JNFKwwwfZmXZHq2s= + dependencies: + graceful-fs "^4.1.2" + jsonfile "^4.0.0" + universalify "^0.1.0" + fs-extra@^0.24.0: version "0.24.0" resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-0.24.0.tgz#d4e4342a96675cb7846633a6099249332b539952" @@ -11674,7 +12987,7 @@ fs-extra@^8.0.0, fs-extra@^8.0.1, fs-extra@^8.1.0: jsonfile "^4.0.0" universalify "^0.1.0" -fs-extra@^9.0.1, fs-extra@^9.1.0: +fs-extra@^9.0.0, fs-extra@^9.0.1, fs-extra@^9.1.0: version "9.1.0" resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-9.1.0.tgz#5954460c764a8da2094ba3554bf839e6b9a7c86d" integrity sha512-hcg3ZmepS30/7BSFqRvoo3DOMQu7IjqxO5nCDt+zM9XWjb33Wg7ziNT+Qvqbuc3+gWpzO02JubVyk2G4Zvo1OQ== @@ -11702,6 +13015,13 @@ fs-minipass@^1.2.7: dependencies: minipass "^2.6.0" +fs-minipass@^2.0.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/fs-minipass/-/fs-minipass-2.1.0.tgz#7f5036fdbf12c63c169190cbe4199c852271f9fb" + integrity sha512-V/JgOLFCS+R6Vcq0slCuaeWEdNC3ouDlJMNIsacH2VtALiu9mV4LPrHc5cDl8k5aw6J8jwgWWpiTo5RYhmIzvg== + dependencies: + minipass "^3.0.0" + fs-tree-diff@^0.5.2, fs-tree-diff@^0.5.3, fs-tree-diff@^0.5.4, fs-tree-diff@^0.5.6: version "0.5.9" resolved "https://registry.yarnpkg.com/fs-tree-diff/-/fs-tree-diff-0.5.9.tgz#a4ec6182c2f5bd80b9b83c8e23e4522e6f5fd946" @@ -11762,6 +13082,11 @@ fsevents@^2.3.2, fsevents@~2.3.1, fsevents@~2.3.2: resolved "https://registry.yarnpkg.com/fsevents/-/fsevents-2.3.2.tgz#8a526f78b8fdf4623b709e0b975c52c24c02fd1a" integrity sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA== +fsevents@~2.1.2: + version "2.1.3" + resolved "https://registry.yarnpkg.com/fsevents/-/fsevents-2.1.3.tgz#fb738703ae8d2f9fe900c33836ddebee8b97f23e" + integrity sha512-Auw9a4AxqWpa9GUfj370BMPzzyncfBABW8Mab7BGWBYDj4Isgq+cDKtx0i6u9jcX9pQDnswsaaOTgTmA5pEjuQ== + function-bind@^1.1.1: version "1.1.1" resolved "https://registry.yarnpkg.com/function-bind/-/function-bind-1.1.1.tgz#a56899d3ea3c9bab874bb9773b7c5ede92f4895d" @@ -11772,6 +13097,11 @@ functional-red-black-tree@^1.0.1: resolved "https://registry.yarnpkg.com/functional-red-black-tree/-/functional-red-black-tree-1.0.1.tgz#1b0ab3bd553b2a0d6399d29c0e3ea0b252078327" integrity sha1-GwqzvVU7Kg1jmdKcDj6gslIHgyc= +functions-have-names@^1.2.2: + version "1.2.3" + resolved "https://registry.yarnpkg.com/functions-have-names/-/functions-have-names-1.2.3.tgz#0404fe4ee2ba2f607f0e0ec3c80bae994133b834" + integrity sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ== + gauge@~2.7.3: version "2.7.4" resolved "https://registry.yarnpkg.com/gauge/-/gauge-2.7.4.tgz#2c03405c7538c39d7eb37b317022e325fb018bf7" @@ -11823,7 +13153,7 @@ genfun@^5.0.0: resolved "https://registry.yarnpkg.com/genfun/-/genfun-5.0.0.tgz#9dd9710a06900a5c4a5bf57aca5da4e52fe76537" integrity sha512-KGDOARWVga7+rnB3z9Sd2Letx515owfk0hSxHGuqjANb1M+x2bGZGqHLiozPsYMdM2OubeMni/Hpwmjq6qIUhA== -gensync@^1.0.0-beta.2: +gensync@^1.0.0-beta.1, gensync@^1.0.0-beta.2: version "1.0.0-beta.2" resolved "https://registry.yarnpkg.com/gensync/-/gensync-1.0.0-beta.2.tgz#32a6ee76c3d7f52d46b2b1ae5d93fea8580a25e0" integrity sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg== @@ -12069,7 +13399,7 @@ glob-parent@^3.1.0: is-glob "^3.1.0" path-dirname "^1.0.0" -glob-parent@^5.1.0, glob-parent@^5.1.2, glob-parent@~5.1.0: +glob-parent@^5.1.0, glob-parent@^5.1.1, glob-parent@^5.1.2, glob-parent@~5.1.0, glob-parent@~5.1.2: version "5.1.2" resolved "https://registry.yarnpkg.com/glob-parent/-/glob-parent-5.1.2.tgz#869832c58034fe68a4093c17dc15e8340d8401c4" integrity sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow== @@ -12098,6 +13428,18 @@ glob@7.1.3: once "^1.3.0" path-is-absolute "^1.0.0" +glob@7.1.6: + version "7.1.6" + resolved "https://registry.yarnpkg.com/glob/-/glob-7.1.6.tgz#141f33b81a7c2492e125594307480c46679278a6" + integrity sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA== + dependencies: + fs.realpath "^1.0.0" + inflight "^1.0.4" + inherits "2" + minimatch "^3.0.4" + once "^1.3.0" + path-is-absolute "^1.0.0" + glob@7.2.0, glob@^7.0.0, glob@^7.0.3, glob@^7.0.4, glob@^7.0.5, glob@^7.1.1, glob@^7.1.2, glob@^7.1.3, glob@^7.1.4, glob@^7.1.6, glob@^7.1.7: version "7.2.0" resolved "https://registry.yarnpkg.com/glob/-/glob-7.2.0.tgz#d15535af7732e02e948f4c41628bd910293f6023" @@ -12207,6 +13549,17 @@ globby@^11.0.1, globby@^11.0.2: merge2 "^1.3.0" slash "^3.0.0" +globby@^6.1.0: + version "6.1.0" + resolved "https://registry.yarnpkg.com/globby/-/globby-6.1.0.tgz#f5a6d70e8395e21c858fb0489d64df02424d506c" + integrity sha1-9abXDoOV4hyFj7BInWTfAkJNUGw= + dependencies: + array-union "^1.0.1" + glob "^7.0.3" + object-assign "^4.0.1" + pify "^2.0.0" + pinkie-promise "^2.0.0" + globby@^8.0.1: version "8.0.2" resolved "https://registry.yarnpkg.com/globby/-/globby-8.0.2.tgz#5697619ccd95c5275dbb2d6faa42087c1a941d8d" @@ -12379,6 +13732,11 @@ gzip-size@^6.0.0: dependencies: duplexer "^0.1.2" +handle-thing@^2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/handle-thing/-/handle-thing-2.0.1.tgz#857f79ce359580c340d43081cc648970d0bb234e" + integrity sha512-9Qn4yBxelxoh2Ow62nP+Ka/kMnOXRi8BXnRaUwezLNhqelnN49xKz4F/dPP8OYLxLxq6JDtZb2i9XznUQbNPTg== + handlebars@^4.0.1, handlebars@^4.0.4, handlebars@^4.3.1, handlebars@^4.7.3, handlebars@^4.7.6: version "4.7.7" resolved "https://registry.yarnpkg.com/handlebars/-/handlebars-4.7.7.tgz#9ce33416aad02dbd6c8fafa8240d5d98004945a1" @@ -12693,6 +14051,13 @@ hosted-git-info@^2.1.4, hosted-git-info@^2.7.1: resolved "https://registry.yarnpkg.com/hosted-git-info/-/hosted-git-info-2.8.8.tgz#7539bd4bc1e0e0a895815a2e0262420b12858488" integrity sha512-f/wzC2QaWBs7t9IYqB4T3sR1xviIViXJRJTWBlx2Gf3g0Xi5vI7Yy4koXQ1c9OYDGHN9sBy1DQ2AB8fqZBWhUg== +hosted-git-info@^3.0.2: + version "3.0.8" + resolved "https://registry.yarnpkg.com/hosted-git-info/-/hosted-git-info-3.0.8.tgz#6e35d4cc87af2c5f816e4cb9ce350ba87a3f370d" + integrity sha512-aXpmwoOhRBrw6X3j0h5RloK4x1OzsxMPyxqIHyNfSe2pypkVTZFpEiRoSipPEPlMrh0HW/XsjkJ5WgnCirpNUw== + dependencies: + lru-cache "^6.0.0" + hosted-git-info@^4.0.1: version "4.0.2" resolved "https://registry.yarnpkg.com/hosted-git-info/-/hosted-git-info-4.0.2.tgz#5e425507eede4fea846b7262f0838456c4209961" @@ -12700,6 +14065,16 @@ hosted-git-info@^4.0.1: dependencies: lru-cache "^6.0.0" +hpack.js@^2.1.6: + version "2.1.6" + resolved "https://registry.yarnpkg.com/hpack.js/-/hpack.js-2.1.6.tgz#87774c0949e513f42e84575b3c45681fade2a0b2" + integrity sha1-h3dMCUnlE/QuhFdbPEVoH63ioLI= + dependencies: + inherits "^2.0.1" + obuf "^1.0.0" + readable-stream "^2.0.1" + wbuf "^1.1.0" + hsl-regex@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/hsl-regex/-/hsl-regex-1.0.0.tgz#d49330c789ed819e276a4c0d272dffa30b18fe6e" @@ -12729,6 +14104,11 @@ html-encoding-sniffer@^3.0.0: dependencies: whatwg-encoding "^2.0.0" +html-entities@^1.3.1: + version "1.4.0" + resolved "https://registry.yarnpkg.com/html-entities/-/html-entities-1.4.0.tgz#cfbd1b01d2afaf9adca1b10ae7dffab98c71d2dc" + integrity sha512-8nxjcBcd8wovbeKx7h3wTji4e6+rhaVuPNpMqwWgnHh+N9ToqsCs6XztWRBPQ+UtzsoMAdKZtUENoVzU/EMtZA== + html-escaper@^2.0.0: version "2.0.2" resolved "https://registry.yarnpkg.com/html-escaper/-/html-escaper-2.0.2.tgz#dfd60027da36a36dfcbe236262c00a5822681453" @@ -12773,6 +14153,11 @@ http-cache-semantics@3.8.1, http-cache-semantics@^3.8.1: resolved "https://registry.yarnpkg.com/http-cache-semantics/-/http-cache-semantics-3.8.1.tgz#39b0e16add9b605bf0a9ef3d9daaf4843b4cacd2" integrity sha512-5ai2iksyV8ZXmnZhHH4rWPoxxistEexSi5936zIQ1bnNTW5VnA85B6P/VpXiRM017IgRvb2kKo1a//y+0wSp3w== +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@1.7.2: version "1.7.2" resolved "https://registry.yarnpkg.com/http-errors/-/http-errors-1.7.2.tgz#4f5029cf13239f31036e5b2e55292bcfbcc85c8f" @@ -12847,7 +14232,17 @@ http-proxy-agent@^5.0.0: agent-base "6" debug "4" -http-proxy@^1.13.1, http-proxy@^1.18.1: +http-proxy-middleware@0.19.1: + version "0.19.1" + resolved "https://registry.yarnpkg.com/http-proxy-middleware/-/http-proxy-middleware-0.19.1.tgz#183c7dc4aa1479150306498c210cdaf96080a43a" + integrity sha512-yHYTgWMQO8VvwNS22eLLloAkvungsKdKTLO8AJlftYIKNfJr3GK3zK0ZCfzDDGUBttdGc8xFy1mCitvNKQtC3Q== + dependencies: + http-proxy "^1.17.0" + is-glob "^4.0.0" + lodash "^4.17.11" + micromatch "^3.1.10" + +http-proxy@^1.13.1, http-proxy@^1.17.0, http-proxy@^1.18.1: version "1.18.1" resolved "https://registry.yarnpkg.com/http-proxy/-/http-proxy-1.18.1.tgz#401541f0534884bbf95260334e72f88ee3976549" integrity sha512-7mz/721AbnJwIVbnaSv1Cz3Am0ZLT/UBwkC92VlxhXv/k/BBQfM2fXElQNC27BVGr0uwUpplYPQM9LnaBMR5NQ== @@ -12937,6 +14332,13 @@ iconv-lite@^0.6.2: dependencies: safer-buffer ">= 2.1.2 < 3.0.0" +icss-utils@^4.0.0, icss-utils@^4.1.1: + version "4.1.1" + resolved "https://registry.yarnpkg.com/icss-utils/-/icss-utils-4.1.1.tgz#21170b53789ee27447c2f47dd683081403f9a467" + integrity sha512-4aFq7wvWyMHKgxsH8QQtGpvbASCf+eM3wPRLI6R+MgAnTCZ6STYsRvttLvRWK0Nfif5piF394St3HeJDaljGPA== + dependencies: + postcss "^7.0.14" + icss-utils@^5.0.0, icss-utils@^5.1.0: version "5.1.0" resolved "https://registry.yarnpkg.com/icss-utils/-/icss-utils-5.1.0.tgz#c6be6858abd013d768e98366ae47e25d5887b1ae" @@ -12979,11 +14381,28 @@ ignore@^5.1.1, ignore@^5.1.4: resolved "https://registry.yarnpkg.com/ignore/-/ignore-5.1.8.tgz#f150a8b50a34289b33e22f5889abd4d8016f0e57" integrity sha512-BMpfD7PpiETpBl/A6S498BaIJ6Y/ABT93ETbby2fP00v4EbvPBXWEoaR1UBPKs3iR53pJY7EtZk5KACI57i1Uw== +image-size@~0.5.0: + version "0.5.5" + resolved "https://registry.yarnpkg.com/image-size/-/image-size-0.5.5.tgz#09dfd4ab9d20e29eb1c3e80b8990378df9e3cb9c" + integrity sha1-Cd/Uq50g4p6xw+gLiZA3jfnjy5w= + immediate@~3.0.5: version "3.0.6" resolved "https://registry.yarnpkg.com/immediate/-/immediate-3.0.6.tgz#9db1dbd0faf8de6fbe0f5dd5e56bb606280de69b" integrity sha1-nbHb0Pr43m++D13V5Wu2BigN5ps= +immutable@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/immutable/-/immutable-4.0.0.tgz#b86f78de6adef3608395efb269a91462797e2c23" + integrity sha512-zIE9hX70qew5qTUjSS7wi1iwj/l7+m54KWU247nhM3v806UdGj1yDndXj+IOYxxtW9zyLI+xqFNZjTuDaLUqFw== + +import-cwd@^2.0.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/import-cwd/-/import-cwd-2.1.0.tgz#aa6cf36e722761285cb371ec6519f53e2435b0a9" + integrity sha1-qmzzbnInYShcs3HsZRn1PiQ1sKk= + dependencies: + import-from "^2.1.0" + import-fresh@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/import-fresh/-/import-fresh-2.0.0.tgz#d81355c15612d386c61f9ddd3922d4304822a546" @@ -13000,6 +14419,13 @@ import-fresh@^3.0.0, import-fresh@^3.2.1: parent-module "^1.0.0" resolve-from "^4.0.0" +import-from@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/import-from/-/import-from-2.1.0.tgz#335db7f2a7affd53aaa471d4b8021dee36b7f3b1" + integrity sha1-M1238qev/VOqpHHUuAId7ja387E= + dependencies: + resolve-from "^3.0.0" + import-local@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/import-local/-/import-local-1.0.0.tgz#5e4ffdc03f4fe6c009c6729beb29631c2f8227bc" @@ -13008,6 +14434,14 @@ import-local@^1.0.0: pkg-dir "^2.0.0" resolve-cwd "^2.0.0" +import-local@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/import-local/-/import-local-2.0.0.tgz#55070be38a5993cf18ef6db7e961f5bee5c5a09d" + integrity sha512-b6s04m3O+s3CGSbqDIyP4R6aAwAeYlVq9+WUWep6iHa8ETRf9yei1U48C5MmfJmV9AiLYYBKPMq/W+/WRpQmCQ== + dependencies: + pkg-dir "^3.0.0" + resolve-cwd "^2.0.0" + import-local@^3.0.2: version "3.1.0" resolved "https://registry.yarnpkg.com/import-local/-/import-local-3.1.0.tgz#b4479df8a5fd44f6cdce24070675676063c95cb4" @@ -13081,6 +14515,11 @@ inherits@2.0.3: resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.3.tgz#633c2c83e3da42a502f52466022480f4208261de" integrity sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4= +ini@1.3.6: + version "1.3.6" + resolved "https://registry.yarnpkg.com/ini/-/ini-1.3.6.tgz#f1c46a2a93a253e7b3905115e74d527cd23061a1" + integrity sha512-IZUoxEjNjubzrmvzZU4lKP7OnYmX72XRl3sqkfJhBKweKi5rnGi5+IUdlj/H1M+Ip5JQ1WzaDMOBRY90Ajc5jg== + ini@^1.3.2, ini@^1.3.4, ini@~1.3.0: version "1.3.8" resolved "https://registry.yarnpkg.com/ini/-/ini-1.3.8.tgz#a29da425b48806f34767a4efce397269af28432c" @@ -13100,6 +14539,13 @@ init-package-json@^1.10.3: validate-npm-package-license "^3.0.1" validate-npm-package-name "^3.0.0" +injection-js@^2.2.1: + version "2.4.0" + resolved "https://registry.yarnpkg.com/injection-js/-/injection-js-2.4.0.tgz#ebe8871b1a349f23294eaa751bbd8209a636e754" + integrity sha512-6jiJt0tCAo9zjHbcwLiPL+IuNe9SQ6a9g0PEzafThW3fOQi0mrmiJGBJvDD6tmhPh8cQHIQtCOrJuBfQME4kPA== + dependencies: + tslib "^2.0.0" + inline-source-map-comment@^1.0.5: version "1.0.5" resolved "https://registry.yarnpkg.com/inline-source-map-comment/-/inline-source-map-comment-1.0.5.tgz#50a8a44c2a790dfac441b5c94eccd5462635faf6" @@ -13118,6 +14564,25 @@ inline-source-map@~0.6.0: dependencies: source-map "~0.5.3" +inquirer@7.3.3, inquirer@^7.0.1: + version "7.3.3" + resolved "https://registry.yarnpkg.com/inquirer/-/inquirer-7.3.3.tgz#04d176b2af04afc157a83fd7c100e98ee0aad003" + integrity sha512-JG3eIAj5V9CwcGvuOmoo6LB9kbAYT8HXffUl6memuszlwDC/qvFAJw49XJ5NROSFNPxp3iQg1GqkFhaY/CR0IA== + dependencies: + ansi-escapes "^4.2.1" + chalk "^4.1.0" + cli-cursor "^3.1.0" + cli-width "^3.0.0" + external-editor "^3.0.3" + figures "^3.0.0" + lodash "^4.17.19" + mute-stream "0.0.8" + run-async "^2.4.0" + rxjs "^6.6.0" + string-width "^4.1.0" + strip-ansi "^6.0.0" + through "^2.3.6" + inquirer@^6, inquirer@^6.2.0: version "6.5.2" resolved "https://registry.yarnpkg.com/inquirer/-/inquirer-6.5.2.tgz#ad50942375d036d327ff528c08bd5fab089928ca" @@ -13137,24 +14602,13 @@ inquirer@^6, inquirer@^6.2.0: strip-ansi "^5.1.0" through "^2.3.6" -inquirer@^7.0.1: - version "7.3.3" - resolved "https://registry.yarnpkg.com/inquirer/-/inquirer-7.3.3.tgz#04d176b2af04afc157a83fd7c100e98ee0aad003" - integrity sha512-JG3eIAj5V9CwcGvuOmoo6LB9kbAYT8HXffUl6memuszlwDC/qvFAJw49XJ5NROSFNPxp3iQg1GqkFhaY/CR0IA== +internal-ip@^4.3.0: + version "4.3.0" + resolved "https://registry.yarnpkg.com/internal-ip/-/internal-ip-4.3.0.tgz#845452baad9d2ca3b69c635a137acb9a0dad0907" + integrity sha512-S1zBo1D6zcsyuC6PMmY5+55YMILQ9av8lotMx447Bq6SAgo/sDK6y6uUKmuYhW7eacnIhFfsPmCNYdDzsnnDCg== dependencies: - ansi-escapes "^4.2.1" - chalk "^4.1.0" - cli-cursor "^3.1.0" - cli-width "^3.0.0" - external-editor "^3.0.3" - figures "^3.0.0" - lodash "^4.17.19" - mute-stream "0.0.8" - run-async "^2.4.0" - rxjs "^6.6.0" - string-width "^4.1.0" - strip-ansi "^6.0.0" - through "^2.3.6" + default-gateway "^4.2.0" + ipaddr.js "^1.9.0" internal-slot@^1.0.3: version "1.0.3" @@ -13190,12 +14644,17 @@ invert-kv@^2.0.0: resolved "https://registry.yarnpkg.com/invert-kv/-/invert-kv-2.0.0.tgz#7393f5afa59ec9ff5f67a27620d11c226e3eec02" integrity sha512-wPVv/y/QQ/Uiirj/vh3oP+1Ww+AWehmi1g5fFWGPF6IpCBCDVrhgHRMvrLfdYcwDh3QJbGXDW4JAuzxElLSqKA== -ip@1.1.5, ip@^1.1.5: +ip-regex@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/ip-regex/-/ip-regex-2.1.0.tgz#fa78bf5d2e6913c911ce9f819ee5146bb6d844e9" + integrity sha1-+ni/XS5pE8kRzp+BnuUUa7bYROk= + +ip@1.1.5, ip@^1.1.0, ip@^1.1.5: version "1.1.5" resolved "https://registry.yarnpkg.com/ip/-/ip-1.1.5.tgz#bdded70114290828c0a039e72ef25f5aaec4354a" integrity sha1-vd7XARQpCCjAoDnnLvJfWq7ENUo= -ipaddr.js@1.9.1: +ipaddr.js@1.9.1, ipaddr.js@^1.9.0: version "1.9.1" resolved "https://registry.yarnpkg.com/ipaddr.js/-/ipaddr.js-1.9.1.tgz#bff38543eeb8984825079ff3a2a8e6cbd46781b3" integrity sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g== @@ -13205,6 +14664,11 @@ is-absolute-url@^2.0.0: resolved "https://registry.yarnpkg.com/is-absolute-url/-/is-absolute-url-2.1.0.tgz#50530dfb84fcc9aa7dbe7852e83a37b93b9f2aa6" integrity sha1-UFMN+4T8yap9vnhS6Do3uTufKqY= +is-absolute-url@^3.0.3: + version "3.0.3" + resolved "https://registry.yarnpkg.com/is-absolute-url/-/is-absolute-url-3.0.3.tgz#96c6a22b6a23929b11ea0afb1836c36ad4a5d698" + integrity sha512-opmNIX7uFnS96NtPmhWQgQx6/NYFgsUXYMllcfzwWKUMwfo8kku1TvE6hkNcH+Q1ts5cMVrsY7j0bxXQDciu9Q== + is-accessor-descriptor@^0.1.6: version "0.1.6" resolved "https://registry.yarnpkg.com/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz#a9e12cb3ae8d876727eeef3843f8a0897b5c98d6" @@ -13322,6 +14786,13 @@ is-core-module@^2.8.0: dependencies: has "^1.0.3" +is-core-module@^2.8.1: + version "2.9.0" + resolved "https://registry.yarnpkg.com/is-core-module/-/is-core-module-2.9.0.tgz#e1c34429cd51c6dd9e09e0799e396e27b19a9c69" + integrity sha512-+5FPy5PnwmO3lvfMb0AsoPaBG+5KHUI0wYFXOtYPnVVVspTFUuMZNfNaNVRt3FZadstu2c8x23vykRW/NBoU6A== + dependencies: + has "^1.0.3" + is-data-descriptor@^0.1.4: version "0.1.4" resolved "https://registry.yarnpkg.com/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz#0b5ee648388e2c860282e793f1856fec3f301b56" @@ -13502,6 +14973,25 @@ is-object@^1.0.1: resolved "https://registry.yarnpkg.com/is-object/-/is-object-1.0.2.tgz#a56552e1c665c9e950b4a025461da87e72f86fcf" integrity sha512-2rRIahhZr2UWb45fIOuvZGpFtz0TyOZLf32KxBbSoUCeZR495zCKlWUKKUByk3geS2eAs7ZAABt0Y/Rx0GiQGA== +is-path-cwd@^2.0.0: + version "2.2.0" + resolved "https://registry.yarnpkg.com/is-path-cwd/-/is-path-cwd-2.2.0.tgz#67d43b82664a7b5191fd9119127eb300048a9fdb" + integrity sha512-w942bTcih8fdJPJmQHFzkS76NEP8Kzzvmw92cXsazb8intwLqPibPPdXf4ANdKV3rYMuuQYGIWtvz9JilB3NFQ== + +is-path-in-cwd@^2.0.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/is-path-in-cwd/-/is-path-in-cwd-2.1.0.tgz#bfe2dca26c69f397265a4009963602935a053acb" + integrity sha512-rNocXHgipO+rvnP6dk3zI20RpOtrAM/kzbB258Uw5BWr3TpXi861yzjo16Dn4hUox07iw5AyeMLHWsujkjzvRQ== + dependencies: + is-path-inside "^2.1.0" + +is-path-inside@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/is-path-inside/-/is-path-inside-2.1.0.tgz#7c9810587d659a40d27bcdb4d5616eab059494b2" + integrity sha512-wiyhTzfDWsvwAW53OBWF5zuvaOGlZ6PwYxAbPVDhpm+gM09xKQGjBq/8uYN12aDvMxnAnq3dxTyoSoRNmg5YFg== + dependencies: + path-is-inside "^1.0.2" + is-plain-obj@2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/is-plain-obj/-/is-plain-obj-2.1.0.tgz#45e42e37fccf1f40da8e5f76ee21515840c09287" @@ -13541,6 +15031,14 @@ is-reference@^1.2.1: dependencies: "@types/estree" "*" +is-regex@^1.0.4, is-regex@^1.1.4: + version "1.1.4" + resolved "https://registry.yarnpkg.com/is-regex/-/is-regex-1.1.4.tgz#eef5663cd59fa4c0ae339505323df6854bb15958" + integrity sha512-kvRdxDsxZjhzUX07ZnLydzS1TU/TJlTUHHY4YLL87e37oUA49DfkLqgy+VjFocowy29cKvcSiu+kIv728jTTVg== + dependencies: + call-bind "^1.0.2" + has-tostringtag "^1.0.0" + is-regex@^1.1.2: version "1.1.2" resolved "https://registry.yarnpkg.com/is-regex/-/is-regex-1.1.2.tgz#81c8ebde4db142f2cf1c53fc86d6a45788266251" @@ -13549,14 +15047,6 @@ is-regex@^1.1.2: call-bind "^1.0.2" has-symbols "^1.0.1" -is-regex@^1.1.4: - version "1.1.4" - resolved "https://registry.yarnpkg.com/is-regex/-/is-regex-1.1.4.tgz#eef5663cd59fa4c0ae339505323df6854bb15958" - integrity sha512-kvRdxDsxZjhzUX07ZnLydzS1TU/TJlTUHHY4YLL87e37oUA49DfkLqgy+VjFocowy29cKvcSiu+kIv728jTTVg== - dependencies: - call-bind "^1.0.2" - has-tostringtag "^1.0.0" - is-regexp@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/is-regexp/-/is-regexp-1.0.0.tgz#fd2d883545c46bac5a633e7b9a09e87fa2cb5069" @@ -13692,6 +15182,11 @@ is-weakref@^1.0.1: dependencies: call-bind "^1.0.0" +is-what@^3.14.1: + version "3.14.1" + resolved "https://registry.yarnpkg.com/is-what/-/is-what-3.14.1.tgz#e1222f46ddda85dead0fd1c9df131760e77755c1" + integrity sha512-sNxgpk9793nzSs7bA6JQJGeIuRBQhAaNGG77kzYQgMkrID+lS6SlK07K5LaptscDlSaIgH+GPFzf+d75FVxozA== + is-windows@^0.2.0: version "0.2.0" resolved "https://registry.yarnpkg.com/is-windows/-/is-windows-0.2.0.tgz#de1aa6d63ea29dd248737b69f1ff8b8002d2108c" @@ -13707,7 +15202,7 @@ is-wsl@^1.1.0: resolved "https://registry.yarnpkg.com/is-wsl/-/is-wsl-1.1.0.tgz#1f16e4aa22b04d1336b66188a66af3c600c3a66d" integrity sha1-HxbkqiKwTRM2tmGIpmrzxgDDpm0= -is-wsl@^2.1.0, is-wsl@^2.2.0: +is-wsl@^2.1.0, is-wsl@^2.1.1, is-wsl@^2.2.0: version "2.2.0" resolved "https://registry.yarnpkg.com/is-wsl/-/is-wsl-2.2.0.tgz#74a4c76e77ca9fd3f932f290c17ea326cd157271" integrity sha512-fKzAra0rGJUUBwGBgNkHZuToZcn+TtXHpeCgmkMJMMYx1sQDYaCSyjJBSCa2nH1DGm7s3n1oBnohoVTBaN7Lww== @@ -13792,6 +15287,16 @@ istanbul-lib-instrument@^3.3.0: istanbul-lib-coverage "^2.0.5" semver "^6.0.0" +istanbul-lib-instrument@^4.0.3: + version "4.0.3" + resolved "https://registry.yarnpkg.com/istanbul-lib-instrument/-/istanbul-lib-instrument-4.0.3.tgz#873c6fff897450118222774696a3f28902d77c1d" + integrity sha512-BXgQl9kf4WTCPCCpmFGoJkz/+uhvm7h7PFKUYxh7qarQd3ER33vHG//qaE8eN25l07YqZPpHXU9I09l/RD5aGQ== + dependencies: + "@babel/core" "^7.7.5" + "@istanbuljs/schema" "^0.1.2" + istanbul-lib-coverage "^3.0.0" + semver "^6.3.0" + istanbul-lib-instrument@^5.0.4, istanbul-lib-instrument@^5.1.0: version "5.1.0" resolved "https://registry.yarnpkg.com/istanbul-lib-instrument/-/istanbul-lib-instrument-5.1.0.tgz#7b49198b657b27a730b8e9cb601f1e1bff24c59a" @@ -14427,6 +15932,15 @@ jest-watcher@^27.5.1: jest-util "^27.5.1" string-length "^4.0.1" +jest-worker@26.3.0: + version "26.3.0" + resolved "https://registry.yarnpkg.com/jest-worker/-/jest-worker-26.3.0.tgz#7c8a97e4f4364b4f05ed8bca8ca0c24de091871f" + integrity sha512-Vmpn2F6IASefL+DVBhPzI2J9/GJUsqzomdeN+P+dK8/jKxbh8R3BtFnx3FIta7wYlPU62cpJMJQo4kuOowcMnw== + dependencies: + "@types/node" "*" + merge-stream "^2.0.0" + supports-color "^7.0.0" + jest-worker@27.0.0-next.5: version "27.0.0-next.5" resolved "https://registry.yarnpkg.com/jest-worker/-/jest-worker-27.0.0-next.5.tgz#5985ee29b12a4e191f4aae4bb73b97971d86ec28" @@ -14444,7 +15958,7 @@ jest-worker@^24.9.0: merge-stream "^2.0.0" supports-color "^6.1.0" -jest-worker@^26.2.1: +jest-worker@^26.2.1, jest-worker@^26.3.0: version "26.6.2" resolved "https://registry.yarnpkg.com/jest-worker/-/jest-worker-26.6.2.tgz#7f72cbc4d643c365e27b9fd775f9d0eaa9c7a8ed" integrity sha512-KWYVV1c4i+jbMpaBC+U++4Va0cp8OisU185o73T1vo99hqi7w8tSJfUXYswwqqrjzwxa6KpRK54WhPvwf5w6PQ== @@ -14689,6 +16203,11 @@ json-stringify-safe@^5.0.1, json-stringify-safe@~5.0.1: resolved "https://registry.yarnpkg.com/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz#1296a2d58fd45f19a0f6ce01d65701e2c735b6eb" integrity sha1-Epai1Y/UXxmg9s4B1lcB4sc1tus= +json3@^3.3.2: + version "3.3.3" + resolved "https://registry.yarnpkg.com/json3/-/json3-3.3.3.tgz#7fc10e375fc5ae42c4705a5cc0aa6f62be305b81" + integrity sha512-c7/8mbUsKigAbLkD5B010BK4D9LZm7A1pNItkEwiUZRpIN66exu/e7YQWysGun+TRKaJp8MhemM+VkfWv42aCA== + json5@2.x, json5@^2.1.2: version "2.2.0" resolved "https://registry.yarnpkg.com/json5/-/json5-2.2.0.tgz#2dfefe720c6ba525d9ebd909950f0515316c89a3" @@ -14713,6 +16232,11 @@ json5@^2.2.1: resolved "https://registry.yarnpkg.com/json5/-/json5-2.2.1.tgz#655d50ed1e6f95ad1a3caababd2b0efda10b395c" integrity sha512-1hqLFMSrGHRHxav9q9gNjJ5EXznIxGVO09xQRrwplcS8qs28pZ8s8hupZAmqDwZUmVZ2Qb2jnyPOWcDH8m8dlA== +jsonc-parser@2.3.0: + version "2.3.0" + resolved "https://registry.yarnpkg.com/jsonc-parser/-/jsonc-parser-2.3.0.tgz#7c7fc988ee1486d35734faaaa866fadb00fa91ee" + integrity sha512-b0EBt8SWFNnixVdvoR2ZtEGa9ZqLhbJnOjezn+WP+8kspFm+PFYDN8Z4Bc7pRlDjvuVcADSUkroIuTWWn/YiIA== + jsonfile@^2.1.0: version "2.4.0" resolved "https://registry.yarnpkg.com/jsonfile/-/jsonfile-2.4.0.tgz#3736a2b428b87bbda0cc83b53fa3d633a35c2ae8" @@ -14863,6 +16387,13 @@ karma-sinon@^1.0.5: resolved "https://registry.yarnpkg.com/karma-sinon/-/karma-sinon-1.0.5.tgz#4e3443f2830fdecff624d3747163f1217daa2a9a" integrity sha1-TjRD8oMP3s/2JNN0cWPxIX2qKpo= +karma-source-map-support@1.4.0: + version "1.4.0" + resolved "https://registry.yarnpkg.com/karma-source-map-support/-/karma-source-map-support-1.4.0.tgz#58526ceccf7e8730e56effd97a4de8d712ac0d6b" + integrity sha512-RsBECncGO17KAoJCYXjv+ckIz+Ii9NCi+9enk+rq6XC81ezYkb4/RHE6CTXdA7IOJqoF3wcaLfVG0CPmE5ca6A== + dependencies: + source-map-support "^0.5.5" + karma-typescript-es6-transform@^4.0.0: version "4.1.1" resolved "https://registry.yarnpkg.com/karma-typescript-es6-transform/-/karma-typescript-es6-transform-4.1.1.tgz#b24e8ea8cb8431c5342f7bbb9f1fd6060335ca39" @@ -14966,6 +16497,11 @@ keyv@3.0.0: dependencies: json-buffer "3.0.0" +killable@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/killable/-/killable-1.0.1.tgz#4c8ce441187a061c7474fb87ca08e2a638194892" + integrity sha512-LzqtLKlUwirEUyl/nicirVmNiPvYs7l5n8wOPP7fyJVpUPkvCnW/vuiXGpylGUlnPDnB7311rARzAt3Mhswpjg== + kind-of@^2.0.1: version "2.0.1" resolved "https://registry.yarnpkg.com/kind-of/-/kind-of-2.0.1.tgz#018ec7a4ce7e3a86cb9141be519d24c8faa981b5" @@ -15002,6 +16538,11 @@ kleur@^3.0.3: resolved "https://registry.yarnpkg.com/kleur/-/kleur-3.0.3.tgz#a79c9ecc86ee1ce3fa6206d1216c501f147fc07e" integrity sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w== +klona@^2.0.3: + version "2.0.5" + resolved "https://registry.yarnpkg.com/klona/-/klona-2.0.5.tgz#d166574d90076395d9963aa7a928fabb8d76afbc" + integrity sha512-pJiBpiXMbt7dkzXe8Ghj/u4FfXOOa98fPW+bihOJ4SjnoijweJrNThJfd3ifXpXhREjpoF2mZVH1GfS9LV3kHQ== + last-call-webpack-plugin@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/last-call-webpack-plugin/-/last-call-webpack-plugin-3.0.0.tgz#9742df0e10e3cf46e5c0381c2de90d3a7a2d7555" @@ -15059,11 +16600,44 @@ lerna@3.13.4: import-local "^1.0.0" npmlog "^4.1.2" +less-loader@6.2.0: + version "6.2.0" + resolved "https://registry.yarnpkg.com/less-loader/-/less-loader-6.2.0.tgz#8b26f621c155b342eefc24f5bd6e9dc40c42a719" + integrity sha512-Cl5h95/Pz/PWub/tCBgT1oNMFeH1WTD33piG80jn5jr12T4XbxZcjThwNXDQ7AG649WEynuIzO4b0+2Tn9Qolg== + dependencies: + clone "^2.1.2" + less "^3.11.3" + loader-utils "^2.0.0" + schema-utils "^2.7.0" + +less@^3.10.3, less@^3.11.3: + version "3.13.1" + resolved "https://registry.yarnpkg.com/less/-/less-3.13.1.tgz#0ebc91d2a0e9c0c6735b83d496b0ab0583077909" + integrity sha512-SwA1aQXGUvp+P5XdZslUOhhLnClSLIjWvJhmd+Vgib5BFIr9lMNlQwmwUNOjXThF/A0x+MCYYPeWEfeWiLRnTw== + dependencies: + copy-anything "^2.0.1" + tslib "^1.10.0" + optionalDependencies: + errno "^0.1.1" + graceful-fs "^4.1.2" + image-size "~0.5.0" + make-dir "^2.1.0" + mime "^1.4.1" + native-request "^1.0.5" + source-map "~0.6.0" + leven@^3.1.0: version "3.1.0" resolved "https://registry.yarnpkg.com/leven/-/leven-3.1.0.tgz#77891de834064cccba82ae7842bb6b14a13ed7f2" integrity sha512-qsda+H8jTaUaN/x5vzW2rzc+8Rw4TAQ/4KjB46IwK5VH+IlVeeeje/EoZRpiXvIqjFgK84QffqPztGI3VBLG1A== +levenary@^1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/levenary/-/levenary-1.1.1.tgz#842a9ee98d2075aa7faeedbe32679e9205f46f77" + integrity sha512-mkAdOIt79FD6irqjYSs4rdbnlT5vRonMEvBVPVb3XmevfS8kgRXwfes0dhPdEtzTWD/1eNE/Bm/G1iRt6DcnQQ== + dependencies: + leven "^3.1.0" + levn@^0.4.1: version "0.4.1" resolved "https://registry.yarnpkg.com/levn/-/levn-0.4.1.tgz#ae4562c007473b932a6200d403268dd2fffc6ade" @@ -15105,6 +16679,14 @@ libnpmpublish@^1.1.1: semver "^5.5.1" ssri "^6.0.1" +license-webpack-plugin@2.3.0: + version "2.3.0" + resolved "https://registry.yarnpkg.com/license-webpack-plugin/-/license-webpack-plugin-2.3.0.tgz#c00f70d5725ba0408de208acb9e66612cc2eceda" + integrity sha512-JK/DXrtN6UeYQSgkg5q1+pgJ8aiKPL9tnz9Wzw+Ikkf+8mJxG56x6t8O+OH/tAeF/5NREnelTEMyFtbJNkjH4w== + dependencies: + "@types/webpack-sources" "^0.1.5" + webpack-sources "^1.2.0" + lie@3.1.1: version "3.1.1" resolved "https://registry.yarnpkg.com/lie/-/lie-3.1.1.tgz#9a436b2cc7746ca59de7a41fa469b3efb76bd87e" @@ -15206,16 +16788,7 @@ loader-utils@1.2.3: emojis-list "^2.0.0" json5 "^1.0.1" -loader-utils@^1.2.3, loader-utils@^1.4.0: - version "1.4.0" - resolved "https://registry.yarnpkg.com/loader-utils/-/loader-utils-1.4.0.tgz#c579b5e34cb34b1a74edc6c1fb36bfa371d5a613" - integrity sha512-qH0WSMBtn/oHuwjy/NucEgbx5dbxxnxup9s4PVXJUDHZBQY+s0NWA9rJf53RBnQZxfch7euUui7hpoAPvALZdA== - dependencies: - big.js "^5.2.2" - emojis-list "^3.0.0" - json5 "^1.0.1" - -loader-utils@^2.0.0: +loader-utils@2.0.0, loader-utils@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/loader-utils/-/loader-utils-2.0.0.tgz#e4cace5b816d425a166b5f097e10cd12b36064b0" integrity sha512-rP4F0h2RaWSvPEkD7BLDFQnvSf+nK+wr3ESUjNTyAGobqrijmW92zc+SO6d4p4B1wh7+B/Jg1mkQe5NYUEHtHQ== @@ -15224,6 +16797,15 @@ loader-utils@^2.0.0: emojis-list "^3.0.0" json5 "^2.1.2" +loader-utils@^1.0.2, loader-utils@^1.1.0, loader-utils@^1.2.3, loader-utils@^1.4.0: + version "1.4.0" + resolved "https://registry.yarnpkg.com/loader-utils/-/loader-utils-1.4.0.tgz#c579b5e34cb34b1a74edc6c1fb36bfa371d5a613" + integrity sha512-qH0WSMBtn/oHuwjy/NucEgbx5dbxxnxup9s4PVXJUDHZBQY+s0NWA9rJf53RBnQZxfch7euUui7hpoAPvALZdA== + dependencies: + big.js "^5.2.2" + emojis-list "^3.0.0" + json5 "^1.0.1" + loader.js@~4.7.0: version "4.7.0" resolved "https://registry.yarnpkg.com/loader.js/-/loader.js-4.7.0.tgz#a1a52902001c83631efde9688b8ab3799325ef1f" @@ -15496,7 +17078,7 @@ log-symbols@2.2.0, log-symbols@^2.1.0, log-symbols@^2.2.0: dependencies: chalk "^2.0.1" -log-symbols@^4.1.0: +log-symbols@^4.0.0, log-symbols@^4.1.0: version "4.1.0" resolved "https://registry.yarnpkg.com/log-symbols/-/log-symbols-4.1.0.tgz#3fbdbb95b4683ac9fc785111e792e558d4abd503" integrity sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg== @@ -15526,6 +17108,11 @@ log4js@^6.4.1: rfdc "^1.3.0" streamroller "^3.0.2" +loglevel@^1.6.8: + version "1.8.0" + resolved "https://registry.yarnpkg.com/loglevel/-/loglevel-1.8.0.tgz#e7ec73a57e1e7b419cb6c6ac06bf050b67356114" + integrity sha512-G6A/nJLRgWOuuwdNuA6koovfEV1YpqqAG4pRUlFaz3jj2QNZ8M4vBqnVA+HBTmU/AMNUtlOsMmSpF6NyOjztbA== + lolex@^4.2.0: version "4.2.0" resolved "https://registry.yarnpkg.com/lolex/-/lolex-4.2.0.tgz#ddbd7f6213ca1ea5826901ab1222b65d714b3cd7" @@ -15649,6 +17236,13 @@ magic-string@0.25.7, magic-string@^0.25.1, magic-string@^0.25.7: dependencies: sourcemap-codec "^1.4.4" +magic-string@^0.25.0: + version "0.25.9" + resolved "https://registry.yarnpkg.com/magic-string/-/magic-string-0.25.9.tgz#de7f9faf91ef8a1c91d02c2e5314c8277dbcdd1c" + integrity sha512-RmF0AsMzgt25qzqqLc1+MbHmhdx0ojF2Fvs4XnOqz2ZOBXzzkEwc/dJQZCYHAn7v1jbVOjAZfK8msRn4BxO4VQ== + dependencies: + sourcemap-codec "^1.4.8" + make-dir@^1.0.0: version "1.3.0" resolved "https://registry.yarnpkg.com/make-dir/-/make-dir-1.3.0.tgz#79c1033b80515bd6d24ec9933e860ca75ee27f0c" @@ -15656,7 +17250,7 @@ make-dir@^1.0.0: dependencies: pify "^3.0.0" -make-dir@^2.0.0: +make-dir@^2.0.0, make-dir@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/make-dir/-/make-dir-2.1.0.tgz#5f0310e18b8be898cc07009295a30ae41e91e6f5" integrity sha512-LS9X+dc8KLxXCb8dni79fLIIUA5VyZoyjSMCwTluaXA0o27cCK0bhXkpgw+sTXVpPy/lSO57ilRixqk0vDmtRA== @@ -15947,6 +17541,13 @@ merge-descriptors@1.0.1: resolved "https://registry.yarnpkg.com/merge-descriptors/-/merge-descriptors-1.0.1.tgz#b00aaa556dd8b44568150ec9d1b953f3f90cbb61" integrity sha1-sAqqVW3YtEVoFQ7J0blT8/kMu2E= +merge-source-map@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/merge-source-map/-/merge-source-map-1.1.0.tgz#2fdde7e6020939f70906a68f2d7ae685e4c8c646" + integrity sha512-Qkcp7P2ygktpMPh2mCQZaf3jhN6D3Z/qVZHSdWvQ+2Ef5HgRAPBO57A77+ENm0CPx2+1Ce/MYKi3ymqdfuqibw== + dependencies: + source-map "^0.6.1" + merge-stream@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/merge-stream/-/merge-stream-2.0.0.tgz#52823629a14dd00c9770fb6ad47dc6310f2c1f60" @@ -16035,6 +17636,11 @@ mime-db@1.51.0: resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.51.0.tgz#d9ff62451859b18342d960850dc3cfb77e63fb0c" integrity sha512-5y8A56jg7XVQx2mbv1lu49NR4dokRnhZYTtL+KGfaa27uq4pSTXkwQkFJl4pkRMyNFz/EtYDSkiiEHx3F7UN6g== +mime-db@1.52.0: + version "1.52.0" + resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.52.0.tgz#bbabcdc02859f4987301c856e3387ce5ec43bf70" + integrity sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg== + mime-types@^2.0.8, mime-types@^2.1.12, mime-types@^2.1.18, mime-types@^2.1.26, mime-types@~2.1.19, mime-types@~2.1.24: version "2.1.30" resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-2.1.30.tgz#6e7be8b4c479825f85ed6326695db73f9305d62d" @@ -16049,12 +17655,19 @@ mime-types@^2.1.27, mime-types@~2.1.34: dependencies: mime-db "1.51.0" -mime@1.6.0: +mime-types@~2.1.17: + version "2.1.35" + resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-2.1.35.tgz#381a871b62a734450660ae3deee44813f70d959a" + integrity sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw== + dependencies: + mime-db "1.52.0" + +mime@1.6.0, mime@^1.4.1: version "1.6.0" resolved "https://registry.yarnpkg.com/mime/-/mime-1.6.0.tgz#32cd9e5c64553bd58d19a568af452acff04981b1" integrity sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg== -mime@^2.2.0, mime@^2.3.1, mime@^2.4.6, mime@^2.5.2: +mime@^2.2.0, mime@^2.3.1, mime@^2.4.4, mime@^2.4.6, mime@^2.5.2: version "2.6.0" resolved "https://registry.yarnpkg.com/mime/-/mime-2.6.0.tgz#a2a682a95cd4d0cb1d6257e28f83da7e35800367" integrity sha512-USPkMeET31rOMiarsBNIHZKLGgvKc/LrjofAnBlOttf5ajRvqiRA8QsenbcooctK6d6Ts6aqZXBA+XbkKthiQg== @@ -16079,6 +17692,16 @@ min-indent@^1.0.0: resolved "https://registry.yarnpkg.com/min-indent/-/min-indent-1.0.1.tgz#a63f681673b30571fbe8bc25686ae746eefa9869" integrity sha512-I9jwMn07Sy/IwOj3zVkVik2JTvgpaykDZEigL6Rx6N9LbMywwUSMtxET+7lVoDLLd3O3IXwJwvuuns8UB/HeAg== +mini-css-extract-plugin@0.10.0: + version "0.10.0" + resolved "https://registry.yarnpkg.com/mini-css-extract-plugin/-/mini-css-extract-plugin-0.10.0.tgz#a0e6bfcad22a9c73f6c882a3c7557a98e2d3d27d" + integrity sha512-QgKgJBjaJhxVPwrLNqqwNS0AGkuQQ31Hp4xGXEK/P7wehEg6qmNtReHKai3zRXqY60wGVWLYcOMJK2b98aGc3A== + dependencies: + loader-utils "^1.1.0" + normalize-url "1.9.1" + schema-utils "^1.0.0" + webpack-sources "^1.1.0" + minimalistic-assert@^1.0.0, minimalistic-assert@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/minimalistic-assert/-/minimalistic-assert-1.0.1.tgz#2e194de044626d4a10e7f7fbc00ce73e83e4d5c7" @@ -16128,6 +17751,27 @@ minimist@^1.1.1, minimist@^1.1.3, minimist@^1.2.0, minimist@^1.2.5: resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.5.tgz#67d66014b66a6a8aaa0c083c5fd58df4e4e97602" integrity sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw== +minipass-collect@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/minipass-collect/-/minipass-collect-1.0.2.tgz#22b813bf745dc6edba2576b940022ad6edc8c617" + integrity sha512-6T6lH0H8OG9kITm/Jm6tdooIbogG9e0tLgpY6mphXSm/A9u8Nq1ryBG+Qspiub9LjWlBPsPS3tWQ/Botq4FdxA== + dependencies: + minipass "^3.0.0" + +minipass-flush@^1.0.5: + version "1.0.5" + resolved "https://registry.yarnpkg.com/minipass-flush/-/minipass-flush-1.0.5.tgz#82e7135d7e89a50ffe64610a787953c4c4cbb373" + integrity sha512-JmQSYYpPUqX5Jyn1mXaRwOda1uQ8HP5KAT/oDSLCzt1BYRhQU0/hDtsB1ufZfEEzMZ9aAVmsBw8+FWsIXlClWw== + dependencies: + minipass "^3.0.0" + +minipass-pipeline@^1.2.2: + version "1.2.4" + resolved "https://registry.yarnpkg.com/minipass-pipeline/-/minipass-pipeline-1.2.4.tgz#68472f79711c084657c067c5c6ad93cddea8214c" + integrity sha512-xuIq7cIOt09RPRJ19gdi4b+RiNvDFYe5JH+ggNvBqGqpQXcru3PcRmOZuHBKWK1Txf9+cQ+HMVN4d6z46LZP7A== + dependencies: + minipass "^3.0.0" + minipass@^2.2.0, minipass@^2.3.5, minipass@^2.6.0, minipass@^2.9.0: version "2.9.0" resolved "https://registry.yarnpkg.com/minipass/-/minipass-2.9.0.tgz#e713762e7d3e32fed803115cf93e04bca9fcc9a6" @@ -16136,6 +17780,13 @@ minipass@^2.2.0, minipass@^2.3.5, minipass@^2.6.0, minipass@^2.9.0: safe-buffer "^5.1.2" yallist "^3.0.0" +minipass@^3.0.0, minipass@^3.1.1: + version "3.1.6" + resolved "https://registry.yarnpkg.com/minipass/-/minipass-3.1.6.tgz#3b8150aa688a711a1521af5e8779c1d3bb4f45ee" + integrity sha512-rty5kpw9/z8SX9dmxblFA6edItUmwJgMeYDZRrwlIVN27i8gysGbznJwUggw2V/FVqFSDdWy040ZPS811DYAqQ== + dependencies: + yallist "^4.0.0" + minizlib@^1.3.3: version "1.3.3" resolved "https://registry.yarnpkg.com/minizlib/-/minizlib-1.3.3.tgz#2290de96818a34c29551c8a8d301216bd65a861d" @@ -16143,6 +17794,14 @@ minizlib@^1.3.3: dependencies: minipass "^2.9.0" +minizlib@^2.1.1: + version "2.1.2" + resolved "https://registry.yarnpkg.com/minizlib/-/minizlib-2.1.2.tgz#e90d3466ba209b932451508a11ce3d3632145931" + integrity sha512-bAxsR8BVfj60DWXHE3u30oHzfl4G7khkSuPW+qvpd7jFRHm7dLxOjUk1EHACJ/hxLY8phGJ0YhYHZo7jil7Qdg== + dependencies: + minipass "^3.0.0" + yallist "^4.0.0" + mississippi@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/mississippi/-/mississippi-3.0.0.tgz#ea0a3291f97e0b5e8776b363d5f0a12d94c67022" @@ -16194,7 +17853,7 @@ mkdirp@0.5.x, mkdirp@^0.5.0, mkdirp@^0.5.1, mkdirp@^0.5.3, mkdirp@^0.5.5, mkdirp dependencies: minimist "^1.2.5" -mkdirp@1.0.4, mkdirp@^1.0.4: +mkdirp@1.0.4, mkdirp@^1.0.3, mkdirp@^1.0.4, mkdirp@~1.0.4: version "1.0.4" resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-1.0.4.tgz#3eb5ed62622756d79a5f0e2a221dfebad75c2f7e" integrity sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw== @@ -16355,6 +18014,19 @@ ms@2.1.3, ms@^2.0.0, ms@^2.1.1, ms@^2.1.2: resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.3.tgz#574c8138ce1d2b5861f0b44579dbadd60c6615b2" integrity sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA== +multicast-dns-service-types@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/multicast-dns-service-types/-/multicast-dns-service-types-1.1.0.tgz#899f11d9686e5e05cb91b35d5f0e63b773cfc901" + integrity sha1-iZ8R2WhuXgXLkbNdXw5jt3PPyQE= + +multicast-dns@^6.0.1: + version "6.2.3" + resolved "https://registry.yarnpkg.com/multicast-dns/-/multicast-dns-6.2.3.tgz#a0ec7bd9055c4282f790c3c82f4e28db3b31b229" + integrity sha512-ji6J5enbMyGRHIAkAOu3WdV8nggqviKCEKtXcOqfphZZtQrmHKycfynJ2V7eVPUA4NhJ6V7Wf4TmGbTwKE9B6g== + dependencies: + dns-packet "^1.3.1" + thunky "^1.0.2" + multimatch@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/multimatch/-/multimatch-2.1.0.tgz#9c7906a22fb4c02919e2f5f75161b4cdbd4b2a2b" @@ -16417,6 +18089,11 @@ nanomatch@^1.2.9: snapdragon "^0.8.1" to-regex "^3.0.1" +native-request@^1.0.5: + version "1.1.0" + resolved "https://registry.yarnpkg.com/native-request/-/native-request-1.1.0.tgz#acdb30fe2eefa3e1bc8c54b3a6852e9c5c0d3cb0" + integrity sha512-uZ5rQaeRn15XmpgE0xoPL8YWqcX90VtCFglYwAgkvKM5e8fog+vePLAhHxuuv/gRkrQxIeh5U3q9sMNUrENqWw== + native-url@0.3.4: version "0.3.4" resolved "https://registry.yarnpkg.com/native-url/-/native-url-0.3.4.tgz#29c943172aed86c63cee62c8c04db7f5756661f8" @@ -16452,6 +18129,11 @@ new-find-package-json@^1.1.0: debug "^4.3.2" tslib "^2.3.0" +next-tick@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/next-tick/-/next-tick-1.1.0.tgz#1836ee30ad56d67ef281b22bd199f709449b35eb" + integrity sha512-CXdUiJembsNjuToQvxayPZF9Vqht7hewsvy2sOWafLvi2awflj9mOC6bHIg50orX8IJvWKY9wYQ/zB2kogPslQ== + next@10.1.3: version "10.1.3" resolved "https://registry.yarnpkg.com/next/-/next-10.1.3.tgz#e26e8371343a42bc2ba9be5cb253a7d324d03673" @@ -16508,6 +18190,37 @@ next@10.1.3: vm-browserify "1.1.2" watchpack "2.1.1" +ng-packagr@^10.1.0: + version "10.1.2" + resolved "https://registry.yarnpkg.com/ng-packagr/-/ng-packagr-10.1.2.tgz#7c869ec5bea92ff3ab65392913ad4e8af749799a" + integrity sha512-pm61gu6jPkohL8tFWk+2DwUtb3rs5GpND1ZjKUYv5WUJPUQmBfG5WvEO/CDVQpSDWhNWWLTt17NIQ+RS3hNUHg== + dependencies: + "@rollup/plugin-commonjs" "^15.0.0" + "@rollup/plugin-json" "^4.0.0" + "@rollup/plugin-node-resolve" "^9.0.0" + ajv "^6.12.3" + ansi-colors "^4.1.1" + autoprefixer "^9.6.5" + browserslist "^4.7.0" + chokidar "^3.2.1" + commander "^6.0.0" + cssnano-preset-default "^4.0.7" + fs-extra "^9.0.0" + glob "^7.1.2" + injection-js "^2.2.1" + less "^3.10.3" + node-sass-tilde-importer "^1.0.0" + postcss "^7.0.29" + postcss-url "^8.0.0" + read-pkg-up "^5.0.0" + rimraf "^3.0.0" + rollup "^2.8.0" + rollup-plugin-sourcemaps "^0.6.0" + rxjs "^6.5.0" + sass "^1.23.0" + stylus "^0.54.7" + terser "^5.0.0" + nice-try@^1.0.4: version "1.0.5" resolved "https://registry.yarnpkg.com/nice-try/-/nice-try-1.0.5.tgz#a3378a7696ce7d223e88fc9b764bd7ef1089e366" @@ -16695,6 +18408,18 @@ node-releases@^2.0.1: resolved "https://registry.yarnpkg.com/node-releases/-/node-releases-2.0.1.tgz#3d1d395f204f1f2f29a54358b9fb678765ad2fc5" integrity sha512-CqyzN6z7Q6aMeF/ktcMVTzhAHCEpf8SOarwpzpf8pNBY2k5/oM34UHldUwp8VKI7uxct2HxSRdJjBaZeESzcxA== +node-releases@^2.0.2: + version "2.0.3" + resolved "https://registry.yarnpkg.com/node-releases/-/node-releases-2.0.3.tgz#225ee7488e4a5e636da8da52854844f9d716ca96" + integrity sha512-maHFz6OLqYxz+VQyCAtA3PTX4UP/53pa05fyDNc9CwjvJ0yEh6+xBwKsgCxMNhS8taUKBFYxfuiaD9U/55iFaw== + +node-sass-tilde-importer@^1.0.0: + version "1.0.2" + resolved "https://registry.yarnpkg.com/node-sass-tilde-importer/-/node-sass-tilde-importer-1.0.2.tgz#1a15105c153f648323b4347693fdb0f331bad1ce" + integrity sha512-Swcmr38Y7uB78itQeBm3mThjxBy9/Ah/ykPIaURY/L6Nec9AyRoL/jJ7ECfMR+oZeCTVQNxVMu/aHU+TLRVbdg== + dependencies: + find-parent-dir "^0.3.0" + node-source-walk@^4.0.0, node-source-walk@^4.2.0: version "4.2.0" resolved "https://registry.yarnpkg.com/node-source-walk/-/node-source-walk-4.2.0.tgz#c2efe731ea8ba9c03c562aa0a9d984e54f27bc2c" @@ -16746,6 +18471,21 @@ 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-range@^0.1.2: + version "0.1.2" + resolved "https://registry.yarnpkg.com/normalize-range/-/normalize-range-0.1.2.tgz#2d10c06bdfd312ea9777695a4d28439456b75942" + integrity sha1-LRDAa9/TEuqXd2laTShDlFa3WUI= + +normalize-url@1.9.1: + version "1.9.1" + resolved "https://registry.yarnpkg.com/normalize-url/-/normalize-url-1.9.1.tgz#2cc0d66b31ea23036458436e3620d85954c66c3c" + integrity sha1-LMDWazHqIwNkWENuNiDYWVTGbDw= + dependencies: + object-assign "^4.0.1" + prepend-http "^1.0.0" + query-string "^4.1.0" + sort-keys "^1.0.0" + normalize-url@2.0.1: version "2.0.1" resolved "https://registry.yarnpkg.com/normalize-url/-/normalize-url-2.0.1.tgz#835a9da1551fa26f70e92329069a23aa6574d7e6" @@ -16767,6 +18507,13 @@ npm-bundled@^1.0.1, npm-bundled@^1.1.1: dependencies: npm-normalize-package-bin "^1.0.1" +npm-install-checks@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/npm-install-checks/-/npm-install-checks-4.0.0.tgz#a37facc763a2fde0497ef2c6d0ac7c3fbe00d7b4" + integrity sha512-09OmyDkNLYwqKPOnbI8exiOZU2GVVmQp7tgez2BPi5OZC8M82elDAps7sxC4l//uSUtotWqoEIDwjRvWH4qz8w== + dependencies: + semver "^7.1.1" + npm-lifecycle@^2.1.0: version "2.1.1" resolved "https://registry.yarnpkg.com/npm-lifecycle/-/npm-lifecycle-2.1.1.tgz#0027c09646f0fd346c5c93377bdaba59c6748fdf" @@ -16786,6 +18533,15 @@ npm-normalize-package-bin@^1.0.0, npm-normalize-package-bin@^1.0.1: resolved "https://registry.yarnpkg.com/npm-normalize-package-bin/-/npm-normalize-package-bin-1.0.1.tgz#6e79a41f23fd235c0623218228da7d9c23b8f6e2" integrity sha512-EPfafl6JL5/rU+ot6P3gRSCpPDW5VmIzX959Ob1+ySFUuuYHWHekXpwdUZcKP5C+DS4GEtdJluwBjnsNDl+fSA== +npm-package-arg@8.0.1: + version "8.0.1" + resolved "https://registry.yarnpkg.com/npm-package-arg/-/npm-package-arg-8.0.1.tgz#9d76f8d7667b2373ffda60bb801a27ef71e3e270" + integrity sha512-/h5Fm6a/exByzFSTm7jAyHbgOqErl9qSNJDQF32Si/ZzgwT2TERVxRxn3Jurw1wflgyVVAxnFR4fRHPM7y1ClQ== + dependencies: + hosted-git-info "^3.0.2" + semver "^7.0.0" + validate-npm-package-name "^3.0.0" + "npm-package-arg@^4.0.0 || ^5.0.0 || ^6.0.0", npm-package-arg@^6.0.0, npm-package-arg@^6.1.0: version "6.1.1" resolved "https://registry.yarnpkg.com/npm-package-arg/-/npm-package-arg-6.1.1.tgz#02168cb0a49a2b75bf988a28698de7b529df5cb7" @@ -16796,6 +18552,15 @@ npm-normalize-package-bin@^1.0.0, npm-normalize-package-bin@^1.0.1: semver "^5.6.0" validate-npm-package-name "^3.0.0" +npm-package-arg@^8.0.0: + version "8.1.5" + resolved "https://registry.yarnpkg.com/npm-package-arg/-/npm-package-arg-8.1.5.tgz#3369b2d5fe8fdc674baa7f1786514ddc15466e44" + integrity sha512-LhgZrg0n0VgvzVdSm1oiZworPbTxYHUJCgtsJW8mGvlDpxTM1vSJc3m5QZeUkhAHIzbz3VCHd/R4osi1L1Tg/Q== + dependencies: + hosted-git-info "^4.0.1" + semver "^7.3.4" + validate-npm-package-name "^3.0.0" + npm-package-arg@^8.1.0: version "8.1.2" resolved "https://registry.yarnpkg.com/npm-package-arg/-/npm-package-arg-8.1.2.tgz#b868016ae7de5619e729993fbd8d11dc3c52ab62" @@ -16824,6 +18589,15 @@ npm-packlist@^2.1.4: npm-bundled "^1.1.1" npm-normalize-package-bin "^1.0.1" +npm-pick-manifest@6.1.0: + version "6.1.0" + resolved "https://registry.yarnpkg.com/npm-pick-manifest/-/npm-pick-manifest-6.1.0.tgz#2befed87b0fce956790f62d32afb56d7539c022a" + integrity sha512-ygs4k6f54ZxJXrzT0x34NybRlLeZ4+6nECAIbr2i0foTnijtS1TJiyzpqtuUAJOps/hO0tNDr8fRV5g+BtRlTw== + dependencies: + npm-install-checks "^4.0.0" + npm-package-arg "^8.0.0" + semver "^7.0.0" + npm-pick-manifest@^3.0.0: version "3.0.2" resolved "https://registry.yarnpkg.com/npm-pick-manifest/-/npm-pick-manifest-3.0.2.tgz#f4d9e5fd4be2153e5f4e5f9b7be8dc419a99abb7" @@ -16923,6 +18697,11 @@ null-check@^1.0.0: resolved "https://registry.yarnpkg.com/null-check/-/null-check-1.0.0.tgz#977dffd7176012b9ec30d2a39db5cf72a0439edd" integrity sha1-l33/1xdgErnsMNKjnbXPcqBDnt0= +num2fraction@^1.2.2: + version "1.2.2" + resolved "https://registry.yarnpkg.com/num2fraction/-/num2fraction-1.2.2.tgz#6f682b6a027a4e9ddfa4564cd2589d1d4e669ede" + integrity sha1-b2gragJ6Tp3fpFZM0lidHU5mnt4= + number-is-nan@^1.0.0: version "1.0.1" resolved "https://registry.yarnpkg.com/number-is-nan/-/number-is-nan-1.0.1.tgz#097b602b53422a522c1afb8790318336941a011d" @@ -17053,6 +18832,11 @@ object.values@^1.1.0, object.values@^1.1.1, object.values@^1.1.3: es-abstract "^1.18.0-next.2" has "^1.0.3" +obuf@^1.0.0, obuf@^1.1.2: + version "1.1.2" + resolved "https://registry.yarnpkg.com/obuf/-/obuf-1.1.2.tgz#09bea3343d41859ebd446292d11c9d4db619084e" + integrity sha512-PX1wu0AmAdPqOL1mWhqmlOd8kOIZQwGZw6rh7uby9fTc5lhaOWFLX3I6R1hrF9k3zUY40e6igsLGkDXK92LJNg== + octokit-pagination-methods@^1.1.0: version "1.1.0" resolved "https://registry.yarnpkg.com/octokit-pagination-methods/-/octokit-pagination-methods-1.1.0.tgz#cf472edc9d551055f9ef73f6e42b4dbb4c80bea4" @@ -17091,6 +18875,14 @@ onetime@^5.1.0, onetime@^5.1.2: dependencies: mimic-fn "^2.1.0" +open@7.2.0: + version "7.2.0" + resolved "https://registry.yarnpkg.com/open/-/open-7.2.0.tgz#212959bd7b0ce2e8e3676adc76e3cf2f0a2498b4" + integrity sha512-4HeyhxCvBTI5uBePsAdi55C5fmqnWZ2e2MlmvWi5KW5tdH5rxoiv/aMtbeVxKZc3eWkT1GymMnLG8XC4Rq4TDQ== + dependencies: + is-docker "^2.0.0" + is-wsl "^2.1.1" + open@^8.3.0: version "8.4.0" resolved "https://registry.yarnpkg.com/open/-/open-8.4.0.tgz#345321ae18f8138f82565a910fdc6b39e8c244f8" @@ -17105,6 +18897,13 @@ opener@^1.5.2: resolved "https://registry.yarnpkg.com/opener/-/opener-1.5.2.tgz#5d37e1f35077b9dcac4301372271afdeb2a13598" integrity sha512-ur5UIdyw5Y7yEj9wLzhqXiy6GZ3Mwx0yGI+5sMn2r0N0v3cKJvUmFH5yPP+WXh9e0xfyzyJX95D8l088DNFj7A== +opn@^5.5.0: + version "5.5.0" + resolved "https://registry.yarnpkg.com/opn/-/opn-5.5.0.tgz#fc7164fab56d235904c51c3b27da6758ca3b9bfc" + integrity sha512-PqHpggC9bLV0VeWcdKhkpxY+3JTzetLSqTCWL/z/tFIbI6G8JCjondXklT1JinczLz2Xib62sSp0T/gKT4KksA== + dependencies: + is-wsl "^1.1.0" + optimize-css-assets-webpack-plugin@^5.0.4: version "5.0.4" resolved "https://registry.yarnpkg.com/optimize-css-assets-webpack-plugin/-/optimize-css-assets-webpack-plugin-5.0.4.tgz#85883c6528aaa02e30bbad9908c92926bb52dc90" @@ -17144,6 +18943,20 @@ optionator@^0.9.1: type-check "^0.4.0" word-wrap "^1.2.3" +ora@5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/ora/-/ora-5.0.0.tgz#4f0b34f2994877b49b452a707245ab1e9f6afccb" + integrity sha512-s26qdWqke2kjN/wC4dy+IQPBIMWBJlSU/0JZhk30ZDBLelW25rv66yutUWARMigpGPzcXHb+Nac5pNhN/WsARw== + dependencies: + chalk "^4.1.0" + cli-cursor "^3.1.0" + cli-spinners "^2.4.0" + is-interactive "^1.0.0" + log-symbols "^4.0.0" + mute-stream "0.0.8" + strip-ansi "^6.0.0" + wcwidth "^1.0.1" + ora@^3.4.0: version "3.4.0" resolved "https://registry.yarnpkg.com/ora/-/ora-3.4.0.tgz#bf0752491059a3ef3ed4c85097531de9fdbcd318" @@ -17171,6 +18984,13 @@ ora@^5.1.0, ora@^5.3.0: strip-ansi "^6.0.0" wcwidth "^1.0.1" +original@^1.0.0: + version "1.0.2" + resolved "https://registry.yarnpkg.com/original/-/original-1.0.2.tgz#e442a61cffe1c5fd20a65f3261c26663b303f25f" + integrity sha512-hyBVl6iqqUOJ8FqRe+l/gS8H+kKYjrEndd5Pm1MfBtsEKA038HkkdbAl/72EAXGyonD/PFsvmVG+EvcIpliMBg== + dependencies: + url-parse "^1.4.3" + os-browserify@0.3.0, os-browserify@^0.3.0: version "0.3.0" resolved "https://registry.yarnpkg.com/os-browserify/-/os-browserify-0.3.0.tgz#854373c7f5c2315914fc9bfc6bd8238fdda1ec27" @@ -17314,6 +19134,18 @@ p-map@^1.2.0: resolved "https://registry.yarnpkg.com/p-map/-/p-map-1.2.0.tgz#e4e94f311eabbc8633a1e79908165fca26241b6b" integrity sha512-r6zKACMNhjPJMTl8KcFH4li//gkrXWfbD6feV8l6doRHlzljFWGJ2AP6iKaCJXyZmAUMOPtvbW7EXkbWO/pLEA== +p-map@^2.0.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/p-map/-/p-map-2.1.0.tgz#310928feef9c9ecc65b68b17693018a665cea175" + integrity sha512-y3b8Kpd8OAN444hxfBbFfj1FY/RjtTd8tzYwhUqNYXx0fXx2iX4maP4Qr6qhIKbQXI02wTLAda4fYUbDagTUFw== + +p-map@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/p-map/-/p-map-4.0.0.tgz#bb2f95a5eda2ec168ec9274e06a747c3e2904d2b" + integrity sha512-/bjOqmgETBYB5BoEeGVea8dmvHb2m9GLy1E9W43yeyfP6QQCZGFNa+XRceJEuDB6zqr+gKpIAmlLebMpykw/MQ== + dependencies: + aggregate-error "^3.0.0" + p-pipe@^1.2.0: version "1.2.0" resolved "https://registry.yarnpkg.com/p-pipe/-/p-pipe-1.2.0.tgz#4b1a11399a11520a67790ee5a0c1d5881d6befe9" @@ -17324,6 +19156,13 @@ p-reduce@^1.0.0: resolved "https://registry.yarnpkg.com/p-reduce/-/p-reduce-1.0.0.tgz#18c2b0dd936a4690a529f8231f58a0fdb6a47dfa" integrity sha1-GMKw3ZNqRpClKfgjH1ig/bakffo= +p-retry@^3.0.1: + version "3.0.1" + resolved "https://registry.yarnpkg.com/p-retry/-/p-retry-3.0.1.tgz#316b4c8893e2c8dc1cfa891f406c4b422bebf328" + integrity sha512-XE6G4+YTTkT2a0UWb2kjZe8xNwf8bIbnqpc/IS/idOBVhyves0mK5OJgeocjx7q5pvX/6m23xuzVPYT1uGM73w== + dependencies: + retry "^0.12.0" + p-timeout@^2.0.1: version "2.0.1" resolved "https://registry.yarnpkg.com/p-timeout/-/p-timeout-2.0.1.tgz#d8dd1979595d2dc0139e1fe46b8b646cb3cdf038" @@ -17375,7 +19214,7 @@ packet-reader@1.0.0: resolved "https://registry.yarnpkg.com/packet-reader/-/packet-reader-1.0.0.tgz#9238e5480dedabacfe1fe3f2771063f164157d74" integrity sha512-HAKu/fG3HpHFO0AA8WE8q2g+gBJaZ9MG7fcKk+IJPLTGAD6Psw4443l+9DGRbOIh3/aXr7Phy0TjilYivJo5XQ== -pacote@^9.5.0: +pacote@9.5.12, pacote@^9.5.0: version "9.5.12" resolved "https://registry.yarnpkg.com/pacote/-/pacote-9.5.12.tgz#1e11dd7a8d736bcc36b375a9804d41bb0377bf66" integrity sha512-BUIj/4kKbwWg4RtnBncXPJd15piFSVNpTzY0rysSr3VnMowTYgkGKcaHrbReepAkjTr8lH2CVWRi58Spg2CicQ== @@ -17523,6 +19362,13 @@ parse-url@^5.0.0: parse-path "^4.0.0" protocols "^1.4.0" +parse5-htmlparser2-tree-adapter@6.0.1: + version "6.0.1" + resolved "https://registry.yarnpkg.com/parse5-htmlparser2-tree-adapter/-/parse5-htmlparser2-tree-adapter-6.0.1.tgz#2cdf9ad823321140370d4dbf5d3e92c7c8ddc6e6" + integrity sha512-qPuWvbLgvDGilKc5BoicRovlT4MtYT6JfJyBOMDsKoiT+GiuP5qyrPCnR9HcPECIJJmZh5jRndyNThnhhb/vlA== + dependencies: + parse5 "^6.0.1" + parse5@6.0.1, parse5@^6.0.1: version "6.0.1" resolved "https://registry.yarnpkg.com/parse5/-/parse5-6.0.1.tgz#e1a1c085c569b3dc08321184f19a39cc27f7c30b" @@ -17538,7 +19384,7 @@ parseuri@0.0.6: resolved "https://registry.yarnpkg.com/parseuri/-/parseuri-0.0.6.tgz#e1496e829e3ac2ff47f39a4dd044b32823c4a25a" integrity sha512-AUjen8sAkGgao7UyCX6Ahv0gIK2fABKmYjvP4xmy5JaKvcbTRueIqIPHLAfq30xJddqSE033IOMUSOMCcK3Sow== -parseurl@~1.3.3: +parseurl@~1.3.2, parseurl@~1.3.3: version "1.3.3" resolved "https://registry.yarnpkg.com/parseurl/-/parseurl-1.3.3.tgz#9da19e7bee8d12dff0513ed5b76957793bc2e8d4" integrity sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ== @@ -17593,6 +19439,11 @@ path-is-absolute@1.0.1, path-is-absolute@^1.0.0, path-is-absolute@^1.0.1: resolved "https://registry.yarnpkg.com/path-is-absolute/-/path-is-absolute-1.0.1.tgz#174b9268735534ffbc7ace6bf53a5a9e1b5c5f5f" integrity sha1-F0uSaHNVNP+8es5r9TpanhtcX18= +path-is-inside@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/path-is-inside/-/path-is-inside-1.0.2.tgz#365417dede44430d1c11af61027facf074bdfc53" + integrity sha1-NlQX3t5EQw0cEa9hAn+s8HS9/FM= + path-is-network-drive@^1.0.13: version "1.0.13" resolved "https://registry.yarnpkg.com/path-is-network-drive/-/path-is-network-drive-1.0.13.tgz#c9aa0183eb72c328aa83f43def93ddcb9d7ec4d4" @@ -17763,6 +19614,11 @@ pgpass@1.x: dependencies: split2 "^4.1.0" +picocolors@^0.2.1: + version "0.2.1" + resolved "https://registry.yarnpkg.com/picocolors/-/picocolors-0.2.1.tgz#570670f793646851d1ba135996962abad587859f" + integrity sha512-cMlDqaLEqfSaW8Z7N5Jw+lyIW869EzT73/F5lhtY9cLGoVxSXznfgfXMO0Z5K0o0Q2TkTXq+0KFsdnSe3jDViA== + picocolors@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/picocolors/-/picocolors-1.0.0.tgz#cb5bdc74ff3f51892236eaf79d68bc44564ab81c" @@ -17942,7 +19798,7 @@ pnp-webpack-plugin@1.6.4, pnp-webpack-plugin@^1.6.4: dependencies: ts-pnp "^1.1.6" -portfinder@^1.0.28: +portfinder@^1.0.26, portfinder@^1.0.28: version "1.0.28" resolved "https://registry.yarnpkg.com/portfinder/-/portfinder-1.0.28.tgz#67c4622852bd5374dd1dd900f779f53462fac778" integrity sha512-Se+2isanIcEqf2XMHjyUKskczxbPH7dQnlMjXX6+dybayyHvAf/TCgyMRlzf/B6QDhAEFOGes0pzRo3by4AbMA== @@ -18012,6 +19868,34 @@ postcss-discard-overridden@^4.0.1: dependencies: postcss "^7.0.0" +postcss-import@12.0.1: + version "12.0.1" + resolved "https://registry.yarnpkg.com/postcss-import/-/postcss-import-12.0.1.tgz#cf8c7ab0b5ccab5649024536e565f841928b7153" + integrity sha512-3Gti33dmCjyKBgimqGxL3vcV8w9+bsHwO5UrBawp796+jdardbcFl4RP5w/76BwNL7aGzpKstIfF9I+kdE8pTw== + dependencies: + postcss "^7.0.1" + postcss-value-parser "^3.2.3" + read-cache "^1.0.0" + resolve "^1.1.7" + +postcss-load-config@^2.0.0: + version "2.1.2" + resolved "https://registry.yarnpkg.com/postcss-load-config/-/postcss-load-config-2.1.2.tgz#c5ea504f2c4aef33c7359a34de3573772ad7502a" + integrity sha512-/rDeGV6vMUo3mwJZmeHfEDvwnTKKqQ0S7OHUi/kJvvtx3aWtyWG2/0ZWnzCt2keEclwN6Tf0DST2v9kITdOKYw== + dependencies: + cosmiconfig "^5.0.0" + import-cwd "^2.0.0" + +postcss-loader@3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/postcss-loader/-/postcss-loader-3.0.0.tgz#6b97943e47c72d845fa9e03f273773d4e8dd6c2d" + integrity sha512-cLWoDEY5OwHcAjDnkyRQzAXfs2jrKjXpO/HQFcc5b5u/r7aa471wdmChmwfnv7x2u840iat/wi0lQ5nbRgSkUA== + dependencies: + loader-utils "^1.1.0" + postcss "^7.0.0" + postcss-load-config "^2.0.0" + schema-utils "^1.0.0" + postcss-merge-longhand@^4.0.11: version "4.0.11" resolved "https://registry.yarnpkg.com/postcss-merge-longhand/-/postcss-merge-longhand-4.0.11.tgz#62f49a13e4a0ee04e7b98f42bb16062ca2549e24" @@ -18074,11 +19958,28 @@ postcss-minify-selectors@^4.0.2: postcss "^7.0.0" postcss-selector-parser "^3.0.0" +postcss-modules-extract-imports@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/postcss-modules-extract-imports/-/postcss-modules-extract-imports-2.0.0.tgz#818719a1ae1da325f9832446b01136eeb493cd7e" + integrity sha512-LaYLDNS4SG8Q5WAWqIJgdHPJrDDr/Lv775rMBFUbgjTz6j34lUznACHcdRWroPvXANP2Vj7yNK57vp9eFqzLWQ== + dependencies: + postcss "^7.0.5" + postcss-modules-extract-imports@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/postcss-modules-extract-imports/-/postcss-modules-extract-imports-3.0.0.tgz#cda1f047c0ae80c97dbe28c3e76a43b88025741d" integrity sha512-bdHleFnP3kZ4NYDhuGlVK+CMrQ/pqUm8bx/oGL93K6gVwiclvX5x0n76fYMKuIGKzlABOy13zsvqjb0f92TEXw== +postcss-modules-local-by-default@^3.0.3: + version "3.0.3" + resolved "https://registry.yarnpkg.com/postcss-modules-local-by-default/-/postcss-modules-local-by-default-3.0.3.tgz#bb14e0cc78279d504dbdcbfd7e0ca28993ffbbb0" + integrity sha512-e3xDq+LotiGesympRlKNgaJ0PCzoUIdpH0dj47iWAui/kyTgh3CiAr1qP54uodmJhl6p9rN6BoNcdEDVJx9RDw== + dependencies: + icss-utils "^4.1.1" + postcss "^7.0.32" + postcss-selector-parser "^6.0.2" + postcss-value-parser "^4.1.0" + postcss-modules-local-by-default@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/postcss-modules-local-by-default/-/postcss-modules-local-by-default-4.0.0.tgz#ebbb54fae1598eecfdf691a02b3ff3b390a5a51c" @@ -18088,6 +19989,14 @@ postcss-modules-local-by-default@^4.0.0: postcss-selector-parser "^6.0.2" postcss-value-parser "^4.1.0" +postcss-modules-scope@^2.2.0: + version "2.2.0" + resolved "https://registry.yarnpkg.com/postcss-modules-scope/-/postcss-modules-scope-2.2.0.tgz#385cae013cc7743f5a7d7602d1073a89eaae62ee" + integrity sha512-YyEgsTMRpNd+HmyC7H/mh3y+MeFWevy7V1evVhJWewmMbjDHIbZbOXICC2y+m1xI1UVfIT1HMW/O04Hxyu9oXQ== + dependencies: + postcss "^7.0.6" + postcss-selector-parser "^6.0.0" + postcss-modules-scope@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/postcss-modules-scope/-/postcss-modules-scope-3.0.0.tgz#9ef3151456d3bbfa120ca44898dfca6f2fa01f06" @@ -18095,6 +20004,14 @@ postcss-modules-scope@^3.0.0: dependencies: postcss-selector-parser "^6.0.4" +postcss-modules-values@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/postcss-modules-values/-/postcss-modules-values-3.0.0.tgz#5b5000d6ebae29b4255301b4a3a54574423e7f10" + integrity sha512-1//E5jCBrZ9DmRX+zCtmQtRSV6PV42Ix7Bzj9GbwJceduuf7IqP8MgeTXuRDHOWj2m0VzZD5+roFWDuU8RQjcg== + dependencies: + icss-utils "^4.0.0" + postcss "^7.0.6" + postcss-modules-values@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/postcss-modules-values/-/postcss-modules-values-4.0.0.tgz#d7c5e7e68c3bb3c9b27cbf48ca0bb3ffb4602c9c" @@ -18221,6 +20138,14 @@ postcss-selector-parser@^3.0.0: indexes-of "^1.0.1" uniq "^1.0.1" +postcss-selector-parser@^6.0.0: + version "6.0.10" + resolved "https://registry.yarnpkg.com/postcss-selector-parser/-/postcss-selector-parser-6.0.10.tgz#79b61e2c0d1bfc2602d549e11d0876256f8df88d" + integrity sha512-IQ7TZdoaqbT+LCpShg46jnZVlhWD2w6iQYAcYXfHARZ7X1t/UGhhceQDs5X0cGqKvYlHNOuv7Oa1xmb0oQuA3w== + dependencies: + cssesc "^3.0.0" + util-deprecate "^1.0.2" + postcss-selector-parser@^6.0.2, postcss-selector-parser@^6.0.4: version "6.0.4" resolved "https://registry.yarnpkg.com/postcss-selector-parser/-/postcss-selector-parser-6.0.4.tgz#56075a1380a04604c38b063ea7767a129af5c2b3" @@ -18250,7 +20175,18 @@ postcss-unique-selectors@^4.0.1: postcss "^7.0.0" uniqs "^2.0.0" -postcss-value-parser@^3.0.0: +postcss-url@^8.0.0: + version "8.0.0" + resolved "https://registry.yarnpkg.com/postcss-url/-/postcss-url-8.0.0.tgz#7b10059bd12929cdbb1971c60f61a0e5af86b4ca" + integrity sha512-E2cbOQ5aii2zNHh8F6fk1cxls7QVFZjLPSrqvmiza8OuXLzIpErij8BDS5Y3STPfJgpIMNCPEr8JlKQWEoozUw== + dependencies: + mime "^2.3.1" + minimatch "^3.0.4" + mkdirp "^0.5.0" + postcss "^7.0.2" + xxhashjs "^0.2.1" + +postcss-value-parser@^3.0.0, postcss-value-parser@^3.2.3: version "3.3.1" resolved "https://registry.yarnpkg.com/postcss-value-parser/-/postcss-value-parser-3.3.1.tgz#9ff822547e2893213cf1c30efa51ac5fd1ba8281" integrity sha512-pISE66AbVkp4fDQ7VHBwRNXzAAKJjw4Vw7nWI/+Q3vuly7SNfgYXvm6i5IgFylHGK5sP/xHAbB7N49OS4gWNyQ== @@ -18269,6 +20205,24 @@ postcss-values-parser@^2.0.1: indexes-of "^1.0.1" uniq "^1.0.1" +postcss@7.0.21: + version "7.0.21" + resolved "https://registry.yarnpkg.com/postcss/-/postcss-7.0.21.tgz#06bb07824c19c2021c5d056d5b10c35b989f7e17" + integrity sha512-uIFtJElxJo29QC753JzhidoAhvp/e/Exezkdhfmt8AymWT6/5B7W1WmponYWkHk2eg6sONyTch0A3nkMPun3SQ== + dependencies: + chalk "^2.4.2" + source-map "^0.6.1" + supports-color "^6.1.0" + +postcss@7.0.32: + version "7.0.32" + resolved "https://registry.yarnpkg.com/postcss/-/postcss-7.0.32.tgz#4310d6ee347053da3433db2be492883d62cec59d" + integrity sha512-03eXong5NLnNCD05xscnGKGDZ98CyzoqPSMjOe6SuoQY7Z2hIj0Ld1g/O/UQRuOle2aRtiIRDg9tDcTGAkLfKw== + dependencies: + chalk "^2.4.2" + source-map "^0.6.1" + supports-color "^6.1.0" + postcss@8.1.7: version "8.1.7" resolved "https://registry.yarnpkg.com/postcss/-/postcss-8.1.7.tgz#ff6a82691bd861f3354fd9b17b2332f88171233f" @@ -18288,6 +20242,14 @@ postcss@^7.0.0, postcss@^7.0.1, postcss@^7.0.27, postcss@^7.0.32: source-map "^0.6.1" supports-color "^6.1.0" +postcss@^7.0.14, postcss@^7.0.2, postcss@^7.0.29, postcss@^7.0.5, postcss@^7.0.6: + version "7.0.39" + resolved "https://registry.yarnpkg.com/postcss/-/postcss-7.0.39.tgz#9624375d965630e2e1f2c02a935c82a59cb48309" + integrity sha512-yioayjNbHn6z1/Bywyb2Y4s3yvDAeXGOyxqD+LnVOinq6Mdmd++SW2wUNVzavyyHxd6+DxzWGIuosg6P1Rj8uA== + dependencies: + picocolors "^0.2.1" + source-map "^0.6.1" + postcss@^8.1.7, postcss@^8.2.8: version "8.2.15" resolved "https://registry.yarnpkg.com/postcss/-/postcss-8.2.15.tgz#9e66ccf07292817d226fc315cbbf9bc148fbca65" @@ -18357,7 +20319,7 @@ prelude-ls@~1.1.2: resolved "https://registry.yarnpkg.com/prelude-ls/-/prelude-ls-1.1.2.tgz#21932a549f5e52ffd9a827f570e04be62a97da54" integrity sha1-IZMqVJ9eUv/ZqCf1cOBL5iqX2lQ= -prepend-http@^1.0.1: +prepend-http@^1.0.0, prepend-http@^1.0.1: version "1.0.4" resolved "https://registry.yarnpkg.com/prepend-http/-/prepend-http-1.0.4.tgz#d4f4562b0ce3696e41ac52d0e002e57a635dc6dc" integrity sha1-1PRWKwzjaW5BrFLQ4ALlemNdxtw= @@ -18698,7 +20660,7 @@ qs@~6.5.2: resolved "https://registry.yarnpkg.com/qs/-/qs-6.5.2.tgz#cb3ae806e8740444584ef154ce8ee98d403f3e36" integrity sha512-N5ZAX4/LxJmF+7wN74pUD6qAh9/wnvdQcjq9TZjevvXzSUo7bfmw91saqMjzGS2xq91/odN2dW/WOl7qQHNDGA== -query-string@^4.2.2: +query-string@^4.1.0, query-string@^4.2.2: version "4.3.4" resolved "https://registry.yarnpkg.com/query-string/-/query-string-4.3.4.tgz#bbb693b9ca915c232515b228b1a02b609043dbeb" integrity sha1-u7aTucqRXCMlFbIosaArYJBD2+s= @@ -18740,6 +20702,11 @@ querystring@^0.2.0: resolved "https://registry.yarnpkg.com/querystring/-/querystring-0.2.1.tgz#40d77615bb09d16902a85c3e38aa8b5ed761c2dd" integrity sha512-wkvS7mL/JMugcup3/rMitHmd9ecIGd2lhFhK9N3UUQ450h66d1r3Y9nvXzQAW1Lq+wyx61k/1pfKS5KuKiyEbg== +querystringify@^2.1.1: + version "2.2.0" + resolved "https://registry.yarnpkg.com/querystringify/-/querystringify-2.2.0.tgz#3345941b4153cb9d082d8eee4cda2016a9aef7f6" + integrity sha512-FIqgj2EUvTa7R50u0rGsyTftzjYmv/a3hO345bZNrqabNqjtgiDMgmo4mkUjd+nzU5oF3dClKqFIPUKybUyqoQ== + queue-microtask@^1.2.2: version "1.2.3" resolved "https://registry.yarnpkg.com/queue-microtask/-/queue-microtask-1.2.3.tgz#4929228bbc724dfac43e0efb058caf7b6cfb6243" @@ -18840,6 +20807,14 @@ raw-body@~1.1.0: bytes "1" string_decoder "0.10" +raw-loader@4.0.1: + version "4.0.1" + resolved "https://registry.yarnpkg.com/raw-loader/-/raw-loader-4.0.1.tgz#14e1f726a359b68437e183d5a5b7d33a3eba6933" + integrity sha512-baolhQBSi3iNh1cglJjA0mYzga+wePk7vdEX//1dTFd+v4TsQlQE0jitJSNF1OIP82rdYulH7otaVmdlDaJ64A== + dependencies: + loader-utils "^2.0.0" + schema-utils "^2.6.5" + rc@^1.0.1, rc@^1.1.6, rc@^1.2.7: version "1.2.8" resolved "https://registry.yarnpkg.com/rc/-/rc-1.2.8.tgz#cd924bf5200a075b83c188cd6b9e211b7fc0d3ed" @@ -18929,6 +20904,13 @@ react@^18.0.0: dependencies: loose-envify "^1.1.0" +read-cache@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/read-cache/-/read-cache-1.0.0.tgz#e664ef31161166c9751cdbe8dbcf86b5fb58f774" + integrity sha1-5mTvMRYRZsl1HNvo28+GtftY93Q= + dependencies: + pify "^2.3.0" + read-cmd-shim@^1.0.1: version "1.0.5" resolved "https://registry.yarnpkg.com/read-cmd-shim/-/read-cmd-shim-1.0.5.tgz#87e43eba50098ba5a32d0ceb583ab8e43b961c16" @@ -18946,7 +20928,7 @@ read-cmd-shim@^1.0.1: normalize-package-data "^2.0.0" npm-normalize-package-bin "^1.0.0" -read-package-tree@^5.1.6: +read-package-tree@5.3.1, read-package-tree@^5.1.6: version "5.3.1" resolved "https://registry.yarnpkg.com/read-package-tree/-/read-package-tree-5.3.1.tgz#a32cb64c7f31eb8a6f31ef06f9cedf74068fe636" integrity sha512-mLUDsD5JVtlZxjSlPPx1RETkNjjvQYuweKwNVt1Sn8kP5Jh44pvYuUHCp6xSVDZWbNxVxG5lyZJ921aJH61sTw== @@ -18987,6 +20969,14 @@ read-pkg-up@^4.0.0: find-up "^3.0.0" read-pkg "^3.0.0" +read-pkg-up@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/read-pkg-up/-/read-pkg-up-5.0.0.tgz#b6a6741cb144ed3610554f40162aa07a6db621b8" + integrity sha512-XBQjqOBtTzyol2CpsQOw8LHV0XbDZVG7xMMjmXAJomlVY03WOBRmYgDJETlvcg0H63AJvPRwT7GFi5rvOzUOKg== + dependencies: + find-up "^3.0.0" + read-pkg "^5.0.0" + read-pkg-up@^7.0.1: version "7.0.1" resolved "https://registry.yarnpkg.com/read-pkg-up/-/read-pkg-up-7.0.1.tgz#f3a6135758459733ae2b95638056e1854e7ef507" @@ -19023,7 +21013,7 @@ read-pkg@^3.0.0: normalize-package-data "^2.3.2" path-type "^3.0.0" -read-pkg@^5.2.0: +read-pkg@^5.0.0, read-pkg@^5.2.0: version "5.2.0" resolved "https://registry.yarnpkg.com/read-pkg/-/read-pkg-5.2.0.tgz#7bf295438ca5a33e56cd30e053b34ee7250c93cc" integrity sha512-Ug69mNOpfvKDAc2Q8DRpMjjzdtrnv9HcSMX+4VsZxD1aZ6ZzrIE7rlzXBtWTyhULSMKg076AW6WR5iZpD0JiOg== @@ -19053,7 +21043,7 @@ read@1, read@~1.0.1: string_decoder "~1.1.1" util-deprecate "~1.0.1" -"readable-stream@2 || 3", readable-stream@3, readable-stream@^3.0.0, readable-stream@^3.0.2, readable-stream@^3.1.1, readable-stream@^3.4.0, readable-stream@^3.5.0, readable-stream@^3.6.0: +"readable-stream@2 || 3", readable-stream@3, readable-stream@^3.0.0, readable-stream@^3.0.2, readable-stream@^3.0.6, readable-stream@^3.1.1, readable-stream@^3.4.0, readable-stream@^3.5.0, readable-stream@^3.6.0: version "3.6.0" resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-3.6.0.tgz#337bbda3adc0706bd3e024426a286d4b4b2c9198" integrity sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA== @@ -19098,6 +21088,13 @@ readdirp@~3.5.0: dependencies: picomatch "^2.2.1" +readdirp@~3.6.0: + version "3.6.0" + resolved "https://registry.yarnpkg.com/readdirp/-/readdirp-3.6.0.tgz#74a370bd857116e245b29cc97340cd431a02a6c7" + integrity sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA== + dependencies: + picomatch "^2.2.1" + realpath-native@^1.1.0: version "1.1.0" resolved "https://registry.yarnpkg.com/realpath-native/-/realpath-native-1.1.0.tgz#2003294fea23fb0672f2476ebe22fcf498a2d65c" @@ -19161,6 +21158,18 @@ redux@^4.0.5: loose-envify "^1.4.0" symbol-observable "^1.2.0" +reflect-metadata@^0.1.2: + version "0.1.13" + resolved "https://registry.yarnpkg.com/reflect-metadata/-/reflect-metadata-0.1.13.tgz#67ae3ca57c972a2aa1642b10fe363fe32d49dc08" + integrity sha512-Ts1Y/anZELhSsjMcU605fU9RE4Oi3p5ORujwbIKXfWa+0Zxs510Qrmrce5/Jowq3cHSZSJqBjypxmHarc+vEWg== + +regenerate-unicode-properties@^10.0.1: + version "10.0.1" + resolved "https://registry.yarnpkg.com/regenerate-unicode-properties/-/regenerate-unicode-properties-10.0.1.tgz#7f442732aa7934a3740c779bb9b3340dccc1fb56" + integrity sha512-vn5DU6yg6h8hP/2OkQo3K7uVILvY4iu0oI4t3HFa81UPkhGJwkRwM10JEc3upjdhHjs/k8GJY1sRBhk5sr69Bw== + dependencies: + regenerate "^1.4.2" + regenerate-unicode-properties@^8.2.0: version "8.2.0" resolved "https://registry.yarnpkg.com/regenerate-unicode-properties/-/regenerate-unicode-properties-8.2.0.tgz#e5de7111d655e7ba60c057dbe9ff37c87e65cdec" @@ -19168,11 +21177,16 @@ regenerate-unicode-properties@^8.2.0: dependencies: regenerate "^1.4.0" -regenerate@^1.2.1, regenerate@^1.4.0: +regenerate@^1.2.1, regenerate@^1.4.0, regenerate@^1.4.2: version "1.4.2" resolved "https://registry.yarnpkg.com/regenerate/-/regenerate-1.4.2.tgz#b9346d8827e8f5a32f7ba29637d398b69014848a" integrity sha512-zrceR/XhGYU/d/opr2EKO7aRHUeiBI8qjtfHqADTwZd6Szfy16la6kqD0MIUs5z5hx6AaKa+PixpPrR289+I0A== +regenerator-runtime@0.13.7, regenerator-runtime@^0.13.4: + version "0.13.7" + resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.13.7.tgz#cac2dacc8a1ea675feaabaeb8ae833898ae46f55" + integrity sha512-a54FxoJDIr27pgf7IgeQGxmqUNYrcV338lf/6gH456HZ/PhX+5BcwHXG9ajESmwe6WRO0tAzRUrRmNONWgkrew== + regenerator-runtime@^0.10.5: version "0.10.5" resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.10.5.tgz#336c3efc1220adcedda2c9fab67b5a7955a33658" @@ -19183,11 +21197,6 @@ regenerator-runtime@^0.11.0: resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.11.1.tgz#be05ad7f9bf7d22e056f9726cee5017fbf19e2e9" integrity sha512-MguG95oij0fC3QV3URf4V2SDYGJhJnJGqvIIgdECeODCT98wSWDAJ94SSuVpYQUoTcGUIL6L4yNB7j1DFFHSBg== -regenerator-runtime@^0.13.4: - version "0.13.7" - resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.13.7.tgz#cac2dacc8a1ea675feaabaeb8ae833898ae46f55" - integrity sha512-a54FxoJDIr27pgf7IgeQGxmqUNYrcV338lf/6gH456HZ/PhX+5BcwHXG9ajESmwe6WRO0tAzRUrRmNONWgkrew== - regenerator-runtime@^0.9.5: version "0.9.6" resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.9.6.tgz#d33eb95d0d2001a4be39659707c51b0cb71ce029" @@ -19209,13 +21218,34 @@ regenerator-transform@^0.14.2: dependencies: "@babel/runtime" "^7.8.4" +regenerator-transform@^0.15.0: + version "0.15.0" + resolved "https://registry.yarnpkg.com/regenerator-transform/-/regenerator-transform-0.15.0.tgz#cbd9ead5d77fae1a48d957cf889ad0586adb6537" + integrity sha512-LsrGtPmbYg19bcPHwdtmXwbW+TqNvtY4riE3P83foeHRroMbH6/2ddFBfab3t7kbzc7v7p4wbkIecHImqt0QNg== + dependencies: + "@babel/runtime" "^7.8.4" + regex-not@^1.0.0, regex-not@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/regex-not/-/regex-not-1.0.2.tgz#1f4ece27e00b0b65e0247a6810e6a85d83a5752c" integrity sha512-J6SDjUgDxQj5NusnOtdFxDwN/+HWykR8GELwctJ7mdqhcyy1xEc4SRFHUXvxTp661YaVKAjfRLZ9cCqS6tn32A== dependencies: - extend-shallow "^3.0.2" - safe-regex "^1.1.0" + extend-shallow "^3.0.2" + safe-regex "^1.1.0" + +regex-parser@^2.2.11: + version "2.2.11" + resolved "https://registry.yarnpkg.com/regex-parser/-/regex-parser-2.2.11.tgz#3b37ec9049e19479806e878cabe7c1ca83ccfe58" + integrity sha512-jbD/FT0+9MBU2XAZluI7w2OBs1RBi6p9M83nkoZayQXXU9e8Robt69FcZc7wU4eJD/YFTjn1JdCk3rbMJajz8Q== + +regexp.prototype.flags@^1.2.0: + version "1.4.3" + resolved "https://registry.yarnpkg.com/regexp.prototype.flags/-/regexp.prototype.flags-1.4.3.tgz#87cab30f80f66660181a3bb7bf5981a872b367ac" + integrity sha512-fjggEOO3slI6Wvgjwflkc4NFRCTZAu5CnNfBd5qOMYhWdn67nJBBu34/TkD++eeFmd8C9r9jfXJ27+nSiRkSUA== + dependencies: + call-bind "^1.0.2" + define-properties "^1.1.3" + functions-have-names "^1.2.2" regexp.prototype.flags@^1.3.1: version "1.3.1" @@ -19251,6 +21281,18 @@ regexpu-core@^4.7.1: unicode-match-property-ecmascript "^1.0.4" unicode-match-property-value-ecmascript "^1.2.0" +regexpu-core@^5.0.1: + version "5.0.1" + resolved "https://registry.yarnpkg.com/regexpu-core/-/regexpu-core-5.0.1.tgz#c531122a7840de743dcf9c83e923b5560323ced3" + integrity sha512-CriEZlrKK9VJw/xQGJpQM5rY88BtuL8DM+AEwvcThHilbxiTAy8vq4iJnd2tqq8wLmjbGZzP7ZcKFjbGkmEFrw== + dependencies: + regenerate "^1.4.2" + regenerate-unicode-properties "^10.0.1" + regjsgen "^0.6.0" + regjsparser "^0.8.2" + unicode-match-property-ecmascript "^2.0.0" + unicode-match-property-value-ecmascript "^2.0.0" + regextras@^0.7.1: version "0.7.1" resolved "https://registry.yarnpkg.com/regextras/-/regextras-0.7.1.tgz#be95719d5f43f9ef0b9fa07ad89b7c606995a3b2" @@ -19281,6 +21323,11 @@ regjsgen@^0.5.1: resolved "https://registry.yarnpkg.com/regjsgen/-/regjsgen-0.5.2.tgz#92ff295fb1deecbf6ecdab2543d207e91aa33733" integrity sha512-OFFT3MfrH90xIW8OOSyUrk6QHD5E9JOTeGodiJeBS3J6IwlgzJMNE/1bZklWz5oTg+9dCMyEetclvCVXOPoN3A== +regjsgen@^0.6.0: + version "0.6.0" + resolved "https://registry.yarnpkg.com/regjsgen/-/regjsgen-0.6.0.tgz#83414c5354afd7d6627b16af5f10f41c4e71808d" + integrity sha512-ozE883Uigtqj3bx7OhL1KNbCzGyW2NQZPl6Hs09WTvCuZD5sTI4JY58bkbQWa/Y9hxIsvJ3M8Nbf7j54IqeZbA== + regjsparser@^0.1.4: version "0.1.5" resolved "https://registry.yarnpkg.com/regjsparser/-/regjsparser-0.1.5.tgz#7ee8f84dc6fa792d3fd0ae228d24bd949ead205c" @@ -19295,6 +21342,13 @@ regjsparser@^0.6.4: dependencies: jsesc "~0.5.0" +regjsparser@^0.8.2: + version "0.8.4" + resolved "https://registry.yarnpkg.com/regjsparser/-/regjsparser-0.8.4.tgz#8a14285ffcc5de78c5b95d62bbf413b6bc132d5f" + integrity sha512-J3LABycON/VNEu3abOviqGHuB/LOtOQj8SKmfP9anY5GfAVw/SPjwzSjxGjbZXIxbGfqTHtJw58C2Li/WkStmA== + dependencies: + jsesc "~0.5.0" + relateurl@^0.2.7: version "0.2.7" resolved "https://registry.yarnpkg.com/relateurl/-/relateurl-0.2.7.tgz#54dbf377e51440aca90a4cd274600d3ff2d888a9" @@ -19361,7 +21415,7 @@ replace-in-file@^4.0.0: glob "^7.1.6" yargs "^15.0.2" -request@^2.87.0, request@^2.88.0: +request@^2.87.0, request@^2.88.0, request@^2.88.2: version "2.88.2" resolved "https://registry.yarnpkg.com/request/-/request-2.88.2.tgz#d73c918731cb5a87da047e207234146f664d12b3" integrity sha512-MsvtOrfG9ZcrOwAW+Qi+F6HbD0CWXEh9ou77uOb7FM2WPhwT7smM833PzanhJLsgXjN89Ir6V2PczXNnMpwKhw== @@ -19544,6 +21598,22 @@ resolve-pathname@^3.0.0: resolved "https://registry.yarnpkg.com/resolve-pathname/-/resolve-pathname-3.0.0.tgz#99d02224d3cf263689becbb393bc560313025dcd" integrity sha512-C7rARubxI8bXFNB/hqcp/4iUeIXJhJZvFPFPiSPRnhU5UPxzMFIl+2E6yY6c4k9giDJAhtV+enfA+G89N6Csng== +resolve-url-loader@3.1.2: + version "3.1.2" + resolved "https://registry.yarnpkg.com/resolve-url-loader/-/resolve-url-loader-3.1.2.tgz#235e2c28e22e3e432ba7a5d4e305c59a58edfc08" + integrity sha512-QEb4A76c8Mi7I3xNKXlRKQSlLBwjUV/ULFMP+G7n3/7tJZ8MG5wsZ3ucxP1Jz8Vevn6fnJsxDx9cIls+utGzPQ== + dependencies: + adjust-sourcemap-loader "3.0.0" + camelcase "5.3.1" + compose-function "3.0.3" + convert-source-map "1.7.0" + es6-iterator "2.0.3" + loader-utils "1.2.3" + postcss "7.0.21" + rework "1.0.1" + rework-visit "1.0.0" + source-map "0.6.1" + resolve-url@^0.2.1: version "0.2.1" resolved "https://registry.yarnpkg.com/resolve-url/-/resolve-url-0.2.1.tgz#2c637fe77c893afd2a663fe21aa9080068e2052a" @@ -19568,6 +21638,15 @@ resolve@^1.1.6: path-parse "^1.0.7" supports-preserve-symlinks-flag "^1.0.0" +resolve@^1.1.7, resolve@^1.3.2, resolve@^1.8.1: + version "1.22.0" + resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.22.0.tgz#5e0b8c67c15df57a89bdbabe603a002f21731198" + integrity sha512-Hhtrw0nLeSrFQ7phPp4OOcVjLPIeMnRlr5mcnVuMe7M/7eBn98A3hmFRLoFo3DLZkivSYwhRUJTyPyWAk56WLw== + dependencies: + is-core-module "^2.8.1" + path-parse "^1.0.7" + supports-preserve-symlinks-flag "^1.0.0" + resolve@^1.10.0, resolve@^1.10.1, resolve@^1.11.1, resolve@^1.12.0, resolve@^1.13.1, resolve@^1.14.2, resolve@^1.17.0, resolve@^1.19.0, resolve@^1.20.0, resolve@^1.3.3, resolve@^1.4.0, resolve@^1.5.0: version "1.20.0" resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.20.0.tgz#629a013fb3f70755d6f0b7935cc1c2c5378b1975" @@ -19634,6 +21713,19 @@ reusify@^1.0.4: resolved "https://registry.yarnpkg.com/reusify/-/reusify-1.0.4.tgz#90da382b1e126efc02146e90845a88db12925d76" integrity sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw== +rework-visit@1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/rework-visit/-/rework-visit-1.0.0.tgz#9945b2803f219e2f7aca00adb8bc9f640f842c9a" + integrity sha1-mUWygD8hni96ygCtuLyfZA+ELJo= + +rework@1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/rework/-/rework-1.0.1.tgz#30806a841342b54510aa4110850cd48534144aa7" + integrity sha1-MIBqhBNCtUUQqkEQhQzUhTQUSqc= + dependencies: + convert-source-map "^0.3.3" + css "^2.0.0" + rfdc@^1.1.4, rfdc@^1.3.0: version "1.3.0" resolved "https://registry.yarnpkg.com/rfdc/-/rfdc-1.3.0.tgz#d0b7c441ab2720d05dc4cf26e01c89631d9da08b" @@ -19656,7 +21748,7 @@ rimraf@2, rimraf@^2.2.8, rimraf@^2.3.4, rimraf@^2.4.3, rimraf@^2.4.4, rimraf@^2. dependencies: glob "^7.1.3" -rimraf@^3.0.0, rimraf@^3.0.1, rimraf@^3.0.2: +rimraf@3.0.2, rimraf@^3.0.0, rimraf@^3.0.1, rimraf@^3.0.2: version "3.0.2" resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-3.0.2.tgz#f1a5402ba6220ad52cc1282bac1ae3aa49fd061a" integrity sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA== @@ -19700,6 +21792,14 @@ rollup-plugin-license@^2.6.1: spdx-expression-validate "2.0.0" spdx-satisfies "5.0.1" +rollup-plugin-sourcemaps@^0.6.0: + version "0.6.3" + resolved "https://registry.yarnpkg.com/rollup-plugin-sourcemaps/-/rollup-plugin-sourcemaps-0.6.3.tgz#bf93913ffe056e414419607f1d02780d7ece84ed" + integrity sha512-paFu+nT1xvuO1tPFYXGe+XnQvg4Hjqv/eIhG8i5EspfYYPBKL57X7iVbfv55aNVASg3dzWvES9dmWsL2KhfByw== + dependencies: + "@rollup/pluginutils" "^3.0.9" + source-map-resolve "^0.6.0" + rollup-plugin-terser@^7.0.2: version "7.0.2" resolved "https://registry.yarnpkg.com/rollup-plugin-terser/-/rollup-plugin-terser-7.0.2.tgz#e8fbba4869981b2dc35ae7e8a502d5c6c04d324d" @@ -19722,6 +21822,13 @@ rollup-plugin-typescript2@^0.31.2: resolve "^1.20.0" tslib "^2.3.1" +rollup@2.26.5: + version "2.26.5" + resolved "https://registry.yarnpkg.com/rollup/-/rollup-2.26.5.tgz#5562ec36fcba3eed65cfd630bd78e037ad0e0307" + integrity sha512-rCyFG3ZtQdnn9YwfuAVH0l/Om34BdO5lwCA0W6Hq+bNB21dVEBbCRxhaHOmu1G7OBFDWytbzAC104u7rxHwGjA== + optionalDependencies: + fsevents "~2.1.2" + rollup@^2.67.1: version "2.67.1" resolved "https://registry.yarnpkg.com/rollup/-/rollup-2.67.1.tgz#4402665706fa00f321d446ce45f880e02cf54f01" @@ -19729,6 +21836,13 @@ rollup@^2.67.1: optionalDependencies: fsevents "~2.3.2" +rollup@^2.8.0: + version "2.70.2" + resolved "https://registry.yarnpkg.com/rollup/-/rollup-2.70.2.tgz#808d206a8851628a065097b7ba2053bd83ba0c0d" + integrity sha512-EitogNZnfku65I1DD5Mxe8JYRUCy0hkK5X84IlDtUs+O6JRMpRciXTzyCUuX11b5L5pvjH+OmFXiQ3XjabcXgg== + optionalDependencies: + fsevents "~2.3.2" + rsvp@^3.0.14, rsvp@^3.0.17, rsvp@^3.0.18, rsvp@^3.0.21, rsvp@^3.0.6, rsvp@^3.1.0: version "3.6.2" resolved "https://registry.yarnpkg.com/rsvp/-/rsvp-3.6.2.tgz#2e96491599a96cde1b515d5674a8f7a91452926a" @@ -19768,7 +21882,14 @@ rx@^4.1.0: resolved "https://registry.yarnpkg.com/rx/-/rx-4.1.0.tgz#a5f13ff79ef3b740fe30aa803fb09f98805d4782" integrity sha1-pfE/957zt0D+MKqAP7CfmIBdR4I= -rxjs@^6.4.0, rxjs@^6.6.0: +rxjs@6.6.2: + version "6.6.2" + resolved "https://registry.yarnpkg.com/rxjs/-/rxjs-6.6.2.tgz#8096a7ac03f2cc4fe5860ef6e572810d9e01c0d2" + integrity sha512-BHdBMVoWC2sL26w//BCu3YzKT4s2jip/WhwsGEDmeKYBhKDZeYezVUnHatYB7L85v5xs0BAQmg6BEYJEKxBabg== + dependencies: + tslib "^1.9.0" + +rxjs@^6.4.0, rxjs@^6.5.0, rxjs@^6.6.0: version "6.6.7" resolved "https://registry.yarnpkg.com/rxjs/-/rxjs-6.6.7.tgz#90ac018acabf491bf65044235d5863c4dab804c9" integrity sha512-hTdwr+7yYNIT5n4AMYp85KA6yw2Va0FLa3Rguvbpa4W3I5xynaBZo41cM3XM+4Q6fRMj3sBYIR1VAmZMXYJvRQ== @@ -19797,7 +21918,7 @@ safe-regex@^1.1.0: dependencies: ret "~0.1.10" -"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", safer-buffer@^2.0.2, safer-buffer@^2.1.0, safer-buffer@^2.1.2, 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== @@ -19824,6 +21945,17 @@ saslprep@^1.0.0: dependencies: sparse-bitfield "^3.0.3" +sass-loader@10.0.1: + version "10.0.1" + resolved "https://registry.yarnpkg.com/sass-loader/-/sass-loader-10.0.1.tgz#10c0364d8034f22fee25ddcc9eded20f99bbe3b4" + integrity sha512-b2PSldKVTS3JcFPHSrEXh3BeAfR7XknGiGCAO5aHruR3Pf3kqLP3Gb2ypXLglRrAzgZkloNxLZ7GXEGDX0hBUQ== + dependencies: + klona "^2.0.3" + loader-utils "^2.0.0" + neo-async "^2.6.2" + schema-utils "^2.7.0" + semver "^7.3.2" + sass-lookup@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/sass-lookup/-/sass-lookup-3.0.0.tgz#3b395fa40569738ce857bc258e04df2617c48cac" @@ -19831,6 +21963,22 @@ sass-lookup@^3.0.0: dependencies: commander "^2.16.0" +sass@1.26.10: + version "1.26.10" + resolved "https://registry.yarnpkg.com/sass/-/sass-1.26.10.tgz#851d126021cdc93decbf201d1eca2a20ee434760" + integrity sha512-bzN0uvmzfsTvjz0qwccN1sPm2HxxpNI/Xa+7PlUEMS+nQvbyuEK7Y0qFqxlPHhiNHb1Ze8WQJtU31olMObkAMw== + dependencies: + chokidar ">=2.0.0 <4.0.0" + +sass@^1.23.0: + version "1.50.1" + resolved "https://registry.yarnpkg.com/sass/-/sass-1.50.1.tgz#e9b078a1748863013c4712d2466ce8ca4e4ed292" + integrity sha512-noTnY41KnlW2A9P8sdwESpDmo+KBNkukI1i8+hOK3footBUcohNHtdOJbckp46XO95nuvcHDDZ+4tmOnpK3hjw== + dependencies: + chokidar ">=3.0.0 <4.0.0" + immutable "^4.0.0" + source-map-js ">=0.6.2 <2.0.0" + sax@1.2.1: version "1.2.1" resolved "https://registry.yarnpkg.com/sax/-/sax-1.2.1.tgz#7b8e656190b228e81a66aea748480d828cd2d37a" @@ -19864,7 +22012,7 @@ schema-utils@^1.0.0: ajv-errors "^1.0.0" ajv-keywords "^3.1.0" -schema-utils@^2.6.5: +schema-utils@^2.6.5, schema-utils@^2.6.6, schema-utils@^2.7.0: version "2.7.1" resolved "https://registry.yarnpkg.com/schema-utils/-/schema-utils-2.7.1.tgz#1ca4f32d1b24c590c203b8e7a50bf0ea4cd394d7" integrity sha512-SHiNtMOUGWBQJwzISiVYKu82GiV4QYGePp3odlY1tuKO7gPtphAT5R/py0fA6xtbgLL/RvtJZnU9b8s0F1q0Xg== @@ -19891,7 +22039,26 @@ schema-utils@^3.1.0, schema-utils@^3.1.1: ajv "^6.12.5" ajv-keywords "^3.5.2" -"semver@2 || 3 || 4 || 5", "semver@2.x || 3.x || 4 || 5", semver@^5.1.0, semver@^5.3.0, semver@^5.4.1, semver@^5.5.0, semver@^5.5.1, semver@^5.6.0, semver@^5.7.0: +select-hose@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/select-hose/-/select-hose-2.0.0.tgz#625d8658f865af43ec962bfc376a37359a4994ca" + integrity sha1-Yl2GWPhlr0Psliv8N2o3NZpJlMo= + +selfsigned@^1.10.7: + version "1.10.14" + resolved "https://registry.yarnpkg.com/selfsigned/-/selfsigned-1.10.14.tgz#ee51d84d9dcecc61e07e4aba34f229ab525c1574" + integrity sha512-lkjaiAye+wBZDCBsu5BGi0XiLRxeUlsGod5ZP924CRSEoGuZAw/f7y9RKu28rwTfiHVhdavhB0qH0INV6P1lEA== + dependencies: + node-forge "^0.10.0" + +semver-intersect@1.4.0: + version "1.4.0" + resolved "https://registry.yarnpkg.com/semver-intersect/-/semver-intersect-1.4.0.tgz#bdd9c06bedcdd2fedb8cd352c3c43ee8c61321f3" + integrity sha512-d8fvGg5ycKAq0+I6nfWeCx6ffaWJCsBYU0H2Rq56+/zFePYfT8mXkB3tWBSjR5BerkHNZ5eTPIk1/LBYas35xQ== + dependencies: + semver "^5.0.0" + +"semver@2 || 3 || 4 || 5", "semver@2.x || 3.x || 4 || 5", semver@^5.0.0, semver@^5.1.0, semver@^5.3.0, semver@^5.4.1, semver@^5.5.0, semver@^5.5.1, semver@^5.6.0, semver@^5.7.0: version "5.7.1" resolved "https://registry.yarnpkg.com/semver/-/semver-5.7.1.tgz#a954f931aeba508d307bbf069eff0c01c96116f7" integrity sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ== @@ -19901,6 +22068,11 @@ semver@7.0.0: resolved "https://registry.yarnpkg.com/semver/-/semver-7.0.0.tgz#5f3ca35761e47e05b206c6daff2cf814f0316b8e" integrity sha512-+GB6zVA9LWh6zovYQLALHwv5rb2PHGlJi3lfiqIHxR0uuwCgefcOJc59v9fv1w8GbStwxuuqqAjI9NMAOOgq1A== +semver@7.3.2: + version "7.3.2" + resolved "https://registry.yarnpkg.com/semver/-/semver-7.3.2.tgz#604962b052b81ed0786aae84389ffba70ffd3938" + integrity sha512-OrOb32TeeambH6UrhtShmF7CRDqhL6/5XpPNp2DuRH6+9QLw/orhp72j87v8Qa1ScDkvrrBNpZcDejAirJmfXQ== + semver@7.3.4: version "7.3.4" resolved "https://registry.yarnpkg.com/semver/-/semver-7.3.4.tgz#27aaa7d2e4ca76452f98d3add093a72c943edc97" @@ -19920,6 +22092,13 @@ semver@^6.0.0, semver@^6.1.0, semver@^6.1.1, semver@^6.1.2, semver@^6.2.0, semve resolved "https://registry.yarnpkg.com/semver/-/semver-6.3.0.tgz#ee0a64c8af5e8ceea67687b133761e1becbd1d3d" integrity sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw== +semver@^7.0.0, semver@^7.1.1: + version "7.3.7" + resolved "https://registry.yarnpkg.com/semver/-/semver-7.3.7.tgz#12c5b649afdbf9049707796e22a4028814ce523f" + integrity sha512-QlYTucUYOews+WeEujDoEGziz4K6c47V/Bd+LjSSYcA94p+DmINdf7ncaUinThfvZyu13lN9OY1XDxt8C0Tw0g== + dependencies: + lru-cache "^6.0.0" + semver@^7.1.3, semver@^7.2.1, semver@^7.3.2, semver@^7.3.4, semver@^7.3.5: version "7.3.5" resolved "https://registry.yarnpkg.com/semver/-/semver-7.3.5.tgz#0b621c879348d8998e4b0e4be94b3f12e6018ef7" @@ -19984,6 +22163,19 @@ serialize-javascript@^6.0.0: dependencies: randombytes "^2.1.0" +serve-index@^1.9.1: + version "1.9.1" + resolved "https://registry.yarnpkg.com/serve-index/-/serve-index-1.9.1.tgz#d3768d69b1e7d82e5ce050fff5b453bea12a9239" + integrity sha1-03aNabHn2C5c4FD/9bRTvqEqkjk= + dependencies: + accepts "~1.3.4" + batch "0.6.1" + debug "2.6.9" + escape-html "~1.0.3" + http-errors "~1.6.2" + mime-types "~2.1.17" + parseurl "~1.3.2" + serve-static@1.14.1: version "1.14.1" resolved "https://registry.yarnpkg.com/serve-static/-/serve-static-1.14.1.tgz#666e636dc4f010f7ef29970a88a674320898b2f9" @@ -20346,6 +22538,27 @@ socket.io@^4.2.0: socket.io-adapter "~2.3.3" socket.io-parser "~4.0.4" +sockjs-client@1.4.0: + version "1.4.0" + resolved "https://registry.yarnpkg.com/sockjs-client/-/sockjs-client-1.4.0.tgz#c9f2568e19c8fd8173b4997ea3420e0bb306c7d5" + integrity sha512-5zaLyO8/nri5cua0VtOrFXBPK1jbL4+1cebT/mmKA1E1ZXOvJrII75bPu0l0k843G/+iAbhEqzyKr0w/eCCj7g== + dependencies: + debug "^3.2.5" + eventsource "^1.0.7" + faye-websocket "~0.11.1" + inherits "^2.0.3" + json3 "^3.3.2" + url-parse "^1.4.3" + +sockjs@0.3.20: + version "0.3.20" + resolved "https://registry.yarnpkg.com/sockjs/-/sockjs-0.3.20.tgz#b26a283ec562ef8b2687b44033a4eeceac75d855" + integrity sha512-SpmVOVpdq0DJc0qArhF3E5xsxvaiqGNb73XfgBpK1y3UD5gs8DSo8aCTsuT5pX8rssdc2NDIzANwP9eCAiSdTA== + dependencies: + faye-websocket "^0.10.0" + uuid "^3.4.0" + websocket-driver "0.6.5" + socks-proxy-agent@^4.0.0: version "4.0.2" resolved "https://registry.yarnpkg.com/socks-proxy-agent/-/socks-proxy-agent-4.0.2.tgz#3c8991f3145b2799e70e11bd5fbc8b1963116386" @@ -20379,6 +22592,13 @@ socks@~2.3.2: ip "1.1.5" smart-buffer "^4.1.0" +sort-keys@^1.0.0: + version "1.1.2" + resolved "https://registry.yarnpkg.com/sort-keys/-/sort-keys-1.1.2.tgz#441b6d4d346798f1b4e49e8920adfba0e543f9ad" + integrity sha1-RBttTTRnmPG05J6JIK37oOVD+a0= + dependencies: + is-plain-obj "^1.0.0" + sort-keys@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/sort-keys/-/sort-keys-2.0.0.tgz#658535584861ec97d730d6cf41822e1f56684128" @@ -20408,12 +22628,28 @@ source-list-map@^2.0.0: resolved "https://registry.yarnpkg.com/source-list-map/-/source-list-map-2.0.1.tgz#3993bd873bfc48479cca9ea3a547835c7c154b34" integrity sha512-qnQ7gVMxGNxsiL4lEuJwe/To8UnK7fAnmbGEEH8RpLouuKbeEm0lhbQVFIrNSuB+G7tVrAlVsZgETT5nljf+Iw== +"source-map-js@>=0.6.2 <2.0.0": + version "1.0.2" + resolved "https://registry.yarnpkg.com/source-map-js/-/source-map-js-1.0.2.tgz#adbc361d9c62df380125e7f161f71c826f1e490c" + integrity sha512-R0XvVJ9WusLiqTCEiGCmICCMplcCkIwwR11mOSD9CR5u+IXYdiseeEuXCVAjS54zqwkLcPNnmU4OeJ6tUrWhDw== + source-map-js@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/source-map-js/-/source-map-js-1.0.1.tgz#a1741c131e3c77d048252adfa24e23b908670caf" integrity sha512-4+TN2b3tqOCd/kaGRJ/sTYA0tR0mdXx26ipdolxcwtJVqEnqNYvlCAt1q3ypy4QMlYus+Zh34RNtYLoq2oQ4IA== -source-map-resolve@^0.5.0: +source-map-loader@1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/source-map-loader/-/source-map-loader-1.0.2.tgz#b0a6582b2eaa387ede1ecf8061ae0b93c23f9eb0" + integrity sha512-oX8d6ndRjN+tVyjj6PlXSyFPhDdVAPsZA30nD3/II8g4uOv8fCz0DMn5sy8KtVbDfKQxOpGwGJnK3xIW3tauDw== + dependencies: + data-urls "^2.0.0" + iconv-lite "^0.6.2" + loader-utils "^2.0.0" + schema-utils "^2.7.0" + source-map "^0.6.1" + +source-map-resolve@^0.5.0, source-map-resolve@^0.5.2: version "0.5.3" resolved "https://registry.yarnpkg.com/source-map-resolve/-/source-map-resolve-0.5.3.tgz#190866bece7553e1f8f267a2ee82c606b5509a1a" integrity sha512-Htz+RnsXWk5+P2slx5Jh3Q66vhQj1Cllm0zvnaY98+NFx+Dv2CF/f5O/t8x+KaNdrdIAsruNzoh/KpialbqAnw== @@ -20424,6 +22660,22 @@ source-map-resolve@^0.5.0: source-map-url "^0.4.0" urix "^0.1.0" +source-map-resolve@^0.6.0: + version "0.6.0" + resolved "https://registry.yarnpkg.com/source-map-resolve/-/source-map-resolve-0.6.0.tgz#3d9df87e236b53f16d01e58150fc7711138e5ed2" + integrity sha512-KXBr9d/fO/bWo97NXsPIAW1bFSBOuCnjbNTBMO7N59hsv5i9yzRDfcYwwt0l04+VqnKC+EwzvJZIP/qkuMgR/w== + dependencies: + atob "^2.1.2" + decode-uri-component "^0.2.0" + +source-map-support@0.5.19, source-map-support@^0.5.6, source-map-support@~0.5.12: + version "0.5.19" + resolved "https://registry.yarnpkg.com/source-map-support/-/source-map-support-0.5.19.tgz#a98b62f86dcaf4f67399648c085291ab9e8fed61" + integrity sha512-Wonm7zOCIJzBGQdB+thsPar0kYuCIzYvxZwlBa87yi/Mdjv7Tip2cyVbLj5o0cFPN4EVkuTwb3GDDyUx2DGnGw== + dependencies: + buffer-from "^1.0.0" + source-map "^0.6.0" + source-map-support@^0.4.15, source-map-support@^0.4.18: version "0.4.18" resolved "https://registry.yarnpkg.com/source-map-support/-/source-map-support-0.4.18.tgz#0286a6de8be42641338594e97ccea75f0a2c585f" @@ -20431,7 +22683,7 @@ source-map-support@^0.4.15, source-map-support@^0.4.18: dependencies: source-map "^0.5.6" -source-map-support@^0.5.17, source-map-support@~0.5.20: +source-map-support@^0.5.17, source-map-support@^0.5.5, source-map-support@~0.5.20: version "0.5.21" resolved "https://registry.yarnpkg.com/source-map-support/-/source-map-support-0.5.21.tgz#04fe7c7f9e1ed2d662233c28cb2b35b9f63f6e4f" integrity sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w== @@ -20439,14 +22691,6 @@ source-map-support@^0.5.17, source-map-support@~0.5.20: buffer-from "^1.0.0" source-map "^0.6.0" -source-map-support@^0.5.6, source-map-support@~0.5.12: - version "0.5.19" - resolved "https://registry.yarnpkg.com/source-map-support/-/source-map-support-0.5.19.tgz#a98b62f86dcaf4f67399648c085291ab9e8fed61" - integrity sha512-Wonm7zOCIJzBGQdB+thsPar0kYuCIzYvxZwlBa87yi/Mdjv7Tip2cyVbLj5o0cFPN4EVkuTwb3GDDyUx2DGnGw== - dependencies: - buffer-from "^1.0.0" - source-map "^0.6.0" - source-map-url@^0.3.0: version "0.3.0" resolved "https://registry.yarnpkg.com/source-map-url/-/source-map-url-0.3.0.tgz#7ecaf13b57bcd09da8a40c5d269db33799d4aaf9" @@ -20500,7 +22744,7 @@ source-map@~0.2.0: dependencies: amdefine ">=0.0.4" -sourcemap-codec@^1.4.4: +sourcemap-codec@^1.4.4, sourcemap-codec@^1.4.8: version "1.4.8" resolved "https://registry.yarnpkg.com/sourcemap-codec/-/sourcemap-codec-1.4.8.tgz#ea804bd94857402e6992d05a38ef1ae35a9ab4c4" integrity sha512-9NykojV5Uih4lgo5So5dtw+f0JgJX30KCNI8gwhz2J9A15wD0Ml6tjHKwf6fTSa6fAdVBdZeNOs9eJ71qCk8vA== @@ -20593,6 +22837,36 @@ spdx-satisfies@5.0.1: spdx-expression-parse "^3.0.0" spdx-ranges "^2.0.0" +spdy-transport@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/spdy-transport/-/spdy-transport-3.0.0.tgz#00d4863a6400ad75df93361a1608605e5dcdcf31" + integrity sha512-hsLVFE5SjA6TCisWeJXFKniGGOpBgMLmerfO2aCyCU5s7nJ/rpAepqmFifv/GCbSbueEeAJJnmSQ2rKC/g8Fcw== + dependencies: + debug "^4.1.0" + detect-node "^2.0.4" + hpack.js "^2.1.6" + obuf "^1.1.2" + readable-stream "^3.0.6" + wbuf "^1.7.3" + +spdy@^4.0.2: + version "4.0.2" + resolved "https://registry.yarnpkg.com/spdy/-/spdy-4.0.2.tgz#b74f466203a3eda452c02492b91fb9e84a27677b" + integrity sha512-r46gZQZQV+Kl9oItvl1JZZqJKGr+oEkB08A6BzkiR7593/7IbtuncXHd2YoYeTsG4157ZssMu9KYvUHLcjcDoA== + dependencies: + debug "^4.1.0" + handle-thing "^2.0.0" + http-deceiver "^1.2.7" + select-hose "^2.0.0" + spdy-transport "^3.0.0" + +speed-measure-webpack-plugin@1.3.3: + version "1.3.3" + resolved "https://registry.yarnpkg.com/speed-measure-webpack-plugin/-/speed-measure-webpack-plugin-1.3.3.tgz#6ff894fc83e8a6310dde3af863a0329cd79da4f5" + integrity sha512-2ljD4Ch/rz2zG3HsLsnPfp23osuPBS0qPuz9sGpkNXTN1Ic4M+W9xB8l8rS8ob2cO4b1L+WTJw/0AJwWYVgcxQ== + dependencies: + chalk "^2.0.1" + split-on-first@^1.0.0: version "1.1.0" resolved "https://registry.yarnpkg.com/split-on-first/-/split-on-first-1.1.0.tgz#f610afeee3b12bce1d0c30425e76398b78249a5f" @@ -20680,6 +22954,13 @@ ssri@^6.0.0, ssri@^6.0.1: dependencies: figgy-pudding "^3.5.1" +ssri@^8.0.0, ssri@^8.0.1: + version "8.0.1" + resolved "https://registry.yarnpkg.com/ssri/-/ssri-8.0.1.tgz#638e4e439e2ffbd2cd289776d5ca457c4f51a2af" + integrity sha512-97qShzy1AiyxvPNIkLWoGua7xoQzzPjQ0HAH4B0rWKo7SZ6USuPcrUiAFrws0UH8RrbWmgq3LMTObhPIHbbBeQ== + dependencies: + minipass "^3.1.1" + stable@^0.1.8: version "0.1.8" resolved "https://registry.yarnpkg.com/stable/-/stable-0.1.8.tgz#836eb3c8382fe2936feaf544631017ce7d47a3cf" @@ -21065,6 +23346,14 @@ stubs@^3.0.0: resolved "https://registry.yarnpkg.com/stubs/-/stubs-3.0.0.tgz#e8d2ba1fa9c90570303c030b6900f7d5f89abe5b" integrity sha1-6NK6H6nJBXAwPAMLaQD31fiavls= +style-loader@1.2.1: + version "1.2.1" + resolved "https://registry.yarnpkg.com/style-loader/-/style-loader-1.2.1.tgz#c5cbbfbf1170d076cfdd86e0109c5bba114baa1a" + integrity sha512-ByHSTQvHLkWE9Ir5+lGbVOXhxX10fbprhLvdg96wedFZb4NDekDPxVKv5Fwmio+QcMlkkNfuK+5W1peQ5CUhZg== + dependencies: + loader-utils "^2.0.0" + schema-utils "^2.6.6" + style-loader@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/style-loader/-/style-loader-2.0.0.tgz#9669602fd4690740eaaec137799a03addbbc393c" @@ -21111,6 +23400,15 @@ stylis@3.5.4: resolved "https://registry.yarnpkg.com/stylis/-/stylis-3.5.4.tgz#f665f25f5e299cf3d64654ab949a57c768b73fbe" integrity sha512-8/3pSmthWM7lsPBKv7NXkzn2Uc9W7NotcwGNpJaa3k7WMM1XDCA4MgT5k/8BIexd5ydZdboXtU90XH9Ec4Bv/Q== +stylus-loader@3.0.2: + version "3.0.2" + resolved "https://registry.yarnpkg.com/stylus-loader/-/stylus-loader-3.0.2.tgz#27a706420b05a38e038e7cacb153578d450513c6" + integrity sha512-+VomPdZ6a0razP+zinir61yZgpw2NfljeSsdUF5kJuEzlo3khXhY19Fn6l8QQz1GRJGtMCo8nG5C04ePyV7SUA== + dependencies: + loader-utils "^1.0.2" + lodash.clonedeep "^4.5.0" + when "~3.6.x" + stylus-lookup@^3.0.1: version "3.0.2" resolved "https://registry.yarnpkg.com/stylus-lookup/-/stylus-lookup-3.0.2.tgz#c9eca3ff799691020f30b382260a67355fefdddd" @@ -21119,6 +23417,20 @@ stylus-lookup@^3.0.1: commander "^2.8.1" debug "^4.1.0" +stylus@0.54.8, stylus@^0.54.7: + version "0.54.8" + resolved "https://registry.yarnpkg.com/stylus/-/stylus-0.54.8.tgz#3da3e65966bc567a7b044bfe0eece653e099d147" + integrity sha512-vr54Or4BZ7pJafo2mpf0ZcwA74rpuYCZbxrHBsH8kbcXOwSfvBFwsRfpGO5OD5fhG5HDCFW737PKaawI7OqEAg== + dependencies: + css-parse "~2.0.0" + debug "~3.1.0" + glob "^7.1.6" + mkdirp "~1.0.4" + safer-buffer "^2.1.2" + sax "~1.2.4" + semver "^6.3.0" + source-map "^0.7.3" + sum-up@^1.0.1: version "1.0.3" resolved "https://registry.yarnpkg.com/sum-up/-/sum-up-1.0.3.tgz#1c661f667057f63bcb7875aa1438bc162525156e" @@ -21205,7 +23517,7 @@ svgo@^1.0.0: unquote "~1.1.1" util.promisify "~1.0.0" -symbol-observable@^1.2.0: +symbol-observable@1.2.0, symbol-observable@^1.2.0: version "1.2.0" resolved "https://registry.yarnpkg.com/symbol-observable/-/symbol-observable-1.2.0.tgz#c22688aed4eab3cdc2dfeacbb561660560a00804" integrity sha512-e900nM8RRtGhlV36KGEU9k65K3mPb1WV70OdjfxlG2EAuM1noi/E/BaW/uMhL7bPEssK8QV57vN3esixjUvcXQ== @@ -21312,6 +23624,18 @@ tar@^4.4.10, tar@^4.4.8: safe-buffer "^5.2.1" yallist "^3.1.1" +tar@^6.0.2: + version "6.1.11" + resolved "https://registry.yarnpkg.com/tar/-/tar-6.1.11.tgz#6760a38f003afa1b2ffd0ffe9e9abbd0eab3d621" + integrity sha512-an/KZQzQUkZCkuoAA64hM92X0Urb6VpRhAFllDzz44U2mcD5scmT3zBc4VgVpkugF580+DQn8eAFSyoQt0tznA== + dependencies: + chownr "^2.0.0" + fs-minipass "^2.0.0" + minipass "^3.0.0" + minizlib "^2.1.1" + mkdirp "^1.0.3" + yallist "^4.0.0" + teeny-request@6.0.1: version "6.0.1" resolved "https://registry.yarnpkg.com/teeny-request/-/teeny-request-6.0.1.tgz#9b1f512cef152945827ba7e34f62523a4ce2c5b0" @@ -21379,6 +23703,21 @@ terminal-link@^2.0.0: ansi-escapes "^4.2.1" supports-hyperlinks "^2.0.0" +terser-webpack-plugin@4.1.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/terser-webpack-plugin/-/terser-webpack-plugin-4.1.0.tgz#6e9d6ae4e1a900d88ddce8da6a47507ea61f44bc" + integrity sha512-0ZWDPIP8BtEDZdChbufcXUigOYk6dOX/P/X0hWxqDDcVAQLb8Yy/0FAaemSfax3PAA67+DJR778oz8qVbmy4hA== + dependencies: + cacache "^15.0.5" + find-cache-dir "^3.3.1" + jest-worker "^26.3.0" + p-limit "^3.0.2" + schema-utils "^2.6.6" + serialize-javascript "^4.0.0" + source-map "^0.6.1" + terser "^5.0.0" + webpack-sources "^1.4.3" + terser-webpack-plugin@^1.4.3: version "1.4.5" resolved "https://registry.yarnpkg.com/terser-webpack-plugin/-/terser-webpack-plugin-1.4.5.tgz#a217aefaea330e734ffacb6120ec1fa312d6040b" @@ -21405,6 +23744,15 @@ terser-webpack-plugin@^5.1.3: source-map "^0.6.1" terser "^5.7.2" +terser@5.3.0: + version "5.3.0" + resolved "https://registry.yarnpkg.com/terser/-/terser-5.3.0.tgz#c481f4afecdcc182d5e2bdd2ff2dc61555161e81" + integrity sha512-XTT3D3AwxC54KywJijmY2mxZ8nJiEjBHVYzq8l9OaYuRFWeQNBwvipuzzYEP4e+/AVcd1hqG/CqgsdIRyT45Fg== + dependencies: + commander "^2.20.0" + source-map "~0.6.1" + source-map-support "~0.5.12" + terser@^4.1.2, terser@^4.3.9: version "4.8.0" resolved "https://registry.yarnpkg.com/terser/-/terser-4.8.0.tgz#63056343d7c70bb29f3af665865a46fe03a0df17" @@ -21538,6 +23886,11 @@ through@~2.2.0, through@~2.2.7: resolved "https://registry.yarnpkg.com/through/-/through-2.2.7.tgz#6e8e21200191d4eb6a99f6f010df46aa1c6eb2bd" integrity sha1-bo4hIAGR1OtqmfbwEN9Gqhxusr0= +thunky@^1.0.2: + version "1.1.0" + resolved "https://registry.yarnpkg.com/thunky/-/thunky-1.1.0.tgz#5abaf714a9405db0504732bbccd2cedd9ef9537d" + integrity sha512-eHY7nBftgThBqOyHGVN+l8gF0BucP09fMo0oO/Lb0w1OF80dJv+lDVpXG60WMQvkcxAkNybKsrEIE3ZtKGmPrA== + timed-out@^4.0.0, timed-out@^4.0.1: version "4.0.1" resolved "https://registry.yarnpkg.com/timed-out/-/timed-out-4.0.1.tgz#f32eacac5a175bea25d7fab565ab3ed8741ef56f" @@ -21733,7 +24086,7 @@ tr46@~0.0.3: resolved "https://registry.yarnpkg.com/tr46/-/tr46-0.0.3.tgz#8184fd347dac9cdc185992f3a6622e14b9d9ab6a" integrity sha1-gYT9NH2snNwYWZLzpmIuFLnZq2o= -tree-kill@^1.2.2: +tree-kill@1.2.2, tree-kill@^1.2.2: version "1.2.2" resolved "https://registry.yarnpkg.com/tree-kill/-/tree-kill-1.2.2.tgz#4ca09a9092c88b73a7cdc5e8a01b507b0790a0cc" integrity sha512-L0Orpi8qGpRG//Nd+H90vFB+3iHnue1zSSGmNOOCh1GLJ7rUKVwV2HvijphGQS2UmhUZewS9VgvxYIdgr+fG1A== @@ -21833,6 +24186,11 @@ tsconfig-paths@^3.9.0: minimist "^1.2.0" strip-bom "^3.0.0" +tslib@2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.0.1.tgz#410eb0d113e5b6356490eec749603725b021b43e" + integrity sha512-SgIkNheinmEBgx1IUNirK0TUD4X9yjjBRTqqjggWCU3pUEqIk3/Uwl3yRixYKT6WjQuGiwDv4NomL3wqRCj+CQ== + tslib@^1.10.0, tslib@^1.8.1, tslib@^1.9.0, tslib@^1.9.3: version "1.14.1" resolved "https://registry.yarnpkg.com/tslib/-/tslib-1.14.1.tgz#cf2d38bdc34a134bcaf1091c41f6619e2f672d00" @@ -21939,6 +24297,16 @@ type-is@~1.6.17, type-is@~1.6.18: media-typer "0.3.0" mime-types "~2.1.24" +type@^1.0.1: + version "1.2.0" + resolved "https://registry.yarnpkg.com/type/-/type-1.2.0.tgz#848dd7698dafa3e54a6c479e759c4bc3f18847a0" + integrity sha512-+5nt5AAniqsCnu2cEQQdpzCAh33kVx8n0VoFidKpB1dVVLAN/F+bgVOqOJqOnEnrhp222clB5p3vUlD+1QAnfg== + +type@^2.5.0: + version "2.6.0" + resolved "https://registry.yarnpkg.com/type/-/type-2.6.0.tgz#3ca6099af5981d36ca86b78442973694278a219f" + integrity sha512-eiDBDOmkih5pMbo9OqsqPRGMljLodLcwd5XD5JbtNB0o89xZAwynY9EdCDsJU7LtcVCClu9DvM7/0Ep1hYX3EQ== + typedarray-dts@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/typedarray-dts/-/typedarray-dts-1.0.0.tgz#9dec9811386dbfba964c295c2606cf9a6b982d06" @@ -21994,6 +24362,11 @@ typescript@3.8.3: resolved "https://registry.yarnpkg.com/typescript/-/typescript-3.8.3.tgz#409eb8544ea0335711205869ec458ab109ee1061" integrity sha512-MYlEfn5VrLNsgudQTVJeNaQFUAI7DkhnOjdpAp4T+ku1TfQClewlbSuTVHiA+8skNBgaf02TL/kLOvig4y3G8w== +typescript@4.0.2: + version "4.0.2" + resolved "https://registry.yarnpkg.com/typescript/-/typescript-4.0.2.tgz#7ea7c88777c723c681e33bf7988be5d008d05ac2" + integrity sha512-e4ERvRV2wb+rRZ/IQeb3jm2VxBsirQLpQhdxplZ2MEzGvDkkMmPglecnNDfSUBivMjP93vRbngYYDQqQ/78bcQ== + typescript@^3.9.5, typescript@^3.9.7: version "3.9.9" resolved "https://registry.yarnpkg.com/typescript/-/typescript-3.9.9.tgz#e69905c54bc0681d0518bd4d587cc6f2d0b1a674" @@ -22004,6 +24377,11 @@ typescript@^4.5.2: resolved "https://registry.yarnpkg.com/typescript/-/typescript-4.5.4.tgz#a17d3a0263bf5c8723b9c52f43c5084edf13c2e8" integrity sha512-VgYs2A2QIRuGphtzFV7aQJduJ2gyfTljngLzjpfW9FoYZF6xuw1W0vW9ghCKLfcWrCFxK81CSGRAvS1pn4fIUg== +typescript@~4.0.2: + version "4.0.8" + resolved "https://registry.yarnpkg.com/typescript/-/typescript-4.0.8.tgz#5739105541db80a971fdbd0d56511d1a6f17d37f" + integrity sha512-oz1765PN+imfz1MlZzSZPtC/tqcwsCyIYA8L47EkRnRW97ztRk83SzMiWLrnChC0vqoYxSU1fcFUDA5gV/ZiPg== + ua-parser-js@^0.7.18, ua-parser-js@^0.7.30: version "0.7.31" resolved "https://registry.yarnpkg.com/ua-parser-js/-/ua-parser-js-0.7.31.tgz#649a656b191dffab4f21d5e053e27ca17cbff5c6" @@ -22065,6 +24443,11 @@ unicode-canonical-property-names-ecmascript@^1.0.4: resolved "https://registry.yarnpkg.com/unicode-canonical-property-names-ecmascript/-/unicode-canonical-property-names-ecmascript-1.0.4.tgz#2619800c4c825800efdd8343af7dd9933cbe2818" integrity sha512-jDrNnXWHd4oHiTZnx/ZG7gtUTVp+gCcTTKr8L0HjlwphROEW3+Him+IpvC+xcJEFegapiMZyZe02CyuOnRmbnQ== +unicode-canonical-property-names-ecmascript@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/unicode-canonical-property-names-ecmascript/-/unicode-canonical-property-names-ecmascript-2.0.0.tgz#301acdc525631670d39f6146e0e77ff6bbdebddc" + integrity sha512-yY5PpDlfVIU5+y/BSCxAJRBIS1Zc2dDG3Ujq+sR0U+JjUevW2JhocOF+soROYDSaAezOzOKuyyixhD6mBknSmQ== + unicode-match-property-ecmascript@^1.0.4: version "1.0.4" resolved "https://registry.yarnpkg.com/unicode-match-property-ecmascript/-/unicode-match-property-ecmascript-1.0.4.tgz#8ed2a32569961bce9227d09cd3ffbb8fed5f020c" @@ -22073,16 +24456,34 @@ unicode-match-property-ecmascript@^1.0.4: unicode-canonical-property-names-ecmascript "^1.0.4" unicode-property-aliases-ecmascript "^1.0.4" +unicode-match-property-ecmascript@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/unicode-match-property-ecmascript/-/unicode-match-property-ecmascript-2.0.0.tgz#54fd16e0ecb167cf04cf1f756bdcc92eba7976c3" + integrity sha512-5kaZCrbp5mmbz5ulBkDkbY0SsPOjKqVS35VpL9ulMPfSl0J0Xsm+9Evphv9CoIZFwre7aJoa94AY6seMKGVN5Q== + dependencies: + unicode-canonical-property-names-ecmascript "^2.0.0" + unicode-property-aliases-ecmascript "^2.0.0" + unicode-match-property-value-ecmascript@^1.2.0: version "1.2.0" resolved "https://registry.yarnpkg.com/unicode-match-property-value-ecmascript/-/unicode-match-property-value-ecmascript-1.2.0.tgz#0d91f600eeeb3096aa962b1d6fc88876e64ea531" integrity sha512-wjuQHGQVofmSJv1uVISKLE5zO2rNGzM/KCYZch/QQvez7C1hUhBIuZ701fYXExuufJFMPhv2SyL8CyoIfMLbIQ== +unicode-match-property-value-ecmascript@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/unicode-match-property-value-ecmascript/-/unicode-match-property-value-ecmascript-2.0.0.tgz#1a01aa57247c14c568b89775a54938788189a714" + integrity sha512-7Yhkc0Ye+t4PNYzOGKedDhXbYIBe1XEQYQxOPyhcXNMJ0WCABqqj6ckydd6pWRZTHV4GuCPKdBAUiMc60tsKVw== + unicode-property-aliases-ecmascript@^1.0.4: version "1.1.0" resolved "https://registry.yarnpkg.com/unicode-property-aliases-ecmascript/-/unicode-property-aliases-ecmascript-1.1.0.tgz#dd57a99f6207bedff4628abefb94c50db941c8f4" integrity sha512-PqSoPh/pWetQ2phoj5RLiaqIk4kCNwoV3CI+LfGmWLKI3rE3kl1h59XpX2BjgDrmbxD9ARtQobPGU1SguCYuQg== +unicode-property-aliases-ecmascript@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/unicode-property-aliases-ecmascript/-/unicode-property-aliases-ecmascript-2.0.0.tgz#0a36cb9a585c4f6abd51ad1deddb285c165297c8" + integrity sha512-5Zfuy9q/DFr4tfO7ZPeVXb1aPoeQSdeFMLpYuFebehDAhbuevLs5yxSZmIFN1tP5F9Wl4IpJrYojg85/zgyZHQ== + union-value@^1.0.0: version "1.0.1" resolved "https://registry.yarnpkg.com/union-value/-/union-value-1.0.1.tgz#0b6fe7b835aecda61c6ea4d4f02c14221e109847" @@ -22124,6 +24525,15 @@ unique-string@^2.0.0: dependencies: crypto-random-string "^2.0.0" +universal-analytics@0.4.23: + version "0.4.23" + resolved "https://registry.yarnpkg.com/universal-analytics/-/universal-analytics-0.4.23.tgz#d915e676850c25c4156762471bdd7cf2eaaca8ac" + integrity sha512-lgMIH7XBI6OgYn1woDEmxhGdj8yDefMKg7GkWdeATAlQZFrMrNyxSkpDzY57iY0/6fdlzTbBV03OawvvzG+q7A== + dependencies: + debug "^4.1.1" + request "^2.88.2" + uuid "^3.0.0" + universal-user-agent@^4.0.0: version "4.0.1" resolved "https://registry.yarnpkg.com/universal-user-agent/-/universal-user-agent-4.0.1.tgz#fd8d6cb773a679a709e967ef8288a31fcc03e557" @@ -22216,6 +24626,14 @@ url-parse-lax@^3.0.0: dependencies: prepend-http "^2.0.0" +url-parse@^1.4.3: + version "1.5.10" + resolved "https://registry.yarnpkg.com/url-parse/-/url-parse-1.5.10.tgz#9d3c2f736c1d75dd3bd2be507dcc111f1e2ea9c1" + integrity sha512-WypcfiRhfeUP9vvF0j6rw0J3hrWrw6iZv3+22h6iRMJ/8z1Tj6XfLP4DsUix5MhMPnXpiHDoKyoZ/bdCkwBCiQ== + dependencies: + querystringify "^2.1.1" + requires-port "^1.0.0" + url-to-options@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/url-to-options/-/url-to-options-1.0.1.tgz#1505a03a289a48cbd7a434efbaeec5055f5633a9" @@ -22333,7 +24751,12 @@ uuid@3.3.2: resolved "https://registry.yarnpkg.com/uuid/-/uuid-3.3.2.tgz#1b4af4955eb3077c501c23872fc6513811587131" integrity sha512-yXJmeNaw3DnnKAOKJE51sL/ZaYfWJRl1pK9dr19YFCu0ObS231AB1/LbqTKRAQ5kw8A90rA6fr4riOUpTZvQZA== -uuid@^3.0.1, uuid@^3.3.2: +uuid@8.3.0: + version "8.3.0" + resolved "https://registry.yarnpkg.com/uuid/-/uuid-8.3.0.tgz#ab738085ca22dc9a8c92725e459b1d507df5d6ea" + integrity sha512-fX6Z5o4m6XsXBdli9g7DtWgAx+osMsRRZFKma1mIUsLCz6vRvv+pz5VNbyu9UEDzpMWulZfvpgb/cmDXVulYFQ== + +uuid@^3.0.0, uuid@^3.0.1, uuid@^3.3.2, uuid@^3.4.0: version "3.4.0" resolved "https://registry.yarnpkg.com/uuid/-/uuid-3.4.0.tgz#b23e4358afa8a202fe7a100af1f5f883f02007ee" integrity sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A== @@ -22560,6 +24983,13 @@ watchpack@^2.3.1: glob-to-regexp "^0.4.1" graceful-fs "^4.1.2" +wbuf@^1.1.0, wbuf@^1.7.3: + version "1.7.3" + resolved "https://registry.yarnpkg.com/wbuf/-/wbuf-1.7.3.tgz#c1d8d149316d3ea852848895cb6a0bfe887b87df" + integrity sha512-O84QOnr0icsbFGLS0O3bI5FswxzRr8/gHwWkDlQFskhSPryQXvrTMxjxGP4+iWYoauLoBvfDpkrOauZ+0iZpDA== + dependencies: + minimalistic-assert "^1.0.0" + wcwidth@^1.0.0, wcwidth@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/wcwidth/-/wcwidth-1.0.1.tgz#f0b0dcf915bc5ff1528afadb2c0e17b532da2fe8" @@ -22607,7 +25037,83 @@ webpack-bundle-analyzer@^4.4.0: sirv "^1.0.7" ws "^7.3.1" -webpack-sources@^1.1.0, webpack-sources@^1.4.0, webpack-sources@^1.4.1: +webpack-dev-middleware@3.7.2: + version "3.7.2" + resolved "https://registry.yarnpkg.com/webpack-dev-middleware/-/webpack-dev-middleware-3.7.2.tgz#0019c3db716e3fa5cecbf64f2ab88a74bab331f3" + integrity sha512-1xC42LxbYoqLNAhV6YzTYacicgMZQTqRd27Sim9wn5hJrX3I5nxYy1SxSd4+gjUFsz1dQFj+yEe6zEVmSkeJjw== + dependencies: + memory-fs "^0.4.1" + mime "^2.4.4" + mkdirp "^0.5.1" + range-parser "^1.2.1" + webpack-log "^2.0.0" + +webpack-dev-middleware@^3.7.2: + version "3.7.3" + resolved "https://registry.yarnpkg.com/webpack-dev-middleware/-/webpack-dev-middleware-3.7.3.tgz#0639372b143262e2b84ab95d3b91a7597061c2c5" + integrity sha512-djelc/zGiz9nZj/U7PTBi2ViorGJXEWo/3ltkPbDyxCXhhEXkW0ce99falaok4TPj+AsxLiXJR0EBOb0zh9fKQ== + dependencies: + memory-fs "^0.4.1" + mime "^2.4.4" + mkdirp "^0.5.1" + range-parser "^1.2.1" + webpack-log "^2.0.0" + +webpack-dev-server@3.11.0: + version "3.11.0" + resolved "https://registry.yarnpkg.com/webpack-dev-server/-/webpack-dev-server-3.11.0.tgz#8f154a3bce1bcfd1cc618ef4e703278855e7ff8c" + integrity sha512-PUxZ+oSTxogFQgkTtFndEtJIPNmml7ExwufBZ9L2/Xyyd5PnOL5UreWe5ZT7IU25DSdykL9p1MLQzmLh2ljSeg== + dependencies: + ansi-html "0.0.7" + bonjour "^3.5.0" + chokidar "^2.1.8" + compression "^1.7.4" + connect-history-api-fallback "^1.6.0" + debug "^4.1.1" + del "^4.1.1" + express "^4.17.1" + html-entities "^1.3.1" + http-proxy-middleware "0.19.1" + import-local "^2.0.0" + internal-ip "^4.3.0" + ip "^1.1.5" + is-absolute-url "^3.0.3" + killable "^1.0.1" + loglevel "^1.6.8" + opn "^5.5.0" + p-retry "^3.0.1" + portfinder "^1.0.26" + schema-utils "^1.0.0" + selfsigned "^1.10.7" + semver "^6.3.0" + serve-index "^1.9.1" + sockjs "0.3.20" + sockjs-client "1.4.0" + spdy "^4.0.2" + strip-ansi "^3.0.1" + supports-color "^6.1.0" + url "^0.11.0" + webpack-dev-middleware "^3.7.2" + webpack-log "^2.0.0" + ws "^6.2.1" + yargs "^13.3.2" + +webpack-log@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/webpack-log/-/webpack-log-2.0.0.tgz#5b7928e0637593f119d32f6227c1e0ac31e1b47f" + integrity sha512-cX8G2vR/85UYG59FgkoMamwHUIkSSlV3bBMRsbxVXVUk2j6NleCKjQ/WE9eYg9WY4w25O9w8wKP4rzNZFmUcUg== + dependencies: + ansi-colors "^3.0.0" + uuid "^3.3.2" + +webpack-merge@4.2.2: + version "4.2.2" + resolved "https://registry.yarnpkg.com/webpack-merge/-/webpack-merge-4.2.2.tgz#a27c52ea783d1398afd2087f547d7b9d2f43634d" + integrity sha512-TUE1UGoTX2Cd42j3krGYqObZbOD+xF7u28WB7tfUordytSjbWTIjK/8V0amkBfTYN4/pB/GIDlJZZ657BGG19g== + dependencies: + lodash "^4.17.15" + +webpack-sources@1.4.3, webpack-sources@^1.1.0, webpack-sources@^1.2.0, webpack-sources@^1.3.0, webpack-sources@^1.4.0, webpack-sources@^1.4.1, webpack-sources@^1.4.3: version "1.4.3" resolved "https://registry.yarnpkg.com/webpack-sources/-/webpack-sources-1.4.3.tgz#eedd8ec0b928fbf1cbfe994e22d2d890f330a933" integrity sha512-lgTS3Xhv1lCOKo7SA5TjKXMjpSM4sBjNV5+q2bqesbSPs5FjGmU6jjtBSkX9b4qW87vDIsCIlUPOEhbZrMdjeQ== @@ -22620,6 +25126,42 @@ webpack-sources@^3.2.2: resolved "https://registry.yarnpkg.com/webpack-sources/-/webpack-sources-3.2.2.tgz#d88e3741833efec57c4c789b6010db9977545260" integrity sha512-cp5qdmHnu5T8wRg2G3vZZHoJPN14aqQ89SyQ11NpGH5zEMDCclt49rzo+MaRazk7/UeILhAI+/sEtcM+7Fr0nw== +webpack-subresource-integrity@1.4.1: + version "1.4.1" + resolved "https://registry.yarnpkg.com/webpack-subresource-integrity/-/webpack-subresource-integrity-1.4.1.tgz#e8bf918b444277df46a66cd84542cbcdc5a6272d" + integrity sha512-XMLFInbGbB1HV7K4vHWANzc1CN0t/c4bBvnlvGxGwV45yE/S/feAXIm8dJsCkzqWtSKnmaEgTp/meyeThxG4Iw== + dependencies: + webpack-sources "^1.3.0" + +webpack@4.44.1: + version "4.44.1" + resolved "https://registry.yarnpkg.com/webpack/-/webpack-4.44.1.tgz#17e69fff9f321b8f117d1fda714edfc0b939cc21" + integrity sha512-4UOGAohv/VGUNQJstzEywwNxqX417FnjZgZJpJQegddzPmTvph37eBIRbRTfdySXzVtJXLJfbMN3mMYhM6GdmQ== + dependencies: + "@webassemblyjs/ast" "1.9.0" + "@webassemblyjs/helper-module-context" "1.9.0" + "@webassemblyjs/wasm-edit" "1.9.0" + "@webassemblyjs/wasm-parser" "1.9.0" + acorn "^6.4.1" + ajv "^6.10.2" + ajv-keywords "^3.4.1" + chrome-trace-event "^1.0.2" + enhanced-resolve "^4.3.0" + eslint-scope "^4.0.3" + json-parse-better-errors "^1.0.2" + loader-runner "^2.4.0" + loader-utils "^1.2.3" + memory-fs "^0.4.1" + micromatch "^3.1.10" + mkdirp "^0.5.3" + neo-async "^2.6.1" + node-libs-browser "^2.2.1" + schema-utils "^1.0.0" + tapable "^1.1.3" + terser-webpack-plugin "^1.4.3" + watchpack "^1.7.4" + webpack-sources "^1.4.1" + webpack@^4.30.0, webpack@^4.44.1: version "4.46.0" resolved "https://registry.yarnpkg.com/webpack/-/webpack-4.46.0.tgz#bf9b4404ea20a073605e0a011d188d77cb6ad542" @@ -22679,6 +25221,13 @@ webpack@^5.52.0, webpack@^5.65.0: watchpack "^2.3.1" webpack-sources "^3.2.2" +websocket-driver@0.6.5: + version "0.6.5" + resolved "https://registry.yarnpkg.com/websocket-driver/-/websocket-driver-0.6.5.tgz#5cb2556ceb85f4373c6d8238aa691c8454e13a36" + integrity sha1-XLJVbOuF9Dc8bYI4qmkchFThOjY= + dependencies: + websocket-extensions ">=0.1.1" + websocket-driver@>=0.5.1: version "0.7.4" resolved "https://registry.yarnpkg.com/websocket-driver/-/websocket-driver-0.7.4.tgz#89ad5295bbf64b480abcba31e4953aca706f5760" @@ -22756,6 +25305,11 @@ whatwg-url@^8.0.0, whatwg-url@^8.5.0: tr46 "^2.0.2" webidl-conversions "^6.1.0" +when@~3.6.x: + version "3.6.4" + resolved "https://registry.yarnpkg.com/when/-/when-3.6.4.tgz#473b517ec159e2b85005497a13983f095412e34e" + integrity sha1-RztRfsFZ4rhQBUl6E5g/CVQS404= + which-boxed-primitive@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/which-boxed-primitive/-/which-boxed-primitive-1.0.2.tgz#13757bc89b209b049fe5d86430e21cf40a89a8e6" @@ -22835,6 +25389,13 @@ worker-farm@^1.7.0: dependencies: errno "~0.1.7" +worker-plugin@5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/worker-plugin/-/worker-plugin-5.0.0.tgz#113b5fe1f4a5d6a957cecd29915bedafd70bb537" + integrity sha512-AXMUstURCxDD6yGam2r4E34aJg6kW85IiaeX72hi+I1cxyaMUtrvVY6sbfpGKAj5e7f68Acl62BjQF5aOOx2IQ== + dependencies: + loader-utils "^1.1.0" + workerpool@^2.3.0: version "2.3.3" resolved "https://registry.yarnpkg.com/workerpool/-/workerpool-2.3.3.tgz#49a70089bd55e890d68cc836a19419451d7c81d7" @@ -22949,6 +25510,13 @@ write-pkg@^3.1.0: sort-keys "^2.0.0" write-json-file "^2.2.0" +ws@^6.2.1: + version "6.2.2" + resolved "https://registry.yarnpkg.com/ws/-/ws-6.2.2.tgz#dd5cdbd57a9979916097652d78f1cc5faea0c32e" + integrity sha512-zmhltoSR8u1cnDsD43TX59mzoMZsLKqUweyYBAIvTngR3shc0W6aOZylZmq/7hqyVxPdi+5Ud2QInblgyE72fw== + dependencies: + async-limiter "~1.0.0" + ws@^7.2.3, ws@^7.3.1, ws@~7.4.2: version "7.4.4" resolved "https://registry.yarnpkg.com/ws/-/ws-7.4.4.tgz#383bc9742cb202292c9077ceab6f6047b17f2d59" @@ -23017,6 +25585,13 @@ xtend@^4.0.0, xtend@^4.0.2, xtend@~4.0.1: resolved "https://registry.yarnpkg.com/xtend/-/xtend-4.0.2.tgz#bb72779f5fa465186b1f438f674fa347fdb5db54" integrity sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ== +xxhashjs@^0.2.1: + version "0.2.2" + resolved "https://registry.yarnpkg.com/xxhashjs/-/xxhashjs-0.2.2.tgz#8a6251567621a1c46a5ae204da0249c7f8caa9d8" + integrity sha512-AkTuIuVTET12tpsVIQo+ZU6f/qDmKuRUcjaqR+OIvm+aCBsZ95i7UVY5WJ9TMsSaZ0DA2WxoZ4acu0sPH+OKAw== + dependencies: + cuint "^0.2.2" + "y18n@^3.2.1 || ^4.0.0", y18n@^4.0.0: version "4.0.1" resolved "https://registry.yarnpkg.com/y18n/-/y18n-4.0.1.tgz#8db2b83c31c5d75099bb890b23f3094891e247d4" @@ -23088,7 +25663,7 @@ yargs-unparser@1.6.0: lodash "^4.17.15" yargs "^13.3.0" -yargs@13.3.2, yargs@^13.3.0: +yargs@13.3.2, yargs@^13.3.0, yargs@^13.3.2: version "13.3.2" resolved "https://registry.yarnpkg.com/yargs/-/yargs-13.3.2.tgz#ad7ffefec1aa59565ac915f82dccb38a9c31a2dd" integrity sha512-AX3Zw5iPruN5ie6xGRIDgqkT+ZhnRlZMLMHAs8tg7nRruy2Nb+i5o9bwghAogtM08q1dpr2LVoS8KSTMYpWXUw== From 4581faa6aeb43cce65d04231ac55daa252897e1a Mon Sep 17 00:00:00 2001 From: Lukas Stracke Date: Thu, 21 Apr 2022 16:43:48 +0200 Subject: [PATCH 065/204] doc(angular): Add Angular migration instructions to migration doc (#4960) Co-authored-by: Luca Forstner --- MIGRATION.md | 13 +++++++++++++ packages/angular/README.md | 5 +++++ 2 files changed, 18 insertions(+) diff --git a/MIGRATION.md b/MIGRATION.md index a33feb194af3..77f1c21e7aca 100644 --- a/MIGRATION.md +++ b/MIGRATION.md @@ -123,6 +123,19 @@ For our efforts to reduce bundle size of the SDK we had to remove and refactor p `setupBrowserTransport` or `setupNodeTransport` for default transports, depending on your requirements. - Remove support for Opera browser pre v15 +## Sentry Angular SDK Changes + +The Sentry Angular SDK (`@sentry/angular`) is now compiled with the Angular compiler (see [#4641](https://github.com/getsentry/sentry-javascript/pull/4641)). This change was necessary to fix a long-lasting bug in the SDK (see [#3282](https://github.com/getsentry/sentry-javascript/issues/3282)): `TraceDirective` and `TraceModule` can now be used again without risking an application compiler error or having to disable AOT compilation. + +### Angular Version Compatibility + +Given the forward compatibility of the Angular compiler, v7 of our SDK will only work with Angular 10 and above. Previously, it was possible to use the SDK with versions <10, although we officially only supported Angular 10-13 as peer dependencies. If you are using Angular <10 in your project, we recommend staying on the latest 6.x version until you can upgrade your Angular version. + +### Import Changes + +Due to the compiler change, our NPM package structure changed as well as it now conforms to the [Angular Package Format v10](https://docs.google.com/document/d/1uh2D6XqaGh2yjjXwfF4SrJqWl1MBhMPntlNBBsk6rbw/edit). +In case you're importing from specific paths other than `@sentry/angular` you will have to adjust these paths. As an example, `import ... from '@sentry/angular/esm/injex.js'` should be changed to `import ... from '@sentry/angular/esm2015/index.js'`. Generally, we strongly recommend only importing from `@sentry/angular`. + # Upgrading from 6.17.x to 6.18.0 Version 6.18.0 deprecates the `frameContextLines` top-level option for the Node SDK. This option will be removed in an upcoming major version. To migrate off of the top-level option, pass it instead to the new `ContextLines` integration. diff --git a/packages/angular/README.md b/packages/angular/README.md index d42a32344082..2a110538a349 100644 --- a/packages/angular/README.md +++ b/packages/angular/README.md @@ -12,6 +12,11 @@ - [Official SDK Docs](https://docs.sentry.io/platforms/javascript/angular/) - [TypeDoc](http://getsentry.github.io/sentry-javascript/) +## Angular Version Compatibility + +For Angular 10-13 use the latest version of the Sentry Angular SDK. In case +you are using an Angular 9 or earlier, use version 6.x of the SDK as newer versions do not support Angular <10. + ## General This package is a wrapper around `@sentry/browser`, with added functionality related to Angular. All methods available From deac0eb514abb2e56c199e8368e4b65e3b9e2ea9 Mon Sep 17 00:00:00 2001 From: Abhijeet Prasad Date: Thu, 21 Apr 2022 13:31:46 -0400 Subject: [PATCH 066/204] ref: Enforce `stackParser` through options. (#4953) As stackParser will always be defined in client options, we can remove the usage of `stackParserFromOptions` outside of SDK init. --- packages/browser/src/client.ts | 12 +++------- .../src/integrations/globalhandlers.ts | 10 ++++----- .../browser/src/integrations/linkederrors.ts | 11 +++++----- packages/browser/src/sdk.ts | 5 +---- packages/node/src/client.ts | 6 ++--- .../node/src/integrations/linkederrors.ts | 10 ++++----- packages/node/src/sdk.ts | 6 +---- packages/utils/src/stacktrace.ts | 22 +++++-------------- 8 files changed, 29 insertions(+), 53 deletions(-) diff --git a/packages/browser/src/client.ts b/packages/browser/src/client.ts index a889d7f69d06..0679413da1c4 100644 --- a/packages/browser/src/client.ts +++ b/packages/browser/src/client.ts @@ -1,6 +1,6 @@ import { BaseClient, NewTransport, Scope, SDK_VERSION } from '@sentry/core'; import { ClientOptions, Event, EventHint, Options, Severity, SeverityLevel, Transport } from '@sentry/types'; -import { getGlobalObject, logger, stackParserFromOptions } from '@sentry/utils'; +import { getGlobalObject, logger } from '@sentry/utils'; import { eventFromException, eventFromMessage } from './eventbuilder'; import { IS_DEBUG_BUILD } from './flags'; @@ -89,7 +89,7 @@ export class BrowserClient extends BaseClient { * @inheritDoc */ public eventFromException(exception: unknown, hint?: EventHint): PromiseLike { - return eventFromException(stackParserFromOptions(this._options), exception, hint, this._options.attachStacktrace); + return eventFromException(this._options.stackParser, exception, hint, this._options.attachStacktrace); } /** @@ -101,13 +101,7 @@ export class BrowserClient extends BaseClient { level: Severity | SeverityLevel = 'info', hint?: EventHint, ): PromiseLike { - return eventFromMessage( - stackParserFromOptions(this._options), - message, - level, - hint, - this._options.attachStacktrace, - ); + return eventFromMessage(this._options.stackParser, message, level, hint, this._options.attachStacktrace); } /** diff --git a/packages/browser/src/integrations/globalhandlers.ts b/packages/browser/src/integrations/globalhandlers.ts index 61e56a533629..9452bf8f7103 100644 --- a/packages/browser/src/integrations/globalhandlers.ts +++ b/packages/browser/src/integrations/globalhandlers.ts @@ -9,7 +9,6 @@ import { isPrimitive, isString, logger, - stackParserFromOptions, } from '@sentry/utils'; import { BrowserClient } from '../client'; @@ -255,8 +254,9 @@ function addMechanismAndCapture(hub: Hub, error: EventHint['originalException'], function getHubAndOptions(): [Hub, StackParser, boolean | undefined] { const hub = getCurrentHub(); const client = hub.getClient(); - const options = client?.getOptions(); - const parser = stackParserFromOptions(options); - const attachStacktrace = options?.attachStacktrace; - return [hub, parser, attachStacktrace]; + const options = (client && client.getOptions()) || { + stackParser: () => [], + attachStacktrace: false, + }; + return [hub, options.stackParser, options.attachStacktrace]; } diff --git a/packages/browser/src/integrations/linkederrors.ts b/packages/browser/src/integrations/linkederrors.ts index 1cc30c182950..8fa4413f5a83 100644 --- a/packages/browser/src/integrations/linkederrors.ts +++ b/packages/browser/src/integrations/linkederrors.ts @@ -1,6 +1,6 @@ import { addGlobalEventProcessor, getCurrentHub } from '@sentry/core'; import { Event, EventHint, Exception, ExtendedError, Integration, StackParser } from '@sentry/types'; -import { isInstanceOf, stackParserFromOptions } from '@sentry/utils'; +import { isInstanceOf } from '@sentry/utils'; import { BrowserClient } from '../client'; import { exceptionFromError } from '../eventbuilder'; @@ -47,12 +47,13 @@ export class LinkedErrors implements Integration { * @inheritDoc */ public setupOnce(): void { - const options = getCurrentHub().getClient()?.getOptions(); - const parser = stackParserFromOptions(options); - + const client = getCurrentHub().getClient(); + if (!client) { + return; + } addGlobalEventProcessor((event: Event, hint?: EventHint) => { const self = getCurrentHub().getIntegration(LinkedErrors); - return self ? _handler(parser, self._key, self._limit, event, hint) : event; + return self ? _handler(client.getOptions().stackParser, self._key, self._limit, event, hint) : event; }); } } diff --git a/packages/browser/src/sdk.ts b/packages/browser/src/sdk.ts index 05d9d9c34462..f8f233a1c230 100644 --- a/packages/browser/src/sdk.ts +++ b/packages/browser/src/sdk.ts @@ -102,14 +102,11 @@ export function init(options: BrowserOptions = {}): void { if (options.sendClientReports === undefined) { options.sendClientReports = true; } - if (options.stackParser === undefined) { - options.stackParser = defaultStackParsers; - } const { transport, newTransport } = setupBrowserTransport(options); const clientOptions: BrowserClientOptions = { ...options, - stackParser: stackParserFromOptions(options), + stackParser: stackParserFromOptions(options.stackParser || defaultStackParsers), integrations: getIntegrationsToSetup(options), // TODO(v7): get rid of transport being passed down below transport: options.transport || (supportsFetch() ? FetchTransport : XHRTransport), diff --git a/packages/node/src/client.ts b/packages/node/src/client.ts index 7864fd1b7ccc..4ba91d0db9a2 100644 --- a/packages/node/src/client.ts +++ b/packages/node/src/client.ts @@ -1,7 +1,7 @@ import { BaseClient, NewTransport, Scope, SDK_VERSION } from '@sentry/core'; import { SessionFlusher } from '@sentry/hub'; import { Event, EventHint, Severity, SeverityLevel, Transport } from '@sentry/types'; -import { logger, resolvedSyncPromise, stackParserFromOptions } from '@sentry/utils'; +import { logger, resolvedSyncPromise } from '@sentry/utils'; import { eventFromMessage, eventFromUnknownInput } from './eventbuilder'; import { IS_DEBUG_BUILD } from './flags'; @@ -111,7 +111,7 @@ export class NodeClient extends BaseClient { */ // eslint-disable-next-line @typescript-eslint/no-explicit-any, @typescript-eslint/explicit-module-boundary-types public eventFromException(exception: any, hint?: EventHint): PromiseLike { - return resolvedSyncPromise(eventFromUnknownInput(stackParserFromOptions(this._options), exception, hint)); + return resolvedSyncPromise(eventFromUnknownInput(this._options.stackParser, exception, hint)); } /** @@ -124,7 +124,7 @@ export class NodeClient extends BaseClient { hint?: EventHint, ): PromiseLike { return resolvedSyncPromise( - eventFromMessage(stackParserFromOptions(this._options), message, level, hint, this._options.attachStacktrace), + eventFromMessage(this._options.stackParser, message, level, hint, this._options.attachStacktrace), ); } diff --git a/packages/node/src/integrations/linkederrors.ts b/packages/node/src/integrations/linkederrors.ts index c1dfd285db84..19555c9bb542 100644 --- a/packages/node/src/integrations/linkederrors.ts +++ b/packages/node/src/integrations/linkederrors.ts @@ -1,6 +1,6 @@ import { addGlobalEventProcessor, getCurrentHub } from '@sentry/core'; import { Event, EventHint, Exception, ExtendedError, Integration, StackParser } from '@sentry/types'; -import { isInstanceOf, resolvedSyncPromise, stackParserFromOptions, SyncPromise } from '@sentry/utils'; +import { isInstanceOf, resolvedSyncPromise, SyncPromise } from '@sentry/utils'; import { NodeClient } from '../client'; import { exceptionFromError } from '../eventbuilder'; @@ -46,12 +46,10 @@ export class LinkedErrors implements Integration { addGlobalEventProcessor(async (event: Event, hint?: EventHint) => { const hub = getCurrentHub(); const self = hub.getIntegration(LinkedErrors); - const stackParser = stackParserFromOptions(hub.getClient()?.getOptions()); - - if (self) { - await self._handler(stackParser, event, hint); + const client = hub.getClient(); + if (client && self) { + await self._handler(client.getOptions().stackParser, event, hint); } - return event; }); } diff --git a/packages/node/src/sdk.ts b/packages/node/src/sdk.ts index fa4b22a12066..4b1e503e851a 100644 --- a/packages/node/src/sdk.ts +++ b/packages/node/src/sdk.ts @@ -122,10 +122,6 @@ export function init(options: NodeOptions = {}): void { options.autoSessionTracking = true; } - if (options.stackParser === undefined) { - options.stackParser = [nodeStackParser]; - } - // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access, @typescript-eslint/no-explicit-any if ((domain as any).active) { setHubOnCarrier(carrier, getCurrentHub()); @@ -136,7 +132,7 @@ export function init(options: NodeOptions = {}): void { // TODO(v7): Refactor this to reduce the logic above const clientOptions: NodeClientOptions = { ...options, - stackParser: stackParserFromOptions(options), + stackParser: stackParserFromOptions(options.stackParser || [nodeStackParser]), integrations: getIntegrationsToSetup(options), // TODO(v7): Fix me when we switch to new transports entirely. transport: options.transport || (transport instanceof HTTPTransport ? HTTPTransport : HTTPSTransport), diff --git a/packages/utils/src/stacktrace.ts b/packages/utils/src/stacktrace.ts index 15c5401c09bd..b63f7b4c8354 100644 --- a/packages/utils/src/stacktrace.ts +++ b/packages/utils/src/stacktrace.ts @@ -30,27 +30,17 @@ export function createStackParser(...parsers: StackLineParser[]): StackParser { }; } -interface StackParserOptions { - stackParser?: StackParser | StackLineParser[]; -} - /** - * Gets a stack parser implementation from options + * Gets a stack parser implementation from Options.stackParser + * @see Options * * If options contains an array of line parsers, it is converted into a parser */ -export function stackParserFromOptions(options: StackParserOptions | undefined): StackParser { - if (options) { - if (Array.isArray(options.stackParser)) { - options.stackParser = createStackParser(...options.stackParser); - } - - if (typeof options.stackParser === 'function') { - return options.stackParser; - } +export function stackParserFromOptions(stackParser: StackParser | StackLineParser[]): StackParser { + if (Array.isArray(stackParser)) { + return createStackParser(...stackParser); } - - return _ => []; + return stackParser; } /** From c2ebd42d1731ed265403efdcce94b039d1466d2a Mon Sep 17 00:00:00 2001 From: Onur Temizkan Date: Thu, 21 Apr 2022 17:40:18 +0000 Subject: [PATCH 067/204] feat(tracing): Add Prisma ORM integration. (#4931) Co-authored-by: Abhijeet Prasad --- packages/tracing/src/integrations/index.ts | 1 + .../tracing/src/integrations/node/prisma.ts | 99 +++++++++++++++++++ .../test/integrations/node/prisma.test.ts | 61 ++++++++++++ 3 files changed, 161 insertions(+) create mode 100644 packages/tracing/src/integrations/node/prisma.ts create mode 100644 packages/tracing/test/integrations/node/prisma.test.ts diff --git a/packages/tracing/src/integrations/index.ts b/packages/tracing/src/integrations/index.ts index b35eff6c495f..6fb86c1afc78 100644 --- a/packages/tracing/src/integrations/index.ts +++ b/packages/tracing/src/integrations/index.ts @@ -2,6 +2,7 @@ export { Express } from './node/express'; export { Postgres } from './node/postgres'; export { Mysql } from './node/mysql'; export { Mongo } from './node/mongo'; +export { Prisma } from './node/prisma'; // TODO(v7): Remove this export // Please see `src/index.ts` for more details. diff --git a/packages/tracing/src/integrations/node/prisma.ts b/packages/tracing/src/integrations/node/prisma.ts new file mode 100644 index 000000000000..e70fce9f0f60 --- /dev/null +++ b/packages/tracing/src/integrations/node/prisma.ts @@ -0,0 +1,99 @@ +import { Hub } from '@sentry/hub'; +import { EventProcessor, Integration } from '@sentry/types'; +import { isThenable, logger } from '@sentry/utils'; + +import { IS_DEBUG_BUILD } from '../../flags'; + +type PrismaAction = + | 'findUnique' + | 'findMany' + | 'findFirst' + | 'create' + | 'createMany' + | 'update' + | 'updateMany' + | 'upsert' + | 'delete' + | 'deleteMany' + | 'executeRaw' + | 'queryRaw' + | 'aggregate' + | 'count' + | 'runCommandRaw'; + +interface PrismaMiddlewareParams { + model?: unknown; + action: PrismaAction; + args: unknown; + dataPath: string[]; + runInTransaction: boolean; +} + +type PrismaMiddleware = ( + params: PrismaMiddlewareParams, + next: (params: PrismaMiddlewareParams) => Promise, +) => Promise; + +interface PrismaClient { + $use: (cb: PrismaMiddleware) => void; +} + +/** Tracing integration for @prisma/client package */ +export class Prisma implements Integration { + /** + * @inheritDoc + */ + public static id: string = 'Prisma'; + + /** + * @inheritDoc + */ + public name: string = Prisma.id; + + /** + * Prisma ORM Client Instance + */ + private readonly _client?: PrismaClient; + + /** + * @inheritDoc + */ + public constructor(options: { client?: PrismaClient } = {}) { + this._client = options.client; + } + + /** + * @inheritDoc + */ + public setupOnce(_: (callback: EventProcessor) => void, getCurrentHub: () => Hub): void { + if (!this._client) { + IS_DEBUG_BUILD && logger.error('PrismaIntegration is missing a Prisma Client Instance'); + return; + } + + this._client.$use((params: PrismaMiddlewareParams, next: (params: PrismaMiddlewareParams) => Promise) => { + const scope = getCurrentHub().getScope(); + const parentSpan = scope?.getSpan(); + + const action = params.action; + const model = params.model; + + const span = parentSpan?.startChild({ + description: model ? `${model} ${action}` : action, + op: 'db.prisma', + }); + + const rv = next(params); + + if (isThenable(rv)) { + return rv.then((res: unknown) => { + span?.finish(); + return res; + }); + } + + span?.finish(); + return rv; + }); + } +} diff --git a/packages/tracing/test/integrations/node/prisma.test.ts b/packages/tracing/test/integrations/node/prisma.test.ts new file mode 100644 index 000000000000..501101dbce6f --- /dev/null +++ b/packages/tracing/test/integrations/node/prisma.test.ts @@ -0,0 +1,61 @@ +/* eslint-disable @typescript-eslint/unbound-method */ +import { Hub, Scope } from '@sentry/hub'; + +import { Prisma } from '../../../src/integrations/node/prisma'; +import { Span } from '../../../src/span'; + +type PrismaMiddleware = (params: unknown, next: (params?: unknown) => Promise) => Promise; + +class PrismaClient { + public user: { create: () => Promise | undefined } = { + create: () => this._middleware?.({ action: 'create', model: 'user' }, () => Promise.resolve('result')), + }; + + private _middleware?: PrismaMiddleware; + + constructor() { + this._middleware = undefined; + } + + public $use(cb: PrismaMiddleware) { + this._middleware = cb; + } +} + +describe('setupOnce', function () { + const Client: PrismaClient = new PrismaClient(); + + let scope = new Scope(); + let parentSpan: Span; + let childSpan: Span; + + beforeAll(() => { + // @ts-ignore, not to export PrismaClient types from integration source + new Prisma({ client: Client }).setupOnce( + () => undefined, + () => new Hub(undefined, scope), + ); + }); + + beforeEach(() => { + scope = new Scope(); + parentSpan = new Span(); + childSpan = parentSpan.startChild(); + jest.spyOn(scope, 'getSpan').mockReturnValueOnce(parentSpan); + jest.spyOn(parentSpan, 'startChild').mockReturnValueOnce(childSpan); + jest.spyOn(childSpan, 'finish'); + }); + + it('should add middleware with $use method correctly', done => { + void Client.user.create()?.then(res => { + expect(res).toBe('result'); + expect(scope.getSpan).toBeCalled(); + expect(parentSpan.startChild).toBeCalledWith({ + description: 'user create', + op: 'db.prisma', + }); + expect(childSpan.finish).toBeCalled(); + done(); + }); + }); +}); From ec384dbbbe338e0ac2996e24309ca6f7060d0e3d Mon Sep 17 00:00:00 2001 From: Chad Whitacre Date: Thu, 21 Apr 2022 14:37:01 -0400 Subject: [PATCH 068/204] meta(gha): Deploy action `enforce-license-compliance.yml` (#4961) --- .github/workflows/enforce-license-compliance.yml | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) create mode 100644 .github/workflows/enforce-license-compliance.yml diff --git a/.github/workflows/enforce-license-compliance.yml b/.github/workflows/enforce-license-compliance.yml new file mode 100644 index 000000000000..b331974711f3 --- /dev/null +++ b/.github/workflows/enforce-license-compliance.yml @@ -0,0 +1,16 @@ +name: Enforce License Compliance + +on: + push: + branches: [master, main, release/*] + pull_request: + branches: [master, main] + +jobs: + enforce-license-compliance: + runs-on: ubuntu-latest + steps: + - name: 'Enforce License Compliance' + uses: getsentry/action-enforce-license-compliance@main + with: + fossa_api_key: ${{ secrets.FOSSA_API_KEY }} From df555d1c9a615ef7e984c831b996ee51bbfb2143 Mon Sep 17 00:00:00 2001 From: Katie Byers Date: Thu, 21 Apr 2022 16:15:16 -0700 Subject: [PATCH 069/204] fix(dev): Correctly lint `scripts` typescript files (#4963) Previously, we've had to tell eslint to ignore TS files in package-level `scripts` directories, because they weren't included by any existing TS eslint config. This fixes that, by adding them to both our main `.eslintrc.js` and (since that new entry needs a tsconfig to which to point) our main `tsconfig.json`. --- .eslintrc.js | 8 ++++++++ packages/gatsby/.eslintrc.js | 2 +- tsconfig.json | 4 ++++ 3 files changed, 13 insertions(+), 1 deletion(-) diff --git a/.eslintrc.js b/.eslintrc.js index 956f3681ba72..01d0da16cf14 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -33,6 +33,14 @@ module.exports = { project: ['tsconfig.test.json'], }, }, + { + files: ['**/scripts/**/*.ts'], + parserOptions: { + // since filepaths are relative to the working directory, we need to go back up to reach the repo root level + // tsconfig + project: ['../../tsconfig.json'], + }, + }, { files: ['*.tsx'], rules: { diff --git a/packages/gatsby/.eslintrc.js b/packages/gatsby/.eslintrc.js index 6aff306daa40..56cb2e62ad47 100644 --- a/packages/gatsby/.eslintrc.js +++ b/packages/gatsby/.eslintrc.js @@ -7,6 +7,6 @@ module.exports = { jsx: true, }, // ignore these because they're not covered by a `tsconfig`, which makes eslint throw an error - ignorePatterns: ['scripts/prepack.ts', 'gatsby-browser.d.ts', 'gatsby-node.d.ts'], + ignorePatterns: ['gatsby-browser.d.ts', 'gatsby-node.d.ts'], extends: ['../../.eslintrc.js'], }; diff --git a/tsconfig.json b/tsconfig.json index f2ffa0c4e07c..98848bfdcbe8 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -1,6 +1,10 @@ { "extends": "./packages/typescript/tsconfig.json", + // include scripts here because their TS config isn't package-specific, and they need to be included in a tsconfig + // file to be linted + "include": ["**/scripts/**/*.ts"], + "compilerOptions": { // TODO: turn these on once we switch to only generating types once, using `tsconfig.types.json` // "declaration": false, From 18257b1b74441487d10760db78e45babceafc7fa Mon Sep 17 00:00:00 2001 From: Lukas Stracke Date: Fri, 22 Apr 2022 10:44:31 +0200 Subject: [PATCH 070/204] feat(angular): Log warning if SDK is used with an older Angular version than officially supported (#4964) introduces a check in the Angular SDK's init function to check if the Angular version it is used with is older than our minimum supported version (Angular 10). In case it is, a warning will be logged. adjusts the migration and readme docs to reflect better our officially supported Angular versions and what to do if users are using an older Angular version if they experience problems (i.e. stay on v6). Co-authored-by: Luca Forstner --- MIGRATION.md | 6 +++++- packages/angular/README.md | 3 +-- packages/angular/src/constants.ts | 5 +++++ packages/angular/src/sdk.ts | 20 ++++++++++++++++++++ 4 files changed, 31 insertions(+), 3 deletions(-) diff --git a/MIGRATION.md b/MIGRATION.md index 77f1c21e7aca..b3a3a9eb7944 100644 --- a/MIGRATION.md +++ b/MIGRATION.md @@ -129,7 +129,11 @@ The Sentry Angular SDK (`@sentry/angular`) is now compiled with the Angular comp ### Angular Version Compatibility -Given the forward compatibility of the Angular compiler, v7 of our SDK will only work with Angular 10 and above. Previously, it was possible to use the SDK with versions <10, although we officially only supported Angular 10-13 as peer dependencies. If you are using Angular <10 in your project, we recommend staying on the latest 6.x version until you can upgrade your Angular version. +As in v6, we continue to list Angular 10-13 in our peer dependencies, meaning that these are the Angular versions we officially support. +If you are using v7 with Angular <10 in your project and you experience problems, we recommend staying on the latest 6.x +version until you can upgrade your Angular version. As v7 of our SDK is compiled with the Angular 10 compiler and we upgraded +our Typescript version, the SDK will work with Angular 10 and above. Tests have shown that Angular 9 seems to work as well +(use at your own risk) but we recommend upgrading to a more recent Angular version. ### Import Changes diff --git a/packages/angular/README.md b/packages/angular/README.md index 2a110538a349..82132d7543b2 100644 --- a/packages/angular/README.md +++ b/packages/angular/README.md @@ -14,8 +14,7 @@ ## Angular Version Compatibility -For Angular 10-13 use the latest version of the Sentry Angular SDK. In case -you are using an Angular 9 or earlier, use version 6.x of the SDK as newer versions do not support Angular <10. +The latest version of this SDK officially supports Angular 10-13. If you are using an older version of Angular and experience problems with the Angular SDK, we recommend downgrading the SDK to version 6.x. ## General diff --git a/packages/angular/src/constants.ts b/packages/angular/src/constants.ts index a0786e7b516c..9e98b366d841 100644 --- a/packages/angular/src/constants.ts +++ b/packages/angular/src/constants.ts @@ -3,3 +3,8 @@ export const ANGULAR_ROUTING_OP = 'ui.angular.routing'; export const ANGULAR_INIT_OP = 'ui.angular.init'; export const ANGULAR_OP = 'ui.angular'; + +/** + * Minimum Angular version this SDK supports + */ +export const ANGULAR_MINIMUM_VERSION = 10; diff --git a/packages/angular/src/sdk.ts b/packages/angular/src/sdk.ts index 09267d1f2082..bcce7c85ccdb 100644 --- a/packages/angular/src/sdk.ts +++ b/packages/angular/src/sdk.ts @@ -1,4 +1,9 @@ +import { VERSION } from '@angular/core'; import { BrowserOptions, init as browserInit, SDK_VERSION } from '@sentry/browser'; +import { logger } from '@sentry/utils'; + +import { ANGULAR_MINIMUM_VERSION } from './constants'; +import { IS_DEBUG_BUILD } from './flags'; /** * Inits the Angular SDK @@ -15,5 +20,20 @@ export function init(options: BrowserOptions): void { ], version: SDK_VERSION, }; + checkAngularVersion(); browserInit(options); } + +function checkAngularVersion(): void { + if (VERSION && VERSION.major) { + const major = parseInt(VERSION.major, 10); + if (major < ANGULAR_MINIMUM_VERSION) { + IS_DEBUG_BUILD && + logger.warn( + `The Sentry SDK does not officially support Angular ${major}.`, + `This version of the Sentry SDK supports Angular ${ANGULAR_MINIMUM_VERSION} and above.`, + 'Please consider upgrading your Angular version or downgrading the Sentry SDK.', + ); + } + } +} From b5abe03a66b9c6e0bb27c232a6b26219cf954a49 Mon Sep 17 00:00:00 2001 From: Abhijeet Prasad Date: Sun, 24 Apr 2022 23:08:46 -0400 Subject: [PATCH 071/204] ref: Switch to new transports (#4943) This PR switches over from the old transports to the new ones. It deletes the functionality from the client of passing in transports explicitly and instead expects the transport to be passed in through options. Instead of passing in an instance of a transport to the client, we pass in a constructor function. This allows the client to control exactly what options are passed into the transport. ```ts this._transport = options.transport({ ...options.transportOptions, url }); ``` --- MIGRATION.md | 6 - packages/browser/src/client.ts | 8 +- packages/browser/src/exports.ts | 1 + packages/browser/src/sdk.ts | 9 +- packages/browser/src/transports/index.ts | 2 - packages/browser/src/transports/new-fetch.ts | 9 +- packages/browser/src/transports/new-xhr.ts | 9 +- packages/browser/src/transports/setup.ts | 71 ----- .../unit/helper/browser-client-options.ts | 5 +- packages/browser/test/unit/index.test.ts | 24 +- .../unit/integrations/linkederrors.test.ts | 7 +- .../test/unit/mocks/simpletransport.ts | 17 +- packages/browser/test/unit/sdk.test.ts | 6 +- .../browser/test/unit/transports/base.test.ts | 3 +- .../test/unit/transports/setup.test.ts | 101 ------ packages/core/src/baseclient.ts | 114 +++---- packages/core/src/index.ts | 7 - packages/core/src/request.ts | 33 +- packages/core/src/sdk.ts | 13 +- packages/core/src/transports/base.ts | 62 +--- packages/core/test/lib/base.test.ts | 297 ++++++++---------- packages/core/test/lib/sdk.test.ts | 4 +- .../core/test/lib/transports/base.test.ts | 15 +- packages/core/test/mocks/client.ts | 32 +- packages/core/test/mocks/transport.ts | 41 +-- packages/ember/tests/dummy/app/app.js | 20 +- packages/ember/tests/test-helper.js | 27 +- packages/hub/src/sessionflusher.ts | 32 +- packages/hub/test/sessionflusher.test.ts | 28 +- .../fetch-captureException/subject.js | 1 - .../fetch-captureException/test.ts | 21 -- .../fetch-startTransaction/subject.js | 2 - .../fetch-startTransaction/test.ts | 13 - .../suites/new-transports/init.js | 13 - .../xhr-captureException/subject.js | 4 - .../xhr-captureException/test.ts | 21 -- .../xhr-startTransaction/subject.js | 5 - .../xhr-startTransaction/test.ts | 13 - .../addBreadcrumb/empty_obj/test.ts | 5 +- .../multiple_breadcrumbs/test.ts | 5 +- .../addBreadcrumb/simple_breadcrumb/test.ts | 5 +- .../addBreadcrumb/undefined_arg/test.ts | 5 +- .../captureException/empty_obj/test.ts | 5 +- .../captureException/simple_error/test.ts | 5 +- .../captureException/undefined_arg/test.ts | 5 +- .../captureMessage/simple_message/test.ts | 5 +- .../captureMessage/with_level/test.ts | 5 +- .../configureScope/clear_scope/test.ts | 5 +- .../configureScope/set_properties/test.ts | 5 +- .../setContext/multiple_contexts/test.ts | 5 +- .../non_serializable_context/test.ts | 5 +- .../setContext/simple_context/test.ts | 5 +- .../setExtra/multiple_extras/test.ts | 5 +- .../setExtra/non_serializable_extra/test.ts | 5 +- .../public-api/setExtra/simple_extra/test.ts | 5 +- .../setExtras/consecutive_calls/test.ts | 5 +- .../setExtras/multiple_extras/test.ts | 5 +- .../setTag/with_non_primitives/test.ts | 5 +- .../public-api/setTag/with_primitives/test.ts | 5 +- .../setTags/with_non_primitives/test.ts | 5 +- .../setTags/with_primitives/test.ts | 5 +- .../public-api/setUser/unset_user/test.ts | 5 +- .../public-api/setUser/update_user/test.ts | 5 +- .../withScope/nested_scopes/test.ts | 5 +- .../suites/tracing/request/fetch/test.ts | 22 +- packages/integration-tests/utils/helpers.ts | 83 ++--- packages/nextjs/test/index.client.test.ts | 4 +- packages/nextjs/test/index.server.test.ts | 4 +- .../test/integration/test/utils/client.js | 29 +- .../test/integration/test/utils/server.js | 10 +- .../suites/express/handle-error/test.ts | 8 +- .../addBreadcrumb/empty-obj/test.ts | 9 +- .../multiple_breadcrumbs/test.ts | 6 +- .../addBreadcrumb/simple_breadcrumb/test.ts | 6 +- .../captureException/catched-error/test.ts | 7 +- .../captureException/empty-obj/test.ts | 7 +- .../new-transport/scenario.ts | 11 - .../captureException/new-transport/test.ts | 23 -- .../captureException/simple-error/test.ts | 7 +- .../captureMessage/simple_message/test.ts | 7 +- .../captureMessage/with_level/test.ts | 18 +- .../configureScope/clear_scope/test.ts | 8 +- .../configureScope/set_properties/test.ts | 7 +- .../setContext/multiple-contexts/test.ts | 9 +- .../non-serializable-context/test.ts | 9 +- .../setContext/simple-context/test.ts | 9 +- .../setExtra/multiple-extras/test.ts | 7 +- .../setExtra/non-serializable-extra/test.ts | 7 +- .../public-api/setExtra/simple-extra/test.ts | 7 +- .../setExtras/consecutive-calls/test.ts | 7 +- .../setExtras/multiple-extras/test.ts | 7 +- .../public-api/setTag/with-primitives/test.ts | 7 +- .../setTags/with-primitives/test.ts | 7 +- .../public-api/setUser/unset_user/test.ts | 14 +- .../public-api/setUser/update_user/test.ts | 8 +- .../withScope/nested-scopes/test.ts | 16 +- .../crashed-session-aggregate/test.ts | 12 +- .../errored-session-aggregate/test.ts | 19 +- .../node-integration-tests/utils/index.ts | 36 --- packages/node/src/client.ts | 10 +- packages/node/src/index.ts | 1 + packages/node/src/sdk.ts | 9 +- packages/node/src/transports/index.ts | 1 - packages/node/src/transports/new.ts | 5 +- packages/node/src/transports/setup.ts | 52 --- packages/node/test/client.test.ts | 25 +- packages/node/test/handlers.test.ts | 21 +- .../node/test/helper/node-client-options.ts | 5 +- packages/node/test/index.test.ts | 27 +- packages/node/test/integrations/http.test.ts | 5 +- .../test/integrations/linkederrors.test.ts | 11 +- .../manual/express-scope-separation/start.js | 10 +- .../aggregates-disable-single-session.js | 15 +- .../caught-exception-errored-session.js | 50 +-- .../errors-in-session-capped-to-one.js | 48 +-- .../single-session/healthy-session.js | 18 +- .../terminal-state-sessions-sent-once.js | 32 +- .../uncaught-exception-crashed-session.js | 27 +- .../unhandled-rejection-crashed-session.js | 30 +- .../test/manual/release-health/test-utils.js | 20 +- .../node/test/manual/webpack-domain/index.js | 8 +- packages/node/test/transports/setup.test.ts | 62 ---- packages/tracing/src/transaction.ts | 11 +- .../test/browser/backgroundtab.test.ts | 5 +- .../test/browser/browsertracing.test.ts | 9 +- packages/tracing/test/browser/request.test.ts | 5 +- packages/tracing/test/errors.test.ts | 7 +- packages/tracing/test/hub.test.ts | 83 ++--- packages/tracing/test/idletransaction.test.ts | 29 +- packages/tracing/test/span.test.ts | 9 +- packages/tracing/test/testutils.ts | 13 +- packages/types/src/client.ts | 8 +- packages/types/src/index.ts | 14 +- packages/types/src/options.ts | 13 +- packages/types/src/session.ts | 3 - packages/types/src/transport.ts | 42 +++ 136 files changed, 932 insertions(+), 1494 deletions(-) delete mode 100644 packages/browser/src/transports/setup.ts delete mode 100644 packages/browser/test/unit/transports/setup.test.ts delete mode 100644 packages/integration-tests/suites/new-transports/fetch-captureException/subject.js delete mode 100644 packages/integration-tests/suites/new-transports/fetch-captureException/test.ts delete mode 100644 packages/integration-tests/suites/new-transports/fetch-startTransaction/subject.js delete mode 100644 packages/integration-tests/suites/new-transports/fetch-startTransaction/test.ts delete mode 100644 packages/integration-tests/suites/new-transports/init.js delete mode 100644 packages/integration-tests/suites/new-transports/xhr-captureException/subject.js delete mode 100644 packages/integration-tests/suites/new-transports/xhr-captureException/test.ts delete mode 100644 packages/integration-tests/suites/new-transports/xhr-startTransaction/subject.js delete mode 100644 packages/integration-tests/suites/new-transports/xhr-startTransaction/test.ts delete mode 100644 packages/node-integration-tests/suites/public-api/captureException/new-transport/scenario.ts delete mode 100644 packages/node-integration-tests/suites/public-api/captureException/new-transport/test.ts delete mode 100644 packages/node/src/transports/setup.ts delete mode 100644 packages/node/test/transports/setup.test.ts diff --git a/MIGRATION.md b/MIGRATION.md index b3a3a9eb7944..3c5b999f11a3 100644 --- a/MIGRATION.md +++ b/MIGRATION.md @@ -115,12 +115,6 @@ For our efforts to reduce bundle size of the SDK we had to remove and refactor p [#4919](https://github.com/getsentry/sentry-javascript/pull/4919)). `Backend` was an unnecessary abstraction which is not present in other Sentry SDKs. For the sake of reducing complexity, increasing consistency with other Sentry SDKs and decreasing bundle-size, `Backend` was removed. - - -- Inject transport into client instead of initializing it in the client in `setupTransport` (see - [#4921](https://github.com/getsentry/sentry-javascript/pull/4921/)). If you are creating your own `Client` or - calling `initAndBind`, you will have to supply your desired transport. Either provide a custom one or call - `setupBrowserTransport` or `setupNodeTransport` for default transports, depending on your requirements. - Remove support for Opera browser pre v15 ## Sentry Angular SDK Changes diff --git a/packages/browser/src/client.ts b/packages/browser/src/client.ts index 0679413da1c4..d57b35c02d31 100644 --- a/packages/browser/src/client.ts +++ b/packages/browser/src/client.ts @@ -1,5 +1,5 @@ -import { BaseClient, NewTransport, Scope, SDK_VERSION } from '@sentry/core'; -import { ClientOptions, Event, EventHint, Options, Severity, SeverityLevel, Transport } from '@sentry/types'; +import { BaseClient, Scope, SDK_VERSION } from '@sentry/core'; +import { ClientOptions, Event, EventHint, Options, Severity, SeverityLevel } from '@sentry/types'; import { getGlobalObject, logger } from '@sentry/utils'; import { eventFromException, eventFromMessage } from './eventbuilder'; @@ -47,7 +47,7 @@ export class BrowserClient extends BaseClient { * * @param options Configuration options for this SDK. */ - public constructor(options: BrowserClientOptions, transport: Transport, newTransport?: NewTransport) { + public constructor(options: BrowserClientOptions) { options._metadata = options._metadata || {}; options._metadata.sdk = options._metadata.sdk || { name: 'sentry.javascript.browser', @@ -59,7 +59,7 @@ export class BrowserClient extends BaseClient { ], version: SDK_VERSION, }; - super(options, transport, newTransport); + super(options); } /** diff --git a/packages/browser/src/exports.ts b/packages/browser/src/exports.ts index 1af8a7d8aaf5..0777d8124579 100644 --- a/packages/browser/src/exports.ts +++ b/packages/browser/src/exports.ts @@ -27,6 +27,7 @@ export { captureEvent, captureMessage, configureScope, + createTransport, getHubFromCarrier, getCurrentHub, Hub, diff --git a/packages/browser/src/sdk.ts b/packages/browser/src/sdk.ts index f8f233a1c230..9b138864515a 100644 --- a/packages/browser/src/sdk.ts +++ b/packages/browser/src/sdk.ts @@ -14,8 +14,7 @@ import { IS_DEBUG_BUILD } from './flags'; import { ReportDialogOptions, wrap as internalWrap } from './helpers'; import { Breadcrumbs, Dedupe, GlobalHandlers, LinkedErrors, TryCatch, UserAgent } from './integrations'; import { defaultStackParsers } from './stack-parsers'; -import { FetchTransport, XHRTransport } from './transports'; -import { setupBrowserTransport } from './transports/setup'; +import { makeNewFetchTransport, makeNewXHRTransport } from './transports'; export const defaultIntegrations = [ new CoreIntegrations.InboundFilters(), @@ -102,17 +101,15 @@ export function init(options: BrowserOptions = {}): void { if (options.sendClientReports === undefined) { options.sendClientReports = true; } - const { transport, newTransport } = setupBrowserTransport(options); const clientOptions: BrowserClientOptions = { ...options, stackParser: stackParserFromOptions(options.stackParser || defaultStackParsers), integrations: getIntegrationsToSetup(options), - // TODO(v7): get rid of transport being passed down below - transport: options.transport || (supportsFetch() ? FetchTransport : XHRTransport), + transport: options.transport || (supportsFetch() ? makeNewFetchTransport : makeNewXHRTransport), }; - initAndBind(BrowserClient, clientOptions, transport, newTransport); + initAndBind(BrowserClient, clientOptions); if (options.autoSessionTracking) { startSessionTracking(); diff --git a/packages/browser/src/transports/index.ts b/packages/browser/src/transports/index.ts index 31871a76d01c..287e14e0ac50 100644 --- a/packages/browser/src/transports/index.ts +++ b/packages/browser/src/transports/index.ts @@ -4,5 +4,3 @@ export { XHRTransport } from './xhr'; export { makeNewFetchTransport } from './new-fetch'; export { makeNewXHRTransport } from './new-xhr'; - -export { setupBrowserTransport } from './setup'; diff --git a/packages/browser/src/transports/new-fetch.ts b/packages/browser/src/transports/new-fetch.ts index 47a85b517e77..9a9d7b14ae19 100644 --- a/packages/browser/src/transports/new-fetch.ts +++ b/packages/browser/src/transports/new-fetch.ts @@ -1,10 +1,5 @@ -import { - BaseTransportOptions, - createTransport, - NewTransport, - TransportMakeRequestResponse, - TransportRequest, -} from '@sentry/core'; +import { createTransport } from '@sentry/core'; +import { BaseTransportOptions, NewTransport, TransportMakeRequestResponse, TransportRequest } from '@sentry/types'; import { FetchImpl, getNativeFetchImplementation } from './utils'; diff --git a/packages/browser/src/transports/new-xhr.ts b/packages/browser/src/transports/new-xhr.ts index cd19b1de0cd4..d45a0019914c 100644 --- a/packages/browser/src/transports/new-xhr.ts +++ b/packages/browser/src/transports/new-xhr.ts @@ -1,10 +1,5 @@ -import { - BaseTransportOptions, - createTransport, - NewTransport, - TransportMakeRequestResponse, - TransportRequest, -} from '@sentry/core'; +import { createTransport } from '@sentry/core'; +import { BaseTransportOptions, NewTransport, TransportMakeRequestResponse, TransportRequest } from '@sentry/types'; import { SyncPromise } from '@sentry/utils'; /** diff --git a/packages/browser/src/transports/setup.ts b/packages/browser/src/transports/setup.ts deleted file mode 100644 index f72365e7dc94..000000000000 --- a/packages/browser/src/transports/setup.ts +++ /dev/null @@ -1,71 +0,0 @@ -import { - BaseTransportOptions, - getEnvelopeEndpointWithUrlEncodedAuth, - initAPIDetails, - NewTransport, - NoopTransport, -} from '@sentry/core'; -import { Transport, TransportOptions } from '@sentry/types'; -import { supportsFetch } from '@sentry/utils'; - -import { BrowserOptions } from '../client'; -import { FetchTransport } from './fetch'; -import { makeNewFetchTransport } from './new-fetch'; -import { makeNewXHRTransport } from './new-xhr'; -import { XHRTransport } from './xhr'; - -export interface BrowserTransportOptions extends BaseTransportOptions { - // options to pass into fetch request - fetchParams: Record; - headers?: Record; - sendClientReports?: boolean; -} - -/** - * Sets up Browser transports based on the passed `options`. If available, the returned - * transport will use the fetch API. In case fetch is not supported, an XMLHttpRequest - * based transport is created. - * - * @returns an object currently still containing both, the old `Transport` and - * `NewTransport` which will eventually replace `Transport`. Once this is replaced, - * this function will return a ready to use `NewTransport`. - */ -// TODO(v7): Adjust return value when NewTransport is the default -export function setupBrowserTransport(options: BrowserOptions): { - transport: Transport; - newTransport?: NewTransport; -} { - if (!options.dsn) { - // We return the noop transport here in case there is no Dsn. - return { transport: new NoopTransport() }; - } - - const transportOptions: TransportOptions = { - ...options.transportOptions, - dsn: options.dsn, - tunnel: options.tunnel, - sendClientReports: options.sendClientReports, - _metadata: options._metadata, - }; - - const api = initAPIDetails(transportOptions.dsn, transportOptions._metadata, transportOptions.tunnel); - const url = getEnvelopeEndpointWithUrlEncodedAuth(api.dsn, api.tunnel); - - if (options.transport) { - return { transport: new options.transport(transportOptions) }; - } - - if (supportsFetch()) { - const requestOptions: RequestInit = { ...transportOptions.fetchParameters }; - const newTransport = makeNewFetchTransport({ requestOptions, url }); - const fetchTransport = new FetchTransport(transportOptions); - return { transport: fetchTransport, newTransport }; - } - - const newTransport = makeNewXHRTransport({ - url, - headers: transportOptions.headers, - }); - const transport = new XHRTransport(transportOptions); - return { transport, newTransport }; -} diff --git a/packages/browser/test/unit/helper/browser-client-options.ts b/packages/browser/test/unit/helper/browser-client-options.ts index aa763a5de06b..19d4a4cb8c05 100644 --- a/packages/browser/test/unit/helper/browser-client-options.ts +++ b/packages/browser/test/unit/helper/browser-client-options.ts @@ -1,11 +1,12 @@ -import { NoopTransport } from '@sentry/core'; +import { createTransport } from '@sentry/core'; +import { resolvedSyncPromise } from '@sentry/utils'; import { BrowserClientOptions } from '../../../src/client'; export function getDefaultBrowserClientOptions(options: Partial = {}): BrowserClientOptions { return { integrations: [], - transport: NoopTransport, + transport: () => createTransport({}, _ => resolvedSyncPromise({ statusCode: 200 })), stackParser: () => [], ...options, }; diff --git a/packages/browser/test/unit/index.test.ts b/packages/browser/test/unit/index.test.ts index 434dea98977a..c8d1df23bac9 100644 --- a/packages/browser/test/unit/index.test.ts +++ b/packages/browser/test/unit/index.test.ts @@ -17,7 +17,7 @@ import { wrap, } from '../../src'; import { getDefaultBrowserClientOptions } from './helper/browser-client-options'; -import { SimpleTransport } from './mocks/simpletransport'; +import { makeSimpleTransport } from './mocks/simpletransport'; const dsn = 'https://53039209a22b4ec1bcc296a3c9fdecd6@sentry.io/4291'; @@ -31,7 +31,7 @@ describe('SentryBrowser', () => { init({ beforeSend, dsn, - transport: SimpleTransport, + transport: makeSimpleTransport, }); }); @@ -77,7 +77,7 @@ describe('SentryBrowser', () => { describe('user', () => { const EX_USER = { email: 'test@example.com' }; const options = getDefaultBrowserClientOptions({ dsn }); - const client = new BrowserClient(options, new SimpleTransport({ dsn })); + const client = new BrowserClient(options); const reportDialogSpy = jest.spyOn(client, 'showReportDialog'); beforeEach(() => { @@ -150,7 +150,7 @@ describe('SentryBrowser', () => { }, dsn, }); - getCurrentHub().bindClient(new BrowserClient(options, new SimpleTransport({ dsn }))); + getCurrentHub().bindClient(new BrowserClient(options)); captureMessage('test'); }); @@ -164,7 +164,7 @@ describe('SentryBrowser', () => { }, dsn, }); - getCurrentHub().bindClient(new BrowserClient(options, new SimpleTransport({ dsn }))); + getCurrentHub().bindClient(new BrowserClient(options)); captureEvent({ message: 'event' }); }); @@ -175,7 +175,7 @@ describe('SentryBrowser', () => { dsn, integrations: [], }); - getCurrentHub().bindClient(new BrowserClient(options, new SimpleTransport({ dsn }))); + getCurrentHub().bindClient(new BrowserClient(options)); captureMessage('event222'); captureMessage('event222'); @@ -192,7 +192,7 @@ describe('SentryBrowser', () => { dsn, integrations: [new Integrations.InboundFilters({ ignoreErrors: ['capture'] })], }); - getCurrentHub().bindClient(new BrowserClient(options, new SimpleTransport({ dsn }))); + getCurrentHub().bindClient(new BrowserClient(options)); captureMessage('capture'); @@ -244,7 +244,7 @@ describe('SentryBrowser initialization', () => { it('should set SDK data when Sentry.init() is called', () => { init({ dsn }); - const sdkData = (getCurrentHub().getClient() as any).getTransport()._api.metadata?.sdk; + const sdkData = (getCurrentHub().getClient() as any).getOptions()._metadata.sdk; expect(sdkData?.name).toBe('sentry.javascript.browser'); expect(sdkData?.packages[0].name).toBe('npm:@sentry/browser'); @@ -254,9 +254,9 @@ describe('SentryBrowser initialization', () => { it('should set SDK data when instantiating a client directly', () => { const options = getDefaultBrowserClientOptions({ dsn }); - const client = new BrowserClient(options, new SimpleTransport({ dsn })); + const client = new BrowserClient(options); - const sdkData = (client.getTransport() as any)._api.metadata?.sdk; + const sdkData = client.getOptions()._metadata?.sdk as any; expect(sdkData.name).toBe('sentry.javascript.browser'); expect(sdkData.packages[0].name).toBe('npm:@sentry/browser'); @@ -284,7 +284,7 @@ describe('SentryBrowser initialization', () => { }, }); - const sdkData = (getCurrentHub().getClient() as any).getTransport()._api.metadata?.sdk; + const sdkData = (getCurrentHub().getClient() as any).getOptions()._metadata?.sdk; expect(sdkData.name).toBe('sentry.javascript.angular'); expect(sdkData.packages[0].name).toBe('npm:@sentry/angular'); @@ -305,7 +305,7 @@ describe('wrap()', () => { }, dsn, }); - getCurrentHub().bindClient(new BrowserClient(options, new SimpleTransport({ dsn }))); + getCurrentHub().bindClient(new BrowserClient(options)); try { wrap(() => { diff --git a/packages/browser/test/unit/integrations/linkederrors.test.ts b/packages/browser/test/unit/integrations/linkederrors.test.ts index 4b862705bccc..445a4fde8699 100644 --- a/packages/browser/test/unit/integrations/linkederrors.test.ts +++ b/packages/browser/test/unit/integrations/linkederrors.test.ts @@ -4,7 +4,6 @@ import { createStackParser } from '@sentry/utils'; import { BrowserClient } from '../../../src/client'; import * as LinkedErrorsModule from '../../../src/integrations/linkederrors'; import { defaultStackParsers } from '../../../src/stack-parsers'; -import { setupBrowserTransport } from '../../../src/transports'; import { getDefaultBrowserClientOptions } from '../helper/browser-client-options'; const parser = createStackParser(...defaultStackParsers); @@ -47,7 +46,7 @@ describe('LinkedErrors', () => { const originalException = one; const options = getDefaultBrowserClientOptions({ stackParser: parser }); - const client = new BrowserClient(options, setupBrowserTransport(options).transport); + const client = new BrowserClient(options); return client.eventFromException(originalException).then(event => { const result = LinkedErrorsModule._handler(parser, 'cause', 5, event, { originalException, @@ -78,7 +77,7 @@ describe('LinkedErrors', () => { const originalException = one; const options = getDefaultBrowserClientOptions({ stackParser: parser }); - const client = new BrowserClient(options, setupBrowserTransport(options).transport); + const client = new BrowserClient(options); return client.eventFromException(originalException).then(event => { const result = LinkedErrorsModule._handler(parser, 'reason', 5, event, { originalException, @@ -105,7 +104,7 @@ describe('LinkedErrors', () => { two.cause = three; const options = getDefaultBrowserClientOptions({ stackParser: parser }); - const client = new BrowserClient(options, setupBrowserTransport(options).transport); + const client = new BrowserClient(options); const originalException = one; return client.eventFromException(originalException).then(event => { const result = LinkedErrorsModule._handler(parser, 'cause', 2, event, { diff --git a/packages/browser/test/unit/mocks/simpletransport.ts b/packages/browser/test/unit/mocks/simpletransport.ts index 398feb1a3e6a..59a21aed4ac3 100644 --- a/packages/browser/test/unit/mocks/simpletransport.ts +++ b/packages/browser/test/unit/mocks/simpletransport.ts @@ -1,15 +1,6 @@ -import { eventStatusFromHttpCode, resolvedSyncPromise } from '@sentry/utils'; +import { createTransport } from '@sentry/core'; +import { resolvedSyncPromise } from '@sentry/utils'; -import { Event, Response } from '../../../src'; -import { BaseTransport } from '../../../src/transports'; - -// @ts-ignore It's okay that we're not implementing the `_sendRequest()` method because we don't use it in our tests -export class SimpleTransport extends BaseTransport { - public sendEvent(_: Event): PromiseLike { - return this._buffer.add(() => - resolvedSyncPromise({ - status: eventStatusFromHttpCode(200), - }), - ); - } +export function makeSimpleTransport() { + return createTransport({}, () => resolvedSyncPromise({ statusCode: 200 })); } diff --git a/packages/browser/test/unit/sdk.test.ts b/packages/browser/test/unit/sdk.test.ts index 8814329f16e9..85fa04b21612 100644 --- a/packages/browser/test/unit/sdk.test.ts +++ b/packages/browser/test/unit/sdk.test.ts @@ -1,7 +1,9 @@ /* eslint-disable @typescript-eslint/unbound-method */ -import { NoopTransport, Scope } from '@sentry/core'; +import { Scope } from '@sentry/core'; +import { createTransport } from '@sentry/core'; import { MockIntegration } from '@sentry/core/test/lib/sdk.test'; import { Client, Integration } from '@sentry/types'; +import { resolvedSyncPromise } from '@sentry/utils'; import { BrowserOptions } from '../../src'; import { init } from '../../src/sdk'; @@ -13,7 +15,7 @@ const PUBLIC_DSN = 'https://username@domain/123'; function getDefaultBrowserOptions(options: Partial = {}): BrowserOptions { return { integrations: [], - transport: NoopTransport, + transport: () => createTransport({}, _ => resolvedSyncPromise({ statusCode: 200 })), stackParser: () => [], ...options, }; diff --git a/packages/browser/test/unit/transports/base.test.ts b/packages/browser/test/unit/transports/base.test.ts index 9df498352c1e..75894049c1ca 100644 --- a/packages/browser/test/unit/transports/base.test.ts +++ b/packages/browser/test/unit/transports/base.test.ts @@ -7,7 +7,8 @@ const envelopeEndpoint = 'https://sentry.io/api/42/envelope/?sentry_key=123&sent // assert on what the class provides and what it leaves to the concrete class to implement class SimpleTransport extends BaseTransport {} -describe('BaseTransport', () => { +// TODO(v7): Re-enable these tests with client reports +describe.skip('BaseTransport', () => { describe('Client Reports', () => { const sendBeaconSpy = jest.fn(); let visibilityState: string; diff --git a/packages/browser/test/unit/transports/setup.test.ts b/packages/browser/test/unit/transports/setup.test.ts deleted file mode 100644 index 41b361d684d5..000000000000 --- a/packages/browser/test/unit/transports/setup.test.ts +++ /dev/null @@ -1,101 +0,0 @@ -import { NoopTransport } from '@sentry/core'; - -import { - FetchTransport, - makeNewFetchTransport, - makeNewXHRTransport, - setupBrowserTransport, - XHRTransport, -} from '../../../src/transports'; -import { getDefaultBrowserClientOptions } from '../helper/browser-client-options'; -import { SimpleTransport } from '../mocks/simpletransport'; - -const DSN = 'https://username@domain/123'; - -let fetchSupported = true; - -jest.mock('@sentry/utils', () => { - const original = jest.requireActual('@sentry/utils'); - return { - ...original, - supportsFetch(): boolean { - return fetchSupported; - }, - getGlobalObject(): any { - return { - fetch: () => {}, - }; - }, - }; -}); - -jest.mock('../../../src/transports/new-fetch', () => { - const original = jest.requireActual('../../../src/transports/new-fetch'); - return { - ...original, - makeNewFetchTransport: jest.fn(() => ({ - send: () => Promise.resolve({ status: 'success' }), - flush: () => Promise.resolve(true), - })), - }; -}); - -jest.mock('../../../src/transports/new-xhr', () => { - const original = jest.requireActual('../../../src/transports/new-xhr'); - return { - ...original, - makeNewXHRTransport: jest.fn(() => ({ - send: () => Promise.resolve({ status: 'success' }), - flush: () => Promise.resolve(true), - })), - }; -}); - -describe('setupBrowserTransport', () => { - afterEach(() => jest.clearAllMocks()); - - afterAll(() => jest.resetAllMocks()); - - it('returns NoopTransport if no dsn is passed', () => { - const { transport, newTransport } = setupBrowserTransport({}); - - expect(transport).toBeDefined(); - expect(transport).toBeInstanceOf(NoopTransport); - expect(newTransport).toBeUndefined(); - }); - - it('returns the instantiated transport passed via the options', () => { - const options = getDefaultBrowserClientOptions({ dsn: DSN, transport: SimpleTransport }); - const { transport, newTransport } = setupBrowserTransport(options); - - expect(transport).toBeDefined(); - expect(transport).toBeInstanceOf(SimpleTransport); - expect(newTransport).toBeUndefined(); - }); - - it('returns fetchTransports if fetch is supported', () => { - const options = getDefaultBrowserClientOptions({ dsn: DSN }); - delete options.transport; - const { transport, newTransport } = setupBrowserTransport(options); - - expect(transport).toBeDefined(); - expect(transport).toBeInstanceOf(FetchTransport); - expect(newTransport).toBeDefined(); - expect(makeNewFetchTransport).toHaveBeenCalledTimes(1); - expect(makeNewXHRTransport).toHaveBeenCalledTimes(0); - }); - - it('returns xhrTransports if fetch is not supported', () => { - fetchSupported = false; - - const options = getDefaultBrowserClientOptions({ dsn: DSN }); - delete options.transport; - const { transport, newTransport } = setupBrowserTransport(options); - - expect(transport).toBeDefined(); - expect(transport).toBeInstanceOf(XHRTransport); - expect(newTransport).toBeDefined(); - expect(makeNewFetchTransport).toHaveBeenCalledTimes(0); - expect(makeNewXHRTransport).toHaveBeenCalledTimes(1); - }); -}); diff --git a/packages/core/src/baseclient.ts b/packages/core/src/baseclient.ts index 314d3d6216a0..e2502289742a 100644 --- a/packages/core/src/baseclient.ts +++ b/packages/core/src/baseclient.ts @@ -4,10 +4,13 @@ import { Client, ClientOptions, DsnComponents, + Envelope, Event, EventHint, Integration, IntegrationClass, + NewTransport, + SessionAggregates, Severity, SeverityLevel, Transport, @@ -29,11 +32,10 @@ import { uuid4, } from '@sentry/utils'; -import { APIDetails, initAPIDetails } from './api'; +import { getEnvelopeEndpointWithUrlEncodedAuth } from './api'; import { IS_DEBUG_BUILD } from './flags'; import { IntegrationIndex, setupIntegrations } from './integration'; import { createEventEnvelope, createSessionEnvelope } from './request'; -import { NewTransport } from './transports/base'; const ALREADY_SEEN_ERROR = "Not capturing exception because it's already been captured."; @@ -75,6 +77,8 @@ export abstract class BaseClient implements Client { /** The client Dsn, if specified in options. Without this Dsn, the SDK will be disabled. */ protected readonly _dsn?: DsnComponents; + protected readonly _transport?: NewTransport; + /** Array of set up integrations. */ protected _integrations: IntegrationIndex = {}; @@ -84,12 +88,6 @@ export abstract class BaseClient implements Client { /** Number of calls being processed */ protected _numProcessing: number = 0; - /** Cached transport used internally. */ - protected _transport: Transport; - - /** New v7 Transport that is initialized alongside the old one */ - protected _newTransport?: NewTransport; - /** * Initializes this client instance. * @@ -97,25 +95,15 @@ export abstract class BaseClient implements Client { * @param transport The (old) Transport instance for the client to use (TODO(v7): remove) * @param newTransport The NewTransport instance for the client to use */ - protected constructor(options: O, transport: Transport, newTransport?: NewTransport) { + protected constructor(options: O) { this._options = options; - if (options.dsn) { this._dsn = makeDsn(options.dsn); + const url = getEnvelopeEndpointWithUrlEncodedAuth(this._dsn, options.tunnel); + this._transport = options.transport({ ...options.transportOptions, url }); } else { IS_DEBUG_BUILD && logger.warn('No DSN provided, client will not do anything.'); } - - // TODO(v7): remove old transport - this._transport = transport; - this._newTransport = newTransport; - - // TODO(v7): refactor this to keep metadata/api outside of transport. This hack is used to - // satisfy tests until we move to NewTransport where we have to revisit this. - (this._transport as unknown as { _api: Partial })._api = { - ...((this._transport as unknown as { _api: Partial })._api || {}), - metadata: options._metadata || {}, - }; } /** @@ -202,7 +190,7 @@ export abstract class BaseClient implements Client { if (!(typeof session.release === 'string')) { IS_DEBUG_BUILD && logger.warn('Discarded session because of missing or non-string release'); } else { - this._sendSession(session); + this.sendSession(session); // After sending, we set init false to indicate it's not the first occurrence session.update({ init: false }); } @@ -225,7 +213,7 @@ export abstract class BaseClient implements Client { /** * @inheritDoc */ - public getTransport(): Transport { + public getTransport(): NewTransport | undefined { return this._transport; } @@ -233,11 +221,14 @@ export abstract class BaseClient implements Client { * @inheritDoc */ public flush(timeout?: number): PromiseLike { - return this._isClientDoneProcessing(timeout).then(clientFinished => { - return this.getTransport() - .close(timeout) - .then(transportFlushed => clientFinished && transportFlushed); - }); + const transport = this._transport; + if (transport) { + return this._isClientDoneProcessing(timeout).then(clientFinished => { + return transport.flush(timeout).then(transportFlushed => clientFinished && transportFlushed); + }); + } else { + return resolvedSyncPromise(true); + } } /** @@ -276,50 +267,32 @@ export abstract class BaseClient implements Client { * @inheritDoc */ public sendEvent(event: Event): void { - // TODO(v7): Remove the if-else - if ( - this._newTransport && - this._options.dsn && - this._options._experiments && - this._options._experiments.newTransport - ) { - const api = initAPIDetails(this._options.dsn, this._options._metadata, this._options.tunnel); - const env = createEventEnvelope(event, api); - void this._newTransport.send(env).then(null, reason => { - IS_DEBUG_BUILD && logger.error('Error while sending event:', reason); - }); - } else { - void this._transport.sendEvent(event).then(null, reason => { - IS_DEBUG_BUILD && logger.error('Error while sending event:', reason); - }); + if (this._dsn) { + const env = createEventEnvelope(event, this._dsn, this._options._metadata, this._options.tunnel); + this.sendEnvelope(env); } } /** * @inheritDoc */ - public sendSession(session: Session): void { - if (!this._transport.sendSession) { - IS_DEBUG_BUILD && logger.warn("Dropping session because custom transport doesn't implement sendSession"); - return; + public sendSession(session: Session | SessionAggregates): void { + if (this._dsn) { + const [env] = createSessionEnvelope(session, this._dsn, this._options._metadata, this._options.tunnel); + this.sendEnvelope(env); } + } - // TODO(v7): Remove the if-else - if ( - this._newTransport && - this._options.dsn && - this._options._experiments && - this._options._experiments.newTransport - ) { - const api = initAPIDetails(this._options.dsn, this._options._metadata, this._options.tunnel); - const [env] = createSessionEnvelope(session, api); - void this._newTransport.send(env).then(null, reason => { - IS_DEBUG_BUILD && logger.error('Error while sending session:', reason); + /** + * @inheritdoc + */ + public sendEnvelope(env: Envelope): void { + if (this._transport && this._dsn) { + this._transport.send(env).then(null, reason => { + IS_DEBUG_BUILD && logger.error('Error while sending event:', reason); }); } else { - void this._transport.sendSession(session).then(null, reason => { - IS_DEBUG_BUILD && logger.error('Error while sending session:', reason); - }); + IS_DEBUG_BUILD && logger.error('Transport disabled'); } } @@ -356,12 +329,6 @@ export abstract class BaseClient implements Client { } } - /** Deliver captured session to Sentry */ - // TODO(v7): should this be deleted? - protected _sendSession(session: Session): void { - this.sendSession(session); - } - /** * Determine if the client is finished processing. Returns a promise because it will wait `timeout` ms before saying * "no" (resolving to `false`) in order to give the client a chance to potentially finish first. @@ -599,15 +566,16 @@ export abstract class BaseClient implements Client { protected _processEvent(event: Event, hint?: EventHint, scope?: Scope): PromiseLike { // eslint-disable-next-line @typescript-eslint/unbound-method const { beforeSend, sampleRate } = this.getOptions(); - const transport = this.getTransport(); type RecordLostEvent = NonNullable; type RecordLostEventParams = Parameters; - function recordLostEvent(outcome: RecordLostEventParams[0], category: RecordLostEventParams[1]): void { - if (transport.recordLostEvent) { - transport.recordLostEvent(outcome, category); - } + function recordLostEvent(_outcome: RecordLostEventParams[0], _category: RecordLostEventParams[1]): void { + // no-op as new transports don't have client outcomes + // TODO(v7): Re-add this functionality + // if (transport.recordLostEvent) { + // transport.recordLostEvent(outcome, category); + // } } if (!this._isEnabled()) { diff --git a/packages/core/src/index.ts b/packages/core/src/index.ts index da68917c936e..f5932bdbeaa4 100644 --- a/packages/core/src/index.ts +++ b/packages/core/src/index.ts @@ -1,12 +1,5 @@ export type { APIDetails } from './api'; export type { ClientClass } from './sdk'; -export type { - BaseTransportOptions, - NewTransport, - TransportMakeRequestResponse, - TransportRequest, - TransportRequestExecutor, -} from './transports/base'; export { addBreadcrumb, diff --git a/packages/core/src/request.ts b/packages/core/src/request.ts index 2d65006272dd..9e58710af719 100644 --- a/packages/core/src/request.ts +++ b/packages/core/src/request.ts @@ -1,8 +1,10 @@ import { + DsnComponents, Event, EventEnvelope, EventItem, SdkInfo, + SdkMetadata, SentryRequest, SentryRequestType, Session, @@ -15,11 +17,11 @@ import { createEnvelope, dsnToString, normalize, serializeEnvelope } from '@sent import { APIDetails, getEnvelopeEndpointWithUrlEncodedAuth, getStoreEndpointWithUrlEncodedAuth } from './api'; /** Extract sdk info from from the API metadata */ -function getSdkMetadataForEnvelopeHeader(api: APIDetails): SdkInfo | undefined { - if (!api.metadata || !api.metadata.sdk) { +function getSdkMetadataForEnvelopeHeader(metadata?: SdkMetadata): SdkInfo | undefined { + if (!metadata || !metadata.sdk) { return; } - const { name, version } = api.metadata.sdk; + const { name, version } = metadata.sdk; return { name, version }; } @@ -42,13 +44,15 @@ function enhanceEventWithSdkInfo(event: Event, sdkInfo?: SdkInfo): Event { /** Creates an envelope from a Session */ export function createSessionEnvelope( session: Session | SessionAggregates, - api: APIDetails, + dsn: DsnComponents, + metadata?: SdkMetadata, + tunnel?: string, ): [SessionEnvelope, SentryRequestType] { - const sdkInfo = getSdkMetadataForEnvelopeHeader(api); + const sdkInfo = getSdkMetadataForEnvelopeHeader(metadata); const envelopeHeaders = { sent_at: new Date().toISOString(), ...(sdkInfo && { sdk: sdkInfo }), - ...(!!api.tunnel && { dsn: dsnToString(api.dsn) }), + ...(!!tunnel && { dsn: dsnToString(dsn) }), }; // I know this is hacky but we don't want to add `sessions` to request type since it's never rate limited @@ -63,7 +67,7 @@ export function createSessionEnvelope( /** Creates a SentryRequest from a Session. */ export function sessionToSentryRequest(session: Session | SessionAggregates, api: APIDetails): SentryRequest { - const [envelope, type] = createSessionEnvelope(session, api); + const [envelope, type] = createSessionEnvelope(session, api.dsn, api.metadata, api.tunnel); return { body: serializeEnvelope(envelope), type, @@ -75,8 +79,13 @@ export function sessionToSentryRequest(session: Session | SessionAggregates, api * Create an Envelope from an event. Note that this is duplicated from below, * but on purpose as this will be refactored in v7. */ -export function createEventEnvelope(event: Event, api: APIDetails): EventEnvelope { - const sdkInfo = getSdkMetadataForEnvelopeHeader(api); +export function createEventEnvelope( + event: Event, + dsn: DsnComponents, + metadata?: SdkMetadata, + tunnel?: string, +): EventEnvelope { + const sdkInfo = getSdkMetadataForEnvelopeHeader(metadata); const eventType = event.type || 'event'; const { transactionSampling } = event.sdkProcessingMetadata || {}; @@ -96,7 +105,7 @@ export function createEventEnvelope(event: Event, api: APIDetails): EventEnvelop // 2. Restore the original version of the request body, which is commented out // 3. Search for either of the PR URLs above and pull out the companion hacks in the browser playwright tests and the // baseClient tests in this package - enhanceEventWithSdkInfo(event, api.metadata.sdk); + enhanceEventWithSdkInfo(event, metadata && metadata.sdk); event.tags = event.tags || {}; event.extra = event.extra || {}; @@ -115,7 +124,7 @@ export function createEventEnvelope(event: Event, api: APIDetails): EventEnvelop event_id: event.event_id as string, sent_at: new Date().toISOString(), ...(sdkInfo && { sdk: sdkInfo }), - ...(!!api.tunnel && { dsn: dsnToString(api.dsn) }), + ...(!!tunnel && { dsn: dsnToString(dsn) }), }; const eventItem: EventItem = [ { @@ -129,7 +138,7 @@ export function createEventEnvelope(event: Event, api: APIDetails): EventEnvelop /** Creates a SentryRequest from an event. */ export function eventToSentryRequest(event: Event, api: APIDetails): SentryRequest { - const sdkInfo = getSdkMetadataForEnvelopeHeader(api); + const sdkInfo = getSdkMetadataForEnvelopeHeader(api.metadata); const eventType = event.type || 'event'; const useEnvelope = eventType === 'transaction' || !!api.tunnel; diff --git a/packages/core/src/sdk.ts b/packages/core/src/sdk.ts index c7f7bb4916a3..b549163a1ecc 100644 --- a/packages/core/src/sdk.ts +++ b/packages/core/src/sdk.ts @@ -1,16 +1,11 @@ import { getCurrentHub } from '@sentry/hub'; -import { Client, ClientOptions, Transport } from '@sentry/types'; +import { Client, ClientOptions } from '@sentry/types'; import { logger } from '@sentry/utils'; import { IS_DEBUG_BUILD } from './flags'; -import { NewTransport } from './transports/base'; /** A class object that can instantiate Client objects. */ -export type ClientClass = new ( - options: O, - transport: Transport, - newTransport?: NewTransport, -) => F; +export type ClientClass = new (options: O) => F; /** * Internal function to create a new SDK client instance. The client is @@ -22,8 +17,6 @@ export type ClientClass = new ( export function initAndBind( clientClass: ClientClass, options: O, - transport: Transport, - newTransport?: NewTransport, ): void { if (options.debug === true) { if (IS_DEBUG_BUILD) { @@ -40,6 +33,6 @@ export function initAndBind( scope.update(options.initialScope); } - const client = new clientClass(options, transport, newTransport); + const client = new clientClass(options); hub.bindClient(client); } diff --git a/packages/core/src/transports/base.ts b/packages/core/src/transports/base.ts index 787e175b9985..8c6cfe373bfe 100644 --- a/packages/core/src/transports/base.ts +++ b/packages/core/src/transports/base.ts @@ -1,4 +1,12 @@ -import { Envelope, EventStatus } from '@sentry/types'; +import { + Envelope, + InternalBaseTransportOptions, + NewTransport, + TransportCategory, + TransportRequest, + TransportRequestExecutor, + TransportResponse, +} from '@sentry/types'; import { disabledUntil, eventStatusFromHttpCode, @@ -13,58 +21,6 @@ import { updateRateLimits, } from '@sentry/utils'; -export const ERROR_TRANSPORT_CATEGORY = 'error'; - -export const TRANSACTION_TRANSPORT_CATEGORY = 'transaction'; - -export const ATTACHMENT_TRANSPORT_CATEGORY = 'attachment'; - -export const SESSION_TRANSPORT_CATEGORY = 'session'; - -type TransportCategory = - | typeof ERROR_TRANSPORT_CATEGORY - | typeof TRANSACTION_TRANSPORT_CATEGORY - | typeof ATTACHMENT_TRANSPORT_CATEGORY - | typeof SESSION_TRANSPORT_CATEGORY; - -export type TransportRequest = { - body: string; - category: TransportCategory; -}; - -export type TransportMakeRequestResponse = { - body?: string; - headers?: { - [key: string]: string | null; - 'x-sentry-rate-limits': string | null; - 'retry-after': string | null; - }; - reason?: string; - statusCode: number; -}; - -export type TransportResponse = { - status: EventStatus; - reason?: string; -}; - -interface InternalBaseTransportOptions { - bufferSize?: number; -} -export interface BaseTransportOptions extends InternalBaseTransportOptions { - // url to send the event - // transport does not care about dsn specific - client should take care of - // parsing and figuring that out - url: string; -} - -export interface NewTransport { - send(request: Envelope): PromiseLike; - flush(timeout?: number): PromiseLike; -} - -export type TransportRequestExecutor = (request: TransportRequest) => PromiseLike; - export const DEFAULT_TRANSPORT_BUFFER_SIZE = 30; /** diff --git a/packages/core/test/lib/base.test.ts b/packages/core/test/lib/base.test.ts index 47ec8ae70266..feb18390c3e8 100644 --- a/packages/core/test/lib/base.test.ts +++ b/packages/core/test/lib/base.test.ts @@ -1,12 +1,11 @@ import { Hub, Scope, Session } from '@sentry/hub'; -import { Event, Span, Transport } from '@sentry/types'; +import { Event, Span } from '@sentry/types'; import { dsnToString, logger, SentryError, SyncPromise } from '@sentry/utils'; import * as integrationModule from '../../src/integration'; -import { NoopTransport } from '../../src/transports/noop'; -import { getDefaultTestClientOptions, setupTestTransport, TestClient } from '../mocks/client'; +import { getDefaultTestClientOptions, TestClient } from '../mocks/client'; import { TestIntegration } from '../mocks/integration'; -import { FakeTransport } from '../mocks/transport'; +import { makeFakeTransport } from '../mocks/transport'; const PUBLIC_DSN = 'https://username@domain/123'; // eslint-disable-next-line no-var @@ -68,7 +67,7 @@ describe('BaseClient', () => { expect.assertions(1); const options = getDefaultTestClientOptions({ dsn: PUBLIC_DSN }); - const client = new TestClient(options, setupTestTransport(options).transport); + const client = new TestClient(options); expect(dsnToString(client.getDsn()!)).toBe(PUBLIC_DSN); }); @@ -76,7 +75,7 @@ describe('BaseClient', () => { expect.assertions(1); const options = getDefaultTestClientOptions(); - const client = new TestClient(options, setupTestTransport(options).transport); + const client = new TestClient(options); expect(client.getDsn()).toBeUndefined(); }); @@ -85,7 +84,7 @@ describe('BaseClient', () => { expect.assertions(1); const options = getDefaultTestClientOptions({ dsn: 'abc' }); - expect(() => new TestClient(options, setupTestTransport(options).transport)).toThrow(SentryError); + expect(() => new TestClient(options)).toThrow(SentryError); }); }); @@ -94,31 +93,20 @@ describe('BaseClient', () => { expect.assertions(1); const options = getDefaultTestClientOptions({ dsn: PUBLIC_DSN, test: true }); - const client = new TestClient(options, setupTestTransport(options).transport); + const client = new TestClient(options); expect(client.getOptions()).toEqual(options); }); }); describe('getTransport()', () => { - test('returns the transport from client', () => { - expect.assertions(2); - - const options = getDefaultTestClientOptions({ dsn: PUBLIC_DSN, transport: FakeTransport }); - const client = new TestClient(options, new FakeTransport()); - - expect(client.getTransport()).toBeInstanceOf(FakeTransport); - expect(TestClient.instance!.getTransport()).toBe(client.getTransport()); - }); - - test('retruns NoopTransport when no transport is passed', () => { - expect.assertions(2); + test('returns undefined when no dsn is set', () => { + expect.assertions(1); - const options = getDefaultTestClientOptions({ dsn: PUBLIC_DSN }); - const client = new TestClient(options, setupTestTransport(options).transport); + const options = getDefaultTestClientOptions({}); + const client = new TestClient(options); - expect(client.getTransport()).toBeInstanceOf(NoopTransport); - expect(TestClient.instance!.getTransport()).toBe(client.getTransport()); + expect(client.getTransport()).toBeUndefined(); }); }); @@ -127,7 +115,7 @@ describe('BaseClient', () => { expect.assertions(1); const options = getDefaultTestClientOptions({}); - const client = new TestClient(options, setupTestTransport(options).transport); + const client = new TestClient(options); const scope = new Scope(); const hub = new Hub(client, scope); @@ -141,7 +129,7 @@ describe('BaseClient', () => { expect.assertions(1); const options = getDefaultTestClientOptions({}); - const client = new TestClient(options, setupTestTransport(options).transport); + const client = new TestClient(options); const scope = new Scope(); const hub = new Hub(client, scope); @@ -155,7 +143,7 @@ describe('BaseClient', () => { expect.assertions(2); const options = getDefaultTestClientOptions({ maxBreadcrumbs: 1 }); - const client = new TestClient(options, setupTestTransport(options).transport); + const client = new TestClient(options); const scope = new Scope(); const hub = new Hub(client, scope); @@ -170,7 +158,7 @@ describe('BaseClient', () => { expect.assertions(1); const options = getDefaultTestClientOptions({}); - const client = new TestClient(options, setupTestTransport(options).transport); + const client = new TestClient(options); const scope = new Scope(); const hub = new Hub(client, scope); @@ -185,7 +173,7 @@ describe('BaseClient', () => { const beforeBreadcrumb = jest.fn(breadcrumb => breadcrumb); const options = getDefaultTestClientOptions({ beforeBreadcrumb }); - const client = new TestClient(options, setupTestTransport(options).transport); + const client = new TestClient(options); const scope = new Scope(); const hub = new Hub(client, scope); @@ -199,7 +187,7 @@ describe('BaseClient', () => { const beforeBreadcrumb = jest.fn(() => ({ message: 'changed' })); const options = getDefaultTestClientOptions({ beforeBreadcrumb }); - const client = new TestClient(options, setupTestTransport(options).transport); + const client = new TestClient(options); const scope = new Scope(); const hub = new Hub(client, scope); @@ -213,7 +201,7 @@ describe('BaseClient', () => { const beforeBreadcrumb = jest.fn(() => null); const options = getDefaultTestClientOptions({ beforeBreadcrumb }); - const client = new TestClient(options, setupTestTransport(options).transport); + const client = new TestClient(options); const scope = new Scope(); const hub = new Hub(client, scope); @@ -227,7 +215,7 @@ describe('BaseClient', () => { const beforeBreadcrumb = jest.fn((breadcrumb, hint) => ({ ...breadcrumb, data: hint.data })); const options = getDefaultTestClientOptions({ beforeBreadcrumb }); - const client = new TestClient(options, setupTestTransport(options).transport); + const client = new TestClient(options); const scope = new Scope(); const hub = new Hub(client, scope); @@ -241,7 +229,7 @@ describe('BaseClient', () => { describe('captureException', () => { test('captures and sends exceptions', () => { const options = getDefaultTestClientOptions({ dsn: PUBLIC_DSN }); - const client = new TestClient(options, setupTestTransport(options).transport); + const client = new TestClient(options); client.captureException(new Error('test exception')); @@ -264,7 +252,7 @@ describe('BaseClient', () => { test('allows for providing explicit scope', () => { const options = getDefaultTestClientOptions({ dsn: PUBLIC_DSN }); - const client = new TestClient(options, setupTestTransport(options).transport); + const client = new TestClient(options); const scope = new Scope(); scope.setExtra('foo', 'wat'); @@ -292,7 +280,7 @@ describe('BaseClient', () => { test('allows for clearing data from existing scope if explicit one does so in a callback function', () => { const options = getDefaultTestClientOptions({ dsn: PUBLIC_DSN }); - const client = new TestClient(options, setupTestTransport(options).transport); + const client = new TestClient(options); const scope = new Scope(); scope.setExtra('foo', 'wat'); @@ -327,7 +315,7 @@ describe('BaseClient', () => { // is encountered, so this test doesn't apply. ])("doesn't capture the same exception twice - %s", (_name: string, thrown: any) => { const options = getDefaultTestClientOptions({ dsn: PUBLIC_DSN }); - const client = new TestClient(options, setupTestTransport(options).transport); + const client = new TestClient(options); expect(thrown.__sentry_captured__).toBeUndefined(); @@ -346,7 +334,7 @@ describe('BaseClient', () => { describe('captureMessage', () => { test('captures and sends messages', () => { const options = getDefaultTestClientOptions({ dsn: PUBLIC_DSN }); - const client = new TestClient(options, setupTestTransport(options).transport); + const client = new TestClient(options); client.captureMessage('test message'); @@ -363,7 +351,7 @@ describe('BaseClient', () => { test('should call eventFromException if input to captureMessage is not a primitive', () => { const options = getDefaultTestClientOptions({ dsn: PUBLIC_DSN }); - const client = new TestClient(options, setupTestTransport(options).transport); + const client = new TestClient(options); const spy = jest.spyOn(TestClient.instance!, 'eventFromException'); client.captureMessage('foo'); @@ -382,7 +370,7 @@ describe('BaseClient', () => { test('allows for providing explicit scope', () => { const options = getDefaultTestClientOptions({ dsn: PUBLIC_DSN }); - const client = new TestClient(options, setupTestTransport(options).transport); + const client = new TestClient(options); const scope = new Scope(); scope.setExtra('foo', 'wat'); @@ -416,7 +404,7 @@ describe('BaseClient', () => { expect.assertions(1); const options = getDefaultTestClientOptions({ enabled: false, dsn: PUBLIC_DSN }); - const client = new TestClient(options, setupTestTransport(options).transport); + const client = new TestClient(options); const scope = new Scope(); client.captureEvent({}, undefined, scope); @@ -428,7 +416,7 @@ describe('BaseClient', () => { expect.assertions(1); const options = getDefaultTestClientOptions({}); - const client = new TestClient(options, setupTestTransport(options).transport); + const client = new TestClient(options); const scope = new Scope(); client.captureEvent({}, undefined, scope); @@ -449,7 +437,7 @@ describe('BaseClient', () => { // Note: this is the same test as in `describe(captureException)`, except with the exception already wrapped in a // hint and accompanying an event. Duplicated here because some methods skip `captureException` and go straight to // `captureEvent`. - const client = new TestClient(options, setupTestTransport(options).transport); + const client = new TestClient(options); const event = { exception: { values: [{ type: 'Error', message: 'Will I get caught twice?' }] } }; const hint = { originalException: thrown }; @@ -470,7 +458,7 @@ describe('BaseClient', () => { expect.assertions(2); const options = getDefaultTestClientOptions({ dsn: PUBLIC_DSN }); - const client = new TestClient(options, setupTestTransport(options).transport); + const client = new TestClient(options); const scope = new Scope(); client.captureEvent({ message: 'message' }, undefined, scope); @@ -490,7 +478,7 @@ describe('BaseClient', () => { expect.assertions(2); const options = getDefaultTestClientOptions({ dsn: PUBLIC_DSN }); - const client = new TestClient(options, setupTestTransport(options).transport); + const client = new TestClient(options); const scope = new Scope(); client.captureEvent({ message: 'message', timestamp: 1234 }, undefined, scope); @@ -510,7 +498,7 @@ describe('BaseClient', () => { expect.assertions(1); const options = getDefaultTestClientOptions({ dsn: PUBLIC_DSN }); - const client = new TestClient(options, setupTestTransport(options).transport); + const client = new TestClient(options); const scope = new Scope(); client.captureEvent({ message: 'message' }, { event_id: 'wat' }, scope); @@ -529,7 +517,7 @@ describe('BaseClient', () => { expect.assertions(1); const options = getDefaultTestClientOptions({ dsn: PUBLIC_DSN }); - const client = new TestClient(options, setupTestTransport(options).transport); + const client = new TestClient(options); const scope = new Scope(); client.captureEvent({ message: 'message' }, undefined, scope); @@ -548,7 +536,7 @@ describe('BaseClient', () => { expect.assertions(1); const options = getDefaultTestClientOptions({ environment: 'env', dsn: PUBLIC_DSN }); - const client = new TestClient(options, setupTestTransport(options).transport); + const client = new TestClient(options); const scope = new Scope(); client.captureEvent({ message: 'message' }, undefined, scope); @@ -567,7 +555,7 @@ describe('BaseClient', () => { expect.assertions(1); const options = getDefaultTestClientOptions({ dsn: PUBLIC_DSN, environment: undefined }); - const client = new TestClient(options, setupTestTransport(options).transport); + const client = new TestClient(options); const scope = new Scope(); client.captureEvent({ message: 'message' }, undefined, scope); @@ -586,7 +574,7 @@ describe('BaseClient', () => { expect.assertions(1); const options = getDefaultTestClientOptions({ dsn: PUBLIC_DSN, release: 'v1.0.0' }); - const client = new TestClient(options, setupTestTransport(options).transport); + const client = new TestClient(options); const scope = new Scope(); client.captureEvent({ message: 'message' }, undefined, scope); @@ -606,7 +594,7 @@ describe('BaseClient', () => { expect.assertions(4); const options = getDefaultTestClientOptions({ dsn: PUBLIC_DSN }); - const client = new TestClient(options, setupTestTransport(options).transport); + const client = new TestClient(options); const scope = new Scope(); scope.addBreadcrumb({ message: 'breadcrumb' }, 100); @@ -622,7 +610,7 @@ describe('BaseClient', () => { expect.assertions(2); const options = getDefaultTestClientOptions({ dsn: PUBLIC_DSN, maxBreadcrumbs: 1 }); - const client = new TestClient(options, setupTestTransport(options).transport); + const client = new TestClient(options); const scope = new Scope(); const hub = new Hub(client, scope); hub.addBreadcrumb({ message: '1' }); @@ -638,7 +626,7 @@ describe('BaseClient', () => { expect.assertions(1); const options = getDefaultTestClientOptions({ dsn: PUBLIC_DSN }); - const client = new TestClient(options, setupTestTransport(options).transport); + const client = new TestClient(options); const scope = new Scope(); scope.setExtra('b', 'b'); scope.setTag('a', 'a'); @@ -663,7 +651,7 @@ describe('BaseClient', () => { expect.assertions(1); const options = getDefaultTestClientOptions({ dsn: PUBLIC_DSN }); - const client = new TestClient(options, setupTestTransport(options).transport); + const client = new TestClient(options); const scope = new Scope(); scope.setFingerprint(['abcd']); @@ -682,7 +670,7 @@ describe('BaseClient', () => { test('adds installed integrations to sdk info', () => { const options = getDefaultTestClientOptions({ dsn: PUBLIC_DSN, integrations: [new TestIntegration()] }); - const client = new TestClient(options, setupTestTransport(options).transport); + const client = new TestClient(options); client.setupIntegrations(); client.captureEvent({ message: 'message' }); @@ -696,7 +684,7 @@ describe('BaseClient', () => { expect.assertions(1); const options = getDefaultTestClientOptions({ dsn: PUBLIC_DSN }); - const client = new TestClient(options, setupTestTransport(options).transport); + const client = new TestClient(options); const fourLevelsObject = { a: { b: { @@ -748,7 +736,7 @@ describe('BaseClient', () => { expect.assertions(1); const options = getDefaultTestClientOptions({ dsn: PUBLIC_DSN, normalizeDepth: 2 }); - const client = new TestClient(options, setupTestTransport(options).transport); + const client = new TestClient(options); const fourLevelsObject = { a: { b: { @@ -797,7 +785,7 @@ describe('BaseClient', () => { expect.assertions(1); const options = getDefaultTestClientOptions({ dsn: PUBLIC_DSN, normalizeDepth: 0 }); - const client = new TestClient(options, setupTestTransport(options).transport); + const client = new TestClient(options); const fourLevelsObject = { a: { b: { @@ -851,7 +839,7 @@ describe('BaseClient', () => { expect.assertions(1); const options = getDefaultTestClientOptions({ dsn: PUBLIC_DSN }); - const client = new TestClient(options, setupTestTransport(options).transport); + const client = new TestClient(options); const transaction: Event = { contexts: { trace: { @@ -926,7 +914,7 @@ describe('BaseClient', () => { const beforeSend = jest.fn(event => event); const options = getDefaultTestClientOptions({ dsn: PUBLIC_DSN, beforeSend }); - const client = new TestClient(options, setupTestTransport(options).transport); + const client = new TestClient(options); client.captureEvent({ message: 'hello' }); @@ -938,7 +926,7 @@ describe('BaseClient', () => { const beforeSend = jest.fn(() => ({ message: 'changed1' })); const options = getDefaultTestClientOptions({ dsn: PUBLIC_DSN, beforeSend }); - const client = new TestClient(options, setupTestTransport(options).transport); + const client = new TestClient(options); client.captureEvent({ message: 'hello' }); @@ -950,7 +938,7 @@ describe('BaseClient', () => { const beforeSend = jest.fn(() => null); const options = getDefaultTestClientOptions({ dsn: PUBLIC_DSN, beforeSend }); - const client = new TestClient(options, setupTestTransport(options).transport); + const client = new TestClient(options); const captureExceptionSpy = jest.spyOn(client, 'captureException'); const loggerErrorSpy = jest.spyOn(logger, 'error'); @@ -969,7 +957,7 @@ describe('BaseClient', () => { const beforeSend = jest.fn(() => val); // @ts-ignore we need to test regular-js behavior const options = getDefaultTestClientOptions({ dsn: PUBLIC_DSN, beforeSend }); - const client = new TestClient(options, setupTestTransport(options).transport); + const client = new TestClient(options); const loggerErrorSpy = jest.spyOn(logger, 'error'); client.captureEvent({ message: 'hello' }); @@ -994,7 +982,7 @@ describe('BaseClient', () => { }), ); const options = getDefaultTestClientOptions({ dsn: PUBLIC_DSN, beforeSend }); - const client = new TestClient(options, setupTestTransport(options).transport); + const client = new TestClient(options); client.captureEvent({ message: 'hello' }); jest.runOnlyPendingTimers(); @@ -1023,7 +1011,7 @@ describe('BaseClient', () => { }), ); const options = getDefaultTestClientOptions({ dsn: PUBLIC_DSN, beforeSend }); - const client = new TestClient(options, setupTestTransport(options).transport); + const client = new TestClient(options); client.captureEvent({ message: 'hello' }); jest.runOnlyPendingTimers(); @@ -1052,7 +1040,7 @@ describe('BaseClient', () => { }), ); const options = getDefaultTestClientOptions({ dsn: PUBLIC_DSN, beforeSend }); - const client = new TestClient(options, setupTestTransport(options).transport); + const client = new TestClient(options); client.captureEvent({ message: 'hello' }); jest.runAllTimers(); @@ -1065,7 +1053,7 @@ describe('BaseClient', () => { const beforeSend = jest.fn((event, hint) => ({ ...event, data: hint.data })); const options = getDefaultTestClientOptions({ dsn: PUBLIC_DSN, beforeSend }); - const client = new TestClient(options, setupTestTransport(options).transport); + const client = new TestClient(options); client.captureEvent({ message: 'hello' }, { data: 'someRandomThing' }); @@ -1073,38 +1061,35 @@ describe('BaseClient', () => { expect((TestClient.instance!.event! as any).data).toBe('someRandomThing'); }); - test('beforeSend records dropped events', () => { - expect.assertions(1); + // TODO(v7): Add back test with client reports + // test('beforeSend records dropped events', () => { + // expect.assertions(1); - const client = new TestClient( - getDefaultTestClientOptions({ - dsn: PUBLIC_DSN, - beforeSend() { - return null; - }, - }), - setupTestTransport(getDefaultTestClientOptions({ dsn: PUBLIC_DSN })).transport, - ); - const recordLostEventSpy = jest.fn(); - jest.spyOn(client, 'getTransport').mockImplementationOnce( - () => - ({ - recordLostEvent: recordLostEventSpy, - } as any as Transport), - ); + // const client = new TestClient( + // getDefaultTestClientOptions({ + // dsn: PUBLIC_DSN, + // beforeSend() { + // return null; + // }, + // }), + // ); + // const recordLostEventSpy = jest.fn(); + // jest.spyOn(client, 'getTransport').mockImplementationOnce( + // () => + // ({ + // recordLostEvent: recordLostEventSpy, + // } as any as Transport), + // ); - client.captureEvent({ message: 'hello' }, {}); + // client.captureEvent({ message: 'hello' }, {}); - expect(recordLostEventSpy).toHaveBeenCalledWith('before_send', 'event'); - }); + // expect(recordLostEventSpy).toHaveBeenCalledWith('before_send', 'event'); + // }); test('eventProcessor can drop the even when it returns null', () => { expect.assertions(3); - const client = new TestClient( - getDefaultTestClientOptions({ dsn: PUBLIC_DSN }), - setupTestTransport(getDefaultTestClientOptions({ dsn: PUBLIC_DSN })).transport, - ); + const client = new TestClient(getDefaultTestClientOptions({ dsn: PUBLIC_DSN })); const captureExceptionSpy = jest.spyOn(client, 'captureException'); const loggerErrorSpy = jest.spyOn(logger, 'error'); const scope = new Scope(); @@ -1117,33 +1102,34 @@ describe('BaseClient', () => { expect(loggerErrorSpy).toBeCalledWith(new SentryError('An event processor returned null, will not send event.')); }); - test('eventProcessor records dropped events', () => { - expect.assertions(1); + // TODO(v7): Add back tests with client reports + // test('eventProcessor records dropped events', () => { + // expect.assertions(1); - const options = getDefaultTestClientOptions({ dsn: PUBLIC_DSN }); - const client = new TestClient(options, setupTestTransport(options).transport); - - const recordLostEventSpy = jest.fn(); - jest.spyOn(client, 'getTransport').mockImplementationOnce( - () => - ({ - recordLostEvent: recordLostEventSpy, - } as any as Transport), - ); + // const options = getDefaultTestClientOptions({ dsn: PUBLIC_DSN }); + // const client = new TestClient(options); - const scope = new Scope(); - scope.addEventProcessor(() => null); + // const recordLostEventSpy = jest.fn(); + // jest.spyOn(client, 'getTransport').mockImplementationOnce( + // () => + // ({ + // recordLostEvent: recordLostEventSpy, + // } as any as Transport), + // ); - client.captureEvent({ message: 'hello' }, {}, scope); + // const scope = new Scope(); + // scope.addEventProcessor(() => null); - expect(recordLostEventSpy).toHaveBeenCalledWith('event_processor', 'event'); - }); + // client.captureEvent({ message: 'hello' }, {}, scope); + + // expect(recordLostEventSpy).toHaveBeenCalledWith('event_processor', 'event'); + // }); test('eventProcessor sends an event and logs when it crashes', () => { expect.assertions(3); const options = getDefaultTestClientOptions({ dsn: PUBLIC_DSN }); - const client = new TestClient(options, setupTestTransport(options).transport); + const client = new TestClient(options); const captureExceptionSpy = jest.spyOn(client, 'captureException'); const loggerErrorSpy = jest.spyOn(logger, 'error'); const scope = new Scope(); @@ -1168,23 +1154,24 @@ describe('BaseClient', () => { ); }); - test('records events dropped due to sampleRate', () => { - expect.assertions(1); + // TODO(v7): Add back test with client reports + // test('records events dropped due to sampleRate', () => { + // expect.assertions(1); - const options = getDefaultTestClientOptions({ dsn: PUBLIC_DSN, sampleRate: 0 }); - const client = new TestClient(options, setupTestTransport(options).transport); + // const options = getDefaultTestClientOptions({ dsn: PUBLIC_DSN, sampleRate: 0 }); + // const client = new TestClient(options); - const recordLostEventSpy = jest.fn(); - jest.spyOn(client, 'getTransport').mockImplementationOnce( - () => - ({ - recordLostEvent: recordLostEventSpy, - } as any as Transport), - ); + // const recordLostEventSpy = jest.fn(); + // jest.spyOn(client, 'getTransport').mockImplementationOnce( + // () => + // ({ + // recordLostEvent: recordLostEventSpy, + // } as any as Transport), + // ); - client.captureEvent({ message: 'hello' }, {}); - expect(recordLostEventSpy).toHaveBeenCalledWith('sample_rate', 'event'); - }); + // client.captureEvent({ message: 'hello' }, {}); + // expect(recordLostEventSpy).toHaveBeenCalledWith('sample_rate', 'event'); + // }); }); describe('integrations', () => { @@ -1196,7 +1183,7 @@ describe('BaseClient', () => { expect.assertions(2); const options = getDefaultTestClientOptions({ dsn: PUBLIC_DSN, integrations: [new TestIntegration()] }); - const client = new TestClient(options, setupTestTransport(options).transport); + const client = new TestClient(options); client.setupIntegrations(); expect(Object.keys((client as any)._integrations).length).toBe(1); @@ -1207,7 +1194,7 @@ describe('BaseClient', () => { expect.assertions(2); const options = getDefaultTestClientOptions({ integrations: [new TestIntegration()] }); - const client = new TestClient(options, setupTestTransport(options).transport); + const client = new TestClient(options); client.setupIntegrations(); expect(Object.keys((client as any)._integrations).length).toBe(0); @@ -1222,7 +1209,7 @@ describe('BaseClient', () => { enabled: false, integrations: [new TestIntegration()], }); - const client = new TestClient(options, setupTestTransport(options).transport); + const client = new TestClient(options); client.setupIntegrations(); expect(Object.keys((client as any)._integrations).length).toBe(0); @@ -1233,7 +1220,7 @@ describe('BaseClient', () => { expect.assertions(4); const options = getDefaultTestClientOptions({ dsn: PUBLIC_DSN, integrations: [new TestIntegration()] }); - const client = new TestClient(options, setupTestTransport(options).transport); + const client = new TestClient(options); // note: not the `Client` method `setupIntegrations`, but the free-standing function which that method calls const setupIntegrationsHelper = jest.spyOn(integrationModule, 'setupIntegrations'); @@ -1254,47 +1241,43 @@ describe('BaseClient', () => { describe('flush/close', () => { test('flush', async () => { jest.useRealTimers(); - expect.assertions(5); + expect.assertions(4); + + const { makeTransport, getSendCalled, getSentCount, delay } = makeFakeTransport(1); const client = new TestClient( getDefaultTestClientOptions({ dsn: PUBLIC_DSN, enableSend: true, - transport: FakeTransport, + transport: makeTransport, }), - new FakeTransport(), ); - const delay = 1; - const transportInstance = client.getTransport() as FakeTransport; - transportInstance.delay = delay; - client.captureMessage('test'); - expect(transportInstance).toBeInstanceOf(FakeTransport); - expect(transportInstance.sendCalled).toEqual(1); - expect(transportInstance.sentCount).toEqual(0); + expect(getSendCalled()).toEqual(1); + expect(getSentCount()).toEqual(0); await client.flush(delay); - expect(transportInstance.sentCount).toEqual(1); - expect(transportInstance.sendCalled).toEqual(1); + expect(getSentCount()).toEqual(1); + expect(getSendCalled()).toEqual(1); }); test('flush with some events being processed async', async () => { jest.useRealTimers(); - expect.assertions(5); + expect.assertions(4); + + const { makeTransport, getSendCalled, getSentCount, delay } = makeFakeTransport(300); const client = new TestClient( getDefaultTestClientOptions({ dsn: PUBLIC_DSN, enableSend: true, - transport: FakeTransport, + transport: makeTransport, }), - new FakeTransport(), ); - const delay = 300; const spy = jest.spyOn(TestClient.instance!, 'eventFromMessage'); spy.mockImplementationOnce( (message, level) => @@ -1302,20 +1285,16 @@ describe('BaseClient', () => { setTimeout(() => resolve({ message, level }), 150); }), ); - const transportInstance = client.getTransport() as FakeTransport; - transportInstance.delay = delay; - client.captureMessage('test async'); client.captureMessage('test non-async'); - expect(transportInstance).toBeInstanceOf(FakeTransport); - expect(transportInstance.sendCalled).toEqual(1); - expect(transportInstance.sentCount).toEqual(0); + expect(getSendCalled()).toEqual(1); + expect(getSentCount()).toEqual(0); await client.flush(delay); - expect(transportInstance.sentCount).toEqual(2); - expect(transportInstance.sendCalled).toEqual(2); + expect(getSentCount()).toEqual(2); + expect(getSendCalled()).toEqual(2); spy.mockRestore(); }); @@ -1324,19 +1303,15 @@ describe('BaseClient', () => { jest.useRealTimers(); expect.assertions(2); + const { makeTransport, delay } = makeFakeTransport(300); + const client = new TestClient( getDefaultTestClientOptions({ dsn: PUBLIC_DSN, enableSend: true, - transport: FakeTransport, + transport: makeTransport, }), - new FakeTransport(), ); - - const delay = 1; - const transportInstance = client.getTransport() as FakeTransport; - transportInstance.delay = delay; - expect(client.captureMessage('test')).toBeTruthy(); await client.close(delay); @@ -1350,7 +1325,7 @@ describe('BaseClient', () => { expect.assertions(3); const options = getDefaultTestClientOptions({ dsn: PUBLIC_DSN }); - const client = new TestClient(options, setupTestTransport(options).transport); + const client = new TestClient(options); return Promise.all([ client.flush(1).then(() => { @@ -1371,7 +1346,7 @@ describe('BaseClient', () => { expect.assertions(1); const options = getDefaultTestClientOptions({ dsn: PUBLIC_DSN }); - const client = new TestClient(options, setupTestTransport(options).transport); + const client = new TestClient(options); const session = new Session({ release: 'test' }); client.captureSession(session); @@ -1383,7 +1358,7 @@ describe('BaseClient', () => { expect.assertions(1); const options = getDefaultTestClientOptions({ enabled: false, dsn: PUBLIC_DSN }); - const client = new TestClient(options, setupTestTransport(options).transport); + const client = new TestClient(options); const session = new Session({ release: 'test' }); client.captureSession(session); diff --git a/packages/core/test/lib/sdk.test.ts b/packages/core/test/lib/sdk.test.ts index 7dd3229c5c7e..d2857a2d83bd 100644 --- a/packages/core/test/lib/sdk.test.ts +++ b/packages/core/test/lib/sdk.test.ts @@ -3,7 +3,7 @@ import { Client, Integration } from '@sentry/types'; import { installedIntegrations } from '../../src/integration'; import { initAndBind } from '../../src/sdk'; -import { getDefaultTestClientOptions, setupTestTransport, TestClient } from '../mocks/client'; +import { getDefaultTestClientOptions, TestClient } from '../mocks/client'; // eslint-disable-next-line no-var declare var global: any; @@ -56,7 +56,7 @@ describe('SDK', () => { new MockIntegration('MockIntegration 2'), ]; const options = getDefaultTestClientOptions({ dsn: PUBLIC_DSN, integrations }); - initAndBind(TestClient, options, setupTestTransport(options).transport); + initAndBind(TestClient, options); expect((integrations[0].setupOnce as jest.Mock).mock.calls.length).toBe(1); expect((integrations[1].setupOnce as jest.Mock).mock.calls.length).toBe(1); }); diff --git a/packages/core/test/lib/transports/base.test.ts b/packages/core/test/lib/transports/base.test.ts index 2257a67165b1..78b40d0a4f7c 100644 --- a/packages/core/test/lib/transports/base.test.ts +++ b/packages/core/test/lib/transports/base.test.ts @@ -1,14 +1,11 @@ -import { EventEnvelope, EventItem } from '@sentry/types'; +import { EventEnvelope, EventItem, NewTransport, TransportMakeRequestResponse, TransportResponse } from '@sentry/types'; import { createEnvelope, PromiseBuffer, resolvedSyncPromise, serializeEnvelope } from '@sentry/utils'; -import { - createTransport, - ERROR_TRANSPORT_CATEGORY, - NewTransport, - TRANSACTION_TRANSPORT_CATEGORY, - TransportMakeRequestResponse, - TransportResponse, -} from '../../../src/transports/base'; +import { createTransport } from '../../../src/transports/base'; + +const ERROR_TRANSPORT_CATEGORY = 'error'; + +const TRANSACTION_TRANSPORT_CATEGORY = 'transaction'; const ERROR_ENVELOPE = createEnvelope({ event_id: 'aa3ff046696b4bc6b609ce6d28fde9e2', sent_at: '123' }, [ [{ type: 'event' }, { event_id: 'aa3ff046696b4bc6b609ce6d28fde9e2' }] as EventItem, diff --git a/packages/core/test/mocks/client.ts b/packages/core/test/mocks/client.ts index 5778dcf5e193..9f02e5b6540d 100644 --- a/packages/core/test/mocks/client.ts +++ b/packages/core/test/mocks/client.ts @@ -1,16 +1,15 @@ import { Session } from '@sentry/hub'; -import { ClientOptions, Event, Integration, Severity, SeverityLevel, Transport } from '@sentry/types'; +import { ClientOptions, Event, Integration, Severity, SeverityLevel } from '@sentry/types'; import { resolvedSyncPromise } from '@sentry/utils'; import { BaseClient } from '../../src/baseclient'; import { initAndBind } from '../../src/sdk'; -import { NewTransport } from '../../src/transports/base'; -import { NoopTransport } from '../../src/transports/noop'; +import { createTransport } from '../../src/transports/base'; export function getDefaultTestClientOptions(options: Partial = {}): TestClientOptions { return { integrations: [], - transport: NoopTransport, + transport: () => createTransport({}, _ => resolvedSyncPromise({ statusCode: 200 })), stackParser: () => [], ...options, }; @@ -30,8 +29,8 @@ export class TestClient extends BaseClient { public event?: Event; public session?: Session; - public constructor(options: TestClientOptions, transport: Transport, newTransport?: NewTransport) { - super(options, transport, newTransport); + public constructor(options: TestClientOptions) { + super(options); TestClient.instance = this; } @@ -74,23 +73,6 @@ export class TestClient extends BaseClient { } } -export function init(options: TestClientOptions, transport: Transport, newTransport?: NewTransport): void { - initAndBind(TestClient, options, transport, newTransport); -} - -export function setupTestTransport(options: TestClientOptions): { transport: Transport; newTransport?: NewTransport } { - const noop = { transport: new NoopTransport() }; - - if (!options.dsn) { - // We return the noop transport here in case there is no Dsn. - return noop; - } - - const transportOptions = options.transportOptions ? options.transportOptions : { dsn: options.dsn }; - - if (options.transport) { - return { transport: new options.transport(transportOptions) }; - } - - return noop; +export function init(options: TestClientOptions): void { + initAndBind(TestClient, options); } diff --git a/packages/core/test/mocks/transport.ts b/packages/core/test/mocks/transport.ts index 1037fada987d..c7fdb02e558a 100644 --- a/packages/core/test/mocks/transport.ts +++ b/packages/core/test/mocks/transport.ts @@ -1,31 +1,22 @@ -import { Event, Response, Transport } from '@sentry/types'; -import { makePromiseBuffer, PromiseBuffer, SyncPromise } from '@sentry/utils'; +import { SyncPromise } from '@sentry/utils'; + +import { createTransport } from '../../src/transports/base'; async function sleep(delay: number): Promise { return new SyncPromise(resolve => setTimeout(resolve, delay)); } -export class FakeTransport implements Transport { - public sendCalled: number = 0; - public sentCount: number = 0; - public delay: number = 2000; - - /** A simple buffer holding all requests. */ - protected readonly _buffer: PromiseBuffer = makePromiseBuffer(9999); - - public sendEvent(_event: Event): PromiseLike { - this.sendCalled += 1; - return this._buffer.add( - () => - new SyncPromise(async res => { - await sleep(this.delay); - this.sentCount += 1; - res({ status: 'success' }); - }), - ); - } - - public close(timeout?: number): PromiseLike { - return this._buffer.drain(timeout); - } +export function makeFakeTransport(delay: number = 2000) { + let sendCalled = 0; + let sentCount = 0; + const makeTransport = () => + createTransport({}, () => { + sendCalled += 1; + return new SyncPromise(async res => { + await sleep(delay); + sentCount += 1; + res({ statusCode: 200 }); + }); + }); + return { makeTransport, getSendCalled: () => sendCalled, getSentCount: () => sentCount, delay }; } diff --git a/packages/ember/tests/dummy/app/app.js b/packages/ember/tests/dummy/app/app.js index 25a843863e69..3479bd41f315 100644 --- a/packages/ember/tests/dummy/app/app.js +++ b/packages/ember/tests/dummy/app/app.js @@ -4,25 +4,7 @@ import loadInitializers from 'ember-load-initializers'; import config from './config/environment'; import * as Sentry from '@sentry/ember'; -import { Transports } from '@sentry/browser'; -import Ember from 'ember'; - -class TestFetchTransport extends Transports.FetchTransport { - sendEvent(event) { - if (Ember.testing) { - if (!window._sentryTestEvents) { - window._sentryTestEvents = []; - } - window._sentryTestEvents.push(event); - return Promise.resolve(); - } - return super.sendEvent(event); - } -} - -Sentry.init({ - transport: TestFetchTransport, -}); +Sentry.init(); export default class App extends Application { modulePrefix = config.modulePrefix; diff --git a/packages/ember/tests/test-helper.js b/packages/ember/tests/test-helper.js index eae0acb0b24e..da7c45929c8f 100644 --- a/packages/ember/tests/test-helper.js +++ b/packages/ember/tests/test-helper.js @@ -10,30 +10,17 @@ import Application from '../app'; import config from '../config/environment'; import { setApplication } from '@ember/test-helpers'; import { start } from 'ember-qunit'; -import { Transports } from '@sentry/browser'; import Ember from 'ember'; -import { getConfig } from '@embroider/macros'; - -function getSentryConfig() { - return getConfig('@sentry/ember').sentryConfig; -} - -export class TestFetchTransport extends Transports.FetchTransport { - sendEvent(event) { - if (Ember.testing) { - if (!window._sentryTestEvents) { - window._sentryTestEvents = []; - } - window._sentryTestEvents.push(event); - return Promise.resolve(); +Sentry.addGlobalEventProcessor((event) => { + if (Ember.testing) { + if (!window._sentryTestEvents) { + window._sentryTestEvents = []; } - return super.sendEvent(event); + window._sentryTestEvents.push(event); } -} - -const sentryConfig = getSentryConfig(); -sentryConfig.sentry['transport'] = TestFetchTransport; + return event; +}); setApplication(Application.create(config.APP)); diff --git a/packages/hub/src/sessionflusher.ts b/packages/hub/src/sessionflusher.ts index fcc4386c70d8..7b2bda98c4da 100644 --- a/packages/hub/src/sessionflusher.ts +++ b/packages/hub/src/sessionflusher.ts @@ -1,13 +1,6 @@ -import { - AggregationCounts, - RequestSessionStatus, - SessionAggregates, - SessionFlusherLike, - Transport, -} from '@sentry/types'; -import { dropUndefinedKeys, logger } from '@sentry/utils'; +import { AggregationCounts, Client, RequestSessionStatus, SessionAggregates, SessionFlusherLike } from '@sentry/types'; +import { dropUndefinedKeys } from '@sentry/utils'; -import { IS_DEBUG_BUILD } from './flags'; import { getCurrentHub } from './hub'; type ReleaseHealthAttributes = { @@ -24,34 +17,23 @@ export class SessionFlusher implements SessionFlusherLike { private _sessionAttrs: ReleaseHealthAttributes; private _intervalId: ReturnType; private _isEnabled: boolean = true; - private _transport: Transport; + private _client: Client; - public constructor(transport: Transport, attrs: ReleaseHealthAttributes) { - this._transport = transport; + public constructor(client: Client, attrs: ReleaseHealthAttributes) { + this._client = client; // Call to setInterval, so that flush is called every 60 seconds this._intervalId = setInterval(() => this.flush(), this.flushTimeout * 1000); this._sessionAttrs = attrs; } - /** Sends session aggregates to Transport */ - public sendSessionAggregates(sessionAggregates: SessionAggregates): void { - if (!this._transport.sendSession) { - IS_DEBUG_BUILD && logger.warn("Dropping session because custom transport doesn't implement sendSession"); - return; - } - void this._transport.sendSession(sessionAggregates).then(null, reason => { - IS_DEBUG_BUILD && logger.error('Error while sending session:', reason); - }); - } - - /** Checks if `pendingAggregates` has entries, and if it does flushes them by calling `sendSessions` */ + /** Checks if `pendingAggregates` has entries, and if it does flushes them by calling `sendSession` */ public flush(): void { const sessionAggregates = this.getSessionAggregates(); if (sessionAggregates.aggregates.length === 0) { return; } this._pendingAggregates = {}; - this.sendSessionAggregates(sessionAggregates); + this._client.sendSession(sessionAggregates); } /** Massages the entries in `pendingAggregates` and returns aggregated sessions */ diff --git a/packages/hub/test/sessionflusher.test.ts b/packages/hub/test/sessionflusher.test.ts index 3c7dc9782615..58ce3ee374ce 100644 --- a/packages/hub/test/sessionflusher.test.ts +++ b/packages/hub/test/sessionflusher.test.ts @@ -1,21 +1,17 @@ +import { Client } from '@sentry/types'; + import { SessionFlusher } from '../src/sessionflusher'; describe('Session Flusher', () => { let sendSession: jest.Mock; - let transport: { - sendEvent: jest.Mock; - sendSession: jest.Mock; - close: jest.Mock; - }; + let mockClient: Client; beforeEach(() => { jest.useFakeTimers(); sendSession = jest.fn(() => Promise.resolve({ status: 'success' })); - transport = { - sendEvent: jest.fn(), + mockClient = { sendSession, - close: jest.fn(), - }; + } as unknown as Client; }); afterEach(() => { @@ -23,7 +19,7 @@ describe('Session Flusher', () => { }); test('test incrementSessionStatusCount updates the internal SessionFlusher state', () => { - const flusher = new SessionFlusher(transport, { release: '1.0.0', environment: 'dev' }); + const flusher = new SessionFlusher(mockClient, { release: '1.0.0', environment: 'dev' }); const date = new Date('2021-04-08T12:18:23.043Z'); let count = (flusher as any)._incrementSessionStatusCount('ok', date); @@ -46,7 +42,7 @@ describe('Session Flusher', () => { }); test('test undefined attributes are excluded, on incrementSessionStatusCount call', () => { - const flusher = new SessionFlusher(transport, { release: '1.0.0' }); + const flusher = new SessionFlusher(mockClient, { release: '1.0.0' }); const date = new Date('2021-04-08T12:18:23.043Z'); (flusher as any)._incrementSessionStatusCount('ok', date); @@ -59,7 +55,7 @@ describe('Session Flusher', () => { }); test('flush is called every 60 seconds after initialisation of an instance of SessionFlusher', () => { - const flusher = new SessionFlusher(transport, { release: '1.0.0', environment: 'dev' }); + const flusher = new SessionFlusher(mockClient, { release: '1.0.0', environment: 'dev' }); const flusherFlushFunc = jest.spyOn(flusher, 'flush'); jest.advanceTimersByTime(59000); expect(flusherFlushFunc).toHaveBeenCalledTimes(0); @@ -72,7 +68,7 @@ describe('Session Flusher', () => { }); test('sendSessions is called on flush if sessions were captured', () => { - const flusher = new SessionFlusher(transport, { release: '1.0.0', environment: 'dev' }); + const flusher = new SessionFlusher(mockClient, { release: '1.0.0', environment: 'dev' }); const flusherFlushFunc = jest.spyOn(flusher, 'flush'); const date = new Date('2021-04-08T12:18:23.043Z'); (flusher as any)._incrementSessionStatusCount('ok', date); @@ -92,7 +88,7 @@ describe('Session Flusher', () => { }); test('sendSessions is not called on flush if no sessions were captured', () => { - const flusher = new SessionFlusher(transport, { release: '1.0.0', environment: 'dev' }); + const flusher = new SessionFlusher(mockClient, { release: '1.0.0', environment: 'dev' }); const flusherFlushFunc = jest.spyOn(flusher, 'flush'); expect(sendSession).toHaveBeenCalledTimes(0); @@ -102,13 +98,13 @@ describe('Session Flusher', () => { }); test('calling close on SessionFlusher should disable SessionFlusher', () => { - const flusher = new SessionFlusher(transport, { release: '1.0.x' }); + const flusher = new SessionFlusher(mockClient, { release: '1.0.x' }); flusher.close(); expect((flusher as any)._isEnabled).toEqual(false); }); test('calling close on SessionFlusher will force call flush', () => { - const flusher = new SessionFlusher(transport, { release: '1.0.x' }); + const flusher = new SessionFlusher(mockClient, { release: '1.0.x' }); const flusherFlushFunc = jest.spyOn(flusher, 'flush'); const date = new Date('2021-04-08T12:18:23.043Z'); (flusher as any)._incrementSessionStatusCount('ok', date); diff --git a/packages/integration-tests/suites/new-transports/fetch-captureException/subject.js b/packages/integration-tests/suites/new-transports/fetch-captureException/subject.js deleted file mode 100644 index 9cc217bdb087..000000000000 --- a/packages/integration-tests/suites/new-transports/fetch-captureException/subject.js +++ /dev/null @@ -1 +0,0 @@ -Sentry.captureException(new Error('this is an error')); diff --git a/packages/integration-tests/suites/new-transports/fetch-captureException/test.ts b/packages/integration-tests/suites/new-transports/fetch-captureException/test.ts deleted file mode 100644 index cb92e50e2dc5..000000000000 --- a/packages/integration-tests/suites/new-transports/fetch-captureException/test.ts +++ /dev/null @@ -1,21 +0,0 @@ -import { expect } from '@playwright/test'; -import { Event } from '@sentry/types'; - -import { sentryTest } from '../../../utils/fixtures'; -import { getFirstSentryEnvelopeRequest } from '../../../utils/helpers'; - -sentryTest('should capture an error with the new fetch transport', async ({ getLocalTestPath, page }) => { - const url = await getLocalTestPath({ testDir: __dirname }); - - const eventData = await getFirstSentryEnvelopeRequest(page, url); - - expect(eventData.exception?.values).toHaveLength(1); - expect(eventData.exception?.values?.[0]).toMatchObject({ - type: 'Error', - value: 'this is an error', - mechanism: { - type: 'generic', - handled: true, - }, - }); -}); diff --git a/packages/integration-tests/suites/new-transports/fetch-startTransaction/subject.js b/packages/integration-tests/suites/new-transports/fetch-startTransaction/subject.js deleted file mode 100644 index 78c7c33c654c..000000000000 --- a/packages/integration-tests/suites/new-transports/fetch-startTransaction/subject.js +++ /dev/null @@ -1,2 +0,0 @@ -const transaction = Sentry.startTransaction({ name: 'test_transaction_1' }); -transaction.finish(); diff --git a/packages/integration-tests/suites/new-transports/fetch-startTransaction/test.ts b/packages/integration-tests/suites/new-transports/fetch-startTransaction/test.ts deleted file mode 100644 index 8daef2e06b54..000000000000 --- a/packages/integration-tests/suites/new-transports/fetch-startTransaction/test.ts +++ /dev/null @@ -1,13 +0,0 @@ -import { expect } from '@playwright/test'; -import { Event } from '@sentry/types'; - -import { sentryTest } from '../../../utils/fixtures'; -import { getFirstSentryEnvelopeRequest } from '../../../utils/helpers'; - -sentryTest('should report a transaction with the new fetch transport', async ({ getLocalTestPath, page }) => { - const url = await getLocalTestPath({ testDir: __dirname }); - const transaction = await getFirstSentryEnvelopeRequest(page, url); - - expect(transaction.transaction).toBe('test_transaction_1'); - expect(transaction.spans).toBeDefined(); -}); diff --git a/packages/integration-tests/suites/new-transports/init.js b/packages/integration-tests/suites/new-transports/init.js deleted file mode 100644 index 6cc8110c0475..000000000000 --- a/packages/integration-tests/suites/new-transports/init.js +++ /dev/null @@ -1,13 +0,0 @@ -import * as Sentry from '@sentry/browser'; -// eslint-disable-next-line no-unused-vars -import * as _ from '@sentry/tracing'; - -window.Sentry = Sentry; - -Sentry.init({ - dsn: 'https://public@dsn.ingest.sentry.io/1337', - _experiments: { - newTransport: true, - }, - tracesSampleRate: 1.0, -}); diff --git a/packages/integration-tests/suites/new-transports/xhr-captureException/subject.js b/packages/integration-tests/suites/new-transports/xhr-captureException/subject.js deleted file mode 100644 index e42102004dad..000000000000 --- a/packages/integration-tests/suites/new-transports/xhr-captureException/subject.js +++ /dev/null @@ -1,4 +0,0 @@ -// deactivate fetch s.t. the SDK falls back to XHR transport -window.fetch = undefined; - -Sentry.captureException(new Error('this is an error')); diff --git a/packages/integration-tests/suites/new-transports/xhr-captureException/test.ts b/packages/integration-tests/suites/new-transports/xhr-captureException/test.ts deleted file mode 100644 index cb92e50e2dc5..000000000000 --- a/packages/integration-tests/suites/new-transports/xhr-captureException/test.ts +++ /dev/null @@ -1,21 +0,0 @@ -import { expect } from '@playwright/test'; -import { Event } from '@sentry/types'; - -import { sentryTest } from '../../../utils/fixtures'; -import { getFirstSentryEnvelopeRequest } from '../../../utils/helpers'; - -sentryTest('should capture an error with the new fetch transport', async ({ getLocalTestPath, page }) => { - const url = await getLocalTestPath({ testDir: __dirname }); - - const eventData = await getFirstSentryEnvelopeRequest(page, url); - - expect(eventData.exception?.values).toHaveLength(1); - expect(eventData.exception?.values?.[0]).toMatchObject({ - type: 'Error', - value: 'this is an error', - mechanism: { - type: 'generic', - handled: true, - }, - }); -}); diff --git a/packages/integration-tests/suites/new-transports/xhr-startTransaction/subject.js b/packages/integration-tests/suites/new-transports/xhr-startTransaction/subject.js deleted file mode 100644 index 444e095ed3a1..000000000000 --- a/packages/integration-tests/suites/new-transports/xhr-startTransaction/subject.js +++ /dev/null @@ -1,5 +0,0 @@ -// deactivate fetch s.t. the SDK falls back to XHR transport -window.fetch = undefined; - -const transaction = Sentry.startTransaction({ name: 'test_transaction_1' }); -transaction.finish(); diff --git a/packages/integration-tests/suites/new-transports/xhr-startTransaction/test.ts b/packages/integration-tests/suites/new-transports/xhr-startTransaction/test.ts deleted file mode 100644 index 59ddfb00c6a1..000000000000 --- a/packages/integration-tests/suites/new-transports/xhr-startTransaction/test.ts +++ /dev/null @@ -1,13 +0,0 @@ -import { expect } from '@playwright/test'; -import { Event } from '@sentry/types'; - -import { sentryTest } from '../../../utils/fixtures'; -import { getFirstSentryEnvelopeRequest } from '../../../utils/helpers'; - -sentryTest('should report a transaction with the new XHR transport', async ({ getLocalTestPath, page }) => { - const url = await getLocalTestPath({ testDir: __dirname }); - const transaction = await getFirstSentryEnvelopeRequest(page, url); - - expect(transaction.transaction).toBe('test_transaction_1'); - expect(transaction.spans).toBeDefined(); -}); diff --git a/packages/integration-tests/suites/public-api/addBreadcrumb/empty_obj/test.ts b/packages/integration-tests/suites/public-api/addBreadcrumb/empty_obj/test.ts index e6e2b9a8f4dd..3fea4283b71e 100644 --- a/packages/integration-tests/suites/public-api/addBreadcrumb/empty_obj/test.ts +++ b/packages/integration-tests/suites/public-api/addBreadcrumb/empty_obj/test.ts @@ -1,14 +1,15 @@ import { expect } from '@playwright/test'; +import { Event } from '@sentry/types'; import { sentryTest } from '../../../../utils/fixtures'; -import { getSentryRequest } from '../../../../utils/helpers'; +import { getFirstSentryEnvelopeRequest } from '../../../../utils/helpers'; sentryTest( 'should add an empty breadcrumb initialized with a timestamp, when an empty object is given', async ({ getLocalTestPath, page }) => { const url = await getLocalTestPath({ testDir: __dirname }); - const eventData = await getSentryRequest(page, url); + const eventData = await getFirstSentryEnvelopeRequest(page, url); expect(eventData.breadcrumbs).toHaveLength(1); expect(eventData.breadcrumbs?.[0]).toMatchObject({ diff --git a/packages/integration-tests/suites/public-api/addBreadcrumb/multiple_breadcrumbs/test.ts b/packages/integration-tests/suites/public-api/addBreadcrumb/multiple_breadcrumbs/test.ts index c7bca64aafd5..d864be4f9073 100644 --- a/packages/integration-tests/suites/public-api/addBreadcrumb/multiple_breadcrumbs/test.ts +++ b/packages/integration-tests/suites/public-api/addBreadcrumb/multiple_breadcrumbs/test.ts @@ -1,12 +1,13 @@ import { expect } from '@playwright/test'; +import { Event } from '@sentry/types'; import { sentryTest } from '../../../../utils/fixtures'; -import { getSentryRequest } from '../../../../utils/helpers'; +import { getFirstSentryEnvelopeRequest } from '../../../../utils/helpers'; sentryTest('should add multiple breadcrumbs', async ({ getLocalTestPath, page }) => { const url = await getLocalTestPath({ testDir: __dirname }); - const eventData = await getSentryRequest(page, url); + const eventData = await getFirstSentryEnvelopeRequest(page, url); expect(eventData.breadcrumbs).toHaveLength(2); expect(eventData.breadcrumbs?.[0]).toMatchObject({ diff --git a/packages/integration-tests/suites/public-api/addBreadcrumb/simple_breadcrumb/test.ts b/packages/integration-tests/suites/public-api/addBreadcrumb/simple_breadcrumb/test.ts index 98e36a254076..224d4dba0932 100644 --- a/packages/integration-tests/suites/public-api/addBreadcrumb/simple_breadcrumb/test.ts +++ b/packages/integration-tests/suites/public-api/addBreadcrumb/simple_breadcrumb/test.ts @@ -1,12 +1,13 @@ import { expect } from '@playwright/test'; +import { Event } from '@sentry/types'; import { sentryTest } from '../../../../utils/fixtures'; -import { getSentryRequest } from '../../../../utils/helpers'; +import { getFirstSentryEnvelopeRequest } from '../../../../utils/helpers'; sentryTest('should add a simple breadcrumb', async ({ getLocalTestPath, page }) => { const url = await getLocalTestPath({ testDir: __dirname }); - const eventData = await getSentryRequest(page, url); + const eventData = await getFirstSentryEnvelopeRequest(page, url); expect(eventData.breadcrumbs).toHaveLength(1); expect(eventData.breadcrumbs?.[0]).toMatchObject({ diff --git a/packages/integration-tests/suites/public-api/addBreadcrumb/undefined_arg/test.ts b/packages/integration-tests/suites/public-api/addBreadcrumb/undefined_arg/test.ts index b41f527c58ed..5e5ec669a7dc 100644 --- a/packages/integration-tests/suites/public-api/addBreadcrumb/undefined_arg/test.ts +++ b/packages/integration-tests/suites/public-api/addBreadcrumb/undefined_arg/test.ts @@ -1,14 +1,15 @@ import { expect } from '@playwright/test'; +import { Event } from '@sentry/types'; import { sentryTest } from '../../../../utils/fixtures'; -import { getSentryRequest } from '../../../../utils/helpers'; +import { getFirstSentryEnvelopeRequest } from '../../../../utils/helpers'; sentryTest( 'should add an empty breadcrumb initialized with a timestamp, when no argument is given', async ({ getLocalTestPath, page }) => { const url = await getLocalTestPath({ testDir: __dirname }); - const eventData = await getSentryRequest(page, url); + const eventData = await getFirstSentryEnvelopeRequest(page, url); expect(eventData.breadcrumbs).toHaveLength(1); expect(eventData.breadcrumbs?.[0]).toMatchObject({ diff --git a/packages/integration-tests/suites/public-api/captureException/empty_obj/test.ts b/packages/integration-tests/suites/public-api/captureException/empty_obj/test.ts index 2606b2984d08..a41fdcc6a6e1 100644 --- a/packages/integration-tests/suites/public-api/captureException/empty_obj/test.ts +++ b/packages/integration-tests/suites/public-api/captureException/empty_obj/test.ts @@ -1,12 +1,13 @@ import { expect } from '@playwright/test'; +import { Event } from '@sentry/types'; import { sentryTest } from '../../../../utils/fixtures'; -import { getSentryRequest } from '../../../../utils/helpers'; +import { getFirstSentryEnvelopeRequest } from '../../../../utils/helpers'; sentryTest('should capture an empty object', async ({ getLocalTestPath, page }) => { const url = await getLocalTestPath({ testDir: __dirname }); - const eventData = await getSentryRequest(page, url); + const eventData = await getFirstSentryEnvelopeRequest(page, url); expect(eventData.exception?.values).toHaveLength(1); expect(eventData.exception?.values?.[0]).toMatchObject({ diff --git a/packages/integration-tests/suites/public-api/captureException/simple_error/test.ts b/packages/integration-tests/suites/public-api/captureException/simple_error/test.ts index f52e951c20c6..49627e826726 100644 --- a/packages/integration-tests/suites/public-api/captureException/simple_error/test.ts +++ b/packages/integration-tests/suites/public-api/captureException/simple_error/test.ts @@ -1,12 +1,13 @@ import { expect } from '@playwright/test'; +import { Event } from '@sentry/types'; import { sentryTest } from '../../../../utils/fixtures'; -import { getSentryRequest } from '../../../../utils/helpers'; +import { getFirstSentryEnvelopeRequest } from '../../../../utils/helpers'; sentryTest('should capture a simple error with message', async ({ getLocalTestPath, page }) => { const url = await getLocalTestPath({ testDir: __dirname }); - const eventData = await getSentryRequest(page, url); + const eventData = await getFirstSentryEnvelopeRequest(page, url); expect(eventData.exception?.values).toHaveLength(1); expect(eventData.exception?.values?.[0]).toMatchObject({ diff --git a/packages/integration-tests/suites/public-api/captureException/undefined_arg/test.ts b/packages/integration-tests/suites/public-api/captureException/undefined_arg/test.ts index 021af6f922f3..52e2ef5c21f8 100644 --- a/packages/integration-tests/suites/public-api/captureException/undefined_arg/test.ts +++ b/packages/integration-tests/suites/public-api/captureException/undefined_arg/test.ts @@ -1,12 +1,13 @@ import { expect } from '@playwright/test'; +import { Event } from '@sentry/types'; import { sentryTest } from '../../../../utils/fixtures'; -import { getSentryRequest } from '../../../../utils/helpers'; +import { getFirstSentryEnvelopeRequest } from '../../../../utils/helpers'; sentryTest('should capture an undefined error when no arguments are provided', async ({ getLocalTestPath, page }) => { const url = await getLocalTestPath({ testDir: __dirname }); - const eventData = await getSentryRequest(page, url); + const eventData = await getFirstSentryEnvelopeRequest(page, url); expect(eventData.exception?.values).toHaveLength(1); expect(eventData.exception?.values?.[0]).toMatchObject({ diff --git a/packages/integration-tests/suites/public-api/captureMessage/simple_message/test.ts b/packages/integration-tests/suites/public-api/captureMessage/simple_message/test.ts index 7b4b68f228d6..cfd5580653ac 100644 --- a/packages/integration-tests/suites/public-api/captureMessage/simple_message/test.ts +++ b/packages/integration-tests/suites/public-api/captureMessage/simple_message/test.ts @@ -1,12 +1,13 @@ import { expect } from '@playwright/test'; +import { Event } from '@sentry/types'; import { sentryTest } from '../../../../utils/fixtures'; -import { getSentryRequest } from '../../../../utils/helpers'; +import { getFirstSentryEnvelopeRequest } from '../../../../utils/helpers'; sentryTest('should capture a simple message string', async ({ getLocalTestPath, page }) => { const url = await getLocalTestPath({ testDir: __dirname }); - const eventData = await getSentryRequest(page, url); + const eventData = await getFirstSentryEnvelopeRequest(page, url); expect(eventData.message).toBe('foo'); expect(eventData.level).toBe('info'); diff --git a/packages/integration-tests/suites/public-api/captureMessage/with_level/test.ts b/packages/integration-tests/suites/public-api/captureMessage/with_level/test.ts index ba8bb18d729a..45eaeca66161 100644 --- a/packages/integration-tests/suites/public-api/captureMessage/with_level/test.ts +++ b/packages/integration-tests/suites/public-api/captureMessage/with_level/test.ts @@ -1,12 +1,13 @@ import { expect } from '@playwright/test'; +import { Event } from '@sentry/types'; import { sentryTest } from '../../../../utils/fixtures'; -import { getMultipleSentryRequests } from '../../../../utils/helpers'; +import { getMultipleSentryEnvelopeRequests } from '../../../../utils/helpers'; sentryTest('should capture with different severity levels', async ({ getLocalTestPath, page }) => { const url = await getLocalTestPath({ testDir: __dirname }); - const events = await getMultipleSentryRequests(page, 7, url); + const events = await getMultipleSentryEnvelopeRequests(page, 7, { url }); expect(events[0].message).toBe('debug_message'); expect(events[0].level).toBe('debug'); diff --git a/packages/integration-tests/suites/public-api/configureScope/clear_scope/test.ts b/packages/integration-tests/suites/public-api/configureScope/clear_scope/test.ts index 4bbb0faf4c56..8626cf1adf66 100644 --- a/packages/integration-tests/suites/public-api/configureScope/clear_scope/test.ts +++ b/packages/integration-tests/suites/public-api/configureScope/clear_scope/test.ts @@ -1,12 +1,13 @@ import { expect } from '@playwright/test'; +import { Event } from '@sentry/types'; import { sentryTest } from '../../../../utils/fixtures'; -import { getSentryRequest } from '../../../../utils/helpers'; +import { getFirstSentryEnvelopeRequest } from '../../../../utils/helpers'; sentryTest('should clear previously set properties of a scope', async ({ getLocalTestPath, page }) => { const url = await getLocalTestPath({ testDir: __dirname }); - const eventData = await getSentryRequest(page, url); + const eventData = await getFirstSentryEnvelopeRequest(page, url); // TODO: This is to compensate for a temporary debugging hack which adds data the tests aren't anticipating to the // event. The code can be restored to its original form (the commented-out line below) once that hack is diff --git a/packages/integration-tests/suites/public-api/configureScope/set_properties/test.ts b/packages/integration-tests/suites/public-api/configureScope/set_properties/test.ts index d4001c317d05..992dd7c31043 100644 --- a/packages/integration-tests/suites/public-api/configureScope/set_properties/test.ts +++ b/packages/integration-tests/suites/public-api/configureScope/set_properties/test.ts @@ -1,12 +1,13 @@ import { expect } from '@playwright/test'; +import { Event } from '@sentry/types'; import { sentryTest } from '../../../../utils/fixtures'; -import { getSentryRequest } from '../../../../utils/helpers'; +import { getFirstSentryEnvelopeRequest } from '../../../../utils/helpers'; sentryTest('should set different properties of a scope', async ({ getLocalTestPath, page }) => { const url = await getLocalTestPath({ testDir: __dirname }); - const eventData = await getSentryRequest(page, url); + const eventData = await getFirstSentryEnvelopeRequest(page, url); expect(eventData.message).toBe('configured_scope'); expect(eventData.user).toMatchObject({ id: 'baz' }); diff --git a/packages/integration-tests/suites/public-api/setContext/multiple_contexts/test.ts b/packages/integration-tests/suites/public-api/setContext/multiple_contexts/test.ts index 96eecdd38662..6d00519cbad4 100644 --- a/packages/integration-tests/suites/public-api/setContext/multiple_contexts/test.ts +++ b/packages/integration-tests/suites/public-api/setContext/multiple_contexts/test.ts @@ -1,12 +1,13 @@ import { expect } from '@playwright/test'; +import { Event } from '@sentry/types'; import { sentryTest } from '../../../../utils/fixtures'; -import { getSentryRequest } from '../../../../utils/helpers'; +import { getFirstSentryEnvelopeRequest } from '../../../../utils/helpers'; sentryTest('should record multiple contexts', async ({ getLocalTestPath, page }) => { const url = await getLocalTestPath({ testDir: __dirname }); - const eventData = await getSentryRequest(page, url); + const eventData = await getFirstSentryEnvelopeRequest(page, url); expect(eventData.message).toBe('multiple_contexts'); expect(eventData.contexts).toMatchObject({ diff --git a/packages/integration-tests/suites/public-api/setContext/non_serializable_context/test.ts b/packages/integration-tests/suites/public-api/setContext/non_serializable_context/test.ts index fe67bdaff3e7..54fea2c68908 100644 --- a/packages/integration-tests/suites/public-api/setContext/non_serializable_context/test.ts +++ b/packages/integration-tests/suites/public-api/setContext/non_serializable_context/test.ts @@ -1,12 +1,13 @@ import { expect } from '@playwright/test'; +import { Event } from '@sentry/types'; import { sentryTest } from '../../../../utils/fixtures'; -import { getSentryRequest } from '../../../../utils/helpers'; +import { getFirstSentryEnvelopeRequest } from '../../../../utils/helpers'; sentryTest('should normalize non-serializable context', async ({ getLocalTestPath, page }) => { const url = await getLocalTestPath({ testDir: __dirname }); - const eventData = await getSentryRequest(page, url); + const eventData = await getFirstSentryEnvelopeRequest(page, url); expect(eventData.contexts?.non_serializable).toMatchObject({}); expect(eventData.message).toBe('non_serializable'); diff --git a/packages/integration-tests/suites/public-api/setContext/simple_context/test.ts b/packages/integration-tests/suites/public-api/setContext/simple_context/test.ts index 05f534888796..a39f838f5b18 100644 --- a/packages/integration-tests/suites/public-api/setContext/simple_context/test.ts +++ b/packages/integration-tests/suites/public-api/setContext/simple_context/test.ts @@ -1,12 +1,13 @@ import { expect } from '@playwright/test'; +import { Event } from '@sentry/types'; import { sentryTest } from '../../../../utils/fixtures'; -import { getSentryRequest } from '../../../../utils/helpers'; +import { getFirstSentryEnvelopeRequest } from '../../../../utils/helpers'; sentryTest('should set a simple context', async ({ getLocalTestPath, page }) => { const url = await getLocalTestPath({ testDir: __dirname }); - const eventData = await getSentryRequest(page, url); + const eventData = await getFirstSentryEnvelopeRequest(page, url); expect(eventData.message).toBe('simple_context_object'); expect(eventData.contexts).toMatchObject({ diff --git a/packages/integration-tests/suites/public-api/setExtra/multiple_extras/test.ts b/packages/integration-tests/suites/public-api/setExtra/multiple_extras/test.ts index 13d8aa83d9c4..82a2b4ce21e5 100644 --- a/packages/integration-tests/suites/public-api/setExtra/multiple_extras/test.ts +++ b/packages/integration-tests/suites/public-api/setExtra/multiple_extras/test.ts @@ -1,12 +1,13 @@ import { expect } from '@playwright/test'; +import { Event } from '@sentry/types'; import { sentryTest } from '../../../../utils/fixtures'; -import { getSentryRequest } from '../../../../utils/helpers'; +import { getFirstSentryEnvelopeRequest } from '../../../../utils/helpers'; sentryTest('should record multiple extras of different types', async ({ getLocalTestPath, page }) => { const url = await getLocalTestPath({ testDir: __dirname }); - const eventData = await getSentryRequest(page, url); + const eventData = await getFirstSentryEnvelopeRequest(page, url); expect(eventData.message).toBe('multiple_extras'); expect(eventData.extra).toMatchObject({ extra_1: { foo: 'bar', baz: { qux: 'quux' } }, extra_2: false }); diff --git a/packages/integration-tests/suites/public-api/setExtra/non_serializable_extra/test.ts b/packages/integration-tests/suites/public-api/setExtra/non_serializable_extra/test.ts index eaa9d342e4e8..168bfc88e2c5 100644 --- a/packages/integration-tests/suites/public-api/setExtra/non_serializable_extra/test.ts +++ b/packages/integration-tests/suites/public-api/setExtra/non_serializable_extra/test.ts @@ -1,12 +1,13 @@ import { expect } from '@playwright/test'; +import { Event } from '@sentry/types'; import { sentryTest } from '../../../../utils/fixtures'; -import { getSentryRequest } from '../../../../utils/helpers'; +import { getFirstSentryEnvelopeRequest } from '../../../../utils/helpers'; sentryTest('should normalize non-serializable extra', async ({ getLocalTestPath, page }) => { const url = await getLocalTestPath({ testDir: __dirname }); - const eventData = await getSentryRequest(page, url); + const eventData = await getFirstSentryEnvelopeRequest(page, url); expect(eventData.message).toBe('non_serializable'); expect(eventData.extra).toMatchObject({}); diff --git a/packages/integration-tests/suites/public-api/setExtra/simple_extra/test.ts b/packages/integration-tests/suites/public-api/setExtra/simple_extra/test.ts index 352b01191e6e..95a6184e95a9 100644 --- a/packages/integration-tests/suites/public-api/setExtra/simple_extra/test.ts +++ b/packages/integration-tests/suites/public-api/setExtra/simple_extra/test.ts @@ -1,12 +1,13 @@ import { expect } from '@playwright/test'; +import { Event } from '@sentry/types'; import { sentryTest } from '../../../../utils/fixtures'; -import { getSentryRequest } from '../../../../utils/helpers'; +import { getFirstSentryEnvelopeRequest } from '../../../../utils/helpers'; sentryTest('should record a simple extra object', async ({ getLocalTestPath, page }) => { const url = await getLocalTestPath({ testDir: __dirname }); - const eventData = await getSentryRequest(page, url); + const eventData = await getFirstSentryEnvelopeRequest(page, url); expect(eventData.message).toBe('simple_extra'); expect(eventData.extra).toMatchObject({ simple_extra: { foo: 'bar', baz: { qux: 'quux' } } }); diff --git a/packages/integration-tests/suites/public-api/setExtras/consecutive_calls/test.ts b/packages/integration-tests/suites/public-api/setExtras/consecutive_calls/test.ts index 319afc32255b..641325affa34 100644 --- a/packages/integration-tests/suites/public-api/setExtras/consecutive_calls/test.ts +++ b/packages/integration-tests/suites/public-api/setExtras/consecutive_calls/test.ts @@ -1,12 +1,13 @@ import { expect } from '@playwright/test'; +import { Event } from '@sentry/types'; import { sentryTest } from '../../../../utils/fixtures'; -import { getSentryRequest } from '../../../../utils/helpers'; +import { getFirstSentryEnvelopeRequest } from '../../../../utils/helpers'; sentryTest('should set extras from multiple consecutive calls', async ({ getLocalTestPath, page }) => { const url = await getLocalTestPath({ testDir: __dirname }); - const eventData = await getSentryRequest(page, url); + const eventData = await getFirstSentryEnvelopeRequest(page, url); expect(eventData.message).toBe('consecutive_calls'); expect(eventData.extra).toMatchObject({ extra: [], Infinity: 2, null: null, obj: { foo: ['bar', 'baz', 1] } }); diff --git a/packages/integration-tests/suites/public-api/setExtras/multiple_extras/test.ts b/packages/integration-tests/suites/public-api/setExtras/multiple_extras/test.ts index 07a43458e94b..1e238739d8a1 100644 --- a/packages/integration-tests/suites/public-api/setExtras/multiple_extras/test.ts +++ b/packages/integration-tests/suites/public-api/setExtras/multiple_extras/test.ts @@ -1,12 +1,13 @@ import { expect } from '@playwright/test'; +import { Event } from '@sentry/types'; import { sentryTest } from '../../../../utils/fixtures'; -import { getSentryRequest } from '../../../../utils/helpers'; +import { getFirstSentryEnvelopeRequest } from '../../../../utils/helpers'; sentryTest('should record an extras object', async ({ getLocalTestPath, page }) => { const url = await getLocalTestPath({ testDir: __dirname }); - const eventData = await getSentryRequest(page, url); + const eventData = await getFirstSentryEnvelopeRequest(page, url); expect(eventData.message).toBe('multiple_extras'); expect(eventData.extra).toMatchObject({ diff --git a/packages/integration-tests/suites/public-api/setTag/with_non_primitives/test.ts b/packages/integration-tests/suites/public-api/setTag/with_non_primitives/test.ts index 56843d8f6652..e4a1f9b19bd4 100644 --- a/packages/integration-tests/suites/public-api/setTag/with_non_primitives/test.ts +++ b/packages/integration-tests/suites/public-api/setTag/with_non_primitives/test.ts @@ -1,12 +1,13 @@ import { expect } from '@playwright/test'; +import { Event } from '@sentry/types'; import { sentryTest } from '../../../../utils/fixtures'; -import { getSentryRequest } from '../../../../utils/helpers'; +import { getFirstSentryEnvelopeRequest } from '../../../../utils/helpers'; sentryTest('should not accept non-primitive tags', async ({ getLocalTestPath, page }) => { const url = await getLocalTestPath({ testDir: __dirname }); - const eventData = await getSentryRequest(page, url); + const eventData = await getFirstSentryEnvelopeRequest(page, url); expect(eventData.message).toBe('non_primitives'); expect(eventData.tags).toMatchObject({}); diff --git a/packages/integration-tests/suites/public-api/setTag/with_primitives/test.ts b/packages/integration-tests/suites/public-api/setTag/with_primitives/test.ts index b3c24a8cd2c9..ba2b648ad913 100644 --- a/packages/integration-tests/suites/public-api/setTag/with_primitives/test.ts +++ b/packages/integration-tests/suites/public-api/setTag/with_primitives/test.ts @@ -1,12 +1,13 @@ import { expect } from '@playwright/test'; +import { Event } from '@sentry/types'; import { sentryTest } from '../../../../utils/fixtures'; -import { getSentryRequest } from '../../../../utils/helpers'; +import { getFirstSentryEnvelopeRequest } from '../../../../utils/helpers'; sentryTest('should set primitive tags', async ({ getLocalTestPath, page }) => { const url = await getLocalTestPath({ testDir: __dirname }); - const eventData = await getSentryRequest(page, url); + const eventData = await getFirstSentryEnvelopeRequest(page, url); expect(eventData.message).toBe('primitive_tags'); expect(eventData.tags).toMatchObject({ diff --git a/packages/integration-tests/suites/public-api/setTags/with_non_primitives/test.ts b/packages/integration-tests/suites/public-api/setTags/with_non_primitives/test.ts index 56843d8f6652..e4a1f9b19bd4 100644 --- a/packages/integration-tests/suites/public-api/setTags/with_non_primitives/test.ts +++ b/packages/integration-tests/suites/public-api/setTags/with_non_primitives/test.ts @@ -1,12 +1,13 @@ import { expect } from '@playwright/test'; +import { Event } from '@sentry/types'; import { sentryTest } from '../../../../utils/fixtures'; -import { getSentryRequest } from '../../../../utils/helpers'; +import { getFirstSentryEnvelopeRequest } from '../../../../utils/helpers'; sentryTest('should not accept non-primitive tags', async ({ getLocalTestPath, page }) => { const url = await getLocalTestPath({ testDir: __dirname }); - const eventData = await getSentryRequest(page, url); + const eventData = await getFirstSentryEnvelopeRequest(page, url); expect(eventData.message).toBe('non_primitives'); expect(eventData.tags).toMatchObject({}); diff --git a/packages/integration-tests/suites/public-api/setTags/with_primitives/test.ts b/packages/integration-tests/suites/public-api/setTags/with_primitives/test.ts index b3c24a8cd2c9..ba2b648ad913 100644 --- a/packages/integration-tests/suites/public-api/setTags/with_primitives/test.ts +++ b/packages/integration-tests/suites/public-api/setTags/with_primitives/test.ts @@ -1,12 +1,13 @@ import { expect } from '@playwright/test'; +import { Event } from '@sentry/types'; import { sentryTest } from '../../../../utils/fixtures'; -import { getSentryRequest } from '../../../../utils/helpers'; +import { getFirstSentryEnvelopeRequest } from '../../../../utils/helpers'; sentryTest('should set primitive tags', async ({ getLocalTestPath, page }) => { const url = await getLocalTestPath({ testDir: __dirname }); - const eventData = await getSentryRequest(page, url); + const eventData = await getFirstSentryEnvelopeRequest(page, url); expect(eventData.message).toBe('primitive_tags'); expect(eventData.tags).toMatchObject({ diff --git a/packages/integration-tests/suites/public-api/setUser/unset_user/test.ts b/packages/integration-tests/suites/public-api/setUser/unset_user/test.ts index 045b79597b72..2aed7beb60aa 100644 --- a/packages/integration-tests/suites/public-api/setUser/unset_user/test.ts +++ b/packages/integration-tests/suites/public-api/setUser/unset_user/test.ts @@ -1,12 +1,13 @@ import { expect } from '@playwright/test'; +import { Event } from '@sentry/types'; import { sentryTest } from '../../../../utils/fixtures'; -import { getMultipleSentryRequests } from '../../../../utils/helpers'; +import { getMultipleSentryEnvelopeRequests } from '../../../../utils/helpers'; sentryTest('should unset user', async ({ getLocalTestPath, page }) => { const url = await getLocalTestPath({ testDir: __dirname }); - const eventData = await getMultipleSentryRequests(page, 3, url); + const eventData = await getMultipleSentryEnvelopeRequests(page, 3, { url }); expect(eventData[0].message).toBe('no_user'); expect(eventData[0].user).toBeUndefined(); diff --git a/packages/integration-tests/suites/public-api/setUser/update_user/test.ts b/packages/integration-tests/suites/public-api/setUser/update_user/test.ts index 1520655c8363..fa846f0221c2 100644 --- a/packages/integration-tests/suites/public-api/setUser/update_user/test.ts +++ b/packages/integration-tests/suites/public-api/setUser/update_user/test.ts @@ -1,12 +1,13 @@ import { expect } from '@playwright/test'; +import { Event } from '@sentry/types'; import { sentryTest } from '../../../../utils/fixtures'; -import { getMultipleSentryRequests } from '../../../../utils/helpers'; +import { getMultipleSentryEnvelopeRequests } from '../../../../utils/helpers'; sentryTest('should update user', async ({ getLocalTestPath, page }) => { const url = await getLocalTestPath({ testDir: __dirname }); - const eventData = await getMultipleSentryRequests(page, 2, url); + const eventData = await getMultipleSentryEnvelopeRequests(page, 2, { url }); expect(eventData[0].message).toBe('first_user'); expect(eventData[0].user).toMatchObject({ diff --git a/packages/integration-tests/suites/public-api/withScope/nested_scopes/test.ts b/packages/integration-tests/suites/public-api/withScope/nested_scopes/test.ts index 7175eb10ae52..1cc024e799fc 100644 --- a/packages/integration-tests/suites/public-api/withScope/nested_scopes/test.ts +++ b/packages/integration-tests/suites/public-api/withScope/nested_scopes/test.ts @@ -1,12 +1,13 @@ import { expect } from '@playwright/test'; +import { Event } from '@sentry/types'; import { sentryTest } from '../../../../utils/fixtures'; -import { getMultipleSentryRequests } from '../../../../utils/helpers'; +import { getMultipleSentryEnvelopeRequests } from '../../../../utils/helpers'; sentryTest('should allow nested scoping', async ({ getLocalTestPath, page }) => { const url = await getLocalTestPath({ testDir: __dirname }); - const eventData = await getMultipleSentryRequests(page, 5, url); + const eventData = await getMultipleSentryEnvelopeRequests(page, 5, { url }); expect(eventData[0].message).toBe('root_before'); expect(eventData[0].user).toMatchObject({ id: 'qux' }); diff --git a/packages/integration-tests/suites/tracing/request/fetch/test.ts b/packages/integration-tests/suites/tracing/request/fetch/test.ts index 77db6f8c18aa..88bbeaf2c00d 100644 --- a/packages/integration-tests/suites/tracing/request/fetch/test.ts +++ b/packages/integration-tests/suites/tracing/request/fetch/test.ts @@ -2,24 +2,36 @@ import { expect, Request } from '@playwright/test'; import { Event } from '@sentry/types'; import { sentryTest } from '../../../../utils/fixtures'; -import { getFirstSentryEnvelopeRequest } from '../../../../utils/helpers'; +import { getMultipleSentryEnvelopeRequests } from '../../../../utils/helpers'; sentryTest('should create spans for multiple fetch requests', async ({ getLocalTestPath, page }) => { const url = await getLocalTestPath({ testDir: __dirname }); - const eventData = await getFirstSentryEnvelopeRequest(page, url); - const requestSpans = eventData.spans?.filter(({ op }) => op === 'http.client'); + // Because we fetch from http://example.com, fetch will throw a CORS error in firefox and webkit. + // Chromium does not throw for cors errors. + // This means that we will intercept a dynamic amount of envelopes here. + + // We will wait 500ms for all envelopes to be sent. Generally, in all browsers, the last sent + // envelope contains tracing data. + + // If we are on FF or webkit: + // 1st envelope contains CORS error + // 2nd envelope contains the tracing data we want to check here + const envelopes = await getMultipleSentryEnvelopeRequests(page, 2, { url, timeout: 10000 }); + const tracingEvent = envelopes[envelopes.length - 1]; // last envelope contains tracing data on all browsers + + const requestSpans = tracingEvent.spans?.filter(({ op }) => op === 'http.client'); expect(requestSpans).toHaveLength(3); requestSpans?.forEach((span, index) => expect(span).toMatchObject({ description: `GET http://example.com/${index}`, - parent_span_id: eventData.contexts?.trace.span_id, + parent_span_id: tracingEvent.contexts?.trace.span_id, span_id: expect.any(String), start_timestamp: expect.any(Number), timestamp: expect.any(Number), - trace_id: eventData.contexts?.trace.trace_id, + trace_id: tracingEvent.contexts?.trace.trace_id, }), ); }); diff --git a/packages/integration-tests/utils/helpers.ts b/packages/integration-tests/utils/helpers.ts index 5218dc02c27c..8f6e06b97d5a 100644 --- a/packages/integration-tests/utils/helpers.ts +++ b/packages/integration-tests/utils/helpers.ts @@ -1,10 +1,8 @@ import { Page, Request } from '@playwright/test'; import { Event } from '@sentry/types'; -const storeUrlRegex = /\.sentry\.io\/api\/\d+\/store\//; const envelopeUrlRegex = /\.sentry\.io\/api\/\d+\/envelope\//; -const storeRequestParser = (request: Request | null): Event => JSON.parse((request && request.postData()) || ''); const envelopeRequestParser = (request: Request | null): Event => { // https://develop.sentry.dev/sdk/envelopes/ const envelope = request?.postData() || ''; @@ -24,17 +22,6 @@ async function runScriptInSandbox(page: Page, path: string): Promise { await page.addScriptTag({ path }); } -/** - * Wait and get Sentry's request sending the event at the given URL, or the current page - * - * @param {Page} page - * @param {string} [url] - * @return {*} {Promise} - */ -async function getSentryRequest(page: Page, url?: string): Promise { - return (await getMultipleSentryRequests(page, 1, url))[0]; -} - /** * Get Sentry events at the given URL, or the current page. * @@ -52,27 +39,26 @@ async function getSentryEvents(page: Page, url?: string): Promise> } /** - * Wait and get multiple requests matching urlRgx at the given URL, or the current page - * - * @param {Page} page - * @param {number} count - * @param {RegExp} urlRgx - * @param {(req: Request) => Event} requestParser - * @param {string} [url] - * @return {*} {Promise} + * Waits until a number of requests matching urlRgx at the given URL arrive. + * If the timout option is configured, this function will abort waiting, even if it hasn't reveived the configured + * amount of requests, and returns all the events recieved up to that point in time. */ async function getMultipleRequests( page: Page, count: number, urlRgx: RegExp, requestParser: (req: Request) => Event, - url?: string, + options?: { + url?: string; + timeout?: number; + }, ): Promise { const requests: Promise = new Promise((resolve, reject) => { let reqCount = count; const requestData: Event[] = []; + let timeoutId: NodeJS.Timeout | undefined = undefined; - page.on('request', request => { + function requestHandler(request: Request): void { if (urlRgx.test(request.url())) { try { reqCount -= 1; @@ -98,47 +84,48 @@ async function getMultipleRequests( // requestData.push(requestParser(request)); if (reqCount === 0) { + if (timeoutId) { + clearTimeout(timeoutId); + } + page.off('request', requestHandler); resolve(requestData); } } catch (err) { reject(err); } } - }); + } + + page.on('request', requestHandler); + + if (options?.timeout) { + timeoutId = setTimeout(() => { + resolve(requestData); + }, options.timeout); + } }); - if (url) { - await page.goto(url); + if (options?.url) { + await page.goto(options.url); } return requests; } -/** - * Wait and get multiple event requests at the given URL, or the current page - * - * @param {Page} page - * @param {number} count - * @param {string} [url] - * @return {*} {Promise} - */ -async function getMultipleSentryRequests(page: Page, count: number, url?: string): Promise { - return getMultipleRequests(page, count, storeUrlRegex, storeRequestParser, url); -} - /** * Wait and get multiple envelope requests at the given URL, or the current page - * - * @template T - * @param {Page} page - * @param {number} count - * @param {string} [url] - * @return {*} {Promise} */ -async function getMultipleSentryEnvelopeRequests(page: Page, count: number, url?: string): Promise { +async function getMultipleSentryEnvelopeRequests( + page: Page, + count: number, + options?: { + url?: string; + timeout?: number; + }, +): Promise { // TODO: This is not currently checking the type of envelope, just casting for now. // We can update this to include optional type-guarding when we have types for Envelope. - return getMultipleRequests(page, count, envelopeUrlRegex, envelopeRequestParser, url) as Promise; + return getMultipleRequests(page, count, envelopeUrlRegex, envelopeRequestParser, options) as Promise; } /** @@ -150,7 +137,7 @@ async function getMultipleSentryEnvelopeRequests(page: Page, count: number, u * @return {*} {Promise} */ async function getFirstSentryEnvelopeRequest(page: Page, url?: string): Promise { - return (await getMultipleSentryEnvelopeRequests(page, 1, url))[0]; + return (await getMultipleSentryEnvelopeRequests(page, 1, { url }))[0]; } /** @@ -172,10 +159,8 @@ async function injectScriptAndGetEvents(page: Page, url: string, scriptPath: str export { runScriptInSandbox, - getMultipleSentryRequests, getMultipleSentryEnvelopeRequests, getFirstSentryEnvelopeRequest, - getSentryRequest, getSentryEvents, injectScriptAndGetEvents, }; diff --git a/packages/nextjs/test/index.client.test.ts b/packages/nextjs/test/index.client.test.ts index 44edf997ef50..5d7053c00def 100644 --- a/packages/nextjs/test/index.client.test.ts +++ b/packages/nextjs/test/index.client.test.ts @@ -68,12 +68,12 @@ describe('Client init()', () => { tracesSampleRate: 1.0, }); const hub = getCurrentHub(); - const sendEvent = jest.spyOn(hub.getClient()!.getTransport!(), 'sendEvent'); + const transportSend = jest.spyOn(hub.getClient()!.getTransport()!, 'send'); const transaction = hub.startTransaction({ name: '/404' }); transaction.finish(); - expect(sendEvent).not.toHaveBeenCalled(); + expect(transportSend).not.toHaveBeenCalled(); expect(captureEvent.mock.results[0].value).toBeUndefined(); expect(logError).toHaveBeenCalledWith(new SentryError('An event processor returned null, will not send event.')); }); diff --git a/packages/nextjs/test/index.server.test.ts b/packages/nextjs/test/index.server.test.ts index e4e359a10624..477ad252cd29 100644 --- a/packages/nextjs/test/index.server.test.ts +++ b/packages/nextjs/test/index.server.test.ts @@ -95,7 +95,7 @@ describe('Server init()', () => { tracesSampleRate: 1.0, }); const hub = getCurrentHub(); - const sendEvent = jest.spyOn(hub.getClient()!.getTransport!(), 'sendEvent'); + const transportSend = jest.spyOn(hub.getClient()!.getTransport()!, 'send'); const transaction = hub.startTransaction({ name: '/404' }); transaction.finish(); @@ -103,7 +103,7 @@ describe('Server init()', () => { // We need to flush because the event processor pipeline is async whereas transaction.finish() is sync. await SentryNode.flush(); - expect(sendEvent).not.toHaveBeenCalled(); + expect(transportSend).not.toHaveBeenCalled(); expect(logError).toHaveBeenCalledWith(new SentryError('An event processor returned null, will not send event.')); }); diff --git a/packages/nextjs/test/integration/test/utils/client.js b/packages/nextjs/test/integration/test/utils/client.js index d2c11837db3d..76d88832c863 100644 --- a/packages/nextjs/test/integration/test/utils/client.js +++ b/packages/nextjs/test/integration/test/utils/client.js @@ -20,7 +20,7 @@ const createRequestInterceptor = env => { } if (isEventRequest(request)) { - logIf(process.env.LOG_REQUESTS, 'Intercepted Event', extractEventFromRequest(request), env.argv.depth); + logIf(process.env.LOG_REQUESTS, 'Intercepted Event', extractEnvelopeFromRequest(request), env.argv.depth); env.requests.events.push(request); } else if (isSessionRequest(request)) { logIf(process.env.LOG_REQUESTS, 'Intercepted Session', extractEnvelopeFromRequest(request), env.argv.depth); @@ -38,14 +38,14 @@ const isSentryRequest = request => { return /sentry.io\/api/.test(request.url()); }; -const isEventRequest = request => { - return /sentry.io\/api\/\d+\/store/.test(request.url()); -}; - const isEnvelopeRequest = request => { return /sentry.io\/api\/\d+\/envelope/.test(request.url()); }; +const isEventRequest = request => { + return isEnvelopeRequest(request) && extractEnvelopeFromRequest(request).itemHeader.type === 'event'; +}; + const isSessionRequest = request => { return isEnvelopeRequest(request) && extractEnvelopeFromRequest(request).itemHeader.type === 'session'; }; @@ -54,21 +54,25 @@ const isTransactionRequest = request => { return isEnvelopeRequest(request) && extractEnvelopeFromRequest(request).itemHeader.type === 'transaction'; }; -const expectEvent = (request, expectedEvent) => { +const expectEvent = (request, expectedItem) => { if (!request) throw new Error('Event missing'); - return assertObjectMatches(extractEventFromRequest(request), expectedEvent); + const { itemHeader, item } = extractEnvelopeFromRequest(request); + strictEqual(itemHeader.type, 'event'); + assertObjectMatches(item, expectedItem); }; const expectSession = (request, expectedItem) => { if (!request) throw new Error('Session missing'); const { itemHeader, item } = extractEnvelopeFromRequest(request); - return itemHeader.type === 'session' && assertObjectMatches(item, expectedItem); + strictEqual(itemHeader.type, 'session'); + assertObjectMatches(item, expectedItem); }; const expectTransaction = (request, expectedItem) => { if (!request) throw new Error('Transaction missing'); const { itemHeader, item } = extractEnvelopeFromRequest(request); - return itemHeader.type === 'transaction' && assertObjectMatches(item, expectedItem); + strictEqual(itemHeader.type, 'transaction'); + assertObjectMatches(item, expectedItem); }; const expectRequestCount = (requests, expectedCount, timeout = 100) => { @@ -89,10 +93,6 @@ const expectRequestCount = (requests, expectedCount, timeout = 100) => { }); }; -const extractEventFromRequest = request => { - return JSON.parse(request.postData()); -}; - const extractEnvelopeFromRequest = request => { return parseEnvelope(request.postData()); }; @@ -111,8 +111,6 @@ const assertObjectMatches = (actual, expected) => { strictEqual(actual[key], expectedValue); } } - - return true; }; module.exports = { @@ -122,7 +120,6 @@ module.exports = { expectSession, expectTransaction, extractEnvelopeFromRequest, - extractEventFromRequest, isEnvelopeRequest, isEventRequest, isSentryRequest, diff --git a/packages/nextjs/test/integration/test/utils/server.js b/packages/nextjs/test/integration/test/utils/server.js index 8844c8cc1799..8c38446e3f41 100644 --- a/packages/nextjs/test/integration/test/utils/server.js +++ b/packages/nextjs/test/integration/test/utils/server.js @@ -35,15 +35,17 @@ const getAsync = (url, rewrap = false) => { const interceptEventRequest = (expectedEvent, argv, testName = '') => { return nock('https://dsn.ingest.sentry.io') - .post('/api/1337/store/', body => { + .post('/api/1337/envelope/', body => { + const { envelopeHeader, itemHeader, item } = parseEnvelope(body); logIf( process.env.LOG_REQUESTS, '\nIntercepted Event' + (testName.length ? ` (from test \`${testName}\`)` : ''), - body, + { envelopeHeader, itemHeader, item }, argv.depth, ); - return objectMatches(body, expectedEvent); + return itemHeader.type === 'event' && objectMatches(item, expectedEvent); }) + .query(true) // accept any query params - used for sentry_key param used by the envelope endpoint .reply(200); }; @@ -59,6 +61,7 @@ const interceptSessionRequest = (expectedItem, argv, testName = '') => { ); return itemHeader.type === 'session' && objectMatches(item, expectedItem); }) + .query(true) // accept any query params - used for sentry_key param used by the envelope endpoint .reply(200); }; @@ -74,6 +77,7 @@ const interceptTracingRequest = (expectedItem, argv, testName = '') => { ); return itemHeader.type === 'transaction' && objectMatches(item, expectedItem); }) + .query(true) // accept any query params - used for sentry_key param used by the envelope endpoint .reply(200); }; diff --git a/packages/node-integration-tests/suites/express/handle-error/test.ts b/packages/node-integration-tests/suites/express/handle-error/test.ts index 66cc1ec2d9ae..9ae4586f6510 100644 --- a/packages/node-integration-tests/suites/express/handle-error/test.ts +++ b/packages/node-integration-tests/suites/express/handle-error/test.ts @@ -1,12 +1,12 @@ -import { assertSentryEvent, getEventRequest, runServer } from '../../../utils/index'; +import { assertSentryEvent, getEnvelopeRequest, runServer } from '../../../utils/index'; test('should capture and send Express controller error.', async () => { const url = await runServer(__dirname, `${__dirname}/server.ts`); - const event = await getEventRequest(`${url}/express`); + const event = await getEnvelopeRequest(`${url}/express`); - expect((event as any).exception.values[0].stacktrace.frames.length).toBeGreaterThan(0); + expect((event[2] as any).exception.values[0].stacktrace.frames.length).toBeGreaterThan(0); - assertSentryEvent(event, { + assertSentryEvent(event[2] as any, { exception: { values: [ { diff --git a/packages/node-integration-tests/suites/public-api/addBreadcrumb/empty-obj/test.ts b/packages/node-integration-tests/suites/public-api/addBreadcrumb/empty-obj/test.ts index dd9a8ca4fe87..be9c0b6c15b6 100644 --- a/packages/node-integration-tests/suites/public-api/addBreadcrumb/empty-obj/test.ts +++ b/packages/node-integration-tests/suites/public-api/addBreadcrumb/empty-obj/test.ts @@ -1,10 +1,13 @@ -import { assertSentryEvent, getEventRequest, runServer } from '../../../../utils'; +import { assertSentryEvent, getMultipleEnvelopeRequest, runServer } from '../../../../utils'; test('should add an empty breadcrumb, when an empty object is given', async () => { const url = await runServer(__dirname); - const requestBody = await getEventRequest(url); + const envelopes = await getMultipleEnvelopeRequest(url, 2); + const errorEnvelope = envelopes[1]; - assertSentryEvent(requestBody, { + expect(errorEnvelope).toHaveLength(3); + + assertSentryEvent(errorEnvelope[2], { message: 'test-empty-obj', }); }); diff --git a/packages/node-integration-tests/suites/public-api/addBreadcrumb/multiple_breadcrumbs/test.ts b/packages/node-integration-tests/suites/public-api/addBreadcrumb/multiple_breadcrumbs/test.ts index b643a187bc6f..c8b894a62102 100644 --- a/packages/node-integration-tests/suites/public-api/addBreadcrumb/multiple_breadcrumbs/test.ts +++ b/packages/node-integration-tests/suites/public-api/addBreadcrumb/multiple_breadcrumbs/test.ts @@ -1,10 +1,10 @@ -import { assertSentryEvent, getEventRequest, runServer } from '../../../../utils'; +import { assertSentryEvent, getMultipleEnvelopeRequest, runServer } from '../../../../utils'; test('should add multiple breadcrumbs', async () => { const url = await runServer(__dirname); - const requestBody = await getEventRequest(url); + const envelopes = await getMultipleEnvelopeRequest(url, 2); - assertSentryEvent(requestBody, { + assertSentryEvent(envelopes[1][2], { message: 'test_multi_breadcrumbs', breadcrumbs: [ { diff --git a/packages/node-integration-tests/suites/public-api/addBreadcrumb/simple_breadcrumb/test.ts b/packages/node-integration-tests/suites/public-api/addBreadcrumb/simple_breadcrumb/test.ts index 8bc0ee26fea2..a56f0711e086 100644 --- a/packages/node-integration-tests/suites/public-api/addBreadcrumb/simple_breadcrumb/test.ts +++ b/packages/node-integration-tests/suites/public-api/addBreadcrumb/simple_breadcrumb/test.ts @@ -1,10 +1,10 @@ -import { assertSentryEvent, getEventRequest, runServer } from '../../../../utils'; +import { assertSentryEvent, getMultipleEnvelopeRequest, runServer } from '../../../../utils'; test('should add a simple breadcrumb', async () => { const url = await runServer(__dirname); - const requestBody = await getEventRequest(url); + const envelopes = await getMultipleEnvelopeRequest(url, 2); - assertSentryEvent(requestBody, { + assertSentryEvent(envelopes[1][2], { message: 'test_simple', breadcrumbs: [ { diff --git a/packages/node-integration-tests/suites/public-api/captureException/catched-error/test.ts b/packages/node-integration-tests/suites/public-api/captureException/catched-error/test.ts index f6fee1d8b819..2070199f77d9 100644 --- a/packages/node-integration-tests/suites/public-api/captureException/catched-error/test.ts +++ b/packages/node-integration-tests/suites/public-api/captureException/catched-error/test.ts @@ -1,10 +1,11 @@ -import { assertSentryEvent, getEventRequest, runServer } from '../../../../utils'; +import { assertSentryEvent, getMultipleEnvelopeRequest, runServer } from '../../../../utils'; test('should work inside catch block', async () => { const url = await runServer(__dirname); - const requestBody = await getEventRequest(url); + const envelopes = await getMultipleEnvelopeRequest(url, 2); + const errorEnvelope = envelopes[1]; - assertSentryEvent(requestBody, { + assertSentryEvent(errorEnvelope[2], { exception: { values: [ { diff --git a/packages/node-integration-tests/suites/public-api/captureException/empty-obj/test.ts b/packages/node-integration-tests/suites/public-api/captureException/empty-obj/test.ts index fb6ca293a27f..0df21996f08c 100644 --- a/packages/node-integration-tests/suites/public-api/captureException/empty-obj/test.ts +++ b/packages/node-integration-tests/suites/public-api/captureException/empty-obj/test.ts @@ -1,10 +1,11 @@ -import { assertSentryEvent, getEventRequest, runServer } from '../../../../utils'; +import { assertSentryEvent, getMultipleEnvelopeRequest, runServer } from '../../../../utils'; test('should capture an empty object', async () => { const url = await runServer(__dirname); - const requestBody = await getEventRequest(url); + const envelopes = await getMultipleEnvelopeRequest(url, 2); + const errorEnvelope = envelopes[1]; - assertSentryEvent(requestBody, { + assertSentryEvent(errorEnvelope[2], { exception: { values: [ { diff --git a/packages/node-integration-tests/suites/public-api/captureException/new-transport/scenario.ts b/packages/node-integration-tests/suites/public-api/captureException/new-transport/scenario.ts deleted file mode 100644 index a03dea7cbce5..000000000000 --- a/packages/node-integration-tests/suites/public-api/captureException/new-transport/scenario.ts +++ /dev/null @@ -1,11 +0,0 @@ -import * as Sentry from '@sentry/node'; - -Sentry.init({ - dsn: 'https://public@dsn.ingest.sentry.io/1337', - release: '1.0', - _experiments: { - newTransport: true, // use new transport - }, -}); - -Sentry.captureException(new Error('test_simple_error')); diff --git a/packages/node-integration-tests/suites/public-api/captureException/new-transport/test.ts b/packages/node-integration-tests/suites/public-api/captureException/new-transport/test.ts deleted file mode 100644 index 0424ac121dcd..000000000000 --- a/packages/node-integration-tests/suites/public-api/captureException/new-transport/test.ts +++ /dev/null @@ -1,23 +0,0 @@ -import { getMultipleEnvelopeRequest, runServer } from '../../../../utils'; - -test('should correctly send envelope', async () => { - const url = await runServer(__dirname); - const envelopes = await getMultipleEnvelopeRequest(url, 2); - - const errorEnvelope = envelopes[1]; - - expect(errorEnvelope).toHaveLength(3); - expect(errorEnvelope[2]).toMatchObject({ - exception: { - values: [ - { - type: 'Error', - value: 'test_simple_error', - }, - ], - }, - release: '1.0', - event_id: expect.any(String), - timestamp: expect.any(Number), - }); -}); diff --git a/packages/node-integration-tests/suites/public-api/captureException/simple-error/test.ts b/packages/node-integration-tests/suites/public-api/captureException/simple-error/test.ts index 8553cb1be0d7..66ca0410377a 100644 --- a/packages/node-integration-tests/suites/public-api/captureException/simple-error/test.ts +++ b/packages/node-integration-tests/suites/public-api/captureException/simple-error/test.ts @@ -1,10 +1,11 @@ -import { assertSentryEvent, getEventRequest, runServer } from '../../../../utils'; +import { assertSentryEvent, getMultipleEnvelopeRequest, runServer } from '../../../../utils'; test('should capture a simple error with message', async () => { const url = await runServer(__dirname); - const requestBody = await getEventRequest(url); + const envelopes = await getMultipleEnvelopeRequest(url, 2); + const errorEnvelope = envelopes[1]; - assertSentryEvent(requestBody, { + assertSentryEvent(errorEnvelope[2], { exception: { values: [ { diff --git a/packages/node-integration-tests/suites/public-api/captureMessage/simple_message/test.ts b/packages/node-integration-tests/suites/public-api/captureMessage/simple_message/test.ts index 69fa8c27764b..2ecc0e86720d 100644 --- a/packages/node-integration-tests/suites/public-api/captureMessage/simple_message/test.ts +++ b/packages/node-integration-tests/suites/public-api/captureMessage/simple_message/test.ts @@ -1,10 +1,11 @@ -import { assertSentryEvent, getEventRequest, runServer } from '../../../../utils'; +import { assertSentryEvent, getMultipleEnvelopeRequest, runServer } from '../../../../utils'; test('should capture a simple message string', async () => { const url = await runServer(__dirname); - const requestBody = await getEventRequest(url); + const envelopes = await getMultipleEnvelopeRequest(url, 2); + const errorEnvelope = envelopes[1]; - assertSentryEvent(requestBody, { + assertSentryEvent(errorEnvelope[2], { message: 'Message', level: 'info', }); diff --git a/packages/node-integration-tests/suites/public-api/captureMessage/with_level/test.ts b/packages/node-integration-tests/suites/public-api/captureMessage/with_level/test.ts index e43c13db3711..5db1ef646168 100644 --- a/packages/node-integration-tests/suites/public-api/captureMessage/with_level/test.ts +++ b/packages/node-integration-tests/suites/public-api/captureMessage/with_level/test.ts @@ -1,40 +1,40 @@ -import { assertSentryEvent, getMultipleEventRequests, runServer } from '../../../../utils'; +import { assertSentryEvent, getMultipleEnvelopeRequest, runServer } from '../../../../utils'; test('should capture with different severity levels', async () => { const url = await runServer(__dirname); - const events = await getMultipleEventRequests(url, 7); + const envelopes = await getMultipleEnvelopeRequest(url, 14); - assertSentryEvent(events[0], { + assertSentryEvent(envelopes[1][2], { message: 'debug_message', level: 'debug', }); - assertSentryEvent(events[1], { + assertSentryEvent(envelopes[3][2], { message: 'info_message', level: 'info', }); - assertSentryEvent(events[2], { + assertSentryEvent(envelopes[5][2], { message: 'warning_message', level: 'warning', }); - assertSentryEvent(events[3], { + assertSentryEvent(envelopes[7][2], { message: 'error_message', level: 'error', }); - assertSentryEvent(events[4], { + assertSentryEvent(envelopes[9][2], { message: 'fatal_message', level: 'fatal', }); - assertSentryEvent(events[5], { + assertSentryEvent(envelopes[11][2], { message: 'critical_message', level: 'critical', }); - assertSentryEvent(events[6], { + assertSentryEvent(envelopes[13][2], { message: 'log_message', level: 'log', }); diff --git a/packages/node-integration-tests/suites/public-api/configureScope/clear_scope/test.ts b/packages/node-integration-tests/suites/public-api/configureScope/clear_scope/test.ts index 97437a4e7e90..e1cf213e2e4c 100644 --- a/packages/node-integration-tests/suites/public-api/configureScope/clear_scope/test.ts +++ b/packages/node-integration-tests/suites/public-api/configureScope/clear_scope/test.ts @@ -1,16 +1,16 @@ import { Event } from '@sentry/node'; -import { assertSentryEvent, getEventRequest, runServer } from '../../../../utils'; +import { assertSentryEvent, getEnvelopeRequest, runServer } from '../../../../utils'; test('should clear previously set properties of a scope', async () => { const url = await runServer(__dirname); - const requestBody = await getEventRequest(url); + const envelope = await getEnvelopeRequest(url); - assertSentryEvent(requestBody, { + assertSentryEvent(envelope[2], { message: 'cleared_scope', tags: {}, extra: {}, }); - expect((requestBody as Event).user).not.toBeDefined(); + expect((envelope[2] as Event).user).not.toBeDefined(); }); diff --git a/packages/node-integration-tests/suites/public-api/configureScope/set_properties/test.ts b/packages/node-integration-tests/suites/public-api/configureScope/set_properties/test.ts index 6e482197470c..17d7c9b8df42 100644 --- a/packages/node-integration-tests/suites/public-api/configureScope/set_properties/test.ts +++ b/packages/node-integration-tests/suites/public-api/configureScope/set_properties/test.ts @@ -1,10 +1,11 @@ -import { assertSentryEvent, getEventRequest, runServer } from '../../../../utils'; +import { assertSentryEvent, getMultipleEnvelopeRequest, runServer } from '../../../../utils'; test('should set different properties of a scope', async () => { const url = await runServer(__dirname); - const requestBody = await getEventRequest(url); + const envelopes = await getMultipleEnvelopeRequest(url, 2); + const errorEnvelope = envelopes[1]; - assertSentryEvent(requestBody, { + assertSentryEvent(errorEnvelope[2], { message: 'configured_scope', tags: { foo: 'bar', diff --git a/packages/node-integration-tests/suites/public-api/setContext/multiple-contexts/test.ts b/packages/node-integration-tests/suites/public-api/setContext/multiple-contexts/test.ts index 46d3de6bc2bf..f1c8981fad9a 100644 --- a/packages/node-integration-tests/suites/public-api/setContext/multiple-contexts/test.ts +++ b/packages/node-integration-tests/suites/public-api/setContext/multiple-contexts/test.ts @@ -1,12 +1,13 @@ import { Event } from '@sentry/node'; -import { assertSentryEvent, getEventRequest, runServer } from '../../../../utils'; +import { assertSentryEvent, getMultipleEnvelopeRequest, runServer } from '../../../../utils'; test('should record multiple contexts', async () => { const url = await runServer(__dirname); - const requestBody = await getEventRequest(url); + const envelopes = await getMultipleEnvelopeRequest(url, 2); + const errorEnvelope = envelopes[1]; - assertSentryEvent(requestBody, { + assertSentryEvent(errorEnvelope[2], { message: 'multiple_contexts', contexts: { context_1: { @@ -17,5 +18,5 @@ test('should record multiple contexts', async () => { }, }); - expect((requestBody as Event).contexts?.context_3).not.toBeDefined(); + expect((errorEnvelope[2] as Event).contexts?.context_3).not.toBeDefined(); }); diff --git a/packages/node-integration-tests/suites/public-api/setContext/non-serializable-context/test.ts b/packages/node-integration-tests/suites/public-api/setContext/non-serializable-context/test.ts index 2ea859de480c..26b5fe8c7025 100644 --- a/packages/node-integration-tests/suites/public-api/setContext/non-serializable-context/test.ts +++ b/packages/node-integration-tests/suites/public-api/setContext/non-serializable-context/test.ts @@ -1,15 +1,16 @@ import { Event } from '@sentry/node'; -import { assertSentryEvent, getEventRequest, runServer } from '../../../../utils'; +import { assertSentryEvent, getMultipleEnvelopeRequest, runServer } from '../../../../utils'; test('should normalize non-serializable context', async () => { const url = await runServer(__dirname); - const requestBody = await getEventRequest(url); + const envelopes = await getMultipleEnvelopeRequest(url, 2); + const errorEnvelope = envelopes[1]; - assertSentryEvent(requestBody, { + assertSentryEvent(errorEnvelope[2], { message: 'non_serializable', contexts: {}, }); - expect((requestBody as Event).contexts?.context_3).not.toBeDefined(); + expect((errorEnvelope[2] as Event).contexts?.context_3).not.toBeDefined(); }); diff --git a/packages/node-integration-tests/suites/public-api/setContext/simple-context/test.ts b/packages/node-integration-tests/suites/public-api/setContext/simple-context/test.ts index cdabe34d8680..362afb9e55e4 100644 --- a/packages/node-integration-tests/suites/public-api/setContext/simple-context/test.ts +++ b/packages/node-integration-tests/suites/public-api/setContext/simple-context/test.ts @@ -1,12 +1,13 @@ import { Event } from '@sentry/node'; -import { assertSentryEvent, getEventRequest, runServer } from '../../../../utils'; +import { assertSentryEvent, getMultipleEnvelopeRequest, runServer } from '../../../../utils'; test('should set a simple context', async () => { const url = await runServer(__dirname); - const requestBody = await getEventRequest(url); + const envelopes = await getMultipleEnvelopeRequest(url, 2); + const errorEnvelope = envelopes[1]; - assertSentryEvent(requestBody, { + assertSentryEvent(errorEnvelope[2], { message: 'simple_context_object', contexts: { foo: { @@ -15,5 +16,5 @@ test('should set a simple context', async () => { }, }); - expect((requestBody as Event).contexts?.context_3).not.toBeDefined(); + expect((errorEnvelope[2] as Event).contexts?.context_3).not.toBeDefined(); }); diff --git a/packages/node-integration-tests/suites/public-api/setExtra/multiple-extras/test.ts b/packages/node-integration-tests/suites/public-api/setExtra/multiple-extras/test.ts index 0bc530b13cef..428ebd7f45c4 100644 --- a/packages/node-integration-tests/suites/public-api/setExtra/multiple-extras/test.ts +++ b/packages/node-integration-tests/suites/public-api/setExtra/multiple-extras/test.ts @@ -1,10 +1,11 @@ -import { assertSentryEvent, getEventRequest, runServer } from '../../../../utils'; +import { assertSentryEvent, getMultipleEnvelopeRequest, runServer } from '../../../../utils'; test('should record multiple extras of different types', async () => { const url = await runServer(__dirname); - const requestBody = await getEventRequest(url); + const envelopes = await getMultipleEnvelopeRequest(url, 2); + const errorEnvelope = envelopes[1]; - assertSentryEvent(requestBody, { + assertSentryEvent(errorEnvelope[2], { message: 'multiple_extras', extra: { extra_1: { foo: 'bar', baz: { qux: 'quux' } }, diff --git a/packages/node-integration-tests/suites/public-api/setExtra/non-serializable-extra/test.ts b/packages/node-integration-tests/suites/public-api/setExtra/non-serializable-extra/test.ts index 24005031781b..3cd2ca078eeb 100644 --- a/packages/node-integration-tests/suites/public-api/setExtra/non-serializable-extra/test.ts +++ b/packages/node-integration-tests/suites/public-api/setExtra/non-serializable-extra/test.ts @@ -1,10 +1,11 @@ -import { assertSentryEvent, getEventRequest, runServer } from '../../../../utils'; +import { assertSentryEvent, getMultipleEnvelopeRequest, runServer } from '../../../../utils'; test('should normalize non-serializable extra', async () => { const url = await runServer(__dirname); - const requestBody = await getEventRequest(url); + const envelopes = await getMultipleEnvelopeRequest(url, 2); + const errorEnvelope = envelopes[1]; - assertSentryEvent(requestBody, { + assertSentryEvent(errorEnvelope[2], { message: 'non_serializable', extra: {}, }); diff --git a/packages/node-integration-tests/suites/public-api/setExtra/simple-extra/test.ts b/packages/node-integration-tests/suites/public-api/setExtra/simple-extra/test.ts index 23c0cbc6ad42..33bfe641bfa3 100644 --- a/packages/node-integration-tests/suites/public-api/setExtra/simple-extra/test.ts +++ b/packages/node-integration-tests/suites/public-api/setExtra/simple-extra/test.ts @@ -1,10 +1,11 @@ -import { assertSentryEvent, getEventRequest, runServer } from '../../../../utils'; +import { assertSentryEvent, getMultipleEnvelopeRequest, runServer } from '../../../../utils'; test('should set a simple extra', async () => { const url = await runServer(__dirname); - const requestBody = await getEventRequest(url); + const envelopes = await getMultipleEnvelopeRequest(url, 2); + const errorEnvelope = envelopes[1]; - assertSentryEvent(requestBody, { + assertSentryEvent(errorEnvelope[2], { message: 'simple_extra', extra: { foo: { diff --git a/packages/node-integration-tests/suites/public-api/setExtras/consecutive-calls/test.ts b/packages/node-integration-tests/suites/public-api/setExtras/consecutive-calls/test.ts index 5126cde1a58f..464324c97fdf 100644 --- a/packages/node-integration-tests/suites/public-api/setExtras/consecutive-calls/test.ts +++ b/packages/node-integration-tests/suites/public-api/setExtras/consecutive-calls/test.ts @@ -1,10 +1,11 @@ -import { assertSentryEvent, getEventRequest, runServer } from '../../../../utils'; +import { assertSentryEvent, getMultipleEnvelopeRequest, runServer } from '../../../../utils'; test('should set extras from multiple consecutive calls', async () => { const url = await runServer(__dirname); - const requestBody = await getEventRequest(url); + const envelopes = await getMultipleEnvelopeRequest(url, 2); + const errorEnvelope = envelopes[1]; - assertSentryEvent(requestBody, { + assertSentryEvent(errorEnvelope[2], { message: 'consecutive_calls', extra: { extra: [], Infinity: 2, null: 0, obj: { foo: ['bar', 'baz', 1] } }, }); diff --git a/packages/node-integration-tests/suites/public-api/setExtras/multiple-extras/test.ts b/packages/node-integration-tests/suites/public-api/setExtras/multiple-extras/test.ts index f1c653307e45..d23c8e815a06 100644 --- a/packages/node-integration-tests/suites/public-api/setExtras/multiple-extras/test.ts +++ b/packages/node-integration-tests/suites/public-api/setExtras/multiple-extras/test.ts @@ -1,10 +1,11 @@ -import { assertSentryEvent, getEventRequest, runServer } from '../../../../utils'; +import { assertSentryEvent, getMultipleEnvelopeRequest, runServer } from '../../../../utils'; test('should record an extras object', async () => { const url = await runServer(__dirname); - const requestBody = await getEventRequest(url); + const envelopes = await getMultipleEnvelopeRequest(url, 2); + const errorEnvelope = envelopes[1]; - assertSentryEvent(requestBody, { + assertSentryEvent(errorEnvelope[2], { message: 'multiple_extras', extra: { extra_1: [1, ['foo'], 'bar'], diff --git a/packages/node-integration-tests/suites/public-api/setTag/with-primitives/test.ts b/packages/node-integration-tests/suites/public-api/setTag/with-primitives/test.ts index c6b94aca64d1..88d78b70c655 100644 --- a/packages/node-integration-tests/suites/public-api/setTag/with-primitives/test.ts +++ b/packages/node-integration-tests/suites/public-api/setTag/with-primitives/test.ts @@ -1,10 +1,11 @@ -import { assertSentryEvent, getEventRequest, runServer } from '../../../../utils'; +import { assertSentryEvent, getMultipleEnvelopeRequest, runServer } from '../../../../utils'; test('should set primitive tags', async () => { const url = await runServer(__dirname); - const requestBody = await getEventRequest(url); + const envelopes = await getMultipleEnvelopeRequest(url, 2); + const envelope = envelopes[1]; - assertSentryEvent(requestBody, { + assertSentryEvent(envelope[2], { message: 'primitive_tags', tags: { tag_1: 'foo', diff --git a/packages/node-integration-tests/suites/public-api/setTags/with-primitives/test.ts b/packages/node-integration-tests/suites/public-api/setTags/with-primitives/test.ts index c6b94aca64d1..88d78b70c655 100644 --- a/packages/node-integration-tests/suites/public-api/setTags/with-primitives/test.ts +++ b/packages/node-integration-tests/suites/public-api/setTags/with-primitives/test.ts @@ -1,10 +1,11 @@ -import { assertSentryEvent, getEventRequest, runServer } from '../../../../utils'; +import { assertSentryEvent, getMultipleEnvelopeRequest, runServer } from '../../../../utils'; test('should set primitive tags', async () => { const url = await runServer(__dirname); - const requestBody = await getEventRequest(url); + const envelopes = await getMultipleEnvelopeRequest(url, 2); + const envelope = envelopes[1]; - assertSentryEvent(requestBody, { + assertSentryEvent(envelope[2], { message: 'primitive_tags', tags: { tag_1: 'foo', diff --git a/packages/node-integration-tests/suites/public-api/setUser/unset_user/test.ts b/packages/node-integration-tests/suites/public-api/setUser/unset_user/test.ts index 8c804f3864b8..736ed1fdf38c 100644 --- a/packages/node-integration-tests/suites/public-api/setUser/unset_user/test.ts +++ b/packages/node-integration-tests/suites/public-api/setUser/unset_user/test.ts @@ -1,18 +1,18 @@ import { Event } from '@sentry/node'; -import { assertSentryEvent, getMultipleEventRequests, runServer } from '../../../../utils'; +import { assertSentryEvent, getMultipleEnvelopeRequest, runServer } from '../../../../utils'; test('should unset user', async () => { const url = await runServer(__dirname); - const events = await getMultipleEventRequests(url, 3); + const envelopes = await getMultipleEnvelopeRequest(url, 6); - assertSentryEvent(events[0], { + assertSentryEvent(envelopes[1][2], { message: 'no_user', }); - expect((events[0] as Event).user).not.toBeDefined(); + expect((envelopes[0][2] as Event).user).not.toBeDefined(); - assertSentryEvent(events[1], { + assertSentryEvent(envelopes[3][2], { message: 'user', user: { id: 'foo', @@ -21,9 +21,9 @@ test('should unset user', async () => { }, }); - assertSentryEvent(events[2], { + assertSentryEvent(envelopes[5][2], { message: 'unset_user', }); - expect((events[2] as Event).user).not.toBeDefined(); + expect((envelopes[2][2] as Event).user).not.toBeDefined(); }); diff --git a/packages/node-integration-tests/suites/public-api/setUser/update_user/test.ts b/packages/node-integration-tests/suites/public-api/setUser/update_user/test.ts index 3f84a390cd05..27c7de89fe45 100644 --- a/packages/node-integration-tests/suites/public-api/setUser/update_user/test.ts +++ b/packages/node-integration-tests/suites/public-api/setUser/update_user/test.ts @@ -1,10 +1,10 @@ -import { assertSentryEvent, getMultipleEventRequests, runServer } from '../../../../utils'; +import { assertSentryEvent, getMultipleEnvelopeRequest, runServer } from '../../../../utils'; test('should update user', async () => { const url = await runServer(__dirname); - const events = await getMultipleEventRequests(url, 2); + const envelopes = await getMultipleEnvelopeRequest(url, 4); - assertSentryEvent(events[0], { + assertSentryEvent(envelopes[1][2], { message: 'first_user', user: { id: 'foo', @@ -12,7 +12,7 @@ test('should update user', async () => { }, }); - assertSentryEvent(events[1], { + assertSentryEvent(envelopes[3][2], { message: 'second_user', user: { id: 'baz', diff --git a/packages/node-integration-tests/suites/public-api/withScope/nested-scopes/test.ts b/packages/node-integration-tests/suites/public-api/withScope/nested-scopes/test.ts index 03a219470b0f..57047696cf0c 100644 --- a/packages/node-integration-tests/suites/public-api/withScope/nested-scopes/test.ts +++ b/packages/node-integration-tests/suites/public-api/withScope/nested-scopes/test.ts @@ -1,12 +1,12 @@ import { Event } from '@sentry/node'; -import { assertSentryEvent, getMultipleEventRequests, runServer } from '../../../../utils'; +import { assertSentryEvent, getMultipleEnvelopeRequest, runServer } from '../../../../utils'; test('should allow nested scoping', async () => { const url = await runServer(__dirname); - const events = await getMultipleEventRequests(url, 5); + const envelopes = await getMultipleEnvelopeRequest(url, 10); - assertSentryEvent(events[0], { + assertSentryEvent(envelopes[1][2], { message: 'root_before', user: { id: 'qux', @@ -14,7 +14,7 @@ test('should allow nested scoping', async () => { tags: {}, }); - assertSentryEvent(events[1], { + assertSentryEvent(envelopes[3][2], { message: 'outer_before', user: { id: 'qux', @@ -24,7 +24,7 @@ test('should allow nested scoping', async () => { }, }); - assertSentryEvent(events[2], { + assertSentryEvent(envelopes[5][2], { message: 'inner', tags: { foo: false, @@ -32,9 +32,9 @@ test('should allow nested scoping', async () => { }, }); - expect((events[2] as Event).user).toBeUndefined(); + expect((envelopes[4][2] as Event).user).toBeUndefined(); - assertSentryEvent(events[3], { + assertSentryEvent(envelopes[7][2], { message: 'outer_after', user: { id: 'baz', @@ -44,7 +44,7 @@ test('should allow nested scoping', async () => { }, }); - assertSentryEvent(events[4], { + assertSentryEvent(envelopes[9][2], { message: 'root_after', user: { id: 'qux', diff --git a/packages/node-integration-tests/suites/sessions/crashed-session-aggregate/test.ts b/packages/node-integration-tests/suites/sessions/crashed-session-aggregate/test.ts index 28ce0ba822c0..13051fd9c47a 100644 --- a/packages/node-integration-tests/suites/sessions/crashed-session-aggregate/test.ts +++ b/packages/node-integration-tests/suites/sessions/crashed-session-aggregate/test.ts @@ -1,17 +1,17 @@ import path from 'path'; -import { getEnvelopeRequest, runServer } from '../../../utils'; +import { getMultipleEnvelopeRequest, runServer } from '../../../utils'; test('should aggregate successful and crashed sessions', async () => { const url = await runServer(__dirname, `${path.resolve(__dirname, '..')}/server.ts`); - const envelope = await Promise.race([ - getEnvelopeRequest(`${url}/success`), - getEnvelopeRequest(`${url}/error_unhandled`), - getEnvelopeRequest(`${url}/success_next`), + const envelopes = await Promise.race([ + getMultipleEnvelopeRequest(`${url}/success`, 2), + getMultipleEnvelopeRequest(`${url}/error_unhandled`, 2), + getMultipleEnvelopeRequest(`${url}/success_next`, 2), ]); + const envelope = envelopes[1]; - expect(envelope).toHaveLength(3); expect(envelope[0]).toMatchObject({ sent_at: expect.any(String), sdk: { diff --git a/packages/node-integration-tests/suites/sessions/errored-session-aggregate/test.ts b/packages/node-integration-tests/suites/sessions/errored-session-aggregate/test.ts index ad1cf46f2704..0f3130cd9b09 100644 --- a/packages/node-integration-tests/suites/sessions/errored-session-aggregate/test.ts +++ b/packages/node-integration-tests/suites/sessions/errored-session-aggregate/test.ts @@ -1,18 +1,19 @@ import path from 'path'; -import { getEnvelopeRequest, runServer } from '../../../utils'; +import { getMultipleEnvelopeRequest, runServer } from '../../../utils'; test('should aggregate successful, crashed and erroneous sessions', async () => { const url = await runServer(__dirname, `${path.resolve(__dirname, '..')}/server.ts`); - const envelope = await Promise.race([ - getEnvelopeRequest(`${url}/success`), - getEnvelopeRequest(`${url}/error_handled`), - getEnvelopeRequest(`${url}/error_unhandled`), + const envelopes = await Promise.race([ + getMultipleEnvelopeRequest(`${url}/success`, 3), + getMultipleEnvelopeRequest(`${url}/error_handled`, 3), + getMultipleEnvelopeRequest(`${url}/error_unhandled`, 3), ]); - expect(envelope).toHaveLength(3); - expect(envelope[0]).toMatchObject({ + expect(envelopes).toHaveLength(3); + const aggregateSessionEnvelope = envelopes[2]; + expect(aggregateSessionEnvelope[0]).toMatchObject({ sent_at: expect.any(String), sdk: { name: 'sentry.javascript.node', @@ -20,11 +21,11 @@ test('should aggregate successful, crashed and erroneous sessions', async () => }, }); - expect(envelope[1]).toMatchObject({ + expect(aggregateSessionEnvelope[1]).toMatchObject({ type: 'sessions', }); - expect(envelope[2]).toMatchObject({ + expect(aggregateSessionEnvelope[2]).toMatchObject({ aggregates: [ { started: expect.any(String), diff --git a/packages/node-integration-tests/utils/index.ts b/packages/node-integration-tests/utils/index.ts index d652f63185b0..fba8bb7d52de 100644 --- a/packages/node-integration-tests/utils/index.ts +++ b/packages/node-integration-tests/utils/index.ts @@ -65,42 +65,6 @@ export const parseEnvelope = (body: string): Array> => { return body.split('\n').map(e => JSON.parse(e)); }; -/** - * Intercepts and extracts multiple requests containing a Sentry Event - * - * @param {string} url - * @param {number} count - * @return {*} {Promise>>} - */ -export const getMultipleEventRequests = async (url: string, count: number): Promise>> => { - const events: Record[] = []; - - return new Promise(resolve => { - nock('https://dsn.ingest.sentry.io') - .post('/api/1337/store/', body => { - events.push(body); - - if (events.length === count) { - resolve(events); - } - return true; - }) - .times(7) - .reply(200); - http.get(url); - }); -}; - -/** - * Intercepts and extracts a single request containing a Sentry Event - * - * @param {string} url - * @return {*} {Promise>} - */ -export const getEventRequest = async (url: string): Promise> => { - return (await getMultipleEventRequests(url, 1))[0]; -}; - /** * Intercepts and extracts up to a number of requests containing Sentry envelopes. * diff --git a/packages/node/src/client.ts b/packages/node/src/client.ts index 4ba91d0db9a2..e6a0fc7a43a0 100644 --- a/packages/node/src/client.ts +++ b/packages/node/src/client.ts @@ -1,6 +1,6 @@ -import { BaseClient, NewTransport, Scope, SDK_VERSION } from '@sentry/core'; +import { BaseClient, Scope, SDK_VERSION } from '@sentry/core'; import { SessionFlusher } from '@sentry/hub'; -import { Event, EventHint, Severity, SeverityLevel, Transport } from '@sentry/types'; +import { Event, EventHint, Severity, SeverityLevel } from '@sentry/types'; import { logger, resolvedSyncPromise } from '@sentry/utils'; import { eventFromMessage, eventFromUnknownInput } from './eventbuilder'; @@ -20,7 +20,7 @@ export class NodeClient extends BaseClient { * Creates a new Node SDK instance. * @param options Configuration options for this SDK. */ - public constructor(options: NodeClientOptions, transport: Transport, newTransport?: NewTransport) { + public constructor(options: NodeClientOptions) { options._metadata = options._metadata || {}; options._metadata.sdk = options._metadata.sdk || { name: 'sentry.javascript.node', @@ -33,7 +33,7 @@ export class NodeClient extends BaseClient { version: SDK_VERSION, }; - super(options, transport, newTransport); + super(options); } /** @@ -99,7 +99,7 @@ export class NodeClient extends BaseClient { if (!release) { IS_DEBUG_BUILD && logger.warn('Cannot initialise an instance of SessionFlusher if no release is provided!'); } else { - this._sessionFlusher = new SessionFlusher(this.getTransport(), { + this._sessionFlusher = new SessionFlusher(this, { release, environment, }); diff --git a/packages/node/src/index.ts b/packages/node/src/index.ts index 0e8febd8a92a..ec659b543bff 100644 --- a/packages/node/src/index.ts +++ b/packages/node/src/index.ts @@ -26,6 +26,7 @@ export { captureEvent, captureMessage, configureScope, + createTransport, getHubFromCarrier, getCurrentHub, Hub, diff --git a/packages/node/src/sdk.ts b/packages/node/src/sdk.ts index 4b1e503e851a..5658257776b1 100644 --- a/packages/node/src/sdk.ts +++ b/packages/node/src/sdk.ts @@ -8,7 +8,7 @@ import { NodeClient } from './client'; import { IS_DEBUG_BUILD } from './flags'; import { Console, ContextLines, Http, LinkedErrors, OnUncaughtException, OnUnhandledRejection } from './integrations'; import { nodeStackParser } from './stack-parser'; -import { HTTPSTransport, HTTPTransport, setupNodeTransport } from './transports'; +import { makeNodeTransport } from './transports'; import { NodeClientOptions, NodeOptions } from './types'; export const defaultIntegrations = [ @@ -127,18 +127,15 @@ export function init(options: NodeOptions = {}): void { setHubOnCarrier(carrier, getCurrentHub()); } - const { transport, newTransport } = setupNodeTransport(options); - // TODO(v7): Refactor this to reduce the logic above const clientOptions: NodeClientOptions = { ...options, stackParser: stackParserFromOptions(options.stackParser || [nodeStackParser]), integrations: getIntegrationsToSetup(options), - // TODO(v7): Fix me when we switch to new transports entirely. - transport: options.transport || (transport instanceof HTTPTransport ? HTTPTransport : HTTPSTransport), + transport: options.transport || makeNodeTransport, }; - initAndBind(NodeClient, clientOptions, transport, newTransport); + initAndBind(NodeClient, clientOptions); if (options.autoSessionTracking) { startSessionTracking(); diff --git a/packages/node/src/transports/index.ts b/packages/node/src/transports/index.ts index 01877029269e..958562933321 100644 --- a/packages/node/src/transports/index.ts +++ b/packages/node/src/transports/index.ts @@ -4,4 +4,3 @@ export { BaseTransport } from './base'; export { HTTPTransport } from './http'; export { HTTPSTransport } from './https'; export { makeNodeTransport } from './new'; -export { setupNodeTransport } from './setup'; diff --git a/packages/node/src/transports/new.ts b/packages/node/src/transports/new.ts index b30dc3ffe36f..e31a58b03b7b 100644 --- a/packages/node/src/transports/new.ts +++ b/packages/node/src/transports/new.ts @@ -1,11 +1,11 @@ +import { createTransport } from '@sentry/core'; import { BaseTransportOptions, - createTransport, NewTransport, TransportMakeRequestResponse, TransportRequest, TransportRequestExecutor, -} from '@sentry/core'; +} from '@sentry/types'; import { eventStatusFromHttpCode } from '@sentry/utils'; import * as http from 'http'; import * as https from 'https'; @@ -91,7 +91,6 @@ function createRequestExecutor( agent: http.Agent, ): TransportRequestExecutor { const { hostname, pathname, port, protocol, search } = new URL(options.url); - return function makeRequest(request: TransportRequest): Promise { return new Promise((resolve, reject) => { const req = httpModule.request( diff --git a/packages/node/src/transports/setup.ts b/packages/node/src/transports/setup.ts deleted file mode 100644 index 2cffd2d43ecd..000000000000 --- a/packages/node/src/transports/setup.ts +++ /dev/null @@ -1,52 +0,0 @@ -import { getEnvelopeEndpointWithUrlEncodedAuth, initAPIDetails, NewTransport, NoopTransport } from '@sentry/core'; -import { Transport, TransportOptions } from '@sentry/types'; -import { makeDsn } from '@sentry/utils'; - -import { NodeOptions } from '../types'; -import { HTTPSTransport, HTTPTransport, makeNodeTransport } from '.'; - -/** - * Sets up Node transport based on the passed `options`. - * - * @returns an object currently still containing both, the old `Transport` and - * `NewTransport` which will eventually replace `Transport`. Once this is replaced, - * this function will return a ready to use `NewTransport`. - */ -// TODO(v7): Adjust return value when NewTransport is the default -export function setupNodeTransport(options: NodeOptions): { transport: Transport; newTransport?: NewTransport } { - if (!options.dsn) { - // We return the noop transport here in case there is no Dsn. - return { transport: new NoopTransport() }; - } - - const dsn = makeDsn(options.dsn); - - const transportOptions: TransportOptions = { - ...options.transportOptions, - ...(options.httpProxy && { httpProxy: options.httpProxy }), - ...(options.httpsProxy && { httpsProxy: options.httpsProxy }), - ...(options.caCerts && { caCerts: options.caCerts }), - dsn: options.dsn, - tunnel: options.tunnel, - _metadata: options._metadata, - }; - - if (options.transport) { - return { transport: new options.transport(transportOptions) }; - } - - const api = initAPIDetails(transportOptions.dsn, transportOptions._metadata, transportOptions.tunnel); - const url = getEnvelopeEndpointWithUrlEncodedAuth(api.dsn, api.tunnel); - - const newTransport = makeNodeTransport({ - url, - headers: transportOptions.headers, - proxy: transportOptions.httpProxy, - caCerts: transportOptions.caCerts, - }); - - if (dsn.protocol === 'http') { - return { transport: new HTTPTransport(transportOptions), newTransport }; - } - return { transport: new HTTPSTransport(transportOptions), newTransport }; -} diff --git a/packages/node/test/client.test.ts b/packages/node/test/client.test.ts index bb3d13ca4122..2aafb8a2544e 100644 --- a/packages/node/test/client.test.ts +++ b/packages/node/test/client.test.ts @@ -1,7 +1,6 @@ import { Scope, SessionFlusher } from '@sentry/hub'; import { NodeClient } from '../src'; -import { setupNodeTransport } from '../src/transports'; import { getDefaultNodeClientOptions } from './helper/node-client-options'; const PUBLIC_DSN = 'https://username@domain/123'; @@ -17,7 +16,7 @@ describe('NodeClient', () => { describe('captureException', () => { test('when autoSessionTracking is enabled, and requestHandler is not used -> requestStatus should not be set', () => { const options = getDefaultNodeClientOptions({ dsn: PUBLIC_DSN, autoSessionTracking: true, release: '1.4' }); - client = new NodeClient(options, setupNodeTransport(options).transport); + client = new NodeClient(options); const scope = new Scope(); scope.setRequestSession({ status: 'ok' }); @@ -28,7 +27,7 @@ describe('NodeClient', () => { }); test('when autoSessionTracking is disabled -> requestStatus should not be set', () => { const options = getDefaultNodeClientOptions({ dsn: PUBLIC_DSN, autoSessionTracking: false, release: '1.4' }); - client = new NodeClient(options, setupNodeTransport(options).transport); + client = new NodeClient(options); // It is required to initialise SessionFlusher to capture Session Aggregates (it is usually initialised // by the`requestHandler`) client.initSessionFlusher(); @@ -43,7 +42,7 @@ describe('NodeClient', () => { }); test('when autoSessionTracking is enabled + requestSession status is Crashed -> requestStatus should not be overridden', () => { const options = getDefaultNodeClientOptions({ dsn: PUBLIC_DSN, autoSessionTracking: true, release: '1.4' }); - client = new NodeClient(options, setupNodeTransport(options).transport); + client = new NodeClient(options); // It is required to initialise SessionFlusher to capture Session Aggregates (it is usually initialised // by the`requestHandler`) client.initSessionFlusher(); @@ -58,7 +57,7 @@ describe('NodeClient', () => { }); test('when autoSessionTracking is enabled + error occurs within request bounds -> requestStatus should be set to Errored', () => { const options = getDefaultNodeClientOptions({ dsn: PUBLIC_DSN, autoSessionTracking: true, release: '1.4' }); - client = new NodeClient(options, setupNodeTransport(options).transport); + client = new NodeClient(options); // It is required to initialise SessionFlusher to capture Session Aggregates (it is usually initialised // by the`requestHandler`) client.initSessionFlusher(); @@ -73,7 +72,7 @@ describe('NodeClient', () => { }); test('when autoSessionTracking is enabled + error occurs outside of request bounds -> requestStatus should not be set to Errored', () => { const options = getDefaultNodeClientOptions({ dsn: PUBLIC_DSN, autoSessionTracking: true, release: '1.4' }); - client = new NodeClient(options, setupNodeTransport(options).transport); + client = new NodeClient(options); // It is required to initialise SessionFlusher to capture Session Aggregates (it is usually initialised // by the`requestHandler`) client.initSessionFlusher(); @@ -90,7 +89,7 @@ describe('NodeClient', () => { describe('captureEvent()', () => { test('If autoSessionTracking is disabled, requestSession status should not be set', () => { const options = getDefaultNodeClientOptions({ dsn: PUBLIC_DSN, autoSessionTracking: false, release: '1.4' }); - client = new NodeClient(options, setupNodeTransport(options).transport); + client = new NodeClient(options); // It is required to initialise SessionFlusher to capture Session Aggregates (it is usually initialised // by the`requestHandler`) client.initSessionFlusher(); @@ -109,7 +108,7 @@ describe('NodeClient', () => { test('When captureEvent is called with an exception, requestSession status should be set to Errored', () => { const options = getDefaultNodeClientOptions({ dsn: PUBLIC_DSN, autoSessionTracking: true, release: '2.2' }); - client = new NodeClient(options, setupNodeTransport(options).transport); + client = new NodeClient(options); // It is required to initialise SessionFlusher to capture Session Aggregates (it is usually initialised // by the`requestHandler`) client.initSessionFlusher(); @@ -125,7 +124,7 @@ describe('NodeClient', () => { test('When captureEvent is called without an exception, requestSession status should not be set to Errored', () => { const options = getDefaultNodeClientOptions({ dsn: PUBLIC_DSN, autoSessionTracking: true, release: '2.2' }); - client = new NodeClient(options, setupNodeTransport(options).transport); + client = new NodeClient(options); // It is required to initialise SessionFlusher to capture Session Aggregates (it is usually initialised // by the`requestHandler`) client.initSessionFlusher(); @@ -141,7 +140,7 @@ describe('NodeClient', () => { test('When captureEvent is called with an exception but outside of a request, then requestStatus should not be set', () => { const options = getDefaultNodeClientOptions({ dsn: PUBLIC_DSN, autoSessionTracking: true, release: '2.2' }); - client = new NodeClient(options, setupNodeTransport(options).transport); + client = new NodeClient(options); // It is required to initialise SessionFlusher to capture Session Aggregates (it is usually initialised // by the`requestHandler`) client.initSessionFlusher(); @@ -159,7 +158,7 @@ describe('NodeClient', () => { test('When captureEvent is called with a transaction, then requestSession status should not be set', () => { const options = getDefaultNodeClientOptions({ dsn: PUBLIC_DSN, autoSessionTracking: true, release: '1.3' }); - client = new NodeClient(options, setupNodeTransport(options).transport); + client = new NodeClient(options); // It is required to initialise SessionFlusher to capture Session Aggregates (it is usually initialised // by the`requestHandler`) client.initSessionFlusher(); @@ -174,7 +173,7 @@ describe('NodeClient', () => { test('When captureEvent is called with an exception but requestHandler is not used, then requestSession status should not be set', () => { const options = getDefaultNodeClientOptions({ dsn: PUBLIC_DSN, autoSessionTracking: true, release: '1.3' }); - client = new NodeClient(options, setupNodeTransport(options).transport); + client = new NodeClient(options); const scope = new Scope(); scope.setRequestSession({ status: 'ok' }); @@ -198,7 +197,7 @@ describe('flush/close', () => { autoSessionTracking: true, release: '1.1', }); - const client = new NodeClient(options, setupNodeTransport(options).transport); + const client = new NodeClient(options); client.initSessionFlusher(); // Clearing interval is important here to ensure that the flush function later on is called by the `client.close()` // not due to the interval running every 60s diff --git a/packages/node/test/handlers.test.ts b/packages/node/test/handlers.test.ts index b857e1b74bf5..a423a92aa3d0 100644 --- a/packages/node/test/handlers.test.ts +++ b/packages/node/test/handlers.test.ts @@ -18,7 +18,6 @@ import { tracingHandler, } from '../src/handlers'; import * as SDK from '../src/sdk'; -import { setupNodeTransport } from '../src/transports'; import { getDefaultNodeClientOptions } from './helper/node-client-options'; describe('parseRequest', () => { @@ -226,7 +225,7 @@ describe('requestHandler', () => { it('autoSessionTracking is enabled, sets requestSession status to ok, when handling a request', () => { const options = getDefaultNodeClientOptions({ autoSessionTracking: true, release: '1.2' }); - client = new NodeClient(options, setupNodeTransport(options).transport); + client = new NodeClient(options); const hub = new Hub(client); jest.spyOn(sentryCore, 'getCurrentHub').mockReturnValue(hub); @@ -239,7 +238,7 @@ describe('requestHandler', () => { it('autoSessionTracking is disabled, does not set requestSession, when handling a request', () => { const options = getDefaultNodeClientOptions({ autoSessionTracking: false, release: '1.2' }); - client = new NodeClient(options, setupNodeTransport(options).transport); + client = new NodeClient(options); const hub = new Hub(client); jest.spyOn(sentryCore, 'getCurrentHub').mockReturnValue(hub); @@ -252,7 +251,7 @@ describe('requestHandler', () => { it('autoSessionTracking is enabled, calls _captureRequestSession, on response finish', done => { const options = getDefaultNodeClientOptions({ autoSessionTracking: true, release: '1.2' }); - client = new NodeClient(options, setupNodeTransport(options).transport); + client = new NodeClient(options); const hub = new Hub(client); jest.spyOn(sentryCore, 'getCurrentHub').mockReturnValue(hub); @@ -273,7 +272,7 @@ describe('requestHandler', () => { it('autoSessionTracking is disabled, does not call _captureRequestSession, on response finish', done => { const options = getDefaultNodeClientOptions({ autoSessionTracking: false, release: '1.2' }); - client = new NodeClient(options, setupNodeTransport(options).transport); + client = new NodeClient(options); const hub = new Hub(client); jest.spyOn(sentryCore, 'getCurrentHub').mockReturnValue(hub); @@ -374,7 +373,7 @@ describe('tracingHandler', () => { it('extracts request data for sampling context', () => { const tracesSampler = jest.fn(); const options = getDefaultNodeClientOptions({ tracesSampler }); - const hub = new Hub(new NodeClient(options, setupNodeTransport(options).transport)); + const hub = new Hub(new NodeClient(options)); // we need to mock both of these because the tracing handler relies on `@sentry/core` while the sampler relies on // `@sentry/hub`, and mocking breaks the link between the two jest.spyOn(sentryCore, 'getCurrentHub').mockReturnValue(hub); @@ -397,7 +396,7 @@ describe('tracingHandler', () => { it('puts its transaction on the scope', () => { const options = getDefaultNodeClientOptions({ tracesSampleRate: 1.0 }); - const hub = new Hub(new NodeClient(options, setupNodeTransport(options).transport)); + const hub = new Hub(new NodeClient(options)); // we need to mock both of these because the tracing handler relies on `@sentry/core` while the sampler relies on // `@sentry/hub`, and mocking breaks the link between the two jest.spyOn(sentryCore, 'getCurrentHub').mockReturnValue(hub); @@ -729,7 +728,7 @@ describe('errorHandler()', () => { }); it('when autoSessionTracking is disabled, does not set requestSession status on Crash', () => { const options = getDefaultNodeClientOptions({ autoSessionTracking: false, release: '3.3' }); - client = new NodeClient(options, setupNodeTransport(options).transport); + client = new NodeClient(options); // It is required to initialise SessionFlusher to capture Session Aggregates (it is usually initialised // by the`requestHandler`) client.initSessionFlusher(); @@ -749,7 +748,7 @@ describe('errorHandler()', () => { it('autoSessionTracking is enabled + requestHandler is not used -> does not set requestSession status on Crash', () => { const options = getDefaultNodeClientOptions({ autoSessionTracking: false, release: '3.3' }); - client = new NodeClient(options, setupNodeTransport(options).transport); + client = new NodeClient(options); const scope = sentryCore.getCurrentHub().getScope(); const hub = new Hub(client); @@ -766,7 +765,7 @@ describe('errorHandler()', () => { it('when autoSessionTracking is enabled, should set requestSession status to Crashed when an unhandled error occurs within the bounds of a request', () => { const options = getDefaultNodeClientOptions({ autoSessionTracking: true, release: '1.1' }); - client = new NodeClient(options, setupNodeTransport(options).transport); + client = new NodeClient(options); // It is required to initialise SessionFlusher to capture Session Aggregates (it is usually initialised // by the`requestHandler`) client.initSessionFlusher(); @@ -785,7 +784,7 @@ describe('errorHandler()', () => { it('when autoSessionTracking is enabled, should not set requestSession status on Crash when it occurs outside the bounds of a request', () => { const options = getDefaultNodeClientOptions({ autoSessionTracking: true, release: '2.2' }); - client = new NodeClient(options, setupNodeTransport(options).transport); + client = new NodeClient(options); // It is required to initialise SessionFlusher to capture Session Aggregates (it is usually initialised // by the`requestHandler`) client.initSessionFlusher(); diff --git a/packages/node/test/helper/node-client-options.ts b/packages/node/test/helper/node-client-options.ts index c2bb1d42a871..a010c18f5811 100644 --- a/packages/node/test/helper/node-client-options.ts +++ b/packages/node/test/helper/node-client-options.ts @@ -1,11 +1,12 @@ -import { NoopTransport } from '@sentry/core'; +import { createTransport } from '@sentry/core'; +import { resolvedSyncPromise } from '@sentry/utils'; import { NodeClientOptions } from '../../src/types'; export function getDefaultNodeClientOptions(options: Partial = {}): NodeClientOptions { return { integrations: [], - transport: NoopTransport, + transport: () => createTransport({}, _ => resolvedSyncPromise({ statusCode: 200 })), stackParser: () => [], ...options, }; diff --git a/packages/node/test/index.test.ts b/packages/node/test/index.test.ts index 2bdf8497097c..502542a25491 100644 --- a/packages/node/test/index.test.ts +++ b/packages/node/test/index.test.ts @@ -18,7 +18,6 @@ import { } from '../src'; import { ContextLines, LinkedErrors } from '../src/integrations'; import { nodeStackParser } from '../src/stack-parser'; -import { setupNodeTransport } from '../src/transports'; import { getDefaultNodeClientOptions } from './helper/node-client-options'; const stackParser = createStackParser(nodeStackParser); @@ -102,7 +101,7 @@ describe('SentryNode', () => { dsn, stackParser, }); - const client = new NodeClient(options, setupNodeTransport(options).transport); + const client = new NodeClient(options); getCurrentHub().bindClient(client); addBreadcrumb({ message: 'test1' }); addBreadcrumb({ message: 'test2' }); @@ -137,7 +136,7 @@ describe('SentryNode', () => { }, dsn, }); - getCurrentHub().bindClient(new NodeClient(options, setupNodeTransport(options).transport)); + getCurrentHub().bindClient(new NodeClient(options)); configureScope((scope: Scope) => { scope.setTag('test', '1'); }); @@ -164,7 +163,7 @@ describe('SentryNode', () => { }, dsn, }); - getCurrentHub().bindClient(new NodeClient(options, setupNodeTransport(options).transport)); + getCurrentHub().bindClient(new NodeClient(options)); configureScope((scope: Scope) => { scope.setTag('test', '1'); }); @@ -195,7 +194,7 @@ describe('SentryNode', () => { }, dsn, }); - getCurrentHub().bindClient(new NodeClient(options, setupNodeTransport(options).transport)); + getCurrentHub().bindClient(new NodeClient(options)); configureScope((scope: Scope) => { scope.setTag('test', '1'); }); @@ -233,7 +232,7 @@ describe('SentryNode', () => { }, dsn, }); - getCurrentHub().bindClient(new NodeClient(options, setupNodeTransport(options).transport)); + getCurrentHub().bindClient(new NodeClient(options)); try { throw new Error('test'); } catch (e) { @@ -258,7 +257,7 @@ describe('SentryNode', () => { }, dsn, }); - getCurrentHub().bindClient(new NodeClient(options, setupNodeTransport(options).transport)); + getCurrentHub().bindClient(new NodeClient(options)); captureMessage('test'); }); @@ -274,7 +273,7 @@ describe('SentryNode', () => { }, dsn, }); - getCurrentHub().bindClient(new NodeClient(options, setupNodeTransport(options).transport)); + getCurrentHub().bindClient(new NodeClient(options)); captureEvent({ message: 'test event' }); }); @@ -291,7 +290,7 @@ describe('SentryNode', () => { }, dsn, }); - const client = new NodeClient(options, setupNodeTransport(options).transport); + const client = new NodeClient(options); d.run(() => { getCurrentHub().bindClient(client); @@ -314,7 +313,7 @@ describe('SentryNode', () => { }, dsn, }); - getCurrentHub().bindClient(new NodeClient(options, setupNodeTransport(options).transport)); + getCurrentHub().bindClient(new NodeClient(options)); try { // @ts-ignore allow function declarations in strict mode // eslint-disable-next-line no-inner-declarations @@ -368,7 +367,7 @@ describe('SentryNode initialization', () => { init({ dsn }); // eslint-disable-next-line @typescript-eslint/no-explicit-any - const sdkData = (getCurrentHub().getClient() as any).getTransport()._api.metadata?.sdk; + const sdkData = (getCurrentHub().getClient() as any).getOptions()._metadata.sdk; expect(sdkData.name).toEqual('sentry.javascript.node'); expect(sdkData.packages[0].name).toEqual('npm:@sentry/node'); @@ -378,10 +377,10 @@ describe('SentryNode initialization', () => { it('should set SDK data when instantiating a client directly', () => { const options = getDefaultNodeClientOptions({ dsn }); - const client = new NodeClient(options, setupNodeTransport(options).transport); + const client = new NodeClient(options); // eslint-disable-next-line @typescript-eslint/no-explicit-any - const sdkData = (client as any).getTransport()._api.metadata?.sdk; + const sdkData = (client as any).getOptions()._metadata.sdk; expect(sdkData.name).toEqual('sentry.javascript.node'); expect(sdkData.packages[0].name).toEqual('npm:@sentry/node'); @@ -410,7 +409,7 @@ describe('SentryNode initialization', () => { }); // eslint-disable-next-line @typescript-eslint/no-explicit-any - const sdkData = (getCurrentHub().getClient() as any).getTransport()._api.metadata?.sdk; + const sdkData = (getCurrentHub().getClient() as any).getOptions()._metadata.sdk; expect(sdkData.name).toEqual('sentry.javascript.serverless'); expect(sdkData.packages[0].name).toEqual('npm:@sentry/serverless'); diff --git a/packages/node/test/integrations/http.test.ts b/packages/node/test/integrations/http.test.ts index 30a923524a3e..ff0af8b65acf 100644 --- a/packages/node/test/integrations/http.test.ts +++ b/packages/node/test/integrations/http.test.ts @@ -11,7 +11,6 @@ import * as nock from 'nock'; import { Breadcrumb } from '../../src'; import { NodeClient } from '../../src/client'; import { Http as HttpIntegration } from '../../src/integrations/http'; -import { setupNodeTransport } from '../../src/transports'; import { getDefaultNodeClientOptions } from '../helper/node-client-options'; const NODE_VERSION = parseSemver(process.versions.node); @@ -23,7 +22,7 @@ describe('tracing', () => { tracesSampleRate: 1.0, integrations: [new HttpIntegration({ tracing: true })], }); - const hub = new Hub(new NodeClient(options, setupNodeTransport(options).transport)); + const hub = new Hub(new NodeClient(options)); addExtensionMethods(); // we need to mock both of these because the tracing handler relies on `@sentry/core` while the sampler relies on @@ -108,7 +107,7 @@ describe('default protocols', () => { return b; }, }); - hub.bindClient(new NodeClient(options, setupNodeTransport(options).transport)); + hub.bindClient(new NodeClient(options)); return p; } diff --git a/packages/node/test/integrations/linkederrors.test.ts b/packages/node/test/integrations/linkederrors.test.ts index 4d18707be904..6b01faa11614 100644 --- a/packages/node/test/integrations/linkederrors.test.ts +++ b/packages/node/test/integrations/linkederrors.test.ts @@ -4,7 +4,6 @@ import { createStackParser } from '@sentry/utils'; import { Event, NodeClient } from '../../src'; import { LinkedErrors } from '../../src/integrations/linkederrors'; import { nodeStackParser } from '../../src/stack-parser'; -import { setupNodeTransport } from '../../src/transports'; import { getDefaultNodeClientOptions } from '../helper/node-client-options'; const stackParser = createStackParser(nodeStackParser); @@ -34,7 +33,7 @@ describe('LinkedErrors', () => { const spy = jest.spyOn(linkedErrors, '_walkErrorTree'); const one = new Error('originalException'); const options = getDefaultNodeClientOptions({ stackParser }); - const client = new NodeClient(options, setupNodeTransport(options).transport); + const client = new NodeClient(options); let event: Event | undefined; return client .eventFromException(one) @@ -58,7 +57,7 @@ describe('LinkedErrors', () => { ); const one = new Error('originalException'); const options = getDefaultNodeClientOptions({ stackParser }); - const client = new NodeClient(options, setupNodeTransport(options).transport); + const client = new NodeClient(options); return client.eventFromException(one).then(event => linkedErrors ._handler(stackParser, event, { @@ -79,7 +78,7 @@ describe('LinkedErrors', () => { two.cause = three; const options = getDefaultNodeClientOptions({ stackParser }); - const client = new NodeClient(options, setupNodeTransport(options).transport); + const client = new NodeClient(options); return client.eventFromException(one).then(event => linkedErrors ._handler(stackParser, event, { @@ -113,7 +112,7 @@ describe('LinkedErrors', () => { two.reason = three; const options = getDefaultNodeClientOptions({ stackParser }); - const client = new NodeClient(options, setupNodeTransport(options).transport); + const client = new NodeClient(options); return client.eventFromException(one).then(event => linkedErrors ._handler(stackParser, event, { @@ -147,7 +146,7 @@ describe('LinkedErrors', () => { two.cause = three; const options = getDefaultNodeClientOptions({ stackParser }); - const client = new NodeClient(options, setupNodeTransport(options).transport); + const client = new NodeClient(options); return client.eventFromException(one).then(event => linkedErrors ._handler(stackParser, event, { diff --git a/packages/node/test/manual/express-scope-separation/start.js b/packages/node/test/manual/express-scope-separation/start.js index 9146c3ab2eba..689c9ef358e8 100644 --- a/packages/node/test/manual/express-scope-separation/start.js +++ b/packages/node/test/manual/express-scope-separation/start.js @@ -12,8 +12,8 @@ function assertTags(actual, expected) { let remaining = 3; -class DummyTransport { - sendEvent(event) { +function makeDummyTransport() { + return Sentry.createTransport({}, req => { --remaining; if (!remaining) { @@ -23,14 +23,14 @@ class DummyTransport { } return Promise.resolve({ - status: 'success', + statusCode: 200, }); - } + }) } Sentry.init({ dsn: 'http://test@example.com/1337', - transport: DummyTransport, + transport: makeDummyTransport, beforeSend(event) { if (event.transaction === 'GET /foo') { assertTags(event.tags, { diff --git a/packages/node/test/manual/release-health/session-aggregates/aggregates-disable-single-session.js b/packages/node/test/manual/release-health/session-aggregates/aggregates-disable-single-session.js index 0c536e0accae..f02788174ea0 100644 --- a/packages/node/test/manual/release-health/session-aggregates/aggregates-disable-single-session.js +++ b/packages/node/test/manual/release-health/session-aggregates/aggregates-disable-single-session.js @@ -2,7 +2,7 @@ const http = require('http'); const express = require('express'); const app = express(); const Sentry = require('../../../../build/cjs'); -const { assertSessions, BaseDummyTransport } = require('../test-utils'); +const { assertSessions } = require('../test-utils'); function cleanUpAndExitSuccessfully() { server.close(); @@ -27,9 +27,10 @@ function assertSessionAggregates(session, expected) { assertSessions(session, expected); } -class DummyTransport extends BaseDummyTransport { - sendSession(session) { - assertSessionAggregates(session, { +function makeDummyTransport() { + return Sentry.createTransport({}, req => { + const sessionEnv = req.body.split('\n').map(e => JSON.parse(e)); + assertSessionAggregates(sessionEnv[2], { attrs: { release: '1.1' }, aggregates: [{ crashed: 2, errored: 1, exited: 1 }], }); @@ -37,15 +38,15 @@ class DummyTransport extends BaseDummyTransport { cleanUpAndExitSuccessfully(); return Promise.resolve({ - status: 'success', + statusCode: 200, }); - } + }) } Sentry.init({ dsn: 'http://test@example.com/1337', release: '1.1', - transport: DummyTransport, + transport: makeDummyTransport, autoSessionTracking: true, }); /** diff --git a/packages/node/test/manual/release-health/single-session/caught-exception-errored-session.js b/packages/node/test/manual/release-health/single-session/caught-exception-errored-session.js index db052b2bc508..8f5634163f25 100644 --- a/packages/node/test/manual/release-health/single-session/caught-exception-errored-session.js +++ b/packages/node/test/manual/release-health/single-session/caught-exception-errored-session.js @@ -2,7 +2,6 @@ const Sentry = require('../../../../build/cjs'); const { assertSessions, constructStrippedSessionObject, - BaseDummyTransport, validateSessionCountFunction, } = require('../test-utils'); @@ -13,36 +12,41 @@ const sessionCounts = { validateSessionCountFunction(sessionCounts); -class DummyTransport extends BaseDummyTransport { - sendSession(session) { - sessionCounts.sessionCounter++; - - if (sessionCounts.sessionCounter === 1) { - assertSessions(constructStrippedSessionObject(session), { - init: true, - status: 'ok', - errors: 1, - release: '1.1', - }); - } +function makeDummyTransport() { + return Sentry.createTransport({}, req => { + if (req.category === 'session') { + sessionCounts.sessionCounter++; + const sessionEnv = req.body.split('\n').map(e => JSON.parse(e)); + + if (sessionCounts.sessionCounter === 1) { + assertSessions(constructStrippedSessionObject(sessionEnv[2]), { + init: true, + status: 'ok', + errors: 1, + release: '1.1', + }); + } - if (sessionCounts.sessionCounter === 2) { - assertSessions(constructStrippedSessionObject(session), { - init: false, - status: 'exited', - errors: 1, - release: '1.1', - }); + if (sessionCounts.sessionCounter === 2) { + assertSessions(constructStrippedSessionObject(sessionEnv[2]), { + init: false, + status: 'exited', + errors: 1, + release: '1.1', + }); + } } - return super.sendSession(session); - } + return Promise.resolve({ + statusCode: 200, + }); + }) } Sentry.init({ dsn: 'http://test@example.com/1337', release: '1.1', - transport: DummyTransport, + transport: makeDummyTransport, autoSessionTracking: true, }); diff --git a/packages/node/test/manual/release-health/single-session/errors-in-session-capped-to-one.js b/packages/node/test/manual/release-health/single-session/errors-in-session-capped-to-one.js index 983f10e9b294..6d230292d180 100644 --- a/packages/node/test/manual/release-health/single-session/errors-in-session-capped-to-one.js +++ b/packages/node/test/manual/release-health/single-session/errors-in-session-capped-to-one.js @@ -2,7 +2,6 @@ const Sentry = require('../../../../build/cjs'); const { assertSessions, constructStrippedSessionObject, - BaseDummyTransport, validateSessionCountFunction, } = require('../test-utils'); @@ -13,36 +12,41 @@ const sessionCounts = { validateSessionCountFunction(sessionCounts); -class DummyTransport extends BaseDummyTransport { - sendSession(session) { - sessionCounts.sessionCounter++; +function makeDummyTransport() { + return Sentry.createTransport({}, req => { + if (req.category === 'session') { + sessionCounts.sessionCounter++; + const sessionEnv = req.body.split('\n').map(e => JSON.parse(e)); - if (sessionCounts.sessionCounter === 1) { - assertSessions(constructStrippedSessionObject(session), { - init: true, - status: 'ok', - errors: 1, - release: '1.1', - }); - } + if (sessionCounts.sessionCounter === 1) { + assertSessions(constructStrippedSessionObject(sessionEnv[2]), { + init: true, + status: 'ok', + errors: 1, + release: '1.1', + }); + } - if (sessionCounts.sessionCounter === 2) { - assertSessions(constructStrippedSessionObject(session), { - init: false, - status: 'exited', - errors: 1, - release: '1.1', - }); + if (sessionCounts.sessionCounter === 2) { + assertSessions(constructStrippedSessionObject(sessionEnv[2]), { + init: false, + status: 'exited', + errors: 1, + release: '1.1', + }); + } } - return super.sendSession(session); - } + return Promise.resolve({ + statusCode: 200, + }); + }) } Sentry.init({ dsn: 'http://test@example.com/1337', release: '1.1', - transport: DummyTransport, + transport: makeDummyTransport, autoSessionTracking: true, }); /** diff --git a/packages/node/test/manual/release-health/single-session/healthy-session.js b/packages/node/test/manual/release-health/single-session/healthy-session.js index 1906b8196bf5..cfc3909449ef 100644 --- a/packages/node/test/manual/release-health/single-session/healthy-session.js +++ b/packages/node/test/manual/release-health/single-session/healthy-session.js @@ -2,7 +2,6 @@ const Sentry = require('../../../../build/cjs'); const { assertSessions, constructStrippedSessionObject, - BaseDummyTransport, validateSessionCountFunction, } = require('../test-utils'); @@ -13,25 +12,28 @@ const sessionCounts = { validateSessionCountFunction(sessionCounts); -class DummyTransport extends BaseDummyTransport { - sendSession(session) { +function makeDummyTransport() { + return Sentry.createTransport({}, req => { sessionCounts.sessionCounter++; + const sessionEnv = req.body.split('\n').map(e => JSON.parse(e)); - assertSessions(constructStrippedSessionObject(session), { + assertSessions(constructStrippedSessionObject(sessionEnv[2]), { init: true, status: 'exited', errors: 0, - release: '1.1', + release: '1.1' }); - return super.sendSession(session); - } + return Promise.resolve({ + statusCode: 200, + }); + }) } Sentry.init({ dsn: 'http://test@example.com/1337', release: '1.1', - transport: DummyTransport, + transport: makeDummyTransport, autoSessionTracking: true, }); diff --git a/packages/node/test/manual/release-health/single-session/terminal-state-sessions-sent-once.js b/packages/node/test/manual/release-health/single-session/terminal-state-sessions-sent-once.js index b2692957aa70..d0b8f72f4af4 100644 --- a/packages/node/test/manual/release-health/single-session/terminal-state-sessions-sent-once.js +++ b/packages/node/test/manual/release-health/single-session/terminal-state-sessions-sent-once.js @@ -2,7 +2,6 @@ const Sentry = require('../../../../build/cjs'); const { assertSessions, constructStrippedSessionObject, - BaseDummyTransport, validateSessionCountFunction, } = require('../test-utils'); @@ -13,25 +12,30 @@ const sessionCounts = { validateSessionCountFunction(sessionCounts); -class DummyTransport extends BaseDummyTransport { - sendSession(session) { - sessionCounts.sessionCounter++; - - assertSessions(constructStrippedSessionObject(session), { - init: true, - status: 'crashed', - errors: 1, - release: '1.1', +function makeDummyTransport() { + return Sentry.createTransport({}, req => { + if (req.category === 'session') { + sessionCounts.sessionCounter++; + const sessionEnv = req.body.split('\n').map(e => JSON.parse(e)); + + assertSessions(constructStrippedSessionObject(sessionEnv[2]), { + init: true, + status: 'crashed', + errors: 1, + release: '1.1', + }); + } + + return Promise.resolve({ + statusCode: 200, }); - - return super.sendSession(session); - } + }) } Sentry.init({ dsn: 'http://test@example.com/1337', release: '1.1', - transport: DummyTransport, + transport: makeDummyTransport, autoSessionTracking: true, }); diff --git a/packages/node/test/manual/release-health/single-session/uncaught-exception-crashed-session.js b/packages/node/test/manual/release-health/single-session/uncaught-exception-crashed-session.js index eaf247aa080a..5cee4449c23a 100644 --- a/packages/node/test/manual/release-health/single-session/uncaught-exception-crashed-session.js +++ b/packages/node/test/manual/release-health/single-session/uncaught-exception-crashed-session.js @@ -1,24 +1,29 @@ const Sentry = require('../../../../build/cjs'); -const { assertSessions, constructStrippedSessionObject, BaseDummyTransport } = require('../test-utils'); +const { assertSessions, constructStrippedSessionObject } = require('../test-utils'); -class DummyTransport extends BaseDummyTransport { - sendSession(session) { - assertSessions(constructStrippedSessionObject(session), { - init: true, - status: 'crashed', - errors: 1, - release: '1.1', - }); +function makeDummyTransport() { + return Sentry.createTransport({}, req => { + if (req.category === 'session') { + sessionCounts.sessionCounter++; + const sessionEnv = req.body.split('\n').map(e => JSON.parse(e)); + + assertSessions(constructStrippedSessionObject(sessionEnv[2]), { + init: true, + status: 'crashed', + errors: 1, + release: '1.1', + }); + } // We need to explicitly exit process early here to allow for 0 exit code process.exit(0); - } + }) } Sentry.init({ dsn: 'http://test@example.com/1337', release: '1.1', - transport: DummyTransport, + transport: makeDummyTransport, autoSessionTracking: true, }); diff --git a/packages/node/test/manual/release-health/single-session/unhandled-rejection-crashed-session.js b/packages/node/test/manual/release-health/single-session/unhandled-rejection-crashed-session.js index c5a1874024ba..d94b6add0c00 100644 --- a/packages/node/test/manual/release-health/single-session/unhandled-rejection-crashed-session.js +++ b/packages/node/test/manual/release-health/single-session/unhandled-rejection-crashed-session.js @@ -2,7 +2,6 @@ const Sentry = require('../../../../build/cjs'); const { assertSessions, constructStrippedSessionObject, - BaseDummyTransport, validateSessionCountFunction, } = require('../test-utils'); @@ -13,25 +12,30 @@ const sessionCounts = { validateSessionCountFunction(sessionCounts); -class DummyTransport extends BaseDummyTransport { - sendSession(session) { - sessionCounts.sessionCounter++; +function makeDummyTransport() { + return Sentry.createTransport({}, req => { + if (req.category === 'session') { + sessionCounts.sessionCounter++; + const sessionEnv = req.body.split('\n').map(e => JSON.parse(e)); - assertSessions(constructStrippedSessionObject(session), { - init: true, - status: 'crashed', - errors: 1, - release: '1.1', - }); + assertSessions(constructStrippedSessionObject(sessionEnv[2]), { + init: true, + status: 'crashed', + errors: 1, + release: '1.1', + }); + } - return super.sendSession(session); - } + return Promise.resolve({ + statusCode: 200, + }); + }) } Sentry.init({ dsn: 'http://test@example.com/1337', release: '1.1', - transport: DummyTransport, + transport: makeDummyTransport, autoSessionTracking: true, }); diff --git a/packages/node/test/manual/release-health/test-utils.js b/packages/node/test/manual/release-health/test-utils.js index 668923e74dd3..2b3f88a440b9 100644 --- a/packages/node/test/manual/release-health/test-utils.js +++ b/packages/node/test/manual/release-health/test-utils.js @@ -8,26 +8,10 @@ function assertSessions(actual, expected) { } function constructStrippedSessionObject(actual) { - const { init, status, errors, release, did } = actual; + const { init, status, errors, attrs: { release }, did } = actual; return { init, status, errors, release, did }; } -class BaseDummyTransport { - sendEvent(event) { - return Promise.resolve({ - status: 'success', - }); - } - sendSession(session) { - return Promise.resolve({ - status: 'success', - }); - } - close(timeout) { - return Promise.resolve(true); - } -} - function validateSessionCountFunction(sessionCounts) { process.on('exit', () => { const { sessionCounter, expectedSessions } = sessionCounts; @@ -38,4 +22,4 @@ function validateSessionCountFunction(sessionCounts) { }); } -module.exports = { assertSessions, constructStrippedSessionObject, BaseDummyTransport, validateSessionCountFunction }; +module.exports = { assertSessions, constructStrippedSessionObject, validateSessionCountFunction }; diff --git a/packages/node/test/manual/webpack-domain/index.js b/packages/node/test/manual/webpack-domain/index.js index 521699a6349a..b970c95d2a98 100644 --- a/packages/node/test/manual/webpack-domain/index.js +++ b/packages/node/test/manual/webpack-domain/index.js @@ -2,8 +2,8 @@ const Sentry = require('../../../build/cjs'); let remaining = 2; -class DummyTransport { - sendEvent(event) { +function makeDummyTransport() { + return Sentry.createTransport({}, req => { --remaining; if (!remaining) { @@ -14,12 +14,12 @@ class DummyTransport { return Promise.resolve({ status: 'success', }); - } + }) } Sentry.init({ dsn: 'https://a@example.com/1', - transport: DummyTransport, + transport: makeDummyTransport, beforeSend(event) { if (event.message === 'inside') { if (event.tags.a !== 'x' && event.tags.b !== 'c') { diff --git a/packages/node/test/transports/setup.test.ts b/packages/node/test/transports/setup.test.ts deleted file mode 100644 index 38f99a4c95f3..000000000000 --- a/packages/node/test/transports/setup.test.ts +++ /dev/null @@ -1,62 +0,0 @@ -import { NoopTransport } from '@sentry/core'; -import { FakeTransport } from '@sentry/core/test/mocks/transport'; -import { HTTPSTransport, HTTPTransport, setupNodeTransport } from '@sentry/node/src/transports'; - -import { makeNodeTransport } from '../../src/transports/new'; -import { getDefaultNodeClientOptions } from '../helper/node-client-options'; - -jest.mock('../../src/transports/new', () => { - const original = jest.requireActual('../../src/transports/new'); - return { - ...original, - makeNodeTransport: jest.fn(() => ({ - send: () => Promise.resolve({ status: 'success' }), - flush: () => Promise.resolve(true), - })), - }; -}); - -const DSN = 'https://username@domain/123'; - -describe('setupNodeTransport', () => { - afterEach(() => jest.clearAllMocks()); - - afterAll(() => jest.resetAllMocks()); - - it('returns NoopTransport if no dsn is passed', () => { - const { transport, newTransport } = setupNodeTransport({}); - - expect(transport).toBeDefined(); - expect(transport).toBeInstanceOf(NoopTransport); - expect(newTransport).toBeUndefined(); - }); - - it('returns the instantiated transport passed via the options', () => { - const options = getDefaultNodeClientOptions({ dsn: DSN, transport: FakeTransport }); - const { transport, newTransport } = setupNodeTransport(options); - - expect(transport).toBeDefined(); - expect(transport).toBeInstanceOf(FakeTransport); - expect(newTransport).toBeUndefined(); - }); - - it('returns HTTPS transport as a default', () => { - const options = { dsn: DSN }; - const { transport, newTransport } = setupNodeTransport(options); - - expect(transport).toBeDefined(); - expect(transport).toBeInstanceOf(HTTPSTransport); - expect(newTransport).toBeDefined(); - expect(makeNodeTransport).toHaveBeenCalledTimes(1); - }); - - it('returns HTTP transport if specified in the dsn', () => { - const options = { dsn: 'http://username@domain/123' }; - const { transport, newTransport } = setupNodeTransport(options); - - expect(transport).toBeDefined(); - expect(transport).toBeInstanceOf(HTTPTransport); - expect(newTransport).toBeDefined(); - expect(makeNodeTransport).toHaveBeenCalledTimes(1); - }); -}); diff --git a/packages/tracing/src/transaction.ts b/packages/tracing/src/transaction.ts index 91fa4d3c435b..3e3c148fd5d3 100644 --- a/packages/tracing/src/transaction.ts +++ b/packages/tracing/src/transaction.ts @@ -103,11 +103,12 @@ export class Transaction extends SpanClass implements TransactionInterface { // At this point if `sampled !== true` we want to discard the transaction. IS_DEBUG_BUILD && logger.log('[Tracing] Discarding transaction because its trace was not chosen to be sampled.'); - const client = this._hub.getClient(); - const transport = client && client.getTransport && client.getTransport(); - if (transport && transport.recordLostEvent) { - transport.recordLostEvent('sample_rate', 'transaction'); - } + // TODO(v7): Add back client reports functionality + // const client = this._hub.getClient(); + // const transport = client && client.getTransport && client.getTransport(); + // if (transport && transport.recordLostEvent) { + // transport.recordLostEvent('sample_rate', 'transaction'); + // } return undefined; } diff --git a/packages/tracing/test/browser/backgroundtab.test.ts b/packages/tracing/test/browser/backgroundtab.test.ts index 29612b410322..3e332033d74d 100644 --- a/packages/tracing/test/browser/backgroundtab.test.ts +++ b/packages/tracing/test/browser/backgroundtab.test.ts @@ -1,11 +1,10 @@ import { BrowserClient } from '@sentry/browser'; -import { setupBrowserTransport } from '@sentry/browser/src/transports'; -import { getDefaultBrowserClientOptions } from '@sentry/browser/test/unit/helper/browser-client-options'; import { Hub, makeMain } from '@sentry/hub'; import { JSDOM } from 'jsdom'; import { registerBackgroundTabDetection } from '../../src/browser/backgroundtab'; import { addExtensionMethods } from '../../src/hubextensions'; +import { getDefaultBrowserClientOptions } from '../testutils'; describe('registerBackgroundTabDetection', () => { let events: Record = {}; @@ -16,7 +15,7 @@ describe('registerBackgroundTabDetection', () => { global.document = dom.window.document; const options = getDefaultBrowserClientOptions({ tracesSampleRate: 1 }); - hub = new Hub(new BrowserClient(options, setupBrowserTransport(options).transport)); + hub = new Hub(new BrowserClient(options)); makeMain(hub); // If we do not add extension methods, invoking hub.startTransaction returns undefined diff --git a/packages/tracing/test/browser/browsertracing.test.ts b/packages/tracing/test/browser/browsertracing.test.ts index 76ccaf947271..5cb144cfd207 100644 --- a/packages/tracing/test/browser/browsertracing.test.ts +++ b/packages/tracing/test/browser/browsertracing.test.ts @@ -1,6 +1,4 @@ import { BrowserClient } from '@sentry/browser'; -import { setupBrowserTransport } from '@sentry/browser/src/transports'; -import { getDefaultBrowserClientOptions } from '@sentry/browser/test/unit/helper/browser-client-options'; import { Hub, makeMain } from '@sentry/hub'; import { getGlobalObject, InstrumentHandlerCallback, InstrumentHandlerType } from '@sentry/utils'; import { JSDOM } from 'jsdom'; @@ -18,6 +16,7 @@ import { instrumentRoutingWithDefaults } from '../../src/browser/router'; import * as hubExtensions from '../../src/hubextensions'; import { DEFAULT_IDLE_TIMEOUT, IdleTransaction } from '../../src/idletransaction'; import { getActiveTransaction, secToMs } from '../../src/utils'; +import { getDefaultBrowserClientOptions } from '../testutils'; let mockChangeHistory: ({ to, from }: { to: string; from?: string }) => void = () => undefined; @@ -54,7 +53,7 @@ describe('BrowserTracing', () => { beforeEach(() => { jest.useFakeTimers(); const options = getDefaultBrowserClientOptions({ tracesSampleRate: 1 }); - hub = new Hub(new BrowserClient(options, setupBrowserTransport(options).transport)); + hub = new Hub(new BrowserClient(options)); makeMain(hub); document.head.innerHTML = ''; @@ -476,7 +475,7 @@ describe('BrowserTracing', () => { const tracesSampler = jest.fn(); const options = getDefaultBrowserClientOptions({ tracesSampler }); - hub.bindClient(new BrowserClient(options, setupBrowserTransport(options).transport)); + hub.bindClient(new BrowserClient(options)); // setting up the BrowserTracing integration automatically starts a pageload transaction createBrowserTracing(true); @@ -493,7 +492,7 @@ describe('BrowserTracing', () => { const tracesSampler = jest.fn(); const options = getDefaultBrowserClientOptions({ tracesSampler }); - hub.bindClient(new BrowserClient(options, setupBrowserTransport(options).transport)); + hub.bindClient(new BrowserClient(options)); // setting up the BrowserTracing integration normally automatically starts a pageload transaction, but that's not // what we're testing here createBrowserTracing(true, { startTransactionOnPageLoad: false }); diff --git a/packages/tracing/test/browser/request.test.ts b/packages/tracing/test/browser/request.test.ts index 4ead6fe5bb9b..e77a0ae15c36 100644 --- a/packages/tracing/test/browser/request.test.ts +++ b/packages/tracing/test/browser/request.test.ts @@ -1,6 +1,4 @@ import { BrowserClient } from '@sentry/browser'; -import { setupBrowserTransport } from '@sentry/browser/src/transports'; -import { getDefaultBrowserClientOptions } from '@sentry/browser/test/unit/helper/browser-client-options'; import { Hub, makeMain } from '@sentry/hub'; import * as utils from '@sentry/utils'; @@ -8,6 +6,7 @@ import { Span, spanStatusfromHttpCode, Transaction } from '../../src'; import { fetchCallback, FetchData, instrumentOutgoingRequests, xhrCallback } from '../../src/browser/request'; import { addExtensionMethods } from '../../src/hubextensions'; import * as tracingUtils from '../../src/utils'; +import { getDefaultBrowserClientOptions } from '../testutils'; beforeAll(() => { addExtensionMethods(); @@ -75,7 +74,7 @@ describe('callbacks', () => { beforeAll(() => { const options = getDefaultBrowserClientOptions({ tracesSampleRate: 1 }); - hub = new Hub(new BrowserClient(options, setupBrowserTransport(options).transport)); + hub = new Hub(new BrowserClient(options)); makeMain(hub); }); diff --git a/packages/tracing/test/errors.test.ts b/packages/tracing/test/errors.test.ts index 8dbcb2454539..6d3a3fa50768 100644 --- a/packages/tracing/test/errors.test.ts +++ b/packages/tracing/test/errors.test.ts @@ -1,11 +1,10 @@ import { BrowserClient } from '@sentry/browser'; -import { setupBrowserTransport } from '@sentry/browser/src/transports'; -import { NoopTransport } from '@sentry/core/src/transports/noop'; import { Hub, makeMain } from '@sentry/hub'; import { InstrumentHandlerCallback, InstrumentHandlerType } from '@sentry/utils'; import { registerErrorInstrumentation } from '../src/errors'; import { _addTracingExtensions } from '../src/hubextensions'; +import { getDefaultBrowserClientOptions } from './testutils'; const mockAddInstrumentationHandler = jest.fn(); let mockErrorCallback: InstrumentHandlerCallback = () => undefined; @@ -36,8 +35,8 @@ describe('registerErrorHandlers()', () => { let hub: Hub; beforeEach(() => { mockAddInstrumentationHandler.mockClear(); - const options = { tracesSampleRate: 1, transport: NoopTransport, integrations: [], stackParser: () => [] }; - hub = new Hub(new BrowserClient(options, setupBrowserTransport(options).transport)); + const options = getDefaultBrowserClientOptions(); + hub = new Hub(new BrowserClient(options)); makeMain(hub); }); diff --git a/packages/tracing/test/hub.test.ts b/packages/tracing/test/hub.test.ts index 6df8bad175c1..5fe7d84066ab 100644 --- a/packages/tracing/test/hub.test.ts +++ b/packages/tracing/test/hub.test.ts @@ -1,7 +1,5 @@ /* eslint-disable @typescript-eslint/unbound-method */ import { BrowserClient } from '@sentry/browser'; -import { setupBrowserTransport } from '@sentry/browser/src/transports'; -import { getDefaultBrowserClientOptions } from '@sentry/browser/test/unit/helper/browser-client-options'; import { Hub, makeMain } from '@sentry/hub'; import * as utilsModule from '@sentry/utils'; // for mocking import { logger } from '@sentry/utils'; @@ -10,7 +8,12 @@ import { BrowserTracing } from '../src/browser/browsertracing'; import { addExtensionMethods } from '../src/hubextensions'; import { Transaction } from '../src/transaction'; import { extractTraceparentData, TRACEPARENT_REGEXP } from '../src/utils'; -import { addDOMPropertiesToGlobal, getSymbolObjectKeyByName, testOnlyIfNodeVersionAtLeast } from './testutils'; +import { + addDOMPropertiesToGlobal, + getDefaultBrowserClientOptions, + getSymbolObjectKeyByName, + testOnlyIfNodeVersionAtLeast, +} from './testutils'; addExtensionMethods(); @@ -35,7 +38,7 @@ describe('Hub', () => { describe('getTransaction()', () => { it('should find a transaction which has been set on the scope if sampled = true', () => { const options = getDefaultBrowserClientOptions({ tracesSampleRate: 1 }); - const hub = new Hub(new BrowserClient(options, setupBrowserTransport(options).transport)); + const hub = new Hub(new BrowserClient(options)); makeMain(hub); const transaction = hub.startTransaction({ name: 'dogpark' }); transaction.sampled = true; @@ -49,7 +52,7 @@ describe('Hub', () => { it('should find a transaction which has been set on the scope if sampled = false', () => { const options = getDefaultBrowserClientOptions({ tracesSampleRate: 1 }); - const hub = new Hub(new BrowserClient(options, setupBrowserTransport(options).transport)); + const hub = new Hub(new BrowserClient(options)); makeMain(hub); const transaction = hub.startTransaction({ name: 'dogpark', sampled: false }); @@ -62,7 +65,7 @@ describe('Hub', () => { it("should not find an open transaction if it's not on the scope", () => { const options = getDefaultBrowserClientOptions({ tracesSampleRate: 1 }); - const hub = new Hub(new BrowserClient(options, setupBrowserTransport(options).transport)); + const hub = new Hub(new BrowserClient(options)); makeMain(hub); hub.startTransaction({ name: 'dogpark' }); @@ -75,7 +78,7 @@ describe('Hub', () => { it('should add transaction context data to default sample context', () => { const tracesSampler = jest.fn(); const options = getDefaultBrowserClientOptions({ tracesSampler }); - const hub = new Hub(new BrowserClient(options, setupBrowserTransport(options).transport)); + const hub = new Hub(new BrowserClient(options)); makeMain(hub); const transactionContext = { @@ -92,7 +95,7 @@ describe('Hub', () => { it("should add parent's sampling decision to default sample context", () => { const tracesSampler = jest.fn(); const options = getDefaultBrowserClientOptions({ tracesSampler }); - const hub = new Hub(new BrowserClient(options, setupBrowserTransport(options).transport)); + const hub = new Hub(new BrowserClient(options)); makeMain(hub); const parentSamplingDecsion = false; @@ -112,7 +115,7 @@ describe('Hub', () => { it('should set sampled = false when tracing is disabled', () => { const options = getDefaultBrowserClientOptions({}); // neither tracesSampleRate nor tracesSampler is defined -> tracing disabled - const hub = new Hub(new BrowserClient(options, setupBrowserTransport(options).transport)); + const hub = new Hub(new BrowserClient(options)); makeMain(hub); const transaction = hub.startTransaction({ name: 'dogpark' }); @@ -121,7 +124,7 @@ describe('Hub', () => { it('should set sampled = false if tracesSampleRate is 0', () => { const options = getDefaultBrowserClientOptions({ tracesSampleRate: 0 }); - const hub = new Hub(new BrowserClient(options, setupBrowserTransport(options).transport)); + const hub = new Hub(new BrowserClient(options)); makeMain(hub); const transaction = hub.startTransaction({ name: 'dogpark' }); @@ -130,7 +133,7 @@ describe('Hub', () => { it('should set sampled = true if tracesSampleRate is 1', () => { const options = getDefaultBrowserClientOptions({ tracesSampleRate: 1 }); - const hub = new Hub(new BrowserClient(options, setupBrowserTransport(options).transport)); + const hub = new Hub(new BrowserClient(options)); makeMain(hub); const transaction = hub.startTransaction({ name: 'dogpark' }); @@ -139,7 +142,7 @@ describe('Hub', () => { it('should set sampled = true if tracesSampleRate is 1 (without global hub)', () => { const options = getDefaultBrowserClientOptions({ tracesSampleRate: 1 }); - const hub = new Hub(new BrowserClient(options, setupBrowserTransport(options).transport)); + const hub = new Hub(new BrowserClient(options)); const transaction = hub.startTransaction({ name: 'dogpark' }); expect(transaction.sampled).toBe(true); @@ -148,7 +151,7 @@ describe('Hub', () => { it("should call tracesSampler if it's defined", () => { const tracesSampler = jest.fn(); const options = getDefaultBrowserClientOptions({ tracesSampler }); - const hub = new Hub(new BrowserClient(options, setupBrowserTransport(options).transport)); + const hub = new Hub(new BrowserClient(options)); makeMain(hub); hub.startTransaction({ name: 'dogpark' }); @@ -158,7 +161,7 @@ describe('Hub', () => { it('should set sampled = false if tracesSampler returns 0', () => { const tracesSampler = jest.fn().mockReturnValue(0); const options = getDefaultBrowserClientOptions({ tracesSampler }); - const hub = new Hub(new BrowserClient(options, setupBrowserTransport(options).transport)); + const hub = new Hub(new BrowserClient(options)); makeMain(hub); const transaction = hub.startTransaction({ name: 'dogpark' }); @@ -169,7 +172,7 @@ describe('Hub', () => { it('should set sampled = true if tracesSampler returns 1', () => { const tracesSampler = jest.fn().mockReturnValue(1); const options = getDefaultBrowserClientOptions({ tracesSampler }); - const hub = new Hub(new BrowserClient(options, setupBrowserTransport(options).transport)); + const hub = new Hub(new BrowserClient(options)); makeMain(hub); const transaction = hub.startTransaction({ name: 'dogpark' }); @@ -180,7 +183,7 @@ describe('Hub', () => { it('should set sampled = true if tracesSampler returns 1 (without global hub)', () => { const tracesSampler = jest.fn().mockReturnValue(1); const options = getDefaultBrowserClientOptions({ tracesSampler }); - const hub = new Hub(new BrowserClient(options, setupBrowserTransport(options).transport)); + const hub = new Hub(new BrowserClient(options)); const transaction = hub.startTransaction({ name: 'dogpark' }); expect(tracesSampler).toHaveBeenCalled(); @@ -191,7 +194,7 @@ describe('Hub', () => { // so that the decision otherwise would be false const tracesSampler = jest.fn().mockReturnValue(0); const options = getDefaultBrowserClientOptions({ tracesSampler }); - const hub = new Hub(new BrowserClient(options, setupBrowserTransport(options).transport)); + const hub = new Hub(new BrowserClient(options)); makeMain(hub); const transaction = hub.startTransaction({ name: 'dogpark', sampled: true }); @@ -202,7 +205,7 @@ describe('Hub', () => { // so that the decision otherwise would be true const tracesSampler = jest.fn().mockReturnValue(1); const options = getDefaultBrowserClientOptions({ tracesSampler }); - const hub = new Hub(new BrowserClient(options, setupBrowserTransport(options).transport)); + const hub = new Hub(new BrowserClient(options)); makeMain(hub); const transaction = hub.startTransaction({ name: 'dogpark', sampled: false }); @@ -213,7 +216,7 @@ describe('Hub', () => { // make the two options do opposite things to prove precedence const tracesSampler = jest.fn().mockReturnValue(true); const options = getDefaultBrowserClientOptions({ tracesSampleRate: 0, tracesSampler }); - const hub = new Hub(new BrowserClient(options, setupBrowserTransport(options).transport)); + const hub = new Hub(new BrowserClient(options)); makeMain(hub); const transaction = hub.startTransaction({ name: 'dogpark' }); @@ -224,7 +227,7 @@ describe('Hub', () => { it('should tolerate tracesSampler returning a boolean', () => { const tracesSampler = jest.fn().mockReturnValue(true); const options = getDefaultBrowserClientOptions({ tracesSampler }); - const hub = new Hub(new BrowserClient(options, setupBrowserTransport(options).transport)); + const hub = new Hub(new BrowserClient(options)); makeMain(hub); const transaction = hub.startTransaction({ name: 'dogpark' }); @@ -235,7 +238,7 @@ describe('Hub', () => { it('should record sampling method when sampling decision is explicitly set', () => { const tracesSampler = jest.fn().mockReturnValue(0.1121); const options = getDefaultBrowserClientOptions({ tracesSampler }); - const hub = new Hub(new BrowserClient(options, setupBrowserTransport(options).transport)); + const hub = new Hub(new BrowserClient(options)); makeMain(hub); hub.startTransaction({ name: 'dogpark', sampled: true }); @@ -247,7 +250,7 @@ describe('Hub', () => { it('should record sampling method and rate when sampling decision comes from tracesSampler', () => { const tracesSampler = jest.fn().mockReturnValue(0.1121); const options = getDefaultBrowserClientOptions({ tracesSampler }); - const hub = new Hub(new BrowserClient(options, setupBrowserTransport(options).transport)); + const hub = new Hub(new BrowserClient(options)); makeMain(hub); hub.startTransaction({ name: 'dogpark' }); @@ -258,7 +261,7 @@ describe('Hub', () => { it('should record sampling method when sampling decision is inherited', () => { const options = getDefaultBrowserClientOptions({ tracesSampleRate: 0.1121 }); - const hub = new Hub(new BrowserClient(options, setupBrowserTransport(options).transport)); + const hub = new Hub(new BrowserClient(options)); makeMain(hub); hub.startTransaction({ name: 'dogpark', parentSampled: true }); @@ -269,7 +272,7 @@ describe('Hub', () => { it('should record sampling method and rate when sampling decision comes from traceSampleRate', () => { const options = getDefaultBrowserClientOptions({ tracesSampleRate: 0.1121 }); - const hub = new Hub(new BrowserClient(options, setupBrowserTransport(options).transport)); + const hub = new Hub(new BrowserClient(options)); makeMain(hub); hub.startTransaction({ name: 'dogpark' }); @@ -282,7 +285,7 @@ describe('Hub', () => { describe('isValidSampleRate()', () => { it("should reject tracesSampleRates which aren't numbers or booleans", () => { const options = getDefaultBrowserClientOptions({ tracesSampleRate: 'dogs!' as any }); - const hub = new Hub(new BrowserClient(options, setupBrowserTransport(options).transport)); + const hub = new Hub(new BrowserClient(options)); makeMain(hub); hub.startTransaction({ name: 'dogpark' }); @@ -291,7 +294,7 @@ describe('Hub', () => { it('should reject tracesSampleRates which are NaN', () => { const options = getDefaultBrowserClientOptions({ tracesSampleRate: 'dogs!' as any }); - const hub = new Hub(new BrowserClient(options, setupBrowserTransport(options).transport)); + const hub = new Hub(new BrowserClient(options)); makeMain(hub); hub.startTransaction({ name: 'dogpark' }); @@ -301,7 +304,7 @@ describe('Hub', () => { // the rate might be a boolean, but for our purposes, false is equivalent to 0 and true is equivalent to 1 it('should reject tracesSampleRates less than 0', () => { const options = getDefaultBrowserClientOptions({ tracesSampleRate: -26 }); - const hub = new Hub(new BrowserClient(options, setupBrowserTransport(options).transport)); + const hub = new Hub(new BrowserClient(options)); makeMain(hub); hub.startTransaction({ name: 'dogpark' }); @@ -311,7 +314,7 @@ describe('Hub', () => { // the rate might be a boolean, but for our purposes, false is equivalent to 0 and true is equivalent to 1 it('should reject tracesSampleRates greater than 1', () => { const options = getDefaultBrowserClientOptions({ tracesSampleRate: 26 }); - const hub = new Hub(new BrowserClient(options, setupBrowserTransport(options).transport)); + const hub = new Hub(new BrowserClient(options)); makeMain(hub); hub.startTransaction({ name: 'dogpark' }); @@ -321,7 +324,7 @@ describe('Hub', () => { it("should reject tracesSampler return values which aren't numbers or booleans", () => { const tracesSampler = jest.fn().mockReturnValue('dogs!'); const options = getDefaultBrowserClientOptions({ tracesSampler }); - const hub = new Hub(new BrowserClient(options, setupBrowserTransport(options).transport)); + const hub = new Hub(new BrowserClient(options)); makeMain(hub); hub.startTransaction({ name: 'dogpark' }); @@ -331,7 +334,7 @@ describe('Hub', () => { it('should reject tracesSampler return values which are NaN', () => { const tracesSampler = jest.fn().mockReturnValue(NaN); const options = getDefaultBrowserClientOptions({ tracesSampler }); - const hub = new Hub(new BrowserClient(options, setupBrowserTransport(options).transport)); + const hub = new Hub(new BrowserClient(options)); makeMain(hub); hub.startTransaction({ name: 'dogpark' }); @@ -342,7 +345,7 @@ describe('Hub', () => { it('should reject tracesSampler return values less than 0', () => { const tracesSampler = jest.fn().mockReturnValue(-12); const options = getDefaultBrowserClientOptions({ tracesSampler }); - const hub = new Hub(new BrowserClient(options, setupBrowserTransport(options).transport)); + const hub = new Hub(new BrowserClient(options)); makeMain(hub); hub.startTransaction({ name: 'dogpark' }); @@ -353,7 +356,7 @@ describe('Hub', () => { it('should reject tracesSampler return values greater than 1', () => { const tracesSampler = jest.fn().mockReturnValue(31); const options = getDefaultBrowserClientOptions({ tracesSampler }); - const hub = new Hub(new BrowserClient(options, setupBrowserTransport(options).transport)); + const hub = new Hub(new BrowserClient(options)); makeMain(hub); hub.startTransaction({ name: 'dogpark' }); @@ -363,7 +366,7 @@ describe('Hub', () => { it('should drop transactions with sampled = false', () => { const options = getDefaultBrowserClientOptions({ tracesSampleRate: 0 }); - const client = new BrowserClient(options, setupBrowserTransport(options).transport); + const client = new BrowserClient(options); jest.spyOn(client, 'captureEvent'); const hub = new Hub(client); @@ -381,7 +384,7 @@ describe('Hub', () => { describe('sampling inheritance', () => { it('should propagate sampling decision to child spans', () => { const options = getDefaultBrowserClientOptions({ tracesSampleRate: Math.random() }); - const hub = new Hub(new BrowserClient(options, setupBrowserTransport(options).transport)); + const hub = new Hub(new BrowserClient(options)); makeMain(hub); const transaction = hub.startTransaction({ name: 'dogpark' }); const child = transaction.startChild({ op: 'ball.chase' }); @@ -398,7 +401,7 @@ describe('Hub', () => { tracesSampleRate: 1, integrations: [new BrowserTracing()], }); - const hub = new Hub(new BrowserClient(options, setupBrowserTransport(options).transport)); + const hub = new Hub(new BrowserClient(options)); makeMain(hub); const transaction = hub.startTransaction({ name: 'dogpark' }); @@ -439,7 +442,7 @@ describe('Hub', () => { tracesSampleRate: 1, integrations: [new BrowserTracing()], }); - const hub = new Hub(new BrowserClient(options, setupBrowserTransport(options).transport)); + const hub = new Hub(new BrowserClient(options)); makeMain(hub); const transaction = hub.startTransaction({ name: 'dogpark', sampled: false }); @@ -485,7 +488,7 @@ describe('Hub', () => { // tracesSampleRate mathRandom.mockReturnValueOnce(1); const options = getDefaultBrowserClientOptions({ tracesSampleRate: 0.5 }); - const hub = new Hub(new BrowserClient(options, setupBrowserTransport(options).transport)); + const hub = new Hub(new BrowserClient(options)); makeMain(hub); const parentSamplingDecsion = true; @@ -502,7 +505,7 @@ describe('Hub', () => { const options = getDefaultBrowserClientOptions({ tracesSampleRate: 1 }); // tracesSampleRate = 1 means every transaction should end up with sampled = true, so make parent's decision the // opposite to prove that inheritance takes precedence over tracesSampleRate - const hub = new Hub(new BrowserClient(options, setupBrowserTransport(options).transport)); + const hub = new Hub(new BrowserClient(options)); makeMain(hub); const parentSamplingDecsion = false; @@ -522,7 +525,7 @@ describe('Hub', () => { const parentSamplingDecsion = false; const options = getDefaultBrowserClientOptions({ tracesSampler }); - const hub = new Hub(new BrowserClient(options, setupBrowserTransport(options).transport)); + const hub = new Hub(new BrowserClient(options)); makeMain(hub); const transaction = hub.startTransaction({ @@ -541,7 +544,7 @@ describe('Hub', () => { const parentSamplingDecsion = true; const options = getDefaultBrowserClientOptions({ tracesSampler }); - const hub = new Hub(new BrowserClient(options, setupBrowserTransport(options).transport)); + const hub = new Hub(new BrowserClient(options)); makeMain(hub); const transaction = hub.startTransaction({ diff --git a/packages/tracing/test/idletransaction.test.ts b/packages/tracing/test/idletransaction.test.ts index b040f4adbd9b..e6035ad491a1 100644 --- a/packages/tracing/test/idletransaction.test.ts +++ b/packages/tracing/test/idletransaction.test.ts @@ -1,5 +1,4 @@ -import { BrowserClient, Transports } from '@sentry/browser'; -import { getDefaultBrowserClientOptions } from '@sentry/browser/test/unit/helper/browser-client-options'; +import { BrowserClient } from '@sentry/browser'; import { Hub } from '@sentry/hub'; import { @@ -9,16 +8,13 @@ import { IdleTransactionSpanRecorder, } from '../src/idletransaction'; import { Span } from '../src/span'; - -// @ts-ignore It's okay that we're not implementing the methods of the abstract `BaseTransport` class, because it's not -// what we're testing here -class SimpleTransport extends Transports.BaseTransport {} +import { getDefaultBrowserClientOptions } from './testutils'; const dsn = 'https://123@sentry.io/42'; let hub: Hub; beforeEach(() => { - const options = getDefaultBrowserClientOptions({ dsn, tracesSampleRate: 1, transport: SimpleTransport }); - hub = new Hub(new BrowserClient(options, new SimpleTransport({ dsn }))); + const options = getDefaultBrowserClientOptions({ dsn, tracesSampleRate: 1 }); + hub = new Hub(new BrowserClient(options)); }); describe('IdleTransaction', () => { @@ -167,18 +163,19 @@ describe('IdleTransaction', () => { } }); - it('should record dropped transactions', async () => { - const transaction = new IdleTransaction({ name: 'foo', startTimestamp: 1234, sampled: false }, hub, 1000); + // TODO(v7): Add with client reports + // it('should record dropped transactions', async () => { + // const transaction = new IdleTransaction({ name: 'foo', startTimestamp: 1234, sampled: false }, hub, 1000); - const transport = hub.getClient()!.getTransport!(); + // const transport = hub.getClient()!.getTransport!(); - const spy = jest.spyOn(transport, 'recordLostEvent'); + // const spy = jest.spyOn(transport, 'recordLostEvent'); - transaction.initSpanRecorder(10); - transaction.finish(transaction.startTimestamp + 10); + // transaction.initSpanRecorder(10); + // transaction.finish(transaction.startTimestamp + 10); - expect(spy).toHaveBeenCalledWith('sample_rate', 'transaction'); - }); + // expect(spy).toHaveBeenCalledWith('sample_rate', 'transaction'); + // }); describe('_initTimeout', () => { it('finishes if no activities are added to the transaction', () => { diff --git a/packages/tracing/test/span.test.ts b/packages/tracing/test/span.test.ts index 13f47c51ae7b..27df400ffc9e 100644 --- a/packages/tracing/test/span.test.ts +++ b/packages/tracing/test/span.test.ts @@ -1,10 +1,9 @@ import { BrowserClient } from '@sentry/browser'; -import { setupBrowserTransport } from '@sentry/browser/src/transports'; -import { getDefaultBrowserClientOptions } from '@sentry/browser/test/unit/helper/browser-client-options'; import { Hub, makeMain, Scope } from '@sentry/hub'; import { Span, Transaction } from '../src'; import { TRACEPARENT_REGEXP } from '../src/utils'; +import { getDefaultBrowserClientOptions } from './testutils'; describe('Span', () => { let hub: Hub; @@ -12,7 +11,7 @@ describe('Span', () => { beforeEach(() => { const myScope = new Scope(); const options = getDefaultBrowserClientOptions({ tracesSampleRate: 1 }); - hub = new Hub(new BrowserClient(options, setupBrowserTransport(options).transport), myScope); + hub = new Hub(new BrowserClient(options), myScope); makeMain(hub); }); @@ -223,7 +222,7 @@ describe('Span', () => { _experiments: { maxSpans: 3 }, tracesSampleRate: 1, }); - const _hub = new Hub(new BrowserClient(options, setupBrowserTransport(options).transport)); + const _hub = new Hub(new BrowserClient(options)); const spy = jest.spyOn(_hub as any, 'captureEvent') as any; const transaction = _hub.startTransaction({ name: 'test' }); for (let i = 0; i < 10; i++) { @@ -238,7 +237,7 @@ describe('Span', () => { const options = getDefaultBrowserClientOptions({ tracesSampleRate: 1, }); - const _hub = new Hub(new BrowserClient(options, setupBrowserTransport(options).transport)); + const _hub = new Hub(new BrowserClient(options)); const spy = jest.spyOn(_hub as any, 'captureEvent') as any; const transaction = _hub.startTransaction({ name: 'test', sampled: false }); for (let i = 0; i < 10; i++) { diff --git a/packages/tracing/test/testutils.ts b/packages/tracing/test/testutils.ts index a8f42f84b9b8..2c0aef189b57 100644 --- a/packages/tracing/test/testutils.ts +++ b/packages/tracing/test/testutils.ts @@ -1,4 +1,6 @@ -import { getGlobalObject } from '@sentry/utils'; +import { createTransport } from '@sentry/browser'; +import { ClientOptions } from '@sentry/types'; +import { getGlobalObject, resolvedSyncPromise } from '@sentry/utils'; import { JSDOM } from 'jsdom'; /** @@ -56,3 +58,12 @@ export const testOnlyIfNodeVersionAtLeast = (minVersion: number): jest.It => { return it; }; + +export function getDefaultBrowserClientOptions(options: Partial = {}): ClientOptions { + return { + integrations: [], + transport: () => createTransport({}, _ => resolvedSyncPromise({ statusCode: 200 })), + stackParser: () => [], + ...options, + }; +} diff --git a/packages/types/src/client.ts b/packages/types/src/client.ts index f0cdc8b3a988..b1cadf971197 100644 --- a/packages/types/src/client.ts +++ b/packages/types/src/client.ts @@ -3,9 +3,9 @@ import { Event, EventHint } from './event'; import { Integration, IntegrationClass } from './integration'; import { ClientOptions } from './options'; import { Scope } from './scope'; -import { Session } from './session'; +import { Session, SessionAggregates } from './session'; import { Severity, SeverityLevel } from './severity'; -import { Transport } from './transport'; +import { NewTransport } from './transport'; /** * User-Facing Sentry SDK Client. @@ -72,7 +72,7 @@ export interface Client { * * @returns The transport. */ - getTransport?(): Transport; + getTransport(): NewTransport | undefined; /** * Flush the event queue and set the client to `enabled = false`. See {@link Client.flush}. @@ -116,5 +116,5 @@ export interface Client { sendEvent(event: Event): void; /** Submits the session to Sentry */ - sendSession(session: Session): void; + sendSession(session: Session | SessionAggregates): void; } diff --git a/packages/types/src/index.ts b/packages/types/src/index.ts index 5fe4ced6375b..3c066e3cc0bf 100644 --- a/packages/types/src/index.ts +++ b/packages/types/src/index.ts @@ -62,6 +62,18 @@ export type { TransactionSamplingMethod, } from './transaction'; export type { Thread } from './thread'; -export type { Outcome, Transport, TransportOptions, TransportClass } from './transport'; +export type { + Outcome, + Transport, + TransportOptions, + TransportCategory, + TransportRequest, + TransportMakeRequestResponse, + TransportResponse, + InternalBaseTransportOptions, + BaseTransportOptions, + NewTransport, + TransportRequestExecutor, +} from './transport'; export type { User, UserFeedback } from './user'; export type { WrappedFunction } from './wrappedfunction'; diff --git a/packages/types/src/options.ts b/packages/types/src/options.ts index 8bd273752700..61188d6c099f 100644 --- a/packages/types/src/options.ts +++ b/packages/types/src/options.ts @@ -5,9 +5,9 @@ import { CaptureContext } from './scope'; import { SdkMetadata } from './sdkmetadata'; import { StackLineParser, StackParser } from './stacktrace'; import { SamplingContext } from './transaction'; -import { Transport, TransportClass, TransportOptions } from './transport'; +import { BaseTransportOptions, NewTransport } from './transport'; -export interface ClientOptions { +export interface ClientOptions { /** * Enable debug functionality in the SDK itself */ @@ -61,7 +61,7 @@ export interface ClientOptions { /** * Transport object that should be used to send events to Sentry */ - transport: TransportClass; + transport: (transportOptions: TO) => NewTransport; /** * A stack parser implementation @@ -72,7 +72,7 @@ export interface ClientOptions { /** * Options for the default transport that the SDK uses. */ - transportOptions?: TransportOptions; + transportOptions?: Partial; /** * Sample rate to determine trace sampling. @@ -202,7 +202,8 @@ export interface ClientOptions { } /** Base configuration options for every SDK. */ -export interface Options extends Omit, 'integrations' | 'transport' | 'stackParser'> { +export interface Options + extends Omit>, 'integrations' | 'transport' | 'stackParser'> { /** * If this is set to false, default integrations will not be added, otherwise this will internally be set to the * recommended default integrations. @@ -220,7 +221,7 @@ export interface Options extends Omit, 'integrations' | ' /** * Transport object that should be used to send events to Sentry */ - transport?: TransportClass; + transport?: (transportOptions: TO) => NewTransport; /** * A stack parser implementation or an array of stack line parsers diff --git a/packages/types/src/session.ts b/packages/types/src/session.ts index 552dda002531..237e4c41808d 100644 --- a/packages/types/src/session.ts +++ b/packages/types/src/session.ts @@ -74,9 +74,6 @@ export interface SessionFlusherLike { */ incrementSessionStatusCount(): void; - /** Submits the aggregates request mode sessions to Sentry */ - sendSessionAggregates(sessionAggregates: SessionAggregates): void; - /** Empties Aggregate Buckets and Sends them to Transport Buffer */ flush(): void; diff --git a/packages/types/src/transport.ts b/packages/types/src/transport.ts index a1ba2983b1a3..1250d4cb01d5 100644 --- a/packages/types/src/transport.ts +++ b/packages/types/src/transport.ts @@ -1,5 +1,7 @@ import { DsnLike } from './dsn'; +import { Envelope } from './envelope'; import { Event } from './event'; +import { EventStatus } from './eventstatus'; import { SentryRequestType } from './request'; import { Response } from './response'; import { SdkMetadata } from './sdkmetadata'; @@ -13,6 +15,46 @@ export type Outcome = | 'ratelimit_backoff' | 'sample_rate'; +export type TransportCategory = 'error' | 'transaction' | 'attachment' | 'session'; + +export type TransportRequest = { + body: string; + category: TransportCategory; +}; + +export type TransportMakeRequestResponse = { + body?: string; + headers?: { + [key: string]: string | null; + 'x-sentry-rate-limits': string | null; + 'retry-after': string | null; + }; + reason?: string; + statusCode: number; +}; + +export type TransportResponse = { + status: EventStatus; + reason?: string; +}; + +export interface InternalBaseTransportOptions { + bufferSize?: number; +} +export interface BaseTransportOptions extends InternalBaseTransportOptions { + // url to send the event + // transport does not care about dsn specific - client should take care of + // parsing and figuring that out + url: string; +} + +export interface NewTransport { + send(request: Envelope): PromiseLike; + flush(timeout?: number): PromiseLike; +} + +export type TransportRequestExecutor = (request: TransportRequest) => PromiseLike; + /** Transport used sending data to Sentry */ export interface Transport { /** From 4a35b8de1cd5b0b9ff872562eb9180e4d6549d33 Mon Sep 17 00:00:00 2001 From: Katie Byers Date: Tue, 26 Apr 2022 01:09:33 -0700 Subject: [PATCH 072/204] chore(dev): Update `caniuse-lite` (#4975) This updates `caniuse-lite`, in order to silence a warning during build. --- yarn.lock | 17 +---------------- 1 file changed, 1 insertion(+), 16 deletions(-) diff --git a/yarn.lock b/yarn.lock index ca4e027ca772..15c5f85afc39 100644 --- a/yarn.lock +++ b/yarn.lock @@ -8490,26 +8490,11 @@ caniuse-api@^3.0.0: lodash.memoize "^4.1.2" lodash.uniq "^4.5.0" -caniuse-lite@^1.0.0, caniuse-lite@^1.0.30000844, caniuse-lite@^1.0.30001173, caniuse-lite@^1.0.30001179, caniuse-lite@^1.0.30001254: - version "1.0.30001257" - resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001257.tgz#150aaf649a48bee531104cfeda57f92ce587f6e5" - integrity sha512-JN49KplOgHSXpIsVSF+LUyhD8PUp6xPpAXeRrrcBh4KBeP7W864jHn6RvzJgDlrReyeVjMFJL3PLpPvKIxlIHA== - -caniuse-lite@^1.0.30001032, caniuse-lite@^1.0.30001109, caniuse-lite@^1.0.30001317: +caniuse-lite@^1.0.0, caniuse-lite@^1.0.30000844, caniuse-lite@^1.0.30001032, caniuse-lite@^1.0.30001109, caniuse-lite@^1.0.30001173, caniuse-lite@^1.0.30001179, caniuse-lite@^1.0.30001254, caniuse-lite@^1.0.30001274, caniuse-lite@^1.0.30001280, caniuse-lite@^1.0.30001317: version "1.0.30001332" resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001332.tgz#39476d3aa8d83ea76359c70302eafdd4a1d727dd" integrity sha512-10T30NYOEQtN6C11YGg411yebhvpnC6Z102+B95eAsN0oB6KUs01ivE8u+G6FMIRtIrVlYXhL+LUwQ3/hXwDWw== -caniuse-lite@^1.0.30001274: - version "1.0.30001279" - resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001279.tgz#eb06818da481ef5096a3b3760f43e5382ed6b0ce" - integrity sha512-VfEHpzHEXj6/CxggTwSFoZBBYGQfQv9Cf42KPlO79sWXCD1QNKWKsKzFeWL7QpZHJQYAvocqV6Rty1yJMkqWLQ== - -caniuse-lite@^1.0.30001280: - version "1.0.30001286" - resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001286.tgz#3e9debad420419618cfdf52dc9b6572b28a8fff6" - integrity sha512-zaEMRH6xg8ESMi2eQ3R4eZ5qw/hJiVsO/HlLwniIwErij0JDr9P+8V4dtx1l+kLq6j3yy8l8W4fst1lBnat5wQ== - canonical-path@1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/canonical-path/-/canonical-path-1.0.0.tgz#fcb470c23958def85081856be7a86e904f180d1d" From 8d52e144d1ca7154121e0742b4d47d371e2855b1 Mon Sep 17 00:00:00 2001 From: Katie Byers Date: Tue, 26 Apr 2022 06:55:49 -0700 Subject: [PATCH 073/204] chore(dev): Update `ts-node` to 10.7 (#4976) This updates `ts-node` to latest, and adds a command to the Node 8 test setup to downgrade it again, for compatibility. (Unlike the other downgrading of packages for Node 8 and 10 tests, this has to happen outside of our `test.ts` script, because it's the thing that's running that script in the first place.) --- .github/workflows/build.yml | 4 ++- package.json | 2 +- yarn.lock | 67 +++++++++++++++++++++++++++++++++---- 3 files changed, 65 insertions(+), 8 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 0ff66932e93b..155126752160 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -254,7 +254,9 @@ jobs: - name: Run tests env: NODE_VERSION: ${{ matrix.node }} - run: yarn test-ci + run: | + [[ $NODE_VERSION == 8 ]] && yarn add --dev --ignore-engines --ignore-scripts --ignore-workspace-root-check ts-node@8.x + yarn test-ci - name: Compute test coverage uses: codecov/codecov-action@v1 diff --git a/package.json b/package.json index bf91547479aa..6edf51939fc1 100644 --- a/package.json +++ b/package.json @@ -89,7 +89,7 @@ "sinon": "^7.3.2", "size-limit": "^4.5.5", "ts-jest": "^27.1.4", - "ts-node": "^8.10.2", + "ts-node": "^10.7.0", "tslib": "^2.3.1", "typedoc": "^0.18.0", "typescript": "3.8.3" diff --git a/yarn.lock b/yarn.lock index 15c5f85afc39..2d280ea21e26 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2422,6 +2422,18 @@ exec-sh "^0.3.2" minimist "^1.2.0" +"@cspotcode/source-map-consumer@0.8.0": + version "0.8.0" + resolved "https://registry.yarnpkg.com/@cspotcode/source-map-consumer/-/source-map-consumer-0.8.0.tgz#33bf4b7b39c178821606f669bbc447a6a629786b" + integrity sha512-41qniHzTU8yAGbCp04ohlmSrZf8bkf/iJsl3V0dRGsQN/5GFfx+LbCSsCpp2gqrqjTVg/K6O8ycoV35JIwAzAg== + +"@cspotcode/source-map-support@0.7.0": + version "0.7.0" + resolved "https://registry.yarnpkg.com/@cspotcode/source-map-support/-/source-map-support-0.7.0.tgz#4789840aa859e46d2f3173727ab707c66bf344f5" + integrity sha512-X4xqRHqN8ACt2aHVe51OxeA2HjbcL4MqFqXkrmQszJ1NOUuUu5u6Vqx/0lZSVNku7velL5FC/s5uEAj1lsBMhA== + dependencies: + "@cspotcode/source-map-consumer" "0.8.0" + "@ember-data/rfc395-data@^0.0.4": version "0.0.4" resolved "https://registry.yarnpkg.com/@ember-data/rfc395-data/-/rfc395-data-0.0.4.tgz#ecb86efdf5d7733a76ff14ea651a1b0ed1f8a843" @@ -4402,6 +4414,26 @@ "@types/semver" "^7.3.9" ts-type "^2.1.4" +"@tsconfig/node10@^1.0.7": + version "1.0.8" + resolved "https://registry.yarnpkg.com/@tsconfig/node10/-/node10-1.0.8.tgz#c1e4e80d6f964fbecb3359c43bd48b40f7cadad9" + integrity sha512-6XFfSQmMgq0CFLY1MslA/CPUfhIL919M1rMsa5lP2P097N2Wd1sSX0tx1u4olM16fLNhtHZpRhedZJphNJqmZg== + +"@tsconfig/node12@^1.0.7": + version "1.0.9" + resolved "https://registry.yarnpkg.com/@tsconfig/node12/-/node12-1.0.9.tgz#62c1f6dee2ebd9aead80dc3afa56810e58e1a04c" + integrity sha512-/yBMcem+fbvhSREH+s14YJi18sp7J9jpuhYByADT2rypfajMZZN4WQ6zBGgBKp53NKmqI36wFYDb3yaMPurITw== + +"@tsconfig/node14@^1.0.0": + version "1.0.1" + resolved "https://registry.yarnpkg.com/@tsconfig/node14/-/node14-1.0.1.tgz#95f2d167ffb9b8d2068b0b235302fafd4df711f2" + integrity sha512-509r2+yARFfHHE7T6Puu2jjkoycftovhXRqW328PDXTVGKihlb1P8Z9mMZH04ebyajfRY7dedfGynlrFHJUQCg== + +"@tsconfig/node16@^1.0.2": + version "1.0.2" + resolved "https://registry.yarnpkg.com/@tsconfig/node16/-/node16-1.0.2.tgz#423c77877d0569db20e1fc80885ac4118314010e" + integrity sha512-eZxlbI8GZscaGS7kkc/trHTT5xgrjH3/1n2JDwusC9iahPKWMRvRjJSAN5mCXviuTGQ/lHnhvv8Q1YTpnfz9gA== + "@types/aria-query@^4.2.0": version "4.2.1" resolved "https://registry.yarnpkg.com/@types/aria-query/-/aria-query-4.2.1.tgz#78b5433344e2f92e8b306c06a5622c50c245bf6b" @@ -5681,6 +5713,11 @@ acorn-walk@^8.0.0: resolved "https://registry.yarnpkg.com/acorn-walk/-/acorn-walk-8.0.2.tgz#d4632bfc63fd93d0f15fd05ea0e984ffd3f5a8c3" integrity sha512-+bpA9MJsHdZ4bgfDcpk0ozQyhhVct7rzOmO0s1IIr0AGGgKBljss8n2zp11rRP2wid5VGeh04CgeKzgat5/25A== +acorn-walk@^8.1.1: + version "8.2.0" + resolved "https://registry.yarnpkg.com/acorn-walk/-/acorn-walk-8.2.0.tgz#741210f2e2426454508853a2f44d0ab83b7f69c1" + integrity sha512-k+iyHEuPgSw6SbuDpGQM+06HQUa04DZ3o+F6CSzXMvvI5KMvnaEqXe+YVe555R9nn6GPt404fos4wcgpw12SDA== + acorn@^6.0.5, acorn@^6.4.1: version "6.4.2" resolved "https://registry.yarnpkg.com/acorn/-/acorn-6.4.2.tgz#35866fd710528e92de10cf06016498e47e39e1e6" @@ -9527,6 +9564,11 @@ create-react-context@^0.2.2: fbjs "^0.8.0" gud "^1.0.0" +create-require@^1.1.0: + version "1.1.1" + resolved "https://registry.yarnpkg.com/create-require/-/create-require-1.1.1.tgz#c1d7e8f1e5f6cfc9ff65f9cd352d37348756c333" + integrity sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ== + cross-env@^7.0.3: version "7.0.3" resolved "https://registry.yarnpkg.com/cross-env/-/cross-env-7.0.3.tgz#865264b29677dc015ba8418918965dd232fc54cf" @@ -22668,7 +22710,7 @@ source-map-support@^0.4.15, source-map-support@^0.4.18: dependencies: source-map "^0.5.6" -source-map-support@^0.5.17, source-map-support@^0.5.5, source-map-support@~0.5.20: +source-map-support@^0.5.5, source-map-support@~0.5.20: version "0.5.21" resolved "https://registry.yarnpkg.com/source-map-support/-/source-map-support-0.5.21.tgz#04fe7c7f9e1ed2d662233c28cb2b35b9f63f6e4f" integrity sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w== @@ -24137,15 +24179,23 @@ ts-jest@^27.1.4: semver "7.x" yargs-parser "20.x" -ts-node@^8.10.2: - version "8.10.2" - resolved "https://registry.yarnpkg.com/ts-node/-/ts-node-8.10.2.tgz#eee03764633b1234ddd37f8db9ec10b75ec7fb8d" - integrity sha512-ISJJGgkIpDdBhWVu3jufsWpK3Rzo7bdiIXJjQc0ynKxVOVcg2oIrf2H2cejminGrptVc6q6/uynAHNCuWGbpVA== +ts-node@^10.7.0: + version "10.7.0" + resolved "https://registry.yarnpkg.com/ts-node/-/ts-node-10.7.0.tgz#35d503d0fab3e2baa672a0e94f4b40653c2463f5" + integrity sha512-TbIGS4xgJoX2i3do417KSaep1uRAW/Lu+WAL2doDHC0D6ummjirVOXU5/7aiZotbQ5p1Zp9tP7U6cYhA0O7M8A== dependencies: + "@cspotcode/source-map-support" "0.7.0" + "@tsconfig/node10" "^1.0.7" + "@tsconfig/node12" "^1.0.7" + "@tsconfig/node14" "^1.0.0" + "@tsconfig/node16" "^1.0.2" + acorn "^8.4.1" + acorn-walk "^8.1.1" arg "^4.1.0" + create-require "^1.1.0" diff "^4.0.1" make-error "^1.1.1" - source-map-support "^0.5.17" + v8-compile-cache-lib "^3.0.0" yn "3.1.1" ts-pnp@^1.1.6: @@ -24751,6 +24801,11 @@ uuid@^8.0.0, uuid@^8.3.0, uuid@^8.3.1, uuid@^8.3.2: resolved "https://registry.yarnpkg.com/uuid/-/uuid-8.3.2.tgz#80d5b5ced271bb9af6c445f21a1a04c606cefbe2" integrity sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg== +v8-compile-cache-lib@^3.0.0: + version "3.0.1" + resolved "https://registry.yarnpkg.com/v8-compile-cache-lib/-/v8-compile-cache-lib-3.0.1.tgz#6336e8d71965cb3d35a1bbb7868445a7c05264bf" + integrity sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg== + v8-compile-cache@^2.0.3: version "2.3.0" resolved "https://registry.yarnpkg.com/v8-compile-cache/-/v8-compile-cache-2.3.0.tgz#2de19618c66dc247dcfb6f99338035d8245a2cee" From 84555ac2db82b49b0ea94e44d5277ea955bd6025 Mon Sep 17 00:00:00 2001 From: asottile-sentry <103459774+asottile-sentry@users.noreply.github.com> Date: Tue, 26 Apr 2022 17:33:13 +0200 Subject: [PATCH 074/204] fix(build): Replace `git.io` links with redirect targets (#4986) see: https://github.blog/changelog/2022-04-25-git-io-deprecation/ --- .github/workflows/codeql-analysis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml index 00b74bf93f88..facd3fa52abf 100644 --- a/.github/workflows/codeql-analysis.yml +++ b/.github/workflows/codeql-analysis.yml @@ -53,7 +53,7 @@ jobs: uses: github/codeql-action/autobuild@v1 # ℹ️ Command-line programs to run using the OS shell. - # 📚 https://git.io/JvXDl + # 📚 https://docs.github.com/en/actions/reference/workflow-syntax-for-github-actions # ✏️ If the Autobuild fails above, remove it and uncomment the following three lines # and modify them (or add more) to build your code if your project From e6a51cb960f8f6de8d67658e989e6298f0841d95 Mon Sep 17 00:00:00 2001 From: Lukas Stracke Date: Tue, 26 Apr 2022 18:21:38 +0200 Subject: [PATCH 075/204] fix(tracing): Adjust `sideEffects` package.json entry for v7 (#4987) adjust the fix from #4955 which was merged into 6.19.7 for v7. Change the sideEffects paths in package.json of @sentry/tracing by renaming the `dist` dir to `cjs`. --- packages/tracing/package.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/tracing/package.json b/packages/tracing/package.json index f9465746e0e0..09bf285f90e4 100644 --- a/packages/tracing/package.json +++ b/packages/tracing/package.json @@ -58,9 +58,9 @@ "extends": "../../package.json" }, "sideEffects": [ - "./dist/index.js", + "./cjs/index.js", "./esm/index.js", - "./build/npm/dist/index.js", + "./build/npm/cjs/index.js", "./build/npm/esm/index.js", "./src/index.ts" ] From a8591eafb6807f603cf3a8322dfee4445d1ec9b1 Mon Sep 17 00:00:00 2001 From: Abhijeet Prasad Date: Tue, 26 Apr 2022 12:41:44 -0400 Subject: [PATCH 076/204] ref: Delete old transports (#4967) Delete old transports as we've switched entirely to new v7 transports. --- packages/browser/src/sdk.ts | 4 +- packages/browser/src/transports/base.ts | 212 ----- packages/browser/src/transports/fetch.ts | 107 +-- packages/browser/src/transports/index.ts | 8 +- packages/browser/src/transports/new-fetch.ts | 39 - packages/browser/src/transports/new-xhr.ts | 55 -- packages/browser/src/transports/xhr.ts | 112 +-- .../browser/test/unit/transports/base.test.ts | 133 --- .../test/unit/transports/fetch.test.ts | 585 ++---------- .../test/unit/transports/new-fetch.test.ts | 98 -- .../test/unit/transports/new-xhr.test.ts | 109 --- .../browser/test/unit/transports/xhr.test.ts | 506 ++-------- packages/core/src/baseclient.ts | 15 +- packages/core/src/index.ts | 1 - packages/core/src/transports/base.ts | 6 +- packages/core/src/transports/noop.ts | 22 - .../core/test/lib/transports/base.test.ts | 4 +- packages/core/test/mocks/client.ts | 4 + .../new-transport/scenario.ts | 17 - .../startTransaction/new-transport/test.ts | 12 - packages/node/src/transports/base/index.ts | 270 ------ .../src/transports/{base => }/http-module.ts | 0 packages/node/src/transports/http.ts | 154 ++- packages/node/src/transports/https.ts | 32 - packages/node/src/transports/index.ts | 7 +- packages/node/src/transports/new.ts | 141 --- .../node/test/transports/custom/index.test.ts | 24 - .../node/test/transports/custom/transports.ts | 11 - packages/node/test/transports/http.test.ts | 895 ++++++------------ packages/node/test/transports/https.test.ts | 588 +++++++----- .../node/test/transports/new/http.test.ts | 347 ------- .../node/test/transports/new/https.test.ts | 397 -------- .../transports/{new => }/test-server-certs.ts | 0 packages/types/src/client.ts | 4 +- packages/types/src/index.ts | 2 - packages/types/src/options.ts | 6 +- packages/types/src/transport.ts | 68 +- 37 files changed, 999 insertions(+), 3996 deletions(-) delete mode 100644 packages/browser/src/transports/base.ts delete mode 100644 packages/browser/src/transports/new-fetch.ts delete mode 100644 packages/browser/src/transports/new-xhr.ts delete mode 100644 packages/browser/test/unit/transports/base.test.ts delete mode 100644 packages/browser/test/unit/transports/new-fetch.test.ts delete mode 100644 packages/browser/test/unit/transports/new-xhr.test.ts delete mode 100644 packages/core/src/transports/noop.ts delete mode 100644 packages/node-integration-tests/suites/public-api/startTransaction/new-transport/scenario.ts delete mode 100644 packages/node-integration-tests/suites/public-api/startTransaction/new-transport/test.ts delete mode 100644 packages/node/src/transports/base/index.ts rename packages/node/src/transports/{base => }/http-module.ts (100%) delete mode 100644 packages/node/src/transports/https.ts delete mode 100644 packages/node/src/transports/new.ts delete mode 100644 packages/node/test/transports/custom/index.test.ts delete mode 100644 packages/node/test/transports/custom/transports.ts delete mode 100644 packages/node/test/transports/new/http.test.ts delete mode 100644 packages/node/test/transports/new/https.test.ts rename packages/node/test/transports/{new => }/test-server-certs.ts (100%) diff --git a/packages/browser/src/sdk.ts b/packages/browser/src/sdk.ts index 9b138864515a..184893f15cf0 100644 --- a/packages/browser/src/sdk.ts +++ b/packages/browser/src/sdk.ts @@ -14,7 +14,7 @@ import { IS_DEBUG_BUILD } from './flags'; import { ReportDialogOptions, wrap as internalWrap } from './helpers'; import { Breadcrumbs, Dedupe, GlobalHandlers, LinkedErrors, TryCatch, UserAgent } from './integrations'; import { defaultStackParsers } from './stack-parsers'; -import { makeNewFetchTransport, makeNewXHRTransport } from './transports'; +import { makeFetchTransport, makeXHRTransport } from './transports'; export const defaultIntegrations = [ new CoreIntegrations.InboundFilters(), @@ -106,7 +106,7 @@ export function init(options: BrowserOptions = {}): void { ...options, stackParser: stackParserFromOptions(options.stackParser || defaultStackParsers), integrations: getIntegrationsToSetup(options), - transport: options.transport || (supportsFetch() ? makeNewFetchTransport : makeNewXHRTransport), + transport: options.transport || (supportsFetch() ? makeFetchTransport : makeXHRTransport), }; initAndBind(BrowserClient, clientOptions); diff --git a/packages/browser/src/transports/base.ts b/packages/browser/src/transports/base.ts deleted file mode 100644 index c7785b4eb7c5..000000000000 --- a/packages/browser/src/transports/base.ts +++ /dev/null @@ -1,212 +0,0 @@ -import { - APIDetails, - eventToSentryRequest, - getEnvelopeEndpointWithUrlEncodedAuth, - getStoreEndpointWithUrlEncodedAuth, - initAPIDetails, - sessionToSentryRequest, -} from '@sentry/core'; -import { - ClientReport, - Event, - Outcome, - Response as SentryResponse, - SentryRequest, - SentryRequestType, - Session, - Transport, - TransportOptions, -} from '@sentry/types'; -import { - createClientReportEnvelope, - disabledUntil, - dsnToString, - eventStatusFromHttpCode, - getGlobalObject, - isRateLimited, - logger, - makePromiseBuffer, - PromiseBuffer, - RateLimits, - serializeEnvelope, - updateRateLimits, -} from '@sentry/utils'; - -import { IS_DEBUG_BUILD } from '../flags'; -import { sendReport } from './utils'; - -function requestTypeToCategory(ty: SentryRequestType): string { - const tyStr = ty as string; - return tyStr === 'event' ? 'error' : tyStr; -} - -const global = getGlobalObject(); - -/** Base Transport class implementation */ -export abstract class BaseTransport implements Transport { - /** - * @deprecated - */ - public url: string; - - /** Helper to get Sentry API endpoints. */ - protected readonly _api: APIDetails; - - /** A simple buffer holding all requests. */ - protected readonly _buffer: PromiseBuffer = makePromiseBuffer(30); - - /** Locks transport after receiving rate limits in a response */ - protected _rateLimits: RateLimits = {}; - - protected _outcomes: { [key: string]: number } = {}; - - public constructor(public options: TransportOptions) { - this._api = initAPIDetails(options.dsn, options._metadata, options.tunnel); - // eslint-disable-next-line deprecation/deprecation - this.url = getStoreEndpointWithUrlEncodedAuth(this._api.dsn); - - if (this.options.sendClientReports && global.document) { - global.document.addEventListener('visibilitychange', () => { - if (global.document.visibilityState === 'hidden') { - this._flushOutcomes(); - } - }); - } - } - - /** - * @inheritDoc - */ - public sendEvent(event: Event): PromiseLike { - return this._sendRequest(eventToSentryRequest(event, this._api), event); - } - - /** - * @inheritDoc - */ - public sendSession(session: Session): PromiseLike { - return this._sendRequest(sessionToSentryRequest(session, this._api), session); - } - - /** - * @inheritDoc - */ - public close(timeout?: number): PromiseLike { - return this._buffer.drain(timeout); - } - - /** - * @inheritDoc - */ - public recordLostEvent(reason: Outcome, category: SentryRequestType): void { - if (!this.options.sendClientReports) { - return; - } - // We want to track each category (event, transaction, session) separately - // but still keep the distinction between different type of outcomes. - // We could use nested maps, but it's much easier to read and type this way. - // A correct type for map-based implementation if we want to go that route - // would be `Partial>>>` - const key = `${requestTypeToCategory(category)}:${reason}`; - IS_DEBUG_BUILD && logger.log(`Adding outcome: ${key}`); - this._outcomes[key] = (this._outcomes[key] ?? 0) + 1; - } - - /** - * Send outcomes as an envelope - */ - protected _flushOutcomes(): void { - if (!this.options.sendClientReports) { - return; - } - - const outcomes = this._outcomes; - this._outcomes = {}; - - // Nothing to send - if (!Object.keys(outcomes).length) { - IS_DEBUG_BUILD && logger.log('No outcomes to flush'); - return; - } - - IS_DEBUG_BUILD && logger.log(`Flushing outcomes:\n${JSON.stringify(outcomes, null, 2)}`); - - const url = getEnvelopeEndpointWithUrlEncodedAuth(this._api.dsn, this._api.tunnel); - - const discardedEvents = Object.keys(outcomes).map(key => { - const [category, reason] = key.split(':'); - return { - reason, - category, - quantity: outcomes[key], - }; - // TODO: Improve types on discarded_events to get rid of cast - }) as ClientReport['discarded_events']; - const envelope = createClientReportEnvelope(discardedEvents, this._api.tunnel && dsnToString(this._api.dsn)); - - try { - sendReport(url, serializeEnvelope(envelope)); - } catch (e) { - IS_DEBUG_BUILD && logger.error(e); - } - } - - /** - * Handle Sentry repsonse for promise-based transports. - */ - protected _handleResponse({ - requestType, - response, - headers, - resolve, - reject, - }: { - requestType: SentryRequestType; - response: Response | XMLHttpRequest; - headers: Record; - resolve: (value?: SentryResponse | PromiseLike | null | undefined) => void; - reject: (reason?: unknown) => void; - }): void { - const status = eventStatusFromHttpCode(response.status); - - this._rateLimits = updateRateLimits(this._rateLimits, headers); - // eslint-disable-next-line deprecation/deprecation - if (this._isRateLimited(requestType)) { - IS_DEBUG_BUILD && - // eslint-disable-next-line deprecation/deprecation - logger.warn(`Too many ${requestType} requests, backing off until: ${this._disabledUntil(requestType)}`); - } - - if (status === 'success') { - resolve({ status }); - return; - } - - reject(response); - } - - /** - * Gets the time that given category is disabled until for rate limiting - * - * @deprecated Please use `disabledUntil` from @sentry/utils - */ - protected _disabledUntil(requestType: SentryRequestType): Date { - const category = requestTypeToCategory(requestType); - return new Date(disabledUntil(this._rateLimits, category)); - } - - /** - * Checks if a category is rate limited - * - * @deprecated Please use `isRateLimited` from @sentry/utils - */ - protected _isRateLimited(requestType: SentryRequestType): boolean { - const category = requestTypeToCategory(requestType); - return isRateLimited(this._rateLimits, category); - } - - protected abstract _sendRequest( - sentryRequest: SentryRequest, - originalPayload: Event | Session, - ): PromiseLike; -} diff --git a/packages/browser/src/transports/fetch.ts b/packages/browser/src/transports/fetch.ts index cddf1b53c1ae..f1aa6709da80 100644 --- a/packages/browser/src/transports/fetch.ts +++ b/packages/browser/src/transports/fetch.ts @@ -1,86 +1,39 @@ -import { Event, Response, SentryRequest, Session, TransportOptions } from '@sentry/types'; -import { SentryError, supportsReferrerPolicy, SyncPromise } from '@sentry/utils'; +import { createTransport } from '@sentry/core'; +import { BaseTransportOptions, Transport, TransportMakeRequestResponse, TransportRequest } from '@sentry/types'; -import { BaseTransport } from './base'; import { FetchImpl, getNativeFetchImplementation } from './utils'; -/** `fetch` based transport */ -export class FetchTransport extends BaseTransport { - /** - * Fetch API reference which always points to native browser implementation. - */ - private _fetch: typeof fetch; - - public constructor(options: TransportOptions, fetchImpl: FetchImpl = getNativeFetchImplementation()) { - super(options); - this._fetch = fetchImpl; - } - - /** - * @param sentryRequest Prepared SentryRequest to be delivered - * @param originalPayload Original payload used to create SentryRequest - */ - protected _sendRequest(sentryRequest: SentryRequest, originalPayload: Event | Session): PromiseLike { - // eslint-disable-next-line deprecation/deprecation - if (this._isRateLimited(sentryRequest.type)) { - this.recordLostEvent('ratelimit_backoff', sentryRequest.type); - - return Promise.reject({ - event: originalPayload, - type: sentryRequest.type, - // eslint-disable-next-line deprecation/deprecation - reason: `Transport for ${sentryRequest.type} requests locked till ${this._disabledUntil( - sentryRequest.type, - )} due to too many requests.`, - status: 429, - }); - } +export interface FetchTransportOptions extends BaseTransportOptions { + requestOptions?: RequestInit; +} - const options: RequestInit = { - body: sentryRequest.body, +/** + * Creates a Transport that uses the Fetch API to send events to Sentry. + */ +export function makeFetchTransport( + options: FetchTransportOptions, + nativeFetch: FetchImpl = getNativeFetchImplementation(), +): Transport { + function makeRequest(request: TransportRequest): PromiseLike { + const requestOptions: RequestInit = { + body: request.body, method: 'POST', - // Despite all stars in the sky saying that Edge supports old draft syntax, aka 'never', 'always', 'origin' and 'default' - // (see https://caniuse.com/#feat=referrer-policy), - // it doesn't. And it throws an exception instead of ignoring this parameter... - // REF: https://github.com/getsentry/raven-js/issues/1233 - referrerPolicy: (supportsReferrerPolicy() ? 'origin' : '') as ReferrerPolicy, + referrerPolicy: 'origin', + ...options.requestOptions, }; - if (this.options.fetchParameters !== undefined) { - Object.assign(options, this.options.fetchParameters); - } - if (this.options.headers !== undefined) { - options.headers = this.options.headers; - } - return this._buffer - .add( - () => - new SyncPromise((resolve, reject) => { - void this._fetch(sentryRequest.url, options) - .then(response => { - const headers = { - 'x-sentry-rate-limits': response.headers.get('X-Sentry-Rate-Limits'), - 'retry-after': response.headers.get('Retry-After'), - }; - this._handleResponse({ - requestType: sentryRequest.type, - response, - headers, - resolve, - reject, - }); - }) - .catch(reject); - }), - ) - .then(undefined, reason => { - // It's either buffer rejection or any other xhr/fetch error, which are treated as NetworkError. - if (reason instanceof SentryError) { - this.recordLostEvent('queue_overflow', sentryRequest.type); - } else { - this.recordLostEvent('network_error', sentryRequest.type); - } - throw reason; - }); + return nativeFetch(options.url, requestOptions).then(response => { + return response.text().then(body => ({ + body, + headers: { + 'x-sentry-rate-limits': response.headers.get('X-Sentry-Rate-Limits'), + 'retry-after': response.headers.get('Retry-After'), + }, + reason: response.statusText, + statusCode: response.status, + })); + }); } + + return createTransport({ bufferSize: options.bufferSize }, makeRequest); } diff --git a/packages/browser/src/transports/index.ts b/packages/browser/src/transports/index.ts index 287e14e0ac50..c30287e3e616 100644 --- a/packages/browser/src/transports/index.ts +++ b/packages/browser/src/transports/index.ts @@ -1,6 +1,2 @@ -export { BaseTransport } from './base'; -export { FetchTransport } from './fetch'; -export { XHRTransport } from './xhr'; - -export { makeNewFetchTransport } from './new-fetch'; -export { makeNewXHRTransport } from './new-xhr'; +export { makeFetchTransport } from './fetch'; +export { makeXHRTransport } from './xhr'; diff --git a/packages/browser/src/transports/new-fetch.ts b/packages/browser/src/transports/new-fetch.ts deleted file mode 100644 index 9a9d7b14ae19..000000000000 --- a/packages/browser/src/transports/new-fetch.ts +++ /dev/null @@ -1,39 +0,0 @@ -import { createTransport } from '@sentry/core'; -import { BaseTransportOptions, NewTransport, TransportMakeRequestResponse, TransportRequest } from '@sentry/types'; - -import { FetchImpl, getNativeFetchImplementation } from './utils'; - -export interface FetchTransportOptions extends BaseTransportOptions { - requestOptions?: RequestInit; -} - -/** - * Creates a Transport that uses the Fetch API to send events to Sentry. - */ -export function makeNewFetchTransport( - options: FetchTransportOptions, - nativeFetch: FetchImpl = getNativeFetchImplementation(), -): NewTransport { - function makeRequest(request: TransportRequest): PromiseLike { - const requestOptions: RequestInit = { - body: request.body, - method: 'POST', - referrerPolicy: 'origin', - ...options.requestOptions, - }; - - return nativeFetch(options.url, requestOptions).then(response => { - return response.text().then(body => ({ - body, - headers: { - 'x-sentry-rate-limits': response.headers.get('X-Sentry-Rate-Limits'), - 'retry-after': response.headers.get('Retry-After'), - }, - reason: response.statusText, - statusCode: response.status, - })); - }); - } - - return createTransport({ bufferSize: options.bufferSize }, makeRequest); -} diff --git a/packages/browser/src/transports/new-xhr.ts b/packages/browser/src/transports/new-xhr.ts deleted file mode 100644 index d45a0019914c..000000000000 --- a/packages/browser/src/transports/new-xhr.ts +++ /dev/null @@ -1,55 +0,0 @@ -import { createTransport } from '@sentry/core'; -import { BaseTransportOptions, NewTransport, TransportMakeRequestResponse, TransportRequest } from '@sentry/types'; -import { SyncPromise } from '@sentry/utils'; - -/** - * The DONE ready state for XmlHttpRequest - * - * Defining it here as a constant b/c XMLHttpRequest.DONE is not always defined - * (e.g. during testing, it is `undefined`) - * - * @see {@link https://developer.mozilla.org/en-US/docs/Web/API/XMLHttpRequest/readyState} - */ -const XHR_READYSTATE_DONE = 4; - -export interface XHRTransportOptions extends BaseTransportOptions { - headers?: { [key: string]: string }; -} - -/** - * Creates a Transport that uses the XMLHttpRequest API to send events to Sentry. - */ -export function makeNewXHRTransport(options: XHRTransportOptions): NewTransport { - function makeRequest(request: TransportRequest): PromiseLike { - return new SyncPromise((resolve, _reject) => { - const xhr = new XMLHttpRequest(); - - xhr.onreadystatechange = (): void => { - if (xhr.readyState === XHR_READYSTATE_DONE) { - const response = { - body: xhr.response, - headers: { - 'x-sentry-rate-limits': xhr.getResponseHeader('X-Sentry-Rate-Limits'), - 'retry-after': xhr.getResponseHeader('Retry-After'), - }, - reason: xhr.statusText, - statusCode: xhr.status, - }; - resolve(response); - } - }; - - xhr.open('POST', options.url); - - for (const header in options.headers) { - if (Object.prototype.hasOwnProperty.call(options.headers, header)) { - xhr.setRequestHeader(header, options.headers[header]); - } - } - - xhr.send(request.body); - }); - } - - return createTransport({ bufferSize: options.bufferSize }, makeRequest); -} diff --git a/packages/browser/src/transports/xhr.ts b/packages/browser/src/transports/xhr.ts index 5da7de258bf6..4b36e348de73 100644 --- a/packages/browser/src/transports/xhr.ts +++ b/packages/browser/src/transports/xhr.ts @@ -1,63 +1,55 @@ -import { Event, Response, SentryRequest, Session } from '@sentry/types'; -import { SentryError, SyncPromise } from '@sentry/utils'; - -import { BaseTransport } from './base'; - -/** `XHR` based transport */ -export class XHRTransport extends BaseTransport { - /** - * @param sentryRequest Prepared SentryRequest to be delivered - * @param originalPayload Original payload used to create SentryRequest - */ - protected _sendRequest(sentryRequest: SentryRequest, originalPayload: Event | Session): PromiseLike { - // eslint-disable-next-line deprecation/deprecation - if (this._isRateLimited(sentryRequest.type)) { - this.recordLostEvent('ratelimit_backoff', sentryRequest.type); - - return Promise.reject({ - event: originalPayload, - type: sentryRequest.type, - // eslint-disable-next-line deprecation/deprecation - reason: `Transport for ${sentryRequest.type} requests locked till ${this._disabledUntil( - sentryRequest.type, - )} due to too many requests.`, - status: 429, - }); - } - - return this._buffer - .add( - () => - new SyncPromise((resolve, reject) => { - const request = new XMLHttpRequest(); - - request.onreadystatechange = (): void => { - if (request.readyState === 4) { - const headers = { - 'x-sentry-rate-limits': request.getResponseHeader('X-Sentry-Rate-Limits'), - 'retry-after': request.getResponseHeader('Retry-After'), - }; - this._handleResponse({ requestType: sentryRequest.type, response: request, headers, resolve, reject }); - } - }; - - request.open('POST', sentryRequest.url); - for (const header in this.options.headers) { - if (Object.prototype.hasOwnProperty.call(this.options.headers, header)) { - request.setRequestHeader(header, this.options.headers[header]); - } - } - request.send(sentryRequest.body); - }), - ) - .then(undefined, reason => { - // It's either buffer rejection or any other xhr/fetch error, which are treated as NetworkError. - if (reason instanceof SentryError) { - this.recordLostEvent('queue_overflow', sentryRequest.type); - } else { - this.recordLostEvent('network_error', sentryRequest.type); +import { createTransport } from '@sentry/core'; +import { BaseTransportOptions, Transport, TransportMakeRequestResponse, TransportRequest } from '@sentry/types'; +import { SyncPromise } from '@sentry/utils'; + +/** + * The DONE ready state for XmlHttpRequest + * + * Defining it here as a constant b/c XMLHttpRequest.DONE is not always defined + * (e.g. during testing, it is `undefined`) + * + * @see {@link https://developer.mozilla.org/en-US/docs/Web/API/XMLHttpRequest/readyState} + */ +const XHR_READYSTATE_DONE = 4; + +export interface XHRTransportOptions extends BaseTransportOptions { + headers?: { [key: string]: string }; +} + +/** + * Creates a Transport that uses the XMLHttpRequest API to send events to Sentry. + */ +export function makeXHRTransport(options: XHRTransportOptions): Transport { + function makeRequest(request: TransportRequest): PromiseLike { + return new SyncPromise((resolve, _reject) => { + const xhr = new XMLHttpRequest(); + + xhr.onreadystatechange = (): void => { + if (xhr.readyState === XHR_READYSTATE_DONE) { + const response = { + body: xhr.response, + headers: { + 'x-sentry-rate-limits': xhr.getResponseHeader('X-Sentry-Rate-Limits'), + 'retry-after': xhr.getResponseHeader('Retry-After'), + }, + reason: xhr.statusText, + statusCode: xhr.status, + }; + resolve(response); } - throw reason; - }); + }; + + xhr.open('POST', options.url); + + for (const header in options.headers) { + if (Object.prototype.hasOwnProperty.call(options.headers, header)) { + xhr.setRequestHeader(header, options.headers[header]); + } + } + + xhr.send(request.body); + }); } + + return createTransport({ bufferSize: options.bufferSize }, makeRequest); } diff --git a/packages/browser/test/unit/transports/base.test.ts b/packages/browser/test/unit/transports/base.test.ts deleted file mode 100644 index 75894049c1ca..000000000000 --- a/packages/browser/test/unit/transports/base.test.ts +++ /dev/null @@ -1,133 +0,0 @@ -import { BaseTransport } from '../../../src/transports/base'; - -const testDsn = 'https://123@sentry.io/42'; -const envelopeEndpoint = 'https://sentry.io/api/42/envelope/?sentry_key=123&sentry_version=7'; - -// @ts-ignore We're purposely not implementing the methods of the abstract `BaseTransport` class in order to be able to -// assert on what the class provides and what it leaves to the concrete class to implement -class SimpleTransport extends BaseTransport {} - -// TODO(v7): Re-enable these tests with client reports -describe.skip('BaseTransport', () => { - describe('Client Reports', () => { - const sendBeaconSpy = jest.fn(); - let visibilityState: string; - - beforeAll(() => { - navigator.sendBeacon = sendBeaconSpy; - Object.defineProperty(document, 'visibilityState', { - configurable: true, - get: function () { - return visibilityState; - }, - }); - jest.spyOn(Date, 'now').mockImplementation(() => 12345); - }); - - beforeEach(() => { - sendBeaconSpy.mockClear(); - }); - - it('attaches visibilitychange handler if sendClientReport is set to true', () => { - const eventListenerSpy = jest.spyOn(document, 'addEventListener'); - new SimpleTransport({ dsn: testDsn, sendClientReports: true }); - expect(eventListenerSpy.mock.calls[0][0]).toBe('visibilitychange'); - eventListenerSpy.mockRestore(); - }); - - it('doesnt attach visibilitychange handler if sendClientReport is set to false', () => { - const eventListenerSpy = jest.spyOn(document, 'addEventListener'); - new SimpleTransport({ dsn: testDsn, sendClientReports: false }); - expect(eventListenerSpy).not.toHaveBeenCalled(); - eventListenerSpy.mockRestore(); - }); - - it('sends beacon request when there are outcomes captured and visibility changed to `hidden`', () => { - const transport = new SimpleTransport({ dsn: testDsn, sendClientReports: true }); - - transport.recordLostEvent('before_send', 'event'); - - visibilityState = 'hidden'; - document.dispatchEvent(new Event('visibilitychange')); - - const outcomes = [{ reason: 'before_send', category: 'error', quantity: 1 }]; - - expect(sendBeaconSpy).toHaveBeenCalledWith( - envelopeEndpoint, - `{}\n{"type":"client_report"}\n{"timestamp":12.345,"discarded_events":${JSON.stringify(outcomes)}}`, - ); - }); - - it('doesnt send beacon request when there are outcomes captured, but visibility state did not change to `hidden`', () => { - const transport = new SimpleTransport({ dsn: testDsn, sendClientReports: true }); - transport.recordLostEvent('before_send', 'event'); - - visibilityState = 'visible'; - document.dispatchEvent(new Event('visibilitychange')); - - expect(sendBeaconSpy).not.toHaveBeenCalled(); - }); - - it('correctly serializes request with different categories/reasons pairs', () => { - const transport = new SimpleTransport({ dsn: testDsn, sendClientReports: true }); - - transport.recordLostEvent('before_send', 'event'); - transport.recordLostEvent('before_send', 'event'); - transport.recordLostEvent('sample_rate', 'transaction'); - transport.recordLostEvent('network_error', 'session'); - transport.recordLostEvent('network_error', 'session'); - transport.recordLostEvent('ratelimit_backoff', 'event'); - - visibilityState = 'hidden'; - document.dispatchEvent(new Event('visibilitychange')); - - const outcomes = [ - { reason: 'before_send', category: 'error', quantity: 2 }, - { reason: 'sample_rate', category: 'transaction', quantity: 1 }, - { reason: 'network_error', category: 'session', quantity: 2 }, - { reason: 'ratelimit_backoff', category: 'error', quantity: 1 }, - ]; - - expect(sendBeaconSpy).toHaveBeenCalledWith( - envelopeEndpoint, - `{}\n{"type":"client_report"}\n{"timestamp":12.345,"discarded_events":${JSON.stringify(outcomes)}}`, - ); - }); - - it('attaches DSN to envelope header if tunnel is configured', () => { - const tunnel = 'https://hello.com/world'; - const transport = new SimpleTransport({ dsn: testDsn, sendClientReports: true, tunnel }); - - transport.recordLostEvent('before_send', 'event'); - - visibilityState = 'hidden'; - document.dispatchEvent(new Event('visibilitychange')); - - const outcomes = [{ reason: 'before_send', category: 'error', quantity: 1 }]; - - expect(sendBeaconSpy).toHaveBeenCalledWith( - tunnel, - `{"dsn":"${testDsn}"}\n{"type":"client_report"}\n{"timestamp":12.345,"discarded_events":${JSON.stringify( - outcomes, - )}}`, - ); - }); - }); - - it('doesnt provide sendEvent() implementation', async () => { - expect.assertions(1); - const transport = new SimpleTransport({ dsn: testDsn }); - - try { - await transport.sendEvent({}); - } catch (e) { - expect(e).toBeDefined(); - } - }); - - it('has correct endpoint url', () => { - const transport = new SimpleTransport({ dsn: testDsn }); - // eslint-disable-next-line deprecation/deprecation - expect(transport.url).toBe('https://sentry.io/api/42/store/?sentry_key=123&sentry_version=7'); - }); -}); diff --git a/packages/browser/test/unit/transports/fetch.test.ts b/packages/browser/test/unit/transports/fetch.test.ts index f7af4e38349c..64ea580c1845 100644 --- a/packages/browser/test/unit/transports/fetch.test.ts +++ b/packages/browser/test/unit/transports/fetch.test.ts @@ -1,515 +1,98 @@ -import { SentryError } from '@sentry/utils'; +import { EventEnvelope, EventItem } from '@sentry/types'; +import { createEnvelope, serializeEnvelope } from '@sentry/utils'; -import { Event, Response, Transports } from '../../../src'; +import { FetchTransportOptions, makeFetchTransport } from '../../../src/transports/fetch'; +import { FetchImpl } from '../../../src/transports/utils'; -const testDsn = 'https://123@sentry.io/42'; -const storeUrl = 'https://sentry.io/api/42/store/?sentry_key=123&sentry_version=7'; -const tunnel = 'https://hello.com/world'; -const eventPayload: Event = { - event_id: '1337', +const DEFAULT_FETCH_TRANSPORT_OPTIONS: FetchTransportOptions = { + url: 'https://sentry.io/api/42/store/?sentry_key=123&sentry_version=7', }; -const transactionPayload: Event = { - event_id: '42', - type: 'transaction', -}; - -const fetch = jest.fn(); -let transport: Transports.BaseTransport; - -// eslint-disable-next-line no-var -declare var window: any; -jest.mock('@sentry/utils', () => { - return { - ...jest.requireActual('@sentry/utils'), - supportsReferrerPolicy(): boolean { - return true; - }, - }; -}); - -describe('FetchTransport', () => { - beforeEach(() => { - window.fetch = fetch; - window.Headers = class Headers { - headers: { [key: string]: string } = {}; - get(key: string) { - return this.headers[key]; - } - set(key: string, value: string) { - this.headers[key] = value; - } - }; - transport = new Transports.FetchTransport({ dsn: testDsn }, window.fetch); +const ERROR_ENVELOPE = createEnvelope({ event_id: 'aa3ff046696b4bc6b609ce6d28fde9e2', sent_at: '123' }, [ + [{ type: 'event' }, { event_id: 'aa3ff046696b4bc6b609ce6d28fde9e2' }] as EventItem, +]); + +class Headers { + headers: { [key: string]: string } = {}; + get(key: string) { + return this.headers[key] || null; + } + set(key: string, value: string) { + this.headers[key] = value; + } +} + +describe('NewFetchTransport', () => { + it('calls fetch with the given URL', async () => { + const mockFetch = jest.fn(() => + Promise.resolve({ + headers: new Headers(), + status: 200, + text: () => Promise.resolve({}), + }), + ) as unknown as FetchImpl; + const transport = makeFetchTransport(DEFAULT_FETCH_TRANSPORT_OPTIONS, mockFetch); + + expect(mockFetch).toHaveBeenCalledTimes(0); + const res = await transport.send(ERROR_ENVELOPE); + expect(mockFetch).toHaveBeenCalledTimes(1); + + expect(res.status).toBe('success'); + + expect(mockFetch).toHaveBeenLastCalledWith(DEFAULT_FETCH_TRANSPORT_OPTIONS.url, { + body: serializeEnvelope(ERROR_ENVELOPE), + method: 'POST', + referrerPolicy: 'origin', + }); }); - afterEach(() => { - fetch.mockRestore(); - }); + it('sets rate limit headers', async () => { + const headers = { + get: jest.fn(), + }; - it('inherits composeEndpointUrl() implementation', () => { - // eslint-disable-next-line deprecation/deprecation - expect(transport.url).toBe(storeUrl); + const mockFetch = jest.fn(() => + Promise.resolve({ + headers, + status: 200, + text: () => Promise.resolve({}), + }), + ) as unknown as FetchImpl; + const transport = makeFetchTransport(DEFAULT_FETCH_TRANSPORT_OPTIONS, mockFetch); + + expect(headers.get).toHaveBeenCalledTimes(0); + await transport.send(ERROR_ENVELOPE); + + expect(headers.get).toHaveBeenCalledTimes(2); + expect(headers.get).toHaveBeenCalledWith('X-Sentry-Rate-Limits'); + expect(headers.get).toHaveBeenCalledWith('Retry-After'); }); - describe('sendEvent()', () => { - it('sends a request to Sentry servers', async () => { - const response = { status: 200, headers: new Headers() }; - - window.fetch.mockImplementation(() => Promise.resolve(response)); - - const res = await transport.sendEvent(eventPayload); - - expect((res as Response).status).toBe('success'); - expect(fetch).toHaveBeenCalledWith(storeUrl, { - body: JSON.stringify(eventPayload), - method: 'POST', - referrerPolicy: 'origin', - }); - }); - - it('sends a request to tunnel if configured', async () => { - transport = new Transports.FetchTransport({ dsn: testDsn, tunnel }, window.fetch); - window.fetch.mockImplementation(() => Promise.resolve({ status: 200, headers: new Headers() })); - - await transport.sendEvent(eventPayload); - - expect(fetch.mock.calls[0][0]).toBe(tunnel); - }); - - it('rejects with non-200 status code', async () => { - const response = { status: 403, headers: new Headers() }; - - window.fetch.mockImplementation(() => Promise.resolve(response)); - - try { - await transport.sendEvent(eventPayload); - } catch (res) { - expect((res as Response).status).toBe(403); - expect(fetch).toHaveBeenCalledWith(storeUrl, { - body: JSON.stringify(eventPayload), - method: 'POST', - referrerPolicy: 'origin', - }); - } - }); - - it('pass the error to rejection when fetch fails', async () => { - const response = { status: 403, headers: new Headers() }; - - window.fetch.mockImplementation(() => Promise.reject(response)); - - try { - await transport.sendEvent(eventPayload); - } catch (res) { - expect(res).toBe(response); - } - }); - - it('should record dropped event when fetch fails', async () => { - const response = { status: 403, headers: new Headers() }; - - window.fetch.mockImplementation(() => Promise.reject(response)); - - const spy = jest.spyOn(transport, 'recordLostEvent'); - - try { - await transport.sendEvent(eventPayload); - } catch (_) { - expect(spy).toHaveBeenCalledWith('network_error', 'event'); - } - }); - - it('should record dropped event when queue buffer overflows', async () => { - // @ts-ignore private method - jest.spyOn(transport._buffer, 'add').mockRejectedValue(new SentryError('Buffer Full')); - const spy = jest.spyOn(transport, 'recordLostEvent'); - - try { - await transport.sendEvent(transactionPayload); - } catch (_) { - expect(spy).toHaveBeenCalledWith('queue_overflow', 'transaction'); - } - }); - - it('passes in headers', async () => { - transport = new Transports.FetchTransport( - { - dsn: testDsn, - headers: { - Accept: 'application/json', - }, - }, - window.fetch, - ); - const response = { status: 200, headers: new Headers() }; - - window.fetch.mockImplementation(() => Promise.resolve(response)); - - const res = await transport.sendEvent(eventPayload); - - expect((res as Response).status).toBe('success'); - expect(fetch).toHaveBeenCalledWith(storeUrl, { - body: JSON.stringify(eventPayload), - headers: { - Accept: 'application/json', - }, - method: 'POST', - referrerPolicy: 'origin', - }); - }); - - it('passes in fetch parameters', async () => { - transport = new Transports.FetchTransport( - { - dsn: testDsn, - fetchParameters: { - credentials: 'include', - }, - }, - window.fetch, - ); - const response = { status: 200, headers: new Headers() }; - - window.fetch.mockImplementation(() => Promise.resolve(response)); - - const res = await transport.sendEvent(eventPayload); - - expect((res as Response).status).toBe('success'); - expect(fetch).toHaveBeenCalledWith(storeUrl, { - body: JSON.stringify(eventPayload), - credentials: 'include', - method: 'POST', - referrerPolicy: 'origin', - }); - }); - - describe('Rate-limiting', () => { - it('back-off using Retry-After header', async () => { - const retryAfterSeconds = 10; - const beforeLimit = Date.now(); - const withinLimit = beforeLimit + (retryAfterSeconds / 2) * 1000; - const afterLimit = beforeLimit + retryAfterSeconds * 1000; - - jest - .spyOn(Date, 'now') - // 1st event - updateRateLimits - false - .mockImplementationOnce(() => beforeLimit) - // 1st event - _handleRateLimit - .mockImplementationOnce(() => beforeLimit) - // 1st event - _handleRateLimit - .mockImplementationOnce(() => beforeLimit) - // 2nd event - _isRateLimited - true - .mockImplementationOnce(() => withinLimit) - // 3rd event - _isRateLimited - false - .mockImplementationOnce(() => afterLimit) - // 3rd event - _handleRateLimit - .mockImplementationOnce(() => afterLimit); - - const headers = new Headers(); - headers.set('Retry-After', `${retryAfterSeconds}`); - window.fetch.mockImplementation(() => Promise.resolve({ status: 429, headers })); - - try { - await transport.sendEvent(eventPayload); - throw new Error('unreachable!'); - } catch (res) { - expect((res as Response).status).toBe(429); - expect((res as Response).reason).toBeUndefined(); - expect(fetch).toHaveBeenCalled(); - } - - try { - await transport.sendEvent(eventPayload); - throw new Error('unreachable!'); - } catch (res) { - expect((res as Response).status).toBe(429); - expect((res as Response).reason).toBe( - `Transport for event requests locked till ${new Date(afterLimit)} due to too many requests.`, - ); - expect(fetch).toHaveBeenCalled(); - } - - window.fetch.mockImplementation(() => Promise.resolve({ status: 200, headers: new Headers() })); - - const eventRes = await transport.sendEvent(eventPayload); - expect(eventRes.status).toBe('success'); - expect(fetch).toHaveBeenCalledTimes(2); - }); - - it('back-off using X-Sentry-Rate-Limits with single category', async () => { - const retryAfterSeconds = 10; - const beforeLimit = Date.now(); - const withinLimit = beforeLimit + (retryAfterSeconds / 2) * 1000; - const afterLimit = beforeLimit + retryAfterSeconds * 1000; - - jest - .spyOn(Date, 'now') - // 1st event - _isRateLimited - false - .mockImplementationOnce(() => beforeLimit) - // 1st event - _handleRateLimit - .mockImplementationOnce(() => beforeLimit) - // 1st event - _isRateLimited - .mockImplementationOnce(() => beforeLimit) - // 1st event - _handleRateLimit - .mockImplementationOnce(() => beforeLimit) - // 2nd event - _isRateLimited - false (different category) - .mockImplementationOnce(() => withinLimit) - // 2nd event - _handleRateLimit - .mockImplementationOnce(() => withinLimit) - // 3rd event - _isRateLimited - true - .mockImplementationOnce(() => withinLimit) - // 4th event - _isRateLimited - false - .mockImplementationOnce(() => afterLimit) - // 4th event - _handleRateLimit - .mockImplementationOnce(() => afterLimit); - - const headers = new Headers(); - headers.set('X-Sentry-Rate-Limits', `${retryAfterSeconds}:error:scope`); - window.fetch.mockImplementation(() => Promise.resolve({ status: 429, headers })); - - try { - await transport.sendEvent(eventPayload); - throw new Error('unreachable!'); - } catch (res) { - expect((res as Response).status).toBe(429); - expect((res as Response).reason).toBeUndefined(); - expect(fetch).toHaveBeenCalled(); - } - - window.fetch.mockImplementation(() => Promise.resolve({ status: 200, headers: new Headers() })); - - const transactionRes = await transport.sendEvent(transactionPayload); - expect(transactionRes.status).toBe('success'); - expect(fetch).toHaveBeenCalledTimes(2); - - try { - await transport.sendEvent(eventPayload); - throw new Error('unreachable!'); - } catch (res) { - expect((res as Response).status).toBe(429); - expect((res as Response).reason).toBe( - `Transport for event requests locked till ${new Date(afterLimit)} due to too many requests.`, - ); - expect(fetch).toHaveBeenCalledTimes(2); - } - - const eventRes = await transport.sendEvent(eventPayload); - expect(eventRes.status).toBe('success'); - expect(fetch).toHaveBeenCalledTimes(3); - }); - - it('back-off using X-Sentry-Rate-Limits with multiple categories', async () => { - const retryAfterSeconds = 10; - const beforeLimit = Date.now(); - const withinLimit = beforeLimit + (retryAfterSeconds / 2) * 1000; - const afterLimit = beforeLimit + retryAfterSeconds * 1000; - - jest - .spyOn(Date, 'now') - // 1st event - _isRateLimited - false - .mockImplementationOnce(() => beforeLimit) - // 1st event - updateRateLimits - .mockImplementationOnce(() => beforeLimit) - // 1st event - _isRateLimited - .mockImplementationOnce(() => beforeLimit) - // 2nd event - _isRateLimited - true (event category) - .mockImplementationOnce(() => withinLimit) - // 3rd event - _isRateLimited - true (transaction category) - .mockImplementationOnce(() => withinLimit) - // 4th event - _isRateLimited - false (event category) - .mockImplementationOnce(() => afterLimit) - // 4th event - _handleRateLimit - .mockImplementationOnce(() => afterLimit) - // 5th event - _isRateLimited - false (transaction category) - .mockImplementationOnce(() => afterLimit) - // 5th event - _handleRateLimit - .mockImplementationOnce(() => afterLimit); - - const headers = new Headers(); - headers.set('X-Sentry-Rate-Limits', `${retryAfterSeconds}:error;transaction:scope`); - window.fetch.mockImplementation(() => Promise.resolve({ status: 429, headers })); - - try { - await transport.sendEvent(eventPayload); - throw new Error('unreachable!'); - } catch (res) { - expect((res as Response).status).toBe(429); - expect((res as Response).reason).toBeUndefined(); - expect(fetch).toHaveBeenCalled(); - } - - try { - await transport.sendEvent(eventPayload); - throw new Error('unreachable!'); - } catch (res) { - expect((res as Response).status).toBe(429); - expect((res as Response).reason).toBe( - `Transport for event requests locked till ${new Date(afterLimit)} due to too many requests.`, - ); - expect(fetch).toHaveBeenCalled(); - } - - try { - await transport.sendEvent(transactionPayload); - throw new Error('unreachable!'); - } catch (res) { - expect((res as Response).status).toBe(429); - expect((res as Response).reason).toBe( - `Transport for transaction requests locked till ${new Date(afterLimit)} due to too many requests.`, - ); - expect(fetch).toHaveBeenCalled(); - } - - window.fetch.mockImplementation(() => Promise.resolve({ status: 200, headers: new Headers() })); - - const eventRes = await transport.sendEvent(eventPayload); - expect(eventRes.status).toBe('success'); - expect(fetch).toHaveBeenCalledTimes(2); - - const transactionRes = await transport.sendEvent(transactionPayload); - expect(transactionRes.status).toBe('success'); - expect(fetch).toHaveBeenCalledTimes(3); - }); - - it('back-off using X-Sentry-Rate-Limits with missing categories should lock them all', async () => { - const retryAfterSeconds = 10; - const beforeLimit = Date.now(); - const withinLimit = beforeLimit + (retryAfterSeconds / 2) * 1000; - const afterLimit = beforeLimit + retryAfterSeconds * 1000; - - jest - .spyOn(Date, 'now') - // 1st event - _isRateLimited - false - .mockImplementationOnce(() => beforeLimit) - // 1st event - _handleRateLimit - .mockImplementationOnce(() => beforeLimit) - // 1st event - _isRateLimited - .mockImplementationOnce(() => beforeLimit) - // 2nd event - _isRateLimited - true (event category) - .mockImplementationOnce(() => withinLimit) - // 3rd event - _isRateLimited - true (transaction category) - .mockImplementationOnce(() => withinLimit) - // 4th event - _isRateLimited - false (event category) - .mockImplementationOnce(() => afterLimit) - // 4th event - _handleRateLimit - .mockImplementationOnce(() => afterLimit) - // 5th event - _isRateLimited - false (transaction category) - .mockImplementationOnce(() => afterLimit) - // 5th event - _handleRateLimit - .mockImplementationOnce(() => afterLimit); - - const headers = new Headers(); - headers.set('X-Sentry-Rate-Limits', `${retryAfterSeconds}::scope`); - window.fetch.mockImplementation(() => Promise.resolve({ status: 429, headers })); - - try { - await transport.sendEvent(eventPayload); - throw new Error('unreachable!'); - } catch (res) { - expect((res as Response).status).toBe(429); - expect((res as Response).reason).toBeUndefined(); - expect(fetch).toHaveBeenCalled(); - } - - try { - await transport.sendEvent(eventPayload); - throw new Error('unreachable!'); - } catch (res) { - expect((res as Response).status).toBe(429); - expect((res as Response).reason).toBe( - `Transport for event requests locked till ${new Date(afterLimit)} due to too many requests.`, - ); - expect(fetch).toHaveBeenCalled(); - } - - try { - await transport.sendEvent(transactionPayload); - throw new Error('unreachable!'); - } catch (res) { - expect((res as Response).status).toBe(429); - expect((res as Response).reason).toBe( - `Transport for transaction requests locked till ${new Date(afterLimit)} due to too many requests.`, - ); - expect(fetch).toHaveBeenCalled(); - } - - window.fetch.mockImplementation(() => Promise.resolve({ status: 200, headers: new Headers() })); - - const eventRes = await transport.sendEvent(eventPayload); - expect(eventRes.status).toBe('success'); - expect(fetch).toHaveBeenCalledTimes(2); - - const transactionRes = await transport.sendEvent(transactionPayload); - expect(transactionRes.status).toBe('success'); - expect(fetch).toHaveBeenCalledTimes(3); - }); - - it('back-off using X-Sentry-Rate-Limits should also trigger for 200 responses', async () => { - const retryAfterSeconds = 10; - const beforeLimit = Date.now(); - const withinLimit = beforeLimit + (retryAfterSeconds / 2) * 1000; - const afterLimit = beforeLimit + retryAfterSeconds * 1000; - - jest - .spyOn(Date, 'now') - // 1st event - _isRateLimited - false - .mockImplementationOnce(() => beforeLimit) - // 1st event - _handleRateLimit - .mockImplementationOnce(() => beforeLimit) - // 1st event - _isRateLimited - .mockImplementationOnce(() => beforeLimit) - // 2nd event - _isRateLimited - true - .mockImplementationOnce(() => withinLimit) - // 3rd event - _isRateLimited - false - .mockImplementationOnce(() => afterLimit) - // 3rd event - _handleRateLimit - .mockImplementationOnce(() => afterLimit); - - const headers = new Headers(); - headers.set('X-Sentry-Rate-Limits', `${retryAfterSeconds}:error;transaction:scope`); - window.fetch.mockImplementation(() => Promise.resolve({ status: 200, headers })); - - let eventRes = await transport.sendEvent(eventPayload); - expect(eventRes.status).toBe('success'); - expect(fetch).toHaveBeenCalled(); - - try { - await transport.sendEvent(eventPayload); - throw new Error('unreachable!'); - } catch (res) { - expect((res as Response).status).toBe(429); - expect((res as Response).reason).toBe( - `Transport for event requests locked till ${new Date(afterLimit)} due to too many requests.`, - ); - expect(fetch).toHaveBeenCalled(); - } - - window.fetch.mockImplementation(() => Promise.resolve({ status: 200, headers: new Headers() })); - - eventRes = await transport.sendEvent(eventPayload); - expect(eventRes.status).toBe('success'); - expect(fetch).toHaveBeenCalledTimes(2); - }); - - it('should record dropped event', async () => { - // @ts-ignore private method - jest.spyOn(transport, '_isRateLimited').mockReturnValue(true); - - const spy = jest.spyOn(transport, 'recordLostEvent'); + it('allows for custom options to be passed in', async () => { + const mockFetch = jest.fn(() => + Promise.resolve({ + headers: new Headers(), + status: 200, + text: () => Promise.resolve({}), + }), + ) as unknown as FetchImpl; + + const REQUEST_OPTIONS: RequestInit = { + referrerPolicy: 'strict-origin', + keepalive: true, + referrer: 'http://example.org', + }; - try { - await transport.sendEvent(eventPayload); - } catch (_) { - expect(spy).toHaveBeenCalledWith('ratelimit_backoff', 'event'); - } + const transport = makeFetchTransport( + { ...DEFAULT_FETCH_TRANSPORT_OPTIONS, requestOptions: REQUEST_OPTIONS }, + mockFetch, + ); - try { - await transport.sendEvent(transactionPayload); - } catch (_) { - expect(spy).toHaveBeenCalledWith('ratelimit_backoff', 'transaction'); - } - }); + await transport.send(ERROR_ENVELOPE); + expect(mockFetch).toHaveBeenLastCalledWith(DEFAULT_FETCH_TRANSPORT_OPTIONS.url, { + body: serializeEnvelope(ERROR_ENVELOPE), + method: 'POST', + ...REQUEST_OPTIONS, }); }); }); diff --git a/packages/browser/test/unit/transports/new-fetch.test.ts b/packages/browser/test/unit/transports/new-fetch.test.ts deleted file mode 100644 index e1030be07204..000000000000 --- a/packages/browser/test/unit/transports/new-fetch.test.ts +++ /dev/null @@ -1,98 +0,0 @@ -import { EventEnvelope, EventItem } from '@sentry/types'; -import { createEnvelope, serializeEnvelope } from '@sentry/utils'; - -import { FetchTransportOptions, makeNewFetchTransport } from '../../../src/transports/new-fetch'; -import { FetchImpl } from '../../../src/transports/utils'; - -const DEFAULT_FETCH_TRANSPORT_OPTIONS: FetchTransportOptions = { - url: 'https://sentry.io/api/42/store/?sentry_key=123&sentry_version=7', -}; - -const ERROR_ENVELOPE = createEnvelope({ event_id: 'aa3ff046696b4bc6b609ce6d28fde9e2', sent_at: '123' }, [ - [{ type: 'event' }, { event_id: 'aa3ff046696b4bc6b609ce6d28fde9e2' }] as EventItem, -]); - -class Headers { - headers: { [key: string]: string } = {}; - get(key: string) { - return this.headers[key] || null; - } - set(key: string, value: string) { - this.headers[key] = value; - } -} - -describe('NewFetchTransport', () => { - it('calls fetch with the given URL', async () => { - const mockFetch = jest.fn(() => - Promise.resolve({ - headers: new Headers(), - status: 200, - text: () => Promise.resolve({}), - }), - ) as unknown as FetchImpl; - const transport = makeNewFetchTransport(DEFAULT_FETCH_TRANSPORT_OPTIONS, mockFetch); - - expect(mockFetch).toHaveBeenCalledTimes(0); - const res = await transport.send(ERROR_ENVELOPE); - expect(mockFetch).toHaveBeenCalledTimes(1); - - expect(res.status).toBe('success'); - - expect(mockFetch).toHaveBeenLastCalledWith(DEFAULT_FETCH_TRANSPORT_OPTIONS.url, { - body: serializeEnvelope(ERROR_ENVELOPE), - method: 'POST', - referrerPolicy: 'origin', - }); - }); - - it('sets rate limit headers', async () => { - const headers = { - get: jest.fn(), - }; - - const mockFetch = jest.fn(() => - Promise.resolve({ - headers, - status: 200, - text: () => Promise.resolve({}), - }), - ) as unknown as FetchImpl; - const transport = makeNewFetchTransport(DEFAULT_FETCH_TRANSPORT_OPTIONS, mockFetch); - - expect(headers.get).toHaveBeenCalledTimes(0); - await transport.send(ERROR_ENVELOPE); - - expect(headers.get).toHaveBeenCalledTimes(2); - expect(headers.get).toHaveBeenCalledWith('X-Sentry-Rate-Limits'); - expect(headers.get).toHaveBeenCalledWith('Retry-After'); - }); - - it('allows for custom options to be passed in', async () => { - const mockFetch = jest.fn(() => - Promise.resolve({ - headers: new Headers(), - status: 200, - text: () => Promise.resolve({}), - }), - ) as unknown as FetchImpl; - - const REQUEST_OPTIONS: RequestInit = { - referrerPolicy: 'strict-origin', - keepalive: true, - referrer: 'http://example.org', - }; - - const transport = makeNewFetchTransport( - { ...DEFAULT_FETCH_TRANSPORT_OPTIONS, requestOptions: REQUEST_OPTIONS }, - mockFetch, - ); - - await transport.send(ERROR_ENVELOPE); - expect(mockFetch).toHaveBeenLastCalledWith(DEFAULT_FETCH_TRANSPORT_OPTIONS.url, { - body: serializeEnvelope(ERROR_ENVELOPE), - method: 'POST', - ...REQUEST_OPTIONS, - }); - }); -}); diff --git a/packages/browser/test/unit/transports/new-xhr.test.ts b/packages/browser/test/unit/transports/new-xhr.test.ts deleted file mode 100644 index 603b0f6037dc..000000000000 --- a/packages/browser/test/unit/transports/new-xhr.test.ts +++ /dev/null @@ -1,109 +0,0 @@ -import { EventEnvelope, EventItem } from '@sentry/types'; -import { createEnvelope, serializeEnvelope } from '@sentry/utils'; - -import { makeNewXHRTransport, XHRTransportOptions } from '../../../src/transports/new-xhr'; - -const DEFAULT_XHR_TRANSPORT_OPTIONS: XHRTransportOptions = { - url: 'https://sentry.io/api/42/store/?sentry_key=123&sentry_version=7', -}; - -const ERROR_ENVELOPE = createEnvelope({ event_id: 'aa3ff046696b4bc6b609ce6d28fde9e2', sent_at: '123' }, [ - [{ type: 'event' }, { event_id: 'aa3ff046696b4bc6b609ce6d28fde9e2' }] as EventItem, -]); - -function createXHRMock() { - const retryAfterSeconds = 10; - - const xhrMock: Partial = { - open: jest.fn(), - send: jest.fn(), - setRequestHeader: jest.fn(), - readyState: 4, - status: 200, - response: 'Hello World!', - onreadystatechange: () => {}, - getResponseHeader: jest.fn((header: string) => { - switch (header) { - case 'Retry-After': - return '10'; - case `${retryAfterSeconds}`: - return null; - default: - return `${retryAfterSeconds}:error:scope`; - } - }), - }; - - // casting `window` as `any` because XMLHttpRequest is missing in Window (TS-only) - jest.spyOn(window as any, 'XMLHttpRequest').mockImplementation(() => xhrMock as XMLHttpRequest); - - return xhrMock; -} - -describe('NewXHRTransport', () => { - const xhrMock: Partial = createXHRMock(); - - afterEach(() => { - jest.clearAllMocks(); - }); - - afterAll(() => { - jest.restoreAllMocks(); - }); - - it('makes an XHR request to the given URL', async () => { - const transport = makeNewXHRTransport(DEFAULT_XHR_TRANSPORT_OPTIONS); - expect(xhrMock.open).toHaveBeenCalledTimes(0); - expect(xhrMock.setRequestHeader).toHaveBeenCalledTimes(0); - expect(xhrMock.send).toHaveBeenCalledTimes(0); - - await Promise.all([transport.send(ERROR_ENVELOPE), (xhrMock as XMLHttpRequest).onreadystatechange!({} as Event)]); - - expect(xhrMock.open).toHaveBeenCalledTimes(1); - expect(xhrMock.open).toHaveBeenCalledWith('POST', DEFAULT_XHR_TRANSPORT_OPTIONS.url); - expect(xhrMock.send).toHaveBeenCalledTimes(1); - expect(xhrMock.send).toHaveBeenCalledWith(serializeEnvelope(ERROR_ENVELOPE)); - }); - - it('returns the correct response', async () => { - const transport = makeNewXHRTransport(DEFAULT_XHR_TRANSPORT_OPTIONS); - - const [res] = await Promise.all([ - transport.send(ERROR_ENVELOPE), - (xhrMock as XMLHttpRequest).onreadystatechange!({} as Event), - ]); - - expect(res).toBeDefined(); - expect(res.status).toEqual('success'); - }); - - it('sets rate limit response headers', async () => { - const transport = makeNewXHRTransport(DEFAULT_XHR_TRANSPORT_OPTIONS); - - await Promise.all([transport.send(ERROR_ENVELOPE), (xhrMock as XMLHttpRequest).onreadystatechange!({} as Event)]); - - expect(xhrMock.getResponseHeader).toHaveBeenCalledTimes(2); - expect(xhrMock.getResponseHeader).toHaveBeenCalledWith('X-Sentry-Rate-Limits'); - expect(xhrMock.getResponseHeader).toHaveBeenCalledWith('Retry-After'); - }); - - it('sets custom request headers', async () => { - const headers = { - referrerPolicy: 'strict-origin', - keepalive: 'true', - referrer: 'http://example.org', - }; - const options: XHRTransportOptions = { - ...DEFAULT_XHR_TRANSPORT_OPTIONS, - headers, - }; - - const transport = makeNewXHRTransport(options); - await Promise.all([transport.send(ERROR_ENVELOPE), (xhrMock as XMLHttpRequest).onreadystatechange!({} as Event)]); - - expect(xhrMock.setRequestHeader).toHaveBeenCalledTimes(3); - expect(xhrMock.setRequestHeader).toHaveBeenCalledWith('referrerPolicy', headers.referrerPolicy); - expect(xhrMock.setRequestHeader).toHaveBeenCalledWith('keepalive', headers.keepalive); - expect(xhrMock.setRequestHeader).toHaveBeenCalledWith('referrer', headers.referrer); - }); -}); diff --git a/packages/browser/test/unit/transports/xhr.test.ts b/packages/browser/test/unit/transports/xhr.test.ts index 2a0f43d89815..3ec634360177 100644 --- a/packages/browser/test/unit/transports/xhr.test.ts +++ b/packages/browser/test/unit/transports/xhr.test.ts @@ -1,441 +1,109 @@ -import { SentryError } from '@sentry/utils'; -import { fakeServer, SinonFakeServer } from 'sinon'; +import { EventEnvelope, EventItem } from '@sentry/types'; +import { createEnvelope, serializeEnvelope } from '@sentry/utils'; -import { Event, Response, Transports } from '../../../src'; +import { makeXHRTransport, XHRTransportOptions } from '../../../src/transports/xhr'; -const testDsn = 'https://123@sentry.io/42'; -const storeUrl = 'https://sentry.io/api/42/store/?sentry_key=123&sentry_version=7'; -const envelopeUrl = 'https://sentry.io/api/42/envelope/?sentry_key=123&sentry_version=7'; -const tunnel = 'https://hello.com/world'; -const eventPayload: Event = { - event_id: '1337', +const DEFAULT_XHR_TRANSPORT_OPTIONS: XHRTransportOptions = { + url: 'https://sentry.io/api/42/store/?sentry_key=123&sentry_version=7', }; -const transactionPayload: Event = { - event_id: '42', - type: 'transaction', -}; - -let server: SinonFakeServer; -let transport: Transports.BaseTransport; - -describe('XHRTransport', () => { - beforeEach(() => { - server = fakeServer.create(); - server.respondImmediately = true; - transport = new Transports.XHRTransport({ dsn: testDsn }); - }); - - afterEach(() => { - server.restore(); - }); - - it('inherits composeEndpointUrl() implementation', () => { - // eslint-disable-next-line deprecation/deprecation - expect(transport.url).toBe(storeUrl); - }); - - describe('sendEvent()', () => { - it('sends a request to Sentry servers', async () => { - server.respondWith('POST', storeUrl, [200, {}, '']); - - const res = await transport.sendEvent(eventPayload); - - expect((res as Response).status).toBe('success'); - const request = server.requests[0]; - expect(server.requests.length).toBe(1); - expect(request.method).toBe('POST'); - expect(JSON.parse(request.requestBody)).toEqual(eventPayload); - }); - - it('sends a request to tunnel if configured', async () => { - transport = new Transports.XHRTransport({ dsn: testDsn, tunnel }); - server.respondWith('POST', tunnel, [200, {}, '']); - - await transport.sendEvent(eventPayload); - - expect(server.requests[0].url).toBe(tunnel); - }); - - it('rejects with non-200 status code', async () => { - server.respondWith('POST', storeUrl, [403, {}, '']); - try { - await transport.sendEvent(eventPayload); - } catch (res) { - expect((res as Response).status).toBe(403); - const request = server.requests[0]; - expect(server.requests.length).toBe(1); - expect(request.method).toBe('POST'); - expect(JSON.parse(request.requestBody)).toEqual(eventPayload); +const ERROR_ENVELOPE = createEnvelope({ event_id: 'aa3ff046696b4bc6b609ce6d28fde9e2', sent_at: '123' }, [ + [{ type: 'event' }, { event_id: 'aa3ff046696b4bc6b609ce6d28fde9e2' }] as EventItem, +]); + +function createXHRMock() { + const retryAfterSeconds = 10; + + const xhrMock: Partial = { + open: jest.fn(), + send: jest.fn(), + setRequestHeader: jest.fn(), + readyState: 4, + status: 200, + response: 'Hello World!', + onreadystatechange: () => {}, + getResponseHeader: jest.fn((header: string) => { + switch (header) { + case 'Retry-After': + return '10'; + case `${retryAfterSeconds}`: + return null; + default: + return `${retryAfterSeconds}:error:scope`; } - }); - - it('should record dropped event when request fails', async () => { - server.respondWith('POST', storeUrl, [403, {}, '']); - - const spy = jest.spyOn(transport, 'recordLostEvent'); - - try { - await transport.sendEvent(eventPayload); - } catch (_) { - expect(spy).toHaveBeenCalledWith('network_error', 'event'); - } - }); - - it('should record dropped event when queue buffer overflows', async () => { - // @ts-ignore private method - jest.spyOn(transport._buffer, 'add').mockRejectedValue(new SentryError('Buffer Full')); - const spy = jest.spyOn(transport, 'recordLostEvent'); - - try { - await transport.sendEvent(transactionPayload); - } catch (_) { - expect(spy).toHaveBeenCalledWith('queue_overflow', 'transaction'); - } - }); - - it('passes in headers', async () => { - transport = new Transports.XHRTransport({ - dsn: testDsn, - headers: { - Accept: 'application/json', - }, - }); - - server.respondWith('POST', storeUrl, [200, {}, '']); - const res = await transport.sendEvent(eventPayload); - const request = server.requests[0]; - - expect((res as Response).status).toBe('success'); - const requestHeaders: { [key: string]: string } = request.requestHeaders as { [key: string]: string }; - expect(requestHeaders['Accept']).toBe('application/json'); - }); - - describe('Rate-limiting', () => { - it('back-off using Retry-After header', async () => { - const retryAfterSeconds = 10; - const beforeLimit = Date.now(); - const withinLimit = beforeLimit + (retryAfterSeconds / 2) * 1000; - const afterLimit = beforeLimit + retryAfterSeconds * 1000; - - server.respondWith('POST', storeUrl, [429, { 'Retry-After': `${retryAfterSeconds}` }, '']); - - jest - .spyOn(Date, 'now') - // 1st event - _isRateLimited - false - .mockImplementationOnce(() => beforeLimit) - // 1st event - _handleRateLimit - .mockImplementationOnce(() => beforeLimit) - // 1st event - _handleRateLimit - .mockImplementationOnce(() => beforeLimit) - // 2nd event - _isRateLimited - true - .mockImplementationOnce(() => withinLimit) - // 3rd event - _isRateLimited - false - .mockImplementationOnce(() => afterLimit) - // 3rd event - _handleRateLimit - .mockImplementationOnce(() => afterLimit); - - try { - await transport.sendEvent(eventPayload); - throw new Error('unreachable!'); - } catch (res) { - expect((res as Response).status).toBe(429); - expect((res as Response).reason).toBeUndefined(); - expect(server.requests.length).toBe(1); - } - - try { - await transport.sendEvent(eventPayload); - throw new Error('unreachable!'); - } catch (res) { - expect((res as Response).status).toBe(429); - expect((res as Response).reason).toBe( - `Transport for event requests locked till ${new Date(afterLimit)} due to too many requests.`, - ); - expect(server.requests.length).toBe(1); - } + }), + }; - server.respondWith('POST', storeUrl, [200, {}, '']); + // casting `window` as `any` because XMLHttpRequest is missing in Window (TS-only) + jest.spyOn(window as any, 'XMLHttpRequest').mockImplementation(() => xhrMock as XMLHttpRequest); - const eventRes = await transport.sendEvent(eventPayload); - expect(eventRes.status).toBe('success'); - expect(server.requests.length).toBe(2); - }); + return xhrMock; +} - it('back-off using X-Sentry-Rate-Limits with single category', async () => { - const retryAfterSeconds = 10; - const beforeLimit = Date.now(); - const withinLimit = beforeLimit + (retryAfterSeconds / 2) * 1000; - const afterLimit = beforeLimit + retryAfterSeconds * 1000; +describe('NewXHRTransport', () => { + const xhrMock: Partial = createXHRMock(); - server.respondWith('POST', storeUrl, [429, { 'X-Sentry-Rate-Limits': `${retryAfterSeconds}:error:scope` }, '']); - server.respondWith('POST', envelopeUrl, [200, {}, '']); - - jest - .spyOn(Date, 'now') - // 1st event - _isRateLimited - false - .mockImplementationOnce(() => beforeLimit) - // 1st event - _handleRateLimit - .mockImplementationOnce(() => beforeLimit) - // 1st event - _isRateLimited - .mockImplementationOnce(() => beforeLimit) - // 1st event - _handleRateLimit - .mockImplementationOnce(() => beforeLimit) - // 2nd event - _isRateLimited - false (different category) - .mockImplementationOnce(() => withinLimit) - // 2nd event - _handleRateLimit - .mockImplementationOnce(() => withinLimit) - // 3rd event - _isRateLimited - true - .mockImplementationOnce(() => withinLimit) - // 4th event - _isRateLimited - false - .mockImplementationOnce(() => afterLimit) - // 4th event - _handleRateLimit - .mockImplementationOnce(() => afterLimit); - - try { - await transport.sendEvent(eventPayload); - throw new Error('unreachable!'); - } catch (res) { - expect((res as Response).status).toBe(429); - expect((res as Response).reason).toBeUndefined(); - expect(server.requests.length).toBe(1); - } - - const transactionRes = await transport.sendEvent(transactionPayload); - expect(transactionRes.status).toBe('success'); - expect(server.requests.length).toBe(2); - - try { - await transport.sendEvent(eventPayload); - throw new Error('unreachable!'); - } catch (res) { - expect((res as Response).status).toBe(429); - expect((res as Response).reason).toBe( - `Transport for event requests locked till ${new Date(afterLimit)} due to too many requests.`, - ); - expect(server.requests.length).toBe(2); - } - - server.respondWith('POST', storeUrl, [200, {}, '']); - - const eventRes = await transport.sendEvent(eventPayload); - expect(eventRes.status).toBe('success'); - expect(server.requests.length).toBe(3); - }); - - it('back-off using X-Sentry-Rate-Limits with multiple categories', async () => { - const retryAfterSeconds = 10; - const beforeLimit = Date.now(); - const withinLimit = beforeLimit + (retryAfterSeconds / 2) * 1000; - const afterLimit = beforeLimit + retryAfterSeconds * 1000; - - server.respondWith('POST', storeUrl, [ - 429, - { 'X-Sentry-Rate-Limits': `${retryAfterSeconds}:error;transaction:scope` }, - '', - ]); - server.respondWith('POST', envelopeUrl, [200, {}, '']); - - jest - .spyOn(Date, 'now') - // 1st event - _isRateLimited - false - .mockImplementationOnce(() => beforeLimit) - // 1st event - _handleRateLimit - .mockImplementationOnce(() => beforeLimit) - // 1st event - _isRateLimited - .mockImplementationOnce(() => beforeLimit) - // 2nd event - _isRateLimited - true (event category) - .mockImplementationOnce(() => withinLimit) - // 3rd event - _isRateLimited - true (transaction category) - .mockImplementationOnce(() => withinLimit) - // 4th event - _isRateLimited - false (event category) - .mockImplementationOnce(() => afterLimit) - // 4th event - _handleRateLimit - .mockImplementationOnce(() => afterLimit) - // 5th event - _isRateLimited - false (transaction category) - .mockImplementationOnce(() => afterLimit) - // 5th event - _handleRateLimit - .mockImplementationOnce(() => afterLimit); - - try { - await transport.sendEvent(eventPayload); - throw new Error('unreachable!'); - } catch (res) { - expect((res as Response).status).toBe(429); - expect((res as Response).reason).toBeUndefined(); - expect(server.requests.length).toBe(1); - } - - try { - await transport.sendEvent(eventPayload); - throw new Error('unreachable!'); - } catch (res) { - expect((res as Response).status).toBe(429); - expect((res as Response).reason).toBe( - `Transport for event requests locked till ${new Date(afterLimit)} due to too many requests.`, - ); - expect(server.requests.length).toBe(1); - } - - try { - await transport.sendEvent(transactionPayload); - throw new Error('unreachable!'); - } catch (res) { - expect((res as Response).status).toBe(429); - expect((res as Response).reason).toBe( - `Transport for transaction requests locked till ${new Date(afterLimit)} due to too many requests.`, - ); - expect(server.requests.length).toBe(1); - } - - server.respondWith('POST', storeUrl, [200, {}, '']); - server.respondWith('POST', envelopeUrl, [200, {}, '']); - - const eventRes = await transport.sendEvent(eventPayload); - expect(eventRes.status).toBe('success'); - expect(server.requests.length).toBe(2); - - const transactionRes = await transport.sendEvent(transactionPayload); - expect(transactionRes.status).toBe('success'); - expect(server.requests.length).toBe(3); - }); - - it('back-off using X-Sentry-Rate-Limits with missing categories should lock them all', async () => { - const retryAfterSeconds = 10; - const beforeLimit = Date.now(); - const withinLimit = beforeLimit + (retryAfterSeconds / 2) * 1000; - const afterLimit = beforeLimit + retryAfterSeconds * 1000; - - server.respondWith('POST', storeUrl, [429, { 'X-Sentry-Rate-Limits': `${retryAfterSeconds}::scope` }, '']); - server.respondWith('POST', envelopeUrl, [200, {}, '']); - - jest - .spyOn(Date, 'now') - // 1st event - _isRateLimited - false - .mockImplementationOnce(() => beforeLimit) - // 1st event - _handleRateLimit - .mockImplementationOnce(() => beforeLimit) - // 1st event - _isRateLimited - .mockImplementationOnce(() => beforeLimit) - // 2nd event - _isRateLimited - true (event category) - .mockImplementationOnce(() => withinLimit) - // 3rd event - _isRateLimited - true (transaction category) - .mockImplementationOnce(() => withinLimit) - // 4th event - _isRateLimited - false (event category) - .mockImplementationOnce(() => afterLimit) - // 4th event - _handleRateLimit - .mockImplementationOnce(() => afterLimit) - // 5th event - _isRateLimited - false (transaction category) - .mockImplementationOnce(() => afterLimit) - // 5th event - _handleRateLimit - .mockImplementationOnce(() => afterLimit); - - try { - await transport.sendEvent(eventPayload); - throw new Error('unreachable!'); - } catch (res) { - expect((res as Response).status).toBe(429); - expect((res as Response).reason).toBeUndefined(); - expect(server.requests.length).toBe(1); - } - - try { - await transport.sendEvent(eventPayload); - throw new Error('unreachable!'); - } catch (res) { - expect((res as Response).status).toBe(429); - expect((res as Response).reason).toBe( - `Transport for event requests locked till ${new Date(afterLimit)} due to too many requests.`, - ); - expect(server.requests.length).toBe(1); - } - - try { - await transport.sendEvent(transactionPayload); - throw new Error('unreachable!'); - } catch (res) { - expect((res as Response).status).toBe(429); - expect((res as Response).reason).toBe( - `Transport for transaction requests locked till ${new Date(afterLimit)} due to too many requests.`, - ); - expect(server.requests.length).toBe(1); - } - - server.respondWith('POST', storeUrl, [200, {}, '']); - server.respondWith('POST', envelopeUrl, [200, {}, '']); - - const eventRes = await transport.sendEvent(eventPayload); - expect(eventRes.status).toBe('success'); - expect(server.requests.length).toBe(2); - - const transactionRes = await transport.sendEvent(transactionPayload); - expect(transactionRes.status).toBe('success'); - expect(server.requests.length).toBe(3); - }); + afterEach(() => { + jest.clearAllMocks(); + }); - it('back-off using X-Sentry-Rate-Limits should also trigger for 200 responses', async () => { - const retryAfterSeconds = 10; - const beforeLimit = Date.now(); - const withinLimit = beforeLimit + (retryAfterSeconds / 2) * 1000; - const afterLimit = beforeLimit + retryAfterSeconds * 1000; + afterAll(() => { + jest.restoreAllMocks(); + }); - server.respondWith('POST', storeUrl, [200, { 'X-Sentry-Rate-Limits': `${retryAfterSeconds}:error:scope` }, '']); + it('makes an XHR request to the given URL', async () => { + const transport = makeXHRTransport(DEFAULT_XHR_TRANSPORT_OPTIONS); + expect(xhrMock.open).toHaveBeenCalledTimes(0); + expect(xhrMock.setRequestHeader).toHaveBeenCalledTimes(0); + expect(xhrMock.send).toHaveBeenCalledTimes(0); - jest - .spyOn(Date, 'now') - // 1st event - _isRateLimited - false - .mockImplementationOnce(() => beforeLimit) - // 1st event - _handleRateLimit - .mockImplementationOnce(() => beforeLimit) - // 1st event - _handleRateLimit - .mockImplementationOnce(() => beforeLimit) - // 2nd event - _isRateLimited - true - .mockImplementationOnce(() => withinLimit) - // 3rd event - _isRateLimited - false - .mockImplementationOnce(() => afterLimit) - // 3rd event - _handleRateLimit - .mockImplementationOnce(() => afterLimit); + await Promise.all([transport.send(ERROR_ENVELOPE), (xhrMock as XMLHttpRequest).onreadystatechange!({} as Event)]); - let eventRes = await transport.sendEvent(eventPayload); - expect(eventRes.status).toBe('success'); - expect(server.requests.length).toBe(1); + expect(xhrMock.open).toHaveBeenCalledTimes(1); + expect(xhrMock.open).toHaveBeenCalledWith('POST', DEFAULT_XHR_TRANSPORT_OPTIONS.url); + expect(xhrMock.send).toHaveBeenCalledTimes(1); + expect(xhrMock.send).toHaveBeenCalledWith(serializeEnvelope(ERROR_ENVELOPE)); + }); - try { - await transport.sendEvent(eventPayload); - throw new Error('unreachable!'); - } catch (res) { - expect((res as Response).status).toBe(429); - expect((res as Response).reason).toBe( - `Transport for event requests locked till ${new Date(afterLimit)} due to too many requests.`, - ); - expect(server.requests.length).toBe(1); - } + it('returns the correct response', async () => { + const transport = makeXHRTransport(DEFAULT_XHR_TRANSPORT_OPTIONS); - server.respondWith('POST', storeUrl, [200, {}, '']); + const [res] = await Promise.all([ + transport.send(ERROR_ENVELOPE), + (xhrMock as XMLHttpRequest).onreadystatechange!({} as Event), + ]); - eventRes = await transport.sendEvent(eventPayload); - expect(eventRes.status).toBe('success'); - expect(server.requests.length).toBe(2); - }); + expect(res).toBeDefined(); + expect(res.status).toEqual('success'); + }); - it('should record dropped event', async () => { - // @ts-ignore private method - jest.spyOn(transport, '_isRateLimited').mockReturnValue(true); + it('sets rate limit response headers', async () => { + const transport = makeXHRTransport(DEFAULT_XHR_TRANSPORT_OPTIONS); - const spy = jest.spyOn(transport, 'recordLostEvent'); + await Promise.all([transport.send(ERROR_ENVELOPE), (xhrMock as XMLHttpRequest).onreadystatechange!({} as Event)]); - try { - await transport.sendEvent(eventPayload); - } catch (_) { - expect(spy).toHaveBeenCalledWith('ratelimit_backoff', 'event'); - } + expect(xhrMock.getResponseHeader).toHaveBeenCalledTimes(2); + expect(xhrMock.getResponseHeader).toHaveBeenCalledWith('X-Sentry-Rate-Limits'); + expect(xhrMock.getResponseHeader).toHaveBeenCalledWith('Retry-After'); + }); - try { - await transport.sendEvent(transactionPayload); - } catch (_) { - expect(spy).toHaveBeenCalledWith('ratelimit_backoff', 'transaction'); - } - }); - }); + it('sets custom request headers', async () => { + const headers = { + referrerPolicy: 'strict-origin', + keepalive: 'true', + referrer: 'http://example.org', + }; + const options: XHRTransportOptions = { + ...DEFAULT_XHR_TRANSPORT_OPTIONS, + headers, + }; + + const transport = makeXHRTransport(options); + await Promise.all([transport.send(ERROR_ENVELOPE), (xhrMock as XMLHttpRequest).onreadystatechange!({} as Event)]); + + expect(xhrMock.setRequestHeader).toHaveBeenCalledTimes(3); + expect(xhrMock.setRequestHeader).toHaveBeenCalledWith('referrerPolicy', headers.referrerPolicy); + expect(xhrMock.setRequestHeader).toHaveBeenCalledWith('keepalive', headers.keepalive); + expect(xhrMock.setRequestHeader).toHaveBeenCalledWith('referrer', headers.referrer); }); }); diff --git a/packages/core/src/baseclient.ts b/packages/core/src/baseclient.ts index e2502289742a..833b40aaf9c7 100644 --- a/packages/core/src/baseclient.ts +++ b/packages/core/src/baseclient.ts @@ -9,7 +9,6 @@ import { EventHint, Integration, IntegrationClass, - NewTransport, SessionAggregates, Severity, SeverityLevel, @@ -77,7 +76,7 @@ export abstract class BaseClient implements Client { /** The client Dsn, if specified in options. Without this Dsn, the SDK will be disabled. */ protected readonly _dsn?: DsnComponents; - protected readonly _transport?: NewTransport; + protected readonly _transport?: Transport; /** Array of set up integrations. */ protected _integrations: IntegrationIndex = {}; @@ -92,8 +91,6 @@ export abstract class BaseClient implements Client { * Initializes this client instance. * * @param options Options for the client. - * @param transport The (old) Transport instance for the client to use (TODO(v7): remove) - * @param newTransport The NewTransport instance for the client to use */ protected constructor(options: O) { this._options = options; @@ -213,7 +210,7 @@ export abstract class BaseClient implements Client { /** * @inheritDoc */ - public getTransport(): NewTransport | undefined { + public getTransport(): Transport | undefined { return this._transport; } @@ -567,12 +564,12 @@ export abstract class BaseClient implements Client { // eslint-disable-next-line @typescript-eslint/unbound-method const { beforeSend, sampleRate } = this.getOptions(); - type RecordLostEvent = NonNullable; - type RecordLostEventParams = Parameters; + // type RecordLostEvent = NonNullable; + type RecordLostEventParams = unknown[]; // Parameters; + // no-op as new transports don't have client outcomes + // TODO(v7): Re-add this functionality function recordLostEvent(_outcome: RecordLostEventParams[0], _category: RecordLostEventParams[1]): void { - // no-op as new transports don't have client outcomes - // TODO(v7): Re-add this functionality // if (transport.recordLostEvent) { // transport.recordLostEvent(outcome, category); // } diff --git a/packages/core/src/index.ts b/packages/core/src/index.ts index f5932bdbeaa4..0b0585a3da68 100644 --- a/packages/core/src/index.ts +++ b/packages/core/src/index.ts @@ -27,7 +27,6 @@ export { export { BaseClient } from './baseclient'; export { eventToSentryRequest, sessionToSentryRequest } from './request'; export { initAndBind } from './sdk'; -export { NoopTransport } from './transports/noop'; export { createTransport } from './transports/base'; export { SDK_VERSION } from './version'; export { getIntegrationsToSetup } from './integration'; diff --git a/packages/core/src/transports/base.ts b/packages/core/src/transports/base.ts index 8c6cfe373bfe..97389ea9a45a 100644 --- a/packages/core/src/transports/base.ts +++ b/packages/core/src/transports/base.ts @@ -1,7 +1,7 @@ import { Envelope, InternalBaseTransportOptions, - NewTransport, + Transport, TransportCategory, TransportRequest, TransportRequestExecutor, @@ -24,7 +24,7 @@ import { export const DEFAULT_TRANSPORT_BUFFER_SIZE = 30; /** - * Creates a `NewTransport` + * Creates an instance of a Sentry `Transport` * * @param options * @param makeRequest @@ -33,7 +33,7 @@ export function createTransport( options: InternalBaseTransportOptions, makeRequest: TransportRequestExecutor, buffer: PromiseBuffer = makePromiseBuffer(options.bufferSize || DEFAULT_TRANSPORT_BUFFER_SIZE), -): NewTransport { +): Transport { let rateLimits: RateLimits = {}; const flush = (timeout?: number): PromiseLike => buffer.drain(timeout); diff --git a/packages/core/src/transports/noop.ts b/packages/core/src/transports/noop.ts deleted file mode 100644 index 8ea66c7b8a4e..000000000000 --- a/packages/core/src/transports/noop.ts +++ /dev/null @@ -1,22 +0,0 @@ -import { Event, Response, Transport } from '@sentry/types'; -import { resolvedSyncPromise } from '@sentry/utils'; - -/** Noop transport */ -export class NoopTransport implements Transport { - /** - * @inheritDoc - */ - public sendEvent(_: Event): PromiseLike { - return resolvedSyncPromise({ - reason: 'NoopTransport: Event has been skipped because no Dsn is configured.', - status: 'skipped', - }); - } - - /** - * @inheritDoc - */ - public close(_?: number): PromiseLike { - return resolvedSyncPromise(true); - } -} diff --git a/packages/core/test/lib/transports/base.test.ts b/packages/core/test/lib/transports/base.test.ts index 78b40d0a4f7c..8b4dc8965c02 100644 --- a/packages/core/test/lib/transports/base.test.ts +++ b/packages/core/test/lib/transports/base.test.ts @@ -1,4 +1,4 @@ -import { EventEnvelope, EventItem, NewTransport, TransportMakeRequestResponse, TransportResponse } from '@sentry/types'; +import { EventEnvelope, EventItem, Transport, TransportMakeRequestResponse, TransportResponse } from '@sentry/types'; import { createEnvelope, PromiseBuffer, resolvedSyncPromise, serializeEnvelope } from '@sentry/utils'; import { createTransport } from '../../../src/transports/base'; @@ -88,7 +88,7 @@ describe('createTransport', () => { function createTestTransport( initialTransportResponse: TransportMakeRequestResponse, - ): [NewTransport, (res: TransportMakeRequestResponse) => void] { + ): [Transport, (res: TransportMakeRequestResponse) => void] { let transportResponse: TransportMakeRequestResponse = initialTransportResponse; function setTransportResponse(res: TransportMakeRequestResponse) { diff --git a/packages/core/test/mocks/client.ts b/packages/core/test/mocks/client.ts index 9f02e5b6540d..076b1f7b34c2 100644 --- a/packages/core/test/mocks/client.ts +++ b/packages/core/test/mocks/client.ts @@ -6,6 +6,10 @@ import { BaseClient } from '../../src/baseclient'; import { initAndBind } from '../../src/sdk'; import { createTransport } from '../../src/transports/base'; +// TODO(v7): Add client reports tests to this file +// See old tests in packages/browser/test/unit/transports/base.test.ts +// from https://github.com/getsentry/sentry-javascript/pull/4967 + export function getDefaultTestClientOptions(options: Partial = {}): TestClientOptions { return { integrations: [], diff --git a/packages/node-integration-tests/suites/public-api/startTransaction/new-transport/scenario.ts b/packages/node-integration-tests/suites/public-api/startTransaction/new-transport/scenario.ts deleted file mode 100644 index 82ae5e905410..000000000000 --- a/packages/node-integration-tests/suites/public-api/startTransaction/new-transport/scenario.ts +++ /dev/null @@ -1,17 +0,0 @@ -// eslint-disable-next-line @typescript-eslint/no-unused-vars -import '@sentry/tracing'; - -import * as Sentry from '@sentry/node'; - -Sentry.init({ - dsn: 'https://public@dsn.ingest.sentry.io/1337', - release: '1.0', - tracesSampleRate: 1.0, - _experiments: { - newTransport: true, // use new transport - }, -}); - -const transaction = Sentry.startTransaction({ name: 'test_transaction_1' }); - -transaction.finish(); diff --git a/packages/node-integration-tests/suites/public-api/startTransaction/new-transport/test.ts b/packages/node-integration-tests/suites/public-api/startTransaction/new-transport/test.ts deleted file mode 100644 index 62b8d8fb4402..000000000000 --- a/packages/node-integration-tests/suites/public-api/startTransaction/new-transport/test.ts +++ /dev/null @@ -1,12 +0,0 @@ -import { assertSentryTransaction, getEnvelopeRequest, runServer } from '../../../../utils'; - -test('should send a manually started transaction when @sentry/tracing is imported using unnamed import', async () => { - const url = await runServer(__dirname); - const envelope = await getEnvelopeRequest(url); - - expect(envelope).toHaveLength(3); - - assertSentryTransaction(envelope[2], { - transaction: 'test_transaction_1', - }); -}); diff --git a/packages/node/src/transports/base/index.ts b/packages/node/src/transports/base/index.ts deleted file mode 100644 index 0cbe39c42b2b..000000000000 --- a/packages/node/src/transports/base/index.ts +++ /dev/null @@ -1,270 +0,0 @@ -import { APIDetails, getRequestHeaders, initAPIDetails, SDK_VERSION } from '@sentry/core'; -import { - DsnProtocol, - Event, - Response, - SentryRequest, - SentryRequestType, - Session, - SessionAggregates, - Transport, - TransportOptions, -} from '@sentry/types'; -import { - eventStatusFromHttpCode, - logger, - makePromiseBuffer, - parseRetryAfterHeader, - PromiseBuffer, - SentryError, -} from '@sentry/utils'; -import * as fs from 'fs'; -import * as http from 'http'; -import * as https from 'https'; -import { URL } from 'url'; - -import { IS_DEBUG_BUILD } from '../../flags'; -import { SDK_NAME } from '../../version'; -import { HTTPModule } from './http-module'; - -export type URLParts = Pick; -export type UrlParser = (url: string) => URLParts; - -const CATEGORY_MAPPING: { - [key in SentryRequestType]: string; -} = { - event: 'error', - transaction: 'transaction', - session: 'session', - attachment: 'attachment', -}; - -/** Base Transport class implementation */ -export abstract class BaseTransport implements Transport { - /** The Agent used for corresponding transport */ - public module?: HTTPModule; - - /** The Agent used for corresponding transport */ - public client?: http.Agent | https.Agent; - - /** API object */ - protected _api: APIDetails; - - /** A simple buffer holding all requests. */ - protected readonly _buffer: PromiseBuffer = makePromiseBuffer(30); - - /** Locks transport after receiving rate limits in a response */ - protected readonly _rateLimits: Record = {}; - - /** Create instance and set this.dsn */ - public constructor(public options: TransportOptions) { - // eslint-disable-next-line deprecation/deprecation - this._api = initAPIDetails(options.dsn, options._metadata, options.tunnel); - } - - /** Default function used to parse URLs */ - public urlParser: UrlParser = url => new URL(url); - - /** - * @inheritDoc - */ - public sendEvent(_: Event): PromiseLike { - throw new SentryError('Transport Class has to implement `sendEvent` method.'); - } - - /** - * @inheritDoc - */ - public close(timeout?: number): PromiseLike { - return this._buffer.drain(timeout); - } - - /** - * Extracts proxy settings from client options and env variables. - * - * Honors `no_proxy` env variable with the highest priority to allow for hosts exclusion. - * - * An order of priority for available protocols is: - * `http` => `options.httpProxy` | `process.env.http_proxy` - * `https` => `options.httpsProxy` | `options.httpProxy` | `process.env.https_proxy` | `process.env.http_proxy` - */ - protected _getProxy(protocol: DsnProtocol): string | undefined { - const { no_proxy, http_proxy, https_proxy } = process.env; - const { httpProxy, httpsProxy } = this.options; - const proxy = protocol === 'http' ? httpProxy || http_proxy : httpsProxy || httpProxy || https_proxy || http_proxy; - - if (!no_proxy) { - return proxy; - } - - const { host, port } = this._api.dsn; - for (const np of no_proxy.split(',')) { - if (host.endsWith(np) || `${host}:${port}`.endsWith(np)) { - return; - } - } - - return proxy; - } - - /** Returns a build request option object used by request */ - protected _getRequestOptions(urlParts: URLParts): http.RequestOptions | https.RequestOptions { - const headers = { - ...getRequestHeaders(this._api.dsn, SDK_NAME, SDK_VERSION), - ...this.options.headers, - }; - const { hostname, pathname, port, protocol } = urlParts; - // See https://github.com/nodejs/node/blob/38146e717fed2fabe3aacb6540d839475e0ce1c6/lib/internal/url.js#L1268-L1290 - // We ignore the query string on purpose - const path = `${pathname}`; - - return { - agent: this.client, - headers, - hostname, - method: 'POST', - path, - port, - protocol, - ...(this.options.caCerts && { - ca: fs.readFileSync(this.options.caCerts), - }), - }; - } - - /** - * Gets the time that given category is disabled until for rate limiting - */ - protected _disabledUntil(requestType: SentryRequestType): Date { - const category = CATEGORY_MAPPING[requestType]; - return this._rateLimits[category] || this._rateLimits.all; - } - - /** - * Checks if a category is rate limited - */ - protected _isRateLimited(requestType: SentryRequestType): boolean { - return this._disabledUntil(requestType) > new Date(Date.now()); - } - - /** - * Sets internal _rateLimits from incoming headers. Returns true if headers contains a non-empty rate limiting header. - */ - protected _handleRateLimit(headers: Record): boolean { - const now = Date.now(); - const rlHeader = headers['x-sentry-rate-limits']; - const raHeader = headers['retry-after']; - - if (rlHeader) { - // rate limit headers are of the form - //
,
,.. - // where each
is of the form - // : : : - // where - // is a delay in ms - // is the event type(s) (error, transaction, etc) being rate limited and is of the form - // ;;... - // is what's being limited (org, project, or key) - ignored by SDK - // is an arbitrary string like "org_quota" - ignored by SDK - for (const limit of rlHeader.trim().split(',')) { - const parameters = limit.split(':', 2); - const headerDelay = parseInt(parameters[0], 10); - const delay = (!isNaN(headerDelay) ? headerDelay : 60) * 1000; // 60sec default - for (const category of (parameters[1] && parameters[1].split(';')) || ['all']) { - // categoriesAllowed is added here to ensure we are only storing rate limits for categories we support in this - // sdk and any categories that are not supported will not be added redundantly to the rateLimits object - const categoriesAllowed = [ - ...(Object.keys(CATEGORY_MAPPING) as [SentryRequestType]).map(k => CATEGORY_MAPPING[k]), - 'all', - ]; - if (categoriesAllowed.includes(category)) this._rateLimits[category] = new Date(now + delay); - } - } - return true; - } else if (raHeader) { - this._rateLimits.all = new Date(now + parseRetryAfterHeader(raHeader, now)); - return true; - } - return false; - } - - /** JSDoc */ - protected async _send( - sentryRequest: SentryRequest, - originalPayload?: Event | Session | SessionAggregates, - ): Promise { - if (!this.module) { - throw new SentryError('No module available'); - } - if (originalPayload && this._isRateLimited(sentryRequest.type)) { - return Promise.reject({ - payload: originalPayload, - type: sentryRequest.type, - reason: `Transport for ${sentryRequest.type} requests locked till ${this._disabledUntil( - sentryRequest.type, - )} due to too many requests.`, - status: 429, - }); - } - - return this._buffer.add( - () => - new Promise((resolve, reject) => { - if (!this.module) { - throw new SentryError('No module available'); - } - const options = this._getRequestOptions(this.urlParser(sentryRequest.url)); - const req = this.module.request(options, res => { - const statusCode = res.statusCode || 500; - const status = eventStatusFromHttpCode(statusCode); - - res.setEncoding('utf8'); - - /** - * "Key-value pairs of header names and values. Header names are lower-cased." - * https://nodejs.org/api/http.html#http_message_headers - */ - let retryAfterHeader = res.headers ? res.headers['retry-after'] : ''; - retryAfterHeader = (Array.isArray(retryAfterHeader) ? retryAfterHeader[0] : retryAfterHeader) as string; - - let rlHeader = res.headers ? res.headers['x-sentry-rate-limits'] : ''; - rlHeader = (Array.isArray(rlHeader) ? rlHeader[0] : rlHeader) as string; - - const headers = { - 'x-sentry-rate-limits': rlHeader, - 'retry-after': retryAfterHeader, - }; - - const limited = this._handleRateLimit(headers); - if (limited) - IS_DEBUG_BUILD && - logger.warn( - `Too many ${sentryRequest.type} requests, backing off until: ${this._disabledUntil( - sentryRequest.type, - )}`, - ); - - if (status === 'success') { - resolve({ status }); - } else { - let rejectionMessage = `HTTP Error (${statusCode})`; - if (res.headers && res.headers['x-sentry-error']) { - rejectionMessage += `: ${res.headers['x-sentry-error']}`; - } - reject(new SentryError(rejectionMessage)); - } - - // Force the socket to drain - res.on('data', () => { - // Drain - }); - res.on('end', () => { - // Drain - }); - }); - req.on('error', reject); - req.end(sentryRequest.body); - }), - ); - } -} diff --git a/packages/node/src/transports/base/http-module.ts b/packages/node/src/transports/http-module.ts similarity index 100% rename from packages/node/src/transports/base/http-module.ts rename to packages/node/src/transports/http-module.ts diff --git a/packages/node/src/transports/http.ts b/packages/node/src/transports/http.ts index a9234d830876..0352a8232e04 100644 --- a/packages/node/src/transports/http.ts +++ b/packages/node/src/transports/http.ts @@ -1,32 +1,134 @@ -import { eventToSentryRequest, sessionToSentryRequest } from '@sentry/core'; -import { Event, Response, Session, SessionAggregates, TransportOptions } from '@sentry/types'; +import { createTransport } from '@sentry/core'; +import { + BaseTransportOptions, + Transport, + TransportMakeRequestResponse, + TransportRequest, + TransportRequestExecutor, +} from '@sentry/types'; +import { eventStatusFromHttpCode } from '@sentry/utils'; import * as http from 'http'; +import * as https from 'https'; +import { URL } from 'url'; -import { BaseTransport } from './base'; - -/** Node http module transport */ -export class HTTPTransport extends BaseTransport { - /** Create a new instance and set this.agent */ - public constructor(public options: TransportOptions) { - super(options); - const proxy = this._getProxy('http'); - this.module = http; - this.client = proxy - ? (new (require('https-proxy-agent'))(proxy) as http.Agent) - : new http.Agent({ keepAlive: false, maxSockets: 30, timeout: 2000 }); - } +import { HTTPModule } from './http-module'; - /** - * @inheritDoc - */ - public sendEvent(event: Event): Promise { - return this._send(eventToSentryRequest(event, this._api), event); - } +export interface NodeTransportOptions extends BaseTransportOptions { + /** Define custom headers */ + headers?: Record; + /** Set a proxy that should be used for outbound requests. */ + proxy?: string; + /** HTTPS proxy CA certificates */ + caCerts?: string | Buffer | Array; + /** Custom HTTP module. Defaults to the native 'http' and 'https' modules. */ + httpModule?: HTTPModule; +} + +/** + * Creates a Transport that uses native the native 'http' and 'https' modules to send events to Sentry. + */ +export function makeNodeTransport(options: NodeTransportOptions): Transport { + const urlSegments = new URL(options.url); + const isHttps = urlSegments.protocol === 'https:'; + + // Proxy prioritization: http => `options.proxy` | `process.env.http_proxy` + // Proxy prioritization: https => `options.proxy` | `process.env.https_proxy` | `process.env.http_proxy` + const proxy = applyNoProxyOption( + urlSegments, + options.proxy || (isHttps ? process.env.https_proxy : undefined) || process.env.http_proxy, + ); + + const nativeHttpModule = isHttps ? https : http; + + // TODO(v7): Evaluate if we can set keepAlive to true. This would involve testing for memory leaks in older node + // versions(>= 8) as they had memory leaks when using it: #2555 + const agent = proxy + ? (new (require('https-proxy-agent'))(proxy) as http.Agent) + : new nativeHttpModule.Agent({ keepAlive: false, maxSockets: 30, timeout: 2000 }); + + const requestExecutor = createRequestExecutor(options, options.httpModule ?? nativeHttpModule, agent); + return createTransport({ bufferSize: options.bufferSize }, requestExecutor); +} + +/** + * Honors the `no_proxy` env variable with the highest priority to allow for hosts exclusion. + * + * @param transportUrl The URL the transport intends to send events to. + * @param proxy The client configured proxy. + * @returns A proxy the transport should use. + */ +function applyNoProxyOption(transportUrlSegments: URL, proxy: string | undefined): string | undefined { + const { no_proxy } = process.env; - /** - * @inheritDoc - */ - public sendSession(session: Session | SessionAggregates): PromiseLike { - return this._send(sessionToSentryRequest(session, this._api), session); + const urlIsExemptFromProxy = + no_proxy && + no_proxy + .split(',') + .some( + exemption => transportUrlSegments.host.endsWith(exemption) || transportUrlSegments.hostname.endsWith(exemption), + ); + + if (urlIsExemptFromProxy) { + return undefined; + } else { + return proxy; } } + +/** + * Creates a RequestExecutor to be used with `createTransport`. + */ +function createRequestExecutor( + options: NodeTransportOptions, + httpModule: HTTPModule, + agent: http.Agent, +): TransportRequestExecutor { + const { hostname, pathname, port, protocol, search } = new URL(options.url); + return function makeRequest(request: TransportRequest): Promise { + return new Promise((resolve, reject) => { + const req = httpModule.request( + { + method: 'POST', + agent, + headers: options.headers, + hostname, + path: `${pathname}${search}`, + port, + protocol, + ca: options.caCerts, + }, + res => { + res.on('data', () => { + // Drain socket + }); + + res.on('end', () => { + // Drain socket + }); + + const statusCode = res.statusCode ?? 500; + const status = eventStatusFromHttpCode(statusCode); + + res.setEncoding('utf8'); + + // "Key-value pairs of header names and values. Header names are lower-cased." + // https://nodejs.org/api/http.html#http_message_headers + const retryAfterHeader = res.headers['retry-after'] ?? null; + const rateLimitsHeader = res.headers['x-sentry-rate-limits'] ?? null; + + resolve({ + headers: { + 'retry-after': retryAfterHeader, + 'x-sentry-rate-limits': Array.isArray(rateLimitsHeader) ? rateLimitsHeader[0] : rateLimitsHeader, + }, + reason: status, + statusCode: statusCode, + }); + }, + ); + + req.on('error', reject); + req.end(request.body); + }); + }; +} diff --git a/packages/node/src/transports/https.ts b/packages/node/src/transports/https.ts deleted file mode 100644 index d6c312608504..000000000000 --- a/packages/node/src/transports/https.ts +++ /dev/null @@ -1,32 +0,0 @@ -import { eventToSentryRequest, sessionToSentryRequest } from '@sentry/core'; -import { Event, Response, Session, SessionAggregates, TransportOptions } from '@sentry/types'; -import * as https from 'https'; - -import { BaseTransport } from './base'; - -/** Node https module transport */ -export class HTTPSTransport extends BaseTransport { - /** Create a new instance and set this.agent */ - public constructor(public options: TransportOptions) { - super(options); - const proxy = this._getProxy('https'); - this.module = https; - this.client = proxy - ? (new (require('https-proxy-agent'))(proxy) as https.Agent) - : new https.Agent({ keepAlive: false, maxSockets: 30, timeout: 2000 }); - } - - /** - * @inheritDoc - */ - public sendEvent(event: Event): Promise { - return this._send(eventToSentryRequest(event, this._api), event); - } - - /** - * @inheritDoc - */ - public sendSession(session: Session | SessionAggregates): PromiseLike { - return this._send(sessionToSentryRequest(session, this._api), session); - } -} diff --git a/packages/node/src/transports/index.ts b/packages/node/src/transports/index.ts index 958562933321..ba59ba8878a4 100644 --- a/packages/node/src/transports/index.ts +++ b/packages/node/src/transports/index.ts @@ -1,6 +1,3 @@ -export type { NodeTransportOptions } from './new'; +export type { NodeTransportOptions } from './http'; -export { BaseTransport } from './base'; -export { HTTPTransport } from './http'; -export { HTTPSTransport } from './https'; -export { makeNodeTransport } from './new'; +export { makeNodeTransport } from './http'; diff --git a/packages/node/src/transports/new.ts b/packages/node/src/transports/new.ts deleted file mode 100644 index e31a58b03b7b..000000000000 --- a/packages/node/src/transports/new.ts +++ /dev/null @@ -1,141 +0,0 @@ -import { createTransport } from '@sentry/core'; -import { - BaseTransportOptions, - NewTransport, - TransportMakeRequestResponse, - TransportRequest, - TransportRequestExecutor, -} from '@sentry/types'; -import { eventStatusFromHttpCode } from '@sentry/utils'; -import * as http from 'http'; -import * as https from 'https'; -import { URL } from 'url'; - -import { HTTPModule } from './base/http-module'; - -// TODO(v7): -// - Rename this file "transport.ts" -// - Move this file one folder upwards -// - Delete "transports" folder -// OR -// - Split this file up and leave it in the transports folder - -export interface NodeTransportOptions extends BaseTransportOptions { - /** Define custom headers */ - headers?: Record; - /** Set a proxy that should be used for outbound requests. */ - proxy?: string; - /** HTTPS proxy CA certificates */ - caCerts?: string | Buffer | Array; - /** Custom HTTP module. Defaults to the native 'http' and 'https' modules. */ - httpModule?: HTTPModule; -} - -/** - * Creates a Transport that uses native the native 'http' and 'https' modules to send events to Sentry. - */ -export function makeNodeTransport(options: NodeTransportOptions): NewTransport { - const urlSegments = new URL(options.url); - const isHttps = urlSegments.protocol === 'https:'; - - // Proxy prioritization: http => `options.proxy` | `process.env.http_proxy` - // Proxy prioritization: https => `options.proxy` | `process.env.https_proxy` | `process.env.http_proxy` - const proxy = applyNoProxyOption( - urlSegments, - options.proxy || (isHttps ? process.env.https_proxy : undefined) || process.env.http_proxy, - ); - - const nativeHttpModule = isHttps ? https : http; - - // TODO(v7): Evaluate if we can set keepAlive to true. This would involve testing for memory leaks in older node - // versions(>= 8) as they had memory leaks when using it: #2555 - const agent = proxy - ? (new (require('https-proxy-agent'))(proxy) as http.Agent) - : new nativeHttpModule.Agent({ keepAlive: false, maxSockets: 30, timeout: 2000 }); - - const requestExecutor = createRequestExecutor(options, options.httpModule ?? nativeHttpModule, agent); - return createTransport({ bufferSize: options.bufferSize }, requestExecutor); -} - -/** - * Honors the `no_proxy` env variable with the highest priority to allow for hosts exclusion. - * - * @param transportUrl The URL the transport intends to send events to. - * @param proxy The client configured proxy. - * @returns A proxy the transport should use. - */ -function applyNoProxyOption(transportUrlSegments: URL, proxy: string | undefined): string | undefined { - const { no_proxy } = process.env; - - const urlIsExemptFromProxy = - no_proxy && - no_proxy - .split(',') - .some( - exemption => transportUrlSegments.host.endsWith(exemption) || transportUrlSegments.hostname.endsWith(exemption), - ); - - if (urlIsExemptFromProxy) { - return undefined; - } else { - return proxy; - } -} - -/** - * Creates a RequestExecutor to be used with `createTransport`. - */ -function createRequestExecutor( - options: NodeTransportOptions, - httpModule: HTTPModule, - agent: http.Agent, -): TransportRequestExecutor { - const { hostname, pathname, port, protocol, search } = new URL(options.url); - return function makeRequest(request: TransportRequest): Promise { - return new Promise((resolve, reject) => { - const req = httpModule.request( - { - method: 'POST', - agent, - headers: options.headers, - hostname, - path: `${pathname}${search}`, - port, - protocol, - ca: options.caCerts, - }, - res => { - res.on('data', () => { - // Drain socket - }); - - res.on('end', () => { - // Drain socket - }); - - const statusCode = res.statusCode ?? 500; - const status = eventStatusFromHttpCode(statusCode); - - res.setEncoding('utf8'); - - // "Key-value pairs of header names and values. Header names are lower-cased." - // https://nodejs.org/api/http.html#http_message_headers - const retryAfterHeader = res.headers['retry-after'] ?? null; - const rateLimitsHeader = res.headers['x-sentry-rate-limits'] ?? null; - - resolve({ - headers: { - 'retry-after': retryAfterHeader, - 'x-sentry-rate-limits': Array.isArray(rateLimitsHeader) ? rateLimitsHeader[0] : rateLimitsHeader, - }, - reason: status, - statusCode: statusCode, - }); - }, - ); - - req.on('error', reject); - req.end(request.body); - }); - }; -} diff --git a/packages/node/test/transports/custom/index.test.ts b/packages/node/test/transports/custom/index.test.ts deleted file mode 100644 index 88295bbc7e9b..000000000000 --- a/packages/node/test/transports/custom/index.test.ts +++ /dev/null @@ -1,24 +0,0 @@ -import { CustomUrlTransport } from './transports'; - -describe('Custom transport', () => { - describe('URL parser support', () => { - const noop = () => null; - const sampleDsn = 'https://username@sentry.tld/path/1'; - - test('use URL parser for sendEvent() method', async () => { - const urlParser = jest.fn(); - const transport = new CustomUrlTransport({ dsn: sampleDsn }, urlParser); - await transport.sendEvent({}).catch(noop); - - expect(urlParser).toHaveBeenCalled(); - }); - - test('use URL parser for sendSession() method', async () => { - const urlParser = jest.fn(); - const transport = new CustomUrlTransport({ dsn: sampleDsn }, urlParser); - await transport.sendSession({ aggregates: [] }).then(noop, noop); - - expect(urlParser).toHaveBeenCalled(); - }); - }); -}); diff --git a/packages/node/test/transports/custom/transports.ts b/packages/node/test/transports/custom/transports.ts deleted file mode 100644 index e3305fbf8a20..000000000000 --- a/packages/node/test/transports/custom/transports.ts +++ /dev/null @@ -1,11 +0,0 @@ -import { TransportOptions } from '@sentry/types'; - -import { HTTPTransport } from '../../../src/transports'; -import { UrlParser } from '../../../src/transports/base'; - -export class CustomUrlTransport extends HTTPTransport { - public constructor(public options: TransportOptions, urlParser: UrlParser) { - super(options); - this.urlParser = urlParser; - } -} diff --git a/packages/node/test/transports/http.test.ts b/packages/node/test/transports/http.test.ts index 8c7abfa2ade2..d8e1f5889226 100644 --- a/packages/node/test/transports/http.test.ts +++ b/packages/node/test/transports/http.test.ts @@ -1,711 +1,346 @@ -import { Session } from '@sentry/hub'; -import { Event, SessionAggregates, TransportOptions } from '@sentry/types'; -import { SentryError } from '@sentry/utils'; +import { createTransport } from '@sentry/core'; +import { EventEnvelope, EventItem } from '@sentry/types'; +import { createEnvelope, serializeEnvelope } from '@sentry/utils'; import * as http from 'http'; -import * as HttpsProxyAgent from 'https-proxy-agent'; - -import { HTTPTransport } from '../../src/transports/http'; - -const mockSetEncoding = jest.fn(); -const dsn = 'http://9e9fd4523d784609a5fc0ebb1080592f@sentry.io:8989/mysubpath/50622'; -const storePath = '/mysubpath/api/50622/store/'; -const envelopePath = '/mysubpath/api/50622/envelope/'; -const tunnel = 'https://hello.com/world'; -const eventPayload: Event = { - event_id: '1337', -}; -const transactionPayload: Event = { - event_id: '42', - type: 'transaction', -}; -const sessionPayload: Session = { - environment: 'test', - release: '1.0', - sid: '353463243253453254', - errors: 0, - started: Date.now(), - timestamp: Date.now(), - init: true, - duration: 0, - status: 'exited', - update: jest.fn(), - close: jest.fn(), - toJSON: jest.fn(), - ignoreDuration: false, -}; -const sessionsPayload: SessionAggregates = { - attrs: { environment: 'test', release: '1.0' }, - aggregates: [{ started: '2021-03-17T16:00:00.000Z', exited: 1 }], -}; -let mockReturnCode = 200; -let mockHeaders = {}; - -function createTransport(options: TransportOptions): HTTPTransport { - const transport = new HTTPTransport(options); - transport.module = { - request: jest.fn().mockImplementation((_options: any, callback: any) => ({ - end: () => { - callback({ - headers: mockHeaders, - setEncoding: mockSetEncoding, - statusCode: mockReturnCode, - }); - }, - on: jest.fn(), - })), + +import { makeNodeTransport } from '../../src/transports'; + +jest.mock('@sentry/core', () => { + const actualCore = jest.requireActual('@sentry/core'); + return { + ...actualCore, + createTransport: jest.fn().mockImplementation(actualCore.createTransport), }; - return transport; -} +}); + +// eslint-disable-next-line @typescript-eslint/no-var-requires +const httpProxyAgent = require('https-proxy-agent'); +jest.mock('https-proxy-agent', () => { + return jest.fn().mockImplementation(() => new http.Agent({ keepAlive: false, maxSockets: 30, timeout: 2000 })); +}); + +const SUCCESS = 200; +const RATE_LIMIT = 429; +const INVALID = 400; +const FAILED = 500; -function assertBasicOptions(options: any, useEnvelope: boolean = false): void { - expect(options.headers['X-Sentry-Auth']).toContain('sentry_version'); - expect(options.headers['X-Sentry-Auth']).toContain('sentry_client'); - expect(options.headers['X-Sentry-Auth']).toContain('sentry_key'); - expect(options.port).toEqual('8989'); - expect(options.path).toEqual(useEnvelope ? envelopePath : storePath); - expect(options.hostname).toEqual('sentry.io'); +interface TestServerOptions { + statusCode: number; + responseHeaders?: Record; } -describe('HTTPTransport', () => { - beforeEach(() => { - mockReturnCode = 200; - mockHeaders = {}; - jest.clearAllMocks(); - }); +let testServer: http.Server | undefined; - test('send 200', async () => { - const transport = createTransport({ dsn }); - await transport.sendEvent({ - message: 'test', +function setupTestServer( + options: TestServerOptions, + requestInspector?: (req: http.IncomingMessage, body: string) => void, +) { + testServer = http.createServer((req, res) => { + let body = ''; + + req.on('data', data => { + body += data; }); - const requestOptions = (transport.module!.request as jest.Mock).mock.calls[0][0]; - assertBasicOptions(requestOptions); - expect(mockSetEncoding).toHaveBeenCalled(); - }); + req.on('end', () => { + requestInspector?.(req, body); + }); - test('send 400', async () => { - mockReturnCode = 400; - const transport = createTransport({ dsn }); + res.writeHead(options.statusCode, options.responseHeaders); + res.end(); - try { - await transport.sendEvent({ - message: 'test', - }); - } catch (e) { - const requestOptions = (transport.module!.request as jest.Mock).mock.calls[0][0]; - assertBasicOptions(requestOptions); - expect(e).toEqual(new SentryError(`HTTP Error (${mockReturnCode})`)); - } + // also terminate socket because keepalive hangs connection a bit + res.connection.end(); }); - test('send 200 session', async () => { - const transport = createTransport({ dsn }); - await transport.sendSession(new Session()); + testServer.listen(18099); - const requestOptions = (transport.module!.request as jest.Mock).mock.calls[0][0]; - assertBasicOptions(requestOptions, true); - expect(mockSetEncoding).toHaveBeenCalled(); + return new Promise(resolve => { + testServer?.on('listening', resolve); }); +} - test('send 400 session', async () => { - mockReturnCode = 400; - const transport = createTransport({ dsn }); +const TEST_SERVER_URL = 'http://localhost:18099'; - try { - await transport.sendSession(new Session()); - } catch (e) { - const requestOptions = (transport.module!.request as jest.Mock).mock.calls[0][0]; - assertBasicOptions(requestOptions, true); - expect(e).toEqual(new SentryError(`HTTP Error (${mockReturnCode})`)); - } - }); +const EVENT_ENVELOPE = createEnvelope({ event_id: 'aa3ff046696b4bc6b609ce6d28fde9e2', sent_at: '123' }, [ + [{ type: 'event' }, { event_id: 'aa3ff046696b4bc6b609ce6d28fde9e2' }] as EventItem, +]); - test('send 200 request mode sessions', async () => { - const transport = createTransport({ dsn }); - await transport.sendSession(sessionsPayload); +const SERIALIZED_EVENT_ENVELOPE = serializeEnvelope(EVENT_ENVELOPE); - const requestOptions = (transport.module!.request as jest.Mock).mock.calls[0][0]; - assertBasicOptions(requestOptions, true); - expect(mockSetEncoding).toHaveBeenCalled(); - }); - - test('send 400 request mode session', async () => { - mockReturnCode = 400; - const transport = createTransport({ dsn }); +describe('makeNewHttpTransport()', () => { + afterEach(() => { + jest.clearAllMocks(); - try { - await transport.sendSession(sessionsPayload); - } catch (e) { - const requestOptions = (transport.module!.request as jest.Mock).mock.calls[0][0]; - assertBasicOptions(requestOptions, true); - expect(e).toEqual(new SentryError(`HTTP Error (${mockReturnCode})`)); + if (testServer) { + testServer.close(); } }); - test('send x-sentry-error header', async () => { - mockReturnCode = 429; - mockHeaders = { - 'x-sentry-error': 'test-failed', - }; - const transport = createTransport({ dsn }); - - try { - await transport.sendEvent({ - message: 'test', - }); - } catch (e) { - const requestOptions = (transport.module!.request as jest.Mock).mock.calls[0][0]; - assertBasicOptions(requestOptions); - expect(e).toEqual(new SentryError(`HTTP Error (${mockReturnCode}): test-failed`)); - } - }); + describe('.send()', () => { + it('should correctly return successful server response', async () => { + await setupTestServer({ statusCode: SUCCESS }); - test('sends a request to tunnel if configured', async () => { - const transport = createTransport({ dsn, tunnel }); + const transport = makeNodeTransport({ url: TEST_SERVER_URL }); + const transportResponse = await transport.send(EVENT_ENVELOPE); - await transport.sendEvent({ - message: 'test', + expect(transportResponse).toEqual(expect.objectContaining({ status: 'success' })); }); - const requestOptions = (transport.module!.request as jest.Mock).mock.calls[0][0]; - expect(requestOptions.protocol).toEqual('https:'); - expect(requestOptions.hostname).toEqual('hello.com'); - expect(requestOptions.path).toEqual('/world'); - }); - - test('back-off using retry-after header', async () => { - const retryAfterSeconds = 10; - mockReturnCode = 429; - mockHeaders = { - 'retry-after': retryAfterSeconds, - }; - const transport = createTransport({ dsn }); - - const now = Date.now(); - const mock = jest - .spyOn(Date, 'now') - // Check for first event - .mockReturnValueOnce(now) - // Setting disabledUntil - .mockReturnValueOnce(now) - // Check for second event - .mockReturnValueOnce(now + (retryAfterSeconds / 2) * 1000) - // Check for third event - .mockReturnValueOnce(now + retryAfterSeconds * 1000); - - try { - await transport.sendEvent({ message: 'test' }); - } catch (e) { - expect(e).toEqual(new SentryError(`HTTP Error (${mockReturnCode})`)); - } - - try { - await transport.sendEvent({ message: 'test' }); - } catch (e) { - expect(e.status).toEqual(429); - expect(e.reason).toEqual( - `Transport for event requests locked till ${new Date( - now + retryAfterSeconds * 1000, - )} due to too many requests.`, - ); - expect(e.payload.message).toEqual('test'); - expect(e.type).toEqual('event'); - } - - try { - await transport.sendEvent({ message: 'test' }); - } catch (e) { - expect(e).toEqual(new SentryError(`HTTP Error (${mockReturnCode})`)); - } + it('should correctly send envelope to server', async () => { + await setupTestServer({ statusCode: SUCCESS }, (req, body) => { + expect(req.method).toBe('POST'); + expect(body).toBe(SERIALIZED_EVENT_ENVELOPE); + }); - mock.mockRestore(); - }); + const transport = makeNodeTransport({ url: TEST_SERVER_URL }); + await transport.send(EVENT_ENVELOPE); + }); - test('back-off using x-sentry-rate-limits with bogus headers and missing categories should just lock them all', async () => { - const retryAfterSeconds = 60; - mockReturnCode = 429; - mockHeaders = { - 'x-sentry-rate-limits': 'sgthrthewhertht', - }; - const transport = createTransport({ dsn }); - const now = Date.now(); - const mock = jest - .spyOn(Date, 'now') - // 1st event - _isRateLimited - false - .mockReturnValueOnce(now) - // 1st event - _handleRateLimit - .mockReturnValueOnce(now) - // 2nd event - _isRateLimited - true (event category) - .mockReturnValueOnce(now + (retryAfterSeconds / 2) * 1000) - // 3rd event - _isRateLimited - true (transaction category) - .mockReturnValueOnce(now + (retryAfterSeconds / 2) * 1000) - // 4th event - _isRateLimited - false (event category) - .mockReturnValueOnce(now + retryAfterSeconds * 1000) - // 4th event - _handleRateLimit - .mockReturnValueOnce(now + retryAfterSeconds * 1000) - // 5th event - _isRateLimited - false (transaction category) - .mockReturnValueOnce(now + retryAfterSeconds * 1000) - // 5th event - _handleRateLimit - .mockReturnValueOnce(now + retryAfterSeconds * 1000); - - try { - await transport.sendEvent(eventPayload); - } catch (e) { - expect(e).toEqual(new SentryError(`HTTP Error (${mockReturnCode})`)); - } + it('should correctly send user-provided headers to server', async () => { + await setupTestServer({ statusCode: SUCCESS }, req => { + expect(req.headers).toEqual( + expect.objectContaining({ + // node http module lower-cases incoming headers + 'x-some-custom-header-1': 'value1', + 'x-some-custom-header-2': 'value2', + }), + ); + }); - try { - await transport.sendEvent(eventPayload); - } catch (e) { - expect(e.status).toEqual(429); - expect(e.reason).toEqual( - `Transport for event requests locked till ${new Date( - now + retryAfterSeconds * 1000, - )} due to too many requests.`, - ); - expect(e.payload).toEqual(eventPayload); - expect(e.type).toEqual('event'); - } + const transport = makeNodeTransport({ + url: TEST_SERVER_URL, + headers: { + 'X-Some-Custom-Header-1': 'value1', + 'X-Some-Custom-Header-2': 'value2', + }, + }); - try { - await transport.sendEvent(transactionPayload); - } catch (e) { - expect(e.status).toEqual(429); - expect(e.reason).toEqual( - `Transport for transaction requests locked till ${new Date( - now + retryAfterSeconds * 1000, - )} due to too many requests.`, - ); - expect(e.payload).toEqual(transactionPayload); - expect(e.type).toEqual('transaction'); - } + await transport.send(EVENT_ENVELOPE); + }); - mockHeaders = {}; - mockReturnCode = 200; + it.each([ + [RATE_LIMIT, 'rate_limit'], + [INVALID, 'invalid'], + [FAILED, 'failed'], + ])('should correctly reject bad server response (status %i)', async (serverStatusCode, expectedStatus) => { + await setupTestServer({ statusCode: serverStatusCode }); - const eventRes = await transport.sendEvent(eventPayload); - expect(eventRes.status).toEqual('success'); + const transport = makeNodeTransport({ url: TEST_SERVER_URL }); + await expect(transport.send(EVENT_ENVELOPE)).rejects.toEqual(expect.objectContaining({ status: expectedStatus })); + }); - const transactionRes = await transport.sendEvent(transactionPayload); - expect(transactionRes.status).toEqual('success'); + it('should resolve when server responds with rate limit header and status code 200', async () => { + await setupTestServer({ + statusCode: SUCCESS, + responseHeaders: { + 'Retry-After': '2700', + 'X-Sentry-Rate-Limits': '60::organization, 2700::organization', + }, + }); - mock.mockRestore(); - }); + const transport = makeNodeTransport({ url: TEST_SERVER_URL }); + const transportResponse = await transport.send(EVENT_ENVELOPE); - test('back-off using x-sentry-rate-limits with single category', async () => { - const retryAfterSeconds = 10; - mockReturnCode = 429; - mockHeaders = { - 'x-sentry-rate-limits': `${retryAfterSeconds}:error:scope`, - }; - const transport = createTransport({ dsn }); - const now = Date.now(); - const mock = jest - .spyOn(Date, 'now') - // 1st event - _isRateLimited - false - .mockReturnValueOnce(now) - // 1st event - _handleRateLimit - .mockReturnValueOnce(now) - // 2nd event - _isRateLimited - false (different category) - .mockReturnValueOnce(now + (retryAfterSeconds / 2) * 1000) - // 2nd event - _handleRateLimit - .mockReturnValueOnce(now + (retryAfterSeconds / 2) * 1000) - // 3rd event - _isRateLimited - false (different category - sessions) - .mockReturnValueOnce(now + (retryAfterSeconds / 2) * 1000) - // 3rd event - _handleRateLimit - .mockReturnValueOnce(now + (retryAfterSeconds / 2) * 1000) - // 4th event - _isRateLimited - true - .mockReturnValueOnce(now + (retryAfterSeconds / 2) * 1000) - // 5th event - _isRateLimited - false - .mockReturnValueOnce(now + retryAfterSeconds * 1000) - // 5th event - _handleRateLimit - .mockReturnValueOnce(now + retryAfterSeconds * 1000); - - try { - await transport.sendEvent(eventPayload); - } catch (e) { - expect(e).toEqual(new SentryError(`HTTP Error (${mockReturnCode})`)); - } + expect(transportResponse).toEqual(expect.objectContaining({ status: 'success' })); + }); - mockHeaders = {}; - mockReturnCode = 200; - - const transactionRes = await transport.sendEvent(transactionPayload); - expect(transactionRes.status).toEqual('success'); - - const sessionsRes = await transport.sendSession(sessionPayload); - expect(sessionsRes.status).toEqual('success'); - - try { - await transport.sendEvent(eventPayload); - } catch (e) { - expect(e.status).toEqual(429); - expect(e.reason).toEqual( - `Transport for event requests locked till ${new Date( - now + retryAfterSeconds * 1000, - )} due to too many requests.`, - ); - expect(e.payload).toEqual(eventPayload); - expect(e.type).toEqual('event'); - } + it('should resolve when server responds with rate limit header and status code 200', async () => { + await setupTestServer({ + statusCode: SUCCESS, + responseHeaders: { + 'Retry-After': '2700', + 'X-Sentry-Rate-Limits': '60::organization, 2700::organization', + }, + }); - const eventRes = await transport.sendEvent(eventPayload); - expect(eventRes.status).toEqual('success'); + const transport = makeNodeTransport({ url: TEST_SERVER_URL }); + const transportResponse = await transport.send(EVENT_ENVELOPE); - mock.mockRestore(); + expect(transportResponse).toEqual(expect.objectContaining({ status: 'success' })); + }); }); - test('back-off using x-sentry-rate-limits with multiple category', async () => { - const retryAfterSeconds = 10; - mockReturnCode = 429; - mockHeaders = { - 'x-sentry-rate-limits': `${retryAfterSeconds}:error;transaction;session:scope`, - }; - const transport = createTransport({ dsn }); - const now = Date.now(); - const mock = jest - .spyOn(Date, 'now') - // 1st event - _isRateLimited - false - .mockReturnValueOnce(now) - // 1st event - _handleRateLimit - .mockReturnValueOnce(now) - // 2nd event - _isRateLimited - true (event category) - .mockReturnValueOnce(now + (retryAfterSeconds / 2) * 1000) - // 3rd event - _isRateLimited - true (sessions category) - .mockReturnValueOnce(now + (retryAfterSeconds / 2) * 1000) - // 4th event - _isRateLimited - true (transactions category) - .mockReturnValueOnce(now + (retryAfterSeconds / 2) * 1000) - // 5th event - _isRateLimited - false (event category) - .mockReturnValueOnce(now + retryAfterSeconds * 1000) - // 5th event - _handleRateLimit - .mockReturnValueOnce(now + retryAfterSeconds * 1000) - // 6th event - _isRateLimited - false (sessions category) - .mockReturnValueOnce(now + retryAfterSeconds * 1000) - // 6th event - handleRateLimit - .mockReturnValueOnce(now + retryAfterSeconds * 1000) - // 7th event - _isRateLimited - false (transaction category) - .mockReturnValueOnce(now + retryAfterSeconds * 1000) - // 7th event - handleRateLimit - .mockReturnValueOnce(now + retryAfterSeconds * 1000); - - try { - await transport.sendEvent(eventPayload); - } catch (e) { - expect(e).toEqual(new SentryError(`HTTP Error (${mockReturnCode})`)); - } - - try { - await transport.sendEvent(eventPayload); - } catch (e) { - expect(e.status).toEqual(429); - expect(e.reason).toEqual( - `Transport for event requests locked till ${new Date( - now + retryAfterSeconds * 1000, - )} due to too many requests.`, - ); - expect(e.payload).toEqual(eventPayload); - expect(e.type).toEqual('event'); - } + describe('proxy', () => { + it('can be configured through option', () => { + makeNodeTransport({ + url: 'http://9e9fd4523d784609a5fc0ebb1080592f@sentry.io:8989/mysubpath/50622', + proxy: 'http://example.com', + }); - try { - await transport.sendSession(sessionPayload); - } catch (e) { - expect(e.status).toEqual(429); - expect(e.reason).toEqual( - `Transport for session requests locked till ${new Date( - now + retryAfterSeconds * 1000, - )} due to too many requests.`, - ); - expect(e.payload.environment).toEqual(sessionPayload.environment); - expect(e.payload.release).toEqual(sessionPayload.release); - expect(e.payload.sid).toEqual(sessionPayload.sid); - expect(e.type).toEqual('session'); - } + expect(httpProxyAgent).toHaveBeenCalledTimes(1); + expect(httpProxyAgent).toHaveBeenCalledWith('http://example.com'); + }); - try { - await transport.sendEvent(transactionPayload); - } catch (e) { - expect(e.status).toEqual(429); - expect(e.reason).toEqual( - `Transport for transaction requests locked till ${new Date( - now + retryAfterSeconds * 1000, - )} due to too many requests.`, - ); - expect(e.payload).toEqual(transactionPayload); - expect(e.type).toEqual('transaction'); - } + it('can be configured through env variables option', () => { + process.env.http_proxy = 'http://example.com'; + makeNodeTransport({ + url: 'http://9e9fd4523d784609a5fc0ebb1080592f@sentry.io:8989/mysubpath/50622', + }); - mockHeaders = {}; - mockReturnCode = 200; + expect(httpProxyAgent).toHaveBeenCalledTimes(1); + expect(httpProxyAgent).toHaveBeenCalledWith('http://example.com'); + delete process.env.http_proxy; + }); - const eventRes = await transport.sendEvent(eventPayload); - expect(eventRes.status).toEqual('success'); + it('client options have priority over env variables', () => { + process.env.http_proxy = 'http://foo.com'; + makeNodeTransport({ + url: 'http://9e9fd4523d784609a5fc0ebb1080592f@sentry.io:8989/mysubpath/50622', + proxy: 'http://bar.com', + }); - const sessionsRes = await transport.sendSession(sessionPayload); - expect(sessionsRes.status).toEqual('success'); + expect(httpProxyAgent).toHaveBeenCalledTimes(1); + expect(httpProxyAgent).toHaveBeenCalledWith('http://bar.com'); + delete process.env.http_proxy; + }); - const transactionRes = await transport.sendEvent(transactionPayload); - expect(transactionRes.status).toEqual('success'); + it('no_proxy allows for skipping specific hosts', () => { + process.env.no_proxy = 'sentry.io'; + makeNodeTransport({ + url: 'http://9e9fd4523d784609a5fc0ebb1080592f@sentry.io:8989/mysubpath/50622', + proxy: 'http://example.com', + }); - mock.mockRestore(); - }); + expect(httpProxyAgent).not.toHaveBeenCalled(); - test('back-off using x-sentry-rate-limits with missing categories should lock them all', async () => { - const retryAfterSeconds = 10; - mockReturnCode = 429; - mockHeaders = { - 'x-sentry-rate-limits': `${retryAfterSeconds}::scope`, - }; - const transport = createTransport({ dsn }); - const now = Date.now(); - const mock = jest - .spyOn(Date, 'now') - // 1st event - _isRateLimited - false - .mockReturnValueOnce(now) - // 1st event - _handleRateLimit - .mockReturnValueOnce(now) - // 2nd event - _isRateLimited - true (event category) - .mockReturnValueOnce(now + (retryAfterSeconds / 2) * 1000) - // 3rd event - _isRateLimited - true (transaction category) - .mockReturnValueOnce(now + (retryAfterSeconds / 2) * 1000) - // 4th event - _isRateLimited - false (event category) - .mockReturnValueOnce(now + retryAfterSeconds * 1000) - // 4th event - _handleRateLimit - .mockReturnValueOnce(now + retryAfterSeconds * 1000) - // 5th event - _isRateLimited - false (transaction category) - .mockReturnValueOnce(now + retryAfterSeconds * 1000) - // 5th event - _handleRateLimit - .mockReturnValueOnce(now + retryAfterSeconds * 1000); - - try { - await transport.sendEvent(eventPayload); - } catch (e) { - expect(e).toEqual(new SentryError(`HTTP Error (${mockReturnCode})`)); - } + delete process.env.no_proxy; + }); - try { - await transport.sendEvent(eventPayload); - } catch (e) { - expect(e.status).toEqual(429); - expect(e.reason).toEqual( - `Transport for event requests locked till ${new Date( - now + retryAfterSeconds * 1000, - )} due to too many requests.`, - ); - expect(e.payload).toEqual(eventPayload); - expect(e.type).toEqual('event'); - } + it('no_proxy works with a port', () => { + process.env.http_proxy = 'http://example.com:8080'; + process.env.no_proxy = 'sentry.io:8989'; - try { - await transport.sendEvent(transactionPayload); - } catch (e) { - expect(e.status).toEqual(429); - expect(e.reason).toEqual( - `Transport for transaction requests locked till ${new Date( - now + retryAfterSeconds * 1000, - )} due to too many requests.`, - ); - expect(e.payload).toEqual(transactionPayload); - expect(e.type).toEqual('transaction'); - } + makeNodeTransport({ + url: 'http://9e9fd4523d784609a5fc0ebb1080592f@sentry.io:8989/mysubpath/50622', + }); - mockHeaders = {}; - mockReturnCode = 200; + expect(httpProxyAgent).not.toHaveBeenCalled(); - const eventRes = await transport.sendEvent(eventPayload); - expect(eventRes.status).toEqual('success'); + delete process.env.no_proxy; + delete process.env.http_proxy; + }); - const transactionRes = await transport.sendEvent(transactionPayload); - expect(transactionRes.status).toEqual('success'); + it('no_proxy works with multiple comma-separated hosts', () => { + process.env.http_proxy = 'http://example.com:8080'; + process.env.no_proxy = 'example.com,sentry.io,wat.com:1337'; - mock.mockRestore(); - }); + makeNodeTransport({ + url: 'http://9e9fd4523d784609a5fc0ebb1080592f@sentry.io:8989/mysubpath/50622', + }); - test('back-off using x-sentry-rate-limits with bogus categories should be dropped', async () => { - const retryAfterSeconds = 10; - mockReturnCode = 429; - mockHeaders = { - 'x-sentry-rate-limits': `${retryAfterSeconds}:error;safegreg;eqwerw:scope`, - }; - const transport = createTransport({ dsn }); - const now = Date.now(); - const mock = jest - .spyOn(Date, 'now') - // 1st event - _isRateLimited - false - .mockReturnValueOnce(now) - // 1st event - _handleRateLimit - .mockReturnValueOnce(now) - // 2nd event - _isRateLimited - true (event category) - .mockReturnValueOnce(now + (retryAfterSeconds / 2) * 1000) - // 3rd event - _isRateLimited - false (transaction category) - .mockReturnValueOnce(now + (retryAfterSeconds / 2) * 1000) - // 3rd Event - _handleRateLimit - .mockReturnValueOnce(now + (retryAfterSeconds / 2) * 1000) - // 4th event - _isRateLimited - false (event category) - .mockReturnValueOnce(now + retryAfterSeconds * 1000) - // 4th event - _handleRateLimit - .mockReturnValueOnce(now + retryAfterSeconds * 1000); - - try { - await transport.sendEvent(eventPayload); - } catch (e) { - expect(e).toEqual(new SentryError(`HTTP Error (${mockReturnCode})`)); - } + expect(httpProxyAgent).not.toHaveBeenCalled(); - try { - await transport.sendEvent(eventPayload); - } catch (e) { - expect(e.status).toEqual(429); - expect(e.reason).toEqual( - `Transport for event requests locked till ${new Date( - now + retryAfterSeconds * 1000, - )} due to too many requests.`, - ); - expect(e.payload).toEqual(eventPayload); - expect(e.type).toEqual('event'); - } + delete process.env.no_proxy; + delete process.env.http_proxy; + }); + }); - mockHeaders = {}; - mockReturnCode = 200; + it('should register TransportRequestExecutor that returns the correct object from server response (rate limit)', async () => { + await setupTestServer({ + statusCode: RATE_LIMIT, + responseHeaders: { + 'Retry-After': '2700', + 'X-Sentry-Rate-Limits': '60::organization, 2700::organization', + }, + }); - const transactionRes = await transport.sendEvent(transactionPayload); - expect(transactionRes.status).toEqual('success'); + makeNodeTransport({ url: TEST_SERVER_URL }); + const registeredRequestExecutor = (createTransport as jest.Mock).mock.calls[0][1]; - const eventRes = await transport.sendEvent(eventPayload); - expect(eventRes.status).toEqual('success'); + const executorResult = registeredRequestExecutor({ + body: serializeEnvelope(EVENT_ENVELOPE), + category: 'error', + }); - mock.mockRestore(); + await expect(executorResult).resolves.toEqual( + expect.objectContaining({ + headers: { + 'retry-after': '2700', + 'x-sentry-rate-limits': '60::organization, 2700::organization', + }, + statusCode: RATE_LIMIT, + }), + ); }); - test('back-off using x-sentry-rate-limits should also trigger for 200 responses', async () => { - const retryAfterSeconds = 10; - mockReturnCode = 200; - mockHeaders = { - 'x-sentry-rate-limits': `${retryAfterSeconds}:error;transaction:scope`, - }; - const transport = createTransport({ dsn }); - const now = Date.now(); - const mock = jest - .spyOn(Date, 'now') - // 1st event - _isRateLimited - false - .mockReturnValueOnce(now) - // 1st event - _handleRateLimit - .mockReturnValueOnce(now) - // 2nd event - _isRateLimited - true - .mockReturnValueOnce(now + (retryAfterSeconds / 2) * 1000) - // 3rd event - _isRateLimited - false - .mockReturnValueOnce(now + retryAfterSeconds * 1000) - // 3rd event - _handleRateLimit - .mockReturnValueOnce(now + retryAfterSeconds * 1000); - - let eventRes = await transport.sendEvent(eventPayload); - expect(eventRes.status).toEqual('success'); - - try { - await transport.sendEvent(eventPayload); - } catch (e) { - expect(e.status).toEqual(429); - expect(e.reason).toEqual( - `Transport for event requests locked till ${new Date( - now + retryAfterSeconds * 1000, - )} due to too many requests.`, - ); - expect(e.payload).toEqual(eventPayload); - expect(e.type).toEqual('event'); - } + it('should register TransportRequestExecutor that returns the correct object from server response (OK)', async () => { + await setupTestServer({ + statusCode: SUCCESS, + }); - mockReturnCode = 200; - mockHeaders = {}; + makeNodeTransport({ url: TEST_SERVER_URL }); + const registeredRequestExecutor = (createTransport as jest.Mock).mock.calls[0][1]; - eventRes = await transport.sendEvent(eventPayload); - expect(eventRes.status).toEqual('success'); + const executorResult = registeredRequestExecutor({ + body: serializeEnvelope(EVENT_ENVELOPE), + category: 'error', + }); - mock.mockRestore(); + await expect(executorResult).resolves.toEqual( + expect.objectContaining({ + headers: { + 'retry-after': null, + 'x-sentry-rate-limits': null, + }, + statusCode: SUCCESS, + }), + ); }); - test('transport options', async () => { - mockReturnCode = 200; - const transport = createTransport({ - dsn, - headers: { - a: 'b', + it('should register TransportRequestExecutor that returns the correct object from server response (OK with rate-limit headers)', async () => { + await setupTestServer({ + statusCode: SUCCESS, + responseHeaders: { + 'Retry-After': '2700', + 'X-Sentry-Rate-Limits': '60::organization, 2700::organization', }, }); - await transport.sendEvent({ - message: 'test', - }); - const requestOptions = (transport.module!.request as jest.Mock).mock.calls[0][0]; - assertBasicOptions(requestOptions); - expect(requestOptions.headers).toEqual(expect.objectContaining({ a: 'b' })); - }); + makeNodeTransport({ url: TEST_SERVER_URL }); + const registeredRequestExecutor = (createTransport as jest.Mock).mock.calls[0][1]; - describe('proxy', () => { - test('can be configured through client option', async () => { - const transport = createTransport({ - dsn, - httpProxy: 'http://example.com:8080', - }); - const client = transport.client as unknown as { proxy: Record; secureProxy: boolean }; - expect(client).toBeInstanceOf(HttpsProxyAgent); - expect(client.secureProxy).toEqual(false); - expect(client.proxy).toEqual(expect.objectContaining({ protocol: 'http:', port: 8080, host: 'example.com' })); + const executorResult = registeredRequestExecutor({ + body: serializeEnvelope(EVENT_ENVELOPE), + category: 'error', }); - test('can be configured through env variables option', async () => { - process.env.http_proxy = 'http://example.com:8080'; - const transport = createTransport({ - dsn, - httpProxy: 'http://example.com:8080', - }); - const client = transport.client as unknown as { proxy: Record; secureProxy: boolean }; - expect(client).toBeInstanceOf(HttpsProxyAgent); - expect(client.secureProxy).toEqual(false); - expect(client.proxy).toEqual(expect.objectContaining({ protocol: 'http:', port: 8080, host: 'example.com' })); - delete process.env.http_proxy; - }); + await expect(executorResult).resolves.toEqual( + expect.objectContaining({ + headers: { + 'retry-after': '2700', + 'x-sentry-rate-limits': '60::organization, 2700::organization', + }, + statusCode: SUCCESS, + }), + ); + }); - test('client options have priority over env variables', async () => { - process.env.http_proxy = 'http://env-example.com:8080'; - const transport = createTransport({ - dsn, - httpProxy: 'http://example.com:8080', - }); - const client = transport.client as unknown as { proxy: Record; secureProxy: boolean }; - expect(client).toBeInstanceOf(HttpsProxyAgent); - expect(client.secureProxy).toEqual(false); - expect(client.proxy).toEqual(expect.objectContaining({ protocol: 'http:', port: 8080, host: 'example.com' })); - delete process.env.http_proxy; + it('should register TransportRequestExecutor that returns the correct object from server response (NOK with rate-limit headers)', async () => { + await setupTestServer({ + statusCode: RATE_LIMIT, + responseHeaders: { + 'Retry-After': '2700', + 'X-Sentry-Rate-Limits': '60::organization, 2700::organization', + }, }); - test('no_proxy allows for skipping specific hosts', async () => { - process.env.no_proxy = 'sentry.io'; - const transport = createTransport({ - dsn, - httpProxy: 'http://example.com:8080', - }); - expect(transport.client).toBeInstanceOf(http.Agent); - }); + makeNodeTransport({ url: TEST_SERVER_URL }); + const registeredRequestExecutor = (createTransport as jest.Mock).mock.calls[0][1]; - test('no_proxy works with a port', async () => { - process.env.http_proxy = 'http://example.com:8080'; - process.env.no_proxy = 'sentry.io:8989'; - const transport = createTransport({ - dsn, - }); - expect(transport.client).toBeInstanceOf(http.Agent); - delete process.env.http_proxy; + const executorResult = registeredRequestExecutor({ + body: serializeEnvelope(EVENT_ENVELOPE), + category: 'error', }); - test('no_proxy works with multiple comma-separated hosts', async () => { - process.env.http_proxy = 'http://example.com:8080'; - process.env.no_proxy = 'example.com,sentry.io,wat.com:1337'; - const transport = createTransport({ - dsn, - }); - expect(transport.client).toBeInstanceOf(http.Agent); - delete process.env.http_proxy; - }); + await expect(executorResult).resolves.toEqual( + expect.objectContaining({ + headers: { + 'retry-after': '2700', + 'x-sentry-rate-limits': '60::organization, 2700::organization', + }, + statusCode: RATE_LIMIT, + }), + ); }); }); diff --git a/packages/node/test/transports/https.test.ts b/packages/node/test/transports/https.test.ts index 8d7f4dd9aa65..28c37b7c966b 100644 --- a/packages/node/test/transports/https.test.ts +++ b/packages/node/test/transports/https.test.ts @@ -1,332 +1,396 @@ -import { Session } from '@sentry/hub'; -import { SessionAggregates, TransportOptions } from '@sentry/types'; -import { SentryError } from '@sentry/utils'; +import { createTransport } from '@sentry/core'; +import { EventEnvelope, EventItem } from '@sentry/types'; +import { createEnvelope, serializeEnvelope } from '@sentry/utils'; +import * as http from 'http'; import * as https from 'https'; -import * as HttpsProxyAgent from 'https-proxy-agent'; - -import { HTTPSTransport } from '../../src/transports/https'; - -const mockSetEncoding = jest.fn(); -const dsn = 'https://9e9fd4523d784609a5fc0ebb1080592f@sentry.io:8989/mysubpath/50622'; -const storePath = '/mysubpath/api/50622/store/'; -const envelopePath = '/mysubpath/api/50622/envelope/'; -const tunnel = 'https://hello.com/world'; -const sessionsPayload: SessionAggregates = { - attrs: { environment: 'test', release: '1.0' }, - aggregates: [{ started: '2021-03-17T16:00:00.000Z', exited: 1 }], -}; -let mockReturnCode = 200; -let mockHeaders = {}; - -jest.mock('fs', () => ({ - readFileSync(): string { - return 'mockedCert'; - }, -})); - -function createTransport(options: TransportOptions): HTTPSTransport { - const transport = new HTTPSTransport(options); - transport.module = { - request: jest.fn().mockImplementation((_options: any, callback: any) => ({ - end: () => { - callback({ - headers: mockHeaders, - setEncoding: mockSetEncoding, - statusCode: mockReturnCode, - }); - }, - on: jest.fn(), - })), + +import { makeNodeTransport } from '../../src/transports'; +import { HTTPModule, HTTPModuleRequestIncomingMessage } from '../../src/transports/http-module'; +import testServerCerts from './test-server-certs'; + +jest.mock('@sentry/core', () => { + const actualCore = jest.requireActual('@sentry/core'); + return { + ...actualCore, + createTransport: jest.fn().mockImplementation(actualCore.createTransport), }; - return transport; -} +}); -function assertBasicOptions(options: any, useEnvelope: boolean = false): void { - expect(options.headers['X-Sentry-Auth']).toContain('sentry_version'); - expect(options.headers['X-Sentry-Auth']).toContain('sentry_client'); - expect(options.headers['X-Sentry-Auth']).toContain('sentry_key'); - expect(options.port).toEqual('8989'); - expect(options.path).toEqual(useEnvelope ? envelopePath : storePath); - expect(options.hostname).toEqual('sentry.io'); +// eslint-disable-next-line @typescript-eslint/no-var-requires +const httpProxyAgent = require('https-proxy-agent'); +jest.mock('https-proxy-agent', () => { + return jest.fn().mockImplementation(() => new http.Agent({ keepAlive: false, maxSockets: 30, timeout: 2000 })); +}); + +const SUCCESS = 200; +const RATE_LIMIT = 429; +const INVALID = 400; +const FAILED = 500; + +interface TestServerOptions { + statusCode: number; + responseHeaders?: Record; } -describe('HTTPSTransport', () => { - beforeEach(() => { - mockReturnCode = 200; - mockHeaders = {}; - jest.clearAllMocks(); - }); +let testServer: http.Server | undefined; + +function setupTestServer( + options: TestServerOptions, + requestInspector?: (req: http.IncomingMessage, body: string) => void, +) { + testServer = https.createServer(testServerCerts, (req, res) => { + let body = ''; - test('send 200', async () => { - const transport = createTransport({ dsn }); - await transport.sendEvent({ - message: 'test', + req.on('data', data => { + body += data; }); - const requestOptions = (transport.module!.request as jest.Mock).mock.calls[0][0]; - assertBasicOptions(requestOptions); - expect(mockSetEncoding).toHaveBeenCalled(); - }); + req.on('end', () => { + requestInspector?.(req, body); + }); - test('send 400', async () => { - mockReturnCode = 400; - const transport = createTransport({ dsn }); + res.writeHead(options.statusCode, options.responseHeaders); + res.end(); - try { - await transport.sendEvent({ - message: 'test', - }); - } catch (e) { - const requestOptions = (transport.module!.request as jest.Mock).mock.calls[0][0]; - assertBasicOptions(requestOptions); - expect(e).toEqual(new SentryError(`HTTP Error (${mockReturnCode})`)); - } + // also terminate socket because keepalive hangs connection a bit + res.connection.end(); }); - test('send 200 session', async () => { - const transport = createTransport({ dsn }); - await transport.sendSession(new Session()); + testServer.listen(8099); - const requestOptions = (transport.module!.request as jest.Mock).mock.calls[0][0]; - assertBasicOptions(requestOptions, true); - expect(mockSetEncoding).toHaveBeenCalled(); + return new Promise(resolve => { + testServer?.on('listening', resolve); }); +} - test('send 400 session', async () => { - mockReturnCode = 400; - const transport = createTransport({ dsn }); +const TEST_SERVER_URL = 'https://localhost:8099'; - try { - await transport.sendSession(new Session()); - } catch (e) { - const requestOptions = (transport.module!.request as jest.Mock).mock.calls[0][0]; - assertBasicOptions(requestOptions, true); - expect(e).toEqual(new SentryError(`HTTP Error (${mockReturnCode})`)); - } - }); +const EVENT_ENVELOPE = createEnvelope({ event_id: 'aa3ff046696b4bc6b609ce6d28fde9e2', sent_at: '123' }, [ + [{ type: 'event' }, { event_id: 'aa3ff046696b4bc6b609ce6d28fde9e2' }] as EventItem, +]); - test('send 200 request mode session', async () => { - const transport = createTransport({ dsn }); - await transport.sendSession(sessionsPayload); +const SERIALIZED_EVENT_ENVELOPE = serializeEnvelope(EVENT_ENVELOPE); - const requestOptions = (transport.module!.request as jest.Mock).mock.calls[0][0]; - assertBasicOptions(requestOptions, true); - expect(mockSetEncoding).toHaveBeenCalled(); - }); +const unsafeHttpsModule: HTTPModule = { + request: jest + .fn() + .mockImplementation((options: https.RequestOptions, callback?: (res: HTTPModuleRequestIncomingMessage) => void) => { + return https.request({ ...options, rejectUnauthorized: false }, callback); + }), +}; - test('send 400 request mode session', async () => { - mockReturnCode = 400; - const transport = createTransport({ dsn }); +describe('makeNewHttpsTransport()', () => { + afterEach(() => { + jest.clearAllMocks(); - try { - await transport.sendSession(sessionsPayload); - } catch (e) { - const requestOptions = (transport.module!.request as jest.Mock).mock.calls[0][0]; - assertBasicOptions(requestOptions, true); - expect(e).toEqual(new SentryError(`HTTP Error (${mockReturnCode})`)); + if (testServer) { + testServer.close(); } }); - test('send x-sentry-error header', async () => { - mockReturnCode = 429; - mockHeaders = { - 'x-sentry-error': 'test-failed', - }; - const transport = createTransport({ dsn }); + describe('.send()', () => { + it('should correctly return successful server response', async () => { + await setupTestServer({ statusCode: SUCCESS }); - try { - await transport.sendEvent({ - message: 'test', - }); - } catch (e) { - const requestOptions = (transport.module!.request as jest.Mock).mock.calls[0][0]; - assertBasicOptions(requestOptions); - expect(e).toEqual(new SentryError(`HTTP Error (${mockReturnCode}): test-failed`)); - } - }); + const transport = makeNodeTransport({ httpModule: unsafeHttpsModule, url: TEST_SERVER_URL }); + const transportResponse = await transport.send(EVENT_ENVELOPE); + + expect(transportResponse).toEqual(expect.objectContaining({ status: 'success' })); + }); - test('sends a request to tunnel if configured', async () => { - const transport = createTransport({ dsn, tunnel }); + it('should correctly send envelope to server', async () => { + await setupTestServer({ statusCode: SUCCESS }, (req, body) => { + expect(req.method).toBe('POST'); + expect(body).toBe(SERIALIZED_EVENT_ENVELOPE); + }); - await transport.sendEvent({ - message: 'test', + const transport = makeNodeTransport({ httpModule: unsafeHttpsModule, url: TEST_SERVER_URL }); + await transport.send(EVENT_ENVELOPE); }); - const requestOptions = (transport.module!.request as jest.Mock).mock.calls[0][0]; - expect(requestOptions.protocol).toEqual('https:'); - expect(requestOptions.hostname).toEqual('hello.com'); - expect(requestOptions.path).toEqual('/world'); - }); + it('should correctly send user-provided headers to server', async () => { + await setupTestServer({ statusCode: SUCCESS }, req => { + expect(req.headers).toEqual( + expect.objectContaining({ + // node http module lower-cases incoming headers + 'x-some-custom-header-1': 'value1', + 'x-some-custom-header-2': 'value2', + }), + ); + }); - test('back-off using retry-after header', async () => { - const retryAfterSeconds = 10; - mockReturnCode = 429; - mockHeaders = { - 'retry-after': retryAfterSeconds, - }; - const transport = createTransport({ dsn }); - - const now = Date.now(); - const mock = jest - .spyOn(Date, 'now') - // Check for first event - .mockReturnValueOnce(now) - // Setting disabledUntil - .mockReturnValueOnce(now) - // Check for second event - .mockReturnValueOnce(now + (retryAfterSeconds / 2) * 1000) - // Check for third event - .mockReturnValueOnce(now + retryAfterSeconds * 1000); - - try { - await transport.sendEvent({ message: 'test' }); - } catch (e) { - expect(e).toEqual(new SentryError(`HTTP Error (${mockReturnCode})`)); - } + const transport = makeNodeTransport({ + httpModule: unsafeHttpsModule, + url: TEST_SERVER_URL, + headers: { + 'X-Some-Custom-Header-1': 'value1', + 'X-Some-Custom-Header-2': 'value2', + }, + }); - try { - await transport.sendEvent({ message: 'test' }); - } catch (e) { - expect(e.status).toEqual(429); - expect(e.reason).toEqual( - `Transport for event requests locked till ${new Date( - now + retryAfterSeconds * 1000, - )} due to too many requests.`, - ); - expect(e.payload.message).toEqual('test'); - expect(e.type).toEqual('event'); - } + await transport.send(EVENT_ENVELOPE); + }); - try { - await transport.sendEvent({ message: 'test' }); - } catch (e) { - expect(e).toEqual(new SentryError(`HTTP Error (${mockReturnCode})`)); - } + it.each([ + [RATE_LIMIT, 'rate_limit'], + [INVALID, 'invalid'], + [FAILED, 'failed'], + ])('should correctly reject bad server response (status %i)', async (serverStatusCode, expectedStatus) => { + await setupTestServer({ statusCode: serverStatusCode }); - mock.mockRestore(); - }); + const transport = makeNodeTransport({ httpModule: unsafeHttpsModule, url: TEST_SERVER_URL }); + await expect(transport.send(EVENT_ENVELOPE)).rejects.toEqual(expect.objectContaining({ status: expectedStatus })); + }); - test('transport options', async () => { - mockReturnCode = 200; - const transport = createTransport({ - dsn, - headers: { - a: 'b', - }, + it('should resolve when server responds with rate limit header and status code 200', async () => { + await setupTestServer({ + statusCode: SUCCESS, + responseHeaders: { + 'Retry-After': '2700', + 'X-Sentry-Rate-Limits': '60::organization, 2700::organization', + }, + }); + + const transport = makeNodeTransport({ httpModule: unsafeHttpsModule, url: TEST_SERVER_URL }); + const transportResponse = await transport.send(EVENT_ENVELOPE); + + expect(transportResponse).toEqual(expect.objectContaining({ status: 'success' })); }); - await transport.sendEvent({ - message: 'test', + + it('should resolve when server responds with rate limit header and status code 200', async () => { + await setupTestServer({ + statusCode: SUCCESS, + responseHeaders: { + 'Retry-After': '2700', + 'X-Sentry-Rate-Limits': '60::organization, 2700::organization', + }, + }); + + const transport = makeNodeTransport({ httpModule: unsafeHttpsModule, url: TEST_SERVER_URL }); + const transportResponse = await transport.send(EVENT_ENVELOPE); + + expect(transportResponse).toEqual(expect.objectContaining({ status: 'success' })); }); - const requestOptions = (transport.module!.request as jest.Mock).mock.calls[0][0]; - assertBasicOptions(requestOptions); - expect(requestOptions.headers).toEqual(expect.objectContaining({ a: 'b' })); - }); + it('should use `caCerts` option', async () => { + await setupTestServer({ statusCode: SUCCESS }); - describe('proxy', () => { - test('can be configured through client option', async () => { - const transport = createTransport({ - dsn, - httpsProxy: 'https://example.com:8080', + const transport = makeNodeTransport({ + httpModule: unsafeHttpsModule, + url: TEST_SERVER_URL, + caCerts: 'some cert', }); - const client = transport.client as unknown as { proxy: Record; secureProxy: boolean }; - expect(client).toBeInstanceOf(HttpsProxyAgent); - expect(client.secureProxy).toEqual(true); - expect(client.proxy).toEqual(expect.objectContaining({ protocol: 'https:', port: 8080, host: 'example.com' })); + + await transport.send(EVENT_ENVELOPE); + + // eslint-disable-next-line @typescript-eslint/unbound-method + expect(unsafeHttpsModule.request).toHaveBeenCalledWith( + expect.objectContaining({ + ca: 'some cert', + }), + expect.anything(), + ); }); + }); - test('can be configured through env variables option', async () => { - process.env.https_proxy = 'https://example.com:8080'; - const transport = createTransport({ - dsn, - httpsProxy: 'https://example.com:8080', + describe('proxy', () => { + it('can be configured through option', () => { + makeNodeTransport({ + httpModule: unsafeHttpsModule, + url: 'https://9e9fd4523d784609a5fc0ebb1080592f@sentry.io:8989/mysubpath/50622', + proxy: 'https://example.com', }); - const client = transport.client as unknown as { proxy: Record; secureProxy: boolean }; - expect(client).toBeInstanceOf(HttpsProxyAgent); - expect(client.secureProxy).toEqual(true); - expect(client.proxy).toEqual(expect.objectContaining({ protocol: 'https:', port: 8080, host: 'example.com' })); - delete process.env.https_proxy; + + expect(httpProxyAgent).toHaveBeenCalledTimes(1); + expect(httpProxyAgent).toHaveBeenCalledWith('https://example.com'); }); - test('https proxies have priority in client option', async () => { - const transport = createTransport({ - dsn, - httpProxy: 'http://unsecure-example.com:8080', - httpsProxy: 'https://example.com:8080', + it('can be configured through env variables option (http)', () => { + process.env.http_proxy = 'https://example.com'; + makeNodeTransport({ + httpModule: unsafeHttpsModule, + url: 'https://9e9fd4523d784609a5fc0ebb1080592f@sentry.io:8989/mysubpath/50622', }); - const client = transport.client as unknown as { proxy: Record; secureProxy: boolean }; - expect(client).toBeInstanceOf(HttpsProxyAgent); - expect(client.secureProxy).toEqual(true); - expect(client.proxy).toEqual(expect.objectContaining({ protocol: 'https:', port: 8080, host: 'example.com' })); + + expect(httpProxyAgent).toHaveBeenCalledTimes(1); + expect(httpProxyAgent).toHaveBeenCalledWith('https://example.com'); + delete process.env.http_proxy; }); - test('https proxies have priority in env variables', async () => { - process.env.http_proxy = 'http://unsecure-example.com:8080'; - process.env.https_proxy = 'https://example.com:8080'; - const transport = createTransport({ - dsn, + it('can be configured through env variables option (https)', () => { + process.env.https_proxy = 'https://example.com'; + makeNodeTransport({ + httpModule: unsafeHttpsModule, + url: 'https://9e9fd4523d784609a5fc0ebb1080592f@sentry.io:8989/mysubpath/50622', }); - const client = transport.client as unknown as { proxy: Record; secureProxy: boolean }; - expect(client).toBeInstanceOf(HttpsProxyAgent); - expect(client.secureProxy).toEqual(true); - expect(client.proxy).toEqual(expect.objectContaining({ protocol: 'https:', port: 8080, host: 'example.com' })); - delete process.env.http_proxy; + + expect(httpProxyAgent).toHaveBeenCalledTimes(1); + expect(httpProxyAgent).toHaveBeenCalledWith('https://example.com'); delete process.env.https_proxy; }); - test('client options have priority over env variables', async () => { - process.env.https_proxy = 'https://env-example.com:8080'; - const transport = createTransport({ - dsn, - httpsProxy: 'https://example.com:8080', + it('client options have priority over env variables', () => { + process.env.https_proxy = 'https://foo.com'; + makeNodeTransport({ + httpModule: unsafeHttpsModule, + url: 'https://9e9fd4523d784609a5fc0ebb1080592f@sentry.io:8989/mysubpath/50622', + proxy: 'https://bar.com', }); - const client = transport.client as unknown as { proxy: Record; secureProxy: boolean }; - expect(client).toBeInstanceOf(HttpsProxyAgent); - expect(client.secureProxy).toEqual(true); - expect(client.proxy).toEqual(expect.objectContaining({ protocol: 'https:', port: 8080, host: 'example.com' })); + + expect(httpProxyAgent).toHaveBeenCalledTimes(1); + expect(httpProxyAgent).toHaveBeenCalledWith('https://bar.com'); delete process.env.https_proxy; }); - test('no_proxy allows for skipping specific hosts', async () => { + it('no_proxy allows for skipping specific hosts', () => { process.env.no_proxy = 'sentry.io'; - const transport = createTransport({ - dsn, - httpsProxy: 'https://example.com:8080', + makeNodeTransport({ + httpModule: unsafeHttpsModule, + url: 'https://9e9fd4523d784609a5fc0ebb1080592f@sentry.io:8989/mysubpath/50622', + proxy: 'https://example.com', }); - expect(transport.client).toBeInstanceOf(https.Agent); + + expect(httpProxyAgent).not.toHaveBeenCalled(); + + delete process.env.no_proxy; }); - test('no_proxy works with a port', async () => { - process.env.https_proxy = 'https://example.com:8080'; + it('no_proxy works with a port', () => { + process.env.http_proxy = 'https://example.com:8080'; process.env.no_proxy = 'sentry.io:8989'; - const transport = createTransport({ - dsn, + + makeNodeTransport({ + httpModule: unsafeHttpsModule, + url: 'https://9e9fd4523d784609a5fc0ebb1080592f@sentry.io:8989/mysubpath/50622', }); - expect(transport.client).toBeInstanceOf(https.Agent); - delete process.env.https_proxy; + + expect(httpProxyAgent).not.toHaveBeenCalled(); + + delete process.env.no_proxy; + delete process.env.http_proxy; }); - test('no_proxy works with multiple comma-separated hosts', async () => { + it('no_proxy works with multiple comma-separated hosts', () => { process.env.http_proxy = 'https://example.com:8080'; process.env.no_proxy = 'example.com,sentry.io,wat.com:1337'; - const transport = createTransport({ - dsn, + + makeNodeTransport({ + httpModule: unsafeHttpsModule, + url: 'https://9e9fd4523d784609a5fc0ebb1080592f@sentry.io:8989/mysubpath/50622', }); - expect(transport.client).toBeInstanceOf(https.Agent); - delete process.env.https_proxy; + + expect(httpProxyAgent).not.toHaveBeenCalled(); + + delete process.env.no_proxy; + delete process.env.http_proxy; }); + }); - test('can configure tls certificate through client option', async () => { - mockReturnCode = 200; - const transport = createTransport({ - caCerts: './some/path.pem', - dsn, - }); - await transport.sendEvent({ - message: 'test', - }); - const requestOptions = (transport.module!.request as jest.Mock).mock.calls[0][0]; - expect(requestOptions.ca).toEqual('mockedCert'); + it('should register TransportRequestExecutor that returns the correct object from server response (rate limit)', async () => { + await setupTestServer({ + statusCode: RATE_LIMIT, + responseHeaders: { + 'Retry-After': '2700', + 'X-Sentry-Rate-Limits': '60::organization, 2700::organization', + }, + }); + + makeNodeTransport({ httpModule: unsafeHttpsModule, url: TEST_SERVER_URL }); + const registeredRequestExecutor = (createTransport as jest.Mock).mock.calls[0][1]; + + const executorResult = registeredRequestExecutor({ + body: serializeEnvelope(EVENT_ENVELOPE), + category: 'error', + }); + + await expect(executorResult).resolves.toEqual( + expect.objectContaining({ + headers: { + 'retry-after': '2700', + 'x-sentry-rate-limits': '60::organization, 2700::organization', + }, + statusCode: RATE_LIMIT, + }), + ); + }); + + it('should register TransportRequestExecutor that returns the correct object from server response (OK)', async () => { + await setupTestServer({ + statusCode: SUCCESS, + }); + + makeNodeTransport({ httpModule: unsafeHttpsModule, url: TEST_SERVER_URL }); + const registeredRequestExecutor = (createTransport as jest.Mock).mock.calls[0][1]; + + const executorResult = registeredRequestExecutor({ + body: serializeEnvelope(EVENT_ENVELOPE), + category: 'error', + }); + + await expect(executorResult).resolves.toEqual( + expect.objectContaining({ + headers: { + 'retry-after': null, + 'x-sentry-rate-limits': null, + }, + statusCode: SUCCESS, + }), + ); + }); + + it('should register TransportRequestExecutor that returns the correct object from server response (OK with rate-limit headers)', async () => { + await setupTestServer({ + statusCode: SUCCESS, + responseHeaders: { + 'Retry-After': '2700', + 'X-Sentry-Rate-Limits': '60::organization, 2700::organization', + }, + }); + + makeNodeTransport({ httpModule: unsafeHttpsModule, url: TEST_SERVER_URL }); + const registeredRequestExecutor = (createTransport as jest.Mock).mock.calls[0][1]; + + const executorResult = registeredRequestExecutor({ + body: serializeEnvelope(EVENT_ENVELOPE), + category: 'error', }); + + await expect(executorResult).resolves.toEqual( + expect.objectContaining({ + headers: { + 'retry-after': '2700', + 'x-sentry-rate-limits': '60::organization, 2700::organization', + }, + statusCode: SUCCESS, + }), + ); + }); + + it('should register TransportRequestExecutor that returns the correct object from server response (NOK with rate-limit headers)', async () => { + await setupTestServer({ + statusCode: RATE_LIMIT, + responseHeaders: { + 'Retry-After': '2700', + 'X-Sentry-Rate-Limits': '60::organization, 2700::organization', + }, + }); + + makeNodeTransport({ httpModule: unsafeHttpsModule, url: TEST_SERVER_URL }); + const registeredRequestExecutor = (createTransport as jest.Mock).mock.calls[0][1]; + + const executorResult = registeredRequestExecutor({ + body: serializeEnvelope(EVENT_ENVELOPE), + category: 'error', + }); + + await expect(executorResult).resolves.toEqual( + expect.objectContaining({ + headers: { + 'retry-after': '2700', + 'x-sentry-rate-limits': '60::organization, 2700::organization', + }, + statusCode: RATE_LIMIT, + }), + ); }); }); diff --git a/packages/node/test/transports/new/http.test.ts b/packages/node/test/transports/new/http.test.ts deleted file mode 100644 index b3ce46d5a542..000000000000 --- a/packages/node/test/transports/new/http.test.ts +++ /dev/null @@ -1,347 +0,0 @@ -import { createTransport } from '@sentry/core'; -import { EventEnvelope, EventItem } from '@sentry/types'; -import { createEnvelope, serializeEnvelope } from '@sentry/utils'; -import * as http from 'http'; - -// TODO(v7): We're renaming the imported file so this needs to be changed as well -import { makeNodeTransport } from '../../../src/transports/new'; - -jest.mock('@sentry/core', () => { - const actualCore = jest.requireActual('@sentry/core'); - return { - ...actualCore, - createTransport: jest.fn().mockImplementation(actualCore.createTransport), - }; -}); - -// eslint-disable-next-line @typescript-eslint/no-var-requires -const httpProxyAgent = require('https-proxy-agent'); -jest.mock('https-proxy-agent', () => { - return jest.fn().mockImplementation(() => new http.Agent({ keepAlive: false, maxSockets: 30, timeout: 2000 })); -}); - -const SUCCESS = 200; -const RATE_LIMIT = 429; -const INVALID = 400; -const FAILED = 500; - -interface TestServerOptions { - statusCode: number; - responseHeaders?: Record; -} - -let testServer: http.Server | undefined; - -function setupTestServer( - options: TestServerOptions, - requestInspector?: (req: http.IncomingMessage, body: string) => void, -) { - testServer = http.createServer((req, res) => { - let body = ''; - - req.on('data', data => { - body += data; - }); - - req.on('end', () => { - requestInspector?.(req, body); - }); - - res.writeHead(options.statusCode, options.responseHeaders); - res.end(); - - // also terminate socket because keepalive hangs connection a bit - res.connection.end(); - }); - - testServer.listen(18099); - - return new Promise(resolve => { - testServer?.on('listening', resolve); - }); -} - -const TEST_SERVER_URL = 'http://localhost:18099'; - -const EVENT_ENVELOPE = createEnvelope({ event_id: 'aa3ff046696b4bc6b609ce6d28fde9e2', sent_at: '123' }, [ - [{ type: 'event' }, { event_id: 'aa3ff046696b4bc6b609ce6d28fde9e2' }] as EventItem, -]); - -const SERIALIZED_EVENT_ENVELOPE = serializeEnvelope(EVENT_ENVELOPE); - -describe('makeNewHttpTransport()', () => { - afterEach(() => { - jest.clearAllMocks(); - - if (testServer) { - testServer.close(); - } - }); - - describe('.send()', () => { - it('should correctly return successful server response', async () => { - await setupTestServer({ statusCode: SUCCESS }); - - const transport = makeNodeTransport({ url: TEST_SERVER_URL }); - const transportResponse = await transport.send(EVENT_ENVELOPE); - - expect(transportResponse).toEqual(expect.objectContaining({ status: 'success' })); - }); - - it('should correctly send envelope to server', async () => { - await setupTestServer({ statusCode: SUCCESS }, (req, body) => { - expect(req.method).toBe('POST'); - expect(body).toBe(SERIALIZED_EVENT_ENVELOPE); - }); - - const transport = makeNodeTransport({ url: TEST_SERVER_URL }); - await transport.send(EVENT_ENVELOPE); - }); - - it('should correctly send user-provided headers to server', async () => { - await setupTestServer({ statusCode: SUCCESS }, req => { - expect(req.headers).toEqual( - expect.objectContaining({ - // node http module lower-cases incoming headers - 'x-some-custom-header-1': 'value1', - 'x-some-custom-header-2': 'value2', - }), - ); - }); - - const transport = makeNodeTransport({ - url: TEST_SERVER_URL, - headers: { - 'X-Some-Custom-Header-1': 'value1', - 'X-Some-Custom-Header-2': 'value2', - }, - }); - - await transport.send(EVENT_ENVELOPE); - }); - - it.each([ - [RATE_LIMIT, 'rate_limit'], - [INVALID, 'invalid'], - [FAILED, 'failed'], - ])('should correctly reject bad server response (status %i)', async (serverStatusCode, expectedStatus) => { - await setupTestServer({ statusCode: serverStatusCode }); - - const transport = makeNodeTransport({ url: TEST_SERVER_URL }); - await expect(transport.send(EVENT_ENVELOPE)).rejects.toEqual(expect.objectContaining({ status: expectedStatus })); - }); - - it('should resolve when server responds with rate limit header and status code 200', async () => { - await setupTestServer({ - statusCode: SUCCESS, - responseHeaders: { - 'Retry-After': '2700', - 'X-Sentry-Rate-Limits': '60::organization, 2700::organization', - }, - }); - - const transport = makeNodeTransport({ url: TEST_SERVER_URL }); - const transportResponse = await transport.send(EVENT_ENVELOPE); - - expect(transportResponse).toEqual(expect.objectContaining({ status: 'success' })); - }); - - it('should resolve when server responds with rate limit header and status code 200', async () => { - await setupTestServer({ - statusCode: SUCCESS, - responseHeaders: { - 'Retry-After': '2700', - 'X-Sentry-Rate-Limits': '60::organization, 2700::organization', - }, - }); - - const transport = makeNodeTransport({ url: TEST_SERVER_URL }); - const transportResponse = await transport.send(EVENT_ENVELOPE); - - expect(transportResponse).toEqual(expect.objectContaining({ status: 'success' })); - }); - }); - - describe('proxy', () => { - it('can be configured through option', () => { - makeNodeTransport({ - url: 'http://9e9fd4523d784609a5fc0ebb1080592f@sentry.io:8989/mysubpath/50622', - proxy: 'http://example.com', - }); - - expect(httpProxyAgent).toHaveBeenCalledTimes(1); - expect(httpProxyAgent).toHaveBeenCalledWith('http://example.com'); - }); - - it('can be configured through env variables option', () => { - process.env.http_proxy = 'http://example.com'; - makeNodeTransport({ - url: 'http://9e9fd4523d784609a5fc0ebb1080592f@sentry.io:8989/mysubpath/50622', - }); - - expect(httpProxyAgent).toHaveBeenCalledTimes(1); - expect(httpProxyAgent).toHaveBeenCalledWith('http://example.com'); - delete process.env.http_proxy; - }); - - it('client options have priority over env variables', () => { - process.env.http_proxy = 'http://foo.com'; - makeNodeTransport({ - url: 'http://9e9fd4523d784609a5fc0ebb1080592f@sentry.io:8989/mysubpath/50622', - proxy: 'http://bar.com', - }); - - expect(httpProxyAgent).toHaveBeenCalledTimes(1); - expect(httpProxyAgent).toHaveBeenCalledWith('http://bar.com'); - delete process.env.http_proxy; - }); - - it('no_proxy allows for skipping specific hosts', () => { - process.env.no_proxy = 'sentry.io'; - makeNodeTransport({ - url: 'http://9e9fd4523d784609a5fc0ebb1080592f@sentry.io:8989/mysubpath/50622', - proxy: 'http://example.com', - }); - - expect(httpProxyAgent).not.toHaveBeenCalled(); - - delete process.env.no_proxy; - }); - - it('no_proxy works with a port', () => { - process.env.http_proxy = 'http://example.com:8080'; - process.env.no_proxy = 'sentry.io:8989'; - - makeNodeTransport({ - url: 'http://9e9fd4523d784609a5fc0ebb1080592f@sentry.io:8989/mysubpath/50622', - }); - - expect(httpProxyAgent).not.toHaveBeenCalled(); - - delete process.env.no_proxy; - delete process.env.http_proxy; - }); - - it('no_proxy works with multiple comma-separated hosts', () => { - process.env.http_proxy = 'http://example.com:8080'; - process.env.no_proxy = 'example.com,sentry.io,wat.com:1337'; - - makeNodeTransport({ - url: 'http://9e9fd4523d784609a5fc0ebb1080592f@sentry.io:8989/mysubpath/50622', - }); - - expect(httpProxyAgent).not.toHaveBeenCalled(); - - delete process.env.no_proxy; - delete process.env.http_proxy; - }); - }); - - it('should register TransportRequestExecutor that returns the correct object from server response (rate limit)', async () => { - await setupTestServer({ - statusCode: RATE_LIMIT, - responseHeaders: { - 'Retry-After': '2700', - 'X-Sentry-Rate-Limits': '60::organization, 2700::organization', - }, - }); - - makeNodeTransport({ url: TEST_SERVER_URL }); - const registeredRequestExecutor = (createTransport as jest.Mock).mock.calls[0][1]; - - const executorResult = registeredRequestExecutor({ - body: serializeEnvelope(EVENT_ENVELOPE), - category: 'error', - }); - - await expect(executorResult).resolves.toEqual( - expect.objectContaining({ - headers: { - 'retry-after': '2700', - 'x-sentry-rate-limits': '60::organization, 2700::organization', - }, - statusCode: RATE_LIMIT, - }), - ); - }); - - it('should register TransportRequestExecutor that returns the correct object from server response (OK)', async () => { - await setupTestServer({ - statusCode: SUCCESS, - }); - - makeNodeTransport({ url: TEST_SERVER_URL }); - const registeredRequestExecutor = (createTransport as jest.Mock).mock.calls[0][1]; - - const executorResult = registeredRequestExecutor({ - body: serializeEnvelope(EVENT_ENVELOPE), - category: 'error', - }); - - await expect(executorResult).resolves.toEqual( - expect.objectContaining({ - headers: { - 'retry-after': null, - 'x-sentry-rate-limits': null, - }, - statusCode: SUCCESS, - }), - ); - }); - - it('should register TransportRequestExecutor that returns the correct object from server response (OK with rate-limit headers)', async () => { - await setupTestServer({ - statusCode: SUCCESS, - responseHeaders: { - 'Retry-After': '2700', - 'X-Sentry-Rate-Limits': '60::organization, 2700::organization', - }, - }); - - makeNodeTransport({ url: TEST_SERVER_URL }); - const registeredRequestExecutor = (createTransport as jest.Mock).mock.calls[0][1]; - - const executorResult = registeredRequestExecutor({ - body: serializeEnvelope(EVENT_ENVELOPE), - category: 'error', - }); - - await expect(executorResult).resolves.toEqual( - expect.objectContaining({ - headers: { - 'retry-after': '2700', - 'x-sentry-rate-limits': '60::organization, 2700::organization', - }, - statusCode: SUCCESS, - }), - ); - }); - - it('should register TransportRequestExecutor that returns the correct object from server response (NOK with rate-limit headers)', async () => { - await setupTestServer({ - statusCode: RATE_LIMIT, - responseHeaders: { - 'Retry-After': '2700', - 'X-Sentry-Rate-Limits': '60::organization, 2700::organization', - }, - }); - - makeNodeTransport({ url: TEST_SERVER_URL }); - const registeredRequestExecutor = (createTransport as jest.Mock).mock.calls[0][1]; - - const executorResult = registeredRequestExecutor({ - body: serializeEnvelope(EVENT_ENVELOPE), - category: 'error', - }); - - await expect(executorResult).resolves.toEqual( - expect.objectContaining({ - headers: { - 'retry-after': '2700', - 'x-sentry-rate-limits': '60::organization, 2700::organization', - }, - statusCode: RATE_LIMIT, - }), - ); - }); -}); diff --git a/packages/node/test/transports/new/https.test.ts b/packages/node/test/transports/new/https.test.ts deleted file mode 100644 index 7784e16c65df..000000000000 --- a/packages/node/test/transports/new/https.test.ts +++ /dev/null @@ -1,397 +0,0 @@ -import { createTransport } from '@sentry/core'; -import { EventEnvelope, EventItem } from '@sentry/types'; -import { createEnvelope, serializeEnvelope } from '@sentry/utils'; -import * as http from 'http'; -import * as https from 'https'; - -import { HTTPModule, HTTPModuleRequestIncomingMessage } from '../../../src/transports/base/http-module'; -// TODO(v7): We're renaming the imported file so this needs to be changed as well -import { makeNodeTransport } from '../../../src/transports/new'; -import testServerCerts from './test-server-certs'; - -jest.mock('@sentry/core', () => { - const actualCore = jest.requireActual('@sentry/core'); - return { - ...actualCore, - createTransport: jest.fn().mockImplementation(actualCore.createTransport), - }; -}); - -// eslint-disable-next-line @typescript-eslint/no-var-requires -const httpProxyAgent = require('https-proxy-agent'); -jest.mock('https-proxy-agent', () => { - return jest.fn().mockImplementation(() => new http.Agent({ keepAlive: false, maxSockets: 30, timeout: 2000 })); -}); - -const SUCCESS = 200; -const RATE_LIMIT = 429; -const INVALID = 400; -const FAILED = 500; - -interface TestServerOptions { - statusCode: number; - responseHeaders?: Record; -} - -let testServer: http.Server | undefined; - -function setupTestServer( - options: TestServerOptions, - requestInspector?: (req: http.IncomingMessage, body: string) => void, -) { - testServer = https.createServer(testServerCerts, (req, res) => { - let body = ''; - - req.on('data', data => { - body += data; - }); - - req.on('end', () => { - requestInspector?.(req, body); - }); - - res.writeHead(options.statusCode, options.responseHeaders); - res.end(); - - // also terminate socket because keepalive hangs connection a bit - res.connection.end(); - }); - - testServer.listen(8099); - - return new Promise(resolve => { - testServer?.on('listening', resolve); - }); -} - -const TEST_SERVER_URL = 'https://localhost:8099'; - -const EVENT_ENVELOPE = createEnvelope({ event_id: 'aa3ff046696b4bc6b609ce6d28fde9e2', sent_at: '123' }, [ - [{ type: 'event' }, { event_id: 'aa3ff046696b4bc6b609ce6d28fde9e2' }] as EventItem, -]); - -const SERIALIZED_EVENT_ENVELOPE = serializeEnvelope(EVENT_ENVELOPE); - -const unsafeHttpsModule: HTTPModule = { - request: jest - .fn() - .mockImplementation((options: https.RequestOptions, callback?: (res: HTTPModuleRequestIncomingMessage) => void) => { - return https.request({ ...options, rejectUnauthorized: false }, callback); - }), -}; - -describe('makeNewHttpsTransport()', () => { - afterEach(() => { - jest.clearAllMocks(); - - if (testServer) { - testServer.close(); - } - }); - - describe('.send()', () => { - it('should correctly return successful server response', async () => { - await setupTestServer({ statusCode: SUCCESS }); - - const transport = makeNodeTransport({ httpModule: unsafeHttpsModule, url: TEST_SERVER_URL }); - const transportResponse = await transport.send(EVENT_ENVELOPE); - - expect(transportResponse).toEqual(expect.objectContaining({ status: 'success' })); - }); - - it('should correctly send envelope to server', async () => { - await setupTestServer({ statusCode: SUCCESS }, (req, body) => { - expect(req.method).toBe('POST'); - expect(body).toBe(SERIALIZED_EVENT_ENVELOPE); - }); - - const transport = makeNodeTransport({ httpModule: unsafeHttpsModule, url: TEST_SERVER_URL }); - await transport.send(EVENT_ENVELOPE); - }); - - it('should correctly send user-provided headers to server', async () => { - await setupTestServer({ statusCode: SUCCESS }, req => { - expect(req.headers).toEqual( - expect.objectContaining({ - // node http module lower-cases incoming headers - 'x-some-custom-header-1': 'value1', - 'x-some-custom-header-2': 'value2', - }), - ); - }); - - const transport = makeNodeTransport({ - httpModule: unsafeHttpsModule, - url: TEST_SERVER_URL, - headers: { - 'X-Some-Custom-Header-1': 'value1', - 'X-Some-Custom-Header-2': 'value2', - }, - }); - - await transport.send(EVENT_ENVELOPE); - }); - - it.each([ - [RATE_LIMIT, 'rate_limit'], - [INVALID, 'invalid'], - [FAILED, 'failed'], - ])('should correctly reject bad server response (status %i)', async (serverStatusCode, expectedStatus) => { - await setupTestServer({ statusCode: serverStatusCode }); - - const transport = makeNodeTransport({ httpModule: unsafeHttpsModule, url: TEST_SERVER_URL }); - await expect(transport.send(EVENT_ENVELOPE)).rejects.toEqual(expect.objectContaining({ status: expectedStatus })); - }); - - it('should resolve when server responds with rate limit header and status code 200', async () => { - await setupTestServer({ - statusCode: SUCCESS, - responseHeaders: { - 'Retry-After': '2700', - 'X-Sentry-Rate-Limits': '60::organization, 2700::organization', - }, - }); - - const transport = makeNodeTransport({ httpModule: unsafeHttpsModule, url: TEST_SERVER_URL }); - const transportResponse = await transport.send(EVENT_ENVELOPE); - - expect(transportResponse).toEqual(expect.objectContaining({ status: 'success' })); - }); - - it('should resolve when server responds with rate limit header and status code 200', async () => { - await setupTestServer({ - statusCode: SUCCESS, - responseHeaders: { - 'Retry-After': '2700', - 'X-Sentry-Rate-Limits': '60::organization, 2700::organization', - }, - }); - - const transport = makeNodeTransport({ httpModule: unsafeHttpsModule, url: TEST_SERVER_URL }); - const transportResponse = await transport.send(EVENT_ENVELOPE); - - expect(transportResponse).toEqual(expect.objectContaining({ status: 'success' })); - }); - - it('should use `caCerts` option', async () => { - await setupTestServer({ statusCode: SUCCESS }); - - const transport = makeNodeTransport({ - httpModule: unsafeHttpsModule, - url: TEST_SERVER_URL, - caCerts: 'some cert', - }); - - await transport.send(EVENT_ENVELOPE); - - // eslint-disable-next-line @typescript-eslint/unbound-method - expect(unsafeHttpsModule.request).toHaveBeenCalledWith( - expect.objectContaining({ - ca: 'some cert', - }), - expect.anything(), - ); - }); - }); - - describe('proxy', () => { - it('can be configured through option', () => { - makeNodeTransport({ - httpModule: unsafeHttpsModule, - url: 'https://9e9fd4523d784609a5fc0ebb1080592f@sentry.io:8989/mysubpath/50622', - proxy: 'https://example.com', - }); - - expect(httpProxyAgent).toHaveBeenCalledTimes(1); - expect(httpProxyAgent).toHaveBeenCalledWith('https://example.com'); - }); - - it('can be configured through env variables option (http)', () => { - process.env.http_proxy = 'https://example.com'; - makeNodeTransport({ - httpModule: unsafeHttpsModule, - url: 'https://9e9fd4523d784609a5fc0ebb1080592f@sentry.io:8989/mysubpath/50622', - }); - - expect(httpProxyAgent).toHaveBeenCalledTimes(1); - expect(httpProxyAgent).toHaveBeenCalledWith('https://example.com'); - delete process.env.http_proxy; - }); - - it('can be configured through env variables option (https)', () => { - process.env.https_proxy = 'https://example.com'; - makeNodeTransport({ - httpModule: unsafeHttpsModule, - url: 'https://9e9fd4523d784609a5fc0ebb1080592f@sentry.io:8989/mysubpath/50622', - }); - - expect(httpProxyAgent).toHaveBeenCalledTimes(1); - expect(httpProxyAgent).toHaveBeenCalledWith('https://example.com'); - delete process.env.https_proxy; - }); - - it('client options have priority over env variables', () => { - process.env.https_proxy = 'https://foo.com'; - makeNodeTransport({ - httpModule: unsafeHttpsModule, - url: 'https://9e9fd4523d784609a5fc0ebb1080592f@sentry.io:8989/mysubpath/50622', - proxy: 'https://bar.com', - }); - - expect(httpProxyAgent).toHaveBeenCalledTimes(1); - expect(httpProxyAgent).toHaveBeenCalledWith('https://bar.com'); - delete process.env.https_proxy; - }); - - it('no_proxy allows for skipping specific hosts', () => { - process.env.no_proxy = 'sentry.io'; - makeNodeTransport({ - httpModule: unsafeHttpsModule, - url: 'https://9e9fd4523d784609a5fc0ebb1080592f@sentry.io:8989/mysubpath/50622', - proxy: 'https://example.com', - }); - - expect(httpProxyAgent).not.toHaveBeenCalled(); - - delete process.env.no_proxy; - }); - - it('no_proxy works with a port', () => { - process.env.http_proxy = 'https://example.com:8080'; - process.env.no_proxy = 'sentry.io:8989'; - - makeNodeTransport({ - httpModule: unsafeHttpsModule, - url: 'https://9e9fd4523d784609a5fc0ebb1080592f@sentry.io:8989/mysubpath/50622', - }); - - expect(httpProxyAgent).not.toHaveBeenCalled(); - - delete process.env.no_proxy; - delete process.env.http_proxy; - }); - - it('no_proxy works with multiple comma-separated hosts', () => { - process.env.http_proxy = 'https://example.com:8080'; - process.env.no_proxy = 'example.com,sentry.io,wat.com:1337'; - - makeNodeTransport({ - httpModule: unsafeHttpsModule, - url: 'https://9e9fd4523d784609a5fc0ebb1080592f@sentry.io:8989/mysubpath/50622', - }); - - expect(httpProxyAgent).not.toHaveBeenCalled(); - - delete process.env.no_proxy; - delete process.env.http_proxy; - }); - }); - - it('should register TransportRequestExecutor that returns the correct object from server response (rate limit)', async () => { - await setupTestServer({ - statusCode: RATE_LIMIT, - responseHeaders: { - 'Retry-After': '2700', - 'X-Sentry-Rate-Limits': '60::organization, 2700::organization', - }, - }); - - makeNodeTransport({ httpModule: unsafeHttpsModule, url: TEST_SERVER_URL }); - const registeredRequestExecutor = (createTransport as jest.Mock).mock.calls[0][1]; - - const executorResult = registeredRequestExecutor({ - body: serializeEnvelope(EVENT_ENVELOPE), - category: 'error', - }); - - await expect(executorResult).resolves.toEqual( - expect.objectContaining({ - headers: { - 'retry-after': '2700', - 'x-sentry-rate-limits': '60::organization, 2700::organization', - }, - statusCode: RATE_LIMIT, - }), - ); - }); - - it('should register TransportRequestExecutor that returns the correct object from server response (OK)', async () => { - await setupTestServer({ - statusCode: SUCCESS, - }); - - makeNodeTransport({ httpModule: unsafeHttpsModule, url: TEST_SERVER_URL }); - const registeredRequestExecutor = (createTransport as jest.Mock).mock.calls[0][1]; - - const executorResult = registeredRequestExecutor({ - body: serializeEnvelope(EVENT_ENVELOPE), - category: 'error', - }); - - await expect(executorResult).resolves.toEqual( - expect.objectContaining({ - headers: { - 'retry-after': null, - 'x-sentry-rate-limits': null, - }, - statusCode: SUCCESS, - }), - ); - }); - - it('should register TransportRequestExecutor that returns the correct object from server response (OK with rate-limit headers)', async () => { - await setupTestServer({ - statusCode: SUCCESS, - responseHeaders: { - 'Retry-After': '2700', - 'X-Sentry-Rate-Limits': '60::organization, 2700::organization', - }, - }); - - makeNodeTransport({ httpModule: unsafeHttpsModule, url: TEST_SERVER_URL }); - const registeredRequestExecutor = (createTransport as jest.Mock).mock.calls[0][1]; - - const executorResult = registeredRequestExecutor({ - body: serializeEnvelope(EVENT_ENVELOPE), - category: 'error', - }); - - await expect(executorResult).resolves.toEqual( - expect.objectContaining({ - headers: { - 'retry-after': '2700', - 'x-sentry-rate-limits': '60::organization, 2700::organization', - }, - statusCode: SUCCESS, - }), - ); - }); - - it('should register TransportRequestExecutor that returns the correct object from server response (NOK with rate-limit headers)', async () => { - await setupTestServer({ - statusCode: RATE_LIMIT, - responseHeaders: { - 'Retry-After': '2700', - 'X-Sentry-Rate-Limits': '60::organization, 2700::organization', - }, - }); - - makeNodeTransport({ httpModule: unsafeHttpsModule, url: TEST_SERVER_URL }); - const registeredRequestExecutor = (createTransport as jest.Mock).mock.calls[0][1]; - - const executorResult = registeredRequestExecutor({ - body: serializeEnvelope(EVENT_ENVELOPE), - category: 'error', - }); - - await expect(executorResult).resolves.toEqual( - expect.objectContaining({ - headers: { - 'retry-after': '2700', - 'x-sentry-rate-limits': '60::organization, 2700::organization', - }, - statusCode: RATE_LIMIT, - }), - ); - }); -}); diff --git a/packages/node/test/transports/new/test-server-certs.ts b/packages/node/test/transports/test-server-certs.ts similarity index 100% rename from packages/node/test/transports/new/test-server-certs.ts rename to packages/node/test/transports/test-server-certs.ts diff --git a/packages/types/src/client.ts b/packages/types/src/client.ts index b1cadf971197..9b91c6712e06 100644 --- a/packages/types/src/client.ts +++ b/packages/types/src/client.ts @@ -5,7 +5,7 @@ import { ClientOptions } from './options'; import { Scope } from './scope'; import { Session, SessionAggregates } from './session'; import { Severity, SeverityLevel } from './severity'; -import { NewTransport } from './transport'; +import { Transport } from './transport'; /** * User-Facing Sentry SDK Client. @@ -72,7 +72,7 @@ export interface Client { * * @returns The transport. */ - getTransport(): NewTransport | undefined; + getTransport(): Transport | undefined; /** * Flush the event queue and set the client to `enabled = false`. See {@link Client.flush}. diff --git a/packages/types/src/index.ts b/packages/types/src/index.ts index 3c066e3cc0bf..ab2592cfe02e 100644 --- a/packages/types/src/index.ts +++ b/packages/types/src/index.ts @@ -65,14 +65,12 @@ export type { Thread } from './thread'; export type { Outcome, Transport, - TransportOptions, TransportCategory, TransportRequest, TransportMakeRequestResponse, TransportResponse, InternalBaseTransportOptions, BaseTransportOptions, - NewTransport, TransportRequestExecutor, } from './transport'; export type { User, UserFeedback } from './user'; diff --git a/packages/types/src/options.ts b/packages/types/src/options.ts index 61188d6c099f..b310b8e1799e 100644 --- a/packages/types/src/options.ts +++ b/packages/types/src/options.ts @@ -5,7 +5,7 @@ import { CaptureContext } from './scope'; import { SdkMetadata } from './sdkmetadata'; import { StackLineParser, StackParser } from './stacktrace'; import { SamplingContext } from './transaction'; -import { BaseTransportOptions, NewTransport } from './transport'; +import { BaseTransportOptions, Transport } from './transport'; export interface ClientOptions { /** @@ -61,7 +61,7 @@ export interface ClientOptions NewTransport; + transport: (transportOptions: TO) => Transport; /** * A stack parser implementation @@ -221,7 +221,7 @@ export interface Options /** * Transport object that should be used to send events to Sentry */ - transport?: (transportOptions: TO) => NewTransport; + transport?: (transportOptions: TO) => Transport; /** * A stack parser implementation or an array of stack line parsers diff --git a/packages/types/src/transport.ts b/packages/types/src/transport.ts index 1250d4cb01d5..1c2449a386b5 100644 --- a/packages/types/src/transport.ts +++ b/packages/types/src/transport.ts @@ -1,11 +1,5 @@ -import { DsnLike } from './dsn'; import { Envelope } from './envelope'; -import { Event } from './event'; import { EventStatus } from './eventstatus'; -import { SentryRequestType } from './request'; -import { Response } from './response'; -import { SdkMetadata } from './sdkmetadata'; -import { Session, SessionAggregates } from './session'; export type Outcome = | 'before_send' @@ -48,69 +42,9 @@ export interface BaseTransportOptions extends InternalBaseTransportOptions { url: string; } -export interface NewTransport { +export interface Transport { send(request: Envelope): PromiseLike; flush(timeout?: number): PromiseLike; } export type TransportRequestExecutor = (request: TransportRequest) => PromiseLike; - -/** Transport used sending data to Sentry */ -export interface Transport { - /** - * Sends the event to the Store endpoint in Sentry. - * - * @param event Event that should be sent to Sentry. - */ - sendEvent(event: Event): PromiseLike; - - /** - * Sends the session to the Envelope endpoint in Sentry. - * - * @param session Session that should be sent to Sentry | Session Aggregates that should be sent to Sentry. - */ - sendSession?(session: Session | SessionAggregates): PromiseLike; - - /** - * Wait for all events to be sent or the timeout to expire, whichever comes first. - * - * @param timeout Maximum time in ms the transport should wait for events to be flushed. Omitting this parameter will - * cause the transport to wait until all events are sent before resolving the promise. - * @returns A promise that will resolve with `true` if all events are sent before the timeout, or `false` if there are - * still events in the queue when the timeout is reached. - */ - close(timeout?: number): PromiseLike; - - /** - * Increment the counter for the specific client outcome - */ - recordLostEvent?(type: Outcome, category: SentryRequestType): void; -} - -/** JSDoc */ -export type TransportClass = new (options: TransportOptions) => T; - -/** JSDoc */ -export interface TransportOptions { - /** Sentry DSN */ - dsn: DsnLike; - /** Define custom headers */ - headers?: { [key: string]: string }; - /** Set a HTTP proxy that should be used for outbound requests. */ - httpProxy?: string; - /** Set a HTTPS proxy that should be used for outbound requests. */ - httpsProxy?: string; - /** HTTPS proxy certificates path */ - caCerts?: string; - /** Fetch API init parameters */ - fetchParameters?: { [key: string]: string }; - /** The envelope tunnel to use. */ - tunnel?: string; - /** Send SDK Client Reports. Enabled by default. */ - sendClientReports?: boolean; - /** - * Set of metadata about the SDK that can be internally used to enhance envelopes and events, - * and provide additional data about every request. - * */ - _metadata?: SdkMetadata; -} From e0374a3b0e9efc2c5000d9e9e708b63365963a07 Mon Sep 17 00:00:00 2001 From: Abhijeet Prasad Date: Tue, 26 Apr 2022 12:42:43 -0400 Subject: [PATCH 077/204] ref(minimal): Delete `@sentry/minimal` (#4971) Remove the minimal package and `callOnHub`. Instead, call hub methods directly. This gives static typing and bundle size improvements. --- MIGRATION.md | 22 ++++ README.md | 2 - package.json | 1 - packages/core/package.json | 1 - packages/core/src/index.ts | 10 +- packages/core/test/mocks/integration.ts | 3 +- .../src/index.ts => hub/src/exports.ts} | 115 ++++++------------ packages/hub/src/hub.ts | 46 +------ packages/hub/src/index.ts | 15 +++ .../test/exports.test.ts} | 38 +++--- packages/minimal/.eslintrc.js | 3 - packages/minimal/.npmignore | 8 -- packages/minimal/LICENSE | 29 ----- packages/minimal/README.md | 63 ---------- packages/minimal/jest.config.js | 1 - packages/minimal/package.json | 53 -------- packages/minimal/test/mocks/client.ts | 19 --- packages/minimal/tsconfig.cjs.json | 8 -- packages/minimal/tsconfig.esm.json | 8 -- packages/minimal/tsconfig.json | 9 -- packages/minimal/tsconfig.test.json | 12 -- packages/minimal/tsconfig.types.json | 10 -- packages/nextjs/test/integration/package.json | 1 - packages/node/test/handlers.test.ts | 45 ++++--- packages/react/package.json | 1 - packages/react/src/redux.ts | 2 +- packages/react/test/redux.test.ts | 5 +- packages/serverless/package.json | 1 - packages/tracing/package.json | 1 - packages/vue/package.json | 1 - rollup/plugins/bundlePlugins.js | 1 - yarn.lock | 12 +- 32 files changed, 149 insertions(+), 397 deletions(-) rename packages/{minimal/src/index.ts => hub/src/exports.ts} (61%) rename packages/{minimal/test/lib/minimal.test.ts => hub/test/exports.test.ts} (94%) delete mode 100644 packages/minimal/.eslintrc.js delete mode 100644 packages/minimal/.npmignore delete mode 100644 packages/minimal/LICENSE delete mode 100644 packages/minimal/README.md delete mode 100644 packages/minimal/jest.config.js delete mode 100644 packages/minimal/package.json delete mode 100644 packages/minimal/test/mocks/client.ts delete mode 100644 packages/minimal/tsconfig.cjs.json delete mode 100644 packages/minimal/tsconfig.esm.json delete mode 100644 packages/minimal/tsconfig.json delete mode 100644 packages/minimal/tsconfig.test.json delete mode 100644 packages/minimal/tsconfig.types.json diff --git a/MIGRATION.md b/MIGRATION.md index 3c5b999f11a3..0a1141cf5cac 100644 --- a/MIGRATION.md +++ b/MIGRATION.md @@ -10,6 +10,28 @@ Node.js version 6 has reached end of life in April 2019. For Sentry JavaScript S As far as SDK development goes, dropping support means no longer running integration tests for Node.js version 6, and also no longer handling edge cases specific to version 6. Running the new SDK version on Node.js v6 is therefore highly discouraged. +## Removal of `@sentry/minimal` + +The `@sentry/minimal` package was deleted and it's functionality was moved to `@sentry/hub`. All exports from `@sentry/minimal` should be avaliable in `@sentry/hub` other than `_callOnClient` function which was removed. + +```ts +// New in v7: +import { + addBreadcrumb, + captureException, + configureScope, + setTag, +} from '@sentry/hub'; + +// Before: +import { + addBreadcrumb, + captureException, + configureScope, + setTag, +} from '@sentry/minimal'; +``` + ## Removal Of Old Platform Integrations From `@sentry/integrations` Package The following classes will be removed from the `@sentry/integrations` package and can no longer be used: diff --git a/README.md b/README.md index 84e46f6b362a..369c363e3599 100644 --- a/README.md +++ b/README.md @@ -104,8 +104,6 @@ below: extensions for Performance Monitoring / Tracing - [`@sentry/hub`](https://github.com/getsentry/sentry-javascript/tree/master/packages/hub): Global state management of SDKs -- [`@sentry/minimal`](https://github.com/getsentry/sentry-javascript/tree/master/packages/minimal): Minimal SDK for - library authors to add Sentry support - [`@sentry/core`](https://github.com/getsentry/sentry-javascript/tree/master/packages/core): The base for all JavaScript SDKs with interfaces, type definitions and base classes. - [`@sentry/utils`](https://github.com/getsentry/sentry-javascript/tree/master/packages/utils): A set of helpers and diff --git a/package.json b/package.json index 6edf51939fc1..ab2d3e224bfc 100644 --- a/package.json +++ b/package.json @@ -40,7 +40,6 @@ "packages/hub", "packages/integration-tests", "packages/integrations", - "packages/minimal", "packages/nextjs", "packages/node", "packages/node-integration-tests", diff --git a/packages/core/package.json b/packages/core/package.json index 8a53ffca97e3..737007cf648b 100644 --- a/packages/core/package.json +++ b/packages/core/package.json @@ -17,7 +17,6 @@ }, "dependencies": { "@sentry/hub": "7.0.0-alpha.1", - "@sentry/minimal": "7.0.0-alpha.1", "@sentry/types": "7.0.0-alpha.1", "@sentry/utils": "7.0.0-alpha.1", "tslib": "^1.9.3" diff --git a/packages/core/src/index.ts b/packages/core/src/index.ts index 0b0585a3da68..a5231620e7a3 100644 --- a/packages/core/src/index.ts +++ b/packages/core/src/index.ts @@ -15,8 +15,14 @@ export { setTags, setUser, withScope, -} from '@sentry/minimal'; -export { addGlobalEventProcessor, getCurrentHub, getHubFromCarrier, Hub, makeMain, Scope, Session } from '@sentry/hub'; + addGlobalEventProcessor, + getCurrentHub, + getHubFromCarrier, + Hub, + makeMain, + Scope, + Session, +} from '@sentry/hub'; export { getEnvelopeEndpointWithUrlEncodedAuth, getStoreEndpointWithUrlEncodedAuth, diff --git a/packages/core/test/mocks/integration.ts b/packages/core/test/mocks/integration.ts index 8b95b5673af8..2e192fb12227 100644 --- a/packages/core/test/mocks/integration.ts +++ b/packages/core/test/mocks/integration.ts @@ -1,5 +1,4 @@ -import { getCurrentHub } from '@sentry/hub'; -import { configureScope } from '@sentry/minimal'; +import { configureScope, getCurrentHub } from '@sentry/hub'; import { Event, EventProcessor, Integration } from '@sentry/types'; export class TestIntegration implements Integration { diff --git a/packages/minimal/src/index.ts b/packages/hub/src/exports.ts similarity index 61% rename from packages/minimal/src/index.ts rename to packages/hub/src/exports.ts index 7cb0271786ab..451b2b13e9ed 100644 --- a/packages/minimal/src/index.ts +++ b/packages/hub/src/exports.ts @@ -1,4 +1,3 @@ -import { getCurrentHub, Hub, Scope } from '@sentry/hub'; import { Breadcrumb, CaptureContext, @@ -9,25 +8,19 @@ import { Primitive, Severity, SeverityLevel, - Transaction, TransactionContext, User, } from '@sentry/types'; -/** - * This calls a function on the current hub. - * @param method function to call on hub. - * @param args to pass to function. - */ -// eslint-disable-next-line @typescript-eslint/no-explicit-any -function callOnHub(method: string, ...args: any[]): T { - const hub = getCurrentHub(); - if (hub && hub[method as keyof Hub]) { - // eslint-disable-next-line @typescript-eslint/no-explicit-any - return (hub[method as keyof Hub] as any)(...args); - } - throw new Error(`No hub defined or ${method} was not found on the hub, please open a bug report.`); -} +import { getCurrentHub, Hub } from './hub'; +import { Scope } from './scope'; + +// Note: All functions in this file are typed with a return value of `ReturnType`, +// where HUB_FUNCTION is some method on the Hub class. +// +// This is done to make sure the top level SDK methods stay in sync with the hub methods. +// Although every method here has an explicit return type, some of them (that map to void returns) do not +// contain `return` keywords. This is done to save on bundle size, as `return` is not minifiable. /** * Captures an exception event and sends it to Sentry. @@ -36,14 +29,8 @@ function callOnHub(method: string, ...args: any[]): T { * @returns The generated eventId. */ // eslint-disable-next-line @typescript-eslint/no-explicit-any, @typescript-eslint/explicit-module-boundary-types -export function captureException(exception: any, captureContext?: CaptureContext): string { - const syntheticException = new Error('Sentry syntheticException'); - - return callOnHub('captureException', exception, { - captureContext, - originalException: exception, - syntheticException, - }); +export function captureException(exception: any, captureContext?: CaptureContext): ReturnType { + return getCurrentHub().captureException(exception, { captureContext }); } /** @@ -57,19 +44,12 @@ export function captureMessage( message: string, // eslint-disable-next-line deprecation/deprecation captureContext?: CaptureContext | Severity | SeverityLevel, -): string { - const syntheticException = new Error(message); - +): ReturnType { // This is necessary to provide explicit scopes upgrade, without changing the original // arity of the `captureMessage(message, level)` method. const level = typeof captureContext === 'string' ? captureContext : undefined; const context = typeof captureContext !== 'string' ? { captureContext } : undefined; - - return callOnHub('captureMessage', message, level, { - originalException: message, - syntheticException, - ...context, - }); + return getCurrentHub().captureMessage(message, level, context); } /** @@ -78,16 +58,16 @@ export function captureMessage( * @param event The event to send to Sentry. * @returns The generated eventId. */ -export function captureEvent(event: Event): string { - return callOnHub('captureEvent', event); +export function captureEvent(event: Event): ReturnType { + return getCurrentHub().captureEvent(event); } /** * Callback to set context information onto the scope. * @param callback Callback function that receives Scope. */ -export function configureScope(callback: (scope: Scope) => void): void { - callOnHub('configureScope', callback); +export function configureScope(callback: (scope: Scope) => void): ReturnType { + getCurrentHub().configureScope(callback); } /** @@ -98,8 +78,8 @@ export function configureScope(callback: (scope: Scope) => void): void { * * @param breadcrumb The breadcrumb to record. */ -export function addBreadcrumb(breadcrumb: Breadcrumb): void { - callOnHub('addBreadcrumb', breadcrumb); +export function addBreadcrumb(breadcrumb: Breadcrumb): ReturnType { + getCurrentHub().addBreadcrumb(breadcrumb); } /** @@ -108,33 +88,33 @@ export function addBreadcrumb(breadcrumb: Breadcrumb): void { * @param context Any kind of data. This data will be normalized. */ // eslint-disable-next-line @typescript-eslint/no-explicit-any -export function setContext(name: string, context: { [key: string]: any } | null): void { - callOnHub('setContext', name, context); +export function setContext(name: string, context: { [key: string]: any } | null): ReturnType { + getCurrentHub().setContext(name, context); } /** * Set an object that will be merged sent as extra data with the event. * @param extras Extras object to merge into current context. */ -export function setExtras(extras: Extras): void { - callOnHub('setExtras', extras); +export function setExtras(extras: Extras): ReturnType { + getCurrentHub().setExtras(extras); } /** - * Set an object that will be merged sent as tags data with the event. - * @param tags Tags context object to merge into current context. + * Set key:value that will be sent as extra data with the event. + * @param key String of extra + * @param extra Any kind of data. This data will be normalized. */ -export function setTags(tags: { [key: string]: Primitive }): void { - callOnHub('setTags', tags); +export function setExtra(key: string, extra: Extra): ReturnType { + getCurrentHub().setExtra(key, extra); } /** - * Set key:value that will be sent as extra data with the event. - * @param key String of extra - * @param extra Any kind of data. This data will be normalized. + * Set an object that will be merged sent as tags data with the event. + * @param tags Tags context object to merge into current context. */ -export function setExtra(key: string, extra: Extra): void { - callOnHub('setExtra', key, extra); +export function setTags(tags: { [key: string]: Primitive }): ReturnType { + getCurrentHub().setTags(tags); } /** @@ -145,8 +125,8 @@ export function setExtra(key: string, extra: Extra): void { * @param key String key of tag * @param value Value of tag */ -export function setTag(key: string, value: Primitive): void { - callOnHub('setTag', key, value); +export function setTag(key: string, value: Primitive): ReturnType { + getCurrentHub().setTag(key, value); } /** @@ -154,8 +134,8 @@ export function setTag(key: string, value: Primitive): void { * * @param user User context object to be set in the current context. Pass `null` to unset the user. */ -export function setUser(user: User | null): void { - callOnHub('setUser', user); +export function setUser(user: User | null): ReturnType { + getCurrentHub().setUser(user); } /** @@ -171,23 +151,8 @@ export function setUser(user: User | null): void { * * @param callback that will be enclosed into push/popScope. */ -export function withScope(callback: (scope: Scope) => void): void { - callOnHub('withScope', callback); -} - -/** - * Calls a function on the latest client. Use this with caution, it's meant as - * in "internal" helper so we don't need to expose every possible function in - * the shim. It is not guaranteed that the client actually implements the - * function. - * - * @param method The method to call on the client/client. - * @param args Arguments to pass to the client/fontend. - * @hidden - */ -// eslint-disable-next-line @typescript-eslint/no-explicit-any -export function _callOnClient(method: string, ...args: any[]): void { - callOnHub('_invokeClient', method, ...args); +export function withScope(callback: (scope: Scope) => void): ReturnType { + getCurrentHub().withScope(callback); } /** @@ -210,6 +175,6 @@ export function _callOnClient(method: string, ...args: any[]): void { export function startTransaction( context: TransactionContext, customSamplingContext?: CustomSamplingContext, -): Transaction { - return callOnHub('startTransaction', { ...context }, customSamplingContext); +): ReturnType { + return getCurrentHub().startTransaction({ ...context }, customSamplingContext); } diff --git a/packages/hub/src/hub.ts b/packages/hub/src/hub.ts index 74a810fafeea..4e97cd8aa9b3 100644 --- a/packages/hub/src/hub.ts +++ b/packages/hub/src/hub.ts @@ -185,27 +185,10 @@ export class Hub implements HubInterface { // eslint-disable-next-line @typescript-eslint/no-explicit-any, @typescript-eslint/explicit-module-boundary-types public captureException(exception: any, hint?: EventHint): string { const eventId = (this._lastEventId = hint && hint.event_id ? hint.event_id : uuid4()); - let finalHint = hint; - - // If there's no explicit hint provided, mimic the same thing that would happen - // in the minimal itself to create a consistent behavior. - // We don't do this in the client, as it's the lowest level API, and doing this, - // would prevent user from having full control over direct calls. - if (!hint) { - let syntheticException: Error; - try { - throw new Error('Sentry syntheticException'); - } catch (exception) { - syntheticException = exception as Error; - } - finalHint = { - originalException: exception, - syntheticException, - }; - } - this._invokeClient('captureException', exception, { - ...finalHint, + originalException: exception, + syntheticException: new Error('Sentry syntheticException'), + ...hint, event_id: eventId, }); return eventId; @@ -221,27 +204,10 @@ export class Hub implements HubInterface { hint?: EventHint, ): string { const eventId = (this._lastEventId = hint && hint.event_id ? hint.event_id : uuid4()); - let finalHint = hint; - - // If there's no explicit hint provided, mimic the same thing that would happen - // in the minimal itself to create a consistent behavior. - // We don't do this in the client, as it's the lowest level API, and doing this, - // would prevent user from having full control over direct calls. - if (!hint) { - let syntheticException: Error; - try { - throw new Error(message); - } catch (exception) { - syntheticException = exception as Error; - } - finalHint = { - originalException: message, - syntheticException, - }; - } - this._invokeClient('captureMessage', message, level, { - ...finalHint, + originalException: message, + syntheticException: new Error(message), + ...hint, event_id: eventId, }); return eventId; diff --git a/packages/hub/src/index.ts b/packages/hub/src/index.ts index 16c99ec7bc89..92b85f370d80 100644 --- a/packages/hub/src/index.ts +++ b/packages/hub/src/index.ts @@ -4,3 +4,18 @@ export { addGlobalEventProcessor, Scope } from './scope'; export { Session } from './session'; export { SessionFlusher } from './sessionflusher'; export { getCurrentHub, getHubFromCarrier, getMainCarrier, Hub, makeMain, setHubOnCarrier } from './hub'; +export { + addBreadcrumb, + captureException, + captureEvent, + captureMessage, + configureScope, + startTransaction, + setContext, + setExtra, + setExtras, + setTag, + setTags, + setUser, + withScope, +} from './exports'; diff --git a/packages/minimal/test/lib/minimal.test.ts b/packages/hub/test/exports.test.ts similarity index 94% rename from packages/minimal/test/lib/minimal.test.ts rename to packages/hub/test/exports.test.ts index 48a117cd5c5d..91b7450b66eb 100644 --- a/packages/minimal/test/lib/minimal.test.ts +++ b/packages/hub/test/exports.test.ts @@ -1,7 +1,5 @@ -import { getCurrentHub, getHubFromCarrier, Scope } from '@sentry/hub'; - +import { getCurrentHub, getHubFromCarrier, Scope } from '../src'; import { - _callOnClient, captureEvent, captureException, captureMessage, @@ -13,13 +11,30 @@ import { setTags, setUser, withScope, -} from '../../src'; -import { init, TestClient, TestClient2 } from '../mocks/client'; +} from '../src/exports'; + +export class TestClient { + public static instance?: TestClient; + + public constructor(public options: Record) { + TestClient.instance = this; + } + + public mySecretPublicMethod(str: string): string { + return `secret: ${str}`; + } +} + +export class TestClient2 {} + +export function init(options: Record): void { + getCurrentHub().bindClient(new TestClient(options) as any); +} // eslint-disable-next-line no-var declare var global: any; -describe('Minimal', () => { +describe('Top Level API', () => { beforeEach(() => { global.__SENTRY__ = { hub: undefined, @@ -196,17 +211,6 @@ describe('Minimal', () => { expect(getCurrentHub().getClient()).toBe(TestClient.instance); }); - test('Calls function on the client', done => { - const s = jest.spyOn(TestClient.prototype, 'mySecretPublicMethod'); - getCurrentHub().withScope(() => { - getCurrentHub().bindClient(new TestClient({}) as any); - _callOnClient('mySecretPublicMethod', 'test'); - expect(s.mock.calls[0][0]).toBe('test'); - s.mockRestore(); - done(); - }); - }); - test('does not throw an error when pushing different clients', () => { init({}); expect(() => { diff --git a/packages/minimal/.eslintrc.js b/packages/minimal/.eslintrc.js deleted file mode 100644 index 5a2cc7f1ec08..000000000000 --- a/packages/minimal/.eslintrc.js +++ /dev/null @@ -1,3 +0,0 @@ -module.exports = { - extends: ['../../.eslintrc.js'], -}; diff --git a/packages/minimal/.npmignore b/packages/minimal/.npmignore deleted file mode 100644 index cb864514088e..000000000000 --- a/packages/minimal/.npmignore +++ /dev/null @@ -1,8 +0,0 @@ -# The paths in this file are specified so that they align with the file structure in `./build` after this file is copied -# into it by the prepack script `scripts/prepack.ts`. - -* - -!/cjs/**/* -!/esm/**/* -!/types/**/* diff --git a/packages/minimal/LICENSE b/packages/minimal/LICENSE deleted file mode 100644 index 8b42db873c95..000000000000 --- a/packages/minimal/LICENSE +++ /dev/null @@ -1,29 +0,0 @@ -BSD 3-Clause License - -Copyright (c) 2019, Sentry -All rights reserved. - -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions are met: - -* Redistributions of source code must retain the above copyright notice, this - list of conditions and the following disclaimer. - -* Redistributions in binary form must reproduce the above copyright notice, - this list of conditions and the following disclaimer in the documentation - and/or other materials provided with the distribution. - -* Neither the name of the copyright holder nor the names of its - contributors may be used to endorse or promote products derived from - this software without specific prior written permission. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" -AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE -FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL -DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR -SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER -CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, -OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/packages/minimal/README.md b/packages/minimal/README.md deleted file mode 100644 index 4204f58846d4..000000000000 --- a/packages/minimal/README.md +++ /dev/null @@ -1,63 +0,0 @@ -

- - - -
-

- -# Sentry JavaScript SDK Minimal - -[![npm version](https://img.shields.io/npm/v/@sentry/minimal.svg)](https://www.npmjs.com/package/@sentry/minimal) -[![npm dm](https://img.shields.io/npm/dm/@sentry/minimal.svg)](https://www.npmjs.com/package/@sentry/minimal) -[![npm dt](https://img.shields.io/npm/dt/@sentry/minimal.svg)](https://www.npmjs.com/package/@sentry/minimal) -[![typedoc](https://img.shields.io/badge/docs-typedoc-blue.svg)](http://getsentry.github.io/sentry-javascript/) - -## Links - -- [Official SDK Docs](https://docs.sentry.io/quickstart/) -- [TypeDoc](http://getsentry.github.io/sentry-javascript/) - -## General - -A minimal Sentry SDK that uses a configured client when embedded into an application. It allows library authors add -support for a Sentry SDK without having to bundle the entire SDK or being dependent on a specific platform. If the user -is using Sentry in their application and your library uses `@sentry/minimal`, the user receives all -breadcrumbs/messages/events you added to your libraries codebase. - -## Usage - -To use the minimal, you do not have to initialize an SDK. This should be handled by the user of your library. Instead, -directly use the exported functions of `@sentry/minimal` to add breadcrumbs or capture events: - -```javascript -import * as Sentry from '@sentry/minimal'; - -// Add a breadcrumb for future events -Sentry.addBreadcrumb({ - message: 'My Breadcrumb', - // ... -}); - -// Capture exceptions, messages or manual events -Sentry.captureMessage('Hello, world!'); -Sentry.captureException(new Error('Good bye')); -Sentry.captureEvent({ - message: 'Manual', - stacktrace: [ - // ... - ], -}); -``` - -Note that while strictly possible, it is discouraged to interfere with the event context. If for some reason your -library needs to inject context information, beware that this might override the user's context values: - -```javascript -// Set user information, as well as tags and further extras -Sentry.configureScope(scope => { - scope.setExtra('battery', 0.7); - scope.setTag('user_mode', 'admin'); - scope.setUser({ id: '4711' }); - // scope.clear(); -}); -``` diff --git a/packages/minimal/jest.config.js b/packages/minimal/jest.config.js deleted file mode 100644 index 58141f076dc4..000000000000 --- a/packages/minimal/jest.config.js +++ /dev/null @@ -1 +0,0 @@ -module.exports = require('../../jest.config.js'); diff --git a/packages/minimal/package.json b/packages/minimal/package.json deleted file mode 100644 index 3ca8889512a2..000000000000 --- a/packages/minimal/package.json +++ /dev/null @@ -1,53 +0,0 @@ -{ - "name": "@sentry/minimal", - "version": "7.0.0-alpha.1", - "description": "Sentry minimal library that can be used in other packages", - "repository": "git://github.com/getsentry/sentry-javascript.git", - "homepage": "https://github.com/getsentry/sentry-javascript/tree/master/packages/minimal", - "author": "Sentry", - "license": "BSD-3-Clause", - "engines": { - "node": ">=8" - }, - "main": "build/cjs/index.js", - "module": "build/esm/index.js", - "types": "build/types/index.d.ts", - "publishConfig": { - "access": "public" - }, - "dependencies": { - "@sentry/hub": "7.0.0-alpha.1", - "@sentry/types": "7.0.0-alpha.1", - "tslib": "^1.9.3" - }, - "scripts": { - "build": "run-p build:cjs build:esm build:types", - "build:cjs": "tsc -p tsconfig.cjs.json", - "build:dev": "run-s build", - "build:es5": "yarn build:cjs # *** backwards compatibility - remove in v7 ***", - "build:esm": "tsc -p tsconfig.esm.json", - "build:types": "tsc -p tsconfig.types.json", - "build:watch": "run-p build:cjs:watch build:esm:watch build:types:watch", - "build:cjs:watch": "tsc -p tsconfig.cjs.json --watch", - "build:dev:watch": "run-s build:watch", - "build:es5:watch": "yarn build:cjs:watch # *** backwards compatibility - remove in v7 ***", - "build:esm:watch": "tsc -p tsconfig.esm.json --watch", - "build:types:watch": "tsc -p tsconfig.types.json --watch", - "build:npm": "ts-node ../../scripts/prepack.ts && npm pack ./build", - "circularDepCheck": "madge --circular src/index.ts", - "clean": "rimraf build coverage", - "fix": "run-s fix:eslint fix:prettier", - "fix:eslint": "eslint . --format stylish --fix", - "fix:prettier": "prettier --write \"{src,test,scripts}/**/*.ts\"", - "link:yarn": "yarn link", - "lint": "run-s lint:prettier lint:eslint", - "lint:eslint": "eslint . --cache --cache-location '../../eslintcache/' --format stylish", - "lint:prettier": "prettier --check \"{src,test,scripts}/**/*.ts\"", - "test": "jest", - "test:watch": "jest --watch" - }, - "volta": { - "extends": "../../package.json" - }, - "sideEffects": false -} diff --git a/packages/minimal/test/mocks/client.ts b/packages/minimal/test/mocks/client.ts deleted file mode 100644 index 813fd694c38a..000000000000 --- a/packages/minimal/test/mocks/client.ts +++ /dev/null @@ -1,19 +0,0 @@ -import { getCurrentHub } from '@sentry/hub'; - -export class TestClient { - public static instance?: TestClient; - - public constructor(public options: Record) { - TestClient.instance = this; - } - - public mySecretPublicMethod(str: string): string { - return `secret: ${str}`; - } -} - -export class TestClient2 {} - -export function init(options: Record): void { - getCurrentHub().bindClient(new TestClient(options) as any); -} diff --git a/packages/minimal/tsconfig.cjs.json b/packages/minimal/tsconfig.cjs.json deleted file mode 100644 index c1edc81a9657..000000000000 --- a/packages/minimal/tsconfig.cjs.json +++ /dev/null @@ -1,8 +0,0 @@ -{ - "extends": "./tsconfig.json", - - "compilerOptions": { - "module": "commonjs", - "outDir": "build/cjs" - } -} diff --git a/packages/minimal/tsconfig.esm.json b/packages/minimal/tsconfig.esm.json deleted file mode 100644 index 0b86c52918cc..000000000000 --- a/packages/minimal/tsconfig.esm.json +++ /dev/null @@ -1,8 +0,0 @@ -{ - "extends": "./tsconfig.json", - - "compilerOptions": { - "module": "es6", - "outDir": "build/esm" - } -} diff --git a/packages/minimal/tsconfig.json b/packages/minimal/tsconfig.json deleted file mode 100644 index bf45a09f2d71..000000000000 --- a/packages/minimal/tsconfig.json +++ /dev/null @@ -1,9 +0,0 @@ -{ - "extends": "../../tsconfig.json", - - "include": ["src/**/*"], - - "compilerOptions": { - // package-specific options - } -} diff --git a/packages/minimal/tsconfig.test.json b/packages/minimal/tsconfig.test.json deleted file mode 100644 index 87f6afa06b86..000000000000 --- a/packages/minimal/tsconfig.test.json +++ /dev/null @@ -1,12 +0,0 @@ -{ - "extends": "./tsconfig.json", - - "include": ["test/**/*"], - - "compilerOptions": { - // should include all types from `./tsconfig.json` plus types for all test frameworks used - "types": ["node", "jest"] - - // other package-specific, test-specific options - } -} diff --git a/packages/minimal/tsconfig.types.json b/packages/minimal/tsconfig.types.json deleted file mode 100644 index 65455f66bd75..000000000000 --- a/packages/minimal/tsconfig.types.json +++ /dev/null @@ -1,10 +0,0 @@ -{ - "extends": "./tsconfig.json", - - "compilerOptions": { - "declaration": true, - "declarationMap": true, - "emitDeclarationOnly": true, - "outDir": "build/types" - } -} diff --git a/packages/nextjs/test/integration/package.json b/packages/nextjs/test/integration/package.json index 5523ee933d20..2d8588dbf1a3 100644 --- a/packages/nextjs/test/integration/package.json +++ b/packages/nextjs/test/integration/package.json @@ -28,7 +28,6 @@ "@sentry/core": "file:../../../core", "@sentry/hub": "file:../../../hub", "@sentry/integrations": "file:../../../integrations", - "@sentry/minimal": "file:../../../minimal", "@sentry/node": "file:../../../node", "@sentry/react": "file:../../../react", "@sentry/tracing": "file:../../../tracing", diff --git a/packages/node/test/handlers.test.ts b/packages/node/test/handlers.test.ts index a423a92aa3d0..57f6528139f9 100644 --- a/packages/node/test/handlers.test.ts +++ b/packages/node/test/handlers.test.ts @@ -374,24 +374,21 @@ describe('tracingHandler', () => { const tracesSampler = jest.fn(); const options = getDefaultNodeClientOptions({ tracesSampler }); const hub = new Hub(new NodeClient(options)); - // we need to mock both of these because the tracing handler relies on `@sentry/core` while the sampler relies on - // `@sentry/hub`, and mocking breaks the link between the two - jest.spyOn(sentryCore, 'getCurrentHub').mockReturnValue(hub); - jest.spyOn(sentryHub, 'getCurrentHub').mockReturnValue(hub); - - sentryTracingMiddleware(req, res, next); - - expect(tracesSampler).toHaveBeenCalledWith( - expect.objectContaining({ - request: { - headers, - method, - url: `http://${hostname}${path}?${queryString}`, - cookies: { favorite: 'zukes' }, - query_string: queryString, - }, - }), - ); + hub.run(() => { + sentryTracingMiddleware(req, res, next); + + expect(tracesSampler).toHaveBeenCalledWith( + expect.objectContaining({ + request: { + headers, + method, + url: `http://${hostname}${path}?${queryString}`, + cookies: { favorite: 'zukes' }, + query_string: queryString, + }, + }), + ); + }); }); it('puts its transaction on the scope', () => { @@ -773,13 +770,13 @@ describe('errorHandler()', () => { const hub = new Hub(client, scope); jest.spyOn(client, '_captureRequestSession'); - jest.spyOn(sentryCore, 'getCurrentHub').mockReturnValue(hub); - jest.spyOn(sentryHub, 'getCurrentHub').mockReturnValue(hub); - scope?.setRequestSession({ status: 'ok' }); - sentryErrorMiddleware({ name: 'error', message: 'this is an error' }, req, res, next); - const requestSession = scope?.getRequestSession(); - expect(requestSession).toEqual({ status: 'crashed' }); + hub.run(() => { + scope?.setRequestSession({ status: 'ok' }); + sentryErrorMiddleware({ name: 'error', message: 'this is an error' }, req, res, next); + const requestSession = scope?.getRequestSession(); + expect(requestSession).toEqual({ status: 'crashed' }); + }); }); it('when autoSessionTracking is enabled, should not set requestSession status on Crash when it occurs outside the bounds of a request', () => { diff --git a/packages/react/package.json b/packages/react/package.json index 2fea1d64d5be..0365a6968bc5 100644 --- a/packages/react/package.json +++ b/packages/react/package.json @@ -17,7 +17,6 @@ }, "dependencies": { "@sentry/browser": "7.0.0-alpha.1", - "@sentry/minimal": "7.0.0-alpha.1", "@sentry/types": "7.0.0-alpha.1", "@sentry/utils": "7.0.0-alpha.1", "hoist-non-react-statics": "^3.3.2", diff --git a/packages/react/src/redux.ts b/packages/react/src/redux.ts index e1d0f42c3046..eb67a0a05a7f 100644 --- a/packages/react/src/redux.ts +++ b/packages/react/src/redux.ts @@ -1,5 +1,5 @@ /* eslint-disable @typescript-eslint/no-explicit-any */ -import { configureScope } from '@sentry/minimal'; +import { configureScope } from '@sentry/browser'; import { Scope } from '@sentry/types'; interface Action { diff --git a/packages/react/test/redux.test.ts b/packages/react/test/redux.test.ts index 55729ddc860b..9c75bc944d91 100644 --- a/packages/react/test/redux.test.ts +++ b/packages/react/test/redux.test.ts @@ -1,4 +1,4 @@ -import * as Sentry from '@sentry/minimal'; +import * as Sentry from '@sentry/browser'; import { Scope } from '@sentry/types'; import * as Redux from 'redux'; @@ -7,7 +7,8 @@ import { createReduxEnhancer } from '../src/redux'; const mockAddBreadcrumb = jest.fn(); const mockSetContext = jest.fn(); -jest.mock('@sentry/minimal', () => ({ +jest.mock('@sentry/browser', () => ({ + ...jest.requireActual('@sentry/browser'), configureScope: (callback: (scope: any) => Partial) => callback({ addBreadcrumb: mockAddBreadcrumb, diff --git a/packages/serverless/package.json b/packages/serverless/package.json index d77177b66c1a..c7a83cfdaa20 100644 --- a/packages/serverless/package.json +++ b/packages/serverless/package.json @@ -16,7 +16,6 @@ "access": "public" }, "dependencies": { - "@sentry/minimal": "7.0.0-alpha.1", "@sentry/node": "7.0.0-alpha.1", "@sentry/tracing": "7.0.0-alpha.1", "@sentry/types": "7.0.0-alpha.1", diff --git a/packages/tracing/package.json b/packages/tracing/package.json index 09bf285f90e4..00a298e3937f 100644 --- a/packages/tracing/package.json +++ b/packages/tracing/package.json @@ -17,7 +17,6 @@ }, "dependencies": { "@sentry/hub": "7.0.0-alpha.1", - "@sentry/minimal": "7.0.0-alpha.1", "@sentry/types": "7.0.0-alpha.1", "@sentry/utils": "7.0.0-alpha.1", "tslib": "^1.9.3" diff --git a/packages/vue/package.json b/packages/vue/package.json index b6eefc1ab6f3..406f335f05de 100644 --- a/packages/vue/package.json +++ b/packages/vue/package.json @@ -18,7 +18,6 @@ "dependencies": { "@sentry/browser": "7.0.0-alpha.1", "@sentry/core": "7.0.0-alpha.1", - "@sentry/minimal": "7.0.0-alpha.1", "@sentry/types": "7.0.0-alpha.1", "@sentry/utils": "7.0.0-alpha.1", "tslib": "^1.9.3" diff --git a/rollup/plugins/bundlePlugins.js b/rollup/plugins/bundlePlugins.js index 17c8d5977044..c678152a7b63 100644 --- a/rollup/plugins/bundlePlugins.js +++ b/rollup/plugins/bundlePlugins.js @@ -109,7 +109,6 @@ export function makeTSPlugin(jsVersion) { '@sentry/browser': ['../browser/src'], '@sentry/core': ['../core/src'], '@sentry/hub': ['../hub/src'], - '@sentry/minimal': ['../minimal/src'], '@sentry/types': ['../types/src'], '@sentry/utils': ['../utils/src'], }, diff --git a/yarn.lock b/yarn.lock index 2d280ea21e26..1bf0c3d2554b 100644 --- a/yarn.lock +++ b/yarn.lock @@ -8527,11 +8527,21 @@ caniuse-api@^3.0.0: lodash.memoize "^4.1.2" lodash.uniq "^4.5.0" -caniuse-lite@^1.0.0, caniuse-lite@^1.0.30000844, caniuse-lite@^1.0.30001032, caniuse-lite@^1.0.30001109, caniuse-lite@^1.0.30001173, caniuse-lite@^1.0.30001179, caniuse-lite@^1.0.30001254, caniuse-lite@^1.0.30001274, caniuse-lite@^1.0.30001280, caniuse-lite@^1.0.30001317: +caniuse-lite@^1.0.0, caniuse-lite@^1.0.30000844, caniuse-lite@^1.0.30001173, caniuse-lite@^1.0.30001179, caniuse-lite@^1.0.30001254: + version "1.0.30001257" + resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001257.tgz#150aaf649a48bee531104cfeda57f92ce587f6e5" + integrity sha512-JN49KplOgHSXpIsVSF+LUyhD8PUp6xPpAXeRrrcBh4KBeP7W864jHn6RvzJgDlrReyeVjMFJL3PLpPvKIxlIHA== + +caniuse-lite@^1.0.30001032, caniuse-lite@^1.0.30001109, caniuse-lite@^1.0.30001280, caniuse-lite@^1.0.30001317: version "1.0.30001332" resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001332.tgz#39476d3aa8d83ea76359c70302eafdd4a1d727dd" integrity sha512-10T30NYOEQtN6C11YGg411yebhvpnC6Z102+B95eAsN0oB6KUs01ivE8u+G6FMIRtIrVlYXhL+LUwQ3/hXwDWw== +caniuse-lite@^1.0.30001274: + version "1.0.30001279" + resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001279.tgz#eb06818da481ef5096a3b3760f43e5382ed6b0ce" + integrity sha512-VfEHpzHEXj6/CxggTwSFoZBBYGQfQv9Cf42KPlO79sWXCD1QNKWKsKzFeWL7QpZHJQYAvocqV6Rty1yJMkqWLQ== + canonical-path@1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/canonical-path/-/canonical-path-1.0.0.tgz#fcb470c23958def85081856be7a86e904f180d1d" From 5037d76f2a461e312579b4b04274b0ffafb8573a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kamil=20Og=C3=B3rek?= Date: Tue, 26 Apr 2022 19:30:02 +0200 Subject: [PATCH 078/204] fix(nextjs): Fix missing types build in `install-sentry-from-branch` (#4985) Add missing `yarn build:types` command --- packages/nextjs/vercel/install-sentry-from-branch.sh | 2 ++ 1 file changed, 2 insertions(+) diff --git a/packages/nextjs/vercel/install-sentry-from-branch.sh b/packages/nextjs/vercel/install-sentry-from-branch.sh index 704601894308..1bb94b0cc833 100644 --- a/packages/nextjs/vercel/install-sentry-from-branch.sh +++ b/packages/nextjs/vercel/install-sentry-from-branch.sh @@ -27,6 +27,8 @@ yarn --prod false echo " " echo "BUILDING SDK" +# Types are required for any type of build to succeed +yarn build:types # We need to build es5 versions because `next.config.js` calls `require` on the SDK (to get `withSentryConfig`) and # therefore it looks for `cjs/index.js` yarn build:cjs From 299aeb378f762e1b891667838dff35990a7c32ae Mon Sep 17 00:00:00 2001 From: Abhijeet Prasad Date: Tue, 26 Apr 2022 13:48:39 -0400 Subject: [PATCH 079/204] ref: Delete store endpoint code (#4969) --- packages/core/src/api.ts | 47 +---- packages/core/src/baseclient.ts | 4 +- packages/core/src/envelope.ts | 123 +++++++++++++ packages/core/src/index.ts | 9 +- packages/core/src/request.ts | 236 ------------------------- packages/core/test/lib/api.test.ts | 33 +--- packages/core/test/lib/request.test.ts | 215 ---------------------- 7 files changed, 130 insertions(+), 537 deletions(-) create mode 100644 packages/core/src/envelope.ts delete mode 100644 packages/core/src/request.ts delete mode 100644 packages/core/test/lib/request.test.ts diff --git a/packages/core/src/api.ts b/packages/core/src/api.ts index 8d76cb135400..09dff0b8f291 100644 --- a/packages/core/src/api.ts +++ b/packages/core/src/api.ts @@ -35,8 +35,8 @@ function getBaseApiEndpoint(dsn: DsnComponents): string { } /** Returns the ingest API endpoint for target. */ -function _getIngestEndpoint(dsn: DsnComponents, target: 'store' | 'envelope'): string { - return `${getBaseApiEndpoint(dsn)}${dsn.projectId}/${target}/`; +function _getIngestEndpoint(dsn: DsnComponents): string { + return `${getBaseApiEndpoint(dsn)}${dsn.projectId}/envelope/`; } /** Returns a URL-encoded string with auth config suitable for a query string. */ @@ -49,54 +49,13 @@ function _encodedAuth(dsn: DsnComponents): string { }); } -/** Returns the store endpoint URL. */ -export function getStoreEndpoint(dsn: DsnComponents): string { - return _getIngestEndpoint(dsn, 'store'); -} - -/** - * Returns the store endpoint URL with auth in the query string. - * - * Sending auth as part of the query string and not as custom HTTP headers avoids CORS preflight requests. - */ -export function getStoreEndpointWithUrlEncodedAuth(dsn: DsnComponents): string { - return `${getStoreEndpoint(dsn)}?${_encodedAuth(dsn)}`; -} - -/** Returns the envelope endpoint URL. */ -function _getEnvelopeEndpoint(dsn: DsnComponents): string { - return _getIngestEndpoint(dsn, 'envelope'); -} - /** * Returns the envelope endpoint URL with auth in the query string. * * Sending auth as part of the query string and not as custom HTTP headers avoids CORS preflight requests. */ export function getEnvelopeEndpointWithUrlEncodedAuth(dsn: DsnComponents, tunnel?: string): string { - return tunnel ? tunnel : `${_getEnvelopeEndpoint(dsn)}?${_encodedAuth(dsn)}`; -} - -/** - * Returns an object that can be used in request headers. - * This is needed for node and the old /store endpoint in sentry - */ -export function getRequestHeaders( - dsn: DsnComponents, - clientName: string, - clientVersion: string, -): { [key: string]: string } { - // CHANGE THIS to use metadata but keep clientName and clientVersion compatible - const header = [`Sentry sentry_version=${SENTRY_API_VERSION}`]; - header.push(`sentry_client=${clientName}/${clientVersion}`); - header.push(`sentry_key=${dsn.publicKey}`); - if (dsn.pass) { - header.push(`sentry_secret=${dsn.pass}`); - } - return { - 'Content-Type': 'application/json', - 'X-Sentry-Auth': header.join(', '), - }; + return tunnel ? tunnel : `${_getIngestEndpoint(dsn)}?${_encodedAuth(dsn)}`; } /** Returns the url to the report dialog endpoint. */ diff --git a/packages/core/src/baseclient.ts b/packages/core/src/baseclient.ts index 833b40aaf9c7..1969f2d8ef0a 100644 --- a/packages/core/src/baseclient.ts +++ b/packages/core/src/baseclient.ts @@ -32,9 +32,9 @@ import { } from '@sentry/utils'; import { getEnvelopeEndpointWithUrlEncodedAuth } from './api'; +import { createEventEnvelope, createSessionEnvelope } from './envelope'; import { IS_DEBUG_BUILD } from './flags'; import { IntegrationIndex, setupIntegrations } from './integration'; -import { createEventEnvelope, createSessionEnvelope } from './request'; const ALREADY_SEEN_ERROR = "Not capturing exception because it's already been captured."; @@ -275,7 +275,7 @@ export abstract class BaseClient implements Client { */ public sendSession(session: Session | SessionAggregates): void { if (this._dsn) { - const [env] = createSessionEnvelope(session, this._dsn, this._options._metadata, this._options.tunnel); + const env = createSessionEnvelope(session, this._dsn, this._options._metadata, this._options.tunnel); this.sendEnvelope(env); } } diff --git a/packages/core/src/envelope.ts b/packages/core/src/envelope.ts new file mode 100644 index 000000000000..53572c844a3e --- /dev/null +++ b/packages/core/src/envelope.ts @@ -0,0 +1,123 @@ +import { + DsnComponents, + Event, + EventEnvelope, + EventItem, + SdkInfo, + SdkMetadata, + SentryRequestType, + Session, + SessionAggregates, + SessionEnvelope, + SessionItem, +} from '@sentry/types'; +import { createEnvelope, dsnToString } from '@sentry/utils'; + +/** Extract sdk info from from the API metadata */ +function getSdkMetadataForEnvelopeHeader(metadata?: SdkMetadata): SdkInfo | undefined { + if (!metadata || !metadata.sdk) { + return; + } + const { name, version } = metadata.sdk; + return { name, version }; +} + +/** + * Apply SdkInfo (name, version, packages, integrations) to the corresponding event key. + * Merge with existing data if any. + **/ +function enhanceEventWithSdkInfo(event: Event, sdkInfo?: SdkInfo): Event { + if (!sdkInfo) { + return event; + } + event.sdk = event.sdk || {}; + event.sdk.name = event.sdk.name || sdkInfo.name; + event.sdk.version = event.sdk.version || sdkInfo.version; + event.sdk.integrations = [...(event.sdk.integrations || []), ...(sdkInfo.integrations || [])]; + event.sdk.packages = [...(event.sdk.packages || []), ...(sdkInfo.packages || [])]; + return event; +} + +/** Creates an envelope from a Session */ +export function createSessionEnvelope( + session: Session | SessionAggregates, + dsn: DsnComponents, + metadata?: SdkMetadata, + tunnel?: string, +): SessionEnvelope { + const sdkInfo = getSdkMetadataForEnvelopeHeader(metadata); + const envelopeHeaders = { + sent_at: new Date().toISOString(), + ...(sdkInfo && { sdk: sdkInfo }), + ...(!!tunnel && { dsn: dsnToString(dsn) }), + }; + + // I know this is hacky but we don't want to add `sessions` to request type since it's never rate limited + const type = 'aggregates' in session ? ('sessions' as SentryRequestType) : 'session'; + + // TODO (v7) Have to cast type because envelope items do not accept a `SentryRequestType` + const envelopeItem = [{ type } as { type: 'session' | 'sessions' }, session] as SessionItem; + const envelope = createEnvelope(envelopeHeaders, [envelopeItem]); + + return envelope; +} + +/** + * Create an Envelope from an event. + */ +export function createEventEnvelope( + event: Event, + dsn: DsnComponents, + metadata?: SdkMetadata, + tunnel?: string, +): EventEnvelope { + const sdkInfo = getSdkMetadataForEnvelopeHeader(metadata); + const eventType = event.type || 'event'; + + const { transactionSampling } = event.sdkProcessingMetadata || {}; + const { method: samplingMethod, rate: sampleRate } = transactionSampling || {}; + + // TODO: Below is a temporary hack in order to debug a serialization error - see + // https://github.com/getsentry/sentry-javascript/issues/2809, + // https://github.com/getsentry/sentry-javascript/pull/4425, and + // https://github.com/getsentry/sentry-javascript/pull/4574. + // + // TL; DR: even though we normalize all events (which should prevent this), something is causing `JSON.stringify` to + // throw a circular reference error. + // + // When it's time to remove it: + // 1. Delete everything between here and where the request object `req` is created, EXCEPT the line deleting + // `sdkProcessingMetadata` + // 2. Restore the original version of the request body, which is commented out + // 3. Search for either of the PR URLs above and pull out the companion hacks in the browser playwright tests and the + // baseClient tests in this package + enhanceEventWithSdkInfo(event, metadata && metadata.sdk); + event.tags = event.tags || {}; + event.extra = event.extra || {}; + + // In theory, all events should be marked as having gone through normalization and so + // we should never set this tag/extra data + if (!(event.sdkProcessingMetadata && event.sdkProcessingMetadata.baseClientNormalized)) { + event.tags.skippedNormalization = true; + event.extra.normalizeDepth = event.sdkProcessingMetadata ? event.sdkProcessingMetadata.normalizeDepth : 'unset'; + } + + // prevent this data from being sent to sentry + // TODO: This is NOT part of the hack - DO NOT DELETE + delete event.sdkProcessingMetadata; + + const envelopeHeaders = { + event_id: event.event_id as string, + sent_at: new Date().toISOString(), + ...(sdkInfo && { sdk: sdkInfo }), + ...(!!tunnel && { dsn: dsnToString(dsn) }), + }; + const eventItem: EventItem = [ + { + type: eventType, + sample_rates: [{ id: samplingMethod, rate: sampleRate }], + }, + event, + ]; + return createEnvelope(envelopeHeaders, [eventItem]); +} diff --git a/packages/core/src/index.ts b/packages/core/src/index.ts index a5231620e7a3..00123055009c 100644 --- a/packages/core/src/index.ts +++ b/packages/core/src/index.ts @@ -23,15 +23,8 @@ export { Scope, Session, } from '@sentry/hub'; -export { - getEnvelopeEndpointWithUrlEncodedAuth, - getStoreEndpointWithUrlEncodedAuth, - getRequestHeaders, - initAPIDetails, - getReportDialogEndpoint, -} from './api'; +export { getEnvelopeEndpointWithUrlEncodedAuth, initAPIDetails, getReportDialogEndpoint } from './api'; export { BaseClient } from './baseclient'; -export { eventToSentryRequest, sessionToSentryRequest } from './request'; export { initAndBind } from './sdk'; export { createTransport } from './transports/base'; export { SDK_VERSION } from './version'; diff --git a/packages/core/src/request.ts b/packages/core/src/request.ts deleted file mode 100644 index 9e58710af719..000000000000 --- a/packages/core/src/request.ts +++ /dev/null @@ -1,236 +0,0 @@ -import { - DsnComponents, - Event, - EventEnvelope, - EventItem, - SdkInfo, - SdkMetadata, - SentryRequest, - SentryRequestType, - Session, - SessionAggregates, - SessionEnvelope, - SessionItem, -} from '@sentry/types'; -import { createEnvelope, dsnToString, normalize, serializeEnvelope } from '@sentry/utils'; - -import { APIDetails, getEnvelopeEndpointWithUrlEncodedAuth, getStoreEndpointWithUrlEncodedAuth } from './api'; - -/** Extract sdk info from from the API metadata */ -function getSdkMetadataForEnvelopeHeader(metadata?: SdkMetadata): SdkInfo | undefined { - if (!metadata || !metadata.sdk) { - return; - } - const { name, version } = metadata.sdk; - return { name, version }; -} - -/** - * Apply SdkInfo (name, version, packages, integrations) to the corresponding event key. - * Merge with existing data if any. - **/ -function enhanceEventWithSdkInfo(event: Event, sdkInfo?: SdkInfo): Event { - if (!sdkInfo) { - return event; - } - event.sdk = event.sdk || {}; - event.sdk.name = event.sdk.name || sdkInfo.name; - event.sdk.version = event.sdk.version || sdkInfo.version; - event.sdk.integrations = [...(event.sdk.integrations || []), ...(sdkInfo.integrations || [])]; - event.sdk.packages = [...(event.sdk.packages || []), ...(sdkInfo.packages || [])]; - return event; -} - -/** Creates an envelope from a Session */ -export function createSessionEnvelope( - session: Session | SessionAggregates, - dsn: DsnComponents, - metadata?: SdkMetadata, - tunnel?: string, -): [SessionEnvelope, SentryRequestType] { - const sdkInfo = getSdkMetadataForEnvelopeHeader(metadata); - const envelopeHeaders = { - sent_at: new Date().toISOString(), - ...(sdkInfo && { sdk: sdkInfo }), - ...(!!tunnel && { dsn: dsnToString(dsn) }), - }; - - // I know this is hacky but we don't want to add `sessions` to request type since it's never rate limited - const type = 'aggregates' in session ? ('sessions' as SentryRequestType) : 'session'; - - // TODO (v7) Have to cast type because envelope items do not accept a `SentryRequestType` - const envelopeItem = [{ type } as { type: 'session' | 'sessions' }, session] as SessionItem; - const envelope = createEnvelope(envelopeHeaders, [envelopeItem]); - - return [envelope, type]; -} - -/** Creates a SentryRequest from a Session. */ -export function sessionToSentryRequest(session: Session | SessionAggregates, api: APIDetails): SentryRequest { - const [envelope, type] = createSessionEnvelope(session, api.dsn, api.metadata, api.tunnel); - return { - body: serializeEnvelope(envelope), - type, - url: getEnvelopeEndpointWithUrlEncodedAuth(api.dsn, api.tunnel), - }; -} - -/** - * Create an Envelope from an event. Note that this is duplicated from below, - * but on purpose as this will be refactored in v7. - */ -export function createEventEnvelope( - event: Event, - dsn: DsnComponents, - metadata?: SdkMetadata, - tunnel?: string, -): EventEnvelope { - const sdkInfo = getSdkMetadataForEnvelopeHeader(metadata); - const eventType = event.type || 'event'; - - const { transactionSampling } = event.sdkProcessingMetadata || {}; - const { method: samplingMethod, rate: sampleRate } = transactionSampling || {}; - - // TODO: Below is a temporary hack in order to debug a serialization error - see - // https://github.com/getsentry/sentry-javascript/issues/2809, - // https://github.com/getsentry/sentry-javascript/pull/4425, and - // https://github.com/getsentry/sentry-javascript/pull/4574. - // - // TL; DR: even though we normalize all events (which should prevent this), something is causing `JSON.stringify` to - // throw a circular reference error. - // - // When it's time to remove it: - // 1. Delete everything between here and where the request object `req` is created, EXCEPT the line deleting - // `sdkProcessingMetadata` - // 2. Restore the original version of the request body, which is commented out - // 3. Search for either of the PR URLs above and pull out the companion hacks in the browser playwright tests and the - // baseClient tests in this package - enhanceEventWithSdkInfo(event, metadata && metadata.sdk); - event.tags = event.tags || {}; - event.extra = event.extra || {}; - - // In theory, all events should be marked as having gone through normalization and so - // we should never set this tag/extra data - if (!(event.sdkProcessingMetadata && event.sdkProcessingMetadata.baseClientNormalized)) { - event.tags.skippedNormalization = true; - event.extra.normalizeDepth = event.sdkProcessingMetadata ? event.sdkProcessingMetadata.normalizeDepth : 'unset'; - } - - // prevent this data from being sent to sentry - // TODO: This is NOT part of the hack - DO NOT DELETE - delete event.sdkProcessingMetadata; - - const envelopeHeaders = { - event_id: event.event_id as string, - sent_at: new Date().toISOString(), - ...(sdkInfo && { sdk: sdkInfo }), - ...(!!tunnel && { dsn: dsnToString(dsn) }), - }; - const eventItem: EventItem = [ - { - type: eventType, - sample_rates: [{ id: samplingMethod, rate: sampleRate }], - }, - event, - ]; - return createEnvelope(envelopeHeaders, [eventItem]); -} - -/** Creates a SentryRequest from an event. */ -export function eventToSentryRequest(event: Event, api: APIDetails): SentryRequest { - const sdkInfo = getSdkMetadataForEnvelopeHeader(api.metadata); - const eventType = event.type || 'event'; - const useEnvelope = eventType === 'transaction' || !!api.tunnel; - - const { transactionSampling } = event.sdkProcessingMetadata || {}; - const { method: samplingMethod, rate: sampleRate } = transactionSampling || {}; - - // TODO: Below is a temporary hack in order to debug a serialization error - see - // https://github.com/getsentry/sentry-javascript/issues/2809, - // https://github.com/getsentry/sentry-javascript/pull/4425, and - // https://github.com/getsentry/sentry-javascript/pull/4574. - // - // TL; DR: even though we normalize all events (which should prevent this), something is causing `JSON.stringify` to - // throw a circular reference error. - // - // When it's time to remove it: - // 1. Delete everything between here and where the request object `req` is created, EXCEPT the line deleting - // `sdkProcessingMetadata` - // 2. Restore the original version of the request body, which is commented out - // 3. Search for either of the PR URLs above and pull out the companion hacks in the browser playwright tests and the - // baseClient tests in this package - enhanceEventWithSdkInfo(event, api.metadata.sdk); - event.tags = event.tags || {}; - event.extra = event.extra || {}; - - // In theory, all events should be marked as having gone through normalization and so - // we should never set this tag/extra data - if (!(event.sdkProcessingMetadata && event.sdkProcessingMetadata.baseClientNormalized)) { - event.tags.skippedNormalization = true; - event.extra.normalizeDepth = event.sdkProcessingMetadata ? event.sdkProcessingMetadata.normalizeDepth : 'unset'; - } - - // prevent this data from being sent to sentry - // TODO: This is NOT part of the hack - DO NOT DELETE - delete event.sdkProcessingMetadata; - - let body; - try { - // 99.9% of events should get through just fine - no change in behavior for them - body = JSON.stringify(event); - } catch (err) { - // Record data about the error without replacing original event data, then force renormalization - event.tags.JSONStringifyError = true; - event.extra.JSONStringifyError = err; - try { - body = JSON.stringify(normalize(event)); - } catch (newErr) { - // At this point even renormalization hasn't worked, meaning something about the event data has gone very wrong. - // Time to cut our losses and record only the new error. With luck, even in the problematic cases we're trying to - // debug with this hack, we won't ever land here. - const innerErr = newErr as Error; - body = JSON.stringify({ - message: 'JSON.stringify error after renormalization', - // setting `extra: { innerErr }` here for some reason results in an empty object, so unpack manually - extra: { message: innerErr.message, stack: innerErr.stack }, - }); - } - } - - const req: SentryRequest = { - // this is the relevant line of code before the hack was added, to make it easy to undo said hack once we've solved - // the mystery - // body: JSON.stringify(sdkInfo ? enhanceEventWithSdkInfo(event, api.metadata.sdk) : event), - body, - type: eventType, - url: useEnvelope - ? getEnvelopeEndpointWithUrlEncodedAuth(api.dsn, api.tunnel) - : getStoreEndpointWithUrlEncodedAuth(api.dsn), - }; - - // https://develop.sentry.dev/sdk/envelopes/ - - // Since we don't need to manipulate envelopes nor store them, there is no - // exported concept of an Envelope with operations including serialization and - // deserialization. Instead, we only implement a minimal subset of the spec to - // serialize events inline here. - if (useEnvelope) { - const envelopeHeaders = { - event_id: event.event_id as string, - sent_at: new Date().toISOString(), - ...(sdkInfo && { sdk: sdkInfo }), - ...(!!api.tunnel && { dsn: dsnToString(api.dsn) }), - }; - const eventItem: EventItem = [ - { - type: eventType, - sample_rates: [{ id: samplingMethod, rate: sampleRate }], - }, - req.body, - ]; - const envelope = createEnvelope(envelopeHeaders, [eventItem]); - req.body = serializeEnvelope(envelope); - } - - return req; -} diff --git a/packages/core/test/lib/api.test.ts b/packages/core/test/lib/api.test.ts index af3a7bfb8ca3..d556d9ec7cce 100644 --- a/packages/core/test/lib/api.test.ts +++ b/packages/core/test/lib/api.test.ts @@ -1,32 +1,15 @@ /* eslint-disable deprecation/deprecation */ import { makeDsn } from '@sentry/utils'; -import { - getEnvelopeEndpointWithUrlEncodedAuth, - getReportDialogEndpoint, - getRequestHeaders, - getStoreEndpoint, - getStoreEndpointWithUrlEncodedAuth, - initAPIDetails, -} from '../../src/api'; +import { getEnvelopeEndpointWithUrlEncodedAuth, getReportDialogEndpoint, initAPIDetails } from '../../src/api'; const ingestDsn = 'https://abc@xxxx.ingest.sentry.io:1234/subpath/123'; const dsnPublic = 'https://abc@sentry.io:1234/subpath/123'; -const legacyDsn = 'https://abc:123@sentry.io:1234/subpath/123'; const tunnel = 'https://hello.com/world'; -const ingestDsnAPI = initAPIDetails(ingestDsn); const dsnPublicAPI = initAPIDetails(dsnPublic); describe('API', () => { - test('getStoreEndpoint', () => { - expect(getStoreEndpointWithUrlEncodedAuth(dsnPublicAPI.dsn)).toEqual( - 'https://sentry.io:1234/subpath/api/123/store/?sentry_key=abc&sentry_version=7', - ); - expect(getStoreEndpoint(dsnPublicAPI.dsn)).toEqual('https://sentry.io:1234/subpath/api/123/store/'); - expect(getStoreEndpoint(ingestDsnAPI.dsn)).toEqual('https://xxxx.ingest.sentry.io:1234/subpath/api/123/store/'); - }); - test('getEnvelopeEndpoint', () => { expect(getEnvelopeEndpointWithUrlEncodedAuth(dsnPublicAPI.dsn)).toEqual( 'https://sentry.io:1234/subpath/api/123/envelope/?sentry_key=abc&sentry_version=7', @@ -35,20 +18,6 @@ describe('API', () => { expect(getEnvelopeEndpointWithUrlEncodedAuth(dsnPublicAPIWithTunnel.dsn, tunnel)).toEqual(tunnel); }); - test('getRequestHeaders', () => { - expect(getRequestHeaders(makeDsn(dsnPublic), 'a', '1.0')).toMatchObject({ - 'Content-Type': 'application/json', - 'X-Sentry-Auth': expect.stringMatching(/^Sentry sentry_version=\d, sentry_client=a\/1\.0, sentry_key=abc$/), - }); - - expect(getRequestHeaders(makeDsn(legacyDsn), 'a', '1.0')).toMatchObject({ - 'Content-Type': 'application/json', - 'X-Sentry-Auth': expect.stringMatching( - /^Sentry sentry_version=\d, sentry_client=a\/1\.0, sentry_key=abc, sentry_secret=123$/, - ), - }); - }); - describe('getReportDialogEndpoint', () => { test.each([ [ diff --git a/packages/core/test/lib/request.test.ts b/packages/core/test/lib/request.test.ts deleted file mode 100644 index c740611a5996..000000000000 --- a/packages/core/test/lib/request.test.ts +++ /dev/null @@ -1,215 +0,0 @@ -import { Event, SentryRequest } from '@sentry/types'; - -import { initAPIDetails } from '../../src/api'; -import { eventToSentryRequest, sessionToSentryRequest } from '../../src/request'; - -const ingestDsn = 'https://dogsarebadatkeepingsecrets@squirrelchasers.ingest.sentry.io/12312012'; -const storeUrl = - 'https://squirrelchasers.ingest.sentry.io/api/12312012/store/?sentry_key=dogsarebadatkeepingsecrets&sentry_version=7'; -const envelopeUrl = - 'https://squirrelchasers.ingest.sentry.io/api/12312012/envelope/?sentry_key=dogsarebadatkeepingsecrets&sentry_version=7'; -const tunnel = 'https://hello.com/world'; - -const api = initAPIDetails(ingestDsn, { - sdk: { - integrations: ['AWSLambda'], - name: 'sentry.javascript.browser', - version: '12.31.12', - packages: [{ name: 'npm:@sentry/browser', version: '12.31.12' }], - }, -}); - -function parseEnvelopeRequest(request: SentryRequest): any { - const [envelopeHeaderString, itemHeaderString, eventString] = request.body.split('\n'); - - return { - envelopeHeader: JSON.parse(envelopeHeaderString), - itemHeader: JSON.parse(itemHeaderString), - event: JSON.parse(eventString), - }; -} - -describe('eventToSentryRequest', () => { - let event: Event; - - beforeEach(() => { - event = { - contexts: { trace: { trace_id: '1231201211212012', span_id: '12261980', op: 'pageload' } }, - environment: 'dogpark', - event_id: '0908201304152013', - release: 'off.leash.park', - spans: [], - transaction: '/dogs/are/great/', - type: 'transaction', - user: { id: '1121', username: 'CharlieDog', ip_address: '11.21.20.12' }, - sdkProcessingMetadata: {}, - }; - }); - - it('adds transaction sampling information to item header', () => { - event.sdkProcessingMetadata = { transactionSampling: { method: 'client_rate', rate: 0.1121 } }; - - const result = eventToSentryRequest(event, api); - const envelope = parseEnvelopeRequest(result); - - expect(envelope.itemHeader).toEqual( - expect.objectContaining({ - sample_rates: [{ id: 'client_rate', rate: 0.1121 }], - }), - ); - }); - - it('adds sdk info to envelope header', () => { - const result = eventToSentryRequest(event, api); - const envelope = parseEnvelopeRequest(result); - - expect(envelope.envelopeHeader).toEqual( - expect.objectContaining({ sdk: { name: 'sentry.javascript.browser', version: '12.31.12' } }), - ); - }); - - it('adds sdk info to event body', () => { - const result = eventToSentryRequest(event, api); - const envelope = parseEnvelopeRequest(result); - - expect(envelope.event).toEqual( - expect.objectContaining({ - sdk: { - integrations: ['AWSLambda'], - name: 'sentry.javascript.browser', - version: '12.31.12', - packages: [{ name: 'npm:@sentry/browser', version: '12.31.12' }], - }, - }), - ); - }); - - it('merges existing sdk info if one is present on the event body', () => { - event.sdk = { - integrations: ['Clojure'], - name: 'foo', - packages: [{ name: 'npm:@sentry/clj', version: '12.31.12' }], - version: '1337', - }; - - const result = eventToSentryRequest(event, api); - const envelope = parseEnvelopeRequest(result); - - expect(envelope.event).toEqual( - expect.objectContaining({ - sdk: { - integrations: ['Clojure', 'AWSLambda'], - name: 'foo', - packages: [ - { name: 'npm:@sentry/clj', version: '12.31.12' }, - { name: 'npm:@sentry/browser', version: '12.31.12' }, - ], - version: '1337', - }, - }), - ); - }); - - it('uses tunnel as the url if it is configured', () => { - const tunnelRequest = eventToSentryRequest(event, initAPIDetails(ingestDsn, {}, tunnel)); - expect(tunnelRequest.url).toEqual(tunnel); - - const defaultRequest = eventToSentryRequest(event, initAPIDetails(ingestDsn, {})); - expect(defaultRequest.url).toEqual(envelopeUrl); - }); - - it('adds dsn to envelope header if tunnel is configured', () => { - const result = eventToSentryRequest(event, initAPIDetails(ingestDsn, {}, tunnel)); - const envelope = parseEnvelopeRequest(result); - - expect(envelope.envelopeHeader).toEqual( - expect.objectContaining({ - dsn: ingestDsn, - }), - ); - }); - - it('adds default "event" item type to item header if tunnel is configured', () => { - delete event.type; - - const result = eventToSentryRequest(event, initAPIDetails(ingestDsn, {}, tunnel)); - const envelope = parseEnvelopeRequest(result); - - expect(envelope.itemHeader).toEqual( - expect.objectContaining({ - type: 'event', - }), - ); - }); - - it('removes processing metadata before serializing event', () => { - event.sdkProcessingMetadata = { dogs: 'are great!' }; - - const result = eventToSentryRequest(event, api); - const envelope = parseEnvelopeRequest(result); - - expect(envelope.event.processingMetadata).toBeUndefined(); - }); - - it("doesn't depend on optional event fields for success ", () => { - // all event fields are optional - const emptyEvent = {}; - - const result = eventToSentryRequest(emptyEvent, api); - expect(result).toEqual({ - // The body isn't empty because SDK info gets added in `eventToSentryRequest`. (The specifics of that SDK info are - // tested elsewhere.) - body: expect.any(String), - type: 'event', - url: storeUrl, - }); - }); -}); - -describe('sessionToSentryRequest', () => { - it('test envelope creation for aggregateSessions', () => { - const aggregatedSession = { - attrs: { release: '1.0.x', environment: 'prod' }, - aggregates: [{ started: '2021-04-08T12:18:00.000Z', exited: 2 }], - }; - const result = sessionToSentryRequest(aggregatedSession, api); - - const [envelopeHeaderString, itemHeaderString, sessionString] = result.body.split('\n'); - - expect(JSON.parse(envelopeHeaderString)).toEqual( - expect.objectContaining({ - sdk: { name: 'sentry.javascript.browser', version: '12.31.12' }, - }), - ); - expect(JSON.parse(itemHeaderString)).toEqual( - expect.objectContaining({ - type: 'sessions', - }), - ); - expect(JSON.parse(sessionString)).toEqual( - expect.objectContaining({ - attrs: { release: '1.0.x', environment: 'prod' }, - aggregates: [{ started: '2021-04-08T12:18:00.000Z', exited: 2 }], - }), - ); - }); - - it('uses tunnel as the url if it is configured', () => { - const tunnelRequest = sessionToSentryRequest({ aggregates: [] }, initAPIDetails(ingestDsn, {}, tunnel)); - expect(tunnelRequest.url).toEqual(tunnel); - - const defaultRequest = sessionToSentryRequest({ aggregates: [] }, initAPIDetails(ingestDsn, {})); - expect(defaultRequest.url).toEqual(envelopeUrl); - }); - - it('adds dsn to envelope header if tunnel is configured', () => { - const result = sessionToSentryRequest({ aggregates: [] }, initAPIDetails(ingestDsn, {}, tunnel)); - const envelope = parseEnvelopeRequest(result); - - expect(envelope.envelopeHeader).toEqual( - expect.objectContaining({ - dsn: ingestDsn, - }), - ); - }); -}); From a5c380821d06d7fdcfd0404ae93ab31877c9493f Mon Sep 17 00:00:00 2001 From: Katie Byers Date: Wed, 27 Apr 2022 00:28:54 -0700 Subject: [PATCH 080/204] fix(dev): Fix linting for top-level `scripts` folder (#4977) This is a follow up to https://github.com/getsentry/sentry-javascript/pull/4963, which aimed to fix linting for `ts-node` scripts. That PR worked for `packages/*/scripts/*.ts`, but turned out not to work for the top-level `scripts/*.ts`, because the glob for file matching in the eslint config override matched both sets of scripts, but the tsconfig being pointed at only worked for package-level scripts. This fixes that by pulling the package-level linting override into the package-level `.eslintrc.js`, thus allowing the top-level override to apply only to the top-level scripts folder. Also, in order not to have to muck with the main repo `tsconfig` in any future changes/fixes, both sets of scripts now point to a new, dev-specific tsconfig for their linting. Finally, it fixes linting errors in the top-level `build:types:watch` script which were brought to light by the newly-fixed linting. --- .eslintrc.js | 6 ++---- packages/gatsby/.eslintrc.js | 8 ++++++++ scripts/build-types-watch.ts | 5 ++++- tsconfig.dev.json | 6 ++++++ tsconfig.json | 4 ---- 5 files changed, 20 insertions(+), 9 deletions(-) create mode 100644 tsconfig.dev.json diff --git a/.eslintrc.js b/.eslintrc.js index 01d0da16cf14..9da9cd411f5b 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -34,11 +34,9 @@ module.exports = { }, }, { - files: ['**/scripts/**/*.ts'], + files: ['scripts/**/*.ts'], parserOptions: { - // since filepaths are relative to the working directory, we need to go back up to reach the repo root level - // tsconfig - project: ['../../tsconfig.json'], + project: ['tsconfig.dev.json'], }, }, { diff --git a/packages/gatsby/.eslintrc.js b/packages/gatsby/.eslintrc.js index 56cb2e62ad47..88a6a96293ee 100644 --- a/packages/gatsby/.eslintrc.js +++ b/packages/gatsby/.eslintrc.js @@ -8,5 +8,13 @@ module.exports = { }, // ignore these because they're not covered by a `tsconfig`, which makes eslint throw an error ignorePatterns: ['gatsby-browser.d.ts', 'gatsby-node.d.ts'], + overrides: [ + { + files: ['scripts/**/*.ts'], + parserOptions: { + project: ['../../tsconfig.dev.json'], + }, + }, + ], extends: ['../../.eslintrc.js'], }; diff --git a/scripts/build-types-watch.ts b/scripts/build-types-watch.ts index d8a4cedb32aa..8b29635479d0 100644 --- a/scripts/build-types-watch.ts +++ b/scripts/build-types-watch.ts @@ -1,3 +1,4 @@ +/* eslint-disable no-console */ /** * If `yarn build:types:watch` is run without types files previously having been created, the build will get stuck in an * errored state. This happens because lerna runs all of the packages' `yarn build:types:watch` statements in parallel, @@ -28,7 +29,9 @@ for (const pkg of packages) { continue; } - const packageJSON = JSON.parse(fs.readFileSync(path.resolve(packagePath, 'package.json'), 'utf-8')); + const packageJSON = JSON.parse(fs.readFileSync(path.resolve(packagePath, 'package.json'), 'utf-8')) as { + scripts: Record; + }; if ('build:types' in packageJSON.scripts && !fs.existsSync(path.resolve(packagePath, 'build/types'))) { console.warn( diff --git a/tsconfig.dev.json b/tsconfig.dev.json new file mode 100644 index 000000000000..49053ea1124c --- /dev/null +++ b/tsconfig.dev.json @@ -0,0 +1,6 @@ +// TODO This should eventually end up as the tsconfig for a dev-utils package +{ + "extends": "./tsconfig.json", + + "include": ["**/scripts/**/*.ts"], +} diff --git a/tsconfig.json b/tsconfig.json index 98848bfdcbe8..f2ffa0c4e07c 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -1,10 +1,6 @@ { "extends": "./packages/typescript/tsconfig.json", - // include scripts here because their TS config isn't package-specific, and they need to be included in a tsconfig - // file to be linted - "include": ["**/scripts/**/*.ts"], - "compilerOptions": { // TODO: turn these on once we switch to only generating types once, using `tsconfig.types.json` // "declaration": false, From 4176bcb4582bb2fc91ac954e1bee701be19f3b89 Mon Sep 17 00:00:00 2001 From: Vladan Paunovic Date: Wed, 27 Apr 2022 10:41:28 +0200 Subject: [PATCH 081/204] chore: set ignoreSentryErrors to true (#4994) --- packages/serverless/src/awslambda.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/serverless/src/awslambda.ts b/packages/serverless/src/awslambda.ts index bac3e1412c8b..302607e25142 100644 --- a/packages/serverless/src/awslambda.ts +++ b/packages/serverless/src/awslambda.ts @@ -226,7 +226,7 @@ export function wrapHandler( captureTimeoutWarning: true, timeoutWarningLimit: 500, captureAllSettledReasons: false, - ignoreSentryErrors: false, + ignoreSentryErrors: true, ...wrapOptions, }; let timeoutWarningTimer: NodeJS.Timeout; From 4ca2de23c549071087ea388cc8fcf26d7f7697b1 Mon Sep 17 00:00:00 2001 From: Katie Byers Date: Wed, 27 Apr 2022 06:54:29 -0700 Subject: [PATCH 082/204] ref(tests): Move top-level jest config to `jest` folder (#4978) This moves our base jest config into a `jest` folder, in anticipation of more jest config files in future PRs. It also sets up those files to be linted, by adding the folder to our dev tsconfig and to our main eslint config. --- .eslintrc.js | 2 +- jest.config.js => jest/jest.config.js | 0 packages/browser/jest.config.js | 2 +- packages/core/jest.config.js | 2 +- packages/gatsby/jest.config.js | 2 +- packages/hub/jest.config.js | 2 +- packages/integrations/jest.config.js | 2 +- packages/nextjs/jest.config.js | 2 +- packages/node-integration-tests/jest.config.js | 2 +- packages/node/jest.config.js | 2 +- packages/react/jest.config.js | 2 +- packages/serverless/jest.config.js | 2 +- packages/tracing/jest.config.js | 2 +- packages/utils/jest.config.js | 2 +- packages/vue/jest.config.js | 2 +- tsconfig.dev.json | 6 +++++- 16 files changed, 19 insertions(+), 15 deletions(-) rename jest.config.js => jest/jest.config.js (100%) diff --git a/.eslintrc.js b/.eslintrc.js index 9da9cd411f5b..eb8e6e8faca8 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -34,7 +34,7 @@ module.exports = { }, }, { - files: ['scripts/**/*.ts'], + files: ['jest/**/*.ts', 'scripts/**/*.ts'], parserOptions: { project: ['tsconfig.dev.json'], }, diff --git a/jest.config.js b/jest/jest.config.js similarity index 100% rename from jest.config.js rename to jest/jest.config.js diff --git a/packages/browser/jest.config.js b/packages/browser/jest.config.js index 06b833307dd8..f9cd8056a454 100644 --- a/packages/browser/jest.config.js +++ b/packages/browser/jest.config.js @@ -1,4 +1,4 @@ -const baseConfig = require('../../jest.config.js'); +const baseConfig = require('../../jest/jest.config.js'); module.exports = { ...baseConfig, diff --git a/packages/core/jest.config.js b/packages/core/jest.config.js index 58141f076dc4..24f49ab59a4c 100644 --- a/packages/core/jest.config.js +++ b/packages/core/jest.config.js @@ -1 +1 @@ -module.exports = require('../../jest.config.js'); +module.exports = require('../../jest/jest.config.js'); diff --git a/packages/gatsby/jest.config.js b/packages/gatsby/jest.config.js index 6ea22d92d139..cc7d8162cd59 100644 --- a/packages/gatsby/jest.config.js +++ b/packages/gatsby/jest.config.js @@ -1,4 +1,4 @@ -const baseConfig = require('../../jest.config.js'); +const baseConfig = require('../../jest/jest.config.js'); module.exports = { ...baseConfig, diff --git a/packages/hub/jest.config.js b/packages/hub/jest.config.js index 58141f076dc4..24f49ab59a4c 100644 --- a/packages/hub/jest.config.js +++ b/packages/hub/jest.config.js @@ -1 +1 @@ -module.exports = require('../../jest.config.js'); +module.exports = require('../../jest/jest.config.js'); diff --git a/packages/integrations/jest.config.js b/packages/integrations/jest.config.js index 58141f076dc4..24f49ab59a4c 100644 --- a/packages/integrations/jest.config.js +++ b/packages/integrations/jest.config.js @@ -1 +1 @@ -module.exports = require('../../jest.config.js'); +module.exports = require('../../jest/jest.config.js'); diff --git a/packages/nextjs/jest.config.js b/packages/nextjs/jest.config.js index 58141f076dc4..24f49ab59a4c 100644 --- a/packages/nextjs/jest.config.js +++ b/packages/nextjs/jest.config.js @@ -1 +1 @@ -module.exports = require('../../jest.config.js'); +module.exports = require('../../jest/jest.config.js'); diff --git a/packages/node-integration-tests/jest.config.js b/packages/node-integration-tests/jest.config.js index 81e7d8f63d18..fd4e06a64fb3 100644 --- a/packages/node-integration-tests/jest.config.js +++ b/packages/node-integration-tests/jest.config.js @@ -1,4 +1,4 @@ -const baseConfig = require('../../jest.config.js'); +const baseConfig = require('../../jest/jest.config.js'); module.exports = { globalSetup: '/setup-tests.ts', diff --git a/packages/node/jest.config.js b/packages/node/jest.config.js index 58141f076dc4..24f49ab59a4c 100644 --- a/packages/node/jest.config.js +++ b/packages/node/jest.config.js @@ -1 +1 @@ -module.exports = require('../../jest.config.js'); +module.exports = require('../../jest/jest.config.js'); diff --git a/packages/react/jest.config.js b/packages/react/jest.config.js index 8baa8e01db0c..cd02790794a7 100644 --- a/packages/react/jest.config.js +++ b/packages/react/jest.config.js @@ -1,4 +1,4 @@ -const baseConfig = require('../../jest.config.js'); +const baseConfig = require('../../jest/jest.config.js'); module.exports = { ...baseConfig, diff --git a/packages/serverless/jest.config.js b/packages/serverless/jest.config.js index 58141f076dc4..24f49ab59a4c 100644 --- a/packages/serverless/jest.config.js +++ b/packages/serverless/jest.config.js @@ -1 +1 @@ -module.exports = require('../../jest.config.js'); +module.exports = require('../../jest/jest.config.js'); diff --git a/packages/tracing/jest.config.js b/packages/tracing/jest.config.js index 58141f076dc4..24f49ab59a4c 100644 --- a/packages/tracing/jest.config.js +++ b/packages/tracing/jest.config.js @@ -1 +1 @@ -module.exports = require('../../jest.config.js'); +module.exports = require('../../jest/jest.config.js'); diff --git a/packages/utils/jest.config.js b/packages/utils/jest.config.js index 58141f076dc4..24f49ab59a4c 100644 --- a/packages/utils/jest.config.js +++ b/packages/utils/jest.config.js @@ -1 +1 @@ -module.exports = require('../../jest.config.js'); +module.exports = require('../../jest/jest.config.js'); diff --git a/packages/vue/jest.config.js b/packages/vue/jest.config.js index 8baa8e01db0c..cd02790794a7 100644 --- a/packages/vue/jest.config.js +++ b/packages/vue/jest.config.js @@ -1,4 +1,4 @@ -const baseConfig = require('../../jest.config.js'); +const baseConfig = require('../../jest/jest.config.js'); module.exports = { ...baseConfig, diff --git a/tsconfig.dev.json b/tsconfig.dev.json index 49053ea1124c..8448d6c7a046 100644 --- a/tsconfig.dev.json +++ b/tsconfig.dev.json @@ -2,5 +2,9 @@ { "extends": "./tsconfig.json", - "include": ["**/scripts/**/*.ts"], + "include": ["**/scripts/**/*.ts", "jest/**/*.ts"], + + "compilerOptions": { + "types": ["node", "jest"], + } } From 87cf8c52b15fa2e43555f0d38960ac4aaa2633f7 Mon Sep 17 00:00:00 2001 From: Katie Byers Date: Wed, 27 Apr 2022 08:00:15 -0700 Subject: [PATCH 083/204] fix(tests): Silence dummy output in tests (#4979) We have a number of tests which log messages to the console, for reasons other than communicating with the person running the tests. When trying to debug a large number of tests, across the whole repo, it's distracting to see this output (some of which actually even looks like failing tests). This silences that output, so it doesn't seem like tests are erroring when they're not. --- .../integrations/test/captureconsole.test.ts | 111 +++++++++++------- .../auto-instrument/mongodb/scenario.ts | 3 + packages/node/package.json | 2 +- packages/node/test/domain.test.ts | 5 +- .../manual/express-scope-separation/start.js | 3 + .../node/test/onunhandledrejection.test.ts | 4 + 6 files changed, 82 insertions(+), 46 deletions(-) diff --git a/packages/integrations/test/captureconsole.test.ts b/packages/integrations/test/captureconsole.test.ts index 966c76879522..f7979497ba6b 100644 --- a/packages/integrations/test/captureconsole.test.ts +++ b/packages/integrations/test/captureconsole.test.ts @@ -17,6 +17,15 @@ const mockHub = { captureException: jest.fn(), }; +const mockConsole = { + debug: jest.fn(), + log: jest.fn(), + warn: jest.fn(), + error: jest.fn(), + assert: jest.fn(), + info: jest.fn(), +}; + const getMockHubWithIntegration = (integration: Integration) => ({ ...mockHub, @@ -27,6 +36,11 @@ const getMockHubWithIntegration = (integration: Integration) => const originalConsole = Object.assign({}, global.console); describe('CaptureConsole setup', () => { + beforeEach(() => { + // this suppresses output to the terminal running the tests, but doesn't interfere with our wrapping + Object.assign(global.console, mockConsole); + }); + afterEach(() => { jest.clearAllMocks(); @@ -34,56 +48,67 @@ describe('CaptureConsole setup', () => { Object.assign(global.console, originalConsole); }); - it('should patch user-configured console levels', () => { - const captureConsoleIntegration = new CaptureConsole({ levels: ['log', 'warn'] }); - captureConsoleIntegration.setupOnce( - () => undefined, - () => getMockHubWithIntegration(captureConsoleIntegration), - ); + describe('monkeypatching', () => { + beforeEach(() => { + // for these tests only, we don't want to use the mock console, because we're testing for equality to methods from + // the original, so undo the global `beforeEach()` + Object.assign(global.console, originalConsole); + }); - expect(global.console.error).toBe(originalConsole.error); // not monkey patched - expect(global.console.log).not.toBe(originalConsole.log); // monkey patched - expect(global.console.warn).not.toBe(originalConsole.warn); // monkey patched - }); + it('should patch user-configured console levels', () => { + const captureConsoleIntegration = new CaptureConsole({ levels: ['log', 'warn'] }); + captureConsoleIntegration.setupOnce( + () => undefined, + () => getMockHubWithIntegration(captureConsoleIntegration), + ); - it('should fall back to default console levels if none are provided', () => { - const captureConsoleIntegration = new CaptureConsole(); - captureConsoleIntegration.setupOnce( - () => undefined, - () => getMockHubWithIntegration(captureConsoleIntegration), - ); + expect(global.console.error).toBe(originalConsole.error); // not monkey patched + expect(global.console.log).not.toBe(originalConsole.log); // monkey patched + expect(global.console.warn).not.toBe(originalConsole.warn); // monkey patched + }); - // expect a set of defined console levels to have been monkey patched - expect(global.console.debug).not.toBe(originalConsole.debug); - expect(global.console.info).not.toBe(originalConsole.info); - expect(global.console.warn).not.toBe(originalConsole.warn); - expect(global.console.error).not.toBe(originalConsole.error); - expect(global.console.log).not.toBe(originalConsole.log); - expect(global.console.assert).not.toBe(originalConsole.assert); + it('should fall back to default console levels if none are provided', () => { + const captureConsoleIntegration = new CaptureConsole(); + captureConsoleIntegration.setupOnce( + () => undefined, + () => getMockHubWithIntegration(captureConsoleIntegration), + ); - // any other fields should not have been patched - expect(global.console.trace).toBe(originalConsole.trace); - expect(global.console.table).toBe(originalConsole.table); - }); + // expect a set of defined console levels to have been monkey patched + expect(global.console.debug).not.toBe(originalConsole.debug); + expect(global.console.info).not.toBe(originalConsole.info); + expect(global.console.warn).not.toBe(originalConsole.warn); + expect(global.console.error).not.toBe(originalConsole.error); + expect(global.console.log).not.toBe(originalConsole.log); + expect(global.console.assert).not.toBe(originalConsole.assert); + + // any other fields should not have been patched + expect(global.console.trace).toBe(originalConsole.trace); + expect(global.console.table).toBe(originalConsole.table); + }); + + it('should not wrap any functions with an empty levels option', () => { + const captureConsoleIntegration = new CaptureConsole({ levels: [] }); + captureConsoleIntegration.setupOnce( + () => undefined, + () => getMockHubWithIntegration(captureConsoleIntegration), + ); - it('should not wrap any functions with an empty levels option', () => { - const captureConsoleIntegration = new CaptureConsole({ levels: [] }); - captureConsoleIntegration.setupOnce( - () => undefined, - () => getMockHubWithIntegration(captureConsoleIntegration), - ); + // expect the default set of console levels not to have been monkey patched + expect(global.console.debug).toBe(originalConsole.debug); + expect(global.console.info).toBe(originalConsole.info); + expect(global.console.warn).toBe(originalConsole.warn); + expect(global.console.error).toBe(originalConsole.error); + expect(global.console.log).toBe(originalConsole.log); + expect(global.console.assert).toBe(originalConsole.assert); - // expect the default set of console levels not to have been monkey patched - expect(global.console.debug).toBe(originalConsole.debug); - expect(global.console.info).toBe(originalConsole.info); - expect(global.console.warn).toBe(originalConsole.warn); - expect(global.console.error).toBe(originalConsole.error); - expect(global.console.log).toBe(originalConsole.log); - expect(global.console.assert).toBe(originalConsole.assert); + // suppress output from the logging we're about to do + global.console.log = global.console.info = jest.fn(); - // expect no message to be captured with console.log - global.console.log('some message'); - expect(mockHub.captureMessage).not.toHaveBeenCalled(); + // expect no message to be captured with console.log + global.console.log('some message'); + expect(mockHub.captureMessage).not.toHaveBeenCalled(); + }); }); it('setup should fail gracefully when console is not available', () => { diff --git a/packages/node-integration-tests/suites/tracing/auto-instrument/mongodb/scenario.ts b/packages/node-integration-tests/suites/tracing/auto-instrument/mongodb/scenario.ts index d9c8afd2e9df..870553f8b0b4 100644 --- a/packages/node-integration-tests/suites/tracing/auto-instrument/mongodb/scenario.ts +++ b/packages/node-integration-tests/suites/tracing/auto-instrument/mongodb/scenario.ts @@ -3,6 +3,9 @@ import '@sentry/tracing'; import * as Sentry from '@sentry/node'; import { MongoClient } from 'mongodb'; +// suppress logging of the mongo download +global.console.log = () => null; + Sentry.init({ dsn: 'https://public@dsn.ingest.sentry.io/1337', release: '1.0', diff --git a/packages/node/package.json b/packages/node/package.json index d0710af4fe4c..cbb1e4e2f7b0 100644 --- a/packages/node/package.json +++ b/packages/node/package.json @@ -60,7 +60,7 @@ "test:express": "node test/manual/express-scope-separation/start.js", "test:jest": "jest", "test:release-health": "node test/manual/release-health/runner.js", - "test:webpack": "cd test/manual/webpack-domain/ && yarn && node npm-build.js", + "test:webpack": "cd test/manual/webpack-domain/ && yarn --silent && node npm-build.js", "test:watch": "jest --watch" }, "volta": { diff --git a/packages/node/test/domain.test.ts b/packages/node/test/domain.test.ts index 2d70fe1275e2..fa6bb94db292 100644 --- a/packages/node/test/domain.test.ts +++ b/packages/node/test/domain.test.ts @@ -4,8 +4,9 @@ import * as domain from 'domain'; // We need this import here to patch domain on the global object import * as Sentry from '../src'; -// eslint-disable-next-line no-console -console.log(Sentry.SDK_NAME); +// TODO This is here because if we don't use the `Sentry` object, the 'concurrent domain hubs' test will fail. Is this a +// product of treeshaking? +Sentry.getCurrentHub(); describe('domains', () => { test('without domain', () => { diff --git a/packages/node/test/manual/express-scope-separation/start.js b/packages/node/test/manual/express-scope-separation/start.js index 689c9ef358e8..37733cf142a5 100644 --- a/packages/node/test/manual/express-scope-separation/start.js +++ b/packages/node/test/manual/express-scope-separation/start.js @@ -3,6 +3,9 @@ const express = require('express'); const app = express(); const Sentry = require('../../../build/cjs'); +// 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.error = () => null; + function assertTags(actual, expected) { if (JSON.stringify(actual) !== JSON.stringify(expected)) { console.error('FAILED: Scope contains incorrect tags'); diff --git a/packages/node/test/onunhandledrejection.test.ts b/packages/node/test/onunhandledrejection.test.ts index 8588c9edddd6..012e6b37643f 100644 --- a/packages/node/test/onunhandledrejection.test.ts +++ b/packages/node/test/onunhandledrejection.test.ts @@ -2,6 +2,10 @@ import { Hub } from '@sentry/hub'; import { OnUnhandledRejection } 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; + jest.mock('@sentry/hub', () => { // we just want to short-circuit it, so dont worry about types const original = jest.requireActual('@sentry/hub'); From 8145ce95c4f127a0afd487738fd6e139df2d154b Mon Sep 17 00:00:00 2001 From: Abhijeet Prasad Date: Wed, 27 Apr 2022 11:53:02 -0400 Subject: [PATCH 084/204] ref(nextjs): Update webpack-plugin and change how cli binary is detected (#4988) Switch to using eval to workaround `@vercel/nft` detecting the binary itself as the hard dependency. --- packages/gatsby/package.json | 2 +- packages/nextjs/package.json | 2 +- packages/nextjs/src/config/webpack.ts | 8 +++++++- yarn.lock | 19 ++++++++++--------- 4 files changed, 19 insertions(+), 12 deletions(-) diff --git a/packages/gatsby/package.json b/packages/gatsby/package.json index 23cb258fb512..e65aa2714b27 100644 --- a/packages/gatsby/package.json +++ b/packages/gatsby/package.json @@ -22,7 +22,7 @@ "dependencies": { "@sentry/react": "7.0.0-alpha.1", "@sentry/tracing": "7.0.0-alpha.1", - "@sentry/webpack-plugin": "1.18.8" + "@sentry/webpack-plugin": "1.18.9" }, "peerDependencies": { "gatsby": "^2.0.0 || ^3.0.0 || ^4.0.0", diff --git a/packages/nextjs/package.json b/packages/nextjs/package.json index c4ca89888d3a..dd232c23d374 100644 --- a/packages/nextjs/package.json +++ b/packages/nextjs/package.json @@ -24,7 +24,7 @@ "@sentry/react": "7.0.0-alpha.1", "@sentry/tracing": "7.0.0-alpha.1", "@sentry/utils": "7.0.0-alpha.1", - "@sentry/webpack-plugin": "1.18.8", + "@sentry/webpack-plugin": "1.18.9", "tslib": "^1.9.3" }, "devDependencies": { diff --git a/packages/nextjs/src/config/webpack.ts b/packages/nextjs/src/config/webpack.ts index d98d7ec3503c..568a23cf5d88 100644 --- a/packages/nextjs/src/config/webpack.ts +++ b/packages/nextjs/src/config/webpack.ts @@ -325,6 +325,12 @@ export function getWebpackPluginOptions( return { ...defaultPluginOptions, ...userPluginOptions }; } +/** + * NOTE: `eval` usage is a workaround for @vercel/nft detecting the binary itself as the hard dependency + * and effectively always including it in the bundle, which is not what we want. + * ref: https://github.com/getsentry/sentry-javascript/issues/3865 + * ref: https://github.com/vercel/nft/issues/203 + */ function ensureCLIBinaryExists(): boolean { - return fs.existsSync(path.join(require.resolve('@sentry/cli'), '../../sentry-cli')); + return eval("fs.existsSync(path.join(require.resolve('@sentry/cli'), '../../sentry-cli'))"); } diff --git a/yarn.lock b/yarn.lock index 1bf0c3d2554b..9304663e7767 100644 --- a/yarn.lock +++ b/yarn.lock @@ -4237,10 +4237,10 @@ semver "7.3.2" semver-intersect "1.4.0" -"@sentry/cli@^1.73.0": - version "1.73.0" - resolved "https://registry.yarnpkg.com/@sentry/cli/-/cli-1.73.0.tgz#0d0bce913e0060ae192741c6693c57e50078c886" - integrity sha512-n4YINqmoncGUkLEpd4WuW+oD+aoUyQPhRbSBSYkbCFxPPmopn1VExCB2Vvzwj7vjXYRRGkix6keBMS0LLs3A3Q== +"@sentry/cli@^1.74.4": + version "1.74.4" + resolved "https://registry.yarnpkg.com/@sentry/cli/-/cli-1.74.4.tgz#7df82f68045a155e1885bfcbb5d303e5259eb18e" + integrity sha512-BMfzYiedbModsNBJlKeBOLVYUtwSi99LJ8gxxE4Bp5N8hyjNIN0WVrozAVZ27mqzAuy6151Za3dpmOLO86YlGw== dependencies: https-proxy-agent "^5.0.0" mkdirp "^0.5.5" @@ -4248,13 +4248,14 @@ npmlog "^4.1.2" progress "^2.0.3" proxy-from-env "^1.1.0" + which "^2.0.2" -"@sentry/webpack-plugin@1.18.8": - version "1.18.8" - resolved "https://registry.yarnpkg.com/@sentry/webpack-plugin/-/webpack-plugin-1.18.8.tgz#247a73a0aa9e28099a736bbe89ca0d35cbac7636" - integrity sha512-PtKr0NL62b5L3kPFGjwSNbIUwwcW5E5G6bQxAYZGpkgL1MFPnS4ND0SAsySuX0byQJRFFium5A19LpzyvQZSlQ== +"@sentry/webpack-plugin@1.18.9": + version "1.18.9" + resolved "https://registry.yarnpkg.com/@sentry/webpack-plugin/-/webpack-plugin-1.18.9.tgz#acb48c0f96fdb9e73f1e1db374ea31ded6d883a8" + integrity sha512-+TrenJrgFM0QTOwBnw0ZXWMvc0PiOebp6GN5EbGEx3JPCQqXOfXFzCaEjBtASKRgcNCL7zGly41S25YR6Hm+jw== dependencies: - "@sentry/cli" "^1.73.0" + "@sentry/cli" "^1.74.4" "@simple-dom/interface@^1.4.0": version "1.4.0" From a71cfb9cafb4ea33a6dfb5ccd82831a46c706241 Mon Sep 17 00:00:00 2001 From: Katie Byers Date: Wed, 27 Apr 2022 11:12:40 -0700 Subject: [PATCH 085/204] fix(dev): Remove eslint cache when cleaning repo (#4980) This adds the eslint cache to the list of things which are removed when running the repo-level `yarn clean` command, to prevent false positives or negatives because of cached results. --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index ab2d3e224bfc..a9990a79561b 100644 --- a/package.json +++ b/package.json @@ -14,7 +14,7 @@ "build:types:watch": "ts-node scripts/build-types-watch.ts", "build:npm": "lerna run --parallel build:npm", "circularDepCheck": "lerna run --parallel circularDepCheck", - "clean": "lerna run --parallel clean && lerna clean --yes", + "clean": "lerna run --parallel clean && lerna clean --yes && yarn rimraf eslintcache", "codecov": "codecov", "fix": "lerna run --parallel fix", "link:yarn": "lerna run --stream --concurrency 1 link:yarn", From e86fa94f171bc4d1fc3a475b99303e208654838e Mon Sep 17 00:00:00 2001 From: Abhijeet Prasad Date: Wed, 27 Apr 2022 14:21:55 -0400 Subject: [PATCH 086/204] ref(core): Delete API Details (#4999) --- MIGRATION.md | 10 +++++----- packages/core/src/api.ts | 26 +------------------------- packages/core/src/index.ts | 3 +-- packages/core/test/lib/api.test.ts | 19 ++++--------------- 4 files changed, 11 insertions(+), 47 deletions(-) diff --git a/MIGRATION.md b/MIGRATION.md index 0a1141cf5cac..e7f2ecd7b028 100644 --- a/MIGRATION.md +++ b/MIGRATION.md @@ -78,7 +78,7 @@ However, directly importing from specific files is discouraged. ## Removing the `API` class from `@sentry/core` -The internal `API` class was removed in favor of the `initAPIDetails` function and the `APIDetails` type. More details can be found in the [PR that deprecated this class](https://github.com/getsentry/sentry-javascript/pull/4281). To migrate, see the following example. +The internal `API` class was removed in favor of using client options explicitly. ```js // New in v7: @@ -88,10 +88,10 @@ import { getStoreEndpointWithUrlEncodedAuth, } from '@sentry/core'; -const dsn = initAPIDetails(dsn, metadata, tunnel); -const dsn = api.dsn; -const storeEndpoint = getEnvelopeEndpointWithUrlEncodedAuth(api.dsn, api.tunnel); -const envelopeEndpoint = getStoreEndpointWithUrlEncodedAuth(api.dsn); +const client = getCurrentHub().getClient(); +const dsn = client.getDsn(); +const options = client.getOptions(); +const envelopeEndpoint = getEnvelopeEndpointWithUrlEncodedAuth(dsn, options.tunnel); // Before: import { API } from '@sentry/core'; diff --git a/packages/core/src/api.ts b/packages/core/src/api.ts index 09dff0b8f291..dced7a8b181e 100644 --- a/packages/core/src/api.ts +++ b/packages/core/src/api.ts @@ -1,32 +1,8 @@ -import { DsnComponents, DsnLike, SdkMetadata } from '@sentry/types'; +import { DsnComponents, DsnLike } from '@sentry/types'; import { dsnToString, makeDsn, urlEncode } from '@sentry/utils'; const SENTRY_API_VERSION = '7'; -/** - * Stores details about a Sentry SDK - */ -export interface APIDetails { - /** The DSN as passed to Sentry.init() */ - initDsn: DsnLike; - /** Metadata about the SDK (name, version, etc) for inclusion in envelope headers */ - metadata: SdkMetadata; - /** The internally used Dsn object. */ - readonly dsn: DsnComponents; - /** The envelope tunnel to use. */ - readonly tunnel?: string; -} - -/** Initializes API Details */ -export function initAPIDetails(dsn: DsnLike, metadata?: SdkMetadata, tunnel?: string): APIDetails { - return { - initDsn: dsn, - metadata: metadata || {}, - dsn: makeDsn(dsn), - tunnel, - } as APIDetails; -} - /** Returns the prefix to construct Sentry ingestion API endpoints. */ function getBaseApiEndpoint(dsn: DsnComponents): string { const protocol = dsn.protocol ? `${dsn.protocol}:` : ''; diff --git a/packages/core/src/index.ts b/packages/core/src/index.ts index 00123055009c..2c9eb53a9ef8 100644 --- a/packages/core/src/index.ts +++ b/packages/core/src/index.ts @@ -1,4 +1,3 @@ -export type { APIDetails } from './api'; export type { ClientClass } from './sdk'; export { @@ -23,7 +22,7 @@ export { Scope, Session, } from '@sentry/hub'; -export { getEnvelopeEndpointWithUrlEncodedAuth, initAPIDetails, getReportDialogEndpoint } from './api'; +export { getEnvelopeEndpointWithUrlEncodedAuth, getReportDialogEndpoint } from './api'; export { BaseClient } from './baseclient'; export { initAndBind } from './sdk'; export { createTransport } from './transports/base'; diff --git a/packages/core/test/lib/api.test.ts b/packages/core/test/lib/api.test.ts index d556d9ec7cce..0a5ba52196e7 100644 --- a/packages/core/test/lib/api.test.ts +++ b/packages/core/test/lib/api.test.ts @@ -1,21 +1,20 @@ /* eslint-disable deprecation/deprecation */ import { makeDsn } from '@sentry/utils'; -import { getEnvelopeEndpointWithUrlEncodedAuth, getReportDialogEndpoint, initAPIDetails } from '../../src/api'; +import { getEnvelopeEndpointWithUrlEncodedAuth, getReportDialogEndpoint } from '../../src/api'; const ingestDsn = 'https://abc@xxxx.ingest.sentry.io:1234/subpath/123'; const dsnPublic = 'https://abc@sentry.io:1234/subpath/123'; const tunnel = 'https://hello.com/world'; -const dsnPublicAPI = initAPIDetails(dsnPublic); +const dsnPublicComponents = makeDsn(dsnPublic); describe('API', () => { test('getEnvelopeEndpoint', () => { - expect(getEnvelopeEndpointWithUrlEncodedAuth(dsnPublicAPI.dsn)).toEqual( + expect(getEnvelopeEndpointWithUrlEncodedAuth(dsnPublicComponents)).toEqual( 'https://sentry.io:1234/subpath/api/123/envelope/?sentry_key=abc&sentry_version=7', ); - const dsnPublicAPIWithTunnel = initAPIDetails(dsnPublic, {}, tunnel); - expect(getEnvelopeEndpointWithUrlEncodedAuth(dsnPublicAPIWithTunnel.dsn, tunnel)).toEqual(tunnel); + expect(getEnvelopeEndpointWithUrlEncodedAuth(dsnPublicComponents, tunnel)).toEqual(tunnel); }); describe('getReportDialogEndpoint', () => { @@ -97,14 +96,4 @@ describe('API', () => { }, ); }); - - test('initAPIDetails dsn', () => { - expect(dsnPublicAPI.dsn.host).toEqual(makeDsn(dsnPublic).host); - expect(dsnPublicAPI.dsn.path).toEqual(makeDsn(dsnPublic).path); - expect(dsnPublicAPI.dsn.pass).toEqual(makeDsn(dsnPublic).pass); - expect(dsnPublicAPI.dsn.port).toEqual(makeDsn(dsnPublic).port); - expect(dsnPublicAPI.dsn.protocol).toEqual(makeDsn(dsnPublic).protocol); - expect(dsnPublicAPI.dsn.projectId).toEqual(makeDsn(dsnPublic).projectId); - expect(dsnPublicAPI.dsn.publicKey).toEqual(makeDsn(dsnPublic).publicKey); - }); }); From 3a512bce8ac8c77c136fb4065948cca2d9b8d47a Mon Sep 17 00:00:00 2001 From: Katie Byers Date: Wed, 27 Apr 2022 11:53:53 -0700 Subject: [PATCH 087/204] feat(dev): Add VSCode debugger config for ts-node scripts (#4990) This adds a VSCode debug profile for the ts-node scripts (those in our `scripts/` folders and elsewhere), which will run whatever script is in the active editor when the debugger is invoked. --- .vscode/launch.json | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/.vscode/launch.json b/.vscode/launch.json index 2a1d7141b53c..207c63ceefce 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -18,6 +18,21 @@ } ], "configurations": [ + // Debug the ts-node script in the currently active window + { + "name": "Debug ts-node script (open file)", + "type": "pwa-node", + "cwd": "${workspaceFolder}/packages/${input:getPackageName}", + "request": "launch", + "runtimeExecutable": "yarn", + "runtimeArgs": ["ts-node", "-P", "${workspaceFolder}/tsconfig.dev.json", "${file}"], + "skipFiles": ["/**"], + "outFiles": ["${workspaceFolder}/**/*.js", "!**/node_modules/**"], + "sourceMaps": true, + "smartStep": true, + "internalConsoleOptions": "openOnSessionStart", + "outputCapture": "std" + }, // Run rollup using the config file which is in the currently active tab. { "name": "Debug rollup (config from open file)", From 78569520766d58355977da482ae9fb6de4ac85f8 Mon Sep 17 00:00:00 2001 From: Luca Forstner Date: Wed, 27 Apr 2022 21:11:43 +0200 Subject: [PATCH 088/204] ref: Add argument-less overload to `resolvedSyncPromise` (#5000) --- packages/utils/src/syncpromise.ts | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/packages/utils/src/syncpromise.ts b/packages/utils/src/syncpromise.ts index c824d1b09063..ec5cd9e9faee 100644 --- a/packages/utils/src/syncpromise.ts +++ b/packages/utils/src/syncpromise.ts @@ -14,13 +14,17 @@ const enum States { REJECTED = 2, } +// Overloads so we can call resolvedSyncPromise without arguments and generic argument +export function resolvedSyncPromise(): PromiseLike; +export function resolvedSyncPromise(value: T | PromiseLike): PromiseLike; + /** * Creates a resolved sync promise. * * @param value the value to resolve the promise with * @returns the resolved sync promise */ -export function resolvedSyncPromise(value: T | PromiseLike): PromiseLike { +export function resolvedSyncPromise(value?: T | PromiseLike): PromiseLike { return new SyncPromise(resolve => { resolve(value); }); From d619372d70bae8622e668ba530570cffae7e883d Mon Sep 17 00:00:00 2001 From: Katie Byers Date: Wed, 27 Apr 2022 12:58:42 -0700 Subject: [PATCH 089/204] fix(hub): Add missing parameter to `captureException` docstring (#5001) --- packages/hub/src/exports.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/hub/src/exports.ts b/packages/hub/src/exports.ts index 451b2b13e9ed..2ae3000df1c1 100644 --- a/packages/hub/src/exports.ts +++ b/packages/hub/src/exports.ts @@ -26,6 +26,7 @@ import { Scope } from './scope'; * Captures an exception event and sends it to Sentry. * * @param exception An exception-like object. + * @param captureContext Additional scope data to apply to exception event. * @returns The generated eventId. */ // eslint-disable-next-line @typescript-eslint/no-explicit-any, @typescript-eslint/explicit-module-boundary-types From c55003ff058da9a70cf5beaf78c449690669c00a Mon Sep 17 00:00:00 2001 From: Katie Byers Date: Wed, 27 Apr 2022 15:00:32 -0700 Subject: [PATCH 090/204] chore(build): Remove unnecessary `esModuleInterop` settings (#4991) This removes the `esModuleInterop` setting in the following places: - `@sentry/gatsby`, where it's only being used for `@sentry/tracing`, which is an ES module, making the injected helper a pass-through[1]. - `@sentry/vue` and `@sentry/wasm`, where it isn't being used at all (i.e., none of the helpers show up in the built code, meaning there's nothing in the source code that requires (no pun intended!) them). Doing this saves us having to port more config than needed to the new build process. [1] https://github.com/microsoft/tslib/blob/a7129c7bd500ce378ec19234247f3d0b1635e89d/tslib.js#L263 --- packages/gatsby/tsconfig.json | 1 - packages/vue/tsconfig.json | 1 - packages/wasm/tsconfig.json | 1 - 3 files changed, 3 deletions(-) diff --git a/packages/gatsby/tsconfig.json b/packages/gatsby/tsconfig.json index f074f990a911..b2c40b91a630 100644 --- a/packages/gatsby/tsconfig.json +++ b/packages/gatsby/tsconfig.json @@ -5,7 +5,6 @@ "compilerOptions": { // package-specific options - "esModuleInterop": true, "jsx": "react" } } diff --git a/packages/vue/tsconfig.json b/packages/vue/tsconfig.json index e5ec1017893d..bf45a09f2d71 100644 --- a/packages/vue/tsconfig.json +++ b/packages/vue/tsconfig.json @@ -5,6 +5,5 @@ "compilerOptions": { // package-specific options - "esModuleInterop": true, } } diff --git a/packages/wasm/tsconfig.json b/packages/wasm/tsconfig.json index e5ec1017893d..bf45a09f2d71 100644 --- a/packages/wasm/tsconfig.json +++ b/packages/wasm/tsconfig.json @@ -5,6 +5,5 @@ "compilerOptions": { // package-specific options - "esModuleInterop": true, } } From 69028bf68f9d44c218456d575ac68b84db5dec9e Mon Sep 17 00:00:00 2001 From: Luca Forstner Date: Thu, 28 Apr 2022 08:52:39 +0200 Subject: [PATCH 091/204] ref: Remove unneeded logic from transports (#5002) --- packages/browser/src/exports.ts | 2 - packages/browser/src/transports/fetch.ts | 17 ++-- packages/browser/src/transports/xhr.ts | 8 +- .../unit/helper/browser-client-options.ts | 2 +- .../test/unit/mocks/simpletransport.ts | 2 +- packages/browser/test/unit/sdk.test.ts | 2 +- .../test/unit/transports/fetch.test.ts | 4 +- .../browser/test/unit/transports/xhr.test.ts | 12 --- packages/core/src/transports/base.ts | 39 ++------- .../core/test/lib/transports/base.test.ts | 83 ++++++------------- packages/core/test/mocks/client.ts | 2 +- packages/core/test/mocks/transport.ts | 2 +- packages/node/src/index.ts | 2 - packages/node/src/transports/http.ts | 6 -- .../node/test/helper/node-client-options.ts | 2 +- .../caught-exception-errored-session.js | 18 ++-- .../errors-in-session-capped-to-one.js | 18 ++-- .../terminal-state-sessions-sent-once.js | 16 ++-- .../unhandled-rejection-crashed-session.js | 16 ++-- packages/node/test/transports/http.test.ts | 39 +++------ packages/node/test/transports/https.test.ts | 40 +++------ packages/tracing/src/index.bundle.ts | 2 - packages/tracing/test/testutils.ts | 2 +- packages/types/src/eventstatus.ts | 13 --- packages/types/src/index.ts | 3 - packages/types/src/response.ts | 11 --- packages/types/src/transport.ts | 12 +-- packages/utils/src/index.ts | 1 - packages/utils/src/status.ts | 26 ------ packages/vue/src/index.bundle.ts | 2 - 30 files changed, 100 insertions(+), 304 deletions(-) delete mode 100644 packages/types/src/eventstatus.ts delete mode 100644 packages/types/src/response.ts delete mode 100644 packages/utils/src/status.ts diff --git a/packages/browser/src/exports.ts b/packages/browser/src/exports.ts index 0777d8124579..4a1dc5837747 100644 --- a/packages/browser/src/exports.ts +++ b/packages/browser/src/exports.ts @@ -5,9 +5,7 @@ export type { SdkInfo, Event, EventHint, - EventStatus, Exception, - Response, // eslint-disable-next-line deprecation/deprecation Severity, SeverityLevel, diff --git a/packages/browser/src/transports/fetch.ts b/packages/browser/src/transports/fetch.ts index f1aa6709da80..f500f33b22d6 100644 --- a/packages/browser/src/transports/fetch.ts +++ b/packages/browser/src/transports/fetch.ts @@ -22,17 +22,12 @@ export function makeFetchTransport( ...options.requestOptions, }; - return nativeFetch(options.url, requestOptions).then(response => { - return response.text().then(body => ({ - body, - headers: { - 'x-sentry-rate-limits': response.headers.get('X-Sentry-Rate-Limits'), - 'retry-after': response.headers.get('Retry-After'), - }, - reason: response.statusText, - statusCode: response.status, - })); - }); + return nativeFetch(options.url, requestOptions).then(response => ({ + headers: { + 'x-sentry-rate-limits': response.headers.get('X-Sentry-Rate-Limits'), + 'retry-after': response.headers.get('Retry-After'), + }, + })); } return createTransport({ bufferSize: options.bufferSize }, makeRequest); diff --git a/packages/browser/src/transports/xhr.ts b/packages/browser/src/transports/xhr.ts index 4b36e348de73..a08d51cf5a9a 100644 --- a/packages/browser/src/transports/xhr.ts +++ b/packages/browser/src/transports/xhr.ts @@ -26,16 +26,12 @@ export function makeXHRTransport(options: XHRTransportOptions): Transport { xhr.onreadystatechange = (): void => { if (xhr.readyState === XHR_READYSTATE_DONE) { - const response = { - body: xhr.response, + resolve({ headers: { 'x-sentry-rate-limits': xhr.getResponseHeader('X-Sentry-Rate-Limits'), 'retry-after': xhr.getResponseHeader('Retry-After'), }, - reason: xhr.statusText, - statusCode: xhr.status, - }; - resolve(response); + }); } }; diff --git a/packages/browser/test/unit/helper/browser-client-options.ts b/packages/browser/test/unit/helper/browser-client-options.ts index 19d4a4cb8c05..eb4d78768b94 100644 --- a/packages/browser/test/unit/helper/browser-client-options.ts +++ b/packages/browser/test/unit/helper/browser-client-options.ts @@ -6,7 +6,7 @@ import { BrowserClientOptions } from '../../../src/client'; export function getDefaultBrowserClientOptions(options: Partial = {}): BrowserClientOptions { return { integrations: [], - transport: () => createTransport({}, _ => resolvedSyncPromise({ statusCode: 200 })), + transport: () => createTransport({}, _ => resolvedSyncPromise({})), stackParser: () => [], ...options, }; diff --git a/packages/browser/test/unit/mocks/simpletransport.ts b/packages/browser/test/unit/mocks/simpletransport.ts index 59a21aed4ac3..a9c2d5264ae6 100644 --- a/packages/browser/test/unit/mocks/simpletransport.ts +++ b/packages/browser/test/unit/mocks/simpletransport.ts @@ -2,5 +2,5 @@ import { createTransport } from '@sentry/core'; import { resolvedSyncPromise } from '@sentry/utils'; export function makeSimpleTransport() { - return createTransport({}, () => resolvedSyncPromise({ statusCode: 200 })); + return createTransport({}, () => resolvedSyncPromise({})); } diff --git a/packages/browser/test/unit/sdk.test.ts b/packages/browser/test/unit/sdk.test.ts index 85fa04b21612..b59d380e8e76 100644 --- a/packages/browser/test/unit/sdk.test.ts +++ b/packages/browser/test/unit/sdk.test.ts @@ -15,7 +15,7 @@ const PUBLIC_DSN = 'https://username@domain/123'; function getDefaultBrowserOptions(options: Partial = {}): BrowserOptions { return { integrations: [], - transport: () => createTransport({}, _ => resolvedSyncPromise({ statusCode: 200 })), + transport: () => createTransport({}, _ => resolvedSyncPromise({})), stackParser: () => [], ...options, }; diff --git a/packages/browser/test/unit/transports/fetch.test.ts b/packages/browser/test/unit/transports/fetch.test.ts index 64ea580c1845..29b7b241c8dd 100644 --- a/packages/browser/test/unit/transports/fetch.test.ts +++ b/packages/browser/test/unit/transports/fetch.test.ts @@ -34,11 +34,9 @@ describe('NewFetchTransport', () => { const transport = makeFetchTransport(DEFAULT_FETCH_TRANSPORT_OPTIONS, mockFetch); expect(mockFetch).toHaveBeenCalledTimes(0); - const res = await transport.send(ERROR_ENVELOPE); + await transport.send(ERROR_ENVELOPE); expect(mockFetch).toHaveBeenCalledTimes(1); - expect(res.status).toBe('success'); - expect(mockFetch).toHaveBeenLastCalledWith(DEFAULT_FETCH_TRANSPORT_OPTIONS.url, { body: serializeEnvelope(ERROR_ENVELOPE), method: 'POST', diff --git a/packages/browser/test/unit/transports/xhr.test.ts b/packages/browser/test/unit/transports/xhr.test.ts index 3ec634360177..ae144480de08 100644 --- a/packages/browser/test/unit/transports/xhr.test.ts +++ b/packages/browser/test/unit/transports/xhr.test.ts @@ -65,18 +65,6 @@ describe('NewXHRTransport', () => { expect(xhrMock.send).toHaveBeenCalledWith(serializeEnvelope(ERROR_ENVELOPE)); }); - it('returns the correct response', async () => { - const transport = makeXHRTransport(DEFAULT_XHR_TRANSPORT_OPTIONS); - - const [res] = await Promise.all([ - transport.send(ERROR_ENVELOPE), - (xhrMock as XMLHttpRequest).onreadystatechange!({} as Event), - ]); - - expect(res).toBeDefined(); - expect(res.status).toEqual('success'); - }); - it('sets rate limit response headers', async () => { const transport = makeXHRTransport(DEFAULT_XHR_TRANSPORT_OPTIONS); diff --git a/packages/core/src/transports/base.ts b/packages/core/src/transports/base.ts index 97389ea9a45a..69872908a997 100644 --- a/packages/core/src/transports/base.ts +++ b/packages/core/src/transports/base.ts @@ -3,19 +3,14 @@ import { InternalBaseTransportOptions, Transport, TransportCategory, - TransportRequest, TransportRequestExecutor, - TransportResponse, } from '@sentry/types'; import { - disabledUntil, - eventStatusFromHttpCode, getEnvelopeType, isRateLimited, makePromiseBuffer, PromiseBuffer, RateLimits, - rejectedSyncPromise, resolvedSyncPromise, serializeEnvelope, updateRateLimits, @@ -32,44 +27,26 @@ export const DEFAULT_TRANSPORT_BUFFER_SIZE = 30; export function createTransport( options: InternalBaseTransportOptions, makeRequest: TransportRequestExecutor, - buffer: PromiseBuffer = makePromiseBuffer(options.bufferSize || DEFAULT_TRANSPORT_BUFFER_SIZE), + buffer: PromiseBuffer = makePromiseBuffer(options.bufferSize || DEFAULT_TRANSPORT_BUFFER_SIZE), ): Transport { let rateLimits: RateLimits = {}; const flush = (timeout?: number): PromiseLike => buffer.drain(timeout); - function send(envelope: Envelope): PromiseLike { + function send(envelope: Envelope): PromiseLike { const envCategory = getEnvelopeType(envelope); const category = envCategory === 'event' ? 'error' : (envCategory as TransportCategory); - const request: TransportRequest = { - category, - body: serializeEnvelope(envelope), - }; // Don't add to buffer if transport is already rate-limited if (isRateLimited(rateLimits, category)) { - return rejectedSyncPromise({ - status: 'rate_limit', - reason: getRateLimitReason(rateLimits, category), - }); + return resolvedSyncPromise(); } - const requestTask = (): PromiseLike => - makeRequest(request).then(({ body, headers, reason, statusCode }): PromiseLike => { - const status = eventStatusFromHttpCode(statusCode); + const requestTask = (): PromiseLike => + makeRequest({ body: serializeEnvelope(envelope) }).then(({ headers }): void => { if (headers) { rateLimits = updateRateLimits(rateLimits, headers); } - if (status === 'success') { - return resolvedSyncPromise({ status, reason }); - } - return rejectedSyncPromise({ - status, - reason: - reason || - body || - (status === 'rate_limit' ? getRateLimitReason(rateLimits, category) : 'Unknown transport error'), - }); }); return buffer.add(requestTask); @@ -80,9 +57,3 @@ export function createTransport( flush, }; } - -function getRateLimitReason(rateLimits: RateLimits, category: TransportCategory): string { - return `Too many ${category} requests, backing off until: ${new Date( - disabledUntil(rateLimits, category), - ).toISOString()}`; -} diff --git a/packages/core/test/lib/transports/base.test.ts b/packages/core/test/lib/transports/base.test.ts index 8b4dc8965c02..39166a1a06c8 100644 --- a/packages/core/test/lib/transports/base.test.ts +++ b/packages/core/test/lib/transports/base.test.ts @@ -1,12 +1,8 @@ -import { EventEnvelope, EventItem, Transport, TransportMakeRequestResponse, TransportResponse } from '@sentry/types'; +import { EventEnvelope, EventItem, Transport, TransportMakeRequestResponse } from '@sentry/types'; import { createEnvelope, PromiseBuffer, resolvedSyncPromise, serializeEnvelope } from '@sentry/utils'; import { createTransport } from '../../../src/transports/base'; -const ERROR_TRANSPORT_CATEGORY = 'error'; - -const TRANSACTION_TRANSPORT_CATEGORY = 'transaction'; - const ERROR_ENVELOPE = createEnvelope({ event_id: 'aa3ff046696b4bc6b609ce6d28fde9e2', sent_at: '123' }, [ [{ type: 'event' }, { event_id: 'aa3ff046696b4bc6b609ce6d28fde9e2' }] as EventItem, ]); @@ -18,12 +14,12 @@ const TRANSACTION_ENVELOPE = createEnvelope( describe('createTransport', () => { it('flushes the buffer', async () => { - const mockBuffer: PromiseBuffer = { + const mockBuffer: PromiseBuffer = { $: [], add: jest.fn(), drain: jest.fn(), }; - const transport = createTransport({}, _ => resolvedSyncPromise({ statusCode: 200 }), mockBuffer); + const transport = createTransport({}, _ => resolvedSyncPromise({}), mockBuffer); /* eslint-disable @typescript-eslint/unbound-method */ expect(mockBuffer.drain).toHaveBeenCalledTimes(0); await transport.flush(1000); @@ -34,45 +30,29 @@ describe('createTransport', () => { describe('send', () => { it('constructs a request to send to Sentry', async () => { + expect.assertions(1); const transport = createTransport({}, req => { - expect(req.category).toEqual(ERROR_TRANSPORT_CATEGORY); expect(req.body).toEqual(serializeEnvelope(ERROR_ENVELOPE)); - return resolvedSyncPromise({ statusCode: 200, reason: 'OK' }); + return resolvedSyncPromise({}); }); - const res = await transport.send(ERROR_ENVELOPE); - expect(res.status).toBe('success'); - expect(res.reason).toBe('OK'); + await transport.send(ERROR_ENVELOPE); }); - it('returns an error if request failed', async () => { + it('does throw if request fails', async () => { + expect.assertions(2); + const transport = createTransport({}, req => { - expect(req.category).toEqual(ERROR_TRANSPORT_CATEGORY); expect(req.body).toEqual(serializeEnvelope(ERROR_ENVELOPE)); - return resolvedSyncPromise({ statusCode: 400, reason: 'Bad Request' }); + throw new Error(); }); - try { - await transport.send(ERROR_ENVELOPE); - } catch (res) { - expect(res.status).toBe('invalid'); - expect(res.reason).toBe('Bad Request'); - } - }); - it('returns a default reason if reason not provided and request failed', async () => { - const transport = createTransport({}, req => { - expect(req.category).toEqual(TRANSACTION_TRANSPORT_CATEGORY); - expect(req.body).toEqual(serializeEnvelope(TRANSACTION_ENVELOPE)); - return resolvedSyncPromise({ statusCode: 500 }); - }); - try { - await transport.send(TRANSACTION_ENVELOPE); - } catch (res) { - expect(res.status).toBe('failed'); - expect(res.reason).toBe('Unknown transport error'); - } + expect(() => { + void transport.send(ERROR_ENVELOPE); + }).toThrow(); }); - describe('Rate-limiting', () => { + // TODO(v7): Add tests back in and test by using client report logic + describe.skip('Rate-limiting', () => { function setRateLimitTimes(): { retryAfterSeconds: number; beforeLimit: number; @@ -123,7 +103,6 @@ describe('createTransport', () => { 'x-sentry-rate-limits': null, 'retry-after': `${retryAfterSeconds}`, }, - statusCode: 429, }); try { @@ -133,7 +112,7 @@ describe('createTransport', () => { expect(res.reason).toBe(`Too many error requests, backing off until: ${new Date(afterLimit).toISOString()}`); } - setTransportResponse({ statusCode: 200 }); + setTransportResponse({}); try { await transport.send(ERROR_ENVELOPE); @@ -142,8 +121,7 @@ describe('createTransport', () => { expect(res.reason).toBe(`Too many error requests, backing off until: ${new Date(afterLimit).toISOString()}`); } - const res = await transport.send(ERROR_ENVELOPE); - expect(res.status).toBe('success'); + await transport.send(ERROR_ENVELOPE); }); it('back-off using X-Sentry-Rate-Limits with single category', async () => { @@ -169,7 +147,6 @@ describe('createTransport', () => { 'x-sentry-rate-limits': `${retryAfterSeconds}:error:scope`, 'retry-after': null, }, - statusCode: 429, }); try { @@ -179,7 +156,7 @@ describe('createTransport', () => { expect(res.reason).toBe(`Too many error requests, backing off until: ${new Date(afterLimit).toISOString()}`); } - setTransportResponse({ statusCode: 200 }); + setTransportResponse({}); try { await transport.send(TRANSACTION_ENVELOPE); @@ -195,8 +172,7 @@ describe('createTransport', () => { expect(res.reason).toBe(`Too many error requests, backing off until: ${new Date(afterLimit).toISOString()}`); } - const res = await transport.send(TRANSACTION_ENVELOPE); - expect(res.status).toBe('success'); + await transport.send(TRANSACTION_ENVELOPE); }); it('back-off using X-Sentry-Rate-Limits with multiple categories', async () => { @@ -222,7 +198,6 @@ describe('createTransport', () => { 'x-sentry-rate-limits': `${retryAfterSeconds}:error;transaction:scope`, 'retry-after': null, }, - statusCode: 429, }); try { @@ -248,13 +223,10 @@ describe('createTransport', () => { ); } - setTransportResponse({ statusCode: 200 }); + setTransportResponse({}); - const eventRes = await transport.send(ERROR_ENVELOPE); - expect(eventRes.status).toBe('success'); - - const transactionRes = await transport.send(TRANSACTION_ENVELOPE); - expect(transactionRes.status).toBe('success'); + await transport.send(ERROR_ENVELOPE); + await transport.send(TRANSACTION_ENVELOPE); }); it('back-off using X-Sentry-Rate-Limits with missing categories should lock them all', async () => { @@ -284,7 +256,6 @@ describe('createTransport', () => { 'x-sentry-rate-limits': `${retryAfterSeconds}::scope`, 'retry-after': null, }, - statusCode: 429, }); try { @@ -310,13 +281,10 @@ describe('createTransport', () => { ); } - setTransportResponse({ statusCode: 200 }); - - const eventRes = await transport.send(ERROR_ENVELOPE); - expect(eventRes.status).toBe('success'); + setTransportResponse({}); - const transactionRes = await transport.send(TRANSACTION_ENVELOPE); - expect(transactionRes.status).toBe('success'); + await transport.send(ERROR_ENVELOPE); + await transport.send(TRANSACTION_ENVELOPE); }); it('back-off using X-Sentry-Rate-Limits should also trigger for 200 responses', async () => { @@ -340,7 +308,6 @@ describe('createTransport', () => { 'x-sentry-rate-limits': `${retryAfterSeconds}:error;transaction:scope`, 'retry-after': null, }, - statusCode: 200, }); try { diff --git a/packages/core/test/mocks/client.ts b/packages/core/test/mocks/client.ts index 076b1f7b34c2..737abb7be476 100644 --- a/packages/core/test/mocks/client.ts +++ b/packages/core/test/mocks/client.ts @@ -13,7 +13,7 @@ import { createTransport } from '../../src/transports/base'; export function getDefaultTestClientOptions(options: Partial = {}): TestClientOptions { return { integrations: [], - transport: () => createTransport({}, _ => resolvedSyncPromise({ statusCode: 200 })), + transport: () => createTransport({}, _ => resolvedSyncPromise({})), stackParser: () => [], ...options, }; diff --git a/packages/core/test/mocks/transport.ts b/packages/core/test/mocks/transport.ts index c7fdb02e558a..64b23e1cfd90 100644 --- a/packages/core/test/mocks/transport.ts +++ b/packages/core/test/mocks/transport.ts @@ -15,7 +15,7 @@ export function makeFakeTransport(delay: number = 2000) { return new SyncPromise(async res => { await sleep(delay); sentCount += 1; - res({ statusCode: 200 }); + res({}); }); }); return { makeTransport, getSendCalled: () => sendCalled, getSentCount: () => sentCount, delay }; diff --git a/packages/node/src/index.ts b/packages/node/src/index.ts index ec659b543bff..c84836251510 100644 --- a/packages/node/src/index.ts +++ b/packages/node/src/index.ts @@ -5,9 +5,7 @@ export type { SdkInfo, Event, EventHint, - EventStatus, Exception, - Response, // eslint-disable-next-line deprecation/deprecation Severity, SeverityLevel, diff --git a/packages/node/src/transports/http.ts b/packages/node/src/transports/http.ts index 0352a8232e04..1ba591c54379 100644 --- a/packages/node/src/transports/http.ts +++ b/packages/node/src/transports/http.ts @@ -6,7 +6,6 @@ import { TransportRequest, TransportRequestExecutor, } from '@sentry/types'; -import { eventStatusFromHttpCode } from '@sentry/utils'; import * as http from 'http'; import * as https from 'https'; import { URL } from 'url'; @@ -106,9 +105,6 @@ function createRequestExecutor( // Drain socket }); - const statusCode = res.statusCode ?? 500; - const status = eventStatusFromHttpCode(statusCode); - res.setEncoding('utf8'); // "Key-value pairs of header names and values. Header names are lower-cased." @@ -121,8 +117,6 @@ function createRequestExecutor( 'retry-after': retryAfterHeader, 'x-sentry-rate-limits': Array.isArray(rateLimitsHeader) ? rateLimitsHeader[0] : rateLimitsHeader, }, - reason: status, - statusCode: statusCode, }); }, ); diff --git a/packages/node/test/helper/node-client-options.ts b/packages/node/test/helper/node-client-options.ts index a010c18f5811..fc31b11738fd 100644 --- a/packages/node/test/helper/node-client-options.ts +++ b/packages/node/test/helper/node-client-options.ts @@ -6,7 +6,7 @@ import { NodeClientOptions } from '../../src/types'; export function getDefaultNodeClientOptions(options: Partial = {}): NodeClientOptions { return { integrations: [], - transport: () => createTransport({}, _ => resolvedSyncPromise({ statusCode: 200 })), + transport: () => createTransport({}, _ => resolvedSyncPromise({})), stackParser: () => [], ...options, }; diff --git a/packages/node/test/manual/release-health/single-session/caught-exception-errored-session.js b/packages/node/test/manual/release-health/single-session/caught-exception-errored-session.js index 8f5634163f25..686a3eb264ed 100644 --- a/packages/node/test/manual/release-health/single-session/caught-exception-errored-session.js +++ b/packages/node/test/manual/release-health/single-session/caught-exception-errored-session.js @@ -1,9 +1,5 @@ const Sentry = require('../../../../build/cjs'); -const { - assertSessions, - constructStrippedSessionObject, - validateSessionCountFunction, -} = require('../test-utils'); +const { assertSessions, constructStrippedSessionObject, validateSessionCountFunction } = require('../test-utils'); const sessionCounts = { sessionCounter: 0, @@ -14,12 +10,14 @@ validateSessionCountFunction(sessionCounts); function makeDummyTransport() { return Sentry.createTransport({}, req => { - if (req.category === 'session') { + const payload = req.body.split('\n').map(e => JSON.parse(e)); + const isSessionPayload = payload[1].type === 'session'; + + if (isSessionPayload) { sessionCounts.sessionCounter++; - const sessionEnv = req.body.split('\n').map(e => JSON.parse(e)); if (sessionCounts.sessionCounter === 1) { - assertSessions(constructStrippedSessionObject(sessionEnv[2]), { + assertSessions(constructStrippedSessionObject(payload[2]), { init: true, status: 'ok', errors: 1, @@ -28,7 +26,7 @@ function makeDummyTransport() { } if (sessionCounts.sessionCounter === 2) { - assertSessions(constructStrippedSessionObject(sessionEnv[2]), { + assertSessions(constructStrippedSessionObject(payload[2]), { init: false, status: 'exited', errors: 1, @@ -40,7 +38,7 @@ function makeDummyTransport() { return Promise.resolve({ statusCode: 200, }); - }) + }); } Sentry.init({ diff --git a/packages/node/test/manual/release-health/single-session/errors-in-session-capped-to-one.js b/packages/node/test/manual/release-health/single-session/errors-in-session-capped-to-one.js index 6d230292d180..d550614201b9 100644 --- a/packages/node/test/manual/release-health/single-session/errors-in-session-capped-to-one.js +++ b/packages/node/test/manual/release-health/single-session/errors-in-session-capped-to-one.js @@ -1,9 +1,5 @@ const Sentry = require('../../../../build/cjs'); -const { - assertSessions, - constructStrippedSessionObject, - validateSessionCountFunction, -} = require('../test-utils'); +const { assertSessions, constructStrippedSessionObject, validateSessionCountFunction } = require('../test-utils'); const sessionCounts = { sessionCounter: 0, @@ -14,12 +10,14 @@ validateSessionCountFunction(sessionCounts); function makeDummyTransport() { return Sentry.createTransport({}, req => { - if (req.category === 'session') { + const payload = req.body.split('\n').map(e => JSON.parse(e)); + const isSessionPayload = payload[1].type === 'session'; + + if (isSessionPayload) { sessionCounts.sessionCounter++; - const sessionEnv = req.body.split('\n').map(e => JSON.parse(e)); if (sessionCounts.sessionCounter === 1) { - assertSessions(constructStrippedSessionObject(sessionEnv[2]), { + assertSessions(constructStrippedSessionObject(payload[2]), { init: true, status: 'ok', errors: 1, @@ -28,7 +26,7 @@ function makeDummyTransport() { } if (sessionCounts.sessionCounter === 2) { - assertSessions(constructStrippedSessionObject(sessionEnv[2]), { + assertSessions(constructStrippedSessionObject(payload[2]), { init: false, status: 'exited', errors: 1, @@ -40,7 +38,7 @@ function makeDummyTransport() { return Promise.resolve({ statusCode: 200, }); - }) + }); } Sentry.init({ diff --git a/packages/node/test/manual/release-health/single-session/terminal-state-sessions-sent-once.js b/packages/node/test/manual/release-health/single-session/terminal-state-sessions-sent-once.js index d0b8f72f4af4..6eccd3e21dba 100644 --- a/packages/node/test/manual/release-health/single-session/terminal-state-sessions-sent-once.js +++ b/packages/node/test/manual/release-health/single-session/terminal-state-sessions-sent-once.js @@ -1,9 +1,5 @@ const Sentry = require('../../../../build/cjs'); -const { - assertSessions, - constructStrippedSessionObject, - validateSessionCountFunction, -} = require('../test-utils'); +const { assertSessions, constructStrippedSessionObject, validateSessionCountFunction } = require('../test-utils'); const sessionCounts = { sessionCounter: 0, @@ -14,11 +10,13 @@ validateSessionCountFunction(sessionCounts); function makeDummyTransport() { return Sentry.createTransport({}, req => { - if (req.category === 'session') { + const payload = req.body.split('\n').map(e => JSON.parse(e)); + const isSessionPayload = payload[1].type === 'session'; + + if (isSessionPayload) { sessionCounts.sessionCounter++; - const sessionEnv = req.body.split('\n').map(e => JSON.parse(e)); - assertSessions(constructStrippedSessionObject(sessionEnv[2]), { + assertSessions(constructStrippedSessionObject(payload[2]), { init: true, status: 'crashed', errors: 1, @@ -29,7 +27,7 @@ function makeDummyTransport() { return Promise.resolve({ statusCode: 200, }); - }) + }); } Sentry.init({ diff --git a/packages/node/test/manual/release-health/single-session/unhandled-rejection-crashed-session.js b/packages/node/test/manual/release-health/single-session/unhandled-rejection-crashed-session.js index d94b6add0c00..0024ee16f798 100644 --- a/packages/node/test/manual/release-health/single-session/unhandled-rejection-crashed-session.js +++ b/packages/node/test/manual/release-health/single-session/unhandled-rejection-crashed-session.js @@ -1,9 +1,5 @@ const Sentry = require('../../../../build/cjs'); -const { - assertSessions, - constructStrippedSessionObject, - validateSessionCountFunction, -} = require('../test-utils'); +const { assertSessions, constructStrippedSessionObject, validateSessionCountFunction } = require('../test-utils'); const sessionCounts = { sessionCounter: 0, @@ -14,11 +10,13 @@ validateSessionCountFunction(sessionCounts); function makeDummyTransport() { return Sentry.createTransport({}, req => { - if (req.category === 'session') { + const payload = req.body.split('\n').map(e => JSON.parse(e)); + const isSessionPayload = payload[1].type === 'session'; + + if (isSessionPayload) { sessionCounts.sessionCounter++; - const sessionEnv = req.body.split('\n').map(e => JSON.parse(e)); - assertSessions(constructStrippedSessionObject(sessionEnv[2]), { + assertSessions(constructStrippedSessionObject(payload[2]), { init: true, status: 'crashed', errors: 1, @@ -29,7 +27,7 @@ function makeDummyTransport() { return Promise.resolve({ statusCode: 200, }); - }) + }); } Sentry.init({ diff --git a/packages/node/test/transports/http.test.ts b/packages/node/test/transports/http.test.ts index d8e1f5889226..909532d5a3b8 100644 --- a/packages/node/test/transports/http.test.ts +++ b/packages/node/test/transports/http.test.ts @@ -78,15 +78,6 @@ describe('makeNewHttpTransport()', () => { }); describe('.send()', () => { - it('should correctly return successful server response', async () => { - await setupTestServer({ statusCode: SUCCESS }); - - const transport = makeNodeTransport({ url: TEST_SERVER_URL }); - const transportResponse = await transport.send(EVENT_ENVELOPE); - - expect(transportResponse).toEqual(expect.objectContaining({ status: 'success' })); - }); - it('should correctly send envelope to server', async () => { await setupTestServer({ statusCode: SUCCESS }, (req, body) => { expect(req.method).toBe('POST'); @@ -119,16 +110,16 @@ describe('makeNewHttpTransport()', () => { await transport.send(EVENT_ENVELOPE); }); - it.each([ - [RATE_LIMIT, 'rate_limit'], - [INVALID, 'invalid'], - [FAILED, 'failed'], - ])('should correctly reject bad server response (status %i)', async (serverStatusCode, expectedStatus) => { - await setupTestServer({ statusCode: serverStatusCode }); + it.each([RATE_LIMIT, INVALID, FAILED])( + 'should resolve on bad server response (status %i)', + async serverStatusCode => { + await setupTestServer({ statusCode: serverStatusCode }); - const transport = makeNodeTransport({ url: TEST_SERVER_URL }); - await expect(transport.send(EVENT_ENVELOPE)).rejects.toEqual(expect.objectContaining({ status: expectedStatus })); - }); + const transport = makeNodeTransport({ url: TEST_SERVER_URL }); + + await expect(transport.send(EVENT_ENVELOPE)).resolves.toBeUndefined(); + }, + ); it('should resolve when server responds with rate limit header and status code 200', async () => { await setupTestServer({ @@ -140,9 +131,7 @@ describe('makeNewHttpTransport()', () => { }); const transport = makeNodeTransport({ url: TEST_SERVER_URL }); - const transportResponse = await transport.send(EVENT_ENVELOPE); - - expect(transportResponse).toEqual(expect.objectContaining({ status: 'success' })); + await expect(transport.send(EVENT_ENVELOPE)).resolves.toBeUndefined(); }); it('should resolve when server responds with rate limit header and status code 200', async () => { @@ -155,9 +144,7 @@ describe('makeNewHttpTransport()', () => { }); const transport = makeNodeTransport({ url: TEST_SERVER_URL }); - const transportResponse = await transport.send(EVENT_ENVELOPE); - - expect(transportResponse).toEqual(expect.objectContaining({ status: 'success' })); + await transport.send(EVENT_ENVELOPE); }); }); @@ -259,7 +246,6 @@ describe('makeNewHttpTransport()', () => { 'retry-after': '2700', 'x-sentry-rate-limits': '60::organization, 2700::organization', }, - statusCode: RATE_LIMIT, }), ); }); @@ -283,7 +269,6 @@ describe('makeNewHttpTransport()', () => { 'retry-after': null, 'x-sentry-rate-limits': null, }, - statusCode: SUCCESS, }), ); }); @@ -311,7 +296,6 @@ describe('makeNewHttpTransport()', () => { 'retry-after': '2700', 'x-sentry-rate-limits': '60::organization, 2700::organization', }, - statusCode: SUCCESS, }), ); }); @@ -339,7 +323,6 @@ describe('makeNewHttpTransport()', () => { 'retry-after': '2700', 'x-sentry-rate-limits': '60::organization, 2700::organization', }, - statusCode: RATE_LIMIT, }), ); }); diff --git a/packages/node/test/transports/https.test.ts b/packages/node/test/transports/https.test.ts index 28c37b7c966b..dce56983680f 100644 --- a/packages/node/test/transports/https.test.ts +++ b/packages/node/test/transports/https.test.ts @@ -89,15 +89,6 @@ describe('makeNewHttpsTransport()', () => { }); describe('.send()', () => { - it('should correctly return successful server response', async () => { - await setupTestServer({ statusCode: SUCCESS }); - - const transport = makeNodeTransport({ httpModule: unsafeHttpsModule, url: TEST_SERVER_URL }); - const transportResponse = await transport.send(EVENT_ENVELOPE); - - expect(transportResponse).toEqual(expect.objectContaining({ status: 'success' })); - }); - it('should correctly send envelope to server', async () => { await setupTestServer({ statusCode: SUCCESS }, (req, body) => { expect(req.method).toBe('POST'); @@ -131,16 +122,17 @@ describe('makeNewHttpsTransport()', () => { await transport.send(EVENT_ENVELOPE); }); - it.each([ - [RATE_LIMIT, 'rate_limit'], - [INVALID, 'invalid'], - [FAILED, 'failed'], - ])('should correctly reject bad server response (status %i)', async (serverStatusCode, expectedStatus) => { - await setupTestServer({ statusCode: serverStatusCode }); + it.each([RATE_LIMIT, INVALID, FAILED])( + 'should resolve on bad server response (status %i)', + async serverStatusCode => { + await setupTestServer({ statusCode: serverStatusCode }); - const transport = makeNodeTransport({ httpModule: unsafeHttpsModule, url: TEST_SERVER_URL }); - await expect(transport.send(EVENT_ENVELOPE)).rejects.toEqual(expect.objectContaining({ status: expectedStatus })); - }); + const transport = makeNodeTransport({ httpModule: unsafeHttpsModule, url: TEST_SERVER_URL }); + expect(() => { + expect(transport.send(EVENT_ENVELOPE)); + }).not.toThrow(); + }, + ); it('should resolve when server responds with rate limit header and status code 200', async () => { await setupTestServer({ @@ -152,9 +144,7 @@ describe('makeNewHttpsTransport()', () => { }); const transport = makeNodeTransport({ httpModule: unsafeHttpsModule, url: TEST_SERVER_URL }); - const transportResponse = await transport.send(EVENT_ENVELOPE); - - expect(transportResponse).toEqual(expect.objectContaining({ status: 'success' })); + await expect(transport.send(EVENT_ENVELOPE)).resolves.toBeUndefined(); }); it('should resolve when server responds with rate limit header and status code 200', async () => { @@ -167,9 +157,7 @@ describe('makeNewHttpsTransport()', () => { }); const transport = makeNodeTransport({ httpModule: unsafeHttpsModule, url: TEST_SERVER_URL }); - const transportResponse = await transport.send(EVENT_ENVELOPE); - - expect(transportResponse).toEqual(expect.objectContaining({ status: 'success' })); + await expect(transport.send(EVENT_ENVELOPE)).resolves.toBeUndefined(); }); it('should use `caCerts` option', async () => { @@ -309,7 +297,6 @@ describe('makeNewHttpsTransport()', () => { 'retry-after': '2700', 'x-sentry-rate-limits': '60::organization, 2700::organization', }, - statusCode: RATE_LIMIT, }), ); }); @@ -333,7 +320,6 @@ describe('makeNewHttpsTransport()', () => { 'retry-after': null, 'x-sentry-rate-limits': null, }, - statusCode: SUCCESS, }), ); }); @@ -361,7 +347,6 @@ describe('makeNewHttpsTransport()', () => { 'retry-after': '2700', 'x-sentry-rate-limits': '60::organization, 2700::organization', }, - statusCode: SUCCESS, }), ); }); @@ -389,7 +374,6 @@ describe('makeNewHttpsTransport()', () => { 'retry-after': '2700', 'x-sentry-rate-limits': '60::organization, 2700::organization', }, - statusCode: RATE_LIMIT, }), ); }); diff --git a/packages/tracing/src/index.bundle.ts b/packages/tracing/src/index.bundle.ts index 56e35e0ceaaa..130270af8851 100644 --- a/packages/tracing/src/index.bundle.ts +++ b/packages/tracing/src/index.bundle.ts @@ -3,9 +3,7 @@ export type { Request, SdkInfo, Event, - EventStatus, Exception, - Response, // eslint-disable-next-line deprecation/deprecation Severity, SeverityLevel, diff --git a/packages/tracing/test/testutils.ts b/packages/tracing/test/testutils.ts index 2c0aef189b57..298d65f94ce3 100644 --- a/packages/tracing/test/testutils.ts +++ b/packages/tracing/test/testutils.ts @@ -62,7 +62,7 @@ export const testOnlyIfNodeVersionAtLeast = (minVersion: number): jest.It => { export function getDefaultBrowserClientOptions(options: Partial = {}): ClientOptions { return { integrations: [], - transport: () => createTransport({}, _ => resolvedSyncPromise({ statusCode: 200 })), + transport: () => createTransport({}, _ => resolvedSyncPromise({})), stackParser: () => [], ...options, }; diff --git a/packages/types/src/eventstatus.ts b/packages/types/src/eventstatus.ts deleted file mode 100644 index 498f0f45b3dd..000000000000 --- a/packages/types/src/eventstatus.ts +++ /dev/null @@ -1,13 +0,0 @@ -export type EventStatus = - /** The status could not be determined. */ - | 'unknown' - /** The event was skipped due to configuration or callbacks. */ - | 'skipped' - /** The event was sent to Sentry successfully. */ - | 'rate_limit' - /** The client is currently rate limited and will try again later. */ - | 'invalid' - /** The event could not be processed. */ - | 'failed' - /** A server-side error occurred during submission. */ - | 'success'; diff --git a/packages/types/src/index.ts b/packages/types/src/index.ts index ab2592cfe02e..107664a68ae0 100644 --- a/packages/types/src/index.ts +++ b/packages/types/src/index.ts @@ -19,7 +19,6 @@ export type { } from './envelope'; export type { ExtendedError } from './error'; export type { Event, EventHint } from './event'; -export type { EventStatus } from './eventstatus'; export type { EventProcessor } from './eventprocessor'; export type { Exception } from './exception'; export type { Extra, Extras } from './extra'; @@ -30,7 +29,6 @@ export type { ExtractedNodeRequestData, Primitive, WorkerLocation } from './misc export type { ClientOptions, Options } from './options'; export type { Package } from './package'; export type { QueryParams, Request, SentryRequest, SentryRequestType } from './request'; -export type { Response } from './response'; export type { Runtime } from './runtime'; export type { CaptureContext, Scope, ScopeContext } from './scope'; export type { SdkInfo } from './sdkinfo'; @@ -68,7 +66,6 @@ export type { TransportCategory, TransportRequest, TransportMakeRequestResponse, - TransportResponse, InternalBaseTransportOptions, BaseTransportOptions, TransportRequestExecutor, diff --git a/packages/types/src/response.ts b/packages/types/src/response.ts deleted file mode 100644 index 4add517f4870..000000000000 --- a/packages/types/src/response.ts +++ /dev/null @@ -1,11 +0,0 @@ -import { Event, EventType } from './event'; -import { EventStatus } from './eventstatus'; -import { Session } from './session'; - -/** JSDoc */ -export interface Response { - status: EventStatus; - event?: Event | Session; - type?: EventType; - reason?: string; -} diff --git a/packages/types/src/transport.ts b/packages/types/src/transport.ts index 1c2449a386b5..5fb204fdd546 100644 --- a/packages/types/src/transport.ts +++ b/packages/types/src/transport.ts @@ -1,5 +1,4 @@ import { Envelope } from './envelope'; -import { EventStatus } from './eventstatus'; export type Outcome = | 'before_send' @@ -13,23 +12,14 @@ export type TransportCategory = 'error' | 'transaction' | 'attachment' | 'sessio export type TransportRequest = { body: string; - category: TransportCategory; }; export type TransportMakeRequestResponse = { - body?: string; headers?: { [key: string]: string | null; 'x-sentry-rate-limits': string | null; 'retry-after': string | null; }; - reason?: string; - statusCode: number; -}; - -export type TransportResponse = { - status: EventStatus; - reason?: string; }; export interface InternalBaseTransportOptions { @@ -43,7 +33,7 @@ export interface BaseTransportOptions extends InternalBaseTransportOptions { } export interface Transport { - send(request: Envelope): PromiseLike; + send(request: Envelope): PromiseLike; flush(timeout?: number): PromiseLike; } diff --git a/packages/utils/src/index.ts b/packages/utils/src/index.ts index 0f004218c052..01b875dc31c3 100644 --- a/packages/utils/src/index.ts +++ b/packages/utils/src/index.ts @@ -14,7 +14,6 @@ export * from './path'; export * from './promisebuffer'; export * from './severity'; export * from './stacktrace'; -export * from './status'; export * from './string'; export * from './supports'; export * from './syncpromise'; diff --git a/packages/utils/src/status.ts b/packages/utils/src/status.ts deleted file mode 100644 index 381a0971b66f..000000000000 --- a/packages/utils/src/status.ts +++ /dev/null @@ -1,26 +0,0 @@ -import { EventStatus } from '@sentry/types'; -/** - * Converts an HTTP status code to sentry status {@link EventStatus}. - * - * @param code number HTTP status code - * @returns EventStatus - */ -export function eventStatusFromHttpCode(code: number): EventStatus { - if (code >= 200 && code < 300) { - return 'success'; - } - - if (code === 429) { - return 'rate_limit'; - } - - if (code >= 400 && code < 500) { - return 'invalid'; - } - - if (code >= 500) { - return 'failed'; - } - - return 'unknown'; -} diff --git a/packages/vue/src/index.bundle.ts b/packages/vue/src/index.bundle.ts index c2b744280372..f28600b793a6 100644 --- a/packages/vue/src/index.bundle.ts +++ b/packages/vue/src/index.bundle.ts @@ -3,9 +3,7 @@ export type { Request, SdkInfo, Event, - EventStatus, Exception, - Response, SeverityLevel, StackFrame, Stacktrace, From 9e46944fa22361b8e0595d7e9e180f5d06a3c659 Mon Sep 17 00:00:00 2001 From: Katie Byers Date: Thu, 28 Apr 2022 00:27:22 -0700 Subject: [PATCH 092/204] ref(build): Switch tsconfig `target` to es6 (#5005) This switches our default tsonfig `target` value from `es5` to `es6`. (Though we'll soon not be using `tsc` for transpilation, our tsconfig still matters for things like tests, because ts-jest uses it.) Note: Doing this required adding a temporary workaround to get node 8 tests to pass. Once we switch to sucrase builds (which don't have the same problem), it will come out. --- packages/typescript/tsconfig.json | 2 +- scripts/test.ts | 8 ++++++++ 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/packages/typescript/tsconfig.json b/packages/typescript/tsconfig.json index 49c2339c7f21..217159d3b73c 100644 --- a/packages/typescript/tsconfig.json +++ b/packages/typescript/tsconfig.json @@ -23,6 +23,6 @@ "sourceMap": true, "strict": true, "strictBindCallApply": false, - "target": "es5" + "target": "es6" } } diff --git a/scripts/test.ts b/scripts/test.ts index 02f440af1af9..ca33e54ee3d0 100644 --- a/scripts/test.ts +++ b/scripts/test.ts @@ -1,4 +1,5 @@ import { spawnSync } from 'child_process'; +import * as fs from 'fs'; import { join } from 'path'; function run(cmd: string, cwd: string = '') { @@ -37,6 +38,7 @@ if (nodeMajorVersion <= 10) { '@sentry/gatsby', '@sentry/serverless', '@sentry/nextjs', + '@sentry/angular', ]; // This is a hack, to deal the fact that the browser-based tests fail under Node 8, because of a conflict buried @@ -46,6 +48,12 @@ if (nodeMajorVersion <= 10) { // against a single version of node, but in the short run, this at least allows us to not be blocked by the // failures.) run('rm -rf packages/tracing/test/browser'); + + // TODO Pull this out once we switch to sucrase builds + // Recompile as es5, so as not to have to fix a compatibility problem that will soon be moot + const baseTSConfig = 'packages/typescript/tsconfig.json'; + fs.writeFileSync(baseTSConfig, String(fs.readFileSync(baseTSConfig)).replace('"target": "es6"', '"target": "es5"')); + run(`yarn build:dev ${ignorePackages.map(dep => `--ignore="${dep}"`).join(' ')}`); } // Node 10 else { From 7f04d333deed87bbd43042a86993e8db41b650f5 Mon Sep 17 00:00:00 2001 From: Katie Byers Date: Thu, 28 Apr 2022 00:55:27 -0700 Subject: [PATCH 093/204] feat(build): Add sucrase build foundations (#4992) This lays the initial groundwork for the new build process, by making the following additions: - `@rollup/plugin-sucrase`, which we will use to transpile TS, has been added as a dev dependency. - Functions have been added, similar to those which exist for CDN bundles, to generate the base rollup config and then turn it into a CJS version and an ESM version. - New `build:rollup` and `build:rollup:watch` commands have been added to each package's `package.json`. - A new `rollup.npm.config.js` file has been added to each package, generating a base config and modifying it as necessary given each package's specific needs. - A temporary set of CI jobs has been added, running tests against the sucrase build. Note that other than in tests, none of this code is yet being used - the actual switch from one build process to another will happen in a future PR. --- .github/workflows/build.yml | 305 +++++++++++++++++++++ package.json | 2 + packages/browser/package.json | 2 + packages/browser/rollup.npm.config.js | 8 + packages/core/package.json | 2 + packages/core/rollup.npm.config.js | 3 + packages/gatsby/package.json | 2 + packages/gatsby/rollup.npm.config.js | 3 + packages/hub/package.json | 2 + packages/hub/rollup.npm.config.js | 3 + packages/integrations/package.json | 2 + packages/integrations/rollup.npm.config.js | 8 + packages/nextjs/package.json | 2 + packages/nextjs/rollup.npm.config.js | 12 + packages/node/package.json | 2 + packages/node/rollup.npm.config.js | 3 + packages/react/package.json | 2 + packages/react/rollup.npm.config.js | 7 + packages/serverless/package.json | 2 + packages/serverless/rollup.npm.config.js | 7 + packages/tracing/package.json | 2 + packages/tracing/rollup.npm.config.js | 8 + packages/types/package.json | 2 + packages/types/rollup.npm.config.js | 3 + packages/utils/package.json | 2 + packages/utils/rollup.npm.config.js | 3 + packages/vue/package.json | 2 + packages/vue/rollup.npm.config.js | 3 + packages/wasm/package.json | 2 + packages/wasm/rollup.npm.config.js | 8 + rollup/index.js | 1 + rollup/npmHelpers.js | 90 ++++++ rollup/plugins/index.js | 1 + rollup/plugins/npmPlugins.js | 16 ++ yarn.lock | 63 ++++- 35 files changed, 584 insertions(+), 1 deletion(-) create mode 100644 packages/browser/rollup.npm.config.js create mode 100644 packages/core/rollup.npm.config.js create mode 100644 packages/gatsby/rollup.npm.config.js create mode 100644 packages/hub/rollup.npm.config.js create mode 100644 packages/integrations/rollup.npm.config.js create mode 100644 packages/nextjs/rollup.npm.config.js create mode 100644 packages/node/rollup.npm.config.js create mode 100644 packages/react/rollup.npm.config.js create mode 100644 packages/serverless/rollup.npm.config.js create mode 100644 packages/tracing/rollup.npm.config.js create mode 100644 packages/types/rollup.npm.config.js create mode 100644 packages/utils/rollup.npm.config.js create mode 100644 packages/vue/rollup.npm.config.js create mode 100644 packages/wasm/rollup.npm.config.js create mode 100644 rollup/npmHelpers.js create mode 100644 rollup/plugins/npmPlugins.js diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 155126752160..577de41be06d 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -105,6 +105,45 @@ jobs: # `job_build` can't see `job_install_deps` and what it returned) dependency_cache_key: ${{ needs.job_install_deps.outputs.dependency_cache_key }} + # This isn't a full `yarn build` using sucrase - it's just the cache from the normal build, with `build/cjs` and + # `build/esm` overwritten by sucrase. This way we don't need to worry about all of the other random stuff which + # packages build, because it will already be there. + job_build_with_sucrase: + name: Sucrase Build + needs: [job_install_deps, job_build] + runs-on: ubuntu-latest + timeout-minutes: 20 + steps: + - name: Check out current commit (${{ env.HEAD_COMMIT }}) + uses: actions/checkout@v2 + with: + ref: ${{ env.HEAD_COMMIT }} + - name: Set up Node + uses: actions/setup-node@v1 + - name: Check dependency cache + uses: actions/cache@v2 + with: + path: ${{ env.CACHED_DEPENDENCY_PATHS }} + key: ${{ needs.job_install_deps.outputs.dependency_cache_key }} + - name: Check tsc build cache + uses: actions/cache@v2 + with: + path: ${{ env.CACHED_BUILD_PATHS }} + key: ${{ env.BUILD_CACHE_KEY }} + - name: Check sucrase build cache + uses: actions/cache@v2 + id: cache_built_sucrase_packages + with: + path: ${{ env.CACHED_BUILD_PATHS }} + key: ${{ env.BUILD_CACHE_KEY }}-sucrase + - name: Build packages with sucrase + if: steps.cache_built_sucrase_packages.outputs.cache-hit == '' + run: 'yarn build:rollup' + outputs: + # this needs to be passed on, because the `needs` context only looks at direct ancestors (so steps which depend on + # `job_build` can't see `job_install_deps` and what it returned) + dependency_cache_key: ${{ needs.job_install_deps.outputs.dependency_cache_key }} + job_size_check: name: Size Check needs: job_build @@ -492,3 +531,269 @@ jobs: run: | cd packages/node-integration-tests yarn test + + job_unit_test_sucrase: + name: Sucrase Test (Node ${{ matrix.node }}) + needs: job_build_with_sucrase + continue-on-error: true + timeout-minutes: 30 + runs-on: ubuntu-latest + strategy: + matrix: + node: [8, 10, 12, 14, 16] + steps: + - name: Check out current commit (${{ env.HEAD_COMMIT }}) + uses: actions/checkout@v2 + with: + ref: ${{ env.HEAD_COMMIT }} + - name: Set up Node + uses: actions/setup-node@v1 + with: + node-version: ${{ matrix.node }} + - name: Check dependency cache + uses: actions/cache@v2 + with: + path: ${{ env.CACHED_DEPENDENCY_PATHS }} + key: ${{ needs.job_build_with_sucrase.outputs.dependency_cache_key }} + - name: Check build cache + uses: actions/cache@v2 + with: + path: ${{ env.CACHED_BUILD_PATHS }} + key: ${{ env.BUILD_CACHE_KEY }}-sucrase + - name: Run tests + env: + NODE_VERSION: ${{ matrix.node }} + run: | + [[ $NODE_VERSION == 8 ]] && yarn add --dev --ignore-engines --ignore-scripts --ignore-workspace-root-check ts-node@8.x + yarn test-ci + - name: Compute test coverage + uses: codecov/codecov-action@v1 + + job_nextjs_integration_test_sucrase: + name: Sucrase Test @sentry/nextjs on (Node ${{ matrix.node }}) + needs: job_build_with_sucrase + continue-on-error: true + timeout-minutes: 30 + runs-on: ubuntu-latest + strategy: + matrix: + node: [10, 12, 14, 16] + steps: + - name: Check out current commit (${{ env.HEAD_COMMIT }}) + uses: actions/checkout@v2 + with: + ref: ${{ env.HEAD_COMMIT }} + - name: Set up Node + uses: actions/setup-node@v1 + with: + node-version: ${{ matrix.node }} + - name: Check dependency cache + uses: actions/cache@v2 + with: + path: ${{ env.CACHED_DEPENDENCY_PATHS }} + key: ${{ needs.job_build_with_sucrase.outputs.dependency_cache_key }} + - name: Check build cache + uses: actions/cache@v2 + with: + path: ${{ env.CACHED_BUILD_PATHS }} + key: ${{ env.BUILD_CACHE_KEY }}-sucrase + - name: Run tests + env: + NODE_VERSION: ${{ matrix.node }} + run: | + cd packages/nextjs + yarn test:integration + + # Ember tests are separate from the rest because they are the slowest part of the test suite, and making them a + # separate job allows them to run in parallel with the other tests. + job_ember_tests_sucrase: + name: Sucrase Test @sentry/ember + needs: job_build_with_sucrase + continue-on-error: true + timeout-minutes: 30 + runs-on: ubuntu-latest + steps: + - name: Check out current commit (${{ env.HEAD_COMMIT }}) + uses: actions/checkout@v2 + with: + ref: ${{ env.HEAD_COMMIT }} + # TODO: removing `fetch-depth` below seems to have no effect, and the commit which added it had no description, + # so it's not clear why it's necessary. That said, right now ember tests are xfail, so it's a little hard to + # tell if it's safe to remove. Once ember tests are fixed, let's try again with it turned off, and if all goes + # well, we can pull it out. + fetch-depth: 0 + - name: Set up Node + uses: actions/setup-node@v1 + with: + # The only danger with Ember in terms of Node versions is that the build tool, ember-cli, won't work if Node + # is too old. Since Oct 2019, Node 10 has been the oldest version supported by ember-cli, so test against + # that. If it passes, newer versions of Node should also be fine. This saves us from having to run the Ember + # tests in our Node matrix above. + node-version: '10' + - name: Check dependency cache + uses: actions/cache@v2 + with: + path: ${{ env.CACHED_DEPENDENCY_PATHS }} + key: ${{ needs.job_build_with_sucrase.outputs.dependency_cache_key }} + - name: Check build cache + uses: actions/cache@v2 + with: + path: ${{ env.CACHED_BUILD_PATHS }} + key: ${{ env.BUILD_CACHE_KEY }}-sucrase + - name: Run Ember tests + run: yarn test --scope=@sentry/ember + - name: Compute test coverage + uses: codecov/codecov-action@v1 + + job_browser_playwright_tests_sucrase: + name: Sucrase Playwright - ${{ (matrix.tracing_only && 'Browser + Tracing') || 'Browser' }} (${{ matrix.bundle }}) + needs: job_build_with_sucrase + runs-on: ubuntu-latest + strategy: + matrix: + bundle: + - esm + - cjs + tracing_only: + - true + - false + exclude: + # `tracing_only` only makes a difference for bundles - tests of the esm and cjs builds always include the + # tracing tests + - bundle: esm + tracing_only: false + - bundle: cjs + tracing_only: false + steps: + - name: Check out current commit (${{ env.HEAD_COMMIT }}) + uses: actions/checkout@v2 + with: + ref: ${{ env.HEAD_COMMIT }} + - name: Set up Node + uses: actions/setup-node@v1 + with: + node-version: '16' + - name: Check dependency cache + uses: actions/cache@v2 + with: + path: ${{ env.CACHED_DEPENDENCY_PATHS }} + key: ${{ needs.job_build_with_sucrase.outputs.dependency_cache_key }} + - name: Check build cache + uses: actions/cache@v2 + with: + path: ${{ env.CACHED_BUILD_PATHS }} + key: ${{ env.BUILD_CACHE_KEY }}-sucrase + - name: Run Playwright tests + env: + PW_BUNDLE: ${{ matrix.bundle }} + PW_TRACING_ONLY: ${{ matrix.tracing_only }} + run: | + cd packages/integration-tests + yarn run playwright install-deps webkit + yarn test:ci + + job_browser_integration_tests_sucrase: + name: Sucrase Old Browser Integration Tests (${{ matrix.browser }}) + needs: job_build_with_sucrase + runs-on: ubuntu-latest + timeout-minutes: 10 + continue-on-error: true + strategy: + matrix: + browser: + - ChromeHeadless + - FirefoxHeadless + - WebkitHeadless + steps: + - name: Check out current commit (${{ env.HEAD_COMMIT }}) + uses: actions/checkout@v2 + with: + ref: ${{ env.HEAD_COMMIT }} + - name: Set up Node + uses: actions/setup-node@v1 + - name: Check dependency cache + uses: actions/cache@v2 + with: + path: ${{ env.CACHED_DEPENDENCY_PATHS }} + key: ${{ needs.job_build_with_sucrase.outputs.dependency_cache_key }} + - name: Check build cache + uses: actions/cache@v2 + with: + path: ${{ env.CACHED_BUILD_PATHS }} + key: ${{ env.BUILD_CACHE_KEY }}-sucrase + - name: Run integration tests + env: + KARMA_BROWSER: ${{ matrix.browser }} + run: | + cd packages/browser + [[ $KARMA_BROWSER == WebkitHeadless ]] && yarn run playwright install-deps webkit + yarn test:integration + + job_browser_build_tests_sucrase: + name: Sucrase Browser Build Tests + needs: job_build_with_sucrase + runs-on: ubuntu-latest + timeout-minutes: 5 + continue-on-error: true + steps: + - name: Check out current commit (${ env.HEAD_COMMIT }}) + uses: actions/checkout@v2 + with: + ref: ${{ env.HEAD_COMMIT }} + - name: Set up Node + uses: actions/setup-node@v1 + with: + node-version: '16' + - name: Check dependency cache + uses: actions/cache@v2 + with: + path: ${{ env.CACHED_DEPENDENCY_PATHS }} + key: ${{ needs.job_build_with_sucrase.outputs.dependency_cache_key }} + - name: Check build cache + uses: actions/cache@v2 + with: + path: ${{ env.CACHED_BUILD_PATHS }} + key: ${{ env.BUILD_CACHE_KEY }}-sucrase + - name: Run browser build tests + run: | + cd packages/browser + yarn test:package + - name: Run utils build tests + run: | + cd packages/utils + yarn test:package + + job_node_integration_tests_sucrase: + name: Sucrase Node SDK Integration Tests (${{ matrix.node }}) + needs: job_build_with_sucrase + runs-on: ubuntu-latest + timeout-minutes: 10 + continue-on-error: true + strategy: + matrix: + node: [10, 12, 14, 16] + steps: + - name: Check out current commit (${{ github.sha }}) + uses: actions/checkout@v2 + with: + ref: ${{ env.HEAD_COMMIT }} + - name: Set up Node + uses: actions/setup-node@v1 + with: + node-version: ${{ matrix.node }} + - name: Check dependency cache + uses: actions/cache@v2 + with: + path: ${{ env.CACHED_DEPENDENCY_PATHS }} + key: ${{ needs.job_build_with_sucrase.outputs.dependency_cache_key }} + - name: Check build cache + uses: actions/cache@v2 + with: + path: ${{ env.CACHED_BUILD_PATHS }} + key: ${{ env.BUILD_CACHE_KEY }}-sucrase + - name: Run integration tests + env: + NODE_VERSION: ${{ matrix.node }} + run: | + cd packages/node-integration-tests + yarn test diff --git a/package.json b/package.json index a9990a79561b..e3cccf304ea0 100644 --- a/package.json +++ b/package.json @@ -8,6 +8,7 @@ "build:dev:filter": "lerna run --stream --concurrency 1 --sort build:dev --include-filtered-dependencies --include-filtered-dependents --scope", "build:es5": "yarn lerna run --stream --concurrency 1 --sort build:cjs # *** backwards compatibility - remove in v7 ***", "build:esm": "lerna run --stream --concurrency 1 --sort build:esm", + "build:rollup": "lerna run --stream --concurrency 1 --sort build:rollup", "build:types": "lerna run --stream --concurrency 1 --sort build:types", "build:watch": "lerna run --parallel build:watch", "build:dev:watch": "lerna run --parallel build:dev:watch", @@ -57,6 +58,7 @@ "@rollup/plugin-commonjs": "^21.0.1", "@rollup/plugin-node-resolve": "^13.1.3", "@rollup/plugin-replace": "^3.0.1", + "@rollup/plugin-sucrase": "^4.0.3", "@size-limit/preset-small-lib": "^4.5.5", "@strictsoftware/typedoc-plugin-monorepo": "^0.3.1", "@types/chai": "^4.1.3", diff --git a/packages/browser/package.json b/packages/browser/package.json index 8f9eb0013e26..2263eff402cc 100644 --- a/packages/browser/package.json +++ b/packages/browser/package.json @@ -49,6 +49,7 @@ "build:dev": "run-p build:cjs build:esm build:types", "build:es5": "yarn build:cjs # *** backwards compatibility - remove in v7 ***", "build:esm": "tsc -p tsconfig.esm.json", + "build:rollup": "rollup -c rollup.npm.config.js", "build:types": "tsc -p tsconfig.types.json", "build:watch": "run-p build:cjs:watch build:esm:watch build:bundle:watch build:types:watch", "build:bundle:watch": "rollup --config --watch", @@ -56,6 +57,7 @@ "build:es5:watch": "yarn build:cjs:watch # *** backwards compatibility - remove in v7 ***", "build:dev:watch": "run-p build:cjs:watch build:esm:watch build:types:watch", "build:esm:watch": "tsc -p tsconfig.esm.json --watch", + "build:rollup:watch": "rollup -c rollup.npm.config.js --watch", "build:types:watch": "tsc -p tsconfig.types.json --watch", "build:npm": "ts-node ../../scripts/prepack.ts --bundles && npm pack ./build/npm", "circularDepCheck": "madge --circular src/index.ts", diff --git a/packages/browser/rollup.npm.config.js b/packages/browser/rollup.npm.config.js new file mode 100644 index 000000000000..4ffa8b9396d8 --- /dev/null +++ b/packages/browser/rollup.npm.config.js @@ -0,0 +1,8 @@ +import { makeBaseNPMConfig, makeNPMConfigVariants } from '../../rollup/index.js'; + +export default makeNPMConfigVariants( + makeBaseNPMConfig({ + // packages with bundles have a different build directory structure + hasBundles: true, + }), +); diff --git a/packages/core/package.json b/packages/core/package.json index 737007cf648b..a66c34b5035f 100644 --- a/packages/core/package.json +++ b/packages/core/package.json @@ -27,12 +27,14 @@ "build:dev": "run-s build", "build:es5": "yarn build:cjs # *** backwards compatibility - remove in v7 ***", "build:esm": "tsc -p tsconfig.esm.json", + "build:rollup": "rollup -c rollup.npm.config.js", "build:types": "tsc -p tsconfig.types.json", "build:watch": "run-p build:cjs:watch build:esm:watch build:types:watch", "build:cjs:watch": "tsc -p tsconfig.cjs.json --watch", "build:dev:watch": "run-s build:watch", "build:es5:watch": "yarn build:cjs:watch # *** backwards compatibility - remove in v7 ***", "build:esm:watch": "tsc -p tsconfig.esm.json --watch", + "build:rollup:watch": "rollup -c rollup.npm.config.js --watch", "build:types:watch": "tsc -p tsconfig.types.json --watch", "build:npm": "ts-node ../../scripts/prepack.ts && npm pack ./build", "circularDepCheck": "madge --circular src/index.ts", diff --git a/packages/core/rollup.npm.config.js b/packages/core/rollup.npm.config.js new file mode 100644 index 000000000000..5a62b528ef44 --- /dev/null +++ b/packages/core/rollup.npm.config.js @@ -0,0 +1,3 @@ +import { makeBaseNPMConfig, makeNPMConfigVariants } from '../../rollup/index.js'; + +export default makeNPMConfigVariants(makeBaseNPMConfig()); diff --git a/packages/gatsby/package.json b/packages/gatsby/package.json index e65aa2714b27..5fc7e5e08402 100644 --- a/packages/gatsby/package.json +++ b/packages/gatsby/package.json @@ -40,12 +40,14 @@ "build:es5": "yarn build:cjs # *** backwards compatibility - remove in v7 ***", "build:esm": "tsc -p tsconfig.esm.json", "build:plugin": "tsc -p tsconfig.plugin.json", + "build:rollup": "rollup -c rollup.npm.config.js", "build:types": "tsc -p tsconfig.types.json", "build:watch": "run-p build:cjs:watch build:esm:watch build:types:watch", "build:cjs:watch": "tsc -p tsconfig.cjs.json --watch", "build:dev:watch": "run-s build:watch", "build:es5:watch": "yarn build:cjs:watch # *** backwards compatibility - remove in v7 ***", "build:esm:watch": "tsc -p tsconfig.esm.json --watch", + "build:rollup:watch": "rollup -c rollup.npm.config.js --watch", "build:types:watch": "tsc -p tsconfig.types.json --watch", "build:npm": "ts-node ../../scripts/prepack.ts && npm pack ./build", "circularDepCheck": "madge --circular src/index.ts", diff --git a/packages/gatsby/rollup.npm.config.js b/packages/gatsby/rollup.npm.config.js new file mode 100644 index 000000000000..5a62b528ef44 --- /dev/null +++ b/packages/gatsby/rollup.npm.config.js @@ -0,0 +1,3 @@ +import { makeBaseNPMConfig, makeNPMConfigVariants } from '../../rollup/index.js'; + +export default makeNPMConfigVariants(makeBaseNPMConfig()); diff --git a/packages/hub/package.json b/packages/hub/package.json index 13351981746c..66cca210583a 100644 --- a/packages/hub/package.json +++ b/packages/hub/package.json @@ -26,12 +26,14 @@ "build:dev": "run-s build", "build:es5": "yarn build:cjs # *** backwards compatibility - remove in v7 ***", "build:esm": "tsc -p tsconfig.esm.json", + "build:rollup": "rollup -c rollup.npm.config.js", "build:types": "tsc -p tsconfig.types.json", "build:watch": "run-p build:cjs:watch build:esm:watch build:types:watch", "build:cjs:watch": "tsc -p tsconfig.cjs.json --watch", "build:dev:watch": "run-s build:watch", "build:es5:watch": "yarn build:cjs:watch # *** backwards compatibility - remove in v7 ***", "build:esm:watch": "tsc -p tsconfig.esm.json --watch", + "build:rollup:watch": "rollup -c rollup.npm.config.js --watch", "build:types:watch": "tsc -p tsconfig.types.json --watch", "build:npm": "ts-node ../../scripts/prepack.ts && npm pack ./build", "circularDepCheck": "madge --circular src/index.ts", diff --git a/packages/hub/rollup.npm.config.js b/packages/hub/rollup.npm.config.js new file mode 100644 index 000000000000..5a62b528ef44 --- /dev/null +++ b/packages/hub/rollup.npm.config.js @@ -0,0 +1,3 @@ +import { makeBaseNPMConfig, makeNPMConfigVariants } from '../../rollup/index.js'; + +export default makeNPMConfigVariants(makeBaseNPMConfig()); diff --git a/packages/integrations/package.json b/packages/integrations/package.json index de3eb70ed7bd..e7097f2c486c 100644 --- a/packages/integrations/package.json +++ b/packages/integrations/package.json @@ -31,12 +31,14 @@ "build:dev": "run-p build:cjs build:esm build:types", "build:es5": "yarn build:cjs # *** backwards compatibility - remove in v7 ***", "build:esm": "tsc -p tsconfig.esm.json", + "build:rollup": "rollup -c rollup.npm.config.js", "build:types": "tsc -p tsconfig.types.json", "build:watch": "run-p build:cjs:watch build:esm:watch build:types:watch", "build:cjs:watch": "tsc -p tsconfig.cjs.json --watch", "build:dev:watch": "run-s build:watch", "build:es5:watch": "yarn build:cjs:watch # *** backwards compatibility - remove in v7 ***", "build:esm:watch": "tsc -p tsconfig.esm.json --watch", + "build:rollup:watch": "rollup -c rollup.npm.config.js --watch", "build:types:watch": "tsc -p tsconfig.types.json --watch", "build:npm": "ts-node ../../scripts/prepack.ts --bundles && npm pack ./build/npm", "circularDepCheck": "madge --circular src/index.ts", diff --git a/packages/integrations/rollup.npm.config.js b/packages/integrations/rollup.npm.config.js new file mode 100644 index 000000000000..4ffa8b9396d8 --- /dev/null +++ b/packages/integrations/rollup.npm.config.js @@ -0,0 +1,8 @@ +import { makeBaseNPMConfig, makeNPMConfigVariants } from '../../rollup/index.js'; + +export default makeNPMConfigVariants( + makeBaseNPMConfig({ + // packages with bundles have a different build directory structure + hasBundles: true, + }), +); diff --git a/packages/nextjs/package.json b/packages/nextjs/package.json index dd232c23d374..07529b57ba03 100644 --- a/packages/nextjs/package.json +++ b/packages/nextjs/package.json @@ -48,12 +48,14 @@ "build:dev": "run-s build", "build:es5": "yarn build:cjs # *** backwards compatibility - remove in v7 ***", "build:esm": "tsc -p tsconfig.esm.json", + "build:rollup": "rollup -c rollup.npm.config.js", "build:types": "tsc -p tsconfig.types.json", "build:watch": "run-p build:cjs:watch build:esm:watch build:types:watch", "build:cjs:watch": "tsc -p tsconfig.cjs.json --watch", "build:dev:watch": "run-s build:watch", "build:es5:watch": "yarn build:cjs:watch # *** backwards compatibility - remove in v7 ***", "build:esm:watch": "tsc -p tsconfig.esm.json --watch", + "build:rollup:watch": "rollup -c rollup.npm.config.js --watch", "build:types:watch": "tsc -p tsconfig.types.json --watch", "build:npm": "ts-node ../../scripts/prepack.ts && npm pack ./build", "circularDepCheck": "madge --circular src/index.client.ts && madge --circular --exclude 'config/types\\.ts' src/index.server.ts # see https://github.com/pahen/madge/issues/306", diff --git a/packages/nextjs/rollup.npm.config.js b/packages/nextjs/rollup.npm.config.js new file mode 100644 index 000000000000..a41fb991432c --- /dev/null +++ b/packages/nextjs/rollup.npm.config.js @@ -0,0 +1,12 @@ +import { makeBaseNPMConfig, makeNPMConfigVariants } from '../../rollup/index.js'; + +export default makeNPMConfigVariants( + makeBaseNPMConfig({ + // We need to include `instrumentServer.ts` separately because it's only conditionally required, and so rollup + // doesn't automatically include it when calculating the module dependency tree. + entrypoints: ['src/index.server.ts', 'src/index.client.ts', 'src/utils/instrumentServer.ts'], + // prevent this nextjs code from ending up in our built package (this doesn't happen automatially because the name + // doesn't match an SDK dependency) + externals: ['next/router'], + }), +); diff --git a/packages/node/package.json b/packages/node/package.json index cbb1e4e2f7b0..f67eace899d3 100644 --- a/packages/node/package.json +++ b/packages/node/package.json @@ -39,12 +39,14 @@ "build:dev": "run-s build", "build:es5": "yarn build:cjs # *** backwards compatibility - remove in v7 ***", "build:esm": "tsc -p tsconfig.esm.json", + "build:rollup": "rollup -c rollup.npm.config.js", "build:types": "tsc -p tsconfig.types.json", "build:watch": "run-p build:cjs:watch build:esm:watch build:types:watch", "build:cjs:watch": "tsc -p tsconfig.cjs.json --watch", "build:dev:watch": "run-s build:watch", "build:es5:watch": "yarn build:cjs:watch # *** backwards compatibility - remove in v7 ***", "build:esm:watch": "tsc -p tsconfig.esm.json --watch", + "build:rollup:watch": "rollup -c rollup.npm.config.js --watch", "build:types:watch": "tsc -p tsconfig.types.json --watch", "build:npm": "ts-node ../../scripts/prepack.ts && npm pack ./build", "circularDepCheck": "madge --circular src/index.ts", diff --git a/packages/node/rollup.npm.config.js b/packages/node/rollup.npm.config.js new file mode 100644 index 000000000000..5a62b528ef44 --- /dev/null +++ b/packages/node/rollup.npm.config.js @@ -0,0 +1,3 @@ +import { makeBaseNPMConfig, makeNPMConfigVariants } from '../../rollup/index.js'; + +export default makeNPMConfigVariants(makeBaseNPMConfig()); diff --git a/packages/react/package.json b/packages/react/package.json index 0365a6968bc5..67a29a291385 100644 --- a/packages/react/package.json +++ b/packages/react/package.json @@ -52,12 +52,14 @@ "build:dev": "run-s build", "build:es5": "yarn build:cjs # *** backwards compatibility - remove in v7 ***", "build:esm": "tsc -p tsconfig.esm.json", + "build:rollup": "rollup -c rollup.npm.config.js", "build:types": "tsc -p tsconfig.types.json", "build:watch": "run-p build:cjs:watch build:esm:watch build:types:watch", "build:cjs:watch": "tsc -p tsconfig.cjs.json --watch", "build:dev:watch": "run-s build:watch", "build:es5:watch": "yarn build:cjs:watch # *** backwards compatibility - remove in v7 ***", "build:esm:watch": "tsc -p tsconfig.esm.json --watch", + "build:rollup:watch": "rollup -c rollup.npm.config.js --watch", "build:types:watch": "tsc -p tsconfig.types.json --watch", "build:npm": "ts-node ../../scripts/prepack.ts && npm pack ./build", "circularDepCheck": "madge --circular src/index.ts", diff --git a/packages/react/rollup.npm.config.js b/packages/react/rollup.npm.config.js new file mode 100644 index 000000000000..ebe81bb263c6 --- /dev/null +++ b/packages/react/rollup.npm.config.js @@ -0,0 +1,7 @@ +import { makeBaseNPMConfig, makeNPMConfigVariants } from '../../rollup/index.js'; + +export default makeNPMConfigVariants( + makeBaseNPMConfig({ + esModuleInterop: true, + }), +); diff --git a/packages/serverless/package.json b/packages/serverless/package.json index c7a83cfdaa20..97c20654f559 100644 --- a/packages/serverless/package.json +++ b/packages/serverless/package.json @@ -44,12 +44,14 @@ "build:dev": "run-p build:cjs build:esm build:types", "build:es5": "yarn build:cjs # *** backwards compatibility - remove in v7 ***", "build:esm": "tsc -p tsconfig.esm.json", + "build:rollup": "rollup -c rollup.npm.config.js", "build:types": "tsc -p tsconfig.types.json", "build:watch": "run-p build:cjs:watch build:esm:watch build:types:watch", "build:cjs:watch": "tsc -p tsconfig.cjs.json --watch", "build:dev:watch": "run-s build:watch", "build:es5:watch": "yarn build:cjs:watch # *** backwards compatibility - remove in v7 ***", "build:esm:watch": "tsc -p tsconfig.esm.json --watch", + "build:rollup:watch": "rollup -c rollup.npm.config.js --watch", "build:types:watch": "tsc -p tsconfig.types.json --watch", "build:npm": "ts-node ../../scripts/prepack.ts && npm pack ./build", "circularDepCheck": "madge --circular src/index.ts", diff --git a/packages/serverless/rollup.npm.config.js b/packages/serverless/rollup.npm.config.js new file mode 100644 index 000000000000..9b3074be8002 --- /dev/null +++ b/packages/serverless/rollup.npm.config.js @@ -0,0 +1,7 @@ +import { makeBaseNPMConfig, makeNPMConfigVariants } from '../../rollup/index.js'; + +export default makeNPMConfigVariants( + makeBaseNPMConfig({ + entrypoints: ['src/index.ts', 'src/awslambda-auto.ts'], + }), +); diff --git a/packages/tracing/package.json b/packages/tracing/package.json index 00a298e3937f..1cfa4aa2dafe 100644 --- a/packages/tracing/package.json +++ b/packages/tracing/package.json @@ -32,6 +32,7 @@ "build:dev": "run-p build:cjs build:esm build:types", "build:es5": "yarn build:cjs # *** backwards compatibility - remove in v7 ***", "build:esm": "tsc -p tsconfig.esm.json", + "build:rollup": "rollup -c rollup.npm.config.js", "build:types": "tsc -p tsconfig.types.json", "build:watch": "run-p build:cjs:watch build:esm:watch build:bundle:watch build:types:watch", "build:bundle:watch": "rollup --config --watch", @@ -39,6 +40,7 @@ "build:dev:watch": "run-p build:cjs:watch build:esm:watch build:types:watch", "build:es5:watch": "yarn build:cjs:watch # *** backwards compatibility - remove in v7 ***", "build:esm:watch": "tsc -p tsconfig.esm.json --watch", + "build:rollup:watch": "rollup -c rollup.npm.config.js --watch", "build:types:watch": "tsc -p tsconfig.types.json --watch", "build:npm": "ts-node ../../scripts/prepack.ts --bundles && npm pack ./build/npm", "clean": "rimraf build coverage", diff --git a/packages/tracing/rollup.npm.config.js b/packages/tracing/rollup.npm.config.js new file mode 100644 index 000000000000..4ffa8b9396d8 --- /dev/null +++ b/packages/tracing/rollup.npm.config.js @@ -0,0 +1,8 @@ +import { makeBaseNPMConfig, makeNPMConfigVariants } from '../../rollup/index.js'; + +export default makeNPMConfigVariants( + makeBaseNPMConfig({ + // packages with bundles have a different build directory structure + hasBundles: true, + }), +); diff --git a/packages/types/package.json b/packages/types/package.json index 5c1c37c1c159..9799b8aa4fcf 100644 --- a/packages/types/package.json +++ b/packages/types/package.json @@ -21,12 +21,14 @@ "build:dev": "run-s build", "build:es5": "yarn build:cjs # *** backwards compatibility - remove in v7 ***", "build:esm": "tsc -p tsconfig.esm.json", + "build:rollup": "rollup -c rollup.npm.config.js", "build:types": "tsc -p tsconfig.types.json", "build:watch": "run-p build:cjs:watch build:esm:watch build:types:watch", "build:cjs:watch": "tsc -p tsconfig.cjs.json --watch", "build:dev:watch": "run-s build:watch", "build:es5:watch": "yarn build:cjs:watch # *** backwards compatibility - remove in v7 ***", "build:esm:watch": "tsc -p tsconfig.esm.json --watch", + "build:rollup:watch": "rollup -c rollup.npm.config.js --watch", "build:types:watch": "tsc -p tsconfig.types.json --watch", "build:npm": "ts-node ../../scripts/prepack.ts && npm pack ./build", "clean": "rimraf build", diff --git a/packages/types/rollup.npm.config.js b/packages/types/rollup.npm.config.js new file mode 100644 index 000000000000..5a62b528ef44 --- /dev/null +++ b/packages/types/rollup.npm.config.js @@ -0,0 +1,3 @@ +import { makeBaseNPMConfig, makeNPMConfigVariants } from '../../rollup/index.js'; + +export default makeNPMConfigVariants(makeBaseNPMConfig()); diff --git a/packages/utils/package.json b/packages/utils/package.json index 142ae23e6010..1e84b03ca42d 100644 --- a/packages/utils/package.json +++ b/packages/utils/package.json @@ -28,12 +28,14 @@ "build:dev": "run-s build", "build:es5": "yarn build:cjs # *** backwards compatibility - remove in v7 ***", "build:esm": "tsc -p tsconfig.esm.json", + "build:rollup": "rollup -c rollup.npm.config.js", "build:types": "tsc -p tsconfig.types.json", "build:watch": "run-p build:cjs:watch build:esm:watch build:types:watch", "build:cjs:watch": "tsc -p tsconfig.cjs.json --watch", "build:dev:watch": "run-s build:watch", "build:es5:watch": "yarn build:cjs:watch # *** backwards compatibility - remove in v7 ***", "build:esm:watch": "tsc -p tsconfig.esm.json --watch", + "build:rollup:watch": "rollup -c rollup.npm.config.js --watch", "build:types:watch": "tsc -p tsconfig.types.json --watch", "build:npm": "ts-node ../../scripts/prepack.ts && npm pack ./build", "circularDepCheck": "madge --circular src/index.ts", diff --git a/packages/utils/rollup.npm.config.js b/packages/utils/rollup.npm.config.js new file mode 100644 index 000000000000..5a62b528ef44 --- /dev/null +++ b/packages/utils/rollup.npm.config.js @@ -0,0 +1,3 @@ +import { makeBaseNPMConfig, makeNPMConfigVariants } from '../../rollup/index.js'; + +export default makeNPMConfigVariants(makeBaseNPMConfig()); diff --git a/packages/vue/package.json b/packages/vue/package.json index 406f335f05de..66af3d30fc92 100644 --- a/packages/vue/package.json +++ b/packages/vue/package.json @@ -32,6 +32,7 @@ "build:dev": "run-p build:cjs build:esm build:types", "build:es5": "yarn build:cjs # *** backwards compatibility - remove in v7 ***", "build:esm": "tsc -p tsconfig.esm.json", + "build:rollup": "rollup -c rollup.npm.config.js", "build:types": "tsc -p tsconfig.types.json", "build:watch": "run-p build:cjs:watch build:esm:watch build:types:watch", "build:bundle:watch": "rollup --config --watch", @@ -39,6 +40,7 @@ "build:dev:watch": "run-p build:cjs:watch build:esm:watch build:types:watch", "build:es5:watch": "yarn build:cjs:watch # *** backwards compatibility - remove in v7 ***", "build:esm:watch": "tsc -p tsconfig.esm.json --watch", + "build:rollup:watch": "rollup -c rollup.npm.config.js --watch", "build:types:watch": "tsc -p tsconfig.types.json --watch", "build:npm": "ts-node ../../scripts/prepack.ts && npm pack ./build", "circularDepCheck": "madge --circular src/index.ts", diff --git a/packages/vue/rollup.npm.config.js b/packages/vue/rollup.npm.config.js new file mode 100644 index 000000000000..5a62b528ef44 --- /dev/null +++ b/packages/vue/rollup.npm.config.js @@ -0,0 +1,3 @@ +import { makeBaseNPMConfig, makeNPMConfigVariants } from '../../rollup/index.js'; + +export default makeNPMConfigVariants(makeBaseNPMConfig()); diff --git a/packages/wasm/package.json b/packages/wasm/package.json index 671ddad9c5f2..e34b788aceef 100644 --- a/packages/wasm/package.json +++ b/packages/wasm/package.json @@ -35,6 +35,7 @@ "build:dev": "run-p build:cjs build:esm build:types", "build:es5": "yarn build:cjs # *** backwards compatibility - remove in v7 ***", "build:esm": "tsc -p tsconfig.esm.json", + "build:rollup": "rollup -c rollup.npm.config.js", "build:types": "tsc -p tsconfig.types.json", "build:watch": "run-p build:cjs:watch build:esm:watch build:bundle:watch build:types:watch", "build:bundle:watch": "rollup --config --watch", @@ -42,6 +43,7 @@ "build:dev:watch": "run-p build:cjs:watch build:esm:watch build:types:watch", "build:es5:watch": "yarn build:cjs:watch # *** backwards compatibility - remove in v7 ***", "build:esm:watch": "tsc -p tsconfig.esm.json --watch", + "build:rollup:watch": "rollup -c rollup.npm.config.js --watch", "build:types:watch": "tsc -p tsconfig.types.json --watch", "build:npm": "ts-node ../../scripts/prepack.ts --bundles && npm pack ./build/npm", "circularDepCheck": "madge --circular src/index.ts", diff --git a/packages/wasm/rollup.npm.config.js b/packages/wasm/rollup.npm.config.js new file mode 100644 index 000000000000..4ffa8b9396d8 --- /dev/null +++ b/packages/wasm/rollup.npm.config.js @@ -0,0 +1,8 @@ +import { makeBaseNPMConfig, makeNPMConfigVariants } from '../../rollup/index.js'; + +export default makeNPMConfigVariants( + makeBaseNPMConfig({ + // packages with bundles have a different build directory structure + hasBundles: true, + }), +); diff --git a/rollup/index.js b/rollup/index.js index 51baa9ba247c..2ae4712165ad 100644 --- a/rollup/index.js +++ b/rollup/index.js @@ -5,4 +5,5 @@ import * as plugins from './plugins/index.js'; export { plugins }; export * from './bundleHelpers.js'; +export * from './npmHelpers.js'; export { insertAt } from './utils.js'; diff --git a/rollup/npmHelpers.js b/rollup/npmHelpers.js new file mode 100644 index 000000000000..b4397a56f466 --- /dev/null +++ b/rollup/npmHelpers.js @@ -0,0 +1,90 @@ +/** + * Rollup config docs: https://rollupjs.org/guide/en/#big-list-of-options + */ + +import { builtinModules } from 'module'; +import * as path from 'path'; + +import deepMerge from 'deepmerge'; + +import { makeNodeResolvePlugin, makeSucrasePlugin } from './plugins/index.js'; + +const packageDotJSON = require(path.resolve(process.cwd(), './package.json')); + +export function makeBaseNPMConfig(options = {}) { + const { + entrypoints = ['src/index.ts'], + esModuleInterop = false, + externals: packageSpecificExternals = [], + hasBundles = false, + } = options; + + const nodeResolvePlugin = makeNodeResolvePlugin(); + const sucrasePlugin = makeSucrasePlugin(); + + return { + input: entrypoints, + + output: { + // an appropriately-named directory will be added to this base value when we specify either a cjs or esm build + dir: hasBundles ? 'build/npm' : 'build', + + sourcemap: true, + + // output individual files rather than one big bundle + preserveModules: true, + + // any wrappers or helper functions generated by rollup can use ES6 features + generatedCode: 'es2015', + + // don't add `"use strict"` to the top of cjs files + strict: false, + + // do TS-3.8-style exports + // exports.dogs = are.great + // rather than TS-3.9-style exports + // Object.defineProperty(exports, 'dogs', { + // enumerable: true, + // get: () => are.great, + // }); + externalLiveBindings: false, + + // Equivalent to `esModuleInterop` in tsconfig. + // Controls whether rollup emits helpers to handle special cases where turning + // `import * as dogs from 'dogs'` + // into + // `const dogs = require('dogs')` + // doesn't work. + // + // `auto` -> emit helpers + // `esModule` -> don't emit helpers + interop: esModuleInterop ? 'auto' : 'esModule', + }, + + plugins: [nodeResolvePlugin, sucrasePlugin], + + // don't include imported modules from outside the package in the final output + external: [ + ...builtinModules, + ...Object.keys(packageDotJSON.dependencies || {}), + ...Object.keys(packageDotJSON.devDependencies || {}), + ...Object.keys(packageDotJSON.peerDependencies || {}), + ...packageSpecificExternals, + ], + + // TODO `'smallest'` will get rid of `isDebugBuild()` by evaluating it and inlining the result and then treeshaking + // from there. The current setting (false) prevents this, in case we want to leave it there for users to use in + // their own bundling. That said, we don't yet know for sure that that works, so come back to this. + // treeshake: 'smallest', + treeshake: false, + }; +} + +export function makeNPMConfigVariants(baseConfig) { + const variantSpecificConfigs = [ + { output: { format: 'cjs', dir: path.join(baseConfig.output.dir, 'cjs') } }, + { output: { format: 'esm', dir: path.join(baseConfig.output.dir, 'esm') } }, + ]; + + return variantSpecificConfigs.map(variant => deepMerge(baseConfig, variant)); +} diff --git a/rollup/plugins/index.js b/rollup/plugins/index.js index bb05969c2caf..014b3b383b4d 100644 --- a/rollup/plugins/index.js +++ b/rollup/plugins/index.js @@ -1 +1,2 @@ export * from './bundlePlugins'; +export * from './npmPlugins'; diff --git a/rollup/plugins/npmPlugins.js b/rollup/plugins/npmPlugins.js new file mode 100644 index 000000000000..5f4c443682ef --- /dev/null +++ b/rollup/plugins/npmPlugins.js @@ -0,0 +1,16 @@ +/** + * Sucrase plugin docs: https://github.com/rollup/plugins/tree/master/packages/sucrase + */ + +import sucrase from '@rollup/plugin-sucrase'; + +/** + * Create a plugin to transpile TS syntax using `sucrase`. + * + * @returns An instance of the `@rollup/plugin-sucrase` plugin + */ +export function makeSucrasePlugin() { + return sucrase({ + transforms: ['typescript', 'jsx'], + }); +} diff --git a/yarn.lock b/yarn.lock index 9304663e7767..6b21e6baa196 100644 --- a/yarn.lock +++ b/yarn.lock @@ -4197,6 +4197,14 @@ "@rollup/pluginutils" "^3.1.0" magic-string "^0.25.7" +"@rollup/plugin-sucrase@^4.0.3": + version "4.0.3" + resolved "https://registry.yarnpkg.com/@rollup/plugin-sucrase/-/plugin-sucrase-4.0.3.tgz#b972ba61db0faaba397e09daaffcdbd38c167e2c" + integrity sha512-gZrjT985isK+EmHt3Dyr9z4JfO9IGuYkxck96yIyIUU9EKnZtDXUZ6ap5kvIdEnY8kLeiypiUEfK+/QtMIlA2A== + dependencies: + "@rollup/pluginutils" "^4.1.1" + sucrase "^3.20.0" + "@rollup/pluginutils@^3.0.8", "@rollup/pluginutils@^3.0.9", "@rollup/pluginutils@^3.1.0": version "3.1.0" resolved "https://registry.yarnpkg.com/@rollup/pluginutils/-/pluginutils-3.1.0.tgz#706b4524ee6dc8b103b3c995533e5ad680c02b9b" @@ -4206,6 +4214,14 @@ estree-walker "^1.0.1" picomatch "^2.2.2" +"@rollup/pluginutils@^4.1.1": + version "4.2.1" + resolved "https://registry.yarnpkg.com/@rollup/pluginutils/-/pluginutils-4.2.1.tgz#e6c6c3aba0744edce3fb2074922d3776c0af2a6d" + integrity sha512-iKnFXr7NkdZAIHiIWE+BX5ULi/ucVFYWD6TbAV+rZctiRTY2PL6tsIKhoIOaoskiWAkgu+VsbXgUVDNLHf+InQ== + dependencies: + estree-walker "^2.0.1" + picomatch "^2.2.2" + "@rollup/pluginutils@^4.1.2": version "4.1.2" resolved "https://registry.yarnpkg.com/@rollup/pluginutils/-/pluginutils-4.1.2.tgz#ed5821c15e5e05e32816f5fb9ec607cdf5a75751" @@ -5959,6 +5975,11 @@ ansicolors@~0.2.1: resolved "https://registry.yarnpkg.com/ansicolors/-/ansicolors-0.2.1.tgz#be089599097b74a5c9c4a84a0cdbcdb62bd87aef" integrity sha1-vgiVmQl7dKXJxKhKDNvNtivYeu8= +any-promise@^1.0.0: + version "1.3.0" + resolved "https://registry.yarnpkg.com/any-promise/-/any-promise-1.3.0.tgz#abc6afeedcea52e809cdc0376aed3ce39635d17f" + integrity sha1-q8av7tzqUugJzcA3au0845Y10X8= + anymatch@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/anymatch/-/anymatch-2.0.0.tgz#bcb24b4f37934d9aa7ac17b4adaf89e7c76ef2eb" @@ -9077,7 +9098,7 @@ commander@^3.0.2: resolved "https://registry.yarnpkg.com/commander/-/commander-3.0.2.tgz#6837c3fb677ad9933d1cfba42dd14d5117d6b39e" integrity sha512-Gar0ASD4BDyKC4hl4DwHqDrmvjoxWKZigVnAbn5H1owvm4CxCPdb0HQDehwNYMJpla5+M2tPmPARzhtYuwpHow== -commander@^4.1.1: +commander@^4.0.0, commander@^4.1.1: version "4.1.1" resolved "https://registry.yarnpkg.com/commander/-/commander-4.1.1.tgz#9fd602bd936294e9e9ef46a3f4d6964044b18068" integrity sha512-NOKm8xhkzAjzFx8B2v5OAHT+u5pRQc2UCa2Vq9jYL/31o2wi9mxBA7LIFs3sV5VSC49z6pEhfbMULvShKj26WA== @@ -18100,6 +18121,15 @@ mysql@^2.18.1: safe-buffer "5.1.2" sqlstring "2.3.1" +mz@^2.7.0: + version "2.7.0" + resolved "https://registry.yarnpkg.com/mz/-/mz-2.7.0.tgz#95008057a56cafadc2bc63dde7f9ff6955948e32" + integrity sha512-z81GNO7nnYMEhrGh9LeymoE4+Yr0Wn5McHIZMK5cfQCl+NDX08sCZgUc9/6MHni9IWuFLm1Z3HTCXu2z9fN62Q== + dependencies: + any-promise "^1.0.0" + object-assign "^4.0.1" + thenify-all "^1.0.0" + nan@^2.12.1: version "2.14.2" resolved "https://registry.yarnpkg.com/nan/-/nan-2.14.2.tgz#f5376400695168f4cc694ac9393d0c9585eeea19" @@ -23469,6 +23499,18 @@ stylus@0.54.8, stylus@^0.54.7: semver "^6.3.0" source-map "^0.7.3" +sucrase@^3.20.0: + version "3.21.0" + resolved "https://registry.yarnpkg.com/sucrase/-/sucrase-3.21.0.tgz#6a5affdbe716b22e4dc99c57d366ad0d216444b9" + integrity sha512-FjAhMJjDcifARI7bZej0Bi1yekjWQHoEvWIXhLPwDhC6O4iZ5PtGb86WV56riW87hzpgB13wwBKO9vKAiWu5VQ== + dependencies: + commander "^4.0.0" + glob "7.1.6" + lines-and-columns "^1.1.6" + mz "^2.7.0" + pirates "^4.0.1" + ts-interface-checker "^0.1.9" + sum-up@^1.0.1: version "1.0.3" resolved "https://registry.yarnpkg.com/sum-up/-/sum-up-1.0.3.tgz#1c661f667057f63bcb7875aa1438bc162525156e" @@ -23878,6 +23920,20 @@ text-table@^0.2.0: resolved "https://registry.yarnpkg.com/textextensions/-/textextensions-2.6.0.tgz#d7e4ab13fe54e32e08873be40d51b74229b00fc4" integrity sha512-49WtAWS+tcsy93dRt6P0P3AMD2m5PvXRhuEA0kaXos5ZLlujtYmpmFsB+QvWUSxE1ZsstmYXfQ7L40+EcQgpAQ== +thenify-all@^1.0.0: + version "1.6.0" + resolved "https://registry.yarnpkg.com/thenify-all/-/thenify-all-1.6.0.tgz#1a1918d402d8fc3f98fbf234db0bcc8cc10e9726" + integrity sha1-GhkY1ALY/D+Y+/I02wvMjMEOlyY= + dependencies: + thenify ">= 3.1.0 < 4" + +"thenify@>= 3.1.0 < 4": + version "3.3.1" + resolved "https://registry.yarnpkg.com/thenify/-/thenify-3.3.1.tgz#8932e686a4066038a016dd9e2ca46add9838a95f" + integrity sha512-RVZSIV5IG10Hk3enotrhvz0T9em6cyHBLkH/YAZuKqd8hRkKhSfCGIcP2KUY0EPxndzANBmNllzWPwak+bheSw== + dependencies: + any-promise "^1.0.0" + throat@^6.0.1: version "6.0.1" resolved "https://registry.yarnpkg.com/throat/-/throat-6.0.1.tgz#d514fedad95740c12c2d7fc70ea863eb51ade375" @@ -24176,6 +24232,11 @@ trim-right@^1.0.1: resolved "https://registry.yarnpkg.com/trim-right/-/trim-right-1.0.1.tgz#cb2e1203067e0c8de1f614094b9fe45704ea6003" integrity sha1-yy4SAwZ+DI3h9hQJS5/kVwTqYAM= +ts-interface-checker@^0.1.9: + version "0.1.13" + resolved "https://registry.yarnpkg.com/ts-interface-checker/-/ts-interface-checker-0.1.13.tgz#784fd3d679722bc103b1b4b8030bcddb5db2a699" + integrity sha512-Y/arvbn+rrz3JCKl9C4kVNfTfSm2/mEp5FSz5EsZSANGPSlQrpRI5M4PKF+mJnE52jOO90PnPSc3Ur3bTQw0gA== + ts-jest@^27.1.4: version "27.1.4" resolved "https://registry.yarnpkg.com/ts-jest/-/ts-jest-27.1.4.tgz#84d42cf0f4e7157a52e7c64b1492c46330943e00" From aefa9b125ccedd1d161f09053edb08a822e7d9ab Mon Sep 17 00:00:00 2001 From: Luca Forstner Date: Thu, 28 Apr 2022 12:45:42 +0200 Subject: [PATCH 094/204] ref: Refactor transport, request, and client report types (#5007) --- packages/core/src/envelope.ts | 11 +++-------- packages/core/src/transports/base.ts | 4 ++-- packages/types/src/clientreport.ts | 13 ++++++++++--- packages/types/src/datacategory.ts | 20 ++++++++++++++++++++ packages/types/src/index.ts | 7 +++---- packages/types/src/request.ts | 11 ----------- packages/types/src/transport.ts | 10 ---------- packages/utils/test/clientreport.test.ts | 4 ++-- 8 files changed, 40 insertions(+), 40 deletions(-) create mode 100644 packages/types/src/datacategory.ts diff --git a/packages/core/src/envelope.ts b/packages/core/src/envelope.ts index 53572c844a3e..2f69884e8bc3 100644 --- a/packages/core/src/envelope.ts +++ b/packages/core/src/envelope.ts @@ -5,7 +5,6 @@ import { EventItem, SdkInfo, SdkMetadata, - SentryRequestType, Session, SessionAggregates, SessionEnvelope, @@ -52,14 +51,10 @@ export function createSessionEnvelope( ...(!!tunnel && { dsn: dsnToString(dsn) }), }; - // I know this is hacky but we don't want to add `sessions` to request type since it's never rate limited - const type = 'aggregates' in session ? ('sessions' as SentryRequestType) : 'session'; + const envelopeItem: SessionItem = + 'aggregates' in session ? [{ type: 'sessions' }, session] : [{ type: 'session' }, session]; - // TODO (v7) Have to cast type because envelope items do not accept a `SentryRequestType` - const envelopeItem = [{ type } as { type: 'session' | 'sessions' }, session] as SessionItem; - const envelope = createEnvelope(envelopeHeaders, [envelopeItem]); - - return envelope; + return createEnvelope(envelopeHeaders, [envelopeItem]); } /** diff --git a/packages/core/src/transports/base.ts b/packages/core/src/transports/base.ts index 69872908a997..0491d7422f6b 100644 --- a/packages/core/src/transports/base.ts +++ b/packages/core/src/transports/base.ts @@ -1,8 +1,8 @@ import { + DataCategory, Envelope, InternalBaseTransportOptions, Transport, - TransportCategory, TransportRequestExecutor, } from '@sentry/types'; import { @@ -35,7 +35,7 @@ export function createTransport( function send(envelope: Envelope): PromiseLike { const envCategory = getEnvelopeType(envelope); - const category = envCategory === 'event' ? 'error' : (envCategory as TransportCategory); + const category = envCategory === 'event' ? 'error' : (envCategory as DataCategory); // Don't add to buffer if transport is already rate-limited if (isRateLimited(rateLimits, category)) { diff --git a/packages/types/src/clientreport.ts b/packages/types/src/clientreport.ts index 22c590d0cc64..1f59ad4910a3 100644 --- a/packages/types/src/clientreport.ts +++ b/packages/types/src/clientreport.ts @@ -1,7 +1,14 @@ -import { SentryRequestType } from './request'; -import { Outcome } from './transport'; +import { DataCategory } from './datacategory'; + +export type EventDropReason = + | 'before_send' + | 'event_processor' + | 'network_error' + | 'queue_overflow' + | 'ratelimit_backoff' + | 'sample_rate'; export type ClientReport = { timestamp: number; - discarded_events: Array<{ reason: Outcome; category: SentryRequestType; quantity: number }>; + discarded_events: Array<{ reason: EventDropReason; category: DataCategory; quantity: number }>; }; diff --git a/packages/types/src/datacategory.ts b/packages/types/src/datacategory.ts new file mode 100644 index 000000000000..dee9fc16d270 --- /dev/null +++ b/packages/types/src/datacategory.ts @@ -0,0 +1,20 @@ +// This type is used in various places like Client Reports and Rate Limit Categories +// See: +// - https://develop.sentry.dev/sdk/rate-limiting/#definitions +// - https://github.com/getsentry/relay/blob/10874b587bb676bd6d50ad42d507216513660082/relay-common/src/constants.rs#L97-L113 +// - https://develop.sentry.dev/sdk/client-reports/#envelope-item-payload under `discarded_events` +export type DataCategory = + // Reserved and only used in edgecases, unlikely to be ever actually used + | 'default' + // Error events + | 'error' + // Transaction type event + | 'transaction' + // Events with `event_type` csp, hpkp, expectct, expectstaple + | 'security' + // Attachment bytes stored (unused for rate limiting + | 'attachment' + // Session update events + | 'session' + // SDK internal event, like client_reports + | 'internal'; diff --git a/packages/types/src/index.ts b/packages/types/src/index.ts index 107664a68ae0..d74b24fb5266 100644 --- a/packages/types/src/index.ts +++ b/packages/types/src/index.ts @@ -1,7 +1,8 @@ export type { Breadcrumb, BreadcrumbHint } from './breadcrumb'; export type { Client } from './client'; -export type { ClientReport } from './clientreport'; +export type { ClientReport, EventDropReason } from './clientreport'; export type { Context, Contexts } from './context'; +export type { DataCategory } from './datacategory'; export type { DsnComponents, DsnLike, DsnProtocol } from './dsn'; export type { DebugImage, DebugImageType, DebugMeta } from './debugMeta'; export type { @@ -28,7 +29,7 @@ export type { Mechanism } from './mechanism'; export type { ExtractedNodeRequestData, Primitive, WorkerLocation } from './misc'; export type { ClientOptions, Options } from './options'; export type { Package } from './package'; -export type { QueryParams, Request, SentryRequest, SentryRequestType } from './request'; +export type { QueryParams, Request } from './request'; export type { Runtime } from './runtime'; export type { CaptureContext, Scope, ScopeContext } from './scope'; export type { SdkInfo } from './sdkinfo'; @@ -61,9 +62,7 @@ export type { } from './transaction'; export type { Thread } from './thread'; export type { - Outcome, Transport, - TransportCategory, TransportRequest, TransportMakeRequestResponse, InternalBaseTransportOptions, diff --git a/packages/types/src/request.ts b/packages/types/src/request.ts index 052de1b41c08..c06b29525a84 100644 --- a/packages/types/src/request.ts +++ b/packages/types/src/request.ts @@ -1,14 +1,3 @@ -/** Possible SentryRequest types that can be used to make a distinction between Sentry features */ -// NOTE(kamil): It would be nice if we make it a valid enum instead -export type SentryRequestType = 'event' | 'transaction' | 'session' | 'attachment'; - -/** A generic client request. */ -export interface SentryRequest { - body: string; - type: SentryRequestType; - url: string; -} - /** Request data included in an event as sent to Sentry */ export interface Request { url?: string; diff --git a/packages/types/src/transport.ts b/packages/types/src/transport.ts index 5fb204fdd546..e87d664e6388 100644 --- a/packages/types/src/transport.ts +++ b/packages/types/src/transport.ts @@ -1,15 +1,5 @@ import { Envelope } from './envelope'; -export type Outcome = - | 'before_send' - | 'event_processor' - | 'network_error' - | 'queue_overflow' - | 'ratelimit_backoff' - | 'sample_rate'; - -export type TransportCategory = 'error' | 'transaction' | 'attachment' | 'session'; - export type TransportRequest = { body: string; }; diff --git a/packages/utils/test/clientreport.test.ts b/packages/utils/test/clientreport.test.ts index 156ddb95008f..f2abca98e798 100644 --- a/packages/utils/test/clientreport.test.ts +++ b/packages/utils/test/clientreport.test.ts @@ -6,7 +6,7 @@ import { serializeEnvelope } from '../src/envelope'; const DEFAULT_DISCARDED_EVENTS: ClientReport['discarded_events'] = [ { reason: 'before_send', - category: 'event', + category: 'error', quantity: 30, }, { @@ -45,7 +45,7 @@ describe('createClientReportEnvelope', () => { expect(serializedEnv).toMatchInlineSnapshot(` "{\\"dsn\\":\\"https://public@example.com/1\\"} {\\"type\\":\\"client_report\\"} - {\\"timestamp\\":123456,\\"discarded_events\\":[{\\"reason\\":\\"before_send\\",\\"category\\":\\"event\\",\\"quantity\\":30},{\\"reason\\":\\"network_error\\",\\"category\\":\\"transaction\\",\\"quantity\\":23}]}" + {\\"timestamp\\":123456,\\"discarded_events\\":[{\\"reason\\":\\"before_send\\",\\"category\\":\\"error\\",\\"quantity\\":30},{\\"reason\\":\\"network_error\\",\\"category\\":\\"transaction\\",\\"quantity\\":23}]}" `); }); }); From ebc938a03d6efe7d0c4bbcb47714e84c9a566a9c Mon Sep 17 00:00:00 2001 From: Lukas Stracke Date: Thu, 28 Apr 2022 13:37:45 +0200 Subject: [PATCH 095/204] fix(serverless): Adjust v6 Lambda layer hotfix for v7 (#5006) - ensure that the `s/dist/cjs` change is applied to the .npmignore files added and to build-awslambda-layer.js - add or adjusts .npmignore files in all Sentry packages that are dependencies of @sentry/serverless therefore, the unnecessary source, test or config files that previously bloated the lambda layer zip are no longer present - improve the build-awslambda-layer.js script by improving documentation and changing variable names to be more expressive --- packages/core/.npmignore | 15 +++ packages/hub/.npmignore | 15 +++ packages/node/.npmignore | 7 ++ packages/serverless/.npmignore | 13 +++ .../scripts/build-awslambda-layer.js | 102 ++++++++++++------ packages/tracing/.npmignore | 7 ++ packages/types/.npmignore | 5 + packages/utils/.npmignore | 7 ++ 8 files changed, 138 insertions(+), 33 deletions(-) create mode 100644 packages/core/.npmignore create mode 100644 packages/hub/.npmignore create mode 100644 packages/serverless/.npmignore diff --git a/packages/core/.npmignore b/packages/core/.npmignore new file mode 100644 index 000000000000..329293958886 --- /dev/null +++ b/packages/core/.npmignore @@ -0,0 +1,15 @@ +# The paths in this file are specified so that they align with the file structure in `./build` after this file is copied +# into it by the prepack script `scripts/prepack.ts`. + +* + +!/cjs/**/* +!/esm/**/* +!/types/**/* + +# These paths are necessary for Node AWS Lambda layer creation +# This package is a (transitive) dependency of @sentry/serverless and thus it is pulled into +# the lambda layer zip file. +!/build/cjs/**/* +!/build/esm/**/* +!/build/types/**/* diff --git a/packages/hub/.npmignore b/packages/hub/.npmignore new file mode 100644 index 000000000000..329293958886 --- /dev/null +++ b/packages/hub/.npmignore @@ -0,0 +1,15 @@ +# The paths in this file are specified so that they align with the file structure in `./build` after this file is copied +# into it by the prepack script `scripts/prepack.ts`. + +* + +!/cjs/**/* +!/esm/**/* +!/types/**/* + +# These paths are necessary for Node AWS Lambda layer creation +# This package is a (transitive) dependency of @sentry/serverless and thus it is pulled into +# the lambda layer zip file. +!/build/cjs/**/* +!/build/esm/**/* +!/build/types/**/* diff --git a/packages/node/.npmignore b/packages/node/.npmignore index cb864514088e..79d2576ebbb5 100644 --- a/packages/node/.npmignore +++ b/packages/node/.npmignore @@ -6,3 +6,10 @@ !/cjs/**/* !/esm/**/* !/types/**/* + +# These paths are necessary for Node AWS Lambda creation +# This package is a (transitive) dependency of @sentry/serverless and thus it is pulled into +# the lambda layer zip file. By specifying these paths, we +!/build/cjs/**/* +!/build/esm/**/* +!/build/types/**/* diff --git a/packages/serverless/.npmignore b/packages/serverless/.npmignore new file mode 100644 index 000000000000..53b2a9d51a37 --- /dev/null +++ b/packages/serverless/.npmignore @@ -0,0 +1,13 @@ +# The paths in this file are specified so that they align with the file structure in `./build` after this file is copied +# into it by the prepack script `scripts/prepack.ts`. + +* + +!/cjs/**/* +!/esm/**/* +!/types/**/* + +# These paths are necessary for Node AWS Lambda layer creation +!/build/cjs/**/* +!/build/esm/**/* +!/build/types/**/* diff --git a/packages/serverless/scripts/build-awslambda-layer.js b/packages/serverless/scripts/build-awslambda-layer.js index dfea6a0c3b73..25be1107e203 100644 --- a/packages/serverless/scripts/build-awslambda-layer.js +++ b/packages/serverless/scripts/build-awslambda-layer.js @@ -8,7 +8,7 @@ const findUp = require('find-up'); const packList = require('npm-packlist'); const readPkg = require('read-pkg'); -const serverlessPackage = require('../package.json'); +const serverlessPackageJson = require('../package.json'); if (!process.env.GITHUB_ACTIONS) { console.log('Skipping build-awslambda-layer script in local environment.'); @@ -36,7 +36,14 @@ if (!process.env.GITHUB_ACTIONS) { // And yet another way is to bundle everything with webpack into a single file. I tried and it seems to be error-prone // so I think it's better to have a classic package directory with node_modules file structure. -/** Recursively traverse all the dependencies and collect all the info to the map */ +/** + * Recursively traverses all the dependencies of @param pkg and collects all the info to the map + * The map ultimately contains @sentry/serverless itself, its direct dependencies and + * its transitive dependencies. + * + * @param cwd the root directory of the package + * @param packages the map accumulating all packages + */ async function collectPackages(cwd, packages = {}) { const packageJson = await readPkg({ cwd }); @@ -68,67 +75,89 @@ async function collectPackages(cwd, packages = {}) { } async function main() { - const workDir = path.resolve(__dirname, '..'); // packages/serverless directory - const distRequirements = path.resolve(workDir, 'build', 'cjs'); - if (!fs.existsSync(distRequirements)) { - console.log(`The path ${distRequirements} must exist.`); + const serverlessDir = path.resolve(__dirname, '..'); // packages/serverless directory + + const cjsBuildDir = path.resolve(serverlessDir, 'build', 'cjs'); + if (!fs.existsSync(cjsBuildDir)) { + console.log(`The path ${cjsBuildDir} must exist.`); return; } - const packages = await collectPackages(workDir); - const dist = path.resolve(workDir, 'dist-awslambda-layer'); + const packages = await collectPackages(serverlessDir); + + // the build directory of the Lambda layer + const layerBuildDir = path.resolve(serverlessDir, 'dist-awslambda-layer'); + + // the root directory in which the Lambda layer files + dependencies are copied to + // this structure resembles the structure where Lambda expects to find @sentry/serverless const destRootRelative = 'nodejs/node_modules/@sentry/serverless'; - const destRoot = path.resolve(dist, destRootRelative); - const destModulesRoot = path.resolve(destRoot, 'node_modules'); + const destRootDir = path.resolve(layerBuildDir, destRootRelative); + + // this is where all the (transitive) dependencies of @sentry/serverless go + const destRootNodeModulesDir = path.resolve(destRootDir, 'node_modules'); try { // Setting `force: true` ignores exceptions when paths don't exist. - fs.rmSync(destRoot, { force: true, recursive: true, maxRetries: 1 }); - fs.mkdirSync(destRoot, { recursive: true }); + fs.rmSync(destRootDir, { force: true, recursive: true, maxRetries: 1 }); + fs.mkdirSync(destRootDir, { recursive: true }); } catch (error) { // Ignore errors. } await Promise.all( Object.entries(packages).map(async ([name, pkg]) => { - const isRoot = name == serverlessPackage.name; - const destPath = isRoot ? destRoot : path.resolve(destModulesRoot, name); - - // Scan over the distributable files of the module and symlink each of them. + const isServelessPkg = name == serverlessPackageJson.name; + const destDir = isServelessPkg ? destRootDir : path.resolve(destRootNodeModulesDir, name); + + // Scan over the "distributable" files of `pkg` and symlink all of them. + // `packList` returns all files it deems "distributable" from `pkg.cwd`. + // "Distributable" means in this case that the file would end up in the NPM tarball of `pkg`. + // To find out which files are distributable, packlist scans for NPM file configurations in the following order: + // 1. if `files` section present in package.json, take everything* from there + // 2. if `.npmignore` present, take everything* except what's ignored there + // 3. if `.gitignore` present, take everything* except what's ignored there + // 4. else take everything* + // In our case, rule 2 applies. + // * everything except certain unimportant files similarly to what `npm pack` does when packing a tarball. + // For more information on the rules see: https://github.com/npm/npm-packlist#readme const sourceFiles = await packList({ path: pkg.cwd }); + await Promise.all( sourceFiles.map(async filename => { - const sourceFilename = path.resolve(pkg.cwd, filename); - const destFilename = path.resolve(destPath, filename); + const sourceFilePath = path.resolve(pkg.cwd, filename); + const destFilePath = path.resolve(destDir, filename); try { - fs.mkdirSync(path.dirname(destFilename), { recursive: true }); - fs.symlinkSync(sourceFilename, destFilename); + fs.mkdirSync(path.dirname(destFilePath), { recursive: true }); + fs.symlinkSync(sourceFilePath, destFilePath); } catch (error) { // Ignore errors. } }), ); - const sourceModulesRoot = path.resolve(pkg.cwd, 'node_modules'); + // Now we deal with the `pkg`'s dependencies in its local `node_modules` directory + const pkgNodeModulesDir = path.resolve(pkg.cwd, 'node_modules'); + + // First, check if `pkg` has node modules. If not, we're done with this `pkg`. // `fs.constants.F_OK` indicates whether the file is visible to the current process, but it doesn't check // its permissions. For more information, refer to https://nodejs.org/api/fs.html#fs_file_access_constants. try { - fs.accessSync(path.resolve(sourceModulesRoot), fs.constants.F_OK); + fs.accessSync(path.resolve(pkgNodeModulesDir), fs.constants.F_OK); } catch (error) { return; } - // Scan over local node_modules folder of the package and symlink its non-dev dependencies. - const sourceModules = fs.readdirSync(sourceModulesRoot); + // Then, scan over local node_modules folder of `pkg` and symlink its non-dev dependencies. + const pkgNodeModules = fs.readdirSync(pkgNodeModulesDir); await Promise.all( - sourceModules.map(async sourceModule => { - if (!pkg.packageJson.dependencies || !pkg.packageJson.dependencies[sourceModule]) { + pkgNodeModules.map(async nodeModule => { + if (!pkg.packageJson.dependencies || !pkg.packageJson.dependencies[nodeModule]) { return; } - const sourceModulePath = path.resolve(sourceModulesRoot, sourceModule); - const destModulePath = path.resolve(destPath, 'node_modules', sourceModule); + const sourceModulePath = path.resolve(pkgNodeModulesDir, nodeModule); + const destModulePath = path.resolve(destDir, 'node_modules', nodeModule); try { fs.mkdirSync(path.dirname(destModulePath), { recursive: true }); @@ -141,25 +170,32 @@ async function main() { }), ); - const version = serverlessPackage.version; + const version = serverlessPackageJson.version; const zipFilename = `sentry-node-serverless-${version}.zip`; + // link from `./build/cjs` to `./dist` + // This needs to be done to satisfy the NODE_OPTIONS environment variable path that is set in + // AWS lambda functions when connecting them to Sentry. On initialization, the layer preloads a js + // file specified in NODE_OPTIONS to initialize the SDK. + // Hence we symlink everything from `.build/cjs` to `.dist`. + // This creates duplication but it's not too bad file size wise. try { - fs.symlinkSync(path.resolve(destRoot, 'build', 'dist'), path.resolve(destRoot, 'dist')); - fs.symlinkSync(path.resolve(destRoot, 'build', 'esm'), path.resolve(destRoot, 'esm')); + fs.symlinkSync(path.resolve(destRootDir, 'build', 'cjs'), path.resolve(destRootDir, 'dist')); } catch (error) { console.error(error); } + // remove previously created layer zip try { - fs.unlinkSync(path.resolve(dist, zipFilename)); + fs.unlinkSync(path.resolve(layerBuildDir, zipFilename)); } catch (error) { // If the ZIP file hasn't been previously created (e.g. running this script for the first time), // `unlinkSync` will try to delete a non-existing file. This error is ignored. } + // create new layer zip try { - childProcess.execSync(`zip -r ${zipFilename} ${destRootRelative}`, { cwd: dist }); + childProcess.execSync(`zip -r ${zipFilename} ${destRootRelative}`, { cwd: layerBuildDir }); } catch (error) { // The child process timed out or had non-zero exit code. // The error contains the entire result from `childProcess.spawnSync`. diff --git a/packages/tracing/.npmignore b/packages/tracing/.npmignore index cb864514088e..37cb0bef4d30 100644 --- a/packages/tracing/.npmignore +++ b/packages/tracing/.npmignore @@ -6,3 +6,10 @@ !/cjs/**/* !/esm/**/* !/types/**/* + +# These paths are necessary for Node AWS Lambda layer creation +# This package is a (transitive) dependency of @sentry/serverless and thus it is pulled into +# the lambda layer zip file. +!/build/npm/cjs/**/* +!/build/npm/esm/**/* +!/build/npm/types/**/* diff --git a/packages/types/.npmignore b/packages/types/.npmignore index cb864514088e..7e33b95686a8 100644 --- a/packages/types/.npmignore +++ b/packages/types/.npmignore @@ -6,3 +6,8 @@ !/cjs/**/* !/esm/**/* !/types/**/* + +# These paths are necessary for @sentry/serverless AWS Lambda Layer creation +!/build/cjs/**/* +!/build/esm/**/* +!/build/types/**/* diff --git a/packages/utils/.npmignore b/packages/utils/.npmignore index cb864514088e..329293958886 100644 --- a/packages/utils/.npmignore +++ b/packages/utils/.npmignore @@ -6,3 +6,10 @@ !/cjs/**/* !/esm/**/* !/types/**/* + +# These paths are necessary for Node AWS Lambda layer creation +# This package is a (transitive) dependency of @sentry/serverless and thus it is pulled into +# the lambda layer zip file. +!/build/cjs/**/* +!/build/esm/**/* +!/build/types/**/* From de2822b2c5747efe6b3b94695c8a656cfe4b204b Mon Sep 17 00:00:00 2001 From: Luca Forstner Date: Thu, 28 Apr 2022 16:03:22 +0200 Subject: [PATCH 096/204] feat(transport): Add client report hook to `makeTransport` (#5008) --- packages/browser/src/transports/fetch.ts | 2 +- packages/browser/src/transports/xhr.ts | 11 +- .../unit/helper/browser-client-options.ts | 2 +- .../test/unit/mocks/simpletransport.ts | 2 +- packages/browser/test/unit/sdk.test.ts | 2 +- .../test/unit/transports/fetch.test.ts | 1 + .../browser/test/unit/transports/xhr.test.ts | 1 + packages/core/src/baseclient.ts | 6 +- packages/core/src/transports/base.ts | 67 +++- .../core/test/lib/transports/base.test.ts | 332 ++++++++---------- packages/core/test/mocks/client.ts | 6 +- packages/core/test/mocks/transport.ts | 2 +- packages/node/src/transports/http.ts | 2 +- .../node/test/helper/node-client-options.ts | 2 +- .../manual/express-scope-separation/start.js | 4 +- .../aggregates-disable-single-session.js | 4 +- .../caught-exception-errored-session.js | 2 +- .../errors-in-session-capped-to-one.js | 2 +- .../single-session/healthy-session.js | 12 +- .../terminal-state-sessions-sent-once.js | 2 +- .../uncaught-exception-crashed-session.js | 4 +- .../unhandled-rejection-crashed-session.js | 2 +- .../node/test/manual/webpack-domain/index.js | 4 +- packages/node/test/transports/http.test.ts | 29 +- packages/node/test/transports/https.test.ts | 33 +- packages/tracing/test/testutils.ts | 2 +- packages/types/src/envelope.ts | 12 +- packages/types/src/index.ts | 2 + packages/types/src/transport.ts | 4 + packages/utils/src/envelope.ts | 34 +- packages/utils/src/ratelimit.ts | 2 +- packages/utils/test/envelope.test.ts | 28 +- 32 files changed, 348 insertions(+), 272 deletions(-) diff --git a/packages/browser/src/transports/fetch.ts b/packages/browser/src/transports/fetch.ts index f500f33b22d6..d6f44090abb9 100644 --- a/packages/browser/src/transports/fetch.ts +++ b/packages/browser/src/transports/fetch.ts @@ -30,5 +30,5 @@ export function makeFetchTransport( })); } - return createTransport({ bufferSize: options.bufferSize }, makeRequest); + return createTransport(options, makeRequest); } diff --git a/packages/browser/src/transports/xhr.ts b/packages/browser/src/transports/xhr.ts index a08d51cf5a9a..4c4982225b85 100644 --- a/packages/browser/src/transports/xhr.ts +++ b/packages/browser/src/transports/xhr.ts @@ -21,17 +21,20 @@ export interface XHRTransportOptions extends BaseTransportOptions { */ export function makeXHRTransport(options: XHRTransportOptions): Transport { function makeRequest(request: TransportRequest): PromiseLike { - return new SyncPromise((resolve, _reject) => { + return new SyncPromise((resolve, reject) => { const xhr = new XMLHttpRequest(); + xhr.onerror = reject; + xhr.onreadystatechange = (): void => { if (xhr.readyState === XHR_READYSTATE_DONE) { - resolve({ + const response = { headers: { 'x-sentry-rate-limits': xhr.getResponseHeader('X-Sentry-Rate-Limits'), 'retry-after': xhr.getResponseHeader('Retry-After'), }, - }); + }; + resolve(response); } }; @@ -47,5 +50,5 @@ export function makeXHRTransport(options: XHRTransportOptions): Transport { }); } - return createTransport({ bufferSize: options.bufferSize }, makeRequest); + return createTransport(options, makeRequest); } diff --git a/packages/browser/test/unit/helper/browser-client-options.ts b/packages/browser/test/unit/helper/browser-client-options.ts index eb4d78768b94..9bdaf2518d40 100644 --- a/packages/browser/test/unit/helper/browser-client-options.ts +++ b/packages/browser/test/unit/helper/browser-client-options.ts @@ -6,7 +6,7 @@ import { BrowserClientOptions } from '../../../src/client'; export function getDefaultBrowserClientOptions(options: Partial = {}): BrowserClientOptions { return { integrations: [], - transport: () => createTransport({}, _ => resolvedSyncPromise({})), + transport: () => createTransport({ recordDroppedEvent: () => undefined }, _ => resolvedSyncPromise({})), stackParser: () => [], ...options, }; diff --git a/packages/browser/test/unit/mocks/simpletransport.ts b/packages/browser/test/unit/mocks/simpletransport.ts index a9c2d5264ae6..cbad94fb310d 100644 --- a/packages/browser/test/unit/mocks/simpletransport.ts +++ b/packages/browser/test/unit/mocks/simpletransport.ts @@ -2,5 +2,5 @@ import { createTransport } from '@sentry/core'; import { resolvedSyncPromise } from '@sentry/utils'; export function makeSimpleTransport() { - return createTransport({}, () => resolvedSyncPromise({})); + return createTransport({ recordDroppedEvent: () => undefined }, () => resolvedSyncPromise({})); } diff --git a/packages/browser/test/unit/sdk.test.ts b/packages/browser/test/unit/sdk.test.ts index b59d380e8e76..a2df869fe90d 100644 --- a/packages/browser/test/unit/sdk.test.ts +++ b/packages/browser/test/unit/sdk.test.ts @@ -15,7 +15,7 @@ const PUBLIC_DSN = 'https://username@domain/123'; function getDefaultBrowserOptions(options: Partial = {}): BrowserOptions { return { integrations: [], - transport: () => createTransport({}, _ => resolvedSyncPromise({})), + transport: () => createTransport({ recordDroppedEvent: () => undefined }, _ => resolvedSyncPromise({})), stackParser: () => [], ...options, }; diff --git a/packages/browser/test/unit/transports/fetch.test.ts b/packages/browser/test/unit/transports/fetch.test.ts index 29b7b241c8dd..63fe62814628 100644 --- a/packages/browser/test/unit/transports/fetch.test.ts +++ b/packages/browser/test/unit/transports/fetch.test.ts @@ -6,6 +6,7 @@ import { FetchImpl } from '../../../src/transports/utils'; const DEFAULT_FETCH_TRANSPORT_OPTIONS: FetchTransportOptions = { url: 'https://sentry.io/api/42/store/?sentry_key=123&sentry_version=7', + recordDroppedEvent: () => undefined, }; const ERROR_ENVELOPE = createEnvelope({ event_id: 'aa3ff046696b4bc6b609ce6d28fde9e2', sent_at: '123' }, [ diff --git a/packages/browser/test/unit/transports/xhr.test.ts b/packages/browser/test/unit/transports/xhr.test.ts index ae144480de08..35ca842db3d1 100644 --- a/packages/browser/test/unit/transports/xhr.test.ts +++ b/packages/browser/test/unit/transports/xhr.test.ts @@ -5,6 +5,7 @@ import { makeXHRTransport, XHRTransportOptions } from '../../../src/transports/x const DEFAULT_XHR_TRANSPORT_OPTIONS: XHRTransportOptions = { url: 'https://sentry.io/api/42/store/?sentry_key=123&sentry_version=7', + recordDroppedEvent: () => undefined, }; const ERROR_ENVELOPE = createEnvelope({ event_id: 'aa3ff046696b4bc6b609ce6d28fde9e2', sent_at: '123' }, [ diff --git a/packages/core/src/baseclient.ts b/packages/core/src/baseclient.ts index 1969f2d8ef0a..c50403ab3903 100644 --- a/packages/core/src/baseclient.ts +++ b/packages/core/src/baseclient.ts @@ -97,7 +97,11 @@ export abstract class BaseClient implements Client { if (options.dsn) { this._dsn = makeDsn(options.dsn); const url = getEnvelopeEndpointWithUrlEncodedAuth(this._dsn, options.tunnel); - this._transport = options.transport({ ...options.transportOptions, url }); + this._transport = options.transport({ + recordDroppedEvent: () => undefined, // TODO(v7): Provide a proper function instead of noop + ...options.transportOptions, + url, + }); } else { IS_DEBUG_BUILD && logger.warn('No DSN provided, client will not do anything.'); } diff --git a/packages/core/src/transports/base.ts b/packages/core/src/transports/base.ts index 0491d7422f6b..4e6c04e8c516 100644 --- a/packages/core/src/transports/base.ts +++ b/packages/core/src/transports/base.ts @@ -1,21 +1,28 @@ import { - DataCategory, Envelope, + EnvelopeItem, + EventDropReason, InternalBaseTransportOptions, Transport, TransportRequestExecutor, } from '@sentry/types'; import { - getEnvelopeType, + createEnvelope, + envelopeItemTypeToDataCategory, + forEachEnvelopeItem, isRateLimited, + logger, makePromiseBuffer, PromiseBuffer, RateLimits, resolvedSyncPromise, + SentryError, serializeEnvelope, updateRateLimits, } from '@sentry/utils'; +import { IS_DEBUG_BUILD } from '../flags'; + export const DEFAULT_TRANSPORT_BUFFER_SIZE = 30; /** @@ -34,22 +41,58 @@ export function createTransport( const flush = (timeout?: number): PromiseLike => buffer.drain(timeout); function send(envelope: Envelope): PromiseLike { - const envCategory = getEnvelopeType(envelope); - const category = envCategory === 'event' ? 'error' : (envCategory as DataCategory); + const filteredEnvelopeItems: EnvelopeItem[] = []; + + // Drop rate limited items from envelope + forEachEnvelopeItem(envelope, (item, type) => { + const envelopeItemDataCategory = envelopeItemTypeToDataCategory(type); + if (isRateLimited(rateLimits, envelopeItemDataCategory)) { + options.recordDroppedEvent('ratelimit_backoff', envelopeItemDataCategory); + } else { + filteredEnvelopeItems.push(item); + } + }); - // Don't add to buffer if transport is already rate-limited - if (isRateLimited(rateLimits, category)) { + // Skip sending if envelope is empty after filtering out rate limited events + if (filteredEnvelopeItems.length === 0) { return resolvedSyncPromise(); } - const requestTask = (): PromiseLike => - makeRequest({ body: serializeEnvelope(envelope) }).then(({ headers }): void => { - if (headers) { - rateLimits = updateRateLimits(rateLimits, headers); - } + // eslint-disable-next-line @typescript-eslint/no-explicit-any + const filteredEnvelope: Envelope = createEnvelope(envelope[0], filteredEnvelopeItems as any); + + // Creates client report for each item in an envelope + const recordEnvelopeLoss = (reason: EventDropReason): void => { + forEachEnvelopeItem(filteredEnvelope, (_, type) => { + options.recordDroppedEvent(reason, envelopeItemTypeToDataCategory(type)); }); + }; + + const requestTask = (): PromiseLike => + makeRequest({ body: serializeEnvelope(filteredEnvelope) }).then( + ({ headers }): void => { + if (headers) { + rateLimits = updateRateLimits(rateLimits, headers); + } + }, + error => { + IS_DEBUG_BUILD && logger.error('Failed while recording event:', error); + recordEnvelopeLoss('network_error'); + }, + ); - return buffer.add(requestTask); + return buffer.add(requestTask).then( + result => result, + error => { + if (error instanceof SentryError) { + IS_DEBUG_BUILD && logger.error('Skipped sending event due to full buffer'); + recordEnvelopeLoss('queue_overflow'); + return resolvedSyncPromise(); + } else { + throw error; + } + }, + ); } return { diff --git a/packages/core/test/lib/transports/base.test.ts b/packages/core/test/lib/transports/base.test.ts index 39166a1a06c8..b57cc36f6e6f 100644 --- a/packages/core/test/lib/transports/base.test.ts +++ b/packages/core/test/lib/transports/base.test.ts @@ -1,4 +1,4 @@ -import { EventEnvelope, EventItem, Transport, TransportMakeRequestResponse } from '@sentry/types'; +import { EventEnvelope, EventItem, TransportMakeRequestResponse } from '@sentry/types'; import { createEnvelope, PromiseBuffer, resolvedSyncPromise, serializeEnvelope } from '@sentry/utils'; import { createTransport } from '../../../src/transports/base'; @@ -12,6 +12,10 @@ const TRANSACTION_ENVELOPE = createEnvelope( [[{ type: 'transaction' }, { event_id: 'aa3ff046696b4bc6b609ce6d28fde9e2' }] as EventItem], ); +const transportOptions = { + recordDroppedEvent: () => undefined, // noop +}; + describe('createTransport', () => { it('flushes the buffer', async () => { const mockBuffer: PromiseBuffer = { @@ -19,7 +23,7 @@ describe('createTransport', () => { add: jest.fn(), drain: jest.fn(), }; - const transport = createTransport({}, _ => resolvedSyncPromise({}), mockBuffer); + const transport = createTransport(transportOptions, _ => resolvedSyncPromise({}), mockBuffer); /* eslint-disable @typescript-eslint/unbound-method */ expect(mockBuffer.drain).toHaveBeenCalledTimes(0); await transport.flush(1000); @@ -31,7 +35,7 @@ describe('createTransport', () => { describe('send', () => { it('constructs a request to send to Sentry', async () => { expect.assertions(1); - const transport = createTransport({}, req => { + const transport = createTransport(transportOptions, req => { expect(req.body).toEqual(serializeEnvelope(ERROR_ENVELOPE)); return resolvedSyncPromise({}); }); @@ -41,7 +45,7 @@ describe('createTransport', () => { it('does throw if request fails', async () => { expect.assertions(2); - const transport = createTransport({}, req => { + const transport = createTransport(transportOptions, req => { expect(req.body).toEqual(serializeEnvelope(ERROR_ENVELOPE)); throw new Error(); }); @@ -51,8 +55,7 @@ describe('createTransport', () => { }).toThrow(); }); - // TODO(v7): Add tests back in and test by using client report logic - describe.skip('Rate-limiting', () => { + describe('Rate-limiting', () => { function setRateLimitTimes(): { retryAfterSeconds: number; beforeLimit: number; @@ -66,265 +69,208 @@ describe('createTransport', () => { return { retryAfterSeconds, beforeLimit, withinLimit, afterLimit }; } - function createTestTransport( - initialTransportResponse: TransportMakeRequestResponse, - ): [Transport, (res: TransportMakeRequestResponse) => void] { + function createTestTransport(initialTransportResponse: TransportMakeRequestResponse) { let transportResponse: TransportMakeRequestResponse = initialTransportResponse; function setTransportResponse(res: TransportMakeRequestResponse) { transportResponse = res; } - const transport = createTransport({}, _ => { + const mockRequestExecutor = jest.fn(_ => { return resolvedSyncPromise(transportResponse); }); - return [transport, setTransportResponse]; + const mockRecordDroppedEventCallback = jest.fn(); + + const transport = createTransport({ recordDroppedEvent: mockRecordDroppedEventCallback }, mockRequestExecutor); + + return [transport, setTransportResponse, mockRequestExecutor, mockRecordDroppedEventCallback] as const; } - it('back-off using Retry-After header', async () => { + it('back-off _after_ Retry-After header was received', async () => { const { retryAfterSeconds, beforeLimit, withinLimit, afterLimit } = setRateLimitTimes(); + const [transport, setTransportResponse, requestExecutor, recordDroppedEventCallback] = createTestTransport({}); + + const dateNowSpy = jest.spyOn(Date, 'now').mockImplementation(() => beforeLimit); - jest - .spyOn(Date, 'now') - // 1st event - isRateLimited - FALSE - .mockImplementationOnce(() => beforeLimit) - // 1st event - updateRateLimits - .mockImplementationOnce(() => beforeLimit) - // 2nd event - isRateLimited - TRUE - .mockImplementationOnce(() => withinLimit) - // 3rd event - isRateLimited - FALSE - .mockImplementationOnce(() => afterLimit) - // 3rd event - updateRateLimits - .mockImplementationOnce(() => afterLimit); - - const [transport, setTransportResponse] = createTestTransport({ + await transport.send(ERROR_ENVELOPE); + expect(requestExecutor).toHaveBeenCalledTimes(1); + expect(recordDroppedEventCallback).not.toHaveBeenCalled(); + requestExecutor.mockClear(); + recordDroppedEventCallback.mockClear(); + + setTransportResponse({ headers: { 'x-sentry-rate-limits': null, 'retry-after': `${retryAfterSeconds}`, }, }); - try { - await transport.send(ERROR_ENVELOPE); - } catch (res) { - expect(res.status).toBe('rate_limit'); - expect(res.reason).toBe(`Too many error requests, backing off until: ${new Date(afterLimit).toISOString()}`); - } + await transport.send(ERROR_ENVELOPE); + expect(requestExecutor).toHaveBeenCalledTimes(1); + expect(recordDroppedEventCallback).not.toHaveBeenCalled(); + requestExecutor.mockClear(); + recordDroppedEventCallback.mockClear(); - setTransportResponse({}); + // act like were in the rate limited period + dateNowSpy.mockImplementation(() => withinLimit); - try { - await transport.send(ERROR_ENVELOPE); - } catch (res) { - expect(res.status).toBe('rate_limit'); - expect(res.reason).toBe(`Too many error requests, backing off until: ${new Date(afterLimit).toISOString()}`); - } + await transport.send(ERROR_ENVELOPE); + expect(requestExecutor).not.toHaveBeenCalled(); + expect(recordDroppedEventCallback).toHaveBeenCalledWith('ratelimit_backoff', 'error'); + requestExecutor.mockClear(); + recordDroppedEventCallback.mockClear(); + + // act like it's after the rate limited period + dateNowSpy.mockImplementation(() => afterLimit); await transport.send(ERROR_ENVELOPE); + expect(requestExecutor).toHaveBeenCalledTimes(1); + expect(recordDroppedEventCallback).not.toHaveBeenCalled(); }); it('back-off using X-Sentry-Rate-Limits with single category', async () => { const { retryAfterSeconds, beforeLimit, withinLimit, afterLimit } = setRateLimitTimes(); - - jest - .spyOn(Date, 'now') - // 1st event - isRateLimited - FALSE - .mockImplementationOnce(() => beforeLimit) - // 1st event - updateRateLimits - .mockImplementationOnce(() => beforeLimit) - // 2nd event - isRateLimited - FALSE (different category) - .mockImplementationOnce(() => withinLimit) - // 3rd event - isRateLimited - TRUE - .mockImplementationOnce(() => withinLimit) - // 4th event - isRateLimited - FALSE - .mockImplementationOnce(() => afterLimit) - // 4th event - updateRateLimits - .mockImplementationOnce(() => afterLimit); - - const [transport, setTransportResponse] = createTestTransport({ + const [transport, setTransportResponse, requestExecutor, recordDroppedEventCallback] = createTestTransport({ headers: { 'x-sentry-rate-limits': `${retryAfterSeconds}:error:scope`, 'retry-after': null, }, }); - try { - await transport.send(ERROR_ENVELOPE); - } catch (res) { - expect(res.status).toBe('rate_limit'); - expect(res.reason).toBe(`Too many error requests, backing off until: ${new Date(afterLimit).toISOString()}`); - } + const dateNowSpy = jest.spyOn(Date, 'now').mockImplementation(() => beforeLimit); + + await transport.send(ERROR_ENVELOPE); + expect(requestExecutor).toHaveBeenCalledTimes(1); + expect(recordDroppedEventCallback).not.toHaveBeenCalled(); + requestExecutor.mockClear(); + recordDroppedEventCallback.mockClear(); setTransportResponse({}); - try { - await transport.send(TRANSACTION_ENVELOPE); - } catch (res) { - expect(res.status).toBe('rate_limit'); - expect(res.reason).toBe(`Too many error requests, backing off until: ${new Date(afterLimit).toISOString()}`); - } + // act like were in the rate limited period + dateNowSpy.mockImplementation(() => withinLimit); - try { - await transport.send(ERROR_ENVELOPE); - } catch (res) { - expect(res.status).toBe('rate_limit'); - expect(res.reason).toBe(`Too many error requests, backing off until: ${new Date(afterLimit).toISOString()}`); - } + await transport.send(TRANSACTION_ENVELOPE); // Transaction envelope should be sent + expect(requestExecutor).toHaveBeenCalledTimes(1); + expect(recordDroppedEventCallback).not.toHaveBeenCalled(); + requestExecutor.mockClear(); + recordDroppedEventCallback.mockClear(); + + await transport.send(ERROR_ENVELOPE); // Error envelope should not be sent because of pending rate limit + expect(requestExecutor).not.toHaveBeenCalled(); + expect(recordDroppedEventCallback).toHaveBeenCalledWith('ratelimit_backoff', 'error'); + requestExecutor.mockClear(); + recordDroppedEventCallback.mockClear(); + + // act like it's after the rate limited period + dateNowSpy.mockImplementation(() => afterLimit); await transport.send(TRANSACTION_ENVELOPE); + expect(requestExecutor).toHaveBeenCalledTimes(1); + expect(recordDroppedEventCallback).not.toHaveBeenCalled(); + requestExecutor.mockClear(); + recordDroppedEventCallback.mockClear(); + + await transport.send(ERROR_ENVELOPE); + expect(requestExecutor).toHaveBeenCalledTimes(1); + expect(recordDroppedEventCallback).not.toHaveBeenCalled(); }); it('back-off using X-Sentry-Rate-Limits with multiple categories', async () => { const { retryAfterSeconds, beforeLimit, withinLimit, afterLimit } = setRateLimitTimes(); - - jest - .spyOn(Date, 'now') - // 1st event - isRateLimited - FALSE - .mockImplementationOnce(() => beforeLimit) - // 1st event - updateRateLimits - .mockImplementationOnce(() => beforeLimit) - // 2nd event - isRateLimited - TRUE (different category) - .mockImplementationOnce(() => withinLimit) - // 3rd event - isRateLimited - TRUE - .mockImplementationOnce(() => withinLimit) - // 4th event - isRateLimited - FALSE - .mockImplementationOnce(() => afterLimit) - // 4th event - updateRateLimits - .mockImplementationOnce(() => afterLimit); - - const [transport, setTransportResponse] = createTestTransport({ + const [transport, setTransportResponse, requestExecutor, recordDroppedEventCallback] = createTestTransport({ headers: { 'x-sentry-rate-limits': `${retryAfterSeconds}:error;transaction:scope`, 'retry-after': null, }, }); - try { - await transport.send(ERROR_ENVELOPE); - } catch (res) { - expect(res.status).toBe('rate_limit'); - expect(res.reason).toBe(`Too many error requests, backing off until: ${new Date(afterLimit).toISOString()}`); - } + const dateNowSpy = jest.spyOn(Date, 'now').mockImplementation(() => beforeLimit); - try { - await transport.send(ERROR_ENVELOPE); - } catch (res) { - expect(res.status).toBe('rate_limit'); - expect(res.reason).toBe(`Too many error requests, backing off until: ${new Date(afterLimit).toISOString()}`); - } - - try { - await transport.send(TRANSACTION_ENVELOPE); - } catch (res) { - expect(res.status).toBe('rate_limit'); - expect(res.reason).toBe( - `Too many transaction requests, backing off until: ${new Date(afterLimit).toISOString()}`, - ); - } + await transport.send(ERROR_ENVELOPE); + expect(requestExecutor).toHaveBeenCalledTimes(1); + expect(recordDroppedEventCallback).not.toHaveBeenCalled(); + requestExecutor.mockClear(); + recordDroppedEventCallback.mockClear(); setTransportResponse({}); - await transport.send(ERROR_ENVELOPE); + // act like were in the rate limited period + dateNowSpy.mockImplementation(() => withinLimit); + + await transport.send(TRANSACTION_ENVELOPE); // Transaction envelope should not be sent because of pending rate limit + expect(requestExecutor).not.toHaveBeenCalled(); + expect(recordDroppedEventCallback).toHaveBeenCalledWith('ratelimit_backoff', 'transaction'); + requestExecutor.mockClear(); + recordDroppedEventCallback.mockClear(); + + await transport.send(ERROR_ENVELOPE); // Error envelope should not be sent because of pending rate limit + expect(requestExecutor).not.toHaveBeenCalled(); + expect(recordDroppedEventCallback).toHaveBeenCalledWith('ratelimit_backoff', 'error'); + requestExecutor.mockClear(); + recordDroppedEventCallback.mockClear(); + + // act like it's after the rate limited period + dateNowSpy.mockImplementation(() => afterLimit); + await transport.send(TRANSACTION_ENVELOPE); + expect(requestExecutor).toHaveBeenCalledTimes(1); + expect(recordDroppedEventCallback).not.toHaveBeenCalled(); + requestExecutor.mockClear(); + recordDroppedEventCallback.mockClear(); + + await transport.send(ERROR_ENVELOPE); + expect(requestExecutor).toHaveBeenCalledTimes(1); + expect(recordDroppedEventCallback).not.toHaveBeenCalled(); }); it('back-off using X-Sentry-Rate-Limits with missing categories should lock them all', async () => { const { retryAfterSeconds, beforeLimit, withinLimit, afterLimit } = setRateLimitTimes(); - - jest - .spyOn(Date, 'now') - // 1st event - isRateLimited - false - .mockImplementationOnce(() => beforeLimit) - // 1st event - updateRateLimits - .mockImplementationOnce(() => beforeLimit) - // 2nd event - isRateLimited - true (event category) - .mockImplementationOnce(() => withinLimit) - // 3rd event - isRateLimited - true (transaction category) - .mockImplementationOnce(() => withinLimit) - // 4th event - isRateLimited - false (event category) - .mockImplementationOnce(() => afterLimit) - // 4th event - updateRateLimits - .mockImplementationOnce(() => afterLimit) - // 5th event - isRateLimited - false (transaction category) - .mockImplementationOnce(() => afterLimit) - // 5th event - updateRateLimits - .mockImplementationOnce(() => afterLimit); - - const [transport, setTransportResponse] = createTestTransport({ + const [transport, setTransportResponse, requestExecutor, recordDroppedEventCallback] = createTestTransport({ headers: { 'x-sentry-rate-limits': `${retryAfterSeconds}::scope`, 'retry-after': null, }, }); - try { - await transport.send(ERROR_ENVELOPE); - } catch (res) { - expect(res.status).toBe('rate_limit'); - expect(res.reason).toBe(`Too many error requests, backing off until: ${new Date(afterLimit).toISOString()}`); - } - - try { - await transport.send(ERROR_ENVELOPE); - } catch (res) { - expect(res.status).toBe('rate_limit'); - expect(res.reason).toBe(`Too many error requests, backing off until: ${new Date(afterLimit).toISOString()}`); - } + const dateNowSpy = jest.spyOn(Date, 'now').mockImplementation(() => beforeLimit); - try { - await transport.send(TRANSACTION_ENVELOPE); - } catch (res) { - expect(res.status).toBe('rate_limit'); - expect(res.reason).toBe( - `Too many transaction requests, backing off until: ${new Date(afterLimit).toISOString()}`, - ); - } + await transport.send(ERROR_ENVELOPE); + expect(requestExecutor).toHaveBeenCalledTimes(1); + expect(recordDroppedEventCallback).not.toHaveBeenCalled(); + requestExecutor.mockClear(); + recordDroppedEventCallback.mockClear(); setTransportResponse({}); - await transport.send(ERROR_ENVELOPE); - await transport.send(TRANSACTION_ENVELOPE); - }); + // act like were in the rate limited period + dateNowSpy.mockImplementation(() => withinLimit); - it('back-off using X-Sentry-Rate-Limits should also trigger for 200 responses', async () => { - const { retryAfterSeconds, beforeLimit, withinLimit, afterLimit } = setRateLimitTimes(); + await transport.send(TRANSACTION_ENVELOPE); // Transaction envelope should not be sent because of pending rate limit + expect(requestExecutor).not.toHaveBeenCalled(); + expect(recordDroppedEventCallback).toHaveBeenCalledWith('ratelimit_backoff', 'transaction'); + requestExecutor.mockClear(); + recordDroppedEventCallback.mockClear(); - jest - .spyOn(Date, 'now') - // 1st event - isRateLimited - FALSE - .mockImplementationOnce(() => beforeLimit) - // 1st event - updateRateLimits - .mockImplementationOnce(() => beforeLimit) - // 2nd event - isRateLimited - TRUE - .mockImplementationOnce(() => withinLimit) - // 3rd event - isRateLimited - FALSE - .mockImplementationOnce(() => afterLimit) - // 3rd event - updateRateLimits - .mockImplementationOnce(() => afterLimit); - - const [transport] = createTestTransport({ - headers: { - 'x-sentry-rate-limits': `${retryAfterSeconds}:error;transaction:scope`, - 'retry-after': null, - }, - }); + await transport.send(ERROR_ENVELOPE); // Error envelope should not be sent because of pending rate limit + expect(requestExecutor).not.toHaveBeenCalled(); + expect(recordDroppedEventCallback).toHaveBeenCalledWith('ratelimit_backoff', 'error'); + requestExecutor.mockClear(); + recordDroppedEventCallback.mockClear(); - try { - await transport.send(ERROR_ENVELOPE); - } catch (res) { - expect(res.status).toBe('rate_limit'); - expect(res.reason).toBe(`Too many error requests, backing off until: ${new Date(afterLimit).toISOString()}`); - } + // act like it's after the rate limited period + dateNowSpy.mockImplementation(() => afterLimit); - try { - await transport.send(TRANSACTION_ENVELOPE); - } catch (res) { - expect(res.status).toBe('rate_limit'); - expect(res.reason).toBe( - `Too many transaction requests, backing off until: ${new Date(afterLimit).toISOString()}`, - ); - } + await transport.send(TRANSACTION_ENVELOPE); + expect(requestExecutor).toHaveBeenCalledTimes(1); + expect(recordDroppedEventCallback).not.toHaveBeenCalled(); + requestExecutor.mockClear(); + recordDroppedEventCallback.mockClear(); + + await transport.send(ERROR_ENVELOPE); + expect(requestExecutor).toHaveBeenCalledTimes(1); + expect(recordDroppedEventCallback).not.toHaveBeenCalled(); }); }); }); diff --git a/packages/core/test/mocks/client.ts b/packages/core/test/mocks/client.ts index 737abb7be476..6878a649bf17 100644 --- a/packages/core/test/mocks/client.ts +++ b/packages/core/test/mocks/client.ts @@ -13,7 +13,11 @@ import { createTransport } from '../../src/transports/base'; export function getDefaultTestClientOptions(options: Partial = {}): TestClientOptions { return { integrations: [], - transport: () => createTransport({}, _ => resolvedSyncPromise({})), + transport: () => + createTransport( + { recordDroppedEvent: () => undefined }, // noop + _ => resolvedSyncPromise({}), + ), stackParser: () => [], ...options, }; diff --git a/packages/core/test/mocks/transport.ts b/packages/core/test/mocks/transport.ts index 64b23e1cfd90..f59e72a516a1 100644 --- a/packages/core/test/mocks/transport.ts +++ b/packages/core/test/mocks/transport.ts @@ -10,7 +10,7 @@ export function makeFakeTransport(delay: number = 2000) { let sendCalled = 0; let sentCount = 0; const makeTransport = () => - createTransport({}, () => { + createTransport({ recordDroppedEvent: () => undefined }, () => { sendCalled += 1; return new SyncPromise(async res => { await sleep(delay); diff --git a/packages/node/src/transports/http.ts b/packages/node/src/transports/http.ts index 1ba591c54379..0916b9f0b60a 100644 --- a/packages/node/src/transports/http.ts +++ b/packages/node/src/transports/http.ts @@ -46,7 +46,7 @@ export function makeNodeTransport(options: NodeTransportOptions): Transport { : new nativeHttpModule.Agent({ keepAlive: false, maxSockets: 30, timeout: 2000 }); const requestExecutor = createRequestExecutor(options, options.httpModule ?? nativeHttpModule, agent); - return createTransport({ bufferSize: options.bufferSize }, requestExecutor); + return createTransport(options, requestExecutor); } /** diff --git a/packages/node/test/helper/node-client-options.ts b/packages/node/test/helper/node-client-options.ts index fc31b11738fd..8831af6a2896 100644 --- a/packages/node/test/helper/node-client-options.ts +++ b/packages/node/test/helper/node-client-options.ts @@ -6,7 +6,7 @@ import { NodeClientOptions } from '../../src/types'; export function getDefaultNodeClientOptions(options: Partial = {}): NodeClientOptions { return { integrations: [], - transport: () => createTransport({}, _ => resolvedSyncPromise({})), + transport: () => createTransport({ recordDroppedEvent: () => undefined }, _ => resolvedSyncPromise({})), stackParser: () => [], ...options, }; diff --git a/packages/node/test/manual/express-scope-separation/start.js b/packages/node/test/manual/express-scope-separation/start.js index 37733cf142a5..faf7b381ecbc 100644 --- a/packages/node/test/manual/express-scope-separation/start.js +++ b/packages/node/test/manual/express-scope-separation/start.js @@ -16,7 +16,7 @@ function assertTags(actual, expected) { let remaining = 3; function makeDummyTransport() { - return Sentry.createTransport({}, req => { + return Sentry.createTransport({ recordDroppedEvent: () => undefined }, req => { --remaining; if (!remaining) { @@ -28,7 +28,7 @@ function makeDummyTransport() { return Promise.resolve({ statusCode: 200, }); - }) + }); } Sentry.init({ diff --git a/packages/node/test/manual/release-health/session-aggregates/aggregates-disable-single-session.js b/packages/node/test/manual/release-health/session-aggregates/aggregates-disable-single-session.js index f02788174ea0..ae6a660607b1 100644 --- a/packages/node/test/manual/release-health/session-aggregates/aggregates-disable-single-session.js +++ b/packages/node/test/manual/release-health/session-aggregates/aggregates-disable-single-session.js @@ -28,7 +28,7 @@ function assertSessionAggregates(session, expected) { } function makeDummyTransport() { - return Sentry.createTransport({}, req => { + return Sentry.createTransport({ recordDroppedEvent: () => undefined }, req => { const sessionEnv = req.body.split('\n').map(e => JSON.parse(e)); assertSessionAggregates(sessionEnv[2], { attrs: { release: '1.1' }, @@ -40,7 +40,7 @@ function makeDummyTransport() { return Promise.resolve({ statusCode: 200, }); - }) + }); } Sentry.init({ diff --git a/packages/node/test/manual/release-health/single-session/caught-exception-errored-session.js b/packages/node/test/manual/release-health/single-session/caught-exception-errored-session.js index 686a3eb264ed..b7a9538a3fa2 100644 --- a/packages/node/test/manual/release-health/single-session/caught-exception-errored-session.js +++ b/packages/node/test/manual/release-health/single-session/caught-exception-errored-session.js @@ -9,7 +9,7 @@ const sessionCounts = { validateSessionCountFunction(sessionCounts); function makeDummyTransport() { - return Sentry.createTransport({}, req => { + return Sentry.createTransport({ recordDroppedEvent: () => undefined }, req => { const payload = req.body.split('\n').map(e => JSON.parse(e)); const isSessionPayload = payload[1].type === 'session'; diff --git a/packages/node/test/manual/release-health/single-session/errors-in-session-capped-to-one.js b/packages/node/test/manual/release-health/single-session/errors-in-session-capped-to-one.js index d550614201b9..dae307182ed1 100644 --- a/packages/node/test/manual/release-health/single-session/errors-in-session-capped-to-one.js +++ b/packages/node/test/manual/release-health/single-session/errors-in-session-capped-to-one.js @@ -9,7 +9,7 @@ const sessionCounts = { validateSessionCountFunction(sessionCounts); function makeDummyTransport() { - return Sentry.createTransport({}, req => { + return Sentry.createTransport({ recordDroppedEvent: () => undefined }, req => { const payload = req.body.split('\n').map(e => JSON.parse(e)); const isSessionPayload = payload[1].type === 'session'; diff --git a/packages/node/test/manual/release-health/single-session/healthy-session.js b/packages/node/test/manual/release-health/single-session/healthy-session.js index cfc3909449ef..0533b8a28728 100644 --- a/packages/node/test/manual/release-health/single-session/healthy-session.js +++ b/packages/node/test/manual/release-health/single-session/healthy-session.js @@ -1,9 +1,5 @@ const Sentry = require('../../../../build/cjs'); -const { - assertSessions, - constructStrippedSessionObject, - validateSessionCountFunction, -} = require('../test-utils'); +const { assertSessions, constructStrippedSessionObject, validateSessionCountFunction } = require('../test-utils'); const sessionCounts = { sessionCounter: 0, @@ -13,7 +9,7 @@ const sessionCounts = { validateSessionCountFunction(sessionCounts); function makeDummyTransport() { - return Sentry.createTransport({}, req => { + return Sentry.createTransport({ recordDroppedEvent: () => undefined }, req => { sessionCounts.sessionCounter++; const sessionEnv = req.body.split('\n').map(e => JSON.parse(e)); @@ -21,13 +17,13 @@ function makeDummyTransport() { init: true, status: 'exited', errors: 0, - release: '1.1' + release: '1.1', }); return Promise.resolve({ statusCode: 200, }); - }) + }); } Sentry.init({ diff --git a/packages/node/test/manual/release-health/single-session/terminal-state-sessions-sent-once.js b/packages/node/test/manual/release-health/single-session/terminal-state-sessions-sent-once.js index 6eccd3e21dba..aa0796782c2f 100644 --- a/packages/node/test/manual/release-health/single-session/terminal-state-sessions-sent-once.js +++ b/packages/node/test/manual/release-health/single-session/terminal-state-sessions-sent-once.js @@ -9,7 +9,7 @@ const sessionCounts = { validateSessionCountFunction(sessionCounts); function makeDummyTransport() { - return Sentry.createTransport({}, req => { + return Sentry.createTransport({ recordDroppedEvent: () => undefined }, req => { const payload = req.body.split('\n').map(e => JSON.parse(e)); const isSessionPayload = payload[1].type === 'session'; diff --git a/packages/node/test/manual/release-health/single-session/uncaught-exception-crashed-session.js b/packages/node/test/manual/release-health/single-session/uncaught-exception-crashed-session.js index 5cee4449c23a..6fa3ac0a6821 100644 --- a/packages/node/test/manual/release-health/single-session/uncaught-exception-crashed-session.js +++ b/packages/node/test/manual/release-health/single-session/uncaught-exception-crashed-session.js @@ -2,7 +2,7 @@ const Sentry = require('../../../../build/cjs'); const { assertSessions, constructStrippedSessionObject } = require('../test-utils'); function makeDummyTransport() { - return Sentry.createTransport({}, req => { + return Sentry.createTransport({ recordDroppedEvent: () => undefined }, req => { if (req.category === 'session') { sessionCounts.sessionCounter++; const sessionEnv = req.body.split('\n').map(e => JSON.parse(e)); @@ -17,7 +17,7 @@ function makeDummyTransport() { // We need to explicitly exit process early here to allow for 0 exit code process.exit(0); - }) + }); } Sentry.init({ diff --git a/packages/node/test/manual/release-health/single-session/unhandled-rejection-crashed-session.js b/packages/node/test/manual/release-health/single-session/unhandled-rejection-crashed-session.js index 0024ee16f798..b550260b62b7 100644 --- a/packages/node/test/manual/release-health/single-session/unhandled-rejection-crashed-session.js +++ b/packages/node/test/manual/release-health/single-session/unhandled-rejection-crashed-session.js @@ -9,7 +9,7 @@ const sessionCounts = { validateSessionCountFunction(sessionCounts); function makeDummyTransport() { - return Sentry.createTransport({}, req => { + return Sentry.createTransport({ recordDroppedEvent: () => undefined }, req => { const payload = req.body.split('\n').map(e => JSON.parse(e)); const isSessionPayload = payload[1].type === 'session'; diff --git a/packages/node/test/manual/webpack-domain/index.js b/packages/node/test/manual/webpack-domain/index.js index b970c95d2a98..09ed18f3166f 100644 --- a/packages/node/test/manual/webpack-domain/index.js +++ b/packages/node/test/manual/webpack-domain/index.js @@ -3,7 +3,7 @@ const Sentry = require('../../../build/cjs'); let remaining = 2; function makeDummyTransport() { - return Sentry.createTransport({}, req => { + return Sentry.createTransport({ recordDroppedEvent: () => undefined }, req => { --remaining; if (!remaining) { @@ -14,7 +14,7 @@ function makeDummyTransport() { return Promise.resolve({ status: 'success', }); - }) + }); } Sentry.init({ diff --git a/packages/node/test/transports/http.test.ts b/packages/node/test/transports/http.test.ts index 909532d5a3b8..013468c3eb5d 100644 --- a/packages/node/test/transports/http.test.ts +++ b/packages/node/test/transports/http.test.ts @@ -68,6 +68,11 @@ const EVENT_ENVELOPE = createEnvelope({ event_id: 'aa3ff046696b4b const SERIALIZED_EVENT_ENVELOPE = serializeEnvelope(EVENT_ENVELOPE); +const defaultOptions = { + url: TEST_SERVER_URL, + recordDroppedEvent: () => undefined, +}; + describe('makeNewHttpTransport()', () => { afterEach(() => { jest.clearAllMocks(); @@ -84,7 +89,7 @@ describe('makeNewHttpTransport()', () => { expect(body).toBe(SERIALIZED_EVENT_ENVELOPE); }); - const transport = makeNodeTransport({ url: TEST_SERVER_URL }); + const transport = makeNodeTransport(defaultOptions); await transport.send(EVENT_ENVELOPE); }); @@ -100,7 +105,7 @@ describe('makeNewHttpTransport()', () => { }); const transport = makeNodeTransport({ - url: TEST_SERVER_URL, + ...defaultOptions, headers: { 'X-Some-Custom-Header-1': 'value1', 'X-Some-Custom-Header-2': 'value2', @@ -115,7 +120,7 @@ describe('makeNewHttpTransport()', () => { async serverStatusCode => { await setupTestServer({ statusCode: serverStatusCode }); - const transport = makeNodeTransport({ url: TEST_SERVER_URL }); + const transport = makeNodeTransport(defaultOptions); await expect(transport.send(EVENT_ENVELOPE)).resolves.toBeUndefined(); }, @@ -130,7 +135,7 @@ describe('makeNewHttpTransport()', () => { }, }); - const transport = makeNodeTransport({ url: TEST_SERVER_URL }); + const transport = makeNodeTransport(defaultOptions); await expect(transport.send(EVENT_ENVELOPE)).resolves.toBeUndefined(); }); @@ -143,7 +148,7 @@ describe('makeNewHttpTransport()', () => { }, }); - const transport = makeNodeTransport({ url: TEST_SERVER_URL }); + const transport = makeNodeTransport(defaultOptions); await transport.send(EVENT_ENVELOPE); }); }); @@ -151,6 +156,7 @@ describe('makeNewHttpTransport()', () => { describe('proxy', () => { it('can be configured through option', () => { makeNodeTransport({ + ...defaultOptions, url: 'http://9e9fd4523d784609a5fc0ebb1080592f@sentry.io:8989/mysubpath/50622', proxy: 'http://example.com', }); @@ -162,6 +168,7 @@ describe('makeNewHttpTransport()', () => { it('can be configured through env variables option', () => { process.env.http_proxy = 'http://example.com'; makeNodeTransport({ + ...defaultOptions, url: 'http://9e9fd4523d784609a5fc0ebb1080592f@sentry.io:8989/mysubpath/50622', }); @@ -173,6 +180,7 @@ describe('makeNewHttpTransport()', () => { it('client options have priority over env variables', () => { process.env.http_proxy = 'http://foo.com'; makeNodeTransport({ + ...defaultOptions, url: 'http://9e9fd4523d784609a5fc0ebb1080592f@sentry.io:8989/mysubpath/50622', proxy: 'http://bar.com', }); @@ -185,6 +193,7 @@ describe('makeNewHttpTransport()', () => { it('no_proxy allows for skipping specific hosts', () => { process.env.no_proxy = 'sentry.io'; makeNodeTransport({ + ...defaultOptions, url: 'http://9e9fd4523d784609a5fc0ebb1080592f@sentry.io:8989/mysubpath/50622', proxy: 'http://example.com', }); @@ -199,6 +208,7 @@ describe('makeNewHttpTransport()', () => { process.env.no_proxy = 'sentry.io:8989'; makeNodeTransport({ + ...defaultOptions, url: 'http://9e9fd4523d784609a5fc0ebb1080592f@sentry.io:8989/mysubpath/50622', }); @@ -213,6 +223,7 @@ describe('makeNewHttpTransport()', () => { process.env.no_proxy = 'example.com,sentry.io,wat.com:1337'; makeNodeTransport({ + ...defaultOptions, url: 'http://9e9fd4523d784609a5fc0ebb1080592f@sentry.io:8989/mysubpath/50622', }); @@ -232,7 +243,7 @@ describe('makeNewHttpTransport()', () => { }, }); - makeNodeTransport({ url: TEST_SERVER_URL }); + makeNodeTransport(defaultOptions); const registeredRequestExecutor = (createTransport as jest.Mock).mock.calls[0][1]; const executorResult = registeredRequestExecutor({ @@ -255,7 +266,7 @@ describe('makeNewHttpTransport()', () => { statusCode: SUCCESS, }); - makeNodeTransport({ url: TEST_SERVER_URL }); + makeNodeTransport(defaultOptions); const registeredRequestExecutor = (createTransport as jest.Mock).mock.calls[0][1]; const executorResult = registeredRequestExecutor({ @@ -282,7 +293,7 @@ describe('makeNewHttpTransport()', () => { }, }); - makeNodeTransport({ url: TEST_SERVER_URL }); + makeNodeTransport(defaultOptions); const registeredRequestExecutor = (createTransport as jest.Mock).mock.calls[0][1]; const executorResult = registeredRequestExecutor({ @@ -309,7 +320,7 @@ describe('makeNewHttpTransport()', () => { }, }); - makeNodeTransport({ url: TEST_SERVER_URL }); + makeNodeTransport(defaultOptions); const registeredRequestExecutor = (createTransport as jest.Mock).mock.calls[0][1]; const executorResult = registeredRequestExecutor({ diff --git a/packages/node/test/transports/https.test.ts b/packages/node/test/transports/https.test.ts index dce56983680f..118a9a70dc88 100644 --- a/packages/node/test/transports/https.test.ts +++ b/packages/node/test/transports/https.test.ts @@ -79,6 +79,12 @@ const unsafeHttpsModule: HTTPModule = { }), }; +const defaultOptions = { + httpModule: unsafeHttpsModule, + url: TEST_SERVER_URL, + recordDroppedEvent: () => undefined, // noop +}; + describe('makeNewHttpsTransport()', () => { afterEach(() => { jest.clearAllMocks(); @@ -95,7 +101,7 @@ describe('makeNewHttpsTransport()', () => { expect(body).toBe(SERIALIZED_EVENT_ENVELOPE); }); - const transport = makeNodeTransport({ httpModule: unsafeHttpsModule, url: TEST_SERVER_URL }); + const transport = makeNodeTransport(defaultOptions); await transport.send(EVENT_ENVELOPE); }); @@ -111,8 +117,7 @@ describe('makeNewHttpsTransport()', () => { }); const transport = makeNodeTransport({ - httpModule: unsafeHttpsModule, - url: TEST_SERVER_URL, + ...defaultOptions, headers: { 'X-Some-Custom-Header-1': 'value1', 'X-Some-Custom-Header-2': 'value2', @@ -127,7 +132,7 @@ describe('makeNewHttpsTransport()', () => { async serverStatusCode => { await setupTestServer({ statusCode: serverStatusCode }); - const transport = makeNodeTransport({ httpModule: unsafeHttpsModule, url: TEST_SERVER_URL }); + const transport = makeNodeTransport(defaultOptions); expect(() => { expect(transport.send(EVENT_ENVELOPE)); }).not.toThrow(); @@ -143,7 +148,7 @@ describe('makeNewHttpsTransport()', () => { }, }); - const transport = makeNodeTransport({ httpModule: unsafeHttpsModule, url: TEST_SERVER_URL }); + const transport = makeNodeTransport(defaultOptions); await expect(transport.send(EVENT_ENVELOPE)).resolves.toBeUndefined(); }); @@ -156,7 +161,7 @@ describe('makeNewHttpsTransport()', () => { }, }); - const transport = makeNodeTransport({ httpModule: unsafeHttpsModule, url: TEST_SERVER_URL }); + const transport = makeNodeTransport(defaultOptions); await expect(transport.send(EVENT_ENVELOPE)).resolves.toBeUndefined(); }); @@ -164,6 +169,7 @@ describe('makeNewHttpsTransport()', () => { await setupTestServer({ statusCode: SUCCESS }); const transport = makeNodeTransport({ + ...defaultOptions, httpModule: unsafeHttpsModule, url: TEST_SERVER_URL, caCerts: 'some cert', @@ -184,6 +190,7 @@ describe('makeNewHttpsTransport()', () => { describe('proxy', () => { it('can be configured through option', () => { makeNodeTransport({ + ...defaultOptions, httpModule: unsafeHttpsModule, url: 'https://9e9fd4523d784609a5fc0ebb1080592f@sentry.io:8989/mysubpath/50622', proxy: 'https://example.com', @@ -196,6 +203,7 @@ describe('makeNewHttpsTransport()', () => { it('can be configured through env variables option (http)', () => { process.env.http_proxy = 'https://example.com'; makeNodeTransport({ + ...defaultOptions, httpModule: unsafeHttpsModule, url: 'https://9e9fd4523d784609a5fc0ebb1080592f@sentry.io:8989/mysubpath/50622', }); @@ -208,6 +216,7 @@ describe('makeNewHttpsTransport()', () => { it('can be configured through env variables option (https)', () => { process.env.https_proxy = 'https://example.com'; makeNodeTransport({ + ...defaultOptions, httpModule: unsafeHttpsModule, url: 'https://9e9fd4523d784609a5fc0ebb1080592f@sentry.io:8989/mysubpath/50622', }); @@ -220,6 +229,7 @@ describe('makeNewHttpsTransport()', () => { it('client options have priority over env variables', () => { process.env.https_proxy = 'https://foo.com'; makeNodeTransport({ + ...defaultOptions, httpModule: unsafeHttpsModule, url: 'https://9e9fd4523d784609a5fc0ebb1080592f@sentry.io:8989/mysubpath/50622', proxy: 'https://bar.com', @@ -233,6 +243,7 @@ describe('makeNewHttpsTransport()', () => { it('no_proxy allows for skipping specific hosts', () => { process.env.no_proxy = 'sentry.io'; makeNodeTransport({ + ...defaultOptions, httpModule: unsafeHttpsModule, url: 'https://9e9fd4523d784609a5fc0ebb1080592f@sentry.io:8989/mysubpath/50622', proxy: 'https://example.com', @@ -248,6 +259,7 @@ describe('makeNewHttpsTransport()', () => { process.env.no_proxy = 'sentry.io:8989'; makeNodeTransport({ + ...defaultOptions, httpModule: unsafeHttpsModule, url: 'https://9e9fd4523d784609a5fc0ebb1080592f@sentry.io:8989/mysubpath/50622', }); @@ -263,6 +275,7 @@ describe('makeNewHttpsTransport()', () => { process.env.no_proxy = 'example.com,sentry.io,wat.com:1337'; makeNodeTransport({ + ...defaultOptions, httpModule: unsafeHttpsModule, url: 'https://9e9fd4523d784609a5fc0ebb1080592f@sentry.io:8989/mysubpath/50622', }); @@ -283,7 +296,7 @@ describe('makeNewHttpsTransport()', () => { }, }); - makeNodeTransport({ httpModule: unsafeHttpsModule, url: TEST_SERVER_URL }); + makeNodeTransport(defaultOptions); const registeredRequestExecutor = (createTransport as jest.Mock).mock.calls[0][1]; const executorResult = registeredRequestExecutor({ @@ -306,7 +319,7 @@ describe('makeNewHttpsTransport()', () => { statusCode: SUCCESS, }); - makeNodeTransport({ httpModule: unsafeHttpsModule, url: TEST_SERVER_URL }); + makeNodeTransport(defaultOptions); const registeredRequestExecutor = (createTransport as jest.Mock).mock.calls[0][1]; const executorResult = registeredRequestExecutor({ @@ -333,7 +346,7 @@ describe('makeNewHttpsTransport()', () => { }, }); - makeNodeTransport({ httpModule: unsafeHttpsModule, url: TEST_SERVER_URL }); + makeNodeTransport(defaultOptions); const registeredRequestExecutor = (createTransport as jest.Mock).mock.calls[0][1]; const executorResult = registeredRequestExecutor({ @@ -360,7 +373,7 @@ describe('makeNewHttpsTransport()', () => { }, }); - makeNodeTransport({ httpModule: unsafeHttpsModule, url: TEST_SERVER_URL }); + makeNodeTransport(defaultOptions); const registeredRequestExecutor = (createTransport as jest.Mock).mock.calls[0][1]; const executorResult = registeredRequestExecutor({ diff --git a/packages/tracing/test/testutils.ts b/packages/tracing/test/testutils.ts index 298d65f94ce3..5b4594c64913 100644 --- a/packages/tracing/test/testutils.ts +++ b/packages/tracing/test/testutils.ts @@ -62,7 +62,7 @@ export const testOnlyIfNodeVersionAtLeast = (minVersion: number): jest.It => { export function getDefaultBrowserClientOptions(options: Partial = {}): ClientOptions { return { integrations: [], - transport: () => createTransport({}, _ => resolvedSyncPromise({})), + transport: () => createTransport({ recordDroppedEvent: () => undefined }, _ => resolvedSyncPromise({})), stackParser: () => [], ...options, }; diff --git a/packages/types/src/envelope.ts b/packages/types/src/envelope.ts index 8e5091dbbfa9..33846f077a79 100644 --- a/packages/types/src/envelope.ts +++ b/packages/types/src/envelope.ts @@ -7,6 +7,15 @@ import { UserFeedback } from './user'; // Based on: https://develop.sentry.dev/sdk/envelopes/ +export type EnvelopeItemType = + | 'client_report' + | 'user_report' + | 'session' + | 'sessions' + | 'transaction' + | 'attachment' + | 'event'; + export type BaseEnvelopeHeaders = { [key: string]: unknown; dsn?: string; @@ -15,7 +24,7 @@ export type BaseEnvelopeHeaders = { export type BaseEnvelopeItemHeaders = { [key: string]: unknown; - type: string; + type: EnvelopeItemType; length?: number; }; @@ -56,3 +65,4 @@ export type SessionEnvelope = BaseEnvelope; export type ClientReportEnvelope = BaseEnvelope; export type Envelope = EventEnvelope | SessionEnvelope | ClientReportEnvelope; +export type EnvelopeItem = Envelope[1][number]; diff --git a/packages/types/src/index.ts b/packages/types/src/index.ts index d74b24fb5266..545a894a81e7 100644 --- a/packages/types/src/index.ts +++ b/packages/types/src/index.ts @@ -12,6 +12,8 @@ export type { ClientReportEnvelope, ClientReportItem, Envelope, + EnvelopeItemType, + EnvelopeItem, EventEnvelope, EventItem, SessionEnvelope, diff --git a/packages/types/src/transport.ts b/packages/types/src/transport.ts index e87d664e6388..f757b271970a 100644 --- a/packages/types/src/transport.ts +++ b/packages/types/src/transport.ts @@ -1,3 +1,5 @@ +import { EventDropReason } from './clientreport'; +import { DataCategory } from './datacategory'; import { Envelope } from './envelope'; export type TransportRequest = { @@ -14,7 +16,9 @@ export type TransportMakeRequestResponse = { export interface InternalBaseTransportOptions { bufferSize?: number; + recordDroppedEvent: (reason: EventDropReason, dataCategory: DataCategory) => void; } + export interface BaseTransportOptions extends InternalBaseTransportOptions { // url to send the event // transport does not care about dsn specific - client should take care of diff --git a/packages/utils/src/envelope.ts b/packages/utils/src/envelope.ts index 0000e6007aee..a58c48029066 100644 --- a/packages/utils/src/envelope.ts +++ b/packages/utils/src/envelope.ts @@ -1,4 +1,4 @@ -import { Envelope } from '@sentry/types'; +import { DataCategory, Envelope, EnvelopeItem, EnvelopeItemType } from '@sentry/types'; import { isPrimitive } from './is'; @@ -22,11 +22,18 @@ export function addItemToEnvelope(envelope: E, newItem: E[1] } /** - * Get the type of the envelope. Grabs the type from the first envelope item. + * Convenience function to loop through the items and item types of an envelope. + * (This function was mostly created because working with envelope types is painful at the moment) */ -export function getEnvelopeType(envelope: E): string { - const [, [[firstItemHeader]]] = envelope; - return firstItemHeader.type; +export function forEachEnvelopeItem( + envelope: Envelope, + callback: (envelopeItem: E[1][number], envelopeItemType: E[1][number][0]['type']) => void, +): void { + const envelopeItems = envelope[1]; + envelopeItems.forEach((envelopeItem: EnvelopeItem) => { + const envelopeItemType = envelopeItem[0].type; + callback(envelopeItem, envelopeItemType); + }); } /** @@ -48,3 +55,20 @@ export function serializeEnvelope(envelope: Envelope): string { return `${acc}\n${JSON.stringify(itemHeaders)}\n${serializedPayload}`; }, serializedHeaders); } + +const ITEM_TYPE_TO_DATA_CATEGORY_MAP: Record = { + session: 'session', + sessions: 'session', + attachment: 'attachment', + transaction: 'transaction', + event: 'error', + client_report: 'internal', + user_report: 'default', +}; + +/** + * Maps the type of an envelope item to a data category. + */ +export function envelopeItemTypeToDataCategory(type: EnvelopeItemType): DataCategory { + return ITEM_TYPE_TO_DATA_CATEGORY_MAP[type]; +} diff --git a/packages/utils/src/ratelimit.ts b/packages/utils/src/ratelimit.ts index 59906c0abdaf..66614b7fc6b2 100644 --- a/packages/utils/src/ratelimit.ts +++ b/packages/utils/src/ratelimit.ts @@ -1,4 +1,4 @@ -// Keeping the key broad until we add the new transports +// Intentionally keeping the key broad, as we don't know for sure what rate limit headers get returned from backend export type RateLimits = Record; export const DEFAULT_RETRY_AFTER = 60 * 1000; // 60 seconds diff --git a/packages/utils/test/envelope.test.ts b/packages/utils/test/envelope.test.ts index dab2b92d5f47..df07629a083e 100644 --- a/packages/utils/test/envelope.test.ts +++ b/packages/utils/test/envelope.test.ts @@ -1,6 +1,6 @@ import { EventEnvelope } from '@sentry/types'; -import { addItemToEnvelope, createEnvelope, getEnvelopeType, serializeEnvelope } from '../src/envelope'; +import { addItemToEnvelope, createEnvelope, forEachEnvelopeItem, serializeEnvelope } from '../src/envelope'; import { parseEnvelope } from './testutils'; describe('envelope', () => { @@ -45,13 +45,27 @@ describe('envelope', () => { }); }); - describe('getEnvelopeType', () => { - it('returns the type of the envelope', () => { - const env = createEnvelope({ event_id: 'aa3ff046696b4bc6b609ce6d28fde9e2', sent_at: '123' }, [ + describe('forEachEnvelopeItem', () => { + it('loops through an envelope', () => { + const items: EventEnvelope[1] = [ [{ type: 'event' }, { event_id: 'aa3ff046696b4bc6b609ce6d28fde9e2' }], - [{ type: 'attachment', filename: 'me.txt' }, '123456'], - ]); - expect(getEnvelopeType(env)).toEqual('event'); + [{ type: 'attachment', filename: 'bar.txt' }, '123456'], + [{ type: 'attachment', filename: 'foo.txt' }, '123456'], + ]; + + const env = createEnvelope( + { event_id: 'aa3ff046696b4bc6b609ce6d28fde9e2', sent_at: '123' }, + items, + ); + + expect.assertions(6); + + let iteration = 0; + forEachEnvelopeItem(env, (item, type) => { + expect(item).toBe(items[iteration]); + expect(type).toBe(items[iteration][0].type); + iteration = iteration + 1; + }); }); }); }); From 389f4eee498e43edf1baf994c649f489881ac10f Mon Sep 17 00:00:00 2001 From: Abhijeet Prasad Date: Thu, 28 Apr 2022 10:23:36 -0400 Subject: [PATCH 097/204] ref(browser): Remove showReportDialog on browser client (#4973) Remove `showReportDialog` as a client method so it can be tree-shaken out if not used. Also allow for hub to be explicitly passed in to `showReportDialog`. --- packages/browser/src/client.ts | 26 -------------- packages/browser/src/exports.ts | 1 - packages/browser/src/helpers.ts | 42 +--------------------- packages/browser/src/sdk.ts | 46 +++++++++++++++++++----- packages/browser/test/unit/index.test.ts | 34 ++++++++++++------ packages/core/src/api.ts | 11 +++--- 6 files changed, 68 insertions(+), 92 deletions(-) diff --git a/packages/browser/src/client.ts b/packages/browser/src/client.ts index d57b35c02d31..004f1359ffab 100644 --- a/packages/browser/src/client.ts +++ b/packages/browser/src/client.ts @@ -1,10 +1,7 @@ import { BaseClient, Scope, SDK_VERSION } from '@sentry/core'; import { ClientOptions, Event, EventHint, Options, Severity, SeverityLevel } from '@sentry/types'; -import { getGlobalObject, logger } from '@sentry/utils'; import { eventFromException, eventFromMessage } from './eventbuilder'; -import { IS_DEBUG_BUILD } from './flags'; -import { injectReportDialog, ReportDialogOptions } from './helpers'; import { Breadcrumbs } from './integrations'; export interface BaseBrowserOptions { @@ -62,29 +59,6 @@ export class BrowserClient extends BaseClient { super(options); } - /** - * Show a report dialog to the user to send feedback to a specific event. - * - * @param options Set individual options for the dialog - */ - public showReportDialog(options: ReportDialogOptions = {}): void { - // doesn't work without a document (React Native) - const document = getGlobalObject().document; - if (!document) { - return; - } - - if (!this._isEnabled()) { - IS_DEBUG_BUILD && logger.error('Trying to call showReportDialog with Sentry Client disabled'); - return; - } - - injectReportDialog({ - ...options, - dsn: options.dsn || this.getDsn(), - }); - } - /** * @inheritDoc */ diff --git a/packages/browser/src/exports.ts b/packages/browser/src/exports.ts index 4a1dc5837747..93a45d353ea9 100644 --- a/packages/browser/src/exports.ts +++ b/packages/browser/src/exports.ts @@ -52,6 +52,5 @@ export { opera11StackParser, winjsStackParser, } from './stack-parsers'; -export { injectReportDialog } from './helpers'; export { defaultIntegrations, forceLoad, init, lastEventId, onLoad, showReportDialog, flush, close, wrap } from './sdk'; export { SDK_NAME } from './version'; diff --git a/packages/browser/src/helpers.ts b/packages/browser/src/helpers.ts index d0b3f8f5c9aa..9bf1d5631288 100644 --- a/packages/browser/src/helpers.ts +++ b/packages/browser/src/helpers.ts @@ -1,18 +1,13 @@ -import { captureException, getReportDialogEndpoint, withScope } from '@sentry/core'; +import { captureException, withScope } from '@sentry/core'; import { DsnLike, Event as SentryEvent, Mechanism, Scope, WrappedFunction } from '@sentry/types'; import { addExceptionMechanism, addExceptionTypeValue, addNonEnumerableProperty, - getGlobalObject, getOriginalFunction, - logger, markFunctionWrapped, } from '@sentry/utils'; -import { IS_DEBUG_BUILD } from './flags'; - -const global = getGlobalObject(); let ignoreOnError: number = 0; /** @@ -182,38 +177,3 @@ export interface ReportDialogOptions { /** Callback after reportDialog showed up */ onLoad?(): void; } - -/** - * Injects the Report Dialog script - * @hidden - */ -export function injectReportDialog(options: ReportDialogOptions = {}): void { - if (!global.document) { - return; - } - - if (!options.eventId) { - IS_DEBUG_BUILD && logger.error('Missing eventId option in showReportDialog call'); - return; - } - - if (!options.dsn) { - IS_DEBUG_BUILD && logger.error('Missing dsn option in showReportDialog call'); - return; - } - - const script = global.document.createElement('script'); - script.async = true; - script.src = getReportDialogEndpoint(options.dsn, options); - - if (options.onLoad) { - // eslint-disable-next-line @typescript-eslint/unbound-method - script.onload = options.onLoad; - } - - const injectionPoint = global.document.head || global.document.body; - - if (injectionPoint) { - injectionPoint.appendChild(script); - } -} diff --git a/packages/browser/src/sdk.ts b/packages/browser/src/sdk.ts index 184893f15cf0..3740a1318f0a 100644 --- a/packages/browser/src/sdk.ts +++ b/packages/browser/src/sdk.ts @@ -1,5 +1,11 @@ -import { getCurrentHub, getIntegrationsToSetup, initAndBind, Integrations as CoreIntegrations } from '@sentry/core'; -import { Hub } from '@sentry/types'; +import { + getCurrentHub, + getIntegrationsToSetup, + getReportDialogEndpoint, + Hub, + initAndBind, + Integrations as CoreIntegrations, +} from '@sentry/core'; import { addInstrumentationHandler, getGlobalObject, @@ -121,9 +127,21 @@ export function init(options: BrowserOptions = {}): void { * * @param options Everything is optional, we try to fetch all info need from the global scope. */ -export function showReportDialog(options: ReportDialogOptions = {}): void { - const hub = getCurrentHub(); - const scope = hub.getScope(); +export function showReportDialog(options: ReportDialogOptions = {}, hub: Hub = getCurrentHub()): void { + // doesn't work without a document (React Native) + const global = getGlobalObject(); + if (!global.document) { + IS_DEBUG_BUILD && logger.error('Global document not defined in showReportDialog call'); + return; + } + + const { client, scope } = hub.getStackTop(); + const dsn = options.dsn || (client && client.getDsn()); + if (!dsn) { + IS_DEBUG_BUILD && logger.error('DSN not configured for showReportDialog call'); + return; + } + if (scope) { options.user = { ...scope.getUser(), @@ -134,9 +152,21 @@ export function showReportDialog(options: ReportDialogOptions = {}): void { if (!options.eventId) { options.eventId = hub.lastEventId(); } - const client = hub.getClient(); - if (client) { - client.showReportDialog(options); + + const script = global.document.createElement('script'); + script.async = true; + script.src = getReportDialogEndpoint(dsn, options); + + if (options.onLoad) { + // eslint-disable-next-line @typescript-eslint/unbound-method + script.onload = options.onLoad; + } + + const injectionPoint = global.document.head || global.document.body; + if (injectionPoint) { + injectionPoint.appendChild(script); + } else { + IS_DEBUG_BUILD && logger.error('Not injecting report dialog. No injection point found in HTML'); } } diff --git a/packages/browser/test/unit/index.test.ts b/packages/browser/test/unit/index.test.ts index c8d1df23bac9..37fa972512a1 100644 --- a/packages/browser/test/unit/index.test.ts +++ b/packages/browser/test/unit/index.test.ts @@ -1,4 +1,4 @@ -import { SDK_VERSION } from '@sentry/core'; +import { getReportDialogEndpoint, SDK_VERSION } from '@sentry/core'; import { addBreadcrumb, @@ -24,6 +24,14 @@ const dsn = 'https://53039209a22b4ec1bcc296a3c9fdecd6@sentry.io/4291'; // eslint-disable-next-line no-var declare var global: any; +jest.mock('@sentry/core', () => { + const original = jest.requireActual('@sentry/core'); + return { + ...original, + getReportDialogEndpoint: jest.fn(), + }; +}); + describe('SentryBrowser', () => { const beforeSend = jest.fn(); @@ -74,16 +82,14 @@ describe('SentryBrowser', () => { }); describe('showReportDialog', () => { + beforeEach(() => { + (getReportDialogEndpoint as jest.Mock).mockReset(); + }); + describe('user', () => { const EX_USER = { email: 'test@example.com' }; const options = getDefaultBrowserClientOptions({ dsn }); const client = new BrowserClient(options); - const reportDialogSpy = jest.spyOn(client, 'showReportDialog'); - - beforeEach(() => { - reportDialogSpy.mockReset(); - }); - it('uses the user on the scope', () => { configureScope(scope => { scope.setUser(EX_USER); @@ -92,8 +98,11 @@ describe('SentryBrowser', () => { showReportDialog(); - expect(reportDialogSpy).toBeCalled(); - expect(reportDialogSpy.mock.calls[0][0]!.user!.email).toBe(EX_USER.email); + expect(getReportDialogEndpoint).toHaveBeenCalledTimes(1); + expect(getReportDialogEndpoint).toHaveBeenCalledWith( + expect.any(Object), + expect.objectContaining({ user: { email: EX_USER.email } }), + ); }); it('prioritizes options user over scope user', () => { @@ -105,8 +114,11 @@ describe('SentryBrowser', () => { const DIALOG_OPTION_USER = { email: 'option@example.com' }; showReportDialog({ user: DIALOG_OPTION_USER }); - expect(reportDialogSpy).toBeCalled(); - expect(reportDialogSpy.mock.calls[0][0]!.user!.email).toBe(DIALOG_OPTION_USER.email); + expect(getReportDialogEndpoint).toHaveBeenCalledTimes(1); + expect(getReportDialogEndpoint).toHaveBeenCalledWith( + expect.any(Object), + expect.objectContaining({ user: { email: DIALOG_OPTION_USER.email } }), + ); }); }); }); diff --git a/packages/core/src/api.ts b/packages/core/src/api.ts index dced7a8b181e..31375593fd4a 100644 --- a/packages/core/src/api.ts +++ b/packages/core/src/api.ts @@ -53,14 +53,15 @@ export function getReportDialogEndpoint( } if (key === 'user') { - if (!dialogOptions.user) { + const user = dialogOptions.user; + if (!user) { continue; } - if (dialogOptions.user.name) { - encodedOptions += `&name=${encodeURIComponent(dialogOptions.user.name)}`; + if (user.name) { + encodedOptions += `&name=${encodeURIComponent(user.name)}`; } - if (dialogOptions.user.email) { - encodedOptions += `&email=${encodeURIComponent(dialogOptions.user.email)}`; + if (user.email) { + encodedOptions += `&email=${encodeURIComponent(user.email)}`; } } else { encodedOptions += `&${encodeURIComponent(key)}=${encodeURIComponent(dialogOptions[key] as string)}`; From b3fddcd6a224210fd2a2219776e30a8180e481e6 Mon Sep 17 00:00:00 2001 From: Abhijeet Prasad Date: Thu, 28 Apr 2022 10:34:22 -0400 Subject: [PATCH 098/204] feat(hub): Remove _invokeClient (#4972) This PR removes the dynamic dispatch of `_invokeClient` in favour of explicitly using client methods. We introduce a `_withClient` helper that does this. `_withClient` grabs the client and scope from the stack of scopes on the hub, and executes a callback with those values. This allows the consumer of the function to directly access the client instance and call methods on it. --- packages/hub/src/hub.ts | 48 ++++++++++++++++++++++------------- packages/hub/test/hub.test.ts | 7 ----- 2 files changed, 30 insertions(+), 25 deletions(-) diff --git a/packages/hub/src/hub.ts b/packages/hub/src/hub.ts index 4e97cd8aa9b3..ac98e053faf5 100644 --- a/packages/hub/src/hub.ts +++ b/packages/hub/src/hub.ts @@ -185,11 +185,18 @@ export class Hub implements HubInterface { // eslint-disable-next-line @typescript-eslint/no-explicit-any, @typescript-eslint/explicit-module-boundary-types public captureException(exception: any, hint?: EventHint): string { const eventId = (this._lastEventId = hint && hint.event_id ? hint.event_id : uuid4()); - this._invokeClient('captureException', exception, { - originalException: exception, - syntheticException: new Error('Sentry syntheticException'), - ...hint, - event_id: eventId, + const syntheticException = new Error('Sentry syntheticException'); + this._withClient((client, scope) => { + client.captureException( + exception, + { + originalException: exception, + syntheticException, + ...hint, + event_id: eventId, + }, + scope, + ); }); return eventId; } @@ -204,11 +211,19 @@ export class Hub implements HubInterface { hint?: EventHint, ): string { const eventId = (this._lastEventId = hint && hint.event_id ? hint.event_id : uuid4()); - this._invokeClient('captureMessage', message, level, { - originalException: message, - syntheticException: new Error(message), - ...hint, - event_id: eventId, + const syntheticException = new Error(message); + this._withClient((client, scope) => { + client.captureMessage( + message, + level, + { + originalException: message, + syntheticException, + ...hint, + event_id: eventId, + }, + scope, + ); }); return eventId; } @@ -222,9 +237,8 @@ export class Hub implements HubInterface { this._lastEventId = eventId; } - this._invokeClient('captureEvent', event, { - ...hint, - event_id: eventId, + this._withClient((client, scope) => { + client.captureEvent(event, { ...hint, event_id: eventId }, scope); }); return eventId; } @@ -446,12 +460,10 @@ export class Hub implements HubInterface { * @param method The method to call on the client. * @param args Arguments to pass to the client function. */ - // eslint-disable-next-line @typescript-eslint/no-explicit-any - private _invokeClient(method: M, ...args: any[]): void { + private _withClient(callback: (client: Client, scope: Scope | undefined) => void): void { const { scope, client } = this.getStackTop(); - if (client && client[method]) { - // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access, @typescript-eslint/no-explicit-any - (client as any)[method](...args, scope); + if (client) { + callback(client, scope); } } diff --git a/packages/hub/test/hub.test.ts b/packages/hub/test/hub.test.ts index 6754ac258669..443f56b19e11 100644 --- a/packages/hub/test/hub.test.ts +++ b/packages/hub/test/hub.test.ts @@ -52,13 +52,6 @@ describe('Hub', () => { expect(hub.getStack()).toHaveLength(1); }); - test("don't invoke client sync with wrong func", () => { - const hub = new Hub(clientFn); - // @ts-ignore we want to able to call private method - hub._invokeClient('funca', true); - expect(clientFn).not.toHaveBeenCalled(); - }); - test('isOlderThan', () => { const hub = new Hub(); expect(hub.isOlderThan(0)).toBeFalsy(); From f979104d9415e12aa51d7947df9030cb972c72e4 Mon Sep 17 00:00:00 2001 From: Lukas Stracke Date: Thu, 28 Apr 2022 17:37:34 +0200 Subject: [PATCH 099/204] docs(transports): Update Migration document with custom Transport changes (#5012) add the Transport changes to MIGRATION.md. show the difference between old and new transports and give example how to migrate a custom v6 transport to v7. Co-authored-by: Abhijeet Prasad --- MIGRATION.md | 105 ++++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 104 insertions(+), 1 deletion(-) diff --git a/MIGRATION.md b/MIGRATION.md index e7f2ecd7b028..646fb4e86036 100644 --- a/MIGRATION.md +++ b/MIGRATION.md @@ -102,7 +102,110 @@ const storeEndpoint = api.getStoreEndpointWithUrlEncodedAuth(); const envelopeEndpoint = api.getEnvelopeEndpointWithUrlEncodedAuth(); ``` -## Enum changes +## Transport Changes + +The `Transport` API was simplified and some functionality (e.g. APIDetails and client reports) was refactored and moved +to the Client. To send data to Sentry, we switched from the previously used [Store endpoint](https://develop.sentry.dev/sdk/store/) to the [Envelopes endpoint](https://develop.sentry.dev/sdk/envelopes/). + +This example shows the new v7 and the v6 Transport API: + +```js +// New in v7: +export interface Transport { + /* Sends an envelope to the Envelope endpoint in Sentry */ + send(request: Envelope): PromiseLike; + /* Waits for all events to be sent or the timeout to expire, whichever comes first */ + flush(timeout?: number): PromiseLike; +} + +// Before: +export interface Transport { + /* Sends the event to the Store endpoint in Sentry */ + sendEvent(event: Event): PromiseLike; + /* Sends the session to the Envelope endpoint in Sentry */ + sendSession?(session: Session | SessionAggregates): PromiseLike; + /* Waits for all events to be sent or the timeout to expire, whichever comes first */ + close(timeout?: number): PromiseLike; + /* Increment the counter for the specific client outcome */ + recordLostEvent?(type: Outcome, category: SentryRequestType): void; +} +``` + +### Custom Transports +If you rely on a custom transport, you will need to make some adjustments to how it is created when migrating +to v7. Note that we changed our transports from a class-based to a functional approach, meaning that +the previously class-based transports are now created via functions. This also means that custom transports +are now passed by specifying a factory function in the `Sentry.init` options object instead passing the custom +transport's class. + +The following example shows how to create a custom transport in v7 vs. how it was done in v6: + +```js +// New in v7: +import { BaseTransportOptions, Transport, TransportMakeRequestResponse, TransportRequest } from '@sentry/types'; +import { createTransport } from '@sentry/core'; + +export function makeMyCustomTransport(options: BaseTransportOptions): Transport { + function makeRequest(request: TransportRequest): PromiseLike { + // this is where your sending logic goes + const myCustomRequest = { + body: request.body, + url: options.url + }; + // you define how `sendMyCustomRequest` works + return sendMyCustomRequest(myCustomRequest).then(response => ({ + headers: { + 'x-sentry-rate-limits': response.headers.get('X-Sentry-Rate-Limits'), + 'retry-after': response.headers.get('Retry-After'), + }, + })); + } + + // `createTransport` takes care of rate limiting and flushing + return createTransport({ bufferSize: options.bufferSize }, makeRequest); +} + +Sentry.init({ + dsn: '...', + transport: makeMyCustomTransport, // this function will be called when the client is initialized + ... +}) + + + +// Before: +class MyCustomTransport extends BaseTransport { + constructor(options: TransportOptions) { + // initialize your transport here + super(options); + } + + public sendEvent(event: Event): PromiseLike { + // this is where your sending logic goes + // `url` is decoded from dsn in BaseTransport + const myCustomRequest = createMyCustomRequestFromEvent(event, this.url); + return sendMyCustomRequest(myCustomRequest).then(() => resolve({status: 'success'})); + } + + public sendSession(session: Session): PromiseLike {...} + // ... +} + +Sentry.init({ + dsn: '...', + transport: MyCustomTransport, // the constructor was called when the client was initialized + ... +}) +``` + +Overall, the new way of transport creation allows you to create your custom sending implementation +without having to deal with the conversion of events or sessions to envelopes. +We recommend calling using the `createTransport` function from `@sentry/core` as demonstrated in the example above which, besides creating the `Transport` +object with your custom logic, will also take care of rate limiting and flushing. + +For a complete v7 transport implementation, take a look at our [browser fetch transport](https://github.com/getsentry/sentry-javascript/blob/ebc938a03d6efe7d0c4bbcb47714e84c9a566a9c/packages/browser/src/transports/fetch.ts#L1-L34). + +## Enum Changes Given that enums have a high bundle-size impact, our long term goal is to eventually remove all enums from the SDK in favor of string literals. From 1c5bff02e4d1fc3b36cd83ffac93fd65ab46236b Mon Sep 17 00:00:00 2001 From: Abhijeet Prasad Date: Thu, 28 Apr 2022 13:41:02 -0400 Subject: [PATCH 100/204] ref(core): Make event processing logs warnings (#5010) It can be confusing to users why event processing errors log out as errors. Let's bump this down to warnings instead - which better represent how actionable most of these thrown errors are. --- packages/core/src/baseclient.ts | 2 +- packages/core/test/lib/base.test.ts | 16 ++++++++-------- packages/nextjs/test/index.client.test.ts | 4 ++-- packages/nextjs/test/index.server.test.ts | 4 ++-- 4 files changed, 13 insertions(+), 13 deletions(-) diff --git a/packages/core/src/baseclient.ts b/packages/core/src/baseclient.ts index c50403ab3903..88f543a6f97e 100644 --- a/packages/core/src/baseclient.ts +++ b/packages/core/src/baseclient.ts @@ -545,7 +545,7 @@ export abstract class BaseClient implements Client { return finalEvent.event_id; }, reason => { - IS_DEBUG_BUILD && logger.error(reason); + IS_DEBUG_BUILD && logger.warn(reason); return undefined; }, ); diff --git a/packages/core/test/lib/base.test.ts b/packages/core/test/lib/base.test.ts index feb18390c3e8..476fcbdbb87d 100644 --- a/packages/core/test/lib/base.test.ts +++ b/packages/core/test/lib/base.test.ts @@ -940,13 +940,13 @@ describe('BaseClient', () => { const options = getDefaultTestClientOptions({ dsn: PUBLIC_DSN, beforeSend }); const client = new TestClient(options); const captureExceptionSpy = jest.spyOn(client, 'captureException'); - const loggerErrorSpy = jest.spyOn(logger, 'error'); + const loggerWarnSpy = jest.spyOn(logger, 'warn'); client.captureEvent({ message: 'hello' }); expect(TestClient.instance!.event).toBeUndefined(); expect(captureExceptionSpy).not.toBeCalled(); - expect(loggerErrorSpy).toBeCalledWith(new SentryError('`beforeSend` returned `null`, will not send event.')); + expect(loggerWarnSpy).toBeCalledWith(new SentryError('`beforeSend` returned `null`, will not send event.')); }); test('calls beforeSend and log info about invalid return value', () => { @@ -958,12 +958,12 @@ describe('BaseClient', () => { // @ts-ignore we need to test regular-js behavior const options = getDefaultTestClientOptions({ dsn: PUBLIC_DSN, beforeSend }); const client = new TestClient(options); - const loggerErrorSpy = jest.spyOn(logger, 'error'); + const loggerWarnSpy = jest.spyOn(logger, 'warn'); client.captureEvent({ message: 'hello' }); expect(TestClient.instance!.event).toBeUndefined(); - expect(loggerErrorSpy).toBeCalledWith( + expect(loggerWarnSpy).toBeCalledWith( new SentryError('`beforeSend` method has to return `null` or a valid event.'), ); } @@ -1091,7 +1091,7 @@ describe('BaseClient', () => { const client = new TestClient(getDefaultTestClientOptions({ dsn: PUBLIC_DSN })); const captureExceptionSpy = jest.spyOn(client, 'captureException'); - const loggerErrorSpy = jest.spyOn(logger, 'error'); + const loggerWarnSpy = jest.spyOn(logger, 'warn'); const scope = new Scope(); scope.addEventProcessor(() => null); @@ -1099,7 +1099,7 @@ describe('BaseClient', () => { expect(TestClient.instance!.event).toBeUndefined(); expect(captureExceptionSpy).not.toBeCalled(); - expect(loggerErrorSpy).toBeCalledWith(new SentryError('An event processor returned null, will not send event.')); + expect(loggerWarnSpy).toBeCalledWith(new SentryError('An event processor returned null, will not send event.')); }); // TODO(v7): Add back tests with client reports @@ -1131,7 +1131,7 @@ describe('BaseClient', () => { const options = getDefaultTestClientOptions({ dsn: PUBLIC_DSN }); const client = new TestClient(options); const captureExceptionSpy = jest.spyOn(client, 'captureException'); - const loggerErrorSpy = jest.spyOn(logger, 'error'); + const loggerWarnSpy = jest.spyOn(logger, 'warn'); const scope = new Scope(); const exception = new Error('sorry'); scope.addEventProcessor(() => { @@ -1147,7 +1147,7 @@ describe('BaseClient', () => { }, originalException: exception, }); - expect(loggerErrorSpy).toBeCalledWith( + expect(loggerWarnSpy).toBeCalledWith( new SentryError( `Event processing pipeline threw an error, original event will not be sent. Details have been sent as a new event.\nReason: ${exception}`, ), diff --git a/packages/nextjs/test/index.client.test.ts b/packages/nextjs/test/index.client.test.ts index 5d7053c00def..a143591ad8f0 100644 --- a/packages/nextjs/test/index.client.test.ts +++ b/packages/nextjs/test/index.client.test.ts @@ -14,7 +14,7 @@ const global = getGlobalObject(); const reactInit = jest.spyOn(SentryReact, 'init'); const captureEvent = jest.spyOn(BaseClient.prototype, 'captureEvent'); -const logError = jest.spyOn(logger, 'error'); +const logWarn = jest.spyOn(logger, 'warn'); describe('Client init()', () => { afterEach(() => { @@ -75,7 +75,7 @@ describe('Client init()', () => { expect(transportSend).not.toHaveBeenCalled(); expect(captureEvent.mock.results[0].value).toBeUndefined(); - expect(logError).toHaveBeenCalledWith(new SentryError('An event processor returned null, will not send event.')); + expect(logWarn).toHaveBeenCalledWith(new SentryError('An event processor returned null, will not send event.')); }); describe('integrations', () => { diff --git a/packages/nextjs/test/index.server.test.ts b/packages/nextjs/test/index.server.test.ts index 477ad252cd29..d926d8bbd57e 100644 --- a/packages/nextjs/test/index.server.test.ts +++ b/packages/nextjs/test/index.server.test.ts @@ -16,7 +16,7 @@ const global = getGlobalObject(); (global as typeof global & { __rewriteFramesDistDir__: string }).__rewriteFramesDistDir__ = '.next'; const nodeInit = jest.spyOn(SentryNode, 'init'); -const logError = jest.spyOn(logger, 'error'); +const logWarn = jest.spyOn(logger, 'warn'); describe('Server init()', () => { afterEach(() => { @@ -104,7 +104,7 @@ describe('Server init()', () => { await SentryNode.flush(); expect(transportSend).not.toHaveBeenCalled(); - expect(logError).toHaveBeenCalledWith(new SentryError('An event processor returned null, will not send event.')); + expect(logWarn).toHaveBeenCalledWith(new SentryError('An event processor returned null, will not send event.')); }); it("initializes both global hub and domain hub when there's an active domain", () => { From 031ffa7d2a878c84eb1d593cbc18a9ee80b7984d Mon Sep 17 00:00:00 2001 From: Lukas Stracke Date: Fri, 29 Apr 2022 17:51:55 +0200 Subject: [PATCH 101/204] docs(core): Improve documentation of `Options` and `ClientOptions` properties (#5019) slightly improve documentation of (mostly) user-facing Options / ClientOptions properties. Remove a TODO which we agreed we won't do for now. --- packages/types/src/options.ts | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) diff --git a/packages/types/src/options.ts b/packages/types/src/options.ts index b310b8e1799e..92627bb8142a 100644 --- a/packages/types/src/options.ts +++ b/packages/types/src/options.ts @@ -59,7 +59,8 @@ export interface ClientOptions Transport; @@ -96,7 +97,12 @@ export interface ClientOptions /** * If this is set to false, default integrations will not be added, otherwise this will internally be set to the * recommended default integrations. - * TODO: We should consider changing this to `boolean | Integration[]` */ defaultIntegrations?: false | Integration[]; @@ -219,7 +224,9 @@ export interface Options integrations?: Integration[] | ((integrations: Integration[]) => Integration[]); /** - * Transport object that should be used to send events to Sentry + * A function that takes transport options and returns the Transport object which is used to send events to Sentry. + * The function is invoked internally during SDK initialization. + * By default, the SDK initializes its default transports. */ transport?: (transportOptions: TO) => Transport; From dd1fb29976285b08eea6ed0d18269a25d28670a8 Mon Sep 17 00:00:00 2001 From: Luca Forstner Date: Fri, 29 Apr 2022 17:52:16 +0200 Subject: [PATCH 102/204] ref: Record dropped events in `BaseClient` (#5017) --- packages/core/src/baseclient.ts | 81 ++++++--- packages/core/test/lib/base.test.ts | 157 +++++++++++------- packages/core/test/mocks/client.ts | 12 +- packages/tracing/src/transaction.ts | 11 +- packages/tracing/test/idletransaction.test.ts | 17 +- packages/types/src/client.ts | 13 +- packages/types/src/clientreport.ts | 8 +- packages/types/src/index.ts | 2 +- 8 files changed, 196 insertions(+), 105 deletions(-) diff --git a/packages/core/src/baseclient.ts b/packages/core/src/baseclient.ts index 88f543a6f97e..f27742e6f251 100644 --- a/packages/core/src/baseclient.ts +++ b/packages/core/src/baseclient.ts @@ -3,12 +3,15 @@ import { Scope, Session } from '@sentry/hub'; import { Client, ClientOptions, + DataCategory, DsnComponents, Envelope, Event, + EventDropReason, EventHint, Integration, IntegrationClass, + Outcome, SessionAggregates, Severity, SeverityLevel, @@ -87,6 +90,9 @@ export abstract class BaseClient implements Client { /** Number of calls being processed */ protected _numProcessing: number = 0; + /** Holds flushable */ + private _outcomes: { [key: string]: number } = {}; + /** * Initializes this client instance. * @@ -98,7 +104,7 @@ export abstract class BaseClient implements Client { this._dsn = makeDsn(options.dsn); const url = getEnvelopeEndpointWithUrlEncodedAuth(this._dsn, options.tunnel); this._transport = options.transport({ - recordDroppedEvent: () => undefined, // TODO(v7): Provide a proper function instead of noop + recordDroppedEvent: this.recordDroppedEvent.bind(this), ...options.transportOptions, url, }); @@ -270,7 +276,7 @@ export abstract class BaseClient implements Client { public sendEvent(event: Event): void { if (this._dsn) { const env = createEventEnvelope(event, this._dsn, this._options._metadata, this._options.tunnel); - this.sendEnvelope(env); + this._sendEnvelope(env); } } @@ -280,20 +286,26 @@ export abstract class BaseClient implements Client { public sendSession(session: Session | SessionAggregates): void { if (this._dsn) { const env = createSessionEnvelope(session, this._dsn, this._options._metadata, this._options.tunnel); - this.sendEnvelope(env); + this._sendEnvelope(env); } } /** - * @inheritdoc + * @inheritDoc */ - public sendEnvelope(env: Envelope): void { - if (this._transport && this._dsn) { - this._transport.send(env).then(null, reason => { - IS_DEBUG_BUILD && logger.error('Error while sending event:', reason); - }); - } else { - IS_DEBUG_BUILD && logger.error('Transport disabled'); + public recordDroppedEvent(reason: EventDropReason, category: DataCategory): void { + if (this._options.sendClientReports) { + // We want to track each category (error, transaction, session) separately + // but still keep the distinction between different type of outcomes. + // We could use nested maps, but it's much easier to read and type this way. + // A correct type for map-based implementation if we want to go that route + // would be `Partial>>>` + // With typescript 4.1 we could even use template literal types + const key = `${reason}:${category}`; + IS_DEBUG_BUILD && logger.log(`Adding outcome: "${key}"`); + + // The following works because undefined + 1 === NaN and NaN is falsy + this._outcomes[key] = this._outcomes[key] + 1 || 1; } } @@ -565,20 +577,8 @@ export abstract class BaseClient implements Client { * @returns A SyncPromise that resolves with the event or rejects in case event was/will not be send. */ protected _processEvent(event: Event, hint?: EventHint, scope?: Scope): PromiseLike { - // eslint-disable-next-line @typescript-eslint/unbound-method const { beforeSend, sampleRate } = this.getOptions(); - // type RecordLostEvent = NonNullable; - type RecordLostEventParams = unknown[]; // Parameters; - - // no-op as new transports don't have client outcomes - // TODO(v7): Re-add this functionality - function recordLostEvent(_outcome: RecordLostEventParams[0], _category: RecordLostEventParams[1]): void { - // if (transport.recordLostEvent) { - // transport.recordLostEvent(outcome, category); - // } - } - if (!this._isEnabled()) { return rejectedSyncPromise(new SentryError('SDK not enabled, will not capture event.')); } @@ -588,7 +588,7 @@ export abstract class BaseClient implements Client { // 0.0 === 0% events are sent // Sampling for transaction happens somewhere else if (!isTransaction && typeof sampleRate === 'number' && Math.random() > sampleRate) { - recordLostEvent('sample_rate', 'event'); + this.recordDroppedEvent('sample_rate', 'error'); return rejectedSyncPromise( new SentryError( `Discarding event because it's not included in the random sample (sampling rate = ${sampleRate})`, @@ -599,7 +599,7 @@ export abstract class BaseClient implements Client { return this._prepareEvent(event, scope, hint) .then(prepared => { if (prepared === null) { - recordLostEvent('event_processor', event.type || 'event'); + this.recordDroppedEvent('event_processor', event.type || 'error'); throw new SentryError('An event processor returned null, will not send event.'); } @@ -613,7 +613,7 @@ export abstract class BaseClient implements Client { }) .then(processedEvent => { if (processedEvent === null) { - recordLostEvent('before_send', event.type || 'event'); + this.recordDroppedEvent('before_send', event.type || 'error'); throw new SentryError('`beforeSend` returned `null`, will not send event.'); } @@ -659,6 +659,35 @@ export abstract class BaseClient implements Client { ); } + /** + * @inheritdoc + */ + protected _sendEnvelope(envelope: Envelope): void { + if (this._transport && this._dsn) { + this._transport.send(envelope).then(null, reason => { + IS_DEBUG_BUILD && logger.error('Error while sending event:', reason); + }); + } else { + IS_DEBUG_BUILD && logger.error('Transport disabled'); + } + } + + /** + * Clears outcomes on this client and returns them. + */ + protected _clearOutcomes(): Outcome[] { + const outcomes = this._outcomes; + this._outcomes = {}; + return Object.keys(outcomes).map(key => { + const [reason, category] = key.split(':') as [EventDropReason, DataCategory]; + return { + reason, + category, + quantity: outcomes[key], + }; + }); + } + /** * @inheritDoc */ diff --git a/packages/core/test/lib/base.test.ts b/packages/core/test/lib/base.test.ts index 476fcbdbb87d..520cd40931a6 100644 --- a/packages/core/test/lib/base.test.ts +++ b/packages/core/test/lib/base.test.ts @@ -1061,30 +1061,24 @@ describe('BaseClient', () => { expect((TestClient.instance!.event! as any).data).toBe('someRandomThing'); }); - // TODO(v7): Add back test with client reports - // test('beforeSend records dropped events', () => { - // expect.assertions(1); - - // const client = new TestClient( - // getDefaultTestClientOptions({ - // dsn: PUBLIC_DSN, - // beforeSend() { - // return null; - // }, - // }), - // ); - // const recordLostEventSpy = jest.fn(); - // jest.spyOn(client, 'getTransport').mockImplementationOnce( - // () => - // ({ - // recordLostEvent: recordLostEventSpy, - // } as any as Transport), - // ); - - // client.captureEvent({ message: 'hello' }, {}); - - // expect(recordLostEventSpy).toHaveBeenCalledWith('before_send', 'event'); - // }); + test('beforeSend records dropped events', () => { + expect.assertions(1); + + const client = new TestClient( + getDefaultTestClientOptions({ + dsn: PUBLIC_DSN, + beforeSend() { + return null; + }, + }), + ); + + const recordLostEventSpy = jest.spyOn(client, 'recordDroppedEvent'); + + client.captureEvent({ message: 'hello' }, {}); + + expect(recordLostEventSpy).toHaveBeenCalledWith('before_send', 'error'); + }); test('eventProcessor can drop the even when it returns null', () => { expect.assertions(3); @@ -1102,28 +1096,21 @@ describe('BaseClient', () => { expect(loggerWarnSpy).toBeCalledWith(new SentryError('An event processor returned null, will not send event.')); }); - // TODO(v7): Add back tests with client reports - // test('eventProcessor records dropped events', () => { - // expect.assertions(1); + test('eventProcessor records dropped events', () => { + expect.assertions(1); - // const options = getDefaultTestClientOptions({ dsn: PUBLIC_DSN }); - // const client = new TestClient(options); + const options = getDefaultTestClientOptions({ dsn: PUBLIC_DSN }); + const client = new TestClient(options); - // const recordLostEventSpy = jest.fn(); - // jest.spyOn(client, 'getTransport').mockImplementationOnce( - // () => - // ({ - // recordLostEvent: recordLostEventSpy, - // } as any as Transport), - // ); + const recordLostEventSpy = jest.spyOn(client, 'recordDroppedEvent'); - // const scope = new Scope(); - // scope.addEventProcessor(() => null); + const scope = new Scope(); + scope.addEventProcessor(() => null); - // client.captureEvent({ message: 'hello' }, {}, scope); + client.captureEvent({ message: 'hello' }, {}, scope); - // expect(recordLostEventSpy).toHaveBeenCalledWith('event_processor', 'event'); - // }); + expect(recordLostEventSpy).toHaveBeenCalledWith('event_processor', 'error'); + }); test('eventProcessor sends an event and logs when it crashes', () => { expect.assertions(3); @@ -1154,24 +1141,17 @@ describe('BaseClient', () => { ); }); - // TODO(v7): Add back test with client reports - // test('records events dropped due to sampleRate', () => { - // expect.assertions(1); + test('records events dropped due to sampleRate', () => { + expect.assertions(1); - // const options = getDefaultTestClientOptions({ dsn: PUBLIC_DSN, sampleRate: 0 }); - // const client = new TestClient(options); + const options = getDefaultTestClientOptions({ dsn: PUBLIC_DSN, sampleRate: 0 }); + const client = new TestClient(options); - // const recordLostEventSpy = jest.fn(); - // jest.spyOn(client, 'getTransport').mockImplementationOnce( - // () => - // ({ - // recordLostEvent: recordLostEventSpy, - // } as any as Transport), - // ); + const recordLostEventSpy = jest.spyOn(client, 'recordDroppedEvent'); - // client.captureEvent({ message: 'hello' }, {}); - // expect(recordLostEventSpy).toHaveBeenCalledWith('sample_rate', 'event'); - // }); + client.captureEvent({ message: 'hello' }, {}); + expect(recordLostEventSpy).toHaveBeenCalledWith('sample_rate', 'error'); + }); }); describe('integrations', () => { @@ -1366,4 +1346,69 @@ describe('BaseClient', () => { expect(TestClient.instance!.session).toBeUndefined(); }); }); + + describe('recordDroppedEvent()/_clearOutcomes()', () => { + test('record and return outcomes', () => { + const options = getDefaultTestClientOptions({ dsn: PUBLIC_DSN }); + const client = new TestClient(options); + + client.recordDroppedEvent('ratelimit_backoff', 'error'); + client.recordDroppedEvent('ratelimit_backoff', 'error'); + client.recordDroppedEvent('network_error', 'transaction'); + client.recordDroppedEvent('network_error', 'transaction'); + client.recordDroppedEvent('before_send', 'session'); + client.recordDroppedEvent('event_processor', 'attachment'); + client.recordDroppedEvent('network_error', 'transaction'); + + const clearedOutcomes = client._clearOutcomes(); + + expect(clearedOutcomes).toEqual( + expect.arrayContaining([ + { + reason: 'ratelimit_backoff', + category: 'error', + quantity: 2, + }, + { + reason: 'network_error', + category: 'transaction', + quantity: 3, + }, + { + reason: 'before_send', + category: 'session', + quantity: 1, + }, + { + reason: 'event_processor', + category: 'attachment', + quantity: 1, + }, + ]), + ); + }); + + test('to clear outcomes', () => { + const options = getDefaultTestClientOptions({ dsn: PUBLIC_DSN }); + const client = new TestClient(options); + + client.recordDroppedEvent('ratelimit_backoff', 'error'); + client.recordDroppedEvent('ratelimit_backoff', 'error'); + client.recordDroppedEvent('event_processor', 'attachment'); + + const clearedOutcomes1 = client._clearOutcomes(); + expect(clearedOutcomes1.length).toEqual(2); + + const clearedOutcomes2 = client._clearOutcomes(); + expect(clearedOutcomes2.length).toEqual(0); + + client.recordDroppedEvent('network_error', 'attachment'); + + const clearedOutcomes3 = client._clearOutcomes(); + expect(clearedOutcomes3.length).toEqual(1); + + const clearedOutcomes4 = client._clearOutcomes(); + expect(clearedOutcomes4.length).toEqual(0); + }); + }); }); diff --git a/packages/core/test/mocks/client.ts b/packages/core/test/mocks/client.ts index 6878a649bf17..d46eb000d480 100644 --- a/packages/core/test/mocks/client.ts +++ b/packages/core/test/mocks/client.ts @@ -1,18 +1,15 @@ import { Session } from '@sentry/hub'; -import { ClientOptions, Event, Integration, Severity, SeverityLevel } from '@sentry/types'; +import { ClientOptions, Event, Integration, Outcome, Severity, SeverityLevel } from '@sentry/types'; import { resolvedSyncPromise } from '@sentry/utils'; import { BaseClient } from '../../src/baseclient'; import { initAndBind } from '../../src/sdk'; import { createTransport } from '../../src/transports/base'; -// TODO(v7): Add client reports tests to this file -// See old tests in packages/browser/test/unit/transports/base.test.ts -// from https://github.com/getsentry/sentry-javascript/pull/4967 - export function getDefaultTestClientOptions(options: Partial = {}): TestClientOptions { return { integrations: [], + sendClientReports: true, transport: () => createTransport( { recordDroppedEvent: () => undefined }, // noop @@ -79,6 +76,11 @@ export class TestClient extends BaseClient { public sendSession(session: Session): void { this.session = session; } + + // Public proxy for protected method + public _clearOutcomes(): Outcome[] { + return super._clearOutcomes(); + } } export function init(options: TestClientOptions): void { diff --git a/packages/tracing/src/transaction.ts b/packages/tracing/src/transaction.ts index 3e3c148fd5d3..0001589349ca 100644 --- a/packages/tracing/src/transaction.ts +++ b/packages/tracing/src/transaction.ts @@ -103,12 +103,11 @@ export class Transaction extends SpanClass implements TransactionInterface { // At this point if `sampled !== true` we want to discard the transaction. IS_DEBUG_BUILD && logger.log('[Tracing] Discarding transaction because its trace was not chosen to be sampled.'); - // TODO(v7): Add back client reports functionality - // const client = this._hub.getClient(); - // const transport = client && client.getTransport && client.getTransport(); - // if (transport && transport.recordLostEvent) { - // transport.recordLostEvent('sample_rate', 'transaction'); - // } + const client = this._hub.getClient(); + if (client) { + client.recordDroppedEvent('sample_rate', 'transaction'); + } + return undefined; } diff --git a/packages/tracing/test/idletransaction.test.ts b/packages/tracing/test/idletransaction.test.ts index e6035ad491a1..bf4d5d51af41 100644 --- a/packages/tracing/test/idletransaction.test.ts +++ b/packages/tracing/test/idletransaction.test.ts @@ -163,19 +163,18 @@ describe('IdleTransaction', () => { } }); - // TODO(v7): Add with client reports - // it('should record dropped transactions', async () => { - // const transaction = new IdleTransaction({ name: 'foo', startTimestamp: 1234, sampled: false }, hub, 1000); + it('should record dropped transactions', async () => { + const transaction = new IdleTransaction({ name: 'foo', startTimestamp: 1234, sampled: false }, hub, 1000); - // const transport = hub.getClient()!.getTransport!(); + const client = hub.getClient()!; - // const spy = jest.spyOn(transport, 'recordLostEvent'); + const recordDroppedEventSpy = jest.spyOn(client, 'recordDroppedEvent'); - // transaction.initSpanRecorder(10); - // transaction.finish(transaction.startTimestamp + 10); + transaction.initSpanRecorder(10); + transaction.finish(transaction.startTimestamp + 10); - // expect(spy).toHaveBeenCalledWith('sample_rate', 'transaction'); - // }); + expect(recordDroppedEventSpy).toHaveBeenCalledWith('sample_rate', 'transaction'); + }); describe('_initTimeout', () => { it('finishes if no activities are added to the transaction', () => { diff --git a/packages/types/src/client.ts b/packages/types/src/client.ts index 9b91c6712e06..3425006e645d 100644 --- a/packages/types/src/client.ts +++ b/packages/types/src/client.ts @@ -1,3 +1,5 @@ +import { EventDropReason } from './clientreport'; +import { DataCategory } from './datacategory'; import { DsnComponents } from './dsn'; import { Event, EventHint } from './event'; import { Integration, IntegrationClass } from './integration'; @@ -54,7 +56,8 @@ export interface Client { */ captureEvent(event: Event, hint?: EventHint, scope?: Scope): string | undefined; - /** Captures a session + /** + * Captures a session * * @param session Session to be delivered */ @@ -117,4 +120,12 @@ export interface Client { /** Submits the session to Sentry */ sendSession(session: Session | SessionAggregates): void; + + /** + * Record on the client that an event got dropped (ie, an event that will not be sent to sentry). + * + * @param reason The reason why the event got dropped. + * @param category The data category of the dropped event. + */ + recordDroppedEvent(reason: EventDropReason, category: DataCategory): void; } diff --git a/packages/types/src/clientreport.ts b/packages/types/src/clientreport.ts index 1f59ad4910a3..79b3c2a4454c 100644 --- a/packages/types/src/clientreport.ts +++ b/packages/types/src/clientreport.ts @@ -8,7 +8,13 @@ export type EventDropReason = | 'ratelimit_backoff' | 'sample_rate'; +export type Outcome = { + reason: EventDropReason; + category: DataCategory; + quantity: number; +}; + export type ClientReport = { timestamp: number; - discarded_events: Array<{ reason: EventDropReason; category: DataCategory; quantity: number }>; + discarded_events: Outcome[]; }; diff --git a/packages/types/src/index.ts b/packages/types/src/index.ts index 545a894a81e7..2fbcf4bbed6b 100644 --- a/packages/types/src/index.ts +++ b/packages/types/src/index.ts @@ -1,6 +1,6 @@ export type { Breadcrumb, BreadcrumbHint } from './breadcrumb'; export type { Client } from './client'; -export type { ClientReport, EventDropReason } from './clientreport'; +export type { ClientReport, Outcome, EventDropReason } from './clientreport'; export type { Context, Contexts } from './context'; export type { DataCategory } from './datacategory'; export type { DsnComponents, DsnLike, DsnProtocol } from './dsn'; From b450538013c8dcc02bd0e9b88e92b28c7c360ba9 Mon Sep 17 00:00:00 2001 From: Luca Forstner Date: Fri, 29 Apr 2022 18:18:17 +0200 Subject: [PATCH 103/204] ref: Add back client reporting to `BrowserClient` (#5018) --- packages/browser/src/client.ts | 44 +++++++++++++++++++++++- packages/browser/src/transports/utils.ts | 6 ++-- 2 files changed, 45 insertions(+), 5 deletions(-) diff --git a/packages/browser/src/client.ts b/packages/browser/src/client.ts index 004f1359ffab..4f85103a86a6 100644 --- a/packages/browser/src/client.ts +++ b/packages/browser/src/client.ts @@ -1,8 +1,13 @@ -import { BaseClient, Scope, SDK_VERSION } from '@sentry/core'; +import { BaseClient, getEnvelopeEndpointWithUrlEncodedAuth, Scope, SDK_VERSION } from '@sentry/core'; import { ClientOptions, Event, EventHint, Options, Severity, SeverityLevel } from '@sentry/types'; +import { createClientReportEnvelope, dsnToString, getGlobalObject, logger, serializeEnvelope } from '@sentry/utils'; import { eventFromException, eventFromMessage } from './eventbuilder'; +import { IS_DEBUG_BUILD } from './flags'; import { Breadcrumbs } from './integrations'; +import { sendReport } from './transports/utils'; + +const globalObject = getGlobalObject(); export interface BaseBrowserOptions { /** @@ -56,7 +61,16 @@ export class BrowserClient extends BaseClient { ], version: SDK_VERSION, }; + super(options); + + if (options.sendClientReports && globalObject.document) { + globalObject.document.addEventListener('visibilitychange', () => { + if (globalObject.document.visibilityState === 'hidden') { + this._flushOutcomes(); + } + }); + } } /** @@ -96,4 +110,32 @@ export class BrowserClient extends BaseClient { } super._sendEvent(event); } + + /** + * Sends client reports as an envelope. + */ + private _flushOutcomes(): void { + const outcomes = this._clearOutcomes(); + + if (outcomes.length === 0) { + IS_DEBUG_BUILD && logger.log('No outcomes to send'); + return; + } + + if (!this._dsn) { + IS_DEBUG_BUILD && logger.log('No dsn provided, will not send outcomes'); + return; + } + + IS_DEBUG_BUILD && logger.log('Sending outcomes:', outcomes); + + const url = getEnvelopeEndpointWithUrlEncodedAuth(this._dsn, this._options.tunnel); + const envelope = createClientReportEnvelope(outcomes, this._options.tunnel && dsnToString(this._dsn)); + + try { + sendReport(url, serializeEnvelope(envelope)); + } catch (e) { + IS_DEBUG_BUILD && logger.error(e); + } + } } diff --git a/packages/browser/src/transports/utils.ts b/packages/browser/src/transports/utils.ts index 5a8e17b42019..b386557499c5 100644 --- a/packages/browser/src/transports/utils.ts +++ b/packages/browser/src/transports/utils.ts @@ -93,10 +93,8 @@ export function sendReport(url: string, body: string): void { if (hasSendBeacon) { // Prevent illegal invocations - https://xgwang.me/posts/you-may-not-know-beacon/#it-may-throw-error%2C-be-sure-to-catch const sendBeacon = global.navigator.sendBeacon.bind(global.navigator); - return sendBeacon(url, body); - } - - if (supportsFetch()) { + sendBeacon(url, body); + } else if (supportsFetch()) { const fetch = getNativeFetchImplementation(); fetch(url, { body, From e16a6999229908303ea4524eccd903688937e9fa Mon Sep 17 00:00:00 2001 From: Onur Temizkan Date: Fri, 29 Apr 2022 20:00:58 +0100 Subject: [PATCH 104/204] test(tracing): Add Prisma ORM integration tests. (#4962) Add integration tests for Prisma ORM integration based on https://www.prisma.io/docs/guides/testing/integration-testing. Also update integration perform validation on Prisma Client. --- packages/node-integration-tests/.gitignore | 1 + packages/node-integration-tests/package.json | 4 ++ .../tracing/prisma-orm/docker-compose.yml | 13 +++++ .../suites/tracing/prisma-orm/package.json | 22 +++++++++ .../prisma/migrations/migration_lock.toml | 3 ++ .../migrations/sentry_test/migration.sql | 12 +++++ .../tracing/prisma-orm/prisma/schema.prisma | 15 ++++++ .../suites/tracing/prisma-orm/scenario.ts | 48 +++++++++++++++++++ .../suites/tracing/prisma-orm/setup.ts | 16 +++++++ .../suites/tracing/prisma-orm/test.ts | 17 +++++++ .../suites/tracing/prisma-orm/yarn.lock | 27 +++++++++++ .../tracing/src/integrations/node/prisma.ts | 16 +++++-- yarn.lock | 12 +++++ 13 files changed, 203 insertions(+), 3 deletions(-) create mode 100644 packages/node-integration-tests/.gitignore create mode 100644 packages/node-integration-tests/suites/tracing/prisma-orm/docker-compose.yml create mode 100644 packages/node-integration-tests/suites/tracing/prisma-orm/package.json create mode 100644 packages/node-integration-tests/suites/tracing/prisma-orm/prisma/migrations/migration_lock.toml create mode 100644 packages/node-integration-tests/suites/tracing/prisma-orm/prisma/migrations/sentry_test/migration.sql create mode 100644 packages/node-integration-tests/suites/tracing/prisma-orm/prisma/schema.prisma create mode 100644 packages/node-integration-tests/suites/tracing/prisma-orm/scenario.ts create mode 100755 packages/node-integration-tests/suites/tracing/prisma-orm/setup.ts create mode 100644 packages/node-integration-tests/suites/tracing/prisma-orm/test.ts create mode 100644 packages/node-integration-tests/suites/tracing/prisma-orm/yarn.lock diff --git a/packages/node-integration-tests/.gitignore b/packages/node-integration-tests/.gitignore new file mode 100644 index 000000000000..3c3629e647f5 --- /dev/null +++ b/packages/node-integration-tests/.gitignore @@ -0,0 +1 @@ +node_modules diff --git a/packages/node-integration-tests/package.json b/packages/node-integration-tests/package.json index 002031983f52..87920c697cad 100644 --- a/packages/node-integration-tests/package.json +++ b/packages/node-integration-tests/package.json @@ -7,14 +7,18 @@ }, "private": true, "scripts": { + "clean": "rimraf -g **/node_modules", + "prisma:init": "(cd suites/tracing/prisma-orm && ts-node ./setup.ts)", "lint": "run-s lint:prettier lint:eslint", "lint:eslint": "eslint . --cache --cache-location '../../eslintcache/' --format stylish", "lint:prettier": "prettier --check \"{suites,utils}/**/*.ts\"", "type-check": "tsc", + "pretest": "run-s --silent prisma:init", "test": "jest --runInBand --forceExit", "test:watch": "yarn test --watch" }, "dependencies": { + "@prisma/client": "^3.12.0", "@types/mongodb": "^3.6.20", "@types/mysql": "^2.15.21", "@types/pg": "^8.6.5", diff --git a/packages/node-integration-tests/suites/tracing/prisma-orm/docker-compose.yml b/packages/node-integration-tests/suites/tracing/prisma-orm/docker-compose.yml new file mode 100644 index 000000000000..45caa4bb3179 --- /dev/null +++ b/packages/node-integration-tests/suites/tracing/prisma-orm/docker-compose.yml @@ -0,0 +1,13 @@ +version: '3.9' + +services: + db: + image: postgres:13 + restart: always + container_name: integration-tests-prisma + ports: + - '5433:5432' + environment: + POSTGRES_USER: prisma + POSTGRES_PASSWORD: prisma + POSTGRES_DB: tests diff --git a/packages/node-integration-tests/suites/tracing/prisma-orm/package.json b/packages/node-integration-tests/suites/tracing/prisma-orm/package.json new file mode 100644 index 000000000000..f8b24d7d0465 --- /dev/null +++ b/packages/node-integration-tests/suites/tracing/prisma-orm/package.json @@ -0,0 +1,22 @@ +{ + "name": "sentry-prisma-test", + "version": "1.0.0", + "description": "", + "main": "index.js", + "engines": { + "node": ">=12" + }, + "scripts": { + "db-up": "docker-compose up -d", + "generate": "prisma generate", + "migrate": "prisma migrate dev -n sentry-test", + "setup": "run-s --silent db-up generate migrate" + }, + "keywords": [], + "author": "", + "license": "ISC", + "dependencies": { + "@prisma/client": "3.12.0", + "prisma": "^3.12.0" + } +} diff --git a/packages/node-integration-tests/suites/tracing/prisma-orm/prisma/migrations/migration_lock.toml b/packages/node-integration-tests/suites/tracing/prisma-orm/prisma/migrations/migration_lock.toml new file mode 100644 index 000000000000..fbffa92c2bb7 --- /dev/null +++ b/packages/node-integration-tests/suites/tracing/prisma-orm/prisma/migrations/migration_lock.toml @@ -0,0 +1,3 @@ +# Please do not edit this file manually +# It should be added in your version-control system (i.e. Git) +provider = "postgresql" \ No newline at end of file diff --git a/packages/node-integration-tests/suites/tracing/prisma-orm/prisma/migrations/sentry_test/migration.sql b/packages/node-integration-tests/suites/tracing/prisma-orm/prisma/migrations/sentry_test/migration.sql new file mode 100644 index 000000000000..8619aaceb2b0 --- /dev/null +++ b/packages/node-integration-tests/suites/tracing/prisma-orm/prisma/migrations/sentry_test/migration.sql @@ -0,0 +1,12 @@ +-- CreateTable +CREATE TABLE "User" ( + "id" SERIAL NOT NULL, + "createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, + "email" TEXT NOT NULL, + "name" TEXT, + + CONSTRAINT "User_pkey" PRIMARY KEY ("id") +); + +-- CreateIndex +CREATE UNIQUE INDEX "User_email_key" ON "User"("email"); diff --git a/packages/node-integration-tests/suites/tracing/prisma-orm/prisma/schema.prisma b/packages/node-integration-tests/suites/tracing/prisma-orm/prisma/schema.prisma new file mode 100644 index 000000000000..4363c97738ee --- /dev/null +++ b/packages/node-integration-tests/suites/tracing/prisma-orm/prisma/schema.prisma @@ -0,0 +1,15 @@ +datasource db { + url = "postgresql://prisma:prisma@localhost:5433/tests" + provider = "postgresql" +} + +generator client { + provider = "prisma-client-js" +} + +model User { + id Int @id @default(autoincrement()) + createdAt DateTime @default(now()) + email String @unique + name String? +} diff --git a/packages/node-integration-tests/suites/tracing/prisma-orm/scenario.ts b/packages/node-integration-tests/suites/tracing/prisma-orm/scenario.ts new file mode 100644 index 000000000000..047166a9e136 --- /dev/null +++ b/packages/node-integration-tests/suites/tracing/prisma-orm/scenario.ts @@ -0,0 +1,48 @@ +/* eslint-disable @typescript-eslint/no-unsafe-member-access */ +import { PrismaClient } from '@prisma/client'; +import * as Sentry from '@sentry/node'; +import * as Tracing from '@sentry/tracing'; +import { randomBytes } from 'crypto'; + +const client = new PrismaClient(); + +Sentry.init({ + dsn: 'https://public@dsn.ingest.sentry.io/1337', + release: '1.0', + tracesSampleRate: 1.0, + integrations: [new Tracing.Integrations.Prisma({ client })], +}); + +async function run(): Promise { + const transaction = Sentry.startTransaction({ + name: 'Test Transaction', + op: 'transaction', + }); + + Sentry.configureScope(scope => { + scope.setSpan(transaction); + }); + + try { + await client.user.create({ + data: { + name: 'Tilda', + email: `tilda_${randomBytes(4).toString('hex')}@sentry.io`, + }, + }); + + await client.user.findMany(); + + await client.user.deleteMany({ + where: { + email: { + contains: 'sentry.io', + }, + }, + }); + } finally { + if (transaction) transaction.finish(); + } +} + +void run(); diff --git a/packages/node-integration-tests/suites/tracing/prisma-orm/setup.ts b/packages/node-integration-tests/suites/tracing/prisma-orm/setup.ts new file mode 100755 index 000000000000..3c40d12f7337 --- /dev/null +++ b/packages/node-integration-tests/suites/tracing/prisma-orm/setup.ts @@ -0,0 +1,16 @@ +import { parseSemver } from '@sentry/utils'; +import { execSync } from 'child_process'; + +const NODE_VERSION = parseSemver(process.versions.node); + +if (NODE_VERSION.major && NODE_VERSION.major < 12) { + // eslint-disable-next-line no-console + console.warn(`Skipping Prisma tests on Node: ${NODE_VERSION.major}`); + process.exit(0); +} + +try { + execSync('yarn && yarn setup'); +} catch (_) { + process.exit(1); +} diff --git a/packages/node-integration-tests/suites/tracing/prisma-orm/test.ts b/packages/node-integration-tests/suites/tracing/prisma-orm/test.ts new file mode 100644 index 000000000000..91e6f39da889 --- /dev/null +++ b/packages/node-integration-tests/suites/tracing/prisma-orm/test.ts @@ -0,0 +1,17 @@ +import { assertSentryTransaction, conditionalTest, getEnvelopeRequest, runServer } from '../../../utils'; + +conditionalTest({ min: 12 })('Prisma ORM Integration', () => { + test('should instrument Prisma client for tracing.', async () => { + const url = await runServer(__dirname); + const envelope = await getEnvelopeRequest(url); + + assertSentryTransaction(envelope[2], { + transaction: 'Test Transaction', + spans: [ + { description: 'User create', op: 'db.prisma' }, + { description: 'User findMany', op: 'db.prisma' }, + { description: 'User deleteMany', op: 'db.prisma' }, + ], + }); + }); +}); diff --git a/packages/node-integration-tests/suites/tracing/prisma-orm/yarn.lock b/packages/node-integration-tests/suites/tracing/prisma-orm/yarn.lock new file mode 100644 index 000000000000..d228adebd621 --- /dev/null +++ b/packages/node-integration-tests/suites/tracing/prisma-orm/yarn.lock @@ -0,0 +1,27 @@ +# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. +# yarn lockfile v1 + + +"@prisma/client@3.12.0": + version "3.12.0" + resolved "https://registry.yarnpkg.com/@prisma/client/-/client-3.12.0.tgz#a0eb49ffea5c128dd11dffb896d7139a60073d12" + integrity sha512-4NEQjUcWja/NVBvfuDFscWSk1/rXg3+wj+TSkqXCb1tKlx/bsUE00rxsvOvGg7VZ6lw1JFpGkwjwmsOIc4zvQw== + dependencies: + "@prisma/engines-version" "3.12.0-37.22b822189f46ef0dc5c5b503368d1bee01213980" + +"@prisma/engines-version@3.12.0-37.22b822189f46ef0dc5c5b503368d1bee01213980": + version "3.12.0-37.22b822189f46ef0dc5c5b503368d1bee01213980" + resolved "https://registry.yarnpkg.com/@prisma/engines-version/-/engines-version-3.12.0-37.22b822189f46ef0dc5c5b503368d1bee01213980.tgz#829ca3d9d0d92555f44644606d4edfd45b2f5886" + integrity sha512-o+jo8d7ZEiVpcpNWUDh3fj2uPQpBxl79XE9ih9nkogJbhw6P33274SHnqheedZ7PyvPIK/mvU8MLNYgetgXPYw== + +"@prisma/engines@3.12.0-37.22b822189f46ef0dc5c5b503368d1bee01213980": + version "3.12.0-37.22b822189f46ef0dc5c5b503368d1bee01213980" + resolved "https://registry.yarnpkg.com/@prisma/engines/-/engines-3.12.0-37.22b822189f46ef0dc5c5b503368d1bee01213980.tgz#e52e364084c4d05278f62768047b788665e64a45" + integrity sha512-zULjkN8yhzS7B3yeEz4aIym4E2w1ChrV12i14pht3ePFufvsAvBSoZ+tuXMvfSoNTgBS5E4bolRzLbMmbwkkMQ== + +prisma@^3.12.0: + version "3.12.0" + resolved "https://registry.yarnpkg.com/prisma/-/prisma-3.12.0.tgz#9675e0e72407122759d3eadcb6d27cdccd3497bd" + integrity sha512-ltCMZAx1i0i9xuPM692Srj8McC665h6E5RqJom999sjtVSccHSD8Z+HSdBN2183h9PJKvC5dapkn78dd0NWMBg== + dependencies: + "@prisma/engines" "3.12.0-37.22b822189f46ef0dc5c5b503368d1bee01213980" diff --git a/packages/tracing/src/integrations/node/prisma.ts b/packages/tracing/src/integrations/node/prisma.ts index e70fce9f0f60..3aa7a07f4da3 100644 --- a/packages/tracing/src/integrations/node/prisma.ts +++ b/packages/tracing/src/integrations/node/prisma.ts @@ -38,6 +38,10 @@ interface PrismaClient { $use: (cb: PrismaMiddleware) => void; } +function isValidPrismaClient(possibleClient: unknown): possibleClient is PrismaClient { + return possibleClient && !!(possibleClient as PrismaClient)['$use']; +} + /** Tracing integration for @prisma/client package */ export class Prisma implements Integration { /** @@ -58,8 +62,14 @@ export class Prisma implements Integration { /** * @inheritDoc */ - public constructor(options: { client?: PrismaClient } = {}) { - this._client = options.client; + public constructor(options: { client?: unknown } = {}) { + if (isValidPrismaClient(options.client)) { + this._client = options.client; + } else { + logger.warn( + `Unsupported Prisma client provided to PrismaIntegration. Provided client: ${JSON.stringify(options.client)}`, + ); + } } /** @@ -71,7 +81,7 @@ export class Prisma implements Integration { return; } - this._client.$use((params: PrismaMiddlewareParams, next: (params: PrismaMiddlewareParams) => Promise) => { + this._client.$use((params, next: (params: PrismaMiddlewareParams) => Promise) => { const scope = getCurrentHub().getScope(); const parentSpan = scope?.getSpan(); diff --git a/yarn.lock b/yarn.lock index 6b21e6baa196..f6ba304563d7 100644 --- a/yarn.lock +++ b/yarn.lock @@ -4079,6 +4079,18 @@ resolved "https://registry.yarnpkg.com/@polka/url/-/url-1.0.0-next.12.tgz#431ec342a7195622f86688bbda82e3166ce8cb28" integrity sha512-6RglhutqrGFMO1MNUXp95RBuYIuc8wTnMAV5MUhLmjTOy78ncwOw7RgeQ/HeymkKXRhZd0s2DNrM1rL7unk3MQ== +"@prisma/client@^3.12.0": + version "3.12.0" + resolved "https://registry.yarnpkg.com/@prisma/client/-/client-3.12.0.tgz#a0eb49ffea5c128dd11dffb896d7139a60073d12" + integrity sha512-4NEQjUcWja/NVBvfuDFscWSk1/rXg3+wj+TSkqXCb1tKlx/bsUE00rxsvOvGg7VZ6lw1JFpGkwjwmsOIc4zvQw== + dependencies: + "@prisma/engines-version" "3.12.0-37.22b822189f46ef0dc5c5b503368d1bee01213980" + +"@prisma/engines-version@3.12.0-37.22b822189f46ef0dc5c5b503368d1bee01213980": + version "3.12.0-37.22b822189f46ef0dc5c5b503368d1bee01213980" + resolved "https://registry.yarnpkg.com/@prisma/engines-version/-/engines-version-3.12.0-37.22b822189f46ef0dc5c5b503368d1bee01213980.tgz#829ca3d9d0d92555f44644606d4edfd45b2f5886" + integrity sha512-o+jo8d7ZEiVpcpNWUDh3fj2uPQpBxl79XE9ih9nkogJbhw6P33274SHnqheedZ7PyvPIK/mvU8MLNYgetgXPYw== + "@protobufjs/aspromise@^1.1.1", "@protobufjs/aspromise@^1.1.2": version "1.1.2" resolved "https://registry.yarnpkg.com/@protobufjs/aspromise/-/aspromise-1.1.2.tgz#9b8b0cc663d669a7d8f6f5d0893a14d348f30fbf" From f18a938df3d6162ca2664aabc7c29a4b32e33eb5 Mon Sep 17 00:00:00 2001 From: Katie Byers Date: Fri, 29 Apr 2022 14:57:33 -0700 Subject: [PATCH 105/204] fix(gatsby): Add script to ensure types files exist when testing (#5020) This adds a check to the beginning of the gatsby tests which makes sure that the necessary types files exist and creates them before starting the tests if they don't. Note: These files are not the normal files built by `yarn build:types` but additional ones made necessary by the structure gatsby plugins are required to have. See https://github.com/getsentry/sentry-javascript/pull/4928. --- packages/gatsby/package.json | 4 ++-- packages/gatsby/scripts/pretest.ts | 14 ++++++++++++++ 2 files changed, 16 insertions(+), 2 deletions(-) create mode 100644 packages/gatsby/scripts/pretest.ts diff --git a/packages/gatsby/package.json b/packages/gatsby/package.json index 5fc7e5e08402..8b02e542af23 100644 --- a/packages/gatsby/package.json +++ b/packages/gatsby/package.json @@ -59,8 +59,8 @@ "lint": "run-s lint:prettier lint:eslint", "lint:eslint": "eslint . --cache --cache-location '../../eslintcache/' --format stylish", "lint:prettier": "prettier --check \"{src,test,scripts}/**/*.ts\"", - "test": "jest", - "test:watch": "jest --watch" + "test": "yarn ts-node scripts/pretest.ts && yarn jest", + "test:watch": "yarn ts-node scripts/pretest.ts && yarn jest --watch" }, "volta": { "extends": "../../package.json" diff --git a/packages/gatsby/scripts/pretest.ts b/packages/gatsby/scripts/pretest.ts new file mode 100644 index 000000000000..834301f7c38a --- /dev/null +++ b/packages/gatsby/scripts/pretest.ts @@ -0,0 +1,14 @@ +import { execSync } from 'child_process'; +import * as fs from 'fs'; + +function ensurePluginTypes(): void { + if (!fs.existsSync('gatsby-browser.d.ts') || !fs.existsSync('gatsby-node.d.ts')) { + // eslint-disable-next-line no-console + console.warn( + '\nWARNING: Missing types for gatsby plugin files. Types will be created before running gatsby tests.', + ); + execSync('yarn build:plugin'); + } +} + +ensurePluginTypes(); From 69e62758a42f46b8c21fafca5603c4cc200b600c Mon Sep 17 00:00:00 2001 From: Katie Byers Date: Fri, 29 Apr 2022 16:08:17 -0700 Subject: [PATCH 106/204] fix(build): Fix temporary sucrase CI jobs (#5021) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Until we officially switch over to rullup/sucrase builds, there are extra CI jobs running to test the build changes so far without us having had to switch to them yet. That said, the first attempt at creating such a system had a significant flaw, in that the supposedly-sucrase unit tests were actually running against the normal tsc build output. Further, it was very flaky, but it seems there might occasionally have been some scope bleed between the tsc-built file cache and the sucrase-built one. (It seems unlikely, I know, and I could be imagining it. Regardless, the changes here make it a moot point.) This fixes those unit tests to run against sucrase builds, by adding a script to build with sucrase in the test job itself (and all other sucrase test jobs). It also moves a prior hack (rebuilding in es5 for node 8 tests) from the main test script into the new hacky script, so it can't get missed when pulling these temporary fixes out of the code when we switch. (That move is why our regular unit tests also run that script, to get the prior hack.) Finally, it makes a temporary modification to our base rollup config when in node 8, to enable the just-in-time rollup builds. Disclaimer: This is definitely a hack, and is a little ugly, but it's also coming out in a week, so... ¯\\_(ツ)_/¯ Note: Node 8 sucrase tests are an xfail at this point, until more of the new build pipeline is merged. --- .github/workflows/build.yml | 96 +++++++++++++----------------------- rollup/npmHelpers.js | 18 +++++-- scripts/sucrase-test-hack.ts | 49 ++++++++++++++++++ scripts/test.ts | 7 --- 4 files changed, 98 insertions(+), 72 deletions(-) create mode 100644 scripts/sucrase-test-hack.ts diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 577de41be06d..1e88cd491876 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -105,45 +105,6 @@ jobs: # `job_build` can't see `job_install_deps` and what it returned) dependency_cache_key: ${{ needs.job_install_deps.outputs.dependency_cache_key }} - # This isn't a full `yarn build` using sucrase - it's just the cache from the normal build, with `build/cjs` and - # `build/esm` overwritten by sucrase. This way we don't need to worry about all of the other random stuff which - # packages build, because it will already be there. - job_build_with_sucrase: - name: Sucrase Build - needs: [job_install_deps, job_build] - runs-on: ubuntu-latest - timeout-minutes: 20 - steps: - - name: Check out current commit (${{ env.HEAD_COMMIT }}) - uses: actions/checkout@v2 - with: - ref: ${{ env.HEAD_COMMIT }} - - name: Set up Node - uses: actions/setup-node@v1 - - name: Check dependency cache - uses: actions/cache@v2 - with: - path: ${{ env.CACHED_DEPENDENCY_PATHS }} - key: ${{ needs.job_install_deps.outputs.dependency_cache_key }} - - name: Check tsc build cache - uses: actions/cache@v2 - with: - path: ${{ env.CACHED_BUILD_PATHS }} - key: ${{ env.BUILD_CACHE_KEY }} - - name: Check sucrase build cache - uses: actions/cache@v2 - id: cache_built_sucrase_packages - with: - path: ${{ env.CACHED_BUILD_PATHS }} - key: ${{ env.BUILD_CACHE_KEY }}-sucrase - - name: Build packages with sucrase - if: steps.cache_built_sucrase_packages.outputs.cache-hit == '' - run: 'yarn build:rollup' - outputs: - # this needs to be passed on, because the `needs` context only looks at direct ancestors (so steps which depend on - # `job_build` can't see `job_install_deps` and what it returned) - dependency_cache_key: ${{ needs.job_install_deps.outputs.dependency_cache_key }} - job_size_check: name: Size Check needs: job_build @@ -295,6 +256,8 @@ jobs: NODE_VERSION: ${{ matrix.node }} run: | [[ $NODE_VERSION == 8 ]] && yarn add --dev --ignore-engines --ignore-scripts --ignore-workspace-root-check ts-node@8.x + # TODO remove this when we switch to sucrase + yarn ts-node scripts/sucrase-test-hack.ts yarn test-ci - name: Compute test coverage uses: codecov/codecov-action@v1 @@ -534,7 +497,7 @@ jobs: job_unit_test_sucrase: name: Sucrase Test (Node ${{ matrix.node }}) - needs: job_build_with_sucrase + needs: job_build continue-on-error: true timeout-minutes: 30 runs-on: ubuntu-latest @@ -554,24 +517,26 @@ jobs: uses: actions/cache@v2 with: path: ${{ env.CACHED_DEPENDENCY_PATHS }} - key: ${{ needs.job_build_with_sucrase.outputs.dependency_cache_key }} + key: ${{ needs.job_build.outputs.dependency_cache_key }} - name: Check build cache uses: actions/cache@v2 with: path: ${{ env.CACHED_BUILD_PATHS }} - key: ${{ env.BUILD_CACHE_KEY }}-sucrase + key: ${{ env.BUILD_CACHE_KEY }} - name: Run tests env: NODE_VERSION: ${{ matrix.node }} + SUCRASE: true run: | - [[ $NODE_VERSION == 8 ]] && yarn add --dev --ignore-engines --ignore-scripts --ignore-workspace-root-check ts-node@8.x + [[ $NODE_VERSION == 8 ]] && yarn add --dev --ignore-engines --ignore-scripts --ignore-workspace-root-check ts-node@8.x rollup@2.2.0 @rollup/plugin-node-resolve@10.x rollup-plugin-terser@5.0.0 rollup-plugin-typescript2@0.30.0 + yarn ts-node scripts/sucrase-test-hack.ts yarn test-ci - name: Compute test coverage uses: codecov/codecov-action@v1 job_nextjs_integration_test_sucrase: name: Sucrase Test @sentry/nextjs on (Node ${{ matrix.node }}) - needs: job_build_with_sucrase + needs: job_build continue-on-error: true timeout-minutes: 30 runs-on: ubuntu-latest @@ -591,16 +556,17 @@ jobs: uses: actions/cache@v2 with: path: ${{ env.CACHED_DEPENDENCY_PATHS }} - key: ${{ needs.job_build_with_sucrase.outputs.dependency_cache_key }} + key: ${{ needs.job_build.outputs.dependency_cache_key }} - name: Check build cache uses: actions/cache@v2 with: path: ${{ env.CACHED_BUILD_PATHS }} - key: ${{ env.BUILD_CACHE_KEY }}-sucrase + key: ${{ env.BUILD_CACHE_KEY }} - name: Run tests env: NODE_VERSION: ${{ matrix.node }} run: | + yarn ts-node scripts/sucrase-test-hack.ts cd packages/nextjs yarn test:integration @@ -608,7 +574,7 @@ jobs: # separate job allows them to run in parallel with the other tests. job_ember_tests_sucrase: name: Sucrase Test @sentry/ember - needs: job_build_with_sucrase + needs: job_build continue-on-error: true timeout-minutes: 30 runs-on: ubuntu-latest @@ -634,20 +600,22 @@ jobs: uses: actions/cache@v2 with: path: ${{ env.CACHED_DEPENDENCY_PATHS }} - key: ${{ needs.job_build_with_sucrase.outputs.dependency_cache_key }} + key: ${{ needs.job_build.outputs.dependency_cache_key }} - name: Check build cache uses: actions/cache@v2 with: path: ${{ env.CACHED_BUILD_PATHS }} - key: ${{ env.BUILD_CACHE_KEY }}-sucrase + key: ${{ env.BUILD_CACHE_KEY }} - name: Run Ember tests - run: yarn test --scope=@sentry/ember + run: | + yarn ts-node scripts/sucrase-test-hack.ts + yarn test --scope=@sentry/ember - name: Compute test coverage uses: codecov/codecov-action@v1 job_browser_playwright_tests_sucrase: name: Sucrase Playwright - ${{ (matrix.tracing_only && 'Browser + Tracing') || 'Browser' }} (${{ matrix.bundle }}) - needs: job_build_with_sucrase + needs: job_build runs-on: ubuntu-latest strategy: matrix: @@ -677,24 +645,25 @@ jobs: uses: actions/cache@v2 with: path: ${{ env.CACHED_DEPENDENCY_PATHS }} - key: ${{ needs.job_build_with_sucrase.outputs.dependency_cache_key }} + key: ${{ needs.job_build.outputs.dependency_cache_key }} - name: Check build cache uses: actions/cache@v2 with: path: ${{ env.CACHED_BUILD_PATHS }} - key: ${{ env.BUILD_CACHE_KEY }}-sucrase + key: ${{ env.BUILD_CACHE_KEY }} - name: Run Playwright tests env: PW_BUNDLE: ${{ matrix.bundle }} PW_TRACING_ONLY: ${{ matrix.tracing_only }} run: | + yarn ts-node scripts/sucrase-test-hack.ts cd packages/integration-tests yarn run playwright install-deps webkit yarn test:ci job_browser_integration_tests_sucrase: name: Sucrase Old Browser Integration Tests (${{ matrix.browser }}) - needs: job_build_with_sucrase + needs: job_build runs-on: ubuntu-latest timeout-minutes: 10 continue-on-error: true @@ -715,23 +684,24 @@ jobs: uses: actions/cache@v2 with: path: ${{ env.CACHED_DEPENDENCY_PATHS }} - key: ${{ needs.job_build_with_sucrase.outputs.dependency_cache_key }} + key: ${{ needs.job_build.outputs.dependency_cache_key }} - name: Check build cache uses: actions/cache@v2 with: path: ${{ env.CACHED_BUILD_PATHS }} - key: ${{ env.BUILD_CACHE_KEY }}-sucrase + key: ${{ env.BUILD_CACHE_KEY }} - name: Run integration tests env: KARMA_BROWSER: ${{ matrix.browser }} run: | + yarn ts-node scripts/sucrase-test-hack.ts cd packages/browser [[ $KARMA_BROWSER == WebkitHeadless ]] && yarn run playwright install-deps webkit yarn test:integration job_browser_build_tests_sucrase: name: Sucrase Browser Build Tests - needs: job_build_with_sucrase + needs: job_build runs-on: ubuntu-latest timeout-minutes: 5 continue-on-error: true @@ -748,14 +718,15 @@ jobs: uses: actions/cache@v2 with: path: ${{ env.CACHED_DEPENDENCY_PATHS }} - key: ${{ needs.job_build_with_sucrase.outputs.dependency_cache_key }} + key: ${{ needs.job_build.outputs.dependency_cache_key }} - name: Check build cache uses: actions/cache@v2 with: path: ${{ env.CACHED_BUILD_PATHS }} - key: ${{ env.BUILD_CACHE_KEY }}-sucrase + key: ${{ env.BUILD_CACHE_KEY }} - name: Run browser build tests run: | + yarn ts-node scripts/sucrase-test-hack.ts cd packages/browser yarn test:package - name: Run utils build tests @@ -765,7 +736,7 @@ jobs: job_node_integration_tests_sucrase: name: Sucrase Node SDK Integration Tests (${{ matrix.node }}) - needs: job_build_with_sucrase + needs: job_build runs-on: ubuntu-latest timeout-minutes: 10 continue-on-error: true @@ -785,15 +756,16 @@ jobs: uses: actions/cache@v2 with: path: ${{ env.CACHED_DEPENDENCY_PATHS }} - key: ${{ needs.job_build_with_sucrase.outputs.dependency_cache_key }} + key: ${{ needs.job_build.outputs.dependency_cache_key }} - name: Check build cache uses: actions/cache@v2 with: path: ${{ env.CACHED_BUILD_PATHS }} - key: ${{ env.BUILD_CACHE_KEY }}-sucrase + key: ${{ env.BUILD_CACHE_KEY }} - name: Run integration tests env: NODE_VERSION: ${{ matrix.node }} run: | + yarn ts-node scripts/sucrase-test-hack.ts cd packages/node-integration-tests yarn test diff --git a/rollup/npmHelpers.js b/rollup/npmHelpers.js index b4397a56f466..71bdc58fb953 100644 --- a/rollup/npmHelpers.js +++ b/rollup/npmHelpers.js @@ -22,7 +22,8 @@ export function makeBaseNPMConfig(options = {}) { const nodeResolvePlugin = makeNodeResolvePlugin(); const sucrasePlugin = makeSucrasePlugin(); - return { + // return { + const config = { input: entrypoints, output: { @@ -32,10 +33,10 @@ export function makeBaseNPMConfig(options = {}) { sourcemap: true, // output individual files rather than one big bundle - preserveModules: true, + // preserveModules: true, // any wrappers or helper functions generated by rollup can use ES6 features - generatedCode: 'es2015', + // generatedCode: 'es2015', // don't add `"use strict"` to the top of cjs files strict: false, @@ -78,6 +79,17 @@ export function makeBaseNPMConfig(options = {}) { // treeshake: 'smallest', treeshake: false, }; + + // temporary hack for testing sucrase bundles before we switch over + if (!process.version.startsWith('v8')) { + console.log('Doing normal preserveModules'); + config.output.preserveModules = true; + } else { + console.log('Doing node 8 preserveModules'); + config.preserveModules = true; + } + + return config; } export function makeNPMConfigVariants(baseConfig) { diff --git a/scripts/sucrase-test-hack.ts b/scripts/sucrase-test-hack.ts new file mode 100644 index 000000000000..e697145abbdb --- /dev/null +++ b/scripts/sucrase-test-hack.ts @@ -0,0 +1,49 @@ +// A temporary hack to use sucrase versions of packages for testing in CI. + +import * as childProcess from 'child_process'; +import * as fs from 'fs'; +import * as path from 'path'; + +function run(cmd: string, options?: childProcess.ExecSyncOptions): unknown { + return childProcess.execSync(cmd, { stdio: 'inherit', ...options }); +} + +const ignorePackages = process.version.startsWith('v8') + ? [ + '@sentry/ember', + '@sentry-internal/eslint-plugin-sdk', + '@sentry/react', + '@sentry/wasm', + '@sentry/gatsby', + '@sentry/serverless', + '@sentry/nextjs', + '@sentry/angular', + ] + : ['@sentry/serverless']; + +// clear current builds and rebuild with rollup/sucrase (this way, all of the extra, random stuff which gets built in +// the main build job remains, and only the part affected by this project gets overwritten) +if (process.env.SUCRASE) { + // just to be super sure + fs.readdirSync(path.join(process.cwd(), 'packages')).forEach(dir => { + if (fs.existsSync(path.join(process.cwd(), 'packages', dir, 'build', 'npm'))) { + run(`rm -rf packages/${dir}/build/npm/cjs`); + run(`rm -rf packages/${dir}/build/npm/esm`); + } else if (fs.existsSync(path.join(process.cwd(), 'packages', dir, 'build', 'cjs'))) { + run(`rm -rf packages/${dir}/build/cjs`); + run(`rm -rf packages/${dir}/build/esm`); + } + }); + + // rebuild the packages we're going to test with rollup/sucrase + run(`yarn build:rollup ${ignorePackages.map(dep => `--ignore="${dep}"`).join(' ')}`); +} +// if we're in tsc-land, rebuild using es5 - temporary until switch to sucrase +else { + const baseTSConfigPath = 'packages/typescript/tsconfig.json'; + fs.writeFileSync( + baseTSConfigPath, + String(fs.readFileSync(baseTSConfigPath)).replace('"target": "es6"', '"target": "es5"'), + ); + run(`yarn build:dev ${ignorePackages.map(dep => `--ignore="${dep}"`).join(' ')}`); +} diff --git a/scripts/test.ts b/scripts/test.ts index ca33e54ee3d0..1232d9f0d104 100644 --- a/scripts/test.ts +++ b/scripts/test.ts @@ -1,5 +1,4 @@ import { spawnSync } from 'child_process'; -import * as fs from 'fs'; import { join } from 'path'; function run(cmd: string, cwd: string = '') { @@ -48,12 +47,6 @@ if (nodeMajorVersion <= 10) { // against a single version of node, but in the short run, this at least allows us to not be blocked by the // failures.) run('rm -rf packages/tracing/test/browser'); - - // TODO Pull this out once we switch to sucrase builds - // Recompile as es5, so as not to have to fix a compatibility problem that will soon be moot - const baseTSConfig = 'packages/typescript/tsconfig.json'; - fs.writeFileSync(baseTSConfig, String(fs.readFileSync(baseTSConfig)).replace('"target": "es6"', '"target": "es5"')); - run(`yarn build:dev ${ignorePackages.map(dep => `--ignore="${dep}"`).join(' ')}`); } // Node 10 else { From 1130c1c89ed5af84c378557e5ce1cf32a46c3ec8 Mon Sep 17 00:00:00 2001 From: Katie Byers Date: Fri, 29 Apr 2022 19:12:36 -0700 Subject: [PATCH 107/204] feat(build): Add various rollup plugins to sucrase build (#4993) This adds four rollup plugins for use in the new build process: - A plugin which converts all `const`s in the output to `var`s, both after sucrase's code modifications and after those done by rollup itself, to make sure that all instances of `const` are caught. This transformation has two advantages: - It prevents errors arising from the way we redefine `global` (as the return value of `getGlobalObject`), since `var`s can be redeclared where `const`s can't. - It replaces a very common token which is four letters with one which is three letters. Not a huge bundle size savings, but it at least prevents us from going the wrong direction. (Our current builds use `var` because they are ES5, so this puts the new build in line with the current build, number-of-characters-in-variable-specifiers-wise.) - A plugin which strips eslint-style comments and another which reduces multiple consecutive blank lines down to one. Neither of these affects either code behavior or bundle size, but they do make the eventual output a little less messy. One of the odd quirks of sucrase is that it has the goal of never changing the line number of an expression[1], in order that even stacktraces from un-sourcemapped code are meaningful, and this can result in some odd-looking files. These two plugins don't totally undo the oddness, but they're simple and fast and certainly don't hurt readability. - A plugin to be used during our development, which allows you to place a debugger in the rollup hook[2] of your choice. [1] https://github.com/alangpierce/sucrase/issues/452#issuecomment-522278591 [2] https://rollupjs.org/guide/en/#build-hooks --- package.json | 1 + rollup/npmHelpers.js | 13 +++++- rollup/plugins/npmPlugins.js | 83 ++++++++++++++++++++++++++++++++++++ yarn.lock | 32 ++++++++++++++ 4 files changed, 127 insertions(+), 2 deletions(-) diff --git a/package.json b/package.json index e3cccf304ea0..f2b81a98123e 100644 --- a/package.json +++ b/package.json @@ -85,6 +85,7 @@ "rimraf": "^3.0.2", "rollup": "^2.67.1", "rollup-plugin-license": "^2.6.1", + "rollup-plugin-re": "^1.0.7", "rollup-plugin-terser": "^7.0.2", "rollup-plugin-typescript2": "^0.31.2", "sinon": "^7.3.2", diff --git a/rollup/npmHelpers.js b/rollup/npmHelpers.js index 71bdc58fb953..79a14189a880 100644 --- a/rollup/npmHelpers.js +++ b/rollup/npmHelpers.js @@ -7,7 +7,13 @@ import * as path from 'path'; import deepMerge from 'deepmerge'; -import { makeNodeResolvePlugin, makeSucrasePlugin } from './plugins/index.js'; +import { + makeConstToVarPlugin, + makeNodeResolvePlugin, + makeRemoveBlankLinesPlugin, + makeRemoveESLintCommentsPlugin, + makeSucrasePlugin, +} from './plugins/index.js'; const packageDotJSON = require(path.resolve(process.cwd(), './package.json')); @@ -21,6 +27,9 @@ export function makeBaseNPMConfig(options = {}) { const nodeResolvePlugin = makeNodeResolvePlugin(); const sucrasePlugin = makeSucrasePlugin(); + const constToVarPlugin = makeConstToVarPlugin(); + const removeESLintCommentsPlugin = makeRemoveESLintCommentsPlugin(); + const removeBlankLinesPlugin = makeRemoveBlankLinesPlugin(); // return { const config = { @@ -62,7 +71,7 @@ export function makeBaseNPMConfig(options = {}) { interop: esModuleInterop ? 'auto' : 'esModule', }, - plugins: [nodeResolvePlugin, sucrasePlugin], + plugins: [nodeResolvePlugin, sucrasePlugin, constToVarPlugin, removeESLintCommentsPlugin, removeBlankLinesPlugin], // don't include imported modules from outside the package in the final output external: [ diff --git a/rollup/plugins/npmPlugins.js b/rollup/plugins/npmPlugins.js index 5f4c443682ef..568ef6b69e10 100644 --- a/rollup/plugins/npmPlugins.js +++ b/rollup/plugins/npmPlugins.js @@ -1,7 +1,13 @@ /** + * Regex Replace plugin docs: https://github.com/jetiny/rollup-plugin-re + * Replace plugin docs: https://github.com/rollup/plugins/tree/master/packages/replace * Sucrase plugin docs: https://github.com/rollup/plugins/tree/master/packages/sucrase */ +// We need both replacement plugins because one handles regex and the other runs both before and after rollup does its +// bundling work. +import regexReplace from 'rollup-plugin-re'; +import replace from '@rollup/plugin-replace'; import sucrase from '@rollup/plugin-sucrase'; /** @@ -14,3 +20,80 @@ export function makeSucrasePlugin() { transforms: ['typescript', 'jsx'], }); } + +/** + * Create a plugin to switch all instances of `const` to `var`, both to prevent problems when we shadow `global` and + * because it's fewer characters. + * + * Note that the generated plugin runs the replacement both before and after rollup does its code manipulation, to + * increase the chances that nothing is missed. + * + * TODO This is pretty brute-force-y. Perhaps we could switch to using a parser, the way we (will) do for both our jest + * transformer and the polyfill build script. + * + * @returns An instance of the `@rollup/plugin-replace` plugin + */ +export function makeConstToVarPlugin() { + return replace({ + // TODO `preventAssignment` will default to true in version 5.x of the replace plugin, at which point we can get rid + // of this. (It actually makes no difference in this case whether it's true or false, since we never assign to + // `const`, but if we don't give it a value, it will spam with warnings.) + preventAssignment: true, + values: { + // Include a space at the end to guarantee we're not accidentally catching the beginning of the words "constant," + // "constantly," etc. + 'const ': 'var ', + }, + }); +} + +/** + * Create a plugin which can be used to pause the build process at the given hook. + * + * Hooks can be found here: https://rollupjs.org/guide/en/#build-hooks + * + * @param hookName The name of the hook at which to pause. + * @returns A plugin which inserts a debugger statement in the phase represented by the given hook + */ +export function makeDebuggerPlugin(hookName) { + return { + name: 'debugger-plugin', + [hookName]: () => { + // eslint-disable-next-line no-debugger + debugger; + return null; + }, + }; +} + +/** + * Create a plugin to strip eslint-style comments from the output. + * + * @returns A `rollup-plugin-re` instance. + */ +export function makeRemoveESLintCommentsPlugin() { + return regexReplace({ + patterns: [ + { + test: /\/[/*] eslint-disable.*\n/g, + replace: '', + }, + ], + }); +} + +/** + * Create a plugin to strip multiple consecutive blank lines, with or without whitespace in them. from the output. + * + * @returns A `rollup-plugin-re` instance. + */ +export function makeRemoveBlankLinesPlugin() { + return regexReplace({ + patterns: [ + { + test: /\n(\n\s*)+\n/g, + replace: '\n\n', + }, + ], + }); +} diff --git a/yarn.lock b/yarn.lock index f6ba304563d7..bf506395377a 100644 --- a/yarn.lock +++ b/yarn.lock @@ -12051,6 +12051,11 @@ estraverse@^5.1.0, estraverse@^5.2.0: resolved "https://registry.yarnpkg.com/estraverse/-/estraverse-5.2.0.tgz#307df42547e6cc7324d3cf03c155d5cdb8c53880" integrity sha512-BxbNGGNm0RyRYvUdHpIwv9IWzeM9XClbOxwoATuFdOE7ZE6wHL+HQ5T8hoPM+zHvmKzzsEqhgy0GrQ5X13afiQ== +estree-walker@^0.6.1: + version "0.6.1" + resolved "https://registry.yarnpkg.com/estree-walker/-/estree-walker-0.6.1.tgz#53049143f40c6eb918b23671d1fe3219f3a1b362" + integrity sha512-SqmZANLWS0mnatqbSfRP5g8OXZC12Fgg1IwNtLsyHDzJizORW4khDfjPqJZsemPWBB2uqykUah5YpQ6epsqC/w== + estree-walker@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/estree-walker/-/estree-walker-1.0.1.tgz#31bc5d612c96b704106b477e6dd5d8aa138cb700" @@ -17307,6 +17312,13 @@ magic-string@0.25.7, magic-string@^0.25.1, magic-string@^0.25.7: dependencies: sourcemap-codec "^1.4.4" +magic-string@^0.16.0: + version "0.16.0" + resolved "https://registry.yarnpkg.com/magic-string/-/magic-string-0.16.0.tgz#970ebb0da7193301285fb1aa650f39bdd81eb45a" + integrity sha1-lw67DacZMwEoX7GqZQ85vdgetFo= + dependencies: + vlq "^0.2.1" + magic-string@^0.25.0: version "0.25.9" resolved "https://registry.yarnpkg.com/magic-string/-/magic-string-0.25.9.tgz#de7f9faf91ef8a1c91d02c2e5314c8277dbcdd1c" @@ -21872,6 +21884,14 @@ rollup-plugin-license@^2.6.1: spdx-expression-validate "2.0.0" spdx-satisfies "5.0.1" +rollup-plugin-re@^1.0.7: + version "1.0.7" + resolved "https://registry.yarnpkg.com/rollup-plugin-re/-/rollup-plugin-re-1.0.7.tgz#fe174704ed59cda84caf02bd013b582e6fdaa4f6" + integrity sha1-/hdHBO1ZzahMrwK9ATtYLm/apPY= + dependencies: + magic-string "^0.16.0" + rollup-pluginutils "^2.0.1" + rollup-plugin-sourcemaps@^0.6.0: version "0.6.3" resolved "https://registry.yarnpkg.com/rollup-plugin-sourcemaps/-/rollup-plugin-sourcemaps-0.6.3.tgz#bf93913ffe056e414419607f1d02780d7ece84ed" @@ -21902,6 +21922,13 @@ rollup-plugin-typescript2@^0.31.2: resolve "^1.20.0" tslib "^2.3.1" +rollup-pluginutils@^2.0.1: + version "2.8.2" + resolved "https://registry.yarnpkg.com/rollup-pluginutils/-/rollup-pluginutils-2.8.2.tgz#72f2af0748b592364dbd3389e600e5a9444a351e" + integrity sha512-EEp9NhnUkwY8aif6bxgovPHMoMoNr2FulJziTndpt5H9RdwC47GSGuII9XxpSdzVGM0GWrNPHV6ie1LTNJPaLQ== + dependencies: + estree-walker "^0.6.1" + rollup@2.26.5: version "2.26.5" resolved "https://registry.yarnpkg.com/rollup/-/rollup-2.26.5.tgz#5562ec36fcba3eed65cfd630bd78e037ad0e0307" @@ -24953,6 +24980,11 @@ verror@1.10.0: core-util-is "1.0.2" extsprintf "^1.2.0" +vlq@^0.2.1: + version "0.2.3" + resolved "https://registry.yarnpkg.com/vlq/-/vlq-0.2.3.tgz#8f3e4328cf63b1540c0d67e1b2778386f8975b26" + integrity sha512-DRibZL6DsNhIgYQ+wNdWDL2SL3bKPlVrRiBqV5yuMm++op8W4kGFtaQfCs4KEJn0wBZcHVHJ3eoywX8983k1ow== + vm-browserify@1.1.0: version "1.1.0" resolved "https://registry.yarnpkg.com/vm-browserify/-/vm-browserify-1.1.0.tgz#bd76d6a23323e2ca8ffa12028dc04559c75f9019" From 16356cbe31b0d1deeb69388409976df4b00c2034 Mon Sep 17 00:00:00 2001 From: Katie Byers Date: Mon, 2 May 2022 12:46:02 -0700 Subject: [PATCH 108/204] fix(tests): Add `const`-to-`var` ts-jest transformer (#5022) This does, during tests, what the `const`-to-`var` plugin in https://github.com/getsentry/sentry-javascript/pull/4993 does for our built code. Though for tests bundle size isn't a concern, the first point raised there (of our shadowing of `global` being an issue now that we're ES6ified and living in a land of `const`s) stands, though in this case it seems to only affect Node 8. Since `ts-jest` doesn't use our built code, but instead compiles our TS on the fly, the existing plugin is of no help. Hence this transformer, which `ts-jest `will apply after it has transpiled our code to JS. It works by providing a visitor function which `ts-jest` passes on to TS, which then lets it loose on the AST. In our case (as you'd expect) our visitor stops on every `const` variable declaration node and replaces it with an equivalent `var` declaration node, and ignores all other nodes. Since it's only needed for node 8, we leave transpilation of the transformer itself and injection into our jest config until runtime on the Node 8 CI machine. As part of this work, the script which runs tests in CI (whose main job is to ensure compatibility with older versions of Node, and to which the aforementioned transpilation and injection were added) has been reorganized into functions, for (hopefully) better readability. --- .gitignore | 2 + jest/transformers/constReplacer.ts | 84 +++++++++++++++ scripts/test.ts | 165 ++++++++++++++++++++--------- 3 files changed, 199 insertions(+), 52 deletions(-) create mode 100644 jest/transformers/constReplacer.ts diff --git a/.gitignore b/.gitignore index ae301fadfde7..430a977178a7 100644 --- a/.gitignore +++ b/.gitignore @@ -12,6 +12,8 @@ scratch/ *.pyc *.tsbuildinfo scenarios/*/dist/ +# transpiled transformers +jest/transformers/*.js # logs yarn-error.log diff --git a/jest/transformers/constReplacer.ts b/jest/transformers/constReplacer.ts new file mode 100644 index 000000000000..725aa9ac48c8 --- /dev/null +++ b/jest/transformers/constReplacer.ts @@ -0,0 +1,84 @@ +/** + * This is a transformer which `ts-jest` applies during the compilation process, which switches all of the `const`s out + * for `var`s. Unlike in our package builds, where we do the same substiution for bundle size reasons, here we do it + * because otherwise `const global = getGlobalObject()` throws an error about redifining `global`. (This didn't used to + * be a problem because our down-compilation did the `const`-`var` substitution for us, but now that we're ES6-only, we + * have to do it ourselves.) + * + * Note: If you ever have to change this, and are testing it locally in the process, be sure to call + * `yarn jest --clearCache` + * before each test run, as transformation results are cached between runs. + */ + +import { + createVariableDeclarationList, + getCombinedNodeFlags, + isVariableDeclarationList, + Node, + NodeFlags, + SourceFile, + TransformationContext, + Transformer, + TransformerFactory, + visitEachChild, + visitNode, + VisitResult, +} from 'typescript'; + +// These can be anything - they're just used to construct a cache key for the transformer returned by the factory below. +// This really only matters when you're testing the transformer itself, as changing these values gives you a quick way +// to invalidate the cache and ensure that changes you've made to the code here are immediately picked up on and used. +export const name = 'const-to-var'; +export const version = '1.0'; + +/** + * Check whether the given AST node represents a `const` token. + * + * This function comes from the TS compiler, and is copied here to get around the fact that it's not exported by the + * `typescript` package. + * + * @param node The node to check + * @returns A boolean indicating if the node is a `const` token. + */ +function isVarConst(node: Node): boolean { + // eslint-disable-next-line no-bitwise + return !!(getCombinedNodeFlags(node) & NodeFlags.Const); +} + +/** + * Return a set of nested factory functions, which ultimately creates an AST-node visitor function, which can modify + * each visited node as it sees fit, and uses it to walk the AST, returning the results. + * + * In our case, we're modifying all `const` variable declarations to use `var` instead. + */ +export function factory(): TransformerFactory { + // Create the transformer + function transformerFactory(context: TransformationContext): Transformer { + // Create a visitor function and use it to walk the AST + function transformer(sourceFile: SourceFile): SourceFile { + // This visitor function can either return a node, in which case the subtree rooted at the returned node is + // substituted for the subtree rooted at the visited node, or can use the recursive `visitEachChild` function + // provided by TS to continue traversing the tree. + function visitor(node: Node): VisitResult { + // If we've found a `const` declaration, return a `var` declaration in its place + if (isVariableDeclarationList(node) && isVarConst(node)) { + // A declaration list with a `None` flag defaults to using `var` + return createVariableDeclarationList(node.declarations, NodeFlags.None); + } + + // This wasn't a node we're interested in, so keep walking down the tree. + return visitEachChild(node, visitor, context); + } + + // Having defined our visitor, pass it to the TS-provided `visitNode` function, which will use it to walk the AST, + // and return the results of that walk. + return visitNode(sourceFile, visitor); + } + + // Back in the transformer factory, return the transformer we just created + return transformer; + } + + // Finally, we're back in `factory`, and can return the whole nested system + return transformerFactory; +} diff --git a/scripts/test.ts b/scripts/test.ts index 1232d9f0d104..1aa4dc6ac8b1 100644 --- a/scripts/test.ts +++ b/scripts/test.ts @@ -1,66 +1,127 @@ -import { spawnSync } from 'child_process'; -import { join } from 'path'; +import * as childProcess from 'child_process'; +import * as fs from 'fs'; +import * as path from 'path'; -function run(cmd: string, cwd: string = '') { - const result = spawnSync(cmd, { shell: true, stdio: 'inherit', cwd: join(__dirname, '..', cwd || '') }); +const CURRENT_NODE_VERSION = process.version.replace('v', '').split('.')[0]; - if (result.status !== 0) { - process.exit(result.status || undefined); - } -} +// We run ember tests in their own job. +const DEFAULT_SKIP_TESTS_PACKAGES = ['@sentry/ember']; +// These packages don't support Node 8 for syntax or dependency reasons. +const NODE_8_SKIP_TESTS_PACKAGES = [ + ...DEFAULT_SKIP_TESTS_PACKAGES, + '@sentry-internal/eslint-plugin-sdk', + '@sentry/react', + '@sentry/wasm', + '@sentry/gatsby', + '@sentry/serverless', + '@sentry/nextjs', + '@sentry/angular', +]; -const nodeMajorVersion = parseInt(process.version.split('.')[0].replace('v', ''), 10); +// We have to downgrade some of our dependencies in order to run tests in Node 8 and 10. +const NODE_8_LEGACY_DEPENDENCIES = [ + 'jsdom@15.x', + 'jest@25.x', + 'jest-environment-jsdom@25.x', + 'jest-environment-node@25.x', + 'ts-jest@25.x', +]; +const NODE_10_LEGACY_DEPENDENCIES = ['jsdom@16.x']; -// Ember tests require dependency changes for each set of tests, making them quite slow. To compensate for this, in CI -// we run them in a separate, parallel job. -let ignorePackages = ['@sentry/ember']; +/** + * Run the given shell command, piping the shell process's `stdin`, `stdout`, and `stderr` to that of the current + * process. Returns contents of `stdout`. + */ +function run(cmd: string, options?: childProcess.ExecSyncOptions) { + return childProcess.execSync(cmd, { stdio: 'inherit', ...options }); +} -// install legacy versions of third-party packages whose current versions don't support node 8 or 10, and skip testing -// our own packages which don't support node 8 for various syntax or dependency reasons -if (nodeMajorVersion <= 10) { - let legacyDependencies; +/** + * Install the given legacy dependencies, for compatibility with tests run in older versions of Node. + */ +function installLegacyDeps(legacyDeps: string[] = []): void { + // Ignoring engines and scripts lets us get away with having incompatible things installed for SDK packages we're not + // testing in the current node version, and ignoring the root check lets us install things at the repo root. + run(`yarn add --dev --ignore-engines --ignore-scripts --ignore-workspace-root-check ${legacyDeps.join(' ')}`); +} - if (nodeMajorVersion === 8) { - legacyDependencies = [ - 'jsdom@15.x', - 'jest@25.x', - 'jest-environment-jsdom@25.x', - 'jest-environment-node@25.x', - 'ts-jest@25.x', - ]; +/** + * Add a tranformer to our jest config, to do the same `const`-to-`var` replacement as our rollup plugin does. + * + * This is needed because Node 8 doesn't like the way we shadow `global` (`const global = getGlobalObject()`). Changing + * it to a `var` solves this by making it redeclarable. + * + */ +function addTransformer(): void { + // Though newer `ts-jest` versions support transformers written in TS, the legacy version does not. + run('yarn tsc --skipLibCheck jest/transformers/constReplacer.ts'); - ignorePackages = [ - ...ignorePackages, - '@sentry-internal/eslint-plugin-sdk', - '@sentry/react', - '@sentry/wasm', - '@sentry/gatsby', - '@sentry/serverless', - '@sentry/nextjs', - '@sentry/angular', - ]; + // Loading the existing Jest config will error out unless the config file has an accompanying types file, so we have + // to create that before we can load it. + run('yarn tsc --allowJs --skipLibCheck --declaration --emitDeclarationOnly jest/jest.config.js'); + // eslint-disable-next-line @typescript-eslint/no-var-requires + const jestConfig = require('../jest/jest.config.js'); - // This is a hack, to deal the fact that the browser-based tests fail under Node 8, because of a conflict buried - // somewhere in the interaction between our current overall set of dependencies and the older versions of a small - // subset we're about to install below. Since they're browser-based, these tests are never going to be running in a - // node 8 environment in any case, so it's fine to skip them here. (In the long run, we should only run such tests - // against a single version of node, but in the short run, this at least allows us to not be blocked by the - // failures.) - run('rm -rf packages/tracing/test/browser'); - } - // Node 10 - else { - legacyDependencies = ['jsdom@16.x']; - } + // Inject the transformer + jestConfig.globals['ts-jest'].astTransformers = ['/../../jest/transformers/constReplacer.js']; - const legacyDepStr = legacyDependencies.join(' '); + // When we required the jest config file above, all expressions it contained were evaluated. Specifically, the + // `rootDir: process.cwd()` + // entry was replaced with + // `rootDir: ""`, + // Though it's a little brute-force-y, the easiest way to fix this is to just stringify the code and perform the + // substitution in reverse. + const stringifiedConfig = JSON.stringify(jestConfig, null, 2).replace( + `"rootDir": "${process.cwd()}"`, + 'rootDir: process.cwd()', + ); - // ignoring engines and scripts lets us get away with having incompatible things installed for packages we're not testing - run(`yarn add --dev --ignore-engines --ignore-scripts --ignore-workspace-root-check ${legacyDepStr}`); + // Now we just have to convert it back to a module and write it to disk + const code = `module.exports = ${stringifiedConfig}`; + fs.writeFileSync(path.resolve('jest/jest.config.js'), code); } -const ignoreFlags = ignorePackages.map(dep => `--ignore="${dep}"`).join(' '); +/** + * Skip tests which don't apply to Node and therefore don't need to run in older Node versions. + * + * TODO We're foreced to skip these tests for compatibility reasons (right now this function only gets called in Node + * 8), but we could be skipping a lot more tests in Node 8-14 - anything where compatibility with different Node + * versions is irrelevant - and only running them in Node 16. + */ +function skipNonNodeTests(): void { + run('rm -rf packages/tracing/test/browser'); +} -run(`yarn test ${ignoreFlags}`); +/** + * Run tests, ignoring the given packages + */ +function runWithIgnores(skipPackages: string[] = []): void { + const ignoreFlags = skipPackages.map(dep => `--ignore="${dep}"`).join(' '); + run(`yarn test ${ignoreFlags}`); +} + +/** + * Run the tests, accounting for compatibility problems in older versions of Node. + */ +function runTests(): void { + if (CURRENT_NODE_VERSION === '8') { + installLegacyDeps(NODE_8_LEGACY_DEPENDENCIES); + // Inject a `const`-to-`var` transformer, in order to stop Node 8 from complaining when we shadow `global` + addTransformer(); + // TODO Right now, this just skips incompatible tests, but it could be skipping more (hence the aspirational name), + // and not just in Node 8. See `skipNonNodeTests`'s docstring. + skipNonNodeTests(); + runWithIgnores(NODE_8_SKIP_TESTS_PACKAGES); + } + // + else if (CURRENT_NODE_VERSION === '10') { + installLegacyDeps(NODE_10_LEGACY_DEPENDENCIES); + runWithIgnores(DEFAULT_SKIP_TESTS_PACKAGES); + } + // + else { + runWithIgnores(DEFAULT_SKIP_TESTS_PACKAGES); + } +} -process.exit(0); +runTests(); From 2df499bed6309017039b835bc35e736f5199ddeb Mon Sep 17 00:00:00 2001 From: Abhijeet Prasad Date: Tue, 3 May 2022 07:12:29 -0400 Subject: [PATCH 109/204] ref: Make it easier to use stackParser (#5015) Co-authored-by: Luca Forstner --- MIGRATION.md | 36 +++++++++++++++++++ packages/browser/src/eventbuilder.ts | 2 +- packages/browser/src/exports.ts | 14 ++++---- packages/browser/src/index.ts | 3 +- packages/browser/src/sdk.ts | 6 ++-- packages/browser/src/stack-parsers.ts | 15 ++++---- .../unit/integrations/linkederrors.test.ts | 5 +-- .../test/unit/tracekit/chromium.test.ts | 6 +--- .../test/unit/tracekit/firefox.test.ts | 6 +--- .../browser/test/unit/tracekit/ie.test.ts | 6 +--- .../browser/test/unit/tracekit/misc.test.ts | 6 +--- .../browser/test/unit/tracekit/opera.test.ts | 6 ++-- .../test/unit/tracekit/react-native.test.ts | 6 +--- .../browser/test/unit/tracekit/react.test.ts | 6 +--- .../browser/test/unit/tracekit/safari.test.ts | 6 +--- packages/node/src/index.ts | 6 ++-- packages/node/src/sdk.ts | 6 ++-- packages/node/src/stack-parser.ts | 6 ++-- packages/node/test/context-lines.test.ts | 13 +++---- packages/node/test/index.test.ts | 23 ++++++------ .../test/integrations/linkederrors.test.ts | 5 +-- packages/node/test/stacktrace.test.ts | 8 ++--- packages/tracing/src/index.bundle.ts | 3 +- packages/utils/src/stacktrace.ts | 2 +- packages/vue/src/index.bundle.ts | 3 +- 25 files changed, 102 insertions(+), 102 deletions(-) diff --git a/MIGRATION.md b/MIGRATION.md index 646fb4e86036..a7809c188296 100644 --- a/MIGRATION.md +++ b/MIGRATION.md @@ -32,6 +32,42 @@ import { } from '@sentry/minimal'; ``` +## Explicit Client Options + +In v7, we've updated the `Client` to have options seperate from the options passed into `Sentry.init`. This means that constructing a client now requires 3 options: `integrations`, `transport` and `stackParser`. These can be customized as you see fit. + +```ts +import { BrowserClient, defaultStackParser, defaultIntegrations, makeFetchTransport } from '@sentry/browser'; + +// New in v7: +const client = new BrowserClient({ + transport: makeFetchTransport, + stackParser: defaultStackParser, + integrations: [...defaultIntegrations], +}); + +// Before: +const client = new BrowserClient(); +``` + +Since you now explicitly pass in the dependencies of the client, you can also tree-shake out dependencies that you do not use this way. For example, you can tree-shake out the SDK's default integrations and only use the ones that you want like so: + +```ts +import { BrowserClient, defaultStackParser, Integrations, makeFetchTransport } from '@sentry/browser'; + +// New in v7: +const client = new BrowserClient({ + transport: makeFetchTransport, + stackParser: defaultStackParser, + integrations: [ + new Integrations.Breadcrumbs(), + new Integrations.GlobalHandlers(), + new Integrations.LinkedErrors(), + new Integrations.Dedupe(), + ], +}); +``` + ## Removal Of Old Platform Integrations From `@sentry/integrations` Package The following classes will be removed from the `@sentry/integrations` package and can no longer be used: diff --git a/packages/browser/src/eventbuilder.ts b/packages/browser/src/eventbuilder.ts index c3052c41dd48..2324fcd8b296 100644 --- a/packages/browser/src/eventbuilder.ts +++ b/packages/browser/src/eventbuilder.ts @@ -14,7 +14,7 @@ import { } from '@sentry/utils'; /** - * This function creates an exception from an TraceKitStackTrace + * This function creates an exception from a JavaScript Error */ export function exceptionFromError(stackParser: StackParser, ex: Error): Exception { // Get the frames first since Opera can lose the stack if we touch anything else first diff --git a/packages/browser/src/exports.ts b/packages/browser/src/exports.ts index 93a45d353ea9..2ec436c05b84 100644 --- a/packages/browser/src/exports.ts +++ b/packages/browser/src/exports.ts @@ -44,13 +44,15 @@ export { } from '@sentry/core'; export { BrowserClient } from './client'; +export { makeFetchTransport, makeXHRTransport } from './transports'; export { - defaultStackParsers, - chromeStackParser, - geckoStackParser, - opera10StackParser, - opera11StackParser, - winjsStackParser, + defaultStackParser, + defaultStackLineParsers, + chromeStackLineParser, + geckoStackLineParser, + opera10StackLineParser, + opera11StackLineParser, + winjsStackLineParser, } from './stack-parsers'; export { defaultIntegrations, forceLoad, init, lastEventId, onLoad, showReportDialog, flush, close, wrap } from './sdk'; export { SDK_NAME } from './version'; diff --git a/packages/browser/src/index.ts b/packages/browser/src/index.ts index 0b94c020592f..ab754f6a192f 100644 --- a/packages/browser/src/index.ts +++ b/packages/browser/src/index.ts @@ -4,7 +4,6 @@ import { Integrations as CoreIntegrations } from '@sentry/core'; import { getGlobalObject } from '@sentry/utils'; import * as BrowserIntegrations from './integrations'; -import * as Transports from './transports'; let windowIntegrations = {}; @@ -20,4 +19,4 @@ const INTEGRATIONS = { ...BrowserIntegrations, }; -export { INTEGRATIONS as Integrations, Transports }; +export { INTEGRATIONS as Integrations }; diff --git a/packages/browser/src/sdk.ts b/packages/browser/src/sdk.ts index 3740a1318f0a..98dd9e6e3270 100644 --- a/packages/browser/src/sdk.ts +++ b/packages/browser/src/sdk.ts @@ -11,7 +11,7 @@ import { getGlobalObject, logger, resolvedSyncPromise, - stackParserFromOptions, + stackParserFromStackParserOptions, supportsFetch, } from '@sentry/utils'; @@ -19,7 +19,7 @@ import { BrowserClient, BrowserClientOptions, BrowserOptions } from './client'; import { IS_DEBUG_BUILD } from './flags'; import { ReportDialogOptions, wrap as internalWrap } from './helpers'; import { Breadcrumbs, Dedupe, GlobalHandlers, LinkedErrors, TryCatch, UserAgent } from './integrations'; -import { defaultStackParsers } from './stack-parsers'; +import { defaultStackParser } from './stack-parsers'; import { makeFetchTransport, makeXHRTransport } from './transports'; export const defaultIntegrations = [ @@ -110,7 +110,7 @@ export function init(options: BrowserOptions = {}): void { const clientOptions: BrowserClientOptions = { ...options, - stackParser: stackParserFromOptions(options.stackParser || defaultStackParsers), + stackParser: stackParserFromStackParserOptions(options.stackParser || defaultStackParser), integrations: getIntegrationsToSetup(options), transport: options.transport || (supportsFetch() ? makeFetchTransport : makeXHRTransport), }; diff --git a/packages/browser/src/stack-parsers.ts b/packages/browser/src/stack-parsers.ts index 5d58e9865cd4..0d01878ae7fe 100644 --- a/packages/browser/src/stack-parsers.ts +++ b/packages/browser/src/stack-parsers.ts @@ -1,4 +1,5 @@ import { StackFrame, StackLineParser, StackLineParserFn } from '@sentry/types'; +import { createStackParser } from '@sentry/utils'; // global reference to slice const UNKNOWN_FUNCTION = '?'; @@ -60,7 +61,7 @@ const chrome: StackLineParserFn = line => { return; }; -export const chromeStackParser: StackLineParser = [CHROME_PRIORITY, chrome]; +export const chromeStackLineParser: StackLineParser = [CHROME_PRIORITY, chrome]; // gecko regex: `(?:bundle|\d+\.js)`: `bundle` is for react native, `\d+\.js` also but specifically for ram bundles because it // generates filenames without a prefix like `file://` the filenames in the stacktrace are just 42.js @@ -96,7 +97,7 @@ const gecko: StackLineParserFn = line => { return; }; -export const geckoStackParser: StackLineParser = [GECKO_PRIORITY, gecko]; +export const geckoStackLineParser: StackLineParser = [GECKO_PRIORITY, gecko]; const winjsRegex = /^\s*at (?:((?:\[object object\])?.+) )?\(?((?:file|ms-appx|https?|webpack|blob):.*?):(\d+)(?::(\d+))?\)?\s*$/i; @@ -109,7 +110,7 @@ const winjs: StackLineParserFn = line => { : undefined; }; -export const winjsStackParser: StackLineParser = [WINJS_PRIORITY, winjs]; +export const winjsStackLineParser: StackLineParser = [WINJS_PRIORITY, winjs]; const opera10Regex = / line (\d+).*script (?:in )?(\S+)(?:: in function (\S+))?$/i; @@ -118,7 +119,7 @@ const opera10: StackLineParserFn = line => { return parts ? createFrame(parts[2], parts[3] || UNKNOWN_FUNCTION, +parts[1]) : undefined; }; -export const opera10StackParser: StackLineParser = [OPERA10_PRIORITY, opera10]; +export const opera10StackLineParser: StackLineParser = [OPERA10_PRIORITY, opera10]; const opera11Regex = / line (\d+), column (\d+)\s*(?:in (?:]+)>|([^)]+))\(.*\))? in (.*):\s*$/i; @@ -128,9 +129,11 @@ const opera11: StackLineParserFn = line => { return parts ? createFrame(parts[5], parts[3] || parts[4] || UNKNOWN_FUNCTION, +parts[1], +parts[2]) : undefined; }; -export const opera11StackParser: StackLineParser = [OPERA11_PRIORITY, opera11]; +export const opera11StackLineParser: StackLineParser = [OPERA11_PRIORITY, opera11]; -export const defaultStackParsers = [chromeStackParser, geckoStackParser, winjsStackParser]; +export const defaultStackLineParsers = [chromeStackLineParser, geckoStackLineParser, winjsStackLineParser]; + +export const defaultStackParser = createStackParser(...defaultStackLineParsers); /** * Safari web extensions, starting version unknown, can produce "frames-only" stacktraces. diff --git a/packages/browser/test/unit/integrations/linkederrors.test.ts b/packages/browser/test/unit/integrations/linkederrors.test.ts index 445a4fde8699..1a80a86093b2 100644 --- a/packages/browser/test/unit/integrations/linkederrors.test.ts +++ b/packages/browser/test/unit/integrations/linkederrors.test.ts @@ -1,13 +1,10 @@ import { Event as SentryEvent, Exception, ExtendedError } from '@sentry/types'; -import { createStackParser } from '@sentry/utils'; import { BrowserClient } from '../../../src/client'; import * as LinkedErrorsModule from '../../../src/integrations/linkederrors'; -import { defaultStackParsers } from '../../../src/stack-parsers'; +import { defaultStackParser as parser } from '../../../src/stack-parsers'; import { getDefaultBrowserClientOptions } from '../helper/browser-client-options'; -const parser = createStackParser(...defaultStackParsers); - type EventWithException = SentryEvent & { exception: { values: Exception[]; diff --git a/packages/browser/test/unit/tracekit/chromium.test.ts b/packages/browser/test/unit/tracekit/chromium.test.ts index 67189984563a..183ffaa7eb97 100644 --- a/packages/browser/test/unit/tracekit/chromium.test.ts +++ b/packages/browser/test/unit/tracekit/chromium.test.ts @@ -1,9 +1,5 @@ -import { createStackParser } from '@sentry/utils'; - import { exceptionFromError } from '../../../src/eventbuilder'; -import { defaultStackParsers } from '../../../src/stack-parsers'; - -const parser = createStackParser(...defaultStackParsers); +import { defaultStackParser as parser } from '../../../src/stack-parsers'; describe('Tracekit - Chrome Tests', () => { it('should parse Chrome error with no location', () => { diff --git a/packages/browser/test/unit/tracekit/firefox.test.ts b/packages/browser/test/unit/tracekit/firefox.test.ts index 87929568c857..a9f8ee2d50cb 100644 --- a/packages/browser/test/unit/tracekit/firefox.test.ts +++ b/packages/browser/test/unit/tracekit/firefox.test.ts @@ -1,9 +1,5 @@ -import { createStackParser } from '@sentry/utils'; - import { exceptionFromError } from '../../../src/eventbuilder'; -import { defaultStackParsers } from '../../../src/stack-parsers'; - -const parser = createStackParser(...defaultStackParsers); +import { defaultStackParser as parser } from '../../../src/stack-parsers'; describe('Tracekit - Firefox Tests', () => { it('should parse Firefox 3 error', () => { diff --git a/packages/browser/test/unit/tracekit/ie.test.ts b/packages/browser/test/unit/tracekit/ie.test.ts index 9a796060f1cc..544542b0dcaf 100644 --- a/packages/browser/test/unit/tracekit/ie.test.ts +++ b/packages/browser/test/unit/tracekit/ie.test.ts @@ -1,9 +1,5 @@ -import { createStackParser } from '@sentry/utils'; - import { exceptionFromError } from '../../../src/eventbuilder'; -import { defaultStackParsers } from '../../../src/stack-parsers'; - -const parser = createStackParser(...defaultStackParsers); +import { defaultStackParser as parser } from '../../../src/stack-parsers'; describe('Tracekit - IE Tests', () => { it('should parse IE 10 error', () => { diff --git a/packages/browser/test/unit/tracekit/misc.test.ts b/packages/browser/test/unit/tracekit/misc.test.ts index 976f39e2449e..e9db457ea196 100644 --- a/packages/browser/test/unit/tracekit/misc.test.ts +++ b/packages/browser/test/unit/tracekit/misc.test.ts @@ -1,9 +1,5 @@ -import { createStackParser } from '@sentry/utils'; - import { exceptionFromError } from '../../../src/eventbuilder'; -import { defaultStackParsers } from '../../../src/stack-parsers'; - -const parser = createStackParser(...defaultStackParsers); +import { defaultStackParser as parser } from '../../../src/stack-parsers'; describe('Tracekit - Misc Tests', () => { it('should parse PhantomJS 1.19 error', () => { diff --git a/packages/browser/test/unit/tracekit/opera.test.ts b/packages/browser/test/unit/tracekit/opera.test.ts index 9084447d4e29..e86855dc172a 100644 --- a/packages/browser/test/unit/tracekit/opera.test.ts +++ b/packages/browser/test/unit/tracekit/opera.test.ts @@ -1,10 +1,10 @@ import { createStackParser } from '@sentry/utils'; import { exceptionFromError } from '../../../src/eventbuilder'; -import { defaultStackParsers, opera10StackParser, opera11StackParser } from '../../../src/stack-parsers'; +import { defaultStackParser, opera10StackLineParser, opera11StackLineParser } from '../../../src/stack-parsers'; -const operaParser = createStackParser(opera10StackParser, opera11StackParser); -const chromiumParser = createStackParser(...defaultStackParsers); +const operaParser = createStackParser(opera10StackLineParser, opera11StackLineParser); +const chromiumParser = defaultStackParser; describe('Tracekit - Opera Tests', () => { it('should parse Opera 10 error', () => { diff --git a/packages/browser/test/unit/tracekit/react-native.test.ts b/packages/browser/test/unit/tracekit/react-native.test.ts index ac469a92246a..9a74e46007b1 100644 --- a/packages/browser/test/unit/tracekit/react-native.test.ts +++ b/packages/browser/test/unit/tracekit/react-native.test.ts @@ -1,9 +1,5 @@ -import { createStackParser } from '@sentry/utils'; - import { exceptionFromError } from '../../../src/eventbuilder'; -import { defaultStackParsers } from '../../../src/stack-parsers'; - -const parser = createStackParser(...defaultStackParsers); +import { defaultStackParser as parser } from '../../../src/stack-parsers'; describe('Tracekit - React Native Tests', () => { it('should parse exceptions for react-native-v8', () => { diff --git a/packages/browser/test/unit/tracekit/react.test.ts b/packages/browser/test/unit/tracekit/react.test.ts index 55229b333403..d949a4dee0eb 100644 --- a/packages/browser/test/unit/tracekit/react.test.ts +++ b/packages/browser/test/unit/tracekit/react.test.ts @@ -1,9 +1,5 @@ -import { createStackParser } from '@sentry/utils'; - import { exceptionFromError } from '../../../src/eventbuilder'; -import { defaultStackParsers } from '../../../src/stack-parsers'; - -const parser = createStackParser(...defaultStackParsers); +import { defaultStackParser as parser } from '../../../src/stack-parsers'; describe('Tracekit - React Tests', () => { it('should correctly parse Invariant Violation errors and use framesToPop to drop info message', () => { diff --git a/packages/browser/test/unit/tracekit/safari.test.ts b/packages/browser/test/unit/tracekit/safari.test.ts index beff492a6c1d..657ffc7daecc 100644 --- a/packages/browser/test/unit/tracekit/safari.test.ts +++ b/packages/browser/test/unit/tracekit/safari.test.ts @@ -1,9 +1,5 @@ -import { createStackParser } from '@sentry/utils'; - import { exceptionFromError } from '../../../src/eventbuilder'; -import { defaultStackParsers } from '../../../src/stack-parsers'; - -const parser = createStackParser(...defaultStackParsers); +import { defaultStackParser as parser } from '../../../src/stack-parsers'; describe('Tracekit - Safari Tests', () => { it('should parse Safari 6 error', () => { diff --git a/packages/node/src/index.ts b/packages/node/src/index.ts index c84836251510..ae1e38cc150d 100644 --- a/packages/node/src/index.ts +++ b/packages/node/src/index.ts @@ -43,10 +43,11 @@ export { } from '@sentry/core'; export { NodeClient } from './client'; +export { makeNodeTransport } from './transports'; export { defaultIntegrations, init, lastEventId, flush, close, getSentryRelease } from './sdk'; export { deepReadDirSync } from './utils'; export { SDK_NAME } from './version'; -export { nodeStackParser } from './stack-parser'; +export { defaultStackParser } from './stack-parser'; import { Integrations as CoreIntegrations } from '@sentry/core'; import { getMainCarrier } from '@sentry/hub'; @@ -54,14 +55,13 @@ import * as domain from 'domain'; import * as Handlers from './handlers'; import * as NodeIntegrations from './integrations'; -import * as Transports from './transports'; const INTEGRATIONS = { ...CoreIntegrations, ...NodeIntegrations, }; -export { INTEGRATIONS as Integrations, Transports, Handlers }; +export { INTEGRATIONS as Integrations, Handlers }; // We need to patch domain on the global __SENTRY__ object to make it work for node in cross-platform packages like // @sentry/hub. If we don't do this, browser bundlers will have troubles resolving `require('domain')`. diff --git a/packages/node/src/sdk.ts b/packages/node/src/sdk.ts index 5658257776b1..34ce79ef480b 100644 --- a/packages/node/src/sdk.ts +++ b/packages/node/src/sdk.ts @@ -1,13 +1,13 @@ import { getCurrentHub, getIntegrationsToSetup, initAndBind, Integrations as CoreIntegrations } from '@sentry/core'; import { getMainCarrier, setHubOnCarrier } from '@sentry/hub'; import { SessionStatus } from '@sentry/types'; -import { getGlobalObject, logger, stackParserFromOptions } from '@sentry/utils'; +import { getGlobalObject, logger, stackParserFromStackParserOptions } from '@sentry/utils'; import * as domain from 'domain'; import { NodeClient } from './client'; import { IS_DEBUG_BUILD } from './flags'; import { Console, ContextLines, Http, LinkedErrors, OnUncaughtException, OnUnhandledRejection } from './integrations'; -import { nodeStackParser } from './stack-parser'; +import { defaultStackParser } from './stack-parser'; import { makeNodeTransport } from './transports'; import { NodeClientOptions, NodeOptions } from './types'; @@ -130,7 +130,7 @@ export function init(options: NodeOptions = {}): void { // TODO(v7): Refactor this to reduce the logic above const clientOptions: NodeClientOptions = { ...options, - stackParser: stackParserFromOptions(options.stackParser || [nodeStackParser]), + stackParser: stackParserFromStackParserOptions(options.stackParser || defaultStackParser), integrations: getIntegrationsToSetup(options), transport: options.transport || makeNodeTransport, }; diff --git a/packages/node/src/stack-parser.ts b/packages/node/src/stack-parser.ts index 79a86e9018ee..ea7cb1468ca1 100644 --- a/packages/node/src/stack-parser.ts +++ b/packages/node/src/stack-parser.ts @@ -1,5 +1,5 @@ import { StackLineParser, StackLineParserFn } from '@sentry/types'; -import { basename, dirname } from '@sentry/utils'; +import { basename, createStackParser, dirname } from '@sentry/utils'; /** Gets the module */ function getModule(filename: string | undefined): string | undefined { @@ -114,4 +114,6 @@ const node: StackLineParserFn = (line: string) => { }; }; -export const nodeStackParser: StackLineParser = [90, node]; +export const nodeStackLineParser: StackLineParser = [90, node]; + +export const defaultStackParser = createStackParser(nodeStackLineParser); diff --git a/packages/node/test/context-lines.test.ts b/packages/node/test/context-lines.test.ts index 55dce7be1615..446429fdff33 100644 --- a/packages/node/test/context-lines.test.ts +++ b/packages/node/test/context-lines.test.ts @@ -1,14 +1,11 @@ import { StackFrame } from '@sentry/types'; -import { createStackParser } from '@sentry/utils'; import * as fs from 'fs'; import { parseStackFrames } from '../src/eventbuilder'; import { ContextLines, resetFileContentCache } from '../src/integrations/contextlines'; -import { nodeStackParser } from '../src/stack-parser'; +import { defaultStackParser } from '../src/stack-parser'; import { getError } from './helper/error'; -const parser = createStackParser(nodeStackParser); - describe('ContextLines', () => { let readFileSpy: jest.SpyInstance; let contextLines: ContextLines; @@ -31,7 +28,7 @@ describe('ContextLines', () => { test('parseStack with same file', async () => { expect.assertions(1); - const frames = parseStackFrames(parser, new Error('test')); + const frames = parseStackFrames(defaultStackParser, new Error('test')); await addContext(Array.from(frames)); @@ -61,12 +58,12 @@ describe('ContextLines', () => { test('parseStack with adding different file', async () => { expect.assertions(1); - const frames = parseStackFrames(parser, new Error('test')); + const frames = parseStackFrames(defaultStackParser, new Error('test')); await addContext(frames); const numCalls = readFileSpy.mock.calls.length; - const parsedFrames = parseStackFrames(parser, getError()); + const parsedFrames = parseStackFrames(defaultStackParser, getError()); await addContext(parsedFrames); const newErrorCalls = readFileSpy.mock.calls.length; @@ -104,7 +101,7 @@ describe('ContextLines', () => { contextLines = new ContextLines({ frameContextLines: 0 }); expect.assertions(1); - const frames = parseStackFrames(parser, new Error('test')); + const frames = parseStackFrames(defaultStackParser, new Error('test')); await addContext(frames); expect(readFileSpy).toHaveBeenCalledTimes(0); diff --git a/packages/node/test/index.test.ts b/packages/node/test/index.test.ts index 502542a25491..8731922267af 100644 --- a/packages/node/test/index.test.ts +++ b/packages/node/test/index.test.ts @@ -1,7 +1,6 @@ import { initAndBind, SDK_VERSION } from '@sentry/core'; import { getMainCarrier } from '@sentry/hub'; import { Integration } from '@sentry/types'; -import { createStackParser } from '@sentry/utils'; import * as domain from 'domain'; import { @@ -17,11 +16,9 @@ import { Scope, } from '../src'; import { ContextLines, LinkedErrors } from '../src/integrations'; -import { nodeStackParser } from '../src/stack-parser'; +import { defaultStackParser } from '../src/stack-parser'; import { getDefaultNodeClientOptions } from './helper/node-client-options'; -const stackParser = createStackParser(nodeStackParser); - jest.mock('@sentry/core', () => { const original = jest.requireActual('@sentry/core'); return { @@ -99,7 +96,7 @@ describe('SentryNode', () => { return null; }, dsn, - stackParser, + stackParser: defaultStackParser, }); const client = new NodeClient(options); getCurrentHub().bindClient(client); @@ -123,7 +120,7 @@ describe('SentryNode', () => { test('capture an exception', done => { expect.assertions(6); const options = getDefaultNodeClientOptions({ - stackParser, + stackParser: defaultStackParser, beforeSend: (event: Event) => { expect(event.tags).toEqual({ test: '1' }); expect(event.exception).not.toBeUndefined(); @@ -150,7 +147,7 @@ describe('SentryNode', () => { test('capture a string exception', done => { expect.assertions(6); const options = getDefaultNodeClientOptions({ - stackParser, + stackParser: defaultStackParser, beforeSend: (event: Event) => { expect(event.tags).toEqual({ test: '1' }); expect(event.exception).not.toBeUndefined(); @@ -177,7 +174,7 @@ describe('SentryNode', () => { test('capture an exception with pre/post context', done => { expect.assertions(10); const options = getDefaultNodeClientOptions({ - stackParser, + stackParser: defaultStackParser, beforeSend: (event: Event) => { expect(event.tags).toEqual({ test: '1' }); expect(event.exception).not.toBeUndefined(); @@ -208,7 +205,7 @@ describe('SentryNode', () => { test('capture a linked exception with pre/post context', done => { expect.assertions(15); const options = getDefaultNodeClientOptions({ - stackParser, + stackParser: defaultStackParser, integrations: [new ContextLines(), new LinkedErrors()], beforeSend: (event: Event) => { expect(event.exception).not.toBeUndefined(); @@ -248,7 +245,7 @@ describe('SentryNode', () => { test('capture a message', done => { expect.assertions(2); const options = getDefaultNodeClientOptions({ - stackParser, + stackParser: defaultStackParser, beforeSend: (event: Event) => { expect(event.message).toBe('test'); expect(event.exception).toBeUndefined(); @@ -264,7 +261,7 @@ describe('SentryNode', () => { test('capture an event', done => { expect.assertions(2); const options = getDefaultNodeClientOptions({ - stackParser, + stackParser: defaultStackParser, beforeSend: (event: Event) => { expect(event.message).toBe('test event'); expect(event.exception).toBeUndefined(); @@ -281,7 +278,7 @@ describe('SentryNode', () => { const d = domain.create(); const options = getDefaultNodeClientOptions({ - stackParser, + stackParser: defaultStackParser, beforeSend: (event: Event) => { expect(event.message).toBe('test domain'); expect(event.exception).toBeUndefined(); @@ -302,7 +299,7 @@ describe('SentryNode', () => { test('stacktrace order', done => { expect.assertions(1); const options = getDefaultNodeClientOptions({ - stackParser, + stackParser: defaultStackParser, beforeSend: (event: Event) => { expect( event.exception!.values![0].stacktrace!.frames![event.exception!.values![0].stacktrace!.frames!.length - 1] diff --git a/packages/node/test/integrations/linkederrors.test.ts b/packages/node/test/integrations/linkederrors.test.ts index 6b01faa11614..c2bba0bd97d2 100644 --- a/packages/node/test/integrations/linkederrors.test.ts +++ b/packages/node/test/integrations/linkederrors.test.ts @@ -1,13 +1,10 @@ import { ExtendedError } from '@sentry/types'; -import { createStackParser } from '@sentry/utils'; import { Event, NodeClient } from '../../src'; import { LinkedErrors } from '../../src/integrations/linkederrors'; -import { nodeStackParser } from '../../src/stack-parser'; +import { defaultStackParser as stackParser } from '../../src/stack-parser'; import { getDefaultNodeClientOptions } from '../helper/node-client-options'; -const stackParser = createStackParser(nodeStackParser); - let linkedErrors: any; describe('LinkedErrors', () => { diff --git a/packages/node/test/stacktrace.test.ts b/packages/node/test/stacktrace.test.ts index ec5ef8b94800..227f87991c8a 100644 --- a/packages/node/test/stacktrace.test.ts +++ b/packages/node/test/stacktrace.test.ts @@ -10,12 +10,8 @@ * @license MIT */ -import { createStackParser } from '@sentry/utils'; - import { parseStackFrames } from '../src/eventbuilder'; -import { nodeStackParser } from '../src/stack-parser'; - -const stackParser = createStackParser(nodeStackParser); +import { defaultStackParser as stackParser } from '../src/stack-parser'; function testBasic() { return new Error('something went wrong'); @@ -36,7 +32,7 @@ describe('Stack parsing', () => { const last = frames.length - 1; expect(frames[last].filename).toEqual(__filename); expect(frames[last].function).toEqual('testBasic'); - expect(frames[last].lineno).toEqual(21); + expect(frames[last].lineno).toEqual(17); expect(frames[last].colno).toEqual(10); }); diff --git a/packages/tracing/src/index.bundle.ts b/packages/tracing/src/index.bundle.ts index 130270af8851..05936d798a66 100644 --- a/packages/tracing/src/index.bundle.ts +++ b/packages/tracing/src/index.bundle.ts @@ -33,7 +33,8 @@ export { setTags, setUser, startTransaction, - Transports, + makeFetchTransport, + makeXHRTransport, withScope, } from '@sentry/browser'; diff --git a/packages/utils/src/stacktrace.ts b/packages/utils/src/stacktrace.ts index b63f7b4c8354..0949a6194526 100644 --- a/packages/utils/src/stacktrace.ts +++ b/packages/utils/src/stacktrace.ts @@ -36,7 +36,7 @@ export function createStackParser(...parsers: StackLineParser[]): StackParser { * * If options contains an array of line parsers, it is converted into a parser */ -export function stackParserFromOptions(stackParser: StackParser | StackLineParser[]): StackParser { +export function stackParserFromStackParserOptions(stackParser: StackParser | StackLineParser[]): StackParser { if (Array.isArray(stackParser)) { return createStackParser(...stackParser); } diff --git a/packages/vue/src/index.bundle.ts b/packages/vue/src/index.bundle.ts index f28600b793a6..690731711855 100644 --- a/packages/vue/src/index.bundle.ts +++ b/packages/vue/src/index.bundle.ts @@ -40,7 +40,8 @@ export { setTags, setUser, startTransaction, - Transports, + makeFetchTransport, + makeXHRTransport, withScope, SDK_NAME, SDK_VERSION, From d8fc94ccd6a4255bdb4ebb14189ce708a8a29257 Mon Sep 17 00:00:00 2001 From: Abhijeet Prasad Date: Tue, 3 May 2022 09:28:31 -0400 Subject: [PATCH 110/204] test(utils): Remove usage of testOnlyIfNodeVersionAtLeast(8) (#5025) We do not support Node 6 anymore, so we don't need to guard against running on Node 6 in the tests. --- packages/utils/test/normalize.test.ts | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/packages/utils/test/normalize.test.ts b/packages/utils/test/normalize.test.ts index a9702d133897..756020b403de 100644 --- a/packages/utils/test/normalize.test.ts +++ b/packages/utils/test/normalize.test.ts @@ -5,7 +5,6 @@ import * as isModule from '../src/is'; import { normalize } from '../src/normalize'; import * as stacktraceModule from '../src/stacktrace'; -import { testOnlyIfNodeVersionAtLeast } from './testutils'; describe('normalize()', () => { describe('acts as a pass-through for simple-cases', () => { @@ -48,7 +47,7 @@ describe('normalize()', () => { }); }); - testOnlyIfNodeVersionAtLeast(8)('extracts data from `Event` objects', () => { + describe('extracts data from `Event` objects', () => { const isElement = jest.spyOn(isModule, 'isElement').mockReturnValue(true); const getAttribute = () => undefined; From 00c00faaed531d552065cfec4c231d8aa2f21770 Mon Sep 17 00:00:00 2001 From: Luca Forstner Date: Tue, 3 May 2022 15:40:11 +0200 Subject: [PATCH 111/204] ref(browser): Don't use Breadcrumbs integration in send event flow (#5024) --- packages/browser/src/client.ts | 42 ++++++++++++++-- .../browser/src/integrations/breadcrumbs.ts | 48 +++++++------------ packages/core/src/baseclient.ts | 9 ++++ 3 files changed, 62 insertions(+), 37 deletions(-) diff --git a/packages/browser/src/client.ts b/packages/browser/src/client.ts index 4f85103a86a6..5476a5e08adc 100644 --- a/packages/browser/src/client.ts +++ b/packages/browser/src/client.ts @@ -1,10 +1,18 @@ -import { BaseClient, getEnvelopeEndpointWithUrlEncodedAuth, Scope, SDK_VERSION } from '@sentry/core'; +import { BaseClient, getCurrentHub, getEnvelopeEndpointWithUrlEncodedAuth, Scope, SDK_VERSION } from '@sentry/core'; import { ClientOptions, Event, EventHint, Options, Severity, SeverityLevel } from '@sentry/types'; -import { createClientReportEnvelope, dsnToString, getGlobalObject, logger, serializeEnvelope } from '@sentry/utils'; +import { + createClientReportEnvelope, + dsnToString, + getEventDescription, + getGlobalObject, + logger, + serializeEnvelope, +} from '@sentry/utils'; import { eventFromException, eventFromMessage } from './eventbuilder'; import { IS_DEBUG_BUILD } from './flags'; import { Breadcrumbs } from './integrations'; +import { BREADCRUMB_INTEGRATION_ID } from './integrations/breadcrumbs'; import { sendReport } from './transports/utils'; const globalObject = getGlobalObject(); @@ -104,10 +112,34 @@ export class BrowserClient extends BaseClient { * @inheritDoc */ protected _sendEvent(event: Event): void { - const integration = this.getIntegration(Breadcrumbs); - if (integration) { - integration.addSentryBreadcrumb(event); + // We only want to add the sentry event breadcrumb when the user has the breadcrumb integration installed and + // activated its `sentry` option. + // We also do not want to use the `Breadcrumbs` class here directly, because we do not want it to be included in + // bundles, if it is not used by the SDK. + // This all sadly is a bit ugly, but we currently don't have a "pre-send" hook on the integrations so we do it this + // way for now. + const breadcrumbIntegration = this.getIntegrationById(BREADCRUMB_INTEGRATION_ID) as Breadcrumbs | null; + if ( + breadcrumbIntegration && + // We check for definedness of `options`, even though it is not strictly necessary, because that access to + // `.sentry` below does not throw, in case users provided their own integration with id "Breadcrumbs" that does + // not have an`options` field + breadcrumbIntegration.options && + breadcrumbIntegration.options.sentry + ) { + getCurrentHub().addBreadcrumb( + { + category: `sentry.${event.type === 'transaction' ? 'transaction' : 'event'}`, + event_id: event.event_id, + level: event.level, + message: getEventDescription(event), + }, + { + event, + }, + ); } + super._sendEvent(event); } diff --git a/packages/browser/src/integrations/breadcrumbs.ts b/packages/browser/src/integrations/breadcrumbs.ts index 3cca50248458..c633bf5d139c 100644 --- a/packages/browser/src/integrations/breadcrumbs.ts +++ b/packages/browser/src/integrations/breadcrumbs.ts @@ -1,10 +1,9 @@ /* eslint-disable @typescript-eslint/no-unsafe-member-access */ /* eslint-disable max-lines */ import { getCurrentHub } from '@sentry/core'; -import { Event, Integration } from '@sentry/types'; +import { Integration } from '@sentry/types'; import { addInstrumentationHandler, - getEventDescription, getGlobalObject, htmlTreeAsString, parseUrl, @@ -22,6 +21,8 @@ interface BreadcrumbsOptions { xhr: boolean; } +export const BREADCRUMB_INTEGRATION_ID = 'Breadcrumbs'; + /** * Default Breadcrumbs instrumentations * TODO: Deprecated - with v6, this will be renamed to `Instrument` @@ -30,21 +31,24 @@ export class Breadcrumbs implements Integration { /** * @inheritDoc */ - public static id: string = 'Breadcrumbs'; + public static id: string = BREADCRUMB_INTEGRATION_ID; /** * @inheritDoc */ public name: string = Breadcrumbs.id; - /** JSDoc */ - private readonly _options: BreadcrumbsOptions; + /** + * Options of the breadcrumbs integration. + */ + // This field is public, because we use it in the browser client to check if the `sentry` option is enabled. + public readonly options: Readonly; /** * @inheritDoc */ public constructor(options?: Partial) { - this._options = { + this.options = { console: true, dom: true, fetch: true, @@ -55,26 +59,6 @@ export class Breadcrumbs implements Integration { }; } - /** - * Create a breadcrumb of `sentry` from the events themselves - */ - public addSentryBreadcrumb(event: Event): void { - if (!this._options.sentry) { - return; - } - getCurrentHub().addBreadcrumb( - { - category: `sentry.${event.type === 'transaction' ? 'transaction' : 'event'}`, - event_id: event.event_id, - level: event.level, - message: getEventDescription(event), - }, - { - event, - }, - ); - } - /** * Instrument browser built-ins w/ breadcrumb capturing * - Console API @@ -84,19 +68,19 @@ export class Breadcrumbs implements Integration { * - History API */ public setupOnce(): void { - if (this._options.console) { + if (this.options.console) { addInstrumentationHandler('console', _consoleBreadcrumb); } - if (this._options.dom) { - addInstrumentationHandler('dom', _domBreadcrumb(this._options.dom)); + if (this.options.dom) { + addInstrumentationHandler('dom', _domBreadcrumb(this.options.dom)); } - if (this._options.xhr) { + if (this.options.xhr) { addInstrumentationHandler('xhr', _xhrBreadcrumb); } - if (this._options.fetch) { + if (this.options.fetch) { addInstrumentationHandler('fetch', _fetchBreadcrumb); } - if (this._options.history) { + if (this.options.history) { addInstrumentationHandler('history', _historyBreadcrumb); } } diff --git a/packages/core/src/baseclient.ts b/packages/core/src/baseclient.ts index f27742e6f251..bee451451a68 100644 --- a/packages/core/src/baseclient.ts +++ b/packages/core/src/baseclient.ts @@ -258,6 +258,15 @@ export abstract class BaseClient implements Client { } } + /** + * Gets an installed integration by its `id`. + * + * @returns The installed integration or `undefined` if no integration with that `id` was installed. + */ + public getIntegrationById(integrationId: string): Integration | undefined { + return this._integrations[integrationId]; + } + /** * @inheritDoc */ From 39553b56cd1162db13e9cc512988728f01d9dca6 Mon Sep 17 00:00:00 2001 From: Luca Forstner Date: Tue, 3 May 2022 15:43:59 +0200 Subject: [PATCH 112/204] ref: Remove unused `_sendEvent` method in base client (#5026) --- packages/browser/src/client.ts | 20 ++++++++++---------- packages/core/src/baseclient.ts | 11 +---------- 2 files changed, 11 insertions(+), 20 deletions(-) diff --git a/packages/browser/src/client.ts b/packages/browser/src/client.ts index 5476a5e08adc..261ceba0a88e 100644 --- a/packages/browser/src/client.ts +++ b/packages/browser/src/client.ts @@ -103,15 +103,7 @@ export class BrowserClient extends BaseClient { /** * @inheritDoc */ - protected _prepareEvent(event: Event, scope?: Scope, hint?: EventHint): PromiseLike { - event.platform = event.platform || 'javascript'; - return super._prepareEvent(event, scope, hint); - } - - /** - * @inheritDoc - */ - protected _sendEvent(event: Event): void { + public sendEvent(event: Event): void { // We only want to add the sentry event breadcrumb when the user has the breadcrumb integration installed and // activated its `sentry` option. // We also do not want to use the `Breadcrumbs` class here directly, because we do not want it to be included in @@ -140,7 +132,15 @@ export class BrowserClient extends BaseClient { ); } - super._sendEvent(event); + super.sendEvent(event); + } + + /** + * @inheritDoc + */ + protected _prepareEvent(event: Event, scope?: Scope, hint?: EventHint): PromiseLike { + event.platform = event.platform || 'javascript'; + return super._prepareEvent(event, scope, hint); } /** diff --git a/packages/core/src/baseclient.ts b/packages/core/src/baseclient.ts index bee451451a68..3c961218ca8d 100644 --- a/packages/core/src/baseclient.ts +++ b/packages/core/src/baseclient.ts @@ -545,15 +545,6 @@ export abstract class BaseClient implements Client { } } - /** - * Sends the passed event - * @param event The Sentry event to send - */ - // TODO(v7): refactor: get rid of method? - protected _sendEvent(event: Event): void { - this.sendEvent(event); - } - /** * Processes the event and logs an error in case of rejection * @param event @@ -631,7 +622,7 @@ export abstract class BaseClient implements Client { this._updateSessionFromEvent(session, processedEvent); } - this._sendEvent(processedEvent); + this.sendEvent(processedEvent); return processedEvent; }) .then(null, reason => { From c9e899747c315c6c402a431fc92ed6ea8c009053 Mon Sep 17 00:00:00 2001 From: Abhijeet Prasad Date: Tue, 3 May 2022 11:28:19 -0400 Subject: [PATCH 113/204] meta: 7.0.0-beta.0 CHANGELOG (#5011) Co-authored-by: Luca Forstner --- CHANGELOG.md | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 8d406b4fa7c8..cee1db4c7eb4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,31 @@ - "You miss 100 percent of the chances you don't take. — Wayne Gretzky" — Michael Scott +## 7.0.0-beta.0 + +- **(breaking)**: ref: Make it easier to use stackParser (#5015) +- **(breaking)**: ref: Switch to new transports (#4943) +- **(breaking)**: ref: Delete store endpoint code (#4969) +- **(breaking)**: chore: set ignoreSentryErrors to true (#4994) +- **(breaking)**: fix(angular): Use Angular compiler to compile @sentry/angular (#4641) +- **(breaking)**: ref(browser): Remove showReportDialog on browser client (#4973) +- **(breaking)**: ref(build): Rename CDN bundles to be es6 per default (#4958) +- **(breaking)**: feat(core): Introduce separate client options (#4927) +- **(breaking)**: ref(core): Delete API Details (#4999) +- **(breaking)**: feat(hub): Remove _invokeClient (#4972) +- **(breaking)**: ref(minimal): Delete @sentry/minimal (#4971) +- **(breaking)**: ref(node): Remove raven-node backward-compat code (#4942) +- chore: Remove tslint from `@sentry-internal/typescript` (#4940) +- feat: Add client report hook to makeTransport (#5008) +- ref(build): Switch tsconfig target to es6 (#5005) +- ref(core): Make event processing log warnings instead of errors (#5010) +- fix(hub): Add missing parameter to captureException docstring (#5001) +- fix(serverless): Adjust v6 Lambda layer hotfix for v7 (#5006) +- fix(tracing): Adjust sideEffects package.json entry for v7 (#4987) +- feat(tracing): Add GB unit to device memory tag value (#4935) +- feat(tracing): Add Prisma ORM integration. (#4931) +- ref(utils): Remove forget async utility function (#4941) + ## 7.0.0-alpha.1 - **(breaking)** ref: Inject Transports into Client (#4921) From 3532b33de438b0acc764338632cfa7cc29a86f9a Mon Sep 17 00:00:00 2001 From: Abhijeet Prasad Date: Tue, 3 May 2022 16:11:38 -0400 Subject: [PATCH 114/204] feat: Export browser integrations individually (#5028) In this PR, we switch to exporting everything individually. This means users don't have import the entire `INTEGRATIONS` object in, allowing it to be treeshaken. --- CHANGELOG.md | 1 + MIGRATION.md | 20 ++++++++++++-------- packages/browser/src/exports.ts | 3 +++ packages/core/src/index.ts | 1 + 4 files changed, 17 insertions(+), 8 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index cee1db4c7eb4..18b4db957232 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -20,6 +20,7 @@ - **(breaking)**: ref(node): Remove raven-node backward-compat code (#4942) - chore: Remove tslint from `@sentry-internal/typescript` (#4940) - feat: Add client report hook to makeTransport (#5008) +- feat: Export browser integrations individually (#5028) - ref(build): Switch tsconfig target to es6 (#5005) - ref(core): Make event processing log warnings instead of errors (#5010) - fix(hub): Add missing parameter to captureException docstring (#5001) diff --git a/MIGRATION.md b/MIGRATION.md index a7809c188296..ba33ed5c0507 100644 --- a/MIGRATION.md +++ b/MIGRATION.md @@ -43,7 +43,7 @@ import { BrowserClient, defaultStackParser, defaultIntegrations, makeFetchTransp const client = new BrowserClient({ transport: makeFetchTransport, stackParser: defaultStackParser, - integrations: [...defaultIntegrations], + integrations: defaultIntegrations, }); // Before: @@ -53,18 +53,22 @@ const client = new BrowserClient(); Since you now explicitly pass in the dependencies of the client, you can also tree-shake out dependencies that you do not use this way. For example, you can tree-shake out the SDK's default integrations and only use the ones that you want like so: ```ts -import { BrowserClient, defaultStackParser, Integrations, makeFetchTransport } from '@sentry/browser'; +import { + BrowserClient, + Breadcrumbs, + Dedupe, + defaultStackParser, + GlobalHandlers, + Integrations, + makeFetchTransport, + LinkedErrors, +} from '@sentry/browser'; // New in v7: const client = new BrowserClient({ transport: makeFetchTransport, stackParser: defaultStackParser, - integrations: [ - new Integrations.Breadcrumbs(), - new Integrations.GlobalHandlers(), - new Integrations.LinkedErrors(), - new Integrations.Dedupe(), - ], + integrations: [new Breadcrumbs(), new GlobalHandlers(), new LinkedErrors(), new Dedupe()], }); ``` diff --git a/packages/browser/src/exports.ts b/packages/browser/src/exports.ts index 2ec436c05b84..4396e88408db 100644 --- a/packages/browser/src/exports.ts +++ b/packages/browser/src/exports.ts @@ -41,6 +41,8 @@ export { setTags, setUser, withScope, + FunctionToString, + InboundFilters, } from '@sentry/core'; export { BrowserClient } from './client'; @@ -56,3 +58,4 @@ export { } from './stack-parsers'; export { defaultIntegrations, forceLoad, init, lastEventId, onLoad, showReportDialog, flush, close, wrap } from './sdk'; export { SDK_NAME } from './version'; +export { GlobalHandlers, TryCatch, Breadcrumbs, LinkedErrors, UserAgent, Dedupe } from './integrations'; diff --git a/packages/core/src/index.ts b/packages/core/src/index.ts index 2c9eb53a9ef8..9e4afbbc86d0 100644 --- a/packages/core/src/index.ts +++ b/packages/core/src/index.ts @@ -28,6 +28,7 @@ export { initAndBind } from './sdk'; export { createTransport } from './transports/base'; export { SDK_VERSION } from './version'; export { getIntegrationsToSetup } from './integration'; +export { FunctionToString, InboundFilters } from './integrations'; import * as Integrations from './integrations'; From 50361b68d3b532e9a6b807e281922586a1e7ec76 Mon Sep 17 00:00:00 2001 From: getsentry-bot Date: Tue, 3 May 2022 20:19:08 +0000 Subject: [PATCH 115/204] release: 7.0.0-beta.0 --- lerna.json | 2 +- packages/angular/package.json | 8 ++++---- packages/browser/package.json | 8 ++++---- packages/core/package.json | 8 ++++---- packages/core/src/version.ts | 2 +- packages/ember/package.json | 10 +++++----- packages/eslint-config-sdk/package.json | 6 +++--- packages/eslint-plugin-sdk/package.json | 2 +- packages/gatsby/package.json | 8 ++++---- packages/hub/package.json | 6 +++--- packages/integration-tests/package.json | 2 +- packages/integrations/package.json | 6 +++--- packages/nextjs/package.json | 18 +++++++++--------- packages/node-integration-tests/package.json | 2 +- packages/node/package.json | 10 +++++----- packages/react/package.json | 8 ++++---- packages/serverless/package.json | 10 +++++----- packages/tracing/package.json | 10 +++++----- packages/types/package.json | 2 +- packages/typescript/package.json | 3 +-- packages/utils/package.json | 4 ++-- packages/vue/package.json | 10 +++++----- packages/wasm/package.json | 6 +++--- 23 files changed, 75 insertions(+), 76 deletions(-) diff --git a/lerna.json b/lerna.json index c99f1bd4f741..a2a644cee9c8 100644 --- a/lerna.json +++ b/lerna.json @@ -1,6 +1,6 @@ { "lerna": "3.4.0", - "version": "7.0.0-alpha.1", + "version": "7.0.0-beta.0", "packages": "packages/*", "npmClient": "yarn", "useWorkspaces": true diff --git a/packages/angular/package.json b/packages/angular/package.json index 2b1c8f69fd55..3a14bd2e2d80 100644 --- a/packages/angular/package.json +++ b/packages/angular/package.json @@ -1,6 +1,6 @@ { "name": "@sentry/angular", - "version": "7.0.0-alpha.1", + "version": "7.0.0-beta.0", "description": "Official Sentry SDK for Angular", "repository": "git://github.com/getsentry/sentry-javascript.git", "homepage": "https://github.com/getsentry/sentry-javascript/tree/master/packages/angular", @@ -21,9 +21,9 @@ "rxjs": "^6.5.5 || ^7.x" }, "dependencies": { - "@sentry/browser": "7.0.0-alpha.1", - "@sentry/types": "7.0.0-alpha.1", - "@sentry/utils": "7.0.0-alpha.1", + "@sentry/browser": "7.0.0-beta.0", + "@sentry/types": "7.0.0-beta.0", + "@sentry/utils": "7.0.0-beta.0", "tslib": "^2.0.0" }, "devDependencies": { diff --git a/packages/browser/package.json b/packages/browser/package.json index 2263eff402cc..9ca3ad829d88 100644 --- a/packages/browser/package.json +++ b/packages/browser/package.json @@ -1,6 +1,6 @@ { "name": "@sentry/browser", - "version": "7.0.0-alpha.1", + "version": "7.0.0-beta.0", "description": "Official Sentry SDK for browsers", "repository": "git://github.com/getsentry/sentry-javascript.git", "homepage": "https://github.com/getsentry/sentry-javascript/tree/master/packages/browser", @@ -16,9 +16,9 @@ "access": "public" }, "dependencies": { - "@sentry/core": "7.0.0-alpha.1", - "@sentry/types": "7.0.0-alpha.1", - "@sentry/utils": "7.0.0-alpha.1", + "@sentry/core": "7.0.0-beta.0", + "@sentry/types": "7.0.0-beta.0", + "@sentry/utils": "7.0.0-beta.0", "tslib": "^1.9.3" }, "devDependencies": { diff --git a/packages/core/package.json b/packages/core/package.json index a66c34b5035f..104ca8c24a13 100644 --- a/packages/core/package.json +++ b/packages/core/package.json @@ -1,6 +1,6 @@ { "name": "@sentry/core", - "version": "7.0.0-alpha.1", + "version": "7.0.0-beta.0", "description": "Base implementation for all Sentry JavaScript SDKs", "repository": "git://github.com/getsentry/sentry-javascript.git", "homepage": "https://github.com/getsentry/sentry-javascript/tree/master/packages/core", @@ -16,9 +16,9 @@ "access": "public" }, "dependencies": { - "@sentry/hub": "7.0.0-alpha.1", - "@sentry/types": "7.0.0-alpha.1", - "@sentry/utils": "7.0.0-alpha.1", + "@sentry/hub": "7.0.0-beta.0", + "@sentry/types": "7.0.0-beta.0", + "@sentry/utils": "7.0.0-beta.0", "tslib": "^1.9.3" }, "scripts": { diff --git a/packages/core/src/version.ts b/packages/core/src/version.ts index 2dcfdae39507..9d44e7a95026 100644 --- a/packages/core/src/version.ts +++ b/packages/core/src/version.ts @@ -1 +1 @@ -export const SDK_VERSION = '7.0.0-alpha.1'; +export const SDK_VERSION = '7.0.0-beta.0'; diff --git a/packages/ember/package.json b/packages/ember/package.json index 23d65a60b571..1646f9155582 100644 --- a/packages/ember/package.json +++ b/packages/ember/package.json @@ -1,6 +1,6 @@ { "name": "@sentry/ember", - "version": "7.0.0-alpha.1", + "version": "7.0.0-beta.0", "description": "Official Sentry SDK for Ember.js", "repository": "git://github.com/getsentry/sentry-javascript.git", "homepage": "https://github.com/getsentry/sentry-javascript/tree/master/packages/ember", @@ -29,10 +29,10 @@ }, "dependencies": { "@embroider/macros": "~0.47.2", - "@sentry/browser": "7.0.0-alpha.1", - "@sentry/tracing": "7.0.0-alpha.1", - "@sentry/types": "7.0.0-alpha.1", - "@sentry/utils": "7.0.0-alpha.1", + "@sentry/browser": "7.0.0-beta.0", + "@sentry/tracing": "7.0.0-beta.0", + "@sentry/types": "7.0.0-beta.0", + "@sentry/utils": "7.0.0-beta.0", "ember-auto-import": "~1.12.1 || ~2.2.0", "ember-cli-babel": "~7.26.6", "ember-cli-htmlbars": "^6.0.1", diff --git a/packages/eslint-config-sdk/package.json b/packages/eslint-config-sdk/package.json index 844251a06f14..ab7abdd27992 100644 --- a/packages/eslint-config-sdk/package.json +++ b/packages/eslint-config-sdk/package.json @@ -1,6 +1,6 @@ { "name": "@sentry-internal/eslint-config-sdk", - "version": "7.0.0-alpha.1", + "version": "7.0.0-beta.0", "description": "Official Sentry SDK eslint config", "repository": "git://github.com/getsentry/sentry-javascript.git", "homepage": "https://github.com/getsentry/sentry-javascript/tree/master/packages/eslint-config-sdk", @@ -19,8 +19,8 @@ "access": "public" }, "dependencies": { - "@sentry-internal/eslint-plugin-sdk": "7.0.0-alpha.1", - "@sentry-internal/typescript": "7.0.0-alpha.1", + "@sentry-internal/eslint-plugin-sdk": "7.0.0-beta.0", + "@sentry-internal/typescript": "7.0.0-beta.0", "@typescript-eslint/eslint-plugin": "^3.9.0", "@typescript-eslint/parser": "^3.9.0", "eslint-config-prettier": "^6.11.0", diff --git a/packages/eslint-plugin-sdk/package.json b/packages/eslint-plugin-sdk/package.json index 65a439ba5555..8f705729c304 100644 --- a/packages/eslint-plugin-sdk/package.json +++ b/packages/eslint-plugin-sdk/package.json @@ -1,6 +1,6 @@ { "name": "@sentry-internal/eslint-plugin-sdk", - "version": "7.0.0-alpha.1", + "version": "7.0.0-beta.0", "description": "Official Sentry SDK eslint plugin", "repository": "git://github.com/getsentry/sentry-javascript.git", "homepage": "https://github.com/getsentry/sentry-javascript/tree/master/packages/eslint-plugin-sdk", diff --git a/packages/gatsby/package.json b/packages/gatsby/package.json index 8b02e542af23..50811fce6b03 100644 --- a/packages/gatsby/package.json +++ b/packages/gatsby/package.json @@ -1,6 +1,6 @@ { "name": "@sentry/gatsby", - "version": "7.0.0-alpha.1", + "version": "7.0.0-beta.0", "description": "Official Sentry SDK for Gatsby.js", "repository": "git://github.com/getsentry/sentry-javascript.git", "homepage": "https://github.com/getsentry/sentry-javascript/tree/master/packages/gatsby", @@ -20,8 +20,8 @@ "access": "public" }, "dependencies": { - "@sentry/react": "7.0.0-alpha.1", - "@sentry/tracing": "7.0.0-alpha.1", + "@sentry/react": "7.0.0-beta.0", + "@sentry/tracing": "7.0.0-beta.0", "@sentry/webpack-plugin": "1.18.9" }, "peerDependencies": { @@ -29,7 +29,7 @@ "react": "15.x || 16.x || 17.x || 18.x" }, "devDependencies": { - "@sentry/types": "7.0.0-alpha.1", + "@sentry/types": "7.0.0-beta.0", "@testing-library/react": "^13.0.0", "react": "^18.0.0" }, diff --git a/packages/hub/package.json b/packages/hub/package.json index 66cca210583a..7903d295158d 100644 --- a/packages/hub/package.json +++ b/packages/hub/package.json @@ -1,6 +1,6 @@ { "name": "@sentry/hub", - "version": "7.0.0-alpha.1", + "version": "7.0.0-beta.0", "description": "Sentry hub which handles global state managment.", "repository": "git://github.com/getsentry/sentry-javascript.git", "homepage": "https://github.com/getsentry/sentry-javascript/tree/master/packages/hub", @@ -16,8 +16,8 @@ "access": "public" }, "dependencies": { - "@sentry/types": "7.0.0-alpha.1", - "@sentry/utils": "7.0.0-alpha.1", + "@sentry/types": "7.0.0-beta.0", + "@sentry/utils": "7.0.0-beta.0", "tslib": "^1.9.3" }, "scripts": { diff --git a/packages/integration-tests/package.json b/packages/integration-tests/package.json index e501c87aca91..d4dee475b59c 100644 --- a/packages/integration-tests/package.json +++ b/packages/integration-tests/package.json @@ -1,6 +1,6 @@ { "name": "@sentry-internal/browser-integration-tests", - "version": "7.0.0-alpha.1", + "version": "7.0.0-beta.0", "main": "index.js", "license": "MIT", "engines": { diff --git a/packages/integrations/package.json b/packages/integrations/package.json index e7097f2c486c..9c012e616cbe 100644 --- a/packages/integrations/package.json +++ b/packages/integrations/package.json @@ -1,6 +1,6 @@ { "name": "@sentry/integrations", - "version": "7.0.0-alpha.1", + "version": "7.0.0-beta.0", "description": "Pluggable integrations that can be used to enhance JS SDKs", "repository": "git://github.com/getsentry/sentry-javascript.git", "homepage": "https://github.com/getsentry/sentry-javascript/tree/master/packages/integrations", @@ -16,8 +16,8 @@ "module": "build/npm/esm/index.js", "types": "build/npm/types/index.d.ts", "dependencies": { - "@sentry/types": "7.0.0-alpha.1", - "@sentry/utils": "7.0.0-alpha.1", + "@sentry/types": "7.0.0-beta.0", + "@sentry/utils": "7.0.0-beta.0", "localforage": "^1.8.1", "tslib": "^1.9.3" }, diff --git a/packages/nextjs/package.json b/packages/nextjs/package.json index 07529b57ba03..266239365938 100644 --- a/packages/nextjs/package.json +++ b/packages/nextjs/package.json @@ -1,6 +1,6 @@ { "name": "@sentry/nextjs", - "version": "7.0.0-alpha.1", + "version": "7.0.0-beta.0", "description": "Official Sentry SDK for Next.js", "repository": "git://github.com/getsentry/sentry-javascript.git", "homepage": "https://github.com/getsentry/sentry-javascript/tree/master/packages/nextjs", @@ -17,18 +17,18 @@ "access": "public" }, "dependencies": { - "@sentry/core": "7.0.0-alpha.1", - "@sentry/hub": "7.0.0-alpha.1", - "@sentry/integrations": "7.0.0-alpha.1", - "@sentry/node": "7.0.0-alpha.1", - "@sentry/react": "7.0.0-alpha.1", - "@sentry/tracing": "7.0.0-alpha.1", - "@sentry/utils": "7.0.0-alpha.1", + "@sentry/core": "7.0.0-beta.0", + "@sentry/hub": "7.0.0-beta.0", + "@sentry/integrations": "7.0.0-beta.0", + "@sentry/node": "7.0.0-beta.0", + "@sentry/react": "7.0.0-beta.0", + "@sentry/tracing": "7.0.0-beta.0", + "@sentry/utils": "7.0.0-beta.0", "@sentry/webpack-plugin": "1.18.9", "tslib": "^1.9.3" }, "devDependencies": { - "@sentry/types": "7.0.0-alpha.1", + "@sentry/types": "7.0.0-beta.0", "@types/webpack": "^4.41.31", "next": "10.1.3" }, diff --git a/packages/node-integration-tests/package.json b/packages/node-integration-tests/package.json index 87920c697cad..505db755265b 100644 --- a/packages/node-integration-tests/package.json +++ b/packages/node-integration-tests/package.json @@ -1,6 +1,6 @@ { "name": "@sentry-internal/node-integration-tests", - "version": "7.0.0-alpha.1", + "version": "7.0.0-beta.0", "license": "MIT", "engines": { "node": ">=10" diff --git a/packages/node/package.json b/packages/node/package.json index f67eace899d3..a1d82575277a 100644 --- a/packages/node/package.json +++ b/packages/node/package.json @@ -1,6 +1,6 @@ { "name": "@sentry/node", - "version": "7.0.0-alpha.1", + "version": "7.0.0-beta.0", "description": "Official Sentry SDK for Node.js", "repository": "git://github.com/getsentry/sentry-javascript.git", "homepage": "https://github.com/getsentry/sentry-javascript/tree/master/packages/node", @@ -16,10 +16,10 @@ "access": "public" }, "dependencies": { - "@sentry/core": "7.0.0-alpha.1", - "@sentry/hub": "7.0.0-alpha.1", - "@sentry/types": "7.0.0-alpha.1", - "@sentry/utils": "7.0.0-alpha.1", + "@sentry/core": "7.0.0-beta.0", + "@sentry/hub": "7.0.0-beta.0", + "@sentry/types": "7.0.0-beta.0", + "@sentry/utils": "7.0.0-beta.0", "cookie": "^0.4.1", "https-proxy-agent": "^5.0.0", "lru_map": "^0.3.3", diff --git a/packages/react/package.json b/packages/react/package.json index 67a29a291385..03f1873aa436 100644 --- a/packages/react/package.json +++ b/packages/react/package.json @@ -1,6 +1,6 @@ { "name": "@sentry/react", - "version": "7.0.0-alpha.1", + "version": "7.0.0-beta.0", "description": "Official Sentry SDK for React.js", "repository": "git://github.com/getsentry/sentry-javascript.git", "homepage": "https://github.com/getsentry/sentry-javascript/tree/master/packages/react", @@ -16,9 +16,9 @@ "access": "public" }, "dependencies": { - "@sentry/browser": "7.0.0-alpha.1", - "@sentry/types": "7.0.0-alpha.1", - "@sentry/utils": "7.0.0-alpha.1", + "@sentry/browser": "7.0.0-beta.0", + "@sentry/types": "7.0.0-beta.0", + "@sentry/utils": "7.0.0-beta.0", "hoist-non-react-statics": "^3.3.2", "tslib": "^1.9.3" }, diff --git a/packages/serverless/package.json b/packages/serverless/package.json index 97c20654f559..ab2c00d479dc 100644 --- a/packages/serverless/package.json +++ b/packages/serverless/package.json @@ -1,6 +1,6 @@ { "name": "@sentry/serverless", - "version": "7.0.0-alpha.1", + "version": "7.0.0-beta.0", "description": "Official Sentry SDK for various serverless solutions", "repository": "git://github.com/getsentry/sentry-javascript.git", "homepage": "https://github.com/getsentry/sentry-javascript/tree/master/packages/serverless", @@ -16,10 +16,10 @@ "access": "public" }, "dependencies": { - "@sentry/node": "7.0.0-alpha.1", - "@sentry/tracing": "7.0.0-alpha.1", - "@sentry/types": "7.0.0-alpha.1", - "@sentry/utils": "7.0.0-alpha.1", + "@sentry/node": "7.0.0-beta.0", + "@sentry/tracing": "7.0.0-beta.0", + "@sentry/types": "7.0.0-beta.0", + "@sentry/utils": "7.0.0-beta.0", "@types/aws-lambda": "^8.10.62", "@types/express": "^4.17.2", "tslib": "^1.9.3" diff --git a/packages/tracing/package.json b/packages/tracing/package.json index 1cfa4aa2dafe..c5098efa4880 100644 --- a/packages/tracing/package.json +++ b/packages/tracing/package.json @@ -1,6 +1,6 @@ { "name": "@sentry/tracing", - "version": "7.0.0-alpha.1", + "version": "7.0.0-beta.0", "description": "Extensions for Sentry AM", "repository": "git://github.com/getsentry/sentry-javascript.git", "homepage": "https://github.com/getsentry/sentry-javascript/tree/master/packages/tracing", @@ -16,13 +16,13 @@ "access": "public" }, "dependencies": { - "@sentry/hub": "7.0.0-alpha.1", - "@sentry/types": "7.0.0-alpha.1", - "@sentry/utils": "7.0.0-alpha.1", + "@sentry/hub": "7.0.0-beta.0", + "@sentry/types": "7.0.0-beta.0", + "@sentry/utils": "7.0.0-beta.0", "tslib": "^1.9.3" }, "devDependencies": { - "@sentry/browser": "7.0.0-alpha.1", + "@sentry/browser": "7.0.0-beta.0", "@types/express": "^4.17.1" }, "scripts": { diff --git a/packages/types/package.json b/packages/types/package.json index 9799b8aa4fcf..823868e048fd 100644 --- a/packages/types/package.json +++ b/packages/types/package.json @@ -1,6 +1,6 @@ { "name": "@sentry/types", - "version": "7.0.0-alpha.1", + "version": "7.0.0-beta.0", "description": "Types for all Sentry JavaScript SDKs", "repository": "git://github.com/getsentry/sentry-javascript.git", "homepage": "https://github.com/getsentry/sentry-javascript/tree/master/packages/types", diff --git a/packages/typescript/package.json b/packages/typescript/package.json index 9a8a3ed28d4c..6c1fbeef3229 100644 --- a/packages/typescript/package.json +++ b/packages/typescript/package.json @@ -1,6 +1,6 @@ { "name": "@sentry-internal/typescript", - "version": "7.0.0-alpha.1", + "version": "7.0.0-beta.0", "description": "Typescript configuration used at Sentry", "repository": "git://github.com/getsentry/sentry-javascript.git", "homepage": "https://github.com/getsentry/sentry-javascript/tree/master/packages/typescript", @@ -10,7 +10,6 @@ "publishConfig": { "access": "public" }, - "dependencies": {}, "peerDependencies": { "typescript": "3.8.3" }, diff --git a/packages/utils/package.json b/packages/utils/package.json index 1e84b03ca42d..d67412868965 100644 --- a/packages/utils/package.json +++ b/packages/utils/package.json @@ -1,6 +1,6 @@ { "name": "@sentry/utils", - "version": "7.0.0-alpha.1", + "version": "7.0.0-beta.0", "description": "Utilities for all Sentry JavaScript SDKs", "repository": "git://github.com/getsentry/sentry-javascript.git", "homepage": "https://github.com/getsentry/sentry-javascript/tree/master/packages/utils", @@ -16,7 +16,7 @@ "access": "public" }, "dependencies": { - "@sentry/types": "7.0.0-alpha.1", + "@sentry/types": "7.0.0-beta.0", "tslib": "^1.9.3" }, "devDependencies": { diff --git a/packages/vue/package.json b/packages/vue/package.json index 66af3d30fc92..a29eb014518a 100644 --- a/packages/vue/package.json +++ b/packages/vue/package.json @@ -1,6 +1,6 @@ { "name": "@sentry/vue", - "version": "7.0.0-alpha.1", + "version": "7.0.0-beta.0", "description": "Official Sentry SDK for Vue.js", "repository": "git://github.com/getsentry/sentry-javascript.git", "homepage": "https://github.com/getsentry/sentry-javascript/tree/master/packages/vue", @@ -16,10 +16,10 @@ "access": "public" }, "dependencies": { - "@sentry/browser": "7.0.0-alpha.1", - "@sentry/core": "7.0.0-alpha.1", - "@sentry/types": "7.0.0-alpha.1", - "@sentry/utils": "7.0.0-alpha.1", + "@sentry/browser": "7.0.0-beta.0", + "@sentry/core": "7.0.0-beta.0", + "@sentry/types": "7.0.0-beta.0", + "@sentry/utils": "7.0.0-beta.0", "tslib": "^1.9.3" }, "peerDependencies": { diff --git a/packages/wasm/package.json b/packages/wasm/package.json index e34b788aceef..831d45671b0c 100644 --- a/packages/wasm/package.json +++ b/packages/wasm/package.json @@ -1,6 +1,6 @@ { "name": "@sentry/wasm", - "version": "7.0.0-alpha.1", + "version": "7.0.0-beta.0", "description": "Support for WASM.", "repository": "git://github.com/getsentry/sentry-javascript.git", "homepage": "https://github.com/getsentry/sentry-javascript/tree/master/packages/wasm", @@ -16,8 +16,8 @@ "access": "public" }, "dependencies": { - "@sentry/browser": "7.0.0-alpha.1", - "@sentry/types": "7.0.0-alpha.1", + "@sentry/browser": "7.0.0-beta.0", + "@sentry/types": "7.0.0-beta.0", "tslib": "^1.9.3" }, "devDependencies": { From 1b924e79feecb2be80e28dea4db154017923454d Mon Sep 17 00:00:00 2001 From: Lukas Stracke Date: Wed, 4 May 2022 13:28:27 +0200 Subject: [PATCH 116/204] chore(browser): Rename `UserAgent` integration to `HttpContext` (#5027) As suggested in this [comment](https://github.com/getsentry/sentry-javascript/pull/2906#issuecomment-692910191) of #2906, rename the Browser `UserAgent` integration to `HttpContext`. The integration does more than collecting the user agent information and thus the old name wasn't reflecting its entire purpose. --- MIGRATION.md | 1 + .../src/integrations/{useragent.ts => httpcontext.ts} | 10 +++++----- packages/browser/src/integrations/index.ts | 2 +- packages/browser/src/sdk.ts | 4 ++-- 4 files changed, 9 insertions(+), 8 deletions(-) rename packages/browser/src/integrations/{useragent.ts => httpcontext.ts} (81%) diff --git a/MIGRATION.md b/MIGRATION.md index ba33ed5c0507..fb96493bc8ce 100644 --- a/MIGRATION.md +++ b/MIGRATION.md @@ -281,6 +281,7 @@ For our efforts to reduce bundle size of the SDK we had to remove and refactor p not present in other Sentry SDKs. For the sake of reducing complexity, increasing consistency with other Sentry SDKs and decreasing bundle-size, `Backend` was removed. - Remove support for Opera browser pre v15 +- Rename `UserAgent` integration to `HttpContext` (see [#5027](https://github.com/getsentry/sentry-javascript/pull/5027)) ## Sentry Angular SDK Changes diff --git a/packages/browser/src/integrations/useragent.ts b/packages/browser/src/integrations/httpcontext.ts similarity index 81% rename from packages/browser/src/integrations/useragent.ts rename to packages/browser/src/integrations/httpcontext.ts index 1160320f9d93..2c7d5aa4e841 100644 --- a/packages/browser/src/integrations/useragent.ts +++ b/packages/browser/src/integrations/httpcontext.ts @@ -4,24 +4,24 @@ import { getGlobalObject } from '@sentry/utils'; const global = getGlobalObject(); -/** UserAgent */ -export class UserAgent implements Integration { +/** HttpContext integration collects information about HTTP request headers */ +export class HttpContext implements Integration { /** * @inheritDoc */ - public static id: string = 'UserAgent'; + public static id: string = 'HttpContext'; /** * @inheritDoc */ - public name: string = UserAgent.id; + public name: string = HttpContext.id; /** * @inheritDoc */ public setupOnce(): void { addGlobalEventProcessor((event: Event) => { - if (getCurrentHub().getIntegration(UserAgent)) { + if (getCurrentHub().getIntegration(HttpContext)) { // if none of the information we want exists, don't bother if (!global.navigator && !global.location && !global.document) { return event; diff --git a/packages/browser/src/integrations/index.ts b/packages/browser/src/integrations/index.ts index a354fdc883e9..e029422f363c 100644 --- a/packages/browser/src/integrations/index.ts +++ b/packages/browser/src/integrations/index.ts @@ -2,5 +2,5 @@ export { GlobalHandlers } from './globalhandlers'; export { TryCatch } from './trycatch'; export { Breadcrumbs } from './breadcrumbs'; export { LinkedErrors } from './linkederrors'; -export { UserAgent } from './useragent'; +export { HttpContext } from './httpcontext'; export { Dedupe } from './dedupe'; diff --git a/packages/browser/src/sdk.ts b/packages/browser/src/sdk.ts index 98dd9e6e3270..584742b26f69 100644 --- a/packages/browser/src/sdk.ts +++ b/packages/browser/src/sdk.ts @@ -18,7 +18,7 @@ import { import { BrowserClient, BrowserClientOptions, BrowserOptions } from './client'; import { IS_DEBUG_BUILD } from './flags'; import { ReportDialogOptions, wrap as internalWrap } from './helpers'; -import { Breadcrumbs, Dedupe, GlobalHandlers, LinkedErrors, TryCatch, UserAgent } from './integrations'; +import { Breadcrumbs, Dedupe, GlobalHandlers, HttpContext, LinkedErrors, TryCatch } from './integrations'; import { defaultStackParser } from './stack-parsers'; import { makeFetchTransport, makeXHRTransport } from './transports'; @@ -30,7 +30,7 @@ export const defaultIntegrations = [ new GlobalHandlers(), new LinkedErrors(), new Dedupe(), - new UserAgent(), + new HttpContext(), ]; /** From 794f65b9ae79766b819a77f6a6f84956774bc225 Mon Sep 17 00:00:00 2001 From: Lukas Stracke Date: Wed, 4 May 2022 14:06:15 +0200 Subject: [PATCH 117/204] fix(browser): Change `UserAgent` export to `HttpContext` (#5033) --- packages/browser/src/exports.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/browser/src/exports.ts b/packages/browser/src/exports.ts index 4396e88408db..4055ef8e595b 100644 --- a/packages/browser/src/exports.ts +++ b/packages/browser/src/exports.ts @@ -58,4 +58,4 @@ export { } from './stack-parsers'; export { defaultIntegrations, forceLoad, init, lastEventId, onLoad, showReportDialog, flush, close, wrap } from './sdk'; export { SDK_NAME } from './version'; -export { GlobalHandlers, TryCatch, Breadcrumbs, LinkedErrors, UserAgent, Dedupe } from './integrations'; +export { GlobalHandlers, TryCatch, Breadcrumbs, LinkedErrors, HttpContext, Dedupe } from './integrations'; From 6496a116c570bd67d5cf222cc91987496674b8b5 Mon Sep 17 00:00:00 2001 From: Luca Forstner Date: Wed, 4 May 2022 15:19:55 +0200 Subject: [PATCH 118/204] ref: Remove `critical` severity (#5032) --- MIGRATION.md | 1 + packages/hub/test/scope.test.ts | 10 +++++----- .../public-api/captureMessage/with_level/subject.js | 1 - .../public-api/captureMessage/with_level/test.ts | 9 +++------ .../addBreadcrumb/multiple_breadcrumbs/scenario.ts | 2 +- .../addBreadcrumb/multiple_breadcrumbs/test.ts | 2 +- .../addBreadcrumb/simple_breadcrumb/scenario.ts | 2 +- .../public-api/addBreadcrumb/simple_breadcrumb/test.ts | 2 +- .../public-api/captureMessage/with_level/scenario.ts | 1 - .../public-api/captureMessage/with_level/test.ts | 7 +------ packages/types/src/severity.ts | 6 ++---- packages/utils/src/severity.ts | 2 +- 12 files changed, 17 insertions(+), 28 deletions(-) diff --git a/MIGRATION.md b/MIGRATION.md index fb96493bc8ce..64a795237c24 100644 --- a/MIGRATION.md +++ b/MIGRATION.md @@ -273,6 +273,7 @@ For our efforts to reduce bundle size of the SDK we had to remove and refactor p - Remove deprecated `whitelistUrls` and `blacklistUrls` options from `Sentry.init`. They have been superseded by `allowUrls` and `denyUrls` specifically. See [our docs page on inclusive language](https://develop.sentry.dev/inclusion/) for more details. - Gatsby SDK: Remove `Sentry` from `window` object. - Remove deprecated `Status`, `SessionStatus`, and `RequestSessionStatus` enums. These were only part of an internal API. If you are using these enums, we encourage you to to look at [b177690d](https://github.com/getsentry/sentry-javascript/commit/b177690d89640aef2587039113c614672c07d2be), [5fc3147d](https://github.com/getsentry/sentry-javascript/commit/5fc3147dfaaf1a856d5923e4ba409479e87273be), and [f99bdd16](https://github.com/getsentry/sentry-javascript/commit/f99bdd16539bf6fac14eccf1a974a4988d586b28) to to see the changes we've made to our code as result. We generally recommend using string literals instead of the removed enums. +- Remove 'critical' severity. - Remove deprecated `getActiveDomain` method and `DomainAsCarrier` type from `@sentry/hub`. - Rename `registerRequestInstrumentation` to `instrumentOutgoingRequests` in `@sentry/tracing`. - Remove `Backend` and port its functionality into `Client` (see diff --git a/packages/hub/test/scope.test.ts b/packages/hub/test/scope.test.ts index b734ac59fcf6..1bad52db6d9f 100644 --- a/packages/hub/test/scope.test.ts +++ b/packages/hub/test/scope.test.ts @@ -85,8 +85,8 @@ describe('Scope', () => { test('setLevel', () => { const scope = new Scope(); - scope.setLevel('critical'); - expect((scope as any)._level).toEqual('critical'); + scope.setLevel('fatal'); + expect((scope as any)._level).toEqual('fatal'); }); test('setTransactionName', () => { @@ -137,8 +137,8 @@ describe('Scope', () => { test('chaining', () => { const scope = new Scope(); - scope.setLevel('critical').setUser({ id: '1' }); - expect((scope as any)._level).toEqual('critical'); + scope.setLevel('fatal').setUser({ id: '1' }); + expect((scope as any)._level).toEqual('fatal'); expect((scope as any)._user).toEqual({ id: '1' }); }); }); @@ -296,7 +296,7 @@ describe('Scope', () => { const scope = new Scope(); scope.setLevel('warning'); const event: Event = {}; - event.level = 'critical'; + event.level = 'fatal'; return scope.applyToEvent(event).then(processedEvent => { expect(processedEvent!.level).toEqual('warning'); }); diff --git a/packages/integration-tests/suites/public-api/captureMessage/with_level/subject.js b/packages/integration-tests/suites/public-api/captureMessage/with_level/subject.js index 3d7368c95fef..0d12e7115a7e 100644 --- a/packages/integration-tests/suites/public-api/captureMessage/with_level/subject.js +++ b/packages/integration-tests/suites/public-api/captureMessage/with_level/subject.js @@ -3,5 +3,4 @@ Sentry.captureMessage('info_message', 'info'); Sentry.captureMessage('warning_message', 'warning'); Sentry.captureMessage('error_message', 'error'); Sentry.captureMessage('fatal_message', 'fatal'); -Sentry.captureMessage('critical_message', 'critical'); Sentry.captureMessage('log_message', 'log'); diff --git a/packages/integration-tests/suites/public-api/captureMessage/with_level/test.ts b/packages/integration-tests/suites/public-api/captureMessage/with_level/test.ts index 45eaeca66161..da17ff07a77e 100644 --- a/packages/integration-tests/suites/public-api/captureMessage/with_level/test.ts +++ b/packages/integration-tests/suites/public-api/captureMessage/with_level/test.ts @@ -7,7 +7,7 @@ import { getMultipleSentryEnvelopeRequests } from '../../../../utils/helpers'; sentryTest('should capture with different severity levels', async ({ getLocalTestPath, page }) => { const url = await getLocalTestPath({ testDir: __dirname }); - const events = await getMultipleSentryEnvelopeRequests(page, 7, { url }); + const events = await getMultipleSentryEnvelopeRequests(page, 6, { url }); expect(events[0].message).toBe('debug_message'); expect(events[0].level).toBe('debug'); @@ -24,9 +24,6 @@ sentryTest('should capture with different severity levels', async ({ getLocalTes expect(events[4].message).toBe('fatal_message'); expect(events[4].level).toBe('fatal'); - expect(events[5].message).toBe('critical_message'); - expect(events[5].level).toBe('critical'); - - expect(events[6].message).toBe('log_message'); - expect(events[6].level).toBe('log'); + expect(events[5].message).toBe('log_message'); + expect(events[5].level).toBe('log'); }); diff --git a/packages/node-integration-tests/suites/public-api/addBreadcrumb/multiple_breadcrumbs/scenario.ts b/packages/node-integration-tests/suites/public-api/addBreadcrumb/multiple_breadcrumbs/scenario.ts index 993049500b08..b9ae05edefac 100644 --- a/packages/node-integration-tests/suites/public-api/addBreadcrumb/multiple_breadcrumbs/scenario.ts +++ b/packages/node-integration-tests/suites/public-api/addBreadcrumb/multiple_breadcrumbs/scenario.ts @@ -8,7 +8,7 @@ Sentry.init({ Sentry.addBreadcrumb({ category: 'foo', message: 'bar', - level: 'critical', + level: 'fatal', }); Sentry.addBreadcrumb({ diff --git a/packages/node-integration-tests/suites/public-api/addBreadcrumb/multiple_breadcrumbs/test.ts b/packages/node-integration-tests/suites/public-api/addBreadcrumb/multiple_breadcrumbs/test.ts index c8b894a62102..38ef745cb28e 100644 --- a/packages/node-integration-tests/suites/public-api/addBreadcrumb/multiple_breadcrumbs/test.ts +++ b/packages/node-integration-tests/suites/public-api/addBreadcrumb/multiple_breadcrumbs/test.ts @@ -10,7 +10,7 @@ test('should add multiple breadcrumbs', async () => { { category: 'foo', message: 'bar', - level: 'critical', + level: 'fatal', }, { category: 'qux', diff --git a/packages/node-integration-tests/suites/public-api/addBreadcrumb/simple_breadcrumb/scenario.ts b/packages/node-integration-tests/suites/public-api/addBreadcrumb/simple_breadcrumb/scenario.ts index a31b33c68d54..94ebd23dfaa5 100644 --- a/packages/node-integration-tests/suites/public-api/addBreadcrumb/simple_breadcrumb/scenario.ts +++ b/packages/node-integration-tests/suites/public-api/addBreadcrumb/simple_breadcrumb/scenario.ts @@ -8,7 +8,7 @@ Sentry.init({ Sentry.addBreadcrumb({ category: 'foo', message: 'bar', - level: 'critical', + level: 'fatal', }); Sentry.captureMessage('test_simple'); diff --git a/packages/node-integration-tests/suites/public-api/addBreadcrumb/simple_breadcrumb/test.ts b/packages/node-integration-tests/suites/public-api/addBreadcrumb/simple_breadcrumb/test.ts index a56f0711e086..d5fc2ef8df73 100644 --- a/packages/node-integration-tests/suites/public-api/addBreadcrumb/simple_breadcrumb/test.ts +++ b/packages/node-integration-tests/suites/public-api/addBreadcrumb/simple_breadcrumb/test.ts @@ -10,7 +10,7 @@ test('should add a simple breadcrumb', async () => { { category: 'foo', message: 'bar', - level: 'critical', + level: 'fatal', }, ], }); diff --git a/packages/node-integration-tests/suites/public-api/captureMessage/with_level/scenario.ts b/packages/node-integration-tests/suites/public-api/captureMessage/with_level/scenario.ts index 32d46fa171fe..be175d36e816 100644 --- a/packages/node-integration-tests/suites/public-api/captureMessage/with_level/scenario.ts +++ b/packages/node-integration-tests/suites/public-api/captureMessage/with_level/scenario.ts @@ -10,5 +10,4 @@ Sentry.captureMessage('info_message', 'info'); Sentry.captureMessage('warning_message', 'warning'); Sentry.captureMessage('error_message', 'error'); Sentry.captureMessage('fatal_message', 'fatal'); -Sentry.captureMessage('critical_message', 'critical'); Sentry.captureMessage('log_message', 'log'); diff --git a/packages/node-integration-tests/suites/public-api/captureMessage/with_level/test.ts b/packages/node-integration-tests/suites/public-api/captureMessage/with_level/test.ts index 5db1ef646168..18bd395b148d 100644 --- a/packages/node-integration-tests/suites/public-api/captureMessage/with_level/test.ts +++ b/packages/node-integration-tests/suites/public-api/captureMessage/with_level/test.ts @@ -2,7 +2,7 @@ import { assertSentryEvent, getMultipleEnvelopeRequest, runServer } from '../../ test('should capture with different severity levels', async () => { const url = await runServer(__dirname); - const envelopes = await getMultipleEnvelopeRequest(url, 14); + const envelopes = await getMultipleEnvelopeRequest(url, 12); assertSentryEvent(envelopes[1][2], { message: 'debug_message', @@ -30,11 +30,6 @@ test('should capture with different severity levels', async () => { }); assertSentryEvent(envelopes[11][2], { - message: 'critical_message', - level: 'critical', - }); - - assertSentryEvent(envelopes[13][2], { message: 'log_message', level: 'log', }); diff --git a/packages/types/src/severity.ts b/packages/types/src/severity.ts index ad96231cb013..de13f2a02b99 100644 --- a/packages/types/src/severity.ts +++ b/packages/types/src/severity.ts @@ -1,6 +1,6 @@ /** * @deprecated Please use a `SeverityLevel` string instead of the `Severity` enum. Acceptable values are 'fatal', - * 'critical', 'error', 'warning', 'log', 'info', and 'debug'. + * 'error', 'warning', 'log', 'info', and 'debug'. */ export enum Severity { /** JSDoc */ @@ -15,10 +15,8 @@ export enum Severity { Info = 'info', /** JSDoc */ Debug = 'debug', - /** JSDoc */ - Critical = 'critical', } // Note: If this is ever changed, the `validSeverityLevels` array in `@sentry/utils` needs to be changed, also. (See // note there for why we can't derive one from the other.) -export type SeverityLevel = 'fatal' | 'error' | 'warning' | 'log' | 'info' | 'debug' | 'critical'; +export type SeverityLevel = 'fatal' | 'error' | 'warning' | 'log' | 'info' | 'debug'; diff --git a/packages/utils/src/severity.ts b/packages/utils/src/severity.ts index ba2ad2822851..c40f0c2a6004 100644 --- a/packages/utils/src/severity.ts +++ b/packages/utils/src/severity.ts @@ -11,7 +11,7 @@ import { Severity, SeverityLevel } from '@sentry/types'; // create a circular dependency between `@sentry/types` and `@sentry/utils` (also not good). So a TODO accompanying the // type, reminding anyone who changes it to change this list also, will have to do. -export const validSeverityLevels = ['fatal', 'error', 'warning', 'log', 'info', 'debug', 'critical']; +export const validSeverityLevels = ['fatal', 'error', 'warning', 'log', 'info', 'debug']; /** * Converts a string-based level into a member of the deprecated {@link Severity} enum. From 9782d699cd28baf2eac1cd4a41e3726cc9d89cd0 Mon Sep 17 00:00:00 2001 From: Luca Forstner Date: Thu, 5 May 2022 13:57:55 +0200 Subject: [PATCH 119/204] ref: Delete unneeded `SDK_NAME` (#5040) --- MIGRATION.md | 5 +++-- packages/browser/src/exports.ts | 1 - packages/browser/src/version.ts | 2 -- packages/node/src/index.ts | 1 - packages/node/src/version.ts | 2 -- packages/tracing/src/index.bundle.ts | 2 +- packages/vue/src/index.bundle.ts | 1 - 7 files changed, 4 insertions(+), 10 deletions(-) delete mode 100644 packages/browser/src/version.ts delete mode 100644 packages/node/src/version.ts diff --git a/MIGRATION.md b/MIGRATION.md index 64a795237c24..950a8ce6cea1 100644 --- a/MIGRATION.md +++ b/MIGRATION.md @@ -281,8 +281,9 @@ For our efforts to reduce bundle size of the SDK we had to remove and refactor p [#4919](https://github.com/getsentry/sentry-javascript/pull/4919)). `Backend` was an unnecessary abstraction which is not present in other Sentry SDKs. For the sake of reducing complexity, increasing consistency with other Sentry SDKs and decreasing bundle-size, `Backend` was removed. -- Remove support for Opera browser pre v15 -- Rename `UserAgent` integration to `HttpContext` (see [#5027](https://github.com/getsentry/sentry-javascript/pull/5027)) +- Remove support for Opera browser pre v15. +- Rename `UserAgent` integration to `HttpContext`. (see [#5027](https://github.com/getsentry/sentry-javascript/pull/5027)) +- Remove `SDK_NAME` export from `@sentry/browser`, `@sentry/node`, `@sentry/tracing` and `@sentry/vue` packages. ## Sentry Angular SDK Changes diff --git a/packages/browser/src/exports.ts b/packages/browser/src/exports.ts index 4055ef8e595b..ac3cea82190d 100644 --- a/packages/browser/src/exports.ts +++ b/packages/browser/src/exports.ts @@ -57,5 +57,4 @@ export { winjsStackLineParser, } from './stack-parsers'; export { defaultIntegrations, forceLoad, init, lastEventId, onLoad, showReportDialog, flush, close, wrap } from './sdk'; -export { SDK_NAME } from './version'; export { GlobalHandlers, TryCatch, Breadcrumbs, LinkedErrors, HttpContext, Dedupe } from './integrations'; diff --git a/packages/browser/src/version.ts b/packages/browser/src/version.ts deleted file mode 100644 index 462be137a6ca..000000000000 --- a/packages/browser/src/version.ts +++ /dev/null @@ -1,2 +0,0 @@ -// TODO: Remove in the next major release and rely only on @sentry/core SDK_VERSION and SdkInfo metadata -export const SDK_NAME = 'sentry.javascript.browser'; diff --git a/packages/node/src/index.ts b/packages/node/src/index.ts index ae1e38cc150d..823dc73d848e 100644 --- a/packages/node/src/index.ts +++ b/packages/node/src/index.ts @@ -46,7 +46,6 @@ export { NodeClient } from './client'; export { makeNodeTransport } from './transports'; export { defaultIntegrations, init, lastEventId, flush, close, getSentryRelease } from './sdk'; export { deepReadDirSync } from './utils'; -export { SDK_NAME } from './version'; export { defaultStackParser } from './stack-parser'; import { Integrations as CoreIntegrations } from '@sentry/core'; diff --git a/packages/node/src/version.ts b/packages/node/src/version.ts deleted file mode 100644 index 8cfd1070cec5..000000000000 --- a/packages/node/src/version.ts +++ /dev/null @@ -1,2 +0,0 @@ -// TODO: Remove in the next major release and rely only on @sentry/core SDK_VERSION and SdkMetadata -export const SDK_NAME = 'sentry.javascript.node'; diff --git a/packages/tracing/src/index.bundle.ts b/packages/tracing/src/index.bundle.ts index 05936d798a66..bb55da2cd273 100644 --- a/packages/tracing/src/index.bundle.ts +++ b/packages/tracing/src/index.bundle.ts @@ -50,7 +50,7 @@ export { close, wrap, } from '@sentry/browser'; -export { SDK_NAME, SDK_VERSION } from '@sentry/browser'; +export { SDK_VERSION } from '@sentry/browser'; import { Integrations as BrowserIntegrations } from '@sentry/browser'; import { getGlobalObject } from '@sentry/utils'; diff --git a/packages/vue/src/index.bundle.ts b/packages/vue/src/index.bundle.ts index 690731711855..57059e55e80c 100644 --- a/packages/vue/src/index.bundle.ts +++ b/packages/vue/src/index.bundle.ts @@ -43,7 +43,6 @@ export { makeFetchTransport, makeXHRTransport, withScope, - SDK_NAME, SDK_VERSION, } from '@sentry/browser'; From 419416a33f3e84dee4caa55e3708927fa02a3d89 Mon Sep 17 00:00:00 2001 From: Luca Forstner Date: Thu, 5 May 2022 14:41:04 +0200 Subject: [PATCH 120/204] docs(migration): Add TLDR to v7 migration docs (#5041) --- MIGRATION.md | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/MIGRATION.md b/MIGRATION.md index 950a8ce6cea1..cac9ad2feb96 100644 --- a/MIGRATION.md +++ b/MIGRATION.md @@ -3,6 +3,13 @@ The main goal of version 7 is to reduce bundle size. This version is breaking because we removed deprecated APIs, upgraded our build tooling, and restructured npm package contents. Below we will outline all the breaking changes you should consider when upgrading. +**TL;DR** If you only use basic features of Sentry, or you simply copy & pasted the setup examples from our docs, here's what changed for you: +- Our CDN bundles are now ES6 - you will need to [reconfigure your script tags](#renaming-of-cdn-bundles) if you want to keep supporting ES5 and IE11 on the new SDK version. +- Distributed CommonJS files will be ES6. Use a transpiler if you need to support old node versions. +- We bumped the TypeScript version we generate our types with to 3.8.3. Please check if your TypeScript projects using TypeScript version 3.7 or lower still compile. Otherwise, upgrade your TypeScript version. +- `whitelistUrls` and `blacklistUrls` have been renamed to `allowUrls` and `denyUrls` in the `Sentry.init()` options. +- The `UserAgent` integration is now called `HttpContext`. + ## Dropping Support for Node.js v6 Node.js version 6 has reached end of life in April 2019. For Sentry JavaScript SDK version 7, we will no longer be supporting version 6 of Node.js. @@ -252,7 +259,6 @@ favor of string literals. ### Removed Enums * The previously deprecated enum `Status` was removed (see [#4891](https://github.com/getsentry/sentry-javascript/pull/4891)). - [This example](#status) explains how to migrate. * The previously deprecated internal-only enum `RequestSessionStatus` was removed (see [#4889](https://github.com/getsentry/sentry-javascript/pull/4889)) in favor of string literals. * The previously deprecated internal-only enum `SessionStatus` was removed (see @@ -284,6 +290,7 @@ For our efforts to reduce bundle size of the SDK we had to remove and refactor p - Remove support for Opera browser pre v15. - Rename `UserAgent` integration to `HttpContext`. (see [#5027](https://github.com/getsentry/sentry-javascript/pull/5027)) - Remove `SDK_NAME` export from `@sentry/browser`, `@sentry/node`, `@sentry/tracing` and `@sentry/vue` packages. +- Removed `eventStatusFromHttpCode` to save on bundle size. ## Sentry Angular SDK Changes From 1e210fe1d5d08301373a11e9f07515e072de66ed Mon Sep 17 00:00:00 2001 From: Matt Johnson-Pint Date: Thu, 5 May 2022 13:39:05 -0700 Subject: [PATCH 121/204] chore: update sentry logo in readme files (#5045) --- CONTRIBUTING.md | 5 ++--- README.md | 5 ++--- packages/angular/README.md | 5 ++--- packages/browser/README.md | 5 ++--- packages/core/README.md | 5 ++--- packages/ember/README.md | 5 ++--- packages/eslint-config-sdk/README.md | 5 ++--- packages/gatsby/README.md | 5 ++--- packages/hub/README.md | 5 ++--- packages/integrations/README.md | 5 ++--- packages/nextjs/README.md | 5 ++--- packages/node/README.md | 5 ++--- packages/react/README.md | 5 ++--- packages/serverless/README.md | 5 ++--- packages/tracing/README.md | 5 ++--- packages/types/README.md | 5 ++--- packages/typescript/README.md | 5 ++--- packages/utils/README.md | 5 ++--- packages/vue/README.md | 5 ++--- packages/wasm/README.md | 5 ++--- 20 files changed, 40 insertions(+), 60 deletions(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 7ca43b81a28f..7aee8627a350 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -1,8 +1,7 @@

- - + + Sentry -

# Contributing diff --git a/README.md b/README.md index 369c363e3599..503d198e15d2 100644 --- a/README.md +++ b/README.md @@ -1,8 +1,7 @@

- - + + Sentry -

_Bad software is everywhere, and we're tired of it. Sentry is on a mission to help developers write better software faster, so we can get back to enjoying technology. If you want to join us [**Check out our open positions**](https://sentry.io/careers/)_ diff --git a/packages/angular/README.md b/packages/angular/README.md index 82132d7543b2..dd288b4951a4 100644 --- a/packages/angular/README.md +++ b/packages/angular/README.md @@ -1,8 +1,7 @@

- - + + Sentry -

# Official Sentry SDK for Angular diff --git a/packages/browser/README.md b/packages/browser/README.md index 486505c970b8..63c3ab561a6a 100644 --- a/packages/browser/README.md +++ b/packages/browser/README.md @@ -1,8 +1,7 @@

- - + + Sentry -

# Official Sentry SDK for Browsers diff --git a/packages/core/README.md b/packages/core/README.md index b228fc669a80..0226b9e41be3 100644 --- a/packages/core/README.md +++ b/packages/core/README.md @@ -1,8 +1,7 @@

- - + + Sentry -

# Sentry JavaScript SDK Core diff --git a/packages/ember/README.md b/packages/ember/README.md index a141ec8c2431..0b801a071ad4 100644 --- a/packages/ember/README.md +++ b/packages/ember/README.md @@ -1,8 +1,7 @@

- - + + Sentry -

# Official Sentry SDK for Ember.js diff --git a/packages/eslint-config-sdk/README.md b/packages/eslint-config-sdk/README.md index 7af88bd47989..28d113bdfbcd 100644 --- a/packages/eslint-config-sdk/README.md +++ b/packages/eslint-config-sdk/README.md @@ -1,8 +1,7 @@

- - + + Sentry -

# Official Sentry SDK eslint config diff --git a/packages/gatsby/README.md b/packages/gatsby/README.md index 85f3e70dee85..22e181c5c70f 100644 --- a/packages/gatsby/README.md +++ b/packages/gatsby/README.md @@ -1,8 +1,7 @@

- - + + Sentry -

# Official Sentry SDK for GatsbyJS diff --git a/packages/hub/README.md b/packages/hub/README.md index 7a4da6390db0..ed2e5a20d950 100644 --- a/packages/hub/README.md +++ b/packages/hub/README.md @@ -1,8 +1,7 @@

- - + + Sentry -

# Sentry JavaScript SDK Hub diff --git a/packages/integrations/README.md b/packages/integrations/README.md index 1a666b201983..042a01139d58 100644 --- a/packages/integrations/README.md +++ b/packages/integrations/README.md @@ -1,8 +1,7 @@

- - + + Sentry -

# Sentry JavaScript SDK Integrations diff --git a/packages/nextjs/README.md b/packages/nextjs/README.md index 19660295f666..3f8147d1d9a6 100644 --- a/packages/nextjs/README.md +++ b/packages/nextjs/README.md @@ -1,8 +1,7 @@

- - + + Sentry -

# Official Sentry SDK for Next.js diff --git a/packages/node/README.md b/packages/node/README.md index f59c831b0055..fee8104478ac 100644 --- a/packages/node/README.md +++ b/packages/node/README.md @@ -1,8 +1,7 @@

- - + + Sentry -

# Official Sentry SDK for NodeJS diff --git a/packages/react/README.md b/packages/react/README.md index e5aea3323b14..761083801ca1 100644 --- a/packages/react/README.md +++ b/packages/react/README.md @@ -1,8 +1,7 @@

- - + + Sentry -

# Official Sentry SDK for ReactJS diff --git a/packages/serverless/README.md b/packages/serverless/README.md index e6f819f0cf66..c0ac6f7659aa 100644 --- a/packages/serverless/README.md +++ b/packages/serverless/README.md @@ -1,8 +1,7 @@

- - + + Sentry -

# Official Sentry SDK for Serverless environments diff --git a/packages/tracing/README.md b/packages/tracing/README.md index 5454433f73b8..d37633c9decd 100644 --- a/packages/tracing/README.md +++ b/packages/tracing/README.md @@ -1,8 +1,7 @@

- - + + Sentry -

# Sentry Tracing Extensions diff --git a/packages/types/README.md b/packages/types/README.md index 6d0a1322cffa..c5870f59ba41 100644 --- a/packages/types/README.md +++ b/packages/types/README.md @@ -1,8 +1,7 @@

- - + + Sentry -

# Sentry JavaScript SDK Types diff --git a/packages/typescript/README.md b/packages/typescript/README.md index aa32b9fdf48b..fc8d7e2f570d 100644 --- a/packages/typescript/README.md +++ b/packages/typescript/README.md @@ -1,8 +1,7 @@

- - + + Sentry -

# Sentry TypeScript Configuration diff --git a/packages/utils/README.md b/packages/utils/README.md index 185fc3778f44..b2dac05757dc 100644 --- a/packages/utils/README.md +++ b/packages/utils/README.md @@ -1,8 +1,7 @@

- - + + Sentry -

# Sentry JavaScript SDK Utilities diff --git a/packages/vue/README.md b/packages/vue/README.md index 6c2d5072b1a5..c8fd91af2b24 100644 --- a/packages/vue/README.md +++ b/packages/vue/README.md @@ -1,8 +1,7 @@

- - + + Sentry -

# Official Sentry SDK for Vue.js diff --git a/packages/wasm/README.md b/packages/wasm/README.md index c1189e294d20..099bcb1e8f20 100644 --- a/packages/wasm/README.md +++ b/packages/wasm/README.md @@ -1,8 +1,7 @@

- - + + Sentry -

# Sentry JavaScript WebAssembly Support From c9e3602fcdc8bdcd1b0a18c99c6ed5181651f38f Mon Sep 17 00:00:00 2001 From: Lukas Stracke Date: Fri, 6 May 2022 15:32:42 +0200 Subject: [PATCH 122/204] fix(tracing): Remove `isInstanceOf` check in Hub constructor (#5046) fix a bug which assigned the wrong Hub instance to a Transaction in the Transaction constructor. Remove the isInstanceOf check which incorrectly failed when passing a user-created Hub the constructor. Overall, simplify the hub initialization by deciding in the constructor if the passed Hub should be used. As a fallback, the Hub returned from getCurrentHub is used. Co-authored by: @lobsterkatie --- packages/tracing/src/transaction.ts | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/packages/tracing/src/transaction.ts b/packages/tracing/src/transaction.ts index 0001589349ca..fb6cd9d02b6c 100644 --- a/packages/tracing/src/transaction.ts +++ b/packages/tracing/src/transaction.ts @@ -6,7 +6,7 @@ import { TransactionContext, TransactionMetadata, } from '@sentry/types'; -import { dropUndefinedKeys, isInstanceOf, logger } from '@sentry/utils'; +import { dropUndefinedKeys, logger } from '@sentry/utils'; import { IS_DEBUG_BUILD } from './flags'; import { Span as SpanClass, SpanRecorder } from './span'; @@ -22,7 +22,7 @@ export class Transaction extends SpanClass implements TransactionInterface { /** * The reference to the current hub. */ - private readonly _hub: Hub = getCurrentHub() as unknown as Hub; + private readonly _hub: Hub; private _trimEnd?: boolean; @@ -36,9 +36,7 @@ export class Transaction extends SpanClass implements TransactionInterface { public constructor(transactionContext: TransactionContext, hub?: Hub) { super(transactionContext); - if (isInstanceOf(hub, Hub)) { - this._hub = hub as Hub; - } + this._hub = hub || getCurrentHub(); this.name = transactionContext.name || ''; From 8b16378387683c095f79f783916d4bd43b2f8d27 Mon Sep 17 00:00:00 2001 From: Abhijeet Prasad Date: Fri, 6 May 2022 16:11:57 -0400 Subject: [PATCH 123/204] ref(tracing): Reset IdleTimeout based on activities count (#5044) Previously, when the activities count of an idle transaction hit 0, it would trigger a timeout for `idleTimeout` ms, and then always end the transaction. This means that if a span (like fetch/xhr request) started right after the activities count went to 0 (1 -> 0 -> 1), the transaction would always finish after `idleTimeout` ms, instead of waiting for the newest activity to finish. This was primarily done to prevent polling conditions and to not artificially inflate duration. Nowadays though, web vitals like LCP are a lot more important as a measurement in transactions than the strict duration (as with activities, they are a bit arbitrary). By making `idleTimeout` be strict about finish after activities go to 0, we often times would miss the LCP value that the browser would record, leading to many transactions not having proper LCPs. To get the LCP value close to browser quality as possible, we now reset the `idleTimeout` timeout if there are still existing activities. The concern here is with polling. To prevent polling issues, we now also have an additional `finalTimeout` that is started when the idle transaction is created. This double timeout system (alongside the heartbeat), should always enforce that the transaction is finished. --- MIGRATION.md | 1 + .../backgroundtab-custom/test.ts | 2 +- .../backgroundtab-pageload/test.ts | 1 - packages/tracing/src/browser/backgroundtab.ts | 2 - .../tracing/src/browser/browsertracing.ts | 55 ++++------- packages/tracing/src/constants.ts | 5 - packages/tracing/src/hubextensions.ts | 6 +- packages/tracing/src/idletransaction.ts | 81 ++++++++------- .../test/browser/browsertracing.test.ts | 48 +-------- packages/tracing/test/idletransaction.test.ts | 99 +++++++++++++------ 10 files changed, 149 insertions(+), 151 deletions(-) delete mode 100644 packages/tracing/src/constants.ts diff --git a/MIGRATION.md b/MIGRATION.md index cac9ad2feb96..cda6cb2836db 100644 --- a/MIGRATION.md +++ b/MIGRATION.md @@ -291,6 +291,7 @@ For our efforts to reduce bundle size of the SDK we had to remove and refactor p - Rename `UserAgent` integration to `HttpContext`. (see [#5027](https://github.com/getsentry/sentry-javascript/pull/5027)) - Remove `SDK_NAME` export from `@sentry/browser`, `@sentry/node`, `@sentry/tracing` and `@sentry/vue` packages. - Removed `eventStatusFromHttpCode` to save on bundle size. +- Replace `BrowserTracing` `maxTransactionDuration` option with `finalTimeout` option ## Sentry Angular SDK Changes diff --git a/packages/integration-tests/suites/tracing/browsertracing/backgroundtab-custom/test.ts b/packages/integration-tests/suites/tracing/browsertracing/backgroundtab-custom/test.ts index a21cff2f1831..8a50d643e0c9 100644 --- a/packages/integration-tests/suites/tracing/browsertracing/backgroundtab-custom/test.ts +++ b/packages/integration-tests/suites/tracing/browsertracing/backgroundtab-custom/test.ts @@ -36,5 +36,5 @@ sentryTest('should finish a custom transaction when the page goes background', a expect(id_before).toBe(id_after); expect(name_after).toBe(name_before); expect(status_after).toBe('cancelled'); - expect(tags_after).toStrictEqual({ finishReason: 'documentHidden', visibilitychange: 'document.hidden' }); + expect(tags_after).toStrictEqual({ visibilitychange: 'document.hidden' }); }); diff --git a/packages/integration-tests/suites/tracing/browsertracing/backgroundtab-pageload/test.ts b/packages/integration-tests/suites/tracing/browsertracing/backgroundtab-pageload/test.ts index 47f695b0440c..149eb3e87a33 100644 --- a/packages/integration-tests/suites/tracing/browsertracing/backgroundtab-pageload/test.ts +++ b/packages/integration-tests/suites/tracing/browsertracing/backgroundtab-pageload/test.ts @@ -17,6 +17,5 @@ sentryTest('should finish pageload transaction when the page goes background', a expect(pageloadTransaction.contexts?.trace.status).toBe('cancelled'); expect(pageloadTransaction.contexts?.trace.tags).toMatchObject({ visibilitychange: 'document.hidden', - finishReason: 'documentHidden', }); }); diff --git a/packages/tracing/src/browser/backgroundtab.ts b/packages/tracing/src/browser/backgroundtab.ts index 2448247188bd..64bb06d2d70f 100644 --- a/packages/tracing/src/browser/backgroundtab.ts +++ b/packages/tracing/src/browser/backgroundtab.ts @@ -1,6 +1,5 @@ import { getGlobalObject, logger } from '@sentry/utils'; -import { FINISH_REASON_TAG, IDLE_TRANSACTION_FINISH_REASONS } from '../constants'; import { IS_DEBUG_BUILD } from '../flags'; import { IdleTransaction } from '../idletransaction'; import { SpanStatusType } from '../span'; @@ -29,7 +28,6 @@ export function registerBackgroundTabDetection(): void { activeTransaction.setStatus(statusType); } activeTransaction.setTag('visibilitychange', 'document.hidden'); - activeTransaction.setTag(FINISH_REASON_TAG, IDLE_TRANSACTION_FINISH_REASONS[2]); activeTransaction.finish(); } }); diff --git a/packages/tracing/src/browser/browsertracing.ts b/packages/tracing/src/browser/browsertracing.ts index e9d02db6d7d7..780edf9052d4 100644 --- a/packages/tracing/src/browser/browsertracing.ts +++ b/packages/tracing/src/browser/browsertracing.ts @@ -4,8 +4,8 @@ import { getGlobalObject, logger } from '@sentry/utils'; import { IS_DEBUG_BUILD } from '../flags'; import { startIdleTransaction } from '../hubextensions'; -import { DEFAULT_IDLE_TIMEOUT, IdleTransaction } from '../idletransaction'; -import { extractTraceparentData, secToMs } from '../utils'; +import { DEFAULT_FINAL_TIMEOUT, DEFAULT_IDLE_TIMEOUT } from '../idletransaction'; +import { extractTraceparentData } from '../utils'; import { registerBackgroundTabDetection } from './backgroundtab'; import { MetricsInstrumentation } from './metrics'; import { @@ -15,19 +15,29 @@ import { } from './request'; import { instrumentRoutingWithDefaults } from './router'; -export const DEFAULT_MAX_TRANSACTION_DURATION_SECONDS = 600; - /** Options for Browser Tracing integration */ export interface BrowserTracingOptions extends RequestInstrumentationOptions { /** - * The time to wait in ms until the transaction will be finished. The transaction will use the end timestamp of - * the last finished span as the endtime for the transaction. + * The time to wait in ms until the transaction will be finished during an idle state. An idle state is defined + * by a moment where there are no in-progress spans. + * + * The transaction will use the end timestamp of the last finished span as the endtime for the transaction. + * If there are still active spans when this the `idleTimeout` is set, the `idleTimeout` will get reset. * Time is in ms. * * Default: 1000 */ idleTimeout: number; + /** + * The max duration for a transaction. If a transaction duration hits the `finalTimeout` value, it + * will be finished. + * Time is in ms. + * + * Default: 30000 + */ + finalTimeout: number; + /** * Flag to enable/disable creation of `navigation` transaction on history changes. * @@ -42,15 +52,6 @@ export interface BrowserTracingOptions extends RequestInstrumentationOptions { */ startTransactionOnPageLoad: boolean; - /** - * The maximum duration of a transaction before it will be marked as "deadline_exceeded". - * If you never want to mark a transaction set it to 0. - * Time is in seconds. - * - * Default: 600 - */ - maxTransactionDuration: number; - /** * Flag Transactions where tabs moved to background with "cancelled". Browser background tab timing is * not suited towards doing precise measurements of operations. By default, we recommend that this option @@ -94,8 +95,8 @@ export interface BrowserTracingOptions extends RequestInstrumentationOptions { const DEFAULT_BROWSER_TRACING_OPTIONS = { idleTimeout: DEFAULT_IDLE_TIMEOUT, + finalTimeout: DEFAULT_FINAL_TIMEOUT, markBackgroundTransactions: true, - maxTransactionDuration: DEFAULT_MAX_TRANSACTION_DURATION_SECONDS, routingInstrumentation: instrumentRoutingWithDefaults, startTransactionOnLocationChange: true, startTransactionOnPageLoad: true, @@ -129,14 +130,10 @@ export class BrowserTracing implements Integration { private readonly _emitOptionsWarning?: boolean; - /** Store configured idle timeout so that it can be added as a tag to transactions */ - private _configuredIdleTimeout: BrowserTracingOptions['idleTimeout'] | undefined = undefined; - public constructor(_options?: Partial) { let tracingOrigins = defaultRequestInstrumentationOptions.tracingOrigins; // NOTE: Logger doesn't work in constructors, as it's initialized after integrations instances if (_options) { - this._configuredIdleTimeout = _options.idleTimeout; if (_options.tracingOrigins && Array.isArray(_options.tracingOrigins) && _options.tracingOrigins.length !== 0) { tracingOrigins = _options.tracingOrigins; } else { @@ -205,7 +202,7 @@ export class BrowserTracing implements Integration { } // eslint-disable-next-line @typescript-eslint/unbound-method - const { beforeNavigate, idleTimeout, maxTransactionDuration } = this.options; + const { beforeNavigate, idleTimeout, finalTimeout } = this.options; const parentContextFromHeader = context.op === 'pageload' ? getHeaderContext() : undefined; @@ -233,16 +230,14 @@ export class BrowserTracing implements Integration { hub, finalContext, idleTimeout, + finalTimeout, true, { location }, // for use in the tracesSampler ); - idleTransaction.registerBeforeFinishCallback((transaction, endTimestamp) => { + idleTransaction.registerBeforeFinishCallback(transaction => { this._metrics.addPerformanceEntries(transaction); - adjustTransactionDuration(secToMs(maxTransactionDuration), transaction, endTimestamp); }); - idleTransaction.setTag('idleTimeout', this._configuredIdleTimeout); - return idleTransaction as Transaction; } } @@ -266,13 +261,3 @@ export function getMetaContent(metaName: string): string | null { const el = getGlobalObject().document.querySelector(`meta[name=${metaName}]`); return el ? el.getAttribute('content') : null; } - -/** Adjusts transaction value based on max transaction duration */ -function adjustTransactionDuration(maxDuration: number, transaction: IdleTransaction, endTimestamp: number): void { - const diff = endTimestamp - transaction.startTimestamp; - const isOutdatedTransaction = endTimestamp && (diff > maxDuration || diff < 0); - if (isOutdatedTransaction) { - transaction.setStatus('deadline_exceeded'); - transaction.setTag('maxTransactionDurationExceeded', 'true'); - } -} diff --git a/packages/tracing/src/constants.ts b/packages/tracing/src/constants.ts deleted file mode 100644 index 3f01982c3614..000000000000 --- a/packages/tracing/src/constants.ts +++ /dev/null @@ -1,5 +0,0 @@ -// Store finish reasons in tuple to save on bundle size -// Readonly type should enforce that this is not mutated. -export const FINISH_REASON_TAG = 'finishReason'; - -export const IDLE_TRANSACTION_FINISH_REASONS = ['heartbeatFailed', 'idleTimeout', 'documentHidden'] as const; diff --git a/packages/tracing/src/hubextensions.ts b/packages/tracing/src/hubextensions.ts index 80bd7a5783c8..dbcb4fb66029 100644 --- a/packages/tracing/src/hubextensions.ts +++ b/packages/tracing/src/hubextensions.ts @@ -1,3 +1,4 @@ +/* eslint-disable max-lines */ import { getMainCarrier, Hub } from '@sentry/hub'; import { ClientOptions, @@ -196,14 +197,15 @@ function _startTransaction( export function startIdleTransaction( hub: Hub, transactionContext: TransactionContext, - idleTimeout?: number, + idleTimeout: number, + finalTimeout: number, onScope?: boolean, customSamplingContext?: CustomSamplingContext, ): IdleTransaction { const client = hub.getClient(); const options: Partial = (client && client.getOptions()) || {}; - let transaction = new IdleTransaction(transactionContext, hub, idleTimeout, onScope); + let transaction = new IdleTransaction(transactionContext, hub, idleTimeout, finalTimeout, onScope); transaction = sample(transaction, options, { parentSampled: transactionContext.parentSampled, transactionContext, diff --git a/packages/tracing/src/idletransaction.ts b/packages/tracing/src/idletransaction.ts index 0d292eebba65..9353f552fcc9 100644 --- a/packages/tracing/src/idletransaction.ts +++ b/packages/tracing/src/idletransaction.ts @@ -1,13 +1,14 @@ +/* eslint-disable max-lines */ import { Hub } from '@sentry/hub'; import { TransactionContext } from '@sentry/types'; import { logger, timestampWithMs } from '@sentry/utils'; -import { FINISH_REASON_TAG, IDLE_TRANSACTION_FINISH_REASONS } from './constants'; import { IS_DEBUG_BUILD } from './flags'; import { Span, SpanRecorder } from './span'; import { Transaction } from './transaction'; export const DEFAULT_IDLE_TIMEOUT = 1000; +export const DEFAULT_FINAL_TIMEOUT = 30000; export const HEARTBEAT_INTERVAL = 5000; /** @@ -17,7 +18,7 @@ export class IdleTransactionSpanRecorder extends SpanRecorder { public constructor( private readonly _pushActivity: (id: string) => void, private readonly _popActivity: (id: string) => void, - public transactionSpanId: string = '', + public transactionSpanId: string, maxlen?: number, ) { super(maxlen); @@ -69,25 +70,28 @@ export class IdleTransaction extends Transaction { private readonly _beforeFinishCallbacks: BeforeFinishCallback[] = []; /** - * If a transaction is created and no activities are added, we want to make sure that - * it times out properly. This is cleared and not used when activities are added. + * Timer that tracks Transaction idleTimeout */ - private _initTimeout: ReturnType | undefined; + private _idleTimeoutID: ReturnType | undefined; public constructor( transactionContext: TransactionContext, - private readonly _idleHub?: Hub, + private readonly _idleHub: Hub, /** - * The time to wait in ms until the idle transaction will be finished. - * @default 1000 + * The time to wait in ms until the idle transaction will be finished. This timer is started each time + * there are no active spans on this transaction. */ private readonly _idleTimeout: number = DEFAULT_IDLE_TIMEOUT, + /** + * The final value in ms that a transaction cannot exceed + */ + private readonly _finalTimeout: number = DEFAULT_FINAL_TIMEOUT, // Whether or not the transaction should put itself on the scope when it starts and pop itself off when it ends private readonly _onScope: boolean = false, ) { super(transactionContext, _idleHub); - if (_idleHub && _onScope) { + if (_onScope) { // There should only be one active transaction on the scope clearActiveTransaction(_idleHub); @@ -97,11 +101,13 @@ export class IdleTransaction extends Transaction { _idleHub.configureScope(scope => scope.setSpan(this)); } - this._initTimeout = setTimeout(() => { + this._startIdleTimeout(); + setTimeout(() => { if (!this._finished) { + this.setStatus('deadline_exceeded'); this.finish(); } - }, this._idleTimeout); + }, this._finalTimeout); } /** {@inheritDoc} */ @@ -193,15 +199,34 @@ export class IdleTransaction extends Transaction { this.spanRecorder.add(this); } + /** + * Cancels the existing idletimeout, if there is one + */ + private _cancelIdleTimeout(): void { + if (this._idleTimeoutID) { + clearTimeout(this._idleTimeoutID); + this._idleTimeoutID = undefined; + } + } + + /** + * Creates an idletimeout + */ + private _startIdleTimeout(endTimestamp?: Parameters[0]): void { + this._cancelIdleTimeout(); + this._idleTimeoutID = setTimeout(() => { + if (!this._finished && Object.keys(this.activities).length === 0) { + this.finish(endTimestamp); + } + }, this._idleTimeout); + } + /** * Start tracking a specific activity. * @param spanId The span id that represents the activity */ private _pushActivity(spanId: string): void { - if (this._initTimeout) { - clearTimeout(this._initTimeout); - this._initTimeout = undefined; - } + this._cancelIdleTimeout(); IS_DEBUG_BUILD && logger.log(`[Tracing] pushActivity: ${spanId}`); this.activities[spanId] = true; IS_DEBUG_BUILD && logger.log('[Tracing] new activities count', Object.keys(this.activities).length); @@ -220,17 +245,10 @@ export class IdleTransaction extends Transaction { } if (Object.keys(this.activities).length === 0) { - const timeout = this._idleTimeout; // We need to add the timeout here to have the real endtimestamp of the transaction // Remember timestampWithMs is in seconds, timeout is in ms - const end = timestampWithMs() + timeout / 1000; - - setTimeout(() => { - if (!this._finished) { - this.setTag(FINISH_REASON_TAG, IDLE_TRANSACTION_FINISH_REASONS[1]); - this.finish(end); - } - }, timeout); + const endTimestamp = timestampWithMs() + this._idleTimeout / 1000; + this._startIdleTimeout(endTimestamp); } } @@ -257,7 +275,6 @@ export class IdleTransaction extends Transaction { if (this._heartbeatCounter >= 3) { IS_DEBUG_BUILD && logger.log('[Tracing] Transaction finished because of no change for 3 heart beats'); this.setStatus('deadline_exceeded'); - this.setTag(FINISH_REASON_TAG, IDLE_TRANSACTION_FINISH_REASONS[0]); this.finish(); } else { this._pingHeartbeat(); @@ -278,14 +295,12 @@ export class IdleTransaction extends Transaction { /** * Reset transaction on scope to `undefined` */ -function clearActiveTransaction(hub?: Hub): void { - if (hub) { - const scope = hub.getScope(); - if (scope) { - const transaction = scope.getTransaction(); - if (transaction) { - scope.setSpan(undefined); - } +function clearActiveTransaction(hub: Hub): void { + const scope = hub.getScope(); + if (scope) { + const transaction = scope.getTransaction(); + if (transaction) { + scope.setSpan(undefined); } } } diff --git a/packages/tracing/test/browser/browsertracing.test.ts b/packages/tracing/test/browser/browsertracing.test.ts index 5cb144cfd207..4bfc093fbe2b 100644 --- a/packages/tracing/test/browser/browsertracing.test.ts +++ b/packages/tracing/test/browser/browsertracing.test.ts @@ -6,7 +6,6 @@ import { JSDOM } from 'jsdom'; import { BrowserTracing, BrowserTracingOptions, - DEFAULT_MAX_TRANSACTION_DURATION_SECONDS, getHeaderContext, getMetaContent, } from '../../src/browser/browsertracing'; @@ -14,8 +13,8 @@ import { MetricsInstrumentation } from '../../src/browser/metrics'; import { defaultRequestInstrumentationOptions } from '../../src/browser/request'; import { instrumentRoutingWithDefaults } from '../../src/browser/router'; import * as hubExtensions from '../../src/hubextensions'; -import { DEFAULT_IDLE_TIMEOUT, IdleTransaction } from '../../src/idletransaction'; -import { getActiveTransaction, secToMs } from '../../src/utils'; +import { DEFAULT_FINAL_TIMEOUT, DEFAULT_IDLE_TIMEOUT, IdleTransaction } from '../../src/idletransaction'; +import { getActiveTransaction } from '../../src/utils'; import { getDefaultBrowserClientOptions } from '../testutils'; let mockChangeHistory: ({ to, from }: { to: string; from?: string }) => void = () => undefined; @@ -85,8 +84,8 @@ describe('BrowserTracing', () => { expect(browserTracing.options).toEqual({ idleTimeout: DEFAULT_IDLE_TIMEOUT, + finalTimeout: DEFAULT_FINAL_TIMEOUT, markBackgroundTransactions: true, - maxTransactionDuration: DEFAULT_MAX_TRANSACTION_DURATION_SECONDS, routingInstrumentation: instrumentRoutingWithDefaults, startTransactionOnLocationChange: true, startTransactionOnPageLoad: true, @@ -229,6 +228,7 @@ describe('BrowserTracing', () => { parentSampled: true, }), expect.any(Number), + expect.any(Number), expect.any(Boolean), expect.any(Object), ); @@ -247,8 +247,6 @@ describe('BrowserTracing', () => { expect(mockFinish).toHaveBeenCalledTimes(0); jest.advanceTimersByTime(DEFAULT_IDLE_TIMEOUT); expect(mockFinish).toHaveBeenCalledTimes(1); - - expect(transaction.tags).toEqual({ finishReason: 'idleTimeout', idleTimeout: undefined }); }); it('can be a custom value', () => { @@ -263,44 +261,6 @@ describe('BrowserTracing', () => { expect(mockFinish).toHaveBeenCalledTimes(0); jest.advanceTimersByTime(2000); expect(mockFinish).toHaveBeenCalledTimes(1); - - expect(transaction.tags).toEqual({ finishReason: 'idleTimeout', idleTimeout: 2000 }); - }); - }); - - describe('maxTransactionDuration', () => { - it('cancels a transaction if exceeded', () => { - createBrowserTracing(true, { routingInstrumentation: customInstrumentRouting }); - const transaction = getActiveTransaction(hub) as IdleTransaction; - transaction.finish(transaction.startTimestamp + secToMs(DEFAULT_MAX_TRANSACTION_DURATION_SECONDS) + 1); - - expect(transaction.status).toBe('deadline_exceeded'); - expect(transaction.tags.maxTransactionDurationExceeded).toBeDefined(); - }); - - it('does not cancel a transaction if not exceeded', () => { - createBrowserTracing(true, { routingInstrumentation: customInstrumentRouting }); - const transaction = getActiveTransaction(hub) as IdleTransaction; - transaction.finish(transaction.startTimestamp + secToMs(DEFAULT_MAX_TRANSACTION_DURATION_SECONDS)); - - expect(transaction.status).toBe(undefined); - expect(transaction.tags.maxTransactionDurationExceeded).not.toBeDefined(); - }); - - it('can have a custom value', () => { - const customMaxTransactionDuration = 700; - // Test to make sure default duration is less than tested custom value. - expect(DEFAULT_MAX_TRANSACTION_DURATION_SECONDS < customMaxTransactionDuration).toBe(true); - createBrowserTracing(true, { - maxTransactionDuration: customMaxTransactionDuration, - routingInstrumentation: customInstrumentRouting, - }); - const transaction = getActiveTransaction(hub) as IdleTransaction; - - transaction.finish(transaction.startTimestamp + secToMs(customMaxTransactionDuration)); - - expect(transaction.status).toBe(undefined); - expect(transaction.tags.maxTransactionDurationExceeded).not.toBeDefined(); }); }); }); diff --git a/packages/tracing/test/idletransaction.test.ts b/packages/tracing/test/idletransaction.test.ts index bf4d5d51af41..956de96b5ea5 100644 --- a/packages/tracing/test/idletransaction.test.ts +++ b/packages/tracing/test/idletransaction.test.ts @@ -2,6 +2,7 @@ import { BrowserClient } from '@sentry/browser'; import { Hub } from '@sentry/hub'; import { + DEFAULT_FINAL_TIMEOUT, DEFAULT_IDLE_TIMEOUT, HEARTBEAT_INTERVAL, IdleTransaction, @@ -20,7 +21,7 @@ beforeEach(() => { describe('IdleTransaction', () => { describe('onScope', () => { it('sets the transaction on the scope on creation if onScope is true', () => { - const transaction = new IdleTransaction({ name: 'foo' }, hub, DEFAULT_IDLE_TIMEOUT, true); + const transaction = new IdleTransaction({ name: 'foo' }, hub, DEFAULT_IDLE_TIMEOUT, DEFAULT_FINAL_TIMEOUT, true); transaction.initSpanRecorder(10); hub.configureScope(s => { @@ -29,7 +30,7 @@ describe('IdleTransaction', () => { }); it('does not set the transaction on the scope on creation if onScope is falsey', () => { - const transaction = new IdleTransaction({ name: 'foo' }, hub, DEFAULT_IDLE_TIMEOUT); + const transaction = new IdleTransaction({ name: 'foo' }, hub); transaction.initSpanRecorder(10); hub.configureScope(s => { @@ -38,7 +39,7 @@ describe('IdleTransaction', () => { }); it('removes sampled transaction from scope on finish if onScope is true', () => { - const transaction = new IdleTransaction({ name: 'foo' }, hub, DEFAULT_IDLE_TIMEOUT, true); + const transaction = new IdleTransaction({ name: 'foo' }, hub, DEFAULT_IDLE_TIMEOUT, DEFAULT_FINAL_TIMEOUT, true); transaction.initSpanRecorder(10); transaction.finish(); @@ -50,7 +51,13 @@ describe('IdleTransaction', () => { }); it('removes unsampled transaction from scope on finish if onScope is true', () => { - const transaction = new IdleTransaction({ name: 'foo', sampled: false }, hub, DEFAULT_IDLE_TIMEOUT, true); + const transaction = new IdleTransaction( + { name: 'foo', sampled: false }, + hub, + DEFAULT_IDLE_TIMEOUT, + DEFAULT_FINAL_TIMEOUT, + true, + ); transaction.finish(); jest.runAllTimers(); @@ -66,7 +73,7 @@ describe('IdleTransaction', () => { }); it('push and pops activities', () => { - const transaction = new IdleTransaction({ name: 'foo' }, hub, DEFAULT_IDLE_TIMEOUT); + const transaction = new IdleTransaction({ name: 'foo' }, hub); const mockFinish = jest.spyOn(transaction, 'finish'); transaction.initSpanRecorder(10); expect(transaction.activities).toMatchObject({}); @@ -84,7 +91,7 @@ describe('IdleTransaction', () => { }); it('does not push activities if a span already has an end timestamp', () => { - const transaction = new IdleTransaction({ name: 'foo' }, hub, DEFAULT_IDLE_TIMEOUT); + const transaction = new IdleTransaction({ name: 'foo' }, hub); transaction.initSpanRecorder(10); expect(transaction.activities).toMatchObject({}); @@ -93,7 +100,7 @@ describe('IdleTransaction', () => { }); it('does not finish if there are still active activities', () => { - const transaction = new IdleTransaction({ name: 'foo' }, hub, DEFAULT_IDLE_TIMEOUT); + const transaction = new IdleTransaction({ name: 'foo' }, hub); const mockFinish = jest.spyOn(transaction, 'finish'); transaction.initSpanRecorder(10); expect(transaction.activities).toMatchObject({}); @@ -103,7 +110,7 @@ describe('IdleTransaction', () => { expect(transaction.activities).toMatchObject({ [span.spanId]: true, [childSpan.spanId]: true }); span.finish(); - jest.runOnlyPendingTimers(); + jest.advanceTimersByTime(DEFAULT_IDLE_TIMEOUT + 1); expect(mockFinish).toHaveBeenCalledTimes(0); expect(transaction.activities).toMatchObject({ [childSpan.spanId]: true }); @@ -112,7 +119,7 @@ describe('IdleTransaction', () => { it('calls beforeFinish callback before finishing', () => { const mockCallback1 = jest.fn(); const mockCallback2 = jest.fn(); - const transaction = new IdleTransaction({ name: 'foo' }, hub, DEFAULT_IDLE_TIMEOUT); + const transaction = new IdleTransaction({ name: 'foo' }, hub); transaction.initSpanRecorder(10); transaction.registerBeforeFinishCallback(mockCallback1); transaction.registerBeforeFinishCallback(mockCallback2); @@ -131,7 +138,7 @@ describe('IdleTransaction', () => { }); it('filters spans on finish', () => { - const transaction = new IdleTransaction({ name: 'foo', startTimestamp: 1234 }, hub, DEFAULT_IDLE_TIMEOUT); + const transaction = new IdleTransaction({ name: 'foo', startTimestamp: 1234 }, hub); transaction.initSpanRecorder(10); // regular child - should be kept @@ -176,9 +183,9 @@ describe('IdleTransaction', () => { expect(recordDroppedEventSpy).toHaveBeenCalledWith('sample_rate', 'transaction'); }); - describe('_initTimeout', () => { + describe('_idleTimeout', () => { it('finishes if no activities are added to the transaction', () => { - const transaction = new IdleTransaction({ name: 'foo', startTimestamp: 1234 }, hub, DEFAULT_IDLE_TIMEOUT); + const transaction = new IdleTransaction({ name: 'foo', startTimestamp: 1234 }, hub); transaction.initSpanRecorder(10); jest.advanceTimersByTime(DEFAULT_IDLE_TIMEOUT); @@ -186,13 +193,49 @@ describe('IdleTransaction', () => { }); it('does not finish if a activity is started', () => { - const transaction = new IdleTransaction({ name: 'foo', startTimestamp: 1234 }, hub, DEFAULT_IDLE_TIMEOUT); + const transaction = new IdleTransaction({ name: 'foo', startTimestamp: 1234 }, hub); transaction.initSpanRecorder(10); transaction.startChild({}); jest.advanceTimersByTime(DEFAULT_IDLE_TIMEOUT); expect(transaction.endTimestamp).toBeUndefined(); }); + + it('does not finish when idleTimeout is not exceed after last activity finished', () => { + const idleTimeout = 10; + const transaction = new IdleTransaction({ name: 'foo', startTimestamp: 1234 }, hub, idleTimeout); + transaction.initSpanRecorder(10); + + const span = transaction.startChild({}); + span.finish(); + + jest.advanceTimersByTime(2); + + const span2 = transaction.startChild({}); + span2.finish(); + + jest.advanceTimersByTime(8); + + expect(transaction.endTimestamp).toBeUndefined(); + }); + + it('finish when idleTimeout is exceeded after last activity finished', () => { + const idleTimeout = 10; + const transaction = new IdleTransaction({ name: 'foo', startTimestamp: 1234 }, hub, idleTimeout); + transaction.initSpanRecorder(10); + + const span = transaction.startChild({}); + span.finish(); + + jest.advanceTimersByTime(2); + + const span2 = transaction.startChild({}); + span2.finish(); + + jest.advanceTimersByTime(10); + + expect(transaction.endTimestamp).toBeDefined(); + }); }); describe('heartbeat', () => { @@ -229,20 +272,20 @@ describe('IdleTransaction', () => { transaction.startChild({}); // Beat 1 - jest.runOnlyPendingTimers(); + jest.advanceTimersByTime(HEARTBEAT_INTERVAL); expect(mockFinish).toHaveBeenCalledTimes(0); // Beat 2 - jest.runOnlyPendingTimers(); + jest.advanceTimersByTime(HEARTBEAT_INTERVAL); expect(mockFinish).toHaveBeenCalledTimes(0); // Beat 3 - jest.runOnlyPendingTimers(); + jest.advanceTimersByTime(HEARTBEAT_INTERVAL); expect(mockFinish).toHaveBeenCalledTimes(1); }); it('resets after new activities are added', () => { - const transaction = new IdleTransaction({ name: 'foo' }, hub, DEFAULT_IDLE_TIMEOUT); + const transaction = new IdleTransaction({ name: 'foo' }, hub, DEFAULT_IDLE_TIMEOUT, 50000); const mockFinish = jest.spyOn(transaction, 'finish'); transaction.initSpanRecorder(10); @@ -250,42 +293,42 @@ describe('IdleTransaction', () => { transaction.startChild({}); // Beat 1 - jest.runOnlyPendingTimers(); + jest.advanceTimersByTime(HEARTBEAT_INTERVAL); expect(mockFinish).toHaveBeenCalledTimes(0); const span = transaction.startChild(); // push activity // Beat 1 - jest.runOnlyPendingTimers(); + jest.advanceTimersByTime(HEARTBEAT_INTERVAL); expect(mockFinish).toHaveBeenCalledTimes(0); // Beat 2 - jest.runOnlyPendingTimers(); + jest.advanceTimersByTime(HEARTBEAT_INTERVAL); expect(mockFinish).toHaveBeenCalledTimes(0); transaction.startChild(); // push activity transaction.startChild(); // push activity // Beat 1 - jest.runOnlyPendingTimers(); + jest.advanceTimersByTime(HEARTBEAT_INTERVAL); expect(mockFinish).toHaveBeenCalledTimes(0); // Beat 2 - jest.runOnlyPendingTimers(); + jest.advanceTimersByTime(HEARTBEAT_INTERVAL); expect(mockFinish).toHaveBeenCalledTimes(0); span.finish(); // pop activity // Beat 1 - jest.runOnlyPendingTimers(); + jest.advanceTimersByTime(HEARTBEAT_INTERVAL); expect(mockFinish).toHaveBeenCalledTimes(0); // Beat 2 - jest.runOnlyPendingTimers(); + jest.advanceTimersByTime(HEARTBEAT_INTERVAL); expect(mockFinish).toHaveBeenCalledTimes(0); // Beat 3 - jest.runOnlyPendingTimers(); + jest.advanceTimersByTime(HEARTBEAT_INTERVAL); expect(mockFinish).toHaveBeenCalledTimes(1); // Heartbeat does not keep going after finish has been called @@ -299,7 +342,7 @@ describe('IdleTransactionSpanRecorder', () => { it('pushes and pops activities', () => { const mockPushActivity = jest.fn(); const mockPopActivity = jest.fn(); - const spanRecorder = new IdleTransactionSpanRecorder(mockPushActivity, mockPopActivity, undefined, 10); + const spanRecorder = new IdleTransactionSpanRecorder(mockPushActivity, mockPopActivity, '', 10); expect(mockPushActivity).toHaveBeenCalledTimes(0); expect(mockPopActivity).toHaveBeenCalledTimes(0); @@ -322,7 +365,7 @@ describe('IdleTransactionSpanRecorder', () => { it('does not push activities if a span has a timestamp', () => { const mockPushActivity = jest.fn(); const mockPopActivity = jest.fn(); - const spanRecorder = new IdleTransactionSpanRecorder(mockPushActivity, mockPopActivity, undefined, 10); + const spanRecorder = new IdleTransactionSpanRecorder(mockPushActivity, mockPopActivity, '', 10); const span = new Span({ sampled: true, startTimestamp: 765, endTimestamp: 345 }); spanRecorder.add(span); @@ -334,7 +377,7 @@ describe('IdleTransactionSpanRecorder', () => { const mockPushActivity = jest.fn(); const mockPopActivity = jest.fn(); - const transaction = new IdleTransaction({ name: 'foo' }, hub, DEFAULT_IDLE_TIMEOUT); + const transaction = new IdleTransaction({ name: 'foo' }, hub); const spanRecorder = new IdleTransactionSpanRecorder(mockPushActivity, mockPopActivity, transaction.spanId, 10); spanRecorder.add(transaction); From 8d475b191434f81c7a2abdbc302a4161034d57d8 Mon Sep 17 00:00:00 2001 From: Luca Forstner Date: Mon, 9 May 2022 10:26:45 +0200 Subject: [PATCH 124/204] ref(utils): Clean up dangerous type casts in object helper file (#5047) --- packages/types/src/index.ts | 1 + packages/types/src/polymorphics.ts | 11 ++++ packages/utils/src/is.ts | 4 +- packages/utils/src/normalize.ts | 4 +- packages/utils/src/object.ts | 88 +++++++++++++++++------------- 5 files changed, 66 insertions(+), 42 deletions(-) create mode 100644 packages/types/src/polymorphics.ts diff --git a/packages/types/src/index.ts b/packages/types/src/index.ts index 2fbcf4bbed6b..8fe14cd3ac16 100644 --- a/packages/types/src/index.ts +++ b/packages/types/src/index.ts @@ -31,6 +31,7 @@ export type { Mechanism } from './mechanism'; export type { ExtractedNodeRequestData, Primitive, WorkerLocation } from './misc'; export type { ClientOptions, Options } from './options'; export type { Package } from './package'; +export type { PolymorphicEvent } from './polymorphics'; export type { QueryParams, Request } from './request'; export type { Runtime } from './runtime'; export type { CaptureContext, Scope, ScopeContext } from './scope'; diff --git a/packages/types/src/polymorphics.ts b/packages/types/src/polymorphics.ts new file mode 100644 index 000000000000..0274aedfb2b4 --- /dev/null +++ b/packages/types/src/polymorphics.ts @@ -0,0 +1,11 @@ +/** + * Event-like interface that's usable in browser and node. + * + * Property availability taken from https://developer.mozilla.org/en-US/docs/Web/API/Event#browser_compatibility + */ +export interface PolymorphicEvent { + [key: string]: unknown; + readonly type: string; + readonly target?: unknown; + readonly currentTarget?: unknown; +} diff --git a/packages/utils/src/is.ts b/packages/utils/src/is.ts index 4981359968c9..81ae28349a0a 100644 --- a/packages/utils/src/is.ts +++ b/packages/utils/src/is.ts @@ -1,7 +1,7 @@ /* eslint-disable @typescript-eslint/no-explicit-any */ /* eslint-disable @typescript-eslint/explicit-module-boundary-types */ -import { Primitive } from '@sentry/types'; +import { PolymorphicEvent, Primitive } from '@sentry/types'; // eslint-disable-next-line @typescript-eslint/unbound-method const objectToString = Object.prototype.toString; @@ -101,7 +101,7 @@ export function isPlainObject(wat: unknown): wat is Record { * @param wat A value to be checked. * @returns A boolean representing the result. */ -export function isEvent(wat: unknown): boolean { +export function isEvent(wat: unknown): wat is PolymorphicEvent { return typeof Event !== 'undefined' && isInstanceOf(wat, Event); } diff --git a/packages/utils/src/normalize.ts b/packages/utils/src/normalize.ts index 51566a2125a7..f0899f47b4c0 100644 --- a/packages/utils/src/normalize.ts +++ b/packages/utils/src/normalize.ts @@ -1,6 +1,6 @@ import { Primitive } from '@sentry/types'; -import { isError, isEvent, isNaN, isSyntheticEvent } from './is'; +import { isNaN, isSyntheticEvent } from './is'; import { memoBuilder, MemoFunc } from './memo'; import { convertToPlainObject } from './object'; import { getFunctionName } from './stacktrace'; @@ -117,7 +117,7 @@ function visit( // Before we begin, convert`Error` and`Event` instances into plain objects, since some of each of their relevant // properties are non-enumerable and otherwise would get missed. - const visitable = (isError(value) || isEvent(value) ? convertToPlainObject(value) : value) as ObjOrArray; + const visitable = convertToPlainObject(value as ObjOrArray); for (const visitKey in visitable) { // Avoid iterating over fields in the prototype if they've somehow been exposed to enumeration. diff --git a/packages/utils/src/object.ts b/packages/utils/src/object.ts index cc401d2fbdfe..d4974cecbf44 100644 --- a/packages/utils/src/object.ts +++ b/packages/utils/src/object.ts @@ -1,6 +1,6 @@ /* eslint-disable max-lines */ /* eslint-disable @typescript-eslint/no-explicit-any */ -import { ExtendedError, WrappedFunction } from '@sentry/types'; +import { WrappedFunction } from '@sentry/types'; import { htmlTreeAsString } from './browser'; import { isElement, isError, isEvent, isInstanceOf, isPlainObject, isPrimitive } from './is'; @@ -92,50 +92,59 @@ export function urlEncode(object: { [key: string]: any }): string { } /** - * Transforms any object into an object literal with all its attributes - * attached to it. + * Transforms any `Error` or `Event` into a plain object with all of their enumerable properties, and some of their + * non-enumerable properties attached. * * @param value Initial source that we have to transform in order for it to be usable by the serializer + * @returns An Event or Error turned into an object - or the value argurment itself, when value is neither an Event nor + * an Error. */ -export function convertToPlainObject(value: unknown): { - [key: string]: unknown; -} { - let newObj = value as { - [key: string]: unknown; - }; - +export function convertToPlainObject( + value: V, +): + | { + [ownProps: string]: unknown; + type: string; + target: string; + currentTarget: string; + detail?: unknown; + } + | { + [ownProps: string]: unknown; + message: string; + name: string; + stack?: string; + } + | V { if (isError(value)) { - newObj = { + return { message: value.message, name: value.name, stack: value.stack, - ...getOwnProperties(value as ExtendedError), + ...getOwnProperties(value), }; } else if (isEvent(value)) { - /** - * Event-like interface that's usable in browser and node - */ - interface SimpleEvent { - [key: string]: unknown; + const newObj: { + [ownProps: string]: unknown; type: string; - target?: unknown; - currentTarget?: unknown; - } - - const event = value as SimpleEvent; - - newObj = { - type: event.type, - target: serializeEventTarget(event.target), - currentTarget: serializeEventTarget(event.currentTarget), - ...getOwnProperties(event), + target: string; + currentTarget: string; + detail?: unknown; + } = { + type: value.type, + target: serializeEventTarget(value.target), + currentTarget: serializeEventTarget(value.currentTarget), + ...getOwnProperties(value), }; if (typeof CustomEvent !== 'undefined' && isInstanceOf(value, CustomEvent)) { - newObj.detail = event.detail; + newObj.detail = value.detail; } + + return newObj; + } else { + return value; } - return newObj; } /** Creates a string representation of the target of an `Event` object */ @@ -148,14 +157,18 @@ function serializeEventTarget(target: unknown): string { } /** Filters out all but an object's own properties */ -function getOwnProperties(obj: { [key: string]: unknown }): { [key: string]: unknown } { - const extractedProps: { [key: string]: unknown } = {}; - for (const property in obj) { - if (Object.prototype.hasOwnProperty.call(obj, property)) { - extractedProps[property] = obj[property]; +function getOwnProperties(obj: unknown): { [key: string]: unknown } { + if (typeof obj === 'object' && obj !== null) { + const extractedProps: { [key: string]: unknown } = {}; + for (const property in obj) { + if (Object.prototype.hasOwnProperty.call(obj, property)) { + extractedProps[property] = (obj as Record)[property]; + } } + return extractedProps; + } else { + return {}; } - return extractedProps; } /** @@ -163,8 +176,7 @@ function getOwnProperties(obj: { [key: string]: unknown }): { [key: string]: unk * and truncated list that will be used inside the event message. * eg. `Non-error exception captured with keys: foo, bar, baz` */ -// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types -export function extractExceptionKeysForMessage(exception: any, maxLength: number = 40): string { +export function extractExceptionKeysForMessage(exception: Record, maxLength: number = 40): string { const keys = Object.keys(convertToPlainObject(exception)); keys.sort(); From 5bd31acf5cdb6b1a3865100aaa1cbdd345e1605a Mon Sep 17 00:00:00 2001 From: Abhijeet Prasad Date: Mon, 9 May 2022 11:29:09 -0400 Subject: [PATCH 125/204] chore: Add NextJS cli fix to changelog (#5055) --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 18b4db957232..15c23e44e2e9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -24,6 +24,7 @@ - ref(build): Switch tsconfig target to es6 (#5005) - ref(core): Make event processing log warnings instead of errors (#5010) - fix(hub): Add missing parameter to captureException docstring (#5001) +- fix(nextjs): Update webpack-plugin and change how cli binary is detected (#4988) - fix(serverless): Adjust v6 Lambda layer hotfix for v7 (#5006) - fix(tracing): Adjust sideEffects package.json entry for v7 (#4987) - feat(tracing): Add GB unit to device memory tag value (#4935) From 8d36f0b334770c9a0334dc06bfb125b27173fdd7 Mon Sep 17 00:00:00 2001 From: Luca Forstner Date: Mon, 9 May 2022 17:45:26 +0200 Subject: [PATCH 126/204] ref(utils): Add logic to enable skipping of normalization (#5052) --- packages/utils/src/normalize.ts | 11 +++++- packages/utils/test/normalize.test.ts | 49 +++++++++++++++++++++++++++ 2 files changed, 59 insertions(+), 1 deletion(-) diff --git a/packages/utils/src/normalize.ts b/packages/utils/src/normalize.ts index f0899f47b4c0..4ef7b0b121cb 100644 --- a/packages/utils/src/normalize.ts +++ b/packages/utils/src/normalize.ts @@ -33,7 +33,7 @@ type ObjOrArray = { [key: string]: T }; */ export function normalize(input: unknown, depth: number = +Infinity, maxProperties: number = +Infinity): any { try { - // since we're at the outermost level, there is no key + // since we're at the outermost level, we don't provide a key return visit('', input, depth, maxProperties); } catch (err) { return { ERROR: `**non-serializable** (${err})` }; @@ -98,6 +98,15 @@ function visit( return stringified; } + // From here on, we can assert that `value` is either an object or an array. + + // Do not normalize objects that we know have already been normalized. As a general rule, the + // "__sentry_skip_normalization__" property should only be used sparingly and only should only be set on objects that + // have already been normalized. + if ((value as ObjOrArray)['__sentry_skip_normalization__']) { + return value as ObjOrArray; + } + // We're also done if we've reached the max depth if (depth === 0) { // At this point we know `serialized` is a string of the form `"[object XXXX]"`. Clean it up so it's just `"[XXXX]"`. diff --git a/packages/utils/test/normalize.test.ts b/packages/utils/test/normalize.test.ts index 756020b403de..e5d01de4e962 100644 --- a/packages/utils/test/normalize.test.ts +++ b/packages/utils/test/normalize.test.ts @@ -4,6 +4,7 @@ import * as isModule from '../src/is'; import { normalize } from '../src/normalize'; +import { addNonEnumerableProperty } from '../src/object'; import * as stacktraceModule from '../src/stacktrace'; describe('normalize()', () => { @@ -504,4 +505,52 @@ describe('normalize()', () => { qux: '[Function: qux]', }); }); + + describe('skips normalizing objects marked with a non-enumerable property __sentry_skip_normalization__', () => { + test('by leaving non-serializable values intact', () => { + const someFun = () => undefined; + const alreadyNormalizedObj = { + nan: NaN, + fun: someFun, + }; + + addNonEnumerableProperty(alreadyNormalizedObj, '__sentry_skip_normalization__', true); + + const result = normalize(alreadyNormalizedObj); + expect(result).toEqual({ + nan: NaN, + fun: someFun, + }); + }); + + test('by ignoring normalization depth', () => { + const alreadyNormalizedObj = { + three: { + more: { + layers: '!', + }, + }, + }; + + addNonEnumerableProperty(alreadyNormalizedObj, '__sentry_skip_normalization__', true); + + const obj = { + foo: { + bar: { + baz: alreadyNormalizedObj, + boo: { + bam: { + pow: 'poof', + }, + }, + }, + }, + }; + + const result = normalize(obj, 4); + + expect(result?.foo?.bar?.baz?.three?.more?.layers).toBe('!'); + expect(result?.foo?.bar?.boo?.bam?.pow).not.toBe('poof'); + }); + }); }); From 19a9d0049114c9c62fa4111200a7fd86357c66ca Mon Sep 17 00:00:00 2001 From: Katie Byers Date: Mon, 9 May 2022 10:00:08 -0700 Subject: [PATCH 127/204] feat(build): Vendor polyfills injected during build (#5051) Both Rollup and Sucrase add polyfills (similar to those in `tslib`) during the build process, to down-compile newer language features and help ESM and CJS modules play nicely together. Unlike with `tslib`, however, there's no option equivalent to tsc's `importHelpers` option[1], which allows those polyfills to be imports from one central place rather than copies of the full code for each necessary polyfill in every file that uses them. Having all that repeated code is a surefire way to wreck our bundle-size progress, so as part of the switch to using those tools, we will replicate the behavior of `importHelpers` in the context of our new sucrase/rollup builds. This is the first of two PRs accomplishing that change. In this one, the polyfills we'll use have been added to the repo. In the next one, a rollup plugin will be added to replace the duplicated injected code with import statements, which will then import the polyfills this PR adds. Originally this was one PR, but it has been split into two for easier reviewing. Notes: - The polyfills themselves have been added to `@sentry/utils`, in a separate folder under `src/`. Various discussions have been had about where they should live[2], and they have moved around as this PR evolved. Without going into too much boring detail about about the exact pros and cons of each location, suffice it to say that my first instinct was to put them in `utils`, and as I worked on other options, each one of the reasons why I didn't actually do that to begin with (they weren't in TS, they weren't originally ours, I didn't want to pollute the `@sentry/utils` namespace) got solved, and in the end, putting them in `utils` was the simplest and most logical option. - The added polyfills are copies of the code injected by Sucrase or Rollup, translated into TS and in some cases modified in order to make them less verbose. Since some treeshaking algorithms can only work file by file, each one is in its own file. Polyfills which were modified have had tests added, both to ensure that they do the same thing as the originals, and to ensure that what they do is actually correct. - As we're not the original authors, attribution has been given within each file to the original source. Further, since the MIT license under which both Rollup and Sucrase are distributed requires duplication of the license blurb, it has been added to the README in the polyfills' folder. - Because we don't want these to become part of our public API, their code is not exported by `index.ts`, meaning they are not importable directly from `@sentry/utils` and won't come up under intellisense code completions and the like. When our code imports them, it will import from `@sentry/utils/cjs/buildPolyfills` or `@sentry/utils/esm/buildPolyfills` as appropriate. - Though not every polyfill Rollup and Sucrase can inject ends up being used in our built code, they are all included just to cover our bases. That said, in the interest of time, only the ones we use or are likely to use have had tests written for them. - One of the polyfills we do use is the optional chain polyfill. Typing it is tricky, because it takes a variable number of arguments, whose types are effectively `[ A, B, C, B, C, B, C,... ]` (one type for the first argument, and then alternating types for the rest). Because it's not going to be used by the public, the typing in the polyfill itself uses the generic `unknown`. For tests it was easier, because only the test cases needed to be typed, and could have a slightly modified structure: `[ A, [ B, C ][] ]`, which then gets flattened again before being passed to the optional chain polyfill. In order to be able to use `Array.prototype.flat` in tests run on older versions of Node, it itself has been polyfilled, and our testing `tsconfig` target ramped back down when running tests under those old versions. [1] https://www.typescriptlang.org/tsconfig#importHelpers [2] https://github.com/getsentry/sentry-javascript/pull/5023#discussion_r867655327 --- packages/utils/.eslintrc.js | 16 ++ packages/utils/.gitignore | 6 + packages/utils/jest.config.js | 10 +- packages/utils/package.json | 6 +- packages/utils/rollup.npm.config.js | 8 +- packages/utils/scripts/buildRollup.ts | 30 +++ packages/utils/src/buildPolyfills/README.md | 15 ++ .../buildPolyfills/_asyncNullishCoalesce.ts | 30 +++ .../src/buildPolyfills/_asyncOptionalChain.ts | 59 +++++ .../_asyncOptionalChainDelete.ts | 28 +++ .../buildPolyfills/_createNamedExportFrom.ts | 21 ++ .../src/buildPolyfills/_createStarExport.ts | 28 +++ .../src/buildPolyfills/_interopDefault.ts | 18 ++ .../src/buildPolyfills/_interopNamespace.ts | 26 +++ .../_interopNamespaceDefaultOnly.ts | 24 +++ .../buildPolyfills/_interopRequireDefault.ts | 18 ++ .../buildPolyfills/_interopRequireWildcard.ts | 31 +++ .../src/buildPolyfills/_nullishCoalesce.ts | 25 +++ .../src/buildPolyfills/_optionalChain.ts | 58 +++++ .../buildPolyfills/_optionalChainDelete.ts | 28 +++ packages/utils/src/buildPolyfills/index.ts | 13 ++ packages/utils/src/buildPolyfills/types.ts | 7 + .../utils/test/buildPolyfills/interop.test.ts | 180 ++++++++++++++++ .../buildPolyfills/nullishCoalesce.test.ts | 29 +++ .../test/buildPolyfills/optionalChain.test.ts | 87 ++++++++ .../utils/test/buildPolyfills/originals.d.ts | 23 ++ .../utils/test/buildPolyfills/originals.js | 147 +++++++++++++ packages/utils/tsconfig.test.json | 13 +- rollup/npmHelpers.js | 4 + scripts/test.ts | 50 ++++- yarn.lock | 202 +++++++++++++++++- 31 files changed, 1231 insertions(+), 9 deletions(-) create mode 100644 packages/utils/.gitignore create mode 100644 packages/utils/scripts/buildRollup.ts create mode 100644 packages/utils/src/buildPolyfills/README.md create mode 100644 packages/utils/src/buildPolyfills/_asyncNullishCoalesce.ts create mode 100644 packages/utils/src/buildPolyfills/_asyncOptionalChain.ts create mode 100644 packages/utils/src/buildPolyfills/_asyncOptionalChainDelete.ts create mode 100644 packages/utils/src/buildPolyfills/_createNamedExportFrom.ts create mode 100644 packages/utils/src/buildPolyfills/_createStarExport.ts create mode 100644 packages/utils/src/buildPolyfills/_interopDefault.ts create mode 100644 packages/utils/src/buildPolyfills/_interopNamespace.ts create mode 100644 packages/utils/src/buildPolyfills/_interopNamespaceDefaultOnly.ts create mode 100644 packages/utils/src/buildPolyfills/_interopRequireDefault.ts create mode 100644 packages/utils/src/buildPolyfills/_interopRequireWildcard.ts create mode 100644 packages/utils/src/buildPolyfills/_nullishCoalesce.ts create mode 100644 packages/utils/src/buildPolyfills/_optionalChain.ts create mode 100644 packages/utils/src/buildPolyfills/_optionalChainDelete.ts create mode 100644 packages/utils/src/buildPolyfills/index.ts create mode 100644 packages/utils/src/buildPolyfills/types.ts create mode 100644 packages/utils/test/buildPolyfills/interop.test.ts create mode 100644 packages/utils/test/buildPolyfills/nullishCoalesce.test.ts create mode 100644 packages/utils/test/buildPolyfills/optionalChain.test.ts create mode 100644 packages/utils/test/buildPolyfills/originals.d.ts create mode 100644 packages/utils/test/buildPolyfills/originals.js diff --git a/packages/utils/.eslintrc.js b/packages/utils/.eslintrc.js index 5a2cc7f1ec08..35c6aab563f5 100644 --- a/packages/utils/.eslintrc.js +++ b/packages/utils/.eslintrc.js @@ -1,3 +1,19 @@ module.exports = { extends: ['../../.eslintrc.js'], + overrides: [ + { + files: ['scripts/**/*.ts'], + parserOptions: { + project: ['../../tsconfig.dev.json'], + }, + }, + { + files: ['test/**'], + parserOptions: { + sourceType: 'module', + }, + }, + ], + // symlinks to the folders inside of `build`, created to simulate what's in the npm package + ignorePatterns: ['cjs/**', 'esm/**'], }; diff --git a/packages/utils/.gitignore b/packages/utils/.gitignore new file mode 100644 index 000000000000..4b89517c06c8 --- /dev/null +++ b/packages/utils/.gitignore @@ -0,0 +1,6 @@ +# symlinks to the folders in `build`, needed for tests +cjs +esm + +# needed so we can test our versions of polyfills against Sucrase and Rollup's originals +!test/buildPolyfills/originals.d.ts diff --git a/packages/utils/jest.config.js b/packages/utils/jest.config.js index 24f49ab59a4c..f9b7ccfa4502 100644 --- a/packages/utils/jest.config.js +++ b/packages/utils/jest.config.js @@ -1 +1,9 @@ -module.exports = require('../../jest/jest.config.js'); +const baseConfig = require('../../jest/jest.config.js'); + +module.exports = { + ...baseConfig, + transform: { + '^.+\\.ts$': 'ts-jest', + '^.+\\.js$': 'ts-jest', + }, +}; diff --git a/packages/utils/package.json b/packages/utils/package.json index d67412868965..94e477d16fbd 100644 --- a/packages/utils/package.json +++ b/packages/utils/package.json @@ -20,6 +20,8 @@ "tslib": "^1.9.3" }, "devDependencies": { + "@types/array.prototype.flat": "^1.2.1", + "array.prototype.flat": "^1.3.0", "chai": "^4.1.2" }, "scripts": { @@ -28,7 +30,7 @@ "build:dev": "run-s build", "build:es5": "yarn build:cjs # *** backwards compatibility - remove in v7 ***", "build:esm": "tsc -p tsconfig.esm.json", - "build:rollup": "rollup -c rollup.npm.config.js", + "build:rollup": "yarn ts-node scripts/buildRollup.ts", "build:types": "tsc -p tsconfig.types.json", "build:watch": "run-p build:cjs:watch build:esm:watch build:types:watch", "build:cjs:watch": "tsc -p tsconfig.cjs.json --watch", @@ -39,7 +41,7 @@ "build:types:watch": "tsc -p tsconfig.types.json --watch", "build:npm": "ts-node ../../scripts/prepack.ts && npm pack ./build", "circularDepCheck": "madge --circular src/index.ts", - "clean": "rimraf build coverage", + "clean": "rimraf build coverage cjs esm", "fix": "run-s fix:eslint fix:prettier", "fix:eslint": "eslint . --format stylish --fix", "fix:prettier": "prettier --write \"{src,test,scripts}/**/*.ts\"", diff --git a/packages/utils/rollup.npm.config.js b/packages/utils/rollup.npm.config.js index 5a62b528ef44..dcf32469a4e4 100644 --- a/packages/utils/rollup.npm.config.js +++ b/packages/utils/rollup.npm.config.js @@ -1,3 +1,9 @@ import { makeBaseNPMConfig, makeNPMConfigVariants } from '../../rollup/index.js'; -export default makeNPMConfigVariants(makeBaseNPMConfig()); +export default makeNPMConfigVariants( + makeBaseNPMConfig({ + // We build the polyfills separately because they're not included in the top-level exports of the package, in order + // to keep them out of the public API. + entrypoints: ['src/index.ts', 'src/buildPolyfills/index.ts'], + }), +); diff --git a/packages/utils/scripts/buildRollup.ts b/packages/utils/scripts/buildRollup.ts new file mode 100644 index 000000000000..f48b0b23d6d9 --- /dev/null +++ b/packages/utils/scripts/buildRollup.ts @@ -0,0 +1,30 @@ +import * as childProcess from 'child_process'; +import * as fs from 'fs'; + +/** + * Run the given shell command, piping the shell process's `stdin`, `stdout`, and `stderr` to that of the current + * process. Returns contents of `stdout`. + */ +function run(cmd: string, options?: childProcess.ExecSyncOptions): string | Buffer { + return childProcess.execSync(cmd, { stdio: 'inherit', ...options }); +} + +run('yarn rollup -c rollup.npm.config.js'); + +// We want to distribute the README because it contains the MIT license blurb from Sucrase and Rollup +fs.copyFileSync('src/buildPolyfills/README.md', 'build/cjs/buildPolyfills/README.md'); +fs.copyFileSync('src/buildPolyfills/README.md', 'build/esm/buildPolyfills/README.md'); + +// Because we import our polyfills from `@sentry/utils/cjs/buildPolyfills` and `@sentry/utils/esm/buildPolyfills` rather +// than straight from `@sentry/utils` (so as to avoid having them in the package's public API), when tests run, they'll +// expect to find `cjs` and `esm` at the root level of the repo. +try { + fs.symlinkSync('build/cjs', 'cjs'); +} catch (oO) { + // if we get here, it's because the symlink already exists, so we're good +} +try { + fs.symlinkSync('build/esm', 'esm'); +} catch (oO) { + // same as above +} diff --git a/packages/utils/src/buildPolyfills/README.md b/packages/utils/src/buildPolyfills/README.md new file mode 100644 index 000000000000..8171e8583a96 --- /dev/null +++ b/packages/utils/src/buildPolyfills/README.md @@ -0,0 +1,15 @@ +## Build Polyfills + +This is a collection of syntax and import/export polyfills either copied directly from or heavily inspired by those used by [Rollup](https://github.com/rollup/rollup) and [Sucrase](https://github.com/alangpierce/sucrase). When either tool uses one of these polyfills during a build, it injects the function source code into each file needing the function, which can lead to a great deal of duplication. For our builds, we have therefore implemented something similar to [`tsc`'s `importHelpers` behavior](https://www.typescriptlang.org/tsconfig#importHelpers): Instead of leaving the polyfills injected in multiple places, we instead replace each injected function with an `import` or `require` statement, pulling from the CJS or ESM builds as appropriate. (In other words, the injected `import` statements import from `@sentry/utils/esm/buildPolyfills` and the injected `require` statements pull from `@sentry/utils/cjs/buildPolyfills/`. Because these functions should never be part of the public API, they're not exported from the package directly.) + +Note that not all polyfills are currently used by the SDK, but all are included here for future compatitibility, should they ever be needed. Also, since we're never going to be calling these directly from within another TS file, their types are fairly generic. In some cases testing required more specific types, which can be found in the test files. + +-------- + +_Code from both Rollup and Sucrase is used under the MIT license, copyright 2017 and 2012-2018, respectively._ + +_Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:_ + +_The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software._ + +_THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE._ diff --git a/packages/utils/src/buildPolyfills/_asyncNullishCoalesce.ts b/packages/utils/src/buildPolyfills/_asyncNullishCoalesce.ts new file mode 100644 index 000000000000..c1b075ee5222 --- /dev/null +++ b/packages/utils/src/buildPolyfills/_asyncNullishCoalesce.ts @@ -0,0 +1,30 @@ +// adapted from Sucrase (https://github.com/alangpierce/sucrase) + +import { _nullishCoalesce } from './_nullishCoalesce'; + +/** + * Polyfill for the nullish coalescing operator (`??`), when used in situations where at least one of the values is the + * result of an async operation. + * + * Note that the RHS is wrapped in a function so that if it's a computed value, that evaluation won't happen unless the + * LHS evaluates to a nullish value, to mimic the operator's short-circuiting behavior. + * + * Adapted from Sucrase (https://github.com/alangpierce/sucrase) + * + * @param lhs The value of the expression to the left of the `??` + * @param rhsFn A function returning the value of the expression to the right of the `??` + * @returns The LHS value, unless it's `null` or `undefined`, in which case, the RHS value + */ +// eslint-disable-next-line @sentry-internal/sdk/no-async-await +export async function _asyncNullishCoalesce(lhs: unknown, rhsFn: () => unknown): Promise { + return _nullishCoalesce(lhs, rhsFn); +} + +// Sucrase version: +// async function _asyncNullishCoalesce(lhs, rhsFn) { +// if (lhs != null) { +// return lhs; +// } else { +// return await rhsFn(); +// } +// } diff --git a/packages/utils/src/buildPolyfills/_asyncOptionalChain.ts b/packages/utils/src/buildPolyfills/_asyncOptionalChain.ts new file mode 100644 index 000000000000..10a10aa03f3a --- /dev/null +++ b/packages/utils/src/buildPolyfills/_asyncOptionalChain.ts @@ -0,0 +1,59 @@ +import { GenericFunction } from './types'; + +/** + * Polyfill for the optional chain operator, `?.`, given previous conversion of the expression into an array of values, + * descriptors, and functions, for situations in which at least one part of the expression is async. + * + * Adapted from Sucrase (https://github.com/alangpierce/sucrase) See + * https://github.com/alangpierce/sucrase/blob/265887868966917f3b924ce38dfad01fbab1329f/src/transformers/OptionalChainingNullishTransformer.ts#L15 + * + * @param ops Array result of expression conversion + * @returns The value of the expression + */ +// eslint-disable-next-line @sentry-internal/sdk/no-async-await +export async function _asyncOptionalChain(ops: unknown[]): Promise { + let lastAccessLHS: unknown = undefined; + let value = ops[0]; + let i = 1; + while (i < ops.length) { + const op = ops[i] as string; + const fn = ops[i + 1] as (intermediateValue: unknown) => Promise; + i += 2; + // by checking for loose equality to `null`, we catch both `null` and `undefined` + if ((op === 'optionalAccess' || op === 'optionalCall') && value == null) { + // really we're meaning to return `undefined` as an actual value here, but it saves bytes not to write it + return; + } + if (op === 'access' || op === 'optionalAccess') { + lastAccessLHS = value; + value = await fn(value); + } else if (op === 'call' || op === 'optionalCall') { + value = await fn((...args: unknown[]) => (value as GenericFunction).call(lastAccessLHS, ...args)); + lastAccessLHS = undefined; + } + } + return value; +} + +// Sucrase version: +// async function _asyncOptionalChain(ops) { +// let lastAccessLHS = undefined; +// let value = ops[0]; +// let i = 1; +// while (i < ops.length) { +// const op = ops[i]; +// const fn = ops[i + 1]; +// i += 2; +// if ((op === 'optionalAccess' || op === 'optionalCall') && value == null) { +// return undefined; +// } +// if (op === 'access' || op === 'optionalAccess') { +// lastAccessLHS = value; +// value = await fn(value); +// } else if (op === 'call' || op === 'optionalCall') { +// value = await fn((...args) => value.call(lastAccessLHS, ...args)); +// lastAccessLHS = undefined; +// } +// } +// return value; +// } diff --git a/packages/utils/src/buildPolyfills/_asyncOptionalChainDelete.ts b/packages/utils/src/buildPolyfills/_asyncOptionalChainDelete.ts new file mode 100644 index 000000000000..bf0260337919 --- /dev/null +++ b/packages/utils/src/buildPolyfills/_asyncOptionalChainDelete.ts @@ -0,0 +1,28 @@ +import { _asyncOptionalChain } from './_asyncOptionalChain'; + +/** + * Polyfill for the optional chain operator, `?.`, given previous conversion of the expression into an array of values, + * descriptors, and functions, in cases where the value of the expression is to be deleted. + * + * Adapted from Sucrase (https://github.com/alangpierce/sucrase) See + * https://github.com/alangpierce/sucrase/blob/265887868966917f3b924ce38dfad01fbab1329f/src/transformers/OptionalChainingNullishTransformer.ts#L15 + * + * @param ops Array result of expression conversion + * @returns The return value of the `delete` operator: `true`, unless the deletion target is an own, non-configurable + * property (one which can't be deleted or turned into an accessor, and whose enumerability can't be changed), in which + * case `false`. + */ +// eslint-disable-next-line @sentry-internal/sdk/no-async-await +export async function _asyncOptionalChainDelete(ops: unknown[]): Promise { + const result = (await _asyncOptionalChain(ops)) as Promise; + // If `result` is `null`, it means we didn't get to the end of the chain and so nothing was deleted (in which case, + // return `true` since that's what `delete` does when it no-ops). If it's non-null, we know the delete happened, in + // which case we return whatever the `delete` returned, which will be a boolean. + return result == null ? true : (result as Promise); +} + +// Sucrase version: +// async function asyncOptionalChainDelete(ops) { +// const result = await ASYNC_OPTIONAL_CHAIN_NAME(ops); +// return result == null ? true : result; +// } diff --git a/packages/utils/src/buildPolyfills/_createNamedExportFrom.ts b/packages/utils/src/buildPolyfills/_createNamedExportFrom.ts new file mode 100644 index 000000000000..0c632e586aba --- /dev/null +++ b/packages/utils/src/buildPolyfills/_createNamedExportFrom.ts @@ -0,0 +1,21 @@ +import { GenericObject } from './types'; + +declare const exports: GenericObject; + +/** + * Copy a property from the given object into `exports`, under the given name. + * + * Adapted from Sucrase (https://github.com/alangpierce/sucrase) + * + * @param obj The object containing the property to copy. + * @param localName The name under which to export the property + * @param importedName The name under which the property lives in `obj` + */ +export function _createNamedExportFrom(obj: GenericObject, localName: string, importedName: string): void { + exports[localName] = obj[importedName]; +} + +// Sucrase version: +// function _createNamedExportFrom(obj, localName, importedName) { +// Object.defineProperty(exports, localName, {enumerable: true, get: () => obj[importedName]}); +// } diff --git a/packages/utils/src/buildPolyfills/_createStarExport.ts b/packages/utils/src/buildPolyfills/_createStarExport.ts new file mode 100644 index 000000000000..f4f36c8c041a --- /dev/null +++ b/packages/utils/src/buildPolyfills/_createStarExport.ts @@ -0,0 +1,28 @@ +import { GenericObject } from './types'; + +declare const exports: GenericObject; + +/** + * Copy properties from an object into `exports`. + * + * Adapted from Sucrase (https://github.com/alangpierce/sucrase) + * + * @param obj The object containing the properties to copy. + */ +export function _createStarExport(obj: GenericObject): void { + Object.keys(obj) + .filter(key => key !== 'default' && key !== '__esModule' && !(key in exports)) + .forEach(key => (exports[key] = obj[key])); +} + +// Sucrase version: +// function _createStarExport(obj) { +// Object.keys(obj) +// .filter(key => key !== 'default' && key !== '__esModule') +// .forEach(key => { +// if (exports.hasOwnProperty(key)) { +// return; +// } +// Object.defineProperty(exports, key, { enumerable: true, get: () => obj[key] }); +// }); +// } diff --git a/packages/utils/src/buildPolyfills/_interopDefault.ts b/packages/utils/src/buildPolyfills/_interopDefault.ts new file mode 100644 index 000000000000..5bed0ef4e3f1 --- /dev/null +++ b/packages/utils/src/buildPolyfills/_interopDefault.ts @@ -0,0 +1,18 @@ +import { RequireResult } from './types'; + +/** + * Unwraps a module if it has been wrapped in an object under the key `default`. + * + * Adapted from Rollup (https://github.com/rollup/rollup) + * + * @param requireResult The result of calling `require` on a module + * @returns The full module, unwrapped if necessary. + */ +export function _interopDefault(requireResult: RequireResult): RequireResult { + return requireResult.__esModule ? (requireResult.default as RequireResult) : requireResult; +} + +// Rollup version: +// function _interopDefault(e) { +// return e && e.__esModule ? e['default'] : e; +// } diff --git a/packages/utils/src/buildPolyfills/_interopNamespace.ts b/packages/utils/src/buildPolyfills/_interopNamespace.ts new file mode 100644 index 000000000000..2211e21accfa --- /dev/null +++ b/packages/utils/src/buildPolyfills/_interopNamespace.ts @@ -0,0 +1,26 @@ +import { RequireResult } from './types'; + +/** + * Adds a self-referential `default` property to CJS modules which aren't the result of transpilation from ESM modules. + * + * Adapted from Rollup (https://github.com/rollup/rollup) + * + * @param requireResult The result of calling `require` on a module + * @returns Either `requireResult` or a copy of `requireResult` with an added self-referential `default` property + */ +export function _interopNamespace(requireResult: RequireResult): RequireResult { + return requireResult.__esModule ? requireResult : { ...requireResult, default: requireResult }; +} + +// Rollup version (with `output.externalLiveBindings` and `output.freeze` both set to false) +// function _interopNamespace(e) { +// if (e && e.__esModule) return e; +// var n = Object.create(null); +// if (e) { +// for (var k in e) { +// n[k] = e[k]; +// } +// } +// n["default"] = e; +// return n; +// } diff --git a/packages/utils/src/buildPolyfills/_interopNamespaceDefaultOnly.ts b/packages/utils/src/buildPolyfills/_interopNamespaceDefaultOnly.ts new file mode 100644 index 000000000000..66785a79e92f --- /dev/null +++ b/packages/utils/src/buildPolyfills/_interopNamespaceDefaultOnly.ts @@ -0,0 +1,24 @@ +import { RequireResult } from './types'; + +/** + * Wrap a module in an object, as the value under the key `default`. + * + * Adapted from Rollup (https://github.com/rollup/rollup) + * + * @param requireResult The result of calling `require` on a module + * @returns An object containing the key-value pair (`default`, `requireResult`) + */ +export function _interopNamespaceDefaultOnly(requireResult: RequireResult): RequireResult { + return { + __proto__: null, + default: requireResult, + }; +} + +// Rollup version +// function _interopNamespaceDefaultOnly(e) { +// return { +// __proto__: null, +// 'default': e +// }; +// } diff --git a/packages/utils/src/buildPolyfills/_interopRequireDefault.ts b/packages/utils/src/buildPolyfills/_interopRequireDefault.ts new file mode 100644 index 000000000000..9d9a7767cb7c --- /dev/null +++ b/packages/utils/src/buildPolyfills/_interopRequireDefault.ts @@ -0,0 +1,18 @@ +import { RequireResult } from './types'; + +/** + * Wraps modules which aren't the result of transpiling an ESM module in an object under the key `default` + * + * Adapted from Sucrase (https://github.com/alangpierce/sucrase) + * + * @param requireResult The result of calling `require` on a module + * @returns `requireResult` or `requireResult` wrapped in an object, keyed as `default` + */ +export function _interopRequireDefault(requireResult: RequireResult): RequireResult { + return requireResult.__esModule ? requireResult : { default: requireResult }; +} + +// Sucrase version +// function _interopRequireDefault(obj) { +// return obj && obj.__esModule ? obj : { default: obj }; +// } diff --git a/packages/utils/src/buildPolyfills/_interopRequireWildcard.ts b/packages/utils/src/buildPolyfills/_interopRequireWildcard.ts new file mode 100644 index 000000000000..411939ab68d6 --- /dev/null +++ b/packages/utils/src/buildPolyfills/_interopRequireWildcard.ts @@ -0,0 +1,31 @@ +import { RequireResult } from './types'; + +/** + * Adds a `default` property to CJS modules which aren't the result of transpilation from ESM modules. + * + * Adapted from Sucrase (https://github.com/alangpierce/sucrase) + * + * @param requireResult The result of calling `require` on a module + * @returns Either `requireResult` or a copy of `requireResult` with an added self-referential `default` property + */ +export function _interopRequireWildcard(requireResult: RequireResult): RequireResult { + return requireResult.__esModule ? requireResult : { ...requireResult, default: requireResult }; +} + +// Sucrase version +// function _interopRequireWildcard(obj) { +// if (obj && obj.__esModule) { +// return obj; +// } else { +// var newObj = {}; +// if (obj != null) { +// for (var key in obj) { +// if (Object.prototype.hasOwnProperty.call(obj, key)) { +// newObj[key] = obj[key]; +// } +// } +// } +// newObj.default = obj; +// return newObj; +// } +// } diff --git a/packages/utils/src/buildPolyfills/_nullishCoalesce.ts b/packages/utils/src/buildPolyfills/_nullishCoalesce.ts new file mode 100644 index 000000000000..70ba98a2bd8f --- /dev/null +++ b/packages/utils/src/buildPolyfills/_nullishCoalesce.ts @@ -0,0 +1,25 @@ +/** + * Polyfill for the nullish coalescing operator (`??`). + * + * Note that the RHS is wrapped in a function so that if it's a computed value, that evaluation won't happen unless the + * LHS evaluates to a nullish value, to mimic the operator's short-circuiting behavior. + * + * Adapted from Sucrase (https://github.com/alangpierce/sucrase) + * + * @param lhs The value of the expression to the left of the `??` + * @param rhsFn A function returning the value of the expression to the right of the `??` + * @returns The LHS value, unless it's `null` or `undefined`, in which case, the RHS value + */ +export function _nullishCoalesce(lhs: unknown, rhsFn: () => unknown): unknown { + // by checking for loose equality to `null`, we catch both `null` and `undefined` + return lhs != null ? lhs : rhsFn(); +} + +// Sucrase version: +// function _nullishCoalesce(lhs, rhsFn) { +// if (lhs != null) { +// return lhs; +// } else { +// return rhsFn(); +// } +// } diff --git a/packages/utils/src/buildPolyfills/_optionalChain.ts b/packages/utils/src/buildPolyfills/_optionalChain.ts new file mode 100644 index 000000000000..452c6ac110b0 --- /dev/null +++ b/packages/utils/src/buildPolyfills/_optionalChain.ts @@ -0,0 +1,58 @@ +import { GenericFunction } from './types'; + +/** + * Polyfill for the optional chain operator, `?.`, given previous conversion of the expression into an array of values, + * descriptors, and functions. + * + * Adapted from Sucrase (https://github.com/alangpierce/sucrase) + * See https://github.com/alangpierce/sucrase/blob/265887868966917f3b924ce38dfad01fbab1329f/src/transformers/OptionalChainingNullishTransformer.ts#L15 + * + * @param ops Array result of expression conversion + * @returns The value of the expression + */ +export function _optionalChain(ops: unknown[]): unknown { + let lastAccessLHS: unknown = undefined; + let value = ops[0]; + let i = 1; + while (i < ops.length) { + const op = ops[i] as string; + const fn = ops[i + 1] as (intermediateValue: unknown) => unknown; + i += 2; + // by checking for loose equality to `null`, we catch both `null` and `undefined` + if ((op === 'optionalAccess' || op === 'optionalCall') && value == null) { + // really we're meaning to return `undefined` as an actual value here, but it saves bytes not to write it + return; + } + if (op === 'access' || op === 'optionalAccess') { + lastAccessLHS = value; + value = fn(value); + } else if (op === 'call' || op === 'optionalCall') { + value = fn((...args: unknown[]) => (value as GenericFunction).call(lastAccessLHS, ...args)); + lastAccessLHS = undefined; + } + } + return value; +} + +// Sucrase version +// function _optionalChain(ops) { +// let lastAccessLHS = undefined; +// let value = ops[0]; +// let i = 1; +// while (i < ops.length) { +// const op = ops[i]; +// const fn = ops[i + 1]; +// i += 2; +// if ((op === 'optionalAccess' || op === 'optionalCall') && value == null) { +// return undefined; +// } +// if (op === 'access' || op === 'optionalAccess') { +// lastAccessLHS = value; +// value = fn(value); +// } else if (op === 'call' || op === 'optionalCall') { +// value = fn((...args) => value.call(lastAccessLHS, ...args)); +// lastAccessLHS = undefined; +// } +// } +// return value; +// } diff --git a/packages/utils/src/buildPolyfills/_optionalChainDelete.ts b/packages/utils/src/buildPolyfills/_optionalChainDelete.ts new file mode 100644 index 000000000000..61147bedc5b5 --- /dev/null +++ b/packages/utils/src/buildPolyfills/_optionalChainDelete.ts @@ -0,0 +1,28 @@ +import { _optionalChain } from './_optionalChain'; + +/** + * Polyfill for the optional chain operator, `?.`, given previous conversion of the expression into an array of values, + * descriptors, and functions, in cases where the value of the expression is to be deleted. + * + * Adapted from Sucrase (https://github.com/alangpierce/sucrase) See + * https://github.com/alangpierce/sucrase/blob/265887868966917f3b924ce38dfad01fbab1329f/src/transformers/OptionalChainingNullishTransformer.ts#L15 + * + * @param ops Array result of expression conversion + * @returns The return value of the `delete` operator: `true`, unless the deletion target is an own, non-configurable + * property (one which can't be deleted or turned into an accessor, and whose enumerability can't be changed), in which + * case `false`. + */ +export function _optionalChainDelete(ops: unknown[]): boolean { + const result = _optionalChain(ops) as boolean | null; + // If `result` is `null`, it means we didn't get to the end of the chain and so nothing was deleted (in which case, + // return `true` since that's what `delete` does when it no-ops). If it's non-null, we know the delete happened, in + // which case we return whatever the `delete` returned, which will be a boolean. + return result == null ? true : result; +} + +// Sucrase version: +// function _optionalChainDelete(ops) { +// const result = _optionalChain(ops); +// // by checking for loose equality to `null`, we catch both `null` and `undefined` +// return result == null ? true : result; +// } diff --git a/packages/utils/src/buildPolyfills/index.ts b/packages/utils/src/buildPolyfills/index.ts new file mode 100644 index 000000000000..9717453e98fa --- /dev/null +++ b/packages/utils/src/buildPolyfills/index.ts @@ -0,0 +1,13 @@ +export { _asyncNullishCoalesce } from './_asyncNullishCoalesce'; +export { _asyncOptionalChain } from './_asyncOptionalChain'; +export { _asyncOptionalChainDelete } from './_asyncOptionalChainDelete'; +export { _createNamedExportFrom } from './_createNamedExportFrom'; +export { _createStarExport } from './_createStarExport'; +export { _interopDefault } from './_interopDefault'; +export { _interopNamespace } from './_interopNamespace'; +export { _interopNamespaceDefaultOnly } from './_interopNamespaceDefaultOnly'; +export { _interopRequireDefault } from './_interopRequireDefault'; +export { _interopRequireWildcard } from './_interopRequireWildcard'; +export { _nullishCoalesce } from './_nullishCoalesce'; +export { _optionalChain } from './_optionalChain'; +export { _optionalChainDelete } from './_optionalChainDelete'; diff --git a/packages/utils/src/buildPolyfills/types.ts b/packages/utils/src/buildPolyfills/types.ts new file mode 100644 index 000000000000..41f1a8a31ee5 --- /dev/null +++ b/packages/utils/src/buildPolyfills/types.ts @@ -0,0 +1,7 @@ +import { Primitive } from '@sentry/types'; + +export type GenericObject = { [key: string]: Value }; +export type GenericFunction = (...args: unknown[]) => Value; +export type Value = Primitive | GenericFunction | GenericObject; + +export type RequireResult = GenericObject | (GenericFunction & GenericObject); diff --git a/packages/utils/test/buildPolyfills/interop.test.ts b/packages/utils/test/buildPolyfills/interop.test.ts new file mode 100644 index 000000000000..917d62daee2f --- /dev/null +++ b/packages/utils/test/buildPolyfills/interop.test.ts @@ -0,0 +1,180 @@ +import { + _interopDefault, + _interopNamespace, + _interopNamespaceDefaultOnly, + _interopRequireDefault, + _interopRequireWildcard, +} from '../../src/buildPolyfills'; +import { RequireResult } from '../../src/buildPolyfills/types'; +import { + _interopDefault as _interopDefaultOrig, + _interopNamespace as _interopNamespaceOrig, + _interopNamespaceDefaultOnly as _interopNamespaceDefaultOnlyOrig, + _interopRequireDefault as _interopRequireDefaultOrig, + _interopRequireWildcard as _interopRequireWildcardOrig, +} from './originals'; + +// This file tests five different functions against a range of test cases. Though the inputs are the same for each +// function's test cases, the expected output differs. The testcases for each function are therefore built from separate +// collections of expected inputs and expected outputs. Further, for readability purposes, the tests labels have also +// been split into their own object. It's also worth noting that in real life, there are some test-case/function +// pairings which would never happen, but by testing all combinations, we're guaranteed to have tested the ones which +// show up in the wild. + +const dogStr = 'dogs are great!'; +const dogFunc = () => dogStr; +const dogAdjectives = { maisey: 'silly', charlie: 'goofy' }; + +const withESModuleFlag = { __esModule: true, ...dogAdjectives }; +const withESModuleFlagAndDefault = { __esModule: true, default: dogFunc, ...dogAdjectives }; +const namedExports = { ...dogAdjectives }; +const withNonEnumerableProp = { ...dogAdjectives }; +// Properties added using `Object.defineProperty` are non-enumerable by default +Object.defineProperty(withNonEnumerableProp, 'hiddenProp', { value: 'shhhhhhhh' }); +const withDefaultExport = { default: dogFunc, ...dogAdjectives }; +const withOnlyDefaultExport = { default: dogFunc }; +const exportsEquals = dogFunc as RequireResult; +const exportsEqualsWithDefault = dogFunc as RequireResult; +exportsEqualsWithDefault.default = exportsEqualsWithDefault; + +const mockRequireResults: Record = { + withESModuleFlag, + withESModuleFlagAndDefault, + namedExports, + withNonEnumerableProp, + withDefaultExport, + withOnlyDefaultExport, + exportsEquals: exportsEquals, + exportsEqualsWithDefault: exportsEqualsWithDefault as unknown as RequireResult, +}; + +const testLabels: Record = { + withESModuleFlag: 'module with `__esModule` flag', + withESModuleFlagAndDefault: 'module with `__esModule` flag and default export', + namedExports: 'module with named exports', + withNonEnumerableProp: 'module with named exports and non-enumerable prop', + withDefaultExport: 'module with default export', + withOnlyDefaultExport: 'module with only default export', + exportsEquals: 'module using `exports =`', + exportsEqualsWithDefault: 'module using `exports =` with default export', +}; + +function makeTestCases(expectedOutputs: Record): Array<[string, RequireResult, RequireResult]> { + return Object.keys(mockRequireResults).map(key => [testLabels[key], mockRequireResults[key], expectedOutputs[key]]); +} + +describe('_interopNamespace', () => { + describe('returns the same result as the original', () => { + const expectedOutputs: Record = { + withESModuleFlag: withESModuleFlag, + withESModuleFlagAndDefault: withESModuleFlagAndDefault, + namedExports: { ...namedExports, default: namedExports }, + withNonEnumerableProp: { + ...withNonEnumerableProp, + default: withNonEnumerableProp, + }, + withDefaultExport: { ...withDefaultExport, default: withDefaultExport }, + withOnlyDefaultExport: { default: withOnlyDefaultExport }, + exportsEquals: { default: exportsEquals }, + exportsEqualsWithDefault: { default: exportsEqualsWithDefault }, + }; + + const testCases = makeTestCases(expectedOutputs); + + it.each(testCases)('%s', (_, requireResult, expectedOutput) => { + expect(_interopNamespace(requireResult)).toEqual(_interopNamespaceOrig(requireResult)); + expect(_interopNamespace(requireResult)).toEqual(expectedOutput); + }); + }); +}); + +describe('_interopNamespaceDefaultOnly', () => { + describe('returns the same result as the original', () => { + const expectedOutputs: Record = { + withESModuleFlag: { default: withESModuleFlag }, + withESModuleFlagAndDefault: { default: withESModuleFlagAndDefault }, + namedExports: { default: namedExports }, + withNonEnumerableProp: { default: withNonEnumerableProp }, + withDefaultExport: { default: withDefaultExport }, + withOnlyDefaultExport: { default: withOnlyDefaultExport }, + exportsEquals: { default: exportsEquals }, + exportsEqualsWithDefault: { default: exportsEqualsWithDefault }, + }; + + const testCases = makeTestCases(expectedOutputs); + + it.each(testCases)('%s', (_, requireResult, expectedOutput) => { + expect(_interopNamespaceDefaultOnly(requireResult)).toEqual(_interopNamespaceDefaultOnlyOrig(requireResult)); + expect(_interopNamespaceDefaultOnly(requireResult)).toEqual(expectedOutput); + }); + }); +}); + +describe('_interopRequireWildcard', () => { + describe('returns the same result as the original', () => { + const expectedOutputs: Record = { + withESModuleFlag: withESModuleFlag, + withESModuleFlagAndDefault: withESModuleFlagAndDefault, + namedExports: { ...namedExports, default: namedExports }, + withNonEnumerableProp: { + ...withNonEnumerableProp, + default: withNonEnumerableProp, + }, + withDefaultExport: { ...withDefaultExport, default: withDefaultExport }, + withOnlyDefaultExport: { default: withOnlyDefaultExport }, + exportsEquals: { default: exportsEquals }, + exportsEqualsWithDefault: { default: exportsEqualsWithDefault }, + }; + + const testCases = makeTestCases(expectedOutputs); + + it.each(testCases)('%s', (_, requireResult, expectedOutput) => { + expect(_interopRequireWildcard(requireResult)).toEqual(_interopRequireWildcardOrig(requireResult)); + expect(_interopRequireWildcard(requireResult)).toEqual(expectedOutput); + }); + }); +}); + +describe('_interopDefault', () => { + describe('returns the same result as the original', () => { + const expectedOutputs: Record = { + withESModuleFlag: undefined as unknown as RequireResult, + withESModuleFlagAndDefault: withESModuleFlagAndDefault.default as RequireResult, + namedExports: namedExports, + withNonEnumerableProp: withNonEnumerableProp, + withDefaultExport: withDefaultExport, + withOnlyDefaultExport: withOnlyDefaultExport, + exportsEquals: exportsEquals, + exportsEqualsWithDefault: exportsEqualsWithDefault, + }; + + const testCases = makeTestCases(expectedOutputs); + + it.each(testCases)('%s', (_, requireResult, expectedOutput) => { + expect(_interopDefault(requireResult)).toEqual(_interopDefaultOrig(requireResult)); + expect(_interopDefault(requireResult)).toEqual(expectedOutput); + }); + }); +}); + +describe('_interopRequireDefault', () => { + describe('returns the same result as the original', () => { + const expectedOutputs: Record = { + withESModuleFlag: withESModuleFlag, + withESModuleFlagAndDefault: withESModuleFlagAndDefault, + namedExports: { default: namedExports }, + withNonEnumerableProp: { default: withNonEnumerableProp }, + withDefaultExport: { default: withDefaultExport }, + withOnlyDefaultExport: { default: withOnlyDefaultExport }, + exportsEquals: { default: exportsEquals }, + exportsEqualsWithDefault: { default: exportsEqualsWithDefault }, + }; + + const testCases = makeTestCases(expectedOutputs); + + it.each(testCases)('%s', (_, requireResult, expectedOutput) => { + expect(_interopRequireDefault(requireResult)).toEqual(_interopRequireDefaultOrig(requireResult)); + expect(_interopRequireDefault(requireResult)).toEqual(expectedOutput); + }); + }); +}); diff --git a/packages/utils/test/buildPolyfills/nullishCoalesce.test.ts b/packages/utils/test/buildPolyfills/nullishCoalesce.test.ts new file mode 100644 index 000000000000..55fd5ee9c996 --- /dev/null +++ b/packages/utils/test/buildPolyfills/nullishCoalesce.test.ts @@ -0,0 +1,29 @@ +import { _nullishCoalesce } from '../../src/buildPolyfills'; +import { Value } from '../../src/buildPolyfills/types'; +import { _nullishCoalesce as _nullishCoalesceOrig } from './originals'; + +const dogStr = 'dogs are great!'; +const dogFunc = () => dogStr; +const dogAdjectives = { maisey: 'silly', charlie: 'goofy' }; +const dogAdjectiveFunc = () => dogAdjectives; + +describe('_nullishCoalesce', () => { + describe('returns the same result as the original', () => { + const testCases: Array<[string, Value, () => Value, Value]> = [ + ['null LHS', null, dogFunc, dogStr], + ['undefined LHS', undefined, dogFunc, dogStr], + ['false LHS', false, dogFunc, false], + ['zero LHS', 0, dogFunc, 0], + ['empty string LHS', '', dogFunc, ''], + ['true LHS', true, dogFunc, true], + ['truthy primitive LHS', 12312012, dogFunc, 12312012], + ['truthy object LHS', dogAdjectives, dogFunc, dogAdjectives], + ['truthy function LHS', dogAdjectiveFunc, dogFunc, dogAdjectiveFunc], + ]; + + it.each(testCases)('%s', (_, lhs, rhs, expectedValue) => { + expect(_nullishCoalesce(lhs, rhs)).toEqual(_nullishCoalesceOrig(lhs, rhs)); + expect(_nullishCoalesce(lhs, rhs)).toEqual(expectedValue); + }); + }); +}); diff --git a/packages/utils/test/buildPolyfills/optionalChain.test.ts b/packages/utils/test/buildPolyfills/optionalChain.test.ts new file mode 100644 index 000000000000..4fec5ee1dc63 --- /dev/null +++ b/packages/utils/test/buildPolyfills/optionalChain.test.ts @@ -0,0 +1,87 @@ +import { default as arrayFlat } from 'array.prototype.flat'; + +import { _optionalChain } from '../../src/buildPolyfills'; +import { GenericFunction, GenericObject, Value } from '../../src/buildPolyfills/types'; +import { _optionalChain as _optionalChainOrig } from './originals'; + +// Older versions of Node don't have `Array.prototype.flat`, which crashes these tests. On newer versions that do have +// it, this is a no-op. +arrayFlat.shim(); + +type OperationType = 'access' | 'call' | 'optionalAccess' | 'optionalCall'; +type OperationExecutor = + | ((intermediateValue: GenericObject) => Value) + | ((intermediateValue: GenericFunction) => Value); +type Operation = [OperationType, OperationExecutor]; + +const truthyObject = { maisey: 'silly', charlie: 'goofy' }; +const nullishObject = null; +const truthyFunc = (): GenericObject => truthyObject; +const nullishFunc = undefined; +const truthyReturn = (): GenericObject => truthyObject; +const nullishReturn = (): null => nullishObject; + +// The polyfill being tested here works under the assumption that the original code containing the optional chain has +// been transformed into an array of values, labels, and functions. For example, `truthyObject?.charlie` will have been +// transformed into `_optionalChain([truthyObject, 'optionalAccess', _ => _.charlie])`. We are not testing the +// transformation here, only what the polyfill does with the already-transformed inputs. + +describe('_optionalChain', () => { + describe('returns the same result as the original', () => { + // In these test cases, the array passed to `_optionalChain` has been broken up into the first entry followed by an + // array of pairs of subsequent elements, because this seemed the easiest way to express the type, which is really + // + // [Value, OperationType, Value => Value, OperationType, Value => Value, OperationType, Value => Value, ...]. + // + // (In other words, `[A, B, C, D, E]` has become `A, [[B, C], [D, E]]`, and these are then the second and third + // entries in each test case.) We then undo this wrapping before passing the data to our functions. + const testCases: Array<[string, Value, Operation[], Value]> = [ + ['truthyObject?.charlie', truthyObject, [['optionalAccess', (_: GenericObject) => _.charlie]], 'goofy'], + ['nullishObject?.maisey', nullishObject, [['optionalAccess', (_: GenericObject) => _.maisey]], undefined], + [ + 'truthyFunc?.().maisey', + truthyFunc, + [ + ['optionalCall', (_: GenericFunction) => _()], + ['access', (_: GenericObject) => _.maisey], + ], + 'silly', + ], + [ + 'nullishFunc?.().charlie', + nullishFunc, + [ + ['optionalCall', (_: GenericFunction) => _()], + ['access', (_: GenericObject) => _.charlie], + ], + undefined, + ], + [ + 'truthyReturn()?.maisey', + truthyReturn, + [ + ['call', (_: GenericFunction) => _()], + ['optionalAccess', (_: GenericObject) => _.maisey], + ], + 'silly', + ], + [ + 'nullishReturn()?.charlie', + nullishReturn, + [ + ['call', (_: GenericFunction) => _()], + ['optionalAccess', (_: GenericObject) => _.charlie], + ], + undefined, + ], + ]; + + it.each(testCases)('%s', (_, initialChainComponent, operations, expectedValue) => { + // `operations` is flattened and spread in order to undo the wrapping done in the test cases for TS purposes. + expect(_optionalChain([initialChainComponent, ...operations.flat()])).toEqual( + _optionalChainOrig([initialChainComponent, ...operations.flat()]), + ); + expect(_optionalChain([initialChainComponent, ...operations.flat()])).toEqual(expectedValue); + }); + }); +}); diff --git a/packages/utils/test/buildPolyfills/originals.d.ts b/packages/utils/test/buildPolyfills/originals.d.ts new file mode 100644 index 000000000000..323d6f26e93c --- /dev/null +++ b/packages/utils/test/buildPolyfills/originals.d.ts @@ -0,0 +1,23 @@ +// NOTE: Unlike other types files, this is NOT auto-generated by our build scripts. Since these functions are the +// originals of functions we adapted from Rollup and Sucrase, there's no reason they should ever change, but if they do, +// this file needs to be regenerated, by running +// `yarn tsc --allowJs --skipLibCheck --declaration --emitDeclarationOnly test/buildPolyfills/originals.js` +// from within the `utils` package. Keep in mind that running that command will clobber this note, so make sure to copy +// it before you regenerate the types, so you can add it back in.) + +export function _asyncNullishCoalesce(lhs: any, rhsFn: any): Promise; +export function _asyncOptionalChain(ops: any): Promise; +export function _asyncOptionalChainDelete(ops: any): Promise; +export function _createNamedExportFrom(obj: any, localName: any, importedName: any): void; +export function _createStarExport(obj: any): void; +export function _interopDefault(e: any): any; +export function _interopNamespace(e: any): any; +export function _interopNamespaceDefaultOnly(e: any): { + __proto__: any; + default: any; +}; +export function _interopRequireDefault(obj: any): any; +export function _interopRequireWildcard(obj: any): any; +export function _nullishCoalesce(lhs: any, rhsFn: any): any; +export function _optionalChain(ops: any): any; +export function _optionalChainDelete(ops: any): any; diff --git a/packages/utils/test/buildPolyfills/originals.js b/packages/utils/test/buildPolyfills/originals.js new file mode 100644 index 000000000000..d3dcb22e8082 --- /dev/null +++ b/packages/utils/test/buildPolyfills/originals.js @@ -0,0 +1,147 @@ +// Originals of the buildPolyfills from Sucrase and Rollup we use (which we have adapted in various ways), preserved here for testing, to prove that +// the modified versions do the same thing the originals do. + +// From Sucrase +export async function _asyncNullishCoalesce(lhs, rhsFn) { + if (lhs != null) { + return lhs; + } else { + return await rhsFn(); + } +} + +// From Sucrase +export async function _asyncOptionalChain(ops) { + let lastAccessLHS = undefined; + let value = ops[0]; + let i = 1; + while (i < ops.length) { + const op = ops[i]; + const fn = ops[i + 1]; + i += 2; + if ((op === 'optionalAccess' || op === 'optionalCall') && value == null) { + return undefined; + } + if (op === 'access' || op === 'optionalAccess') { + lastAccessLHS = value; + value = await fn(value); + } else if (op === 'call' || op === 'optionalCall') { + value = await fn((...args) => value.call(lastAccessLHS, ...args)); + lastAccessLHS = undefined; + } + } + return value; +} + +// From Sucrase +export async function _asyncOptionalChainDelete(ops) { + const result = await _asyncOptionalChain(ops); + // by checking for loose equality to `null`, we catch both `null` and `undefined` + return result == null ? true : result; +} + +// From Sucrase +export function _createNamedExportFrom(obj, localName, importedName) { + Object.defineProperty(exports, localName, { enumerable: true, get: () => obj[importedName] }); +} + +// From Sucrase +export function _createStarExport(obj) { + Object.keys(obj) + .filter(key => key !== 'default' && key !== '__esModule') + .forEach(key => { + // eslint-disable-next-line no-prototype-builtins + if (exports.hasOwnProperty(key)) { + return; + } + Object.defineProperty(exports, key, { enumerable: true, get: () => obj[key] }); + }); +} + +// From Rollup +export function _interopDefault(e) { + return e && e.__esModule ? e['default'] : e; +} + +// From Rollup +export function _interopNamespace(e) { + if (e && e.__esModule) return e; + var n = Object.create(null); + if (e) { + // eslint-disable-next-line guard-for-in + for (var k in e) { + n[k] = e[k]; + } + } + n['default'] = e; + return n; +} + +export function _interopNamespaceDefaultOnly(e) { + return { + __proto__: null, + default: e, + }; +} + +// From Sucrase +export function _interopRequireDefault(obj) { + return obj && obj.__esModule ? obj : { default: obj }; +} + +// From Sucrase +export function _interopRequireWildcard(obj) { + if (obj && obj.__esModule) { + return obj; + } else { + var newObj = {}; + if (obj != null) { + for (var key in obj) { + if (Object.prototype.hasOwnProperty.call(obj, key)) { + newObj[key] = obj[key]; + } + } + } + newObj.default = obj; + return newObj; + } +} + +// From Sucrase +export function _nullishCoalesce(lhs, rhsFn) { + if (lhs != null) { + return lhs; + } else { + return rhsFn(); + } +} + +// From Sucrase +export function _optionalChain(ops) { + let lastAccessLHS = undefined; + let value = ops[0]; + let i = 1; + while (i < ops.length) { + const op = ops[i]; + const fn = ops[i + 1]; + i += 2; + if ((op === 'optionalAccess' || op === 'optionalCall') && value == null) { + return undefined; + } + if (op === 'access' || op === 'optionalAccess') { + lastAccessLHS = value; + value = fn(value); + } else if (op === 'call' || op === 'optionalCall') { + value = fn((...args) => value.call(lastAccessLHS, ...args)); + lastAccessLHS = undefined; + } + } + return value; +} + +// From Sucrase +export function _optionalChainDelete(ops) { + const result = _optionalChain(ops); + // by checking for loose equality to `null`, we catch both `null` and `undefined` + return result == null ? true : result; +} diff --git a/packages/utils/tsconfig.test.json b/packages/utils/tsconfig.test.json index 87f6afa06b86..0f755903fa64 100644 --- a/packages/utils/tsconfig.test.json +++ b/packages/utils/tsconfig.test.json @@ -5,8 +5,19 @@ "compilerOptions": { // should include all types from `./tsconfig.json` plus types for all test frameworks used - "types": ["node", "jest"] + "types": ["node", "jest"], // other package-specific, test-specific options + // this is necessary in order to be able to handle the buildPolyfills `originals.js` which is used for testing + "allowJs": true, + + // `es2020` is the recommended `lib` and `target` for Node 14 + // see https://github.com/tsconfig/bases/blob/main/bases/node14.json + "lib": ["dom", "es2020"], + "module": "commonjs", + "target": "es2020", + + // so we don't have to worry about how libraries we use export things + "esModuleInterop": true } } diff --git a/rollup/npmHelpers.js b/rollup/npmHelpers.js index 79a14189a880..440223c7e757 100644 --- a/rollup/npmHelpers.js +++ b/rollup/npmHelpers.js @@ -59,6 +59,10 @@ export function makeBaseNPMConfig(options = {}) { // }); externalLiveBindings: false, + // Don't call `Object.freeze` on the results of `import * as someModule from '...'` + // (We don't need it, so why waste the bytes?) + freeze: false, + // Equivalent to `esModuleInterop` in tsconfig. // Controls whether rollup emits helpers to handle special cases where turning // `import * as dogs from 'dogs'` diff --git a/scripts/test.ts b/scripts/test.ts index 1aa4dc6ac8b1..e7058f2b66a8 100644 --- a/scripts/test.ts +++ b/scripts/test.ts @@ -28,6 +28,17 @@ const NODE_8_LEGACY_DEPENDENCIES = [ ]; const NODE_10_LEGACY_DEPENDENCIES = ['jsdom@16.x']; +type JSONValue = string | number | boolean | null | JSONArray | JSONObject; + +type JSONObject = { + [key: string]: JSONValue; +}; +type JSONArray = Array; + +interface TSConfigJSON extends JSONObject { + compilerOptions: { lib: string[]; target: string }; +} + /** * Run the given shell command, piping the shell process's `stdin`, `stdout`, and `stderr` to that of the current * process. Returns contents of `stdout`. @@ -52,7 +63,7 @@ function installLegacyDeps(legacyDeps: string[] = []): void { * it to a `var` solves this by making it redeclarable. * */ -function addTransformer(): void { +function addJestTransformer(): void { // Though newer `ts-jest` versions support transformers written in TS, the legacy version does not. run('yarn tsc --skipLibCheck jest/transformers/constReplacer.ts'); @@ -81,6 +92,34 @@ function addTransformer(): void { fs.writeFileSync(path.resolve('jest/jest.config.js'), code); } +/** + * Modify a json file on disk. + * + * @param filepath The path to the file to be modified + * @param transformer A function which takes the JSON data as input and returns a mutated version. It may mutate the + * JSON data in place, but it isn't required to do so. + */ +export function modifyJSONFile(filepath: string, transformer: (json: JSONObject) => JSONObject): void { + const fileContents = fs + .readFileSync(filepath) + .toString() + // get rid of comments, which the `jsonc` format allows, but which will crash `JSON.parse` + .replace(/\/\/.*\n/g, ''); + const json = JSON.parse(fileContents); + const newJSON = transformer(json); + fs.writeFileSync(filepath, JSON.stringify(newJSON, null, 2)); +} + +const es6ifyTestTSConfig = (pkg: string): void => { + const filepath = `packages/${pkg}/tsconfig.test.json`; + const transformer = (json: JSONObject): JSONObject => { + const tsconfig = json as TSConfigJSON; + tsconfig.compilerOptions.target = 'es6'; + return json; + }; + modifyJSONFile(filepath, transformer); +}; + /** * Skip tests which don't apply to Node and therefore don't need to run in older Node versions. * @@ -107,15 +146,22 @@ function runTests(): void { if (CURRENT_NODE_VERSION === '8') { installLegacyDeps(NODE_8_LEGACY_DEPENDENCIES); // Inject a `const`-to-`var` transformer, in order to stop Node 8 from complaining when we shadow `global` - addTransformer(); + addJestTransformer(); // TODO Right now, this just skips incompatible tests, but it could be skipping more (hence the aspirational name), // and not just in Node 8. See `skipNonNodeTests`'s docstring. skipNonNodeTests(); + es6ifyTestTSConfig('utils'); runWithIgnores(NODE_8_SKIP_TESTS_PACKAGES); } // else if (CURRENT_NODE_VERSION === '10') { installLegacyDeps(NODE_10_LEGACY_DEPENDENCIES); + es6ifyTestTSConfig('utils'); + runWithIgnores(DEFAULT_SKIP_TESTS_PACKAGES); + } + // + else if (CURRENT_NODE_VERSION === '12') { + es6ifyTestTSConfig('utils'); runWithIgnores(DEFAULT_SKIP_TESTS_PACKAGES); } // diff --git a/yarn.lock b/yarn.lock index bf506395377a..4e6d2a0f257d 100644 --- a/yarn.lock +++ b/yarn.lock @@ -4468,6 +4468,11 @@ resolved "https://registry.yarnpkg.com/@types/aria-query/-/aria-query-4.2.1.tgz#78b5433344e2f92e8b306c06a5622c50c245bf6b" integrity sha512-S6oPal772qJZHoRZLFc/XoZW2gFvwXusYUmXPXkgxJLuEk2vOt7jc4Yo6z/vtI0EBkbPBVrJJ0B+prLIKiWqHg== +"@types/array.prototype.flat@^1.2.1": + version "1.2.1" + resolved "https://registry.yarnpkg.com/@types/array.prototype.flat/-/array.prototype.flat-1.2.1.tgz#5433a141730f8e1d7a8e7486458ceb8144ee5edc" + integrity sha512-JOvNJUU/zjfJWcA1aHDnCKHwQjZ7VQ3UNfbcMKXrkQKKyMkJHrQ9vpSVMhgsztrtsbIRJKazMDvg2QggFVwJqw== + "@types/aws-lambda@^8.10.62": version "8.10.73" resolved "https://registry.yarnpkg.com/@types/aws-lambda/-/aws-lambda-8.10.73.tgz#77773c9accb2cec26fcb7c6b510a555805604a53" @@ -5772,6 +5777,11 @@ acorn@^8.4.1: resolved "https://registry.yarnpkg.com/acorn/-/acorn-8.6.0.tgz#e3692ba0eb1a0c83eaa4f37f5fa7368dd7142895" integrity sha512-U1riIR+lBSNi3IbxtaHOIKdH8sLFv3NYfNv8sg7ZsNhcfl4HF2++BfqqrNAxoCLQW1iiylOj76ecnaUxz+z9yw== +acorn@^8.7.0: + version "8.7.1" + resolved "https://registry.yarnpkg.com/acorn/-/acorn-8.7.1.tgz#0197122c843d1bf6d0a5e83220a788f278f63c30" + integrity sha512-Xx54uLJQZ19lKygFXOWsscKUbsBZW0CPykPhVQdhIeIwrbPmJzqeASDInc8nKBnp/JT6igTs82qPXz069H8I/A== + adjust-sourcemap-loader@3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/adjust-sourcemap-loader/-/adjust-sourcemap-loader-3.0.0.tgz#5ae12fb5b7b1c585e80bbb5a63ec163a1a45e61e" @@ -5785,11 +5795,32 @@ after@0.8.2: resolved "https://registry.yarnpkg.com/after/-/after-0.8.2.tgz#fedb394f9f0e02aa9768e702bda23b505fae7e1f" integrity sha1-/ts5T58OAqqXaOcCvaI7UF+ufh8= -agent-base@4, agent-base@5, agent-base@6, agent-base@^4.3.0, agent-base@^6.0.2, agent-base@~4.2.1: +agent-base@4, agent-base@^4.3.0: + version "4.3.0" + resolved "https://registry.yarnpkg.com/agent-base/-/agent-base-4.3.0.tgz#8165f01c436009bccad0b1d122f05ed770efc6ee" + integrity sha512-salcGninV0nPrwpGNn4VTXBb1SOuXQBiqbrNXoeizJsHrsL6ERFM2Ne3JUSBWRE6aeNJI2ROP/WEEIDUiDe3cg== + dependencies: + es6-promisify "^5.0.0" + +agent-base@5: version "5.1.1" resolved "https://registry.yarnpkg.com/agent-base/-/agent-base-5.1.1.tgz#e8fb3f242959db44d63be665db7a8e739537a32c" integrity sha512-TMeqbNl2fMW0nMjTEPOwe3J/PRFP4vqeoNuQMG0HlMrtm5QxKqdvAkZ1pRBQ/ulIyDD5Yq0nJ7YbdD8ey0TO3g== +agent-base@6, agent-base@^6.0.2: + version "6.0.2" + resolved "https://registry.yarnpkg.com/agent-base/-/agent-base-6.0.2.tgz#49fff58577cfee3f37176feab4c22e00f86d7f77" + integrity sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ== + dependencies: + debug "4" + +agent-base@~4.2.1: + version "4.2.1" + resolved "https://registry.yarnpkg.com/agent-base/-/agent-base-4.2.1.tgz#d89e5999f797875674c07d87f260fc41e83e8ca9" + integrity sha512-JVwXMr9nHYTUXsBFKUqhJwvlcYU/blreOEUkhNR2eXZIvwd+c+o5V4MgDPKWnMS/56awN3TRzIP+KoPn+roQtg== + dependencies: + es6-promisify "^5.0.0" + agentkeepalive@^3.4.1: version "3.5.2" resolved "https://registry.yarnpkg.com/agentkeepalive/-/agentkeepalive-3.5.2.tgz#a113924dd3fa24a0bc3b78108c450c2abee00f67" @@ -6172,6 +6203,16 @@ array.prototype.flat@^1.2.3: define-properties "^1.1.3" es-abstract "^1.18.0-next.1" +array.prototype.flat@^1.3.0: + version "1.3.0" + resolved "https://registry.yarnpkg.com/array.prototype.flat/-/array.prototype.flat-1.3.0.tgz#0b0c1567bf57b38b56b4c97b8aa72ab45e4adc7b" + integrity sha512-12IUEkHsAhA4DY5s0FPgNXIdc8VRSqD9Zp78a5au9abH/SOBrsp082JOWFNTjkMozh8mqcdiKuaLGhPeYztxSw== + dependencies: + call-bind "^1.0.2" + define-properties "^1.1.3" + es-abstract "^1.19.2" + es-shim-unscopables "^1.0.0" + array.prototype.flatmap@^1.2.4: version "1.2.4" resolved "https://registry.yarnpkg.com/array.prototype.flatmap/-/array.prototype.flatmap-1.2.4.tgz#94cfd47cc1556ec0747d97f7c7738c58122004c9" @@ -6272,6 +6313,13 @@ ast-types@0.13.3: resolved "https://registry.yarnpkg.com/ast-types/-/ast-types-0.13.3.tgz#50da3f28d17bdbc7969a3a2d83a0e4a72ae755a7" integrity sha512-XTZ7xGML849LkQP86sWdQzfhwbt3YwIO6MqbX9mUNYY98VKaaVZP7YNNm70IpwecbkkxmfC5IYAzOQ/2p29zRA== +ast-types@0.14.2: + version "0.14.2" + resolved "https://registry.yarnpkg.com/ast-types/-/ast-types-0.14.2.tgz#600b882df8583e3cd4f2df5fa20fa83759d4bdfd" + integrity sha512-O0yuUDnZeQDL+ncNGlJ78BiO4jnYI3bvMsD5prT0/nsgijG/LpNBIr63gTjVTNsiGkgQhiyCShTgxt8oXOrklA== + dependencies: + tslib "^2.0.1" + astral-regex@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/astral-regex/-/astral-regex-2.0.0.tgz#483143c567aeed4785759c0865786dc77d7d2e31" @@ -10206,6 +10254,14 @@ define-properties@^1.1.2, define-properties@^1.1.3: dependencies: object-keys "^1.0.12" +define-properties@^1.1.4: + version "1.1.4" + resolved "https://registry.yarnpkg.com/define-properties/-/define-properties-1.1.4.tgz#0b14d7bd7fbeb2f3572c3a7eda80ea5d57fb05b1" + integrity sha512-uckOqKcfaVvtBdsVkdPv3XjveQJsNQqmhXgRi8uhvWWuPYZCNlzT8qAyblUgNoXdHdjMTzAqeGjAoli8f+bzPA== + dependencies: + has-property-descriptors "^1.0.0" + object-keys "^1.1.1" + define-property@^0.2.5: version "0.2.5" resolved "https://registry.yarnpkg.com/define-property/-/define-property-0.2.5.tgz#c35b1ef918ec3c990f9a5bc57be04aacec5c8116" @@ -11672,6 +11728,35 @@ es-abstract@^1.17.2, es-abstract@^1.18.0-next.1, es-abstract@^1.18.0-next.2: string.prototype.trimstart "^1.0.4" unbox-primitive "^1.0.0" +es-abstract@^1.19.0, es-abstract@^1.19.2, es-abstract@^1.19.5: + version "1.20.0" + resolved "https://registry.yarnpkg.com/es-abstract/-/es-abstract-1.20.0.tgz#b2d526489cceca004588296334726329e0a6bfb6" + integrity sha512-URbD8tgRthKD3YcC39vbvSDrX23upXnPcnGAjQfgxXF5ID75YcENawc9ZX/9iTP9ptUyfCLIxTTuMYoRfiOVKA== + dependencies: + call-bind "^1.0.2" + es-to-primitive "^1.2.1" + function-bind "^1.1.1" + function.prototype.name "^1.1.5" + get-intrinsic "^1.1.1" + get-symbol-description "^1.0.0" + has "^1.0.3" + has-property-descriptors "^1.0.0" + has-symbols "^1.0.3" + internal-slot "^1.0.3" + is-callable "^1.2.4" + is-negative-zero "^2.0.2" + is-regex "^1.1.4" + is-shared-array-buffer "^1.0.2" + is-string "^1.0.7" + is-weakref "^1.0.2" + object-inspect "^1.12.0" + object-keys "^1.1.1" + object.assign "^4.1.2" + regexp.prototype.flags "^1.4.1" + string.prototype.trimend "^1.0.5" + string.prototype.trimstart "^1.0.5" + unbox-primitive "^1.0.2" + es-abstract@^1.19.1: version "1.19.1" resolved "https://registry.yarnpkg.com/es-abstract/-/es-abstract-1.19.1.tgz#d4885796876916959de78edaa0df456627115ec3" @@ -11703,6 +11788,13 @@ es-module-lexer@^0.9.0: resolved "https://registry.yarnpkg.com/es-module-lexer/-/es-module-lexer-0.9.3.tgz#6f13db00cc38417137daf74366f535c8eb438f19" integrity sha512-1HQ2M2sPtxwnvOvT1ZClHyQDiggdNjURWpY2we6aMKCQiUVxTmVs2UYPLIrD84sS+kMdUwfBSylbJPwNnBrnHQ== +es-shim-unscopables@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/es-shim-unscopables/-/es-shim-unscopables-1.0.0.tgz#702e632193201e3edf8713635d083d378e510241" + integrity sha512-Jm6GPcCdC30eMLbZ2x8z2WuRwAws3zTBBKuusffYVUrNj/GVSUAZ+xKMaUpfNDR5IbyNA5LJbaecoUVbmUcB1w== + dependencies: + has "^1.0.3" + es-to-primitive@^1.2.1: version "1.2.1" resolved "https://registry.yarnpkg.com/es-to-primitive/-/es-to-primitive-1.2.1.tgz#e55cd4c9cdc188bcefb03b366c736323fc5c898a" @@ -11735,6 +11827,18 @@ es6-object-assign@^1.1.0: resolved "https://registry.yarnpkg.com/es6-object-assign/-/es6-object-assign-1.1.0.tgz#c2c3582656247c39ea107cb1e6652b6f9f24523c" integrity sha1-wsNYJlYkfDnqEHyx5mUrb58kUjw= +es6-promise@^4.0.3: + version "4.2.8" + resolved "https://registry.yarnpkg.com/es6-promise/-/es6-promise-4.2.8.tgz#4eb21594c972bc40553d276e510539143db53e0a" + integrity sha512-HJDGx5daxeIvxdBxvG2cb9g4tEvwIk3i8+nhX0yGrYmZUzbkdg8QbDevheDB8gd0//uPj4c1EQua8Q+MViT0/w== + +es6-promisify@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/es6-promisify/-/es6-promisify-5.0.0.tgz#5109d62f3e56ea967c4b63505aef08291c8a5203" + integrity sha1-UQnWLz5W6pZ8S2NQWu8IKRyKUgM= + dependencies: + es6-promise "^4.0.3" + es6-symbol@^3.1.1, es6-symbol@^3.1.3: version "3.1.3" resolved "https://registry.yarnpkg.com/es6-symbol/-/es6-symbol-3.1.3.tgz#bad5d3c1bcdac28269f4cb331e431c78ac705d18" @@ -13168,6 +13272,16 @@ function-bind@^1.1.1: resolved "https://registry.yarnpkg.com/function-bind/-/function-bind-1.1.1.tgz#a56899d3ea3c9bab874bb9773b7c5ede92f4895d" integrity sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A== +function.prototype.name@^1.1.5: + version "1.1.5" + resolved "https://registry.yarnpkg.com/function.prototype.name/-/function.prototype.name-1.1.5.tgz#cce0505fe1ffb80503e6f9e46cc64e46a12a9621" + integrity sha512-uN7m/BzVKQnCUF/iW8jYea67v++2u7m5UgENbHRtdDVclOUP+FMPlCNdmk0h/ysGyo2tavMJEDqJAkJdRa1vMA== + dependencies: + call-bind "^1.0.2" + define-properties "^1.1.3" + es-abstract "^1.19.0" + functions-have-names "^1.2.2" + functional-red-black-tree@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/functional-red-black-tree/-/functional-red-black-tree-1.0.1.tgz#1b0ab3bd553b2a0d6399d29c0e3ea0b252078327" @@ -13862,6 +13976,11 @@ has-bigints@^1.0.1: resolved "https://registry.yarnpkg.com/has-bigints/-/has-bigints-1.0.1.tgz#64fe6acb020673e3b78db035a5af69aa9d07b113" integrity sha512-LSBS2LjbNBTf6287JEbEzvJgftkF5qFkmCo9hDRpAzKhUOlJ+hx8dd4USs00SgsUNwc4617J9ki5YtEClM2ffA== +has-bigints@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/has-bigints/-/has-bigints-1.0.2.tgz#0871bd3e3d51626f6ca0966668ba35d5602d6eaa" + integrity sha512-tSvCKtBr9lkF0Ex0aQiP9N+OpV4zi2r/Nee5VkRDbaqv35RLYMzbwQfFSZZH0kR+Rd6302UJZ2p/bJCEoR3VoQ== + has-binary2@~1.0.2: version "1.0.3" resolved "https://registry.yarnpkg.com/has-binary2/-/has-binary2-1.0.3.tgz#7776ac627f3ea77250cfc332dab7ddf5e4f5d11d" @@ -13889,6 +14008,13 @@ has-flag@^4.0.0: resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-4.0.0.tgz#944771fd9c81c81265c4d6941860da06bb59479b" integrity sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ== +has-property-descriptors@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/has-property-descriptors/-/has-property-descriptors-1.0.0.tgz#610708600606d36961ed04c196193b6a607fa861" + integrity sha512-62DVLZGoiEBDHQyqG4w9xCuZ7eJEwNmJRWw2VY84Oedb7WFcA27fiEVe8oUQx9hAUJ4ekurquucTGwsyO1XGdQ== + dependencies: + get-intrinsic "^1.1.1" + has-symbol-support-x@^1.4.1: version "1.4.2" resolved "https://registry.yarnpkg.com/has-symbol-support-x/-/has-symbol-support-x-1.4.2.tgz#1409f98bc00247da45da67cee0a36f282ff26455" @@ -13899,6 +14025,11 @@ has-symbols@^1.0.0, has-symbols@^1.0.1, has-symbols@^1.0.2: resolved "https://registry.yarnpkg.com/has-symbols/-/has-symbols-1.0.2.tgz#165d3070c00309752a1236a479331e3ac56f1423" integrity sha512-chXa79rL/UC2KlX17jo3vRGz0azaWEx5tGqZg5pO3NUyEJVB17dMruQlzCCOfUvElghKcm5194+BCRvi2Rv/Gw== +has-symbols@^1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/has-symbols/-/has-symbols-1.0.3.tgz#bb7b2c4349251dce87b125f7bdf874aa7c8b39f8" + integrity sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A== + has-to-string-tag-x@^1.2.0: version "1.4.1" resolved "https://registry.yarnpkg.com/has-to-string-tag-x/-/has-to-string-tag-x-1.4.1.tgz#a045ab383d7b4b2012a00148ab0aa5f290044d4d" @@ -15017,6 +15148,11 @@ is-negative-zero@^2.0.1: resolved "https://registry.yarnpkg.com/is-negative-zero/-/is-negative-zero-2.0.1.tgz#3de746c18dda2319241a53675908d8f766f11c24" integrity sha512-2z6JzQvZRa9A2Y7xC6dQQm4FSTSTNWjKIYYTt4246eMTJmIo0Q+ZyOsU66X8lxK1AbB92dFeglPLrhwpeRKO6w== +is-negative-zero@^2.0.2: + version "2.0.2" + resolved "https://registry.yarnpkg.com/is-negative-zero/-/is-negative-zero-2.0.2.tgz#7bf6f03a28003b8b3965de3ac26f664d765f3150" + integrity sha512-dqJvarLawXsFbNDeJW7zAz8ItJ9cd28YufuuFzh0G8pNHjJMnY08Dv7sYX2uF5UpQOwieAeOExEYAWWfu7ZZUA== + is-number-object@^1.0.4: version "1.0.4" resolved "https://registry.yarnpkg.com/is-number-object/-/is-number-object-1.0.4.tgz#36ac95e741cf18b283fc1ddf5e83da798e3ec197" @@ -15153,6 +15289,13 @@ is-shared-array-buffer@^1.0.1: resolved "https://registry.yarnpkg.com/is-shared-array-buffer/-/is-shared-array-buffer-1.0.1.tgz#97b0c85fbdacb59c9c446fe653b82cf2b5b7cfe6" integrity sha512-IU0NmyknYZN0rChcKhRO1X8LYz5Isj/Fsqh8NJOSf+N/hCOTwy29F32Ik7a+QszE63IdvmwdTPDd6cZ5pg4cwA== +is-shared-array-buffer@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/is-shared-array-buffer/-/is-shared-array-buffer-1.0.2.tgz#8f259c573b60b6a32d4058a1a07430c0a7344c79" + integrity sha512-sqN2UDu1/0y6uvXyStCOzyhAjCSlHceFoMKJW8W9EU9cvic/QdsZ0kEU93HEy3IUEFZIiH/3w+AH/UQbPHNdhA== + dependencies: + call-bind "^1.0.2" + is-ssh@^1.3.0: version "1.3.2" resolved "https://registry.yarnpkg.com/is-ssh/-/is-ssh-1.3.2.tgz#a4b82ab63d73976fd8263cceee27f99a88bdae2b" @@ -15258,6 +15401,13 @@ is-weakref@^1.0.1: dependencies: call-bind "^1.0.0" +is-weakref@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/is-weakref/-/is-weakref-1.0.2.tgz#9529f383a9338205e89765e0392efc2f100f06f2" + integrity sha512-qctsuLZmIQ0+vSSMfoVvyFe2+GSEvnmZ2ezTup1SBse9+twCCeial6EEi3Nc2KFcf6+qz2FBPnjXsk8xhKSaPQ== + dependencies: + call-bind "^1.0.2" + is-what@^3.14.1: version "3.14.1" resolved "https://registry.yarnpkg.com/is-what/-/is-what-3.14.1.tgz#e1222f46ddda85dead0fd1c9df131760e77755c1" @@ -18833,6 +18983,11 @@ object-inspect@^1.11.0: resolved "https://registry.yarnpkg.com/object-inspect/-/object-inspect-1.11.1.tgz#d4bd7d7de54b9a75599f59a00bd698c1f1c6549b" integrity sha512-If7BjFlpkzzBeV1cqgT3OSWT3azyoxDGajR+iGnFBfVV2EWyDyWaZZW2ERDjUaY2QM8i5jI3Sj7mhsM4DDAqWA== +object-inspect@^1.12.0: + version "1.12.0" + resolved "https://registry.yarnpkg.com/object-inspect/-/object-inspect-1.12.0.tgz#6e2c120e868fd1fd18cb4f18c31741d0d6e776f0" + integrity sha512-Ho2z80bVIvJloH+YzRmpZVQe87+qASmBUKZDWgx9cu+KDrX2ZDH/3tMy+gXbZETVGs2M8YdxObOh7XAtim9Y0g== + object-inspect@^1.9.0: version "1.9.0" resolved "https://registry.yarnpkg.com/object-inspect/-/object-inspect-1.9.0.tgz#c90521d74e1127b67266ded3394ad6116986533a" @@ -21204,6 +21359,16 @@ recast@^0.18.1: private "^0.1.8" source-map "~0.6.1" +recast@^0.20.5: + version "0.20.5" + resolved "https://registry.yarnpkg.com/recast/-/recast-0.20.5.tgz#8e2c6c96827a1b339c634dd232957d230553ceae" + integrity sha512-E5qICoPoNL4yU0H0NoBDntNB0Q5oMSNh9usFctYniLBluTthi3RsQVBXIJNbApOlvSwW/RGxIuokPcAc59J5fQ== + dependencies: + ast-types "0.14.2" + esprima "~4.0.0" + source-map "~0.6.1" + tslib "^2.0.1" + rechoir@^0.6.2: version "0.6.2" resolved "https://registry.yarnpkg.com/rechoir/-/rechoir-0.6.2.tgz#85204b54dba82d5742e28c96756ef43af50e3384" @@ -21330,7 +21495,7 @@ regex-parser@^2.2.11: resolved "https://registry.yarnpkg.com/regex-parser/-/regex-parser-2.2.11.tgz#3b37ec9049e19479806e878cabe7c1ca83ccfe58" integrity sha512-jbD/FT0+9MBU2XAZluI7w2OBs1RBi6p9M83nkoZayQXXU9e8Robt69FcZc7wU4eJD/YFTjn1JdCk3rbMJajz8Q== -regexp.prototype.flags@^1.2.0: +regexp.prototype.flags@^1.2.0, regexp.prototype.flags@^1.4.1: version "1.4.3" resolved "https://registry.yarnpkg.com/regexp.prototype.flags/-/regexp.prototype.flags-1.4.3.tgz#87cab30f80f66660181a3bb7bf5981a872b367ac" integrity sha512-fjggEOO3slI6Wvgjwflkc4NFRCTZAu5CnNfBd5qOMYhWdn67nJBBu34/TkD++eeFmd8C9r9jfXJ27+nSiRkSUA== @@ -23312,6 +23477,15 @@ string.prototype.trimend@^1.0.4: call-bind "^1.0.2" define-properties "^1.1.3" +string.prototype.trimend@^1.0.5: + version "1.0.5" + resolved "https://registry.yarnpkg.com/string.prototype.trimend/-/string.prototype.trimend-1.0.5.tgz#914a65baaab25fbdd4ee291ca7dde57e869cb8d0" + integrity sha512-I7RGvmjV4pJ7O3kdf+LXFpVfdNOxtCW/2C8f6jNiW4+PQchwxkCDzlk1/7p+Wl4bqFIZeF47qAHXLuHHWKAxog== + dependencies: + call-bind "^1.0.2" + define-properties "^1.1.4" + es-abstract "^1.19.5" + string.prototype.trimstart@^1.0.4: version "1.0.4" resolved "https://registry.yarnpkg.com/string.prototype.trimstart/-/string.prototype.trimstart-1.0.4.tgz#b36399af4ab2999b4c9c648bd7a3fb2bb26feeed" @@ -23320,6 +23494,15 @@ string.prototype.trimstart@^1.0.4: call-bind "^1.0.2" define-properties "^1.1.3" +string.prototype.trimstart@^1.0.5: + version "1.0.5" + resolved "https://registry.yarnpkg.com/string.prototype.trimstart/-/string.prototype.trimstart-1.0.5.tgz#5466d93ba58cfa2134839f81d7f42437e8c01fef" + integrity sha512-THx16TJCGlsN0o6dl2o6ncWUsdgnLRSA23rRE5pyGBw/mLr3Ej/R2LaqCtgP8VNMGZsvMWnf9ooZPyY2bHvUFg== + dependencies: + call-bind "^1.0.2" + define-properties "^1.1.4" + es-abstract "^1.19.5" + string_decoder@0.10, string_decoder@~0.10.x: version "0.10.31" resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-0.10.31.tgz#62e203bc41766c6c28c9fc84301dab1c5310fa94" @@ -24347,6 +24530,11 @@ tslib@^2.0.0, tslib@^2.0.3: resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.1.0.tgz#da60860f1c2ecaa5703ab7d39bc05b6bf988b97a" integrity sha512-hcVC3wYEziELGGmEEXue7D75zbwIIVUMWAVbHItGPx0ziyXxrOMQx4rQEVEV45Ut/1IotuEvwqPopzIOkDMf0A== +tslib@^2.0.1: + version "2.4.0" + resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.4.0.tgz#7cecaa7f073ce680a05847aa77be941098f36dc3" + integrity sha512-d6xOpEDfsi2CZVlPQzGeux8XMwLT9hssAsaPYExaQMuYskwb+x1x7J371tWlbBdWHroy99KnVB6qIkUbs5X3UQ== + tslib@^2.3.0, tslib@^2.3.1: version "2.3.1" resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.3.1.tgz#e8a335add5ceae51aa261d32a490158ef042ef01" @@ -24563,6 +24751,16 @@ unbox-primitive@^1.0.0, unbox-primitive@^1.0.1: has-symbols "^1.0.2" which-boxed-primitive "^1.0.2" +unbox-primitive@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/unbox-primitive/-/unbox-primitive-1.0.2.tgz#29032021057d5e6cdbd08c5129c226dff8ed6f9e" + integrity sha512-61pPlCD9h51VoreyJ0BReideM3MDKMKnh6+V9L08331ipq6Q8OFXZYiqP6n/tbHx4s5I9uRhcye6BrbkizkBDw== + dependencies: + call-bind "^1.0.2" + has-bigints "^1.0.2" + has-symbols "^1.0.3" + which-boxed-primitive "^1.0.2" + unbzip2-stream@^1.3.3: version "1.4.3" resolved "https://registry.yarnpkg.com/unbzip2-stream/-/unbzip2-stream-1.4.3.tgz#b0da04c4371311df771cdc215e87f2130991ace7" From 904b2d3fcd2e7c655a75ace4b4dc937c0d866fa0 Mon Sep 17 00:00:00 2001 From: Luca Forstner Date: Mon, 9 May 2022 20:51:46 +0200 Subject: [PATCH 128/204] fix(integrations): Mark ExtraErrorData as already normalized (#5053) --- packages/integrations/src/extraerrordata.ts | 19 +++++++++---------- 1 file changed, 9 insertions(+), 10 deletions(-) diff --git a/packages/integrations/src/extraerrordata.ts b/packages/integrations/src/extraerrordata.ts index aad7210a85b2..c2baf7197bda 100644 --- a/packages/integrations/src/extraerrordata.ts +++ b/packages/integrations/src/extraerrordata.ts @@ -1,5 +1,5 @@ -import { Event, EventHint, EventProcessor, ExtendedError, Hub, Integration } from '@sentry/types'; -import { isError, isPlainObject, logger, normalize } from '@sentry/utils'; +import { Contexts, Event, EventHint, EventProcessor, ExtendedError, Hub, Integration } from '@sentry/types'; +import { addNonEnumerableProperty, isError, isPlainObject, logger, normalize } from '@sentry/utils'; import { IS_DEBUG_BUILD } from './flags'; @@ -53,23 +53,22 @@ export class ExtraErrorData implements Integration { if (!hint || !hint.originalException || !isError(hint.originalException)) { return event; } - const name = (hint.originalException as ExtendedError).name || hint.originalException.constructor.name; + const exceptionName = (hint.originalException as ExtendedError).name || hint.originalException.constructor.name; const errorData = this._extractErrorData(hint.originalException as ExtendedError); if (errorData) { - let contexts = { + const contexts: Contexts = { ...event.contexts, }; const normalizedErrorData = normalize(errorData, this._options.depth); + if (isPlainObject(normalizedErrorData)) { - contexts = { - ...event.contexts, - [name]: { - ...normalizedErrorData, - }, - }; + // We mark the error data as "already normalized" here, because we don't want other normalization procedures to + // potentially truncate the data we just already normalized, with a certain depth setting. + addNonEnumerableProperty(normalizedErrorData, '__sentry_skip_normalization__', true); + contexts[exceptionName] = normalizedErrorData; } return { From cb9260f26a5ca42cc79a77d58a8613924046c58b Mon Sep 17 00:00:00 2001 From: Katie Byers Date: Tue, 10 May 2022 06:26:54 -0700 Subject: [PATCH 129/204] feat(build): Add polyfill-extraction rollup plugin (#5023) This is a follow-up to https://github.com/getsentry/sentry-javascript/pull/5051, which added our own versions of the polyfills that Rollup and Sucrase inject during build, in order to be able to import them rather than have their code repeated in every file in which they're needed, essentially replicating the behavior of `tslib`'s `importHelpers` option. This completes that change, by adding a rollup plugin to the build process which extracts the injected code and replaces it with an equivalent `import` or `require` statement. Because the import comes from `@sentry/utils`, the few packages which didn't already depend on it have had it added as a dependency. --- package.json | 2 + packages/gatsby/package.json | 1 + packages/wasm/package.json | 1 + rollup/npmHelpers.js | 11 +- rollup/plugins/extractPolyfillsPlugin.js | 216 +++++++++++++++++++++++ rollup/plugins/npmPlugins.js | 2 + 6 files changed, 232 insertions(+), 1 deletion(-) create mode 100644 rollup/plugins/extractPolyfillsPlugin.js diff --git a/package.json b/package.json index f2b81a98123e..3d2a67cea99f 100644 --- a/package.json +++ b/package.json @@ -67,6 +67,7 @@ "@types/mocha": "^5.2.0", "@types/node": "~10.17.0", "@types/sinon": "^7.0.11", + "acorn": "^8.7.0", "chai": "^4.1.2", "codecov": "^3.6.5", "deepmerge": "^4.2.2", @@ -81,6 +82,7 @@ "mocha": "^6.1.4", "npm-run-all": "^4.1.5", "prettier": "2.5.1", + "recast": "^0.20.5", "replace-in-file": "^4.0.0", "rimraf": "^3.0.2", "rollup": "^2.67.1", diff --git a/packages/gatsby/package.json b/packages/gatsby/package.json index 50811fce6b03..504b34015793 100644 --- a/packages/gatsby/package.json +++ b/packages/gatsby/package.json @@ -22,6 +22,7 @@ "dependencies": { "@sentry/react": "7.0.0-beta.0", "@sentry/tracing": "7.0.0-beta.0", + "@sentry/utils": "7.0.0-beta.0", "@sentry/webpack-plugin": "1.18.9" }, "peerDependencies": { diff --git a/packages/wasm/package.json b/packages/wasm/package.json index 831d45671b0c..0dfffd50df12 100644 --- a/packages/wasm/package.json +++ b/packages/wasm/package.json @@ -18,6 +18,7 @@ "dependencies": { "@sentry/browser": "7.0.0-beta.0", "@sentry/types": "7.0.0-beta.0", + "@sentry/utils": "7.0.0-beta.0", "tslib": "^1.9.3" }, "devDependencies": { diff --git a/rollup/npmHelpers.js b/rollup/npmHelpers.js index 440223c7e757..0ac0ad33a01b 100644 --- a/rollup/npmHelpers.js +++ b/rollup/npmHelpers.js @@ -9,6 +9,7 @@ import deepMerge from 'deepmerge'; import { makeConstToVarPlugin, + makeExtractPolyfillsPlugin, makeNodeResolvePlugin, makeRemoveBlankLinesPlugin, makeRemoveESLintCommentsPlugin, @@ -30,6 +31,7 @@ export function makeBaseNPMConfig(options = {}) { const constToVarPlugin = makeConstToVarPlugin(); const removeESLintCommentsPlugin = makeRemoveESLintCommentsPlugin(); const removeBlankLinesPlugin = makeRemoveBlankLinesPlugin(); + const extractPolyfillsPlugin = makeExtractPolyfillsPlugin(); // return { const config = { @@ -75,7 +77,14 @@ export function makeBaseNPMConfig(options = {}) { interop: esModuleInterop ? 'auto' : 'esModule', }, - plugins: [nodeResolvePlugin, sucrasePlugin, constToVarPlugin, removeESLintCommentsPlugin, removeBlankLinesPlugin], + plugins: [ + nodeResolvePlugin, + sucrasePlugin, + constToVarPlugin, + removeESLintCommentsPlugin, + removeBlankLinesPlugin, + extractPolyfillsPlugin, + ], // don't include imported modules from outside the package in the final output external: [ diff --git a/rollup/plugins/extractPolyfillsPlugin.js b/rollup/plugins/extractPolyfillsPlugin.js new file mode 100644 index 000000000000..e7b83b23dd35 --- /dev/null +++ b/rollup/plugins/extractPolyfillsPlugin.js @@ -0,0 +1,216 @@ +import * as path from 'path'; + +import * as recast from 'recast'; +import * as acornParser from 'recast/parsers/acorn'; + +const POLYFILL_NAMES = new Set([ + '_asyncNullishCoalesce', + '_asyncOptionalChain', + '_asyncOptionalChainDelete', + '_createNamedExportFrom', + '_createStarExport', + '_interopDefault', // rollup's version + '_interopNamespace', // rollup's version + '_interopNamespaceDefaultOnly', + '_interopRequireDefault', // sucrase's version + '_interopRequireWildcard', // sucrase's version + '_nullishCoalesce', + '_optionalChain', + '_optionalChainDelete', +]); + +/** + * Create a plugin which will replace function definitions of any of the above funcions with an `import` or `require` + * statement pulling them in from a central source. Mimics tsc's `importHelpers` option. + */ +export function makeExtractPolyfillsPlugin() { + let moduleFormat; + + // For more on the hooks used in this plugin, see https://rollupjs.org/guide/en/#output-generation-hooks + return { + name: 'extractPolyfills', + + // Figure out which build we're currently in (esm or cjs) + outputOptions(options) { + moduleFormat = options.format; + }, + + // This runs after both the sucrase transpilation (which happens in the `transform` hook) and rollup's own + // esm-i-fying or cjs-i-fying work (which happens right before `renderChunk`), in other words, after all polyfills + // will have been injected + renderChunk(code, chunk) { + const sourceFile = chunk.fileName; + + // We don't want to pull the function definitions out of their actual sourcefiles, just the places where they've + // been injected + if (sourceFile.includes('buildPolyfills')) { + return null; + } + + const parserOptions = { + sourceFileName: sourceFile, + // We supply a custom parser which wraps the provided `acorn` parser in order to override the `ecmaVersion` value. + // See https://github.com/benjamn/recast/issues/578. + parser: { + parse(source, options) { + return acornParser.parse(source, { + ...options, + // By this point in the build, everything should already have been down-compiled to whatever JS version + // we're targeting. Setting this parser to `latest` just means that whatever that version is (or changes + // to in the future), this parser will be able to handle the generated code. + ecmaVersion: 'latest', + }); + }, + }, + }; + + const ast = recast.parse(code, parserOptions); + + // Find function definitions and function expressions whose identifiers match a known polyfill name + const polyfillNodes = findPolyfillNodes(ast); + + if (polyfillNodes.length === 0) { + return null; + } + + console.log(`${sourceFile} - polyfills: ${polyfillNodes.map(node => node.name)}`); + + // Depending on the output format, generate `import { x, y, z } from '...'` or `var { x, y, z } = require('...')` + const importOrRequireNode = createImportOrRequireNode(polyfillNodes, sourceFile, moduleFormat); + + // Insert our new `import` or `require` node at the top of the file, and then delete the function definitions it's + // meant to replace (polyfill nodes get marked for deletion in `findPolyfillNodes`) + ast.program.body = [importOrRequireNode, ...ast.program.body.filter(node => !node.shouldDelete)]; + + // In spite of the name, this doesn't actually print anything - it just stringifies the code, and keeps track of + // where original nodes end up in order to generate a sourcemap. + const result = recast.print(ast, { + sourceMapName: `${sourceFile}.map`, + quote: 'single', + }); + + return { code: result.code, map: result.map }; + }, + }; +} + +/** + * Extract the function name, regardless of the format in which the function is declared + */ +function getNodeName(node) { + // Function expressions and functions pulled from objects + if (node.type === 'VariableDeclaration') { + // In practice sucrase and rollup only ever declare one polyfill at a time, so it's safe to just grab the first + // entry here + const declarationId = node.declarations[0].id; + + // Note: Sucrase and rollup seem to only use the first type of variable declaration for their polyfills, but good to + // cover our bases + + // Declarations of the form + // `const dogs = function() { return "are great"; };` + // or + // `const dogs = () => "are great"; + if (declarationId.type === 'Identifier') { + return declarationId.name; + } + // Declarations of the form + // `const { dogs } = { dogs: function() { return "are great"; } }` + // or + // `const { dogs } = { dogs: () => "are great" }` + else if (declarationId.type === 'ObjectPattern') { + return declarationId.properties[0].key.name; + } + // Any other format + else { + return 'unknown variable'; + } + } + + // Regular old functions, of the form + // `function dogs() { return "are great"; }` + else if (node.type === 'FunctionDeclaration') { + return node.id.name; + } + + // If we get here, this isn't a node we're interested in, so just return a string we know will never match any of the + // polyfill names + else { + return 'nope'; + } +} + +/** + * Find all nodes whose identifiers match a known polyfill name. + * + * Note: In theory, this could yield false positives, if any of the magic names were assigned to something other than a + * polyfill function, but the chances of that are slim. Also, it only searches the module global scope, but that's + * always where the polyfills appear, so no reason to traverse the whole tree. + */ +function findPolyfillNodes(ast) { + const isPolyfillNode = node => { + const nodeName = getNodeName(node); + if (POLYFILL_NAMES.has(nodeName)) { + // Mark this node for later deletion, since we're going to replace it with an import statement + node.shouldDelete = true; + // Store the name in a consistent spot, regardless of node type + node.name = nodeName; + + return true; + } + + return false; + }; + + return ast.program.body.filter(isPolyfillNode); +} + +/** + * Create a node representing an `import` or `require` statement of the form + * + * import { < polyfills > } from '...' + * or + * var { < polyfills > } = require('...') + * + * @param polyfillNodes The nodes from the current version of the code, defining the polyfill functions + * @param currentSourceFile The path, relative to `src/`, of the file currently being transpiled + * @param moduleFormat Either 'cjs' or 'esm' + * @returns A single node which can be subbed in for the polyfill definition nodes + */ +function createImportOrRequireNode(polyfillNodes, currentSourceFile, moduleFormat) { + const { + callExpression, + identifier, + importDeclaration, + importSpecifier, + literal, + objectPattern, + property, + variableDeclaration, + variableDeclarator, + } = recast.types.builders; + + // Since our polyfills live in `@sentry/utils`, if we're importing or requiring them there the path will have to be + // relative + const isUtilsPackage = process.cwd().endsWith('packages/utils'); + const importSource = literal( + isUtilsPackage + ? `./${path.relative(path.dirname(currentSourceFile), 'buildPolyfills')}` + : `@sentry/utils/${moduleFormat}/buildPolyfills`, + ); + + // This is the `x, y, z` of inside of `import { x, y, z }` or `var { x, y, z }` + const importees = polyfillNodes.map(({ name: fnName }) => + moduleFormat === 'esm' + ? importSpecifier(identifier(fnName)) + : property.from({ kind: 'init', key: identifier(fnName), value: identifier(fnName), shorthand: true }), + ); + + const requireFn = identifier('require'); + + return moduleFormat === 'esm' + ? importDeclaration(importees, importSource) + : variableDeclaration('var', [ + variableDeclarator(objectPattern(importees), callExpression(requireFn, [importSource])), + ]); +} diff --git a/rollup/plugins/npmPlugins.js b/rollup/plugins/npmPlugins.js index 568ef6b69e10..eec8eab81cda 100644 --- a/rollup/plugins/npmPlugins.js +++ b/rollup/plugins/npmPlugins.js @@ -97,3 +97,5 @@ export function makeRemoveBlankLinesPlugin() { ], }); } + +export { makeExtractPolyfillsPlugin } from './extractPolyfillsPlugin.js'; From 1342c4253abbb5afc7fae95fc991f1510cf8b24c Mon Sep 17 00:00:00 2001 From: Abhijeet Prasad Date: Tue, 10 May 2022 09:48:30 -0400 Subject: [PATCH 130/204] ref(node): Explicitly pass down node transport options (#5057) We enforce the transport types by assigning the generic when typing the Node Client class (we use `ClientOptions`). To further refine the transport types, we remove the transport options from the top level of the sdk init and instead require users to pass it in under the `transportOptions` key explicitly. --- MIGRATION.md | 26 ++++++++++++++++++++++++-- packages/node/src/types.ts | 15 ++++----------- 2 files changed, 28 insertions(+), 13 deletions(-) diff --git a/MIGRATION.md b/MIGRATION.md index cda6cb2836db..9c2663c05b46 100644 --- a/MIGRATION.md +++ b/MIGRATION.md @@ -218,8 +218,6 @@ Sentry.init({ ... }) - - // Before: class MyCustomTransport extends BaseTransport { constructor(options: TransportOptions) { @@ -252,6 +250,30 @@ object with your custom logic, will also take care of rate limiting and flushing For a complete v7 transport implementation, take a look at our [browser fetch transport](https://github.com/getsentry/sentry-javascript/blob/ebc938a03d6efe7d0c4bbcb47714e84c9a566a9c/packages/browser/src/transports/fetch.ts#L1-L34). +### Node Transport Changes + +To clean up the options interface, we now require users to pass down transport related options under the `transportOptions` key. The options that +were changed were `caCerts`, `httpProxy`, and `httpsProxy`. In addition, `httpProxy` and `httpsProxy` were unified to a single option under +the `transportOptions` key, `proxy`. + +```ts +// New in v7: +Sentry.init({ + dsn: '...', + transportOptions: { + caCerts: getMyCaCert(), + proxy: 'http://example.com', + }, +}); + +// Before: +Sentry.init({ + dsn: '...', + caCerts: getMyCaCert(), + httpsProxy: 'http://example.com', +}) +``` + ## Enum Changes Given that enums have a high bundle-size impact, our long term goal is to eventually remove all enums from the SDK in diff --git a/packages/node/src/types.ts b/packages/node/src/types.ts index a2311a4698f9..57b19dcd1820 100644 --- a/packages/node/src/types.ts +++ b/packages/node/src/types.ts @@ -1,18 +1,11 @@ import { ClientOptions, Options } from '@sentry/types'; +import { NodeTransportOptions } from './transports'; + export interface BaseNodeOptions { /** Sets an optional server name (device name) */ serverName?: string; - /** Set a HTTP proxy that should be used for outbound requests. */ - httpProxy?: string; - - /** Set a HTTPS proxy that should be used for outbound requests. */ - httpsProxy?: string; - - /** HTTPS proxy certificates path */ - caCerts?: string; - /** Callback that is executed when a fatal global error occurs. */ onFatalError?(error: Error): void; } @@ -21,10 +14,10 @@ export interface BaseNodeOptions { * Configuration options for the Sentry Node SDK * @see @sentry/types Options for more information. */ -export interface NodeOptions extends Options, BaseNodeOptions {} +export interface NodeOptions extends Options, BaseNodeOptions {} /** * Configuration options for the Sentry Node SDK Client class * @see NodeClient for more information. */ -export interface NodeClientOptions extends ClientOptions, BaseNodeOptions {} +export interface NodeClientOptions extends ClientOptions, BaseNodeOptions {} From 52af0b10bf20624926946c83620b04e8ab1e394d Mon Sep 17 00:00:00 2001 From: Abhijeet Prasad Date: Tue, 10 May 2022 11:02:27 -0400 Subject: [PATCH 131/204] ref(browser): Unify BrowserTransportOptions (#5058) Enforce browser transport types by unifying fetch and xhr types into a single `BrowserTransportOptions` interface. This is used as a generic for `ClientOptions`. This cleans up the transport typing, and enables the transportOptions to be typed correctly in init. --- packages/browser/src/client.ts | 5 +++-- packages/browser/src/transports/fetch.ts | 12 +++++------- packages/browser/src/transports/types.ts | 8 ++++++++ packages/browser/src/transports/xhr.ts | 10 ++++------ packages/browser/test/unit/transports/fetch.test.ts | 7 ++++--- packages/browser/test/unit/transports/xhr.test.ts | 7 ++++--- 6 files changed, 28 insertions(+), 21 deletions(-) create mode 100644 packages/browser/src/transports/types.ts diff --git a/packages/browser/src/client.ts b/packages/browser/src/client.ts index 261ceba0a88e..2eeefc18c7ed 100644 --- a/packages/browser/src/client.ts +++ b/packages/browser/src/client.ts @@ -13,6 +13,7 @@ import { eventFromException, eventFromMessage } from './eventbuilder'; import { IS_DEBUG_BUILD } from './flags'; import { Breadcrumbs } from './integrations'; import { BREADCRUMB_INTEGRATION_ID } from './integrations/breadcrumbs'; +import { BrowserTransportOptions } from './transports/types'; import { sendReport } from './transports/utils'; const globalObject = getGlobalObject(); @@ -37,13 +38,13 @@ export interface BaseBrowserOptions { * Configuration options for the Sentry Browser SDK. * @see @sentry/types Options for more information. */ -export interface BrowserOptions extends Options, BaseBrowserOptions {} +export interface BrowserOptions extends Options, BaseBrowserOptions {} /** * Configuration options for the Sentry Browser SDK Client class * @see BrowserClient for more information. */ -export interface BrowserClientOptions extends ClientOptions, BaseBrowserOptions {} +export interface BrowserClientOptions extends ClientOptions, BaseBrowserOptions {} /** * The Sentry Browser SDK Client. diff --git a/packages/browser/src/transports/fetch.ts b/packages/browser/src/transports/fetch.ts index d6f44090abb9..d4e57e59862e 100644 --- a/packages/browser/src/transports/fetch.ts +++ b/packages/browser/src/transports/fetch.ts @@ -1,17 +1,14 @@ import { createTransport } from '@sentry/core'; -import { BaseTransportOptions, Transport, TransportMakeRequestResponse, TransportRequest } from '@sentry/types'; +import { Transport, TransportMakeRequestResponse, TransportRequest } from '@sentry/types'; +import { BrowserTransportOptions } from './types'; import { FetchImpl, getNativeFetchImplementation } from './utils'; -export interface FetchTransportOptions extends BaseTransportOptions { - requestOptions?: RequestInit; -} - /** * Creates a Transport that uses the Fetch API to send events to Sentry. */ export function makeFetchTransport( - options: FetchTransportOptions, + options: BrowserTransportOptions, nativeFetch: FetchImpl = getNativeFetchImplementation(), ): Transport { function makeRequest(request: TransportRequest): PromiseLike { @@ -19,7 +16,8 @@ export function makeFetchTransport( body: request.body, method: 'POST', referrerPolicy: 'origin', - ...options.requestOptions, + headers: options.headers, + ...options.fetchOptions, }; return nativeFetch(options.url, requestOptions).then(response => ({ diff --git a/packages/browser/src/transports/types.ts b/packages/browser/src/transports/types.ts new file mode 100644 index 000000000000..4f4b7ef2c98a --- /dev/null +++ b/packages/browser/src/transports/types.ts @@ -0,0 +1,8 @@ +import { BaseTransportOptions } from '@sentry/types'; + +export interface BrowserTransportOptions extends BaseTransportOptions { + /** Fetch API init parameters. Used by the FetchTransport */ + fetchOptions?: RequestInit; + /** Custom headers for the transport. Used by the XHRTransport and FetchTransport */ + headers?: { [key: string]: string }; +} diff --git a/packages/browser/src/transports/xhr.ts b/packages/browser/src/transports/xhr.ts index 4c4982225b85..9a8b10c6187d 100644 --- a/packages/browser/src/transports/xhr.ts +++ b/packages/browser/src/transports/xhr.ts @@ -1,7 +1,9 @@ import { createTransport } from '@sentry/core'; -import { BaseTransportOptions, Transport, TransportMakeRequestResponse, TransportRequest } from '@sentry/types'; +import { Transport, TransportMakeRequestResponse, TransportRequest } from '@sentry/types'; import { SyncPromise } from '@sentry/utils'; +import { BrowserTransportOptions } from './types'; + /** * The DONE ready state for XmlHttpRequest * @@ -12,14 +14,10 @@ import { SyncPromise } from '@sentry/utils'; */ const XHR_READYSTATE_DONE = 4; -export interface XHRTransportOptions extends BaseTransportOptions { - headers?: { [key: string]: string }; -} - /** * Creates a Transport that uses the XMLHttpRequest API to send events to Sentry. */ -export function makeXHRTransport(options: XHRTransportOptions): Transport { +export function makeXHRTransport(options: BrowserTransportOptions): Transport { function makeRequest(request: TransportRequest): PromiseLike { return new SyncPromise((resolve, reject) => { const xhr = new XMLHttpRequest(); diff --git a/packages/browser/test/unit/transports/fetch.test.ts b/packages/browser/test/unit/transports/fetch.test.ts index 63fe62814628..4684b87fdf8b 100644 --- a/packages/browser/test/unit/transports/fetch.test.ts +++ b/packages/browser/test/unit/transports/fetch.test.ts @@ -1,10 +1,11 @@ import { EventEnvelope, EventItem } from '@sentry/types'; import { createEnvelope, serializeEnvelope } from '@sentry/utils'; -import { FetchTransportOptions, makeFetchTransport } from '../../../src/transports/fetch'; +import { makeFetchTransport } from '../../../src/transports/fetch'; +import { BrowserTransportOptions } from '../../../src/transports/types'; import { FetchImpl } from '../../../src/transports/utils'; -const DEFAULT_FETCH_TRANSPORT_OPTIONS: FetchTransportOptions = { +const DEFAULT_FETCH_TRANSPORT_OPTIONS: BrowserTransportOptions = { url: 'https://sentry.io/api/42/store/?sentry_key=123&sentry_version=7', recordDroppedEvent: () => undefined, }; @@ -83,7 +84,7 @@ describe('NewFetchTransport', () => { }; const transport = makeFetchTransport( - { ...DEFAULT_FETCH_TRANSPORT_OPTIONS, requestOptions: REQUEST_OPTIONS }, + { ...DEFAULT_FETCH_TRANSPORT_OPTIONS, fetchOptions: REQUEST_OPTIONS }, mockFetch, ); diff --git a/packages/browser/test/unit/transports/xhr.test.ts b/packages/browser/test/unit/transports/xhr.test.ts index 35ca842db3d1..15a5c7ce3d62 100644 --- a/packages/browser/test/unit/transports/xhr.test.ts +++ b/packages/browser/test/unit/transports/xhr.test.ts @@ -1,9 +1,10 @@ import { EventEnvelope, EventItem } from '@sentry/types'; import { createEnvelope, serializeEnvelope } from '@sentry/utils'; -import { makeXHRTransport, XHRTransportOptions } from '../../../src/transports/xhr'; +import { BrowserTransportOptions } from '../../../src/transports/types'; +import { makeXHRTransport } from '../../../src/transports/xhr'; -const DEFAULT_XHR_TRANSPORT_OPTIONS: XHRTransportOptions = { +const DEFAULT_XHR_TRANSPORT_OPTIONS: BrowserTransportOptions = { url: 'https://sentry.io/api/42/store/?sentry_key=123&sentry_version=7', recordDroppedEvent: () => undefined, }; @@ -82,7 +83,7 @@ describe('NewXHRTransport', () => { keepalive: 'true', referrer: 'http://example.org', }; - const options: XHRTransportOptions = { + const options: BrowserTransportOptions = { ...DEFAULT_XHR_TRANSPORT_OPTIONS, headers, }; From 94554b3f45da916e6750b3153b90c4a366119d5b Mon Sep 17 00:00:00 2001 From: Katie Byers Date: Tue, 10 May 2022 12:32:12 -0700 Subject: [PATCH 132/204] ref(nextjs): Use new build process in vercel script (#5065) This updates the script for testing an SDK branch in Vercel to use the new build process. It also updates the regex used to match version numbers when updating monorepo dependencies' `package.json` files, to accept alpha and beta releases. --- packages/nextjs/vercel/install-sentry-from-branch.sh | 11 +++-------- 1 file changed, 3 insertions(+), 8 deletions(-) diff --git a/packages/nextjs/vercel/install-sentry-from-branch.sh b/packages/nextjs/vercel/install-sentry-from-branch.sh index 1bb94b0cc833..84a0f59eb7c3 100644 --- a/packages/nextjs/vercel/install-sentry-from-branch.sh +++ b/packages/nextjs/vercel/install-sentry-from-branch.sh @@ -27,13 +27,8 @@ yarn --prod false echo " " echo "BUILDING SDK" -# Types are required for any type of build to succeed -yarn build:types -# We need to build es5 versions because `next.config.js` calls `require` on the SDK (to get `withSentryConfig`) and -# therefore it looks for `cjs/index.js` -yarn build:cjs -# We need to build esm versions because that's what `next` actually uses when it builds the app -yarn build:esm +# build types, cjs, and esm +yarn build:dev # Set all packages in the repo to point to their siblings as file dependencies. That way, when we install the local copy # of @sentry/nextjs, it'll pull the local copy of each of its @sentry/* dependents. This mimics what Lerna does with @@ -53,7 +48,7 @@ PACKAGE_NAMES=$(ls $PACKAGES_DIR) for package in ${PACKAGE_NAMES[@]}; do # Within a given package.json file, search for each of the other packages in turn, and if found, make the replacement for package_dep in ${PACKAGE_NAMES[@]}; do - sed -Ei /"@sentry\/${package_dep}"/s/"[0-9]+\.[0-9]+\.[0-9]+"/"file:${ESCAPED_PACKAGES_DIR}\/${package_dep}"/ ${PACKAGES_DIR}/${package}/package.json + sed -Ei /"@sentry\/${package_dep}"/s/"[0-9]+\.[0-9]+\.[0-9]+(-(alpha|beta)\.[0-9]+)?"/"file:${ESCAPED_PACKAGES_DIR}\/${package_dep}"/ ${PACKAGES_DIR}/${package}/package.json done done From f715933282fe446c26d6012aefdf775d75d241ca Mon Sep 17 00:00:00 2001 From: Katie Byers Date: Tue, 10 May 2022 12:46:15 -0700 Subject: [PATCH 133/204] ref(build): Use rollup/sucrase for non-watch npm builds (#5035) This switches our `build` and `build:dev` yarn scripts to use the new rollup/sucrase build process. This is the culmination of a number of previous changes, whose highlights include: - https://github.com/getsentry/sentry-javascript/pull/4724, which made building types files a separate build process - https://github.com/getsentry/sentry-javascript/pull/4895, which updated the SDK to use TS 3.8.3 - https://github.com/getsentry/sentry-javascript/pull/4926, which removed use of the `Severity` enum - https://github.com/getsentry/sentry-javascript/pull/5005, which switch our default tsconfig to target es6 - https://github.com/getsentry/sentry-javascript/pull/4992, which added the Sucrase plugin, some helper functions, and the `yarn build:rollup` script - https://github.com/getsentry/sentry-javascript/pull/4993, which added rollup plugins to use `var` rather than `const` and clean up the built code in various ways - https://github.com/getsentry/sentry-javascript/pull/5022, which applied the same `const`-to-`var` translation to tests - https://github.com/getsentry/sentry-javascript/pull/5023, which added the ability to change injected polyfills into imports The result is that, as of this PR, we will no longer use `tsc` to transpile or down-complile our code when building npm packages. Instead, we will be using Rollup to handle making our code CJS-friendlly and Sucrase to handle the transpilation from TS to JS. The main advantages of this change are: - It forced us to do a lot of updating, centralizing, and cleanup of our tooling, not just for build but also for testing and linting. - It brings our CDN build process and our npm build process much more in line with each other, for easier maintainability. - It gives us more control over the eventual output, because we have access to a whole internet full of Rollup plugins (not to mention the ability to make our own), rather than being constrained to tsconfig options. (Plugins also allow us to interact with the code directly.) - It speeds up our builds fairly significantly. I ran a number of trials in GHA of running `yarn build:dev` at the top level of the repo. Before this change, the average time was ~150 seconds. After this change, it's about half that, roughly 75 seconds. Because of the switch in tooling, the code we publish is going to be slightly different. In order to make sure that those differences weren't going to be breaking, I built each package under the old system and under the new system, ran a `git diff`, and checked every file, both CJS and ESM, in every package affected by this change. The differences (none of which affect behavior or eventual bundle size by more than a few bytes in each direction), fell into a few categories: - Purely cosmetic changes, things like which comments are retained, the order of imports, where in the file exports live, etc. - Changes to class constructors, things like not explicitly assigning `undefined` to undefined attributes, using regular assignment rather than `Object.defineProperty` for attributes which are assigned values, and splitting some of those assignments off into helper functions. - Changes related to the upgrade to ES6 and dropping of support for Node 6, things like not polyfilling object spread or async/await While this represents the most significant part of the overall change, a few outstanding tasks remain: - Making this same switch in `build:watch` - Parallelizing the builds, both locally and in CI - Perhaps applying the new process to our CDN bundle builds - Generalized cleanup These will all be included in separate PRs, some in the immediate future and some in the hopefully-not-too-distant short-to-medium term. --- .github/workflows/build.yml | 277 ----------------------------- packages/browser/package.json | 4 +- packages/core/package.json | 2 +- packages/gatsby/package.json | 2 +- packages/hub/package.json | 2 +- packages/integrations/package.json | 4 +- packages/nextjs/package.json | 2 +- packages/node/package.json | 2 +- packages/react/package.json | 2 +- packages/serverless/package.json | 4 +- packages/tracing/package.json | 4 +- packages/types/package.json | 2 +- packages/utils/package.json | 2 +- packages/vue/package.json | 4 +- packages/wasm/package.json | 4 +- rollup/npmHelpers.js | 18 +- scripts/sucrase-test-hack.ts | 49 ----- 17 files changed, 23 insertions(+), 361 deletions(-) delete mode 100644 scripts/sucrase-test-hack.ts diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 1e88cd491876..155126752160 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -256,8 +256,6 @@ jobs: NODE_VERSION: ${{ matrix.node }} run: | [[ $NODE_VERSION == 8 ]] && yarn add --dev --ignore-engines --ignore-scripts --ignore-workspace-root-check ts-node@8.x - # TODO remove this when we switch to sucrase - yarn ts-node scripts/sucrase-test-hack.ts yarn test-ci - name: Compute test coverage uses: codecov/codecov-action@v1 @@ -494,278 +492,3 @@ jobs: run: | cd packages/node-integration-tests yarn test - - job_unit_test_sucrase: - name: Sucrase Test (Node ${{ matrix.node }}) - needs: job_build - continue-on-error: true - timeout-minutes: 30 - runs-on: ubuntu-latest - strategy: - matrix: - node: [8, 10, 12, 14, 16] - steps: - - name: Check out current commit (${{ env.HEAD_COMMIT }}) - uses: actions/checkout@v2 - with: - ref: ${{ env.HEAD_COMMIT }} - - name: Set up Node - uses: actions/setup-node@v1 - with: - node-version: ${{ matrix.node }} - - name: Check dependency cache - uses: actions/cache@v2 - with: - path: ${{ env.CACHED_DEPENDENCY_PATHS }} - key: ${{ needs.job_build.outputs.dependency_cache_key }} - - name: Check build cache - uses: actions/cache@v2 - with: - path: ${{ env.CACHED_BUILD_PATHS }} - key: ${{ env.BUILD_CACHE_KEY }} - - name: Run tests - env: - NODE_VERSION: ${{ matrix.node }} - SUCRASE: true - run: | - [[ $NODE_VERSION == 8 ]] && yarn add --dev --ignore-engines --ignore-scripts --ignore-workspace-root-check ts-node@8.x rollup@2.2.0 @rollup/plugin-node-resolve@10.x rollup-plugin-terser@5.0.0 rollup-plugin-typescript2@0.30.0 - yarn ts-node scripts/sucrase-test-hack.ts - yarn test-ci - - name: Compute test coverage - uses: codecov/codecov-action@v1 - - job_nextjs_integration_test_sucrase: - name: Sucrase Test @sentry/nextjs on (Node ${{ matrix.node }}) - needs: job_build - continue-on-error: true - timeout-minutes: 30 - runs-on: ubuntu-latest - strategy: - matrix: - node: [10, 12, 14, 16] - steps: - - name: Check out current commit (${{ env.HEAD_COMMIT }}) - uses: actions/checkout@v2 - with: - ref: ${{ env.HEAD_COMMIT }} - - name: Set up Node - uses: actions/setup-node@v1 - with: - node-version: ${{ matrix.node }} - - name: Check dependency cache - uses: actions/cache@v2 - with: - path: ${{ env.CACHED_DEPENDENCY_PATHS }} - key: ${{ needs.job_build.outputs.dependency_cache_key }} - - name: Check build cache - uses: actions/cache@v2 - with: - path: ${{ env.CACHED_BUILD_PATHS }} - key: ${{ env.BUILD_CACHE_KEY }} - - name: Run tests - env: - NODE_VERSION: ${{ matrix.node }} - run: | - yarn ts-node scripts/sucrase-test-hack.ts - cd packages/nextjs - yarn test:integration - - # Ember tests are separate from the rest because they are the slowest part of the test suite, and making them a - # separate job allows them to run in parallel with the other tests. - job_ember_tests_sucrase: - name: Sucrase Test @sentry/ember - needs: job_build - continue-on-error: true - timeout-minutes: 30 - runs-on: ubuntu-latest - steps: - - name: Check out current commit (${{ env.HEAD_COMMIT }}) - uses: actions/checkout@v2 - with: - ref: ${{ env.HEAD_COMMIT }} - # TODO: removing `fetch-depth` below seems to have no effect, and the commit which added it had no description, - # so it's not clear why it's necessary. That said, right now ember tests are xfail, so it's a little hard to - # tell if it's safe to remove. Once ember tests are fixed, let's try again with it turned off, and if all goes - # well, we can pull it out. - fetch-depth: 0 - - name: Set up Node - uses: actions/setup-node@v1 - with: - # The only danger with Ember in terms of Node versions is that the build tool, ember-cli, won't work if Node - # is too old. Since Oct 2019, Node 10 has been the oldest version supported by ember-cli, so test against - # that. If it passes, newer versions of Node should also be fine. This saves us from having to run the Ember - # tests in our Node matrix above. - node-version: '10' - - name: Check dependency cache - uses: actions/cache@v2 - with: - path: ${{ env.CACHED_DEPENDENCY_PATHS }} - key: ${{ needs.job_build.outputs.dependency_cache_key }} - - name: Check build cache - uses: actions/cache@v2 - with: - path: ${{ env.CACHED_BUILD_PATHS }} - key: ${{ env.BUILD_CACHE_KEY }} - - name: Run Ember tests - run: | - yarn ts-node scripts/sucrase-test-hack.ts - yarn test --scope=@sentry/ember - - name: Compute test coverage - uses: codecov/codecov-action@v1 - - job_browser_playwright_tests_sucrase: - name: Sucrase Playwright - ${{ (matrix.tracing_only && 'Browser + Tracing') || 'Browser' }} (${{ matrix.bundle }}) - needs: job_build - runs-on: ubuntu-latest - strategy: - matrix: - bundle: - - esm - - cjs - tracing_only: - - true - - false - exclude: - # `tracing_only` only makes a difference for bundles - tests of the esm and cjs builds always include the - # tracing tests - - bundle: esm - tracing_only: false - - bundle: cjs - tracing_only: false - steps: - - name: Check out current commit (${{ env.HEAD_COMMIT }}) - uses: actions/checkout@v2 - with: - ref: ${{ env.HEAD_COMMIT }} - - name: Set up Node - uses: actions/setup-node@v1 - with: - node-version: '16' - - name: Check dependency cache - uses: actions/cache@v2 - with: - path: ${{ env.CACHED_DEPENDENCY_PATHS }} - key: ${{ needs.job_build.outputs.dependency_cache_key }} - - name: Check build cache - uses: actions/cache@v2 - with: - path: ${{ env.CACHED_BUILD_PATHS }} - key: ${{ env.BUILD_CACHE_KEY }} - - name: Run Playwright tests - env: - PW_BUNDLE: ${{ matrix.bundle }} - PW_TRACING_ONLY: ${{ matrix.tracing_only }} - run: | - yarn ts-node scripts/sucrase-test-hack.ts - cd packages/integration-tests - yarn run playwright install-deps webkit - yarn test:ci - - job_browser_integration_tests_sucrase: - name: Sucrase Old Browser Integration Tests (${{ matrix.browser }}) - needs: job_build - runs-on: ubuntu-latest - timeout-minutes: 10 - continue-on-error: true - strategy: - matrix: - browser: - - ChromeHeadless - - FirefoxHeadless - - WebkitHeadless - steps: - - name: Check out current commit (${{ env.HEAD_COMMIT }}) - uses: actions/checkout@v2 - with: - ref: ${{ env.HEAD_COMMIT }} - - name: Set up Node - uses: actions/setup-node@v1 - - name: Check dependency cache - uses: actions/cache@v2 - with: - path: ${{ env.CACHED_DEPENDENCY_PATHS }} - key: ${{ needs.job_build.outputs.dependency_cache_key }} - - name: Check build cache - uses: actions/cache@v2 - with: - path: ${{ env.CACHED_BUILD_PATHS }} - key: ${{ env.BUILD_CACHE_KEY }} - - name: Run integration tests - env: - KARMA_BROWSER: ${{ matrix.browser }} - run: | - yarn ts-node scripts/sucrase-test-hack.ts - cd packages/browser - [[ $KARMA_BROWSER == WebkitHeadless ]] && yarn run playwright install-deps webkit - yarn test:integration - - job_browser_build_tests_sucrase: - name: Sucrase Browser Build Tests - needs: job_build - runs-on: ubuntu-latest - timeout-minutes: 5 - continue-on-error: true - steps: - - name: Check out current commit (${ env.HEAD_COMMIT }}) - uses: actions/checkout@v2 - with: - ref: ${{ env.HEAD_COMMIT }} - - name: Set up Node - uses: actions/setup-node@v1 - with: - node-version: '16' - - name: Check dependency cache - uses: actions/cache@v2 - with: - path: ${{ env.CACHED_DEPENDENCY_PATHS }} - key: ${{ needs.job_build.outputs.dependency_cache_key }} - - name: Check build cache - uses: actions/cache@v2 - with: - path: ${{ env.CACHED_BUILD_PATHS }} - key: ${{ env.BUILD_CACHE_KEY }} - - name: Run browser build tests - run: | - yarn ts-node scripts/sucrase-test-hack.ts - cd packages/browser - yarn test:package - - name: Run utils build tests - run: | - cd packages/utils - yarn test:package - - job_node_integration_tests_sucrase: - name: Sucrase Node SDK Integration Tests (${{ matrix.node }}) - needs: job_build - runs-on: ubuntu-latest - timeout-minutes: 10 - continue-on-error: true - strategy: - matrix: - node: [10, 12, 14, 16] - steps: - - name: Check out current commit (${{ github.sha }}) - uses: actions/checkout@v2 - with: - ref: ${{ env.HEAD_COMMIT }} - - name: Set up Node - uses: actions/setup-node@v1 - with: - node-version: ${{ matrix.node }} - - name: Check dependency cache - uses: actions/cache@v2 - with: - path: ${{ env.CACHED_DEPENDENCY_PATHS }} - key: ${{ needs.job_build.outputs.dependency_cache_key }} - - name: Check build cache - uses: actions/cache@v2 - with: - path: ${{ env.CACHED_BUILD_PATHS }} - key: ${{ env.BUILD_CACHE_KEY }} - - name: Run integration tests - env: - NODE_VERSION: ${{ matrix.node }} - run: | - yarn ts-node scripts/sucrase-test-hack.ts - cd packages/node-integration-tests - yarn test diff --git a/packages/browser/package.json b/packages/browser/package.json index 9ca3ad829d88..90a1287f0a9c 100644 --- a/packages/browser/package.json +++ b/packages/browser/package.json @@ -43,10 +43,10 @@ "webpack": "^4.30.0" }, "scripts": { - "build": "run-p build:cjs build:esm build:bundle build:types", + "build": "run-p build:rollup build:bundle build:types", "build:bundle": "rollup --config rollup.bundle.config.js", "build:cjs": "tsc -p tsconfig.cjs.json", - "build:dev": "run-p build:cjs build:esm build:types", + "build:dev": "run-p build:rollup build:types", "build:es5": "yarn build:cjs # *** backwards compatibility - remove in v7 ***", "build:esm": "tsc -p tsconfig.esm.json", "build:rollup": "rollup -c rollup.npm.config.js", diff --git a/packages/core/package.json b/packages/core/package.json index 104ca8c24a13..be4629490380 100644 --- a/packages/core/package.json +++ b/packages/core/package.json @@ -22,7 +22,7 @@ "tslib": "^1.9.3" }, "scripts": { - "build": "run-p build:cjs build:esm build:types", + "build": "run-p build:rollup build:types", "build:cjs": "tsc -p tsconfig.cjs.json", "build:dev": "run-s build", "build:es5": "yarn build:cjs # *** backwards compatibility - remove in v7 ***", diff --git a/packages/gatsby/package.json b/packages/gatsby/package.json index 504b34015793..0f727e8470df 100644 --- a/packages/gatsby/package.json +++ b/packages/gatsby/package.json @@ -35,7 +35,7 @@ "react": "^18.0.0" }, "scripts": { - "build": "run-p build:cjs build:esm build:types build:plugin", + "build": "run-p build:rollup build:types build:plugin", "build:cjs": "tsc -p tsconfig.cjs.json", "build:dev": "run-s build", "build:es5": "yarn build:cjs # *** backwards compatibility - remove in v7 ***", diff --git a/packages/hub/package.json b/packages/hub/package.json index 7903d295158d..7c86c6e8a223 100644 --- a/packages/hub/package.json +++ b/packages/hub/package.json @@ -21,7 +21,7 @@ "tslib": "^1.9.3" }, "scripts": { - "build": "run-p build:cjs build:esm build:types", + "build": "run-p build:rollup build:types", "build:cjs": "tsc -p tsconfig.cjs.json", "build:dev": "run-s build", "build:es5": "yarn build:cjs # *** backwards compatibility - remove in v7 ***", diff --git a/packages/integrations/package.json b/packages/integrations/package.json index 9c012e616cbe..95c179312a95 100644 --- a/packages/integrations/package.json +++ b/packages/integrations/package.json @@ -25,10 +25,10 @@ "chai": "^4.1.2" }, "scripts": { - "build": "run-p build:cjs build:esm build:types build:bundle", + "build": "run-p build:rollup build:types build:bundle", "build:bundle": "bash scripts/buildBundles.sh", "build:cjs": "tsc -p tsconfig.cjs.json", - "build:dev": "run-p build:cjs build:esm build:types", + "build:dev": "run-p build:rollup build:types", "build:es5": "yarn build:cjs # *** backwards compatibility - remove in v7 ***", "build:esm": "tsc -p tsconfig.esm.json", "build:rollup": "rollup -c rollup.npm.config.js", diff --git a/packages/nextjs/package.json b/packages/nextjs/package.json index 266239365938..afc12c499a49 100644 --- a/packages/nextjs/package.json +++ b/packages/nextjs/package.json @@ -43,7 +43,7 @@ } }, "scripts": { - "build": "run-p build:cjs build:esm build:types", + "build": "run-p build:rollup build:types", "build:cjs": "tsc -p tsconfig.cjs.json", "build:dev": "run-s build", "build:es5": "yarn build:cjs # *** backwards compatibility - remove in v7 ***", diff --git a/packages/node/package.json b/packages/node/package.json index a1d82575277a..bf2bd6ffbad8 100644 --- a/packages/node/package.json +++ b/packages/node/package.json @@ -34,7 +34,7 @@ "nock": "^13.0.5" }, "scripts": { - "build": "run-p build:cjs build:esm build:types", + "build": "run-p build:rollup build:types", "build:cjs": "tsc -p tsconfig.cjs.json", "build:dev": "run-s build", "build:es5": "yarn build:cjs # *** backwards compatibility - remove in v7 ***", diff --git a/packages/react/package.json b/packages/react/package.json index 03f1873aa436..4c4c15444d67 100644 --- a/packages/react/package.json +++ b/packages/react/package.json @@ -47,7 +47,7 @@ "redux": "^4.0.5" }, "scripts": { - "build": "run-p build:cjs build:esm build:types", + "build": "run-p build:rollup build:types", "build:cjs": "tsc -p tsconfig.cjs.json", "build:dev": "run-s build", "build:es5": "yarn build:cjs # *** backwards compatibility - remove in v7 ***", diff --git a/packages/serverless/package.json b/packages/serverless/package.json index ab2c00d479dc..f306491323fb 100644 --- a/packages/serverless/package.json +++ b/packages/serverless/package.json @@ -38,10 +38,10 @@ "read-pkg": "^5.2.0" }, "scripts": { - "build": "run-p build:cjs build:esm build:types && yarn build:awslambda-layer", + "build": "run-p build:rollup build:types && yarn build:awslambda-layer", "build:awslambda-layer": "node scripts/build-awslambda-layer.js", "build:cjs": "tsc -p tsconfig.cjs.json", - "build:dev": "run-p build:cjs build:esm build:types", + "build:dev": "run-p build:rollup build:types", "build:es5": "yarn build:cjs # *** backwards compatibility - remove in v7 ***", "build:esm": "tsc -p tsconfig.esm.json", "build:rollup": "rollup -c rollup.npm.config.js", diff --git a/packages/tracing/package.json b/packages/tracing/package.json index c5098efa4880..7b6c2f81a226 100644 --- a/packages/tracing/package.json +++ b/packages/tracing/package.json @@ -26,10 +26,10 @@ "@types/express": "^4.17.1" }, "scripts": { - "build": "run-p build:cjs build:esm build:types build:bundle && ts-node ../../scripts/prepack.ts --bundles #necessary for integration tests", + "build": "run-p build:rollup build:types build:bundle && ts-node ../../scripts/prepack.ts --bundles #necessary for integration tests", "build:bundle": "rollup --config rollup.bundle.config.js", "build:cjs": "tsc -p tsconfig.cjs.json", - "build:dev": "run-p build:cjs build:esm build:types", + "build:dev": "run-p build:rollup build:types", "build:es5": "yarn build:cjs # *** backwards compatibility - remove in v7 ***", "build:esm": "tsc -p tsconfig.esm.json", "build:rollup": "rollup -c rollup.npm.config.js", diff --git a/packages/types/package.json b/packages/types/package.json index 823868e048fd..e18e95165281 100644 --- a/packages/types/package.json +++ b/packages/types/package.json @@ -16,7 +16,7 @@ "access": "public" }, "scripts": { - "build": "run-p build:cjs build:esm build:types", + "build": "run-p build:rollup build:types", "build:cjs": "tsc -p tsconfig.cjs.json", "build:dev": "run-s build", "build:es5": "yarn build:cjs # *** backwards compatibility - remove in v7 ***", diff --git a/packages/utils/package.json b/packages/utils/package.json index 94e477d16fbd..f57d714f0ffb 100644 --- a/packages/utils/package.json +++ b/packages/utils/package.json @@ -25,7 +25,7 @@ "chai": "^4.1.2" }, "scripts": { - "build": "run-p build:cjs build:esm build:types", + "build": "run-p build:rollup build:types", "build:cjs": "tsc -p tsconfig.cjs.json", "build:dev": "run-s build", "build:es5": "yarn build:cjs # *** backwards compatibility - remove in v7 ***", diff --git a/packages/vue/package.json b/packages/vue/package.json index a29eb014518a..c29002fd3b70 100644 --- a/packages/vue/package.json +++ b/packages/vue/package.json @@ -26,10 +26,10 @@ "vue": "2.x || 3.x" }, "scripts": { - "build": "run-p build:cjs build:esm build:types", + "build": "run-p build:rollup build:types", "build:bundle": "rollup --config rollup.bundle.config.js", "build:cjs": "tsc -p tsconfig.cjs.json", - "build:dev": "run-p build:cjs build:esm build:types", + "build:dev": "run-p build:rollup build:types", "build:es5": "yarn build:cjs # *** backwards compatibility - remove in v7 ***", "build:esm": "tsc -p tsconfig.esm.json", "build:rollup": "rollup -c rollup.npm.config.js", diff --git a/packages/wasm/package.json b/packages/wasm/package.json index 0dfffd50df12..10f1b9570fed 100644 --- a/packages/wasm/package.json +++ b/packages/wasm/package.json @@ -30,10 +30,10 @@ "puppeteer": "^5.5.0" }, "scripts": { - "build": "run-p build:cjs build:esm build:bundle build:types", + "build": "run-p build:rollup build:bundle build:types", "build:bundle": "rollup --config rollup.bundle.config.js", "build:cjs": "tsc -p tsconfig.cjs.json", - "build:dev": "run-p build:cjs build:esm build:types", + "build:dev": "run-p build:rollup build:types", "build:es5": "yarn build:cjs # *** backwards compatibility - remove in v7 ***", "build:esm": "tsc -p tsconfig.esm.json", "build:rollup": "rollup -c rollup.npm.config.js", diff --git a/rollup/npmHelpers.js b/rollup/npmHelpers.js index 0ac0ad33a01b..40d9499f75a7 100644 --- a/rollup/npmHelpers.js +++ b/rollup/npmHelpers.js @@ -33,8 +33,7 @@ export function makeBaseNPMConfig(options = {}) { const removeBlankLinesPlugin = makeRemoveBlankLinesPlugin(); const extractPolyfillsPlugin = makeExtractPolyfillsPlugin(); - // return { - const config = { + return { input: entrypoints, output: { @@ -44,10 +43,10 @@ export function makeBaseNPMConfig(options = {}) { sourcemap: true, // output individual files rather than one big bundle - // preserveModules: true, + preserveModules: true, // any wrappers or helper functions generated by rollup can use ES6 features - // generatedCode: 'es2015', + generatedCode: 'es2015', // don't add `"use strict"` to the top of cjs files strict: false, @@ -101,17 +100,6 @@ export function makeBaseNPMConfig(options = {}) { // treeshake: 'smallest', treeshake: false, }; - - // temporary hack for testing sucrase bundles before we switch over - if (!process.version.startsWith('v8')) { - console.log('Doing normal preserveModules'); - config.output.preserveModules = true; - } else { - console.log('Doing node 8 preserveModules'); - config.preserveModules = true; - } - - return config; } export function makeNPMConfigVariants(baseConfig) { diff --git a/scripts/sucrase-test-hack.ts b/scripts/sucrase-test-hack.ts deleted file mode 100644 index e697145abbdb..000000000000 --- a/scripts/sucrase-test-hack.ts +++ /dev/null @@ -1,49 +0,0 @@ -// A temporary hack to use sucrase versions of packages for testing in CI. - -import * as childProcess from 'child_process'; -import * as fs from 'fs'; -import * as path from 'path'; - -function run(cmd: string, options?: childProcess.ExecSyncOptions): unknown { - return childProcess.execSync(cmd, { stdio: 'inherit', ...options }); -} - -const ignorePackages = process.version.startsWith('v8') - ? [ - '@sentry/ember', - '@sentry-internal/eslint-plugin-sdk', - '@sentry/react', - '@sentry/wasm', - '@sentry/gatsby', - '@sentry/serverless', - '@sentry/nextjs', - '@sentry/angular', - ] - : ['@sentry/serverless']; - -// clear current builds and rebuild with rollup/sucrase (this way, all of the extra, random stuff which gets built in -// the main build job remains, and only the part affected by this project gets overwritten) -if (process.env.SUCRASE) { - // just to be super sure - fs.readdirSync(path.join(process.cwd(), 'packages')).forEach(dir => { - if (fs.existsSync(path.join(process.cwd(), 'packages', dir, 'build', 'npm'))) { - run(`rm -rf packages/${dir}/build/npm/cjs`); - run(`rm -rf packages/${dir}/build/npm/esm`); - } else if (fs.existsSync(path.join(process.cwd(), 'packages', dir, 'build', 'cjs'))) { - run(`rm -rf packages/${dir}/build/cjs`); - run(`rm -rf packages/${dir}/build/esm`); - } - }); - - // rebuild the packages we're going to test with rollup/sucrase - run(`yarn build:rollup ${ignorePackages.map(dep => `--ignore="${dep}"`).join(' ')}`); -} -// if we're in tsc-land, rebuild using es5 - temporary until switch to sucrase -else { - const baseTSConfigPath = 'packages/typescript/tsconfig.json'; - fs.writeFileSync( - baseTSConfigPath, - String(fs.readFileSync(baseTSConfigPath)).replace('"target": "es6"', '"target": "es5"'), - ); - run(`yarn build:dev ${ignorePackages.map(dep => `--ignore="${dep}"`).join(' ')}`); -} From 529650c8ec3efb8bb958e2b0a78a476f15baed6b Mon Sep 17 00:00:00 2001 From: Katie Byers Date: Tue, 10 May 2022 14:36:36 -0700 Subject: [PATCH 134/204] chore(build): Handle npm tarballs (#5067) Running `yarn build:npm` at the root level of the repo (which one might do while testing out build changes) creates a tarball in every package directory. This adds them to `.gitignore` and removes them when `yarn clean` is run. --- .gitignore | 2 ++ packages/angular/package.json | 2 +- packages/browser/package.json | 2 +- packages/core/package.json | 2 +- packages/ember/package.json | 1 + packages/eslint-config-sdk/package.json | 1 + packages/eslint-plugin-sdk/package.json | 1 + packages/gatsby/package.json | 2 +- packages/hub/package.json | 2 +- packages/integrations/package.json | 2 +- packages/nextjs/package.json | 2 +- packages/node/package.json | 2 +- packages/react/package.json | 2 +- packages/serverless/package.json | 2 +- packages/tracing/package.json | 2 +- packages/types/package.json | 2 +- packages/typescript/package.json | 1 + packages/utils/package.json | 2 +- packages/vue/package.json | 2 +- packages/wasm/package.json | 2 +- 20 files changed, 21 insertions(+), 15 deletions(-) diff --git a/.gitignore b/.gitignore index 430a977178a7..be80e41d00b7 100644 --- a/.gitignore +++ b/.gitignore @@ -14,6 +14,8 @@ scratch/ scenarios/*/dist/ # transpiled transformers jest/transformers/*.js +# node tarballs +packages/*/sentry-*.tgz # logs yarn-error.log diff --git a/packages/angular/package.json b/packages/angular/package.json index 3a14bd2e2d80..941506e5871a 100644 --- a/packages/angular/package.json +++ b/packages/angular/package.json @@ -45,7 +45,7 @@ "build:ngc:watch": "ng build --prod --watch", "build:npm": "npm pack ./build", "circularDepCheck": "madge --circular src/index.ts", - "clean": "rimraf build coverage", + "clean": "rimraf build coverage sentry-angular-*.tgz", "fix": "run-s fix:eslint fix:prettier", "fix:eslint": "eslint . --format stylish --fix", "fix:prettier": "prettier --write \"{src,test,scripts}/**/*.ts\"", diff --git a/packages/browser/package.json b/packages/browser/package.json index 90a1287f0a9c..a34fcf820edd 100644 --- a/packages/browser/package.json +++ b/packages/browser/package.json @@ -61,7 +61,7 @@ "build:types:watch": "tsc -p tsconfig.types.json --watch", "build:npm": "ts-node ../../scripts/prepack.ts --bundles && npm pack ./build/npm", "circularDepCheck": "madge --circular src/index.ts", - "clean": "rimraf build coverage .rpt2_cache", + "clean": "rimraf build coverage .rpt2_cache sentry-browser-*.tgz", "fix": "run-s fix:eslint fix:prettier", "fix:eslint": "eslint . --format stylish --fix", "fix:prettier": "prettier --write \"{src,test,scripts}/**/*.ts\"", diff --git a/packages/core/package.json b/packages/core/package.json index be4629490380..862538bed0a7 100644 --- a/packages/core/package.json +++ b/packages/core/package.json @@ -38,7 +38,7 @@ "build:types:watch": "tsc -p tsconfig.types.json --watch", "build:npm": "ts-node ../../scripts/prepack.ts && npm pack ./build", "circularDepCheck": "madge --circular src/index.ts", - "clean": "rimraf build coverage", + "clean": "rimraf build coverage sentry-core-*.tgz", "fix": "run-s fix:eslint fix:prettier", "fix:eslint": "eslint . --format stylish --fix", "fix:prettier": "prettier --write \"{src,test,scripts}/**/*.ts\"", diff --git a/packages/ember/package.json b/packages/ember/package.json index 1646f9155582..6f4d1457aeb6 100644 --- a/packages/ember/package.json +++ b/packages/ember/package.json @@ -19,6 +19,7 @@ "scripts": { "build": "ember build --environment=production", "build:npm": "ember ts:precompile && npm pack && ember ts:clean", + "clean": "yarn rimraf sentry-ember-*.tgz", "link:yarn": "yarn link", "lint": "npm-run-all --aggregate-output --continue-on-error --parallel lint:*", "lint:hbs": "ember-template-lint .", diff --git a/packages/eslint-config-sdk/package.json b/packages/eslint-config-sdk/package.json index ab7abdd27992..d30b4614ce23 100644 --- a/packages/eslint-config-sdk/package.json +++ b/packages/eslint-config-sdk/package.json @@ -36,6 +36,7 @@ "eslint": "7.32.0" }, "scripts": { + "clean": "yarn rimraf sentry-internal-eslint-config-sdk-*.tgz", "link:yarn": "yarn link", "lint": "prettier --check \"**/*.js\"", "fix": "prettier --write \"**/*.js\"", diff --git a/packages/eslint-plugin-sdk/package.json b/packages/eslint-plugin-sdk/package.json index 8f705729c304..4609b0399fea 100644 --- a/packages/eslint-plugin-sdk/package.json +++ b/packages/eslint-plugin-sdk/package.json @@ -25,6 +25,7 @@ "mocha": "^6.2.0" }, "scripts": { + "clean": "yarn rimraf sentry-internal-eslint-plugin-sdk-*.tgz", "link:yarn": "yarn link", "fix": "run-s fix:eslint fix:prettier", "fix:eslint": "eslint . --format stylish --fix", diff --git a/packages/gatsby/package.json b/packages/gatsby/package.json index 0f727e8470df..2b24faf69698 100644 --- a/packages/gatsby/package.json +++ b/packages/gatsby/package.json @@ -52,7 +52,7 @@ "build:types:watch": "tsc -p tsconfig.types.json --watch", "build:npm": "ts-node ../../scripts/prepack.ts && npm pack ./build", "circularDepCheck": "madge --circular src/index.ts", - "clean": "rimraf build coverage *.d.ts", + "clean": "rimraf build coverage *.d.ts sentry-gatsby-*.tgz", "fix": "run-s fix:eslint fix:prettier", "fix:eslint": "eslint . --format stylish --fix", "fix:prettier": "prettier --write \"{src,test,scripts}/**/*.ts\"", diff --git a/packages/hub/package.json b/packages/hub/package.json index 7c86c6e8a223..6c8cc51ef2ad 100644 --- a/packages/hub/package.json +++ b/packages/hub/package.json @@ -37,7 +37,7 @@ "build:types:watch": "tsc -p tsconfig.types.json --watch", "build:npm": "ts-node ../../scripts/prepack.ts && npm pack ./build", "circularDepCheck": "madge --circular src/index.ts", - "clean": "rimraf build coverage", + "clean": "rimraf build coverage sentry-hub-*.tgz", "fix": "run-s fix:eslint fix:prettier", "fix:eslint": "eslint . --format stylish --fix", "fix:prettier": "prettier --write \"{src,test,scripts}/**/*.ts\"", diff --git a/packages/integrations/package.json b/packages/integrations/package.json index 95c179312a95..c1d1c1829c87 100644 --- a/packages/integrations/package.json +++ b/packages/integrations/package.json @@ -42,7 +42,7 @@ "build:types:watch": "tsc -p tsconfig.types.json --watch", "build:npm": "ts-node ../../scripts/prepack.ts --bundles && npm pack ./build/npm", "circularDepCheck": "madge --circular src/index.ts", - "clean": "rimraf build coverage .rpt2_cache", + "clean": "rimraf build coverage .rpt2_cache sentry-integrations-*.tgz", "fix": "run-s fix:eslint fix:prettier", "fix:eslint": "eslint . --format stylish --fix", "fix:prettier": "prettier --write \"{src,test,scripts}/**/*.ts\"", diff --git a/packages/nextjs/package.json b/packages/nextjs/package.json index afc12c499a49..ea447fc7bd5b 100644 --- a/packages/nextjs/package.json +++ b/packages/nextjs/package.json @@ -59,7 +59,7 @@ "build:types:watch": "tsc -p tsconfig.types.json --watch", "build:npm": "ts-node ../../scripts/prepack.ts && npm pack ./build", "circularDepCheck": "madge --circular src/index.client.ts && madge --circular --exclude 'config/types\\.ts' src/index.server.ts # see https://github.com/pahen/madge/issues/306", - "clean": "rimraf build coverage", + "clean": "rimraf build coverage sentry-nextjs-*.tgz", "fix": "run-s fix:eslint fix:prettier", "fix:eslint": "eslint . --format stylish --fix", "fix:prettier": "prettier --write \"{src,test,scripts}/**/*.ts\"", diff --git a/packages/node/package.json b/packages/node/package.json index bf2bd6ffbad8..750a63232996 100644 --- a/packages/node/package.json +++ b/packages/node/package.json @@ -50,7 +50,7 @@ "build:types:watch": "tsc -p tsconfig.types.json --watch", "build:npm": "ts-node ../../scripts/prepack.ts && npm pack ./build", "circularDepCheck": "madge --circular src/index.ts", - "clean": "rimraf build coverage", + "clean": "rimraf build coverage sentry-node-*.tgz", "fix": "run-s fix:eslint fix:prettier", "fix:eslint": "eslint . --format stylish --fix", "fix:prettier": "prettier --write \"{src,test,scripts}/**/*.ts\"", diff --git a/packages/react/package.json b/packages/react/package.json index 4c4c15444d67..78d3ffc7b606 100644 --- a/packages/react/package.json +++ b/packages/react/package.json @@ -63,7 +63,7 @@ "build:types:watch": "tsc -p tsconfig.types.json --watch", "build:npm": "ts-node ../../scripts/prepack.ts && npm pack ./build", "circularDepCheck": "madge --circular src/index.ts", - "clean": "rimraf build coverage", + "clean": "rimraf build coverage sentry-react-*.tgz", "fix": "run-s fix:eslint fix:prettier", "fix:eslint": "eslint . --format stylish --fix", "fix:prettier": "prettier --write \"{src,test,scripts}/**/*.ts\"", diff --git a/packages/serverless/package.json b/packages/serverless/package.json index f306491323fb..330becc81897 100644 --- a/packages/serverless/package.json +++ b/packages/serverless/package.json @@ -55,7 +55,7 @@ "build:types:watch": "tsc -p tsconfig.types.json --watch", "build:npm": "ts-node ../../scripts/prepack.ts && npm pack ./build", "circularDepCheck": "madge --circular src/index.ts", - "clean": "rimraf build dist-awslambda-layer coverage", + "clean": "rimraf build dist-awslambda-layer coverage sentry-serverless-*.tgz", "fix": "run-s fix:eslint fix:prettier", "fix:eslint": "eslint . --format stylish --fix", "fix:prettier": "prettier --write \"{src,test,scripts}/**/*.ts\"", diff --git a/packages/tracing/package.json b/packages/tracing/package.json index 7b6c2f81a226..dd1d56d5d895 100644 --- a/packages/tracing/package.json +++ b/packages/tracing/package.json @@ -43,7 +43,7 @@ "build:rollup:watch": "rollup -c rollup.npm.config.js --watch", "build:types:watch": "tsc -p tsconfig.types.json --watch", "build:npm": "ts-node ../../scripts/prepack.ts --bundles && npm pack ./build/npm", - "clean": "rimraf build coverage", + "clean": "rimraf build coverage sentry-tracing-*.tgz", "circularDepCheck": "madge --circular src/index.ts", "fix": "run-s fix:eslint fix:prettier", "fix:eslint": "eslint . --format stylish --fix", diff --git a/packages/types/package.json b/packages/types/package.json index e18e95165281..c4114bb958a4 100644 --- a/packages/types/package.json +++ b/packages/types/package.json @@ -31,7 +31,7 @@ "build:rollup:watch": "rollup -c rollup.npm.config.js --watch", "build:types:watch": "tsc -p tsconfig.types.json --watch", "build:npm": "ts-node ../../scripts/prepack.ts && npm pack ./build", - "clean": "rimraf build", + "clean": "rimraf build sentry-types-*.tgz", "link:yarn": "yarn link", "lint": "run-s lint:prettier lint:eslint", "lint:eslint": "eslint . --cache --cache-location '../../eslintcache/' --format stylish", diff --git a/packages/typescript/package.json b/packages/typescript/package.json index 6c1fbeef3229..c540c8d2ff48 100644 --- a/packages/typescript/package.json +++ b/packages/typescript/package.json @@ -14,6 +14,7 @@ "typescript": "3.8.3" }, "scripts": { + "clean": "yarn rimraf sentry-internal-typescript-*.tgz", "link:yarn": "yarn link", "build:npm": "npm pack" }, diff --git a/packages/utils/package.json b/packages/utils/package.json index f57d714f0ffb..3f7627c066f2 100644 --- a/packages/utils/package.json +++ b/packages/utils/package.json @@ -41,7 +41,7 @@ "build:types:watch": "tsc -p tsconfig.types.json --watch", "build:npm": "ts-node ../../scripts/prepack.ts && npm pack ./build", "circularDepCheck": "madge --circular src/index.ts", - "clean": "rimraf build coverage cjs esm", + "clean": "rimraf build coverage cjs esm sentry-utils-*.tgz", "fix": "run-s fix:eslint fix:prettier", "fix:eslint": "eslint . --format stylish --fix", "fix:prettier": "prettier --write \"{src,test,scripts}/**/*.ts\"", diff --git a/packages/vue/package.json b/packages/vue/package.json index c29002fd3b70..3df80edc622b 100644 --- a/packages/vue/package.json +++ b/packages/vue/package.json @@ -44,7 +44,7 @@ "build:types:watch": "tsc -p tsconfig.types.json --watch", "build:npm": "ts-node ../../scripts/prepack.ts && npm pack ./build", "circularDepCheck": "madge --circular src/index.ts", - "clean": "rimraf build coverage", + "clean": "rimraf build coverage sentry-vue-*.tgz", "fix": "run-s fix:eslint fix:prettier", "fix:eslint": "eslint . --format stylish --fix", "fix:prettier": "prettier --write \"{src,test,scripts}/**/*.ts\"", diff --git a/packages/wasm/package.json b/packages/wasm/package.json index 10f1b9570fed..76b9c68d21bb 100644 --- a/packages/wasm/package.json +++ b/packages/wasm/package.json @@ -48,7 +48,7 @@ "build:types:watch": "tsc -p tsconfig.types.json --watch", "build:npm": "ts-node ../../scripts/prepack.ts --bundles && npm pack ./build/npm", "circularDepCheck": "madge --circular src/index.ts", - "clean": "rimraf build coverage", + "clean": "rimraf build coverage sentry-wasm-*.tgz", "fix": "run-s fix:eslint fix:prettier", "fix:eslint": "eslint . --format stylish --fix", "fix:prettier": "prettier --write \"{src,test,scripts}/**/*.ts\"", From 73ee42b3b841d96270b9692324f3810bfca1b0f0 Mon Sep 17 00:00:00 2001 From: Katie Byers Date: Tue, 10 May 2022 18:11:22 -0700 Subject: [PATCH 135/204] fix yarn.lock --- yarn.lock | 35 +---------------------------------- 1 file changed, 1 insertion(+), 34 deletions(-) diff --git a/yarn.lock b/yarn.lock index 4e6d2a0f257d..2a26d551c33f 100644 --- a/yarn.lock +++ b/yarn.lock @@ -5795,32 +5795,11 @@ after@0.8.2: resolved "https://registry.yarnpkg.com/after/-/after-0.8.2.tgz#fedb394f9f0e02aa9768e702bda23b505fae7e1f" integrity sha1-/ts5T58OAqqXaOcCvaI7UF+ufh8= -agent-base@4, agent-base@^4.3.0: - version "4.3.0" - resolved "https://registry.yarnpkg.com/agent-base/-/agent-base-4.3.0.tgz#8165f01c436009bccad0b1d122f05ed770efc6ee" - integrity sha512-salcGninV0nPrwpGNn4VTXBb1SOuXQBiqbrNXoeizJsHrsL6ERFM2Ne3JUSBWRE6aeNJI2ROP/WEEIDUiDe3cg== - dependencies: - es6-promisify "^5.0.0" - -agent-base@5: +agent-base@4, agent-base@5, agent-base@6, agent-base@^4.3.0, agent-base@^6.0.2, agent-base@~4.2.1: version "5.1.1" resolved "https://registry.yarnpkg.com/agent-base/-/agent-base-5.1.1.tgz#e8fb3f242959db44d63be665db7a8e739537a32c" integrity sha512-TMeqbNl2fMW0nMjTEPOwe3J/PRFP4vqeoNuQMG0HlMrtm5QxKqdvAkZ1pRBQ/ulIyDD5Yq0nJ7YbdD8ey0TO3g== -agent-base@6, agent-base@^6.0.2: - version "6.0.2" - resolved "https://registry.yarnpkg.com/agent-base/-/agent-base-6.0.2.tgz#49fff58577cfee3f37176feab4c22e00f86d7f77" - integrity sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ== - dependencies: - debug "4" - -agent-base@~4.2.1: - version "4.2.1" - resolved "https://registry.yarnpkg.com/agent-base/-/agent-base-4.2.1.tgz#d89e5999f797875674c07d87f260fc41e83e8ca9" - integrity sha512-JVwXMr9nHYTUXsBFKUqhJwvlcYU/blreOEUkhNR2eXZIvwd+c+o5V4MgDPKWnMS/56awN3TRzIP+KoPn+roQtg== - dependencies: - es6-promisify "^5.0.0" - agentkeepalive@^3.4.1: version "3.5.2" resolved "https://registry.yarnpkg.com/agentkeepalive/-/agentkeepalive-3.5.2.tgz#a113924dd3fa24a0bc3b78108c450c2abee00f67" @@ -11827,18 +11806,6 @@ es6-object-assign@^1.1.0: resolved "https://registry.yarnpkg.com/es6-object-assign/-/es6-object-assign-1.1.0.tgz#c2c3582656247c39ea107cb1e6652b6f9f24523c" integrity sha1-wsNYJlYkfDnqEHyx5mUrb58kUjw= -es6-promise@^4.0.3: - version "4.2.8" - resolved "https://registry.yarnpkg.com/es6-promise/-/es6-promise-4.2.8.tgz#4eb21594c972bc40553d276e510539143db53e0a" - integrity sha512-HJDGx5daxeIvxdBxvG2cb9g4tEvwIk3i8+nhX0yGrYmZUzbkdg8QbDevheDB8gd0//uPj4c1EQua8Q+MViT0/w== - -es6-promisify@^5.0.0: - version "5.0.0" - resolved "https://registry.yarnpkg.com/es6-promisify/-/es6-promisify-5.0.0.tgz#5109d62f3e56ea967c4b63505aef08291c8a5203" - integrity sha1-UQnWLz5W6pZ8S2NQWu8IKRyKUgM= - dependencies: - es6-promise "^4.0.3" - es6-symbol@^3.1.1, es6-symbol@^3.1.3: version "3.1.3" resolved "https://registry.yarnpkg.com/es6-symbol/-/es6-symbol-3.1.3.tgz#bad5d3c1bcdac28269f4cb331e431c78ac705d18" From 46400ff1445d4b86e2c1e9fafa2de59a67aacc17 Mon Sep 17 00:00:00 2001 From: Katie Byers Date: Tue, 10 May 2022 22:16:04 -0700 Subject: [PATCH 136/204] chore(dev): Update `caniuse-lite` (#5069) This (at least for the moment) gets rid of a spammy warning in tests. --- yarn.lock | 18 ++++-------------- 1 file changed, 4 insertions(+), 14 deletions(-) diff --git a/yarn.lock b/yarn.lock index 2a26d551c33f..7f2f1d191960 100644 --- a/yarn.lock +++ b/yarn.lock @@ -8588,20 +8588,10 @@ caniuse-api@^3.0.0: lodash.memoize "^4.1.2" lodash.uniq "^4.5.0" -caniuse-lite@^1.0.0, caniuse-lite@^1.0.30000844, caniuse-lite@^1.0.30001173, caniuse-lite@^1.0.30001179, caniuse-lite@^1.0.30001254: - version "1.0.30001257" - resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001257.tgz#150aaf649a48bee531104cfeda57f92ce587f6e5" - integrity sha512-JN49KplOgHSXpIsVSF+LUyhD8PUp6xPpAXeRrrcBh4KBeP7W864jHn6RvzJgDlrReyeVjMFJL3PLpPvKIxlIHA== - -caniuse-lite@^1.0.30001032, caniuse-lite@^1.0.30001109, caniuse-lite@^1.0.30001280, caniuse-lite@^1.0.30001317: - version "1.0.30001332" - resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001332.tgz#39476d3aa8d83ea76359c70302eafdd4a1d727dd" - integrity sha512-10T30NYOEQtN6C11YGg411yebhvpnC6Z102+B95eAsN0oB6KUs01ivE8u+G6FMIRtIrVlYXhL+LUwQ3/hXwDWw== - -caniuse-lite@^1.0.30001274: - version "1.0.30001279" - resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001279.tgz#eb06818da481ef5096a3b3760f43e5382ed6b0ce" - integrity sha512-VfEHpzHEXj6/CxggTwSFoZBBYGQfQv9Cf42KPlO79sWXCD1QNKWKsKzFeWL7QpZHJQYAvocqV6Rty1yJMkqWLQ== +caniuse-lite@^1.0.0, caniuse-lite@^1.0.30000844, caniuse-lite@^1.0.30001032, caniuse-lite@^1.0.30001109, caniuse-lite@^1.0.30001173, caniuse-lite@^1.0.30001179, caniuse-lite@^1.0.30001254, caniuse-lite@^1.0.30001274, caniuse-lite@^1.0.30001280, caniuse-lite@^1.0.30001317: + version "1.0.30001339" + resolved "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001339.tgz" + integrity sha512-Es8PiVqCe+uXdms0Gu5xP5PF2bxLR7OBp3wUzUnuO7OHzhOfCyg3hdiGWVPVxhiuniOzng+hTc1u3fEQ0TlkSQ== canonical-path@1.0.0: version "1.0.0" From 5beee115a12c2abb5d3711f3125b4f3a6c078a47 Mon Sep 17 00:00:00 2001 From: Luca Forstner Date: Wed, 11 May 2022 13:26:00 +0200 Subject: [PATCH 137/204] fix(utils): Consider 429 responses in transports (#5062) --- packages/browser/src/transports/fetch.ts | 1 + packages/browser/src/transports/xhr.ts | 6 +-- packages/core/src/transports/base.ts | 8 ++-- packages/node/src/transports/http.ts | 1 + packages/node/test/transports/http.test.ts | 13 +++--- packages/node/test/transports/https.test.ts | 13 +++--- packages/types/src/transport.ts | 1 + packages/utils/src/ratelimit.ts | 18 +++++--- packages/utils/test/ratelimit.test.ts | 49 +++++++++++++++++---- 9 files changed, 70 insertions(+), 40 deletions(-) diff --git a/packages/browser/src/transports/fetch.ts b/packages/browser/src/transports/fetch.ts index d4e57e59862e..2d7bea31ae6a 100644 --- a/packages/browser/src/transports/fetch.ts +++ b/packages/browser/src/transports/fetch.ts @@ -21,6 +21,7 @@ export function makeFetchTransport( }; return nativeFetch(options.url, requestOptions).then(response => ({ + statusCode: response.status, headers: { 'x-sentry-rate-limits': response.headers.get('X-Sentry-Rate-Limits'), 'retry-after': response.headers.get('Retry-After'), diff --git a/packages/browser/src/transports/xhr.ts b/packages/browser/src/transports/xhr.ts index 9a8b10c6187d..b10ca4e2b7ca 100644 --- a/packages/browser/src/transports/xhr.ts +++ b/packages/browser/src/transports/xhr.ts @@ -26,13 +26,13 @@ export function makeXHRTransport(options: BrowserTransportOptions): Transport { xhr.onreadystatechange = (): void => { if (xhr.readyState === XHR_READYSTATE_DONE) { - const response = { + resolve({ + statusCode: xhr.status, headers: { 'x-sentry-rate-limits': xhr.getResponseHeader('X-Sentry-Rate-Limits'), 'retry-after': xhr.getResponseHeader('Retry-After'), }, - }; - resolve(response); + }); } }; diff --git a/packages/core/src/transports/base.ts b/packages/core/src/transports/base.ts index 4e6c04e8c516..08a3f5e33a37 100644 --- a/packages/core/src/transports/base.ts +++ b/packages/core/src/transports/base.ts @@ -70,13 +70,11 @@ export function createTransport( const requestTask = (): PromiseLike => makeRequest({ body: serializeEnvelope(filteredEnvelope) }).then( - ({ headers }): void => { - if (headers) { - rateLimits = updateRateLimits(rateLimits, headers); - } + response => { + rateLimits = updateRateLimits(rateLimits, response); }, error => { - IS_DEBUG_BUILD && logger.error('Failed while recording event:', error); + IS_DEBUG_BUILD && logger.error('Failed while sending event:', error); recordEnvelopeLoss('network_error'); }, ); diff --git a/packages/node/src/transports/http.ts b/packages/node/src/transports/http.ts index 0916b9f0b60a..64bf52070eb9 100644 --- a/packages/node/src/transports/http.ts +++ b/packages/node/src/transports/http.ts @@ -113,6 +113,7 @@ function createRequestExecutor( const rateLimitsHeader = res.headers['x-sentry-rate-limits'] ?? null; resolve({ + statusCode: res.statusCode, headers: { 'retry-after': retryAfterHeader, 'x-sentry-rate-limits': Array.isArray(rateLimitsHeader) ? rateLimitsHeader[0] : rateLimitsHeader, diff --git a/packages/node/test/transports/http.test.ts b/packages/node/test/transports/http.test.ts index 013468c3eb5d..d33efc42ff34 100644 --- a/packages/node/test/transports/http.test.ts +++ b/packages/node/test/transports/http.test.ts @@ -237,10 +237,7 @@ describe('makeNewHttpTransport()', () => { it('should register TransportRequestExecutor that returns the correct object from server response (rate limit)', async () => { await setupTestServer({ statusCode: RATE_LIMIT, - responseHeaders: { - 'Retry-After': '2700', - 'X-Sentry-Rate-Limits': '60::organization, 2700::organization', - }, + responseHeaders: {}, }); makeNodeTransport(defaultOptions); @@ -253,10 +250,7 @@ describe('makeNewHttpTransport()', () => { await expect(executorResult).resolves.toEqual( expect.objectContaining({ - headers: { - 'retry-after': '2700', - 'x-sentry-rate-limits': '60::organization, 2700::organization', - }, + statusCode: RATE_LIMIT, }), ); }); @@ -276,6 +270,7 @@ describe('makeNewHttpTransport()', () => { await expect(executorResult).resolves.toEqual( expect.objectContaining({ + statusCode: SUCCESS, headers: { 'retry-after': null, 'x-sentry-rate-limits': null, @@ -303,6 +298,7 @@ describe('makeNewHttpTransport()', () => { await expect(executorResult).resolves.toEqual( expect.objectContaining({ + statusCode: SUCCESS, headers: { 'retry-after': '2700', 'x-sentry-rate-limits': '60::organization, 2700::organization', @@ -330,6 +326,7 @@ describe('makeNewHttpTransport()', () => { await expect(executorResult).resolves.toEqual( expect.objectContaining({ + statusCode: RATE_LIMIT, headers: { 'retry-after': '2700', 'x-sentry-rate-limits': '60::organization, 2700::organization', diff --git a/packages/node/test/transports/https.test.ts b/packages/node/test/transports/https.test.ts index 118a9a70dc88..4313f326e933 100644 --- a/packages/node/test/transports/https.test.ts +++ b/packages/node/test/transports/https.test.ts @@ -290,10 +290,7 @@ describe('makeNewHttpsTransport()', () => { it('should register TransportRequestExecutor that returns the correct object from server response (rate limit)', async () => { await setupTestServer({ statusCode: RATE_LIMIT, - responseHeaders: { - 'Retry-After': '2700', - 'X-Sentry-Rate-Limits': '60::organization, 2700::organization', - }, + responseHeaders: {}, }); makeNodeTransport(defaultOptions); @@ -306,10 +303,7 @@ describe('makeNewHttpsTransport()', () => { await expect(executorResult).resolves.toEqual( expect.objectContaining({ - headers: { - 'retry-after': '2700', - 'x-sentry-rate-limits': '60::organization, 2700::organization', - }, + statusCode: RATE_LIMIT, }), ); }); @@ -329,6 +323,7 @@ describe('makeNewHttpsTransport()', () => { await expect(executorResult).resolves.toEqual( expect.objectContaining({ + statusCode: SUCCESS, headers: { 'retry-after': null, 'x-sentry-rate-limits': null, @@ -356,6 +351,7 @@ describe('makeNewHttpsTransport()', () => { await expect(executorResult).resolves.toEqual( expect.objectContaining({ + statusCode: SUCCESS, headers: { 'retry-after': '2700', 'x-sentry-rate-limits': '60::organization, 2700::organization', @@ -383,6 +379,7 @@ describe('makeNewHttpsTransport()', () => { await expect(executorResult).resolves.toEqual( expect.objectContaining({ + statusCode: RATE_LIMIT, headers: { 'retry-after': '2700', 'x-sentry-rate-limits': '60::organization, 2700::organization', diff --git a/packages/types/src/transport.ts b/packages/types/src/transport.ts index f757b271970a..3c900819a785 100644 --- a/packages/types/src/transport.ts +++ b/packages/types/src/transport.ts @@ -7,6 +7,7 @@ export type TransportRequest = { }; export type TransportMakeRequestResponse = { + statusCode?: number; headers?: { [key: string]: string | null; 'x-sentry-rate-limits': string | null; diff --git a/packages/utils/src/ratelimit.ts b/packages/utils/src/ratelimit.ts index 66614b7fc6b2..78720c888576 100644 --- a/packages/utils/src/ratelimit.ts +++ b/packages/utils/src/ratelimit.ts @@ -1,3 +1,5 @@ +import { TransportMakeRequestResponse } from '@sentry/types'; + // Intentionally keeping the key broad, as we don't know for sure what rate limit headers get returned from backend export type RateLimits = Record; @@ -43,7 +45,7 @@ export function isRateLimited(limits: RateLimits, category: string, now: number */ export function updateRateLimits( limits: RateLimits, - headers: Record, + { statusCode, headers }: TransportMakeRequestResponse, now: number = Date.now(), ): RateLimits { const updatedRateLimits: RateLimits = { @@ -52,8 +54,8 @@ export function updateRateLimits( // "The name is case-insensitive." // https://developer.mozilla.org/en-US/docs/Web/API/Headers/get - const rateLimitHeader = headers['x-sentry-rate-limits']; - const retryAfterHeader = headers['retry-after']; + const rateLimitHeader = headers && headers['x-sentry-rate-limits']; + const retryAfterHeader = headers && headers['retry-after']; if (rateLimitHeader) { /** @@ -69,19 +71,21 @@ export function updateRateLimits( * is an arbitrary string like "org_quota" - ignored by SDK */ for (const limit of rateLimitHeader.trim().split(',')) { - const parameters = limit.split(':', 2); - const headerDelay = parseInt(parameters[0], 10); + const [retryAfter, categories] = limit.split(':', 2); + const headerDelay = parseInt(retryAfter, 10); const delay = (!isNaN(headerDelay) ? headerDelay : 60) * 1000; // 60sec default - if (!parameters[1]) { + if (!categories) { updatedRateLimits.all = now + delay; } else { - for (const category of parameters[1].split(';')) { + for (const category of categories.split(';')) { updatedRateLimits[category] = now + delay; } } } } else if (retryAfterHeader) { updatedRateLimits.all = now + parseRetryAfterHeader(retryAfterHeader, now); + } else if (statusCode === 429) { + updatedRateLimits.all = now + 60 * 1000; } return updatedRateLimits; diff --git a/packages/utils/test/ratelimit.test.ts b/packages/utils/test/ratelimit.test.ts index fec1b3413ae7..52a04683563f 100644 --- a/packages/utils/test/ratelimit.test.ts +++ b/packages/utils/test/ratelimit.test.ts @@ -70,27 +70,30 @@ describe('updateRateLimits()', () => { test('should update the `all` category based on `retry-after` header ', () => { const rateLimits: RateLimits = {}; const headers = { + 'x-sentry-rate-limits': null, 'retry-after': '42', }; - const updatedRateLimits = updateRateLimits(rateLimits, headers, 0); + const updatedRateLimits = updateRateLimits(rateLimits, { headers }, 0); expect(updatedRateLimits.all).toEqual(42 * 1000); }); test('should update a single category based on `x-sentry-rate-limits` header', () => { const rateLimits: RateLimits = {}; const headers = { + 'retry-after': null, 'x-sentry-rate-limits': '13:error', }; - const updatedRateLimits = updateRateLimits(rateLimits, headers, 0); + const updatedRateLimits = updateRateLimits(rateLimits, { headers }, 0); expect(updatedRateLimits.error).toEqual(13 * 1000); }); test('should update multiple categories based on `x-sentry-rate-limits` header', () => { const rateLimits: RateLimits = {}; const headers = { + 'retry-after': null, 'x-sentry-rate-limits': '13:error;transaction', }; - const updatedRateLimits = updateRateLimits(rateLimits, headers, 0); + const updatedRateLimits = updateRateLimits(rateLimits, { headers }, 0); expect(updatedRateLimits.error).toEqual(13 * 1000); expect(updatedRateLimits.transaction).toEqual(13 * 1000); }); @@ -98,9 +101,10 @@ describe('updateRateLimits()', () => { test('should update multiple categories with different values based on multi `x-sentry-rate-limits` header', () => { const rateLimits: RateLimits = {}; const headers = { + 'retry-after': null, 'x-sentry-rate-limits': '13:error,15:transaction', }; - const updatedRateLimits = updateRateLimits(rateLimits, headers, 0); + const updatedRateLimits = updateRateLimits(rateLimits, { headers }, 0); expect(updatedRateLimits.error).toEqual(13 * 1000); expect(updatedRateLimits.transaction).toEqual(15 * 1000); }); @@ -108,9 +112,10 @@ describe('updateRateLimits()', () => { test('should use last entry from multi `x-sentry-rate-limits` header for a given category', () => { const rateLimits: RateLimits = {}; const headers = { + 'retry-after': null, 'x-sentry-rate-limits': '13:error,15:transaction;error', }; - const updatedRateLimits = updateRateLimits(rateLimits, headers, 0); + const updatedRateLimits = updateRateLimits(rateLimits, { headers }, 0); expect(updatedRateLimits.error).toEqual(15 * 1000); expect(updatedRateLimits.transaction).toEqual(15 * 1000); }); @@ -118,18 +123,20 @@ describe('updateRateLimits()', () => { test('should fallback to `all` if `x-sentry-rate-limits` header is missing a category', () => { const rateLimits: RateLimits = {}; const headers = { + 'retry-after': null, 'x-sentry-rate-limits': '13', }; - const updatedRateLimits = updateRateLimits(rateLimits, headers, 0); + const updatedRateLimits = updateRateLimits(rateLimits, { headers }, 0); expect(updatedRateLimits.all).toEqual(13 * 1000); }); test('should use 60s default if delay in `x-sentry-rate-limits` header is malformed', () => { const rateLimits: RateLimits = {}; const headers = { + 'retry-after': null, 'x-sentry-rate-limits': 'x', }; - const updatedRateLimits = updateRateLimits(rateLimits, headers, 0); + const updatedRateLimits = updateRateLimits(rateLimits, { headers }, 0); expect(updatedRateLimits.all).toEqual(60 * 1000); }); @@ -138,9 +145,10 @@ describe('updateRateLimits()', () => { error: 1337, }; const headers = { + 'retry-after': null, 'x-sentry-rate-limits': '13:transaction', }; - const updatedRateLimits = updateRateLimits(rateLimits, headers, 0); + const updatedRateLimits = updateRateLimits(rateLimits, { headers }, 0); expect(updatedRateLimits.error).toEqual(1337); expect(updatedRateLimits.transaction).toEqual(13 * 1000); }); @@ -151,8 +159,31 @@ describe('updateRateLimits()', () => { 'retry-after': '42', 'x-sentry-rate-limits': '13:error', }; - const updatedRateLimits = updateRateLimits(rateLimits, headers, 0); + const updatedRateLimits = updateRateLimits(rateLimits, { headers }, 0); expect(updatedRateLimits.error).toEqual(13 * 1000); expect(updatedRateLimits.all).toBeUndefined(); }); + + test('should apply a global rate limit of 60s when no headers are provided on a 429 status code', () => { + const rateLimits: RateLimits = {}; + const updatedRateLimits = updateRateLimits(rateLimits, { statusCode: 429 }, 0); + expect(updatedRateLimits.all).toBe(60_000); + }); + + test('should not apply a global rate limit specific headers are provided on a 429 status code', () => { + const rateLimits: RateLimits = {}; + const headers = { + 'retry-after': null, + 'x-sentry-rate-limits': '13:error', + }; + const updatedRateLimits = updateRateLimits(rateLimits, { statusCode: 429, headers }, 0); + expect(updatedRateLimits.error).toEqual(13 * 1000); + expect(updatedRateLimits.all).toBeUndefined(); + }); + + test('should not apply a default rate limit on a non-429 status code', () => { + const rateLimits: RateLimits = {}; + const updatedRateLimits = updateRateLimits(rateLimits, { statusCode: 200 }, 0); + expect(updatedRateLimits).toEqual(rateLimits); + }); }); From 656d7c498a91692bd1627870af6f28498dd0333b Mon Sep 17 00:00:00 2001 From: Lukas Stracke Date: Wed, 11 May 2022 15:22:41 +0200 Subject: [PATCH 138/204] ref(hub): Convert `Session` class to object and functions (#5054) convert the `Session` class to a more FP style `Session` object with additional functions that replace the methods of the class. API-wise, this makes the following changes: * `new Session(context)` => `makeSession(context)` * `session.update(context)` => `updateSession(session, context)` * `session.close(status)` => `closeSession(session, status)` `session.toJSON()` is left untouched because this method is called when the Session object is serialized to the JSON object that's sent to the Sentry servers. Additionally, this modify the session-related interfaces. Interface `Session` now describes the session object while the `SessionContext` interface is used to make modifications to the session by calling `updateSession`. Note that `updateSession` and `closeSession` mutate the session object that's passed in as a parameter. I originally wanted to return a new, mutated, object and leave the original one untouched instead of changing it. However this is unfortunately not possible (without bigger modifications) as `BaseClient` makes a modification to a session after it's already passed to `sendSession` to keep it from getting re-sent as a new session. --- MIGRATION.md | 30 ++++ packages/browser/src/exports.ts | 2 +- packages/core/src/baseclient.ts | 7 +- packages/core/src/index.ts | 1 - packages/core/test/lib/base.test.ts | 6 +- packages/core/test/mocks/client.ts | 3 +- packages/hub/src/hub.ts | 11 +- packages/hub/src/index.ts | 2 +- packages/hub/src/scope.ts | 5 +- packages/hub/src/session.ts | 256 +++++++++++++++------------- packages/hub/test/session.test.ts | 28 +-- packages/node/src/index.ts | 2 +- packages/types/src/hub.ts | 4 +- packages/types/src/session.ts | 76 ++++----- 14 files changed, 241 insertions(+), 192 deletions(-) diff --git a/MIGRATION.md b/MIGRATION.md index 9c2663c05b46..9a27dd6f3f26 100644 --- a/MIGRATION.md +++ b/MIGRATION.md @@ -292,6 +292,32 @@ changes in v7. They will be removed in the next major release which is why we st corresponding string literals. Here's how to adjust [`Severity`](#severity-severitylevel-and-severitylevels) and [`SpanStatus`](#spanstatus). +## Session Changes + +Note: These changes are not relevant for the majority of Sentry users but if you are building an +SDK on top of the Javascript SDK, you might need to make some adaptions. +The internal `Session` class was refactored and replaced with a more functional approach in +[#5054](https://github.com/getsentry/sentry-javascript/pull/5054). +Instead of the class, we now export a `Session` interface from `@sentry/types` and three utility functions +to create and update a `Session` object from `@sentry/hub`. +This short example shows what has changed and how to deal with the new functions: + +```js +// New in v7: +import { makeSession, updateSession, closeSession } from '@sentry/hub'; + +const session = makeSession({ release: 'v1.0' }); +updateSession(session, { environment: 'prod' }); +closeSession(session, 'ok'); + +// Before: +import { Session } from '@sentry/hub'; + +const session = new Session({ release: 'v1.0' }); +session.update({ environment: 'prod' }); +session.close('ok'); +``` + ## General API Changes For our efforts to reduce bundle size of the SDK we had to remove and refactor parts of the package which introduced a few changes to the API: @@ -313,7 +339,11 @@ For our efforts to reduce bundle size of the SDK we had to remove and refactor p - Rename `UserAgent` integration to `HttpContext`. (see [#5027](https://github.com/getsentry/sentry-javascript/pull/5027)) - Remove `SDK_NAME` export from `@sentry/browser`, `@sentry/node`, `@sentry/tracing` and `@sentry/vue` packages. - Removed `eventStatusFromHttpCode` to save on bundle size. +<<<<<<< HEAD - Replace `BrowserTracing` `maxTransactionDuration` option with `finalTimeout` option +- Replace `Session` class with a session object and functions (see [#5054](https://github.com/getsentry/sentry-javascript/pull/5054)). +======= +>>>>>>> 5c81e32c4 (Add "Session Changes" section to Migration docs) ## Sentry Angular SDK Changes diff --git a/packages/browser/src/exports.ts b/packages/browser/src/exports.ts index ac3cea82190d..fd8ff9842a22 100644 --- a/packages/browser/src/exports.ts +++ b/packages/browser/src/exports.ts @@ -13,6 +13,7 @@ export type { Stacktrace, Thread, User, + Session, } from '@sentry/types'; export type { BrowserOptions } from './client'; @@ -31,7 +32,6 @@ export { Hub, makeMain, Scope, - Session, startTransaction, SDK_VERSION, setContext, diff --git a/packages/core/src/baseclient.ts b/packages/core/src/baseclient.ts index 3c961218ca8d..f64f2b11d114 100644 --- a/packages/core/src/baseclient.ts +++ b/packages/core/src/baseclient.ts @@ -1,5 +1,5 @@ /* eslint-disable max-lines */ -import { Scope, Session } from '@sentry/hub'; +import { Scope, updateSession } from '@sentry/hub'; import { Client, ClientOptions, @@ -12,6 +12,7 @@ import { Integration, IntegrationClass, Outcome, + Session, SessionAggregates, Severity, SeverityLevel, @@ -199,7 +200,7 @@ export abstract class BaseClient implements Client { } else { this.sendSession(session); // After sending, we set init false to indicate it's not the first occurrence - session.update({ init: false }); + updateSession(session, { init: false }); } } @@ -343,7 +344,7 @@ export abstract class BaseClient implements Client { const shouldUpdateAndSend = (sessionNonTerminal && session.errors === 0) || (sessionNonTerminal && crashed); if (shouldUpdateAndSend) { - session.update({ + updateSession(session, { ...(crashed && { status: 'crashed' }), errors: session.errors || Number(errored || crashed), }); diff --git a/packages/core/src/index.ts b/packages/core/src/index.ts index 9e4afbbc86d0..0d1a686b8e5e 100644 --- a/packages/core/src/index.ts +++ b/packages/core/src/index.ts @@ -20,7 +20,6 @@ export { Hub, makeMain, Scope, - Session, } from '@sentry/hub'; export { getEnvelopeEndpointWithUrlEncodedAuth, getReportDialogEndpoint } from './api'; export { BaseClient } from './baseclient'; diff --git a/packages/core/test/lib/base.test.ts b/packages/core/test/lib/base.test.ts index 520cd40931a6..7a5431d1f9cb 100644 --- a/packages/core/test/lib/base.test.ts +++ b/packages/core/test/lib/base.test.ts @@ -1,4 +1,4 @@ -import { Hub, Scope, Session } from '@sentry/hub'; +import { Hub, makeSession, Scope } from '@sentry/hub'; import { Event, Span } from '@sentry/types'; import { dsnToString, logger, SentryError, SyncPromise } from '@sentry/utils'; @@ -1327,7 +1327,7 @@ describe('BaseClient', () => { const options = getDefaultTestClientOptions({ dsn: PUBLIC_DSN }); const client = new TestClient(options); - const session = new Session({ release: 'test' }); + const session = makeSession({ release: 'test' }); client.captureSession(session); @@ -1339,7 +1339,7 @@ describe('BaseClient', () => { const options = getDefaultTestClientOptions({ enabled: false, dsn: PUBLIC_DSN }); const client = new TestClient(options); - const session = new Session({ release: 'test' }); + const session = makeSession({ release: 'test' }); client.captureSession(session); diff --git a/packages/core/test/mocks/client.ts b/packages/core/test/mocks/client.ts index d46eb000d480..9caef7b0e0cf 100644 --- a/packages/core/test/mocks/client.ts +++ b/packages/core/test/mocks/client.ts @@ -1,5 +1,4 @@ -import { Session } from '@sentry/hub'; -import { ClientOptions, Event, Integration, Outcome, Severity, SeverityLevel } from '@sentry/types'; +import { ClientOptions, Event, Integration, Outcome, Session, Severity, SeverityLevel } from '@sentry/types'; import { resolvedSyncPromise } from '@sentry/utils'; import { BaseClient } from '../../src/baseclient'; diff --git a/packages/hub/src/hub.ts b/packages/hub/src/hub.ts index ac98e053faf5..7f591e44c2e8 100644 --- a/packages/hub/src/hub.ts +++ b/packages/hub/src/hub.ts @@ -12,6 +12,7 @@ import { Integration, IntegrationClass, Primitive, + Session, SessionContext, Severity, SeverityLevel, @@ -31,7 +32,7 @@ import { import { IS_DEBUG_BUILD } from './flags'; import { Scope } from './scope'; -import { Session } from './session'; +import { closeSession, makeSession, updateSession } from './session'; /** * API compatibility version of this hub. @@ -395,7 +396,7 @@ export class Hub implements HubInterface { const scope = layer && layer.scope; const session = scope && scope.getSession(); if (session) { - session.close(); + closeSession(session); } this._sendSessionUpdate(); @@ -416,7 +417,7 @@ export class Hub implements HubInterface { const global = getGlobalObject<{ navigator?: { userAgent?: string } }>(); const { userAgent } = global.navigator || {}; - const session = new Session({ + const session = makeSession({ release, environment, ...(scope && { user: scope.getUser() }), @@ -428,7 +429,7 @@ export class Hub implements HubInterface { // End existing session if there's one const currentSession = scope.getSession && scope.getSession(); if (currentSession && currentSession.status === 'ok') { - currentSession.update({ status: 'exited' }); + updateSession(currentSession, { status: 'exited' }); } this.endSession(); @@ -446,7 +447,7 @@ export class Hub implements HubInterface { const { scope, client } = this.getStackTop(); if (!scope) return; - const session = scope.getSession && scope.getSession(); + const session = scope.getSession(); if (session) { if (client && client.captureSession) { client.captureSession(session); diff --git a/packages/hub/src/index.ts b/packages/hub/src/index.ts index 92b85f370d80..16c4f3680dd8 100644 --- a/packages/hub/src/index.ts +++ b/packages/hub/src/index.ts @@ -1,7 +1,7 @@ export type { Carrier, Layer } from './hub'; export { addGlobalEventProcessor, Scope } from './scope'; -export { Session } from './session'; +export { closeSession, makeSession, updateSession } from './session'; export { SessionFlusher } from './sessionflusher'; export { getCurrentHub, getHubFromCarrier, getMainCarrier, Hub, makeMain, setHubOnCarrier } from './hub'; export { diff --git a/packages/hub/src/scope.ts b/packages/hub/src/scope.ts index 271827519792..b51c620d86b0 100644 --- a/packages/hub/src/scope.ts +++ b/packages/hub/src/scope.ts @@ -13,6 +13,7 @@ import { RequestSession, Scope as ScopeInterface, ScopeContext, + Session, Severity, SeverityLevel, Span, @@ -29,7 +30,7 @@ import { } from '@sentry/utils'; import { IS_DEBUG_BUILD } from './flags'; -import { Session } from './session'; +import { updateSession } from './session'; /** * Absolute maximum number of breadcrumbs added to an event. @@ -136,7 +137,7 @@ export class Scope implements ScopeInterface { public setUser(user: User | null): this { this._user = user || {}; if (this._session) { - this._session.update({ user }); + updateSession(this._session, { user }); } this._notifyScopeListeners(); return this; diff --git a/packages/hub/src/session.ts b/packages/hub/src/session.ts index 3206ef9306dc..fed5cfa004ad 100644 --- a/packages/hub/src/session.ts +++ b/packages/hub/src/session.ts @@ -1,136 +1,154 @@ -import { Session as SessionInterface, SessionContext, SessionStatus } from '@sentry/types'; +import { Session, SessionContext, SessionStatus } from '@sentry/types'; +import { SerializedSession } from '@sentry/types/build/types/session'; import { dropUndefinedKeys, timestampInSeconds, uuid4 } from '@sentry/utils'; /** - * @inheritdoc + * Creates a new `Session` object by setting certain default parameters. If optional @param context + * is passed, the passed properties are applied to the session object. + * + * @param context (optional) additional properties to be applied to the returned session object + * + * @returns a new `Session` object */ -export class Session implements SessionInterface { - public userAgent?: string; - public errors: number = 0; - public release?: string; - public sid: string = uuid4(); - public did?: string; - public timestamp: number; - public started: number; - public duration?: number = 0; - public status: SessionStatus = 'ok'; - public environment?: string; - public ipAddress?: string; - public init: boolean = true; - public ignoreDuration: boolean = false; +export function makeSession(context?: Omit): Session { + // Both timestamp and started are in seconds since the UNIX epoch. + const startingTime = timestampInSeconds(); - public constructor(context?: Omit) { - // Both timestamp and started are in seconds since the UNIX epoch. - const startingTime = timestampInSeconds(); - this.timestamp = startingTime; - this.started = startingTime; - if (context) { - this.update(context); - } + const session: Session = { + sid: uuid4(), + init: true, + timestamp: startingTime, + started: startingTime, + duration: 0, + status: 'ok', + errors: 0, + ignoreDuration: false, + toJSON: () => sessionToJSON(session), + }; + + if (context) { + updateSession(session, context); } - /** JSDoc */ - // eslint-disable-next-line complexity - public update(context: SessionContext = {}): void { - if (context.user) { - if (!this.ipAddress && context.user.ip_address) { - this.ipAddress = context.user.ip_address; - } + return session; +} - if (!this.did && !context.did) { - this.did = context.user.id || context.user.email || context.user.username; - } +/** + * Updates a session object with the properties passed in the context. + * + * Note that this function mutates the passed object and returns void. + * (Had to do this instead of returning a new and updated session because closing and sending a session + * makes an update to the session after it was passed to the sending logic. + * @see BaseClient.captureSession ) + * + * @param session the `Session` to update + * @param context the `SessionContext` holding the properties that should be updated in @param session + */ +// eslint-disable-next-line complexity +export function updateSession(session: Session, context: SessionContext = {}): void { + if (context.user) { + if (!session.ipAddress && context.user.ip_address) { + session.ipAddress = context.user.ip_address; } - this.timestamp = context.timestamp || timestampInSeconds(); - if (context.ignoreDuration) { - this.ignoreDuration = context.ignoreDuration; - } - if (context.sid) { - // Good enough uuid validation. — Kamil - this.sid = context.sid.length === 32 ? context.sid : uuid4(); - } - if (context.init !== undefined) { - this.init = context.init; - } - if (!this.did && context.did) { - this.did = `${context.did}`; - } - if (typeof context.started === 'number') { - this.started = context.started; - } - if (this.ignoreDuration) { - this.duration = undefined; - } else if (typeof context.duration === 'number') { - this.duration = context.duration; - } else { - const duration = this.timestamp - this.started; - this.duration = duration >= 0 ? duration : 0; - } - if (context.release) { - this.release = context.release; - } - if (context.environment) { - this.environment = context.environment; - } - if (!this.ipAddress && context.ipAddress) { - this.ipAddress = context.ipAddress; - } - if (!this.userAgent && context.userAgent) { - this.userAgent = context.userAgent; - } - if (typeof context.errors === 'number') { - this.errors = context.errors; - } - if (context.status) { - this.status = context.status; + if (!session.did && !context.did) { + session.did = context.user.id || context.user.email || context.user.username; } } - /** JSDoc */ - public close(status?: Exclude): void { - if (status) { - this.update({ status }); - } else if (this.status === 'ok') { - this.update({ status: 'exited' }); - } else { - this.update(); - } + session.timestamp = context.timestamp || timestampInSeconds(); + + if (context.ignoreDuration) { + session.ignoreDuration = context.ignoreDuration; + } + if (context.sid) { + // Good enough uuid validation. — Kamil + session.sid = context.sid.length === 32 ? context.sid : uuid4(); } + if (context.init !== undefined) { + session.init = context.init; + } + if (!session.did && context.did) { + session.did = `${context.did}`; + } + if (typeof context.started === 'number') { + session.started = context.started; + } + if (session.ignoreDuration) { + session.duration = undefined; + } else if (typeof context.duration === 'number') { + session.duration = context.duration; + } else { + const duration = session.timestamp - session.started; + session.duration = duration >= 0 ? duration : 0; + } + if (context.release) { + session.release = context.release; + } + if (context.environment) { + session.environment = context.environment; + } + if (!session.ipAddress && context.ipAddress) { + session.ipAddress = context.ipAddress; + } + if (!session.userAgent && context.userAgent) { + session.userAgent = context.userAgent; + } + if (typeof context.errors === 'number') { + session.errors = context.errors; + } + if (context.status) { + session.status = context.status; + } +} - /** JSDoc */ - public toJSON(): { - init: boolean; - sid: string; - did?: string; - timestamp: string; - started: string; - duration?: number; - status: SessionStatus; - errors: number; - attrs?: { - release?: string; - environment?: string; - user_agent?: string; - ip_address?: string; - }; - } { - return dropUndefinedKeys({ - sid: `${this.sid}`, - init: this.init, - // Make sure that sec is converted to ms for date constructor - started: new Date(this.started * 1000).toISOString(), - timestamp: new Date(this.timestamp * 1000).toISOString(), - status: this.status, - errors: this.errors, - did: typeof this.did === 'number' || typeof this.did === 'string' ? `${this.did}` : undefined, - duration: this.duration, - attrs: { - release: this.release, - environment: this.environment, - ip_address: this.ipAddress, - user_agent: this.userAgent, - }, - }); +/** + * Closes a session by setting its status and updating the session object with it. + * Internally calls `updateSession` to update the passed session object. + * + * Note that this function mutates the passed session (@see updateSession for explanation). + * + * @param session the `Session` object to be closed + * @param status the `SessionStatus` with which the session was closed. If you don't pass a status, + * this function will keep the previously set status, unless it was `'ok'` in which case + * it is changed to `'exited'`. + */ +export function closeSession(session: Session, status?: Exclude): void { + let context = {}; + if (status) { + context = { status }; + } else if (session.status === 'ok') { + context = { status: 'exited' }; } + + updateSession(session, context); +} + +/** + * Serializes a passed session object to a JSON object with a slightly different structure. + * This is necessary because the Sentry backend requires a slightly different schema of a session + * than the one the JS SDKs use internally. + * + * @param session the session to be converted + * + * @returns a JSON object of the passed session + */ +function sessionToJSON(session: Session): SerializedSession { + return dropUndefinedKeys({ + sid: `${session.sid}`, + init: session.init, + // Make sure that sec is converted to ms for date constructor + started: new Date(session.started * 1000).toISOString(), + timestamp: new Date(session.timestamp * 1000).toISOString(), + status: session.status, + errors: session.errors, + did: typeof session.did === 'number' || typeof session.did === 'string' ? `${session.did}` : undefined, + duration: session.duration, + attrs: { + release: session.release, + environment: session.environment, + ip_address: session.ipAddress, + user_agent: session.userAgent, + }, + }); } diff --git a/packages/hub/test/session.test.ts b/packages/hub/test/session.test.ts index f25e5ad4189b..f57267fd11c0 100644 --- a/packages/hub/test/session.test.ts +++ b/packages/hub/test/session.test.ts @@ -1,11 +1,12 @@ import { SessionContext } from '@sentry/types'; import { timestampInSeconds } from '@sentry/utils'; -import { Session } from '../src/session'; +import { closeSession, makeSession, updateSession } from '../src/session'; describe('Session', () => { it('initializes with the proper defaults', () => { - const session = new Session().toJSON(); + const newSession = makeSession(); + const session = newSession.toJSON(); // Grab current year to check if we are converting from sec -> ms correctly const currentYear = new Date(timestampInSeconds() * 1000).toISOString().slice(0, 4); @@ -82,36 +83,39 @@ describe('Session', () => { // the out variable in a test explicitly refers to it. const DEFAULT_OUT = { duration: expect.any(Number), timestamp: expect.any(String) }; - const session = new Session(); + const session = makeSession(); const initSessionProps = session.toJSON(); - session.update(test[1]); - expect(session.toJSON()).toEqual({ ...initSessionProps, ...DEFAULT_OUT, ...test[2] }); + updateSession(session, test[1]); + const updatedSessionProps = session.toJSON(); + + expect(updatedSessionProps).toEqual({ ...initSessionProps, ...DEFAULT_OUT, ...test[2] }); }); }); describe('close', () => { it('exits a normal session', () => { - const session = new Session(); + const session = makeSession(); expect(session.status).toEqual('ok'); - session.close(); + + closeSession(session); expect(session.status).toEqual('exited'); }); it('updates session status when give status', () => { - const session = new Session(); + const session = makeSession(); expect(session.status).toEqual('ok'); - session.close('abnormal'); + closeSession(session, 'abnormal'); expect(session.status).toEqual('abnormal'); }); it('only changes status ok to exited', () => { - const session = new Session(); - session.update({ status: 'crashed' }); + const session = makeSession(); + updateSession(session, { status: 'crashed' }); expect(session.status).toEqual('crashed'); - session.close(); + closeSession(session, 'crashed'); expect(session.status).toEqual('crashed'); }); }); diff --git a/packages/node/src/index.ts b/packages/node/src/index.ts index 823dc73d848e..cea9a4554ccb 100644 --- a/packages/node/src/index.ts +++ b/packages/node/src/index.ts @@ -6,6 +6,7 @@ export type { Event, EventHint, Exception, + Session, // eslint-disable-next-line deprecation/deprecation Severity, SeverityLevel, @@ -30,7 +31,6 @@ export { Hub, makeMain, Scope, - Session, startTransaction, SDK_VERSION, setContext, diff --git a/packages/types/src/hub.ts b/packages/types/src/hub.ts index 9a67968a4fc1..a34d4372e08d 100644 --- a/packages/types/src/hub.ts +++ b/packages/types/src/hub.ts @@ -5,7 +5,7 @@ import { Extra, Extras } from './extra'; import { Integration, IntegrationClass } from './integration'; import { Primitive } from './misc'; import { Scope } from './scope'; -import { Session, SessionContext } from './session'; +import { Session } from './session'; import { Severity, SeverityLevel } from './severity'; import { CustomSamplingContext, Transaction, TransactionContext } from './transaction'; import { User } from './user'; @@ -215,7 +215,7 @@ export interface Hub { * * @returns The session which was just started */ - startSession(context?: SessionContext): Session; + startSession(context?: Session): Session; /** * Ends the session that lives on the current scope and sends it to Sentry diff --git a/packages/types/src/session.ts b/packages/types/src/session.ts index 237e4c41808d..35d5e0840a3e 100644 --- a/packages/types/src/session.ts +++ b/packages/types/src/session.ts @@ -1,60 +1,39 @@ import { User } from './user'; -/** - * @inheritdoc - */ -export interface Session extends SessionContext { - /** JSDoc */ - update(context?: SessionContext): void; - - /** JSDoc */ - close(status?: SessionStatus): void; - - /** JSDoc */ - toJSON(): { - init: boolean; - sid: string; - did?: string; - timestamp: string; - started: string; - duration?: number; - status: SessionStatus; - errors: number; - attrs?: { - release?: string; - environment?: string; - user_agent?: string; - ip_address?: string; - }; - }; -} - export interface RequestSession { status?: RequestSessionStatus; } -/** - * Session Context - */ -export interface SessionContext { - sid?: string; +export interface Session { + sid: string; did?: string; - init?: boolean; + init: boolean; // seconds since the UNIX epoch - timestamp?: number; + timestamp: number; // seconds since the UNIX epoch - started?: number; + started: number; duration?: number; - status?: SessionStatus; + status: SessionStatus; release?: string; environment?: string; userAgent?: string; ipAddress?: string; - errors?: number; + errors: number; user?: User | null; - ignoreDuration?: boolean; + ignoreDuration: boolean; + + /** + * Overrides default JSON serialization of the Session because + * the Sentry servers expect a slightly different schema of a session + * which is described in the interface @see SerializedSession in this file. + * + * @return a Sentry-backend conforming JSON object of the session + */ + toJSON(): SerializedSession; } +export type SessionContext = Partial; + export type SessionStatus = 'ok' | 'exited' | 'crashed' | 'abnormal'; export type RequestSessionStatus = 'ok' | 'errored' | 'crashed'; @@ -87,3 +66,20 @@ export interface AggregationCounts { exited?: number; crashed?: number; } + +export interface SerializedSession { + init: boolean; + sid: string; + did?: string; + timestamp: string; + started: string; + duration?: number; + status: SessionStatus; + errors: number; + attrs?: { + release?: string; + environment?: string; + user_agent?: string; + ip_address?: string; + }; +} From 8c185dbb9f9ac442170d4f0fbb1c4c9872a134c1 Mon Sep 17 00:00:00 2001 From: Katie Byers Date: Wed, 11 May 2022 06:32:55 -0700 Subject: [PATCH 139/204] fix(build): Fix references to `rollup.bundle.config.js` (#5074) This fixes a few spots where https://github.com/getsentry/sentry-javascript/pull/4950 missed changing references to the new name for our rollup config for CDN bundles, `rollup.bundle.config.js`. --- packages/browser/package.json | 2 +- packages/browser/test/integration/debugging.md | 4 ++-- packages/tracing/package.json | 2 +- packages/wasm/package.json | 2 +- 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/packages/browser/package.json b/packages/browser/package.json index a34fcf820edd..f83013c625b1 100644 --- a/packages/browser/package.json +++ b/packages/browser/package.json @@ -52,7 +52,7 @@ "build:rollup": "rollup -c rollup.npm.config.js", "build:types": "tsc -p tsconfig.types.json", "build:watch": "run-p build:cjs:watch build:esm:watch build:bundle:watch build:types:watch", - "build:bundle:watch": "rollup --config --watch", + "build:bundle:watch": "rollup --config rollup.bundle.config.js --watch", "build:cjs:watch": "tsc -p tsconfig.cjs.json --watch", "build:es5:watch": "yarn build:cjs:watch # *** backwards compatibility - remove in v7 ***", "build:dev:watch": "run-p build:cjs:watch build:esm:watch build:types:watch", diff --git a/packages/browser/test/integration/debugging.md b/packages/browser/test/integration/debugging.md index eb4849a48493..f97abe998aed 100644 --- a/packages/browser/test/integration/debugging.md +++ b/packages/browser/test/integration/debugging.md @@ -12,10 +12,10 @@ These tests are hard to debug, because the testing system is somewhat complex, s - `suites.yourTestFile.js`: - Use `it.only` to only run the single test you're interested in. -- Repo-level `rollup.config.js`: +- Repo-level `rollup/bundleHelpers.js`: - Comment out all bundle variants except whichever one `run.js` is turning into `artifacts/sdk.js`. -- Browser-package-level `rollup.config.js`: +- Browser-package-level `rollup.bundle.config.js`: - Build only one of `es5` and `es6`. - Run `build:bundle:watch` in a separate terminal tab, so that when you add `console.log`s to the SDK, they get picked up. diff --git a/packages/tracing/package.json b/packages/tracing/package.json index dd1d56d5d895..9ed2d2f9d5c3 100644 --- a/packages/tracing/package.json +++ b/packages/tracing/package.json @@ -35,7 +35,7 @@ "build:rollup": "rollup -c rollup.npm.config.js", "build:types": "tsc -p tsconfig.types.json", "build:watch": "run-p build:cjs:watch build:esm:watch build:bundle:watch build:types:watch", - "build:bundle:watch": "rollup --config --watch", + "build:bundle:watch": "rollup --config rollup.bundle.config.js --watch", "build:cjs:watch": "tsc -p tsconfig.cjs.json --watch", "build:dev:watch": "run-p build:cjs:watch build:esm:watch build:types:watch", "build:es5:watch": "yarn build:cjs:watch # *** backwards compatibility - remove in v7 ***", diff --git a/packages/wasm/package.json b/packages/wasm/package.json index 76b9c68d21bb..613a88e16ee9 100644 --- a/packages/wasm/package.json +++ b/packages/wasm/package.json @@ -39,7 +39,7 @@ "build:rollup": "rollup -c rollup.npm.config.js", "build:types": "tsc -p tsconfig.types.json", "build:watch": "run-p build:cjs:watch build:esm:watch build:bundle:watch build:types:watch", - "build:bundle:watch": "rollup --config --watch", + "build:bundle:watch": "rollup --config rollup.bundle.config.js --watch", "build:cjs:watch": "tsc -p tsconfig.cjs.json --watch", "build:dev:watch": "run-p build:cjs:watch build:esm:watch build:types:watch", "build:es5:watch": "yarn build:cjs:watch # *** backwards compatibility - remove in v7 ***", From 7bf6540bd0d7d455a0972245d4544defb5ec7589 Mon Sep 17 00:00:00 2001 From: Katie Byers Date: Wed, 11 May 2022 06:35:29 -0700 Subject: [PATCH 140/204] fix(tests): Fix node integration test linting and type errors (#5070) This fixes a number of linting and type errors in our node integration tests. Included changes: - Fix eslint config so it's clear which tsconfig file applies to which code files - Use ESM import for `path`. - Create `TestAPIResponse` type. - Remove redundant `await` on returned promise. - Use `unknown` in place of `any`. --- packages/node-integration-tests/.eslintrc.js | 19 ++++++++++++++++--- .../node-integration-tests/jest.config.js | 2 +- .../suites/express/sentry-trace/server.ts | 2 ++ .../sentry-trace/trace-header-assign/test.ts | 7 ++++--- .../sentry-trace/trace-header-out/test.ts | 5 +++-- packages/node-integration-tests/tsconfig.json | 2 +- .../node-integration-tests/tsconfig.test.json | 2 +- .../node-integration-tests/utils/index.ts | 4 ++-- .../{ => utils}/setup-tests.ts | 0 9 files changed, 30 insertions(+), 13 deletions(-) rename packages/node-integration-tests/{ => utils}/setup-tests.ts (100%) diff --git a/packages/node-integration-tests/.eslintrc.js b/packages/node-integration-tests/.eslintrc.js index fdcde4fa0f14..5a3ecdd7617a 100644 --- a/packages/node-integration-tests/.eslintrc.js +++ b/packages/node-integration-tests/.eslintrc.js @@ -4,7 +4,20 @@ module.exports = { jest: true, }, extends: ['../../.eslintrc.js'], - parserOptions: { - sourceType: 'module', - }, + overrides: [ + { + files: ['utils/**/*.ts'], + parserOptions: { + project: ['tsconfig.json'], + sourceType: 'module', + }, + }, + { + files: ['suites/**/*.ts'], + parserOptions: { + project: ['tsconfig.test.json'], + sourceType: 'module', + }, + }, + ], }; diff --git a/packages/node-integration-tests/jest.config.js b/packages/node-integration-tests/jest.config.js index fd4e06a64fb3..9a2862dd9b8d 100644 --- a/packages/node-integration-tests/jest.config.js +++ b/packages/node-integration-tests/jest.config.js @@ -1,7 +1,7 @@ const baseConfig = require('../../jest/jest.config.js'); module.exports = { - globalSetup: '/setup-tests.ts', + globalSetup: '/utils/setup-tests.ts', ...baseConfig, testMatch: ['**/test.ts'], }; diff --git a/packages/node-integration-tests/suites/express/sentry-trace/server.ts b/packages/node-integration-tests/suites/express/sentry-trace/server.ts index 172c2edd3900..e93b50f18cd6 100644 --- a/packages/node-integration-tests/suites/express/sentry-trace/server.ts +++ b/packages/node-integration-tests/suites/express/sentry-trace/server.ts @@ -6,6 +6,8 @@ import http from 'http'; const app = express(); +export type TestAPIResponse = { test_data: { host: string; 'sentry-trace': string } }; + Sentry.init({ dsn: 'https://public@dsn.ingest.sentry.io/1337', release: '1.0', diff --git a/packages/node-integration-tests/suites/express/sentry-trace/trace-header-assign/test.ts b/packages/node-integration-tests/suites/express/sentry-trace/trace-header-assign/test.ts index 5ee98789c299..47382b3fdb4b 100644 --- a/packages/node-integration-tests/suites/express/sentry-trace/trace-header-assign/test.ts +++ b/packages/node-integration-tests/suites/express/sentry-trace/trace-header-assign/test.ts @@ -1,14 +1,15 @@ import { TRACEPARENT_REGEXP } from '@sentry/utils'; +import * as path from 'path'; import { getAPIResponse, runServer } from '../../../../utils/index'; -import path = require('path'); +import { TestAPIResponse } from '../server'; test('Should assign `sentry-trace` header which sets parent trace id of an outgoing request.', async () => { const url = await runServer(__dirname, `${path.resolve(__dirname, '..')}/server.ts`); - const response = await getAPIResponse(new URL(`${url}/express`), { + const response = (await getAPIResponse(new URL(`${url}/express`), { 'sentry-trace': '12312012123120121231201212312012-1121201211212012-0', - }); + })) as TestAPIResponse; expect(response).toBeDefined(); expect(response).toMatchObject({ diff --git a/packages/node-integration-tests/suites/express/sentry-trace/trace-header-out/test.ts b/packages/node-integration-tests/suites/express/sentry-trace/trace-header-out/test.ts index ca7eb56fd61c..788a3f7086ab 100644 --- a/packages/node-integration-tests/suites/express/sentry-trace/trace-header-out/test.ts +++ b/packages/node-integration-tests/suites/express/sentry-trace/trace-header-out/test.ts @@ -1,12 +1,13 @@ import { TRACEPARENT_REGEXP } from '@sentry/utils'; +import * as path from 'path'; import { getAPIResponse, runServer } from '../../../../utils/index'; -import path = require('path'); +import { TestAPIResponse } from '../server'; test('should attach a `sentry-trace` header to an outgoing request.', async () => { const url = await runServer(__dirname, `${path.resolve(__dirname, '..')}/server.ts`); - const response = await getAPIResponse(new URL(`${url}/express`)); + const response = (await getAPIResponse(new URL(`${url}/express`))) as TestAPIResponse; expect(response).toBeDefined(); expect(response).toMatchObject({ diff --git a/packages/node-integration-tests/tsconfig.json b/packages/node-integration-tests/tsconfig.json index c98602a3af23..782d8f9c517f 100644 --- a/packages/node-integration-tests/tsconfig.json +++ b/packages/node-integration-tests/tsconfig.json @@ -1,7 +1,7 @@ { "extends": "../../tsconfig.json", - "include": ["**/*.ts"], + "include": ["utils/**/*.ts"], "compilerOptions": { // package-specific options diff --git a/packages/node-integration-tests/tsconfig.test.json b/packages/node-integration-tests/tsconfig.test.json index b43ec254274b..5a37b90c4fe2 100644 --- a/packages/node-integration-tests/tsconfig.test.json +++ b/packages/node-integration-tests/tsconfig.test.json @@ -1,7 +1,7 @@ { "extends": "./tsconfig.json", - "include": ["**/*.ts"], + "include": ["suites/**/*.ts"], "compilerOptions": { // should include all types from `./tsconfig.json` plus types for all test frameworks used diff --git a/packages/node-integration-tests/utils/index.ts b/packages/node-integration-tests/utils/index.ts index fba8bb7d52de..872c9d72521d 100644 --- a/packages/node-integration-tests/utils/index.ts +++ b/packages/node-integration-tests/utils/index.ts @@ -103,8 +103,8 @@ export const getMultipleEnvelopeRequest = async (url: string, count: number): Pr * @param {Record} [headers] * @return {*} {Promise} */ -export const getAPIResponse = async (url: URL, headers?: Record): Promise => { - return await new Promise(resolve => { +export const getAPIResponse = async (url: URL, headers?: Record): Promise => { + return new Promise(resolve => { http.get( headers ? ({ diff --git a/packages/node-integration-tests/setup-tests.ts b/packages/node-integration-tests/utils/setup-tests.ts similarity index 100% rename from packages/node-integration-tests/setup-tests.ts rename to packages/node-integration-tests/utils/setup-tests.ts From 6536eb6e4864d6456b95c41ac4d5696938ed800c Mon Sep 17 00:00:00 2001 From: Lukas Stracke Date: Wed, 11 May 2022 15:49:00 +0200 Subject: [PATCH 141/204] chore(docs): Fix unresolved rebase conflict in Migration.MD (#5076) fix after merging #5054 which left an unresolved conflict in Migration.MD that emerged while rebasing the PR onto the current 7.x branch. --- MIGRATION.md | 4 ---- 1 file changed, 4 deletions(-) diff --git a/MIGRATION.md b/MIGRATION.md index 9a27dd6f3f26..01addf214ef4 100644 --- a/MIGRATION.md +++ b/MIGRATION.md @@ -339,11 +339,7 @@ For our efforts to reduce bundle size of the SDK we had to remove and refactor p - Rename `UserAgent` integration to `HttpContext`. (see [#5027](https://github.com/getsentry/sentry-javascript/pull/5027)) - Remove `SDK_NAME` export from `@sentry/browser`, `@sentry/node`, `@sentry/tracing` and `@sentry/vue` packages. - Removed `eventStatusFromHttpCode` to save on bundle size. -<<<<<<< HEAD - Replace `BrowserTracing` `maxTransactionDuration` option with `finalTimeout` option -- Replace `Session` class with a session object and functions (see [#5054](https://github.com/getsentry/sentry-javascript/pull/5054)). -======= ->>>>>>> 5c81e32c4 (Add "Session Changes" section to Migration docs) ## Sentry Angular SDK Changes From cd7186413c53ed4bc140b8b77f00e36270a0b75b Mon Sep 17 00:00:00 2001 From: Onur Temizkan Date: Wed, 11 May 2022 16:33:21 +0100 Subject: [PATCH 142/204] feat(react): Add react-router-v6 integration (#5042) Tracing integration for [`react-router@6.x.x`](https://reactrouter.com/docs/en/v6) This implementation will provide a HoC that wraps [``](https://reactrouter.com/docs/en/v6/api#routes-and-route) which replaced `` from `react-router@5.x.x`. --- packages/react/package.json | 1 + packages/react/src/index.ts | 1 + packages/react/src/reactrouterv6.tsx | 180 +++++++++++++++++++ packages/react/test/reactrouterv6.test.tsx | 190 +++++++++++++++++++++ yarn.lock | 21 +++ 5 files changed, 393 insertions(+) create mode 100644 packages/react/src/reactrouterv6.tsx create mode 100644 packages/react/test/reactrouterv6.test.tsx diff --git a/packages/react/package.json b/packages/react/package.json index 78d3ffc7b606..0ec14a723c11 100644 --- a/packages/react/package.json +++ b/packages/react/package.json @@ -44,6 +44,7 @@ "react-router-3": "npm:react-router@3.2.0", "react-router-4": "npm:react-router@4.1.0", "react-router-5": "npm:react-router@5.0.0", + "react-router-6": "npm:react-router@6.3.0", "redux": "^4.0.5" }, "scripts": { diff --git a/packages/react/src/index.ts b/packages/react/src/index.ts index 306f90e4f943..150f4571ef85 100644 --- a/packages/react/src/index.ts +++ b/packages/react/src/index.ts @@ -6,3 +6,4 @@ export { ErrorBoundary, withErrorBoundary } from './errorboundary'; export { createReduxEnhancer } from './redux'; export { reactRouterV3Instrumentation } from './reactrouterv3'; export { reactRouterV4Instrumentation, reactRouterV5Instrumentation, withSentryRouting } from './reactrouter'; +export { reactRouterV6Instrumentation, withSentryReactRouterV6Routing } from './reactrouterv6'; diff --git a/packages/react/src/reactrouterv6.tsx b/packages/react/src/reactrouterv6.tsx new file mode 100644 index 000000000000..a793f8824e1d --- /dev/null +++ b/packages/react/src/reactrouterv6.tsx @@ -0,0 +1,180 @@ +// Inspired from Donnie McNeal's solution: +// https://gist.github.com/wontondon/e8c4bdf2888875e4c755712e99279536 + +import { Transaction, TransactionContext } from '@sentry/types'; +import { getGlobalObject, logger } from '@sentry/utils'; +import hoistNonReactStatics from 'hoist-non-react-statics'; +import React from 'react'; + +import { IS_DEBUG_BUILD } from './flags'; +import { Action, Location } from './types'; + +interface RouteObject { + caseSensitive?: boolean; + children?: RouteObject[]; + element?: React.ReactNode; + index?: boolean; + path?: string; +} + +type Params = { + readonly [key in Key]: string | undefined; +}; + +interface RouteMatch { + params: Params; + pathname: string; + route: RouteObject; +} + +type UseEffect = (cb: () => void, deps: unknown[]) => void; +type UseLocation = () => Location; +type UseNavigationType = () => Action; +type CreateRoutesFromChildren = (children: JSX.Element[]) => RouteObject[]; +type MatchRoutes = (routes: RouteObject[], location: Location) => RouteMatch[] | null; + +let activeTransaction: Transaction | undefined; + +let _useEffect: UseEffect; +let _useLocation: UseLocation; +let _useNavigationType: UseNavigationType; +let _createRoutesFromChildren: CreateRoutesFromChildren; +let _matchRoutes: MatchRoutes; +let _customStartTransaction: (context: TransactionContext) => Transaction | undefined; +let _startTransactionOnLocationChange: boolean; + +const global = getGlobalObject(); + +const SENTRY_TAGS = { + 'routing.instrumentation': 'react-router-v6', +}; + +function getInitPathName(): string | undefined { + if (global && global.location) { + return global.location.pathname; + } + + return undefined; +} + +export function reactRouterV6Instrumentation( + useEffect: UseEffect, + useLocation: UseLocation, + useNavigationType: UseNavigationType, + createRoutesFromChildren: CreateRoutesFromChildren, + matchRoutes: MatchRoutes, +) { + return ( + customStartTransaction: (context: TransactionContext) => Transaction | undefined, + startTransactionOnPageLoad = true, + startTransactionOnLocationChange = true, + ): void => { + const initPathName = getInitPathName(); + if (startTransactionOnPageLoad && initPathName) { + activeTransaction = customStartTransaction({ + name: initPathName, + op: 'pageload', + tags: SENTRY_TAGS, + }); + } + + _useEffect = useEffect; + _useLocation = useLocation; + _useNavigationType = useNavigationType; + _matchRoutes = matchRoutes; + _createRoutesFromChildren = createRoutesFromChildren; + + _customStartTransaction = customStartTransaction; + _startTransactionOnLocationChange = startTransactionOnLocationChange; + }; +} + +const getTransactionName = (routes: RouteObject[], location: Location, matchRoutes: MatchRoutes): string => { + if (!routes || routes.length === 0 || !matchRoutes) { + return location.pathname; + } + + const branches = matchRoutes(routes, location); + + if (branches) { + // eslint-disable-next-line @typescript-eslint/prefer-for-of + for (let x = 0; x < branches.length; x++) { + if (branches[x].route && branches[x].route.path && branches[x].pathname === location.pathname) { + return branches[x].route.path || location.pathname; + } + } + } + + return location.pathname; +}; + +export function withSentryReactRouterV6Routing

, R extends React.FC

>(Routes: R): R { + if ( + !_useEffect || + !_useLocation || + !_useNavigationType || + !_createRoutesFromChildren || + !_matchRoutes || + !_customStartTransaction + ) { + IS_DEBUG_BUILD && + logger.warn('reactRouterV6Instrumentation was unable to wrap Routes because of one or more missing parameters.'); + + return Routes; + } + + let isBaseLocation: boolean = false; + let routes: RouteObject[]; + + const SentryRoutes: React.FC

= (props: P) => { + const location = _useLocation(); + const navigationType = _useNavigationType(); + + _useEffect(() => { + // Performance concern: + // This is repeated when is rendered. + routes = _createRoutesFromChildren(props.children); + isBaseLocation = true; + + if (activeTransaction) { + activeTransaction.setName(getTransactionName(routes, location, _matchRoutes)); + } + + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [props.children]); + + _useEffect(() => { + if (isBaseLocation) { + if (activeTransaction) { + activeTransaction.finish(); + } + + return; + } + + if (_startTransactionOnLocationChange && (navigationType === 'PUSH' || navigationType === 'POP')) { + if (activeTransaction) { + activeTransaction.finish(); + } + + activeTransaction = _customStartTransaction({ + name: getTransactionName(routes, location, _matchRoutes), + op: 'navigation', + tags: SENTRY_TAGS, + }); + } + }, [props.children, location, navigationType, isBaseLocation]); + + isBaseLocation = false; + + // @ts-ignore Setting more specific React Component typing for `R` generic above + // will break advanced type inference done by react router params + return ; + }; + + hoistNonReactStatics(SentryRoutes, Routes); + + // @ts-ignore Setting more specific React Component typing for `R` generic above + // will break advanced type inference done by react router params + return SentryRoutes; +} diff --git a/packages/react/test/reactrouterv6.test.tsx b/packages/react/test/reactrouterv6.test.tsx new file mode 100644 index 000000000000..5e30d96a83a9 --- /dev/null +++ b/packages/react/test/reactrouterv6.test.tsx @@ -0,0 +1,190 @@ +import { render } from '@testing-library/react'; +import * as React from 'react'; +import { + createRoutesFromChildren, + matchPath, + matchRoutes, + MemoryRouter, + Navigate, + Route, + Routes, + useLocation, + useNavigationType, +} from 'react-router-6'; + +import { reactRouterV6Instrumentation } from '../src'; +import { withSentryReactRouterV6Routing } from '../src/reactrouterv6'; + +describe('React Router v6', () => { + function createInstrumentation(_opts?: { + startTransactionOnPageLoad?: boolean; + startTransactionOnLocationChange?: boolean; + }): [jest.Mock, { mockSetName: jest.Mock; mockFinish: jest.Mock }] { + const options = { + matchPath: _opts ? matchPath : undefined, + startTransactionOnLocationChange: true, + startTransactionOnPageLoad: true, + ..._opts, + }; + const mockFinish = jest.fn(); + const mockSetName = jest.fn(); + const mockStartTransaction = jest.fn().mockReturnValue({ setName: mockSetName, finish: mockFinish }); + + reactRouterV6Instrumentation( + React.useEffect, + useLocation, + useNavigationType, + createRoutesFromChildren, + matchRoutes, + )(mockStartTransaction, options.startTransactionOnPageLoad, options.startTransactionOnLocationChange); + return [mockStartTransaction, { mockSetName, mockFinish }]; + } + + it('starts a pageload transaction', () => { + const [mockStartTransaction] = createInstrumentation(); + const SentryRoutes = withSentryReactRouterV6Routing(Routes); + + render( + + + Home} /> + + , + ); + + expect(mockStartTransaction).toHaveBeenCalledTimes(1); + expect(mockStartTransaction).toHaveBeenLastCalledWith({ + name: '/', + op: 'pageload', + tags: { 'routing.instrumentation': 'react-router-v6' }, + }); + }); + + it('skips pageload transaction with `startTransactionOnPageLoad: false`', () => { + const [mockStartTransaction] = createInstrumentation({ startTransactionOnPageLoad: false }); + const SentryRoutes = withSentryReactRouterV6Routing(Routes); + + render( + + + Home} /> + + , + ); + + expect(mockStartTransaction).toHaveBeenCalledTimes(0); + }); + + it('skips navigation transaction, with `startTransactionOnLocationChange: false`', () => { + const [mockStartTransaction] = createInstrumentation({ startTransactionOnLocationChange: false }); + const SentryRoutes = withSentryReactRouterV6Routing(Routes); + + render( + + + About} /> + } /> + + , + ); + + expect(mockStartTransaction).toHaveBeenCalledTimes(1); + expect(mockStartTransaction).toHaveBeenLastCalledWith({ + name: '/', + op: 'pageload', + tags: { 'routing.instrumentation': 'react-router-v6' }, + }); + }); + + it('starts a navigation transaction', () => { + const [mockStartTransaction] = createInstrumentation(); + const SentryRoutes = withSentryReactRouterV6Routing(Routes); + + render( + + + About} /> + } /> + + , + ); + + expect(mockStartTransaction).toHaveBeenCalledTimes(2); + expect(mockStartTransaction).toHaveBeenLastCalledWith({ + name: '/about', + op: 'navigation', + tags: { 'routing.instrumentation': 'react-router-v6' }, + }); + }); + + it('works with nested routes', () => { + const [mockStartTransaction] = createInstrumentation(); + const SentryRoutes = withSentryReactRouterV6Routing(Routes); + + render( + + + About}> + us} /> + + } /> + + , + ); + + expect(mockStartTransaction).toHaveBeenCalledTimes(2); + expect(mockStartTransaction).toHaveBeenLastCalledWith({ + name: '/about/us', + op: 'navigation', + tags: { 'routing.instrumentation': 'react-router-v6' }, + }); + }); + + it('works with paramaterized paths', () => { + const [mockStartTransaction] = createInstrumentation(); + const SentryRoutes = withSentryReactRouterV6Routing(Routes); + + render( + + + About}> + page} /> + + } /> + + , + ); + + expect(mockStartTransaction).toHaveBeenCalledTimes(2); + expect(mockStartTransaction).toHaveBeenLastCalledWith({ + name: '/about/:page', + op: 'navigation', + tags: { 'routing.instrumentation': 'react-router-v6' }, + }); + }); + + it('works with paths with multiple parameters', () => { + const [mockStartTransaction] = createInstrumentation(); + const SentryRoutes = withSentryReactRouterV6Routing(Routes); + + render( + + + Stores}> + Store}> + Product} /> + + + } /> + + , + ); + + expect(mockStartTransaction).toHaveBeenCalledTimes(2); + expect(mockStartTransaction).toHaveBeenLastCalledWith({ + name: '/stores/:storeId/products/:productId', + op: 'navigation', + tags: { 'routing.instrumentation': 'react-router-v6' }, + }); + }); +}); diff --git a/yarn.lock b/yarn.lock index 7f2f1d191960..e5950f2a5cca 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2247,6 +2247,13 @@ dependencies: regenerator-runtime "^0.13.4" +"@babel/runtime@^7.7.6": + version "7.17.9" + resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.17.9.tgz#d19fbf802d01a8cb6cf053a64e472d42c434ba72" + integrity sha512-lSiBBvodq29uShpWGNbgFdKYNiFDo5/HIYsaCEY9ff4sb10x9jizo2+pRrSyF4jKZCXqgzuqBOQKbUm90gQwJg== + dependencies: + regenerator-runtime "^0.13.4" + "@babel/template@7.10.4": version "7.10.4" resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.10.4.tgz#3251996c4200ebc71d1a8fc405fba940f36ba278" @@ -14174,6 +14181,13 @@ history@^4.6.0, history@^4.9.0: tiny-warning "^1.0.0" value-equal "^1.0.1" +history@^5.2.0: + version "5.3.0" + resolved "https://registry.yarnpkg.com/history/-/history-5.3.0.tgz#1548abaa245ba47992f063a0783db91ef201c73b" + integrity sha512-ZqaKwjjrAYUYfLG+htGaIIZ4nioX2L70ZUMIFysS3xvBsSG4x/n1V6TXV3N8ZYNuFGlDirFg32T7B6WOUPDYcQ== + dependencies: + "@babel/runtime" "^7.7.6" + hmac-drbg@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/hmac-drbg/-/hmac-drbg-1.0.1.tgz#d2745701025a6c775a6c545793ed502fc0c649a1" @@ -21101,6 +21115,13 @@ react-refresh@0.8.3: tiny-invariant "^1.0.2" tiny-warning "^1.0.0" +"react-router-6@npm:react-router@6.3.0": + version "6.3.0" + resolved "https://registry.yarnpkg.com/react-router/-/react-router-6.3.0.tgz#3970cc64b4cb4eae0c1ea5203a80334fdd175557" + integrity sha512-7Wh1DzVQ+tlFjkeo+ujvjSqSJmkt1+8JO+T5xklPlgrh70y7ogx75ODRW0ThWhY7S+6yEDks8TYrtQe/aoboBQ== + dependencies: + history "^5.2.0" + react@^18.0.0: version "18.0.0" resolved "https://registry.yarnpkg.com/react/-/react-18.0.0.tgz#b468736d1f4a5891f38585ba8e8fb29f91c3cb96" From 7723d32552e37371a029a46bdd839d84a54762b4 Mon Sep 17 00:00:00 2001 From: Katie Byers Date: Wed, 11 May 2022 11:31:28 -0700 Subject: [PATCH 143/204] ref(build): Use sucrase for `watch` npm builds (#5059) This follows up on https://github.com/getsentry/sentry-javascript/pull/5035, which switched our main build commands to use rollup and sucrase instead of tsc, and brings that same change to our `build:watch` commands. Note: Running either of these `watch` commands produces different feedback than the `tsc` watch commands we're used to, in that the `build:types` component auto-watches dependencies (and will trigger a cascading rebuild on change), but the `build:rollup` component doesn't (and therefore only rebuilds the changed package). The reason for this is that sucrase and rollup are truly only *trans*piling (IOW, smushing the code touching other packages around in a non-semantic way, only really worried about whether it's legal JS, not whether it plays the way it should with those other packages), not *com*piling (actually understanding the entire, cross-package AST and refusing to produce code if the interfaces between packages don't match up). The utility of `tsc`'s rebuild of dependent packages was never actually to change the built code of those packages, it was just that the rebuild attempt forced a cascading typecheck, so we could know what _we_ had to change in the dependent packages' code. The process building types still serves this function, but the process building code doesn't need to and therefore there's no need for the cascade. (As you'll see in the conversation below, I myself was fuzzy on this point at first, which is why I'm spelling it out here quite so explicitly, in case any future readers stumble into the same wrong assumptions I did.) --- packages/browser/package.json | 4 ++-- packages/core/package.json | 2 +- packages/gatsby/package.json | 2 +- packages/hub/package.json | 2 +- packages/integrations/package.json | 2 +- packages/nextjs/package.json | 2 +- packages/node/package.json | 2 +- packages/react/package.json | 2 +- packages/serverless/package.json | 2 +- packages/tracing/package.json | 4 ++-- packages/types/package.json | 2 +- packages/utils/package.json | 2 +- packages/vue/package.json | 4 ++-- packages/wasm/package.json | 4 ++-- 14 files changed, 18 insertions(+), 18 deletions(-) diff --git a/packages/browser/package.json b/packages/browser/package.json index f83013c625b1..17f9a9d9879e 100644 --- a/packages/browser/package.json +++ b/packages/browser/package.json @@ -51,11 +51,11 @@ "build:esm": "tsc -p tsconfig.esm.json", "build:rollup": "rollup -c rollup.npm.config.js", "build:types": "tsc -p tsconfig.types.json", - "build:watch": "run-p build:cjs:watch build:esm:watch build:bundle:watch build:types:watch", + "build:watch": "run-p build:rollup:watch build:bundle:watch build:types:watch", "build:bundle:watch": "rollup --config rollup.bundle.config.js --watch", "build:cjs:watch": "tsc -p tsconfig.cjs.json --watch", "build:es5:watch": "yarn build:cjs:watch # *** backwards compatibility - remove in v7 ***", - "build:dev:watch": "run-p build:cjs:watch build:esm:watch build:types:watch", + "build:dev:watch": "run-p build:rollup:watch build:types:watch", "build:esm:watch": "tsc -p tsconfig.esm.json --watch", "build:rollup:watch": "rollup -c rollup.npm.config.js --watch", "build:types:watch": "tsc -p tsconfig.types.json --watch", diff --git a/packages/core/package.json b/packages/core/package.json index 862538bed0a7..df5ed748e43c 100644 --- a/packages/core/package.json +++ b/packages/core/package.json @@ -29,7 +29,7 @@ "build:esm": "tsc -p tsconfig.esm.json", "build:rollup": "rollup -c rollup.npm.config.js", "build:types": "tsc -p tsconfig.types.json", - "build:watch": "run-p build:cjs:watch build:esm:watch build:types:watch", + "build:watch": "run-p build:rollup:watch build:types:watch", "build:cjs:watch": "tsc -p tsconfig.cjs.json --watch", "build:dev:watch": "run-s build:watch", "build:es5:watch": "yarn build:cjs:watch # *** backwards compatibility - remove in v7 ***", diff --git a/packages/gatsby/package.json b/packages/gatsby/package.json index 2b24faf69698..7c2a8aea0bc4 100644 --- a/packages/gatsby/package.json +++ b/packages/gatsby/package.json @@ -43,7 +43,7 @@ "build:plugin": "tsc -p tsconfig.plugin.json", "build:rollup": "rollup -c rollup.npm.config.js", "build:types": "tsc -p tsconfig.types.json", - "build:watch": "run-p build:cjs:watch build:esm:watch build:types:watch", + "build:watch": "run-p build:rollup:watch build:types:watch", "build:cjs:watch": "tsc -p tsconfig.cjs.json --watch", "build:dev:watch": "run-s build:watch", "build:es5:watch": "yarn build:cjs:watch # *** backwards compatibility - remove in v7 ***", diff --git a/packages/hub/package.json b/packages/hub/package.json index 6c8cc51ef2ad..acb20df1e41b 100644 --- a/packages/hub/package.json +++ b/packages/hub/package.json @@ -28,7 +28,7 @@ "build:esm": "tsc -p tsconfig.esm.json", "build:rollup": "rollup -c rollup.npm.config.js", "build:types": "tsc -p tsconfig.types.json", - "build:watch": "run-p build:cjs:watch build:esm:watch build:types:watch", + "build:watch": "run-p build:rollup:watch build:types:watch", "build:cjs:watch": "tsc -p tsconfig.cjs.json --watch", "build:dev:watch": "run-s build:watch", "build:es5:watch": "yarn build:cjs:watch # *** backwards compatibility - remove in v7 ***", diff --git a/packages/integrations/package.json b/packages/integrations/package.json index c1d1c1829c87..7ca2c2d31af9 100644 --- a/packages/integrations/package.json +++ b/packages/integrations/package.json @@ -33,7 +33,7 @@ "build:esm": "tsc -p tsconfig.esm.json", "build:rollup": "rollup -c rollup.npm.config.js", "build:types": "tsc -p tsconfig.types.json", - "build:watch": "run-p build:cjs:watch build:esm:watch build:types:watch", + "build:watch": "run-p build:rollup:watch build:types:watch", "build:cjs:watch": "tsc -p tsconfig.cjs.json --watch", "build:dev:watch": "run-s build:watch", "build:es5:watch": "yarn build:cjs:watch # *** backwards compatibility - remove in v7 ***", diff --git a/packages/nextjs/package.json b/packages/nextjs/package.json index ea447fc7bd5b..8a682d5dbd17 100644 --- a/packages/nextjs/package.json +++ b/packages/nextjs/package.json @@ -50,7 +50,7 @@ "build:esm": "tsc -p tsconfig.esm.json", "build:rollup": "rollup -c rollup.npm.config.js", "build:types": "tsc -p tsconfig.types.json", - "build:watch": "run-p build:cjs:watch build:esm:watch build:types:watch", + "build:watch": "run-p build:rollup:watch build:types:watch", "build:cjs:watch": "tsc -p tsconfig.cjs.json --watch", "build:dev:watch": "run-s build:watch", "build:es5:watch": "yarn build:cjs:watch # *** backwards compatibility - remove in v7 ***", diff --git a/packages/node/package.json b/packages/node/package.json index 750a63232996..97ce2e5d541f 100644 --- a/packages/node/package.json +++ b/packages/node/package.json @@ -41,7 +41,7 @@ "build:esm": "tsc -p tsconfig.esm.json", "build:rollup": "rollup -c rollup.npm.config.js", "build:types": "tsc -p tsconfig.types.json", - "build:watch": "run-p build:cjs:watch build:esm:watch build:types:watch", + "build:watch": "run-p build:rollup:watch build:types:watch", "build:cjs:watch": "tsc -p tsconfig.cjs.json --watch", "build:dev:watch": "run-s build:watch", "build:es5:watch": "yarn build:cjs:watch # *** backwards compatibility - remove in v7 ***", diff --git a/packages/react/package.json b/packages/react/package.json index 0ec14a723c11..c1fccc261b9e 100644 --- a/packages/react/package.json +++ b/packages/react/package.json @@ -55,7 +55,7 @@ "build:esm": "tsc -p tsconfig.esm.json", "build:rollup": "rollup -c rollup.npm.config.js", "build:types": "tsc -p tsconfig.types.json", - "build:watch": "run-p build:cjs:watch build:esm:watch build:types:watch", + "build:watch": "run-p build:rollup:watch build:types:watch", "build:cjs:watch": "tsc -p tsconfig.cjs.json --watch", "build:dev:watch": "run-s build:watch", "build:es5:watch": "yarn build:cjs:watch # *** backwards compatibility - remove in v7 ***", diff --git a/packages/serverless/package.json b/packages/serverless/package.json index 330becc81897..6496ee81fec2 100644 --- a/packages/serverless/package.json +++ b/packages/serverless/package.json @@ -46,7 +46,7 @@ "build:esm": "tsc -p tsconfig.esm.json", "build:rollup": "rollup -c rollup.npm.config.js", "build:types": "tsc -p tsconfig.types.json", - "build:watch": "run-p build:cjs:watch build:esm:watch build:types:watch", + "build:watch": "run-p build:rollup:watch build:types:watch", "build:cjs:watch": "tsc -p tsconfig.cjs.json --watch", "build:dev:watch": "run-s build:watch", "build:es5:watch": "yarn build:cjs:watch # *** backwards compatibility - remove in v7 ***", diff --git a/packages/tracing/package.json b/packages/tracing/package.json index 9ed2d2f9d5c3..baa3055dfb1c 100644 --- a/packages/tracing/package.json +++ b/packages/tracing/package.json @@ -34,10 +34,10 @@ "build:esm": "tsc -p tsconfig.esm.json", "build:rollup": "rollup -c rollup.npm.config.js", "build:types": "tsc -p tsconfig.types.json", - "build:watch": "run-p build:cjs:watch build:esm:watch build:bundle:watch build:types:watch", + "build:watch": "run-p build:rollup:watch build:bundle:watch build:types:watch", "build:bundle:watch": "rollup --config rollup.bundle.config.js --watch", "build:cjs:watch": "tsc -p tsconfig.cjs.json --watch", - "build:dev:watch": "run-p build:cjs:watch build:esm:watch build:types:watch", + "build:dev:watch": "run-p build:rollup:watch build:types:watch", "build:es5:watch": "yarn build:cjs:watch # *** backwards compatibility - remove in v7 ***", "build:esm:watch": "tsc -p tsconfig.esm.json --watch", "build:rollup:watch": "rollup -c rollup.npm.config.js --watch", diff --git a/packages/types/package.json b/packages/types/package.json index c4114bb958a4..195cbe89a788 100644 --- a/packages/types/package.json +++ b/packages/types/package.json @@ -23,7 +23,7 @@ "build:esm": "tsc -p tsconfig.esm.json", "build:rollup": "rollup -c rollup.npm.config.js", "build:types": "tsc -p tsconfig.types.json", - "build:watch": "run-p build:cjs:watch build:esm:watch build:types:watch", + "build:watch": "run-p build:rollup:watch build:types:watch", "build:cjs:watch": "tsc -p tsconfig.cjs.json --watch", "build:dev:watch": "run-s build:watch", "build:es5:watch": "yarn build:cjs:watch # *** backwards compatibility - remove in v7 ***", diff --git a/packages/utils/package.json b/packages/utils/package.json index 3f7627c066f2..29ffb5678973 100644 --- a/packages/utils/package.json +++ b/packages/utils/package.json @@ -32,7 +32,7 @@ "build:esm": "tsc -p tsconfig.esm.json", "build:rollup": "yarn ts-node scripts/buildRollup.ts", "build:types": "tsc -p tsconfig.types.json", - "build:watch": "run-p build:cjs:watch build:esm:watch build:types:watch", + "build:watch": "run-p build:rollup:watch build:types:watch", "build:cjs:watch": "tsc -p tsconfig.cjs.json --watch", "build:dev:watch": "run-s build:watch", "build:es5:watch": "yarn build:cjs:watch # *** backwards compatibility - remove in v7 ***", diff --git a/packages/vue/package.json b/packages/vue/package.json index 3df80edc622b..20ca30b73cd4 100644 --- a/packages/vue/package.json +++ b/packages/vue/package.json @@ -34,10 +34,10 @@ "build:esm": "tsc -p tsconfig.esm.json", "build:rollup": "rollup -c rollup.npm.config.js", "build:types": "tsc -p tsconfig.types.json", - "build:watch": "run-p build:cjs:watch build:esm:watch build:types:watch", + "build:watch": "run-p build:rollup:watch build:types:watch", "build:bundle:watch": "rollup --config --watch", "build:cjs:watch": "tsc -p tsconfig.cjs.json --watch", - "build:dev:watch": "run-p build:cjs:watch build:esm:watch build:types:watch", + "build:dev:watch": "run-p build:rollup:watch build:types:watch", "build:es5:watch": "yarn build:cjs:watch # *** backwards compatibility - remove in v7 ***", "build:esm:watch": "tsc -p tsconfig.esm.json --watch", "build:rollup:watch": "rollup -c rollup.npm.config.js --watch", diff --git a/packages/wasm/package.json b/packages/wasm/package.json index 613a88e16ee9..6aa44b21a181 100644 --- a/packages/wasm/package.json +++ b/packages/wasm/package.json @@ -38,10 +38,10 @@ "build:esm": "tsc -p tsconfig.esm.json", "build:rollup": "rollup -c rollup.npm.config.js", "build:types": "tsc -p tsconfig.types.json", - "build:watch": "run-p build:cjs:watch build:esm:watch build:bundle:watch build:types:watch", + "build:watch": "run-p build:rollup:watch build:bundle:watch build:types:watch", "build:bundle:watch": "rollup --config rollup.bundle.config.js --watch", "build:cjs:watch": "tsc -p tsconfig.cjs.json --watch", - "build:dev:watch": "run-p build:cjs:watch build:esm:watch build:types:watch", + "build:dev:watch": "run-p build:rollup:watch build:types:watch", "build:es5:watch": "yarn build:cjs:watch # *** backwards compatibility - remove in v7 ***", "build:esm:watch": "tsc -p tsconfig.esm.json --watch", "build:rollup:watch": "rollup -c rollup.npm.config.js --watch", From 6b3ea050ab3014e7a732e4eadf535c1b875bb382 Mon Sep 17 00:00:00 2001 From: Katie Byers Date: Wed, 11 May 2022 15:12:35 -0700 Subject: [PATCH 144/204] ref(build): Improve logging in prepack scripts (#5071) This improves logging in our prepack scripts, primarily by adding newlines and an `ERROR` prefix (to make errors easier to spot in a long stream of logs) and by logging the actual error when something goes wrong (rather than just logging that something is wrong without saying _what_ is wrong). --- packages/gatsby/scripts/prepack.ts | 11 ++++-- scripts/prepack.ts | 61 ++++++++++++++---------------- 2 files changed, 36 insertions(+), 36 deletions(-) diff --git a/packages/gatsby/scripts/prepack.ts b/packages/gatsby/scripts/prepack.ts index 3f5a8e24d674..c103875f93d3 100644 --- a/packages/gatsby/scripts/prepack.ts +++ b/packages/gatsby/scripts/prepack.ts @@ -12,14 +12,19 @@ export function prepack(buildDir: string): boolean { // copy package-specific assets to build dir return PACKAGE_ASSETS.every(asset => { const assetPath = path.resolve(asset); + const destinationPath = path.resolve(buildDir, asset); try { if (!fs.existsSync(assetPath)) { - console.error(`Asset ${asset} does not exist.`); + console.error(`\nERROR: Asset '${asset}' does not exist.`); return false; } - fs.copyFileSync(assetPath, path.resolve(buildDir, asset)); + console.log(`Copying ${path.basename(asset)} to ${path.relative('../..', destinationPath)}.`); + fs.copyFileSync(assetPath, destinationPath); } catch (error) { - console.error(`Error while copying ${asset} to ${buildDir}`); + console.error( + `\nERROR: Error while copying ${path.basename(asset)} to ${path.relative('../..', destinationPath)}:\n`, + error, + ); return false; } return true; diff --git a/scripts/prepack.ts b/scripts/prepack.ts index a7609a8087bd..ace6846db457 100644 --- a/scripts/prepack.ts +++ b/scripts/prepack.ts @@ -19,54 +19,47 @@ const ENTRY_POINTS = ['main', 'module', 'types', 'browser']; const packageWithBundles = process.argv.includes('--bundles'); const buildDir = packageWithBundles ? NPM_BUILD_DIR : BUILD_DIR; +// eslint-disable-next-line @typescript-eslint/no-var-requires +const pkgJson: { [key: string]: unknown } = require(path.resolve('package.json')); + // check if build dir exists -try { - if (!fs.existsSync(path.resolve(buildDir))) { - console.error(`Directory ${buildDir} DOES NOT exist`); - console.error("This script should only be executed after you've run `yarn build`."); - process.exit(1); - } -} catch (error) { - console.error(`Error while looking up directory ${buildDir}`); +if (!fs.existsSync(path.resolve(buildDir))) { + console.error(`\nERROR: Directory '${buildDir}' does not exist in ${pkgJson.name}.`); + console.error("This script should only be executed after you've run `yarn build`."); process.exit(1); } // copy non-code assets to build dir ASSETS.forEach(asset => { const assetPath = path.resolve(asset); - try { - if (!fs.existsSync(assetPath)) { - console.error(`Asset ${asset} does not exist.`); - process.exit(1); - } - const destinationPath = path.resolve(buildDir, path.basename(asset)); - console.log(`Copying ${path.basename(asset)} to ${path.relative('../..', destinationPath)}.`); - fs.copyFileSync(assetPath, destinationPath); - } catch (error) { - console.error(`Error while copying ${asset} to ${buildDir}`); + if (!fs.existsSync(assetPath)) { + console.error(`\nERROR: Asset '${asset}' does not exist.`); process.exit(1); } + const destinationPath = path.resolve(buildDir, path.basename(asset)); + console.log(`Copying ${path.basename(asset)} to ${path.relative('../..', destinationPath)}.`); + fs.copyFileSync(assetPath, destinationPath); }); // package.json modifications -const packageJsonPath = path.resolve(buildDir, 'package.json'); +const newPackageJsonPath = path.resolve(buildDir, 'package.json'); // eslint-disable-next-line @typescript-eslint/no-var-requires -const pkgJson: { [key: string]: unknown } = require(packageJsonPath); +const newPkgJson: { [key: string]: unknown } = require(newPackageJsonPath); // modify entry points to point to correct paths (i.e. strip out the build directory) -ENTRY_POINTS.filter(entryPoint => pkgJson[entryPoint]).forEach(entryPoint => { - pkgJson[entryPoint] = (pkgJson[entryPoint] as string).replace(`${buildDir}/`, ''); +ENTRY_POINTS.filter(entryPoint => newPkgJson[entryPoint]).forEach(entryPoint => { + newPkgJson[entryPoint] = (newPkgJson[entryPoint] as string).replace(`${buildDir}/`, ''); }); -delete pkgJson.scripts; -delete pkgJson.volta; -delete pkgJson.jest; +delete newPkgJson.scripts; +delete newPkgJson.volta; +delete newPkgJson.jest; // write modified package.json to file (pretty-printed with 2 spaces) try { - fs.writeFileSync(packageJsonPath, JSON.stringify(pkgJson, null, 2)); + fs.writeFileSync(newPackageJsonPath, JSON.stringify(newPkgJson, null, 2)); } catch (error) { - console.error('Error while writing package.json to disk'); + console.error(`\nERROR: Error while writing modified 'package.json' to disk in ${pkgJson.name}:\n`, error); process.exit(1); } @@ -78,26 +71,28 @@ async function runPackagePrepack(packagePrepackPath: string): Promise { process.exit(1); } } else { - console.error(`Could not find a prepack function in ${packagePrepackPath}.`); + console.error(`\nERROR: Could not find a \`prepack\` function in './scripts/prepack.ts' in ${pkgJson.name}.`); console.error( - 'Make sure, your package-specific prepack script exports `function prepack(buildDir: string): boolean`.', + 'Make sure your package-specific prepack script exports `function prepack(buildDir: string): boolean`.', ); process.exit(1); } } // execute package specific settings -// 1. check if a package called `/scripts/prepack.ts` exitsts +// 1. check if a script called `/scripts/prepack.ts` exists // if yes, 2.) execute that script for things that are package-specific -void (async () => { +async function runPackageSpecificScripts(): Promise { const packagePrepackPath = path.resolve('scripts', 'prepack.ts'); try { if (fs.existsSync(packagePrepackPath)) { await runPackagePrepack(packagePrepackPath); } } catch (error) { - console.error(`Error while trying to access ${packagePrepackPath.toString()}`); + console.error(`\nERROR: Error while trying to load and run ./scripts/prepack.ts in ${pkgJson.name}:\n`, error); process.exit(1); } console.log(`\nSuccessfully finished prepack commands for ${pkgJson.name}\n`); -})(); +} + +void runPackageSpecificScripts(); From c8d99053b19f2a378715e509c7dd7cf8e24d5609 Mon Sep 17 00:00:00 2001 From: Katie Byers Date: Wed, 11 May 2022 15:22:44 -0700 Subject: [PATCH 145/204] ref(build): Remove `es5`, `cjs`, and `esm` build commands (#5073) Now that we've switched to using rollup and sucrase for our builds, we no longer need the old build commands or the tsconfig files which went with them. --- package.json | 3 --- packages/browser/package.json | 6 ------ packages/browser/tsconfig.cjs.json | 8 -------- packages/browser/tsconfig.esm.json | 8 -------- packages/core/package.json | 6 ------ packages/core/tsconfig.cjs.json | 8 -------- packages/core/tsconfig.esm.json | 8 -------- packages/gatsby/package.json | 6 ------ packages/gatsby/tsconfig.cjs.json | 8 -------- packages/gatsby/tsconfig.esm.json | 8 -------- packages/hub/package.json | 6 ------ packages/hub/tsconfig.cjs.json | 8 -------- packages/hub/tsconfig.esm.json | 8 -------- packages/integrations/package.json | 6 ------ packages/integrations/tsconfig.cjs.json | 8 -------- packages/integrations/tsconfig.esm.json | 8 -------- packages/nextjs/package.json | 6 ------ packages/nextjs/tsconfig.cjs.json | 8 -------- packages/nextjs/tsconfig.esm.json | 8 -------- packages/node/package.json | 6 ------ packages/node/tsconfig.cjs.json | 8 -------- packages/node/tsconfig.esm.json | 8 -------- packages/react/package.json | 6 ------ packages/react/tsconfig.cjs.json | 8 -------- packages/react/tsconfig.esm.json | 8 -------- packages/serverless/package.json | 6 ------ packages/serverless/tsconfig.cjs.json | 8 -------- packages/serverless/tsconfig.esm.json | 8 -------- packages/tracing/package.json | 6 ------ packages/tracing/tsconfig.cjs.json | 8 -------- packages/tracing/tsconfig.esm.json | 8 -------- packages/types/package.json | 6 ------ packages/types/tsconfig.cjs.json | 8 -------- packages/types/tsconfig.esm.json | 8 -------- packages/utils/package.json | 6 ------ packages/utils/tsconfig.cjs.json | 8 -------- packages/utils/tsconfig.esm.json | 8 -------- packages/vue/package.json | 6 ------ packages/vue/tsconfig.cjs.json | 8 -------- packages/vue/tsconfig.esm.json | 8 -------- packages/wasm/package.json | 6 ------ packages/wasm/tsconfig.cjs.json | 8 -------- packages/wasm/tsconfig.esm.json | 8 -------- rollup/plugins/bundlePlugins.js | 2 +- tsconfig-templates/README.md | 6 +++--- tsconfig-templates/tsconfig.cjs.json | 8 -------- tsconfig-templates/tsconfig.esm.json | 8 -------- 47 files changed, 4 insertions(+), 331 deletions(-) delete mode 100644 packages/browser/tsconfig.cjs.json delete mode 100644 packages/browser/tsconfig.esm.json delete mode 100644 packages/core/tsconfig.cjs.json delete mode 100644 packages/core/tsconfig.esm.json delete mode 100644 packages/gatsby/tsconfig.cjs.json delete mode 100644 packages/gatsby/tsconfig.esm.json delete mode 100644 packages/hub/tsconfig.cjs.json delete mode 100644 packages/hub/tsconfig.esm.json delete mode 100644 packages/integrations/tsconfig.cjs.json delete mode 100644 packages/integrations/tsconfig.esm.json delete mode 100644 packages/nextjs/tsconfig.cjs.json delete mode 100644 packages/nextjs/tsconfig.esm.json delete mode 100644 packages/node/tsconfig.cjs.json delete mode 100644 packages/node/tsconfig.esm.json delete mode 100644 packages/react/tsconfig.cjs.json delete mode 100644 packages/react/tsconfig.esm.json delete mode 100644 packages/serverless/tsconfig.cjs.json delete mode 100644 packages/serverless/tsconfig.esm.json delete mode 100644 packages/tracing/tsconfig.cjs.json delete mode 100644 packages/tracing/tsconfig.esm.json delete mode 100644 packages/types/tsconfig.cjs.json delete mode 100644 packages/types/tsconfig.esm.json delete mode 100644 packages/utils/tsconfig.cjs.json delete mode 100644 packages/utils/tsconfig.esm.json delete mode 100644 packages/vue/tsconfig.cjs.json delete mode 100644 packages/vue/tsconfig.esm.json delete mode 100644 packages/wasm/tsconfig.cjs.json delete mode 100644 packages/wasm/tsconfig.esm.json delete mode 100644 tsconfig-templates/tsconfig.cjs.json delete mode 100644 tsconfig-templates/tsconfig.esm.json diff --git a/package.json b/package.json index 3d2a67cea99f..5b49d570c7fc 100644 --- a/package.json +++ b/package.json @@ -3,11 +3,8 @@ "scripts": { "build": "node ./scripts/verify-packages-versions.js && lerna run --stream --concurrency 1 --sort build", "build:bundle": "lerna run --parallel build:bundle", - "build:cjs": "lerna run --stream --concurrency 1 --sort build:cjs", "build:dev": "lerna run --stream --concurrency 1 --sort build:dev", "build:dev:filter": "lerna run --stream --concurrency 1 --sort build:dev --include-filtered-dependencies --include-filtered-dependents --scope", - "build:es5": "yarn lerna run --stream --concurrency 1 --sort build:cjs # *** backwards compatibility - remove in v7 ***", - "build:esm": "lerna run --stream --concurrency 1 --sort build:esm", "build:rollup": "lerna run --stream --concurrency 1 --sort build:rollup", "build:types": "lerna run --stream --concurrency 1 --sort build:types", "build:watch": "lerna run --parallel build:watch", diff --git a/packages/browser/package.json b/packages/browser/package.json index 17f9a9d9879e..d8e93731e155 100644 --- a/packages/browser/package.json +++ b/packages/browser/package.json @@ -45,18 +45,12 @@ "scripts": { "build": "run-p build:rollup build:bundle build:types", "build:bundle": "rollup --config rollup.bundle.config.js", - "build:cjs": "tsc -p tsconfig.cjs.json", "build:dev": "run-p build:rollup build:types", - "build:es5": "yarn build:cjs # *** backwards compatibility - remove in v7 ***", - "build:esm": "tsc -p tsconfig.esm.json", "build:rollup": "rollup -c rollup.npm.config.js", "build:types": "tsc -p tsconfig.types.json", "build:watch": "run-p build:rollup:watch build:bundle:watch build:types:watch", "build:bundle:watch": "rollup --config rollup.bundle.config.js --watch", - "build:cjs:watch": "tsc -p tsconfig.cjs.json --watch", - "build:es5:watch": "yarn build:cjs:watch # *** backwards compatibility - remove in v7 ***", "build:dev:watch": "run-p build:rollup:watch build:types:watch", - "build:esm:watch": "tsc -p tsconfig.esm.json --watch", "build:rollup:watch": "rollup -c rollup.npm.config.js --watch", "build:types:watch": "tsc -p tsconfig.types.json --watch", "build:npm": "ts-node ../../scripts/prepack.ts --bundles && npm pack ./build/npm", diff --git a/packages/browser/tsconfig.cjs.json b/packages/browser/tsconfig.cjs.json deleted file mode 100644 index 0fa3132e7510..000000000000 --- a/packages/browser/tsconfig.cjs.json +++ /dev/null @@ -1,8 +0,0 @@ -{ - "extends": "./tsconfig.json", - - "compilerOptions": { - "module": "commonjs", - "outDir": "build/npm/cjs" - } -} diff --git a/packages/browser/tsconfig.esm.json b/packages/browser/tsconfig.esm.json deleted file mode 100644 index feffe52ca581..000000000000 --- a/packages/browser/tsconfig.esm.json +++ /dev/null @@ -1,8 +0,0 @@ -{ - "extends": "./tsconfig.json", - - "compilerOptions": { - "module": "es6", - "outDir": "build/npm/esm" - } -} diff --git a/packages/core/package.json b/packages/core/package.json index df5ed748e43c..f4843db9acbb 100644 --- a/packages/core/package.json +++ b/packages/core/package.json @@ -23,17 +23,11 @@ }, "scripts": { "build": "run-p build:rollup build:types", - "build:cjs": "tsc -p tsconfig.cjs.json", "build:dev": "run-s build", - "build:es5": "yarn build:cjs # *** backwards compatibility - remove in v7 ***", - "build:esm": "tsc -p tsconfig.esm.json", "build:rollup": "rollup -c rollup.npm.config.js", "build:types": "tsc -p tsconfig.types.json", "build:watch": "run-p build:rollup:watch build:types:watch", - "build:cjs:watch": "tsc -p tsconfig.cjs.json --watch", "build:dev:watch": "run-s build:watch", - "build:es5:watch": "yarn build:cjs:watch # *** backwards compatibility - remove in v7 ***", - "build:esm:watch": "tsc -p tsconfig.esm.json --watch", "build:rollup:watch": "rollup -c rollup.npm.config.js --watch", "build:types:watch": "tsc -p tsconfig.types.json --watch", "build:npm": "ts-node ../../scripts/prepack.ts && npm pack ./build", diff --git a/packages/core/tsconfig.cjs.json b/packages/core/tsconfig.cjs.json deleted file mode 100644 index c1edc81a9657..000000000000 --- a/packages/core/tsconfig.cjs.json +++ /dev/null @@ -1,8 +0,0 @@ -{ - "extends": "./tsconfig.json", - - "compilerOptions": { - "module": "commonjs", - "outDir": "build/cjs" - } -} diff --git a/packages/core/tsconfig.esm.json b/packages/core/tsconfig.esm.json deleted file mode 100644 index 0b86c52918cc..000000000000 --- a/packages/core/tsconfig.esm.json +++ /dev/null @@ -1,8 +0,0 @@ -{ - "extends": "./tsconfig.json", - - "compilerOptions": { - "module": "es6", - "outDir": "build/esm" - } -} diff --git a/packages/gatsby/package.json b/packages/gatsby/package.json index 7c2a8aea0bc4..ce550c72aad7 100644 --- a/packages/gatsby/package.json +++ b/packages/gatsby/package.json @@ -36,18 +36,12 @@ }, "scripts": { "build": "run-p build:rollup build:types build:plugin", - "build:cjs": "tsc -p tsconfig.cjs.json", "build:dev": "run-s build", - "build:es5": "yarn build:cjs # *** backwards compatibility - remove in v7 ***", - "build:esm": "tsc -p tsconfig.esm.json", "build:plugin": "tsc -p tsconfig.plugin.json", "build:rollup": "rollup -c rollup.npm.config.js", "build:types": "tsc -p tsconfig.types.json", "build:watch": "run-p build:rollup:watch build:types:watch", - "build:cjs:watch": "tsc -p tsconfig.cjs.json --watch", "build:dev:watch": "run-s build:watch", - "build:es5:watch": "yarn build:cjs:watch # *** backwards compatibility - remove in v7 ***", - "build:esm:watch": "tsc -p tsconfig.esm.json --watch", "build:rollup:watch": "rollup -c rollup.npm.config.js --watch", "build:types:watch": "tsc -p tsconfig.types.json --watch", "build:npm": "ts-node ../../scripts/prepack.ts && npm pack ./build", diff --git a/packages/gatsby/tsconfig.cjs.json b/packages/gatsby/tsconfig.cjs.json deleted file mode 100644 index c1edc81a9657..000000000000 --- a/packages/gatsby/tsconfig.cjs.json +++ /dev/null @@ -1,8 +0,0 @@ -{ - "extends": "./tsconfig.json", - - "compilerOptions": { - "module": "commonjs", - "outDir": "build/cjs" - } -} diff --git a/packages/gatsby/tsconfig.esm.json b/packages/gatsby/tsconfig.esm.json deleted file mode 100644 index 0b86c52918cc..000000000000 --- a/packages/gatsby/tsconfig.esm.json +++ /dev/null @@ -1,8 +0,0 @@ -{ - "extends": "./tsconfig.json", - - "compilerOptions": { - "module": "es6", - "outDir": "build/esm" - } -} diff --git a/packages/hub/package.json b/packages/hub/package.json index acb20df1e41b..ba900dcd5c0d 100644 --- a/packages/hub/package.json +++ b/packages/hub/package.json @@ -22,17 +22,11 @@ }, "scripts": { "build": "run-p build:rollup build:types", - "build:cjs": "tsc -p tsconfig.cjs.json", "build:dev": "run-s build", - "build:es5": "yarn build:cjs # *** backwards compatibility - remove in v7 ***", - "build:esm": "tsc -p tsconfig.esm.json", "build:rollup": "rollup -c rollup.npm.config.js", "build:types": "tsc -p tsconfig.types.json", "build:watch": "run-p build:rollup:watch build:types:watch", - "build:cjs:watch": "tsc -p tsconfig.cjs.json --watch", "build:dev:watch": "run-s build:watch", - "build:es5:watch": "yarn build:cjs:watch # *** backwards compatibility - remove in v7 ***", - "build:esm:watch": "tsc -p tsconfig.esm.json --watch", "build:rollup:watch": "rollup -c rollup.npm.config.js --watch", "build:types:watch": "tsc -p tsconfig.types.json --watch", "build:npm": "ts-node ../../scripts/prepack.ts && npm pack ./build", diff --git a/packages/hub/tsconfig.cjs.json b/packages/hub/tsconfig.cjs.json deleted file mode 100644 index c1edc81a9657..000000000000 --- a/packages/hub/tsconfig.cjs.json +++ /dev/null @@ -1,8 +0,0 @@ -{ - "extends": "./tsconfig.json", - - "compilerOptions": { - "module": "commonjs", - "outDir": "build/cjs" - } -} diff --git a/packages/hub/tsconfig.esm.json b/packages/hub/tsconfig.esm.json deleted file mode 100644 index 0b86c52918cc..000000000000 --- a/packages/hub/tsconfig.esm.json +++ /dev/null @@ -1,8 +0,0 @@ -{ - "extends": "./tsconfig.json", - - "compilerOptions": { - "module": "es6", - "outDir": "build/esm" - } -} diff --git a/packages/integrations/package.json b/packages/integrations/package.json index 7ca2c2d31af9..1072cf8df201 100644 --- a/packages/integrations/package.json +++ b/packages/integrations/package.json @@ -27,17 +27,11 @@ "scripts": { "build": "run-p build:rollup build:types build:bundle", "build:bundle": "bash scripts/buildBundles.sh", - "build:cjs": "tsc -p tsconfig.cjs.json", "build:dev": "run-p build:rollup build:types", - "build:es5": "yarn build:cjs # *** backwards compatibility - remove in v7 ***", - "build:esm": "tsc -p tsconfig.esm.json", "build:rollup": "rollup -c rollup.npm.config.js", "build:types": "tsc -p tsconfig.types.json", "build:watch": "run-p build:rollup:watch build:types:watch", - "build:cjs:watch": "tsc -p tsconfig.cjs.json --watch", "build:dev:watch": "run-s build:watch", - "build:es5:watch": "yarn build:cjs:watch # *** backwards compatibility - remove in v7 ***", - "build:esm:watch": "tsc -p tsconfig.esm.json --watch", "build:rollup:watch": "rollup -c rollup.npm.config.js --watch", "build:types:watch": "tsc -p tsconfig.types.json --watch", "build:npm": "ts-node ../../scripts/prepack.ts --bundles && npm pack ./build/npm", diff --git a/packages/integrations/tsconfig.cjs.json b/packages/integrations/tsconfig.cjs.json deleted file mode 100644 index 0fa3132e7510..000000000000 --- a/packages/integrations/tsconfig.cjs.json +++ /dev/null @@ -1,8 +0,0 @@ -{ - "extends": "./tsconfig.json", - - "compilerOptions": { - "module": "commonjs", - "outDir": "build/npm/cjs" - } -} diff --git a/packages/integrations/tsconfig.esm.json b/packages/integrations/tsconfig.esm.json deleted file mode 100644 index feffe52ca581..000000000000 --- a/packages/integrations/tsconfig.esm.json +++ /dev/null @@ -1,8 +0,0 @@ -{ - "extends": "./tsconfig.json", - - "compilerOptions": { - "module": "es6", - "outDir": "build/npm/esm" - } -} diff --git a/packages/nextjs/package.json b/packages/nextjs/package.json index 8a682d5dbd17..d49ae66b75a3 100644 --- a/packages/nextjs/package.json +++ b/packages/nextjs/package.json @@ -44,17 +44,11 @@ }, "scripts": { "build": "run-p build:rollup build:types", - "build:cjs": "tsc -p tsconfig.cjs.json", "build:dev": "run-s build", - "build:es5": "yarn build:cjs # *** backwards compatibility - remove in v7 ***", - "build:esm": "tsc -p tsconfig.esm.json", "build:rollup": "rollup -c rollup.npm.config.js", "build:types": "tsc -p tsconfig.types.json", "build:watch": "run-p build:rollup:watch build:types:watch", - "build:cjs:watch": "tsc -p tsconfig.cjs.json --watch", "build:dev:watch": "run-s build:watch", - "build:es5:watch": "yarn build:cjs:watch # *** backwards compatibility - remove in v7 ***", - "build:esm:watch": "tsc -p tsconfig.esm.json --watch", "build:rollup:watch": "rollup -c rollup.npm.config.js --watch", "build:types:watch": "tsc -p tsconfig.types.json --watch", "build:npm": "ts-node ../../scripts/prepack.ts && npm pack ./build", diff --git a/packages/nextjs/tsconfig.cjs.json b/packages/nextjs/tsconfig.cjs.json deleted file mode 100644 index c1edc81a9657..000000000000 --- a/packages/nextjs/tsconfig.cjs.json +++ /dev/null @@ -1,8 +0,0 @@ -{ - "extends": "./tsconfig.json", - - "compilerOptions": { - "module": "commonjs", - "outDir": "build/cjs" - } -} diff --git a/packages/nextjs/tsconfig.esm.json b/packages/nextjs/tsconfig.esm.json deleted file mode 100644 index 0b86c52918cc..000000000000 --- a/packages/nextjs/tsconfig.esm.json +++ /dev/null @@ -1,8 +0,0 @@ -{ - "extends": "./tsconfig.json", - - "compilerOptions": { - "module": "es6", - "outDir": "build/esm" - } -} diff --git a/packages/node/package.json b/packages/node/package.json index 97ce2e5d541f..3a8bfc5ad79b 100644 --- a/packages/node/package.json +++ b/packages/node/package.json @@ -35,17 +35,11 @@ }, "scripts": { "build": "run-p build:rollup build:types", - "build:cjs": "tsc -p tsconfig.cjs.json", "build:dev": "run-s build", - "build:es5": "yarn build:cjs # *** backwards compatibility - remove in v7 ***", - "build:esm": "tsc -p tsconfig.esm.json", "build:rollup": "rollup -c rollup.npm.config.js", "build:types": "tsc -p tsconfig.types.json", "build:watch": "run-p build:rollup:watch build:types:watch", - "build:cjs:watch": "tsc -p tsconfig.cjs.json --watch", "build:dev:watch": "run-s build:watch", - "build:es5:watch": "yarn build:cjs:watch # *** backwards compatibility - remove in v7 ***", - "build:esm:watch": "tsc -p tsconfig.esm.json --watch", "build:rollup:watch": "rollup -c rollup.npm.config.js --watch", "build:types:watch": "tsc -p tsconfig.types.json --watch", "build:npm": "ts-node ../../scripts/prepack.ts && npm pack ./build", diff --git a/packages/node/tsconfig.cjs.json b/packages/node/tsconfig.cjs.json deleted file mode 100644 index c1edc81a9657..000000000000 --- a/packages/node/tsconfig.cjs.json +++ /dev/null @@ -1,8 +0,0 @@ -{ - "extends": "./tsconfig.json", - - "compilerOptions": { - "module": "commonjs", - "outDir": "build/cjs" - } -} diff --git a/packages/node/tsconfig.esm.json b/packages/node/tsconfig.esm.json deleted file mode 100644 index 0b86c52918cc..000000000000 --- a/packages/node/tsconfig.esm.json +++ /dev/null @@ -1,8 +0,0 @@ -{ - "extends": "./tsconfig.json", - - "compilerOptions": { - "module": "es6", - "outDir": "build/esm" - } -} diff --git a/packages/react/package.json b/packages/react/package.json index c1fccc261b9e..6a9ea910748c 100644 --- a/packages/react/package.json +++ b/packages/react/package.json @@ -49,17 +49,11 @@ }, "scripts": { "build": "run-p build:rollup build:types", - "build:cjs": "tsc -p tsconfig.cjs.json", "build:dev": "run-s build", - "build:es5": "yarn build:cjs # *** backwards compatibility - remove in v7 ***", - "build:esm": "tsc -p tsconfig.esm.json", "build:rollup": "rollup -c rollup.npm.config.js", "build:types": "tsc -p tsconfig.types.json", "build:watch": "run-p build:rollup:watch build:types:watch", - "build:cjs:watch": "tsc -p tsconfig.cjs.json --watch", "build:dev:watch": "run-s build:watch", - "build:es5:watch": "yarn build:cjs:watch # *** backwards compatibility - remove in v7 ***", - "build:esm:watch": "tsc -p tsconfig.esm.json --watch", "build:rollup:watch": "rollup -c rollup.npm.config.js --watch", "build:types:watch": "tsc -p tsconfig.types.json --watch", "build:npm": "ts-node ../../scripts/prepack.ts && npm pack ./build", diff --git a/packages/react/tsconfig.cjs.json b/packages/react/tsconfig.cjs.json deleted file mode 100644 index c1edc81a9657..000000000000 --- a/packages/react/tsconfig.cjs.json +++ /dev/null @@ -1,8 +0,0 @@ -{ - "extends": "./tsconfig.json", - - "compilerOptions": { - "module": "commonjs", - "outDir": "build/cjs" - } -} diff --git a/packages/react/tsconfig.esm.json b/packages/react/tsconfig.esm.json deleted file mode 100644 index 0b86c52918cc..000000000000 --- a/packages/react/tsconfig.esm.json +++ /dev/null @@ -1,8 +0,0 @@ -{ - "extends": "./tsconfig.json", - - "compilerOptions": { - "module": "es6", - "outDir": "build/esm" - } -} diff --git a/packages/serverless/package.json b/packages/serverless/package.json index 6496ee81fec2..508d39e0bd99 100644 --- a/packages/serverless/package.json +++ b/packages/serverless/package.json @@ -40,17 +40,11 @@ "scripts": { "build": "run-p build:rollup build:types && yarn build:awslambda-layer", "build:awslambda-layer": "node scripts/build-awslambda-layer.js", - "build:cjs": "tsc -p tsconfig.cjs.json", "build:dev": "run-p build:rollup build:types", - "build:es5": "yarn build:cjs # *** backwards compatibility - remove in v7 ***", - "build:esm": "tsc -p tsconfig.esm.json", "build:rollup": "rollup -c rollup.npm.config.js", "build:types": "tsc -p tsconfig.types.json", "build:watch": "run-p build:rollup:watch build:types:watch", - "build:cjs:watch": "tsc -p tsconfig.cjs.json --watch", "build:dev:watch": "run-s build:watch", - "build:es5:watch": "yarn build:cjs:watch # *** backwards compatibility - remove in v7 ***", - "build:esm:watch": "tsc -p tsconfig.esm.json --watch", "build:rollup:watch": "rollup -c rollup.npm.config.js --watch", "build:types:watch": "tsc -p tsconfig.types.json --watch", "build:npm": "ts-node ../../scripts/prepack.ts && npm pack ./build", diff --git a/packages/serverless/tsconfig.cjs.json b/packages/serverless/tsconfig.cjs.json deleted file mode 100644 index c1edc81a9657..000000000000 --- a/packages/serverless/tsconfig.cjs.json +++ /dev/null @@ -1,8 +0,0 @@ -{ - "extends": "./tsconfig.json", - - "compilerOptions": { - "module": "commonjs", - "outDir": "build/cjs" - } -} diff --git a/packages/serverless/tsconfig.esm.json b/packages/serverless/tsconfig.esm.json deleted file mode 100644 index 0b86c52918cc..000000000000 --- a/packages/serverless/tsconfig.esm.json +++ /dev/null @@ -1,8 +0,0 @@ -{ - "extends": "./tsconfig.json", - - "compilerOptions": { - "module": "es6", - "outDir": "build/esm" - } -} diff --git a/packages/tracing/package.json b/packages/tracing/package.json index baa3055dfb1c..8be246804f0f 100644 --- a/packages/tracing/package.json +++ b/packages/tracing/package.json @@ -28,18 +28,12 @@ "scripts": { "build": "run-p build:rollup build:types build:bundle && ts-node ../../scripts/prepack.ts --bundles #necessary for integration tests", "build:bundle": "rollup --config rollup.bundle.config.js", - "build:cjs": "tsc -p tsconfig.cjs.json", "build:dev": "run-p build:rollup build:types", - "build:es5": "yarn build:cjs # *** backwards compatibility - remove in v7 ***", - "build:esm": "tsc -p tsconfig.esm.json", "build:rollup": "rollup -c rollup.npm.config.js", "build:types": "tsc -p tsconfig.types.json", "build:watch": "run-p build:rollup:watch build:bundle:watch build:types:watch", "build:bundle:watch": "rollup --config rollup.bundle.config.js --watch", - "build:cjs:watch": "tsc -p tsconfig.cjs.json --watch", "build:dev:watch": "run-p build:rollup:watch build:types:watch", - "build:es5:watch": "yarn build:cjs:watch # *** backwards compatibility - remove in v7 ***", - "build:esm:watch": "tsc -p tsconfig.esm.json --watch", "build:rollup:watch": "rollup -c rollup.npm.config.js --watch", "build:types:watch": "tsc -p tsconfig.types.json --watch", "build:npm": "ts-node ../../scripts/prepack.ts --bundles && npm pack ./build/npm", diff --git a/packages/tracing/tsconfig.cjs.json b/packages/tracing/tsconfig.cjs.json deleted file mode 100644 index 0fa3132e7510..000000000000 --- a/packages/tracing/tsconfig.cjs.json +++ /dev/null @@ -1,8 +0,0 @@ -{ - "extends": "./tsconfig.json", - - "compilerOptions": { - "module": "commonjs", - "outDir": "build/npm/cjs" - } -} diff --git a/packages/tracing/tsconfig.esm.json b/packages/tracing/tsconfig.esm.json deleted file mode 100644 index feffe52ca581..000000000000 --- a/packages/tracing/tsconfig.esm.json +++ /dev/null @@ -1,8 +0,0 @@ -{ - "extends": "./tsconfig.json", - - "compilerOptions": { - "module": "es6", - "outDir": "build/npm/esm" - } -} diff --git a/packages/types/package.json b/packages/types/package.json index 195cbe89a788..fdeee75c3f75 100644 --- a/packages/types/package.json +++ b/packages/types/package.json @@ -17,17 +17,11 @@ }, "scripts": { "build": "run-p build:rollup build:types", - "build:cjs": "tsc -p tsconfig.cjs.json", "build:dev": "run-s build", - "build:es5": "yarn build:cjs # *** backwards compatibility - remove in v7 ***", - "build:esm": "tsc -p tsconfig.esm.json", "build:rollup": "rollup -c rollup.npm.config.js", "build:types": "tsc -p tsconfig.types.json", "build:watch": "run-p build:rollup:watch build:types:watch", - "build:cjs:watch": "tsc -p tsconfig.cjs.json --watch", "build:dev:watch": "run-s build:watch", - "build:es5:watch": "yarn build:cjs:watch # *** backwards compatibility - remove in v7 ***", - "build:esm:watch": "tsc -p tsconfig.esm.json --watch", "build:rollup:watch": "rollup -c rollup.npm.config.js --watch", "build:types:watch": "tsc -p tsconfig.types.json --watch", "build:npm": "ts-node ../../scripts/prepack.ts && npm pack ./build", diff --git a/packages/types/tsconfig.cjs.json b/packages/types/tsconfig.cjs.json deleted file mode 100644 index c1edc81a9657..000000000000 --- a/packages/types/tsconfig.cjs.json +++ /dev/null @@ -1,8 +0,0 @@ -{ - "extends": "./tsconfig.json", - - "compilerOptions": { - "module": "commonjs", - "outDir": "build/cjs" - } -} diff --git a/packages/types/tsconfig.esm.json b/packages/types/tsconfig.esm.json deleted file mode 100644 index 0b86c52918cc..000000000000 --- a/packages/types/tsconfig.esm.json +++ /dev/null @@ -1,8 +0,0 @@ -{ - "extends": "./tsconfig.json", - - "compilerOptions": { - "module": "es6", - "outDir": "build/esm" - } -} diff --git a/packages/utils/package.json b/packages/utils/package.json index 29ffb5678973..bd05f1b07a63 100644 --- a/packages/utils/package.json +++ b/packages/utils/package.json @@ -26,17 +26,11 @@ }, "scripts": { "build": "run-p build:rollup build:types", - "build:cjs": "tsc -p tsconfig.cjs.json", "build:dev": "run-s build", - "build:es5": "yarn build:cjs # *** backwards compatibility - remove in v7 ***", - "build:esm": "tsc -p tsconfig.esm.json", "build:rollup": "yarn ts-node scripts/buildRollup.ts", "build:types": "tsc -p tsconfig.types.json", "build:watch": "run-p build:rollup:watch build:types:watch", - "build:cjs:watch": "tsc -p tsconfig.cjs.json --watch", "build:dev:watch": "run-s build:watch", - "build:es5:watch": "yarn build:cjs:watch # *** backwards compatibility - remove in v7 ***", - "build:esm:watch": "tsc -p tsconfig.esm.json --watch", "build:rollup:watch": "rollup -c rollup.npm.config.js --watch", "build:types:watch": "tsc -p tsconfig.types.json --watch", "build:npm": "ts-node ../../scripts/prepack.ts && npm pack ./build", diff --git a/packages/utils/tsconfig.cjs.json b/packages/utils/tsconfig.cjs.json deleted file mode 100644 index c1edc81a9657..000000000000 --- a/packages/utils/tsconfig.cjs.json +++ /dev/null @@ -1,8 +0,0 @@ -{ - "extends": "./tsconfig.json", - - "compilerOptions": { - "module": "commonjs", - "outDir": "build/cjs" - } -} diff --git a/packages/utils/tsconfig.esm.json b/packages/utils/tsconfig.esm.json deleted file mode 100644 index 0b86c52918cc..000000000000 --- a/packages/utils/tsconfig.esm.json +++ /dev/null @@ -1,8 +0,0 @@ -{ - "extends": "./tsconfig.json", - - "compilerOptions": { - "module": "es6", - "outDir": "build/esm" - } -} diff --git a/packages/vue/package.json b/packages/vue/package.json index 20ca30b73cd4..12722d745b08 100644 --- a/packages/vue/package.json +++ b/packages/vue/package.json @@ -28,18 +28,12 @@ "scripts": { "build": "run-p build:rollup build:types", "build:bundle": "rollup --config rollup.bundle.config.js", - "build:cjs": "tsc -p tsconfig.cjs.json", "build:dev": "run-p build:rollup build:types", - "build:es5": "yarn build:cjs # *** backwards compatibility - remove in v7 ***", - "build:esm": "tsc -p tsconfig.esm.json", "build:rollup": "rollup -c rollup.npm.config.js", "build:types": "tsc -p tsconfig.types.json", "build:watch": "run-p build:rollup:watch build:types:watch", "build:bundle:watch": "rollup --config --watch", - "build:cjs:watch": "tsc -p tsconfig.cjs.json --watch", "build:dev:watch": "run-p build:rollup:watch build:types:watch", - "build:es5:watch": "yarn build:cjs:watch # *** backwards compatibility - remove in v7 ***", - "build:esm:watch": "tsc -p tsconfig.esm.json --watch", "build:rollup:watch": "rollup -c rollup.npm.config.js --watch", "build:types:watch": "tsc -p tsconfig.types.json --watch", "build:npm": "ts-node ../../scripts/prepack.ts && npm pack ./build", diff --git a/packages/vue/tsconfig.cjs.json b/packages/vue/tsconfig.cjs.json deleted file mode 100644 index c1edc81a9657..000000000000 --- a/packages/vue/tsconfig.cjs.json +++ /dev/null @@ -1,8 +0,0 @@ -{ - "extends": "./tsconfig.json", - - "compilerOptions": { - "module": "commonjs", - "outDir": "build/cjs" - } -} diff --git a/packages/vue/tsconfig.esm.json b/packages/vue/tsconfig.esm.json deleted file mode 100644 index 0b86c52918cc..000000000000 --- a/packages/vue/tsconfig.esm.json +++ /dev/null @@ -1,8 +0,0 @@ -{ - "extends": "./tsconfig.json", - - "compilerOptions": { - "module": "es6", - "outDir": "build/esm" - } -} diff --git a/packages/wasm/package.json b/packages/wasm/package.json index 6aa44b21a181..af0ebdbaf9a9 100644 --- a/packages/wasm/package.json +++ b/packages/wasm/package.json @@ -32,18 +32,12 @@ "scripts": { "build": "run-p build:rollup build:bundle build:types", "build:bundle": "rollup --config rollup.bundle.config.js", - "build:cjs": "tsc -p tsconfig.cjs.json", "build:dev": "run-p build:rollup build:types", - "build:es5": "yarn build:cjs # *** backwards compatibility - remove in v7 ***", - "build:esm": "tsc -p tsconfig.esm.json", "build:rollup": "rollup -c rollup.npm.config.js", "build:types": "tsc -p tsconfig.types.json", "build:watch": "run-p build:rollup:watch build:bundle:watch build:types:watch", "build:bundle:watch": "rollup --config rollup.bundle.config.js --watch", - "build:cjs:watch": "tsc -p tsconfig.cjs.json --watch", "build:dev:watch": "run-p build:rollup:watch build:types:watch", - "build:es5:watch": "yarn build:cjs:watch # *** backwards compatibility - remove in v7 ***", - "build:esm:watch": "tsc -p tsconfig.esm.json --watch", "build:rollup:watch": "rollup -c rollup.npm.config.js --watch", "build:types:watch": "tsc -p tsconfig.types.json --watch", "build:npm": "ts-node ../../scripts/prepack.ts --bundles && npm pack ./build/npm", diff --git a/packages/wasm/tsconfig.cjs.json b/packages/wasm/tsconfig.cjs.json deleted file mode 100644 index 0fa3132e7510..000000000000 --- a/packages/wasm/tsconfig.cjs.json +++ /dev/null @@ -1,8 +0,0 @@ -{ - "extends": "./tsconfig.json", - - "compilerOptions": { - "module": "commonjs", - "outDir": "build/npm/cjs" - } -} diff --git a/packages/wasm/tsconfig.esm.json b/packages/wasm/tsconfig.esm.json deleted file mode 100644 index feffe52ca581..000000000000 --- a/packages/wasm/tsconfig.esm.json +++ /dev/null @@ -1,8 +0,0 @@ -{ - "extends": "./tsconfig.json", - - "compilerOptions": { - "module": "es6", - "outDir": "build/npm/esm" - } -} diff --git a/rollup/plugins/bundlePlugins.js b/rollup/plugins/bundlePlugins.js index c678152a7b63..c9939ca0b4bf 100644 --- a/rollup/plugins/bundlePlugins.js +++ b/rollup/plugins/bundlePlugins.js @@ -100,7 +100,7 @@ export function makeTerserPlugin() { */ export function makeTSPlugin(jsVersion) { const baseTSPluginOptions = { - tsconfig: 'tsconfig.esm.json', + tsconfig: 'tsconfig.json', tsconfigOverride: { compilerOptions: { declaration: false, diff --git a/tsconfig-templates/README.md b/tsconfig-templates/README.md index 4cb4d29d788f..faab3d4983f8 100644 --- a/tsconfig-templates/README.md +++ b/tsconfig-templates/README.md @@ -1,5 +1,5 @@ # `tsconfig` Templates -Every package should get its own copy of these five files. Package-specific options should go in `tsconfig.json` and -test-specific options in `tsconfig.test.json`. The `cjs`, `esm`, and `types` files shouldn't need to be modified, and -only exist because tsconfigs don't support multiple inheritence. +Every package should get its own copy of these three files. Package-specific options should go in `tsconfig.json` and +test-specific options in `tsconfig.test.json`. The `types` file shouldn't need to be modified, and only exists because +tsconfigs don't support multiple inheritence. diff --git a/tsconfig-templates/tsconfig.cjs.json b/tsconfig-templates/tsconfig.cjs.json deleted file mode 100644 index 4ec31d2ff68b..000000000000 --- a/tsconfig-templates/tsconfig.cjs.json +++ /dev/null @@ -1,8 +0,0 @@ -{ - "extends": "./tsconfig.json", - - "compilerOptions": { - "module": "commonjs", - "outDir": "cjs" - } -} diff --git a/tsconfig-templates/tsconfig.esm.json b/tsconfig-templates/tsconfig.esm.json deleted file mode 100644 index b6ee3fa615c0..000000000000 --- a/tsconfig-templates/tsconfig.esm.json +++ /dev/null @@ -1,8 +0,0 @@ -{ - "extends": "./tsconfig.json", - - "compilerOptions": { - "module": "es6", - "outDir": "esm" - } -} From 23982cf1b63085e67763685c188d2d0d29ead75b Mon Sep 17 00:00:00 2001 From: Katie Byers Date: Wed, 11 May 2022 17:36:41 -0700 Subject: [PATCH 146/204] chore(dev): Remove redundant `prepublishOnly` build step in CI (#5083) Based both on the description in the comment above it and the restrictions on when it is supposed to run, it appears that the `prepublishOnly` step of the `Build` job is meant to do the same thing as the `pack` step of the `Upload Artifacts` job. "Meant" is the operative word, however, because in reality, it doesn't actually do anything - though it calls the repo-level `prepublishOnly` script, which calls the `prepublishOnly` script in every package that has one... no package has one, so it's a no-op. This removes the step and the associated repo-level yarn script, since they seem to have been replaced by the `build:npm` script called in `Upload Artifacts`. --- .github/workflows/build.yml | 8 -------- package.json | 1 - 2 files changed, 9 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 155126752160..2ea04b75ede6 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -92,14 +92,6 @@ jobs: # this file) to a constant and skip rebuilding all of the packages each time CI runs. if: steps.cache_built_packages.outputs.cache-hit == '' run: yarn build - # We are performing a `prepublishOnly` step manually because build workflow is not responsible for publishing - # the actual release. It only creates artifacts which then are uploaded and used by another workflow. - # Because of that, any `prepublishOnly` script is skipped and files it produces are not included in the tarball. - # We also cannot use `prepare` script which would be more suited, because it's run only before `pack` is called, - # and it's done from a `release` workflow and not here. - - name: Run prepublishOnly script - if: startsWith(github.ref, 'refs/heads/release/') - run: yarn prepublishOnly outputs: # this needs to be passed on, because the `needs` context only looks at direct ancestors (so steps which depend on # `job_build` can't see `job_install_deps` and what it returned) diff --git a/package.json b/package.json index 5b49d570c7fc..85d5f62e8074 100644 --- a/package.json +++ b/package.json @@ -18,7 +18,6 @@ "link:yarn": "lerna run --stream --concurrency 1 link:yarn", "lint": "lerna run --parallel lint", "lint:eslint": "lerna run --parallel lint:eslint", - "prepublishOnly": "lerna run --stream --concurrency 1 prepublishOnly", "postpublish": "make publish-docs && lerna run --stream --concurrency 1 postpublish", "test": "lerna run --ignore @sentry-internal/browser-integration-tests --ignore @sentry-internal/node-integration-tests --stream --concurrency 1 --sort test", "test-ci": "ts-node ./scripts/test.ts" From e5bfbaa2ecf94a4b9cd121530fc9282d0b1a817e Mon Sep 17 00:00:00 2001 From: Katie Byers Date: Thu, 12 May 2022 01:12:44 -0700 Subject: [PATCH 147/204] feat(dev): Add default Node version for CI jobs (#5081) In our CI workflow, we currently use version 1 of GHA's `setup-node` action, whose default Node version is 10. Though we could (and should) update to version 3 of the action, whose default is Node 16, the `setup-node` docs[1] recommend explicitly setting a version for greater control and clarity. This does so, by adding the default Node version as an env variable and using it in all jobs which don't otherwise specify their version. [1] https://github.com/actions/setup-node#usage --- .github/workflows/build.yml | 21 ++++++++++++++++++--- 1 file changed, 18 insertions(+), 3 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 2ea04b75ede6..4824bc2a5a94 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -12,6 +12,8 @@ on: required: false env: + DEFAULT_NODE_VERSION: '16' + HEAD_COMMIT: ${{ github.event.inputs.commit || github.sha }} CACHED_DEPENDENCY_PATHS: | @@ -45,6 +47,8 @@ jobs: ref: ${{ env.HEAD_COMMIT }} - name: Set up Node uses: actions/setup-node@v1 + with: + node-version: ${{ env.DEFAULT_NODE_VERSION }} # we use a hash of yarn.lock as our cache key, because if it hasn't changed, our dependencies haven't changed, # so no need to reinstall them - name: Compute dependency cache key @@ -74,6 +78,9 @@ jobs: ref: ${{ env.HEAD_COMMIT }} - name: Set up Node uses: actions/setup-node@v1 + with: + # ember won't build under node 16, at least not with the versions of the ember build tools we use + node-version: '14' - name: Check dependency cache uses: actions/cache@v2 with: @@ -110,7 +117,7 @@ jobs: - name: Set up Node uses: actions/setup-node@v1 with: - node-version: '12' + node-version: ${{ env.DEFAULT_NODE_VERSION }} - name: Check dependency cache uses: actions/cache@v2 with: @@ -141,6 +148,8 @@ jobs: ref: ${{ env.HEAD_COMMIT }} - name: Set up Node uses: actions/setup-node@v1 + with: + node-version: ${{ env.DEFAULT_NODE_VERSION }} - name: Check dependency cache uses: actions/cache@v2 with: @@ -166,6 +175,8 @@ jobs: ref: ${{ env.HEAD_COMMIT }} - name: Set up Node uses: actions/setup-node@v1 + with: + node-version: ${{ env.DEFAULT_NODE_VERSION }} - name: Check dependency cache uses: actions/cache@v2 with: @@ -192,6 +203,8 @@ jobs: ref: ${{ env.HEAD_COMMIT }} - name: Set up Node uses: actions/setup-node@v1 + with: + node-version: ${{ env.DEFAULT_NODE_VERSION }} - name: Check dependency cache uses: actions/cache@v2 with: @@ -359,7 +372,7 @@ jobs: - name: Set up Node uses: actions/setup-node@v1 with: - node-version: '16' + node-version: ${{ env.DEFAULT_NODE_VERSION }} - name: Check dependency cache uses: actions/cache@v2 with: @@ -398,6 +411,8 @@ jobs: ref: ${{ env.HEAD_COMMIT }} - name: Set up Node uses: actions/setup-node@v1 + with: + node-version: ${{ env.DEFAULT_NODE_VERSION }} - name: Check dependency cache uses: actions/cache@v2 with: @@ -430,7 +445,7 @@ jobs: - name: Set up Node uses: actions/setup-node@v1 with: - node-version: '16' + node-version: ${{ env.DEFAULT_NODE_VERSION }} - name: Check dependency cache uses: actions/cache@v2 with: From d9722dca47348a376d85ca978800cb93b162b27f Mon Sep 17 00:00:00 2001 From: Katie Byers Date: Thu, 12 May 2022 01:18:35 -0700 Subject: [PATCH 148/204] ref(dev): Use `lerna exec` for top-level `link:yarn` yarn script (#5084) Many of our packages have a `link:yarn` yarn script, which runs `yarn link`. At first this is puzzling (why is it better to type `yarn link:yarn` than `yarn link`?) until you realize that they're only there in aid of the repo-level `link:yarn` script, which allows you to set up the entire repo for linking into a test project in one go. Convenient! But also accomplishable without all of the package-level scripts, by switching from `lerna run` to `lerna exec`, which will run a shell command in every package. This makes that change, and removes the unnecessary package-level scripts. (While it's true that this means that `yarn link` is run in the two integration test packages (where it wasn't before), that's a harmless change, as all `yarn linnk` actually does is create a symlink in `~/.config/yarn/link/`.) --- package.json | 2 +- packages/angular/package.json | 1 - packages/browser/package.json | 1 - packages/core/package.json | 1 - packages/ember/package.json | 1 - packages/eslint-config-sdk/package.json | 1 - packages/eslint-plugin-sdk/package.json | 1 - packages/gatsby/package.json | 1 - packages/hub/package.json | 1 - packages/integrations/package.json | 1 - packages/nextjs/package.json | 1 - packages/node/package.json | 1 - packages/react/package.json | 1 - packages/serverless/package.json | 1 - packages/tracing/package.json | 1 - packages/types/package.json | 1 - packages/typescript/package.json | 1 - packages/utils/package.json | 1 - packages/vue/package.json | 1 - packages/wasm/package.json | 1 - 20 files changed, 1 insertion(+), 20 deletions(-) diff --git a/package.json b/package.json index 85d5f62e8074..9d6710419d65 100644 --- a/package.json +++ b/package.json @@ -15,7 +15,7 @@ "clean": "lerna run --parallel clean && lerna clean --yes && yarn rimraf eslintcache", "codecov": "codecov", "fix": "lerna run --parallel fix", - "link:yarn": "lerna run --stream --concurrency 1 link:yarn", + "link:yarn": "lerna exec --parallel yarn link", "lint": "lerna run --parallel lint", "lint:eslint": "lerna run --parallel lint:eslint", "postpublish": "make publish-docs && lerna run --stream --concurrency 1 postpublish", diff --git a/packages/angular/package.json b/packages/angular/package.json index 941506e5871a..9899b961a323 100644 --- a/packages/angular/package.json +++ b/packages/angular/package.json @@ -49,7 +49,6 @@ "fix": "run-s fix:eslint fix:prettier", "fix:eslint": "eslint . --format stylish --fix", "fix:prettier": "prettier --write \"{src,test,scripts}/**/*.ts\"", - "link:yarn": "yarn link", "lint": "run-s lint:prettier lint:eslint", "lint:eslint": "eslint . --cache --cache-location '../../eslintcache/' --format stylish", "lint:prettier": "prettier --check \"{src,test,scripts}/**/*.ts\"" diff --git a/packages/browser/package.json b/packages/browser/package.json index d8e93731e155..34d43259abcf 100644 --- a/packages/browser/package.json +++ b/packages/browser/package.json @@ -59,7 +59,6 @@ "fix": "run-s fix:eslint fix:prettier", "fix:eslint": "eslint . --format stylish --fix", "fix:prettier": "prettier --write \"{src,test,scripts}/**/*.ts\"", - "link:yarn": "yarn link", "lint": "run-s lint:prettier lint:eslint", "lint:eslint": "eslint . --cache --cache-location '../../eslintcache/' --format stylish", "lint:prettier": "prettier --check \"{src,test,scripts}/**/*.ts\"", diff --git a/packages/core/package.json b/packages/core/package.json index f4843db9acbb..83b6a0131dfe 100644 --- a/packages/core/package.json +++ b/packages/core/package.json @@ -36,7 +36,6 @@ "fix": "run-s fix:eslint fix:prettier", "fix:eslint": "eslint . --format stylish --fix", "fix:prettier": "prettier --write \"{src,test,scripts}/**/*.ts\"", - "link:yarn": "yarn link", "lint": "run-s lint:prettier lint:eslint", "lint:eslint": "eslint . --cache --cache-location '../../eslintcache/' --format stylish", "lint:prettier": "prettier --check \"{src,test,scripts}/**/*.ts\"", diff --git a/packages/ember/package.json b/packages/ember/package.json index 6f4d1457aeb6..bb65fcbfc880 100644 --- a/packages/ember/package.json +++ b/packages/ember/package.json @@ -20,7 +20,6 @@ "build": "ember build --environment=production", "build:npm": "ember ts:precompile && npm pack && ember ts:clean", "clean": "yarn rimraf sentry-ember-*.tgz", - "link:yarn": "yarn link", "lint": "npm-run-all --aggregate-output --continue-on-error --parallel lint:*", "lint:hbs": "ember-template-lint .", "lint:js": "eslint . --cache --cache-location '../../eslintcache/'", diff --git a/packages/eslint-config-sdk/package.json b/packages/eslint-config-sdk/package.json index d30b4614ce23..a87e8f205972 100644 --- a/packages/eslint-config-sdk/package.json +++ b/packages/eslint-config-sdk/package.json @@ -37,7 +37,6 @@ }, "scripts": { "clean": "yarn rimraf sentry-internal-eslint-config-sdk-*.tgz", - "link:yarn": "yarn link", "lint": "prettier --check \"**/*.js\"", "fix": "prettier --write \"**/*.js\"", "build:npm": "npm pack", diff --git a/packages/eslint-plugin-sdk/package.json b/packages/eslint-plugin-sdk/package.json index 4609b0399fea..22f14a3bc117 100644 --- a/packages/eslint-plugin-sdk/package.json +++ b/packages/eslint-plugin-sdk/package.json @@ -26,7 +26,6 @@ }, "scripts": { "clean": "yarn rimraf sentry-internal-eslint-plugin-sdk-*.tgz", - "link:yarn": "yarn link", "fix": "run-s fix:eslint fix:prettier", "fix:eslint": "eslint . --format stylish --fix", "fix:prettier": "prettier --write \"{src,test}/**/*.js\"", diff --git a/packages/gatsby/package.json b/packages/gatsby/package.json index ce550c72aad7..f4703f14cae2 100644 --- a/packages/gatsby/package.json +++ b/packages/gatsby/package.json @@ -50,7 +50,6 @@ "fix": "run-s fix:eslint fix:prettier", "fix:eslint": "eslint . --format stylish --fix", "fix:prettier": "prettier --write \"{src,test,scripts}/**/*.ts\"", - "link:yarn": "yarn link", "lint": "run-s lint:prettier lint:eslint", "lint:eslint": "eslint . --cache --cache-location '../../eslintcache/' --format stylish", "lint:prettier": "prettier --check \"{src,test,scripts}/**/*.ts\"", diff --git a/packages/hub/package.json b/packages/hub/package.json index ba900dcd5c0d..7885c26df368 100644 --- a/packages/hub/package.json +++ b/packages/hub/package.json @@ -35,7 +35,6 @@ "fix": "run-s fix:eslint fix:prettier", "fix:eslint": "eslint . --format stylish --fix", "fix:prettier": "prettier --write \"{src,test,scripts}/**/*.ts\"", - "link:yarn": "yarn link", "lint": "run-s lint:prettier lint:eslint", "lint:eslint": "eslint . --cache --cache-location '../../eslintcache/' --format stylish", "lint:prettier": "prettier --check \"{src,test}/**/*.ts\"", diff --git a/packages/integrations/package.json b/packages/integrations/package.json index 1072cf8df201..17ebabcda0cc 100644 --- a/packages/integrations/package.json +++ b/packages/integrations/package.json @@ -40,7 +40,6 @@ "fix": "run-s fix:eslint fix:prettier", "fix:eslint": "eslint . --format stylish --fix", "fix:prettier": "prettier --write \"{src,test,scripts}/**/*.ts\"", - "link:yarn": "yarn link", "lint": "run-s lint:prettier lint:eslint", "lint:eslint": "eslint . --cache --cache-location '../../eslintcache/' --format stylish", "lint:prettier": "prettier --check \"{src,test,scripts}/**/*.ts\"", diff --git a/packages/nextjs/package.json b/packages/nextjs/package.json index d49ae66b75a3..3256eae476aa 100644 --- a/packages/nextjs/package.json +++ b/packages/nextjs/package.json @@ -57,7 +57,6 @@ "fix": "run-s fix:eslint fix:prettier", "fix:eslint": "eslint . --format stylish --fix", "fix:prettier": "prettier --write \"{src,test,scripts}/**/*.ts\"", - "link:yarn": "yarn link", "lint": "run-s lint:prettier lint:eslint", "lint:eslint": "eslint . --cache --cache-location '../../eslintcache/' --format stylish", "lint:prettier": "prettier --check \"{src,test,scripts}/**/*.ts\"", diff --git a/packages/node/package.json b/packages/node/package.json index 3a8bfc5ad79b..fd0130f54785 100644 --- a/packages/node/package.json +++ b/packages/node/package.json @@ -48,7 +48,6 @@ "fix": "run-s fix:eslint fix:prettier", "fix:eslint": "eslint . --format stylish --fix", "fix:prettier": "prettier --write \"{src,test,scripts}/**/*.ts\"", - "link:yarn": "yarn link", "lint": "run-s lint:prettier lint:eslint", "lint:eslint": "eslint . --cache --cache-location '../../eslintcache/' --format stylish", "lint:prettier": "prettier --check \"{src,test,scripts}/**/*.ts\"", diff --git a/packages/react/package.json b/packages/react/package.json index 6a9ea910748c..13a14ef0ca7c 100644 --- a/packages/react/package.json +++ b/packages/react/package.json @@ -62,7 +62,6 @@ "fix": "run-s fix:eslint fix:prettier", "fix:eslint": "eslint . --format stylish --fix", "fix:prettier": "prettier --write \"{src,test,scripts}/**/*.ts\"", - "link:yarn": "yarn link", "lint": "run-s lint:prettier lint:eslint", "lint:eslint": "eslint . --cache --cache-location '../../eslintcache/' --format stylish", "lint:prettier": "prettier --check \"{src,test,scripts}/**/*.ts\"", diff --git a/packages/serverless/package.json b/packages/serverless/package.json index 508d39e0bd99..6fe8c659780c 100644 --- a/packages/serverless/package.json +++ b/packages/serverless/package.json @@ -53,7 +53,6 @@ "fix": "run-s fix:eslint fix:prettier", "fix:eslint": "eslint . --format stylish --fix", "fix:prettier": "prettier --write \"{src,test,scripts}/**/*.ts\"", - "link:yarn": "yarn link", "lint": "run-s lint:prettier lint:eslint", "lint:eslint": "eslint . --cache --cache-location '../../eslintcache/' --format stylish", "lint:prettier": "prettier --check \"{src,test,scripts}/**/*.ts\"", diff --git a/packages/tracing/package.json b/packages/tracing/package.json index 8be246804f0f..840a55aa6326 100644 --- a/packages/tracing/package.json +++ b/packages/tracing/package.json @@ -42,7 +42,6 @@ "fix": "run-s fix:eslint fix:prettier", "fix:eslint": "eslint . --format stylish --fix", "fix:prettier": "prettier --write \"{src,test,scripts}/**/*.ts\"", - "link:yarn": "yarn link", "lint": "run-s lint:prettier lint:eslint", "lint:eslint": "eslint . --cache --cache-location '../../eslintcache/' --format stylish", "lint:prettier": "prettier --check \"{src,test,scripts}/**/*.ts\"", diff --git a/packages/types/package.json b/packages/types/package.json index fdeee75c3f75..e32547bab2c5 100644 --- a/packages/types/package.json +++ b/packages/types/package.json @@ -26,7 +26,6 @@ "build:types:watch": "tsc -p tsconfig.types.json --watch", "build:npm": "ts-node ../../scripts/prepack.ts && npm pack ./build", "clean": "rimraf build sentry-types-*.tgz", - "link:yarn": "yarn link", "lint": "run-s lint:prettier lint:eslint", "lint:eslint": "eslint . --cache --cache-location '../../eslintcache/' --format stylish", "lint:prettier": "prettier --check \"{src,test,scripts}/**/*.ts\"", diff --git a/packages/typescript/package.json b/packages/typescript/package.json index c540c8d2ff48..1c3e71b8778c 100644 --- a/packages/typescript/package.json +++ b/packages/typescript/package.json @@ -15,7 +15,6 @@ }, "scripts": { "clean": "yarn rimraf sentry-internal-typescript-*.tgz", - "link:yarn": "yarn link", "build:npm": "npm pack" }, "volta": { diff --git a/packages/utils/package.json b/packages/utils/package.json index bd05f1b07a63..8ee24544ce98 100644 --- a/packages/utils/package.json +++ b/packages/utils/package.json @@ -39,7 +39,6 @@ "fix": "run-s fix:eslint fix:prettier", "fix:eslint": "eslint . --format stylish --fix", "fix:prettier": "prettier --write \"{src,test,scripts}/**/*.ts\"", - "link:yarn": "yarn link", "lint": "run-s lint:prettier lint:eslint", "lint:eslint": "eslint . --cache --cache-location '../../eslintcache/' --format stylish", "lint:prettier": "prettier --check \"{src,test,scripts}/**/*.ts\"", diff --git a/packages/vue/package.json b/packages/vue/package.json index 12722d745b08..2a95c8aedaa4 100644 --- a/packages/vue/package.json +++ b/packages/vue/package.json @@ -42,7 +42,6 @@ "fix": "run-s fix:eslint fix:prettier", "fix:eslint": "eslint . --format stylish --fix", "fix:prettier": "prettier --write \"{src,test,scripts}/**/*.ts\"", - "link:yarn": "yarn link", "lint": "run-s lint:prettier lint:eslint", "lint:eslint": "eslint . --cache --cache-location '../../eslintcache/' --format stylish", "lint:prettier": "prettier --check \"{src,test,scripts}/**/*.ts\"", diff --git a/packages/wasm/package.json b/packages/wasm/package.json index af0ebdbaf9a9..cd072a0eb67e 100644 --- a/packages/wasm/package.json +++ b/packages/wasm/package.json @@ -46,7 +46,6 @@ "fix": "run-s fix:eslint fix:prettier", "fix:eslint": "eslint . --format stylish --fix", "fix:prettier": "prettier --write \"{src,test,scripts}/**/*.ts\"", - "link:yarn": "yarn link", "lint": "run-s lint:prettier lint:eslint", "lint:eslint": "eslint . --cache --cache-location '../../eslintcache/' --format stylish", "lint:prettier": "prettier --check \"{src,test,scripts}/**/*.ts\"", From e39b3f696201885610b932ca2ae376ca299be1d5 Mon Sep 17 00:00:00 2001 From: Katie Byers Date: Thu, 12 May 2022 01:20:40 -0700 Subject: [PATCH 149/204] feat(build): Add args and templates to rollup debugger plugin (#5085) This makes two improvements to the rollup debugging plugin, which allows you to stick a breakpoint into any phase of the build process: - It now collects the arguments passed to the hook function for that phase, so they can be examined in the debugger. - The docstring now includes pre-built copies of the plugin (or, rather, calls to the plugin factory function) for every hook, so you can just copy any that you need over into the helper functions which create the rollup config. --- rollup/plugins/npmPlugins.js | 33 +++++++++++++++++++++++++++++++-- 1 file changed, 31 insertions(+), 2 deletions(-) diff --git a/rollup/plugins/npmPlugins.js b/rollup/plugins/npmPlugins.js index eec8eab81cda..1656feaab53b 100644 --- a/rollup/plugins/npmPlugins.js +++ b/rollup/plugins/npmPlugins.js @@ -50,15 +50,44 @@ export function makeConstToVarPlugin() { /** * Create a plugin which can be used to pause the build process at the given hook. * - * Hooks can be found here: https://rollupjs.org/guide/en/#build-hooks + * Hooks can be found here: https://rollupjs.org/guide/en/#build-hooks. * * @param hookName The name of the hook at which to pause. * @returns A plugin which inserts a debugger statement in the phase represented by the given hook + * + * For convenience, here are pre-built debuggers for every hook: + * + * makeDebuggerPlugin('buildStart'), + * makeDebuggerPlugin('options'), + * makeDebuggerPlugin('resolveId'), + * makeDebuggerPlugin('resolveDynamicImport'), + * makeDebuggerPlugin('load'), + * makeDebuggerPlugin('transform'), + * makeDebuggerPlugin('shouldTransformCachedModule'), + * makeDebuggerPlugin('moduleParsed'), + * makeDebuggerPlugin('buildEnd'), + * makeDebuggerPlugin('watchChange'), + * makeDebuggerPlugin('closeWatcher'), + * makeDebuggerPlugin('outputOptions'), + * makeDebuggerPlugin('renderStart'), + * makeDebuggerPlugin('banner'), + * makeDebuggerPlugin('footer'), + * makeDebuggerPlugin('intro'), + * makeDebuggerPlugin('outro'), + * makeDebuggerPlugin('augmentChunkHash'), + * makeDebuggerPlugin('renderDynamicImport'), + * makeDebuggerPlugin('resolveFileUrl'), + * makeDebuggerPlugin('resolveImportMeta'), + * makeDebuggerPlugin('renderChunk'), + * makeDebuggerPlugin('renderError'), + * makeDebuggerPlugin('generateBundle'), + * makeDebuggerPlugin('writeBundle'), + * makeDebuggerPlugin('closeBundle'), */ export function makeDebuggerPlugin(hookName) { return { name: 'debugger-plugin', - [hookName]: () => { + [hookName]: (..._args) => { // eslint-disable-next-line no-debugger debugger; return null; From 042e96eba5d88bda3690937a55f219a0c50bb6fb Mon Sep 17 00:00:00 2001 From: Katie Byers Date: Thu, 12 May 2022 01:21:53 -0700 Subject: [PATCH 150/204] fix(types): Make `@sentry/types` a runtime dependency in gatsby and nextjs (#5086) In order that it be installed with the SDK, we make the types package a runtime/regular dependency of all of our other packages, so it's there for anyone using the SDK in a TS app. In the gatsby and nextjs packages, though, it's listed as a dev dependency, and therefore won't be installed. This corrects that by moving it to be a regular dependency in both packages. --- packages/gatsby/package.json | 2 +- packages/nextjs/package.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/gatsby/package.json b/packages/gatsby/package.json index f4703f14cae2..b8bb4758d42a 100644 --- a/packages/gatsby/package.json +++ b/packages/gatsby/package.json @@ -22,6 +22,7 @@ "dependencies": { "@sentry/react": "7.0.0-beta.0", "@sentry/tracing": "7.0.0-beta.0", + "@sentry/types": "7.0.0-beta.0", "@sentry/utils": "7.0.0-beta.0", "@sentry/webpack-plugin": "1.18.9" }, @@ -30,7 +31,6 @@ "react": "15.x || 16.x || 17.x || 18.x" }, "devDependencies": { - "@sentry/types": "7.0.0-beta.0", "@testing-library/react": "^13.0.0", "react": "^18.0.0" }, diff --git a/packages/nextjs/package.json b/packages/nextjs/package.json index 3256eae476aa..de56d10fc1ef 100644 --- a/packages/nextjs/package.json +++ b/packages/nextjs/package.json @@ -25,10 +25,10 @@ "@sentry/tracing": "7.0.0-beta.0", "@sentry/utils": "7.0.0-beta.0", "@sentry/webpack-plugin": "1.18.9", + "@sentry/types": "7.0.0-beta.0", "tslib": "^1.9.3" }, "devDependencies": { - "@sentry/types": "7.0.0-beta.0", "@types/webpack": "^4.41.31", "next": "10.1.3" }, From f4a18406cc9807a57ac9393780f40729d94951be Mon Sep 17 00:00:00 2001 From: Katie Byers Date: Thu, 12 May 2022 01:24:17 -0700 Subject: [PATCH 151/204] ref(utils): Remove `Object.setPrototypeOf` polyfill (#5087) Now that we're ES6-only, we no longer need to polyfill `Object.setPrototypeOf`. (The last browser to gain support for it did so in September of 2015[1].) [1] https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/setPrototypeOf#browser_compatibility --- packages/utils/src/error.ts | 4 +--- packages/utils/src/polyfill.ts | 27 --------------------------- 2 files changed, 1 insertion(+), 30 deletions(-) delete mode 100644 packages/utils/src/polyfill.ts diff --git a/packages/utils/src/error.ts b/packages/utils/src/error.ts index f913d80a7438..30b35c115d14 100644 --- a/packages/utils/src/error.ts +++ b/packages/utils/src/error.ts @@ -1,5 +1,3 @@ -import { setPrototypeOf } from './polyfill'; - /** An error emitted by Sentry SDKs and related utilities. */ export class SentryError extends Error { /** Display name of this error instance. */ @@ -9,6 +7,6 @@ export class SentryError extends Error { super(message); this.name = new.target.prototype.constructor.name; - setPrototypeOf(this, new.target.prototype); + Object.setPrototypeOf(this, new.target.prototype); } } diff --git a/packages/utils/src/polyfill.ts b/packages/utils/src/polyfill.ts deleted file mode 100644 index a11b47fc55e1..000000000000 --- a/packages/utils/src/polyfill.ts +++ /dev/null @@ -1,27 +0,0 @@ -export const setPrototypeOf = - Object.setPrototypeOf || ({ __proto__: [] } instanceof Array ? setProtoOf : mixinProperties); - -/** - * setPrototypeOf polyfill using __proto__ - */ -// eslint-disable-next-line @typescript-eslint/ban-types -function setProtoOf(obj: TTarget, proto: TProto): TTarget & TProto { - // @ts-ignore __proto__ does not exist on obj - obj.__proto__ = proto; - return obj as TTarget & TProto; -} - -/** - * setPrototypeOf polyfill using mixin - */ -// eslint-disable-next-line @typescript-eslint/ban-types -function mixinProperties(obj: TTarget, proto: TProto): TTarget & TProto { - for (const prop in proto) { - if (!Object.prototype.hasOwnProperty.call(obj, prop)) { - // @ts-ignore typescript complains about indexing so we remove - obj[prop] = proto[prop]; - } - } - - return obj as TTarget & TProto; -} From 1badac1745ef16517628d895a354d1bd9a1dfabd Mon Sep 17 00:00:00 2001 From: Katie Byers Date: Thu, 12 May 2022 01:25:53 -0700 Subject: [PATCH 152/204] ref(node): Improve Node manual test logging (#5088) This makes some small changes to the logs emitted by the manual tests in the node package, to make it a little cleaner and easier to see what's what. Included changes: - Use `colorize` function from release health tests in all test suites. - Capture and display console logs in express test. - Standardize success message across test suites. - Use whitespace to separate each suite's logs from the next. --- packages/node/test/manual/colorize.js | 16 ++++++++++++++++ .../manual/express-scope-separation/start.js | 5 +++-- .../node/test/manual/release-health/runner.js | 16 +--------------- .../node/test/manual/webpack-domain/index.js | 7 ++++--- .../node/test/manual/webpack-domain/npm-build.js | 2 +- 5 files changed, 25 insertions(+), 21 deletions(-) create mode 100644 packages/node/test/manual/colorize.js diff --git a/packages/node/test/manual/colorize.js b/packages/node/test/manual/colorize.js new file mode 100644 index 000000000000..632d20aba4f7 --- /dev/null +++ b/packages/node/test/manual/colorize.js @@ -0,0 +1,16 @@ +const COLOR_RESET = '\x1b[0m'; +const COLORS = { + green: '\x1b[32m', + red: '\x1b[31m', + yellow: '\x1b[33m', +}; + +function colorize(str, color) { + if (!(color in COLORS)) { + throw new Error(`Unknown color. Available colors: ${Object.keys(COLORS).join(', ')}`); + } + + return `${COLORS[color]}${str}${COLOR_RESET}`; +} + +module.exports = { colorize }; diff --git a/packages/node/test/manual/express-scope-separation/start.js b/packages/node/test/manual/express-scope-separation/start.js index faf7b381ecbc..b85d68c7b1f8 100644 --- a/packages/node/test/manual/express-scope-separation/start.js +++ b/packages/node/test/manual/express-scope-separation/start.js @@ -2,13 +2,14 @@ const http = require('http'); const express = require('express'); const app = express(); const Sentry = require('../../../build/cjs'); +const { colorize } = require('../colorize'); // 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.error = () => null; function assertTags(actual, expected) { if (JSON.stringify(actual) !== JSON.stringify(expected)) { - console.error('FAILED: Scope contains incorrect tags'); + console.log(colorize('FAILED: Scope contains incorrect tags\n', 'red')); process.exit(1); } } @@ -20,7 +21,7 @@ function makeDummyTransport() { --remaining; if (!remaining) { - console.error('SUCCESS: All scopes contain correct tags'); + console.log(colorize('PASSED: All scopes contain correct tags\n', 'green')); server.close(); process.exit(0); } diff --git a/packages/node/test/manual/release-health/runner.js b/packages/node/test/manual/release-health/runner.js index ecbcf7fe15bc..c8ecab182859 100644 --- a/packages/node/test/manual/release-health/runner.js +++ b/packages/node/test/manual/release-health/runner.js @@ -1,21 +1,7 @@ const fs = require('fs'); const path = require('path'); const { spawn } = require('child_process'); - -const COLOR_RESET = '\x1b[0m'; -const COLORS = { - green: '\x1b[32m', - red: '\x1b[31m', - yellow: '\x1b[33m', -}; - -const colorize = (str, color) => { - if (!(color in COLORS)) { - throw new Error(`Unknown color. Available colors: ${Object.keys(COLORS).join(', ')}`); - } - - return `${COLORS[color]}${str}${COLOR_RESET}`; -}; +const { colorize } = require('../colorize'); const scenariosDirs = ['session-aggregates', 'single-session']; const scenarios = []; diff --git a/packages/node/test/manual/webpack-domain/index.js b/packages/node/test/manual/webpack-domain/index.js index 09ed18f3166f..5d3968106bab 100644 --- a/packages/node/test/manual/webpack-domain/index.js +++ b/packages/node/test/manual/webpack-domain/index.js @@ -1,4 +1,5 @@ const Sentry = require('../../../build/cjs'); +const { colorize } = require('../colorize'); let remaining = 2; @@ -7,7 +8,7 @@ function makeDummyTransport() { --remaining; if (!remaining) { - console.error('SUCCESS: Webpack Node Domain test OK!'); + console.log(colorize('PASSED: Webpack Node Domain test OK!\n', 'green')); process.exit(0); } @@ -23,13 +24,13 @@ Sentry.init({ beforeSend(event) { if (event.message === 'inside') { if (event.tags.a !== 'x' && event.tags.b !== 'c') { - console.error('FAILED: Scope contains incorrect tags'); + console.log(colorize('FAILED: Scope contains incorrect tags\n', 'red')); process.exit(1); } } if (event.message === 'outside') { if (event.tags.a !== 'b') { - console.error('FAILED: Scope contains incorrect tags'); + console.log(colorize('FAILED: Scope contains incorrect tags\n', 'red')); process.exit(1); } } diff --git a/packages/node/test/manual/webpack-domain/npm-build.js b/packages/node/test/manual/webpack-domain/npm-build.js index 923d437212c5..cf01a15d6ef1 100644 --- a/packages/node/test/manual/webpack-domain/npm-build.js +++ b/packages/node/test/manual/webpack-domain/npm-build.js @@ -39,7 +39,7 @@ webpack( function runTests() { try { - execSync('node ' + path.resolve(__dirname, 'dist', 'bundle.js')); + execSync('node ' + path.resolve(__dirname, 'dist', 'bundle.js'), { stdio: 'inherit' }); } catch (_) { process.exit(1); } From 28c09ad95bfb40f50b91f1d1199ac49cb8e66d3b Mon Sep 17 00:00:00 2001 From: Luca Forstner Date: Thu, 12 May 2022 14:38:58 +0200 Subject: [PATCH 153/204] ref(core): Log warning on NOK transport response (#5091) --- packages/core/src/transports/base.ts | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/packages/core/src/transports/base.ts b/packages/core/src/transports/base.ts index 08a3f5e33a37..8b435ed77541 100644 --- a/packages/core/src/transports/base.ts +++ b/packages/core/src/transports/base.ts @@ -71,6 +71,11 @@ export function createTransport( const requestTask = (): PromiseLike => makeRequest({ body: serializeEnvelope(filteredEnvelope) }).then( response => { + // We don't want to throw on NOK responses, but we want to at least log them + if (response.statusCode !== undefined && (response.statusCode < 200 || response.statusCode >= 300)) { + IS_DEBUG_BUILD && logger.warn(`Sentry responded with status code ${response.statusCode} to sent event.`); + } + rateLimits = updateRateLimits(rateLimits, response); }, error => { From 845346a96276c18aebd2ac068063ee740833a7aa Mon Sep 17 00:00:00 2001 From: Katie Byers Date: Thu, 12 May 2022 06:25:49 -0700 Subject: [PATCH 154/204] fix(dev): Restrict size check action to PRs (#5082) The size check action we use in our main CI workflow will throw an error if run outside of the context of a PR (as when running CI manually, for example). As a result, the overall run is always marked as a failure, even when all other jobs passed. This restricts the action to running on PRs, to avoid that problem. --- .github/workflows/build.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 4824bc2a5a94..e7f3bf5ad6c1 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -109,6 +109,8 @@ jobs: needs: job_build timeout-minutes: 15 runs-on: ubuntu-latest + # Size Check will error out outside of the context of a PR + if: ${{ github.event_name == 'pull_request' }} steps: - name: Check out current commit (${{ env.HEAD_COMMIT }}) uses: actions/checkout@v2 From 10bca50bc34440f4d3442b8c7dfb749e7b5b9471 Mon Sep 17 00:00:00 2001 From: Abhijeet Prasad Date: Thu, 12 May 2022 11:58:30 -0400 Subject: [PATCH 155/204] meta: 7.0.0-beta.1 changelog (#5092) --- CHANGELOG.md | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 15c23e44e2e9..44499728af0e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,24 @@ - "You miss 100 percent of the chances you don't take. — Wayne Gretzky" — Michael Scott +## 7.0.0-beta.1 + +- **(breaking)** ref: Remove critical severity (#5032) +- **(breaking)** ref: Delete unneeded SDK_NAME (#5040) +- **(breaking)** ref(browser): Rename UserAgent integration to HttpContext (#5027) +- **(breaking)** ref(hub): Convert Session class to object and functions (#5054) +- **(breaking)** ref(node): Explicitly pass down node transport options (#5057) +- **(breaking)** ref(tracing): Reset IdleTimeout based on activities count (#5044) +- ref(browser): Unify BrowserTransportOptions (#5058) +- feat(build): Vendor polyfills injected during build (#5051) +- ref(core): Log warning on NOK transport response (#5091) +- fix(integrations): Mark ExtraErrorData as already normalized (#5053) +- feat(react): Add react-router-v6 integration (#5042) +- fix(tracing): Remove isInstanceOf check in Hub constructor (#5046) +- fix(utils): Consider 429 responses in transports (#5062) +- ref(utils): Clean up dangerous type casts in object helper file (#5047) +- ref(utils): Add logic to enable skipping of normalization (#5052) + ## 7.0.0-beta.0 - **(breaking)**: ref: Make it easier to use stackParser (#5015) From 30a909243aa58135f3148671f33ddc4ad29b50a2 Mon Sep 17 00:00:00 2001 From: getsentry-bot Date: Thu, 12 May 2022 16:30:46 +0000 Subject: [PATCH 156/204] release: 7.0.0-beta.1 --- lerna.json | 2 +- packages/angular/package.json | 8 ++++---- packages/browser/package.json | 8 ++++---- packages/core/package.json | 8 ++++---- packages/core/src/version.ts | 2 +- packages/ember/package.json | 10 +++++----- packages/eslint-config-sdk/package.json | 6 +++--- packages/eslint-plugin-sdk/package.json | 2 +- packages/gatsby/package.json | 10 +++++----- packages/hub/package.json | 6 +++--- packages/integration-tests/package.json | 2 +- packages/integrations/package.json | 6 +++--- packages/nextjs/package.json | 18 +++++++++--------- packages/node-integration-tests/package.json | 2 +- packages/node/package.json | 10 +++++----- packages/react/package.json | 8 ++++---- packages/serverless/package.json | 10 +++++----- packages/tracing/package.json | 10 +++++----- packages/types/package.json | 2 +- packages/typescript/package.json | 2 +- packages/utils/package.json | 4 ++-- packages/vue/package.json | 10 +++++----- packages/wasm/package.json | 8 ++++---- 23 files changed, 77 insertions(+), 77 deletions(-) diff --git a/lerna.json b/lerna.json index a2a644cee9c8..a36faf95be31 100644 --- a/lerna.json +++ b/lerna.json @@ -1,6 +1,6 @@ { "lerna": "3.4.0", - "version": "7.0.0-beta.0", + "version": "7.0.0-beta.1", "packages": "packages/*", "npmClient": "yarn", "useWorkspaces": true diff --git a/packages/angular/package.json b/packages/angular/package.json index 9899b961a323..24952960e55b 100644 --- a/packages/angular/package.json +++ b/packages/angular/package.json @@ -1,6 +1,6 @@ { "name": "@sentry/angular", - "version": "7.0.0-beta.0", + "version": "7.0.0-beta.1", "description": "Official Sentry SDK for Angular", "repository": "git://github.com/getsentry/sentry-javascript.git", "homepage": "https://github.com/getsentry/sentry-javascript/tree/master/packages/angular", @@ -21,9 +21,9 @@ "rxjs": "^6.5.5 || ^7.x" }, "dependencies": { - "@sentry/browser": "7.0.0-beta.0", - "@sentry/types": "7.0.0-beta.0", - "@sentry/utils": "7.0.0-beta.0", + "@sentry/browser": "7.0.0-beta.1", + "@sentry/types": "7.0.0-beta.1", + "@sentry/utils": "7.0.0-beta.1", "tslib": "^2.0.0" }, "devDependencies": { diff --git a/packages/browser/package.json b/packages/browser/package.json index 34d43259abcf..05c3cd7b30e4 100644 --- a/packages/browser/package.json +++ b/packages/browser/package.json @@ -1,6 +1,6 @@ { "name": "@sentry/browser", - "version": "7.0.0-beta.0", + "version": "7.0.0-beta.1", "description": "Official Sentry SDK for browsers", "repository": "git://github.com/getsentry/sentry-javascript.git", "homepage": "https://github.com/getsentry/sentry-javascript/tree/master/packages/browser", @@ -16,9 +16,9 @@ "access": "public" }, "dependencies": { - "@sentry/core": "7.0.0-beta.0", - "@sentry/types": "7.0.0-beta.0", - "@sentry/utils": "7.0.0-beta.0", + "@sentry/core": "7.0.0-beta.1", + "@sentry/types": "7.0.0-beta.1", + "@sentry/utils": "7.0.0-beta.1", "tslib": "^1.9.3" }, "devDependencies": { diff --git a/packages/core/package.json b/packages/core/package.json index 83b6a0131dfe..e2af28fcf4f9 100644 --- a/packages/core/package.json +++ b/packages/core/package.json @@ -1,6 +1,6 @@ { "name": "@sentry/core", - "version": "7.0.0-beta.0", + "version": "7.0.0-beta.1", "description": "Base implementation for all Sentry JavaScript SDKs", "repository": "git://github.com/getsentry/sentry-javascript.git", "homepage": "https://github.com/getsentry/sentry-javascript/tree/master/packages/core", @@ -16,9 +16,9 @@ "access": "public" }, "dependencies": { - "@sentry/hub": "7.0.0-beta.0", - "@sentry/types": "7.0.0-beta.0", - "@sentry/utils": "7.0.0-beta.0", + "@sentry/hub": "7.0.0-beta.1", + "@sentry/types": "7.0.0-beta.1", + "@sentry/utils": "7.0.0-beta.1", "tslib": "^1.9.3" }, "scripts": { diff --git a/packages/core/src/version.ts b/packages/core/src/version.ts index 9d44e7a95026..ab3d7f550375 100644 --- a/packages/core/src/version.ts +++ b/packages/core/src/version.ts @@ -1 +1 @@ -export const SDK_VERSION = '7.0.0-beta.0'; +export const SDK_VERSION = '7.0.0-beta.1'; diff --git a/packages/ember/package.json b/packages/ember/package.json index bb65fcbfc880..f4f9cd2e1894 100644 --- a/packages/ember/package.json +++ b/packages/ember/package.json @@ -1,6 +1,6 @@ { "name": "@sentry/ember", - "version": "7.0.0-beta.0", + "version": "7.0.0-beta.1", "description": "Official Sentry SDK for Ember.js", "repository": "git://github.com/getsentry/sentry-javascript.git", "homepage": "https://github.com/getsentry/sentry-javascript/tree/master/packages/ember", @@ -29,10 +29,10 @@ }, "dependencies": { "@embroider/macros": "~0.47.2", - "@sentry/browser": "7.0.0-beta.0", - "@sentry/tracing": "7.0.0-beta.0", - "@sentry/types": "7.0.0-beta.0", - "@sentry/utils": "7.0.0-beta.0", + "@sentry/browser": "7.0.0-beta.1", + "@sentry/tracing": "7.0.0-beta.1", + "@sentry/types": "7.0.0-beta.1", + "@sentry/utils": "7.0.0-beta.1", "ember-auto-import": "~1.12.1 || ~2.2.0", "ember-cli-babel": "~7.26.6", "ember-cli-htmlbars": "^6.0.1", diff --git a/packages/eslint-config-sdk/package.json b/packages/eslint-config-sdk/package.json index a87e8f205972..34b639a010a9 100644 --- a/packages/eslint-config-sdk/package.json +++ b/packages/eslint-config-sdk/package.json @@ -1,6 +1,6 @@ { "name": "@sentry-internal/eslint-config-sdk", - "version": "7.0.0-beta.0", + "version": "7.0.0-beta.1", "description": "Official Sentry SDK eslint config", "repository": "git://github.com/getsentry/sentry-javascript.git", "homepage": "https://github.com/getsentry/sentry-javascript/tree/master/packages/eslint-config-sdk", @@ -19,8 +19,8 @@ "access": "public" }, "dependencies": { - "@sentry-internal/eslint-plugin-sdk": "7.0.0-beta.0", - "@sentry-internal/typescript": "7.0.0-beta.0", + "@sentry-internal/eslint-plugin-sdk": "7.0.0-beta.1", + "@sentry-internal/typescript": "7.0.0-beta.1", "@typescript-eslint/eslint-plugin": "^3.9.0", "@typescript-eslint/parser": "^3.9.0", "eslint-config-prettier": "^6.11.0", diff --git a/packages/eslint-plugin-sdk/package.json b/packages/eslint-plugin-sdk/package.json index 22f14a3bc117..00b2bdbc526b 100644 --- a/packages/eslint-plugin-sdk/package.json +++ b/packages/eslint-plugin-sdk/package.json @@ -1,6 +1,6 @@ { "name": "@sentry-internal/eslint-plugin-sdk", - "version": "7.0.0-beta.0", + "version": "7.0.0-beta.1", "description": "Official Sentry SDK eslint plugin", "repository": "git://github.com/getsentry/sentry-javascript.git", "homepage": "https://github.com/getsentry/sentry-javascript/tree/master/packages/eslint-plugin-sdk", diff --git a/packages/gatsby/package.json b/packages/gatsby/package.json index b8bb4758d42a..e555c220b242 100644 --- a/packages/gatsby/package.json +++ b/packages/gatsby/package.json @@ -1,6 +1,6 @@ { "name": "@sentry/gatsby", - "version": "7.0.0-beta.0", + "version": "7.0.0-beta.1", "description": "Official Sentry SDK for Gatsby.js", "repository": "git://github.com/getsentry/sentry-javascript.git", "homepage": "https://github.com/getsentry/sentry-javascript/tree/master/packages/gatsby", @@ -20,10 +20,10 @@ "access": "public" }, "dependencies": { - "@sentry/react": "7.0.0-beta.0", - "@sentry/tracing": "7.0.0-beta.0", - "@sentry/types": "7.0.0-beta.0", - "@sentry/utils": "7.0.0-beta.0", + "@sentry/react": "7.0.0-beta.1", + "@sentry/tracing": "7.0.0-beta.1", + "@sentry/types": "7.0.0-beta.1", + "@sentry/utils": "7.0.0-beta.1", "@sentry/webpack-plugin": "1.18.9" }, "peerDependencies": { diff --git a/packages/hub/package.json b/packages/hub/package.json index 7885c26df368..a13999512d83 100644 --- a/packages/hub/package.json +++ b/packages/hub/package.json @@ -1,6 +1,6 @@ { "name": "@sentry/hub", - "version": "7.0.0-beta.0", + "version": "7.0.0-beta.1", "description": "Sentry hub which handles global state managment.", "repository": "git://github.com/getsentry/sentry-javascript.git", "homepage": "https://github.com/getsentry/sentry-javascript/tree/master/packages/hub", @@ -16,8 +16,8 @@ "access": "public" }, "dependencies": { - "@sentry/types": "7.0.0-beta.0", - "@sentry/utils": "7.0.0-beta.0", + "@sentry/types": "7.0.0-beta.1", + "@sentry/utils": "7.0.0-beta.1", "tslib": "^1.9.3" }, "scripts": { diff --git a/packages/integration-tests/package.json b/packages/integration-tests/package.json index d4dee475b59c..39506bcd8272 100644 --- a/packages/integration-tests/package.json +++ b/packages/integration-tests/package.json @@ -1,6 +1,6 @@ { "name": "@sentry-internal/browser-integration-tests", - "version": "7.0.0-beta.0", + "version": "7.0.0-beta.1", "main": "index.js", "license": "MIT", "engines": { diff --git a/packages/integrations/package.json b/packages/integrations/package.json index 17ebabcda0cc..dc64f179c932 100644 --- a/packages/integrations/package.json +++ b/packages/integrations/package.json @@ -1,6 +1,6 @@ { "name": "@sentry/integrations", - "version": "7.0.0-beta.0", + "version": "7.0.0-beta.1", "description": "Pluggable integrations that can be used to enhance JS SDKs", "repository": "git://github.com/getsentry/sentry-javascript.git", "homepage": "https://github.com/getsentry/sentry-javascript/tree/master/packages/integrations", @@ -16,8 +16,8 @@ "module": "build/npm/esm/index.js", "types": "build/npm/types/index.d.ts", "dependencies": { - "@sentry/types": "7.0.0-beta.0", - "@sentry/utils": "7.0.0-beta.0", + "@sentry/types": "7.0.0-beta.1", + "@sentry/utils": "7.0.0-beta.1", "localforage": "^1.8.1", "tslib": "^1.9.3" }, diff --git a/packages/nextjs/package.json b/packages/nextjs/package.json index de56d10fc1ef..84eb372adf58 100644 --- a/packages/nextjs/package.json +++ b/packages/nextjs/package.json @@ -1,6 +1,6 @@ { "name": "@sentry/nextjs", - "version": "7.0.0-beta.0", + "version": "7.0.0-beta.1", "description": "Official Sentry SDK for Next.js", "repository": "git://github.com/getsentry/sentry-javascript.git", "homepage": "https://github.com/getsentry/sentry-javascript/tree/master/packages/nextjs", @@ -17,15 +17,15 @@ "access": "public" }, "dependencies": { - "@sentry/core": "7.0.0-beta.0", - "@sentry/hub": "7.0.0-beta.0", - "@sentry/integrations": "7.0.0-beta.0", - "@sentry/node": "7.0.0-beta.0", - "@sentry/react": "7.0.0-beta.0", - "@sentry/tracing": "7.0.0-beta.0", - "@sentry/utils": "7.0.0-beta.0", + "@sentry/core": "7.0.0-beta.1", + "@sentry/hub": "7.0.0-beta.1", + "@sentry/integrations": "7.0.0-beta.1", + "@sentry/node": "7.0.0-beta.1", + "@sentry/react": "7.0.0-beta.1", + "@sentry/tracing": "7.0.0-beta.1", + "@sentry/types": "7.0.0-beta.1", + "@sentry/utils": "7.0.0-beta.1", "@sentry/webpack-plugin": "1.18.9", - "@sentry/types": "7.0.0-beta.0", "tslib": "^1.9.3" }, "devDependencies": { diff --git a/packages/node-integration-tests/package.json b/packages/node-integration-tests/package.json index 505db755265b..17b03145ae1a 100644 --- a/packages/node-integration-tests/package.json +++ b/packages/node-integration-tests/package.json @@ -1,6 +1,6 @@ { "name": "@sentry-internal/node-integration-tests", - "version": "7.0.0-beta.0", + "version": "7.0.0-beta.1", "license": "MIT", "engines": { "node": ">=10" diff --git a/packages/node/package.json b/packages/node/package.json index fd0130f54785..06a85125fcd3 100644 --- a/packages/node/package.json +++ b/packages/node/package.json @@ -1,6 +1,6 @@ { "name": "@sentry/node", - "version": "7.0.0-beta.0", + "version": "7.0.0-beta.1", "description": "Official Sentry SDK for Node.js", "repository": "git://github.com/getsentry/sentry-javascript.git", "homepage": "https://github.com/getsentry/sentry-javascript/tree/master/packages/node", @@ -16,10 +16,10 @@ "access": "public" }, "dependencies": { - "@sentry/core": "7.0.0-beta.0", - "@sentry/hub": "7.0.0-beta.0", - "@sentry/types": "7.0.0-beta.0", - "@sentry/utils": "7.0.0-beta.0", + "@sentry/core": "7.0.0-beta.1", + "@sentry/hub": "7.0.0-beta.1", + "@sentry/types": "7.0.0-beta.1", + "@sentry/utils": "7.0.0-beta.1", "cookie": "^0.4.1", "https-proxy-agent": "^5.0.0", "lru_map": "^0.3.3", diff --git a/packages/react/package.json b/packages/react/package.json index 13a14ef0ca7c..d5cd0a4b6502 100644 --- a/packages/react/package.json +++ b/packages/react/package.json @@ -1,6 +1,6 @@ { "name": "@sentry/react", - "version": "7.0.0-beta.0", + "version": "7.0.0-beta.1", "description": "Official Sentry SDK for React.js", "repository": "git://github.com/getsentry/sentry-javascript.git", "homepage": "https://github.com/getsentry/sentry-javascript/tree/master/packages/react", @@ -16,9 +16,9 @@ "access": "public" }, "dependencies": { - "@sentry/browser": "7.0.0-beta.0", - "@sentry/types": "7.0.0-beta.0", - "@sentry/utils": "7.0.0-beta.0", + "@sentry/browser": "7.0.0-beta.1", + "@sentry/types": "7.0.0-beta.1", + "@sentry/utils": "7.0.0-beta.1", "hoist-non-react-statics": "^3.3.2", "tslib": "^1.9.3" }, diff --git a/packages/serverless/package.json b/packages/serverless/package.json index 6fe8c659780c..1e99c704cafe 100644 --- a/packages/serverless/package.json +++ b/packages/serverless/package.json @@ -1,6 +1,6 @@ { "name": "@sentry/serverless", - "version": "7.0.0-beta.0", + "version": "7.0.0-beta.1", "description": "Official Sentry SDK for various serverless solutions", "repository": "git://github.com/getsentry/sentry-javascript.git", "homepage": "https://github.com/getsentry/sentry-javascript/tree/master/packages/serverless", @@ -16,10 +16,10 @@ "access": "public" }, "dependencies": { - "@sentry/node": "7.0.0-beta.0", - "@sentry/tracing": "7.0.0-beta.0", - "@sentry/types": "7.0.0-beta.0", - "@sentry/utils": "7.0.0-beta.0", + "@sentry/node": "7.0.0-beta.1", + "@sentry/tracing": "7.0.0-beta.1", + "@sentry/types": "7.0.0-beta.1", + "@sentry/utils": "7.0.0-beta.1", "@types/aws-lambda": "^8.10.62", "@types/express": "^4.17.2", "tslib": "^1.9.3" diff --git a/packages/tracing/package.json b/packages/tracing/package.json index 840a55aa6326..d6d0d3b1ca94 100644 --- a/packages/tracing/package.json +++ b/packages/tracing/package.json @@ -1,6 +1,6 @@ { "name": "@sentry/tracing", - "version": "7.0.0-beta.0", + "version": "7.0.0-beta.1", "description": "Extensions for Sentry AM", "repository": "git://github.com/getsentry/sentry-javascript.git", "homepage": "https://github.com/getsentry/sentry-javascript/tree/master/packages/tracing", @@ -16,13 +16,13 @@ "access": "public" }, "dependencies": { - "@sentry/hub": "7.0.0-beta.0", - "@sentry/types": "7.0.0-beta.0", - "@sentry/utils": "7.0.0-beta.0", + "@sentry/hub": "7.0.0-beta.1", + "@sentry/types": "7.0.0-beta.1", + "@sentry/utils": "7.0.0-beta.1", "tslib": "^1.9.3" }, "devDependencies": { - "@sentry/browser": "7.0.0-beta.0", + "@sentry/browser": "7.0.0-beta.1", "@types/express": "^4.17.1" }, "scripts": { diff --git a/packages/types/package.json b/packages/types/package.json index e32547bab2c5..c8d275c6c1b1 100644 --- a/packages/types/package.json +++ b/packages/types/package.json @@ -1,6 +1,6 @@ { "name": "@sentry/types", - "version": "7.0.0-beta.0", + "version": "7.0.0-beta.1", "description": "Types for all Sentry JavaScript SDKs", "repository": "git://github.com/getsentry/sentry-javascript.git", "homepage": "https://github.com/getsentry/sentry-javascript/tree/master/packages/types", diff --git a/packages/typescript/package.json b/packages/typescript/package.json index 1c3e71b8778c..3608d5139367 100644 --- a/packages/typescript/package.json +++ b/packages/typescript/package.json @@ -1,6 +1,6 @@ { "name": "@sentry-internal/typescript", - "version": "7.0.0-beta.0", + "version": "7.0.0-beta.1", "description": "Typescript configuration used at Sentry", "repository": "git://github.com/getsentry/sentry-javascript.git", "homepage": "https://github.com/getsentry/sentry-javascript/tree/master/packages/typescript", diff --git a/packages/utils/package.json b/packages/utils/package.json index 8ee24544ce98..6cc5ac6def18 100644 --- a/packages/utils/package.json +++ b/packages/utils/package.json @@ -1,6 +1,6 @@ { "name": "@sentry/utils", - "version": "7.0.0-beta.0", + "version": "7.0.0-beta.1", "description": "Utilities for all Sentry JavaScript SDKs", "repository": "git://github.com/getsentry/sentry-javascript.git", "homepage": "https://github.com/getsentry/sentry-javascript/tree/master/packages/utils", @@ -16,7 +16,7 @@ "access": "public" }, "dependencies": { - "@sentry/types": "7.0.0-beta.0", + "@sentry/types": "7.0.0-beta.1", "tslib": "^1.9.3" }, "devDependencies": { diff --git a/packages/vue/package.json b/packages/vue/package.json index 2a95c8aedaa4..430d46cbac0f 100644 --- a/packages/vue/package.json +++ b/packages/vue/package.json @@ -1,6 +1,6 @@ { "name": "@sentry/vue", - "version": "7.0.0-beta.0", + "version": "7.0.0-beta.1", "description": "Official Sentry SDK for Vue.js", "repository": "git://github.com/getsentry/sentry-javascript.git", "homepage": "https://github.com/getsentry/sentry-javascript/tree/master/packages/vue", @@ -16,10 +16,10 @@ "access": "public" }, "dependencies": { - "@sentry/browser": "7.0.0-beta.0", - "@sentry/core": "7.0.0-beta.0", - "@sentry/types": "7.0.0-beta.0", - "@sentry/utils": "7.0.0-beta.0", + "@sentry/browser": "7.0.0-beta.1", + "@sentry/core": "7.0.0-beta.1", + "@sentry/types": "7.0.0-beta.1", + "@sentry/utils": "7.0.0-beta.1", "tslib": "^1.9.3" }, "peerDependencies": { diff --git a/packages/wasm/package.json b/packages/wasm/package.json index cd072a0eb67e..baf0f28e3bc5 100644 --- a/packages/wasm/package.json +++ b/packages/wasm/package.json @@ -1,6 +1,6 @@ { "name": "@sentry/wasm", - "version": "7.0.0-beta.0", + "version": "7.0.0-beta.1", "description": "Support for WASM.", "repository": "git://github.com/getsentry/sentry-javascript.git", "homepage": "https://github.com/getsentry/sentry-javascript/tree/master/packages/wasm", @@ -16,9 +16,9 @@ "access": "public" }, "dependencies": { - "@sentry/browser": "7.0.0-beta.0", - "@sentry/types": "7.0.0-beta.0", - "@sentry/utils": "7.0.0-beta.0", + "@sentry/browser": "7.0.0-beta.1", + "@sentry/types": "7.0.0-beta.1", + "@sentry/utils": "7.0.0-beta.1", "tslib": "^1.9.3" }, "devDependencies": { From e0ad5c1b5d44db74e92c7302180792b93b99a43c Mon Sep 17 00:00:00 2001 From: Luca Forstner Date: Fri, 13 May 2022 09:24:01 +0200 Subject: [PATCH 157/204] fix(ember): Export sha hashes of injected scripts (#5089) --- packages/ember/index.js | 25 +++++++++++++++++++------ 1 file changed, 19 insertions(+), 6 deletions(-) diff --git a/packages/ember/index.js b/packages/ember/index.js index c51410e9348c..130d5f33ed5f 100644 --- a/packages/ember/index.js +++ b/packages/ember/index.js @@ -1,10 +1,21 @@ 'use strict'; const fs = require('fs'); +const crypto = require('crypto'); function readSnippet(fileName) { - return ``; + return fs.readFileSync(`${__dirname}/vendor/${fileName}`, 'utf8'); } +function hashSha256base64(string) { + return crypto.createHash('sha256').update(string).digest('base64'); +} + +const initialLoadHeadSnippet = readSnippet('initial-load-head.js'); +const initialLoadBodySnippet = readSnippet('initial-load-body.js'); + +const initialLoadHeadSnippetHash = hashSha256base64(initialLoadHeadSnippet); +const initialLoadBodySnippetHash = hashSha256base64(initialLoadBodySnippet); + module.exports = { name: require('./package').name, options: { @@ -24,16 +35,18 @@ module.exports = { contentFor(type, config) { const addonConfig = config['@sentry/ember'] || {}; - const { disablePerformance, disableInitialLoadInstrumentation } = addonConfig; + if (disablePerformance || disableInitialLoadInstrumentation) { return; } + if (type === 'head') { - return readSnippet('initial-load-head.js'); - } - if (type === 'body-footer') { - return readSnippet('initial-load-body.js'); + return ``; + } else if (type === 'body-footer') { + return ``; } }, + + injectedScriptHashes: [initialLoadHeadSnippetHash, initialLoadBodySnippetHash], }; From 5c34152a4d79ba52ee36ab6abecff9a144c06053 Mon Sep 17 00:00:00 2001 From: Luca Forstner Date: Fri, 13 May 2022 09:28:28 +0200 Subject: [PATCH 158/204] ref(serverless): Do not throw on flush error (#5090) --- MIGRATION.md | 1 + packages/serverless/src/awslambda.ts | 10 +--- .../src/gcpfunction/cloud_events.ts | 6 +-- packages/serverless/src/gcpfunction/events.ts | 6 +-- packages/serverless/src/gcpfunction/http.ts | 6 +-- packages/serverless/test/awslambda.test.ts | 54 ++++++------------- packages/serverless/test/gcpfunction.test.ts | 28 ++++++++++ 7 files changed, 55 insertions(+), 56 deletions(-) diff --git a/MIGRATION.md b/MIGRATION.md index 01addf214ef4..6f8db51f1c9f 100644 --- a/MIGRATION.md +++ b/MIGRATION.md @@ -340,6 +340,7 @@ For our efforts to reduce bundle size of the SDK we had to remove and refactor p - Remove `SDK_NAME` export from `@sentry/browser`, `@sentry/node`, `@sentry/tracing` and `@sentry/vue` packages. - Removed `eventStatusFromHttpCode` to save on bundle size. - Replace `BrowserTracing` `maxTransactionDuration` option with `finalTimeout` option +- Removed `ignoreSentryErrors` option from AWS lambda SDK. Errors originating from the SDK will now *always* be caught internally. ## Sentry Angular SDK Changes diff --git a/packages/serverless/src/awslambda.ts b/packages/serverless/src/awslambda.ts index 302607e25142..92802bf808cd 100644 --- a/packages/serverless/src/awslambda.ts +++ b/packages/serverless/src/awslambda.ts @@ -11,7 +11,7 @@ import { } from '@sentry/node'; import { extractTraceparentData } from '@sentry/tracing'; import { Integration } from '@sentry/types'; -import { isString, logger, SentryError } from '@sentry/utils'; +import { isString, logger } from '@sentry/utils'; // NOTE: I have no idea how to fix this right now, and don't want to waste more time, as it builds just fine — Kamil // eslint-disable-next-line import/no-unresolved import { Context, Handler } from 'aws-lambda'; @@ -54,7 +54,6 @@ export interface WrapperOptions { * @default false */ captureAllSettledReasons: boolean; - ignoreSentryErrors: boolean; } export const defaultIntegrations: Integration[] = [...Sentry.defaultIntegrations, new AWSServices({ optional: true })]; @@ -226,7 +225,6 @@ export function wrapHandler( captureTimeoutWarning: true, timeoutWarningLimit: 500, captureAllSettledReasons: false, - ignoreSentryErrors: true, ...wrapOptions, }; let timeoutWarningTimer: NodeJS.Timeout; @@ -318,11 +316,7 @@ export function wrapHandler( transaction.finish(); hub.popScope(); await flush(options.flushTimeout).catch(e => { - if (options.ignoreSentryErrors && e instanceof SentryError) { - IS_DEBUG_BUILD && logger.error(e); - return; - } - throw e; + IS_DEBUG_BUILD && logger.error(e); }); } return rv; diff --git a/packages/serverless/src/gcpfunction/cloud_events.ts b/packages/serverless/src/gcpfunction/cloud_events.ts index f4c79c410d9c..ae993128139f 100644 --- a/packages/serverless/src/gcpfunction/cloud_events.ts +++ b/packages/serverless/src/gcpfunction/cloud_events.ts @@ -61,11 +61,11 @@ function _wrapCloudEventFunction( transaction.finish(); void flush(options.flushTimeout) - .then(() => { - callback(...args); - }) .then(null, e => { IS_DEBUG_BUILD && logger.error(e); + }) + .then(() => { + callback(...args); }); }); diff --git a/packages/serverless/src/gcpfunction/events.ts b/packages/serverless/src/gcpfunction/events.ts index e47efc1571eb..4b29a26270e4 100644 --- a/packages/serverless/src/gcpfunction/events.ts +++ b/packages/serverless/src/gcpfunction/events.ts @@ -56,11 +56,11 @@ function _wrapEventFunction( transaction.finish(); void flush(options.flushTimeout) - .then(() => { - callback(...args); - }) .then(null, e => { IS_DEBUG_BUILD && logger.error(e); + }) + .then(() => { + callback(...args); }); }); diff --git a/packages/serverless/src/gcpfunction/http.ts b/packages/serverless/src/gcpfunction/http.ts index 2e274bad3491..a1eec1e48736 100644 --- a/packages/serverless/src/gcpfunction/http.ts +++ b/packages/serverless/src/gcpfunction/http.ts @@ -91,11 +91,11 @@ function _wrapHttpFunction(fn: HttpFunction, wrapOptions: Partial { - _end.call(this, chunk, encoding, cb); - }) .then(null, e => { IS_DEBUG_BUILD && logger.error(e); + }) + .then(() => { + _end.call(this, chunk, encoding, cb); }); }; diff --git a/packages/serverless/test/awslambda.test.ts b/packages/serverless/test/awslambda.test.ts index ab0c38d2f08a..1e49d3d8326f 100644 --- a/packages/serverless/test/awslambda.test.ts +++ b/packages/serverless/test/awslambda.test.ts @@ -1,4 +1,3 @@ -import { SentryError } from '@sentry/utils'; // NOTE: I have no idea how to fix this right now, and don't want to waste more time, as it builds just fine — Kamil // eslint-disable-next-line import/no-unresolved import { Callback, Handler } from 'aws-lambda'; @@ -178,44 +177,6 @@ describe('AWSLambda', () => { expect(Sentry.captureException).toHaveBeenNthCalledWith(2, error2); expect(Sentry.captureException).toBeCalledTimes(2); }); - - test('ignoreSentryErrors - with successful handler', async () => { - const sentryError = new SentryError('HTTP Error (429)'); - jest.spyOn(Sentry, 'flush').mockRejectedValueOnce(sentryError); - - const handledError = new Error('handled error, and we want to monitor it'); - const expectedSuccessStatus = { status: 'success', reason: 'we handled error, so success' }; - const handler = () => { - Sentry.captureException(handledError); - return Promise.resolve(expectedSuccessStatus); - }; - const wrappedHandlerWithoutIgnoringSentryErrors = wrapHandler(handler, { ignoreSentryErrors: false }); - const wrappedHandlerWithIgnoringSentryErrors = wrapHandler(handler, { ignoreSentryErrors: true }); - - await expect(wrappedHandlerWithoutIgnoringSentryErrors(fakeEvent, fakeContext, fakeCallback)).rejects.toThrow( - sentryError, - ); - await expect(wrappedHandlerWithIgnoringSentryErrors(fakeEvent, fakeContext, fakeCallback)).resolves.toBe( - expectedSuccessStatus, - ); - }); - - test('ignoreSentryErrors - with failed handler', async () => { - const sentryError = new SentryError('HTTP Error (429)'); - jest.spyOn(Sentry, 'flush').mockRejectedValueOnce(sentryError); - - const criticalUnhandledError = new Error('critical unhandled error '); - const handler = () => Promise.reject(criticalUnhandledError); - const wrappedHandlerWithoutIgnoringSentryErrors = wrapHandler(handler, { ignoreSentryErrors: false }); - const wrappedHandlerWithIgnoringSentryErrors = wrapHandler(handler, { ignoreSentryErrors: true }); - - await expect(wrappedHandlerWithoutIgnoringSentryErrors(fakeEvent, fakeContext, fakeCallback)).rejects.toThrow( - sentryError, - ); - await expect(wrappedHandlerWithIgnoringSentryErrors(fakeEvent, fakeContext, fakeCallback)).rejects.toThrow( - criticalUnhandledError, - ); - }); }); describe('wrapHandler() on sync handler', () => { @@ -345,6 +306,21 @@ describe('AWSLambda', () => { expect(Sentry.flush).toBeCalled(); } }); + + test('should not throw when flush rejects', async () => { + const handler: Handler = async () => { + // Friendly handler with no errors :) + return 'some string'; + }; + + const wrappedHandler = wrapHandler(handler); + + jest.spyOn(Sentry, 'flush').mockImplementationOnce(async () => { + throw new Error(); + }); + + await expect(wrappedHandler(fakeEvent, fakeContext, fakeCallback)).resolves.toBe('some string'); + }); }); describe('wrapHandler() on async handler with a callback method (aka incorrect usage)', () => { diff --git a/packages/serverless/test/gcpfunction.test.ts b/packages/serverless/test/gcpfunction.test.ts index 0a77742a33c6..5072de8b17b7 100644 --- a/packages/serverless/test/gcpfunction.test.ts +++ b/packages/serverless/test/gcpfunction.test.ts @@ -148,6 +148,34 @@ describe('GCPFunction', () => { expect(Sentry.fakeTransaction.finish).toBeCalled(); expect(Sentry.flush).toBeCalled(); }); + + test('should not throw when flush rejects', async () => { + expect.assertions(2); + + const handler: HttpFunction = async (_req, res) => { + res.statusCode = 200; + res.end(); + }; + + const wrappedHandler = wrapHttpFunction(handler); + + const request = { + method: 'POST', + url: '/path?q=query', + headers: { host: 'hostname', 'content-type': 'application/json' }, + body: { foo: 'bar' }, + } as Request; + + const mockEnd = jest.fn(); + const response = { end: mockEnd } as unknown as Response; + + jest.spyOn(Sentry, 'flush').mockImplementationOnce(async () => { + throw new Error(); + }); + + await expect(wrappedHandler(request, response)).resolves.toBeUndefined(); + expect(mockEnd).toHaveBeenCalledTimes(1); + }); }); test('wrapHttpFunction request data', async () => { From 8a4039db572fdd1d8339dbf9168de9cd2a490f5f Mon Sep 17 00:00:00 2001 From: Anton Pirker Date: Fri, 13 May 2022 11:08:02 +0200 Subject: [PATCH 159/204] ref(serverless): Use GitHub action to zip lambda layer (second try) (#5093) * Removed zipping from build script * Assemble Lambda Layer zip using GitHub action. Co-authored-by: Lukas Stracke --- .github/workflows/build.yml | 55 +++++++++++++++++-- .gitignore | 1 + packages/serverless/.gitignore | 1 - .../scripts/build-awslambda-layer.js | 24 +------- 4 files changed, 52 insertions(+), 29 deletions(-) delete mode 100644 packages/serverless/.gitignore diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index e7f3bf5ad6c1..9985bd48f6bd 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -31,7 +31,8 @@ env: ${{ github.workspace }}/packages/ember/*.d.ts ${{ github.workspace }}/packages/ember/instance-initializers ${{ github.workspace }}/packages/gatsby/*.d.ts - ${{ github.workspace }}/packages/serverless/dist-awslambda-layer/*.zip + ${{ github.workspace }}/packages/core/src/version.ts + ${{ github.workspace }}/dist-serverless BUILD_CACHE_KEY: ${{ github.event.inputs.commit || github.sha }} @@ -99,6 +100,11 @@ jobs: # this file) to a constant and skip rebuilding all of the packages each time CI runs. if: steps.cache_built_packages.outputs.cache-hit == '' run: yarn build + - name: Save SDK version for later + run: | + echo "Saving SDK_VERSION for later" + export SDK_VERSION=$(cat packages/core/src/version.ts | cut -f5 -d' ' | tr -d "'" | tr -d ";") + echo $SDK_VERSION > dist-serverless/version outputs: # this needs to be passed on, because the `needs` context only looks at direct ancestors (so steps which depend on # `job_build` can't see `job_install_deps` and what it returned) @@ -228,7 +234,6 @@ jobs: ${{ github.workspace }}/packages/integrations/build/bundles/** ${{ github.workspace }}/packages/tracing/build/bundles/** ${{ github.workspace }}/packages/**/*.tgz - ${{ github.workspace }}/packages/serverless/dist-awslambda-layer/*.zip job_unit_test: name: Test (Node ${{ matrix.node }}) @@ -315,10 +320,10 @@ jobs: uses: actions/checkout@v2 with: ref: ${{ env.HEAD_COMMIT }} - # TODO: removing `fetch-depth` below seems to have no effect, and the commit which added it had no description, - # so it's not clear why it's necessary. That said, right now ember tests are xfail, so it's a little hard to - # tell if it's safe to remove. Once ember tests are fixed, let's try again with it turned off, and if all goes - # well, we can pull it out. + # TODO: removing `fetch-depth` below seems to have no effect, and the commit which added it had no description, + # so it's not clear why it's necessary. That said, right now ember tests are xfail, so it's a little hard to + # tell if it's safe to remove. Once ember tests are fixed, let's try again with it turned off, and if all goes + # well, we can pull it out. fetch-depth: 0 - name: Set up Node uses: actions/setup-node@v1 @@ -501,3 +506,41 @@ jobs: run: | cd packages/node-integration-tests yarn test + + job_build_aws_lambda_layer: + name: Build AWS Lambda Layer + needs: job_build + runs-on: ubuntu-latest + steps: + - name: Check out current commit (${{ env.HEAD_COMMIT }}) + uses: actions/checkout@v2 + with: + ref: ${{ env.HEAD_COMMIT }} + - name: Set up Node + uses: actions/setup-node@v1 + with: + node-version: ${{ env.DEFAULT_NODE_VERSION }} + - name: Check dependency cache + uses: actions/cache@v2 + with: + path: ${{ env.CACHED_DEPENDENCY_PATHS }} + key: ${{ needs.job_build.outputs.dependency_cache_key }} + - name: Check build cache + uses: actions/cache@v2 + with: + path: ${{ env.CACHED_BUILD_PATHS }} + key: ${{ env.BUILD_CACHE_KEY }} + - name: Get SDK version + run: | + export SDK_VERSION=$(cat dist-serverless/version) + echo "SDK_VERSION=$SDK_VERSION" + echo "SDK_VERSION=$SDK_VERSION" >> $GITHUB_ENV + - uses: actions/upload-artifact@v3 + with: + name: ${{ env.HEAD_COMMIT }} + path: | + dist-serverless/* + - uses: getsentry/action-build-aws-lambda-extension@v1 + with: + artifact_name: ${{ env.HEAD_COMMIT }} + zip_file_name: sentry-node-serverless-${{ env.SDK_VERSION }}.zip diff --git a/.gitignore b/.gitignore index be80e41d00b7..0dc14b0dea7a 100644 --- a/.gitignore +++ b/.gitignore @@ -12,6 +12,7 @@ scratch/ *.pyc *.tsbuildinfo scenarios/*/dist/ +dist-serverless/ # transpiled transformers jest/transformers/*.js # node tarballs diff --git a/packages/serverless/.gitignore b/packages/serverless/.gitignore deleted file mode 100644 index c94757f34037..000000000000 --- a/packages/serverless/.gitignore +++ /dev/null @@ -1 +0,0 @@ -dist-awslambda-layer/ diff --git a/packages/serverless/scripts/build-awslambda-layer.js b/packages/serverless/scripts/build-awslambda-layer.js index 25be1107e203..8fbc493558e0 100644 --- a/packages/serverless/scripts/build-awslambda-layer.js +++ b/packages/serverless/scripts/build-awslambda-layer.js @@ -2,7 +2,6 @@ const path = require('path'); const process = require('process'); const fs = require('fs'); -const childProcess = require('child_process'); const findUp = require('find-up'); const packList = require('npm-packlist'); @@ -75,6 +74,7 @@ async function collectPackages(cwd, packages = {}) { } async function main() { + const baseDir = path.resolve(__dirname, '../../../') const serverlessDir = path.resolve(__dirname, '..'); // packages/serverless directory const cjsBuildDir = path.resolve(serverlessDir, 'build', 'cjs'); @@ -86,7 +86,7 @@ async function main() { const packages = await collectPackages(serverlessDir); // the build directory of the Lambda layer - const layerBuildDir = path.resolve(serverlessDir, 'dist-awslambda-layer'); + const layerBuildDir = path.resolve(baseDir, 'dist-serverless'); // the root directory in which the Lambda layer files + dependencies are copied to // this structure resembles the structure where Lambda expects to find @sentry/serverless @@ -170,9 +170,6 @@ async function main() { }), ); - const version = serverlessPackageJson.version; - const zipFilename = `sentry-node-serverless-${version}.zip`; - // link from `./build/cjs` to `./dist` // This needs to be done to satisfy the NODE_OPTIONS environment variable path that is set in // AWS lambda functions when connecting them to Sentry. On initialization, the layer preloads a js @@ -184,23 +181,6 @@ async function main() { } catch (error) { console.error(error); } - - // remove previously created layer zip - try { - fs.unlinkSync(path.resolve(layerBuildDir, zipFilename)); - } catch (error) { - // If the ZIP file hasn't been previously created (e.g. running this script for the first time), - // `unlinkSync` will try to delete a non-existing file. This error is ignored. - } - - // create new layer zip - try { - childProcess.execSync(`zip -r ${zipFilename} ${destRootRelative}`, { cwd: layerBuildDir }); - } catch (error) { - // The child process timed out or had non-zero exit code. - // The error contains the entire result from `childProcess.spawnSync`. - console.log(error); - } } main().then( From 25aed4d4e469150f0471af8101b9c9cc6a196392 Mon Sep 17 00:00:00 2001 From: Anton Pirker Date: Fri, 13 May 2022 11:42:51 +0200 Subject: [PATCH 160/204] ref(serverless): Improve handling of SDK version in GitHub action (#5100) * Fix for properly handling SDK version in GitHub actions. --- .github/workflows/build.yml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 9985bd48f6bd..820719e2db67 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -103,8 +103,9 @@ jobs: - name: Save SDK version for later run: | echo "Saving SDK_VERSION for later" - export SDK_VERSION=$(cat packages/core/src/version.ts | cut -f5 -d' ' | tr -d "'" | tr -d ";") - echo $SDK_VERSION > dist-serverless/version + cat packages/core/src/version.ts | awk -F"'" '{print $2}' > dist-serverless/version + [ -z $(cat dist-serverless/version) ] && echo "Version extraction failed" && exit 1 + outputs: # this needs to be passed on, because the `needs` context only looks at direct ancestors (so steps which depend on # `job_build` can't see `job_install_deps` and what it returned) @@ -533,8 +534,7 @@ jobs: - name: Get SDK version run: | export SDK_VERSION=$(cat dist-serverless/version) - echo "SDK_VERSION=$SDK_VERSION" - echo "SDK_VERSION=$SDK_VERSION" >> $GITHUB_ENV + echo "SDK_VERSION=$SDK_VERSION" | tee -a $GITHUB_ENV - uses: actions/upload-artifact@v3 with: name: ${{ env.HEAD_COMMIT }} From fa7e3b724e470372880098d9d4edfaa95b508aec Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kamil=20Og=C3=B3rek?= Date: Fri, 13 May 2022 12:54:52 +0200 Subject: [PATCH 161/204] ref(serverless): Correctly exit on failed version extraction during build (#5101) --- .github/workflows/build.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 820719e2db67..5a4895da5793 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -104,7 +104,7 @@ jobs: run: | echo "Saving SDK_VERSION for later" cat packages/core/src/version.ts | awk -F"'" '{print $2}' > dist-serverless/version - [ -z $(cat dist-serverless/version) ] && echo "Version extraction failed" && exit 1 + [ ! -z $(cat dist-serverless/version) ] && echo SDK_VERSION=$(cat dist-serverless/version) || (echo "Version extraction failed" && exit 1) outputs: # this needs to be passed on, because the `needs` context only looks at direct ancestors (so steps which depend on From ce665e262abb52abda139350baa538cbccdc4a4e Mon Sep 17 00:00:00 2001 From: Katie Byers Date: Fri, 13 May 2022 06:28:11 -0700 Subject: [PATCH 162/204] ref(build): Run repo-level builds in parallel (#5094) This parallelizes our repo-level build yarn scripts where possible, in order to make them run a bit faster. In the end, the time savings isn't enormous, but still worthwhile: in a series of 10 time trials on GHA, the old build averaged ~9 minutes, while the new one averaged a bit over 8 minutes. (The time savings isn't that dramatic because the current lack of parallelity at the repo level isn't the biggest driver of our slow build speed; that would be the lack of parallelity at the individual package level, a problem which will be tackled in future PRs. Doing this now will let us take full advantage of that future work.) The over-arching goal here was to make as few tasks as possible have to wait on other tasks. Simply because of computing power, it isn't realistic to run _all_ tasks, for the entire repo, simultaneously. But the closer we can get to maxing out the theoretical potential for infinite parallelization, the better off we'll be, because then the only constraint does become the build runner. In order to accomplish this maximum-possible parallelization, a few things had to be considered: - Is there any interdependency between packages which means that a particular task can't be done in all packages simultaneously? - Is there any interdependency between tasks within a package that means that they can't be done simultaneously? - How do we make sure that at both the repo and package level, `yarn build:dev` builds only what's needed for local development and testing and `yarn build` builds everything? - Is there a way to organize things such that it works for every package, even ones with non-standard build setups? After investigation, it turned out that the key constraints were: - Types can't be built entirely in parallel across packages, because `tsc` checks that imports are being used correctly, which it can't do if types for the packages exporting those imports aren't yet themselves built. - Rollup and bundle builds can happen in parallel across packages, because each individual package's tasks are independent of the same tasks in other packages. Further, type-, rollup-, and bundle-building as overall process are also independent (so, for example, rollup builds don't have to wait on type builds). - Some packages have build tasks in addition to types, rollup, and bundles, and in some cases those tasks can't happen until after the rest of the build completes. - Some packages (angular and ember) have their own build commands, and don't have types, rollup, or bundle builds. To solve these constraints, the build system now follows these principles: - Every build task, in every package, is now represented in its package in exactly one of four scripts: `yarn build:types`, `yarn build:rollup`, `yarn build:bundle`, and a new `yarn build:extras`. Tasks can be parts of other package-level scripts (types and rollup builds together forming each package's `yarn build:dev`, for example), but as long as every task is in one of the canonical four, running those canonical scripts in every package that has them means we're guaranteed not to miss anything. - Types are build using lerna's `--stream` option, now with no limit on concurrency, in order to parallelize where possible without breaking dependency ordering. - Types are built independently of the other three kinds of tasks, since everything else _can_ be fully parallelized. This means not using package-level `yarn build` or `yarn build:dev` commands in the repo-level build, since they tie other build tasks to types build tasks. - To make things as easy as possible to reason about, not just types, but all four task kinds are therefore addressed separately by the repo-level build. - Angular and ember's build tasks are each aliased to a `yarn build:extras` script, so as not to be left out. - Because some "extras" tasks can't be done until types, rollup, and/or bundle tasks are done, make it so all "extras" tasks are held until the other three kinds have finished. This does push a few later than need be, but it's worth it in order to standardize the logic. - All of these principles are duplicated at the package level. For the visual learners among us, there is diagram illustrating this in the PR description. Oh, and while I was in there, I got rid of `yarn build:dev:filter`. It doesn't fit the new system, and no one uses it (not even me, and I made it up). --- package.json | 10 +++++----- packages/angular/package.json | 1 + packages/ember/package.json | 1 + packages/gatsby/package.json | 3 ++- packages/serverless/package.json | 3 ++- packages/tracing/package.json | 4 +++- packages/tracing/tsconfig.types.json | 6 ++++++ 7 files changed, 20 insertions(+), 8 deletions(-) diff --git a/package.json b/package.json index 9d6710419d65..37a546b16e04 100644 --- a/package.json +++ b/package.json @@ -1,12 +1,12 @@ { "private": true, "scripts": { - "build": "node ./scripts/verify-packages-versions.js && lerna run --stream --concurrency 1 --sort build", + "build": "node ./scripts/verify-packages-versions.js && yarn run-p build:rollup build:types build:bundle && yarn build:extras", "build:bundle": "lerna run --parallel build:bundle", - "build:dev": "lerna run --stream --concurrency 1 --sort build:dev", - "build:dev:filter": "lerna run --stream --concurrency 1 --sort build:dev --include-filtered-dependencies --include-filtered-dependents --scope", - "build:rollup": "lerna run --stream --concurrency 1 --sort build:rollup", - "build:types": "lerna run --stream --concurrency 1 --sort build:types", + "build:dev": "run-p build:types build:rollup", + "build:extras": "lerna run --parallel build:extras", + "build:rollup": "lerna run --parallel build:rollup", + "build:types": "lerna run --stream build:types", "build:watch": "lerna run --parallel build:watch", "build:dev:watch": "lerna run --parallel build:dev:watch", "build:types:watch": "ts-node scripts/build-types-watch.ts", diff --git a/packages/angular/package.json b/packages/angular/package.json index 24952960e55b..e3fcb5dd0a93 100644 --- a/packages/angular/package.json +++ b/packages/angular/package.json @@ -41,6 +41,7 @@ "build": "yarn build:ngc", "build:ngc": "ng build --prod", "build:dev": "run-s build", + "build:extras": "yarn build", "build:watch": "run-p build:ngc:watch", "build:ngc:watch": "ng build --prod --watch", "build:npm": "npm pack ./build", diff --git a/packages/ember/package.json b/packages/ember/package.json index f4f9cd2e1894..40c00646e7d6 100644 --- a/packages/ember/package.json +++ b/packages/ember/package.json @@ -18,6 +18,7 @@ }, "scripts": { "build": "ember build --environment=production", + "build:extras": "yarn build", "build:npm": "ember ts:precompile && npm pack && ember ts:clean", "clean": "yarn rimraf sentry-ember-*.tgz", "lint": "npm-run-all --aggregate-output --continue-on-error --parallel lint:*", diff --git a/packages/gatsby/package.json b/packages/gatsby/package.json index e555c220b242..997bfe69ecbe 100644 --- a/packages/gatsby/package.json +++ b/packages/gatsby/package.json @@ -35,8 +35,9 @@ "react": "^18.0.0" }, "scripts": { - "build": "run-p build:rollup build:types build:plugin", + "build": "run-p build:rollup build:types && yarn build:extras", "build:dev": "run-s build", + "build:extras": "yarn build:plugin", "build:plugin": "tsc -p tsconfig.plugin.json", "build:rollup": "rollup -c rollup.npm.config.js", "build:types": "tsc -p tsconfig.types.json", diff --git a/packages/serverless/package.json b/packages/serverless/package.json index 1e99c704cafe..1db678aed15e 100644 --- a/packages/serverless/package.json +++ b/packages/serverless/package.json @@ -38,9 +38,10 @@ "read-pkg": "^5.2.0" }, "scripts": { - "build": "run-p build:rollup build:types && yarn build:awslambda-layer", + "build": "run-p build:rollup build:types && yarn build:extras", "build:awslambda-layer": "node scripts/build-awslambda-layer.js", "build:dev": "run-p build:rollup build:types", + "build:extras": "yarn build:awslambda-layer", "build:rollup": "rollup -c rollup.npm.config.js", "build:types": "tsc -p tsconfig.types.json", "build:watch": "run-p build:rollup:watch build:types:watch", diff --git a/packages/tracing/package.json b/packages/tracing/package.json index d6d0d3b1ca94..8344bbdec582 100644 --- a/packages/tracing/package.json +++ b/packages/tracing/package.json @@ -26,9 +26,11 @@ "@types/express": "^4.17.1" }, "scripts": { - "build": "run-p build:rollup build:types build:bundle && ts-node ../../scripts/prepack.ts --bundles #necessary for integration tests", + "build": "run-p build:rollup build:types build:bundle && yarn build:extras #necessary for integration tests", "build:bundle": "rollup --config rollup.bundle.config.js", "build:dev": "run-p build:rollup build:types", + "build:extras": "yarn build:prepack", + "build:prepack": "ts-node ../../scripts/prepack.ts --bundles", "build:rollup": "rollup -c rollup.npm.config.js", "build:types": "tsc -p tsconfig.types.json", "build:watch": "run-p build:rollup:watch build:bundle:watch build:types:watch", diff --git a/packages/tracing/tsconfig.types.json b/packages/tracing/tsconfig.types.json index 374fd9bc9364..1df465fa6534 100644 --- a/packages/tracing/tsconfig.types.json +++ b/packages/tracing/tsconfig.types.json @@ -1,6 +1,12 @@ { "extends": "./tsconfig.json", + // We don't need types for this because we don't ship it in our npm bundle. Skipping it here also lets us get around + // the fact that it introduces a dependency on `@sentry/browser` which doesn't exist anywhere else in the SDK, which + // then prevents us from building that and this at the same time when doing a parallellized build from the repo root + // level. + "exclude": ["src/index.bundle.ts"], + "compilerOptions": { "declaration": true, "declarationMap": true, From 3534f15c086b5198eafe2f4c4059440c92200696 Mon Sep 17 00:00:00 2001 From: Katie Byers Date: Fri, 13 May 2022 06:30:27 -0700 Subject: [PATCH 163/204] ref(dev): Split up top-level `yarn clean` script (#5095) This splits the repo-level `yarn clean` command, which currently leaves the repo in a broken state (build artifacts deleted, and unable to be rebuilt because package-level `node_modules` folders - though not the top `node_modules` folder - have also been deleted), into a number of new, more focused commands. In the `clean:deps` command, it now deletes _all_ `node_modules` folders (repo- and package-level) and reinstalls dependencies, so that the repo is no longer broken after running it. The new commands: ``` // Meant to be a useful default for day-to-day use "clean": "run-p clean:build clean:caches" // Runs all package-level clean commands, which delete build and testing artifacts "clean:build": "lerna run --parallel clean" // TODO: Are there other caches we should add here? "clean:caches": "yarn rimraf eslintcache && yarn jest --clearCache" // Nuke all node modules and reinstall dependencies "clean:deps": "lerna clean --yes && rm -rf node_modules && yarn" // insert "Clean all the things!" meme here "clean:all": "run-p clean:build clean:caches clean:deps" ``` --- package.json | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/package.json b/package.json index 37a546b16e04..e5df6c69b4a8 100644 --- a/package.json +++ b/package.json @@ -12,7 +12,11 @@ "build:types:watch": "ts-node scripts/build-types-watch.ts", "build:npm": "lerna run --parallel build:npm", "circularDepCheck": "lerna run --parallel circularDepCheck", - "clean": "lerna run --parallel clean && lerna clean --yes && yarn rimraf eslintcache", + "clean": "run-p clean:build clean:caches", + "clean:build": "lerna run --parallel clean", + "clean:caches": "yarn rimraf eslintcache && yarn jest --clearCache", + "clean:deps": "lerna clean --yes && rm -rf node_modules && yarn", + "clean:all": "run-p clean:build clean:caches clean:deps", "codecov": "codecov", "fix": "lerna run --parallel fix", "link:yarn": "lerna exec --parallel yarn link", From cac6312298bfe29042781432582c0a348438e1fd Mon Sep 17 00:00:00 2001 From: Katie Byers Date: Fri, 13 May 2022 06:36:56 -0700 Subject: [PATCH 164/204] fix(build): Fix express import in `gcpfunction` (#5097) This fixes a spot where we're importing types only to _say_ that we're importing types only, in order to get rid of a warning in build. (If we don't say so, it gets mad because we only have `@types/express`, not `express`, as a listed dependency.) --- packages/serverless/src/gcpfunction/general.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/serverless/src/gcpfunction/general.ts b/packages/serverless/src/gcpfunction/general.ts index 4ba794958ec4..9c5f95615506 100644 --- a/packages/serverless/src/gcpfunction/general.ts +++ b/packages/serverless/src/gcpfunction/general.ts @@ -1,6 +1,6 @@ import { Scope } from '@sentry/node'; import { Context as SentryContext } from '@sentry/types'; -import { Request, Response } from 'express'; // eslint-disable-line import/no-extraneous-dependencies +import type { Request, Response } from 'express'; import { hostname } from 'os'; export interface HttpFunction { From 98bc01a4b5d4e99724ccc6997a9f9cfa9570713e Mon Sep 17 00:00:00 2001 From: Katie Byers Date: Fri, 13 May 2022 06:53:41 -0700 Subject: [PATCH 165/204] ref(dev): Consolidate `.gitignore` files (#5096) This removes `.gitignore` files containing redundant entries (ones covered by the main `.gitignore`) and organizes the main `.gitignore` just a bit. It also removes the old build directories, `cjs` and `esm`, from the main `.gitignore`. --- .gitignore | 11 +++++------ packages/integration-tests/.gitignore | 1 - packages/node-integration-tests/.gitignore | 1 - packages/node/test/manual/webpack-domain/.gitignore | 1 - packages/serverless/.gitignore | 1 + packages/wasm/.gitignore | 4 ---- 6 files changed, 6 insertions(+), 13 deletions(-) delete mode 100644 packages/integration-tests/.gitignore delete mode 100644 packages/node-integration-tests/.gitignore delete mode 100644 packages/node/test/manual/webpack-domain/.gitignore create mode 100644 packages/serverless/.gitignore delete mode 100644 packages/wasm/.gitignore diff --git a/.gitignore b/.gitignore index 0dc14b0dea7a..3c490212dcc2 100644 --- a/.gitignore +++ b/.gitignore @@ -4,15 +4,16 @@ packages/*/package-lock.json package-lock.json # build and test +# SDK builds build/ -packages/*/cjs/ -packages/*/esm/ +# various integration test builds +dist/ coverage/ scratch/ +*.d.ts +*.js.map *.pyc *.tsbuildinfo -scenarios/*/dist/ -dist-serverless/ # transpiled transformers jest/transformers/*.js # node tarballs @@ -45,5 +46,3 @@ tmp.js # eslint .eslintcache eslintcache/* - -*.d.ts diff --git a/packages/integration-tests/.gitignore b/packages/integration-tests/.gitignore deleted file mode 100644 index 1521c8b7652b..000000000000 --- a/packages/integration-tests/.gitignore +++ /dev/null @@ -1 +0,0 @@ -dist diff --git a/packages/node-integration-tests/.gitignore b/packages/node-integration-tests/.gitignore deleted file mode 100644 index 3c3629e647f5..000000000000 --- a/packages/node-integration-tests/.gitignore +++ /dev/null @@ -1 +0,0 @@ -node_modules diff --git a/packages/node/test/manual/webpack-domain/.gitignore b/packages/node/test/manual/webpack-domain/.gitignore deleted file mode 100644 index 1521c8b7652b..000000000000 --- a/packages/node/test/manual/webpack-domain/.gitignore +++ /dev/null @@ -1 +0,0 @@ -dist diff --git a/packages/serverless/.gitignore b/packages/serverless/.gitignore new file mode 100644 index 000000000000..466d272fde97 --- /dev/null +++ b/packages/serverless/.gitignore @@ -0,0 +1 @@ +dist-serverless/ diff --git a/packages/wasm/.gitignore b/packages/wasm/.gitignore deleted file mode 100644 index 22f01289c6db..000000000000 --- a/packages/wasm/.gitignore +++ /dev/null @@ -1,4 +0,0 @@ -*.js.map -*.d.ts -!test/*.js -!.eslintrc.js From 78d730488878f3ab3baace0a7251f0baaf4c3576 Mon Sep 17 00:00:00 2001 From: Katie Byers Date: Fri, 13 May 2022 08:20:54 -0700 Subject: [PATCH 166/204] add changelog entries (#5102) Releasing a patch to get the express import change, which turns to to fix a bug with the structure of the build artifacts for serverless. --- CHANGELOG.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 44499728af0e..26742610a78e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,12 @@ - "You miss 100 percent of the chances you don't take. — Wayne Gretzky" — Michael Scott +## 7.0.0-beta.2 + +- fix(build): Fix express import in `gcpfunction` (#5097) +- fix(ember): Export sha hashes of injected scripts (#5089) +- ref(serverless): Do not throw on flush error (#5090) + ## 7.0.0-beta.1 - **(breaking)** ref: Remove critical severity (#5032) From 6b6e746a36235d269831b0c1b59afd1df083940c Mon Sep 17 00:00:00 2001 From: Katie Byers Date: Mon, 16 May 2022 20:55:43 -0700 Subject: [PATCH 167/204] fix(build): Narrow scope of build cache in GHA (#5105) Currently, as part of caching our built files in GHA, we cache any directory named `build`, `cjs`, or `esm`, no matter how deeply nested. As a result, we end up caching files from any number of node modules which happen to contain directories of the same name(s). Since we have a separate dependency cache, this is redundant and only serves to slow down our CI checks. This fixes that problem by narrowing the scope to the `build` folder at the top level of each package, so that now we upload ~2700 files rather than ~4000. Two legitimate files which would otherwise be excluded as a result of this change have also been added to the cache. --- .github/workflows/build.yml | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 5a4895da5793..933ec85c3af9 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -24,15 +24,16 @@ env: # DEPENDENCY_CACHE_KEY: can't be set here because we don't have access to yarn.lock + # packages/utils/cjs and packages/utils/esm: Symlinks to the folders inside of `build`, needed for tests CACHED_BUILD_PATHS: | - ${{ github.workspace }}/packages/**/build - ${{ github.workspace }}/packages/**/cjs - ${{ github.workspace }}/packages/**/esm + ${{ github.workspace }}/packages/*/build ${{ github.workspace }}/packages/ember/*.d.ts ${{ github.workspace }}/packages/ember/instance-initializers ${{ github.workspace }}/packages/gatsby/*.d.ts ${{ github.workspace }}/packages/core/src/version.ts ${{ github.workspace }}/dist-serverless + ${{ github.workspace }}/packages/utils/cjs + ${{ github.workspace }}/packages/utils/esm BUILD_CACHE_KEY: ${{ github.event.inputs.commit || github.sha }} From 774065f4f6032de15a7345b2d51b89c5278b43a6 Mon Sep 17 00:00:00 2001 From: Katie Byers Date: Tue, 17 May 2022 00:38:07 -0700 Subject: [PATCH 168/204] chore(dev): Clean up main tsconfigs (#5114) This does some clean up work on our two main tsconfig files, in `@sentry/typescript` and at the root level of our repo. Included changes: - Removed `pretty` - what we had is the default - Removed `noImplicitAny` and `noImplicitThis` - implied by `strict: true` - Turned off emitting types files, as we now do this separately - Moved `noErrorTruncation` up to the typescript package, so that anyone (is there anyone besides us?) using it gets the benefit of actually being able to see full errors --- packages/typescript/tsconfig.json | 5 +---- tsconfig.json | 7 ++----- 2 files changed, 3 insertions(+), 9 deletions(-) diff --git a/packages/typescript/tsconfig.json b/packages/typescript/tsconfig.json index 217159d3b73c..6ffd79b4ccc7 100644 --- a/packages/typescript/tsconfig.json +++ b/packages/typescript/tsconfig.json @@ -8,18 +8,15 @@ "inlineSources": true, "isolatedModules": true, "lib": ["es6", "dom"], - // "module": "commonjs", // implied by "target" : "es5" "moduleResolution": "node", "noEmitHelpers": true, + "noErrorTruncation": true, "noFallthroughCasesInSwitch": true, - "noImplicitAny": true, "noImplicitReturns": true, - "noImplicitThis": true, "noImplicitUseStrict": true, "noUnusedLocals": true, "noUnusedParameters": true, "preserveWatchOutput": true, - "pretty": true, "sourceMap": true, "strict": true, "strictBindCallApply": false, diff --git a/tsconfig.json b/tsconfig.json index f2ffa0c4e07c..d3ca923d9e28 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -2,12 +2,9 @@ "extends": "./packages/typescript/tsconfig.json", "compilerOptions": { - // TODO: turn these on once we switch to only generating types once, using `tsconfig.types.json` - // "declaration": false, - // "declarationMap": false, - "allowSyntheticDefaultImports": true, + "declaration": false, + "declarationMap": false, "types": ["node"], - "noErrorTruncation": true // move me up to @sentry/typescript } } From 797fe90bafe8647500ef593960be51644fea143a Mon Sep 17 00:00:00 2001 From: Katie Byers Date: Tue, 17 May 2022 00:40:05 -0700 Subject: [PATCH 169/204] fix(build): Strip `eslint-enable` comments in rollup plugin (#5112) The `removeESLintCommentsPlugin` rollup plugin we use during build strips all comments beginning `eslint-disable`, but misses comments beginning `eslint-enable`. This makes the regex used in that plugin more general, so that it catches all comments beginning with `eslint-`. --- rollup/plugins/npmPlugins.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rollup/plugins/npmPlugins.js b/rollup/plugins/npmPlugins.js index 1656feaab53b..ccdb99bb2ba1 100644 --- a/rollup/plugins/npmPlugins.js +++ b/rollup/plugins/npmPlugins.js @@ -104,7 +104,7 @@ export function makeRemoveESLintCommentsPlugin() { return regexReplace({ patterns: [ { - test: /\/[/*] eslint-disable.*\n/g, + test: /\/[/*] eslint-.*\n/g, replace: '', }, ], From 95db446f467bd8011a8b01f6621a1172a00be6ff Mon Sep 17 00:00:00 2001 From: Katie Byers Date: Tue, 17 May 2022 06:07:52 -0700 Subject: [PATCH 170/204] fix(build): Prevent redundant typechecking (#5115) When the tsconfig option`skipLibCheck`[1] is false (as it is by default), TS typechecks not only your code, and not only the code you use from dependencies, but the entire codebase of all of your dependencies. Not only does this seem like overkill, in our case it's actually largely redundant, because our packages have very few dependencies other than each other. Turning this off speeds up the build and prevents us from, for example, typechecking the entire `utils` package 15 times (once for its own sake, and once for every one of the 14 packages which depend on it). [1] https://www.typescriptlang.org/tsconfig#skipLibCheck --- tsconfig.json | 1 + 1 file changed, 1 insertion(+) diff --git a/tsconfig.json b/tsconfig.json index d3ca923d9e28..9f46e21db53c 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -5,6 +5,7 @@ "allowSyntheticDefaultImports": true, "declaration": false, "declarationMap": false, + "skipLibCheck": true, "types": ["node"], } } From 66a6b59985e616ab8701d0420cbd3c12bd39a63d Mon Sep 17 00:00:00 2001 From: Katie Byers Date: Tue, 17 May 2022 06:10:41 -0700 Subject: [PATCH 171/204] ref(build): Use sucrase for es6 bundles (#5111) This switches the building of our es6 CDN bundles to use sucrase instead of `tsc` (or, more accurately, to use the sucrase rollup plugin rather than the typescript rollup plugin), in order both to build more quickly (in a time trial of multiple runs on GHA, this change brings the average `yarn build` run down from ~8 minutes to ~4 minutes) and to ensure that we have exact code parity between our CDN bundles and our npm packages. Because sucrase doesn't down-compile the way `tsc` will, the building of the es5 bundles hasn't been changed, and that build still uses the typescript plugin. --- rollup/bundleHelpers.js | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/rollup/bundleHelpers.js b/rollup/bundleHelpers.js index d4ddc0e706e2..7a3d08dce715 100644 --- a/rollup/bundleHelpers.js +++ b/rollup/bundleHelpers.js @@ -11,6 +11,9 @@ import { makeIsDebugBuildPlugin, makeLicensePlugin, makeNodeResolvePlugin, + makeRemoveBlankLinesPlugin, + makeRemoveESLintCommentsPlugin, + makeSucrasePlugin, makeTerserPlugin, makeTSPlugin, } from './plugins/index.js'; @@ -20,6 +23,9 @@ export function makeBaseBundleConfig(options) { const { input, isAddOn, jsVersion, licenseTitle, outputFileBase } = options; const nodeResolvePlugin = makeNodeResolvePlugin(); + const sucrasePlugin = makeSucrasePlugin(); + const removeBlankLinesPlugin = makeRemoveBlankLinesPlugin(); + const removeESLintCommentsPlugin = makeRemoveESLintCommentsPlugin(); const markAsBrowserBuildPlugin = makeBrowserBuildPlugin(true); const licensePlugin = makeLicensePlugin(licenseTitle); const tsPlugin = makeTSPlugin(jsVersion.toLowerCase()); @@ -75,7 +81,17 @@ export function makeBaseBundleConfig(options) { strict: false, esModule: false, }, - plugins: [tsPlugin, markAsBrowserBuildPlugin, nodeResolvePlugin, licensePlugin], + plugins: + jsVersion === 'es5' + ? [tsPlugin, markAsBrowserBuildPlugin, nodeResolvePlugin, licensePlugin] + : [ + sucrasePlugin, + removeBlankLinesPlugin, + removeESLintCommentsPlugin, + markAsBrowserBuildPlugin, + nodeResolvePlugin, + licensePlugin, + ], treeshake: 'smallest', }; From 33e48ca9652a0813ce039b454f98533ddddbf684 Mon Sep 17 00:00:00 2001 From: Katie Byers Date: Tue, 17 May 2022 08:54:56 -0700 Subject: [PATCH 172/204] chore(tests): Fix react `act` warning in tests (#5113) When running tests, React will throw a warning unless you surround state changes with `act(() => )`[1]. This does that everywhere necessary to get rid of the warning. [1] https://reactjs.org/docs/test-utils.html#act --- packages/react/test/reactrouterv3.test.tsx | 16 +++++++++---- packages/react/test/reactrouterv4.test.tsx | 24 +++++++++++++++----- packages/react/test/reactrouterv5.test.tsx | 26 ++++++++++++++++------ 3 files changed, 49 insertions(+), 17 deletions(-) diff --git a/packages/react/test/reactrouterv3.test.tsx b/packages/react/test/reactrouterv3.test.tsx index e36a8d20d9ab..1faf7c7ce6f0 100644 --- a/packages/react/test/reactrouterv3.test.tsx +++ b/packages/react/test/reactrouterv3.test.tsx @@ -58,7 +58,9 @@ describe('React Router V3', () => { instrumentation(mockStartTransaction); render({routes}); - history.push('/about'); + act(() => { + history.push('/about'); + }); expect(mockStartTransaction).toHaveBeenCalledTimes(2); expect(mockStartTransaction).toHaveBeenLastCalledWith({ name: '/about', @@ -66,7 +68,9 @@ describe('React Router V3', () => { tags: { from: '/', 'routing.instrumentation': 'react-router-v3' }, }); - history.push('/features'); + act(() => { + history.push('/features'); + }); expect(mockStartTransaction).toHaveBeenCalledTimes(3); expect(mockStartTransaction).toHaveBeenLastCalledWith({ name: '/features', @@ -87,7 +91,9 @@ describe('React Router V3', () => { instrumentation(mockStartTransaction); render({routes}); - history.replace('hello'); + act(() => { + history.replace('hello'); + }); expect(mockStartTransaction).toHaveBeenCalledTimes(1); }); @@ -98,7 +104,9 @@ describe('React Router V3', () => { render({routes}); expect(mockStartTransaction).toHaveBeenCalledTimes(1); - history.push('/features'); + act(() => { + history.push('/features'); + }); expect(mockFinish).toHaveBeenCalledTimes(1); expect(mockStartTransaction).toHaveBeenCalledTimes(2); }); diff --git a/packages/react/test/reactrouterv4.test.tsx b/packages/react/test/reactrouterv4.test.tsx index 89caf7b398ee..60f617516482 100644 --- a/packages/react/test/reactrouterv4.test.tsx +++ b/packages/react/test/reactrouterv4.test.tsx @@ -58,7 +58,9 @@ describe('React Router v4', () => { , ); - history.push('/about'); + act(() => { + history.push('/about'); + }); expect(mockStartTransaction).toHaveBeenCalledTimes(2); expect(mockStartTransaction).toHaveBeenLastCalledWith({ name: '/about', @@ -66,7 +68,9 @@ describe('React Router v4', () => { tags: { 'routing.instrumentation': 'react-router-v4' }, }); - history.push('/features'); + act(() => { + history.push('/features'); + }); expect(mockStartTransaction).toHaveBeenCalledTimes(3); expect(mockStartTransaction).toHaveBeenLastCalledWith({ name: '/features', @@ -101,7 +105,9 @@ describe('React Router v4', () => { , ); - history.replace('hello'); + act(() => { + history.replace('hello'); + }); expect(mockStartTransaction).toHaveBeenCalledTimes(1); }); @@ -118,7 +124,9 @@ describe('React Router v4', () => { ); expect(mockStartTransaction).toHaveBeenCalledTimes(1); - history.push('/features'); + act(() => { + history.push('/features'); + }); expect(mockFinish).toHaveBeenCalledTimes(1); expect(mockStartTransaction).toHaveBeenCalledTimes(2); }); @@ -237,7 +245,9 @@ describe('React Router v4', () => { , ); - history.push('/organizations/1234/v1/758'); + act(() => { + history.push('/organizations/1234/v1/758'); + }); expect(mockStartTransaction).toHaveBeenCalledTimes(2); expect(mockStartTransaction).toHaveBeenLastCalledWith({ name: '/organizations/:orgid/v1/:teamid', @@ -245,7 +255,9 @@ describe('React Router v4', () => { tags: { 'routing.instrumentation': 'react-router-v4' }, }); - history.push('/organizations/1234'); + act(() => { + history.push('/organizations/1234'); + }); expect(mockStartTransaction).toHaveBeenCalledTimes(3); expect(mockStartTransaction).toHaveBeenLastCalledWith({ name: '/organizations/:orgid', diff --git a/packages/react/test/reactrouterv5.test.tsx b/packages/react/test/reactrouterv5.test.tsx index 5d9cd66c9898..daa5a4fa5554 100644 --- a/packages/react/test/reactrouterv5.test.tsx +++ b/packages/react/test/reactrouterv5.test.tsx @@ -1,4 +1,4 @@ -import { act,render } from '@testing-library/react'; +import { act, render } from '@testing-library/react'; import { createMemoryHistory } from 'history-4'; import * as React from 'react'; import { matchPath, Route, Router, Switch } from 'react-router-5'; @@ -58,7 +58,9 @@ describe('React Router v5', () => { , ); - history.push('/about'); + act(() => { + history.push('/about'); + }); expect(mockStartTransaction).toHaveBeenCalledTimes(2); expect(mockStartTransaction).toHaveBeenLastCalledWith({ name: '/about', @@ -66,7 +68,9 @@ describe('React Router v5', () => { tags: { 'routing.instrumentation': 'react-router-v5' }, }); - history.push('/features'); + act(() => { + history.push('/features'); + }); expect(mockStartTransaction).toHaveBeenCalledTimes(3); expect(mockStartTransaction).toHaveBeenLastCalledWith({ name: '/features', @@ -101,7 +105,9 @@ describe('React Router v5', () => { , ); - history.replace('hello'); + act(() => { + history.replace('hello'); + }); expect(mockStartTransaction).toHaveBeenCalledTimes(1); }); @@ -118,7 +124,9 @@ describe('React Router v5', () => { ); expect(mockStartTransaction).toHaveBeenCalledTimes(1); - history.push('/features'); + act(() => { + history.push('/features'); + }); expect(mockFinish).toHaveBeenCalledTimes(1); expect(mockStartTransaction).toHaveBeenCalledTimes(2); }); @@ -238,7 +246,9 @@ describe('React Router v5', () => { , ); - history.push('/organizations/1234/v1/758'); + act(() => { + history.push('/organizations/1234/v1/758'); + }); expect(mockStartTransaction).toHaveBeenCalledTimes(2); expect(mockStartTransaction).toHaveBeenLastCalledWith({ name: '/organizations/:orgid/v1/:teamid', @@ -246,7 +256,9 @@ describe('React Router v5', () => { tags: { 'routing.instrumentation': 'react-router-v5' }, }); - history.push('/organizations/1234'); + act(() => { + history.push('/organizations/1234'); + }); expect(mockStartTransaction).toHaveBeenCalledTimes(3); expect(mockStartTransaction).toHaveBeenLastCalledWith({ name: '/organizations/:orgid', From 0a6b4f2afbbbeed83d4bee046c28dc4727e8306e Mon Sep 17 00:00:00 2001 From: Abhijeet Prasad Date: Tue, 17 May 2022 11:57:57 -0400 Subject: [PATCH 173/204] feat(utils): Introduce Baggage API (#5066) This patch introduced a basic data structure for baggage, as well as controls to create, manipulate and serialize the baggage data structure. The baggage data structure represents key,value pairs based on the baggage spec: https://www.w3.org/TR/baggage It is expected that users interact with baggage using the helpers methods: `createBaggage`, `getBaggageValue`, and `setBaggageValue`. Internally, the baggage data structure is a tuple of length 2, separating baggage values based on if they are related to Sentry or not. If the baggage values are set/used by sentry, they will be stored in an object to be easily accessed. If they are not, they are kept as a string to be only accessed when serialized at baggage propagation time. As a next step, let's add the baggage values to the envelope header so they can be processed and used by relay - this will allow us to do some early validations of our assumptions. --- packages/utils/src/baggage.ts | 86 +++++++++++++++++++++++++++ packages/utils/test/baggage.test.ts | 92 +++++++++++++++++++++++++++++ 2 files changed, 178 insertions(+) create mode 100644 packages/utils/src/baggage.ts create mode 100644 packages/utils/test/baggage.test.ts diff --git a/packages/utils/src/baggage.ts b/packages/utils/src/baggage.ts new file mode 100644 index 000000000000..db402608a22b --- /dev/null +++ b/packages/utils/src/baggage.ts @@ -0,0 +1,86 @@ +import { IS_DEBUG_BUILD } from './flags'; +import { logger } from './logger'; + +export type AllowedBaggageKeys = 'environment' | 'release'; // TODO: Add remaining allowed baggage keys | 'transaction' | 'userid' | 'usersegment'; +export type BaggageObj = Partial & Record>; + +/** + * The baggage data structure represents key,value pairs based on the baggage + * spec: https://www.w3.org/TR/baggage + * + * It is expected that users interact with baggage using the helpers methods: + * `createBaggage`, `getBaggageValue`, and `setBaggageValue`. + * + * Internally, the baggage data structure is a tuple of length 2, separating baggage values + * based on if they are related to Sentry or not. If the baggage values are + * set/used by sentry, they will be stored in an object to be easily accessed. + * If they are not, they are kept as a string to be only accessed when serialized + * at baggage propagation time. + */ +export type Baggage = [BaggageObj, string]; + +export const BAGGAGE_HEADER_NAME = 'baggage'; + +export const SENTRY_BAGGAGE_KEY_PREFIX = 'sentry-'; + +export const SENTRY_BAGGAGE_KEY_PREFIX_REGEX = /^sentry-/; + +/** + * Max length of a serialized baggage string + * + * https://www.w3.org/TR/baggage/#limits + */ +export const MAX_BAGGAGE_STRING_LENGTH = 8192; + +/** Create an instance of Baggage */ +export function createBaggage(initItems: BaggageObj, baggageString: string = ''): Baggage { + return [{ ...initItems }, baggageString]; +} + +/** Get a value from baggage */ +export function getBaggageValue(baggage: Baggage, key: keyof BaggageObj): BaggageObj[keyof BaggageObj] { + return baggage[0][key]; +} + +/** Add a value to baggage */ +export function setBaggageValue(baggage: Baggage, key: keyof BaggageObj, value: BaggageObj[keyof BaggageObj]): void { + baggage[0][key] = value; +} + +/** Serialize a baggage object */ +export function serializeBaggage(baggage: Baggage): string { + return Object.keys(baggage[0]).reduce((prev, key: keyof BaggageObj) => { + const val = baggage[0][key] as string; + const baggageEntry = `${SENTRY_BAGGAGE_KEY_PREFIX}${encodeURIComponent(key)}=${encodeURIComponent(val)}`; + const newVal = prev === '' ? baggageEntry : `${prev},${baggageEntry}`; + if (newVal.length > MAX_BAGGAGE_STRING_LENGTH) { + IS_DEBUG_BUILD && + logger.warn(`Not adding key: ${key} with val: ${val} to baggage due to exceeding baggage size limits.`); + return prev; + } else { + return newVal; + } + }, baggage[1]); +} + +/** Parse a baggage header to a string */ +export function parseBaggageString(inputBaggageString: string): Baggage { + return inputBaggageString.split(',').reduce( + ([baggageObj, baggageString], curr) => { + const [key, val] = curr.split('='); + if (SENTRY_BAGGAGE_KEY_PREFIX_REGEX.test(key)) { + const baggageKey = decodeURIComponent(key.split('-')[1]); + return [ + { + ...baggageObj, + [baggageKey]: decodeURIComponent(val), + }, + baggageString, + ]; + } else { + return [baggageObj, baggageString === '' ? curr : `${baggageString},${curr}`]; + } + }, + [{}, ''], + ); +} diff --git a/packages/utils/test/baggage.test.ts b/packages/utils/test/baggage.test.ts new file mode 100644 index 000000000000..08d4213f2a99 --- /dev/null +++ b/packages/utils/test/baggage.test.ts @@ -0,0 +1,92 @@ +import { createBaggage, getBaggageValue, parseBaggageString, serializeBaggage, setBaggageValue } from '../src/baggage'; + +describe('Baggage', () => { + describe('createBaggage', () => { + it.each([ + ['creates an empty baggage instance', {}, [{}, '']], + [ + 'creates a baggage instance with initial values', + { environment: 'production', anyKey: 'anyValue' }, + [{ environment: 'production', anyKey: 'anyValue' }, ''], + ], + ])('%s', (_: string, input, output) => { + expect(createBaggage(input)).toEqual(output); + }); + }); + + describe('getBaggageValue', () => { + it.each([ + [ + 'gets a baggage item', + createBaggage({ environment: 'production', anyKey: 'anyValue' }), + 'environment', + 'production', + ], + ['finds undefined items', createBaggage({}), 'environment', undefined], + ])('%s', (_: string, baggage, key, value) => { + expect(getBaggageValue(baggage, key)).toEqual(value); + }); + }); + + describe('setBaggageValue', () => { + it.each([ + ['sets a baggage item', createBaggage({}), 'environment', 'production'], + ['overwrites a baggage item', createBaggage({ environment: 'development' }), 'environment', 'production'], + ])('%s', (_: string, baggage, key, value) => { + setBaggageValue(baggage, key, value); + expect(getBaggageValue(baggage, key)).toEqual(value); + }); + }); + + describe('serializeBaggage', () => { + it.each([ + ['serializes empty baggage', createBaggage({}), ''], + [ + 'serializes baggage with a single value', + createBaggage({ environment: 'production' }), + 'sentry-environment=production', + ], + [ + 'serializes baggage with multiple values', + createBaggage({ environment: 'production', release: '10.0.2' }), + 'sentry-environment=production,sentry-release=10.0.2', + ], + [ + 'keeps non-sentry prefixed baggage items', + createBaggage( + { environment: 'production', release: '10.0.2' }, + 'userId=alice,serverNode=DF%2028,isProduction=false', + ), + 'userId=alice,serverNode=DF%2028,isProduction=false,sentry-environment=production,sentry-release=10.0.2', + ], + [ + 'can only use non-sentry prefixed baggage items', + createBaggage({}, 'userId=alice,serverNode=DF%2028,isProduction=false'), + 'userId=alice,serverNode=DF%2028,isProduction=false', + ], + ])('%s', (_: string, baggage, serializedBaggage) => { + expect(serializeBaggage(baggage)).toEqual(serializedBaggage); + }); + }); + + describe('parseBaggageString', () => { + it.each([ + ['parses an empty string', '', createBaggage({})], + [ + 'parses sentry values into baggage', + 'sentry-environment=production,sentry-release=10.0.2', + createBaggage({ environment: 'production', release: '10.0.2' }), + ], + [ + 'parses arbitrary baggage headers', + 'userId=alice,serverNode=DF%2028,isProduction=false,sentry-environment=production,sentry-release=10.0.2', + createBaggage( + { environment: 'production', release: '10.0.2' }, + 'userId=alice,serverNode=DF%2028,isProduction=false', + ), + ], + ])('%s', (_: string, baggageString, baggage) => { + expect(parseBaggageString(baggageString)).toEqual(baggage); + }); + }); +}); From 888f2b8abcd4dbfbf308354758b99a5fab35c845 Mon Sep 17 00:00:00 2001 From: Katie Byers Date: Tue, 17 May 2022 18:05:01 -0700 Subject: [PATCH 174/204] chore(build): Move aws lambda build step (#5123) This moves, without making any changes, the aws lambda build job to live with the other build job in our CI workflow. (I'll admit that it's not totally obvious, but there is in fact a vague organizing principle to that doc.) Extracted from another PR to make it easier to see, in that PR, what actual changes are being made. --- .github/workflows/build.yml | 75 ++++++++++++++++++------------------- 1 file changed, 37 insertions(+), 38 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 933ec85c3af9..13834dc75ce1 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -106,12 +106,48 @@ jobs: echo "Saving SDK_VERSION for later" cat packages/core/src/version.ts | awk -F"'" '{print $2}' > dist-serverless/version [ ! -z $(cat dist-serverless/version) ] && echo SDK_VERSION=$(cat dist-serverless/version) || (echo "Version extraction failed" && exit 1) - outputs: # this needs to be passed on, because the `needs` context only looks at direct ancestors (so steps which depend on # `job_build` can't see `job_install_deps` and what it returned) dependency_cache_key: ${{ needs.job_install_deps.outputs.dependency_cache_key }} + job_build_aws_lambda_layer: + name: Build AWS Lambda Layer + needs: job_build + runs-on: ubuntu-latest + steps: + - name: Check out current commit (${{ env.HEAD_COMMIT }}) + uses: actions/checkout@v2 + with: + ref: ${{ env.HEAD_COMMIT }} + - name: Set up Node + uses: actions/setup-node@v1 + with: + node-version: ${{ env.DEFAULT_NODE_VERSION }} + - name: Check dependency cache + uses: actions/cache@v2 + with: + path: ${{ env.CACHED_DEPENDENCY_PATHS }} + key: ${{ needs.job_build.outputs.dependency_cache_key }} + - name: Check build cache + uses: actions/cache@v2 + with: + path: ${{ env.CACHED_BUILD_PATHS }} + key: ${{ env.BUILD_CACHE_KEY }} + - name: Get SDK version + run: | + export SDK_VERSION=$(cat dist-serverless/version) + echo "SDK_VERSION=$SDK_VERSION" | tee -a $GITHUB_ENV + - uses: actions/upload-artifact@v3 + with: + name: ${{ env.HEAD_COMMIT }} + path: | + dist-serverless/* + - uses: getsentry/action-build-aws-lambda-extension@v1 + with: + artifact_name: ${{ env.HEAD_COMMIT }} + zip_file_name: sentry-node-serverless-${{ env.SDK_VERSION }}.zip + job_size_check: name: Size Check needs: job_build @@ -508,40 +544,3 @@ jobs: run: | cd packages/node-integration-tests yarn test - - job_build_aws_lambda_layer: - name: Build AWS Lambda Layer - needs: job_build - runs-on: ubuntu-latest - steps: - - name: Check out current commit (${{ env.HEAD_COMMIT }}) - uses: actions/checkout@v2 - with: - ref: ${{ env.HEAD_COMMIT }} - - name: Set up Node - uses: actions/setup-node@v1 - with: - node-version: ${{ env.DEFAULT_NODE_VERSION }} - - name: Check dependency cache - uses: actions/cache@v2 - with: - path: ${{ env.CACHED_DEPENDENCY_PATHS }} - key: ${{ needs.job_build.outputs.dependency_cache_key }} - - name: Check build cache - uses: actions/cache@v2 - with: - path: ${{ env.CACHED_BUILD_PATHS }} - key: ${{ env.BUILD_CACHE_KEY }} - - name: Get SDK version - run: | - export SDK_VERSION=$(cat dist-serverless/version) - echo "SDK_VERSION=$SDK_VERSION" | tee -a $GITHUB_ENV - - uses: actions/upload-artifact@v3 - with: - name: ${{ env.HEAD_COMMIT }} - path: | - dist-serverless/* - - uses: getsentry/action-build-aws-lambda-extension@v1 - with: - artifact_name: ${{ env.HEAD_COMMIT }} - zip_file_name: sentry-node-serverless-${{ env.SDK_VERSION }}.zip From a917ddaf8d71c8ac527925ded7c604e16c6efcb1 Mon Sep 17 00:00:00 2001 From: Katie Byers Date: Tue, 17 May 2022 20:02:29 -0700 Subject: [PATCH 175/204] fix(build): Ensure bundle builds have needed dependencies (#5119) In https://github.com/getsentry/sentry-javascript/pull/5094, a change was made to parallelize our repo-level build commands as much as possible. In that PR, it was stated that `build:bundle` could run independent of, and therefore in parallel with, the types and rollup builds, and at the time that was true. When TS (through rollup) builds a bundle, it creates a giant AST out of the bundled package and all of its monorepo dependencies, based on the original source code, then transpiles the whole thing - no prework needed. But in https://github.com/getsentry/sentry-javascript/pull/5111 we switched our ES6 bundles to use sucrase for transpilation (still through rollup), and that changed things. Sucrase (along with every other non-TS transpilation tool) only considers files one by one, not as part of a larger whole, and won't reach across package boundaries, even within the monorepo. As a result, rollup needs all dependencies to already exist in transpiled form, since sucrase doesn't touch them, which becomes a problem if both processes are happening at once. (_But how has CI even been passing since that second PR, then?_, you may ask. The answer is, sucrase is very fast, and lerna can only start so many things at once. It ends up being a race condition between sucrase finishing with the dependencies and lerna kicking off the bundle builds, and almost all the time, sucrase wins. And the other situations in which this is broken all involve using something other than the top-level `build` script to create bundles in a repo with no existing build artifacts, and CI just never does that.) So TL;DR, we need to have already transpiled a packages's monorepo dependencies before that package can be turned into a bundle. For `build:bundle` at both the repo and package level, and for `build` at the package level, this means that if they're not there, we have to build them. For `build` at the repo level, where transpilation of all packages does in fact already happen, we have two options: 1) Push bundle builds to happen alongside `build:extras`, after rollup builds have finished. 2) Mimic what happens when the race condition is successful, but in a way that isn't flaky. In other words, continue to run `build:bundle` in parallel with the types and npm package builds, but guarantee that the needed dependencies have finished building themselves before starting the bundle build. Of the two options, the first is certainly simpler, but it also forces the two longest parts of the build (bundle and types builds) to be sequential, which is the exact _opposite_ of what we want, given that the goal all along has been to make the builds noticeably faster. Choosing the second solution also gives us an excuse to add the extra few lines of code needed to fix the repo-level-build:bundle/package-level-build/package-level-build:bundle problem, which we otherwise probably wouldn't fix. This implements that second strategy, by polling at 5 second intervals for the existence of the needed files, for up to 60 seconds, before beginning the bundle builds. (In practice, it fairly reliably seems to take two retries, or ten seconds, before the bundle build can begin.) It also handles the other three situations above, by building the missing files when necessary. Finally, it fixes a type import to rely on the main types package, rather than a built version of it. This prevents our ES5 bundles (which still use TS for transpilation, in order to also take advantage of its ability to down-compile) from running to the same problem as the sucrase bundles are having. --- package.json | 2 +- packages/browser/package.json | 2 +- packages/hub/src/session.ts | 3 +- packages/integrations/package.json | 2 +- packages/tracing/package.json | 2 +- packages/types/src/index.ts | 1 + packages/vue/package.json | 2 +- packages/wasm/package.json | 2 +- scripts/ensure-bundle-deps.ts | 155 +++++++++++++++++++++++++++++ 9 files changed, 163 insertions(+), 8 deletions(-) create mode 100644 scripts/ensure-bundle-deps.ts diff --git a/package.json b/package.json index e5df6c69b4a8..09848d739f99 100644 --- a/package.json +++ b/package.json @@ -2,7 +2,7 @@ "private": true, "scripts": { "build": "node ./scripts/verify-packages-versions.js && yarn run-p build:rollup build:types build:bundle && yarn build:extras", - "build:bundle": "lerna run --parallel build:bundle", + "build:bundle": "yarn ts-node scripts/ensure-bundle-deps.ts && yarn lerna run --parallel build:bundle", "build:dev": "run-p build:types build:rollup", "build:extras": "lerna run --parallel build:extras", "build:rollup": "lerna run --parallel build:rollup", diff --git a/packages/browser/package.json b/packages/browser/package.json index 05c3cd7b30e4..487294870c5b 100644 --- a/packages/browser/package.json +++ b/packages/browser/package.json @@ -44,7 +44,7 @@ }, "scripts": { "build": "run-p build:rollup build:bundle build:types", - "build:bundle": "rollup --config rollup.bundle.config.js", + "build:bundle": "yarn ts-node ../../scripts/ensure-bundle-deps.ts && yarn rollup --config rollup.bundle.config.js", "build:dev": "run-p build:rollup build:types", "build:rollup": "rollup -c rollup.npm.config.js", "build:types": "tsc -p tsconfig.types.json", diff --git a/packages/hub/src/session.ts b/packages/hub/src/session.ts index fed5cfa004ad..c8cba3e30a04 100644 --- a/packages/hub/src/session.ts +++ b/packages/hub/src/session.ts @@ -1,5 +1,4 @@ -import { Session, SessionContext, SessionStatus } from '@sentry/types'; -import { SerializedSession } from '@sentry/types/build/types/session'; +import { SerializedSession, Session, SessionContext, SessionStatus } from '@sentry/types'; import { dropUndefinedKeys, timestampInSeconds, uuid4 } from '@sentry/utils'; /** diff --git a/packages/integrations/package.json b/packages/integrations/package.json index dc64f179c932..ea1e050dad5a 100644 --- a/packages/integrations/package.json +++ b/packages/integrations/package.json @@ -26,7 +26,7 @@ }, "scripts": { "build": "run-p build:rollup build:types build:bundle", - "build:bundle": "bash scripts/buildBundles.sh", + "build:bundle": "yarn ts-node ../../scripts/ensure-bundle-deps.ts && bash scripts/buildBundles.sh", "build:dev": "run-p build:rollup build:types", "build:rollup": "rollup -c rollup.npm.config.js", "build:types": "tsc -p tsconfig.types.json", diff --git a/packages/tracing/package.json b/packages/tracing/package.json index 8344bbdec582..25510c2a1376 100644 --- a/packages/tracing/package.json +++ b/packages/tracing/package.json @@ -27,7 +27,7 @@ }, "scripts": { "build": "run-p build:rollup build:types build:bundle && yarn build:extras #necessary for integration tests", - "build:bundle": "rollup --config rollup.bundle.config.js", + "build:bundle": "yarn ts-node ../../scripts/ensure-bundle-deps.ts && yarn rollup --config rollup.bundle.config.js", "build:dev": "run-p build:rollup build:types", "build:extras": "yarn build:prepack", "build:prepack": "ts-node ../../scripts/prepack.ts --bundles", diff --git a/packages/types/src/index.ts b/packages/types/src/index.ts index 8fe14cd3ac16..91470886bb2c 100644 --- a/packages/types/src/index.ts +++ b/packages/types/src/index.ts @@ -46,6 +46,7 @@ export type { RequestSession, RequestSessionStatus, SessionFlusherLike, + SerializedSession, } from './session'; // eslint-disable-next-line deprecation/deprecation diff --git a/packages/vue/package.json b/packages/vue/package.json index 430d46cbac0f..e1f37eade04a 100644 --- a/packages/vue/package.json +++ b/packages/vue/package.json @@ -27,7 +27,7 @@ }, "scripts": { "build": "run-p build:rollup build:types", - "build:bundle": "rollup --config rollup.bundle.config.js", + "build:bundle": "yarn ts-node ../../scripts/ensure-bundle-deps.ts && yarn rollup --config rollup.bundle.config.js", "build:dev": "run-p build:rollup build:types", "build:rollup": "rollup -c rollup.npm.config.js", "build:types": "tsc -p tsconfig.types.json", diff --git a/packages/wasm/package.json b/packages/wasm/package.json index baf0f28e3bc5..2bc4a612132b 100644 --- a/packages/wasm/package.json +++ b/packages/wasm/package.json @@ -31,7 +31,7 @@ }, "scripts": { "build": "run-p build:rollup build:bundle build:types", - "build:bundle": "rollup --config rollup.bundle.config.js", + "build:bundle": "yarn ts-node ../../scripts/ensure-bundle-deps.ts && yarn rollup --config rollup.bundle.config.js", "build:dev": "run-p build:rollup build:types", "build:rollup": "rollup -c rollup.npm.config.js", "build:types": "tsc -p tsconfig.types.json", diff --git a/scripts/ensure-bundle-deps.ts b/scripts/ensure-bundle-deps.ts new file mode 100644 index 000000000000..61e724fc0029 --- /dev/null +++ b/scripts/ensure-bundle-deps.ts @@ -0,0 +1,155 @@ +/* eslint-disable no-console */ +import * as childProcess from 'child_process'; +import * as fs from 'fs'; +import * as path from 'path'; +import * as util from 'util'; + +/** + * Ensure that `build:bundle` has all of the dependencies it needs to run. Works at both the repo and package level. + */ +async function ensureBundleBuildPrereqs(options: { dependencies: string[]; maxRetries?: number }): Promise { + const { maxRetries = 12, dependencies } = options; + + const { + // The directory in which the yarn command was originally invoked (which won't necessarily be the same as + // `process.cwd()`) + INIT_CWD: yarnInitialDir, + // JSON containing the args passed to `yarn` + npm_config_argv: yarnArgJSON, + } = process.env; + + if (!yarnInitialDir || !yarnArgJSON) { + const received = { INIT_CWD: yarnInitialDir, npm_config_argv: yarnArgJSON }; + throw new Error( + `Missing environment variables needed for ensuring bundle dependencies. Received:\n${util.inspect(received)}\n`, + ); + } + + // Did this build get invoked by a repo-level script, or a package-level script, and which script was it? + const isTopLevelBuild = path.basename(yarnInitialDir) === 'sentry-javascript'; + const yarnScript = (JSON.parse(yarnArgJSON) as { original: string[] }).original[0]; + + // convert '@sentry/xyz` to `xyz` + const dependencyDirs = dependencies.map(npmPackageName => npmPackageName.split('/')[1]); + + // The second half of the conditional tests if this script is being run by the original top-level command or a + // package-level command spawned by it. + const packagesDir = isTopLevelBuild && yarnInitialDir === process.cwd() ? 'packages' : '..'; + + if (checkForBundleDeps(packagesDir, dependencyDirs)) { + // We're good, nothing to do, the files we need are there + return; + } + + // If we get here, the at least some of the dependencies are missing, but how we handle that depends on how we got + // here. There are six possibilities: + // - We ran `build` or `build:bundle` at the repo level + // - We ran `build` or `build:bundle` at the package level + // - We ran `build` or `build:bundle` at the repo level and lerna then ran `build:bundle` at the package level. (We + // shouldn't ever land here under this scenario - the top-level build should already have handled any missing + // dependencies - but it's helpful to consider all the possibilities.) + // + // In the first version of the first scenario (repo-level `build` -> repo-level `build:bundle`), all we have to do is + // wait, because other parts of `build` are creating them as this check is being done. (Waiting 5 or 10 or even 15 + // seconds to start running `build:bundle` in parallel is better than pushing it to the second half of `build`, + // because `build:bundle` is the slowest part of the build and therefore the one we most want to parallelize with + // other slow parts, like `build:types`.) + // + // In all other scenarios, if the dependencies are missing, we have to build them ourselves - with `build:bundle` at + // either level, we're the only thing happening (so no one's going to do it for us), and with package-level `build`, + // types and npm assets are being built simultaneously, but only for the package being bundled, not for its + // dependencies. Either way, it's on us to fix the problem. + // + // TODO: This actually *doesn't* work for package-level `build`, not because of a flaw in this logic, but because + // `build:rollup` has similar dependency needs (it needs types rather than npm builds). We should do something like + // this for that at some point. + + if (isTopLevelBuild && yarnScript === 'build') { + let retries = 0; + + console.log('\nSearching for bundle dependencies...'); + + while (retries < maxRetries && !checkForBundleDeps(packagesDir, dependencyDirs)) { + console.log('Bundle dependencies not found. Trying again in 5 seconds.'); + retries += 1; + await sleep(5000); + } + + if (retries === maxRetries) { + throw new Error( + `\nERROR: \`yarn build:bundle\` (triggered by \`yarn build\`) cannot find its depdendencies, despite waiting ${ + 5 * maxRetries + } seconds for the rest of \`yarn build\` to create them. Something is wrong - it shouldn't take that long. Exiting.`, + ); + } + + console.log(`\nFound all bundle dependencies after ${retries} retries. Beginning bundle build...`); + } + + // top-level `build:bundle`, package-level `build` and `build:bundle` + else { + console.warn('\nWARNING: Missing dependencies for bundle build. They will be built before continuing.'); + + for (const dependencyDir of dependencyDirs) { + console.log(`\nBuilding \`${dependencyDir}\` package...`); + run('yarn build:rollup', { cwd: `${packagesDir}/${dependencyDir}` }); + } + + console.log('\nAll dependencies built successfully. Beginning bundle build...'); + } +} + +/** + * See if all of the necessary dependencies exist + */ +function checkForBundleDeps(packagesDir: string, dependencyDirs: string[]): boolean { + for (const dependencyDir of dependencyDirs) { + const depBuildDir = `${packagesDir}/${dependencyDir}/build`; + + // Checking that the directories exist isn't 100% the same as checking that the files themselves exist, of course, + // but it's a decent proxy, and much simpler to do than checking for individual files. + if ( + !( + (fs.existsSync(`${depBuildDir}/cjs`) && fs.existsSync(`${depBuildDir}/esm`)) || + (fs.existsSync(`${depBuildDir}/npm/cjs`) && fs.existsSync(`${depBuildDir}/npm/esm`)) + ) + ) { + // Fail fast + return false; + } + } + + return true; +} + +/** + * Wait the given number of milliseconds before continuing. + */ +async function sleep(ms: number): Promise { + await new Promise(resolve => + setTimeout(() => { + resolve(); + }, ms), + ); +} + +/** + * Run the given shell command, piping the shell process's `stdin`, `stdout`, and `stderr` to that of the current + * process. Returns contents of `stdout`. + */ +function run(cmd: string, options?: childProcess.ExecSyncOptions): string { + return String(childProcess.execSync(cmd, { stdio: 'inherit', ...options })); +} + +// TODO: Not ideal that we're hard-coding this, and it's easy to get when we're in a package directory, but would take +// more work to get from the repo level. Fortunately this list is unlikely to change very often, and we're the only ones +// we'll break if it gets out of date. +const dependencies = ['@sentry/utils', '@sentry/hub', '@sentry/core']; + +if (['sentry-javascript', 'tracing', 'wasm'].includes(path.basename(process.cwd()))) { + dependencies.push('@sentry/browser'); +} + +void ensureBundleBuildPrereqs({ + dependencies, +}); From efbd343eb5aa85628564de6569e1517eaaaa64e9 Mon Sep 17 00:00:00 2001 From: Lukas Stracke Date: Wed, 18 May 2022 14:20:24 +0200 Subject: [PATCH 176/204] docs(migration): Add upgrading information for multiple Sentry packages to TL;DR section (#5125) add a point to the TL;DR section stating that other Sentry packages like `@sentry/tracing` should be upgraded alongside the used Sentry SDK --- MIGRATION.md | 1 + 1 file changed, 1 insertion(+) diff --git a/MIGRATION.md b/MIGRATION.md index 6f8db51f1c9f..d2d1695719b4 100644 --- a/MIGRATION.md +++ b/MIGRATION.md @@ -4,6 +4,7 @@ The main goal of version 7 is to reduce bundle size. This version is breaking be Below we will outline all the breaking changes you should consider when upgrading. **TL;DR** If you only use basic features of Sentry, or you simply copy & pasted the setup examples from our docs, here's what changed for you: +- If you installed additional Sentry packages, such as`@sentry/tracing` alongside your Sentry SDK (e.g. `@sentry/react` or `@sentry/node`), make sure to upgrade all of them to version 7. - Our CDN bundles are now ES6 - you will need to [reconfigure your script tags](#renaming-of-cdn-bundles) if you want to keep supporting ES5 and IE11 on the new SDK version. - Distributed CommonJS files will be ES6. Use a transpiler if you need to support old node versions. - We bumped the TypeScript version we generate our types with to 3.8.3. Please check if your TypeScript projects using TypeScript version 3.7 or lower still compile. Otherwise, upgrade your TypeScript version. From d478c77962f77c5b5fcbd70879e1137e9b0d07cc Mon Sep 17 00:00:00 2001 From: Lukas Stracke Date: Wed, 18 May 2022 14:54:26 +0200 Subject: [PATCH 177/204] feat(core): Send Baggage in Envelope Header (#5104) This patch Introduces the first step of getting Dynamic Sampling working with the JS SDKs. It adds a very simple approach of attaching baggage to the envelope header of a transaction's event envelope. It does not take immutability of baggage in multiple transactions per trace scenarios into account. This is something we have to address when we're going to add the baggage header to outgoing requests. However, the simple approach should be enough to send baggage information to relay to get DS working on individual transactions. --- packages/core/src/baseclient.ts | 2 +- packages/core/src/envelope.ts | 47 +++++++++++++--- packages/core/test/lib/envelope.test.ts | 54 +++++++++++++++++++ .../suites/tracing/baggage/init.js | 16 ++++++ .../suites/tracing/baggage/test.ts | 16 ++++++ packages/integration-tests/utils/helpers.ts | 21 ++++++-- packages/types/src/envelope.ts | 2 +- packages/types/src/index.ts | 1 + packages/utils/src/baggage.ts | 7 ++- packages/utils/src/index.ts | 1 + packages/utils/test/baggage.test.ts | 18 ++++++- 11 files changed, 170 insertions(+), 15 deletions(-) create mode 100644 packages/core/test/lib/envelope.test.ts create mode 100644 packages/integration-tests/suites/tracing/baggage/init.js create mode 100644 packages/integration-tests/suites/tracing/baggage/test.ts diff --git a/packages/core/src/baseclient.ts b/packages/core/src/baseclient.ts index f64f2b11d114..563c8ce599c3 100644 --- a/packages/core/src/baseclient.ts +++ b/packages/core/src/baseclient.ts @@ -618,7 +618,7 @@ export abstract class BaseClient implements Client { throw new SentryError('`beforeSend` returned `null`, will not send event.'); } - const session = scope && scope.getSession && scope.getSession(); + const session = scope && scope.getSession(); if (!isTransaction && session) { this._updateSessionFromEvent(session, processedEvent); } diff --git a/packages/core/src/envelope.ts b/packages/core/src/envelope.ts index 2f69884e8bc3..149d98d5242e 100644 --- a/packages/core/src/envelope.ts +++ b/packages/core/src/envelope.ts @@ -2,6 +2,7 @@ import { DsnComponents, Event, EventEnvelope, + EventEnvelopeHeaders, EventItem, SdkInfo, SdkMetadata, @@ -10,7 +11,15 @@ import { SessionEnvelope, SessionItem, } from '@sentry/types'; -import { createEnvelope, dsnToString } from '@sentry/utils'; +import { + BaggageObj, + createBaggage, + createEnvelope, + dropUndefinedKeys, + dsnToString, + isBaggageEmpty, + serializeBaggage, +} from '@sentry/utils'; /** Extract sdk info from from the API metadata */ function getSdkMetadataForEnvelopeHeader(metadata?: SdkMetadata): SdkInfo | undefined { @@ -101,12 +110,8 @@ export function createEventEnvelope( // TODO: This is NOT part of the hack - DO NOT DELETE delete event.sdkProcessingMetadata; - const envelopeHeaders = { - event_id: event.event_id as string, - sent_at: new Date().toISOString(), - ...(sdkInfo && { sdk: sdkInfo }), - ...(!!tunnel && { dsn: dsnToString(dsn) }), - }; + const envelopeHeaders = createEventEnvelopeHeaders(event, sdkInfo, tunnel, dsn); + const eventItem: EventItem = [ { type: eventType, @@ -116,3 +121,31 @@ export function createEventEnvelope( ]; return createEnvelope(envelopeHeaders, [eventItem]); } + +function createEventEnvelopeHeaders( + event: Event, + sdkInfo: SdkInfo | undefined, + tunnel: string | undefined, + dsn: DsnComponents, +): EventEnvelopeHeaders { + const baggage = + event.type === 'transaction' && + createBaggage( + dropUndefinedKeys({ + environment: event.environment, + release: event.release, + transaction: event.transaction, + userid: event.user && event.user.id, + // user.segment currently doesn't exist explicitly in interface User (just as a record key) + usersegment: event.user && event.user.segment, + } as BaggageObj), + ); + + return { + event_id: event.event_id as string, + sent_at: new Date().toISOString(), + ...(sdkInfo && { sdk: sdkInfo }), + ...(!!tunnel && { dsn: dsnToString(dsn) }), + ...(baggage && !isBaggageEmpty(baggage) && { baggage: serializeBaggage(baggage) }), + }; +} diff --git a/packages/core/test/lib/envelope.test.ts b/packages/core/test/lib/envelope.test.ts new file mode 100644 index 000000000000..a5c8cb8ae06a --- /dev/null +++ b/packages/core/test/lib/envelope.test.ts @@ -0,0 +1,54 @@ +import { DsnComponents, Event } from '@sentry/types'; + +import { createEventEnvelope } from '../../src/envelope'; + +const testDsn: DsnComponents = { protocol: 'https', projectId: 'abc', host: 'testry.io' }; + +describe('createEventEnvelope', () => { + describe('baggage header', () => { + it("doesn't add baggage header if event is not a transaction", () => { + const event: Event = {}; + const envelopeHeaders = createEventEnvelope(event, testDsn)[0]; + + expect(envelopeHeaders).toBeDefined(); + expect(envelopeHeaders.baggage).toBeUndefined(); + }); + + it("doesn't add baggage header if no baggage data is available", () => { + const event: Event = { + type: 'transaction', + }; + const envelopeHeaders = createEventEnvelope(event, testDsn)[0]; + + expect(envelopeHeaders).toBeDefined(); + expect(envelopeHeaders.baggage).toBeUndefined(); + }); + + const testTable: Array<[string, Event, string]> = [ + ['adds only baggage item', { type: 'transaction', release: '1.0.0' }, 'sentry-release=1.0.0'], + [ + 'adds two baggage items', + { type: 'transaction', release: '1.0.0', environment: 'prod' }, + 'sentry-environment=prod,sentry-release=1.0.0', + ], + [ + 'adds all baggageitems', + { + type: 'transaction', + release: '1.0.0', + environment: 'prod', + user: { id: 'bob', segment: 'segmentA' }, + transaction: 'TX', + }, + 'sentry-environment=prod,sentry-release=1.0.0,sentry-transaction=TX,sentry-userid=bob,sentry-usersegment=segmentA', + ], + ]; + it.each(testTable)('%s', (_: string, event, serializedBaggage) => { + const envelopeHeaders = createEventEnvelope(event, testDsn)[0]; + + expect(envelopeHeaders).toBeDefined(); + expect(envelopeHeaders.baggage).toBeDefined(); + expect(envelopeHeaders.baggage).toEqual(serializedBaggage); + }); + }); +}); diff --git a/packages/integration-tests/suites/tracing/baggage/init.js b/packages/integration-tests/suites/tracing/baggage/init.js new file mode 100644 index 000000000000..5cd0764d4da5 --- /dev/null +++ b/packages/integration-tests/suites/tracing/baggage/init.js @@ -0,0 +1,16 @@ +import * as Sentry from '@sentry/browser'; +import { Integrations } from '@sentry/tracing'; + +window.Sentry = Sentry; + +Sentry.init({ + dsn: 'https://public@dsn.ingest.sentry.io/1337', + integrations: [new Integrations.BrowserTracing({ tracingOrigins: [/.*/] })], + environment: 'production', + tracesSampleRate: 1, +}); + +Sentry.configureScope(scope => { + scope.setUser({ id: 'user123', segment: 'segmentB' }); + scope.setTransactionName('testTransactionBaggage'); +}); diff --git a/packages/integration-tests/suites/tracing/baggage/test.ts b/packages/integration-tests/suites/tracing/baggage/test.ts new file mode 100644 index 000000000000..5617e0d57309 --- /dev/null +++ b/packages/integration-tests/suites/tracing/baggage/test.ts @@ -0,0 +1,16 @@ +import { expect } from '@playwright/test'; +import { Event, EventEnvelopeHeaders } from '@sentry/types'; + +import { sentryTest } from '../../../utils/fixtures'; +import { envelopeHeaderRequestParser, getFirstSentryEnvelopeRequest } from '../../../utils/helpers'; + +sentryTest('should send baggage data in transaction envelope header', async ({ getLocalTestPath, page }) => { + const url = await getLocalTestPath({ testDir: __dirname }); + + const envHeader = await getFirstSentryEnvelopeRequest(page, url, envelopeHeaderRequestParser); + + expect(envHeader.baggage).toBeDefined(); + expect(envHeader.baggage).toEqual( + 'sentry-environment=production,sentry-transaction=testTransactionBaggage,sentry-userid=user123,sentry-usersegment=segmentB', + ); +}); diff --git a/packages/integration-tests/utils/helpers.ts b/packages/integration-tests/utils/helpers.ts index 8f6e06b97d5a..34b512ec9e01 100644 --- a/packages/integration-tests/utils/helpers.ts +++ b/packages/integration-tests/utils/helpers.ts @@ -1,5 +1,5 @@ import { Page, Request } from '@playwright/test'; -import { Event } from '@sentry/types'; +import { Event, EventEnvelopeHeaders } from '@sentry/types'; const envelopeUrlRegex = /\.sentry\.io\/api\/\d+\/envelope\//; @@ -11,6 +11,14 @@ const envelopeRequestParser = (request: Request | null): Event => { return envelope.split('\n').map(line => JSON.parse(line))[2]; }; +export const envelopeHeaderRequestParser = (request: Request | null): EventEnvelopeHeaders => { + // https://develop.sentry.dev/sdk/envelopes/ + const envelope = request?.postData() || ''; + + // First row of the envelop is the event payload. + return envelope.split('\n').map(line => JSON.parse(line))[0]; +}; + /** * Run script at the given path inside the test environment. * @@ -122,10 +130,11 @@ async function getMultipleSentryEnvelopeRequests( url?: string; timeout?: number; }, + requestParser: (req: Request) => T = envelopeRequestParser as (req: Request) => T, ): Promise { // TODO: This is not currently checking the type of envelope, just casting for now. // We can update this to include optional type-guarding when we have types for Envelope. - return getMultipleRequests(page, count, envelopeUrlRegex, envelopeRequestParser, options) as Promise; + return getMultipleRequests(page, count, envelopeUrlRegex, requestParser, options) as Promise; } /** @@ -136,8 +145,12 @@ async function getMultipleSentryEnvelopeRequests( * @param {string} [url] * @return {*} {Promise} */ -async function getFirstSentryEnvelopeRequest(page: Page, url?: string): Promise { - return (await getMultipleSentryEnvelopeRequests(page, 1, { url }))[0]; +async function getFirstSentryEnvelopeRequest( + page: Page, + url?: string, + requestParser: (req: Request) => T = envelopeRequestParser as (req: Request) => T, +): Promise { + return (await getMultipleSentryEnvelopeRequests(page, 1, { url }, requestParser))[0]; } /** diff --git a/packages/types/src/envelope.ts b/packages/types/src/envelope.ts index 33846f077a79..7e350b129b21 100644 --- a/packages/types/src/envelope.ts +++ b/packages/types/src/envelope.ts @@ -56,7 +56,7 @@ export type SessionItem = | BaseEnvelopeItem; export type ClientReportItem = BaseEnvelopeItem; -type EventEnvelopeHeaders = { event_id: string; sent_at: string }; +export type EventEnvelopeHeaders = { event_id: string; sent_at: string; baggage?: string }; type SessionEnvelopeHeaders = { sent_at: string }; type ClientReportEnvelopeHeaders = BaseEnvelopeHeaders; diff --git a/packages/types/src/index.ts b/packages/types/src/index.ts index 91470886bb2c..e774bf149a54 100644 --- a/packages/types/src/index.ts +++ b/packages/types/src/index.ts @@ -15,6 +15,7 @@ export type { EnvelopeItemType, EnvelopeItem, EventEnvelope, + EventEnvelopeHeaders, EventItem, SessionEnvelope, SessionItem, diff --git a/packages/utils/src/baggage.ts b/packages/utils/src/baggage.ts index db402608a22b..f8bec69439b3 100644 --- a/packages/utils/src/baggage.ts +++ b/packages/utils/src/baggage.ts @@ -1,7 +1,7 @@ import { IS_DEBUG_BUILD } from './flags'; import { logger } from './logger'; -export type AllowedBaggageKeys = 'environment' | 'release'; // TODO: Add remaining allowed baggage keys | 'transaction' | 'userid' | 'usersegment'; +export type AllowedBaggageKeys = 'environment' | 'release' | 'userid' | 'transaction' | 'usersegment'; export type BaggageObj = Partial & Record>; /** @@ -47,6 +47,11 @@ export function setBaggageValue(baggage: Baggage, key: keyof BaggageObj, value: baggage[0][key] = value; } +/** Check if the baggage object (i.e. the first element in the tuple) is empty */ +export function isBaggageEmpty(baggage: Baggage): boolean { + return Object.keys(baggage[0]).length === 0; +} + /** Serialize a baggage object */ export function serializeBaggage(baggage: Baggage): string { return Object.keys(baggage[0]).reduce((prev, key: keyof BaggageObj) => { diff --git a/packages/utils/src/index.ts b/packages/utils/src/index.ts index 01b875dc31c3..fd627db6f2e5 100644 --- a/packages/utils/src/index.ts +++ b/packages/utils/src/index.ts @@ -23,3 +23,4 @@ export * from './env'; export * from './envelope'; export * from './clientreport'; export * from './ratelimit'; +export * from './baggage'; diff --git a/packages/utils/test/baggage.test.ts b/packages/utils/test/baggage.test.ts index 08d4213f2a99..e1c91a87f5ee 100644 --- a/packages/utils/test/baggage.test.ts +++ b/packages/utils/test/baggage.test.ts @@ -1,4 +1,11 @@ -import { createBaggage, getBaggageValue, parseBaggageString, serializeBaggage, setBaggageValue } from '../src/baggage'; +import { + createBaggage, + getBaggageValue, + isBaggageEmpty, + parseBaggageString, + serializeBaggage, + setBaggageValue, +} from '../src/baggage'; describe('Baggage', () => { describe('createBaggage', () => { @@ -89,4 +96,13 @@ describe('Baggage', () => { expect(parseBaggageString(baggageString)).toEqual(baggage); }); }); + + describe('isBaggageEmpty', () => { + it.each([ + ['returns true if the modifyable part of baggage is empty', createBaggage({}), true], + ['returns false if the modifyable part of baggage is not empty', createBaggage({ release: '10.0.2' }), false], + ])('%s', (_: string, baggage, outcome) => { + expect(isBaggageEmpty(baggage)).toEqual(outcome); + }); + }); }); From 8d08a8b63a96c58b92e3530bb403db4cb1da8b9d Mon Sep 17 00:00:00 2001 From: Anton Pirker Date: Wed, 18 May 2022 15:10:31 +0200 Subject: [PATCH 178/204] Change DSN in Lambda Layer to point to relay running in Lambda Extension. (#5126) If the serverless SDK is running in an AWS Lambda Function it should not send data directly to sentry.io but to the relay that is running in the AWS Lambda Extension (which is running on localhost:3000) This PR parses the DSN and changes the host and port to point to localhost:3000. --- packages/serverless/src/awslambda.ts | 4 +++- packages/utils/src/dsn.ts | 28 ++++++++++++++++++++++++++++ 2 files changed, 31 insertions(+), 1 deletion(-) diff --git a/packages/serverless/src/awslambda.ts b/packages/serverless/src/awslambda.ts index 92802bf808cd..88af7f66c3b5 100644 --- a/packages/serverless/src/awslambda.ts +++ b/packages/serverless/src/awslambda.ts @@ -11,7 +11,7 @@ import { } from '@sentry/node'; import { extractTraceparentData } from '@sentry/tracing'; import { Integration } from '@sentry/types'; -import { isString, logger } from '@sentry/utils'; +import { extensionRelayDSN, isString, logger } from '@sentry/utils'; // NOTE: I have no idea how to fix this right now, and don't want to waste more time, as it builds just fine — Kamil // eslint-disable-next-line import/no-unresolved import { Context, Handler } from 'aws-lambda'; @@ -79,6 +79,8 @@ export function init(options: Sentry.NodeOptions = {}): void { version: Sentry.SDK_VERSION, }; + options.dsn = extensionRelayDSN(options.dsn) + Sentry.init(options); Sentry.addGlobalEventProcessor(serverlessEventProcessor); } diff --git a/packages/utils/src/dsn.ts b/packages/utils/src/dsn.ts index 56b864e2863b..b99c983aad71 100644 --- a/packages/utils/src/dsn.ts +++ b/packages/utils/src/dsn.ts @@ -27,6 +27,12 @@ export function dsnToString(dsn: DsnComponents, withPassword: boolean = false): ); } +/** + * Parses a Dsn from a given string. + * + * @param str A Dsn as string + * @returns Dsn as DsnComponents + */ function dsnFromString(str: string): DsnComponents { const match = DSN_REGEX.exec(str); @@ -101,3 +107,25 @@ export function makeDsn(from: DsnLike): DsnComponents { validateDsn(components); return components; } + + +/** + * Changes a Dsn to point to the `relay` server running in the Lambda Extension. + * + * This is only used by the serverless integration for AWS Lambda. + * + * @param originalDsn The original Dsn of the customer. + * @returns Dsn pointing to Lambda extension. + */ +export function extensionRelayDSN(originalDsn: string | undefined): string | undefined { + if (originalDsn === undefined) { + return undefined; + } + + const dsn = dsnFromString(originalDsn); + dsn.host = 'localhost'; + dsn.port = '3000'; + dsn.protocol = 'http'; + + return dsnToString(dsn); +} From 457b8061883a8e90d2576b4384ad28b85a6b45e6 Mon Sep 17 00:00:00 2001 From: Tim Fish Date: Wed, 18 May 2022 16:45:59 +0100 Subject: [PATCH 179/204] feat: Add attachments API (#5004) - Adds `Attachment` interface and extends `AttachmentItemHeaders` with required fields - Adds `addAttachment`, `getAttachments` and `clearAttachments` to `Scope` - Adds `attachments: Attachment[]` to `EventHint` - In the private capture/process/prepare/send methods in the `BaseClient`, the `EventHint` argument has moved as it's no longer optional - We can't mutate the hint if it's undefined! - Copies attachments from the scope to the hint in `_prepareEvent` - Adds optional `textEncoder` to `InternalBaseTransportOptions` and overrides this in the node client to support node 8-10 - Manually pass `new TextEncoder()` in many of the tests so they pass on node.js 8-10 - Adds binary serialisation support for envelopes - `serializeEnvelope` returns `string | Uint8Array` which all transports supported without modification - Defaults to concatenating strings when no attachments are found. String concatenation is about 10x faster than the binary serialisation - Rewrites `parseEnvelope` in `testutils.ts` so that it can parse binary envelopes --- packages/browser/src/client.ts | 8 +- packages/browser/src/transports/utils.ts | 2 +- .../test/unit/transports/fetch.test.ts | 6 +- .../browser/test/unit/transports/xhr.test.ts | 4 +- packages/core/src/baseclient.ts | 32 +++++-- packages/core/src/transports/base.ts | 2 +- .../core/test/lib/transports/base.test.ts | 11 ++- packages/core/test/mocks/client.ts | 13 ++- packages/core/test/mocks/transport.ts | 3 +- packages/gatsby/test/integration.test.tsx | 3 + packages/hub/src/exports.ts | 5 +- packages/hub/src/scope.ts | 29 +++++++ packages/node/src/client.ts | 11 ++- packages/node/src/transports/http-module.ts | 2 +- packages/node/test/index.test.ts | 6 +- .../manual/express-scope-separation/start.js | 3 +- .../aggregates-disable-single-session.js | 8 +- .../caught-exception-errored-session.js | 8 +- .../errors-in-session-capped-to-one.js | 8 +- .../single-session/healthy-session.js | 9 +- .../terminal-state-sessions-sent-once.js | 8 +- .../uncaught-exception-crashed-session.js | 8 +- .../unhandled-rejection-crashed-session.js | 8 +- .../node/test/manual/webpack-domain/index.js | 3 +- packages/node/test/transports/http.test.ts | 12 +-- packages/node/test/transports/https.test.ts | 12 +-- packages/types/src/attachment.ts | 6 ++ packages/types/src/client.ts | 2 +- packages/types/src/envelope.ts | 15 ++-- packages/types/src/event.ts | 2 + packages/types/src/index.ts | 1 + packages/types/src/scope.ts | 17 ++++ packages/types/src/transport.ts | 8 +- packages/utils/src/envelope.ts | 86 +++++++++++++++---- packages/utils/test/clientreport.test.ts | 24 ++++-- packages/utils/test/envelope.test.ts | 58 ++++++++++--- packages/utils/test/testutils.ts | 56 +++++++++++- 37 files changed, 393 insertions(+), 106 deletions(-) create mode 100644 packages/types/src/attachment.ts diff --git a/packages/browser/src/client.ts b/packages/browser/src/client.ts index 2eeefc18c7ed..9916ebd9c984 100644 --- a/packages/browser/src/client.ts +++ b/packages/browser/src/client.ts @@ -104,7 +104,7 @@ export class BrowserClient extends BaseClient { /** * @inheritDoc */ - public sendEvent(event: Event): void { + public sendEvent(event: Event, hint?: EventHint): void { // We only want to add the sentry event breadcrumb when the user has the breadcrumb integration installed and // activated its `sentry` option. // We also do not want to use the `Breadcrumbs` class here directly, because we do not want it to be included in @@ -133,15 +133,15 @@ export class BrowserClient extends BaseClient { ); } - super.sendEvent(event); + super.sendEvent(event, hint); } /** * @inheritDoc */ - protected _prepareEvent(event: Event, scope?: Scope, hint?: EventHint): PromiseLike { + protected _prepareEvent(event: Event, hint: EventHint, scope?: Scope): PromiseLike { event.platform = event.platform || 'javascript'; - return super._prepareEvent(event, scope, hint); + return super._prepareEvent(event, hint, scope); } /** diff --git a/packages/browser/src/transports/utils.ts b/packages/browser/src/transports/utils.ts index b386557499c5..bd17ec097c28 100644 --- a/packages/browser/src/transports/utils.ts +++ b/packages/browser/src/transports/utils.ts @@ -86,7 +86,7 @@ export function getNativeFetchImplementation(): FetchImpl { * @param url report endpoint * @param body report payload */ -export function sendReport(url: string, body: string): void { +export function sendReport(url: string, body: string | Uint8Array): void { const isRealNavigator = Object.prototype.toString.call(global && global.navigator) === '[object Navigator]'; const hasSendBeacon = isRealNavigator && typeof global.navigator.sendBeacon === 'function'; diff --git a/packages/browser/test/unit/transports/fetch.test.ts b/packages/browser/test/unit/transports/fetch.test.ts index 4684b87fdf8b..87ad77266106 100644 --- a/packages/browser/test/unit/transports/fetch.test.ts +++ b/packages/browser/test/unit/transports/fetch.test.ts @@ -1,5 +1,6 @@ import { EventEnvelope, EventItem } from '@sentry/types'; import { createEnvelope, serializeEnvelope } from '@sentry/utils'; +import { TextEncoder } from 'util'; import { makeFetchTransport } from '../../../src/transports/fetch'; import { BrowserTransportOptions } from '../../../src/transports/types'; @@ -8,6 +9,7 @@ import { FetchImpl } from '../../../src/transports/utils'; const DEFAULT_FETCH_TRANSPORT_OPTIONS: BrowserTransportOptions = { url: 'https://sentry.io/api/42/store/?sentry_key=123&sentry_version=7', recordDroppedEvent: () => undefined, + textEncoder: new TextEncoder(), }; const ERROR_ENVELOPE = createEnvelope({ event_id: 'aa3ff046696b4bc6b609ce6d28fde9e2', sent_at: '123' }, [ @@ -40,7 +42,7 @@ describe('NewFetchTransport', () => { expect(mockFetch).toHaveBeenCalledTimes(1); expect(mockFetch).toHaveBeenLastCalledWith(DEFAULT_FETCH_TRANSPORT_OPTIONS.url, { - body: serializeEnvelope(ERROR_ENVELOPE), + body: serializeEnvelope(ERROR_ENVELOPE, new TextEncoder()), method: 'POST', referrerPolicy: 'origin', }); @@ -90,7 +92,7 @@ describe('NewFetchTransport', () => { await transport.send(ERROR_ENVELOPE); expect(mockFetch).toHaveBeenLastCalledWith(DEFAULT_FETCH_TRANSPORT_OPTIONS.url, { - body: serializeEnvelope(ERROR_ENVELOPE), + body: serializeEnvelope(ERROR_ENVELOPE, new TextEncoder()), method: 'POST', ...REQUEST_OPTIONS, }); diff --git a/packages/browser/test/unit/transports/xhr.test.ts b/packages/browser/test/unit/transports/xhr.test.ts index 15a5c7ce3d62..117edce8d2ea 100644 --- a/packages/browser/test/unit/transports/xhr.test.ts +++ b/packages/browser/test/unit/transports/xhr.test.ts @@ -1,5 +1,6 @@ import { EventEnvelope, EventItem } from '@sentry/types'; import { createEnvelope, serializeEnvelope } from '@sentry/utils'; +import { TextEncoder } from 'util'; import { BrowserTransportOptions } from '../../../src/transports/types'; import { makeXHRTransport } from '../../../src/transports/xhr'; @@ -7,6 +8,7 @@ import { makeXHRTransport } from '../../../src/transports/xhr'; const DEFAULT_XHR_TRANSPORT_OPTIONS: BrowserTransportOptions = { url: 'https://sentry.io/api/42/store/?sentry_key=123&sentry_version=7', recordDroppedEvent: () => undefined, + textEncoder: new TextEncoder(), }; const ERROR_ENVELOPE = createEnvelope({ event_id: 'aa3ff046696b4bc6b609ce6d28fde9e2', sent_at: '123' }, [ @@ -64,7 +66,7 @@ describe('NewXHRTransport', () => { expect(xhrMock.open).toHaveBeenCalledTimes(1); expect(xhrMock.open).toHaveBeenCalledWith('POST', DEFAULT_XHR_TRANSPORT_OPTIONS.url); expect(xhrMock.send).toHaveBeenCalledTimes(1); - expect(xhrMock.send).toHaveBeenCalledWith(serializeEnvelope(ERROR_ENVELOPE)); + expect(xhrMock.send).toHaveBeenCalledWith(serializeEnvelope(ERROR_ENVELOPE, new TextEncoder())); }); it('sets rate limit response headers', async () => { diff --git a/packages/core/src/baseclient.ts b/packages/core/src/baseclient.ts index 563c8ce599c3..260eb412561b 100644 --- a/packages/core/src/baseclient.ts +++ b/packages/core/src/baseclient.ts @@ -19,7 +19,9 @@ import { Transport, } from '@sentry/types'; import { + addItemToEnvelope, checkOrSetAlreadyCaught, + createAttachmentEnvelopeItem, dateTimestampInSeconds, isPlainObject, isPrimitive, @@ -283,9 +285,14 @@ export abstract class BaseClient implements Client { /** * @inheritDoc */ - public sendEvent(event: Event): void { + public sendEvent(event: Event, hint: EventHint = {}): void { if (this._dsn) { const env = createEventEnvelope(event, this._dsn, this._options._metadata, this._options.tunnel); + + for (const attachment of hint.attachments || []) { + addItemToEnvelope(env, createAttachmentEnvelopeItem(attachment, this._options.transportOptions?.textEncoder)); + } + this._sendEnvelope(env); } } @@ -401,11 +408,11 @@ export abstract class BaseClient implements Client { * @param scope A scope containing event metadata. * @returns A new event with more information. */ - protected _prepareEvent(event: Event, scope?: Scope, hint?: EventHint): PromiseLike { + protected _prepareEvent(event: Event, hint: EventHint, scope?: Scope): PromiseLike { const { normalizeDepth = 3, normalizeMaxBreadth = 1_000 } = this.getOptions(); const prepared: Event = { ...event, - event_id: event.event_id || (hint && hint.event_id ? hint.event_id : uuid4()), + event_id: event.event_id || hint.event_id || uuid4(), timestamp: event.timestamp || dateTimestampInSeconds(), }; @@ -415,7 +422,7 @@ export abstract class BaseClient implements Client { // If we have scope given to us, use it as the base for further modifications. // This allows us to prevent unnecessary copying of data if `captureContext` is not provided. let finalScope = scope; - if (hint && hint.captureContext) { + if (hint.captureContext) { finalScope = Scope.clone(finalScope).update(hint.captureContext); } @@ -425,6 +432,13 @@ export abstract class BaseClient implements Client { // This should be the last thing called, since we want that // {@link Hub.addEventProcessor} gets the finished prepared event. if (finalScope) { + // Collect attachments from the hint and scope + const attachments = [...(hint.attachments || []), ...finalScope.getAttachments()]; + + if (attachments.length) { + hint.attachments = attachments; + } + // In case we have a hub we reassign it. result = finalScope.applyToEvent(prepared, hint); } @@ -552,7 +566,7 @@ export abstract class BaseClient implements Client { * @param hint * @param scope */ - protected _captureEvent(event: Event, hint?: EventHint, scope?: Scope): PromiseLike { + protected _captureEvent(event: Event, hint: EventHint = {}, scope?: Scope): PromiseLike { return this._processEvent(event, hint, scope).then( finalEvent => { return finalEvent.event_id; @@ -577,7 +591,7 @@ export abstract class BaseClient implements Client { * @param scope A scope containing event metadata. * @returns A SyncPromise that resolves with the event or rejects in case event was/will not be send. */ - protected _processEvent(event: Event, hint?: EventHint, scope?: Scope): PromiseLike { + protected _processEvent(event: Event, hint: EventHint, scope?: Scope): PromiseLike { const { beforeSend, sampleRate } = this.getOptions(); if (!this._isEnabled()) { @@ -597,14 +611,14 @@ export abstract class BaseClient implements Client { ); } - return this._prepareEvent(event, scope, hint) + return this._prepareEvent(event, hint, scope) .then(prepared => { if (prepared === null) { this.recordDroppedEvent('event_processor', event.type || 'error'); throw new SentryError('An event processor returned null, will not send event.'); } - const isInternalException = hint && hint.data && (hint.data as { __sentry__: boolean }).__sentry__ === true; + const isInternalException = hint.data && (hint.data as { __sentry__: boolean }).__sentry__ === true; if (isInternalException || isTransaction || !beforeSend) { return prepared; } @@ -623,7 +637,7 @@ export abstract class BaseClient implements Client { this._updateSessionFromEvent(session, processedEvent); } - this.sendEvent(processedEvent); + this.sendEvent(processedEvent, hint); return processedEvent; }) .then(null, reason => { diff --git a/packages/core/src/transports/base.ts b/packages/core/src/transports/base.ts index 8b435ed77541..fc83fb6b2855 100644 --- a/packages/core/src/transports/base.ts +++ b/packages/core/src/transports/base.ts @@ -69,7 +69,7 @@ export function createTransport( }; const requestTask = (): PromiseLike => - makeRequest({ body: serializeEnvelope(filteredEnvelope) }).then( + makeRequest({ body: serializeEnvelope(filteredEnvelope, options.textEncoder) }).then( response => { // We don't want to throw on NOK responses, but we want to at least log them if (response.statusCode !== undefined && (response.statusCode < 200 || response.statusCode >= 300)) { diff --git a/packages/core/test/lib/transports/base.test.ts b/packages/core/test/lib/transports/base.test.ts index b57cc36f6e6f..94d83751a21d 100644 --- a/packages/core/test/lib/transports/base.test.ts +++ b/packages/core/test/lib/transports/base.test.ts @@ -1,5 +1,6 @@ import { EventEnvelope, EventItem, TransportMakeRequestResponse } from '@sentry/types'; import { createEnvelope, PromiseBuffer, resolvedSyncPromise, serializeEnvelope } from '@sentry/utils'; +import { TextEncoder } from 'util'; import { createTransport } from '../../../src/transports/base'; @@ -14,6 +15,7 @@ const TRANSACTION_ENVELOPE = createEnvelope( const transportOptions = { recordDroppedEvent: () => undefined, // noop + textEncoder: new TextEncoder(), }; describe('createTransport', () => { @@ -36,7 +38,7 @@ describe('createTransport', () => { it('constructs a request to send to Sentry', async () => { expect.assertions(1); const transport = createTransport(transportOptions, req => { - expect(req.body).toEqual(serializeEnvelope(ERROR_ENVELOPE)); + expect(req.body).toEqual(serializeEnvelope(ERROR_ENVELOPE, new TextEncoder())); return resolvedSyncPromise({}); }); await transport.send(ERROR_ENVELOPE); @@ -46,7 +48,7 @@ describe('createTransport', () => { expect.assertions(2); const transport = createTransport(transportOptions, req => { - expect(req.body).toEqual(serializeEnvelope(ERROR_ENVELOPE)); + expect(req.body).toEqual(serializeEnvelope(ERROR_ENVELOPE, new TextEncoder())); throw new Error(); }); @@ -82,7 +84,10 @@ describe('createTransport', () => { const mockRecordDroppedEventCallback = jest.fn(); - const transport = createTransport({ recordDroppedEvent: mockRecordDroppedEventCallback }, mockRequestExecutor); + const transport = createTransport( + { recordDroppedEvent: mockRecordDroppedEventCallback, textEncoder: new TextEncoder() }, + mockRequestExecutor, + ); return [transport, setTransportResponse, mockRequestExecutor, mockRecordDroppedEventCallback] as const; } diff --git a/packages/core/test/mocks/client.ts b/packages/core/test/mocks/client.ts index 9caef7b0e0cf..62dfa10219da 100644 --- a/packages/core/test/mocks/client.ts +++ b/packages/core/test/mocks/client.ts @@ -1,5 +1,6 @@ -import { ClientOptions, Event, Integration, Outcome, Session, Severity, SeverityLevel } from '@sentry/types'; +import { ClientOptions, Event, EventHint, Integration, Outcome, Session, Severity, SeverityLevel } from '@sentry/types'; import { resolvedSyncPromise } from '@sentry/utils'; +import { TextEncoder } from 'util'; import { BaseClient } from '../../src/baseclient'; import { initAndBind } from '../../src/sdk'; @@ -9,9 +10,13 @@ export function getDefaultTestClientOptions(options: Partial return { integrations: [], sendClientReports: true, + transportOptions: { textEncoder: new TextEncoder() }, transport: () => createTransport( - { recordDroppedEvent: () => undefined }, // noop + { + recordDroppedEvent: () => undefined, + textEncoder: new TextEncoder(), + }, // noop _ => resolvedSyncPromise({}), ), stackParser: () => [], @@ -62,10 +67,10 @@ export class TestClient extends BaseClient { return resolvedSyncPromise({ message, level }); } - public sendEvent(event: Event): void { + public sendEvent(event: Event, hint?: EventHint): void { this.event = event; if (this._options.enableSend) { - super.sendEvent(event); + super.sendEvent(event, hint); return; } // eslint-disable-next-line @typescript-eslint/no-unused-expressions diff --git a/packages/core/test/mocks/transport.ts b/packages/core/test/mocks/transport.ts index f59e72a516a1..90d35e2a0247 100644 --- a/packages/core/test/mocks/transport.ts +++ b/packages/core/test/mocks/transport.ts @@ -1,4 +1,5 @@ import { SyncPromise } from '@sentry/utils'; +import { TextEncoder } from 'util'; import { createTransport } from '../../src/transports/base'; @@ -10,7 +11,7 @@ export function makeFakeTransport(delay: number = 2000) { let sendCalled = 0; let sentCount = 0; const makeTransport = () => - createTransport({ recordDroppedEvent: () => undefined }, () => { + createTransport({ recordDroppedEvent: () => undefined, textEncoder: new TextEncoder() }, () => { sendCalled += 1; return new SyncPromise(async res => { await sleep(delay); diff --git a/packages/gatsby/test/integration.test.tsx b/packages/gatsby/test/integration.test.tsx index 18c738ba2ac2..3fd06dc21990 100644 --- a/packages/gatsby/test/integration.test.tsx +++ b/packages/gatsby/test/integration.test.tsx @@ -3,6 +3,7 @@ import { render } from '@testing-library/react'; import { useEffect } from 'react'; // eslint-disable-next-line @typescript-eslint/no-unused-vars import * as React from 'react'; +import { TextDecoder,TextEncoder } from 'util'; import { onClientEntry } from '../gatsby-browser'; import * as Sentry from '../src'; @@ -10,6 +11,8 @@ import * as Sentry from '../src'; beforeAll(() => { (global as any).__SENTRY_RELEASE__ = '683f3a6ab819d47d23abfca9a914c81f0524d35b'; (global as any).__SENTRY_DSN__ = 'https://examplePublicKey@o0.ingest.sentry.io/0'; + (global as any).TextEncoder = TextEncoder; + (global as any).TextDecoder = TextDecoder; }); describe('useEffect', () => { diff --git a/packages/hub/src/exports.ts b/packages/hub/src/exports.ts index 2ae3000df1c1..528da6289d3e 100644 --- a/packages/hub/src/exports.ts +++ b/packages/hub/src/exports.ts @@ -3,6 +3,7 @@ import { CaptureContext, CustomSamplingContext, Event, + EventHint, Extra, Extras, Primitive, @@ -59,8 +60,8 @@ export function captureMessage( * @param event The event to send to Sentry. * @returns The generated eventId. */ -export function captureEvent(event: Event): ReturnType { - return getCurrentHub().captureEvent(event); +export function captureEvent(event: Event, hint?: EventHint): ReturnType { + return getCurrentHub().captureEvent(event, hint); } /** diff --git a/packages/hub/src/scope.ts b/packages/hub/src/scope.ts index b51c620d86b0..f1ffd51cc9e5 100644 --- a/packages/hub/src/scope.ts +++ b/packages/hub/src/scope.ts @@ -1,5 +1,6 @@ /* eslint-disable max-lines */ import { + Attachment, Breadcrumb, CaptureContext, Context, @@ -86,6 +87,9 @@ export class Scope implements ScopeInterface { /** Request Mode Session Status */ protected _requestSession?: RequestSession; + /** Attachments */ + protected _attachments: Attachment[] = []; + /** * A place to stash data which is needed at some point in the SDK's event processing pipeline but which shouldn't get * sent to Sentry @@ -111,6 +115,7 @@ export class Scope implements ScopeInterface { newScope._fingerprint = scope._fingerprint; newScope._eventProcessors = [...scope._eventProcessors]; newScope._requestSession = scope._requestSession; + newScope._attachments = [...scope._attachments]; } return newScope; } @@ -366,6 +371,7 @@ export class Scope implements ScopeInterface { this._span = undefined; this._session = undefined; this._notifyScopeListeners(); + this._attachments = []; return this; } @@ -399,6 +405,29 @@ export class Scope implements ScopeInterface { return this; } + /** + * @inheritDoc + */ + public addAttachment(attachment: Attachment): this { + this._attachments.push(attachment); + return this; + } + + /** + * @inheritDoc + */ + public getAttachments(): Attachment[] { + return this._attachments; + } + + /** + * @inheritDoc + */ + public clearAttachments(): this { + this._attachments = []; + return this; + } + /** * Applies the current context and fingerprint to the event. * Note that breadcrumbs will be added by the client. diff --git a/packages/node/src/client.ts b/packages/node/src/client.ts index e6a0fc7a43a0..9ebad827d3e4 100644 --- a/packages/node/src/client.ts +++ b/packages/node/src/client.ts @@ -2,6 +2,7 @@ import { BaseClient, Scope, SDK_VERSION } from '@sentry/core'; import { SessionFlusher } from '@sentry/hub'; import { Event, EventHint, Severity, SeverityLevel } from '@sentry/types'; import { logger, resolvedSyncPromise } from '@sentry/utils'; +import { TextEncoder } from 'util'; import { eventFromMessage, eventFromUnknownInput } from './eventbuilder'; import { IS_DEBUG_BUILD } from './flags'; @@ -33,6 +34,12 @@ export class NodeClient extends BaseClient { version: SDK_VERSION, }; + // Until node supports global TextEncoder in all versions we support, we are forced to pass it from util + options.transportOptions = { + textEncoder: new TextEncoder(), + ...options.transportOptions, + }; + super(options); } @@ -131,12 +138,12 @@ export class NodeClient extends BaseClient { /** * @inheritDoc */ - protected _prepareEvent(event: Event, scope?: Scope, hint?: EventHint): PromiseLike { + protected _prepareEvent(event: Event, hint: EventHint, scope?: Scope): PromiseLike { event.platform = event.platform || 'node'; if (this.getOptions().serverName) { event.server_name = this.getOptions().serverName; } - return super._prepareEvent(event, scope, hint); + return super._prepareEvent(event, hint, scope); } /** diff --git a/packages/node/src/transports/http-module.ts b/packages/node/src/transports/http-module.ts index 0189d4971e4b..3d21faf2fc34 100644 --- a/packages/node/src/transports/http-module.ts +++ b/packages/node/src/transports/http-module.ts @@ -20,7 +20,7 @@ export interface HTTPModuleRequestIncomingMessage { * Some transports work in a special Javascript environment where http.IncomingMessage is not available. */ export interface HTTPModuleClientRequest { - end(chunk: string): void; + end(chunk: string | Uint8Array): void; on(event: 'error', listener: () => void): void; } diff --git a/packages/node/test/index.test.ts b/packages/node/test/index.test.ts index 8731922267af..b12bd23fdd17 100644 --- a/packages/node/test/index.test.ts +++ b/packages/node/test/index.test.ts @@ -1,6 +1,6 @@ import { initAndBind, SDK_VERSION } from '@sentry/core'; import { getMainCarrier } from '@sentry/hub'; -import { Integration } from '@sentry/types'; +import { EventHint, Integration } from '@sentry/types'; import * as domain from 'domain'; import { @@ -76,7 +76,7 @@ describe('SentryNode', () => { }); describe('breadcrumbs', () => { - let s: jest.SpyInstance; + let s: jest.SpyInstance; beforeEach(() => { s = jest.spyOn(NodeClient.prototype, 'sendEvent').mockImplementation(async () => Promise.resolve({ code: 200 })); @@ -107,7 +107,7 @@ describe('SentryNode', () => { }); describe('capture', () => { - let s: jest.SpyInstance; + let s: jest.SpyInstance; beforeEach(() => { s = jest.spyOn(NodeClient.prototype, 'sendEvent').mockImplementation(async () => Promise.resolve({ code: 200 })); diff --git a/packages/node/test/manual/express-scope-separation/start.js b/packages/node/test/manual/express-scope-separation/start.js index b85d68c7b1f8..81ca392a4407 100644 --- a/packages/node/test/manual/express-scope-separation/start.js +++ b/packages/node/test/manual/express-scope-separation/start.js @@ -3,6 +3,7 @@ const express = require('express'); const app = express(); const Sentry = require('../../../build/cjs'); const { colorize } = require('../colorize'); +const { TextEncoder } = require('util'); // 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.error = () => null; @@ -17,7 +18,7 @@ function assertTags(actual, expected) { let remaining = 3; function makeDummyTransport() { - return Sentry.createTransport({ recordDroppedEvent: () => undefined }, req => { + return Sentry.createTransport({ recordDroppedEvent: () => undefined, textEncoder: new TextEncoder() }, req => { --remaining; if (!remaining) { diff --git a/packages/node/test/manual/release-health/session-aggregates/aggregates-disable-single-session.js b/packages/node/test/manual/release-health/session-aggregates/aggregates-disable-single-session.js index ae6a660607b1..300870d21fac 100644 --- a/packages/node/test/manual/release-health/session-aggregates/aggregates-disable-single-session.js +++ b/packages/node/test/manual/release-health/session-aggregates/aggregates-disable-single-session.js @@ -3,6 +3,7 @@ const express = require('express'); const app = express(); const Sentry = require('../../../../build/cjs'); const { assertSessions } = require('../test-utils'); +const { TextEncoder } = require('util'); function cleanUpAndExitSuccessfully() { server.close(); @@ -28,8 +29,11 @@ function assertSessionAggregates(session, expected) { } function makeDummyTransport() { - return Sentry.createTransport({ recordDroppedEvent: () => undefined }, req => { - const sessionEnv = req.body.split('\n').map(e => JSON.parse(e)); + return Sentry.createTransport({ recordDroppedEvent: () => undefined, textEncoder: new TextEncoder() }, req => { + const sessionEnv = req.body + .split('\n') + .filter(l => !!l) + .map(e => JSON.parse(e)); assertSessionAggregates(sessionEnv[2], { attrs: { release: '1.1' }, aggregates: [{ crashed: 2, errored: 1, exited: 1 }], diff --git a/packages/node/test/manual/release-health/single-session/caught-exception-errored-session.js b/packages/node/test/manual/release-health/single-session/caught-exception-errored-session.js index b7a9538a3fa2..0cecc8ee75e4 100644 --- a/packages/node/test/manual/release-health/single-session/caught-exception-errored-session.js +++ b/packages/node/test/manual/release-health/single-session/caught-exception-errored-session.js @@ -1,5 +1,6 @@ const Sentry = require('../../../../build/cjs'); const { assertSessions, constructStrippedSessionObject, validateSessionCountFunction } = require('../test-utils'); +const { TextEncoder } = require('util'); const sessionCounts = { sessionCounter: 0, @@ -9,8 +10,11 @@ const sessionCounts = { validateSessionCountFunction(sessionCounts); function makeDummyTransport() { - return Sentry.createTransport({ recordDroppedEvent: () => undefined }, req => { - const payload = req.body.split('\n').map(e => JSON.parse(e)); + return Sentry.createTransport({ recordDroppedEvent: () => undefined, textEncoder: new TextEncoder() }, req => { + const payload = req.body + .split('\n') + .filter(l => !!l) + .map(e => JSON.parse(e)); const isSessionPayload = payload[1].type === 'session'; if (isSessionPayload) { diff --git a/packages/node/test/manual/release-health/single-session/errors-in-session-capped-to-one.js b/packages/node/test/manual/release-health/single-session/errors-in-session-capped-to-one.js index dae307182ed1..18ec6fefdc78 100644 --- a/packages/node/test/manual/release-health/single-session/errors-in-session-capped-to-one.js +++ b/packages/node/test/manual/release-health/single-session/errors-in-session-capped-to-one.js @@ -1,5 +1,6 @@ const Sentry = require('../../../../build/cjs'); const { assertSessions, constructStrippedSessionObject, validateSessionCountFunction } = require('../test-utils'); +const { TextEncoder } = require('util'); const sessionCounts = { sessionCounter: 0, @@ -9,8 +10,11 @@ const sessionCounts = { validateSessionCountFunction(sessionCounts); function makeDummyTransport() { - return Sentry.createTransport({ recordDroppedEvent: () => undefined }, req => { - const payload = req.body.split('\n').map(e => JSON.parse(e)); + return Sentry.createTransport({ recordDroppedEvent: () => undefined, textEncoder: new TextEncoder() }, req => { + const payload = req.body + .split('\n') + .filter(l => !!l) + .map(e => JSON.parse(e)); const isSessionPayload = payload[1].type === 'session'; if (isSessionPayload) { diff --git a/packages/node/test/manual/release-health/single-session/healthy-session.js b/packages/node/test/manual/release-health/single-session/healthy-session.js index 0533b8a28728..11c3092dfcad 100644 --- a/packages/node/test/manual/release-health/single-session/healthy-session.js +++ b/packages/node/test/manual/release-health/single-session/healthy-session.js @@ -1,5 +1,6 @@ const Sentry = require('../../../../build/cjs'); const { assertSessions, constructStrippedSessionObject, validateSessionCountFunction } = require('../test-utils'); +const { TextEncoder } = require('util'); const sessionCounts = { sessionCounter: 0, @@ -9,9 +10,13 @@ const sessionCounts = { validateSessionCountFunction(sessionCounts); function makeDummyTransport() { - return Sentry.createTransport({ recordDroppedEvent: () => undefined }, req => { + return Sentry.createTransport({ recordDroppedEvent: () => undefined, textEncoder: new TextEncoder() }, req => { sessionCounts.sessionCounter++; - const sessionEnv = req.body.split('\n').map(e => JSON.parse(e)); + + const sessionEnv = req.body + .split('\n') + .filter(l => !!l) + .map(e => JSON.parse(e)); assertSessions(constructStrippedSessionObject(sessionEnv[2]), { init: true, diff --git a/packages/node/test/manual/release-health/single-session/terminal-state-sessions-sent-once.js b/packages/node/test/manual/release-health/single-session/terminal-state-sessions-sent-once.js index aa0796782c2f..571af19d0f94 100644 --- a/packages/node/test/manual/release-health/single-session/terminal-state-sessions-sent-once.js +++ b/packages/node/test/manual/release-health/single-session/terminal-state-sessions-sent-once.js @@ -1,5 +1,6 @@ const Sentry = require('../../../../build/cjs'); const { assertSessions, constructStrippedSessionObject, validateSessionCountFunction } = require('../test-utils'); +const { TextEncoder } = require('util'); const sessionCounts = { sessionCounter: 0, @@ -9,8 +10,11 @@ const sessionCounts = { validateSessionCountFunction(sessionCounts); function makeDummyTransport() { - return Sentry.createTransport({ recordDroppedEvent: () => undefined }, req => { - const payload = req.body.split('\n').map(e => JSON.parse(e)); + return Sentry.createTransport({ recordDroppedEvent: () => undefined, textEncoder: new TextEncoder() }, req => { + const payload = req.body + .split('\n') + .filter(l => !!l) + .map(e => JSON.parse(e)); const isSessionPayload = payload[1].type === 'session'; if (isSessionPayload) { diff --git a/packages/node/test/manual/release-health/single-session/uncaught-exception-crashed-session.js b/packages/node/test/manual/release-health/single-session/uncaught-exception-crashed-session.js index 6fa3ac0a6821..1759c1cc2d0f 100644 --- a/packages/node/test/manual/release-health/single-session/uncaught-exception-crashed-session.js +++ b/packages/node/test/manual/release-health/single-session/uncaught-exception-crashed-session.js @@ -1,11 +1,15 @@ const Sentry = require('../../../../build/cjs'); const { assertSessions, constructStrippedSessionObject } = require('../test-utils'); +const { TextEncoder } = require('util'); function makeDummyTransport() { - return Sentry.createTransport({ recordDroppedEvent: () => undefined }, req => { + return Sentry.createTransport({ recordDroppedEvent: () => undefined, textEncoder: new TextEncoder() }, req => { if (req.category === 'session') { sessionCounts.sessionCounter++; - const sessionEnv = req.body.split('\n').map(e => JSON.parse(e)); + const sessionEnv = req.body + .split('\n') + .filter(l => !!l) + .map(e => JSON.parse(e)); assertSessions(constructStrippedSessionObject(sessionEnv[2]), { init: true, diff --git a/packages/node/test/manual/release-health/single-session/unhandled-rejection-crashed-session.js b/packages/node/test/manual/release-health/single-session/unhandled-rejection-crashed-session.js index b550260b62b7..cc8cf921c5d0 100644 --- a/packages/node/test/manual/release-health/single-session/unhandled-rejection-crashed-session.js +++ b/packages/node/test/manual/release-health/single-session/unhandled-rejection-crashed-session.js @@ -1,5 +1,6 @@ const Sentry = require('../../../../build/cjs'); const { assertSessions, constructStrippedSessionObject, validateSessionCountFunction } = require('../test-utils'); +const { TextEncoder } = require('util'); const sessionCounts = { sessionCounter: 0, @@ -9,8 +10,11 @@ const sessionCounts = { validateSessionCountFunction(sessionCounts); function makeDummyTransport() { - return Sentry.createTransport({ recordDroppedEvent: () => undefined }, req => { - const payload = req.body.split('\n').map(e => JSON.parse(e)); + return Sentry.createTransport({ recordDroppedEvent: () => undefined, textEncoder: new TextEncoder() }, req => { + const payload = req.body + .split('\n') + .filter(l => !!l) + .map(e => JSON.parse(e)); const isSessionPayload = payload[1].type === 'session'; if (isSessionPayload) { diff --git a/packages/node/test/manual/webpack-domain/index.js b/packages/node/test/manual/webpack-domain/index.js index 5d3968106bab..001ba1fa8c78 100644 --- a/packages/node/test/manual/webpack-domain/index.js +++ b/packages/node/test/manual/webpack-domain/index.js @@ -1,10 +1,11 @@ const Sentry = require('../../../build/cjs'); const { colorize } = require('../colorize'); +const { TextEncoder } = require('util'); let remaining = 2; function makeDummyTransport() { - return Sentry.createTransport({ recordDroppedEvent: () => undefined }, req => { + return Sentry.createTransport({ recordDroppedEvent: () => undefined, textEncoder: new TextEncoder() }, req => { --remaining; if (!remaining) { diff --git a/packages/node/test/transports/http.test.ts b/packages/node/test/transports/http.test.ts index d33efc42ff34..789baaf871ab 100644 --- a/packages/node/test/transports/http.test.ts +++ b/packages/node/test/transports/http.test.ts @@ -2,6 +2,7 @@ import { createTransport } from '@sentry/core'; import { EventEnvelope, EventItem } from '@sentry/types'; import { createEnvelope, serializeEnvelope } from '@sentry/utils'; import * as http from 'http'; +import { TextEncoder } from 'util'; import { makeNodeTransport } from '../../src/transports'; @@ -66,11 +67,12 @@ const EVENT_ENVELOPE = createEnvelope({ event_id: 'aa3ff046696b4b [{ type: 'event' }, { event_id: 'aa3ff046696b4bc6b609ce6d28fde9e2' }] as EventItem, ]); -const SERIALIZED_EVENT_ENVELOPE = serializeEnvelope(EVENT_ENVELOPE); +const SERIALIZED_EVENT_ENVELOPE = serializeEnvelope(EVENT_ENVELOPE, new TextEncoder()); const defaultOptions = { url: TEST_SERVER_URL, recordDroppedEvent: () => undefined, + textEncoder: new TextEncoder(), }; describe('makeNewHttpTransport()', () => { @@ -244,7 +246,7 @@ describe('makeNewHttpTransport()', () => { const registeredRequestExecutor = (createTransport as jest.Mock).mock.calls[0][1]; const executorResult = registeredRequestExecutor({ - body: serializeEnvelope(EVENT_ENVELOPE), + body: serializeEnvelope(EVENT_ENVELOPE, new TextEncoder()), category: 'error', }); @@ -264,7 +266,7 @@ describe('makeNewHttpTransport()', () => { const registeredRequestExecutor = (createTransport as jest.Mock).mock.calls[0][1]; const executorResult = registeredRequestExecutor({ - body: serializeEnvelope(EVENT_ENVELOPE), + body: serializeEnvelope(EVENT_ENVELOPE, new TextEncoder()), category: 'error', }); @@ -292,7 +294,7 @@ describe('makeNewHttpTransport()', () => { const registeredRequestExecutor = (createTransport as jest.Mock).mock.calls[0][1]; const executorResult = registeredRequestExecutor({ - body: serializeEnvelope(EVENT_ENVELOPE), + body: serializeEnvelope(EVENT_ENVELOPE, new TextEncoder()), category: 'error', }); @@ -320,7 +322,7 @@ describe('makeNewHttpTransport()', () => { const registeredRequestExecutor = (createTransport as jest.Mock).mock.calls[0][1]; const executorResult = registeredRequestExecutor({ - body: serializeEnvelope(EVENT_ENVELOPE), + body: serializeEnvelope(EVENT_ENVELOPE, new TextEncoder()), category: 'error', }); diff --git a/packages/node/test/transports/https.test.ts b/packages/node/test/transports/https.test.ts index 4313f326e933..cf7051b54fe4 100644 --- a/packages/node/test/transports/https.test.ts +++ b/packages/node/test/transports/https.test.ts @@ -3,6 +3,7 @@ import { EventEnvelope, EventItem } from '@sentry/types'; import { createEnvelope, serializeEnvelope } from '@sentry/utils'; import * as http from 'http'; import * as https from 'https'; +import { TextEncoder } from 'util'; import { makeNodeTransport } from '../../src/transports'; import { HTTPModule, HTTPModuleRequestIncomingMessage } from '../../src/transports/http-module'; @@ -69,7 +70,7 @@ const EVENT_ENVELOPE = createEnvelope({ event_id: 'aa3ff046696b4b [{ type: 'event' }, { event_id: 'aa3ff046696b4bc6b609ce6d28fde9e2' }] as EventItem, ]); -const SERIALIZED_EVENT_ENVELOPE = serializeEnvelope(EVENT_ENVELOPE); +const SERIALIZED_EVENT_ENVELOPE = serializeEnvelope(EVENT_ENVELOPE, new TextEncoder()); const unsafeHttpsModule: HTTPModule = { request: jest @@ -83,6 +84,7 @@ const defaultOptions = { httpModule: unsafeHttpsModule, url: TEST_SERVER_URL, recordDroppedEvent: () => undefined, // noop + textEncoder: new TextEncoder(), }; describe('makeNewHttpsTransport()', () => { @@ -297,7 +299,7 @@ describe('makeNewHttpsTransport()', () => { const registeredRequestExecutor = (createTransport as jest.Mock).mock.calls[0][1]; const executorResult = registeredRequestExecutor({ - body: serializeEnvelope(EVENT_ENVELOPE), + body: serializeEnvelope(EVENT_ENVELOPE, new TextEncoder()), category: 'error', }); @@ -317,7 +319,7 @@ describe('makeNewHttpsTransport()', () => { const registeredRequestExecutor = (createTransport as jest.Mock).mock.calls[0][1]; const executorResult = registeredRequestExecutor({ - body: serializeEnvelope(EVENT_ENVELOPE), + body: serializeEnvelope(EVENT_ENVELOPE, new TextEncoder()), category: 'error', }); @@ -345,7 +347,7 @@ describe('makeNewHttpsTransport()', () => { const registeredRequestExecutor = (createTransport as jest.Mock).mock.calls[0][1]; const executorResult = registeredRequestExecutor({ - body: serializeEnvelope(EVENT_ENVELOPE), + body: serializeEnvelope(EVENT_ENVELOPE, new TextEncoder()), category: 'error', }); @@ -373,7 +375,7 @@ describe('makeNewHttpsTransport()', () => { const registeredRequestExecutor = (createTransport as jest.Mock).mock.calls[0][1]; const executorResult = registeredRequestExecutor({ - body: serializeEnvelope(EVENT_ENVELOPE), + body: serializeEnvelope(EVENT_ENVELOPE, new TextEncoder()), category: 'error', }); diff --git a/packages/types/src/attachment.ts b/packages/types/src/attachment.ts new file mode 100644 index 000000000000..55cc795732ea --- /dev/null +++ b/packages/types/src/attachment.ts @@ -0,0 +1,6 @@ +export interface Attachment { + data: string | Uint8Array; + filename: string; + contentType?: string; + attachmentType?: string; +} diff --git a/packages/types/src/client.ts b/packages/types/src/client.ts index 3425006e645d..b09557ccba75 100644 --- a/packages/types/src/client.ts +++ b/packages/types/src/client.ts @@ -116,7 +116,7 @@ export interface Client { ): PromiseLike; /** Submits the event to Sentry */ - sendEvent(event: Event): void; + sendEvent(event: Event, hint?: EventHint): void; /** Submits the session to Sentry */ sendSession(session: Session | SessionAggregates): void; diff --git a/packages/types/src/envelope.ts b/packages/types/src/envelope.ts index 7e350b129b21..bd05b1eb0a7f 100644 --- a/packages/types/src/envelope.ts +++ b/packages/types/src/envelope.ts @@ -39,17 +39,20 @@ type EventItemHeaders = { type: 'event' | 'transaction'; sample_rates?: [{ id?: TransactionSamplingMethod; rate?: number }]; }; -type AttachmentItemHeaders = { type: 'attachment'; filename: string }; +type AttachmentItemHeaders = { + type: 'attachment'; + length: number; + filename: string; + content_type?: string; + attachment_type?: string; +}; type UserFeedbackItemHeaders = { type: 'user_report' }; type SessionItemHeaders = { type: 'session' }; type SessionAggregatesItemHeaders = { type: 'sessions' }; type ClientReportItemHeaders = { type: 'client_report' }; -// TODO(v7): Remove the string union from `Event | string` -// We have to allow this hack for now as we pre-serialize events because we support -// both store and envelope endpoints. -export type EventItem = BaseEnvelopeItem; -export type AttachmentItem = BaseEnvelopeItem; +export type EventItem = BaseEnvelopeItem; +export type AttachmentItem = BaseEnvelopeItem; export type UserFeedbackItem = BaseEnvelopeItem; export type SessionItem = | BaseEnvelopeItem diff --git a/packages/types/src/event.ts b/packages/types/src/event.ts index 7cb3047ed4d5..c769feee65ff 100644 --- a/packages/types/src/event.ts +++ b/packages/types/src/event.ts @@ -1,3 +1,4 @@ +import { Attachment } from './attachment'; import { Breadcrumb } from './breadcrumb'; import { Contexts } from './context'; import { DebugMeta } from './debugMeta'; @@ -56,5 +57,6 @@ export interface EventHint { captureContext?: CaptureContext; syntheticException?: Error | null; originalException?: Error | string | null; + attachments?: Attachment[]; data?: any; } diff --git a/packages/types/src/index.ts b/packages/types/src/index.ts index e774bf149a54..17eb63bb7842 100644 --- a/packages/types/src/index.ts +++ b/packages/types/src/index.ts @@ -1,3 +1,4 @@ +export type { Attachment } from './attachment'; export type { Breadcrumb, BreadcrumbHint } from './breadcrumb'; export type { Client } from './client'; export type { ClientReport, Outcome, EventDropReason } from './clientreport'; diff --git a/packages/types/src/scope.ts b/packages/types/src/scope.ts index c3ee56a2a763..a2a14ffd4664 100644 --- a/packages/types/src/scope.ts +++ b/packages/types/src/scope.ts @@ -1,3 +1,4 @@ +import { Attachment } from './attachment'; import { Breadcrumb } from './breadcrumb'; import { Context, Contexts } from './context'; import { EventProcessor } from './eventprocessor'; @@ -158,4 +159,20 @@ export interface Scope { * Clears all currently set Breadcrumbs. */ clearBreadcrumbs(): this; + + /** + * Adds an attachment to the scope + * @param attachment Attachment options + */ + addAttachment(attachment: Attachment): this; + + /** + * Returns an array of attachments on the scope + */ + getAttachments(): Attachment[]; + + /** + * Clears attachments from the scope + */ + clearAttachments(): this; } diff --git a/packages/types/src/transport.ts b/packages/types/src/transport.ts index 3c900819a785..6d358dc7ddc9 100644 --- a/packages/types/src/transport.ts +++ b/packages/types/src/transport.ts @@ -3,7 +3,7 @@ import { DataCategory } from './datacategory'; import { Envelope } from './envelope'; export type TransportRequest = { - body: string; + body: string | Uint8Array; }; export type TransportMakeRequestResponse = { @@ -15,9 +15,15 @@ export type TransportMakeRequestResponse = { }; }; +// Combination of global TextEncoder and Node require('util').TextEncoder +interface TextEncoderInternal extends TextEncoderCommon { + encode(input?: string): Uint8Array; +} + export interface InternalBaseTransportOptions { bufferSize?: number; recordDroppedEvent: (reason: EventDropReason, dataCategory: DataCategory) => void; + textEncoder?: TextEncoderInternal; } export interface BaseTransportOptions extends InternalBaseTransportOptions { diff --git a/packages/utils/src/envelope.ts b/packages/utils/src/envelope.ts index a58c48029066..1bc69915345d 100644 --- a/packages/utils/src/envelope.ts +++ b/packages/utils/src/envelope.ts @@ -1,6 +1,6 @@ -import { DataCategory, Envelope, EnvelopeItem, EnvelopeItemType } from '@sentry/types'; +import { Attachment, AttachmentItem, DataCategory, Envelope, EnvelopeItem, EnvelopeItemType } from '@sentry/types'; -import { isPrimitive } from './is'; +import { dropUndefinedKeys } from './object'; /** * Creates an envelope. @@ -36,24 +36,74 @@ export function forEachEnvelopeItem( }); } +// Combination of global TextEncoder and Node require('util').TextEncoder +interface TextEncoderInternal extends TextEncoderCommon { + encode(input?: string): Uint8Array; +} + +function encodeUTF8(input: string, textEncoder?: TextEncoderInternal): Uint8Array { + const utf8 = textEncoder || new TextEncoder(); + return utf8.encode(input); +} + /** - * Serializes an envelope into a string. + * Serializes an envelope. */ -export function serializeEnvelope(envelope: Envelope): string { - const [headers, items] = envelope; - const serializedHeaders = JSON.stringify(headers); - - // Have to cast items to any here since Envelope is a union type - // Fixed in Typescript 4.2 - // TODO: Remove any[] cast when we upgrade to TS 4.2 - // https://github.com/microsoft/TypeScript/issues/36390 - // eslint-disable-next-line @typescript-eslint/no-explicit-any - return (items as any[]).reduce((acc, item: typeof items[number]) => { - const [itemHeaders, payload] = item; - // We do not serialize payloads that are primitives - const serializedPayload = isPrimitive(payload) ? String(payload) : JSON.stringify(payload); - return `${acc}\n${JSON.stringify(itemHeaders)}\n${serializedPayload}`; - }, serializedHeaders); +export function serializeEnvelope(envelope: Envelope, textEncoder?: TextEncoderInternal): string | Uint8Array { + const [envHeaders, items] = envelope; + + // Initially we construct our envelope as a string and only convert to binary chunks if we encounter binary data + let parts: string | Uint8Array[] = JSON.stringify(envHeaders); + + function append(next: string | Uint8Array): void { + if (typeof parts === 'string') { + parts = typeof next === 'string' ? parts + next : [encodeUTF8(parts, textEncoder), next]; + } else { + parts.push(typeof next === 'string' ? encodeUTF8(next, textEncoder) : next); + } + } + + for (const item of items) { + const [itemHeaders, payload] = item as typeof items[number]; + append(`\n${JSON.stringify(itemHeaders)}\n`); + append(typeof payload === 'string' || payload instanceof Uint8Array ? payload : JSON.stringify(payload)); + } + + return typeof parts === 'string' ? parts : concatBuffers(parts); +} + +function concatBuffers(buffers: Uint8Array[]): Uint8Array { + const totalLength = buffers.reduce((acc, buf) => acc + buf.length, 0); + + const merged = new Uint8Array(totalLength); + let offset = 0; + for (const buffer of buffers) { + merged.set(buffer, offset); + offset += buffer.length; + } + + return merged; +} + +/** + * Creates attachment envelope items + */ +export function createAttachmentEnvelopeItem( + attachment: Attachment, + textEncoder?: TextEncoderInternal, +): AttachmentItem { + const buffer = typeof attachment.data === 'string' ? encodeUTF8(attachment.data, textEncoder) : attachment.data; + + return [ + dropUndefinedKeys({ + type: 'attachment', + length: buffer.length, + filename: attachment.filename, + content_type: attachment.contentType, + attachment_type: attachment.attachmentType, + }), + buffer, + ]; } const ITEM_TYPE_TO_DATA_CATEGORY_MAP: Record = { diff --git a/packages/utils/test/clientreport.test.ts b/packages/utils/test/clientreport.test.ts index f2abca98e798..442df49c6edf 100644 --- a/packages/utils/test/clientreport.test.ts +++ b/packages/utils/test/clientreport.test.ts @@ -1,7 +1,9 @@ import { ClientReport } from '@sentry/types'; +import { TextEncoder } from 'util'; import { createClientReportEnvelope } from '../src/clientreport'; import { serializeEnvelope } from '../src/envelope'; +import { parseEnvelope } from './testutils'; const DEFAULT_DISCARDED_EVENTS: ClientReport['discarded_events'] = [ { @@ -41,11 +43,21 @@ describe('createClientReportEnvelope', () => { it('serializes an envelope', () => { const env = createClientReportEnvelope(DEFAULT_DISCARDED_EVENTS, MOCK_DSN, 123456); - const serializedEnv = serializeEnvelope(env); - expect(serializedEnv).toMatchInlineSnapshot(` - "{\\"dsn\\":\\"https://public@example.com/1\\"} - {\\"type\\":\\"client_report\\"} - {\\"timestamp\\":123456,\\"discarded_events\\":[{\\"reason\\":\\"before_send\\",\\"category\\":\\"error\\",\\"quantity\\":30},{\\"reason\\":\\"network_error\\",\\"category\\":\\"transaction\\",\\"quantity\\":23}]}" - `); + + const [headers, items] = parseEnvelope(serializeEnvelope(env, new TextEncoder())); + + expect(headers).toEqual({ dsn: 'https://public@example.com/1' }); + expect(items).toEqual([ + [ + { type: 'client_report' }, + { + timestamp: 123456, + discarded_events: [ + { reason: 'before_send', category: 'error', quantity: 30 }, + { reason: 'network_error', category: 'transaction', quantity: 23 }, + ], + }, + ], + ]); }); }); diff --git a/packages/utils/test/envelope.test.ts b/packages/utils/test/envelope.test.ts index df07629a083e..af63b5bfb9bf 100644 --- a/packages/utils/test/envelope.test.ts +++ b/packages/utils/test/envelope.test.ts @@ -1,4 +1,5 @@ import { EventEnvelope } from '@sentry/types'; +import { TextEncoder } from 'util'; import { addItemToEnvelope, createEnvelope, forEachEnvelopeItem, serializeEnvelope } from '../src/envelope'; import { parseEnvelope } from './testutils'; @@ -20,28 +21,61 @@ describe('envelope', () => { describe('serializeEnvelope()', () => { it('serializes an envelope', () => { const env = createEnvelope({ event_id: 'aa3ff046696b4bc6b609ce6d28fde9e2', sent_at: '123' }, []); - expect(serializeEnvelope(env)).toMatchInlineSnapshot( - '"{\\"event_id\\":\\"aa3ff046696b4bc6b609ce6d28fde9e2\\",\\"sent_at\\":\\"123\\"}"', + const serializedEnvelope = serializeEnvelope(env, new TextEncoder()); + expect(typeof serializedEnvelope).toBe('string'); + + const [headers] = parseEnvelope(serializedEnvelope); + expect(headers).toEqual({ event_id: 'aa3ff046696b4bc6b609ce6d28fde9e2', sent_at: '123' }); + }); + + it('serializes an envelope with attachments', () => { + const items: EventEnvelope[1] = [ + [{ type: 'event' }, { event_id: 'aa3ff046696b4bc6b609ce6d28fde9e2' }], + [{ type: 'attachment', filename: 'bar.txt', length: 6 }, Uint8Array.from([1, 2, 3, 4, 5, 6])], + [{ type: 'attachment', filename: 'foo.txt', length: 6 }, Uint8Array.from([7, 8, 9, 10, 11, 12])], + ]; + + const env = createEnvelope( + { event_id: 'aa3ff046696b4bc6b609ce6d28fde9e2', sent_at: '123' }, + items, ); + + expect.assertions(6); + + const serializedEnvelope = serializeEnvelope(env, new TextEncoder()); + expect(serializedEnvelope).toBeInstanceOf(Uint8Array); + + const [parsedHeaders, parsedItems] = parseEnvelope(serializedEnvelope); + expect(parsedHeaders).toEqual({ event_id: 'aa3ff046696b4bc6b609ce6d28fde9e2', sent_at: '123' }); + expect(parsedItems).toHaveLength(3); + expect(items[0]).toEqual([{ type: 'event' }, { event_id: 'aa3ff046696b4bc6b609ce6d28fde9e2' }]); + expect(items[1]).toEqual([ + { type: 'attachment', filename: 'bar.txt', length: 6 }, + Uint8Array.from([1, 2, 3, 4, 5, 6]), + ]); + expect(items[2]).toEqual([ + { type: 'attachment', filename: 'foo.txt', length: 6 }, + Uint8Array.from([7, 8, 9, 10, 11, 12]), + ]); }); }); describe('addItemToEnvelope()', () => { it('adds an item to an envelope', () => { const env = createEnvelope({ event_id: 'aa3ff046696b4bc6b609ce6d28fde9e2', sent_at: '123' }, []); - const parsedEnvelope = parseEnvelope(serializeEnvelope(env)); - expect(parsedEnvelope).toHaveLength(1); - expect(parsedEnvelope[0]).toEqual({ event_id: 'aa3ff046696b4bc6b609ce6d28fde9e2', sent_at: '123' }); + let [envHeaders, items] = parseEnvelope(serializeEnvelope(env, new TextEncoder())); + expect(items).toHaveLength(0); + expect(envHeaders).toEqual({ event_id: 'aa3ff046696b4bc6b609ce6d28fde9e2', sent_at: '123' }); const newEnv = addItemToEnvelope(env, [ { type: 'event' }, { event_id: 'aa3ff046696b4bc6b609ce6d28fde9e2' }, ]); - const parsedNewEnvelope = parseEnvelope(serializeEnvelope(newEnv)); - expect(parsedNewEnvelope).toHaveLength(3); - expect(parsedNewEnvelope[0]).toEqual({ event_id: 'aa3ff046696b4bc6b609ce6d28fde9e2', sent_at: '123' }); - expect(parsedNewEnvelope[1]).toEqual({ type: 'event' }); - expect(parsedNewEnvelope[2]).toEqual({ event_id: 'aa3ff046696b4bc6b609ce6d28fde9e2' }); + + [envHeaders, items] = parseEnvelope(serializeEnvelope(newEnv, new TextEncoder())); + expect(envHeaders).toEqual({ event_id: 'aa3ff046696b4bc6b609ce6d28fde9e2', sent_at: '123' }); + expect(items).toHaveLength(1); + expect(items[0]).toEqual([{ type: 'event' }, { event_id: 'aa3ff046696b4bc6b609ce6d28fde9e2' }]); }); }); @@ -49,8 +83,8 @@ describe('envelope', () => { it('loops through an envelope', () => { const items: EventEnvelope[1] = [ [{ type: 'event' }, { event_id: 'aa3ff046696b4bc6b609ce6d28fde9e2' }], - [{ type: 'attachment', filename: 'bar.txt' }, '123456'], - [{ type: 'attachment', filename: 'foo.txt' }, '123456'], + [{ type: 'attachment', filename: 'bar.txt', length: 6 }, Uint8Array.from([1, 2, 3, 4, 5, 6])], + [{ type: 'attachment', filename: 'foo.txt', length: 6 }, Uint8Array.from([7, 8, 9, 10, 11, 12])], ]; const env = createEnvelope( diff --git a/packages/utils/test/testutils.ts b/packages/utils/test/testutils.ts index aa3c5485eec1..b708d77064eb 100644 --- a/packages/utils/test/testutils.ts +++ b/packages/utils/test/testutils.ts @@ -1,3 +1,6 @@ +import { BaseEnvelopeHeaders, BaseEnvelopeItemHeaders, Envelope } from '@sentry/types'; +import { TextDecoder, TextEncoder } from 'util'; + export const testOnlyIfNodeVersionAtLeast = (minVersion: number): jest.It => { const currentNodeVersion = process.env.NODE_VERSION; @@ -12,6 +15,55 @@ export const testOnlyIfNodeVersionAtLeast = (minVersion: number): jest.It => { return it; }; -export function parseEnvelope(env: string): Array> { - return env.split('\n').map(e => JSON.parse(e)); +/** + * A naive binary envelope parser + */ +export function parseEnvelope(env: string | Uint8Array): Envelope { + let buf = typeof env === 'string' ? new TextEncoder().encode(env) : env; + + let envelopeHeaders: BaseEnvelopeHeaders | undefined; + let lastItemHeader: BaseEnvelopeItemHeaders | undefined; + const items: [any, any][] = []; + + let binaryLength = 0; + while (buf.length) { + // Next length is either the binary length from the previous header + // or the next newline character + let i = binaryLength || buf.indexOf(0xa); + + // If no newline was found, assume this is the last block + if (i < 0) { + i = buf.length; + } + + // If we read out a length in the previous header, assume binary + if (binaryLength > 0) { + const bin = buf.slice(0, binaryLength); + binaryLength = 0; + items.push([lastItemHeader, bin]); + } else { + const json = JSON.parse(new TextDecoder().decode(buf.slice(0, i + 1))); + + if (typeof json.length === 'number') { + binaryLength = json.length; + } + + // First json is always the envelope headers + if (!envelopeHeaders) { + envelopeHeaders = json; + } else { + // If there is a type property, assume this is an item header + if ('type' in json) { + lastItemHeader = json; + } else { + items.push([lastItemHeader, json]); + } + } + } + + // Replace the buffer with the previous block and newline removed + buf = buf.slice(i + 1); + } + + return [envelopeHeaders as BaseEnvelopeHeaders, items]; } From 152e3c56ea9f10cad08153150faf28989b2f3795 Mon Sep 17 00:00:00 2001 From: Abhijeet Prasad Date: Thu, 19 May 2022 11:09:37 -0400 Subject: [PATCH 180/204] feat(ci): Add Node 18 to test matrix (#5049) https://nodejs.org/en/blog/announcements/v18-release-announce/ From reviewing the CHANGELOG (https://github.com/nodejs/node/blob/master/doc/changelogs/CHANGELOG_V18.md), there seems to be no changes that will affect us in the code. See v17 CHANGELOG: https://github.com/nodejs/node/blob/master/doc/changelogs/CHANGELOG_V17.md --- .github/workflows/build.yml | 6 +++--- packages/nextjs/test/run-integration-tests.sh | 13 +++++++++++++ .../node/test/manual/webpack-domain/npm-build.js | 7 ++++++- packages/serverless/src/awslambda.ts | 2 +- packages/utils/src/dsn.ts | 1 - packages/utils/src/normalize.ts | 3 +++ 6 files changed, 26 insertions(+), 6 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 13834dc75ce1..e0f62156e14d 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -281,7 +281,7 @@ jobs: runs-on: ubuntu-latest strategy: matrix: - node: [8, 10, 12, 14, 16] + node: [8, 10, 12, 14, 16, 18] steps: - name: Check out current commit (${{ env.HEAD_COMMIT }}) uses: actions/checkout@v2 @@ -318,7 +318,7 @@ jobs: runs-on: ubuntu-latest strategy: matrix: - node: [10, 12, 14, 16] + node: [10, 12, 14, 16, 18] steps: - name: Check out current commit (${{ env.HEAD_COMMIT }}) uses: actions/checkout@v2 @@ -518,7 +518,7 @@ jobs: continue-on-error: true strategy: matrix: - node: [10, 12, 14, 16] + node: [10, 12, 14, 16, 18] steps: - name: Check out current commit (${{ github.sha }}) uses: actions/checkout@v2 diff --git a/packages/nextjs/test/run-integration-tests.sh b/packages/nextjs/test/run-integration-tests.sh index 4f31ce75cd2b..715273afb42c 100755 --- a/packages/nextjs/test/run-integration-tests.sh +++ b/packages/nextjs/test/run-integration-tests.sh @@ -78,6 +78,19 @@ for NEXTJS_VERSION in 10 11 12; do WEBPACK_VERSION=5 || WEBPACK_VERSION=4 + # Node v18 only with Webpack 5 and above + # https://github.com/webpack/webpack/issues/14532#issuecomment-947513562 + # Context: https://github.com/vercel/next.js/issues/30078#issuecomment-947338268 + if [ "$NODE_MAJOR" -gt "17" ] && [ "$WEBPACK_VERSION" -eq "4" ]; then + echo "[nextjs$NEXTJS_VERSION | webpack@$WEBPACK_VERSION] Node $NODE_MAJOR not compatible with Webpack $WEBPACK_VERSION" + exit 0 + fi + if [ "$NODE_MAJOR" -gt "17" ] && [ "$NEXTJS_VERSION" -eq "10" ]; then + echo "[nextjs$NEXTJS_VERSION | webpack@$WEBPACK_VERSION] Node $NODE_MAJOR not compatible with Webpack $WEBPACK_VERSION" + exit 0 + fi + + # next 10 defaults to webpack 4 and next 11 defaults to webpack 5, but each can use either based on settings if [ "$NEXTJS_VERSION" -eq "10" ]; then sed "s/%RUN_WEBPACK_5%/$RUN_WEBPACK_5/g" next.config.js diff --git a/packages/node/test/manual/webpack-domain/npm-build.js b/packages/node/test/manual/webpack-domain/npm-build.js index cf01a15d6ef1..03db46e84955 100644 --- a/packages/node/test/manual/webpack-domain/npm-build.js +++ b/packages/node/test/manual/webpack-domain/npm-build.js @@ -2,6 +2,11 @@ const path = require('path'); const webpack = require('webpack'); const { execSync } = require('child_process'); +// Webpack test does not work in Node 18 and above. +if (Number(process.versions.node.split('.')[0]) >= 18) { + return; +} + // prettier-ignore webpack( { @@ -13,7 +18,7 @@ webpack( target: 'node', mode: 'development', }, - function(err, stats) { + function (err, stats) { if (err) { console.error(err.stack || err); if (err.details) { diff --git a/packages/serverless/src/awslambda.ts b/packages/serverless/src/awslambda.ts index 88af7f66c3b5..5e00e3419153 100644 --- a/packages/serverless/src/awslambda.ts +++ b/packages/serverless/src/awslambda.ts @@ -79,7 +79,7 @@ export function init(options: Sentry.NodeOptions = {}): void { version: Sentry.SDK_VERSION, }; - options.dsn = extensionRelayDSN(options.dsn) + options.dsn = extensionRelayDSN(options.dsn); Sentry.init(options); Sentry.addGlobalEventProcessor(serverlessEventProcessor); diff --git a/packages/utils/src/dsn.ts b/packages/utils/src/dsn.ts index b99c983aad71..2c43c2934fb4 100644 --- a/packages/utils/src/dsn.ts +++ b/packages/utils/src/dsn.ts @@ -108,7 +108,6 @@ export function makeDsn(from: DsnLike): DsnComponents { return components; } - /** * Changes a Dsn to point to the `relay` server running in the Lambda Extension. * diff --git a/packages/utils/src/normalize.ts b/packages/utils/src/normalize.ts index 4ef7b0b121cb..c88ab00b510f 100644 --- a/packages/utils/src/normalize.ts +++ b/packages/utils/src/normalize.ts @@ -31,6 +31,7 @@ type ObjOrArray = { [key: string]: T }; * object in the normallized output.. * @returns A normalized version of the object, or `"**non-serializable**"` if any errors are thrown during normalization. */ +// eslint-disable-next-line @typescript-eslint/no-explicit-any export function normalize(input: unknown, depth: number = +Infinity, maxProperties: number = +Infinity): any { try { // since we're at the outermost level, we don't provide a key @@ -42,6 +43,7 @@ export function normalize(input: unknown, depth: number = +Infinity, maxProperti /** JSDoc */ export function normalizeToSize( + // eslint-disable-next-line @typescript-eslint/no-explicit-any object: { [key: string]: any }, // Default Node.js REPL depth depth: number = 3, @@ -241,6 +243,7 @@ function utf8Length(value: string): number { } /** Calculates bytes size of input object */ +// eslint-disable-next-line @typescript-eslint/no-explicit-any function jsonSize(value: any): number { return utf8Length(JSON.stringify(value)); } From 263257fe97e40d09fbbca6a097bf4fc2bfe71bf6 Mon Sep 17 00:00:00 2001 From: Katie Byers Date: Fri, 20 May 2022 00:03:08 -0700 Subject: [PATCH 181/204] feat(build): Add rollup config for Node bundles (#5142) Currently, we have the ability to create rollup configs for two types of bundles, both intended for use in the browser: standalone SDKs (like browser or vue) and SDK add-ons (like integrations). This adds a third option, namely bundles for Node. Though it's clearly a less-common use case, there are Node situations (like serverless functions) where having a single, fully-treeshaken file could be helpful. (Indeed, the reason for this addition is our AWS lambda layer, and a desire for a simpler and more robust way to make sure that it includes all of the necessary dependencies without a lot of unnecessary extras.) Adding this option required a small amount of refactoring - exchanging a boolean indicator of bundle type for a string option, and switching to a(n admittedly slightly hacky) sorting mechanism for ordering plugins rather than manually inserting specific plugins at various spots in the array. (This was necessary because adding the node config meant shifting plugins around in such a way that it became impossible to merge plugin arrays by simple concatenation. As a bonus side effect, it allowed the insertion logic to be removed as well.) --- packages/browser/rollup.bundle.config.js | 2 +- packages/integrations/rollup.bundle.config.js | 2 +- packages/tracing/rollup.bundle.config.js | 2 +- packages/vue/rollup.bundle.config.js | 2 +- packages/wasm/rollup.bundle.config.js | 2 +- rollup/bundleHelpers.js | 67 +++++++++++-------- rollup/plugins/bundlePlugins.js | 21 ++++-- rollup/utils.js | 26 +++++-- 8 files changed, 82 insertions(+), 42 deletions(-) diff --git a/packages/browser/rollup.bundle.config.js b/packages/browser/rollup.bundle.config.js index 04ce4b438bc3..a17ef4f9d26d 100644 --- a/packages/browser/rollup.bundle.config.js +++ b/packages/browser/rollup.bundle.config.js @@ -4,8 +4,8 @@ const builds = []; ['es5', 'es6'].forEach(jsVersion => { const baseBundleConfig = makeBaseBundleConfig({ + bundleType: 'standalone', input: 'src/index.ts', - isAddOn: false, jsVersion, licenseTitle: '@sentry/browser', outputFileBase: `bundles/bundle${jsVersion === 'es5' ? '.es5' : ''}`, diff --git a/packages/integrations/rollup.bundle.config.js b/packages/integrations/rollup.bundle.config.js index 9549a07b8f94..322f5d992267 100644 --- a/packages/integrations/rollup.bundle.config.js +++ b/packages/integrations/rollup.bundle.config.js @@ -8,8 +8,8 @@ const file = process.env.INTEGRATION_FILE; const jsVersion = process.env.JS_VERSION; const baseBundleConfig = makeBaseBundleConfig({ + bundleType: 'addon', input: `src/${file}`, - isAddOn: true, jsVersion, licenseTitle: '@sentry/integrations', outputFileBase: `bundles/${file.replace('.ts', '')}${jsVersion === 'ES5' ? '.es5' : ''}`, diff --git a/packages/tracing/rollup.bundle.config.js b/packages/tracing/rollup.bundle.config.js index 091cb1f56958..0c3f4bdacf89 100644 --- a/packages/tracing/rollup.bundle.config.js +++ b/packages/tracing/rollup.bundle.config.js @@ -4,8 +4,8 @@ const builds = []; ['es5', 'es6'].forEach(jsVersion => { const baseBundleConfig = makeBaseBundleConfig({ + bundleType: 'standalone', input: 'src/index.bundle.ts', - isAddOn: false, jsVersion, licenseTitle: '@sentry/tracing & @sentry/browser', outputFileBase: `bundles/bundle.tracing${jsVersion === 'es5' ? '.es5' : ''}`, diff --git a/packages/vue/rollup.bundle.config.js b/packages/vue/rollup.bundle.config.js index 41bf0e7f659d..56fde34726cb 100644 --- a/packages/vue/rollup.bundle.config.js +++ b/packages/vue/rollup.bundle.config.js @@ -1,8 +1,8 @@ import { makeBaseBundleConfig, makeBundleConfigVariants } from '../../rollup/index.js'; const baseBundleConfig = makeBaseBundleConfig({ + bundleType: 'standalone', input: 'src/index.bundle.ts', - isAddOn: false, jsVersion: 'es6', licenseTitle: '@sentry/vue', outputFileBase: 'bundle.vue', diff --git a/packages/wasm/rollup.bundle.config.js b/packages/wasm/rollup.bundle.config.js index e928d466049d..4ddef06d4e25 100644 --- a/packages/wasm/rollup.bundle.config.js +++ b/packages/wasm/rollup.bundle.config.js @@ -1,8 +1,8 @@ import { makeBaseBundleConfig, makeBundleConfigVariants } from '../../rollup/index.js'; const baseBundleConfig = makeBaseBundleConfig({ + bundleType: 'addon', input: 'src/index.ts', - isAddOn: true, jsVersion: 'es6', licenseTitle: '@sentry/wasm', outputFileBase: 'bundles/wasm', diff --git a/rollup/bundleHelpers.js b/rollup/bundleHelpers.js index 7a3d08dce715..be67ffbbdfb1 100644 --- a/rollup/bundleHelpers.js +++ b/rollup/bundleHelpers.js @@ -2,12 +2,13 @@ * Rollup config docs: https://rollupjs.org/guide/en/#big-list-of-options */ -import assert from 'assert'; +import { builtinModules } from 'module'; import deepMerge from 'deepmerge'; import { makeBrowserBuildPlugin, + makeCommonJSPlugin, makeIsDebugBuildPlugin, makeLicensePlugin, makeNodeResolvePlugin, @@ -17,10 +18,10 @@ import { makeTerserPlugin, makeTSPlugin, } from './plugins/index.js'; -import { getLastElement, insertAt } from './utils.js'; +import { mergePlugins } from './utils'; export function makeBaseBundleConfig(options) { - const { input, isAddOn, jsVersion, licenseTitle, outputFileBase } = options; + const { bundleType, input, jsVersion, licenseTitle, outputFileBase } = options; const nodeResolvePlugin = makeNodeResolvePlugin(); const sucrasePlugin = makeSucrasePlugin(); @@ -30,6 +31,11 @@ export function makeBaseBundleConfig(options) { const licensePlugin = makeLicensePlugin(licenseTitle); const tsPlugin = makeTSPlugin(jsVersion.toLowerCase()); + // The `commonjs` plugin is the `esModuleInterop` of the bundling world. When used with `transformMixedEsModules`, it + // will include all dependencies, imported or required, in the final bundle. (Without it, CJS modules aren't included + // at all, and without `transformMixedEsModules`, they're only included if they're imported, not if they're required.) + const commonJSPlugin = makeCommonJSPlugin({ transformMixedEsModules: true }); + // used by `@sentry/browser`, `@sentry/tracing`, and `@sentry/vue` (bundles which are a full SDK in and of themselves) const standAloneBundleConfig = { output: { @@ -37,6 +43,7 @@ export function makeBaseBundleConfig(options) { name: 'Sentry', }, context: 'window', + plugins: [markAsBrowserBuildPlugin], }; // used by `@sentry/integrations` and `@sentry/wasm` (bundles which need to be combined with a stand-alone SDK bundle) @@ -69,6 +76,17 @@ export function makeBaseBundleConfig(options) { // code to add after the CJS wrapper footer: '}(window));', }, + plugins: [markAsBrowserBuildPlugin], + }; + + // used by `@sentry/serverless`, when creating the lambda layer + const nodeBundleConfig = { + output: { + format: 'cjs', + }, + plugins: [commonJSPlugin], + // Don't bundle any of Node's core modules + external: builtinModules, }; // used by all bundles @@ -83,19 +101,21 @@ export function makeBaseBundleConfig(options) { }, plugins: jsVersion === 'es5' - ? [tsPlugin, markAsBrowserBuildPlugin, nodeResolvePlugin, licensePlugin] - : [ - sucrasePlugin, - removeBlankLinesPlugin, - removeESLintCommentsPlugin, - markAsBrowserBuildPlugin, - nodeResolvePlugin, - licensePlugin, - ], + ? [tsPlugin, nodeResolvePlugin, licensePlugin] + : [sucrasePlugin, removeBlankLinesPlugin, removeESLintCommentsPlugin, nodeResolvePlugin, licensePlugin], treeshake: 'smallest', }; - return deepMerge(sharedBundleConfig, isAddOn ? addOnBundleConfig : standAloneBundleConfig); + const bundleTypeConfigMap = { + standalone: standAloneBundleConfig, + addon: addOnBundleConfig, + node: nodeBundleConfig, + }; + + return deepMerge(sharedBundleConfig, bundleTypeConfigMap[bundleType], { + // Plugins have to be in the correct order or everything breaks, so when merging we have to manually re-order them + customMerge: key => (key === 'plugins' ? mergePlugins : undefined), + }); } /** @@ -108,24 +128,17 @@ export function makeBaseBundleConfig(options) { * @returns An array of versions of that config */ export function makeBundleConfigVariants(baseConfig) { - const { plugins: baseConfigPlugins } = baseConfig; const includeDebuggingPlugin = makeIsDebugBuildPlugin(true); const stripDebuggingPlugin = makeIsDebugBuildPlugin(false); const terserPlugin = makeTerserPlugin(); - // The license plugin has to be last, so it ends up after terser. Otherwise, terser will remove the license banner. - assert( - getLastElement(baseConfigPlugins).name === 'rollup-plugin-license', - `Last plugin in given options should be \`rollup-plugin-license\`. Found ${getLastElement(baseConfigPlugins).name}`, - ); - // The additional options to use for each variant we're going to create const variantSpecificConfigs = [ { output: { file: `${baseConfig.output.file}.js`, }, - plugins: insertAt(baseConfigPlugins, -2, includeDebuggingPlugin), + plugins: [includeDebuggingPlugin], }, // This variant isn't particularly helpful for an SDK user, as it strips logging while making no other minification // changes, so by default we don't create it. It is however very useful when debugging rollup's treeshaking, so it's @@ -133,27 +146,27 @@ export function makeBundleConfigVariants(baseConfig) { // { // output: { file: `${baseConfig.output.file}.no-debug.js`, // }, - // plugins: insertAt(plugins, -2, stripDebuggingPlugin), + // plugins: [stripDebuggingPlugin], // }, { output: { file: `${baseConfig.output.file}.min.js`, }, - plugins: insertAt(baseConfigPlugins, -2, stripDebuggingPlugin, terserPlugin), + plugins: [stripDebuggingPlugin, terserPlugin], }, { output: { file: `${baseConfig.output.file}.debug.min.js`, }, - plugins: insertAt(baseConfigPlugins, -2, includeDebuggingPlugin, terserPlugin), + plugins: [terserPlugin], }, ]; return variantSpecificConfigs.map(variant => deepMerge(baseConfig, variant, { - // this makes it so that instead of concatenating the `plugin` properties of the two objects, the first value is - // just overwritten by the second value - arrayMerge: (first, second) => second, + // Merge the plugin arrays and make sure the end result is in the correct order. Everything else can use the + // default merge strategy. + customMerge: key => (key === 'plugins' ? mergePlugins : undefined), }), ); } diff --git a/rollup/plugins/bundlePlugins.js b/rollup/plugins/bundlePlugins.js index c9939ca0b4bf..59f3fec01cd1 100644 --- a/rollup/plugins/bundlePlugins.js +++ b/rollup/plugins/bundlePlugins.js @@ -1,4 +1,5 @@ /** + * CommonJS plugin docs: https://github.com/rollup/plugins/tree/master/packages/commonjs * License plugin docs: https://github.com/mjeanroy/rollup-plugin-license * Replace plugin docs: https://github.com/rollup/plugins/tree/master/packages/replace * Resolve plugin docs: https://github.com/rollup/plugins/tree/master/packages/node-resolve @@ -7,6 +8,7 @@ * Typescript plugin docs: https://github.com/ezolenko/rollup-plugin-typescript2 */ +import commonjs from '@rollup/plugin-commonjs'; import deepMerge from 'deepmerge'; import license from 'rollup-plugin-license'; import resolve from '@rollup/plugin-node-resolve'; @@ -23,12 +25,17 @@ import typescript from 'rollup-plugin-typescript2'; export function makeLicensePlugin(title) { const commitHash = require('child_process').execSync('git rev-parse --short HEAD', { encoding: 'utf-8' }).trim(); - return license({ + const plugin = license({ banner: { content: `/*! <%= data.title %> <%= pkg.version %> (${commitHash}) | https://github.com/getsentry/sentry-javascript */`, data: { title }, }, }); + + // give it a nicer name for later, when we'll need to sort the plugins + plugin.name = 'license'; + + return plugin; } /** @@ -125,7 +132,7 @@ export function makeTSPlugin(jsVersion) { // verbosity: 0, }; - return typescript( + const plugin = typescript( deepMerge(baseTSPluginOptions, { tsconfigOverride: { compilerOptions: { @@ -134,8 +141,14 @@ export function makeTSPlugin(jsVersion) { }, }), ); + + // give it a nicer name for later, when we'll need to sort the plugins + plugin.name = 'typescript'; + + return plugin; } -// We don't pass this plugin any options, so no need to wrap it in another factory function, as `resolve` is itself -// already a factory function. +// We don't pass these plugins any options which need to be calculated or changed by us, so no need to wrap them in +// another factory function, as they are themselves already factory functions. export { resolve as makeNodeResolvePlugin }; +export { commonjs as makeCommonJSPlugin }; diff --git a/rollup/utils.js b/rollup/utils.js index 00a97e991edf..6a7462788a47 100644 --- a/rollup/utils.js +++ b/rollup/utils.js @@ -1,11 +1,6 @@ /** - * Helper functions to compensate for the fact that JS can't handle negative array indices very well + * Helper function to compensate for the fact that JS can't handle negative array indices very well */ - -export const getLastElement = array => { - return array[array.length - 1]; -}; - export const insertAt = (arr, index, ...insertees) => { const newArr = [...arr]; // Add 1 to the array length so that the inserted element ends up in the right spot with respect to the length of the @@ -14,3 +9,22 @@ export const insertAt = (arr, index, ...insertees) => { newArr.splice(destinationIndex, 0, ...insertees); return newArr; }; + +/** + * Merge two arrays of plugins, making sure they're sorted in the correct order. + */ +export function mergePlugins(pluginsA, pluginsB) { + const plugins = [...pluginsA, ...pluginsB]; + plugins.sort((a, b) => { + // Hacky way to make sure the ones we care about end up where they belong in the order. (Really the TS and sucrase + // plugins are tied - both should come first - but they're mutually exclusive, so they can come in arbitrary order + // here.) + const order = ['typescript', 'sucrase', '...', 'terser', 'license']; + const sortKeyA = order.includes(a.name) ? a.name : '...'; + const sortKeyB = order.includes(b.name) ? b.name : '...'; + + return order.indexOf(sortKeyA) - order.indexOf(sortKeyB); + }); + + return plugins; +} From 7c71013557436c3b0932b931a0737e7a8bb3c545 Mon Sep 17 00:00:00 2001 From: Katie Byers Date: Fri, 20 May 2022 00:22:02 -0700 Subject: [PATCH 182/204] feat(build): Allow for multiple bundle entrypoints (#5143) This adds the ability to create more than one bundle with a single bundle rollup config, by specifying multiple entrypoints. Because multiple files can now be output, the way they're named also needed to be switched, from providing a static string to providing a function to determine the name of each bundle. Finally, the name of the option (`input`) was changed to match the corresponding option used in the building of npm packages (`entryponts`). --- packages/browser/rollup.bundle.config.js | 4 ++-- packages/integrations/rollup.bundle.config.js | 4 ++-- packages/tracing/rollup.bundle.config.js | 4 ++-- packages/vue/rollup.bundle.config.js | 4 ++-- packages/wasm/rollup.bundle.config.js | 4 ++-- rollup/bundleHelpers.js | 13 +++++++------ 6 files changed, 17 insertions(+), 16 deletions(-) diff --git a/packages/browser/rollup.bundle.config.js b/packages/browser/rollup.bundle.config.js index a17ef4f9d26d..23f9bb474c94 100644 --- a/packages/browser/rollup.bundle.config.js +++ b/packages/browser/rollup.bundle.config.js @@ -5,10 +5,10 @@ const builds = []; ['es5', 'es6'].forEach(jsVersion => { const baseBundleConfig = makeBaseBundleConfig({ bundleType: 'standalone', - input: 'src/index.ts', + entrypoints: ['src/index.ts'], jsVersion, licenseTitle: '@sentry/browser', - outputFileBase: `bundles/bundle${jsVersion === 'es5' ? '.es5' : ''}`, + outputFileBase: () => `bundles/bundle${jsVersion === 'es5' ? '.es5' : ''}`, }); builds.push(...makeBundleConfigVariants(baseBundleConfig)); diff --git a/packages/integrations/rollup.bundle.config.js b/packages/integrations/rollup.bundle.config.js index 322f5d992267..8f6e21de1937 100644 --- a/packages/integrations/rollup.bundle.config.js +++ b/packages/integrations/rollup.bundle.config.js @@ -9,10 +9,10 @@ const jsVersion = process.env.JS_VERSION; const baseBundleConfig = makeBaseBundleConfig({ bundleType: 'addon', - input: `src/${file}`, + entrypoints: [`src/${file}`], jsVersion, licenseTitle: '@sentry/integrations', - outputFileBase: `bundles/${file.replace('.ts', '')}${jsVersion === 'ES5' ? '.es5' : ''}`, + outputFileBase: ({ name: entrypoint }) => `bundles/${entrypoint}${jsVersion === 'ES5' ? '.es5' : ''}`, }); // TODO We only need `commonjs` for localforage (used in the offline plugin). Once that's fixed, this can come out. diff --git a/packages/tracing/rollup.bundle.config.js b/packages/tracing/rollup.bundle.config.js index 0c3f4bdacf89..0d5f4fcc8867 100644 --- a/packages/tracing/rollup.bundle.config.js +++ b/packages/tracing/rollup.bundle.config.js @@ -5,10 +5,10 @@ const builds = []; ['es5', 'es6'].forEach(jsVersion => { const baseBundleConfig = makeBaseBundleConfig({ bundleType: 'standalone', - input: 'src/index.bundle.ts', + entrypoints: ['src/index.bundle.ts'], jsVersion, licenseTitle: '@sentry/tracing & @sentry/browser', - outputFileBase: `bundles/bundle.tracing${jsVersion === 'es5' ? '.es5' : ''}`, + outputFileBase: () => `bundles/bundle.tracing${jsVersion === 'es5' ? '.es5' : ''}`, }); builds.push(...makeBundleConfigVariants(baseBundleConfig)); diff --git a/packages/vue/rollup.bundle.config.js b/packages/vue/rollup.bundle.config.js index 56fde34726cb..4df9b0d5b614 100644 --- a/packages/vue/rollup.bundle.config.js +++ b/packages/vue/rollup.bundle.config.js @@ -2,10 +2,10 @@ import { makeBaseBundleConfig, makeBundleConfigVariants } from '../../rollup/ind const baseBundleConfig = makeBaseBundleConfig({ bundleType: 'standalone', - input: 'src/index.bundle.ts', + entrypoints: ['src/index.bundle.ts'], jsVersion: 'es6', licenseTitle: '@sentry/vue', - outputFileBase: 'bundle.vue', + outputFileBase: () => 'bundle.vue', }); export default makeBundleConfigVariants(baseBundleConfig); diff --git a/packages/wasm/rollup.bundle.config.js b/packages/wasm/rollup.bundle.config.js index 4ddef06d4e25..2c97176f0dee 100644 --- a/packages/wasm/rollup.bundle.config.js +++ b/packages/wasm/rollup.bundle.config.js @@ -2,10 +2,10 @@ import { makeBaseBundleConfig, makeBundleConfigVariants } from '../../rollup/ind const baseBundleConfig = makeBaseBundleConfig({ bundleType: 'addon', - input: 'src/index.ts', + entrypoints: ['src/index.ts'], jsVersion: 'es6', licenseTitle: '@sentry/wasm', - outputFileBase: 'bundles/wasm', + outputFileBase: () => 'bundles/wasm', }); export default makeBundleConfigVariants(baseBundleConfig); diff --git a/rollup/bundleHelpers.js b/rollup/bundleHelpers.js index be67ffbbdfb1..b3acb7b36e1f 100644 --- a/rollup/bundleHelpers.js +++ b/rollup/bundleHelpers.js @@ -21,7 +21,7 @@ import { import { mergePlugins } from './utils'; export function makeBaseBundleConfig(options) { - const { bundleType, input, jsVersion, licenseTitle, outputFileBase } = options; + const { bundleType, entrypoints, jsVersion, licenseTitle, outputFileBase } = options; const nodeResolvePlugin = makeNodeResolvePlugin(); const sucrasePlugin = makeSucrasePlugin(); @@ -91,10 +91,11 @@ export function makeBaseBundleConfig(options) { // used by all bundles const sharedBundleConfig = { - input, + input: entrypoints, output: { // a file extension will be added to this base value when we specify either a minified or non-minified build - file: `build/${outputFileBase}`, + entryFileNames: outputFileBase, + dir: 'build', sourcemap: true, strict: false, esModule: false, @@ -136,7 +137,7 @@ export function makeBundleConfigVariants(baseConfig) { const variantSpecificConfigs = [ { output: { - file: `${baseConfig.output.file}.js`, + entryFileNames: chunkInfo => `${baseConfig.output.entryFileNames(chunkInfo)}.js`, }, plugins: [includeDebuggingPlugin], }, @@ -150,13 +151,13 @@ export function makeBundleConfigVariants(baseConfig) { // }, { output: { - file: `${baseConfig.output.file}.min.js`, + entryFileNames: chunkInfo => `${baseConfig.output.entryFileNames(chunkInfo)}.min.js`, }, plugins: [stripDebuggingPlugin, terserPlugin], }, { output: { - file: `${baseConfig.output.file}.debug.min.js`, + entryFileNames: chunkInfo => `${baseConfig.output.entryFileNames(chunkInfo)}.debug.min.js`, }, plugins: [terserPlugin], }, From 161d3026da9e42b9705a94bf1c6846c8faa0c804 Mon Sep 17 00:00:00 2001 From: Katie Byers Date: Fri, 20 May 2022 00:48:23 -0700 Subject: [PATCH 183/204] feat(build): Add ability to combine package-specific rollup config with standard options (#5144) Currently, the functions which we use to generate rollup configs accept a small handful of pre-defined options for customizing the output. For greater flexibility, this adds the option of passing arbitrary rollup options, which will get merged into what is currently being generated. --- packages/nextjs/rollup.npm.config.js | 2 +- rollup/bundleHelpers.js | 4 ++-- rollup/npmHelpers.js | 11 ++++++++--- 3 files changed, 11 insertions(+), 6 deletions(-) diff --git a/packages/nextjs/rollup.npm.config.js b/packages/nextjs/rollup.npm.config.js index a41fb991432c..b0ecb2b94b05 100644 --- a/packages/nextjs/rollup.npm.config.js +++ b/packages/nextjs/rollup.npm.config.js @@ -7,6 +7,6 @@ export default makeNPMConfigVariants( entrypoints: ['src/index.server.ts', 'src/index.client.ts', 'src/utils/instrumentServer.ts'], // prevent this nextjs code from ending up in our built package (this doesn't happen automatially because the name // doesn't match an SDK dependency) - externals: ['next/router'], + packageSpecificConfig: { external: ['next/router'] }, }), ); diff --git a/rollup/bundleHelpers.js b/rollup/bundleHelpers.js index b3acb7b36e1f..92fbd53b6513 100644 --- a/rollup/bundleHelpers.js +++ b/rollup/bundleHelpers.js @@ -21,7 +21,7 @@ import { import { mergePlugins } from './utils'; export function makeBaseBundleConfig(options) { - const { bundleType, entrypoints, jsVersion, licenseTitle, outputFileBase } = options; + const { bundleType, entrypoints, jsVersion, licenseTitle, outputFileBase, packageSpecificConfig } = options; const nodeResolvePlugin = makeNodeResolvePlugin(); const sucrasePlugin = makeSucrasePlugin(); @@ -113,7 +113,7 @@ export function makeBaseBundleConfig(options) { node: nodeBundleConfig, }; - return deepMerge(sharedBundleConfig, bundleTypeConfigMap[bundleType], { + return deepMerge.all([sharedBundleConfig, bundleTypeConfigMap[bundleType], packageSpecificConfig || {}], { // Plugins have to be in the correct order or everything breaks, so when merging we have to manually re-order them customMerge: key => (key === 'plugins' ? mergePlugins : undefined), }); diff --git a/rollup/npmHelpers.js b/rollup/npmHelpers.js index 40d9499f75a7..c0b983dac158 100644 --- a/rollup/npmHelpers.js +++ b/rollup/npmHelpers.js @@ -15,6 +15,7 @@ import { makeRemoveESLintCommentsPlugin, makeSucrasePlugin, } from './plugins/index.js'; +import { mergePlugins } from './utils'; const packageDotJSON = require(path.resolve(process.cwd(), './package.json')); @@ -22,8 +23,8 @@ export function makeBaseNPMConfig(options = {}) { const { entrypoints = ['src/index.ts'], esModuleInterop = false, - externals: packageSpecificExternals = [], hasBundles = false, + packageSpecificConfig = {}, } = options; const nodeResolvePlugin = makeNodeResolvePlugin(); @@ -33,7 +34,7 @@ export function makeBaseNPMConfig(options = {}) { const removeBlankLinesPlugin = makeRemoveBlankLinesPlugin(); const extractPolyfillsPlugin = makeExtractPolyfillsPlugin(); - return { + const defaultBaseConfig = { input: entrypoints, output: { @@ -91,7 +92,6 @@ export function makeBaseNPMConfig(options = {}) { ...Object.keys(packageDotJSON.dependencies || {}), ...Object.keys(packageDotJSON.devDependencies || {}), ...Object.keys(packageDotJSON.peerDependencies || {}), - ...packageSpecificExternals, ], // TODO `'smallest'` will get rid of `isDebugBuild()` by evaluating it and inlining the result and then treeshaking @@ -100,6 +100,11 @@ export function makeBaseNPMConfig(options = {}) { // treeshake: 'smallest', treeshake: false, }; + + return deepMerge(defaultBaseConfig, packageSpecificConfig, { + // Plugins have to be in the correct order or everything breaks, so when merging we have to manually re-order them + customMerge: key => (key === 'plugins' ? mergePlugins : undefined), + }); } export function makeNPMConfigVariants(baseConfig) { From 18af87d8a74b3800c1315267a487130caae753c3 Mon Sep 17 00:00:00 2001 From: Katie Byers Date: Fri, 20 May 2022 01:25:39 -0700 Subject: [PATCH 184/204] feat(build): Add option to build only specified bundle variants (#5145) There are situations in which we really only need one version of a bundle rather than all versions, but right now there's no way to build one without building all of them. This adds the option to specify which bundles you need, defaulting to the current behavior of building all three types (regular, minified, and minified with logging) if particular ones aren't requested. --- rollup/bundleHelpers.js | 39 ++++++++++++++++++++------------------- 1 file changed, 20 insertions(+), 19 deletions(-) diff --git a/rollup/bundleHelpers.js b/rollup/bundleHelpers.js index 92fbd53b6513..a406ae6905de 100644 --- a/rollup/bundleHelpers.js +++ b/rollup/bundleHelpers.js @@ -20,6 +20,8 @@ import { } from './plugins/index.js'; import { mergePlugins } from './utils'; +const BUNDLE_VARIANTS = ['.js', '.min.js', '.debug.min.js']; + export function makeBaseBundleConfig(options) { const { bundleType, entrypoints, jsVersion, licenseTitle, outputFileBase, packageSpecificConfig } = options; @@ -128,46 +130,45 @@ export function makeBaseBundleConfig(options) { * @param baseConfig The rollup config shared by the entire package * @returns An array of versions of that config */ -export function makeBundleConfigVariants(baseConfig) { +export function makeBundleConfigVariants(baseConfig, options = {}) { + const { variants = BUNDLE_VARIANTS } = options; + const includeDebuggingPlugin = makeIsDebugBuildPlugin(true); const stripDebuggingPlugin = makeIsDebugBuildPlugin(false); const terserPlugin = makeTerserPlugin(); - // The additional options to use for each variant we're going to create - const variantSpecificConfigs = [ - { + // The additional options to use for each variant we're going to create. + const variantSpecificConfigMap = { + '.js': { output: { entryFileNames: chunkInfo => `${baseConfig.output.entryFileNames(chunkInfo)}.js`, }, plugins: [includeDebuggingPlugin], }, - // This variant isn't particularly helpful for an SDK user, as it strips logging while making no other minification - // changes, so by default we don't create it. It is however very useful when debugging rollup's treeshaking, so it's - // left here for that purpose. - // { - // output: { file: `${baseConfig.output.file}.no-debug.js`, - // }, - // plugins: [stripDebuggingPlugin], - // }, - { + + '.min.js': { output: { entryFileNames: chunkInfo => `${baseConfig.output.entryFileNames(chunkInfo)}.min.js`, }, plugins: [stripDebuggingPlugin, terserPlugin], }, - { + + '.debug.min.js': { output: { entryFileNames: chunkInfo => `${baseConfig.output.entryFileNames(chunkInfo)}.debug.min.js`, }, plugins: [terserPlugin], }, - ]; + }; - return variantSpecificConfigs.map(variant => - deepMerge(baseConfig, variant, { + return variants.map(variant => { + if (!BUNDLE_VARIANTS.includes(variant)) { + throw new Error(`Unknown bundle variant requested: ${variant}`); + } + return deepMerge(baseConfig, variantSpecificConfigMap[variant], { // Merge the plugin arrays and make sure the end result is in the correct order. Everything else can use the // default merge strategy. customMerge: key => (key === 'plugins' ? mergePlugins : undefined), - }), - ); + }); + }); } From 9759e27f9260c6e7a02785fbc3ee03e4c88e69c7 Mon Sep 17 00:00:00 2001 From: Abhijeet Prasad Date: Mon, 23 May 2022 12:15:38 +0200 Subject: [PATCH 185/204] ref: Rename baggage env header to trace (#5128) This patch renames the tracing data envelope header from `baggage` to `trace`. Additionally, it now includes the `trace_id` and `public_key` fields as required by Relay Co-authored-by: Abhijeet Prasad Co-authored-by: Lukas Stracke --- .github/workflows/build.yml | 72 +++++++++---------- packages/core/src/envelope.ts | 44 ++++++------ packages/core/test/lib/envelope.test.ts | 67 +++++++++++++---- .../suites/tracing/baggage/test.ts | 19 +++-- packages/types/src/baggage.ts | 17 +++++ packages/types/src/envelope.ts | 18 ++++- packages/types/src/index.ts | 1 + packages/types/src/user.ts | 1 + packages/utils/src/baggage.ts | 25 ++----- 9 files changed, 165 insertions(+), 99 deletions(-) create mode 100644 packages/types/src/baggage.ts diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index e0f62156e14d..c55483590816 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -111,42 +111,42 @@ jobs: # `job_build` can't see `job_install_deps` and what it returned) dependency_cache_key: ${{ needs.job_install_deps.outputs.dependency_cache_key }} - job_build_aws_lambda_layer: - name: Build AWS Lambda Layer - needs: job_build - runs-on: ubuntu-latest - steps: - - name: Check out current commit (${{ env.HEAD_COMMIT }}) - uses: actions/checkout@v2 - with: - ref: ${{ env.HEAD_COMMIT }} - - name: Set up Node - uses: actions/setup-node@v1 - with: - node-version: ${{ env.DEFAULT_NODE_VERSION }} - - name: Check dependency cache - uses: actions/cache@v2 - with: - path: ${{ env.CACHED_DEPENDENCY_PATHS }} - key: ${{ needs.job_build.outputs.dependency_cache_key }} - - name: Check build cache - uses: actions/cache@v2 - with: - path: ${{ env.CACHED_BUILD_PATHS }} - key: ${{ env.BUILD_CACHE_KEY }} - - name: Get SDK version - run: | - export SDK_VERSION=$(cat dist-serverless/version) - echo "SDK_VERSION=$SDK_VERSION" | tee -a $GITHUB_ENV - - uses: actions/upload-artifact@v3 - with: - name: ${{ env.HEAD_COMMIT }} - path: | - dist-serverless/* - - uses: getsentry/action-build-aws-lambda-extension@v1 - with: - artifact_name: ${{ env.HEAD_COMMIT }} - zip_file_name: sentry-node-serverless-${{ env.SDK_VERSION }}.zip + # job_build_aws_lambda_layer: + # name: Build AWS Lambda Layer + # needs: job_build + # runs-on: ubuntu-latest + # steps: + # - name: Check out current commit (${{ env.HEAD_COMMIT }}) + # uses: actions/checkout@v2 + # with: + # ref: ${{ env.HEAD_COMMIT }} + # - name: Set up Node + # uses: actions/setup-node@v1 + # with: + # node-version: ${{ env.DEFAULT_NODE_VERSION }} + # - name: Check dependency cache + # uses: actions/cache@v2 + # with: + # path: ${{ env.CACHED_DEPENDENCY_PATHS }} + # key: ${{ needs.job_build.outputs.dependency_cache_key }} + # - name: Check build cache + # uses: actions/cache@v2 + # with: + # path: ${{ env.CACHED_BUILD_PATHS }} + # key: ${{ env.BUILD_CACHE_KEY }} + # - name: Get SDK version + # run: | + # export SDK_VERSION=$(cat dist-serverless/version) + # echo "SDK_VERSION=$SDK_VERSION" | tee -a $GITHUB_ENV + # - uses: actions/upload-artifact@v3 + # with: + # name: ${{ env.HEAD_COMMIT }} + # path: | + # dist-serverless/* + # - uses: getsentry/action-build-aws-lambda-extension@v1 + # with: + # artifact_name: ${{ env.HEAD_COMMIT }} + # zip_file_name: sentry-node-serverless-${{ env.SDK_VERSION }}.zip job_size_check: name: Size Check diff --git a/packages/core/src/envelope.ts b/packages/core/src/envelope.ts index 149d98d5242e..9ec9bd6db58f 100644 --- a/packages/core/src/envelope.ts +++ b/packages/core/src/envelope.ts @@ -11,15 +11,7 @@ import { SessionEnvelope, SessionItem, } from '@sentry/types'; -import { - BaggageObj, - createBaggage, - createEnvelope, - dropUndefinedKeys, - dsnToString, - isBaggageEmpty, - serializeBaggage, -} from '@sentry/utils'; +import { createEnvelope, dropUndefinedKeys, dsnToString } from '@sentry/utils'; /** Extract sdk info from from the API metadata */ function getSdkMetadataForEnvelopeHeader(metadata?: SdkMetadata): SdkInfo | undefined { @@ -128,24 +120,30 @@ function createEventEnvelopeHeaders( tunnel: string | undefined, dsn: DsnComponents, ): EventEnvelopeHeaders { - const baggage = - event.type === 'transaction' && - createBaggage( - dropUndefinedKeys({ - environment: event.environment, - release: event.release, - transaction: event.transaction, - userid: event.user && event.user.id, - // user.segment currently doesn't exist explicitly in interface User (just as a record key) - usersegment: event.user && event.user.segment, - } as BaggageObj), - ); - return { event_id: event.event_id as string, sent_at: new Date().toISOString(), ...(sdkInfo && { sdk: sdkInfo }), ...(!!tunnel && { dsn: dsnToString(dsn) }), - ...(baggage && !isBaggageEmpty(baggage) && { baggage: serializeBaggage(baggage) }), + ...(event.type === 'transaction' && + event.contexts && + event.contexts.trace && { + // TODO: Grab this from baggage + trace: dropUndefinedKeys({ + // Trace context must be defined for transactions + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion + trace_id: event.contexts!.trace.trace_id as string, + environment: event.environment, + release: event.release, + transaction: event.transaction, + user: + event.user && + dropUndefinedKeys({ + id: event.user.id, + segment: event.user.segment, + }), + public_key: dsn.publicKey, + }), + }), }; } diff --git a/packages/core/test/lib/envelope.test.ts b/packages/core/test/lib/envelope.test.ts index a5c8cb8ae06a..72060cb81364 100644 --- a/packages/core/test/lib/envelope.test.ts +++ b/packages/core/test/lib/envelope.test.ts @@ -1,35 +1,62 @@ import { DsnComponents, Event } from '@sentry/types'; +import { EventTraceContext } from '@sentry/types/build/types/envelope'; import { createEventEnvelope } from '../../src/envelope'; -const testDsn: DsnComponents = { protocol: 'https', projectId: 'abc', host: 'testry.io' }; +const testDsn: DsnComponents = { protocol: 'https', projectId: 'abc', host: 'testry.io', publicKey: 'pubKey123' }; describe('createEventEnvelope', () => { - describe('baggage header', () => { - it("doesn't add baggage header if event is not a transaction", () => { + describe('trace header', () => { + it("doesn't add trace header if event is not a transaction", () => { const event: Event = {}; const envelopeHeaders = createEventEnvelope(event, testDsn)[0]; expect(envelopeHeaders).toBeDefined(); - expect(envelopeHeaders.baggage).toBeUndefined(); + expect(envelopeHeaders.trace).toBeUndefined(); }); - it("doesn't add baggage header if no baggage data is available", () => { + it('adds minimal trace data if event is a transaction and no other baggage-related data is available', () => { const event: Event = { type: 'transaction', + contexts: { + trace: { + trace_id: '1234', + }, + }, }; const envelopeHeaders = createEventEnvelope(event, testDsn)[0]; expect(envelopeHeaders).toBeDefined(); - expect(envelopeHeaders.baggage).toBeUndefined(); + expect(envelopeHeaders.trace).toEqual({ trace_id: '1234', public_key: 'pubKey123' }); }); - const testTable: Array<[string, Event, string]> = [ - ['adds only baggage item', { type: 'transaction', release: '1.0.0' }, 'sentry-release=1.0.0'], + const testTable: Array<[string, Event, EventTraceContext]> = [ + [ + 'adds only baggage item', + { + type: 'transaction', + release: '1.0.0', + contexts: { + trace: { + trace_id: '1234', + }, + }, + }, + { release: '1.0.0', trace_id: '1234', public_key: 'pubKey123' }, + ], [ 'adds two baggage items', - { type: 'transaction', release: '1.0.0', environment: 'prod' }, - 'sentry-environment=prod,sentry-release=1.0.0', + { + type: 'transaction', + release: '1.0.0', + environment: 'prod', + contexts: { + trace: { + trace_id: '1234', + }, + }, + }, + { release: '1.0.0', environment: 'prod', trace_id: '1234', public_key: 'pubKey123' }, ], [ 'adds all baggageitems', @@ -39,16 +66,28 @@ describe('createEventEnvelope', () => { environment: 'prod', user: { id: 'bob', segment: 'segmentA' }, transaction: 'TX', + contexts: { + trace: { + trace_id: '1234', + }, + }, + }, + { + release: '1.0.0', + environment: 'prod', + user: { id: 'bob', segment: 'segmentA' }, + transaction: 'TX', + trace_id: '1234', + public_key: 'pubKey123', }, - 'sentry-environment=prod,sentry-release=1.0.0,sentry-transaction=TX,sentry-userid=bob,sentry-usersegment=segmentA', ], ]; - it.each(testTable)('%s', (_: string, event, serializedBaggage) => { + it.each(testTable)('%s', (_: string, event, trace) => { const envelopeHeaders = createEventEnvelope(event, testDsn)[0]; expect(envelopeHeaders).toBeDefined(); - expect(envelopeHeaders.baggage).toBeDefined(); - expect(envelopeHeaders.baggage).toEqual(serializedBaggage); + expect(envelopeHeaders.trace).toBeDefined(); + expect(envelopeHeaders.trace).toEqual(trace); }); }); }); diff --git a/packages/integration-tests/suites/tracing/baggage/test.ts b/packages/integration-tests/suites/tracing/baggage/test.ts index 5617e0d57309..5dcb8e82bcf1 100644 --- a/packages/integration-tests/suites/tracing/baggage/test.ts +++ b/packages/integration-tests/suites/tracing/baggage/test.ts @@ -1,16 +1,23 @@ import { expect } from '@playwright/test'; -import { Event, EventEnvelopeHeaders } from '@sentry/types'; +import { EventEnvelopeHeaders } from '@sentry/types'; import { sentryTest } from '../../../utils/fixtures'; import { envelopeHeaderRequestParser, getFirstSentryEnvelopeRequest } from '../../../utils/helpers'; -sentryTest('should send baggage data in transaction envelope header', async ({ getLocalTestPath, page }) => { +sentryTest('should send trace context data in transaction envelope header', async ({ getLocalTestPath, page }) => { const url = await getLocalTestPath({ testDir: __dirname }); const envHeader = await getFirstSentryEnvelopeRequest(page, url, envelopeHeaderRequestParser); - expect(envHeader.baggage).toBeDefined(); - expect(envHeader.baggage).toEqual( - 'sentry-environment=production,sentry-transaction=testTransactionBaggage,sentry-userid=user123,sentry-usersegment=segmentB', - ); + expect(envHeader.trace).toBeDefined(); + expect(envHeader.trace).toMatchObject({ + environment: 'production', + transaction: 'testTransactionBaggage', + user: { + id: 'user123', + segment: 'segmentB', + }, + public_key: 'public', + trace_id: expect.any(String), + }); }); diff --git a/packages/types/src/baggage.ts b/packages/types/src/baggage.ts new file mode 100644 index 000000000000..58fc5f294347 --- /dev/null +++ b/packages/types/src/baggage.ts @@ -0,0 +1,17 @@ +export type AllowedBaggageKeys = 'environment' | 'release' | 'userid' | 'transaction' | 'usersegment'; +export type BaggageObj = Partial & Record>; + +/** + * The baggage data structure represents key,value pairs based on the baggage + * spec: https://www.w3.org/TR/baggage + * + * It is expected that users interact with baggage using the helpers methods: + * `createBaggage`, `getBaggageValue`, and `setBaggageValue`. + * + * Internally, the baggage data structure is a tuple of length 2, separating baggage values + * based on if they are related to Sentry or not. If the baggage values are + * set/used by sentry, they will be stored in an object to be easily accessed. + * If they are not, they are kept as a string to be only accessed when serialized + * at baggage propagation time. + */ +export type Baggage = [BaggageObj, string]; diff --git a/packages/types/src/envelope.ts b/packages/types/src/envelope.ts index bd05b1eb0a7f..14df524e37b8 100644 --- a/packages/types/src/envelope.ts +++ b/packages/types/src/envelope.ts @@ -1,12 +1,26 @@ import { ClientReport } from './clientreport'; +import { DsnComponents } from './dsn'; import { Event } from './event'; import { SdkInfo } from './sdkinfo'; import { Session, SessionAggregates } from './session'; -import { TransactionSamplingMethod } from './transaction'; +import { Transaction, TransactionSamplingMethod } from './transaction'; import { UserFeedback } from './user'; // Based on: https://develop.sentry.dev/sdk/envelopes/ +// Based on https://github.com/getsentry/relay/blob/b23b8d3b2360a54aaa4d19ecae0231201f31df5e/relay-sampling/src/lib.rs#L685-L707 +export type EventTraceContext = { + trace_id: Transaction['traceId']; + public_key: DsnComponents['publicKey']; + release?: string; + user?: { + id?: string; + segment?: string; + }; + environment?: string; + transaction?: string; +}; + export type EnvelopeItemType = | 'client_report' | 'user_report' @@ -59,7 +73,7 @@ export type SessionItem = | BaseEnvelopeItem; export type ClientReportItem = BaseEnvelopeItem; -export type EventEnvelopeHeaders = { event_id: string; sent_at: string; baggage?: string }; +export type EventEnvelopeHeaders = { event_id: string; sent_at: string; trace?: EventTraceContext }; type SessionEnvelopeHeaders = { sent_at: string }; type ClientReportEnvelopeHeaders = BaseEnvelopeHeaders; diff --git a/packages/types/src/index.ts b/packages/types/src/index.ts index 17eb63bb7842..6f9e3342b779 100644 --- a/packages/types/src/index.ts +++ b/packages/types/src/index.ts @@ -1,4 +1,5 @@ export type { Attachment } from './attachment'; +export type { AllowedBaggageKeys, Baggage, BaggageObj } from './baggage'; export type { Breadcrumb, BreadcrumbHint } from './breadcrumb'; export type { Client } from './client'; export type { ClientReport, Outcome, EventDropReason } from './clientreport'; diff --git a/packages/types/src/user.ts b/packages/types/src/user.ts index 0f27df252cf6..0295c1432727 100644 --- a/packages/types/src/user.ts +++ b/packages/types/src/user.ts @@ -5,6 +5,7 @@ export interface User { ip_address?: string; email?: string; username?: string; + segment?: string; } export interface UserFeedback { diff --git a/packages/utils/src/baggage.ts b/packages/utils/src/baggage.ts index f8bec69439b3..ddf3b9b24afa 100644 --- a/packages/utils/src/baggage.ts +++ b/packages/utils/src/baggage.ts @@ -1,24 +1,8 @@ +import { Baggage, BaggageObj } from '@sentry/types'; + import { IS_DEBUG_BUILD } from './flags'; import { logger } from './logger'; -export type AllowedBaggageKeys = 'environment' | 'release' | 'userid' | 'transaction' | 'usersegment'; -export type BaggageObj = Partial & Record>; - -/** - * The baggage data structure represents key,value pairs based on the baggage - * spec: https://www.w3.org/TR/baggage - * - * It is expected that users interact with baggage using the helpers methods: - * `createBaggage`, `getBaggageValue`, and `setBaggageValue`. - * - * Internally, the baggage data structure is a tuple of length 2, separating baggage values - * based on if they are related to Sentry or not. If the baggage values are - * set/used by sentry, they will be stored in an object to be easily accessed. - * If they are not, they are kept as a string to be only accessed when serialized - * at baggage propagation time. - */ -export type Baggage = [BaggageObj, string]; - export const BAGGAGE_HEADER_NAME = 'baggage'; export const SENTRY_BAGGAGE_KEY_PREFIX = 'sentry-'; @@ -52,6 +36,11 @@ export function isBaggageEmpty(baggage: Baggage): boolean { return Object.keys(baggage[0]).length === 0; } +/** Returns Sentry specific baggage values */ +export function getSentryBaggageItems(baggage: Baggage): BaggageObj { + return baggage[0]; +} + /** Serialize a baggage object */ export function serializeBaggage(baggage: Baggage): string { return Object.keys(baggage[0]).reduce((prev, key: keyof BaggageObj) => { From d48711f09d5bebe9233e8f8e0a200837b933fe85 Mon Sep 17 00:00:00 2001 From: Lukas Stracke Date: Mon, 23 May 2022 14:18:27 +0200 Subject: [PATCH 186/204] feat(tracing): Add empty baggage header propagation to outgoing requests (#5133) Adds the propagation of an "empty" baggage header. The word "empty" is however kind of misleading as the header is not necessarily empty. In order to comply with the baggage spec, as of this patch, we propagate incoming (3rd party) baggage to outgoing requests. The important part is that we actually add the `baggage` HTTP header to outgoing requests which is a breaking change in terms of CORS rules having to be adjusted. We don't yet add `sentry-` baggage entries to the propagated baggage. This will come in a follow up PR which does not necessarily have to be part of the initial v7 release as it is no longer a breaking change. Overall, this is heavily inspired from #3945 (thanks @lobsterkatie for doing the hard work) More specifically, this PR does the following things: 1. Extract incoming baggage headers and store them in the created transaction's metadata. Incoming baggage data is intercepted at: * Node SDK: TracingHandler * Serverless SDK: AWS wrapHandler * Serverless SDK: GCP wrapHttpFunction * Next.js: SDK makeWrappedReqHandler * Next.js: SDK withSentry * BrowserTracing Integration: by parsing the `` tags (analogously to the `sentry-trace` header) 2. Add the extracted baggage data to outgoing requests we instrument at: * Node SDK: HTTP integration * Tracing: instrumented Fetch and XHR callbacks Co-authored-by: Luca Forstner --- MIGRATION.md | 9 ++ packages/core/test/lib/envelope.test.ts | 3 +- .../tracing/browsertracing/meta/template.html | 1 + .../tracing/browsertracing/meta/test.ts | 18 +++- packages/nextjs/src/utils/instrumentServer.ts | 13 ++- packages/nextjs/src/utils/withSentry.ts | 12 ++- .../baggage-header-assign/test.ts | 52 ++++++++++++ .../sentry-trace/baggage-header-out/test.ts | 19 +++++ .../suites/express/sentry-trace/server.ts | 2 +- packages/node/src/handlers.ts | 9 +- packages/node/src/integrations/http.ts | 11 ++- packages/node/test/handlers.test.ts | 37 ++++++++- packages/node/test/integrations/http.test.ts | 37 +++++++++ packages/serverless/src/awslambda.ts | 9 +- packages/serverless/src/gcpfunction/http.ts | 9 +- packages/serverless/test/awslambda.test.ts | 34 ++++++++ packages/serverless/test/gcpfunction.test.ts | 33 ++++++++ .../tracing/src/browser/browsertracing.ts | 26 ++++-- packages/tracing/src/browser/request.ts | 82 +++++++++++++++---- packages/tracing/src/span.ts | 9 +- .../test/browser/browsertracing.test.ts | 66 ++++++++++++--- packages/types/src/index.ts | 1 + packages/types/src/span.ts | 4 + packages/types/src/transaction.ts | 9 +- packages/utils/src/baggage.ts | 37 +++++++++ packages/utils/test/baggage.test.ts | 24 ++++++ 26 files changed, 507 insertions(+), 59 deletions(-) create mode 100644 packages/node-integration-tests/suites/express/sentry-trace/baggage-header-assign/test.ts create mode 100644 packages/node-integration-tests/suites/express/sentry-trace/baggage-header-out/test.ts diff --git a/MIGRATION.md b/MIGRATION.md index d2d1695719b4..d6aad039ecaf 100644 --- a/MIGRATION.md +++ b/MIGRATION.md @@ -10,6 +10,8 @@ Below we will outline all the breaking changes you should consider when upgradin - We bumped the TypeScript version we generate our types with to 3.8.3. Please check if your TypeScript projects using TypeScript version 3.7 or lower still compile. Otherwise, upgrade your TypeScript version. - `whitelistUrls` and `blacklistUrls` have been renamed to `allowUrls` and `denyUrls` in the `Sentry.init()` options. - The `UserAgent` integration is now called `HttpContext`. +- If you are using Performance Monitoring and with tracing enabled, you might have to [make adjustments to +your server's CORS settings](#-propagation-of-baggage-header) ## Dropping Support for Node.js v6 @@ -319,6 +321,13 @@ session.update({ environment: 'prod' }); session.close('ok'); ``` +## Propagation of Baggage Header + +We introduced a new way of propagating tracing and transaction-related information between services. This +change adds the [`baggage` HTTP header](https://www.w3.org/TR/baggage/) to outgoing requests if the instrumentation of requests is enabled. Since this adds a header to your HTTP requests, you might need +to adjust your Server's CORS settings to allow this additional header. Take a look at the [Sentry docs](https://docs.sentry.io/platforms/javascript/performance/connect-services/#navigation-and-other-xhr-requests) +for more in-depth instructions what to change. + ## General API Changes For our efforts to reduce bundle size of the SDK we had to remove and refactor parts of the package which introduced a few changes to the API: diff --git a/packages/core/test/lib/envelope.test.ts b/packages/core/test/lib/envelope.test.ts index 72060cb81364..13dda0e276d4 100644 --- a/packages/core/test/lib/envelope.test.ts +++ b/packages/core/test/lib/envelope.test.ts @@ -1,5 +1,4 @@ -import { DsnComponents, Event } from '@sentry/types'; -import { EventTraceContext } from '@sentry/types/build/types/envelope'; +import { DsnComponents, Event, EventTraceContext } from '@sentry/types'; import { createEventEnvelope } from '../../src/envelope'; diff --git a/packages/integration-tests/suites/tracing/browsertracing/meta/template.html b/packages/integration-tests/suites/tracing/browsertracing/meta/template.html index 0afff8864522..60c6c062c7f3 100644 --- a/packages/integration-tests/suites/tracing/browsertracing/meta/template.html +++ b/packages/integration-tests/suites/tracing/browsertracing/meta/template.html @@ -2,5 +2,6 @@ + diff --git a/packages/integration-tests/suites/tracing/browsertracing/meta/test.ts b/packages/integration-tests/suites/tracing/browsertracing/meta/test.ts index 263fd9257186..d2d44ae74050 100644 --- a/packages/integration-tests/suites/tracing/browsertracing/meta/test.ts +++ b/packages/integration-tests/suites/tracing/browsertracing/meta/test.ts @@ -1,8 +1,8 @@ import { expect } from '@playwright/test'; -import { Event } from '@sentry/types'; +import { Event, EventEnvelopeHeaders } from '@sentry/types'; import { sentryTest } from '../../../../utils/fixtures'; -import { getFirstSentryEnvelopeRequest } from '../../../../utils/helpers'; +import { envelopeHeaderRequestParser, getFirstSentryEnvelopeRequest } from '../../../../utils/helpers'; sentryTest( 'should create a pageload transaction based on `sentry-trace` ', @@ -21,6 +21,20 @@ sentryTest( }, ); +// TODO this we can't really test until we actually propagate sentry- entries in baggage +// skipping for now but this must be adjusted later on +sentryTest.skip( + 'should pick up `baggage` tag and propagate the content in transaction', + async ({ getLocalTestPath, page }) => { + const url = await getLocalTestPath({ testDir: __dirname }); + + const envHeader = await getFirstSentryEnvelopeRequest(page, url, envelopeHeaderRequestParser); + + expect(envHeader.trace).toBeDefined(); + expect(envHeader.trace).toEqual('{version:2.1.12}'); + }, +); + sentryTest( "should create a navigation that's not influenced by `sentry-trace` ", async ({ getLocalTestPath, page }) => { diff --git a/packages/nextjs/src/utils/instrumentServer.ts b/packages/nextjs/src/utils/instrumentServer.ts index cba8e0ab9423..bf4adc9ec8b3 100644 --- a/packages/nextjs/src/utils/instrumentServer.ts +++ b/packages/nextjs/src/utils/instrumentServer.ts @@ -8,7 +8,14 @@ import { startTransaction, } from '@sentry/node'; import { extractTraceparentData, getActiveTransaction, hasTracingEnabled } from '@sentry/tracing'; -import { addExceptionMechanism, fill, isString, logger, stripUrlQueryAndFragment } from '@sentry/utils'; +import { + addExceptionMechanism, + fill, + isString, + logger, + parseBaggageString, + stripUrlQueryAndFragment, +} from '@sentry/utils'; import * as domain from 'domain'; import * as http from 'http'; import { default as createNextServer } from 'next'; @@ -252,6 +259,9 @@ function makeWrappedReqHandler(origReqHandler: ReqHandler): WrappedReqHandler { IS_DEBUG_BUILD && logger.log(`[Tracing] Continuing trace ${traceparentData?.traceId}.`); } + const baggage = + nextReq.headers && isString(nextReq.headers.baggage) && parseBaggageString(nextReq.headers.baggage); + // pull off query string, if any const reqPath = stripUrlQueryAndFragment(nextReq.url); @@ -265,6 +275,7 @@ function makeWrappedReqHandler(origReqHandler: ReqHandler): WrappedReqHandler { op: 'http.server', metadata: { requestPath: reqPath }, ...traceparentData, + ...(baggage && { metadata: { baggage: baggage } }), }, // Extra context passed to the `tracesSampler` (Note: We're combining `nextReq` and `req` this way in order // to not break people's `tracesSampler` functions, even though the format of `nextReq` has changed (see diff --git a/packages/nextjs/src/utils/withSentry.ts b/packages/nextjs/src/utils/withSentry.ts index 00e76ec40b69..742d9f4c9280 100644 --- a/packages/nextjs/src/utils/withSentry.ts +++ b/packages/nextjs/src/utils/withSentry.ts @@ -1,7 +1,14 @@ import { captureException, flush, getCurrentHub, Handlers, startTransaction } from '@sentry/node'; import { extractTraceparentData, hasTracingEnabled } from '@sentry/tracing'; import { Transaction } from '@sentry/types'; -import { addExceptionMechanism, isString, logger, objectify, stripUrlQueryAndFragment } from '@sentry/utils'; +import { + addExceptionMechanism, + isString, + logger, + objectify, + parseBaggageString, + stripUrlQueryAndFragment, +} from '@sentry/utils'; import * as domain from 'domain'; import { NextApiHandler, NextApiRequest, NextApiResponse } from 'next'; @@ -48,6 +55,8 @@ export const withSentry = (origHandler: NextApiHandler): WrappedNextApiHandler = IS_DEBUG_BUILD && logger.log(`[Tracing] Continuing trace ${traceparentData?.traceId}.`); } + const baggage = req.headers && isString(req.headers.baggage) && parseBaggageString(req.headers.baggage); + const url = `${req.url}`; // pull off query string, if any let reqPath = stripUrlQueryAndFragment(url); @@ -66,6 +75,7 @@ export const withSentry = (origHandler: NextApiHandler): WrappedNextApiHandler = name: `${reqMethod}${reqPath}`, op: 'http.server', ...traceparentData, + ...(baggage && { metadata: { baggage: baggage } }), }, // extra context passed to the `tracesSampler` { request: req }, diff --git a/packages/node-integration-tests/suites/express/sentry-trace/baggage-header-assign/test.ts b/packages/node-integration-tests/suites/express/sentry-trace/baggage-header-assign/test.ts new file mode 100644 index 000000000000..81dddb7c1f97 --- /dev/null +++ b/packages/node-integration-tests/suites/express/sentry-trace/baggage-header-assign/test.ts @@ -0,0 +1,52 @@ +import * as path from 'path'; + +import { getAPIResponse, runServer } from '../../../../utils/index'; +import { TestAPIResponse } from '../server'; + +test('Should assign `baggage` header which contains 3rd party trace baggage data of an outgoing request.', async () => { + const url = await runServer(__dirname, `${path.resolve(__dirname, '..')}/server.ts`); + + const response = (await getAPIResponse(new URL(`${url}/express`), { + baggage: 'foo=bar,bar=baz', + })) as TestAPIResponse; + + expect(response).toBeDefined(); + expect(response).toMatchObject({ + test_data: { + host: 'somewhere.not.sentry', + baggage: expect.stringContaining('foo=bar,bar=baz'), + }, + }); +}); + +test('Should assign `baggage` header which contains sentry trace baggage data of an outgoing request.', async () => { + const url = await runServer(__dirname, `${path.resolve(__dirname, '..')}/server.ts`); + + const response = (await getAPIResponse(new URL(`${url}/express`), { + baggage: 'sentry-version=1.0.0,sentry-environment=production', + })) as TestAPIResponse; + + expect(response).toBeDefined(); + expect(response).toMatchObject({ + test_data: { + host: 'somewhere.not.sentry', + baggage: expect.stringContaining('sentry-version=1.0.0,sentry-environment=production'), + }, + }); +}); + +test('Should assign `baggage` header which contains sentry and 3rd party trace baggage data of an outgoing request.', async () => { + const url = await runServer(__dirname, `${path.resolve(__dirname, '..')}/server.ts`); + + const response = (await getAPIResponse(new URL(`${url}/express`), { + baggage: 'sentry-version=1.0.0,sentry-environment=production,dogs=great', + })) as TestAPIResponse; + + expect(response).toBeDefined(); + expect(response).toMatchObject({ + test_data: { + host: 'somewhere.not.sentry', + baggage: expect.stringContaining('dogs=great,sentry-version=1.0.0,sentry-environment=production'), + }, + }); +}); diff --git a/packages/node-integration-tests/suites/express/sentry-trace/baggage-header-out/test.ts b/packages/node-integration-tests/suites/express/sentry-trace/baggage-header-out/test.ts new file mode 100644 index 000000000000..70b6cc19edee --- /dev/null +++ b/packages/node-integration-tests/suites/express/sentry-trace/baggage-header-out/test.ts @@ -0,0 +1,19 @@ +import * as path from 'path'; + +import { getAPIResponse, runServer } from '../../../../utils/index'; +import { TestAPIResponse } from '../server'; + +test('should attach a `baggage` header to an outgoing request.', async () => { + const url = await runServer(__dirname, `${path.resolve(__dirname, '..')}/server.ts`); + + const response = (await getAPIResponse(new URL(`${url}/express`))) as TestAPIResponse; + + expect(response).toBeDefined(); + expect(response).toMatchObject({ + test_data: { + host: 'somewhere.not.sentry', + // TODO this is currently still empty but eventually it should contain sentry data + baggage: expect.stringMatching(''), + }, + }); +}); diff --git a/packages/node-integration-tests/suites/express/sentry-trace/server.ts b/packages/node-integration-tests/suites/express/sentry-trace/server.ts index e93b50f18cd6..632d9b8338fb 100644 --- a/packages/node-integration-tests/suites/express/sentry-trace/server.ts +++ b/packages/node-integration-tests/suites/express/sentry-trace/server.ts @@ -6,7 +6,7 @@ import http from 'http'; const app = express(); -export type TestAPIResponse = { test_data: { host: string; 'sentry-trace': string } }; +export type TestAPIResponse = { test_data: { host: string; 'sentry-trace': string; baggage: string } }; Sentry.init({ dsn: 'https://public@dsn.ingest.sentry.io/1337', diff --git a/packages/node/src/handlers.ts b/packages/node/src/handlers.ts index 451ef14e9223..27dbeb3fc9cf 100644 --- a/packages/node/src/handlers.ts +++ b/packages/node/src/handlers.ts @@ -8,6 +8,7 @@ import { isString, logger, normalize, + parseBaggageString, stripUrlQueryAndFragment, } from '@sentry/utils'; import * as cookie from 'cookie'; @@ -61,16 +62,16 @@ export function tracingHandler(): ( next: (error?: any) => void, ): void { // If there is a trace header set, we extract the data from it (parentSpanId, traceId, and sampling decision) - let traceparentData; - if (req.headers && isString(req.headers['sentry-trace'])) { - traceparentData = extractTraceparentData(req.headers['sentry-trace']); - } + const traceparentData = + req.headers && isString(req.headers['sentry-trace']) && extractTraceparentData(req.headers['sentry-trace']); + const baggage = req.headers && isString(req.headers.baggage) && parseBaggageString(req.headers.baggage); const transaction = startTransaction( { name: extractExpressTransactionName(req, { path: true, method: true }), op: 'http.server', ...traceparentData, + ...(baggage && { metadata: { baggage: baggage } }), }, // extra context passed to the tracesSampler { request: extractRequestData(req) }, diff --git a/packages/node/src/integrations/http.ts b/packages/node/src/integrations/http.ts index 19a4e6ba1423..074c81ffe842 100644 --- a/packages/node/src/integrations/http.ts +++ b/packages/node/src/integrations/http.ts @@ -1,6 +1,6 @@ import { getCurrentHub } from '@sentry/core'; import { Integration, Span } from '@sentry/types'; -import { fill, logger, parseSemver } from '@sentry/utils'; +import { fill, logger, mergeAndSerializeBaggage, parseSemver } from '@sentry/utils'; import * as http from 'http'; import * as https from 'https'; @@ -123,7 +123,14 @@ function _createWrappedRequestMethodFactory( logger.log( `[Tracing] Adding sentry-trace header ${sentryTraceHeader} to outgoing request to ${requestUrl}: `, ); - requestOptions.headers = { ...requestOptions.headers, 'sentry-trace': sentryTraceHeader }; + + const headerBaggageString = requestOptions.headers && (requestOptions.headers.baggage as string); + + requestOptions.headers = { + ...requestOptions.headers, + 'sentry-trace': sentryTraceHeader, + baggage: mergeAndSerializeBaggage(span.getBaggage(), headerBaggageString), + }; } } diff --git a/packages/node/test/handlers.test.ts b/packages/node/test/handlers.test.ts index 57f6528139f9..8cee24f678fd 100644 --- a/packages/node/test/handlers.test.ts +++ b/packages/node/test/handlers.test.ts @@ -2,7 +2,7 @@ import * as sentryCore from '@sentry/core'; import * as sentryHub from '@sentry/hub'; import { Hub } from '@sentry/hub'; import { Transaction } from '@sentry/tracing'; -import { Runtime } from '@sentry/types'; +import { Baggage, Runtime } from '@sentry/types'; import { SentryError } from '@sentry/utils'; import * as http from 'http'; import * as net from 'net'; @@ -368,6 +368,41 @@ describe('tracingHandler', () => { expect(transaction.traceId).toEqual('12312012123120121231201212312012'); expect(transaction.parentSpanId).toEqual('1121201211212012'); expect(transaction.sampled).toEqual(false); + expect(transaction.metadata?.baggage).toBeUndefined(); + }); + + it("pulls parent's data from tracing and baggage headers on the request", () => { + req.headers = { + 'sentry-trace': '12312012123120121231201212312012-1121201211212012-0', + baggage: 'sentry-version=1.0,sentry-environment=production', + }; + + sentryTracingMiddleware(req, res, next); + + const transaction = (res as any).__sentry_transaction; + + // since we have no tracesSampler defined, the default behavior (inherit if possible) applies + expect(transaction.traceId).toEqual('12312012123120121231201212312012'); + expect(transaction.parentSpanId).toEqual('1121201211212012'); + expect(transaction.sampled).toEqual(false); + expect(transaction.metadata?.baggage).toBeDefined(); + expect(transaction.metadata?.baggage).toEqual([{ version: '1.0', environment: 'production' }, ''] as Baggage); + }); + + it("pulls parent's baggage (sentry + third party entries) headers on the request", () => { + req.headers = { + baggage: 'sentry-version=1.0,sentry-environment=production,dogs=great,cats=boring', + }; + + sentryTracingMiddleware(req, res, next); + + const transaction = (res as any).__sentry_transaction; + + expect(transaction.metadata?.baggage).toBeDefined(); + expect(transaction.metadata?.baggage).toEqual([ + { version: '1.0', environment: 'production' }, + 'dogs=great,cats=boring', + ] as Baggage); }); it('extracts request data for sampling context', () => { diff --git a/packages/node/test/integrations/http.test.ts b/packages/node/test/integrations/http.test.ts index ff0af8b65acf..50d26e92452a 100644 --- a/packages/node/test/integrations/http.test.ts +++ b/packages/node/test/integrations/http.test.ts @@ -86,6 +86,43 @@ describe('tracing', () => { expect(sentryTraceHeader).not.toBeDefined(); }); + + it('attaches the baggage header to outgoing non-sentry requests', async () => { + nock('http://dogs.are.great').get('/').reply(200); + + createTransactionOnScope(); + + const request = http.get('http://dogs.are.great/'); + const baggageHeader = request.getHeader('baggage') as string; + + expect(baggageHeader).toBeDefined(); + // this might change once we actually add our baggage data to the header + expect(baggageHeader).toEqual(''); + }); + + it('propagates 3rd party baggage header data to outgoing non-sentry requests', async () => { + nock('http://dogs.are.great').get('/').reply(200); + + createTransactionOnScope(); + + const request = http.get({ host: 'http://dogs.are.great/', headers: { baggage: 'dog=great' } }); + const baggageHeader = request.getHeader('baggage') as string; + + expect(baggageHeader).toBeDefined(); + // this might change once we actually add our baggage data to the header + expect(baggageHeader).toEqual('dog=great'); + }); + + it("doesn't attach the sentry-trace header to outgoing sentry requests", () => { + nock('http://squirrelchasers.ingest.sentry.io').get('/api/12312012/store/').reply(200); + + createTransactionOnScope(); + + const request = http.get('http://squirrelchasers.ingest.sentry.io/api/12312012/store/'); + const baggage = request.getHeader('baggage'); + + expect(baggage).not.toBeDefined(); + }); }); describe('default protocols', () => { diff --git a/packages/serverless/src/awslambda.ts b/packages/serverless/src/awslambda.ts index 5e00e3419153..a2825b1e1e2e 100644 --- a/packages/serverless/src/awslambda.ts +++ b/packages/serverless/src/awslambda.ts @@ -11,7 +11,7 @@ import { } from '@sentry/node'; import { extractTraceparentData } from '@sentry/tracing'; import { Integration } from '@sentry/types'; -import { extensionRelayDSN, isString, logger } from '@sentry/utils'; +import { extensionRelayDSN, isString, logger, parseBaggageString } from '@sentry/utils'; // NOTE: I have no idea how to fix this right now, and don't want to waste more time, as it builds just fine — Kamil // eslint-disable-next-line import/no-unresolved import { Context, Handler } from 'aws-lambda'; @@ -288,10 +288,17 @@ export function wrapHandler( if (eventWithHeaders.headers && isString(eventWithHeaders.headers['sentry-trace'])) { traceparentData = extractTraceparentData(eventWithHeaders.headers['sentry-trace']); } + + const baggage = + eventWithHeaders.headers && + isString(eventWithHeaders.headers.baggage) && + parseBaggageString(eventWithHeaders.headers.baggage); + const transaction = startTransaction({ name: context.functionName, op: 'awslambda.handler', ...traceparentData, + ...(baggage && { metadata: { baggage: baggage } }), }); const hub = getCurrentHub(); diff --git a/packages/serverless/src/gcpfunction/http.ts b/packages/serverless/src/gcpfunction/http.ts index a1eec1e48736..26c32b4a21cb 100644 --- a/packages/serverless/src/gcpfunction/http.ts +++ b/packages/serverless/src/gcpfunction/http.ts @@ -1,6 +1,6 @@ import { captureException, flush, getCurrentHub, Handlers, startTransaction } from '@sentry/node'; import { extractTraceparentData } from '@sentry/tracing'; -import { isString, logger, stripUrlQueryAndFragment } from '@sentry/utils'; +import { isString, logger, parseBaggageString, stripUrlQueryAndFragment } from '@sentry/utils'; import { IS_DEBUG_BUILD } from '../flags'; import { domainify, getActiveDomain, proxyFunction } from './../utils'; @@ -56,10 +56,17 @@ function _wrapHttpFunction(fn: HttpFunction, wrapOptions: Partial { await wrappedHandler(fakeEvent, fakeContext, fakeCallback); }); + test('incoming trace headers are correctly parsed and used', async () => { + expect.assertions(1); + + fakeEvent.headers = { + 'sentry-trace': '12312012123120121231201212312012-1121201211212012-0', + baggage: 'sentry-release=2.12.1,maisey=silly,charlie=goofy', + }; + + const handler: Handler = (_event, _context, callback) => { + expect(Sentry.startTransaction).toBeCalledWith( + expect.objectContaining({ + parentSpanId: '1121201211212012', + parentSampled: false, + op: 'awslambda.handler', + name: 'functionName', + traceId: '12312012123120121231201212312012', + metadata: { + baggage: [ + { + release: '2.12.1', + }, + 'maisey=silly,charlie=goofy', + ], + }, + }), + ); + + callback(undefined, { its: 'fine' }); + }; + + const wrappedHandler = wrapHandler(handler); + await wrappedHandler(fakeEvent, fakeContext, fakeCallback); + }); + test('capture error', async () => { expect.assertions(10); diff --git a/packages/serverless/test/gcpfunction.test.ts b/packages/serverless/test/gcpfunction.test.ts index 5072de8b17b7..89b6aa084b9b 100644 --- a/packages/serverless/test/gcpfunction.test.ts +++ b/packages/serverless/test/gcpfunction.test.ts @@ -120,6 +120,39 @@ describe('GCPFunction', () => { expect(Sentry.flush).toBeCalledWith(2000); }); + test('incoming trace headers are correctly parsed and used', async () => { + expect.assertions(1); + + const handler: HttpFunction = (_req, res) => { + res.statusCode = 200; + res.end(); + }; + const wrappedHandler = wrapHttpFunction(handler); + const traceHeaders = { + 'sentry-trace': '12312012123120121231201212312012-1121201211212012-0', + baggage: 'sentry-release=2.12.1,maisey=silly,charlie=goofy', + }; + await handleHttp(wrappedHandler, traceHeaders); + + expect(Sentry.startTransaction).toBeCalledWith( + expect.objectContaining({ + name: 'POST /path', + op: 'gcp.function.http', + traceId: '12312012123120121231201212312012', + parentSpanId: '1121201211212012', + parentSampled: false, + metadata: { + baggage: [ + { + release: '2.12.1', + }, + 'maisey=silly,charlie=goofy', + ], + }, + }), + ); + }); + test('capture error', async () => { expect.assertions(5); diff --git a/packages/tracing/src/browser/browsertracing.ts b/packages/tracing/src/browser/browsertracing.ts index 780edf9052d4..0d8e24de8c92 100644 --- a/packages/tracing/src/browser/browsertracing.ts +++ b/packages/tracing/src/browser/browsertracing.ts @@ -1,6 +1,6 @@ import { Hub } from '@sentry/hub'; import { EventProcessor, Integration, Transaction, TransactionContext } from '@sentry/types'; -import { getGlobalObject, logger } from '@sentry/utils'; +import { getGlobalObject, logger, parseBaggageString } from '@sentry/utils'; import { IS_DEBUG_BUILD } from '../flags'; import { startIdleTransaction } from '../hubextensions'; @@ -204,7 +204,7 @@ export class BrowserTracing implements Integration { // eslint-disable-next-line @typescript-eslint/unbound-method const { beforeNavigate, idleTimeout, finalTimeout } = this.options; - const parentContextFromHeader = context.op === 'pageload' ? getHeaderContext() : undefined; + const parentContextFromHeader = context.op === 'pageload' ? extractTraceDataFromMetaTags() : undefined; const expandedContext = { ...context, @@ -243,14 +243,22 @@ export class BrowserTracing implements Integration { } /** - * Gets transaction context from a sentry-trace meta. - * - * @returns Transaction context data from the header or undefined if there's no header or the header is malformed + * Gets transaction context data from `sentry-trace` and `baggage` tags. + * @returns Transaction context data or undefined neither tag exists or has valid data */ -export function getHeaderContext(): Partial | undefined { - const header = getMetaContent('sentry-trace'); - if (header) { - return extractTraceparentData(header); +export function extractTraceDataFromMetaTags(): Partial | undefined { + const sentrytraceValue = getMetaContent('sentry-trace'); + const baggageValue = getMetaContent('baggage'); + + const sentrytraceData = sentrytraceValue ? extractTraceparentData(sentrytraceValue) : undefined; + const baggage = baggageValue ? parseBaggageString(baggageValue) : undefined; + + // TODO more extensive checks for baggage validity/emptyness? + if (sentrytraceData || baggage) { + return { + ...(sentrytraceData && sentrytraceData), + ...(baggage && { metadata: { baggage } }), + }; } return undefined; diff --git a/packages/tracing/src/browser/request.ts b/packages/tracing/src/browser/request.ts index 9a71a7387bed..b45775edd05f 100644 --- a/packages/tracing/src/browser/request.ts +++ b/packages/tracing/src/browser/request.ts @@ -1,4 +1,11 @@ -import { addInstrumentationHandler, isInstanceOf, isMatchingPattern } from '@sentry/utils'; +/* eslint-disable max-lines */ +import { + addInstrumentationHandler, + BAGGAGE_HEADER_NAME, + isInstanceOf, + isMatchingPattern, + mergeAndSerializeBaggage, +} from '@sentry/utils'; import { Span } from '../span'; import { getActiveTransaction, hasTracingEnabled } from '../utils'; @@ -70,12 +77,24 @@ export interface XHRData { }; __sentry_xhr_span_id__?: string; setRequestHeader?: (key: string, val: string) => void; + getRequestHeader?: (key: string) => string; __sentry_own_request__?: boolean; }; startTimestamp: number; endTimestamp?: number; } +type PolymorphicRequestHeaders = + | Record + | Array<[string, string]> + // the below is not preicsely the Header type used in Request, but it'll pass duck-typing + | { + // eslint-disable-next-line @typescript-eslint/no-explicit-any + [key: string]: any; + append: (key: string, value: string) => void; + get: (key: string) => string; + }; + export const defaultRequestInstrumentationOptions: RequestInstrumentationOptions = { traceFetch: true, traceXHR: true, @@ -179,25 +198,48 @@ export function fetchCallback( const request = (handlerData.args[0] = handlerData.args[0] as string | Request); // eslint-disable-next-line @typescript-eslint/no-explicit-any const options = (handlerData.args[1] = (handlerData.args[1] as { [key: string]: any }) || {}); - let headers = options.headers; - if (isInstanceOf(request, Request)) { - headers = (request as Request).headers; - } - if (headers) { + options.headers = addTracingHeaders(request, span, options); + } +} + +function addTracingHeaders( + request: string | Request, + span: Span, + options: { [key: string]: any }, +): PolymorphicRequestHeaders { + let headers = options.headers; + + if (isInstanceOf(request, Request)) { + headers = (request as Request).headers; + } + const incomingBaggage = span.getBaggage(); + + if (headers) { + // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access + if (typeof headers.append === 'function') { // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access - if (typeof headers.append === 'function') { - // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access - headers.append('sentry-trace', span.toTraceparent()); - } else if (Array.isArray(headers)) { - headers = [...headers, ['sentry-trace', span.toTraceparent()]]; - } else { - headers = { ...headers, 'sentry-trace': span.toTraceparent() }; - } + headers.append('sentry-trace', span.toTraceparent()); + // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access + headers.append(BAGGAGE_HEADER_NAME, mergeAndSerializeBaggage(incomingBaggage, headers.get(BAGGAGE_HEADER_NAME))); + } else if (Array.isArray(headers)) { + const [, headerBaggageString] = headers.find(([key, _]) => key === BAGGAGE_HEADER_NAME); + headers = [ + ...headers, + ['sentry-trace', span.toTraceparent()], + [BAGGAGE_HEADER_NAME, mergeAndSerializeBaggage(incomingBaggage, headerBaggageString)], + ]; } else { - headers = { 'sentry-trace': span.toTraceparent() }; + headers = { + ...headers, + 'sentry-trace': span.toTraceparent(), + // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access + baggage: mergeAndSerializeBaggage(incomingBaggage, headers.baggage), + }; } - options.headers = headers; + } else { + headers = { 'sentry-trace': span.toTraceparent(), baggage: mergeAndSerializeBaggage(incomingBaggage) }; } + return headers; } /** @@ -254,6 +296,14 @@ export function xhrCallback( if (handlerData.xhr.setRequestHeader) { try { handlerData.xhr.setRequestHeader('sentry-trace', span.toTraceparent()); + + const headerBaggageString = + handlerData.xhr.getRequestHeader && handlerData.xhr.getRequestHeader(BAGGAGE_HEADER_NAME); + + handlerData.xhr.setRequestHeader( + BAGGAGE_HEADER_NAME, + mergeAndSerializeBaggage(span.getBaggage(), headerBaggageString), + ); } catch (_) { // Error: InvalidStateError: Failed to execute 'setRequestHeader' on 'XMLHttpRequest': The object's state must be OPENED. } diff --git a/packages/tracing/src/span.ts b/packages/tracing/src/span.ts index baece680f52c..68cba79eb7cc 100644 --- a/packages/tracing/src/span.ts +++ b/packages/tracing/src/span.ts @@ -1,5 +1,5 @@ /* eslint-disable max-lines */ -import { Primitive, Span as SpanInterface, SpanContext, Transaction } from '@sentry/types'; +import { Baggage, Primitive, Span as SpanInterface, SpanContext, Transaction } from '@sentry/types'; import { dropUndefinedKeys, timestampWithMs, uuid4 } from '@sentry/utils'; /** @@ -298,6 +298,13 @@ export class Span implements SpanInterface { }); } + /** + * @inheritdoc + */ + public getBaggage(): Baggage | undefined { + return this.transaction && this.transaction.metadata.baggage; + } + /** * @inheritDoc */ diff --git a/packages/tracing/test/browser/browsertracing.test.ts b/packages/tracing/test/browser/browsertracing.test.ts index 4bfc093fbe2b..0d5265522406 100644 --- a/packages/tracing/test/browser/browsertracing.test.ts +++ b/packages/tracing/test/browser/browsertracing.test.ts @@ -1,12 +1,13 @@ import { BrowserClient } from '@sentry/browser'; import { Hub, makeMain } from '@sentry/hub'; +import { BaggageObj } from '@sentry/types'; import { getGlobalObject, InstrumentHandlerCallback, InstrumentHandlerType } from '@sentry/utils'; import { JSDOM } from 'jsdom'; import { BrowserTracing, BrowserTracingOptions, - getHeaderContext, + extractTraceDataFromMetaTags, getMetaContent, } from '../../src/browser/browsertracing'; import { MetricsInstrumentation } from '../../src/browser/metrics'; @@ -215,7 +216,8 @@ describe('BrowserTracing', () => { it('sets transaction context from sentry-trace header', () => { const name = 'sentry-trace'; const content = '126de09502ae4e0fb26c6967190756a4-b6e54397b12a2a0f-1'; - document.head.innerHTML = ``; + document.head.innerHTML = + `` + ''; const startIdleTransaction = jest.spyOn(hubExtensions, 'startIdleTransaction'); createBrowserTracing(true, { routingInstrumentation: customInstrumentRouting }); @@ -226,6 +228,9 @@ describe('BrowserTracing', () => { traceId: '126de09502ae4e0fb26c6967190756a4', parentSpanId: 'b6e54397b12a2a0f', parentSampled: true, + metadata: { + baggage: [{ release: '2.1.14' }, 'foo=bar'], + }, }), expect.any(Number), expect.any(Number), @@ -322,7 +327,7 @@ describe('BrowserTracing', () => { }); }); - describe('sentry-trace element', () => { + describe('sentry-trace and baggage elements', () => { describe('getMetaContent', () => { it('finds the specified tag and extracts the value', () => { const name = 'sentry-trace'; @@ -352,12 +357,12 @@ describe('BrowserTracing', () => { }); }); - describe('getHeaderContext', () => { + describe('extractTraceDataFromMetaTags()', () => { it('correctly parses a valid sentry-trace meta header', () => { document.head.innerHTML = ''; - const headerContext = getHeaderContext(); + const headerContext = extractTraceDataFromMetaTags(); expect(headerContext).toBeDefined(); expect(headerContext!.traceId).toEqual('12312012123120121231201212312012'); @@ -365,54 +370,93 @@ describe('BrowserTracing', () => { expect(headerContext!.parentSampled).toEqual(false); }); - it('returns undefined if the header is malformed', () => { + it('correctly parses a valid baggage meta header', () => { + document.head.innerHTML = ''; + + const headerContext = extractTraceDataFromMetaTags(); + + expect(headerContext).toBeDefined(); + expect(headerContext?.metadata?.baggage).toBeDefined(); + const baggage = headerContext?.metadata?.baggage; + expect(baggage && baggage[0]).toBeDefined(); + expect(baggage && baggage[0]).toEqual({ + release: '2.1.12', + } as BaggageObj); + expect(baggage && baggage[1]).toBeDefined(); + expect(baggage && baggage[1]).toEqual('foo=bar'); + }); + + it('returns undefined if the sentry-trace header is malformed', () => { document.head.innerHTML = ''; - const headerContext = getHeaderContext(); + const headerContext = extractTraceDataFromMetaTags(); expect(headerContext).toBeUndefined(); }); + it('does not crash if the baggage header is malformed', () => { + document.head.innerHTML = ''; + + const headerContext = extractTraceDataFromMetaTags(); + + // TODO currently this creates invalid baggage. This must be adressed in a follow-up PR + expect(headerContext).toBeDefined(); + expect(headerContext?.metadata?.baggage).toBeDefined(); + const baggage = headerContext?.metadata?.baggage; + expect(baggage && baggage[0]).toBeDefined(); + expect(baggage && baggage[1]).toBeDefined(); + }); + it("returns undefined if the header isn't there", () => { document.head.innerHTML = ''; - const headerContext = getHeaderContext(); + const headerContext = extractTraceDataFromMetaTags(); expect(headerContext).toBeUndefined(); }); }); describe('using the data', () => { - it('uses the data for pageload transactions', () => { + it('uses the tracing data for pageload transactions', () => { // make sampled false here, so we can see that it's being used rather than the tracesSampleRate-dictated one document.head.innerHTML = - ''; + '' + + ''; // pageload transactions are created as part of the BrowserTracing integration's initialization createBrowserTracing(true); const transaction = getActiveTransaction(hub) as IdleTransaction; + const baggage = transaction.getBaggage()!; expect(transaction).toBeDefined(); expect(transaction.op).toBe('pageload'); expect(transaction.traceId).toEqual('12312012123120121231201212312012'); expect(transaction.parentSpanId).toEqual('1121201211212012'); expect(transaction.sampled).toBe(false); + expect(baggage).toBeDefined(); + expect(baggage[0]).toBeDefined(); + expect(baggage[0]).toEqual({ release: '2.1.14' }); + expect(baggage[1]).toBeDefined(); + expect(baggage[1]).toEqual('foo=bar'); }); it('ignores the data for navigation transactions', () => { mockChangeHistory = () => undefined; document.head.innerHTML = - ''; + '' + + ''; createBrowserTracing(true); mockChangeHistory({ to: 'here', from: 'there' }); const transaction = getActiveTransaction(hub) as IdleTransaction; + const baggage = transaction.getBaggage()!; expect(transaction).toBeDefined(); expect(transaction.op).toBe('navigation'); expect(transaction.traceId).not.toEqual('12312012123120121231201212312012'); expect(transaction.parentSpanId).toBeUndefined(); + expect(baggage).toBeUndefined(); }); }); }); diff --git a/packages/types/src/index.ts b/packages/types/src/index.ts index 6f9e3342b779..3032d7d6cdec 100644 --- a/packages/types/src/index.ts +++ b/packages/types/src/index.ts @@ -19,6 +19,7 @@ export type { EventEnvelope, EventEnvelopeHeaders, EventItem, + EventTraceContext, SessionEnvelope, SessionItem, UserFeedbackItem, diff --git a/packages/types/src/span.ts b/packages/types/src/span.ts index d26887a93f2a..120d754cc329 100644 --- a/packages/types/src/span.ts +++ b/packages/types/src/span.ts @@ -1,3 +1,4 @@ +import { Baggage } from './baggage'; import { Primitive } from './misc'; import { Transaction } from './transaction'; @@ -174,4 +175,7 @@ export interface Span extends SpanContext { timestamp?: number; trace_id: string; }; + + /** return the baggage for dynamic sampling and trace propagation */ + getBaggage(): Baggage | undefined; } diff --git a/packages/types/src/transaction.ts b/packages/types/src/transaction.ts index 95f8b447e19a..b87fc2b151cf 100644 --- a/packages/types/src/transaction.ts +++ b/packages/types/src/transaction.ts @@ -1,6 +1,6 @@ +import { Baggage } from './baggage'; import { ExtractedNodeRequestData, Primitive, WorkerLocation } from './misc'; import { Span, SpanContext } from './span'; - /** * Interface holding Transaction-specific properties */ @@ -131,11 +131,8 @@ export type TransactionSamplingMethod = 'explicitly_set' | 'client_sampler' | 'c export interface TransactionMetadata { transactionSampling?: { rate?: number; method: TransactionSamplingMethod }; - /** The two halves (sentry and third-party) of a transaction's tracestate header, used for dynamic sampling */ - tracestate?: { - sentry?: string; - thirdparty?: string; - }; + /** The baggage object of a transaction's baggage header, used for dynamic sampling */ + baggage?: Baggage; /** For transactions tracing server-side request handling, the path of the request being tracked. */ requestPath?: string; diff --git a/packages/utils/src/baggage.ts b/packages/utils/src/baggage.ts index ddf3b9b24afa..bc2f6568ff1f 100644 --- a/packages/utils/src/baggage.ts +++ b/packages/utils/src/baggage.ts @@ -41,6 +41,14 @@ export function getSentryBaggageItems(baggage: Baggage): BaggageObj { return baggage[0]; } +/** + * Returns 3rd party baggage string of @param baggage + * @param baggage + */ +export function getThirdPartyBaggage(baggage: Baggage): string { + return baggage[1]; +} + /** Serialize a baggage object */ export function serializeBaggage(baggage: Baggage): string { return Object.keys(baggage[0]).reduce((prev, key: keyof BaggageObj) => { @@ -78,3 +86,32 @@ export function parseBaggageString(inputBaggageString: string): Baggage { [{}, ''], ); } + +/** + * Merges the baggage header we saved from the incoming request (or meta tag) with + * a possibly created or modified baggage header by a third party that's been added + * to the outgoing request header. + * + * In case @param headerBaggageString exists, we can safely add the the 3rd party part of @param headerBaggage + * with our @param incomingBaggage. This is possible because if we modified anything beforehand, + * it would only affect parts of the sentry baggage (@see Baggage interface). + * + * @param incomingBaggage the baggage header of the incoming request that might contain sentry entries + * @param headerBaggageString possibly existing baggage header string added from a third party to request headers + * + * @return a merged and serialized baggage string to be propagated with the outgoing request + */ +export function mergeAndSerializeBaggage(incomingBaggage?: Baggage, headerBaggageString?: string): string { + if (!incomingBaggage && !headerBaggageString) { + return ''; + } + + const headerBaggage = (headerBaggageString && parseBaggageString(headerBaggageString)) || undefined; + const thirdPartyHeaderBaggage = headerBaggage && getThirdPartyBaggage(headerBaggage); + + const finalBaggage = createBaggage( + (incomingBaggage && incomingBaggage[0]) || {}, + thirdPartyHeaderBaggage || (incomingBaggage && incomingBaggage[1]) || '', + ); + return serializeBaggage(finalBaggage); +} diff --git a/packages/utils/test/baggage.test.ts b/packages/utils/test/baggage.test.ts index e1c91a87f5ee..07ccdaaedd48 100644 --- a/packages/utils/test/baggage.test.ts +++ b/packages/utils/test/baggage.test.ts @@ -2,6 +2,7 @@ import { createBaggage, getBaggageValue, isBaggageEmpty, + mergeAndSerializeBaggage, parseBaggageString, serializeBaggage, setBaggageValue, @@ -105,4 +106,27 @@ describe('Baggage', () => { expect(isBaggageEmpty(baggage)).toEqual(outcome); }); }); + + describe('mergeAndSerializeBaggage', () => { + it.each([ + [ + 'returns original baggage when there is no additional baggage', + createBaggage({ release: '1.1.1', userid: '1234' }, 'foo=bar'), + undefined, + 'foo=bar,sentry-release=1.1.1,sentry-userid=1234', + ], + [ + 'returns merged baggage when there is a 3rd party header added', + createBaggage({ release: '1.1.1', userid: '1234' }, 'foo=bar'), + 'bar=baz,key=value', + 'bar=baz,key=value,sentry-release=1.1.1,sentry-userid=1234', + ], + ['returns merged baggage original baggage is empty', createBaggage({}), 'bar=baz,key=value', 'bar=baz,key=value'], + ['returns empty string when original and 3rd party baggage are empty', createBaggage({}), '', ''], + ['returns merged baggage original baggage is undefined', undefined, 'bar=baz,key=value', 'bar=baz,key=value'], + ['returns empty string when both params are undefined', undefined, undefined, ''], + ])('%s', (_: string, baggage, headerBaggageString, outcome) => { + expect(mergeAndSerializeBaggage(baggage, headerBaggageString)).toEqual(outcome); + }); + }); }); From 945437a1eaed8a8dba04ef2a42f418d1c800ef9e Mon Sep 17 00:00:00 2001 From: Abhijeet Prasad Date: Mon, 23 May 2022 15:19:29 +0200 Subject: [PATCH 187/204] ref(tracing): Reduce metrics bundle size (#5122) --- .../tracing/src/browser/browsertracing.ts | 12 +- packages/tracing/src/browser/metrics.ts | 446 ------------------ packages/tracing/src/browser/metrics/index.ts | 421 +++++++++++++++++ packages/tracing/src/browser/metrics/utils.ts | 26 + .../test/browser/browsertracing.test.ts | 26 - packages/tracing/test/browser/metrics.test.ts | 221 --------- .../test/browser/metrics/index.test.ts | 162 +++++++ .../test/browser/metrics/utils.test.ts | 40 ++ 8 files changed, 656 insertions(+), 698 deletions(-) delete mode 100644 packages/tracing/src/browser/metrics.ts create mode 100644 packages/tracing/src/browser/metrics/index.ts create mode 100644 packages/tracing/src/browser/metrics/utils.ts delete mode 100644 packages/tracing/test/browser/metrics.test.ts create mode 100644 packages/tracing/test/browser/metrics/index.test.ts create mode 100644 packages/tracing/test/browser/metrics/utils.test.ts diff --git a/packages/tracing/src/browser/browsertracing.ts b/packages/tracing/src/browser/browsertracing.ts index 0d8e24de8c92..cb50474f3d4c 100644 --- a/packages/tracing/src/browser/browsertracing.ts +++ b/packages/tracing/src/browser/browsertracing.ts @@ -7,7 +7,7 @@ import { startIdleTransaction } from '../hubextensions'; import { DEFAULT_FINAL_TIMEOUT, DEFAULT_IDLE_TIMEOUT } from '../idletransaction'; import { extractTraceparentData } from '../utils'; import { registerBackgroundTabDetection } from './backgroundtab'; -import { MetricsInstrumentation } from './metrics'; +import { addPerformanceEntries, startTrackingWebVitals } from './metrics'; import { defaultRequestInstrumentationOptions, instrumentOutgoingRequests, @@ -126,8 +126,6 @@ export class BrowserTracing implements Integration { private _getCurrentHub?: () => Hub; - private readonly _metrics: MetricsInstrumentation; - private readonly _emitOptionsWarning?: boolean; public constructor(_options?: Partial) { @@ -148,7 +146,7 @@ export class BrowserTracing implements Integration { }; const { _metricOptions } = this.options; - this._metrics = new MetricsInstrumentation(_metricOptions && _metricOptions._reportAllChanges); + startTrackingWebVitals(_metricOptions && _metricOptions._reportAllChanges); } /** @@ -235,7 +233,11 @@ export class BrowserTracing implements Integration { { location }, // for use in the tracesSampler ); idleTransaction.registerBeforeFinishCallback(transaction => { - this._metrics.addPerformanceEntries(transaction); + addPerformanceEntries(transaction); + transaction.setTag( + 'sentry_reportAllChanges', + Boolean(this.options._metricOptions && this.options._metricOptions._reportAllChanges), + ); }); return idleTransaction as Transaction; diff --git a/packages/tracing/src/browser/metrics.ts b/packages/tracing/src/browser/metrics.ts deleted file mode 100644 index 97d28806096d..000000000000 --- a/packages/tracing/src/browser/metrics.ts +++ /dev/null @@ -1,446 +0,0 @@ -/* eslint-disable max-lines */ -/* eslint-disable @typescript-eslint/no-explicit-any */ -import { Measurements, SpanContext } from '@sentry/types'; -import { browserPerformanceTimeOrigin, getGlobalObject, htmlTreeAsString, isNodeEnv, logger } from '@sentry/utils'; - -import { IS_DEBUG_BUILD } from '../flags'; -import { Span } from '../span'; -import { Transaction } from '../transaction'; -import { msToSec } from '../utils'; -import { getCLS, LayoutShift } from './web-vitals/getCLS'; -import { getFID } from './web-vitals/getFID'; -import { getLCP, LargestContentfulPaint } from './web-vitals/getLCP'; -import { getVisibilityWatcher } from './web-vitals/lib/getVisibilityWatcher'; -import { NavigatorDeviceMemory, NavigatorNetworkInformation } from './web-vitals/types'; - -const global = getGlobalObject(); - -/** Class tracking metrics */ -export class MetricsInstrumentation { - private _measurements: Measurements = {}; - - private _performanceCursor: number = 0; - private _lcpEntry: LargestContentfulPaint | undefined; - private _clsEntry: LayoutShift | undefined; - - public constructor(private _reportAllChanges: boolean = false) { - if (!isNodeEnv() && global && global.performance && global.document) { - if (global.performance.mark) { - global.performance.mark('sentry-tracing-init'); - } - - this._trackCLS(); - this._trackLCP(); - this._trackFID(); - } - } - - /** Add performance related spans to a transaction */ - public addPerformanceEntries(transaction: Transaction): void { - if (!global || !global.performance || !global.performance.getEntries || !browserPerformanceTimeOrigin) { - // Gatekeeper if performance API not available - return; - } - - IS_DEBUG_BUILD && logger.log('[Tracing] Adding & adjusting spans using Performance API'); - - const timeOrigin = msToSec(browserPerformanceTimeOrigin); - - let responseStartTimestamp: number | undefined; - let requestStartTimestamp: number | undefined; - - global.performance - .getEntries() - .slice(this._performanceCursor) - .forEach((entry: Record) => { - const startTime = msToSec(entry.startTime as number); - const duration = msToSec(entry.duration as number); - - if (transaction.op === 'navigation' && timeOrigin + startTime < transaction.startTimestamp) { - return; - } - - switch (entry.entryType) { - case 'navigation': { - addNavigationSpans(transaction, entry, timeOrigin); - responseStartTimestamp = timeOrigin + msToSec(entry.responseStart as number); - requestStartTimestamp = timeOrigin + msToSec(entry.requestStart as number); - break; - } - case 'mark': - case 'paint': - case 'measure': { - const startTimestamp = addMeasureSpans(transaction, entry, startTime, duration, timeOrigin); - // capture web vitals - - const firstHidden = getVisibilityWatcher(); - // Only report if the page wasn't hidden prior to the web vital. - const shouldRecord = entry.startTime < firstHidden.firstHiddenTime; - - if (entry.name === 'first-paint' && shouldRecord) { - IS_DEBUG_BUILD && logger.log('[Measurements] Adding FP'); - this._measurements['fp'] = { value: entry.startTime, unit: 'millisecond' }; - this._measurements['mark.fp'] = { value: startTimestamp, unit: 'second' }; - } - - if (entry.name === 'first-contentful-paint' && shouldRecord) { - IS_DEBUG_BUILD && logger.log('[Measurements] Adding FCP'); - this._measurements['fcp'] = { value: entry.startTime, unit: 'millisecond' }; - this._measurements['mark.fcp'] = { value: startTimestamp, unit: 'second' }; - } - - break; - } - case 'resource': { - const resourceName = (entry.name as string).replace(global.location.origin, ''); - addResourceSpans(transaction, entry, resourceName, startTime, duration, timeOrigin); - break; - } - default: - // Ignore other entry types. - } - }); - - this._performanceCursor = Math.max(performance.getEntries().length - 1, 0); - - this._trackNavigator(transaction); - - // Measurements are only available for pageload transactions - if (transaction.op === 'pageload') { - // normalize applicable web vital values to be relative to transaction.startTimestamp - - const timeOrigin = msToSec(browserPerformanceTimeOrigin); - - // Generate TTFB (Time to First Byte), which measured as the time between the beginning of the transaction and the - // start of the response in milliseconds - if (typeof responseStartTimestamp === 'number') { - IS_DEBUG_BUILD && logger.log('[Measurements] Adding TTFB'); - this._measurements['ttfb'] = { - value: (responseStartTimestamp - transaction.startTimestamp) * 1000, - unit: 'millisecond', - }; - - if (typeof requestStartTimestamp === 'number' && requestStartTimestamp <= responseStartTimestamp) { - // Capture the time spent making the request and receiving the first byte of the response. - // This is the time between the start of the request and the start of the response in milliseconds. - this._measurements['ttfb.requestTime'] = { - value: (responseStartTimestamp - requestStartTimestamp) * 1000, - unit: 'second', - }; - } - } - - ['fcp', 'fp', 'lcp'].forEach(name => { - if (!this._measurements[name] || timeOrigin >= transaction.startTimestamp) { - return; - } - - // The web vitals, fcp, fp, lcp, and ttfb, all measure relative to timeOrigin. - // Unfortunately, timeOrigin is not captured within the transaction span data, so these web vitals will need - // to be adjusted to be relative to transaction.startTimestamp. - - const oldValue = this._measurements[name].value; - const measurementTimestamp = timeOrigin + msToSec(oldValue); - // normalizedValue should be in milliseconds - const normalizedValue = Math.abs((measurementTimestamp - transaction.startTimestamp) * 1000); - - const delta = normalizedValue - oldValue; - IS_DEBUG_BUILD && - logger.log(`[Measurements] Normalized ${name} from ${oldValue} to ${normalizedValue} (${delta})`); - - this._measurements[name].value = normalizedValue; - }); - - if (this._measurements['mark.fid'] && this._measurements['fid']) { - // create span for FID - - _startChild(transaction, { - description: 'first input delay', - endTimestamp: this._measurements['mark.fid'].value + msToSec(this._measurements['fid'].value), - op: 'web.vitals', - startTimestamp: this._measurements['mark.fid'].value, - }); - } - - // If FCP is not recorded we should not record the cls value - // according to the new definition of CLS. - if (!('fcp' in this._measurements)) { - delete this._measurements.cls; - } - - Object.keys(this._measurements).forEach(measurementName => { - transaction.setMeasurement( - measurementName, - this._measurements[measurementName].value, - this._measurements[measurementName].unit, - ); - }); - - tagMetricInfo(transaction, this._lcpEntry, this._clsEntry); - transaction.setTag('sentry_reportAllChanges', this._reportAllChanges); - } - } - - /** - * Capture the information of the user agent. - */ - private _trackNavigator(transaction: Transaction): void { - const navigator = global.navigator as null | (Navigator & NavigatorNetworkInformation & NavigatorDeviceMemory); - if (!navigator) { - return; - } - - // track network connectivity - const connection = navigator.connection; - if (connection) { - if (connection.effectiveType) { - transaction.setTag('effectiveConnectionType', connection.effectiveType); - } - - if (connection.type) { - transaction.setTag('connectionType', connection.type); - } - - if (isMeasurementValue(connection.rtt)) { - this._measurements['connection.rtt'] = { value: connection.rtt, unit: 'millisecond' }; - } - - if (isMeasurementValue(connection.downlink)) { - this._measurements['connection.downlink'] = { value: connection.downlink, unit: '' }; // unit is empty string for now, while relay doesn't support download speed units - } - } - - if (isMeasurementValue(navigator.deviceMemory)) { - transaction.setTag('deviceMemory', `${navigator.deviceMemory} GB`); - } - - if (isMeasurementValue(navigator.hardwareConcurrency)) { - transaction.setTag('hardwareConcurrency', String(navigator.hardwareConcurrency)); - } - } - - /** Starts tracking the Cumulative Layout Shift on the current page. */ - private _trackCLS(): void { - // See: - // https://web.dev/evolving-cls/ - // https://web.dev/cls-web-tooling/ - getCLS(metric => { - const entry = metric.entries.pop(); - if (!entry) { - return; - } - - IS_DEBUG_BUILD && logger.log('[Measurements] Adding CLS'); - this._measurements['cls'] = { value: metric.value, unit: 'millisecond' }; - this._clsEntry = entry as LayoutShift; - }); - } - - /** Starts tracking the Largest Contentful Paint on the current page. */ - private _trackLCP(): void { - getLCP(metric => { - const entry = metric.entries.pop(); - if (!entry) { - return; - } - - const timeOrigin = msToSec(browserPerformanceTimeOrigin as number); - const startTime = msToSec(entry.startTime); - IS_DEBUG_BUILD && logger.log('[Measurements] Adding LCP'); - this._measurements['lcp'] = { value: metric.value, unit: 'millisecond' }; - this._measurements['mark.lcp'] = { value: timeOrigin + startTime, unit: 'second' }; - this._lcpEntry = entry as LargestContentfulPaint; - }, this._reportAllChanges); - } - - /** Starts tracking the First Input Delay on the current page. */ - private _trackFID(): void { - getFID(metric => { - const entry = metric.entries.pop(); - if (!entry) { - return; - } - - const timeOrigin = msToSec(browserPerformanceTimeOrigin as number); - const startTime = msToSec(entry.startTime); - IS_DEBUG_BUILD && logger.log('[Measurements] Adding FID'); - this._measurements['fid'] = { value: metric.value, unit: 'millisecond' }; - this._measurements['mark.fid'] = { value: timeOrigin + startTime, unit: 'second' }; - }); - } -} - -/** Instrument navigation entries */ -function addNavigationSpans(transaction: Transaction, entry: Record, timeOrigin: number): void { - ['unloadEvent', 'redirect', 'domContentLoadedEvent', 'loadEvent', 'connect'].forEach(event => { - addPerformanceNavigationTiming(transaction, entry, event, timeOrigin); - }); - addPerformanceNavigationTiming(transaction, entry, 'secureConnection', timeOrigin, 'TLS/SSL', 'connectEnd'); - addPerformanceNavigationTiming(transaction, entry, 'fetch', timeOrigin, 'cache', 'domainLookupStart'); - addPerformanceNavigationTiming(transaction, entry, 'domainLookup', timeOrigin, 'DNS'); - addRequest(transaction, entry, timeOrigin); -} - -/** Create measure related spans */ -function addMeasureSpans( - transaction: Transaction, - entry: Record, - startTime: number, - duration: number, - timeOrigin: number, -): number { - const measureStartTimestamp = timeOrigin + startTime; - const measureEndTimestamp = measureStartTimestamp + duration; - - _startChild(transaction, { - description: entry.name as string, - endTimestamp: measureEndTimestamp, - op: entry.entryType as string, - startTimestamp: measureStartTimestamp, - }); - - return measureStartTimestamp; -} - -export interface ResourceEntry extends Record { - initiatorType?: string; - transferSize?: number; - encodedBodySize?: number; - decodedBodySize?: number; -} - -/** Create resource-related spans */ -export function addResourceSpans( - transaction: Transaction, - entry: ResourceEntry, - resourceName: string, - startTime: number, - duration: number, - timeOrigin: number, -): void { - // we already instrument based on fetch and xhr, so we don't need to - // duplicate spans here. - if (entry.initiatorType === 'xmlhttprequest' || entry.initiatorType === 'fetch') { - return; - } - - const data: Record = {}; - if ('transferSize' in entry) { - data['Transfer Size'] = entry.transferSize; - } - if ('encodedBodySize' in entry) { - data['Encoded Body Size'] = entry.encodedBodySize; - } - if ('decodedBodySize' in entry) { - data['Decoded Body Size'] = entry.decodedBodySize; - } - - const startTimestamp = timeOrigin + startTime; - const endTimestamp = startTimestamp + duration; - - _startChild(transaction, { - description: resourceName, - endTimestamp, - op: entry.initiatorType ? `resource.${entry.initiatorType}` : 'resource', - startTimestamp, - data, - }); -} - -/** Create performance navigation related spans */ -function addPerformanceNavigationTiming( - transaction: Transaction, - entry: Record, - event: string, - timeOrigin: number, - description?: string, - eventEnd?: string, -): void { - const end = eventEnd ? (entry[eventEnd] as number | undefined) : (entry[`${event}End`] as number | undefined); - const start = entry[`${event}Start`] as number | undefined; - if (!start || !end) { - return; - } - _startChild(transaction, { - op: 'browser', - description: description ?? event, - startTimestamp: timeOrigin + msToSec(start), - endTimestamp: timeOrigin + msToSec(end), - }); -} - -/** Create request and response related spans */ -function addRequest(transaction: Transaction, entry: Record, timeOrigin: number): void { - _startChild(transaction, { - op: 'browser', - description: 'request', - startTimestamp: timeOrigin + msToSec(entry.requestStart as number), - endTimestamp: timeOrigin + msToSec(entry.responseEnd as number), - }); - - _startChild(transaction, { - op: 'browser', - description: 'response', - startTimestamp: timeOrigin + msToSec(entry.responseStart as number), - endTimestamp: timeOrigin + msToSec(entry.responseEnd as number), - }); -} - -/** - * Helper function to start child on transactions. This function will make sure that the transaction will - * use the start timestamp of the created child span if it is earlier than the transactions actual - * start timestamp. - */ -export function _startChild(transaction: Transaction, { startTimestamp, ...ctx }: SpanContext): Span { - if (startTimestamp && transaction.startTimestamp > startTimestamp) { - transaction.startTimestamp = startTimestamp; - } - - return transaction.startChild({ - startTimestamp, - ...ctx, - }); -} - -/** - * Checks if a given value is a valid measurement value. - */ -function isMeasurementValue(value: unknown): value is number { - return typeof value === 'number' && isFinite(value); -} - -/** Add LCP / CLS data to transaction to allow debugging */ -function tagMetricInfo( - transaction: Transaction, - lcpEntry: MetricsInstrumentation['_lcpEntry'], - clsEntry: MetricsInstrumentation['_clsEntry'], -): void { - if (lcpEntry) { - IS_DEBUG_BUILD && logger.log('[Measurements] Adding LCP Data'); - - // Capture Properties of the LCP element that contributes to the LCP. - - if (lcpEntry.element) { - transaction.setTag('lcp.element', htmlTreeAsString(lcpEntry.element)); - } - - if (lcpEntry.id) { - transaction.setTag('lcp.id', lcpEntry.id); - } - - if (lcpEntry.url) { - // Trim URL to the first 200 characters. - transaction.setTag('lcp.url', lcpEntry.url.trim().slice(0, 200)); - } - - transaction.setTag('lcp.size', lcpEntry.size); - } - - // See: https://developer.mozilla.org/en-US/docs/Web/API/LayoutShift - if (clsEntry && clsEntry.sources) { - IS_DEBUG_BUILD && logger.log('[Measurements] Adding CLS Data'); - clsEntry.sources.forEach((source, index) => - transaction.setTag(`cls.source.${index + 1}`, htmlTreeAsString(source.node)), - ); - } -} diff --git a/packages/tracing/src/browser/metrics/index.ts b/packages/tracing/src/browser/metrics/index.ts new file mode 100644 index 000000000000..48438ab7b719 --- /dev/null +++ b/packages/tracing/src/browser/metrics/index.ts @@ -0,0 +1,421 @@ +/* eslint-disable max-lines */ +import { Measurements } from '@sentry/types'; +import { browserPerformanceTimeOrigin, getGlobalObject, htmlTreeAsString, isNodeEnv, logger } from '@sentry/utils'; + +import { IS_DEBUG_BUILD } from '../../flags'; +import { Transaction } from '../../transaction'; +import { msToSec } from '../../utils'; +import { getCLS, LayoutShift } from '../web-vitals/getCLS'; +import { getFID } from '../web-vitals/getFID'; +import { getLCP, LargestContentfulPaint } from '../web-vitals/getLCP'; +import { getVisibilityWatcher } from '../web-vitals/lib/getVisibilityWatcher'; +import { NavigatorDeviceMemory, NavigatorNetworkInformation } from '../web-vitals/types'; +import { _startChild, isMeasurementValue } from './utils'; + +const global = getGlobalObject(); + +function getBrowserPerformanceAPI(): false | Performance { + return !isNodeEnv() && global && global.document && global.performance; +} + +let _performanceCursor: number = 0; + +let _measurements: Measurements = {}; +let _lcpEntry: LargestContentfulPaint | undefined; +let _clsEntry: LayoutShift | undefined; + +/** + * Start tracking web vitals + */ +export function startTrackingWebVitals(reportAllChanges: boolean = false): void { + const performance = getBrowserPerformanceAPI(); + if (performance && browserPerformanceTimeOrigin) { + if (performance.mark) { + global.performance.mark('sentry-tracing-init'); + } + _trackCLS(); + _trackLCP(reportAllChanges); + _trackFID(); + } +} + +/** Starts tracking the Cumulative Layout Shift on the current page. */ +function _trackCLS(): void { + // See: + // https://web.dev/evolving-cls/ + // https://web.dev/cls-web-tooling/ + getCLS(metric => { + const entry = metric.entries.pop(); + if (!entry) { + return; + } + + IS_DEBUG_BUILD && logger.log('[Measurements] Adding CLS'); + _measurements['cls'] = { value: metric.value, unit: 'millisecond' }; + _clsEntry = entry as LayoutShift; + }); +} + +/** Starts tracking the Largest Contentful Paint on the current page. */ +function _trackLCP(reportAllChanges: boolean): void { + getLCP(metric => { + const entry = metric.entries.pop(); + if (!entry) { + return; + } + + const timeOrigin = msToSec(browserPerformanceTimeOrigin as number); + const startTime = msToSec(entry.startTime); + IS_DEBUG_BUILD && logger.log('[Measurements] Adding LCP'); + _measurements['lcp'] = { value: metric.value, unit: 'millisecond' }; + _measurements['mark.lcp'] = { value: timeOrigin + startTime, unit: 'second' }; + _lcpEntry = entry as LargestContentfulPaint; + }, reportAllChanges); +} + +/** Starts tracking the First Input Delay on the current page. */ +function _trackFID(): void { + getFID(metric => { + const entry = metric.entries.pop(); + if (!entry) { + return; + } + + const timeOrigin = msToSec(browserPerformanceTimeOrigin as number); + const startTime = msToSec(entry.startTime); + IS_DEBUG_BUILD && logger.log('[Measurements] Adding FID'); + _measurements['fid'] = { value: metric.value, unit: 'millisecond' }; + _measurements['mark.fid'] = { value: timeOrigin + startTime, unit: 'second' }; + }); +} + +/** Add performance related spans to a transaction */ +export function addPerformanceEntries(transaction: Transaction): void { + const performance = getBrowserPerformanceAPI(); + if (!performance || !global.performance.getEntries || !browserPerformanceTimeOrigin) { + // Gatekeeper if performance API not available + return; + } + + IS_DEBUG_BUILD && logger.log('[Tracing] Adding & adjusting spans using Performance API'); + const timeOrigin = msToSec(browserPerformanceTimeOrigin); + + const performanceEntries = performance.getEntries(); + + let responseStartTimestamp: number | undefined; + let requestStartTimestamp: number | undefined; + + // eslint-disable-next-line @typescript-eslint/no-explicit-any + performanceEntries.slice(_performanceCursor).forEach((entry: Record) => { + const startTime = msToSec(entry.startTime); + const duration = msToSec(entry.duration); + + if (transaction.op === 'navigation' && timeOrigin + startTime < transaction.startTimestamp) { + return; + } + + switch (entry.entryType) { + case 'navigation': { + _addNavigationSpans(transaction, entry, timeOrigin); + responseStartTimestamp = timeOrigin + msToSec(entry.responseStart); + requestStartTimestamp = timeOrigin + msToSec(entry.requestStart); + break; + } + case 'mark': + case 'paint': + case 'measure': { + const startTimestamp = _addMeasureSpans(transaction, entry, startTime, duration, timeOrigin); + + // capture web vitals + const firstHidden = getVisibilityWatcher(); + // Only report if the page wasn't hidden prior to the web vital. + const shouldRecord = entry.startTime < firstHidden.firstHiddenTime; + + if (entry.name === 'first-paint' && shouldRecord) { + IS_DEBUG_BUILD && logger.log('[Measurements] Adding FP'); + _measurements['fp'] = { value: entry.startTime, unit: 'millisecond' }; + _measurements['mark.fp'] = { value: startTimestamp, unit: 'second' }; + } + if (entry.name === 'first-contentful-paint' && shouldRecord) { + IS_DEBUG_BUILD && logger.log('[Measurements] Adding FCP'); + _measurements['fcp'] = { value: entry.startTime, unit: 'millisecond' }; + _measurements['mark.fcp'] = { value: startTimestamp, unit: 'second' }; + } + break; + } + case 'resource': { + const resourceName = (entry.name as string).replace(global.location.origin, ''); + _addResourceSpans(transaction, entry, resourceName, startTime, duration, timeOrigin); + break; + } + default: + // Ignore other entry types. + } + }); + + _performanceCursor = Math.max(performanceEntries.length - 1, 0); + + _trackNavigator(transaction); + + // Measurements are only available for pageload transactions + if (transaction.op === 'pageload') { + // Generate TTFB (Time to First Byte), which measured as the time between the beginning of the transaction and the + // start of the response in milliseconds + if (typeof responseStartTimestamp === 'number') { + IS_DEBUG_BUILD && logger.log('[Measurements] Adding TTFB'); + _measurements['ttfb'] = { + value: (responseStartTimestamp - transaction.startTimestamp) * 1000, + unit: 'millisecond', + }; + + if (typeof requestStartTimestamp === 'number' && requestStartTimestamp <= responseStartTimestamp) { + // Capture the time spent making the request and receiving the first byte of the response. + // This is the time between the start of the request and the start of the response in milliseconds. + _measurements['ttfb.requestTime'] = { + value: (responseStartTimestamp - requestStartTimestamp) * 1000, + unit: 'second', + }; + } + } + + ['fcp', 'fp', 'lcp'].forEach(name => { + if (!_measurements[name] || timeOrigin >= transaction.startTimestamp) { + return; + } + // The web vitals, fcp, fp, lcp, and ttfb, all measure relative to timeOrigin. + // Unfortunately, timeOrigin is not captured within the transaction span data, so these web vitals will need + // to be adjusted to be relative to transaction.startTimestamp. + const oldValue = _measurements[name].value; + const measurementTimestamp = timeOrigin + msToSec(oldValue); + + // normalizedValue should be in milliseconds + const normalizedValue = Math.abs((measurementTimestamp - transaction.startTimestamp) * 1000); + const delta = normalizedValue - oldValue; + + IS_DEBUG_BUILD && + logger.log(`[Measurements] Normalized ${name} from ${oldValue} to ${normalizedValue} (${delta})`); + _measurements[name].value = normalizedValue; + }); + + if (_measurements['mark.fid'] && _measurements['fid']) { + // create span for FID + _startChild(transaction, { + description: 'first input delay', + endTimestamp: _measurements['mark.fid'].value + msToSec(_measurements['fid'].value), + op: 'web.vitals', + startTimestamp: _measurements['mark.fid'].value, + }); + } + + // If FCP is not recorded we should not record the cls value + // according to the new definition of CLS. + if (!('fcp' in _measurements)) { + delete _measurements.cls; + } + + Object.keys(_measurements).forEach(measurementName => { + transaction.setMeasurement( + measurementName, + _measurements[measurementName].value, + _measurements[measurementName].unit, + ); + }); + + _tagMetricInfo(transaction); + } + + _lcpEntry = undefined; + _clsEntry = undefined; + _measurements = {}; +} + +/** Create measure related spans */ +export function _addMeasureSpans( + transaction: Transaction, + // eslint-disable-next-line @typescript-eslint/no-explicit-any + entry: Record, + startTime: number, + duration: number, + timeOrigin: number, +): number { + const measureStartTimestamp = timeOrigin + startTime; + const measureEndTimestamp = measureStartTimestamp + duration; + + _startChild(transaction, { + description: entry.name as string, + endTimestamp: measureEndTimestamp, + op: entry.entryType as string, + startTimestamp: measureStartTimestamp, + }); + + return measureStartTimestamp; +} + +/** Instrument navigation entries */ +// eslint-disable-next-line @typescript-eslint/no-explicit-any +function _addNavigationSpans(transaction: Transaction, entry: Record, timeOrigin: number): void { + ['unloadEvent', 'redirect', 'domContentLoadedEvent', 'loadEvent', 'connect'].forEach(event => { + _addPerformanceNavigationTiming(transaction, entry, event, timeOrigin); + }); + _addPerformanceNavigationTiming(transaction, entry, 'secureConnection', timeOrigin, 'TLS/SSL', 'connectEnd'); + _addPerformanceNavigationTiming(transaction, entry, 'fetch', timeOrigin, 'cache', 'domainLookupStart'); + _addPerformanceNavigationTiming(transaction, entry, 'domainLookup', timeOrigin, 'DNS'); + _addRequest(transaction, entry, timeOrigin); +} + +/** Create performance navigation related spans */ +function _addPerformanceNavigationTiming( + transaction: Transaction, + // eslint-disable-next-line @typescript-eslint/no-explicit-any + entry: Record, + event: string, + timeOrigin: number, + description?: string, + eventEnd?: string, +): void { + const end = eventEnd ? (entry[eventEnd] as number | undefined) : (entry[`${event}End`] as number | undefined); + const start = entry[`${event}Start`] as number | undefined; + if (!start || !end) { + return; + } + _startChild(transaction, { + op: 'browser', + description: description ?? event, + startTimestamp: timeOrigin + msToSec(start), + endTimestamp: timeOrigin + msToSec(end), + }); +} + +/** Create request and response related spans */ +// eslint-disable-next-line @typescript-eslint/no-explicit-any +function _addRequest(transaction: Transaction, entry: Record, timeOrigin: number): void { + _startChild(transaction, { + op: 'browser', + description: 'request', + startTimestamp: timeOrigin + msToSec(entry.requestStart as number), + endTimestamp: timeOrigin + msToSec(entry.responseEnd as number), + }); + + _startChild(transaction, { + op: 'browser', + description: 'response', + startTimestamp: timeOrigin + msToSec(entry.responseStart as number), + endTimestamp: timeOrigin + msToSec(entry.responseEnd as number), + }); +} + +export interface ResourceEntry extends Record { + initiatorType?: string; + transferSize?: number; + encodedBodySize?: number; + decodedBodySize?: number; +} + +/** Create resource-related spans */ +export function _addResourceSpans( + transaction: Transaction, + entry: ResourceEntry, + resourceName: string, + startTime: number, + duration: number, + timeOrigin: number, +): void { + // we already instrument based on fetch and xhr, so we don't need to + // duplicate spans here. + if (entry.initiatorType === 'xmlhttprequest' || entry.initiatorType === 'fetch') { + return; + } + + // eslint-disable-next-line @typescript-eslint/no-explicit-any + const data: Record = {}; + if ('transferSize' in entry) { + data['Transfer Size'] = entry.transferSize; + } + if ('encodedBodySize' in entry) { + data['Encoded Body Size'] = entry.encodedBodySize; + } + if ('decodedBodySize' in entry) { + data['Decoded Body Size'] = entry.decodedBodySize; + } + + const startTimestamp = timeOrigin + startTime; + const endTimestamp = startTimestamp + duration; + + _startChild(transaction, { + description: resourceName, + endTimestamp, + op: entry.initiatorType ? `resource.${entry.initiatorType}` : 'resource', + startTimestamp, + data, + }); +} + +/** + * Capture the information of the user agent. + */ +function _trackNavigator(transaction: Transaction): void { + const navigator = global.navigator as null | (Navigator & NavigatorNetworkInformation & NavigatorDeviceMemory); + if (!navigator) { + return; + } + + // track network connectivity + const connection = navigator.connection; + if (connection) { + if (connection.effectiveType) { + transaction.setTag('effectiveConnectionType', connection.effectiveType); + } + + if (connection.type) { + transaction.setTag('connectionType', connection.type); + } + + if (isMeasurementValue(connection.rtt)) { + _measurements['connection.rtt'] = { value: connection.rtt, unit: 'millisecond' }; + } + + if (isMeasurementValue(connection.downlink)) { + _measurements['connection.downlink'] = { value: connection.downlink, unit: '' }; // unit is empty string for now, while relay doesn't support download speed units + } + } + + if (isMeasurementValue(navigator.deviceMemory)) { + transaction.setTag('deviceMemory', `${navigator.deviceMemory} GB`); + } + + if (isMeasurementValue(navigator.hardwareConcurrency)) { + transaction.setTag('hardwareConcurrency', String(navigator.hardwareConcurrency)); + } +} + +/** Add LCP / CLS data to transaction to allow debugging */ +function _tagMetricInfo(transaction: Transaction): void { + if (_lcpEntry) { + IS_DEBUG_BUILD && logger.log('[Measurements] Adding LCP Data'); + + // Capture Properties of the LCP element that contributes to the LCP. + + if (_lcpEntry.element) { + transaction.setTag('lcp.element', htmlTreeAsString(_lcpEntry.element)); + } + + if (_lcpEntry.id) { + transaction.setTag('lcp.id', _lcpEntry.id); + } + + if (_lcpEntry.url) { + // Trim URL to the first 200 characters. + transaction.setTag('lcp.url', _lcpEntry.url.trim().slice(0, 200)); + } + + transaction.setTag('lcp.size', _lcpEntry.size); + } + + // See: https://developer.mozilla.org/en-US/docs/Web/API/LayoutShift + if (_clsEntry && _clsEntry.sources) { + IS_DEBUG_BUILD && logger.log('[Measurements] Adding CLS Data'); + _clsEntry.sources.forEach((source, index) => + transaction.setTag(`cls.source.${index + 1}`, htmlTreeAsString(source.node)), + ); + } +} diff --git a/packages/tracing/src/browser/metrics/utils.ts b/packages/tracing/src/browser/metrics/utils.ts new file mode 100644 index 000000000000..4894dbd3ec8e --- /dev/null +++ b/packages/tracing/src/browser/metrics/utils.ts @@ -0,0 +1,26 @@ +import { Span, SpanContext } from '@sentry/types'; + +import { Transaction } from '../../transaction'; + +/** + * Checks if a given value is a valid measurement value. + */ +export function isMeasurementValue(value: unknown): value is number { + return typeof value === 'number' && isFinite(value); +} + +/** + * Helper function to start child on transactions. This function will make sure that the transaction will + * use the start timestamp of the created child span if it is earlier than the transactions actual + * start timestamp. + */ +export function _startChild(transaction: Transaction, { startTimestamp, ...ctx }: SpanContext): Span { + if (startTimestamp && transaction.startTimestamp > startTimestamp) { + transaction.startTimestamp = startTimestamp; + } + + return transaction.startChild({ + startTimestamp, + ...ctx, + }); +} diff --git a/packages/tracing/test/browser/browsertracing.test.ts b/packages/tracing/test/browser/browsertracing.test.ts index 0d5265522406..6fbcc5b6f6d8 100644 --- a/packages/tracing/test/browser/browsertracing.test.ts +++ b/packages/tracing/test/browser/browsertracing.test.ts @@ -10,7 +10,6 @@ import { extractTraceDataFromMetaTags, getMetaContent, } from '../../src/browser/browsertracing'; -import { MetricsInstrumentation } from '../../src/browser/metrics'; import { defaultRequestInstrumentationOptions } from '../../src/browser/request'; import { instrumentRoutingWithDefaults } from '../../src/browser/router'; import * as hubExtensions from '../../src/hubextensions'; @@ -510,29 +509,4 @@ describe('BrowserTracing', () => { ); }); }); - - describe('metrics', () => { - beforeEach(() => { - // @ts-ignore mock clear - MetricsInstrumentation.mockClear(); - }); - - it('creates metrics instrumentation', () => { - createBrowserTracing(true, {}); - - expect(MetricsInstrumentation).toHaveBeenCalledTimes(1); - expect(MetricsInstrumentation).toHaveBeenLastCalledWith(undefined); - }); - - it('creates metrics instrumentation with custom options', () => { - createBrowserTracing(true, { - _metricOptions: { - _reportAllChanges: true, - }, - }); - - expect(MetricsInstrumentation).toHaveBeenCalledTimes(1); - expect(MetricsInstrumentation).toHaveBeenLastCalledWith(true); - }); - }); }); diff --git a/packages/tracing/test/browser/metrics.test.ts b/packages/tracing/test/browser/metrics.test.ts deleted file mode 100644 index eb79ff30df93..000000000000 --- a/packages/tracing/test/browser/metrics.test.ts +++ /dev/null @@ -1,221 +0,0 @@ -import { Span, Transaction } from '../../src'; -import { _startChild, addResourceSpans, MetricsInstrumentation, ResourceEntry } from '../../src/browser/metrics'; -import { addDOMPropertiesToGlobal } from '../testutils'; - -// eslint-disable-next-line @typescript-eslint/no-explicit-any, no-var -declare var global: any; - -describe('_startChild()', () => { - it('creates a span with given properties', () => { - const transaction = new Transaction({ name: 'test' }); - const span = _startChild(transaction, { - description: 'evaluation', - op: 'script', - }); - - expect(span).toBeInstanceOf(Span); - expect(span.description).toBe('evaluation'); - expect(span.op).toBe('script'); - }); - - it('adjusts the start timestamp if child span starts before transaction', () => { - const transaction = new Transaction({ name: 'test', startTimestamp: 123 }); - const span = _startChild(transaction, { - description: 'script.js', - op: 'resource', - startTimestamp: 100, - }); - - expect(transaction.startTimestamp).toEqual(span.startTimestamp); - expect(transaction.startTimestamp).toEqual(100); - }); - - it('does not adjust start timestamp if child span starts after transaction', () => { - const transaction = new Transaction({ name: 'test', startTimestamp: 123 }); - const span = _startChild(transaction, { - description: 'script.js', - op: 'resource', - startTimestamp: 150, - }); - - expect(transaction.startTimestamp).not.toEqual(span.startTimestamp); - expect(transaction.startTimestamp).toEqual(123); - }); -}); - -describe('addResourceSpans', () => { - const transaction = new Transaction({ name: 'hello' }); - beforeEach(() => { - transaction.startChild = jest.fn(); - }); - - // We already track xhr, we don't need to use - it('does not create spans for xmlhttprequest', () => { - const entry: ResourceEntry = { - initiatorType: 'xmlhttprequest', - transferSize: 256, - encodedBodySize: 256, - decodedBodySize: 256, - }; - addResourceSpans(transaction, entry, '/assets/to/me', 123, 456, 100); - - // eslint-disable-next-line @typescript-eslint/unbound-method - expect(transaction.startChild).toHaveBeenCalledTimes(0); - }); - - it('does not create spans for fetch', () => { - const entry: ResourceEntry = { - initiatorType: 'fetch', - transferSize: 256, - encodedBodySize: 256, - decodedBodySize: 256, - }; - addResourceSpans(transaction, entry, '/assets/to/me', 123, 456, 100); - - // eslint-disable-next-line @typescript-eslint/unbound-method - expect(transaction.startChild).toHaveBeenCalledTimes(0); - }); - - it('creates spans for resource spans', () => { - const entry: ResourceEntry = { - initiatorType: 'css', - transferSize: 256, - encodedBodySize: 456, - decodedBodySize: 593, - }; - - const timeOrigin = 100; - const startTime = 23; - const duration = 356; - - addResourceSpans(transaction, entry, '/assets/to/css', startTime, duration, timeOrigin); - - // eslint-disable-next-line @typescript-eslint/unbound-method - expect(transaction.startChild).toHaveBeenCalledTimes(1); - // eslint-disable-next-line @typescript-eslint/unbound-method - expect(transaction.startChild).toHaveBeenLastCalledWith({ - data: { - ['Decoded Body Size']: entry.decodedBodySize, - ['Encoded Body Size']: entry.encodedBodySize, - ['Transfer Size']: entry.transferSize, - }, - description: '/assets/to/css', - endTimestamp: timeOrigin + startTime + duration, - op: 'resource.css', - startTimestamp: timeOrigin + startTime, - }); - }); - - it('creates a variety of resource spans', () => { - const table = [ - { - initiatorType: undefined, - op: 'resource', - }, - { - initiatorType: '', - op: 'resource', - }, - { - initiatorType: 'css', - op: 'resource.css', - }, - { - initiatorType: 'image', - op: 'resource.image', - }, - { - initiatorType: 'script', - op: 'resource.script', - }, - ]; - - for (const { initiatorType, op } of table) { - const entry: ResourceEntry = { - initiatorType, - }; - addResourceSpans(transaction, entry, '/assets/to/me', 123, 234, 465); - - // eslint-disable-next-line @typescript-eslint/unbound-method - expect(transaction.startChild).toHaveBeenLastCalledWith( - expect.objectContaining({ - op, - }), - ); - } - }); - - it('allows for enter size of 0', () => { - const entry: ResourceEntry = { - initiatorType: 'css', - transferSize: 0, - encodedBodySize: 0, - decodedBodySize: 0, - }; - - addResourceSpans(transaction, entry, '/assets/to/css', 100, 23, 345); - - // eslint-disable-next-line @typescript-eslint/unbound-method - expect(transaction.startChild).toHaveBeenCalledTimes(1); - // eslint-disable-next-line @typescript-eslint/unbound-method - expect(transaction.startChild).toHaveBeenLastCalledWith( - expect.objectContaining({ - data: { - ['Decoded Body Size']: entry.decodedBodySize, - ['Encoded Body Size']: entry.encodedBodySize, - ['Transfer Size']: entry.transferSize, - }, - }), - ); - }); -}); - -describe('MetricsInstrumentation', () => { - afterEach(() => { - jest.clearAllMocks(); - }); - - it('does not initialize trackers when on node', () => { - const trackers = ['_trackCLS', '_trackLCP', '_trackFID'].map(tracker => - jest.spyOn(MetricsInstrumentation.prototype as any, tracker), - ); - - new MetricsInstrumentation(); - - trackers.forEach(tracker => expect(tracker).not.toBeCalled()); - }); - - it('initializes trackers when not on node and `global.performance` and `global.document` are available.', () => { - addDOMPropertiesToGlobal(['performance', 'document', 'addEventListener', 'window']); - - const backup = global.process; - global.process = undefined; - - const trackers = ['_trackCLS', '_trackLCP', '_trackFID'].map(tracker => - jest.spyOn(MetricsInstrumentation.prototype as any, tracker), - ); - new MetricsInstrumentation(); - global.process = backup; - - trackers.forEach(tracker => expect(tracker).toBeCalled()); - }); - - it('does not initialize trackers when not on node but `global.document` is not available (in worker)', () => { - // window not necessary for this test, but it is here to exercise that it is absence of document that is checked - addDOMPropertiesToGlobal(['performance', 'addEventListener', 'window']); - - const processBackup = global.process; - global.process = undefined; - const documentBackup = global.document; - global.document = undefined; - - const trackers = ['_trackCLS', '_trackLCP', '_trackFID'].map(tracker => - jest.spyOn(MetricsInstrumentation.prototype as any, tracker), - ); - new MetricsInstrumentation(); - global.process = processBackup; - global.document = documentBackup; - - trackers.forEach(tracker => expect(tracker).not.toBeCalled()); - }); -}); diff --git a/packages/tracing/test/browser/metrics/index.test.ts b/packages/tracing/test/browser/metrics/index.test.ts new file mode 100644 index 000000000000..b47ebd92a8f3 --- /dev/null +++ b/packages/tracing/test/browser/metrics/index.test.ts @@ -0,0 +1,162 @@ +import { Transaction } from '../../../src'; +import { _addMeasureSpans, _addResourceSpans, ResourceEntry } from '../../../src/browser/metrics'; + +describe('_addMeasureSpans', () => { + const transaction = new Transaction({ op: 'pageload', name: '/' }); + beforeEach(() => { + transaction.startChild = jest.fn(); + }); + + it('adds measure spans to a transaction', () => { + const entry: Omit = { + entryType: 'measure', + name: 'measure-1', + duration: 10, + startTime: 12, + }; + + const timeOrigin = 100; + const startTime = 23; + const duration = 356; + + // eslint-disable-next-line @typescript-eslint/unbound-method + expect(transaction.startChild).toHaveBeenCalledTimes(0); + _addMeasureSpans(transaction, entry, startTime, duration, timeOrigin); + // eslint-disable-next-line @typescript-eslint/unbound-method + expect(transaction.startChild).toHaveBeenCalledTimes(1); + // eslint-disable-next-line @typescript-eslint/unbound-method + expect(transaction.startChild).toHaveBeenLastCalledWith({ + description: 'measure-1', + startTimestamp: timeOrigin + startTime, + endTimestamp: timeOrigin + startTime + duration, + op: 'measure', + }); + }); +}); + +describe('_addResourceSpans', () => { + const transaction = new Transaction({ op: 'pageload', name: '/' }); + beforeEach(() => { + transaction.startChild = jest.fn(); + }); + + // We already track xhr, we don't need to use + it('does not create spans for xmlhttprequest', () => { + const entry: ResourceEntry = { + initiatorType: 'xmlhttprequest', + transferSize: 256, + encodedBodySize: 256, + decodedBodySize: 256, + }; + _addResourceSpans(transaction, entry, '/assets/to/me', 123, 456, 100); + + // eslint-disable-next-line @typescript-eslint/unbound-method + expect(transaction.startChild).toHaveBeenCalledTimes(0); + }); + + it('does not create spans for fetch', () => { + const entry: ResourceEntry = { + initiatorType: 'fetch', + transferSize: 256, + encodedBodySize: 256, + decodedBodySize: 256, + }; + _addResourceSpans(transaction, entry, '/assets/to/me', 123, 456, 100); + + // eslint-disable-next-line @typescript-eslint/unbound-method + expect(transaction.startChild).toHaveBeenCalledTimes(0); + }); + + it('creates spans for resource spans', () => { + const entry: ResourceEntry = { + initiatorType: 'css', + transferSize: 256, + encodedBodySize: 456, + decodedBodySize: 593, + }; + + const timeOrigin = 100; + const startTime = 23; + const duration = 356; + + _addResourceSpans(transaction, entry, '/assets/to/css', startTime, duration, timeOrigin); + + // eslint-disable-next-line @typescript-eslint/unbound-method + expect(transaction.startChild).toHaveBeenCalledTimes(1); + // eslint-disable-next-line @typescript-eslint/unbound-method + expect(transaction.startChild).toHaveBeenLastCalledWith({ + data: { + ['Decoded Body Size']: entry.decodedBodySize, + ['Encoded Body Size']: entry.encodedBodySize, + ['Transfer Size']: entry.transferSize, + }, + description: '/assets/to/css', + endTimestamp: timeOrigin + startTime + duration, + op: 'resource.css', + startTimestamp: timeOrigin + startTime, + }); + }); + + it('creates a variety of resource spans', () => { + const table = [ + { + initiatorType: undefined, + op: 'resource', + }, + { + initiatorType: '', + op: 'resource', + }, + { + initiatorType: 'css', + op: 'resource.css', + }, + { + initiatorType: 'image', + op: 'resource.image', + }, + { + initiatorType: 'script', + op: 'resource.script', + }, + ]; + + for (const { initiatorType, op } of table) { + const entry: ResourceEntry = { + initiatorType, + }; + _addResourceSpans(transaction, entry, '/assets/to/me', 123, 234, 465); + + // eslint-disable-next-line @typescript-eslint/unbound-method + expect(transaction.startChild).toHaveBeenLastCalledWith( + expect.objectContaining({ + op, + }), + ); + } + }); + + it('allows for enter size of 0', () => { + const entry: ResourceEntry = { + initiatorType: 'css', + transferSize: 0, + encodedBodySize: 0, + decodedBodySize: 0, + }; + + _addResourceSpans(transaction, entry, '/assets/to/css', 100, 23, 345); + + // eslint-disable-next-line @typescript-eslint/unbound-method + expect(transaction.startChild).toHaveBeenCalledTimes(1); + // eslint-disable-next-line @typescript-eslint/unbound-method + expect(transaction.startChild).toHaveBeenLastCalledWith( + expect.objectContaining({ + data: { + ['Decoded Body Size']: entry.decodedBodySize, + ['Encoded Body Size']: entry.encodedBodySize, + ['Transfer Size']: entry.transferSize, + }, + }), + ); + }); +}); diff --git a/packages/tracing/test/browser/metrics/utils.test.ts b/packages/tracing/test/browser/metrics/utils.test.ts new file mode 100644 index 000000000000..25f03c11af0b --- /dev/null +++ b/packages/tracing/test/browser/metrics/utils.test.ts @@ -0,0 +1,40 @@ +import { Span, Transaction } from '../../../src'; +import { _startChild } from '../../../src/browser/metrics/utils'; + +describe('_startChild()', () => { + it('creates a span with given properties', () => { + const transaction = new Transaction({ name: 'test' }); + const span = _startChild(transaction, { + description: 'evaluation', + op: 'script', + }); + + expect(span).toBeInstanceOf(Span); + expect(span.description).toBe('evaluation'); + expect(span.op).toBe('script'); + }); + + it('adjusts the start timestamp if child span starts before transaction', () => { + const transaction = new Transaction({ name: 'test', startTimestamp: 123 }); + const span = _startChild(transaction, { + description: 'script.js', + op: 'resource', + startTimestamp: 100, + }); + + expect(transaction.startTimestamp).toEqual(span.startTimestamp); + expect(transaction.startTimestamp).toEqual(100); + }); + + it('does not adjust start timestamp if child span starts after transaction', () => { + const transaction = new Transaction({ name: 'test', startTimestamp: 123 }); + const span = _startChild(transaction, { + description: 'script.js', + op: 'resource', + startTimestamp: 150, + }); + + expect(transaction.startTimestamp).not.toEqual(span.startTimestamp); + expect(transaction.startTimestamp).toEqual(123); + }); +}); From 5a782c3c17aa58c9d4537d8d8ed831d6c0473e08 Mon Sep 17 00:00:00 2001 From: Anton Pirker Date: Mon, 23 May 2022 16:12:23 +0200 Subject: [PATCH 188/204] Deactivated deployment of Lambda Layer until new Lambda Extension is production ready (#5148) Deactivated deployment of Lambda Layer until new Lambda Extension is production ready. With this we make sure, that we do not break existing Lambda Layer users. --- .craft.yml | 24 ++++++++++++++---------- 1 file changed, 14 insertions(+), 10 deletions(-) diff --git a/.craft.yml b/.craft.yml index 44d48ec03684..c28e4777a220 100644 --- a/.craft.yml +++ b/.craft.yml @@ -2,16 +2,20 @@ minVersion: '0.23.1' changelogPolicy: simple preReleaseCommand: bash scripts/craft-pre-release.sh targets: - - name: aws-lambda-layer - includeNames: /^sentry-node-serverless-\d+.\d+.\d+(-(beta|alpha)\.\d+)?\.zip$/ - layerName: SentryNodeServerlessSDK - compatibleRuntimes: - - name: node - versions: - - nodejs10.x - - nodejs12.x - - nodejs14.x - license: MIT + # + # Deactivated for now. This needs to be reactivated if the new Sentry Lambda Extension is deployed to production. + # (ask Anton Pirker if you have questions.) + # + # - name: aws-lambda-layer + # includeNames: /^sentry-node-serverless-\d+.\d+.\d+(-(beta|alpha)\.\d+)?\.zip$/ + # layerName: SentryNodeServerlessSDK + # compatibleRuntimes: + # - name: node + # versions: + # - nodejs10.x + # - nodejs12.x + # - nodejs14.x + # license: MIT - name: gcs includeNames: /.*\.js.*$/ bucket: sentry-js-sdk From 06930f5b085f03c4457ed854cfb0f3494cb01dc8 Mon Sep 17 00:00:00 2001 From: Abhijeet Prasad Date: Mon, 23 May 2022 16:13:19 +0200 Subject: [PATCH 189/204] meta: Update 7.0.0-beta.2 CHANGELOG (#5137) Co-authored-by: Lukas Stracke --- CHANGELOG.md | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 26742610a78e..d5de078c2c39 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,9 +6,16 @@ ## 7.0.0-beta.2 +- **(breaking)** feat(tracing): Add empty baggage header propagation to outgoing requests (#5133) +- feat: Add attachments API (#5004) +- feat(core): Send Baggage in Envelope Header (#5104) +- feat(utils): Introduce Baggage API (#5066) - fix(build): Fix express import in `gcpfunction` (#5097) - fix(ember): Export sha hashes of injected scripts (#5089) +- ref(build): Use sucrase for es6 bundles (#5111) - ref(serverless): Do not throw on flush error (#5090) +- ref(serverless): Point DSN to relay in lambda extension (#5126) +- ref(tracing): Rename baggage env header to trace (#5128) ## 7.0.0-beta.1 From c146db5ed64148f2cd63ed72dbb99938e324eb76 Mon Sep 17 00:00:00 2001 From: getsentry-bot Date: Mon, 23 May 2022 14:37:08 +0000 Subject: [PATCH 190/204] release: 7.0.0-beta.2 --- lerna.json | 2 +- packages/angular/package.json | 8 ++++---- packages/browser/package.json | 8 ++++---- packages/core/package.json | 8 ++++---- packages/core/src/version.ts | 2 +- packages/ember/package.json | 10 +++++----- packages/eslint-config-sdk/package.json | 6 +++--- packages/eslint-plugin-sdk/package.json | 2 +- packages/gatsby/package.json | 10 +++++----- packages/hub/package.json | 6 +++--- packages/integration-tests/package.json | 2 +- packages/integrations/package.json | 6 +++--- packages/nextjs/package.json | 18 +++++++++--------- packages/node-integration-tests/package.json | 2 +- packages/node/package.json | 10 +++++----- packages/react/package.json | 8 ++++---- packages/serverless/package.json | 10 +++++----- packages/tracing/package.json | 10 +++++----- packages/types/package.json | 2 +- packages/typescript/package.json | 2 +- packages/utils/package.json | 4 ++-- packages/vue/package.json | 10 +++++----- packages/wasm/package.json | 8 ++++---- 23 files changed, 77 insertions(+), 77 deletions(-) diff --git a/lerna.json b/lerna.json index a36faf95be31..3cb0ed37c32a 100644 --- a/lerna.json +++ b/lerna.json @@ -1,6 +1,6 @@ { "lerna": "3.4.0", - "version": "7.0.0-beta.1", + "version": "7.0.0-beta.2", "packages": "packages/*", "npmClient": "yarn", "useWorkspaces": true diff --git a/packages/angular/package.json b/packages/angular/package.json index e3fcb5dd0a93..5d2ac98b5db5 100644 --- a/packages/angular/package.json +++ b/packages/angular/package.json @@ -1,6 +1,6 @@ { "name": "@sentry/angular", - "version": "7.0.0-beta.1", + "version": "7.0.0-beta.2", "description": "Official Sentry SDK for Angular", "repository": "git://github.com/getsentry/sentry-javascript.git", "homepage": "https://github.com/getsentry/sentry-javascript/tree/master/packages/angular", @@ -21,9 +21,9 @@ "rxjs": "^6.5.5 || ^7.x" }, "dependencies": { - "@sentry/browser": "7.0.0-beta.1", - "@sentry/types": "7.0.0-beta.1", - "@sentry/utils": "7.0.0-beta.1", + "@sentry/browser": "7.0.0-beta.2", + "@sentry/types": "7.0.0-beta.2", + "@sentry/utils": "7.0.0-beta.2", "tslib": "^2.0.0" }, "devDependencies": { diff --git a/packages/browser/package.json b/packages/browser/package.json index 487294870c5b..db3830e9a94e 100644 --- a/packages/browser/package.json +++ b/packages/browser/package.json @@ -1,6 +1,6 @@ { "name": "@sentry/browser", - "version": "7.0.0-beta.1", + "version": "7.0.0-beta.2", "description": "Official Sentry SDK for browsers", "repository": "git://github.com/getsentry/sentry-javascript.git", "homepage": "https://github.com/getsentry/sentry-javascript/tree/master/packages/browser", @@ -16,9 +16,9 @@ "access": "public" }, "dependencies": { - "@sentry/core": "7.0.0-beta.1", - "@sentry/types": "7.0.0-beta.1", - "@sentry/utils": "7.0.0-beta.1", + "@sentry/core": "7.0.0-beta.2", + "@sentry/types": "7.0.0-beta.2", + "@sentry/utils": "7.0.0-beta.2", "tslib": "^1.9.3" }, "devDependencies": { diff --git a/packages/core/package.json b/packages/core/package.json index e2af28fcf4f9..575a10f48d5d 100644 --- a/packages/core/package.json +++ b/packages/core/package.json @@ -1,6 +1,6 @@ { "name": "@sentry/core", - "version": "7.0.0-beta.1", + "version": "7.0.0-beta.2", "description": "Base implementation for all Sentry JavaScript SDKs", "repository": "git://github.com/getsentry/sentry-javascript.git", "homepage": "https://github.com/getsentry/sentry-javascript/tree/master/packages/core", @@ -16,9 +16,9 @@ "access": "public" }, "dependencies": { - "@sentry/hub": "7.0.0-beta.1", - "@sentry/types": "7.0.0-beta.1", - "@sentry/utils": "7.0.0-beta.1", + "@sentry/hub": "7.0.0-beta.2", + "@sentry/types": "7.0.0-beta.2", + "@sentry/utils": "7.0.0-beta.2", "tslib": "^1.9.3" }, "scripts": { diff --git a/packages/core/src/version.ts b/packages/core/src/version.ts index ab3d7f550375..003c7862c691 100644 --- a/packages/core/src/version.ts +++ b/packages/core/src/version.ts @@ -1 +1 @@ -export const SDK_VERSION = '7.0.0-beta.1'; +export const SDK_VERSION = '7.0.0-beta.2'; diff --git a/packages/ember/package.json b/packages/ember/package.json index 40c00646e7d6..113449308fc4 100644 --- a/packages/ember/package.json +++ b/packages/ember/package.json @@ -1,6 +1,6 @@ { "name": "@sentry/ember", - "version": "7.0.0-beta.1", + "version": "7.0.0-beta.2", "description": "Official Sentry SDK for Ember.js", "repository": "git://github.com/getsentry/sentry-javascript.git", "homepage": "https://github.com/getsentry/sentry-javascript/tree/master/packages/ember", @@ -30,10 +30,10 @@ }, "dependencies": { "@embroider/macros": "~0.47.2", - "@sentry/browser": "7.0.0-beta.1", - "@sentry/tracing": "7.0.0-beta.1", - "@sentry/types": "7.0.0-beta.1", - "@sentry/utils": "7.0.0-beta.1", + "@sentry/browser": "7.0.0-beta.2", + "@sentry/tracing": "7.0.0-beta.2", + "@sentry/types": "7.0.0-beta.2", + "@sentry/utils": "7.0.0-beta.2", "ember-auto-import": "~1.12.1 || ~2.2.0", "ember-cli-babel": "~7.26.6", "ember-cli-htmlbars": "^6.0.1", diff --git a/packages/eslint-config-sdk/package.json b/packages/eslint-config-sdk/package.json index 34b639a010a9..0fe408ad74de 100644 --- a/packages/eslint-config-sdk/package.json +++ b/packages/eslint-config-sdk/package.json @@ -1,6 +1,6 @@ { "name": "@sentry-internal/eslint-config-sdk", - "version": "7.0.0-beta.1", + "version": "7.0.0-beta.2", "description": "Official Sentry SDK eslint config", "repository": "git://github.com/getsentry/sentry-javascript.git", "homepage": "https://github.com/getsentry/sentry-javascript/tree/master/packages/eslint-config-sdk", @@ -19,8 +19,8 @@ "access": "public" }, "dependencies": { - "@sentry-internal/eslint-plugin-sdk": "7.0.0-beta.1", - "@sentry-internal/typescript": "7.0.0-beta.1", + "@sentry-internal/eslint-plugin-sdk": "7.0.0-beta.2", + "@sentry-internal/typescript": "7.0.0-beta.2", "@typescript-eslint/eslint-plugin": "^3.9.0", "@typescript-eslint/parser": "^3.9.0", "eslint-config-prettier": "^6.11.0", diff --git a/packages/eslint-plugin-sdk/package.json b/packages/eslint-plugin-sdk/package.json index 00b2bdbc526b..514e9f742729 100644 --- a/packages/eslint-plugin-sdk/package.json +++ b/packages/eslint-plugin-sdk/package.json @@ -1,6 +1,6 @@ { "name": "@sentry-internal/eslint-plugin-sdk", - "version": "7.0.0-beta.1", + "version": "7.0.0-beta.2", "description": "Official Sentry SDK eslint plugin", "repository": "git://github.com/getsentry/sentry-javascript.git", "homepage": "https://github.com/getsentry/sentry-javascript/tree/master/packages/eslint-plugin-sdk", diff --git a/packages/gatsby/package.json b/packages/gatsby/package.json index 997bfe69ecbe..eddf17c56b55 100644 --- a/packages/gatsby/package.json +++ b/packages/gatsby/package.json @@ -1,6 +1,6 @@ { "name": "@sentry/gatsby", - "version": "7.0.0-beta.1", + "version": "7.0.0-beta.2", "description": "Official Sentry SDK for Gatsby.js", "repository": "git://github.com/getsentry/sentry-javascript.git", "homepage": "https://github.com/getsentry/sentry-javascript/tree/master/packages/gatsby", @@ -20,10 +20,10 @@ "access": "public" }, "dependencies": { - "@sentry/react": "7.0.0-beta.1", - "@sentry/tracing": "7.0.0-beta.1", - "@sentry/types": "7.0.0-beta.1", - "@sentry/utils": "7.0.0-beta.1", + "@sentry/react": "7.0.0-beta.2", + "@sentry/tracing": "7.0.0-beta.2", + "@sentry/types": "7.0.0-beta.2", + "@sentry/utils": "7.0.0-beta.2", "@sentry/webpack-plugin": "1.18.9" }, "peerDependencies": { diff --git a/packages/hub/package.json b/packages/hub/package.json index a13999512d83..c621fa01733f 100644 --- a/packages/hub/package.json +++ b/packages/hub/package.json @@ -1,6 +1,6 @@ { "name": "@sentry/hub", - "version": "7.0.0-beta.1", + "version": "7.0.0-beta.2", "description": "Sentry hub which handles global state managment.", "repository": "git://github.com/getsentry/sentry-javascript.git", "homepage": "https://github.com/getsentry/sentry-javascript/tree/master/packages/hub", @@ -16,8 +16,8 @@ "access": "public" }, "dependencies": { - "@sentry/types": "7.0.0-beta.1", - "@sentry/utils": "7.0.0-beta.1", + "@sentry/types": "7.0.0-beta.2", + "@sentry/utils": "7.0.0-beta.2", "tslib": "^1.9.3" }, "scripts": { diff --git a/packages/integration-tests/package.json b/packages/integration-tests/package.json index 39506bcd8272..7eebe9ebfa6a 100644 --- a/packages/integration-tests/package.json +++ b/packages/integration-tests/package.json @@ -1,6 +1,6 @@ { "name": "@sentry-internal/browser-integration-tests", - "version": "7.0.0-beta.1", + "version": "7.0.0-beta.2", "main": "index.js", "license": "MIT", "engines": { diff --git a/packages/integrations/package.json b/packages/integrations/package.json index ea1e050dad5a..e6ed4bd9266c 100644 --- a/packages/integrations/package.json +++ b/packages/integrations/package.json @@ -1,6 +1,6 @@ { "name": "@sentry/integrations", - "version": "7.0.0-beta.1", + "version": "7.0.0-beta.2", "description": "Pluggable integrations that can be used to enhance JS SDKs", "repository": "git://github.com/getsentry/sentry-javascript.git", "homepage": "https://github.com/getsentry/sentry-javascript/tree/master/packages/integrations", @@ -16,8 +16,8 @@ "module": "build/npm/esm/index.js", "types": "build/npm/types/index.d.ts", "dependencies": { - "@sentry/types": "7.0.0-beta.1", - "@sentry/utils": "7.0.0-beta.1", + "@sentry/types": "7.0.0-beta.2", + "@sentry/utils": "7.0.0-beta.2", "localforage": "^1.8.1", "tslib": "^1.9.3" }, diff --git a/packages/nextjs/package.json b/packages/nextjs/package.json index 84eb372adf58..c395afee3164 100644 --- a/packages/nextjs/package.json +++ b/packages/nextjs/package.json @@ -1,6 +1,6 @@ { "name": "@sentry/nextjs", - "version": "7.0.0-beta.1", + "version": "7.0.0-beta.2", "description": "Official Sentry SDK for Next.js", "repository": "git://github.com/getsentry/sentry-javascript.git", "homepage": "https://github.com/getsentry/sentry-javascript/tree/master/packages/nextjs", @@ -17,14 +17,14 @@ "access": "public" }, "dependencies": { - "@sentry/core": "7.0.0-beta.1", - "@sentry/hub": "7.0.0-beta.1", - "@sentry/integrations": "7.0.0-beta.1", - "@sentry/node": "7.0.0-beta.1", - "@sentry/react": "7.0.0-beta.1", - "@sentry/tracing": "7.0.0-beta.1", - "@sentry/types": "7.0.0-beta.1", - "@sentry/utils": "7.0.0-beta.1", + "@sentry/core": "7.0.0-beta.2", + "@sentry/hub": "7.0.0-beta.2", + "@sentry/integrations": "7.0.0-beta.2", + "@sentry/node": "7.0.0-beta.2", + "@sentry/react": "7.0.0-beta.2", + "@sentry/tracing": "7.0.0-beta.2", + "@sentry/types": "7.0.0-beta.2", + "@sentry/utils": "7.0.0-beta.2", "@sentry/webpack-plugin": "1.18.9", "tslib": "^1.9.3" }, diff --git a/packages/node-integration-tests/package.json b/packages/node-integration-tests/package.json index 17b03145ae1a..20257a5e8b03 100644 --- a/packages/node-integration-tests/package.json +++ b/packages/node-integration-tests/package.json @@ -1,6 +1,6 @@ { "name": "@sentry-internal/node-integration-tests", - "version": "7.0.0-beta.1", + "version": "7.0.0-beta.2", "license": "MIT", "engines": { "node": ">=10" diff --git a/packages/node/package.json b/packages/node/package.json index 06a85125fcd3..08ef7a9e3ffc 100644 --- a/packages/node/package.json +++ b/packages/node/package.json @@ -1,6 +1,6 @@ { "name": "@sentry/node", - "version": "7.0.0-beta.1", + "version": "7.0.0-beta.2", "description": "Official Sentry SDK for Node.js", "repository": "git://github.com/getsentry/sentry-javascript.git", "homepage": "https://github.com/getsentry/sentry-javascript/tree/master/packages/node", @@ -16,10 +16,10 @@ "access": "public" }, "dependencies": { - "@sentry/core": "7.0.0-beta.1", - "@sentry/hub": "7.0.0-beta.1", - "@sentry/types": "7.0.0-beta.1", - "@sentry/utils": "7.0.0-beta.1", + "@sentry/core": "7.0.0-beta.2", + "@sentry/hub": "7.0.0-beta.2", + "@sentry/types": "7.0.0-beta.2", + "@sentry/utils": "7.0.0-beta.2", "cookie": "^0.4.1", "https-proxy-agent": "^5.0.0", "lru_map": "^0.3.3", diff --git a/packages/react/package.json b/packages/react/package.json index d5cd0a4b6502..892e5a3b65e8 100644 --- a/packages/react/package.json +++ b/packages/react/package.json @@ -1,6 +1,6 @@ { "name": "@sentry/react", - "version": "7.0.0-beta.1", + "version": "7.0.0-beta.2", "description": "Official Sentry SDK for React.js", "repository": "git://github.com/getsentry/sentry-javascript.git", "homepage": "https://github.com/getsentry/sentry-javascript/tree/master/packages/react", @@ -16,9 +16,9 @@ "access": "public" }, "dependencies": { - "@sentry/browser": "7.0.0-beta.1", - "@sentry/types": "7.0.0-beta.1", - "@sentry/utils": "7.0.0-beta.1", + "@sentry/browser": "7.0.0-beta.2", + "@sentry/types": "7.0.0-beta.2", + "@sentry/utils": "7.0.0-beta.2", "hoist-non-react-statics": "^3.3.2", "tslib": "^1.9.3" }, diff --git a/packages/serverless/package.json b/packages/serverless/package.json index 1db678aed15e..af75c1b74272 100644 --- a/packages/serverless/package.json +++ b/packages/serverless/package.json @@ -1,6 +1,6 @@ { "name": "@sentry/serverless", - "version": "7.0.0-beta.1", + "version": "7.0.0-beta.2", "description": "Official Sentry SDK for various serverless solutions", "repository": "git://github.com/getsentry/sentry-javascript.git", "homepage": "https://github.com/getsentry/sentry-javascript/tree/master/packages/serverless", @@ -16,10 +16,10 @@ "access": "public" }, "dependencies": { - "@sentry/node": "7.0.0-beta.1", - "@sentry/tracing": "7.0.0-beta.1", - "@sentry/types": "7.0.0-beta.1", - "@sentry/utils": "7.0.0-beta.1", + "@sentry/node": "7.0.0-beta.2", + "@sentry/tracing": "7.0.0-beta.2", + "@sentry/types": "7.0.0-beta.2", + "@sentry/utils": "7.0.0-beta.2", "@types/aws-lambda": "^8.10.62", "@types/express": "^4.17.2", "tslib": "^1.9.3" diff --git a/packages/tracing/package.json b/packages/tracing/package.json index 25510c2a1376..5798635420cc 100644 --- a/packages/tracing/package.json +++ b/packages/tracing/package.json @@ -1,6 +1,6 @@ { "name": "@sentry/tracing", - "version": "7.0.0-beta.1", + "version": "7.0.0-beta.2", "description": "Extensions for Sentry AM", "repository": "git://github.com/getsentry/sentry-javascript.git", "homepage": "https://github.com/getsentry/sentry-javascript/tree/master/packages/tracing", @@ -16,13 +16,13 @@ "access": "public" }, "dependencies": { - "@sentry/hub": "7.0.0-beta.1", - "@sentry/types": "7.0.0-beta.1", - "@sentry/utils": "7.0.0-beta.1", + "@sentry/hub": "7.0.0-beta.2", + "@sentry/types": "7.0.0-beta.2", + "@sentry/utils": "7.0.0-beta.2", "tslib": "^1.9.3" }, "devDependencies": { - "@sentry/browser": "7.0.0-beta.1", + "@sentry/browser": "7.0.0-beta.2", "@types/express": "^4.17.1" }, "scripts": { diff --git a/packages/types/package.json b/packages/types/package.json index c8d275c6c1b1..cab2efca3fcf 100644 --- a/packages/types/package.json +++ b/packages/types/package.json @@ -1,6 +1,6 @@ { "name": "@sentry/types", - "version": "7.0.0-beta.1", + "version": "7.0.0-beta.2", "description": "Types for all Sentry JavaScript SDKs", "repository": "git://github.com/getsentry/sentry-javascript.git", "homepage": "https://github.com/getsentry/sentry-javascript/tree/master/packages/types", diff --git a/packages/typescript/package.json b/packages/typescript/package.json index 3608d5139367..a9d88bf257bf 100644 --- a/packages/typescript/package.json +++ b/packages/typescript/package.json @@ -1,6 +1,6 @@ { "name": "@sentry-internal/typescript", - "version": "7.0.0-beta.1", + "version": "7.0.0-beta.2", "description": "Typescript configuration used at Sentry", "repository": "git://github.com/getsentry/sentry-javascript.git", "homepage": "https://github.com/getsentry/sentry-javascript/tree/master/packages/typescript", diff --git a/packages/utils/package.json b/packages/utils/package.json index 6cc5ac6def18..d1ae0ddf73e2 100644 --- a/packages/utils/package.json +++ b/packages/utils/package.json @@ -1,6 +1,6 @@ { "name": "@sentry/utils", - "version": "7.0.0-beta.1", + "version": "7.0.0-beta.2", "description": "Utilities for all Sentry JavaScript SDKs", "repository": "git://github.com/getsentry/sentry-javascript.git", "homepage": "https://github.com/getsentry/sentry-javascript/tree/master/packages/utils", @@ -16,7 +16,7 @@ "access": "public" }, "dependencies": { - "@sentry/types": "7.0.0-beta.1", + "@sentry/types": "7.0.0-beta.2", "tslib": "^1.9.3" }, "devDependencies": { diff --git a/packages/vue/package.json b/packages/vue/package.json index e1f37eade04a..38bea2e2abb8 100644 --- a/packages/vue/package.json +++ b/packages/vue/package.json @@ -1,6 +1,6 @@ { "name": "@sentry/vue", - "version": "7.0.0-beta.1", + "version": "7.0.0-beta.2", "description": "Official Sentry SDK for Vue.js", "repository": "git://github.com/getsentry/sentry-javascript.git", "homepage": "https://github.com/getsentry/sentry-javascript/tree/master/packages/vue", @@ -16,10 +16,10 @@ "access": "public" }, "dependencies": { - "@sentry/browser": "7.0.0-beta.1", - "@sentry/core": "7.0.0-beta.1", - "@sentry/types": "7.0.0-beta.1", - "@sentry/utils": "7.0.0-beta.1", + "@sentry/browser": "7.0.0-beta.2", + "@sentry/core": "7.0.0-beta.2", + "@sentry/types": "7.0.0-beta.2", + "@sentry/utils": "7.0.0-beta.2", "tslib": "^1.9.3" }, "peerDependencies": { diff --git a/packages/wasm/package.json b/packages/wasm/package.json index 2bc4a612132b..4588cb28568e 100644 --- a/packages/wasm/package.json +++ b/packages/wasm/package.json @@ -1,6 +1,6 @@ { "name": "@sentry/wasm", - "version": "7.0.0-beta.1", + "version": "7.0.0-beta.2", "description": "Support for WASM.", "repository": "git://github.com/getsentry/sentry-javascript.git", "homepage": "https://github.com/getsentry/sentry-javascript/tree/master/packages/wasm", @@ -16,9 +16,9 @@ "access": "public" }, "dependencies": { - "@sentry/browser": "7.0.0-beta.1", - "@sentry/types": "7.0.0-beta.1", - "@sentry/utils": "7.0.0-beta.1", + "@sentry/browser": "7.0.0-beta.2", + "@sentry/types": "7.0.0-beta.2", + "@sentry/utils": "7.0.0-beta.2", "tslib": "^1.9.3" }, "devDependencies": { From bbc9d3d6d924618ae83518271122a06d38497b50 Mon Sep 17 00:00:00 2001 From: Luca Forstner Date: Tue, 24 May 2022 13:28:04 +0200 Subject: [PATCH 191/204] fix(browser): Fix memory leak in `addEventListener` instrumentation (#5147) --- packages/browser/src/helpers.ts | 7 ++-- packages/browser/src/integrations/trycatch.ts | 8 +++- .../subject.js | 21 ++++++++++ .../test.ts | 28 ++++++++++++++ .../subject.js | 24 ++++++++++++ .../eventListener-this-preservation/test.ts | 24 ++++++++++++ .../eventListener-wrapping/subject.js | 18 +++++++++ .../eventListener-wrapping/test.ts | 38 +++++++++++++++++++ .../instrumentation/eventListener/subject.js | 5 +++ .../instrumentation/eventListener/test.ts | 27 +++++++++++++ .../suites/public-api/instrumentation/init.js | 7 ++++ 11 files changed, 203 insertions(+), 4 deletions(-) create mode 100644 packages/integration-tests/suites/public-api/instrumentation/eventListener-instrumentation-behaviour/subject.js create mode 100644 packages/integration-tests/suites/public-api/instrumentation/eventListener-instrumentation-behaviour/test.ts create mode 100644 packages/integration-tests/suites/public-api/instrumentation/eventListener-this-preservation/subject.js create mode 100644 packages/integration-tests/suites/public-api/instrumentation/eventListener-this-preservation/test.ts create mode 100644 packages/integration-tests/suites/public-api/instrumentation/eventListener-wrapping/subject.js create mode 100644 packages/integration-tests/suites/public-api/instrumentation/eventListener-wrapping/test.ts create mode 100644 packages/integration-tests/suites/public-api/instrumentation/eventListener/subject.js create mode 100644 packages/integration-tests/suites/public-api/instrumentation/eventListener/test.ts create mode 100644 packages/integration-tests/suites/public-api/instrumentation/init.js diff --git a/packages/browser/src/helpers.ts b/packages/browser/src/helpers.ts index 9bf1d5631288..870756066df4 100644 --- a/packages/browser/src/helpers.ts +++ b/packages/browser/src/helpers.ts @@ -32,7 +32,8 @@ export function ignoreNextOnError(): void { * Instruments the given function and sends an event to Sentry every time the * function throws an exception. * - * @param fn A function to wrap. + * @param fn A function to wrap. It is generally safe to pass an unbound function, because the returned wrapper always + * has a correct `this` context. * @returns The wrapped function. * @hidden */ @@ -75,8 +76,8 @@ export function wrap( } /* eslint-disable prefer-rest-params */ - // eslint-disable-next-line @typescript-eslint/no-explicit-any - const sentryWrapped: WrappedFunction = function (this: any): void { + // It is important that `sentryWrapped` is not an arrow function to preserve the context of `this` + const sentryWrapped: WrappedFunction = function (this: unknown): void { const args = Array.prototype.slice.call(arguments); try { diff --git a/packages/browser/src/integrations/trycatch.ts b/packages/browser/src/integrations/trycatch.ts index 84391d238b65..b6b293d70c37 100644 --- a/packages/browser/src/integrations/trycatch.ts +++ b/packages/browser/src/integrations/trycatch.ts @@ -208,7 +208,13 @@ function _wrapEventTarget(target: string): void { ): (eventName: string, fn: EventListenerObject, capture?: boolean, secure?: boolean) => void { try { if (typeof fn.handleEvent === 'function') { - fn.handleEvent = wrap(fn.handleEvent.bind(fn), { + // ESlint disable explanation: + // First, it is generally safe to call `wrap` with an unbound function. Furthermore, using `.bind()` would + // introduce a bug here, because bind returns a new function that doesn't have our + // flags(like __sentry_original__) attached. `wrap` checks for those flags to avoid unnecessary wrapping. + // Without those flags, every call to addEventListener wraps the function again, causing a memory leak. + // eslint-disable-next-line @typescript-eslint/unbound-method + fn.handleEvent = wrap(fn.handleEvent, { mechanism: { data: { function: 'handleEvent', diff --git a/packages/integration-tests/suites/public-api/instrumentation/eventListener-instrumentation-behaviour/subject.js b/packages/integration-tests/suites/public-api/instrumentation/eventListener-instrumentation-behaviour/subject.js new file mode 100644 index 000000000000..32f1d09378b0 --- /dev/null +++ b/packages/integration-tests/suites/public-api/instrumentation/eventListener-instrumentation-behaviour/subject.js @@ -0,0 +1,21 @@ +// Simple function event listener +const functionListener = () => { + functionListenerCallback(); +}; + +// Attach event listener twice +window.addEventListener('click', functionListener); +window.addEventListener('click', functionListener); + +// Event listener that has handleEvent() method: https://developer.mozilla.org/en-US/docs/Web/API/EventTarget/addEventListener#listener +class EventHandlerClass { + handleEvent() { + objectListenerCallback(); + } +} + +const objectListener = new EventHandlerClass(); + +// Attach event listener twice +window.addEventListener('click', objectListener); +window.addEventListener('click', objectListener); diff --git a/packages/integration-tests/suites/public-api/instrumentation/eventListener-instrumentation-behaviour/test.ts b/packages/integration-tests/suites/public-api/instrumentation/eventListener-instrumentation-behaviour/test.ts new file mode 100644 index 000000000000..b6d2e6fa9231 --- /dev/null +++ b/packages/integration-tests/suites/public-api/instrumentation/eventListener-instrumentation-behaviour/test.ts @@ -0,0 +1,28 @@ +import { expect } from '@playwright/test'; + +import { sentryTest } from '../../../../utils/fixtures'; + +sentryTest( + 'Event listener instrumentation should attach the same event listener only once', + async ({ getLocalTestPath, page }) => { + const url = await getLocalTestPath({ testDir: __dirname }); + await page.goto(url); + + let functionListenerCalls = 0; + await page.exposeFunction('functionListenerCallback', () => { + functionListenerCalls = functionListenerCalls + 1; + }); + + let objectListenerCalls = 0; + await page.exposeFunction('objectListenerCallback', () => { + objectListenerCalls = objectListenerCalls + 1; + }); + + // Trigger event listeners twice + await page.evaluate('document.body.click()'); + await page.evaluate('document.body.click()'); + + expect(functionListenerCalls).toBe(2); + expect(objectListenerCalls).toBe(2); + }, +); diff --git a/packages/integration-tests/suites/public-api/instrumentation/eventListener-this-preservation/subject.js b/packages/integration-tests/suites/public-api/instrumentation/eventListener-this-preservation/subject.js new file mode 100644 index 000000000000..7f5d2cc290e1 --- /dev/null +++ b/packages/integration-tests/suites/public-api/instrumentation/eventListener-this-preservation/subject.js @@ -0,0 +1,24 @@ +const btn = document.createElement('button'); +btn.id = 'btn'; +document.body.appendChild(btn); + +const functionListener = function () { + functionCallback(this.constructor.name); +}; + +class EventHandlerClass { + handleEvent() { + classInstanceCallback(this.constructor.name); + } +} +const objectListener = new EventHandlerClass(); + +// Attach event listeners a few times for good measure + +btn.addEventListener('click', functionListener); +btn.addEventListener('click', functionListener); +btn.addEventListener('click', functionListener); + +btn.addEventListener('click', objectListener); +btn.addEventListener('click', objectListener); +btn.addEventListener('click', objectListener); diff --git a/packages/integration-tests/suites/public-api/instrumentation/eventListener-this-preservation/test.ts b/packages/integration-tests/suites/public-api/instrumentation/eventListener-this-preservation/test.ts new file mode 100644 index 000000000000..e6304858eed4 --- /dev/null +++ b/packages/integration-tests/suites/public-api/instrumentation/eventListener-this-preservation/test.ts @@ -0,0 +1,24 @@ +import { expect } from '@playwright/test'; + +import { sentryTest } from '../../../../utils/fixtures'; + +sentryTest('Event listener instrumentation preserves "this" context', async ({ getLocalTestPath, page }) => { + const url = await getLocalTestPath({ testDir: __dirname }); + await page.goto(url); + + let assertions = 0; + + await page.exposeFunction('functionCallback', (thisInstanceName: unknown) => { + expect(thisInstanceName).toBe('HTMLButtonElement'); + assertions = assertions + 1; + }); + + await page.exposeFunction('classInstanceCallback', (thisInstanceName: unknown) => { + expect(thisInstanceName).toBe('EventHandlerClass'); + assertions = assertions + 1; + }); + + await page.evaluate('document.getElementById("btn").click()'); + + expect(assertions).toBe(2); +}); diff --git a/packages/integration-tests/suites/public-api/instrumentation/eventListener-wrapping/subject.js b/packages/integration-tests/suites/public-api/instrumentation/eventListener-wrapping/subject.js new file mode 100644 index 000000000000..289068737dc2 --- /dev/null +++ b/packages/integration-tests/suites/public-api/instrumentation/eventListener-wrapping/subject.js @@ -0,0 +1,18 @@ +// Simple function event listener +const functionListener = () => { + reportFunctionListenerStackHeight(new Error().stack.split('\n').length); +}; + +// Event listener that has handleEvent() method: https://developer.mozilla.org/en-US/docs/Web/API/EventTarget/addEventListener#listener +class EventHandlerClass { + handleEvent() { + reportObjectListenerStackHeight(new Error().stack.split('\n').length); + } +} + +const objectListener = new EventHandlerClass(); + +window.attachListeners = function () { + window.addEventListener('click', functionListener); + window.addEventListener('click', objectListener); +}; diff --git a/packages/integration-tests/suites/public-api/instrumentation/eventListener-wrapping/test.ts b/packages/integration-tests/suites/public-api/instrumentation/eventListener-wrapping/test.ts new file mode 100644 index 000000000000..cf77132c98df --- /dev/null +++ b/packages/integration-tests/suites/public-api/instrumentation/eventListener-wrapping/test.ts @@ -0,0 +1,38 @@ +import { expect } from '@playwright/test'; + +import { sentryTest } from '../../../../utils/fixtures'; + +sentryTest( + 'Event listener instrumentation should not wrap event listeners multiple times', + async ({ getLocalTestPath, page }) => { + const url = await getLocalTestPath({ testDir: __dirname }); + await page.goto(url); + + const functionListenerStackHeights: number[] = []; + const objectListenerStackHeights: number[] = []; + + await page.exposeFunction('reportFunctionListenerStackHeight', (height: number) => { + functionListenerStackHeights.push(height); + }); + + await page.exposeFunction('reportObjectListenerStackHeight', (height: number) => { + objectListenerStackHeights.push(height); + }); + + // Attach initial listeners + await page.evaluate('window.attachListeners()'); + await page.evaluate('document.body.click()'); + + await page.evaluate('window.attachListeners()'); + await page.evaluate('window.attachListeners()'); + await page.evaluate('window.attachListeners()'); + await page.evaluate('document.body.click()'); + + expect(functionListenerStackHeights).toHaveLength(2); + expect(objectListenerStackHeights).toHaveLength(2); + + // check if all error stack traces are the same height + expect(functionListenerStackHeights.every((val, _i, arr) => val === arr[0])).toBeTruthy(); + expect(objectListenerStackHeights.every((val, _i, arr) => val === arr[0])).toBeTruthy(); + }, +); diff --git a/packages/integration-tests/suites/public-api/instrumentation/eventListener/subject.js b/packages/integration-tests/suites/public-api/instrumentation/eventListener/subject.js new file mode 100644 index 000000000000..a5e052885a4a --- /dev/null +++ b/packages/integration-tests/suites/public-api/instrumentation/eventListener/subject.js @@ -0,0 +1,5 @@ +window.addEventListener('click', () => { + throw new Error('event_listener_error'); +}); + +document.body.click(); diff --git a/packages/integration-tests/suites/public-api/instrumentation/eventListener/test.ts b/packages/integration-tests/suites/public-api/instrumentation/eventListener/test.ts new file mode 100644 index 000000000000..71eb3e7023b3 --- /dev/null +++ b/packages/integration-tests/suites/public-api/instrumentation/eventListener/test.ts @@ -0,0 +1,27 @@ +import { expect } from '@playwright/test'; +import { Event } from '@sentry/types'; + +import { sentryTest } from '../../../../utils/fixtures'; +import { getFirstSentryEnvelopeRequest } from '../../../../utils/helpers'; + +sentryTest( + 'Event listener instrumentation should capture an error thrown in an event handler', + async ({ getLocalTestPath, page }) => { + const url = await getLocalTestPath({ testDir: __dirname }); + + const eventData = await getFirstSentryEnvelopeRequest(page, url); + + expect(eventData.exception?.values).toHaveLength(1); + expect(eventData.exception?.values?.[0]).toMatchObject({ + type: 'Error', + value: 'event_listener_error', + mechanism: { + type: 'instrument', + handled: true, + }, + stacktrace: { + frames: expect.any(Array), + }, + }); + }, +); diff --git a/packages/integration-tests/suites/public-api/instrumentation/init.js b/packages/integration-tests/suites/public-api/instrumentation/init.js new file mode 100644 index 000000000000..d8c94f36fdd0 --- /dev/null +++ b/packages/integration-tests/suites/public-api/instrumentation/init.js @@ -0,0 +1,7 @@ +import * as Sentry from '@sentry/browser'; + +window.Sentry = Sentry; + +Sentry.init({ + dsn: 'https://public@dsn.ingest.sentry.io/1337', +}); From fa5712e615acf03ea7058d01cb6183b738532c98 Mon Sep 17 00:00:00 2001 From: Luca Forstner Date: Tue, 24 May 2022 15:21:32 +0200 Subject: [PATCH 192/204] fix(tracing): Don't use `querySelector` when not available (#5160) --- packages/tracing/src/browser/browsertracing.ts | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/packages/tracing/src/browser/browsertracing.ts b/packages/tracing/src/browser/browsertracing.ts index cb50474f3d4c..1872a08d728f 100644 --- a/packages/tracing/src/browser/browsertracing.ts +++ b/packages/tracing/src/browser/browsertracing.ts @@ -268,6 +268,13 @@ export function extractTraceDataFromMetaTags(): Partial | un /** Returns the value of a meta tag */ export function getMetaContent(metaName: string): string | null { - const el = getGlobalObject().document.querySelector(`meta[name=${metaName}]`); - return el ? el.getAttribute('content') : null; + const globalObject = getGlobalObject(); + + // DOM/querySelector is not available in all environments + if (globalObject.document && globalObject.document.querySelector) { + const el = globalObject.document.querySelector(`meta[name=${metaName}]`); + return el ? el.getAttribute('content') : null; + } else { + return null; + } } From b0950aa366ec22aefc32d9ae5c334dbc4a3c6792 Mon Sep 17 00:00:00 2001 From: Katie Byers Date: Tue, 24 May 2022 06:28:14 -0700 Subject: [PATCH 193/204] ref(dev): Improve labeling of checkout step in GHA (#5156) Currently, when looking at runs of our `Build & Test` GHA workflow, it's often hard to tell exactly what commit is being used. When the workflow is triggered by a push to a PR, the PR title ends up as the label, which can be pretty uninformative. Runs triggered manually are no better, as they're merely labeled "Build & Test." Runs triggered by pushes _are_ better, at least from the Actions tab, because they're labeled for their commit, but once you open one up, it's not obvious from the logs which commit is being used, since "Check out current commit ()" doesn't actually list the branch's `HEAD` commit but rather a made-up merge commit between `HEAD` and the base branch, making it useless for identifying `HEAD`. (See the PR description for screenshots of these different cases.) This fixes that problem, by adding a preliminary GHA job to gather and store the SHA of the actual branch head (along with its commit message). Subsequent jobs can then use that information to label their checkout step, making it much easier to tell where in the commit history the job falls. --- .github/workflows/build.yml | 73 ++++++++++++++++++++++++------------- 1 file changed, 48 insertions(+), 25 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index c55483590816..35e2c3e95ccf 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -38,12 +38,35 @@ env: BUILD_CACHE_KEY: ${{ github.event.inputs.commit || github.sha }} jobs: + job_get_metadata: + name: Get Metadata + runs-on: ubuntu-latest + steps: + - name: Check out current commit + uses: actions/checkout@v2 + with: + ref: ${{ env.HEAD_COMMIT }} + # We need to check out not only the fake merge commit between the PR and the base branch which GH creates, but + # also its parents, so that we can pull the commit message from the head commit of the PR + fetch-depth: 2 + - name: Get metadata + id: get_metadata + # We need to try a number of different options for finding the head commit, because each kind of trigger event + # stores it in a different location + run: | + COMMIT_SHA=$(git rev-parse --short ${{ github.event.pull_request.head.sha || github.event.head_commit.id || env.HEAD_COMMIT }}) + echo "COMMIT_SHA=$COMMIT_SHA" >> $GITHUB_ENV + echo "COMMIT_MESSAGE=$(git log -n 1 --pretty=format:%s $COMMIT_SHA)" >> $GITHUB_ENV + outputs: + commit_label: "${{ env.COMMIT_SHA }}: ${{ env.COMMIT_MESSAGE }}" + job_install_deps: name: Install Dependencies + needs: job_get_metadata runs-on: ubuntu-latest timeout-minutes: 15 steps: - - name: Check out current commit (${{ env.HEAD_COMMIT }}) + - name: "Check out current commit (${{ needs.job_get_metadata.outputs.commit_label }})" uses: actions/checkout@v2 with: ref: ${{ env.HEAD_COMMIT }} @@ -70,11 +93,11 @@ jobs: job_build: name: Build - needs: job_install_deps + needs: [ job_get_metadata, job_install_deps ] runs-on: ubuntu-latest timeout-minutes: 20 steps: - - name: Check out current commit (${{ env.HEAD_COMMIT }}) + - name: Check out current commit (${{ needs.job_get_metadata.outputs.commit_label }}) uses: actions/checkout@v2 with: ref: ${{ env.HEAD_COMMIT }} @@ -150,13 +173,13 @@ jobs: job_size_check: name: Size Check - needs: job_build + needs: [job_get_metadata, job_build] timeout-minutes: 15 runs-on: ubuntu-latest # Size Check will error out outside of the context of a PR if: ${{ github.event_name == 'pull_request' }} steps: - - name: Check out current commit (${{ env.HEAD_COMMIT }}) + - name: Check out current commit (${{ needs.job_get_metadata.outputs.commit_label }}) uses: actions/checkout@v2 with: ref: ${{ env.HEAD_COMMIT }} @@ -184,11 +207,11 @@ jobs: job_lint: name: Lint - needs: job_build + needs: [job_get_metadata, job_build] timeout-minutes: 10 runs-on: ubuntu-latest steps: - - name: Check out current commit (${{ env.HEAD_COMMIT }}) + - name: Check out current commit (${{ needs.job_get_metadata.outputs.commit_label }}) uses: actions/checkout@v2 with: ref: ${{ env.HEAD_COMMIT }} @@ -211,11 +234,11 @@ jobs: job_circular_dep_check: name: Circular Dependency Check - needs: job_build + needs: [job_get_metadata, job_build] timeout-minutes: 10 runs-on: ubuntu-latest steps: - - name: Check out current commit (${{ env.HEAD_COMMIT }}) + - name: Check out current commit (${{ needs.job_get_metadata.outputs.commit_label }}) uses: actions/checkout@v2 with: ref: ${{ env.HEAD_COMMIT }} @@ -238,12 +261,12 @@ jobs: job_artifacts: name: Upload Artifacts - needs: job_build + needs: [job_get_metadata, job_build] runs-on: ubuntu-latest # Build artifacts are only needed for releasing workflow. if: startsWith(github.ref, 'refs/heads/release/') steps: - - name: Check out current commit (${{ github.sha }}) + - name: Check out current commit (${{ needs.job_get_metadata.outputs.commit_label }}) uses: actions/checkout@v2 with: ref: ${{ env.HEAD_COMMIT }} @@ -275,7 +298,7 @@ jobs: job_unit_test: name: Test (Node ${{ matrix.node }}) - needs: job_build + needs: [job_get_metadata, job_build] continue-on-error: true timeout-minutes: 30 runs-on: ubuntu-latest @@ -283,7 +306,7 @@ jobs: matrix: node: [8, 10, 12, 14, 16, 18] steps: - - name: Check out current commit (${{ env.HEAD_COMMIT }}) + - name: Check out current commit (${{ needs.job_get_metadata.outputs.commit_label }}) uses: actions/checkout@v2 with: ref: ${{ env.HEAD_COMMIT }} @@ -312,7 +335,7 @@ jobs: job_nextjs_integration_test: name: Test @sentry/nextjs on (Node ${{ matrix.node }}) - needs: job_build + needs: [job_get_metadata, job_build] continue-on-error: true timeout-minutes: 30 runs-on: ubuntu-latest @@ -320,7 +343,7 @@ jobs: matrix: node: [10, 12, 14, 16, 18] steps: - - name: Check out current commit (${{ env.HEAD_COMMIT }}) + - name: Check out current commit (${{ needs.job_get_metadata.outputs.commit_label }}) uses: actions/checkout@v2 with: ref: ${{ env.HEAD_COMMIT }} @@ -349,12 +372,12 @@ jobs: # separate job allows them to run in parallel with the other tests. job_ember_tests: name: Test @sentry/ember - needs: job_build + needs: [job_get_metadata, job_build] continue-on-error: true timeout-minutes: 30 runs-on: ubuntu-latest steps: - - name: Check out current commit (${{ env.HEAD_COMMIT }}) + - name: Check out current commit (${{ needs.job_get_metadata.outputs.commit_label }}) uses: actions/checkout@v2 with: ref: ${{ env.HEAD_COMMIT }} @@ -388,7 +411,7 @@ jobs: job_browser_playwright_tests: name: Playwright - ${{ (matrix.tracing_only && 'Browser + Tracing') || 'Browser' }} (${{ matrix.bundle }}) - needs: job_build + needs: [job_get_metadata, job_build] runs-on: ubuntu-latest strategy: matrix: @@ -410,7 +433,7 @@ jobs: - bundle: cjs tracing_only: false steps: - - name: Check out current commit (${{ env.HEAD_COMMIT }}) + - name: Check out current commit (${{ needs.job_get_metadata.outputs.commit_label }}) uses: actions/checkout@v2 with: ref: ${{ env.HEAD_COMMIT }} @@ -439,7 +462,7 @@ jobs: job_browser_integration_tests: name: Old Browser Integration Tests (${{ matrix.browser }}) - needs: job_build + needs: [job_get_metadata, job_build] runs-on: ubuntu-latest timeout-minutes: 10 continue-on-error: true @@ -450,7 +473,7 @@ jobs: - FirefoxHeadless - WebkitHeadless steps: - - name: Check out current commit (${{ env.HEAD_COMMIT }}) + - name: Check out current commit (${{ needs.job_get_metadata.outputs.commit_label }}) uses: actions/checkout@v2 with: ref: ${{ env.HEAD_COMMIT }} @@ -478,12 +501,12 @@ jobs: job_browser_build_tests: name: Browser Build Tests - needs: job_build + needs: [job_get_metadata, job_build] runs-on: ubuntu-latest timeout-minutes: 5 continue-on-error: true steps: - - name: Check out current commit (${ env.HEAD_COMMIT }}) + - name: Check out current commit (${{ needs.job_get_metadata.outputs.commit_label }}) uses: actions/checkout@v2 with: ref: ${{ env.HEAD_COMMIT }} @@ -512,7 +535,7 @@ jobs: job_node_integration_tests: name: Node SDK Integration Tests (${{ matrix.node }}) - needs: job_build + needs: [job_get_metadata, job_build] runs-on: ubuntu-latest timeout-minutes: 10 continue-on-error: true @@ -520,7 +543,7 @@ jobs: matrix: node: [10, 12, 14, 16, 18] steps: - - name: Check out current commit (${{ github.sha }}) + - name: Check out current commit (${{ needs.job_get_metadata.outputs.commit_label }}) uses: actions/checkout@v2 with: ref: ${{ env.HEAD_COMMIT }} From ef9a5d93982d7ecf225689fbec1dd2cb807e8319 Mon Sep 17 00:00:00 2001 From: Lukas Stracke Date: Wed, 25 May 2022 09:13:32 +0200 Subject: [PATCH 194/204] ref(core): Remove unnecessary nested `dropUndefinedKeys` call (#5161) remove an unnecessary nested call to dropUndefinedKeys which we don't need because the function is already recursive. --- packages/core/src/envelope.ts | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/packages/core/src/envelope.ts b/packages/core/src/envelope.ts index 9ec9bd6db58f..0fb974d66bc7 100644 --- a/packages/core/src/envelope.ts +++ b/packages/core/src/envelope.ts @@ -136,12 +136,10 @@ function createEventEnvelopeHeaders( environment: event.environment, release: event.release, transaction: event.transaction, - user: - event.user && - dropUndefinedKeys({ - id: event.user.id, - segment: event.user.segment, - }), + user: event.user && { + id: event.user.id, + segment: event.user.segment, + }, public_key: dsn.publicKey, }), }), From 7b9747fb1000d4ccbd2dec2fdda02f1c345db7c3 Mon Sep 17 00:00:00 2001 From: Tim Fish Date: Wed, 25 May 2022 09:34:49 +0100 Subject: [PATCH 195/204] ref(core): Actually ensure attachments are added to the envelope (#5159) This patch fixes a bug where attachments were previously not added to the envelope that would be sent to Sentry. Additionally, it adds a test that ensures that the attachments actually make it into the raw envelope. --- packages/core/src/baseclient.ts | 7 ++-- packages/core/test/lib/attachments.test.ts | 38 ++++++++++++++++++++++ 2 files changed, 43 insertions(+), 2 deletions(-) create mode 100644 packages/core/test/lib/attachments.test.ts diff --git a/packages/core/src/baseclient.ts b/packages/core/src/baseclient.ts index 260eb412561b..1c8de1472a49 100644 --- a/packages/core/src/baseclient.ts +++ b/packages/core/src/baseclient.ts @@ -287,10 +287,13 @@ export abstract class BaseClient implements Client { */ public sendEvent(event: Event, hint: EventHint = {}): void { if (this._dsn) { - const env = createEventEnvelope(event, this._dsn, this._options._metadata, this._options.tunnel); + let env = createEventEnvelope(event, this._dsn, this._options._metadata, this._options.tunnel); for (const attachment of hint.attachments || []) { - addItemToEnvelope(env, createAttachmentEnvelopeItem(attachment, this._options.transportOptions?.textEncoder)); + env = addItemToEnvelope( + env, + createAttachmentEnvelopeItem(attachment, this._options.transportOptions?.textEncoder), + ); } this._sendEnvelope(env); diff --git a/packages/core/test/lib/attachments.test.ts b/packages/core/test/lib/attachments.test.ts new file mode 100644 index 000000000000..610518225793 --- /dev/null +++ b/packages/core/test/lib/attachments.test.ts @@ -0,0 +1,38 @@ +import { parseEnvelope } from '@sentry/utils/test/testutils'; +import { TextEncoder } from 'util'; + +import { createTransport } from '../../src/transports/base'; +import { getDefaultTestClientOptions, TestClient } from '../mocks/client'; + +describe('Attachments', () => { + beforeEach(() => { + TestClient.sendEventCalled = undefined; + TestClient.instance = undefined; + }); + + afterEach(() => { + jest.clearAllMocks(); + }); + + test('actually end up in envelope', async () => { + expect.assertions(4); + + const options = getDefaultTestClientOptions({ + dsn: 'https://username@domain/123', + enableSend: true, + transport: () => + createTransport({ recordDroppedEvent: () => undefined, textEncoder: new TextEncoder() }, async req => { + const [, items] = parseEnvelope(req.body); + expect(items.length).toEqual(2); + // Second envelope item should be the attachment + expect(items[1][0]).toEqual({ type: 'attachment', length: 50000, filename: 'empty.bin' }); + expect(items[1][1]).toBeInstanceOf(Uint8Array); + expect((items[1][1] as Uint8Array).length).toEqual(50_000); + return {}; + }), + }); + + const client = new TestClient(options); + client.captureEvent({}, { attachments: [{ filename: 'empty.bin', data: new Uint8Array(50_000) }] }); + }); +}); From 31595e3573086141334fab51951bacee9376ab88 Mon Sep 17 00:00:00 2001 From: Tim Fish Date: Wed, 25 May 2022 11:35:05 +0100 Subject: [PATCH 196/204] ref(core): Make hint callback argument non-optional (#5141) --- .../lib/integrations/inboundfilters.test.ts | 58 +++++++++---------- packages/hub/src/scope.ts | 4 +- packages/integrations/src/debug.ts | 6 +- packages/integrations/src/extraerrordata.ts | 6 +- packages/integrations/test/debug.test.ts | 4 +- packages/integrations/test/offline.test.ts | 2 +- .../node/src/integrations/linkederrors.ts | 6 +- .../test/integrations/linkederrors.test.ts | 4 +- packages/types/src/eventprocessor.ts | 2 +- packages/types/src/options.ts | 2 +- 10 files changed, 47 insertions(+), 47 deletions(-) diff --git a/packages/core/test/lib/integrations/inboundfilters.test.ts b/packages/core/test/lib/integrations/inboundfilters.test.ts index 74672a3f56e5..8170e68d9cd5 100644 --- a/packages/core/test/lib/integrations/inboundfilters.test.ts +++ b/packages/core/test/lib/integrations/inboundfilters.test.ts @@ -180,16 +180,16 @@ describe('InboundFilters', () => { describe('_isSentryError', () => { it('should work as expected', () => { const eventProcessor = createInboundFiltersEventProcessor(); - expect(eventProcessor(MESSAGE_EVENT)).toBe(MESSAGE_EVENT); - expect(eventProcessor(EXCEPTION_EVENT)).toBe(EXCEPTION_EVENT); - expect(eventProcessor(SENTRY_EVENT)).toBe(null); + expect(eventProcessor(MESSAGE_EVENT, {})).toBe(MESSAGE_EVENT); + expect(eventProcessor(EXCEPTION_EVENT, {})).toBe(EXCEPTION_EVENT); + expect(eventProcessor(SENTRY_EVENT, {})).toBe(null); }); it('should be configurable', () => { const eventProcessor = createInboundFiltersEventProcessor({ ignoreInternal: false }); - expect(eventProcessor(MESSAGE_EVENT)).toBe(MESSAGE_EVENT); - expect(eventProcessor(EXCEPTION_EVENT)).toBe(EXCEPTION_EVENT); - expect(eventProcessor(SENTRY_EVENT)).toBe(SENTRY_EVENT); + expect(eventProcessor(MESSAGE_EVENT, {})).toBe(MESSAGE_EVENT); + expect(eventProcessor(EXCEPTION_EVENT, {})).toBe(EXCEPTION_EVENT); + expect(eventProcessor(SENTRY_EVENT, {})).toBe(SENTRY_EVENT); }); }); @@ -198,29 +198,29 @@ describe('InboundFilters', () => { const eventProcessor = createInboundFiltersEventProcessor({ ignoreErrors: ['capture'], }); - expect(eventProcessor(MESSAGE_EVENT)).toBe(null); + expect(eventProcessor(MESSAGE_EVENT, {})).toBe(null); }); it('string filter with exact match', () => { const eventProcessor = createInboundFiltersEventProcessor({ ignoreErrors: ['captureMessage'], }); - expect(eventProcessor(MESSAGE_EVENT)).toBe(null); + expect(eventProcessor(MESSAGE_EVENT, {})).toBe(null); }); it('regexp filter with partial match', () => { const eventProcessor = createInboundFiltersEventProcessor({ ignoreErrors: [/capture/], }); - expect(eventProcessor(MESSAGE_EVENT)).toBe(null); + expect(eventProcessor(MESSAGE_EVENT, {})).toBe(null); }); it('regexp filter with exact match', () => { const eventProcessor = createInboundFiltersEventProcessor({ ignoreErrors: [/^captureMessage$/], }); - expect(eventProcessor(MESSAGE_EVENT)).toBe(null); - expect(eventProcessor(MESSAGE_EVENT_2)).toBe(MESSAGE_EVENT_2); + expect(eventProcessor(MESSAGE_EVENT, {})).toBe(null); + expect(eventProcessor(MESSAGE_EVENT_2, {})).toBe(MESSAGE_EVENT_2); }); it('prefers message when both message and exception are available', () => { @@ -231,20 +231,20 @@ describe('InboundFilters', () => { ...EXCEPTION_EVENT, ...MESSAGE_EVENT, }; - expect(eventProcessor(event)).toBe(null); + expect(eventProcessor(event, {})).toBe(null); }); it('can use multiple filters', () => { const eventProcessor = createInboundFiltersEventProcessor({ ignoreErrors: ['captureMessage', /SyntaxError/], }); - expect(eventProcessor(MESSAGE_EVENT)).toBe(null); - expect(eventProcessor(EXCEPTION_EVENT)).toBe(null); + expect(eventProcessor(MESSAGE_EVENT, {})).toBe(null); + expect(eventProcessor(EXCEPTION_EVENT, {})).toBe(null); }); it('uses default filters', () => { const eventProcessor = createInboundFiltersEventProcessor(); - expect(eventProcessor(SCRIPT_ERROR_EVENT)).toBe(null); + expect(eventProcessor(SCRIPT_ERROR_EVENT, {})).toBe(null); }); describe('on exception', () => { @@ -252,21 +252,21 @@ describe('InboundFilters', () => { const eventProcessor = createInboundFiltersEventProcessor({ ignoreErrors: ['SyntaxError: unidentified ? at line 1337'], }); - expect(eventProcessor(EXCEPTION_EVENT)).toBe(null); + expect(eventProcessor(EXCEPTION_EVENT, {})).toBe(null); }); it('can match on exception value', () => { const eventProcessor = createInboundFiltersEventProcessor({ ignoreErrors: [/unidentified \?/], }); - expect(eventProcessor(EXCEPTION_EVENT)).toBe(null); + expect(eventProcessor(EXCEPTION_EVENT, {})).toBe(null); }); it('can match on exception type', () => { const eventProcessor = createInboundFiltersEventProcessor({ ignoreErrors: [/^SyntaxError/], }); - expect(eventProcessor(EXCEPTION_EVENT)).toBe(null); + expect(eventProcessor(EXCEPTION_EVENT, {})).toBe(null); }); }); }); @@ -276,7 +276,7 @@ describe('InboundFilters', () => { const eventProcessorDeny = createInboundFiltersEventProcessor({ denyUrls: ['https://awesome-analytics.io'], }); - expect(eventProcessorDeny(MESSAGE_EVENT_WITH_STACKTRACE)).toBe(null); + expect(eventProcessorDeny(MESSAGE_EVENT_WITH_STACKTRACE, {})).toBe(null); }); it('should allow denyUrls to take precedence', () => { @@ -284,70 +284,70 @@ describe('InboundFilters', () => { allowUrls: ['https://awesome-analytics.io'], denyUrls: ['https://awesome-analytics.io'], }); - expect(eventProcessorBoth(MESSAGE_EVENT_WITH_STACKTRACE)).toBe(null); + expect(eventProcessorBoth(MESSAGE_EVENT_WITH_STACKTRACE, {})).toBe(null); }); it('should filter captured message based on its stack trace using regexp filter', () => { const eventProcessorDeny = createInboundFiltersEventProcessor({ denyUrls: [/awesome-analytics\.io/], }); - expect(eventProcessorDeny(MESSAGE_EVENT_WITH_STACKTRACE)).toBe(null); + expect(eventProcessorDeny(MESSAGE_EVENT_WITH_STACKTRACE, {})).toBe(null); }); it('should not filter captured messages with no stacktraces', () => { const eventProcessor = createInboundFiltersEventProcessor({ denyUrls: ['https://awesome-analytics.io'], }); - expect(eventProcessor(MESSAGE_EVENT)).toBe(MESSAGE_EVENT); + expect(eventProcessor(MESSAGE_EVENT, {})).toBe(MESSAGE_EVENT); }); it('should filter captured exception based on its stack trace using string filter', () => { const eventProcessor = createInboundFiltersEventProcessor({ denyUrls: ['https://awesome-analytics.io'], }); - expect(eventProcessor(EXCEPTION_EVENT_WITH_FRAMES)).toBe(null); + expect(eventProcessor(EXCEPTION_EVENT_WITH_FRAMES, {})).toBe(null); }); it('should filter captured exception based on its stack trace using regexp filter', () => { const eventProcessor = createInboundFiltersEventProcessor({ denyUrls: [/awesome-analytics\.io/], }); - expect(eventProcessor(EXCEPTION_EVENT_WITH_FRAMES)).toBe(null); + expect(eventProcessor(EXCEPTION_EVENT_WITH_FRAMES, {})).toBe(null); }); it("should not filter events that don't match the filtered values", () => { const eventProcessor = createInboundFiltersEventProcessor({ denyUrls: ['some-other-domain.com'], }); - expect(eventProcessor(EXCEPTION_EVENT_WITH_FRAMES)).toBe(EXCEPTION_EVENT_WITH_FRAMES); + expect(eventProcessor(EXCEPTION_EVENT_WITH_FRAMES, {})).toBe(EXCEPTION_EVENT_WITH_FRAMES); }); it('should be able to use multiple filters', () => { const eventProcessor = createInboundFiltersEventProcessor({ denyUrls: ['some-other-domain.com', /awesome-analytics\.io/], }); - expect(eventProcessor(EXCEPTION_EVENT_WITH_FRAMES)).toBe(null); + expect(eventProcessor(EXCEPTION_EVENT_WITH_FRAMES, {})).toBe(null); }); it('should not fail with malformed event event', () => { const eventProcessor = createInboundFiltersEventProcessor({ denyUrls: ['https://awesome-analytics.io'], }); - expect(eventProcessor(MALFORMED_EVENT)).toBe(MALFORMED_EVENT); + expect(eventProcessor(MALFORMED_EVENT, {})).toBe(MALFORMED_EVENT); }); it('should search for script names when there is an anonymous callback at the last frame', () => { const eventProcessor = createInboundFiltersEventProcessor({ denyUrls: ['https://awesome-analytics.io/some/file.js'], }); - expect(eventProcessor(MESSAGE_EVENT_WITH_ANON_LAST_FRAME)).toBe(null); + expect(eventProcessor(MESSAGE_EVENT_WITH_ANON_LAST_FRAME, {})).toBe(null); }); it('should search for script names when the last frame is from native code', () => { const eventProcessor = createInboundFiltersEventProcessor({ denyUrls: ['https://awesome-analytics.io/some/file.js'], }); - expect(eventProcessor(MESSAGE_EVENT_WITH_NATIVE_LAST_FRAME)).toBe(null); + expect(eventProcessor(MESSAGE_EVENT_WITH_NATIVE_LAST_FRAME, {})).toBe(null); }); }); }); diff --git a/packages/hub/src/scope.ts b/packages/hub/src/scope.ts index f1ffd51cc9e5..98f80745004c 100644 --- a/packages/hub/src/scope.ts +++ b/packages/hub/src/scope.ts @@ -436,7 +436,7 @@ export class Scope implements ScopeInterface { * @param hint May contain additional information about the original exception. * @hidden */ - public applyToEvent(event: Event, hint?: EventHint): PromiseLike { + public applyToEvent(event: Event, hint: EventHint = {}): PromiseLike { if (this._extra && Object.keys(this._extra).length) { event.extra = { ...this._extra, ...event.extra }; } @@ -491,7 +491,7 @@ export class Scope implements ScopeInterface { protected _notifyEventProcessors( processors: EventProcessor[], event: Event | null, - hint?: EventHint, + hint: EventHint, index: number = 0, ): PromiseLike { return new SyncPromise((resolve, reject) => { diff --git a/packages/integrations/src/debug.ts b/packages/integrations/src/debug.ts index baf981947b6c..efa1beba35c9 100644 --- a/packages/integrations/src/debug.ts +++ b/packages/integrations/src/debug.ts @@ -37,7 +37,7 @@ export class Debug implements Integration { * @inheritDoc */ public setupOnce(addGlobalEventProcessor: (callback: EventProcessor) => void, getCurrentHub: () => Hub): void { - addGlobalEventProcessor((event: Event, hint?: EventHint) => { + addGlobalEventProcessor((event: Event, hint: EventHint) => { const self = getCurrentHub().getIntegration(Debug); if (self) { if (self._options.debugger) { @@ -49,12 +49,12 @@ export class Debug implements Integration { consoleSandbox(() => { if (self._options.stringify) { console.log(JSON.stringify(event, null, 2)); - if (hint) { + if (Object.keys(hint).length) { console.log(JSON.stringify(hint, null, 2)); } } else { console.log(event); - if (hint) { + if (Object.keys(hint).length) { console.log(hint); } } diff --git a/packages/integrations/src/extraerrordata.ts b/packages/integrations/src/extraerrordata.ts index c2baf7197bda..0dfb8f39bbfc 100644 --- a/packages/integrations/src/extraerrordata.ts +++ b/packages/integrations/src/extraerrordata.ts @@ -37,7 +37,7 @@ export class ExtraErrorData implements Integration { * @inheritDoc */ public setupOnce(addGlobalEventProcessor: (callback: EventProcessor) => void, getCurrentHub: () => Hub): void { - addGlobalEventProcessor((event: Event, hint?: EventHint) => { + addGlobalEventProcessor((event: Event, hint: EventHint) => { const self = getCurrentHub().getIntegration(ExtraErrorData); if (!self) { return event; @@ -49,8 +49,8 @@ export class ExtraErrorData implements Integration { /** * Attaches extracted information from the Error object to extra field in the Event */ - public enhanceEventWithErrorData(event: Event, hint?: EventHint): Event { - if (!hint || !hint.originalException || !isError(hint.originalException)) { + public enhanceEventWithErrorData(event: Event, hint: EventHint = {}): Event { + if (!hint.originalException || !isError(hint.originalException)) { return event; } const exceptionName = (hint.originalException as ExtendedError).name || hint.originalException.constructor.name; diff --git a/packages/integrations/test/debug.test.ts b/packages/integrations/test/debug.test.ts index 768c9606a1fb..eed7e52d509e 100644 --- a/packages/integrations/test/debug.test.ts +++ b/packages/integrations/test/debug.test.ts @@ -27,7 +27,7 @@ describe('Debug integration setup should register an event processor that', () = const captureEventProcessor = (eventProcessor: EventProcessor) => { const testEvent = { event_id: 'some event' }; - void eventProcessor(testEvent); + void eventProcessor(testEvent, {}); expect(mockConsoleLog).toHaveBeenCalledTimes(1); expect(mockConsoleLog).toBeCalledWith(testEvent); }; @@ -55,7 +55,7 @@ describe('Debug integration setup should register an event processor that', () = const captureEventProcessor = (eventProcessor: EventProcessor) => { const testEvent = { event_id: 'some event' }; - void eventProcessor(testEvent); + void eventProcessor(testEvent, {}); expect(mockConsoleLog).toHaveBeenCalledTimes(1); expect(mockConsoleLog).toBeCalledWith(JSON.stringify(testEvent, null, 2)); }; diff --git a/packages/integrations/test/offline.test.ts b/packages/integrations/test/offline.test.ts index 9c4eb8ad2e36..7bd77c047182 100644 --- a/packages/integrations/test/offline.test.ts +++ b/packages/integrations/test/offline.test.ts @@ -202,7 +202,7 @@ function processEventListeners(): void { function processEvents(): void { eventProcessors.forEach(processor => { events.forEach(event => { - processor(event) as Event | null; + processor(event, {}) as Event | null; }); }); } diff --git a/packages/node/src/integrations/linkederrors.ts b/packages/node/src/integrations/linkederrors.ts index 19555c9bb542..2817517513db 100644 --- a/packages/node/src/integrations/linkederrors.ts +++ b/packages/node/src/integrations/linkederrors.ts @@ -43,7 +43,7 @@ export class LinkedErrors implements Integration { * @inheritDoc */ public setupOnce(): void { - addGlobalEventProcessor(async (event: Event, hint?: EventHint) => { + addGlobalEventProcessor(async (event: Event, hint: EventHint) => { const hub = getCurrentHub(); const self = hub.getIntegration(LinkedErrors); const client = hub.getClient(); @@ -57,8 +57,8 @@ export class LinkedErrors implements Integration { /** * @inheritDoc */ - private _handler(stackParser: StackParser, event: Event, hint?: EventHint): PromiseLike { - if (!event.exception || !event.exception.values || !hint || !isInstanceOf(hint.originalException, Error)) { + private _handler(stackParser: StackParser, event: Event, hint: EventHint): PromiseLike { + if (!event.exception || !event.exception.values || !isInstanceOf(hint.originalException, Error)) { return resolvedSyncPromise(event); } diff --git a/packages/node/test/integrations/linkederrors.test.ts b/packages/node/test/integrations/linkederrors.test.ts index c2bba0bd97d2..549481d7299d 100644 --- a/packages/node/test/integrations/linkederrors.test.ts +++ b/packages/node/test/integrations/linkederrors.test.ts @@ -19,7 +19,7 @@ describe('LinkedErrors', () => { const event = { message: 'foo', }; - return linkedErrors._handler(stackParser, event).then((result: any) => { + return linkedErrors._handler(stackParser, event, {}).then((result: any) => { expect(spy.mock.calls.length).toEqual(0); expect(result).toEqual(event); }); @@ -36,7 +36,7 @@ describe('LinkedErrors', () => { .eventFromException(one) .then(eventFromException => { event = eventFromException; - return linkedErrors._handler(stackParser, eventFromException); + return linkedErrors._handler(stackParser, eventFromException, {}); }) .then(result => { expect(spy.mock.calls.length).toEqual(0); diff --git a/packages/types/src/eventprocessor.ts b/packages/types/src/eventprocessor.ts index 22bd74f0ab73..5be90a755367 100644 --- a/packages/types/src/eventprocessor.ts +++ b/packages/types/src/eventprocessor.ts @@ -8,5 +8,5 @@ import { Event, EventHint } from './event'; */ export interface EventProcessor { id?: string; // This field can't be named "name" because functions already have this field natively - (event: Event, hint?: EventHint): PromiseLike | Event | null; + (event: Event, hint: EventHint): PromiseLike | Event | null; } diff --git a/packages/types/src/options.ts b/packages/types/src/options.ts index 92627bb8142a..b8d346f2973e 100644 --- a/packages/types/src/options.ts +++ b/packages/types/src/options.ts @@ -191,7 +191,7 @@ export interface ClientOptions PromiseLike | Event | null; + beforeSend?: (event: Event, hint: EventHint) => PromiseLike | Event | null; /** * A callback invoked when adding a breadcrumb, allowing to optionally modify From 0ad297155d4aa793a1d122230ed44eff9b548a39 Mon Sep 17 00:00:00 2001 From: Luca Forstner Date: Wed, 25 May 2022 12:36:31 +0200 Subject: [PATCH 197/204] fix(utils): Fix infinite recursion in `dropUndefinedKeys` (#5163) --- packages/utils/src/object.ts | 22 ++++++++++++++-- packages/utils/test/object.test.ts | 41 ++++++++++++++++++++++++++++++ 2 files changed, 61 insertions(+), 2 deletions(-) diff --git a/packages/utils/src/object.ts b/packages/utils/src/object.ts index d4974cecbf44..5957214ecc86 100644 --- a/packages/utils/src/object.ts +++ b/packages/utils/src/object.ts @@ -4,6 +4,7 @@ import { WrappedFunction } from '@sentry/types'; import { htmlTreeAsString } from './browser'; import { isElement, isError, isEvent, isInstanceOf, isPlainObject, isPrimitive } from './is'; +import { memoBuilder, MemoFunc } from './memo'; import { truncate } from './string'; /** @@ -205,20 +206,37 @@ export function extractExceptionKeysForMessage(exception: Record(val: T): T { + // This function just proxies `_dropUndefinedKeys` to keep the `memoBuilder` out of this function's API + return _dropUndefinedKeys(val, memoBuilder()); +} + +function _dropUndefinedKeys(val: T, memo: MemoFunc): T { + const [memoize] = memo; // we don't need unmemoize because we don't need to visit nodes twice + if (isPlainObject(val)) { + if (memoize(val)) { + return val; + } const rv: { [key: string]: any } = {}; for (const key of Object.keys(val)) { if (typeof val[key] !== 'undefined') { - rv[key] = dropUndefinedKeys(val[key]); + rv[key] = _dropUndefinedKeys(val[key], memo); } } return rv as T; } if (Array.isArray(val)) { - return (val as any[]).map(dropUndefinedKeys) as any; + if (memoize(val)) { + return val; + } + return (val as any[]).map(item => { + return _dropUndefinedKeys(item, memo); + }) as any; } return val; diff --git a/packages/utils/test/object.test.ts b/packages/utils/test/object.test.ts index e4fc4428ae04..65131fa0cd94 100644 --- a/packages/utils/test/object.test.ts +++ b/packages/utils/test/object.test.ts @@ -199,6 +199,47 @@ describe('dropUndefinedKeys()', () => { }, }); }); + + test('objects with circular reference', () => { + const dog: any = { + food: undefined, + }; + + const human = { + brain: undefined, + pets: dog, + }; + + const rat = { + scares: human, + weight: '4kg', + }; + + dog.chases = rat; + + expect(dropUndefinedKeys(human)).toStrictEqual({ + pets: { + chases: rat, + }, + }); + }); + + test('arrays with circular reference', () => { + const egg: any[] = []; + + const chicken = { + food: undefined, + weight: '1kg', + lays: egg, + }; + + egg[0] = chicken; + + expect(dropUndefinedKeys(chicken)).toStrictEqual({ + lays: egg, + weight: '1kg', + }); + }); }); describe('objectify()', () => { From 98c2cccf3a0a6115b5a40baa39ca6557bcd14679 Mon Sep 17 00:00:00 2001 From: Tim Fish Date: Wed, 25 May 2022 12:37:49 +0100 Subject: [PATCH 198/204] ref(core): Test mutation of attachments in hint (#5140) --- packages/core/test/lib/hint.test.ts | 96 +++++++++++++++++++++++++ packages/core/test/mocks/integration.ts | 13 ++++ 2 files changed, 109 insertions(+) create mode 100644 packages/core/test/lib/hint.test.ts diff --git a/packages/core/test/lib/hint.test.ts b/packages/core/test/lib/hint.test.ts new file mode 100644 index 000000000000..03b755a63c71 --- /dev/null +++ b/packages/core/test/lib/hint.test.ts @@ -0,0 +1,96 @@ +import { captureEvent, configureScope } from '@sentry/hub'; +import { getGlobalObject } from '@sentry/utils'; + +import { initAndBind } from '../../src/sdk'; +import { getDefaultTestClientOptions, TestClient } from '../mocks/client'; +import { AddAttachmentTestIntegration } from '../mocks/integration'; + +const PUBLIC_DSN = 'https://username@domain/123'; +const sendEvent = jest.spyOn(TestClient.prototype, 'sendEvent'); + +describe('Hint', () => { + beforeEach(() => { + TestClient.sendEventCalled = undefined; + TestClient.instance = undefined; + }); + + afterEach(() => { + jest.clearAllMocks(); + delete getGlobalObject().__SENTRY__; + }); + + describe('attachments', () => { + test('can be mutated in beforeSend', () => { + expect.assertions(1); + + const options = getDefaultTestClientOptions({ + dsn: PUBLIC_DSN, + beforeSend: (event, hint) => { + hint.attachments = [...(hint.attachments || []), { filename: 'another.file', data: 'more text' }]; + return event; + }, + }); + + const client = new TestClient(options); + client.captureEvent({}); + + const [, hint] = sendEvent.mock.calls[0]; + expect(hint).toEqual({ attachments: [{ filename: 'another.file', data: 'more text' }] }); + }); + + test('gets passed through to beforeSend and can be further mutated', () => { + expect.assertions(1); + + const options = getDefaultTestClientOptions({ + dsn: PUBLIC_DSN, + beforeSend: (event, hint) => { + hint.attachments = [...(hint.attachments || []), { filename: 'another.file', data: 'more text' }]; + return event; + }, + }); + + const client = new TestClient(options); + client.captureEvent({}, { attachments: [{ filename: 'some-file.txt', data: 'Hello' }] }); + + const [, hint] = sendEvent.mock.calls[0]; + expect(hint).toEqual({ + attachments: [ + { filename: 'some-file.txt', data: 'Hello' }, + { filename: 'another.file', data: 'more text' }, + ], + }); + }); + + test('can be mutated by an integration via event processor', () => { + expect.assertions(1); + + const options = getDefaultTestClientOptions({ + dsn: PUBLIC_DSN, + integrations: [new AddAttachmentTestIntegration()], + }); + + initAndBind(TestClient, options); + captureEvent({}); + + const [, hint] = sendEvent.mock.calls[0]; + expect(hint?.attachments).toEqual([{ filename: 'integration.file', data: 'great content!' }]); + }); + + test('get copied from scope to hint', () => { + expect.assertions(1); + + const options = getDefaultTestClientOptions({ dsn: PUBLIC_DSN }); + initAndBind(TestClient, options); + + configureScope(scope => scope.addAttachment({ filename: 'scope.file', data: 'great content!' })); + + captureEvent({}, { attachments: [{ filename: 'some-file.txt', data: 'Hello' }] }); + + const [, hint] = sendEvent.mock.calls[0]; + expect(hint?.attachments).toEqual([ + { filename: 'some-file.txt', data: 'Hello' }, + { filename: 'scope.file', data: 'great content!' }, + ]); + }); + }); +}); diff --git a/packages/core/test/mocks/integration.ts b/packages/core/test/mocks/integration.ts index 2e192fb12227..868446665949 100644 --- a/packages/core/test/mocks/integration.ts +++ b/packages/core/test/mocks/integration.ts @@ -22,3 +22,16 @@ export class TestIntegration implements Integration { }); } } + +export class AddAttachmentTestIntegration implements Integration { + public static id: string = 'AddAttachmentTestIntegration'; + + public name: string = 'AddAttachmentTestIntegration'; + + public setupOnce(addGlobalEventProcessor: (callback: EventProcessor) => void): void { + addGlobalEventProcessor((event, hint) => { + hint.attachments = [...(hint.attachments || []), { filename: 'integration.file', data: 'great content!' }]; + return event; + }); + } +} From 549512d461cff88da207ecde0a371fb705869e23 Mon Sep 17 00:00:00 2001 From: Katie Byers Date: Wed, 25 May 2022 07:22:10 -0700 Subject: [PATCH 199/204] ref(build): Use rollup to build AWS lambda layer (#5146) Our current system for building the AWS lambda layer involves a script which manually traces the serverless package's dependencies, including other monorepo packages, and symlinks them into a `node_modules` folder in a directory set up to mimic an installation from npm. There are a few disadvantages to this system: - The signals we rely on to figure out what is and isn't a dependency aren't exhaustive, and we're not experts at this, both of which have made getting the right files and only the right files into the layer a brittle process, which has broken down more than once and caused issue for our users. - The script is complicated, which makes it harder to figure out exactly what's causing it when something does go wrong. - We symlink in entire packages at a time, regardless of whether or not we're using most of the code. This is true even of the serverless package itself, where we include all of the GCP code along with the AWS code. This refactors our lambda layer build process to use the new bundling config functions we use for CDN bundles, to create a bundle which can take the place of the the index file, but which contains everything it needs internally. This has the following advantages: - This puts Rollup in charge of figuring out dependencies instead of us. - The config is in line with existing configs and is therefore much easier to reason about and maintain. - It lets us easily exclude anything GCP-related in the SDK. Between that, Rollup's treeshaking, and terser's minifying, the layer will now take up much less of the finite size allotted to each lambda function. - Removing extraneous files means less to cache and retrieve from the cache in each GHA job. Key changes: - The layer now builds in `packages/serverless/build/aws` rather than at the top level of the repo. (It is now moved to the top level only in our GHA workflow creating the lambda zip.) - In that workflow, the process to determine the SDK version has been simplified. - The bundle builds based not on the main index file but on a new bundle-specific index file, which only includes AWS code. - There is new rollup config just for the layer, which uses the bundle-building functions to make the main bundle and the npm-package-building functions to create a separate module for the optional script which automatically starts up the SDK in AWS. - The old build script has been replaced by one which runs rollup with that config, and then symlinks the built auto-startup script into its legacy location, so as to be backwards compatible with folks who run it using an environment variable pointing to the old path. - The building of the layer has temporarily been shifted from `build:awslambda-layer` to `build:bundle`, so that it will run during the first phase of the repo-level `yarn build`. (The goal is to eventually do everything in one phase, for greater parallelization and shorter overall build time.) h/t to @antonpirker for all his help testing and thinking through this with me. --- .github/workflows/build.yml | 82 ++++---- .gitignore | 3 + packages/core/.npmignore | 15 -- packages/hub/.npmignore | 15 -- packages/node/.npmignore | 15 -- packages/serverless/.eslintrc.js | 14 ++ packages/serverless/.gitignore | 1 - packages/serverless/.npmignore | 13 -- packages/serverless/package.json | 13 +- packages/serverless/rollup.aws.config.js | 44 ++++ packages/serverless/rollup.npm.config.js | 5 + .../scripts/build-awslambda-layer.js | 194 ------------------ .../serverless/scripts/buildLambdaLayer.ts | 62 ++++++ packages/serverless/src/index.awslambda.ts | 8 + packages/serverless/tsconfig.types.json | 6 +- packages/tracing/.npmignore | 15 -- packages/types/.npmignore | 13 -- packages/utils/.npmignore | 15 -- scripts/aws-deploy-local-layer.sh | 66 ++++++ scripts/ensure-bundle-deps.ts | 5 +- 20 files changed, 258 insertions(+), 346 deletions(-) delete mode 100644 packages/core/.npmignore delete mode 100644 packages/hub/.npmignore delete mode 100644 packages/node/.npmignore delete mode 100644 packages/serverless/.gitignore delete mode 100644 packages/serverless/.npmignore create mode 100644 packages/serverless/rollup.aws.config.js delete mode 100644 packages/serverless/scripts/build-awslambda-layer.js create mode 100644 packages/serverless/scripts/buildLambdaLayer.ts create mode 100644 packages/serverless/src/index.awslambda.ts delete mode 100644 packages/tracing/.npmignore delete mode 100644 packages/types/.npmignore delete mode 100644 packages/utils/.npmignore create mode 100755 scripts/aws-deploy-local-layer.sh diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 35e2c3e95ccf..9765416a614c 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -31,7 +31,7 @@ env: ${{ github.workspace }}/packages/ember/instance-initializers ${{ github.workspace }}/packages/gatsby/*.d.ts ${{ github.workspace }}/packages/core/src/version.ts - ${{ github.workspace }}/dist-serverless + ${{ github.workspace }}/packages/serverless ${{ github.workspace }}/packages/utils/cjs ${{ github.workspace }}/packages/utils/esm @@ -124,52 +124,50 @@ jobs: # this file) to a constant and skip rebuilding all of the packages each time CI runs. if: steps.cache_built_packages.outputs.cache-hit == '' run: yarn build - - name: Save SDK version for later - run: | - echo "Saving SDK_VERSION for later" - cat packages/core/src/version.ts | awk -F"'" '{print $2}' > dist-serverless/version - [ ! -z $(cat dist-serverless/version) ] && echo SDK_VERSION=$(cat dist-serverless/version) || (echo "Version extraction failed" && exit 1) outputs: # this needs to be passed on, because the `needs` context only looks at direct ancestors (so steps which depend on # `job_build` can't see `job_install_deps` and what it returned) dependency_cache_key: ${{ needs.job_install_deps.outputs.dependency_cache_key }} - # job_build_aws_lambda_layer: - # name: Build AWS Lambda Layer - # needs: job_build - # runs-on: ubuntu-latest - # steps: - # - name: Check out current commit (${{ env.HEAD_COMMIT }}) - # uses: actions/checkout@v2 - # with: - # ref: ${{ env.HEAD_COMMIT }} - # - name: Set up Node - # uses: actions/setup-node@v1 - # with: - # node-version: ${{ env.DEFAULT_NODE_VERSION }} - # - name: Check dependency cache - # uses: actions/cache@v2 - # with: - # path: ${{ env.CACHED_DEPENDENCY_PATHS }} - # key: ${{ needs.job_build.outputs.dependency_cache_key }} - # - name: Check build cache - # uses: actions/cache@v2 - # with: - # path: ${{ env.CACHED_BUILD_PATHS }} - # key: ${{ env.BUILD_CACHE_KEY }} - # - name: Get SDK version - # run: | - # export SDK_VERSION=$(cat dist-serverless/version) - # echo "SDK_VERSION=$SDK_VERSION" | tee -a $GITHUB_ENV - # - uses: actions/upload-artifact@v3 - # with: - # name: ${{ env.HEAD_COMMIT }} - # path: | - # dist-serverless/* - # - uses: getsentry/action-build-aws-lambda-extension@v1 - # with: - # artifact_name: ${{ env.HEAD_COMMIT }} - # zip_file_name: sentry-node-serverless-${{ env.SDK_VERSION }}.zip + job_pack_aws_lambda_layer: + name: Pack and Upload AWS Lambda Layer + needs: [job_get_metadata, job_build] + runs-on: ubuntu-latest + steps: + - name: Check out current commit (${{ needs.job_get_metadata.outputs.commit_label }}) + uses: actions/checkout@v2 + with: + ref: ${{ env.HEAD_COMMIT }} + - name: Set up Node + uses: actions/setup-node@v1 + with: + node-version: ${{ env.DEFAULT_NODE_VERSION }} + - name: Check dependency cache + uses: actions/cache@v2 + with: + path: ${{ env.CACHED_DEPENDENCY_PATHS }} + key: ${{ needs.job_build.outputs.dependency_cache_key }} + - name: Check build cache + uses: actions/cache@v2 + with: + path: ${{ env.CACHED_BUILD_PATHS }} + key: ${{ env.BUILD_CACHE_KEY }} + - name: Get SDK version + # `jq` reads JSON files, and `tee` pipes its input to the given location and to stdout. (Adding `-a` is the + # equivalent of using >> rather than >.) + run: | + export SDK_VERSION=$(cat packages/core/package.json | jq --raw-output '.version') + echo "SDK_VERSION=$SDK_VERSION" | tee -a $GITHUB_ENV + - name: Move dist-serverless to root directory (requirement for zipping action) + run: | + mv ./packages/serverless/build/aws/dist-serverless . + - name: Create and upload final zip file + uses: getsentry/action-build-aws-lambda-extension@v1 + with: + artifact_name: ${{ env.HEAD_COMMIT }} + zip_file_name: sentry-node-serverless-${{ env.SDK_VERSION }}.zip + build_cache_paths: ${{ env.CACHED_BUILD_PATHS }} + build_cache_key: ${{ env.BUILD_CACHE_KEY }} job_size_check: name: Size Check diff --git a/.gitignore b/.gitignore index 3c490212dcc2..bc3f92fa4202 100644 --- a/.gitignore +++ b/.gitignore @@ -14,6 +14,9 @@ scratch/ *.js.map *.pyc *.tsbuildinfo +# side effects of running AWS lambda layer zip action locally +dist-serverless/ +sentry-node-serverless-*.zip # transpiled transformers jest/transformers/*.js # node tarballs diff --git a/packages/core/.npmignore b/packages/core/.npmignore deleted file mode 100644 index 329293958886..000000000000 --- a/packages/core/.npmignore +++ /dev/null @@ -1,15 +0,0 @@ -# The paths in this file are specified so that they align with the file structure in `./build` after this file is copied -# into it by the prepack script `scripts/prepack.ts`. - -* - -!/cjs/**/* -!/esm/**/* -!/types/**/* - -# These paths are necessary for Node AWS Lambda layer creation -# This package is a (transitive) dependency of @sentry/serverless and thus it is pulled into -# the lambda layer zip file. -!/build/cjs/**/* -!/build/esm/**/* -!/build/types/**/* diff --git a/packages/hub/.npmignore b/packages/hub/.npmignore deleted file mode 100644 index 329293958886..000000000000 --- a/packages/hub/.npmignore +++ /dev/null @@ -1,15 +0,0 @@ -# The paths in this file are specified so that they align with the file structure in `./build` after this file is copied -# into it by the prepack script `scripts/prepack.ts`. - -* - -!/cjs/**/* -!/esm/**/* -!/types/**/* - -# These paths are necessary for Node AWS Lambda layer creation -# This package is a (transitive) dependency of @sentry/serverless and thus it is pulled into -# the lambda layer zip file. -!/build/cjs/**/* -!/build/esm/**/* -!/build/types/**/* diff --git a/packages/node/.npmignore b/packages/node/.npmignore deleted file mode 100644 index 79d2576ebbb5..000000000000 --- a/packages/node/.npmignore +++ /dev/null @@ -1,15 +0,0 @@ -# The paths in this file are specified so that they align with the file structure in `./build` after this file is copied -# into it by the prepack script `scripts/prepack.ts`. - -* - -!/cjs/**/* -!/esm/**/* -!/types/**/* - -# These paths are necessary for Node AWS Lambda creation -# This package is a (transitive) dependency of @sentry/serverless and thus it is pulled into -# the lambda layer zip file. By specifying these paths, we -!/build/cjs/**/* -!/build/esm/**/* -!/build/types/**/* diff --git a/packages/serverless/.eslintrc.js b/packages/serverless/.eslintrc.js index ce28fd3a0514..b341e7ca3056 100644 --- a/packages/serverless/.eslintrc.js +++ b/packages/serverless/.eslintrc.js @@ -6,4 +6,18 @@ module.exports = { rules: { '@sentry-internal/sdk/no-async-await': 'off', }, + overrides: [ + { + files: ['scripts/**/*.ts'], + parserOptions: { + project: ['../../tsconfig.dev.json'], + }, + }, + { + files: ['test/**'], + parserOptions: { + sourceType: 'module', + }, + }, + ], }; diff --git a/packages/serverless/.gitignore b/packages/serverless/.gitignore deleted file mode 100644 index 466d272fde97..000000000000 --- a/packages/serverless/.gitignore +++ /dev/null @@ -1 +0,0 @@ -dist-serverless/ diff --git a/packages/serverless/.npmignore b/packages/serverless/.npmignore deleted file mode 100644 index 53b2a9d51a37..000000000000 --- a/packages/serverless/.npmignore +++ /dev/null @@ -1,13 +0,0 @@ -# The paths in this file are specified so that they align with the file structure in `./build` after this file is copied -# into it by the prepack script `scripts/prepack.ts`. - -* - -!/cjs/**/* -!/esm/**/* -!/types/**/* - -# These paths are necessary for Node AWS Lambda layer creation -!/build/cjs/**/* -!/build/esm/**/* -!/build/types/**/* diff --git a/packages/serverless/package.json b/packages/serverless/package.json index af75c1b74272..5350d7396571 100644 --- a/packages/serverless/package.json +++ b/packages/serverless/package.json @@ -9,9 +9,9 @@ "engines": { "node": ">=10" }, - "main": "build/cjs/index.js", - "module": "build/esm/index.js", - "types": "build/types/index.d.ts", + "main": "build/npm/cjs/index.js", + "module": "build/npm/esm/index.js", + "types": "build/npm/types/index.d.ts", "publishConfig": { "access": "public" }, @@ -38,8 +38,9 @@ "read-pkg": "^5.2.0" }, "scripts": { - "build": "run-p build:rollup build:types && yarn build:extras", - "build:awslambda-layer": "node scripts/build-awslambda-layer.js", + "build": "run-p build:rollup build:types build:bundle && yarn build:extras", + "build:awslambda-layer": "echo 'WARNING: AWS lambda layer build emporarily moved to \\`build:bundle\\`.'", + "build:bundle": "yarn ts-node scripts/buildLambdaLayer.ts", "build:dev": "run-p build:rollup build:types", "build:extras": "yarn build:awslambda-layer", "build:rollup": "rollup -c rollup.npm.config.js", @@ -48,7 +49,7 @@ "build:dev:watch": "run-s build:watch", "build:rollup:watch": "rollup -c rollup.npm.config.js --watch", "build:types:watch": "tsc -p tsconfig.types.json --watch", - "build:npm": "ts-node ../../scripts/prepack.ts && npm pack ./build", + "build:npm": "ts-node ../../scripts/prepack.ts --bundles && npm pack ./build/npm", "circularDepCheck": "madge --circular src/index.ts", "clean": "rimraf build dist-awslambda-layer coverage sentry-serverless-*.tgz", "fix": "run-s fix:eslint fix:prettier", diff --git a/packages/serverless/rollup.aws.config.js b/packages/serverless/rollup.aws.config.js new file mode 100644 index 000000000000..23c10b757f33 --- /dev/null +++ b/packages/serverless/rollup.aws.config.js @@ -0,0 +1,44 @@ +import { makeBaseBundleConfig, makeBundleConfigVariants, makeBaseNPMConfig } from '../../rollup/index.js'; + +export default [ + // The SDK + ...makeBundleConfigVariants( + makeBaseBundleConfig({ + // this automatically sets it to be CJS + bundleType: 'node', + entrypoints: ['src/index.awslambda.ts'], + jsVersion: 'es6', + licenseTitle: '@sentry/serverless', + outputFileBase: () => 'index', + packageSpecificConfig: { + output: { + dir: 'build/aws/dist-serverless/nodejs/node_modules/@sentry/serverless/build/npm/cjs', + sourcemap: false, + }, + }, + }), + // We only need one copy of the SDK, and we pick the minified one because there's a cap on how big a lambda function + // plus its dependencies can be, and we might as well take up as little of that space as is necessary. We'll rename + // it to be `index.js` in the build script, since it's standing in for the index file of the npm package. + { variants: ['.min.js'] }, + ), + + // This builds a wrapper file, which our lambda layer integration automatically sets up to run as soon as node + // launches (via the `NODE_OPTIONS="-r @sentry/serverless/dist/awslambda-auto"` variable). Note the inclusion in this + // path of the legacy `dist` folder; for backwards compatibility, in the build script we'll copy the file there. + makeBaseNPMConfig({ + entrypoints: ['src/awslambda-auto.ts'], + packageSpecificConfig: { + // Normally `makeNPMConfigVariants` sets both of these values for us, but we don't actually want the ESM variant, + // and the directory structure is different than normal, so we have to do it ourselves. + output: { + format: 'cjs', + dir: 'build/aws/dist-serverless/nodejs/node_modules/@sentry/serverless/build/npm/cjs', + sourcemap: false, + }, + // We only want `awslambda-auto.js`, not the modules that it imports, because they're all included in the bundle + // we generate above + external: ['./index'], + }, + }), +]; diff --git a/packages/serverless/rollup.npm.config.js b/packages/serverless/rollup.npm.config.js index 9b3074be8002..4e9641d5879e 100644 --- a/packages/serverless/rollup.npm.config.js +++ b/packages/serverless/rollup.npm.config.js @@ -2,6 +2,11 @@ import { makeBaseNPMConfig, makeNPMConfigVariants } from '../../rollup/index.js' export default makeNPMConfigVariants( makeBaseNPMConfig({ + // TODO: `awslambda-auto.ts` is a file which the lambda layer uses to automatically init the SDK. Does it need to be + // in the npm package? Is it possible that some people are using it themselves in the same way the layer uses it (in + // which case removing it would be a breaking change)? Should it stay here or not? entrypoints: ['src/index.ts', 'src/awslambda-auto.ts'], + // packages with bundles have a different build directory structure + hasBundles: true, }), ); diff --git a/packages/serverless/scripts/build-awslambda-layer.js b/packages/serverless/scripts/build-awslambda-layer.js deleted file mode 100644 index 8fbc493558e0..000000000000 --- a/packages/serverless/scripts/build-awslambda-layer.js +++ /dev/null @@ -1,194 +0,0 @@ -/* eslint-disable no-console */ -const path = require('path'); -const process = require('process'); -const fs = require('fs'); - -const findUp = require('find-up'); -const packList = require('npm-packlist'); -const readPkg = require('read-pkg'); - -const serverlessPackageJson = require('../package.json'); - -if (!process.env.GITHUB_ACTIONS) { - console.log('Skipping build-awslambda-layer script in local environment.'); - process.exit(0); -} - -// AWS Lambda layer are being uploaded as zip archive, whose content is then being unpacked to the /opt -// directory in the lambda environment. -// -// So this script does the following: it builds a 'dist-awslambda-layer/nodejs/node_modules/@sentry/serverless' -// directory with a special index.js and with all necessary @sentry packages symlinked as node_modules. -// Then, this directory is compressed with zip. -// -// The tricky part about it is that one cannot just symlink the entire package directories into node_modules because -// all the src/ contents and other unnecessary files will end up in the zip archive. So, we need to symlink only -// individual files from package and it must be only those of them that are distributable. -// There exists a `npm-packlist` library for such purpose. So we need to traverse all the dependencies, -// execute `npm-packlist` on them and symlink the files into 'dist-awslambda-layer/.../@sentry/serverless/node_modules'. -// I didn't find any way to achieve this goal using standard command-line tools so I have to write this script. -// -// Another, and much simpler way to assemble such zip bundle is install all the dependencies from npm registry and -// just bundle the entire node_modules. -// It's easier and looks more stable but it's inconvenient if one wants build a zip bundle out of current source tree. -// -// And yet another way is to bundle everything with webpack into a single file. I tried and it seems to be error-prone -// so I think it's better to have a classic package directory with node_modules file structure. - -/** - * Recursively traverses all the dependencies of @param pkg and collects all the info to the map - * The map ultimately contains @sentry/serverless itself, its direct dependencies and - * its transitive dependencies. - * - * @param cwd the root directory of the package - * @param packages the map accumulating all packages - */ -async function collectPackages(cwd, packages = {}) { - const packageJson = await readPkg({ cwd }); - - packages[packageJson.name] = { cwd, packageJson }; - - if (!packageJson.dependencies) { - return packages; - } - - await Promise.all( - Object.keys(packageJson.dependencies).map(async dep => { - // We are interested only in 'external' dependencies which are strictly upper than current directory. - // Internal deps aka local node_modules folder of each package is handled differently. - const searchPath = path.resolve(cwd, '..'); - const depPath = fs.realpathSync( - await findUp(path.join('node_modules', dep), { type: 'directory', cwd: searchPath }), - ); - if (packages[dep]) { - if (packages[dep].cwd != depPath) { - throw new Error(`${packageJson.name}'s dependency ${dep} maps to both ${packages[dep].cwd} and ${depPath}`); - } - return; - } - await collectPackages(depPath, packages); - }), - ); - - return packages; -} - -async function main() { - const baseDir = path.resolve(__dirname, '../../../') - const serverlessDir = path.resolve(__dirname, '..'); // packages/serverless directory - - const cjsBuildDir = path.resolve(serverlessDir, 'build', 'cjs'); - if (!fs.existsSync(cjsBuildDir)) { - console.log(`The path ${cjsBuildDir} must exist.`); - return; - } - - const packages = await collectPackages(serverlessDir); - - // the build directory of the Lambda layer - const layerBuildDir = path.resolve(baseDir, 'dist-serverless'); - - // the root directory in which the Lambda layer files + dependencies are copied to - // this structure resembles the structure where Lambda expects to find @sentry/serverless - const destRootRelative = 'nodejs/node_modules/@sentry/serverless'; - const destRootDir = path.resolve(layerBuildDir, destRootRelative); - - // this is where all the (transitive) dependencies of @sentry/serverless go - const destRootNodeModulesDir = path.resolve(destRootDir, 'node_modules'); - - try { - // Setting `force: true` ignores exceptions when paths don't exist. - fs.rmSync(destRootDir, { force: true, recursive: true, maxRetries: 1 }); - fs.mkdirSync(destRootDir, { recursive: true }); - } catch (error) { - // Ignore errors. - } - - await Promise.all( - Object.entries(packages).map(async ([name, pkg]) => { - const isServelessPkg = name == serverlessPackageJson.name; - const destDir = isServelessPkg ? destRootDir : path.resolve(destRootNodeModulesDir, name); - - // Scan over the "distributable" files of `pkg` and symlink all of them. - // `packList` returns all files it deems "distributable" from `pkg.cwd`. - // "Distributable" means in this case that the file would end up in the NPM tarball of `pkg`. - // To find out which files are distributable, packlist scans for NPM file configurations in the following order: - // 1. if `files` section present in package.json, take everything* from there - // 2. if `.npmignore` present, take everything* except what's ignored there - // 3. if `.gitignore` present, take everything* except what's ignored there - // 4. else take everything* - // In our case, rule 2 applies. - // * everything except certain unimportant files similarly to what `npm pack` does when packing a tarball. - // For more information on the rules see: https://github.com/npm/npm-packlist#readme - const sourceFiles = await packList({ path: pkg.cwd }); - - await Promise.all( - sourceFiles.map(async filename => { - const sourceFilePath = path.resolve(pkg.cwd, filename); - const destFilePath = path.resolve(destDir, filename); - - try { - fs.mkdirSync(path.dirname(destFilePath), { recursive: true }); - fs.symlinkSync(sourceFilePath, destFilePath); - } catch (error) { - // Ignore errors. - } - }), - ); - - // Now we deal with the `pkg`'s dependencies in its local `node_modules` directory - const pkgNodeModulesDir = path.resolve(pkg.cwd, 'node_modules'); - - // First, check if `pkg` has node modules. If not, we're done with this `pkg`. - // `fs.constants.F_OK` indicates whether the file is visible to the current process, but it doesn't check - // its permissions. For more information, refer to https://nodejs.org/api/fs.html#fs_file_access_constants. - try { - fs.accessSync(path.resolve(pkgNodeModulesDir), fs.constants.F_OK); - } catch (error) { - return; - } - - // Then, scan over local node_modules folder of `pkg` and symlink its non-dev dependencies. - const pkgNodeModules = fs.readdirSync(pkgNodeModulesDir); - await Promise.all( - pkgNodeModules.map(async nodeModule => { - if (!pkg.packageJson.dependencies || !pkg.packageJson.dependencies[nodeModule]) { - return; - } - - const sourceModulePath = path.resolve(pkgNodeModulesDir, nodeModule); - const destModulePath = path.resolve(destDir, 'node_modules', nodeModule); - - try { - fs.mkdirSync(path.dirname(destModulePath), { recursive: true }); - fs.symlinkSync(sourceModulePath, destModulePath); - } catch (error) { - // Ignore errors. - } - }), - ); - }), - ); - - // link from `./build/cjs` to `./dist` - // This needs to be done to satisfy the NODE_OPTIONS environment variable path that is set in - // AWS lambda functions when connecting them to Sentry. On initialization, the layer preloads a js - // file specified in NODE_OPTIONS to initialize the SDK. - // Hence we symlink everything from `.build/cjs` to `.dist`. - // This creates duplication but it's not too bad file size wise. - try { - fs.symlinkSync(path.resolve(destRootDir, 'build', 'cjs'), path.resolve(destRootDir, 'dist')); - } catch (error) { - console.error(error); - } -} - -main().then( - () => { - process.exit(0); - }, - err => { - console.error(err); - process.exit(-1); - }, -); diff --git a/packages/serverless/scripts/buildLambdaLayer.ts b/packages/serverless/scripts/buildLambdaLayer.ts new file mode 100644 index 000000000000..1a04aafde8aa --- /dev/null +++ b/packages/serverless/scripts/buildLambdaLayer.ts @@ -0,0 +1,62 @@ +/* eslint-disable no-console */ +import * as childProcess from 'child_process'; +import * as fs from 'fs'; +import * as rimraf from 'rimraf'; + +import { ensureBundleBuildPrereqs } from '../../../scripts/ensure-bundle-deps'; + +/** + * Run the given shell command, piping the shell process's `stdin`, `stdout`, and `stderr` to that of the current + * process. Returns contents of `stdout`. + */ +function run(cmd: string, options?: childProcess.ExecSyncOptions): string { + return String(childProcess.execSync(cmd, { stdio: 'inherit', ...options })); +} + +async function buildLambdaLayer(): Promise { + // Create the main SDK bundle + await ensureBundleBuildPrereqs({ + dependencies: ['@sentry/utils', '@sentry/hub', '@sentry/core', '@sentry/tracing', '@sentry/node'], + }); + run('yarn rollup --config rollup.aws.config.js'); + + // We build a minified bundle, but it's standing in for the regular `index.js` file listed in `package.json`'s `main` + // property, so we have to rename it so it's findable. + fs.renameSync( + 'build/aws/dist-serverless/nodejs/node_modules/@sentry/serverless/build/npm/cjs/index.min.js', + 'build/aws/dist-serverless/nodejs/node_modules/@sentry/serverless/build/npm/cjs/index.js', + ); + + // We're creating a bundle for the SDK, but still using it in a Node context, so we need to copy in `package.json`, + // purely for its `main` property. + console.log('Copying `package.json` into lambda layer.'); + fs.copyFileSync('package.json', 'build/aws/dist-serverless/nodejs/node_modules/@sentry/serverless/package.json'); + + // The layer also includes `awslambda-auto.js`, a helper file which calls `Sentry.init()` and wraps the lambda + // handler. It gets run when Node is launched inside the lambda, using the environment variable + // + // `NODE_OPTIONS="-r @sentry/serverless/dist/awslambda-auto"`. + // + // (The`-r` is what runs the script on startup.) The `dist` directory is no longer where we emit our built code, so + // for backwards compatibility, we create a symlink. + console.log('Creating symlink for `awslambda-auto.js` in legacy `dist` directory.'); + fsForceMkdirSync('build/aws/dist-serverless/nodejs/node_modules/@sentry/serverless/dist'); + fs.symlinkSync( + '../build/npm/cjs/awslambda-auto.js', + 'build/aws/dist-serverless/nodejs/node_modules/@sentry/serverless/dist/awslambda-auto.js', + ); +} + +void buildLambdaLayer(); + +/** + * Make a directory synchronously, overwriting the old directory if necessary. + * + * This is what `fs.mkdirSync(path, { force: true })` would be, if it existed. Primarily useful for local building and + * testing, where scripts are often run more than once (and so the directory you're trying to create may already be + * there), but also harmless when used in CI. + */ +function fsForceMkdirSync(path: string): void { + rimraf.sync(path); + fs.mkdirSync(path); +} diff --git a/packages/serverless/src/index.awslambda.ts b/packages/serverless/src/index.awslambda.ts new file mode 100644 index 000000000000..c097591ab9dc --- /dev/null +++ b/packages/serverless/src/index.awslambda.ts @@ -0,0 +1,8 @@ +/** This file is used as the entrypoint for the lambda layer bundle, and is not included in the npm package. */ + +// https://medium.com/unsplash/named-namespace-imports-7345212bbffb +import * as AWSLambda from './awslambda'; +export { AWSLambda }; + +export * from './awsservices'; +export * from '@sentry/node'; diff --git a/packages/serverless/tsconfig.types.json b/packages/serverless/tsconfig.types.json index 65455f66bd75..4c51bd21e64b 100644 --- a/packages/serverless/tsconfig.types.json +++ b/packages/serverless/tsconfig.types.json @@ -1,10 +1,14 @@ { "extends": "./tsconfig.json", + // We don't ship this in the npm package (it exists purely for controlling what ends up in the AWS lambda layer), so + // no need to build types for it + "exclude": ["src/index.awslambda.ts"], + "compilerOptions": { "declaration": true, "declarationMap": true, "emitDeclarationOnly": true, - "outDir": "build/types" + "outDir": "build/npm/types" } } diff --git a/packages/tracing/.npmignore b/packages/tracing/.npmignore deleted file mode 100644 index 37cb0bef4d30..000000000000 --- a/packages/tracing/.npmignore +++ /dev/null @@ -1,15 +0,0 @@ -# The paths in this file are specified so that they align with the file structure in `./build` after this file is copied -# into it by the prepack script `scripts/prepack.ts`. - -* - -!/cjs/**/* -!/esm/**/* -!/types/**/* - -# These paths are necessary for Node AWS Lambda layer creation -# This package is a (transitive) dependency of @sentry/serverless and thus it is pulled into -# the lambda layer zip file. -!/build/npm/cjs/**/* -!/build/npm/esm/**/* -!/build/npm/types/**/* diff --git a/packages/types/.npmignore b/packages/types/.npmignore deleted file mode 100644 index 7e33b95686a8..000000000000 --- a/packages/types/.npmignore +++ /dev/null @@ -1,13 +0,0 @@ -# The paths in this file are specified so that they align with the file structure in `./build` after this file is copied -# into it by the prepack script `scripts/prepack.ts`. - -* - -!/cjs/**/* -!/esm/**/* -!/types/**/* - -# These paths are necessary for @sentry/serverless AWS Lambda Layer creation -!/build/cjs/**/* -!/build/esm/**/* -!/build/types/**/* diff --git a/packages/utils/.npmignore b/packages/utils/.npmignore deleted file mode 100644 index 329293958886..000000000000 --- a/packages/utils/.npmignore +++ /dev/null @@ -1,15 +0,0 @@ -# The paths in this file are specified so that they align with the file structure in `./build` after this file is copied -# into it by the prepack script `scripts/prepack.ts`. - -* - -!/cjs/**/* -!/esm/**/* -!/types/**/* - -# These paths are necessary for Node AWS Lambda layer creation -# This package is a (transitive) dependency of @sentry/serverless and thus it is pulled into -# the lambda layer zip file. -!/build/cjs/**/* -!/build/esm/**/* -!/build/types/**/* diff --git a/scripts/aws-deploy-local-layer.sh b/scripts/aws-deploy-local-layer.sh new file mode 100755 index 000000000000..b86dcd344d78 --- /dev/null +++ b/scripts/aws-deploy-local-layer.sh @@ -0,0 +1,66 @@ +#!/usr/bin/env bash +# +# Builds and deploys the Sentry AWS Lambda layer (including the Sentry SDK and the Sentry Lambda Extension) +# +# The currently checked out version of the SDK in your local directory is used. +# The latest version of the Lambda Extension is fetched from the Sentry Release Registry. +# +# Note: While we normally try to write all of our scripts in TS, this is in bash because it's meant to exactly mirror +# what the lambda-zipping GHA is doing (see https://github.com/getsentry/action-build-aws-lambda-extension) + +set -euo pipefail + +# Cleanup +echo "Preparing local directories for new build..." +rm -rf dist-serverless/ +rm -rf ./packages/serverless/build +rm -rf ./packages/serverless/dist +rm -rf ./packages/serverless/node_modules +rm -f sentry-node-serverless-*.zip + +# Creating Lambda layer +echo "Creating Lambda layer in ./packages/serverless/build/aws/dist-serverless..." +cd packages/serverless +yarn build +cd ../../ +echo "Done creating Lambda layer in ./packages/serverless/build/aws/dist-serverless." + +# Move dist-serverless/ to the root folder for the action to pick it up. +# This is only needed in this script, because in GitHub workflow +# this is done with the upload-artifact/download-artifact actions +echo "Copying Lambda layer in ./packages/serverless/build/aws/dist-serverless to working directory..." +mv ./packages/serverless/build/aws/dist-serverless . +echo "Done copying Lambda layer in ./packages/serverless/build/aws/dist-serverless to working directory." + +# IMPORTANT: +# Please make sure that this does the same as the GitHub action that +# is building the Lambda layer in production! +# see: https://github.com/getsentry/action-build-aws-lambda-extension/blob/main/action.yml#L23-L40 + +# Adding Sentry Lambda extension to Lambda layer +echo "Adding Sentry Lambda extension to Lambda layer in ./dist-serverless..." +mkdir -p dist-serverless/extensions +curl -0 --silent --output dist-serverless/extensions/sentry-lambda-extension $(curl -s https://release-registry.services.sentry.io/apps/sentry-lambda-extension/latest | jq -r .files.\"sentry-lambda-extension\".url) +chmod +x dist-serverless/extensions/sentry-lambda-extension +echo "Done adding Sentry Lambda extension to Lambda layer in ./dist-serverless." + +# Zip Lambda layer and included Lambda extension +echo "Zipping Lambda layer and included Lambda extension..." +cd dist-serverless/ +zip -r -y ../sentry-node-serverless-x.x.x-dev.zip . +cd .. +echo "Done Zipping Lambda layer and included Lambda extension to ./sentry-node-serverless-x.x.x-dev.zip." + +# Deploying zipped Lambda layer to AWS +echo "Deploying zipped Lambda layer to AWS..." + +aws lambda publish-layer-version \ + --layer-name "SentryNodeServerlessSDK-local-dev" \ + --region "eu-central-1" \ + --zip-file "fileb://sentry-node-serverless-x.x.x-dev.zip" \ + --description "Local test build of SentryNodeServerlessSDK (can be deleted)" \ + --no-cli-pager + +echo "Done deploying zipped Lambda layer to AWS as 'SentryNodeServerlessSDK-local-dev'." + +echo "All done. Have a nice day!" diff --git a/scripts/ensure-bundle-deps.ts b/scripts/ensure-bundle-deps.ts index 61e724fc0029..06bdaf863582 100644 --- a/scripts/ensure-bundle-deps.ts +++ b/scripts/ensure-bundle-deps.ts @@ -7,7 +7,10 @@ import * as util from 'util'; /** * Ensure that `build:bundle` has all of the dependencies it needs to run. Works at both the repo and package level. */ -async function ensureBundleBuildPrereqs(options: { dependencies: string[]; maxRetries?: number }): Promise { +export async function ensureBundleBuildPrereqs(options: { + dependencies: string[]; + maxRetries?: number; +}): Promise { const { maxRetries = 12, dependencies } = options; const { From 2b36cabe68c11f8e9d673d0eb9044dc0deea72e4 Mon Sep 17 00:00:00 2001 From: Luca Forstner Date: Wed, 25 May 2022 17:26:03 +0200 Subject: [PATCH 200/204] feat(nextjs): Make tracing package treeshakable (#5166) --- MIGRATION.md | 2 ++ .../sentry-performance.ts | 17 ++++++++- packages/nextjs/src/index.client.ts | 35 ++++++++++--------- .../tracing/src/browser/browsertracing.ts | 12 ++++--- packages/tracing/src/browser/index.ts | 2 +- packages/tracing/src/index.ts | 12 +++++-- packages/tracing/test/index.bundle.test.ts | 9 ++++- packages/tracing/test/index.test.ts | 9 ++++- 8 files changed, 70 insertions(+), 28 deletions(-) diff --git a/MIGRATION.md b/MIGRATION.md index d6aad039ecaf..58b8e754ded3 100644 --- a/MIGRATION.md +++ b/MIGRATION.md @@ -351,6 +351,8 @@ For our efforts to reduce bundle size of the SDK we had to remove and refactor p - Removed `eventStatusFromHttpCode` to save on bundle size. - Replace `BrowserTracing` `maxTransactionDuration` option with `finalTimeout` option - Removed `ignoreSentryErrors` option from AWS lambda SDK. Errors originating from the SDK will now *always* be caught internally. +- Removed `Integrations.BrowserTracing` export from `@sentry/nextjs`. Please import `BrowserTracing` from `@sentry/nextjs` directly. +- Removed static `id` property from `BrowserTracing` integration. ## Sentry Angular SDK Changes diff --git a/packages/ember/addon/instance-initializers/sentry-performance.ts b/packages/ember/addon/instance-initializers/sentry-performance.ts index 5bb3024f7df6..8f992f8716cc 100644 --- a/packages/ember/addon/instance-initializers/sentry-performance.ts +++ b/packages/ember/addon/instance-initializers/sentry-performance.ts @@ -380,7 +380,22 @@ export async function instrumentForPerformance(appInstance: ApplicationInstance) }), ]; - if (isTesting() && Sentry.getCurrentHub()?.getIntegration(tracing.Integrations.BrowserTracing)) { + class FakeBrowserTracingClass { + static id = tracing.BROWSER_TRACING_INTEGRATION_ID; + public name = FakeBrowserTracingClass.id; + setupOnce() { + // noop - We're just faking this class for a lookup + } + } + + if ( + isTesting() && + Sentry.getCurrentHub()?.getIntegration( + // This is a temporary hack because the BrowserTracing integration cannot have a static `id` field for tree + // shaking reasons. However, `getIntegration` needs that field. + FakeBrowserTracingClass, + ) + ) { // Initializers are called more than once in tests, causing the integrations to not be setup correctly. return; } diff --git a/packages/nextjs/src/index.client.ts b/packages/nextjs/src/index.client.ts index 05b4dc64a3f3..b5993fb734ab 100644 --- a/packages/nextjs/src/index.client.ts +++ b/packages/nextjs/src/index.client.ts @@ -1,4 +1,4 @@ -import { configureScope, init as reactInit, Integrations as BrowserIntegrations } from '@sentry/react'; +import { configureScope, init as reactInit, Integrations } from '@sentry/react'; import { BrowserTracing, defaultRequestInstrumentationOptions } from '@sentry/tracing'; import { EventProcessor } from '@sentry/types'; @@ -10,12 +10,8 @@ import { addIntegration, UserIntegrations } from './utils/userIntegrations'; export * from '@sentry/react'; export { nextRouterInstrumentation } from './performance/client'; -export const Integrations = { ...BrowserIntegrations, BrowserTracing }; +export { Integrations }; -// This is already exported as part of `Integrations` above (and for the moment will remain so for -// backwards compatibility), but that interferes with treeshaking, so we also export it separately -// here. -// // Previously we expected users to import `BrowserTracing` like this: // // import { Integrations } from '@sentry/nextjs'; @@ -28,16 +24,23 @@ export const Integrations = { ...BrowserIntegrations, BrowserTracing }; // const instance = new BrowserTracing(); export { BrowserTracing }; +// Treeshakable guard to remove all code related to tracing +declare const __SENTRY_TRACING__: boolean; + /** Inits the Sentry NextJS SDK on the browser with the React SDK. */ export function init(options: NextjsOptions): void { buildMetadata(options, ['nextjs', 'react']); options.environment = options.environment || process.env.NODE_ENV; - // Only add BrowserTracing if a tracesSampleRate or tracesSampler is set - const integrations = - options.tracesSampleRate === undefined && options.tracesSampler === undefined - ? options.integrations - : createClientIntegrations(options.integrations); + let integrations = options.integrations; + + // Guard below evaluates to true unless __SENTRY_TRACING__ is text-replaced with "false" + if (typeof __SENTRY_TRACING__ === 'undefined' || __SENTRY_TRACING__) { + // Only add BrowserTracing if a tracesSampleRate or tracesSampler is set + if (options.tracesSampleRate !== undefined || options.tracesSampler !== undefined) { + integrations = createClientIntegrations(options.integrations); + } + } reactInit({ ...options, @@ -53,12 +56,12 @@ export function init(options: NextjsOptions): void { }); } -const defaultBrowserTracingIntegration = new BrowserTracing({ - tracingOrigins: [...defaultRequestInstrumentationOptions.tracingOrigins, /^(api\/)/], - routingInstrumentation: nextRouterInstrumentation, -}); - function createClientIntegrations(integrations?: UserIntegrations): UserIntegrations { + const defaultBrowserTracingIntegration = new BrowserTracing({ + tracingOrigins: [...defaultRequestInstrumentationOptions.tracingOrigins, /^(api\/)/], + routingInstrumentation: nextRouterInstrumentation, + }); + if (integrations) { return addIntegration(defaultBrowserTracingIntegration, integrations, { BrowserTracing: { keyPath: 'options.routingInstrumentation', value: nextRouterInstrumentation }, diff --git a/packages/tracing/src/browser/browsertracing.ts b/packages/tracing/src/browser/browsertracing.ts index 1872a08d728f..8995775a8a41 100644 --- a/packages/tracing/src/browser/browsertracing.ts +++ b/packages/tracing/src/browser/browsertracing.ts @@ -15,6 +15,8 @@ import { } from './request'; import { instrumentRoutingWithDefaults } from './router'; +export const BROWSER_TRACING_INTEGRATION_ID = 'BrowserTracing'; + /** Options for Browser Tracing integration */ export interface BrowserTracingOptions extends RequestInstrumentationOptions { /** @@ -111,10 +113,10 @@ const DEFAULT_BROWSER_TRACING_OPTIONS = { * any routing library. This integration uses {@see IdleTransaction} to create transactions. */ export class BrowserTracing implements Integration { - /** - * @inheritDoc - */ - public static id: string = 'BrowserTracing'; + // This class currently doesn't have a static `id` field like the other integration classes, because it prevented + // @sentry/tracing from being treeshaken. Tree shakers do not like static fields, because they behave like side effects. + // TODO: Come up with a better plan, than using static fields on integration classes, and use that plan on all + // integrations. /** Browser Tracing integration options */ public options: BrowserTracingOptions; @@ -122,7 +124,7 @@ export class BrowserTracing implements Integration { /** * @inheritDoc */ - public name: string = BrowserTracing.id; + public name: string = BROWSER_TRACING_INTEGRATION_ID; private _getCurrentHub?: () => Hub; diff --git a/packages/tracing/src/browser/index.ts b/packages/tracing/src/browser/index.ts index 2f7a3fe0d522..dcf4a08270fd 100644 --- a/packages/tracing/src/browser/index.ts +++ b/packages/tracing/src/browser/index.ts @@ -1,4 +1,4 @@ export type { RequestInstrumentationOptions } from './request'; -export { BrowserTracing } from './browsertracing'; +export { BrowserTracing, BROWSER_TRACING_INTEGRATION_ID } from './browsertracing'; export { instrumentOutgoingRequests, defaultRequestInstrumentationOptions } from './request'; diff --git a/packages/tracing/src/index.ts b/packages/tracing/src/index.ts index 0085ad5f6b0a..14d80fb7b4cc 100644 --- a/packages/tracing/src/index.ts +++ b/packages/tracing/src/index.ts @@ -22,7 +22,7 @@ export { Integrations }; // const instance = new BrowserTracing(); // // For an example of of the new usage of BrowserTracing, see @sentry/nextjs index.client.ts -export { BrowserTracing } from './browser'; +export { BrowserTracing, BROWSER_TRACING_INTEGRATION_ID } from './browser'; export { Span, spanStatusfromHttpCode } from './span'; // eslint-disable-next-line deprecation/deprecation @@ -32,8 +32,14 @@ export { instrumentOutgoingRequests, defaultRequestInstrumentationOptions } from export { IdleTransaction } from './idletransaction'; export { startIdleTransaction } from './hubextensions'; -// We are patching the global object with our hub extension methods -addExtensionMethods(); +// Treeshakable guard to remove all code related to tracing +declare const __SENTRY_TRACING__: boolean; + +// Guard for tree +if (typeof __SENTRY_TRACING__ === 'undefined' || __SENTRY_TRACING__) { + // We are patching the global object with our hub extension methods + addExtensionMethods(); +} export { addExtensionMethods }; diff --git a/packages/tracing/test/index.bundle.test.ts b/packages/tracing/test/index.bundle.test.ts index 319daa826ce6..91f643128d21 100644 --- a/packages/tracing/test/index.bundle.test.ts +++ b/packages/tracing/test/index.bundle.test.ts @@ -3,7 +3,14 @@ import { Integrations } from '../src/index.bundle'; describe('Integrations export', () => { it('is exported correctly', () => { Object.keys(Integrations).forEach(key => { - expect(Integrations[key as keyof typeof Integrations].id).toStrictEqual(expect.any(String)); + // Skip BrowserTracing because it doesn't have a static id field. + if (key === 'BrowserTracing') { + return; + } + + expect(Integrations[key as keyof Omit].id).toStrictEqual( + expect.any(String), + ); }); }); }); diff --git a/packages/tracing/test/index.test.ts b/packages/tracing/test/index.test.ts index 8837e2063cc7..c01b8cad0b54 100644 --- a/packages/tracing/test/index.test.ts +++ b/packages/tracing/test/index.test.ts @@ -12,7 +12,14 @@ describe('index', () => { describe('Integrations', () => { it('is exported correctly', () => { Object.keys(Integrations).forEach(key => { - expect(Integrations[key as keyof typeof Integrations].id).toStrictEqual(expect.any(String)); + // Skip BrowserTracing because it doesn't have a static id field. + if (key === 'BrowserTracing') { + return; + } + + expect(Integrations[key as keyof Omit].id).toStrictEqual( + expect.any(String), + ); }); }); From 1823003f7544e1d6be9ac465916a39d6b50d8757 Mon Sep 17 00:00:00 2001 From: Luca Forstner Date: Wed, 25 May 2022 17:33:37 +0200 Subject: [PATCH 201/204] meta: Update 7.0.0-rc.0 CHANGELOG (#5165) Co-authored-by: Lukas Stracke --- CHANGELOG.md | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index d5de078c2c39..2fa2f7ba7132 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,16 @@ - "You miss 100 percent of the chances you don't take. — Wayne Gretzky" — Michael Scott +## 7.0.0-rc.0 + +- **(breaking)** feat(nextjs): Make tracing package treeshakable (#5166) +- fix(browser): Fix memory leak in `addEventListener` instrumentation (#5147) +- fix(tracing): Don't use `querySelector` when not available (#5160) +- fix(utils): Fix infinite recursion in `dropUndefinedKeys` (#5163) +- ref(build): Use rollup to build AWS lambda layer (#5146) +- ref(core): Actually ensure attachments are added to the envelope (#5159) +- ref(core): Make hint callback argument non-optional (#5141) + ## 7.0.0-beta.2 - **(breaking)** feat(tracing): Add empty baggage header propagation to outgoing requests (#5133) From 8f0cb525fdeba19d6782cec94c938b2ce4a80156 Mon Sep 17 00:00:00 2001 From: getsentry-bot Date: Wed, 25 May 2022 15:37:18 +0000 Subject: [PATCH 202/204] release: 7.0.0-rc.0 --- lerna.json | 2 +- packages/angular/package.json | 8 ++++---- packages/browser/package.json | 8 ++++---- packages/core/package.json | 8 ++++---- packages/core/src/version.ts | 2 +- packages/ember/package.json | 10 +++++----- packages/eslint-config-sdk/package.json | 6 +++--- packages/eslint-plugin-sdk/package.json | 2 +- packages/gatsby/package.json | 10 +++++----- packages/hub/package.json | 6 +++--- packages/integration-tests/package.json | 2 +- packages/integrations/package.json | 6 +++--- packages/nextjs/package.json | 18 +++++++++--------- packages/node-integration-tests/package.json | 2 +- packages/node/package.json | 10 +++++----- packages/react/package.json | 8 ++++---- packages/serverless/package.json | 10 +++++----- packages/tracing/package.json | 10 +++++----- packages/types/package.json | 2 +- packages/typescript/package.json | 2 +- packages/utils/package.json | 4 ++-- packages/vue/package.json | 10 +++++----- packages/wasm/package.json | 8 ++++---- 23 files changed, 77 insertions(+), 77 deletions(-) diff --git a/lerna.json b/lerna.json index 3cb0ed37c32a..5249c4e4ee7b 100644 --- a/lerna.json +++ b/lerna.json @@ -1,6 +1,6 @@ { "lerna": "3.4.0", - "version": "7.0.0-beta.2", + "version": "7.0.0-rc.0", "packages": "packages/*", "npmClient": "yarn", "useWorkspaces": true diff --git a/packages/angular/package.json b/packages/angular/package.json index 5d2ac98b5db5..18c73fbc8422 100644 --- a/packages/angular/package.json +++ b/packages/angular/package.json @@ -1,6 +1,6 @@ { "name": "@sentry/angular", - "version": "7.0.0-beta.2", + "version": "7.0.0-rc.0", "description": "Official Sentry SDK for Angular", "repository": "git://github.com/getsentry/sentry-javascript.git", "homepage": "https://github.com/getsentry/sentry-javascript/tree/master/packages/angular", @@ -21,9 +21,9 @@ "rxjs": "^6.5.5 || ^7.x" }, "dependencies": { - "@sentry/browser": "7.0.0-beta.2", - "@sentry/types": "7.0.0-beta.2", - "@sentry/utils": "7.0.0-beta.2", + "@sentry/browser": "7.0.0-rc.0", + "@sentry/types": "7.0.0-rc.0", + "@sentry/utils": "7.0.0-rc.0", "tslib": "^2.0.0" }, "devDependencies": { diff --git a/packages/browser/package.json b/packages/browser/package.json index db3830e9a94e..571e55071f82 100644 --- a/packages/browser/package.json +++ b/packages/browser/package.json @@ -1,6 +1,6 @@ { "name": "@sentry/browser", - "version": "7.0.0-beta.2", + "version": "7.0.0-rc.0", "description": "Official Sentry SDK for browsers", "repository": "git://github.com/getsentry/sentry-javascript.git", "homepage": "https://github.com/getsentry/sentry-javascript/tree/master/packages/browser", @@ -16,9 +16,9 @@ "access": "public" }, "dependencies": { - "@sentry/core": "7.0.0-beta.2", - "@sentry/types": "7.0.0-beta.2", - "@sentry/utils": "7.0.0-beta.2", + "@sentry/core": "7.0.0-rc.0", + "@sentry/types": "7.0.0-rc.0", + "@sentry/utils": "7.0.0-rc.0", "tslib": "^1.9.3" }, "devDependencies": { diff --git a/packages/core/package.json b/packages/core/package.json index 575a10f48d5d..451937a7f2fd 100644 --- a/packages/core/package.json +++ b/packages/core/package.json @@ -1,6 +1,6 @@ { "name": "@sentry/core", - "version": "7.0.0-beta.2", + "version": "7.0.0-rc.0", "description": "Base implementation for all Sentry JavaScript SDKs", "repository": "git://github.com/getsentry/sentry-javascript.git", "homepage": "https://github.com/getsentry/sentry-javascript/tree/master/packages/core", @@ -16,9 +16,9 @@ "access": "public" }, "dependencies": { - "@sentry/hub": "7.0.0-beta.2", - "@sentry/types": "7.0.0-beta.2", - "@sentry/utils": "7.0.0-beta.2", + "@sentry/hub": "7.0.0-rc.0", + "@sentry/types": "7.0.0-rc.0", + "@sentry/utils": "7.0.0-rc.0", "tslib": "^1.9.3" }, "scripts": { diff --git a/packages/core/src/version.ts b/packages/core/src/version.ts index 003c7862c691..6a67171e89be 100644 --- a/packages/core/src/version.ts +++ b/packages/core/src/version.ts @@ -1 +1 @@ -export const SDK_VERSION = '7.0.0-beta.2'; +export const SDK_VERSION = '7.0.0-rc.0'; diff --git a/packages/ember/package.json b/packages/ember/package.json index 113449308fc4..559f6be775fb 100644 --- a/packages/ember/package.json +++ b/packages/ember/package.json @@ -1,6 +1,6 @@ { "name": "@sentry/ember", - "version": "7.0.0-beta.2", + "version": "7.0.0-rc.0", "description": "Official Sentry SDK for Ember.js", "repository": "git://github.com/getsentry/sentry-javascript.git", "homepage": "https://github.com/getsentry/sentry-javascript/tree/master/packages/ember", @@ -30,10 +30,10 @@ }, "dependencies": { "@embroider/macros": "~0.47.2", - "@sentry/browser": "7.0.0-beta.2", - "@sentry/tracing": "7.0.0-beta.2", - "@sentry/types": "7.0.0-beta.2", - "@sentry/utils": "7.0.0-beta.2", + "@sentry/browser": "7.0.0-rc.0", + "@sentry/tracing": "7.0.0-rc.0", + "@sentry/types": "7.0.0-rc.0", + "@sentry/utils": "7.0.0-rc.0", "ember-auto-import": "~1.12.1 || ~2.2.0", "ember-cli-babel": "~7.26.6", "ember-cli-htmlbars": "^6.0.1", diff --git a/packages/eslint-config-sdk/package.json b/packages/eslint-config-sdk/package.json index 0fe408ad74de..c742e3b78e3e 100644 --- a/packages/eslint-config-sdk/package.json +++ b/packages/eslint-config-sdk/package.json @@ -1,6 +1,6 @@ { "name": "@sentry-internal/eslint-config-sdk", - "version": "7.0.0-beta.2", + "version": "7.0.0-rc.0", "description": "Official Sentry SDK eslint config", "repository": "git://github.com/getsentry/sentry-javascript.git", "homepage": "https://github.com/getsentry/sentry-javascript/tree/master/packages/eslint-config-sdk", @@ -19,8 +19,8 @@ "access": "public" }, "dependencies": { - "@sentry-internal/eslint-plugin-sdk": "7.0.0-beta.2", - "@sentry-internal/typescript": "7.0.0-beta.2", + "@sentry-internal/eslint-plugin-sdk": "7.0.0-rc.0", + "@sentry-internal/typescript": "7.0.0-rc.0", "@typescript-eslint/eslint-plugin": "^3.9.0", "@typescript-eslint/parser": "^3.9.0", "eslint-config-prettier": "^6.11.0", diff --git a/packages/eslint-plugin-sdk/package.json b/packages/eslint-plugin-sdk/package.json index 514e9f742729..0682c3510655 100644 --- a/packages/eslint-plugin-sdk/package.json +++ b/packages/eslint-plugin-sdk/package.json @@ -1,6 +1,6 @@ { "name": "@sentry-internal/eslint-plugin-sdk", - "version": "7.0.0-beta.2", + "version": "7.0.0-rc.0", "description": "Official Sentry SDK eslint plugin", "repository": "git://github.com/getsentry/sentry-javascript.git", "homepage": "https://github.com/getsentry/sentry-javascript/tree/master/packages/eslint-plugin-sdk", diff --git a/packages/gatsby/package.json b/packages/gatsby/package.json index eddf17c56b55..dfed5b0d6352 100644 --- a/packages/gatsby/package.json +++ b/packages/gatsby/package.json @@ -1,6 +1,6 @@ { "name": "@sentry/gatsby", - "version": "7.0.0-beta.2", + "version": "7.0.0-rc.0", "description": "Official Sentry SDK for Gatsby.js", "repository": "git://github.com/getsentry/sentry-javascript.git", "homepage": "https://github.com/getsentry/sentry-javascript/tree/master/packages/gatsby", @@ -20,10 +20,10 @@ "access": "public" }, "dependencies": { - "@sentry/react": "7.0.0-beta.2", - "@sentry/tracing": "7.0.0-beta.2", - "@sentry/types": "7.0.0-beta.2", - "@sentry/utils": "7.0.0-beta.2", + "@sentry/react": "7.0.0-rc.0", + "@sentry/tracing": "7.0.0-rc.0", + "@sentry/types": "7.0.0-rc.0", + "@sentry/utils": "7.0.0-rc.0", "@sentry/webpack-plugin": "1.18.9" }, "peerDependencies": { diff --git a/packages/hub/package.json b/packages/hub/package.json index c621fa01733f..1f5b488ad26f 100644 --- a/packages/hub/package.json +++ b/packages/hub/package.json @@ -1,6 +1,6 @@ { "name": "@sentry/hub", - "version": "7.0.0-beta.2", + "version": "7.0.0-rc.0", "description": "Sentry hub which handles global state managment.", "repository": "git://github.com/getsentry/sentry-javascript.git", "homepage": "https://github.com/getsentry/sentry-javascript/tree/master/packages/hub", @@ -16,8 +16,8 @@ "access": "public" }, "dependencies": { - "@sentry/types": "7.0.0-beta.2", - "@sentry/utils": "7.0.0-beta.2", + "@sentry/types": "7.0.0-rc.0", + "@sentry/utils": "7.0.0-rc.0", "tslib": "^1.9.3" }, "scripts": { diff --git a/packages/integration-tests/package.json b/packages/integration-tests/package.json index 7eebe9ebfa6a..fc69c3e4a72f 100644 --- a/packages/integration-tests/package.json +++ b/packages/integration-tests/package.json @@ -1,6 +1,6 @@ { "name": "@sentry-internal/browser-integration-tests", - "version": "7.0.0-beta.2", + "version": "7.0.0-rc.0", "main": "index.js", "license": "MIT", "engines": { diff --git a/packages/integrations/package.json b/packages/integrations/package.json index e6ed4bd9266c..491cbe9c35c8 100644 --- a/packages/integrations/package.json +++ b/packages/integrations/package.json @@ -1,6 +1,6 @@ { "name": "@sentry/integrations", - "version": "7.0.0-beta.2", + "version": "7.0.0-rc.0", "description": "Pluggable integrations that can be used to enhance JS SDKs", "repository": "git://github.com/getsentry/sentry-javascript.git", "homepage": "https://github.com/getsentry/sentry-javascript/tree/master/packages/integrations", @@ -16,8 +16,8 @@ "module": "build/npm/esm/index.js", "types": "build/npm/types/index.d.ts", "dependencies": { - "@sentry/types": "7.0.0-beta.2", - "@sentry/utils": "7.0.0-beta.2", + "@sentry/types": "7.0.0-rc.0", + "@sentry/utils": "7.0.0-rc.0", "localforage": "^1.8.1", "tslib": "^1.9.3" }, diff --git a/packages/nextjs/package.json b/packages/nextjs/package.json index c395afee3164..9acd4f1e3332 100644 --- a/packages/nextjs/package.json +++ b/packages/nextjs/package.json @@ -1,6 +1,6 @@ { "name": "@sentry/nextjs", - "version": "7.0.0-beta.2", + "version": "7.0.0-rc.0", "description": "Official Sentry SDK for Next.js", "repository": "git://github.com/getsentry/sentry-javascript.git", "homepage": "https://github.com/getsentry/sentry-javascript/tree/master/packages/nextjs", @@ -17,14 +17,14 @@ "access": "public" }, "dependencies": { - "@sentry/core": "7.0.0-beta.2", - "@sentry/hub": "7.0.0-beta.2", - "@sentry/integrations": "7.0.0-beta.2", - "@sentry/node": "7.0.0-beta.2", - "@sentry/react": "7.0.0-beta.2", - "@sentry/tracing": "7.0.0-beta.2", - "@sentry/types": "7.0.0-beta.2", - "@sentry/utils": "7.0.0-beta.2", + "@sentry/core": "7.0.0-rc.0", + "@sentry/hub": "7.0.0-rc.0", + "@sentry/integrations": "7.0.0-rc.0", + "@sentry/node": "7.0.0-rc.0", + "@sentry/react": "7.0.0-rc.0", + "@sentry/tracing": "7.0.0-rc.0", + "@sentry/types": "7.0.0-rc.0", + "@sentry/utils": "7.0.0-rc.0", "@sentry/webpack-plugin": "1.18.9", "tslib": "^1.9.3" }, diff --git a/packages/node-integration-tests/package.json b/packages/node-integration-tests/package.json index 20257a5e8b03..2ce973eceb4b 100644 --- a/packages/node-integration-tests/package.json +++ b/packages/node-integration-tests/package.json @@ -1,6 +1,6 @@ { "name": "@sentry-internal/node-integration-tests", - "version": "7.0.0-beta.2", + "version": "7.0.0-rc.0", "license": "MIT", "engines": { "node": ">=10" diff --git a/packages/node/package.json b/packages/node/package.json index 08ef7a9e3ffc..77b0826e374e 100644 --- a/packages/node/package.json +++ b/packages/node/package.json @@ -1,6 +1,6 @@ { "name": "@sentry/node", - "version": "7.0.0-beta.2", + "version": "7.0.0-rc.0", "description": "Official Sentry SDK for Node.js", "repository": "git://github.com/getsentry/sentry-javascript.git", "homepage": "https://github.com/getsentry/sentry-javascript/tree/master/packages/node", @@ -16,10 +16,10 @@ "access": "public" }, "dependencies": { - "@sentry/core": "7.0.0-beta.2", - "@sentry/hub": "7.0.0-beta.2", - "@sentry/types": "7.0.0-beta.2", - "@sentry/utils": "7.0.0-beta.2", + "@sentry/core": "7.0.0-rc.0", + "@sentry/hub": "7.0.0-rc.0", + "@sentry/types": "7.0.0-rc.0", + "@sentry/utils": "7.0.0-rc.0", "cookie": "^0.4.1", "https-proxy-agent": "^5.0.0", "lru_map": "^0.3.3", diff --git a/packages/react/package.json b/packages/react/package.json index 892e5a3b65e8..33b5beea3a4e 100644 --- a/packages/react/package.json +++ b/packages/react/package.json @@ -1,6 +1,6 @@ { "name": "@sentry/react", - "version": "7.0.0-beta.2", + "version": "7.0.0-rc.0", "description": "Official Sentry SDK for React.js", "repository": "git://github.com/getsentry/sentry-javascript.git", "homepage": "https://github.com/getsentry/sentry-javascript/tree/master/packages/react", @@ -16,9 +16,9 @@ "access": "public" }, "dependencies": { - "@sentry/browser": "7.0.0-beta.2", - "@sentry/types": "7.0.0-beta.2", - "@sentry/utils": "7.0.0-beta.2", + "@sentry/browser": "7.0.0-rc.0", + "@sentry/types": "7.0.0-rc.0", + "@sentry/utils": "7.0.0-rc.0", "hoist-non-react-statics": "^3.3.2", "tslib": "^1.9.3" }, diff --git a/packages/serverless/package.json b/packages/serverless/package.json index 5350d7396571..c2133f07297f 100644 --- a/packages/serverless/package.json +++ b/packages/serverless/package.json @@ -1,6 +1,6 @@ { "name": "@sentry/serverless", - "version": "7.0.0-beta.2", + "version": "7.0.0-rc.0", "description": "Official Sentry SDK for various serverless solutions", "repository": "git://github.com/getsentry/sentry-javascript.git", "homepage": "https://github.com/getsentry/sentry-javascript/tree/master/packages/serverless", @@ -16,10 +16,10 @@ "access": "public" }, "dependencies": { - "@sentry/node": "7.0.0-beta.2", - "@sentry/tracing": "7.0.0-beta.2", - "@sentry/types": "7.0.0-beta.2", - "@sentry/utils": "7.0.0-beta.2", + "@sentry/node": "7.0.0-rc.0", + "@sentry/tracing": "7.0.0-rc.0", + "@sentry/types": "7.0.0-rc.0", + "@sentry/utils": "7.0.0-rc.0", "@types/aws-lambda": "^8.10.62", "@types/express": "^4.17.2", "tslib": "^1.9.3" diff --git a/packages/tracing/package.json b/packages/tracing/package.json index 5798635420cc..ca22615d94e7 100644 --- a/packages/tracing/package.json +++ b/packages/tracing/package.json @@ -1,6 +1,6 @@ { "name": "@sentry/tracing", - "version": "7.0.0-beta.2", + "version": "7.0.0-rc.0", "description": "Extensions for Sentry AM", "repository": "git://github.com/getsentry/sentry-javascript.git", "homepage": "https://github.com/getsentry/sentry-javascript/tree/master/packages/tracing", @@ -16,13 +16,13 @@ "access": "public" }, "dependencies": { - "@sentry/hub": "7.0.0-beta.2", - "@sentry/types": "7.0.0-beta.2", - "@sentry/utils": "7.0.0-beta.2", + "@sentry/hub": "7.0.0-rc.0", + "@sentry/types": "7.0.0-rc.0", + "@sentry/utils": "7.0.0-rc.0", "tslib": "^1.9.3" }, "devDependencies": { - "@sentry/browser": "7.0.0-beta.2", + "@sentry/browser": "7.0.0-rc.0", "@types/express": "^4.17.1" }, "scripts": { diff --git a/packages/types/package.json b/packages/types/package.json index cab2efca3fcf..cb5fcb05967b 100644 --- a/packages/types/package.json +++ b/packages/types/package.json @@ -1,6 +1,6 @@ { "name": "@sentry/types", - "version": "7.0.0-beta.2", + "version": "7.0.0-rc.0", "description": "Types for all Sentry JavaScript SDKs", "repository": "git://github.com/getsentry/sentry-javascript.git", "homepage": "https://github.com/getsentry/sentry-javascript/tree/master/packages/types", diff --git a/packages/typescript/package.json b/packages/typescript/package.json index a9d88bf257bf..af30f54a0b02 100644 --- a/packages/typescript/package.json +++ b/packages/typescript/package.json @@ -1,6 +1,6 @@ { "name": "@sentry-internal/typescript", - "version": "7.0.0-beta.2", + "version": "7.0.0-rc.0", "description": "Typescript configuration used at Sentry", "repository": "git://github.com/getsentry/sentry-javascript.git", "homepage": "https://github.com/getsentry/sentry-javascript/tree/master/packages/typescript", diff --git a/packages/utils/package.json b/packages/utils/package.json index d1ae0ddf73e2..af3dbedd4340 100644 --- a/packages/utils/package.json +++ b/packages/utils/package.json @@ -1,6 +1,6 @@ { "name": "@sentry/utils", - "version": "7.0.0-beta.2", + "version": "7.0.0-rc.0", "description": "Utilities for all Sentry JavaScript SDKs", "repository": "git://github.com/getsentry/sentry-javascript.git", "homepage": "https://github.com/getsentry/sentry-javascript/tree/master/packages/utils", @@ -16,7 +16,7 @@ "access": "public" }, "dependencies": { - "@sentry/types": "7.0.0-beta.2", + "@sentry/types": "7.0.0-rc.0", "tslib": "^1.9.3" }, "devDependencies": { diff --git a/packages/vue/package.json b/packages/vue/package.json index 38bea2e2abb8..dc518d7a67a4 100644 --- a/packages/vue/package.json +++ b/packages/vue/package.json @@ -1,6 +1,6 @@ { "name": "@sentry/vue", - "version": "7.0.0-beta.2", + "version": "7.0.0-rc.0", "description": "Official Sentry SDK for Vue.js", "repository": "git://github.com/getsentry/sentry-javascript.git", "homepage": "https://github.com/getsentry/sentry-javascript/tree/master/packages/vue", @@ -16,10 +16,10 @@ "access": "public" }, "dependencies": { - "@sentry/browser": "7.0.0-beta.2", - "@sentry/core": "7.0.0-beta.2", - "@sentry/types": "7.0.0-beta.2", - "@sentry/utils": "7.0.0-beta.2", + "@sentry/browser": "7.0.0-rc.0", + "@sentry/core": "7.0.0-rc.0", + "@sentry/types": "7.0.0-rc.0", + "@sentry/utils": "7.0.0-rc.0", "tslib": "^1.9.3" }, "peerDependencies": { diff --git a/packages/wasm/package.json b/packages/wasm/package.json index 4588cb28568e..d92887004264 100644 --- a/packages/wasm/package.json +++ b/packages/wasm/package.json @@ -1,6 +1,6 @@ { "name": "@sentry/wasm", - "version": "7.0.0-beta.2", + "version": "7.0.0-rc.0", "description": "Support for WASM.", "repository": "git://github.com/getsentry/sentry-javascript.git", "homepage": "https://github.com/getsentry/sentry-javascript/tree/master/packages/wasm", @@ -16,9 +16,9 @@ "access": "public" }, "dependencies": { - "@sentry/browser": "7.0.0-beta.2", - "@sentry/types": "7.0.0-beta.2", - "@sentry/utils": "7.0.0-beta.2", + "@sentry/browser": "7.0.0-rc.0", + "@sentry/types": "7.0.0-rc.0", + "@sentry/utils": "7.0.0-rc.0", "tslib": "^1.9.3" }, "devDependencies": { From b0919ef1e60393b56c066f85733e9843007b5111 Mon Sep 17 00:00:00 2001 From: Tim Fish Date: Fri, 27 May 2022 10:54:04 +0100 Subject: [PATCH 203/204] ref(node): Allow node stack parser to work in browser context (#5135) --- packages/node/src/index.ts | 3 +- packages/node/src/module.ts | 36 ++++ packages/node/src/sdk.ts | 10 +- packages/node/src/stack-parser.ts | 166 +++++++----------- packages/node/test/context-lines.test.ts | 2 +- packages/node/test/index.test.ts | 2 +- .../test/integrations/linkederrors.test.ts | 2 +- packages/node/test/stacktrace.test.ts | 2 +- 8 files changed, 116 insertions(+), 107 deletions(-) create mode 100644 packages/node/src/module.ts diff --git a/packages/node/src/index.ts b/packages/node/src/index.ts index cea9a4554ccb..96e2383c6ec0 100644 --- a/packages/node/src/index.ts +++ b/packages/node/src/index.ts @@ -44,9 +44,8 @@ export { export { NodeClient } from './client'; export { makeNodeTransport } from './transports'; -export { defaultIntegrations, init, lastEventId, flush, close, getSentryRelease } from './sdk'; +export { defaultIntegrations, init, defaultStackParser, lastEventId, flush, close, getSentryRelease } from './sdk'; export { deepReadDirSync } from './utils'; -export { defaultStackParser } from './stack-parser'; import { Integrations as CoreIntegrations } from '@sentry/core'; import { getMainCarrier } from '@sentry/hub'; diff --git a/packages/node/src/module.ts b/packages/node/src/module.ts new file mode 100644 index 000000000000..15682d16d121 --- /dev/null +++ b/packages/node/src/module.ts @@ -0,0 +1,36 @@ +import { basename, dirname } from '@sentry/utils'; + +/** Gets the module from a filename */ +export function getModule(filename: string | undefined): string | undefined { + if (!filename) { + return; + } + + // We could use optional chaining here but webpack does like that mixed with require + const base = `${ + (require && require.main && require.main.filename && dirname(require.main.filename)) || global.process.cwd() + }/`; + + // It's specifically a module + const file = basename(filename, '.js'); + + const path = dirname(filename); + let n = path.lastIndexOf('/node_modules/'); + if (n > -1) { + // /node_modules/ is 14 chars + return `${path.substr(n + 14).replace(/\//g, '.')}:${file}`; + } + // Let's see if it's a part of the main module + // To be a part of main module, it has to share the same base + n = `${path}/`.lastIndexOf(base, 0); + + if (n === 0) { + let moduleName = path.substr(base.length).replace(/\//g, '.'); + if (moduleName) { + moduleName += ':'; + } + moduleName += file; + return moduleName; + } + return file; +} diff --git a/packages/node/src/sdk.ts b/packages/node/src/sdk.ts index 34ce79ef480b..e83320103cb6 100644 --- a/packages/node/src/sdk.ts +++ b/packages/node/src/sdk.ts @@ -1,13 +1,14 @@ import { getCurrentHub, getIntegrationsToSetup, initAndBind, Integrations as CoreIntegrations } from '@sentry/core'; import { getMainCarrier, setHubOnCarrier } from '@sentry/hub'; -import { SessionStatus } from '@sentry/types'; -import { getGlobalObject, logger, stackParserFromStackParserOptions } from '@sentry/utils'; +import { SessionStatus, StackParser } from '@sentry/types'; +import { createStackParser, getGlobalObject, logger, stackParserFromStackParserOptions } from '@sentry/utils'; import * as domain from 'domain'; import { NodeClient } from './client'; import { IS_DEBUG_BUILD } from './flags'; import { Console, ContextLines, Http, LinkedErrors, OnUncaughtException, OnUnhandledRejection } from './integrations'; -import { defaultStackParser } from './stack-parser'; +import { getModule } from './module'; +import { nodeStackLineParser } from './stack-parser'; import { makeNodeTransport } from './transports'; import { NodeClientOptions, NodeOptions } from './types'; @@ -232,6 +233,9 @@ export function getSentryRelease(fallback?: string): string | undefined { ); } +/** Node.js stack parser */ +export const defaultStackParser: StackParser = createStackParser(nodeStackLineParser(getModule)); + /** * Enable automatic Session Tracking for the node process. */ diff --git a/packages/node/src/stack-parser.ts b/packages/node/src/stack-parser.ts index ea7cb1468ca1..7fd8a5ee3abd 100644 --- a/packages/node/src/stack-parser.ts +++ b/packages/node/src/stack-parser.ts @@ -1,119 +1,89 @@ import { StackLineParser, StackLineParserFn } from '@sentry/types'; -import { basename, createStackParser, dirname } from '@sentry/utils'; - -/** Gets the module */ -function getModule(filename: string | undefined): string | undefined { - if (!filename) { - return; - } - - // We could use optional chaining here but webpack does like that mixed with require - const base = `${ - (require && require.main && require.main.filename && dirname(require.main.filename)) || global.process.cwd() - }/`; - - // It's specifically a module - const file = basename(filename, '.js'); - - const path = dirname(filename); - let n = path.lastIndexOf('/node_modules/'); - if (n > -1) { - // /node_modules/ is 14 chars - return `${path.substr(n + 14).replace(/\//g, '.')}:${file}`; - } - // Let's see if it's a part of the main module - // To be a part of main module, it has to share the same base - n = `${path}/`.lastIndexOf(base, 0); - - if (n === 0) { - let moduleName = path.substr(base.length).replace(/\//g, '.'); - if (moduleName) { - moduleName += ':'; - } - moduleName += file; - return moduleName; - } - return file; -} const FILENAME_MATCH = /^\s*[-]{4,}$/; const FULL_MATCH = /at (?:async )?(?:(.+?)\s+\()?(?:(.+?):(\d+)(?::(\d+))?|([^)]+))\)?/; +type GetModuleFn = (filename: string | undefined) => string | undefined; + // eslint-disable-next-line complexity -const node: StackLineParserFn = (line: string) => { - if (line.match(FILENAME_MATCH)) { - return { - filename: line, - }; - } +function node(getModule?: GetModuleFn): StackLineParserFn { + // eslint-disable-next-line complexity + return (line: string) => { + if (line.match(FILENAME_MATCH)) { + return { + filename: line, + }; + } - const lineMatch = line.match(FULL_MATCH); - if (!lineMatch) { - return undefined; - } + const lineMatch = line.match(FULL_MATCH); + if (!lineMatch) { + return undefined; + } - let object: string | undefined; - let method: string | undefined; - let functionName: string | undefined; - let typeName: string | undefined; - let methodName: string | undefined; + let object: string | undefined; + let method: string | undefined; + let functionName: string | undefined; + let typeName: string | undefined; + let methodName: string | undefined; - if (lineMatch[1]) { - functionName = lineMatch[1]; + if (lineMatch[1]) { + functionName = lineMatch[1]; - let methodStart = functionName.lastIndexOf('.'); - if (functionName[methodStart - 1] === '.') { - // eslint-disable-next-line no-plusplus - methodStart--; - } + let methodStart = functionName.lastIndexOf('.'); + if (functionName[methodStart - 1] === '.') { + // eslint-disable-next-line no-plusplus + methodStart--; + } - if (methodStart > 0) { - object = functionName.substr(0, methodStart); - method = functionName.substr(methodStart + 1); - const objectEnd = object.indexOf('.Module'); - if (objectEnd > 0) { - functionName = functionName.substr(objectEnd + 1); - object = object.substr(0, objectEnd); + if (methodStart > 0) { + object = functionName.substr(0, methodStart); + method = functionName.substr(methodStart + 1); + const objectEnd = object.indexOf('.Module'); + if (objectEnd > 0) { + functionName = functionName.substr(objectEnd + 1); + object = object.substr(0, objectEnd); + } } + typeName = undefined; } - typeName = undefined; - } - if (method) { - typeName = object; - methodName = method; - } + if (method) { + typeName = object; + methodName = method; + } - if (method === '') { - methodName = undefined; - functionName = undefined; - } + if (method === '') { + methodName = undefined; + functionName = undefined; + } - if (functionName === undefined) { - methodName = methodName || ''; - functionName = typeName ? `${typeName}.${methodName}` : methodName; - } + if (functionName === undefined) { + methodName = methodName || ''; + functionName = typeName ? `${typeName}.${methodName}` : methodName; + } - const filename = lineMatch[2]?.startsWith('file://') ? lineMatch[2].substr(7) : lineMatch[2]; - const isNative = lineMatch[5] === 'native'; - const isInternal = - isNative || (filename && !filename.startsWith('/') && !filename.startsWith('.') && filename.indexOf(':\\') !== 1); + const filename = lineMatch[2]?.startsWith('file://') ? lineMatch[2].substr(7) : lineMatch[2]; + const isNative = lineMatch[5] === 'native'; + const isInternal = + isNative || (filename && !filename.startsWith('/') && !filename.startsWith('.') && filename.indexOf(':\\') !== 1); - // in_app is all that's not an internal Node function or a module within node_modules - // note that isNative appears to return true even for node core libraries - // see https://github.com/getsentry/raven-node/issues/176 - const in_app = !isInternal && filename !== undefined && !filename.includes('node_modules/'); + // in_app is all that's not an internal Node function or a module within node_modules + // note that isNative appears to return true even for node core libraries + // see https://github.com/getsentry/raven-node/issues/176 + const in_app = !isInternal && filename !== undefined && !filename.includes('node_modules/'); - return { - filename, - module: getModule(filename), - function: functionName, - lineno: parseInt(lineMatch[3], 10) || undefined, - colno: parseInt(lineMatch[4], 10) || undefined, - in_app, + return { + filename, + module: getModule?.(filename), + function: functionName, + lineno: parseInt(lineMatch[3], 10) || undefined, + colno: parseInt(lineMatch[4], 10) || undefined, + in_app, + }; }; -}; - -export const nodeStackLineParser: StackLineParser = [90, node]; +} -export const defaultStackParser = createStackParser(nodeStackLineParser); +/** Node.js stack line parser */ +export function nodeStackLineParser(getModule?: GetModuleFn): StackLineParser { + return [90, node(getModule)]; +} diff --git a/packages/node/test/context-lines.test.ts b/packages/node/test/context-lines.test.ts index 446429fdff33..2b0fef8206b8 100644 --- a/packages/node/test/context-lines.test.ts +++ b/packages/node/test/context-lines.test.ts @@ -3,7 +3,7 @@ import * as fs from 'fs'; import { parseStackFrames } from '../src/eventbuilder'; import { ContextLines, resetFileContentCache } from '../src/integrations/contextlines'; -import { defaultStackParser } from '../src/stack-parser'; +import { defaultStackParser } from '../src/sdk'; import { getError } from './helper/error'; describe('ContextLines', () => { diff --git a/packages/node/test/index.test.ts b/packages/node/test/index.test.ts index b12bd23fdd17..7b127637778c 100644 --- a/packages/node/test/index.test.ts +++ b/packages/node/test/index.test.ts @@ -16,7 +16,7 @@ import { Scope, } from '../src'; import { ContextLines, LinkedErrors } from '../src/integrations'; -import { defaultStackParser } from '../src/stack-parser'; +import { defaultStackParser } from '../src/sdk'; import { getDefaultNodeClientOptions } from './helper/node-client-options'; jest.mock('@sentry/core', () => { diff --git a/packages/node/test/integrations/linkederrors.test.ts b/packages/node/test/integrations/linkederrors.test.ts index 549481d7299d..390a847eea34 100644 --- a/packages/node/test/integrations/linkederrors.test.ts +++ b/packages/node/test/integrations/linkederrors.test.ts @@ -2,7 +2,7 @@ import { ExtendedError } from '@sentry/types'; import { Event, NodeClient } from '../../src'; import { LinkedErrors } from '../../src/integrations/linkederrors'; -import { defaultStackParser as stackParser } from '../../src/stack-parser'; +import { defaultStackParser as stackParser } from '../../src/sdk'; import { getDefaultNodeClientOptions } from '../helper/node-client-options'; let linkedErrors: any; diff --git a/packages/node/test/stacktrace.test.ts b/packages/node/test/stacktrace.test.ts index 227f87991c8a..e84c436364ed 100644 --- a/packages/node/test/stacktrace.test.ts +++ b/packages/node/test/stacktrace.test.ts @@ -11,7 +11,7 @@ */ import { parseStackFrames } from '../src/eventbuilder'; -import { defaultStackParser as stackParser } from '../src/stack-parser'; +import { defaultStackParser as stackParser } from '../src/sdk'; function testBasic() { return new Error('something went wrong'); From e80ccdc4aefba581f8f38cd945f037133fb71230 Mon Sep 17 00:00:00 2001 From: Abhijeet Prasad Date: Mon, 30 May 2022 13:05:31 +0200 Subject: [PATCH 204/204] meta: 7.0.0 CHANGELOG (#5177) --- CHANGELOG.md | 189 ++++++++++++++++++++++++++------------------------- MIGRATION.md | 1 + 2 files changed, 97 insertions(+), 93 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 2fa2f7ba7132..63c97cf378dc 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,109 +4,112 @@ - "You miss 100 percent of the chances you don't take. — Wayne Gretzky" — Michael Scott -## 7.0.0-rc.0 +## 7.0.0 + +Version 7 of the Sentry JavaScript SDK brings a variety of features and fixes including bundle size and performance improvements, brand new integrations, support for the attachments API, and key bug fixes. + +This release does not change or remove any top level public API methods (`captureException`, `captureMessage`), and only requires changes to certain configuration options or custom clients/integrations/transports. + +**Note: The v7 version of the JavaScript SDK requires a self-hosted version of Sentry 20.6.0 or higher. If you are using a version of [self-hosted Sentry](https://develop.sentry.dev/self-hosted/) (aka onpremise) older than `20.6.0` then you will need to [upgrade](https://develop.sentry.dev/self-hosted/releases/).** + +For detailed overview of all the changes, please see our [v7 migration guide](./MIGRATION.md#upgrading-from-6x-to-7x). + +### Breaking Changes + +If you are a regular consumer of the Sentry JavaScript SDK you only need to focus on the general items. The internal breaking changes are aimed at libraries that build on top of and extend the JavaScript SDK (like [`@sentry/electron`](https://github.com/getsentry/sentry-electron/) or [`@sentry/react-native`](https://github.com/getsentry/sentry-react-native/)). + +#### General + +- [Updated CommonJS distributions to use ES6 by default](./MIGRATION.md#moving-to-es6-for-commonjs-files). If you need to support Internet Explorer 11 or old Node.js versions, we recommend using a preprocessing tool like [Babel](https://babeljs.io/) to convert Sentry packages to ES5. (#5005) +- Default `bundle.min.js` to ES6 instead of ES5. [ES5 bundles are still available at `bundle.es5.min.js`](./MIGRATION.md#renaming-of-cdn-bundles). (#4958) +- Updated build system to use TypeScript 3.8.3 (#4895) +- Deprecated `Severity` enum for bundle size reasons. [Please use string literals instead](./MIGRATION.md#severity-severitylevel-and-severitylevels). (#4926) +- Removed `critical` Severity level. (#5032) +- `whitelistUrls` and `blacklistUrls` have been renamed to `allowUrls` and `denyUrls` in the `Sentry.init()` options. (#4850) +- `BaseClient` and it's child classes now require `transport`, `stackParser`, and `integrations` to be [explicitly passed in](./MIGRATION.md#explicit-client-options). This was done to improve tree-shakability. (#4927) +- Updated package distribution structure and stopped distributing CDN bundles through `@sentry/*` npm packages. [See details in our migration docs.](./MIGRATION.md#restructuring-of-package-content). (#4900) (#4901) +- [Simplified `Transport` API](./MIGRATION.md#transport-changes). This means [custom transports will have to be adjusted accordingly.](./MIGRATION.md#custom-transports). +- Updated how [Node Transport Options are passed down](./MIGRATION.md#node-transport-changes). +- Start propogating [`baggage` HTTP header](https://www.w3.org/TR/baggage/) alongside `sentry-trace` header to [propogate additional tracing related information.](./MIGRATION.md#propagation-of-baggage-header). (#5133) +- Renamed `registerRequestInstrumentation` export to `instrumentOutgoingRequests` in `@sentry/tracing`. (#4859) +- Renamed `UserAgent` integration to `HttpContext`. (#5027) +- Replaced `BrowserTracing` integration's `maxTransactionDuration` option with `finalTimeout` option in the `@sentry/tracing` package and reset `idleTimeout` based on activities count. This should improve accuracy of web-vitals like LCP by 20-30%. (#5044) +- [Updated `@sentry/angular` to be compiled by the angular compiler](./MIGRATION.md#sentry-angular-sdk-changes). (#4641) +- Made tracing package treeshakable (#5166) + +- Removed support for [Node v6](./MIGRATION.md#dropping-support-for-nodejs-v6). (#4851) +- Removed `@sentry/minimal` package in favour of using [`@sentry/hub`](./MIGRATION.md#removal-of-sentryminimal). (#4971) +- Removed support for Opera browser pre v15 (#4923) +- Removed `ignoreSentryErrors` option from AWS lambda SDK. Errors originating from the SDK will now *always* be caught internally. (#4994) +- Removed `Integrations.BrowserTracing` export from `@sentry/nextjs`. Please import `BrowserTracing` from `@sentry/nextjs` directly. +- Removed static `id` property from `BrowserTracing` integration. +- Removed `SDK_NAME` export from `@sentry/browser`, `@sentry/node`, `@sentry/tracing` and `@sentry/vue` packages. (#5040) +- Removed `Angular`, `Ember`, and `Vue` integrations from `@sentry/integrations` [in favour of the explicit framework packages: `@sentry/angular`, `@sentry/ember`, and `@sentry/vue`](./MIGRATION.md#removal-of-old-platform-integrations-from-sentryintegrations-package). (#4893) +- Removed [enums `Status`, `RequestSessionStatus`, and `SessionStatus`.](./MIGRATION.md#removed-enums). Deprecated [enums `SpanStatus` and `Severity`](./MIGRATION.md#deprecated-enums). This was done to save on bundle size. (#4891) (#4889) (#4890) +- Removed support for deprecated `@sentry/apm` package. (#4845) +- Removed deprecated `user` field from DSN interface. `publicKey` should be used instead. (#4864) +- Removed deprecated `getActiveDomain` method and `DomainAsCarrier` type from `@sentry/hub`. (#4858) +- Removed `eventStatusFromHttpCode` to save on bundle size. +- Removed usage of deprecated `event.stacktrace` field. (#4885) +- Removed raven-node backward-compat code (#4942) +- Removed `showReportDialog` method on `BrowserClient` (#4973) +- Removed deprecated `startSpan` and `child` methods (#4849) +- Removed deprecated `frameContextLines` options (#4884) +- Removed `Sentry` from window in the Gatsby SDK (#4857) + +#### Internal + +- Removed support for the store endpoint (#4969) +- Made hint callback argument non-optional (#5141) +- Switched to using new transports internally (#4943) +- [Removed `API` class from `@sentry/core`.](./MIGRATION.md#removing-the-api-class-from-sentrycore). (#4848) +- [Refactored `Session` class to use a more functional approach.](./MIGRATION.md#session-changes). (#5054) +- Removed `Backend` class in favour of moving functionality into the `Client` class (for more details, see [#4911](https://github.com/getsentry/sentry-javascript/pull/4911) and [#4919](https://github.com/getsentry/sentry-javascript/pull/4919)). +- Removed forget async utility function (#4941) +- Removed tslint from `@sentry-internal/typescript` (#4940) +- Removed `_invokeClient` function from `@sentry/hub` (#4972) +- Removed top level eventbuilder exports (#4887) +- Added baggage API helpers in `@sentry/utils` (#5066) + +### Other Changes + +#### Features -- **(breaking)** feat(nextjs): Make tracing package treeshakable (#5166) -- fix(browser): Fix memory leak in `addEventListener` instrumentation (#5147) -- fix(tracing): Don't use `querySelector` when not available (#5160) -- fix(utils): Fix infinite recursion in `dropUndefinedKeys` (#5163) -- ref(build): Use rollup to build AWS lambda layer (#5146) -- ref(core): Actually ensure attachments are added to the envelope (#5159) -- ref(core): Make hint callback argument non-optional (#5141) - -## 7.0.0-beta.2 - -- **(breaking)** feat(tracing): Add empty baggage header propagation to outgoing requests (#5133) +- feat(tracing): Add Prisma ORM integration. (#4931) +- feat(react): Add react-router-v6 integration (#5042) - feat: Add attachments API (#5004) -- feat(core): Send Baggage in Envelope Header (#5104) -- feat(utils): Introduce Baggage API (#5066) -- fix(build): Fix express import in `gcpfunction` (#5097) -- fix(ember): Export sha hashes of injected scripts (#5089) -- ref(build): Use sucrase for es6 bundles (#5111) -- ref(serverless): Do not throw on flush error (#5090) -- ref(serverless): Point DSN to relay in lambda extension (#5126) -- ref(tracing): Rename baggage env header to trace (#5128) -## 7.0.0-beta.1 +- feat: Add `name` field to `EventProcessor` (#4932) +- feat: Expose configurable stack parser (#4902) +- feat(tracing): Make `setMeasurement` public API (#4933) +- feat(tracing): Add GB unit to device memory tag value (#4935) +- feat: Export browser integrations individually (#5028) +- feat(core): Send Baggage in Envelope Header (#5104) -- **(breaking)** ref: Remove critical severity (#5032) -- **(breaking)** ref: Delete unneeded SDK_NAME (#5040) -- **(breaking)** ref(browser): Rename UserAgent integration to HttpContext (#5027) -- **(breaking)** ref(hub): Convert Session class to object and functions (#5054) -- **(breaking)** ref(node): Explicitly pass down node transport options (#5057) -- **(breaking)** ref(tracing): Reset IdleTimeout based on activities count (#5044) -- ref(browser): Unify BrowserTransportOptions (#5058) -- feat(build): Vendor polyfills injected during build (#5051) -- ref(core): Log warning on NOK transport response (#5091) -- fix(integrations): Mark ExtraErrorData as already normalized (#5053) -- feat(react): Add react-router-v6 integration (#5042) -- fix(tracing): Remove isInstanceOf check in Hub constructor (#5046) -- fix(utils): Consider 429 responses in transports (#5062) -- ref(utils): Clean up dangerous type casts in object helper file (#5047) -- ref(utils): Add logic to enable skipping of normalization (#5052) +#### Fixes -## 7.0.0-beta.0 - -- **(breaking)**: ref: Make it easier to use stackParser (#5015) -- **(breaking)**: ref: Switch to new transports (#4943) -- **(breaking)**: ref: Delete store endpoint code (#4969) -- **(breaking)**: chore: set ignoreSentryErrors to true (#4994) -- **(breaking)**: fix(angular): Use Angular compiler to compile @sentry/angular (#4641) -- **(breaking)**: ref(browser): Remove showReportDialog on browser client (#4973) -- **(breaking)**: ref(build): Rename CDN bundles to be es6 per default (#4958) -- **(breaking)**: feat(core): Introduce separate client options (#4927) -- **(breaking)**: ref(core): Delete API Details (#4999) -- **(breaking)**: feat(hub): Remove _invokeClient (#4972) -- **(breaking)**: ref(minimal): Delete @sentry/minimal (#4971) -- **(breaking)**: ref(node): Remove raven-node backward-compat code (#4942) -- chore: Remove tslint from `@sentry-internal/typescript` (#4940) -- feat: Add client report hook to makeTransport (#5008) -- feat: Export browser integrations individually (#5028) -- ref(build): Switch tsconfig target to es6 (#5005) -- ref(core): Make event processing log warnings instead of errors (#5010) +- fix(browser): Fix memory leak in `addEventListener` instrumentation (#5147) +- fix(build): Fix express import in `gcpfunction` (#5097) +- fix(ember): Export sha hashes of injected scripts (#5089) - fix(hub): Add missing parameter to captureException docstring (#5001) -- fix(nextjs): Update webpack-plugin and change how cli binary is detected (#4988) +- fix(integrations): Mark ExtraErrorData as already normalized (#5053) - fix(serverless): Adjust v6 Lambda layer hotfix for v7 (#5006) - fix(tracing): Adjust sideEffects package.json entry for v7 (#4987) -- feat(tracing): Add GB unit to device memory tag value (#4935) -- feat(tracing): Add Prisma ORM integration. (#4931) -- ref(utils): Remove forget async utility function (#4941) +- fix(tracing): Remove isInstanceOf check in Hub constructor (#5046) +- fix(tracing): Don't use `querySelector` when not available (#5160) +- fix(nextjs): Update webpack-plugin and change how cli binary is detected. This should reduce bundle size of NextJS applications. (#4988) +- fix(utils): Fix infinite recursion in `dropUndefinedKeys` (#5163) -## 7.0.0-alpha.1 +#### Misc -- **(breaking)** ref: Inject Transports into Client (#4921) -- **(breaking)** ref: Port functionality from `Backend` to `Client` (#4911) -- **(breaking)** ref: Delete `Backend` classes (#4919) -- **(breaking)** ref(browser): Remove stack parser support for Opera pre v15 (#4923) -- **(breaking)** ref(various): Remove usage of deprecated `event.stacktrace` (#4885) -- feat: Add `name` field to `EventProcessor` (#4932) -- feat: Expose configurable stack parser (#4902) -- ref(build): Turn on `isolatedModules` TS option (#4896) -- feat(tracing): Make `setMeasurement` public API (#4933) -- ref(tracing): Update `setMeasurements` to only set a single measurement (#4920) -- ref(types): Stop using `Severity` enum (#4926) - -## 7.0.0-alpha.0 - -- **(breaking)** ref: Drop support for Node 6 (#4851) -- **(breaking)** ref(bundles): Stop publishing CDN bundles on npm (#4901) -- **(breaking)** ref(build): Rename `dist` directories to `cjs` (#4900) -- **(breaking)** ref(build): Update to TypeScript 3.8.3 (#4895) -- **(breaking)** ref(browser): Remove top level eventbuilder exports (#4887) -- **(breaking)** ref(core): Remove `whitelistUrls`/`blacklistUrls` (#4850) -- **(breaking)** ref(core): Delete `API` class (#4848) -- **(breaking)** ref(gatsby): Remove `Sentry` from window (#4857) -- **(breaking)** ref(hub): Remove `setTransaction` scope method (#4865) -- **(breaking)** ref(hub): Remove `getActiveDomain` (#4858) -- **(breaking)** ref(integrations): Remove old `angular`, `ember`, and `vue` integrations (#4893) -- **(breaking)** ref(node): Remove deprecated `frameContextLines` (#4884) -- **(breaking)** ref(tracing): Rename `registerRequestInstrumentation` -> `instrumentOutgoingRequests` (#4859) -- **(breaking)** ref(tracing): Delete deprecated `startSpan` and `child` methods (#4849) -- **(breaking)** ref(tracing): Remove references to `@sentry/apm` (#4845) -- **(breaking)** ref(types): Delete `SessionStatus` enum (#4890) -- **(breaking)** ref(types): Delete `RequestSessionStatus` enum (#4889) -- **(breaking)** ref(types): Remove deprecated `user` dsn field (#4864) -- **(breaking)** ref(types): Delete `Status` enum (#4891) +- feat(build): Vendor polyfills injected during build (#5051) +- ref(build): Use rollup to build AWS lambda layer (#5146) +- ref(core): Make event processing log warnings instead of errors (#5010) +- ref(node): Allow node stack parser to work in browser context (#5135) +- ref(serverless): Point DSN to relay in lambda extension (#5126) +- ref(serverless): Do not throw on flush error (#5090) +- ref(utils): Clean up dangerous type casts in object helper file (#5047) +- ref(utils): Add logic to enable skipping of normalization (#5052) ## 6.19.7 diff --git a/MIGRATION.md b/MIGRATION.md index 58b8e754ded3..aef8293fbc9a 100644 --- a/MIGRATION.md +++ b/MIGRATION.md @@ -353,6 +353,7 @@ For our efforts to reduce bundle size of the SDK we had to remove and refactor p - Removed `ignoreSentryErrors` option from AWS lambda SDK. Errors originating from the SDK will now *always* be caught internally. - Removed `Integrations.BrowserTracing` export from `@sentry/nextjs`. Please import `BrowserTracing` from `@sentry/nextjs` directly. - Removed static `id` property from `BrowserTracing` integration. +- Removed usage of deprecated `event.stacktrace` field ## Sentry Angular SDK Changes