diff --git a/packages/util/__tests__/runtime.test.ts b/packages/util/__tests__/runtime.test.ts new file mode 100644 index 000000000..7c168ae34 --- /dev/null +++ b/packages/util/__tests__/runtime.test.ts @@ -0,0 +1,47 @@ +import { afterEach, describe, expect, test, vi } from 'vitest'; +import { shouldUseGlobalFetchAndWebSocket } from '../src/index.js'; + +describe('shouldUseGlobalFetchAndWebSocket', () => { + afterEach(() => { + vi.unstubAllGlobals(); + }); + + test('GIVEN browser env with fetch and WebSocket THEN returns true', () => { + vi.stubGlobal('process', undefined); + vi.stubGlobal('fetch', () => void 0); + vi.stubGlobal('WebSocket', class {}); + expect(shouldUseGlobalFetchAndWebSocket()).toBe(true); + }); + + test('GIVEN browser env without fetch or WebSocket THEN returns false', () => { + vi.stubGlobal('process', undefined); + vi.stubGlobal('fetch', globalThis.fetch); + vi.stubGlobal('WebSocket', globalThis.WebSocket); + // @ts-expect-error Testing missing globals + delete globalThis.fetch; + // @ts-expect-error Testing missing globals + delete globalThis.WebSocket; + expect(shouldUseGlobalFetchAndWebSocket()).toBe(false); + }); + + test('GIVEN Cloudflare Workers with nodejs_compat THEN returns true', () => { + vi.stubGlobal('process', { versions: { node: '22.19.0' } }); + vi.stubGlobal('WebSocketPair', class {}); + expect(shouldUseGlobalFetchAndWebSocket()).toBe(true); + }); + + test('GIVEN Node.js THEN returns false', () => { + vi.stubGlobal('process', { versions: { node: '22.19.0' } }); + expect(shouldUseGlobalFetchAndWebSocket()).toBe(false); + }); + + test('GIVEN Deno THEN returns true', () => { + vi.stubGlobal('process', { versions: { deno: '1.0.0' } }); + expect(shouldUseGlobalFetchAndWebSocket()).toBe(true); + }); + + test('GIVEN Bun THEN returns true', () => { + vi.stubGlobal('process', { versions: { bun: '1.0.0' } }); + expect(shouldUseGlobalFetchAndWebSocket()).toBe(true); + }); +}); diff --git a/packages/util/src/functions/runtime.ts b/packages/util/src/functions/runtime.ts index a84014397..d06f16c9b 100644 --- a/packages/util/src/functions/runtime.ts +++ b/packages/util/src/functions/runtime.ts @@ -6,6 +6,14 @@ export function shouldUseGlobalFetchAndWebSocket() { return 'fetch' in globalThis && 'WebSocket' in globalThis; } + // Cloudflare Workers with nodejs_compat polyfills process (including + // process.versions.node), but natively supports the Web WebSocket API. + // WebSocketPair is a Workers-only global; no other runtime exposes it. + // @ts-expect-error WebSocketPair is not in the globalThis type + if (typeof globalThis.WebSocketPair !== 'undefined') { + return true; + } + if ('versions' in globalThis.process) { return 'deno' in globalThis.process.versions || 'bun' in globalThis.process.versions; }