From 28c8587d511de5837c17f48b85b48b14f1414e1e Mon Sep 17 00:00:00 2001 From: Steven Zhang Date: Tue, 21 Apr 2026 09:47:10 -0500 Subject: [PATCH 1/3] fix: correct typeof comparisons in browser SDK Fix typeof comparisons that compared against the value `undefined` instead of the string `'undefined'`. Since typeof always returns a string, comparing to the value `undefined` made these checks always truthy. Affected functions: - isDocument() and isWindow() in BrowserApi.ts now correctly return false in service worker contexts where document/window are unavailable - getCrypto() now correctly throws when crypto API is unavailable - randomUuidV4() now correctly falls back when crypto.randomUUID is missing Re-enables the valid-typeof ESLint rule to catch this class of bug. Co-Authored-By: Claude Opus 4.6 (1M context) --- .eslintrc.js | 2 +- packages/sdk/browser/src/BrowserApi.ts | 6 +++--- packages/sdk/browser/src/platform/randomUuidV4.ts | 2 +- packages/telemetry/browser-telemetry/src/randomUuidV4.ts | 2 +- 4 files changed, 6 insertions(+), 6 deletions(-) diff --git a/.eslintrc.js b/.eslintrc.js index 15060abbe8..12819f6665 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -28,7 +28,7 @@ module.exports = { 'eqeqeq': ['error', 'always', { null: 'ignore' }], 'prefer-const': ['error', { ignoreReadBeforeAssign: true }], 'no-var': 'error', - 'valid-typeof': 'off', + 'valid-typeof': 'error', 'no-restricted-syntax': [ 'error', { selector: 'ForInStatement', message: 'Use Object.{keys,values,entries} instead.' }, diff --git a/packages/sdk/browser/src/BrowserApi.ts b/packages/sdk/browser/src/BrowserApi.ts index a9c233b7a6..512220a3de 100644 --- a/packages/sdk/browser/src/BrowserApi.ts +++ b/packages/sdk/browser/src/BrowserApi.ts @@ -5,11 +5,11 @@ */ export function isDocument() { - return typeof document !== undefined; + return typeof document !== 'undefined'; } export function isWindow() { - return typeof window !== undefined; + return typeof window !== 'undefined'; } /** @@ -91,7 +91,7 @@ export function getLocationHash(): string { } export function getCrypto(): Crypto { - if (typeof crypto !== undefined) { + if (typeof crypto !== 'undefined') { return crypto; } // This would indicate running in an environment that doesn't have window.crypto or self.crypto. diff --git a/packages/sdk/browser/src/platform/randomUuidV4.ts b/packages/sdk/browser/src/platform/randomUuidV4.ts index 0659d58f72..620cd9acc1 100644 --- a/packages/sdk/browser/src/platform/randomUuidV4.ts +++ b/packages/sdk/browser/src/platform/randomUuidV4.ts @@ -91,7 +91,7 @@ export function fallbackUuidV4(): string { } export default function randomUuidV4(): string { - if (typeof crypto !== undefined && typeof crypto.randomUUID === 'function') { + if (typeof crypto !== 'undefined' && typeof crypto.randomUUID === 'function') { return crypto.randomUUID(); } diff --git a/packages/telemetry/browser-telemetry/src/randomUuidV4.ts b/packages/telemetry/browser-telemetry/src/randomUuidV4.ts index ba455a577c..7e9c065c57 100644 --- a/packages/telemetry/browser-telemetry/src/randomUuidV4.ts +++ b/packages/telemetry/browser-telemetry/src/randomUuidV4.ts @@ -94,7 +94,7 @@ export function fallbackUuidV4(): string { } export default function randomUuidV4(): string { - if (typeof crypto !== undefined && typeof crypto.randomUUID === 'function') { + if (typeof crypto !== 'undefined' && typeof crypto.randomUUID === 'function') { return crypto.randomUUID(); } From 0869f9dab4531d9aaec6af47229a5c869c21f507 Mon Sep 17 00:00:00 2001 From: Steven Zhang Date: Tue, 21 Apr 2026 12:08:08 -0500 Subject: [PATCH 2/3] test: add unit tests for typeof fix - BrowserApi: verify isDocument() and isWindow() return true when globals exist - randomUuidV4: verify fallback path when crypto.randomUUID is unavailable Co-Authored-By: Claude Opus 4.6 (1M context) --- .../sdk/browser/__tests__/BrowserApi.test.ts | 9 +++++++++ .../__tests__/platform/randomUuidV4.test.ts | 18 +++++++++++++++++- 2 files changed, 26 insertions(+), 1 deletion(-) create mode 100644 packages/sdk/browser/__tests__/BrowserApi.test.ts diff --git a/packages/sdk/browser/__tests__/BrowserApi.test.ts b/packages/sdk/browser/__tests__/BrowserApi.test.ts new file mode 100644 index 0000000000..e2ca44ed85 --- /dev/null +++ b/packages/sdk/browser/__tests__/BrowserApi.test.ts @@ -0,0 +1,9 @@ +import { isDocument, isWindow } from '../src/BrowserApi'; + +it('isDocument returns true when document is defined', () => { + expect(isDocument()).toBe(true); +}); + +it('isWindow returns true when window is defined', () => { + expect(isWindow()).toBe(true); +}); diff --git a/packages/sdk/browser/__tests__/platform/randomUuidV4.test.ts b/packages/sdk/browser/__tests__/platform/randomUuidV4.test.ts index 73be7647bc..748dfe547f 100644 --- a/packages/sdk/browser/__tests__/platform/randomUuidV4.test.ts +++ b/packages/sdk/browser/__tests__/platform/randomUuidV4.test.ts @@ -1,4 +1,4 @@ -import { fallbackUuidV4, formatDataAsUuidV4 } from '../../src/platform/randomUuidV4'; +import randomUuidV4, { fallbackUuidV4, formatDataAsUuidV4 } from '../../src/platform/randomUuidV4'; it('formats conformant UUID', () => { // For this test we remove the random component and just inspect the variant and version. @@ -26,3 +26,19 @@ it('formats conformant UUID', () => { expect(specifierB).toEqual(0x8); expect(specifierC).toEqual(0x8); }); + +it('falls back when crypto.randomUUID is unavailable', () => { + const original = globalThis.crypto; + Object.defineProperty(globalThis, 'crypto', { + value: { getRandomValues: original.getRandomValues.bind(original) }, + configurable: true, + }); + + try { + const uuid = randomUuidV4(); + expect(uuid).toHaveLength(36); + expect(uuid[14]).toEqual('4'); + } finally { + Object.defineProperty(globalThis, 'crypto', { value: original, configurable: true }); + } +}); From 074d3f332cde43b1bd533774c135a02e21f957ae Mon Sep 17 00:00:00 2001 From: Steven Zhang Date: Tue, 21 Apr 2026 12:29:46 -0500 Subject: [PATCH 3/3] test: add negative tests for isDocument and isWindow Verify isDocument() and isWindow() return false when the globals are undefined, simulating a service worker environment. Co-Authored-By: Claude Opus 4.6 (1M context) --- .../sdk/browser/__tests__/BrowserApi.test.ts | 20 +++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/packages/sdk/browser/__tests__/BrowserApi.test.ts b/packages/sdk/browser/__tests__/BrowserApi.test.ts index e2ca44ed85..391e1d11a0 100644 --- a/packages/sdk/browser/__tests__/BrowserApi.test.ts +++ b/packages/sdk/browser/__tests__/BrowserApi.test.ts @@ -4,6 +4,26 @@ it('isDocument returns true when document is defined', () => { expect(isDocument()).toBe(true); }); +it('isDocument returns false when document is not defined', () => { + const original = Object.getOwnPropertyDescriptor(globalThis, 'document'); + Object.defineProperty(globalThis, 'document', { value: undefined, configurable: true }); + try { + expect(isDocument()).toBe(false); + } finally { + Object.defineProperty(globalThis, 'document', original!); + } +}); + it('isWindow returns true when window is defined', () => { expect(isWindow()).toBe(true); }); + +it('isWindow returns false when window is not defined', () => { + const original = Object.getOwnPropertyDescriptor(globalThis, 'window'); + Object.defineProperty(globalThis, 'window', { value: undefined, configurable: true }); + try { + expect(isWindow()).toBe(false); + } finally { + Object.defineProperty(globalThis, 'window', original!); + } +});