@kubb/cli 5.0.0-beta.37 → 5.0.0-beta.39

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.
Files changed (85) hide show
  1. package/dist/{agent-B8oJFhcN.js → agent-CLspGfSn.js} +4 -4
  2. package/dist/{agent-B8oJFhcN.js.map → agent-CLspGfSn.js.map} +1 -1
  3. package/dist/{agent-DtuTV_Qk.cjs → agent-RdNQgRXD.cjs} +4 -4
  4. package/dist/{agent-DtuTV_Qk.cjs.map → agent-RdNQgRXD.cjs.map} +1 -1
  5. package/dist/{constants-CYxk4aNm.js → constants-84a47qA-.js} +2 -6
  6. package/dist/constants-84a47qA-.js.map +1 -0
  7. package/dist/{constants-CAKUpLcQ.cjs → constants-BtmponZ3.cjs} +1 -11
  8. package/dist/constants-BtmponZ3.cjs.map +1 -0
  9. package/dist/{generate-BvaMqrBk.cjs → generate-DK1pLJMi.cjs} +3 -4
  10. package/dist/generate-DK1pLJMi.cjs.map +1 -0
  11. package/dist/{generate-CzTjeiji.js → generate-Db0pJmbG.js} +3 -4
  12. package/dist/{generate-CzTjeiji.js.map → generate-Db0pJmbG.js.map} +1 -1
  13. package/dist/index.cjs +9 -9
  14. package/dist/index.cjs.map +1 -1
  15. package/dist/index.js +9 -9
  16. package/dist/index.js.map +1 -1
  17. package/dist/{init-CaMeuE1-.js → init-DKMwmbmx.js} +2 -2
  18. package/dist/{init-CaMeuE1-.js.map → init-DKMwmbmx.js.map} +1 -1
  19. package/dist/{init-C59u3T68.cjs → init-D_MQBDVz.cjs} +2 -2
  20. package/dist/{init-C59u3T68.cjs.map → init-D_MQBDVz.cjs.map} +1 -1
  21. package/dist/{mcp-Ca3ZcpKB.js → mcp-DqNyN0cN.js} +3 -3
  22. package/dist/{mcp-Ca3ZcpKB.js.map → mcp-DqNyN0cN.js.map} +1 -1
  23. package/dist/{mcp-D4NMV9lk.cjs → mcp-DvEeDWlW.cjs} +3 -3
  24. package/dist/{mcp-D4NMV9lk.cjs.map → mcp-DvEeDWlW.cjs.map} +1 -1
  25. package/dist/package-C8u_WvqI.js +6 -0
  26. package/dist/package-C8u_WvqI.js.map +1 -0
  27. package/dist/{package-DQFf9DB2.cjs → package-CusjBrSS.cjs} +2 -2
  28. package/dist/package-CusjBrSS.cjs.map +1 -0
  29. package/dist/{run-DJxYClJV.js → run-BQ3Qj0xB.js} +4 -4
  30. package/dist/run-BQ3Qj0xB.js.map +1 -0
  31. package/dist/{run-BvXxelGR.js → run-BQzoaxjR.js} +3 -3
  32. package/dist/run-BQzoaxjR.js.map +1 -0
  33. package/dist/{run-Ca2h07rN.js → run-BabEDDqN.js} +130 -369
  34. package/dist/run-BabEDDqN.js.map +1 -0
  35. package/dist/{run-BFEK9md9.js → run-CGf0KEts.js} +3 -3
  36. package/dist/run-CGf0KEts.js.map +1 -0
  37. package/dist/{run-CK8Cvq6n.cjs → run-CJUmJcbC.cjs} +130 -369
  38. package/dist/run-CJUmJcbC.cjs.map +1 -0
  39. package/dist/{run-Bz9IFMWg.cjs → run-CkTpemme.cjs} +3 -3
  40. package/dist/run-CkTpemme.cjs.map +1 -0
  41. package/dist/{run-BQZyg7If.cjs → run-Cl4SrSob.cjs} +3 -3
  42. package/dist/run-Cl4SrSob.cjs.map +1 -0
  43. package/dist/{run-BFv6avA_.cjs → run-D-s2LdlW.cjs} +5 -5
  44. package/dist/run-D-s2LdlW.cjs.map +1 -0
  45. package/dist/{validate-B_wfDSHQ.cjs → validate-CkW_AKZp.cjs} +3 -3
  46. package/dist/{validate-B_wfDSHQ.cjs.map → validate-CkW_AKZp.cjs.map} +1 -1
  47. package/dist/{validate-BEEerg2-.js → validate-jRewvR0c.js} +3 -3
  48. package/dist/{validate-BEEerg2-.js.map → validate-jRewvR0c.js.map} +1 -1
  49. package/package.json +7 -7
  50. package/src/commands/generate.ts +2 -3
  51. package/src/constants.ts +0 -15
  52. package/src/index.ts +2 -2
  53. package/src/loggers/clackLogger.ts +4 -6
  54. package/src/loggers/githubActionsLogger.ts +3 -3
  55. package/src/loggers/plainLogger.ts +2 -3
  56. package/src/loggers/utils.ts +29 -28
  57. package/src/runners/agent/run.ts +2 -2
  58. package/src/runners/generate/run.ts +21 -17
  59. package/src/runners/generate/utils.ts +2 -2
  60. package/src/runners/mcp/run.ts +2 -2
  61. package/src/runners/validate/run.ts +2 -2
  62. package/dist/constants-CAKUpLcQ.cjs.map +0 -1
  63. package/dist/constants-CYxk4aNm.js.map +0 -1
  64. package/dist/generate-BvaMqrBk.cjs.map +0 -1
  65. package/dist/package-DQFf9DB2.cjs.map +0 -1
  66. package/dist/package-DUwUSFeL.js +0 -6
  67. package/dist/package-DUwUSFeL.js.map +0 -1
  68. package/dist/run-BFEK9md9.js.map +0 -1
  69. package/dist/run-BFv6avA_.cjs.map +0 -1
  70. package/dist/run-BQZyg7If.cjs.map +0 -1
  71. package/dist/run-BvXxelGR.js.map +0 -1
  72. package/dist/run-Bz9IFMWg.cjs.map +0 -1
  73. package/dist/run-CK8Cvq6n.cjs.map +0 -1
  74. package/dist/run-Ca2h07rN.js.map +0 -1
  75. package/dist/run-DJxYClJV.js.map +0 -1
  76. package/dist/telemetry-B80oJfxR.cjs +0 -280
  77. package/dist/telemetry-B80oJfxR.cjs.map +0 -1
  78. package/dist/telemetry-ueaMzs_c.js +0 -243
  79. package/dist/telemetry-ueaMzs_c.js.map +0 -1
  80. package/src/loggers/diagnostics.ts +0 -77
  81. package/src/reporters/cliReporter.ts +0 -89
  82. package/src/reporters/fileReporter.ts +0 -103
  83. package/src/reporters/jsonReporter.ts +0 -15
  84. package/src/reporters/report.ts +0 -84
  85. package/src/telemetry.ts +0 -280
