Skip to content

Commit 808245c

Browse files
committed
feat(browser): Add debugId sync APIs between web worker and main thread
1 parent b0a1e0c commit 808245c

File tree

2 files changed

+439
-0
lines changed

2 files changed

+439
-0
lines changed
Lines changed: 107 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,107 @@
1+
import { debug, defineIntegration, isPlainObject } from '@sentry/core';
2+
import { DEBUG_BUILD } from '../debug-build';
3+
import { WINDOW } from '../helpers';
4+
5+
export const INTEGRATION_NAME = 'WebWorker';
6+
7+
interface WebWorkerMessage {
8+
_sentryMessage: boolean;
9+
_sentryDebugIds: Record<string, string>;
10+
}
11+
12+
interface WebWorkerIntegrationOptions {
13+
worker: Worker;
14+
}
15+
16+
/**
17+
* Use this integration to set up Sentry with web workers.
18+
*
19+
* IMPORTANT: This integration must be added **before** you start listening to
20+
* any messages from the worker. Otherwise, your message handlers will receive
21+
* messages from Sentry which you need to ignore.
22+
*
23+
* This integration only has an effect, if you call `Sentry.registerWorker(self)`
24+
* from within the worker you're adding to the integration.
25+
*
26+
* Given that you want to initialize the SDK as early as possible, you most likely
27+
* want to add the integration after initializing the SDK:
28+
*
29+
* @example:
30+
* ```ts filename={main.js}
31+
* import * as Sentry from '@sentry/<your-sdk>';
32+
*
33+
* // some time earlier:
34+
* Sentry.init(...)
35+
*
36+
* // 1. Initialize the worker
37+
* const worker = new Worker(new URL('./worker.ts', import.meta.url));
38+
*
39+
* // 2. Add the integration
40+
* Sentry.addIntegration(Sentry.webWorkerIntegration({ worker }));
41+
*
42+
* // 3. Register message listeners on the worker
43+
* worker.addEventListener('message', event => {
44+
* // ...
45+
* });
46+
* ```
47+
*
48+
* Of course, you can also directly add the integration in Sentry.init:
49+
* ```ts filename={main.js}
50+
* import * as Sentry from '@sentry/<your-sdk>';
51+
*
52+
* // 1. Initialize the worker
53+
* const worker = new Worker(new URL('./worker.ts', import.meta.url));
54+
*
55+
* // 2. Initialize the SDK
56+
* Sentry.init({
57+
* integrations: [Sentry.webWorkerIntegration({ worker })]
58+
* });
59+
*
60+
* // 3. Register message listeners on the worker
61+
* worker.addEventListener('message', event => {
62+
* // ...
63+
* });
64+
* ```
65+
*/
66+
export const webWorkerIntegration = defineIntegration(({ worker }: WebWorkerIntegrationOptions) => ({
67+
name: INTEGRATION_NAME,
68+
setupOnce: () => {
69+
worker.addEventListener('message', event => {
70+
if (isWebWorkerMessage(event.data)) {
71+
event.stopImmediatePropagation(); // other listeners should not receive this message
72+
DEBUG_BUILD && debug.log('Sentry debugId web worker message received', event.data);
73+
WINDOW._sentryDebugIds = {
74+
...event.data._sentryDebugIds,
75+
// debugIds of the main thread have precedence over the worker's in case of a collision.
76+
...WINDOW._sentryDebugIds,
77+
};
78+
}
79+
});
80+
},
81+
}));
82+
83+
/**
84+
* Use this function to register the worker with the Sentry SDK.
85+
*
86+
* @example
87+
* ```ts filename={worker.js}
88+
* import * as Sentry from '@sentry/<your-sdk>';
89+
*
90+
* // Do this as early as possible in your worker.
91+
* Sentry.registerWorker(self);
92+
*
93+
* // continue setting up your worker
94+
* self.postMessage(...)
95+
* ```
96+
* @param self The worker instance.
97+
*/
98+
export function registerWebWorker(self: Worker & { _sentryDebugIds: Record<string, string> }): void {
99+
self.postMessage({
100+
_sentryMessage: true,
101+
_sentryDebugIds: self._sentryDebugIds ?? undefined,
102+
});
103+
}
104+
105+
function isWebWorkerMessage(eventData: unknown): eventData is WebWorkerMessage {
106+
return isPlainObject(eventData) && eventData._sentryMessage === true && typeof eventData._sentry === 'object';
107+
}

0 commit comments

Comments
 (0)