Skip to content

Commit 65aefd3

Browse files
committed
Simplify by sending raw module data from worker to main-thread
1 parent 862a096 commit 65aefd3

File tree

5 files changed

+30
-142
lines changed

5 files changed

+30
-142
lines changed

packages/browser/src/integrations/webWorker.ts

Lines changed: 10 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,8 @@
11
import type { Integration, IntegrationFn } from '@sentry/core';
2-
import {
3-
captureEvent,
4-
debug,
5-
defineIntegration,
6-
getClient,
7-
getFilenameToMetadataMap,
8-
isPlainObject,
9-
isPrimitive,
10-
mergeMetadataMap,
11-
} from '@sentry/core';
2+
import { captureEvent, debug, defineIntegration, getClient, isPlainObject, isPrimitive } from '@sentry/core';
123
import { DEBUG_BUILD } from '../debug-build';
134
import { eventFromUnknownInput } from '../eventbuilder';
145
import { WINDOW } from '../helpers';
15-
import { defaultStackParser } from '../stack-parsers';
166
import { _eventFromRejectionWithPrimitive, _getUnhandledRejectionError } from './globalhandlers';
177

188
export const INTEGRATION_NAME = 'WebWorker';
@@ -136,8 +126,12 @@ function listenForSentryMessages(worker: Worker): void {
136126
// Handle module metadata
137127
if (event.data._sentryModuleMetadata) {
138128
DEBUG_BUILD && debug.log('Sentry module metadata web worker message received', event.data);
139-
// Merge worker metadata into the main thread's metadata cache
140-
mergeMetadataMap(event.data._sentryModuleMetadata);
129+
// Merge worker's raw metadata into the global object
130+
// It will be parsed lazily when needed by getMetadataForUrl
131+
WINDOW._sentryModuleMetadata = {
132+
...WINDOW._sentryModuleMetadata,
133+
...event.data._sentryModuleMetadata,
134+
};
141135
}
142136

143137
// Handle unhandled rejections forwarded from worker
@@ -237,13 +231,12 @@ interface RegisterWebWorkerOptions {
237231
* - `self`: The worker instance you're calling this function from (self).
238232
*/
239233
export function registerWebWorker({ self }: RegisterWebWorkerOptions): void {
240-
const moduleMetadata = self._sentryModuleMetadata ? getFilenameToMetadataMap(defaultStackParser) : undefined;
241-
242-
// Send debug IDs and module metadata to parent thread
234+
// Send debug IDs and raw module metadata to parent thread
235+
// The metadata will be parsed lazily on the main thread when needed
243236
self.postMessage({
244237
_sentryMessage: true,
245238
_sentryDebugIds: self._sentryDebugIds ?? undefined,
246-
_sentryModuleMetadata: moduleMetadata,
239+
_sentryModuleMetadata: self._sentryModuleMetadata ?? undefined,
247240
});
248241

249242
// Set up unhandledrejection handler inside the worker

packages/browser/test/integrations/webWorker.test.ts

Lines changed: 19 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -14,8 +14,6 @@ vi.mock('@sentry/core', async importActual => {
1414
debug: {
1515
log: vi.fn(),
1616
},
17-
mergeMetadataMap: vi.fn(),
18-
getFilenameToMetadataMap: vi.fn(),
1917
};
2018
});
2119

@@ -213,10 +211,10 @@ describe('webWorkerIntegration', () => {
213211
});
214212

215213
it('processes module metadata from worker', () => {
216-
const mockMergeMetadataMap = SentryCore.mergeMetadataMap as any;
214+
(helpers.WINDOW as any)._sentryModuleMetadata = undefined;
217215
const moduleMetadata = {
218-
'worker-file1.js': { '_sentryBundlerPluginAppKey:my-app': true },
219-
'worker-file2.js': { '_sentryBundlerPluginAppKey:my-app': true },
216+
'Error\n at worker-file1.js:1:1': { '_sentryBundlerPluginAppKey:my-app': true },
217+
'Error\n at worker-file2.js:2:2': { '_sentryBundlerPluginAppKey:my-app': true },
220218
};
221219

222220
mockEvent.data = {
@@ -228,13 +226,13 @@ describe('webWorkerIntegration', () => {
228226

229227
expect(mockEvent.stopImmediatePropagation).toHaveBeenCalled();
230228
expect(mockDebugLog).toHaveBeenCalledWith('Sentry module metadata web worker message received', mockEvent.data);
231-
expect(mockMergeMetadataMap).toHaveBeenCalledWith(moduleMetadata);
229+
expect((helpers.WINDOW as any)._sentryModuleMetadata).toEqual(moduleMetadata);
232230
});
233231

234232
it('handles message with both debug IDs and module metadata', () => {
235-
const mockMergeMetadataMap = SentryCore.mergeMetadataMap as any;
233+
(helpers.WINDOW as any)._sentryModuleMetadata = undefined;
236234
const moduleMetadata = {
237-
'worker-file.js': { '_sentryBundlerPluginAppKey:my-app': true },
235+
'Error\n at worker-file.js:1:1': { '_sentryBundlerPluginAppKey:my-app': true },
238236
};
239237

240238
mockEvent.data = {
@@ -246,16 +244,16 @@ describe('webWorkerIntegration', () => {
246244
messageHandler(mockEvent);
247245

248246
expect(mockEvent.stopImmediatePropagation).toHaveBeenCalled();
249-
expect(mockMergeMetadataMap).toHaveBeenCalledWith(moduleMetadata);
247+
expect((helpers.WINDOW as any)._sentryModuleMetadata).toEqual(moduleMetadata);
250248
expect((helpers.WINDOW as any)._sentryDebugIds).toEqual({
251249
'worker-file.js': 'debug-id-1',
252250
});
253251
});
254252

255253
it('accepts message with only module metadata', () => {
256-
const mockMergeMetadataMap = SentryCore.mergeMetadataMap as any;
254+
(helpers.WINDOW as any)._sentryModuleMetadata = undefined;
257255
const moduleMetadata = {
258-
'worker-file.js': { '_sentryBundlerPluginAppKey:my-app': true },
256+
'Error\n at worker-file.js:1:1': { '_sentryBundlerPluginAppKey:my-app': true },
259257
};
260258

261259
mockEvent.data = {
@@ -266,7 +264,7 @@ describe('webWorkerIntegration', () => {
266264
messageHandler(mockEvent);
267265

268266
expect(mockEvent.stopImmediatePropagation).toHaveBeenCalled();
269-
expect(mockMergeMetadataMap).toHaveBeenCalledWith(moduleMetadata);
267+
expect((helpers.WINDOW as any)._sentryModuleMetadata).toEqual(moduleMetadata);
270268
});
271269

272270
it('ignores invalid module metadata', () => {
@@ -343,38 +341,28 @@ describe('registerWebWorker', () => {
343341
});
344342
});
345343

346-
it('calls getFilenameToMetadataMap when module metadata is available', () => {
347-
const mockGetFilenameToMetadataMap = SentryCore.getFilenameToMetadataMap as any;
348-
const extractedMetadata = {
349-
'worker-file1.js': { '_sentryBundlerPluginAppKey:my-app': true },
350-
'worker-file2.js': { '_sentryBundlerPluginAppKey:my-app': true },
351-
};
352-
353-
mockWorkerSelf._sentryModuleMetadata = {
344+
it('includes raw module metadata when available', () => {
345+
const rawMetadata = {
354346
'Error\n at worker-file1.js:1:1': { '_sentryBundlerPluginAppKey:my-app': true },
355347
'Error\n at worker-file2.js:1:1': { '_sentryBundlerPluginAppKey:my-app': true },
356348
};
357349

358-
mockGetFilenameToMetadataMap.mockReturnValue(extractedMetadata);
350+
mockWorkerSelf._sentryModuleMetadata = rawMetadata;
359351

360352
registerWebWorker({ self: mockWorkerSelf as any });
361353

362-
expect(mockGetFilenameToMetadataMap).toHaveBeenCalledWith(expect.any(Function));
363354
expect(mockWorkerSelf.postMessage).toHaveBeenCalledWith({
364355
_sentryMessage: true,
365356
_sentryDebugIds: undefined,
366-
_sentryModuleMetadata: extractedMetadata,
357+
_sentryModuleMetadata: rawMetadata,
367358
});
368359
});
369360

370-
it('does not call getFilenameToMetadataMap when module metadata is not available', () => {
371-
const mockGetFilenameToMetadataMap = SentryCore.getFilenameToMetadataMap as any;
372-
361+
it('sends undefined module metadata when not available', () => {
373362
mockWorkerSelf._sentryModuleMetadata = undefined;
374363

375364
registerWebWorker({ self: mockWorkerSelf as any });
376365

377-
expect(mockGetFilenameToMetadataMap).not.toHaveBeenCalled();
378366
expect(mockWorkerSelf.postMessage).toHaveBeenCalledWith({
379367
_sentryMessage: true,
380368
_sentryDebugIds: undefined,
@@ -383,19 +371,14 @@ describe('registerWebWorker', () => {
383371
});
384372

385373
it('includes both debug IDs and module metadata when both available', () => {
386-
const mockGetFilenameToMetadataMap = SentryCore.getFilenameToMetadataMap as any;
387-
const extractedMetadata = {
388-
'worker-file.js': { '_sentryBundlerPluginAppKey:my-app': true },
374+
const rawMetadata = {
375+
'Error\n at worker-file.js:1:1': { '_sentryBundlerPluginAppKey:my-app': true },
389376
};
390377

391378
mockWorkerSelf._sentryDebugIds = {
392379
'worker-file.js': 'debug-id-1',
393380
};
394-
mockWorkerSelf._sentryModuleMetadata = {
395-
'Error\n at worker-file.js:1:1': { '_sentryBundlerPluginAppKey:my-app': true },
396-
};
397-
398-
mockGetFilenameToMetadataMap.mockReturnValue(extractedMetadata);
381+
mockWorkerSelf._sentryModuleMetadata = rawMetadata;
399382

400383
registerWebWorker({ self: mockWorkerSelf as any });
401384

@@ -404,7 +387,7 @@ describe('registerWebWorker', () => {
404387
_sentryDebugIds: {
405388
'worker-file.js': 'debug-id-1',
406389
},
407-
_sentryModuleMetadata: extractedMetadata,
390+
_sentryModuleMetadata: rawMetadata,
408391
});
409392
});
410393
});

packages/core/src/index.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -322,7 +322,7 @@ export { vercelWaitUntil } from './utils/vercelWaitUntil';
322322
export { flushIfServerless } from './utils/flushIfServerless';
323323
export { SDK_VERSION } from './utils/version';
324324
export { getDebugImagesForResources, getFilenameToDebugIdMap } from './utils/debug-ids';
325-
export { getFilenameToMetadataMap, mergeMetadataMap } from './metadata';
325+
export { getFilenameToMetadataMap } from './metadata';
326326
export { escapeStringForRegex } from './vendor/escapeStringForRegex';
327327

328328
export type { Attachment } from './types-hoist/attachment';

packages/core/src/metadata.ts

Lines changed: 0 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -67,21 +67,6 @@ function ensureMetadataStacksAreParsed(parser: StackParser): void {
6767
}
6868
}
6969

70-
/**
71-
* Merges a filename-to-metadata map into the internal metadata cache.
72-
* This is used to integrate metadata from web workers into the main thread.
73-
*
74-
* @param metadataMap - A map of filename to metadata object
75-
*/
76-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
77-
export function mergeMetadataMap(metadataMap: Record<string, any>): void {
78-
for (const filename of Object.keys(metadataMap)) {
79-
if (!filenameMetadataMap.has(filename)) {
80-
filenameMetadataMap.set(filename, metadataMap[filename]);
81-
}
82-
}
83-
}
84-
8570
/**
8671
* Retrieve metadata for a specific JavaScript file URL.
8772
*

packages/core/test/lib/metadata.test.ts

Lines changed: 0 additions & 73 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@ import {
33
addMetadataToStackFrames,
44
getFilenameToMetadataMap,
55
getMetadataForUrl,
6-
mergeMetadataMap,
76
stripMetadataFromStackFrames,
87
} from '../../src/metadata';
98
import type { Event } from '../../src/types-hoist/event';
@@ -175,75 +174,3 @@ describe('getFilenameToMetadataMap', () => {
175174
expect(result['/path/to/same-file.js']).toBeDefined();
176175
});
177176
});
178-
179-
describe('mergeMetadataMap', () => {
180-
beforeEach(() => {
181-
delete GLOBAL_OBJ._sentryModuleMetadata;
182-
});
183-
184-
it('merges metadata from a map into internal cache', () => {
185-
const workerMetadata = {
186-
'worker-file1.js': { '_sentryBundlerPluginAppKey:my-app': true },
187-
'worker-file2.js': { '_sentryBundlerPluginAppKey:my-app': true },
188-
};
189-
190-
mergeMetadataMap(workerMetadata);
191-
192-
const metadata1 = getMetadataForUrl(parser, 'worker-file1.js');
193-
const metadata2 = getMetadataForUrl(parser, 'worker-file2.js');
194-
195-
expect(metadata1).toEqual({ '_sentryBundlerPluginAppKey:my-app': true });
196-
expect(metadata2).toEqual({ '_sentryBundlerPluginAppKey:my-app': true });
197-
});
198-
199-
it('does not overwrite existing metadata', () => {
200-
const stack = `Error
201-
at Object.<anonymous> (/existing-file.js:10:15)`;
202-
203-
GLOBAL_OBJ._sentryModuleMetadata = {
204-
[stack]: { '_sentryBundlerPluginAppKey:main-app': true, existing: true },
205-
};
206-
207-
const existingMetadata = getMetadataForUrl(parser, '/existing-file.js');
208-
expect(existingMetadata).toEqual({ '_sentryBundlerPluginAppKey:main-app': true, existing: true });
209-
210-
const workerMetadata = {
211-
'/existing-file.js': { '_sentryBundlerPluginAppKey:worker-app': true, worker: true },
212-
};
213-
214-
mergeMetadataMap(workerMetadata);
215-
216-
const metadataAfterMerge = getMetadataForUrl(parser, '/existing-file.js');
217-
expect(metadataAfterMerge).toEqual({ '_sentryBundlerPluginAppKey:main-app': true, existing: true });
218-
});
219-
220-
it('handles empty metadata map', () => {
221-
mergeMetadataMap({});
222-
223-
const metadata = getMetadataForUrl(parser, 'nonexistent-file.js');
224-
expect(metadata).toBeUndefined();
225-
});
226-
227-
it('adds new files without affecting existing ones', () => {
228-
const stack = `Error
229-
at Object.<anonymous> (/main-file.js:10:15)`;
230-
231-
GLOBAL_OBJ._sentryModuleMetadata = {
232-
[stack]: { '_sentryBundlerPluginAppKey:main-app': true },
233-
};
234-
235-
getMetadataForUrl(parser, '/main-file.js');
236-
237-
const workerMetadata = {
238-
'/worker-file.js': { '_sentryBundlerPluginAppKey:worker-app': true },
239-
};
240-
241-
mergeMetadataMap(workerMetadata);
242-
243-
const mainMetadata = getMetadataForUrl(parser, '/main-file.js');
244-
const workerMetadataResult = getMetadataForUrl(parser, '/worker-file.js');
245-
246-
expect(mainMetadata).toEqual({ '_sentryBundlerPluginAppKey:main-app': true });
247-
expect(workerMetadataResult).toEqual({ '_sentryBundlerPluginAppKey:worker-app': true });
248-
});
249-
});

0 commit comments

Comments
 (0)