@kubb/core 5.0.0-beta.4 → 5.0.0-beta.40
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/README.md +15 -148
- package/dist/diagnostics-DhfW8YpT.d.ts +2963 -0
- package/dist/index.cjs +775 -1193
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.ts +165 -171
- package/dist/index.js +760 -1188
- package/dist/index.js.map +1 -1
- package/dist/memoryStorage-DTv1Kub1.js +2852 -0
- package/dist/memoryStorage-DTv1Kub1.js.map +1 -0
- package/dist/memoryStorage-Dkxnid2K.cjs +2985 -0
- package/dist/memoryStorage-Dkxnid2K.cjs.map +1 -0
- package/dist/mocks.cjs +80 -18
- package/dist/mocks.cjs.map +1 -1
- package/dist/mocks.d.ts +35 -8
- package/dist/mocks.js +81 -21
- package/dist/mocks.js.map +1 -1
- package/package.json +8 -19
- package/src/FileManager.ts +85 -63
- package/src/FileProcessor.ts +171 -43
- package/src/HookRegistry.ts +45 -0
- package/src/KubbDriver.ts +897 -0
- package/src/Telemetry.ts +278 -0
- package/src/Transform.ts +58 -0
- package/src/constants.ts +111 -19
- package/src/createAdapter.ts +113 -17
- package/src/createKubb.ts +921 -493
- package/src/createRenderer.ts +58 -27
- package/src/createReporter.ts +147 -0
- package/src/createStorage.ts +36 -23
- package/src/defineGenerator.ts +129 -17
- package/src/defineLogger.ts +78 -7
- package/src/defineMiddleware.ts +19 -17
- package/src/defineParser.ts +30 -13
- package/src/definePlugin.ts +320 -17
- package/src/defineResolver.ts +381 -179
- package/src/diagnostics.ts +660 -0
- package/src/index.ts +8 -6
- package/src/mocks.ts +92 -19
- 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/storages/fsStorage.ts +13 -37
- package/src/types.ts +59 -1297
- package/dist/PluginDriver-Ds-Us-e4.cjs +0 -1036
- package/dist/PluginDriver-Ds-Us-e4.cjs.map +0 -1
- package/dist/PluginDriver-Wi34Pegx.js +0 -945
- package/dist/PluginDriver-Wi34Pegx.js.map +0 -1
- package/dist/types-Cd0jhNmx.d.ts +0 -2153
- package/src/Kubb.ts +0 -300
- package/src/PluginDriver.ts +0 -424
- package/src/devtools.ts +0 -59
- package/src/renderNode.ts +0 -35
- package/src/utils/diagnostics.ts +0 -18
- package/src/utils/isInputPath.ts +0 -10
- package/src/utils/packageJSON.ts +0 -99
- /package/dist/{chunk--u3MIqq1.js → chunk-C0LytTxp.js} +0 -0
package/dist/index.cjs
CHANGED
|
@@ -1,200 +1,87 @@
|
|
|
1
1
|
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
2
|
-
const
|
|
3
|
-
let
|
|
2
|
+
const require_memoryStorage = require("./memoryStorage-Dkxnid2K.cjs");
|
|
3
|
+
let node_util = require("node:util");
|
|
4
|
+
let node_crypto = require("node:crypto");
|
|
4
5
|
let node_fs_promises = require("node:fs/promises");
|
|
5
6
|
let node_path = require("node:path");
|
|
7
|
+
let node_dns = require("node:dns");
|
|
6
8
|
let _kubb_ast = require("@kubb/ast");
|
|
7
|
-
_kubb_ast =
|
|
9
|
+
_kubb_ast = require_memoryStorage.__toESM(_kubb_ast, 1);
|
|
8
10
|
let node_process = require("node:process");
|
|
9
|
-
|
|
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
|
|
10
15
|
/**
|
|
11
|
-
*
|
|
12
|
-
*
|
|
13
|
-
*
|
|
14
|
-
* @example
|
|
15
|
-
* ```ts
|
|
16
|
-
* throw new BuildError('Build failed', { errors: [err1, err2] })
|
|
17
|
-
* ```
|
|
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
18
|
*/
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
*
|
|
31
|
-
* @example
|
|
32
|
-
* ```ts
|
|
33
|
-
* try { ... } catch(err) {
|
|
34
|
-
* throw new BuildError('Build failed', { cause: toError(err), errors: [] })
|
|
35
|
-
* }
|
|
36
|
-
* ```
|
|
37
|
-
*/
|
|
38
|
-
function toError(value) {
|
|
39
|
-
return value instanceof Error ? value : new Error(String(value));
|
|
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
|
+
};
|
|
40
30
|
}
|
|
41
|
-
//#endregion
|
|
42
|
-
//#region ../../internals/utils/src/asyncEventEmitter.ts
|
|
43
31
|
/**
|
|
44
|
-
*
|
|
45
|
-
*
|
|
46
|
-
*
|
|
47
|
-
* @example
|
|
48
|
-
* ```ts
|
|
49
|
-
* const emitter = new AsyncEventEmitter<{ build: [name: string] }>()
|
|
50
|
-
* emitter.on('build', async (name) => { console.log(name) })
|
|
51
|
-
* await emitter.emit('build', 'petstore') // all listeners awaited
|
|
52
|
-
* ```
|
|
32
|
+
* Returns a function that wraps a string in a 24-bit ANSI true-color escape sequence
|
|
33
|
+
* for the given hex color.
|
|
53
34
|
*/
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
constructor(maxListener = 10) {
|
|
60
|
-
this.#emitter.setMaxListeners(maxListener);
|
|
61
|
-
}
|
|
62
|
-
#emitter = new node_events.EventEmitter();
|
|
63
|
-
/**
|
|
64
|
-
* Emits `eventName` and awaits all registered listeners sequentially.
|
|
65
|
-
* Throws if any listener rejects, wrapping the cause with the event name and serialized arguments.
|
|
66
|
-
*
|
|
67
|
-
* @example
|
|
68
|
-
* ```ts
|
|
69
|
-
* await emitter.emit('build', 'petstore')
|
|
70
|
-
* ```
|
|
71
|
-
*/
|
|
72
|
-
async emit(eventName, ...eventArgs) {
|
|
73
|
-
const listeners = this.#emitter.listeners(eventName);
|
|
74
|
-
if (listeners.length === 0) return;
|
|
75
|
-
for (const listener of listeners) try {
|
|
76
|
-
await listener(...eventArgs);
|
|
77
|
-
} catch (err) {
|
|
78
|
-
let serializedArgs;
|
|
79
|
-
try {
|
|
80
|
-
serializedArgs = JSON.stringify(eventArgs);
|
|
81
|
-
} catch {
|
|
82
|
-
serializedArgs = String(eventArgs);
|
|
83
|
-
}
|
|
84
|
-
throw new Error(`Error in async listener for "${eventName}" with eventArgs ${serializedArgs}`, { cause: toError(err) });
|
|
85
|
-
}
|
|
86
|
-
}
|
|
87
|
-
/**
|
|
88
|
-
* Registers a persistent listener for `eventName`.
|
|
89
|
-
*
|
|
90
|
-
* @example
|
|
91
|
-
* ```ts
|
|
92
|
-
* emitter.on('build', async (name) => { console.log(name) })
|
|
93
|
-
* ```
|
|
94
|
-
*/
|
|
95
|
-
on(eventName, handler) {
|
|
96
|
-
this.#emitter.on(eventName, handler);
|
|
97
|
-
}
|
|
98
|
-
/**
|
|
99
|
-
* Registers a one-shot listener that removes itself after the first invocation.
|
|
100
|
-
*
|
|
101
|
-
* @example
|
|
102
|
-
* ```ts
|
|
103
|
-
* emitter.onOnce('build', async (name) => { console.log(name) })
|
|
104
|
-
* ```
|
|
105
|
-
*/
|
|
106
|
-
onOnce(eventName, handler) {
|
|
107
|
-
const wrapper = (...args) => {
|
|
108
|
-
this.off(eventName, wrapper);
|
|
109
|
-
return handler(...args);
|
|
110
|
-
};
|
|
111
|
-
this.on(eventName, wrapper);
|
|
112
|
-
}
|
|
113
|
-
/**
|
|
114
|
-
* Removes a previously registered listener.
|
|
115
|
-
*
|
|
116
|
-
* @example
|
|
117
|
-
* ```ts
|
|
118
|
-
* emitter.off('build', handler)
|
|
119
|
-
* ```
|
|
120
|
-
*/
|
|
121
|
-
off(eventName, handler) {
|
|
122
|
-
this.#emitter.off(eventName, handler);
|
|
123
|
-
}
|
|
124
|
-
/**
|
|
125
|
-
* Returns the number of listeners registered for `eventName`.
|
|
126
|
-
*
|
|
127
|
-
* @example
|
|
128
|
-
* ```ts
|
|
129
|
-
* emitter.on('build', handler)
|
|
130
|
-
* emitter.listenerCount('build') // 1
|
|
131
|
-
* ```
|
|
132
|
-
*/
|
|
133
|
-
listenerCount(eventName) {
|
|
134
|
-
return this.#emitter.listenerCount(eventName);
|
|
135
|
-
}
|
|
136
|
-
/**
|
|
137
|
-
* Removes all listeners from every event channel.
|
|
138
|
-
*
|
|
139
|
-
* @example
|
|
140
|
-
* ```ts
|
|
141
|
-
* emitter.removeAll()
|
|
142
|
-
* ```
|
|
143
|
-
*/
|
|
144
|
-
removeAll() {
|
|
145
|
-
this.#emitter.removeAllListeners();
|
|
146
|
-
}
|
|
147
|
-
};
|
|
148
|
-
//#endregion
|
|
149
|
-
//#region ../../internals/utils/src/time.ts
|
|
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");
|
|
150
40
|
/**
|
|
151
|
-
*
|
|
152
|
-
* Rounds to 2 decimal places for sub-millisecond precision without noise.
|
|
153
|
-
*
|
|
154
|
-
* @example
|
|
155
|
-
* ```ts
|
|
156
|
-
* const start = process.hrtime()
|
|
157
|
-
* doWork()
|
|
158
|
-
* getElapsedMs(start) // 42.35
|
|
159
|
-
* ```
|
|
41
|
+
* ANSI color names used by {@link randomCliColor} for deterministic terminal coloring.
|
|
160
42
|
*/
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
43
|
+
const randomColors = [
|
|
44
|
+
"black",
|
|
45
|
+
"red",
|
|
46
|
+
"green",
|
|
47
|
+
"yellow",
|
|
48
|
+
"blue",
|
|
49
|
+
"white",
|
|
50
|
+
"magenta",
|
|
51
|
+
"cyan",
|
|
52
|
+
"gray"
|
|
53
|
+
];
|
|
166
54
|
/**
|
|
167
|
-
*
|
|
55
|
+
* Wraps `text` in a deterministic ANSI color derived from the text's SHA-256 hash.
|
|
168
56
|
*
|
|
169
57
|
* @example
|
|
170
58
|
* ```ts
|
|
171
|
-
*
|
|
172
|
-
* formatMs(1500) // '1.50s'
|
|
173
|
-
* formatMs(90000) // '1m 30.0s'
|
|
59
|
+
* randomCliColor('petstore') // '\x1b[33m' + 'petstore' + '\x1b[39m' (always the same color for 'petstore')
|
|
174
60
|
* ```
|
|
175
61
|
*/
|
|
176
|
-
function
|
|
177
|
-
if (
|
|
178
|
-
|
|
179
|
-
return `${Math.round(ms)}ms`;
|
|
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);
|
|
180
65
|
}
|
|
181
66
|
//#endregion
|
|
182
|
-
//#region ../../internals/utils/src/
|
|
67
|
+
//#region ../../internals/utils/src/env.ts
|
|
183
68
|
/**
|
|
184
|
-
*
|
|
185
|
-
*
|
|
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.
|
|
186
72
|
*
|
|
187
73
|
* @example
|
|
188
74
|
* ```ts
|
|
189
|
-
* if (
|
|
190
|
-
*
|
|
75
|
+
* if (isCIEnvironment()) {
|
|
76
|
+
* logger.level = 'error'
|
|
191
77
|
* }
|
|
192
78
|
* ```
|
|
193
79
|
*/
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
return (0, node_fs_promises.access)(path).then(() => true, () => false);
|
|
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);
|
|
197
82
|
}
|
|
83
|
+
//#endregion
|
|
84
|
+
//#region ../../internals/utils/src/fs.ts
|
|
198
85
|
/**
|
|
199
86
|
* Writes `data` to `path`, trimming leading/trailing whitespace before saving.
|
|
200
87
|
* Skips the write when the trimmed content is empty or identical to what is already on disk.
|
|
@@ -245,502 +132,88 @@ async function clean(path) {
|
|
|
245
132
|
});
|
|
246
133
|
}
|
|
247
134
|
//#endregion
|
|
248
|
-
//#region ../../internals/utils/src/
|
|
135
|
+
//#region ../../internals/utils/src/network.ts
|
|
249
136
|
/**
|
|
250
|
-
*
|
|
251
|
-
* @link https://github.com/jonschlinkert/reserved/blob/master/index.js
|
|
137
|
+
* Well-known stable domains used as DNS probes to check internet connectivity.
|
|
252
138
|
*/
|
|
253
|
-
const
|
|
254
|
-
"
|
|
255
|
-
"
|
|
256
|
-
"
|
|
257
|
-
|
|
258
|
-
"byte",
|
|
259
|
-
"case",
|
|
260
|
-
"catch",
|
|
261
|
-
"char",
|
|
262
|
-
"class",
|
|
263
|
-
"const",
|
|
264
|
-
"continue",
|
|
265
|
-
"debugger",
|
|
266
|
-
"default",
|
|
267
|
-
"delete",
|
|
268
|
-
"do",
|
|
269
|
-
"double",
|
|
270
|
-
"else",
|
|
271
|
-
"enum",
|
|
272
|
-
"eval",
|
|
273
|
-
"export",
|
|
274
|
-
"extends",
|
|
275
|
-
"false",
|
|
276
|
-
"final",
|
|
277
|
-
"finally",
|
|
278
|
-
"float",
|
|
279
|
-
"for",
|
|
280
|
-
"function",
|
|
281
|
-
"goto",
|
|
282
|
-
"if",
|
|
283
|
-
"implements",
|
|
284
|
-
"import",
|
|
285
|
-
"in",
|
|
286
|
-
"instanceof",
|
|
287
|
-
"int",
|
|
288
|
-
"interface",
|
|
289
|
-
"let",
|
|
290
|
-
"long",
|
|
291
|
-
"native",
|
|
292
|
-
"new",
|
|
293
|
-
"null",
|
|
294
|
-
"package",
|
|
295
|
-
"private",
|
|
296
|
-
"protected",
|
|
297
|
-
"public",
|
|
298
|
-
"return",
|
|
299
|
-
"short",
|
|
300
|
-
"static",
|
|
301
|
-
"super",
|
|
302
|
-
"switch",
|
|
303
|
-
"synchronized",
|
|
304
|
-
"this",
|
|
305
|
-
"throw",
|
|
306
|
-
"throws",
|
|
307
|
-
"transient",
|
|
308
|
-
"true",
|
|
309
|
-
"try",
|
|
310
|
-
"typeof",
|
|
311
|
-
"var",
|
|
312
|
-
"void",
|
|
313
|
-
"volatile",
|
|
314
|
-
"while",
|
|
315
|
-
"with",
|
|
316
|
-
"yield",
|
|
317
|
-
"Array",
|
|
318
|
-
"Date",
|
|
319
|
-
"hasOwnProperty",
|
|
320
|
-
"Infinity",
|
|
321
|
-
"isFinite",
|
|
322
|
-
"isNaN",
|
|
323
|
-
"isPrototypeOf",
|
|
324
|
-
"length",
|
|
325
|
-
"Math",
|
|
326
|
-
"name",
|
|
327
|
-
"NaN",
|
|
328
|
-
"Number",
|
|
329
|
-
"Object",
|
|
330
|
-
"prototype",
|
|
331
|
-
"String",
|
|
332
|
-
"toString",
|
|
333
|
-
"undefined",
|
|
334
|
-
"valueOf"
|
|
335
|
-
]);
|
|
139
|
+
const TEST_DOMAINS = [
|
|
140
|
+
"dns.google.com",
|
|
141
|
+
"cloudflare.com",
|
|
142
|
+
"one.one.one.one"
|
|
143
|
+
];
|
|
336
144
|
/**
|
|
337
|
-
* Returns `true` when
|
|
145
|
+
* Returns `true` when the system has internet connectivity.
|
|
146
|
+
* Probes DNS resolution against well-known stable domains.
|
|
338
147
|
*
|
|
339
148
|
* @example
|
|
340
149
|
* ```ts
|
|
341
|
-
*
|
|
342
|
-
*
|
|
343
|
-
*
|
|
150
|
+
* if (await isOnline()) {
|
|
151
|
+
* await fetchLatestVersion()
|
|
152
|
+
* }
|
|
344
153
|
* ```
|
|
345
154
|
*/
|
|
346
|
-
function
|
|
347
|
-
|
|
348
|
-
|
|
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;
|
|
349
161
|
}
|
|
350
|
-
//#endregion
|
|
351
|
-
//#region ../../internals/utils/src/urlPath.ts
|
|
352
|
-
/**
|
|
353
|
-
* Parses and transforms an OpenAPI/Swagger path string into various URL formats.
|
|
354
|
-
*
|
|
355
|
-
* @example
|
|
356
|
-
* const p = new URLPath('/pet/{petId}')
|
|
357
|
-
* p.URL // '/pet/:petId'
|
|
358
|
-
* p.template // '`/pet/${petId}`'
|
|
359
|
-
*/
|
|
360
|
-
var URLPath = class {
|
|
361
|
-
/**
|
|
362
|
-
* The raw OpenAPI/Swagger path string, e.g. `/pet/{petId}`.
|
|
363
|
-
*/
|
|
364
|
-
path;
|
|
365
|
-
#options;
|
|
366
|
-
constructor(path, options = {}) {
|
|
367
|
-
this.path = path;
|
|
368
|
-
this.#options = options;
|
|
369
|
-
}
|
|
370
|
-
/** Converts the OpenAPI path to Express-style colon syntax, e.g. `/pet/{petId}` → `/pet/:petId`.
|
|
371
|
-
*
|
|
372
|
-
* @example
|
|
373
|
-
* ```ts
|
|
374
|
-
* new URLPath('/pet/{petId}').URL // '/pet/:petId'
|
|
375
|
-
* ```
|
|
376
|
-
*/
|
|
377
|
-
get URL() {
|
|
378
|
-
return this.toURLPath();
|
|
379
|
-
}
|
|
380
|
-
/** Returns `true` when `path` is a fully-qualified URL (e.g. starts with `https://`).
|
|
381
|
-
*
|
|
382
|
-
* @example
|
|
383
|
-
* ```ts
|
|
384
|
-
* new URLPath('https://petstore.swagger.io/v2/pet').isURL // true
|
|
385
|
-
* new URLPath('/pet/{petId}').isURL // false
|
|
386
|
-
* ```
|
|
387
|
-
*/
|
|
388
|
-
get isURL() {
|
|
389
|
-
try {
|
|
390
|
-
return !!new URL(this.path).href;
|
|
391
|
-
} catch {
|
|
392
|
-
return false;
|
|
393
|
-
}
|
|
394
|
-
}
|
|
395
|
-
/**
|
|
396
|
-
* Converts the OpenAPI path to a TypeScript template literal string.
|
|
397
|
-
*
|
|
398
|
-
* @example
|
|
399
|
-
* new URLPath('/pet/{petId}').template // '`/pet/${petId}`'
|
|
400
|
-
* new URLPath('/account/monetary-accountID').template // '`/account/${monetaryAccountId}`'
|
|
401
|
-
*/
|
|
402
|
-
get template() {
|
|
403
|
-
return this.toTemplateString();
|
|
404
|
-
}
|
|
405
|
-
/** Returns the path and its extracted params as a structured `URLObject`, or as a stringified expression when `stringify` is set.
|
|
406
|
-
*
|
|
407
|
-
* @example
|
|
408
|
-
* ```ts
|
|
409
|
-
* new URLPath('/pet/{petId}').object
|
|
410
|
-
* // { url: '/pet/:petId', params: { petId: 'petId' } }
|
|
411
|
-
* ```
|
|
412
|
-
*/
|
|
413
|
-
get object() {
|
|
414
|
-
return this.toObject();
|
|
415
|
-
}
|
|
416
|
-
/** Returns a map of path parameter names, or `undefined` when the path has no parameters.
|
|
417
|
-
*
|
|
418
|
-
* @example
|
|
419
|
-
* ```ts
|
|
420
|
-
* new URLPath('/pet/{petId}').params // { petId: 'petId' }
|
|
421
|
-
* new URLPath('/pet').params // undefined
|
|
422
|
-
* ```
|
|
423
|
-
*/
|
|
424
|
-
get params() {
|
|
425
|
-
return this.getParams();
|
|
426
|
-
}
|
|
427
|
-
#transformParam(raw) {
|
|
428
|
-
const param = isValidVarName(raw) ? raw : require_PluginDriver.camelCase(raw);
|
|
429
|
-
return this.#options.casing === "camelcase" ? require_PluginDriver.camelCase(param) : param;
|
|
430
|
-
}
|
|
431
|
-
/**
|
|
432
|
-
* Iterates over every `{param}` token in `path`, calling `fn` with the raw token and transformed name.
|
|
433
|
-
*/
|
|
434
|
-
#eachParam(fn) {
|
|
435
|
-
for (const match of this.path.matchAll(/\{([^}]+)\}/g)) {
|
|
436
|
-
const raw = match[1];
|
|
437
|
-
fn(raw, this.#transformParam(raw));
|
|
438
|
-
}
|
|
439
|
-
}
|
|
440
|
-
toObject({ type = "path", replacer, stringify } = {}) {
|
|
441
|
-
const object = {
|
|
442
|
-
url: type === "path" ? this.toURLPath() : this.toTemplateString({ replacer }),
|
|
443
|
-
params: this.getParams()
|
|
444
|
-
};
|
|
445
|
-
if (stringify) {
|
|
446
|
-
if (type === "template") return JSON.stringify(object).replaceAll("'", "").replaceAll(`"`, "");
|
|
447
|
-
if (object.params) return `{ url: '${object.url}', params: ${JSON.stringify(object.params).replaceAll("'", "").replaceAll(`"`, "")} }`;
|
|
448
|
-
return `{ url: '${object.url}' }`;
|
|
449
|
-
}
|
|
450
|
-
return object;
|
|
451
|
-
}
|
|
452
|
-
/**
|
|
453
|
-
* Converts the OpenAPI path to a TypeScript template literal string.
|
|
454
|
-
* An optional `replacer` can transform each extracted parameter name before interpolation.
|
|
455
|
-
*
|
|
456
|
-
* @example
|
|
457
|
-
* new URLPath('/pet/{petId}').toTemplateString() // '`/pet/${petId}`'
|
|
458
|
-
*/
|
|
459
|
-
toTemplateString({ prefix = "", replacer } = {}) {
|
|
460
|
-
return `\`${prefix}${this.path.split(/\{([^}]+)\}/).map((part, i) => {
|
|
461
|
-
if (i % 2 === 0) return part;
|
|
462
|
-
const param = this.#transformParam(part);
|
|
463
|
-
return `\${${replacer ? replacer(param) : param}}`;
|
|
464
|
-
}).join("")}\``;
|
|
465
|
-
}
|
|
466
|
-
/**
|
|
467
|
-
* Extracts all `{param}` segments from the path and returns them as a key-value map.
|
|
468
|
-
* An optional `replacer` transforms each parameter name in both key and value positions.
|
|
469
|
-
* Returns `undefined` when no path parameters are found.
|
|
470
|
-
*
|
|
471
|
-
* @example
|
|
472
|
-
* ```ts
|
|
473
|
-
* new URLPath('/pet/{petId}/tag/{tagId}').getParams()
|
|
474
|
-
* // { petId: 'petId', tagId: 'tagId' }
|
|
475
|
-
* ```
|
|
476
|
-
*/
|
|
477
|
-
getParams(replacer) {
|
|
478
|
-
const params = {};
|
|
479
|
-
this.#eachParam((_raw, param) => {
|
|
480
|
-
const key = replacer ? replacer(param) : param;
|
|
481
|
-
params[key] = key;
|
|
482
|
-
});
|
|
483
|
-
return Object.keys(params).length > 0 ? params : void 0;
|
|
484
|
-
}
|
|
485
|
-
/** Converts the OpenAPI path to Express-style colon syntax.
|
|
486
|
-
*
|
|
487
|
-
* @example
|
|
488
|
-
* ```ts
|
|
489
|
-
* new URLPath('/pet/{petId}').toURLPath() // '/pet/:petId'
|
|
490
|
-
* ```
|
|
491
|
-
*/
|
|
492
|
-
toURLPath() {
|
|
493
|
-
return this.path.replace(/\{([^}]+)\}/g, ":$1");
|
|
494
|
-
}
|
|
495
|
-
};
|
|
496
|
-
//#endregion
|
|
497
|
-
//#region src/createAdapter.ts
|
|
498
162
|
/**
|
|
499
|
-
*
|
|
500
|
-
*
|
|
501
|
-
* Use this to support GraphQL schemas, gRPC definitions, AsyncAPI, or custom domain-specific languages.
|
|
502
|
-
* Built-in adapters include `@kubb/adapter-oas` for OpenAPI and Swagger documents.
|
|
503
|
-
*
|
|
504
|
-
* @note Adapters must parse their input format to Kubb's `InputNode` structure.
|
|
163
|
+
* Executes `fn` only when the system is online. Returns `null` when offline or on error.
|
|
505
164
|
*
|
|
506
165
|
* @example
|
|
507
166
|
* ```ts
|
|
508
|
-
*
|
|
509
|
-
*
|
|
510
|
-
* name: 'my-adapter',
|
|
511
|
-
* options,
|
|
512
|
-
* async parse(source) {
|
|
513
|
-
* // Transform source format to InputNode
|
|
514
|
-
* return { ... }
|
|
515
|
-
* },
|
|
516
|
-
* }
|
|
517
|
-
* })
|
|
518
|
-
*
|
|
519
|
-
* // Instantiate:
|
|
520
|
-
* const adapter = myAdapter({ validate: true })
|
|
167
|
+
* const version = await executeIfOnline(() => fetchLatestVersion('kubb'))
|
|
168
|
+
* // null when offline
|
|
521
169
|
* ```
|
|
522
170
|
*/
|
|
523
|
-
function
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
value;
|
|
530
|
-
next;
|
|
531
|
-
constructor(value) {
|
|
532
|
-
this.value = value;
|
|
533
|
-
}
|
|
534
|
-
};
|
|
535
|
-
var Queue = class {
|
|
536
|
-
#head;
|
|
537
|
-
#tail;
|
|
538
|
-
#size;
|
|
539
|
-
constructor() {
|
|
540
|
-
this.clear();
|
|
541
|
-
}
|
|
542
|
-
enqueue(value) {
|
|
543
|
-
const node = new Node(value);
|
|
544
|
-
if (this.#head) {
|
|
545
|
-
this.#tail.next = node;
|
|
546
|
-
this.#tail = node;
|
|
547
|
-
} else {
|
|
548
|
-
this.#head = node;
|
|
549
|
-
this.#tail = node;
|
|
550
|
-
}
|
|
551
|
-
this.#size++;
|
|
552
|
-
}
|
|
553
|
-
dequeue() {
|
|
554
|
-
const current = this.#head;
|
|
555
|
-
if (!current) return;
|
|
556
|
-
this.#head = this.#head.next;
|
|
557
|
-
this.#size--;
|
|
558
|
-
if (!this.#head) this.#tail = void 0;
|
|
559
|
-
return current.value;
|
|
560
|
-
}
|
|
561
|
-
peek() {
|
|
562
|
-
if (!this.#head) return;
|
|
563
|
-
return this.#head.value;
|
|
564
|
-
}
|
|
565
|
-
clear() {
|
|
566
|
-
this.#head = void 0;
|
|
567
|
-
this.#tail = void 0;
|
|
568
|
-
this.#size = 0;
|
|
569
|
-
}
|
|
570
|
-
get size() {
|
|
571
|
-
return this.#size;
|
|
572
|
-
}
|
|
573
|
-
*[Symbol.iterator]() {
|
|
574
|
-
let current = this.#head;
|
|
575
|
-
while (current) {
|
|
576
|
-
yield current.value;
|
|
577
|
-
current = current.next;
|
|
578
|
-
}
|
|
579
|
-
}
|
|
580
|
-
*drain() {
|
|
581
|
-
while (this.#head) yield this.dequeue();
|
|
171
|
+
async function executeIfOnline(fn) {
|
|
172
|
+
if (!await isOnline()) return null;
|
|
173
|
+
try {
|
|
174
|
+
return await fn();
|
|
175
|
+
} catch {
|
|
176
|
+
return null;
|
|
582
177
|
}
|
|
583
|
-
};
|
|
584
|
-
//#endregion
|
|
585
|
-
//#region ../../node_modules/.pnpm/p-limit@7.3.0/node_modules/p-limit/index.js
|
|
586
|
-
function pLimit(concurrency) {
|
|
587
|
-
let rejectOnClear = false;
|
|
588
|
-
if (typeof concurrency === "object") ({concurrency, rejectOnClear = false} = concurrency);
|
|
589
|
-
validateConcurrency(concurrency);
|
|
590
|
-
if (typeof rejectOnClear !== "boolean") throw new TypeError("Expected `rejectOnClear` to be a boolean");
|
|
591
|
-
const queue = new Queue();
|
|
592
|
-
let activeCount = 0;
|
|
593
|
-
const resumeNext = () => {
|
|
594
|
-
if (activeCount < concurrency && queue.size > 0) {
|
|
595
|
-
activeCount++;
|
|
596
|
-
queue.dequeue().run();
|
|
597
|
-
}
|
|
598
|
-
};
|
|
599
|
-
const next = () => {
|
|
600
|
-
activeCount--;
|
|
601
|
-
resumeNext();
|
|
602
|
-
};
|
|
603
|
-
const run = async (function_, resolve, arguments_) => {
|
|
604
|
-
const result = (async () => function_(...arguments_))();
|
|
605
|
-
resolve(result);
|
|
606
|
-
try {
|
|
607
|
-
await result;
|
|
608
|
-
} catch {}
|
|
609
|
-
next();
|
|
610
|
-
};
|
|
611
|
-
const enqueue = (function_, resolve, reject, arguments_) => {
|
|
612
|
-
const queueItem = { reject };
|
|
613
|
-
new Promise((internalResolve) => {
|
|
614
|
-
queueItem.run = internalResolve;
|
|
615
|
-
queue.enqueue(queueItem);
|
|
616
|
-
}).then(run.bind(void 0, function_, resolve, arguments_));
|
|
617
|
-
if (activeCount < concurrency) resumeNext();
|
|
618
|
-
};
|
|
619
|
-
const generator = (function_, ...arguments_) => new Promise((resolve, reject) => {
|
|
620
|
-
enqueue(function_, resolve, reject, arguments_);
|
|
621
|
-
});
|
|
622
|
-
Object.defineProperties(generator, {
|
|
623
|
-
activeCount: { get: () => activeCount },
|
|
624
|
-
pendingCount: { get: () => queue.size },
|
|
625
|
-
clearQueue: { value() {
|
|
626
|
-
if (!rejectOnClear) {
|
|
627
|
-
queue.clear();
|
|
628
|
-
return;
|
|
629
|
-
}
|
|
630
|
-
const abortError = AbortSignal.abort().reason;
|
|
631
|
-
while (queue.size > 0) queue.dequeue().reject(abortError);
|
|
632
|
-
} },
|
|
633
|
-
concurrency: {
|
|
634
|
-
get: () => concurrency,
|
|
635
|
-
set(newConcurrency) {
|
|
636
|
-
validateConcurrency(newConcurrency);
|
|
637
|
-
concurrency = newConcurrency;
|
|
638
|
-
queueMicrotask(() => {
|
|
639
|
-
while (activeCount < concurrency && queue.size > 0) resumeNext();
|
|
640
|
-
});
|
|
641
|
-
}
|
|
642
|
-
},
|
|
643
|
-
map: { async value(iterable, function_) {
|
|
644
|
-
const promises = Array.from(iterable, (value, index) => this(function_, value, index));
|
|
645
|
-
return Promise.all(promises);
|
|
646
|
-
} }
|
|
647
|
-
});
|
|
648
|
-
return generator;
|
|
649
|
-
}
|
|
650
|
-
function validateConcurrency(concurrency) {
|
|
651
|
-
if (!((Number.isInteger(concurrency) || concurrency === Number.POSITIVE_INFINITY) && concurrency > 0)) throw new TypeError("Expected `concurrency` to be a number from 1 and up");
|
|
652
|
-
}
|
|
653
|
-
//#endregion
|
|
654
|
-
//#region src/FileProcessor.ts
|
|
655
|
-
function joinSources(file) {
|
|
656
|
-
return file.sources.map((item) => (0, _kubb_ast.extractStringsFromNodes)(item.nodes)).filter(Boolean).join("\n\n");
|
|
657
178
|
}
|
|
658
|
-
/**
|
|
659
|
-
* Converts a single file to a string using the registered parsers.
|
|
660
|
-
* Falls back to joining source values when no matching parser is found.
|
|
661
|
-
*
|
|
662
|
-
* @internal
|
|
663
|
-
*/
|
|
664
|
-
var FileProcessor = class {
|
|
665
|
-
#limit = pLimit(100);
|
|
666
|
-
async parse(file, { parsers, extension } = {}) {
|
|
667
|
-
const parseExtName = extension?.[file.extname] || void 0;
|
|
668
|
-
if (!parsers || !file.extname) return joinSources(file);
|
|
669
|
-
const parser = parsers.get(file.extname);
|
|
670
|
-
if (!parser) return joinSources(file);
|
|
671
|
-
return parser.parse(file, { extname: parseExtName });
|
|
672
|
-
}
|
|
673
|
-
async run(files, { parsers, mode = "sequential", extension, onStart, onEnd, onUpdate } = {}) {
|
|
674
|
-
await onStart?.(files);
|
|
675
|
-
const total = files.length;
|
|
676
|
-
let processed = 0;
|
|
677
|
-
const processOne = async (file) => {
|
|
678
|
-
const source = await this.parse(file, {
|
|
679
|
-
extension,
|
|
680
|
-
parsers
|
|
681
|
-
});
|
|
682
|
-
const currentProcessed = ++processed;
|
|
683
|
-
const percentage = currentProcessed / total * 100;
|
|
684
|
-
await onUpdate?.({
|
|
685
|
-
file,
|
|
686
|
-
source,
|
|
687
|
-
processed: currentProcessed,
|
|
688
|
-
percentage,
|
|
689
|
-
total
|
|
690
|
-
});
|
|
691
|
-
};
|
|
692
|
-
if (mode === "sequential") for (const file of files) await processOne(file);
|
|
693
|
-
else await Promise.all(files.map((file) => this.#limit(() => processOne(file))));
|
|
694
|
-
await onEnd?.(files);
|
|
695
|
-
return files;
|
|
696
|
-
}
|
|
697
|
-
};
|
|
698
179
|
//#endregion
|
|
699
|
-
//#region src/
|
|
180
|
+
//#region src/createAdapter.ts
|
|
700
181
|
/**
|
|
701
|
-
*
|
|
702
|
-
*
|
|
703
|
-
*
|
|
704
|
-
*
|
|
182
|
+
* Defines a custom adapter that translates a spec format into Kubb's universal
|
|
183
|
+
* AST. Use this when you need to consume GraphQL, gRPC, AsyncAPI, or another
|
|
184
|
+
* domain-specific schema. Built-in adapters: `@kubb/adapter-oas` for
|
|
185
|
+
* OpenAPI/Swagger documents.
|
|
705
186
|
*
|
|
706
|
-
*
|
|
187
|
+
* Adapters must return an `InputNode` from `parse`. That node is what every
|
|
188
|
+
* plugin in the build consumes.
|
|
707
189
|
*
|
|
708
190
|
* @example
|
|
709
191
|
* ```ts
|
|
710
|
-
* import {
|
|
192
|
+
* import { createAdapter, ast, type AdapterFactoryOptions } from '@kubb/core'
|
|
711
193
|
*
|
|
712
|
-
*
|
|
713
|
-
* const store = new Map<string, string>()
|
|
714
|
-
* return {
|
|
715
|
-
* name: 'memory',
|
|
716
|
-
* async hasItem(key) { return store.has(key) },
|
|
717
|
-
* async getItem(key) { return store.get(key) ?? null },
|
|
718
|
-
* async setItem(key, value) { store.set(key, value) },
|
|
719
|
-
* async removeItem(key) { store.delete(key) },
|
|
720
|
-
* async getKeys(base) {
|
|
721
|
-
* const keys = [...store.keys()]
|
|
722
|
-
* return base ? keys.filter((k) => k.startsWith(base)) : keys
|
|
723
|
-
* },
|
|
724
|
-
* async clear(base) { if (!base) store.clear() },
|
|
725
|
-
* }
|
|
726
|
-
* })
|
|
194
|
+
* type MyAdapter = AdapterFactoryOptions<'my-adapter', { validate?: boolean }>
|
|
727
195
|
*
|
|
728
|
-
*
|
|
729
|
-
*
|
|
196
|
+
* export const myAdapter = createAdapter<MyAdapter>((options) => ({
|
|
197
|
+
* name: 'my-adapter',
|
|
198
|
+
* options,
|
|
199
|
+
* document: null,
|
|
200
|
+
* async parse(_source) {
|
|
201
|
+
* // Convert `source` (path or inline data) into an InputNode.
|
|
202
|
+
* return ast.createInput()
|
|
203
|
+
* },
|
|
204
|
+
* getImports: () => [],
|
|
205
|
+
* async validate() {
|
|
206
|
+
* // Throw or call ctx.error here when the spec is invalid.
|
|
207
|
+
* },
|
|
208
|
+
* }))
|
|
730
209
|
* ```
|
|
731
210
|
*/
|
|
732
|
-
function
|
|
211
|
+
function createAdapter(build) {
|
|
733
212
|
return (options) => build(options ?? {});
|
|
734
213
|
}
|
|
735
214
|
//#endregion
|
|
736
215
|
//#region src/storages/fsStorage.ts
|
|
737
216
|
/**
|
|
738
|
-
* Detects the filesystem error used to indicate that a path does not exist.
|
|
739
|
-
*/
|
|
740
|
-
function isMissingPathError(error) {
|
|
741
|
-
return typeof error === "object" && error !== null && "code" in error && error.code === "ENOENT";
|
|
742
|
-
}
|
|
743
|
-
/**
|
|
744
217
|
* Built-in filesystem storage driver.
|
|
745
218
|
*
|
|
746
219
|
* This is the default storage when no `storage` option is configured in the root config.
|
|
@@ -765,23 +238,21 @@ function isMissingPathError(error) {
|
|
|
765
238
|
* })
|
|
766
239
|
* ```
|
|
767
240
|
*/
|
|
768
|
-
const fsStorage = createStorage(() => ({
|
|
241
|
+
const fsStorage = require_memoryStorage.createStorage(() => ({
|
|
769
242
|
name: "fs",
|
|
770
243
|
async hasItem(key) {
|
|
771
244
|
try {
|
|
772
245
|
await (0, node_fs_promises.access)((0, node_path.resolve)(key));
|
|
773
246
|
return true;
|
|
774
|
-
} catch (
|
|
775
|
-
|
|
776
|
-
throw new Error(`Failed to access storage item "${key}"`, { cause: error });
|
|
247
|
+
} catch (_error) {
|
|
248
|
+
return false;
|
|
777
249
|
}
|
|
778
250
|
},
|
|
779
251
|
async getItem(key) {
|
|
780
252
|
try {
|
|
781
253
|
return await (0, node_fs_promises.readFile)((0, node_path.resolve)(key), "utf8");
|
|
782
|
-
} catch (
|
|
783
|
-
|
|
784
|
-
throw new Error(`Failed to read storage item "${key}"`, { cause: error });
|
|
254
|
+
} catch (_error) {
|
|
255
|
+
return null;
|
|
785
256
|
}
|
|
786
257
|
},
|
|
787
258
|
async setItem(key, value) {
|
|
@@ -791,23 +262,22 @@ const fsStorage = createStorage(() => ({
|
|
|
791
262
|
await (0, node_fs_promises.rm)((0, node_path.resolve)(key), { force: true });
|
|
792
263
|
},
|
|
793
264
|
async getKeys(base) {
|
|
794
|
-
const keys = [];
|
|
795
265
|
const resolvedBase = (0, node_path.resolve)(base ?? process.cwd());
|
|
796
|
-
async function walk(dir, prefix) {
|
|
266
|
+
async function* walk(dir, prefix) {
|
|
797
267
|
let entries;
|
|
798
268
|
try {
|
|
799
269
|
entries = await (0, node_fs_promises.readdir)(dir, { withFileTypes: true });
|
|
800
|
-
} catch (
|
|
801
|
-
|
|
802
|
-
throw new Error(`Failed to list storage keys under "${resolvedBase}"`, { cause: error });
|
|
270
|
+
} catch (_error) {
|
|
271
|
+
return;
|
|
803
272
|
}
|
|
804
273
|
for (const entry of entries) {
|
|
805
274
|
const rel = prefix ? `${prefix}/${entry.name}` : entry.name;
|
|
806
|
-
if (entry.isDirectory())
|
|
807
|
-
else
|
|
275
|
+
if (entry.isDirectory()) yield* walk((0, node_path.join)(dir, entry.name), rel);
|
|
276
|
+
else yield rel;
|
|
808
277
|
}
|
|
809
278
|
}
|
|
810
|
-
|
|
279
|
+
const keys = [];
|
|
280
|
+
for await (const key of walk(resolvedBase, "")) keys.push(key);
|
|
811
281
|
return keys;
|
|
812
282
|
},
|
|
813
283
|
async clear(base) {
|
|
@@ -816,476 +286,652 @@ const fsStorage = createStorage(() => ({
|
|
|
816
286
|
}
|
|
817
287
|
}));
|
|
818
288
|
//#endregion
|
|
819
|
-
//#region
|
|
820
|
-
var version = "5.0.0-beta.4";
|
|
821
|
-
//#endregion
|
|
822
|
-
//#region src/utils/diagnostics.ts
|
|
289
|
+
//#region src/createKubb.ts
|
|
823
290
|
/**
|
|
824
|
-
*
|
|
825
|
-
*
|
|
826
|
-
*
|
|
827
|
-
*
|
|
291
|
+
* Builds a `Storage` view scoped to the file paths produced by the current build.
|
|
292
|
+
* Reads delegate to the underlying `storage` so source bytes stay where they were
|
|
293
|
+
* written. Writes register the key so subsequent reads and `getKeys` are scoped
|
|
294
|
+
* to this build's output.
|
|
828
295
|
*/
|
|
829
|
-
function
|
|
830
|
-
|
|
831
|
-
|
|
832
|
-
|
|
833
|
-
|
|
834
|
-
|
|
835
|
-
|
|
836
|
-
|
|
837
|
-
|
|
838
|
-
|
|
839
|
-
|
|
840
|
-
|
|
841
|
-
|
|
842
|
-
}
|
|
843
|
-
|
|
844
|
-
|
|
845
|
-
|
|
846
|
-
|
|
847
|
-
|
|
848
|
-
|
|
849
|
-
|
|
850
|
-
|
|
851
|
-
|
|
852
|
-
|
|
853
|
-
|
|
854
|
-
|
|
855
|
-
|
|
856
|
-
` • Output: ${userConfig.output?.path || "not specified"}`,
|
|
857
|
-
` • Plugins: ${userConfig.plugins?.length || 0}`,
|
|
858
|
-
"Output Settings:",
|
|
859
|
-
` • Storage: ${userConfig.storage ? `custom(${userConfig.storage.name})` : userConfig.output?.write === false ? "disabled" : "filesystem (default)"}`,
|
|
860
|
-
` • Formatter: ${userConfig.output?.format || "none"}`,
|
|
861
|
-
` • Linter: ${userConfig.output?.lint || "none"}`,
|
|
862
|
-
"Environment:",
|
|
863
|
-
Object.entries(diagnosticInfo).map(([key, value]) => ` • ${key}: ${value}`).join("\n")
|
|
864
|
-
]
|
|
865
|
-
});
|
|
866
|
-
try {
|
|
867
|
-
if (isInputPath(userConfig) && !new URLPath(userConfig.input.path).isURL) {
|
|
868
|
-
await exists(userConfig.input.path);
|
|
869
|
-
await hooks.emit("kubb:debug", {
|
|
870
|
-
date: /* @__PURE__ */ new Date(),
|
|
871
|
-
logs: [`✓ Input file validated: ${userConfig.input.path}`]
|
|
872
|
-
});
|
|
873
|
-
}
|
|
874
|
-
} catch (caughtError) {
|
|
875
|
-
if (isInputPath(userConfig)) {
|
|
876
|
-
const error = caughtError;
|
|
877
|
-
throw new Error(`Cannot read file/URL defined in \`input.path\` or set with \`kubb generate PATH\` in the CLI of your Kubb config ${userConfig.input.path}`, { cause: error });
|
|
296
|
+
function createSourcesView(storage) {
|
|
297
|
+
const paths = /* @__PURE__ */ new Set();
|
|
298
|
+
return require_memoryStorage.createStorage(() => ({
|
|
299
|
+
name: `${storage.name}:sources`,
|
|
300
|
+
async hasItem(key) {
|
|
301
|
+
return paths.has(key) && await storage.hasItem(key);
|
|
302
|
+
},
|
|
303
|
+
async getItem(key) {
|
|
304
|
+
return paths.has(key) ? storage.getItem(key) : null;
|
|
305
|
+
},
|
|
306
|
+
async setItem(key, value) {
|
|
307
|
+
paths.add(key);
|
|
308
|
+
await storage.setItem(key, value);
|
|
309
|
+
},
|
|
310
|
+
async removeItem(key) {
|
|
311
|
+
paths.delete(key);
|
|
312
|
+
await storage.removeItem(key);
|
|
313
|
+
},
|
|
314
|
+
async getKeys(base) {
|
|
315
|
+
if (!base) return [...paths];
|
|
316
|
+
const result = [];
|
|
317
|
+
for (const key of paths) if (key.startsWith(base)) result.push(key);
|
|
318
|
+
return result;
|
|
319
|
+
},
|
|
320
|
+
async clear() {
|
|
321
|
+
paths.clear();
|
|
322
|
+
await storage.clear();
|
|
878
323
|
}
|
|
879
|
-
}
|
|
880
|
-
|
|
324
|
+
}))();
|
|
325
|
+
}
|
|
326
|
+
function resolveConfig(userConfig) {
|
|
327
|
+
return {
|
|
881
328
|
...userConfig,
|
|
882
329
|
root: userConfig.root || process.cwd(),
|
|
883
330
|
parsers: userConfig.parsers ?? [],
|
|
884
|
-
adapter: userConfig.adapter,
|
|
885
331
|
output: {
|
|
886
332
|
format: false,
|
|
887
333
|
lint: false,
|
|
888
|
-
|
|
889
|
-
|
|
890
|
-
defaultBanner: require_PluginDriver.DEFAULT_BANNER,
|
|
334
|
+
extension: { ".ts": ".ts" },
|
|
335
|
+
defaultBanner: "simple",
|
|
891
336
|
...userConfig.output
|
|
892
337
|
},
|
|
893
|
-
|
|
894
|
-
|
|
895
|
-
|
|
896
|
-
} : void 0,
|
|
897
|
-
plugins: userConfig.plugins
|
|
898
|
-
};
|
|
899
|
-
const storage = config.output.write === false ? null : config.storage ?? fsStorage();
|
|
900
|
-
if (config.output.clean) {
|
|
901
|
-
await hooks.emit("kubb:debug", {
|
|
902
|
-
date: /* @__PURE__ */ new Date(),
|
|
903
|
-
logs: ["Cleaning output directories", ` • Output: ${config.output.path}`]
|
|
904
|
-
});
|
|
905
|
-
await storage?.clear((0, node_path.resolve)(config.root, config.output.path));
|
|
906
|
-
}
|
|
907
|
-
const driver = new require_PluginDriver.PluginDriver(config, { hooks });
|
|
908
|
-
function registerMiddlewareHook(event, middlewareHooks) {
|
|
909
|
-
const handler = middlewareHooks[event];
|
|
910
|
-
if (handler) hooks.on(event, handler);
|
|
911
|
-
}
|
|
912
|
-
for (const middleware of config.middleware ?? []) for (const event of Object.keys(middleware.hooks)) registerMiddlewareHook(event, middleware.hooks);
|
|
913
|
-
if (config.adapter) {
|
|
914
|
-
const source = inputToAdapterSource(config);
|
|
915
|
-
await hooks.emit("kubb:debug", {
|
|
916
|
-
date: /* @__PURE__ */ new Date(),
|
|
917
|
-
logs: [`Running adapter: ${config.adapter.name}`]
|
|
918
|
-
});
|
|
919
|
-
driver.adapter = config.adapter;
|
|
920
|
-
driver.inputNode = await config.adapter.parse(source);
|
|
921
|
-
await hooks.emit("kubb:debug", {
|
|
922
|
-
date: /* @__PURE__ */ new Date(),
|
|
923
|
-
logs: [
|
|
924
|
-
`✓ Adapter '${config.adapter.name}' resolved InputNode`,
|
|
925
|
-
` • Schemas: ${driver.inputNode.schemas.length}`,
|
|
926
|
-
` • Operations: ${driver.inputNode.operations.length}`
|
|
927
|
-
]
|
|
928
|
-
});
|
|
929
|
-
}
|
|
930
|
-
return {
|
|
931
|
-
config,
|
|
932
|
-
hooks,
|
|
933
|
-
driver,
|
|
934
|
-
sources,
|
|
935
|
-
storage
|
|
338
|
+
storage: userConfig.storage ?? fsStorage(),
|
|
339
|
+
reporters: userConfig.reporters ?? [],
|
|
340
|
+
plugins: userConfig.plugins ?? []
|
|
936
341
|
};
|
|
937
342
|
}
|
|
938
343
|
/**
|
|
939
|
-
*
|
|
940
|
-
* (`
|
|
344
|
+
* Kubb code-generation instance bound to a single config entry. Resolves the user
|
|
345
|
+
* config during `setup()` and shares `hooks`, `storage`, `driver`, and `config` across
|
|
346
|
+
* the `setup → build` lifecycle.
|
|
347
|
+
*
|
|
348
|
+
* Attach event listeners to `.hooks` before calling `setup()` or `build()`.
|
|
941
349
|
*
|
|
942
|
-
*
|
|
943
|
-
*
|
|
944
|
-
*
|
|
945
|
-
*
|
|
946
|
-
*
|
|
350
|
+
* @example
|
|
351
|
+
* ```ts
|
|
352
|
+
* const kubb = createKubb(userConfig)
|
|
353
|
+
* kubb.hooks.on('kubb:plugin:end', ({ plugin, duration }) => console.log(plugin.name, duration))
|
|
354
|
+
* const { files, diagnostics } = await kubb.safeBuild()
|
|
355
|
+
* ```
|
|
947
356
|
*/
|
|
948
|
-
|
|
949
|
-
|
|
950
|
-
|
|
951
|
-
|
|
952
|
-
|
|
953
|
-
|
|
357
|
+
var Kubb = class {
|
|
358
|
+
hooks;
|
|
359
|
+
#userConfig;
|
|
360
|
+
#config = null;
|
|
361
|
+
#driver = null;
|
|
362
|
+
#storage = null;
|
|
363
|
+
constructor(userConfig, options = {}) {
|
|
364
|
+
this.#userConfig = userConfig;
|
|
365
|
+
this.hooks = options.hooks ?? new require_memoryStorage.AsyncEventEmitter();
|
|
954
366
|
}
|
|
955
|
-
|
|
956
|
-
|
|
957
|
-
|
|
958
|
-
|
|
959
|
-
|
|
960
|
-
|
|
961
|
-
|
|
962
|
-
|
|
963
|
-
|
|
964
|
-
"
|
|
965
|
-
|
|
966
|
-
|
|
967
|
-
|
|
968
|
-
|
|
969
|
-
|
|
970
|
-
|
|
971
|
-
|
|
972
|
-
|
|
973
|
-
|
|
974
|
-
|
|
975
|
-
|
|
976
|
-
|
|
977
|
-
|
|
978
|
-
|
|
979
|
-
|
|
980
|
-
|
|
981
|
-
|
|
982
|
-
|
|
983
|
-
|
|
984
|
-
|
|
985
|
-
|
|
986
|
-
|
|
987
|
-
|
|
988
|
-
|
|
989
|
-
|
|
990
|
-
...generatorContext,
|
|
991
|
-
options
|
|
992
|
-
};
|
|
993
|
-
for (const gen of generators) {
|
|
994
|
-
if (!gen.schema) continue;
|
|
995
|
-
await require_PluginDriver.applyHookResult(await gen.schema(transformedNode, ctx), driver, resolveRenderer(gen));
|
|
996
|
-
}
|
|
997
|
-
await driver.hooks.emit("kubb:generate:schema", transformedNode, ctx);
|
|
998
|
-
},
|
|
999
|
-
async operation(node) {
|
|
1000
|
-
const transformedNode = plugin.transformer ? (0, _kubb_ast.transform)(node, plugin.transformer) : node;
|
|
1001
|
-
const options = resolver.resolveOptions(transformedNode, {
|
|
1002
|
-
options: plugin.options,
|
|
1003
|
-
exclude,
|
|
1004
|
-
include,
|
|
1005
|
-
override
|
|
1006
|
-
});
|
|
1007
|
-
if (options !== null) {
|
|
1008
|
-
collectedOperations.push(transformedNode);
|
|
1009
|
-
const ctx = {
|
|
1010
|
-
...generatorContext,
|
|
1011
|
-
options
|
|
1012
|
-
};
|
|
1013
|
-
for (const gen of generators) {
|
|
1014
|
-
if (!gen.operation) continue;
|
|
1015
|
-
await require_PluginDriver.applyHookResult(await gen.operation(transformedNode, ctx), driver, resolveRenderer(gen));
|
|
1016
|
-
}
|
|
1017
|
-
await driver.hooks.emit("kubb:generate:operation", transformedNode, ctx);
|
|
1018
|
-
}
|
|
1019
|
-
}
|
|
1020
|
-
});
|
|
1021
|
-
if (collectedOperations.length > 0) {
|
|
1022
|
-
const ctx = {
|
|
1023
|
-
...generatorContext,
|
|
1024
|
-
options: plugin.options
|
|
1025
|
-
};
|
|
1026
|
-
for (const gen of generators) {
|
|
1027
|
-
if (!gen.operations) continue;
|
|
1028
|
-
await require_PluginDriver.applyHookResult(await gen.operations(collectedOperations, ctx), driver, resolveRenderer(gen));
|
|
367
|
+
get storage() {
|
|
368
|
+
if (!this.#storage) throw new Error("[kubb] setup() must be called before accessing storage");
|
|
369
|
+
return this.#storage;
|
|
370
|
+
}
|
|
371
|
+
get driver() {
|
|
372
|
+
if (!this.#driver) throw new Error("[kubb] setup() must be called before accessing driver");
|
|
373
|
+
return this.#driver;
|
|
374
|
+
}
|
|
375
|
+
get config() {
|
|
376
|
+
if (!this.#config) throw new Error("[kubb] setup() must be called before accessing config");
|
|
377
|
+
return this.#config;
|
|
378
|
+
}
|
|
379
|
+
/**
|
|
380
|
+
* Resolves config and initializes the driver. `build()` calls this automatically.
|
|
381
|
+
*/
|
|
382
|
+
async setup() {
|
|
383
|
+
const config = resolveConfig(this.#userConfig);
|
|
384
|
+
const driver = new require_memoryStorage.KubbDriver(config, { hooks: this.hooks });
|
|
385
|
+
const storage = createSourcesView(config.storage);
|
|
386
|
+
this.hooks.setMaxListeners(Math.max(10, config.plugins.length * 4));
|
|
387
|
+
if (config.output.clean) await config.storage.clear((0, node_path.resolve)(config.root, config.output.path));
|
|
388
|
+
await driver.setup();
|
|
389
|
+
this.#config = config;
|
|
390
|
+
this.#driver = driver;
|
|
391
|
+
this.#storage = storage;
|
|
392
|
+
}
|
|
393
|
+
/**
|
|
394
|
+
* Runs the full pipeline and throws on any plugin error.
|
|
395
|
+
* Automatically calls `setup()` if needed.
|
|
396
|
+
*/
|
|
397
|
+
async build() {
|
|
398
|
+
const out = await this.safeBuild();
|
|
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 });
|
|
1029
402
|
}
|
|
1030
|
-
|
|
403
|
+
return out;
|
|
1031
404
|
}
|
|
1032
|
-
|
|
1033
|
-
|
|
1034
|
-
|
|
1035
|
-
|
|
1036
|
-
|
|
1037
|
-
|
|
1038
|
-
|
|
1039
|
-
|
|
1040
|
-
|
|
1041
|
-
|
|
1042
|
-
|
|
1043
|
-
|
|
1044
|
-
|
|
1045
|
-
|
|
1046
|
-
|
|
1047
|
-
|
|
1048
|
-
|
|
1049
|
-
|
|
1050
|
-
|
|
1051
|
-
|
|
1052
|
-
|
|
1053
|
-
|
|
1054
|
-
const timestamp = /* @__PURE__ */ new Date();
|
|
1055
|
-
await hooks.emit("kubb:plugin:start", { plugin });
|
|
1056
|
-
await hooks.emit("kubb:debug", {
|
|
1057
|
-
date: timestamp,
|
|
1058
|
-
logs: ["Starting plugin...", ` • Plugin Name: ${plugin.name}`]
|
|
1059
|
-
});
|
|
1060
|
-
if (plugin.generators?.length || driver.hasRegisteredGenerators(plugin.name)) await runPluginAstHooks(plugin, context);
|
|
1061
|
-
const duration = getElapsedMs(hrStart);
|
|
1062
|
-
pluginTimings.set(plugin.name, duration);
|
|
1063
|
-
await hooks.emit("kubb:plugin:end", {
|
|
1064
|
-
plugin,
|
|
1065
|
-
duration,
|
|
1066
|
-
success: true,
|
|
1067
|
-
config,
|
|
1068
|
-
get files() {
|
|
1069
|
-
return driver.fileManager.files;
|
|
1070
|
-
},
|
|
1071
|
-
upsertFile: (...files) => driver.fileManager.upsert(...files)
|
|
1072
|
-
});
|
|
1073
|
-
await hooks.emit("kubb:debug", {
|
|
1074
|
-
date: /* @__PURE__ */ new Date(),
|
|
1075
|
-
logs: [`✓ Plugin started successfully (${formatMs(duration)})`]
|
|
1076
|
-
});
|
|
1077
|
-
} catch (caughtError) {
|
|
1078
|
-
const error = caughtError;
|
|
1079
|
-
const errorTimestamp = /* @__PURE__ */ new Date();
|
|
1080
|
-
const duration = getElapsedMs(hrStart);
|
|
1081
|
-
await hooks.emit("kubb:plugin:end", {
|
|
1082
|
-
plugin,
|
|
1083
|
-
duration,
|
|
1084
|
-
success: false,
|
|
1085
|
-
error,
|
|
1086
|
-
config,
|
|
1087
|
-
get files() {
|
|
1088
|
-
return driver.fileManager.files;
|
|
1089
|
-
},
|
|
1090
|
-
upsertFile: (...files) => driver.fileManager.upsert(...files)
|
|
1091
|
-
});
|
|
1092
|
-
await hooks.emit("kubb:debug", {
|
|
1093
|
-
date: errorTimestamp,
|
|
1094
|
-
logs: [
|
|
1095
|
-
"✗ Plugin start failed",
|
|
1096
|
-
` • Plugin Name: ${plugin.name}`,
|
|
1097
|
-
` • Error: ${error.constructor.name} - ${error.message}`,
|
|
1098
|
-
" • Stack Trace:",
|
|
1099
|
-
error.stack || "No stack trace available"
|
|
1100
|
-
]
|
|
1101
|
-
});
|
|
1102
|
-
failedPlugins.add({
|
|
1103
|
-
plugin,
|
|
1104
|
-
error
|
|
1105
|
-
});
|
|
1106
|
-
}
|
|
405
|
+
/**
|
|
406
|
+
* Runs the full pipeline and captures errors in `BuildOutput` instead of throwing.
|
|
407
|
+
* Automatically calls `setup()` if needed.
|
|
408
|
+
*/
|
|
409
|
+
async safeBuild() {
|
|
410
|
+
try {
|
|
411
|
+
var _usingCtx$1 = require_memoryStorage._usingCtx();
|
|
412
|
+
if (!this.#driver) await this.setup();
|
|
413
|
+
const cleanup = _usingCtx$1.u(this);
|
|
414
|
+
const driver = cleanup.driver;
|
|
415
|
+
const storage = cleanup.storage;
|
|
416
|
+
const { diagnostics } = await driver.run({ storage });
|
|
417
|
+
return {
|
|
418
|
+
diagnostics,
|
|
419
|
+
files: driver.fileManager.files,
|
|
420
|
+
driver,
|
|
421
|
+
storage
|
|
422
|
+
};
|
|
423
|
+
} catch (_) {
|
|
424
|
+
_usingCtx$1.e = _;
|
|
425
|
+
} finally {
|
|
426
|
+
_usingCtx$1.d();
|
|
1107
427
|
}
|
|
1108
|
-
await hooks.emit("kubb:plugins:end", {
|
|
1109
|
-
config,
|
|
1110
|
-
get files() {
|
|
1111
|
-
return driver.fileManager.files;
|
|
1112
|
-
},
|
|
1113
|
-
upsertFile: (...files) => driver.fileManager.upsert(...files)
|
|
1114
|
-
});
|
|
1115
|
-
const files = driver.fileManager.files;
|
|
1116
|
-
const parsersMap = /* @__PURE__ */ new Map();
|
|
1117
|
-
for (const parser of config.parsers) if (parser.extNames) for (const extname of parser.extNames) parsersMap.set(extname, parser);
|
|
1118
|
-
const fileProcessor = new FileProcessor();
|
|
1119
|
-
await hooks.emit("kubb:debug", {
|
|
1120
|
-
date: /* @__PURE__ */ new Date(),
|
|
1121
|
-
logs: [`Writing ${files.length} files...`]
|
|
1122
|
-
});
|
|
1123
|
-
await fileProcessor.run(files, {
|
|
1124
|
-
parsers: parsersMap,
|
|
1125
|
-
extension: config.output.extension,
|
|
1126
|
-
onStart: async (processingFiles) => {
|
|
1127
|
-
await hooks.emit("kubb:files:processing:start", { files: processingFiles });
|
|
1128
|
-
},
|
|
1129
|
-
onUpdate: async ({ file, source, processed, total, percentage }) => {
|
|
1130
|
-
await hooks.emit("kubb:file:processing:update", {
|
|
1131
|
-
file,
|
|
1132
|
-
source,
|
|
1133
|
-
processed,
|
|
1134
|
-
total,
|
|
1135
|
-
percentage,
|
|
1136
|
-
config
|
|
1137
|
-
});
|
|
1138
|
-
if (source) {
|
|
1139
|
-
await storage?.setItem(file.path, source);
|
|
1140
|
-
sources.set(file.path, source);
|
|
1141
|
-
}
|
|
1142
|
-
},
|
|
1143
|
-
onEnd: async (processedFiles) => {
|
|
1144
|
-
await hooks.emit("kubb:files:processing:end", { files: processedFiles });
|
|
1145
|
-
await hooks.emit("kubb:debug", {
|
|
1146
|
-
date: /* @__PURE__ */ new Date(),
|
|
1147
|
-
logs: [`✓ File write process completed for ${processedFiles.length} files`]
|
|
1148
|
-
});
|
|
1149
|
-
}
|
|
1150
|
-
});
|
|
1151
|
-
await hooks.emit("kubb:build:end", {
|
|
1152
|
-
files,
|
|
1153
|
-
config,
|
|
1154
|
-
outputDir: (0, node_path.resolve)(config.root, config.output.path)
|
|
1155
|
-
});
|
|
1156
|
-
return {
|
|
1157
|
-
failedPlugins,
|
|
1158
|
-
files,
|
|
1159
|
-
driver,
|
|
1160
|
-
pluginTimings,
|
|
1161
|
-
sources
|
|
1162
|
-
};
|
|
1163
|
-
} catch (error) {
|
|
1164
|
-
return {
|
|
1165
|
-
failedPlugins,
|
|
1166
|
-
files: [],
|
|
1167
|
-
driver,
|
|
1168
|
-
pluginTimings,
|
|
1169
|
-
error,
|
|
1170
|
-
sources
|
|
1171
|
-
};
|
|
1172
|
-
} finally {
|
|
1173
|
-
driver.dispose();
|
|
1174
428
|
}
|
|
1175
|
-
|
|
1176
|
-
|
|
1177
|
-
const { files, driver, failedPlugins, pluginTimings, error, sources } = await safeBuild(setupResult);
|
|
1178
|
-
if (error) throw error;
|
|
1179
|
-
if (failedPlugins.size > 0) {
|
|
1180
|
-
const errors = [...failedPlugins].map(({ error }) => error);
|
|
1181
|
-
throw new BuildError(`Build Error with ${failedPlugins.size} failed plugins`, { errors });
|
|
429
|
+
dispose() {
|
|
430
|
+
this.#driver?.dispose();
|
|
1182
431
|
}
|
|
1183
|
-
|
|
1184
|
-
|
|
1185
|
-
|
|
1186
|
-
|
|
1187
|
-
|
|
1188
|
-
|
|
1189
|
-
|
|
1190
|
-
|
|
432
|
+
[Symbol.dispose]() {
|
|
433
|
+
this.dispose();
|
|
434
|
+
}
|
|
435
|
+
};
|
|
436
|
+
/**
|
|
437
|
+
* Constructs a {@link Kubb} build orchestrator from a user config. Equivalent
|
|
438
|
+
* to `new Kubb(userConfig, options)` and the canonical public entry point.
|
|
439
|
+
*
|
|
440
|
+
* @example
|
|
441
|
+
* ```ts
|
|
442
|
+
* import { createKubb } from '@kubb/core'
|
|
443
|
+
* import { adapterOas } from '@kubb/adapter-oas'
|
|
444
|
+
* import { pluginTs } from '@kubb/plugin-ts'
|
|
445
|
+
*
|
|
446
|
+
* const kubb = createKubb({
|
|
447
|
+
* input: { path: './petStore.yaml' },
|
|
448
|
+
* output: { path: './src/gen' },
|
|
449
|
+
* adapter: adapterOas(),
|
|
450
|
+
* plugins: [pluginTs()],
|
|
451
|
+
* })
|
|
452
|
+
*
|
|
453
|
+
* await kubb.build()
|
|
454
|
+
* ```
|
|
455
|
+
*/
|
|
456
|
+
function createKubb(userConfig, options = {}) {
|
|
457
|
+
return new Kubb(userConfig, options);
|
|
1191
458
|
}
|
|
1192
|
-
|
|
1193
|
-
|
|
1194
|
-
|
|
1195
|
-
|
|
1196
|
-
|
|
1197
|
-
|
|
1198
|
-
|
|
1199
|
-
|
|
1200
|
-
|
|
1201
|
-
|
|
1202
|
-
|
|
1203
|
-
|
|
1204
|
-
|
|
1205
|
-
|
|
459
|
+
//#endregion
|
|
460
|
+
//#region src/createReporter.ts
|
|
461
|
+
/**
|
|
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
|
|
464
|
+
* is buffered. Wiring the reporter onto the run's events is the host's job, so the reporter only
|
|
465
|
+
* ever deals with a {@link GenerationResult}.
|
|
466
|
+
*
|
|
467
|
+
* @example
|
|
468
|
+
* ```ts
|
|
469
|
+
* import { createReporter, Diagnostics } from '@kubb/core'
|
|
470
|
+
*
|
|
471
|
+
* export const jsonReporter = createReporter({
|
|
472
|
+
* name: 'json',
|
|
473
|
+
* report(result) {
|
|
474
|
+
* return { status: Diagnostics.hasError(result.diagnostics) ? 'failed' : 'success', diagnostics: result.diagnostics }
|
|
475
|
+
* },
|
|
476
|
+
* drain(context, reports) {
|
|
477
|
+
* process.stdout.write(`${JSON.stringify(reports, null, 2)}\n`)
|
|
478
|
+
* },
|
|
479
|
+
* })
|
|
480
|
+
* ```
|
|
481
|
+
*/
|
|
482
|
+
function createReporter(reporter) {
|
|
483
|
+
const drain = reporter.drain;
|
|
484
|
+
if (!drain) return {
|
|
485
|
+
name: reporter.name,
|
|
486
|
+
async report(result, context) {
|
|
487
|
+
await reporter.report(result, context);
|
|
488
|
+
}
|
|
1206
489
|
};
|
|
490
|
+
const reports = [];
|
|
1207
491
|
return {
|
|
1208
|
-
|
|
1209
|
-
|
|
492
|
+
name: reporter.name,
|
|
493
|
+
async report(result, context) {
|
|
494
|
+
reports.push(await reporter.report(result, context));
|
|
495
|
+
},
|
|
496
|
+
async drain(context) {
|
|
497
|
+
await drain(context, reports);
|
|
498
|
+
reports.length = 0;
|
|
499
|
+
}
|
|
1210
500
|
};
|
|
1211
501
|
}
|
|
1212
502
|
/**
|
|
1213
|
-
*
|
|
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.
|
|
1214
523
|
*
|
|
1215
|
-
*
|
|
1216
|
-
|
|
1217
|
-
|
|
1218
|
-
|
|
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.
|
|
1219
537
|
*
|
|
1220
|
-
* @example
|
|
538
|
+
* @example Basic logger
|
|
1221
539
|
* ```ts
|
|
1222
|
-
*
|
|
540
|
+
* import { defineLogger } from '@kubb/core'
|
|
1223
541
|
*
|
|
1224
|
-
*
|
|
1225
|
-
*
|
|
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
|
+
* },
|
|
1226
548
|
* })
|
|
549
|
+
* ```
|
|
1227
550
|
*
|
|
1228
|
-
*
|
|
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
|
+
* })
|
|
1229
563
|
* ```
|
|
1230
564
|
*/
|
|
1231
|
-
function
|
|
1232
|
-
|
|
1233
|
-
|
|
1234
|
-
|
|
1235
|
-
|
|
1236
|
-
|
|
1237
|
-
|
|
1238
|
-
|
|
1239
|
-
|
|
1240
|
-
|
|
1241
|
-
|
|
1242
|
-
|
|
1243
|
-
|
|
1244
|
-
|
|
1245
|
-
|
|
1246
|
-
|
|
1247
|
-
|
|
1248
|
-
|
|
1249
|
-
|
|
1250
|
-
|
|
1251
|
-
|
|
1252
|
-
|
|
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
|
|
1253
592
|
},
|
|
1254
|
-
|
|
1255
|
-
|
|
1256
|
-
|
|
1257
|
-
|
|
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))
|
|
1258
599
|
};
|
|
1259
|
-
return instance;
|
|
1260
600
|
}
|
|
1261
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
|
+
};
|
|
899
|
+
//#endregion
|
|
1262
900
|
//#region src/createRenderer.ts
|
|
1263
901
|
/**
|
|
1264
|
-
*
|
|
902
|
+
* Defines a renderer factory. Renderers turn the generator's return value
|
|
903
|
+
* (JSX, a template string, a tree of any shape) into `FileNode`s that get
|
|
904
|
+
* written to disk.
|
|
1265
905
|
*
|
|
1266
|
-
*
|
|
1267
|
-
* renderer
|
|
1268
|
-
* to
|
|
906
|
+
* Use this to support output formats beyond JSX, for instance, a Handlebars
|
|
907
|
+
* renderer, a string-template renderer, or a renderer that writes binary
|
|
908
|
+
* files. Plugins and generators pick the renderer to use via the `renderer`
|
|
909
|
+
* field on `defineGenerator`.
|
|
1269
910
|
*
|
|
1270
|
-
* @example
|
|
911
|
+
* @example A minimal renderer that wraps a custom runtime
|
|
1271
912
|
* ```ts
|
|
1272
|
-
*
|
|
1273
|
-
*
|
|
1274
|
-
*
|
|
913
|
+
* import { createRenderer } from '@kubb/core'
|
|
914
|
+
*
|
|
915
|
+
* export const myRenderer = createRenderer(() => {
|
|
916
|
+
* const runtime = new MyRuntime()
|
|
1275
917
|
* return {
|
|
1276
|
-
* async render(element) {
|
|
1277
|
-
*
|
|
1278
|
-
*
|
|
918
|
+
* async render(element) {
|
|
919
|
+
* await runtime.render(element)
|
|
920
|
+
* },
|
|
921
|
+
* get files() {
|
|
922
|
+
* return runtime.files
|
|
923
|
+
* },
|
|
924
|
+
* dispose() {
|
|
925
|
+
* runtime.dispose()
|
|
926
|
+
* },
|
|
927
|
+
* unmount(error) {
|
|
928
|
+
* runtime.dispose(error)
|
|
929
|
+
* },
|
|
930
|
+
* [Symbol.dispose]() {
|
|
931
|
+
* this.dispose()
|
|
932
|
+
* },
|
|
1279
933
|
* }
|
|
1280
934
|
* })
|
|
1281
|
-
*
|
|
1282
|
-
* // packages/plugin-zod/src/generators/zodGenerator.tsx
|
|
1283
|
-
* import { jsxRenderer } from '@kubb/renderer-jsx'
|
|
1284
|
-
* export const zodGenerator = defineGenerator<PluginZod>({
|
|
1285
|
-
* name: 'zod',
|
|
1286
|
-
* renderer: jsxRenderer,
|
|
1287
|
-
* schema(node, options) { return <File ...>...</File> },
|
|
1288
|
-
* })
|
|
1289
935
|
* ```
|
|
1290
936
|
*/
|
|
1291
937
|
function createRenderer(factory) {
|
|
@@ -1294,47 +940,50 @@ function createRenderer(factory) {
|
|
|
1294
940
|
//#endregion
|
|
1295
941
|
//#region src/defineGenerator.ts
|
|
1296
942
|
/**
|
|
1297
|
-
* Defines a generator
|
|
1298
|
-
*
|
|
1299
|
-
*
|
|
1300
|
-
*/
|
|
1301
|
-
function defineGenerator(generator) {
|
|
1302
|
-
return generator;
|
|
1303
|
-
}
|
|
1304
|
-
//#endregion
|
|
1305
|
-
//#region src/defineLogger.ts
|
|
1306
|
-
/**
|
|
1307
|
-
* Wraps a logger definition into a typed {@link Logger}.
|
|
943
|
+
* Defines a generator: a unit of work that runs during the plugin's AST walk
|
|
944
|
+
* and produces files. Plugins register generators via `ctx.addGenerator()`
|
|
945
|
+
* inside `kubb:plugin:setup`.
|
|
1308
946
|
*
|
|
1309
|
-
*
|
|
1310
|
-
*
|
|
1311
|
-
*
|
|
1312
|
-
*
|
|
1313
|
-
*
|
|
1314
|
-
*
|
|
1315
|
-
*
|
|
947
|
+
* The returned object is the input as-is, but with `this` types preserved so
|
|
948
|
+
* `schema`/`operation`/`operations` methods are correctly typed against the
|
|
949
|
+
* plugin's `PluginFactoryOptions`. Renderer elements and `FileNode[]` returns
|
|
950
|
+
* are both handled by the runtime, so pick whichever style fits.
|
|
951
|
+
*
|
|
952
|
+
* @example JSX-based schema generator
|
|
953
|
+
* ```tsx
|
|
954
|
+
* import { defineGenerator } from '@kubb/core'
|
|
955
|
+
* import { jsxRenderer } from '@kubb/renderer-jsx'
|
|
956
|
+
*
|
|
957
|
+
* export const typeGenerator = defineGenerator({
|
|
958
|
+
* name: 'typescript',
|
|
959
|
+
* renderer: jsxRenderer,
|
|
960
|
+
* schema(node, ctx) {
|
|
961
|
+
* return (
|
|
962
|
+
* <File path={`${ctx.root}/${node.name}.ts`}>
|
|
963
|
+
* <Type node={node} resolver={ctx.resolver} />
|
|
964
|
+
* </File>
|
|
965
|
+
* )
|
|
1316
966
|
* },
|
|
1317
967
|
* })
|
|
1318
968
|
* ```
|
|
1319
969
|
*/
|
|
1320
|
-
function
|
|
1321
|
-
return
|
|
970
|
+
function defineGenerator(generator) {
|
|
971
|
+
return generator;
|
|
1322
972
|
}
|
|
1323
973
|
//#endregion
|
|
1324
974
|
//#region src/defineMiddleware.ts
|
|
1325
975
|
/**
|
|
1326
|
-
* Creates a middleware factory
|
|
1327
|
-
*
|
|
1328
|
-
*
|
|
1329
|
-
* Per-build state (such as accumulators) belongs inside the factory closure so each `createKubb` invocation gets its own isolated instance.
|
|
976
|
+
* Creates a middleware factory. Middleware fires after every plugin handler
|
|
977
|
+
* for the same event, which makes it the natural place for post-processing
|
|
978
|
+
* (barrel files, lint runs, audit logs).
|
|
1330
979
|
*
|
|
1331
|
-
*
|
|
980
|
+
* Per-build state belongs inside the factory closure so each `createKubb`
|
|
981
|
+
* invocation gets its own isolated instance.
|
|
1332
982
|
*
|
|
1333
|
-
* @example
|
|
983
|
+
* @example Stateless middleware
|
|
1334
984
|
* ```ts
|
|
1335
985
|
* import { defineMiddleware } from '@kubb/core'
|
|
1336
986
|
*
|
|
1337
|
-
* // Stateless middleware
|
|
1338
987
|
* export const logMiddleware = defineMiddleware(() => ({
|
|
1339
988
|
* name: 'log-middleware',
|
|
1340
989
|
* hooks: {
|
|
@@ -1343,8 +992,12 @@ function defineLogger(logger) {
|
|
|
1343
992
|
* },
|
|
1344
993
|
* },
|
|
1345
994
|
* }))
|
|
995
|
+
* ```
|
|
996
|
+
*
|
|
997
|
+
* @example Middleware with options and per-build state
|
|
998
|
+
* ```ts
|
|
999
|
+
* import { defineMiddleware } from '@kubb/core'
|
|
1346
1000
|
*
|
|
1347
|
-
* // Middleware with options and per-build state
|
|
1348
1001
|
* export const prefixMiddleware = defineMiddleware((options: { prefix: string } = { prefix: '' }) => {
|
|
1349
1002
|
* const seen = new Set<string>()
|
|
1350
1003
|
* return {
|
|
@@ -1364,20 +1017,23 @@ function defineMiddleware(factory) {
|
|
|
1364
1017
|
//#endregion
|
|
1365
1018
|
//#region src/defineParser.ts
|
|
1366
1019
|
/**
|
|
1367
|
-
* Defines a parser with type
|
|
1368
|
-
*
|
|
1369
|
-
* @note Call the returned factory with optional options to instantiate the parser.
|
|
1020
|
+
* Defines a parser with type-safe `this`. Used to register handlers for new
|
|
1021
|
+
* file extensions or to plug a non-TypeScript output into the build.
|
|
1370
1022
|
*
|
|
1371
1023
|
* @example
|
|
1372
1024
|
* ```ts
|
|
1373
|
-
* import { defineParser } from '@kubb/core'
|
|
1025
|
+
* import { defineParser, ast } from '@kubb/core'
|
|
1374
1026
|
*
|
|
1375
1027
|
* export const jsonParser = defineParser({
|
|
1376
1028
|
* name: 'json',
|
|
1377
1029
|
* extNames: ['.json'],
|
|
1378
1030
|
* parse(file) {
|
|
1379
|
-
*
|
|
1380
|
-
*
|
|
1031
|
+
* return file.sources
|
|
1032
|
+
* .map((source) => ast.extractStringsFromNodes(source.nodes ?? []))
|
|
1033
|
+
* .join('\n')
|
|
1034
|
+
* },
|
|
1035
|
+
* print(...nodes) {
|
|
1036
|
+
* return nodes.map(String).join('\n')
|
|
1381
1037
|
* },
|
|
1382
1038
|
* })
|
|
1383
1039
|
* ```
|
|
@@ -1386,108 +1042,34 @@ function defineParser(parser) {
|
|
|
1386
1042
|
return parser;
|
|
1387
1043
|
}
|
|
1388
1044
|
//#endregion
|
|
1389
|
-
|
|
1390
|
-
|
|
1391
|
-
|
|
1392
|
-
|
|
1393
|
-
|
|
1394
|
-
* All lifecycle events from `KubbHooks` are available for subscription.
|
|
1395
|
-
*
|
|
1396
|
-
* @note For real plugins, use a `PluginFactoryOptions` type parameter to get type-safe context in `kubb:plugin:setup`.
|
|
1397
|
-
* Plugin names should follow the convention `plugin-<feature>` (e.g., `plugin-react-query`, `plugin-zod`).
|
|
1398
|
-
*
|
|
1399
|
-
* @example
|
|
1400
|
-
* ```ts
|
|
1401
|
-
* import { definePlugin } from '@kubb/core'
|
|
1402
|
-
*
|
|
1403
|
-
* export const pluginTs = definePlugin((options: { prefix?: string } = {}) => ({
|
|
1404
|
-
* name: 'plugin-ts',
|
|
1405
|
-
* hooks: {
|
|
1406
|
-
* 'kubb:plugin:setup'(ctx) {
|
|
1407
|
-
* ctx.setResolver(resolverTs)
|
|
1408
|
-
* },
|
|
1409
|
-
* },
|
|
1410
|
-
* }))
|
|
1411
|
-
* ```
|
|
1412
|
-
*/
|
|
1413
|
-
function definePlugin(factory) {
|
|
1414
|
-
return (options) => factory(options ?? {});
|
|
1415
|
-
}
|
|
1416
|
-
//#endregion
|
|
1417
|
-
//#region src/storages/memoryStorage.ts
|
|
1418
|
-
/**
|
|
1419
|
-
* In-memory storage driver. Useful for testing and dry-run scenarios where
|
|
1420
|
-
* generated output should be captured without touching the filesystem.
|
|
1421
|
-
*
|
|
1422
|
-
* All data lives in a `Map` scoped to the storage instance and is discarded
|
|
1423
|
-
* when the instance is garbage-collected.
|
|
1424
|
-
*
|
|
1425
|
-
* @example
|
|
1426
|
-
* ```ts
|
|
1427
|
-
* import { memoryStorage } from '@kubb/core'
|
|
1428
|
-
* import { defineConfig } from 'kubb'
|
|
1429
|
-
*
|
|
1430
|
-
* export default defineConfig({
|
|
1431
|
-
* input: { path: './petStore.yaml' },
|
|
1432
|
-
* output: { path: './src/gen' },
|
|
1433
|
-
* storage: memoryStorage(),
|
|
1434
|
-
* })
|
|
1435
|
-
* ```
|
|
1436
|
-
*/
|
|
1437
|
-
const memoryStorage = createStorage(() => {
|
|
1438
|
-
const store = /* @__PURE__ */ new Map();
|
|
1439
|
-
return {
|
|
1440
|
-
name: "memory",
|
|
1441
|
-
async hasItem(key) {
|
|
1442
|
-
return store.has(key);
|
|
1443
|
-
},
|
|
1444
|
-
async getItem(key) {
|
|
1445
|
-
return store.get(key) ?? null;
|
|
1446
|
-
},
|
|
1447
|
-
async setItem(key, value) {
|
|
1448
|
-
store.set(key, value);
|
|
1449
|
-
},
|
|
1450
|
-
async removeItem(key) {
|
|
1451
|
-
store.delete(key);
|
|
1452
|
-
},
|
|
1453
|
-
async getKeys(base) {
|
|
1454
|
-
const keys = [...store.keys()];
|
|
1455
|
-
return base ? keys.filter((k) => k.startsWith(base)) : keys;
|
|
1456
|
-
},
|
|
1457
|
-
async clear(base) {
|
|
1458
|
-
if (!base) {
|
|
1459
|
-
store.clear();
|
|
1460
|
-
return;
|
|
1461
|
-
}
|
|
1462
|
-
for (const key of store.keys()) if (key.startsWith(base)) store.delete(key);
|
|
1463
|
-
}
|
|
1464
|
-
};
|
|
1465
|
-
});
|
|
1466
|
-
//#endregion
|
|
1467
|
-
exports.AsyncEventEmitter = AsyncEventEmitter;
|
|
1468
|
-
exports.FileManager = require_PluginDriver.FileManager;
|
|
1469
|
-
exports.FileProcessor = FileProcessor;
|
|
1470
|
-
exports.PluginDriver = require_PluginDriver.PluginDriver;
|
|
1471
|
-
exports.URLPath = URLPath;
|
|
1045
|
+
exports.AsyncEventEmitter = require_memoryStorage.AsyncEventEmitter;
|
|
1046
|
+
exports.Diagnostics = require_memoryStorage.Diagnostics;
|
|
1047
|
+
exports.KubbDriver = require_memoryStorage.KubbDriver;
|
|
1048
|
+
exports.Telemetry = Telemetry;
|
|
1049
|
+
exports.URLPath = require_memoryStorage.URLPath;
|
|
1472
1050
|
Object.defineProperty(exports, "ast", {
|
|
1473
1051
|
enumerable: true,
|
|
1474
1052
|
get: function() {
|
|
1475
1053
|
return _kubb_ast;
|
|
1476
1054
|
}
|
|
1477
1055
|
});
|
|
1056
|
+
exports.cliReporter = cliReporter;
|
|
1478
1057
|
exports.createAdapter = createAdapter;
|
|
1479
1058
|
exports.createKubb = createKubb;
|
|
1480
1059
|
exports.createRenderer = createRenderer;
|
|
1481
|
-
exports.
|
|
1060
|
+
exports.createReporter = createReporter;
|
|
1061
|
+
exports.createStorage = require_memoryStorage.createStorage;
|
|
1482
1062
|
exports.defineGenerator = defineGenerator;
|
|
1483
1063
|
exports.defineLogger = defineLogger;
|
|
1484
1064
|
exports.defineMiddleware = defineMiddleware;
|
|
1485
1065
|
exports.defineParser = defineParser;
|
|
1486
|
-
exports.definePlugin = definePlugin;
|
|
1487
|
-
exports.defineResolver =
|
|
1066
|
+
exports.definePlugin = require_memoryStorage.definePlugin;
|
|
1067
|
+
exports.defineResolver = require_memoryStorage.defineResolver;
|
|
1068
|
+
exports.fileReporter = fileReporter;
|
|
1488
1069
|
exports.fsStorage = fsStorage;
|
|
1489
|
-
exports.
|
|
1490
|
-
exports.logLevel =
|
|
1491
|
-
exports.memoryStorage = memoryStorage;
|
|
1070
|
+
exports.jsonReporter = jsonReporter;
|
|
1071
|
+
exports.logLevel = logLevel;
|
|
1072
|
+
exports.memoryStorage = require_memoryStorage.memoryStorage;
|
|
1073
|
+
exports.selectReporters = selectReporters;
|
|
1492
1074
|
|
|
1493
1075
|
//# sourceMappingURL=index.cjs.map
|