mirror of
https://github.com/morluto/rea
synced 2026-10-25 22:08:11 -04:00
refactor(cli): share registered command traversal
This commit is contained in:
@@ -1,7 +1,7 @@
|
||||
import { constants } from "node:buffer";
|
||||
import type { Writable } from "node:stream";
|
||||
|
||||
import { Cli, Filter, Formatter } from "incur";
|
||||
import { Filter, Formatter } from "incur";
|
||||
import { estimateTokenCount, sliceByTokens } from "tokenx";
|
||||
|
||||
import {
|
||||
@@ -23,6 +23,10 @@ import {
|
||||
writeOutputText,
|
||||
} from "./jsonOutput.js";
|
||||
import type { CliInstance } from "./types.js";
|
||||
import {
|
||||
visitRegisteredCommands,
|
||||
type RegisteredCommand,
|
||||
} from "./visitRegisteredCommands.js";
|
||||
|
||||
/** Result metadata supplied by the command that actually ran. */
|
||||
export interface CliResultOutputMetadata {
|
||||
@@ -41,40 +45,18 @@ export interface CliResultOutput {
|
||||
recoverFormattingFailure(cause: unknown): Promise<boolean>;
|
||||
}
|
||||
|
||||
type CommandMap = NonNullable<ReturnType<typeof Cli.toCommands.get>>;
|
||||
type CommandDefinition = Extract<
|
||||
CommandMap extends Map<string, infer Entry> ? Entry : never,
|
||||
{ run: unknown }
|
||||
>;
|
||||
const registeredCommands: WeakMap<object, CommandMap> = Cli.toCommands;
|
||||
|
||||
/** Install formatting once, after registration, for every ordinary CLI result. */
|
||||
export const streamCliCommandResults = (
|
||||
cli: CliInstance,
|
||||
output: CliResultOutput,
|
||||
): void => {
|
||||
const commands = registeredCommands.get(cli);
|
||||
if (commands !== undefined) wrapCommands(commands, [], output);
|
||||
};
|
||||
|
||||
const wrapCommands = (
|
||||
commands: CommandMap,
|
||||
parents: readonly string[],
|
||||
output: CliResultOutput,
|
||||
): void => {
|
||||
for (const [name, entry] of commands) {
|
||||
const path = [...parents, name];
|
||||
if ("_group" in entry) {
|
||||
wrapCommands(entry.commands, path, output);
|
||||
if (entry.root !== undefined)
|
||||
wrapCommand(entry.root, path.join(" "), output);
|
||||
} else if (!("_alias" in entry) && !("_fetch" in entry))
|
||||
wrapCommand(entry, path.join(" "), output);
|
||||
}
|
||||
visitRegisteredCommands(cli, (command, path) =>
|
||||
wrapCommand(command, path, output),
|
||||
);
|
||||
};
|
||||
|
||||
const wrapCommand = (
|
||||
command: Pick<CommandDefinition, "run">,
|
||||
command: Pick<RegisteredCommand, "run">,
|
||||
name: string,
|
||||
output: CliResultOutput,
|
||||
): void => {
|
||||
|
||||
@@ -1,15 +1,12 @@
|
||||
import { Cli, z } from "incur";
|
||||
import { z } from "incur";
|
||||
|
||||
import type { CliInstance } from "./types.js";
|
||||
import { visitRegisteredCommands } from "./visitRegisteredCommands.js";
|
||||
|
||||
type CommandMap = NonNullable<ReturnType<typeof Cli.toCommands.get>>;
|
||||
type CommandInputs = Partial<Record<(typeof INPUT_GROUPS)[number], unknown>>;
|
||||
|
||||
const INPUT_GROUPS = ["args", "env", "options"] as const;
|
||||
|
||||
// Incur types its router key without vars, env, or globals; accept any CLI.
|
||||
const registeredCommands: WeakMap<object, CommandMap> = Cli.toCommands;
|
||||
|
||||
/**
|
||||
* Advertise parser-filled defaults as omittable CLI inputs.
|
||||
*
|
||||
@@ -20,18 +17,7 @@ const registeredCommands: WeakMap<object, CommandMap> = Cli.toCommands;
|
||||
* value stay unchanged.
|
||||
*/
|
||||
export const presentOmittableDefaults = (cli: CliInstance): void => {
|
||||
const commands = registeredCommands.get(cli);
|
||||
if (commands !== undefined) presentCommands(commands);
|
||||
};
|
||||
|
||||
const presentCommands = (commands: CommandMap): void => {
|
||||
for (const entry of commands.values()) {
|
||||
if ("_group" in entry) {
|
||||
presentCommands(entry.commands);
|
||||
if (entry.root !== undefined) presentInputs(entry.root);
|
||||
} else if (!("_alias" in entry) && !("_fetch" in entry))
|
||||
presentInputs(entry);
|
||||
}
|
||||
visitRegisteredCommands(cli, (command) => presentInputs(command));
|
||||
};
|
||||
|
||||
const presentInputs = (command: CommandInputs): void => {
|
||||
|
||||
@@ -0,0 +1,35 @@
|
||||
import { Cli } from "incur";
|
||||
|
||||
import type { CliInstance } from "./types.js";
|
||||
|
||||
type CommandMap = NonNullable<ReturnType<typeof Cli.toCommands.get>>;
|
||||
type CommandEntry = CommandMap extends Map<string, infer Entry> ? Entry : never;
|
||||
export type RegisteredCommand = Extract<CommandEntry, { run: unknown }>;
|
||||
|
||||
// Incur types its router key without vars, env, or globals; accept any CLI.
|
||||
const registeredCommands: WeakMap<object, CommandMap> = Cli.toCommands;
|
||||
|
||||
/** Visit ordinary commands in map order, children before each group's root. */
|
||||
export const visitRegisteredCommands = (
|
||||
cli: CliInstance,
|
||||
visit: (command: RegisteredCommand, path: string) => void,
|
||||
): void => {
|
||||
const commands = registeredCommands.get(cli);
|
||||
if (commands !== undefined) visitCommands(commands, [], visit);
|
||||
};
|
||||
|
||||
const visitCommands = (
|
||||
commands: CommandMap,
|
||||
parents: readonly string[],
|
||||
visit: (command: RegisteredCommand, path: string) => void,
|
||||
): void => {
|
||||
for (const [name, entry] of commands) {
|
||||
const path = [...parents, name];
|
||||
if ("_group" in entry) {
|
||||
visitCommands(entry.commands, path, visit);
|
||||
if (entry.root !== undefined) visit(entry.root, path.join(" "));
|
||||
} else if (!("_alias" in entry) && !("_fetch" in entry)) {
|
||||
visit(entry, path.join(" "));
|
||||
}
|
||||
}
|
||||
};
|
||||
@@ -8,6 +8,7 @@ import {
|
||||
apktoolInputSchemas,
|
||||
MAX_SELECTED_LOCALES,
|
||||
} from "../../../src/domain/apktool/apktoolResourceAnalysis.js";
|
||||
import { presentOmittableDefaults } from "../../../src/cli/omittableDefaults.js";
|
||||
|
||||
type CliInstance = ReturnType<typeof createCli>;
|
||||
type CommandMap = NonNullable<ReturnType<typeof Cli.toCommands.get>>;
|
||||
@@ -165,6 +166,22 @@ describe("CLI advertised input schemas", () => {
|
||||
"archive Relative archive path when the target is a bundle",
|
||||
);
|
||||
});
|
||||
|
||||
it("advertises defaults as omittable on a mounted group root", async () => {
|
||||
const group = Cli.create("group", {
|
||||
args: z.object({ scope: z.enum(["all", "selected"]).default("all") }),
|
||||
run: () => ({ ok: true }),
|
||||
}).command("child", { run: () => ({ ok: true }) });
|
||||
const cli = Cli.create("group-root-test", { sync: false }).command(group);
|
||||
|
||||
presentOmittableDefaults(cli as unknown as CliInstance);
|
||||
|
||||
const advertised = await advertisedSchema(
|
||||
cli as unknown as CliInstance,
|
||||
"group",
|
||||
);
|
||||
expect(advertised.args?.required ?? []).not.toContain("scope");
|
||||
});
|
||||
});
|
||||
|
||||
describe("firmware CLI advertised input schema", () => {
|
||||
|
||||
@@ -161,6 +161,48 @@ describe("incremental CLI JSON", () => {
|
||||
);
|
||||
});
|
||||
|
||||
describe("CLI command output wrapping", () => {
|
||||
it("wraps a mounted group root reached through its alias", async () => {
|
||||
const chunks: Buffer[] = [];
|
||||
const destination = new Writable({
|
||||
write(chunk: Buffer, _encoding, callback) {
|
||||
chunks.push(chunk);
|
||||
callback();
|
||||
},
|
||||
});
|
||||
const output = createCliResultOutput(
|
||||
["--format", "json", "--full-output"],
|
||||
destination,
|
||||
);
|
||||
if (output === undefined)
|
||||
throw new Error("Missing structured output surface");
|
||||
|
||||
const group = Cli.create("nested", {
|
||||
aliases: ["nested-alias"],
|
||||
run: () => ({ kind: "group-root" }),
|
||||
}).command("child", { run: () => ({ kind: "child" }) });
|
||||
const cli = Cli.create("rea-output-group-test", { sync: false }).command(
|
||||
group,
|
||||
);
|
||||
streamCliCommandResults(cli, output);
|
||||
|
||||
let fallback = "";
|
||||
await cli.serve(["nested-alias", "--format", "json"], {
|
||||
stdout: (text) => {
|
||||
if (!output.handled) fallback += text;
|
||||
},
|
||||
});
|
||||
|
||||
expect(output.handled).toBe(true);
|
||||
expect(fallback).toBe("");
|
||||
expect(JSON.parse(Buffer.concat(chunks).toString("utf8"))).toMatchObject({
|
||||
ok: true,
|
||||
data: { kind: "group-root" },
|
||||
meta: { command: "nested" },
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe("CLI result output surface", () => {
|
||||
it.each(["json", "yaml", "toon", "md"] as const)(
|
||||
"places typed analysis failures in the full-output error envelope (%s)",
|
||||
|
||||
Reference in New Issue
Block a user