@kubb/core 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.
package/dist/index.cjs CHANGED
@@ -1,9 +1,86 @@
1
1
  Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
2
- const require_KubbDriver = require("./KubbDriver-BYBUfOZ8.cjs");
2
+ const require_memoryStorage = require("./memoryStorage-DHi1d0To.cjs");
3
+ let node_util = require("node:util");
4
+ let node_crypto = require("node:crypto");
3
5
  let node_fs_promises = require("node:fs/promises");
4
6
  let node_path = require("node:path");
7
+ let node_dns = require("node:dns");
5
8
  let _kubb_ast = require("@kubb/ast");
6
- _kubb_ast = require_KubbDriver.__toESM(_kubb_ast, 1);
9
+ _kubb_ast = require_memoryStorage.__toESM(_kubb_ast, 1);
10
+ let node_process = require("node:process");
11
+ node_process = require_memoryStorage.__toESM(node_process, 1);
12
+ let node_os = require("node:os");
13
+ node_os = require_memoryStorage.__toESM(node_os, 1);
14
+ //#region ../../internals/utils/src/colors.ts
15
+ /**
16
+ * Parses a CSS hex color string (`#RGB`) into its RGB channels.
17
+ * Falls back to `255` for any channel that cannot be parsed.
18
+ */
19
+ function parseHex(color) {
20
+ const int = Number.parseInt(color.replace("#", ""), 16);
21
+ return Number.isNaN(int) ? {
22
+ r: 255,
23
+ g: 255,
24
+ b: 255
25
+ } : {
26
+ r: int >> 16 & 255,
27
+ g: int >> 8 & 255,
28
+ b: int & 255
29
+ };
30
+ }
31
+ /**
32
+ * Returns a function that wraps a string in a 24-bit ANSI true-color escape sequence
33
+ * for the given hex color.
34
+ */
35
+ function hex(color) {
36
+ const { r, g, b } = parseHex(color);
37
+ return (text) => `\x1b[38;2;${r};${g};${b}m${text}\x1b[0m`;
38
+ }
39
+ hex("#F55A17"), hex("#F5A217"), hex("#F58517"), hex("#B45309"), hex("#FFFFFF"), hex("#adadc6"), hex("#FDA4AF");
40
+ /**
41
+ * ANSI color names used by {@link randomCliColor} for deterministic terminal coloring.
42
+ */
43
+ const randomColors = [
44
+ "black",
45
+ "red",
46
+ "green",
47
+ "yellow",
48
+ "blue",
49
+ "white",
50
+ "magenta",
51
+ "cyan",
52
+ "gray"
53
+ ];
54
+ /**
55
+ * Wraps `text` in a deterministic ANSI color derived from the text's SHA-256 hash.
56
+ *
57
+ * @example
58
+ * ```ts
59
+ * randomCliColor('petstore') // '\x1b[33m' + 'petstore' + '\x1b[39m' (always the same color for 'petstore')
60
+ * ```
61
+ */
62
+ function randomCliColor(text) {
63
+ if (!text) return "";
64
+ return (0, node_util.styleText)(randomColors[(0, node_crypto.createHash)("sha256").update(text).digest().readUInt32BE(0) % randomColors.length] ?? "white", text);
65
+ }
66
+ //#endregion
67
+ //#region ../../internals/utils/src/env.ts
68
+ /**
69
+ * Returns `true` when the process is running in a CI environment.
70
+ * Covers GitHub Actions, GitLab CI, CircleCI, Travis CI, Jenkins, Bitbucket,
71
+ * TeamCity, Buildkite, and Azure Pipelines.
72
+ *
73
+ * @example
74
+ * ```ts
75
+ * if (isCIEnvironment()) {
76
+ * logger.level = 'error'
77
+ * }
78
+ * ```
79
+ */
80
+ function isCIEnvironment() {
81
+ 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);
82
+ }
83
+ //#endregion
7
84
  //#region ../../internals/utils/src/fs.ts
8
85
  /**
9
86
  * Writes `data` to `path`, trimming leading/trailing whitespace before saving.
@@ -55,6 +132,51 @@ async function clean(path) {
55
132
  });
56
133
  }
57
134
  //#endregion
135
+ //#region ../../internals/utils/src/network.ts
136
+ /**
137
+ * Well-known stable domains used as DNS probes to check internet connectivity.
138
+ */
139
+ const TEST_DOMAINS = [
140
+ "dns.google.com",
141
+ "cloudflare.com",
142
+ "one.one.one.one"
143
+ ];
144
+ /**
145
+ * Returns `true` when the system has internet connectivity.
146
+ * Probes DNS resolution against well-known stable domains.
147
+ *
148
+ * @example
149
+ * ```ts
150
+ * if (await isOnline()) {
151
+ * await fetchLatestVersion()
152
+ * }
153
+ * ```
154
+ */
155
+ async function isOnline() {
156
+ for (const domain of TEST_DOMAINS) try {
157
+ await node_dns.promises.resolve(domain);
158
+ return true;
159
+ } catch {}
160
+ return false;
161
+ }
162
+ /**
163
+ * Executes `fn` only when the system is online. Returns `null` when offline or on error.
164
+ *
165
+ * @example
166
+ * ```ts
167
+ * const version = await executeIfOnline(() => fetchLatestVersion('kubb'))
168
+ * // null when offline
169
+ * ```
170
+ */
171
+ async function executeIfOnline(fn) {
172
+ if (!await isOnline()) return null;
173
+ try {
174
+ return await fn();
175
+ } catch {
176
+ return null;
177
+ }
178
+ }
179
+ //#endregion
58
180
  //#region src/createAdapter.ts
