From 49b2af0e2a5ce444c2bec57a474324fff2bf5a4b Mon Sep 17 00:00:00 2001 From: Ahmed Abbas Date: Fri, 5 Jun 2026 18:56:42 +0300 Subject: [PATCH 1/3] fix(utils): classify browser Web Workers as browser runtime in HttpClient determineRuntime() equated 'no window' with server-side, so the signals Web Worker fell into 'server-with-fetch' and the ConvertAgent User-Agent announcement (meant for server-side SDK traffic only) ran inside real browsers. The resulting custom header forced a CORS preflight the signals endpoint rejects, breaking every signals worker upload and driving the WAF retry storm. Browser worker scopes are now detected via importScripts, which server runtimes (Node.js) and edge runtimes (Cloudflare Workers) do not expose, so those keep announcing ConvertAgent as intended. Co-Authored-By: Claude Opus 4.8 (1M context) --- packages/utils/src/http-client.ts | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/packages/utils/src/http-client.ts b/packages/utils/src/http-client.ts index 6a6d0ec0..49d77bc0 100644 --- a/packages/utils/src/http-client.ts +++ b/packages/utils/src/http-client.ts @@ -137,6 +137,20 @@ const determineRuntime = (): RuntimeResult => { return {runtime: 'browser'}; } + // Browser Web Workers have no `window` but are still browser runtimes. + // `importScripts` exists only in browser worker scopes — server runtimes + // (Node.js) and edge runtimes (e.g. Cloudflare Workers) do not expose it. + // Without this check a Web Worker falls through to 'server-with-fetch' and + // server-side-only behavior leaks into real browsers (the ConvertAgent + // User-Agent announcement below forced a CORS preflight the signals + // endpoint rejected, breaking every signals worker upload). + if ( + typeof self !== 'undefined' && + typeof (self as {importScripts?: unknown}).importScripts === 'function' + ) { + return {runtime: 'browser'}; + } + // if window is not available, but fetch is, then we're on a server that has the fetch API available if (typeof fetch === 'function') { return {runtime: 'server-with-fetch'}; From 7d47d87a82b6797e7683205a26a5f60589d3c06e Mon Sep 17 00:00:00 2001 From: Ahmed Abbas Date: Fri, 5 Jun 2026 18:56:42 +0300 Subject: [PATCH 2/3] fix(js-sdk): pick up Web Worker runtime detection fix from js-sdk-utils Empty release commit so @convertcom/js-sdk ships a new version carrying the patched @convertcom/js-sdk-utils dependency. Co-Authored-By: Claude Opus 4.8 (1M context) From d852dc370653581abe3d656eb3c35cd2f0edd587 Mon Sep 17 00:00:00 2001 From: Ahmed Abbas Date: Fri, 5 Jun 2026 19:15:12 +0300 Subject: [PATCH 3/3] fix(utils): tolerate null self in worker runtime detection typeof null is 'object', so a mocked environment defining self = null passed the typeof guard and the importScripts access would throw. Optional chaining degrades it to undefined and falls through to the server checks, matching the file's existing navigator?.sendBeacon idiom. Co-Authored-By: Claude Opus 4.8 (1M context) --- packages/utils/src/http-client.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/utils/src/http-client.ts b/packages/utils/src/http-client.ts index 49d77bc0..bc596ab6 100644 --- a/packages/utils/src/http-client.ts +++ b/packages/utils/src/http-client.ts @@ -146,7 +146,7 @@ const determineRuntime = (): RuntimeResult => { // endpoint rejected, breaking every signals worker upload). if ( typeof self !== 'undefined' && - typeof (self as {importScripts?: unknown}).importScripts === 'function' + typeof (self as {importScripts?: unknown})?.importScripts === 'function' ) { return {runtime: 'browser'}; }