@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,19 +1,18 @@
1
1
  import "./chunk-CRm0XQPb.js";
2
2
  import { n as toCause, r as toError } from "./errors-CoxrNXaA.js";
3
- import { a as canUseTTY, i as executeIfOnline, o as isGitHubActions, r as sendTelemetry, t as buildTelemetryEvent } from "./telemetry-ueaMzs_c.js";
4
3
  import { n as tokenize } from "./shell-BrqyJdB7.js";
5
- import { t as version } from "./package-DUwUSFeL.js";
6
- import { i as WATCHER_IGNORED_PATHS, t as KUBB_NPM_PACKAGE_URL } from "./constants-CYxk4aNm.js";
7
- import { stripVTControlCharacters, styleText } from "node:util";
4
+ import { t as version } from "./package-C8u_WvqI.js";
5
+ import { r as WATCHER_IGNORED_PATHS, t as KUBB_NPM_PACKAGE_URL } from "./constants-84a47qA-.js";
6
+ import { styleText } from "node:util";
8
7
  import { EventEmitter } from "node:events";
9
8
  import { createHash } from "node:crypto";
10
9
  import { spawn } from "node:child_process";
11
- import { readdirSync } from "node:fs";
12
- import { mkdir, readFile, writeFile } from "node:fs/promises";
13
- import path, { dirname, relative, resolve } from "node:path";
10
+ import { existsSync } from "node:fs";
11
+ import path, { relative } from "node:path";
12
+ import { promises } from "node:dns";
13
+ import { Diagnostics, Telemetry, cliReporter, createKubb, defineLogger, logLevel, selectReporters } from "@kubb/core";
14
14
  import process$1 from "node:process";
15
15
  import * as clack from "@clack/prompts";
16
- import { Diagnostics, createKubb, createReporter, defineLogger, diagnosticCode, isInputPath, isPerformanceDiagnostic, isProblemDiagnostic, isUpdateDiagnostic, logLevel, narrowDiagnostic } from "@kubb/core";
17
16
  import { cosmiconfig } from "cosmiconfig";
18
17
  import { createJiti } from "jiti";
19
18
  import { NonZeroExitError, x } from "tinyexec";
