Skip to content

feat(browser): Add debugId sync APIs between web worker and main thread - #16981

Merged
Lms24 merged 10 commits into
developfrom
lms/feat-browser-web-worker-debugIds
Jul 17, 2025
Merged

feat(browser): Add debugId sync APIs between web worker and main thread#16981
Lms24 merged 10 commits into
developfrom
lms/feat-browser-web-worker-debugIds

Conversation

@Lms24

@Lms24 Lms24 commented Jul 14, 2025

Copy link
Copy Markdown
Member

This PR adds two Browser SDK APIs to let the main thread know about debugIds of worker files:

  • webWorkerIntegration({worker}) to be used in the main thread
  • registerWebWorker(self) to be used in the web worker

The communication between workers and main thread is established between both APIs and they have to be used both for the sync to work correctly. Another limitation around this approach is that users must set up webWorkerInegration before they register their own message listeners. This ensures that the message from registerWebWorker is not propagated to user-created message listeners. We'll document this thoroughly in docs and I already added a section about this in the JSDoc.

Because of the strict co-dependence of both APIs, I decided to add them in one PR (also makes testing easier).

Usage

// main.js
Sentry.init({...})

const worker = new MyWorker(...);

Sentry.addIntegration(Sentry.webWorkerIntegration({ worker }));

worker.addEventListener('message', e => {...});
// worker.js
Sentry.registerWebWorker({ self });

self.postMessage(...);

Multiple Workers

Multiple workers are also supported:

// main.js
Sentry.init({...})

const worker = new MyWorker(...);
const worker2 = new MyWorker2(...);

Sentry.addIntegration(Sentry.webWorkerIntegration({ worker: [worker, worker2] }));

worker.addEventListener('message', e => {...});

A worker can also be passed to the integration after it was initialized. This is helpful if workers are initialized lazily or at different times:

// main.js
Sentry.init({...})

const worker = new MyWorker(...);

const webWorkerIntegration = Sentry.webWorkerIntegration({ worker });

Sentry.addIntegration(webWorkerIntegration);

worker.addEventListener('message', e => {...});

// sometime later

const lazyWorker = new MyLazyWorker(...);
webWorkerIntegration.addWorker(lazyWorker);
lazyWorker.addEventListener('message', e => {...});

I decided to keep both APIs very general around web workers because I think there could be other use cases in which we can sync data by using the worker's message channels. This should be as easy as listening for other messages.

also added

  • unit tests for the two APIs
  • integration test testing the webWorkerIntegration
  • e2e test demonstrating correct usage of both APIs together

closes #16975
closes #16976

@Lms24 Lms24 self-assigned this Jul 14, 2025
_sentryDebugIds: self._sentryDebugIds,
});

self.addEventListener('message', event => {

Check warning

Code scanning / CodeQL

Missing origin verification in `postMessage` handler

Postmessage handler has no origin check.

Copilot Autofix

AI about 1 year ago

To fix the issue, we need to ensure that the origin of incoming messages is checked before processing them. In the context of web workers, the event.origin property is not available. Instead, we should verify the source of the message via event.source or other data provided in the message, such as custom identifiers or tokens.

In this case, we can use a simple check to ensure that the incoming message contains a specific trusted property (e.g., _sentryMessage) or other predefined criteria. If the criteria are not met, the message should be ignored.


Suggested changeset 1
dev-packages/browser-integration-tests/suites/integrations/webWorker/assets/worker.js

Autofix patch

Autofix patch
Run the following command in your local git repository to apply this patch
cat << 'EOF' | git apply
diff --git a/dev-packages/browser-integration-tests/suites/integrations/webWorker/assets/worker.js b/dev-packages/browser-integration-tests/suites/integrations/webWorker/assets/worker.js
--- a/dev-packages/browser-integration-tests/suites/integrations/webWorker/assets/worker.js
+++ b/dev-packages/browser-integration-tests/suites/integrations/webWorker/assets/worker.js
@@ -8,7 +8,10 @@
 });
 
 self.addEventListener('message', event => {
-  if (event.data.type === 'throw-error') {
-    throw new Error('Worker error for testing');
+  // Verify that the message is from a trusted source
+  if (event.data && event.data._sentryMessage) {
+    if (event.data.type === 'throw-error') {
+      throw new Error('Worker error for testing');
+    }
   }
 });
EOF
@@ -8,7 +8,10 @@
});