59
181
  /**
60
182
  * Defines a custom adapter that translates a spec format into Kubb's universal
@@ -90,49 +212,6 @@ function createAdapter(build) {
90
212
  return (options) => build(options ?? {});
91
213
  }
92
214
  //#endregion
93
- //#region src/createStorage.ts
94
- /**
95
- * Defines a custom storage backend. The builder receives user options and
96
- * returns a `Storage` implementation. Kubb ships with filesystem and
97
- * in-memory storages, reach for this when you need to write generated files
98
- * elsewhere (cloud storage, a database, a remote API).
99
- *
100
- * @example In-memory storage (the built-in implementation)
101
- * ```ts
102
- * import { createStorage } from '@kubb/core'
103
- *
104
- * export const memoryStorage = createStorage(() => {
105
- * const store = new Map<string, string>()
106
- *
107
- * return {
108
- * name: 'memory',
109
- * async hasItem(key) {
110
- * return store.has(key)
111
- * },
112
- * async getItem(key) {
113
- * return store.get(key) ?? null
114
- * },
115
- * async setItem(key, value) {
116
- * store.set(key, value)
117
- * },
118
- * async removeItem(key) {
119
- * store.delete(key)
120
- * },
121
- * async getKeys(base) {
122
- * const keys = [...store.keys()]
123
- * return base ? keys.filter((k) => k.startsWith(base)) : keys
124
- * },
125
- * async clear(base) {
126
- * if (!base) store.clear()
127
- * },
128
- * }
129
- * })
130
- * ```
131
- */
132
- function createStorage(build) {
133
- return (options) => build(options ?? {});
134
- }
135
- //#endregion
136
215
  //#region src/storages/fsStorage.ts
137
216
  /**
138
217
  * Built-in filesystem storage driver.
@@ -159,7 +238,7 @@ function createStorage(build) {
159
238
  * })
160
239
  * ```
161
240
  */
