@sentry/browser 11.5.0 → 11.6.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/build/npm/cjs/dev/client.js +34 -10
- package/build/npm/cjs/dev/client.js.map +1 -1
- package/build/npm/cjs/dev/profiling/utils.js +1 -4
- package/build/npm/cjs/dev/profiling/utils.js.map +1 -1
- package/build/npm/cjs/prod/client.js +34 -10
- package/build/npm/cjs/prod/client.js.map +1 -1
- package/build/npm/cjs/prod/profiling/utils.js +1 -4
- package/build/npm/cjs/prod/profiling/utils.js.map +1 -1
- package/build/npm/esm/dev/client.js +35 -11
- package/build/npm/esm/dev/client.js.map +1 -1
- package/build/npm/esm/dev/package.json +1 -1
- package/build/npm/esm/dev/profiling/utils.js +2 -5
- package/build/npm/esm/dev/profiling/utils.js.map +1 -1
- package/build/npm/esm/prod/client.js +35 -11
- package/build/npm/esm/prod/client.js.map +1 -1
- package/build/npm/esm/prod/package.json +1 -1
- package/build/npm/esm/prod/profiling/utils.js +2 -5
- package/build/npm/esm/prod/profiling/utils.js.map +1 -1
- package/build/npm/types/client.d.ts.map +1 -1
- package/build/npm/types/profiling/utils.d.ts.map +1 -1
- package/package.json +7 -7
|
@@ -30,18 +30,42 @@ class BrowserClient extends core.Client {
|
|
|
30
30
|
};
|
|
31
31
|
}
|
|
32
32
|
const { sendClientReports } = this._options;
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
33
|
+
const checkDrift = () => {
|
|
34
|
+
core.timestampInSeconds();
|
|
35
|
+
};
|
|
36
|
+
const listeners = [
|
|
37
|
+
// Flush buffered data when the page becomes hidden (e.g. tab switch, navigation, or the page
|
|
38
|
+
// being discarded). `flush()` emits the `flush` hook, which drains the span (streaming), log and
|
|
39
|
+
// metric buffers and hands the resulting envelopes to the transport (which uses `keepalive`).
|
|
40
|
+
// Client report outcomes don't listen to the `flush` hook, so we flush them separately.
|
|
41
|
+
[
|
|
42
|
+
helpers.WINDOW.document,
|
|
43
|
+
"visibilitychange",
|
|
44
|
+
() => {
|
|
45
|
+
checkDrift();
|
|
46
|
+
if (helpers.WINDOW.document.visibilityState === "hidden") {
|
|
47
|
+
if (sendClientReports) {
|
|
48
|
+
this._flushOutcomes();
|
|
49
|
+
}
|
|
50
|
+
queueMicrotask(() => {
|
|
51
|
+
void this.flush();
|
|
52
|
+
});
|
|
38
53
|
}
|
|
39
|
-
queueMicrotask(() => {
|
|
40
|
-
void this.flush();
|
|
41
|
-
});
|
|
42
54
|
}
|
|
43
|
-
|
|
44
|
-
|
|
55
|
+
],
|
|
56
|
+
// `freeze` and `resume` are only fired by Chromium browsers. Other browsers never fire them, so these listeners
|
|
57
|
+
// are no-ops there.
|
|
58
|
+
[helpers.WINDOW.document, "freeze", checkDrift],
|
|
59
|
+
[helpers.WINDOW.document, "resume", checkDrift],
|
|
60
|
+
// Pages restored from the back/forward cache were paused while they were cached.
|
|
61
|
+
[helpers.WINDOW, "pagehide", checkDrift],
|
|
62
|
+
[helpers.WINDOW, "pageshow", checkDrift]
|
|
63
|
+
];
|
|
64
|
+
listeners.forEach(([target, name, listener]) => target?.addEventListener?.(name, listener));
|
|
65
|
+
this.on(
|
|
66
|
+
"close",
|
|
67
|
+
() => listeners.forEach(([target, name, listener]) => target?.removeEventListener?.(name, listener))
|
|
68
|
+
);
|
|
45
69
|
if (userInfo) {
|
|
46
70
|
this.on("beforeSendSession", core.addAutoIpAddressToSession);
|
|
47
71
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"client.js","sources":["../../../../src/client.ts"],"sourcesContent":["import type {\n ClientOptions,\n Event,\n EventHint,\n Options as CoreOptions,\n ParameterizedString,\n Scope,\n SeverityLevel,\n} from '@sentry/core';\nimport type { BrowserClientReplayOptions } from '@sentry/core/browser';\nimport type { RouteProvider } from '@sentry/browser-utils';\nimport { setRouteProvider } from '@sentry/browser-utils';\nimport { addAutoIpAddressToSession, applySdkMetadata, Client, getSDKSource } from '@sentry/core';\nimport { eventFromException, eventFromMessage } from './eventbuilder';\nimport { WINDOW } from './helpers';\nimport type { BrowserTransportOptions } from './transports/types';\n\n/**\n * A magic string that build tooling can leverage in order to inject a release value into the SDK.\n */\ndeclare const __SENTRY_RELEASE__: string | undefined;\n\nexport type BrowserClientProfilingOptions = {\n /**\n * Sets profiling session sample rate for the entire profiling session.\n *\n * A profiling session corresponds to a user session, meaning it is set once at integration initialization and\n * persisted until the next page reload. This rate determines what percentage of user sessions will have profiling enabled.\n * @default 0\n */\n profileSessionSampleRate?: number;\n\n /**\n * Set the lifecycle mode of the profiler.\n * - **manual**: The profiler will be manually started and stopped via `startProfiler`/`stopProfiler`.\n * If a session is sampled, is dependent on the `profileSessionSampleRate`.\n * - **trace**: The profiler will be automatically started when a root span exists and stopped when there are no\n * more sampled root spans. Whether a session is sampled, is dependent on the `profileSessionSampleRate` and the\n * existing sampling configuration for tracing (`tracesSampleRate`/`tracesSampler`).\n *\n * @default 'manual'\n */\n profileLifecycle?: 'manual' | 'trace';\n};\n\ntype BrowserSpecificOptions = BrowserClientReplayOptions &\n BrowserClientProfilingOptions & {\n /** If configured, this URL will be used as base URL for lazy loading integration. */\n cdnBaseUrl?: string;\n\n /**\n * Important: Only set this option if you know what you are doing!\n *\n * By default, the SDK will check if `Sentry.init` is called in a browser extension.\n * In case it is, it will stop initialization and log a warning\n * because browser extensions require a different Sentry initialization process:\n * https://docs.sentry.io/platforms/javascript/best-practices/shared-environments/\n *\n * Setting up the SDK in a browser extension with global error monitoring is not recommended\n * and will likely flood you with errors from other web sites or extensions. This can heavily\n * impact your quota and cause interference with your and other Sentry SDKs in shared environments.\n *\n * If this check wrongfully flags your setup as a browser extension, you can set this\n * option to `true` to skip the check.\n *\n * @default false\n */\n skipBrowserExtensionCheck?: boolean;\n\n /**\n * If you use Spotlight by Sentry during development, use\n * this option to forward captured Sentry events to Spotlight.\n *\n * Either set it to true, or provide a specific Spotlight Sidecar URL.\n *\n * More details: https://spotlightjs.com/\n *\n * IMPORTANT: Only set this option to `true` while developing, not in production!\n */\n spotlight?: boolean | string;\n\n /**\n * Resolves URLs to low-cardinality route names, e.g. `/users/42` -> `/users/:id`.\n *\n * Framework SDKs set this for you. Set it yourself to name routes for a router the SDK has no\n * integration for. `setRouteProvider` replaces it at runtime.\n */\n routeProvider?: RouteProvider;\n };\n/**\n * Configuration options for the Sentry Browser SDK.\n * @see @sentry/core Options for more information.\n */\nexport type BrowserOptions = CoreOptions<BrowserTransportOptions> & BrowserSpecificOptions;\n\n/**\n * Configuration options for the Sentry Browser SDK Client class\n * @see BrowserClient for more information.\n */\nexport type BrowserClientOptions = ClientOptions<BrowserTransportOptions> & BrowserSpecificOptions;\n\n/**\n * The Sentry Browser SDK Client.\n *\n * @see BrowserOptions for documentation on configuration options.\n * @see SentryClient for usage documentation.\n */\nexport class BrowserClient extends Client<BrowserClientOptions> {\n /**\n * Creates a new Browser SDK instance.\n *\n * @param options Configuration options for this SDK.\n */\n public constructor(options: BrowserClientOptions) {\n const opts = applyDefaultOptions(options);\n const sdkSource = WINDOW.SENTRY_SDK_SOURCE || getSDKSource();\n applySdkMetadata(opts, 'browser', ['browser'], sdkSource);\n\n super(opts);\n\n if (opts.routeProvider) {\n setRouteProvider(opts.routeProvider, this);\n }\n\n // Unhandled errors don't actually crash the browser, so we report `unhandled` rather than `crashed`.\n this._unhandledSessionStatus = 'unhandled';\n\n const { userInfo } = this.getDataCollectionOptions();\n\n if (opts._metadata?.sdk) {\n opts._metadata.sdk.settings = {\n // Only allow IP inferral by Relay if the user opted in via dataCollection\n infer_ip: userInfo ? 'auto' : 'never',\n // purposefully allowing already passed settings to override the default\n ...opts._metadata.sdk.settings,\n };\n }\n\n const { sendClientReports } = this._options;\n\n // Flush buffered data when the page becomes hidden (e.g. tab switch, navigation, or the page\n // being discarded). `flush()` emits the `flush` hook, which drains the span (streaming), log and\n // metric buffers and hands the resulting envelopes to the transport (which uses `keepalive`).\n // Client report outcomes don't listen to the `flush` hook, so we flush them separately.\n if (WINDOW.document) {\n WINDOW.document.addEventListener('visibilitychange', () => {\n if (WINDOW.document.visibilityState === 'hidden') {\n if (sendClientReports) {\n this._flushOutcomes();\n }\n // Defer the flush to a microtask so that visibilitychange listeners registered after this\n // one have already run. In particular, browser tracing's background-tab detection ends the\n // active pageload/navigation (segment) span when the page is hidden. Deferring ensures that\n // segment span has been added to the streaming buffer before we flush, so it is sent\n // together with its child spans instead of being orphaned.\n queueMicrotask(() => {\n void this.flush();\n });\n }\n });\n }\n\n if (userInfo) {\n this.on('beforeSendSession', addAutoIpAddressToSession);\n }\n }\n\n /**\n * @inheritDoc\n */\n public eventFromException(exception: unknown, hint?: EventHint): PromiseLike<Event> {\n return eventFromException(this._options.stackParser, exception, hint, this._options.attachStacktrace);\n }\n\n /**\n * @inheritDoc\n */\n public eventFromMessage(\n message: ParameterizedString,\n level: SeverityLevel = 'info',\n hint?: EventHint,\n ): PromiseLike<Event> {\n return eventFromMessage(this._options.stackParser, message, level, hint, this._options.attachStacktrace);\n }\n\n /**\n * @inheritDoc\n */\n protected _prepareEvent(\n event: Event,\n hint: EventHint,\n currentScope: Scope,\n isolationScope: Scope,\n ): PromiseLike<Event | null> {\n event.platform = event.platform || 'javascript';\n\n return super._prepareEvent(event, hint, currentScope, isolationScope);\n }\n}\n\n/** Exported only for tests. */\nexport function applyDefaultOptions<T extends Partial<BrowserClientOptions>>(optionsArg: T): T {\n return {\n release:\n typeof __SENTRY_RELEASE__ === 'string' // This allows build tooling to find-and-replace __SENTRY_RELEASE__ to inject a release value\n ? __SENTRY_RELEASE__\n : WINDOW.SENTRY_RELEASE?.id, // This supports the variable that sentry-webpack-plugin injects\n sendClientReports: true,\n // We default this to true, as it is the safer scenario\n parentSpanIsAlwaysRootSpan: true,\n ...optionsArg,\n };\n}\n"],"names":["Client","WINDOW","getSDKSource","applySdkMetadata","setRouteProvider","addAutoIpAddressToSession","eventFromException","eventFromMessage"],"mappings":";;;;;;;AA2GO,MAAM,sBAAsBA,WAAA,CAA6B;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAMvD,YAAY,OAAA,EAA+B;AAChD,IAAA,MAAM,IAAA,GAAO,oBAAoB,OAAO,CAAA;AACxC,IAAA,MAAM,SAAA,GAAYC,cAAA,CAAO,iBAAA,IAAqBC,iBAAA,EAAa;AAC3D,IAAAC,qBAAA,CAAiB,IAAA,EAAM,SAAA,EAAW,CAAC,SAAS,GAAG,SAAS,CAAA;AAExD,IAAA,KAAA,CAAM,IAAI,CAAA;AAEV,IAAA,IAAI,KAAK,aAAA,EAAe;AACtB,MAAAC,6BAAA,CAAiB,IAAA,CAAK,eAAe,IAAI,CAAA;AAAA,IAC3C;AAGA,IAAA,IAAA,CAAK,uBAAA,GAA0B,WAAA;AAE/B,IAAA,MAAM,EAAE,QAAA,EAAS,GAAI,IAAA,CAAK,wBAAA,EAAyB;AAEnD,IAAA,IAAI,IAAA,CAAK,WAAW,GAAA,EAAK;AACvB,MAAA,IAAA,CAAK,SAAA,CAAU,IAAI,QAAA,GAAW;AAAA;AAAA,QAE5B,QAAA,EAAU,WAAW,MAAA,GAAS,OAAA;AAAA;AAAA,QAE9B,GAAG,IAAA,CAAK,SAAA,CAAU,GAAA,CAAI;AAAA,OACxB;AAAA,IACF;AAEA,IAAA,MAAM,EAAE,iBAAA,EAAkB,GAAI,IAAA,CAAK,QAAA;AAMnC,IAAA,IAAIH,eAAO,QAAA,EAAU;AACnB,MAAAA,cAAA,CAAO,QAAA,CAAS,gBAAA,CAAiB,kBAAA,EAAoB,MAAM;AACzD,QAAA,IAAIA,cAAA,CAAO,QAAA,CAAS,eAAA,KAAoB,QAAA,EAAU;AAChD,UAAA,IAAI,iBAAA,EAAmB;AACrB,YAAA,IAAA,CAAK,cAAA,EAAe;AAAA,UACtB;AAMA,UAAA,cAAA,CAAe,MAAM;AACnB,YAAA,KAAK,KAAK,KAAA,EAAM;AAAA,UAClB,CAAC,CAAA;AAAA,QACH;AAAA,MACF,CAAC,CAAA;AAAA,IACH;AAEA,IAAA,IAAI,QAAA,EAAU;AACZ,MAAA,IAAA,CAAK,EAAA,CAAG,qBAAqBI,8BAAyB,CAAA;AAAA,IACxD;AAAA,EACF;AAAA;AAAA;AAAA;AAAA,EAKO,kBAAA,CAAmB,WAAoB,IAAA,EAAsC;AAClF,IAAA,OAAOC,+BAAA,CAAmB,KAAK,QAAA,CAAS,WAAA,EAAa,WAAW,IAAA,EAAM,IAAA,CAAK,SAAS,gBAAgB,CAAA;AAAA,EACtG;AAAA;AAAA;AAAA;AAAA,EAKO,gBAAA,CACL,OAAA,EACA,KAAA,GAAuB,MAAA,EACvB,IAAA,EACoB;AACpB,IAAA,OAAOC,6BAAA,CAAiB,KAAK,QAAA,CAAS,WAAA,EAAa,SAAS,KAAA,EAAO,IAAA,EAAM,IAAA,CAAK,QAAA,CAAS,gBAAgB,CAAA;AAAA,EACzG;AAAA;AAAA;AAAA;AAAA,EAKU,aAAA,CACR,KAAA,EACA,IAAA,EACA,YAAA,EACA,cAAA,EAC2B;AAC3B,IAAA,KAAA,CAAM,QAAA,GAAW,MAAM,QAAA,IAAY,YAAA;AAEnC,IAAA,OAAO,KAAA,CAAM,aAAA,CAAc,KAAA,EAAO,IAAA,EAAM,cAAc,cAAc,CAAA;AAAA,EACtE;AACF;AAGO,SAAS,oBAA6D,UAAA,EAAkB;AAC7F,EAAA,OAAO;AAAA,IACL,SACE,OAAO,kBAAA,KAAuB,QAAA,GAC1B,kBAAA,GACAN,eAAO,cAAA,EAAgB,EAAA;AAAA;AAAA,IAC7B,iBAAA,EAAmB,IAAA;AAAA;AAAA,IAEnB,0BAAA,EAA4B,IAAA;AAAA,IAC5B,GAAG;AAAA,GACL;AACF;;;;;"}
|
|
1
|
+
{"version":3,"file":"client.js","sources":["../../../../src/client.ts"],"sourcesContent":["import type {\n ClientOptions,\n Event,\n EventHint,\n Options as CoreOptions,\n ParameterizedString,\n Scope,\n SeverityLevel,\n} from '@sentry/core';\nimport type { BrowserClientReplayOptions } from '@sentry/core/browser';\nimport type { RouteProvider } from '@sentry/browser-utils';\nimport { setRouteProvider } from '@sentry/browser-utils';\nimport { addAutoIpAddressToSession, applySdkMetadata, Client, getSDKSource, timestampInSeconds } from '@sentry/core';\nimport { eventFromException, eventFromMessage } from './eventbuilder';\nimport { WINDOW } from './helpers';\nimport type { BrowserTransportOptions } from './transports/types';\n\n/**\n * A magic string that build tooling can leverage in order to inject a release value into the SDK.\n */\ndeclare const __SENTRY_RELEASE__: string | undefined;\n\nexport type BrowserClientProfilingOptions = {\n /**\n * Sets profiling session sample rate for the entire profiling session.\n *\n * A profiling session corresponds to a user session, meaning it is set once at integration initialization and\n * persisted until the next page reload. This rate determines what percentage of user sessions will have profiling enabled.\n * @default 0\n */\n profileSessionSampleRate?: number;\n\n /**\n * Set the lifecycle mode of the profiler.\n * - **manual**: The profiler will be manually started and stopped via `startProfiler`/`stopProfiler`.\n * If a session is sampled, is dependent on the `profileSessionSampleRate`.\n * - **trace**: The profiler will be automatically started when a root span exists and stopped when there are no\n * more sampled root spans. Whether a session is sampled, is dependent on the `profileSessionSampleRate` and the\n * existing sampling configuration for tracing (`tracesSampleRate`/`tracesSampler`).\n *\n * @default 'manual'\n */\n profileLifecycle?: 'manual' | 'trace';\n};\n\ntype BrowserSpecificOptions = BrowserClientReplayOptions &\n BrowserClientProfilingOptions & {\n /** If configured, this URL will be used as base URL for lazy loading integration. */\n cdnBaseUrl?: string;\n\n /**\n * Important: Only set this option if you know what you are doing!\n *\n * By default, the SDK will check if `Sentry.init` is called in a browser extension.\n * In case it is, it will stop initialization and log a warning\n * because browser extensions require a different Sentry initialization process:\n * https://docs.sentry.io/platforms/javascript/best-practices/shared-environments/\n *\n * Setting up the SDK in a browser extension with global error monitoring is not recommended\n * and will likely flood you with errors from other web sites or extensions. This can heavily\n * impact your quota and cause interference with your and other Sentry SDKs in shared environments.\n *\n * If this check wrongfully flags your setup as a browser extension, you can set this\n * option to `true` to skip the check.\n *\n * @default false\n */\n skipBrowserExtensionCheck?: boolean;\n\n /**\n * If you use Spotlight by Sentry during development, use\n * this option to forward captured Sentry events to Spotlight.\n *\n * Either set it to true, or provide a specific Spotlight Sidecar URL.\n *\n * More details: https://spotlightjs.com/\n *\n * IMPORTANT: Only set this option to `true` while developing, not in production!\n */\n spotlight?: boolean | string;\n\n /**\n * Resolves URLs to low-cardinality route names, e.g. `/users/42` -> `/users/:id`.\n *\n * Framework SDKs set this for you. Set it yourself to name routes for a router the SDK has no\n * integration for. `setRouteProvider` replaces it at runtime.\n */\n routeProvider?: RouteProvider;\n };\n/**\n * Configuration options for the Sentry Browser SDK.\n * @see @sentry/core Options for more information.\n */\nexport type BrowserOptions = CoreOptions<BrowserTransportOptions> & BrowserSpecificOptions;\n\n/**\n * Configuration options for the Sentry Browser SDK Client class\n * @see BrowserClient for more information.\n */\nexport type BrowserClientOptions = ClientOptions<BrowserTransportOptions> & BrowserSpecificOptions;\n\n/**\n * The Sentry Browser SDK Client.\n *\n * @see BrowserOptions for documentation on configuration options.\n * @see SentryClient for usage documentation.\n */\nexport class BrowserClient extends Client<BrowserClientOptions> {\n /**\n * Creates a new Browser SDK instance.\n *\n * @param options Configuration options for this SDK.\n */\n public constructor(options: BrowserClientOptions) {\n const opts = applyDefaultOptions(options);\n const sdkSource = WINDOW.SENTRY_SDK_SOURCE || getSDKSource();\n applySdkMetadata(opts, 'browser', ['browser'], sdkSource);\n\n super(opts);\n\n if (opts.routeProvider) {\n setRouteProvider(opts.routeProvider, this);\n }\n\n // Unhandled errors don't actually crash the browser, so we report `unhandled` rather than `crashed`.\n this._unhandledSessionStatus = 'unhandled';\n\n const { userInfo } = this.getDataCollectionOptions();\n\n if (opts._metadata?.sdk) {\n opts._metadata.sdk.settings = {\n // Only allow IP inferral by Relay if the user opted in via dataCollection\n infer_ip: userInfo ? 'auto' : 'never',\n // purposefully allowing already passed settings to override the default\n ...opts._metadata.sdk.settings,\n };\n }\n\n const { sendClientReports } = this._options;\n\n // Devices usually hide the page before they sleep and show it again after they wake up. Checking for drift\n // at both points makes the time origin correction happen at the sleep, rather than at the next regular timestamp call.\n const checkDrift = (): void => {\n timestampInSeconds();\n };\n\n // Most bundle-size-efficient way to add and remove listeners: Create a table to avoid repeated\n // addeventlistener and removeeventlistener calls. The alternative, using an AbortController, can\n // lead to app breakage on old Angular<>Zone.js < 0.14.3 versions.\n const listeners: [EventTarget | undefined, string, () => void][] = [\n // Flush buffered data when the page becomes hidden (e.g. tab switch, navigation, or the page\n // being discarded). `flush()` emits the `flush` hook, which drains the span (streaming), log and\n // metric buffers and hands the resulting envelopes to the transport (which uses `keepalive`).\n // Client report outcomes don't listen to the `flush` hook, so we flush them separately.\n [\n WINDOW.document,\n 'visibilitychange',\n () => {\n // Devices usually hide the page before they sleep and show it again after they wake up. Checking for drift\n // at both points makes the time origin correction happen at the sleep, rather than at the next regular timestamp call.\n checkDrift();\n\n if (WINDOW.document.visibilityState === 'hidden') {\n if (sendClientReports) {\n this._flushOutcomes();\n }\n // Defer the flush to a microtask so that visibilitychange listeners registered after this\n // one have already run. In particular, browser tracing's background-tab detection ends the\n // active pageload/navigation (segment) span when the page is hidden. Deferring ensures that\n // segment span has been added to the streaming buffer before we flush, so it is sent\n // together with its child spans instead of being orphaned.\n queueMicrotask(() => {\n void this.flush();\n });\n }\n },\n ],\n // `freeze` and `resume` are only fired by Chromium browsers. Other browsers never fire them, so these listeners\n // are no-ops there.\n [WINDOW.document, 'freeze', checkDrift],\n [WINDOW.document, 'resume', checkDrift],\n // Pages restored from the back/forward cache were paused while they were cached.\n [WINDOW, 'pagehide', checkDrift],\n [WINDOW, 'pageshow', checkDrift],\n ];\n\n listeners.forEach(([target, name, listener]) => target?.addEventListener?.(name, listener));\n this.on('close', () =>\n listeners.forEach(([target, name, listener]) => target?.removeEventListener?.(name, listener)),\n );\n\n if (userInfo) {\n this.on('beforeSendSession', addAutoIpAddressToSession);\n }\n }\n\n /**\n * @inheritDoc\n */\n public eventFromException(exception: unknown, hint?: EventHint): PromiseLike<Event> {\n return eventFromException(this._options.stackParser, exception, hint, this._options.attachStacktrace);\n }\n\n /**\n * @inheritDoc\n */\n public eventFromMessage(\n message: ParameterizedString,\n level: SeverityLevel = 'info',\n hint?: EventHint,\n ): PromiseLike<Event> {\n return eventFromMessage(this._options.stackParser, message, level, hint, this._options.attachStacktrace);\n }\n\n /**\n * @inheritDoc\n */\n protected _prepareEvent(\n event: Event,\n hint: EventHint,\n currentScope: Scope,\n isolationScope: Scope,\n ): PromiseLike<Event | null> {\n event.platform = event.platform || 'javascript';\n\n return super._prepareEvent(event, hint, currentScope, isolationScope);\n }\n}\n\n/** Exported only for tests. */\nexport function applyDefaultOptions<T extends Partial<BrowserClientOptions>>(optionsArg: T): T {\n return {\n release:\n typeof __SENTRY_RELEASE__ === 'string' // This allows build tooling to find-and-replace __SENTRY_RELEASE__ to inject a release value\n ? __SENTRY_RELEASE__\n : WINDOW.SENTRY_RELEASE?.id, // This supports the variable that sentry-webpack-plugin injects\n sendClientReports: true,\n // We default this to true, as it is the safer scenario\n parentSpanIsAlwaysRootSpan: true,\n ...optionsArg,\n };\n}\n"],"names":["Client","WINDOW","getSDKSource","applySdkMetadata","setRouteProvider","timestampInSeconds","addAutoIpAddressToSession","eventFromException","eventFromMessage"],"mappings":"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"}
|
|
@@ -95,16 +95,13 @@ function convertToContinuousProfile(input) {
|
|
|
95
95
|
}
|
|
96
96
|
stacks[i] = list;
|
|
97
97
|
}
|
|
98
|
-
const perfOrigin = core.browserPerformanceTimeOrigin();
|
|
99
|
-
const origin = typeof performance.timeOrigin === "number" ? performance.timeOrigin : perfOrigin || 0;
|
|
100
|
-
const adjustForOriginChange = origin - (perfOrigin || origin);
|
|
101
98
|
const samples = [];
|
|
102
99
|
for (let i = 0; i < input.samples.length; i++) {
|
|
103
100
|
const sample = input.samples[i];
|
|
104
101
|
if (!sample) {
|
|
105
102
|
continue;
|
|
106
103
|
}
|
|
107
|
-
const timestampSeconds = (
|
|
104
|
+
const timestampSeconds = core._INTERNAL_performanceTimeToSeconds(sample.timestamp);
|
|
108
105
|
samples[i] = {
|
|
109
106
|
stack_id: sample.stackId ?? 0,
|
|
110
107
|
thread_id: PROFILER_THREAD_ID_STRING,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"utils.js","sources":["../../../../../src/profiling/utils.ts"],"sourcesContent":["/* eslint-disable max-lines */\nimport type { Client, ContinuousThreadCpuProfile, DebugImage, ProfileChunk, Span } from '@sentry/core';\nimport {\n browserPerformanceTimeOrigin,\n debug,\n getClient,\n getDebugImagesForResources,\n GLOBAL_OBJ,\n uuid4,\n} from '@sentry/core';\nimport type { BrowserOptions } from '../client';\nimport { DEBUG_BUILD } from '../debug-build';\nimport { WINDOW } from '../helpers';\nimport type { JSSelfProfile, JSSelfProfiler, JSSelfProfilerConstructor } from './jsSelfProfiling';\n\n// Checking if we are in Main or Worker thread: `self` (not `window`) is the `globalThis` in Web Workers and `importScripts` are only available in Web Workers\nconst isMainThread = 'window' in GLOBAL_OBJ && GLOBAL_OBJ.window === GLOBAL_OBJ && typeof importScripts === 'undefined';\n\n// Setting ID to 0 as we cannot get an ID from Web Workers\nexport const PROFILER_THREAD_ID_STRING = String(0);\nexport const PROFILER_THREAD_NAME = isMainThread ? 'main' : 'worker';\n\n/**\n * Create a profile chunk envelope item\n */\nexport function createProfileChunkPayload(\n jsSelfProfile: JSSelfProfile,\n client: Client,\n profilerId?: string,\n): ProfileChunk {\n // only == to catch null and undefined\n if (jsSelfProfile == null) {\n throw new TypeError(\n `Cannot construct profiling event envelope without a valid profile. Got ${jsSelfProfile} instead.`,\n );\n }\n\n const continuousProfile = convertToContinuousProfile(jsSelfProfile);\n\n const options = client.getOptions();\n const sdk = client.getSdkMetadata?.()?.sdk;\n\n return {\n chunk_id: uuid4(),\n client_sdk: {\n name: sdk?.name ?? 'sentry.javascript.browser',\n version: sdk?.version ?? '0.0.0',\n },\n profiler_id: profilerId || uuid4(),\n platform: 'javascript',\n version: '2',\n release: options.release ?? '',\n environment: options.environment ?? 'production',\n debug_meta: {\n // function name obfuscation\n images: applyDebugMetadata(jsSelfProfile.resources),\n },\n profile: continuousProfile,\n };\n}\n\n/**\n * Validate a profile chunk against the Sample Format V2 requirements.\n * https://develop.sentry.dev/sdk/telemetry/profiles/sample-format-v2/\n * - Presence of samples, stacks, frames\n * - Required metadata fields\n */\nexport function validateProfileChunk(chunk: ProfileChunk): { valid: true } | { reason: string } {\n try {\n // Required metadata\n if (!chunk || typeof chunk !== 'object') {\n return { reason: 'chunk is not an object' };\n }\n\n // profiler_id and chunk_id must be 32 lowercase hex chars\n const isHex32 = (val: unknown): boolean => typeof val === 'string' && /^[a-f0-9]{32}$/.test(val);\n if (!isHex32(chunk.profiler_id)) {\n return { reason: 'missing or invalid profiler_id' };\n }\n if (!isHex32(chunk.chunk_id)) {\n return { reason: 'missing or invalid chunk_id' };\n }\n\n if (!chunk.client_sdk) {\n return { reason: 'missing client_sdk metadata' };\n }\n\n // Profile data must have frames, stacks, samples\n const profile = chunk.profile as { frames?: unknown[]; stacks?: unknown[]; samples?: unknown[] } | undefined;\n if (!profile) {\n return { reason: 'missing profile data' };\n }\n\n if (!Array.isArray(profile.frames) || !profile.frames.length) {\n return { reason: 'profile has no frames' };\n }\n if (!Array.isArray(profile.stacks) || !profile.stacks.length) {\n return { reason: 'profile has no stacks' };\n }\n if (!Array.isArray(profile.samples) || !profile.samples.length) {\n return { reason: 'profile has no samples' };\n }\n\n return { valid: true };\n } catch (e) {\n return { reason: `unknown validation error: ${e}` };\n }\n}\n\n/**\n * Convert from JSSelfProfile format to ContinuousThreadCpuProfile format.\n */\nfunction convertToContinuousProfile(input: {\n frames: { name: string; resourceId?: number; line?: number; column?: number }[];\n stacks: { frameId: number; parentId?: number }[];\n samples: { timestamp: number; stackId?: number }[];\n resources: string[];\n}): ContinuousThreadCpuProfile {\n // Frames map 1:1 by index; fill only when present to avoid sparse writes\n const frames: ContinuousThreadCpuProfile['frames'] = [];\n for (let i = 0; i < input.frames.length; i++) {\n const frame = input.frames[i];\n if (!frame) {\n continue;\n }\n frames[i] = {\n function: frame.name,\n abs_path: typeof frame.resourceId === 'number' ? input.resources[frame.resourceId] : undefined,\n lineno: frame.line,\n colno: frame.column,\n };\n }\n\n // Build stacks by following parent links, top->down order (root last)\n const stacks: ContinuousThreadCpuProfile['stacks'] = [];\n for (let i = 0; i < input.stacks.length; i++) {\n const stackHead = input.stacks[i];\n if (!stackHead) {\n continue;\n }\n const list: number[] = [];\n let current: { frameId: number; parentId?: number } | undefined = stackHead;\n while (current) {\n list.push(current.frameId);\n current = current.parentId === undefined ? undefined : input.stacks[current.parentId];\n }\n stacks[i] = list;\n }\n\n // Align timestamps to SDK time origin to match span/event timelines\n const perfOrigin = browserPerformanceTimeOrigin();\n const origin = typeof performance.timeOrigin === 'number' ? performance.timeOrigin : perfOrigin || 0;\n const adjustForOriginChange = origin - (perfOrigin || origin);\n\n const samples: ContinuousThreadCpuProfile['samples'] = [];\n for (let i = 0; i < input.samples.length; i++) {\n const sample = input.samples[i];\n if (!sample) {\n continue;\n }\n // Convert ms to seconds epoch-based timestamp\n const timestampSeconds = (origin + (sample.timestamp - adjustForOriginChange)) / 1000;\n samples[i] = {\n stack_id: sample.stackId ?? 0,\n thread_id: PROFILER_THREAD_ID_STRING,\n timestamp: timestampSeconds,\n };\n }\n\n return {\n frames,\n stacks,\n samples,\n thread_metadata: { [PROFILER_THREAD_ID_STRING]: { name: PROFILER_THREAD_NAME } },\n };\n}\n\n/**\n * Applies debug meta data to an event from a list of paths to resources (sourcemaps)\n */\nexport function applyDebugMetadata(resource_paths: ReadonlyArray<string>): DebugImage[] {\n const client = getClient();\n const options = client?.getOptions();\n const stackParser = options?.stackParser;\n\n if (!stackParser) {\n return [];\n }\n\n return getDebugImagesForResources(stackParser, resource_paths);\n}\n\n/**\n * Checks the given sample rate to make sure it is valid type and value (a boolean, or a number between 0 and 1).\n */\nexport function isValidSampleRate(rate: unknown): boolean {\n // we need to check NaN explicitly because it's of type 'number' and therefore wouldn't get caught by this typecheck\n if ((typeof rate !== 'number' && typeof rate !== 'boolean') || (typeof rate === 'number' && isNaN(rate))) {\n DEBUG_BUILD &&\n debug.warn(\n `[Profiling] Invalid sample rate. Sample rate must be a boolean or a number between 0 and 1. Got ${JSON.stringify(\n rate,\n )} of type ${JSON.stringify(typeof rate)}.`,\n );\n return false;\n }\n\n // Boolean sample rates are always valid\n if (rate === true || rate === false) {\n return true;\n }\n\n // in case sampleRate is a boolean, it will get automatically cast to 1 if it's true and 0 if it's false\n if (rate < 0 || rate > 1) {\n DEBUG_BUILD && debug.warn(`[Profiling] Invalid sample rate. Sample rate must be between 0 and 1. Got ${rate}.`);\n return false;\n }\n return true;\n}\n\n// Keep a flag value to avoid re-initializing the profiler constructor. If it fails\n// once, it will always fail and this allows us to early return.\nlet PROFILING_CONSTRUCTOR_FAILED: boolean = false;\nexport const MAX_PROFILE_DURATION_MS = 30_000;\n\n/**\n * Check if profiler constructor is available.\n * @param maybeProfiler\n */\nfunction isJSProfilerSupported(maybeProfiler: unknown): maybeProfiler is typeof JSSelfProfilerConstructor {\n return typeof maybeProfiler === 'function';\n}\n\n/**\n * Starts the profiler and returns the profiler instance.\n */\nexport function startJSSelfProfile(): JSSelfProfiler | undefined {\n // Feature support check first\n const JSProfilerConstructor = WINDOW.Profiler;\n\n if (!isJSProfilerSupported(JSProfilerConstructor)) {\n if (DEBUG_BUILD) {\n debug.log('[Profiling] Profiling is not supported by this browser, Profiler interface missing on window object.');\n }\n return;\n }\n\n // From initial testing, it seems that the minimum value for sampleInterval is 10ms.\n const samplingIntervalMS = 10;\n // Start the profiler\n const maxSamples = Math.floor(MAX_PROFILE_DURATION_MS / samplingIntervalMS);\n\n // Attempt to initialize the profiler constructor, if it fails, we disable profiling for the current user session.\n // This is likely due to a missing 'Document-Policy': 'js-profiling' header. We do not want to throw an error if this happens\n // as we risk breaking the user's application, so just disable profiling and log an error.\n try {\n return new JSProfilerConstructor({ sampleInterval: samplingIntervalMS, maxBufferSize: maxSamples });\n } catch {\n if (DEBUG_BUILD) {\n debug.log(\n \"[Profiling] Failed to initialize the Profiling constructor, this is likely due to a missing 'Document-Policy': 'js-profiling' header.\",\n );\n debug.log('[Profiling] Disabling profiling for current user session.');\n }\n PROFILING_CONSTRUCTOR_FAILED = true;\n }\n\n return;\n}\n\n/**\n * Determine if a profile should be created for the current session.\n */\nexport function shouldProfileSession(options: BrowserOptions): boolean {\n // If constructor failed once, it will always fail, so we can early return.\n if (PROFILING_CONSTRUCTOR_FAILED) {\n if (DEBUG_BUILD) {\n debug.log(\n '[Profiling] Profiling has been disabled for the duration of the current user session as the JS Profiler could not be started.',\n );\n }\n return false;\n }\n\n if (options.profileLifecycle !== 'trace' && options.profileLifecycle !== 'manual') {\n DEBUG_BUILD && debug.warn('[Profiling] Session not sampled. Invalid `profileLifecycle` option.');\n return false;\n }\n\n // Session sampling: profileSessionSampleRate gates whether profiling is enabled for this session\n const profileSessionSampleRate = options.profileSessionSampleRate;\n\n if (!isValidSampleRate(profileSessionSampleRate)) {\n DEBUG_BUILD && debug.warn('[Profiling] Discarding profile because of invalid profileSessionSampleRate.');\n return false;\n }\n\n if (!profileSessionSampleRate) {\n DEBUG_BUILD &&\n debug.log('[Profiling] Discarding profile because profileSessionSampleRate is not defined or set to 0');\n return false;\n }\n\n return Math.random() <= profileSessionSampleRate;\n}\n\nexport function setThreadAttributes(span: Span): void {\n span.setAttribute('thread.id', PROFILER_THREAD_ID_STRING);\n span.setAttribute('thread.name', PROFILER_THREAD_NAME);\n}\n"],"names":["GLOBAL_OBJ","uuid4","browserPerformanceTimeOrigin","getClient","getDebugImagesForResources","DEBUG_BUILD","debug","WINDOW"],"mappings":";;;;;;AAgBA,MAAM,eAAe,QAAA,IAAYA,eAAA,IAAcA,gBAAW,MAAA,KAAWA,eAAA,IAAc,OAAO,aAAA,KAAkB,WAAA;AAGrG,MAAM,yBAAA,GAA4B,OAAO,CAAC;AAC1C,MAAM,oBAAA,GAAuB,eAAe,MAAA,GAAS;AAKrD,SAAS,yBAAA,CACd,aAAA,EACA,MAAA,EACA,UAAA,EACc;AAEd,EAAA,IAAI,iBAAiB,IAAA,EAAM;AACzB,IAAA,MAAM,IAAI,SAAA;AAAA,MACR,0EAA0E,aAAa,CAAA,SAAA;AAAA,KACzF;AAAA,EACF;AAEA,EAAA,MAAM,iBAAA,GAAoB,2BAA2B,aAAa,CAAA;AAElE,EAAA,MAAM,OAAA,GAAU,OAAO,UAAA,EAAW;AAClC,EAAA,MAAM,GAAA,GAAM,MAAA,CAAO,cAAA,IAAiB,EAAG,GAAA;AAEvC,EAAA,OAAO;AAAA,IACL,UAAUC,UAAA,EAAM;AAAA,IAChB,UAAA,EAAY;AAAA,MACV,IAAA,EAAM,KAAK,IAAA,IAAQ,2BAAA;AAAA,MACnB,OAAA,EAAS,KAAK,OAAA,IAAW;AAAA,KAC3B;AAAA,IACA,WAAA,EAAa,cAAcA,UAAA,EAAM;AAAA,IACjC,QAAA,EAAU,YAAA;AAAA,IACV,OAAA,EAAS,GAAA;AAAA,IACT,OAAA,EAAS,QAAQ,OAAA,IAAW,EAAA;AAAA,IAC5B,WAAA,EAAa,QAAQ,WAAA,IAAe,YAAA;AAAA,IACpC,UAAA,EAAY;AAAA;AAAA,MAEV,MAAA,EAAQ,kBAAA,CAAmB,aAAA,CAAc,SAAS;AAAA,KACpD;AAAA,IACA,OAAA,EAAS;AAAA,GACX;AACF;AAQO,SAAS,qBAAqB,KAAA,EAA2D;AAC9F,EAAA,IAAI;AAEF,IAAA,IAAI,CAAC,KAAA,IAAS,OAAO,KAAA,KAAU,QAAA,EAAU;AACvC,MAAA,OAAO,EAAE,QAAQ,wBAAA,EAAyB;AAAA,IAC5C;AAGA,IAAA,MAAM,OAAA,GAAU,CAAC,GAAA,KAA0B,OAAO,QAAQ,QAAA,IAAY,gBAAA,CAAiB,KAAK,GAAG,CAAA;AAC/F,IAAA,IAAI,CAAC,OAAA,CAAQ,KAAA,CAAM,WAAW,CAAA,EAAG;AAC/B,MAAA,OAAO,EAAE,QAAQ,gCAAA,EAAiC;AAAA,IACpD;AACA,IAAA,IAAI,CAAC,OAAA,CAAQ,KAAA,CAAM,QAAQ,CAAA,EAAG;AAC5B,MAAA,OAAO,EAAE,QAAQ,6BAAA,EAA8B;AAAA,IACjD;AAEA,IAAA,IAAI,CAAC,MAAM,UAAA,EAAY;AACrB,MAAA,OAAO,EAAE,QAAQ,6BAAA,EAA8B;AAAA,IACjD;AAGA,IAAA,MAAM,UAAU,KAAA,CAAM,OAAA;AACtB,IAAA,IAAI,CAAC,OAAA,EAAS;AACZ,MAAA,OAAO,EAAE,QAAQ,sBAAA,EAAuB;AAAA,IAC1C;AAEA,IAAA,IAAI,CAAC,MAAM,OAAA,CAAQ,OAAA,CAAQ,MAAM,CAAA,IAAK,CAAC,OAAA,CAAQ,MAAA,CAAO,MAAA,EAAQ;AAC5D,MAAA,OAAO,EAAE,QAAQ,uBAAA,EAAwB;AAAA,IAC3C;AACA,IAAA,IAAI,CAAC,MAAM,OAAA,CAAQ,OAAA,CAAQ,MAAM,CAAA,IAAK,CAAC,OAAA,CAAQ,MAAA,CAAO,MAAA,EAAQ;AAC5D,MAAA,OAAO,EAAE,QAAQ,uBAAA,EAAwB;AAAA,IAC3C;AACA,IAAA,IAAI,CAAC,MAAM,OAAA,CAAQ,OAAA,CAAQ,OAAO,CAAA,IAAK,CAAC,OAAA,CAAQ,OAAA,CAAQ,MAAA,EAAQ;AAC9D,MAAA,OAAO,EAAE,QAAQ,wBAAA,EAAyB;AAAA,IAC5C;AAEA,IAAA,OAAO,EAAE,OAAO,IAAA,EAAK;AAAA,EACvB,SAAS,CAAA,EAAG;AACV,IAAA,OAAO,EAAE,MAAA,EAAQ,CAAA,0BAAA,EAA6B,CAAC,CAAA,CAAA,EAAG;AAAA,EACpD;AACF;AAKA,SAAS,2BAA2B,KAAA,EAKL;AAE7B,EAAA,MAAM,SAA+C,EAAC;AACtD,EAAA,KAAA,IAAS,IAAI,CAAA,EAAG,CAAA,GAAI,KAAA,CAAM,MAAA,CAAO,QAAQ,CAAA,EAAA,EAAK;AAC5C,IAAA,MAAM,KAAA,GAAQ,KAAA,CAAM,MAAA,CAAO,CAAC,CAAA;AAC5B,IAAA,IAAI,CAAC,KAAA,EAAO;AACV,MAAA;AAAA,IACF;AACA,IAAA,MAAA,CAAO,CAAC,CAAA,GAAI;AAAA,MACV,UAAU,KAAA,CAAM,IAAA;AAAA,MAChB,QAAA,EAAU,OAAO,KAAA,CAAM,UAAA,KAAe,WAAW,KAAA,CAAM,SAAA,CAAU,KAAA,CAAM,UAAU,CAAA,GAAI,MAAA;AAAA,MACrF,QAAQ,KAAA,CAAM,IAAA;AAAA,MACd,OAAO,KAAA,CAAM;AAAA,KACf;AAAA,EACF;AAGA,EAAA,MAAM,SAA+C,EAAC;AACtD,EAAA,KAAA,IAAS,IAAI,CAAA,EAAG,CAAA,GAAI,KAAA,CAAM,MAAA,CAAO,QAAQ,CAAA,EAAA,EAAK;AAC5C,IAAA,MAAM,SAAA,GAAY,KAAA,CAAM,MAAA,CAAO,CAAC,CAAA;AAChC,IAAA,IAAI,CAAC,SAAA,EAAW;AACd,MAAA;AAAA,IACF;AACA,IAAA,MAAM,OAAiB,EAAC;AACxB,IAAA,IAAI,OAAA,GAA8D,SAAA;AAClE,IAAA,OAAO,OAAA,EAAS;AACd,MAAA,IAAA,CAAK,IAAA,CAAK,QAAQ,OAAO,CAAA;AACzB,MAAA,OAAA,GAAU,QAAQ,QAAA,KAAa,MAAA,GAAY,SAAY,KAAA,CAAM,MAAA,CAAO,QAAQ,QAAQ,CAAA;AAAA,IACtF;AACA,IAAA,MAAA,CAAO,CAAC,CAAA,GAAI,IAAA;AAAA,EACd;AAGA,EAAA,MAAM,aAAaC,iCAAA,EAA6B;AAChD,EAAA,MAAM,SAAS,OAAO,WAAA,CAAY,eAAe,QAAA,GAAW,WAAA,CAAY,aAAa,UAAA,IAAc,CAAA;AACnG,EAAA,MAAM,qBAAA,GAAwB,UAAU,UAAA,IAAc,MAAA,CAAA;AAEtD,EAAA,MAAM,UAAiD,EAAC;AACxD,EAAA,KAAA,IAAS,IAAI,CAAA,EAAG,CAAA,GAAI,KAAA,CAAM,OAAA,CAAQ,QAAQ,CAAA,EAAA,EAAK;AAC7C,IAAA,MAAM,MAAA,GAAS,KAAA,CAAM,OAAA,CAAQ,CAAC,CAAA;AAC9B,IAAA,IAAI,CAAC,MAAA,EAAQ;AACX,MAAA;AAAA,IACF;AAEA,IAAA,MAAM,gBAAA,GAAA,CAAoB,MAAA,IAAU,MAAA,CAAO,SAAA,GAAY,qBAAA,CAAA,IAA0B,GAAA;AACjF,IAAA,OAAA,CAAQ,CAAC,CAAA,GAAI;AAAA,MACX,QAAA,EAAU,OAAO,OAAA,IAAW,CAAA;AAAA,MAC5B,SAAA,EAAW,yBAAA;AAAA,MACX,SAAA,EAAW;AAAA,KACb;AAAA,EACF;AAEA,EAAA,OAAO;AAAA,IACL,MAAA;AAAA,IACA,MAAA;AAAA,IACA,OAAA;AAAA,IACA,eAAA,EAAiB,EAAE,CAAC,yBAAyB,GAAG,EAAE,IAAA,EAAM,sBAAqB;AAAE,GACjF;AACF;AAKO,SAAS,mBAAmB,cAAA,EAAqD;AACtF,EAAA,MAAM,SAASC,cAAA,EAAU;AACzB,EAAA,MAAM,OAAA,GAAU,QAAQ,UAAA,EAAW;AACnC,EAAA,MAAM,cAAc,OAAA,EAAS,WAAA;AAE7B,EAAA,IAAI,CAAC,WAAA,EAAa;AAChB,IAAA,OAAO,EAAC;AAAA,EACV;AAEA,EAAA,OAAOC,+BAAA,CAA2B,aAAa,cAAc,CAAA;AAC/D;AAKO,SAAS,kBAAkB,IAAA,EAAwB;AAExD,EAAA,IAAK,OAAO,IAAA,KAAS,QAAA,IAAY,OAAO,IAAA,KAAS,SAAA,IAAe,OAAO,IAAA,KAAS,QAAA,IAAY,KAAA,CAAM,IAAI,CAAA,EAAI;AACxG,IAAAC,sBAAA,IACEC,UAAA,CAAM,IAAA;AAAA,MACJ,mGAAmG,IAAA,CAAK,SAAA;AAAA,QACtG;AAAA,OACD,CAAA,SAAA,EAAY,IAAA,CAAK,SAAA,CAAU,OAAO,IAAI,CAAC,CAAA,CAAA;AAAA,KAC1C;AACF,IAAA,OAAO,KAAA;AAAA,EACT;AAGA,EAAA,IAAI,IAAA,KAAS,IAAA,IAAQ,IAAA,KAAS,KAAA,EAAO;AACnC,IAAA,OAAO,IAAA;AAAA,EACT;AAGA,EAAA,IAAI,IAAA,GAAO,CAAA,IAAK,IAAA,GAAO,CAAA,EAAG;AACxB,IAAAD,sBAAA,IAAeC,UAAA,CAAM,IAAA,CAAK,CAAA,0EAAA,EAA6E,IAAI,CAAA,CAAA,CAAG,CAAA;AAC9G,IAAA,OAAO,KAAA;AAAA,EACT;AACA,EAAA,OAAO,IAAA;AACT;AAIA,IAAI,4BAAA,GAAwC,KAAA;AACrC,MAAM,uBAAA,GAA0B;AAMvC,SAAS,sBAAsB,aAAA,EAA2E;AACxG,EAAA,OAAO,OAAO,aAAA,KAAkB,UAAA;AAClC;AAKO,SAAS,kBAAA,GAAiD;AAE/D,EAAA,MAAM,wBAAwBC,cAAA,CAAO,QAAA;AAErC,EAAA,IAAI,CAAC,qBAAA,CAAsB,qBAAqB,CAAA,EAAG;AACjD,IAAA,IAAIF,sBAAA,EAAa;AACf,MAAAC,UAAA,CAAM,IAAI,sGAAsG,CAAA;AAAA,IAClH;AACA,IAAA;AAAA,EACF;AAGA,EAAA,MAAM,kBAAA,GAAqB,EAAA;AAE3B,EAAA,MAAM,UAAA,GAAa,IAAA,CAAK,KAAA,CAAM,uBAAA,GAA0B,kBAAkB,CAAA;AAK1E,EAAA,IAAI;AACF,IAAA,OAAO,IAAI,qBAAA,CAAsB,EAAE,gBAAgB,kBAAA,EAAoB,aAAA,EAAe,YAAY,CAAA;AAAA,EACpG,CAAA,CAAA,MAAQ;AACN,IAAA,IAAID,sBAAA,EAAa;AACf,MAAAC,UAAA,CAAM,GAAA;AAAA,QACJ;AAAA,OACF;AACA,MAAAA,UAAA,CAAM,IAAI,2DAA2D,CAAA;AAAA,IACvE;AACA,IAAA,4BAAA,GAA+B,IAAA;AAAA,EACjC;AAEA,EAAA;AACF;AAKO,SAAS,qBAAqB,OAAA,EAAkC;AAErE,EAAA,IAAI,4BAAA,EAA8B;AAChC,IAAA,IAAID,sBAAA,EAAa;AACf,MAAAC,UAAA,CAAM,GAAA;AAAA,QACJ;AAAA,OACF;AAAA,IACF;AACA,IAAA,OAAO,KAAA;AAAA,EACT;AAEA,EAAA,IAAI,OAAA,CAAQ,gBAAA,KAAqB,OAAA,IAAW,OAAA,CAAQ,qBAAqB,QAAA,EAAU;AACjF,IAAAD,sBAAA,IAAeC,UAAA,CAAM,KAAK,qEAAqE,CAAA;AAC/F,IAAA,OAAO,KAAA;AAAA,EACT;AAGA,EAAA,MAAM,2BAA2B,OAAA,CAAQ,wBAAA;AAEzC,EAAA,IAAI,CAAC,iBAAA,CAAkB,wBAAwB,CAAA,EAAG;AAChD,IAAAD,sBAAA,IAAeC,UAAA,CAAM,KAAK,6EAA6E,CAAA;AACvG,IAAA,OAAO,KAAA;AAAA,EACT;AAEA,EAAA,IAAI,CAAC,wBAAA,EAA0B;AAC7B,IAAAD,sBAAA,IACEC,UAAA,CAAM,IAAI,4FAA4F,CAAA;AACxG,IAAA,OAAO,KAAA;AAAA,EACT;AAEA,EAAA,OAAO,IAAA,CAAK,QAAO,IAAK,wBAAA;AAC1B;AAEO,SAAS,oBAAoB,IAAA,EAAkB;AACpD,EAAA,IAAA,CAAK,YAAA,CAAa,aAAa,yBAAyB,CAAA;AACxD,EAAA,IAAA,CAAK,YAAA,CAAa,eAAe,oBAAoB,CAAA;AACvD;;;;;;;;;;;;;"}
|
|
1
|
+
{"version":3,"file":"utils.js","sources":["../../../../../src/profiling/utils.ts"],"sourcesContent":["/* eslint-disable max-lines */\nimport type { Client, ContinuousThreadCpuProfile, DebugImage, ProfileChunk, Span } from '@sentry/core';\nimport {\n debug,\n getClient,\n getDebugImagesForResources,\n GLOBAL_OBJ,\n _INTERNAL_performanceTimeToSeconds,\n uuid4,\n} from '@sentry/core';\nimport type { BrowserOptions } from '../client';\nimport { DEBUG_BUILD } from '../debug-build';\nimport { WINDOW } from '../helpers';\nimport type { JSSelfProfile, JSSelfProfiler, JSSelfProfilerConstructor } from './jsSelfProfiling';\n\n// Checking if we are in Main or Worker thread: `self` (not `window`) is the `globalThis` in Web Workers and `importScripts` are only available in Web Workers\nconst isMainThread = 'window' in GLOBAL_OBJ && GLOBAL_OBJ.window === GLOBAL_OBJ && typeof importScripts === 'undefined';\n\n// Setting ID to 0 as we cannot get an ID from Web Workers\nexport const PROFILER_THREAD_ID_STRING = String(0);\nexport const PROFILER_THREAD_NAME = isMainThread ? 'main' : 'worker';\n\n/**\n * Create a profile chunk envelope item\n */\nexport function createProfileChunkPayload(\n jsSelfProfile: JSSelfProfile,\n client: Client,\n profilerId?: string,\n): ProfileChunk {\n // only == to catch null and undefined\n if (jsSelfProfile == null) {\n throw new TypeError(\n `Cannot construct profiling event envelope without a valid profile. Got ${jsSelfProfile} instead.`,\n );\n }\n\n const continuousProfile = convertToContinuousProfile(jsSelfProfile);\n\n const options = client.getOptions();\n const sdk = client.getSdkMetadata?.()?.sdk;\n\n return {\n chunk_id: uuid4(),\n client_sdk: {\n name: sdk?.name ?? 'sentry.javascript.browser',\n version: sdk?.version ?? '0.0.0',\n },\n profiler_id: profilerId || uuid4(),\n platform: 'javascript',\n version: '2',\n release: options.release ?? '',\n environment: options.environment ?? 'production',\n debug_meta: {\n // function name obfuscation\n images: applyDebugMetadata(jsSelfProfile.resources),\n },\n profile: continuousProfile,\n };\n}\n\n/**\n * Validate a profile chunk against the Sample Format V2 requirements.\n * https://develop.sentry.dev/sdk/telemetry/profiles/sample-format-v2/\n * - Presence of samples, stacks, frames\n * - Required metadata fields\n */\nexport function validateProfileChunk(chunk: ProfileChunk): { valid: true } | { reason: string } {\n try {\n // Required metadata\n if (!chunk || typeof chunk !== 'object') {\n return { reason: 'chunk is not an object' };\n }\n\n // profiler_id and chunk_id must be 32 lowercase hex chars\n const isHex32 = (val: unknown): boolean => typeof val === 'string' && /^[a-f0-9]{32}$/.test(val);\n if (!isHex32(chunk.profiler_id)) {\n return { reason: 'missing or invalid profiler_id' };\n }\n if (!isHex32(chunk.chunk_id)) {\n return { reason: 'missing or invalid chunk_id' };\n }\n\n if (!chunk.client_sdk) {\n return { reason: 'missing client_sdk metadata' };\n }\n\n // Profile data must have frames, stacks, samples\n const profile = chunk.profile as { frames?: unknown[]; stacks?: unknown[]; samples?: unknown[] } | undefined;\n if (!profile) {\n return { reason: 'missing profile data' };\n }\n\n if (!Array.isArray(profile.frames) || !profile.frames.length) {\n return { reason: 'profile has no frames' };\n }\n if (!Array.isArray(profile.stacks) || !profile.stacks.length) {\n return { reason: 'profile has no stacks' };\n }\n if (!Array.isArray(profile.samples) || !profile.samples.length) {\n return { reason: 'profile has no samples' };\n }\n\n return { valid: true };\n } catch (e) {\n return { reason: `unknown validation error: ${e}` };\n }\n}\n\n/**\n * Convert from JSSelfProfile format to ContinuousThreadCpuProfile format.\n */\nfunction convertToContinuousProfile(input: {\n frames: { name: string; resourceId?: number; line?: number; column?: number }[];\n stacks: { frameId: number; parentId?: number }[];\n samples: { timestamp: number; stackId?: number }[];\n resources: string[];\n}): ContinuousThreadCpuProfile {\n // Frames map 1:1 by index; fill only when present to avoid sparse writes\n const frames: ContinuousThreadCpuProfile['frames'] = [];\n for (let i = 0; i < input.frames.length; i++) {\n const frame = input.frames[i];\n if (!frame) {\n continue;\n }\n frames[i] = {\n function: frame.name,\n abs_path: typeof frame.resourceId === 'number' ? input.resources[frame.resourceId] : undefined,\n lineno: frame.line,\n colno: frame.column,\n };\n }\n\n // Build stacks by following parent links, top->down order (root last)\n const stacks: ContinuousThreadCpuProfile['stacks'] = [];\n for (let i = 0; i < input.stacks.length; i++) {\n const stackHead = input.stacks[i];\n if (!stackHead) {\n continue;\n }\n const list: number[] = [];\n let current: { frameId: number; parentId?: number } | undefined = stackHead;\n while (current) {\n list.push(current.frameId);\n current = current.parentId === undefined ? undefined : input.stacks[current.parentId];\n }\n stacks[i] = list;\n }\n\n const samples: ContinuousThreadCpuProfile['samples'] = [];\n for (let i = 0; i < input.samples.length; i++) {\n const sample = input.samples[i];\n if (!sample) {\n continue;\n }\n // Sample timestamps are relative to `performance.timeOrigin`, so we convert them like performance entries.\n // The cast is safe: the JS Self-Profiling API only exists if the Performance API does.\n const timestampSeconds = _INTERNAL_performanceTimeToSeconds(sample.timestamp) as number;\n samples[i] = {\n stack_id: sample.stackId ?? 0,\n thread_id: PROFILER_THREAD_ID_STRING,\n timestamp: timestampSeconds,\n };\n }\n\n return {\n frames,\n stacks,\n samples,\n thread_metadata: { [PROFILER_THREAD_ID_STRING]: { name: PROFILER_THREAD_NAME } },\n };\n}\n\n/**\n * Applies debug meta data to an event from a list of paths to resources (sourcemaps)\n */\nexport function applyDebugMetadata(resource_paths: ReadonlyArray<string>): DebugImage[] {\n const client = getClient();\n const options = client?.getOptions();\n const stackParser = options?.stackParser;\n\n if (!stackParser) {\n return [];\n }\n\n return getDebugImagesForResources(stackParser, resource_paths);\n}\n\n/**\n * Checks the given sample rate to make sure it is valid type and value (a boolean, or a number between 0 and 1).\n */\nexport function isValidSampleRate(rate: unknown): boolean {\n // we need to check NaN explicitly because it's of type 'number' and therefore wouldn't get caught by this typecheck\n if ((typeof rate !== 'number' && typeof rate !== 'boolean') || (typeof rate === 'number' && isNaN(rate))) {\n DEBUG_BUILD &&\n debug.warn(\n `[Profiling] Invalid sample rate. Sample rate must be a boolean or a number between 0 and 1. Got ${JSON.stringify(\n rate,\n )} of type ${JSON.stringify(typeof rate)}.`,\n );\n return false;\n }\n\n // Boolean sample rates are always valid\n if (rate === true || rate === false) {\n return true;\n }\n\n // in case sampleRate is a boolean, it will get automatically cast to 1 if it's true and 0 if it's false\n if (rate < 0 || rate > 1) {\n DEBUG_BUILD && debug.warn(`[Profiling] Invalid sample rate. Sample rate must be between 0 and 1. Got ${rate}.`);\n return false;\n }\n return true;\n}\n\n// Keep a flag value to avoid re-initializing the profiler constructor. If it fails\n// once, it will always fail and this allows us to early return.\nlet PROFILING_CONSTRUCTOR_FAILED: boolean = false;\nexport const MAX_PROFILE_DURATION_MS = 30_000;\n\n/**\n * Check if profiler constructor is available.\n * @param maybeProfiler\n */\nfunction isJSProfilerSupported(maybeProfiler: unknown): maybeProfiler is typeof JSSelfProfilerConstructor {\n return typeof maybeProfiler === 'function';\n}\n\n/**\n * Starts the profiler and returns the profiler instance.\n */\nexport function startJSSelfProfile(): JSSelfProfiler | undefined {\n // Feature support check first\n const JSProfilerConstructor = WINDOW.Profiler;\n\n if (!isJSProfilerSupported(JSProfilerConstructor)) {\n if (DEBUG_BUILD) {\n debug.log('[Profiling] Profiling is not supported by this browser, Profiler interface missing on window object.');\n }\n return;\n }\n\n // From initial testing, it seems that the minimum value for sampleInterval is 10ms.\n const samplingIntervalMS = 10;\n // Start the profiler\n const maxSamples = Math.floor(MAX_PROFILE_DURATION_MS / samplingIntervalMS);\n\n // Attempt to initialize the profiler constructor, if it fails, we disable profiling for the current user session.\n // This is likely due to a missing 'Document-Policy': 'js-profiling' header. We do not want to throw an error if this happens\n // as we risk breaking the user's application, so just disable profiling and log an error.\n try {\n return new JSProfilerConstructor({ sampleInterval: samplingIntervalMS, maxBufferSize: maxSamples });\n } catch {\n if (DEBUG_BUILD) {\n debug.log(\n \"[Profiling] Failed to initialize the Profiling constructor, this is likely due to a missing 'Document-Policy': 'js-profiling' header.\",\n );\n debug.log('[Profiling] Disabling profiling for current user session.');\n }\n PROFILING_CONSTRUCTOR_FAILED = true;\n }\n\n return;\n}\n\n/**\n * Determine if a profile should be created for the current session.\n */\nexport function shouldProfileSession(options: BrowserOptions): boolean {\n // If constructor failed once, it will always fail, so we can early return.\n if (PROFILING_CONSTRUCTOR_FAILED) {\n if (DEBUG_BUILD) {\n debug.log(\n '[Profiling] Profiling has been disabled for the duration of the current user session as the JS Profiler could not be started.',\n );\n }\n return false;\n }\n\n if (options.profileLifecycle !== 'trace' && options.profileLifecycle !== 'manual') {\n DEBUG_BUILD && debug.warn('[Profiling] Session not sampled. Invalid `profileLifecycle` option.');\n return false;\n }\n\n // Session sampling: profileSessionSampleRate gates whether profiling is enabled for this session\n const profileSessionSampleRate = options.profileSessionSampleRate;\n\n if (!isValidSampleRate(profileSessionSampleRate)) {\n DEBUG_BUILD && debug.warn('[Profiling] Discarding profile because of invalid profileSessionSampleRate.');\n return false;\n }\n\n if (!profileSessionSampleRate) {\n DEBUG_BUILD &&\n debug.log('[Profiling] Discarding profile because profileSessionSampleRate is not defined or set to 0');\n return false;\n }\n\n return Math.random() <= profileSessionSampleRate;\n}\n\nexport function setThreadAttributes(span: Span): void {\n span.setAttribute('thread.id', PROFILER_THREAD_ID_STRING);\n span.setAttribute('thread.name', PROFILER_THREAD_NAME);\n}\n"],"names":["GLOBAL_OBJ","uuid4","_INTERNAL_performanceTimeToSeconds","getClient","getDebugImagesForResources","DEBUG_BUILD","debug","WINDOW"],"mappings":"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"}
|
|
@@ -30,18 +30,42 @@ class BrowserClient extends core.Client {
|
|
|
30
30
|
};
|
|
31
31
|
}
|
|
32
32
|
const { sendClientReports } = this._options;
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
33
|
+
const checkDrift = () => {
|
|
34
|
+
core.timestampInSeconds();
|
|
35
|
+
};
|
|
36
|
+
const listeners = [
|
|
37
|
+
// Flush buffered data when the page becomes hidden (e.g. tab switch, navigation, or the page
|
|
38
|
+
// being discarded). `flush()` emits the `flush` hook, which drains the span (streaming), log and
|
|
39
|
+
// metric buffers and hands the resulting envelopes to the transport (which uses `keepalive`).
|
|
40
|
+
// Client report outcomes don't listen to the `flush` hook, so we flush them separately.
|
|
41
|
+
[
|
|
42
|
+
helpers.WINDOW.document,
|
|
43
|
+
"visibilitychange",
|
|
44
|
+
() => {
|
|
45
|
+
checkDrift();
|
|
46
|
+
if (helpers.WINDOW.document.visibilityState === "hidden") {
|
|
47
|
+
if (sendClientReports) {
|
|
48
|
+
this._flushOutcomes();
|
|
49
|
+
}
|
|
50
|
+
queueMicrotask(() => {
|
|
51
|
+
void this.flush();
|
|
52
|
+
});
|
|
38
53
|
}
|
|
39
|
-
queueMicrotask(() => {
|
|
40
|
-
void this.flush();
|
|
41
|
-
});
|
|
42
54
|
}
|
|
43
|
-
|
|
44
|
-
|
|
55
|
+
],
|
|
56
|
+
// `freeze` and `resume` are only fired by Chromium browsers. Other browsers never fire them, so these listeners
|
|
57
|
+
// are no-ops there.
|
|
58
|
+
[helpers.WINDOW.document, "freeze", checkDrift],
|
|
59
|
+
[helpers.WINDOW.document, "resume", checkDrift],
|
|
60
|
+
// Pages restored from the back/forward cache were paused while they were cached.
|
|
61
|
+
[helpers.WINDOW, "pagehide", checkDrift],
|
|
62
|
+
[helpers.WINDOW, "pageshow", checkDrift]
|
|
63
|
+
];
|
|
64
|
+
listeners.forEach(([target, name, listener]) => target?.addEventListener?.(name, listener));
|
|
65
|
+
this.on(
|
|
66
|
+
"close",
|
|
67
|
+
() => listeners.forEach(([target, name, listener]) => target?.removeEventListener?.(name, listener))
|
|
68
|
+
);
|
|
45
69
|
if (userInfo) {
|
|
46
70
|
this.on("beforeSendSession", core.addAutoIpAddressToSession);
|
|
47
71
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"client.js","sources":["../../../../src/client.ts"],"sourcesContent":["import type {\n ClientOptions,\n Event,\n EventHint,\n Options as CoreOptions,\n ParameterizedString,\n Scope,\n SeverityLevel,\n} from '@sentry/core';\nimport type { BrowserClientReplayOptions } from '@sentry/core/browser';\nimport type { RouteProvider } from '@sentry/browser-utils';\nimport { setRouteProvider } from '@sentry/browser-utils';\nimport { addAutoIpAddressToSession, applySdkMetadata, Client, getSDKSource } from '@sentry/core';\nimport { eventFromException, eventFromMessage } from './eventbuilder';\nimport { WINDOW } from './helpers';\nimport type { BrowserTransportOptions } from './transports/types';\n\n/**\n * A magic string that build tooling can leverage in order to inject a release value into the SDK.\n */\ndeclare const __SENTRY_RELEASE__: string | undefined;\n\nexport type BrowserClientProfilingOptions = {\n /**\n * Sets profiling session sample rate for the entire profiling session.\n *\n * A profiling session corresponds to a user session, meaning it is set once at integration initialization and\n * persisted until the next page reload. This rate determines what percentage of user sessions will have profiling enabled.\n * @default 0\n */\n profileSessionSampleRate?: number;\n\n /**\n * Set the lifecycle mode of the profiler.\n * - **manual**: The profiler will be manually started and stopped via `startProfiler`/`stopProfiler`.\n * If a session is sampled, is dependent on the `profileSessionSampleRate`.\n * - **trace**: The profiler will be automatically started when a root span exists and stopped when there are no\n * more sampled root spans. Whether a session is sampled, is dependent on the `profileSessionSampleRate` and the\n * existing sampling configuration for tracing (`tracesSampleRate`/`tracesSampler`).\n *\n * @default 'manual'\n */\n profileLifecycle?: 'manual' | 'trace';\n};\n\ntype BrowserSpecificOptions = BrowserClientReplayOptions &\n BrowserClientProfilingOptions & {\n /** If configured, this URL will be used as base URL for lazy loading integration. */\n cdnBaseUrl?: string;\n\n /**\n * Important: Only set this option if you know what you are doing!\n *\n * By default, the SDK will check if `Sentry.init` is called in a browser extension.\n * In case it is, it will stop initialization and log a warning\n * because browser extensions require a different Sentry initialization process:\n * https://docs.sentry.io/platforms/javascript/best-practices/shared-environments/\n *\n * Setting up the SDK in a browser extension with global error monitoring is not recommended\n * and will likely flood you with errors from other web sites or extensions. This can heavily\n * impact your quota and cause interference with your and other Sentry SDKs in shared environments.\n *\n * If this check wrongfully flags your setup as a browser extension, you can set this\n * option to `true` to skip the check.\n *\n * @default false\n */\n skipBrowserExtensionCheck?: boolean;\n\n /**\n * If you use Spotlight by Sentry during development, use\n * this option to forward captured Sentry events to Spotlight.\n *\n * Either set it to true, or provide a specific Spotlight Sidecar URL.\n *\n * More details: https://spotlightjs.com/\n *\n * IMPORTANT: Only set this option to `true` while developing, not in production!\n */\n spotlight?: boolean | string;\n\n /**\n * Resolves URLs to low-cardinality route names, e.g. `/users/42` -> `/users/:id`.\n *\n * Framework SDKs set this for you. Set it yourself to name routes for a router the SDK has no\n * integration for. `setRouteProvider` replaces it at runtime.\n */\n routeProvider?: RouteProvider;\n };\n/**\n * Configuration options for the Sentry Browser SDK.\n * @see @sentry/core Options for more information.\n */\nexport type BrowserOptions = CoreOptions<BrowserTransportOptions> & BrowserSpecificOptions;\n\n/**\n * Configuration options for the Sentry Browser SDK Client class\n * @see BrowserClient for more information.\n */\nexport type BrowserClientOptions = ClientOptions<BrowserTransportOptions> & BrowserSpecificOptions;\n\n/**\n * The Sentry Browser SDK Client.\n *\n * @see BrowserOptions for documentation on configuration options.\n * @see SentryClient for usage documentation.\n */\nexport class BrowserClient extends Client<BrowserClientOptions> {\n /**\n * Creates a new Browser SDK instance.\n *\n * @param options Configuration options for this SDK.\n */\n public constructor(options: BrowserClientOptions) {\n const opts = applyDefaultOptions(options);\n const sdkSource = WINDOW.SENTRY_SDK_SOURCE || getSDKSource();\n applySdkMetadata(opts, 'browser', ['browser'], sdkSource);\n\n super(opts);\n\n if (opts.routeProvider) {\n setRouteProvider(opts.routeProvider, this);\n }\n\n // Unhandled errors don't actually crash the browser, so we report `unhandled` rather than `crashed`.\n this._unhandledSessionStatus = 'unhandled';\n\n const { userInfo } = this.getDataCollectionOptions();\n\n if (opts._metadata?.sdk) {\n opts._metadata.sdk.settings = {\n // Only allow IP inferral by Relay if the user opted in via dataCollection\n infer_ip: userInfo ? 'auto' : 'never',\n // purposefully allowing already passed settings to override the default\n ...opts._metadata.sdk.settings,\n };\n }\n\n const { sendClientReports } = this._options;\n\n // Flush buffered data when the page becomes hidden (e.g. tab switch, navigation, or the page\n // being discarded). `flush()` emits the `flush` hook, which drains the span (streaming), log and\n // metric buffers and hands the resulting envelopes to the transport (which uses `keepalive`).\n // Client report outcomes don't listen to the `flush` hook, so we flush them separately.\n if (WINDOW.document) {\n WINDOW.document.addEventListener('visibilitychange', () => {\n if (WINDOW.document.visibilityState === 'hidden') {\n if (sendClientReports) {\n this._flushOutcomes();\n }\n // Defer the flush to a microtask so that visibilitychange listeners registered after this\n // one have already run. In particular, browser tracing's background-tab detection ends the\n // active pageload/navigation (segment) span when the page is hidden. Deferring ensures that\n // segment span has been added to the streaming buffer before we flush, so it is sent\n // together with its child spans instead of being orphaned.\n queueMicrotask(() => {\n void this.flush();\n });\n }\n });\n }\n\n if (userInfo) {\n this.on('beforeSendSession', addAutoIpAddressToSession);\n }\n }\n\n /**\n * @inheritDoc\n */\n public eventFromException(exception: unknown, hint?: EventHint): PromiseLike<Event> {\n return eventFromException(this._options.stackParser, exception, hint, this._options.attachStacktrace);\n }\n\n /**\n * @inheritDoc\n */\n public eventFromMessage(\n message: ParameterizedString,\n level: SeverityLevel = 'info',\n hint?: EventHint,\n ): PromiseLike<Event> {\n return eventFromMessage(this._options.stackParser, message, level, hint, this._options.attachStacktrace);\n }\n\n /**\n * @inheritDoc\n */\n protected _prepareEvent(\n event: Event,\n hint: EventHint,\n currentScope: Scope,\n isolationScope: Scope,\n ): PromiseLike<Event | null> {\n event.platform = event.platform || 'javascript';\n\n return super._prepareEvent(event, hint, currentScope, isolationScope);\n }\n}\n\n/** Exported only for tests. */\nexport function applyDefaultOptions<T extends Partial<BrowserClientOptions>>(optionsArg: T): T {\n return {\n release:\n typeof __SENTRY_RELEASE__ === 'string' // This allows build tooling to find-and-replace __SENTRY_RELEASE__ to inject a release value\n ? __SENTRY_RELEASE__\n : WINDOW.SENTRY_RELEASE?.id, // This supports the variable that sentry-webpack-plugin injects\n sendClientReports: true,\n // We default this to true, as it is the safer scenario\n parentSpanIsAlwaysRootSpan: true,\n ...optionsArg,\n };\n}\n"],"names":["Client","WINDOW","getSDKSource","applySdkMetadata","setRouteProvider","addAutoIpAddressToSession","eventFromException","eventFromMessage"],"mappings":";;;;;;;AA2GO,MAAM,sBAAsBA,WAAA,CAA6B;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAMvD,YAAY,OAAA,EAA+B;AAChD,IAAA,MAAM,IAAA,GAAO,oBAAoB,OAAO,CAAA;AACxC,IAAA,MAAM,SAAA,GAAYC,cAAA,CAAO,iBAAA,IAAqBC,iBAAA,EAAa;AAC3D,IAAAC,qBAAA,CAAiB,IAAA,EAAM,SAAA,EAAW,CAAC,SAAS,GAAG,SAAS,CAAA;AAExD,IAAA,KAAA,CAAM,IAAI,CAAA;AAEV,IAAA,IAAI,KAAK,aAAA,EAAe;AACtB,MAAAC,6BAAA,CAAiB,IAAA,CAAK,eAAe,IAAI,CAAA;AAAA,IAC3C;AAGA,IAAA,IAAA,CAAK,uBAAA,GAA0B,WAAA;AAE/B,IAAA,MAAM,EAAE,QAAA,EAAS,GAAI,IAAA,CAAK,wBAAA,EAAyB;AAEnD,IAAA,IAAI,IAAA,CAAK,WAAW,GAAA,EAAK;AACvB,MAAA,IAAA,CAAK,SAAA,CAAU,IAAI,QAAA,GAAW;AAAA;AAAA,QAE5B,QAAA,EAAU,WAAW,MAAA,GAAS,OAAA;AAAA;AAAA,QAE9B,GAAG,IAAA,CAAK,SAAA,CAAU,GAAA,CAAI;AAAA,OACxB;AAAA,IACF;AAEA,IAAA,MAAM,EAAE,iBAAA,EAAkB,GAAI,IAAA,CAAK,QAAA;AAMnC,IAAA,IAAIH,eAAO,QAAA,EAAU;AACnB,MAAAA,cAAA,CAAO,QAAA,CAAS,gBAAA,CAAiB,kBAAA,EAAoB,MAAM;AACzD,QAAA,IAAIA,cAAA,CAAO,QAAA,CAAS,eAAA,KAAoB,QAAA,EAAU;AAChD,UAAA,IAAI,iBAAA,EAAmB;AACrB,YAAA,IAAA,CAAK,cAAA,EAAe;AAAA,UACtB;AAMA,UAAA,cAAA,CAAe,MAAM;AACnB,YAAA,KAAK,KAAK,KAAA,EAAM;AAAA,UAClB,CAAC,CAAA;AAAA,QACH;AAAA,MACF,CAAC,CAAA;AAAA,IACH;AAEA,IAAA,IAAI,QAAA,EAAU;AACZ,MAAA,IAAA,CAAK,EAAA,CAAG,qBAAqBI,8BAAyB,CAAA;AAAA,IACxD;AAAA,EACF;AAAA;AAAA;AAAA;AAAA,EAKO,kBAAA,CAAmB,WAAoB,IAAA,EAAsC;AAClF,IAAA,OAAOC,+BAAA,CAAmB,KAAK,QAAA,CAAS,WAAA,EAAa,WAAW,IAAA,EAAM,IAAA,CAAK,SAAS,gBAAgB,CAAA;AAAA,EACtG;AAAA;AAAA;AAAA;AAAA,EAKO,gBAAA,CACL,OAAA,EACA,KAAA,GAAuB,MAAA,EACvB,IAAA,EACoB;AACpB,IAAA,OAAOC,6BAAA,CAAiB,KAAK,QAAA,CAAS,WAAA,EAAa,SAAS,KAAA,EAAO,IAAA,EAAM,IAAA,CAAK,QAAA,CAAS,gBAAgB,CAAA;AAAA,EACzG;AAAA;AAAA;AAAA;AAAA,EAKU,aAAA,CACR,KAAA,EACA,IAAA,EACA,YAAA,EACA,cAAA,EAC2B;AAC3B,IAAA,KAAA,CAAM,QAAA,GAAW,MAAM,QAAA,IAAY,YAAA;AAEnC,IAAA,OAAO,KAAA,CAAM,aAAA,CAAc,KAAA,EAAO,IAAA,EAAM,cAAc,cAAc,CAAA;AAAA,EACtE;AACF;AAGO,SAAS,oBAA6D,UAAA,EAAkB;AAC7F,EAAA,OAAO;AAAA,IACL,SACE,OAAO,kBAAA,KAAuB,QAAA,GAC1B,kBAAA,GACAN,eAAO,cAAA,EAAgB,EAAA;AAAA;AAAA,IAC7B,iBAAA,EAAmB,IAAA;AAAA;AAAA,IAEnB,0BAAA,EAA4B,IAAA;AAAA,IAC5B,GAAG;AAAA,GACL;AACF;;;;;"}
|
|
1
|
+
{"version":3,"file":"client.js","sources":["../../../../src/client.ts"],"sourcesContent":["import type {\n ClientOptions,\n Event,\n EventHint,\n Options as CoreOptions,\n ParameterizedString,\n Scope,\n SeverityLevel,\n} from '@sentry/core';\nimport type { BrowserClientReplayOptions } from '@sentry/core/browser';\nimport type { RouteProvider } from '@sentry/browser-utils';\nimport { setRouteProvider } from '@sentry/browser-utils';\nimport { addAutoIpAddressToSession, applySdkMetadata, Client, getSDKSource, timestampInSeconds } from '@sentry/core';\nimport { eventFromException, eventFromMessage } from './eventbuilder';\nimport { WINDOW } from './helpers';\nimport type { BrowserTransportOptions } from './transports/types';\n\n/**\n * A magic string that build tooling can leverage in order to inject a release value into the SDK.\n */\ndeclare const __SENTRY_RELEASE__: string | undefined;\n\nexport type BrowserClientProfilingOptions = {\n /**\n * Sets profiling session sample rate for the entire profiling session.\n *\n * A profiling session corresponds to a user session, meaning it is set once at integration initialization and\n * persisted until the next page reload. This rate determines what percentage of user sessions will have profiling enabled.\n * @default 0\n */\n profileSessionSampleRate?: number;\n\n /**\n * Set the lifecycle mode of the profiler.\n * - **manual**: The profiler will be manually started and stopped via `startProfiler`/`stopProfiler`.\n * If a session is sampled, is dependent on the `profileSessionSampleRate`.\n * - **trace**: The profiler will be automatically started when a root span exists and stopped when there are no\n * more sampled root spans. Whether a session is sampled, is dependent on the `profileSessionSampleRate` and the\n * existing sampling configuration for tracing (`tracesSampleRate`/`tracesSampler`).\n *\n * @default 'manual'\n */\n profileLifecycle?: 'manual' | 'trace';\n};\n\ntype BrowserSpecificOptions = BrowserClientReplayOptions &\n BrowserClientProfilingOptions & {\n /** If configured, this URL will be used as base URL for lazy loading integration. */\n cdnBaseUrl?: string;\n\n /**\n * Important: Only set this option if you know what you are doing!\n *\n * By default, the SDK will check if `Sentry.init` is called in a browser extension.\n * In case it is, it will stop initialization and log a warning\n * because browser extensions require a different Sentry initialization process:\n * https://docs.sentry.io/platforms/javascript/best-practices/shared-environments/\n *\n * Setting up the SDK in a browser extension with global error monitoring is not recommended\n * and will likely flood you with errors from other web sites or extensions. This can heavily\n * impact your quota and cause interference with your and other Sentry SDKs in shared environments.\n *\n * If this check wrongfully flags your setup as a browser extension, you can set this\n * option to `true` to skip the check.\n *\n * @default false\n */\n skipBrowserExtensionCheck?: boolean;\n\n /**\n * If you use Spotlight by Sentry during development, use\n * this option to forward captured Sentry events to Spotlight.\n *\n * Either set it to true, or provide a specific Spotlight Sidecar URL.\n *\n * More details: https://spotlightjs.com/\n *\n * IMPORTANT: Only set this option to `true` while developing, not in production!\n */\n spotlight?: boolean | string;\n\n /**\n * Resolves URLs to low-cardinality route names, e.g. `/users/42` -> `/users/:id`.\n *\n * Framework SDKs set this for you. Set it yourself to name routes for a router the SDK has no\n * integration for. `setRouteProvider` replaces it at runtime.\n */\n routeProvider?: RouteProvider;\n };\n/**\n * Configuration options for the Sentry Browser SDK.\n * @see @sentry/core Options for more information.\n */\nexport type BrowserOptions = CoreOptions<BrowserTransportOptions> & BrowserSpecificOptions;\n\n/**\n * Configuration options for the Sentry Browser SDK Client class\n * @see BrowserClient for more information.\n */\nexport type BrowserClientOptions = ClientOptions<BrowserTransportOptions> & BrowserSpecificOptions;\n\n/**\n * The Sentry Browser SDK Client.\n *\n * @see BrowserOptions for documentation on configuration options.\n * @see SentryClient for usage documentation.\n */\nexport class BrowserClient extends Client<BrowserClientOptions> {\n /**\n * Creates a new Browser SDK instance.\n *\n * @param options Configuration options for this SDK.\n */\n public constructor(options: BrowserClientOptions) {\n const opts = applyDefaultOptions(options);\n const sdkSource = WINDOW.SENTRY_SDK_SOURCE || getSDKSource();\n applySdkMetadata(opts, 'browser', ['browser'], sdkSource);\n\n super(opts);\n\n if (opts.routeProvider) {\n setRouteProvider(opts.routeProvider, this);\n }\n\n // Unhandled errors don't actually crash the browser, so we report `unhandled` rather than `crashed`.\n this._unhandledSessionStatus = 'unhandled';\n\n const { userInfo } = this.getDataCollectionOptions();\n\n if (opts._metadata?.sdk) {\n opts._metadata.sdk.settings = {\n // Only allow IP inferral by Relay if the user opted in via dataCollection\n infer_ip: userInfo ? 'auto' : 'never',\n // purposefully allowing already passed settings to override the default\n ...opts._metadata.sdk.settings,\n };\n }\n\n const { sendClientReports } = this._options;\n\n // Devices usually hide the page before they sleep and show it again after they wake up. Checking for drift\n // at both points makes the time origin correction happen at the sleep, rather than at the next regular timestamp call.\n const checkDrift = (): void => {\n timestampInSeconds();\n };\n\n // Most bundle-size-efficient way to add and remove listeners: Create a table to avoid repeated\n // addeventlistener and removeeventlistener calls. The alternative, using an AbortController, can\n // lead to app breakage on old Angular<>Zone.js < 0.14.3 versions.\n const listeners: [EventTarget | undefined, string, () => void][] = [\n // Flush buffered data when the page becomes hidden (e.g. tab switch, navigation, or the page\n // being discarded). `flush()` emits the `flush` hook, which drains the span (streaming), log and\n // metric buffers and hands the resulting envelopes to the transport (which uses `keepalive`).\n // Client report outcomes don't listen to the `flush` hook, so we flush them separately.\n [\n WINDOW.document,\n 'visibilitychange',\n () => {\n // Devices usually hide the page before they sleep and show it again after they wake up. Checking for drift\n // at both points makes the time origin correction happen at the sleep, rather than at the next regular timestamp call.\n checkDrift();\n\n if (WINDOW.document.visibilityState === 'hidden') {\n if (sendClientReports) {\n this._flushOutcomes();\n }\n // Defer the flush to a microtask so that visibilitychange listeners registered after this\n // one have already run. In particular, browser tracing's background-tab detection ends the\n // active pageload/navigation (segment) span when the page is hidden. Deferring ensures that\n // segment span has been added to the streaming buffer before we flush, so it is sent\n // together with its child spans instead of being orphaned.\n queueMicrotask(() => {\n void this.flush();\n });\n }\n },\n ],\n // `freeze` and `resume` are only fired by Chromium browsers. Other browsers never fire them, so these listeners\n // are no-ops there.\n [WINDOW.document, 'freeze', checkDrift],\n [WINDOW.document, 'resume', checkDrift],\n // Pages restored from the back/forward cache were paused while they were cached.\n [WINDOW, 'pagehide', checkDrift],\n [WINDOW, 'pageshow', checkDrift],\n ];\n\n listeners.forEach(([target, name, listener]) => target?.addEventListener?.(name, listener));\n this.on('close', () =>\n listeners.forEach(([target, name, listener]) => target?.removeEventListener?.(name, listener)),\n );\n\n if (userInfo) {\n this.on('beforeSendSession', addAutoIpAddressToSession);\n }\n }\n\n /**\n * @inheritDoc\n */\n public eventFromException(exception: unknown, hint?: EventHint): PromiseLike<Event> {\n return eventFromException(this._options.stackParser, exception, hint, this._options.attachStacktrace);\n }\n\n /**\n * @inheritDoc\n */\n public eventFromMessage(\n message: ParameterizedString,\n level: SeverityLevel = 'info',\n hint?: EventHint,\n ): PromiseLike<Event> {\n return eventFromMessage(this._options.stackParser, message, level, hint, this._options.attachStacktrace);\n }\n\n /**\n * @inheritDoc\n */\n protected _prepareEvent(\n event: Event,\n hint: EventHint,\n currentScope: Scope,\n isolationScope: Scope,\n ): PromiseLike<Event | null> {\n event.platform = event.platform || 'javascript';\n\n return super._prepareEvent(event, hint, currentScope, isolationScope);\n }\n}\n\n/** Exported only for tests. */\nexport function applyDefaultOptions<T extends Partial<BrowserClientOptions>>(optionsArg: T): T {\n return {\n release:\n typeof __SENTRY_RELEASE__ === 'string' // This allows build tooling to find-and-replace __SENTRY_RELEASE__ to inject a release value\n ? __SENTRY_RELEASE__\n : WINDOW.SENTRY_RELEASE?.id, // This supports the variable that sentry-webpack-plugin injects\n sendClientReports: true,\n // We default this to true, as it is the safer scenario\n parentSpanIsAlwaysRootSpan: true,\n ...optionsArg,\n };\n}\n"],"names":["Client","WINDOW","getSDKSource","applySdkMetadata","setRouteProvider","timestampInSeconds","addAutoIpAddressToSession","eventFromException","eventFromMessage"],"mappings":"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"}
|
|
@@ -95,16 +95,13 @@ function convertToContinuousProfile(input) {
|
|
|
95
95
|
}
|
|
96
96
|
stacks[i] = list;
|
|
97
97
|
}
|
|
98
|
-
const perfOrigin = core.browserPerformanceTimeOrigin();
|
|
99
|
-
const origin = typeof performance.timeOrigin === "number" ? performance.timeOrigin : perfOrigin || 0;
|
|
100
|
-
const adjustForOriginChange = origin - (perfOrigin || origin);
|
|
101
98
|
const samples = [];
|
|
102
99
|
for (let i = 0; i < input.samples.length; i++) {
|
|
103
100
|
const sample = input.samples[i];
|
|
104
101
|
if (!sample) {
|
|
105
102
|
continue;
|
|
106
103
|
}
|
|
107
|
-
const timestampSeconds = (
|
|
104
|
+
const timestampSeconds = core._INTERNAL_performanceTimeToSeconds(sample.timestamp);
|
|
108
105
|
samples[i] = {
|
|
109
106
|
stack_id: sample.stackId ?? 0,
|
|
110
107
|
thread_id: PROFILER_THREAD_ID_STRING,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"utils.js","sources":["../../../../../src/profiling/utils.ts"],"sourcesContent":["/* eslint-disable max-lines */\nimport type { Client, ContinuousThreadCpuProfile, DebugImage, ProfileChunk, Span } from '@sentry/core';\nimport {\n browserPerformanceTimeOrigin,\n debug,\n getClient,\n getDebugImagesForResources,\n GLOBAL_OBJ,\n uuid4,\n} from '@sentry/core';\nimport type { BrowserOptions } from '../client';\nimport { DEBUG_BUILD } from '../debug-build';\nimport { WINDOW } from '../helpers';\nimport type { JSSelfProfile, JSSelfProfiler, JSSelfProfilerConstructor } from './jsSelfProfiling';\n\n// Checking if we are in Main or Worker thread: `self` (not `window`) is the `globalThis` in Web Workers and `importScripts` are only available in Web Workers\nconst isMainThread = 'window' in GLOBAL_OBJ && GLOBAL_OBJ.window === GLOBAL_OBJ && typeof importScripts === 'undefined';\n\n// Setting ID to 0 as we cannot get an ID from Web Workers\nexport const PROFILER_THREAD_ID_STRING = String(0);\nexport const PROFILER_THREAD_NAME = isMainThread ? 'main' : 'worker';\n\n/**\n * Create a profile chunk envelope item\n */\nexport function createProfileChunkPayload(\n jsSelfProfile: JSSelfProfile,\n client: Client,\n profilerId?: string,\n): ProfileChunk {\n // only == to catch null and undefined\n if (jsSelfProfile == null) {\n throw new TypeError(\n `Cannot construct profiling event envelope without a valid profile. Got ${jsSelfProfile} instead.`,\n );\n }\n\n const continuousProfile = convertToContinuousProfile(jsSelfProfile);\n\n const options = client.getOptions();\n const sdk = client.getSdkMetadata?.()?.sdk;\n\n return {\n chunk_id: uuid4(),\n client_sdk: {\n name: sdk?.name ?? 'sentry.javascript.browser',\n version: sdk?.version ?? '0.0.0',\n },\n profiler_id: profilerId || uuid4(),\n platform: 'javascript',\n version: '2',\n release: options.release ?? '',\n environment: options.environment ?? 'production',\n debug_meta: {\n // function name obfuscation\n images: applyDebugMetadata(jsSelfProfile.resources),\n },\n profile: continuousProfile,\n };\n}\n\n/**\n * Validate a profile chunk against the Sample Format V2 requirements.\n * https://develop.sentry.dev/sdk/telemetry/profiles/sample-format-v2/\n * - Presence of samples, stacks, frames\n * - Required metadata fields\n */\nexport function validateProfileChunk(chunk: ProfileChunk): { valid: true } | { reason: string } {\n try {\n // Required metadata\n if (!chunk || typeof chunk !== 'object') {\n return { reason: 'chunk is not an object' };\n }\n\n // profiler_id and chunk_id must be 32 lowercase hex chars\n const isHex32 = (val: unknown): boolean => typeof val === 'string' && /^[a-f0-9]{32}$/.test(val);\n if (!isHex32(chunk.profiler_id)) {\n return { reason: 'missing or invalid profiler_id' };\n }\n if (!isHex32(chunk.chunk_id)) {\n return { reason: 'missing or invalid chunk_id' };\n }\n\n if (!chunk.client_sdk) {\n return { reason: 'missing client_sdk metadata' };\n }\n\n // Profile data must have frames, stacks, samples\n const profile = chunk.profile as { frames?: unknown[]; stacks?: unknown[]; samples?: unknown[] } | undefined;\n if (!profile) {\n return { reason: 'missing profile data' };\n }\n\n if (!Array.isArray(profile.frames) || !profile.frames.length) {\n return { reason: 'profile has no frames' };\n }\n if (!Array.isArray(profile.stacks) || !profile.stacks.length) {\n return { reason: 'profile has no stacks' };\n }\n if (!Array.isArray(profile.samples) || !profile.samples.length) {\n return { reason: 'profile has no samples' };\n }\n\n return { valid: true };\n } catch (e) {\n return { reason: `unknown validation error: ${e}` };\n }\n}\n\n/**\n * Convert from JSSelfProfile format to ContinuousThreadCpuProfile format.\n */\nfunction convertToContinuousProfile(input: {\n frames: { name: string; resourceId?: number; line?: number; column?: number }[];\n stacks: { frameId: number; parentId?: number }[];\n samples: { timestamp: number; stackId?: number }[];\n resources: string[];\n}): ContinuousThreadCpuProfile {\n // Frames map 1:1 by index; fill only when present to avoid sparse writes\n const frames: ContinuousThreadCpuProfile['frames'] = [];\n for (let i = 0; i < input.frames.length; i++) {\n const frame = input.frames[i];\n if (!frame) {\n continue;\n }\n frames[i] = {\n function: frame.name,\n abs_path: typeof frame.resourceId === 'number' ? input.resources[frame.resourceId] : undefined,\n lineno: frame.line,\n colno: frame.column,\n };\n }\n\n // Build stacks by following parent links, top->down order (root last)\n const stacks: ContinuousThreadCpuProfile['stacks'] = [];\n for (let i = 0; i < input.stacks.length; i++) {\n const stackHead = input.stacks[i];\n if (!stackHead) {\n continue;\n }\n const list: number[] = [];\n let current: { frameId: number; parentId?: number } | undefined = stackHead;\n while (current) {\n list.push(current.frameId);\n current = current.parentId === undefined ? undefined : input.stacks[current.parentId];\n }\n stacks[i] = list;\n }\n\n // Align timestamps to SDK time origin to match span/event timelines\n const perfOrigin = browserPerformanceTimeOrigin();\n const origin = typeof performance.timeOrigin === 'number' ? performance.timeOrigin : perfOrigin || 0;\n const adjustForOriginChange = origin - (perfOrigin || origin);\n\n const samples: ContinuousThreadCpuProfile['samples'] = [];\n for (let i = 0; i < input.samples.length; i++) {\n const sample = input.samples[i];\n if (!sample) {\n continue;\n }\n // Convert ms to seconds epoch-based timestamp\n const timestampSeconds = (origin + (sample.timestamp - adjustForOriginChange)) / 1000;\n samples[i] = {\n stack_id: sample.stackId ?? 0,\n thread_id: PROFILER_THREAD_ID_STRING,\n timestamp: timestampSeconds,\n };\n }\n\n return {\n frames,\n stacks,\n samples,\n thread_metadata: { [PROFILER_THREAD_ID_STRING]: { name: PROFILER_THREAD_NAME } },\n };\n}\n\n/**\n * Applies debug meta data to an event from a list of paths to resources (sourcemaps)\n */\nexport function applyDebugMetadata(resource_paths: ReadonlyArray<string>): DebugImage[] {\n const client = getClient();\n const options = client?.getOptions();\n const stackParser = options?.stackParser;\n\n if (!stackParser) {\n return [];\n }\n\n return getDebugImagesForResources(stackParser, resource_paths);\n}\n\n/**\n * Checks the given sample rate to make sure it is valid type and value (a boolean, or a number between 0 and 1).\n */\nexport function isValidSampleRate(rate: unknown): boolean {\n // we need to check NaN explicitly because it's of type 'number' and therefore wouldn't get caught by this typecheck\n if ((typeof rate !== 'number' && typeof rate !== 'boolean') || (typeof rate === 'number' && isNaN(rate))) {\n DEBUG_BUILD &&\n debug.warn(\n `[Profiling] Invalid sample rate. Sample rate must be a boolean or a number between 0 and 1. Got ${JSON.stringify(\n rate,\n )} of type ${JSON.stringify(typeof rate)}.`,\n );\n return false;\n }\n\n // Boolean sample rates are always valid\n if (rate === true || rate === false) {\n return true;\n }\n\n // in case sampleRate is a boolean, it will get automatically cast to 1 if it's true and 0 if it's false\n if (rate < 0 || rate > 1) {\n DEBUG_BUILD && debug.warn(`[Profiling] Invalid sample rate. Sample rate must be between 0 and 1. Got ${rate}.`);\n return false;\n }\n return true;\n}\n\n// Keep a flag value to avoid re-initializing the profiler constructor. If it fails\n// once, it will always fail and this allows us to early return.\nlet PROFILING_CONSTRUCTOR_FAILED: boolean = false;\nexport const MAX_PROFILE_DURATION_MS = 30_000;\n\n/**\n * Check if profiler constructor is available.\n * @param maybeProfiler\n */\nfunction isJSProfilerSupported(maybeProfiler: unknown): maybeProfiler is typeof JSSelfProfilerConstructor {\n return typeof maybeProfiler === 'function';\n}\n\n/**\n * Starts the profiler and returns the profiler instance.\n */\nexport function startJSSelfProfile(): JSSelfProfiler | undefined {\n // Feature support check first\n const JSProfilerConstructor = WINDOW.Profiler;\n\n if (!isJSProfilerSupported(JSProfilerConstructor)) {\n if (DEBUG_BUILD) {\n debug.log('[Profiling] Profiling is not supported by this browser, Profiler interface missing on window object.');\n }\n return;\n }\n\n // From initial testing, it seems that the minimum value for sampleInterval is 10ms.\n const samplingIntervalMS = 10;\n // Start the profiler\n const maxSamples = Math.floor(MAX_PROFILE_DURATION_MS / samplingIntervalMS);\n\n // Attempt to initialize the profiler constructor, if it fails, we disable profiling for the current user session.\n // This is likely due to a missing 'Document-Policy': 'js-profiling' header. We do not want to throw an error if this happens\n // as we risk breaking the user's application, so just disable profiling and log an error.\n try {\n return new JSProfilerConstructor({ sampleInterval: samplingIntervalMS, maxBufferSize: maxSamples });\n } catch {\n if (DEBUG_BUILD) {\n debug.log(\n \"[Profiling] Failed to initialize the Profiling constructor, this is likely due to a missing 'Document-Policy': 'js-profiling' header.\",\n );\n debug.log('[Profiling] Disabling profiling for current user session.');\n }\n PROFILING_CONSTRUCTOR_FAILED = true;\n }\n\n return;\n}\n\n/**\n * Determine if a profile should be created for the current session.\n */\nexport function shouldProfileSession(options: BrowserOptions): boolean {\n // If constructor failed once, it will always fail, so we can early return.\n if (PROFILING_CONSTRUCTOR_FAILED) {\n if (DEBUG_BUILD) {\n debug.log(\n '[Profiling] Profiling has been disabled for the duration of the current user session as the JS Profiler could not be started.',\n );\n }\n return false;\n }\n\n if (options.profileLifecycle !== 'trace' && options.profileLifecycle !== 'manual') {\n DEBUG_BUILD && debug.warn('[Profiling] Session not sampled. Invalid `profileLifecycle` option.');\n return false;\n }\n\n // Session sampling: profileSessionSampleRate gates whether profiling is enabled for this session\n const profileSessionSampleRate = options.profileSessionSampleRate;\n\n if (!isValidSampleRate(profileSessionSampleRate)) {\n DEBUG_BUILD && debug.warn('[Profiling] Discarding profile because of invalid profileSessionSampleRate.');\n return false;\n }\n\n if (!profileSessionSampleRate) {\n DEBUG_BUILD &&\n debug.log('[Profiling] Discarding profile because profileSessionSampleRate is not defined or set to 0');\n return false;\n }\n\n return Math.random() <= profileSessionSampleRate;\n}\n\nexport function setThreadAttributes(span: Span): void {\n span.setAttribute('thread.id', PROFILER_THREAD_ID_STRING);\n span.setAttribute('thread.name', PROFILER_THREAD_NAME);\n}\n"],"names":["GLOBAL_OBJ","uuid4","browserPerformanceTimeOrigin","getClient","getDebugImagesForResources","DEBUG_BUILD","debug","WINDOW"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"utils.js","sources":["../../../../../src/profiling/utils.ts"],"sourcesContent":["/* eslint-disable max-lines */\nimport type { Client, ContinuousThreadCpuProfile, DebugImage, ProfileChunk, Span } from '@sentry/core';\nimport {\n debug,\n getClient,\n getDebugImagesForResources,\n GLOBAL_OBJ,\n _INTERNAL_performanceTimeToSeconds,\n uuid4,\n} from '@sentry/core';\nimport type { BrowserOptions } from '../client';\nimport { DEBUG_BUILD } from '../debug-build';\nimport { WINDOW } from '../helpers';\nimport type { JSSelfProfile, JSSelfProfiler, JSSelfProfilerConstructor } from './jsSelfProfiling';\n\n// Checking if we are in Main or Worker thread: `self` (not `window`) is the `globalThis` in Web Workers and `importScripts` are only available in Web Workers\nconst isMainThread = 'window' in GLOBAL_OBJ && GLOBAL_OBJ.window === GLOBAL_OBJ && typeof importScripts === 'undefined';\n\n// Setting ID to 0 as we cannot get an ID from Web Workers\nexport const PROFILER_THREAD_ID_STRING = String(0);\nexport const PROFILER_THREAD_NAME = isMainThread ? 'main' : 'worker';\n\n/**\n * Create a profile chunk envelope item\n */\nexport function createProfileChunkPayload(\n jsSelfProfile: JSSelfProfile,\n client: Client,\n profilerId?: string,\n): ProfileChunk {\n // only == to catch null and undefined\n if (jsSelfProfile == null) {\n throw new TypeError(\n `Cannot construct profiling event envelope without a valid profile. Got ${jsSelfProfile} instead.`,\n );\n }\n\n const continuousProfile = convertToContinuousProfile(jsSelfProfile);\n\n const options = client.getOptions();\n const sdk = client.getSdkMetadata?.()?.sdk;\n\n return {\n chunk_id: uuid4(),\n client_sdk: {\n name: sdk?.name ?? 'sentry.javascript.browser',\n version: sdk?.version ?? '0.0.0',\n },\n profiler_id: profilerId || uuid4(),\n platform: 'javascript',\n version: '2',\n release: options.release ?? '',\n environment: options.environment ?? 'production',\n debug_meta: {\n // function name obfuscation\n images: applyDebugMetadata(jsSelfProfile.resources),\n },\n profile: continuousProfile,\n };\n}\n\n/**\n * Validate a profile chunk against the Sample Format V2 requirements.\n * https://develop.sentry.dev/sdk/telemetry/profiles/sample-format-v2/\n * - Presence of samples, stacks, frames\n * - Required metadata fields\n */\nexport function validateProfileChunk(chunk: ProfileChunk): { valid: true } | { reason: string } {\n try {\n // Required metadata\n if (!chunk || typeof chunk !== 'object') {\n return { reason: 'chunk is not an object' };\n }\n\n // profiler_id and chunk_id must be 32 lowercase hex chars\n const isHex32 = (val: unknown): boolean => typeof val === 'string' && /^[a-f0-9]{32}$/.test(val);\n if (!isHex32(chunk.profiler_id)) {\n return { reason: 'missing or invalid profiler_id' };\n }\n if (!isHex32(chunk.chunk_id)) {\n return { reason: 'missing or invalid chunk_id' };\n }\n\n if (!chunk.client_sdk) {\n return { reason: 'missing client_sdk metadata' };\n }\n\n // Profile data must have frames, stacks, samples\n const profile = chunk.profile as { frames?: unknown[]; stacks?: unknown[]; samples?: unknown[] } | undefined;\n if (!profile) {\n return { reason: 'missing profile data' };\n }\n\n if (!Array.isArray(profile.frames) || !profile.frames.length) {\n return { reason: 'profile has no frames' };\n }\n if (!Array.isArray(profile.stacks) || !profile.stacks.length) {\n return { reason: 'profile has no stacks' };\n }\n if (!Array.isArray(profile.samples) || !profile.samples.length) {\n return { reason: 'profile has no samples' };\n }\n\n return { valid: true };\n } catch (e) {\n return { reason: `unknown validation error: ${e}` };\n }\n}\n\n/**\n * Convert from JSSelfProfile format to ContinuousThreadCpuProfile format.\n */\nfunction convertToContinuousProfile(input: {\n frames: { name: string; resourceId?: number; line?: number; column?: number }[];\n stacks: { frameId: number; parentId?: number }[];\n samples: { timestamp: number; stackId?: number }[];\n resources: string[];\n}): ContinuousThreadCpuProfile {\n // Frames map 1:1 by index; fill only when present to avoid sparse writes\n const frames: ContinuousThreadCpuProfile['frames'] = [];\n for (let i = 0; i < input.frames.length; i++) {\n const frame = input.frames[i];\n if (!frame) {\n continue;\n }\n frames[i] = {\n function: frame.name,\n abs_path: typeof frame.resourceId === 'number' ? input.resources[frame.resourceId] : undefined,\n lineno: frame.line,\n colno: frame.column,\n };\n }\n\n // Build stacks by following parent links, top->down order (root last)\n const stacks: ContinuousThreadCpuProfile['stacks'] = [];\n for (let i = 0; i < input.stacks.length; i++) {\n const stackHead = input.stacks[i];\n if (!stackHead) {\n continue;\n }\n const list: number[] = [];\n let current: { frameId: number; parentId?: number } | undefined = stackHead;\n while (current) {\n list.push(current.frameId);\n current = current.parentId === undefined ? undefined : input.stacks[current.parentId];\n }\n stacks[i] = list;\n }\n\n const samples: ContinuousThreadCpuProfile['samples'] = [];\n for (let i = 0; i < input.samples.length; i++) {\n const sample = input.samples[i];\n if (!sample) {\n continue;\n }\n // Sample timestamps are relative to `performance.timeOrigin`, so we convert them like performance entries.\n // The cast is safe: the JS Self-Profiling API only exists if the Performance API does.\n const timestampSeconds = _INTERNAL_performanceTimeToSeconds(sample.timestamp) as number;\n samples[i] = {\n stack_id: sample.stackId ?? 0,\n thread_id: PROFILER_THREAD_ID_STRING,\n timestamp: timestampSeconds,\n };\n }\n\n return {\n frames,\n stacks,\n samples,\n thread_metadata: { [PROFILER_THREAD_ID_STRING]: { name: PROFILER_THREAD_NAME } },\n };\n}\n\n/**\n * Applies debug meta data to an event from a list of paths to resources (sourcemaps)\n */\nexport function applyDebugMetadata(resource_paths: ReadonlyArray<string>): DebugImage[] {\n const client = getClient();\n const options = client?.getOptions();\n const stackParser = options?.stackParser;\n\n if (!stackParser) {\n return [];\n }\n\n return getDebugImagesForResources(stackParser, resource_paths);\n}\n\n/**\n * Checks the given sample rate to make sure it is valid type and value (a boolean, or a number between 0 and 1).\n */\nexport function isValidSampleRate(rate: unknown): boolean {\n // we need to check NaN explicitly because it's of type 'number' and therefore wouldn't get caught by this typecheck\n if ((typeof rate !== 'number' && typeof rate !== 'boolean') || (typeof rate === 'number' && isNaN(rate))) {\n DEBUG_BUILD &&\n debug.warn(\n `[Profiling] Invalid sample rate. Sample rate must be a boolean or a number between 0 and 1. Got ${JSON.stringify(\n rate,\n )} of type ${JSON.stringify(typeof rate)}.`,\n );\n return false;\n }\n\n // Boolean sample rates are always valid\n if (rate === true || rate === false) {\n return true;\n }\n\n // in case sampleRate is a boolean, it will get automatically cast to 1 if it's true and 0 if it's false\n if (rate < 0 || rate > 1) {\n DEBUG_BUILD && debug.warn(`[Profiling] Invalid sample rate. Sample rate must be between 0 and 1. Got ${rate}.`);\n return false;\n }\n return true;\n}\n\n// Keep a flag value to avoid re-initializing the profiler constructor. If it fails\n// once, it will always fail and this allows us to early return.\nlet PROFILING_CONSTRUCTOR_FAILED: boolean = false;\nexport const MAX_PROFILE_DURATION_MS = 30_000;\n\n/**\n * Check if profiler constructor is available.\n * @param maybeProfiler\n */\nfunction isJSProfilerSupported(maybeProfiler: unknown): maybeProfiler is typeof JSSelfProfilerConstructor {\n return typeof maybeProfiler === 'function';\n}\n\n/**\n * Starts the profiler and returns the profiler instance.\n */\nexport function startJSSelfProfile(): JSSelfProfiler | undefined {\n // Feature support check first\n const JSProfilerConstructor = WINDOW.Profiler;\n\n if (!isJSProfilerSupported(JSProfilerConstructor)) {\n if (DEBUG_BUILD) {\n debug.log('[Profiling] Profiling is not supported by this browser, Profiler interface missing on window object.');\n }\n return;\n }\n\n // From initial testing, it seems that the minimum value for sampleInterval is 10ms.\n const samplingIntervalMS = 10;\n // Start the profiler\n const maxSamples = Math.floor(MAX_PROFILE_DURATION_MS / samplingIntervalMS);\n\n // Attempt to initialize the profiler constructor, if it fails, we disable profiling for the current user session.\n // This is likely due to a missing 'Document-Policy': 'js-profiling' header. We do not want to throw an error if this happens\n // as we risk breaking the user's application, so just disable profiling and log an error.\n try {\n return new JSProfilerConstructor({ sampleInterval: samplingIntervalMS, maxBufferSize: maxSamples });\n } catch {\n if (DEBUG_BUILD) {\n debug.log(\n \"[Profiling] Failed to initialize the Profiling constructor, this is likely due to a missing 'Document-Policy': 'js-profiling' header.\",\n );\n debug.log('[Profiling] Disabling profiling for current user session.');\n }\n PROFILING_CONSTRUCTOR_FAILED = true;\n }\n\n return;\n}\n\n/**\n * Determine if a profile should be created for the current session.\n */\nexport function shouldProfileSession(options: BrowserOptions): boolean {\n // If constructor failed once, it will always fail, so we can early return.\n if (PROFILING_CONSTRUCTOR_FAILED) {\n if (DEBUG_BUILD) {\n debug.log(\n '[Profiling] Profiling has been disabled for the duration of the current user session as the JS Profiler could not be started.',\n );\n }\n return false;\n }\n\n if (options.profileLifecycle !== 'trace' && options.profileLifecycle !== 'manual') {\n DEBUG_BUILD && debug.warn('[Profiling] Session not sampled. Invalid `profileLifecycle` option.');\n return false;\n }\n\n // Session sampling: profileSessionSampleRate gates whether profiling is enabled for this session\n const profileSessionSampleRate = options.profileSessionSampleRate;\n\n if (!isValidSampleRate(profileSessionSampleRate)) {\n DEBUG_BUILD && debug.warn('[Profiling] Discarding profile because of invalid profileSessionSampleRate.');\n return false;\n }\n\n if (!profileSessionSampleRate) {\n DEBUG_BUILD &&\n debug.log('[Profiling] Discarding profile because profileSessionSampleRate is not defined or set to 0');\n return false;\n }\n\n return Math.random() <= profileSessionSampleRate;\n}\n\nexport function setThreadAttributes(span: Span): void {\n span.setAttribute('thread.id', PROFILER_THREAD_ID_STRING);\n span.setAttribute('thread.name', PROFILER_THREAD_NAME);\n}\n"],"names":["GLOBAL_OBJ","uuid4","_INTERNAL_performanceTimeToSeconds","getClient","getDebugImagesForResources","DEBUG_BUILD","debug","WINDOW"],"mappings":"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"}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { setRouteProvider } from '@sentry/browser-utils';
|
|
2
|
-
import { Client, getSDKSource, applySdkMetadata, addAutoIpAddressToSession } from '@sentry/core';
|
|
2
|
+
import { Client, getSDKSource, applySdkMetadata, addAutoIpAddressToSession, timestampInSeconds } from '@sentry/core';
|
|
3
3
|
import { eventFromException, eventFromMessage } from './eventbuilder.js';
|
|
4
4
|
import { WINDOW } from './helpers.js';
|
|
5
5
|
|
|
@@ -28,18 +28,42 @@ class BrowserClient extends Client {
|
|
|
28
28
|
};
|
|
29
29
|
}
|
|
30
30
|
const { sendClientReports } = this._options;
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
31
|
+
const checkDrift = () => {
|
|
32
|
+
timestampInSeconds();
|
|
33
|
+
};
|
|
34
|
+
const listeners = [
|
|
35
|
+
// Flush buffered data when the page becomes hidden (e.g. tab switch, navigation, or the page
|
|
36
|
+
// being discarded). `flush()` emits the `flush` hook, which drains the span (streaming), log and
|
|
37
|
+
// metric buffers and hands the resulting envelopes to the transport (which uses `keepalive`).
|
|
38
|
+
// Client report outcomes don't listen to the `flush` hook, so we flush them separately.
|
|
39
|
+
[
|
|
40
|
+
WINDOW.document,
|
|
41
|
+
"visibilitychange",
|
|
42
|
+
() => {
|
|
43
|
+
checkDrift();
|
|
44
|
+
if (WINDOW.document.visibilityState === "hidden") {
|
|
45
|
+
if (sendClientReports) {
|
|
46
|
+
this._flushOutcomes();
|
|
47
|
+
}
|
|
48
|
+
queueMicrotask(() => {
|
|
49
|
+
void this.flush();
|
|
50
|
+
});
|
|
36
51
|
}
|
|
37
|
-
queueMicrotask(() => {
|
|
38
|
-
void this.flush();
|
|
39
|
-
});
|
|
40
52
|
}
|
|
41
|
-
|
|
42
|
-
|
|
53
|
+
],
|
|
54
|
+
// `freeze` and `resume` are only fired by Chromium browsers. Other browsers never fire them, so these listeners
|
|
55
|
+
// are no-ops there.
|
|
56
|
+
[WINDOW.document, "freeze", checkDrift],
|
|
57
|
+
[WINDOW.document, "resume", checkDrift],
|
|
58
|
+
// Pages restored from the back/forward cache were paused while they were cached.
|
|
59
|
+
[WINDOW, "pagehide", checkDrift],
|
|
60
|
+
[WINDOW, "pageshow", checkDrift]
|
|
61
|
+
];
|
|
62
|
+
listeners.forEach(([target, name, listener]) => target?.addEventListener?.(name, listener));
|
|
63
|
+
this.on(
|
|
64
|
+
"close",
|
|
65
|
+
() => listeners.forEach(([target, name, listener]) => target?.removeEventListener?.(name, listener))
|
|
66
|
+
);
|
|
43
67
|
if (userInfo) {
|
|
44
68
|
this.on("beforeSendSession", addAutoIpAddressToSession);
|
|
45
69
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"client.js","sources":["../../../../src/client.ts"],"sourcesContent":["import type {\n ClientOptions,\n Event,\n EventHint,\n Options as CoreOptions,\n ParameterizedString,\n Scope,\n SeverityLevel,\n} from '@sentry/core';\nimport type { BrowserClientReplayOptions } from '@sentry/core/browser';\nimport type { RouteProvider } from '@sentry/browser-utils';\nimport { setRouteProvider } from '@sentry/browser-utils';\nimport { addAutoIpAddressToSession, applySdkMetadata, Client, getSDKSource } from '@sentry/core';\nimport { eventFromException, eventFromMessage } from './eventbuilder';\nimport { WINDOW } from './helpers';\nimport type { BrowserTransportOptions } from './transports/types';\n\n/**\n * A magic string that build tooling can leverage in order to inject a release value into the SDK.\n */\ndeclare const __SENTRY_RELEASE__: string | undefined;\n\nexport type BrowserClientProfilingOptions = {\n /**\n * Sets profiling session sample rate for the entire profiling session.\n *\n * A profiling session corresponds to a user session, meaning it is set once at integration initialization and\n * persisted until the next page reload. This rate determines what percentage of user sessions will have profiling enabled.\n * @default 0\n */\n profileSessionSampleRate?: number;\n\n /**\n * Set the lifecycle mode of the profiler.\n * - **manual**: The profiler will be manually started and stopped via `startProfiler`/`stopProfiler`.\n * If a session is sampled, is dependent on the `profileSessionSampleRate`.\n * - **trace**: The profiler will be automatically started when a root span exists and stopped when there are no\n * more sampled root spans. Whether a session is sampled, is dependent on the `profileSessionSampleRate` and the\n * existing sampling configuration for tracing (`tracesSampleRate`/`tracesSampler`).\n *\n * @default 'manual'\n */\n profileLifecycle?: 'manual' | 'trace';\n};\n\ntype BrowserSpecificOptions = BrowserClientReplayOptions &\n BrowserClientProfilingOptions & {\n /** If configured, this URL will be used as base URL for lazy loading integration. */\n cdnBaseUrl?: string;\n\n /**\n * Important: Only set this option if you know what you are doing!\n *\n * By default, the SDK will check if `Sentry.init` is called in a browser extension.\n * In case it is, it will stop initialization and log a warning\n * because browser extensions require a different Sentry initialization process:\n * https://docs.sentry.io/platforms/javascript/best-practices/shared-environments/\n *\n * Setting up the SDK in a browser extension with global error monitoring is not recommended\n * and will likely flood you with errors from other web sites or extensions. This can heavily\n * impact your quota and cause interference with your and other Sentry SDKs in shared environments.\n *\n * If this check wrongfully flags your setup as a browser extension, you can set this\n * option to `true` to skip the check.\n *\n * @default false\n */\n skipBrowserExtensionCheck?: boolean;\n\n /**\n * If you use Spotlight by Sentry during development, use\n * this option to forward captured Sentry events to Spotlight.\n *\n * Either set it to true, or provide a specific Spotlight Sidecar URL.\n *\n * More details: https://spotlightjs.com/\n *\n * IMPORTANT: Only set this option to `true` while developing, not in production!\n */\n spotlight?: boolean | string;\n\n /**\n * Resolves URLs to low-cardinality route names, e.g. `/users/42` -> `/users/:id`.\n *\n * Framework SDKs set this for you. Set it yourself to name routes for a router the SDK has no\n * integration for. `setRouteProvider` replaces it at runtime.\n */\n routeProvider?: RouteProvider;\n };\n/**\n * Configuration options for the Sentry Browser SDK.\n * @see @sentry/core Options for more information.\n */\nexport type BrowserOptions = CoreOptions<BrowserTransportOptions> & BrowserSpecificOptions;\n\n/**\n * Configuration options for the Sentry Browser SDK Client class\n * @see BrowserClient for more information.\n */\nexport type BrowserClientOptions = ClientOptions<BrowserTransportOptions> & BrowserSpecificOptions;\n\n/**\n * The Sentry Browser SDK Client.\n *\n * @see BrowserOptions for documentation on configuration options.\n * @see SentryClient for usage documentation.\n */\nexport class BrowserClient extends Client<BrowserClientOptions> {\n /**\n * Creates a new Browser SDK instance.\n *\n * @param options Configuration options for this SDK.\n */\n public constructor(options: BrowserClientOptions) {\n const opts = applyDefaultOptions(options);\n const sdkSource = WINDOW.SENTRY_SDK_SOURCE || getSDKSource();\n applySdkMetadata(opts, 'browser', ['browser'], sdkSource);\n\n super(opts);\n\n if (opts.routeProvider) {\n setRouteProvider(opts.routeProvider, this);\n }\n\n // Unhandled errors don't actually crash the browser, so we report `unhandled` rather than `crashed`.\n this._unhandledSessionStatus = 'unhandled';\n\n const { userInfo } = this.getDataCollectionOptions();\n\n if (opts._metadata?.sdk) {\n opts._metadata.sdk.settings = {\n // Only allow IP inferral by Relay if the user opted in via dataCollection\n infer_ip: userInfo ? 'auto' : 'never',\n // purposefully allowing already passed settings to override the default\n ...opts._metadata.sdk.settings,\n };\n }\n\n const { sendClientReports } = this._options;\n\n // Flush buffered data when the page becomes hidden (e.g. tab switch, navigation, or the page\n // being discarded). `flush()` emits the `flush` hook, which drains the span (streaming), log and\n // metric buffers and hands the resulting envelopes to the transport (which uses `keepalive`).\n // Client report outcomes don't listen to the `flush` hook, so we flush them separately.\n if (WINDOW.document) {\n WINDOW.document.addEventListener('visibilitychange', () => {\n if (WINDOW.document.visibilityState === 'hidden') {\n if (sendClientReports) {\n this._flushOutcomes();\n }\n // Defer the flush to a microtask so that visibilitychange listeners registered after this\n // one have already run. In particular, browser tracing's background-tab detection ends the\n // active pageload/navigation (segment) span when the page is hidden. Deferring ensures that\n // segment span has been added to the streaming buffer before we flush, so it is sent\n // together with its child spans instead of being orphaned.\n queueMicrotask(() => {\n void this.flush();\n });\n }\n });\n }\n\n if (userInfo) {\n this.on('beforeSendSession', addAutoIpAddressToSession);\n }\n }\n\n /**\n * @inheritDoc\n */\n public eventFromException(exception: unknown, hint?: EventHint): PromiseLike<Event> {\n return eventFromException(this._options.stackParser, exception, hint, this._options.attachStacktrace);\n }\n\n /**\n * @inheritDoc\n */\n public eventFromMessage(\n message: ParameterizedString,\n level: SeverityLevel = 'info',\n hint?: EventHint,\n ): PromiseLike<Event> {\n return eventFromMessage(this._options.stackParser, message, level, hint, this._options.attachStacktrace);\n }\n\n /**\n * @inheritDoc\n */\n protected _prepareEvent(\n event: Event,\n hint: EventHint,\n currentScope: Scope,\n isolationScope: Scope,\n ): PromiseLike<Event | null> {\n event.platform = event.platform || 'javascript';\n\n return super._prepareEvent(event, hint, currentScope, isolationScope);\n }\n}\n\n/** Exported only for tests. */\nexport function applyDefaultOptions<T extends Partial<BrowserClientOptions>>(optionsArg: T): T {\n return {\n release:\n typeof __SENTRY_RELEASE__ === 'string' // This allows build tooling to find-and-replace __SENTRY_RELEASE__ to inject a release value\n ? __SENTRY_RELEASE__\n : WINDOW.SENTRY_RELEASE?.id, // This supports the variable that sentry-webpack-plugin injects\n sendClientReports: true,\n // We default this to true, as it is the safer scenario\n parentSpanIsAlwaysRootSpan: true,\n ...optionsArg,\n };\n}\n"],"names":[],"mappings":";;;;;AA2GO,MAAM,sBAAsB,MAAA,CAA6B;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAMvD,YAAY,OAAA,EAA+B;AAChD,IAAA,MAAM,IAAA,GAAO,oBAAoB,OAAO,CAAA;AACxC,IAAA,MAAM,SAAA,GAAY,MAAA,CAAO,iBAAA,IAAqB,YAAA,EAAa;AAC3D,IAAA,gBAAA,CAAiB,IAAA,EAAM,SAAA,EAAW,CAAC,SAAS,GAAG,SAAS,CAAA;AAExD,IAAA,KAAA,CAAM,IAAI,CAAA;AAEV,IAAA,IAAI,KAAK,aAAA,EAAe;AACtB,MAAA,gBAAA,CAAiB,IAAA,CAAK,eAAe,IAAI,CAAA;AAAA,IAC3C;AAGA,IAAA,IAAA,CAAK,uBAAA,GAA0B,WAAA;AAE/B,IAAA,MAAM,EAAE,QAAA,EAAS,GAAI,IAAA,CAAK,wBAAA,EAAyB;AAEnD,IAAA,IAAI,IAAA,CAAK,WAAW,GAAA,EAAK;AACvB,MAAA,IAAA,CAAK,SAAA,CAAU,IAAI,QAAA,GAAW;AAAA;AAAA,QAE5B,QAAA,EAAU,WAAW,MAAA,GAAS,OAAA;AAAA;AAAA,QAE9B,GAAG,IAAA,CAAK,SAAA,CAAU,GAAA,CAAI;AAAA,OACxB;AAAA,IACF;AAEA,IAAA,MAAM,EAAE,iBAAA,EAAkB,GAAI,IAAA,CAAK,QAAA;AAMnC,IAAA,IAAI,OAAO,QAAA,EAAU;AACnB,MAAA,MAAA,CAAO,QAAA,CAAS,gBAAA,CAAiB,kBAAA,EAAoB,MAAM;AACzD,QAAA,IAAI,MAAA,CAAO,QAAA,CAAS,eAAA,KAAoB,QAAA,EAAU;AAChD,UAAA,IAAI,iBAAA,EAAmB;AACrB,YAAA,IAAA,CAAK,cAAA,EAAe;AAAA,UACtB;AAMA,UAAA,cAAA,CAAe,MAAM;AACnB,YAAA,KAAK,KAAK,KAAA,EAAM;AAAA,UAClB,CAAC,CAAA;AAAA,QACH;AAAA,MACF,CAAC,CAAA;AAAA,IACH;AAEA,IAAA,IAAI,QAAA,EAAU;AACZ,MAAA,IAAA,CAAK,EAAA,CAAG,qBAAqB,yBAAyB,CAAA;AAAA,IACxD;AAAA,EACF;AAAA;AAAA;AAAA;AAAA,EAKO,kBAAA,CAAmB,WAAoB,IAAA,EAAsC;AAClF,IAAA,OAAO,kBAAA,CAAmB,KAAK,QAAA,CAAS,WAAA,EAAa,WAAW,IAAA,EAAM,IAAA,CAAK,SAAS,gBAAgB,CAAA;AAAA,EACtG;AAAA;AAAA;AAAA;AAAA,EAKO,gBAAA,CACL,OAAA,EACA,KAAA,GAAuB,MAAA,EACvB,IAAA,EACoB;AACpB,IAAA,OAAO,gBAAA,CAAiB,KAAK,QAAA,CAAS,WAAA,EAAa,SAAS,KAAA,EAAO,IAAA,EAAM,IAAA,CAAK,QAAA,CAAS,gBAAgB,CAAA;AAAA,EACzG;AAAA;AAAA;AAAA;AAAA,EAKU,aAAA,CACR,KAAA,EACA,IAAA,EACA,YAAA,EACA,cAAA,EAC2B;AAC3B,IAAA,KAAA,CAAM,QAAA,GAAW,MAAM,QAAA,IAAY,YAAA;AAEnC,IAAA,OAAO,KAAA,CAAM,aAAA,CAAc,KAAA,EAAO,IAAA,EAAM,cAAc,cAAc,CAAA;AAAA,EACtE;AACF;AAGO,SAAS,oBAA6D,UAAA,EAAkB;AAC7F,EAAA,OAAO;AAAA,IACL,SACE,OAAO,kBAAA,KAAuB,QAAA,GAC1B,kBAAA,GACA,OAAO,cAAA,EAAgB,EAAA;AAAA;AAAA,IAC7B,iBAAA,EAAmB,IAAA;AAAA;AAAA,IAEnB,0BAAA,EAA4B,IAAA;AAAA,IAC5B,GAAG;AAAA,GACL;AACF;;;;"}
|
|
1
|
+
{"version":3,"file":"client.js","sources":["../../../../src/client.ts"],"sourcesContent":["import type {\n ClientOptions,\n Event,\n EventHint,\n Options as CoreOptions,\n ParameterizedString,\n Scope,\n SeverityLevel,\n} from '@sentry/core';\nimport type { BrowserClientReplayOptions } from '@sentry/core/browser';\nimport type { RouteProvider } from '@sentry/browser-utils';\nimport { setRouteProvider } from '@sentry/browser-utils';\nimport { addAutoIpAddressToSession, applySdkMetadata, Client, getSDKSource, timestampInSeconds } from '@sentry/core';\nimport { eventFromException, eventFromMessage } from './eventbuilder';\nimport { WINDOW } from './helpers';\nimport type { BrowserTransportOptions } from './transports/types';\n\n/**\n * A magic string that build tooling can leverage in order to inject a release value into the SDK.\n */\ndeclare const __SENTRY_RELEASE__: string | undefined;\n\nexport type BrowserClientProfilingOptions = {\n /**\n * Sets profiling session sample rate for the entire profiling session.\n *\n * A profiling session corresponds to a user session, meaning it is set once at integration initialization and\n * persisted until the next page reload. This rate determines what percentage of user sessions will have profiling enabled.\n * @default 0\n */\n profileSessionSampleRate?: number;\n\n /**\n * Set the lifecycle mode of the profiler.\n * - **manual**: The profiler will be manually started and stopped via `startProfiler`/`stopProfiler`.\n * If a session is sampled, is dependent on the `profileSessionSampleRate`.\n * - **trace**: The profiler will be automatically started when a root span exists and stopped when there are no\n * more sampled root spans. Whether a session is sampled, is dependent on the `profileSessionSampleRate` and the\n * existing sampling configuration for tracing (`tracesSampleRate`/`tracesSampler`).\n *\n * @default 'manual'\n */\n profileLifecycle?: 'manual' | 'trace';\n};\n\ntype BrowserSpecificOptions = BrowserClientReplayOptions &\n BrowserClientProfilingOptions & {\n /** If configured, this URL will be used as base URL for lazy loading integration. */\n cdnBaseUrl?: string;\n\n /**\n * Important: Only set this option if you know what you are doing!\n *\n * By default, the SDK will check if `Sentry.init` is called in a browser extension.\n * In case it is, it will stop initialization and log a warning\n * because browser extensions require a different Sentry initialization process:\n * https://docs.sentry.io/platforms/javascript/best-practices/shared-environments/\n *\n * Setting up the SDK in a browser extension with global error monitoring is not recommended\n * and will likely flood you with errors from other web sites or extensions. This can heavily\n * impact your quota and cause interference with your and other Sentry SDKs in shared environments.\n *\n * If this check wrongfully flags your setup as a browser extension, you can set this\n * option to `true` to skip the check.\n *\n * @default false\n */\n skipBrowserExtensionCheck?: boolean;\n\n /**\n * If you use Spotlight by Sentry during development, use\n * this option to forward captured Sentry events to Spotlight.\n *\n * Either set it to true, or provide a specific Spotlight Sidecar URL.\n *\n * More details: https://spotlightjs.com/\n *\n * IMPORTANT: Only set this option to `true` while developing, not in production!\n */\n spotlight?: boolean | string;\n\n /**\n * Resolves URLs to low-cardinality route names, e.g. `/users/42` -> `/users/:id`.\n *\n * Framework SDKs set this for you. Set it yourself to name routes for a router the SDK has no\n * integration for. `setRouteProvider` replaces it at runtime.\n */\n routeProvider?: RouteProvider;\n };\n/**\n * Configuration options for the Sentry Browser SDK.\n * @see @sentry/core Options for more information.\n */\nexport type BrowserOptions = CoreOptions<BrowserTransportOptions> & BrowserSpecificOptions;\n\n/**\n * Configuration options for the Sentry Browser SDK Client class\n * @see BrowserClient for more information.\n */\nexport type BrowserClientOptions = ClientOptions<BrowserTransportOptions> & BrowserSpecificOptions;\n\n/**\n * The Sentry Browser SDK Client.\n *\n * @see BrowserOptions for documentation on configuration options.\n * @see SentryClient for usage documentation.\n */\nexport class BrowserClient extends Client<BrowserClientOptions> {\n /**\n * Creates a new Browser SDK instance.\n *\n * @param options Configuration options for this SDK.\n */\n public constructor(options: BrowserClientOptions) {\n const opts = applyDefaultOptions(options);\n const sdkSource = WINDOW.SENTRY_SDK_SOURCE || getSDKSource();\n applySdkMetadata(opts, 'browser', ['browser'], sdkSource);\n\n super(opts);\n\n if (opts.routeProvider) {\n setRouteProvider(opts.routeProvider, this);\n }\n\n // Unhandled errors don't actually crash the browser, so we report `unhandled` rather than `crashed`.\n this._unhandledSessionStatus = 'unhandled';\n\n const { userInfo } = this.getDataCollectionOptions();\n\n if (opts._metadata?.sdk) {\n opts._metadata.sdk.settings = {\n // Only allow IP inferral by Relay if the user opted in via dataCollection\n infer_ip: userInfo ? 'auto' : 'never',\n // purposefully allowing already passed settings to override the default\n ...opts._metadata.sdk.settings,\n };\n }\n\n const { sendClientReports } = this._options;\n\n // Devices usually hide the page before they sleep and show it again after they wake up. Checking for drift\n // at both points makes the time origin correction happen at the sleep, rather than at the next regular timestamp call.\n const checkDrift = (): void => {\n timestampInSeconds();\n };\n\n // Most bundle-size-efficient way to add and remove listeners: Create a table to avoid repeated\n // addeventlistener and removeeventlistener calls. The alternative, using an AbortController, can\n // lead to app breakage on old Angular<>Zone.js < 0.14.3 versions.\n const listeners: [EventTarget | undefined, string, () => void][] = [\n // Flush buffered data when the page becomes hidden (e.g. tab switch, navigation, or the page\n // being discarded). `flush()` emits the `flush` hook, which drains the span (streaming), log and\n // metric buffers and hands the resulting envelopes to the transport (which uses `keepalive`).\n // Client report outcomes don't listen to the `flush` hook, so we flush them separately.\n [\n WINDOW.document,\n 'visibilitychange',\n () => {\n // Devices usually hide the page before they sleep and show it again after they wake up. Checking for drift\n // at both points makes the time origin correction happen at the sleep, rather than at the next regular timestamp call.\n checkDrift();\n\n if (WINDOW.document.visibilityState === 'hidden') {\n if (sendClientReports) {\n this._flushOutcomes();\n }\n // Defer the flush to a microtask so that visibilitychange listeners registered after this\n // one have already run. In particular, browser tracing's background-tab detection ends the\n // active pageload/navigation (segment) span when the page is hidden. Deferring ensures that\n // segment span has been added to the streaming buffer before we flush, so it is sent\n // together with its child spans instead of being orphaned.\n queueMicrotask(() => {\n void this.flush();\n });\n }\n },\n ],\n // `freeze` and `resume` are only fired by Chromium browsers. Other browsers never fire them, so these listeners\n // are no-ops there.\n [WINDOW.document, 'freeze', checkDrift],\n [WINDOW.document, 'resume', checkDrift],\n // Pages restored from the back/forward cache were paused while they were cached.\n [WINDOW, 'pagehide', checkDrift],\n [WINDOW, 'pageshow', checkDrift],\n ];\n\n listeners.forEach(([target, name, listener]) => target?.addEventListener?.(name, listener));\n this.on('close', () =>\n listeners.forEach(([target, name, listener]) => target?.removeEventListener?.(name, listener)),\n );\n\n if (userInfo) {\n this.on('beforeSendSession', addAutoIpAddressToSession);\n }\n }\n\n /**\n * @inheritDoc\n */\n public eventFromException(exception: unknown, hint?: EventHint): PromiseLike<Event> {\n return eventFromException(this._options.stackParser, exception, hint, this._options.attachStacktrace);\n }\n\n /**\n * @inheritDoc\n */\n public eventFromMessage(\n message: ParameterizedString,\n level: SeverityLevel = 'info',\n hint?: EventHint,\n ): PromiseLike<Event> {\n return eventFromMessage(this._options.stackParser, message, level, hint, this._options.attachStacktrace);\n }\n\n /**\n * @inheritDoc\n */\n protected _prepareEvent(\n event: Event,\n hint: EventHint,\n currentScope: Scope,\n isolationScope: Scope,\n ): PromiseLike<Event | null> {\n event.platform = event.platform || 'javascript';\n\n return super._prepareEvent(event, hint, currentScope, isolationScope);\n }\n}\n\n/** Exported only for tests. */\nexport function applyDefaultOptions<T extends Partial<BrowserClientOptions>>(optionsArg: T): T {\n return {\n release:\n typeof __SENTRY_RELEASE__ === 'string' // This allows build tooling to find-and-replace __SENTRY_RELEASE__ to inject a release value\n ? __SENTRY_RELEASE__\n : WINDOW.SENTRY_RELEASE?.id, // This supports the variable that sentry-webpack-plugin injects\n sendClientReports: true,\n // We default this to true, as it is the safer scenario\n parentSpanIsAlwaysRootSpan: true,\n ...optionsArg,\n };\n}\n"],"names":[],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"type":"module","version":"11.
|
|
1
|
+
{"type":"module","version":"11.6.0","sideEffects":false}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { GLOBAL_OBJ, debug, uuid4,
|
|
1
|
+
import { GLOBAL_OBJ, debug, uuid4, _INTERNAL_performanceTimeToSeconds, getClient, getDebugImagesForResources } from '@sentry/core';
|
|
2
2
|
import { DEBUG_BUILD } from '../debug-build.js';
|
|
3
3
|
import { WINDOW } from '../helpers.js';
|
|
4
4
|
|
|
@@ -93,16 +93,13 @@ function convertToContinuousProfile(input) {
|
|
|
93
93
|
}
|
|
94
94
|
stacks[i] = list;
|
|
95
95
|
}
|
|
96
|
-
const perfOrigin = browserPerformanceTimeOrigin();
|
|
97
|
-
const origin = typeof performance.timeOrigin === "number" ? performance.timeOrigin : perfOrigin || 0;
|
|
98
|
-
const adjustForOriginChange = origin - (perfOrigin || origin);
|
|
99
96
|
const samples = [];
|
|
100
97
|
for (let i = 0; i < input.samples.length; i++) {
|
|
101
98
|
const sample = input.samples[i];
|
|
102
99
|
if (!sample) {
|
|
103
100
|
continue;
|
|
104
101
|
}
|
|
105
|
-
const timestampSeconds = (
|
|
102
|
+
const timestampSeconds = _INTERNAL_performanceTimeToSeconds(sample.timestamp);
|
|
106
103
|
samples[i] = {
|
|
107
104
|
stack_id: sample.stackId ?? 0,
|
|
108
105
|
thread_id: PROFILER_THREAD_ID_STRING,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"utils.js","sources":["../../../../../src/profiling/utils.ts"],"sourcesContent":["/* eslint-disable max-lines */\nimport type { Client, ContinuousThreadCpuProfile, DebugImage, ProfileChunk, Span } from '@sentry/core';\nimport {\n browserPerformanceTimeOrigin,\n debug,\n getClient,\n getDebugImagesForResources,\n GLOBAL_OBJ,\n uuid4,\n} from '@sentry/core';\nimport type { BrowserOptions } from '../client';\nimport { DEBUG_BUILD } from '../debug-build';\nimport { WINDOW } from '../helpers';\nimport type { JSSelfProfile, JSSelfProfiler, JSSelfProfilerConstructor } from './jsSelfProfiling';\n\n// Checking if we are in Main or Worker thread: `self` (not `window`) is the `globalThis` in Web Workers and `importScripts` are only available in Web Workers\nconst isMainThread = 'window' in GLOBAL_OBJ && GLOBAL_OBJ.window === GLOBAL_OBJ && typeof importScripts === 'undefined';\n\n// Setting ID to 0 as we cannot get an ID from Web Workers\nexport const PROFILER_THREAD_ID_STRING = String(0);\nexport const PROFILER_THREAD_NAME = isMainThread ? 'main' : 'worker';\n\n/**\n * Create a profile chunk envelope item\n */\nexport function createProfileChunkPayload(\n jsSelfProfile: JSSelfProfile,\n client: Client,\n profilerId?: string,\n): ProfileChunk {\n // only == to catch null and undefined\n if (jsSelfProfile == null) {\n throw new TypeError(\n `Cannot construct profiling event envelope without a valid profile. Got ${jsSelfProfile} instead.`,\n );\n }\n\n const continuousProfile = convertToContinuousProfile(jsSelfProfile);\n\n const options = client.getOptions();\n const sdk = client.getSdkMetadata?.()?.sdk;\n\n return {\n chunk_id: uuid4(),\n client_sdk: {\n name: sdk?.name ?? 'sentry.javascript.browser',\n version: sdk?.version ?? '0.0.0',\n },\n profiler_id: profilerId || uuid4(),\n platform: 'javascript',\n version: '2',\n release: options.release ?? '',\n environment: options.environment ?? 'production',\n debug_meta: {\n // function name obfuscation\n images: applyDebugMetadata(jsSelfProfile.resources),\n },\n profile: continuousProfile,\n };\n}\n\n/**\n * Validate a profile chunk against the Sample Format V2 requirements.\n * https://develop.sentry.dev/sdk/telemetry/profiles/sample-format-v2/\n * - Presence of samples, stacks, frames\n * - Required metadata fields\n */\nexport function validateProfileChunk(chunk: ProfileChunk): { valid: true } | { reason: string } {\n try {\n // Required metadata\n if (!chunk || typeof chunk !== 'object') {\n return { reason: 'chunk is not an object' };\n }\n\n // profiler_id and chunk_id must be 32 lowercase hex chars\n const isHex32 = (val: unknown): boolean => typeof val === 'string' && /^[a-f0-9]{32}$/.test(val);\n if (!isHex32(chunk.profiler_id)) {\n return { reason: 'missing or invalid profiler_id' };\n }\n if (!isHex32(chunk.chunk_id)) {\n return { reason: 'missing or invalid chunk_id' };\n }\n\n if (!chunk.client_sdk) {\n return { reason: 'missing client_sdk metadata' };\n }\n\n // Profile data must have frames, stacks, samples\n const profile = chunk.profile as { frames?: unknown[]; stacks?: unknown[]; samples?: unknown[] } | undefined;\n if (!profile) {\n return { reason: 'missing profile data' };\n }\n\n if (!Array.isArray(profile.frames) || !profile.frames.length) {\n return { reason: 'profile has no frames' };\n }\n if (!Array.isArray(profile.stacks) || !profile.stacks.length) {\n return { reason: 'profile has no stacks' };\n }\n if (!Array.isArray(profile.samples) || !profile.samples.length) {\n return { reason: 'profile has no samples' };\n }\n\n return { valid: true };\n } catch (e) {\n return { reason: `unknown validation error: ${e}` };\n }\n}\n\n/**\n * Convert from JSSelfProfile format to ContinuousThreadCpuProfile format.\n */\nfunction convertToContinuousProfile(input: {\n frames: { name: string; resourceId?: number; line?: number; column?: number }[];\n stacks: { frameId: number; parentId?: number }[];\n samples: { timestamp: number; stackId?: number }[];\n resources: string[];\n}): ContinuousThreadCpuProfile {\n // Frames map 1:1 by index; fill only when present to avoid sparse writes\n const frames: ContinuousThreadCpuProfile['frames'] = [];\n for (let i = 0; i < input.frames.length; i++) {\n const frame = input.frames[i];\n if (!frame) {\n continue;\n }\n frames[i] = {\n function: frame.name,\n abs_path: typeof frame.resourceId === 'number' ? input.resources[frame.resourceId] : undefined,\n lineno: frame.line,\n colno: frame.column,\n };\n }\n\n // Build stacks by following parent links, top->down order (root last)\n const stacks: ContinuousThreadCpuProfile['stacks'] = [];\n for (let i = 0; i < input.stacks.length; i++) {\n const stackHead = input.stacks[i];\n if (!stackHead) {\n continue;\n }\n const list: number[] = [];\n let current: { frameId: number; parentId?: number } | undefined = stackHead;\n while (current) {\n list.push(current.frameId);\n current = current.parentId === undefined ? undefined : input.stacks[current.parentId];\n }\n stacks[i] = list;\n }\n\n // Align timestamps to SDK time origin to match span/event timelines\n const perfOrigin = browserPerformanceTimeOrigin();\n const origin = typeof performance.timeOrigin === 'number' ? performance.timeOrigin : perfOrigin || 0;\n const adjustForOriginChange = origin - (perfOrigin || origin);\n\n const samples: ContinuousThreadCpuProfile['samples'] = [];\n for (let i = 0; i < input.samples.length; i++) {\n const sample = input.samples[i];\n if (!sample) {\n continue;\n }\n // Convert ms to seconds epoch-based timestamp\n const timestampSeconds = (origin + (sample.timestamp - adjustForOriginChange)) / 1000;\n samples[i] = {\n stack_id: sample.stackId ?? 0,\n thread_id: PROFILER_THREAD_ID_STRING,\n timestamp: timestampSeconds,\n };\n }\n\n return {\n frames,\n stacks,\n samples,\n thread_metadata: { [PROFILER_THREAD_ID_STRING]: { name: PROFILER_THREAD_NAME } },\n };\n}\n\n/**\n * Applies debug meta data to an event from a list of paths to resources (sourcemaps)\n */\nexport function applyDebugMetadata(resource_paths: ReadonlyArray<string>): DebugImage[] {\n const client = getClient();\n const options = client?.getOptions();\n const stackParser = options?.stackParser;\n\n if (!stackParser) {\n return [];\n }\n\n return getDebugImagesForResources(stackParser, resource_paths);\n}\n\n/**\n * Checks the given sample rate to make sure it is valid type and value (a boolean, or a number between 0 and 1).\n */\nexport function isValidSampleRate(rate: unknown): boolean {\n // we need to check NaN explicitly because it's of type 'number' and therefore wouldn't get caught by this typecheck\n if ((typeof rate !== 'number' && typeof rate !== 'boolean') || (typeof rate === 'number' && isNaN(rate))) {\n DEBUG_BUILD &&\n debug.warn(\n `[Profiling] Invalid sample rate. Sample rate must be a boolean or a number between 0 and 1. Got ${JSON.stringify(\n rate,\n )} of type ${JSON.stringify(typeof rate)}.`,\n );\n return false;\n }\n\n // Boolean sample rates are always valid\n if (rate === true || rate === false) {\n return true;\n }\n\n // in case sampleRate is a boolean, it will get automatically cast to 1 if it's true and 0 if it's false\n if (rate < 0 || rate > 1) {\n DEBUG_BUILD && debug.warn(`[Profiling] Invalid sample rate. Sample rate must be between 0 and 1. Got ${rate}.`);\n return false;\n }\n return true;\n}\n\n// Keep a flag value to avoid re-initializing the profiler constructor. If it fails\n// once, it will always fail and this allows us to early return.\nlet PROFILING_CONSTRUCTOR_FAILED: boolean = false;\nexport const MAX_PROFILE_DURATION_MS = 30_000;\n\n/**\n * Check if profiler constructor is available.\n * @param maybeProfiler\n */\nfunction isJSProfilerSupported(maybeProfiler: unknown): maybeProfiler is typeof JSSelfProfilerConstructor {\n return typeof maybeProfiler === 'function';\n}\n\n/**\n * Starts the profiler and returns the profiler instance.\n */\nexport function startJSSelfProfile(): JSSelfProfiler | undefined {\n // Feature support check first\n const JSProfilerConstructor = WINDOW.Profiler;\n\n if (!isJSProfilerSupported(JSProfilerConstructor)) {\n if (DEBUG_BUILD) {\n debug.log('[Profiling] Profiling is not supported by this browser, Profiler interface missing on window object.');\n }\n return;\n }\n\n // From initial testing, it seems that the minimum value for sampleInterval is 10ms.\n const samplingIntervalMS = 10;\n // Start the profiler\n const maxSamples = Math.floor(MAX_PROFILE_DURATION_MS / samplingIntervalMS);\n\n // Attempt to initialize the profiler constructor, if it fails, we disable profiling for the current user session.\n // This is likely due to a missing 'Document-Policy': 'js-profiling' header. We do not want to throw an error if this happens\n // as we risk breaking the user's application, so just disable profiling and log an error.\n try {\n return new JSProfilerConstructor({ sampleInterval: samplingIntervalMS, maxBufferSize: maxSamples });\n } catch {\n if (DEBUG_BUILD) {\n debug.log(\n \"[Profiling] Failed to initialize the Profiling constructor, this is likely due to a missing 'Document-Policy': 'js-profiling' header.\",\n );\n debug.log('[Profiling] Disabling profiling for current user session.');\n }\n PROFILING_CONSTRUCTOR_FAILED = true;\n }\n\n return;\n}\n\n/**\n * Determine if a profile should be created for the current session.\n */\nexport function shouldProfileSession(options: BrowserOptions): boolean {\n // If constructor failed once, it will always fail, so we can early return.\n if (PROFILING_CONSTRUCTOR_FAILED) {\n if (DEBUG_BUILD) {\n debug.log(\n '[Profiling] Profiling has been disabled for the duration of the current user session as the JS Profiler could not be started.',\n );\n }\n return false;\n }\n\n if (options.profileLifecycle !== 'trace' && options.profileLifecycle !== 'manual') {\n DEBUG_BUILD && debug.warn('[Profiling] Session not sampled. Invalid `profileLifecycle` option.');\n return false;\n }\n\n // Session sampling: profileSessionSampleRate gates whether profiling is enabled for this session\n const profileSessionSampleRate = options.profileSessionSampleRate;\n\n if (!isValidSampleRate(profileSessionSampleRate)) {\n DEBUG_BUILD && debug.warn('[Profiling] Discarding profile because of invalid profileSessionSampleRate.');\n return false;\n }\n\n if (!profileSessionSampleRate) {\n DEBUG_BUILD &&\n debug.log('[Profiling] Discarding profile because profileSessionSampleRate is not defined or set to 0');\n return false;\n }\n\n return Math.random() <= profileSessionSampleRate;\n}\n\nexport function setThreadAttributes(span: Span): void {\n span.setAttribute('thread.id', PROFILER_THREAD_ID_STRING);\n span.setAttribute('thread.name', PROFILER_THREAD_NAME);\n}\n"],"names":[],"mappings":";;;;AAgBA,MAAM,eAAe,QAAA,IAAY,UAAA,IAAc,WAAW,MAAA,KAAW,UAAA,IAAc,OAAO,aAAA,KAAkB,WAAA;AAGrG,MAAM,yBAAA,GAA4B,OAAO,CAAC;AAC1C,MAAM,oBAAA,GAAuB,eAAe,MAAA,GAAS;AAKrD,SAAS,yBAAA,CACd,aAAA,EACA,MAAA,EACA,UAAA,EACc;AAEd,EAAA,IAAI,iBAAiB,IAAA,EAAM;AACzB,IAAA,MAAM,IAAI,SAAA;AAAA,MACR,0EAA0E,aAAa,CAAA,SAAA;AAAA,KACzF;AAAA,EACF;AAEA,EAAA,MAAM,iBAAA,GAAoB,2BAA2B,aAAa,CAAA;AAElE,EAAA,MAAM,OAAA,GAAU,OAAO,UAAA,EAAW;AAClC,EAAA,MAAM,GAAA,GAAM,MAAA,CAAO,cAAA,IAAiB,EAAG,GAAA;AAEvC,EAAA,OAAO;AAAA,IACL,UAAU,KAAA,EAAM;AAAA,IAChB,UAAA,EAAY;AAAA,MACV,IAAA,EAAM,KAAK,IAAA,IAAQ,2BAAA;AAAA,MACnB,OAAA,EAAS,KAAK,OAAA,IAAW;AAAA,KAC3B;AAAA,IACA,WAAA,EAAa,cAAc,KAAA,EAAM;AAAA,IACjC,QAAA,EAAU,YAAA;AAAA,IACV,OAAA,EAAS,GAAA;AAAA,IACT,OAAA,EAAS,QAAQ,OAAA,IAAW,EAAA;AAAA,IAC5B,WAAA,EAAa,QAAQ,WAAA,IAAe,YAAA;AAAA,IACpC,UAAA,EAAY;AAAA;AAAA,MAEV,MAAA,EAAQ,kBAAA,CAAmB,aAAA,CAAc,SAAS;AAAA,KACpD;AAAA,IACA,OAAA,EAAS;AAAA,GACX;AACF;AAQO,SAAS,qBAAqB,KAAA,EAA2D;AAC9F,EAAA,IAAI;AAEF,IAAA,IAAI,CAAC,KAAA,IAAS,OAAO,KAAA,KAAU,QAAA,EAAU;AACvC,MAAA,OAAO,EAAE,QAAQ,wBAAA,EAAyB;AAAA,IAC5C;AAGA,IAAA,MAAM,OAAA,GAAU,CAAC,GAAA,KAA0B,OAAO,QAAQ,QAAA,IAAY,gBAAA,CAAiB,KAAK,GAAG,CAAA;AAC/F,IAAA,IAAI,CAAC,OAAA,CAAQ,KAAA,CAAM,WAAW,CAAA,EAAG;AAC/B,MAAA,OAAO,EAAE,QAAQ,gCAAA,EAAiC;AAAA,IACpD;AACA,IAAA,IAAI,CAAC,OAAA,CAAQ,KAAA,CAAM,QAAQ,CAAA,EAAG;AAC5B,MAAA,OAAO,EAAE,QAAQ,6BAAA,EAA8B;AAAA,IACjD;AAEA,IAAA,IAAI,CAAC,MAAM,UAAA,EAAY;AACrB,MAAA,OAAO,EAAE,QAAQ,6BAAA,EAA8B;AAAA,IACjD;AAGA,IAAA,MAAM,UAAU,KAAA,CAAM,OAAA;AACtB,IAAA,IAAI,CAAC,OAAA,EAAS;AACZ,MAAA,OAAO,EAAE,QAAQ,sBAAA,EAAuB;AAAA,IAC1C;AAEA,IAAA,IAAI,CAAC,MAAM,OAAA,CAAQ,OAAA,CAAQ,MAAM,CAAA,IAAK,CAAC,OAAA,CAAQ,MAAA,CAAO,MAAA,EAAQ;AAC5D,MAAA,OAAO,EAAE,QAAQ,uBAAA,EAAwB;AAAA,IAC3C;AACA,IAAA,IAAI,CAAC,MAAM,OAAA,CAAQ,OAAA,CAAQ,MAAM,CAAA,IAAK,CAAC,OAAA,CAAQ,MAAA,CAAO,MAAA,EAAQ;AAC5D,MAAA,OAAO,EAAE,QAAQ,uBAAA,EAAwB;AAAA,IAC3C;AACA,IAAA,IAAI,CAAC,MAAM,OAAA,CAAQ,OAAA,CAAQ,OAAO,CAAA,IAAK,CAAC,OAAA,CAAQ,OAAA,CAAQ,MAAA,EAAQ;AAC9D,MAAA,OAAO,EAAE,QAAQ,wBAAA,EAAyB;AAAA,IAC5C;AAEA,IAAA,OAAO,EAAE,OAAO,IAAA,EAAK;AAAA,EACvB,SAAS,CAAA,EAAG;AACV,IAAA,OAAO,EAAE,MAAA,EAAQ,CAAA,0BAAA,EAA6B,CAAC,CAAA,CAAA,EAAG;AAAA,EACpD;AACF;AAKA,SAAS,2BAA2B,KAAA,EAKL;AAE7B,EAAA,MAAM,SAA+C,EAAC;AACtD,EAAA,KAAA,IAAS,IAAI,CAAA,EAAG,CAAA,GAAI,KAAA,CAAM,MAAA,CAAO,QAAQ,CAAA,EAAA,EAAK;AAC5C,IAAA,MAAM,KAAA,GAAQ,KAAA,CAAM,MAAA,CAAO,CAAC,CAAA;AAC5B,IAAA,IAAI,CAAC,KAAA,EAAO;AACV,MAAA;AAAA,IACF;AACA,IAAA,MAAA,CAAO,CAAC,CAAA,GAAI;AAAA,MACV,UAAU,KAAA,CAAM,IAAA;AAAA,MAChB,QAAA,EAAU,OAAO,KAAA,CAAM,UAAA,KAAe,WAAW,KAAA,CAAM,SAAA,CAAU,KAAA,CAAM,UAAU,CAAA,GAAI,MAAA;AAAA,MACrF,QAAQ,KAAA,CAAM,IAAA;AAAA,MACd,OAAO,KAAA,CAAM;AAAA,KACf;AAAA,EACF;AAGA,EAAA,MAAM,SAA+C,EAAC;AACtD,EAAA,KAAA,IAAS,IAAI,CAAA,EAAG,CAAA,GAAI,KAAA,CAAM,MAAA,CAAO,QAAQ,CAAA,EAAA,EAAK;AAC5C,IAAA,MAAM,SAAA,GAAY,KAAA,CAAM,MAAA,CAAO,CAAC,CAAA;AAChC,IAAA,IAAI,CAAC,SAAA,EAAW;AACd,MAAA;AAAA,IACF;AACA,IAAA,MAAM,OAAiB,EAAC;AACxB,IAAA,IAAI,OAAA,GAA8D,SAAA;AAClE,IAAA,OAAO,OAAA,EAAS;AACd,MAAA,IAAA,CAAK,IAAA,CAAK,QAAQ,OAAO,CAAA;AACzB,MAAA,OAAA,GAAU,QAAQ,QAAA,KAAa,MAAA,GAAY,SAAY,KAAA,CAAM,MAAA,CAAO,QAAQ,QAAQ,CAAA;AAAA,IACtF;AACA,IAAA,MAAA,CAAO,CAAC,CAAA,GAAI,IAAA;AAAA,EACd;AAGA,EAAA,MAAM,aAAa,4BAAA,EAA6B;AAChD,EAAA,MAAM,SAAS,OAAO,WAAA,CAAY,eAAe,QAAA,GAAW,WAAA,CAAY,aAAa,UAAA,IAAc,CAAA;AACnG,EAAA,MAAM,qBAAA,GAAwB,UAAU,UAAA,IAAc,MAAA,CAAA;AAEtD,EAAA,MAAM,UAAiD,EAAC;AACxD,EAAA,KAAA,IAAS,IAAI,CAAA,EAAG,CAAA,GAAI,KAAA,CAAM,OAAA,CAAQ,QAAQ,CAAA,EAAA,EAAK;AAC7C,IAAA,MAAM,MAAA,GAAS,KAAA,CAAM,OAAA,CAAQ,CAAC,CAAA;AAC9B,IAAA,IAAI,CAAC,MAAA,EAAQ;AACX,MAAA;AAAA,IACF;AAEA,IAAA,MAAM,gBAAA,GAAA,CAAoB,MAAA,IAAU,MAAA,CAAO,SAAA,GAAY,qBAAA,CAAA,IAA0B,GAAA;AACjF,IAAA,OAAA,CAAQ,CAAC,CAAA,GAAI;AAAA,MACX,QAAA,EAAU,OAAO,OAAA,IAAW,CAAA;AAAA,MAC5B,SAAA,EAAW,yBAAA;AAAA,MACX,SAAA,EAAW;AAAA,KACb;AAAA,EACF;AAEA,EAAA,OAAO;AAAA,IACL,MAAA;AAAA,IACA,MAAA;AAAA,IACA,OAAA;AAAA,IACA,eAAA,EAAiB,EAAE,CAAC,yBAAyB,GAAG,EAAE,IAAA,EAAM,sBAAqB;AAAE,GACjF;AACF;AAKO,SAAS,mBAAmB,cAAA,EAAqD;AACtF,EAAA,MAAM,SAAS,SAAA,EAAU;AACzB,EAAA,MAAM,OAAA,GAAU,QAAQ,UAAA,EAAW;AACnC,EAAA,MAAM,cAAc,OAAA,EAAS,WAAA;AAE7B,EAAA,IAAI,CAAC,WAAA,EAAa;AAChB,IAAA,OAAO,EAAC;AAAA,EACV;AAEA,EAAA,OAAO,0BAAA,CAA2B,aAAa,cAAc,CAAA;AAC/D;AAKO,SAAS,kBAAkB,IAAA,EAAwB;AAExD,EAAA,IAAK,OAAO,IAAA,KAAS,QAAA,IAAY,OAAO,IAAA,KAAS,SAAA,IAAe,OAAO,IAAA,KAAS,QAAA,IAAY,KAAA,CAAM,IAAI,CAAA,EAAI;AACxG,IAAA,WAAA,IACE,KAAA,CAAM,IAAA;AAAA,MACJ,mGAAmG,IAAA,CAAK,SAAA;AAAA,QACtG;AAAA,OACD,CAAA,SAAA,EAAY,IAAA,CAAK,SAAA,CAAU,OAAO,IAAI,CAAC,CAAA,CAAA;AAAA,KAC1C;AACF,IAAA,OAAO,KAAA;AAAA,EACT;AAGA,EAAA,IAAI,IAAA,KAAS,IAAA,IAAQ,IAAA,KAAS,KAAA,EAAO;AACnC,IAAA,OAAO,IAAA;AAAA,EACT;AAGA,EAAA,IAAI,IAAA,GAAO,CAAA,IAAK,IAAA,GAAO,CAAA,EAAG;AACxB,IAAA,WAAA,IAAe,KAAA,CAAM,IAAA,CAAK,CAAA,0EAAA,EAA6E,IAAI,CAAA,CAAA,CAAG,CAAA;AAC9G,IAAA,OAAO,KAAA;AAAA,EACT;AACA,EAAA,OAAO,IAAA;AACT;AAIA,IAAI,4BAAA,GAAwC,KAAA;AACrC,MAAM,uBAAA,GAA0B;AAMvC,SAAS,sBAAsB,aAAA,EAA2E;AACxG,EAAA,OAAO,OAAO,aAAA,KAAkB,UAAA;AAClC;AAKO,SAAS,kBAAA,GAAiD;AAE/D,EAAA,MAAM,wBAAwB,MAAA,CAAO,QAAA;AAErC,EAAA,IAAI,CAAC,qBAAA,CAAsB,qBAAqB,CAAA,EAAG;AACjD,IAAA,IAAI,WAAA,EAAa;AACf,MAAA,KAAA,CAAM,IAAI,sGAAsG,CAAA;AAAA,IAClH;AACA,IAAA;AAAA,EACF;AAGA,EAAA,MAAM,kBAAA,GAAqB,EAAA;AAE3B,EAAA,MAAM,UAAA,GAAa,IAAA,CAAK,KAAA,CAAM,uBAAA,GAA0B,kBAAkB,CAAA;AAK1E,EAAA,IAAI;AACF,IAAA,OAAO,IAAI,qBAAA,CAAsB,EAAE,gBAAgB,kBAAA,EAAoB,aAAA,EAAe,YAAY,CAAA;AAAA,EACpG,CAAA,CAAA,MAAQ;AACN,IAAA,IAAI,WAAA,EAAa;AACf,MAAA,KAAA,CAAM,GAAA;AAAA,QACJ;AAAA,OACF;AACA,MAAA,KAAA,CAAM,IAAI,2DAA2D,CAAA;AAAA,IACvE;AACA,IAAA,4BAAA,GAA+B,IAAA;AAAA,EACjC;AAEA,EAAA;AACF;AAKO,SAAS,qBAAqB,OAAA,EAAkC;AAErE,EAAA,IAAI,4BAAA,EAA8B;AAChC,IAAA,IAAI,WAAA,EAAa;AACf,MAAA,KAAA,CAAM,GAAA;AAAA,QACJ;AAAA,OACF;AAAA,IACF;AACA,IAAA,OAAO,KAAA;AAAA,EACT;AAEA,EAAA,IAAI,OAAA,CAAQ,gBAAA,KAAqB,OAAA,IAAW,OAAA,CAAQ,qBAAqB,QAAA,EAAU;AACjF,IAAA,WAAA,IAAe,KAAA,CAAM,KAAK,qEAAqE,CAAA;AAC/F,IAAA,OAAO,KAAA;AAAA,EACT;AAGA,EAAA,MAAM,2BAA2B,OAAA,CAAQ,wBAAA;AAEzC,EAAA,IAAI,CAAC,iBAAA,CAAkB,wBAAwB,CAAA,EAAG;AAChD,IAAA,WAAA,IAAe,KAAA,CAAM,KAAK,6EAA6E,CAAA;AACvG,IAAA,OAAO,KAAA;AAAA,EACT;AAEA,EAAA,IAAI,CAAC,wBAAA,EAA0B;AAC7B,IAAA,WAAA,IACE,KAAA,CAAM,IAAI,4FAA4F,CAAA;AACxG,IAAA,OAAO,KAAA;AAAA,EACT;AAEA,EAAA,OAAO,IAAA,CAAK,QAAO,IAAK,wBAAA;AAC1B;AAEO,SAAS,oBAAoB,IAAA,EAAkB;AACpD,EAAA,IAAA,CAAK,YAAA,CAAa,aAAa,yBAAyB,CAAA;AACxD,EAAA,IAAA,CAAK,YAAA,CAAa,eAAe,oBAAoB,CAAA;AACvD;;;;"}
|
|
1
|
+
{"version":3,"file":"utils.js","sources":["../../../../../src/profiling/utils.ts"],"sourcesContent":["/* eslint-disable max-lines */\nimport type { Client, ContinuousThreadCpuProfile, DebugImage, ProfileChunk, Span } from '@sentry/core';\nimport {\n debug,\n getClient,\n getDebugImagesForResources,\n GLOBAL_OBJ,\n _INTERNAL_performanceTimeToSeconds,\n uuid4,\n} from '@sentry/core';\nimport type { BrowserOptions } from '../client';\nimport { DEBUG_BUILD } from '../debug-build';\nimport { WINDOW } from '../helpers';\nimport type { JSSelfProfile, JSSelfProfiler, JSSelfProfilerConstructor } from './jsSelfProfiling';\n\n// Checking if we are in Main or Worker thread: `self` (not `window`) is the `globalThis` in Web Workers and `importScripts` are only available in Web Workers\nconst isMainThread = 'window' in GLOBAL_OBJ && GLOBAL_OBJ.window === GLOBAL_OBJ && typeof importScripts === 'undefined';\n\n// Setting ID to 0 as we cannot get an ID from Web Workers\nexport const PROFILER_THREAD_ID_STRING = String(0);\nexport const PROFILER_THREAD_NAME = isMainThread ? 'main' : 'worker';\n\n/**\n * Create a profile chunk envelope item\n */\nexport function createProfileChunkPayload(\n jsSelfProfile: JSSelfProfile,\n client: Client,\n profilerId?: string,\n): ProfileChunk {\n // only == to catch null and undefined\n if (jsSelfProfile == null) {\n throw new TypeError(\n `Cannot construct profiling event envelope without a valid profile. Got ${jsSelfProfile} instead.`,\n );\n }\n\n const continuousProfile = convertToContinuousProfile(jsSelfProfile);\n\n const options = client.getOptions();\n const sdk = client.getSdkMetadata?.()?.sdk;\n\n return {\n chunk_id: uuid4(),\n client_sdk: {\n name: sdk?.name ?? 'sentry.javascript.browser',\n version: sdk?.version ?? '0.0.0',\n },\n profiler_id: profilerId || uuid4(),\n platform: 'javascript',\n version: '2',\n release: options.release ?? '',\n environment: options.environment ?? 'production',\n debug_meta: {\n // function name obfuscation\n images: applyDebugMetadata(jsSelfProfile.resources),\n },\n profile: continuousProfile,\n };\n}\n\n/**\n * Validate a profile chunk against the Sample Format V2 requirements.\n * https://develop.sentry.dev/sdk/telemetry/profiles/sample-format-v2/\n * - Presence of samples, stacks, frames\n * - Required metadata fields\n */\nexport function validateProfileChunk(chunk: ProfileChunk): { valid: true } | { reason: string } {\n try {\n // Required metadata\n if (!chunk || typeof chunk !== 'object') {\n return { reason: 'chunk is not an object' };\n }\n\n // profiler_id and chunk_id must be 32 lowercase hex chars\n const isHex32 = (val: unknown): boolean => typeof val === 'string' && /^[a-f0-9]{32}$/.test(val);\n if (!isHex32(chunk.profiler_id)) {\n return { reason: 'missing or invalid profiler_id' };\n }\n if (!isHex32(chunk.chunk_id)) {\n return { reason: 'missing or invalid chunk_id' };\n }\n\n if (!chunk.client_sdk) {\n return { reason: 'missing client_sdk metadata' };\n }\n\n // Profile data must have frames, stacks, samples\n const profile = chunk.profile as { frames?: unknown[]; stacks?: unknown[]; samples?: unknown[] } | undefined;\n if (!profile) {\n return { reason: 'missing profile data' };\n }\n\n if (!Array.isArray(profile.frames) || !profile.frames.length) {\n return { reason: 'profile has no frames' };\n }\n if (!Array.isArray(profile.stacks) || !profile.stacks.length) {\n return { reason: 'profile has no stacks' };\n }\n if (!Array.isArray(profile.samples) || !profile.samples.length) {\n return { reason: 'profile has no samples' };\n }\n\n return { valid: true };\n } catch (e) {\n return { reason: `unknown validation error: ${e}` };\n }\n}\n\n/**\n * Convert from JSSelfProfile format to ContinuousThreadCpuProfile format.\n */\nfunction convertToContinuousProfile(input: {\n frames: { name: string; resourceId?: number; line?: number; column?: number }[];\n stacks: { frameId: number; parentId?: number }[];\n samples: { timestamp: number; stackId?: number }[];\n resources: string[];\n}): ContinuousThreadCpuProfile {\n // Frames map 1:1 by index; fill only when present to avoid sparse writes\n const frames: ContinuousThreadCpuProfile['frames'] = [];\n for (let i = 0; i < input.frames.length; i++) {\n const frame = input.frames[i];\n if (!frame) {\n continue;\n }\n frames[i] = {\n function: frame.name,\n abs_path: typeof frame.resourceId === 'number' ? input.resources[frame.resourceId] : undefined,\n lineno: frame.line,\n colno: frame.column,\n };\n }\n\n // Build stacks by following parent links, top->down order (root last)\n const stacks: ContinuousThreadCpuProfile['stacks'] = [];\n for (let i = 0; i < input.stacks.length; i++) {\n const stackHead = input.stacks[i];\n if (!stackHead) {\n continue;\n }\n const list: number[] = [];\n let current: { frameId: number; parentId?: number } | undefined = stackHead;\n while (current) {\n list.push(current.frameId);\n current = current.parentId === undefined ? undefined : input.stacks[current.parentId];\n }\n stacks[i] = list;\n }\n\n const samples: ContinuousThreadCpuProfile['samples'] = [];\n for (let i = 0; i < input.samples.length; i++) {\n const sample = input.samples[i];\n if (!sample) {\n continue;\n }\n // Sample timestamps are relative to `performance.timeOrigin`, so we convert them like performance entries.\n // The cast is safe: the JS Self-Profiling API only exists if the Performance API does.\n const timestampSeconds = _INTERNAL_performanceTimeToSeconds(sample.timestamp) as number;\n samples[i] = {\n stack_id: sample.stackId ?? 0,\n thread_id: PROFILER_THREAD_ID_STRING,\n timestamp: timestampSeconds,\n };\n }\n\n return {\n frames,\n stacks,\n samples,\n thread_metadata: { [PROFILER_THREAD_ID_STRING]: { name: PROFILER_THREAD_NAME } },\n };\n}\n\n/**\n * Applies debug meta data to an event from a list of paths to resources (sourcemaps)\n */\nexport function applyDebugMetadata(resource_paths: ReadonlyArray<string>): DebugImage[] {\n const client = getClient();\n const options = client?.getOptions();\n const stackParser = options?.stackParser;\n\n if (!stackParser) {\n return [];\n }\n\n return getDebugImagesForResources(stackParser, resource_paths);\n}\n\n/**\n * Checks the given sample rate to make sure it is valid type and value (a boolean, or a number between 0 and 1).\n */\nexport function isValidSampleRate(rate: unknown): boolean {\n // we need to check NaN explicitly because it's of type 'number' and therefore wouldn't get caught by this typecheck\n if ((typeof rate !== 'number' && typeof rate !== 'boolean') || (typeof rate === 'number' && isNaN(rate))) {\n DEBUG_BUILD &&\n debug.warn(\n `[Profiling] Invalid sample rate. Sample rate must be a boolean or a number between 0 and 1. Got ${JSON.stringify(\n rate,\n )} of type ${JSON.stringify(typeof rate)}.`,\n );\n return false;\n }\n\n // Boolean sample rates are always valid\n if (rate === true || rate === false) {\n return true;\n }\n\n // in case sampleRate is a boolean, it will get automatically cast to 1 if it's true and 0 if it's false\n if (rate < 0 || rate > 1) {\n DEBUG_BUILD && debug.warn(`[Profiling] Invalid sample rate. Sample rate must be between 0 and 1. Got ${rate}.`);\n return false;\n }\n return true;\n}\n\n// Keep a flag value to avoid re-initializing the profiler constructor. If it fails\n// once, it will always fail and this allows us to early return.\nlet PROFILING_CONSTRUCTOR_FAILED: boolean = false;\nexport const MAX_PROFILE_DURATION_MS = 30_000;\n\n/**\n * Check if profiler constructor is available.\n * @param maybeProfiler\n */\nfunction isJSProfilerSupported(maybeProfiler: unknown): maybeProfiler is typeof JSSelfProfilerConstructor {\n return typeof maybeProfiler === 'function';\n}\n\n/**\n * Starts the profiler and returns the profiler instance.\n */\nexport function startJSSelfProfile(): JSSelfProfiler | undefined {\n // Feature support check first\n const JSProfilerConstructor = WINDOW.Profiler;\n\n if (!isJSProfilerSupported(JSProfilerConstructor)) {\n if (DEBUG_BUILD) {\n debug.log('[Profiling] Profiling is not supported by this browser, Profiler interface missing on window object.');\n }\n return;\n }\n\n // From initial testing, it seems that the minimum value for sampleInterval is 10ms.\n const samplingIntervalMS = 10;\n // Start the profiler\n const maxSamples = Math.floor(MAX_PROFILE_DURATION_MS / samplingIntervalMS);\n\n // Attempt to initialize the profiler constructor, if it fails, we disable profiling for the current user session.\n // This is likely due to a missing 'Document-Policy': 'js-profiling' header. We do not want to throw an error if this happens\n // as we risk breaking the user's application, so just disable profiling and log an error.\n try {\n return new JSProfilerConstructor({ sampleInterval: samplingIntervalMS, maxBufferSize: maxSamples });\n } catch {\n if (DEBUG_BUILD) {\n debug.log(\n \"[Profiling] Failed to initialize the Profiling constructor, this is likely due to a missing 'Document-Policy': 'js-profiling' header.\",\n );\n debug.log('[Profiling] Disabling profiling for current user session.');\n }\n PROFILING_CONSTRUCTOR_FAILED = true;\n }\n\n return;\n}\n\n/**\n * Determine if a profile should be created for the current session.\n */\nexport function shouldProfileSession(options: BrowserOptions): boolean {\n // If constructor failed once, it will always fail, so we can early return.\n if (PROFILING_CONSTRUCTOR_FAILED) {\n if (DEBUG_BUILD) {\n debug.log(\n '[Profiling] Profiling has been disabled for the duration of the current user session as the JS Profiler could not be started.',\n );\n }\n return false;\n }\n\n if (options.profileLifecycle !== 'trace' && options.profileLifecycle !== 'manual') {\n DEBUG_BUILD && debug.warn('[Profiling] Session not sampled. Invalid `profileLifecycle` option.');\n return false;\n }\n\n // Session sampling: profileSessionSampleRate gates whether profiling is enabled for this session\n const profileSessionSampleRate = options.profileSessionSampleRate;\n\n if (!isValidSampleRate(profileSessionSampleRate)) {\n DEBUG_BUILD && debug.warn('[Profiling] Discarding profile because of invalid profileSessionSampleRate.');\n return false;\n }\n\n if (!profileSessionSampleRate) {\n DEBUG_BUILD &&\n debug.log('[Profiling] Discarding profile because profileSessionSampleRate is not defined or set to 0');\n return false;\n }\n\n return Math.random() <= profileSessionSampleRate;\n}\n\nexport function setThreadAttributes(span: Span): void {\n span.setAttribute('thread.id', PROFILER_THREAD_ID_STRING);\n span.setAttribute('thread.name', PROFILER_THREAD_NAME);\n}\n"],"names":[],"mappings":"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"}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { setRouteProvider } from '@sentry/browser-utils';
|
|
2
|
-
import { Client, getSDKSource, applySdkMetadata, addAutoIpAddressToSession } from '@sentry/core';
|
|
2
|
+
import { Client, getSDKSource, applySdkMetadata, addAutoIpAddressToSession, timestampInSeconds } from '@sentry/core';
|
|
3
3
|
import { eventFromException, eventFromMessage } from './eventbuilder.js';
|
|
4
4
|
import { WINDOW } from './helpers.js';
|
|
5
5
|
|
|
@@ -28,18 +28,42 @@ class BrowserClient extends Client {
|
|
|
28
28
|
};
|
|
29
29
|
}
|
|
30
30
|
const { sendClientReports } = this._options;
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
31
|
+
const checkDrift = () => {
|
|
32
|
+
timestampInSeconds();
|
|
33
|
+
};
|
|
34
|
+
const listeners = [
|
|
35
|
+
// Flush buffered data when the page becomes hidden (e.g. tab switch, navigation, or the page
|
|
36
|
+
// being discarded). `flush()` emits the `flush` hook, which drains the span (streaming), log and
|
|
37
|
+
// metric buffers and hands the resulting envelopes to the transport (which uses `keepalive`).
|
|
38
|
+
// Client report outcomes don't listen to the `flush` hook, so we flush them separately.
|
|
39
|
+
[
|
|
40
|
+
WINDOW.document,
|
|
41
|
+
"visibilitychange",
|
|
42
|
+
() => {
|
|
43
|
+
checkDrift();
|
|
44
|
+
if (WINDOW.document.visibilityState === "hidden") {
|
|
45
|
+
if (sendClientReports) {
|
|
46
|
+
this._flushOutcomes();
|
|
47
|
+
}
|
|
48
|
+
queueMicrotask(() => {
|
|
49
|
+
void this.flush();
|
|
50
|
+
});
|
|
36
51
|
}
|
|
37
|
-
queueMicrotask(() => {
|
|
38
|
-
void this.flush();
|
|
39
|
-
});
|
|
40
52
|
}
|
|
41
|
-
|
|
42
|
-
|
|
53
|
+
],
|
|
54
|
+
// `freeze` and `resume` are only fired by Chromium browsers. Other browsers never fire them, so these listeners
|
|
55
|
+
// are no-ops there.
|
|
56
|
+
[WINDOW.document, "freeze", checkDrift],
|
|
57
|
+
[WINDOW.document, "resume", checkDrift],
|
|
58
|
+
// Pages restored from the back/forward cache were paused while they were cached.
|
|
59
|
+
[WINDOW, "pagehide", checkDrift],
|
|
60
|
+
[WINDOW, "pageshow", checkDrift]
|
|
61
|
+
];
|
|
62
|
+
listeners.forEach(([target, name, listener]) => target?.addEventListener?.(name, listener));
|
|
63
|
+
this.on(
|
|
64
|
+
"close",
|
|
65
|
+
() => listeners.forEach(([target, name, listener]) => target?.removeEventListener?.(name, listener))
|
|
66
|
+
);
|
|
43
67
|
if (userInfo) {
|
|
44
68
|
this.on("beforeSendSession", addAutoIpAddressToSession);
|
|
45
69
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"client.js","sources":["../../../../src/client.ts"],"sourcesContent":["import type {\n ClientOptions,\n Event,\n EventHint,\n Options as CoreOptions,\n ParameterizedString,\n Scope,\n SeverityLevel,\n} from '@sentry/core';\nimport type { BrowserClientReplayOptions } from '@sentry/core/browser';\nimport type { RouteProvider } from '@sentry/browser-utils';\nimport { setRouteProvider } from '@sentry/browser-utils';\nimport { addAutoIpAddressToSession, applySdkMetadata, Client, getSDKSource } from '@sentry/core';\nimport { eventFromException, eventFromMessage } from './eventbuilder';\nimport { WINDOW } from './helpers';\nimport type { BrowserTransportOptions } from './transports/types';\n\n/**\n * A magic string that build tooling can leverage in order to inject a release value into the SDK.\n */\ndeclare const __SENTRY_RELEASE__: string | undefined;\n\nexport type BrowserClientProfilingOptions = {\n /**\n * Sets profiling session sample rate for the entire profiling session.\n *\n * A profiling session corresponds to a user session, meaning it is set once at integration initialization and\n * persisted until the next page reload. This rate determines what percentage of user sessions will have profiling enabled.\n * @default 0\n */\n profileSessionSampleRate?: number;\n\n /**\n * Set the lifecycle mode of the profiler.\n * - **manual**: The profiler will be manually started and stopped via `startProfiler`/`stopProfiler`.\n * If a session is sampled, is dependent on the `profileSessionSampleRate`.\n * - **trace**: The profiler will be automatically started when a root span exists and stopped when there are no\n * more sampled root spans. Whether a session is sampled, is dependent on the `profileSessionSampleRate` and the\n * existing sampling configuration for tracing (`tracesSampleRate`/`tracesSampler`).\n *\n * @default 'manual'\n */\n profileLifecycle?: 'manual' | 'trace';\n};\n\ntype BrowserSpecificOptions = BrowserClientReplayOptions &\n BrowserClientProfilingOptions & {\n /** If configured, this URL will be used as base URL for lazy loading integration. */\n cdnBaseUrl?: string;\n\n /**\n * Important: Only set this option if you know what you are doing!\n *\n * By default, the SDK will check if `Sentry.init` is called in a browser extension.\n * In case it is, it will stop initialization and log a warning\n * because browser extensions require a different Sentry initialization process:\n * https://docs.sentry.io/platforms/javascript/best-practices/shared-environments/\n *\n * Setting up the SDK in a browser extension with global error monitoring is not recommended\n * and will likely flood you with errors from other web sites or extensions. This can heavily\n * impact your quota and cause interference with your and other Sentry SDKs in shared environments.\n *\n * If this check wrongfully flags your setup as a browser extension, you can set this\n * option to `true` to skip the check.\n *\n * @default false\n */\n skipBrowserExtensionCheck?: boolean;\n\n /**\n * If you use Spotlight by Sentry during development, use\n * this option to forward captured Sentry events to Spotlight.\n *\n * Either set it to true, or provide a specific Spotlight Sidecar URL.\n *\n * More details: https://spotlightjs.com/\n *\n * IMPORTANT: Only set this option to `true` while developing, not in production!\n */\n spotlight?: boolean | string;\n\n /**\n * Resolves URLs to low-cardinality route names, e.g. `/users/42` -> `/users/:id`.\n *\n * Framework SDKs set this for you. Set it yourself to name routes for a router the SDK has no\n * integration for. `setRouteProvider` replaces it at runtime.\n */\n routeProvider?: RouteProvider;\n };\n/**\n * Configuration options for the Sentry Browser SDK.\n * @see @sentry/core Options for more information.\n */\nexport type BrowserOptions = CoreOptions<BrowserTransportOptions> & BrowserSpecificOptions;\n\n/**\n * Configuration options for the Sentry Browser SDK Client class\n * @see BrowserClient for more information.\n */\nexport type BrowserClientOptions = ClientOptions<BrowserTransportOptions> & BrowserSpecificOptions;\n\n/**\n * The Sentry Browser SDK Client.\n *\n * @see BrowserOptions for documentation on configuration options.\n * @see SentryClient for usage documentation.\n */\nexport class BrowserClient extends Client<BrowserClientOptions> {\n /**\n * Creates a new Browser SDK instance.\n *\n * @param options Configuration options for this SDK.\n */\n public constructor(options: BrowserClientOptions) {\n const opts = applyDefaultOptions(options);\n const sdkSource = WINDOW.SENTRY_SDK_SOURCE || getSDKSource();\n applySdkMetadata(opts, 'browser', ['browser'], sdkSource);\n\n super(opts);\n\n if (opts.routeProvider) {\n setRouteProvider(opts.routeProvider, this);\n }\n\n // Unhandled errors don't actually crash the browser, so we report `unhandled` rather than `crashed`.\n this._unhandledSessionStatus = 'unhandled';\n\n const { userInfo } = this.getDataCollectionOptions();\n\n if (opts._metadata?.sdk) {\n opts._metadata.sdk.settings = {\n // Only allow IP inferral by Relay if the user opted in via dataCollection\n infer_ip: userInfo ? 'auto' : 'never',\n // purposefully allowing already passed settings to override the default\n ...opts._metadata.sdk.settings,\n };\n }\n\n const { sendClientReports } = this._options;\n\n // Flush buffered data when the page becomes hidden (e.g. tab switch, navigation, or the page\n // being discarded). `flush()` emits the `flush` hook, which drains the span (streaming), log and\n // metric buffers and hands the resulting envelopes to the transport (which uses `keepalive`).\n // Client report outcomes don't listen to the `flush` hook, so we flush them separately.\n if (WINDOW.document) {\n WINDOW.document.addEventListener('visibilitychange', () => {\n if (WINDOW.document.visibilityState === 'hidden') {\n if (sendClientReports) {\n this._flushOutcomes();\n }\n // Defer the flush to a microtask so that visibilitychange listeners registered after this\n // one have already run. In particular, browser tracing's background-tab detection ends the\n // active pageload/navigation (segment) span when the page is hidden. Deferring ensures that\n // segment span has been added to the streaming buffer before we flush, so it is sent\n // together with its child spans instead of being orphaned.\n queueMicrotask(() => {\n void this.flush();\n });\n }\n });\n }\n\n if (userInfo) {\n this.on('beforeSendSession', addAutoIpAddressToSession);\n }\n }\n\n /**\n * @inheritDoc\n */\n public eventFromException(exception: unknown, hint?: EventHint): PromiseLike<Event> {\n return eventFromException(this._options.stackParser, exception, hint, this._options.attachStacktrace);\n }\n\n /**\n * @inheritDoc\n */\n public eventFromMessage(\n message: ParameterizedString,\n level: SeverityLevel = 'info',\n hint?: EventHint,\n ): PromiseLike<Event> {\n return eventFromMessage(this._options.stackParser, message, level, hint, this._options.attachStacktrace);\n }\n\n /**\n * @inheritDoc\n */\n protected _prepareEvent(\n event: Event,\n hint: EventHint,\n currentScope: Scope,\n isolationScope: Scope,\n ): PromiseLike<Event | null> {\n event.platform = event.platform || 'javascript';\n\n return super._prepareEvent(event, hint, currentScope, isolationScope);\n }\n}\n\n/** Exported only for tests. */\nexport function applyDefaultOptions<T extends Partial<BrowserClientOptions>>(optionsArg: T): T {\n return {\n release:\n typeof __SENTRY_RELEASE__ === 'string' // This allows build tooling to find-and-replace __SENTRY_RELEASE__ to inject a release value\n ? __SENTRY_RELEASE__\n : WINDOW.SENTRY_RELEASE?.id, // This supports the variable that sentry-webpack-plugin injects\n sendClientReports: true,\n // We default this to true, as it is the safer scenario\n parentSpanIsAlwaysRootSpan: true,\n ...optionsArg,\n };\n}\n"],"names":[],"mappings":";;;;;AA2GO,MAAM,sBAAsB,MAAA,CAA6B;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAMvD,YAAY,OAAA,EAA+B;AAChD,IAAA,MAAM,IAAA,GAAO,oBAAoB,OAAO,CAAA;AACxC,IAAA,MAAM,SAAA,GAAY,MAAA,CAAO,iBAAA,IAAqB,YAAA,EAAa;AAC3D,IAAA,gBAAA,CAAiB,IAAA,EAAM,SAAA,EAAW,CAAC,SAAS,GAAG,SAAS,CAAA;AAExD,IAAA,KAAA,CAAM,IAAI,CAAA;AAEV,IAAA,IAAI,KAAK,aAAA,EAAe;AACtB,MAAA,gBAAA,CAAiB,IAAA,CAAK,eAAe,IAAI,CAAA;AAAA,IAC3C;AAGA,IAAA,IAAA,CAAK,uBAAA,GAA0B,WAAA;AAE/B,IAAA,MAAM,EAAE,QAAA,EAAS,GAAI,IAAA,CAAK,wBAAA,EAAyB;AAEnD,IAAA,IAAI,IAAA,CAAK,WAAW,GAAA,EAAK;AACvB,MAAA,IAAA,CAAK,SAAA,CAAU,IAAI,QAAA,GAAW;AAAA;AAAA,QAE5B,QAAA,EAAU,WAAW,MAAA,GAAS,OAAA;AAAA;AAAA,QAE9B,GAAG,IAAA,CAAK,SAAA,CAAU,GAAA,CAAI;AAAA,OACxB;AAAA,IACF;AAEA,IAAA,MAAM,EAAE,iBAAA,EAAkB,GAAI,IAAA,CAAK,QAAA;AAMnC,IAAA,IAAI,OAAO,QAAA,EAAU;AACnB,MAAA,MAAA,CAAO,QAAA,CAAS,gBAAA,CAAiB,kBAAA,EAAoB,MAAM;AACzD,QAAA,IAAI,MAAA,CAAO,QAAA,CAAS,eAAA,KAAoB,QAAA,EAAU;AAChD,UAAA,IAAI,iBAAA,EAAmB;AACrB,YAAA,IAAA,CAAK,cAAA,EAAe;AAAA,UACtB;AAMA,UAAA,cAAA,CAAe,MAAM;AACnB,YAAA,KAAK,KAAK,KAAA,EAAM;AAAA,UAClB,CAAC,CAAA;AAAA,QACH;AAAA,MACF,CAAC,CAAA;AAAA,IACH;AAEA,IAAA,IAAI,QAAA,EAAU;AACZ,MAAA,IAAA,CAAK,EAAA,CAAG,qBAAqB,yBAAyB,CAAA;AAAA,IACxD;AAAA,EACF;AAAA;AAAA;AAAA;AAAA,EAKO,kBAAA,CAAmB,WAAoB,IAAA,EAAsC;AAClF,IAAA,OAAO,kBAAA,CAAmB,KAAK,QAAA,CAAS,WAAA,EAAa,WAAW,IAAA,EAAM,IAAA,CAAK,SAAS,gBAAgB,CAAA;AAAA,EACtG;AAAA;AAAA;AAAA;AAAA,EAKO,gBAAA,CACL,OAAA,EACA,KAAA,GAAuB,MAAA,EACvB,IAAA,EACoB;AACpB,IAAA,OAAO,gBAAA,CAAiB,KAAK,QAAA,CAAS,WAAA,EAAa,SAAS,KAAA,EAAO,IAAA,EAAM,IAAA,CAAK,QAAA,CAAS,gBAAgB,CAAA;AAAA,EACzG;AAAA;AAAA;AAAA;AAAA,EAKU,aAAA,CACR,KAAA,EACA,IAAA,EACA,YAAA,EACA,cAAA,EAC2B;AAC3B,IAAA,KAAA,CAAM,QAAA,GAAW,MAAM,QAAA,IAAY,YAAA;AAEnC,IAAA,OAAO,KAAA,CAAM,aAAA,CAAc,KAAA,EAAO,IAAA,EAAM,cAAc,cAAc,CAAA;AAAA,EACtE;AACF;AAGO,SAAS,oBAA6D,UAAA,EAAkB;AAC7F,EAAA,OAAO;AAAA,IACL,SACE,OAAO,kBAAA,KAAuB,QAAA,GAC1B,kBAAA,GACA,OAAO,cAAA,EAAgB,EAAA;AAAA;AAAA,IAC7B,iBAAA,EAAmB,IAAA;AAAA;AAAA,IAEnB,0BAAA,EAA4B,IAAA;AAAA,IAC5B,GAAG;AAAA,GACL;AACF;;;;"}
|
|
1
|
+
{"version":3,"file":"client.js","sources":["../../../../src/client.ts"],"sourcesContent":["import type {\n ClientOptions,\n Event,\n EventHint,\n Options as CoreOptions,\n ParameterizedString,\n Scope,\n SeverityLevel,\n} from '@sentry/core';\nimport type { BrowserClientReplayOptions } from '@sentry/core/browser';\nimport type { RouteProvider } from '@sentry/browser-utils';\nimport { setRouteProvider } from '@sentry/browser-utils';\nimport { addAutoIpAddressToSession, applySdkMetadata, Client, getSDKSource, timestampInSeconds } from '@sentry/core';\nimport { eventFromException, eventFromMessage } from './eventbuilder';\nimport { WINDOW } from './helpers';\nimport type { BrowserTransportOptions } from './transports/types';\n\n/**\n * A magic string that build tooling can leverage in order to inject a release value into the SDK.\n */\ndeclare const __SENTRY_RELEASE__: string | undefined;\n\nexport type BrowserClientProfilingOptions = {\n /**\n * Sets profiling session sample rate for the entire profiling session.\n *\n * A profiling session corresponds to a user session, meaning it is set once at integration initialization and\n * persisted until the next page reload. This rate determines what percentage of user sessions will have profiling enabled.\n * @default 0\n */\n profileSessionSampleRate?: number;\n\n /**\n * Set the lifecycle mode of the profiler.\n * - **manual**: The profiler will be manually started and stopped via `startProfiler`/`stopProfiler`.\n * If a session is sampled, is dependent on the `profileSessionSampleRate`.\n * - **trace**: The profiler will be automatically started when a root span exists and stopped when there are no\n * more sampled root spans. Whether a session is sampled, is dependent on the `profileSessionSampleRate` and the\n * existing sampling configuration for tracing (`tracesSampleRate`/`tracesSampler`).\n *\n * @default 'manual'\n */\n profileLifecycle?: 'manual' | 'trace';\n};\n\ntype BrowserSpecificOptions = BrowserClientReplayOptions &\n BrowserClientProfilingOptions & {\n /** If configured, this URL will be used as base URL for lazy loading integration. */\n cdnBaseUrl?: string;\n\n /**\n * Important: Only set this option if you know what you are doing!\n *\n * By default, the SDK will check if `Sentry.init` is called in a browser extension.\n * In case it is, it will stop initialization and log a warning\n * because browser extensions require a different Sentry initialization process:\n * https://docs.sentry.io/platforms/javascript/best-practices/shared-environments/\n *\n * Setting up the SDK in a browser extension with global error monitoring is not recommended\n * and will likely flood you with errors from other web sites or extensions. This can heavily\n * impact your quota and cause interference with your and other Sentry SDKs in shared environments.\n *\n * If this check wrongfully flags your setup as a browser extension, you can set this\n * option to `true` to skip the check.\n *\n * @default false\n */\n skipBrowserExtensionCheck?: boolean;\n\n /**\n * If you use Spotlight by Sentry during development, use\n * this option to forward captured Sentry events to Spotlight.\n *\n * Either set it to true, or provide a specific Spotlight Sidecar URL.\n *\n * More details: https://spotlightjs.com/\n *\n * IMPORTANT: Only set this option to `true` while developing, not in production!\n */\n spotlight?: boolean | string;\n\n /**\n * Resolves URLs to low-cardinality route names, e.g. `/users/42` -> `/users/:id`.\n *\n * Framework SDKs set this for you. Set it yourself to name routes for a router the SDK has no\n * integration for. `setRouteProvider` replaces it at runtime.\n */\n routeProvider?: RouteProvider;\n };\n/**\n * Configuration options for the Sentry Browser SDK.\n * @see @sentry/core Options for more information.\n */\nexport type BrowserOptions = CoreOptions<BrowserTransportOptions> & BrowserSpecificOptions;\n\n/**\n * Configuration options for the Sentry Browser SDK Client class\n * @see BrowserClient for more information.\n */\nexport type BrowserClientOptions = ClientOptions<BrowserTransportOptions> & BrowserSpecificOptions;\n\n/**\n * The Sentry Browser SDK Client.\n *\n * @see BrowserOptions for documentation on configuration options.\n * @see SentryClient for usage documentation.\n */\nexport class BrowserClient extends Client<BrowserClientOptions> {\n /**\n * Creates a new Browser SDK instance.\n *\n * @param options Configuration options for this SDK.\n */\n public constructor(options: BrowserClientOptions) {\n const opts = applyDefaultOptions(options);\n const sdkSource = WINDOW.SENTRY_SDK_SOURCE || getSDKSource();\n applySdkMetadata(opts, 'browser', ['browser'], sdkSource);\n\n super(opts);\n\n if (opts.routeProvider) {\n setRouteProvider(opts.routeProvider, this);\n }\n\n // Unhandled errors don't actually crash the browser, so we report `unhandled` rather than `crashed`.\n this._unhandledSessionStatus = 'unhandled';\n\n const { userInfo } = this.getDataCollectionOptions();\n\n if (opts._metadata?.sdk) {\n opts._metadata.sdk.settings = {\n // Only allow IP inferral by Relay if the user opted in via dataCollection\n infer_ip: userInfo ? 'auto' : 'never',\n // purposefully allowing already passed settings to override the default\n ...opts._metadata.sdk.settings,\n };\n }\n\n const { sendClientReports } = this._options;\n\n // Devices usually hide the page before they sleep and show it again after they wake up. Checking for drift\n // at both points makes the time origin correction happen at the sleep, rather than at the next regular timestamp call.\n const checkDrift = (): void => {\n timestampInSeconds();\n };\n\n // Most bundle-size-efficient way to add and remove listeners: Create a table to avoid repeated\n // addeventlistener and removeeventlistener calls. The alternative, using an AbortController, can\n // lead to app breakage on old Angular<>Zone.js < 0.14.3 versions.\n const listeners: [EventTarget | undefined, string, () => void][] = [\n // Flush buffered data when the page becomes hidden (e.g. tab switch, navigation, or the page\n // being discarded). `flush()` emits the `flush` hook, which drains the span (streaming), log and\n // metric buffers and hands the resulting envelopes to the transport (which uses `keepalive`).\n // Client report outcomes don't listen to the `flush` hook, so we flush them separately.\n [\n WINDOW.document,\n 'visibilitychange',\n () => {\n // Devices usually hide the page before they sleep and show it again after they wake up. Checking for drift\n // at both points makes the time origin correction happen at the sleep, rather than at the next regular timestamp call.\n checkDrift();\n\n if (WINDOW.document.visibilityState === 'hidden') {\n if (sendClientReports) {\n this._flushOutcomes();\n }\n // Defer the flush to a microtask so that visibilitychange listeners registered after this\n // one have already run. In particular, browser tracing's background-tab detection ends the\n // active pageload/navigation (segment) span when the page is hidden. Deferring ensures that\n // segment span has been added to the streaming buffer before we flush, so it is sent\n // together with its child spans instead of being orphaned.\n queueMicrotask(() => {\n void this.flush();\n });\n }\n },\n ],\n // `freeze` and `resume` are only fired by Chromium browsers. Other browsers never fire them, so these listeners\n // are no-ops there.\n [WINDOW.document, 'freeze', checkDrift],\n [WINDOW.document, 'resume', checkDrift],\n // Pages restored from the back/forward cache were paused while they were cached.\n [WINDOW, 'pagehide', checkDrift],\n [WINDOW, 'pageshow', checkDrift],\n ];\n\n listeners.forEach(([target, name, listener]) => target?.addEventListener?.(name, listener));\n this.on('close', () =>\n listeners.forEach(([target, name, listener]) => target?.removeEventListener?.(name, listener)),\n );\n\n if (userInfo) {\n this.on('beforeSendSession', addAutoIpAddressToSession);\n }\n }\n\n /**\n * @inheritDoc\n */\n public eventFromException(exception: unknown, hint?: EventHint): PromiseLike<Event> {\n return eventFromException(this._options.stackParser, exception, hint, this._options.attachStacktrace);\n }\n\n /**\n * @inheritDoc\n */\n public eventFromMessage(\n message: ParameterizedString,\n level: SeverityLevel = 'info',\n hint?: EventHint,\n ): PromiseLike<Event> {\n return eventFromMessage(this._options.stackParser, message, level, hint, this._options.attachStacktrace);\n }\n\n /**\n * @inheritDoc\n */\n protected _prepareEvent(\n event: Event,\n hint: EventHint,\n currentScope: Scope,\n isolationScope: Scope,\n ): PromiseLike<Event | null> {\n event.platform = event.platform || 'javascript';\n\n return super._prepareEvent(event, hint, currentScope, isolationScope);\n }\n}\n\n/** Exported only for tests. */\nexport function applyDefaultOptions<T extends Partial<BrowserClientOptions>>(optionsArg: T): T {\n return {\n release:\n typeof __SENTRY_RELEASE__ === 'string' // This allows build tooling to find-and-replace __SENTRY_RELEASE__ to inject a release value\n ? __SENTRY_RELEASE__\n : WINDOW.SENTRY_RELEASE?.id, // This supports the variable that sentry-webpack-plugin injects\n sendClientReports: true,\n // We default this to true, as it is the safer scenario\n parentSpanIsAlwaysRootSpan: true,\n ...optionsArg,\n };\n}\n"],"names":[],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"type":"module","version":"11.
|
|
1
|
+
{"type":"module","version":"11.6.0","sideEffects":false}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { GLOBAL_OBJ, debug, uuid4,
|
|
1
|
+
import { GLOBAL_OBJ, debug, uuid4, _INTERNAL_performanceTimeToSeconds, getClient, getDebugImagesForResources } from '@sentry/core';
|
|
2
2
|
import { DEBUG_BUILD } from '../debug-build.js';
|
|
3
3
|
import { WINDOW } from '../helpers.js';
|
|
4
4
|
|
|
@@ -93,16 +93,13 @@ function convertToContinuousProfile(input) {
|
|
|
93
93
|
}
|
|
94
94
|
stacks[i] = list;
|
|
95
95
|
}
|
|
96
|
-
const perfOrigin = browserPerformanceTimeOrigin();
|
|
97
|
-
const origin = typeof performance.timeOrigin === "number" ? performance.timeOrigin : perfOrigin || 0;
|
|
98
|
-
const adjustForOriginChange = origin - (perfOrigin || origin);
|
|
99
96
|
const samples = [];
|
|
100
97
|
for (let i = 0; i < input.samples.length; i++) {
|
|
101
98
|
const sample = input.samples[i];
|
|
102
99
|
if (!sample) {
|
|
103
100
|
continue;
|
|
104
101
|
}
|
|
105
|
-
const timestampSeconds = (
|
|
102
|
+
const timestampSeconds = _INTERNAL_performanceTimeToSeconds(sample.timestamp);
|
|
106
103
|
samples[i] = {
|
|
107
104
|
stack_id: sample.stackId ?? 0,
|
|
108
105
|
thread_id: PROFILER_THREAD_ID_STRING,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"utils.js","sources":["../../../../../src/profiling/utils.ts"],"sourcesContent":["/* eslint-disable max-lines */\nimport type { Client, ContinuousThreadCpuProfile, DebugImage, ProfileChunk, Span } from '@sentry/core';\nimport {\n browserPerformanceTimeOrigin,\n debug,\n getClient,\n getDebugImagesForResources,\n GLOBAL_OBJ,\n uuid4,\n} from '@sentry/core';\nimport type { BrowserOptions } from '../client';\nimport { DEBUG_BUILD } from '../debug-build';\nimport { WINDOW } from '../helpers';\nimport type { JSSelfProfile, JSSelfProfiler, JSSelfProfilerConstructor } from './jsSelfProfiling';\n\n// Checking if we are in Main or Worker thread: `self` (not `window`) is the `globalThis` in Web Workers and `importScripts` are only available in Web Workers\nconst isMainThread = 'window' in GLOBAL_OBJ && GLOBAL_OBJ.window === GLOBAL_OBJ && typeof importScripts === 'undefined';\n\n// Setting ID to 0 as we cannot get an ID from Web Workers\nexport const PROFILER_THREAD_ID_STRING = String(0);\nexport const PROFILER_THREAD_NAME = isMainThread ? 'main' : 'worker';\n\n/**\n * Create a profile chunk envelope item\n */\nexport function createProfileChunkPayload(\n jsSelfProfile: JSSelfProfile,\n client: Client,\n profilerId?: string,\n): ProfileChunk {\n // only == to catch null and undefined\n if (jsSelfProfile == null) {\n throw new TypeError(\n `Cannot construct profiling event envelope without a valid profile. Got ${jsSelfProfile} instead.`,\n );\n }\n\n const continuousProfile = convertToContinuousProfile(jsSelfProfile);\n\n const options = client.getOptions();\n const sdk = client.getSdkMetadata?.()?.sdk;\n\n return {\n chunk_id: uuid4(),\n client_sdk: {\n name: sdk?.name ?? 'sentry.javascript.browser',\n version: sdk?.version ?? '0.0.0',\n },\n profiler_id: profilerId || uuid4(),\n platform: 'javascript',\n version: '2',\n release: options.release ?? '',\n environment: options.environment ?? 'production',\n debug_meta: {\n // function name obfuscation\n images: applyDebugMetadata(jsSelfProfile.resources),\n },\n profile: continuousProfile,\n };\n}\n\n/**\n * Validate a profile chunk against the Sample Format V2 requirements.\n * https://develop.sentry.dev/sdk/telemetry/profiles/sample-format-v2/\n * - Presence of samples, stacks, frames\n * - Required metadata fields\n */\nexport function validateProfileChunk(chunk: ProfileChunk): { valid: true } | { reason: string } {\n try {\n // Required metadata\n if (!chunk || typeof chunk !== 'object') {\n return { reason: 'chunk is not an object' };\n }\n\n // profiler_id and chunk_id must be 32 lowercase hex chars\n const isHex32 = (val: unknown): boolean => typeof val === 'string' && /^[a-f0-9]{32}$/.test(val);\n if (!isHex32(chunk.profiler_id)) {\n return { reason: 'missing or invalid profiler_id' };\n }\n if (!isHex32(chunk.chunk_id)) {\n return { reason: 'missing or invalid chunk_id' };\n }\n\n if (!chunk.client_sdk) {\n return { reason: 'missing client_sdk metadata' };\n }\n\n // Profile data must have frames, stacks, samples\n const profile = chunk.profile as { frames?: unknown[]; stacks?: unknown[]; samples?: unknown[] } | undefined;\n if (!profile) {\n return { reason: 'missing profile data' };\n }\n\n if (!Array.isArray(profile.frames) || !profile.frames.length) {\n return { reason: 'profile has no frames' };\n }\n if (!Array.isArray(profile.stacks) || !profile.stacks.length) {\n return { reason: 'profile has no stacks' };\n }\n if (!Array.isArray(profile.samples) || !profile.samples.length) {\n return { reason: 'profile has no samples' };\n }\n\n return { valid: true };\n } catch (e) {\n return { reason: `unknown validation error: ${e}` };\n }\n}\n\n/**\n * Convert from JSSelfProfile format to ContinuousThreadCpuProfile format.\n */\nfunction convertToContinuousProfile(input: {\n frames: { name: string; resourceId?: number; line?: number; column?: number }[];\n stacks: { frameId: number; parentId?: number }[];\n samples: { timestamp: number; stackId?: number }[];\n resources: string[];\n}): ContinuousThreadCpuProfile {\n // Frames map 1:1 by index; fill only when present to avoid sparse writes\n const frames: ContinuousThreadCpuProfile['frames'] = [];\n for (let i = 0; i < input.frames.length; i++) {\n const frame = input.frames[i];\n if (!frame) {\n continue;\n }\n frames[i] = {\n function: frame.name,\n abs_path: typeof frame.resourceId === 'number' ? input.resources[frame.resourceId] : undefined,\n lineno: frame.line,\n colno: frame.column,\n };\n }\n\n // Build stacks by following parent links, top->down order (root last)\n const stacks: ContinuousThreadCpuProfile['stacks'] = [];\n for (let i = 0; i < input.stacks.length; i++) {\n const stackHead = input.stacks[i];\n if (!stackHead) {\n continue;\n }\n const list: number[] = [];\n let current: { frameId: number; parentId?: number } | undefined = stackHead;\n while (current) {\n list.push(current.frameId);\n current = current.parentId === undefined ? undefined : input.stacks[current.parentId];\n }\n stacks[i] = list;\n }\n\n // Align timestamps to SDK time origin to match span/event timelines\n const perfOrigin = browserPerformanceTimeOrigin();\n const origin = typeof performance.timeOrigin === 'number' ? performance.timeOrigin : perfOrigin || 0;\n const adjustForOriginChange = origin - (perfOrigin || origin);\n\n const samples: ContinuousThreadCpuProfile['samples'] = [];\n for (let i = 0; i < input.samples.length; i++) {\n const sample = input.samples[i];\n if (!sample) {\n continue;\n }\n // Convert ms to seconds epoch-based timestamp\n const timestampSeconds = (origin + (sample.timestamp - adjustForOriginChange)) / 1000;\n samples[i] = {\n stack_id: sample.stackId ?? 0,\n thread_id: PROFILER_THREAD_ID_STRING,\n timestamp: timestampSeconds,\n };\n }\n\n return {\n frames,\n stacks,\n samples,\n thread_metadata: { [PROFILER_THREAD_ID_STRING]: { name: PROFILER_THREAD_NAME } },\n };\n}\n\n/**\n * Applies debug meta data to an event from a list of paths to resources (sourcemaps)\n */\nexport function applyDebugMetadata(resource_paths: ReadonlyArray<string>): DebugImage[] {\n const client = getClient();\n const options = client?.getOptions();\n const stackParser = options?.stackParser;\n\n if (!stackParser) {\n return [];\n }\n\n return getDebugImagesForResources(stackParser, resource_paths);\n}\n\n/**\n * Checks the given sample rate to make sure it is valid type and value (a boolean, or a number between 0 and 1).\n */\nexport function isValidSampleRate(rate: unknown): boolean {\n // we need to check NaN explicitly because it's of type 'number' and therefore wouldn't get caught by this typecheck\n if ((typeof rate !== 'number' && typeof rate !== 'boolean') || (typeof rate === 'number' && isNaN(rate))) {\n DEBUG_BUILD &&\n debug.warn(\n `[Profiling] Invalid sample rate. Sample rate must be a boolean or a number between 0 and 1. Got ${JSON.stringify(\n rate,\n )} of type ${JSON.stringify(typeof rate)}.`,\n );\n return false;\n }\n\n // Boolean sample rates are always valid\n if (rate === true || rate === false) {\n return true;\n }\n\n // in case sampleRate is a boolean, it will get automatically cast to 1 if it's true and 0 if it's false\n if (rate < 0 || rate > 1) {\n DEBUG_BUILD && debug.warn(`[Profiling] Invalid sample rate. Sample rate must be between 0 and 1. Got ${rate}.`);\n return false;\n }\n return true;\n}\n\n// Keep a flag value to avoid re-initializing the profiler constructor. If it fails\n// once, it will always fail and this allows us to early return.\nlet PROFILING_CONSTRUCTOR_FAILED: boolean = false;\nexport const MAX_PROFILE_DURATION_MS = 30_000;\n\n/**\n * Check if profiler constructor is available.\n * @param maybeProfiler\n */\nfunction isJSProfilerSupported(maybeProfiler: unknown): maybeProfiler is typeof JSSelfProfilerConstructor {\n return typeof maybeProfiler === 'function';\n}\n\n/**\n * Starts the profiler and returns the profiler instance.\n */\nexport function startJSSelfProfile(): JSSelfProfiler | undefined {\n // Feature support check first\n const JSProfilerConstructor = WINDOW.Profiler;\n\n if (!isJSProfilerSupported(JSProfilerConstructor)) {\n if (DEBUG_BUILD) {\n debug.log('[Profiling] Profiling is not supported by this browser, Profiler interface missing on window object.');\n }\n return;\n }\n\n // From initial testing, it seems that the minimum value for sampleInterval is 10ms.\n const samplingIntervalMS = 10;\n // Start the profiler\n const maxSamples = Math.floor(MAX_PROFILE_DURATION_MS / samplingIntervalMS);\n\n // Attempt to initialize the profiler constructor, if it fails, we disable profiling for the current user session.\n // This is likely due to a missing 'Document-Policy': 'js-profiling' header. We do not want to throw an error if this happens\n // as we risk breaking the user's application, so just disable profiling and log an error.\n try {\n return new JSProfilerConstructor({ sampleInterval: samplingIntervalMS, maxBufferSize: maxSamples });\n } catch {\n if (DEBUG_BUILD) {\n debug.log(\n \"[Profiling] Failed to initialize the Profiling constructor, this is likely due to a missing 'Document-Policy': 'js-profiling' header.\",\n );\n debug.log('[Profiling] Disabling profiling for current user session.');\n }\n PROFILING_CONSTRUCTOR_FAILED = true;\n }\n\n return;\n}\n\n/**\n * Determine if a profile should be created for the current session.\n */\nexport function shouldProfileSession(options: BrowserOptions): boolean {\n // If constructor failed once, it will always fail, so we can early return.\n if (PROFILING_CONSTRUCTOR_FAILED) {\n if (DEBUG_BUILD) {\n debug.log(\n '[Profiling] Profiling has been disabled for the duration of the current user session as the JS Profiler could not be started.',\n );\n }\n return false;\n }\n\n if (options.profileLifecycle !== 'trace' && options.profileLifecycle !== 'manual') {\n DEBUG_BUILD && debug.warn('[Profiling] Session not sampled. Invalid `profileLifecycle` option.');\n return false;\n }\n\n // Session sampling: profileSessionSampleRate gates whether profiling is enabled for this session\n const profileSessionSampleRate = options.profileSessionSampleRate;\n\n if (!isValidSampleRate(profileSessionSampleRate)) {\n DEBUG_BUILD && debug.warn('[Profiling] Discarding profile because of invalid profileSessionSampleRate.');\n return false;\n }\n\n if (!profileSessionSampleRate) {\n DEBUG_BUILD &&\n debug.log('[Profiling] Discarding profile because profileSessionSampleRate is not defined or set to 0');\n return false;\n }\n\n return Math.random() <= profileSessionSampleRate;\n}\n\nexport function setThreadAttributes(span: Span): void {\n span.setAttribute('thread.id', PROFILER_THREAD_ID_STRING);\n span.setAttribute('thread.name', PROFILER_THREAD_NAME);\n}\n"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"utils.js","sources":["../../../../../src/profiling/utils.ts"],"sourcesContent":["/* eslint-disable max-lines */\nimport type { Client, ContinuousThreadCpuProfile, DebugImage, ProfileChunk, Span } from '@sentry/core';\nimport {\n debug,\n getClient,\n getDebugImagesForResources,\n GLOBAL_OBJ,\n _INTERNAL_performanceTimeToSeconds,\n uuid4,\n} from '@sentry/core';\nimport type { BrowserOptions } from '../client';\nimport { DEBUG_BUILD } from '../debug-build';\nimport { WINDOW } from '../helpers';\nimport type { JSSelfProfile, JSSelfProfiler, JSSelfProfilerConstructor } from './jsSelfProfiling';\n\n// Checking if we are in Main or Worker thread: `self` (not `window`) is the `globalThis` in Web Workers and `importScripts` are only available in Web Workers\nconst isMainThread = 'window' in GLOBAL_OBJ && GLOBAL_OBJ.window === GLOBAL_OBJ && typeof importScripts === 'undefined';\n\n// Setting ID to 0 as we cannot get an ID from Web Workers\nexport const PROFILER_THREAD_ID_STRING = String(0);\nexport const PROFILER_THREAD_NAME = isMainThread ? 'main' : 'worker';\n\n/**\n * Create a profile chunk envelope item\n */\nexport function createProfileChunkPayload(\n jsSelfProfile: JSSelfProfile,\n client: Client,\n profilerId?: string,\n): ProfileChunk {\n // only == to catch null and undefined\n if (jsSelfProfile == null) {\n throw new TypeError(\n `Cannot construct profiling event envelope without a valid profile. Got ${jsSelfProfile} instead.`,\n );\n }\n\n const continuousProfile = convertToContinuousProfile(jsSelfProfile);\n\n const options = client.getOptions();\n const sdk = client.getSdkMetadata?.()?.sdk;\n\n return {\n chunk_id: uuid4(),\n client_sdk: {\n name: sdk?.name ?? 'sentry.javascript.browser',\n version: sdk?.version ?? '0.0.0',\n },\n profiler_id: profilerId || uuid4(),\n platform: 'javascript',\n version: '2',\n release: options.release ?? '',\n environment: options.environment ?? 'production',\n debug_meta: {\n // function name obfuscation\n images: applyDebugMetadata(jsSelfProfile.resources),\n },\n profile: continuousProfile,\n };\n}\n\n/**\n * Validate a profile chunk against the Sample Format V2 requirements.\n * https://develop.sentry.dev/sdk/telemetry/profiles/sample-format-v2/\n * - Presence of samples, stacks, frames\n * - Required metadata fields\n */\nexport function validateProfileChunk(chunk: ProfileChunk): { valid: true } | { reason: string } {\n try {\n // Required metadata\n if (!chunk || typeof chunk !== 'object') {\n return { reason: 'chunk is not an object' };\n }\n\n // profiler_id and chunk_id must be 32 lowercase hex chars\n const isHex32 = (val: unknown): boolean => typeof val === 'string' && /^[a-f0-9]{32}$/.test(val);\n if (!isHex32(chunk.profiler_id)) {\n return { reason: 'missing or invalid profiler_id' };\n }\n if (!isHex32(chunk.chunk_id)) {\n return { reason: 'missing or invalid chunk_id' };\n }\n\n if (!chunk.client_sdk) {\n return { reason: 'missing client_sdk metadata' };\n }\n\n // Profile data must have frames, stacks, samples\n const profile = chunk.profile as { frames?: unknown[]; stacks?: unknown[]; samples?: unknown[] } | undefined;\n if (!profile) {\n return { reason: 'missing profile data' };\n }\n\n if (!Array.isArray(profile.frames) || !profile.frames.length) {\n return { reason: 'profile has no frames' };\n }\n if (!Array.isArray(profile.stacks) || !profile.stacks.length) {\n return { reason: 'profile has no stacks' };\n }\n if (!Array.isArray(profile.samples) || !profile.samples.length) {\n return { reason: 'profile has no samples' };\n }\n\n return { valid: true };\n } catch (e) {\n return { reason: `unknown validation error: ${e}` };\n }\n}\n\n/**\n * Convert from JSSelfProfile format to ContinuousThreadCpuProfile format.\n */\nfunction convertToContinuousProfile(input: {\n frames: { name: string; resourceId?: number; line?: number; column?: number }[];\n stacks: { frameId: number; parentId?: number }[];\n samples: { timestamp: number; stackId?: number }[];\n resources: string[];\n}): ContinuousThreadCpuProfile {\n // Frames map 1:1 by index; fill only when present to avoid sparse writes\n const frames: ContinuousThreadCpuProfile['frames'] = [];\n for (let i = 0; i < input.frames.length; i++) {\n const frame = input.frames[i];\n if (!frame) {\n continue;\n }\n frames[i] = {\n function: frame.name,\n abs_path: typeof frame.resourceId === 'number' ? input.resources[frame.resourceId] : undefined,\n lineno: frame.line,\n colno: frame.column,\n };\n }\n\n // Build stacks by following parent links, top->down order (root last)\n const stacks: ContinuousThreadCpuProfile['stacks'] = [];\n for (let i = 0; i < input.stacks.length; i++) {\n const stackHead = input.stacks[i];\n if (!stackHead) {\n continue;\n }\n const list: number[] = [];\n let current: { frameId: number; parentId?: number } | undefined = stackHead;\n while (current) {\n list.push(current.frameId);\n current = current.parentId === undefined ? undefined : input.stacks[current.parentId];\n }\n stacks[i] = list;\n }\n\n const samples: ContinuousThreadCpuProfile['samples'] = [];\n for (let i = 0; i < input.samples.length; i++) {\n const sample = input.samples[i];\n if (!sample) {\n continue;\n }\n // Sample timestamps are relative to `performance.timeOrigin`, so we convert them like performance entries.\n // The cast is safe: the JS Self-Profiling API only exists if the Performance API does.\n const timestampSeconds = _INTERNAL_performanceTimeToSeconds(sample.timestamp) as number;\n samples[i] = {\n stack_id: sample.stackId ?? 0,\n thread_id: PROFILER_THREAD_ID_STRING,\n timestamp: timestampSeconds,\n };\n }\n\n return {\n frames,\n stacks,\n samples,\n thread_metadata: { [PROFILER_THREAD_ID_STRING]: { name: PROFILER_THREAD_NAME } },\n };\n}\n\n/**\n * Applies debug meta data to an event from a list of paths to resources (sourcemaps)\n */\nexport function applyDebugMetadata(resource_paths: ReadonlyArray<string>): DebugImage[] {\n const client = getClient();\n const options = client?.getOptions();\n const stackParser = options?.stackParser;\n\n if (!stackParser) {\n return [];\n }\n\n return getDebugImagesForResources(stackParser, resource_paths);\n}\n\n/**\n * Checks the given sample rate to make sure it is valid type and value (a boolean, or a number between 0 and 1).\n */\nexport function isValidSampleRate(rate: unknown): boolean {\n // we need to check NaN explicitly because it's of type 'number' and therefore wouldn't get caught by this typecheck\n if ((typeof rate !== 'number' && typeof rate !== 'boolean') || (typeof rate === 'number' && isNaN(rate))) {\n DEBUG_BUILD &&\n debug.warn(\n `[Profiling] Invalid sample rate. Sample rate must be a boolean or a number between 0 and 1. Got ${JSON.stringify(\n rate,\n )} of type ${JSON.stringify(typeof rate)}.`,\n );\n return false;\n }\n\n // Boolean sample rates are always valid\n if (rate === true || rate === false) {\n return true;\n }\n\n // in case sampleRate is a boolean, it will get automatically cast to 1 if it's true and 0 if it's false\n if (rate < 0 || rate > 1) {\n DEBUG_BUILD && debug.warn(`[Profiling] Invalid sample rate. Sample rate must be between 0 and 1. Got ${rate}.`);\n return false;\n }\n return true;\n}\n\n// Keep a flag value to avoid re-initializing the profiler constructor. If it fails\n// once, it will always fail and this allows us to early return.\nlet PROFILING_CONSTRUCTOR_FAILED: boolean = false;\nexport const MAX_PROFILE_DURATION_MS = 30_000;\n\n/**\n * Check if profiler constructor is available.\n * @param maybeProfiler\n */\nfunction isJSProfilerSupported(maybeProfiler: unknown): maybeProfiler is typeof JSSelfProfilerConstructor {\n return typeof maybeProfiler === 'function';\n}\n\n/**\n * Starts the profiler and returns the profiler instance.\n */\nexport function startJSSelfProfile(): JSSelfProfiler | undefined {\n // Feature support check first\n const JSProfilerConstructor = WINDOW.Profiler;\n\n if (!isJSProfilerSupported(JSProfilerConstructor)) {\n if (DEBUG_BUILD) {\n debug.log('[Profiling] Profiling is not supported by this browser, Profiler interface missing on window object.');\n }\n return;\n }\n\n // From initial testing, it seems that the minimum value for sampleInterval is 10ms.\n const samplingIntervalMS = 10;\n // Start the profiler\n const maxSamples = Math.floor(MAX_PROFILE_DURATION_MS / samplingIntervalMS);\n\n // Attempt to initialize the profiler constructor, if it fails, we disable profiling for the current user session.\n // This is likely due to a missing 'Document-Policy': 'js-profiling' header. We do not want to throw an error if this happens\n // as we risk breaking the user's application, so just disable profiling and log an error.\n try {\n return new JSProfilerConstructor({ sampleInterval: samplingIntervalMS, maxBufferSize: maxSamples });\n } catch {\n if (DEBUG_BUILD) {\n debug.log(\n \"[Profiling] Failed to initialize the Profiling constructor, this is likely due to a missing 'Document-Policy': 'js-profiling' header.\",\n );\n debug.log('[Profiling] Disabling profiling for current user session.');\n }\n PROFILING_CONSTRUCTOR_FAILED = true;\n }\n\n return;\n}\n\n/**\n * Determine if a profile should be created for the current session.\n */\nexport function shouldProfileSession(options: BrowserOptions): boolean {\n // If constructor failed once, it will always fail, so we can early return.\n if (PROFILING_CONSTRUCTOR_FAILED) {\n if (DEBUG_BUILD) {\n debug.log(\n '[Profiling] Profiling has been disabled for the duration of the current user session as the JS Profiler could not be started.',\n );\n }\n return false;\n }\n\n if (options.profileLifecycle !== 'trace' && options.profileLifecycle !== 'manual') {\n DEBUG_BUILD && debug.warn('[Profiling] Session not sampled. Invalid `profileLifecycle` option.');\n return false;\n }\n\n // Session sampling: profileSessionSampleRate gates whether profiling is enabled for this session\n const profileSessionSampleRate = options.profileSessionSampleRate;\n\n if (!isValidSampleRate(profileSessionSampleRate)) {\n DEBUG_BUILD && debug.warn('[Profiling] Discarding profile because of invalid profileSessionSampleRate.');\n return false;\n }\n\n if (!profileSessionSampleRate) {\n DEBUG_BUILD &&\n debug.log('[Profiling] Discarding profile because profileSessionSampleRate is not defined or set to 0');\n return false;\n }\n\n return Math.random() <= profileSessionSampleRate;\n}\n\nexport function setThreadAttributes(span: Span): void {\n span.setAttribute('thread.id', PROFILER_THREAD_ID_STRING);\n span.setAttribute('thread.name', PROFILER_THREAD_NAME);\n}\n"],"names":[],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../../../src/client.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,aAAa,EACb,KAAK,EACL,SAAS,EACT,OAAO,IAAI,WAAW,EACtB,mBAAmB,EACnB,KAAK,EACL,aAAa,EACd,MAAM,cAAc,CAAC;AACtB,OAAO,KAAK,EAAE,0BAA0B,EAAE,MAAM,sBAAsB,CAAC;AACvE,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,uBAAuB,CAAC;AAE3D,OAAO,EAA+C,MAAM,
|
|
1
|
+
{"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../../../src/client.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,aAAa,EACb,KAAK,EACL,SAAS,EACT,OAAO,IAAI,WAAW,EACtB,mBAAmB,EACnB,KAAK,EACL,aAAa,EACd,MAAM,cAAc,CAAC;AACtB,OAAO,KAAK,EAAE,0BAA0B,EAAE,MAAM,sBAAsB,CAAC;AACvE,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,uBAAuB,CAAC;AAE3D,OAAO,EAA+C,MAAM,EAAoC,MAAM,cAAc,CAAC;AAGrH,OAAO,KAAK,EAAE,uBAAuB,EAAE,MAAM,oBAAoB,CAAC;AAOlE,MAAM,MAAM,6BAA6B,GAAG;IAC1C;;;;;;OAMG;IACH,wBAAwB,CAAC,EAAE,MAAM,CAAC;IAElC;;;;;;;;;OASG;IACH,gBAAgB,CAAC,EAAE,QAAQ,GAAG,OAAO,CAAC;CACvC,CAAC;AAEF,KAAK,sBAAsB,GAAG,0BAA0B,GACtD,6BAA6B,GAAG;IAC9B,qFAAqF;IACrF,UAAU,CAAC,EAAE,MAAM,CAAC;IAEpB;;;;;;;;;;;;;;;;OAgBG;IACH,yBAAyB,CAAC,EAAE,OAAO,CAAC;IAEpC;;;;;;;;;OASG;IACH,SAAS,CAAC,EAAE,OAAO,GAAG,MAAM,CAAC;IAE7B;;;;;OAKG;IACH,aAAa,CAAC,EAAE,aAAa,CAAC;CAC/B,CAAC;AACJ;;;GAGG;AACH,MAAM,MAAM,cAAc,GAAG,WAAW,CAAC,uBAAuB,CAAC,GAAG,sBAAsB,CAAC;AAE3F;;;GAGG;AACH,MAAM,MAAM,oBAAoB,GAAG,aAAa,CAAC,uBAAuB,CAAC,GAAG,sBAAsB,CAAC;AAEnG;;;;;GAKG;AACH,qBAAa,aAAc,SAAQ,MAAM,CAAC,oBAAoB,CAAC;IAC7D;;;;OAIG;IACH,YAAmB,OAAO,EAAE,oBAAoB,EAiF/C;IAED;;OAEG;IACI,kBAAkB,CAAC,SAAS,EAAE,OAAO,EAAE,IAAI,CAAC,EAAE,SAAS,GAAG,WAAW,CAAC,KAAK,CAAC,CAElF;IAED;;OAEG;IACI,gBAAgB,CACrB,OAAO,EAAE,mBAAmB,EAC5B,KAAK,GAAE,aAAsB,EAC7B,IAAI,CAAC,EAAE,SAAS,GACf,WAAW,CAAC,KAAK,CAAC,CAEpB;IAED;;OAEG;IACH,SAAS,CAAC,aAAa,CACrB,KAAK,EAAE,KAAK,EACZ,IAAI,EAAE,SAAS,EACf,YAAY,EAAE,KAAK,EACnB,cAAc,EAAE,KAAK,GACpB,WAAW,CAAC,KAAK,GAAG,IAAI,CAAC,CAI3B;CACF;AAED,+BAA+B;AAC/B,wBAAgB,mBAAmB,CAAC,CAAC,SAAS,OAAO,CAAC,oBAAoB,CAAC,EAAE,UAAU,EAAE,CAAC,GAAG,CAAC,CAW7F"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"utils.d.ts","sourceRoot":"","sources":["../../../../src/profiling/utils.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,MAAM,EAA8B,UAAU,EAAE,YAAY,EAAE,IAAI,EAAE,MAAM,cAAc,CAAC;AASvG,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,WAAW,CAAC;AAGhD,OAAO,KAAK,EAAE,aAAa,EAAE,cAAc,EAA6B,MAAM,mBAAmB,CAAC;AAMlG,eAAO,MAAM,yBAAyB,QAAY,CAAC;AACnD,eAAO,MAAM,oBAAoB,QAAmC,CAAC;AAErE;;GAEG;AACH,wBAAgB,yBAAyB,CACvC,aAAa,EAAE,aAAa,EAC5B,MAAM,EAAE,MAAM,EACd,UAAU,CAAC,EAAE,MAAM,GAClB,YAAY,CA8Bd;AAED;;;;;GAKG;AACH,wBAAgB,oBAAoB,CAAC,KAAK,EAAE,YAAY,GAAG;IAAE,KAAK,EAAE,IAAI,CAAA;CAAE,GAAG;IAAE,MAAM,EAAE,MAAM,CAAA;CAAE,CAwC9F;
|
|
1
|
+
{"version":3,"file":"utils.d.ts","sourceRoot":"","sources":["../../../../src/profiling/utils.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,MAAM,EAA8B,UAAU,EAAE,YAAY,EAAE,IAAI,EAAE,MAAM,cAAc,CAAC;AASvG,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,WAAW,CAAC;AAGhD,OAAO,KAAK,EAAE,aAAa,EAAE,cAAc,EAA6B,MAAM,mBAAmB,CAAC;AAMlG,eAAO,MAAM,yBAAyB,QAAY,CAAC;AACnD,eAAO,MAAM,oBAAoB,QAAmC,CAAC;AAErE;;GAEG;AACH,wBAAgB,yBAAyB,CACvC,aAAa,EAAE,aAAa,EAC5B,MAAM,EAAE,MAAM,EACd,UAAU,CAAC,EAAE,MAAM,GAClB,YAAY,CA8Bd;AAED;;;;;GAKG;AACH,wBAAgB,oBAAoB,CAAC,KAAK,EAAE,YAAY,GAAG;IAAE,KAAK,EAAE,IAAI,CAAA;CAAE,GAAG;IAAE,MAAM,EAAE,MAAM,CAAA;CAAE,CAwC9F;AAkED;;GAEG;AACH,wBAAgB,kBAAkB,CAAC,cAAc,EAAE,aAAa,CAAC,MAAM,CAAC,GAAG,UAAU,EAAE,CAUtF;AAED;;GAEG;AACH,wBAAgB,iBAAiB,CAAC,IAAI,EAAE,OAAO,GAAG,OAAO,CAuBxD;AAKD,eAAO,MAAM,uBAAuB,QAAS,CAAC;AAU9C;;GAEG;AACH,wBAAgB,kBAAkB,IAAI,cAAc,GAAG,SAAS,CAgC/D;AAED;;GAEG;AACH,wBAAgB,oBAAoB,CAAC,OAAO,EAAE,cAAc,GAAG,OAAO,CA+BrE;AAED,wBAAgB,mBAAmB,CAAC,IAAI,EAAE,IAAI,GAAG,IAAI,CAGpD"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@sentry/browser",
|
|
3
|
-
"version": "11.
|
|
3
|
+
"version": "11.6.0",
|
|
4
4
|
"description": "Official Sentry SDK for browsers",
|
|
5
5
|
"repository": "git://github.com/getsentry/sentry-javascript.git",
|
|
6
6
|
"homepage": "https://github.com/getsentry/sentry-javascript/tree/master/packages/browser",
|
|
@@ -38,15 +38,15 @@
|
|
|
38
38
|
"access": "public"
|
|
39
39
|
},
|
|
40
40
|
"dependencies": {
|
|
41
|
-
"@sentry/browser-utils": "11.
|
|
41
|
+
"@sentry/browser-utils": "11.6.0",
|
|
42
42
|
"@sentry/conventions": "^0.26.0",
|
|
43
|
-
"@sentry/feedback": "11.
|
|
44
|
-
"@sentry/replay": "11.
|
|
45
|
-
"@sentry/replay-canvas": "11.
|
|
46
|
-
"@sentry/core": "11.
|
|
43
|
+
"@sentry/feedback": "11.6.0",
|
|
44
|
+
"@sentry/replay": "11.6.0",
|
|
45
|
+
"@sentry/replay-canvas": "11.6.0",
|
|
46
|
+
"@sentry/core": "11.6.0"
|
|
47
47
|
},
|
|
48
48
|
"devDependencies": {
|
|
49
|
-
"@sentry-internal/integration-shims": "11.
|
|
49
|
+
"@sentry-internal/integration-shims": "11.6.0",
|
|
50
50
|
"fake-indexeddb": "^6.2.5"
|
|
51
51
|
},
|
|
52
52
|
"scripts": {
|