@sentry/browser 11.0.0 → 11.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.
Files changed (32) hide show
  1. package/README.md +7 -41
  2. package/build/npm/cjs/dev/integrations/httpcontext.js +1 -1
  3. package/build/npm/cjs/dev/integrations/httpcontext.js.map +1 -1
  4. package/build/npm/cjs/dev/integrations/spotlight.js +2 -1
  5. package/build/npm/cjs/dev/integrations/spotlight.js.map +1 -1
  6. package/build/npm/cjs/dev/report-dialog.js +14 -1
  7. package/build/npm/cjs/dev/report-dialog.js.map +1 -1
  8. package/build/npm/cjs/prod/integrations/httpcontext.js +1 -1
  9. package/build/npm/cjs/prod/integrations/httpcontext.js.map +1 -1
  10. package/build/npm/cjs/prod/integrations/spotlight.js +2 -1
  11. package/build/npm/cjs/prod/integrations/spotlight.js.map +1 -1
  12. package/build/npm/cjs/prod/report-dialog.js +14 -1
  13. package/build/npm/cjs/prod/report-dialog.js.map +1 -1
  14. package/build/npm/esm/dev/integrations/httpcontext.js +2 -2
  15. package/build/npm/esm/dev/integrations/httpcontext.js.map +1 -1
  16. package/build/npm/esm/dev/integrations/spotlight.js +2 -1
  17. package/build/npm/esm/dev/integrations/spotlight.js.map +1 -1
  18. package/build/npm/esm/dev/package.json +1 -1
  19. package/build/npm/esm/dev/report-dialog.js +14 -1
  20. package/build/npm/esm/dev/report-dialog.js.map +1 -1
  21. package/build/npm/esm/prod/integrations/httpcontext.js +2 -2
  22. package/build/npm/esm/prod/integrations/httpcontext.js.map +1 -1
  23. package/build/npm/esm/prod/integrations/spotlight.js +2 -1
  24. package/build/npm/esm/prod/integrations/spotlight.js.map +1 -1
  25. package/build/npm/esm/prod/package.json +1 -1
  26. package/build/npm/esm/prod/report-dialog.js +14 -1
  27. package/build/npm/esm/prod/report-dialog.js.map +1 -1
  28. package/build/npm/types/integrations/httpcontext.d.ts.map +1 -1
  29. package/build/npm/types/integrations/spotlight.d.ts +1 -1
  30. package/build/npm/types/integrations/spotlight.d.ts.map +1 -1
  31. package/build/npm/types/report-dialog.d.ts.map +1 -1
  32. package/package.json +8 -8
