import { defineCommand } from "citty"; import { wrapCommandErrors } from "../lib/error"; function lazyCommand( load: () => Promise, options: { hidden?: boolean } = {}, ): () => Promise { let pending: Promise | undefined; return () => (pending ??= load() .then((command) => { // Keep the exported command objects pristine: tests and callers may // import them directly while the CLI needs wrapped handlers. const cliCommand = cloneCommand(command); if (options.hidden) { const definition = cliCommand as T & { meta?: Record; }; definition.meta = { ...definition.meta, hidden: true }; } wrapCommandErrors(cliCommand); return cliCommand; }) .catch((error) => { pending = undefined; throw error; })); } function cloneCommand(command: T): T { const clone = { ...command } as T & { subCommands?: unknown }; const subCommands = clone.subCommands; if (subCommands && typeof subCommands === "object") { clone.subCommands = Object.fromEntries( Object.entries(subCommands).map(([name, child]) => [ name, child && typeof child === "object" ? cloneCommand(child) : child, ]), ); } return clone; } export const main = defineCommand({ meta: { name: "kuber", version: "2.7.1", description: "Deploy and manage projects on Kubernetes", }, args: { config: { type: "string", description: "Path to a kuber configuration file", }, }, subCommands: { add: lazyCommand(() => import("./add").then((m) => m.add)), audit: lazyCommand(() => import("./audit").then((m) => m.audit), { hidden: true, }), ci: lazyCommand(() => import("./ci").then((m) => m.ci), { hidden: true }), db: lazyCommand(() => import("./db").then((m) => m.db)), down: lazyCommand(() => import("./down").then((m) => m.down)), export: lazyCommand(() => import("./export").then((m) => m.exportCommand)), exec: lazyCommand(() => import("./exec").then((m) => m.exec)), init: lazyCommand(() => import("./init").then((m) => m.init)), logs: lazyCommand(() => import("./logs").then((m) => m.logs)), maintenance: lazyCommand( () => import("./maintenance").then((m) => m.maintenance), { hidden: true }, ), login: lazyCommand(() => import("./auth").then((m) => m.login)), logout: lazyCommand(() => import("./auth").then((m) => m.logout), { hidden: true, }), operations: lazyCommand( () => import("./operations").then((m) => m.operations), { hidden: true }, ), ps: lazyCommand(() => import("./ps").then((m) => m.ps)), restart: lazyCommand(() => import("./restart").then((m) => m.restart)), rollback: lazyCommand(() => import("./rollback").then((m) => m.rollback)), s3: lazyCommand(() => import("./s3").then((m) => m.s3)), start: lazyCommand(() => import("./start").then((m) => m.start)), stop: lazyCommand(() => import("./stop").then((m) => m.stop)), up: lazyCommand(() => import("./up").then((m) => m.up)), users: lazyCommand(() => import("./users").then((m) => m.users), { hidden: true, }), trust: lazyCommand(() => import("./trust").then((m) => m.trust)), whoami: lazyCommand(() => import("./auth").then((m) => m.whoami), { hidden: true, }), }, }); type Completion = Awaited< ReturnType<(typeof import("@bomb.sh/tab/citty"))["default"]> >; let completion: Promise | undefined; export function initializeCompletion() { completion ??= (async () => { // tab inspects command objects directly, unlike citty's lazy resolver. const commands = main.subCommands; if (commands && typeof commands !== "function") { main.subCommands = Object.fromEntries( await Promise.all( Object.entries(commands).map(async ([name, command]) => [ name, typeof command === "function" ? await command() : await command, ]), ), ); } const { default: tab } = await import("@bomb.sh/tab/citty"); return tab(main); })(); return completion; }