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