package/README.md CHANGED
@@ -11,48 +11,14 @@
11
11
  [![npm dm](https://img.shields.io/npm/dm/@sentry/browser.svg)](https://www.npmjs.com/package/@sentry/browser)
12
12
  [![npm dt](https://img.shields.io/npm/dt/@sentry/browser.svg)](https://www.npmjs.com/package/@sentry/browser)
13
13
 
14
- ## Links
14
+ The official Sentry SDK for monitoring JavaScript applications in the browser.
15
15
 
16
- - [Official SDK Docs](https://docs.sentry.io/quickstart/)
16
+ ## Documentation
17
17
 
18
- ## Usage
18
+ - [Getting started](https://docs.sentry.io/platforms/javascript/)
19
+ - [Configuration](https://docs.sentry.io/platforms/javascript/configuration/)
19
20
 
20
- To use this SDK, call `Sentry.init(options)` as early as possible after loading the page. This will initialize the SDK
21
- and hook into the environment. Note that you can turn off almost all side effects using the respective options.
21
+ ## Support
22
22
 
23
- ```javascript
24
- import * as Sentry from '@sentry/browser';
25
-
26
- Sentry.init({
27
- dsn: '__DSN__',
28
- // ...
29
- });
30
- ```
31
-
32
- To set context information or send manual events, use the exported functions of `@sentry/browser`. Note that these
33
- functions will not perform any action before you have called `Sentry.init()`:
34
-
35
- ```javascript
36
- import * as Sentry from '@sentry/browser';
37
-
38
- // Set user information, as well as tags and further extras
39
- Sentry.setExtra('battery', 0.7);
40
- Sentry.setTag('user_mode', 'admin');
41
- Sentry.setUser({ id: '4711' });
42
-
43
- // Add a breadcrumb for future events
44
- Sentry.addBreadcrumb({
45
- message: 'My Breadcrumb',
46
- // ...
47
- });
48
-
49
- // Capture exceptions, messages or manual events
50
- Sentry.captureMessage('Hello, world!');
51
- Sentry.captureException(new Error('Good bye'));
52
- Sentry.captureEvent({
53
- message: 'Manual',
54
- stacktrace: [
55
- // ...
56
- ],
57
- });
58
- ```
23
+ - [Report a bug](https://github.com/getsentry/sentry-javascript/issues/new/choose)
24
+ - [Contributing](https://github.com/getsentry/sentry-javascript/blob/develop/CONTRIBUTING.md)
@@ -43,7 +43,7 @@ const httpContextIntegration = core.defineIntegration(() => {
43
43
  // Likewise for the "Filter out localhost events" feature. Deliberately inlined rather than
44
44
  // sharing the server-side helper, which costs bundle size for request headers and IPs that
45
45
  // don't exist here. Mirrors Relay's localhost filter (relay-filter/src/localhost.rs).
46
- "sentry.is_localhost": protocol === "file:" || hostname === "localhost" || hostname === "127.0.0.1" || // `location.hostname` keeps the brackets for IPv6 hosts, so this is `[::1]`, not `::1`.
46
+ [attributes.SENTRY_IS_LOCALHOST]: protocol === "file:" || hostname === "localhost" || hostname === "127.0.0.1" || // `location.hostname` keeps the brackets for IPv6 hosts, so this is `[::1]`, not `::1`.
47
47
  hostname === "[::1]" || !!hostname?.endsWith(".localhost"),
48
48
  // These attributes, we only need on the segment span (analogous to the `request` context for events)
49
49
  ...span.is_segment && {
@@ -1 +1 @@
1
- {"version":3,"file":"httpcontext.js","sources":["../../../../../src/integrations/httpcontext.ts"],"sourcesContent":["import { _INTERNAL_filterKeyValueData, defineIntegration, safeSetSpanJSONAttributes } from '@sentry/core';\nimport { getHttpRequestData, WINDOW } from '../helpers';\nimport { filterCollectedUrl } from '@sentry/core';\nimport { HTTP_REQUEST_HEADER_KEY_BASE, SENTRY_OP, URL_FULL, USER_AGENT_ORIGINAL } from '@sentry/conventions/attributes';\n\n/**\n * Collects information about HTTP request headers and\n * attaches them to the event.\n */\nexport const httpContextIntegration = defineIntegration(() => {\n return {\n name: 'HttpContext' as const,\n preprocessEvent(event, _hint, client) {\n // if none of the information we want exists, don't bother\n if (!WINDOW.navigator && !WINDOW.location && !WINDOW.document) {\n return;\n }\n\n const { url, headers: collectedHeaders } = getHttpRequestData();\n\n // We only filter the headers we collected ourselves, so headers the user set are left alone. Going by key\n // means we also catch these headers when `browserTracingIntegration` already put them on the event.\n const behavior = client.getDataCollectionOptions().httpHeaders.request;\n const userHeaders = Object.entries(event.request?.headers ?? {}).filter(([key]) => !(key in collectedHeaders));\n\n const headers = {\n ..._INTERNAL_filterKeyValueData(collectedHeaders, behavior),\n ...Object.fromEntries(userHeaders),\n };\n\n event.request = {\n // The URL isn't gated by `dataCollection`, same as on the server.\n url,\n ...event.request,\n ...(Object.keys(headers).length > 0 ? { headers } : { headers: undefined }),\n };\n },\n\n processSpan(span, client) {\n // if none of the information we want exists, don't bother\n if (!WINDOW.navigator && !WINDOW.location && !WINDOW.document) {\n return;\n }\n\n const reqData = getHttpRequestData();\n\n // `httpHeadersToSpanAttributes` would also work here, but its cookie and array handling never runs for these\n // two headers and costs every browser bundle ~400B gzip.\n const headers = _INTERNAL_filterKeyValueData(\n reqData.headers,\n client.getDataCollectionOptions().httpHeaders.request,\n );\n const referer = headers['Referer'];\n const { hostname, protocol } = WINDOW.location || {};\n\n safeSetSpanJSONAttributes(span, {\n // This attribute is used by the \"Filter out events from legacy browsers and crawlers\" features on the Sentry backend.\n // Therefore, it's set on every span.\n [USER_AGENT_ORIGINAL]: headers['User-Agent'],\n\n // Likewise for the \"Filter out localhost events\" feature. Deliberately inlined rather than\n // sharing the server-side helper, which costs bundle size for request headers and IPs that\n // don't exist here. Mirrors Relay's localhost filter (relay-filter/src/localhost.rs).\n 'sentry.is_localhost':\n protocol === 'file:' ||\n hostname === 'localhost' ||\n hostname === '127.0.0.1' ||\n // `location.hostname` keeps the brackets for IPv6 hosts, so this is `[::1]`, not `::1`.\n hostname === '[::1]' ||\n !!hostname?.endsWith('.localhost'),\n\n // These attributes, we only need on the segment span (analogous to the `request` context for events)\n ...(span.is_segment && {\n // Coerce empty string to undefined so the helper's nullish check drops it,\n // rather than writing an empty `url.full` attribute onto the span.\n [URL_FULL]: span.attributes?.[SENTRY_OP] !== 'http.client' ? filterCollectedUrl(reqData.url) : undefined,\n [`${HTTP_REQUEST_HEADER_KEY_BASE}.referer`]: referer ? [referer] : undefined,\n }),\n });\n },\n };\n});\n"],"names":["defineIntegration","WINDOW","getHttpRequestData","_INTERNAL_filterKeyValueData","safeSetSpanJSONAttributes","USER_AGENT_ORIGINAL","URL_FULL","SENTRY_OP","filterCollectedUrl","HTTP_REQUEST_HEADER_KEY_BASE"],"mappings":";;;;;;AASO,MAAM,sBAAA,GAAyBA,uBAAkB,MAAM;AAC5D,EAAA,OAAO;AAAA,IACL,IAAA,EAAM,aAAA;AAAA,IACN,eAAA,CAAgB,KAAA,EAAO,KAAA,EAAO,MAAA,EAAQ;AAEpC,MAAA,IAAI,CAACC,eAAO,SAAA,IAAa,CAACA,eAAO,QAAA,IAAY,CAACA,eAAO,QAAA,EAAU;AAC7D,QAAA;AAAA,MACF;AAEA,MAAA,MAAM,EAAE,GAAA,EAAK,OAAA,EAAS,gBAAA,KAAqBC,0BAAA,EAAmB;AAI9D,MAAA,MAAM,QAAA,GAAW,MAAA,CAAO,wBAAA,EAAyB,CAAE,WAAA,CAAY,OAAA;AAC/D,MAAA,MAAM,cAAc,MAAA,CAAO,OAAA,CAAQ,KAAA,CAAM,OAAA,EAAS,WAAW,EAAE,CAAA,CAAE,MAAA,CAAO,CAAC,CAAC,GAAG,CAAA,KAAM,EAAE,OAAO,gBAAA,CAAiB,CAAA;AAE7G,MAAA,MAAM,OAAA,GAAU;AAAA,QACd,GAAGC,iCAAA,CAA6B,gBAAA,EAAkB,QAAQ,CAAA;AAAA,QAC1D,GAAG,MAAA,CAAO,WAAA,CAAY,WAAW;AAAA,OACnC;AAEA,MAAA,KAAA,CAAM,OAAA,GAAU;AAAA;AAAA,QAEd,GAAA;AAAA,QACA,GAAG,KAAA,CAAM,OAAA;AAAA,QACT,GAAI,MAAA,CAAO,IAAA,CAAK,OAAO,CAAA,CAAE,MAAA,GAAS,CAAA,GAAI,EAAE,OAAA,EAAQ,GAAI,EAAE,OAAA,EAAS,MAAA;AAAU,OAC3E;AAAA,IACF,CAAA;AAAA,IAEA,WAAA,CAAY,MAAM,MAAA,EAAQ;AAExB,MAAA,IAAI,CAACF,eAAO,SAAA,IAAa,CAACA,eAAO,QAAA,IAAY,CAACA,eAAO,QAAA,EAAU;AAC7D,QAAA;AAAA,MACF;AAEA,MAAA,MAAM,UAAUC,0BAAA,EAAmB;AAInC,MAAA,MAAM,OAAA,GAAUC,iCAAA;AAAA,QACd,OAAA,CAAQ,OAAA;AAAA,QACR,MAAA,CAAO,wBAAA,EAAyB,CAAE,WAAA,CAAY;AAAA,OAChD;AACA,MAAA,MAAM,OAAA,GAAU,QAAQ,SAAS,CAAA;AACjC,MAAA,MAAM,EAAE,QAAA,EAAU,QAAA,EAAS,GAAIF,cAAA,CAAO,YAAY,EAAC;AAEnD,MAAAG,8BAAA,CAA0B,IAAA,EAAM;AAAA;AAAA;AAAA,QAG9B,CAACC,8BAAmB,GAAG,OAAA,CAAQ,YAAY,CAAA;AAAA;AAAA;AAAA;AAAA,QAK3C,qBAAA,EACE,QAAA,KAAa,OAAA,IACb,QAAA,KAAa,eACb,QAAA,KAAa,WAAA;AAAA,QAEb,aAAa,OAAA,IACb,CAAC,CAAC,QAAA,EAAU,SAAS,YAAY,CAAA;AAAA;AAAA,QAGnC,GAAI,KAAK,UAAA,IAAc;AAAA;AAAA;AAAA,UAGrB,CAACC,mBAAQ,GAAG,IAAA,CAAK,UAAA,GAAaC,oBAAS,CAAA,KAAM,aAAA,GAAgBC,uBAAA,CAAmB,OAAA,CAAQ,GAAG,CAAA,GAAI,MAAA;AAAA,UAC/F,CAAC,GAAGC,uCAA4B,CAAA,QAAA,CAAU,GAAG,OAAA,GAAU,CAAC,OAAO,CAAA,GAAI;AAAA;AACrE,OACD,CAAA;AAAA,IACH;AAAA,GACF;AACF,CAAC;;;;"}
1
+ {"version":3,"file":"httpcontext.js","sources":["../../../../../src/integrations/httpcontext.ts"],"sourcesContent":["import { _INTERNAL_filterKeyValueData, defineIntegration, safeSetSpanJSONAttributes } from '@sentry/core';\nimport { getHttpRequestData, WINDOW } from '../helpers';\nimport { filterCollectedUrl } from '@sentry/core';\nimport {\n HTTP_REQUEST_HEADER_KEY_BASE,\n SENTRY_IS_LOCALHOST,\n SENTRY_OP,\n URL_FULL,\n USER_AGENT_ORIGINAL,\n} from '@sentry/conventions/attributes';\n\n/**\n * Collects information about HTTP request headers and\n * attaches them to the event.\n */\nexport const httpContextIntegration = defineIntegration(() => {\n return {\n name: 'HttpContext' as const,\n preprocessEvent(event, _hint, client) {\n // if none of the information we want exists, don't bother\n if (!WINDOW.navigator && !WINDOW.location && !WINDOW.document) {\n return;\n }\n\n const { url, headers: collectedHeaders } = getHttpRequestData();\n\n // We only filter the headers we collected ourselves, so headers the user set are left alone. Going by key\n // means we also catch these headers when `browserTracingIntegration` already put them on the event.\n const behavior = client.getDataCollectionOptions().httpHeaders.request;\n const userHeaders = Object.entries(event.request?.headers ?? {}).filter(([key]) => !(key in collectedHeaders));\n\n const headers = {\n ..._INTERNAL_filterKeyValueData(collectedHeaders, behavior),\n ...Object.fromEntries(userHeaders),\n };\n\n event.request = {\n // The URL isn't gated by `dataCollection`, same as on the server.\n url,\n ...event.request,\n ...(Object.keys(headers).length > 0 ? { headers } : { headers: undefined }),\n };\n },\n\n processSpan(span, client) {\n // if none of the information we want exists, don't bother\n if (!WINDOW.navigator && !WINDOW.location && !WINDOW.document) {\n return;\n }\n\n const reqData = getHttpRequestData();\n\n // `httpHeadersToSpanAttributes` would also work here, but its cookie and array handling never runs for these\n // two headers and costs every browser bundle ~400B gzip.\n const headers = _INTERNAL_filterKeyValueData(\n reqData.headers,\n client.getDataCollectionOptions().httpHeaders.request,\n );\n const referer = headers['Referer'];\n const { hostname, protocol } = WINDOW.location || {};\n\n safeSetSpanJSONAttributes(span, {\n // This attribute is used by the \"Filter out events from legacy browsers and crawlers\" features on the Sentry backend.\n // Therefore, it's set on every span.\n [USER_AGENT_ORIGINAL]: headers['User-Agent'],\n\n // Likewise for the \"Filter out localhost events\" feature. Deliberately inlined rather than\n // sharing the server-side helper, which costs bundle size for request headers and IPs that\n // don't exist here. Mirrors Relay's localhost filter (relay-filter/src/localhost.rs).\n [SENTRY_IS_LOCALHOST]:\n protocol === 'file:' ||\n hostname === 'localhost' ||\n hostname === '127.0.0.1' ||\n // `location.hostname` keeps the brackets for IPv6 hosts, so this is `[::1]`, not `::1`.\n hostname === '[::1]' ||\n !!hostname?.endsWith('.localhost'),\n\n // These attributes, we only need on the segment span (analogous to the `request` context for events)\n ...(span.is_segment && {\n // Coerce empty string to undefined so the helper's nullish check drops it,\n // rather than writing an empty `url.full` attribute onto the span.\n [URL_FULL]: span.attributes?.[SENTRY_OP] !== 'http.client' ? filterCollectedUrl(reqData.url) : undefined,\n [`${HTTP_REQUEST_HEADER_KEY_BASE}.referer`]: referer ? [referer] : undefined,\n }),\n });\n },\n };\n});\n"],"names":["defineIntegration","WINDOW","getHttpRequestData","_INTERNAL_filterKeyValueData","safeSetSpanJSONAttributes","USER_AGENT_ORIGINAL","SENTRY_IS_LOCALHOST","URL_FULL","SENTRY_OP","filterCollectedUrl","HTTP_REQUEST_HEADER_KEY_BASE"],"mappings":";;;;;;AAeO,MAAM,sBAAA,GAAyBA,uBAAkB,MAAM;AAC5D,EAAA,OAAO;AAAA,IACL,IAAA,EAAM,aAAA;AAAA,IACN,eAAA,CAAgB,KAAA,EAAO,KAAA,EAAO,MAAA,EAAQ;AAEpC,MAAA,IAAI,CAACC,eAAO,SAAA,IAAa,CAACA,eAAO,QAAA,IAAY,CAACA,eAAO,QAAA,EAAU;AAC7D,QAAA;AAAA,MACF;AAEA,MAAA,MAAM,EAAE,GAAA,EAAK,OAAA,EAAS,gBAAA,KAAqBC,0BAAA,EAAmB;AAI9D,MAAA,MAAM,QAAA,GAAW,MAAA,CAAO,wBAAA,EAAyB,CAAE,WAAA,CAAY,OAAA;AAC/D,MAAA,MAAM,cAAc,MAAA,CAAO,OAAA,CAAQ,KAAA,CAAM,OAAA,EAAS,WAAW,EAAE,CAAA,CAAE,MAAA,CAAO,CAAC,CAAC,GAAG,CAAA,KAAM,EAAE,OAAO,gBAAA,CAAiB,CAAA;AAE7G,MAAA,MAAM,OAAA,GAAU;AAAA,QACd,GAAGC,iCAAA,CAA6B,gBAAA,EAAkB,QAAQ,CAAA;AAAA,QAC1D,GAAG,MAAA,CAAO,WAAA,CAAY,WAAW;AAAA,OACnC;AAEA,MAAA,KAAA,CAAM,OAAA,GAAU;AAAA;AAAA,QAEd,GAAA;AAAA,QACA,GAAG,KAAA,CAAM,OAAA;AAAA,QACT,GAAI,MAAA,CAAO,IAAA,CAAK,OAAO,CAAA,CAAE,MAAA,GAAS,CAAA,GAAI,EAAE,OAAA,EAAQ,GAAI,EAAE,OAAA,EAAS,MAAA;AAAU,OAC3E;AAAA,IACF,CAAA;AAAA,IAEA,WAAA,CAAY,MAAM,MAAA,EAAQ;AAExB,MAAA,IAAI,CAACF,eAAO,SAAA,IAAa,CAACA,eAAO,QAAA,IAAY,CAACA,eAAO,QAAA,EAAU;AAC7D,QAAA;AAAA,MACF;AAEA,MAAA,MAAM,UAAUC,0BAAA,EAAmB;AAInC,MAAA,MAAM,OAAA,GAAUC,iCAAA;AAAA,QACd,OAAA,CAAQ,OAAA;AAAA,QACR,MAAA,CAAO,wBAAA,EAAyB,CAAE,WAAA,CAAY;AAAA,OAChD;AACA,MAAA,MAAM,OAAA,GAAU,QAAQ,SAAS,CAAA;AACjC,MAAA,MAAM,EAAE,QAAA,EAAU,QAAA,EAAS,GAAIF,cAAA,CAAO,YAAY,EAAC;AAEnD,MAAAG,8BAAA,CAA0B,IAAA,EAAM;AAAA;AAAA;AAAA,QAG9B,CAACC,8BAAmB,GAAG,OAAA,CAAQ,YAAY,CAAA;AAAA;AAAA;AAAA;AAAA,QAK3C,CAACC,8BAAmB,GAClB,aAAa,OAAA,IACb,QAAA,KAAa,eACb,QAAA,KAAa,WAAA;AAAA,QAEb,aAAa,OAAA,IACb,CAAC,CAAC,QAAA,EAAU,SAAS,YAAY,CAAA;AAAA;AAAA,QAGnC,GAAI,KAAK,UAAA,IAAc;AAAA;AAAA;AAAA,UAGrB,CAACC,mBAAQ,GAAG,IAAA,CAAK,UAAA,GAAaC,oBAAS,CAAA,KAAM,aAAA,GAAgBC,uBAAA,CAAmB,OAAA,CAAQ,GAAG,CAAA,GAAI,MAAA;AAAA,UAC/F,CAAC,GAAGC,uCAA4B,CAAA,QAAA,CAAU,GAAG,OAAA,GAAU,CAAC,OAAO,CAAA,GAAI;AAAA;AACrE,OACD,CAAA;AAAA,IACH;AAAA,GACF;AACF,CAAC;;;;"}
@@ -2,13 +2,14 @@ Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
2
2
 
3
3
  const core = require('@sentry/core');
4
4
  const browserUtils = require('@sentry/browser-utils');
5
+ const attributes = require('@sentry/conventions/attributes');
5
6
  const op = require('@sentry/conventions/op');
6
7
  const debugBuild = require('../debug-build.js');
7
8
 
8
9
  const INTEGRATION_NAME = "SpotlightBrowser";
9
10
  const SPOTLIGHT_IGNORE_SPANS = [
10
11
  { op: op.UI_INTERACTION_CLICK, name: "#sentry-spotlight" },
11
- { op: op.UI_INTERACTION_CLICK, attributes: { "browser.web_vital.inp.target": "#sentry-spotlight" } }
12
+ { op: op.UI_INTERACTION_CLICK, attributes: { [attributes.BROWSER_WEB_VITAL_INP_TARGET]: "#sentry-spotlight" } }
12
13
  ];
13
14
  const _spotlightIntegration = ((options = {}) => {
14
15
  const sidecarUrl = options.sidecarUrl || "http://localhost:8969/stream";
@@ -1 +1 @@
1
- {"version":3,"file":"spotlight.js","sources":["../../../../../src/integrations/spotlight.ts"],"sourcesContent":["import type { Client, Envelope, IntegrationFn } from '@sentry/core';\nimport { debug, defineIntegration, serializeEnvelope } from '@sentry/core';\nimport { getNativeImplementation } from '@sentry/browser-utils';\nimport { UI_INTERACTION_CLICK } from '@sentry/conventions/op';\nimport { DEBUG_BUILD } from '../debug-build';\nimport type { WINDOW } from '../helpers';\n\nexport type SpotlightConnectionOptions = {\n /**\n * Set this if the Spotlight Sidecar is not running on localhost:8969\n * By default, the Url is set to http://localhost:8969/stream\n */\n sidecarUrl?: string;\n};\n\nexport const INTEGRATION_NAME = 'SpotlightBrowser' as const;\n\nexport const SPOTLIGHT_IGNORE_SPANS = [\n { op: UI_INTERACTION_CLICK, name: '#sentry-spotlight' },\n { op: UI_INTERACTION_CLICK, attributes: { 'browser.web_vital.inp.target': '#sentry-spotlight' } },\n];\n\nconst _spotlightIntegration = ((options: Partial<SpotlightConnectionOptions> = {}) => {\n const sidecarUrl = options.sidecarUrl || 'http://localhost:8969/stream';\n\n return {\n name: INTEGRATION_NAME,\n setup: () => {\n DEBUG_BUILD && debug.log('Using Sidecar URL', sidecarUrl);\n },\n beforeSetup(client: Client) {\n const opts = client.getOptions();\n opts.ignoreSpans = [...(opts.ignoreSpans || []), ...SPOTLIGHT_IGNORE_SPANS];\n },\n afterAllSetup: (client: Client) => {\n setupSidecarForwarding(client, sidecarUrl);\n },\n };\n}) satisfies IntegrationFn;\n\nfunction setupSidecarForwarding(client: Client, sidecarUrl: string): void {\n const makeFetch: typeof WINDOW.fetch | undefined = getNativeImplementation('fetch');\n let failCount = 0;\n\n client.on('beforeEnvelope', (envelope: Envelope) => {\n if (failCount > 3) {\n debug.warn('[Spotlight] Disabled Sentry -> Spotlight integration due to too many failed requests:', failCount);\n return;\n }\n\n makeFetch(sidecarUrl, {\n method: 'POST',\n body: serializeEnvelope(envelope) as BodyInit,\n headers: {\n 'Content-Type': 'application/x-sentry-envelope',\n },\n mode: 'cors',\n }).then(\n res => {\n if (res.status >= 200 && res.status < 400) {\n // Reset failed requests counter on success\n failCount = 0;\n }\n },\n err => {\n failCount++;\n debug.error(\n \"Sentry SDK can't connect to Sidecar is it running? See: https://spotlightjs.com/sidecar/npx/\",\n err,\n );\n },\n );\n });\n}\n\n/**\n * Use this integration to send errors and transactions to Spotlight.\n *\n * Learn more about spotlight at https://spotlightjs.com\n */\nexport const spotlightBrowserIntegration = defineIntegration(_spotlightIntegration);\n"],"names":["UI_INTERACTION_CLICK","DEBUG_BUILD","debug","getNativeImplementation","serializeEnvelope","defineIntegration"],"mappings":";;;;;;;AAeO,MAAM,gBAAA,GAAmB;AAEzB,MAAM,sBAAA,GAAyB;AAAA,EACpC,EAAE,EAAA,EAAIA,uBAAA,EAAsB,IAAA,EAAM,mBAAA,EAAoB;AAAA,EACtD,EAAE,EAAA,EAAIA,uBAAA,EAAsB,YAAY,EAAE,8BAAA,EAAgC,qBAAoB;AAChG;AAEA,MAAM,qBAAA,IAAyB,CAAC,OAAA,GAA+C,EAAC,KAAM;AACpF,EAAA,MAAM,UAAA,GAAa,QAAQ,UAAA,IAAc,8BAAA;AAEzC,EAAA,OAAO;AAAA,IACL,IAAA,EAAM,gBAAA;AAAA,IACN,OAAO,MAAM;AACX,MAAAC,sBAAA,IAAeC,UAAA,CAAM,GAAA,CAAI,mBAAA,EAAqB,UAAU,CAAA;AAAA,IAC1D,CAAA;AAAA,IACA,YAAY,MAAA,EAAgB;AAC1B,MAAA,MAAM,IAAA,GAAO,OAAO,UAAA,EAAW;AAC/B,MAAA,IAAA,CAAK,WAAA,GAAc,CAAC,GAAI,IAAA,CAAK,eAAe,EAAC,EAAI,GAAG,sBAAsB,CAAA;AAAA,IAC5E,CAAA;AAAA,IACA,aAAA,EAAe,CAAC,MAAA,KAAmB;AACjC,MAAA,sBAAA,CAAuB,QAAQ,UAAU,CAAA;AAAA,IAC3C;AAAA,GACF;AACF,CAAA,CAAA;AAEA,SAAS,sBAAA,CAAuB,QAAgB,UAAA,EAA0B;AACxE,EAAA,MAAM,SAAA,GAA6CC,qCAAwB,OAAO,CAAA;AAClF,EAAA,IAAI,SAAA,GAAY,CAAA;AAEhB,EAAA,MAAA,CAAO,EAAA,CAAG,gBAAA,EAAkB,CAAC,QAAA,KAAuB;AAClD,IAAA,IAAI,YAAY,CAAA,EAAG;AACjB,MAAAD,UAAA,CAAM,IAAA,CAAK,yFAAyF,SAAS,CAAA;AAC7G,MAAA;AAAA,IACF;AAEA,IAAA,SAAA,CAAU,UAAA,EAAY;AAAA,MACpB,MAAA,EAAQ,MAAA;AAAA,MACR,IAAA,EAAME,uBAAkB,QAAQ,CAAA;AAAA,MAChC,OAAA,EAAS;AAAA,QACP,cAAA,EAAgB;AAAA,OAClB;AAAA,MACA,IAAA,EAAM;AAAA,KACP,CAAA,CAAE,IAAA;AAAA,MACD,CAAA,GAAA,KAAO;AACL,QAAA,IAAI,GAAA,CAAI,MAAA,IAAU,GAAA,IAAO,GAAA,CAAI,SAAS,GAAA,EAAK;AAEzC,UAAA,SAAA,GAAY,CAAA;AAAA,QACd;AAAA,MACF,CAAA;AAAA,MACA,CAAA,GAAA,KAAO;AACL,QAAA,SAAA,EAAA;AACA,QAAAF,UAAA,CAAM,KAAA;AAAA,UACJ,8FAAA;AAAA,UACA;AAAA,SACF;AAAA,MACF;AAAA,KACF;AAAA,EACF,CAAC,CAAA;AACH;AAOO,MAAM,2BAAA,GAA8BG,uBAAkB,qBAAqB;;;;;;"}
1
+ {"version":3,"file":"spotlight.js","sources":["../../../../../src/integrations/spotlight.ts"],"sourcesContent":["import type { Client, Envelope, IntegrationFn } from '@sentry/core';\nimport { debug, defineIntegration, serializeEnvelope } from '@sentry/core';\nimport { getNativeImplementation } from '@sentry/browser-utils';\nimport { BROWSER_WEB_VITAL_INP_TARGET } from '@sentry/conventions/attributes';\nimport { UI_INTERACTION_CLICK } from '@sentry/conventions/op';\nimport { DEBUG_BUILD } from '../debug-build';\nimport type { WINDOW } from '../helpers';\n\nexport type SpotlightConnectionOptions = {\n /**\n * Set this if the Spotlight Sidecar is not running on localhost:8969\n * By default, the Url is set to http://localhost:8969/stream\n */\n sidecarUrl?: string;\n};\n\nexport const INTEGRATION_NAME = 'SpotlightBrowser' as const;\n\nexport const SPOTLIGHT_IGNORE_SPANS = [\n { op: UI_INTERACTION_CLICK, name: '#sentry-spotlight' },\n { op: UI_INTERACTION_CLICK, attributes: { [BROWSER_WEB_VITAL_INP_TARGET]: '#sentry-spotlight' } },\n];\n\nconst _spotlightIntegration = ((options: Partial<SpotlightConnectionOptions> = {}) => {\n const sidecarUrl = options.sidecarUrl || 'http://localhost:8969/stream';\n\n return {\n name: INTEGRATION_NAME,\n setup: () => {\n DEBUG_BUILD && debug.log('Using Sidecar URL', sidecarUrl);\n },\n beforeSetup(client: Client) {\n const opts = client.getOptions();\n opts.ignoreSpans = [...(opts.ignoreSpans || []), ...SPOTLIGHT_IGNORE_SPANS];\n },\n afterAllSetup: (client: Client) => {\n setupSidecarForwarding(client, sidecarUrl);\n },\n };\n}) satisfies IntegrationFn;\n\nfunction setupSidecarForwarding(client: Client, sidecarUrl: string): void {\n const makeFetch: typeof WINDOW.fetch | undefined = getNativeImplementation('fetch');\n let failCount = 0;\n\n client.on('beforeEnvelope', (envelope: Envelope) => {\n if (failCount > 3) {\n debug.warn('[Spotlight] Disabled Sentry -> Spotlight integration due to too many failed requests:', failCount);\n return;\n }\n\n makeFetch(sidecarUrl, {\n method: 'POST',\n body: serializeEnvelope(envelope) as BodyInit,\n headers: {\n 'Content-Type': 'application/x-sentry-envelope',\n },\n mode: 'cors',\n }).then(\n res => {\n if (res.status >= 200 && res.status < 400) {\n // Reset failed requests counter on success\n failCount = 0;\n }\n },\n err => {\n failCount++;\n debug.error(\n \"Sentry SDK can't connect to Sidecar is it running? See: https://spotlightjs.com/sidecar/npx/\",\n err,\n );\n },\n );\n });\n}\n\n/**\n * Use this integration to send errors and transactions to Spotlight.\n *\n * Learn more about spotlight at https://spotlightjs.com\n */\nexport const spotlightBrowserIntegration = defineIntegration(_spotlightIntegration);\n"],"names":["UI_INTERACTION_CLICK","BROWSER_WEB_VITAL_INP_TARGET","DEBUG_BUILD","debug","getNativeImplementation","serializeEnvelope","defineIntegration"],"mappings":";;;;;;;;AAgBO,MAAM,gBAAA,GAAmB;AAEzB,MAAM,sBAAA,GAAyB;AAAA,EACpC,EAAE,EAAA,EAAIA,uBAAA,EAAsB,IAAA,EAAM,mBAAA,EAAoB;AAAA,EACtD,EAAE,IAAIA,uBAAA,EAAsB,UAAA,EAAY,EAAE,CAACC,uCAA4B,GAAG,mBAAA,EAAoB;AAChG;AAEA,MAAM,qBAAA,IAAyB,CAAC,OAAA,GAA+C,EAAC,KAAM;AACpF,EAAA,MAAM,UAAA,GAAa,QAAQ,UAAA,IAAc,8BAAA;AAEzC,EAAA,OAAO;AAAA,IACL,IAAA,EAAM,gBAAA;AAAA,IACN,OAAO,MAAM;AACX,MAAAC,sBAAA,IAAeC,UAAA,CAAM,GAAA,CAAI,mBAAA,EAAqB,UAAU,CAAA;AAAA,IAC1D,CAAA;AAAA,IACA,YAAY,MAAA,EAAgB;AAC1B,MAAA,MAAM,IAAA,GAAO,OAAO,UAAA,EAAW;AAC/B,MAAA,IAAA,CAAK,WAAA,GAAc,CAAC,GAAI,IAAA,CAAK,eAAe,EAAC,EAAI,GAAG,sBAAsB,CAAA;AAAA,IAC5E,CAAA;AAAA,IACA,aAAA,EAAe,CAAC,MAAA,KAAmB;AACjC,MAAA,sBAAA,CAAuB,QAAQ,UAAU,CAAA;AAAA,IAC3C;AAAA,GACF;AACF,CAAA,CAAA;AAEA,SAAS,sBAAA,CAAuB,QAAgB,UAAA,EAA0B;AACxE,EAAA,MAAM,SAAA,GAA6CC,qCAAwB,OAAO,CAAA;AAClF,EAAA,IAAI,SAAA,GAAY,CAAA;AAEhB,EAAA,MAAA,CAAO,EAAA,CAAG,gBAAA,EAAkB,CAAC,QAAA,KAAuB;AAClD,IAAA,IAAI,YAAY,CAAA,EAAG;AACjB,MAAAD,UAAA,CAAM,IAAA,CAAK,yFAAyF,SAAS,CAAA;AAC7G,MAAA;AAAA,IACF;AAEA,IAAA,SAAA,CAAU,UAAA,EAAY;AAAA,MACpB,MAAA,EAAQ,MAAA;AAAA,MACR,IAAA,EAAME,uBAAkB,QAAQ,CAAA;AAAA,MAChC,OAAA,EAAS;AAAA,QACP,cAAA,EAAgB;AAAA,OAClB;AAAA,MACA,IAAA,EAAM;AAAA,KACP,CAAA,CAAE,IAAA;AAAA,MACD,CAAA,GAAA,KAAO;AACL,QAAA,IAAI,GAAA,CAAI,MAAA,IAAU,GAAA,IAAO,GAAA,CAAI,SAAS,GAAA,EAAK;AAEzC,UAAA,SAAA,GAAY,CAAA;AAAA,QACd;AAAA,MACF,CAAA;AAAA,MACA,CAAA,GAAA,KAAO;AACL,QAAA,SAAA,EAAA;AACA,QAAAF,UAAA,CAAM,KAAA;AAAA,UACJ,8FAAA;AAAA,UACA;AAAA,SACF;AAAA,MACF;AAAA,KACF;AAAA,EACF,CAAC,CAAA;AACH;AAOO,MAAM,2BAAA,GAA8BG,uBAAkB,qBAAqB;;;;;;"}
@@ -26,14 +26,24 @@ function showReportDialog(options = {}) {
26
26
  },
27
27
  eventId: options.eventId || core.lastEventId()
28
28
  };
29
+ const { onLoad, onClose, onError } = mergedOptions;
30
+ if (!mergedOptions.eventId) {
31
+ debugBuild.DEBUG_BUILD && core.debug.error("[showReportDialog] No event ID");
32
+ onError?.(new Error("No event ID to show the report dialog for"));
33
+ return;
34
+ }
29
35
  const script = helpers.WINDOW.document.createElement("script");
30
36
  script.async = true;
31
37
  script.crossOrigin = "anonymous";
32
38
  script.src = core.getReportDialogEndpoint(dsn, mergedOptions);
33
- const { onLoad, onClose } = mergedOptions;
34
39
  if (onLoad) {
35
40
  script.onload = onLoad;
36
41
  }
42
+ if (onError) {
43
+ script.onerror = () => {
44
+ onError(new Error("Failed to load the report dialog script"));
45
+ };
46
+ }
37
47
  if (onClose) {
38
48
  const reportDialogClosedMessageHandler = (event) => {
39
49
  if (event.data === "__sentry_reportdialog_closed__") {
@@ -45,6 +55,9 @@ function showReportDialog(options = {}) {
45
55
  }
46
56
  };
47
57
  helpers.WINDOW.addEventListener("message", reportDialogClosedMessageHandler);
58
+ script.addEventListener("error", () => {
59
+ helpers.WINDOW.removeEventListener("message", reportDialogClosedMessageHandler);
60
+ });
48
61
  }
49
62
  injectionPoint.appendChild(script);
50
63
  }
@@ -1 +1 @@
1
- {"version":3,"file":"report-dialog.js","sources":["../../../../src/report-dialog.ts"],"sourcesContent":["import type { ReportDialogOptions } from '@sentry/core';\nimport { debug, getClient, getCurrentScope, getReportDialogEndpoint, lastEventId } from '@sentry/core';\nimport { DEBUG_BUILD } from './debug-build';\nimport { WINDOW } from './helpers';\n\n/**\n * Present the user with a report dialog.\n *\n * @param options Everything is optional, we try to fetch all info need from the current scope.\n */\nexport function showReportDialog(options: ReportDialogOptions = {}): void {\n const optionalDocument = WINDOW.document as Document | undefined;\n const injectionPoint = optionalDocument?.head || optionalDocument?.body;\n\n // doesn't work without a document (React Native)\n if (!injectionPoint) {\n DEBUG_BUILD && debug.error('[showReportDialog] Global document not defined');\n return;\n }\n\n const scope = getCurrentScope();\n const client = getClient();\n const dsn = client?.getDsn();\n\n if (!dsn) {\n DEBUG_BUILD && debug.error('[showReportDialog] DSN not configured');\n return;\n }\n\n const mergedOptions = {\n ...options,\n user: {\n ...scope.getUser(),\n ...options.user,\n },\n eventId: options.eventId || lastEventId(),\n };\n\n const script = WINDOW.document.createElement('script');\n script.async = true;\n script.crossOrigin = 'anonymous';\n script.src = getReportDialogEndpoint(dsn, mergedOptions);\n\n const { onLoad, onClose } = mergedOptions;\n\n if (onLoad) {\n script.onload = onLoad;\n }\n\n if (onClose) {\n const reportDialogClosedMessageHandler = (event: MessageEvent): void => {\n if (event.data === '__sentry_reportdialog_closed__') {\n try {\n onClose();\n } finally {\n WINDOW.removeEventListener('message', reportDialogClosedMessageHandler);\n }\n }\n };\n WINDOW.addEventListener('message', reportDialogClosedMessageHandler);\n }\n\n injectionPoint.appendChild(script);\n}\n"],"names":["WINDOW","DEBUG_BUILD","debug","getCurrentScope","getClient","lastEventId","getReportDialogEndpoint"],"mappings":";;;;;;AAUO,SAAS,gBAAA,CAAiB,OAAA,GAA+B,EAAC,EAAS;AACxE,EAAA,MAAM,mBAAmBA,cAAA,CAAO,QAAA;AAChC,EAAA,MAAM,cAAA,GAAiB,gBAAA,EAAkB,IAAA,IAAQ,gBAAA,EAAkB,IAAA;AAGnE,EAAA,IAAI,CAAC,cAAA,EAAgB;AACnB,IAAAC,sBAAA,IAAeC,UAAA,CAAM,MAAM,gDAAgD,CAAA;AAC3E,IAAA;AAAA,EACF;AAEA,EAAA,MAAM,QAAQC,oBAAA,EAAgB;AAC9B,EAAA,MAAM,SAASC,cAAA,EAAU;AACzB,EAAA,MAAM,GAAA,GAAM,QAAQ,MAAA,EAAO;AAE3B,EAAA,IAAI,CAAC,GAAA,EAAK;AACR,IAAAH,sBAAA,IAAeC,UAAA,CAAM,MAAM,uCAAuC,CAAA;AAClE,IAAA;AAAA,EACF;AAEA,EAAA,MAAM,aAAA,GAAgB;AAAA,IACpB,GAAG,OAAA;AAAA,IACH,IAAA,EAAM;AAAA,MACJ,GAAG,MAAM,OAAA,EAAQ;AAAA,MACjB,GAAG,OAAA,CAAQ;AAAA,KACb;AAAA,IACA,OAAA,EAAS,OAAA,CAAQ,OAAA,IAAWG,gBAAA;AAAY,GAC1C;AAEA,EAAA,MAAM,MAAA,GAASL,cAAA,CAAO,QAAA,CAAS,aAAA,CAAc,QAAQ,CAAA;AACrD,EAAA,MAAA,CAAO,KAAA,GAAQ,IAAA;AACf,EAAA,MAAA,CAAO,WAAA,GAAc,WAAA;AACrB,EAAA,MAAA,CAAO,GAAA,GAAMM,4BAAA,CAAwB,GAAA,EAAK,aAAa,CAAA;AAEvD,EAAA,MAAM,EAAE,MAAA,EAAQ,OAAA,EAAQ,GAAI,aAAA;AAE5B,EAAA,IAAI,MAAA,EAAQ;AACV,IAAA,MAAA,CAAO,MAAA,GAAS,MAAA;AAAA,EAClB;AAEA,EAAA,IAAI,OAAA,EAAS;AACX,IAAA,MAAM,gCAAA,GAAmC,CAAC,KAAA,KAA8B;AACtE,MAAA,IAAI,KAAA,CAAM,SAAS,gCAAA,EAAkC;AACnD,QAAA,IAAI;AACF,UAAA,OAAA,EAAQ;AAAA,QACV,CAAA,SAAE;AACA,UAAAN,cAAA,CAAO,mBAAA,CAAoB,WAAW,gCAAgC,CAAA;AAAA,QACxE;AAAA,MACF;AAAA,IACF,CAAA;AACA,IAAAA,cAAA,CAAO,gBAAA,CAAiB,WAAW,gCAAgC,CAAA;AAAA,EACrE;AAEA,EAAA,cAAA,CAAe,YAAY,MAAM,CAAA;AACnC;;;;"}
1
+ {"version":3,"file":"report-dialog.js","sources":["../../../../src/report-dialog.ts"],"sourcesContent":["import type { ReportDialogOptions } from '@sentry/core';\nimport { debug, getClient, getCurrentScope, getReportDialogEndpoint, lastEventId } from '@sentry/core';\nimport { DEBUG_BUILD } from './debug-build';\nimport { WINDOW } from './helpers';\n\n/**\n * Present the user with a report dialog.\n *\n * @param options Everything is optional, we try to fetch all info need from the current scope.\n */\nexport function showReportDialog(options: ReportDialogOptions = {}): void {\n const optionalDocument = WINDOW.document as Document | undefined;\n const injectionPoint = optionalDocument?.head || optionalDocument?.body;\n\n // doesn't work without a document (React Native)\n if (!injectionPoint) {\n DEBUG_BUILD && debug.error('[showReportDialog] Global document not defined');\n return;\n }\n\n const scope = getCurrentScope();\n const client = getClient();\n const dsn = client?.getDsn();\n\n if (!dsn) {\n DEBUG_BUILD && debug.error('[showReportDialog] DSN not configured');\n return;\n }\n\n const mergedOptions = {\n ...options,\n user: {\n ...scope.getUser(),\n ...options.user,\n },\n eventId: options.eventId || lastEventId(),\n };\n\n const { onLoad, onClose, onError } = mergedOptions;\n\n // The endpoint rejects requests without an event ID, and a failed script load hides the reason\n if (!mergedOptions.eventId) {\n DEBUG_BUILD && debug.error('[showReportDialog] No event ID');\n onError?.(new Error('No event ID to show the report dialog for'));\n return;\n }\n\n const script = WINDOW.document.createElement('script');\n script.async = true;\n script.crossOrigin = 'anonymous';\n script.src = getReportDialogEndpoint(dsn, mergedOptions);\n\n if (onLoad) {\n script.onload = onLoad;\n }\n\n if (onError) {\n script.onerror = () => {\n onError(new Error('Failed to load the report dialog script'));\n };\n }\n\n if (onClose) {\n const reportDialogClosedMessageHandler = (event: MessageEvent): void => {\n if (event.data === '__sentry_reportdialog_closed__') {\n try {\n onClose();\n } finally {\n WINDOW.removeEventListener('message', reportDialogClosedMessageHandler);\n }\n }\n };\n WINDOW.addEventListener('message', reportDialogClosedMessageHandler);\n // A dialog that never loads never closes, so the listener would stay forever\n script.addEventListener('error', () => {\n WINDOW.removeEventListener('message', reportDialogClosedMessageHandler);\n });\n }\n\n injectionPoint.appendChild(script);\n}\n"],"names":["WINDOW","DEBUG_BUILD","debug","getCurrentScope","getClient","lastEventId","getReportDialogEndpoint"],"mappings":";;;;;;AAUO,SAAS,gBAAA,CAAiB,OAAA,GAA+B,EAAC,EAAS;AACxE,EAAA,MAAM,mBAAmBA,cAAA,CAAO,QAAA;AAChC,EAAA,MAAM,cAAA,GAAiB,gBAAA,EAAkB,IAAA,IAAQ,gBAAA,EAAkB,IAAA;AAGnE,EAAA,IAAI,CAAC,cAAA,EAAgB;AACnB,IAAAC,sBAAA,IAAeC,UAAA,CAAM,MAAM,gDAAgD,CAAA;AAC3E,IAAA;AAAA,EACF;AAEA,EAAA,MAAM,QAAQC,oBAAA,EAAgB;AAC9B,EAAA,MAAM,SAASC,cAAA,EAAU;AACzB,EAAA,MAAM,GAAA,GAAM,QAAQ,MAAA,EAAO;AAE3B,EAAA,IAAI,CAAC,GAAA,EAAK;AACR,IAAAH,sBAAA,IAAeC,UAAA,CAAM,MAAM,uCAAuC,CAAA;AAClE,IAAA;AAAA,EACF;AAEA,EAAA,MAAM,aAAA,GAAgB;AAAA,IACpB,GAAG,OAAA;AAAA,IACH,IAAA,EAAM;AAAA,MACJ,GAAG,MAAM,OAAA,EAAQ;AAAA,MACjB,GAAG,OAAA,CAAQ;AAAA,KACb;AAAA,IACA,OAAA,EAAS,OAAA,CAAQ,OAAA,IAAWG,gBAAA;AAAY,GAC1C;AAEA,EAAA,MAAM,EAAE,MAAA,EAAQ,OAAA,EAAS,OAAA,EAAQ,GAAI,aAAA;AAGrC,EAAA,IAAI,CAAC,cAAc,OAAA,EAAS;AAC1B,IAAAJ,sBAAA,IAAeC,UAAA,CAAM,MAAM,gCAAgC,CAAA;AAC3D,IAAA,OAAA,GAAU,IAAI,KAAA,CAAM,2CAA2C,CAAC,CAAA;AAChE,IAAA;AAAA,EACF;AAEA,EAAA,MAAM,MAAA,GAASF,cAAA,CAAO,QAAA,CAAS,aAAA,CAAc,QAAQ,CAAA;AACrD,EAAA,MAAA,CAAO,KAAA,GAAQ,IAAA;AACf,EAAA,MAAA,CAAO,WAAA,GAAc,WAAA;AACrB,EAAA,MAAA,CAAO,GAAA,GAAMM,4BAAA,CAAwB,GAAA,EAAK,aAAa,CAAA;AAEvD,EAAA,IAAI,MAAA,EAAQ;AACV,IAAA,MAAA,CAAO,MAAA,GAAS,MAAA;AAAA,EAClB;AAEA,EAAA,IAAI,OAAA,EAAS;AACX,IAAA,MAAA,CAAO,UAAU,MAAM;AACrB,MAAA,OAAA,CAAQ,IAAI,KAAA,CAAM,yCAAyC,CAAC,CAAA;AAAA,IAC9D,CAAA;AAAA,EACF;AAEA,EAAA,IAAI,OAAA,EAAS;AACX,IAAA,MAAM,gCAAA,GAAmC,CAAC,KAAA,KAA8B;AACtE,MAAA,IAAI,KAAA,CAAM,SAAS,gCAAA,EAAkC;AACnD,QAAA,IAAI;AACF,UAAA,OAAA,EAAQ;AAAA,QACV,CAAA,SAAE;AACA,UAAAN,cAAA,CAAO,mBAAA,CAAoB,WAAW,gCAAgC,CAAA;AAAA,QACxE;AAAA,MACF;AAAA,IACF,CAAA;AACA,IAAAA,cAAA,CAAO,gBAAA,CAAiB,WAAW,gCAAgC,CAAA;AAEnE,IAAA,MAAA,CAAO,gBAAA,CAAiB,SAAS,MAAM;AACrC,MAAAA,cAAA,CAAO,mBAAA,CAAoB,WAAW,gCAAgC,CAAA;AAAA,IACxE,CAAC,CAAA;AAAA,EACH;AAEA,EAAA,cAAA,CAAe,YAAY,MAAM,CAAA;AACnC;;;;"}
@@ -43,7 +43,7 @@ const httpContextIntegration = core.defineIntegration(() => {
43
43
  // Likewise for the "Filter out localhost events" feature. Deliberately inlined rather than
44
44
  // sharing the server-side helper, which costs bundle size for request headers and IPs that
45
45
  // don't exist here. Mirrors Relay's localhost filter (relay-filter/src/localhost.rs).
46
- "sentry.is_localhost": protocol === "file:" || hostname === "localhost" || hostname === "127.0.0.1" || // `location.hostname` keeps the brackets for IPv6 hosts, so this is `[::1]`, not `::1`.
46
+ [attributes.SENTRY_IS_LOCALHOST]: protocol === "file:" || hostname === "localhost" || hostname === "127.0.0.1" || // `location.hostname` keeps the brackets for IPv6 hosts, so this is `[::1]`, not `::1`.
47
47
  hostname === "[::1]" || !!hostname?.endsWith(".localhost"),
48
48
  // These attributes, we only need on the segment span (analogous to the `request` context for events)
49
49
  ...span.is_segment && {
@@ -1 +1 @@
1
- {"version":3,"file":"httpcontext.js","sources":["../../../../../src/integrations/httpcontext.ts"],"sourcesContent":["import { _INTERNAL_filterKeyValueData, defineIntegration, safeSetSpanJSONAttributes } from '@sentry/core';\nimport { getHttpRequestData, WINDOW } from '../helpers';\nimport { filterCollectedUrl } from '@sentry/core';\nimport { HTTP_REQUEST_HEADER_KEY_BASE, SENTRY_OP, URL_FULL, USER_AGENT_ORIGINAL } from '@sentry/conventions/attributes';\n\n/**\n * Collects information about HTTP request headers and\n * attaches them to the event.\n */\nexport const httpContextIntegration = defineIntegration(() => {\n return {\n name: 'HttpContext' as const,\n preprocessEvent(event, _hint, client) {\n // if none of the information we want exists, don't bother\n if (!WINDOW.navigator && !WINDOW.location && !WINDOW.document) {\n return;\n }\n\n const { url, headers: collectedHeaders } = getHttpRequestData();\n\n // We only filter the headers we collected ourselves, so headers the user set are left alone. Going by key\n // means we also catch these headers when `browserTracingIntegration` already put them on the event.\n const behavior = client.getDataCollectionOptions().httpHeaders.request;\n const userHeaders = Object.entries(event.request?.headers ?? {}).filter(([key]) => !(key in collectedHeaders));\n\n const headers = {\n ..._INTERNAL_filterKeyValueData(collectedHeaders, behavior),\n ...Object.fromEntries(userHeaders),\n };\n\n event.request = {\n // The URL isn't gated by `dataCollection`, same as on the server.\n url,\n ...event.request,\n ...(Object.keys(headers).length > 0 ? { headers } : { headers: undefined }),\n };\n },\n\n processSpan(span, client) {\n // if none of the information we want exists, don't bother\n if (!WINDOW.navigator && !WINDOW.location && !WINDOW.document) {\n return;\n }\n\n const reqData = getHttpRequestData();\n\n // `httpHeadersToSpanAttributes` would also work here, but its cookie and array handling never runs for these\n // two headers and costs every browser bundle ~400B gzip.\n const headers = _INTERNAL_filterKeyValueData(\n reqData.headers,\n client.getDataCollectionOptions().httpHeaders.request,\n );\n const referer = headers['Referer'];\n const { hostname, protocol } = WINDOW.location || {};\n\n safeSetSpanJSONAttributes(span, {\n // This attribute is used by the \"Filter out events from legacy browsers and crawlers\" features on the Sentry backend.\n // Therefore, it's set on every span.\n [USER_AGENT_ORIGINAL]: headers['User-Agent'],\n\n // Likewise for the \"Filter out localhost events\" feature. Deliberately inlined rather than\n // sharing the server-side helper, which costs bundle size for request headers and IPs that\n // don't exist here. Mirrors Relay's localhost filter (relay-filter/src/localhost.rs).\n 'sentry.is_localhost':\n protocol === 'file:' ||\n hostname === 'localhost' ||\n hostname === '127.0.0.1' ||\n // `location.hostname` keeps the brackets for IPv6 hosts, so this is `[::1]`, not `::1`.\n hostname === '[::1]' ||\n !!hostname?.endsWith('.localhost'),\n\n // These attributes, we only need on the segment span (analogous to the `request` context for events)\n ...(span.is_segment && {\n // Coerce empty string to undefined so the helper's nullish check drops it,\n // rather than writing an empty `url.full` attribute onto the span.\n [URL_FULL]: span.attributes?.[SENTRY_OP] !== 'http.client' ? filterCollectedUrl(reqData.url) : undefined,\n [`${HTTP_REQUEST_HEADER_KEY_BASE}.referer`]: referer ? [referer] : undefined,\n }),\n });\n },\n };\n});\n"],"names":["defineIntegration","WINDOW","getHttpRequestData","_INTERNAL_filterKeyValueData","safeSetSpanJSONAttributes","USER_AGENT_ORIGINAL","URL_FULL","SENTRY_OP","filterCollectedUrl","HTTP_REQUEST_HEADER_KEY_BASE"],"mappings":";;;;;;AASO,MAAM,sBAAA,GAAyBA,uBAAkB,MAAM;AAC5D,EAAA,OAAO;AAAA,IACL,IAAA,EAAM,aAAA;AAAA,IACN,eAAA,CAAgB,KAAA,EAAO,KAAA,EAAO,MAAA,EAAQ;AAEpC,MAAA,IAAI,CAACC,eAAO,SAAA,IAAa,CAACA,eAAO,QAAA,IAAY,CAACA,eAAO,QAAA,EAAU;AAC7D,QAAA;AAAA,MACF;AAEA,MAAA,MAAM,EAAE,GAAA,EAAK,OAAA,EAAS,gBAAA,KAAqBC,0BAAA,EAAmB;AAI9D,MAAA,MAAM,QAAA,GAAW,MAAA,CAAO,wBAAA,EAAyB,CAAE,WAAA,CAAY,OAAA;AAC/D,MAAA,MAAM,cAAc,MAAA,CAAO,OAAA,CAAQ,KAAA,CAAM,OAAA,EAAS,WAAW,EAAE,CAAA,CAAE,MAAA,CAAO,CAAC,CAAC,GAAG,CAAA,KAAM,EAAE,OAAO,gBAAA,CAAiB,CAAA;AAE7G,MAAA,MAAM,OAAA,GAAU;AAAA,QACd,GAAGC,iCAAA,CAA6B,gBAAA,EAAkB,QAAQ,CAAA;AAAA,QAC1D,GAAG,MAAA,CAAO,WAAA,CAAY,WAAW;AAAA,OACnC;AAEA,MAAA,KAAA,CAAM,OAAA,GAAU;AAAA;AAAA,QAEd,GAAA;AAAA,QACA,GAAG,KAAA,CAAM,OAAA;AAAA,QACT,GAAI,MAAA,CAAO,IAAA,CAAK,OAAO,CAAA,CAAE,MAAA,GAAS,CAAA,GAAI,EAAE,OAAA,EAAQ,GAAI,EAAE,OAAA,EAAS,MAAA;AAAU,OAC3E;AAAA,IACF,CAAA;AAAA,IAEA,WAAA,CAAY,MAAM,MAAA,EAAQ;AAExB,MAAA,IAAI,CAACF,eAAO,SAAA,IAAa,CAACA,eAAO,QAAA,IAAY,CAACA,eAAO,QAAA,EAAU;AAC7D,QAAA;AAAA,MACF;AAEA,MAAA,MAAM,UAAUC,0BAAA,EAAmB;AAInC,MAAA,MAAM,OAAA,GAAUC,iCAAA;AAAA,QACd,OAAA,CAAQ,OAAA;AAAA,QACR,MAAA,CAAO,wBAAA,EAAyB,CAAE,WAAA,CAAY;AAAA,OAChD;AACA,MAAA,MAAM,OAAA,GAAU,QAAQ,SAAS,CAAA;AACjC,MAAA,MAAM,EAAE,QAAA,EAAU,QAAA,EAAS,GAAIF,cAAA,CAAO,YAAY,EAAC;AAEnD,MAAAG,8BAAA,CAA0B,IAAA,EAAM;AAAA;AAAA;AAAA,QAG9B,CAACC,8BAAmB,GAAG,OAAA,CAAQ,YAAY,CAAA;AAAA;AAAA;AAAA;AAAA,QAK3C,qBAAA,EACE,QAAA,KAAa,OAAA,IACb,QAAA,KAAa,eACb,QAAA,KAAa,WAAA;AAAA,QAEb,aAAa,OAAA,IACb,CAAC,CAAC,QAAA,EAAU,SAAS,YAAY,CAAA;AAAA;AAAA,QAGnC,GAAI,KAAK,UAAA,IAAc;AAAA;AAAA;AAAA,UAGrB,CAACC,mBAAQ,GAAG,IAAA,CAAK,UAAA,GAAaC,oBAAS,CAAA,KAAM,aAAA,GAAgBC,uBAAA,CAAmB,OAAA,CAAQ,GAAG,CAAA,GAAI,MAAA;AAAA,UAC/F,CAAC,GAAGC,uCAA4B,CAAA,QAAA,CAAU,GAAG,OAAA,GAAU,CAAC,OAAO,CAAA,GAAI;AAAA;AACrE,OACD,CAAA;AAAA,IACH;AAAA,GACF;AACF,CAAC;;;;"}
1
+ {"version":3,"file":"httpcontext.js","sources":["../../../../../src/integrations/httpcontext.ts"],"sourcesContent":["import { _INTERNAL_filterKeyValueData, defineIntegration, safeSetSpanJSONAttributes } from '@sentry/core';\nimport { getHttpRequestData, WINDOW } from '../helpers';\nimport { filterCollectedUrl } from '@sentry/core';\nimport {\n HTTP_REQUEST_HEADER_KEY_BASE,\n SENTRY_IS_LOCALHOST,\n SENTRY_OP,\n URL_FULL,\n USER_AGENT_ORIGINAL,\n} from '@sentry/conventions/attributes';\n\n/**\n * Collects information about HTTP request headers and\n * attaches them to the event.\n */\nexport const httpContextIntegration = defineIntegration(() => {\n return {\n name: 'HttpContext' as const,\n preprocessEvent(event, _hint, client) {\n // if none of the information we want exists, don't bother\n if (!WINDOW.navigator && !WINDOW.location && !WINDOW.document) {\n return;\n }\n\n const { url, headers: collectedHeaders } = getHttpRequestData();\n\n // We only filter the headers we collected ourselves, so headers the user set are left alone. Going by key\n // means we also catch these headers when `browserTracingIntegration` already put them on the event.\n const behavior = client.getDataCollectionOptions().httpHeaders.request;\n const userHeaders = Object.entries(event.request?.headers ?? {}).filter(([key]) => !(key in collectedHeaders));\n\n const headers = {\n ..._INTERNAL_filterKeyValueData(collectedHeaders, behavior),\n ...Object.fromEntries(userHeaders),\n };\n\n event.request = {\n // The URL isn't gated by `dataCollection`, same as on the server.\n url,\n ...event.request,\n ...(Object.keys(headers).length > 0 ? { headers } : { headers: undefined }),\n };\n },\n\n processSpan(span, client) {\n // if none of the information we want exists, don't bother\n if (!WINDOW.navigator && !WINDOW.location && !WINDOW.document) {\n return;\n }\n\n const reqData = getHttpRequestData();\n\n // `httpHeadersToSpanAttributes` would also work here, but its cookie and array handling never runs for these\n // two headers and costs every browser bundle ~400B gzip.\n const headers = _INTERNAL_filterKeyValueData(\n reqData.headers,\n client.getDataCollectionOptions().httpHeaders.request,\n );\n const referer = headers['Referer'];\n const { hostname, protocol } = WINDOW.location || {};\n\n safeSetSpanJSONAttributes(span, {\n // This attribute is used by the \"Filter out events from legacy browsers and crawlers\" features on the Sentry backend.\n // Therefore, it's set on every span.\n [USER_AGENT_ORIGINAL]: headers['User-Agent'],\n\n // Likewise for the \"Filter out localhost events\" feature. Deliberately inlined rather than\n // sharing the server-side helper, which costs bundle size for request headers and IPs that\n // don't exist here. Mirrors Relay's localhost filter (relay-filter/src/localhost.rs).\n [SENTRY_IS_LOCALHOST]:\n protocol === 'file:' ||\n hostname === 'localhost' ||\n hostname === '127.0.0.1' ||\n // `location.hostname` keeps the brackets for IPv6 hosts, so this is `[::1]`, not `::1`.\n hostname === '[::1]' ||\n !!hostname?.endsWith('.localhost'),\n\n // These attributes, we only need on the segment span (analogous to the `request` context for events)\n ...(span.is_segment && {\n // Coerce empty string to undefined so the helper's nullish check drops it,\n // rather than writing an empty `url.full` attribute onto the span.\n [URL_FULL]: span.attributes?.[SENTRY_OP] !== 'http.client' ? filterCollectedUrl(reqData.url) : undefined,\n [`${HTTP_REQUEST_HEADER_KEY_BASE}.referer`]: referer ? [referer] : undefined,\n }),\n });\n },\n };\n});\n"],"names":["defineIntegration","WINDOW","getHttpRequestData","_INTERNAL_filterKeyValueData","safeSetSpanJSONAttributes","USER_AGENT_ORIGINAL","SENTRY_IS_LOCALHOST","URL_FULL","SENTRY_OP","filterCollectedUrl","HTTP_REQUEST_HEADER_KEY_BASE"],"mappings":";;;;;;AAeO,MAAM,sBAAA,GAAyBA,uBAAkB,MAAM;AAC5D,EAAA,OAAO;AAAA,IACL,IAAA,EAAM,aAAA;AAAA,IACN,eAAA,CAAgB,KAAA,EAAO,KAAA,EAAO,MAAA,EAAQ;AAEpC,MAAA,IAAI,CAACC,eAAO,SAAA,IAAa,CAACA,eAAO,QAAA,IAAY,CAACA,eAAO,QAAA,EAAU;AAC7D,QAAA;AAAA,MACF;AAEA,MAAA,MAAM,EAAE,GAAA,EAAK,OAAA,EAAS,gBAAA,KAAqBC,0BAAA,EAAmB;AAI9D,MAAA,MAAM,QAAA,GAAW,MAAA,CAAO,wBAAA,EAAyB,CAAE,WAAA,CAAY,OAAA;AAC/D,MAAA,MAAM,cAAc,MAAA,CAAO,OAAA,CAAQ,KAAA,CAAM,OAAA,EAAS,WAAW,EAAE,CAAA,CAAE,MAAA,CAAO,CAAC,CAAC,GAAG,CAAA,KAAM,EAAE,OAAO,gBAAA,CAAiB,CAAA;AAE7G,MAAA,MAAM,OAAA,GAAU;AAAA,QACd,GAAGC,iCAAA,CAA6B,gBAAA,EAAkB,QAAQ,CAAA;AAAA,QAC1D,GAAG,MAAA,CAAO,WAAA,CAAY,WAAW;AAAA,OACnC;AAEA,MAAA,KAAA,CAAM,OAAA,GAAU;AAAA;AAAA,QAEd,GAAA;AAAA,QACA,GAAG,KAAA,CAAM,OAAA;AAAA,QACT,GAAI,MAAA,CAAO,IAAA,CAAK,OAAO,CAAA,CAAE,MAAA,GAAS,CAAA,GAAI,EAAE,OAAA,EAAQ,GAAI,EAAE,OAAA,EAAS,MAAA;AAAU,OAC3E;AAAA,IACF,CAAA;AAAA,IAEA,WAAA,CAAY,MAAM,MAAA,EAAQ;AAExB,MAAA,IAAI,CAACF,eAAO,SAAA,IAAa,CAACA,eAAO,QAAA,IAAY,CAACA,eAAO,QAAA,EAAU;AAC7D,QAAA;AAAA,MACF;AAEA,MAAA,MAAM,UAAUC,0BAAA,EAAmB;AAInC,MAAA,MAAM,OAAA,GAAUC,iCAAA;AAAA,QACd,OAAA,CAAQ,OAAA;AAAA,QACR,MAAA,CAAO,wBAAA,EAAyB,CAAE,WAAA,CAAY;AAAA,OAChD;AACA,MAAA,MAAM,OAAA,GAAU,QAAQ,SAAS,CAAA;AACjC,MAAA,MAAM,EAAE,QAAA,EAAU,QAAA,EAAS,GAAIF,cAAA,CAAO,YAAY,EAAC;AAEnD,MAAAG,8BAAA,CAA0B,IAAA,EAAM;AAAA;AAAA;AAAA,QAG9B,CAACC,8BAAmB,GAAG,OAAA,CAAQ,YAAY,CAAA;AAAA;AAAA;AAAA;AAAA,QAK3C,CAACC,8BAAmB,GAClB,aAAa,OAAA,IACb,QAAA,KAAa,eACb,QAAA,KAAa,WAAA;AAAA,QAEb,aAAa,OAAA,IACb,CAAC,CAAC,QAAA,EAAU,SAAS,YAAY,CAAA;AAAA;AAAA,QAGnC,GAAI,KAAK,UAAA,IAAc;AAAA;AAAA;AAAA,UAGrB,CAACC,mBAAQ,GAAG,IAAA,CAAK,UAAA,GAAaC,oBAAS,CAAA,KAAM,aAAA,GAAgBC,uBAAA,CAAmB,OAAA,CAAQ,GAAG,CAAA,GAAI,MAAA;AAAA,UAC/F,CAAC,GAAGC,uCAA4B,CAAA,QAAA,CAAU,GAAG,OAAA,GAAU,CAAC,OAAO,CAAA,GAAI;AAAA;AACrE,OACD,CAAA;AAAA,IACH;AAAA,GACF;AACF,CAAC;;;;"}
@@ -2,13 +2,14 @@ Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
2
2
 
3
3
  const core = require('@sentry/core');
4
4
  const browserUtils = require('@sentry/browser-utils');
5
+ const attributes = require('@sentry/conventions/attributes');
5
6
  const op = require('@sentry/conventions/op');
6
7
  const debugBuild = require('../debug-build.js');
7
8
 
8
9
  const INTEGRATION_NAME = "SpotlightBrowser";
9
10
  const SPOTLIGHT_IGNORE_SPANS = [
10
11
  { op: op.UI_INTERACTION_CLICK, name: "#sentry-spotlight" },
11
- { op: op.UI_INTERACTION_CLICK, attributes: { "browser.web_vital.inp.target": "#sentry-spotlight" } }
12
+ { op: op.UI_INTERACTION_CLICK, attributes: { [attributes.BROWSER_WEB_VITAL_INP_TARGET]: "#sentry-spotlight" } }
12
13
  ];
13
14
  const _spotlightIntegration = ((options = {}) => {
14
15
  const sidecarUrl = options.sidecarUrl || "http://localhost:8969/stream";
@@ -1 +1 @@
1
- {"version":3,"file":"spotlight.js","sources":["../../../../../src/integrations/spotlight.ts"],"sourcesContent":["import type { Client, Envelope, IntegrationFn } from '@sentry/core';\nimport { debug, defineIntegration, serializeEnvelope } from '@sentry/core';\nimport { getNativeImplementation } from '@sentry/browser-utils';\nimport { UI_INTERACTION_CLICK } from '@sentry/conventions/op';\nimport { DEBUG_BUILD } from '../debug-build';\nimport type { WINDOW } from '../helpers';\n\nexport type SpotlightConnectionOptions = {\n /**\n * Set this if the Spotlight Sidecar is not running on localhost:8969\n * By default, the Url is set to http://localhost:8969/stream\n */\n sidecarUrl?: string;\n};\n\nexport const INTEGRATION_NAME = 'SpotlightBrowser' as const;\n\nexport const SPOTLIGHT_IGNORE_SPANS = [\n { op: UI_INTERACTION_CLICK, name: '#sentry-spotlight' },\n { op: UI_INTERACTION_CLICK, attributes: { 'browser.web_vital.inp.target': '#sentry-spotlight' } },\n];\n\nconst _spotlightIntegration = ((options: Partial<SpotlightConnectionOptions> = {}) => {\n const sidecarUrl = options.sidecarUrl || 'http://localhost:8969/stream';\n\n return {\n name: INTEGRATION_NAME,\n setup: () => {\n DEBUG_BUILD && debug.log('Using Sidecar URL', sidecarUrl);\n },\n beforeSetup(client: Client) {\n const opts = client.getOptions();\n opts.ignoreSpans = [...(opts.ignoreSpans || []), ...SPOTLIGHT_IGNORE_SPANS];\n },\n afterAllSetup: (client: Client) => {\n setupSidecarForwarding(client, sidecarUrl);\n },\n };\n}) satisfies IntegrationFn;\n\nfunction setupSidecarForwarding(client: Client, sidecarUrl: string): void {\n const makeFetch: typeof WINDOW.fetch | undefined = getNativeImplementation('fetch');\n let failCount = 0;\n\n client.on('beforeEnvelope', (envelope: Envelope) => {\n if (failCount > 3) {\n debug.warn('[Spotlight] Disabled Sentry -> Spotlight integration due to too many failed requests:', failCount);\n return;\n }\n\n makeFetch(sidecarUrl, {\n method: 'POST',\n body: serializeEnvelope(envelope) as BodyInit,\n headers: {\n 'Content-Type': 'application/x-sentry-envelope',\n },\n mode: 'cors',\n }).then(\n res => {\n if (res.status >= 200 && res.status < 400) {\n // Reset failed requests counter on success\n failCount = 0;\n }\n },\n err => {\n failCount++;\n debug.error(\n \"Sentry SDK can't connect to Sidecar is it running? See: https://spotlightjs.com/sidecar/npx/\",\n err,\n );\n },\n );\n });\n}\n\n/**\n * Use this integration to send errors and transactions to Spotlight.\n *\n * Learn more about spotlight at https://spotlightjs.com\n */\nexport const spotlightBrowserIntegration = defineIntegration(_spotlightIntegration);\n"],"names":["UI_INTERACTION_CLICK","DEBUG_BUILD","debug","getNativeImplementation","serializeEnvelope","defineIntegration"],"mappings":";;;;;;;AAeO,MAAM,gBAAA,GAAmB;AAEzB,MAAM,sBAAA,GAAyB;AAAA,EACpC,EAAE,EAAA,EAAIA,uBAAA,EAAsB,IAAA,EAAM,mBAAA,EAAoB;AAAA,EACtD,EAAE,EAAA,EAAIA,uBAAA,EAAsB,YAAY,EAAE,8BAAA,EAAgC,qBAAoB;AAChG;AAEA,MAAM,qBAAA,IAAyB,CAAC,OAAA,GAA+C,EAAC,KAAM;AACpF,EAAA,MAAM,UAAA,GAAa,QAAQ,UAAA,IAAc,8BAAA;AAEzC,EAAA,OAAO;AAAA,IACL,IAAA,EAAM,gBAAA;AAAA,IACN,OAAO,MAAM;AACX,MAAAC,sBAAA,IAAeC,UAAA,CAAM,GAAA,CAAI,mBAAA,EAAqB,UAAU,CAAA;AAAA,IAC1D,CAAA;AAAA,IACA,YAAY,MAAA,EAAgB;AAC1B,MAAA,MAAM,IAAA,GAAO,OAAO,UAAA,EAAW;AAC/B,MAAA,IAAA,CAAK,WAAA,GAAc,CAAC,GAAI,IAAA,CAAK,eAAe,EAAC,EAAI,GAAG,sBAAsB,CAAA;AAAA,IAC5E,CAAA;AAAA,IACA,aAAA,EAAe,CAAC,MAAA,KAAmB;AACjC,MAAA,sBAAA,CAAuB,QAAQ,UAAU,CAAA;AAAA,IAC3C;AAAA,GACF;AACF,CAAA,CAAA;AAEA,SAAS,sBAAA,CAAuB,QAAgB,UAAA,EAA0B;AACxE,EAAA,MAAM,SAAA,GAA6CC,qCAAwB,OAAO,CAAA;AAClF,EAAA,IAAI,SAAA,GAAY,CAAA;AAEhB,EAAA,MAAA,CAAO,EAAA,CAAG,gBAAA,EAAkB,CAAC,QAAA,KAAuB;AAClD,IAAA,IAAI,YAAY,CAAA,EAAG;AACjB,MAAAD,UAAA,CAAM,IAAA,CAAK,yFAAyF,SAAS,CAAA;AAC7G,MAAA;AAAA,IACF;AAEA,IAAA,SAAA,CAAU,UAAA,EAAY;AAAA,MACpB,MAAA,EAAQ,MAAA;AAAA,MACR,IAAA,EAAME,uBAAkB,QAAQ,CAAA;AAAA,MAChC,OAAA,EAAS;AAAA,QACP,cAAA,EAAgB;AAAA,OAClB;AAAA,MACA,IAAA,EAAM;AAAA,KACP,CAAA,CAAE,IAAA;AAAA,MACD,CAAA,GAAA,KAAO;AACL,QAAA,IAAI,GAAA,CAAI,MAAA,IAAU,GAAA,IAAO,GAAA,CAAI,SAAS,GAAA,EAAK;AAEzC,UAAA,SAAA,GAAY,CAAA;AAAA,QACd;AAAA,MACF,CAAA;AAAA,MACA,CAAA,GAAA,KAAO;AACL,QAAA,SAAA,EAAA;AACA,QAAAF,UAAA,CAAM,KAAA;AAAA,UACJ,8FAAA;AAAA,UACA;AAAA,SACF;AAAA,MACF;AAAA,KACF;AAAA,EACF,CAAC,CAAA;AACH;AAOO,MAAM,2BAAA,GAA8BG,uBAAkB,qBAAqB;;;;;;"}
1
+ {"version":3,"file":"spotlight.js","sources":["../../../../../src/integrations/spotlight.ts"],"sourcesContent":["import type { Client, Envelope, IntegrationFn } from '@sentry/core';\nimport { debug, defineIntegration, serializeEnvelope } from '@sentry/core';\nimport { getNativeImplementation } from '@sentry/browser-utils';\nimport { BROWSER_WEB_VITAL_INP_TARGET } from '@sentry/conventions/attributes';\nimport { UI_INTERACTION_CLICK } from '@sentry/conventions/op';\nimport { DEBUG_BUILD } from '../debug-build';\nimport type { WINDOW } from '../helpers';\n\nexport type SpotlightConnectionOptions = {\n /**\n * Set this if the Spotlight Sidecar is not running on localhost:8969\n * By default, the Url is set to http://localhost:8969/stream\n */\n sidecarUrl?: string;\n};\n\nexport const INTEGRATION_NAME = 'SpotlightBrowser' as const;\n\nexport const SPOTLIGHT_IGNORE_SPANS = [\n { op: UI_INTERACTION_CLICK, name: '#sentry-spotlight' },\n { op: UI_INTERACTION_CLICK, attributes: { [BROWSER_WEB_VITAL_INP_TARGET]: '#sentry-spotlight' } },\n];\n\nconst _spotlightIntegration = ((options: Partial<SpotlightConnectionOptions> = {}) => {\n const sidecarUrl = options.sidecarUrl || 'http://localhost:8969/stream';\n\n return {\n name: INTEGRATION_NAME,\n setup: () => {\n DEBUG_BUILD && debug.log('Using Sidecar URL', sidecarUrl);\n },\n beforeSetup(client: Client) {\n const opts = client.getOptions();\n opts.ignoreSpans = [...(opts.ignoreSpans || []), ...SPOTLIGHT_IGNORE_SPANS];\n },\n afterAllSetup: (client: Client) => {\n setupSidecarForwarding(client, sidecarUrl);\n },\n };\n}) satisfies IntegrationFn;\n\nfunction setupSidecarForwarding(client: Client, sidecarUrl: string): void {\n const makeFetch: typeof WINDOW.fetch | undefined = getNativeImplementation('fetch');\n let failCount = 0;\n\n client.on('beforeEnvelope', (envelope: Envelope) => {\n if (failCount > 3) {\n debug.warn('[Spotlight] Disabled Sentry -> Spotlight integration due to too many failed requests:', failCount);\n return;\n }\n\n makeFetch(sidecarUrl, {\n method: 'POST',\n body: serializeEnvelope(envelope) as BodyInit,\n headers: {\n 'Content-Type': 'application/x-sentry-envelope',\n },\n mode: 'cors',\n }).then(\n res => {\n if (res.status >= 200 && res.status < 400) {\n // Reset failed requests counter on success\n failCount = 0;\n }\n },\n err => {\n failCount++;\n debug.error(\n \"Sentry SDK can't connect to Sidecar is it running? See: https://spotlightjs.com/sidecar/npx/\",\n err,\n );\n },\n );\n });\n}\n\n/**\n * Use this integration to send errors and transactions to Spotlight.\n *\n * Learn more about spotlight at https://spotlightjs.com\n */\nexport const spotlightBrowserIntegration = defineIntegration(_spotlightIntegration);\n"],"names":["UI_INTERACTION_CLICK","BROWSER_WEB_VITAL_INP_TARGET","DEBUG_BUILD","debug","getNativeImplementation","serializeEnvelope","defineIntegration"],"mappings":";;;;;;;;AAgBO,MAAM,gBAAA,GAAmB;AAEzB,MAAM,sBAAA,GAAyB;AAAA,EACpC,EAAE,EAAA,EAAIA,uBAAA,EAAsB,IAAA,EAAM,mBAAA,EAAoB;AAAA,EACtD,EAAE,IAAIA,uBAAA,EAAsB,UAAA,EAAY,EAAE,CAACC,uCAA4B,GAAG,mBAAA,EAAoB;AAChG;AAEA,MAAM,qBAAA,IAAyB,CAAC,OAAA,GAA+C,EAAC,KAAM;AACpF,EAAA,MAAM,UAAA,GAAa,QAAQ,UAAA,IAAc,8BAAA;AAEzC,EAAA,OAAO;AAAA,IACL,IAAA,EAAM,gBAAA;AAAA,IACN,OAAO,MAAM;AACX,MAAAC,sBAAA,IAAeC,UAAA,CAAM,GAAA,CAAI,mBAAA,EAAqB,UAAU,CAAA;AAAA,IAC1D,CAAA;AAAA,IACA,YAAY,MAAA,EAAgB;AAC1B,MAAA,MAAM,IAAA,GAAO,OAAO,UAAA,EAAW;AAC/B,MAAA,IAAA,CAAK,WAAA,GAAc,CAAC,GAAI,IAAA,CAAK,eAAe,EAAC,EAAI,GAAG,sBAAsB,CAAA;AAAA,IAC5E,CAAA;AAAA,IACA,aAAA,EAAe,CAAC,MAAA,KAAmB;AACjC,MAAA,sBAAA,CAAuB,QAAQ,UAAU,CAAA;AAAA,IAC3C;AAAA,GACF;AACF,CAAA,CAAA;AAEA,SAAS,sBAAA,CAAuB,QAAgB,UAAA,EAA0B;AACxE,EAAA,MAAM,SAAA,GAA6CC,qCAAwB,OAAO,CAAA;AAClF,EAAA,IAAI,SAAA,GAAY,CAAA;AAEhB,EAAA,MAAA,CAAO,EAAA,CAAG,gBAAA,EAAkB,CAAC,QAAA,KAAuB;AAClD,IAAA,IAAI,YAAY,CAAA,EAAG;AACjB,MAAAD,UAAA,CAAM,IAAA,CAAK,yFAAyF,SAAS,CAAA;AAC7G,MAAA;AAAA,IACF;AAEA,IAAA,SAAA,CAAU,UAAA,EAAY;AAAA,MACpB,MAAA,EAAQ,MAAA;AAAA,MACR,IAAA,EAAME,uBAAkB,QAAQ,CAAA;AAAA,MAChC,OAAA,EAAS;AAAA,QACP,cAAA,EAAgB;AAAA,OAClB;AAAA,MACA,IAAA,EAAM;AAAA,KACP,CAAA,CAAE,IAAA;AAAA,MACD,CAAA,GAAA,KAAO;AACL,QAAA,IAAI,GAAA,CAAI,MAAA,IAAU,GAAA,IAAO,GAAA,CAAI,SAAS,GAAA,EAAK;AAEzC,UAAA,SAAA,GAAY,CAAA;AAAA,QACd;AAAA,MACF,CAAA;AAAA,MACA,CAAA,GAAA,KAAO;AACL,QAAA,SAAA,EAAA;AACA,QAAAF,UAAA,CAAM,KAAA;AAAA,UACJ,8FAAA;AAAA,UACA;AAAA,SACF;AAAA,MACF;AAAA,KACF;AAAA,EACF,CAAC,CAAA;AACH;AAOO,MAAM,2BAAA,GAA8BG,uBAAkB,qBAAqB;;;;;;"}
@@ -26,14 +26,24 @@ function showReportDialog(options = {}) {
26
26
  },
27
27
  eventId: options.eventId || core.lastEventId()
28
28
  };
29
+ const { onLoad, onClose, onError } = mergedOptions;
30
+ if (!mergedOptions.eventId) {
31
+ debugBuild.DEBUG_BUILD && core.debug.error("[showReportDialog] No event ID");
32
+ onError?.(new Error("No event ID to show the report dialog for"));
33
+ return;
34
+ }
29
35
  const script = helpers.WINDOW.document.createElement("script");
30
36
  script.async = true;
31
37
  script.crossOrigin = "anonymous";
32
38
  script.src = core.getReportDialogEndpoint(dsn, mergedOptions);
33
- const { onLoad, onClose } = mergedOptions;
34
39
  if (onLoad) {
35
40
  script.onload = onLoad;
36
41
  }
42
+ if (onError) {
43
+ script.onerror = () => {
44
+ onError(new Error("Failed to load the report dialog script"));
45
+ };
46
+ }
37
47
  if (onClose) {
38
48
  const reportDialogClosedMessageHandler = (event) => {
39
49
  if (event.data === "__sentry_reportdialog_closed__") {
@@ -45,6 +55,9 @@ function showReportDialog(options = {}) {
45
55
  }
46
56
  };
47
57
  helpers.WINDOW.addEventListener("message", reportDialogClosedMessageHandler);
58
+ script.addEventListener("error", () => {
59
+ helpers.WINDOW.removeEventListener("message", reportDialogClosedMessageHandler);
60
+ });
48
61
  }
49
62
  injectionPoint.appendChild(script);
50
63
  }
@@ -1 +1 @@
1
- {"version":3,"file":"report-dialog.js","sources":["../../../../src/report-dialog.ts"],"sourcesContent":["import type { ReportDialogOptions } from '@sentry/core';\nimport { debug, getClient, getCurrentScope, getReportDialogEndpoint, lastEventId } from '@sentry/core';\nimport { DEBUG_BUILD } from './debug-build';\nimport { WINDOW } from './helpers';\n\n/**\n * Present the user with a report dialog.\n *\n * @param options Everything is optional, we try to fetch all info need from the current scope.\n */\nexport function showReportDialog(options: ReportDialogOptions = {}): void {\n const optionalDocument = WINDOW.document as Document | undefined;\n const injectionPoint = optionalDocument?.head || optionalDocument?.body;\n\n // doesn't work without a document (React Native)\n if (!injectionPoint) {\n DEBUG_BUILD && debug.error('[showReportDialog] Global document not defined');\n return;\n }\n\n const scope = getCurrentScope();\n const client = getClient();\n const dsn = client?.getDsn();\n\n if (!dsn) {\n DEBUG_BUILD && debug.error('[showReportDialog] DSN not configured');\n return;\n }\n\n const mergedOptions = {\n ...options,\n user: {\n ...scope.getUser(),\n ...options.user,\n },\n eventId: options.eventId || lastEventId(),\n };\n\n const script = WINDOW.document.createElement('script');\n script.async = true;\n script.crossOrigin = 'anonymous';\n script.src = getReportDialogEndpoint(dsn, mergedOptions);\n\n const { onLoad, onClose } = mergedOptions;\n\n if (onLoad) {\n script.onload = onLoad;\n }\n\n if (onClose) {\n const reportDialogClosedMessageHandler = (event: MessageEvent): void => {\n if (event.data === '__sentry_reportdialog_closed__') {\n try {\n onClose();\n } finally {\n WINDOW.removeEventListener('message', reportDialogClosedMessageHandler);\n }\n }\n };\n WINDOW.addEventListener('message', reportDialogClosedMessageHandler);\n }\n\n injectionPoint.appendChild(script);\n}\n"],"names":["WINDOW","DEBUG_BUILD","debug","getCurrentScope","getClient","lastEventId","getReportDialogEndpoint"],"mappings":";;;;;;AAUO,SAAS,gBAAA,CAAiB,OAAA,GAA+B,EAAC,EAAS;AACxE,EAAA,MAAM,mBAAmBA,cAAA,CAAO,QAAA;AAChC,EAAA,MAAM,cAAA,GAAiB,gBAAA,EAAkB,IAAA,IAAQ,gBAAA,EAAkB,IAAA;AAGnE,EAAA,IAAI,CAAC,cAAA,EAAgB;AACnB,IAAAC,sBAAA,IAAeC,UAAA,CAAM,MAAM,gDAAgD,CAAA;AAC3E,IAAA;AAAA,EACF;AAEA,EAAA,MAAM,QAAQC,oBAAA,EAAgB;AAC9B,EAAA,MAAM,SAASC,cAAA,EAAU;AACzB,EAAA,MAAM,GAAA,GAAM,QAAQ,MAAA,EAAO;AAE3B,EAAA,IAAI,CAAC,GAAA,EAAK;AACR,IAAAH,sBAAA,IAAeC,UAAA,CAAM,MAAM,uCAAuC,CAAA;AAClE,IAAA;AAAA,EACF;AAEA,EAAA,MAAM,aAAA,GAAgB;AAAA,IACpB,GAAG,OAAA;AAAA,IACH,IAAA,EAAM;AAAA,MACJ,GAAG,MAAM,OAAA,EAAQ;AAAA,MACjB,GAAG,OAAA,CAAQ;AAAA,KACb;AAAA,IACA,OAAA,EAAS,OAAA,CAAQ,OAAA,IAAWG,gBAAA;AAAY,GAC1C;AAEA,EAAA,MAAM,MAAA,GAASL,cAAA,CAAO,QAAA,CAAS,aAAA,CAAc,QAAQ,CAAA;AACrD,EAAA,MAAA,CAAO,KAAA,GAAQ,IAAA;AACf,EAAA,MAAA,CAAO,WAAA,GAAc,WAAA;AACrB,EAAA,MAAA,CAAO,GAAA,GAAMM,4BAAA,CAAwB,GAAA,EAAK,aAAa,CAAA;AAEvD,EAAA,MAAM,EAAE,MAAA,EAAQ,OAAA,EAAQ,GAAI,aAAA;AAE5B,EAAA,IAAI,MAAA,EAAQ;AACV,IAAA,MAAA,CAAO,MAAA,GAAS,MAAA;AAAA,EAClB;AAEA,EAAA,IAAI,OAAA,EAAS;AACX,IAAA,MAAM,gCAAA,GAAmC,CAAC,KAAA,KAA8B;AACtE,MAAA,IAAI,KAAA,CAAM,SAAS,gCAAA,EAAkC;AACnD,QAAA,IAAI;AACF,UAAA,OAAA,EAAQ;AAAA,QACV,CAAA,SAAE;AACA,UAAAN,cAAA,CAAO,mBAAA,CAAoB,WAAW,gCAAgC,CAAA;AAAA,QACxE;AAAA,MACF;AAAA,IACF,CAAA;AACA,IAAAA,cAAA,CAAO,gBAAA,CAAiB,WAAW,gCAAgC,CAAA;AAAA,EACrE;AAEA,EAAA,cAAA,CAAe,YAAY,MAAM,CAAA;AACnC;;;;"}
1
+ {"version":3,"file":"report-dialog.js","sources":["../../../../src/report-dialog.ts"],"sourcesContent":["import type { ReportDialogOptions } from '@sentry/core';\nimport { debug, getClient, getCurrentScope, getReportDialogEndpoint, lastEventId } from '@sentry/core';\nimport { DEBUG_BUILD } from './debug-build';\nimport { WINDOW } from './helpers';\n\n/**\n * Present the user with a report dialog.\n *\n * @param options Everything is optional, we try to fetch all info need from the current scope.\n */\nexport function showReportDialog(options: ReportDialogOptions = {}): void {\n const optionalDocument = WINDOW.document as Document | undefined;\n const injectionPoint = optionalDocument?.head || optionalDocument?.body;\n\n // doesn't work without a document (React Native)\n if (!injectionPoint) {\n DEBUG_BUILD && debug.error('[showReportDialog] Global document not defined');\n return;\n }\n\n const scope = getCurrentScope();\n const client = getClient();\n const dsn = client?.getDsn();\n\n if (!dsn) {\n DEBUG_BUILD && debug.error('[showReportDialog] DSN not configured');\n return;\n }\n\n const mergedOptions = {\n ...options,\n user: {\n ...scope.getUser(),\n ...options.user,\n },\n eventId: options.eventId || lastEventId(),\n };\n\n const { onLoad, onClose, onError } = mergedOptions;\n\n // The endpoint rejects requests without an event ID, and a failed script load hides the reason\n if (!mergedOptions.eventId) {\n DEBUG_BUILD && debug.error('[showReportDialog] No event ID');\n onError?.(new Error('No event ID to show the report dialog for'));\n return;\n }\n\n const script = WINDOW.document.createElement('script');\n script.async = true;\n script.crossOrigin = 'anonymous';\n script.src = getReportDialogEndpoint(dsn, mergedOptions);\n\n if (onLoad) {\n script.onload = onLoad;\n }\n\n if (onError) {\n script.onerror = () => {\n onError(new Error('Failed to load the report dialog script'));\n };\n }\n\n if (onClose) {\n const reportDialogClosedMessageHandler = (event: MessageEvent): void => {\n if (event.data === '__sentry_reportdialog_closed__') {\n try {\n onClose();\n } finally {\n WINDOW.removeEventListener('message', reportDialogClosedMessageHandler);\n }\n }\n };\n WINDOW.addEventListener('message', reportDialogClosedMessageHandler);\n // A dialog that never loads never closes, so the listener would stay forever\n script.addEventListener('error', () => {\n WINDOW.removeEventListener('message', reportDialogClosedMessageHandler);\n });\n }\n\n injectionPoint.appendChild(script);\n}\n"],"names":["WINDOW","DEBUG_BUILD","debug","getCurrentScope","getClient","lastEventId","getReportDialogEndpoint"],"mappings":";;;;;;AAUO,SAAS,gBAAA,CAAiB,OAAA,GAA+B,EAAC,EAAS;AACxE,EAAA,MAAM,mBAAmBA,cAAA,CAAO,QAAA;AAChC,EAAA,MAAM,cAAA,GAAiB,gBAAA,EAAkB,IAAA,IAAQ,gBAAA,EAAkB,IAAA;AAGnE,EAAA,IAAI,CAAC,cAAA,EAAgB;AACnB,IAAAC,sBAAA,IAAeC,UAAA,CAAM,MAAM,gDAAgD,CAAA;AAC3E,IAAA;AAAA,EACF;AAEA,EAAA,MAAM,QAAQC,oBAAA,EAAgB;AAC9B,EAAA,MAAM,SAASC,cAAA,EAAU;AACzB,EAAA,MAAM,GAAA,GAAM,QAAQ,MAAA,EAAO;AAE3B,EAAA,IAAI,CAAC,GAAA,EAAK;AACR,IAAAH,sBAAA,IAAeC,UAAA,CAAM,MAAM,uCAAuC,CAAA;AAClE,IAAA;AAAA,EACF;AAEA,EAAA,MAAM,aAAA,GAAgB;AAAA,IACpB,GAAG,OAAA;AAAA,IACH,IAAA,EAAM;AAAA,MACJ,GAAG,MAAM,OAAA,EAAQ;AAAA,MACjB,GAAG,OAAA,CAAQ;AAAA,KACb;AAAA,IACA,OAAA,EAAS,OAAA,CAAQ,OAAA,IAAWG,gBAAA;AAAY,GAC1C;AAEA,EAAA,MAAM,EAAE,MAAA,EAAQ,OAAA,EAAS,OAAA,EAAQ,GAAI,aAAA;AAGrC,EAAA,IAAI,CAAC,cAAc,OAAA,EAAS;AAC1B,IAAAJ,sBAAA,IAAeC,UAAA,CAAM,MAAM,gCAAgC,CAAA;AAC3D,IAAA,OAAA,GAAU,IAAI,KAAA,CAAM,2CAA2C,CAAC,CAAA;AAChE,IAAA;AAAA,EACF;AAEA,EAAA,MAAM,MAAA,GAASF,cAAA,CAAO,QAAA,CAAS,aAAA,CAAc,QAAQ,CAAA;AACrD,EAAA,MAAA,CAAO,KAAA,GAAQ,IAAA;AACf,EAAA,MAAA,CAAO,WAAA,GAAc,WAAA;AACrB,EAAA,MAAA,CAAO,GAAA,GAAMM,4BAAA,CAAwB,GAAA,EAAK,aAAa,CAAA;AAEvD,EAAA,IAAI,MAAA,EAAQ;AACV,IAAA,MAAA,CAAO,MAAA,GAAS,MAAA;AAAA,EAClB;AAEA,EAAA,IAAI,OAAA,EAAS;AACX,IAAA,MAAA,CAAO,UAAU,MAAM;AACrB,MAAA,OAAA,CAAQ,IAAI,KAAA,CAAM,yCAAyC,CAAC,CAAA;AAAA,IAC9D,CAAA;AAAA,EACF;AAEA,EAAA,IAAI,OAAA,EAAS;AACX,IAAA,MAAM,gCAAA,GAAmC,CAAC,KAAA,KAA8B;AACtE,MAAA,IAAI,KAAA,CAAM,SAAS,gCAAA,EAAkC;AACnD,QAAA,IAAI;AACF,UAAA,OAAA,EAAQ;AAAA,QACV,CAAA,SAAE;AACA,UAAAN,cAAA,CAAO,mBAAA,CAAoB,WAAW,gCAAgC,CAAA;AAAA,QACxE;AAAA,MACF;AAAA,IACF,CAAA;AACA,IAAAA,cAAA,CAAO,gBAAA,CAAiB,WAAW,gCAAgC,CAAA;AAEnE,IAAA,MAAA,CAAO,gBAAA,CAAiB,SAAS,MAAM;AACrC,MAAAA,cAAA,CAAO,mBAAA,CAAoB,WAAW,gCAAgC,CAAA;AAAA,IACxE,CAAC,CAAA;AAAA,EACH;AAEA,EAAA,cAAA,CAAe,YAAY,MAAM,CAAA;AACnC;;;;"}
@@ -1,6 +1,6 @@
1
1
  import { defineIntegration, _INTERNAL_filterKeyValueData, safeSetSpanJSONAttributes, filterCollectedUrl } from '@sentry/core';
2
2
  import { WINDOW, getHttpRequestData } from '../helpers.js';
3
- import { HTTP_REQUEST_HEADER_KEY_BASE, URL_FULL, SENTRY_OP, USER_AGENT_ORIGINAL } from '@sentry/conventions/attributes';
3
+ import { HTTP_REQUEST_HEADER_KEY_BASE, URL_FULL, SENTRY_OP, SENTRY_IS_LOCALHOST, USER_AGENT_ORIGINAL } from '@sentry/conventions/attributes';
4
4
 
5
5
  const httpContextIntegration = defineIntegration(() => {
6
6
  return {
@@ -41,7 +41,7 @@ const httpContextIntegration = defineIntegration(() => {
41
41
  // Likewise for the "Filter out localhost events" feature. Deliberately inlined rather than
42
42
  // sharing the server-side helper, which costs bundle size for request headers and IPs that
43
43
  // don't exist here. Mirrors Relay's localhost filter (relay-filter/src/localhost.rs).
44
- "sentry.is_localhost": protocol === "file:" || hostname === "localhost" || hostname === "127.0.0.1" || // `location.hostname` keeps the brackets for IPv6 hosts, so this is `[::1]`, not `::1`.
44
+ [SENTRY_IS_LOCALHOST]: protocol === "file:" || hostname === "localhost" || hostname === "127.0.0.1" || // `location.hostname` keeps the brackets for IPv6 hosts, so this is `[::1]`, not `::1`.
45
45
  hostname === "[::1]" || !!hostname?.endsWith(".localhost"),
46
46
  // These attributes, we only need on the segment span (analogous to the `request` context for events)
47
47
  ...span.is_segment && {
@@ -1 +1 @@
1
- {"version":3,"file":"httpcontext.js","sources":["../../../../../src/integrations/httpcontext.ts"],"sourcesContent":["import { _INTERNAL_filterKeyValueData, defineIntegration, safeSetSpanJSONAttributes } from '@sentry/core';\nimport { getHttpRequestData, WINDOW } from '../helpers';\nimport { filterCollectedUrl } from '@sentry/core';\nimport { HTTP_REQUEST_HEADER_KEY_BASE, SENTRY_OP, URL_FULL, USER_AGENT_ORIGINAL } from '@sentry/conventions/attributes';\n\n/**\n * Collects information about HTTP request headers and\n * attaches them to the event.\n */\nexport const httpContextIntegration = defineIntegration(() => {\n return {\n name: 'HttpContext' as const,\n preprocessEvent(event, _hint, client) {\n // if none of the information we want exists, don't bother\n if (!WINDOW.navigator && !WINDOW.location && !WINDOW.document) {\n return;\n }\n\n const { url, headers: collectedHeaders } = getHttpRequestData();\n\n // We only filter the headers we collected ourselves, so headers the user set are left alone. Going by key\n // means we also catch these headers when `browserTracingIntegration` already put them on the event.\n const behavior = client.getDataCollectionOptions().httpHeaders.request;\n const userHeaders = Object.entries(event.request?.headers ?? {}).filter(([key]) => !(key in collectedHeaders));\n\n const headers = {\n ..._INTERNAL_filterKeyValueData(collectedHeaders, behavior),\n ...Object.fromEntries(userHeaders),\n };\n\n event.request = {\n // The URL isn't gated by `dataCollection`, same as on the server.\n url,\n ...event.request,\n ...(Object.keys(headers).length > 0 ? { headers } : { headers: undefined }),\n };\n },\n\n processSpan(span, client) {\n // if none of the information we want exists, don't bother\n if (!WINDOW.navigator && !WINDOW.location && !WINDOW.document) {\n return;\n }\n\n const reqData = getHttpRequestData();\n\n // `httpHeadersToSpanAttributes` would also work here, but its cookie and array handling never runs for these\n // two headers and costs every browser bundle ~400B gzip.\n const headers = _INTERNAL_filterKeyValueData(\n reqData.headers,\n client.getDataCollectionOptions().httpHeaders.request,\n );\n const referer = headers['Referer'];\n const { hostname, protocol } = WINDOW.location || {};\n\n safeSetSpanJSONAttributes(span, {\n // This attribute is used by the \"Filter out events from legacy browsers and crawlers\" features on the Sentry backend.\n // Therefore, it's set on every span.\n [USER_AGENT_ORIGINAL]: headers['User-Agent'],\n\n // Likewise for the \"Filter out localhost events\" feature. Deliberately inlined rather than\n // sharing the server-side helper, which costs bundle size for request headers and IPs that\n // don't exist here. Mirrors Relay's localhost filter (relay-filter/src/localhost.rs).\n 'sentry.is_localhost':\n protocol === 'file:' ||\n hostname === 'localhost' ||\n hostname === '127.0.0.1' ||\n // `location.hostname` keeps the brackets for IPv6 hosts, so this is `[::1]`, not `::1`.\n hostname === '[::1]' ||\n !!hostname?.endsWith('.localhost'),\n\n // These attributes, we only need on the segment span (analogous to the `request` context for events)\n ...(span.is_segment && {\n // Coerce empty string to undefined so the helper's nullish check drops it,\n // rather than writing an empty `url.full` attribute onto the span.\n [URL_FULL]: span.attributes?.[SENTRY_OP] !== 'http.client' ? filterCollectedUrl(reqData.url) : undefined,\n [`${HTTP_REQUEST_HEADER_KEY_BASE}.referer`]: referer ? [referer] : undefined,\n }),\n });\n },\n };\n});\n"],"names":[],"mappings":";;;;AASO,MAAM,sBAAA,GAAyB,kBAAkB,MAAM;AAC5D,EAAA,OAAO;AAAA,IACL,IAAA,EAAM,aAAA;AAAA,IACN,eAAA,CAAgB,KAAA,EAAO,KAAA,EAAO,MAAA,EAAQ;AAEpC,MAAA,IAAI,CAAC,OAAO,SAAA,IAAa,CAAC,OAAO,QAAA,IAAY,CAAC,OAAO,QAAA,EAAU;AAC7D,QAAA;AAAA,MACF;AAEA,MAAA,MAAM,EAAE,GAAA,EAAK,OAAA,EAAS,gBAAA,KAAqB,kBAAA,EAAmB;AAI9D,MAAA,MAAM,QAAA,GAAW,MAAA,CAAO,wBAAA,EAAyB,CAAE,WAAA,CAAY,OAAA;AAC/D,MAAA,MAAM,cAAc,MAAA,CAAO,OAAA,CAAQ,KAAA,CAAM,OAAA,EAAS,WAAW,EAAE,CAAA,CAAE,MAAA,CAAO,CAAC,CAAC,GAAG,CAAA,KAAM,EAAE,OAAO,gBAAA,CAAiB,CAAA;AAE7G,MAAA,MAAM,OAAA,GAAU;AAAA,QACd,GAAG,4BAAA,CAA6B,gBAAA,EAAkB,QAAQ,CAAA;AAAA,QAC1D,GAAG,MAAA,CAAO,WAAA,CAAY,WAAW;AAAA,OACnC;AAEA,MAAA,KAAA,CAAM,OAAA,GAAU;AAAA;AAAA,QAEd,GAAA;AAAA,QACA,GAAG,KAAA,CAAM,OAAA;AAAA,QACT,GAAI,MAAA,CAAO,IAAA,CAAK,OAAO,CAAA,CAAE,MAAA,GAAS,CAAA,GAAI,EAAE,OAAA,EAAQ,GAAI,EAAE,OAAA,EAAS,MAAA;AAAU,OAC3E;AAAA,IACF,CAAA;AAAA,IAEA,WAAA,CAAY,MAAM,MAAA,EAAQ;AAExB,MAAA,IAAI,CAAC,OAAO,SAAA,IAAa,CAAC,OAAO,QAAA,IAAY,CAAC,OAAO,QAAA,EAAU;AAC7D,QAAA;AAAA,MACF;AAEA,MAAA,MAAM,UAAU,kBAAA,EAAmB;AAInC,MAAA,MAAM,OAAA,GAAU,4BAAA;AAAA,QACd,OAAA,CAAQ,OAAA;AAAA,QACR,MAAA,CAAO,wBAAA,EAAyB,CAAE,WAAA,CAAY;AAAA,OAChD;AACA,MAAA,MAAM,OAAA,GAAU,QAAQ,SAAS,CAAA;AACjC,MAAA,MAAM,EAAE,QAAA,EAAU,QAAA,EAAS,GAAI,MAAA,CAAO,YAAY,EAAC;AAEnD,MAAA,yBAAA,CAA0B,IAAA,EAAM;AAAA;AAAA;AAAA,QAG9B,CAAC,mBAAmB,GAAG,OAAA,CAAQ,YAAY,CAAA;AAAA;AAAA;AAAA;AAAA,QAK3C,qBAAA,EACE,QAAA,KAAa,OAAA,IACb,QAAA,KAAa,eACb,QAAA,KAAa,WAAA;AAAA,QAEb,aAAa,OAAA,IACb,CAAC,CAAC,QAAA,EAAU,SAAS,YAAY,CAAA;AAAA;AAAA,QAGnC,GAAI,KAAK,UAAA,IAAc;AAAA;AAAA;AAAA,UAGrB,CAAC,QAAQ,GAAG,IAAA,CAAK,UAAA,GAAa,SAAS,CAAA,KAAM,aAAA,GAAgB,kBAAA,CAAmB,OAAA,CAAQ,GAAG,CAAA,GAAI,MAAA;AAAA,UAC/F,CAAC,GAAG,4BAA4B,CAAA,QAAA,CAAU,GAAG,OAAA,GAAU,CAAC,OAAO,CAAA,GAAI;AAAA;AACrE,OACD,CAAA;AAAA,IACH;AAAA,GACF;AACF,CAAC;;;;"}
1
+ {"version":3,"file":"httpcontext.js","sources":["../../../../../src/integrations/httpcontext.ts"],"sourcesContent":["import { _INTERNAL_filterKeyValueData, defineIntegration, safeSetSpanJSONAttributes } from '@sentry/core';\nimport { getHttpRequestData, WINDOW } from '../helpers';\nimport { filterCollectedUrl } from '@sentry/core';\nimport {\n HTTP_REQUEST_HEADER_KEY_BASE,\n SENTRY_IS_LOCALHOST,\n SENTRY_OP,\n URL_FULL,\n USER_AGENT_ORIGINAL,\n} from '@sentry/conventions/attributes';\n\n/**\n * Collects information about HTTP request headers and\n * attaches them to the event.\n */\nexport const httpContextIntegration = defineIntegration(() => {\n return {\n name: 'HttpContext' as const,\n preprocessEvent(event, _hint, client) {\n // if none of the information we want exists, don't bother\n if (!WINDOW.navigator && !WINDOW.location && !WINDOW.document) {\n return;\n }\n\n const { url, headers: collectedHeaders } = getHttpRequestData();\n\n // We only filter the headers we collected ourselves, so headers the user set are left alone. Going by key\n // means we also catch these headers when `browserTracingIntegration` already put them on the event.\n const behavior = client.getDataCollectionOptions().httpHeaders.request;\n const userHeaders = Object.entries(event.request?.headers ?? {}).filter(([key]) => !(key in collectedHeaders));\n\n const headers = {\n ..._INTERNAL_filterKeyValueData(collectedHeaders, behavior),\n ...Object.fromEntries(userHeaders),\n };\n\n event.request = {\n // The URL isn't gated by `dataCollection`, same as on the server.\n url,\n ...event.request,\n ...(Object.keys(headers).length > 0 ? { headers } : { headers: undefined }),\n };\n },\n\n processSpan(span, client) {\n // if none of the information we want exists, don't bother\n if (!WINDOW.navigator && !WINDOW.location && !WINDOW.document) {\n return;\n }\n\n const reqData = getHttpRequestData();\n\n // `httpHeadersToSpanAttributes` would also work here, but its cookie and array handling never runs for these\n // two headers and costs every browser bundle ~400B gzip.\n const headers = _INTERNAL_filterKeyValueData(\n reqData.headers,\n client.getDataCollectionOptions().httpHeaders.request,\n );\n const referer = headers['Referer'];\n const { hostname, protocol } = WINDOW.location || {};\n\n safeSetSpanJSONAttributes(span, {\n // This attribute is used by the \"Filter out events from legacy browsers and crawlers\" features on the Sentry backend.\n // Therefore, it's set on every span.\n [USER_AGENT_ORIGINAL]: headers['User-Agent'],\n\n // Likewise for the \"Filter out localhost events\" feature. Deliberately inlined rather than\n // sharing the server-side helper, which costs bundle size for request headers and IPs that\n // don't exist here. Mirrors Relay's localhost filter (relay-filter/src/localhost.rs).\n [SENTRY_IS_LOCALHOST]:\n protocol === 'file:' ||\n hostname === 'localhost' ||\n hostname === '127.0.0.1' ||\n // `location.hostname` keeps the brackets for IPv6 hosts, so this is `[::1]`, not `::1`.\n hostname === '[::1]' ||\n !!hostname?.endsWith('.localhost'),\n\n // These attributes, we only need on the segment span (analogous to the `request` context for events)\n ...(span.is_segment && {\n // Coerce empty string to undefined so the helper's nullish check drops it,\n // rather than writing an empty `url.full` attribute onto the span.\n [URL_FULL]: span.attributes?.[SENTRY_OP] !== 'http.client' ? filterCollectedUrl(reqData.url) : undefined,\n [`${HTTP_REQUEST_HEADER_KEY_BASE}.referer`]: referer ? [referer] : undefined,\n }),\n });\n },\n };\n});\n"],"names":[],"mappings":";;;;AAeO,MAAM,sBAAA,GAAyB,kBAAkB,MAAM;AAC5D,EAAA,OAAO;AAAA,IACL,IAAA,EAAM,aAAA;AAAA,IACN,eAAA,CAAgB,KAAA,EAAO,KAAA,EAAO,MAAA,EAAQ;AAEpC,MAAA,IAAI,CAAC,OAAO,SAAA,IAAa,CAAC,OAAO,QAAA,IAAY,CAAC,OAAO,QAAA,EAAU;AAC7D,QAAA;AAAA,MACF;AAEA,MAAA,MAAM,EAAE,GAAA,EAAK,OAAA,EAAS,gBAAA,KAAqB,kBAAA,EAAmB;AAI9D,MAAA,MAAM,QAAA,GAAW,MAAA,CAAO,wBAAA,EAAyB,CAAE,WAAA,CAAY,OAAA;AAC/D,MAAA,MAAM,cAAc,MAAA,CAAO,OAAA,CAAQ,KAAA,CAAM,OAAA,EAAS,WAAW,EAAE,CAAA,CAAE,MAAA,CAAO,CAAC,CAAC,GAAG,CAAA,KAAM,EAAE,OAAO,gBAAA,CAAiB,CAAA;AAE7G,MAAA,MAAM,OAAA,GAAU;AAAA,QACd,GAAG,4BAAA,CAA6B,gBAAA,EAAkB,QAAQ,CAAA;AAAA,QAC1D,GAAG,MAAA,CAAO,WAAA,CAAY,WAAW;AAAA,OACnC;AAEA,MAAA,KAAA,CAAM,OAAA,GAAU;AAAA;AAAA,QAEd,GAAA;AAAA,QACA,GAAG,KAAA,CAAM,OAAA;AAAA,QACT,GAAI,MAAA,CAAO,IAAA,CAAK,OAAO,CAAA,CAAE,MAAA,GAAS,CAAA,GAAI,EAAE,OAAA,EAAQ,GAAI,EAAE,OAAA,EAAS,MAAA;AAAU,OAC3E;AAAA,IACF,CAAA;AAAA,IAEA,WAAA,CAAY,MAAM,MAAA,EAAQ;AAExB,MAAA,IAAI,CAAC,OAAO,SAAA,IAAa,CAAC,OAAO,QAAA,IAAY,CAAC,OAAO,QAAA,EAAU;AAC7D,QAAA;AAAA,MACF;AAEA,MAAA,MAAM,UAAU,kBAAA,EAAmB;AAInC,MAAA,MAAM,OAAA,GAAU,4BAAA;AAAA,QACd,OAAA,CAAQ,OAAA;AAAA,QACR,MAAA,CAAO,wBAAA,EAAyB,CAAE,WAAA,CAAY;AAAA,OAChD;AACA,MAAA,MAAM,OAAA,GAAU,QAAQ,SAAS,CAAA;AACjC,MAAA,MAAM,EAAE,QAAA,EAAU,QAAA,EAAS,GAAI,MAAA,CAAO,YAAY,EAAC;AAEnD,MAAA,yBAAA,CAA0B,IAAA,EAAM;AAAA;AAAA;AAAA,QAG9B,CAAC,mBAAmB,GAAG,OAAA,CAAQ,YAAY,CAAA;AAAA;AAAA;AAAA;AAAA,QAK3C,CAAC,mBAAmB,GAClB,aAAa,OAAA,IACb,QAAA,KAAa,eACb,QAAA,KAAa,WAAA;AAAA,QAEb,aAAa,OAAA,IACb,CAAC,CAAC,QAAA,EAAU,SAAS,YAAY,CAAA;AAAA;AAAA,QAGnC,GAAI,KAAK,UAAA,IAAc;AAAA;AAAA;AAAA,UAGrB,CAAC,QAAQ,GAAG,IAAA,CAAK,UAAA,GAAa,SAAS,CAAA,KAAM,aAAA,GAAgB,kBAAA,CAAmB,OAAA,CAAQ,GAAG,CAAA,GAAI,MAAA;AAAA,UAC/F,CAAC,GAAG,4BAA4B,CAAA,QAAA,CAAU,GAAG,OAAA,GAAU,CAAC,OAAO,CAAA,GAAI;AAAA;AACrE,OACD,CAAA;AAAA,IACH;AAAA,GACF;AACF,CAAC;;;;"}
@@ -1,12 +1,13 @@
1
1
  import { defineIntegration, debug, serializeEnvelope } from '@sentry/core';
2
2
  import { getNativeImplementation } from '@sentry/browser-utils';
3
+ import { BROWSER_WEB_VITAL_INP_TARGET } from '@sentry/conventions/attributes';
3
4
  import { UI_INTERACTION_CLICK } from '@sentry/conventions/op';
4
5
  import { DEBUG_BUILD } from '../debug-build.js';
5
6
 
6
7
  const INTEGRATION_NAME = "SpotlightBrowser";
7
8
  const SPOTLIGHT_IGNORE_SPANS = [
8
9
  { op: UI_INTERACTION_CLICK, name: "#sentry-spotlight" },
9
- { op: UI_INTERACTION_CLICK, attributes: { "browser.web_vital.inp.target": "#sentry-spotlight" } }
10
+ { op: UI_INTERACTION_CLICK, attributes: { [BROWSER_WEB_VITAL_INP_TARGET]: "#sentry-spotlight" } }
10
11
  ];
11
12
  const _spotlightIntegration = ((options = {}) => {
12
13
  const sidecarUrl = options.sidecarUrl || "http://localhost:8969/stream";
@@ -1 +1 @@
1
- {"version":3,"file":"spotlight.js","sources":["../../../../../src/integrations/spotlight.ts"],"sourcesContent":["import type { Client, Envelope, IntegrationFn } from '@sentry/core';\nimport { debug, defineIntegration, serializeEnvelope } from '@sentry/core';\nimport { getNativeImplementation } from '@sentry/browser-utils';\nimport { UI_INTERACTION_CLICK } from '@sentry/conventions/op';\nimport { DEBUG_BUILD } from '../debug-build';\nimport type { WINDOW } from '../helpers';\n\nexport type SpotlightConnectionOptions = {\n /**\n * Set this if the Spotlight Sidecar is not running on localhost:8969\n * By default, the Url is set to http://localhost:8969/stream\n */\n sidecarUrl?: string;\n};\n\nexport const INTEGRATION_NAME = 'SpotlightBrowser' as const;\n\nexport const SPOTLIGHT_IGNORE_SPANS = [\n { op: UI_INTERACTION_CLICK, name: '#sentry-spotlight' },\n { op: UI_INTERACTION_CLICK, attributes: { 'browser.web_vital.inp.target': '#sentry-spotlight' } },\n];\n\nconst _spotlightIntegration = ((options: Partial<SpotlightConnectionOptions> = {}) => {\n const sidecarUrl = options.sidecarUrl || 'http://localhost:8969/stream';\n\n return {\n name: INTEGRATION_NAME,\n setup: () => {\n DEBUG_BUILD && debug.log('Using Sidecar URL', sidecarUrl);\n },\n beforeSetup(client: Client) {\n const opts = client.getOptions();\n opts.ignoreSpans = [...(opts.ignoreSpans || []), ...SPOTLIGHT_IGNORE_SPANS];\n },\n afterAllSetup: (client: Client) => {\n setupSidecarForwarding(client, sidecarUrl);\n },\n };\n}) satisfies IntegrationFn;\n\nfunction setupSidecarForwarding(client: Client, sidecarUrl: string): void {\n const makeFetch: typeof WINDOW.fetch | undefined = getNativeImplementation('fetch');\n let failCount = 0;\n\n client.on('beforeEnvelope', (envelope: Envelope) => {\n if (failCount > 3) {\n debug.warn('[Spotlight] Disabled Sentry -> Spotlight integration due to too many failed requests:', failCount);\n return;\n }\n\n makeFetch(sidecarUrl, {\n method: 'POST',\n body: serializeEnvelope(envelope) as BodyInit,\n headers: {\n 'Content-Type': 'application/x-sentry-envelope',\n },\n mode: 'cors',\n }).then(\n res => {\n if (res.status >= 200 && res.status < 400) {\n // Reset failed requests counter on success\n failCount = 0;\n }\n },\n err => {\n failCount++;\n debug.error(\n \"Sentry SDK can't connect to Sidecar is it running? See: https://spotlightjs.com/sidecar/npx/\",\n err,\n );\n },\n );\n });\n}\n\n/**\n * Use this integration to send errors and transactions to Spotlight.\n *\n * Learn more about spotlight at https://spotlightjs.com\n */\nexport const spotlightBrowserIntegration = defineIntegration(_spotlightIntegration);\n"],"names":[],"mappings":";;;;;AAeO,MAAM,gBAAA,GAAmB;AAEzB,MAAM,sBAAA,GAAyB;AAAA,EACpC,EAAE,EAAA,EAAI,oBAAA,EAAsB,IAAA,EAAM,mBAAA,EAAoB;AAAA,EACtD,EAAE,EAAA,EAAI,oBAAA,EAAsB,YAAY,EAAE,8BAAA,EAAgC,qBAAoB;AAChG;AAEA,MAAM,qBAAA,IAAyB,CAAC,OAAA,GAA+C,EAAC,KAAM;AACpF,EAAA,MAAM,UAAA,GAAa,QAAQ,UAAA,IAAc,8BAAA;AAEzC,EAAA,OAAO;AAAA,IACL,IAAA,EAAM,gBAAA;AAAA,IACN,OAAO,MAAM;AACX,MAAA,WAAA,IAAe,KAAA,CAAM,GAAA,CAAI,mBAAA,EAAqB,UAAU,CAAA;AAAA,IAC1D,CAAA;AAAA,IACA,YAAY,MAAA,EAAgB;AAC1B,MAAA,MAAM,IAAA,GAAO,OAAO,UAAA,EAAW;AAC/B,MAAA,IAAA,CAAK,WAAA,GAAc,CAAC,GAAI,IAAA,CAAK,eAAe,EAAC,EAAI,GAAG,sBAAsB,CAAA;AAAA,IAC5E,CAAA;AAAA,IACA,aAAA,EAAe,CAAC,MAAA,KAAmB;AACjC,MAAA,sBAAA,CAAuB,QAAQ,UAAU,CAAA;AAAA,IAC3C;AAAA,GACF;AACF,CAAA,CAAA;AAEA,SAAS,sBAAA,CAAuB,QAAgB,UAAA,EAA0B;AACxE,EAAA,MAAM,SAAA,GAA6C,wBAAwB,OAAO,CAAA;AAClF,EAAA,IAAI,SAAA,GAAY,CAAA;AAEhB,EAAA,MAAA,CAAO,EAAA,CAAG,gBAAA,EAAkB,CAAC,QAAA,KAAuB;AAClD,IAAA,IAAI,YAAY,CAAA,EAAG;AACjB,MAAA,KAAA,CAAM,IAAA,CAAK,yFAAyF,SAAS,CAAA;AAC7G,MAAA;AAAA,IACF;AAEA,IAAA,SAAA,CAAU,UAAA,EAAY;AAAA,MACpB,MAAA,EAAQ,MAAA;AAAA,MACR,IAAA,EAAM,kBAAkB,QAAQ,CAAA;AAAA,MAChC,OAAA,EAAS;AAAA,QACP,cAAA,EAAgB;AAAA,OAClB;AAAA,MACA,IAAA,EAAM;AAAA,KACP,CAAA,CAAE,IAAA;AAAA,MACD,CAAA,GAAA,KAAO;AACL,QAAA,IAAI,GAAA,CAAI,MAAA,IAAU,GAAA,IAAO,GAAA,CAAI,SAAS,GAAA,EAAK;AAEzC,UAAA,SAAA,GAAY,CAAA;AAAA,QACd;AAAA,MACF,CAAA;AAAA,MACA,CAAA,GAAA,KAAO;AACL,QAAA,SAAA,EAAA;AACA,QAAA,KAAA,CAAM,KAAA;AAAA,UACJ,8FAAA;AAAA,UACA;AAAA,SACF;AAAA,MACF;AAAA,KACF;AAAA,EACF,CAAC,CAAA;AACH;AAOO,MAAM,2BAAA,GAA8B,kBAAkB,qBAAqB;;;;"}
1
+ {"version":3,"file":"spotlight.js","sources":["../../../../../src/integrations/spotlight.ts"],"sourcesContent":["import type { Client, Envelope, IntegrationFn } from '@sentry/core';\nimport { debug, defineIntegration, serializeEnvelope } from '@sentry/core';\nimport { getNativeImplementation } from '@sentry/browser-utils';\nimport { BROWSER_WEB_VITAL_INP_TARGET } from '@sentry/conventions/attributes';\nimport { UI_INTERACTION_CLICK } from '@sentry/conventions/op';\nimport { DEBUG_BUILD } from '../debug-build';\nimport type { WINDOW } from '../helpers';\n\nexport type SpotlightConnectionOptions = {\n /**\n * Set this if the Spotlight Sidecar is not running on localhost:8969\n * By default, the Url is set to http://localhost:8969/stream\n */\n sidecarUrl?: string;\n};\n\nexport const INTEGRATION_NAME = 'SpotlightBrowser' as const;\n\nexport const SPOTLIGHT_IGNORE_SPANS = [\n { op: UI_INTERACTION_CLICK, name: '#sentry-spotlight' },\n { op: UI_INTERACTION_CLICK, attributes: { [BROWSER_WEB_VITAL_INP_TARGET]: '#sentry-spotlight' } },\n];\n\nconst _spotlightIntegration = ((options: Partial<SpotlightConnectionOptions> = {}) => {\n const sidecarUrl = options.sidecarUrl || 'http://localhost:8969/stream';\n\n return {\n name: INTEGRATION_NAME,\n setup: () => {\n DEBUG_BUILD && debug.log('Using Sidecar URL', sidecarUrl);\n },\n beforeSetup(client: Client) {\n const opts = client.getOptions();\n opts.ignoreSpans = [...(opts.ignoreSpans || []), ...SPOTLIGHT_IGNORE_SPANS];\n },\n afterAllSetup: (client: Client) => {\n setupSidecarForwarding(client, sidecarUrl);\n },\n };\n}) satisfies IntegrationFn;\n\nfunction setupSidecarForwarding(client: Client, sidecarUrl: string): void {\n const makeFetch: typeof WINDOW.fetch | undefined = getNativeImplementation('fetch');\n let failCount = 0;\n\n client.on('beforeEnvelope', (envelope: Envelope) => {\n if (failCount > 3) {\n debug.warn('[Spotlight] Disabled Sentry -> Spotlight integration due to too many failed requests:', failCount);\n return;\n }\n\n makeFetch(sidecarUrl, {\n method: 'POST',\n body: serializeEnvelope(envelope) as BodyInit,\n headers: {\n 'Content-Type': 'application/x-sentry-envelope',\n },\n mode: 'cors',\n }).then(\n res => {\n if (res.status >= 200 && res.status < 400) {\n // Reset failed requests counter on success\n failCount = 0;\n }\n },\n err => {\n failCount++;\n debug.error(\n \"Sentry SDK can't connect to Sidecar is it running? See: https://spotlightjs.com/sidecar/npx/\",\n err,\n );\n },\n );\n });\n}\n\n/**\n * Use this integration to send errors and transactions to Spotlight.\n *\n * Learn more about spotlight at https://spotlightjs.com\n */\nexport const spotlightBrowserIntegration = defineIntegration(_spotlightIntegration);\n"],"names":[],"mappings":";;;;;;AAgBO,MAAM,gBAAA,GAAmB;AAEzB,MAAM,sBAAA,GAAyB;AAAA,EACpC,EAAE,EAAA,EAAI,oBAAA,EAAsB,IAAA,EAAM,mBAAA,EAAoB;AAAA,EACtD,EAAE,IAAI,oBAAA,EAAsB,UAAA,EAAY,EAAE,CAAC,4BAA4B,GAAG,mBAAA,EAAoB;AAChG;AAEA,MAAM,qBAAA,IAAyB,CAAC,OAAA,GAA+C,EAAC,KAAM;AACpF,EAAA,MAAM,UAAA,GAAa,QAAQ,UAAA,IAAc,8BAAA;AAEzC,EAAA,OAAO;AAAA,IACL,IAAA,EAAM,gBAAA;AAAA,IACN,OAAO,MAAM;AACX,MAAA,WAAA,IAAe,KAAA,CAAM,GAAA,CAAI,mBAAA,EAAqB,UAAU,CAAA;AAAA,IAC1D,CAAA;AAAA,IACA,YAAY,MAAA,EAAgB;AAC1B,MAAA,MAAM,IAAA,GAAO,OAAO,UAAA,EAAW;AAC/B,MAAA,IAAA,CAAK,WAAA,GAAc,CAAC,GAAI,IAAA,CAAK,eAAe,EAAC,EAAI,GAAG,sBAAsB,CAAA;AAAA,IAC5E,CAAA;AAAA,IACA,aAAA,EAAe,CAAC,MAAA,KAAmB;AACjC,MAAA,sBAAA,CAAuB,QAAQ,UAAU,CAAA;AAAA,IAC3C;AAAA,GACF;AACF,CAAA,CAAA;AAEA,SAAS,sBAAA,CAAuB,QAAgB,UAAA,EAA0B;AACxE,EAAA,MAAM,SAAA,GAA6C,wBAAwB,OAAO,CAAA;AAClF,EAAA,IAAI,SAAA,GAAY,CAAA;AAEhB,EAAA,MAAA,CAAO,EAAA,CAAG,gBAAA,EAAkB,CAAC,QAAA,KAAuB;AAClD,IAAA,IAAI,YAAY,CAAA,EAAG;AACjB,MAAA,KAAA,CAAM,IAAA,CAAK,yFAAyF,SAAS,CAAA;AAC7G,MAAA;AAAA,IACF;AAEA,IAAA,SAAA,CAAU,UAAA,EAAY;AAAA,MACpB,MAAA,EAAQ,MAAA;AAAA,MACR,IAAA,EAAM,kBAAkB,QAAQ,CAAA;AAAA,MAChC,OAAA,EAAS;AAAA,QACP,cAAA,EAAgB;AAAA,OAClB;AAAA,MACA,IAAA,EAAM;AAAA,KACP,CAAA,CAAE,IAAA;AAAA,MACD,CAAA,GAAA,KAAO;AACL,QAAA,IAAI,GAAA,CAAI,MAAA,IAAU,GAAA,IAAO,GAAA,CAAI,SAAS,GAAA,EAAK;AAEzC,UAAA,SAAA,GAAY,CAAA;AAAA,QACd;AAAA,MACF,CAAA;AAAA,MACA,CAAA,GAAA,KAAO;AACL,QAAA,SAAA,EAAA;AACA,QAAA,KAAA,CAAM,KAAA;AAAA,UACJ,8FAAA;AAAA,UACA;AAAA,SACF;AAAA,MACF;AAAA,KACF;AAAA,EACF,CAAC,CAAA;AACH;AAOO,MAAM,2BAAA,GAA8B,kBAAkB,qBAAqB;;;;"}
@@ -1 +1 @@
1
- {"type":"module","version":"11.0.0","sideEffects":false}
1
+ {"type":"module","version":"11.1.0","sideEffects":false}
@@ -24,14 +24,24 @@ function showReportDialog(options = {}) {
24
24
  },
25
25
  eventId: options.eventId || lastEventId()
26
26
  };
27
+ const { onLoad, onClose, onError } = mergedOptions;
28
+ if (!mergedOptions.eventId) {
29
+ DEBUG_BUILD && debug.error("[showReportDialog] No event ID");
30
+ onError?.(new Error("No event ID to show the report dialog for"));
31
+ return;
32
+ }
27
33
  const script = WINDOW.document.createElement("script");
28
34
  script.async = true;
29
35
  script.crossOrigin = "anonymous";
30
36
  script.src = getReportDialogEndpoint(dsn, mergedOptions);
31
- const { onLoad, onClose } = mergedOptions;
32
37
  if (onLoad) {
33
38
  script.onload = onLoad;
34
39
  }
40
+ if (onError) {
41
+ script.onerror = () => {
42
+ onError(new Error("Failed to load the report dialog script"));
43
+ };
44
+ }
35
45
  if (onClose) {
36
46
  const reportDialogClosedMessageHandler = (event) => {
37
47
  if (event.data === "__sentry_reportdialog_closed__") {
@@ -43,6 +53,9 @@ function showReportDialog(options = {}) {
43
53
  }
44
54
  };
45
55
  WINDOW.addEventListener("message", reportDialogClosedMessageHandler);
56
+ script.addEventListener("error", () => {
57
+ WINDOW.removeEventListener("message", reportDialogClosedMessageHandler);
58
+ });
46
59
  }
47
60
  injectionPoint.appendChild(script);
48
61
  }
@@ -1 +1 @@
1
- {"version":3,"file":"report-dialog.js","sources":["../../../../src/report-dialog.ts"],"sourcesContent":["import type { ReportDialogOptions } from '@sentry/core';\nimport { debug, getClient, getCurrentScope, getReportDialogEndpoint, lastEventId } from '@sentry/core';\nimport { DEBUG_BUILD } from './debug-build';\nimport { WINDOW } from './helpers';\n\n/**\n * Present the user with a report dialog.\n *\n * @param options Everything is optional, we try to fetch all info need from the current scope.\n */\nexport function showReportDialog(options: ReportDialogOptions = {}): void {\n const optionalDocument = WINDOW.document as Document | undefined;\n const injectionPoint = optionalDocument?.head || optionalDocument?.body;\n\n // doesn't work without a document (React Native)\n if (!injectionPoint) {\n DEBUG_BUILD && debug.error('[showReportDialog] Global document not defined');\n return;\n }\n\n const scope = getCurrentScope();\n const client = getClient();\n const dsn = client?.getDsn();\n\n if (!dsn) {\n DEBUG_BUILD && debug.error('[showReportDialog] DSN not configured');\n return;\n }\n\n const mergedOptions = {\n ...options,\n user: {\n ...scope.getUser(),\n ...options.user,\n },\n eventId: options.eventId || lastEventId(),\n };\n\n const script = WINDOW.document.createElement('script');\n script.async = true;\n script.crossOrigin = 'anonymous';\n script.src = getReportDialogEndpoint(dsn, mergedOptions);\n\n const { onLoad, onClose } = mergedOptions;\n\n if (onLoad) {\n script.onload = onLoad;\n }\n\n if (onClose) {\n const reportDialogClosedMessageHandler = (event: MessageEvent): void => {\n if (event.data === '__sentry_reportdialog_closed__') {\n try {\n onClose();\n } finally {\n WINDOW.removeEventListener('message', reportDialogClosedMessageHandler);\n }\n }\n };\n WINDOW.addEventListener('message', reportDialogClosedMessageHandler);\n }\n\n injectionPoint.appendChild(script);\n}\n"],"names":[],"mappings":";;;;AAUO,SAAS,gBAAA,CAAiB,OAAA,GAA+B,EAAC,EAAS;AACxE,EAAA,MAAM,mBAAmB,MAAA,CAAO,QAAA;AAChC,EAAA,MAAM,cAAA,GAAiB,gBAAA,EAAkB,IAAA,IAAQ,gBAAA,EAAkB,IAAA;AAGnE,EAAA,IAAI,CAAC,cAAA,EAAgB;AACnB,IAAA,WAAA,IAAe,KAAA,CAAM,MAAM,gDAAgD,CAAA;AAC3E,IAAA;AAAA,EACF;AAEA,EAAA,MAAM,QAAQ,eAAA,EAAgB;AAC9B,EAAA,MAAM,SAAS,SAAA,EAAU;AACzB,EAAA,MAAM,GAAA,GAAM,QAAQ,MAAA,EAAO;AAE3B,EAAA,IAAI,CAAC,GAAA,EAAK;AACR,IAAA,WAAA,IAAe,KAAA,CAAM,MAAM,uCAAuC,CAAA;AAClE,IAAA;AAAA,EACF;AAEA,EAAA,MAAM,aAAA,GAAgB;AAAA,IACpB,GAAG,OAAA;AAAA,IACH,IAAA,EAAM;AAAA,MACJ,GAAG,MAAM,OAAA,EAAQ;AAAA,MACjB,GAAG,OAAA,CAAQ;AAAA,KACb;AAAA,IACA,OAAA,EAAS,OAAA,CAAQ,OAAA,IAAW,WAAA;AAAY,GAC1C;AAEA,EAAA,MAAM,MAAA,GAAS,MAAA,CAAO,QAAA,CAAS,aAAA,CAAc,QAAQ,CAAA;AACrD,EAAA,MAAA,CAAO,KAAA,GAAQ,IAAA;AACf,EAAA,MAAA,CAAO,WAAA,GAAc,WAAA;AACrB,EAAA,MAAA,CAAO,GAAA,GAAM,uBAAA,CAAwB,GAAA,EAAK,aAAa,CAAA;AAEvD,EAAA,MAAM,EAAE,MAAA,EAAQ,OAAA,EAAQ,GAAI,aAAA;AAE5B,EAAA,IAAI,MAAA,EAAQ;AACV,IAAA,MAAA,CAAO,MAAA,GAAS,MAAA;AAAA,EAClB;AAEA,EAAA,IAAI,OAAA,EAAS;AACX,IAAA,MAAM,gCAAA,GAAmC,CAAC,KAAA,KAA8B;AACtE,MAAA,IAAI,KAAA,CAAM,SAAS,gCAAA,EAAkC;AACnD,QAAA,IAAI;AACF,UAAA,OAAA,EAAQ;AAAA,QACV,CAAA,SAAE;AACA,UAAA,MAAA,CAAO,mBAAA,CAAoB,WAAW,gCAAgC,CAAA;AAAA,QACxE;AAAA,MACF;AAAA,IACF,CAAA;AACA,IAAA,MAAA,CAAO,gBAAA,CAAiB,WAAW,gCAAgC,CAAA;AAAA,EACrE;AAEA,EAAA,cAAA,CAAe,YAAY,MAAM,CAAA;AACnC;;;;"}
1
+ {"version":3,"file":"report-dialog.js","sources":["../../../../src/report-dialog.ts"],"sourcesContent":["import type { ReportDialogOptions } from '@sentry/core';\nimport { debug, getClient, getCurrentScope, getReportDialogEndpoint, lastEventId } from '@sentry/core';\nimport { DEBUG_BUILD } from './debug-build';\nimport { WINDOW } from './helpers';\n\n/**\n * Present the user with a report dialog.\n *\n * @param options Everything is optional, we try to fetch all info need from the current scope.\n */\nexport function showReportDialog(options: ReportDialogOptions = {}): void {\n const optionalDocument = WINDOW.document as Document | undefined;\n const injectionPoint = optionalDocument?.head || optionalDocument?.body;\n\n // doesn't work without a document (React Native)\n if (!injectionPoint) {\n DEBUG_BUILD && debug.error('[showReportDialog] Global document not defined');\n return;\n }\n\n const scope = getCurrentScope();\n const client = getClient();\n const dsn = client?.getDsn();\n\n if (!dsn) {\n DEBUG_BUILD && debug.error('[showReportDialog] DSN not configured');\n return;\n }\n\n const mergedOptions = {\n ...options,\n user: {\n ...scope.getUser(),\n ...options.user,\n },\n eventId: options.eventId || lastEventId(),\n };\n\n const { onLoad, onClose, onError } = mergedOptions;\n\n // The endpoint rejects requests without an event ID, and a failed script load hides the reason\n if (!mergedOptions.eventId) {\n DEBUG_BUILD && debug.error('[showReportDialog] No event ID');\n onError?.(new Error('No event ID to show the report dialog for'));\n return;\n }\n\n const script = WINDOW.document.createElement('script');\n script.async = true;\n script.crossOrigin = 'anonymous';\n script.src = getReportDialogEndpoint(dsn, mergedOptions);\n\n if (onLoad) {\n script.onload = onLoad;\n }\n\n if (onError) {\n script.onerror = () => {\n onError(new Error('Failed to load the report dialog script'));\n };\n }\n\n if (onClose) {\n const reportDialogClosedMessageHandler = (event: MessageEvent): void => {\n if (event.data === '__sentry_reportdialog_closed__') {\n try {\n onClose();\n } finally {\n WINDOW.removeEventListener('message', reportDialogClosedMessageHandler);\n }\n }\n };\n WINDOW.addEventListener('message', reportDialogClosedMessageHandler);\n // A dialog that never loads never closes, so the listener would stay forever\n script.addEventListener('error', () => {\n WINDOW.removeEventListener('message', reportDialogClosedMessageHandler);\n });\n }\n\n injectionPoint.appendChild(script);\n}\n"],"names":[],"mappings":";;;;AAUO,SAAS,gBAAA,CAAiB,OAAA,GAA+B,EAAC,EAAS;AACxE,EAAA,MAAM,mBAAmB,MAAA,CAAO,QAAA;AAChC,EAAA,MAAM,cAAA,GAAiB,gBAAA,EAAkB,IAAA,IAAQ,gBAAA,EAAkB,IAAA;AAGnE,EAAA,IAAI,CAAC,cAAA,EAAgB;AACnB,IAAA,WAAA,IAAe,KAAA,CAAM,MAAM,gDAAgD,CAAA;AAC3E,IAAA;AAAA,EACF;AAEA,EAAA,MAAM,QAAQ,eAAA,EAAgB;AAC9B,EAAA,MAAM,SAAS,SAAA,EAAU;AACzB,EAAA,MAAM,GAAA,GAAM,QAAQ,MAAA,EAAO;AAE3B,EAAA,IAAI,CAAC,GAAA,EAAK;AACR,IAAA,WAAA,IAAe,KAAA,CAAM,MAAM,uCAAuC,CAAA;AAClE,IAAA;AAAA,EACF;AAEA,EAAA,MAAM,aAAA,GAAgB;AAAA,IACpB,GAAG,OAAA;AAAA,IACH,IAAA,EAAM;AAAA,MACJ,GAAG,MAAM,OAAA,EAAQ;AAAA,MACjB,GAAG,OAAA,CAAQ;AAAA,KACb;AAAA,IACA,OAAA,EAAS,OAAA,CAAQ,OAAA,IAAW,WAAA;AAAY,GAC1C;AAEA,EAAA,MAAM,EAAE,MAAA,EAAQ,OAAA,EAAS,OAAA,EAAQ,GAAI,aAAA;AAGrC,EAAA,IAAI,CAAC,cAAc,OAAA,EAAS;AAC1B,IAAA,WAAA,IAAe,KAAA,CAAM,MAAM,gCAAgC,CAAA;AAC3D,IAAA,OAAA,GAAU,IAAI,KAAA,CAAM,2CAA2C,CAAC,CAAA;AAChE,IAAA;AAAA,EACF;AAEA,EAAA,MAAM,MAAA,GAAS,MAAA,CAAO,QAAA,CAAS,aAAA,CAAc,QAAQ,CAAA;AACrD,EAAA,MAAA,CAAO,KAAA,GAAQ,IAAA;AACf,EAAA,MAAA,CAAO,WAAA,GAAc,WAAA;AACrB,EAAA,MAAA,CAAO,GAAA,GAAM,uBAAA,CAAwB,GAAA,EAAK,aAAa,CAAA;AAEvD,EAAA,IAAI,MAAA,EAAQ;AACV,IAAA,MAAA,CAAO,MAAA,GAAS,MAAA;AAAA,EAClB;AAEA,EAAA,IAAI,OAAA,EAAS;AACX,IAAA,MAAA,CAAO,UAAU,MAAM;AACrB,MAAA,OAAA,CAAQ,IAAI,KAAA,CAAM,yCAAyC,CAAC,CAAA;AAAA,IAC9D,CAAA;AAAA,EACF;AAEA,EAAA,IAAI,OAAA,EAAS;AACX,IAAA,MAAM,gCAAA,GAAmC,CAAC,KAAA,KAA8B;AACtE,MAAA,IAAI,KAAA,CAAM,SAAS,gCAAA,EAAkC;AACnD,QAAA,IAAI;AACF,UAAA,OAAA,EAAQ;AAAA,QACV,CAAA,SAAE;AACA,UAAA,MAAA,CAAO,mBAAA,CAAoB,WAAW,gCAAgC,CAAA;AAAA,QACxE;AAAA,MACF;AAAA,IACF,CAAA;AACA,IAAA,MAAA,CAAO,gBAAA,CAAiB,WAAW,gCAAgC,CAAA;AAEnE,IAAA,MAAA,CAAO,gBAAA,CAAiB,SAAS,MAAM;AACrC,MAAA,MAAA,CAAO,mBAAA,CAAoB,WAAW,gCAAgC,CAAA;AAAA,IACxE,CAAC,CAAA;AAAA,EACH;AAEA,EAAA,cAAA,CAAe,YAAY,MAAM,CAAA;AACnC;;;;"}
@@ -1,6 +1,6 @@
1
1
  import { defineIntegration, _INTERNAL_filterKeyValueData, safeSetSpanJSONAttributes, filterCollectedUrl } from '@sentry/core';
2
2
  import { WINDOW, getHttpRequestData } from '../helpers.js';
3
- import { HTTP_REQUEST_HEADER_KEY_BASE, URL_FULL, SENTRY_OP, USER_AGENT_ORIGINAL } from '@sentry/conventions/attributes';
3
+ import { HTTP_REQUEST_HEADER_KEY_BASE, URL_FULL, SENTRY_OP, SENTRY_IS_LOCALHOST, USER_AGENT_ORIGINAL } from '@sentry/conventions/attributes';
4
4
 
5
5
  const httpContextIntegration = defineIntegration(() => {
6
6
  return {
@@ -41,7 +41,7 @@ const httpContextIntegration = defineIntegration(() => {
41
41
  // Likewise for the "Filter out localhost events" feature. Deliberately inlined rather than
42
42
  // sharing the server-side helper, which costs bundle size for request headers and IPs that
43
43
  // don't exist here. Mirrors Relay's localhost filter (relay-filter/src/localhost.rs).
44
- "sentry.is_localhost": protocol === "file:" || hostname === "localhost" || hostname === "127.0.0.1" || // `location.hostname` keeps the brackets for IPv6 hosts, so this is `[::1]`, not `::1`.
44
+ [SENTRY_IS_LOCALHOST]: protocol === "file:" || hostname === "localhost" || hostname === "127.0.0.1" || // `location.hostname` keeps the brackets for IPv6 hosts, so this is `[::1]`, not `::1`.
45
45
  hostname === "[::1]" || !!hostname?.endsWith(".localhost"),
46
46
  // These attributes, we only need on the segment span (analogous to the `request` context for events)
47
47
  ...span.is_segment && {
@@ -1 +1 @@
1
- {"version":3,"file":"httpcontext.js","sources":["../../../../../src/integrations/httpcontext.ts"],"sourcesContent":["import { _INTERNAL_filterKeyValueData, defineIntegration, safeSetSpanJSONAttributes } from '@sentry/core';\nimport { getHttpRequestData, WINDOW } from '../helpers';\nimport { filterCollectedUrl } from '@sentry/core';\nimport { HTTP_REQUEST_HEADER_KEY_BASE, SENTRY_OP, URL_FULL, USER_AGENT_ORIGINAL } from '@sentry/conventions/attributes';\n\n/**\n * Collects information about HTTP request headers and\n * attaches them to the event.\n */\nexport const httpContextIntegration = defineIntegration(() => {\n return {\n name: 'HttpContext' as const,\n preprocessEvent(event, _hint, client) {\n // if none of the information we want exists, don't bother\n if (!WINDOW.navigator && !WINDOW.location && !WINDOW.document) {\n return;\n }\n\n const { url, headers: collectedHeaders } = getHttpRequestData();\n\n // We only filter the headers we collected ourselves, so headers the user set are left alone. Going by key\n // means we also catch these headers when `browserTracingIntegration` already put them on the event.\n const behavior = client.getDataCollectionOptions().httpHeaders.request;\n const userHeaders = Object.entries(event.request?.headers ?? {}).filter(([key]) => !(key in collectedHeaders));\n\n const headers = {\n ..._INTERNAL_filterKeyValueData(collectedHeaders, behavior),\n ...Object.fromEntries(userHeaders),\n };\n\n event.request = {\n // The URL isn't gated by `dataCollection`, same as on the server.\n url,\n ...event.request,\n ...(Object.keys(headers).length > 0 ? { headers } : { headers: undefined }),\n };\n },\n\n processSpan(span, client) {\n // if none of the information we want exists, don't bother\n if (!WINDOW.navigator && !WINDOW.location && !WINDOW.document) {\n return;\n }\n\n const reqData = getHttpRequestData();\n\n // `httpHeadersToSpanAttributes` would also work here, but its cookie and array handling never runs for these\n // two headers and costs every browser bundle ~400B gzip.\n const headers = _INTERNAL_filterKeyValueData(\n reqData.headers,\n client.getDataCollectionOptions().httpHeaders.request,\n );\n const referer = headers['Referer'];\n const { hostname, protocol } = WINDOW.location || {};\n\n safeSetSpanJSONAttributes(span, {\n // This attribute is used by the \"Filter out events from legacy browsers and crawlers\" features on the Sentry backend.\n // Therefore, it's set on every span.\n [USER_AGENT_ORIGINAL]: headers['User-Agent'],\n\n // Likewise for the \"Filter out localhost events\" feature. Deliberately inlined rather than\n // sharing the server-side helper, which costs bundle size for request headers and IPs that\n // don't exist here. Mirrors Relay's localhost filter (relay-filter/src/localhost.rs).\n 'sentry.is_localhost':\n protocol === 'file:' ||\n hostname === 'localhost' ||\n hostname === '127.0.0.1' ||\n // `location.hostname` keeps the brackets for IPv6 hosts, so this is `[::1]`, not `::1`.\n hostname === '[::1]' ||\n !!hostname?.endsWith('.localhost'),\n\n // These attributes, we only need on the segment span (analogous to the `request` context for events)\n ...(span.is_segment && {\n // Coerce empty string to undefined so the helper's nullish check drops it,\n // rather than writing an empty `url.full` attribute onto the span.\n [URL_FULL]: span.attributes?.[SENTRY_OP] !== 'http.client' ? filterCollectedUrl(reqData.url) : undefined,\n [`${HTTP_REQUEST_HEADER_KEY_BASE}.referer`]: referer ? [referer] : undefined,\n }),\n });\n },\n };\n});\n"],"names":[],"mappings":";;;;AASO,MAAM,sBAAA,GAAyB,kBAAkB,MAAM;AAC5D,EAAA,OAAO;AAAA,IACL,IAAA,EAAM,aAAA;AAAA,IACN,eAAA,CAAgB,KAAA,EAAO,KAAA,EAAO,MAAA,EAAQ;AAEpC,MAAA,IAAI,CAAC,OAAO,SAAA,IAAa,CAAC,OAAO,QAAA,IAAY,CAAC,OAAO,QAAA,EAAU;AAC7D,QAAA;AAAA,MACF;AAEA,MAAA,MAAM,EAAE,GAAA,EAAK,OAAA,EAAS,gBAAA,KAAqB,kBAAA,EAAmB;AAI9D,MAAA,MAAM,QAAA,GAAW,MAAA,CAAO,wBAAA,EAAyB,CAAE,WAAA,CAAY,OAAA;AAC/D,MAAA,MAAM,cAAc,MAAA,CAAO,OAAA,CAAQ,KAAA,CAAM,OAAA,EAAS,WAAW,EAAE,CAAA,CAAE,MAAA,CAAO,CAAC,CAAC,GAAG,CAAA,KAAM,EAAE,OAAO,gBAAA,CAAiB,CAAA;AAE7G,MAAA,MAAM,OAAA,GAAU;AAAA,QACd,GAAG,4BAAA,CAA6B,gBAAA,EAAkB,QAAQ,CAAA;AAAA,QAC1D,GAAG,MAAA,CAAO,WAAA,CAAY,WAAW;AAAA,OACnC;AAEA,MAAA,KAAA,CAAM,OAAA,GAAU;AAAA;AAAA,QAEd,GAAA;AAAA,QACA,GAAG,KAAA,CAAM,OAAA;AAAA,QACT,GAAI,MAAA,CAAO,IAAA,CAAK,OAAO,CAAA,CAAE,MAAA,GAAS,CAAA,GAAI,EAAE,OAAA,EAAQ,GAAI,EAAE,OAAA,EAAS,MAAA;AAAU,OAC3E;AAAA,IACF,CAAA;AAAA,IAEA,WAAA,CAAY,MAAM,MAAA,EAAQ;AAExB,MAAA,IAAI,CAAC,OAAO,SAAA,IAAa,CAAC,OAAO,QAAA,IAAY,CAAC,OAAO,QAAA,EAAU;AAC7D,QAAA;AAAA,MACF;AAEA,MAAA,MAAM,UAAU,kBAAA,EAAmB;AAInC,MAAA,MAAM,OAAA,GAAU,4BAAA;AAAA,QACd,OAAA,CAAQ,OAAA;AAAA,QACR,MAAA,CAAO,wBAAA,EAAyB,CAAE,WAAA,CAAY;AAAA,OAChD;AACA,MAAA,MAAM,OAAA,GAAU,QAAQ,SAAS,CAAA;AACjC,MAAA,MAAM,EAAE,QAAA,EAAU,QAAA,EAAS,GAAI,MAAA,CAAO,YAAY,EAAC;AAEnD,MAAA,yBAAA,CAA0B,IAAA,EAAM;AAAA;AAAA;AAAA,QAG9B,CAAC,mBAAmB,GAAG,OAAA,CAAQ,YAAY,CAAA;AAAA;AAAA;AAAA;AAAA,QAK3C,qBAAA,EACE,QAAA,KAAa,OAAA,IACb,QAAA,KAAa,eACb,QAAA,KAAa,WAAA;AAAA,QAEb,aAAa,OAAA,IACb,CAAC,CAAC,QAAA,EAAU,SAAS,YAAY,CAAA;AAAA;AAAA,QAGnC,GAAI,KAAK,UAAA,IAAc;AAAA;AAAA;AAAA,UAGrB,CAAC,QAAQ,GAAG,IAAA,CAAK,UAAA,GAAa,SAAS,CAAA,KAAM,aAAA,GAAgB,kBAAA,CAAmB,OAAA,CAAQ,GAAG,CAAA,GAAI,MAAA;AAAA,UAC/F,CAAC,GAAG,4BAA4B,CAAA,QAAA,CAAU,GAAG,OAAA,GAAU,CAAC,OAAO,CAAA,GAAI;AAAA;AACrE,OACD,CAAA;AAAA,IACH;AAAA,GACF;AACF,CAAC;;;;"}
1
+ {"version":3,"file":"httpcontext.js","sources":["../../../../../src/integrations/httpcontext.ts"],"sourcesContent":["import { _INTERNAL_filterKeyValueData, defineIntegration, safeSetSpanJSONAttributes } from '@sentry/core';\nimport { getHttpRequestData, WINDOW } from '../helpers';\nimport { filterCollectedUrl } from '@sentry/core';\nimport {\n HTTP_REQUEST_HEADER_KEY_BASE,\n SENTRY_IS_LOCALHOST,\n SENTRY_OP,\n URL_FULL,\n USER_AGENT_ORIGINAL,\n} from '@sentry/conventions/attributes';\n\n/**\n * Collects information about HTTP request headers and\n * attaches them to the event.\n */\nexport const httpContextIntegration = defineIntegration(() => {\n return {\n name: 'HttpContext' as const,\n preprocessEvent(event, _hint, client) {\n // if none of the information we want exists, don't bother\n if (!WINDOW.navigator && !WINDOW.location && !WINDOW.document) {\n return;\n }\n\n const { url, headers: collectedHeaders } = getHttpRequestData();\n\n // We only filter the headers we collected ourselves, so headers the user set are left alone. Going by key\n // means we also catch these headers when `browserTracingIntegration` already put them on the event.\n const behavior = client.getDataCollectionOptions().httpHeaders.request;\n const userHeaders = Object.entries(event.request?.headers ?? {}).filter(([key]) => !(key in collectedHeaders));\n\n const headers = {\n ..._INTERNAL_filterKeyValueData(collectedHeaders, behavior),\n ...Object.fromEntries(userHeaders),\n };\n\n event.request = {\n // The URL isn't gated by `dataCollection`, same as on the server.\n url,\n ...event.request,\n ...(Object.keys(headers).length > 0 ? { headers } : { headers: undefined }),\n };\n },\n\n processSpan(span, client) {\n // if none of the information we want exists, don't bother\n if (!WINDOW.navigator && !WINDOW.location && !WINDOW.document) {\n return;\n }\n\n const reqData = getHttpRequestData();\n\n // `httpHeadersToSpanAttributes` would also work here, but its cookie and array handling never runs for these\n // two headers and costs every browser bundle ~400B gzip.\n const headers = _INTERNAL_filterKeyValueData(\n reqData.headers,\n client.getDataCollectionOptions().httpHeaders.request,\n );\n const referer = headers['Referer'];\n const { hostname, protocol } = WINDOW.location || {};\n\n safeSetSpanJSONAttributes(span, {\n // This attribute is used by the \"Filter out events from legacy browsers and crawlers\" features on the Sentry backend.\n // Therefore, it's set on every span.\n [USER_AGENT_ORIGINAL]: headers['User-Agent'],\n\n // Likewise for the \"Filter out localhost events\" feature. Deliberately inlined rather than\n // sharing the server-side helper, which costs bundle size for request headers and IPs that\n // don't exist here. Mirrors Relay's localhost filter (relay-filter/src/localhost.rs).\n [SENTRY_IS_LOCALHOST]:\n protocol === 'file:' ||\n hostname === 'localhost' ||\n hostname === '127.0.0.1' ||\n // `location.hostname` keeps the brackets for IPv6 hosts, so this is `[::1]`, not `::1`.\n hostname === '[::1]' ||\n !!hostname?.endsWith('.localhost'),\n\n // These attributes, we only need on the segment span (analogous to the `request` context for events)\n ...(span.is_segment && {\n // Coerce empty string to undefined so the helper's nullish check drops it,\n // rather than writing an empty `url.full` attribute onto the span.\n [URL_FULL]: span.attributes?.[SENTRY_OP] !== 'http.client' ? filterCollectedUrl(reqData.url) : undefined,\n [`${HTTP_REQUEST_HEADER_KEY_BASE}.referer`]: referer ? [referer] : undefined,\n }),\n });\n },\n };\n});\n"],"names":[],"mappings":";;;;AAeO,MAAM,sBAAA,GAAyB,kBAAkB,MAAM;AAC5D,EAAA,OAAO;AAAA,IACL,IAAA,EAAM,aAAA;AAAA,IACN,eAAA,CAAgB,KAAA,EAAO,KAAA,EAAO,MAAA,EAAQ;AAEpC,MAAA,IAAI,CAAC,OAAO,SAAA,IAAa,CAAC,OAAO,QAAA,IAAY,CAAC,OAAO,QAAA,EAAU;AAC7D,QAAA;AAAA,MACF;AAEA,MAAA,MAAM,EAAE,GAAA,EAAK,OAAA,EAAS,gBAAA,KAAqB,kBAAA,EAAmB;AAI9D,MAAA,MAAM,QAAA,GAAW,MAAA,CAAO,wBAAA,EAAyB,CAAE,WAAA,CAAY,OAAA;AAC/D,MAAA,MAAM,cAAc,MAAA,CAAO,OAAA,CAAQ,KAAA,CAAM,OAAA,EAAS,WAAW,EAAE,CAAA,CAAE,MAAA,CAAO,CAAC,CAAC,GAAG,CAAA,KAAM,EAAE,OAAO,gBAAA,CAAiB,CAAA;AAE7G,MAAA,MAAM,OAAA,GAAU;AAAA,QACd,GAAG,4BAAA,CAA6B,gBAAA,EAAkB,QAAQ,CAAA;AAAA,QAC1D,GAAG,MAAA,CAAO,WAAA,CAAY,WAAW;AAAA,OACnC;AAEA,MAAA,KAAA,CAAM,OAAA,GAAU;AAAA;AAAA,QAEd,GAAA;AAAA,QACA,GAAG,KAAA,CAAM,OAAA;AAAA,QACT,GAAI,MAAA,CAAO,IAAA,CAAK,OAAO,CAAA,CAAE,MAAA,GAAS,CAAA,GAAI,EAAE,OAAA,EAAQ,GAAI,EAAE,OAAA,EAAS,MAAA;AAAU,OAC3E;AAAA,IACF,CAAA;AAAA,IAEA,WAAA,CAAY,MAAM,MAAA,EAAQ;AAExB,MAAA,IAAI,CAAC,OAAO,SAAA,IAAa,CAAC,OAAO,QAAA,IAAY,CAAC,OAAO,QAAA,EAAU;AAC7D,QAAA;AAAA,MACF;AAEA,MAAA,MAAM,UAAU,kBAAA,EAAmB;AAInC,MAAA,MAAM,OAAA,GAAU,4BAAA;AAAA,QACd,OAAA,CAAQ,OAAA;AAAA,QACR,MAAA,CAAO,wBAAA,EAAyB,CAAE,WAAA,CAAY;AAAA,OAChD;AACA,MAAA,MAAM,OAAA,GAAU,QAAQ,SAAS,CAAA;AACjC,MAAA,MAAM,EAAE,QAAA,EAAU,QAAA,EAAS,GAAI,MAAA,CAAO,YAAY,EAAC;AAEnD,MAAA,yBAAA,CAA0B,IAAA,EAAM;AAAA;AAAA;AAAA,QAG9B,CAAC,mBAAmB,GAAG,OAAA,CAAQ,YAAY,CAAA;AAAA;AAAA;AAAA;AAAA,QAK3C,CAAC,mBAAmB,GAClB,aAAa,OAAA,IACb,QAAA,KAAa,eACb,QAAA,KAAa,WAAA;AAAA,QAEb,aAAa,OAAA,IACb,CAAC,CAAC,QAAA,EAAU,SAAS,YAAY,CAAA;AAAA;AAAA,QAGnC,GAAI,KAAK,UAAA,IAAc;AAAA;AAAA;AAAA,UAGrB,CAAC,QAAQ,GAAG,IAAA,CAAK,UAAA,GAAa,SAAS,CAAA,KAAM,aAAA,GAAgB,kBAAA,CAAmB,OAAA,CAAQ,GAAG,CAAA,GAAI,MAAA;AAAA,UAC/F,CAAC,GAAG,4BAA4B,CAAA,QAAA,CAAU,GAAG,OAAA,GAAU,CAAC,OAAO,CAAA,GAAI;AAAA;AACrE,OACD,CAAA;AAAA,IACH;AAAA,GACF;AACF,CAAC;;;;"}
@@ -1,12 +1,13 @@
1
1
  import { defineIntegration, debug, serializeEnvelope } from '@sentry/core';
2
2
  import { getNativeImplementation } from '@sentry/browser-utils';
3
+ import { BROWSER_WEB_VITAL_INP_TARGET } from '@sentry/conventions/attributes';
3
4
  import { UI_INTERACTION_CLICK } from '@sentry/conventions/op';
4
5
  import { DEBUG_BUILD } from '../debug-build.js';
5
6
 
6
7
  const INTEGRATION_NAME = "SpotlightBrowser";
7
8
  const SPOTLIGHT_IGNORE_SPANS = [
8
9
  { op: UI_INTERACTION_CLICK, name: "#sentry-spotlight" },
9
- { op: UI_INTERACTION_CLICK, attributes: { "browser.web_vital.inp.target": "#sentry-spotlight" } }
10
+ { op: UI_INTERACTION_CLICK, attributes: { [BROWSER_WEB_VITAL_INP_TARGET]: "#sentry-spotlight" } }
10
11
  ];
11
12
  const _spotlightIntegration = ((options = {}) => {
12
13
  const sidecarUrl = options.sidecarUrl || "http://localhost:8969/stream";
@@ -1 +1 @@
1
- {"version":3,"file":"spotlight.js","sources":["../../../../../src/integrations/spotlight.ts"],"sourcesContent":["import type { Client, Envelope, IntegrationFn } from '@sentry/core';\nimport { debug, defineIntegration, serializeEnvelope } from '@sentry/core';\nimport { getNativeImplementation } from '@sentry/browser-utils';\nimport { UI_INTERACTION_CLICK } from '@sentry/conventions/op';\nimport { DEBUG_BUILD } from '../debug-build';\nimport type { WINDOW } from '../helpers';\n\nexport type SpotlightConnectionOptions = {\n /**\n * Set this if the Spotlight Sidecar is not running on localhost:8969\n * By default, the Url is set to http://localhost:8969/stream\n */\n sidecarUrl?: string;\n};\n\nexport const INTEGRATION_NAME = 'SpotlightBrowser' as const;\n\nexport const SPOTLIGHT_IGNORE_SPANS = [\n { op: UI_INTERACTION_CLICK, name: '#sentry-spotlight' },\n { op: UI_INTERACTION_CLICK, attributes: { 'browser.web_vital.inp.target': '#sentry-spotlight' } },\n];\n\nconst _spotlightIntegration = ((options: Partial<SpotlightConnectionOptions> = {}) => {\n const sidecarUrl = options.sidecarUrl || 'http://localhost:8969/stream';\n\n return {\n name: INTEGRATION_NAME,\n setup: () => {\n DEBUG_BUILD && debug.log('Using Sidecar URL', sidecarUrl);\n },\n beforeSetup(client: Client) {\n const opts = client.getOptions();\n opts.ignoreSpans = [...(opts.ignoreSpans || []), ...SPOTLIGHT_IGNORE_SPANS];\n },\n afterAllSetup: (client: Client) => {\n setupSidecarForwarding(client, sidecarUrl);\n },\n };\n}) satisfies IntegrationFn;\n\nfunction setupSidecarForwarding(client: Client, sidecarUrl: string): void {\n const makeFetch: typeof WINDOW.fetch | undefined = getNativeImplementation('fetch');\n let failCount = 0;\n\n client.on('beforeEnvelope', (envelope: Envelope) => {\n if (failCount > 3) {\n debug.warn('[Spotlight] Disabled Sentry -> Spotlight integration due to too many failed requests:', failCount);\n return;\n }\n\n makeFetch(sidecarUrl, {\n method: 'POST',\n body: serializeEnvelope(envelope) as BodyInit,\n headers: {\n 'Content-Type': 'application/x-sentry-envelope',\n },\n mode: 'cors',\n }).then(\n res => {\n if (res.status >= 200 && res.status < 400) {\n // Reset failed requests counter on success\n failCount = 0;\n }\n },\n err => {\n failCount++;\n debug.error(\n \"Sentry SDK can't connect to Sidecar is it running? See: https://spotlightjs.com/sidecar/npx/\",\n err,\n );\n },\n );\n });\n}\n\n/**\n * Use this integration to send errors and transactions to Spotlight.\n *\n * Learn more about spotlight at https://spotlightjs.com\n */\nexport const spotlightBrowserIntegration = defineIntegration(_spotlightIntegration);\n"],"names":[],"mappings":";;;;;AAeO,MAAM,gBAAA,GAAmB;AAEzB,MAAM,sBAAA,GAAyB;AAAA,EACpC,EAAE,EAAA,EAAI,oBAAA,EAAsB,IAAA,EAAM,mBAAA,EAAoB;AAAA,EACtD,EAAE,EAAA,EAAI,oBAAA,EAAsB,YAAY,EAAE,8BAAA,EAAgC,qBAAoB;AAChG;AAEA,MAAM,qBAAA,IAAyB,CAAC,OAAA,GAA+C,EAAC,KAAM;AACpF,EAAA,MAAM,UAAA,GAAa,QAAQ,UAAA,IAAc,8BAAA;AAEzC,EAAA,OAAO;AAAA,IACL,IAAA,EAAM,gBAAA;AAAA,IACN,OAAO,MAAM;AACX,MAAA,WAAA,IAAe,KAAA,CAAM,GAAA,CAAI,mBAAA,EAAqB,UAAU,CAAA;AAAA,IAC1D,CAAA;AAAA,IACA,YAAY,MAAA,EAAgB;AAC1B,MAAA,MAAM,IAAA,GAAO,OAAO,UAAA,EAAW;AAC/B,MAAA,IAAA,CAAK,WAAA,GAAc,CAAC,GAAI,IAAA,CAAK,eAAe,EAAC,EAAI,GAAG,sBAAsB,CAAA;AAAA,IAC5E,CAAA;AAAA,IACA,aAAA,EAAe,CAAC,MAAA,KAAmB;AACjC,MAAA,sBAAA,CAAuB,QAAQ,UAAU,CAAA;AAAA,IAC3C;AAAA,GACF;AACF,CAAA,CAAA;AAEA,SAAS,sBAAA,CAAuB,QAAgB,UAAA,EAA0B;AACxE,EAAA,MAAM,SAAA,GAA6C,wBAAwB,OAAO,CAAA;AAClF,EAAA,IAAI,SAAA,GAAY,CAAA;AAEhB,EAAA,MAAA,CAAO,EAAA,CAAG,gBAAA,EAAkB,CAAC,QAAA,KAAuB;AAClD,IAAA,IAAI,YAAY,CAAA,EAAG;AACjB,MAAA,KAAA,CAAM,IAAA,CAAK,yFAAyF,SAAS,CAAA;AAC7G,MAAA;AAAA,IACF;AAEA,IAAA,SAAA,CAAU,UAAA,EAAY;AAAA,MACpB,MAAA,EAAQ,MAAA;AAAA,MACR,IAAA,EAAM,kBAAkB,QAAQ,CAAA;AAAA,MAChC,OAAA,EAAS;AAAA,QACP,cAAA,EAAgB;AAAA,OAClB;AAAA,MACA,IAAA,EAAM;AAAA,KACP,CAAA,CAAE,IAAA;AAAA,MACD,CAAA,GAAA,KAAO;AACL,QAAA,IAAI,GAAA,CAAI,MAAA,IAAU,GAAA,IAAO,GAAA,CAAI,SAAS,GAAA,EAAK;AAEzC,UAAA,SAAA,GAAY,CAAA;AAAA,QACd;AAAA,MACF,CAAA;AAAA,MACA,CAAA,GAAA,KAAO;AACL,QAAA,SAAA,EAAA;AACA,QAAA,KAAA,CAAM,KAAA;AAAA,UACJ,8FAAA;AAAA,UACA;AAAA,SACF;AAAA,MACF;AAAA,KACF;AAAA,EACF,CAAC,CAAA;AACH;AAOO,MAAM,2BAAA,GAA8B,kBAAkB,qBAAqB;;;;"}
1
+ {"version":3,"file":"spotlight.js","sources":["../../../../../src/integrations/spotlight.ts"],"sourcesContent":["import type { Client, Envelope, IntegrationFn } from '@sentry/core';\nimport { debug, defineIntegration, serializeEnvelope } from '@sentry/core';\nimport { getNativeImplementation } from '@sentry/browser-utils';\nimport { BROWSER_WEB_VITAL_INP_TARGET } from '@sentry/conventions/attributes';\nimport { UI_INTERACTION_CLICK } from '@sentry/conventions/op';\nimport { DEBUG_BUILD } from '../debug-build';\nimport type { WINDOW } from '../helpers';\n\nexport type SpotlightConnectionOptions = {\n /**\n * Set this if the Spotlight Sidecar is not running on localhost:8969\n * By default, the Url is set to http://localhost:8969/stream\n */\n sidecarUrl?: string;\n};\n\nexport const INTEGRATION_NAME = 'SpotlightBrowser' as const;\n\nexport const SPOTLIGHT_IGNORE_SPANS = [\n { op: UI_INTERACTION_CLICK, name: '#sentry-spotlight' },\n { op: UI_INTERACTION_CLICK, attributes: { [BROWSER_WEB_VITAL_INP_TARGET]: '#sentry-spotlight' } },\n];\n\nconst _spotlightIntegration = ((options: Partial<SpotlightConnectionOptions> = {}) => {\n const sidecarUrl = options.sidecarUrl || 'http://localhost:8969/stream';\n\n return {\n name: INTEGRATION_NAME,\n setup: () => {\n DEBUG_BUILD && debug.log('Using Sidecar URL', sidecarUrl);\n },\n beforeSetup(client: Client) {\n const opts = client.getOptions();\n opts.ignoreSpans = [...(opts.ignoreSpans || []), ...SPOTLIGHT_IGNORE_SPANS];\n },\n afterAllSetup: (client: Client) => {\n setupSidecarForwarding(client, sidecarUrl);\n },\n };\n}) satisfies IntegrationFn;\n\nfunction setupSidecarForwarding(client: Client, sidecarUrl: string): void {\n const makeFetch: typeof WINDOW.fetch | undefined = getNativeImplementation('fetch');\n let failCount = 0;\n\n client.on('beforeEnvelope', (envelope: Envelope) => {\n if (failCount > 3) {\n debug.warn('[Spotlight] Disabled Sentry -> Spotlight integration due to too many failed requests:', failCount);\n return;\n }\n\n makeFetch(sidecarUrl, {\n method: 'POST',\n body: serializeEnvelope(envelope) as BodyInit,\n headers: {\n 'Content-Type': 'application/x-sentry-envelope',\n },\n mode: 'cors',\n }).then(\n res => {\n if (res.status >= 200 && res.status < 400) {\n // Reset failed requests counter on success\n failCount = 0;\n }\n },\n err => {\n failCount++;\n debug.error(\n \"Sentry SDK can't connect to Sidecar is it running? See: https://spotlightjs.com/sidecar/npx/\",\n err,\n );\n },\n );\n });\n}\n\n/**\n * Use this integration to send errors and transactions to Spotlight.\n *\n * Learn more about spotlight at https://spotlightjs.com\n */\nexport const spotlightBrowserIntegration = defineIntegration(_spotlightIntegration);\n"],"names":[],"mappings":";;;;;;AAgBO,MAAM,gBAAA,GAAmB;AAEzB,MAAM,sBAAA,GAAyB;AAAA,EACpC,EAAE,EAAA,EAAI,oBAAA,EAAsB,IAAA,EAAM,mBAAA,EAAoB;AAAA,EACtD,EAAE,IAAI,oBAAA,EAAsB,UAAA,EAAY,EAAE,CAAC,4BAA4B,GAAG,mBAAA,EAAoB;AAChG;AAEA,MAAM,qBAAA,IAAyB,CAAC,OAAA,GAA+C,EAAC,KAAM;AACpF,EAAA,MAAM,UAAA,GAAa,QAAQ,UAAA,IAAc,8BAAA;AAEzC,EAAA,OAAO;AAAA,IACL,IAAA,EAAM,gBAAA;AAAA,IACN,OAAO,MAAM;AACX,MAAA,WAAA,IAAe,KAAA,CAAM,GAAA,CAAI,mBAAA,EAAqB,UAAU,CAAA;AAAA,IAC1D,CAAA;AAAA,IACA,YAAY,MAAA,EAAgB;AAC1B,MAAA,MAAM,IAAA,GAAO,OAAO,UAAA,EAAW;AAC/B,MAAA,IAAA,CAAK,WAAA,GAAc,CAAC,GAAI,IAAA,CAAK,eAAe,EAAC,EAAI,GAAG,sBAAsB,CAAA;AAAA,IAC5E,CAAA;AAAA,IACA,aAAA,EAAe,CAAC,MAAA,KAAmB;AACjC,MAAA,sBAAA,CAAuB,QAAQ,UAAU,CAAA;AAAA,IAC3C;AAAA,GACF;AACF,CAAA,CAAA;AAEA,SAAS,sBAAA,CAAuB,QAAgB,UAAA,EAA0B;AACxE,EAAA,MAAM,SAAA,GAA6C,wBAAwB,OAAO,CAAA;AAClF,EAAA,IAAI,SAAA,GAAY,CAAA;AAEhB,EAAA,MAAA,CAAO,EAAA,CAAG,gBAAA,EAAkB,CAAC,QAAA,KAAuB;AAClD,IAAA,IAAI,YAAY,CAAA,EAAG;AACjB,MAAA,KAAA,CAAM,IAAA,CAAK,yFAAyF,SAAS,CAAA;AAC7G,MAAA;AAAA,IACF;AAEA,IAAA,SAAA,CAAU,UAAA,EAAY;AAAA,MACpB,MAAA,EAAQ,MAAA;AAAA,MACR,IAAA,EAAM,kBAAkB,QAAQ,CAAA;AAAA,MAChC,OAAA,EAAS;AAAA,QACP,cAAA,EAAgB;AAAA,OAClB;AAAA,MACA,IAAA,EAAM;AAAA,KACP,CAAA,CAAE,IAAA;AAAA,MACD,CAAA,GAAA,KAAO;AACL,QAAA,IAAI,GAAA,CAAI,MAAA,IAAU,GAAA,IAAO,GAAA,CAAI,SAAS,GAAA,EAAK;AAEzC,UAAA,SAAA,GAAY,CAAA;AAAA,QACd;AAAA,MACF,CAAA;AAAA,MACA,CAAA,GAAA,KAAO;AACL,QAAA,SAAA,EAAA;AACA,QAAA,KAAA,CAAM,KAAA;AAAA,UACJ,8FAAA;AAAA,UACA;AAAA,SACF;AAAA,MACF;AAAA,KACF;AAAA,EACF,CAAC,CAAA;AACH;AAOO,MAAM,2BAAA,GAA8B,kBAAkB,qBAAqB;;;;"}
@@ -1 +1 @@
1
- {"type":"module","version":"11.0.0","sideEffects":false}
1
+ {"type":"module","version":"11.1.0","sideEffects":false}
@@ -24,14 +24,24 @@ function showReportDialog(options = {}) {
24
24
  },
25
25
  eventId: options.eventId || lastEventId()
26
26
  };
27
+ const { onLoad, onClose, onError } = mergedOptions;
28
+ if (!mergedOptions.eventId) {
29
+ DEBUG_BUILD && debug.error("[showReportDialog] No event ID");
30
+ onError?.(new Error("No event ID to show the report dialog for"));
31
+ return;
32
+ }
27
33
  const script = WINDOW.document.createElement("script");
28
34
  script.async = true;
29
35
  script.crossOrigin = "anonymous";
30
36
  script.src = getReportDialogEndpoint(dsn, mergedOptions);
31
- const { onLoad, onClose } = mergedOptions;
32
37
  if (onLoad) {
33
38
  script.onload = onLoad;
34
39
  }
40
+ if (onError) {
41
+ script.onerror = () => {
42
+ onError(new Error("Failed to load the report dialog script"));
43
+ };
44
+ }
35
45
  if (onClose) {
36
46
  const reportDialogClosedMessageHandler = (event) => {
37
47
  if (event.data === "__sentry_reportdialog_closed__") {
@@ -43,6 +53,9 @@ function showReportDialog(options = {}) {
43
53
  }
44
54
  };
45
55
  WINDOW.addEventListener("message", reportDialogClosedMessageHandler);
56
+ script.addEventListener("error", () => {
57
+ WINDOW.removeEventListener("message", reportDialogClosedMessageHandler);
58
+ });
46
59
  }
47
60
  injectionPoint.appendChild(script);
48
61
  }
@@ -1 +1 @@
1
- {"version":3,"file":"report-dialog.js","sources":["../../../../src/report-dialog.ts"],"sourcesContent":["import type { ReportDialogOptions } from '@sentry/core';\nimport { debug, getClient, getCurrentScope, getReportDialogEndpoint, lastEventId } from '@sentry/core';\nimport { DEBUG_BUILD } from './debug-build';\nimport { WINDOW } from './helpers';\n\n/**\n * Present the user with a report dialog.\n *\n * @param options Everything is optional, we try to fetch all info need from the current scope.\n */\nexport function showReportDialog(options: ReportDialogOptions = {}): void {\n const optionalDocument = WINDOW.document as Document | undefined;\n const injectionPoint = optionalDocument?.head || optionalDocument?.body;\n\n // doesn't work without a document (React Native)\n if (!injectionPoint) {\n DEBUG_BUILD && debug.error('[showReportDialog] Global document not defined');\n return;\n }\n\n const scope = getCurrentScope();\n const client = getClient();\n const dsn = client?.getDsn();\n\n if (!dsn) {\n DEBUG_BUILD && debug.error('[showReportDialog] DSN not configured');\n return;\n }\n\n const mergedOptions = {\n ...options,\n user: {\n ...scope.getUser(),\n ...options.user,\n },\n eventId: options.eventId || lastEventId(),\n };\n\n const script = WINDOW.document.createElement('script');\n script.async = true;\n script.crossOrigin = 'anonymous';\n script.src = getReportDialogEndpoint(dsn, mergedOptions);\n\n const { onLoad, onClose } = mergedOptions;\n\n if (onLoad) {\n script.onload = onLoad;\n }\n\n if (onClose) {\n const reportDialogClosedMessageHandler = (event: MessageEvent): void => {\n if (event.data === '__sentry_reportdialog_closed__') {\n try {\n onClose();\n } finally {\n WINDOW.removeEventListener('message', reportDialogClosedMessageHandler);\n }\n }\n };\n WINDOW.addEventListener('message', reportDialogClosedMessageHandler);\n }\n\n injectionPoint.appendChild(script);\n}\n"],"names":[],"mappings":";;;;AAUO,SAAS,gBAAA,CAAiB,OAAA,GAA+B,EAAC,EAAS;AACxE,EAAA,MAAM,mBAAmB,MAAA,CAAO,QAAA;AAChC,EAAA,MAAM,cAAA,GAAiB,gBAAA,EAAkB,IAAA,IAAQ,gBAAA,EAAkB,IAAA;AAGnE,EAAA,IAAI,CAAC,cAAA,EAAgB;AACnB,IAAA,WAAA,IAAe,KAAA,CAAM,MAAM,gDAAgD,CAAA;AAC3E,IAAA;AAAA,EACF;AAEA,EAAA,MAAM,QAAQ,eAAA,EAAgB;AAC9B,EAAA,MAAM,SAAS,SAAA,EAAU;AACzB,EAAA,MAAM,GAAA,GAAM,QAAQ,MAAA,EAAO;AAE3B,EAAA,IAAI,CAAC,GAAA,EAAK;AACR,IAAA,WAAA,IAAe,KAAA,CAAM,MAAM,uCAAuC,CAAA;AAClE,IAAA;AAAA,EACF;AAEA,EAAA,MAAM,aAAA,GAAgB;AAAA,IACpB,GAAG,OAAA;AAAA,IACH,IAAA,EAAM;AAAA,MACJ,GAAG,MAAM,OAAA,EAAQ;AAAA,MACjB,GAAG,OAAA,CAAQ;AAAA,KACb;AAAA,IACA,OAAA,EAAS,OAAA,CAAQ,OAAA,IAAW,WAAA;AAAY,GAC1C;AAEA,EAAA,MAAM,MAAA,GAAS,MAAA,CAAO,QAAA,CAAS,aAAA,CAAc,QAAQ,CAAA;AACrD,EAAA,MAAA,CAAO,KAAA,GAAQ,IAAA;AACf,EAAA,MAAA,CAAO,WAAA,GAAc,WAAA;AACrB,EAAA,MAAA,CAAO,GAAA,GAAM,uBAAA,CAAwB,GAAA,EAAK,aAAa,CAAA;AAEvD,EAAA,MAAM,EAAE,MAAA,EAAQ,OAAA,EAAQ,GAAI,aAAA;AAE5B,EAAA,IAAI,MAAA,EAAQ;AACV,IAAA,MAAA,CAAO,MAAA,GAAS,MAAA;AAAA,EAClB;AAEA,EAAA,IAAI,OAAA,EAAS;AACX,IAAA,MAAM,gCAAA,GAAmC,CAAC,KAAA,KAA8B;AACtE,MAAA,IAAI,KAAA,CAAM,SAAS,gCAAA,EAAkC;AACnD,QAAA,IAAI;AACF,UAAA,OAAA,EAAQ;AAAA,QACV,CAAA,SAAE;AACA,UAAA,MAAA,CAAO,mBAAA,CAAoB,WAAW,gCAAgC,CAAA;AAAA,QACxE;AAAA,MACF;AAAA,IACF,CAAA;AACA,IAAA,MAAA,CAAO,gBAAA,CAAiB,WAAW,gCAAgC,CAAA;AAAA,EACrE;AAEA,EAAA,cAAA,CAAe,YAAY,MAAM,CAAA;AACnC;;;;"}
1
+ {"version":3,"file":"report-dialog.js","sources":["../../../../src/report-dialog.ts"],"sourcesContent":["import type { ReportDialogOptions } from '@sentry/core';\nimport { debug, getClient, getCurrentScope, getReportDialogEndpoint, lastEventId } from '@sentry/core';\nimport { DEBUG_BUILD } from './debug-build';\nimport { WINDOW } from './helpers';\n\n/**\n * Present the user with a report dialog.\n *\n * @param options Everything is optional, we try to fetch all info need from the current scope.\n */\nexport function showReportDialog(options: ReportDialogOptions = {}): void {\n const optionalDocument = WINDOW.document as Document | undefined;\n const injectionPoint = optionalDocument?.head || optionalDocument?.body;\n\n // doesn't work without a document (React Native)\n if (!injectionPoint) {\n DEBUG_BUILD && debug.error('[showReportDialog] Global document not defined');\n return;\n }\n\n const scope = getCurrentScope();\n const client = getClient();\n const dsn = client?.getDsn();\n\n if (!dsn) {\n DEBUG_BUILD && debug.error('[showReportDialog] DSN not configured');\n return;\n }\n\n const mergedOptions = {\n ...options,\n user: {\n ...scope.getUser(),\n ...options.user,\n },\n eventId: options.eventId || lastEventId(),\n };\n\n const { onLoad, onClose, onError } = mergedOptions;\n\n // The endpoint rejects requests without an event ID, and a failed script load hides the reason\n if (!mergedOptions.eventId) {\n DEBUG_BUILD && debug.error('[showReportDialog] No event ID');\n onError?.(new Error('No event ID to show the report dialog for'));\n return;\n }\n\n const script = WINDOW.document.createElement('script');\n script.async = true;\n script.crossOrigin = 'anonymous';\n script.src = getReportDialogEndpoint(dsn, mergedOptions);\n\n if (onLoad) {\n script.onload = onLoad;\n }\n\n if (onError) {\n script.onerror = () => {\n onError(new Error('Failed to load the report dialog script'));\n };\n }\n\n if (onClose) {\n const reportDialogClosedMessageHandler = (event: MessageEvent): void => {\n if (event.data === '__sentry_reportdialog_closed__') {\n try {\n onClose();\n } finally {\n WINDOW.removeEventListener('message', reportDialogClosedMessageHandler);\n }\n }\n };\n WINDOW.addEventListener('message', reportDialogClosedMessageHandler);\n // A dialog that never loads never closes, so the listener would stay forever\n script.addEventListener('error', () => {\n WINDOW.removeEventListener('message', reportDialogClosedMessageHandler);\n });\n }\n\n injectionPoint.appendChild(script);\n}\n"],"names":[],"mappings":";;;;AAUO,SAAS,gBAAA,CAAiB,OAAA,GAA+B,EAAC,EAAS;AACxE,EAAA,MAAM,mBAAmB,MAAA,CAAO,QAAA;AAChC,EAAA,MAAM,cAAA,GAAiB,gBAAA,EAAkB,IAAA,IAAQ,gBAAA,EAAkB,IAAA;AAGnE,EAAA,IAAI,CAAC,cAAA,EAAgB;AACnB,IAAA,WAAA,IAAe,KAAA,CAAM,MAAM,gDAAgD,CAAA;AAC3E,IAAA;AAAA,EACF;AAEA,EAAA,MAAM,QAAQ,eAAA,EAAgB;AAC9B,EAAA,MAAM,SAAS,SAAA,EAAU;AACzB,EAAA,MAAM,GAAA,GAAM,QAAQ,MAAA,EAAO;AAE3B,EAAA,IAAI,CAAC,GAAA,EAAK;AACR,IAAA,WAAA,IAAe,KAAA,CAAM,MAAM,uCAAuC,CAAA;AAClE,IAAA;AAAA,EACF;AAEA,EAAA,MAAM,aAAA,GAAgB;AAAA,IACpB,GAAG,OAAA;AAAA,IACH,IAAA,EAAM;AAAA,MACJ,GAAG,MAAM,OAAA,EAAQ;AAAA,MACjB,GAAG,OAAA,CAAQ;AAAA,KACb;AAAA,IACA,OAAA,EAAS,OAAA,CAAQ,OAAA,IAAW,WAAA;AAAY,GAC1C;AAEA,EAAA,MAAM,EAAE,MAAA,EAAQ,OAAA,EAAS,OAAA,EAAQ,GAAI,aAAA;AAGrC,EAAA,IAAI,CAAC,cAAc,OAAA,EAAS;AAC1B,IAAA,WAAA,IAAe,KAAA,CAAM,MAAM,gCAAgC,CAAA;AAC3D,IAAA,OAAA,GAAU,IAAI,KAAA,CAAM,2CAA2C,CAAC,CAAA;AAChE,IAAA;AAAA,EACF;AAEA,EAAA,MAAM,MAAA,GAAS,MAAA,CAAO,QAAA,CAAS,aAAA,CAAc,QAAQ,CAAA;AACrD,EAAA,MAAA,CAAO,KAAA,GAAQ,IAAA;AACf,EAAA,MAAA,CAAO,WAAA,GAAc,WAAA;AACrB,EAAA,MAAA,CAAO,GAAA,GAAM,uBAAA,CAAwB,GAAA,EAAK,aAAa,CAAA;AAEvD,EAAA,IAAI,MAAA,EAAQ;AACV,IAAA,MAAA,CAAO,MAAA,GAAS,MAAA;AAAA,EAClB;AAEA,EAAA,IAAI,OAAA,EAAS;AACX,IAAA,MAAA,CAAO,UAAU,MAAM;AACrB,MAAA,OAAA,CAAQ,IAAI,KAAA,CAAM,yCAAyC,CAAC,CAAA;AAAA,IAC9D,CAAA;AAAA,EACF;AAEA,EAAA,IAAI,OAAA,EAAS;AACX,IAAA,MAAM,gCAAA,GAAmC,CAAC,KAAA,KAA8B;AACtE,MAAA,IAAI,KAAA,CAAM,SAAS,gCAAA,EAAkC;AACnD,QAAA,IAAI;AACF,UAAA,OAAA,EAAQ;AAAA,QACV,CAAA,SAAE;AACA,UAAA,MAAA,CAAO,mBAAA,CAAoB,WAAW,gCAAgC,CAAA;AAAA,QACxE;AAAA,MACF;AAAA,IACF,CAAA;AACA,IAAA,MAAA,CAAO,gBAAA,CAAiB,WAAW,gCAAgC,CAAA;AAEnE,IAAA,MAAA,CAAO,gBAAA,CAAiB,SAAS,MAAM;AACrC,MAAA,MAAA,CAAO,mBAAA,CAAoB,WAAW,gCAAgC,CAAA;AAAA,IACxE,CAAC,CAAA;AAAA,EACH;AAEA,EAAA,cAAA,CAAe,YAAY,MAAM,CAAA;AACnC;;;;"}
@@ -1 +1 @@
1
- {"version":3,"file":"httpcontext.d.ts","sourceRoot":"","sources":["../../../../src/integrations/httpcontext.ts"],"names":[],"mappings":"AAKA;;;GAGG;AACH,eAAO,MAAM,sBAAsB;;CAwEjC,CAAC"}
1
+ {"version":3,"file":"httpcontext.d.ts","sourceRoot":"","sources":["../../../../src/integrations/httpcontext.ts"],"names":[],"mappings":"AAWA;;;GAGG;AACH,eAAO,MAAM,sBAAsB;;CAwEjC,CAAC"}
@@ -14,7 +14,7 @@ export declare const SPOTLIGHT_IGNORE_SPANS: ({
14
14
  name?: undefined;
15
15
  op: string;
16
16
  attributes: {
17
- 'browser.web_vital.inp.target': string;
17
+ "browser.web_vital.inp.target": string;
18
18
  };
19
19
  })[];
20
20
  /**
@@ -1 +1 @@
1
- {"version":3,"file":"spotlight.d.ts","sourceRoot":"","sources":["../../../../src/integrations/spotlight.ts"],"names":[],"mappings":"AAOA,MAAM,MAAM,0BAA0B,GAAG;IACvC;;;OAGG;IACH,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB,CAAC;AAEF,eAAO,MAAM,gBAAgB,EAAG,kBAA2B,CAAC;AAE5D,eAAO,MAAM,sBAAsB;;;;;;;;QAES,8BAA8B;;IACzE,CAAC;AAuDF;;;;GAIG;AACH,eAAO,MAAM,2BAA2B;;CAA2C,CAAC"}
1
+ {"version":3,"file":"spotlight.d.ts","sourceRoot":"","sources":["../../../../src/integrations/spotlight.ts"],"names":[],"mappings":"AAQA,MAAM,MAAM,0BAA0B,GAAG;IACvC;;;OAGG;IACH,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB,CAAC;AAEF,eAAO,MAAM,gBAAgB,EAAG,kBAA2B,CAAC;AAE5D,eAAO,MAAM,sBAAsB;;;;;;;;;;IAGlC,CAAC;AAuDF;;;;GAIG;AACH,eAAO,MAAM,2BAA2B;;CAA2C,CAAC"}
@@ -1 +1 @@
1
- {"version":3,"file":"report-dialog.d.ts","sourceRoot":"","sources":["../../../src/report-dialog.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,cAAc,CAAC;AAKxD;;;;GAIG;AACH,wBAAgB,gBAAgB,CAAC,OAAO,GAAE,mBAAwB,GAAG,IAAI,CAqDxE"}
1
+ {"version":3,"file":"report-dialog.d.ts","sourceRoot":"","sources":["../../../src/report-dialog.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,cAAc,CAAC;AAKxD;;;;GAIG;AACH,wBAAgB,gBAAgB,CAAC,OAAO,GAAE,mBAAwB,GAAG,IAAI,CAsExE"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sentry/browser",
3
- "version": "11.0.0",
3
+ "version": "11.1.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.0.0",
42
- "@sentry/conventions": "^0.23.0",
43
- "@sentry/feedback": "11.0.0",
44
- "@sentry/replay": "11.0.0",
45
- "@sentry/replay-canvas": "11.0.0",
46
- "@sentry/core": "11.0.0"
41
+ "@sentry/browser-utils": "11.1.0",
42
+ "@sentry/conventions": "^0.24.0",
43
+ "@sentry/feedback": "11.1.0",
44
+ "@sentry/replay": "11.1.0",
45
+ "@sentry/replay-canvas": "11.1.0",
46
+ "@sentry/core": "11.1.0"
47
47
  },
48
48
  "devDependencies": {
49
- "@sentry-internal/integration-shims": "11.0.0",
49
+ "@sentry-internal/integration-shims": "11.1.0",
50
50
  "fake-indexeddb": "^6.2.5"
51
51
  },
52
52
  "scripts": {