self.addEventListener('message', event => {
if (event.data.type === 'throw-error') {
throw new Error('Worker error for testing');
// Verify that the message is from a trusted source
if (event.data && event.data._sentryMessage) {
if (event.data.type === 'throw-error') {
throw new Error('Worker error for testing');
}
}
});
Copilot is powered by AI and may make mistakes. Always verify output.
@Lms24
Lms24 force-pushed the lms/feat-browser-web-worker-debugIds branch from ef4ba1a to 3acf7a8 Compare July 16, 2025 08:55
msg: 'WORKER_READY',
});

self.addEventListener('message', event => {

Check warning

Code scanning / CodeQL

Missing origin verification in `postMessage` handler

Postmessage handler has no origin check.

Copilot Autofix

AI about 1 year ago

The fix involves verifying the origin of incoming messages in the message event handler before processing them. This ensures that only messages from trusted sources are acted upon. Specifically:

  1. Identify the trusted origin (e.g., 'https://www.example.com').
  2. Add a conditional check in the message event handler to validate the origin of incoming messages against this trusted value.
  3. Only proceed with processing the message if the origin matches the trusted value.

No new imports or dependencies are required to implement this fix.


Suggested changeset 1
dev-packages/e2e-tests/test-applications/browser-webworker-vite/src/worker.ts

Autofix patch

Autofix patch
Run the following command in your local git repository to apply this patch
cat << 'EOF' | git apply
diff --git a/dev-packages/e2e-tests/test-applications/browser-webworker-vite/src/worker.ts b/dev-packages/e2e-tests/test-applications/browser-webworker-vite/src/worker.ts
--- a/dev-packages/e2e-tests/test-applications/browser-webworker-vite/src/worker.ts
+++ b/dev-packages/e2e-tests/test-applications/browser-webworker-vite/src/worker.ts
@@ -10,8 +10,11 @@
 });
 
 self.addEventListener('message', event => {
-  if (event.data.msg === 'TRIGGER_ERROR') {
-    // This will throw an uncaught error in the worker
-    throw new Error(`Uncaught error in worker`);
+  const trustedOrigin = 'https://www.example.com'; // Define the trusted origin
+  if (event.origin === trustedOrigin) {
+    if (event.data.msg === 'TRIGGER_ERROR') {
+      // This will throw an uncaught error in the worker
+      throw new Error(`Uncaught error in worker`);
+    }
   }
 });
EOF
@@ -10,8 +10,11 @@
});

