@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/{diagnostics-CYfKPtbU.d.ts → diagnostics-DhfW8YpT.d.ts} +289 -347
- package/dist/index.cjs +553 -174
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.ts +164 -119
- package/dist/index.js +532 -149
- package/dist/index.js.map +1 -1
- package/dist/{KubbDriver-CyNF-NIb.js → memoryStorage-CNQTs-YG.js} +240 -157
- package/dist/memoryStorage-CNQTs-YG.js.map +1 -0
- package/dist/{KubbDriver-BYBUfOZ8.cjs → memoryStorage-DHi1d0To.cjs} +255 -196
- package/dist/memoryStorage-DHi1d0To.cjs.map +1 -0
- package/dist/mocks.cjs +36 -6
- package/dist/mocks.cjs.map +1 -1
- package/dist/mocks.d.ts +30 -3
- package/dist/mocks.js +33 -5
- package/dist/mocks.js.map +1 -1
- package/package.json +4 -4
- package/src/KubbDriver.ts +21 -65
- package/src/Telemetry.ts +278 -0
- package/src/constants.ts +14 -21
- package/src/createKubb.ts +22 -50
- package/src/createReporter.ts +43 -13
- package/src/defineGenerator.ts +2 -6
- package/src/defineLogger.ts +13 -1
- package/src/defineResolver.ts +3 -4
- package/src/diagnostics.ts +130 -61
- package/src/index.ts +8 -16
- package/src/mocks.ts +57 -2
- package/src/reporters/cliReporter.ts +90 -0
- package/src/reporters/fileReporter.ts +103 -0
- package/src/reporters/jsonReporter.ts +20 -0
- package/src/reporters/report.ts +85 -0
- package/src/types.ts +0 -1
- package/dist/KubbDriver-BYBUfOZ8.cjs.map +0 -1
- package/dist/KubbDriver-CyNF-NIb.js.map +0 -1
- package/src/devtools.ts +0 -53
package/dist/index.js
CHANGED
|
@@ -1,8 +1,83 @@
|
|
|
1
1
|
import "./chunk-C0LytTxp.js";
|
|
2
|
-
import {
|
|
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
|
-
|
|
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(
|
|
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 `
|
|
389
|
-
* `report` returns and hands the array to `
|
|
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
|
-
*
|
|
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
|
|
410
|
-
if (!
|
|
480
|
+
const drain = reporter.drain;
|
|
481
|
+
if (!drain) return {
|
|
411
482
|
name: reporter.name,
|
|
412
|
-
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
|
|
421
|
-
await
|
|
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
|
-
|
|
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
|