@@ -285,32 +284,6 @@ function getIntro({ title, description, version, areEyesOpen }) {
285
284
  `;
286
285
  }
287
286
  /**
288
- * ANSI color names used by {@link randomCliColor} for deterministic terminal coloring.
289
- */
290
- const randomColors = [
291
- "black",
292
- "red",
293
- "green",
294
- "yellow",
295
- "blue",
296
- "white",
297
- "magenta",
298
- "cyan",
299
- "gray"
300
- ];
301
- /**
302
- * Wraps `text` in a deterministic ANSI color derived from the text's SHA-256 hash.
303
- *
304
- * @example
305
- * ```ts
306
- * randomCliColor('petstore') // '\x1b[33m' + 'petstore' + '\x1b[39m' (always the same color for 'petstore')
307
- * ```
308
- */
309
- function randomCliColor(text) {
310
- if (!text) return "";
311
- return styleText(randomColors[createHash("sha256").update(text).digest().readUInt32BE(0) % randomColors.length] ?? "white", text);
312
- }
313
- /**
314
287
  * Formats a millisecond duration with a threshold-based ANSI color.
315
288
  * `≤ 500 ms` → green · `≤ 1 000 ms` → yellow · `> 1 000 ms` → red.
316
289
  *
@@ -328,6 +301,55 @@ function formatMsWithColor(ms) {
328
301
  return styleText("red", formatted);
329
302
  }
330
303
  //#endregion
304
+ //#region ../../internals/utils/src/env.ts
305
+ /**
306
+ * Returns `true` when running inside a GitHub Actions workflow.
307
+ *
308
+ * @example
309
+ * ```ts
310
+ * if (isGitHubActions()) {
311
+ * core.setOutput('result', 'ok')
312
+ * }
313
+ * ```
314
+ */
315
+ function isGitHubActions() {
316
+ return !!process.env.GITHUB_ACTIONS;
317
+ }
318
+ /**
319
+ * Returns `true` when the process is running in a CI environment.
320
+ * Covers GitHub Actions, GitLab CI, CircleCI, Travis CI, Jenkins, Bitbucket,
321
+ * TeamCity, Buildkite, and Azure Pipelines.
322
+ *
323
+ * @example
324
+ * ```ts
325
+ * if (isCIEnvironment()) {
326
+ * logger.level = 'error'
327
+ * }
328
+ * ```
329
+ */
330
+ function isCIEnvironment() {
331
+ 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);
332
+ }
333
+ /**
334
+ * Returns `true` when the process has an interactive TTY with a valid terminal
335
+ * width and is not running in CI.
336
+ *
337
+ * Some IDE-embedded terminals report `isTTY = true` but set `columns` to `0`,
338
+ * which breaks clack's box-drawing helpers (they call `String.prototype.repeat`
339
+ * with a negative count and throw a `RangeError`). We therefore require a
340
+ * positive column count before declaring the TTY usable.
341
+ *
342
+ * @example
343
+ * ```ts
344
+ * if (canUseTTY()) {
345
+ * renderProgressBar()
346
+ * }
347
+ * ```
348
+ */
349
+ function canUseTTY() {
350
+ return !!process.stdout.isTTY && (process.stdout.columns ?? 0) > 0 && !isCIEnvironment();
351
+ }
352
+ //#endregion
331
353
  //#region ../../internals/utils/src/formatters.ts
332
354
  /**
333
355
  * CLI command descriptors for each supported code formatter.
@@ -392,62 +414,8 @@ async function detectFormatter() {
392
414
  return null;
393
415
  }
394
416
  //#endregion
395
- //#region ../../internals/utils/src/fs.ts
396
- /**
397
- * Writes `data` to `path`, trimming leading/trailing whitespace before saving.
398
- * Skips the write when the trimmed content is empty or identical to what is already on disk.
399
- * Creates any missing parent directories automatically.
400
- * When `sanity` is `true`, re-reads the file after writing and throws if the content does not match.
401
- *
402
- * @example
403
- * ```ts
404
- * await write('./src/Pet.ts', source) // writes and returns trimmed content
405
- * await write('./src/Pet.ts', source) // null — file unchanged
406
- * await write('./src/Pet.ts', ' ') // null — empty content skipped
407
- * ```
408
- */
409
- async function write(path, data, options = {}) {
410
- const trimmed = data.trim();
411
- if (trimmed === "") return null;
412
- const resolved = resolve(path);
413
- if (typeof Bun !== "undefined") {
414
- const file = Bun.file(resolved);
415
- if ((await file.exists() ? await file.text() : null) === trimmed) return null;
416
- await Bun.write(resolved, trimmed);
417
- return trimmed;
418
- }
419
- try {
420
- if (await readFile(resolved, { encoding: "utf-8" }) === trimmed) return null;
421
- } catch {}
422
- await mkdir(dirname(resolved), { recursive: true });
423
- await writeFile(resolved, trimmed, { encoding: "utf-8" });
424
- if (options.sanity) {
425
- const savedData = await readFile(resolved, { encoding: "utf-8" });
426
- if (savedData !== trimmed) throw new Error(`Sanity check failed for ${path}\n\nData[${data.length}]:\n${data}\n\nSaved[${savedData.length}]:\n${savedData}\n`);
427
- return savedData;
428
- }
429
- return trimmed;
430
- }
431
- //#endregion
432
417
  //#region ../../internals/utils/src/linters.ts
433
418
  /**
434
- * Collects all files under `dir` recursively using Node's built-in fs APIs.
435
- *
436
- * Passing explicit file paths to oxlint (instead of a directory) bypasses
437
- * oxlint's `.gitignore`-aware directory traversal, which would otherwise skip
438
- * files that are listed in `.gitignore` (e.g. generated output directories).
439
- */
440
- function findLintableFiles(dir) {
441
- try {
442
- return readdirSync(dir, {
443
- withFileTypes: true,
444
- recursive: true
445
- }).filter((d) => d.isFile()).map((d) => `${d.parentPath}/${d.name}`);
446
- } catch {
447
- return [];
448
- }
449
- }
450
- /**
451
419
  * CLI command descriptors for each supported linter.
452
420
  *
453
421
  * Each entry contains the executable `command`, an `args` factory that maps an
@@ -471,7 +439,11 @@ const linters = {
471
439
  },
472
440
  oxlint: {
473
441
  command: "oxlint",
474
- args: (outputPath) => ["--fix", ...findLintableFiles(outputPath)],
442
+ args: (outputPath) => [
443
+ "--fix",
444
+ "--no-ignore",
445
+ outputPath
446
+ ],
475
447
  errorMessage: "Oxlint not found"
476
448
  }
477
449
  };
@@ -506,261 +478,50 @@ async function detectLinter() {
506
478
  return null;
507
479
  }
508
480
  //#endregion
509
- //#region src/reporters/report.ts
481
+ //#region ../../internals/utils/src/network.ts
510
482
  /**
511
- * Builds the normalized {@link Report} for one config from its {@link GenerationResult}. Splits the
512
- * diagnostics into problems and per-plugin timings (slowest first) and derives the plugin and issue
513
- * counts, so every reporter renders the same data.
483
+ * Well-known stable domains used as DNS probes to check internet connectivity.
514
484
  */
515
- function buildReport(result) {
516
- const { config, diagnostics, filesCreated, status, hrStart } = result;
517
- const failed = Diagnostics.failedPlugins(diagnostics);
518
- const total = config.plugins?.length ?? 0;
519
- const counts = Diagnostics.count(diagnostics);
520
- const problems = diagnostics.filter(isProblemDiagnostic);
521
- const timings = diagnostics.filter(isPerformanceDiagnostic).sort((a, b) => b.duration - a.duration).map((diagnostic) => ({
522
- plugin: diagnostic.plugin,
523
- durationMs: diagnostic.duration
524
- }));
525
- return {
526
- name: config.name ?? "",
527
- status,
528
- plugins: {
529
- passed: total - failed.length,
530
- failed,
531
- total
532
- },
533
- counts,
534
- filesCreated,
535
- durationMs: getElapsedMs(hrStart),
536
- output: resolve(config.root, config.output.path),
537
- timings,
538
- diagnostics: problems.map((diagnostic) => Diagnostics.serialize(diagnostic))
539
- };
540
- }
541
- //#endregion
542
- //#region src/reporters/cliReporter.ts
543
- /**
544
- * Builds the vitest/jest-style summary for one {@link Report}: right-aligned dim labels with
545
- * `N passed (total)` counts, and a per-plugin `Timings` section when `showTimings`.
546
- */
547
- function buildSummaryLines(report, { showTimings }) {
548
- const { status, plugins, counts, filesCreated, durationMs, output, timings } = report;
549
- const rows = [];
550
- rows.push(["Plugins", status === "success" ? `${styleText("green", `${plugins.passed} passed`)} (${plugins.total})` : `${styleText("green", `${plugins.passed} passed`)} | ${styleText("red", `${plugins.failed.length} failed`)} (${plugins.total})`]);
551
- if (status === "failed" && plugins.failed.length > 0) rows.push(["Failed", plugins.failed.map((name) => randomCliColor(name)).join(", ")]);
552
- if (counts.errors > 0 || counts.warnings > 0) {
553
- const issues = [counts.errors > 0 ? styleText("red", `${counts.errors} ${counts.errors === 1 ? "error" : "errors"}`) : void 0, counts.warnings > 0 ? styleText("yellow", `${counts.warnings} ${counts.warnings === 1 ? "warning" : "warnings"}`) : void 0].filter(Boolean).join(" | ");
554
- rows.push(["Issues", issues]);
555
- }
556
- rows.push(["Files", `${styleText("green", String(filesCreated))} generated`]);
557
- rows.push(["Duration", styleText("green", formatMs(durationMs))]);
558
- rows.push(["Output", output]);
559
- const labelWidth = Math.max(...rows.map(([label]) => label.length), timings.length > 0 ? 7 : 0);
560
- const lines = rows.map(([label, value]) => `${styleText("dim", label.padStart(labelWidth))} ${value}`);
561
- if (showTimings && timings.length > 0) {
562
- const nameWidth = Math.max(0, ...timings.map((timing) => timing.plugin.length));
563
- const indent = " ".repeat(labelWidth + 2);
564
- lines.push(styleText("dim", "Timings".padStart(labelWidth)));
565
- for (const timing of timings) {
566
- const timeStr = formatMs(timing.durationMs);
567
- const barLength = Math.min(Math.ceil(timing.durationMs / 100), 10);
568
- const bar = styleText("dim", "█".repeat(barLength));
569
- lines.push(`${indent}${styleText("dim", "•")} ${timing.plugin.padEnd(nameWidth)} ${bar} ${timeStr}`);
570
- }
571
- }
572
- return lines;
573
- }
574
- /**
575
- * Renders the summary as plain `console.log` lines so it works in every CLI (no clack/TTY
576
- * dependency): a blank line, the config name colored by status, then the summary rows.
577
- */
578
- function renderSummary(lines, { title, status }) {
579
- console.log("");
580
- if (title) console.log(styleText(status === "failed" ? "red" : "green", title));
581
- for (const line of lines) console.log(line);
582
- }
583
- /**
584
- * The default `cli` reporter. Renders the {@link Report} for each config as it finishes, independent
585
- * of the live logger view. Suppressed at `silent`; the `verbose` level adds the per-plugin timings.
586
- */
587
- const cliReporter = createReporter({
588
- name: "cli",
589
- report(result, { logLevel: logLevel$7 }) {
590
- if (logLevel$7 <= logLevel.silent) return;
591
- const report = buildReport(result);
592
- renderSummary(buildSummaryLines(report, { showTimings: logLevel$7 >= logLevel.verbose }), {
593
- title: report.name,
594
- status: report.status
595
- });
596
- }
597
- });
598
- //#endregion
599
- //#region src/loggers/diagnostics.ts
600
- /**
601
- * Glyph and accent color per severity, matching the miette/oxlint convention
602
- * (`×` error, `⚠` warning, `ℹ` advice).
603
- */
604
- const severityStyle = {
605
- error: {
606
- glyph: "×",
607
- color: "red"
608
- },
609
- warning: {
610
- glyph: "⚠",
611
- color: "yellow"
612
- },
613
- info: {
614
- glyph: "ℹ",
615
- color: "blue"
616
- }
617
- };
618
- /**
619
- * The colored, bold severity glyph (`×`, `⚠`, `ℹ`) on its own. Pass it as clack's
620
- * `symbol` so clack owns the gutter and adds the bar to the continuation lines,
621
- * instead of baking the glyph into the message text.
622
- */
623
- function diagnosticSymbol(severity) {
624
- const { glyph, color } = severityStyle[severity];
625
- return styleText(color, styleText("bold", glyph));
626
- }
627
- /**
628
- * The `plugin(CODE): message` headline, without the leading severity glyph.
629
- */
630
- function diagnosticHeadline(diagnostic) {
631
- const { code, severity, message } = diagnostic;
632
- const plugin = isProblemDiagnostic(diagnostic) ? diagnostic.plugin : void 0;
633
- const { color } = severityStyle[severity];
634
- return `${styleText(color, styleText("bold", plugin ? `${plugin}(${code})` : code))}: ${message}`;
635
- }
636
- /**
637
- * The detail lines below the headline: optional `at <pointer>`, `help:`, and
638
- * `docs:`. OpenAPI has no line/column, so the location is the JSON pointer the
639
- * adapter built. Each line keeps a two-space indent so it sits under the headline.
640
- */
641
- function diagnosticDetails(diagnostic) {
642
- const { code } = diagnostic;
643
- const problem = isProblemDiagnostic(diagnostic) ? diagnostic : void 0;
644
- const location = problem?.location;
645
- const help = problem?.help;
646
- const lines = [];
647
- if (location && "pointer" in location) lines.push(` ${styleText("dim", "at")} ${styleText("cyan", location.pointer)}`);
648
- if (help) lines.push(` ${styleText("cyan", "help:")} ${help}`);
649
- if (code !== diagnosticCode.unknown) lines.push(` ${styleText("dim", "docs:")} ${styleText("cyan", Diagnostics.docsUrl(code))}`);
650
- return lines;
651
- }
485
+ const TEST_DOMAINS = [
486
+ "dns.google.com",
487
+ "cloudflare.com",
488
+ "one.one.one.one"
489
+ ];
652
490
  /**
653
- * Renders a {@link Diagnostic} in the oxlint style as a self-contained block: a
654
- * plugin(CODE): message` header followed by the {@link diagnosticDetails}.
655
- * Use this where clack's gutter is not available (plain, file output); clack
656
- * loggers pass {@link diagnosticSymbol}, {@link diagnosticHeadline}, and
657
- * {@link diagnosticDetails} to `clack.log.message` instead.
491
+ * Returns `true` when the system has internet connectivity.
492
+ * Probes DNS resolution against well-known stable domains.
658
493
  *
659
494
  * @example
660
495
  * ```ts
661
- * 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' } })
496
+ * if (await isOnline()) {
497
+ * await fetchLatestVersion()
498
+ * }
662
499
  * ```
663
500
  */
664
- function formatDiagnostic(diagnostic) {
665
- return [`${diagnosticSymbol(diagnostic.severity)} ${diagnosticHeadline(diagnostic)}`, ...diagnosticDetails(diagnostic)];
666
- }
667
- //#endregion
668
- //#region src/reporters/fileReporter.ts
669
- /**
670
- * Builds the `## Summary` section: the same counts the cli and json reporters expose, as a list of
671
- * `label value` rows with the labels padded to a common width.
672
- */
673
- function buildSummarySection(report) {
674
- const { status, plugins, counts, filesCreated, durationMs, output } = report;
675
- const rows = [["Status", status], ["Plugins", status === "success" ? `${plugins.passed} passed (${plugins.total})` : `${plugins.passed} passed | ${plugins.failed.length} failed (${plugins.total})`]];
676
- if (plugins.failed.length > 0) rows.push(["Failed", plugins.failed.join(", ")]);
677
- rows.push(["Issues", `${counts.errors} errors | ${counts.warnings} warnings | ${counts.infos} infos`]);
678
- rows.push(["Files", `${filesCreated} generated`]);
679
- rows.push(["Duration", formatMs(durationMs)]);
680
- rows.push(["Output", output]);
681
- const labelWidth = Math.max(...rows.map(([label]) => label.length));
682
- return [
683
- "## Summary",
684
- "",
685
- ...rows.map(([label, value]) => ` ${label.padEnd(labelWidth)} ${value}`)
686
- ];
687
- }
688
- /**
689
- * Builds the `## Problems` section: each problem rendered in the miette block format, blocks
690
- * separated by a blank line. Returns an empty array when there are no problems, so the caller
691
- * can drop the heading.
692
- */
693
- function buildProblemSection(diagnostics) {
694
- const problems = diagnostics.filter(isProblemDiagnostic);
695
- if (problems.length === 0) return [];
696
- return [
697
- "## Problems",
698
- "",
699
- problems.map((diagnostic) => formatDiagnostic(diagnostic).join("\n")).join("\n\n")
700
- ];
701
- }
702
- /**
703
- * Builds the `## Timings` section from a {@link Report}: one `plugin duration` row per record,
704
- * slowest first with the plugin names left-aligned and the durations right-aligned. Returns an
705
- * empty array when there are no timings.
706
- */
707
- function buildTimingSection(report) {
708
- const { timings } = report;
709
- if (timings.length === 0) return [];
710
- const nameWidth = Math.max(...timings.map((timing) => timing.plugin.length));
711
- const durations = timings.map((timing) => formatMs(timing.durationMs));
712
- const durationWidth = Math.max(...durations.map((duration) => duration.length));
713
- return [
714
- "## Timings",
715
- "",
716
- ...timings.map((timing, index) => ` ${timing.plugin.padEnd(nameWidth)} ${durations[index].padStart(durationWidth)}`)
717
- ];
501
+ async function isOnline() {
502
+ for (const domain of TEST_DOMAINS) try {
503
+ await promises.resolve(domain);
504
+ return true;
505
+ } catch {}
506
+ return false;
718
507
  }
719
508
  /**
720
- * The `file` reporter. Writes a config's {@link Report} to `.kubb/kubb-<name>-<timestamp>.log` as a
721
- * plain-text document: a `# <name> — <timestamp>` header, a `## Summary` with the same counts the
722
- * cli and json reporters expose, a `## Problems` section in the miette block format, and a
723
- * `## Timings` section. Selected with `--reporter file` (or `reporters: ['file']`), replacing the
724
- * old `--debug` flag.
509
+ * Executes `fn` only when the system is online. Returns `null` when offline or on error.
725
510
  *
726
- * @note Unlike the streaming logger it replaced, it captures the collected diagnostics once a
727
- * config finishes, not the live `kubb:info`/`kubb:plugin` event stream. Color is stripped so the
728
- * file stays plain text even when the run is attached to a TTY.
729
- */
730
- const fileReporter = createReporter({
731
- name: "file",
732
- async report(result) {
733
- const { diagnostics, config } = result;
734
- if (diagnostics.length === 0) return;
735
- const report = buildReport(result);
736
- const content = stripVTControlCharacters([config.name ? `# ${config.name} — ${(/* @__PURE__ */ new Date()).toISOString()}` : `# ${(/* @__PURE__ */ new Date()).toISOString()}`, ...[
737
- buildSummarySection(report),
738
- buildProblemSection(diagnostics),
739
- buildTimingSection(report)
740
- ].filter((section) => section.length > 0).map((section) => section.join("\n"))].join("\n\n"));
741
- const baseName = `${[
742
- "kubb",
743
- config.name,
744
- Date.now()
745
- ].filter(Boolean).join("-")}.log`;
746
- const pathName = resolve(process$1.cwd(), ".kubb", baseName);
747
- await write(pathName, `${content}\n`);
748
- console.error(`Debug log written to ${relative(process$1.cwd(), pathName)}`);
749
- }
750
- });
751
- //#endregion
752
- //#region src/reporters/jsonReporter.ts
753
- /**
754
- * The `json` reporter. Writes the {@link Report} for each config to stdout as JSON, for CI tooling.
755
- * The terminal reporter is suppressed while this is active so stdout stays valid JSON.
511
+ * @example
512
+ * ```ts
513
+ * const version = await executeIfOnline(() => fetchLatestVersion('kubb'))
514
+ * // null when offline
515
+ * ```
756
516
  */
757
- const jsonReporter = createReporter({
758
- name: "json",
759
- report(result) {
760
- const report = buildReport(result);
761
- process$1.stdout.write(`${JSON.stringify(report, null, 2)}\n`);
517
+ async function executeIfOnline(fn) {
518
+ if (!await isOnline()) return null;
519
+ try {
520
+ return await fn();
521
+ } catch {
522
+ return null;
762
523
  }
763
- });
524
+ }
764
525
  //#endregion
765
526
  //#region src/loggers/clackLogger.ts
766
527
  /**
@@ -877,7 +638,7 @@ const clackLogger = defineLogger({
877
638
  if (logLevel$6 <= logLevel.silent && diagnostic.severity !== "error") return;
878
639
  stopSpinner();
879
640
  stopActiveProgress();
880
- if (isUpdateDiagnostic(diagnostic)) {
641
+ if (Diagnostics.isUpdate(diagnostic)) {
881
642
  clack.box(`\`v${diagnostic.currentVersion}\` → \`v${diagnostic.latestVersion}\`
882
643
  Run \`npm install -g @kubb/cli\` to update`, "Update available for `Kubb`", {
883
644
  width: "auto",
@@ -889,7 +650,8 @@ Run \`npm install -g @kubb/cli\` to update`, "Update available for `Kubb`", {
889
650
  });
890
651
  return;
891
652
  }
892
- clack.log.message([diagnosticHeadline(diagnostic), ...diagnosticDetails(diagnostic)], { symbol: diagnosticSymbol(diagnostic.severity) });
653
+ const { symbol, headline, details } = Diagnostics.format(diagnostic);
654
+ clack.log.message([headline, ...details], { symbol });
893
655
  });
894
656
  context.on("kubb:lifecycle:start", async ({ version }) => {
895
657
  console.log(`\n${getIntro({
@@ -1136,7 +898,7 @@ const githubActionsLogger = defineLogger({
1136
898
  context.on("kubb:diagnostic", ({ diagnostic }) => {
1137
899
  closeAllGroups();
1138
900
  if (logLevel$5 <= logLevel.silent && diagnostic.severity !== "error") return;
1139
- if (!isProblemDiagnostic(diagnostic)) {
901
+ if (!Diagnostics.isProblem(diagnostic)) {
1140
902
  console.log(`::notice::${diagnostic.message}`);
1141
903
  return;
1142
904
  }
@@ -1144,7 +906,7 @@ const githubActionsLogger = defineLogger({
1144
906
  if (diagnostic.location && "pointer" in diagnostic.location) parts.push(`(at ${diagnostic.location.pointer})`);
1145
907
  if (diagnostic.plugin) parts.push(`[plugin: ${diagnostic.plugin}]`);
1146
908
  if (diagnostic.help) parts.push(`help: ${diagnostic.help}`);
1147
- if (diagnostic.code !== diagnosticCode.unknown) parts.push(`docs: ${Diagnostics.docsUrl(diagnostic.code)}`);
909
+ if (diagnostic.code !== Diagnostics.code.unknown) parts.push(`docs: ${Diagnostics.docsUrl(diagnostic.code)}`);
1148
910
  console.error(`::error::${parts.join(" ")}`);
1149
911
  });
1150
912
  context.on("kubb:lifecycle:start", ({ version }) => {
@@ -1308,7 +1070,7 @@ const plainLogger = defineLogger({
1308
1070
  });
1309
1071
  context.on("kubb:diagnostic", ({ diagnostic }) => {
1310
1072
  if (logLevel$4 <= logLevel.silent && diagnostic.severity !== "error") return;
1311
- console.log(getMessage(formatDiagnostic(diagnostic).join("\n")));
1073
+ console.log(getMessage(Diagnostics.formatLines(diagnostic).join("\n")));
1312
1074
  });
1313
1075
  context.on("kubb:lifecycle:start", ({ version }) => {
1314
1076
  console.log(`Kubb CLI v${version}`);
@@ -1490,30 +1252,32 @@ function installReporter(context, reporter, ctx) {
1490
1252
  hrStart
1491
1253
  }, ctx);
1492
1254
  });
1255
+ if (reporter.drain) context.on("kubb:lifecycle:end", () => reporter.drain?.(ctx));
1493
1256
  }
1494
1257
  /**
1495
- * Installs the live logger (the TUI view) and the selected reporters (the output), returning the
1496
- * terminal logger's hook sink when one was installed. Loggers and reporters are independent: the
1497
- * `cli` selection activates the env logger plus the {@link cliReporter} summary.
1258
+ * Installs the live logger (the TUI view) and the given reporters (the output), returning the
1259
+ * terminal logger's hook sink when one was installed. The reporters are already selected by the
1260
+ * caller (the CLI maps `--reporter` to names via `selectReporters`); this only wires them.
1498
1261
  *
1499
- * The `json` reporter owns stdout, so the terminal logger and `cli` summary are suppressed whenever
1500
- * `json` is selected, even if `cli` is also listed.
1262
+ * Loggers and reporters are independent: the `cli` reporter also activates the env logger summary.
1263
+ * The `json` reporter owns stdout, so the live logger and the `cli` summary are suppressed whenever
1264
+ * `json` is among the reporters, even if `cli` is also listed.
1501
1265
  */
1502
1266
  async function setupReporters(context, { logLevel, reporters }) {
1503
- const unique = new Set(reporters.length ? reporters : ["cli"]);
1504
- const hasJson = unique.has("json");
1267
+ const hasJson = reporters.some((reporter) => reporter.name === "json");
1505
1268
  const ctx = { logLevel };
1506
1269
  let makeSink = null;
1507
- if (unique.has("cli") && !hasJson) {
1508
- const type = detectLogger();
1509
- const logger = logMapper[type];
1510
- if (!logger) throw new Error(`Unknown adapter type: ${type}`);
1511
- const sink = await logger.install(context, { logLevel });
1512
- makeSink = typeof sink === "function" ? sink : null;
1513
- installReporter(context, cliReporter, ctx);
1270
+ for (const reporter of reporters) {
1271
+ if (reporter.name === "cli") {
1272
+ if (hasJson) continue;
1273
+ const type = detectLogger();
1274
+ const logger = logMapper[type];
1275
+ if (!logger) throw new Error(`Unknown adapter type: ${type}`);
1276
+ const sink = await logger.install(context, { logLevel });
1277
+ makeSink = typeof sink === "function" ? sink : null;
1278
+ }
1279
+ installReporter(context, reporter, ctx);
1514
1280
  }
1515
- if (hasJson) installReporter(context, jsonReporter, ctx);
1516
- if (unique.has("file")) installReporter(context, fileReporter, ctx);
1517
1281
  return makeSink;
1518
1282
  }
1519
1283
  //#endregion
@@ -1642,18 +1406,14 @@ async function runHook({ id, command, args, commandWithArgs, hooks, stream = fal
1642
1406
  await emitEnd(true, null);
1643
1407
  } catch (err) {
1644
1408
  if (!(err instanceof NonZeroExitError)) {
1645
- const error = toError(err);
1646
- await emitEnd(false, error);
1647
- await hooks.emit("kubb:error", { error });
1409
+ await emitEnd(false, toError(err));
1648
1410
  return;
1649
1411
  }
1650
1412
  const stderr = err.output?.stderr ?? "";
1651
1413
  const stdout = err.output?.stdout ?? "";
1652
1414
  if (stderr) sink?.onStderr?.(stderr);
1653
1415
  if (stdout) sink?.onStdout?.(stdout);
1654
- const error = /* @__PURE__ */ new Error(`Hook execute failed: ${commandWithArgs}`);
1655
- await emitEnd(false, error);
1656
- await hooks.emit("kubb:error", { error });
1416
+ await emitEnd(false, /* @__PURE__ */ new Error(`Hook execute failed: ${commandWithArgs}`));
1657
1417
  }
1658
1418
  }
1659
1419
  /**
@@ -1716,7 +1476,7 @@ async function runToolPass({ toolValue, detect, toolMap, toolLabel, successPrefi
1716
1476
  }
1717
1477
  }
1718
1478
  let toolError;
1719
- if (resolvedTool && resolvedTool !== "auto" && resolvedTool in toolMap) {
1479
+ if (resolvedTool && resolvedTool !== "auto" && resolvedTool in toolMap && existsSync(outputPath)) {
1720
1480
  const toolConfig = toolMap[resolvedTool];
1721
1481
  const hookId = createHash("sha256").update([configName, resolvedTool].filter(Boolean).join("-")).digest("hex");
1722
1482
  const successMessage = [
@@ -1784,7 +1544,7 @@ async function generate(options) {
1784
1544
  name: p.name,
1785
1545
  options: p.options
1786
1546
  }));
1787
- const reportTelemetry = (status) => sendTelemetry(buildTelemetryEvent({
1547
+ const reportTelemetry = (status) => Telemetry.send(Telemetry.build({
1788
1548
  command: "generate",
1789
1549
  kubbVersion: version,
1790
1550
  plugins: telemetryPlugins,
@@ -1793,8 +1553,8 @@ async function generate(options) {
1793
1553
  status
1794
1554
  }));
1795
1555
  for (const diagnostic of diagnostics) {
1796
- if (!isProblemDiagnostic(diagnostic)) continue;
1797
- const unknown = narrowDiagnostic(diagnostic, diagnosticCode.unknown);
1556
+ if (!Diagnostics.isProblem(diagnostic)) continue;
1557
+ const unknown = Diagnostics.narrow(diagnostic, Diagnostics.code.unknown);
1798
1558
  if (unknown) await hooks.emit("kubb:error", { error: unknown.cause ?? new Error(unknown.message) });
1799
1559
  else await Diagnostics.emit(hooks, diagnostic);
1800
1560
  }
@@ -1813,7 +1573,7 @@ async function generate(options) {
1813
1573
  const outputPath = path.resolve(config.root, config.output.path);
1814
1574
  const outputDiagnostics = [];
1815
1575
  const toolPasses = [config.output.format && {
1816
- code: diagnosticCode.formatFailed,
1576
+ code: Diagnostics.code.formatFailed,
1817
1577
  toolValue: config.output.format,
1818
1578
  detect: detectFormatter,
1819
1579
  toolMap: formatters,
@@ -1823,7 +1583,7 @@ async function generate(options) {
1823
1583
  onStart: () => hooks.emit("kubb:format:start"),
1824
1584
  onEnd: () => hooks.emit("kubb:format:end")
1825
1585
  }, config.output.lint && {
1826
- code: diagnosticCode.lintFailed,
1586
+ code: Diagnostics.code.lintFailed,
1827
1587
  toolValue: config.output.lint,
1828
1588
  detect: detectLinter,
1829
1589
  toolMap: linters,
@@ -1864,7 +1624,7 @@ async function generate(options) {
1864
1624
  hooks.off("kubb:hook:end", onHookEnd);
1865
1625
  }
1866
1626
  for (const error of hookFailures) {
1867
- const diagnostic = outputDiagnostic(diagnosticCode.hookFailed, "Post-generate hook", error);
1627
+ const diagnostic = outputDiagnostic(Diagnostics.code.hookFailed, "Post-generate hook", error);
1868
1628
  outputDiagnostics.push(diagnostic);
1869
1629
  await Diagnostics.emit(hooks, diagnostic);
1870
1630
  }
@@ -1934,14 +1694,15 @@ async function run({ input, configPath, logLevel: logLevelKey, watch, reporters:
1934
1694
  } catch (error) {
1935
1695
  await setupReporters(hooks, {
1936
1696
  logLevel: logLevel$2,
1937
- reporters: ["cli"]
1697
+ reporters: [cliReporter]
1938
1698
  });
1939
1699
  await hooks.emit("kubb:error", { error: toError(error) });
1940
1700
  process$1.exit(1);
1941
1701
  }
1702
+ const requestedNames = cliReporters?.length ? cliReporters : ["cli"];
1942
1703
  const makeSink = await setupReporters(hooks, {
1943
1704
  logLevel: logLevel$2,
1944
- reporters: cliReporters?.length ? cliReporters : configs[0]?.reporters ?? ["cli"]
1705
+ reporters: selectReporters(configs[0]?.reporters ?? [], requestedNames)
1945
1706
  });
1946
1707
  await hooks.emit("kubb:lifecycle:start", { version });
1947
1708
  await checkForUpdate(hooks);
@@ -1958,7 +1719,7 @@ async function run({ input, configPath, logLevel: logLevelKey, watch, reporters:
1958
1719
  });
1959
1720
  await hooks.emit("kubb:config:end", { configs });
1960
1721
  let anyFailed = false;
1961
- for (const config of configs) if (isInputPath(config) && watch) await startWatcher([input || config.input.path], async (paths) => {
1722
+ for (const config of configs) if (config.input && "path" in config.input && watch) await startWatcher([input || config.input.path], async (paths) => {
1962
1723
  await generate({
1963
1724
  input,
1964
1725
  config,
@@ -1993,4 +1754,4 @@ async function run({ input, configPath, logLevel: logLevelKey, watch, reporters:
1993
1754
  //#endregion
1994
1755
  export { run };
1995
1756
 
1996
- //# sourceMappingURL=run-Ca2h07rN.js.map
1757
+ //# sourceMappingURL=run-BabEDDqN.js.map