import { defineCommand } from "citty"; import { wrapCommandErrors } from "../lib/error"; function lazyCommand( load: () => Promise, ): () => 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); 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.5.2", description: "Docker Compose -> K8s translation layer", }, args: { config: { type: "string", description: "Path to a kuber configuration file", }, }, subCommands: { audit: lazyCommand(() => import("./audit").then((m) => m.audit)), ci: lazyCommand(() => import("./ci").then((m) => m.ci)), 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)), logs: lazyCommand(() => import("./logs").then((m) => m.logs)), maintenance: lazyCommand(() => import("./maintenance").then((m) => m.maintenance), ), login: lazyCommand(() => import("./auth").then((m) => m.login)), logout: lazyCommand(() => import("./auth").then((m) => m.logout)), operations: lazyCommand(() => import("./operations").then((m) => m.operations), ), 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)), trust: lazyCommand(() => import("./trust").then((m) => m.trust)), whoami: lazyCommand(() => import("./auth").then((m) => m.whoami)), }, }); type Completion = Awaited< ReturnType >; 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; }