@kubb/adapter-oas 5.0.0-beta.8 → 5.0.0-beta.81

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 CHANGED
@@ -21,28 +21,24 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
21
21
  enumerable: true
22
22
  }) : target, mod));
23
23
  //#endregion
24
+ let _kubb_ast = require("@kubb/ast");
24
25
  let _kubb_core = require("@kubb/core");
25
26
  let node_path = require("node:path");
26
27
  node_path = __toESM(node_path, 1);
27
- let _redocly_openapi_core = require("@redocly/openapi-core");
28
- let oas_normalize = require("oas-normalize");
29
- oas_normalize = __toESM(oas_normalize, 1);
30
- let swagger2openapi = require("swagger2openapi");
31
- swagger2openapi = __toESM(swagger2openapi, 1);
32
- let oas = require("oas");
33
- oas = __toESM(oas, 1);
34
- let oas_types = require("oas/types");
35
- let oas_utils = require("oas/utils");
28
+ let node_fs_promises = require("node:fs/promises");
29
+ let _readme_openapi_parser = require("@readme/openapi-parser");
30
+ let _scalar_openapi_upgrader = require("@scalar/openapi-upgrader");
31
+ let yaml = require("yaml");
32
+ let api_ref_bundler = require("api-ref-bundler");
36
33
  //#region src/constants.ts
37
34
  /**
38
35
  * Default parser options applied when no explicit options are provided.
39
36
  *
40
37
  * @example
41
38
  * ```ts
42
- * import { DEFAULT_PARSER_OPTIONS } from '@kubb/adapter-oas'
39
+ * import { DEFAULT_PARSER_OPTIONS, parseOas } from '@kubb/adapter-oas'
43
40
  *
44
- * const parser = createOasParser(oas)
45
- * const root = parser.parse({ ...DEFAULT_PARSER_OPTIONS, dateType: 'date' })
41
+ * const { root } = parseOas(document, { ...DEFAULT_PARSER_OPTIONS, dateType: 'date' })
46
42
  * ```
47
43
  */
