@kubb/adapter-oas 5.0.0-beta.54 → 5.0.0-beta.56
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.cjs +388 -255
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.ts +767 -10
- package/dist/index.js +388 -252
- package/dist/index.js.map +1 -1
- package/package.json +7 -7
- package/src/adapter.ts +13 -21
- package/src/bundler.ts +2 -2
- package/src/constants.ts +6 -0
- package/src/factory.ts +21 -25
- package/src/mime.ts +22 -0
- package/src/operation.ts +194 -0
- package/src/parser.ts +42 -30
- package/src/resolvers.ts +8 -9
- package/src/stream.ts +15 -21
- package/src/types.ts +46 -19
package/dist/index.cjs
CHANGED
|
@@ -22,19 +22,14 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
|
|
|
22
22
|
}) : target, mod));
|
|
23
23
|
//#endregion
|
|
24
24
|
let _kubb_core = require("@kubb/core");
|
|
25
|
-
let oas = require("oas");
|
|
26
|
-
oas = __toESM(oas, 1);
|
|
27
25
|
let node_path = require("node:path");
|
|
28
26
|
node_path = __toESM(node_path, 1);
|
|
29
27
|
let node_fs_promises = require("node:fs/promises");
|
|
30
|
-
let
|
|
31
|
-
|
|
28
|
+
let _readme_openapi_parser = require("@readme/openapi-parser");
|
|
29
|
+
let yaml = require("yaml");
|
|
32
30
|
let api_ref_bundler = require("api-ref-bundler");
|
|
33
|
-
let js_yaml = require("js-yaml");
|
|
34
|
-
js_yaml = __toESM(js_yaml, 1);
|
|
35
31
|
let _kubb_ast_utils = require("@kubb/ast/utils");
|
|
36
|
-
let
|
|
37
|
-
let oas_utils = require("oas/utils");
|
|
32
|
+
let _kubb_ast = require("@kubb/ast");
|
|
38
33
|
//#region src/constants.ts
|
|
39
34
|
/**
|
|
40
35
|
* Default parser options applied when no explicit options are provided.
|
|
@@ -66,6 +61,20 @@ const DEFAULT_PARSER_OPTIONS = {
|
|
|
66
61
|
*/
|
|
67
62
|
const SCHEMA_REF_PREFIX = "#/components/schemas/";
|
|
68
63
|
/**
|
|
64
|
+
* HTTP methods that count as operations on an OpenAPI path item. Other keys
|
|
65
|
+
* (`parameters`, `summary`, `$ref`, vendor extensions) are skipped when iterating operations.
|
|
66
|
+
*/
|
|
67
|
+
const SUPPORTED_METHODS = new Set([
|
|
68
|
+
"get",
|
|
69
|
+
"put",
|
|
70
|
+
"post",
|
|
71
|
+
"delete",
|
|
72
|
+
"options",
|
|
73
|
+
"head",
|
|
74
|
+
"patch",
|
|
75
|
+
"trace"
|
|
76
|
+
]);
|
|
77
|
+
/**
|
|
69
78
|
* OpenAPI version string written into the stub document created during multi-spec merges.
|
|
70
79
|
*/
|
|
71
80
|
const MERGE_OPENAPI_VERSION = "3.0.0";
|
|
@@ -178,77 +187,103 @@ const typeOptionMap = new Map([
|
|
|
178
187
|
function toCamelOrPascal(text, pascal) {
|
|
179
188
|
return text.trim().replace(/([a-z\d])([A-Z])/g, "$1 $2").replace(/([A-Z]+)([A-Z][a-z])/g, "$1 $2").replace(/(\d)([a-z])/g, "$1 $2").split(/[\s\-_./\\:]+/).filter(Boolean).map((word, i) => {
|
|
180
189
|
if (word.length > 1 && word === word.toUpperCase()) return word;
|
|
181
|
-
|
|
182
|
-
return word.charAt(0).toUpperCase() + word.slice(1);
|
|
190
|
+
return (i === 0 && !pascal ? word.charAt(0).toLowerCase() : word.charAt(0).toUpperCase()) + word.slice(1);
|
|
183
191
|
}).join("").replace(/[^a-zA-Z0-9]/g, "");
|
|
184
192
|
}
|
|
185
193
|
/**
|
|
186
|
-
*
|
|
187
|
-
* The last segment receives `isLast = true`, all earlier segments receive `false`.
|
|
188
|
-
* Segments are joined with `/` to form a file path.
|
|
194
|
+
* Converts `text` to camelCase.
|
|
189
195
|
*
|
|
190
|
-
*
|
|
191
|
-
*
|
|
196
|
+
* @example Word boundaries
|
|
197
|
+
* `camelCase('hello-world') // 'helloWorld'`
|
|
192
198
|
*
|
|
193
|
-
*
|
|
194
|
-
*
|
|
195
|
-
* and `'..'` transforms to an empty string). Without this filter the join would produce
|
|
196
|
-
* a leading `/`, which `path.resolve` would interpret as an absolute path, allowing
|
|
197
|
-
* generated files to escape the configured output directory.
|
|
199
|
+
* @example With a prefix
|
|
200
|
+
* `camelCase('tag', { prefix: 'create' }) // 'createTag'`
|
|
198
201
|
*/
|
|
199
|
-
function
|
|
200
|
-
const parts = text.split(/\.(?=[a-zA-Z])/);
|
|
201
|
-
return parts.map((part, i) => transformPart(part, i === parts.length - 1)).filter(Boolean).join("/");
|
|
202
|
-
}
|
|
203
|
-
/**
|
|
204
|
-
* Converts `text` to camelCase.
|
|
205
|
-
* When `isFile` is `true`, dot-separated segments are each cased independently and joined with `/`.
|
|
206
|
-
*
|
|
207
|
-
* @example
|
|
208
|
-
* camelCase('hello-world') // 'helloWorld'
|
|
209
|
-
* camelCase('pet.petId', { isFile: true }) // 'pet/petId'
|
|
210
|
-
*/
|
|
211
|
-
function camelCase(text, { isFile, prefix = "", suffix = "" } = {}) {
|
|
212
|
-
if (isFile) return applyToFileParts(text, (part, isLast) => camelCase(part, isLast ? {
|
|
213
|
-
prefix,
|
|
214
|
-
suffix
|
|
215
|
-
} : {}));
|
|
202
|
+
function camelCase(text, { prefix = "", suffix = "" } = {}) {
|
|
216
203
|
return toCamelOrPascal(`${prefix} ${text} ${suffix}`, false);
|
|
217
204
|
}
|
|
218
205
|
/**
|
|
219
206
|
* Converts `text` to PascalCase.
|
|
220
|
-
* When `isFile` is `true`, the last dot-separated segment is PascalCased and earlier segments are camelCased.
|
|
221
207
|
*
|
|
222
|
-
* @example
|
|
223
|
-
* pascalCase('hello-world')
|
|
224
|
-
*
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
suffix
|
|
230
|
-
}) : camelCase(part));
|
|
208
|
+
* @example Word boundaries
|
|
209
|
+
* `pascalCase('hello-world') // 'HelloWorld'`
|
|
210
|
+
*
|
|
211
|
+
* @example With a suffix
|
|
212
|
+
* `pascalCase('tag', { suffix: 'schema' }) // 'TagSchema'`
|
|
213
|
+
*/
|
|
214
|
+
function pascalCase(text, { prefix = "", suffix = "" } = {}) {
|
|
231
215
|
return toCamelOrPascal(`${prefix} ${text} ${suffix}`, true);
|
|
232
216
|
}
|
|
233
217
|
//#endregion
|
|
234
218
|
//#region ../../internals/utils/src/runtime.ts
|
|
235
219
|
/**
|
|
236
|
-
*
|
|
237
|
-
*
|
|
238
|
-
* Detection keys off the global `Bun` object rather than `process.versions`,
|
|
239
|
-
* because Bun polyfills `process.versions.node` for Node compatibility and would
|
|
240
|
-
* otherwise look like Node.
|
|
220
|
+
* Detects the JavaScript runtime executing the current process and exposes its name and version.
|
|
241
221
|
*
|
|
242
|
-
* @
|
|
243
|
-
* ```ts
|
|
244
|
-
* if (isBun()) {
|
|
245
|
-
* await Bun.write(path, data)
|
|
246
|
-
* }
|
|
247
|
-
* ```
|
|
222
|
+
* Prefer the shared {@link runtime} instance over constructing your own.
|
|
248
223
|
*/
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
224
|
+
var Runtime = class {
|
|
225
|
+
/**
|
|
226
|
+
* `true` when the current process is running under Bun.
|
|
227
|
+
*
|
|
228
|
+
* Detection keys off the global `Bun` object rather than `process.versions`,
|
|
229
|
+
* because Bun polyfills `process.versions.node` for Node compatibility and would
|
|
230
|
+
* otherwise look like Node.
|
|
231
|
+
*
|
|
232
|
+
* @example
|
|
233
|
+
* ```ts
|
|
234
|
+
* if (runtime.isBun) {
|
|
235
|
+
* await Bun.write(path, data)
|
|
236
|
+
* }
|
|
237
|
+
* ```
|
|
238
|
+
*/
|
|
239
|
+
get isBun() {
|
|
240
|
+
return typeof Bun !== "undefined";
|
|
241
|
+
}
|
|
242
|
+
/**
|
|
243
|
+
* `true` when the current process is running under Deno.
|
|
244
|
+
*/
|
|
245
|
+
get isDeno() {
|
|
246
|
+
return typeof globalThis.Deno !== "undefined";
|
|
247
|
+
}
|
|
248
|
+
/**
|
|
249
|
+
* `true` when the current process is running under Node.
|
|
250
|
+
*
|
|
251
|
+
* Bun and Deno are excluded first so a polyfilled `process` does not register as Node.
|
|
252
|
+
*/
|
|
253
|
+
get isNode() {
|
|
254
|
+
return !this.isBun && !this.isDeno && typeof process !== "undefined" && process.versions?.node != null;
|
|
255
|
+
}
|
|
256
|
+
/**
|
|
257
|
+
* Name of the runtime executing the current process.
|
|
258
|
+
*
|
|
259
|
+
* @example
|
|
260
|
+
* ```ts
|
|
261
|
+
* runtime.name // 'bun' when run with `bun kubb`, 'node' otherwise
|
|
262
|
+
* ```
|
|
263
|
+
*/
|
|
264
|
+
get name() {
|
|
265
|
+
if (this.isBun) return "bun";
|
|
266
|
+
if (this.isDeno) return "deno";
|
|
267
|
+
return "node";
|
|
268
|
+
}
|
|
269
|
+
/**
|
|
270
|
+
* Version of the active runtime, or an empty string when it cannot be read.
|
|
271
|
+
*
|
|
272
|
+
* @example
|
|
273
|
+
* ```ts
|
|
274
|
+
* runtime.version // '1.3.11' under Bun, '22.22.2' under Node
|
|
275
|
+
* ```
|
|
276
|
+
*/
|
|
277
|
+
get version() {
|
|
278
|
+
if (this.isBun) return process.versions.bun ?? "";
|
|
279
|
+
if (this.isDeno) return globalThis.Deno?.version?.deno ?? "";
|
|
280
|
+
return process.versions?.node ?? "";
|
|
281
|
+
}
|
|
282
|
+
};
|
|
283
|
+
/**
|
|
284
|
+
* Shared {@link Runtime} instance describing the JavaScript runtime executing the current process.
|
|
285
|
+
*/
|
|
286
|
+
const runtime = new Runtime();
|
|
252
287
|
//#endregion
|
|
253
288
|
//#region ../../internals/utils/src/fs.ts
|
|
254
289
|
/**
|
|
@@ -263,7 +298,7 @@ function isBun() {
|
|
|
263
298
|
* ```
|
|
264
299
|
*/
|
|
265
300
|
async function exists(path) {
|
|
266
|
-
if (isBun
|
|
301
|
+
if (runtime.isBun) return Bun.file(path).exists();
|
|
267
302
|
return (0, node_fs_promises.access)(path).then(() => true, () => false);
|
|
268
303
|
}
|
|
269
304
|
/**
|
|
@@ -276,7 +311,7 @@ async function exists(path) {
|
|
|
276
311
|
* ```
|
|
277
312
|
*/
|
|
278
313
|
async function read(path) {
|
|
279
|
-
if (isBun
|
|
314
|
+
if (runtime.isBun) return Bun.file(path).text();
|
|
280
315
|
return (0, node_fs_promises.readFile)(path, { encoding: "utf8" });
|
|
281
316
|
}
|
|
282
317
|
//#endregion
|
|
@@ -433,99 +468,80 @@ function isIdentifier(name) {
|
|
|
433
468
|
return /^[a-zA-Z_$][a-zA-Z0-9_$]*$/.test(name);
|
|
434
469
|
}
|
|
435
470
|
//#endregion
|
|
436
|
-
//#region ../../internals/utils/src/
|
|
471
|
+
//#region ../../internals/utils/src/url.ts
|
|
472
|
+
function transformParam(raw, casing) {
|
|
473
|
+
const param = isValidVarName(raw) ? raw : camelCase(raw);
|
|
474
|
+
return casing === "camelcase" ? camelCase(param) : param;
|
|
475
|
+
}
|
|
476
|
+
function toParamsObject(path, { replacer, casing } = {}) {
|
|
477
|
+
const params = {};
|
|
478
|
+
for (const match of path.matchAll(/\{([^}]+)\}/g)) {
|
|
479
|
+
const param = transformParam(match[1], casing);
|
|
480
|
+
const key = replacer ? replacer(param) : param;
|
|
481
|
+
params[key] = key;
|
|
482
|
+
}
|
|
483
|
+
return Object.keys(params).length > 0 ? params : null;
|
|
484
|
+
}
|
|
437
485
|
/**
|
|
438
|
-
*
|
|
439
|
-
*
|
|
440
|
-
* @example
|
|
441
|
-
* const p = new URLPath('/pet/{petId}')
|
|
442
|
-
* p.URL // '/pet/:petId'
|
|
443
|
-
* p.template // '`/pet/${petId}`'
|
|
486
|
+
* Helpers for OpenAPI/Swagger paths, plus a thin wrapper over the native `URL`.
|
|
444
487
|
*/
|
|
445
|
-
var
|
|
488
|
+
var Url = class Url {
|
|
446
489
|
/**
|
|
447
|
-
*
|
|
448
|
-
*/
|
|
449
|
-
path;
|
|
450
|
-
#options;
|
|
451
|
-
constructor(path, options = {}) {
|
|
452
|
-
this.path = path;
|
|
453
|
-
this.#options = options;
|
|
454
|
-
}
|
|
455
|
-
/** Converts the OpenAPI path to Express-style colon syntax, e.g. `/pet/{petId}` → `/pet/:petId`.
|
|
490
|
+
* Reports whether `url` is a parseable absolute URL. Delegates to the native `URL.canParse`.
|
|
456
491
|
*
|
|
457
492
|
* @example
|
|
458
|
-
*
|
|
459
|
-
*
|
|
460
|
-
* ```
|
|
493
|
+
* Url.canParse('https://petstore.swagger.io/v2') // true
|
|
494
|
+
* Url.canParse('/pet/{petId}') // false
|
|
461
495
|
*/
|
|
462
|
-
|
|
463
|
-
return
|
|
496
|
+
static canParse(url, base) {
|
|
497
|
+
return URL.canParse(url, base);
|
|
464
498
|
}
|
|
465
|
-
/**
|
|
499
|
+
/**
|
|
500
|
+
* Converts an OpenAPI/Swagger path to Express-style colon syntax.
|
|
466
501
|
*
|
|
467
502
|
* @example
|
|
468
|
-
*
|
|
469
|
-
* new URLPath('https://petstore.swagger.io/v2/pet').isURL // true
|
|
470
|
-
* new URLPath('/pet/{petId}').isURL // false
|
|
471
|
-
* ```
|
|
503
|
+
* Url.toPath('/pet/{petId}') // '/pet/:petId'
|
|
472
504
|
*/
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
return !!new URL(this.path).href;
|
|
476
|
-
} catch {
|
|
477
|
-
return false;
|
|
478
|
-
}
|
|
505
|
+
static toPath(path) {
|
|
506
|
+
return path.replace(/\{([^}]+)\}/g, ":$1");
|
|
479
507
|
}
|
|
480
508
|
/**
|
|
481
|
-
* Converts
|
|
509
|
+
* Converts an OpenAPI/Swagger path to a TypeScript template literal string.
|
|
510
|
+
* `prefix` is prepended inside the literal, `replacer` transforms each parameter name,
|
|
511
|
+
* and `casing` controls parameter identifier casing.
|
|
482
512
|
*
|
|
483
513
|
* @example
|
|
484
|
-
*
|
|
485
|
-
* new URLPath('/account/monetary-accountID').template // '`/account/${monetaryAccountId}`'
|
|
486
|
-
*/
|
|
487
|
-
get template() {
|
|
488
|
-
return this.toTemplateString();
|
|
489
|
-
}
|
|
490
|
-
/** Returns the path and its extracted params as a structured `URLObject`, or as a stringified expression when `stringify` is set.
|
|
514
|
+
* Url.toTemplateString('/pet/{petId}') // '`/pet/${petId}`'
|
|
491
515
|
*
|
|
492
516
|
* @example
|
|
493
|
-
*
|
|
494
|
-
* new URLPath('/pet/{petId}').object
|
|
495
|
-
* // { url: '/pet/:petId', params: { petId: 'petId' } }
|
|
496
|
-
* ```
|
|
517
|
+
* Url.toTemplateString('/pet/{petId}', { prefix: 'https://api' }) // '`https://api/pet/${petId}`'
|
|
497
518
|
*/
|
|
498
|
-
|
|
499
|
-
|
|
519
|
+
static toTemplateString(path, { prefix, replacer, casing } = {}) {
|
|
520
|
+
const result = path.split(/\{([^}]+)\}/).map((part, i) => {
|
|
521
|
+
if (i % 2 === 0) return part;
|
|
522
|
+
const param = transformParam(part, casing);
|
|
523
|
+
return `\${${replacer ? replacer(param) : param}}`;
|
|
524
|
+
}).join("");
|
|
525
|
+
return `\`${prefix ?? ""}${result}\``;
|
|
500
526
|
}
|
|
501
|
-
/**
|
|
527
|
+
/**
|
|
528
|
+
* Returns the path and its extracted params as a structured `URLObject`, or as a stringified
|
|
529
|
+
* expression when `stringify` is set.
|
|
502
530
|
*
|
|
503
531
|
* @example
|
|
504
|
-
*
|
|
505
|
-
*
|
|
506
|
-
* new URLPath('/pet').params // undefined
|
|
507
|
-
* ```
|
|
508
|
-
*/
|
|
509
|
-
get params() {
|
|
510
|
-
return this.getParams();
|
|
511
|
-
}
|
|
512
|
-
#transformParam(raw) {
|
|
513
|
-
const param = isValidVarName(raw) ? raw : camelCase(raw);
|
|
514
|
-
return this.#options.casing === "camelcase" ? camelCase(param) : param;
|
|
515
|
-
}
|
|
516
|
-
/**
|
|
517
|
-
* Iterates over every `{param}` token in `path`, calling `fn` with the raw token and transformed name.
|
|
532
|
+
* Url.toObject('/pet/{petId}')
|
|
533
|
+
* // { url: '/pet/:petId', params: { petId: 'petId' } }
|
|
518
534
|
*/
|
|
519
|
-
|
|
520
|
-
for (const match of this.path.matchAll(/\{([^}]+)\}/g)) {
|
|
521
|
-
const raw = match[1];
|
|
522
|
-
fn(raw, this.#transformParam(raw));
|
|
523
|
-
}
|
|
524
|
-
}
|
|
525
|
-
toObject({ type = "path", replacer, stringify } = {}) {
|
|
535
|
+
static toObject(path, { type = "path", replacer, stringify, casing } = {}) {
|
|
526
536
|
const object = {
|
|
527
|
-
url: type === "path" ?
|
|
528
|
-
|
|
537
|
+
url: type === "path" ? Url.toPath(path) : Url.toTemplateString(path, {
|
|
538
|
+
replacer,
|
|
539
|
+
casing
|
|
540
|
+
}),
|
|
541
|
+
params: toParamsObject(path, {
|
|
542
|
+
replacer,
|
|
543
|
+
casing
|
|
544
|
+
})
|
|
529
545
|
};
|
|
530
546
|
if (stringify) {
|
|
531
547
|
if (type === "template") return JSON.stringify(object).replaceAll("'", "").replaceAll(`"`, "");
|
|
@@ -534,50 +550,6 @@ var URLPath = class {
|
|
|
534
550
|
}
|
|
535
551
|
return object;
|
|
536
552
|
}
|
|
537
|
-
/**
|
|
538
|
-
* Converts the OpenAPI path to a TypeScript template literal string.
|
|
539
|
-
* An optional `replacer` can transform each extracted parameter name before interpolation.
|
|
540
|
-
*
|
|
541
|
-
* @example
|
|
542
|
-
* new URLPath('/pet/{petId}').toTemplateString() // '`/pet/${petId}`'
|
|
543
|
-
*/
|
|
544
|
-
toTemplateString({ prefix, replacer } = {}) {
|
|
545
|
-
const result = this.path.split(/\{([^}]+)\}/).map((part, i) => {
|
|
546
|
-
if (i % 2 === 0) return part;
|
|
547
|
-
const param = this.#transformParam(part);
|
|
548
|
-
return `\${${replacer ? replacer(param) : param}}`;
|
|
549
|
-
}).join("");
|
|
550
|
-
return `\`${prefix ?? ""}${result}\``;
|
|
551
|
-
}
|
|
552
|
-
/**
|
|
553
|
-
* Extracts all `{param}` segments from the path and returns them as a key-value map.
|
|
554
|
-
* An optional `replacer` transforms each parameter name in both key and value positions.
|
|
555
|
-
* Returns `undefined` when no path parameters are found.
|
|
556
|
-
*
|
|
557
|
-
* @example
|
|
558
|
-
* ```ts
|
|
559
|
-
* new URLPath('/pet/{petId}/tag/{tagId}').getParams()
|
|
560
|
-
* // { petId: 'petId', tagId: 'tagId' }
|
|
561
|
-
* ```
|
|
562
|
-
*/
|
|
563
|
-
getParams(replacer) {
|
|
564
|
-
const params = {};
|
|
565
|
-
this.#eachParam((_raw, param) => {
|
|
566
|
-
const key = replacer ? replacer(param) : param;
|
|
567
|
-
params[key] = key;
|
|
568
|
-
});
|
|
569
|
-
return Object.keys(params).length > 0 ? params : void 0;
|
|
570
|
-
}
|
|
571
|
-
/** Converts the OpenAPI path to Express-style colon syntax.
|
|
572
|
-
*
|
|
573
|
-
* @example
|
|
574
|
-
* ```ts
|
|
575
|
-
* new URLPath('/pet/{petId}').toURLPath() // '/pet/:petId'
|
|
576
|
-
* ```
|
|
577
|
-
*/
|
|
578
|
-
toURLPath() {
|
|
579
|
-
return this.path.replace(/\{([^}]+)\}/g, ":$1");
|
|
580
|
-
}
|
|
581
553
|
};
|
|
582
554
|
//#endregion
|
|
583
555
|
//#region src/bundler.ts
|
|
@@ -594,7 +566,7 @@ async function readSource(sourcePath) {
|
|
|
594
566
|
async function resolveSource(sourcePath) {
|
|
595
567
|
const data = await readSource(sourcePath);
|
|
596
568
|
if (sourcePath.toLowerCase().endsWith(".md")) return data;
|
|
597
|
-
return
|
|
569
|
+
return (0, yaml.parse)(data);
|
|
598
570
|
}
|
|
599
571
|
/**
|
|
600
572
|
* Bundles a multi-file OpenAPI document into a single document via `api-ref-bundler`.
|
|
@@ -699,15 +671,9 @@ function isDiscriminator(obj) {
|
|
|
699
671
|
* const document = await parse(rawDocumentObject, { canBundle: false })
|
|
700
672
|
* ```
|
|
701
673
|
*/
|
|
702
|
-
async function parseDocument(pathOrApi, { canBundle = true
|
|
703
|
-
if (typeof pathOrApi === "string" && canBundle) return parseDocument(await bundleDocument(pathOrApi), {
|
|
704
|
-
|
|
705
|
-
enablePaths
|
|
706
|
-
});
|
|
707
|
-
const document = await new oas_normalize.default(pathOrApi, {
|
|
708
|
-
enablePaths,
|
|
709
|
-
colorizeErrors: true
|
|
710
|
-
}).load();
|
|
674
|
+
async function parseDocument(pathOrApi, { canBundle = true } = {}) {
|
|
675
|
+
if (typeof pathOrApi === "string" && canBundle) return parseDocument(await bundleDocument(pathOrApi), { canBundle: false });
|
|
676
|
+
const document = typeof pathOrApi === "string" ? (0, yaml.parse)(pathOrApi) : pathOrApi;
|
|
711
677
|
if (isOpenApiV2Document(document)) {
|
|
712
678
|
const { default: swagger2openapi } = await import("swagger2openapi");
|
|
713
679
|
const { openapi } = await swagger2openapi.convertObj(document, { anchors: true });
|
|
@@ -718,7 +684,7 @@ async function parseDocument(pathOrApi, { canBundle = true, enablePaths = true }
|
|
|
718
684
|
/**
|
|
719
685
|
* Deep-merges multiple OpenAPI documents into a single `Document`.
|
|
720
686
|
*
|
|
721
|
-
* Each document is parsed independently then
|
|
687
|
+
* Each document is parsed independently, then deep-merged into one in array order.
|
|
722
688
|
* Throws when the input array is empty.
|
|
723
689
|
*
|
|
724
690
|
* @example
|
|
@@ -727,10 +693,7 @@ async function parseDocument(pathOrApi, { canBundle = true, enablePaths = true }
|
|
|
727
693
|
* ```
|
|
728
694
|
*/
|
|
729
695
|
async function mergeDocuments(pathOrApi) {
|
|
730
|
-
const documents = await Promise.all(pathOrApi.map((p) => parseDocument(p, {
|
|
731
|
-
enablePaths: false,
|
|
732
|
-
canBundle: false
|
|
733
|
-
})));
|
|
696
|
+
const documents = await Promise.all(pathOrApi.map((p) => parseDocument(p, { canBundle: false })));
|
|
734
697
|
if (documents.length === 0) throw new _kubb_core.Diagnostics.Error({
|
|
735
698
|
code: _kubb_core.Diagnostics.code.inputRequired,
|
|
736
699
|
severity: "error",
|
|
@@ -769,7 +732,7 @@ async function parseFromConfig(source) {
|
|
|
769
732
|
return parseDocument(source.data, { canBundle: false });
|
|
770
733
|
}
|
|
771
734
|
if (source.type === "paths") return mergeDocuments(source.paths);
|
|
772
|
-
if (
|
|
735
|
+
if (Url.canParse(source.path)) return parseDocument(source.path);
|
|
773
736
|
const resolved = node_path.default.resolve(node_path.default.dirname(source.path), source.path);
|
|
774
737
|
await assertInputExists(resolved);
|
|
775
738
|
return parseDocument(resolved);
|
|
@@ -780,7 +743,7 @@ async function parseFromConfig(source) {
|
|
|
780
743
|
* its parse error instead.
|
|
781
744
|
*/
|
|
782
745
|
async function assertInputExists(input) {
|
|
783
|
-
if (
|
|
746
|
+
if (Url.canParse(input)) return;
|
|
784
747
|
if (!await exists(input)) throw new _kubb_core.Diagnostics.Error({
|
|
785
748
|
code: _kubb_core.Diagnostics.code.inputNotFound,
|
|
786
749
|
severity: "error",
|
|
@@ -790,7 +753,7 @@ async function assertInputExists(input) {
|
|
|
790
753
|
});
|
|
791
754
|
}
|
|
792
755
|
/**
|
|
793
|
-
* Validates an OpenAPI document using
|
|
756
|
+
* Validates an OpenAPI document using `@readme/openapi-parser` with colorized error output.
|
|
794
757
|
*
|
|
795
758
|
* @example
|
|
796
759
|
* ```ts
|
|
@@ -799,10 +762,8 @@ async function assertInputExists(input) {
|
|
|
799
762
|
*/
|
|
800
763
|
async function validateDocument(document, { throwOnError = false } = {}) {
|
|
801
764
|
try {
|
|
802
|
-
await
|
|
803
|
-
|
|
804
|
-
colorizeErrors: true
|
|
805
|
-
}).validate({ parser: { validate: { errors: { colorize: true } } } });
|
|
765
|
+
const result = await (0, _readme_openapi_parser.validate)(structuredClone(document), { validate: { errors: { colorize: true } } });
|
|
766
|
+
if (!result.valid) throw new Error((0, _readme_openapi_parser.compileErrors)(result));
|
|
806
767
|
} catch (error) {
|
|
807
768
|
if (throwOnError) throw error;
|
|
808
769
|
}
|
|
@@ -902,6 +863,161 @@ const oasDialect = _kubb_core.ast.defineSchemaDialect({
|
|
|
902
863
|
resolveRef
|
|
903
864
|
});
|
|
904
865
|
//#endregion
|
|
866
|
+
//#region src/mime.ts
|
|
867
|
+
/**
|
|
868
|
+
* MIME type fragments that mark a media type as JSON-like.
|
|
869
|
+
*
|
|
870
|
+
* Matches the substrings used by the `oas` library's `matchesMimeType.json`, so a content type is
|
|
871
|
+
* JSON when it contains any of these. The `+json` entry catches structured-syntax suffixes such as
|
|
872
|
+
* `application/vnd.api+json`.
|
|
873
|
+
*/
|
|
874
|
+
const jsonMimeFragments = [
|
|
875
|
+
"application/json",
|
|
876
|
+
"application/x-json",
|
|
877
|
+
"text/json",
|
|
878
|
+
"text/x-json",
|
|
879
|
+
"+json"
|
|
880
|
+
];
|
|
881
|
+
/**
|
|
882
|
+
* Returns `true` when a media type string is JSON-like.
|
|
883
|
+
*
|
|
884
|
+
* @example
|
|
885
|
+
* ```ts
|
|
886
|
+
* isJsonMimeType('application/json') // true
|
|
887
|
+
* isJsonMimeType('application/vnd.api+json') // true
|
|
888
|
+
* isJsonMimeType('multipart/form-data') // false
|
|
889
|
+
* ```
|
|
890
|
+
*/
|
|
891
|
+
function isJsonMimeType(mimeType) {
|
|
892
|
+
return jsonMimeFragments.some((fragment) => mimeType.includes(fragment));
|
|
893
|
+
}
|
|
894
|
+
//#endregion
|
|
895
|
+
//#region src/operation.ts
|
|
896
|
+
/**
|
|
897
|
+
* Slugifies a path for the `operationId` fallback: non-alphanumerics collapse to single dashes,
|
|
898
|
+
* with no leading or trailing dash.
|
|
899
|
+
*/
|
|
900
|
+
function slugify(value) {
|
|
901
|
+
return value.replace(/[^a-zA-Z0-9]/g, "-").replace(/-{2,}/g, "-").replace(/^-|-$/g, "");
|
|
902
|
+
}
|
|
903
|
+
/**
|
|
904
|
+
* Returns the operation's `operationId`, falling back to `<method>_<slugified-path>` when absent.
|
|
905
|
+
*/
|
|
906
|
+
function getOperationId({ path, method, schema }) {
|
|
907
|
+
const { operationId } = schema;
|
|
908
|
+
if (typeof operationId === "string" && operationId.length > 0) return operationId;
|
|
909
|
+
return `${method}_${slugify(path).toLowerCase()}`;
|
|
910
|
+
}
|
|
911
|
+
/**
|
|
912
|
+
* Returns the declared response status codes, skipping `x-` extensions and non-object entries.
|
|
913
|
+
*/
|
|
914
|
+
function getResponseStatusCodes({ schema }) {
|
|
915
|
+
const responses = schema.responses;
|
|
916
|
+
if (!responses || isReference(responses)) return [];
|
|
917
|
+
return Object.keys(responses).filter((key) => !key.startsWith("x-") && !!responses[key] && typeof responses[key] === "object");
|
|
918
|
+
}
|
|
919
|
+
/**
|
|
920
|
+
* Returns the response object for a status code, resolving a `$ref` in place. `false` when absent.
|
|
921
|
+
*/
|
|
922
|
+
function getResponseByStatusCode({ document, operation, statusCode }) {
|
|
923
|
+
const responses = operation.schema.responses;
|
|
924
|
+
if (!responses || isReference(responses)) return false;
|
|
925
|
+
const response = responses[statusCode];
|
|
926
|
+
if (!response) return false;
|
|
927
|
+
if (isReference(response)) {
|
|
928
|
+
const resolved = resolveRef(document, response.$ref);
|
|
929
|
+
responses[statusCode] = resolved;
|
|
930
|
+
if (!resolved || isReference(resolved)) return false;
|
|
931
|
+
return resolved;
|
|
932
|
+
}
|
|
933
|
+
return response;
|
|
934
|
+
}
|
|
935
|
+
/**
|
|
936
|
+
* Resolves the request body (dereferencing a `$ref` in place) and returns its content map, or
|
|
937
|
+
* `undefined` when the operation has no request body.
|
|
938
|
+
*/
|
|
939
|
+
function getRequestBodyContent({ document, operation }) {
|
|
940
|
+
const { schema } = operation;
|
|
941
|
+
let requestBody = schema.requestBody;
|
|
942
|
+
if (!requestBody) return;
|
|
943
|
+
if (isReference(requestBody)) {
|
|
944
|
+
const resolved = resolveRef(document, requestBody.$ref);
|
|
945
|
+
schema.requestBody = resolved;
|
|
946
|
+
if (!resolved || isReference(resolved)) return;
|
|
947
|
+
requestBody = resolved;
|
|
948
|
+
}
|
|
949
|
+
return requestBody.content;
|
|
950
|
+
}
|
|
951
|
+
/**
|
|
952
|
+
* Returns the request body media type. With `mediaType` set, returns that entry or `false`.
|
|
953
|
+
* Otherwise picks the first JSON-like media type, then the first declared one, as a
|
|
954
|
+
* `[mediaType, object]` tuple.
|
|
955
|
+
*/
|
|
956
|
+
function getRequestContent({ document, operation, mediaType }) {
|
|
957
|
+
const content = getRequestBodyContent({
|
|
958
|
+
document,
|
|
959
|
+
operation
|
|
960
|
+
});
|
|
961
|
+
if (!content) return false;
|
|
962
|
+
if (mediaType) return mediaType in content ? content[mediaType] : false;
|
|
963
|
+
const mediaTypes = Object.keys(content);
|
|
964
|
+
const available = mediaTypes.find((mt) => isJsonMimeType(mt)) ?? mediaTypes[0];
|
|
965
|
+
return available ? [available, content[available]] : false;
|
|
966
|
+
}
|
|
967
|
+
/**
|
|
968
|
+
* Returns the primary request content type. Prefers a JSON-like media type (the last one wins,
|
|
969
|
+
* matching the previous behavior), then the first declared one, defaulting to `'application/json'`.
|
|
970
|
+
*/
|
|
971
|
+
function getRequestContentType({ document, operation }) {
|
|
972
|
+
const content = getRequestBodyContent({
|
|
973
|
+
document,
|
|
974
|
+
operation
|
|
975
|
+
});
|
|
976
|
+
const mediaTypes = content ? Object.keys(content) : [];
|
|
977
|
+
let result = mediaTypes[0] ?? "application/json";
|
|
978
|
+
for (const mt of mediaTypes) if (isJsonMimeType(mt)) result = mt;
|
|
979
|
+
return result;
|
|
980
|
+
}
|
|
981
|
+
/**
|
|
982
|
+
* Builds an `Operation` for every supported HTTP method on every path, in document order.
|
|
983
|
+
* `x-` path keys and unresolvable path-item `$ref`s are skipped.
|
|
984
|
+
*
|
|
985
|
+
* @example
|
|
986
|
+
* ```ts
|
|
987
|
+
* for (const operation of getOperations(document)) {
|
|
988
|
+
* parseOperation(options, operation)
|
|
989
|
+
* }
|
|
990
|
+
* ```
|
|
991
|
+
*/
|
|
992
|
+
function getOperations(document) {
|
|
993
|
+
const operations = [];
|
|
994
|
+
const paths = document.paths;
|
|
995
|
+
if (!paths) return operations;
|
|
996
|
+
for (const path of Object.keys(paths)) {
|
|
997
|
+
if (path.startsWith("x-")) continue;
|
|
998
|
+
let pathItem = paths[path];
|
|
999
|
+
if (!pathItem) continue;
|
|
1000
|
+
if (isReference(pathItem)) {
|
|
1001
|
+
const resolved = resolveRef(document, pathItem.$ref);
|
|
1002
|
+
paths[path] = resolved;
|
|
1003
|
+
if (!resolved || isReference(resolved)) continue;
|
|
1004
|
+
pathItem = resolved;
|
|
1005
|
+
}
|
|
1006
|
+
const item = pathItem;
|
|
1007
|
+
for (const method of Object.keys(item)) {
|
|
1008
|
+
if (!SUPPORTED_METHODS.has(method)) continue;
|
|
1009
|
+
const schema = item[method];
|
|
1010
|
+
if (!schema || typeof schema !== "object") continue;
|
|
1011
|
+
operations.push({
|
|
1012
|
+
path,
|
|
1013
|
+
method,
|
|
1014
|
+
schema
|
|
1015
|
+
});
|
|
1016
|
+
}
|
|
1017
|
+
}
|
|
1018
|
+
return operations;
|
|
1019
|
+
}
|
|
1020
|
+
//#endregion
|
|
905
1021
|
//#region src/resolvers.ts
|
|
906
1022
|
/**
|
|
907
1023
|
* Replaces `{variable}` placeholders in an OpenAPI server URL with provided values.
|
|
@@ -994,7 +1110,7 @@ function getResponseBody(responseBody, contentType) {
|
|
|
994
1110
|
}
|
|
995
1111
|
let availableContentType;
|
|
996
1112
|
const contentTypes = Object.keys(body.content);
|
|
997
|
-
for (const mt of contentTypes) if (
|
|
1113
|
+
for (const mt of contentTypes) if (isJsonMimeType(mt)) {
|
|
998
1114
|
availableContentType = mt;
|
|
999
1115
|
break;
|
|
1000
1116
|
}
|
|
@@ -1025,7 +1141,11 @@ function getResponseSchema(document, operation, statusCode, options = {}) {
|
|
|
1025
1141
|
if (schema && isReference(schema)) responses[key] = resolveRef(document, schema.$ref);
|
|
1026
1142
|
}
|
|
1027
1143
|
}
|
|
1028
|
-
const responseBody = getResponseBody(
|
|
1144
|
+
const responseBody = getResponseBody(getResponseByStatusCode({
|
|
1145
|
+
document,
|
|
1146
|
+
operation,
|
|
1147
|
+
statusCode
|
|
1148
|
+
}), options.contentType);
|
|
1029
1149
|
if (responseBody === false) return {};
|
|
1030
1150
|
const schema = Array.isArray(responseBody) ? responseBody[1].schema : responseBody.schema;
|
|
1031
1151
|
if (!schema) return {};
|
|
@@ -1041,7 +1161,11 @@ function getResponseSchema(document, operation, statusCode, options = {}) {
|
|
|
1041
1161
|
*/
|
|
1042
1162
|
function getRequestSchema(document, operation, options = {}) {
|
|
1043
1163
|
if (operation.schema.requestBody) operation.schema.requestBody = dereferenceWithRef(document, operation.schema.requestBody);
|
|
1044
|
-
const requestBody =
|
|
1164
|
+
const requestBody = getRequestContent({
|
|
1165
|
+
document,
|
|
1166
|
+
operation,
|
|
1167
|
+
mediaType: options.contentType
|
|
1168
|
+
});
|
|
1045
1169
|
if (requestBody === false) return null;
|
|
1046
1170
|
const schema = Array.isArray(requestBody) ? requestBody[1].schema : requestBody.schema;
|
|
1047
1171
|
if (!schema) return null;
|
|
@@ -1076,7 +1200,7 @@ function hasStructuralKeywords(fragment) {
|
|
|
1076
1200
|
function flattenSchema(schema) {
|
|
1077
1201
|
if (!schema?.allOf || schema.allOf.length === 0) return schema ?? null;
|
|
1078
1202
|
const allOfFragments = schema.allOf;
|
|
1079
|
-
if (allOfFragments.some((item) => (
|
|
1203
|
+
if (allOfFragments.some((item) => isReference(item))) return schema;
|
|
1080
1204
|
if (allOfFragments.some(hasStructuralKeywords)) return schema;
|
|
1081
1205
|
const merged = { ...schema };
|
|
1082
1206
|
delete merged.allOf;
|
|
@@ -1314,7 +1438,11 @@ function getResponseBodyContentTypes(document, operation, statusCode) {
|
|
|
1314
1438
|
if (schema && isReference(schema)) responses[key] = resolveRef(document, schema.$ref);
|
|
1315
1439
|
}
|
|
1316
1440
|
}
|
|
1317
|
-
const responseObj =
|
|
1441
|
+
const responseObj = getResponseByStatusCode({
|
|
1442
|
+
document,
|
|
1443
|
+
operation,
|
|
1444
|
+
statusCode
|
|
1445
|
+
});
|
|
1318
1446
|
if (!responseObj || typeof responseObj !== "object" || isReference(responseObj)) return [];
|
|
1319
1447
|
const body = responseObj;
|
|
1320
1448
|
return body.content ? Object.keys(body.content) : [];
|
|
@@ -1898,7 +2026,7 @@ function createSchemaParser(ctx, dialect = oasDialect) {
|
|
|
1898
2026
|
});
|
|
1899
2027
|
}
|
|
1900
2028
|
/**
|
|
1901
|
-
* Ordered schema
|
|
2029
|
+
* Ordered schema rule table. Order is significant: composition keywords (`$ref`, `allOf`,
|
|
1902
2030
|
* `oneOf`/`anyOf`) take precedence over `const`/`format`, which take precedence over the plain
|
|
1903
2031
|
* `type`. The first matching rule that produces a node wins. See {@link SchemaRule} for the
|
|
1904
2032
|
* match/convert/fall-through contract.
|
|
@@ -1996,10 +2124,10 @@ function createSchemaParser(ctx, dialect = oasDialect) {
|
|
|
1996
2124
|
}
|
|
1997
2125
|
];
|
|
1998
2126
|
/**
|
|
1999
|
-
*
|
|
2127
|
+
* Converts an OAS `SchemaObject` into a `SchemaNode`.
|
|
2000
2128
|
*
|
|
2001
|
-
* Builds the per-schema context, then
|
|
2002
|
-
*
|
|
2129
|
+
* Builds the per-schema context, then walks the ordered {@link schemaRules} table and returns
|
|
2130
|
+
* the first converter that produces a node. When none match, falls back to the configured
|
|
2003
2131
|
* `emptySchemaType`.
|
|
2004
2132
|
*/
|
|
2005
2133
|
function parseSchema({ schema, name }, rawOptions) {
|
|
@@ -2022,8 +2150,11 @@ function createSchemaParser(ctx, dialect = oasDialect) {
|
|
|
2022
2150
|
rawOptions,
|
|
2023
2151
|
options
|
|
2024
2152
|
};
|
|
2025
|
-
const
|
|
2026
|
-
|
|
2153
|
+
for (const rule of schemaRules) {
|
|
2154
|
+
if (!rule.match(schemaCtx)) continue;
|
|
2155
|
+
const node = rule.convert(schemaCtx);
|
|
2156
|
+
if (node) return node;
|
|
2157
|
+
}
|
|
2027
2158
|
const emptyType = typeOptionMap.get(options.emptySchemaType);
|
|
2028
2159
|
return _kubb_core.ast.createSchema({
|
|
2029
2160
|
type: emptyType,
|
|
@@ -2094,7 +2225,7 @@ function createSchemaParser(ctx, dialect = oasDialect) {
|
|
|
2094
2225
|
* Converts an OAS `Operation` into an `OperationNode`.
|
|
2095
2226
|
*/
|
|
2096
2227
|
function parseOperation(options, operation) {
|
|
2097
|
-
const operationId =
|
|
2228
|
+
const operationId = getOperationId(operation);
|
|
2098
2229
|
const operationName = operationId ? pascalCase(operationId) : void 0;
|
|
2099
2230
|
const parameters = getParameters(document, operation).map((param) => parseParameter(options, param, operationName));
|
|
2100
2231
|
const allContentTypes = ctx.contentType ? [ctx.contentType] : getRequestBodyContentTypes(document, operation);
|
|
@@ -2117,8 +2248,12 @@ function createSchemaParser(ctx, dialect = oasDialect) {
|
|
|
2117
2248
|
required: requestBodyMeta.required || void 0,
|
|
2118
2249
|
content: content.length > 0 ? content : void 0
|
|
2119
2250
|
} : void 0;
|
|
2120
|
-
const responses =
|
|
2121
|
-
const responseObj =
|
|
2251
|
+
const responses = getResponseStatusCodes(operation).map((statusCode) => {
|
|
2252
|
+
const responseObj = getResponseByStatusCode({
|
|
2253
|
+
document,
|
|
2254
|
+
operation,
|
|
2255
|
+
statusCode
|
|
2256
|
+
});
|
|
2122
2257
|
const responseName = operationName ? `${operationName}Status${statusCode}` : void 0;
|
|
2123
2258
|
const { description } = getResponseMeta(responseObj);
|
|
2124
2259
|
const parseEntrySchema = (contentType) => {
|
|
@@ -2136,7 +2271,10 @@ function createSchemaParser(ctx, dialect = oasDialect) {
|
|
|
2136
2271
|
...parseEntrySchema(contentType)
|
|
2137
2272
|
}));
|
|
2138
2273
|
if (content.length === 0) content.push({
|
|
2139
|
-
contentType:
|
|
2274
|
+
contentType: getRequestContentType({
|
|
2275
|
+
document,
|
|
2276
|
+
operation
|
|
2277
|
+
}) || "application/json",
|
|
2140
2278
|
...parseEntrySchema(ctx.contentType)
|
|
2141
2279
|
});
|
|
2142
2280
|
return _kubb_core.ast.createResponse({
|
|
@@ -2145,16 +2283,23 @@ function createSchemaParser(ctx, dialect = oasDialect) {
|
|
|
2145
2283
|
content
|
|
2146
2284
|
});
|
|
2147
2285
|
});
|
|
2148
|
-
const
|
|
2286
|
+
const pathItem = document.paths?.[operation.path];
|
|
2287
|
+
const pathItemDoc = pathItem && !dialect.isReference(pathItem) ? pathItem : void 0;
|
|
2288
|
+
const pickDoc = (key) => {
|
|
2289
|
+
const own = operation.schema[key];
|
|
2290
|
+
if (typeof own === "string") return own;
|
|
2291
|
+
const fallback = pathItemDoc?.[key];
|
|
2292
|
+
return typeof fallback === "string" ? fallback : void 0;
|
|
2293
|
+
};
|
|
2149
2294
|
return _kubb_core.ast.createOperation({
|
|
2150
2295
|
operationId,
|
|
2151
2296
|
protocol: "http",
|
|
2152
2297
|
method: operation.method.toUpperCase(),
|
|
2153
|
-
path:
|
|
2154
|
-
tags: operation.
|
|
2155
|
-
summary:
|
|
2156
|
-
description:
|
|
2157
|
-
deprecated: operation.
|
|
2298
|
+
path: operation.path,
|
|
2299
|
+
tags: Array.isArray(operation.schema.tags) ? operation.schema.tags.map(String) : [],
|
|
2300
|
+
summary: pickDoc("summary") || void 0,
|
|
2301
|
+
description: pickDoc("description") || void 0,
|
|
2302
|
+
deprecated: operation.schema.deprecated || void 0,
|
|
2158
2303
|
parameters,
|
|
2159
2304
|
requestBody,
|
|
2160
2305
|
responses
|
|
@@ -2372,7 +2517,7 @@ function resolveBaseUrl({ document, serverIndex, serverVariables }) {
|
|
|
2372
2517
|
* })
|
|
2373
2518
|
* ```
|
|
2374
2519
|
*/
|
|
2375
|
-
function preScan({ schemas, parseSchema, parseOperation,
|
|
2520
|
+
function preScan({ schemas, parseSchema, parseOperation, document, parserOptions, discriminator, dedupe }) {
|
|
2376
2521
|
const allNodes = [];
|
|
2377
2522
|
const refAliasMap = /* @__PURE__ */ new Map();
|
|
2378
2523
|
const enumNames = [];
|
|
@@ -2396,8 +2541,7 @@ function preScan({ schemas, parseSchema, parseOperation, baseOas, parserOptions,
|
|
|
2396
2541
|
let dedupePlan = null;
|
|
2397
2542
|
if (dedupe) {
|
|
2398
2543
|
const operationNodes = [];
|
|
2399
|
-
for (const
|
|
2400
|
-
if (!operation) continue;
|
|
2544
|
+
for (const operation of getOperations(document)) {
|
|
2401
2545
|
const operationNode = parseOperation(parserOptions, operation);
|
|
2402
2546
|
if (operationNode) operationNodes.push(operationNode);
|
|
2403
2547
|
}
|
|
@@ -2419,7 +2563,7 @@ function preScan({ schemas, parseSchema, parseOperation, baseOas, parserOptions,
|
|
|
2419
2563
|
};
|
|
2420
2564
|
}
|
|
2421
2565
|
/**
|
|
2422
|
-
* Creates a lazy `
|
|
2566
|
+
* Creates a lazy `InputNode<true>` from already-resolved adapter state.
|
|
2423
2567
|
*
|
|
2424
2568
|
* The schema and operation iterables each start a fresh parse pass on every
|
|
2425
2569
|
* `[Symbol.asyncIterator]()` call. This lets multiple plugins consume the same
|
|
@@ -2430,16 +2574,16 @@ function preScan({ schemas, parseSchema, parseOperation, baseOas, parserOptions,
|
|
|
2430
2574
|
*
|
|
2431
2575
|
* @example
|
|
2432
2576
|
* ```ts
|
|
2433
|
-
* const streamNode = createInputStream({ schemas, parseSchema, parseOperation,
|
|
2577
|
+
* const streamNode = createInputStream({ schemas, parseSchema, parseOperation, document, parserOptions, refAliasMap, discriminatorChildMap, meta })
|
|
2434
2578
|
* for await (const schema of streamNode.schemas) {
|
|
2435
2579
|
* // each call to for-await restarts from the first schema
|
|
2436
2580
|
* }
|
|
2437
2581
|
* ```
|
|
2438
2582
|
*/
|
|
2439
|
-
function createInputStream({ schemas, parseSchema, parseOperation,
|
|
2583
|
+
function createInputStream({ schemas, parseSchema, parseOperation, document, parserOptions, refAliasMap, discriminatorChildMap, dedupePlan, meta }) {
|
|
2440
2584
|
const rewriteTopLevelSchema = (node) => {
|
|
2441
2585
|
if (!dedupePlan) return node;
|
|
2442
|
-
const canonical = dedupePlan.canonicalBySignature.get(_kubb_core.ast.
|
|
2586
|
+
const canonical = dedupePlan.canonicalBySignature.get(_kubb_core.ast.signatureOf(node));
|
|
2443
2587
|
if (canonical && canonical.name !== node.name) return _kubb_core.ast.createSchema({
|
|
2444
2588
|
type: "ref",
|
|
2445
2589
|
name: node.name ?? null,
|
|
@@ -2475,8 +2619,7 @@ function createInputStream({ schemas, parseSchema, parseOperation, baseOas, pars
|
|
|
2475
2619
|
} };
|
|
2476
2620
|
const operationsIterable = { [Symbol.asyncIterator]() {
|
|
2477
2621
|
return (async function* () {
|
|
2478
|
-
for (const
|
|
2479
|
-
if (!operation) continue;
|
|
2622
|
+
for (const operation of getOperations(document)) {
|
|
2480
2623
|
const node = parseOperation(parserOptions, operation);
|
|
2481
2624
|
if (node) yield dedupePlan ? _kubb_core.ast.applyDedupe(node, dedupePlan) : node;
|
|
2482
2625
|
}
|
|
@@ -2531,7 +2674,6 @@ const adapterOas = (0, _kubb_core.createAdapter)((options) => {
|
|
|
2531
2674
|
let parsedDocument = null;
|
|
2532
2675
|
const documentCache = /* @__PURE__ */ new WeakMap();
|
|
2533
2676
|
const schemasCache = /* @__PURE__ */ new WeakMap();
|
|
2534
|
-
const baseOasCache = /* @__PURE__ */ new WeakMap();
|
|
2535
2677
|
const schemaParserCache = /* @__PURE__ */ new WeakMap();
|
|
2536
2678
|
const preScanCache = /* @__PURE__ */ new WeakMap();
|
|
2537
2679
|
function ensureDocument(source) {
|
|
@@ -2557,13 +2699,6 @@ const adapterOas = (0, _kubb_core.createAdapter)((options) => {
|
|
|
2557
2699
|
schemasCache.set(document, promise);
|
|
2558
2700
|
return promise;
|
|
2559
2701
|
}
|
|
2560
|
-
function ensureBaseOas(document) {
|
|
2561
|
-
const cached = baseOasCache.get(document);
|
|
2562
|
-
if (cached) return cached;
|
|
2563
|
-
const baseOas = new oas.default(document);
|
|
2564
|
-
baseOasCache.set(document, baseOas);
|
|
2565
|
-
return baseOas;
|
|
2566
|
-
}
|
|
2567
2702
|
function ensureSchemaParser(document) {
|
|
2568
2703
|
const cached = schemaParserCache.get(document);
|
|
2569
2704
|
if (cached) return cached;
|
|
@@ -2574,14 +2709,14 @@ const adapterOas = (0, _kubb_core.createAdapter)((options) => {
|
|
|
2574
2709
|
schemaParserCache.set(document, parser);
|
|
2575
2710
|
return parser;
|
|
2576
2711
|
}
|
|
2577
|
-
function ensurePreScan(document, schemas, parseSchema, parseOperation
|
|
2712
|
+
function ensurePreScan(document, schemas, parseSchema, parseOperation) {
|
|
2578
2713
|
const cached = preScanCache.get(document);
|
|
2579
2714
|
if (cached) return cached;
|
|
2580
2715
|
const result = preScan({
|
|
2581
2716
|
schemas,
|
|
2582
2717
|
parseSchema,
|
|
2583
2718
|
parseOperation,
|
|
2584
|
-
|
|
2719
|
+
document,
|
|
2585
2720
|
parserOptions,
|
|
2586
2721
|
discriminator,
|
|
2587
2722
|
dedupe
|
|
@@ -2593,13 +2728,12 @@ const adapterOas = (0, _kubb_core.createAdapter)((options) => {
|
|
|
2593
2728
|
const document = await ensureDocument(source);
|
|
2594
2729
|
const schemas = await ensureSchemas(document);
|
|
2595
2730
|
const { parseSchema, parseOperation } = ensureSchemaParser(document);
|
|
2596
|
-
const
|
|
2597
|
-
const { refAliasMap, enumNames, circularNames, discriminatorChildMap, dedupePlan } = ensurePreScan(document, schemas, parseSchema, parseOperation, baseOas);
|
|
2731
|
+
const { refAliasMap, enumNames, circularNames, discriminatorChildMap, dedupePlan } = ensurePreScan(document, schemas, parseSchema, parseOperation);
|
|
2598
2732
|
return createInputStream({
|
|
2599
2733
|
schemas,
|
|
2600
2734
|
parseSchema,
|
|
2601
2735
|
parseOperation,
|
|
2602
|
-
|
|
2736
|
+
document,
|
|
2603
2737
|
parserOptions,
|
|
2604
2738
|
refAliasMap,
|
|
2605
2739
|
discriminatorChildMap,
|
|
@@ -2644,18 +2778,17 @@ const adapterOas = (0, _kubb_core.createAdapter)((options) => {
|
|
|
2644
2778
|
await validateDocument(await parseDocument(input), options);
|
|
2645
2779
|
},
|
|
2646
2780
|
getImports(node, resolve) {
|
|
2647
|
-
return
|
|
2648
|
-
|
|
2649
|
-
|
|
2650
|
-
|
|
2651
|
-
|
|
2652
|
-
|
|
2653
|
-
|
|
2654
|
-
|
|
2655
|
-
|
|
2656
|
-
|
|
2657
|
-
|
|
2658
|
-
});
|
|
2781
|
+
return (0, _kubb_ast.collect)(node, { schema(schemaNode) {
|
|
2782
|
+
const schemaRef = (0, _kubb_ast.narrowSchema)(schemaNode, "ref");
|
|
2783
|
+
if (!schemaRef?.ref) return null;
|
|
2784
|
+
const rawName = (0, _kubb_ast_utils.extractRefName)(schemaRef.ref);
|
|
2785
|
+
const result = resolve(nameMapping.get(rawName) ?? rawName);
|
|
2786
|
+
if (!result) return null;
|
|
2787
|
+
return _kubb_core.ast.createImport({
|
|
2788
|
+
name: [result.name],
|
|
2789
|
+
path: result.path
|
|
2790
|
+
});
|
|
2791
|
+
} });
|
|
2659
2792
|
},
|
|
2660
2793
|
async parse(source) {
|
|
2661
2794
|
const streamNode = await createStream(source);
|