diff --git a/open-sse/utils/stream.ts b/open-sse/utils/stream.ts index 24999c2eee..acc85e2c43 100644 --- a/open-sse/utils/stream.ts +++ b/open-sse/utils/stream.ts @@ -74,7 +74,6 @@ import { hasUnsupportedReasoningSignal, } from "./reasoningFields.ts"; import { applyThinkTag, flushThink, initThinkState } from "./thinkTagParser.ts"; -import { sseCommentsEnabled } from "./sseHeartbeat.ts"; import { caseInsensitiveToolNameLookup, restoreOpenAIToolNames, @@ -1882,7 +1881,6 @@ export function createSSEStream(options: StreamOptions = {}) { passthroughSawFinishReason = true; } - if (isFinishChunk && passthroughHasToolCalls) { toolFinishTime = now; try { @@ -2221,7 +2219,8 @@ export function createSSEStream(options: StreamOptions = {}) { }, pushProviderPayload: (payload: unknown) => providerPayloadCollector.push(payload), pushClientPayload: (payload: unknown) => clientPayloadCollector.push(payload), - sanitizeUsagePayload: (payload: unknown) => sanitizeUsagePayloadForRequest(payload, body, clientResponseFormat), + sanitizeUsagePayload: (payload: unknown) => + sanitizeUsagePayloadForRequest(payload, body, clientResponseFormat), setPassthroughResponsesId: (value: string) => { passthroughResponsesId = value; }, @@ -2282,7 +2281,8 @@ export function createSSEStream(options: StreamOptions = {}) { const bufferedPayload = parseSSELine(bufferedLine); if (bufferedPayload) { providerPayloadCollector.push(bufferedPayload); - if (sanitizeUsagePayloadForRequest(bufferedPayload, body, clientResponseFormat)) output = `data: ${JSON.stringify(bufferedPayload)}\n\n`; + if (sanitizeUsagePayloadForRequest(bufferedPayload, body, clientResponseFormat)) + output = `data: ${JSON.stringify(bufferedPayload)}\n\n`; if ( shouldInjectClaudeEmptyResponseBeforeCurrentEvent( claudeEmptyResponseLifecycle, diff --git a/tests/unit/stream-imports-no-duplicates.test.ts b/tests/unit/stream-imports-no-duplicates.test.ts new file mode 100644 index 0000000000..abf4c755cf --- /dev/null +++ b/tests/unit/stream-imports-no-duplicates.test.ts @@ -0,0 +1,53 @@ +/** + * stream-imports-no-duplicates.test.ts — regression guard for duplicate import + * bindings in open-sse/utils/stream.ts. + * + * A merge auto-resolve (#9378 → commit 9a4cca4bc2) left `sseCommentsEnabled` + * imported twice. tsx/esbuild (typecheck + test runners) silently dedupe the + * binding, but the webpack production build fails with "Identifier + * 'sseCommentsEnabled' has already been declared" — so the defect only + * surfaces at release-build time. This static guard fails fast in the unit + * suite instead. + */ +import test from "node:test"; +import assert from "node:assert/strict"; +import { readFile } from "node:fs/promises"; +import { fileURLToPath } from "node:url"; + +const STREAM_TS = fileURLToPath(new URL("../../open-sse/utils/stream.ts", import.meta.url)); + +test("open-sse/utils/stream.ts has no duplicate import bindings", async () => { + const source = await readFile(STREAM_TS, "utf8"); + + const bindings = new Map(); + const importRegex = /^import\s+(?:type\s+)?(\{[\s\S]*?\}|[A-Za-z0-9_$]+)\s+from\s+/gm; + + for (const match of source.matchAll(importRegex)) { + const clause = match[1]; + const names = clause.startsWith("{") + ? clause + .slice(1, -1) + .split(",") + .map( + (n) => + n + .trim() + .split(/\s+as\s+/) + .pop() + ?.trim() ?? "" + ) + .filter(Boolean) + : [clause.trim()]; + for (const name of names) { + bindings.set(name, (bindings.get(name) ?? 0) + 1); + } + } + + const duplicates = [...bindings.entries()].filter(([, count]) => count > 1).map(([name]) => name); + + assert.deepEqual( + duplicates, + [], + `duplicate import bindings in stream.ts (breaks webpack production build): ${duplicates.join(", ")}` + ); +});