@@ -1,22 +1,21 @@
1
1
  const require_chunk = require("./chunk-Bx3C2hgW.cjs");
2
2
  const require_errors = require("./errors-DykI11xo.cjs");
3
- const require_telemetry = require("./telemetry-B80oJfxR.cjs");
4
3
  const require_shell = require("./shell-Lh-vLWwH.cjs");
5
- const require_package = require("./package-DQFf9DB2.cjs");
6
- const require_constants = require("./constants-CAKUpLcQ.cjs");
4
+ const require_package = require("./package-CusjBrSS.cjs");
5
+ const require_constants = require("./constants-BtmponZ3.cjs");
7
6
  let node_util = require("node:util");
8
7
  let node_events = require("node:events");
9
8
  let node_crypto = require("node:crypto");
10
9
  let node_child_process = require("node:child_process");
11
10
  let node_fs = require("node:fs");
12
- let node_fs_promises = require("node:fs/promises");
13
11
  let node_path = require("node:path");
14
12
  node_path = require_chunk.__toESM(node_path, 1);
13
+ let node_dns = require("node:dns");
14
+ let _kubb_core = require("@kubb/core");
15
15
  let node_process = require("node:process");
16
16
  node_process = require_chunk.__toESM(node_process, 1);
17
17
  let _clack_prompts = require("@clack/prompts");
18
18
  _clack_prompts = require_chunk.__toESM(_clack_prompts, 1);
19
- let _kubb_core = require("@kubb/core");
20
19
  let cosmiconfig = require("cosmiconfig");
21
20
  let jiti = require("jiti");
22
21
  let tinyexec = require("tinyexec");
