refactor(ql3): keep application CLI boundary thin

This commit is contained in:
whyour
2026-08-31 01:43:41 +08:00
parent a76618e176
commit 79045a0d43
2 changed files with 57 additions and 46 deletions
+8 -46
View File
@@ -7,6 +7,10 @@ import {
type LocalApplicationProcessSignalSource, type LocalApplicationProcessSignalSource,
} from './production-process/processApplication'; } from './production-process/processApplication';
import { runProductionLocalApplicationCutoverProbe } from './production-process/cutoverProbeProcess'; import { runProductionLocalApplicationCutoverProbe } from './production-process/cutoverProbeProcess';
import {
localApplicationCliFailureFact,
parseLocalApplicationCliCommand,
} from './production-process/cliCommand';
const USAGE = const USAGE =
'Usage: ql3-local-application [--cutover-probe] --config /absolute/private-config.json'; 'Usage: ql3-local-application [--cutover-probe] --config /absolute/private-config.json';
@@ -34,56 +38,12 @@ function emit(record: Readonly<LocalApplicationProcessEvent>): void {
process.stdout.write(`${JSON.stringify(record)}\n`); process.stdout.write(`${JSON.stringify(record)}\n`);
} }
function failureFact(error: unknown): Readonly<Record<string, unknown>> {
const candidate = error as {
readonly name?: unknown;
readonly code?: unknown;
};
return Object.freeze({
schemaVersion: 1,
component: 'qinglong3-local-application',
level: 'error',
event: 'process_failed',
name:
typeof candidate?.name === 'string' && candidate.name.length <= 128
? candidate.name
: 'Error',
...(typeof candidate?.code === 'string' && candidate.code.length <= 128
? { code: candidate.code }
: {}),
});
}
function configFileArgument(argv: readonly string[]): Readonly<{
configFilePath: string;
mode: 'application' | 'cutover_probe';
}> | null {
if (argv.length === 2 && argv[0] === '--config' && argv[1]) {
return Object.freeze({
configFilePath: argv[1],
mode: 'application' as const,
});
}
if (
argv.length === 3 &&
argv[0] === '--cutover-probe' &&
argv[1] === '--config' &&
argv[2]
) {
return Object.freeze({
configFilePath: argv[2],
mode: 'cutover_probe' as const,
});
}
return null;
}
async function main(argv: readonly string[]): Promise<void> { async function main(argv: readonly string[]): Promise<void> {
if (argv.length === 1 && (argv[0] === '--help' || argv[0] === '-h')) { if (argv.length === 1 && (argv[0] === '--help' || argv[0] === '-h')) {
process.stdout.write(`${USAGE}\n`); process.stdout.write(`${USAGE}\n`);
return; return;
} }
const command = configFileArgument(argv); const command = parseLocalApplicationCliCommand(argv);
if (command === null) { if (command === null) {
process.stderr.write( process.stderr.write(
`${JSON.stringify({ `${JSON.stringify({
@@ -109,7 +69,9 @@ async function main(argv: readonly string[]): Promise<void> {
}); });
if (stopResult !== 'stopped') process.exitCode = 1; if (stopResult !== 'stopped') process.exitCode = 1;
} catch (error) { } catch (error) {
process.stderr.write(`${JSON.stringify(failureFact(error))}\n`); process.stderr.write(
`${JSON.stringify(localApplicationCliFailureFact(error))}\n`,
);
process.exitCode = 1; process.exitCode = 1;
} }
} }
@@ -0,0 +1,49 @@
export type LocalApplicationCliCommand = Readonly<{
configFilePath: string;
mode: 'application' | 'cutover_probe';
}>;
export function localApplicationCliFailureFact(
error: unknown,
): Readonly<Record<string, unknown>> {
const candidate = error as {
readonly name?: unknown;
readonly code?: unknown;
};
return Object.freeze({
schemaVersion: 1,
component: 'qinglong3-local-application',
level: 'error',
event: 'process_failed',
name:
typeof candidate?.name === 'string' && candidate.name.length <= 128
? candidate.name
: 'Error',
...(typeof candidate?.code === 'string' && candidate.code.length <= 128
? { code: candidate.code }
: {}),
});
}
export function parseLocalApplicationCliCommand(
argv: readonly string[],
): LocalApplicationCliCommand | null {
if (argv.length === 2 && argv[0] === '--config' && argv[1]) {
return Object.freeze({
configFilePath: argv[1],
mode: 'application' as const,
});
}
if (
argv.length === 3 &&
argv[0] === '--cutover-probe' &&
argv[1] === '--config' &&
argv[2]
) {
return Object.freeze({
configFilePath: argv[2],
mode: 'cutover_probe' as const,
});
}
return null;
}