fix(sse): import localDb through its real .ts extension (#10674) (#10691)

`open-sse/services/combo.ts` imported "../../src/lib/localDb.js" — a .js suffix
on a module that only exists as .ts. Turbopack resolved it by accident until the
dependency-tree change in #10647; after that the instrumentation hook died at boot
with MODULE_NOT_FOUND, breaking `npm run dev` and the production build (60
consecutive red `Build App` runs on release/v3.8.50).

Fixes the same latent pattern in src/lib/usage/usageLedger.ts, which survived only
because it is an `import type` and is erased before resolution.

Adds a guard rejecting relative .js specifiers across open-sse/ and src/. Package
specifiers are untouched: publishing ESM as .js is legitimate there (e.g.
@modelcontextprotocol/sdk), and only first-party relative imports are first-party
TypeScript.

Closes #10674

Co-authored-by: Xiangzhe <bakryun0718@proton.me>
This commit is contained in:
Diego Rodrigues de Sa e Souza
2026-08-18 19:23:16 -03:00
committed by GitHub
parent 539cb3b7bc
commit 37c81ce1d7
3 changed files with 46 additions and 3 deletions

View File

@@ -87,7 +87,7 @@ import { selectQuotaShareTarget } from "./combo/quotaShareStrategy.ts";
import { makeConnectionConcurrencyResolver, lookupPositiveCap } from "./combo/concurrencyCaps.ts";
import { acquireQuotaShareConcurrencySlot } from "./combo/quotaShareConcurrency.ts";
import { canAffordRequest } from "../../src/lib/quota/quotaScheduler.ts";
import { getCachedProviderConnectionById } from "../../src/lib/localDb.js";
import { getCachedProviderConnectionById } from "../../src/lib/localDb.ts";
import { orderTargetsByEvalScores } from "./evalRouting.ts";
/**
@@ -1190,7 +1190,6 @@ export async function handleComboChat({
if (i > 0) fallbackCount++;
return stopProtectedPriorityTarget(`Connection capacity reached for ${modelStr}`);
}
}
// #9654 Wave 2: per-target lane-aware admission probe. With virtual

View File

@@ -1,4 +1,4 @@
import type { ModelPricingRegistry } from "./modelPricingRegistry.js";
import type { ModelPricingRegistry } from "./modelPricingRegistry.ts";
export type UsageStatus = "success" | "failed" | "rate_limited" | "timeout" | "cancelled";

View File

@@ -0,0 +1,44 @@
import assert from "node:assert/strict";
import { test } from "node:test";
import { execFileSync } from "node:child_process";
import path from "node:path";
import { fileURLToPath } from "node:url";
/**
* #10674: `open-sse/services/combo.ts` imported `"../../src/lib/localDb.js"` — a
* `.js` suffix on a file that only exists as `.ts`. Turbopack used to resolve it
* anyway; after the dependency-tree change in #10647 it stopped, and the
* instrumentation hook died at boot with MODULE_NOT_FOUND, taking down `npm run dev`
* and the production build (60 consecutive red `Build App` runs).
*
* A `.js` specifier is legitimate for npm packages that publish ESM that way
* (e.g. `@modelcontextprotocol/sdk/client/index.js`) — this guard only rejects
* RELATIVE specifiers, which always point at first-party TypeScript here.
*/
const REPO_ROOT = path.resolve(path.dirname(fileURLToPath(import.meta.url)), "../..");
function relativeJsImports(): string[] {
let out = "";
try {
out = execFileSync(
"git",
["grep", "-n", "-E", 'from "\\.{1,2}/[^"]*\\.js"', "--", "open-sse/**/*.ts", "src/**/*.ts"],
{ cwd: REPO_ROOT, encoding: "utf8" }
);
} catch (err) {
// git grep exits 1 with no output when nothing matches — that is the clean state.
if ((err as { status?: number }).status === 1) return [];
throw err;
}
return out.split("\n").filter((line) => line.trim().length > 0);
}
test("no first-party TypeScript module is imported through a .js specifier", () => {
const offenders = relativeJsImports();
assert.deepEqual(
offenders,
[],
`Relative imports must use the real .ts extension — a .js suffix resolves only by ` +
`bundler accident and breaks boot when the toolchain changes (#10674):\n${offenders.join("\n")}`
);
});