@kubb/core 5.0.0-beta.19 → 5.0.0-beta.20
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/{PluginDriver-DXp767s2.cjs → KubbDriver-BXSnJ3qM.cjs} +546 -102
- package/dist/KubbDriver-BXSnJ3qM.cjs.map +1 -0
- package/dist/{PluginDriver-uNex0SAr.js → KubbDriver-Cxii_rBp.js} +522 -96
- package/dist/KubbDriver-Cxii_rBp.js.map +1 -0
- package/dist/{createKubb-BJGymYhe.d.ts → createKubb-Dcmtjqds.d.ts} +32 -62
- package/dist/index.cjs +257 -684
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.ts +2 -2
- package/dist/index.js +248 -675
- package/dist/index.js.map +1 -1
- package/dist/mocks.cjs +9 -10
- package/dist/mocks.cjs.map +1 -1
- package/dist/mocks.d.ts +5 -5
- package/dist/mocks.js +5 -6
- package/dist/mocks.js.map +1 -1
- package/package.json +4 -4
- package/src/{PluginDriver.ts → KubbDriver.ts} +175 -88
- package/src/constants.ts +4 -4
- package/src/createAdapter.ts +0 -8
- package/src/createKubb.ts +293 -469
- package/src/defineGenerator.ts +9 -8
- package/src/definePlugin.ts +5 -5
- package/src/defineResolver.ts +26 -40
- package/src/index.ts +1 -1
- package/src/mocks.ts +8 -8
- package/dist/PluginDriver-DXp767s2.cjs.map +0 -1
- package/dist/PluginDriver-uNex0SAr.js.map +0 -1
|
@@ -93,6 +93,381 @@ function pascalCase(text, { isFile, prefix = "", suffix = "" } = {}) {
|
|
|
93
93
|
return toCamelOrPascal(`${prefix} ${text} ${suffix}`, true);
|
|
94
94
|
}
|
|
95
95
|
//#endregion
|
|
96
|
+
//#region ../../internals/utils/src/promise.ts
|
|
97
|
+
function* chunks(arr, size) {
|
|
98
|
+
for (let i = 0; i < arr.length; i += size) yield arr.slice(i, i + size);
|
|
99
|
+
}
|
|
100
|
+
/**
|
|
101
|
+
* Slices `source` into batches of `concurrency` items and awaits `process` for each batch.
|
|
102
|
+
* Accepts both plain arrays (sync) and `AsyncIterable` (streaming).
|
|
103
|
+
*
|
|
104
|
+
* `process` controls whether items inside a batch run in parallel; this helper only
|
|
105
|
+
* controls batch size and per-batch flushing.
|
|
106
|
+
*
|
|
107
|
+
* @example
|
|
108
|
+
* ```ts
|
|
109
|
+
* // parallel dispatch inside each batch
|
|
110
|
+
* await forBatches(schemas, (batch) => Promise.all(batch.map(process)), { concurrency: 8 })
|
|
111
|
+
*
|
|
112
|
+
* // async iterable with a flush after every batch
|
|
113
|
+
* await forBatches(stream.schemas, (batch) => dispatch(batch), { concurrency: 8, flush })
|
|
114
|
+
* ```
|
|
115
|
+
*/
|
|
116
|
+
async function forBatches(source, process, options) {
|
|
117
|
+
const { concurrency, flush } = options;
|
|
118
|
+
if (Array.isArray(source)) {
|
|
119
|
+
for (const batch of chunks(source, concurrency)) {
|
|
120
|
+
await process(batch);
|
|
121
|
+
if (flush) await flush();
|
|
122
|
+
}
|
|
123
|
+
return;
|
|
124
|
+
}
|
|
125
|
+
const batch = [];
|
|
126
|
+
for await (const item of source) {
|
|
127
|
+
batch.push(item);
|
|
128
|
+
if (batch.length >= concurrency) {
|
|
129
|
+
await process(batch.splice(0));
|
|
130
|
+
if (flush) await flush();
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
if (batch.length > 0) {
|
|
134
|
+
await process(batch.splice(0));
|
|
135
|
+
if (flush) await flush();
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
/**
|
|
139
|
+
* Runs `work`, passing `flush` as its periodic-flush callback, then calls
|
|
140
|
+
* `flush` once more to drain any items that did not cross a flush boundary.
|
|
141
|
+
*
|
|
142
|
+
* @example
|
|
143
|
+
* ```ts
|
|
144
|
+
* await withDrain(
|
|
145
|
+
* (flush) => processItems(items, { flush }),
|
|
146
|
+
* () => writeRemainingFiles(),
|
|
147
|
+
* )
|
|
148
|
+
* ```
|
|
149
|
+
*/
|
|
150
|
+
async function withDrain(work, flush) {
|
|
151
|
+
await work(flush);
|
|
152
|
+
await flush();
|
|
153
|
+
}
|
|
154
|
+
/** Returns `true` when `result` is a thenable `Promise`.
|
|
155
|
+
*
|
|
156
|
+
* @example
|
|
157
|
+
* ```ts
|
|
158
|
+
* isPromise(Promise.resolve(1)) // true
|
|
159
|
+
* isPromise(42) // false
|
|
160
|
+
* ```
|
|
161
|
+
*/
|
|
162
|
+
function isPromise(result) {
|
|
163
|
+
return result !== null && result !== void 0 && typeof result["then"] === "function";
|
|
164
|
+
}
|
|
165
|
+
/**
|
|
166
|
+
* Wraps `factory` with a keyed cache backed by the provided store.
|
|
167
|
+
*
|
|
168
|
+
* Pass a `WeakMap` for object keys (results are GC-eligible when the key is
|
|
169
|
+
* collected) or a `Map` for primitive keys. For multi-argument functions,
|
|
170
|
+
* nest two `memoize` calls — the outer keyed by the first argument, the
|
|
171
|
+
* inner (created once per outer miss) keyed by the second.
|
|
172
|
+
*
|
|
173
|
+
* Because the cache is owned by the caller, it can be shared, inspected, or
|
|
174
|
+
* cleared independently of the memoized function.
|
|
175
|
+
*
|
|
176
|
+
* @example Single WeakMap key
|
|
177
|
+
* ```ts
|
|
178
|
+
* const cache = new WeakMap<SchemaNode, Set<string>>()
|
|
179
|
+
* const getRefs = memoize(cache, (node) => collectRefs(node))
|
|
180
|
+
* ```
|
|
181
|
+
*
|
|
182
|
+
* @example Single Map key (primitive)
|
|
183
|
+
* ```ts
|
|
184
|
+
* const cache = new Map<string, Resolver>()
|
|
185
|
+
* const getResolver = memoize(cache, (name) => buildResolver(name))
|
|
186
|
+
* ```
|
|
187
|
+
*
|
|
188
|
+
* @example Two-level (object + primitive)
|
|
189
|
+
* ```ts
|
|
190
|
+
* const outer = new WeakMap<Params[], Map<string, Params[]>>()
|
|
191
|
+
* const fn = memoize(outer, (params) => memoize(new Map(), (key) => transform(params, key)))
|
|
192
|
+
* fn(params)('camelcase')
|
|
193
|
+
* ```
|
|
194
|
+
*/
|
|
195
|
+
function memoize(store, factory) {
|
|
196
|
+
return (key) => {
|
|
197
|
+
if (store.has(key)) return store.get(key);
|
|
198
|
+
const value = factory(key);
|
|
199
|
+
store.set(key, value);
|
|
200
|
+
return value;
|
|
201
|
+
};
|
|
202
|
+
}
|
|
203
|
+
/**
|
|
204
|
+
* Wraps a plain array in a reusable `AsyncIterable`.
|
|
205
|
+
* Each `[Symbol.asyncIterator]()` call returns a fresh generator so the
|
|
206
|
+
* iterable can be consumed multiple times (e.g. once per plugin pre-scan).
|
|
207
|
+
*
|
|
208
|
+
* @example
|
|
209
|
+
* ```ts
|
|
210
|
+
* const stream = arrayToAsyncIterable([1, 2, 3])
|
|
211
|
+
* for await (const n of stream) console.log(n) // 1, 2, 3
|
|
212
|
+
* ```
|
|
213
|
+
*/
|
|
214
|
+
function arrayToAsyncIterable(arr) {
|
|
215
|
+
return { [Symbol.asyncIterator]() {
|
|
216
|
+
return (async function* () {
|
|
217
|
+
yield* arr;
|
|
218
|
+
})();
|
|
219
|
+
} };
|
|
220
|
+
}
|
|
221
|
+
//#endregion
|
|
222
|
+
//#region ../../internals/utils/src/reserved.ts
|
|
223
|
+
/**
|
|
224
|
+
* JavaScript and Java reserved words.
|
|
225
|
+
* @link https://github.com/jonschlinkert/reserved/blob/master/index.js
|
|
226
|
+
*/
|
|
227
|
+
const reservedWords = new Set([
|
|
228
|
+
"abstract",
|
|
229
|
+
"arguments",
|
|
230
|
+
"boolean",
|
|
231
|
+
"break",
|
|
232
|
+
"byte",
|
|
233
|
+
"case",
|
|
234
|
+
"catch",
|
|
235
|
+
"char",
|
|
236
|
+
"class",
|
|
237
|
+
"const",
|
|
238
|
+
"continue",
|
|
239
|
+
"debugger",
|
|
240
|
+
"default",
|
|
241
|
+
"delete",
|
|
242
|
+
"do",
|
|
243
|
+
"double",
|
|
244
|
+
"else",
|
|
245
|
+
"enum",
|
|
246
|
+
"eval",
|
|
247
|
+
"export",
|
|
248
|
+
"extends",
|
|
249
|
+
"false",
|
|
250
|
+
"final",
|
|
251
|
+
"finally",
|
|
252
|
+
"float",
|
|
253
|
+
"for",
|
|
254
|
+
"function",
|
|
255
|
+
"goto",
|
|
256
|
+
"if",
|
|
257
|
+
"implements",
|
|
258
|
+
"import",
|
|
259
|
+
"in",
|
|
260
|
+
"instanceof",
|
|
261
|
+
"int",
|
|
262
|
+
"interface",
|
|
263
|
+
"let",
|
|
264
|
+
"long",
|
|
265
|
+
"native",
|
|
266
|
+
"new",
|
|
267
|
+
"null",
|
|
268
|
+
"package",
|
|
269
|
+
"private",
|
|
270
|
+
"protected",
|
|
271
|
+
"public",
|
|
272
|
+
"return",
|
|
273
|
+
"short",
|
|
274
|
+
"static",
|
|
275
|
+
"super",
|
|
276
|
+
"switch",
|
|
277
|
+
"synchronized",
|
|
278
|
+
"this",
|
|
279
|
+
"throw",
|
|
280
|
+
"throws",
|
|
281
|
+
"transient",
|
|
282
|
+
"true",
|
|
283
|
+
"try",
|
|
284
|
+
"typeof",
|
|
285
|
+
"var",
|
|
286
|
+
"void",
|
|
287
|
+
"volatile",
|
|
288
|
+
"while",
|
|
289
|
+
"with",
|
|
290
|
+
"yield",
|
|
291
|
+
"Array",
|
|
292
|
+
"Date",
|
|
293
|
+
"hasOwnProperty",
|
|
294
|
+
"Infinity",
|
|
295
|
+
"isFinite",
|
|
296
|
+
"isNaN",
|
|
297
|
+
"isPrototypeOf",
|
|
298
|
+
"length",
|
|
299
|
+
"Math",
|
|
300
|
+
"name",
|
|
301
|
+
"NaN",
|
|
302
|
+
"Number",
|
|
303
|
+
"Object",
|
|
304
|
+
"prototype",
|
|
305
|
+
"String",
|
|
306
|
+
"toString",
|
|
307
|
+
"undefined",
|
|
308
|
+
"valueOf"
|
|
309
|
+
]);
|
|
310
|
+
/**
|
|
311
|
+
* Returns `true` when `name` is a syntactically valid JavaScript variable name.
|
|
312
|
+
*
|
|
313
|
+
* @example
|
|
314
|
+
* ```ts
|
|
315
|
+
* isValidVarName('status') // true
|
|
316
|
+
* isValidVarName('class') // false (reserved word)
|
|
317
|
+
* isValidVarName('42foo') // false (starts with digit)
|
|
318
|
+
* ```
|
|
319
|
+
*/
|
|
320
|
+
function isValidVarName(name) {
|
|
321
|
+
if (!name || reservedWords.has(name)) return false;
|
|
322
|
+
return /^[a-zA-Z_$][a-zA-Z0-9_$]*$/.test(name);
|
|
323
|
+
}
|
|
324
|
+
//#endregion
|
|
325
|
+
//#region ../../internals/utils/src/urlPath.ts
|
|
326
|
+
/**
|
|
327
|
+
* Parses and transforms an OpenAPI/Swagger path string into various URL formats.
|
|
328
|
+
*
|
|
329
|
+
* @example
|
|
330
|
+
* const p = new URLPath('/pet/{petId}')
|
|
331
|
+
* p.URL // '/pet/:petId'
|
|
332
|
+
* p.template // '`/pet/${petId}`'
|
|
333
|
+
*/
|
|
334
|
+
var URLPath = class {
|
|
335
|
+
/**
|
|
336
|
+
* The raw OpenAPI/Swagger path string, e.g. `/pet/{petId}`.
|
|
337
|
+
*/
|
|
338
|
+
path;
|
|
339
|
+
#options;
|
|
340
|
+
constructor(path, options = {}) {
|
|
341
|
+
this.path = path;
|
|
342
|
+
this.#options = options;
|
|
343
|
+
}
|
|
344
|
+
/** Converts the OpenAPI path to Express-style colon syntax, e.g. `/pet/{petId}` → `/pet/:petId`.
|
|
345
|
+
*
|
|
346
|
+
* @example
|
|
347
|
+
* ```ts
|
|
348
|
+
* new URLPath('/pet/{petId}').URL // '/pet/:petId'
|
|
349
|
+
* ```
|
|
350
|
+
*/
|
|
351
|
+
get URL() {
|
|
352
|
+
return this.toURLPath();
|
|
353
|
+
}
|
|
354
|
+
/** Returns `true` when `path` is a fully-qualified URL (e.g. starts with `https://`).
|
|
355
|
+
*
|
|
356
|
+
* @example
|
|
357
|
+
* ```ts
|
|
358
|
+
* new URLPath('https://petstore.swagger.io/v2/pet').isURL // true
|
|
359
|
+
* new URLPath('/pet/{petId}').isURL // false
|
|
360
|
+
* ```
|
|
361
|
+
*/
|
|
362
|
+
get isURL() {
|
|
363
|
+
try {
|
|
364
|
+
return !!new URL(this.path).href;
|
|
365
|
+
} catch {
|
|
366
|
+
return false;
|
|
367
|
+
}
|
|
368
|
+
}
|
|
369
|
+
/**
|
|
370
|
+
* Converts the OpenAPI path to a TypeScript template literal string.
|
|
371
|
+
*
|
|
372
|
+
* @example
|
|
373
|
+
* new URLPath('/pet/{petId}').template // '`/pet/${petId}`'
|
|
374
|
+
* new URLPath('/account/monetary-accountID').template // '`/account/${monetaryAccountId}`'
|
|
375
|
+
*/
|
|
376
|
+
get template() {
|
|
377
|
+
return this.toTemplateString();
|
|
378
|
+
}
|
|
379
|
+
/** Returns the path and its extracted params as a structured `URLObject`, or as a stringified expression when `stringify` is set.
|
|
380
|
+
*
|
|
381
|
+
* @example
|
|
382
|
+
* ```ts
|
|
383
|
+
* new URLPath('/pet/{petId}').object
|
|
384
|
+
* // { url: '/pet/:petId', params: { petId: 'petId' } }
|
|
385
|
+
* ```
|
|
386
|
+
*/
|
|
387
|
+
get object() {
|
|
388
|
+
return this.toObject();
|
|
389
|
+
}
|
|
390
|
+
/** Returns a map of path parameter names, or `undefined` when the path has no parameters.
|
|
391
|
+
*
|
|
392
|
+
* @example
|
|
393
|
+
* ```ts
|
|
394
|
+
* new URLPath('/pet/{petId}').params // { petId: 'petId' }
|
|
395
|
+
* new URLPath('/pet').params // undefined
|
|
396
|
+
* ```
|
|
397
|
+
*/
|
|
398
|
+
get params() {
|
|
399
|
+
return this.getParams();
|
|
400
|
+
}
|
|
401
|
+
#transformParam(raw) {
|
|
402
|
+
const param = isValidVarName(raw) ? raw : camelCase(raw);
|
|
403
|
+
return this.#options.casing === "camelcase" ? camelCase(param) : param;
|
|
404
|
+
}
|
|
405
|
+
/**
|
|
406
|
+
* Iterates over every `{param}` token in `path`, calling `fn` with the raw token and transformed name.
|
|
407
|
+
*/
|
|
408
|
+
#eachParam(fn) {
|
|
409
|
+
for (const match of this.path.matchAll(/\{([^}]+)\}/g)) {
|
|
410
|
+
const raw = match[1];
|
|
411
|
+
fn(raw, this.#transformParam(raw));
|
|
412
|
+
}
|
|
413
|
+
}
|
|
414
|
+
toObject({ type = "path", replacer, stringify } = {}) {
|
|
415
|
+
const object = {
|
|
416
|
+
url: type === "path" ? this.toURLPath() : this.toTemplateString({ replacer }),
|
|
417
|
+
params: this.getParams()
|
|
418
|
+
};
|
|
419
|
+
if (stringify) {
|
|
420
|
+
if (type === "template") return JSON.stringify(object).replaceAll("'", "").replaceAll(`"`, "");
|
|
421
|
+
if (object.params) return `{ url: '${object.url}', params: ${JSON.stringify(object.params).replaceAll("'", "").replaceAll(`"`, "")} }`;
|
|
422
|
+
return `{ url: '${object.url}' }`;
|
|
423
|
+
}
|
|
424
|
+
return object;
|
|
425
|
+
}
|
|
426
|
+
/**
|
|
427
|
+
* Converts the OpenAPI path to a TypeScript template literal string.
|
|
428
|
+
* An optional `replacer` can transform each extracted parameter name before interpolation.
|
|
429
|
+
*
|
|
430
|
+
* @example
|
|
431
|
+
* new URLPath('/pet/{petId}').toTemplateString() // '`/pet/${petId}`'
|
|
432
|
+
*/
|
|
433
|
+
toTemplateString({ prefix = "", replacer } = {}) {
|
|
434
|
+
return `\`${prefix}${this.path.split(/\{([^}]+)\}/).map((part, i) => {
|
|
435
|
+
if (i % 2 === 0) return part;
|
|
436
|
+
const param = this.#transformParam(part);
|
|
437
|
+
return `\${${replacer ? replacer(param) : param}}`;
|
|
438
|
+
}).join("")}\``;
|
|
439
|
+
}
|
|
440
|
+
/**
|
|
441
|
+
* Extracts all `{param}` segments from the path and returns them as a key-value map.
|
|
442
|
+
* An optional `replacer` transforms each parameter name in both key and value positions.
|
|
443
|
+
* Returns `undefined` when no path parameters are found.
|
|
444
|
+
*
|
|
445
|
+
* @example
|
|
446
|
+
* ```ts
|
|
447
|
+
* new URLPath('/pet/{petId}/tag/{tagId}').getParams()
|
|
448
|
+
* // { petId: 'petId', tagId: 'tagId' }
|
|
449
|
+
* ```
|
|
450
|
+
*/
|
|
451
|
+
getParams(replacer) {
|
|
452
|
+
const params = {};
|
|
453
|
+
this.#eachParam((_raw, param) => {
|
|
454
|
+
const key = replacer ? replacer(param) : param;
|
|
455
|
+
params[key] = key;
|
|
456
|
+
});
|
|
457
|
+
return Object.keys(params).length > 0 ? params : void 0;
|
|
458
|
+
}
|
|
459
|
+
/** Converts the OpenAPI path to Express-style colon syntax.
|
|
460
|
+
*
|
|
461
|
+
* @example
|
|
462
|
+
* ```ts
|
|
463
|
+
* new URLPath('/pet/{petId}').toURLPath() // '/pet/:petId'
|
|
464
|
+
* ```
|
|
465
|
+
*/
|
|
466
|
+
toURLPath() {
|
|
467
|
+
return this.path.replace(/\{([^}]+)\}/g, ":$1");
|
|
468
|
+
}
|
|
469
|
+
};
|
|
470
|
+
//#endregion
|
|
96
471
|
//#region src/constants.ts
|
|
97
472
|
/**
|
|
98
473
|
* Base URL for the Kubb Studio web app.
|
|
@@ -173,14 +548,12 @@ function testPattern(value, pattern) {
|
|
|
173
548
|
* Checks if an operation matches a pattern for a given filter type (`tag`, `operationId`, `path`, `method`).
|
|
174
549
|
*/
|
|
175
550
|
function matchesOperationPattern(node, type, pattern) {
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
default: return false;
|
|
183
|
-
}
|
|
551
|
+
if (type === "tag") return node.tags.some((tag) => testPattern(tag, pattern));
|
|
552
|
+
if (type === "operationId") return testPattern(node.operationId, pattern);
|
|
553
|
+
if (type === "path") return testPattern(node.path, pattern);
|
|
554
|
+
if (type === "method") return testPattern(node.method.toLowerCase(), pattern);
|
|
555
|
+
if (type === "contentType") return node.requestBody?.content?.some((c) => testPattern(c.contentType, pattern)) ?? false;
|
|
556
|
+
return false;
|
|
184
557
|
}
|
|
185
558
|
/**
|
|
186
559
|
* Checks if a schema matches a pattern for a given filter type (`schemaName`).
|
|
@@ -188,10 +561,8 @@ function matchesOperationPattern(node, type, pattern) {
|
|
|
188
561
|
* Returns `null` when the filter type doesn't apply to schemas.
|
|
189
562
|
*/
|
|
190
563
|
function matchesSchemaPattern(node, type, pattern) {
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
default: return null;
|
|
194
|
-
}
|
|
564
|
+
if (type === "schemaName") return node.name ? testPattern(node.name, pattern) : false;
|
|
565
|
+
return null;
|
|
195
566
|
}
|
|
196
567
|
/**
|
|
197
568
|
* Default name resolver used by `defineResolver`.
|
|
@@ -414,10 +785,9 @@ function buildDefaultBanner({ title, description, version, config }) {
|
|
|
414
785
|
*
|
|
415
786
|
* A user-supplied `output.banner` overrides the default Kubb "Generated by Kubb" notice.
|
|
416
787
|
* When no `output.banner` is set, the Kubb notice is used (including `title` and `version`
|
|
417
|
-
* from the
|
|
788
|
+
* from the document metadata when `meta` is provided).
|
|
418
789
|
*
|
|
419
|
-
* - When `output.banner` is a function
|
|
420
|
-
* - When `output.banner` is a function and `node` is absent, falls back to the Kubb notice.
|
|
790
|
+
* - When `output.banner` is a function, calls it with `meta` and returns the result.
|
|
421
791
|
* - When `output.banner` is a string, returns it directly.
|
|
422
792
|
* - When `config.output.defaultBanner` is `false`, returns `undefined`.
|
|
423
793
|
* - Otherwise returns the Kubb "Generated by Kubb" notice.
|
|
@@ -428,15 +798,15 @@ function buildDefaultBanner({ title, description, version, config }) {
|
|
|
428
798
|
* // → '// my banner'
|
|
429
799
|
* ```
|
|
430
800
|
*
|
|
431
|
-
* @example Function banner with
|
|
801
|
+
* @example Function banner with metadata
|
|
432
802
|
* ```ts
|
|
433
|
-
* defaultResolveBanner(
|
|
803
|
+
* defaultResolveBanner(meta, { output: { banner: (m) => `// v${m?.version}` }, config })
|
|
434
804
|
* // → '// v3.0.0'
|
|
435
805
|
* ```
|
|
436
806
|
*
|
|
437
807
|
* @example No user banner — Kubb notice with OAS metadata
|
|
438
808
|
* ```ts
|
|
439
|
-
* defaultResolveBanner(
|
|
809
|
+
* defaultResolveBanner(meta, { config })
|
|
440
810
|
* // → '/** Generated by Kubb ... Title: Pet Store ... *\/'
|
|
441
811
|
* ```
|
|
442
812
|
*
|
|
@@ -446,21 +816,20 @@ function buildDefaultBanner({ title, description, version, config }) {
|
|
|
446
816
|
* // → undefined
|
|
447
817
|
* ```
|
|
448
818
|
*/
|
|
449
|
-
function defaultResolveBanner(
|
|
450
|
-
if (typeof output?.banner === "function") return output.banner(
|
|
819
|
+
function defaultResolveBanner(meta, { output, config }) {
|
|
820
|
+
if (typeof output?.banner === "function") return output.banner(meta);
|
|
451
821
|
if (typeof output?.banner === "string") return output.banner;
|
|
452
822
|
if (config.output.defaultBanner === false) return;
|
|
453
823
|
return buildDefaultBanner({
|
|
454
|
-
title:
|
|
455
|
-
version:
|
|
824
|
+
title: meta?.title,
|
|
825
|
+
version: meta?.version,
|
|
456
826
|
config
|
|
457
827
|
});
|
|
458
828
|
}
|
|
459
829
|
/**
|
|
460
830
|
* Default footer resolver — returns the footer string for a generated file.
|
|
461
831
|
*
|
|
462
|
-
* - When `output.footer` is a function
|
|
463
|
-
* - When `output.footer` is a function and `node` is absent, returns `undefined`.
|
|
832
|
+
* - When `output.footer` is a function, calls it with `meta` and returns the result.
|
|
464
833
|
* - When `output.footer` is a string, returns it directly.
|
|
465
834
|
* - Otherwise returns `undefined`.
|
|
466
835
|
*
|
|
@@ -470,14 +839,14 @@ function defaultResolveBanner(node, { output, config }) {
|
|
|
470
839
|
* // → '// end of file'
|
|
471
840
|
* ```
|
|
472
841
|
*
|
|
473
|
-
* @example Function footer with
|
|
842
|
+
* @example Function footer with metadata
|
|
474
843
|
* ```ts
|
|
475
|
-
* defaultResolveFooter(
|
|
844
|
+
* defaultResolveFooter(meta, { output: { footer: (m) => `// ${m?.title}` }, config })
|
|
476
845
|
* // → '// Pet Store'
|
|
477
846
|
* ```
|
|
478
847
|
*/
|
|
479
|
-
function defaultResolveFooter(
|
|
480
|
-
if (typeof output?.footer === "function") return
|
|
848
|
+
function defaultResolveFooter(meta, { output }) {
|
|
849
|
+
if (typeof output?.footer === "function") return output.footer(meta);
|
|
481
850
|
if (typeof output?.footer === "string") return output.footer;
|
|
482
851
|
}
|
|
483
852
|
/**
|
|
@@ -690,11 +1059,11 @@ var FileManager = class {
|
|
|
690
1059
|
}
|
|
691
1060
|
};
|
|
692
1061
|
//#endregion
|
|
693
|
-
//#region src/
|
|
1062
|
+
//#region src/KubbDriver.ts
|
|
694
1063
|
function enforceOrder(enforce) {
|
|
695
1064
|
return enforce === "pre" ? -1 : enforce === "post" ? 1 : 0;
|
|
696
1065
|
}
|
|
697
|
-
var
|
|
1066
|
+
var KubbDriver = class KubbDriver {
|
|
698
1067
|
config;
|
|
699
1068
|
options;
|
|
700
1069
|
/**
|
|
@@ -702,25 +1071,34 @@ var PluginDriver = class PluginDriver {
|
|
|
702
1071
|
*
|
|
703
1072
|
* @example
|
|
704
1073
|
* ```ts
|
|
705
|
-
*
|
|
706
|
-
*
|
|
1074
|
+
* KubbDriver.getMode('src/gen/types.ts') // 'single'
|
|
1075
|
+
* KubbDriver.getMode('src/gen/types') // 'split'
|
|
707
1076
|
* ```
|
|
708
1077
|
*/
|
|
709
1078
|
static getMode(fileOrFolder) {
|
|
710
1079
|
return getMode(fileOrFolder);
|
|
711
1080
|
}
|
|
712
1081
|
/**
|
|
713
|
-
* The
|
|
714
|
-
*
|
|
1082
|
+
* The streaming `InputStreamNode` produced by the adapter.
|
|
1083
|
+
* Always set after adapter setup — parse-only adapters are wrapped automatically.
|
|
715
1084
|
*/
|
|
716
1085
|
inputNode = void 0;
|
|
1086
|
+
adapter = void 0;
|
|
717
1087
|
/**
|
|
718
|
-
*
|
|
719
|
-
*
|
|
1088
|
+
* Studio session state, kept together so `dispose()` can reset it atomically.
|
|
1089
|
+
*
|
|
1090
|
+
* - `source` holds the raw adapter source so `adapter.parse()` can be called lazily.
|
|
1091
|
+
* Intentionally outlives the build; cleared by `dispose()`.
|
|
1092
|
+
* - `isOpen` prevents opening the studio more than once per build.
|
|
1093
|
+
* - `inputNode` caches the parse promise so `adapter.parse()` is called at most once
|
|
1094
|
+
* per studio session, even when `openInStudio()` is called multiple times.
|
|
720
1095
|
*/
|
|
721
|
-
|
|
722
|
-
|
|
723
|
-
|
|
1096
|
+
#studio = {
|
|
1097
|
+
source: void 0,
|
|
1098
|
+
isOpen: false,
|
|
1099
|
+
inputNode: void 0
|
|
1100
|
+
};
|
|
1101
|
+
#middlewareListeners = [];
|
|
724
1102
|
/**
|
|
725
1103
|
* Central file store for all generated files.
|
|
726
1104
|
* Plugins should use `this.addFile()` / `this.upsertFile()` (via their context) to
|
|
@@ -732,23 +1110,29 @@ var PluginDriver = class PluginDriver {
|
|
|
732
1110
|
* Tracks which plugins have generators registered via `addGenerator()` (event-based path).
|
|
733
1111
|
* Used by the build loop to decide whether to emit generator events for a given plugin.
|
|
734
1112
|
*/
|
|
735
|
-
#
|
|
1113
|
+
#eventGeneratorPlugins = /* @__PURE__ */ new Set();
|
|
736
1114
|
#resolvers = /* @__PURE__ */ new Map();
|
|
737
1115
|
#defaultResolvers = /* @__PURE__ */ new Map();
|
|
738
1116
|
#hookListeners = /* @__PURE__ */ new Map();
|
|
739
1117
|
constructor(config, options) {
|
|
740
1118
|
this.config = config;
|
|
741
1119
|
this.options = options;
|
|
742
|
-
|
|
743
|
-
|
|
744
|
-
|
|
745
|
-
|
|
1120
|
+
this.adapter = config.adapter;
|
|
1121
|
+
}
|
|
1122
|
+
async setup() {
|
|
1123
|
+
const normalized = this.config.plugins.map((rawPlugin) => this.#normalizePlugin(rawPlugin));
|
|
1124
|
+
normalized.sort((a, b) => {
|
|
746
1125
|
if (b.dependencies?.includes(a.name)) return -1;
|
|
747
1126
|
if (a.dependencies?.includes(b.name)) return 1;
|
|
748
1127
|
return enforceOrder(a.enforce) - enforceOrder(b.enforce);
|
|
749
|
-
}).forEach((plugin) => {
|
|
750
|
-
this.plugins.set(plugin.name, plugin);
|
|
751
1128
|
});
|
|
1129
|
+
for (const plugin of normalized) {
|
|
1130
|
+
if (plugin.apply) plugin.apply(this.config);
|
|
1131
|
+
this.#registerPlugin(plugin);
|
|
1132
|
+
this.plugins.set(plugin.name, plugin);
|
|
1133
|
+
}
|
|
1134
|
+
if (this.config.middleware) for (const middleware of this.config.middleware) for (const event of Object.keys(middleware.hooks)) this.#registerMiddleware(event, middleware.hooks);
|
|
1135
|
+
if (this.config.adapter) await this.#registerAdapter(this.config.adapter);
|
|
752
1136
|
}
|
|
753
1137
|
get hooks() {
|
|
754
1138
|
return this.options.hooks;
|
|
@@ -757,19 +1141,48 @@ var PluginDriver = class PluginDriver {
|
|
|
757
1141
|
* Creates an `NormalizedPlugin` from a hook-style plugin and registers
|
|
758
1142
|
* its lifecycle handlers on the `AsyncEventEmitter`.
|
|
759
1143
|
*/
|
|
760
|
-
#normalizePlugin(
|
|
761
|
-
const
|
|
762
|
-
name:
|
|
763
|
-
dependencies:
|
|
764
|
-
enforce:
|
|
765
|
-
|
|
1144
|
+
#normalizePlugin(plugin) {
|
|
1145
|
+
const normalized = {
|
|
1146
|
+
name: plugin.name,
|
|
1147
|
+
dependencies: plugin.dependencies,
|
|
1148
|
+
enforce: plugin.enforce,
|
|
1149
|
+
hooks: plugin.hooks,
|
|
1150
|
+
options: plugin.options ?? {
|
|
766
1151
|
output: { path: "." },
|
|
767
1152
|
exclude: [],
|
|
768
1153
|
override: []
|
|
769
1154
|
}
|
|
770
1155
|
};
|
|
771
|
-
|
|
772
|
-
return
|
|
1156
|
+
if ("apply" in plugin && typeof plugin.apply === "function") normalized.apply = plugin.apply;
|
|
1157
|
+
return normalized;
|
|
1158
|
+
}
|
|
1159
|
+
async #registerAdapter(adapter) {
|
|
1160
|
+
const source = inputToAdapterSource(this.config);
|
|
1161
|
+
this.#studio.source = source;
|
|
1162
|
+
if (adapter.stream) {
|
|
1163
|
+
this.inputNode = await adapter.stream(source);
|
|
1164
|
+
await this.hooks.emit("kubb:debug", {
|
|
1165
|
+
date: /* @__PURE__ */ new Date(),
|
|
1166
|
+
logs: [`✓ Adapter '${adapter.name}' producing input stream`]
|
|
1167
|
+
});
|
|
1168
|
+
} else {
|
|
1169
|
+
const inputNode = await adapter.parse(source);
|
|
1170
|
+
this.inputNode = (0, _kubb_ast.createStreamInput)(arrayToAsyncIterable(inputNode.schemas), arrayToAsyncIterable(inputNode.operations), inputNode.meta);
|
|
1171
|
+
await this.hooks.emit("kubb:debug", {
|
|
1172
|
+
date: /* @__PURE__ */ new Date(),
|
|
1173
|
+
logs: [
|
|
1174
|
+
`✓ Adapter '${adapter.name}' resolved InputNode (wrapped as stream)`,
|
|
1175
|
+
` • Schemas: ${inputNode.schemas.length}`,
|
|
1176
|
+
` • Operations: ${inputNode.operations.length}`
|
|
1177
|
+
]
|
|
1178
|
+
});
|
|
1179
|
+
}
|
|
1180
|
+
}
|
|
1181
|
+
#registerMiddleware(event, middlewareHooks) {
|
|
1182
|
+
const handler = middlewareHooks[event];
|
|
1183
|
+
if (!handler) return;
|
|
1184
|
+
this.hooks.on(event, handler);
|
|
1185
|
+
this.#middlewareListeners.push([event, handler]);
|
|
773
1186
|
}
|
|
774
1187
|
/**
|
|
775
1188
|
* Registers a hook-style plugin's lifecycle handlers on the shared `AsyncEventEmitter`.
|
|
@@ -786,29 +1199,29 @@ var PluginDriver = class PluginDriver {
|
|
|
786
1199
|
*
|
|
787
1200
|
* @internal
|
|
788
1201
|
*/
|
|
789
|
-
|
|
790
|
-
const { hooks } =
|
|
1202
|
+
#registerPlugin(plugin) {
|
|
1203
|
+
const { hooks } = plugin;
|
|
791
1204
|
if (!hooks) return;
|
|
792
1205
|
if (hooks["kubb:plugin:setup"]) {
|
|
793
1206
|
const setupHandler = (globalCtx) => {
|
|
794
1207
|
const pluginCtx = {
|
|
795
1208
|
...globalCtx,
|
|
796
|
-
options:
|
|
1209
|
+
options: plugin.options ?? {},
|
|
797
1210
|
addGenerator: (gen) => {
|
|
798
|
-
this.registerGenerator(
|
|
1211
|
+
this.registerGenerator(plugin.name, gen);
|
|
799
1212
|
},
|
|
800
1213
|
setResolver: (resolver) => {
|
|
801
|
-
this.setPluginResolver(
|
|
1214
|
+
this.setPluginResolver(plugin.name, resolver);
|
|
802
1215
|
},
|
|
803
1216
|
setTransformer: (visitor) => {
|
|
804
|
-
|
|
1217
|
+
plugin.transformer = visitor;
|
|
805
1218
|
},
|
|
806
1219
|
setRenderer: (renderer) => {
|
|
807
|
-
|
|
1220
|
+
plugin.renderer = renderer;
|
|
808
1221
|
},
|
|
809
1222
|
setOptions: (opts) => {
|
|
810
|
-
|
|
811
|
-
...
|
|
1223
|
+
plugin.options = {
|
|
1224
|
+
...plugin.options,
|
|
812
1225
|
...opts
|
|
813
1226
|
};
|
|
814
1227
|
},
|
|
@@ -902,7 +1315,7 @@ var PluginDriver = class PluginDriver {
|
|
|
902
1315
|
this.hooks.on("kubb:generate:operations", operationsHandler);
|
|
903
1316
|
this.#trackHookListener("kubb:generate:operations", operationsHandler);
|
|
904
1317
|
}
|
|
905
|
-
this.#
|
|
1318
|
+
this.#eventGeneratorPlugins.add(pluginName);
|
|
906
1319
|
}
|
|
907
1320
|
/**
|
|
908
1321
|
* Returns `true` when at least one generator was registered for the given plugin
|
|
@@ -911,8 +1324,8 @@ var PluginDriver = class PluginDriver {
|
|
|
911
1324
|
* Used by the build loop to decide whether to walk the AST and emit generator events
|
|
912
1325
|
* for a plugin that has no static `plugin.generators`.
|
|
913
1326
|
*/
|
|
914
|
-
|
|
915
|
-
return this.#
|
|
1327
|
+
hasEventGenerators(pluginName) {
|
|
1328
|
+
return this.#eventGeneratorPlugins.has(pluginName);
|
|
916
1329
|
}
|
|
917
1330
|
/**
|
|
918
1331
|
* Unregisters all plugin lifecycle listeners from the shared event emitter.
|
|
@@ -923,12 +1336,17 @@ var PluginDriver = class PluginDriver {
|
|
|
923
1336
|
dispose() {
|
|
924
1337
|
for (const [event, handlers] of this.#hookListeners) for (const handler of handlers) this.hooks.off(event, handler);
|
|
925
1338
|
this.#hookListeners.clear();
|
|
926
|
-
this.#
|
|
1339
|
+
this.#eventGeneratorPlugins.clear();
|
|
927
1340
|
this.#resolvers.clear();
|
|
928
1341
|
this.#defaultResolvers.clear();
|
|
929
1342
|
this.fileManager.dispose();
|
|
930
1343
|
this.inputNode = void 0;
|
|
931
|
-
this
|
|
1344
|
+
this.#studio = {
|
|
1345
|
+
source: void 0,
|
|
1346
|
+
isOpen: false,
|
|
1347
|
+
inputNode: void 0
|
|
1348
|
+
};
|
|
1349
|
+
for (const [event, handler] of this.#middlewareListeners) this.hooks.off(event, handler);
|
|
932
1350
|
}
|
|
933
1351
|
[Symbol.dispose]() {
|
|
934
1352
|
this.dispose();
|
|
@@ -941,16 +1359,10 @@ var PluginDriver = class PluginDriver {
|
|
|
941
1359
|
}
|
|
942
1360
|
handlers.add(handler);
|
|
943
1361
|
}
|
|
944
|
-
#
|
|
945
|
-
|
|
946
|
-
|
|
947
|
-
|
|
948
|
-
name: "default",
|
|
949
|
-
pluginName
|
|
950
|
-
}));
|
|
951
|
-
this.#defaultResolvers.set(pluginName, resolver);
|
|
952
|
-
return resolver;
|
|
953
|
-
}
|
|
1362
|
+
#getDefaultResolver = memoize(this.#defaultResolvers, (pluginName) => defineResolver(() => ({
|
|
1363
|
+
name: "default",
|
|
1364
|
+
pluginName
|
|
1365
|
+
})));
|
|
954
1366
|
/**
|
|
955
1367
|
* Merges `partial` with the plugin's default resolver and stores the result.
|
|
956
1368
|
* Also mirrors it onto `plugin.resolver` so callers using `getPlugin(name).resolver`
|
|
@@ -958,7 +1370,7 @@ var PluginDriver = class PluginDriver {
|
|
|
958
1370
|
*/
|
|
959
1371
|
setPluginResolver(pluginName, partial) {
|
|
960
1372
|
const merged = {
|
|
961
|
-
...this.#
|
|
1373
|
+
...this.#getDefaultResolver(pluginName),
|
|
962
1374
|
...partial
|
|
963
1375
|
};
|
|
964
1376
|
this.#resolvers.set(pluginName, merged);
|
|
@@ -966,7 +1378,7 @@ var PluginDriver = class PluginDriver {
|
|
|
966
1378
|
if (plugin) plugin.resolver = merged;
|
|
967
1379
|
}
|
|
968
1380
|
getResolver(pluginName) {
|
|
969
|
-
return this.#resolvers.get(pluginName) ?? this.plugins.get(pluginName)?.resolver ?? this.#
|
|
1381
|
+
return this.#resolvers.get(pluginName) ?? this.plugins.get(pluginName)?.resolver ?? this.#getDefaultResolver(pluginName);
|
|
970
1382
|
}
|
|
971
1383
|
getContext(plugin) {
|
|
972
1384
|
const driver = this;
|
|
@@ -976,7 +1388,7 @@ var PluginDriver = class PluginDriver {
|
|
|
976
1388
|
return (0, node_path.resolve)(driver.config.root, driver.config.output.path);
|
|
977
1389
|
},
|
|
978
1390
|
getMode(output) {
|
|
979
|
-
return
|
|
1391
|
+
return KubbDriver.getMode((0, node_path.resolve)(driver.config.root, driver.config.output.path, output.path));
|
|
980
1392
|
},
|
|
981
1393
|
hooks: driver.hooks,
|
|
982
1394
|
plugin,
|
|
@@ -990,13 +1402,10 @@ var PluginDriver = class PluginDriver {
|
|
|
990
1402
|
upsertFile: async (...files) => {
|
|
991
1403
|
driver.fileManager.upsert(...files);
|
|
992
1404
|
},
|
|
993
|
-
get
|
|
994
|
-
|
|
995
|
-
|
|
996
|
-
|
|
997
|
-
schemas: [],
|
|
998
|
-
operations: [],
|
|
999
|
-
meta: driver.inputStreamNode?.meta
|
|
1405
|
+
get meta() {
|
|
1406
|
+
return driver.inputNode?.meta ?? {
|
|
1407
|
+
circularNames: [],
|
|
1408
|
+
enumNames: []
|
|
1000
1409
|
};
|
|
1001
1410
|
},
|
|
1002
1411
|
get adapter() {
|
|
@@ -1017,13 +1426,14 @@ var PluginDriver = class PluginDriver {
|
|
|
1017
1426
|
info(message) {
|
|
1018
1427
|
driver.hooks.emit("kubb:info", { message });
|
|
1019
1428
|
},
|
|
1020
|
-
openInStudio(options) {
|
|
1021
|
-
if (!driver.config.devtools || driver.#
|
|
1429
|
+
async openInStudio(options) {
|
|
1430
|
+
if (!driver.config.devtools || driver.#studio.isOpen) return;
|
|
1022
1431
|
if (typeof driver.config.devtools !== "object") throw new Error("Devtools must be an object");
|
|
1023
|
-
if (!driver.
|
|
1024
|
-
driver.#
|
|
1432
|
+
if (!driver.adapter || !driver.#studio.source) throw new Error("adapter is not defined, make sure you have set the parser in kubb.config.ts");
|
|
1433
|
+
driver.#studio.isOpen = true;
|
|
1025
1434
|
const studioUrl = driver.config.devtools?.studioUrl ?? "https://kubb.studio";
|
|
1026
|
-
|
|
1435
|
+
driver.#studio.inputNode ??= Promise.resolve(driver.adapter.parse(driver.#studio.source));
|
|
1436
|
+
return openInStudio(await driver.#studio.inputNode, studioUrl, options);
|
|
1027
1437
|
}
|
|
1028
1438
|
};
|
|
1029
1439
|
}
|
|
@@ -1070,6 +1480,22 @@ async function applyAsyncRender({ renderer, result, driver }) {
|
|
|
1070
1480
|
driver.fileManager.upsert(...renderer.files);
|
|
1071
1481
|
renderer.unmount();
|
|
1072
1482
|
}
|
|
1483
|
+
function inputToAdapterSource(config) {
|
|
1484
|
+
const input = config.input;
|
|
1485
|
+
if (!input) throw new Error("[kubb] input is required when using an adapter. Provide input.path or input.data in your config.");
|
|
1486
|
+
if ("data" in input) return {
|
|
1487
|
+
type: "data",
|
|
1488
|
+
data: input.data
|
|
1489
|
+
};
|
|
1490
|
+
if (new URLPath(input.path).isURL) return {
|
|
1491
|
+
type: "path",
|
|
1492
|
+
path: input.path
|
|
1493
|
+
};
|
|
1494
|
+
return {
|
|
1495
|
+
type: "path",
|
|
1496
|
+
path: (0, node_path.resolve)(config.root, input.path)
|
|
1497
|
+
};
|
|
1498
|
+
}
|
|
1073
1499
|
//#endregion
|
|
1074
1500
|
Object.defineProperty(exports, "DEFAULT_BANNER", {
|
|
1075
1501
|
enumerable: true,
|
|
@@ -1095,10 +1521,16 @@ Object.defineProperty(exports, "FileManager", {
|
|
|
1095
1521
|
return FileManager;
|
|
1096
1522
|
}
|
|
1097
1523
|
});
|
|
1098
|
-
Object.defineProperty(exports, "
|
|
1524
|
+
Object.defineProperty(exports, "KubbDriver", {
|
|
1525
|
+
enumerable: true,
|
|
1526
|
+
get: function() {
|
|
1527
|
+
return KubbDriver;
|
|
1528
|
+
}
|
|
1529
|
+
});
|
|
1530
|
+
Object.defineProperty(exports, "URLPath", {
|
|
1099
1531
|
enumerable: true,
|
|
1100
1532
|
get: function() {
|
|
1101
|
-
return
|
|
1533
|
+
return URLPath;
|
|
1102
1534
|
}
|
|
1103
1535
|
});
|
|
1104
1536
|
Object.defineProperty(exports, "__name", {
|
|
@@ -1119,12 +1551,6 @@ Object.defineProperty(exports, "applyHookResult", {
|
|
|
1119
1551
|
return applyHookResult;
|
|
1120
1552
|
}
|
|
1121
1553
|
});
|
|
1122
|
-
Object.defineProperty(exports, "camelCase", {
|
|
1123
|
-
enumerable: true,
|
|
1124
|
-
get: function() {
|
|
1125
|
-
return camelCase;
|
|
1126
|
-
}
|
|
1127
|
-
});
|
|
1128
1554
|
Object.defineProperty(exports, "definePlugin", {
|
|
1129
1555
|
enumerable: true,
|
|
1130
1556
|
get: function() {
|
|
@@ -1137,11 +1563,29 @@ Object.defineProperty(exports, "defineResolver", {
|
|
|
1137
1563
|
return defineResolver;
|
|
1138
1564
|
}
|
|
1139
1565
|
});
|
|
1566
|
+
Object.defineProperty(exports, "forBatches", {
|
|
1567
|
+
enumerable: true,
|
|
1568
|
+
get: function() {
|
|
1569
|
+
return forBatches;
|
|
1570
|
+
}
|
|
1571
|
+
});
|
|
1572
|
+
Object.defineProperty(exports, "isPromise", {
|
|
1573
|
+
enumerable: true,
|
|
1574
|
+
get: function() {
|
|
1575
|
+
return isPromise;
|
|
1576
|
+
}
|
|
1577
|
+
});
|
|
1140
1578
|
Object.defineProperty(exports, "logLevel", {
|
|
1141
1579
|
enumerable: true,
|
|
1142
1580
|
get: function() {
|
|
1143
1581
|
return logLevel;
|
|
1144
1582
|
}
|
|
1145
1583
|
});
|
|
1584
|
+
Object.defineProperty(exports, "withDrain", {
|
|
1585
|
+
enumerable: true,
|
|
1586
|
+
get: function() {
|
|
1587
|
+
return withDrain;
|
|
1588
|
+
}
|
|
1589
|
+
});
|
|
1146
1590
|
|
|
1147
|
-
//# sourceMappingURL=
|
|
1591
|
+
//# sourceMappingURL=KubbDriver-BXSnJ3qM.cjs.map
|