48
44
  const DEFAULT_PARSER_OPTIONS = {
@@ -64,17 +60,19 @@ const DEFAULT_PARSER_OPTIONS = {
64
60
  */
65
61
  const SCHEMA_REF_PREFIX = "#/components/schemas/";
66
62
  /**
67
- * OpenAPI version string written into the stub document created during multi-spec merges.
63
+ * HTTP methods that count as operations on an OpenAPI path item. Other keys
64
+ * (`parameters`, `summary`, `$ref`, vendor extensions) are skipped when iterating operations.
68
65
  */
69
- const MERGE_OPENAPI_VERSION = "3.0.0";
70
- /**
71
- * Fallback `info.title` placed in the stub document when merging multiple API files.
72
- */
73
- const MERGE_DEFAULT_TITLE = "Merged API";
74
- /**
75
- * Fallback `info.version` placed in the stub document when merging multiple API files.
76
- */
77
- const MERGE_DEFAULT_VERSION = "1.0.0";
66
+ const SUPPORTED_METHODS = /* @__PURE__ */ new Set([
67
+ "get",
68
+ "put",
69
+ "post",
70
+ "delete",
71
+ "options",
72
+ "head",
73
+ "patch",
74
+ "trace"
75
+ ]);
78
76
  /**
79
77
  * Set of JSON Schema keywords that prevent a schema fragment from being inlined during `allOf` flattening.
80
78
  *
@@ -89,7 +87,7 @@ const MERGE_DEFAULT_VERSION = "1.0.0";
89
87
  * // true when fragment has e.g. 'properties' or 'oneOf'
90
88
  * ```
91
89
  */
92
- const structuralKeys = new Set([
90
+ const structuralKeys = /* @__PURE__ */ new Set([
93
91
  "properties",
94
92
  "items",
95
93
  "additionalProperties",
@@ -99,12 +97,24 @@ const structuralKeys = new Set([
99
97
  "not"
100
98
  ]);
101
99
  /**
100
+ * Formats `convertFormat` maps to a dedicated type without going through `formatMap`:
101
+ * `int64` and the date/time family. Keep this in sync with the `convertFormat`
102
+ * special-cases in `parser.ts`. `isHandledFormat` reads it so the
103
+ * `KUBB_UNSUPPORTED_FORMAT` diagnostic and the parser agree on what is handled.
104
+ */
105
+ const specialCasedFormats = /* @__PURE__ */ new Set([
106
+ "int64",
107
+ "date-time",
108
+ "date",
109
+ "time"
110
+ ]);
111
+ /**
102
112
  * Static map from OAS `format` strings to Kubb `SchemaType` values.
103
113
  *
104
114
  * Only formats whose AST type differs from the OAS `type` field appear here.
105
- * Formats that depend on runtime options (`int64`, `date-time`, `date`, `time`) are handled separately
106
- * in the parser. `ipv4` and `ipv6` map to their own dedicated schema types; `hostname` and
107
- * `idn-hostname` map to `'url'` as the closest generic string-format type.
115
+ * Formats that depend on runtime options (`int64`, `date-time`, `date`, `time`) are handled
116
+ * separately in the parser. `ipv4` and `ipv6` map to their own dedicated schema types. `hostname`
117
+ * and `idn-hostname` map to `'url'` as the closest generic string-format type.
108
118
  *
109
119
  * @example
110
120
  * ```ts
@@ -144,95 +154,16 @@ const formatMap = {
144
154
  */
145
155
  const enumExtensionKeys = ["x-enumNames", "x-enum-varnames"];
146
156
  /**
147
- * Maps `'any' | 'unknown' | 'void'` option strings to their `ScalarSchemaType` constant.
148
- * Replaces a plain object lookup with a `Map` for explicit key membership testing via `.has()`.
149
- */
150
- const typeOptionMap = new Map([
151
- ["any", _kubb_core.ast.schemaTypes.any],
152
- ["unknown", _kubb_core.ast.schemaTypes.unknown],
153
- ["void", _kubb_core.ast.schemaTypes.void]
154
- ]);
155
- //#endregion
156
- //#region src/discriminator.ts
157
- /**
158
- * Injects discriminator enum values into child schemas so they know which value identifies them.
159
- *
160
- * Finds every union schema in `input.schemas` that has a `discriminatorPropertyName`, collects the
161
- * enum value each union member is mapped to, then adds (or replaces) that property on the matching
162
- * child object schema.
163
- *
164
- * Returns a new `InputNode` — the original is never mutated.
157
+ * Vendor extension keys that attach human-readable descriptions to enum values, checked in priority order.
165
158
  *
166
159
  * @example
167
160
  * ```ts
168
- * const { root } = parseOas(document, options)
169
- * const next = applyDiscriminatorInheritance(root)
161
+ * import { enumDescriptionKeys } from '@kubb/adapter-oas'
162
+ *
163
+ * const key = enumDescriptionKeys.find((k) => k in schema) // 'x-enumDescriptions' | 'x-enum-descriptions' | undefined
170
164
  * ```
171
165
  */
172
- function applyDiscriminatorInheritance(root) {
173
- const childMap = /* @__PURE__ */ new Map();
174
- for (const schema of root.schemas) {
175
- let unionNode = _kubb_core.ast.narrowSchema(schema, "union");
176
- if (!unionNode) {
177
- const intersectionMembers = _kubb_core.ast.narrowSchema(schema, "intersection")?.members;
178
- if (intersectionMembers) for (const m of intersectionMembers) {
179
- const u = _kubb_core.ast.narrowSchema(m, "union");
180
- if (u) {
181
- unionNode = u;
182
- break;
183
- }
184
- }
185
- }
186
- if (!unionNode?.discriminatorPropertyName || !unionNode.members) continue;
187
- const { discriminatorPropertyName, members } = unionNode;
188
- for (const member of members) {
189
- const intersectionNode = _kubb_core.ast.narrowSchema(member, "intersection");
190
- if (!intersectionNode?.members) continue;
191
- let refNode;
192
- let objNode;
193
- for (const m of intersectionNode.members) {
194
- refNode ??= _kubb_core.ast.narrowSchema(m, "ref");
195
- objNode ??= _kubb_core.ast.narrowSchema(m, "object");
196
- }
197
- if (!refNode?.name || !objNode) continue;
198
- const prop = objNode.properties.find((p) => p.name === discriminatorPropertyName);
199
- const enumNode = prop ? _kubb_core.ast.narrowSchema(prop.schema, "enum") : void 0;
200
- if (!enumNode?.enumValues?.length) continue;
201
- const enumValues = enumNode.enumValues.filter((v) => v !== null);
202
- if (!enumValues.length) continue;
203
- const existing = childMap.get(refNode.name);
204
- if (existing) existing.enumValues.push(...enumValues);
205
- else childMap.set(refNode.name, {
206
- propertyName: discriminatorPropertyName,
207
- enumValues: [...enumValues]
208
- });
209
- }
210
- }
211
- if (childMap.size === 0) return root;
212
- return _kubb_core.ast.transform(root, { schema(node, { parent }) {
213
- if (parent?.kind !== "Input" || !node.name) return;
214
- const entry = childMap.get(node.name);
215
- if (!entry) return;
216
- const objectNode = _kubb_core.ast.narrowSchema(node, "object");
217
- if (!objectNode) return;
218
- const { propertyName, enumValues } = entry;
219
- const enumSchema = _kubb_core.ast.createSchema({
220
- type: "enum",
221
- enumValues
222
- });
223
- const newProp = _kubb_core.ast.createProperty({
224
- name: propertyName,
225
- required: true,
226
- schema: enumSchema
227
- });
228
- const existingIdx = objectNode.properties.findIndex((p) => p.name === propertyName);
229
- const newProperties = existingIdx >= 0 ? objectNode.properties.map((p, i) => i === existingIdx ? newProp : p) : [...objectNode.properties, newProp];
230
- return {
231
- ...objectNode,
232
- properties: newProperties
233
- };
234
- } });
235
- }
166
+ const enumDescriptionKeys = ["x-enumDescriptions", "x-enum-descriptions"];
236
167
  //#endregion
237
168
  //#region ../../internals/utils/src/casing.ts
238
169
  /**
@@ -245,357 +176,238 @@ function applyDiscriminatorInheritance(root) {
245
176
  function toCamelOrPascal(text, pascal) {
246
177
  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) => {
247
178
  if (word.length > 1 && word === word.toUpperCase()) return word;
248
- if (i === 0 && !pascal) return word.charAt(0).toLowerCase() + word.slice(1);
249
- return word.charAt(0).toUpperCase() + word.slice(1);
179
+ return (i === 0 && !pascal ? word.charAt(0).toLowerCase() : word.charAt(0).toUpperCase()) + word.slice(1);
250
180
  }).join("").replace(/[^a-zA-Z0-9]/g, "");
251
181
  }
252
182
  /**
253
- * Splits `text` on `.` and applies `transformPart` to each segment.
254
- * The last segment receives `isLast = true`, all earlier segments receive `false`.
255
- * Segments are joined with `/` to form a file path.
256
- *
257
- * Only splits on dots followed by a letter so that version numbers
258
- * embedded in operationIds (e.g. `v2025.0`) are kept intact.
259
- *
260
- * Empty segments are filtered before joining. They arise when the text starts with
261
- * a dot followed immediately by a letter (e.g. `..Schema` splits into `['..', 'Schema']`
262
- * and `'..'` transforms to an empty string). Without this filter the join would produce
263
- * a leading `/`, which `path.resolve` would interpret as an absolute path, allowing
264
- * generated files to escape the configured output directory.
265
- */
266
- function applyToFileParts(text, transformPart) {
267
- const parts = text.split(/\.(?=[a-zA-Z])/);
268
- return parts.map((part, i) => transformPart(part, i === parts.length - 1)).filter(Boolean).join("/");
269
- }
270
- /**
271
- * Converts `text` to camelCase.
272
- * When `isFile` is `true`, dot-separated segments are each cased independently and joined with `/`.
273
- *
274
- * @example
275
- * camelCase('hello-world') // 'helloWorld'
276
- * camelCase('pet.petId', { isFile: true }) // 'pet/petId'
277
- */
278
- function camelCase(text, { isFile, prefix = "", suffix = "" } = {}) {
279
- if (isFile) return applyToFileParts(text, (part, isLast) => camelCase(part, isLast ? {
280
- prefix,
281
- suffix
282
- } : {}));
283
- return toCamelOrPascal(`${prefix} ${text} ${suffix}`, false);
284
- }
285
- /**
286
183
  * Converts `text` to PascalCase.
287
- * When `isFile` is `true`, the last dot-separated segment is PascalCased and earlier segments are camelCased.
288
- *
289
- * @example
290
- * pascalCase('hello-world') // 'HelloWorld'
291
- * pascalCase('pet.petId', { isFile: true }) // 'pet/PetId'
292
- */
293
- function pascalCase(text, { isFile, prefix = "", suffix = "" } = {}) {
294
- if (isFile) return applyToFileParts(text, (part, isLast) => isLast ? pascalCase(part, {
295
- prefix,
296
- suffix
297
- }) : camelCase(part));
298
- return toCamelOrPascal(`${prefix} ${text} ${suffix}`, true);
299
- }
300
- //#endregion
301
- //#region ../../internals/utils/src/object.ts
302
- /**
303
- * Returns `true` when `value` is a plain (non-null, non-array) object.
304
- *
305
- * @example
306
- * ```ts
307
- * isPlainObject({}) // true
308
- * isPlainObject([]) // false
309
- * isPlainObject(null) // false
310
- * ```
311
- */
312
- function isPlainObject(value) {
313
- return typeof value === "object" && value !== null && Object.getPrototypeOf(value) === Object.prototype;
314
- }
315
- /**
316
- * Recursively merges `source` into `target`, combining nested plain objects.
317
- * Arrays and non-object values from `source` override the corresponding values in `target`.
318
184
  *
319
- * @example
320
- * ```ts
321
- * mergeDeep({ a: { x: 1 } }, { a: { y: 2 } })
322
- * // { a: { x: 1, y: 2 } }
323
- * ```
324
- */
325
- function mergeDeep(target, source) {
326
- const result = { ...target };
327
- for (const key of Object.keys(source)) {
328
- const sv = source[key];
329
- const tv = result[key];
330
- result[key] = sv !== null && typeof sv === "object" && !Array.isArray(sv) && tv !== null && typeof tv === "object" && !Array.isArray(tv) ? mergeDeep(tv, sv) : sv;
331
- }
332
- return result;
333
- }
334
- //#endregion
335
- //#region ../../internals/utils/src/reserved.ts
336
- /**
337
- * JavaScript and Java reserved words.
338
- * @link https://github.com/jonschlinkert/reserved/blob/master/index.js
339
- */
340
- const reservedWords = new Set([
341
- "abstract",
342
- "arguments",
343
- "boolean",
344
- "break",
345
- "byte",
346
- "case",
347
- "catch",
348
- "char",
349
- "class",
350
- "const",
351
- "continue",
352
- "debugger",
353
- "default",
354
- "delete",
355
- "do",
356
- "double",
357
- "else",
358
- "enum",
359
- "eval",
360
- "export",
361
- "extends",
362
- "false",
363
- "final",
364
- "finally",
365
- "float",
366
- "for",
367
- "function",
368
- "goto",
369
- "if",
370
- "implements",
371
- "import",
372
- "in",
373
- "instanceof",
374
- "int",
375
- "interface",
376
- "let",
377
- "long",
378
- "native",
379
- "new",
380
- "null",
381
- "package",
382
- "private",
383
- "protected",
384
- "public",
385
- "return",
386
- "short",
387
- "static",
388
- "super",
389
- "switch",
390
- "synchronized",
391
- "this",
392
- "throw",
393
- "throws",
394
- "transient",
395
- "true",
396
- "try",
397
- "typeof",
398
- "var",
399
- "void",
400
- "volatile",
401
- "while",
402
- "with",
403
- "yield",
404
- "Array",
405
- "Date",
406
- "hasOwnProperty",
407
- "Infinity",
408
- "isFinite",
409
- "isNaN",
410
- "isPrototypeOf",
411
- "length",
412
- "Math",
413
- "name",
414
- "NaN",
415
- "Number",
416
- "Object",
417
- "prototype",
418
- "String",
419
- "toString",
420
- "undefined",
421
- "valueOf"
422
- ]);
423
- /**
424
- * Returns `true` when `name` is a syntactically valid JavaScript variable name.
185
+ * @example Word boundaries
186
+ * `pascalCase('hello-world') // 'HelloWorld'`
425
187
  *
426
- * @example
427
- * ```ts
428
- * isValidVarName('status') // true
429
- * isValidVarName('class') // false (reserved word)
430
- * isValidVarName('42foo') // false (starts with digit)
431
- * ```
188
+ * @example With a suffix
189
+ * `pascalCase('tag', { suffix: 'schema' }) // 'TagSchema'`
432
190
  */
433
- function isValidVarName(name) {
434
- if (!name || reservedWords.has(name)) return false;
435
- return /^[a-zA-Z_$][a-zA-Z0-9_$]*$/.test(name);
191
+ function pascalCase(text, { prefix = "", suffix = "" } = {}) {
192
+ return toCamelOrPascal(`${prefix} ${text} ${suffix}`, true);
436
193
  }
437
194
  //#endregion
438
- //#region ../../internals/utils/src/urlPath.ts
195
+ //#region ../../internals/utils/src/runtime.ts
439
196
  /**
440
- * Parses and transforms an OpenAPI/Swagger path string into various URL formats.
197
+ * Detects the JavaScript runtime executing the current process and exposes its name and version.
441
198
  *
442
- * @example
443
- * const p = new URLPath('/pet/{petId}')
444
- * p.URL // '/pet/:petId'
445
- * p.template // '`/pet/${petId}`'
199
+ * Prefer the shared {@link runtime} instance over constructing your own.
446
200
  */
447
- var URLPath = class {
201
+ var Runtime = class {
448
202
  /**
449
- * The raw OpenAPI/Swagger path string, e.g. `/pet/{petId}`.
450
- */
451
- path;
452
- #options;
453
- constructor(path, options = {}) {
454
- this.path = path;
455
- this.#options = options;
456
- }
457
- /** Converts the OpenAPI path to Express-style colon syntax, e.g. `/pet/{petId}` → `/pet/:petId`.
203
+ * `true` when the current process is running under Bun.
458
204
  *
459
- * @example
460
- * ```ts
461
- * new URLPath('/pet/{petId}').URL // '/pet/:petId'
462
- * ```
463
- */
464
- get URL() {
465
- return this.toURLPath();
466
- }
467
- /** Returns `true` when `path` is a fully-qualified URL (e.g. starts with `https://`).
205
+ * Detection keys off the global `Bun` object rather than `process.versions`,
206
+ * because Bun polyfills `process.versions.node` for Node compatibility and would
207
+ * otherwise look like Node.
468
208
  *
469
209
  * @example
470
210
  * ```ts
471
- * new URLPath('https://petstore.swagger.io/v2/pet').isURL // true
472
- * new URLPath('/pet/{petId}').isURL // false
211
+ * if (runtime.isBun) {
212
+ * await Bun.write(path, data)
213
+ * }
473
214
  * ```
474
215
  */
475
- get isURL() {
476
- try {
477
- return !!new URL(this.path).href;
478
- } catch {
479
- return false;
480
- }
216
+ get isBun() {
217
+ return typeof Bun !== "undefined";
481
218
  }
482
219
  /**
483
- * Converts the OpenAPI path to a TypeScript template literal string.
484
- *
485
- * @example
486
- * new URLPath('/pet/{petId}').template // '`/pet/${petId}`'
487
- * new URLPath('/account/monetary-accountID').template // '`/account/${monetaryAccountId}`'
220
+ * `true` when the current process is running under Deno.
488
221
  */
489
- get template() {
490
- return this.toTemplateString();
491
- }
492
- /** Returns the path and its extracted params as a structured `URLObject`, or as a stringified expression when `stringify` is set.
493
- *
494
- * @example
495
- * ```ts
496
- * new URLPath('/pet/{petId}').object
497
- * // { url: '/pet/:petId', params: { petId: 'petId' } }
498
- * ```
499
- */
500
- get object() {
501
- return this.toObject();
502
- }
503
- /** Returns a map of path parameter names, or `undefined` when the path has no parameters.
504
- *
505
- * @example
506
- * ```ts
507
- * new URLPath('/pet/{petId}').params // { petId: 'petId' }
508
- * new URLPath('/pet').params // undefined
509
- * ```
510
- */
511
- get params() {
512
- return this.getParams();
513
- }
514
- #transformParam(raw) {
515
- const param = isValidVarName(raw) ? raw : camelCase(raw);
516
- return this.#options.casing === "camelcase" ? camelCase(param) : param;
517
- }
518
- /**
519
- * Iterates over every `{param}` token in `path`, calling `fn` with the raw token and transformed name.
520
- */
521
- #eachParam(fn) {
522
- for (const match of this.path.matchAll(/\{([^}]+)\}/g)) {
523
- const raw = match[1];
524
- fn(raw, this.#transformParam(raw));
525
- }
526
- }
527
- toObject({ type = "path", replacer, stringify } = {}) {
528
- const object = {
529
- url: type === "path" ? this.toURLPath() : this.toTemplateString({ replacer }),
530
- params: this.getParams()
531
- };
532
- if (stringify) {
533
- if (type === "template") return JSON.stringify(object).replaceAll("'", "").replaceAll(`"`, "");
534
- if (object.params) return `{ url: '${object.url}', params: ${JSON.stringify(object.params).replaceAll("'", "").replaceAll(`"`, "")} }`;
535
- return `{ url: '${object.url}' }`;
536
- }
537
- return object;
222
+ get isDeno() {
223
+ return typeof globalThis.Deno !== "undefined";
538
224
  }
539
225
  /**
540
- * Converts the OpenAPI path to a TypeScript template literal string.
541
- * An optional `replacer` can transform each extracted parameter name before interpolation.
226
+ * `true` when the current process is running under Node.
542
227
  *
543
- * @example
544
- * new URLPath('/pet/{petId}').toTemplateString() // '`/pet/${petId}`'
228
+ * Bun and Deno are excluded first so a polyfilled `process` does not register as Node.
545
229
  */
546
- toTemplateString({ prefix = "", replacer } = {}) {
547
- return `\`${prefix}${this.path.split(/\{([^}]+)\}/).map((part, i) => {
548
- if (i % 2 === 0) return part;
549
- const param = this.#transformParam(part);
550
- return `\${${replacer ? replacer(param) : param}}`;
551
- }).join("")}\``;
230
+ get isNode() {
231
+ return !this.isBun && !this.isDeno && typeof process !== "undefined" && process.versions?.node != null;
552
232
  }
553
233
  /**
554
- * Extracts all `{param}` segments from the path and returns them as a key-value map.
555
- * An optional `replacer` transforms each parameter name in both key and value positions.
556
- * Returns `undefined` when no path parameters are found.
234
+ * Name of the runtime executing the current process.
557
235
  *
558
236
  * @example
559
237
  * ```ts
560
- * new URLPath('/pet/{petId}/tag/{tagId}').getParams()
561
- * // { petId: 'petId', tagId: 'tagId' }
238
+ * runtime.name // 'bun' when run with `bun kubb`, 'node' otherwise
562
239
  * ```
563
240
  */
564
- getParams(replacer) {
565
- const params = {};
566
- this.#eachParam((_raw, param) => {
567
- const key = replacer ? replacer(param) : param;
568
- params[key] = key;
569
- });
570
- return Object.keys(params).length > 0 ? params : void 0;
241
+ get name() {
242
+ if (this.isBun) return "bun";
243
+ if (this.isDeno) return "deno";
244
+ return "node";
571
245
  }
572
- /** Converts the OpenAPI path to Express-style colon syntax.
246
+ /**
247
+ * Version of the active runtime, or an empty string when it cannot be read.
573
248
  *
574
249
  * @example
575
250
  * ```ts
576
- * new URLPath('/pet/{petId}').toURLPath() // '/pet/:petId'
251
+ * runtime.version // '1.3.11' under Bun, '22.22.2' under Node
577
252
  * ```
578
253
  */
579
- toURLPath() {
580
- return this.path.replace(/\{([^}]+)\}/g, ":$1");
254
+ get version() {
255
+ if (this.isBun) return process.versions.bun ?? "";
256
+ if (this.isDeno) return globalThis.Deno?.version?.deno ?? "";
257
+ return process.versions?.node ?? "";
581
258
  }
582
259
  };
260
+ /**
261
+ * Shared {@link Runtime} instance describing the JavaScript runtime executing the current process.
262
+ */
263
+ const runtime = new Runtime();
583
264
  //#endregion
584
- //#region src/guards.ts
265
+ //#region ../../internals/utils/src/fs.ts
585
266
  /**
586
- * Returns `true` when `doc` is a Swagger 2.0 document (no `openapi` key).
267
+ * Resolves to `true` when the file or directory at `path` exists.
268
+ * Uses `Bun.file().exists()` when running under Bun, `fs.access` otherwise.
587
269
  *
588
270
  * @example
589
271
  * ```ts
590
- * if (isOpenApiV2Document(doc)) {
591
- * // doc is OpenAPIV2.Document
272
+ * if (await exists('./kubb.config.ts')) {
273
+ * const content = await read('./kubb.config.ts')
592
274
  * }
593
275
  * ```
594
276
  */
595
- function isOpenApiV2Document(doc) {
596
- return !!doc && isPlainObject(doc) && !("openapi" in doc);
277
+ async function exists(path) {
278
+ if (runtime.isBun) return Bun.file(path).exists();
279
+ return (0, node_fs_promises.access)(path).then(() => true, () => false);
280
+ }
281
+ /**
282
+ * Reads the file at `path` as a UTF-8 string.
283
+ * Uses `Bun.file().text()` when running under Bun, `fs.readFile` otherwise.
284
+ *
285
+ * @example
286
+ * ```ts
287
+ * const source = await read('./src/Pet.ts')
288
+ * ```
289
+ */
290
+ async function read(path) {
291
+ if (runtime.isBun) return Bun.file(path).text();
292
+ return (0, node_fs_promises.readFile)(path, { encoding: "utf8" });
293
+ }
294
+ //#endregion
295
+ //#region src/bundler.ts
296
+ const urlRegExp = /^https?:\/+/i;
297
+ async function readSource(sourcePath) {
298
+ if (urlRegExp.test(sourcePath)) {
299
+ const url = new URL(sourcePath);
300
+ const response = await fetch(url);
301
+ if (!response.ok) throw new Error(`Cannot fetch the OAS document at ${url.href} (HTTP ${response.status})`);
302
+ return response.text();
303
+ }
304
+ return read(sourcePath);
305
+ }
306
+ async function resolveSource(sourcePath) {
307
+ const data = await readSource(sourcePath);
308
+ if (sourcePath.toLowerCase().endsWith(".md")) return data;
309
+ return (0, yaml.parse)(data);
310
+ }
311
+ /**
312
+ * Bundles a multi-file OpenAPI document into a single document via `api-ref-bundler`.
313
+ *
314
+ * External file schemas are hoisted into named `components.schemas` entries, so a property
315
+ * pointing at `./schemas/User.yaml` ends up referencing `#/components/schemas/User`. Generators
316
+ * can then emit a named type with an import instead of inlining the shape. Sources are read with
317
+ * the Bun-aware `read` util for local YAML and JSON files, and with `fetch` for HTTP(S) URLs.
318
+ *
319
+ * @example Local file
320
+ * `const document = await bundleDocument('./openapi.yaml')`
321
+ *
322
+ * @example Remote URL
323
+ * `const document = await bundleDocument('https://example.com/openapi.yaml')`
324
+ */
325
+ async function bundleDocument(pathOrUrl) {
326
+ const cache = /* @__PURE__ */ new Map();
327
+ const resolver = (sourcePath) => {
328
+ const key = urlRegExp.test(sourcePath) ? new URL(sourcePath).href : sourcePath;
329
+ const cached = cache.get(key);
330
+ if (cached) return cached;
331
+ const result = resolveSource(sourcePath);
332
+ cache.set(key, result);
333
+ return result;
334
+ };
335
+ await resolver(pathOrUrl);
336
+ return await (0, api_ref_bundler.bundle)(pathOrUrl, resolver);
337
+ }
338
+ //#endregion
339
+ //#region src/factory.ts
340
+ /**
341
+ * Loads and bundles an OpenAPI document, returning the raw `Document`.
342
+ *
343
+ * A string is a file path or URL: it is bundled via `api-ref-bundler`, hoisting external file
344
+ * schemas into named `components.schemas` entries so generators can emit named types and imports.
345
+ * An object is treated as an already-parsed document. Swagger 2.0 and OpenAPI 3.0 documents are
346
+ * up-converted to OpenAPI 3.1 via `@scalar/openapi-upgrader`.
347
+ *
348
+ * @example
349
+ * ```ts
350
+ * const document = await parseDocument('./openapi.yaml')
351
+ * const document = await parseDocument(rawDocumentObject)
352
+ * ```
353
+ */
354
+ async function parseDocument(pathOrApi) {
355
+ if (typeof pathOrApi === "string") return parseDocument(await bundleDocument(pathOrApi));
356
+ return (0, _scalar_openapi_upgrader.upgrade)(pathOrApi, "3.1");
597
357
  }
598
358
  /**
359
+ * Creates a `Document` from an `AdapterSource`.
360
+ *
361
+ * - `{ type: 'path' }` resolves and bundles a local file path or remote URL.
362
+ * - `{ type: 'data' }` parses an inline string (YAML/JSON) or raw object.
363
+ *
364
+ * @example
365
+ * ```ts
366
+ * const document = await parseFromConfig({ type: 'path', path: './openapi.yaml' })
367
+ * const document = await parseFromConfig({ type: 'data', data: '{"openapi":"3.0.0",...}' })
368
+ * ```
369
+ */
370
+ async function parseFromConfig(source) {
371
+ if (source.type === "data") return parseDocument(typeof source.data === "string" ? (0, yaml.parse)(source.data) : structuredClone(source.data));
372
+ if (URL.canParse(source.path)) return parseDocument(source.path);
373
+ const resolved = node_path.default.resolve(node_path.default.dirname(source.path), source.path);
374
+ await assertInputExists(resolved);
375
+ return parseDocument(resolved);
376
+ }
377
+ /**
378
+ * Throws a coded `KUBB_INPUT_NOT_FOUND` diagnostic when a local input path does not exist.
379
+ * URLs are skipped, and a malformed but readable file is left for `parseDocument` to surface
380
+ * its parse error instead.
381
+ */
382
+ async function assertInputExists(input) {
383
+ if (URL.canParse(input)) return;
384
+ if (!await exists(input)) throw new _kubb_core.Diagnostics.Error({
385
+ code: _kubb_core.Diagnostics.code.inputNotFound,
386
+ severity: "error",
387
+ message: `Cannot read the file set in \`input.path\` (or via \`kubb generate PATH\`): ${input}`,
388
+ help: "Check that the path exists and is readable, then set it in `input.path` or pass it as `kubb generate PATH`.",
389
+ location: { kind: "config" }
390
+ });
391
+ }
392
+ /**
393
+ * Validates an OpenAPI document using `@readme/openapi-parser` with colorized error output.
394
+ *
395
+ * @example
396
+ * ```ts
397
+ * await validateDocument(document)
398
+ * ```
399
+ */
400
+ async function validateDocument(document, { throwOnError = false } = {}) {
401
+ try {
402
+ const result = await (0, _readme_openapi_parser.validate)(structuredClone(document), { validate: { errors: { colorize: true } } });
403
+ if (!result.valid) throw new Error((0, _readme_openapi_parser.compileErrors)(result));
404
+ } catch (error) {
405
+ if (throwOnError) throw error;
406
+ }
407
+ }
408
+ //#endregion
409
+ //#region src/guards.ts
410
+ /**
599
411
  * Returns `true` when a schema should be treated as nullable.
600
412
  *
601
413
  * Recognizes all nullable signals across OAS versions: `nullable: true` (OAS 3.0),
@@ -641,158 +453,369 @@ function isDiscriminator(obj) {
641
453
  return !!obj && !!record["discriminator"] && typeof record["discriminator"] !== "string";
642
454
  }
643
455
  //#endregion
644
- //#region src/factory.ts
456
+ //#region src/refs.ts
457
+ const _refCache = /* @__PURE__ */ new WeakMap();
645
458
  /**
646
- * Loads and dereferences an OpenAPI document, returning the raw `Document`.
459
+ * Resolves a local JSON pointer reference from a document.
647
460
  *
648
- * Accepts a file path string or an already-parsed document object. File paths are bundled via
649
- * Redocly to resolve external `$ref`s. Swagger 2.0 documents are automatically up-converted
650
- * to OpenAPI 3.0 via `swagger2openapi`.
461
+ * Accepts `#/...` refs. Returns `null` for an empty or non-local ref. When the pointer cannot be
462
+ * resolved, reports a `refNotFound` diagnostic into the active build and returns `null`. Outside a
463
+ * build there is no sink to collect it, so it throws instead.
651
464
  *
652
465
  * @example
653
466
  * ```ts
654
- * const document = await parseDocument('./openapi.yaml')
655
- * const document = await parse(rawDocumentObject, { canBundle: false })
467
+ * resolveRef<SchemaObject>(document, '#/components/schemas/Pet')
656
468
  * ```
657
469
  */
658
- async function parseDocument(pathOrApi, { canBundle = true, enablePaths = true } = {}) {
659
- if (typeof pathOrApi === "string" && canBundle) return parseDocument((await (0, _redocly_openapi_core.bundle)({
660
- ref: pathOrApi,
661
- config: await (0, _redocly_openapi_core.loadConfig)(),
662
- base: pathOrApi
663
- })).bundle.parsed, {
664
- canBundle,
665
- enablePaths
666
- });
667
- const document = await new oas_normalize.default(pathOrApi, {
668
- enablePaths,
669
- colorizeErrors: true
670
- }).load();
671
- if (isOpenApiV2Document(document)) {
672
- const { openapi } = await swagger2openapi.default.convertObj(document, { anchors: true });
673
- return openapi;
470
+ function resolveRef(document, $ref) {
471
+ const origRef = $ref;
472
+ $ref = $ref.trim();
473
+ if ($ref === "") return null;
474
+ if (!$ref.startsWith("#")) return null;
475
+ $ref = globalThis.decodeURIComponent($ref.substring(1));
476
+ let docCache = _refCache.get(document);
477
+ if (!docCache) {
478
+ docCache = /* @__PURE__ */ new Map();
479
+ _refCache.set(document, docCache);
480
+ }
481
+ if (docCache.has($ref)) return docCache.get($ref);
482
+ const current = $ref.split("/").filter(Boolean).reduce((obj, key) => obj?.[key], document);
483
+ if (!current) {
484
+ const diagnostic = {
485
+ code: _kubb_core.Diagnostics.code.refNotFound,
486
+ severity: "error",
487
+ message: `Could not find a definition for ${origRef}.`,
488
+ help: "Add the schema under `components.schemas`, or fix the `$ref`. Run `kubb validate` to check the spec.",
489
+ location: {
490
+ kind: "schema",
491
+ pointer: origRef,
492
+ ref: origRef
493
+ }
494
+ };
495
+ if (!_kubb_core.Diagnostics.report(diagnostic)) throw new _kubb_core.Diagnostics.Error(diagnostic);
496
+ return null;
674
497
  }
675
- return document;
498
+ docCache.set($ref, current);
499
+ return current;
676
500
  }
677
501
  /**
678
- * Deep-merges multiple OpenAPI documents into a single `Document`.
502
+ * Resolves a `$ref` object while preserving the original `$ref` field on the result.
679
503
  *
680
- * Each document is parsed independently then recursively merged via `mergeDeep` from `@internals/utils`.
681
- * Throws when the input array is empty.
504
+ * Useful for parser flows that need both dereferenced fields and pointer
505
+ * identity (for naming/import purposes). Non-reference values are returned as-is.
682
506
  *
683
507
  * @example
684
508
  * ```ts
685
- * const document = await mergeDocuments(['./pets.yaml', './orders.yaml'])
509
+ * dereferenceWithRef(document, { $ref: '#/components/schemas/Pet' })
510
+ * // { $ref: '#/components/schemas/Pet', type: 'object', properties: { ... } }
686
511
  * ```
687
512
  */
688
- async function mergeDocuments(pathOrApi) {
689
- const documents = await Promise.all(pathOrApi.map((p) => parseDocument(p, {
690
- enablePaths: false,
691
- canBundle: false
692
- })));
693
- if (documents.length === 0) throw new Error("No OAS documents provided for merging.");
694
- const seed = {
695
- openapi: MERGE_OPENAPI_VERSION,
696
- info: {
697
- title: MERGE_DEFAULT_TITLE,
698
- version: MERGE_DEFAULT_VERSION
699
- },
700
- paths: {},
701
- components: { schemas: {} }
513
+ function dereferenceWithRef(document, schema) {
514
+ if (isReference(schema)) return {
515
+ ...schema,
516
+ ...resolveRef(document, schema.$ref),
517
+ $ref: schema.$ref
702
518
  };
703
- return parseDocument(documents.reduce((acc, current) => mergeDeep(acc, current), seed));
519
+ return schema;
704
520
  }
521
+ //#endregion
522
+ //#region src/dialect.ts
705
523
  /**
706
- * Creates a `Document` from an `AdapterSource`.
524
+ * The OpenAPI / Swagger dialect, the default used by `@kubb/adapter-oas`.
525
+ *
526
+ * Implements the spec-agnostic {@link ast.SchemaDialect} contract: it isolates the
527
+ * decisions that differ between specs (nullability, `$ref`, discriminator, binary,
528
+ * ref resolution) so the converter pipeline and dispatch rules stay shared. A
529
+ * future adapter (e.g. AsyncAPI) ships its own dialect, `type: ['null', …]`
530
+ * nullability, no discriminator object, binary via `contentEncoding` and reuses
531
+ * the rest unchanged.
707
532
  *
708
- * Handles all three source types:
709
- * - `{ type: 'path' }` resolves and bundles a local file path or remote URL.
710
- * - `{ type: 'paths' }` — merges multiple file paths into a single document.
711
- * - `{ type: 'data' }` — parses an inline string (YAML/JSON) or raw object.
533
+ * Formats (`uuid`, `email`, dates, …) are intentionally NOT here: they are shared
534
+ * JSON Schema vocabulary, so the converters keep that common case.
712
535
  *
713
536
  * @example
714
537
  * ```ts
715
- * const document = await parseFromConfig({ type: 'path', path: './openapi.yaml' })
716
- * const document = await parseFromConfig({ type: 'data', data: '{"openapi":"3.0.0",...}' })
538
+ * const parser = createSchemaParser(context) // uses oasDialect
539
+ * const parser = createSchemaParser(context, oasDialect) // explicit
717
540
  * ```
718
541
  */
719
- function parseFromConfig(source) {
720
- if (source.type === "data") {
721
- if (typeof source.data === "object") return parseDocument(structuredClone(source.data));
722
- return parseDocument(source.data, { canBundle: false });
542
+ const oasDialect = _kubb_ast.ast.defineDialect({
543
+ name: "oas",
544
+ schema: {
545
+ isNullable,
546
+ isReference,
547
+ isDiscriminator,
548
+ isBinary: (schema) => schema.type === "string" && schema.contentMediaType === "application/octet-stream",
549
+ resolveRef
723
550
  }
724
- if (source.type === "paths") return mergeDocuments(source.paths);
725
- if (new URLPath(source.path).isURL) return parseDocument(source.path);
726
- return parseDocument(node_path.default.resolve(node_path.default.dirname(source.path), source.path));
551
+ });
552
+ //#endregion
553
+ //#region src/discriminator.ts
554
+ /**
555
+ * Maps each child schema name to its discriminator patch data by scanning the given
556
+ * top-level AST schema nodes for union schemas that carry a `discriminatorPropertyName`.
557
+ *
558
+ * The streaming path calls this on a small pre-parsed subset of schemas (only the
559
+ * discriminator parents) rather than on all schemas at once.
560
+ */
561
+ function buildDiscriminatorChildMap(schemas) {
562
+ const childMap = /* @__PURE__ */ new Map();
563
+ for (const schema of schemas) {
564
+ let unionNode = _kubb_ast.ast.narrowSchema(schema, "union");
565
+ if (!unionNode) {
566
+ const intersectionMembers = _kubb_ast.ast.narrowSchema(schema, "intersection")?.members;
567
+ if (intersectionMembers) for (const m of intersectionMembers) {
568
+ const u = _kubb_ast.ast.narrowSchema(m, "union");
569
+ if (u) {
570
+ unionNode = u;
571
+ break;
572
+ }
573
+ }
574
+ }
575
+ if (!unionNode?.discriminatorPropertyName || !unionNode.members) continue;
576
+ const { discriminatorPropertyName, members } = unionNode;
577
+ for (const member of members) {
578
+ const intersectionNode = _kubb_ast.ast.narrowSchema(member, "intersection");
579
+ if (!intersectionNode?.members) continue;
580
+ let refNode = null;
581
+ let objNode = null;
582
+ for (const m of intersectionNode.members) {
583
+ refNode ??= _kubb_ast.ast.narrowSchema(m, "ref");
584
+ objNode ??= _kubb_ast.ast.narrowSchema(m, "object");
585
+ }
586
+ if (!refNode?.name || !objNode) continue;
587
+ const prop = objNode.properties.find((p) => p.name === discriminatorPropertyName);
588
+ const enumNode = prop ? _kubb_ast.ast.narrowSchema(prop.schema, "enum") : null;
589
+ if (!enumNode?.enumValues?.length) continue;
590
+ const enumValues = enumNode.enumValues.filter((v) => v !== null);
591
+ if (!enumValues.length) continue;
592
+ const existing = childMap.get(refNode.name);
593
+ if (!existing) {
594
+ childMap.set(refNode.name, {
595
+ propertyName: discriminatorPropertyName,
596
+ enumValues: [...enumValues]
597
+ });
598
+ continue;
599
+ }
600
+ existing.enumValues.push(...enumValues);
601
+ }
602
+ }
603
+ return childMap;
604
+ }
605
+ /**
606
+ * Patches a single top-level `SchemaNode` with its discriminator entry (adds or replaces
607
+ * the discriminant property). Used by the streaming path to apply patches inline per yield
608
+ * without buffering all schemas.
609
+ */
610
+ function patchDiscriminatorNode(node, entry) {
611
+ const objectNode = _kubb_ast.ast.narrowSchema(node, "object");
612
+ if (!objectNode) return node;
613
+ const { propertyName, enumValues } = entry;
614
+ const enumSchema = _kubb_ast.ast.factory.createSchema({
615
+ type: "enum",
616
+ enumValues
617
+ });
618
+ const newProp = _kubb_ast.ast.factory.createProperty({
619
+ name: propertyName,
620
+ required: true,
621
+ schema: enumSchema
622
+ });
623
+ const existingIdx = objectNode.properties.findIndex((p) => p.name === propertyName);
624
+ const newProperties = existingIdx >= 0 ? objectNode.properties.map((p, i) => i === existingIdx ? newProp : p) : [...objectNode.properties, newProp];
625
+ return {
626
+ ...objectNode,
627
+ properties: newProperties
628
+ };
727
629
  }
728
630
  /**
729
- * Validates an OpenAPI document using `oas-normalize` with colorized error output.
631
+ * Creates a single-property object schema used as a discriminator literal.
730
632
  *
731
633
  * @example
732
634
  * ```ts
733
- * await validateDocument(document)
635
+ * createDiscriminantNode({ propertyName: 'type', value: 'dog' })
636
+ * // -> { type: 'object', properties: [{ name: 'type', required: true, schema: enum('dog') }] }
734
637
  * ```
735
638
  */
736
- async function validateDocument(document, { throwOnError = false } = {}) {
737
- try {
738
- await new oas_normalize.default(document, {
739
- enablePaths: true,
740
- colorizeErrors: true
741
- }).validate({ parser: { validate: { errors: { colorize: true } } } });
742
- } catch (error) {
743
- if (throwOnError) throw error;
744
- }
639
+ function createDiscriminantNode({ propertyName, value }) {
640
+ return _kubb_ast.ast.factory.createSchema({
641
+ type: "object",
642
+ primitive: "object",
643
+ properties: [_kubb_ast.ast.factory.createProperty({
644
+ name: propertyName,
645
+ schema: _kubb_ast.ast.factory.createSchema({
646
+ type: "enum",
647
+ primitive: "string",
648
+ enumValues: [value]
649
+ }),
650
+ required: true
651
+ })]
652
+ });
653
+ }
654
+ /**
655
+ * Returns the discriminator key whose mapping value matches `ref`, or `null` when there is no match.
656
+ *
657
+ * @example
658
+ * ```ts
659
+ * findDiscriminator({ dog: '#/components/schemas/Dog' }, '#/components/schemas/Dog') // 'dog'
660
+ * ```
661
+ */
662
+ function findDiscriminator(mapping, ref) {
663
+ if (!mapping || !ref) return null;
664
+ return Object.entries(mapping).find(([, value]) => value === ref)?.[0] ?? null;
745
665
  }
746
666
  //#endregion
747
- //#region src/refs.ts
748
- const _refCache = /* @__PURE__ */ new WeakMap();
667
+ //#region src/mime.ts
749
668
  /**
750
- * Resolves a local JSON pointer reference from a document.
669
+ * MIME type fragments that mark a media type as JSON-like.
751
670
  *
752
- * Accepts `#/...` refs. Returns `null` for empty or non-local refs.
753
- * Throws when the pointer cannot be resolved.
671
+ * A content type is JSON when it contains any of these substrings. The `+json` entry catches
672
+ * structured-syntax suffixes such as `application/vnd.api+json`.
673
+ */
674
+ const jsonMimeFragments = [
675
+ "application/json",
676
+ "application/x-json",
677
+ "text/json",
678
+ "text/x-json",
679
+ "+json"
680
+ ];
681
+ /**
682
+ * Returns `true` when a media type string is JSON-like.
754
683
  *
755
684
  * @example
756
685
  * ```ts
757
- * resolveRef<SchemaObject>(document, '#/components/schemas/Pet') // SchemaObject | null
686
+ * isJsonMimeType('application/json') // true
687
+ * isJsonMimeType('application/vnd.api+json') // true
688
+ * isJsonMimeType('multipart/form-data') // false
758
689
  * ```
759
690
  */
760
- function resolveRef(document, $ref) {
761
- const origRef = $ref;
762
- $ref = $ref.trim();
763
- if ($ref === "") return null;
764
- if ($ref.startsWith("#")) $ref = globalThis.decodeURIComponent($ref.substring(1));
765
- else return null;
766
- let docCache = _refCache.get(document);
767
- if (!docCache) {
768
- docCache = /* @__PURE__ */ new Map();
769
- _refCache.set(document, docCache);
691
+ function isJsonMimeType(mimeType) {
692
+ return jsonMimeFragments.some((fragment) => mimeType.includes(fragment));
693
+ }
694
+ //#endregion
695
+ //#region src/operation.ts
696
+ /**
697
+ * Slugifies a path for the `operationId` fallback: non-alphanumerics collapse to single dashes,
698
+ * with no leading or trailing dash.
699
+ */
700
+ function slugify(value) {
701
+ return value.replace(/[^a-zA-Z0-9]/g, "-").replace(/-{2,}/g, "-").replace(/^-|-$/g, "");
702
+ }
703
+ /**
704
+ * Returns the operation's `operationId`, falling back to `<method>_<slugified-path>` when absent.
705
+ */
706
+ function getOperationId({ path, method, schema }) {
707
+ const { operationId } = schema;
708
+ if (typeof operationId === "string" && operationId.length > 0) return operationId;
709
+ return `${method}_${slugify(path).toLowerCase()}`;
710
+ }
711
+ /**
712
+ * Returns the declared response status codes, skipping `x-` extensions and non-object entries.
713
+ */
714
+ function getResponseStatusCodes({ schema }) {
715
+ const responses = schema.responses;
716
+ if (!responses || isReference(responses)) return [];
717
+ return Object.keys(responses).filter((key) => !key.startsWith("x-") && !!responses[key] && typeof responses[key] === "object");
718
+ }
719
+ /**
720
+ * Returns the response object for a status code, resolving a `$ref` in place. `false` when absent.
721
+ */
722
+ function getResponseByStatusCode({ document, operation, statusCode }) {
723
+ const responses = operation.schema.responses;
724
+ if (!responses || isReference(responses)) return false;
725
+ const response = responses[statusCode];
726
+ if (!response) return false;
727
+ if (isReference(response)) {
728
+ const resolved = resolveRef(document, response.$ref);
729
+ responses[statusCode] = resolved;
730
+ if (!resolved || isReference(resolved)) return false;
731
+ return resolved;
770
732
  }
771
- if (docCache.has($ref)) return docCache.get($ref);
772
- const current = $ref.split("/").filter(Boolean).reduce((obj, key) => obj?.[key], document);
773
- if (!current) throw new Error(`Could not find a definition for ${origRef}.`);
774
- docCache.set($ref, current);
775
- return current;
733
+ return response;
776
734
  }
777
735
  /**
778
- * Resolves a `$ref` object while preserving the original `$ref` field on the result.
779
- *
780
- * Useful for parser flows that need both dereferenced fields and pointer
781
- * identity (for naming/import purposes). Non-reference values are returned as-is.
736
+ * Resolves the request body (dereferencing a `$ref` in place) and returns its content map, or
737
+ * `undefined` when the operation has no request body.
738
+ */
739
+ function getRequestBodyContent({ document, operation }) {
740
+ const { schema } = operation;
741
+ let requestBody = schema.requestBody;
742
+ if (!requestBody) return;
743
+ if (isReference(requestBody)) {
744
+ const resolved = resolveRef(document, requestBody.$ref);
745
+ schema.requestBody = resolved;
746
+ if (!resolved || isReference(resolved)) return;
747
+ requestBody = resolved;
748
+ }
749
+ return requestBody.content;
750
+ }
751
+ /**
752
+ * Returns the request body media type. With `mediaType` set, returns that entry or `false`.
753
+ * Otherwise picks the first JSON-like media type, then the first declared one, as a
754
+ * `[mediaType, object]` tuple.
755
+ */
756
+ function getRequestContent({ document, operation, mediaType }) {
757
+ const content = getRequestBodyContent({
758
+ document,
759
+ operation
760
+ });
761
+ if (!content) return false;
762
+ if (mediaType) return mediaType in content ? content[mediaType] : false;
763
+ const mediaTypes = Object.keys(content);
764
+ const available = mediaTypes.find((mt) => isJsonMimeType(mt)) ?? mediaTypes[0];
765
+ return available ? [available, content[available]] : false;
766
+ }
767
+ /**
768
+ * Returns the primary request content type. Prefers a JSON-like media type (the last one wins
769
+ * when several are declared), then the first declared one, defaulting to `'application/json'`.
770
+ */
771
+ function getRequestContentType({ document, operation }) {
772
+ const content = getRequestBodyContent({
773
+ document,
774
+ operation
775
+ });
776
+ const mediaTypes = content ? Object.keys(content) : [];
777
+ let result = mediaTypes[0] ?? "application/json";
778
+ for (const mt of mediaTypes) if (isJsonMimeType(mt)) result = mt;
779
+ return result;
780
+ }
781
+ /**
782
+ * Builds an `Operation` for every supported HTTP method on every path, in document order.
783
+ * `x-` path keys and unresolvable path-item `$ref`s are skipped.
782
784
  *
783
785
  * @example
784
786
  * ```ts
785
- * dereferenceWithRef(document, { $ref: '#/components/schemas/Pet' })
786
- * // { $ref: '#/components/schemas/Pet', type: 'object', properties: { ... } }
787
+ * for (const operation of getOperations(document)) {
788
+ * parseOperation(options, operation)
789
+ * }
787
790
  * ```
788
791
  */
789
- function dereferenceWithRef(document, schema) {
790
- if (isReference(schema)) return {
791
- ...schema,
792
- ...resolveRef(document, schema.$ref),
793
- $ref: schema.$ref
794
- };
795
- return schema;
792
+ function getOperations(document) {
793
+ const operations = [];
794
+ const paths = document.paths;
795
+ if (!paths) return operations;
796
+ for (const path of Object.keys(paths)) {
797
+ if (path.startsWith("x-")) continue;
798
+ let pathItem = paths[path];
799
+ if (!pathItem) continue;
800
+ if (isReference(pathItem)) {
801
+ const resolved = resolveRef(document, pathItem.$ref);
802
+ paths[path] = resolved;
803
+ if (!resolved || isReference(resolved)) continue;
804
+ pathItem = resolved;
805
+ }
806
+ const item = pathItem;
807
+ for (const method of Object.keys(item)) {
808
+ if (!SUPPORTED_METHODS.has(method)) continue;
809
+ const schema = item[method];
810
+ if (!schema || typeof schema !== "object") continue;
811
+ operations.push({
812
+ path,
813
+ method,
814
+ schema
815
+ });
816
+ }
817
+ }
818
+ return operations;
796
819
  }
797
820
  //#endregion
798
821
  //#region src/resolvers.ts
@@ -816,7 +839,16 @@ function resolveServerUrl(server, overrides) {
816
839
  for (const [key, variable] of Object.entries(server.variables)) {
817
840
  const value = overrides?.[key] ?? (variable.default != null ? String(variable.default) : void 0);
818
841
  if (value === void 0) continue;
819
- if (variable.enum?.length && !variable.enum.some((e) => String(e) === value)) throw new Error(`Invalid server variable value '${value}' for '${key}' when resolving ${server.url}. Valid values are: ${variable.enum.join(", ")}.`);
842
+ if (variable.enum?.length && !variable.enum.some((e) => String(e) === value)) throw new _kubb_core.Diagnostics.Error({
843
+ code: _kubb_core.Diagnostics.code.invalidServerVariable,
844
+ severity: "error",
845
+ message: `Invalid server variable value '${value}' for '${key}' when resolving ${server.url}. Valid values are: ${variable.enum.join(", ")}.`,
846
+ help: `Use one of the allowed enum values, or drop the enum on the '${key}' server variable.`,
847
+ location: {
848
+ kind: "document",
849
+ pointer: "#/servers"
850
+ }
851
+ });
820
852
  url = url.replaceAll(`{${key}}`, value);
821
853
  }
822
854
  return url;
@@ -829,6 +861,15 @@ function getSchemaType(format) {
829
861
  return formatMap[format] ?? null;
830
862
  }
831
863
  /**
864
+ * Whether the parser maps `format` to a dedicated type. True for any `formatMap` entry, plus the
865
+ * `specialCasedFormats` that `convertFormat` handles directly. False means the format falls back to
866
+ * the base type, which is what `KUBB_UNSUPPORTED_FORMAT` flags. Reading both sources keeps the
867
+ * diagnostic in step with the parser as `formatMap` grows.
868
+ */
869
+ function isHandledFormat(format) {
870
+ return getSchemaType(format) !== null || specialCasedFormats.has(format);
871
+ }
872
+ /**
832
873
  * Converts an OAS primitive type string to its `PrimitiveSchemaType` equivalent.
833
874
  * Numeric types (`number`, `integer`, `bigint`) pass through unchanged. `boolean` maps to `'boolean'`. Everything else becomes `'string'`.
834
875
  */
@@ -838,15 +879,9 @@ function getPrimitiveType(type) {
838
879
  return "string";
839
880
  }
840
881
  /**
841
- * Narrows a content-type string to the `MediaType` union Kubb recognizes, or returns `null`.
842
- */
843
- function getMediaType(contentType) {
844
- return Object.values(_kubb_core.ast.mediaTypes).includes(contentType) ? contentType : null;
845
- }
846
- /**
847
882
  * Returns all parameters for an operation, merging path-level and operation-level entries.
848
883
  * Operation-level parameters override path-level ones with the same `in:name` key.
849
- * `$ref` parameters resolve via `dereferenceWithRef` for backward compatibility.
884
+ * Each `$ref` parameter is dereferenced via `dereferenceWithRef` before merging.
850
885
  *
851
886
  * @example
852
887
  * ```ts
@@ -875,7 +910,7 @@ function getResponseBody(responseBody, contentType) {
875
910
  }
876
911
  let availableContentType;
877
912
  const contentTypes = Object.keys(body.content);
878
- for (const mt of contentTypes) if (oas_utils.matchesMimeType.json(mt)) {
913
+ for (const mt of contentTypes) if (isJsonMimeType(mt)) {
879
914
  availableContentType = mt;
880
915
  break;
881
916
  }
@@ -898,15 +933,21 @@ function getResponseBody(responseBody, contentType) {
898
933
  * getResponseSchema(document, operation, '4XX') // {}
899
934
  * ```
900
935
  */
901
- function getResponseSchema(document, operation, statusCode, options = {}) {
902
- if (operation.schema.responses) {
903
- const responses = operation.schema.responses;
904
- for (const key in responses) {
905
- const schema = responses[key];
906
- if (schema && isReference(schema)) responses[key] = resolveRef(document, schema.$ref);
907
- }
936
+ function resolveResponseRefs(document, operation) {
937
+ const responses = operation.schema.responses;
938
+ if (!responses) return;
939
+ for (const key in responses) {
940
+ const schema = responses[key];
941
+ if (schema && isReference(schema)) responses[key] = resolveRef(document, schema.$ref);
908
942
  }
909
- const responseBody = getResponseBody(operation.getResponseByStatusCode(statusCode), options.contentType);
943
+ }
944
+ function getResponseSchema(document, operation, statusCode, options = {}) {
945
+ resolveResponseRefs(document, operation);
946
+ const responseBody = getResponseBody(getResponseByStatusCode({
947
+ document,
948
+ operation,
949
+ statusCode
950
+ }), options.contentType);
910
951
  if (responseBody === false) return {};
911
952
  const schema = Array.isArray(responseBody) ? responseBody[1].schema : responseBody.schema;
912
953
  if (!schema) return {};
@@ -922,16 +963,25 @@ function getResponseSchema(document, operation, statusCode, options = {}) {
922
963
  */
923
964
  function getRequestSchema(document, operation, options = {}) {
924
965
  if (operation.schema.requestBody) operation.schema.requestBody = dereferenceWithRef(document, operation.schema.requestBody);
925
- const requestBody = operation.getRequestBody(options.contentType);
966
+ const requestBody = getRequestContent({
967
+ document,
968
+ operation,
969
+ mediaType: options.contentType
970
+ });
926
971
  if (requestBody === false) return null;
972
+ const mediaType = Array.isArray(requestBody) ? requestBody[0] : options.contentType;
927
973
  const schema = Array.isArray(requestBody) ? requestBody[1].schema : requestBody.schema;
974
+ if (mediaType === "application/octet-stream" && (!schema || Object.keys(schema).length === 0)) return {
975
+ type: "string",
976
+ contentMediaType: "application/octet-stream"
977
+ };
928
978
  if (!schema) return null;
929
979
  return dereferenceWithRef(document, schema);
930
980
  }
931
981
  /**
932
982
  * Flattens a keyword-only `allOf` into its parent schema.
933
983
  *
934
- * Only flattens when every member is a plain fragment no `$ref` and no structural keywords
984
+ * Only flattens when every member is a plain fragment, with no `$ref` and no structural keywords
935
985
  * (see `structuralKeys`). Outer schema values take precedence over fragment values.
936
986
  * Returns `null` for a `null` input, and the original schema unchanged when flattening is unsafe.
937
987
  *
@@ -939,9 +989,12 @@ function getRequestSchema(document, operation, options = {}) {
939
989
  * ```ts
940
990
  * flattenSchema({ allOf: [{ description: 'A pet' }], type: 'object', properties: {} })
941
991
  * // { type: 'object', properties: {}, description: 'A pet' }
992
+ * ```
942
993
  *
994
+ * @example
995
+ * ```ts
943
996
  * flattenSchema({ allOf: [{ $ref: '#/components/schemas/Pet' }] })
944
- * // returned unchanged contains a $ref
997
+ * // returned unchanged, contains a $ref
945
998
  * ```
946
999
  */
947
1000
  /**
@@ -957,17 +1010,17 @@ function hasStructuralKeywords(fragment) {
957
1010
  function flattenSchema(schema) {
958
1011
  if (!schema?.allOf || schema.allOf.length === 0) return schema ?? null;
959
1012
  const allOfFragments = schema.allOf;
960
- if (allOfFragments.some((item) => (0, oas_types.isRef)(item))) return schema;
1013
+ if (allOfFragments.some((item) => isReference(item))) return schema;
961
1014
  if (allOfFragments.some(hasStructuralKeywords)) return schema;
962
- const merged = { ...schema };
963
- delete merged.allOf;
1015
+ const { allOf: _allOf, ...rest } = schema;
1016
+ const merged = rest;
964
1017
  for (const fragment of allOfFragments) for (const [key, value] of Object.entries(fragment)) if (merged[key] === void 0) merged[key] = value;
965
1018
  return merged;
966
1019
  }
967
1020
  /**
968
1021
  * Extracts the inline schema from a media-type `content` map.
969
1022
  *
970
- * Prefers `preferredContentType` when given; otherwise uses the first key in the map.
1023
+ * Prefers `preferredContentType` when given, otherwise uses the first key in the map.
971
1024
  * Returns `null` when `content` is absent, the schema is missing, or the schema is a `$ref`.
972
1025
  *
973
1026
  * @example
@@ -986,21 +1039,22 @@ function extractSchemaFromContent(content, preferredContentType) {
986
1039
  /**
987
1040
  * Walks a schema tree and collects the names of all `#/components/schemas/<name>` `$ref`s.
988
1041
  */
989
- function collectRefs(schema, refs = /* @__PURE__ */ new Set()) {
1042
+ function* collectRefs(schema) {
990
1043
  if (Array.isArray(schema)) {
991
- for (const item of schema) collectRefs(item, refs);
992
- return refs;
1044
+ for (const item of schema) yield* collectRefs(item);
1045
+ return;
993
1046
  }
994
1047
  if (schema && typeof schema === "object") for (const key in schema) {
995
1048
  const value = schema[key];
996
- if (key === "$ref" && typeof value === "string") {
997
- if (value.startsWith("#/components/schemas/")) {
998
- const name = value.slice(21);
999
- if (name) refs.add(name);
1000
- }
1001
- } else collectRefs(value, refs);
1049
+ if (!(key === "$ref" && typeof value === "string")) {
1050
+ yield* collectRefs(value);
1051
+ continue;
1052
+ }
1053
+ if (value.startsWith("#/components/schemas/")) {
1054
+ const name = value.slice(21);
1055
+ if (name) yield name;
1056
+ }
1002
1057
  }
1003
- return refs;
1004
1058
  }
1005
1059
  /**
1006
1060
  * Returns a copy of `schemas` topologically sorted by `$ref` dependency.
@@ -1016,7 +1070,7 @@ function collectRefs(schema, refs = /* @__PURE__ */ new Set()) {
1016
1070
  */
1017
1071
  function sortSchemas(schemas) {
1018
1072
  const deps = /* @__PURE__ */ new Map();
1019
- for (const [name, schema] of Object.entries(schemas)) deps.set(name, Array.from(collectRefs(schema)));
1073
+ for (const [name, schema] of Object.entries(schemas)) deps.set(name, [...new Set(collectRefs(schema))]);
1020
1074
  const sorted = [];
1021
1075
  const visited = /* @__PURE__ */ new Set();
1022
1076
  function visit(name, stack) {
@@ -1037,9 +1091,6 @@ const semanticSuffixes = {
1037
1091
  responses: "Response",
1038
1092
  requestBodies: "Request"
1039
1093
  };
1040
- function getSemanticSuffix(source) {
1041
- return semanticSuffixes[source];
1042
- }
1043
1094
  function resolveSchemaRef(document, schema) {
1044
1095
  if (!isReference(schema)) return schema;
1045
1096
  const resolved = resolveRef(document, schema.$ref);
@@ -1088,13 +1139,13 @@ function getSchemas(document, { contentType }) {
1088
1139
  let hasMultipleSources = false;
1089
1140
  if (!isSingle) {
1090
1141
  const firstSource = items[0].source;
1091
- for (let i = 1; i < items.length; i++) if (items[i].source !== firstSource) {
1142
+ for (const item of items) if (item.source !== firstSource) {
1092
1143
  hasMultipleSources = true;
1093
1144
  break;
1094
1145
  }
1095
1146
  }
1096
1147
  items.forEach((item, index) => {
1097
- const suffix = isSingle ? "" : hasMultipleSources ? getSemanticSuffix(item.source) : index === 0 ? "" : String(index + 1);
1148
+ const suffix = isSingle ? "" : hasMultipleSources ? semanticSuffixes[item.source] : index === 0 ? "" : String(index + 1);
1098
1149
  const uniqueName = item.originalName + suffix;
1099
1150
  schemas[uniqueName] = item.schema;
1100
1151
  nameMapping.set(`#/components/${item.source}/${item.originalName}`, uniqueName);
@@ -1107,7 +1158,7 @@ function getSchemas(document, { contentType }) {
1107
1158
  }
1108
1159
  /**
1109
1160
  * Resolves the AST type descriptor for a date/time format, honoring the `dateType` option.
1110
- * Returns `null` when `dateType: false`, signalling the format should fall through to `string`.
1161
+ * Returns `null` when `dateType: false`, so the format falls through to `string`.
1111
1162
  */
1112
1163
  function getDateType(options, format) {
1113
1164
  if (!options.dateType) return null;
@@ -1141,6 +1192,15 @@ function getDateType(options, format) {
1141
1192
  /**
1142
1193
  * Collects the shared metadata fields passed to every `createSchema` call.
1143
1194
  */
1195
+ /**
1196
+ * Reads schema examples as an array. OAS 3.1 uses an `examples` array, but specs (including ones
1197
+ * labeled 3.1) still use the singular OAS 3.0 `example`, which the upgrader only converts on the
1198
+ * 3.0 -> 3.1 hop. Normalize both into one array so the AST node exposes only `examples`.
1199
+ */
1200
+ function extractExamples(schema) {
1201
+ if (Array.isArray(schema.examples)) return schema.examples;
1202
+ return schema.example !== void 0 ? [schema.example] : void 0;
1203
+ }
1144
1204
  function buildSchemaNode(schema, name, nullable, defaultValue) {
1145
1205
  return {
1146
1206
  name,
@@ -1151,15 +1211,16 @@ function buildSchemaNode(schema, name, nullable, defaultValue) {
1151
1211
  readOnly: schema.readOnly,
1152
1212
  writeOnly: schema.writeOnly,
1153
1213
  default: defaultValue,
1154
- example: schema.example
1214
+ examples: extractExamples(schema),
1215
+ format: schema.format
1155
1216
  };
1156
1217
  }
1157
1218
  /**
1158
1219
  * Returns all request body content type keys for an operation.
1159
1220
  *
1160
- * The requestBody is dereferenced **in-place** when it is a `$ref` the same mutation
1161
- * that `getRequestSchema` already performs so that the returned list accurately reflects
1162
- * the available content types even for referenced bodies.
1221
+ * The requestBody is dereferenced in place when it is a `$ref` (the same mutation that
1222
+ * `getRequestSchema` already performs), so the returned list accurately reflects the
1223
+ * available content types even for referenced bodies.
1163
1224
  *
1164
1225
  * @example
1165
1226
  * ```ts
@@ -1173,15 +1234,38 @@ function getRequestBodyContentTypes(document, operation) {
1173
1234
  if (!body) return [];
1174
1235
  return body.content ? Object.keys(body.content) : [];
1175
1236
  }
1237
+ /**
1238
+ * Returns all response content type keys for an operation at a given status code.
1239
+ *
1240
+ * Response `$ref`s are resolved in place first (the same mutation `getResponseSchema` performs),
1241
+ * so the returned list reflects the available content types even for referenced responses.
1242
+ *
1243
+ * @example
1244
+ * ```ts
1245
+ * getResponseBodyContentTypes(document, operation, 200)
1246
+ * // ['application/json', 'application/xml']
1247
+ * ```
1248
+ */
1249
+ function getResponseBodyContentTypes(document, operation, statusCode) {
1250
+ resolveResponseRefs(document, operation);
1251
+ const responseObj = getResponseByStatusCode({
1252
+ document,
1253
+ operation,
1254
+ statusCode
1255
+ });
1256
+ if (!responseObj || typeof responseObj !== "object" || isReference(responseObj)) return [];
1257
+ const body = responseObj;
1258
+ return body.content ? Object.keys(body.content) : [];
1259
+ }
1176
1260
  //#endregion
1177
1261
  //#region src/parser.ts
1178
1262
  /**
1179
1263
  * Normalizes malformed `{ type: 'array', enum: [...] }` schemas by moving enum values into items.
1180
1264
  *
1181
1265
  * This pattern violates the OpenAPI spec but appears in real specs. The fix moves enum values
1182
- * from the array to its items sub-schema, making them valid for downstream processing.
1266
+ * from the array to its items sub-schema, so they are valid for downstream processing.
1183
1267
  *
1184
- * @note This is a defensive measure for robustness with non-compliant specs.
1268
+ * @note A defensive measure for non-compliant specs.
1185
1269
  */
1186
1270
  function normalizeArrayEnum(schema) {
1187
1271
  const normalizedItems = {
@@ -1195,15 +1279,48 @@ function normalizeArrayEnum(schema) {
1195
1279
  };
1196
1280
  }
1197
1281
  /**
1282
+ * Builds a `null` scalar node carrying the schema's documentation. Shared by the `const: null`
1283
+ * and the drf-spectacular `NullEnum` (`{ enum: [null] }`) branches, which render identically.
1284
+ */
1285
+ function createNullSchema(schema, name, nullable) {
1286
+ return _kubb_ast.ast.factory.createSchema({
1287
+ type: "null",
1288
+ primitive: "null",
1289
+ name,
1290
+ title: schema.title,
1291
+ description: schema.description,
1292
+ deprecated: schema.deprecated,
1293
+ nullable,
1294
+ format: schema.format
1295
+ });
1296
+ }
1297
+ /**
1298
+ * Names the inline enums on a property's schema, and on each item when the property is a tuple, from
1299
+ * the parent and property name. Wraps `macroEnumName` at the property construction site.
1300
+ */
1301
+ function nameEnums(node, options) {
1302
+ const macro = (0, _kubb_ast.macroEnumName)(options);
1303
+ const named = _kubb_ast.ast.applyMacros(node, [macro], { depth: "shallow" });
1304
+ const tupleNode = _kubb_ast.ast.narrowSchema(named, "tuple");
1305
+ if (tupleNode?.items) {
1306
+ const namedItems = tupleNode.items.map((item) => _kubb_ast.ast.applyMacros(item, [macro], { depth: "shallow" }));
1307
+ if (namedItems.some((item, i) => item !== tupleNode.items[i])) return {
1308
+ ...tupleNode,
1309
+ items: namedItems
1310
+ };
1311
+ }
1312
+ return named;
1313
+ }
1314
+ /**
1198
1315
  * Factory function that creates schema and operation converters for a given OpenAPI context.
1199
1316
  *
1200
1317
  * Returns closures that share mutable state (`resolvingRefs` set for cycle detection).
1201
1318
  * Each converter branch (`convertRef`, `convertAllOf`, etc.) mutually recursively calls `parseSchema`,
1202
- * made possible by hoisting of function declarations.
1319
+ * which works because function declarations hoist.
1203
1320
  *
1204
- * @note Not exported; called internally by `parseOas()` and `parseSchema()`.
1321
+ * @internal
1205
1322
  */
1206
- function createSchemaParser(ctx) {
1323
+ function createSchemaParser(ctx, dialect = oasDialect) {
1207
1324
  const document = ctx.document;
1208
1325
  /**
1209
1326
  * Tracks `$ref` paths that are currently being resolved to prevent infinite
@@ -1211,6 +1328,33 @@ function createSchemaParser(ctx) {
1211
1328
  */
1212
1329
  const resolvingRefs = /* @__PURE__ */ new Set();
1213
1330
  /**
1331
+ * Cache of `$ref` schemas already resolved in this parser instance, keyed by ref path.
1332
+ *
1333
+ * Without it, a shared schema (e.g. `customer`) is re-expanded for every `$ref` that points at
1334
+ * it. In cross-referenced specs like Stripe (~1400 schemas) that becomes exponential blowup,
1335
+ * since one schema can be referenced from dozens of parents, each re-walking its whole subtree.
1336
+ * Memoizing by ref path drops the work from O(2^depth) to O(N) unique schema names.
1337
+ */
1338
+ const resolvedRefCache = /* @__PURE__ */ new Map();
1339
+ /**
1340
+ * Memoized record of whether a `$ref` path resolves to a node the document actually defines.
1341
+ * A circular ref still resolves to an existing target, so this stays `true` for cycles and only
1342
+ * goes `false` for a `$ref` that points at a component the spec never declares.
1343
+ */
1344
+ const refExistence = /* @__PURE__ */ new Map();
1345
+ function refExists(refPath) {
1346
+ if (!refExistence.has(refPath)) {
1347
+ let exists = false;
1348
+ try {
1349
+ exists = !!dialect.schema.resolveRef(document, refPath);
1350
+ } catch {
1351
+ exists = false;
1352
+ }
1353
+ refExistence.set(refPath, exists);
1354
+ }
1355
+ return refExistence.get(refPath) ?? false;
1356
+ }
1357
+ /**
1214
1358
  * Converts a `$ref` schema into a `RefSchemaNode`.
1215
1359
  *
1216
1360
  * The resolved schema is stored in `node.schema`. Usage-site sibling fields
@@ -1219,20 +1363,30 @@ function createSchemaParser(ctx) {
1219
1363
  * Circular refs are detected via `resolvingRefs` and leave `schema` as `undefined`.
1220
1364
  */
1221
1365
  function convertRef({ schema, name, nullable, defaultValue, rawOptions }) {
1222
- let resolvedSchema;
1366
+ let resolvedSchema = null;
1223
1367
  const refPath = schema.$ref;
1224
- if (refPath && !resolvingRefs.has(refPath)) try {
1225
- const referenced = resolveRef(document, refPath);
1226
- if (referenced) {
1227
- resolvingRefs.add(refPath);
1228
- resolvedSchema = parseSchema({ schema: referenced }, rawOptions);
1229
- resolvingRefs.delete(refPath);
1368
+ if (refPath && !resolvingRefs.has(refPath)) {
1369
+ if (!resolvedRefCache.has(refPath)) {
1370
+ try {
1371
+ const referenced = dialect.schema.resolveRef(document, refPath);
1372
+ if (referenced) {
1373
+ resolvingRefs.add(refPath);
1374
+ resolvedSchema = parseSchema({ schema: referenced }, rawOptions);
1375
+ resolvingRefs.delete(refPath);
1376
+ }
1377
+ } catch {}
1378
+ resolvedRefCache.set(refPath, resolvedSchema);
1230
1379
  }
1231
- } catch {}
1232
- return _kubb_core.ast.createSchema({
1380
+ resolvedSchema = resolvedRefCache.get(refPath) ?? null;
1381
+ }
1382
+ if (refPath && document.components && !refExists(refPath)) return _kubb_ast.ast.factory.createSchema({
1383
+ ...buildSchemaNode(schema, name, nullable, defaultValue),
1384
+ type: "unknown"
1385
+ });
1386
+ return _kubb_ast.ast.factory.createSchema({
1233
1387
  ...buildSchemaNode(schema, name, nullable, defaultValue),
1234
1388
  type: "ref",
1235
- name: _kubb_core.ast.extractRefName(schema.$ref),
1389
+ name: (0, _kubb_ast.extractRefName)(schema.$ref),
1236
1390
  ref: schema.$ref,
1237
1391
  schema: resolvedSchema
1238
1392
  });
@@ -1245,12 +1399,12 @@ function createSchemaParser(ctx) {
1245
1399
  const [memberSchema] = schema.allOf;
1246
1400
  const memberNode = parseSchema({
1247
1401
  schema: memberSchema,
1248
- name: null
1402
+ name
1249
1403
  }, rawOptions);
1250
1404
  const { kind: _kind, ...memberNodeProps } = memberNode;
1251
1405
  const mergedNullable = nullable || memberNode.nullable || void 0;
1252
1406
  const mergedDefault = schema.default === null && mergedNullable ? void 0 : schema.default ?? memberNode.default;
1253
- return _kubb_core.ast.createSchema({
1407
+ return _kubb_ast.ast.factory.createSchema({
1254
1408
  ...memberNodeProps,
1255
1409
  name,
1256
1410
  title: schema.title ?? memberNode.title,
@@ -1260,22 +1414,23 @@ function createSchemaParser(ctx) {
1260
1414
  readOnly: schema.readOnly ?? memberNode.readOnly,
1261
1415
  writeOnly: schema.writeOnly ?? memberNode.writeOnly,
1262
1416
  default: mergedDefault,
1263
- example: schema.example ?? memberNode.example,
1264
- pattern: schema.pattern ?? ("pattern" in memberNode ? memberNode.pattern : void 0)
1417
+ examples: extractExamples(schema) ?? memberNode.examples,
1418
+ pattern: schema.pattern ?? ("pattern" in memberNode ? memberNode.pattern : void 0),
1419
+ format: schema.format ?? memberNode.format
1265
1420
  });
1266
1421
  }
1267
1422
  const filteredDiscriminantValues = [];
1268
1423
  const allOfMembers = schema.allOf.filter((item) => {
1269
- if (!isReference(item) || !name) return true;
1270
- const deref = resolveRef(document, item.$ref);
1271
- if (!deref || !isDiscriminator(deref)) return true;
1424
+ if (!dialect.schema.isReference(item) || !name) return true;
1425
+ const deref = dialect.schema.resolveRef(document, item.$ref);
1426
+ if (!deref || !dialect.schema.isDiscriminator(deref)) return true;
1272
1427
  const parentUnion = deref.oneOf ?? deref.anyOf;
1273
1428
  if (!parentUnion) return true;
1274
1429
  const childRef = `${SCHEMA_REF_PREFIX}${name}`;
1275
- const inOneOf = parentUnion.some((oneOfItem) => isReference(oneOfItem) && oneOfItem.$ref === childRef);
1430
+ const inOneOf = parentUnion.some((oneOfItem) => dialect.schema.isReference(oneOfItem) && oneOfItem.$ref === childRef);
1276
1431
  const inMapping = Object.values(deref.discriminator.mapping ?? {}).some((v) => v === childRef);
1277
1432
  if (inOneOf || inMapping) {
1278
- const discriminatorValue = _kubb_core.ast.findDiscriminator(deref.discriminator.mapping, childRef);
1433
+ const discriminatorValue = findDiscriminator(deref.discriminator.mapping, childRef);
1279
1434
  if (discriminatorValue) filteredDiscriminantValues.push({
1280
1435
  propertyName: deref.discriminator.propertyName,
1281
1436
  value: discriminatorValue
@@ -1283,22 +1438,28 @@ function createSchemaParser(ctx) {
1283
1438
  return false;
1284
1439
  }
1285
1440
  return true;
1286
- }).map((s) => parseSchema({ schema: s }, rawOptions));
1441
+ }).map((s) => parseSchema({
1442
+ schema: s,
1443
+ name
1444
+ }, rawOptions));
1287
1445
  const syntheticStart = allOfMembers.length;
1288
1446
  if (Array.isArray(schema.required) && schema.required.length) {
1289
1447
  const outerKeys = schema.properties ? new Set(Object.keys(schema.properties)) : /* @__PURE__ */ new Set();
1290
1448
  const missingRequired = schema.required.filter((key) => !outerKeys.has(key));
1291
1449
  if (missingRequired.length) {
1292
1450
  const resolvedMembers = schema.allOf.flatMap((item) => {
1293
- if (!isReference(item)) return [item];
1294
- const deref = resolveRef(document, item.$ref);
1295
- return deref && !isReference(deref) ? [deref] : [];
1451
+ if (!dialect.schema.isReference(item)) return [item];
1452
+ const deref = dialect.schema.resolveRef(document, item.$ref);
1453
+ return deref && !dialect.schema.isReference(deref) ? [deref] : [];
1296
1454
  });
1297
1455
  for (const key of missingRequired) for (const resolved of resolvedMembers) if (resolved.properties?.[key]) {
1298
- allOfMembers.push(parseSchema({ schema: {
1299
- properties: { [key]: resolved.properties[key] },
1300
- required: [key]
1301
- } }, rawOptions));
1456
+ allOfMembers.push(parseSchema({
1457
+ schema: {
1458
+ properties: { [key]: resolved.properties[key] },
1459
+ required: [key]
1460
+ },
1461
+ name
1462
+ }, rawOptions));
1302
1463
  break;
1303
1464
  }
1304
1465
  }
@@ -1307,13 +1468,13 @@ function createSchemaParser(ctx) {
1307
1468
  const { allOf: _allOf, ...schemaWithoutAllOf } = schema;
1308
1469
  allOfMembers.push(parseSchema({ schema: schemaWithoutAllOf }, rawOptions));
1309
1470
  }
1310
- for (const { propertyName, value } of filteredDiscriminantValues) allOfMembers.push(_kubb_core.ast.createDiscriminantNode({
1471
+ for (const { propertyName, value } of filteredDiscriminantValues) allOfMembers.push(createDiscriminantNode({
1311
1472
  propertyName,
1312
1473
  value
1313
1474
  }));
1314
- return _kubb_core.ast.createSchema({
1475
+ return _kubb_ast.ast.factory.createSchema({
1315
1476
  type: "intersection",
1316
- members: [..._kubb_core.ast.mergeAdjacentObjects(allOfMembers.slice(0, syntheticStart)), ..._kubb_core.ast.mergeAdjacentObjects(allOfMembers.slice(syntheticStart))],
1477
+ members: [...(0, _kubb_ast.mergeAdjacentObjectsLazy)(allOfMembers.slice(0, syntheticStart)), ...(0, _kubb_ast.mergeAdjacentObjectsLazy)(allOfMembers.slice(syntheticStart))],
1317
1478
  ...buildSchemaNode(schema, name, nullable, defaultValue)
1318
1479
  });
1319
1480
  }
@@ -1322,81 +1483,106 @@ function createSchemaParser(ctx) {
1322
1483
  */
1323
1484
  function convertUnion({ schema, name, nullable, defaultValue, rawOptions }) {
1324
1485
  function pickDiscriminatorPropertyNode(node, propertyName) {
1325
- const discriminatorProperty = _kubb_core.ast.narrowSchema(node, "object")?.properties?.find((property) => property.name === propertyName);
1486
+ const discriminatorProperty = _kubb_ast.ast.narrowSchema(node, "object")?.properties?.find((property) => property.name === propertyName);
1326
1487
  if (!discriminatorProperty) return null;
1327
- return _kubb_core.ast.createSchema({
1488
+ return _kubb_ast.ast.factory.createSchema({
1328
1489
  type: "object",
1329
1490
  primitive: "object",
1330
1491
  properties: [discriminatorProperty]
1331
1492
  });
1332
1493
  }
1494
+ function resolveRefSilent($ref) {
1495
+ if (!$ref.startsWith("#")) return null;
1496
+ return decodeURIComponent($ref.substring(1)).split("/").filter(Boolean).reduce((obj, key) => obj?.[key], document) ?? null;
1497
+ }
1498
+ function implicitDiscriminantValue(member) {
1499
+ if (!discriminator || discriminator.mapping || !dialect.schema.isReference(member)) return null;
1500
+ const value = (0, _kubb_ast.extractRefName)(member.$ref);
1501
+ if (!value) return null;
1502
+ const variant = resolveRefSilent(member.$ref);
1503
+ if (!variant) return null;
1504
+ const propertyName = discriminator.propertyName;
1505
+ const seen = /* @__PURE__ */ new Set([member.$ref]);
1506
+ function constrains(v) {
1507
+ const prop = v.properties?.[propertyName];
1508
+ const resolved = prop && dialect.schema.isReference(prop) ? resolveRefSilent(prop.$ref) : prop;
1509
+ if (resolved && (Array.isArray(resolved.enum) || resolved.const !== void 0)) return true;
1510
+ const composition = v.allOf ?? v.oneOf ?? v.anyOf;
1511
+ if (!composition) return false;
1512
+ return composition.some((m) => {
1513
+ if (!dialect.schema.isReference(m)) return constrains(m);
1514
+ if (seen.has(m.$ref)) return false;
1515
+ seen.add(m.$ref);
1516
+ const r = resolveRefSilent(m.$ref);
1517
+ return r ? constrains(r) : false;
1518
+ });
1519
+ }
1520
+ return constrains(variant) ? null : value;
1521
+ }
1333
1522
  const unionMembers = [...schema.oneOf ?? [], ...schema.anyOf ?? []];
1334
1523
  const strategy = schema.oneOf ? "one" : "any";
1335
1524
  const unionBase = {
1336
1525
  ...buildSchemaNode(schema, name, nullable, defaultValue),
1337
- discriminatorPropertyName: isDiscriminator(schema) ? schema.discriminator.propertyName : void 0,
1526
+ discriminatorPropertyName: dialect.schema.isDiscriminator(schema) ? schema.discriminator.propertyName : void 0,
1338
1527
  strategy
1339
1528
  };
1340
- const discriminator = isDiscriminator(schema) ? schema.discriminator : void 0;
1341
- const sharedPropertiesNode = schema.properties ? (() => {
1342
- const { oneOf: _oneOf, anyOf: _anyOf, ...schemaWithoutUnion } = schema;
1343
- return parseSchema({
1344
- schema: discriminator ? Object.fromEntries(Object.entries(schemaWithoutUnion).filter(([key]) => key !== "discriminator")) : schemaWithoutUnion,
1345
- name
1346
- }, rawOptions);
1347
- })() : void 0;
1348
- if (sharedPropertiesNode || discriminator?.mapping) {
1529
+ const discriminator = dialect.schema.isDiscriminator(schema) ? schema.discriminator : void 0;
1530
+ const { oneOf: _o, anyOf: _a, discriminator: _d, ...memberBaseSchema } = schema;
1531
+ const sharedPropertiesNode = schema.properties ? parseSchema({
1532
+ schema: memberBaseSchema,
1533
+ name
1534
+ }, rawOptions) : void 0;
1535
+ if (sharedPropertiesNode || discriminator) {
1349
1536
  const members = unionMembers.map((s) => {
1350
- const ref = isReference(s) ? s.$ref : void 0;
1351
- const discriminatorValue = _kubb_core.ast.findDiscriminator(discriminator?.mapping, ref);
1352
- const memberNode = parseSchema({ schema: s }, rawOptions);
1537
+ const ref = dialect.schema.isReference(s) ? s.$ref : void 0;
1538
+ const discriminatorValue = findDiscriminator(discriminator?.mapping, ref) ?? implicitDiscriminantValue(s);
1539
+ const memberNode = parseSchema({
1540
+ schema: s,
1541
+ name
1542
+ }, rawOptions);
1353
1543
  if (!discriminatorValue || !discriminator) return memberNode;
1354
- const narrowedDiscriminatorNode = sharedPropertiesNode ? pickDiscriminatorPropertyNode(_kubb_core.ast.setDiscriminatorEnum({
1355
- node: sharedPropertiesNode,
1544
+ const narrowedDiscriminatorNode = sharedPropertiesNode ? pickDiscriminatorPropertyNode(_kubb_ast.ast.applyMacros(sharedPropertiesNode, [(0, _kubb_ast.macroDiscriminatorEnum)({
1356
1545
  propertyName: discriminator.propertyName,
1357
1546
  values: [discriminatorValue]
1358
- }), discriminator.propertyName) : void 0;
1359
- return _kubb_core.ast.createSchema({
1547
+ })], { depth: "shallow" }), discriminator.propertyName) : void 0;
1548
+ return _kubb_ast.ast.factory.createSchema({
1360
1549
  type: "intersection",
1361
- members: [memberNode, narrowedDiscriminatorNode ?? _kubb_core.ast.createDiscriminantNode({
1550
+ members: [memberNode, narrowedDiscriminatorNode ?? createDiscriminantNode({
1362
1551
  propertyName: discriminator.propertyName,
1363
1552
  value: discriminatorValue
1364
1553
  })]
1365
1554
  });
1366
1555
  });
1367
- const unionNode = _kubb_core.ast.createSchema({
1556
+ const unionNode = _kubb_ast.ast.factory.createSchema({
1368
1557
  type: "union",
1369
1558
  ...unionBase,
1370
1559
  members
1371
1560
  });
1372
1561
  if (!sharedPropertiesNode) return unionNode;
1373
- return _kubb_core.ast.createSchema({
1562
+ return _kubb_ast.ast.factory.createSchema({
1374
1563
  type: "intersection",
1375
1564
  ...buildSchemaNode(schema, name, nullable, defaultValue),
1376
1565
  members: [unionNode, sharedPropertiesNode]
1377
1566
  });
1378
1567
  }
1379
- return _kubb_core.ast.createSchema({
1568
+ const unionNode = _kubb_ast.ast.factory.createSchema({
1380
1569
  type: "union",
1381
1570
  ...unionBase,
1382
- members: _kubb_core.ast.simplifyUnion(unionMembers.map((s) => parseSchema({ schema: s }, rawOptions)))
1571
+ members: unionMembers.map((s) => parseSchema({
1572
+ schema: s,
1573
+ name
1574
+ }, rawOptions))
1383
1575
  });
1576
+ return _kubb_ast.ast.applyMacros(unionNode, [_kubb_ast.macroSimplifyUnion], { depth: "shallow" });
1384
1577
  }
1385
1578
  /**
1386
1579
  * Converts an OAS 3.1 `const` schema into a null scalar or a single-value `EnumSchemaNode`.
1387
1580
  */
1388
1581
  function convertConst({ schema, name, nullable, defaultValue }) {
1389
1582
  const constValue = schema.const;
1390
- if (constValue === null) return _kubb_core.ast.createSchema({
1391
- type: "null",
1392
- primitive: "null",
1393
- name,
1394
- title: schema.title,
1395
- description: schema.description,
1396
- deprecated: schema.deprecated
1397
- });
1583
+ if (constValue === null) return createNullSchema(schema, name);
1398
1584
  const constPrimitive = getPrimitiveType(typeof constValue === "number" ? "number" : typeof constValue === "boolean" ? "boolean" : "string");
1399
- return _kubb_core.ast.createSchema({
1585
+ return _kubb_ast.ast.factory.createSchema({
1400
1586
  type: "enum",
1401
1587
  primitive: constPrimitive,
1402
1588
  enumValues: [constValue],
@@ -1409,7 +1595,7 @@ function createSchemaParser(ctx) {
1409
1595
  */
1410
1596
  function convertFormat({ schema, name, nullable, defaultValue, options }) {
1411
1597
  const base = buildSchemaNode(schema, name, nullable, defaultValue);
1412
- if (schema.format === "int64") return _kubb_core.ast.createSchema({
1598
+ if (schema.format === "int64") return _kubb_ast.ast.factory.createSchema({
1413
1599
  type: options.integerType === "bigint" ? "bigint" : "integer",
1414
1600
  primitive: "integer",
1415
1601
  ...base,
@@ -1421,14 +1607,14 @@ function createSchemaParser(ctx) {
1421
1607
  if (schema.format === "date-time" || schema.format === "date" || schema.format === "time") {
1422
1608
  const dateType = getDateType(options, schema.format);
1423
1609
  if (!dateType) return null;
1424
- if (dateType.type === "datetime") return _kubb_core.ast.createSchema({
1610
+ if (dateType.type === "datetime") return _kubb_ast.ast.factory.createSchema({
1425
1611
  ...base,
1426
1612
  primitive: "string",
1427
1613
  type: "datetime",
1428
1614
  offset: dateType.offset,
1429
1615
  local: dateType.local
1430
1616
  });
1431
- return _kubb_core.ast.createSchema({
1617
+ return _kubb_ast.ast.factory.createSchema({
1432
1618
  ...base,
1433
1619
  primitive: "string",
1434
1620
  type: dateType.type,
@@ -1438,36 +1624,36 @@ function createSchemaParser(ctx) {
1438
1624
  const specialType = getSchemaType(schema.format);
1439
1625
  if (!specialType) return null;
1440
1626
  const specialPrimitive = specialType === "number" || specialType === "integer" || specialType === "bigint" ? specialType : "string";
1441
- if (specialType === "number" || specialType === "integer" || specialType === "bigint") return _kubb_core.ast.createSchema({
1627
+ if (specialType === "number" || specialType === "integer" || specialType === "bigint") return _kubb_ast.ast.factory.createSchema({
1442
1628
  ...base,
1443
1629
  primitive: specialPrimitive,
1444
1630
  type: specialType
1445
1631
  });
1446
- if (specialType === "url") return _kubb_core.ast.createSchema({
1632
+ if (specialType === "url") return _kubb_ast.ast.factory.createSchema({
1447
1633
  ...base,
1448
1634
  primitive: "string",
1449
1635
  type: "url",
1450
1636
  min: schema.minLength,
1451
1637
  max: schema.maxLength
1452
1638
  });
1453
- if (specialType === "ipv4") return _kubb_core.ast.createSchema({
1639
+ if (specialType === "ipv4") return _kubb_ast.ast.factory.createSchema({
1454
1640
  ...base,
1455
1641
  primitive: "string",
1456
1642
  type: "ipv4"
1457
1643
  });
1458
- if (specialType === "ipv6") return _kubb_core.ast.createSchema({
1644
+ if (specialType === "ipv6") return _kubb_ast.ast.factory.createSchema({
1459
1645
  ...base,
1460
1646
  primitive: "string",
1461
1647
  type: "ipv6"
1462
1648
  });
1463
- if (specialType === "uuid" || specialType === "email") return _kubb_core.ast.createSchema({
1649
+ if (specialType === "uuid" || specialType === "email") return _kubb_ast.ast.factory.createSchema({
1464
1650
  ...base,
1465
1651
  primitive: "string",
1466
1652
  type: specialType,
1467
1653
  min: schema.minLength,
1468
1654
  max: schema.maxLength
1469
1655
  });
1470
- return _kubb_core.ast.createSchema({
1656
+ return _kubb_ast.ast.factory.createSchema({
1471
1657
  ...base,
1472
1658
  primitive: specialPrimitive,
1473
1659
  type: specialType
@@ -1483,6 +1669,7 @@ function createSchemaParser(ctx) {
1483
1669
  }, rawOptions);
1484
1670
  const nullInEnum = schema.enum.includes(null);
1485
1671
  const filteredValues = nullInEnum ? schema.enum.filter((v) => v !== null) : schema.enum;
1672
+ if (nullInEnum && filteredValues.length === 0) return createNullSchema(schema, name);
1486
1673
  const enumNullable = nullable || nullInEnum || void 0;
1487
1674
  const enumDefault = schema.default === null && enumNullable ? void 0 : schema.default;
1488
1675
  const enumPrimitive = getPrimitiveType(type);
@@ -1497,21 +1684,25 @@ function createSchemaParser(ctx) {
1497
1684
  readOnly: schema.readOnly,
1498
1685
  writeOnly: schema.writeOnly,
1499
1686
  default: enumDefault,
1500
- example: schema.example
1687
+ examples: extractExamples(schema),
1688
+ format: schema.format
1501
1689
  };
1502
1690
  const extensionKey = enumExtensionKeys.find((key) => key in schema);
1503
- if (extensionKey || enumPrimitive === "number" || enumPrimitive === "integer" || enumPrimitive === "boolean") {
1691
+ const descriptionKey = enumDescriptionKeys.find((key) => key in schema);
1692
+ if (extensionKey || descriptionKey || enumPrimitive === "number" || enumPrimitive === "integer" || enumPrimitive === "boolean") {
1504
1693
  const enumPrimitiveType = enumPrimitive === "number" || enumPrimitive === "integer" ? "number" : enumPrimitive === "boolean" ? "boolean" : "string";
1505
1694
  const rawEnumNames = extensionKey ? schema[extensionKey] : void 0;
1695
+ const rawEnumDescriptions = descriptionKey ? schema[descriptionKey] : void 0;
1506
1696
  const uniqueValues = [...new Set(filteredValues)];
1507
1697
  const seenNames = /* @__PURE__ */ new Set();
1508
- return _kubb_core.ast.createSchema({
1698
+ return _kubb_ast.ast.factory.createSchema({
1509
1699
  ...enumBase,
1510
1700
  primitive: enumPrimitiveType,
1511
1701
  namedEnumValues: uniqueValues.map((value, index) => ({
1512
1702
  name: String(rawEnumNames?.[index] ?? value),
1513
1703
  value,
1514
- primitive: enumPrimitiveType
1704
+ primitive: enumPrimitiveType,
1705
+ description: rawEnumDescriptions?.[index]
1515
1706
  })).filter((entry) => {
1516
1707
  if (seenNames.has(entry.name)) return false;
1517
1708
  seenNames.add(entry.name);
@@ -1519,7 +1710,7 @@ function createSchemaParser(ctx) {
1519
1710
  })
1520
1711
  });
1521
1712
  }
1522
- return _kubb_core.ast.createSchema({
1713
+ return _kubb_ast.ast.factory.createSchema({
1523
1714
  ...enumBase,
1524
1715
  enumValues: [...new Set(filteredValues)]
1525
1716
  });
@@ -1531,21 +1722,16 @@ function createSchemaParser(ctx) {
1531
1722
  const properties = schema.properties ? Object.entries(schema.properties).map(([propName, propSchema]) => {
1532
1723
  const required = Array.isArray(schema.required) ? schema.required.includes(propName) : !!schema.required;
1533
1724
  const resolvedPropSchema = propSchema;
1534
- const propNullable = isNullable(resolvedPropSchema);
1535
- const propNode = parseSchema({
1725
+ const propNullable = dialect.schema.isNullable(resolvedPropSchema);
1726
+ const schemaNode = nameEnums(parseSchema({
1536
1727
  schema: resolvedPropSchema,
1537
- name: _kubb_core.ast.childName(name, propName)
1538
- }, rawOptions);
1539
- let schemaNode = _kubb_core.ast.setEnumName(propNode, name, propName, options.enumSuffix);
1540
- const tupleNode = _kubb_core.ast.narrowSchema(schemaNode, "tuple");
1541
- if (tupleNode?.items) {
1542
- const namedItems = tupleNode.items.map((item) => _kubb_core.ast.setEnumName(item, name, propName, options.enumSuffix));
1543
- if (namedItems.some((item, i) => item !== tupleNode.items[i])) schemaNode = {
1544
- ...tupleNode,
1545
- items: namedItems
1546
- };
1547
- }
1548
- return _kubb_core.ast.createProperty({
1728
+ name: (0, _kubb_ast.childName)(name, propName)
1729
+ }, rawOptions), {
1730
+ parentName: name,
1731
+ propName,
1732
+ enumSuffix: options.enumSuffix
1733
+ });
1734
+ return _kubb_ast.ast.factory.createProperty({
1549
1735
  name: propName,
1550
1736
  schema: {
1551
1737
  ...schemaNode,
@@ -1555,14 +1741,15 @@ function createSchemaParser(ctx) {
1555
1741
  });
1556
1742
  }) : [];
1557
1743
  const additionalProperties = schema.additionalProperties;
1558
- let additionalPropertiesNode;
1559
- if (additionalProperties === true) additionalPropertiesNode = true;
1560
- else if (additionalProperties && Object.keys(additionalProperties).length > 0) additionalPropertiesNode = parseSchema({ schema: additionalProperties }, rawOptions);
1561
- else if (additionalProperties === false) additionalPropertiesNode = false;
1562
- else if (additionalProperties) additionalPropertiesNode = _kubb_core.ast.createSchema({ type: typeOptionMap.get(options.unknownType) });
1744
+ const additionalPropertiesNode = (() => {
1745
+ if (additionalProperties === true) return true;
1746
+ if (additionalProperties === false) return false;
1747
+ if (additionalProperties && Object.keys(additionalProperties).length > 0) return parseSchema({ schema: additionalProperties }, rawOptions);
1748
+ if (additionalProperties) return _kubb_ast.ast.factory.createSchema({ type: options.unknownType });
1749
+ })();
1563
1750
  const rawPatternProperties = "patternProperties" in schema ? schema.patternProperties : void 0;
1564
- const patternProperties = rawPatternProperties ? Object.fromEntries(Object.entries(rawPatternProperties).map(([pattern, patternSchema]) => [pattern, patternSchema === true || typeof patternSchema === "object" && Object.keys(patternSchema).length === 0 ? _kubb_core.ast.createSchema({ type: typeOptionMap.get(options.unknownType) }) : parseSchema({ schema: patternSchema }, rawOptions)])) : void 0;
1565
- const objectNode = _kubb_core.ast.createSchema({
1751
+ const patternProperties = rawPatternProperties ? Object.fromEntries(Object.entries(rawPatternProperties).map(([pattern, patternSchema]) => [pattern, patternSchema === true || typeof patternSchema === "object" && Object.keys(patternSchema).length === 0 ? _kubb_ast.ast.factory.createSchema({ type: options.unknownType }) : parseSchema({ schema: patternSchema }, rawOptions)])) : void 0;
1752
+ const objectNode = _kubb_ast.ast.factory.createSchema({
1566
1753
  type: "object",
1567
1754
  primitive: "object",
1568
1755
  properties,
@@ -1572,16 +1759,15 @@ function createSchemaParser(ctx) {
1572
1759
  maxProperties: schema.maxProperties,
1573
1760
  ...buildSchemaNode(schema, name, nullable, defaultValue)
1574
1761
  });
1575
- if (isDiscriminator(schema) && schema.discriminator.mapping) {
1762
+ if (dialect.schema.isDiscriminator(schema) && schema.discriminator.mapping) {
1576
1763
  const discPropName = schema.discriminator.propertyName;
1577
1764
  const values = Object.keys(schema.discriminator.mapping);
1578
- const enumName = name ? _kubb_core.ast.enumPropName(name, discPropName, options.enumSuffix) : void 0;
1579
- return _kubb_core.ast.setDiscriminatorEnum({
1580
- node: objectNode,
1765
+ const enumName = name ? (0, _kubb_ast.enumPropName)(name, discPropName, options.enumSuffix) : void 0;
1766
+ return _kubb_ast.ast.applyMacros(objectNode, [(0, _kubb_ast.macroDiscriminatorEnum)({
1581
1767
  propertyName: discPropName,
1582
1768
  values,
1583
1769
  enumName
1584
- });
1770
+ })], { depth: "shallow" });
1585
1771
  }
1586
1772
  return objectNode;
1587
1773
  }
@@ -1590,8 +1776,8 @@ function createSchemaParser(ctx) {
1590
1776
  */
1591
1777
  function convertTuple({ schema, name, nullable, defaultValue, rawOptions }) {
1592
1778
  const tupleItems = (schema.prefixItems ?? []).map((item) => parseSchema({ schema: item }, rawOptions));
1593
- const rest = schema.items ? parseSchema({ schema: schema.items }, rawOptions) : _kubb_core.ast.createSchema({ type: "any" });
1594
- return _kubb_core.ast.createSchema({
1779
+ const rest = schema.items === false ? void 0 : !schema.items || schema.items === true ? _kubb_ast.ast.factory.createSchema({ type: "any" }) : parseSchema({ schema: schema.items }, rawOptions);
1780
+ return _kubb_ast.ast.factory.createSchema({
1595
1781
  type: "tuple",
1596
1782
  primitive: "array",
1597
1783
  items: tupleItems,
@@ -1606,12 +1792,12 @@ function createSchemaParser(ctx) {
1606
1792
  */
1607
1793
  function convertArray({ schema, name, nullable, defaultValue, rawOptions, options }) {
1608
1794
  const rawItems = schema.items;
1609
- const itemName = rawItems?.enum?.length && name ? _kubb_core.ast.enumPropName(void 0, name, options.enumSuffix) : void 0;
1795
+ const itemName = rawItems?.enum?.length && name ? (0, _kubb_ast.enumPropName)(null, name, options.enumSuffix) : name;
1610
1796
  const items = rawItems ? [parseSchema({
1611
1797
  schema: rawItems,
1612
1798
  name: itemName
1613
1799
  }, rawOptions)] : [];
1614
- return _kubb_core.ast.createSchema({
1800
+ return _kubb_ast.ast.factory.createSchema({
1615
1801
  type: "array",
1616
1802
  primitive: "array",
1617
1803
  items,
@@ -1625,7 +1811,7 @@ function createSchemaParser(ctx) {
1625
1811
  * Converts a `type: 'string'` schema into a `StringSchemaNode`.
1626
1812
  */
1627
1813
  function convertString({ schema, name, nullable, defaultValue }) {
1628
- return _kubb_core.ast.createSchema({
1814
+ return _kubb_ast.ast.factory.createSchema({
1629
1815
  type: "string",
1630
1816
  primitive: "string",
1631
1817
  min: schema.minLength,
@@ -1638,7 +1824,7 @@ function createSchemaParser(ctx) {
1638
1824
  * Converts a `type: 'number'` or `type: 'integer'` schema.
1639
1825
  */
1640
1826
  function convertNumeric({ schema, name, nullable, defaultValue }, type) {
1641
- return _kubb_core.ast.createSchema({
1827
+ return _kubb_ast.ast.factory.createSchema({
1642
1828
  type,
1643
1829
  primitive: type,
1644
1830
  min: schema.minimum,
@@ -1653,32 +1839,132 @@ function createSchemaParser(ctx) {
1653
1839
  * Converts a `type: 'boolean'` schema.
1654
1840
  */
1655
1841
  function convertBoolean({ schema, name, nullable, defaultValue }) {
1656
- return _kubb_core.ast.createSchema({
1842
+ return _kubb_ast.ast.factory.createSchema({
1657
1843
  type: "boolean",
1658
1844
  primitive: "boolean",
1659
1845
  ...buildSchemaNode(schema, name, nullable, defaultValue)
1660
1846
  });
1661
1847
  }
1662
1848
  /**
1663
- * Converts an explicit `type: 'null'` schema.
1849
+ * Converts a binary string schema (`type: 'string'`, `contentMediaType: 'application/octet-stream'`)
1850
+ * into a `blob` node.
1664
1851
  */
1665
- function convertNull({ schema, name, nullable }) {
1666
- return _kubb_core.ast.createSchema({
1667
- type: "null",
1668
- primitive: "null",
1669
- name,
1670
- title: schema.title,
1671
- description: schema.description,
1672
- deprecated: schema.deprecated,
1673
- nullable
1852
+ function convertBlob({ schema, name, nullable, defaultValue }) {
1853
+ return _kubb_ast.ast.factory.createSchema({
1854
+ type: "blob",
1855
+ primitive: "string",
1856
+ ...buildSchemaNode(schema, name, nullable, defaultValue)
1857
+ });
1858
+ }
1859
+ /**
1860
+ * Converts an OAS 3.1 multi-type array (e.g. `type: ['string', 'number']`) into a `UnionSchemaNode`.
1861
+ *
1862
+ * Returns `null` when only one non-`null` type remains (e.g. `['string', 'null']`), so `parseSchema`
1863
+ * falls through and handles it as that single type with nullability already folded in.
1864
+ */
1865
+ function convertMultiType({ schema, name, nullable, defaultValue, rawOptions }) {
1866
+ const types = schema.type;
1867
+ const nonNullTypes = types.filter((t) => t !== "null");
1868
+ if (nonNullTypes.length <= 1) return null;
1869
+ const arrayNullable = types.includes("null") || nullable || void 0;
1870
+ return _kubb_ast.ast.factory.createSchema({
1871
+ type: "union",
1872
+ members: nonNullTypes.map((t) => parseSchema({
1873
+ schema: {
1874
+ ...schema,
1875
+ type: t
1876
+ },
1877
+ name
1878
+ }, rawOptions)),
1879
+ ...buildSchemaNode(schema, name, arrayNullable, defaultValue)
1674
1880
  });
1675
1881
  }
1676
1882
  /**
1677
- * Central dispatcher that converts an OAS `SchemaObject` into a `SchemaNode`.
1883
+ * Ordered schema rule table. Order is significant: composition keywords (`$ref`, `allOf`,
1884
+ * `oneOf`/`anyOf`) take precedence over `const`/`format`, which take precedence over the plain
1885
+ * `type`. The first matching rule that produces a node wins. See {@link SchemaRule} for the
1886
+ * match/convert/fall-through contract.
1887
+ */
1888
+ const schemaRules = [
1889
+ {
1890
+ match: ({ schema }) => dialect.schema.isReference(schema),
1891
+ convert: convertRef
1892
+ },
1893
+ {
1894
+ match: ({ schema }) => !!schema.allOf?.length,
1895
+ convert: convertAllOf
1896
+ },
1897
+ {
1898
+ match: ({ schema }) => !!(schema.oneOf?.length || schema.anyOf?.length),
1899
+ convert: convertUnion
1900
+ },
1901
+ {
1902
+ match: ({ schema }) => "const" in schema && schema.const !== void 0,
1903
+ convert: convertConst
1904
+ },
1905
+ {
1906
+ match: ({ schema }) => !!schema.format,
1907
+ convert: convertFormat
1908
+ },
1909
+ {
1910
+ match: ({ schema }) => dialect.schema.isBinary(schema),
1911
+ convert: convertBlob
1912
+ },
1913
+ {
1914
+ match: ({ schema }) => Array.isArray(schema.type) && schema.type.length > 1,
1915
+ convert: convertMultiType
1916
+ },
1917
+ {
1918
+ match: ({ schema, type }) => !type && (schema.minLength !== void 0 || schema.maxLength !== void 0 || schema.pattern !== void 0),
1919
+ convert: convertString
1920
+ },
1921
+ {
1922
+ match: ({ schema, type }) => !type && (schema.minimum !== void 0 || schema.maximum !== void 0),
1923
+ convert: (ctx) => convertNumeric(ctx, "number")
1924
+ },
1925
+ {
1926
+ match: ({ schema }) => !!schema.enum?.length,
1927
+ convert: convertEnum
1928
+ },
1929
+ {
1930
+ match: ({ schema, type }) => type === "object" || !!schema.properties || !!schema.additionalProperties || "patternProperties" in schema,
1931
+ convert: convertObject
1932
+ },
1933
+ {
1934
+ match: ({ schema }) => "prefixItems" in schema,
1935
+ convert: convertTuple
1936
+ },
1937
+ {
1938
+ match: ({ schema, type }) => type === "array" || "items" in schema,
1939
+ convert: convertArray
1940
+ },
1941
+ {
1942
+ match: ({ type }) => type === "string",
1943
+ convert: convertString
1944
+ },
1945
+ {
1946
+ match: ({ type }) => type === "number",
1947
+ convert: (ctx) => convertNumeric(ctx, "number")
1948
+ },
1949
+ {
1950
+ match: ({ type }) => type === "integer",
1951
+ convert: (ctx) => convertNumeric(ctx, "integer")
1952
+ },
1953
+ {
1954
+ match: ({ type }) => type === "boolean",
1955
+ convert: convertBoolean
1956
+ },
1957
+ {
1958
+ match: ({ type }) => type === "null",
1959
+ convert: ({ schema, name, nullable }) => createNullSchema(schema, name, nullable)
1960
+ }
1961
+ ];
1962
+ /**
1963
+ * Converts an OAS `SchemaObject` into a `SchemaNode`.
1678
1964
  *
1679
- * Dispatch order (first match wins): `$ref` `allOf` `oneOf`/`anyOf` `const` → `format`
1680
- * octet-stream blob multi-type array constraint-inferred type `enum` object/array/tuple/scalar
1681
- * → empty-schema fallback (`emptySchemaType` option).
1965
+ * Builds the per-schema context, then walks the ordered {@link schemaRules} table and returns
1966
+ * the first converter that produces a node. When none match, falls back to the configured
1967
+ * `emptySchemaType`.
1682
1968
  */
1683
1969
  function parseSchema({ schema, name }, rawOptions) {
1684
1970
  const options = {
@@ -1690,81 +1976,53 @@ function createSchemaParser(ctx) {
1690
1976
  schema: flattenedSchema,
1691
1977
  name
1692
1978
  }, rawOptions);
1693
- const nullable = isNullable(schema) || void 0;
1694
- const defaultValue = schema.default === null && nullable ? void 0 : schema.default;
1695
- const type = Array.isArray(schema.type) ? schema.type[0] : schema.type;
1696
- const ctx = {
1979
+ const nullable = dialect.schema.isNullable(schema) || void 0;
1980
+ const schemaCtx = {
1697
1981
  schema,
1698
1982
  name,
1699
1983
  nullable,
1700
- defaultValue,
1701
- type,
1984
+ defaultValue: schema.default === null && nullable ? void 0 : schema.default,
1985
+ type: Array.isArray(schema.type) ? schema.type[0] : schema.type,
1702
1986
  rawOptions,
1703
1987
  options
1704
1988
  };
1705
- if (isReference(schema)) return convertRef(ctx);
1706
- if (schema.allOf?.length) return convertAllOf(ctx);
1707
- if ([...schema.oneOf ?? [], ...schema.anyOf ?? []].length) return convertUnion(ctx);
1708
- if ("const" in schema && schema.const !== void 0) return convertConst(ctx);
1709
- if (schema.format) {
1710
- const formatResult = convertFormat(ctx);
1711
- if (formatResult) return formatResult;
1712
- }
1713
- if (schema.type === "string" && schema.contentMediaType === "application/octet-stream") return _kubb_core.ast.createSchema({
1714
- type: "blob",
1715
- primitive: "string",
1716
- ...buildSchemaNode(schema, name, nullable, defaultValue)
1717
- });
1718
- if (Array.isArray(schema.type) && schema.type.length > 1) {
1719
- const nonNullTypes = schema.type.filter((t) => t !== "null");
1720
- const arrayNullable = schema.type.includes("null") || nullable || void 0;
1721
- if (nonNullTypes.length > 1) return _kubb_core.ast.createSchema({
1722
- type: "union",
1723
- members: nonNullTypes.map((t) => parseSchema({
1724
- schema: {
1725
- ...schema,
1726
- type: t
1727
- },
1728
- name
1729
- }, rawOptions)),
1730
- ...buildSchemaNode(schema, name, arrayNullable, defaultValue)
1731
- });
1989
+ for (const rule of schemaRules) {
1990
+ if (!rule.match(schemaCtx)) continue;
1991
+ const node = rule.convert(schemaCtx);
1992
+ if (node) return node;
1732
1993
  }
1733
- if (!type) {
1734
- if (schema.minLength !== void 0 || schema.maxLength !== void 0 || schema.pattern !== void 0) return convertString(ctx);
1735
- if (schema.minimum !== void 0 || schema.maximum !== void 0) return convertNumeric(ctx, "number");
1736
- }
1737
- if (schema.enum?.length) return convertEnum(ctx);
1738
- if (type === "object" || schema.properties || schema.additionalProperties || "patternProperties" in schema) return convertObject(ctx);
1739
- if ("prefixItems" in schema) return convertTuple(ctx);
1740
- if (type === "array" || "items" in schema) return convertArray(ctx);
1741
- if (type === "string") return convertString(ctx);
1742
- if (type === "number") return convertNumeric(ctx, "number");
1743
- if (type === "integer") return convertNumeric(ctx, "integer");
1744
- if (type === "boolean") return convertBoolean(ctx);
1745
- if (type === "null") return convertNull(ctx);
1746
- const emptyType = typeOptionMap.get(options.emptySchemaType);
1747
- return _kubb_core.ast.createSchema({
1994
+ const emptyType = options.emptySchemaType;
1995
+ return _kubb_ast.ast.factory.createSchema({
1748
1996
  type: emptyType,
1749
1997
  name,
1750
1998
  title: schema.title,
1751
- description: schema.description
1999
+ description: schema.description,
2000
+ format: schema.format
1752
2001
  });
1753
2002
  }
1754
2003
  /**
1755
2004
  * Converts a dereferenced OAS parameter object into a `ParameterNode`.
1756
2005
  */
1757
- function parseParameter(options, param) {
2006
+ function parseParameter(options, param, parentName) {
1758
2007
  const required = param["required"] ?? false;
1759
- const schema = param["schema"] ? parseSchema({ schema: param["schema"] }, options) : _kubb_core.ast.createSchema({ type: typeOptionMap.get(options.unknownType) });
1760
- return _kubb_core.ast.createParameter({
1761
- name: param["name"],
2008
+ const paramName = param["name"];
2009
+ const schemaName = parentName && paramName ? pascalCase(`${parentName} ${paramName}`) : void 0;
2010
+ const schema = param["schema"] ? parseSchema({
2011
+ schema: param["schema"],
2012
+ name: schemaName
2013
+ }, options) : _kubb_ast.ast.factory.createSchema({ type: options.unknownType });
2014
+ const style = param["style"];
2015
+ const explode = param["explode"];
2016
+ return _kubb_ast.ast.factory.createParameter({
2017
+ name: paramName,
1762
2018
  in: param["in"],
1763
2019
  schema: {
1764
2020
  ...schema,
1765
2021
  description: param["description"] ?? schema.description
1766
2022
  },
1767
- required
2023
+ required,
2024
+ ...style !== void 0 ? { style } : {},
2025
+ ...explode !== void 0 ? { explode } : {}
1768
2026
  });
1769
2027
  }
1770
2028
  /**
@@ -1780,73 +2038,97 @@ function createSchemaParser(ctx) {
1780
2038
  };
1781
2039
  }
1782
2040
  /**
1783
- * Reads the inline response object (not a `$ref`) and returns its description plus its `content` map.
1784
- */
1785
- function getResponseMeta(responseObj) {
1786
- if (typeof responseObj !== "object" || responseObj === null || Array.isArray(responseObj)) return {};
1787
- const inline = responseObj;
1788
- return {
1789
- description: inline.description,
1790
- content: inline.content
1791
- };
1792
- }
1793
- /**
1794
2041
  * Collects property names whose schema has a truthy boolean flag (`readOnly` or `writeOnly`).
1795
2042
  * `$ref` entries are skipped since their flags live on the dereferenced target.
1796
2043
  */
1797
2044
  function collectPropertyKeysByFlag(schema, flag) {
1798
- if (!schema?.properties) return void 0;
2045
+ if (!schema?.properties) return null;
1799
2046
  const keys = [];
1800
2047
  for (const key in schema.properties) {
1801
2048
  const prop = schema.properties[key];
1802
- if (prop && !isReference(prop) && prop[flag]) keys.push(key);
2049
+ if (prop && !dialect.schema.isReference(prop) && prop[flag]) keys.push(key);
1803
2050
  }
1804
- return keys.length ? keys : void 0;
2051
+ return keys.length ? keys : null;
1805
2052
  }
1806
2053
  /**
1807
2054
  * Converts an OAS `Operation` into an `OperationNode`.
1808
2055
  */
1809
2056
  function parseOperation(options, operation) {
1810
- const parameters = getParameters(document, operation).map((param) => parseParameter(options, param));
2057
+ const operationId = getOperationId(operation);
2058
+ const operationName = operationId ? pascalCase(operationId) : void 0;
2059
+ const parameters = getParameters(document, operation).map((param) => parseParameter(options, param, operationName));
1811
2060
  const allContentTypes = ctx.contentType ? [ctx.contentType] : getRequestBodyContentTypes(document, operation);
1812
2061
  const requestBodyMeta = getRequestBodyMeta(operation);
2062
+ const requestBodyName = operationName ? `${operationName}Request` : void 0;
1813
2063
  const content = allContentTypes.flatMap((ct) => {
1814
2064
  const schema = getRequestSchema(document, operation, { contentType: ct });
1815
2065
  if (!schema) return [];
1816
- return [{
2066
+ return [_kubb_ast.ast.factory.createContent({
1817
2067
  contentType: ct,
1818
- schema: _kubb_core.ast.syncOptionality(parseSchema({ schema }, options), requestBodyMeta.required),
2068
+ schema: _kubb_ast.ast.optionality(parseSchema({
2069
+ schema,
2070
+ name: requestBodyName
2071
+ }, options), requestBodyMeta.required),
1819
2072
  keysToOmit: collectPropertyKeysByFlag(schema, "readOnly")
1820
- }];
2073
+ })];
1821
2074
  });
1822
2075
  const requestBody = content.length > 0 || requestBodyMeta.description ? {
1823
2076
  description: requestBodyMeta.description,
1824
2077
  required: requestBodyMeta.required || void 0,
1825
2078
  content: content.length > 0 ? content : void 0
1826
2079
  } : void 0;
1827
- const responses = operation.getResponseStatusCodes().map((statusCode) => {
1828
- const responseObj = operation.getResponseByStatusCode(statusCode);
1829
- const responseSchema = getResponseSchema(document, operation, statusCode, { contentType: ctx.contentType });
1830
- const schema = responseSchema && Object.keys(responseSchema).length > 0 ? parseSchema({ schema: responseSchema }, options) : _kubb_core.ast.createSchema({ type: typeOptionMap.get(options.emptySchemaType) });
1831
- const { description, content } = getResponseMeta(responseObj);
1832
- const mediaType = content ? getMediaType(Object.keys(content)[0] ?? "") : getMediaType(operation.contentType ?? "");
1833
- return _kubb_core.ast.createResponse({
2080
+ const responses = getResponseStatusCodes(operation).map((statusCode) => {
2081
+ const responseObj = getResponseByStatusCode({
2082
+ document,
2083
+ operation,
2084
+ statusCode
2085
+ });
2086
+ const responseName = operationName ? `${operationName}Status${statusCode}` : void 0;
2087
+ const description = typeof responseObj === "object" && responseObj !== null ? responseObj.description : void 0;
2088
+ const parseEntrySchema = (contentType) => {
2089
+ const raw = getResponseSchema(document, operation, statusCode, { contentType });
2090
+ return {
2091
+ schema: raw && Object.keys(raw).length > 0 ? parseSchema({
2092
+ schema: raw,
2093
+ name: responseName
2094
+ }, options) : _kubb_ast.ast.factory.createSchema({ type: options.emptySchemaType }),
2095
+ keysToOmit: collectPropertyKeysByFlag(raw, "writeOnly")
2096
+ };
2097
+ };
2098
+ const content = (ctx.contentType ? [ctx.contentType] : getResponseBodyContentTypes(document, operation, statusCode)).map((contentType) => _kubb_ast.ast.factory.createContent({
2099
+ contentType,
2100
+ ...parseEntrySchema(contentType)
2101
+ }));
2102
+ if (content.length === 0) content.push(_kubb_ast.ast.factory.createContent({
2103
+ contentType: getRequestContentType({
2104
+ document,
2105
+ operation
2106
+ }) || "application/json",
2107
+ ...parseEntrySchema(ctx.contentType)
2108
+ }));
2109
+ return _kubb_ast.ast.factory.createResponse({
1834
2110
  statusCode,
1835
2111
  description,
1836
- schema,
1837
- mediaType,
1838
- keysToOmit: collectPropertyKeysByFlag(responseSchema, "writeOnly")
2112
+ content
1839
2113
  });
1840
2114
  });
1841
- const urlPath = new URLPath(operation.path);
1842
- return _kubb_core.ast.createOperation({
1843
- operationId: operation.getOperationId(),
2115
+ const pathItem = document.paths?.[operation.path];
2116
+ const pathItemDoc = pathItem && !dialect.schema.isReference(pathItem) ? pathItem : void 0;
2117
+ const pickDoc = (key) => {
2118
+ const own = operation.schema[key];
2119
+ if (typeof own === "string") return own;
2120
+ const fallback = pathItemDoc?.[key];
2121
+ return typeof fallback === "string" ? fallback : void 0;
2122
+ };
2123
+ return _kubb_ast.ast.factory.createOperation({
2124
+ operationId,
2125
+ protocol: "http",
1844
2126
  method: operation.method.toUpperCase(),
1845
- path: urlPath.path,
1846
- tags: operation.getTags().map((tag) => tag.name),
1847
- summary: operation.getSummary() || void 0,
1848
- description: operation.getDescription() || void 0,
1849
- deprecated: operation.isDeprecated() || void 0,
2127
+ path: operation.path,
2128
+ tags: Array.isArray(operation.schema.tags) ? operation.schema.tags.map(String) : [],
2129
+ summary: pickDoc("summary") || void 0,
2130
+ description: pickDoc("description") || void 0,
2131
+ deprecated: operation.schema.deprecated || void 0,
1850
2132
  parameters,
1851
2133
  requestBody,
1852
2134
  responses
@@ -1858,59 +2140,264 @@ function createSchemaParser(ctx) {
1858
2140
  parseParameter
1859
2141
  };
1860
2142
  }
2143
+ //#endregion
2144
+ //#region src/promoteEnums.ts
2145
+ /**
2146
+ * Collects inline enums to lift to the top level, keyed by the name the parser derived for them
2147
+ * (e.g. `PetStatusEnum`). An enum already defined as a top-level component is left as-is, and a
2148
+ * name that recurs maps to the first definition so each name yields one shared type.
2149
+ */
2150
+ function collectInlineEnums(roots, topLevelNames) {
2151
+ const promoted = /* @__PURE__ */ new Map();
2152
+ for (const root of roots) {
2153
+ const isSchemaRoot = root.kind === "Schema";
2154
+ for (const node of _kubb_ast.ast.collect(root, { schema: (schemaNode) => schemaNode })) {
2155
+ if (node.type !== "enum" || !node.name) continue;
2156
+ if (isSchemaRoot && node === root) continue;
2157
+ if (topLevelNames.has(node.name)) continue;
2158
+ if (!promoted.has(node.name)) promoted.set(node.name, {
2159
+ ...node,
2160
+ optional: void 0,
2161
+ nullish: void 0
2162
+ });
2163
+ }
2164
+ }
2165
+ return promoted;
2166
+ }
2167
+ /**
2168
+ * Replaces every promoted inline enum in `node` with a `ref` to its lifted definition, keeping the
2169
+ * occurrence's usage-slot and documentation fields.
2170
+ */
2171
+ function refPromotedEnums(node, promoted) {
2172
+ if (promoted.size === 0) return node;
2173
+ return _kubb_ast.ast.transform(node, { schema(schemaNode) {
2174
+ if (schemaNode.type !== "enum" || !schemaNode.name || !promoted.has(schemaNode.name)) return void 0;
2175
+ return _kubb_ast.ast.factory.createSchema({
2176
+ type: "ref",
2177
+ name: schemaNode.name,
2178
+ ref: `${SCHEMA_REF_PREFIX}${schemaNode.name}`,
2179
+ optional: schemaNode.optional,
2180
+ nullish: schemaNode.nullish,
2181
+ readOnly: schemaNode.readOnly,
2182
+ writeOnly: schemaNode.writeOnly,
2183
+ deprecated: schemaNode.deprecated,
2184
+ description: schemaNode.description,
2185
+ default: schemaNode.default,
2186
+ examples: schemaNode.examples
2187
+ });
2188
+ } });
2189
+ }
2190
+ //#endregion
2191
+ //#region src/schemaDiagnostics.ts
2192
+ /**
2193
+ * Reports the advisory diagnostics (`KUBB_UNSUPPORTED_FORMAT`, `KUBB_DEPRECATED`) for one
2194
+ * top-level schema. Walks the node the parser produced during `preScan`, threading the RFC 6901
2195
+ * pointer as it descends so a nested field reports against its full path
2196
+ * (`#/components/schemas/Pet/properties/owner/properties/name`). Refs are not followed, so the
2197
+ * resolved schema is reported under its own walk. Reports land in the active build run, are a
2198
+ * no-op outside one, and repeats are deduped by the build.
2199
+ */
2200
+ function reportSchemaDiagnostics({ node, name }) {
2201
+ visit(node, `#/components/schemas/${escapePointerToken(name)}`);
2202
+ }
2203
+ /**
2204
+ * Escapes a single JSON pointer reference token per RFC 6901 (`~` → `~0`, `/` → `~1`), so a
2205
+ * property name with those characters maps to a distinct pointer instead of colliding in the dedupe.
2206
+ */
2207
+ function escapePointerToken(token) {
2208
+ return token.replace(/~/g, "~0").replace(/\//g, "~1");
2209
+ }
2210
+ function visit(node, pointer) {
2211
+ if (node.deprecated) _kubb_core.Diagnostics.report({
2212
+ code: _kubb_core.Diagnostics.code.deprecated,
2213
+ severity: "info",
2214
+ message: "This schema is marked as deprecated.",
2215
+ location: {
2216
+ kind: "schema",
2217
+ pointer
2218
+ }
2219
+ });
2220
+ if (typeof node.format === "string" && !isHandledFormat(node.format)) _kubb_core.Diagnostics.report({
2221
+ code: _kubb_core.Diagnostics.code.unsupportedFormat,
2222
+ severity: "warning",
2223
+ message: `Kubb does not map the format "${node.format}" to a specific type, so it falls back to the base type.`,
2224
+ help: `Use a format Kubb supports, or handle "${node.format}" with a custom parser or plugin.`,
2225
+ location: {
2226
+ kind: "schema",
2227
+ pointer
2228
+ }
2229
+ });
2230
+ if (node.type === "object") {
2231
+ for (const property of node.properties) visit(property.schema, `${pointer}/properties/${escapePointerToken(property.name)}`);
2232
+ if (node.additionalProperties && typeof node.additionalProperties === "object") visit(node.additionalProperties, `${pointer}/additionalProperties`);
2233
+ return;
2234
+ }
2235
+ if (node.type === "array") {
2236
+ for (const item of node.items ?? []) visit(item, `${pointer}/items`);
2237
+ return;
2238
+ }
2239
+ if (node.type === "tuple") {
2240
+ for (const [index, item] of (node.items ?? []).entries()) visit(item, `${pointer}/items/${index}`);
2241
+ return;
2242
+ }
2243
+ if (node.type === "union" || node.type === "intersection") for (const [index, member] of (node.members ?? []).entries()) visit(member, `${pointer}/members/${index}`);
2244
+ }
2245
+ //#endregion
2246
+ //#region src/stream.ts
2247
+ /**
2248
+ * Reads the server URL from the document's `servers` array at `server.index`,
2249
+ * interpolating any `server.variables` into the URL template.
2250
+ *
2251
+ * Returns `null` when `server.index` is omitted or out of range.
2252
+ *
2253
+ * @example Resolve the first server
2254
+ * `resolveBaseUrl({ document, server: { index: 0 } })`
2255
+ *
2256
+ * @example Override a path variable
2257
+ * `resolveBaseUrl({ document, server: { index: 0, variables: { version: 'v2' } } })`
2258
+ */
2259
+ function resolveBaseUrl({ document, server }) {
2260
+ const index = server?.index;
2261
+ const entry = index !== void 0 ? document.servers?.at(index) : void 0;
2262
+ return entry?.url ? resolveServerUrl(entry, server?.variables) : null;
2263
+ }
1861
2264
  /**
1862
- * Parses an OpenAPI specification into Kubb's universal `InputNode` AST.
2265
+ * Parses every schema once to build the lookup structures that streaming needs upfront.
1863
2266
  *
1864
- * This is the main entry point for `@kubb/adapter-oas`. It converts OpenAPI/Swagger specs into a spec-agnostic tree
1865
- * that downstream plugins (`plugin-ts`, `plugin-zod`, etc.) consume for code generation. No code is generated here
1866
- * the tree is a pure data structure representing all schemas and operations.
2267
+ * Three things happen in this single pass:
2268
+ * - `refAliasMap` records schemas that are pure `$ref` aliases so the streaming pass can inline them.
2269
+ * - `enumNames` collects the names of every enum schema so plugins skip re-scanning the stream.
2270
+ * - `circularNames` runs cycle detection, which requires all nodes in memory simultaneously.
2271
+ * The `allNodes` array is local and drops out of scope as soon as this function returns.
1867
2272
  *
1868
- * Returns the AST root and a `nameMapping` for resolving schema references.
2273
+ * After this call, only `refAliasMap` and `discriminatorChildMap` stay alive in the adapter closure.
2274
+ * Both are proportional to the number of aliases or discriminator parents, not total schema count.
2275
+ *
2276
+ * Each schema is parsed again during the streaming pass. This is intentional.
2277
+ * Holding the parsed nodes in memory here would defeat the streaming memory benefit.
1869
2278
  *
1870
2279
  * @example
1871
2280
  * ```ts
1872
- * import { parseOas } from '@kubb/adapter-oas'
1873
- *
1874
- * const document = await parseFromConfig(config)
1875
- * const { root, nameMapping } = parseOas(document, { dateType: 'date', contentType: 'application/json' })
2281
+ * const { refAliasMap, enumNames, circularNames } = preScan({
2282
+ * schemas,
2283
+ * parseSchema,
2284
+ * parserOptions,
2285
+ * discriminator: 'preserve',
2286
+ * })
1876
2287
  * ```
1877
2288
  */
1878
- function parseOas(document, options = {}) {
1879
- const { contentType, ...parserOptions } = options;
1880
- const mergedOptions = {
1881
- ...DEFAULT_PARSER_OPTIONS,
1882
- ...parserOptions
1883
- };
1884
- const { schemas: schemaObjects, nameMapping } = getSchemas(document, { contentType });
1885
- const { parseSchema: _parseSchema, parseOperation: _parseOperation } = createSchemaParser({
1886
- document,
1887
- contentType
1888
- });
1889
- const schemas = Object.entries(schemaObjects).map(([name, schema]) => _parseSchema({
1890
- schema,
1891
- name
1892
- }, mergedOptions));
1893
- const paths = new oas.default(document).getPaths();
1894
- const operations = Object.entries(paths).flatMap(([_path, methods]) => Object.entries(methods).map(([, operation]) => operation ? _parseOperation(mergedOptions, operation) : null).filter((op) => op !== null));
2289
+ function preScan({ schemas, parseSchema, parseOperation, document, parserOptions, discriminator, enums = "inline" }) {
2290
+ const allNodes = [];
2291
+ const refAliasMap = /* @__PURE__ */ new Map();
2292
+ const enumNames = [];
2293
+ const discriminatorParentNodes = [];
2294
+ for (const [name, schema] of Object.entries(schemas)) {
2295
+ const node = parseSchema({
2296
+ schema,
2297
+ name
2298
+ }, parserOptions);
2299
+ allNodes.push(node);
2300
+ reportSchemaDiagnostics({
2301
+ node,
2302
+ name
2303
+ });
2304
+ if (node.type === "ref" && node.name && node.name !== name) refAliasMap.set(name, node);
2305
+ if (_kubb_ast.ast.narrowSchema(node, _kubb_ast.ast.schemaTypes.enum) && node.name) enumNames.push(node.name);
2306
+ if (discriminator === "propagate" && (schema.oneOf ?? schema.anyOf) && schema.discriminator?.propertyName) discriminatorParentNodes.push(node);
2307
+ }
2308
+ const circularNames = [...(0, _kubb_ast.findCircularSchemas)(allNodes)];
2309
+ const discriminatorChildMap = discriminatorParentNodes.length > 0 ? buildDiscriminatorChildMap(discriminatorParentNodes) : null;
2310
+ let promotedEnums = null;
2311
+ if (enums === "root" && document && parseOperation) {
2312
+ const operationNodes = [];
2313
+ for (const operation of getOperations(document)) {
2314
+ const operationNode = parseOperation(parserOptions, operation);
2315
+ if (operationNode) operationNodes.push(operationNode);
2316
+ }
2317
+ promotedEnums = collectInlineEnums([...allNodes, ...operationNodes], new Set(Object.keys(schemas)));
2318
+ for (const name of promotedEnums.keys()) enumNames.push(name);
2319
+ }
1895
2320
  return {
1896
- root: _kubb_core.ast.createInput({
1897
- schemas,
1898
- operations
1899
- }),
1900
- nameMapping
2321
+ refAliasMap,
2322
+ enumNames,
2323
+ circularNames,
2324
+ discriminatorChildMap,
2325
+ promotedEnums
1901
2326
  };
1902
2327
  }
2328
+ /**
2329
+ * Creates a lazy `InputNode<true>` from already-resolved adapter state.
2330
+ *
2331
+ * The schema and operation iterables each start a fresh parse pass on every
2332
+ * `[Symbol.asyncIterator]()` call. This lets multiple plugins consume the same
2333
+ * stream object independently without sharing a cursor or holding all nodes in memory.
2334
+ *
2335
+ * Ref aliases in `refAliasMap` are inlined during iteration: an alias entry is replaced
2336
+ * with its target's parsed node (but keeps the alias name) so plugins never receive bare `ref` nodes.
2337
+ *
2338
+ * @example
2339
+ * ```ts
2340
+ * const streamNode = createInputStream({ schemas, parseSchema, parseOperation, document, parserOptions, refAliasMap, discriminatorChildMap, meta })
2341
+ * for await (const schema of streamNode.schemas) {
2342
+ * // each call to for-await restarts from the first schema
2343
+ * }
2344
+ * ```
2345
+ */
2346
+ function createInputStream({ schemas, parseSchema, parseOperation, document, parserOptions, refAliasMap, discriminatorChildMap, promotedEnums, meta }) {
2347
+ const schemasIterable = { [Symbol.asyncIterator]() {
2348
+ return (async function* () {
2349
+ if (promotedEnums) for (const definition of promotedEnums.values()) yield definition;
2350
+ for (const [name, schema] of Object.entries(schemas)) {
2351
+ const alias = refAliasMap.get(name);
2352
+ if (alias?.name && schemas[alias.name]) {
2353
+ const aliasNode = {
2354
+ ...parseSchema({
2355
+ schema: schemas[alias.name],
2356
+ name: alias.name
2357
+ }, parserOptions),
2358
+ name
2359
+ };
2360
+ yield promotedEnums ? refPromotedEnums(aliasNode, promotedEnums) : aliasNode;
2361
+ continue;
2362
+ }
2363
+ const parsed = parseSchema({
2364
+ schema,
2365
+ name
2366
+ }, parserOptions);
2367
+ const node = discriminatorChildMap?.get(name) ? patchDiscriminatorNode(parsed, discriminatorChildMap.get(name)) : parsed;
2368
+ yield promotedEnums ? refPromotedEnums(node, promotedEnums) : node;
2369
+ }
2370
+ })();
2371
+ } };
2372
+ const operationsIterable = { [Symbol.asyncIterator]() {
2373
+ return (async function* () {
2374
+ for (const operation of getOperations(document)) {
2375
+ const node = parseOperation(parserOptions, operation);
2376
+ if (node) yield promotedEnums ? refPromotedEnums(node, promotedEnums) : node;
2377
+ }
2378
+ })();
2379
+ } };
2380
+ return _kubb_ast.ast.factory.createInput({
2381
+ stream: true,
2382
+ schemas: schemasIterable,
2383
+ operations: operationsIterable,
2384
+ meta
2385
+ });
2386
+ }
1903
2387
  //#endregion
1904
2388
  //#region src/adapter.ts
1905
2389
  /**
1906
- * Stable string identifier for the OAS adapter used in Kubb's adapter registry.
2390
+ * The `name` of `@kubb/adapter-oas`, used to identify this adapter in a Kubb config.
1907
2391
  */
1908
2392
  const adapterOasName = "oas";
1909
2393
  /**
1910
- * Creates the default OpenAPI / Swagger adapter for Kubb.
2394
+ * Default Kubb adapter for OpenAPI 2.0, 3.0, and 3.1 specifications. Reads the
2395
+ * file at `input.path`, validates it, resolves the base URL, and converts every
2396
+ * schema and operation into the universal AST that every downstream plugin
2397
+ * consumes.
1911
2398
  *
1912
- * Parses the spec, optionally validates it, resolves the base URL, and converts
1913
- * everything into an `InputNode` that downstream plugins consume.
2399
+ * Configure once on `defineConfig`. The adapter's choices (date representation,
2400
+ * integer width, server URL) apply to every plugin in the build.
1914
2401
  *
1915
2402
  * @example
1916
2403
  * ```ts
@@ -1919,26 +2406,117 @@ const adapterOasName = "oas";
1919
2406
  * import { pluginTs } from '@kubb/plugin-ts'
1920
2407
  *
1921
2408
  * export default defineConfig({
1922
- * adapter: adapterOas({ dateType: 'date', serverIndex: 0 }),
1923
- * input: { path: './openapi.yaml' },
2409
+ * input: { path: './petStore.yaml' },
2410
+ * output: { path: './src/gen' },
2411
+ * adapter: adapterOas({
2412
+ * server: { index: 0 },
2413
+ * discriminator: 'propagate',
2414
+ * dateType: 'date',
2415
+ * }),
1924
2416
  * plugins: [pluginTs()],
1925
2417
  * })
1926
2418
  * ```
1927
2419
  */
1928
2420
  const adapterOas = (0, _kubb_core.createAdapter)((options) => {
1929
- const { validate = true, contentType, serverIndex, serverVariables, discriminator = "strict", dateType = DEFAULT_PARSER_OPTIONS.dateType, integerType = DEFAULT_PARSER_OPTIONS.integerType, unknownType = DEFAULT_PARSER_OPTIONS.unknownType, enumSuffix = DEFAULT_PARSER_OPTIONS.enumSuffix, emptySchemaType = unknownType || DEFAULT_PARSER_OPTIONS.emptySchemaType } = options;
2421
+ const { validate = true, contentType, server, discriminator = "preserve", enums = "inline", dateType = DEFAULT_PARSER_OPTIONS.dateType, integerType = DEFAULT_PARSER_OPTIONS.integerType, unknownType = DEFAULT_PARSER_OPTIONS.unknownType, enumSuffix = DEFAULT_PARSER_OPTIONS.enumSuffix, emptySchemaType = unknownType || DEFAULT_PARSER_OPTIONS.emptySchemaType } = options;
2422
+ const parserOptions = {
2423
+ ...DEFAULT_PARSER_OPTIONS,
2424
+ dateType,
2425
+ integerType,
2426
+ unknownType,
2427
+ emptySchemaType,
2428
+ enumSuffix
2429
+ };
1930
2430
  let nameMapping = /* @__PURE__ */ new Map();
1931
- let parsedDocument;
1932
- let inputNode;
2431
+ let parsedDocument = null;
2432
+ const documentCache = /* @__PURE__ */ new WeakMap();
2433
+ const schemasCache = /* @__PURE__ */ new WeakMap();
2434
+ const schemaParserCache = /* @__PURE__ */ new WeakMap();
2435
+ const preScanCache = /* @__PURE__ */ new WeakMap();
2436
+ function ensureDocument(source) {
2437
+ const cached = documentCache.get(source);
2438
+ if (cached) return cached;
2439
+ const promise = (async () => {
2440
+ const fresh = await parseFromConfig(source);
2441
+ if (validate) await validateDocument(fresh);
2442
+ parsedDocument = fresh;
2443
+ return fresh;
2444
+ })();
2445
+ documentCache.set(source, promise);
2446
+ return promise;
2447
+ }
2448
+ function ensureSchemas(document) {
2449
+ const cached = schemasCache.get(document);
2450
+ if (cached) return cached;
2451
+ const promise = Promise.resolve().then(() => {
2452
+ const result = getSchemas(document, { contentType });
2453
+ nameMapping = result.nameMapping;
2454
+ return result.schemas;
2455
+ });
2456
+ schemasCache.set(document, promise);
2457
+ return promise;
2458
+ }
2459
+ function ensureSchemaParser(document) {
2460
+ const cached = schemaParserCache.get(document);
2461
+ if (cached) return cached;
2462
+ const parser = createSchemaParser({
2463
+ document,
2464
+ contentType
2465
+ });
2466
+ schemaParserCache.set(document, parser);
2467
+ return parser;
2468
+ }
2469
+ function ensurePreScan(document, schemas, parseSchema, parseOperation) {
2470
+ const cached = preScanCache.get(document);
2471
+ if (cached) return cached;
2472
+ const result = preScan({
2473
+ schemas,
2474
+ parseSchema,
2475
+ parseOperation,
2476
+ document,
2477
+ parserOptions,
2478
+ discriminator,
2479
+ enums
2480
+ });
2481
+ preScanCache.set(document, result);
2482
+ return result;
2483
+ }
2484
+ async function createStream(source) {
2485
+ const document = await ensureDocument(source);
2486
+ const schemas = await ensureSchemas(document);
2487
+ const { parseSchema, parseOperation } = ensureSchemaParser(document);
2488
+ const { refAliasMap, enumNames, circularNames, discriminatorChildMap, promotedEnums } = ensurePreScan(document, schemas, parseSchema, parseOperation);
2489
+ return createInputStream({
2490
+ schemas,
2491
+ parseSchema,
2492
+ parseOperation,
2493
+ document,
2494
+ parserOptions,
2495
+ refAliasMap,
2496
+ discriminatorChildMap,
2497
+ promotedEnums,
2498
+ meta: {
2499
+ title: document.info?.title,
2500
+ description: document.info?.description,
2501
+ version: document.info?.version,
2502
+ baseURL: resolveBaseUrl({
2503
+ document,
2504
+ server
2505
+ }),
2506
+ circularNames,
2507
+ enumNames
2508
+ }
2509
+ });
2510
+ }
1933
2511
  return {
1934
2512
  name: "oas",
1935
2513
  get options() {
1936
2514
  return {
1937
2515
  validate,
1938
2516
  contentType,
1939
- serverIndex,
1940
- serverVariables,
2517
+ server,
1941
2518
  discriminator,
2519
+ enums,
1942
2520
  dateType,
1943
2521
  integerType,
1944
2522
  unknownType,
@@ -1950,71 +2528,36 @@ const adapterOas = (0, _kubb_core.createAdapter)((options) => {
1950
2528
  get document() {
1951
2529
  return parsedDocument;
1952
2530
  },
1953
- get inputNode() {
1954
- return inputNode;
1955
- },
1956
2531
  async validate(input, options) {
2532
+ await assertInputExists(input);
1957
2533
  await validateDocument(await parseDocument(input), options);
1958
2534
  },
1959
2535
  getImports(node, resolve) {
1960
- return _kubb_core.ast.collectImports({
1961
- node,
1962
- nameMapping,
1963
- resolve: (schemaName) => {
1964
- const result = resolve(schemaName);
1965
- if (!result) return;
1966
- return _kubb_core.ast.createImport({
1967
- name: [result.name],
1968
- path: result.path
1969
- });
1970
- }
1971
- });
2536
+ return (0, _kubb_ast.collect)(node, { schema(schemaNode) {
2537
+ const schemaRef = (0, _kubb_ast.narrowSchema)(schemaNode, "ref");
2538
+ if (!schemaRef?.ref) return null;
2539
+ const result = resolve(nameMapping.get(schemaRef.ref) ?? (0, _kubb_ast.extractRefName)(schemaRef.ref));
2540
+ if (!result) return null;
2541
+ return _kubb_ast.ast.factory.createImport({
2542
+ name: [result.name],
2543
+ path: result.path
2544
+ });
2545
+ } });
1972
2546
  },
1973
2547
  async parse(source) {
1974
- const document = await parseFromConfig(source);
1975
- if (validate) await validateDocument(document);
1976
- const server = serverIndex !== void 0 ? document.servers?.at(serverIndex) : void 0;
1977
- const baseURL = server?.url ? resolveServerUrl(server, serverVariables) : void 0;
1978
- const { root: parsedRoot, nameMapping: parsedNameMapping } = parseOas(document, {
1979
- contentType,
1980
- dateType,
1981
- integerType,
1982
- unknownType,
1983
- emptySchemaType,
1984
- enumSuffix
2548
+ const streamNode = await createStream(source);
2549
+ const [schemas, operations] = await Promise.all([Array.fromAsync(streamNode.schemas), Array.fromAsync(streamNode.operations)]);
2550
+ return _kubb_ast.ast.factory.createInput({
2551
+ schemas,
2552
+ operations,
2553
+ meta: streamNode.meta
1985
2554
  });
1986
- const node = discriminator === "inherit" ? applyDiscriminatorInheritance(parsedRoot) : parsedRoot;
1987
- nameMapping = parsedNameMapping;
1988
- parsedDocument = document;
1989
- inputNode = _kubb_core.ast.createInput({
1990
- ...node,
1991
- meta: {
1992
- title: document.info?.title,
1993
- description: document.info?.description,
1994
- version: document.info?.version,
1995
- baseURL
1996
- }
1997
- });
1998
- return inputNode;
1999
- }
2555
+ },
2556
+ stream: createStream
2000
2557
  };
2001
2558
  });
2002
2559
  //#endregion
2003
- //#region src/types.ts
2004
- /**
2005
- * Maps uppercase HTTP method names to lowercase for backwards compatibility.
2006
- *
2007
- * @example
2008
- * ```ts
2009
- * HttpMethods['GET'] // 'get'
2010
- * HttpMethods['POST'] // 'post'
2011
- * ```
2012
- */
2013
- const HttpMethods = Object.fromEntries(Object.entries(_kubb_core.ast.httpMethods).map(([lower, upper]) => [upper, lower]));
2014
- //#endregion
2015
- exports.HttpMethods = HttpMethods;
2016
2560
  exports.adapterOas = adapterOas;
2017
2561
  exports.adapterOasName = adapterOasName;
2018
- exports.mergeDocuments = mergeDocuments;
2019
2562
 
2020
2563
  //# sourceMappingURL=index.cjs.map