@kurotako/cli 0.1.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/index.js ADDED
@@ -0,0 +1,675 @@
1
+ // src/cli.ts
2
+ import { TakoError as TakoError3 } from "@kurotako/core";
3
+ import { defineCommand as defineCommand5, renderUsage, runCommand } from "citty";
4
+
5
+ // src/commands/check.ts
6
+ import { existsSync as existsSync2 } from "fs";
7
+ import { relative } from "path";
8
+ import { loadConfig } from "@kurotako/config";
9
+ import { run } from "@kurotako/core";
10
+ import { defineCommand } from "citty";
11
+
12
+ // src/args.ts
13
+ var sharedArgs = {
14
+ config: {
15
+ type: "string",
16
+ description: "path to the tako config file (default: ./tako.config.ts)"
17
+ },
18
+ /** Hidden: not advertised, kept minimal. Enables `debug` reporter output. */
19
+ debug: {
20
+ type: "boolean",
21
+ default: false
22
+ }
23
+ };
24
+
25
+ // src/diff.ts
26
+ import { existsSync } from "fs";
27
+ import { readdir, readFile } from "fs/promises";
28
+ import path from "path";
29
+ function toPosix(rootDir, absPath) {
30
+ return path.relative(rootDir, absPath).split(path.sep).join("/");
31
+ }
32
+ async function comparePlanToDisk(args) {
33
+ const { plan, roots, ignore, rootDir } = args;
34
+ const plannedByPath = new Map(plan.map((f) => [path.resolve(f.path), f]));
35
+ const entries = [];
36
+ for (const [absPath, file] of plannedByPath) {
37
+ if (!existsSync(absPath)) {
38
+ entries.push({ kind: "missing", path: toPosix(rootDir, absPath) });
39
+ continue;
40
+ }
41
+ const onDisk = await readFile(absPath, "utf8");
42
+ if (onDisk !== file.content) {
43
+ entries.push({ kind: "modified", path: toPosix(rootDir, absPath) });
44
+ }
45
+ }
46
+ const plannedPaths = [...plannedByPath.keys()];
47
+ const hasPlannedUnder = (dir) => {
48
+ const prefix = `${dir}${path.sep}`;
49
+ return plannedPaths.some((p) => p === dir || p.startsWith(prefix));
50
+ };
51
+ const seenRoots = /* @__PURE__ */ new Set();
52
+ for (const root of roots) {
53
+ const absRoot = path.resolve(root);
54
+ if (seenRoots.has(absRoot) || !existsSync(absRoot)) {
55
+ continue;
56
+ }
57
+ seenRoots.add(absRoot);
58
+ await scan(absRoot);
59
+ }
60
+ async function scan(dir) {
61
+ if (!hasPlannedUnder(dir)) {
62
+ if (await hasNonIgnoredFile(dir)) {
63
+ entries.push({ kind: "orphan", path: `${toPosix(rootDir, dir)}/` });
64
+ }
65
+ return;
66
+ }
67
+ for (const dirent of await readdir(dir, { withFileTypes: true })) {
68
+ const abs = path.join(dir, dirent.name);
69
+ if (ignore(abs)) {
70
+ continue;
71
+ }
72
+ if (dirent.isDirectory()) {
73
+ await scan(abs);
74
+ } else if (dirent.isFile() && !plannedByPath.has(abs)) {
75
+ entries.push({ kind: "orphan", path: toPosix(rootDir, abs) });
76
+ }
77
+ }
78
+ }
79
+ async function hasNonIgnoredFile(dir) {
80
+ for (const dirent of await readdir(dir, { withFileTypes: true })) {
81
+ const abs = path.join(dir, dirent.name);
82
+ if (ignore(abs)) {
83
+ continue;
84
+ }
85
+ if (dirent.isFile()) {
86
+ return true;
87
+ }
88
+ if (dirent.isDirectory() && await hasNonIgnoredFile(abs)) {
89
+ return true;
90
+ }
91
+ }
92
+ return false;
93
+ }
94
+ entries.sort(
95
+ (a, b) => a.path < b.path ? -1 : a.path > b.path ? 1 : a.kind < b.kind ? -1 : a.kind > b.kind ? 1 : 0
96
+ );
97
+ return entries;
98
+ }
99
+ function ignoreBuildArtifacts(absPath) {
100
+ return /(^|[/\\])(dist|node_modules)([/\\]|$)/.test(absPath);
101
+ }
102
+ async function hasGeneratedMarker(pkgDir) {
103
+ try {
104
+ const pkg = JSON.parse(
105
+ await readFile(path.join(pkgDir, "package.json"), "utf8")
106
+ );
107
+ return typeof pkg["//"] === "string" && pkg["//"].startsWith("Generated by tako");
108
+ } catch {
109
+ return false;
110
+ }
111
+ }
112
+ async function markedPackageDirs(packagesDir) {
113
+ if (!existsSync(packagesDir)) {
114
+ return [];
115
+ }
116
+ const out = [];
117
+ for (const dirent of await readdir(packagesDir, { withFileTypes: true })) {
118
+ if (!dirent.isDirectory()) {
119
+ continue;
120
+ }
121
+ const abs = path.join(packagesDir, dirent.name);
122
+ if (await hasGeneratedMarker(abs)) {
123
+ out.push(abs);
124
+ }
125
+ }
126
+ return out.sort();
127
+ }
128
+
129
+ // src/reporter.ts
130
+ import { childLogger } from "@kurotako/core";
131
+ var ESC = String.fromCharCode(27);
132
+ var ANSI = {
133
+ debug: `${ESC}[2m`,
134
+ // dim
135
+ info: `${ESC}[36m`,
136
+ // cyan
137
+ warn: `${ESC}[33m`,
138
+ // yellow
139
+ error: `${ESC}[31m`
140
+ // red
141
+ };
142
+ var ANSI_RESET = `${ESC}[0m`;
143
+ var ConsoleReporter = class {
144
+ stream;
145
+ showDebug;
146
+ color;
147
+ constructor(options = {}) {
148
+ this.stream = options.stream ?? process.stderr;
149
+ this.showDebug = options.debug ?? Boolean(process.env.TAKO_DEBUG);
150
+ this.color = options.color ?? (Boolean(this.stream.isTTY) && process.env.NO_COLOR === void 0);
151
+ }
152
+ debug(msg, meta) {
153
+ if (this.showDebug) {
154
+ this.write("debug", msg, meta);
155
+ }
156
+ }
157
+ info(msg, meta) {
158
+ this.write("info", msg, meta);
159
+ }
160
+ warn(msg, meta) {
161
+ this.write("warn", msg, meta);
162
+ }
163
+ error(msg, meta) {
164
+ this.write("error", msg, meta);
165
+ }
166
+ /** A child `Logger` whose calls carry `{ scope: tag }` in `meta`. */
167
+ child(tag) {
168
+ return childLogger(this, { scope: tag });
169
+ }
170
+ write(level, msg, meta) {
171
+ const prefix = this.color ? `${ANSI[level]}tako${ANSI_RESET}` : "tako";
172
+ const detail = meta === void 0 ? "" : ` ${formatMeta(meta)}`;
173
+ this.stream.write(`${prefix} ${msg}${detail}
174
+ `);
175
+ }
176
+ };
177
+ function formatMeta(meta) {
178
+ if (meta !== null && typeof meta === "object" && !Array.isArray(meta)) {
179
+ return Object.entries(meta).map(([k, v]) => `${k}=${stringify(v)}`).join(" ");
180
+ }
181
+ return stringify(meta);
182
+ }
183
+ function stringify(value) {
184
+ if (typeof value === "string") {
185
+ return value;
186
+ }
187
+ try {
188
+ return JSON.stringify(value) ?? String(value);
189
+ } catch {
190
+ return String(value);
191
+ }
192
+ }
193
+
194
+ // src/commands/check.ts
195
+ var checkCommand = defineCommand({
196
+ meta: {
197
+ name: "check",
198
+ description: "check the generated output on disk matches a fresh generate (drift guard)"
199
+ },
200
+ args: { ...sharedArgs },
201
+ run: async ({ args }) => {
202
+ const reporter = new ConsoleReporter({ debug: Boolean(args.debug) });
203
+ const cwd = process.cwd();
204
+ const { config } = await loadConfig({
205
+ cwd,
206
+ configPath: args.config || void 0
207
+ });
208
+ const { plan = [] } = await run(config, { logger: reporter, plan: true });
209
+ const roots = [];
210
+ const ignores = [];
211
+ for (const output of config.outputs) {
212
+ if (output.mode === "package") {
213
+ ignores.push(ignoreBuildArtifacts);
214
+ if (output.packagesDir) {
215
+ roots.push(...await markedPackageDirs(output.packagesDir));
216
+ }
217
+ } else {
218
+ const dir = output.dir;
219
+ if (!dir) {
220
+ continue;
221
+ }
222
+ if (!existsSync2(dir)) {
223
+ reporter.info(
224
+ `output directory ${relative(config.rootDir, dir)} does not exist \u2014 run tako generate`
225
+ );
226
+ process.exitCode = 1;
227
+ return;
228
+ }
229
+ roots.push(dir);
230
+ }
231
+ }
232
+ const ignore = (absPath) => ignores.some((fn) => fn(absPath));
233
+ const drift = await comparePlanToDisk({
234
+ plan,
235
+ roots,
236
+ ignore,
237
+ rootDir: config.rootDir
238
+ });
239
+ if (drift.length === 0) {
240
+ reporter.info("output is in sync");
241
+ process.exitCode ??= 0;
242
+ return;
243
+ }
244
+ reporter.error(formatDrift(drift));
245
+ process.exitCode = 1;
246
+ }
247
+ });
248
+ function formatDrift(drift) {
249
+ const width = Math.max(...drift.map((entry) => entry.kind.length));
250
+ const lines = drift.map(
251
+ (entry) => ` ${entry.kind.padEnd(width)} ${entry.path}`
252
+ );
253
+ return [
254
+ "drift detected",
255
+ "",
256
+ ...lines,
257
+ "",
258
+ `${drift.length} files differ from a fresh generate. Run \`tako generate\`.`
259
+ ].join("\n");
260
+ }
261
+
262
+ // src/commands/generate.ts
263
+ import { relative as relative2 } from "path";
264
+ import { loadConfig as loadConfig2 } from "@kurotako/config";
265
+ import { run as run2 } from "@kurotako/core";
266
+ import { defineCommand as defineCommand2 } from "citty";
267
+
268
+ // src/watch.ts
269
+ import { basename, resolve } from "path";
270
+ import {
271
+ loadConfig as realLoadConfig
272
+ } from "@kurotako/config";
273
+ import { run as realRun, TakoError as TakoError2 } from "@kurotako/core";
274
+ import chokidar from "chokidar";
275
+
276
+ // src/errors.ts
277
+ import { TakoError } from "@kurotako/core";
278
+ var ConfigExistsError = class extends TakoError {
279
+ path;
280
+ constructor(path2) {
281
+ super(
282
+ "config_exists",
283
+ `a config file already exists at '${path2}' (pass --force to overwrite)`
284
+ );
285
+ this.path = path2;
286
+ }
287
+ };
288
+ function renderError(error) {
289
+ const lines = [`error [${error.code}]: ${error.message}`];
290
+ const issues = error.issues;
291
+ if (Array.isArray(issues)) {
292
+ for (const issue of issues) {
293
+ const at = issue.path === void 0 || issue.path === "" ? "<root>" : issue.path;
294
+ lines.push(` - ${at}: ${issue.message}`);
295
+ }
296
+ }
297
+ const cycle = error.cycle;
298
+ if (Array.isArray(cycle) && cycle.length > 0) {
299
+ lines.push(` cycle: ${cycle.join(" -> ")}`);
300
+ }
301
+ const driverName = error.driverName;
302
+ if (typeof driverName === "string") {
303
+ const role = error.role;
304
+ const namespace = error.namespace;
305
+ const roleLabel = typeof role === "string" ? role : "driver";
306
+ const where = typeof namespace === "string" ? ` (namespace '${namespace}')` : "";
307
+ lines.push(` ${roleLabel}: ${driverName}${where}`);
308
+ } else {
309
+ const generator = error.generator;
310
+ if (typeof generator === "string") {
311
+ const namespace = error.namespace;
312
+ const where = typeof namespace === "string" ? ` (namespace '${namespace}')` : "";
313
+ lines.push(` generator: ${generator}${where}`);
314
+ }
315
+ }
316
+ const cause = error.cause;
317
+ if (cause !== void 0 && cause !== null) {
318
+ const message = cause instanceof Error ? cause.message : String(cause);
319
+ const code = cause instanceof TakoError ? ` [${cause.code}]` : "";
320
+ lines.push(` cause${code}: ${message}`);
321
+ }
322
+ return lines.join("\n");
323
+ }
324
+
325
+ // src/watch.ts
326
+ var DEBOUNCE_MS = 100;
327
+ function chokidarWatcher() {
328
+ const fsw = chokidar.watch([], {
329
+ ignoreInitial: true,
330
+ awaitWriteFinish: { stabilityThreshold: 150, pollInterval: 20 }
331
+ });
332
+ return {
333
+ add: (paths) => fsw.add(paths),
334
+ unwatch: (paths) => fsw.unwatch(paths),
335
+ on: (handler) => {
336
+ fsw.on("add", (p) => handler("add", p));
337
+ fsw.on("change", (p) => handler("change", p));
338
+ fsw.on("unlink", (p) => handler("unlink", p));
339
+ },
340
+ close: () => fsw.close()
341
+ };
342
+ }
343
+ function isAbortError(error) {
344
+ return error instanceof Error && error.name === "AbortError";
345
+ }
346
+ async function watchAndRun(opts, deps = {}) {
347
+ const { cwd, configPath, reporter } = opts;
348
+ const createWatcher = deps.createWatcher ?? chokidarWatcher;
349
+ const loadConfig3 = deps.loadConfig ?? realLoadConfig;
350
+ const run3 = deps.run ?? realRun;
351
+ const debounceMs = deps.debounceMs ?? DEBOUNCE_MS;
352
+ const watcher = createWatcher();
353
+ let watched = /* @__PURE__ */ new Set();
354
+ let inFlight = null;
355
+ let queued = false;
356
+ let closed = false;
357
+ let timer = null;
358
+ let reason = "initial build";
359
+ const guessedConfigFile = configPath ? resolve(cwd, configPath) : resolve(cwd, "tako.config.ts");
360
+ function applyWatchSet(desired) {
361
+ for (const path2 of desired) {
362
+ if (!watched.has(path2)) {
363
+ watcher.add(path2);
364
+ }
365
+ }
366
+ for (const path2 of watched) {
367
+ if (!desired.has(path2)) {
368
+ watcher.unwatch(path2);
369
+ }
370
+ }
371
+ watched = desired;
372
+ }
373
+ async function reconcileWatchSet(loaded) {
374
+ const desired = /* @__PURE__ */ new Set([loaded.configFile]);
375
+ for (const [namespace, source] of Object.entries(loaded.config.sources)) {
376
+ const paths = await source.parser.watchPaths?.({
377
+ namespace,
378
+ cwd: loaded.rootDir,
379
+ logger: reporter
380
+ }) ?? [];
381
+ for (const path2 of paths) {
382
+ desired.add(resolve(loaded.rootDir, path2));
383
+ }
384
+ }
385
+ applyWatchSet(desired);
386
+ }
387
+ async function cycle() {
388
+ const why = reason;
389
+ reporter.info(`--- rebuild (${why}) ---`);
390
+ const ac = new AbortController();
391
+ inFlight = ac;
392
+ try {
393
+ const loaded = await loadConfig3({ cwd, configPath });
394
+ await reconcileWatchSet(loaded);
395
+ const result = await run3(loaded.config, {
396
+ logger: reporter,
397
+ signal: ac.signal,
398
+ write: true
399
+ });
400
+ if (!ac.signal.aborted) {
401
+ reporter.info(`wrote ${result.files.length} files`);
402
+ }
403
+ } catch (error) {
404
+ if (isAbortError(error) || ac.signal.aborted) {
405
+ } else if (error instanceof TakoError2) {
406
+ reporter.error(renderError(error));
407
+ applyWatchSet(/* @__PURE__ */ new Set([...watched, guessedConfigFile]));
408
+ } else {
409
+ reporter.error("internal error (this is a bug):");
410
+ console.error(error);
411
+ }
412
+ } finally {
413
+ inFlight = null;
414
+ if (queued && !closed) {
415
+ queued = false;
416
+ await cycle();
417
+ }
418
+ }
419
+ }
420
+ function trigger() {
421
+ if (inFlight) {
422
+ queued = true;
423
+ inFlight.abort();
424
+ return;
425
+ }
426
+ void cycle();
427
+ }
428
+ function schedule(event, path2) {
429
+ if (closed) {
430
+ return;
431
+ }
432
+ reason = `${basename(path2)} ${event === "unlink" ? "removed" : "changed"}`;
433
+ if (timer) {
434
+ clearTimeout(timer);
435
+ }
436
+ timer = setTimeout(() => {
437
+ timer = null;
438
+ trigger();
439
+ }, debounceMs);
440
+ }
441
+ watcher.on(schedule);
442
+ applyWatchSet(/* @__PURE__ */ new Set([guessedConfigFile]));
443
+ await cycle();
444
+ reporter.info(`watching ${watched.size} paths`);
445
+ await new Promise((resolvePromise) => {
446
+ const shutdown = () => {
447
+ closed = true;
448
+ if (timer) {
449
+ clearTimeout(timer);
450
+ }
451
+ inFlight?.abort();
452
+ process.off("SIGINT", shutdown);
453
+ process.off("SIGTERM", shutdown);
454
+ void watcher.close().finally(() => resolvePromise());
455
+ };
456
+ process.on("SIGINT", shutdown);
457
+ process.on("SIGTERM", shutdown);
458
+ });
459
+ }
460
+
461
+ // src/commands/generate.ts
462
+ function outputSummaryDir(output, config) {
463
+ return output.mode === "package" ? output.packagesDir ?? config.rootDir : output.dir ?? config.rootDir;
464
+ }
465
+ async function loadAndRun(opts) {
466
+ const loaded = await loadConfig2({
467
+ cwd: opts.cwd,
468
+ configPath: opts.configPath
469
+ });
470
+ const result = await run2(loaded.config, {
471
+ logger: opts.reporter,
472
+ write: opts.write,
473
+ signal: opts.signal
474
+ });
475
+ return {
476
+ result,
477
+ config: loaded.config,
478
+ configFile: loaded.configFile,
479
+ rootDir: loaded.rootDir
480
+ };
481
+ }
482
+ var generateCommand = defineCommand2({
483
+ meta: {
484
+ name: "generate",
485
+ description: "run the pipeline and write the generated code"
486
+ },
487
+ args: {
488
+ ...sharedArgs,
489
+ watch: {
490
+ type: "boolean",
491
+ description: "rebuild on every schema / config change",
492
+ default: false
493
+ },
494
+ "dry-run": {
495
+ type: "boolean",
496
+ description: "run everything but write nothing",
497
+ default: false
498
+ }
499
+ },
500
+ run: async ({ args }) => {
501
+ const reporter = new ConsoleReporter({ debug: Boolean(args.debug) });
502
+ const cwd = process.cwd();
503
+ const configPath = args.config || void 0;
504
+ if (args.watch) {
505
+ await watchAndRun({ cwd, configPath, reporter });
506
+ return;
507
+ }
508
+ const dryRun = Boolean(args["dry-run"]);
509
+ const { result, config } = await loadAndRun({
510
+ cwd,
511
+ configPath,
512
+ write: !dryRun,
513
+ reporter
514
+ });
515
+ if (dryRun) {
516
+ reporter.info(`dry run: ${result.files.length} files would be written`);
517
+ return;
518
+ }
519
+ const dirs = config.outputs.map((output) => relative2(cwd, outputSummaryDir(output, config))).join(", ");
520
+ reporter.info(`wrote ${result.files.length} files -> ${dirs}`);
521
+ }
522
+ });
523
+
524
+ // src/commands/init.ts
525
+ import { existsSync as existsSync3, readFileSync } from "fs";
526
+ import { mkdir, writeFile } from "fs/promises";
527
+ import { dirname, relative as relative3, resolve as resolve2 } from "path";
528
+ import { CONFIG_TEMPLATE, CONFIG_TEMPLATE_MONOREPO } from "@kurotako/config";
529
+ import { defineCommand as defineCommand3 } from "citty";
530
+ function detectMonorepo(startDir) {
531
+ let dir = resolve2(startDir);
532
+ while (true) {
533
+ const pkgPath = resolve2(dir, "package.json");
534
+ if (existsSync3(pkgPath)) {
535
+ if (existsSync3(resolve2(dir, "pnpm-workspace.yaml"))) {
536
+ return true;
537
+ }
538
+ try {
539
+ const pkg = JSON.parse(readFileSync(pkgPath, "utf8"));
540
+ const ws = pkg.workspaces;
541
+ if (Array.isArray(ws) || typeof ws === "object" && ws !== null && Array.isArray(ws.packages)) {
542
+ return true;
543
+ }
544
+ } catch {
545
+ }
546
+ return false;
547
+ }
548
+ const parent = dirname(dir);
549
+ if (parent === dir) {
550
+ return false;
551
+ }
552
+ dir = parent;
553
+ }
554
+ }
555
+ var initCommand = defineCommand3({
556
+ meta: {
557
+ name: "init",
558
+ description: "create a tako.config.ts in the current directory"
559
+ },
560
+ args: {
561
+ ...sharedArgs,
562
+ force: {
563
+ type: "boolean",
564
+ description: "overwrite an existing config file",
565
+ default: false
566
+ },
567
+ monorepo: {
568
+ type: "boolean",
569
+ description: "write the monorepo config layout (auto-detected from workspaces when unset)",
570
+ default: void 0
571
+ }
572
+ },
573
+ run: async ({ args }) => {
574
+ const reporter = new ConsoleReporter({ debug: Boolean(args.debug) });
575
+ const cwd = process.cwd();
576
+ const target = args.config ? resolve2(cwd, args.config) : resolve2(cwd, "tako.config.ts");
577
+ if (existsSync3(target) && !args.force) {
578
+ throw new ConfigExistsError(target);
579
+ }
580
+ const monorepo = typeof args.monorepo === "boolean" ? args.monorepo : detectMonorepo(cwd);
581
+ await mkdir(dirname(target), { recursive: true });
582
+ await writeFile(
583
+ target,
584
+ monorepo ? CONFIG_TEMPLATE_MONOREPO : CONFIG_TEMPLATE,
585
+ "utf8"
586
+ );
587
+ const name = relative3(cwd, target) || "tako.config.ts";
588
+ reporter.info(
589
+ monorepo ? `created ${name} (monorepo layout)` : `created ${name}`
590
+ );
591
+ }
592
+ });
593
+
594
+ // src/commands/validate.ts
595
+ import { defineCommand as defineCommand4 } from "citty";
596
+ var validateCommand = defineCommand4({
597
+ meta: {
598
+ name: "validate",
599
+ description: "check the config and schema without writing anything"
600
+ },
601
+ args: { ...sharedArgs },
602
+ run: async ({ args }) => {
603
+ const reporter = new ConsoleReporter({ debug: Boolean(args.debug) });
604
+ await loadAndRun({
605
+ cwd: process.cwd(),
606
+ configPath: args.config || void 0,
607
+ write: false,
608
+ reporter
609
+ });
610
+ reporter.info("config and schema are valid");
611
+ }
612
+ });
613
+
614
+ // src/cli.ts
615
+ var VERSION = true ? "0.1.0" : "0.0.0-dev";
616
+ var subCommands = {
617
+ init: initCommand,
618
+ generate: generateCommand,
619
+ validate: validateCommand,
620
+ check: checkCommand
621
+ };
622
+ var main = defineCommand5({
623
+ meta: {
624
+ name: "tako",
625
+ version: VERSION,
626
+ description: "synchronise TypeScript schemas from data model to forms"
627
+ },
628
+ subCommands
629
+ });
630
+ async function runCli(argv) {
631
+ const reporter = new ConsoleReporter({
632
+ debug: argv.includes("--debug") || Boolean(process.env.TAKO_DEBUG)
633
+ });
634
+ if (argv.includes("--version") || argv.includes("-v")) {
635
+ process.stdout.write(`${VERSION}
636
+ `);
637
+ process.exitCode ??= 0;
638
+ return;
639
+ }
640
+ if (argv.length === 0 || argv.includes("--help") || argv.includes("-h")) {
641
+ const sub = argv.find((a) => !a.startsWith("-"));
642
+ const isSub = sub !== void 0 && sub in subCommands;
643
+ const cmd = isSub ? subCommands[sub] : main;
644
+ process.stdout.write(
645
+ `${await renderUsage(cmd, isSub ? main : void 0)}
646
+ `
647
+ );
648
+ process.exitCode ??= 0;
649
+ return;
650
+ }
651
+ try {
652
+ await runCommand(main, { rawArgs: argv });
653
+ process.exitCode ??= 0;
654
+ } catch (error) {
655
+ if (error instanceof TakoError3) {
656
+ reporter.error(renderError(error));
657
+ process.exitCode = 1;
658
+ } else if (error instanceof Error && error.name === "CLIError") {
659
+ reporter.error(error.message);
660
+ process.exitCode = 1;
661
+ } else {
662
+ reporter.error("internal error (this is a bug):");
663
+ console.error(error);
664
+ process.exitCode = 1;
665
+ }
666
+ }
667
+ }
668
+ export {
669
+ ConfigExistsError,
670
+ ConsoleReporter,
671
+ comparePlanToDisk,
672
+ renderError,
673
+ runCli
674
+ };
675
+ //# sourceMappingURL=index.js.map