self.addEventListener('message', event => {
if (event.data.msg === 'TRIGGER_ERROR') {
// This will throw an uncaught error in the worker
throw new Error(`Uncaught error in worker`);
const trustedOrigin = 'https://www.example.com'; // Define the trusted origin
if (event.origin === trustedOrigin) {
if (event.data.msg === 'TRIGGER_ERROR') {
// This will throw an uncaught error in the worker
throw new Error(`Uncaught error in worker`);
}
}
});
Copilot is powered by AI and may make mistakes. Always verify output.
// 2. Fix volta extends
if (!packageJson.volta) {
throw new Error('No volta config found, please provide one!');
throw new Error("No volta config found, please add one to the test app's package.json!");

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

adjusted this message because I didn't realize the volta config needed to be added to the e2e test apps. Might be a me-problem but I think the message is now fool-proof :D

@Lms24
Lms24 marked this pull request as ready for review July 16, 2025 11:00
@Lms24
Lms24 requested review from a team, AbhiPrasad, mydea and timfish and removed request for a team July 16, 2025 11:01
cursor[bot]

This comment was marked as outdated.

@timfish timfish left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM.

What happens if you have more than one worker and try and add them both? If you add the integration twice will it remove the one you added first?

@Lms24

Lms24 commented Jul 16, 2025

Copy link
Copy Markdown
Member Author

What happens if you have more than one worker and try and add them both? If you add the integration twice will it remove the one you added first?

Good point, thanks for raising! The first installed integration wins and the second instance wouldn't be added (=> setupOnce wouldn't be called and debugIds not added).

I think we have a couple of options:

  1. We can let the integration accept an array of workers instead. This assumes though that all workers are initialized at the same time. Which seems unlikely.
  2. We can add a method on the integration (webWorkerIntegration.addWorker(worker)), similarly to how the replay and feature flag integrations work. This increases complexity of the integration (or rather typing-wise) but still comes with the benefit that everything can be handled within one integration.
  3. We could also move away from making the main thread part an integration at all but just a regular function (e.g. Sentry.instrumentWorker()` or something similar). I wanted to go with the integration because it's a well-known pattern and seemed fitting. But maybe we should revisit.

I'm tentatively tending towards 2 but don't yet have a strong opinion. Any thoughts on this? Will also take it to the team to discuss shortly.

(set the PR to draft in the meantime)

@Lms24
Lms24 marked this pull request as draft July 16, 2025 12:48
@timfish

timfish commented Jul 16, 2025

Copy link
Copy Markdown
Collaborator

Another possibly more hacky solution is to set a different name for each instance of the Integration:

let count = 0;

export const webWorkerIntegration = defineIntegration(({ worker }: WebWorkerIntegrationOptions) => ({
  name: `${INTEGRATION_NAME}-${count++}`,
  setupOnce: () => {

Since this integration isn't default included users wont need to use the name to filter it out and because the name differs, multiples can be added?

msg: 'WORKER_2_READY',
});

self.addEventListener('message', event => {

Check warning

Code scanning / CodeQL

Missing origin verification in `postMessage` handler

Postmessage handler has no origin check.

Copilot Autofix

AI about 1 year ago

To fix the issue, the postMessage handler should verify the origin of the incoming message to ensure it comes from a trusted source. This involves:

  1. Adding a conditional check for event.origin against a predefined trusted origin (e.g., 'https://trusted-origin.com').
  2. Ensuring that only messages from the trusted origin are processed, while others are ignored.

The fix will involve modifying the self.addEventListener('message', ...) block to include the origin verification logic. No additional imports or dependencies are required.


Suggested changeset 1
dev-packages/e2e-tests/test-applications/browser-webworker-vite/src/worker2.ts

Autofix patch

Autofix patch
Run the following command in your local git repository to apply this patch
cat << 'EOF' | git apply
diff --git a/dev-packages/e2e-tests/test-applications/browser-webworker-vite/src/worker2.ts b/dev-packages/e2e-tests/test-applications/browser-webworker-vite/src/worker2.ts
--- a/dev-packages/e2e-tests/test-applications/browser-webworker-vite/src/worker2.ts
+++ b/dev-packages/e2e-tests/test-applications/browser-webworker-vite/src/worker2.ts
@@ -12,5 +12,10 @@
 self.addEventListener('message', event => {
-  if (event.data.msg === 'TRIGGER_ERROR') {
-    // This will throw an uncaught error in the worker
-    throw new Error(`Uncaught error in worker 2`);
+  const trustedOrigin = 'https://trusted-origin.com';
+  if (event.origin === trustedOrigin) {
+    if (event.data.msg === 'TRIGGER_ERROR') {
+      // This will throw an uncaught error in the worker
+      throw new Error(`Uncaught error in worker 2`);
+    }
+  } else {
+    console.warn(`Message received from untrusted origin: ${event.origin}`);
   }
EOF
@@ -12,5 +12,10 @@
self.addEventListener('message', event => {
if (event.data.msg === 'TRIGGER_ERROR') {
// This will throw an uncaught error in the worker
throw new Error(`Uncaught error in worker 2`);
const trustedOrigin = 'https://trusted-origin.com';
if (event.origin === trustedOrigin) {
if (event.data.msg === 'TRIGGER_ERROR') {
// This will throw an uncaught error in the worker
throw new Error(`Uncaught error in worker 2`);
}
} else {
console.warn(`Message received from untrusted origin: ${event.origin}`);
}
Copilot is powered by AI and may make mistakes. Always verify output.
msg: 'WORKER_3_READY',
});

self.addEventListener('message', event => {

Check warning

Code scanning / CodeQL

Missing origin verification in `postMessage` handler

Postmessage handler has no origin check.

Copilot Autofix

AI about 1 year ago

To fix the issue, we need to validate the source of the incoming message in the Web Worker. Since the origin property is not available in Web Workers, we can use a custom property in the event.data object (e.g., source) to identify trusted messages. The main thread should include this property when sending messages to the worker, and the worker should verify it before processing the message.

  1. Modify the self.addEventListener('message', ...) handler to include a check for a trusted source property in the event.data object.
  2. Ensure that only messages with the expected source value are processed.

Suggested changeset 1
dev-packages/e2e-tests/test-applications/browser-webworker-vite/src/worker3.ts

Autofix patch

Autofix patch
Run the following command in your local git repository to apply this patch
cat << 'EOF' | git apply
diff --git a/dev-packages/e2e-tests/test-applications/browser-webworker-vite/src/worker3.ts b/dev-packages/e2e-tests/test-applications/browser-webworker-vite/src/worker3.ts
--- a/dev-packages/e2e-tests/test-applications/browser-webworker-vite/src/worker3.ts
+++ b/dev-packages/e2e-tests/test-applications/browser-webworker-vite/src/worker3.ts
@@ -12,2 +12,8 @@
 self.addEventListener('message', event => {
+  // Verify the source of the message
+  if (event.data.source !== 'trusted-main-thread') {
+    console.warn('Untrusted message source:', event.data.source);
+    return;
+  }
+
   if (event.data.msg === 'TRIGGER_ERROR') {
EOF
@@ -12,2 +12,8 @@
self.addEventListener('message', event => {
// Verify the source of the message
if (event.data.source !== 'trusted-main-thread') {
console.warn('Untrusted message source:', event.data.source);
return;
}

if (event.data.msg === 'TRIGGER_ERROR') {
Copilot is powered by AI and may make mistakes. Always verify output.
@Lms24

Lms24 commented Jul 16, 2025

Copy link
Copy Markdown
Member Author

I went with options 1 and 2 combined. Decided to do it "properly" instead of the "hack" (though I liked the suggestion) to avoid the side effect it would introduce and to keep the integration name constant. Not sure why users would look up or remove the integration but let's keep the integration names predictable.

@Lms24
Lms24 marked this pull request as ready for review July 16, 2025 14:09

@timfish timfish left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks good!

@Lms24
Lms24 merged commit e8f2b2d into develop Jul 17, 2025
@Lms24
Lms24 deleted the lms/feat-browser-web-worker-debugIds branch July 17, 2025 07:29
Lms24 added a commit to getsentry/sentry-docs that referenced this pull request Jul 22, 2025
#14395)

<!-- Use this checklist to make sure your PR is ready for merge. You may
delete any sections you don't need. -->

## DESCRIBE YOUR PR

closes getsentry/sentry-javascript#16977
closes getsentry/sentry-javascript#16974

This PR adds documentation for two new `WebWorker`-related APIs:
- `Sentry.webWorkerIntegration()`
- `Sentry.registerWebWorker()`

More details in
getsentry/sentry-javascript#16981

I also gave the entire web worker guide a facelift, removed some
unnecessary `notSupported` platforms and added an integration page for
the new integration. Also, the guide now includes a paragraph about
configuring Vite correctly for worker builds which was raised in
getsentry/sentry-javascript-bundler-plugins#755.


## IS YOUR CHANGE URGENT?  

Help us prioritize incoming PRs by letting us know when the change needs
to go live.
- [ ] Urgent deadline (GA date, etc.): <!-- ENTER DATE HERE -->
- [ ] Other deadline: <!-- ENTER DATE HERE -->
- [x] None: Not urgent, can wait up to 1 week+ (only to be merged after
9.40.0 was released)

## SLA

- Teamwork makes the dream work, so please add a reviewer to your PRs.
- Please give the docs team up to 1 week to review your PR unless you've
added an urgent due date to it.
Thanks in advance for your help!

## PRE-MERGE CHECKLIST

*Make sure you've checked the following before merging your changes:*

- [x] Checked Vercel preview for correctness, including links
- [x] PR was reviewed and approved by any necessary SMEs (subject matter
experts)
- [ ] PR was reviewed and approved by a member of the [Sentry docs
team](https://github.com/orgs/getsentry/teams/docs)

## LEGAL BOILERPLATE

<!-- Sentry employees and contractors can delete or ignore this section.
-->

Look, I get it. The entity doing business as "Sentry" was incorporated
in the State of Delaware in 2015 as Functional Software, Inc. and is
gonna need some rights from me in order to utilize my contributions in
this here PR. So here's the deal: I retain all rights, title and
interest in and to my contributions, and by keeping this boilerplate
intact I confirm that Sentry can use, modify, copy, and redistribute my
contributions, under Sentry's choice of terms.

## EXTRA RESOURCES

- [Sentry Docs contributor guide](https://docs.sentry.io/contributing/)

---------

Co-authored-by: Alex Krawiec <alex.krawiec@sentry.io>
lucas-zimerman pushed a commit to getsentry/sentry-docs that referenced this pull request Jul 29, 2025
#14395)

<!-- Use this checklist to make sure your PR is ready for merge. You may
delete any sections you don't need. -->

## DESCRIBE YOUR PR

closes getsentry/sentry-javascript#16977
closes getsentry/sentry-javascript#16974

This PR adds documentation for two new `WebWorker`-related APIs:
- `Sentry.webWorkerIntegration()`
- `Sentry.registerWebWorker()`

More details in
getsentry/sentry-javascript#16981

I also gave the entire web worker guide a facelift, removed some
unnecessary `notSupported` platforms and added an integration page for
the new integration. Also, the guide now includes a paragraph about
configuring Vite correctly for worker builds which was raised in
getsentry/sentry-javascript-bundler-plugins#755.


## IS YOUR CHANGE URGENT?  

Help us prioritize incoming PRs by letting us know when the change needs
to go live.
- [ ] Urgent deadline (GA date, etc.): <!-- ENTER DATE HERE -->
- [ ] Other deadline: <!-- ENTER DATE HERE -->
- [x] None: Not urgent, can wait up to 1 week+ (only to be merged after
9.40.0 was released)

## SLA

- Teamwork makes the dream work, so please add a reviewer to your PRs.
- Please give the docs team up to 1 week to review your PR unless you've
added an urgent due date to it.
Thanks in advance for your help!

## PRE-MERGE CHECKLIST

*Make sure you've checked the following before merging your changes:*

- [x] Checked Vercel preview for correctness, including links
- [x] PR was reviewed and approved by any necessary SMEs (subject matter
experts)
- [ ] PR was reviewed and approved by a member of the [Sentry docs
team](https://github.com/orgs/getsentry/teams/docs)

## LEGAL BOILERPLATE

<!-- Sentry employees and contractors can delete or ignore this section.
-->

Look, I get it. The entity doing business as "Sentry" was incorporated
in the State of Delaware in 2015 as Functional Software, Inc. and is
gonna need some rights from me in order to utilize my contributions in
this here PR. So here's the deal: I retain all rights, title and
interest in and to my contributions, and by keeping this boilerplate
intact I confirm that Sentry can use, modify, copy, and redistribute my
contributions, under Sentry's choice of terms.

## EXTRA RESOURCES

- [Sentry Docs contributor guide](https://docs.sentry.io/contributing/)

---------

Co-authored-by: Alex Krawiec <alex.krawiec@sentry.io>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Add worker thread Sentry.registerWorker(self) API Add main thread webWorkerIntegration

4 participants