@kubb/cli 5.0.0-beta.38 → 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 (82) hide show
  1. package/dist/{agent-CfZ_Uqde.js → agent-CLspGfSn.js} +4 -4
  2. package/dist/{agent-CfZ_Uqde.js.map → agent-CLspGfSn.js.map} +1 -1
  3. package/dist/{agent-Bl8JwjMa.cjs → agent-RdNQgRXD.cjs} +4 -4
  4. package/dist/{agent-Bl8JwjMa.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-Bgds6Zx3.cjs → generate-DK1pLJMi.cjs} +2 -2
  10. package/dist/{generate-Bgds6Zx3.cjs.map → generate-DK1pLJMi.cjs.map} +1 -1
  11. package/dist/{generate-CfxFqNeb.js → generate-Db0pJmbG.js} +2 -2
  12. package/dist/{generate-CfxFqNeb.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-TIec3Dym.js → init-DKMwmbmx.js} +2 -2
  18. package/dist/{init-TIec3Dym.js.map → init-DKMwmbmx.js.map} +1 -1
  19. package/dist/{init-C5wnuzeK.cjs → init-D_MQBDVz.cjs} +2 -2
  20. package/dist/{init-C5wnuzeK.cjs.map → init-D_MQBDVz.cjs.map} +1 -1
  21. package/dist/{mcp-Damue5Mq.js → mcp-DqNyN0cN.js} +3 -3
  22. package/dist/{mcp-Damue5Mq.js.map → mcp-DqNyN0cN.js.map} +1 -1
  23. package/dist/{mcp-Cr753GW1.cjs → mcp-DvEeDWlW.cjs} +3 -3
  24. package/dist/{mcp-Cr753GW1.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-guApEHiW.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-C752fag9.js → run-BabEDDqN.js} +128 -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-BFZtWpcW.cjs → run-CJUmJcbC.cjs} +128 -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-DMzjP-hd.cjs → validate-CkW_AKZp.cjs} +3 -3
  46. package/dist/{validate-DMzjP-hd.cjs.map → validate-CkW_AKZp.cjs.map} +1 -1
  47. package/dist/{validate-CYTKdezO.js → validate-jRewvR0c.js} +3 -3
  48. package/dist/{validate-CYTKdezO.js.map → validate-jRewvR0c.js.map} +1 -1
  49. package/package.json +7 -7
  50. package/src/constants.ts +0 -15
  51. package/src/index.ts +2 -2
  52. package/src/loggers/clackLogger.ts +4 -6
  53. package/src/loggers/githubActionsLogger.ts +3 -3
  54. package/src/loggers/plainLogger.ts +2 -3
  55. package/src/loggers/utils.ts +27 -32
  56. package/src/runners/agent/run.ts +2 -2
  57. package/src/runners/generate/run.ts +21 -17
  58. package/src/runners/mcp/run.ts +2 -2
  59. package/src/runners/validate/run.ts +2 -2
  60. package/dist/constants-CAKUpLcQ.cjs.map +0 -1
  61. package/dist/constants-CYxk4aNm.js.map +0 -1
  62. package/dist/package-Cnt1K03J.js +0 -6
  63. package/dist/package-Cnt1K03J.js.map +0 -1
  64. package/dist/package-guApEHiW.cjs.map +0 -1
  65. package/dist/run-BFEK9md9.js.map +0 -1
  66. package/dist/run-BFZtWpcW.cjs.map +0 -1
  67. package/dist/run-BFv6avA_.cjs.map +0 -1
  68. package/dist/run-BQZyg7If.cjs.map +0 -1
  69. package/dist/run-BvXxelGR.js.map +0 -1
  70. package/dist/run-Bz9IFMWg.cjs.map +0 -1
  71. package/dist/run-C752fag9.js.map +0 -1
  72. package/dist/run-DJxYClJV.js.map +0 -1
  73. package/dist/telemetry-B80oJfxR.cjs +0 -280
  74. package/dist/telemetry-B80oJfxR.cjs.map +0 -1
  75. package/dist/telemetry-ueaMzs_c.js +0 -243
  76. package/dist/telemetry-ueaMzs_c.js.map +0 -1
  77. package/src/loggers/diagnostics.ts +0 -77
  78. package/src/reporters/cliReporter.ts +0 -89
  79. package/src/reporters/fileReporter.ts +0 -103
  80. package/src/reporters/jsonReporter.ts +0 -20
  81. package/src/reporters/report.ts +0 -84
  82. 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-Cnt1K03J.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,266 +478,50 @@ async function detectLinter() {
506
478
  return null;
507
479
  }