@@ -288,32 +287,6 @@ function getIntro({ title, description, version, areEyesOpen }) {
288
287
  `;
289
288
  }
290
289
  /**
291
- * ANSI color names used by {@link randomCliColor} for deterministic terminal coloring.
292
- */
293
- const randomColors = [
294
- "black",
295
- "red",
296
- "green",
297
- "yellow",
298
- "blue",
299
- "white",
300
- "magenta",
301
- "cyan",
302
- "gray"
303
- ];
304
- /**
305
- * Wraps `text` in a deterministic ANSI color derived from the text's SHA-256 hash.
306
- *
307
- * @example
308
- * ```ts
309
- * randomCliColor('petstore') // '\x1b[33m' + 'petstore' + '\x1b[39m' (always the same color for 'petstore')
310
- * ```
311
- */
312
- function randomCliColor(text) {
313
- if (!text) return "";
314
- return (0, node_util.styleText)(randomColors[(0, node_crypto.createHash)("sha256").update(text).digest().readUInt32BE(0) % randomColors.length] ?? "white", text);
315
- }
316
- /**
317
290
  * Formats a millisecond duration with a threshold-based ANSI color.
318
291
  * `≤ 500 ms` → green · `≤ 1 000 ms` → yellow · `> 1 000 ms` → red.
319
292
  *
@@ -331,6 +304,55 @@ function formatMsWithColor(ms) {
331
304
  return (0, node_util.styleText)("red", formatted);
332
305
  }
333
306
  //#endregion
307
+ //#region ../../internals/utils/src/env.ts
308
+ /**
309
+ * Returns `true` when running inside a GitHub Actions workflow.
310
+ *
311
+ * @example
312
+ * ```ts
313
+ * if (isGitHubActions()) {
314
+ * core.setOutput('result', 'ok')
315
+ * }
316
+ * ```
317
+ */
318
+ function isGitHubActions() {
319
+ return !!process.env.GITHUB_ACTIONS;
320
+ }
321
+ /**
322
+ * Returns `true` when the process is running in a CI environment.
323
+ * Covers GitHub Actions, GitLab CI, CircleCI, Travis CI, Jenkins, Bitbucket,
324
+ * TeamCity, Buildkite, and Azure Pipelines.
325
+ *
326
+ * @example
327
+ * ```ts
328
+ * if (isCIEnvironment()) {
329
+ * logger.level = 'error'
330
+ * }
331
+ * ```
332
+ */
333
+ function isCIEnvironment() {
334
+ return !!(process.env.CI || process.env.GITHUB_ACTIONS || process.env.GITLAB_CI || process.env.BITBUCKET_BUILD_NUMBER || process.env.JENKINS_URL || process.env.CIRCLECI || process.env.TRAVIS || process.env.TEAMCITY_VERSION || process.env.BUILDKITE || process.env.TF_BUILD);
335
+ }
336
+ /**
337
+ * Returns `true` when the process has an interactive TTY with a valid terminal
338
+ * width and is not running in CI.
339
+ *
340
+ * Some IDE-embedded terminals report `isTTY = true` but set `columns` to `0`,
341
+ * which breaks clack's box-drawing helpers (they call `String.prototype.repeat`
342
+ * with a negative count and throw a `RangeError`). We therefore require a
343
+ * positive column count before declaring the TTY usable.
344
+ *
345
+ * @example
346
+ * ```ts
347
+ * if (canUseTTY()) {
348
+ * renderProgressBar()
349
+ * }
350
+ * ```
351
+ */
352
+ function canUseTTY() {
353
+ return !!process.stdout.isTTY && (process.stdout.columns ?? 0) > 0 && !isCIEnvironment();
354
+ }
355
+ //#endregion
334
356
  //#region ../../internals/utils/src/formatters.ts
335
357
  /**
336
358
  * CLI command descriptors for each supported code formatter.
@@ -395,62 +417,8 @@ async function detectFormatter() {
395
417
  return null;
396
418
  }
397
419
  //#endregion
398
- //#region ../../internals/utils/src/fs.ts
399
- /**
400
- * Writes `data` to `path`, trimming leading/trailing whitespace before saving.
401
- * Skips the write when the trimmed content is empty or identical to what is already on disk.
402
- * Creates any missing parent directories automatically.
403
- * When `sanity` is `true`, re-reads the file after writing and throws if the content does not match.
404
- *
405
- * @example
406
- * ```ts
407
- * await write('./src/Pet.ts', source) // writes and returns trimmed content
408
- * await write('./src/Pet.ts', source) // null — file unchanged
409
- * await write('./src/Pet.ts', ' ') // null — empty content skipped
410
- * ```
411
- */
412
- async function write(path, data, options = {}) {
413
- const trimmed = data.trim();
414
- if (trimmed === "") return null;
415
- const resolved = (0, node_path.resolve)(path);
416
- if (typeof Bun !== "undefined") {
417
- const file = Bun.file(resolved);
418
- if ((await file.exists() ? await file.text() : null) === trimmed) return null;
419
- await Bun.write(resolved, trimmed);
420
- return trimmed;
421
- }
422
- try {
423
- if (await (0, node_fs_promises.readFile)(resolved, { encoding: "utf-8" }) === trimmed) return null;
424
- } catch {}
425
- await (0, node_fs_promises.mkdir)((0, node_path.dirname)(resolved), { recursive: true });
426
- await (0, node_fs_promises.writeFile)(resolved, trimmed, { encoding: "utf-8" });
427
- if (options.sanity) {
428
- const savedData = await (0, node_fs_promises.readFile)(resolved, { encoding: "utf-8" });
429
- if (savedData !== trimmed) throw new Error(`Sanity check failed for ${path}\n\nData[${data.length}]:\n${data}\n\nSaved[${savedData.length}]:\n${savedData}\n`);
430
- return savedData;
431
- }
432
- return trimmed;
433
- }
434
- //#endregion
435
420
  //#region ../../internals/utils/src/linters.ts
436
421
  /**
437
- * Collects all files under `dir` recursively using Node's built-in fs APIs.
438
- *
439
- * Passing explicit file paths to oxlint (instead of a directory) bypasses
440
- * oxlint's `.gitignore`-aware directory traversal, which would otherwise skip
441
- * files that are listed in `.gitignore` (e.g. generated output directories).
442
- */
443
- function findLintableFiles(dir) {
444
- try {
445
- return (0, node_fs.readdirSync)(dir, {
446
- withFileTypes: true,
447
- recursive: true
448
- }).filter((d) => d.isFile()).map((d) => `${d.parentPath}/${d.name}`);
449
- } catch {
450
- return [];
451
- }
452
- }
453
- /**
454
422
  * CLI command descriptors for each supported linter.
455
423
  *
456
424
  * Each entry contains the executable `command`, an `args` factory that maps an
@@ -474,7 +442,11 @@ const linters = {
474
442
  },
475
443
  oxlint: {
476
444
  command: "oxlint",
477
- args: (outputPath) => ["--fix", ...findLintableFiles(outputPath)],
445
+ args: (outputPath) => [
446
+ "--fix",
447
+ "--no-ignore",
448
+ outputPath
449
+ ],
478
450
  errorMessage: "Oxlint not found"
479
451
  }
480
452
  };
@@ -509,261 +481,50 @@ async function detectLinter() {
509
481
  return null;
510
482
  }
511
483
  //#endregion
512
- //#region src/reporters/report.ts
484
+ //#region ../../internals/utils/src/network.ts
513
485
  /**
514
- * Builds the normalized {@link Report} for one config from its {@link GenerationResult}. Splits the
515
- * diagnostics into problems and per-plugin timings (slowest first) and derives the plugin and issue
516
- * counts, so every reporter renders the same data.
486
+ * Well-known stable domains used as DNS probes to check internet connectivity.
517
487
  */
518
- function buildReport(result) {
519
- const { config, diagnostics, filesCreated, status, hrStart } = result;
520
- const failed = _kubb_core.Diagnostics.failedPlugins(diagnostics);
521
- const total = config.plugins?.length ?? 0;
522
- const counts = _kubb_core.Diagnostics.count(diagnostics);
523
- const problems = diagnostics.filter(_kubb_core.isProblemDiagnostic);
524
- const timings = diagnostics.filter(_kubb_core.isPerformanceDiagnostic).sort((a, b) => b.duration - a.duration).map((diagnostic) => ({
525
- plugin: diagnostic.plugin,
526
- durationMs: diagnostic.duration
527
- }));
528
- return {
529
- name: config.name ?? "",
530
- status,
531
- plugins: {
532
- passed: total - failed.length,
533
- failed,
534
- total
535
- },
536
- counts,
537
- filesCreated,
538
- durationMs: getElapsedMs(hrStart),
539
- output: (0, node_path.resolve)(config.root, config.output.path),
540
- timings,
541
- diagnostics: problems.map((diagnostic) => _kubb_core.Diagnostics.serialize(diagnostic))
542
- };
543
- }
544
- //#endregion
545
- //#region src/reporters/cliReporter.ts
546
- /**
547
- * Builds the vitest/jest-style summary for one {@link Report}: right-aligned dim labels with
548
- * `N passed (total)` counts, and a per-plugin `Timings` section when `showTimings`.
549
- */
550
- function buildSummaryLines(report, { showTimings }) {
551
- const { status, plugins, counts, filesCreated, durationMs, output, timings } = report;
552
- const rows = [];
553
- rows.push(["Plugins", status === "success" ? `${(0, node_util.styleText)("green", `${plugins.passed} passed`)} (${plugins.total})` : `${(0, node_util.styleText)("green", `${plugins.passed} passed`)} | ${(0, node_util.styleText)("red", `${plugins.failed.length} failed`)} (${plugins.total})`]);
554
- if (status === "failed" && plugins.failed.length > 0) rows.push(["Failed", plugins.failed.map((name) => randomCliColor(name)).join(", ")]);
555
- if (counts.errors > 0 || counts.warnings > 0) {
556
- const issues = [counts.errors > 0 ? (0, node_util.styleText)("red", `${counts.errors} ${counts.errors === 1 ? "error" : "errors"}`) : void 0, counts.warnings > 0 ? (0, node_util.styleText)("yellow", `${counts.warnings} ${counts.warnings === 1 ? "warning" : "warnings"}`) : void 0].filter(Boolean).join(" | ");
557
- rows.push(["Issues", issues]);
558
- }
559
- rows.push(["Files", `${(0, node_util.styleText)("green", String(filesCreated))} generated`]);
560
- rows.push(["Duration", (0, node_util.styleText)("green", formatMs(durationMs))]);
561
- rows.push(["Output", output]);
562
- const labelWidth = Math.max(...rows.map(([label]) => label.length), timings.length > 0 ? 7 : 0);
563
- const lines = rows.map(([label, value]) => `${(0, node_util.styleText)("dim", label.padStart(labelWidth))} ${value}`);
564
- if (showTimings && timings.length > 0) {
565
- const nameWidth = Math.max(0, ...timings.map((timing) => timing.plugin.length));
566
- const indent = " ".repeat(labelWidth + 2);
567
- lines.push((0, node_util.styleText)("dim", "Timings".padStart(labelWidth)));
568
- for (const timing of timings) {
569
- const timeStr = formatMs(timing.durationMs);
570
- const barLength = Math.min(Math.ceil(timing.durationMs / 100), 10);
571
- const bar = (0, node_util.styleText)("dim", "█".repeat(barLength));
572
- lines.push(`${indent}${(0, node_util.styleText)("dim", "•")} ${timing.plugin.padEnd(nameWidth)} ${bar} ${timeStr}`);
573
- }
574
- }
575
- return lines;
576
- }
577
- /**
578
- * Renders the summary as plain `console.log` lines so it works in every CLI (no clack/TTY
579
- * dependency): a blank line, the config name colored by status, then the summary rows.
580
- */
581
- function renderSummary(lines, { title, status }) {
582
- console.log("");
583
- if (title) console.log((0, node_util.styleText)(status === "failed" ? "red" : "green", title));
584
- for (const line of lines) console.log(line);
585
- }
586
- /**
587
- * The default `cli` reporter. Renders the {@link Report} for each config as it finishes, independent
588
- * of the live logger view. Suppressed at `silent`; the `verbose` level adds the per-plugin timings.
589
- */
590
- const cliReporter = (0, _kubb_core.createReporter)({
591
- name: "cli",
592
- report(result, { logLevel }) {
593
- if (logLevel <= _kubb_core.logLevel.silent) return;
594
- const report = buildReport(result);
595
- renderSummary(buildSummaryLines(report, { showTimings: logLevel >= _kubb_core.logLevel.verbose }), {
596
- title: report.name,
597
- status: report.status
598
- });
599
- }
600
- });
601
- //#endregion
602
- //#region src/loggers/diagnostics.ts
603
- /**
604
- * Glyph and accent color per severity, matching the miette/oxlint convention
605
- * (`×` error, `⚠` warning, `ℹ` advice).
606
- */
607
- const severityStyle = {
608
- error: {
609
- glyph: "×",
610
- color: "red"
611
- },
612
- warning: {
613
- glyph: "⚠",
614
- color: "yellow"
615
- },
616
- info: {
617
- glyph: "ℹ",
618
- color: "blue"
619
- }
620
- };
621
- /**
622
- * The colored, bold severity glyph (`×`, `⚠`, `ℹ`) on its own. Pass it as clack's
623
- * `symbol` so clack owns the gutter and adds the bar to the continuation lines,
624
- * instead of baking the glyph into the message text.
625
- */
626
- function diagnosticSymbol(severity) {
627
- const { glyph, color } = severityStyle[severity];
628
- return (0, node_util.styleText)(color, (0, node_util.styleText)("bold", glyph));
629
- }
630
- /**
631
- * The `plugin(CODE): message` headline, without the leading severity glyph.
632
- */
633
- function diagnosticHeadline(diagnostic) {
634
- const { code, severity, message } = diagnostic;
635
- const plugin = (0, _kubb_core.isProblemDiagnostic)(diagnostic) ? diagnostic.plugin : void 0;
636
- const { color } = severityStyle[severity];
637
- return `${(0, node_util.styleText)(color, (0, node_util.styleText)("bold", plugin ? `${plugin}(${code})` : code))}: ${message}`;
638
- }
639
- /**
640
- * The detail lines below the headline: optional `at <pointer>`, `help:`, and
641
- * `docs:`. OpenAPI has no line/column, so the location is the JSON pointer the
642
- * adapter built. Each line keeps a two-space indent so it sits under the headline.
643
- */
644
- function diagnosticDetails(diagnostic) {
645
- const { code } = diagnostic;
646
- const problem = (0, _kubb_core.isProblemDiagnostic)(diagnostic) ? diagnostic : void 0;
647
- const location = problem?.location;
648
- const help = problem?.help;
649
- const lines = [];
650
- if (location && "pointer" in location) lines.push(` ${(0, node_util.styleText)("dim", "at")} ${(0, node_util.styleText)("cyan", location.pointer)}`);
651
- if (help) lines.push(` ${(0, node_util.styleText)("cyan", "help:")} ${help}`);
652
- if (code !== _kubb_core.diagnosticCode.unknown) lines.push(` ${(0, node_util.styleText)("dim", "docs:")} ${(0, node_util.styleText)("cyan", _kubb_core.Diagnostics.docsUrl(code))}`);
653
- return lines;
654
- }
488
+ const TEST_DOMAINS = [
489
+ "dns.google.com",
490
+ "cloudflare.com",
491
+ "one.one.one.one"
492
+ ];
655
493
  /**
656
- * Renders a {@link Diagnostic} in the oxlint style as a self-contained block: a
657
- * plugin(CODE): message` header followed by the {@link diagnosticDetails}.
658
- * Use this where clack's gutter is not available (plain, file output); clack
659
- * loggers pass {@link diagnosticSymbol}, {@link diagnosticHeadline}, and
660
- * {@link diagnosticDetails} to `clack.log.message` instead.
494
+ * Returns `true` when the system has internet connectivity.
495
+ * Probes DNS resolution against well-known stable domains.
661
496
  *
662
497
  * @example
663
498
  * ```ts
664
- * formatDiagnostic({ code: 'KUBB_REF_NOT_FOUND', severity: 'error', message: 'Could not find Pet', help: 'Add Pet under components.schemas.', plugin: '@kubb/plugin-zod', location: { kind: 'schema', pointer: '#/components/schemas/Pet' } })
499
+ * if (await isOnline()) {
500
+ * await fetchLatestVersion()
501
+ * }
665
502
  * ```
666
503
  */
667
- function formatDiagnostic(diagnostic) {
668
- return [`${diagnosticSymbol(diagnostic.severity)} ${diagnosticHeadline(diagnostic)}`, ...diagnosticDetails(diagnostic)];
669
- }
670
- //#endregion
671
- //#region src/reporters/fileReporter.ts
672
- /**
673
- * Builds the `## Summary` section: the same counts the cli and json reporters expose, as a list of
674
- * `label value` rows with the labels padded to a common width.
675
- */
676
- function buildSummarySection(report) {
677
- const { status, plugins, counts, filesCreated, durationMs, output } = report;
678
- const rows = [["Status", status], ["Plugins", status === "success" ? `${plugins.passed} passed (${plugins.total})` : `${plugins.passed} passed | ${plugins.failed.length} failed (${plugins.total})`]];
679
- if (plugins.failed.length > 0) rows.push(["Failed", plugins.failed.join(", ")]);
680
- rows.push(["Issues", `${counts.errors} errors | ${counts.warnings} warnings | ${counts.infos} infos`]);
681
- rows.push(["Files", `${filesCreated} generated`]);
682
- rows.push(["Duration", formatMs(durationMs)]);
683
- rows.push(["Output", output]);
684
- const labelWidth = Math.max(...rows.map(([label]) => label.length));
685
- return [
686
- "## Summary",
687
- "",
688
- ...rows.map(([label, value]) => ` ${label.padEnd(labelWidth)} ${value}`)
689
- ];
690
- }
691
- /**
692
- * Builds the `## Problems` section: each problem rendered in the miette block format, blocks
693
- * separated by a blank line. Returns an empty array when there are no problems, so the caller
694
- * can drop the heading.
695
- */
696
- function buildProblemSection(diagnostics) {
697
- const problems = diagnostics.filter(_kubb_core.isProblemDiagnostic);
698
- if (problems.length === 0) return [];
699
- return [
700
- "## Problems",
701
- "",
702
- problems.map((diagnostic) => formatDiagnostic(diagnostic).join("\n")).join("\n\n")
703
- ];
704
- }
705
- /**
706
- * Builds the `## Timings` section from a {@link Report}: one `plugin duration` row per record,
707
- * slowest first with the plugin names left-aligned and the durations right-aligned. Returns an
708
- * empty array when there are no timings.
709
- */
710
- function buildTimingSection(report) {
711
- const { timings } = report;
712
- if (timings.length === 0) return [];
713
- const nameWidth = Math.max(...timings.map((timing) => timing.plugin.length));
714
- const durations = timings.map((timing) => formatMs(timing.durationMs));
715
- const durationWidth = Math.max(...durations.map((duration) => duration.length));
716
- return [
717
- "## Timings",
718
- "",
719
- ...timings.map((timing, index) => ` ${timing.plugin.padEnd(nameWidth)} ${durations[index].padStart(durationWidth)}`)
720
- ];
504
+ async function isOnline() {
505
+ for (const domain of TEST_DOMAINS) try {
506
+ await node_dns.promises.resolve(domain);
507
+ return true;
508
+ } catch {}
509
+ return false;
721
510
  }
722
511
  /**
723
- * The `file` reporter. Writes a config's {@link Report} to `.kubb/kubb-<name>-<timestamp>.log` as a
724
- * plain-text document: a `# <name> — <timestamp>` header, a `## Summary` with the same counts the
725
- * cli and json reporters expose, a `## Problems` section in the miette block format, and a
726
- * `## Timings` section. Selected with `--reporter file` (or `reporters: ['file']`), replacing the
727
- * old `--debug` flag.
512
+ * Executes `fn` only when the system is online. Returns `null` when offline or on error.
728
513
  *
729
- * @note Unlike the streaming logger it replaced, it captures the collected diagnostics once a
730
- * config finishes, not the live `kubb:info`/`kubb:plugin` event stream. Color is stripped so the
731
- * file stays plain text even when the run is attached to a TTY.
732
- */
733
- const fileReporter = (0, _kubb_core.createReporter)({
734
- name: "file",
735
- async report(result) {
736
- const { diagnostics, config } = result;
737
- if (diagnostics.length === 0) return;
738
- const report = buildReport(result);
739
- const content = (0, node_util.stripVTControlCharacters)([config.name ? `# ${config.name} — ${(/* @__PURE__ */ new Date()).toISOString()}` : `# ${(/* @__PURE__ */ new Date()).toISOString()}`, ...[
740
- buildSummarySection(report),
741
- buildProblemSection(diagnostics),
742
- buildTimingSection(report)
743
- ].filter((section) => section.length > 0).map((section) => section.join("\n"))].join("\n\n"));
744
- const baseName = `${[
745
- "kubb",
746
- config.name,
747
- Date.now()
748
- ].filter(Boolean).join("-")}.log`;
749
- const pathName = (0, node_path.resolve)(node_process.default.cwd(), ".kubb", baseName);
750
- await write(pathName, `${content}\n`);
751
- console.error(`Debug log written to ${(0, node_path.relative)(node_process.default.cwd(), pathName)}`);
752
- }
753
- });
754
- //#endregion
755
- //#region src/reporters/jsonReporter.ts
756
- /**
757
- * The `json` reporter. Writes the {@link Report} for each config to stdout as JSON, for CI tooling.
758
- * The terminal reporter is suppressed while this is active so stdout stays valid JSON.
514
+ * @example
515
+ * ```ts
516
+ * const version = await executeIfOnline(() => fetchLatestVersion('kubb'))
517
+ * // null when offline
518
+ * ```
759
519
  */
760
- const jsonReporter = (0, _kubb_core.createReporter)({
761
- name: "json",
762
- report(result) {
763
- const report = buildReport(result);
764
- node_process.default.stdout.write(`${JSON.stringify(report, null, 2)}\n`);
520
+ async function executeIfOnline(fn) {
521
+ if (!await isOnline()) return null;
522
+ try {
523
+ return await fn();
524
+ } catch {
525
+ return null;
765
526
  }
766
- });
527
+ }
767
528
  //#endregion
768
529
  //#region src/loggers/clackLogger.ts
769
530
  /**
@@ -880,7 +641,7 @@ const clackLogger = (0, _kubb_core.defineLogger)({
880
641
  if (logLevel <= _kubb_core.logLevel.silent && diagnostic.severity !== "error") return;
881
642
  stopSpinner();
882
643
  stopActiveProgress();
883
- if ((0, _kubb_core.isUpdateDiagnostic)(diagnostic)) {
644
+ if (_kubb_core.Diagnostics.isUpdate(diagnostic)) {
884
645
  _clack_prompts.box(`\`v${diagnostic.currentVersion}\` → \`v${diagnostic.latestVersion}\`
885
646
  Run \`npm install -g @kubb/cli\` to update`, "Update available for `Kubb`", {
886
647
  width: "auto",
@@ -892,7 +653,8 @@ Run \`npm install -g @kubb/cli\` to update`, "Update available for `Kubb`", {
892
653
  });
893
654
  return;
894
655
  }
895
- _clack_prompts.log.message([diagnosticHeadline(diagnostic), ...diagnosticDetails(diagnostic)], { symbol: diagnosticSymbol(diagnostic.severity) });
656
+ const { symbol, headline, details } = _kubb_core.Diagnostics.format(diagnostic);
657
+ _clack_prompts.log.message([headline, ...details], { symbol });
896
658
  });
897
659
  context.on("kubb:lifecycle:start", async ({ version }) => {
898
660
  console.log(`\n${getIntro({
@@ -1139,7 +901,7 @@ const githubActionsLogger = (0, _kubb_core.defineLogger)({
1139
901
  context.on("kubb:diagnostic", ({ diagnostic }) => {
1140
902
  closeAllGroups();
1141
903
  if (logLevel <= _kubb_core.logLevel.silent && diagnostic.severity !== "error") return;
1142
- if (!(0, _kubb_core.isProblemDiagnostic)(diagnostic)) {
904
+ if (!_kubb_core.Diagnostics.isProblem(diagnostic)) {
1143
905
  console.log(`::notice::${diagnostic.message}`);
1144
906
  return;
1145
907
  }
@@ -1147,7 +909,7 @@ const githubActionsLogger = (0, _kubb_core.defineLogger)({
1147
909
  if (diagnostic.location && "pointer" in diagnostic.location) parts.push(`(at ${diagnostic.location.pointer})`);
1148
910
  if (diagnostic.plugin) parts.push(`[plugin: ${diagnostic.plugin}]`);
1149
911
  if (diagnostic.help) parts.push(`help: ${diagnostic.help}`);
1150
- if (diagnostic.code !== _kubb_core.diagnosticCode.unknown) parts.push(`docs: ${_kubb_core.Diagnostics.docsUrl(diagnostic.code)}`);
912
+ if (diagnostic.code !== _kubb_core.Diagnostics.code.unknown) parts.push(`docs: ${_kubb_core.Diagnostics.docsUrl(diagnostic.code)}`);
1151
913
  console.error(`::error::${parts.join(" ")}`);
1152
914
  });
1153
915
  context.on("kubb:lifecycle:start", ({ version }) => {
@@ -1311,7 +1073,7 @@ const plainLogger = (0, _kubb_core.defineLogger)({
1311
1073
  });
1312
1074
  context.on("kubb:diagnostic", ({ diagnostic }) => {
1313
1075
  if (logLevel <= _kubb_core.logLevel.silent && diagnostic.severity !== "error") return;
1314
- console.log(getMessage(formatDiagnostic(diagnostic).join("\n")));
1076
+ console.log(getMessage(_kubb_core.Diagnostics.formatLines(diagnostic).join("\n")));
1315
1077
  });
1316
1078
  context.on("kubb:lifecycle:start", ({ version }) => {
1317
1079
  console.log(`Kubb CLI v${version}`);
@@ -1470,8 +1232,8 @@ function formatCommandWithArgs(command, args) {
1470
1232
  return args?.length ? `${command} ${args.join(" ")}` : command;
1471
1233
  }
1472
1234
  function detectLogger() {
1473
- if (require_telemetry.isGitHubActions()) return "github-actions";
1474
- if (require_telemetry.canUseTTY()) return "clack";
1235
+ if (isGitHubActions()) return "github-actions";
1236
+ if (canUseTTY()) return "clack";
1475
1237
  return "plain";
1476
1238
  }
1477
1239
  const logMapper = {
@@ -1493,30 +1255,32 @@ function installReporter(context, reporter, ctx) {
1493
1255
  hrStart
1494
1256
  }, ctx);
1495
1257
  });
1258
+ if (reporter.drain) context.on("kubb:lifecycle:end", () => reporter.drain?.(ctx));
1496
1259
  }
1497
1260
  /**
1498
- * Installs the live logger (the TUI view) and the selected reporters (the output), returning the
1499
- * terminal logger's hook sink when one was installed. Loggers and reporters are independent: the
1500
- * `cli` selection activates the env logger plus the {@link cliReporter} summary.
1261
+ * Installs the live logger (the TUI view) and the given reporters (the output), returning the
1262
+ * terminal logger's hook sink when one was installed. The reporters are already selected by the
1263
+ * caller (the CLI maps `--reporter` to names via `selectReporters`); this only wires them.
1501
1264
  *
1502
- * The `json` reporter owns stdout, so the terminal logger and `cli` summary are suppressed whenever
1503
- * `json` is selected, even if `cli` is also listed.
1265
+ * Loggers and reporters are independent: the `cli` reporter also activates the env logger summary.
1266
+ * The `json` reporter owns stdout, so the live logger and the `cli` summary are suppressed whenever
1267
+ * `json` is among the reporters, even if `cli` is also listed.
1504
1268
  */
1505
1269
  async function setupReporters(context, { logLevel, reporters }) {
1506
- const unique = new Set(reporters.length ? reporters : ["cli"]);
1507
- const hasJson = unique.has("json");
1270
+ const hasJson = reporters.some((reporter) => reporter.name === "json");
1508
1271
  const ctx = { logLevel };
1509
1272
  let makeSink = null;
1510
- if (unique.has("cli") && !hasJson) {
1511
- const type = detectLogger();
1512
- const logger = logMapper[type];
1513
- if (!logger) throw new Error(`Unknown adapter type: ${type}`);
1514
- const sink = await logger.install(context, { logLevel });
1515
- makeSink = typeof sink === "function" ? sink : null;
1516
- installReporter(context, cliReporter, ctx);
1273
+ for (const reporter of reporters) {
1274
+ if (reporter.name === "cli") {
1275
+ if (hasJson) continue;
1276
+ const type = detectLogger();
1277
+ const logger = logMapper[type];
1278
+ if (!logger) throw new Error(`Unknown adapter type: ${type}`);
1279
+ const sink = await logger.install(context, { logLevel });
1280
+ makeSink = typeof sink === "function" ? sink : null;
1281
+ }
1282
+ installReporter(context, reporter, ctx);
1517
1283
  }
1518
- if (hasJson) installReporter(context, jsonReporter, ctx);
1519
- if (unique.has("file")) installReporter(context, fileReporter, ctx);
1520
1284
  return makeSink;
1521
1285
  }
1522
1286
  //#endregion
@@ -1645,18 +1409,14 @@ async function runHook({ id, command, args, commandWithArgs, hooks, stream = fal
1645
1409
  await emitEnd(true, null);
1646
1410
  } catch (err) {
1647
1411
  if (!(err instanceof tinyexec.NonZeroExitError)) {
1648
- const error = require_errors.toError(err);
1649
- await emitEnd(false, error);
1650
- await hooks.emit("kubb:error", { error });
1412
+ await emitEnd(false, require_errors.toError(err));
1651
1413
  return;
1652
1414
  }
1653
1415
  const stderr = err.output?.stderr ?? "";
1654
1416
  const stdout = err.output?.stdout ?? "";
1655
1417
  if (stderr) sink?.onStderr?.(stderr);
1656
1418
  if (stdout) sink?.onStdout?.(stdout);
1657
- const error = /* @__PURE__ */ new Error(`Hook execute failed: ${commandWithArgs}`);
1658
- await emitEnd(false, error);
1659
- await hooks.emit("kubb:error", { error });
1419
+ await emitEnd(false, /* @__PURE__ */ new Error(`Hook execute failed: ${commandWithArgs}`));
1660
1420
  }
1661
1421
  }
1662
1422
  /**
@@ -1719,7 +1479,7 @@ async function runToolPass({ toolValue, detect, toolMap, toolLabel, successPrefi
1719
1479
  }
1720
1480
  }
1721
1481
  let toolError;
1722
- if (resolvedTool && resolvedTool !== "auto" && resolvedTool in toolMap) {
1482
+ if (resolvedTool && resolvedTool !== "auto" && resolvedTool in toolMap && (0, node_fs.existsSync)(outputPath)) {
1723
1483
  const toolConfig = toolMap[resolvedTool];
1724
1484
  const hookId = (0, node_crypto.createHash)("sha256").update([configName, resolvedTool].filter(Boolean).join("-")).digest("hex");
1725
1485
  const successMessage = [
@@ -1787,7 +1547,7 @@ async function generate(options) {
1787
1547
  name: p.name,
1788
1548
  options: p.options
1789
1549
  }));
1790
- const reportTelemetry = (status) => require_telemetry.sendTelemetry(require_telemetry.buildTelemetryEvent({
1550
+ const reportTelemetry = (status) => _kubb_core.Telemetry.send(_kubb_core.Telemetry.build({
1791
1551
  command: "generate",
1792
1552
  kubbVersion: require_package.version,
1793
1553
  plugins: telemetryPlugins,
@@ -1796,8 +1556,8 @@ async function generate(options) {
1796
1556
  status
1797
1557
  }));
1798
1558
  for (const diagnostic of diagnostics) {
1799
- if (!(0, _kubb_core.isProblemDiagnostic)(diagnostic)) continue;
1800
- const unknown = (0, _kubb_core.narrowDiagnostic)(diagnostic, _kubb_core.diagnosticCode.unknown);
1559
+ if (!_kubb_core.Diagnostics.isProblem(diagnostic)) continue;
1560
+ const unknown = _kubb_core.Diagnostics.narrow(diagnostic, _kubb_core.Diagnostics.code.unknown);
1801
1561
  if (unknown) await hooks.emit("kubb:error", { error: unknown.cause ?? new Error(unknown.message) });
1802
1562
  else await _kubb_core.Diagnostics.emit(hooks, diagnostic);
1803
1563
  }
@@ -1816,7 +1576,7 @@ async function generate(options) {
1816
1576
  const outputPath = node_path.default.resolve(config.root, config.output.path);
1817
1577
  const outputDiagnostics = [];
1818
1578
  const toolPasses = [config.output.format && {
1819
- code: _kubb_core.diagnosticCode.formatFailed,
1579
+ code: _kubb_core.Diagnostics.code.formatFailed,
1820
1580
  toolValue: config.output.format,
1821
1581
  detect: detectFormatter,
1822
1582
  toolMap: formatters,
@@ -1826,7 +1586,7 @@ async function generate(options) {
1826
1586
  onStart: () => hooks.emit("kubb:format:start"),
1827
1587
  onEnd: () => hooks.emit("kubb:format:end")
1828
1588
  }, config.output.lint && {
1829
- code: _kubb_core.diagnosticCode.lintFailed,
1589
+ code: _kubb_core.Diagnostics.code.lintFailed,
1830
1590
  toolValue: config.output.lint,
1831
1591
  detect: detectLinter,
1832
1592
  toolMap: linters,
@@ -1867,7 +1627,7 @@ async function generate(options) {
1867
1627
  hooks.off("kubb:hook:end", onHookEnd);
1868
1628
  }
1869
1629
  for (const error of hookFailures) {
1870
- const diagnostic = outputDiagnostic(_kubb_core.diagnosticCode.hookFailed, "Post-generate hook", error);
1630
+ const diagnostic = outputDiagnostic(_kubb_core.Diagnostics.code.hookFailed, "Post-generate hook", error);
1871
1631
  outputDiagnostics.push(diagnostic);
1872
1632
  await _kubb_core.Diagnostics.emit(hooks, diagnostic);
1873
1633
  }
@@ -1905,7 +1665,7 @@ function outputDiagnostic(code, label, caughtError) {
1905
1665
  };
1906
1666
  }
1907
1667
  async function checkForUpdate(hooks) {
1908
- await require_telemetry.executeIfOnline(async () => {
1668
+ await executeIfOnline(async () => {
1909
1669
  try {
1910
1670
  const data = await (await fetch(require_constants.KUBB_NPM_PACKAGE_URL)).json();
1911
1671
  if (data.version && require_package.version < data.version) await _kubb_core.Diagnostics.emit(hooks, _kubb_core.Diagnostics.update({
@@ -1937,14 +1697,15 @@ async function run({ input, configPath, logLevel: logLevelKey, watch, reporters:
1937
1697
  } catch (error) {
1938
1698
  await setupReporters(hooks, {
1939
1699
  logLevel,
1940
- reporters: ["cli"]
1700
+ reporters: [_kubb_core.cliReporter]
1941
1701
  });
1942
1702
  await hooks.emit("kubb:error", { error: require_errors.toError(error) });
1943
1703
  node_process.default.exit(1);
1944
1704
  }
1705
+ const requestedNames = cliReporters?.length ? cliReporters : ["cli"];
1945
1706
  const makeSink = await setupReporters(hooks, {
1946
1707
  logLevel,
1947
- reporters: cliReporters?.length ? cliReporters : configs[0]?.reporters ?? ["cli"]
1708
+ reporters: (0, _kubb_core.selectReporters)(configs[0]?.reporters ?? [], requestedNames)
1948
1709
  });
1949
1710
  await hooks.emit("kubb:lifecycle:start", { version: require_package.version });
1950
1711
  await checkForUpdate(hooks);
@@ -1961,7 +1722,7 @@ async function run({ input, configPath, logLevel: logLevelKey, watch, reporters:
1961
1722
  });
1962
1723
  await hooks.emit("kubb:config:end", { configs });
1963
1724
  let anyFailed = false;
1964
- for (const config of configs) if ((0, _kubb_core.isInputPath)(config) && watch) await startWatcher([input || config.input.path], async (paths) => {
1725
+ for (const config of configs) if (config.input && "path" in config.input && watch) await startWatcher([input || config.input.path], async (paths) => {
1965
1726
  await generate({
1966
1727
  input,
1967
1728
  config,
@@ -1996,4 +1757,4 @@ async function run({ input, configPath, logLevel: logLevelKey, watch, reporters:
1996
1757
  //#endregion
1997
1758
  exports.run = run;
1998
1759
 
1999
- //# sourceMappingURL=run-CK8Cvq6n.cjs.map
1760
+ //# sourceMappingURL=run-CJUmJcbC.cjs.map