@sentry/wasm 11.0.0-rc.1 → 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.
package/README.md CHANGED
@@ -10,10 +10,14 @@
10
10
  [![npm dm](https://img.shields.io/npm/dm/@sentry/wasm.svg)](https://www.npmjs.com/package/@sentry/wasm)
11
11
  [![npm dt](https://img.shields.io/npm/dt/@sentry/wasm.svg)](https://www.npmjs.com/package/@sentry/wasm)
12
12
 
13
- ## Links
13
+ WebAssembly stack-trace support for the Sentry JavaScript SDK.
14
14
 
15
- - [Official SDK Docs](https://docs.sentry.io/quickstart/)
15
+ ## Documentation
16
16
 
17
- ## General
17
+ - [Getting started](https://docs.sentry.io/platforms/javascript/guides/wasm/)
18
+ - [Configuration](https://docs.sentry.io/platforms/javascript/guides/wasm/configuration/)
18
19
 
19
- This package provides support for WebAssembly in stack traces.
20
+ ## Support
21
+
22
+ - [Report a bug](https://github.com/getsentry/sentry-javascript/issues/new/choose)
23
+ - [Contributing](https://github.com/getsentry/sentry-javascript/blob/develop/CONTRIBUTING.md)
@@ -1,6 +1,7 @@
1
1
  Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
2
2
 
3
3
  const core = require('@sentry/core');
4
+ const matchSyntheticWasmFilename = require('./matchSyntheticWasmFilename.js');
4
5
  const patchWebAssembly = require('./patchWebAssembly.js');
5
6
  const registry = require('./registry.js');
6
7
 
@@ -25,8 +26,8 @@ const _wasmIntegration = ((options = {}) => {
25
26
  }
26
27
  if (hasAtLeastOneWasmFrameWithImage) {
27
28
  event.debug_meta = event.debug_meta || {};
28
- const mainThreadImages = registry.getImages();
29
- const workerImages = WINDOW._sentryWasmImages || [];
29
+ const mainThreadImages = registry.getImages().map(registry.toProtocolDebugImage);
30
+ const workerImages = (WINDOW._sentryWasmImages || []).map(registry.toProtocolDebugImage);
30
31
  event.debug_meta.images = [...event.debug_meta.images || [], ...mainThreadImages, ...workerImages];
31
32
  }
32
33
  return event;
@@ -48,8 +49,8 @@ function patchFrames(frames, applicationKey, existingImagesOffset = 0) {
48
49
  match = frame.filename.match(PARSER_REGEX);
49
50
  }
50
51
  if (match) {
51
- const index = registry.getImage(match[1]);
52
- const workerImageIndex = getWorkerImage(match[1]);
52
+ let index = registry.getImage(match[1]);
53
+ let workerImageIndex = getWorkerImage(match[1]);
53
54
  frame.instruction_addr = match[2];
54
55
  frame.filename = match[1];
55
56
  frame.platform = "native";
@@ -59,6 +60,17 @@ function patchFrames(frames, applicationKey, existingImagesOffset = 0) {
59
60
  [`${BUNDLER_PLUGIN_APP_KEY_PREFIX}${applicationKey}`]: true
60
61
  };
61
62
  }
63
+ if (index < 0 && workerImageIndex < 0) {
64
+ const unique = matchSyntheticWasmFilename.uniqueImageForSyntheticFilename(match[1], registry.getImages(), WINDOW._sentryWasmImages || []);
65
+ if (unique) {
66
+ frame.filename = unique.codeFile;
67
+ if (unique.worker) {
68
+ workerImageIndex = unique.index;
69
+ } else {
70
+ index = unique.index;
71
+ }
72
+ }
73
+ }
62
74
  if (index >= 0) {
63
75
  frame.addr_mode = `rel:${existingImagesOffset + index}`;
64
76
  hasAtLeastOneWasmFrameWithImage = true;
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sources":["../../../src/index.ts"],"sourcesContent":["import type { DebugImage, Event, IntegrationFn, StackFrame } from '@sentry/core';\nimport { defineIntegration, GLOBAL_OBJ } from '@sentry/core';\nimport { patchWebAssembly } from './patchWebAssembly';\nimport { getImage, getImages, registerModule } from './registry';\n\nconst INTEGRATION_NAME = 'Wasm';\n\n// We use the same prefix as bundler plugins so that thirdPartyErrorFilterIntegration\n// recognizes WASM frames as first-party code without needing modifications.\nconst BUNDLER_PLUGIN_APP_KEY_PREFIX = '_sentryBundlerPluginAppKey:';\n\n/**\n * Minimal interface for DedicatedWorkerGlobalScope.\n * We can't use the actual type because it breaks everyone who doesn't have {\"lib\": [\"WebWorker\"]}\n */\ninterface MinimalDedicatedWorkerGlobalScope {\n postMessage: (message: unknown) => void;\n}\n\ninterface RegisterWebWorkerWasmOptions {\n self: MinimalDedicatedWorkerGlobalScope;\n}\n\ninterface WasmIntegrationOptions {\n /**\n * Key to identify this application for third-party error filtering.\n * This key should match one of the keys provided to the `filterKeys` option\n * of the `thirdPartyErrorFilterIntegration`.\n */\n applicationKey?: string;\n}\n\n// Access WINDOW with proper typing for _sentryWasmImages\nconst WINDOW = GLOBAL_OBJ as typeof GLOBAL_OBJ & {\n _sentryWasmImages?: Array<DebugImage>;\n};\n\nconst _wasmIntegration = ((options: WasmIntegrationOptions = {}) => {\n return {\n name: INTEGRATION_NAME,\n setupOnce() {\n patchWebAssembly(registerModule);\n },\n processEvent(event: Event): Event {\n let hasAtLeastOneWasmFrameWithImage = false;\n\n const existingImagesCount = event.debug_meta?.images?.length || 0;\n\n if (event.exception?.values) {\n event.exception.values.forEach(exception => {\n if (exception.stacktrace?.frames) {\n hasAtLeastOneWasmFrameWithImage =\n patchFrames(exception.stacktrace.frames, options.applicationKey, existingImagesCount) ||\n hasAtLeastOneWasmFrameWithImage;\n }\n });\n }\n\n if (hasAtLeastOneWasmFrameWithImage) {\n event.debug_meta = event.debug_meta || {};\n const mainThreadImages = getImages();\n const workerImages = WINDOW._sentryWasmImages || [];\n event.debug_meta.images = [...(event.debug_meta.images || []), ...mainThreadImages, ...workerImages];\n }\n\n return event;\n },\n };\n}) satisfies IntegrationFn;\n\nexport const wasmIntegration = defineIntegration(_wasmIntegration);\n\nconst PARSER_REGEX = /^(.*?):wasm-function\\[\\d+\\]:(0x[a-fA-F0-9]+)$/;\n\n/**\n * Patches a list of stackframes with wasm data needed for server-side symbolication\n * if applicable. Returns true if the provided list of stack frames had at least one\n * matching registered image.\n *\n * @param frames - Stack frames to patch\n * @param applicationKey - Optional key for third-party error filtering\n * @param existingImagesOffset - Number of existing debug images that will be prepended\n * to the final images array (used to calculate correct addr_mode indices)\n */\n// Only exported for tests\nexport function patchFrames(\n frames: Array<StackFrame>,\n applicationKey?: string,\n existingImagesOffset: number = 0,\n): boolean {\n let hasAtLeastOneWasmFrameWithImage = false;\n frames.forEach(frame => {\n if (!frame.filename) {\n return;\n }\n\n const split = frame.filename.split('(');\n // eslint-disable-next-line @typescript-eslint/no-non-null-assertion\n const lastSplit = split[split.length - 1]!;\n\n // Let's call this first match a \"messy match\".\n // The browser stacktrace parser spits out frames that have a filename like this: \"int) const (http://localhost:8001/main.wasm:wasm-function[190]:0x5aeb\"\n // It contains some leftover mess because wasm stack frames are more complicated than our parser can handle: \"at MyClass::bar(int) const (http://localhost:8001/main.wasm:wasm-function[190]:0x5aeb)\"\n // This first match simply tries to mitigate the mess up until the first opening parens.\n // The match afterwards is a sensible fallback\n let match = lastSplit.match(PARSER_REGEX) as null | [string, string, string];\n\n if (!match) {\n match = frame.filename.match(PARSER_REGEX) as null | [string, string, string];\n }\n\n if (match) {\n const index = getImage(match[1]);\n const workerImageIndex = getWorkerImage(match[1]);\n frame.instruction_addr = match[2];\n frame.filename = match[1];\n frame.platform = 'native';\n\n if (applicationKey) {\n frame.module_metadata = {\n ...frame.module_metadata,\n [`${BUNDLER_PLUGIN_APP_KEY_PREFIX}${applicationKey}`]: true,\n };\n }\n\n if (index >= 0) {\n frame.addr_mode = `rel:${existingImagesOffset + index}`;\n hasAtLeastOneWasmFrameWithImage = true;\n } else if (workerImageIndex >= 0) {\n const mainThreadImagesCount = getImages().length;\n frame.addr_mode = `rel:${existingImagesOffset + mainThreadImagesCount + workerImageIndex}`;\n hasAtLeastOneWasmFrameWithImage = true;\n }\n }\n });\n\n return hasAtLeastOneWasmFrameWithImage;\n}\n\n/**\n * Looks up an image by URL in worker images.\n */\nfunction getWorkerImage(url: string): number {\n const workerImages = WINDOW._sentryWasmImages || [];\n return workerImages.findIndex(image => {\n return image.type === 'wasm' && image.code_file === url;\n });\n}\n\n/**\n * Use this function to register WASM support in a web worker.\n *\n * This function will:\n * - Patch WebAssembly.instantiateStreaming and WebAssembly.compileStreaming in the worker\n * - Forward WASM debug images to the parent thread for symbolication\n *\n * @param options {RegisterWebWorkerWasmOptions} Options:\n * - `self`: The worker's global scope (self).\n */\nexport function registerWebWorkerWasm({ self }: RegisterWebWorkerWasmOptions): void {\n patchWebAssembly((module, url) => {\n const image = registerModule(module, url);\n\n if (image) {\n self.postMessage({\n _sentryMessage: true,\n _sentryWasmImages: [image],\n });\n }\n });\n}\n"],"names":["GLOBAL_OBJ","patchWebAssembly","registerModule","getImages","defineIntegration","getImage"],"mappings":"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"}
1
+ {"version":3,"file":"index.js","sources":["../../../src/index.ts"],"sourcesContent":["import type { Event, IntegrationFn, StackFrame } from '@sentry/core';\nimport { defineIntegration, GLOBAL_OBJ } from '@sentry/core';\nimport { uniqueImageForSyntheticFilename } from './matchSyntheticWasmFilename';\nimport { patchWebAssembly } from './patchWebAssembly';\nimport { getImage, getImages, registerModule, toProtocolDebugImage, type RegisteredWasmImage } from './registry';\n\nconst INTEGRATION_NAME = 'Wasm';\n\n// We use the same prefix as bundler plugins so that thirdPartyErrorFilterIntegration\n// recognizes WASM frames as first-party code without needing modifications.\nconst BUNDLER_PLUGIN_APP_KEY_PREFIX = '_sentryBundlerPluginAppKey:';\n\n/**\n * Minimal interface for DedicatedWorkerGlobalScope.\n * We can't use the actual type because it breaks everyone who doesn't have {\"lib\": [\"WebWorker\"]}\n */\ninterface MinimalDedicatedWorkerGlobalScope {\n postMessage: (message: unknown) => void;\n}\n\ninterface RegisterWebWorkerWasmOptions {\n self: MinimalDedicatedWorkerGlobalScope;\n}\n\ninterface WasmIntegrationOptions {\n /**\n * Key to identify this application for third-party error filtering.\n * This key should match one of the keys provided to the `filterKeys` option\n * of the `thirdPartyErrorFilterIntegration`.\n */\n applicationKey?: string;\n}\n\n// Access WINDOW with proper typing for _sentryWasmImages\nconst WINDOW = GLOBAL_OBJ as typeof GLOBAL_OBJ & {\n _sentryWasmImages?: Array<RegisteredWasmImage>;\n};\n\nconst _wasmIntegration = ((options: WasmIntegrationOptions = {}) => {\n return {\n name: INTEGRATION_NAME,\n setupOnce() {\n patchWebAssembly(registerModule);\n },\n processEvent(event: Event): Event {\n let hasAtLeastOneWasmFrameWithImage = false;\n\n const existingImagesCount = event.debug_meta?.images?.length || 0;\n\n if (event.exception?.values) {\n event.exception.values.forEach(exception => {\n if (exception.stacktrace?.frames) {\n hasAtLeastOneWasmFrameWithImage =\n patchFrames(exception.stacktrace.frames, options.applicationKey, existingImagesCount) ||\n hasAtLeastOneWasmFrameWithImage;\n }\n });\n }\n\n if (hasAtLeastOneWasmFrameWithImage) {\n event.debug_meta = event.debug_meta || {};\n const mainThreadImages = getImages().map(toProtocolDebugImage);\n const workerImages = (WINDOW._sentryWasmImages || []).map(toProtocolDebugImage);\n event.debug_meta.images = [...(event.debug_meta.images || []), ...mainThreadImages, ...workerImages];\n }\n\n return event;\n },\n };\n}) satisfies IntegrationFn;\n\nexport const wasmIntegration = defineIntegration(_wasmIntegration);\n\nconst PARSER_REGEX = /^(.*?):wasm-function\\[\\d+\\]:(0x[a-fA-F0-9]+)$/;\n\n/**\n * Patches a list of stackframes with wasm data needed for server-side symbolication\n * if applicable. Returns true if the provided list of stack frames had at least one\n * matching registered image.\n *\n * @param frames - Stack frames to patch\n * @param applicationKey - Optional key for third-party error filtering\n * @param existingImagesOffset - Number of existing debug images that will be prepended\n * to the final images array (used to calculate correct addr_mode indices)\n */\n// Only exported for tests\nexport function patchFrames(\n frames: Array<StackFrame>,\n applicationKey?: string,\n existingImagesOffset: number = 0,\n): boolean {\n let hasAtLeastOneWasmFrameWithImage = false;\n frames.forEach(frame => {\n if (!frame.filename) {\n return;\n }\n\n const split = frame.filename.split('(');\n // eslint-disable-next-line @typescript-eslint/no-non-null-assertion\n const lastSplit = split[split.length - 1]!;\n\n // Let's call this first match a \"messy match\".\n // The browser stacktrace parser spits out frames that have a filename like this: \"int) const (http://localhost:8001/main.wasm:wasm-function[190]:0x5aeb\"\n // It contains some leftover mess because wasm stack frames are more complicated than our parser can handle: \"at MyClass::bar(int) const (http://localhost:8001/main.wasm:wasm-function[190]:0x5aeb)\"\n // This first match simply tries to mitigate the mess up until the first opening parens.\n // The match afterwards is a sensible fallback\n let match = lastSplit.match(PARSER_REGEX) as null | [string, string, string];\n\n if (!match) {\n match = frame.filename.match(PARSER_REGEX) as null | [string, string, string];\n }\n\n // `<url>` is the fetch URL, or `wasm://wasm/<name>-<hash>` when Chrome\n // compiled the module from bytes.\n if (match) {\n let index = getImage(match[1]);\n let workerImageIndex = getWorkerImage(match[1]);\n frame.instruction_addr = match[2];\n frame.filename = match[1];\n frame.platform = 'native';\n\n if (applicationKey) {\n frame.module_metadata = {\n ...frame.module_metadata,\n [`${BUNDLER_PLUGIN_APP_KEY_PREFIX}${applicationKey}`]: true,\n };\n }\n\n // Exact `code_file` miss: `match[1]` is `wasm://wasm/…`, not the registered http URL.\n if (index < 0 && workerImageIndex < 0) {\n const unique = uniqueImageForSyntheticFilename(match[1], getImages(), WINDOW._sentryWasmImages || []);\n if (unique) {\n frame.filename = unique.codeFile;\n if (unique.worker) {\n workerImageIndex = unique.index;\n } else {\n index = unique.index;\n }\n }\n }\n\n if (index >= 0) {\n frame.addr_mode = `rel:${existingImagesOffset + index}`;\n hasAtLeastOneWasmFrameWithImage = true;\n } else if (workerImageIndex >= 0) {\n const mainThreadImagesCount = getImages().length;\n frame.addr_mode = `rel:${existingImagesOffset + mainThreadImagesCount + workerImageIndex}`;\n hasAtLeastOneWasmFrameWithImage = true;\n }\n }\n });\n\n return hasAtLeastOneWasmFrameWithImage;\n}\n\n/**\n * Looks up an image by URL in worker images.\n */\nfunction getWorkerImage(url: string): number {\n const workerImages = WINDOW._sentryWasmImages || [];\n return workerImages.findIndex(image => {\n return image.type === 'wasm' && image.code_file === url;\n });\n}\n\n/**\n * Use this function to register WASM support in a web worker.\n *\n * This function will:\n * - Patch WebAssembly.instantiateStreaming and WebAssembly.compileStreaming in the worker\n * - Forward WASM debug images to the parent thread for symbolication\n *\n * @param options {RegisterWebWorkerWasmOptions} Options:\n * - `self`: The worker's global scope (self).\n */\nexport function registerWebWorkerWasm({ self }: RegisterWebWorkerWasmOptions): void {\n patchWebAssembly((module, url) => {\n const image = registerModule(module, url);\n\n if (image) {\n self.postMessage({\n _sentryMessage: true,\n _sentryWasmImages: [image],\n });\n }\n });\n}\n"],"names":["GLOBAL_OBJ","patchWebAssembly","registerModule","getImages","toProtocolDebugImage","defineIntegration","getImage","uniqueImageForSyntheticFilename"],"mappings":"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"}
@@ -0,0 +1,35 @@
1
+ Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
2
+
3
+ function syntheticModuleName(filename) {
4
+ const body = filename.match(/^wasm:\/\/wasm\/(.+)$/i)?.[1];
5
+ if (!body) {
6
+ return void 0;
7
+ }
8
+ const name = body.replace(/-[0-9a-fA-F]{6,16}$/, "");
9
+ return name === body ? void 0 : name;
10
+ }
11
+ function uniqueImageForSyntheticFilename(filename, pageImages, workerImages) {
12
+ const name = syntheticModuleName(filename);
13
+ if (!name) {
14
+ return void 0;
15
+ }
16
+ const hits = [];
17
+ const consider = (images, worker) => {
18
+ images.forEach((image, index) => {
19
+ if (image.moduleName === name) {
20
+ hits.push({ index, worker, codeFile: image.code_file, debugId: image.debug_id });
21
+ }
22
+ });
23
+ };
24
+ consider(pageImages, false);
25
+ consider(workerImages, true);
26
+ const hit = hits[0];
27
+ if (!hit || hits.some((other) => other.debugId !== hit.debugId)) {
28
+ return void 0;
29
+ }
30
+ return { index: hit.index, worker: hit.worker, codeFile: hit.codeFile };
31
+ }
32
+
33
+ exports.syntheticModuleName = syntheticModuleName;
34
+ exports.uniqueImageForSyntheticFilename = uniqueImageForSyntheticFilename;
35
+ //# sourceMappingURL=matchSyntheticWasmFilename.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"matchSyntheticWasmFilename.js","sources":["../../../src/matchSyntheticWasmFilename.ts"],"sourcesContent":["import type { RegisteredWasmImage } from './registry';\n\n/**\n * Maps Chrome `wasm://wasm/<name>-<hash>` frames to a registered `code_file`.\n *\n * V8 builds the label from the wasm `name` section, which is parsed into\n * `moduleName` at registration. A hit is accepted only when every image with\n * that name shares one `debug_id`, since a page and a worker can register the\n * same binary under different URLs.\n *\n * Hash-only `wasm://wasm/<hash>` labels carry no name and are not mapped\n * (see #23781).\n */\n\nexport type SyntheticWasmImageHit = {\n index: number;\n worker: boolean;\n codeFile: string;\n};\n\n/**\n * Module name from Chrome's label: `wasm://wasm/demo.wasm-000197f6` → `demo.wasm`.\n *\n * V8 appends the `-<hash>` suffix only after a module name. A label without\n * it is hash-only and yields `undefined`, so a hex-looking module name such\n * as `ed25519` is still returned.\n */\nexport function syntheticModuleName(filename: string): string | undefined {\n const body = filename.match(/^wasm:\\/\\/wasm\\/(.+)$/i)?.[1];\n if (!body) {\n return undefined;\n }\n const name = body.replace(/-[0-9a-fA-F]{6,16}$/, '');\n return name === body ? undefined : name;\n}\n\nexport function uniqueImageForSyntheticFilename(\n filename: string,\n pageImages: ReadonlyArray<RegisteredWasmImage>,\n workerImages: ReadonlyArray<RegisteredWasmImage>,\n): SyntheticWasmImageHit | undefined {\n const name = syntheticModuleName(filename);\n if (!name) {\n return undefined;\n }\n\n const hits: Array<SyntheticWasmImageHit & { debugId: string }> = [];\n const consider = (images: ReadonlyArray<RegisteredWasmImage>, worker: boolean): void => {\n images.forEach((image, index) => {\n if (image.moduleName === name) {\n hits.push({ index, worker, codeFile: image.code_file, debugId: image.debug_id });\n }\n });\n };\n consider(pageImages, false);\n consider(workerImages, true);\n\n const hit = hits[0];\n if (!hit || hits.some(other => other.debugId !== hit.debugId)) {\n return undefined;\n }\n return { index: hit.index, worker: hit.worker, codeFile: hit.codeFile };\n}\n"],"names":[],"mappings":";;AA2BO,SAAS,oBAAoB,QAAA,EAAsC;AACxE,EAAA,MAAM,IAAA,GAAO,QAAA,CAAS,KAAA,CAAM,wBAAwB,IAAI,CAAC,CAAA;AACzD,EAAA,IAAI,CAAC,IAAA,EAAM;AACT,IAAA,OAAO,MAAA;AAAA,EACT;AACA,EAAA,MAAM,IAAA,GAAO,IAAA,CAAK,OAAA,CAAQ,qBAAA,EAAuB,EAAE,CAAA;AACnD,EAAA,OAAO,IAAA,KAAS,OAAO,MAAA,GAAY,IAAA;AACrC;AAEO,SAAS,+BAAA,CACd,QAAA,EACA,UAAA,EACA,YAAA,EACmC;AACnC,EAAA,MAAM,IAAA,GAAO,oBAAoB,QAAQ,CAAA;AACzC,EAAA,IAAI,CAAC,IAAA,EAAM;AACT,IAAA,OAAO,MAAA;AAAA,EACT;AAEA,EAAA,MAAM,OAA2D,EAAC;AAClE,EAAA,MAAM,QAAA,GAAW,CAAC,MAAA,EAA4C,MAAA,KAA0B;AACtF,IAAA,MAAA,CAAO,OAAA,CAAQ,CAAC,KAAA,EAAO,KAAA,KAAU;AAC/B,MAAA,IAAI,KAAA,CAAM,eAAe,IAAA,EAAM;AAC7B,QAAA,IAAA,CAAK,IAAA,CAAK,EAAE,KAAA,EAAO,MAAA,EAAQ,QAAA,EAAU,MAAM,SAAA,EAAW,OAAA,EAAS,KAAA,CAAM,QAAA,EAAU,CAAA;AAAA,MACjF;AAAA,IACF,CAAC,CAAA;AAAA,EACH,CAAA;AACA,EAAA,QAAA,CAAS,YAAY,KAAK,CAAA;AAC1B,EAAA,QAAA,CAAS,cAAc,IAAI,CAAA;AAE3B,EAAA,MAAM,GAAA,GAAM,KAAK,CAAC,CAAA;AAClB,EAAA,IAAI,CAAC,OAAO,IAAA,CAAK,IAAA,CAAK,WAAS,KAAA,CAAM,OAAA,KAAY,GAAA,CAAI,OAAO,CAAA,EAAG;AAC7D,IAAA,OAAO,MAAA;AAAA,EACT;AACA,EAAA,OAAO,EAAE,OAAO,GAAA,CAAI,KAAA,EAAO,QAAQ,GAAA,CAAI,MAAA,EAAQ,QAAA,EAAU,GAAA,CAAI,QAAA,EAAS;AACxE;;;;;"}
@@ -1,5 +1,7 @@
1
1
  Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
2
2
 
3
+ const wasmNameSection = require('./wasmNameSection.js');
4
+
3
5
  const IMAGES = [];
4
6
  function getModuleInfo(module) {
5
7
  const buildIds = WebAssembly.Module.customSections(module, "build_id");
@@ -19,10 +21,20 @@ function getModuleInfo(module) {
19
21
  const decoder = new TextDecoder("utf-8");
20
22
  debugFile = decoder.decode(firstExternalDebugInfo);
21
23
  }
22
- return { buildId, debugFile };
24
+ let moduleName = null;
25
+ try {
26
+ const nameSections = WebAssembly.Module.customSections(module, "name");
27
+ const nameSection0 = nameSections[0];
28
+ if (nameSection0) {
29
+ moduleName = wasmNameSection.parseNameSectionModuleName(nameSection0);
30
+ }
31
+ } catch {
32
+ moduleName = null;
33
+ }
34
+ return { buildId, debugFile, moduleName };
23
35
  }
24
36
  function registerModule(module, url) {
25
- const { buildId, debugFile } = getModuleInfo(module);
37
+ const { buildId, debugFile, moduleName } = getModuleInfo(module);
26
38
  if (!buildId) {
27
39
  return null;
28
40
  }
@@ -44,12 +56,19 @@ function registerModule(module, url) {
44
56
  debug_file: debugFileUrl,
45
57
  debug_id: `${buildId.padEnd(32, "0").slice(0, 32)}0`
46
58
  };
59
+ if (moduleName) {
60
+ image.moduleName = moduleName;
61
+ }
47
62
  IMAGES.push(image);
48
63
  return image;
49
64
  }
50
65
  function getImages() {
51
66
  return IMAGES;
52
67
  }
68
+ function toProtocolDebugImage(image) {
69
+ const { moduleName: _moduleName, ...protocol } = image;
70
+ return protocol;
71
+ }
53
72
  function getImage(url) {
54
73
  return IMAGES.findIndex((image) => {
55
74
  return image.type === "wasm" && image.code_file === url;
@@ -61,4 +80,5 @@ exports.getImage = getImage;
61
80
  exports.getImages = getImages;
62
81
  exports.getModuleInfo = getModuleInfo;
63
82
  exports.registerModule = registerModule;
83
+ exports.toProtocolDebugImage = toProtocolDebugImage;
64
84
  //# sourceMappingURL=registry.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"registry.js","sources":["../../../src/registry.ts"],"sourcesContent":["import type { DebugImage } from '@sentry/core';\n\nexport const IMAGES: Array<DebugImage> = [];\n\nexport interface ModuleInfo {\n buildId: string | null;\n debugFile: string | null;\n}\n\n/**\n * Returns the extracted meta information from a web assembly module that\n * Sentry uses to identify debug images.\n *\n * @param module\n */\nexport function getModuleInfo(module: WebAssembly.Module): ModuleInfo {\n const buildIds = WebAssembly.Module.customSections(module, 'build_id');\n let buildId = null;\n let debugFile = null;\n\n const buildId0 = buildIds[0];\n if (buildId0) {\n const firstBuildId = new Uint8Array(buildId0);\n buildId = Array.from(firstBuildId).reduce((acc, x) => {\n return acc + x.toString(16).padStart(2, '0');\n }, '');\n }\n\n const externalDebugInfo = WebAssembly.Module.customSections(module, 'external_debug_info');\n const externalDebugInfo0 = externalDebugInfo[0];\n if (externalDebugInfo0) {\n const firstExternalDebugInfo = new Uint8Array(externalDebugInfo0);\n const decoder = new TextDecoder('utf-8');\n debugFile = decoder.decode(firstExternalDebugInfo);\n }\n\n return { buildId, debugFile };\n}\n\n/**\n * Records a module and returns the created debug image.\n */\nexport function registerModule(module: WebAssembly.Module, url: string): DebugImage | null {\n const { buildId, debugFile } = getModuleInfo(module);\n if (!buildId) {\n return null;\n }\n\n const oldIdx = getImage(url);\n if (oldIdx >= 0) {\n IMAGES.splice(oldIdx, 1);\n }\n\n let debugFileUrl = null;\n if (debugFile) {\n try {\n debugFileUrl = new URL(debugFile, url).href;\n } catch {\n // debugFile could be a blob URL which causes the URL constructor to throw\n // for now we just ignore this case\n }\n }\n\n const image: DebugImage = {\n type: 'wasm',\n code_id: buildId,\n code_file: url,\n debug_file: debugFileUrl,\n debug_id: `${buildId.padEnd(32, '0').slice(0, 32)}0`,\n };\n\n IMAGES.push(image);\n return image;\n}\n\n/**\n * Returns all known images.\n */\nexport function getImages(): Array<DebugImage> {\n return IMAGES;\n}\n\n/**\n * Looks up an image by URL.\n *\n * @param url the URL of the WebAssembly module.\n */\nexport function getImage(url: string): number {\n return IMAGES.findIndex(image => {\n return image.type === 'wasm' && image.code_file === url;\n });\n}\n"],"names":[],"mappings":";;AAEO,MAAM,SAA4B;AAalC,SAAS,cAAc,MAAA,EAAwC;AACpE,EAAA,MAAM,QAAA,GAAW,WAAA,CAAY,MAAA,CAAO,cAAA,CAAe,QAAQ,UAAU,CAAA;AACrE,EAAA,IAAI,OAAA,GAAU,IAAA;AACd,EAAA,IAAI,SAAA,GAAY,IAAA;AAEhB,EAAA,MAAM,QAAA,GAAW,SAAS,CAAC,CAAA;AAC3B,EAAA,IAAI,QAAA,EAAU;AACZ,IAAA,MAAM,YAAA,GAAe,IAAI,UAAA,CAAW,QAAQ,CAAA;AAC5C,IAAA,OAAA,GAAU,MAAM,IAAA,CAAK,YAAY,EAAE,MAAA,CAAO,CAAC,KAAK,CAAA,KAAM;AACpD,MAAA,OAAO,MAAM,CAAA,CAAE,QAAA,CAAS,EAAE,CAAA,CAAE,QAAA,CAAS,GAAG,GAAG,CAAA;AAAA,IAC7C,GAAG,EAAE,CAAA;AAAA,EACP;AAEA,EAAA,MAAM,iBAAA,GAAoB,WAAA,CAAY,MAAA,CAAO,cAAA,CAAe,QAAQ,qBAAqB,CAAA;AACzF,EAAA,MAAM,kBAAA,GAAqB,kBAAkB,CAAC,CAAA;AAC9C,EAAA,IAAI,kBAAA,EAAoB;AACtB,IAAA,MAAM,sBAAA,GAAyB,IAAI,UAAA,CAAW,kBAAkB,CAAA;AAChE,IAAA,MAAM,OAAA,GAAU,IAAI,WAAA,CAAY,OAAO,CAAA;AACvC,IAAA,SAAA,GAAY,OAAA,CAAQ,OAAO,sBAAsB,CAAA;AAAA,EACnD;AAEA,EAAA,OAAO,EAAE,SAAS,SAAA,EAAU;AAC9B;AAKO,SAAS,cAAA,CAAe,QAA4B,GAAA,EAAgC;AACzF,EAAA,MAAM,EAAE,OAAA,EAAS,SAAA,EAAU,GAAI,cAAc,MAAM,CAAA;AACnD,EAAA,IAAI,CAAC,OAAA,EAAS;AACZ,IAAA,OAAO,IAAA;AAAA,EACT;AAEA,EAAA,MAAM,MAAA,GAAS,SAAS,GAAG,CAAA;AAC3B,EAAA,IAAI,UAAU,CAAA,EAAG;AACf,IAAA,MAAA,CAAO,MAAA,CAAO,QAAQ,CAAC,CAAA;AAAA,EACzB;AAEA,EAAA,IAAI,YAAA,GAAe,IAAA;AACnB,EAAA,IAAI,SAAA,EAAW;AACb,IAAA,IAAI;AACF,MAAA,YAAA,GAAe,IAAI,GAAA,CAAI,SAAA,EAAW,GAAG,CAAA,CAAE,IAAA;AAAA,IACzC,CAAA,CAAA,MAAQ;AAAA,IAGR;AAAA,EACF;AAEA,EAAA,MAAM,KAAA,GAAoB;AAAA,IACxB,IAAA,EAAM,MAAA;AAAA,IACN,OAAA,EAAS,OAAA;AAAA,IACT,SAAA,EAAW,GAAA;AAAA,IACX,UAAA,EAAY,YAAA;AAAA,IACZ,QAAA,EAAU,CAAA,EAAG,OAAA,CAAQ,MAAA,CAAO,EAAA,EAAI,GAAG,CAAA,CAAE,KAAA,CAAM,CAAA,EAAG,EAAE,CAAC,CAAA,CAAA;AAAA,GACnD;AAEA,EAAA,MAAA,CAAO,KAAK,KAAK,CAAA;AACjB,EAAA,OAAO,KAAA;AACT;AAKO,SAAS,SAAA,GAA+B;AAC7C,EAAA,OAAO,MAAA;AACT;AAOO,SAAS,SAAS,GAAA,EAAqB;AAC5C,EAAA,OAAO,MAAA,CAAO,UAAU,CAAA,KAAA,KAAS;AAC/B,IAAA,OAAO,KAAA,CAAM,IAAA,KAAS,MAAA,IAAU,KAAA,CAAM,SAAA,KAAc,GAAA;AAAA,EACtD,CAAC,CAAA;AACH;;;;;;;;"}
1
+ {"version":3,"file":"registry.js","sources":["../../../src/registry.ts"],"sourcesContent":["import type { DebugImage } from '@sentry/core';\nimport { parseNameSectionModuleName } from './wasmNameSection';\n\nexport type RegisteredWasmImage = Extract<DebugImage, { type: 'wasm' }> & {\n /** Internal: wasm `name` section. Used to link `wasm://` stacks; stripped before the event is sent. */\n moduleName?: string;\n};\n\nexport const IMAGES: Array<RegisteredWasmImage> = [];\n\nexport interface ModuleInfo {\n buildId: string | null;\n debugFile: string | null;\n moduleName: string | null;\n}\n\n/**\n * Returns the extracted meta information from a web assembly module that\n * Sentry uses to identify debug images.\n *\n * @param module\n */\nexport function getModuleInfo(module: WebAssembly.Module): ModuleInfo {\n const buildIds = WebAssembly.Module.customSections(module, 'build_id');\n let buildId = null;\n let debugFile = null;\n\n const buildId0 = buildIds[0];\n if (buildId0) {\n const firstBuildId = new Uint8Array(buildId0);\n buildId = Array.from(firstBuildId).reduce((acc, x) => {\n return acc + x.toString(16).padStart(2, '0');\n }, '');\n }\n\n const externalDebugInfo = WebAssembly.Module.customSections(module, 'external_debug_info');\n const externalDebugInfo0 = externalDebugInfo[0];\n if (externalDebugInfo0) {\n const firstExternalDebugInfo = new Uint8Array(externalDebugInfo0);\n const decoder = new TextDecoder('utf-8');\n debugFile = decoder.decode(firstExternalDebugInfo);\n }\n\n let moduleName = null;\n try {\n const nameSections = WebAssembly.Module.customSections(module, 'name');\n const nameSection0 = nameSections[0];\n if (nameSection0) {\n moduleName = parseNameSectionModuleName(nameSection0);\n }\n } catch {\n moduleName = null;\n }\n\n return { buildId, debugFile, moduleName };\n}\n\n/**\n * Records a module and returns the created debug image.\n */\nexport function registerModule(module: WebAssembly.Module, url: string): RegisteredWasmImage | null {\n const { buildId, debugFile, moduleName } = getModuleInfo(module);\n if (!buildId) {\n return null;\n }\n\n const oldIdx = getImage(url);\n if (oldIdx >= 0) {\n IMAGES.splice(oldIdx, 1);\n }\n\n let debugFileUrl = null;\n if (debugFile) {\n try {\n debugFileUrl = new URL(debugFile, url).href;\n } catch {\n // debugFile could be a blob URL which causes the URL constructor to throw\n // for now we just ignore this case\n }\n }\n\n const image: RegisteredWasmImage = {\n type: 'wasm',\n code_id: buildId,\n code_file: url,\n debug_file: debugFileUrl,\n debug_id: `${buildId.padEnd(32, '0').slice(0, 32)}0`,\n };\n if (moduleName) {\n image.moduleName = moduleName;\n }\n\n IMAGES.push(image);\n return image;\n}\n\n/**\n * Returns all known images.\n */\nexport function getImages(): Array<RegisteredWasmImage> {\n return IMAGES;\n}\n\n/**\n * Debug image payload for Sentry: protocol fields only (no internal `moduleName`).\n */\nexport function toProtocolDebugImage(image: RegisteredWasmImage): DebugImage {\n const { moduleName: _moduleName, ...protocol } = image;\n return protocol;\n}\n\n/**\n * Looks up an image by URL.\n *\n * @param url the URL of the WebAssembly module.\n */\nexport function getImage(url: string): number {\n return IMAGES.findIndex(image => {\n return image.type === 'wasm' && image.code_file === url;\n });\n}\n"],"names":["parseNameSectionModuleName"],"mappings":"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"}
@@ -0,0 +1,67 @@
1
+ Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
2
+
3
+ function parseNameSectionModuleName(source) {
4
+ try {
5
+ return readModuleName(toBytes(source));
6
+ } catch {
7
+ return null;
8
+ }
9
+ }
10
+ function toBytes(source) {
11
+ if (source instanceof ArrayBuffer) {
12
+ return new Uint8Array(source);
13
+ }
14
+ return new Uint8Array(source.buffer, source.byteOffset, source.byteLength);
15
+ }
16
+ function readModuleName(bytes) {
17
+ const cursor = { offset: 0 };
18
+ while (cursor.offset < bytes.length) {
19
+ const id = bytes[cursor.offset];
20
+ cursor.offset += 1;
21
+ if (id === void 0) {
22
+ return null;
23
+ }
24
+ const size = readU32Leb(bytes, cursor);
25
+ if (size === void 0 || cursor.offset + size > bytes.length) {
26
+ return null;
27
+ }
28
+ const start = cursor.offset;
29
+ cursor.offset += size;
30
+ if (id === 0) {
31
+ return readName(bytes.subarray(start, start + size));
32
+ }
33
+ }
34
+ return null;
35
+ }
36
+ function readName(bytes) {
37
+ const cursor = { offset: 0 };
38
+ const length = readU32Leb(bytes, cursor);
39
+ if (length === void 0 || length === 0 || cursor.offset + length !== bytes.length) {
40
+ return null;
41
+ }
42
+ try {
43
+ return new TextDecoder("utf-8", { fatal: true }).decode(bytes.subarray(cursor.offset));
44
+ } catch {
45
+ return null;
46
+ }
47
+ }
48
+ function readU32Leb(bytes, cursor) {
49
+ let result = 0;
50
+ let shift = 0;
51
+ while (cursor.offset < bytes.length) {
52
+ const byte = bytes[cursor.offset];
53
+ cursor.offset += 1;
54
+ if (byte === void 0 || shift >= 35) {
55
+ return void 0;
56
+ }
57
+ result |= (byte & 127) << shift;
58
+ if ((byte & 128) === 0) {
59
+ return result >>> 0;
60
+ }
61
+ shift += 7;
62
+ }
63
+ return void 0;
64
+ }
65
+
66
+ exports.parseNameSectionModuleName = parseNameSectionModuleName;
67
+ //# sourceMappingURL=wasmNameSection.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"wasmNameSection.js","sources":["../../../src/wasmNameSection.ts"],"sourcesContent":["/* eslint-disable no-bitwise -- LEB128 is a bitwise encoding */\n\n/**\n * Parses the wasm `name` custom section payload — the bytes\n * `WebAssembly.Module.customSections(module, 'name')` returns.\n *\n * Chrome's buffer-compiled stacks use this module name as `wasm://wasm/<name>-<hash>`,\n * which often differs from the fetch URL (wasm-bindgen `demo.wasm` vs `demo_bg.wasm`).\n * Missing, stripped, or malformed sections return null. Callers then guess from\n * the fetch URL basename if the name is missing or does not match the stack.\n *\n * @see https://webassembly.github.io/spec/core/appendix/custom.html#name-section\n */\nexport function parseNameSectionModuleName(source: ArrayBuffer | Uint8Array): string | null {\n try {\n return readModuleName(toBytes(source));\n } catch {\n return null;\n }\n}\n\nfunction toBytes(source: ArrayBuffer | Uint8Array): Uint8Array {\n if (source instanceof ArrayBuffer) {\n return new Uint8Array(source);\n }\n return new Uint8Array(source.buffer, source.byteOffset, source.byteLength);\n}\n\nfunction readModuleName(bytes: Uint8Array): string | null {\n const cursor = { offset: 0 };\n while (cursor.offset < bytes.length) {\n const id = bytes[cursor.offset];\n cursor.offset += 1;\n if (id === undefined) {\n return null;\n }\n\n const size = readU32Leb(bytes, cursor);\n if (size === undefined || cursor.offset + size > bytes.length) {\n return null;\n }\n\n const start = cursor.offset;\n cursor.offset += size;\n if (id === 0) {\n return readName(bytes.subarray(start, start + size));\n }\n }\n\n return null;\n}\n\nfunction readName(bytes: Uint8Array): string | null {\n const cursor = { offset: 0 };\n const length = readU32Leb(bytes, cursor);\n if (length === undefined || length === 0 || cursor.offset + length !== bytes.length) {\n return null;\n }\n\n try {\n return new TextDecoder('utf-8', { fatal: true }).decode(bytes.subarray(cursor.offset));\n } catch {\n return null;\n }\n}\n\nfunction readU32Leb(bytes: Uint8Array, cursor: { offset: number }): number | undefined {\n let result = 0;\n let shift = 0;\n\n while (cursor.offset < bytes.length) {\n const byte = bytes[cursor.offset];\n cursor.offset += 1;\n if (byte === undefined || shift >= 35) {\n return undefined;\n }\n\n result |= (byte & 0x7f) << shift;\n if ((byte & 0x80) === 0) {\n return result >>> 0;\n }\n shift += 7;\n }\n\n return undefined;\n}\n"],"names":[],"mappings":"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"}
@@ -1,6 +1,7 @@
1
1
  import { defineIntegration, GLOBAL_OBJ } from '@sentry/core';
2
+ import { uniqueImageForSyntheticFilename } from './matchSyntheticWasmFilename.js';
2
3
  import { patchWebAssembly } from './patchWebAssembly.js';
3
- import { getImage, getImages, registerModule } from './registry.js';
4
+ import { getImages, toProtocolDebugImage, getImage, registerModule } from './registry.js';
4
5
 
5
6
  const INTEGRATION_NAME = "Wasm";
6
7
  const BUNDLER_PLUGIN_APP_KEY_PREFIX = "_sentryBundlerPluginAppKey:";
@@ -23,8 +24,8 @@ const _wasmIntegration = ((options = {}) => {
23
24
  }
24
25
  if (hasAtLeastOneWasmFrameWithImage) {
25
26
  event.debug_meta = event.debug_meta || {};
26
- const mainThreadImages = getImages();
27
- const workerImages = WINDOW._sentryWasmImages || [];
27
+ const mainThreadImages = getImages().map(toProtocolDebugImage);
28
+ const workerImages = (WINDOW._sentryWasmImages || []).map(toProtocolDebugImage);
28
29
  event.debug_meta.images = [...event.debug_meta.images || [], ...mainThreadImages, ...workerImages];
29
30
  }
30
31
  return event;
@@ -46,8 +47,8 @@ function patchFrames(frames, applicationKey, existingImagesOffset = 0) {
46
47
  match = frame.filename.match(PARSER_REGEX);
47
48
  }
48
49
  if (match) {
49
- const index = getImage(match[1]);
50
- const workerImageIndex = getWorkerImage(match[1]);
50
+ let index = getImage(match[1]);
51
+ let workerImageIndex = getWorkerImage(match[1]);
51
52
  frame.instruction_addr = match[2];
52
53
  frame.filename = match[1];
53
54
  frame.platform = "native";
@@ -57,6 +58,17 @@ function patchFrames(frames, applicationKey, existingImagesOffset = 0) {
57
58
  [`${BUNDLER_PLUGIN_APP_KEY_PREFIX}${applicationKey}`]: true
58
59
  };
59
60
  }
61
+ if (index < 0 && workerImageIndex < 0) {
62
+ const unique = uniqueImageForSyntheticFilename(match[1], getImages(), WINDOW._sentryWasmImages || []);
63
+ if (unique) {
64
+ frame.filename = unique.codeFile;
65
+ if (unique.worker) {
66
+ workerImageIndex = unique.index;
67
+ } else {
68
+ index = unique.index;
69
+ }
70
+ }
71
+ }
60
72
  if (index >= 0) {
61
73
  frame.addr_mode = `rel:${existingImagesOffset + index}`;
62
74
  hasAtLeastOneWasmFrameWithImage = true;
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sources":["../../../src/index.ts"],"sourcesContent":["import type { DebugImage, Event, IntegrationFn, StackFrame } from '@sentry/core';\nimport { defineIntegration, GLOBAL_OBJ } from '@sentry/core';\nimport { patchWebAssembly } from './patchWebAssembly';\nimport { getImage, getImages, registerModule } from './registry';\n\nconst INTEGRATION_NAME = 'Wasm';\n\n// We use the same prefix as bundler plugins so that thirdPartyErrorFilterIntegration\n// recognizes WASM frames as first-party code without needing modifications.\nconst BUNDLER_PLUGIN_APP_KEY_PREFIX = '_sentryBundlerPluginAppKey:';\n\n/**\n * Minimal interface for DedicatedWorkerGlobalScope.\n * We can't use the actual type because it breaks everyone who doesn't have {\"lib\": [\"WebWorker\"]}\n */\ninterface MinimalDedicatedWorkerGlobalScope {\n postMessage: (message: unknown) => void;\n}\n\ninterface RegisterWebWorkerWasmOptions {\n self: MinimalDedicatedWorkerGlobalScope;\n}\n\ninterface WasmIntegrationOptions {\n /**\n * Key to identify this application for third-party error filtering.\n * This key should match one of the keys provided to the `filterKeys` option\n * of the `thirdPartyErrorFilterIntegration`.\n */\n applicationKey?: string;\n}\n\n// Access WINDOW with proper typing for _sentryWasmImages\nconst WINDOW = GLOBAL_OBJ as typeof GLOBAL_OBJ & {\n _sentryWasmImages?: Array<DebugImage>;\n};\n\nconst _wasmIntegration = ((options: WasmIntegrationOptions = {}) => {\n return {\n name: INTEGRATION_NAME,\n setupOnce() {\n patchWebAssembly(registerModule);\n },\n processEvent(event: Event): Event {\n let hasAtLeastOneWasmFrameWithImage = false;\n\n const existingImagesCount = event.debug_meta?.images?.length || 0;\n\n if (event.exception?.values) {\n event.exception.values.forEach(exception => {\n if (exception.stacktrace?.frames) {\n hasAtLeastOneWasmFrameWithImage =\n patchFrames(exception.stacktrace.frames, options.applicationKey, existingImagesCount) ||\n hasAtLeastOneWasmFrameWithImage;\n }\n });\n }\n\n if (hasAtLeastOneWasmFrameWithImage) {\n event.debug_meta = event.debug_meta || {};\n const mainThreadImages = getImages();\n const workerImages = WINDOW._sentryWasmImages || [];\n event.debug_meta.images = [...(event.debug_meta.images || []), ...mainThreadImages, ...workerImages];\n }\n\n return event;\n },\n };\n}) satisfies IntegrationFn;\n\nexport const wasmIntegration = defineIntegration(_wasmIntegration);\n\nconst PARSER_REGEX = /^(.*?):wasm-function\\[\\d+\\]:(0x[a-fA-F0-9]+)$/;\n\n/**\n * Patches a list of stackframes with wasm data needed for server-side symbolication\n * if applicable. Returns true if the provided list of stack frames had at least one\n * matching registered image.\n *\n * @param frames - Stack frames to patch\n * @param applicationKey - Optional key for third-party error filtering\n * @param existingImagesOffset - Number of existing debug images that will be prepended\n * to the final images array (used to calculate correct addr_mode indices)\n */\n// Only exported for tests\nexport function patchFrames(\n frames: Array<StackFrame>,\n applicationKey?: string,\n existingImagesOffset: number = 0,\n): boolean {\n let hasAtLeastOneWasmFrameWithImage = false;\n frames.forEach(frame => {\n if (!frame.filename) {\n return;\n }\n\n const split = frame.filename.split('(');\n // eslint-disable-next-line @typescript-eslint/no-non-null-assertion\n const lastSplit = split[split.length - 1]!;\n\n // Let's call this first match a \"messy match\".\n // The browser stacktrace parser spits out frames that have a filename like this: \"int) const (http://localhost:8001/main.wasm:wasm-function[190]:0x5aeb\"\n // It contains some leftover mess because wasm stack frames are more complicated than our parser can handle: \"at MyClass::bar(int) const (http://localhost:8001/main.wasm:wasm-function[190]:0x5aeb)\"\n // This first match simply tries to mitigate the mess up until the first opening parens.\n // The match afterwards is a sensible fallback\n let match = lastSplit.match(PARSER_REGEX) as null | [string, string, string];\n\n if (!match) {\n match = frame.filename.match(PARSER_REGEX) as null | [string, string, string];\n }\n\n if (match) {\n const index = getImage(match[1]);\n const workerImageIndex = getWorkerImage(match[1]);\n frame.instruction_addr = match[2];\n frame.filename = match[1];\n frame.platform = 'native';\n\n if (applicationKey) {\n frame.module_metadata = {\n ...frame.module_metadata,\n [`${BUNDLER_PLUGIN_APP_KEY_PREFIX}${applicationKey}`]: true,\n };\n }\n\n if (index >= 0) {\n frame.addr_mode = `rel:${existingImagesOffset + index}`;\n hasAtLeastOneWasmFrameWithImage = true;\n } else if (workerImageIndex >= 0) {\n const mainThreadImagesCount = getImages().length;\n frame.addr_mode = `rel:${existingImagesOffset + mainThreadImagesCount + workerImageIndex}`;\n hasAtLeastOneWasmFrameWithImage = true;\n }\n }\n });\n\n return hasAtLeastOneWasmFrameWithImage;\n}\n\n/**\n * Looks up an image by URL in worker images.\n */\nfunction getWorkerImage(url: string): number {\n const workerImages = WINDOW._sentryWasmImages || [];\n return workerImages.findIndex(image => {\n return image.type === 'wasm' && image.code_file === url;\n });\n}\n\n/**\n * Use this function to register WASM support in a web worker.\n *\n * This function will:\n * - Patch WebAssembly.instantiateStreaming and WebAssembly.compileStreaming in the worker\n * - Forward WASM debug images to the parent thread for symbolication\n *\n * @param options {RegisterWebWorkerWasmOptions} Options:\n * - `self`: The worker's global scope (self).\n */\nexport function registerWebWorkerWasm({ self }: RegisterWebWorkerWasmOptions): void {\n patchWebAssembly((module, url) => {\n const image = registerModule(module, url);\n\n if (image) {\n self.postMessage({\n _sentryMessage: true,\n _sentryWasmImages: [image],\n });\n }\n });\n}\n"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"index.js","sources":["../../../src/index.ts"],"sourcesContent":["import type { Event, IntegrationFn, StackFrame } from '@sentry/core';\nimport { defineIntegration, GLOBAL_OBJ } from '@sentry/core';\nimport { uniqueImageForSyntheticFilename } from './matchSyntheticWasmFilename';\nimport { patchWebAssembly } from './patchWebAssembly';\nimport { getImage, getImages, registerModule, toProtocolDebugImage, type RegisteredWasmImage } from './registry';\n\nconst INTEGRATION_NAME = 'Wasm';\n\n// We use the same prefix as bundler plugins so that thirdPartyErrorFilterIntegration\n// recognizes WASM frames as first-party code without needing modifications.\nconst BUNDLER_PLUGIN_APP_KEY_PREFIX = '_sentryBundlerPluginAppKey:';\n\n/**\n * Minimal interface for DedicatedWorkerGlobalScope.\n * We can't use the actual type because it breaks everyone who doesn't have {\"lib\": [\"WebWorker\"]}\n */\ninterface MinimalDedicatedWorkerGlobalScope {\n postMessage: (message: unknown) => void;\n}\n\ninterface RegisterWebWorkerWasmOptions {\n self: MinimalDedicatedWorkerGlobalScope;\n}\n\ninterface WasmIntegrationOptions {\n /**\n * Key to identify this application for third-party error filtering.\n * This key should match one of the keys provided to the `filterKeys` option\n * of the `thirdPartyErrorFilterIntegration`.\n */\n applicationKey?: string;\n}\n\n// Access WINDOW with proper typing for _sentryWasmImages\nconst WINDOW = GLOBAL_OBJ as typeof GLOBAL_OBJ & {\n _sentryWasmImages?: Array<RegisteredWasmImage>;\n};\n\nconst _wasmIntegration = ((options: WasmIntegrationOptions = {}) => {\n return {\n name: INTEGRATION_NAME,\n setupOnce() {\n patchWebAssembly(registerModule);\n },\n processEvent(event: Event): Event {\n let hasAtLeastOneWasmFrameWithImage = false;\n\n const existingImagesCount = event.debug_meta?.images?.length || 0;\n\n if (event.exception?.values) {\n event.exception.values.forEach(exception => {\n if (exception.stacktrace?.frames) {\n hasAtLeastOneWasmFrameWithImage =\n patchFrames(exception.stacktrace.frames, options.applicationKey, existingImagesCount) ||\n hasAtLeastOneWasmFrameWithImage;\n }\n });\n }\n\n if (hasAtLeastOneWasmFrameWithImage) {\n event.debug_meta = event.debug_meta || {};\n const mainThreadImages = getImages().map(toProtocolDebugImage);\n const workerImages = (WINDOW._sentryWasmImages || []).map(toProtocolDebugImage);\n event.debug_meta.images = [...(event.debug_meta.images || []), ...mainThreadImages, ...workerImages];\n }\n\n return event;\n },\n };\n}) satisfies IntegrationFn;\n\nexport const wasmIntegration = defineIntegration(_wasmIntegration);\n\nconst PARSER_REGEX = /^(.*?):wasm-function\\[\\d+\\]:(0x[a-fA-F0-9]+)$/;\n\n/**\n * Patches a list of stackframes with wasm data needed for server-side symbolication\n * if applicable. Returns true if the provided list of stack frames had at least one\n * matching registered image.\n *\n * @param frames - Stack frames to patch\n * @param applicationKey - Optional key for third-party error filtering\n * @param existingImagesOffset - Number of existing debug images that will be prepended\n * to the final images array (used to calculate correct addr_mode indices)\n */\n// Only exported for tests\nexport function patchFrames(\n frames: Array<StackFrame>,\n applicationKey?: string,\n existingImagesOffset: number = 0,\n): boolean {\n let hasAtLeastOneWasmFrameWithImage = false;\n frames.forEach(frame => {\n if (!frame.filename) {\n return;\n }\n\n const split = frame.filename.split('(');\n // eslint-disable-next-line @typescript-eslint/no-non-null-assertion\n const lastSplit = split[split.length - 1]!;\n\n // Let's call this first match a \"messy match\".\n // The browser stacktrace parser spits out frames that have a filename like this: \"int) const (http://localhost:8001/main.wasm:wasm-function[190]:0x5aeb\"\n // It contains some leftover mess because wasm stack frames are more complicated than our parser can handle: \"at MyClass::bar(int) const (http://localhost:8001/main.wasm:wasm-function[190]:0x5aeb)\"\n // This first match simply tries to mitigate the mess up until the first opening parens.\n // The match afterwards is a sensible fallback\n let match = lastSplit.match(PARSER_REGEX) as null | [string, string, string];\n\n if (!match) {\n match = frame.filename.match(PARSER_REGEX) as null | [string, string, string];\n }\n\n // `<url>` is the fetch URL, or `wasm://wasm/<name>-<hash>` when Chrome\n // compiled the module from bytes.\n if (match) {\n let index = getImage(match[1]);\n let workerImageIndex = getWorkerImage(match[1]);\n frame.instruction_addr = match[2];\n frame.filename = match[1];\n frame.platform = 'native';\n\n if (applicationKey) {\n frame.module_metadata = {\n ...frame.module_metadata,\n [`${BUNDLER_PLUGIN_APP_KEY_PREFIX}${applicationKey}`]: true,\n };\n }\n\n // Exact `code_file` miss: `match[1]` is `wasm://wasm/…`, not the registered http URL.\n if (index < 0 && workerImageIndex < 0) {\n const unique = uniqueImageForSyntheticFilename(match[1], getImages(), WINDOW._sentryWasmImages || []);\n if (unique) {\n frame.filename = unique.codeFile;\n if (unique.worker) {\n workerImageIndex = unique.index;\n } else {\n index = unique.index;\n }\n }\n }\n\n if (index >= 0) {\n frame.addr_mode = `rel:${existingImagesOffset + index}`;\n hasAtLeastOneWasmFrameWithImage = true;\n } else if (workerImageIndex >= 0) {\n const mainThreadImagesCount = getImages().length;\n frame.addr_mode = `rel:${existingImagesOffset + mainThreadImagesCount + workerImageIndex}`;\n hasAtLeastOneWasmFrameWithImage = true;\n }\n }\n });\n\n return hasAtLeastOneWasmFrameWithImage;\n}\n\n/**\n * Looks up an image by URL in worker images.\n */\nfunction getWorkerImage(url: string): number {\n const workerImages = WINDOW._sentryWasmImages || [];\n return workerImages.findIndex(image => {\n return image.type === 'wasm' && image.code_file === url;\n });\n}\n\n/**\n * Use this function to register WASM support in a web worker.\n *\n * This function will:\n * - Patch WebAssembly.instantiateStreaming and WebAssembly.compileStreaming in the worker\n * - Forward WASM debug images to the parent thread for symbolication\n *\n * @param options {RegisterWebWorkerWasmOptions} Options:\n * - `self`: The worker's global scope (self).\n */\nexport function registerWebWorkerWasm({ self }: RegisterWebWorkerWasmOptions): void {\n patchWebAssembly((module, url) => {\n const image = registerModule(module, url);\n\n if (image) {\n self.postMessage({\n _sentryMessage: true,\n _sentryWasmImages: [image],\n });\n }\n });\n}\n"],"names":[],"mappings":"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"}
@@ -0,0 +1,32 @@
1
+ function syntheticModuleName(filename) {
2
+ const body = filename.match(/^wasm:\/\/wasm\/(.+)$/i)?.[1];
3
+ if (!body) {
4
+ return void 0;
5
+ }
6
+ const name = body.replace(/-[0-9a-fA-F]{6,16}$/, "");
7
+ return name === body ? void 0 : name;
8
+ }
9
+ function uniqueImageForSyntheticFilename(filename, pageImages, workerImages) {
10
+ const name = syntheticModuleName(filename);
11
+ if (!name) {
12
+ return void 0;
13
+ }
14
+ const hits = [];
15
+ const consider = (images, worker) => {
16
+ images.forEach((image, index) => {
17
+ if (image.moduleName === name) {
18
+ hits.push({ index, worker, codeFile: image.code_file, debugId: image.debug_id });
19
+ }
20
+ });
21
+ };
22
+ consider(pageImages, false);
23
+ consider(workerImages, true);
24
+ const hit = hits[0];
25
+ if (!hit || hits.some((other) => other.debugId !== hit.debugId)) {
26
+ return void 0;
27
+ }
28
+ return { index: hit.index, worker: hit.worker, codeFile: hit.codeFile };
29
+ }
30
+
31
+ export { syntheticModuleName, uniqueImageForSyntheticFilename };
32
+ //# sourceMappingURL=matchSyntheticWasmFilename.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"matchSyntheticWasmFilename.js","sources":["../../../src/matchSyntheticWasmFilename.ts"],"sourcesContent":["import type { RegisteredWasmImage } from './registry';\n\n/**\n * Maps Chrome `wasm://wasm/<name>-<hash>` frames to a registered `code_file`.\n *\n * V8 builds the label from the wasm `name` section, which is parsed into\n * `moduleName` at registration. A hit is accepted only when every image with\n * that name shares one `debug_id`, since a page and a worker can register the\n * same binary under different URLs.\n *\n * Hash-only `wasm://wasm/<hash>` labels carry no name and are not mapped\n * (see #23781).\n */\n\nexport type SyntheticWasmImageHit = {\n index: number;\n worker: boolean;\n codeFile: string;\n};\n\n/**\n * Module name from Chrome's label: `wasm://wasm/demo.wasm-000197f6` → `demo.wasm`.\n *\n * V8 appends the `-<hash>` suffix only after a module name. A label without\n * it is hash-only and yields `undefined`, so a hex-looking module name such\n * as `ed25519` is still returned.\n */\nexport function syntheticModuleName(filename: string): string | undefined {\n const body = filename.match(/^wasm:\\/\\/wasm\\/(.+)$/i)?.[1];\n if (!body) {\n return undefined;\n }\n const name = body.replace(/-[0-9a-fA-F]{6,16}$/, '');\n return name === body ? undefined : name;\n}\n\nexport function uniqueImageForSyntheticFilename(\n filename: string,\n pageImages: ReadonlyArray<RegisteredWasmImage>,\n workerImages: ReadonlyArray<RegisteredWasmImage>,\n): SyntheticWasmImageHit | undefined {\n const name = syntheticModuleName(filename);\n if (!name) {\n return undefined;\n }\n\n const hits: Array<SyntheticWasmImageHit & { debugId: string }> = [];\n const consider = (images: ReadonlyArray<RegisteredWasmImage>, worker: boolean): void => {\n images.forEach((image, index) => {\n if (image.moduleName === name) {\n hits.push({ index, worker, codeFile: image.code_file, debugId: image.debug_id });\n }\n });\n };\n consider(pageImages, false);\n consider(workerImages, true);\n\n const hit = hits[0];\n if (!hit || hits.some(other => other.debugId !== hit.debugId)) {\n return undefined;\n }\n return { index: hit.index, worker: hit.worker, codeFile: hit.codeFile };\n}\n"],"names":[],"mappings":"AA2BO,SAAS,oBAAoB,QAAA,EAAsC;AACxE,EAAA,MAAM,IAAA,GAAO,QAAA,CAAS,KAAA,CAAM,wBAAwB,IAAI,CAAC,CAAA;AACzD,EAAA,IAAI,CAAC,IAAA,EAAM;AACT,IAAA,OAAO,MAAA;AAAA,EACT;AACA,EAAA,MAAM,IAAA,GAAO,IAAA,CAAK,OAAA,CAAQ,qBAAA,EAAuB,EAAE,CAAA;AACnD,EAAA,OAAO,IAAA,KAAS,OAAO,MAAA,GAAY,IAAA;AACrC;AAEO,SAAS,+BAAA,CACd,QAAA,EACA,UAAA,EACA,YAAA,EACmC;AACnC,EAAA,MAAM,IAAA,GAAO,oBAAoB,QAAQ,CAAA;AACzC,EAAA,IAAI,CAAC,IAAA,EAAM;AACT,IAAA,OAAO,MAAA;AAAA,EACT;AAEA,EAAA,MAAM,OAA2D,EAAC;AAClE,EAAA,MAAM,QAAA,GAAW,CAAC,MAAA,EAA4C,MAAA,KAA0B;AACtF,IAAA,MAAA,CAAO,OAAA,CAAQ,CAAC,KAAA,EAAO,KAAA,KAAU;AAC/B,MAAA,IAAI,KAAA,CAAM,eAAe,IAAA,EAAM;AAC7B,QAAA,IAAA,CAAK,IAAA,CAAK,EAAE,KAAA,EAAO,MAAA,EAAQ,QAAA,EAAU,MAAM,SAAA,EAAW,OAAA,EAAS,KAAA,CAAM,QAAA,EAAU,CAAA;AAAA,MACjF;AAAA,IACF,CAAC,CAAA;AAAA,EACH,CAAA;AACA,EAAA,QAAA,CAAS,YAAY,KAAK,CAAA;AAC1B,EAAA,QAAA,CAAS,cAAc,IAAI,CAAA;AAE3B,EAAA,MAAM,GAAA,GAAM,KAAK,CAAC,CAAA;AAClB,EAAA,IAAI,CAAC,OAAO,IAAA,CAAK,IAAA,CAAK,WAAS,KAAA,CAAM,OAAA,KAAY,GAAA,CAAI,OAAO,CAAA,EAAG;AAC7D,IAAA,OAAO,MAAA;AAAA,EACT;AACA,EAAA,OAAO,EAAE,OAAO,GAAA,CAAI,KAAA,EAAO,QAAQ,GAAA,CAAI,MAAA,EAAQ,QAAA,EAAU,GAAA,CAAI,QAAA,EAAS;AACxE;;;;"}
@@ -1 +1 @@
1
- {"type":"module","version":"11.0.0-rc.1","sideEffects":false}
1
+ {"type":"module","version":"11.1.0","sideEffects":false}
@@ -1,3 +1,5 @@
1
+ import { parseNameSectionModuleName } from './wasmNameSection.js';
2
+
1
3
  const IMAGES = [];
2
4
  function getModuleInfo(module) {
3
5
  const buildIds = WebAssembly.Module.customSections(module, "build_id");
@@ -17,10 +19,20 @@ function getModuleInfo(module) {
17
19
  const decoder = new TextDecoder("utf-8");
18
20
  debugFile = decoder.decode(firstExternalDebugInfo);
19
21
  }
20
- return { buildId, debugFile };
22
+ let moduleName = null;
23
+ try {
24
+ const nameSections = WebAssembly.Module.customSections(module, "name");
25
+ const nameSection0 = nameSections[0];
26
+ if (nameSection0) {
27
+ moduleName = parseNameSectionModuleName(nameSection0);
28
+ }
29
+ } catch {
30
+ moduleName = null;
31
+ }
32
+ return { buildId, debugFile, moduleName };
21
33
  }
22
34
  function registerModule(module, url) {
23
- const { buildId, debugFile } = getModuleInfo(module);
35
+ const { buildId, debugFile, moduleName } = getModuleInfo(module);
24
36
  if (!buildId) {
25
37
  return null;
26
38
  }
@@ -42,17 +54,24 @@ function registerModule(module, url) {
42
54
  debug_file: debugFileUrl,
43
55
  debug_id: `${buildId.padEnd(32, "0").slice(0, 32)}0`
44
56
  };
57
+ if (moduleName) {
58
+ image.moduleName = moduleName;
59
+ }
45
60
  IMAGES.push(image);
46
61
  return image;
47
62
  }
48
63
  function getImages() {
49
64
  return IMAGES;
50
65
  }
66
+ function toProtocolDebugImage(image) {
67
+ const { moduleName: _moduleName, ...protocol } = image;
68
+ return protocol;
69
+ }
51
70
  function getImage(url) {
52
71
  return IMAGES.findIndex((image) => {
53
72
  return image.type === "wasm" && image.code_file === url;
54
73
  });
55
74
  }
56
75
 
57
- export { IMAGES, getImage, getImages, getModuleInfo, registerModule };
76
+ export { IMAGES, getImage, getImages, getModuleInfo, registerModule, toProtocolDebugImage };
58
77
  //# sourceMappingURL=registry.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"registry.js","sources":["../../../src/registry.ts"],"sourcesContent":["import type { DebugImage } from '@sentry/core';\n\nexport const IMAGES: Array<DebugImage> = [];\n\nexport interface ModuleInfo {\n buildId: string | null;\n debugFile: string | null;\n}\n\n/**\n * Returns the extracted meta information from a web assembly module that\n * Sentry uses to identify debug images.\n *\n * @param module\n */\nexport function getModuleInfo(module: WebAssembly.Module): ModuleInfo {\n const buildIds = WebAssembly.Module.customSections(module, 'build_id');\n let buildId = null;\n let debugFile = null;\n\n const buildId0 = buildIds[0];\n if (buildId0) {\n const firstBuildId = new Uint8Array(buildId0);\n buildId = Array.from(firstBuildId).reduce((acc, x) => {\n return acc + x.toString(16).padStart(2, '0');\n }, '');\n }\n\n const externalDebugInfo = WebAssembly.Module.customSections(module, 'external_debug_info');\n const externalDebugInfo0 = externalDebugInfo[0];\n if (externalDebugInfo0) {\n const firstExternalDebugInfo = new Uint8Array(externalDebugInfo0);\n const decoder = new TextDecoder('utf-8');\n debugFile = decoder.decode(firstExternalDebugInfo);\n }\n\n return { buildId, debugFile };\n}\n\n/**\n * Records a module and returns the created debug image.\n */\nexport function registerModule(module: WebAssembly.Module, url: string): DebugImage | null {\n const { buildId, debugFile } = getModuleInfo(module);\n if (!buildId) {\n return null;\n }\n\n const oldIdx = getImage(url);\n if (oldIdx >= 0) {\n IMAGES.splice(oldIdx, 1);\n }\n\n let debugFileUrl = null;\n if (debugFile) {\n try {\n debugFileUrl = new URL(debugFile, url).href;\n } catch {\n // debugFile could be a blob URL which causes the URL constructor to throw\n // for now we just ignore this case\n }\n }\n\n const image: DebugImage = {\n type: 'wasm',\n code_id: buildId,\n code_file: url,\n debug_file: debugFileUrl,\n debug_id: `${buildId.padEnd(32, '0').slice(0, 32)}0`,\n };\n\n IMAGES.push(image);\n return image;\n}\n\n/**\n * Returns all known images.\n */\nexport function getImages(): Array<DebugImage> {\n return IMAGES;\n}\n\n/**\n * Looks up an image by URL.\n *\n * @param url the URL of the WebAssembly module.\n */\nexport function getImage(url: string): number {\n return IMAGES.findIndex(image => {\n return image.type === 'wasm' && image.code_file === url;\n });\n}\n"],"names":[],"mappings":"AAEO,MAAM,SAA4B;AAalC,SAAS,cAAc,MAAA,EAAwC;AACpE,EAAA,MAAM,QAAA,GAAW,WAAA,CAAY,MAAA,CAAO,cAAA,CAAe,QAAQ,UAAU,CAAA;AACrE,EAAA,IAAI,OAAA,GAAU,IAAA;AACd,EAAA,IAAI,SAAA,GAAY,IAAA;AAEhB,EAAA,MAAM,QAAA,GAAW,SAAS,CAAC,CAAA;AAC3B,EAAA,IAAI,QAAA,EAAU;AACZ,IAAA,MAAM,YAAA,GAAe,IAAI,UAAA,CAAW,QAAQ,CAAA;AAC5C,IAAA,OAAA,GAAU,MAAM,IAAA,CAAK,YAAY,EAAE,MAAA,CAAO,CAAC,KAAK,CAAA,KAAM;AACpD,MAAA,OAAO,MAAM,CAAA,CAAE,QAAA,CAAS,EAAE,CAAA,CAAE,QAAA,CAAS,GAAG,GAAG,CAAA;AAAA,IAC7C,GAAG,EAAE,CAAA;AAAA,EACP;AAEA,EAAA,MAAM,iBAAA,GAAoB,WAAA,CAAY,MAAA,CAAO,cAAA,CAAe,QAAQ,qBAAqB,CAAA;AACzF,EAAA,MAAM,kBAAA,GAAqB,kBAAkB,CAAC,CAAA;AAC9C,EAAA,IAAI,kBAAA,EAAoB;AACtB,IAAA,MAAM,sBAAA,GAAyB,IAAI,UAAA,CAAW,kBAAkB,CAAA;AAChE,IAAA,MAAM,OAAA,GAAU,IAAI,WAAA,CAAY,OAAO,CAAA;AACvC,IAAA,SAAA,GAAY,OAAA,CAAQ,OAAO,sBAAsB,CAAA;AAAA,EACnD;AAEA,EAAA,OAAO,EAAE,SAAS,SAAA,EAAU;AAC9B;AAKO,SAAS,cAAA,CAAe,QAA4B,GAAA,EAAgC;AACzF,EAAA,MAAM,EAAE,OAAA,EAAS,SAAA,EAAU,GAAI,cAAc,MAAM,CAAA;AACnD,EAAA,IAAI,CAAC,OAAA,EAAS;AACZ,IAAA,OAAO,IAAA;AAAA,EACT;AAEA,EAAA,MAAM,MAAA,GAAS,SAAS,GAAG,CAAA;AAC3B,EAAA,IAAI,UAAU,CAAA,EAAG;AACf,IAAA,MAAA,CAAO,MAAA,CAAO,QAAQ,CAAC,CAAA;AAAA,EACzB;AAEA,EAAA,IAAI,YAAA,GAAe,IAAA;AACnB,EAAA,IAAI,SAAA,EAAW;AACb,IAAA,IAAI;AACF,MAAA,YAAA,GAAe,IAAI,GAAA,CAAI,SAAA,EAAW,GAAG,CAAA,CAAE,IAAA;AAAA,IACzC,CAAA,CAAA,MAAQ;AAAA,IAGR;AAAA,EACF;AAEA,EAAA,MAAM,KAAA,GAAoB;AAAA,IACxB,IAAA,EAAM,MAAA;AAAA,IACN,OAAA,EAAS,OAAA;AAAA,IACT,SAAA,EAAW,GAAA;AAAA,IACX,UAAA,EAAY,YAAA;AAAA,IACZ,QAAA,EAAU,CAAA,EAAG,OAAA,CAAQ,MAAA,CAAO,EAAA,EAAI,GAAG,CAAA,CAAE,KAAA,CAAM,CAAA,EAAG,EAAE,CAAC,CAAA,CAAA;AAAA,GACnD;AAEA,EAAA,MAAA,CAAO,KAAK,KAAK,CAAA;AACjB,EAAA,OAAO,KAAA;AACT;AAKO,SAAS,SAAA,GAA+B;AAC7C,EAAA,OAAO,MAAA;AACT;AAOO,SAAS,SAAS,GAAA,EAAqB;AAC5C,EAAA,OAAO,MAAA,CAAO,UAAU,CAAA,KAAA,KAAS;AAC/B,IAAA,OAAO,KAAA,CAAM,IAAA,KAAS,MAAA,IAAU,KAAA,CAAM,SAAA,KAAc,GAAA;AAAA,EACtD,CAAC,CAAA;AACH;;;;"}
1
+ {"version":3,"file":"registry.js","sources":["../../../src/registry.ts"],"sourcesContent":["import type { DebugImage } from '@sentry/core';\nimport { parseNameSectionModuleName } from './wasmNameSection';\n\nexport type RegisteredWasmImage = Extract<DebugImage, { type: 'wasm' }> & {\n /** Internal: wasm `name` section. Used to link `wasm://` stacks; stripped before the event is sent. */\n moduleName?: string;\n};\n\nexport const IMAGES: Array<RegisteredWasmImage> = [];\n\nexport interface ModuleInfo {\n buildId: string | null;\n debugFile: string | null;\n moduleName: string | null;\n}\n\n/**\n * Returns the extracted meta information from a web assembly module that\n * Sentry uses to identify debug images.\n *\n * @param module\n */\nexport function getModuleInfo(module: WebAssembly.Module): ModuleInfo {\n const buildIds = WebAssembly.Module.customSections(module, 'build_id');\n let buildId = null;\n let debugFile = null;\n\n const buildId0 = buildIds[0];\n if (buildId0) {\n const firstBuildId = new Uint8Array(buildId0);\n buildId = Array.from(firstBuildId).reduce((acc, x) => {\n return acc + x.toString(16).padStart(2, '0');\n }, '');\n }\n\n const externalDebugInfo = WebAssembly.Module.customSections(module, 'external_debug_info');\n const externalDebugInfo0 = externalDebugInfo[0];\n if (externalDebugInfo0) {\n const firstExternalDebugInfo = new Uint8Array(externalDebugInfo0);\n const decoder = new TextDecoder('utf-8');\n debugFile = decoder.decode(firstExternalDebugInfo);\n }\n\n let moduleName = null;\n try {\n const nameSections = WebAssembly.Module.customSections(module, 'name');\n const nameSection0 = nameSections[0];\n if (nameSection0) {\n moduleName = parseNameSectionModuleName(nameSection0);\n }\n } catch {\n moduleName = null;\n }\n\n return { buildId, debugFile, moduleName };\n}\n\n/**\n * Records a module and returns the created debug image.\n */\nexport function registerModule(module: WebAssembly.Module, url: string): RegisteredWasmImage | null {\n const { buildId, debugFile, moduleName } = getModuleInfo(module);\n if (!buildId) {\n return null;\n }\n\n const oldIdx = getImage(url);\n if (oldIdx >= 0) {\n IMAGES.splice(oldIdx, 1);\n }\n\n let debugFileUrl = null;\n if (debugFile) {\n try {\n debugFileUrl = new URL(debugFile, url).href;\n } catch {\n // debugFile could be a blob URL which causes the URL constructor to throw\n // for now we just ignore this case\n }\n }\n\n const image: RegisteredWasmImage = {\n type: 'wasm',\n code_id: buildId,\n code_file: url,\n debug_file: debugFileUrl,\n debug_id: `${buildId.padEnd(32, '0').slice(0, 32)}0`,\n };\n if (moduleName) {\n image.moduleName = moduleName;\n }\n\n IMAGES.push(image);\n return image;\n}\n\n/**\n * Returns all known images.\n */\nexport function getImages(): Array<RegisteredWasmImage> {\n return IMAGES;\n}\n\n/**\n * Debug image payload for Sentry: protocol fields only (no internal `moduleName`).\n */\nexport function toProtocolDebugImage(image: RegisteredWasmImage): DebugImage {\n const { moduleName: _moduleName, ...protocol } = image;\n return protocol;\n}\n\n/**\n * Looks up an image by URL.\n *\n * @param url the URL of the WebAssembly module.\n */\nexport function getImage(url: string): number {\n return IMAGES.findIndex(image => {\n return image.type === 'wasm' && image.code_file === url;\n });\n}\n"],"names":[],"mappings":"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"}
@@ -0,0 +1,65 @@
1
+ function parseNameSectionModuleName(source) {
2
+ try {
3
+ return readModuleName(toBytes(source));
4
+ } catch {
5
+ return null;
6
+ }
7
+ }
8
+ function toBytes(source) {
9
+ if (source instanceof ArrayBuffer) {
10
+ return new Uint8Array(source);
11
+ }
12
+ return new Uint8Array(source.buffer, source.byteOffset, source.byteLength);
13
+ }
14
+ function readModuleName(bytes) {
15
+ const cursor = { offset: 0 };
16
+ while (cursor.offset < bytes.length) {
17
+ const id = bytes[cursor.offset];
18
+ cursor.offset += 1;
19
+ if (id === void 0) {
20
+ return null;
21
+ }
22
+ const size = readU32Leb(bytes, cursor);
23
+ if (size === void 0 || cursor.offset + size > bytes.length) {
24
+ return null;
25
+ }
26
+ const start = cursor.offset;
27
+ cursor.offset += size;
28
+ if (id === 0) {
29
+ return readName(bytes.subarray(start, start + size));
30
+ }
31
+ }
32
+ return null;
33
+ }
34
+ function readName(bytes) {
35
+ const cursor = { offset: 0 };
36
+ const length = readU32Leb(bytes, cursor);
37
+ if (length === void 0 || length === 0 || cursor.offset + length !== bytes.length) {
38
+ return null;
39
+ }
40
+ try {
41
+ return new TextDecoder("utf-8", { fatal: true }).decode(bytes.subarray(cursor.offset));
42
+ } catch {
43
+ return null;
44
+ }
45
+ }
46
+ function readU32Leb(bytes, cursor) {
47
+ let result = 0;
48
+ let shift = 0;
49
+ while (cursor.offset < bytes.length) {
50
+ const byte = bytes[cursor.offset];
51
+ cursor.offset += 1;
52
+ if (byte === void 0 || shift >= 35) {
53
+ return void 0;
54
+ }
55
+ result |= (byte & 127) << shift;
56
+ if ((byte & 128) === 0) {
57
+ return result >>> 0;
58
+ }
59
+ shift += 7;
60
+ }
61
+ return void 0;
62
+ }
63
+
64
+ export { parseNameSectionModuleName };
65
+ //# sourceMappingURL=wasmNameSection.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"wasmNameSection.js","sources":["../../../src/wasmNameSection.ts"],"sourcesContent":["/* eslint-disable no-bitwise -- LEB128 is a bitwise encoding */\n\n/**\n * Parses the wasm `name` custom section payload — the bytes\n * `WebAssembly.Module.customSections(module, 'name')` returns.\n *\n * Chrome's buffer-compiled stacks use this module name as `wasm://wasm/<name>-<hash>`,\n * which often differs from the fetch URL (wasm-bindgen `demo.wasm` vs `demo_bg.wasm`).\n * Missing, stripped, or malformed sections return null. Callers then guess from\n * the fetch URL basename if the name is missing or does not match the stack.\n *\n * @see https://webassembly.github.io/spec/core/appendix/custom.html#name-section\n */\nexport function parseNameSectionModuleName(source: ArrayBuffer | Uint8Array): string | null {\n try {\n return readModuleName(toBytes(source));\n } catch {\n return null;\n }\n}\n\nfunction toBytes(source: ArrayBuffer | Uint8Array): Uint8Array {\n if (source instanceof ArrayBuffer) {\n return new Uint8Array(source);\n }\n return new Uint8Array(source.buffer, source.byteOffset, source.byteLength);\n}\n\nfunction readModuleName(bytes: Uint8Array): string | null {\n const cursor = { offset: 0 };\n while (cursor.offset < bytes.length) {\n const id = bytes[cursor.offset];\n cursor.offset += 1;\n if (id === undefined) {\n return null;\n }\n\n const size = readU32Leb(bytes, cursor);\n if (size === undefined || cursor.offset + size > bytes.length) {\n return null;\n }\n\n const start = cursor.offset;\n cursor.offset += size;\n if (id === 0) {\n return readName(bytes.subarray(start, start + size));\n }\n }\n\n return null;\n}\n\nfunction readName(bytes: Uint8Array): string | null {\n const cursor = { offset: 0 };\n const length = readU32Leb(bytes, cursor);\n if (length === undefined || length === 0 || cursor.offset + length !== bytes.length) {\n return null;\n }\n\n try {\n return new TextDecoder('utf-8', { fatal: true }).decode(bytes.subarray(cursor.offset));\n } catch {\n return null;\n }\n}\n\nfunction readU32Leb(bytes: Uint8Array, cursor: { offset: number }): number | undefined {\n let result = 0;\n let shift = 0;\n\n while (cursor.offset < bytes.length) {\n const byte = bytes[cursor.offset];\n cursor.offset += 1;\n if (byte === undefined || shift >= 35) {\n return undefined;\n }\n\n result |= (byte & 0x7f) << shift;\n if ((byte & 0x80) === 0) {\n return result >>> 0;\n }\n shift += 7;\n }\n\n return undefined;\n}\n"],"names":[],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAoC,UAAU,EAAE,MAAM,cAAc,CAAC;AAWjF;;;GAGG;AACH,UAAU,iCAAiC;IACzC,WAAW,EAAE,CAAC,OAAO,EAAE,OAAO,KAAK,IAAI,CAAC;CACzC;AAED,UAAU,4BAA4B;IACpC,IAAI,EAAE,iCAAiC,CAAC;CACzC;AAED,UAAU,sBAAsB;IAC9B;;;;OAIG;IACH,cAAc,CAAC,EAAE,MAAM,CAAC;CACzB;AAwCD,eAAO,MAAM,eAAe;;CAAsC,CAAC;AAInE;;;;;;;;;GASG;AAEH,wBAAgB,WAAW,CACzB,MAAM,EAAE,KAAK,CAAC,UAAU,CAAC,EACzB,cAAc,CAAC,EAAE,MAAM,EACvB,oBAAoB,GAAE,MAAU,GAC/B,OAAO,CAgDT;AAYD;;;;;;;;;GASG;AACH,wBAAgB,qBAAqB,CAAC,EAAE,IAAI,EAAE,EAAE,4BAA4B,GAAG,IAAI,CAWlF"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAwB,UAAU,EAAE,MAAM,cAAc,CAAC;AAYrE;;;GAGG;AACH,UAAU,iCAAiC;IACzC,WAAW,EAAE,CAAC,OAAO,EAAE,OAAO,KAAK,IAAI,CAAC;CACzC;AAED,UAAU,4BAA4B;IACpC,IAAI,EAAE,iCAAiC,CAAC;CACzC;AAED,UAAU,sBAAsB;IAC9B;;;;OAIG;IACH,cAAc,CAAC,EAAE,MAAM,CAAC;CACzB;AAwCD,eAAO,MAAM,eAAe;;CAAsC,CAAC;AAInE;;;;;;;;;GASG;AAEH,wBAAgB,WAAW,CACzB,MAAM,EAAE,KAAK,CAAC,UAAU,CAAC,EACzB,cAAc,CAAC,EAAE,MAAM,EACvB,oBAAoB,GAAE,MAAU,GAC/B,OAAO,CA+DT;AAYD;;;;;;;;;GASG;AACH,wBAAgB,qBAAqB,CAAC,EAAE,IAAI,EAAE,EAAE,4BAA4B,GAAG,IAAI,CAWlF"}
@@ -0,0 +1,27 @@
1
+ import type { RegisteredWasmImage } from './registry';
2
+ /**
3
+ * Maps Chrome `wasm://wasm/<name>-<hash>` frames to a registered `code_file`.
4
+ *
5
+ * V8 builds the label from the wasm `name` section, which is parsed into
6
+ * `moduleName` at registration. A hit is accepted only when every image with
7
+ * that name shares one `debug_id`, since a page and a worker can register the
8
+ * same binary under different URLs.
9
+ *
10
+ * Hash-only `wasm://wasm/<hash>` labels carry no name and are not mapped
11
+ * (see #23781).
12
+ */
13
+ export type SyntheticWasmImageHit = {
14
+ index: number;
15
+ worker: boolean;
16
+ codeFile: string;
17
+ };
18
+ /**
19
+ * Module name from Chrome's label: `wasm://wasm/demo.wasm-000197f6` → `demo.wasm`.
20
+ *
21
+ * V8 appends the `-<hash>` suffix only after a module name. A label without
22
+ * it is hash-only and yields `undefined`, so a hex-looking module name such
23
+ * as `ed25519` is still returned.
24
+ */
25
+ export declare function syntheticModuleName(filename: string): string | undefined;
26
+ export declare function uniqueImageForSyntheticFilename(filename: string, pageImages: ReadonlyArray<RegisteredWasmImage>, workerImages: ReadonlyArray<RegisteredWasmImage>): SyntheticWasmImageHit | undefined;
27
+ //# sourceMappingURL=matchSyntheticWasmFilename.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"matchSyntheticWasmFilename.d.ts","sourceRoot":"","sources":["../../../src/matchSyntheticWasmFilename.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,YAAY,CAAC;AAEtD;;;;;;;;;;GAUG;AAEH,MAAM,MAAM,qBAAqB,GAAG;IAClC,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,OAAO,CAAC;IAChB,QAAQ,EAAE,MAAM,CAAC;CAClB,CAAC;AAEF;;;;;;GAMG;AACH,wBAAgB,mBAAmB,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAOxE;AAED,wBAAgB,+BAA+B,CAC7C,QAAQ,EAAE,MAAM,EAChB,UAAU,EAAE,aAAa,CAAC,mBAAmB,CAAC,EAC9C,YAAY,EAAE,aAAa,CAAC,mBAAmB,CAAC,GAC/C,qBAAqB,GAAG,SAAS,CAsBnC"}
@@ -1,8 +1,15 @@
1
1
  import type { DebugImage } from '@sentry/core';
2
- export declare const IMAGES: Array<DebugImage>;
2
+ export type RegisteredWasmImage = Extract<DebugImage, {
3
+ type: 'wasm';
4
+ }> & {
5
+ /** Internal: wasm `name` section. Used to link `wasm://` stacks; stripped before the event is sent. */
6
+ moduleName?: string;
7
+ };
8
+ export declare const IMAGES: Array<RegisteredWasmImage>;
3
9
  export interface ModuleInfo {
4
10
  buildId: string | null;
5
11
  debugFile: string | null;
12
+ moduleName: string | null;
6
13
  }
7
14
  /**
8
15
  * Returns the extracted meta information from a web assembly module that
@@ -14,11 +21,15 @@ export declare function getModuleInfo(module: WebAssembly.Module): ModuleInfo;
14
21
  /**
15
22
  * Records a module and returns the created debug image.
16
23
  */
17
- export declare function registerModule(module: WebAssembly.Module, url: string): DebugImage | null;
24
+ export declare function registerModule(module: WebAssembly.Module, url: string): RegisteredWasmImage | null;
18
25
  /**
19
26
  * Returns all known images.
20
27
  */
21
- export declare function getImages(): Array<DebugImage>;
28
+ export declare function getImages(): Array<RegisteredWasmImage>;
29
+ /**
30
+ * Debug image payload for Sentry: protocol fields only (no internal `moduleName`).
31
+ */
32
+ export declare function toProtocolDebugImage(image: RegisteredWasmImage): DebugImage;
22
33
  /**
23
34
  * Looks up an image by URL.
24
35
  *
@@ -1 +1 @@
1
- {"version":3,"file":"registry.d.ts","sourceRoot":"","sources":["../../../src/registry.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,cAAc,CAAC;AAE/C,eAAO,MAAM,MAAM,EAAE,KAAK,CAAC,UAAU,CAAM,CAAC;AAE5C,MAAM,WAAW,UAAU;IACzB,OAAO,EAAE,MAAM,GAAG,IAAI,CAAC;IACvB,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;CAC1B;AAED;;;;;GAKG;AACH,wBAAgB,aAAa,CAAC,MAAM,EAAE,WAAW,CAAC,MAAM,GAAG,UAAU,CAsBpE;AAED;;GAEG;AACH,wBAAgB,cAAc,CAAC,MAAM,EAAE,WAAW,CAAC,MAAM,EAAE,GAAG,EAAE,MAAM,GAAG,UAAU,GAAG,IAAI,CA+BzF;AAED;;GAEG;AACH,wBAAgB,SAAS,IAAI,KAAK,CAAC,UAAU,CAAC,CAE7C;AAED;;;;GAIG;AACH,wBAAgB,QAAQ,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAI5C"}
1
+ {"version":3,"file":"registry.d.ts","sourceRoot":"","sources":["../../../src/registry.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,cAAc,CAAC;AAG/C,MAAM,MAAM,mBAAmB,GAAG,OAAO,CAAC,UAAU,EAAE;IAAE,IAAI,EAAE,MAAM,CAAA;CAAE,CAAC,GAAG;IACxE,uGAAuG;IACvG,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB,CAAC;AAEF,eAAO,MAAM,MAAM,EAAE,KAAK,CAAC,mBAAmB,CAAM,CAAC;AAErD,MAAM,WAAW,UAAU;IACzB,OAAO,EAAE,MAAM,GAAG,IAAI,CAAC;IACvB,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;IACzB,UAAU,EAAE,MAAM,GAAG,IAAI,CAAC;CAC3B;AAED;;;;;GAKG;AACH,wBAAgB,aAAa,CAAC,MAAM,EAAE,WAAW,CAAC,MAAM,GAAG,UAAU,CAiCpE;AAED;;GAEG;AACH,wBAAgB,cAAc,CAAC,MAAM,EAAE,WAAW,CAAC,MAAM,EAAE,GAAG,EAAE,MAAM,GAAG,mBAAmB,GAAG,IAAI,CAkClG;AAED;;GAEG;AACH,wBAAgB,SAAS,IAAI,KAAK,CAAC,mBAAmB,CAAC,CAEtD;AAED;;GAEG;AACH,wBAAgB,oBAAoB,CAAC,KAAK,EAAE,mBAAmB,GAAG,UAAU,CAG3E;AAED;;;;GAIG;AACH,wBAAgB,QAAQ,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAI5C"}
@@ -0,0 +1,13 @@
1
+ /**
2
+ * Parses the wasm `name` custom section payload — the bytes
3
+ * `WebAssembly.Module.customSections(module, 'name')` returns.
4
+ *
5
+ * Chrome's buffer-compiled stacks use this module name as `wasm://wasm/<name>-<hash>`,
6
+ * which often differs from the fetch URL (wasm-bindgen `demo.wasm` vs `demo_bg.wasm`).
7
+ * Missing, stripped, or malformed sections return null. Callers then guess from
8
+ * the fetch URL basename if the name is missing or does not match the stack.
9
+ *
10
+ * @see https://webassembly.github.io/spec/core/appendix/custom.html#name-section
11
+ */
12
+ export declare function parseNameSectionModuleName(source: ArrayBuffer | Uint8Array): string | null;
13
+ //# sourceMappingURL=wasmNameSection.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"wasmNameSection.d.ts","sourceRoot":"","sources":["../../../src/wasmNameSection.ts"],"names":[],"mappings":"AAEA;;;;;;;;;;GAUG;AACH,wBAAgB,0BAA0B,CAAC,MAAM,EAAE,WAAW,GAAG,UAAU,GAAG,MAAM,GAAG,IAAI,CAM1F"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sentry/wasm",
3
- "version": "11.0.0-rc.1",
3
+ "version": "11.1.0",
4
4
  "description": "Support for WASM.",
5
5
  "repository": "git://github.com/getsentry/sentry-javascript.git",
6
6
  "homepage": "https://github.com/getsentry/sentry-javascript/tree/master/packages/wasm",
@@ -32,8 +32,8 @@
32
32
  "access": "public"
33
33
  },
34
34
  "dependencies": {
35
- "@sentry/browser": "11.0.0-rc.1",
36
- "@sentry/core": "11.0.0-rc.1"
35
+ "@sentry/browser": "11.1.0",
36
+ "@sentry/core": "11.1.0"
37
37
  },
38
38
  "scripts": {
39
39
  "build": "run-p build:transpile build:bundle build:types",