508
480
  //#endregion
509
- //#region src/reporters/report.ts
510
- /**
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.
514
- */
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
- }
481
+ //#region ../../internals/utils/src/network.ts
574
482
  /**
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.
483
+ * Well-known stable domains used as DNS probes to check internet connectivity.
577
484
  */
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. `report` returns one config's {@link Report}, which {@link createReporter}
755
- * buffers, and `flush` writes them as a single pretty-printed JSON array on `kubb:lifecycle:end`.
756
- * Buffering keeps a multi-config run one valid JSON document on stdout instead of concatenated
757
- * objects that would break `jq .`. The terminal reporter is suppressed while `json` is active so
758
- * stdout stays valid JSON.
511
+ * @example
512
+ * ```ts
513
+ * const version = await executeIfOnline(() => fetchLatestVersion('kubb'))
514
+ * // null when offline
515
+ * ```
759
516
  */
760
- const jsonReporter = createReporter({
761
- name: "json",
762
- report(result) {
763
- return buildReport(result);
764
- },
765
- flush(_context, reports) {
766
- process$1.stdout.write(`${JSON.stringify(reports, 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;
767
523
  }
768
- });
524
+ }
769
525
  //#endregion
770
526
  //#region src/loggers/clackLogger.ts
771
527
  /**
@@ -882,7 +638,7 @@ const clackLogger = defineLogger({
882
638
  if (logLevel$6 <= logLevel.silent && diagnostic.severity !== "error") return;
883
639
  stopSpinner();
884
640
  stopActiveProgress();
885
- if (isUpdateDiagnostic(diagnostic)) {
641
+ if (Diagnostics.isUpdate(diagnostic)) {
886
642
  clack.box(`\`v${diagnostic.currentVersion}\` → \`v${diagnostic.latestVersion}\`
887
643
  Run \`npm install -g @kubb/cli\` to update`, "Update available for `Kubb`", {
888
644
  width: "auto",
@@ -894,7 +650,8 @@ Run \`npm install -g @kubb/cli\` to update`, "Update available for `Kubb`", {
894
650
  });
895
651
  return;
896
652
  }
897
- 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 });
898
655
  });
899
656
  context.on("kubb:lifecycle:start", async ({ version }) => {
900
657
  console.log(`\n${getIntro({
@@ -1141,7 +898,7 @@ const githubActionsLogger = defineLogger({
1141
898
  context.on("kubb:diagnostic", ({ diagnostic }) => {
1142
899
  closeAllGroups();
1143
900
  if (logLevel$5 <= logLevel.silent && diagnostic.severity !== "error") return;
1144
- if (!isProblemDiagnostic(diagnostic)) {
901
+ if (!Diagnostics.isProblem(diagnostic)) {
1145
902
  console.log(`::notice::${diagnostic.message}`);
1146
903
  return;
1147
904
  }
@@ -1149,7 +906,7 @@ const githubActionsLogger = defineLogger({
1149
906
  if (diagnostic.location && "pointer" in diagnostic.location) parts.push(`(at ${diagnostic.location.pointer})`);
1150
907
  if (diagnostic.plugin) parts.push(`[plugin: ${diagnostic.plugin}]`);
1151
908
  if (diagnostic.help) parts.push(`help: ${diagnostic.help}`);
1152
- 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)}`);
1153
910
  console.error(`::error::${parts.join(" ")}`);
1154
911
  });
