@sentry/browser 9.0.0 → 9.1.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/index.js +2 -0
- package/build/npm/cjs/index.js.map +1 -1
- package/build/npm/cjs/integrations/breadcrumbs.js +40 -35
- package/build/npm/cjs/integrations/breadcrumbs.js.map +1 -1
- package/build/npm/cjs/integrations/featureFlags/unleash/integration.js +2 -3
- package/build/npm/cjs/integrations/featureFlags/unleash/integration.js.map +1 -1
- package/build/npm/cjs/integrations/graphqlClient.js +180 -0
- package/build/npm/cjs/integrations/graphqlClient.js.map +1 -0
- package/build/npm/cjs/tracing/request.js +5 -0
- package/build/npm/cjs/tracing/request.js.map +1 -1
- package/build/npm/cjs/utils/lazyLoadIntegration.js +1 -0
- package/build/npm/cjs/utils/lazyLoadIntegration.js.map +1 -1
- package/build/npm/esm/index.js +1 -0
- package/build/npm/esm/index.js.map +1 -1
- package/build/npm/esm/integrations/breadcrumbs.js +40 -35
- package/build/npm/esm/integrations/breadcrumbs.js.map +1 -1
- package/build/npm/esm/integrations/featureFlags/unleash/integration.js +2 -3
- package/build/npm/esm/integrations/featureFlags/unleash/integration.js.map +1 -1
- package/build/npm/esm/integrations/graphqlClient.js +175 -0
- package/build/npm/esm/integrations/graphqlClient.js.map +1 -0
- package/build/npm/esm/package.json +1 -1
- package/build/npm/esm/tracing/request.js +6 -1
- package/build/npm/esm/tracing/request.js.map +1 -1
- package/build/npm/esm/utils/lazyLoadIntegration.js +1 -0
- package/build/npm/esm/utils/lazyLoadIntegration.js.map +1 -1
- package/build/npm/types/index.d.ts +1 -0
- package/build/npm/types/index.d.ts.map +1 -1
- package/build/npm/types/integrations/breadcrumbs.d.ts.map +1 -1
- package/build/npm/types/integrations/featureFlags/unleash/integration.d.ts +6 -5
- package/build/npm/types/integrations/featureFlags/unleash/integration.d.ts.map +1 -1
- package/build/npm/types/integrations/graphqlClient.d.ts +41 -0
- package/build/npm/types/integrations/graphqlClient.d.ts.map +1 -0
- package/build/npm/types/integrations-bundle/index.graphqlclient.d.ts +2 -0
- package/build/npm/types/integrations-bundle/index.graphqlclient.d.ts.map +1 -0
- package/build/npm/types/tracing/request.d.ts.map +1 -1
- package/build/npm/types/utils/lazyLoadIntegration.d.ts +1 -0
- package/build/npm/types/utils/lazyLoadIntegration.d.ts.map +1 -1
- package/build/npm/types-ts3.8/index.d.ts +1 -0
- package/build/npm/types-ts3.8/integrations/featureFlags/unleash/integration.d.ts +6 -5
- package/build/npm/types-ts3.8/integrations/graphqlClient.d.ts +41 -0
- package/build/npm/types-ts3.8/integrations-bundle/index.graphqlclient.d.ts +2 -0
- package/build/npm/types-ts3.8/utils/lazyLoadIntegration.d.ts +1 -0
- package/package.json +7 -7
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"lazyLoadIntegration.js","sources":["../../../../src/utils/lazyLoadIntegration.ts"],"sourcesContent":["import { SDK_VERSION, getClient } from '@sentry/core';\nimport type { IntegrationFn } from '@sentry/core';\nimport type { BrowserClient } from '../client';\nimport { WINDOW } from '../helpers';\n\n// This is a map of integration function method to bundle file name.\nconst LazyLoadableIntegrations = {\n replayIntegration: 'replay',\n replayCanvasIntegration: 'replay-canvas',\n feedbackIntegration: 'feedback',\n feedbackModalIntegration: 'feedback-modal',\n feedbackScreenshotIntegration: 'feedback-screenshot',\n captureConsoleIntegration: 'captureconsole',\n contextLinesIntegration: 'contextlines',\n linkedErrorsIntegration: 'linkederrors',\n dedupeIntegration: 'dedupe',\n extraErrorDataIntegration: 'extraerrordata',\n httpClientIntegration: 'httpclient',\n reportingObserverIntegration: 'reportingobserver',\n rewriteFramesIntegration: 'rewriteframes',\n browserProfilingIntegration: 'browserprofiling',\n moduleMetadataIntegration: 'modulemetadata',\n} as const;\n\nconst WindowWithMaybeIntegration = WINDOW as {\n Sentry?: Partial<Record<keyof typeof LazyLoadableIntegrations, IntegrationFn>>;\n};\n\n/**\n * Lazy load an integration from the CDN.\n * Rejects if the integration cannot be loaded.\n */\nexport async function lazyLoadIntegration(\n name: keyof typeof LazyLoadableIntegrations,\n scriptNonce?: string,\n): Promise<IntegrationFn> {\n const bundle = LazyLoadableIntegrations[name];\n\n // `window.Sentry` is only set when using a CDN bundle, but this method can also be used via the NPM package\n const sentryOnWindow = (WindowWithMaybeIntegration.Sentry = WindowWithMaybeIntegration.Sentry || {});\n\n if (!bundle) {\n throw new Error(`Cannot lazy load integration: ${name}`);\n }\n\n // Bail if the integration already exists\n const existing = sentryOnWindow[name];\n // The `feedbackIntegration` is loaded by default in the CDN bundles,\n // so we need to differentiate between the real integration and the shim.\n // if only the shim exists, we still want to lazy load the real integration.\n if (typeof existing === 'function' && !('_isShim' in existing)) {\n return existing;\n }\n\n const url = getScriptURL(bundle);\n const script = WINDOW.document.createElement('script');\n script.src = url;\n script.crossOrigin = 'anonymous';\n script.referrerPolicy = 'strict-origin';\n\n if (scriptNonce) {\n script.setAttribute('nonce', scriptNonce);\n }\n\n const waitForLoad = new Promise<void>((resolve, reject) => {\n script.addEventListener('load', () => resolve());\n script.addEventListener('error', reject);\n });\n\n const currentScript = WINDOW.document.currentScript;\n const parent = WINDOW.document.body || WINDOW.document.head || currentScript?.parentElement;\n\n if (parent) {\n parent.appendChild(script);\n } else {\n throw new Error(`Could not find parent element to insert lazy-loaded ${name} script`);\n }\n\n try {\n await waitForLoad;\n } catch {\n throw new Error(`Error when loading integration: ${name}`);\n }\n\n const integrationFn = sentryOnWindow[name];\n\n if (typeof integrationFn !== 'function') {\n throw new Error(`Could not load integration: ${name}`);\n }\n\n return integrationFn;\n}\n\nfunction getScriptURL(bundle: string): string {\n const client = getClient<BrowserClient>();\n const baseURL = client?.getOptions()?.cdnBaseUrl || 'https://browser.sentry-cdn.com';\n\n return new URL(`/${SDK_VERSION}/${bundle}.min.js`, baseURL).toString();\n}\n"],"names":["WINDOW","getClient","SDK_VERSION"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"lazyLoadIntegration.js","sources":["../../../../src/utils/lazyLoadIntegration.ts"],"sourcesContent":["import { SDK_VERSION, getClient } from '@sentry/core';\nimport type { IntegrationFn } from '@sentry/core';\nimport type { BrowserClient } from '../client';\nimport { WINDOW } from '../helpers';\n\n// This is a map of integration function method to bundle file name.\nconst LazyLoadableIntegrations = {\n replayIntegration: 'replay',\n replayCanvasIntegration: 'replay-canvas',\n feedbackIntegration: 'feedback',\n feedbackModalIntegration: 'feedback-modal',\n feedbackScreenshotIntegration: 'feedback-screenshot',\n captureConsoleIntegration: 'captureconsole',\n contextLinesIntegration: 'contextlines',\n linkedErrorsIntegration: 'linkederrors',\n dedupeIntegration: 'dedupe',\n extraErrorDataIntegration: 'extraerrordata',\n graphqlClientIntegration: 'graphqlclient',\n httpClientIntegration: 'httpclient',\n reportingObserverIntegration: 'reportingobserver',\n rewriteFramesIntegration: 'rewriteframes',\n browserProfilingIntegration: 'browserprofiling',\n moduleMetadataIntegration: 'modulemetadata',\n} as const;\n\nconst WindowWithMaybeIntegration = WINDOW as {\n Sentry?: Partial<Record<keyof typeof LazyLoadableIntegrations, IntegrationFn>>;\n};\n\n/**\n * Lazy load an integration from the CDN.\n * Rejects if the integration cannot be loaded.\n */\nexport async function lazyLoadIntegration(\n name: keyof typeof LazyLoadableIntegrations,\n scriptNonce?: string,\n): Promise<IntegrationFn> {\n const bundle = LazyLoadableIntegrations[name];\n\n // `window.Sentry` is only set when using a CDN bundle, but this method can also be used via the NPM package\n const sentryOnWindow = (WindowWithMaybeIntegration.Sentry = WindowWithMaybeIntegration.Sentry || {});\n\n if (!bundle) {\n throw new Error(`Cannot lazy load integration: ${name}`);\n }\n\n // Bail if the integration already exists\n const existing = sentryOnWindow[name];\n // The `feedbackIntegration` is loaded by default in the CDN bundles,\n // so we need to differentiate between the real integration and the shim.\n // if only the shim exists, we still want to lazy load the real integration.\n if (typeof existing === 'function' && !('_isShim' in existing)) {\n return existing;\n }\n\n const url = getScriptURL(bundle);\n const script = WINDOW.document.createElement('script');\n script.src = url;\n script.crossOrigin = 'anonymous';\n script.referrerPolicy = 'strict-origin';\n\n if (scriptNonce) {\n script.setAttribute('nonce', scriptNonce);\n }\n\n const waitForLoad = new Promise<void>((resolve, reject) => {\n script.addEventListener('load', () => resolve());\n script.addEventListener('error', reject);\n });\n\n const currentScript = WINDOW.document.currentScript;\n const parent = WINDOW.document.body || WINDOW.document.head || currentScript?.parentElement;\n\n if (parent) {\n parent.appendChild(script);\n } else {\n throw new Error(`Could not find parent element to insert lazy-loaded ${name} script`);\n }\n\n try {\n await waitForLoad;\n } catch {\n throw new Error(`Error when loading integration: ${name}`);\n }\n\n const integrationFn = sentryOnWindow[name];\n\n if (typeof integrationFn !== 'function') {\n throw new Error(`Could not load integration: ${name}`);\n }\n\n return integrationFn;\n}\n\nfunction getScriptURL(bundle: string): string {\n const client = getClient<BrowserClient>();\n const baseURL = client?.getOptions()?.cdnBaseUrl || 'https://browser.sentry-cdn.com';\n\n return new URL(`/${SDK_VERSION}/${bundle}.min.js`, baseURL).toString();\n}\n"],"names":["WINDOW","getClient","SDK_VERSION"],"mappings":"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"}
|
package/build/npm/esm/index.js
CHANGED
|
@@ -15,6 +15,7 @@ export { lazyLoadIntegration } from './utils/lazyLoadIntegration.js';
|
|
|
15
15
|
export { reportingObserverIntegration } from './integrations/reportingobserver.js';
|
|
16
16
|
export { httpClientIntegration } from './integrations/httpclient.js';
|
|
17
17
|
export { contextLinesIntegration } from './integrations/contextlines.js';
|
|
18
|
+
export { graphqlClientIntegration } from './integrations/graphqlClient.js';
|
|
18
19
|
export { getReplay, replayIntegration } from '@sentry-internal/replay';
|
|
19
20
|
export { replayCanvasIntegration } from '@sentry-internal/replay-canvas';
|
|
20
21
|
export { feedbackAsyncIntegration } from './feedbackAsync.js';
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sources":[],"sourcesContent":[],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"index.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;"}
|
|
@@ -204,17 +204,16 @@ function _getXhrBreadcrumbHandler(client) {
|
|
|
204
204
|
endTimestamp,
|
|
205
205
|
};
|
|
206
206
|
|
|
207
|
-
const
|
|
207
|
+
const breadcrumb = {
|
|
208
|
+
category: 'xhr',
|
|
209
|
+
data,
|
|
210
|
+
type: 'http',
|
|
211
|
+
level: getBreadcrumbLogLevelFromHttpStatusCode(status_code),
|
|
212
|
+
};
|
|
208
213
|
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
data,
|
|
213
|
-
type: 'http',
|
|
214
|
-
level,
|
|
215
|
-
},
|
|
216
|
-
hint,
|
|
217
|
-
);
|
|
214
|
+
client.emit('beforeOutgoingRequestBreadcrumb', breadcrumb, hint );
|
|
215
|
+
|
|
216
|
+
addBreadcrumb(breadcrumb, hint);
|
|
218
217
|
};
|
|
219
218
|
}
|
|
220
219
|
|
|
@@ -239,12 +238,13 @@ function _getFetchBreadcrumbHandler(client) {
|
|
|
239
238
|
return;
|
|
240
239
|
}
|
|
241
240
|
|
|
242
|
-
|
|
241
|
+
({
|
|
243
242
|
method: handlerData.fetchData.method,
|
|
244
243
|
url: handlerData.fetchData.url,
|
|
245
|
-
};
|
|
244
|
+
});
|
|
246
245
|
|
|
247
246
|
if (handlerData.error) {
|
|
247
|
+
const data = handlerData.fetchData;
|
|
248
248
|
const hint = {
|
|
249
249
|
data: handlerData.error,
|
|
250
250
|
input: handlerData.args,
|
|
@@ -252,21 +252,26 @@ function _getFetchBreadcrumbHandler(client) {
|
|
|
252
252
|
endTimestamp,
|
|
253
253
|
};
|
|
254
254
|
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
255
|
+
const breadcrumb = {
|
|
256
|
+
category: 'fetch',
|
|
257
|
+
data,
|
|
258
|
+
level: 'error',
|
|
259
|
+
type: 'http',
|
|
260
|
+
} ;
|
|
261
|
+
|
|
262
|
+
client.emit('beforeOutgoingRequestBreadcrumb', breadcrumb, hint );
|
|
263
|
+
|
|
264
|
+
addBreadcrumb(breadcrumb, hint);
|
|
264
265
|
} else {
|
|
265
266
|
const response = handlerData.response ;
|
|
267
|
+
const data = {
|
|
268
|
+
...handlerData.fetchData,
|
|
269
|
+
status_code: response?.status,
|
|
270
|
+
};
|
|
266
271
|
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
272
|
+
handlerData.fetchData.request_body_size;
|
|
273
|
+
handlerData.fetchData.response_body_size;
|
|
274
|
+
response?.status;
|
|
270
275
|
|
|
271
276
|
const hint = {
|
|
272
277
|
input: handlerData.args,
|
|
@@ -274,17 +279,17 @@ function _getFetchBreadcrumbHandler(client) {
|
|
|
274
279
|
startTimestamp,
|
|
275
280
|
endTimestamp,
|
|
276
281
|
};
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
);
|
|
282
|
+
|
|
283
|
+
const breadcrumb = {
|
|
284
|
+
category: 'fetch',
|
|
285
|
+
data,
|
|
286
|
+
type: 'http',
|
|
287
|
+
level: getBreadcrumbLogLevelFromHttpStatusCode(data.status_code),
|
|
288
|
+
};
|
|
289
|
+
|
|
290
|
+
client.emit('beforeOutgoingRequestBreadcrumb', breadcrumb, hint );
|
|
291
|
+
|
|
292
|
+
addBreadcrumb(breadcrumb, hint);
|
|
288
293
|
}
|
|
289
294
|
};
|
|
290
295
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"breadcrumbs.js","sources":["../../../../src/integrations/breadcrumbs.ts"],"sourcesContent":["/* eslint-disable max-lines */\n\nimport {\n SENTRY_XHR_DATA_KEY,\n addClickKeypressInstrumentationHandler,\n addHistoryInstrumentationHandler,\n addXhrInstrumentationHandler,\n} from '@sentry-internal/browser-utils';\nimport type {\n Breadcrumb,\n Client,\n Event as SentryEvent,\n FetchBreadcrumbData,\n FetchBreadcrumbHint,\n HandlerDataConsole,\n HandlerDataDom,\n HandlerDataFetch,\n HandlerDataHistory,\n HandlerDataXhr,\n IntegrationFn,\n XhrBreadcrumbData,\n XhrBreadcrumbHint,\n} from '@sentry/core';\nimport {\n addBreadcrumb,\n addConsoleInstrumentationHandler,\n addFetchInstrumentationHandler,\n defineIntegration,\n getBreadcrumbLogLevelFromHttpStatusCode,\n getClient,\n getComponentName,\n getEventDescription,\n htmlTreeAsString,\n logger,\n parseUrl,\n safeJoin,\n severityLevelFromString,\n} from '@sentry/core';\nimport { DEBUG_BUILD } from '../debug-build';\nimport { WINDOW } from '../helpers';\n\ninterface BreadcrumbsOptions {\n console: boolean;\n dom:\n | boolean\n | {\n serializeAttribute?: string | string[];\n maxStringLength?: number;\n };\n fetch: boolean;\n history: boolean;\n sentry: boolean;\n xhr: boolean;\n}\n\n/** maxStringLength gets capped to prevent 100 breadcrumbs exceeding 1MB event payload size */\nconst MAX_ALLOWED_STRING_LENGTH = 1024;\n\nconst INTEGRATION_NAME = 'Breadcrumbs';\n\nconst _breadcrumbsIntegration = ((options: Partial<BreadcrumbsOptions> = {}) => {\n const _options = {\n console: true,\n dom: true,\n fetch: true,\n history: true,\n sentry: true,\n xhr: true,\n ...options,\n };\n\n return {\n name: INTEGRATION_NAME,\n setup(client) {\n if (_options.console) {\n addConsoleInstrumentationHandler(_getConsoleBreadcrumbHandler(client));\n }\n if (_options.dom) {\n addClickKeypressInstrumentationHandler(_getDomBreadcrumbHandler(client, _options.dom));\n }\n if (_options.xhr) {\n addXhrInstrumentationHandler(_getXhrBreadcrumbHandler(client));\n }\n if (_options.fetch) {\n addFetchInstrumentationHandler(_getFetchBreadcrumbHandler(client));\n }\n if (_options.history) {\n addHistoryInstrumentationHandler(_getHistoryBreadcrumbHandler(client));\n }\n if (_options.sentry) {\n client.on('beforeSendEvent', _getSentryBreadcrumbHandler(client));\n }\n },\n };\n}) satisfies IntegrationFn;\n\nexport const breadcrumbsIntegration = defineIntegration(_breadcrumbsIntegration);\n\n/**\n * Adds a breadcrumb for Sentry events or transactions if this option is enabled.\n */\nfunction _getSentryBreadcrumbHandler(client: Client): (event: SentryEvent) => void {\n return function addSentryBreadcrumb(event: SentryEvent): void {\n if (getClient() !== client) {\n return;\n }\n\n addBreadcrumb(\n {\n category: `sentry.${event.type === 'transaction' ? 'transaction' : 'event'}`,\n event_id: event.event_id,\n level: event.level,\n message: getEventDescription(event),\n },\n {\n event,\n },\n );\n };\n}\n\n/**\n * A HOC that creates a function that creates breadcrumbs from DOM API calls.\n * This is a HOC so that we get access to dom options in the closure.\n */\nfunction _getDomBreadcrumbHandler(\n client: Client,\n dom: BreadcrumbsOptions['dom'],\n): (handlerData: HandlerDataDom) => void {\n return function _innerDomBreadcrumb(handlerData: HandlerDataDom): void {\n if (getClient() !== client) {\n return;\n }\n\n let target;\n let componentName;\n let keyAttrs = typeof dom === 'object' ? dom.serializeAttribute : undefined;\n\n let maxStringLength =\n typeof dom === 'object' && typeof dom.maxStringLength === 'number' ? dom.maxStringLength : undefined;\n if (maxStringLength && maxStringLength > MAX_ALLOWED_STRING_LENGTH) {\n DEBUG_BUILD &&\n logger.warn(\n `\\`dom.maxStringLength\\` cannot exceed ${MAX_ALLOWED_STRING_LENGTH}, but a value of ${maxStringLength} was configured. Sentry will use ${MAX_ALLOWED_STRING_LENGTH} instead.`,\n );\n maxStringLength = MAX_ALLOWED_STRING_LENGTH;\n }\n\n if (typeof keyAttrs === 'string') {\n keyAttrs = [keyAttrs];\n }\n\n // Accessing event.target can throw (see getsentry/raven-js#838, #768)\n try {\n const event = handlerData.event as Event | Node;\n const element = _isEvent(event) ? event.target : event;\n\n target = htmlTreeAsString(element, { keyAttrs, maxStringLength });\n componentName = getComponentName(element);\n } catch (e) {\n target = '<unknown>';\n }\n\n if (target.length === 0) {\n return;\n }\n\n const breadcrumb: Breadcrumb = {\n category: `ui.${handlerData.name}`,\n message: target,\n };\n\n if (componentName) {\n breadcrumb.data = { 'ui.component_name': componentName };\n }\n\n addBreadcrumb(breadcrumb, {\n event: handlerData.event,\n name: handlerData.name,\n global: handlerData.global,\n });\n };\n}\n\n/**\n * Creates breadcrumbs from console API calls\n */\nfunction _getConsoleBreadcrumbHandler(client: Client): (handlerData: HandlerDataConsole) => void {\n return function _consoleBreadcrumb(handlerData: HandlerDataConsole): void {\n if (getClient() !== client) {\n return;\n }\n\n const breadcrumb = {\n category: 'console',\n data: {\n arguments: handlerData.args,\n logger: 'console',\n },\n level: severityLevelFromString(handlerData.level),\n message: safeJoin(handlerData.args, ' '),\n };\n\n if (handlerData.level === 'assert') {\n if (handlerData.args[0] === false) {\n breadcrumb.message = `Assertion failed: ${safeJoin(handlerData.args.slice(1), ' ') || 'console.assert'}`;\n breadcrumb.data.arguments = handlerData.args.slice(1);\n } else {\n // Don't capture a breadcrumb for passed assertions\n return;\n }\n }\n\n addBreadcrumb(breadcrumb, {\n input: handlerData.args,\n level: handlerData.level,\n });\n };\n}\n\n/**\n * Creates breadcrumbs from XHR API calls\n */\nfunction _getXhrBreadcrumbHandler(client: Client): (handlerData: HandlerDataXhr) => void {\n return function _xhrBreadcrumb(handlerData: HandlerDataXhr): void {\n if (getClient() !== client) {\n return;\n }\n\n const { startTimestamp, endTimestamp } = handlerData;\n\n const sentryXhrData = handlerData.xhr[SENTRY_XHR_DATA_KEY];\n\n // We only capture complete, non-sentry requests\n if (!startTimestamp || !endTimestamp || !sentryXhrData) {\n return;\n }\n\n const { method, url, status_code, body } = sentryXhrData;\n\n const data: XhrBreadcrumbData = {\n method,\n url,\n status_code,\n };\n\n const hint: XhrBreadcrumbHint = {\n xhr: handlerData.xhr,\n input: body,\n startTimestamp,\n endTimestamp,\n };\n\n const level = getBreadcrumbLogLevelFromHttpStatusCode(status_code);\n\n addBreadcrumb(\n {\n category: 'xhr',\n data,\n type: 'http',\n level,\n },\n hint,\n );\n };\n}\n\n/**\n * Creates breadcrumbs from fetch API calls\n */\nfunction _getFetchBreadcrumbHandler(client: Client): (handlerData: HandlerDataFetch) => void {\n return function _fetchBreadcrumb(handlerData: HandlerDataFetch): void {\n if (getClient() !== client) {\n return;\n }\n\n const { startTimestamp, endTimestamp } = handlerData;\n\n // We only capture complete fetch requests\n if (!endTimestamp) {\n return;\n }\n\n if (handlerData.fetchData.url.match(/sentry_key/) && handlerData.fetchData.method === 'POST') {\n // We will not create breadcrumbs for fetch requests that contain `sentry_key` (internal sentry requests)\n return;\n }\n\n const breadcrumbData: FetchBreadcrumbData = {\n method: handlerData.fetchData.method,\n url: handlerData.fetchData.url,\n };\n\n if (handlerData.error) {\n const hint: FetchBreadcrumbHint = {\n data: handlerData.error,\n input: handlerData.args,\n startTimestamp,\n endTimestamp,\n };\n\n addBreadcrumb(\n {\n category: 'fetch',\n data: breadcrumbData,\n level: 'error',\n type: 'http',\n },\n hint,\n );\n } else {\n const response = handlerData.response as Response | undefined;\n\n breadcrumbData.request_body_size = handlerData.fetchData.request_body_size;\n breadcrumbData.response_body_size = handlerData.fetchData.response_body_size;\n breadcrumbData.status_code = response?.status;\n\n const hint: FetchBreadcrumbHint = {\n input: handlerData.args,\n response,\n startTimestamp,\n endTimestamp,\n };\n const level = getBreadcrumbLogLevelFromHttpStatusCode(breadcrumbData.status_code);\n\n addBreadcrumb(\n {\n category: 'fetch',\n data: breadcrumbData,\n type: 'http',\n level,\n },\n hint,\n );\n }\n };\n}\n\n/**\n * Creates breadcrumbs from history API calls\n */\nfunction _getHistoryBreadcrumbHandler(client: Client): (handlerData: HandlerDataHistory) => void {\n return function _historyBreadcrumb(handlerData: HandlerDataHistory): void {\n if (getClient() !== client) {\n return;\n }\n\n let from: string | undefined = handlerData.from;\n let to: string | undefined = handlerData.to;\n const parsedLoc = parseUrl(WINDOW.location.href);\n let parsedFrom = from ? parseUrl(from) : undefined;\n const parsedTo = parseUrl(to);\n\n // Initial pushState doesn't provide `from` information\n if (!parsedFrom?.path) {\n parsedFrom = parsedLoc;\n }\n\n // Use only the path component of the URL if the URL matches the current\n // document (almost all the time when using pushState)\n if (parsedLoc.protocol === parsedTo.protocol && parsedLoc.host === parsedTo.host) {\n to = parsedTo.relative;\n }\n if (parsedLoc.protocol === parsedFrom.protocol && parsedLoc.host === parsedFrom.host) {\n from = parsedFrom.relative;\n }\n\n addBreadcrumb({\n category: 'navigation',\n data: {\n from,\n to,\n },\n });\n };\n}\n\nfunction _isEvent(event: unknown): event is Event {\n return !!event && !!(event as Record<string, unknown>).target;\n}\n"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"breadcrumbs.js","sources":["../../../../src/integrations/breadcrumbs.ts"],"sourcesContent":["/* eslint-disable max-lines */\n\nimport {\n SENTRY_XHR_DATA_KEY,\n addClickKeypressInstrumentationHandler,\n addHistoryInstrumentationHandler,\n addXhrInstrumentationHandler,\n} from '@sentry-internal/browser-utils';\nimport type { FetchHint, XhrHint } from '@sentry-internal/browser-utils';\nimport type {\n Breadcrumb,\n Client,\n Event as SentryEvent,\n FetchBreadcrumbData,\n FetchBreadcrumbHint,\n HandlerDataConsole,\n HandlerDataDom,\n HandlerDataFetch,\n HandlerDataHistory,\n HandlerDataXhr,\n IntegrationFn,\n XhrBreadcrumbData,\n XhrBreadcrumbHint,\n} from '@sentry/core';\nimport {\n addBreadcrumb,\n addConsoleInstrumentationHandler,\n addFetchInstrumentationHandler,\n defineIntegration,\n getBreadcrumbLogLevelFromHttpStatusCode,\n getClient,\n getComponentName,\n getEventDescription,\n htmlTreeAsString,\n logger,\n parseUrl,\n safeJoin,\n severityLevelFromString,\n} from '@sentry/core';\n\nimport { DEBUG_BUILD } from '../debug-build';\nimport { WINDOW } from '../helpers';\n\ninterface BreadcrumbsOptions {\n console: boolean;\n dom:\n | boolean\n | {\n serializeAttribute?: string | string[];\n maxStringLength?: number;\n };\n fetch: boolean;\n history: boolean;\n sentry: boolean;\n xhr: boolean;\n}\n\n/** maxStringLength gets capped to prevent 100 breadcrumbs exceeding 1MB event payload size */\nconst MAX_ALLOWED_STRING_LENGTH = 1024;\n\nconst INTEGRATION_NAME = 'Breadcrumbs';\n\nconst _breadcrumbsIntegration = ((options: Partial<BreadcrumbsOptions> = {}) => {\n const _options = {\n console: true,\n dom: true,\n fetch: true,\n history: true,\n sentry: true,\n xhr: true,\n ...options,\n };\n\n return {\n name: INTEGRATION_NAME,\n setup(client) {\n if (_options.console) {\n addConsoleInstrumentationHandler(_getConsoleBreadcrumbHandler(client));\n }\n if (_options.dom) {\n addClickKeypressInstrumentationHandler(_getDomBreadcrumbHandler(client, _options.dom));\n }\n if (_options.xhr) {\n addXhrInstrumentationHandler(_getXhrBreadcrumbHandler(client));\n }\n if (_options.fetch) {\n addFetchInstrumentationHandler(_getFetchBreadcrumbHandler(client));\n }\n if (_options.history) {\n addHistoryInstrumentationHandler(_getHistoryBreadcrumbHandler(client));\n }\n if (_options.sentry) {\n client.on('beforeSendEvent', _getSentryBreadcrumbHandler(client));\n }\n },\n };\n}) satisfies IntegrationFn;\n\nexport const breadcrumbsIntegration = defineIntegration(_breadcrumbsIntegration);\n\n/**\n * Adds a breadcrumb for Sentry events or transactions if this option is enabled.\n */\nfunction _getSentryBreadcrumbHandler(client: Client): (event: SentryEvent) => void {\n return function addSentryBreadcrumb(event: SentryEvent): void {\n if (getClient() !== client) {\n return;\n }\n\n addBreadcrumb(\n {\n category: `sentry.${event.type === 'transaction' ? 'transaction' : 'event'}`,\n event_id: event.event_id,\n level: event.level,\n message: getEventDescription(event),\n },\n {\n event,\n },\n );\n };\n}\n\n/**\n * A HOC that creates a function that creates breadcrumbs from DOM API calls.\n * This is a HOC so that we get access to dom options in the closure.\n */\nfunction _getDomBreadcrumbHandler(\n client: Client,\n dom: BreadcrumbsOptions['dom'],\n): (handlerData: HandlerDataDom) => void {\n return function _innerDomBreadcrumb(handlerData: HandlerDataDom): void {\n if (getClient() !== client) {\n return;\n }\n\n let target;\n let componentName;\n let keyAttrs = typeof dom === 'object' ? dom.serializeAttribute : undefined;\n\n let maxStringLength =\n typeof dom === 'object' && typeof dom.maxStringLength === 'number' ? dom.maxStringLength : undefined;\n if (maxStringLength && maxStringLength > MAX_ALLOWED_STRING_LENGTH) {\n DEBUG_BUILD &&\n logger.warn(\n `\\`dom.maxStringLength\\` cannot exceed ${MAX_ALLOWED_STRING_LENGTH}, but a value of ${maxStringLength} was configured. Sentry will use ${MAX_ALLOWED_STRING_LENGTH} instead.`,\n );\n maxStringLength = MAX_ALLOWED_STRING_LENGTH;\n }\n\n if (typeof keyAttrs === 'string') {\n keyAttrs = [keyAttrs];\n }\n\n // Accessing event.target can throw (see getsentry/raven-js#838, #768)\n try {\n const event = handlerData.event as Event | Node;\n const element = _isEvent(event) ? event.target : event;\n\n target = htmlTreeAsString(element, { keyAttrs, maxStringLength });\n componentName = getComponentName(element);\n } catch (e) {\n target = '<unknown>';\n }\n\n if (target.length === 0) {\n return;\n }\n\n const breadcrumb: Breadcrumb = {\n category: `ui.${handlerData.name}`,\n message: target,\n };\n\n if (componentName) {\n breadcrumb.data = { 'ui.component_name': componentName };\n }\n\n addBreadcrumb(breadcrumb, {\n event: handlerData.event,\n name: handlerData.name,\n global: handlerData.global,\n });\n };\n}\n\n/**\n * Creates breadcrumbs from console API calls\n */\nfunction _getConsoleBreadcrumbHandler(client: Client): (handlerData: HandlerDataConsole) => void {\n return function _consoleBreadcrumb(handlerData: HandlerDataConsole): void {\n if (getClient() !== client) {\n return;\n }\n\n const breadcrumb = {\n category: 'console',\n data: {\n arguments: handlerData.args,\n logger: 'console',\n },\n level: severityLevelFromString(handlerData.level),\n message: safeJoin(handlerData.args, ' '),\n };\n\n if (handlerData.level === 'assert') {\n if (handlerData.args[0] === false) {\n breadcrumb.message = `Assertion failed: ${safeJoin(handlerData.args.slice(1), ' ') || 'console.assert'}`;\n breadcrumb.data.arguments = handlerData.args.slice(1);\n } else {\n // Don't capture a breadcrumb for passed assertions\n return;\n }\n }\n\n addBreadcrumb(breadcrumb, {\n input: handlerData.args,\n level: handlerData.level,\n });\n };\n}\n\n/**\n * Creates breadcrumbs from XHR API calls\n */\nfunction _getXhrBreadcrumbHandler(client: Client): (handlerData: HandlerDataXhr) => void {\n return function _xhrBreadcrumb(handlerData: HandlerDataXhr): void {\n if (getClient() !== client) {\n return;\n }\n\n const { startTimestamp, endTimestamp } = handlerData;\n\n const sentryXhrData = handlerData.xhr[SENTRY_XHR_DATA_KEY];\n\n // We only capture complete, non-sentry requests\n if (!startTimestamp || !endTimestamp || !sentryXhrData) {\n return;\n }\n\n const { method, url, status_code, body } = sentryXhrData;\n\n const data: XhrBreadcrumbData = {\n method,\n url,\n status_code,\n };\n\n const hint: XhrBreadcrumbHint = {\n xhr: handlerData.xhr,\n input: body,\n startTimestamp,\n endTimestamp,\n };\n\n const breadcrumb = {\n category: 'xhr',\n data,\n type: 'http',\n level: getBreadcrumbLogLevelFromHttpStatusCode(status_code),\n };\n\n client.emit('beforeOutgoingRequestBreadcrumb', breadcrumb, hint as XhrHint);\n\n addBreadcrumb(breadcrumb, hint);\n };\n}\n\n/**\n * Creates breadcrumbs from fetch API calls\n */\nfunction _getFetchBreadcrumbHandler(client: Client): (handlerData: HandlerDataFetch) => void {\n return function _fetchBreadcrumb(handlerData: HandlerDataFetch): void {\n if (getClient() !== client) {\n return;\n }\n\n const { startTimestamp, endTimestamp } = handlerData;\n\n // We only capture complete fetch requests\n if (!endTimestamp) {\n return;\n }\n\n if (handlerData.fetchData.url.match(/sentry_key/) && handlerData.fetchData.method === 'POST') {\n // We will not create breadcrumbs for fetch requests that contain `sentry_key` (internal sentry requests)\n return;\n }\n\n const breadcrumbData: FetchBreadcrumbData = {\n method: handlerData.fetchData.method,\n url: handlerData.fetchData.url,\n };\n\n if (handlerData.error) {\n const data: FetchBreadcrumbData = handlerData.fetchData;\n const hint: FetchBreadcrumbHint = {\n data: handlerData.error,\n input: handlerData.args,\n startTimestamp,\n endTimestamp,\n };\n\n const breadcrumb = {\n category: 'fetch',\n data,\n level: 'error',\n type: 'http',\n } satisfies Breadcrumb;\n\n client.emit('beforeOutgoingRequestBreadcrumb', breadcrumb, hint as FetchHint);\n\n addBreadcrumb(breadcrumb, hint);\n } else {\n const response = handlerData.response as Response | undefined;\n const data: FetchBreadcrumbData = {\n ...handlerData.fetchData,\n status_code: response?.status,\n };\n\n breadcrumbData.request_body_size = handlerData.fetchData.request_body_size;\n breadcrumbData.response_body_size = handlerData.fetchData.response_body_size;\n breadcrumbData.status_code = response?.status;\n\n const hint: FetchBreadcrumbHint = {\n input: handlerData.args,\n response,\n startTimestamp,\n endTimestamp,\n };\n\n const breadcrumb = {\n category: 'fetch',\n data,\n type: 'http',\n level: getBreadcrumbLogLevelFromHttpStatusCode(data.status_code),\n };\n\n client.emit('beforeOutgoingRequestBreadcrumb', breadcrumb, hint as FetchHint);\n\n addBreadcrumb(breadcrumb, hint);\n }\n };\n}\n\n/**\n * Creates breadcrumbs from history API calls\n */\nfunction _getHistoryBreadcrumbHandler(client: Client): (handlerData: HandlerDataHistory) => void {\n return function _historyBreadcrumb(handlerData: HandlerDataHistory): void {\n if (getClient() !== client) {\n return;\n }\n\n let from: string | undefined = handlerData.from;\n let to: string | undefined = handlerData.to;\n const parsedLoc = parseUrl(WINDOW.location.href);\n let parsedFrom = from ? parseUrl(from) : undefined;\n const parsedTo = parseUrl(to);\n\n // Initial pushState doesn't provide `from` information\n if (!parsedFrom?.path) {\n parsedFrom = parsedLoc;\n }\n\n // Use only the path component of the URL if the URL matches the current\n // document (almost all the time when using pushState)\n if (parsedLoc.protocol === parsedTo.protocol && parsedLoc.host === parsedTo.host) {\n to = parsedTo.relative;\n }\n if (parsedLoc.protocol === parsedFrom.protocol && parsedLoc.host === parsedFrom.host) {\n from = parsedFrom.relative;\n }\n\n addBreadcrumb({\n category: 'navigation',\n data: {\n from,\n to,\n },\n });\n };\n}\n\nfunction _isEvent(event: unknown): event is Event {\n return !!event && !!(event as Record<string, unknown>).target;\n}\n"],"names":[],"mappings":"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"}
|
|
@@ -14,19 +14,18 @@ import { copyFlagsFromScopeToEvent, insertFlagToScope } from '../../../utils/fea
|
|
|
14
14
|
*
|
|
15
15
|
* Sentry.init({
|
|
16
16
|
* dsn: '___PUBLIC_DSN___',
|
|
17
|
-
* integrations: [Sentry.unleashIntegration({
|
|
17
|
+
* integrations: [Sentry.unleashIntegration({featureFlagClientClass: UnleashClient})],
|
|
18
18
|
* });
|
|
19
19
|
*
|
|
20
20
|
* const unleash = new UnleashClient(...);
|
|
21
21
|
* unleash.start();
|
|
22
22
|
*
|
|
23
23
|
* unleash.isEnabled('my-feature');
|
|
24
|
-
* unleash.getVariant('other-feature');
|
|
25
24
|
* Sentry.captureException(new Error('something went wrong'));
|
|
26
25
|
* ```
|
|
27
26
|
*/
|
|
28
27
|
const unleashIntegration = defineIntegration(
|
|
29
|
-
({ unleashClientClass }) => {
|
|
28
|
+
({ featureFlagClientClass: unleashClientClass }) => {
|
|
30
29
|
return {
|
|
31
30
|
name: 'Unleash',
|
|
32
31
|
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"integration.js","sources":["../../../../../../src/integrations/featureFlags/unleash/integration.ts"],"sourcesContent":["import type { Client, Event, EventHint, IntegrationFn } from '@sentry/core';\n\nimport { defineIntegration, fill, logger } from '@sentry/core';\nimport { DEBUG_BUILD } from '../../../debug-build';\nimport { copyFlagsFromScopeToEvent, insertFlagToScope } from '../../../utils/featureFlags';\nimport type { UnleashClient, UnleashClientClass } from './types';\n\n/**\n * Sentry integration for capturing feature flag evaluations from the Unleash SDK.\n *\n * See the [feature flag documentation](https://develop.sentry.dev/sdk/expected-features/#feature-flags) for more information.\n *\n * @example\n * ```\n * import { UnleashClient } from 'unleash-proxy-client';\n * import * as Sentry from '@sentry/browser';\n *\n * Sentry.init({\n * dsn: '___PUBLIC_DSN___',\n * integrations: [Sentry.unleashIntegration({
|
|
1
|
+
{"version":3,"file":"integration.js","sources":["../../../../../../src/integrations/featureFlags/unleash/integration.ts"],"sourcesContent":["import type { Client, Event, EventHint, IntegrationFn } from '@sentry/core';\n\nimport { defineIntegration, fill, logger } from '@sentry/core';\nimport { DEBUG_BUILD } from '../../../debug-build';\nimport { copyFlagsFromScopeToEvent, insertFlagToScope } from '../../../utils/featureFlags';\nimport type { UnleashClient, UnleashClientClass } from './types';\n\ntype UnleashIntegrationOptions = {\n featureFlagClientClass: UnleashClientClass;\n};\n\n/**\n * Sentry integration for capturing feature flag evaluations from the Unleash SDK.\n *\n * See the [feature flag documentation](https://develop.sentry.dev/sdk/expected-features/#feature-flags) for more information.\n *\n * @example\n * ```\n * import { UnleashClient } from 'unleash-proxy-client';\n * import * as Sentry from '@sentry/browser';\n *\n * Sentry.init({\n * dsn: '___PUBLIC_DSN___',\n * integrations: [Sentry.unleashIntegration({featureFlagClientClass: UnleashClient})],\n * });\n *\n * const unleash = new UnleashClient(...);\n * unleash.start();\n *\n * unleash.isEnabled('my-feature');\n * Sentry.captureException(new Error('something went wrong'));\n * ```\n */\nexport const unleashIntegration = defineIntegration(\n ({ featureFlagClientClass: unleashClientClass }: UnleashIntegrationOptions) => {\n return {\n name: 'Unleash',\n\n processEvent(event: Event, _hint: EventHint, _client: Client): Event {\n return copyFlagsFromScopeToEvent(event);\n },\n\n setupOnce() {\n const unleashClientPrototype = unleashClientClass.prototype as UnleashClient;\n fill(unleashClientPrototype, 'isEnabled', _wrappedIsEnabled);\n },\n };\n },\n) satisfies IntegrationFn;\n\n/**\n * Wraps the UnleashClient.isEnabled method to capture feature flag evaluations. Its only side effect is writing to Sentry scope.\n *\n * This wrapper is safe for all isEnabled signatures. If the signature does not match (this: UnleashClient, toggleName: string, ...args: unknown[]) => boolean,\n * we log an error and return the original result.\n *\n * @param original - The original method.\n * @returns Wrapped method. Results should match the original.\n */\nfunction _wrappedIsEnabled(\n original: (this: UnleashClient, ...args: unknown[]) => unknown,\n): (this: UnleashClient, ...args: unknown[]) => unknown {\n return function (this: UnleashClient, ...args: unknown[]): unknown {\n const toggleName = args[0];\n const result = original.apply(this, args);\n\n if (typeof toggleName === 'string' && typeof result === 'boolean') {\n insertFlagToScope(toggleName, result);\n } else if (DEBUG_BUILD) {\n logger.error(\n `[Feature Flags] UnleashClient.isEnabled does not match expected signature. arg0: ${toggleName} (${typeof toggleName}), result: ${result} (${typeof result})`,\n );\n }\n return result;\n };\n}\n"],"names":[],"mappings":";;;;AAWA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACO,MAAM,kBAAmB,GAAE,iBAAiB;AACnD,EAAE,CAAC,EAAE,sBAAsB,EAAE,kBAAmB,EAAC,KAAgC;AACjF,IAAI,OAAO;AACX,MAAM,IAAI,EAAE,SAAS;;AAErB,MAAM,YAAY,CAAC,KAAK,EAAS,KAAK,EAAa,OAAO,EAAiB;AAC3E,QAAQ,OAAO,yBAAyB,CAAC,KAAK,CAAC;AAC/C,OAAO;;AAEP,MAAM,SAAS,GAAG;AAClB,QAAQ,MAAM,sBAAA,GAAyB,kBAAkB,CAAC,SAAU;AACpE,QAAQ,IAAI,CAAC,sBAAsB,EAAE,WAAW,EAAE,iBAAiB,CAAC;AACpE,OAAO;AACP,KAAK;AACL,GAAG;AACH,CAAE;;AAEF;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,SAAS,iBAAiB;AAC1B,EAAE,QAAQ;AACV,EAAwD;AACxD,EAAE,OAAO,WAA+B,GAAG,IAAI,EAAsB;AACrE,IAAI,MAAM,UAAW,GAAE,IAAI,CAAC,CAAC,CAAC;AAC9B,IAAI,MAAM,MAAO,GAAE,QAAQ,CAAC,KAAK,CAAC,IAAI,EAAE,IAAI,CAAC;;AAE7C,IAAI,IAAI,OAAO,UAAW,KAAI,QAAS,IAAG,OAAO,MAAA,KAAW,SAAS,EAAE;AACvE,MAAM,iBAAiB,CAAC,UAAU,EAAE,MAAM,CAAC;AAC3C,KAAM,MAAK,IAAI,WAAW,EAAE;AAC5B,MAAM,MAAM,CAAC,KAAK;AAClB,QAAQ,CAAC,iFAAiF,EAAE,UAAU,CAAC,EAAE,EAAE,OAAO,UAAU,CAAC,WAAW,EAAE,MAAM,CAAC,EAAE,EAAE,OAAO,MAAM,CAAC,CAAC,CAAC;AACrK,OAAO;AACP;AACA,IAAI,OAAO,MAAM;AACjB,GAAG;AACH;;;;"}
|
|
@@ -0,0 +1,175 @@
|
|
|
1
|
+
import { SENTRY_XHR_DATA_KEY, getBodyString, getFetchRequestArgBody } from '@sentry-internal/browser-utils';
|
|
2
|
+
import { defineIntegration, spanToJSON, SEMANTIC_ATTRIBUTE_SENTRY_OP, SEMANTIC_ATTRIBUTE_URL_FULL, SEMANTIC_ATTRIBUTE_HTTP_REQUEST_METHOD, isString, stringMatchesSomePattern } from '@sentry/core';
|
|
3
|
+
|
|
4
|
+
const INTEGRATION_NAME = 'GraphQLClient';
|
|
5
|
+
|
|
6
|
+
const _graphqlClientIntegration = ((options) => {
|
|
7
|
+
return {
|
|
8
|
+
name: INTEGRATION_NAME,
|
|
9
|
+
setup(client) {
|
|
10
|
+
_updateSpanWithGraphQLData(client, options);
|
|
11
|
+
_updateBreadcrumbWithGraphQLData(client, options);
|
|
12
|
+
},
|
|
13
|
+
};
|
|
14
|
+
}) ;
|
|
15
|
+
|
|
16
|
+
function _updateSpanWithGraphQLData(client, options) {
|
|
17
|
+
client.on('beforeOutgoingRequestSpan', (span, hint) => {
|
|
18
|
+
const spanJSON = spanToJSON(span);
|
|
19
|
+
|
|
20
|
+
const spanAttributes = spanJSON.data || {};
|
|
21
|
+
const spanOp = spanAttributes[SEMANTIC_ATTRIBUTE_SENTRY_OP];
|
|
22
|
+
|
|
23
|
+
const isHttpClientSpan = spanOp === 'http.client';
|
|
24
|
+
|
|
25
|
+
if (!isHttpClientSpan) {
|
|
26
|
+
return;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
const httpUrl = spanAttributes[SEMANTIC_ATTRIBUTE_URL_FULL] || spanAttributes['http.url'];
|
|
30
|
+
const httpMethod = spanAttributes[SEMANTIC_ATTRIBUTE_HTTP_REQUEST_METHOD] || spanAttributes['http.method'];
|
|
31
|
+
|
|
32
|
+
if (!isString(httpUrl) || !isString(httpMethod)) {
|
|
33
|
+
return;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
const { endpoints } = options;
|
|
37
|
+
const isTracedGraphqlEndpoint = stringMatchesSomePattern(httpUrl, endpoints);
|
|
38
|
+
const payload = getRequestPayloadXhrOrFetch(hint );
|
|
39
|
+
|
|
40
|
+
if (isTracedGraphqlEndpoint && payload) {
|
|
41
|
+
const graphqlBody = getGraphQLRequestPayload(payload);
|
|
42
|
+
|
|
43
|
+
if (graphqlBody) {
|
|
44
|
+
const operationInfo = _getGraphQLOperation(graphqlBody);
|
|
45
|
+
span.updateName(`${httpMethod} ${httpUrl} (${operationInfo})`);
|
|
46
|
+
span.setAttribute('graphql.document', payload);
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
});
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
function _updateBreadcrumbWithGraphQLData(client, options) {
|
|
53
|
+
client.on('beforeOutgoingRequestBreadcrumb', (breadcrumb, handlerData) => {
|
|
54
|
+
const { category, type, data } = breadcrumb;
|
|
55
|
+
|
|
56
|
+
const isFetch = category === 'fetch';
|
|
57
|
+
const isXhr = category === 'xhr';
|
|
58
|
+
const isHttpBreadcrumb = type === 'http';
|
|
59
|
+
|
|
60
|
+
if (isHttpBreadcrumb && (isFetch || isXhr)) {
|
|
61
|
+
const httpUrl = data?.url;
|
|
62
|
+
const { endpoints } = options;
|
|
63
|
+
|
|
64
|
+
const isTracedGraphqlEndpoint = stringMatchesSomePattern(httpUrl, endpoints);
|
|
65
|
+
const payload = getRequestPayloadXhrOrFetch(handlerData );
|
|
66
|
+
|
|
67
|
+
if (isTracedGraphqlEndpoint && data && payload) {
|
|
68
|
+
const graphqlBody = getGraphQLRequestPayload(payload);
|
|
69
|
+
|
|
70
|
+
if (!data.graphql && graphqlBody) {
|
|
71
|
+
const operationInfo = _getGraphQLOperation(graphqlBody);
|
|
72
|
+
data['graphql.document'] = graphqlBody.query;
|
|
73
|
+
data['graphql.operation'] = operationInfo;
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
});
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
/**
|
|
81
|
+
* @param requestBody - GraphQL request
|
|
82
|
+
* @returns A formatted version of the request: 'TYPE NAME' or 'TYPE'
|
|
83
|
+
*/
|
|
84
|
+
function _getGraphQLOperation(requestBody) {
|
|
85
|
+
const { query: graphqlQuery, operationName: graphqlOperationName } = requestBody;
|
|
86
|
+
|
|
87
|
+
const { operationName = graphqlOperationName, operationType } = parseGraphQLQuery(graphqlQuery);
|
|
88
|
+
const operationInfo = operationName ? `${operationType} ${operationName}` : `${operationType}`;
|
|
89
|
+
|
|
90
|
+
return operationInfo;
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
/**
|
|
94
|
+
* Get the request body/payload based on the shape of the hint.
|
|
95
|
+
*
|
|
96
|
+
* Exported for tests only.
|
|
97
|
+
*/
|
|
98
|
+
function getRequestPayloadXhrOrFetch(hint) {
|
|
99
|
+
const isXhr = 'xhr' in hint;
|
|
100
|
+
|
|
101
|
+
let body;
|
|
102
|
+
|
|
103
|
+
if (isXhr) {
|
|
104
|
+
const sentryXhrData = hint.xhr[SENTRY_XHR_DATA_KEY];
|
|
105
|
+
body = sentryXhrData && getBodyString(sentryXhrData.body)[0];
|
|
106
|
+
} else {
|
|
107
|
+
const sentryFetchData = getFetchRequestArgBody(hint.input);
|
|
108
|
+
body = getBodyString(sentryFetchData)[0];
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
return body;
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
/**
|
|
115
|
+
* Extract the name and type of the operation from the GraphQL query.
|
|
116
|
+
*
|
|
117
|
+
* Exported for tests only.
|
|
118
|
+
*/
|
|
119
|
+
function parseGraphQLQuery(query) {
|
|
120
|
+
const namedQueryRe = /^(?:\s*)(query|mutation|subscription)(?:\s*)(\w+)(?:\s*)[{(]/;
|
|
121
|
+
const unnamedQueryRe = /^(?:\s*)(query|mutation|subscription)(?:\s*)[{(]/;
|
|
122
|
+
|
|
123
|
+
const namedMatch = query.match(namedQueryRe);
|
|
124
|
+
if (namedMatch) {
|
|
125
|
+
return {
|
|
126
|
+
operationType: namedMatch[1],
|
|
127
|
+
operationName: namedMatch[2],
|
|
128
|
+
};
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
const unnamedMatch = query.match(unnamedQueryRe);
|
|
132
|
+
if (unnamedMatch) {
|
|
133
|
+
return {
|
|
134
|
+
operationType: unnamedMatch[1],
|
|
135
|
+
operationName: undefined,
|
|
136
|
+
};
|
|
137
|
+
}
|
|
138
|
+
return {
|
|
139
|
+
operationType: undefined,
|
|
140
|
+
operationName: undefined,
|
|
141
|
+
};
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
/**
|
|
145
|
+
* Extract the payload of a request if it's GraphQL.
|
|
146
|
+
* Exported for tests only.
|
|
147
|
+
* @param payload - A valid JSON string
|
|
148
|
+
* @returns A POJO or undefined
|
|
149
|
+
*/
|
|
150
|
+
function getGraphQLRequestPayload(payload) {
|
|
151
|
+
let graphqlBody = undefined;
|
|
152
|
+
try {
|
|
153
|
+
const requestBody = JSON.parse(payload) ;
|
|
154
|
+
|
|
155
|
+
// eslint-disable-next-line @typescript-eslint/no-unsafe-member-access
|
|
156
|
+
const isGraphQLRequest = !!requestBody['query'];
|
|
157
|
+
if (isGraphQLRequest) {
|
|
158
|
+
graphqlBody = requestBody;
|
|
159
|
+
}
|
|
160
|
+
} finally {
|
|
161
|
+
// Fallback to undefined if payload is an invalid JSON (SyntaxError)
|
|
162
|
+
|
|
163
|
+
/* eslint-disable no-unsafe-finally */
|
|
164
|
+
return graphqlBody;
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
/**
|
|
169
|
+
* This integration ensures that GraphQL requests made in the browser
|
|
170
|
+
* have their GraphQL-specific data captured and attached to spans and breadcrumbs.
|
|
171
|
+
*/
|
|
172
|
+
const graphqlClientIntegration = defineIntegration(_graphqlClientIntegration);
|
|
173
|
+
|
|
174
|
+
export { getGraphQLRequestPayload, getRequestPayloadXhrOrFetch, graphqlClientIntegration, parseGraphQLQuery };
|
|
175
|
+
//# sourceMappingURL=graphqlClient.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"graphqlClient.js","sources":["../../../../src/integrations/graphqlClient.ts"],"sourcesContent":["import { SENTRY_XHR_DATA_KEY, getBodyString, getFetchRequestArgBody } from '@sentry-internal/browser-utils';\nimport type { FetchHint, XhrHint } from '@sentry-internal/browser-utils';\nimport {\n SEMANTIC_ATTRIBUTE_HTTP_REQUEST_METHOD,\n SEMANTIC_ATTRIBUTE_SENTRY_OP,\n SEMANTIC_ATTRIBUTE_URL_FULL,\n defineIntegration,\n isString,\n spanToJSON,\n stringMatchesSomePattern,\n} from '@sentry/core';\nimport type { Client, IntegrationFn } from '@sentry/core';\n\ninterface GraphQLClientOptions {\n endpoints: Array<string | RegExp>;\n}\n\n/** Standard graphql request shape: https://graphql.org/learn/serving-over-http/#post-request-and-body */\ninterface GraphQLRequestPayload {\n query: string;\n operationName?: string;\n variables?: Record<string, unknown>;\n extensions?: Record<string, unknown>;\n}\n\ninterface GraphQLOperation {\n operationType?: string;\n operationName?: string;\n}\n\nconst INTEGRATION_NAME = 'GraphQLClient';\n\nconst _graphqlClientIntegration = ((options: GraphQLClientOptions) => {\n return {\n name: INTEGRATION_NAME,\n setup(client) {\n _updateSpanWithGraphQLData(client, options);\n _updateBreadcrumbWithGraphQLData(client, options);\n },\n };\n}) satisfies IntegrationFn;\n\nfunction _updateSpanWithGraphQLData(client: Client, options: GraphQLClientOptions): void {\n client.on('beforeOutgoingRequestSpan', (span, hint) => {\n const spanJSON = spanToJSON(span);\n\n const spanAttributes = spanJSON.data || {};\n const spanOp = spanAttributes[SEMANTIC_ATTRIBUTE_SENTRY_OP];\n\n const isHttpClientSpan = spanOp === 'http.client';\n\n if (!isHttpClientSpan) {\n return;\n }\n\n const httpUrl = spanAttributes[SEMANTIC_ATTRIBUTE_URL_FULL] || spanAttributes['http.url'];\n const httpMethod = spanAttributes[SEMANTIC_ATTRIBUTE_HTTP_REQUEST_METHOD] || spanAttributes['http.method'];\n\n if (!isString(httpUrl) || !isString(httpMethod)) {\n return;\n }\n\n const { endpoints } = options;\n const isTracedGraphqlEndpoint = stringMatchesSomePattern(httpUrl, endpoints);\n const payload = getRequestPayloadXhrOrFetch(hint as XhrHint | FetchHint);\n\n if (isTracedGraphqlEndpoint && payload) {\n const graphqlBody = getGraphQLRequestPayload(payload);\n\n if (graphqlBody) {\n const operationInfo = _getGraphQLOperation(graphqlBody);\n span.updateName(`${httpMethod} ${httpUrl} (${operationInfo})`);\n span.setAttribute('graphql.document', payload);\n }\n }\n });\n}\n\nfunction _updateBreadcrumbWithGraphQLData(client: Client, options: GraphQLClientOptions): void {\n client.on('beforeOutgoingRequestBreadcrumb', (breadcrumb, handlerData) => {\n const { category, type, data } = breadcrumb;\n\n const isFetch = category === 'fetch';\n const isXhr = category === 'xhr';\n const isHttpBreadcrumb = type === 'http';\n\n if (isHttpBreadcrumb && (isFetch || isXhr)) {\n const httpUrl = data?.url;\n const { endpoints } = options;\n\n const isTracedGraphqlEndpoint = stringMatchesSomePattern(httpUrl, endpoints);\n const payload = getRequestPayloadXhrOrFetch(handlerData as XhrHint | FetchHint);\n\n if (isTracedGraphqlEndpoint && data && payload) {\n const graphqlBody = getGraphQLRequestPayload(payload);\n\n if (!data.graphql && graphqlBody) {\n const operationInfo = _getGraphQLOperation(graphqlBody);\n data['graphql.document'] = graphqlBody.query;\n data['graphql.operation'] = operationInfo;\n }\n }\n }\n });\n}\n\n/**\n * @param requestBody - GraphQL request\n * @returns A formatted version of the request: 'TYPE NAME' or 'TYPE'\n */\nfunction _getGraphQLOperation(requestBody: GraphQLRequestPayload): string {\n const { query: graphqlQuery, operationName: graphqlOperationName } = requestBody;\n\n const { operationName = graphqlOperationName, operationType } = parseGraphQLQuery(graphqlQuery);\n const operationInfo = operationName ? `${operationType} ${operationName}` : `${operationType}`;\n\n return operationInfo;\n}\n\n/**\n * Get the request body/payload based on the shape of the hint.\n *\n * Exported for tests only.\n */\nexport function getRequestPayloadXhrOrFetch(hint: XhrHint | FetchHint): string | undefined {\n const isXhr = 'xhr' in hint;\n\n let body: string | undefined;\n\n if (isXhr) {\n const sentryXhrData = hint.xhr[SENTRY_XHR_DATA_KEY];\n body = sentryXhrData && getBodyString(sentryXhrData.body)[0];\n } else {\n const sentryFetchData = getFetchRequestArgBody(hint.input);\n body = getBodyString(sentryFetchData)[0];\n }\n\n return body;\n}\n\n/**\n * Extract the name and type of the operation from the GraphQL query.\n *\n * Exported for tests only.\n */\nexport function parseGraphQLQuery(query: string): GraphQLOperation {\n const namedQueryRe = /^(?:\\s*)(query|mutation|subscription)(?:\\s*)(\\w+)(?:\\s*)[{(]/;\n const unnamedQueryRe = /^(?:\\s*)(query|mutation|subscription)(?:\\s*)[{(]/;\n\n const namedMatch = query.match(namedQueryRe);\n if (namedMatch) {\n return {\n operationType: namedMatch[1],\n operationName: namedMatch[2],\n };\n }\n\n const unnamedMatch = query.match(unnamedQueryRe);\n if (unnamedMatch) {\n return {\n operationType: unnamedMatch[1],\n operationName: undefined,\n };\n }\n return {\n operationType: undefined,\n operationName: undefined,\n };\n}\n\n/**\n * Extract the payload of a request if it's GraphQL.\n * Exported for tests only.\n * @param payload - A valid JSON string\n * @returns A POJO or undefined\n */\nexport function getGraphQLRequestPayload(payload: string): GraphQLRequestPayload | undefined {\n let graphqlBody = undefined;\n try {\n const requestBody = JSON.parse(payload) satisfies GraphQLRequestPayload;\n\n // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access\n const isGraphQLRequest = !!requestBody['query'];\n if (isGraphQLRequest) {\n graphqlBody = requestBody;\n }\n } finally {\n // Fallback to undefined if payload is an invalid JSON (SyntaxError)\n\n /* eslint-disable no-unsafe-finally */\n return graphqlBody;\n }\n}\n\n/**\n * This integration ensures that GraphQL requests made in the browser\n * have their GraphQL-specific data captured and attached to spans and breadcrumbs.\n */\nexport const graphqlClientIntegration = defineIntegration(_graphqlClientIntegration);\n"],"names":[],"mappings":";;;AA8BA,MAAM,gBAAA,GAAmB,eAAe;;AAExC,MAAM,6BAA6B,CAAC,OAAO,KAA2B;AACtE,EAAE,OAAO;AACT,IAAI,IAAI,EAAE,gBAAgB;AAC1B,IAAI,KAAK,CAAC,MAAM,EAAE;AAClB,MAAM,0BAA0B,CAAC,MAAM,EAAE,OAAO,CAAC;AACjD,MAAM,gCAAgC,CAAC,MAAM,EAAE,OAAO,CAAC;AACvD,KAAK;AACL,GAAG;AACH,CAAC,CAAE;;AAEH,SAAS,0BAA0B,CAAC,MAAM,EAAU,OAAO,EAA8B;AACzF,EAAE,MAAM,CAAC,EAAE,CAAC,2BAA2B,EAAE,CAAC,IAAI,EAAE,IAAI,KAAK;AACzD,IAAI,MAAM,QAAS,GAAE,UAAU,CAAC,IAAI,CAAC;;AAErC,IAAI,MAAM,iBAAiB,QAAQ,CAAC,IAAK,IAAG,EAAE;AAC9C,IAAI,MAAM,MAAO,GAAE,cAAc,CAAC,4BAA4B,CAAC;;AAE/D,IAAI,MAAM,gBAAA,GAAmB,MAAA,KAAW,aAAa;;AAErD,IAAI,IAAI,CAAC,gBAAgB,EAAE;AAC3B,MAAM;AACN;;AAEA,IAAI,MAAM,OAAQ,GAAE,cAAc,CAAC,2BAA2B,CAAA,IAAK,cAAc,CAAC,UAAU,CAAC;AAC7F,IAAI,MAAM,UAAW,GAAE,cAAc,CAAC,sCAAsC,CAAA,IAAK,cAAc,CAAC,aAAa,CAAC;;AAE9G,IAAI,IAAI,CAAC,QAAQ,CAAC,OAAO,CAAE,IAAG,CAAC,QAAQ,CAAC,UAAU,CAAC,EAAE;AACrD,MAAM;AACN;;AAEA,IAAI,MAAM,EAAE,SAAU,EAAA,GAAI,OAAO;AACjC,IAAI,MAAM,0BAA0B,wBAAwB,CAAC,OAAO,EAAE,SAAS,CAAC;AAChF,IAAI,MAAM,OAAQ,GAAE,2BAA2B,CAAC,MAA4B;;AAE5E,IAAI,IAAI,uBAAwB,IAAG,OAAO,EAAE;AAC5C,MAAM,MAAM,WAAY,GAAE,wBAAwB,CAAC,OAAO,CAAC;;AAE3D,MAAM,IAAI,WAAW,EAAE;AACvB,QAAQ,MAAM,aAAc,GAAE,oBAAoB,CAAC,WAAW,CAAC;AAC/D,QAAQ,IAAI,CAAC,UAAU,CAAC,CAAC,EAAA,UAAA,CAAA,CAAA,EAAA,OAAA,CAAA,EAAA,EAAA,aAAA,CAAA,CAAA,CAAA,CAAA;AACA,QAAA,IAAA,CAAA,YAAA,CAAA,kBAAA,EAAA,OAAA,CAAA;AACA;AACA;AACA,GAAA,CAAA;AACA;;AAEA,SAAA,gCAAA,CAAA,MAAA,EAAA,OAAA,EAAA;AACA,EAAA,MAAA,CAAA,EAAA,CAAA,iCAAA,EAAA,CAAA,UAAA,EAAA,WAAA,KAAA;AACA,IAAA,MAAA,EAAA,QAAA,EAAA,IAAA,EAAA,IAAA,EAAA,GAAA,UAAA;;AAEA,IAAA,MAAA,OAAA,GAAA,QAAA,KAAA,OAAA;AACA,IAAA,MAAA,KAAA,GAAA,QAAA,KAAA,KAAA;AACA,IAAA,MAAA,gBAAA,GAAA,IAAA,KAAA,MAAA;;AAEA,IAAA,IAAA,gBAAA,KAAA,OAAA,IAAA,KAAA,CAAA,EAAA;AACA,MAAA,MAAA,OAAA,GAAA,IAAA,EAAA,GAAA;AACA,MAAA,MAAA,EAAA,SAAA,EAAA,GAAA,OAAA;;AAEA,MAAA,MAAA,uBAAA,GAAA,wBAAA,CAAA,OAAA,EAAA,SAAA,CAAA;AACA,MAAA,MAAA,OAAA,GAAA,2BAAA,CAAA,WAAA,EAAA;;AAEA,MAAA,IAAA,uBAAA,IAAA,IAAA,IAAA,OAAA,EAAA;AACA,QAAA,MAAA,WAAA,GAAA,wBAAA,CAAA,OAAA,CAAA;;AAEA,QAAA,IAAA,CAAA,IAAA,CAAA,OAAA,IAAA,WAAA,EAAA;AACA,UAAA,MAAA,aAAA,GAAA,oBAAA,CAAA,WAAA,CAAA;AACA,UAAA,IAAA,CAAA,kBAAA,CAAA,GAAA,WAAA,CAAA,KAAA;AACA,UAAA,IAAA,CAAA,mBAAA,CAAA,GAAA,aAAA;AACA;AACA;AACA;AACA,GAAA,CAAA;AACA;;AAEA;AACA;AACA;AACA;AACA,SAAA,oBAAA,CAAA,WAAA,EAAA;AACA,EAAA,MAAA,EAAA,KAAA,EAAA,YAAA,EAAA,aAAA,EAAA,oBAAA,EAAA,GAAA,WAAA;;AAEA,EAAA,MAAA,EAAA,aAAA,GAAA,oBAAA,EAAA,aAAA,EAAA,GAAA,iBAAA,CAAA,YAAA,CAAA;AACA,EAAA,MAAA,aAAA,GAAA,aAAA,GAAA,CAAA,EAAA,aAAA,CAAA,CAAA,EAAA,aAAA,CAAA,CAAA,GAAA,CAAA,EAAA,aAAA,CAAA,CAAA;;AAEA,EAAA,OAAA,aAAA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA,SAAA,2BAAA,CAAA,IAAA,EAAA;AACA,EAAA,MAAA,KAAA,GAAA,KAAA,IAAA,IAAA;;AAEA,EAAA,IAAA,IAAA;;AAEA,EAAA,IAAA,KAAA,EAAA;AACA,IAAA,MAAA,aAAA,GAAA,IAAA,CAAA,GAAA,CAAA,mBAAA,CAAA;AACA,IAAA,IAAA,GAAA,aAAA,IAAA,aAAA,CAAA,aAAA,CAAA,IAAA,CAAA,CAAA,CAAA,CAAA;AACA,GAAA,MAAA;AACA,IAAA,MAAA,eAAA,GAAA,sBAAA,CAAA,IAAA,CAAA,KAAA,CAAA;AACA,IAAA,IAAA,GAAA,aAAA,CAAA,eAAA,CAAA,CAAA,CAAA,CAAA;AACA;;AAEA,EAAA,OAAA,IAAA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA,SAAA,iBAAA,CAAA,KAAA,EAAA;AACA,EAAA,MAAA,YAAA,GAAA,8DAAA;AACA,EAAA,MAAA,cAAA,GAAA,kDAAA;;AAEA,EAAA,MAAA,UAAA,GAAA,KAAA,CAAA,KAAA,CAAA,YAAA,CAAA;AACA,EAAA,IAAA,UAAA,EAAA;AACA,IAAA,OAAA;AACA,MAAA,aAAA,EAAA,UAAA,CAAA,CAAA,CAAA;AACA,MAAA,aAAA,EAAA,UAAA,CAAA,CAAA,CAAA;AACA,KAAA;AACA;;AAEA,EAAA,MAAA,YAAA,GAAA,KAAA,CAAA,KAAA,CAAA,cAAA,CAAA;AACA,EAAA,IAAA,YAAA,EAAA;AACA,IAAA,OAAA;AACA,MAAA,aAAA,EAAA,YAAA,CAAA,CAAA,CAAA;AACA,MAAA,aAAA,EAAA,SAAA;AACA,KAAA;AACA;AACA,EAAA,OAAA;AACA,IAAA,aAAA,EAAA,SAAA;AACA,IAAA,aAAA,EAAA,SAAA;AACA,GAAA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA,SAAA,wBAAA,CAAA,OAAA,EAAA;AACA,EAAA,IAAA,WAAA,GAAA,SAAA;AACA,EAAA,IAAA;AACA,IAAA,MAAA,WAAA,GAAA,IAAA,CAAA,KAAA,CAAA,OAAA,CAAA;;AAEA;AACA,IAAA,MAAA,gBAAA,GAAA,CAAA,CAAA,WAAA,CAAA,OAAA,CAAA;AACA,IAAA,IAAA,gBAAA,EAAA;AACA,MAAA,WAAA,GAAA,WAAA;AACA;AACA,GAAA,SAAA;AACA;;AAEA;AACA,IAAA,OAAA,WAAA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA,MAAA,wBAAA,GAAA,iBAAA,CAAA,yBAAA;;;;"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"type":"module","version":"9.
|
|
1
|
+
{"type":"module","version":"9.1.0","sideEffects":false}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { addXhrInstrumentationHandler, addPerformanceInstrumentationHandler, extractNetworkProtocol, SENTRY_XHR_DATA_KEY } from '@sentry-internal/browser-utils';
|
|
2
|
-
import { addFetchEndInstrumentationHandler, addFetchInstrumentationHandler, instrumentFetchRequest, parseUrl, spanToJSON, browserPerformanceTimeOrigin, hasSpansEnabled, setHttpStatus, getActiveSpan, startInactiveSpan, SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN, SEMANTIC_ATTRIBUTE_SENTRY_OP, SentryNonRecordingSpan, getTraceData, getLocationHref, stringMatchesSomePattern } from '@sentry/core';
|
|
2
|
+
import { addFetchEndInstrumentationHandler, addFetchInstrumentationHandler, instrumentFetchRequest, parseUrl, spanToJSON, browserPerformanceTimeOrigin, hasSpansEnabled, setHttpStatus, getActiveSpan, startInactiveSpan, SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN, SEMANTIC_ATTRIBUTE_SENTRY_OP, SentryNonRecordingSpan, getClient, getTraceData, getLocationHref, stringMatchesSomePattern } from '@sentry/core';
|
|
3
3
|
import { WINDOW } from '../helpers.js';
|
|
4
4
|
|
|
5
5
|
/** Options for Request Instrumentation */
|
|
@@ -281,6 +281,11 @@ function xhrCallback(
|
|
|
281
281
|
);
|
|
282
282
|
}
|
|
283
283
|
|
|
284
|
+
const client = getClient();
|
|
285
|
+
if (client) {
|
|
286
|
+
client.emit('beforeOutgoingRequestSpan', span, handlerData );
|
|
287
|
+
}
|
|
288
|
+
|
|
284
289
|
return span;
|
|
285
290
|
}
|
|
286
291
|
|