From d9be8136179fe697731c1a3fda3702b0d9ef7009 Mon Sep 17 00:00:00 2001 From: Markus Hartung Date: Wed, 2 Sep 2026 03:56:07 -0300 Subject: [PATCH] fix(cli): ship config/i18n.json in the npm package so OMNIROUTE_LANG aliases work on global installs; pin regional/alias resolution tests --- package.json | 1 + scripts/build/pack-artifact-policy.ts | 9 +++++++++ tests/unit/cli-i18n-catalog.test.ts | 19 ++++++++++++++++++ tests/unit/pack-artifact-policy.test.ts | 26 +++++++++++++++++++++++++ 4 files changed, 55 insertions(+) diff --git a/package.json b/package.json index cbb09d1ed8..bf1dd62a96 100644 --- a/package.json +++ b/package.json @@ -21,6 +21,7 @@ "src/sse/", "src/types/", ".env.example", + "config/i18n.json", "scripts/build/postinstall.mjs", "scripts/build/fixTlsClientNodeBinary.mjs", "scripts/build/fixPlaywrightAndroid.mjs", diff --git a/scripts/build/pack-artifact-policy.ts b/scripts/build/pack-artifact-policy.ts index 2358399fa6..a6037440a3 100644 --- a/scripts/build/pack-artifact-policy.ts +++ b/scripts/build/pack-artifact-policy.ts @@ -119,6 +119,11 @@ export const PACK_ARTIFACT_ROOT_ALLOWED_EXACT_PATHS: string[] = [ "bin/restore-policies.sh", "bin/rollback.sh", "bin/snapshot-data.sh", + // Locale source of truth read at runtime by bin/cli/i18n.mjs (OMNIROUTE_LANG alias + // resolution: uk → uk-UA, fil/tl → phi, zh-hk/zh-mo/zh-hant → zh-TW) and by + // bin/cli/commands/config.mjs (`config lang list`). Shipped via package.json "files"; + // without it the published CLI cannot resolve aliases and `config lang list` is empty. + "config/i18n.json", "open-sse/mcp-server/README.md", "open-sse/mcp-server/audit.ts", "open-sse/mcp-server/httpTransport.ts", @@ -228,6 +233,10 @@ export const PACK_ARTIFACT_REQUIRED_PATHS: string[] = [ // or the CLI fails to boot — list them REQUIRED so a regression is loud. "bin/aliasResolver.mjs", "bin/aliasResolverHook.mjs", + // Locale aliases consumed by bin/cli/i18n.mjs at runtime. config/ is not an allowlist + // PREFIX, so a vanished file would never fail the unexpected-paths check — list it + // REQUIRED so the tarball can never silently lose it again (#7065 class). + "config/i18n.json", "package.json", "scripts/build/native-binary-compat.mjs", "scripts/build/postinstall.mjs", diff --git a/tests/unit/cli-i18n-catalog.test.ts b/tests/unit/cli-i18n-catalog.test.ts index af83f12fcf..3a8224c7dd 100644 --- a/tests/unit/cli-i18n-catalog.test.ts +++ b/tests/unit/cli-i18n-catalog.test.ts @@ -126,6 +126,25 @@ test("i18n.mjs resolve alias do config: OMNIROUTE_LANG=uk → uk-UA, fil_PH.UTF- resetForTests(); }); +test("i18n.mjs resolve base→regional e aliases: zh → zh-CN, zh-hant/zh_HK → zh-TW, UK → uk-UA", async () => { + const { resetForTests, detectLocale } = await import("../../bin/cli/i18n.mjs"); + const orig = process.env.OMNIROUTE_LANG; + const cases: Array<[string, string]> = [ + ["zh", "zh-CN"], // bare base language → first regional catalog declared in config/i18n.json + ["zh-hant", "zh-TW"], // alias family declared on zh-TW + ["zh_HK.UTF-8", "zh-TW"], // POSIX form of the zh-hk alias (charset stripped, _ → -) + ["UK", "uk-UA"], // upper-case input canonicalized through the alias map + ]; + for (const [input, expected] of cases) { + process.env.OMNIROUTE_LANG = input; + resetForTests(); + assert.equal(detectLocale(), expected, `OMNIROUTE_LANG=${input}`); + } + if (orig === undefined) delete process.env.OMNIROUTE_LANG; + else process.env.OMNIROUTE_LANG = orig; + resetForTests(); +}); + test("t() interpola variáveis {var}", async () => { const { resetForTests, t, setLocale } = await import("../../bin/cli/i18n.mjs"); resetForTests(); diff --git a/tests/unit/pack-artifact-policy.test.ts b/tests/unit/pack-artifact-policy.test.ts index aeea454fad..c7c013e584 100644 --- a/tests/unit/pack-artifact-policy.test.ts +++ b/tests/unit/pack-artifact-policy.test.ts @@ -225,6 +225,31 @@ test("setupPolyfill.ts is allowed in the tarball (bin/omniroute.mjs imports it a assert.deepEqual(unexpectedPaths, []); }); +test("config/i18n.json ships in the tarball: allowed, required, and in package.json files[]", () => { + // Locale source of truth read at runtime by bin/cli/i18n.mjs (OMNIROUTE_LANG alias + // resolution, e.g. uk → uk-UA / fil → phi) and bin/cli/commands/config.mjs + // (`config lang list`). package.json "files" never shipped config/, so the published + // CLI silently fell back to en for every alias — pin all three layers so the file can + // never drop out of the tarball again. + const configPath = "config/i18n.json"; + + const unexpectedPaths = findUnexpectedArtifactPaths([configPath], { + exactPaths: PACK_ARTIFACT_ALLOWED_EXACT_PATHS, + prefixPaths: PACK_ARTIFACT_ALLOWED_PATH_PREFIXES, + }); + assert.deepEqual(unexpectedPaths, [], `${configPath} must be allowed in the tarball`); + + assert.ok( + PACK_ARTIFACT_REQUIRED_PATHS.includes(configPath), + `${configPath} must be a required tarball path (check:pack-artifact regression guard)` + ); + + const files: string[] = JSON.parse( + readFileSync(new URL("../../package.json", import.meta.url), "utf8") + ).files; + assert.ok(files.includes(configPath), `package.json "files" must list ${configPath}`); +}); + test("findMissingArtifactPaths flags missing root runtime files in the tarball", () => { const missingPaths = findMissingArtifactPaths( [ @@ -253,6 +278,7 @@ test("findMissingArtifactPaths flags missing root runtime files in the tarball", "bin/mcp-server.mjs", "bin/mcpStdioConsoleGuard.mjs", "bin/nodeRuntimeSupport.mjs", + "config/i18n.json", "dist/head-response-guard.cjs", "dist/http-method-guard.cjs", "dist/main-server-timeouts.mjs",