1155
912
  context.on("kubb:lifecycle:start", ({ version }) => {
@@ -1313,7 +1070,7 @@ const plainLogger = defineLogger({
1313
1070
  });
1314
1071
  context.on("kubb:diagnostic", ({ diagnostic }) => {
1315
1072
  if (logLevel$4 <= logLevel.silent && diagnostic.severity !== "error") return;
1316
- console.log(getMessage(formatDiagnostic(diagnostic).join("\n")));
1073
+ console.log(getMessage(Diagnostics.formatLines(diagnostic).join("\n")));
1317
1074
  });
1318
1075
  context.on("kubb:lifecycle:start", ({ version }) => {
1319
1076
  console.log(`Kubb CLI v${version}`);
@@ -1495,31 +1252,32 @@ function installReporter(context, reporter, ctx) {
1495
1252
  hrStart
1496
1253
  }, ctx);
1497
1254
  });
1498
- if (reporter.flush) context.on("kubb:lifecycle:end", () => reporter.flush?.(ctx));
1255
+ if (reporter.drain) context.on("kubb:lifecycle:end", () => reporter.drain?.(ctx));
1499
1256
  }
1500
1257
  /**
1501
- * Installs the live logger (the TUI view) and the selected reporters (the output), returning the
1502
- * terminal logger's hook sink when one was installed. Loggers and reporters are independent: the
1503
- * `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.
1504
1261
  *
1505
- * The `json` reporter owns stdout, so the terminal logger and `cli` summary are suppressed whenever
1506
- * `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.
1507
1265
  */
1508
1266
  async function setupReporters(context, { logLevel, reporters }) {
1509
- const unique = new Set(reporters.length ? reporters : ["cli"]);
1510
- const hasJson = unique.has("json");
1267
+ const hasJson = reporters.some((reporter) => reporter.name === "json");
1511
1268
  const ctx = { logLevel };
1512
1269
  let makeSink = null;
1513
- if (unique.has("cli") && !hasJson) {
1514
- const type = detectLogger();
1515
- const logger = logMapper[type];
1516
- if (!logger) throw new Error(`Unknown adapter type: ${type}`);
1517
- const sink = await logger.install(context, { logLevel });
1518
- makeSink = typeof sink === "function" ? sink : null;
1519
- 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);
1520
1280
  }
1521
- if (hasJson) installReporter(context, jsonReporter, ctx);
1522
- if (unique.has("file")) installReporter(context, fileReporter, ctx);
1523
1281
  return makeSink;
1524
1282
  }
1525
1283
  //#endregion
@@ -1718,7 +1476,7 @@ async function runToolPass({ toolValue, detect, toolMap, toolLabel, successPrefi
1718
1476
  }
1719
1477
  }
1720
1478
  let toolError;
