@the-i18n-kit/cli 7.0.0 → 8.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/bin.js +1 -1
- package/dist/{cli-waEjbUM1.js → cli-BTMcWXGs.js} +3 -3
- package/dist/{cli-waEjbUM1.js.map → cli-BTMcWXGs.js.map} +1 -1
- package/dist/{define-config-e0B0VHq7.d.ts → define-config-BpdVaEVR.d.ts} +1 -1
- package/dist/{define-config-ChY3jk6K.d.ts → define-config-ZRZw5PWy.d.ts} +15 -3
- package/dist/define-config-ZRZw5PWy.d.ts.map +1 -0
- package/dist/define-config.d.ts +1 -1
- package/dist/{descriptors-10sgyqEs.js → descriptors-Bo5UM031.js} +23 -6
- package/dist/{descriptors-10sgyqEs.js.map → descriptors-Bo5UM031.js.map} +1 -1
- package/dist/{detector-DtFY4qm3.js → detector-B4z_RZde.js} +2 -2
- package/dist/{detector-DtFY4qm3.js.map → detector-B4z_RZde.js.map} +1 -1
- package/dist/{detector-BJTkyhQ0.js → detector-DTfFbwSU.js} +2 -2
- package/dist/{index-CdjJ71Ag.d.ts → index-CWFWYHf5.d.ts} +76 -50
- package/dist/index-CWFWYHf5.d.ts.map +1 -0
- package/dist/index.d.ts +1 -1
- package/dist/index.js +5 -5
- package/dist/operations-CKHEmLNJ.js +8 -0
- package/dist/{operations-CNqEBD__.js → operations-Daf2Ommm.js} +183 -46
- package/dist/operations-Daf2Ommm.js.map +1 -0
- package/dist/{project-config-C3ao4Uii.js → project-config-eLR0J377.js} +6 -2
- package/dist/project-config-eLR0J377.js.map +1 -0
- package/package.json +1 -1
- package/dist/define-config-ChY3jk6K.d.ts.map +0 -1
- package/dist/index-CdjJ71Ag.d.ts.map +0 -1
- package/dist/operations-CNqEBD__.js.map +0 -1
- package/dist/operations-D7IMu_xY.js +0 -8
- package/dist/project-config-C3ao4Uii.js.map +0 -1
package/dist/bin.js
CHANGED
|
@@ -3,7 +3,7 @@ import { t as guardStdout } from "./stdout-guard-sJHFWVem.js";
|
|
|
3
3
|
//#region src/bin.ts
|
|
4
4
|
const args = process.argv.slice(2);
|
|
5
5
|
if (!(args.includes("--help") || args.includes("-h") || args.includes("--version") || args.includes("-v"))) guardStdout();
|
|
6
|
-
const { runCli } = await import("./cli-
|
|
6
|
+
const { runCli } = await import("./cli-BTMcWXGs.js");
|
|
7
7
|
await runCli();
|
|
8
8
|
//#endregion
|
|
9
9
|
export {};
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { n as writeResult } from "./stdout-guard-sJHFWVem.js";
|
|
2
2
|
import { i as toErrorMessage } from "./errors-coI1dhw1.js";
|
|
3
3
|
import { a as resolveProviderBaseUrl, i as createTranslateFn, t as BASE_URL_ENV } from "./providers-BbVPelvp.js";
|
|
4
|
-
import { i as log, n as loadProjectConfig } from "./project-config-
|
|
5
|
-
import { i as divertToReport, t as descriptors } from "./descriptors-
|
|
4
|
+
import { i as log, n as loadProjectConfig } from "./project-config-eLR0J377.js";
|
|
5
|
+
import { i as divertToReport, t as descriptors } from "./descriptors-Bo5UM031.js";
|
|
6
6
|
import { createRequire } from "node:module";
|
|
7
7
|
import { defineCommand, runCommand, runMain } from "citty";
|
|
8
8
|
//#region src/commands/_shared.ts
|
|
@@ -387,4 +387,4 @@ async function runCli() {
|
|
|
387
387
|
//#endregion
|
|
388
388
|
export { runCli };
|
|
389
389
|
|
|
390
|
-
//# sourceMappingURL=cli-
|
|
390
|
+
//# sourceMappingURL=cli-BTMcWXGs.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"cli-waEjbUM1.js","names":["commands","allCommands"],"sources":["../src/commands/_shared.ts","../src/commands/index.ts","../src/cli.ts"],"sourcesContent":["import { defineCommand } from 'citty'\nimport type { CommandDef } from 'citty'\nimport { log } from '../utils/logger.js'\nimport { toErrorMessage } from '../utils/errors.js'\nimport { writeResult } from '../utils/stdout-guard.js'\nimport { createTranslateFn, resolveProviderBaseUrl, BASE_URL_ENV } from '../llm/providers.js'\nimport type { LlmProvider } from '../llm/providers.js'\nimport type { TranslateFn } from '../core/types.js'\nimport { loadProjectConfig } from '../config/project-config.js'\nimport { divertToReport } from '../surface/report.js'\nimport type {\n AnyOperationDescriptor,\n FlaggedGateSpec,\n GateSpec,\n ParamSpec,\n Params,\n} from '../surface/types.js'\n\n// Declared with the descriptors, since a gate is something an operation\n// declares; re-exported here because this is where they are evaluated and\n// where the reference generator reads the exit codes from.\nexport type { GateSpec, FlaggedGateSpec, AlwaysOnGateSpec } from '../surface/types.js'\n\n/**\n * Factory for commands that call an operation and output its result.\n *\n * Reached through `commandFromDescriptor`, which is the only production caller:\n * it stays a separate function because output handling, gate evaluation and\n * exit codes are worth testing against a fixed result rather than against a\n * project on disk.\n */\nexport function createCommand(opts: {\n name: string\n description: string\n args?: Record<string, unknown>\n /**\n * CI gates this command evaluates. The factory reads the requesting flag off\n * args and evaluates every gate uniformly, so no command grows bespoke exit\n * logic. Declaring a flagged gate does not add its flag — pair each spec\n * with an entry in `args`. A spec without a flag is always evaluated.\n */\n gates?: GateSpec[]\n /**\n * Receives citty's parsed args. `any` is deliberate: the `args` above are\n * built at runtime from a descriptor's parameters and citty does not thread\n * that type through to the handler. The typed view of these arguments is the\n * descriptor's own `run`, which `commandFromDescriptor` calls with values\n * already coerced to the declared types.\n */\n // eslint-disable-next-line @typescript-eslint/no-explicit-any -- see above\n run: (args: any) => Promise<unknown>\n /**\n * Applied to the result after the gates have been evaluated. That order is\n * the point: a run that writes its result to a file still exits on the\n * counters that result carried, so `--output-file` and a gate flag combine\n * the way a pipeline expects.\n */\n // eslint-disable-next-line @typescript-eslint/no-explicit-any -- as above\n divert?: (result: unknown, args: any) => Promise<unknown>\n}): CommandDef {\n // Retained on the definition, not only consumed here: the generated CLI\n // reference has to state which commands fail a build on findings, and a gate\n // with no flag leaves no trace in the arg descriptions to infer it from.\n return withGates(defineCommand({\n meta: { name: opts.name, description: opts.description },\n args: { ...sharedArgs, ...(opts.args ?? {}) },\n async run({ args }) {\n try {\n const result = await opts.run(args)\n const decision = resolveExitCode(result, requestedGates(opts.gates ?? [], args), isTotalFailure(result))\n const output = opts.divert === undefined ? result : await opts.divert(result, args)\n outputResult(withGateReport(output, decision.tripped), args)\n // Assign only on a non-zero decision: a clean run must leave the exit\n // code exactly as it found it, as it did before gates existed.\n if (decision.code !== EXIT_SUCCESS) process.exitCode = decision.code\n } catch (error) {\n emitErrorResult(error, args)\n process.exitCode = EXIT_RUN_FAILED\n }\n },\n }) as CommandDef, opts.gates ?? [])\n}\n\n/**\n * A command definition carrying the gates its factory evaluates. Read\n * structurally by the reference generator rather than by importing this type,\n * which would make the docs build depend on the CLI's internals.\n */\ninterface GatedCommandDef extends CommandDef {\n gates: GateSpec[]\n}\n\nfunction withGates(def: CommandDef, gates: GateSpec[]): GatedCommandDef {\n return Object.assign(def, { gates })\n}\n\n/** The run succeeded and no gate tripped. */\nexport const EXIT_SUCCESS = 0\n/** The run itself failed — a bad API key, an unreadable project, a total translate failure. */\nexport const EXIT_RUN_FAILED = 1\n/** The run succeeded but a requested gate tripped — findings exist, the tool worked. */\nexport const EXIT_GATE_TRIPPED = 2\n\n/** A gate the caller asked for, with its threshold already resolved. */\nexport interface RequestedGate {\n name: string\n counter: string\n direction: 'above' | 'below'\n threshold: number\n}\n\n/** A gate that tripped, as reported in the result. */\nexport interface TrippedGate extends RequestedGate {\n observed: number\n}\n\nexport interface ExitDecision {\n code: typeof EXIT_SUCCESS | typeof EXIT_RUN_FAILED | typeof EXIT_GATE_TRIPPED\n tripped: TrippedGate[]\n}\n\n/**\n * Pure decision from an operation result plus the gates the caller requested\n * to an exit code. A failed run outranks a tripped gate — exit 1 wins over\n * exit 2 — and gates are not even consulted in that case, because counters\n * from a run that fell over say nothing about the project.\n */\nexport function resolveExitCode(\n result: unknown,\n gates: RequestedGate[],\n runFailed = false,\n): ExitDecision {\n if (runFailed) return { code: EXIT_RUN_FAILED, tripped: [] }\n\n const tripped: TrippedGate[] = []\n for (const gate of gates) {\n const observed = observedValue(result, gate.counter)\n if (observed === undefined || !trips(gate, observed)) continue\n tripped.push({ ...gate, observed })\n }\n\n return {\n code: tripped.length > 0 ? EXIT_GATE_TRIPPED : EXIT_SUCCESS,\n tripped,\n }\n}\n\n/**\n * Read a gate's counter off result.summary. Gates are evaluated before a result\n * is diverted to a file, so this sees the whole result — and it still reads the\n * { reportFile, summary } shape, which carries the same summary.\n * A missing or non-numeric counter yields undefined and never trips a gate.\n */\nfunction observedValue(result: unknown, counter: string): number | undefined {\n if (result === null || typeof result !== 'object') return undefined\n const summary = (result as Record<string, unknown>).summary\n if (summary === null || typeof summary !== 'object') return undefined\n const value = (summary as Record<string, unknown>)[counter]\n return typeof value === 'number' && Number.isFinite(value) ? value : undefined\n}\n\nfunction trips(gate: RequestedGate, observed: number): boolean {\n return gate.direction === 'below' ? observed < gate.threshold : observed > gate.threshold\n}\n\n/** Filter the declared gates down to the ones this invocation asked for. */\nfunction requestedGates(specs: GateSpec[], args: Record<string, unknown>): RequestedGate[] {\n const requested: RequestedGate[] = []\n for (const spec of specs) {\n const resolved = spec.flag === undefined\n ? { name: spec.name, threshold: spec.threshold }\n : { name: kebabCase(spec.flag), threshold: thresholdFromFlag(spec, args) }\n if (resolved.threshold === undefined || !Number.isFinite(resolved.threshold)) continue\n requested.push({\n name: resolved.name,\n counter: spec.counter,\n direction: spec.direction ?? 'above',\n threshold: resolved.threshold,\n })\n }\n return requested\n}\n\n/** The gate's threshold, or undefined when this invocation did not ask for it. */\nfunction thresholdFromFlag(spec: FlaggedGateSpec, args: Record<string, unknown>): number | undefined {\n const raw = args[spec.flag]\n if (raw === undefined || raw === null || raw === false || raw === '') return undefined\n return spec.threshold ?? Number(raw)\n}\n\n/**\n * Name a tripped gate by the flag that requested it, so the JSON says\n * \"fail-on-missing\" — what the user typed — rather than \"failOnMissing\".\n */\nfunction kebabCase(flag: string): string {\n return flag.replace(/[A-Z]/g, c => `-${c.toLowerCase()}`)\n}\n\n/**\n * Attach the gate report without disturbing the rest of the result: consumers\n * parsing today's shape keep working, and a run where nothing tripped is\n * byte-for-byte what it was before gates existed.\n */\nfunction withGateReport(result: unknown, tripped: TrippedGate[]): unknown {\n if (tripped.length === 0) return result\n if (result === null || typeof result !== 'object' || Array.isArray(result)) return result\n return { ...(result as Record<string, unknown>), gatesTripped: tripped }\n}\n\n/**\n * True when a run completed but achieved nothing: failures present and zero\n * successes. Covers translate-missing-style results (summary.totalFailed /\n * summary.totalTranslated) and translate-key-style results (top-level\n * failed[] / translated[]). Results without those fields are never a\n * total failure, so unrelated commands are unaffected.\n */\nexport function isTotalFailure(result: unknown): boolean {\n if (result === null || typeof result !== 'object') return false\n const r = result as Record<string, unknown>\n\n const summary = r.summary\n if (summary !== null && typeof summary === 'object') {\n const s = summary as Record<string, unknown>\n if (typeof s.totalFailed === 'number' && typeof s.totalTranslated === 'number') {\n return s.totalFailed > 0 && s.totalTranslated === 0\n }\n }\n\n if (Array.isArray(r.failed) && Array.isArray(r.translated)) {\n return r.failed.length > 0 && r.translated.length === 0\n }\n\n return false\n}\n\nconst sharedArgs = {\n projectDir: {\n type: 'string' as const,\n alias: 'd',\n description: 'Project directory (default: cwd)',\n },\n json: {\n type: 'boolean' as const,\n description: 'Output as JSON (default for non-TTY)',\n default: false,\n },\n}\n\n/** Output result — stdout always carries the result, machine-parseable when piped/--json */\nfunction outputResult(data: unknown, args: { json?: boolean }): void {\n const jsonMode = args.json || !process.stdout.isTTY\n if (\n !jsonMode &&\n data !== null &&\n typeof data === 'object' &&\n 'reportFile' in (data as Record<string, unknown>)\n ) {\n const { reportFile, ...rest } = data as Record<string, unknown>\n log.info(`Wrote report to: ${reportFile}`)\n writeResult(JSON.stringify(rest, null, 2) + '\\n')\n return\n }\n // JSON mode emits the full result (including reportFile when present) as pure JSON\n writeResult(JSON.stringify(data, null, 2) + '\\n')\n}\n\n/**\n * Failure output. In JSON mode stdout must still carry parseable JSON —\n * consumers pipe it into jq, and zero bytes is a parse error — so the\n * structured error object IS the result on stdout; the human-readable\n * message goes to stderr in every mode. Exit code stays non-zero (callers\n * set it).\n */\nexport function emitErrorResult(error: unknown, args: { json?: boolean }): void {\n log.error(toErrorMessage(error))\n const jsonMode = args.json || !process.stdout.isTTY\n if (!jsonMode) return\n const payload = { error: { code: errorCode(error), message: toErrorMessage(error) } }\n writeResult(JSON.stringify(payload, null, 2) + '\\n')\n}\n\n/** ToolError/FileIOError carry codes; Node errors expose e.g. ENOENT. */\nfunction errorCode(error: unknown): string {\n if (error instanceof Error) {\n const code = (error as { code?: unknown }).code\n if (typeof code === 'string') return code\n if (error.name === 'ConfigError') return 'CONFIG_ERROR'\n }\n return 'UNKNOWN_ERROR'\n}\n\n/**\n * The citty command for one descriptor: its flags, its gates, and a run that\n * hands the operation arguments of the types the descriptor declared.\n *\n * Everything a command needs is read off the descriptor, so a command is not a\n * file anyone writes — which is what keeps it from drifting from the tool that\n * runs the same operation.\n */\nexport function commandFromDescriptor(descriptor: AnyOperationDescriptor): CommandDef {\n const cli = descriptor.cli\n if (cli === null) {\n throw new Error(`Operation \"${descriptor.id}\" declares no CLI command.`)\n }\n\n return createCommand({\n name: cli.name,\n description: descriptor.description,\n args: cliArgs(descriptor.params),\n gates: descriptor.gates,\n run: async args => descriptor.run(operationArgs(descriptor.params, args), {\n surface: 'cli',\n // Resolved from the provider flags rather than passed through: which\n // flags select a backend is the CLI's business, not the operation's.\n translateFn: descriptor.usesTranslateFn === true\n ? await resolveProviderTranslateFn(args)\n : undefined,\n }),\n divert: async (result, args) =>\n divertToReport(result, descriptor, operationArgs(descriptor.params, args)),\n })\n}\n\n/** The citty `args` for a descriptor's parameters, skipping the CLI-hidden ones. */\nfunction cliArgs(params: Params): Record<string, unknown> {\n const args: Record<string, unknown> = {}\n for (const [name, spec] of Object.entries(params)) {\n if (spec.cli?.hidden === true) continue\n args[name] = {\n // citty parses booleans and strings. A list, a number and a JSON object\n // all arrive as one string and are converted in operationArgs.\n type: spec.type === 'boolean' ? 'boolean' : 'string',\n description: flagDescription(spec),\n ...(spec.required === true ? { required: true } : {}),\n ...(spec.default === undefined ? {} : { default: spec.default }),\n ...(spec.cli?.alias === undefined ? {} : { alias: [...toArray(spec.cli.alias)] }),\n ...(spec.enum === undefined ? {} : { valueHint: spec.enum.join('|') }),\n }\n }\n return args\n}\n\n/**\n * The description a flag prints. The declared one is written for a reader of\n * either surface, so how to spell a list or an object at a shell prompt is\n * added here rather than in every spec.\n */\nfunction flagDescription(spec: ParamSpec): string {\n if (spec.type === 'string[]') {\n return `${spec.description} Comma-separated${spec.allowAll === true ? ', or \"all\"' : ''}.`\n }\n if (spec.type === 'record') return `${spec.description} Pass it as JSON.`\n return spec.description\n}\n\n/**\n * citty's parsed args as the operation takes them: lists split, numbers and\n * JSON parsed, enums checked. `projectDir` comes along because every operation\n * accepts it without declaring it.\n */\nfunction operationArgs(params: Params, args: Record<string, unknown>): Record<string, unknown> {\n const operation: Record<string, unknown> = { projectDir: args.projectDir }\n for (const [name, spec] of Object.entries(params)) {\n if (spec.cli?.hidden === true) continue\n const value = coerce(name, spec, args[name])\n if (value !== undefined) operation[name] = value\n }\n return operation\n}\n\nfunction coerce(name: string, spec: ParamSpec, raw: unknown): unknown {\n if (spec.type === 'boolean') return typeof raw === 'boolean' ? raw : undefined\n\n const value = typeof raw === 'string' ? raw : undefined\n\n switch (spec.type) {\n case 'number':\n return value === undefined || value === '' ? undefined : toNumber(name, spec, value)\n case 'string[]':\n return toList(name, spec, value)\n case 'record':\n return value === undefined ? undefined : parseJsonArg(value, name)\n default:\n return toEnumChecked(name, spec, value)\n }\n}\n\nfunction toEnumChecked(name: string, spec: ParamSpec, value: string | undefined): string | undefined {\n if (spec.enum === undefined) return value\n // An empty value is an unset flag, not a value to reject: a shell produces\n // one routinely from an unset variable.\n if (value === undefined || value === '') return undefined\n if (!spec.enum.includes(value)) {\n throw new Error(`Invalid --${name} value: \"${value}\". Must be one of: ${spec.enum.join(', ')}`)\n }\n return value\n}\n\nfunction toNumber(name: string, spec: ParamSpec, raw: string): number {\n const value = Number(raw)\n const wellFormed = Number.isFinite(value)\n && (spec.integer !== true || (Number.isInteger(value) && String(value) === raw.trim()))\n && (spec.min === undefined || value >= spec.min)\n if (!wellFormed) {\n throw new Error(`Invalid --${name}: \"${raw}\". Must be ${numberRequirement(spec)}`)\n }\n return value\n}\n\nfunction numberRequirement(spec: ParamSpec): string {\n if (spec.integer !== true) {\n return spec.min === undefined ? 'a number' : `a number of at least ${spec.min}`\n }\n if (spec.min === 1) return 'a positive integer'\n return spec.min === undefined ? 'an integer' : `an integer of at least ${spec.min}`\n}\n\nfunction toList(name: string, spec: ParamSpec, raw: string | undefined): string[] | 'all' | undefined {\n if (spec.allowAll === true && raw === 'all') return 'all'\n\n const list = splitList(raw)\n // A list flag that was passed and yielded nothing is a mistake, not a request\n // for the default — and a required list with nothing in it has no meaning.\n const empty = list === undefined || list.length === 0\n if (empty && (spec.required === true || (raw !== undefined && raw !== ''))) {\n throw new Error(`No ${name} provided. Pass a comma-separated list via --${name}.`)\n }\n return list\n}\n\nfunction toArray(value: string | readonly string[]): readonly string[] {\n return typeof value === 'string' ? [value] : value\n}\n\n/**\n * Build a TranslateFn from the provider flags, or undefined when no provider\n * was given (agent mode). The base URL resolves flag > env > project config;\n * the config file is only read when neither of the first two is set, so a\n * fully-flagged invocation never depends on config discovery.\n */\nexport async function resolveProviderTranslateFn(args: {\n provider?: string\n model?: string\n apiKey?: string\n baseUrl?: string\n projectDir?: string\n}): Promise<TranslateFn | undefined> {\n if (!args.provider) return undefined\n if (!args.model) {\n throw new Error('--model is required when --provider is set')\n }\n\n let baseUrl = resolveProviderBaseUrl({ flag: args.baseUrl, env: process.env[BASE_URL_ENV] })\n if (!baseUrl) {\n const projectConfig = await loadProjectConfig(args.projectDir ?? process.cwd())\n baseUrl = resolveProviderBaseUrl({ config: projectConfig?.providerBaseUrl })\n }\n if (baseUrl) log.debug(`Provider base URL: ${redactBaseUrl(baseUrl)}`)\n\n return createTranslateFn({\n provider: args.provider as LlmProvider,\n model: args.model,\n apiKey: args.apiKey,\n baseUrl,\n })\n}\n\n/**\n * Strip anything secret-shaped from a base URL before it reaches a log.\n * Gateways are routinely addressed as https://user:pass@host or with the key\n * in a query parameter, so keep only origin and path.\n */\nexport function redactBaseUrl(raw: string): string {\n let url: URL\n try {\n url = new URL(raw)\n } catch {\n return '<unparseable base URL>'\n }\n const credentials = url.username || url.password ? '<redacted>@' : ''\n const query = url.search || url.hash ? ' (query redacted)' : ''\n return `${url.protocol}//${credentials}${url.host}${url.pathname}${query}`\n}\n\n/** Split a comma-separated string into a trimmed array, or return undefined */\nfunction splitList(val: string | undefined): string[] | undefined {\n if (!val) return undefined\n return val.split(',').map(s => s.trim()).filter(Boolean)\n}\n\n/** Parse a JSON string with a user-friendly error */\nfunction parseJsonArg<T = Record<string, Record<string, string>>>(\n value: string,\n argName: string,\n): T {\n try {\n return JSON.parse(value) as T\n } catch (err) {\n const detail = err instanceof SyntaxError ? err.message : String(err)\n throw new Error(`Invalid JSON in --${argName}: ${detail}`)\n }\n}\n","import type { CommandDef } from 'citty'\nimport { commandFromDescriptor } from './_shared.js'\nimport { descriptors } from '../surface/descriptors.js'\n\n/**\n * A registered command: nothing but its lazy loader.\n *\n * There is no way to register a command the CLI does not expose. The flag that\n * allowed it kept producing documented commands that printed \"Unknown command\"\n * when anyone tried them, and every operation an MCP tool covers has to be\n * reachable from a terminal or the two surfaces are not the same tool.\n *\n * The commands themselves are built from the operation descriptors, so the\n * registry states no name, no flag and no description of its own. The loader\n * stays because callers hold it: citty resolves subcommands through it, and the\n * reference generator awaits it. It also keeps the definition's identity\n * stable, which is how the generator recognises two names for one command.\n */\nexport interface CommandEntry {\n load: () => Promise<CommandDef>\n}\n\nfunction entry(build: () => CommandDef): CommandEntry {\n let built: CommandDef | undefined\n return { load: async () => (built ??= build()) }\n}\n\nfunction registry(): Record<string, CommandEntry> {\n const commands: Record<string, CommandEntry> = {}\n for (const descriptor of descriptors) {\n const cli = descriptor.cli\n if (cli === null) continue\n commands[cli.name] = entry(() => commandFromDescriptor(descriptor))\n }\n return commands\n}\n\nexport const commands: Record<string, CommandEntry> = registry()\n","import { createRequire } from 'node:module'\nimport { defineCommand, runCommand, runMain } from 'citty'\nimport { commands as allCommands } from './commands/index.js'\nimport type { CommandEntry } from './commands/index.js'\nimport { emitErrorResult } from './commands/_shared.js'\n\n// Every registered command is a subcommand. There is no filter here, and no\n// second list to fall out of step with the registry.\nconst commands = Object.fromEntries(\n Object.entries(allCommands as Record<string, CommandEntry>)\n .map(([name, entry]) => [name, entry.load]),\n)\n\nconst require = createRequire(import.meta.url)\nconst { version, description } = require('../package.json') as { version: string; description: string }\n\nconst main = defineCommand({\n meta: {\n name: 'the-i18n-cli',\n version,\n description,\n },\n subCommands: commands,\n})\n\nexport async function runCli(): Promise<void> {\n const rawArgs = process.argv.slice(2)\n\n // Let citty handle --help and --version natively (pretty-printed usage)\n if (rawArgs.includes('--help') || rawArgs.includes('-h')\n || rawArgs.includes('--version') || rawArgs.includes('-v')) {\n await runMain(main)\n return\n }\n\n // For normal execution, use runCommand so we control error output.\n // Command run() errors are handled inside createCommand; this catch only\n // sees pre-run failures (unknown command, argument parsing) — those must\n // also keep stdout parseable in JSON mode.\n try {\n await runCommand(main, { rawArgs })\n } catch (error: unknown) {\n emitErrorResult(error, { json: rawArgs.includes('--json') })\n process.exitCode = 1\n }\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"cli-BTMcWXGs.js","names":["commands","allCommands"],"sources":["../src/commands/_shared.ts","../src/commands/index.ts","../src/cli.ts"],"sourcesContent":["import { defineCommand } from 'citty'\nimport type { CommandDef } from 'citty'\nimport { log } from '../utils/logger.js'\nimport { toErrorMessage } from '../utils/errors.js'\nimport { writeResult } from '../utils/stdout-guard.js'\nimport { createTranslateFn, resolveProviderBaseUrl, BASE_URL_ENV } from '../llm/providers.js'\nimport type { LlmProvider } from '../llm/providers.js'\nimport type { TranslateFn } from '../core/types.js'\nimport { loadProjectConfig } from '../config/project-config.js'\nimport { divertToReport } from '../surface/report.js'\nimport type {\n AnyOperationDescriptor,\n FlaggedGateSpec,\n GateSpec,\n ParamSpec,\n Params,\n} from '../surface/types.js'\n\n// Declared with the descriptors, since a gate is something an operation\n// declares; re-exported here because this is where they are evaluated and\n// where the reference generator reads the exit codes from.\nexport type { GateSpec, FlaggedGateSpec, AlwaysOnGateSpec } from '../surface/types.js'\n\n/**\n * Factory for commands that call an operation and output its result.\n *\n * Reached through `commandFromDescriptor`, which is the only production caller:\n * it stays a separate function because output handling, gate evaluation and\n * exit codes are worth testing against a fixed result rather than against a\n * project on disk.\n */\nexport function createCommand(opts: {\n name: string\n description: string\n args?: Record<string, unknown>\n /**\n * CI gates this command evaluates. The factory reads the requesting flag off\n * args and evaluates every gate uniformly, so no command grows bespoke exit\n * logic. Declaring a flagged gate does not add its flag — pair each spec\n * with an entry in `args`. A spec without a flag is always evaluated.\n */\n gates?: GateSpec[]\n /**\n * Receives citty's parsed args. `any` is deliberate: the `args` above are\n * built at runtime from a descriptor's parameters and citty does not thread\n * that type through to the handler. The typed view of these arguments is the\n * descriptor's own `run`, which `commandFromDescriptor` calls with values\n * already coerced to the declared types.\n */\n // eslint-disable-next-line @typescript-eslint/no-explicit-any -- see above\n run: (args: any) => Promise<unknown>\n /**\n * Applied to the result after the gates have been evaluated. That order is\n * the point: a run that writes its result to a file still exits on the\n * counters that result carried, so `--output-file` and a gate flag combine\n * the way a pipeline expects.\n */\n // eslint-disable-next-line @typescript-eslint/no-explicit-any -- as above\n divert?: (result: unknown, args: any) => Promise<unknown>\n}): CommandDef {\n // Retained on the definition, not only consumed here: the generated CLI\n // reference has to state which commands fail a build on findings, and a gate\n // with no flag leaves no trace in the arg descriptions to infer it from.\n return withGates(defineCommand({\n meta: { name: opts.name, description: opts.description },\n args: { ...sharedArgs, ...(opts.args ?? {}) },\n async run({ args }) {\n try {\n const result = await opts.run(args)\n const decision = resolveExitCode(result, requestedGates(opts.gates ?? [], args), isTotalFailure(result))\n const output = opts.divert === undefined ? result : await opts.divert(result, args)\n outputResult(withGateReport(output, decision.tripped), args)\n // Assign only on a non-zero decision: a clean run must leave the exit\n // code exactly as it found it, as it did before gates existed.\n if (decision.code !== EXIT_SUCCESS) process.exitCode = decision.code\n } catch (error) {\n emitErrorResult(error, args)\n process.exitCode = EXIT_RUN_FAILED\n }\n },\n }) as CommandDef, opts.gates ?? [])\n}\n\n/**\n * A command definition carrying the gates its factory evaluates. Read\n * structurally by the reference generator rather than by importing this type,\n * which would make the docs build depend on the CLI's internals.\n */\ninterface GatedCommandDef extends CommandDef {\n gates: GateSpec[]\n}\n\nfunction withGates(def: CommandDef, gates: GateSpec[]): GatedCommandDef {\n return Object.assign(def, { gates })\n}\n\n/** The run succeeded and no gate tripped. */\nexport const EXIT_SUCCESS = 0\n/** The run itself failed — a bad API key, an unreadable project, a total translate failure. */\nexport const EXIT_RUN_FAILED = 1\n/** The run succeeded but a requested gate tripped — findings exist, the tool worked. */\nexport const EXIT_GATE_TRIPPED = 2\n\n/** A gate the caller asked for, with its threshold already resolved. */\nexport interface RequestedGate {\n name: string\n counter: string\n direction: 'above' | 'below'\n threshold: number\n}\n\n/** A gate that tripped, as reported in the result. */\nexport interface TrippedGate extends RequestedGate {\n observed: number\n}\n\nexport interface ExitDecision {\n code: typeof EXIT_SUCCESS | typeof EXIT_RUN_FAILED | typeof EXIT_GATE_TRIPPED\n tripped: TrippedGate[]\n}\n\n/**\n * Pure decision from an operation result plus the gates the caller requested\n * to an exit code. A failed run outranks a tripped gate — exit 1 wins over\n * exit 2 — and gates are not even consulted in that case, because counters\n * from a run that fell over say nothing about the project.\n */\nexport function resolveExitCode(\n result: unknown,\n gates: RequestedGate[],\n runFailed = false,\n): ExitDecision {\n if (runFailed) return { code: EXIT_RUN_FAILED, tripped: [] }\n\n const tripped: TrippedGate[] = []\n for (const gate of gates) {\n const observed = observedValue(result, gate.counter)\n if (observed === undefined || !trips(gate, observed)) continue\n tripped.push({ ...gate, observed })\n }\n\n return {\n code: tripped.length > 0 ? EXIT_GATE_TRIPPED : EXIT_SUCCESS,\n tripped,\n }\n}\n\n/**\n * Read a gate's counter off result.summary. Gates are evaluated before a result\n * is diverted to a file, so this sees the whole result — and it still reads the\n * { reportFile, summary } shape, which carries the same summary.\n * A missing or non-numeric counter yields undefined and never trips a gate.\n */\nfunction observedValue(result: unknown, counter: string): number | undefined {\n if (result === null || typeof result !== 'object') return undefined\n const summary = (result as Record<string, unknown>).summary\n if (summary === null || typeof summary !== 'object') return undefined\n const value = (summary as Record<string, unknown>)[counter]\n return typeof value === 'number' && Number.isFinite(value) ? value : undefined\n}\n\nfunction trips(gate: RequestedGate, observed: number): boolean {\n return gate.direction === 'below' ? observed < gate.threshold : observed > gate.threshold\n}\n\n/** Filter the declared gates down to the ones this invocation asked for. */\nfunction requestedGates(specs: GateSpec[], args: Record<string, unknown>): RequestedGate[] {\n const requested: RequestedGate[] = []\n for (const spec of specs) {\n const resolved = spec.flag === undefined\n ? { name: spec.name, threshold: spec.threshold }\n : { name: kebabCase(spec.flag), threshold: thresholdFromFlag(spec, args) }\n if (resolved.threshold === undefined || !Number.isFinite(resolved.threshold)) continue\n requested.push({\n name: resolved.name,\n counter: spec.counter,\n direction: spec.direction ?? 'above',\n threshold: resolved.threshold,\n })\n }\n return requested\n}\n\n/** The gate's threshold, or undefined when this invocation did not ask for it. */\nfunction thresholdFromFlag(spec: FlaggedGateSpec, args: Record<string, unknown>): number | undefined {\n const raw = args[spec.flag]\n if (raw === undefined || raw === null || raw === false || raw === '') return undefined\n return spec.threshold ?? Number(raw)\n}\n\n/**\n * Name a tripped gate by the flag that requested it, so the JSON says\n * \"fail-on-missing\" — what the user typed — rather than \"failOnMissing\".\n */\nfunction kebabCase(flag: string): string {\n return flag.replace(/[A-Z]/g, c => `-${c.toLowerCase()}`)\n}\n\n/**\n * Attach the gate report without disturbing the rest of the result: consumers\n * parsing today's shape keep working, and a run where nothing tripped is\n * byte-for-byte what it was before gates existed.\n */\nfunction withGateReport(result: unknown, tripped: TrippedGate[]): unknown {\n if (tripped.length === 0) return result\n if (result === null || typeof result !== 'object' || Array.isArray(result)) return result\n return { ...(result as Record<string, unknown>), gatesTripped: tripped }\n}\n\n/**\n * True when a run completed but achieved nothing: failures present and zero\n * successes. Covers translate-missing-style results (summary.totalFailed /\n * summary.totalTranslated) and translate-key-style results (top-level\n * failed[] / translated[]). Results without those fields are never a\n * total failure, so unrelated commands are unaffected.\n */\nexport function isTotalFailure(result: unknown): boolean {\n if (result === null || typeof result !== 'object') return false\n const r = result as Record<string, unknown>\n\n const summary = r.summary\n if (summary !== null && typeof summary === 'object') {\n const s = summary as Record<string, unknown>\n if (typeof s.totalFailed === 'number' && typeof s.totalTranslated === 'number') {\n return s.totalFailed > 0 && s.totalTranslated === 0\n }\n }\n\n if (Array.isArray(r.failed) && Array.isArray(r.translated)) {\n return r.failed.length > 0 && r.translated.length === 0\n }\n\n return false\n}\n\nconst sharedArgs = {\n projectDir: {\n type: 'string' as const,\n alias: 'd',\n description: 'Project directory (default: cwd)',\n },\n json: {\n type: 'boolean' as const,\n description: 'Output as JSON (default for non-TTY)',\n default: false,\n },\n}\n\n/** Output result — stdout always carries the result, machine-parseable when piped/--json */\nfunction outputResult(data: unknown, args: { json?: boolean }): void {\n const jsonMode = args.json || !process.stdout.isTTY\n if (\n !jsonMode &&\n data !== null &&\n typeof data === 'object' &&\n 'reportFile' in (data as Record<string, unknown>)\n ) {\n const { reportFile, ...rest } = data as Record<string, unknown>\n log.info(`Wrote report to: ${reportFile}`)\n writeResult(JSON.stringify(rest, null, 2) + '\\n')\n return\n }\n // JSON mode emits the full result (including reportFile when present) as pure JSON\n writeResult(JSON.stringify(data, null, 2) + '\\n')\n}\n\n/**\n * Failure output. In JSON mode stdout must still carry parseable JSON —\n * consumers pipe it into jq, and zero bytes is a parse error — so the\n * structured error object IS the result on stdout; the human-readable\n * message goes to stderr in every mode. Exit code stays non-zero (callers\n * set it).\n */\nexport function emitErrorResult(error: unknown, args: { json?: boolean }): void {\n log.error(toErrorMessage(error))\n const jsonMode = args.json || !process.stdout.isTTY\n if (!jsonMode) return\n const payload = { error: { code: errorCode(error), message: toErrorMessage(error) } }\n writeResult(JSON.stringify(payload, null, 2) + '\\n')\n}\n\n/** ToolError/FileIOError carry codes; Node errors expose e.g. ENOENT. */\nfunction errorCode(error: unknown): string {\n if (error instanceof Error) {\n const code = (error as { code?: unknown }).code\n if (typeof code === 'string') return code\n if (error.name === 'ConfigError') return 'CONFIG_ERROR'\n }\n return 'UNKNOWN_ERROR'\n}\n\n/**\n * The citty command for one descriptor: its flags, its gates, and a run that\n * hands the operation arguments of the types the descriptor declared.\n *\n * Everything a command needs is read off the descriptor, so a command is not a\n * file anyone writes — which is what keeps it from drifting from the tool that\n * runs the same operation.\n */\nexport function commandFromDescriptor(descriptor: AnyOperationDescriptor): CommandDef {\n const cli = descriptor.cli\n if (cli === null) {\n throw new Error(`Operation \"${descriptor.id}\" declares no CLI command.`)\n }\n\n return createCommand({\n name: cli.name,\n description: descriptor.description,\n args: cliArgs(descriptor.params),\n gates: descriptor.gates,\n run: async args => descriptor.run(operationArgs(descriptor.params, args), {\n surface: 'cli',\n // Resolved from the provider flags rather than passed through: which\n // flags select a backend is the CLI's business, not the operation's.\n translateFn: descriptor.usesTranslateFn === true\n ? await resolveProviderTranslateFn(args)\n : undefined,\n }),\n divert: async (result, args) =>\n divertToReport(result, descriptor, operationArgs(descriptor.params, args)),\n })\n}\n\n/** The citty `args` for a descriptor's parameters, skipping the CLI-hidden ones. */\nfunction cliArgs(params: Params): Record<string, unknown> {\n const args: Record<string, unknown> = {}\n for (const [name, spec] of Object.entries(params)) {\n if (spec.cli?.hidden === true) continue\n args[name] = {\n // citty parses booleans and strings. A list, a number and a JSON object\n // all arrive as one string and are converted in operationArgs.\n type: spec.type === 'boolean' ? 'boolean' : 'string',\n description: flagDescription(spec),\n ...(spec.required === true ? { required: true } : {}),\n ...(spec.default === undefined ? {} : { default: spec.default }),\n ...(spec.cli?.alias === undefined ? {} : { alias: [...toArray(spec.cli.alias)] }),\n ...(spec.enum === undefined ? {} : { valueHint: spec.enum.join('|') }),\n }\n }\n return args\n}\n\n/**\n * The description a flag prints. The declared one is written for a reader of\n * either surface, so how to spell a list or an object at a shell prompt is\n * added here rather than in every spec.\n */\nfunction flagDescription(spec: ParamSpec): string {\n if (spec.type === 'string[]') {\n return `${spec.description} Comma-separated${spec.allowAll === true ? ', or \"all\"' : ''}.`\n }\n if (spec.type === 'record') return `${spec.description} Pass it as JSON.`\n return spec.description\n}\n\n/**\n * citty's parsed args as the operation takes them: lists split, numbers and\n * JSON parsed, enums checked. `projectDir` comes along because every operation\n * accepts it without declaring it.\n */\nfunction operationArgs(params: Params, args: Record<string, unknown>): Record<string, unknown> {\n const operation: Record<string, unknown> = { projectDir: args.projectDir }\n for (const [name, spec] of Object.entries(params)) {\n if (spec.cli?.hidden === true) continue\n const value = coerce(name, spec, args[name])\n if (value !== undefined) operation[name] = value\n }\n return operation\n}\n\nfunction coerce(name: string, spec: ParamSpec, raw: unknown): unknown {\n if (spec.type === 'boolean') return typeof raw === 'boolean' ? raw : undefined\n\n const value = typeof raw === 'string' ? raw : undefined\n\n switch (spec.type) {\n case 'number':\n return value === undefined || value === '' ? undefined : toNumber(name, spec, value)\n case 'string[]':\n return toList(name, spec, value)\n case 'record':\n return value === undefined ? undefined : parseJsonArg(value, name)\n default:\n return toEnumChecked(name, spec, value)\n }\n}\n\nfunction toEnumChecked(name: string, spec: ParamSpec, value: string | undefined): string | undefined {\n if (spec.enum === undefined) return value\n // An empty value is an unset flag, not a value to reject: a shell produces\n // one routinely from an unset variable.\n if (value === undefined || value === '') return undefined\n if (!spec.enum.includes(value)) {\n throw new Error(`Invalid --${name} value: \"${value}\". Must be one of: ${spec.enum.join(', ')}`)\n }\n return value\n}\n\nfunction toNumber(name: string, spec: ParamSpec, raw: string): number {\n const value = Number(raw)\n const wellFormed = Number.isFinite(value)\n && (spec.integer !== true || (Number.isInteger(value) && String(value) === raw.trim()))\n && (spec.min === undefined || value >= spec.min)\n if (!wellFormed) {\n throw new Error(`Invalid --${name}: \"${raw}\". Must be ${numberRequirement(spec)}`)\n }\n return value\n}\n\nfunction numberRequirement(spec: ParamSpec): string {\n if (spec.integer !== true) {\n return spec.min === undefined ? 'a number' : `a number of at least ${spec.min}`\n }\n if (spec.min === 1) return 'a positive integer'\n return spec.min === undefined ? 'an integer' : `an integer of at least ${spec.min}`\n}\n\nfunction toList(name: string, spec: ParamSpec, raw: string | undefined): string[] | 'all' | undefined {\n if (spec.allowAll === true && raw === 'all') return 'all'\n\n const list = splitList(raw)\n // A list flag that was passed and yielded nothing is a mistake, not a request\n // for the default — and a required list with nothing in it has no meaning.\n const empty = list === undefined || list.length === 0\n if (empty && (spec.required === true || (raw !== undefined && raw !== ''))) {\n throw new Error(`No ${name} provided. Pass a comma-separated list via --${name}.`)\n }\n return list\n}\n\nfunction toArray(value: string | readonly string[]): readonly string[] {\n return typeof value === 'string' ? [value] : value\n}\n\n/**\n * Build a TranslateFn from the provider flags, or undefined when no provider\n * was given (agent mode). The base URL resolves flag > env > project config;\n * the config file is only read when neither of the first two is set, so a\n * fully-flagged invocation never depends on config discovery.\n */\nexport async function resolveProviderTranslateFn(args: {\n provider?: string\n model?: string\n apiKey?: string\n baseUrl?: string\n projectDir?: string\n}): Promise<TranslateFn | undefined> {\n if (!args.provider) return undefined\n if (!args.model) {\n throw new Error('--model is required when --provider is set')\n }\n\n let baseUrl = resolveProviderBaseUrl({ flag: args.baseUrl, env: process.env[BASE_URL_ENV] })\n if (!baseUrl) {\n const projectConfig = await loadProjectConfig(args.projectDir ?? process.cwd())\n baseUrl = resolveProviderBaseUrl({ config: projectConfig?.providerBaseUrl })\n }\n if (baseUrl) log.debug(`Provider base URL: ${redactBaseUrl(baseUrl)}`)\n\n return createTranslateFn({\n provider: args.provider as LlmProvider,\n model: args.model,\n apiKey: args.apiKey,\n baseUrl,\n })\n}\n\n/**\n * Strip anything secret-shaped from a base URL before it reaches a log.\n * Gateways are routinely addressed as https://user:pass@host or with the key\n * in a query parameter, so keep only origin and path.\n */\nexport function redactBaseUrl(raw: string): string {\n let url: URL\n try {\n url = new URL(raw)\n } catch {\n return '<unparseable base URL>'\n }\n const credentials = url.username || url.password ? '<redacted>@' : ''\n const query = url.search || url.hash ? ' (query redacted)' : ''\n return `${url.protocol}//${credentials}${url.host}${url.pathname}${query}`\n}\n\n/** Split a comma-separated string into a trimmed array, or return undefined */\nfunction splitList(val: string | undefined): string[] | undefined {\n if (!val) return undefined\n return val.split(',').map(s => s.trim()).filter(Boolean)\n}\n\n/** Parse a JSON string with a user-friendly error */\nfunction parseJsonArg<T = Record<string, Record<string, string>>>(\n value: string,\n argName: string,\n): T {\n try {\n return JSON.parse(value) as T\n } catch (err) {\n const detail = err instanceof SyntaxError ? err.message : String(err)\n throw new Error(`Invalid JSON in --${argName}: ${detail}`)\n }\n}\n","import type { CommandDef } from 'citty'\nimport { commandFromDescriptor } from './_shared.js'\nimport { descriptors } from '../surface/descriptors.js'\n\n/**\n * A registered command: nothing but its lazy loader.\n *\n * There is no way to register a command the CLI does not expose. The flag that\n * allowed it kept producing documented commands that printed \"Unknown command\"\n * when anyone tried them, and every operation an MCP tool covers has to be\n * reachable from a terminal or the two surfaces are not the same tool.\n *\n * The commands themselves are built from the operation descriptors, so the\n * registry states no name, no flag and no description of its own. The loader\n * stays because callers hold it: citty resolves subcommands through it, and the\n * reference generator awaits it. It also keeps the definition's identity\n * stable, which is how the generator recognises two names for one command.\n */\nexport interface CommandEntry {\n load: () => Promise<CommandDef>\n}\n\nfunction entry(build: () => CommandDef): CommandEntry {\n let built: CommandDef | undefined\n return { load: async () => (built ??= build()) }\n}\n\nfunction registry(): Record<string, CommandEntry> {\n const commands: Record<string, CommandEntry> = {}\n for (const descriptor of descriptors) {\n const cli = descriptor.cli\n if (cli === null) continue\n commands[cli.name] = entry(() => commandFromDescriptor(descriptor))\n }\n return commands\n}\n\nexport const commands: Record<string, CommandEntry> = registry()\n","import { createRequire } from 'node:module'\nimport { defineCommand, runCommand, runMain } from 'citty'\nimport { commands as allCommands } from './commands/index.js'\nimport type { CommandEntry } from './commands/index.js'\nimport { emitErrorResult } from './commands/_shared.js'\n\n// Every registered command is a subcommand. There is no filter here, and no\n// second list to fall out of step with the registry.\nconst commands = Object.fromEntries(\n Object.entries(allCommands as Record<string, CommandEntry>)\n .map(([name, entry]) => [name, entry.load]),\n)\n\nconst require = createRequire(import.meta.url)\nconst { version, description } = require('../package.json') as { version: string; description: string }\n\nconst main = defineCommand({\n meta: {\n name: 'the-i18n-cli',\n version,\n description,\n },\n subCommands: commands,\n})\n\nexport async function runCli(): Promise<void> {\n const rawArgs = process.argv.slice(2)\n\n // Let citty handle --help and --version natively (pretty-printed usage)\n if (rawArgs.includes('--help') || rawArgs.includes('-h')\n || rawArgs.includes('--version') || rawArgs.includes('-v')) {\n await runMain(main)\n return\n }\n\n // For normal execution, use runCommand so we control error output.\n // Command run() errors are handled inside createCommand; this catch only\n // sees pre-run failures (unknown command, argument parsing) — those must\n // also keep stdout parseable in JSON mode.\n try {\n await runCommand(main, { rawArgs })\n } catch (error: unknown) {\n emitErrorResult(error, { json: rawArgs.includes('--json') })\n process.exitCode = 1\n }\n}\n"],"mappings":"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"}
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { a as LocaleDir, i as LocaleDefinition, n as defineI18nKitConfig, o as ProjectConfig, r as I18nConfig, t as I18nKitConfig } from "./define-config-
|
|
1
|
+
import { a as LocaleDir, i as LocaleDefinition, n as defineI18nKitConfig, o as ProjectConfig, r as I18nConfig, t as I18nKitConfig } from "./define-config-ZRZw5PWy.js";
|
|
2
2
|
export { I18nConfig, I18nKitConfig, LocaleDefinition, LocaleDir, ProjectConfig, defineI18nKitConfig };
|
|
@@ -72,9 +72,21 @@ interface ProjectConfig {
|
|
|
72
72
|
key: string;
|
|
73
73
|
note?: string;
|
|
74
74
|
} & Record<string, string>>;
|
|
75
|
+
/**
|
|
76
|
+
* Namespaces whose keys exist by contract rather than by a call site: sent by
|
|
77
|
+
* a backend over the wire, keyed by runtime data, built from a registry. The
|
|
78
|
+
* scanner cannot see them, so they are declared once — with the contract that
|
|
79
|
+
* keeps them alive — and protected in every layer.
|
|
80
|
+
*/
|
|
81
|
+
declaredNamespaces?: Array<{
|
|
82
|
+
/** Key pattern the declaration covers (e.g., "views.defaults.**"). */
|
|
83
|
+
pattern: string;
|
|
84
|
+
/** What keeps these keys alive (e.g., "sent by bookings-api as name_key"). */
|
|
85
|
+
reason: string;
|
|
86
|
+
}>;
|
|
75
87
|
/** Per-layer scan directories and ignore patterns for orphan key detection. Keys are layer names. */
|
|
76
88
|
orphanScan?: Record<string, {
|
|
77
|
-
/** Glob patterns for translation keys to exclude from orphan detection (e.g., "common.datetime.months.*"). */
|
|
89
|
+
/** Glob patterns for translation keys to exclude from orphan detection in this layer (e.g., "common.datetime.months.*"). */
|
|
78
90
|
ignorePatterns?: string[];
|
|
79
91
|
}>;
|
|
80
92
|
/**
|
|
@@ -167,5 +179,5 @@ type I18nKitConfig = ProjectConfig;
|
|
|
167
179
|
*/
|
|
168
180
|
declare function defineI18nKitConfig(config: I18nKitConfig): I18nKitConfig;
|
|
169
181
|
//#endregion
|
|
170
|
-
export { LocaleDir as a, LocaleDefinition as i, defineI18nKitConfig as n, ProjectConfig as o, I18nConfig as r,
|
|
171
|
-
//# sourceMappingURL=define-config-
|
|
182
|
+
export { LocaleDir as a, LocaleDefinition as i, defineI18nKitConfig as n, ProjectConfig as o, I18nConfig as r, I18nKitConfig as t };
|
|
183
|
+
//# sourceMappingURL=define-config-ZRZw5PWy.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"define-config-ZRZw5PWy.d.ts","names":[],"sources":["../src/adapters/types.ts","../src/config/types.ts","../src/define-config.ts"],"sourcesContent":[],"mappings":";AAEY,KAAA,gBAAA,GAAgB,MAAA,GAAA,WAAA,GAAA,MAAA;;;AAA5B;;;UCGiB,gBAAA;EAAA;EAcA,IAAA,EAAA,MAAS;EAWT;EAmBA,QAAA,EAAA,MAAa;EAAA;MAMf,CAAA,EAAA,MAAA;;MAUC,CAAA,EAAA,MAAA;;;;;AA+BD,UA7EE,SAAA,CA6EF;;MAkBM,EAAA,MAAA;EAAgB;EAsBpB,KAAA,EAAA,MAAU;EAAA;cAQT,EAAA,MAAA;;SAIJ,CAAA,EAAA,MAAA;;AAMO,UA5HJ,OAAA,CA4HI;;EAEN,IAAA,EAAA,MAAA;;;;ECxIH,MAAA,EAAA,MAAA,EAAa;EAQT;;;;;;;;;;;UDqBC,aAAA;;;;;;eAMF;;;;;;aAMF;;;;gBAIG;;;;;;aAMH;;;MAAuC;;;;;;;uBAO7B;;;;;;;eAOR;;;;;;;;;;;eAWA;;;;;SAEN;;;;;;;;;;;;;;;;;;;qBAgBY;;;;;;;;;;;;;;;;;;;;;UAsBJ,UAAA;;;;;;;;kBAQC;;WAEP;;cAEG;;;;kBAII;;qBAEG;;QAEb;;;;;;;;;AA1Ca,KC9FT,aAAA,GAAgB,aD8FP;;AAsBrB;;;;;AAgBkB,iBC5HF,mBAAA,CD4HE,MAAA,EC5H0B,aD4H1B,CAAA,EC5H0C,aD4H1C"}
|
package/dist/define-config.d.ts
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export * from './define-config-
|
|
1
|
+
export * from './define-config-BpdVaEVR.d.ts';
|
|
@@ -266,7 +266,7 @@ async function divertToReport(result, descriptor, args) {
|
|
|
266
266
|
if (spec === void 0 || result === null || typeof result !== "object") return result;
|
|
267
267
|
const projectDir = typeof args.projectDir === "string" ? args.projectDir : process.cwd();
|
|
268
268
|
const [{ detectI18nConfig }, { resolveOutputFile, resolveReportFilePath, validateReportPath }, { writeCodequalityFile, writeReportFile }] = await Promise.all([
|
|
269
|
-
import("./detector-
|
|
269
|
+
import("./detector-DTfFbwSU.js"),
|
|
270
270
|
import("./report-CHZgH9Wy.js"),
|
|
271
271
|
import("./json-writer-D0b6vEax.js")
|
|
272
272
|
]);
|
|
@@ -399,7 +399,7 @@ function applyTranslateKeyGuidance(result, surface) {
|
|
|
399
399
|
* scanner, the parsers and the file writers — a quarter of a megabyte nobody
|
|
400
400
|
* asking for usage text should pay for.
|
|
401
401
|
*/
|
|
402
|
-
const core = () => import("./operations-
|
|
402
|
+
const core = () => import("./operations-CKHEmLNJ.js");
|
|
403
403
|
const layerFilter = {
|
|
404
404
|
type: "string",
|
|
405
405
|
description: "Layer name to scope this to (e.g., \"root\", \"app-admin\"). If omitted, every layer is included. Call discover to list the layers."
|
|
@@ -718,13 +718,13 @@ const descriptors = [
|
|
|
718
718
|
name: "search_translations",
|
|
719
719
|
title: "Search Translations"
|
|
720
720
|
},
|
|
721
|
-
description: "Search translation files by key path or value
|
|
722
|
-
longDescription: "Useful for finding an existing translation before adding a duplicate of it.",
|
|
721
|
+
description: "Search translation files by key path or value, one compact row per matching key rather than one per key and locale.",
|
|
722
|
+
longDescription: "Useful for finding an existing translation before adding a duplicate of it. A key that seven layers and thirty locales define comes back as a single row: layers names every layer that defines it, which is what tells reuse from duplication, and value is the one the reference locale holds. Pass includeLocales for the detail rows — one per key and locale — when what each locale holds is the question. Matching is a case-insensitive substring unless matchMode says otherwise.",
|
|
723
723
|
params: {
|
|
724
724
|
query: {
|
|
725
725
|
type: "string",
|
|
726
726
|
required: true,
|
|
727
|
-
description: "
|
|
727
|
+
description: "Text to search for, matched against keys and/or values. Compared as a case-insensitive substring unless matchMode says otherwise. Example: \"save\" matches the key \"common.actions.save\" and the value \"Save changes\"."
|
|
728
728
|
},
|
|
729
729
|
searchIn: {
|
|
730
730
|
type: "string",
|
|
@@ -737,6 +737,16 @@ const descriptors = [
|
|
|
737
737
|
description: "Whether to search translation keys, values, or both. Default: \"both\".",
|
|
738
738
|
cli: { alias: "in" }
|
|
739
739
|
},
|
|
740
|
+
matchMode: {
|
|
741
|
+
type: "string",
|
|
742
|
+
enum: [
|
|
743
|
+
"contains",
|
|
744
|
+
"exact",
|
|
745
|
+
"fuzzy"
|
|
746
|
+
],
|
|
747
|
+
default: "contains",
|
|
748
|
+
description: "How query is compared. \"contains\" is a case-insensitive substring, over every locale searched. \"exact\" is the whole string, and \"fuzzy\" also accepts near-misses in wording, both ignoring case, accents, punctuation and whitespace and both comparing against one locale only — locale when given, otherwise the project default. Default: \"contains\"."
|
|
749
|
+
},
|
|
740
750
|
layer: {
|
|
741
751
|
...layerFilter,
|
|
742
752
|
description: "Layer name to search in (e.g., \"root\", \"app-admin\"), or \"*\" for all layers. If omitted, searches every layer."
|
|
@@ -744,6 +754,11 @@ const descriptors = [
|
|
|
744
754
|
locale: {
|
|
745
755
|
type: "string",
|
|
746
756
|
description: "Locale code to search in (e.g., \"en\", \"de\"). If omitted, searches every locale."
|
|
757
|
+
},
|
|
758
|
+
includeLocales: {
|
|
759
|
+
type: "boolean",
|
|
760
|
+
default: false,
|
|
761
|
+
description: "Return one row per key and locale — layer, locale, key, value — instead of one row per key. Several times the output for the same findings, so ask for it when the per-locale values are what you are after. Default: false."
|
|
747
762
|
}
|
|
748
763
|
},
|
|
749
764
|
report: {
|
|
@@ -759,6 +774,8 @@ const descriptors = [
|
|
|
759
774
|
return searchTranslations({
|
|
760
775
|
query: args.query,
|
|
761
776
|
searchIn: args.searchIn,
|
|
777
|
+
matchMode: args.matchMode,
|
|
778
|
+
includeLocales: args.includeLocales,
|
|
762
779
|
layer: args.layer,
|
|
763
780
|
locale: args.locale,
|
|
764
781
|
projectDir: args.projectDir
|
|
@@ -1222,4 +1239,4 @@ function visibleParams(descriptor, surface) {
|
|
|
1222
1239
|
//#endregion
|
|
1223
1240
|
export { divertToReport as i, descriptorsFor as n, visibleParams as r, descriptors as t };
|
|
1224
1241
|
|
|
1225
|
-
//# sourceMappingURL=descriptors-
|
|
1242
|
+
//# sourceMappingURL=descriptors-Bo5UM031.js.map
|