162
- const fsStorage = createStorage(() => ({
241
+ const fsStorage = require_memoryStorage.createStorage(() => ({
163
242
  name: "fs",
164
243
  async hasItem(key) {
165
244
  try {
@@ -216,7 +295,7 @@ const fsStorage = createStorage(() => ({
216
295
  */
217
296
  function createSourcesView(storage) {
218
297
  const paths = /* @__PURE__ */ new Set();
219
- return createStorage(() => ({
298
+ return require_memoryStorage.createStorage(() => ({
220
299
  name: `${storage.name}:sources`,
221
300
  async hasItem(key) {
222
301
  return paths.has(key) && await storage.hasItem(key);
@@ -257,16 +336,10 @@ function resolveConfig(userConfig) {
257
336
  ...userConfig.output
258
337
  },
259
338
  storage: userConfig.storage ?? fsStorage(),
260
- devtools: userConfig.devtools ? {
261
- studioUrl: require_KubbDriver.DEFAULT_STUDIO_URL,
262
- ...typeof userConfig.devtools === "boolean" ? {} : userConfig.devtools
263
- } : void 0,
339
+ reporters: userConfig.reporters ?? [],
264
340
  plugins: userConfig.plugins ?? []
265
341
  };
266
342
  }
267
- function isInputPath(config) {
268
- return typeof config?.input === "object" && config.input !== null && "path" in config.input;
269
- }
270
343
  /**
271
344
  * Kubb code-generation instance bound to a single config entry. Resolves the user
272
345
  * config during `setup()` and shares `hooks`, `storage`, `driver`, and `config` across
@@ -289,7 +362,7 @@ var Kubb = class {
289
362
  #storage = null;
290
363
  constructor(userConfig, options = {}) {
291
364
  this.#userConfig = userConfig;
292
- this.hooks = options.hooks ?? new require_KubbDriver.AsyncEventEmitter();
365
+ this.hooks = options.hooks ?? new require_memoryStorage.AsyncEventEmitter();
293
366
  }
294
367
  get storage() {
295
368
  if (!this.#storage) throw new Error("[kubb] setup() must be called before accessing storage");
@@ -308,7 +381,7 @@ var Kubb = class {
308
381
  */
309
382
  async setup() {
310
383
  const config = resolveConfig(this.#userConfig);
311
- const driver = new require_KubbDriver.KubbDriver(config, { hooks: this.hooks });
384
+ const driver = new require_memoryStorage.KubbDriver(config, { hooks: this.hooks });
312
385
  const storage = createSourcesView(config.storage);
313
386
  this.hooks.setMaxListeners(Math.max(10, config.plugins.length * 4));
314
387
  if (config.output.clean) await config.storage.clear((0, node_path.resolve)(config.root, config.output.path));
@@ -323,9 +396,9 @@ var Kubb = class {
323
396
  */
324
397
  async build() {
325
398
  const out = await this.safeBuild();
326
- if (require_KubbDriver.Diagnostics.hasError(out.diagnostics)) {
327
- const errors = out.diagnostics.filter(require_KubbDriver.isProblemDiagnostic).filter((diagnostic) => diagnostic.severity === "error").map((diagnostic) => diagnostic.cause ?? new require_KubbDriver.DiagnosticError(diagnostic));
328
- throw new require_KubbDriver.BuildError(`Build failed with ${errors.length} ${errors.length === 1 ? "error" : "errors"}`, { errors });
399
+ if (require_memoryStorage.Diagnostics.hasError(out.diagnostics)) {
400
+ const errors = out.diagnostics.filter(require_memoryStorage.Diagnostics.isProblem).filter((diagnostic) => diagnostic.severity === "error").map((diagnostic) => diagnostic.cause ?? new require_memoryStorage.Diagnostics.Error(diagnostic));
401
+ throw new require_memoryStorage.BuildError(`Build failed with ${errors.length} ${errors.length === 1 ? "error" : "errors"}`, { errors });
329
402
  }
330
403
  return out;
331
404
  }
@@ -335,7 +408,7 @@ var Kubb = class {
335
408
  */
336
409
  async safeBuild() {
337
410
  try {
338
- var _usingCtx$1 = require_KubbDriver._usingCtx();
411
+ var _usingCtx$1 = require_memoryStorage._usingCtx();
339
412
  if (!this.#driver) await this.setup();
340
413
  const cleanup = _usingCtx$1.u(this);
341
414
  const driver = cleanup.driver;
@@ -386,8 +459,8 @@ function createKubb(userConfig, options = {}) {
386
459
  //#endregion
387
460
  //#region src/createReporter.ts
388
461
  /**
389
- * Defines a reporter. When the definition has a `flush`, the returned reporter buffers each value
390
- * `report` returns and hands the array to `flush` once, then clears it. Without a `flush`, nothing
462
+ * Defines a reporter. When the definition has a `drain`, the returned reporter buffers each value
463
+ * `report` returns and hands the array to `drain` once, then clears it. Without a `drain`, nothing
391
464
  * is buffered. Wiring the reporter onto the run's events is the host's job, so the reporter only
392
465
  * ever deals with a {@link GenerationResult}.
393
466
  *
@@ -400,17 +473,19 @@ function createKubb(userConfig, options = {}) {
400
473
  * report(result) {
401
474
  * return { status: Diagnostics.hasError(result.diagnostics) ? 'failed' : 'success', diagnostics: result.diagnostics }
402
475
  * },
403
- * flush(context, reports) {
476
+ * drain(context, reports) {
404
477
  * process.stdout.write(`${JSON.stringify(reports, null, 2)}\n`)
405
478
  * },
406
479
  * })
407
480
  * ```
408
481
  */
409
482
  function createReporter(reporter) {
410
- const flush = reporter.flush;
411
- if (!flush) return {
483
+ const drain = reporter.drain;
484
+ if (!drain) return {
412
485
  name: reporter.name,
413
- report: reporter.report
486
+ async report(result, context) {
487
+ await reporter.report(result, context);
488
+ }
414
489
  };
415
490
  const reports = [];
416
491
  return {
@@ -418,12 +493,409 @@ function createReporter(reporter) {
418
493
  async report(result, context) {
419
494
  reports.push(await reporter.report(result, context));
420
495
  },
421
- async flush(context) {
422
- await flush(context, reports);
496
+ async drain(context) {
497
+ await drain(context, reports);
423
498
  reports.length = 0;
424
499
  }
425
500
  };
426
501
  }
502
+ /**
503
+ * Picks the reporters whose `name` matches one of `names`, in the order the names are given.
504
+ * The config carries every available reporter, and the host selects which to activate by name
505
+ * (the CLI maps `--reporter` to this). Duplicate names and names without a matching reporter are
506
+ * skipped.
507
+ */
508
+ function selectReporters(reporters, names) {
509
+ const seen = /* @__PURE__ */ new Set();
510
+ const selected = [];
511
+ for (const name of names) {
512
+ if (seen.has(name)) continue;
513
+ seen.add(name);
514
+ const reporter = reporters.find((candidate) => candidate.name === name);
515
+ if (reporter) selected.push(reporter);
516
+ }
517
+ return selected;
518
+ }
519
+ //#endregion
520
+ //#region src/defineLogger.ts
521
+ /**
522
+ * Numeric log-level thresholds used internally to compare verbosity.
523
+ *
524
+ * Higher numbers are more verbose.
525
+ */
526
+ const logLevel = {
527
+ silent: Number.NEGATIVE_INFINITY,
528
+ error: 0,
529
+ warn: 1,
530
+ info: 3,
531
+ verbose: 4
532
+ };
533
+ /**
534
+ * Defines a typed logger. Use the second type parameter to declare a return
535
+ * value from `install`, which is handy when the logger exposes a sink factory
536
+ * or cleanup callback to the caller.
537
+ *
538
+ * @example Basic logger
539
+ * ```ts
540
+ * import { defineLogger } from '@kubb/core'
541
+ *
542
+ * export const myLogger = defineLogger({
543
+ * name: 'my-logger',
544
+ * install(context) {
545
+ * context.on('kubb:info', ({ message }) => console.log('ℹ', message))
546
+ * context.on('kubb:error', ({ error }) => console.error('✗', error.message))
547
+ * },
548
+ * })
549
+ * ```
550
+ *
551
+ * @example Logger that returns a hook sink factory
552
+ * ```ts
553
+ * import { defineLogger, type LoggerOptions } from '@kubb/core'
554
+ * import type { HookSinkFactory } from './sinks'
555
+ *
556
+ * export const myLogger = defineLogger<LoggerOptions, HookSinkFactory>({
557
+ * name: 'my-logger',
558
+ * install(context) {
559
+ * // … register event handlers …
560
+ * return () => ({ onStdout: console.log })
561
+ * },
562
+ * })
563
+ * ```
564
+ */
565
+ function defineLogger(logger) {
566
+ return logger;
567
+ }
568
+ //#endregion
569
+ //#region src/reporters/report.ts
570
+ /**
571
+ * Builds the normalized {@link Report} for one config from its {@link GenerationResult}. Splits the
572
+ * diagnostics into problems and per-plugin timings (slowest first) and derives the plugin and issue
573
+ * counts, so every reporter renders the same data.
574
+ */
575
+ function buildReport(result) {
576
+ const { config, diagnostics, filesCreated, status, hrStart } = result;
577
+ const failed = require_memoryStorage.Diagnostics.failedPlugins(diagnostics);
578
+ const total = config.plugins?.length ?? 0;
579
+ const counts = require_memoryStorage.Diagnostics.count(diagnostics);
580
+ const problems = diagnostics.filter(require_memoryStorage.Diagnostics.isProblem);
581
+ const timings = diagnostics.filter(require_memoryStorage.Diagnostics.isPerformance).sort((a, b) => b.duration - a.duration).map((diagnostic) => ({
582
+ plugin: diagnostic.plugin,
583
+ durationMs: diagnostic.duration
584
+ }));
585
+ return {
586
+ name: config.name ?? "",
587
+ status,
588
+ plugins: {
589
+ passed: total - failed.length,
590
+ failed,
591
+ total
592
+ },
593
+ counts,
594
+ filesCreated,
595
+ durationMs: require_memoryStorage.getElapsedMs(hrStart),
596
+ output: (0, node_path.resolve)(config.root, config.output.path),
597
+ timings,
598
+ diagnostics: problems.map((diagnostic) => require_memoryStorage.Diagnostics.serialize(diagnostic))
599
+ };
600
+ }
601
+ //#endregion
602
+ //#region src/reporters/cliReporter.ts
603
+ /**
604
+ * Builds the vitest/jest-style summary for one {@link Report}: right-aligned dim labels with
605
+ * `N passed (total)` counts, and a per-plugin `Timings` section when `showTimings`.
606
+ */
607
+ function buildSummaryLines(report, { showTimings }) {
608
+ const { status, plugins, counts, filesCreated, durationMs, output, timings } = report;
609
+ const rows = [];
610
+ 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})`]);
611
+ if (status === "failed" && plugins.failed.length > 0) rows.push(["Failed", plugins.failed.map((name) => randomCliColor(name)).join(", ")]);
612
+ if (counts.errors > 0 || counts.warnings > 0) {
613
+ 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(" | ");
614
+ rows.push(["Issues", issues]);
615
+ }
616
+ rows.push(["Files", `${(0, node_util.styleText)("green", String(filesCreated))} generated`]);
617
+ rows.push(["Duration", (0, node_util.styleText)("green", require_memoryStorage.formatMs(durationMs))]);
618
+ rows.push(["Output", output]);
619
+ const labelWidth = Math.max(...rows.map(([label]) => label.length), timings.length > 0 ? 7 : 0);
620
+ const lines = rows.map(([label, value]) => `${(0, node_util.styleText)("dim", label.padStart(labelWidth))} ${value}`);
621
+ if (showTimings && timings.length > 0) {
622
+ const nameWidth = Math.max(0, ...timings.map((timing) => timing.plugin.length));
623
+ const indent = " ".repeat(labelWidth + 2);
624
+ lines.push((0, node_util.styleText)("dim", "Timings".padStart(labelWidth)));
625
+ for (const timing of timings) {
626
+ const timeStr = require_memoryStorage.formatMs(timing.durationMs);
627
+ const barLength = Math.min(Math.ceil(timing.durationMs / 100), 10);
628
+ const bar = (0, node_util.styleText)("dim", "█".repeat(barLength));
629
+ lines.push(`${indent}${(0, node_util.styleText)("dim", "•")} ${timing.plugin.padEnd(nameWidth)} ${bar} ${timeStr}`);
630
+ }
631
+ }
632
+ return lines;
633
+ }
634
+ /**
635
+ * Renders the summary as plain `console.log` lines so it works in every CLI (no clack/TTY
636
+ * dependency): a blank line, the config name colored by status, then the summary rows.
637
+ */
638
+ function renderSummary(lines, { title, status }) {
639
+ console.log("");
640
+ if (title) console.log((0, node_util.styleText)(status === "failed" ? "red" : "green", title));
641
+ for (const line of lines) console.log(line);
642
+ }
643
+ /**
644
+ * The default `cli` reporter. Renders the {@link Report} for each config as it finishes, independent
645
+ * of the live logger view. Suppressed at `silent`; the `verbose` level adds the per-plugin timings.
646
+ */
647
+ const cliReporter = createReporter({
648
+ name: "cli",
649
+ report(result, { logLevel: logLevel$1 }) {
650
+ if (logLevel$1 <= logLevel.silent) return;
651
+ const report = buildReport(result);
652
+ renderSummary(buildSummaryLines(report, { showTimings: logLevel$1 >= logLevel.verbose }), {
653
+ title: report.name,
654
+ status: report.status
655
+ });
656
+ }
657
+ });
658
+ //#endregion
659
+ //#region src/reporters/fileReporter.ts
660
+ /**
661
+ * Builds the `## Summary` section: the same counts the cli and json reporters expose, as a list of
662
+ * `label value` rows with the labels padded to a common width.
663
+ */
664
+ function buildSummarySection(report) {
665
+ const { status, plugins, counts, filesCreated, durationMs, output } = report;
666
+ const rows = [["Status", status], ["Plugins", status === "success" ? `${plugins.passed} passed (${plugins.total})` : `${plugins.passed} passed | ${plugins.failed.length} failed (${plugins.total})`]];
667
+ if (plugins.failed.length > 0) rows.push(["Failed", plugins.failed.join(", ")]);
668
+ rows.push(["Issues", `${counts.errors} errors | ${counts.warnings} warnings | ${counts.infos} infos`]);
669
+ rows.push(["Files", `${filesCreated} generated`]);
670
+ rows.push(["Duration", require_memoryStorage.formatMs(durationMs)]);
671
+ rows.push(["Output", output]);
672
+ const labelWidth = Math.max(...rows.map(([label]) => label.length));
673
+ return [
674
+ "## Summary",
675
+ "",
676
+ ...rows.map(([label, value]) => ` ${label.padEnd(labelWidth)} ${value}`)
677
+ ];
678
+ }
679
+ /**
680
+ * Builds the `## Problems` section: each problem rendered in the miette block format, blocks
681
+ * separated by a blank line. Returns an empty array when there are no problems, so the caller
682
+ * can drop the heading.
683
+ */
684
+ function buildProblemSection(diagnostics) {
685
+ const problems = diagnostics.filter(require_memoryStorage.Diagnostics.isProblem);
686
+ if (problems.length === 0) return [];
687
+ return [
688
+ "## Problems",
689
+ "",
690
+ problems.map((diagnostic) => require_memoryStorage.Diagnostics.formatLines(diagnostic).join("\n")).join("\n\n")
691
+ ];
692
+ }
693
+ /**
694
+ * Builds the `## Timings` section from a {@link Report}: one `plugin duration` row per record,
695
+ * slowest first with the plugin names left-aligned and the durations right-aligned. Returns an
696
+ * empty array when there are no timings.
697
+ */
698
+ function buildTimingSection(report) {
699
+ const { timings } = report;
700
+ if (timings.length === 0) return [];
701
+ const nameWidth = Math.max(...timings.map((timing) => timing.plugin.length));
702
+ const durations = timings.map((timing) => require_memoryStorage.formatMs(timing.durationMs));
703
+ const durationWidth = Math.max(...durations.map((duration) => duration.length));
704
+ return [
705
+ "## Timings",
706
+ "",
707
+ ...timings.map((timing, index) => ` ${timing.plugin.padEnd(nameWidth)} ${durations[index].padStart(durationWidth)}`)
708
+ ];
709
+ }
710
+ /**
711
+ * The `file` reporter. Writes a config's {@link Report} to `.kubb/kubb-<name>-<timestamp>.log` as a
712
+ * plain-text document: a `# <name> — <timestamp>` header, a `## Summary` with the same counts the
713
+ * cli and json reporters expose, a `## Problems` section in the miette block format, and a
714
+ * `## Timings` section. Selected with `--reporter file` (or `reporters: ['file']`), replacing the
715
+ * old `--debug` flag.
716
+ *
717
+ * @note Unlike the streaming logger it replaced, it captures the collected diagnostics once a
718
+ * config finishes, not the live `kubb:info`/`kubb:plugin` event stream. Color is stripped so the
719
+ * file stays plain text even when the run is attached to a TTY.
720
+ */
721
+ const fileReporter = createReporter({
722
+ name: "file",
723
+ async report(result) {
724
+ const { diagnostics, config } = result;
725
+ if (diagnostics.length === 0) return;
726
+ const report = buildReport(result);
727
+ const content = (0, node_util.stripVTControlCharacters)([config.name ? `# ${config.name} — ${(/* @__PURE__ */ new Date()).toISOString()}` : `# ${(/* @__PURE__ */ new Date()).toISOString()}`, ...[
728
+ buildSummarySection(report),
729
+ buildProblemSection(diagnostics),
730
+ buildTimingSection(report)
731
+ ].filter((section) => section.length > 0).map((section) => section.join("\n"))].join("\n\n"));
732
+ const baseName = `${[
733
+ "kubb",
734
+ config.name,
735
+ Date.now()
736
+ ].filter(Boolean).join("-")}.log`;
737
+ const pathName = (0, node_path.resolve)(node_process.default.cwd(), ".kubb", baseName);
738
+ await write(pathName, `${content}\n`);
739
+ console.error(`Debug log written to ${(0, node_path.relative)(node_process.default.cwd(), pathName)}`);
740
+ }
741
+ });
742
+ //#endregion
743
+ //#region src/reporters/jsonReporter.ts
744
+ /**
745
+ * The `json` reporter. `report` returns one config's {@link Report}, which {@link createReporter}
746
+ * buffers, and `drain` writes them as a single pretty-printed JSON array on `kubb:lifecycle:end`.
747
+ * Buffering keeps a multi-config run one valid JSON document on stdout instead of concatenated
748
+ * objects that would break `jq .`. The terminal reporter is suppressed while `json` is active so
749
+ * stdout stays valid JSON.
750
+ */
751
+ const jsonReporter = createReporter({
752
+ name: "json",
753
+ report(result) {
754
+ return buildReport(result);
755
+ },
756
+ drain(_context, reports) {
757
+ node_process.default.stdout.write(`${JSON.stringify(reports, null, 2)}\n`);
758
+ }
759
+ });
760
+ //#endregion
761
+ //#region src/Telemetry.ts
762
+ /**
763
+ * Anonymous OTLP usage telemetry for the Kubb run. All methods are static, so call them as
764
+ * `Telemetry.build(...)`, `Telemetry.send(...)`, and `Telemetry.isDisabled()`. No file paths,
765
+ * OpenAPI specs, or secrets are ever included, and sending fails silently to never break a run.
766
+ */
767
+ var Telemetry = class Telemetry {
768
+ /**
769
+ * Returns `true` when telemetry is disabled via `DO_NOT_TRACK` or `KUBB_DISABLE_TELEMETRY`.
770
+ */
771
+ static isDisabled() {
772
+ return node_process.default.env["DO_NOT_TRACK"] === "1" || node_process.default.env["DO_NOT_TRACK"] === "true" || node_process.default.env["KUBB_DISABLE_TELEMETRY"] === "1" || node_process.default.env["KUBB_DISABLE_TELEMETRY"] === "true";
773
+ }
774
+ /**
775
+ * Build an anonymous telemetry payload from a completed generation run.
776
+ */
777
+ static build(options) {
778
+ const [seconds, nanoseconds] = node_process.default.hrtime(options.hrStart);
779
+ const duration = Math.round(seconds * 1e3 + nanoseconds / 1e6);
780
+ return {
781
+ command: options.command,
782
+ kubbVersion: options.kubbVersion,
783
+ nodeVersion: node_process.default.versions.node.split(".")[0],
784
+ platform: node_os.default.platform(),
785
+ ci: isCIEnvironment(),
786
+ plugins: options.plugins ?? [],
787
+ duration,
788
+ filesCreated: options.filesCreated ?? 0,
789
+ status: options.status
790
+ };
791
+ }
792
+ /**
793
+ * Convert a {@link TelemetryEvent} into an OTLP-compatible JSON trace payload.
794
+ * See https://opentelemetry.io/docs/languages/sdk-configuration/otlp-exporter/
795
+ */
796
+ static buildOtlpPayload(event) {
797
+ const traceId = (0, node_crypto.randomBytes)(16).toString("hex");
798
+ const spanId = (0, node_crypto.randomBytes)(8).toString("hex");
799
+ const endTimeNs = BigInt(Date.now()) * 1000000n;
800
+ const startTimeNs = endTimeNs - BigInt(event.duration) * 1000000n;
801
+ const attributes = [
802
+ {
803
+ key: "kubb.command",
804
+ value: { stringValue: event.command }
805
+ },
806
+ {
807
+ key: "kubb.version",
808
+ value: { stringValue: event.kubbVersion }
809
+ },
810
+ {
811
+ key: "kubb.node_version",
812
+ value: { stringValue: event.nodeVersion }
813
+ },
814
+ {
815
+ key: "kubb.platform",
816
+ value: { stringValue: event.platform }
817
+ },
818
+ {
819
+ key: "kubb.ci",
820
+ value: { boolValue: event.ci }
821
+ },
822
+ {
823
+ key: "kubb.files_created",
824
+ value: { intValue: event.filesCreated }
825
+ },
826
+ {
827
+ key: "kubb.status",
828
+ value: { stringValue: event.status }
829
+ },
830
+ {
831
+ key: "kubb.plugins",
832
+ value: { arrayValue: { values: event.plugins.map((p) => ({ kvlistValue: { values: [{
833
+ key: "name",
834
+ value: { stringValue: p.name }
835
+ }, {
836
+ key: "options",
837
+ value: { stringValue: JSON.stringify({
838
+ ...p.options,
839
+ usedEnumNames: void 0
840
+ }) }
841
+ }] } })) } }
842
+ }
843
+ ];
844
+ return { resourceSpans: [{
845
+ resource: { attributes: [
846
+ {
847
+ key: "service.name",
848
+ value: { stringValue: "kubb-core" }
849
+ },
850
+ {
851
+ key: "service.version",
852
+ value: { stringValue: event.kubbVersion }
853
+ },
854
+ {
855
+ key: "telemetry.sdk.language",
856
+ value: { stringValue: "nodejs" }
857
+ }
858
+ ] },
859
+ scopeSpans: [{
860
+ scope: {
861
+ name: "kubb-core",
862
+ version: event.kubbVersion
863
+ },
864
+ spans: [{
865
+ traceId,
866
+ spanId,
867
+ name: event.command,
868
+ kind: 1,
869
+ startTimeUnixNano: String(startTimeNs),
870
+ endTimeUnixNano: String(endTimeNs),
871
+ attributes,
872
+ status: { code: event.status === "success" ? 1 : 2 }
873
+ }]
874
+ }]
875
+ }] };
876
+ }
877
+ /**
878
+ * Send an anonymous telemetry event to the Kubb OTLP endpoint. Respects `DO_NOT_TRACK` and
879
+ * `KUBB_DISABLE_TELEMETRY`, and fails silently so telemetry never interrupts a run.
880
+ */
881
+ static async send(event) {
882
+ if (Telemetry.isDisabled()) return;
883
+ await executeIfOnline(async () => {
884
+ try {
885
+ await fetch(`${require_memoryStorage.OTLP_ENDPOINT}/v1/traces`, {
886
+ method: "POST",
887
+ headers: {
888
+ "Content-Type": "application/json",
889
+ "Kubb-Telemetry-Version": "1",
890
+ "Kubb-Telemetry-Source": "kubb-core"
891
+ },
892
+ body: JSON.stringify(Telemetry.buildOtlpPayload(event)),
893
+ signal: AbortSignal.timeout(5e3)
894
+ });
895
+ } catch (_e) {}
896
+ });
897
+ }
898
+ };
427
899
  //#endregion
428
900
  //#region src/createRenderer.ts
429
901
  /**
@@ -499,43 +971,6 @@ function defineGenerator(generator) {
499
971
  return generator;
500
972
  }
501
973
  //#endregion
502
- //#region src/defineLogger.ts
503
- /**
504
- * Defines a typed logger. Use the second type parameter to declare a return
505
- * value from `install`, which is handy when the logger exposes a sink factory
506
- * or cleanup callback to the caller.
507
- *
508
- * @example Basic logger
509
- * ```ts
510
- * import { defineLogger } from '@kubb/core'
511
- *
512
- * export const myLogger = defineLogger({
513
- * name: 'my-logger',
514
- * install(context) {
515
- * context.on('kubb:info', ({ message }) => console.log('ℹ', message))
516
- * context.on('kubb:error', ({ error }) => console.error('✗', error.message))
517
- * },
518
- * })
519
- * ```
520
- *
521
- * @example Logger that returns a hook sink factory
522
- * ```ts
523
- * import { defineLogger, type LoggerOptions } from '@kubb/core'
524
- * import type { HookSinkFactory } from './sinks'
525
- *
526
- * export const myLogger = defineLogger<LoggerOptions, HookSinkFactory>({
527
- * name: 'my-logger',
528
- * install(context) {
529
- * // … register event handlers …
530
- * return () => ({ onStdout: console.log })
531
- * },
532
- * })
533
- * ```
534
- */
535
- function defineLogger(logger) {
536
- return logger;
537
- }
538
- //#endregion
539
974
  //#region src/defineMiddleware.ts
540
975
  /**
541
976
  * Creates a middleware factory. Middleware fires after every plugin handler
@@ -607,89 +1042,33 @@ function defineParser(parser) {
607
1042
  return parser;
608
1043
  }
609
1044
  //#endregion
610
- //#region src/storages/memoryStorage.ts
611
- /**
612
- * In-memory storage driver. Useful for testing and dry-run scenarios where
613
- * generated output should be captured without touching the filesystem.
614
- *
615
- * All data lives in a `Map` scoped to the storage instance and is discarded
616
- * when the instance is garbage-collected.
617
- *
618
- * @example
619
- * ```ts
620
- * import { memoryStorage } from '@kubb/core'
621
- * import { defineConfig } from 'kubb'
622
- *
623
- * export default defineConfig({
624
- * input: { path: './petStore.yaml' },
625
- * output: { path: './src/gen' },
626
- * storage: memoryStorage(),
627
- * })
628
- * ```
629
- */
630
- const memoryStorage = createStorage(() => {
631
- const store = /* @__PURE__ */ new Map();
632
- return {
633
- name: "memory",
634
- async hasItem(key) {
635
- return store.has(key);
636
- },
637
- async getItem(key) {
638
- return store.get(key) ?? null;
639
- },
640
- async setItem(key, value) {
641
- store.set(key, value);
642
- },
643
- async removeItem(key) {
644
- store.delete(key);
645
- },
646
- async getKeys(base) {
647
- const keys = [...store.keys()];
648
- return base ? keys.filter((k) => k.startsWith(base)) : keys;
649
- },
650
- async clear(base) {
651
- if (!base) {
652
- store.clear();
653
- return;
654
- }
655
- for (const key of store.keys()) if (key.startsWith(base)) store.delete(key);
656
- }
657
- };
658
- });
659
- //#endregion
660
- exports.AsyncEventEmitter = require_KubbDriver.AsyncEventEmitter;
661
- exports.DiagnosticError = require_KubbDriver.DiagnosticError;
662
- exports.Diagnostics = require_KubbDriver.Diagnostics;
663
- exports.FileManager = require_KubbDriver.FileManager;
664
- exports.FileProcessor = require_KubbDriver.FileProcessor;
665
- exports.KubbDriver = require_KubbDriver.KubbDriver;
666
- exports.URLPath = require_KubbDriver.URLPath;
1045
+ exports.AsyncEventEmitter = require_memoryStorage.AsyncEventEmitter;
1046
+ exports.Diagnostics = require_memoryStorage.Diagnostics;
1047
+ exports.KubbDriver = require_memoryStorage.KubbDriver;
1048
+ exports.Telemetry = Telemetry;
667
1049
  Object.defineProperty(exports, "ast", {
668
1050
  enumerable: true,
669
1051
  get: function() {
670
1052
  return _kubb_ast;
671
1053
  }
672
1054
  });
1055
+ exports.cliReporter = cliReporter;
673
1056
  exports.createAdapter = createAdapter;
674
1057
  exports.createKubb = createKubb;
675
1058
  exports.createRenderer = createRenderer;
676
1059
  exports.createReporter = createReporter;
677
- exports.createStorage = createStorage;
1060
+ exports.createStorage = require_memoryStorage.createStorage;
678
1061
  exports.defineGenerator = defineGenerator;
679
1062
  exports.defineLogger = defineLogger;
680
1063
  exports.defineMiddleware = defineMiddleware;
681
1064
  exports.defineParser = defineParser;
682
- exports.definePlugin = require_KubbDriver.definePlugin;
683
- exports.defineResolver = require_KubbDriver.defineResolver;
684
- exports.diagnosticCatalog = require_KubbDriver.diagnosticCatalog;
685
- exports.diagnosticCode = require_KubbDriver.diagnosticCode;
1065
+ exports.definePlugin = require_memoryStorage.definePlugin;
1066
+ exports.defineResolver = require_memoryStorage.defineResolver;
1067
+ exports.fileReporter = fileReporter;
686
1068
  exports.fsStorage = fsStorage;
687
- exports.isInputPath = isInputPath;
688
- exports.isPerformanceDiagnostic = require_KubbDriver.isPerformanceDiagnostic;
689
- exports.isProblemDiagnostic = require_KubbDriver.isProblemDiagnostic;
690
- exports.isUpdateDiagnostic = require_KubbDriver.isUpdateDiagnostic;
691
- exports.logLevel = require_KubbDriver.logLevel;
692
- exports.memoryStorage = memoryStorage;
693
- exports.narrowDiagnostic = require_KubbDriver.narrowDiagnostic;
1069
+ exports.jsonReporter = jsonReporter;
1070
+ exports.logLevel = logLevel;
1071
+ exports.memoryStorage = require_memoryStorage.memoryStorage;
1072
+ exports.selectReporters = selectReporters;
694
1073
 
695
1074
  //# sourceMappingURL=index.cjs.map