1721
- if (resolvedTool && resolvedTool !== "auto" && resolvedTool in toolMap) {
1479
+ if (resolvedTool && resolvedTool !== "auto" && resolvedTool in toolMap && existsSync(outputPath)) {
1722
1480
  const toolConfig = toolMap[resolvedTool];
1723
1481
  const hookId = createHash("sha256").update([configName, resolvedTool].filter(Boolean).join("-")).digest("hex");
1724
1482
  const successMessage = [
@@ -1786,7 +1544,7 @@ async function generate(options) {
1786
1544
  name: p.name,
1787
1545
  options: p.options
1788
1546
  }));
1789
- const reportTelemetry = (status) => sendTelemetry(buildTelemetryEvent({
1547
+ const reportTelemetry = (status) => Telemetry.send(Telemetry.build({
1790
1548
  command: "generate",
1791
1549
  kubbVersion: version,
1792
1550
  plugins: telemetryPlugins,
@@ -1795,8 +1553,8 @@ async function generate(options) {
1795
1553
  status
1796
1554
  }));
1797
1555
  for (const diagnostic of diagnostics) {
1798
- if (!isProblemDiagnostic(diagnostic)) continue;
1799
- const unknown = narrowDiagnostic(diagnostic, diagnosticCode.unknown);
1556
+ if (!Diagnostics.isProblem(diagnostic)) continue;
1557
+ const unknown = Diagnostics.narrow(diagnostic, Diagnostics.code.unknown);
1800
1558
  if (unknown) await hooks.emit("kubb:error", { error: unknown.cause ?? new Error(unknown.message) });
1801
1559
  else await Diagnostics.emit(hooks, diagnostic);
1802
1560
  }
@@ -1815,7 +1573,7 @@ async function generate(options) {
1815
1573
  const outputPath = path.resolve(config.root, config.output.path);
1816
1574
  const outputDiagnostics = [];
1817
1575
  const toolPasses = [config.output.format && {
1818
- code: diagnosticCode.formatFailed,
1576
+ code: Diagnostics.code.formatFailed,
1819
1577
  toolValue: config.output.format,
1820
1578
  detect: detectFormatter,
1821
1579
  toolMap: formatters,
@@ -1825,7 +1583,7 @@ async function generate(options) {
1825
1583
  onStart: () => hooks.emit("kubb:format:start"),
1826
1584
  onEnd: () => hooks.emit("kubb:format:end")
1827
1585
  }, config.output.lint && {
1828
- code: diagnosticCode.lintFailed,
1586
+ code: Diagnostics.code.lintFailed,
1829
1587
  toolValue: config.output.lint,
1830
1588
  detect: detectLinter,
1831
1589
  toolMap: linters,
@@ -1866,7 +1624,7 @@ async function generate(options) {
1866
1624
  hooks.off("kubb:hook:end", onHookEnd);
1867
1625
  }
1868
1626
  for (const error of hookFailures) {
1869
- const diagnostic = outputDiagnostic(diagnosticCode.hookFailed, "Post-generate hook", error);
1627
+ const diagnostic = outputDiagnostic(Diagnostics.code.hookFailed, "Post-generate hook", error);
1870
1628
  outputDiagnostics.push(diagnostic);
1871
1629
  await Diagnostics.emit(hooks, diagnostic);
1872
1630
  }
@@ -1936,14 +1694,15 @@ async function run({ input, configPath, logLevel: logLevelKey, watch, reporters:
1936
1694
  } catch (error) {
1937
1695
  await setupReporters(hooks, {
1938
1696
  logLevel: logLevel$2,
1939
- reporters: ["cli"]
1697
+ reporters: [cliReporter]
1940
1698
  });
1941
1699
  await hooks.emit("kubb:error", { error: toError(error) });
1942
1700
  process$1.exit(1);
1943
1701
  }
1702
+ const requestedNames = cliReporters?.length ? cliReporters : ["cli"];
1944
1703
  const makeSink = await setupReporters(hooks, {
1945
1704
  logLevel: logLevel$2,
1946
- reporters: cliReporters?.length ? cliReporters : configs[0]?.reporters ?? ["cli"]
1705
+ reporters: selectReporters(configs[0]?.reporters ?? [], requestedNames)
1947
1706
  });
1948
1707
  await hooks.emit("kubb:lifecycle:start", { version });
1949
1708
  await checkForUpdate(hooks);
@@ -1960,7 +1719,7 @@ async function run({ input, configPath, logLevel: logLevelKey, watch, reporters:
1960
1719
  });
1961
1720
  await hooks.emit("kubb:config:end", { configs });
1962
1721
  let anyFailed = false;
1963
- 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) => {
1964
1723
  await generate({
1965
1724
  input,
1966
1725
  config,
@@ -1995,4 +1754,4 @@ async function run({ input, configPath, logLevel: logLevelKey, watch, reporters:
1995
1754
  //#endregion
1996
1755
  export { run };
1997
1756
 
1998
- //# sourceMappingURL=run-C752fag9.js.map
1757
+ //# sourceMappingURL=run-BabEDDqN.js.map