fix(startup): rename reasoningControls.ts to avoid webpack casing collision (#6584)

This commit is contained in:
Diego Rodrigues de Sa e Souza 2026-07-09 04:47:10 -03:00
parent f4fb0d310b
commit c7fa7c40c6
8 changed files with 84 additions and 5 deletions

View file

@ -49,6 +49,7 @@ _Living section — bullets land here as PRs merge into `release/v3.8.47` (paral
- **fix(api):** editing any existing OpenAI Codex provider connection in the dashboard returned "Invalid request" and the edit could never be saved ([#6562](https://github.com/diegosouzapw/OmniRoute/issues/6562)) — `createProviderConnection()` (`src/lib/db/providers.ts`) auto-increments a new connection's `priority` to `MAX(priority)+1` per provider with no upper bound, and OAuth-imported connections (Codex `codex-auth/import` / `import-bulk`, up to 50 accounts per call, callable repeatedly — the standard Codex bulk-account-rotation workflow) never pass through `createProviderSchema`'s Zod validation at all, so nothing ever capped that value; `EditConnectionModal`'s `handleSubmit` always resends the connection's current `priority` unchanged on every save, and `updateProviderConnectionSchema` capped `priority`/`globalPriority` at `max(100)` — a UI-only ceiling the create path never enforced — so the first edit of any connection whose priority had already grown past 100 (routine once a Codex account count exceeds 100) failed validation regardless of which field the user actually changed. Raised the ceiling to `max(100_000)` on both fields — still bounded (a genuinely out-of-range value is still rejected), just wide enough to accept priorities the app itself already produces. Regression guard: `tests/unit/codex-connection-edit-6562.test.ts` (a Codex OAuth connection whose priority already exceeds the old 100 cap now validates + persists on edit; a control payload with a still-genuinely-invalid priority is still rejected with "Invalid request").
- **mimocode**: rotate accounts on MiMoCode's rate-limit-style 400s (body-classified) instead of failing on the first account; malformed 400s still fail fast with the real upstream error (#6648 — thanks @pizzav-xyz)
- **fix(cli):** compression CLI REST fallback now reads/writes the canonical `defaultMode` field (surfaced as `strategy`) instead of a nonexistent `engine` key, and table output renders nested objects as JSON instead of `[object Object]` (#6571 — thanks @charleszolot)
- **fix(startup):** webpack build broke on case-insensitive filesystems (macOS APFS default, Windows) with a casing-collision warning plus "not exported" errors in `StudioConfigPane.tsx`/`ChatTab.tsx` (#6584) — `src/app/(dashboard)/dashboard/playground/components/ReasoningControls.tsx` (the component) and `reasoningControls.ts` (the utils module) shared the same lower-cased stem in the same directory, and two importers used the extensionless form `from "./reasoningControls"`, the exact resolution path that becomes ambiguous once casing is folded. Renamed the utils module to `reasoningControlUtils.ts` (no collision) and updated the 3 import sites. Regression guard: `tests/unit/case-collision-6584.test.ts` (scans `src/`/`open-sse/` for any same-directory, case-only filename collision). (#6584)
### 📝 Maintenance

View file

@ -7,7 +7,7 @@
// (fallback to the canonical vocabulary), resolved by `resolveReasoningControls`.
import type { PlaygroundParams } from "./ParamSliders";
import type { ReasoningControlSpec } from "./reasoningControls";
import type { ReasoningControlSpec } from "./reasoningControlUtils";
interface ReasoningControlsProps {
spec: ReasoningControlSpec;

View file

@ -20,7 +20,7 @@ import ReasoningControls from "./ReasoningControls";
import {
resolveReasoningControls,
type ReasoningControlSpec,
} from "./reasoningControls";
} from "./reasoningControlUtils";
export interface ConfigState {
endpoint: PlaygroundEndpoint;

View file

@ -9,7 +9,7 @@ import { useStreamMetrics } from "../../hooks/useStreamMetrics";
import { getModelPricing } from "@/lib/playground/types";
import type { ConfigState } from "../StudioConfigPane";
import type { StreamMetrics } from "@/shared/schemas/playground";
import { buildReasoningRequestFields } from "../reasoningControls";
import { buildReasoningRequestFields } from "../reasoningControlUtils";
interface Message {
role: "system" | "user" | "assistant";

View file

@ -2,7 +2,7 @@
import { useState, useEffect, useCallback, useMemo } from "react";
import { compareTr } from "@/shared/utils/turkishText";
import type { ModelReasoningCapabilities } from "@/app/(dashboard)/dashboard/playground/components/reasoningControls";
import type { ModelReasoningCapabilities } from "@/app/(dashboard)/dashboard/playground/components/reasoningControlUtils";
/**
* Prefix-based formatmodel matching, used to pick a smart default

View file

@ -0,0 +1,78 @@
import { test } from "node:test";
import assert from "node:assert/strict";
import fs from "node:fs";
import path from "node:path";
import { fileURLToPath } from "node:url";
const __dirname = path.dirname(fileURLToPath(import.meta.url));
// Regression guard for issue #6584: webpack (OMNIROUTE_USE_TURBOPACK=0) build breaks on
// case-insensitive filesystems (macOS APFS default, Windows) because two source
// files in the same directory differ only by casing:
// src/app/(dashboard)/dashboard/playground/components/ReasoningControls.tsx
// src/app/(dashboard)/dashboard/playground/components/reasoningControls.ts
// On a case-sensitive FS (Linux, this sandbox) both files resolve fine, so this
// test does not literally reproduce the webpack warning/build error. Instead it
// encodes the same-directory case-collision root cause as a filesystem-level
// invariant check that fails deterministically on ANY OS: two distinct files
// whose relative path is identical after lower-casing (stem, ignoring extension,
// since extensionless imports are what collide) must not coexist in the same
// directory tree.
const ROOT = path.resolve(__dirname, "..", "..");
const SCAN_DIRS = ["src", "open-sse"];
const EXTENSIONS = new Set([".ts", ".tsx", ".js", ".jsx"]);
const IGNORE_DIR_NAMES = new Set(["node_modules", ".next", "dist", "build", ".git"]);
function walk(dir: string, out: string[]): void {
const entries = fs.readdirSync(dir, { withFileTypes: true });
for (const entry of entries) {
if (IGNORE_DIR_NAMES.has(entry.name)) continue;
const full = path.join(dir, entry.name);
if (entry.isDirectory()) {
walk(full, out);
} else if (EXTENSIONS.has(path.extname(entry.name))) {
out.push(full);
}
}
}
test("#6584: no two source files in the same directory differ only by casing", () => {
const files: string[] = [];
for (const d of SCAN_DIRS) {
const abs = path.join(ROOT, d);
if (fs.existsSync(abs)) walk(abs, files);
}
// Group by (directory, lower-cased basename WITHOUT extension). Extensionless
// imports (e.g. `from "./reasoningControls"`) are resolved by webpack/Node's
// module resolver trying several extensions; on a case-insensitive filesystem
// that resolution collapses "ReasoningControls.tsx" and "reasoningControls.ts"
// into the same candidate, so the stem — not the full filename — is what must
// stay unique per directory regardless of extension.
const byDirLowerStem = new Map<string, string[]>();
for (const f of files) {
const dir = path.dirname(f);
const ext = path.extname(f);
const stem = path.basename(f, ext);
const lowerStem = stem.toLowerCase();
const key = `${dir}::${lowerStem}`;
const arr = byDirLowerStem.get(key) ?? [];
arr.push(f);
byDirLowerStem.set(key, arr);
}
const collisions: string[][] = [];
for (const arr of byDirLowerStem.values()) {
const distinctStems = new Set(arr.map((f) => path.basename(f, path.extname(f))));
if (distinctStems.size > 1) {
collisions.push(arr);
}
}
assert.deepEqual(
collisions,
[],
`Found case-only filename collisions (breaks webpack builds on case-insensitive filesystems): ${JSON.stringify(collisions, null, 2)}`
);
});

View file

@ -4,7 +4,7 @@ import {
resolveReasoningControls,
buildReasoningRequestFields,
type ReasoningControlSpec,
} from "../../src/app/(dashboard)/dashboard/playground/components/reasoningControls.ts";
} from "../../src/app/(dashboard)/dashboard/playground/components/reasoningControlUtils.ts";
import { CANONICAL_EFFORT_VALUES } from "../../src/shared/reasoning/effortStandardization.ts";
// #6241: the Playground effort selector + thinking toggle read a model's `supportsThinking` /