Files
OmniRoute/tests/unit/providers-constants-split.test.ts
Diego Rodrigues de Sa e Souza af65171e3f fix(ci): clear the base-reds the afternoon merge batch left on release/v3.8.51 (round 5: provider count 352, TS2554/TS2677) (#12144)
* fix(ci): clear the base-reds the 2026-08-30 afternoon merge batch left on release/v3.8.51 (round 5)

- docs-counts / check-docs-counts-sync test: #12103 (Perplexity Agent) made it 352
  providers; README, AGENTS.md, llm.txt (+42 i18n mirrors), package.json description
  and the 4 README diagrams still said 351.
- api-route-typecheck: #11971 passes a third `{ featureEnabled }` argument to
  appendNoThinkingVariants() that the helper never accepted (TS2554 — and the flag
  silently did nothing); the helper now honours it. src/lib/skills/interception.ts
  narrowed a mapped object with a `Record<string, string>` predicate (TS2677) —
  predicate typed with the actual element shape.

Gates: check:docs-counts OK (test 28/28), check:docs-sync PASS, check:api-typecheck
OK (289 frozen). Refs #12103, #11971

* docs(env): document RATE_LIMIT_EXECUTION_MAX_WAIT_MS (#12027 added it to .env.example only)

* fix(ci): round 5b — freeze the react-hooks compiler-rule violations, align 7 tests to merged contracts

No new ESLint warnings: the exact CI command (lint:json --max-warnings 0) reports 278
problems on the tip — 226 from eslint-plugin-react-hooks 7 compiler rules
(set-state-in-effect 167, immutability 36, refs/static-components/purity/
preserve-manual-memoization) that were masked until the lockfile change of
dfc84ba030 invalidated the ESLint cache, plus 46 no-explicit-any in
tests/unit/call-log-cap.test.ts (#12026). Velocity phase: frozen with
`eslint --suppress-all` (+668 suppressions); the 5 now-unused
`eslint-disable react-hooks/immutability` directives and one unused import removed.
Verified: lint:json --max-warnings 0 → 0 problems.

Tests aligned to contracts merged this afternoon (all reproduced red on the pure tip):
- providers-constants-split: 235 → 236 (Perplexity Agent, #12103)
- sse-auth: a forced pin outside allowedConnections now yields no credential
  instead of silently falling back (#12080)
- with-chat-admission-10786: withInjectionGuard(postHandler, { logger: null }) (#12117)
- hard-session-lease-bypass-inventory: classify src/app/api/oauth/codex/import/route.ts (#12116)
- usage-service-hardening: OpenCode Go official usage API shape (#12124)
- i18n placeholder parity: apiManager.restrictedToConnections rewritten as a plain
  ICU plural (`{count, plural, one {# connection} other {# connections}}`) in en,
  vi, pt-BR and the 40 __MISSING__ mirrors — the parity extractor counts every
  `{word}` including the old literal `{s}`

Refs #12103, #12080, #12117, #12116, #12124, #12026

* fix(ci): run the ESLint warnings job on the box with an 8 GB heap; reserved-prefix set 398 → 400

The cold full lint with the react-hooks 7 compiler rules is killed on the 7 GB hosted
runner with no message (status null → exit 1, JSON never written) — it only looked
green while the ESLint cache was warm. tests/unit/provider-node-reserved-prefix.test.ts
aligned to the two prefixes the afternoon batch registered (#12103).

* test(ci): document the lint-guard runner exception; #9147 event-loop gap 400 → 800 ms

quality-rail-gate-membership pinned lint-guard to ubuntu-latest; the cold full lint is
OOM-killed there, so the job now runs on omni-light with an 8 GB heap — the test keeps
fast-gates pinned and asserts the documented exception. With the catalog at 352
providers the hosted shards measure 410–633 ms gaps on 9147-catalog-eventloop-yield
(3 runs); 800 ms still fails a true pin. Re-tighten with the v4.0 catalog split.

* chore(quality): summarize the ESLint report on failure — a red lint:json printed nothing

--format json --output-file swallows every problem; a red 'No new ESLint warnings' job
gave zero output (three blind debugging rounds in #12144), and a killed process (OOM,
status null) was equally silent. On any non-zero exit the runner now prints the problem
count and the first 60 'file:line rule — message' lines from the report.

* chore(lint): freeze react-hooks/immutability for the 5 UI test harnesses in the suppressions file

The rule fires for these files in CI but not locally (compiler analysis divergence),
so the inline eslint-disable directives read as 'unused directive' warnings locally.
A suppressions entry is symmetric: suppressed where the rule fires, tolerated as
unpruned (--pass-on-unpruned-suppressions) where it does not. Found via the new
lint:json failure summary.
2026-08-30 18:03:47 -03:00

117 lines
6.2 KiB
TypeScript

// Characterization of the providers.ts catalog split (god-file decomposition): the host became a
// barrel that re-exports 10 data catalogs now living under constants/providers/*, and APIKEY is
// merged from 6 semantic family files (apikey/<family>.ts). Locks: the public surface (every catalog
// + helpers still exported), the spread-merge integrity (198 APIKEY entries, no loss/dup), and that
// load-time Zod validation still runs. Pure-data move → behavior must be identical.
// Count was 171 before obsolete provider removals (PR #6675: glhf/kluster/cablyai/inclusionai etc.,
// 171->167) plus #6126 (ClinePass dual-auth): the API-key-only APIKEY_PROVIDERS_GATEWAYS entry was
// removed as a duplicate now that clinepass is OAuth-primary (OAUTH_PROVIDERS.clinepass) with its
// BYOK path admitted through the DUAL_AUTH_APIKEY_PROVIDER_IDS gate instead (167->166), then the
// OpenVecta inference-gateway addition brought it back to 167, then #7246 (Chenzk API gateway)
// brought it to 168, then more additions brought it to 172, then #6650 (g4f.space no-key gateway:
// 5 new sub-path entries — g4f-groq/g4f-gemini/g4f-pollinations/g4f-ollama/g4f-nvidia) brought it
// to 177, then 2 more provider additions in the v3.8.49 cycle brought it to 179, the free-catalog
// expansion (#7840, navy) to 180, the Alibaba/Qwen Cloud regional additions (#7882) to 182, and
// #7887 (5 free-tier providers: ainative/aion/sealion/routeway/nara) to 187, then #8077 (clova-studio/
// internlm/ant-ling, all regional) to 190, then #8161 (sarvam/writer/plamo — writer in frontier-labs,
// sarvam+plamo in regional) to 193, then #8170 (inception/typhoon — inception in frontier-labs,
// typhoon in regional) to 195, then Firecrawl dual search+fetch under SEARCH_PROVIDERS.firecrawl
// (removed specialty-media duplicate) to 194, #8861 (Xiaomi MiMo Token Plan, regional) to 195, and
// the Cheaper Inference gateway (OSS-sponsor reseller, gateways family) to 198 (UnoRouter #9009),
// then later additions to 199; retiring GitHub Models brings it to 198.
// The v3.8.50 free-tier gateway waves (#9631 registry cycle, waves 2-5, #9210 phase 3) grew the
// gateways family to 228 measured on the tip; Puter retired (#10210) and chatanywhere restored
// (base-reds round 3, #9985) are both included in that measurement; Cursor API (specialty-media,
// #10729) brings it to 229; Token Kiosk (gateways, #10722) — merged in the same
// merge-train batch — independently bumped the gateways family too, landing at 231; Freebuff
// (gateways, #10531) brings it to 232. #8864 moves uncloseai (gateways family) into
// NOAUTH_PROVIDERS, dropping the APIKEY_PROVIDERS count to 231. Logfare (gateways, #10987) brings it back to 232.
// #11176 removes hackclub (gateways family), landing at 231.
// The v3.8.50 back-merge (f95b03d) adds Synthetic (specialty-media) and
// Kilo Gateway (gateways); #11434 adds volcengine-agent-plan and
// volcengine-coding-plan (regional family) — both land at 233.
// release/v3.8.51 adds Opper (gateways, #11629) and 1min.ai (gateways, #11631) — lands at 235;
// Perplexity Agent API (#12103) makes it 236.
import { test } from "node:test";
import assert from "node:assert/strict";
const P = await import("../../src/shared/constants/providers.ts");
test("barrel still exports every catalog + key helpers", () => {
for (const name of [
"NOAUTH_PROVIDERS",
"OAUTH_PROVIDERS",
"WEB_COOKIE_PROVIDERS",
"APIKEY_PROVIDERS",
"LOCAL_PROVIDERS",
"SEARCH_PROVIDERS",
"AUDIO_ONLY_PROVIDERS",
"UPSTREAM_PROXY_PROVIDERS",
"CLOUD_AGENT_PROVIDERS",
"SYSTEM_PROVIDERS",
"AI_PROVIDERS",
"ALIAS_TO_ID",
"ID_TO_ALIAS",
"getProviderById",
"getProviderByAlias",
"resolveProviderId",
]) {
assert.ok(name in P, `missing export: ${name}`);
}
});
test("APIKEY_PROVIDERS merges the 6 family files into 236 entries (no loss / no dup)", async () => {
const keys = Object.keys((P as Record<string, object>).APIKEY_PROVIDERS);
assert.equal(keys.length, 236);
assert.equal(new Set(keys).size, 236, "duplicate keys after spread-merge");
// the merged object's entry-count equals the sum of the 6 semantic family files; families are a
// strict partition (every provider in exactly one), so the sum must be exactly 236.
const families: [string, string][] = [
["gateways", "APIKEY_PROVIDERS_GATEWAYS"],
["frontier-labs", "APIKEY_PROVIDERS_FRONTIER"],
["inference-hosts", "APIKEY_PROVIDERS_INFERENCE"],
["enterprise-cloud", "APIKEY_PROVIDERS_ENTERPRISE"],
["regional", "APIKEY_PROVIDERS_REGIONAL"],
["specialty-media", "APIKEY_PROVIDERS_SPECIALTY"],
];
let famTotal = 0;
const seen = new Set<string>();
for (const [file, exportName] of families) {
const mod = await import(`../../src/shared/constants/providers/apikey/${file}.ts`);
const famKeys = Object.keys(mod[exportName]);
famTotal += famKeys.length;
for (const k of famKeys) {
assert.ok(!seen.has(k), `provider ${k} appears in more than one family`);
seen.add(k);
}
}
assert.equal(famTotal, 236, "families must partition all 236 providers");
});
test("AI_PROVIDERS Proxy aggregates all sections; lookups resolve", () => {
const ai = (P as Record<string, Record<string, unknown>>).AI_PROVIDERS;
assert.ok(Object.keys(ai).length > 200);
assert.ok((P as Record<string, (id: string) => unknown>).getProviderById("openai"));
assert.ok((P as Record<string, (id: string) => unknown>).getProviderById("claude"));
// a moved catalog is reachable through the barrel re-export
assert.ok((P as Record<string, Record<string, unknown>>).APIKEY_PROVIDERS["openai"]);
});
test("each extracted data module is importable on its own", async () => {
const mods = [
["noauth", "NOAUTH_PROVIDERS"],
["oauth", "OAUTH_PROVIDERS"],
["web-cookie", "WEB_COOKIE_PROVIDERS"],
["local", "LOCAL_PROVIDERS"],
["search", "SEARCH_PROVIDERS"],
["audio", "AUDIO_ONLY_PROVIDERS"],
["upstream-proxy", "UPSTREAM_PROXY_PROVIDERS"],
["cloud-agent", "CLOUD_AGENT_PROVIDERS"],
["system", "SYSTEM_PROVIDERS"],
];
for (const [file, name] of mods) {
const m = await import(`../../src/shared/constants/providers/${file}.ts`);
assert.ok(m[name] && typeof m[name] === "object", `${file}.ts must export ${name}`);
}
});