@kubb/adapter-oas 5.0.0-alpha.2 → 5.0.0-alpha.21

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,45 +21,210 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
21
21
  enumerable: true
22
22
  }) : target, mod));
23
23
  //#endregion
24
- let node_path = require("node:path");
25
- node_path = __toESM(node_path);
26
24
  let _kubb_ast = require("@kubb/ast");
27
25
  let _kubb_core = require("@kubb/core");
26
+ let node_path = require("node:path");
27
+ node_path = __toESM(node_path);
28
28
  let _redocly_openapi_core = require("@redocly/openapi-core");
29
29
  let _stoplight_yaml = require("@stoplight/yaml");
30
30
  _stoplight_yaml = __toESM(_stoplight_yaml);
31
- let oas_types = require("oas/types");
32
31
  let oas_normalize = require("oas-normalize");
33
32
  oas_normalize = __toESM(oas_normalize);
34
33
  let remeda = require("remeda");
35
34
  let swagger2openapi = require("swagger2openapi");
36
35
  swagger2openapi = __toESM(swagger2openapi);
37
- let jsonpointer = require("jsonpointer");
38
- jsonpointer = __toESM(jsonpointer);
39
36
  let oas = require("oas");
40
37
  oas = __toESM(oas);
38
+ let jsonpointer = require("jsonpointer");
39
+ jsonpointer = __toESM(jsonpointer);
40
+ let oas_types = require("oas/types");
41
41
  let oas_utils = require("oas/utils");
42
- //#region src/oas/resolveServerUrl.ts
42
+ //#region src/constants.ts
43
43
  /**
44
- * Resolves an OpenAPI server URL by substituting `{variable}` placeholders.
44
+ * Default parser options applied when no explicit options are provided.
45
45
  *
46
- * Resolution priority per variable:
47
- * 1. `overrides[key]` — caller-supplied value.
48
- * 2. `variable.default` spec-defined default, coerced to `string`.
49
- * 3. Variable is left unreplaced when neither is available.
46
+ * @example
47
+ * ```ts
48
+ * import { DEFAULT_PARSER_OPTIONS } from '@kubb/adapter-oas'
50
49
  *
51
- * Throws when an `overrides` value is not present in the variable's `enum` list.
50
+ * const parser = createOasParser(oas)
51
+ * const root = parser.parse({ ...DEFAULT_PARSER_OPTIONS, dateType: 'date' })
52
+ * ```
52
53
  */
53
- function resolveServerUrl(server, overrides) {
54
- if (!server.variables) return server.url;
55
- let url = server.url;
56
- for (const [key, variable] of Object.entries(server.variables)) {
57
- const value = overrides?.[key] ?? (variable.default != null ? String(variable.default) : void 0);
58
- if (value === void 0) continue;
59
- 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(", ")}.`);
60
- url = url.replaceAll(`{${key}}`, value);
54
+ const DEFAULT_PARSER_OPTIONS = {
55
+ dateType: "string",
56
+ integerType: "number",
57
+ unknownType: "any",
58
+ emptySchemaType: "any",
59
+ enumSuffix: "enum"
60
+ };
61
+ /**
62
+ * OpenAPI version string written into the stub document created during multi-spec merges.
63
+ */
64
+ const MERGE_OPENAPI_VERSION = "3.0.0";
65
+ /**
66
+ * Fallback `info.title` placed in the stub document when merging multiple API files.
67
+ */
68
+ const MERGE_DEFAULT_TITLE = "Merged API";
69
+ /**
70
+ * Fallback `info.version` placed in the stub document when merging multiple API files.
71
+ */
72
+ const MERGE_DEFAULT_VERSION = "1.0.0";
73
+ /**
74
+ * Set of JSON Schema keywords that prevent a schema fragment from being inlined during `allOf` flattening.
75
+ *
76
+ * A fragment that contains any of these keys carries structural meaning of its own and must stay as a separate
77
+ * intersection member rather than being merged into the parent.
78
+ *
79
+ * @example
80
+ * ```ts
81
+ * import { structuralKeys } from '@kubb/adapter-oas'
82
+ *
83
+ * const isStructural = Object.keys(fragment).some((key) => structuralKeys.has(key))
84
+ * // true when fragment has e.g. 'properties' or 'oneOf'
85
+ * ```
86
+ */
87
+ const structuralKeys = new Set([
88
+ "properties",
89
+ "items",
90
+ "additionalProperties",
91
+ "oneOf",
92
+ "anyOf",
93
+ "allOf",
94
+ "not"
95
+ ]);
96
+ /**
97
+ * Static map from OAS `format` strings to Kubb `SchemaType` values.
98
+ *
99
+ * Only formats whose AST type differs from the OAS `type` field appear here.
100
+ * Formats that depend on runtime options (`int64`, `date-time`, `date`, `time`) are handled separately
101
+ * in the parser. `ipv4`, `ipv6`, and `hostname` map to `'url'` as the closest supported scalar.
102
+ *
103
+ * @example
104
+ * ```ts
105
+ * import { formatMap } from '@kubb/adapter-oas'
106
+ *
107
+ * formatMap['uuid'] // 'uuid'
108
+ * formatMap['binary'] // 'blob'
109
+ * formatMap['float'] // 'number'
110
+ * ```
111
+ */
112
+ const formatMap = {
113
+ uuid: "uuid",
114
+ email: "email",
115
+ "idn-email": "email",
116
+ uri: "url",
117
+ "uri-reference": "url",
118
+ url: "url",
119
+ ipv4: "url",
120
+ ipv6: "url",
121
+ hostname: "url",
122
+ "idn-hostname": "url",
123
+ binary: "blob",
124
+ byte: "blob",
125
+ int32: "integer",
126
+ float: "number",
127
+ double: "number"
128
+ };
129
+ /**
130
+ * Vendor extension keys that attach human-readable labels to enum values, checked in priority order.
131
+ *
132
+ * @example
133
+ * ```ts
134
+ * import { enumExtensionKeys } from '@kubb/adapter-oas'
135
+ *
136
+ * const key = enumExtensionKeys.find((k) => k in schema) // 'x-enumNames' | 'x-enum-varnames' | undefined
137
+ * ```
138
+ */
139
+ const enumExtensionKeys = ["x-enumNames", "x-enum-varnames"];
140
+ /**
141
+ * Maps `'any' | 'unknown' | 'void'` option strings to their `ScalarSchemaType` constant.
142
+ * Replaces a plain object lookup with a `Map` for explicit key membership testing via `.has()`.
143
+ */
144
+ const typeOptionMap = new Map([
145
+ ["any", _kubb_ast.schemaTypes.any],
146
+ ["unknown", _kubb_ast.schemaTypes.unknown],
147
+ ["void", _kubb_ast.schemaTypes.void]
148
+ ]);
149
+ //#endregion
150
+ //#region src/discriminator.ts
151
+ /**
152
+ * Injects discriminator enum values into child schemas so they know which value identifies them.
153
+ *
154
+ * Finds every union schema in `root.schemas` that has a `discriminatorPropertyName`, collects the
155
+ * enum value each union member is mapped to, then adds (or replaces) that property on the matching
156
+ * child object schema.
157
+ *
158
+ * Returns a new `RootNode` — the original is never mutated.
159
+ *
160
+ * @example
161
+ * ```ts
162
+ * const { root } = parseOas(document, options)
163
+ * const next = applyDiscriminatorInheritance(root)
164
+ * ```
165
+ */
166
+ function applyDiscriminatorInheritance(root) {
167
+ const childMap = /* @__PURE__ */ new Map();
168
+ for (const schema of root.schemas) {
169
+ let unionNode = (0, _kubb_ast.narrowSchema)(schema, "union");
170
+ if (!unionNode) {
171
+ const intersectionMembers = (0, _kubb_ast.narrowSchema)(schema, "intersection")?.members;
172
+ if (intersectionMembers) for (const m of intersectionMembers) {
173
+ const u = (0, _kubb_ast.narrowSchema)(m, "union");
174
+ if (u) {
175
+ unionNode = u;
176
+ break;
177
+ }
178
+ }
179
+ }
180
+ if (!unionNode?.discriminatorPropertyName || !unionNode.members) continue;
181
+ const { discriminatorPropertyName, members } = unionNode;
182
+ for (const member of members) {
183
+ const intersectionNode = (0, _kubb_ast.narrowSchema)(member, "intersection");
184
+ if (!intersectionNode?.members) continue;
185
+ let refNode;
186
+ let objNode;
187
+ for (const m of intersectionNode.members) {
188
+ refNode ??= (0, _kubb_ast.narrowSchema)(m, "ref");
189
+ objNode ??= (0, _kubb_ast.narrowSchema)(m, "object");
190
+ }
191
+ if (!refNode?.name || !objNode) continue;
192
+ const prop = objNode.properties.find((p) => p.name === discriminatorPropertyName);
193
+ const enumNode = prop ? (0, _kubb_ast.narrowSchema)(prop.schema, "enum") : void 0;
194
+ if (!enumNode?.enumValues?.length) continue;
195
+ const enumValues = enumNode.enumValues.filter((v) => v !== null);
196
+ if (!enumValues.length) continue;
197
+ const existing = childMap.get(refNode.name);
198
+ if (existing) existing.enumValues.push(...enumValues);
199
+ else childMap.set(refNode.name, {
200
+ propertyName: discriminatorPropertyName,
201
+ enumValues: [...enumValues]
202
+ });
203
+ }
61
204
  }
62
- return url;
205
+ if (childMap.size === 0) return root;
206
+ return (0, _kubb_ast.transform)(root, { schema(node, { parent }) {
207
+ if (parent?.kind !== "Root" || !node.name) return;
208
+ const entry = childMap.get(node.name);
209
+ if (!entry) return;
210
+ const objectNode = (0, _kubb_ast.narrowSchema)(node, "object");
211
+ if (!objectNode) return;
212
+ const { propertyName, enumValues } = entry;
213
+ const newProp = (0, _kubb_ast.createProperty)({
214
+ name: propertyName,
215
+ required: true,
216
+ schema: (0, _kubb_ast.createSchema)({
217
+ type: "enum",
218
+ enumValues
219
+ })
220
+ });
221
+ const existingIdx = objectNode.properties.findIndex((p) => p.name === propertyName);
222
+ const newProperties = existingIdx >= 0 ? objectNode.properties.map((p, i) => i === existingIdx ? newProp : p) : [...objectNode.properties, newProp];
223
+ return {
224
+ ...objectNode,
225
+ properties: newProperties
226
+ };
227
+ } });
63
228
  }
64
229
  //#endregion
65
230
  //#region ../../internals/utils/src/casing.ts
@@ -81,9 +246,12 @@ function toCamelOrPascal(text, pascal) {
81
246
  * Splits `text` on `.` and applies `transformPart` to each segment.
82
247
  * The last segment receives `isLast = true`, all earlier segments receive `false`.
83
248
  * Segments are joined with `/` to form a file path.
249
+ *
250
+ * Only splits on dots followed by a letter so that version numbers
251
+ * embedded in operationIds (e.g. `v2025.0`) are kept intact.
84
252
  */
85
253
  function applyToFileParts(text, transformPart) {
86
- const parts = text.split(".");
254
+ const parts = text.split(/\.(?=[a-zA-Z])/);
87
255
  return parts.map((part, i) => transformPart(part, i === parts.length - 1)).join("/");
88
256
  }
89
257
  /**
@@ -120,6 +288,13 @@ function pascalCase(text, { isFile, prefix = "", suffix = "" } = {}) {
120
288
  //#region ../../internals/utils/src/reserved.ts
121
289
  /**
122
290
  * Returns `true` when `name` is a syntactically valid JavaScript variable name.
291
+ *
292
+ * @example
293
+ * ```ts
294
+ * isValidVarName('status') // true
295
+ * isValidVarName('class') // false (reserved word)
296
+ * isValidVarName('42foo') // false (starts with digit)
297
+ * ```
123
298
  */
124
299
  function isValidVarName(name) {
125
300
  try {
@@ -140,18 +315,33 @@ function isValidVarName(name) {
140
315
  * p.template // '`/pet/${petId}`'
141
316
  */
142
317
  var URLPath = class {
143
- /** The raw OpenAPI/Swagger path string, e.g. `/pet/{petId}`. */
318
+ /**
319
+ * The raw OpenAPI/Swagger path string, e.g. `/pet/{petId}`.
320
+ */
144
321
  path;
145
322
  #options;
146
323
  constructor(path, options = {}) {
147
324
  this.path = path;
148
325
  this.#options = options;
149
326
  }
150
- /** Converts the OpenAPI path to Express-style colon syntax, e.g. `/pet/{petId}` → `/pet/:petId`. */
327
+ /** Converts the OpenAPI path to Express-style colon syntax, e.g. `/pet/{petId}` → `/pet/:petId`.
328
+ *
329
+ * @example
330
+ * ```ts
331
+ * new URLPath('/pet/{petId}').URL // '/pet/:petId'
332
+ * ```
333
+ */
151
334
  get URL() {
152
335
  return this.toURLPath();
153
336
  }
154
- /** Returns `true` when `path` is a fully-qualified URL (e.g. starts with `https://`). */
337
+ /** Returns `true` when `path` is a fully-qualified URL (e.g. starts with `https://`).
338
+ *
339
+ * @example
340
+ * ```ts
341
+ * new URLPath('https://petstore.swagger.io/v2/pet').isURL // true
342
+ * new URLPath('/pet/{petId}').isURL // false
343
+ * ```
344
+ */
155
345
  get isURL() {
156
346
  try {
157
347
  return !!new URL(this.path).href;
@@ -169,11 +359,25 @@ var URLPath = class {
169
359
  get template() {
170
360
  return this.toTemplateString();
171
361
  }
172
- /** Returns the path and its extracted params as a structured `URLObject`, or as a stringified expression when `stringify` is set. */
362
+ /** Returns the path and its extracted params as a structured `URLObject`, or as a stringified expression when `stringify` is set.
363
+ *
364
+ * @example
365
+ * ```ts
366
+ * new URLPath('/pet/{petId}').object
367
+ * // { url: '/pet/:petId', params: { petId: 'petId' } }
368
+ * ```
369
+ */
173
370
  get object() {
174
371
  return this.toObject();
175
372
  }
176
- /** Returns a map of path parameter names, or `undefined` when the path has no parameters. */
373
+ /** Returns a map of path parameter names, or `undefined` when the path has no parameters.
374
+ *
375
+ * @example
376
+ * ```ts
377
+ * new URLPath('/pet/{petId}').params // { petId: 'petId' }
378
+ * new URLPath('/pet').params // undefined
379
+ * ```
380
+ */
177
381
  get params() {
178
382
  return this.getParams();
179
383
  }
@@ -181,7 +385,9 @@ var URLPath = class {
181
385
  const param = isValidVarName(raw) ? raw : camelCase(raw);
182
386
  return this.#options.casing === "camelcase" ? camelCase(param) : param;
183
387
  }
184
- /** Iterates over every `{param}` token in `path`, calling `fn` with the raw token and transformed name. */
388
+ /**
389
+ * Iterates over every `{param}` token in `path`, calling `fn` with the raw token and transformed name.
390
+ */
185
391
  #eachParam(fn) {
186
392
  for (const match of this.path.matchAll(/\{([^}]+)\}/g)) {
187
393
  const raw = match[1];
@@ -218,6 +424,12 @@ var URLPath = class {
218
424
  * Extracts all `{param}` segments from the path and returns them as a key-value map.
219
425
  * An optional `replacer` transforms each parameter name in both key and value positions.
220
426
  * Returns `undefined` when no path parameters are found.
427
+ *
428
+ * @example
429
+ * ```ts
430
+ * new URLPath('/pet/{petId}/tag/{tagId}').getParams()
431
+ * // { petId: 'petId', tagId: 'tagId' }
432
+ * ```
221
433
  */
222
434
  getParams(replacer) {
223
435
  const params = {};
@@ -227,439 +439,28 @@ var URLPath = class {
227
439
  });
228
440
  return Object.keys(params).length > 0 ? params : void 0;
229
441
  }
230
- /** Converts the OpenAPI path to Express-style colon syntax, e.g. `/pet/{petId}` → `/pet/:petId`. */
442
+ /** Converts the OpenAPI path to Express-style colon syntax.
443
+ *
444
+ * @example
445
+ * ```ts
446
+ * new URLPath('/pet/{petId}').toURLPath() // '/pet/:petId'
447
+ * ```
448
+ */
231
449
  toURLPath() {
232
450
  return this.path.replace(/\{([^}]+)\}/g, ":$1");
233
451
  }
234
452
  };
235
453
  //#endregion
236
- //#region src/constants.ts
237
- /**
238
- * OpenAPI version string written into merged document stubs.
239
- */
240
- const MERGE_OPENAPI_VERSION = "3.0.0";
241
- /**
242
- * Fallback `info.title` used when merging multiple API documents.
243
- */
244
- const MERGE_DEFAULT_TITLE = "Merged API";
454
+ //#region src/guards.ts
245
455
  /**
246
- * Fallback `info.version` used when merging multiple API documents.
247
- */
248
- const MERGE_DEFAULT_VERSION = "1.0.0";
249
- /**
250
- * JSON Schema keywords that indicate structural composition.
251
- * A schema fragment containing any of these keys must not be inlined into its
252
- * parent during `allOf` flattening — it carries semantic meaning of its own.
253
- */
254
- const structuralKeys = new Set([
255
- "properties",
256
- "items",
257
- "additionalProperties",
258
- "oneOf",
259
- "anyOf",
260
- "allOf",
261
- "not"
262
- ]);
263
- /**
264
- * Maps OAS/JSON Schema `format` strings to their Kubb `SchemaType` equivalents.
265
- *
266
- * Only formats that need a type different from the raw OAS `type` are listed.
267
- * `int64`, `date-time`, `date`, and `time` are handled separately because their
268
- * mapping depends on runtime parser options.
456
+ * Returns `true` when `doc` is a Swagger 2.0 document (no `openapi` key).
269
457
  *
270
- * Note: `ipv4`, `ipv6`, and `hostname` map to `'url'` — the closest supported
271
- * scalar type in the Kubb AST, even though these are not strictly URLs.
272
- */
273
- const formatMap = {
274
- uuid: "uuid",
275
- email: "email",
276
- "idn-email": "email",
277
- uri: "url",
278
- "uri-reference": "url",
279
- url: "url",
280
- ipv4: "url",
281
- ipv6: "url",
282
- hostname: "url",
283
- "idn-hostname": "url",
284
- binary: "blob",
285
- byte: "blob",
286
- int32: "integer",
287
- float: "number",
288
- double: "number"
289
- };
290
- /**
291
- * Exhaustive list of media types that Kubb recognizes.
292
- * Kept as a module-level constant to avoid re-allocating the array on every call.
293
- */
294
- const knownMediaTypes = [
295
- "application/json",
296
- "application/xml",
297
- "application/x-www-form-urlencoded",
298
- "application/octet-stream",
299
- "application/pdf",
300
- "application/zip",
301
- "application/graphql",
302
- "multipart/form-data",
303
- "text/plain",
304
- "text/html",
305
- "text/csv",
306
- "text/xml",
307
- "image/png",
308
- "image/jpeg",
309
- "image/gif",
310
- "image/webp",
311
- "image/svg+xml",
312
- "audio/mpeg",
313
- "video/mp4"
314
- ];
315
- /**
316
- * Vendor extension keys used to attach human-readable labels to enum values.
317
- * Checked in priority order: the first key found wins.
318
- */
319
- const enumExtensionKeys = ["x-enumNames", "x-enum-varnames"];
320
- //#endregion
321
- //#region src/oas/Oas.ts
322
- /**
323
- * Prefix used to create synthetic `$ref` values for anonymous (inline) discriminator schemas.
324
- * The suffix is the schema index within the discriminator's `oneOf`/`anyOf` array.
325
- * @example `#kubb-inline-0`
326
- */
327
- const KUBB_INLINE_REF_PREFIX = "#kubb-inline-";
328
- var Oas = class extends oas.default {
329
- #options = { discriminator: "strict" };
330
- document;
331
- constructor(document) {
332
- super(document, void 0);
333
- this.document = document;
334
- }
335
- setOptions(options) {
336
- this.#options = {
337
- ...this.#options,
338
- ...options
339
- };
340
- if (this.#options.discriminator === "inherit") this.#applyDiscriminatorInheritance();
341
- }
342
- get options() {
343
- return this.#options;
344
- }
345
- get($ref) {
346
- const origRef = $ref;
347
- $ref = $ref.trim();
348
- if ($ref === "") return null;
349
- if ($ref.startsWith("#")) $ref = globalThis.decodeURIComponent($ref.substring(1));
350
- else return null;
351
- const current = jsonpointer.default.get(this.api, $ref);
352
- if (!current) throw new Error(`Could not find a definition for ${origRef}.`);
353
- return current;
354
- }
355
- getKey($ref) {
356
- const key = $ref.split("/").pop();
357
- return key === "" ? void 0 : key;
358
- }
359
- set($ref, value) {
360
- $ref = $ref.trim();
361
- if ($ref === "") return false;
362
- if ($ref.startsWith("#")) {
363
- $ref = globalThis.decodeURIComponent($ref.substring(1));
364
- jsonpointer.default.set(this.api, $ref, value);
365
- }
366
- }
367
- #setDiscriminator(schema) {
368
- const { mapping = {}, propertyName } = schema.discriminator;
369
- if (this.#options.discriminator === "inherit") Object.entries(mapping).forEach(([mappingKey, mappingValue]) => {
370
- if (mappingValue) {
371
- const childSchema = this.get(mappingValue);
372
- if (!childSchema) return;
373
- if (!childSchema.properties) childSchema.properties = {};
374
- const property = childSchema.properties[propertyName];
375
- if (childSchema.properties) {
376
- childSchema.properties[propertyName] = {
377
- ...childSchema.properties ? childSchema.properties[propertyName] : {},
378
- enum: [...property?.enum?.filter((value) => value !== mappingKey) ?? [], mappingKey]
379
- };
380
- childSchema.required = typeof childSchema.required === "boolean" ? childSchema.required : [...new Set([...childSchema.required ?? [], propertyName])];
381
- this.set(mappingValue, childSchema);
382
- }
383
- }
384
- });
385
- }
386
- getDiscriminator(schema) {
387
- if (!isDiscriminator(schema) || !schema) return null;
388
- const { mapping = {}, propertyName } = schema.discriminator;
389
- /**
390
- * Helper to extract discriminator value from a schema.
391
- * Checks in order:
392
- * 1. Extension property matching propertyName (e.g., x-linode-ref-name)
393
- * 2. Property with const value
394
- * 3. Property with single enum value
395
- * 4. Title as fallback
396
- */
397
- const getDiscriminatorValue = (schema) => {
398
- if (!schema) return null;
399
- if (propertyName.startsWith("x-")) {
400
- const extensionValue = schema[propertyName];
401
- if (extensionValue && typeof extensionValue === "string") return extensionValue;
402
- }
403
- const propertySchema = schema.properties?.[propertyName];
404
- if (propertySchema && "const" in propertySchema && propertySchema.const !== void 0) return String(propertySchema.const);
405
- if (propertySchema && propertySchema.enum?.length === 1) return String(propertySchema.enum[0]);
406
- return schema.title || null;
407
- };
408
- /**
409
- * Process oneOf/anyOf items to build mapping.
410
- * Handles both $ref and inline schemas.
411
- */
412
- const processSchemas = (schemas, existingMapping) => {
413
- schemas.forEach((schemaItem, index) => {
414
- if (isReference(schemaItem)) {
415
- const key = this.getKey(schemaItem.$ref);
416
- try {
417
- const discriminatorValue = getDiscriminatorValue(this.get(schemaItem.$ref));
418
- const canAdd = key && !Object.values(existingMapping).includes(schemaItem.$ref);
419
- if (canAdd && discriminatorValue) existingMapping[discriminatorValue] = schemaItem.$ref;
420
- else if (canAdd) existingMapping[key] = schemaItem.$ref;
421
- } catch (_error) {
422
- if (key && !Object.values(existingMapping).includes(schemaItem.$ref)) existingMapping[key] = schemaItem.$ref;
423
- }
424
- } else {
425
- const discriminatorValue = getDiscriminatorValue(schemaItem);
426
- if (discriminatorValue) existingMapping[discriminatorValue] = `${KUBB_INLINE_REF_PREFIX}${index}`;
427
- }
428
- });
429
- };
430
- if (schema.oneOf) processSchemas(schema.oneOf, mapping);
431
- if (schema.anyOf) processSchemas(schema.anyOf, mapping);
432
- return {
433
- ...schema.discriminator,
434
- mapping
435
- };
436
- }
437
- dereferenceWithRef(schema) {
438
- if (isReference(schema)) return {
439
- ...schema,
440
- ...this.get(schema.$ref),
441
- $ref: schema.$ref
442
- };
443
- return schema;
444
- }
445
- #applyDiscriminatorInheritance() {
446
- const components = this.api.components;
447
- if (!components?.schemas) return;
448
- const visited = /* @__PURE__ */ new WeakSet();
449
- const enqueue = (value) => {
450
- if (!value) return;
451
- if (Array.isArray(value)) {
452
- for (const item of value) enqueue(item);
453
- return;
454
- }
455
- if (typeof value === "object") visit(value);
456
- };
457
- const visit = (schema) => {
458
- if (!schema || typeof schema !== "object") return;
459
- if (isReference(schema)) {
460
- visit(this.get(schema.$ref));
461
- return;
462
- }
463
- const schemaObject = schema;
464
- if (visited.has(schemaObject)) return;
465
- visited.add(schemaObject);
466
- if (isDiscriminator(schemaObject)) this.#setDiscriminator(schemaObject);
467
- if ("allOf" in schemaObject) enqueue(schemaObject.allOf);
468
- if ("oneOf" in schemaObject) enqueue(schemaObject.oneOf);
469
- if ("anyOf" in schemaObject) enqueue(schemaObject.anyOf);
470
- if ("not" in schemaObject) enqueue(schemaObject.not);
471
- if ("items" in schemaObject) enqueue(schemaObject.items);
472
- if ("prefixItems" in schemaObject) enqueue(schemaObject.prefixItems);
473
- if (schemaObject.properties) enqueue(Object.values(schemaObject.properties));
474
- if (schemaObject.additionalProperties && typeof schemaObject.additionalProperties === "object") enqueue(schemaObject.additionalProperties);
475
- };
476
- for (const schema of Object.values(components.schemas)) visit(schema);
477
- }
478
- /**
479
- * Oas does not have a getResponseBody(contentType)
480
- */
481
- #getResponseBodyFactory(responseBody) {
482
- function hasResponseBody(res = responseBody) {
483
- return !!res;
484
- }
485
- return (contentType) => {
486
- if (!hasResponseBody(responseBody)) return false;
487
- if (isReference(responseBody)) return false;
488
- if (!responseBody.content) return false;
489
- if (contentType) {
490
- if (!(contentType in responseBody.content)) return false;
491
- return responseBody.content[contentType];
492
- }
493
- let availableContentType;
494
- const contentTypes = Object.keys(responseBody.content);
495
- contentTypes.forEach((mt) => {
496
- if (!availableContentType && oas_utils.matchesMimeType.json(mt)) availableContentType = mt;
497
- });
498
- if (!availableContentType) contentTypes.forEach((mt) => {
499
- if (!availableContentType) availableContentType = mt;
500
- });
501
- if (availableContentType) return [
502
- availableContentType,
503
- responseBody.content[availableContentType],
504
- ...responseBody.description ? [responseBody.description] : []
505
- ];
506
- return false;
507
- };
508
- }
509
- getResponseSchema(operation, statusCode) {
510
- if (operation.schema.responses) Object.keys(operation.schema.responses).forEach((key) => {
511
- const schema = operation.schema.responses[key];
512
- const $ref = isReference(schema) ? schema.$ref : void 0;
513
- if (schema && $ref) operation.schema.responses[key] = this.get($ref);
514
- });
515
- const getResponseBody = this.#getResponseBodyFactory(operation.getResponseByStatusCode(statusCode));
516
- const { contentType } = this.#options;
517
- const responseBody = getResponseBody(contentType);
518
- if (responseBody === false) return {};
519
- const schema = Array.isArray(responseBody) ? responseBody[1].schema : responseBody.schema;
520
- if (!schema) return {};
521
- return this.dereferenceWithRef(schema);
522
- }
523
- getRequestSchema(operation) {
524
- const { contentType } = this.#options;
525
- if (operation.schema.requestBody) operation.schema.requestBody = this.dereferenceWithRef(operation.schema.requestBody);
526
- const requestBody = operation.getRequestBody(contentType);
527
- if (requestBody === false) return;
528
- const schema = Array.isArray(requestBody) ? requestBody[1].schema : requestBody.schema;
529
- if (!schema) return;
530
- return this.dereferenceWithRef(schema);
531
- }
532
- getParametersSchema(operation, inKey) {
533
- const { contentType = operation.getContentType() } = this.#options;
534
- const resolveParams = (params) => params.map((p) => this.dereferenceWithRef(p)).filter((p) => !!p && typeof p === "object" && "in" in p && "name" in p);
535
- const operationParams = resolveParams(operation.schema?.parameters || []);
536
- const pathItem = this.api?.paths?.[operation.path];
537
- const pathLevelParams = resolveParams(pathItem && !isReference(pathItem) && pathItem.parameters ? pathItem.parameters : []);
538
- const paramMap = /* @__PURE__ */ new Map();
539
- for (const p of pathLevelParams) if (p.name && p.in) paramMap.set(`${p.in}:${p.name}`, p);
540
- for (const p of operationParams) if (p.name && p.in) paramMap.set(`${p.in}:${p.name}`, p);
541
- const params = Array.from(paramMap.values()).filter((v) => v.in === inKey);
542
- if (!params.length) return null;
543
- return params.reduce((schema, pathParameters) => {
544
- const property = pathParameters.content?.[contentType]?.schema ?? pathParameters.schema;
545
- const required = typeof schema.required === "boolean" ? schema.required : [...schema.required || [], pathParameters.required ? pathParameters.name : void 0].filter(Boolean);
546
- const getDefaultStyle = (location) => {
547
- if (location === "query") return "form";
548
- if (location === "path") return "simple";
549
- return "simple";
550
- };
551
- const style = pathParameters.style || getDefaultStyle(inKey);
552
- const explode = pathParameters.explode !== void 0 ? pathParameters.explode : style === "form";
553
- if (inKey === "query" && style === "form" && explode === true && property?.type === "object" && property?.additionalProperties && !property?.properties) return {
554
- ...schema,
555
- description: pathParameters.description || schema.description,
556
- deprecated: schema.deprecated,
557
- example: property.example || schema.example,
558
- additionalProperties: property.additionalProperties
559
- };
560
- return {
561
- ...schema,
562
- description: schema.description,
563
- deprecated: schema.deprecated,
564
- example: schema.example,
565
- required,
566
- properties: {
567
- ...schema.properties,
568
- [pathParameters.name]: {
569
- description: pathParameters.description,
570
- ...property
571
- }
572
- }
573
- };
574
- }, {
575
- type: "object",
576
- required: [],
577
- properties: {}
578
- });
579
- }
580
- async validate() {
581
- return validate(this.api);
582
- }
583
- flattenSchema(schema) {
584
- return flattenSchema(schema);
585
- }
586
- /**
587
- * Get schemas from OpenAPI components (schemas, responses, requestBodies).
588
- * Returns schemas in dependency order along with name mapping for collision resolution.
589
- */
590
- getSchemas(options = {}) {
591
- const contentType = options.contentType ?? this.#options.contentType;
592
- const includes = options.includes ?? [
593
- "schemas",
594
- "requestBodies",
595
- "responses"
596
- ];
597
- const shouldResolveCollisions = options.collisionDetection ?? this.#options.collisionDetection ?? false;
598
- const components = this.getDefinition().components;
599
- const schemasWithMeta = [];
600
- if (includes.includes("schemas")) {
601
- const componentSchemas = components?.schemas || {};
602
- for (const [name, schemaObject] of Object.entries(componentSchemas)) {
603
- let schema = schemaObject;
604
- if (isReference(schemaObject)) {
605
- const resolved = this.get(schemaObject.$ref);
606
- if (resolved && !isReference(resolved)) schema = resolved;
607
- }
608
- schemasWithMeta.push({
609
- schema,
610
- source: "schemas",
611
- originalName: name
612
- });
613
- }
614
- }
615
- if (includes.includes("responses")) {
616
- const responses = components?.responses || {};
617
- for (const [name, response] of Object.entries(responses)) {
618
- const schema = extractSchemaFromContent(response.content, contentType);
619
- if (schema) {
620
- let resolvedSchema = schema;
621
- if (isReference(schema)) {
622
- const resolved = this.get(schema.$ref);
623
- if (resolved && !isReference(resolved)) resolvedSchema = resolved;
624
- }
625
- schemasWithMeta.push({
626
- schema: resolvedSchema,
627
- source: "responses",
628
- originalName: name
629
- });
630
- }
631
- }
632
- }
633
- if (includes.includes("requestBodies")) {
634
- const requestBodies = components?.requestBodies || {};
635
- for (const [name, request] of Object.entries(requestBodies)) {
636
- const schema = extractSchemaFromContent(request.content, contentType);
637
- if (schema) {
638
- let resolvedSchema = schema;
639
- if (isReference(schema)) {
640
- const resolved = this.get(schema.$ref);
641
- if (resolved && !isReference(resolved)) resolvedSchema = resolved;
642
- }
643
- schemasWithMeta.push({
644
- schema: resolvedSchema,
645
- source: "requestBodies",
646
- originalName: name
647
- });
648
- }
649
- }
650
- }
651
- const { schemas, nameMapping } = shouldResolveCollisions ? resolveCollisions(schemasWithMeta) : legacyResolve(schemasWithMeta);
652
- return {
653
- schemas: sortSchemas(schemas),
654
- nameMapping
655
- };
656
- }
657
- };
658
- //#endregion
659
- //#region src/oas/utils.ts
660
- /**
661
- * Narrows `doc` to a Swagger 2.0 document.
662
- * Swagger 2.0 documents do not have an `openapi` version key.
458
+ * @example
459
+ * ```ts
460
+ * if (isOpenApiV2Document(doc)) {
461
+ * // doc is OpenAPIV2.Document
462
+ * }
463
+ * ```
663
464
  */
664
465
  function isOpenApiV2Document(doc) {
665
466
  return !!doc && (0, remeda.isPlainObject)(doc) && !("openapi" in doc);
@@ -667,9 +468,15 @@ function isOpenApiV2Document(doc) {
667
468
  /**
668
469
  * Returns `true` when a schema should be treated as nullable.
669
470
  *
670
- * Covers three nullable signals across OAS versions:
671
- * - OAS 3.0: `nullable: true` or the vendor extension `x-nullable: true`.
672
- * - OAS 3.1 / JSON Schema: `type: 'null'` or `type: ['null', ...]` (multi-type array).
471
+ * Recognizes all nullable signals across OAS versions: `nullable: true` (OAS 3.0),
472
+ * `x-nullable: true` (vendor extension), `type: 'null'`, and `type: ['null', ...]` (OAS 3.1).
473
+ *
474
+ * @example
475
+ * ```ts
476
+ * isNullable({ type: 'string', nullable: true }) // true
477
+ * isNullable({ type: ['string', 'null'] }) // true
478
+ * isNullable({ type: 'string' }) // false
479
+ * ```
673
480
  */
674
481
  function isNullable(schema) {
675
482
  if ((schema?.nullable ?? schema?.["x-nullable"]) === true) return true;
@@ -679,34 +486,51 @@ function isNullable(schema) {
679
486
  return false;
680
487
  }
681
488
  /**
682
- * Narrows `obj` to an OpenAPI `$ref` pointer object.
683
- * Delegates to the `oas` package's own `isRef` helper.
489
+ * Returns `true` when `obj` is an OpenAPI `$ref` pointer object.
490
+ *
491
+ * @example
492
+ * ```ts
493
+ * isReference({ $ref: '#/components/schemas/Pet' }) // true
494
+ * isReference({ type: 'string' }) // false
495
+ * ```
684
496
  */
685
497
  function isReference(obj) {
686
- return !!obj && (0, oas_types.isRef)(obj);
498
+ return !!obj && typeof obj === "object" && "$ref" in obj;
687
499
  }
688
500
  /**
689
- * Returns `true` when `obj` is a schema that carries a structured OAS 3.x `discriminator`
690
- * object — as opposed to a plain-string discriminator found in some Swagger 2 specs.
501
+ * Returns `true` when `obj` is a schema with a structured OAS 3.x `discriminator` object.
502
+ *
503
+ * @example
504
+ * ```ts
505
+ * isDiscriminator({ discriminator: { propertyName: 'type', mapping: {} } }) // true
506
+ * isDiscriminator({ discriminator: 'type' }) // false (Swagger 2 string form)
507
+ * ```
691
508
  */
692
509
  function isDiscriminator(obj) {
693
510
  const record = obj;
694
511
  return !!obj && !!record["discriminator"] && typeof record["discriminator"] !== "string";
695
512
  }
513
+ //#endregion
514
+ //#region src/factory.ts
696
515
  /**
697
- * Loads, dereferences, and wraps a raw OpenAPI document into an `Oas` instance.
516
+ * Loads and dereferences an OpenAPI document, returning the raw `Document`.
517
+ *
518
+ * Accepts a file path string or an already-parsed document object. File paths are bundled via
519
+ * Redocly to resolve external `$ref`s. Swagger 2.0 documents are automatically up-converted
520
+ * to OpenAPI 3.0 via `swagger2openapi`.
698
521
  *
699
- * When given a file path string with `canBundle: true` (the default), Redocly's
700
- * bundler resolves all external `$ref`s first. Swagger 2.0 documents are
701
- * automatically up-converted to OpenAPI 3.0 via `swagger2openapi`.
522
+ * @example
523
+ * ```ts
524
+ * const document = await parseDocument('./openapi.yaml')
525
+ * const document = await parse(rawDocumentObject, { canBundle: false })
526
+ * ```
702
527
  */
703
- async function parse(pathOrApi, { oasClass = Oas, canBundle = true, enablePaths = true } = {}) {
704
- if (typeof pathOrApi === "string" && canBundle) return parse((await (0, _redocly_openapi_core.bundle)({
528
+ async function parseDocument(pathOrApi, { canBundle = true, enablePaths = true } = {}) {
529
+ if (typeof pathOrApi === "string" && canBundle) return parseDocument((await (0, _redocly_openapi_core.bundle)({
705
530
  ref: pathOrApi,
706
531
  config: await (0, _redocly_openapi_core.loadConfig)(),
707
532
  base: pathOrApi
708
533
  })).bundle.parsed, {
709
- oasClass,
710
534
  canBundle,
711
535
  enablePaths
712
536
  });
@@ -716,26 +540,27 @@ async function parse(pathOrApi, { oasClass = Oas, canBundle = true, enablePaths
716
540
  }).load();
717
541
  if (isOpenApiV2Document(document)) {
718
542
  const { openapi } = await swagger2openapi.default.convertObj(document, { anchors: true });
719
- return new oasClass(openapi);
543
+ return openapi;
720
544
  }
721
- return new oasClass(document);
545
+ return document;
722
546
  }
723
547
  /**
724
- * Deep-merges multiple OpenAPI documents into a single `Oas` instance.
548
+ * Deep-merges multiple OpenAPI documents into a single `Document`.
725
549
  *
726
- * Each document is parsed independently (without path dereferencing) and then
727
- * recursively merged using `remeda`'s `mergeDeep`. The result is re-parsed so
728
- * the returned `Oas` instance is fully initialized.
550
+ * Each document is parsed independently then recursively merged with `remeda`'s `mergeDeep`.
551
+ * Throws when the input array is empty.
729
552
  *
730
- * Throws when the input array is empty — at least one document is required.
553
+ * @example
554
+ * ```ts
555
+ * const document = await mergeDocuments(['./pets.yaml', './orders.yaml'])
556
+ * ```
731
557
  */
732
- async function merge(pathOrApi, { oasClass = Oas } = {}) {
733
- const instances = await Promise.all(pathOrApi.map((p) => parse(p, {
734
- oasClass,
558
+ async function mergeDocuments(pathOrApi) {
559
+ const documents = await Promise.all(pathOrApi.map((p) => parseDocument(p, {
735
560
  enablePaths: false,
736
561
  canBundle: false
737
562
  })));
738
- if (instances.length === 0) throw new Error("No OAS instances provided for merging.");
563
+ if (documents.length === 0) throw new Error("No OAS documents provided for merging.");
739
564
  const seed = {
740
565
  openapi: MERGE_OPENAPI_VERSION,
741
566
  info: {
@@ -745,38 +570,248 @@ async function merge(pathOrApi, { oasClass = Oas } = {}) {
745
570
  paths: {},
746
571
  components: { schemas: {} }
747
572
  };
748
- return parse(instances.reduce((acc, current) => (0, remeda.mergeDeep)(acc, current.document), seed), { oasClass });
573
+ return parseDocument(documents.reduce((acc, current) => (0, remeda.mergeDeep)(acc, current), seed));
574
+ }
575
+ /**
576
+ * Creates a `Document` from an `AdapterSource`.
577
+ *
578
+ * Handles all three source types:
579
+ * - `{ type: 'path' }` — resolves and bundles a local file path or remote URL.
580
+ * - `{ type: 'paths' }` — merges multiple file paths into a single document.
581
+ * - `{ type: 'data' }` — parses an inline string (YAML/JSON) or raw object.
582
+ *
583
+ * @example
584
+ * ```ts
585
+ * const document = await parseFromConfig({ type: 'path', path: './openapi.yaml' })
586
+ * const document = await parseFromConfig({ type: 'data', data: '{"openapi":"3.0.0",...}' })
587
+ * ```
588
+ */
589
+ function parseFromConfig(source) {
590
+ if (source.type === "data") {
591
+ if (typeof source.data === "object") return parseDocument(structuredClone(source.data));
592
+ try {
593
+ return parseDocument(_stoplight_yaml.default.parse(source.data));
594
+ } catch {
595
+ return parseDocument(source.data);
596
+ }
597
+ }
598
+ if (source.type === "paths") return mergeDocuments(source.paths);
599
+ if (new URLPath(source.path).isURL) return parseDocument(source.path);
600
+ return parseDocument(node_path.default.resolve(node_path.default.dirname(source.path), source.path));
601
+ }
602
+ /**
603
+ * Validates an OpenAPI document using `oas-normalize` with colorized error output.
604
+ *
605
+ * @example
606
+ * ```ts
607
+ * await validateDocument(document)
608
+ * ```
609
+ */
610
+ async function validateDocument(document) {
611
+ try {
612
+ await new oas_normalize.default(document, {
613
+ enablePaths: true,
614
+ colorizeErrors: true
615
+ }).validate({ parser: { validate: { errors: { colorize: true } } } });
616
+ } catch (_err) {}
617
+ }
618
+ //#endregion
619
+ //#region src/refs.ts
620
+ /**
621
+ * Resolves a local JSON pointer reference from a document.
622
+ *
623
+ * Accepts `#/...` refs. Returns `null` for empty or non-local refs.
624
+ * Throws when the pointer cannot be resolved.
625
+ *
626
+ * @example
627
+ * ```ts
628
+ * resolveRef<SchemaObject>(document, '#/components/schemas/Pet') // SchemaObject | null
629
+ * ```
630
+ */
631
+ function resolveRef(document, $ref) {
632
+ const origRef = $ref;
633
+ $ref = $ref.trim();
634
+ if ($ref === "") return null;
635
+ if ($ref.startsWith("#")) $ref = globalThis.decodeURIComponent($ref.substring(1));
636
+ else return null;
637
+ const current = jsonpointer.default.get(document, $ref);
638
+ if (!current) throw new Error(`Could not find a definition for ${origRef}.`);
639
+ return current;
640
+ }
641
+ /**
642
+ * Resolves a `$ref` object while preserving the original `$ref` field on the result.
643
+ *
644
+ * Useful for parser flows that need both dereferenced fields and pointer
645
+ * identity (for naming/import purposes). Non-reference values are returned as-is.
646
+ *
647
+ * @example
648
+ * ```ts
649
+ * dereferenceWithRef(document, { $ref: '#/components/schemas/Pet' })
650
+ * // { $ref: '#/components/schemas/Pet', type: 'object', properties: { ... } }
651
+ * ```
652
+ */
653
+ function dereferenceWithRef(document, schema) {
654
+ if (isReference(schema)) return {
655
+ ...schema,
656
+ ...resolveRef(document, schema.$ref),
657
+ $ref: schema.$ref
658
+ };
659
+ return schema;
749
660
  }
661
+ //#endregion
662
+ //#region src/resolvers.ts
750
663
  /**
751
- * Constructs an `Oas` instance from a Kubb `Config` object.
664
+ * Resolves `{variable}` placeholders in an OpenAPI server URL.
665
+ *
666
+ * Resolution order per variable: `overrides[key]` → `variable.default` → left unreplaced.
667
+ * Throws when an override value is not in the variable's allowed `enum` list.
752
668
  *
753
- * Handles all three input forms supported by `Config`:
754
- * - `data` — an inline YAML string, JSON string, or pre-parsed object.
755
- * - Array — multiple file paths that are deep-merged via {@link merge}.
756
- * - `path` a local file path or a remote URL.
669
+ * @example
670
+ * ```ts
671
+ * resolveServerUrl(
672
+ * { url: 'https://{env}.api.example.com', variables: { env: { default: 'dev', enum: ['dev', 'prod'] } } },
673
+ * { env: 'prod' },
674
+ * )
675
+ * // 'https://prod.api.example.com'
676
+ * ```
677
+ */
678
+ function resolveServerUrl(server, overrides) {
679
+ if (!server.variables) return server.url;
680
+ let url = server.url;
681
+ for (const [key, variable] of Object.entries(server.variables)) {
682
+ const value = overrides?.[key] ?? (variable.default != null ? String(variable.default) : void 0);
683
+ if (value === void 0) continue;
684
+ 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(", ")}.`);
685
+ url = url.replaceAll(`{${key}}`, value);
686
+ }
687
+ return url;
688
+ }
689
+ /**
690
+ * Looks up the Kubb `SchemaType` for a given OAS `format` string.
691
+ * Returns `undefined` for formats not in `formatMap` (e.g. `int64`, `date-time`),
692
+ * which are handled separately because their output depends on parser options.
693
+ */
694
+ function getSchemaType(format) {
695
+ return formatMap[format] ?? null;
696
+ }
697
+ /**
698
+ * Maps an OAS primitive type string to its `PrimitiveSchemaType` equivalent.
699
+ * Numeric types (`number`, `integer`, `bigint`) are returned unchanged;
700
+ * `boolean` maps to `'boolean'`; everything else defaults to `'string'`.
701
+ */
702
+ function getPrimitiveType(type) {
703
+ if (type === "number" || type === "integer" || type === "bigint") return type;
704
+ if (type === "boolean") return "boolean";
705
+ return "string";
706
+ }
707
+ /**
708
+ * Narrows a raw content-type string to the `MediaType` union recognized by Kubb.
709
+ * Returns `undefined` for content types not present in `KNOWN_MEDIA_TYPES`.
710
+ */
711
+ function getMediaType(contentType) {
712
+ return Object.values(_kubb_ast.mediaTypes).includes(contentType) ? contentType : null;
713
+ }
714
+ /**
715
+ * Returns all resolved parameters for an operation, merging path-level and operation-level entries.
716
+ *
717
+ * Operation-level parameters take precedence over path-level ones with the same `in:name` key.
718
+ * `$ref` parameters are resolved via `dereferenceWithRef` to restore backward compatibility
719
+ * with `oas` v31+ which otherwise filters them out.
720
+ *
721
+ * @example
722
+ * ```ts
723
+ * getParameters(document, operation)
724
+ * // [{ name: 'petId', in: 'path', required: true, schema: { type: 'integer' } }]
725
+ * ```
726
+ */
727
+ function getParameters(document, operation) {
728
+ const resolveParams = (params) => params.map((p) => dereferenceWithRef(document, p)).filter((p) => !!p && typeof p === "object" && "in" in p && "name" in p);
729
+ const operationParams = resolveParams(operation.schema?.parameters || []);
730
+ const pathItem = document.paths?.[operation.path];
731
+ const pathLevelParams = resolveParams(pathItem && !isReference(pathItem) && pathItem.parameters ? pathItem.parameters : []);
732
+ const paramMap = /* @__PURE__ */ new Map();
733
+ for (const p of pathLevelParams) if (p.name && p.in) paramMap.set(`${p.in}:${p.name}`, p);
734
+ for (const p of operationParams) if (p.name && p.in) paramMap.set(`${p.in}:${p.name}`, p);
735
+ return Array.from(paramMap.values());
736
+ }
737
+ function getResponseBody(responseBody, contentType) {
738
+ if (!responseBody) return false;
739
+ if (isReference(responseBody)) return false;
740
+ const body = responseBody;
741
+ if (!body.content) return false;
742
+ if (contentType) {
743
+ if (!(contentType in body.content)) return false;
744
+ return body.content[contentType];
745
+ }
746
+ let availableContentType;
747
+ const contentTypes = Object.keys(body.content);
748
+ contentTypes.forEach((mt) => {
749
+ if (!availableContentType && oas_utils.matchesMimeType.json(mt)) availableContentType = mt;
750
+ });
751
+ if (!availableContentType) contentTypes.forEach((mt) => {
752
+ if (!availableContentType) availableContentType = mt;
753
+ });
754
+ if (availableContentType) return [
755
+ availableContentType,
756
+ body.content[availableContentType],
757
+ ...body.description ? [body.description] : []
758
+ ];
759
+ return false;
760
+ }
761
+ /**
762
+ * Returns the response schema for a given operation and HTTP status code.
763
+ *
764
+ * Returns an empty object `{}` when no response body schema is available.
765
+ *
766
+ * @example
767
+ * ```ts
768
+ * getResponseSchema(document, operation, 200) // SchemaObject
769
+ * getResponseSchema(document, operation, '4XX') // {}
770
+ * ```
771
+ */
772
+ function getResponseSchema(document, operation, statusCode, options = {}) {
773
+ if (operation.schema.responses) Object.keys(operation.schema.responses).forEach((key) => {
774
+ const schema = operation.schema.responses[key];
775
+ const $ref = isReference(schema) ? schema.$ref : void 0;
776
+ if (schema && $ref) operation.schema.responses[key] = resolveRef(document, $ref);
777
+ });
778
+ const responseBody = getResponseBody(operation.getResponseByStatusCode(statusCode), options.contentType);
779
+ if (responseBody === false) return {};
780
+ const schema = Array.isArray(responseBody) ? responseBody[1].schema : responseBody.schema;
781
+ if (!schema) return {};
782
+ return dereferenceWithRef(document, schema);
783
+ }
784
+ /**
785
+ * Returns the request body schema for an operation, or `undefined` when absent.
786
+ *
787
+ * @example
788
+ * ```ts
789
+ * getRequestSchema(document, operation) // SchemaObject | null
790
+ * ```
757
791
  */
758
- function parseFromConfig(config, oasClass = Oas) {
759
- if ("data" in config.input) {
760
- if (typeof config.input.data === "object") return parse(structuredClone(config.input.data), { oasClass });
761
- try {
762
- return parse(_stoplight_yaml.default.parse(config.input.data), { oasClass });
763
- } catch {
764
- return parse(config.input.data, { oasClass });
765
- }
766
- }
767
- if (Array.isArray(config.input)) return merge(config.input.map((input) => node_path.default.resolve(config.root, input.path)), { oasClass });
768
- if (new URLPath(config.input.path).isURL) return parse(config.input.path, { oasClass });
769
- return parse(node_path.default.resolve(config.root, config.input.path), { oasClass });
792
+ function getRequestSchema(document, operation, options = {}) {
793
+ if (operation.schema.requestBody) operation.schema.requestBody = dereferenceWithRef(document, operation.schema.requestBody);
794
+ const requestBody = operation.getRequestBody(options.contentType);
795
+ if (requestBody === false) return null;
796
+ const schema = Array.isArray(requestBody) ? requestBody[1].schema : requestBody.schema;
797
+ if (!schema) return null;
798
+ return dereferenceWithRef(document, schema);
770
799
  }
771
800
  /**
772
- * Flattens a single-member or keyword-only `allOf` into its parent schema.
801
+ * Flattens a keyword-only `allOf` into its parent schema.
802
+ *
803
+ * Only flattens when every member is a plain fragment — no `$ref` and no structural keywords
804
+ * (see `structuralKeys`). Outer schema values take precedence over fragment values.
805
+ * Returns `null` for a `null` input, and the original schema unchanged when flattening is unsafe.
773
806
  *
774
- * Flattening is only performed when every `allOf` member is a "plain fragment" —
775
- * i.e. contains no structural composition keywords (see `structuralKeys`) and no
776
- * `$ref`. When any member carries structural meaning, the schema is returned unchanged.
807
+ * @example
808
+ * ```ts
809
+ * flattenSchema({ allOf: [{ description: 'A pet' }], type: 'object', properties: {} })
810
+ * // { type: 'object', properties: {}, description: 'A pet' }
777
811
  *
778
- * Keyword values from allOf members are merged into the parent on a first-write basis:
779
- * outer schema values always win over fragment values.
812
+ * flattenSchema({ allOf: [{ $ref: '#/components/schemas/Pet' }] })
813
+ * // returned unchanged contains a $ref
814
+ * ```
780
815
  */
781
816
  function flattenSchema(schema) {
782
817
  if (!schema?.allOf || schema.allOf.length === 0) return schema ?? null;
@@ -789,18 +824,26 @@ function flattenSchema(schema) {
789
824
  return merged;
790
825
  }
791
826
  /**
792
- * Validates an OpenAPI document using `oas-normalize`.
793
- * Enables path validation and colorized error output.
827
+ * Extracts the inline schema from a media-type `content` map.
828
+ *
829
+ * Prefers `preferredContentType` when given; otherwise uses the first key in the map.
830
+ * Returns `null` when `content` is absent, the schema is missing, or the schema is a `$ref`.
831
+ *
832
+ * @example
833
+ * ```ts
834
+ * extractSchemaFromContent(operation.content, 'application/json')
835
+ * // SchemaObject | null
836
+ * ```
794
837
  */
795
- async function validate(document) {
796
- return new oas_normalize.default(document, {
797
- enablePaths: true,
798
- colorizeErrors: true
799
- }).validate({ parser: { validate: { errors: { colorize: true } } } });
838
+ function extractSchemaFromContent(content, preferredContentType) {
839
+ if (!content) return null;
840
+ const firstContentType = Object.keys(content)[0] ?? "application/json";
841
+ const schema = content[preferredContentType ?? firstContentType]?.schema;
842
+ if (schema && "$ref" in schema) return null;
843
+ return schema ?? null;
800
844
  }
801
845
  /**
802
- * Walks a schema tree and collects the names of all `#/components/schemas/<name>` refs.
803
- * Used by `sortSchemas` to build the dependency graph.
846
+ * Walks a schema tree and collects the names of all `#/components/schemas/<name>` `$ref`s.
804
847
  */
805
848
  function collectRefs(schema, refs = /* @__PURE__ */ new Set()) {
806
849
  if (Array.isArray(schema)) {
@@ -816,11 +859,14 @@ function collectRefs(schema, refs = /* @__PURE__ */ new Set()) {
816
859
  /**
817
860
  * Returns a copy of `schemas` topologically sorted by `$ref` dependency.
818
861
  *
819
- * Schemas with no references come first; schemas that are referenced by others
820
- * come before those that reference them. This ensures code generators emit
821
- * referenced types before the types that depend on them.
862
+ * Referenced schemas appear before the schemas that depend on them, so code generators
863
+ * can emit types in the correct order. Cycles are silently skipped.
822
864
  *
823
- * Cycles are silently skipped via the `stack` guard inside `visit`.
865
+ * @example
866
+ * ```ts
867
+ * const sorted = sortSchemas({ Order: orderSchema, Pet: petSchema })
868
+ * // Pet appears before Order when Order.$ref points at Pet
869
+ * ```
824
870
  */
825
871
  function sortSchemas(schemas) {
826
872
  const deps = /* @__PURE__ */ new Map();
@@ -840,283 +886,172 @@ function sortSchemas(schemas) {
840
886
  for (const name of sorted) result[name] = schemas[name];
841
887
  return result;
842
888
  }
843
- /**
844
- * Extracts the inline schema from a media-type `content` map.
845
- *
846
- * Prefers `preferredContentType` when provided; otherwise falls back to the
847
- * first key in the map. Returns `null` when:
848
- * - `content` is absent.
849
- * - The target content-type has no `schema` entry.
850
- * - The schema is a `$ref` (callers resolve refs separately).
851
- */
852
- function extractSchemaFromContent(content, preferredContentType) {
853
- if (!content) return null;
854
- const firstContentType = Object.keys(content)[0] ?? "application/json";
855
- const schema = content[preferredContentType ?? firstContentType]?.schema;
856
- if (schema && "$ref" in schema) return null;
857
- return schema ?? null;
858
- }
859
- /**
860
- * Returns the PascalCase suffix appended to a component name when resolving
861
- * cross-source name collisions (schemas vs. responses vs. requestBodies).
862
- */
889
+ const semanticSuffixes = {
890
+ schemas: "Schema",
891
+ responses: "Response",
892
+ requestBodies: "Request"
893
+ };
863
894
  function getSemanticSuffix(source) {
864
- switch (source) {
865
- case "schemas": return "Schema";
866
- case "responses": return "Response";
867
- case "requestBodies": return "Request";
868
- }
895
+ return semanticSuffixes[source];
869
896
  }
870
- /**
871
- * Builds `GetSchemasResult` without any collision detection.
872
- * Each schema is registered under its original component name and its full
873
- * `#/components/<source>/<name>` ref path.
874
- */
875
- function legacyResolve(schemasWithMeta) {
876
- const schemas = {};
877
- const nameMapping = /* @__PURE__ */ new Map();
878
- for (const item of schemasWithMeta) {
879
- schemas[item.originalName] = item.schema;
880
- nameMapping.set(`#/components/${item.source}/${item.originalName}`, item.originalName);
881
- }
882
- return {
883
- schemas,
884
- nameMapping
885
- };
897
+ function resolveSchemaRef(document, schema) {
898
+ if (!isReference(schema)) return schema;
899
+ const resolved = resolveRef(document, schema.$ref);
900
+ return resolved && !isReference(resolved) ? resolved : schema;
886
901
  }
887
902
  /**
888
- * Builds `GetSchemasResult` with automatic name-collision resolution.
903
+ * Collects component schemas from one or more sources and resolves name collisions.
904
+ *
905
+ * Sources default to `['schemas', 'requestBodies', 'responses']`. Returned schemas are
906
+ * topologically sorted by `$ref` dependency so generators emit types in the correct order.
889
907
  *
890
908
  * When two or more schemas normalize to the same PascalCase name:
891
- * - If they share the same source, a numeric suffix (`2`, `3`, …) is appended.
892
- * - If they come from different sources (schemas / responses / requestBodies),
893
- * a semantic suffix (`Schema`, `Response`, `Request`) is appended.
909
+ * - Same source numeric suffix (`2`, `3`, …).
910
+ * - Different sources semantic suffix (`Schema`, `Response`, `Request`).
894
911
  *
895
- * Non-colliding schemas are left unchanged.
912
+ * @example
913
+ * ```ts
914
+ * const { schemas, nameMapping } = getSchemas(document, { contentType: 'application/json' })
915
+ * ```
896
916
  */
897
- function resolveCollisions(schemasWithMeta) {
898
- const schemas = {};
899
- const nameMapping = /* @__PURE__ */ new Map();
917
+ function getSchemas(document, { contentType }) {
918
+ const components = document.components;
919
+ const candidates = [...Object.entries(components?.schemas ?? {}).map(([name, schema]) => ({
920
+ schema: resolveSchemaRef(document, schema),
921
+ source: "schemas",
922
+ originalName: name
923
+ })), ...["responses", "requestBodies"].flatMap((source) => Object.entries(components?.[source] ?? {}).flatMap(([name, item]) => {
924
+ const schema = extractSchemaFromContent(item.content, contentType);
925
+ return schema ? [{
926
+ schema: resolveSchemaRef(document, schema),
927
+ source,
928
+ originalName: name
929
+ }] : [];
930
+ }))];
900
931
  const normalizedNames = /* @__PURE__ */ new Map();
901
- for (const item of schemasWithMeta) {
902
- const normalized = pascalCase(item.originalName);
903
- const bucket = normalizedNames.get(normalized) ?? [];
932
+ for (const item of candidates) {
933
+ const key = pascalCase(item.originalName);
934
+ const bucket = normalizedNames.get(key) ?? [];
904
935
  bucket.push(item);
905
- normalizedNames.set(normalized, bucket);
906
- }
907
- for (const [, items] of normalizedNames) {
908
- if (items.length === 1) {
909
- const item = items[0];
910
- schemas[item.originalName] = item.schema;
911
- nameMapping.set(`#/components/${item.source}/${item.originalName}`, item.originalName);
912
- continue;
913
- }
914
- if (new Set(items.map((item) => item.source)).size === 1) items.forEach((item, index) => {
915
- const uniqueName = item.originalName + (index === 0 ? "" : (index + 1).toString());
916
- schemas[uniqueName] = item.schema;
917
- nameMapping.set(`#/components/${item.source}/${item.originalName}`, uniqueName);
918
- });
919
- else items.forEach((item) => {
920
- const uniqueName = item.originalName + getSemanticSuffix(item.source);
921
- schemas[uniqueName] = item.schema;
922
- nameMapping.set(`#/components/${item.source}/${item.originalName}`, uniqueName);
923
- });
936
+ normalizedNames.set(key, bucket);
924
937
  }
938
+ const schemas = {};
939
+ const nameMapping = /* @__PURE__ */ new Map();
940
+ const multipleSources = (items) => new Set(items.map((i) => i.source)).size > 1;
941
+ for (const [, items] of normalizedNames) items.forEach((item, index) => {
942
+ const suffix = items.length === 1 ? "" : multipleSources(items) ? getSemanticSuffix(item.source) : index === 0 ? "" : String(index + 1);
943
+ const uniqueName = item.originalName + suffix;
944
+ schemas[uniqueName] = item.schema;
945
+ nameMapping.set(`#/components/${item.source}/${item.originalName}`, uniqueName);
946
+ });
925
947
  return {
926
- schemas,
948
+ schemas: sortSchemas(schemas),
927
949
  nameMapping
928
950
  };
929
951
  }
930
- //#endregion
931
- //#region src/parser.ts
932
- /**
933
- * Default values for all `Options` fields.
934
- */
935
- const DEFAULT_OPTIONS = {
936
- dateType: "string",
937
- integerType: "number",
938
- unknownType: "any",
939
- emptySchemaType: "any",
940
- enumSuffix: "enum"
941
- };
942
- /**
943
- * Looks up the Kubb `SchemaType` for a given OAS `format` string.
944
- * Returns `undefined` for formats not in `formatMap` (e.g. `int64`, `date-time`),
945
- * which are handled separately because their output depends on parser options.
946
- */
947
- function formatToSchemaType(format) {
948
- return formatMap[format];
949
- }
950
- /**
951
- * Extracts the final path segment of a JSON Pointer `$ref` string.
952
- * For `#/components/schemas/Order` this returns `'Order'`.
953
- * Falls back to the full ref string when no slash is present.
954
- */
955
- function extractRefName($ref) {
956
- return $ref.split("/").at(-1) ?? $ref;
957
- }
958
952
  /**
959
- * Maps an OAS primitive type string to its `PrimitiveSchemaType` equivalent.
960
- * Numeric types (`number`, `integer`, `bigint`) are returned unchanged;
961
- * `boolean` maps to `'boolean'`; everything else defaults to `'string'`.
953
+ * Resolves the AST type descriptor for a date/time format, honoring the `dateType` option.
954
+ * Returns `null` when `dateType: false`, signalling the format should fall through to `string`.
962
955
  */
963
- function getPrimitiveType(type) {
964
- if (type === "number" || type === "integer" || type === "bigint") return type;
965
- if (type === "boolean") return "boolean";
966
- return "string";
956
+ function getDateType(options, format) {
957
+ if (!options.dateType) return null;
958
+ if (format === "date-time") {
959
+ if (options.dateType === "date") return {
960
+ type: "date",
961
+ representation: "date"
962
+ };
963
+ if (options.dateType === "stringOffset") return {
964
+ type: "datetime",
965
+ offset: true
966
+ };
967
+ if (options.dateType === "stringLocal") return {
968
+ type: "datetime",
969
+ local: true
970
+ };
971
+ return {
972
+ type: "datetime",
973
+ offset: false
974
+ };
975
+ }
976
+ if (format === "date") return {
977
+ type: "date",
978
+ representation: options.dateType === "date" ? "date" : "string"
979
+ };
980
+ return {
981
+ type: "time",
982
+ representation: options.dateType === "date" ? "date" : "string"
983
+ };
967
984
  }
968
985
  /**
969
- * Narrows a raw content-type string to the `MediaType` union recognized by Kubb.
970
- * Returns `undefined` for content types not present in `KNOWN_MEDIA_TYPES`.
986
+ * Collects the shared metadata fields passed to every `createSchema` call.
971
987
  */
972
- function toMediaType(contentType) {
973
- return knownMediaTypes.includes(contentType) ? contentType : void 0;
988
+ function buildSchemaNode(schema, name, nullable, defaultValue) {
989
+ return {
990
+ name,
991
+ nullable,
992
+ title: schema.title,
993
+ description: schema.description,
994
+ deprecated: schema.deprecated,
995
+ readOnly: schema.readOnly,
996
+ writeOnly: schema.writeOnly,
997
+ default: defaultValue,
998
+ example: schema.example
999
+ };
974
1000
  }
1001
+ //#endregion
1002
+ //#region src/parser.ts
975
1003
  /**
976
- * When a discriminator is present, replaces the discriminator property's schema with
977
- * an enum of the mapping keys so downstream code emits a precise literal-union type.
978
- * Returns the original schema unchanged when there is no discriminator or no mapping.
1004
+ * Normalize a malformed `{ type: 'array', enum: [...] }` schema by moving the
1005
+ * enum values into the items sub-schema. This pattern is technically invalid OAS
1006
+ * but appears in the wild and must be handled gracefully.
979
1007
  */
980
- function applyDiscriminatorEnum(schema) {
981
- if (!isDiscriminator(schema)) return schema;
982
- const propName = schema.discriminator.propertyName;
983
- if (!schema.properties?.[propName]) return schema;
1008
+ function normalizeArrayEnum(schema) {
1009
+ const normalizedItems = {
1010
+ ...typeof schema.items === "object" && !Array.isArray(schema.items) ? schema.items : {},
1011
+ enum: schema.enum
1012
+ };
1013
+ const { enum: _enum, ...schemaWithoutEnum } = schema;
984
1014
  return {
985
- ...schema,
986
- properties: {
987
- ...schema.properties,
988
- [propName]: {
989
- ...schema.properties[propName],
990
- enum: schema.discriminator.mapping ? Object.keys(schema.discriminator.mapping) : void 0
991
- }
992
- }
1015
+ ...schemaWithoutEnum,
1016
+ items: normalizedItems
993
1017
  };
994
1018
  }
995
1019
  /**
996
- * Creates an OAS parser that converts an OpenAPI/Swagger spec into
997
- * the `@kubb/ast` tree.
998
- *
999
- * Options are passed per-call to `parse` or `convertSchema` rather than
1000
- * at construction time, keeping the factory lightweight.
1020
+ * Builds the internal converter functions for a given `OasParserContext`.
1001
1021
  *
1002
- * This is the **kubb-parser** stage of the compilation lifecycle:
1003
- * OpenAPI / Swagger → Kubb AST
1004
- *
1005
- * No code is generated here; the resulting tree is spec-agnostic and can
1006
- * be consumed by any downstream plugin (plugin-ts, plugin-zod, …).
1007
- *
1008
- * @example
1009
- * ```ts
1010
- * const parser = createOasParser(oas)
1011
- * const root = parser.parse({ emptySchemaType: 'unknown' })
1012
- * ```
1022
+ * All `convert*` functions are defined as function declarations so they can freely
1023
+ * reference each other and `parseSchema` via JS hoisting (mutual recursion).
1013
1024
  */
1014
- function createOasParser(oas, { contentType, collisionDetection } = {}) {
1015
- const { schemas: schemaObjects, nameMapping } = oas.getSchemas({
1016
- contentType,
1017
- collisionDetection
1018
- });
1019
- /**
1020
- * Maps an `'any' | 'unknown' | 'void'` option string to the corresponding `SchemaType` constant.
1021
- * Used for both `unknownType` (unannotated schemas) and `emptySchemaType` (empty `{}` schemas).
1022
- */
1023
- function resolveTypeOption(value) {
1024
- if (value === "any") return _kubb_ast.schemaTypes.any;
1025
- if (value === "void") return _kubb_ast.schemaTypes.void;
1026
- return _kubb_ast.schemaTypes.unknown;
1027
- }
1028
- /**
1029
- * Resolves the AST type and datetime modifiers for a date/time format, honoring the `dateType` option.
1030
- * Returns `undefined` when `dateType` is `false`, meaning the format should fall through to `string`.
1031
- */
1032
- function getDateType(options, format) {
1033
- if (!options.dateType) return;
1034
- if (format === "date-time") {
1035
- if (options.dateType === "date") return {
1036
- type: "date",
1037
- representation: "date"
1038
- };
1039
- if (options.dateType === "stringOffset") return {
1040
- type: "datetime",
1041
- offset: true
1042
- };
1043
- if (options.dateType === "stringLocal") return {
1044
- type: "datetime",
1045
- local: true
1046
- };
1047
- return {
1048
- type: "datetime",
1049
- offset: false
1050
- };
1051
- }
1052
- if (format === "date") return {
1053
- type: "date",
1054
- representation: options.dateType === "date" ? "date" : "string"
1055
- };
1056
- return {
1057
- type: "time",
1058
- representation: options.dateType === "date" ? "date" : "string"
1059
- };
1060
- }
1025
+ function createSchemaParser(ctx) {
1026
+ const document = ctx.document;
1061
1027
  /**
1062
- * Shared metadata fields included in every `createSchema` call.
1063
- * Centralizes the common properties so sub-handlers don't repeat them.
1028
+ * Converts a `$ref` schema into a `RefSchemaNode`.
1064
1029
  */
1065
- function buildSchemaBase(schema, name, nullable, defaultValue) {
1066
- return {
1067
- name,
1030
+ function convertRef({ schema, nullable, defaultValue }) {
1031
+ return (0, _kubb_ast.createSchema)({
1032
+ type: "ref",
1033
+ name: (0, _kubb_ast.extractRefName)(schema.$ref),
1034
+ ref: schema.$ref,
1068
1035
  nullable,
1069
- title: schema.title,
1070
1036
  description: schema.description,
1071
1037
  deprecated: schema.deprecated,
1072
1038
  readOnly: schema.readOnly,
1073
1039
  writeOnly: schema.writeOnly,
1074
- default: defaultValue,
1075
- example: schema.example
1076
- };
1077
- }
1078
- /**
1079
- * Converts a `$ref` schema pointer into a `RefSchemaNode`.
1080
- *
1081
- * In OAS 3.0 siblings of `$ref` are technically ignored by the spec, but Kubb intentionally
1082
- * preserves them so that annotations like `pattern`, `description`, and `nullable` are
1083
- * reflected in generated JSDoc and type modifiers.
1084
- */
1085
- function convertRef({ schema, nullable, defaultValue }) {
1086
- const schemaObject = schema;
1087
- return (0, _kubb_ast.createSchema)({
1088
- type: "ref",
1089
- name: extractRefName(schemaObject.$ref),
1090
- ref: schemaObject.$ref,
1091
- nullable,
1092
- description: schemaObject.description,
1093
- deprecated: schemaObject.deprecated,
1094
- readOnly: schemaObject.readOnly,
1095
- writeOnly: schemaObject.writeOnly,
1096
- pattern: schemaObject.type === "string" ? schemaObject.pattern : void 0,
1097
- example: schemaObject.example,
1040
+ pattern: schema.type === "string" ? schema.pattern : void 0,
1041
+ example: schema.example,
1098
1042
  default: defaultValue
1099
1043
  });
1100
1044
  }
1101
1045
  /**
1102
- * Converts a `allOf` schema into either a flattened member node (single-member `allOf`)
1103
- * or an `IntersectionSchemaNode` (multi-member `allOf`).
1104
- *
1105
- * Single-member `allOf` without sibling structural keys is the common OAS 3.0 pattern for
1106
- * annotating a `$ref` or primitive with extra constraints; it is flattened to avoid
1107
- * producing needless intersection wrappers.
1108
- *
1109
- * The flatten path is skipped when the outer schema carries structural keys that cannot be
1110
- * merged into annotation fields: `properties`, `required`, or `additionalProperties`.
1111
- * Those cases must become an intersection so the constraints are preserved.
1112
- *
1113
- * Circular references through discriminator parents are detected and skipped to prevent
1114
- * infinite recursion during code generation.
1046
+ * Converts an `allOf` schema into a flattened node or an `IntersectionSchemaNode`.
1115
1047
  */
1116
- function convertAllOf({ schema, name, nullable, defaultValue, options }) {
1048
+ function convertAllOf({ schema, name, nullable, defaultValue, rawOptions }) {
1117
1049
  if (schema.allOf.length === 1 && !schema.properties && !(Array.isArray(schema.required) && schema.required.length) && schema.additionalProperties === void 0) {
1118
1050
  const [memberSchema] = schema.allOf;
1119
- const memberNode = convertSchema({ schema: memberSchema }, options);
1051
+ const memberNode = parseSchema({
1052
+ schema: memberSchema,
1053
+ name: null
1054
+ }, rawOptions);
1120
1055
  const { kind: _kind, ...memberNodeProps } = memberNode;
1121
1056
  const mergedNullable = nullable || memberNode.nullable || void 0;
1122
1057
  const mergedDefault = schema.default === null && mergedNullable ? void 0 : schema.default ?? memberNode.default;
@@ -1134,81 +1069,123 @@ function createOasParser(oas, { contentType, collisionDetection } = {}) {
1134
1069
  pattern: schema.pattern ?? ("pattern" in memberNode ? memberNode.pattern : void 0)
1135
1070
  });
1136
1071
  }
1072
+ const filteredDiscriminantValues = [];
1137
1073
  const allOfMembers = schema.allOf.filter((item) => {
1138
1074
  if (!isReference(item) || !name) return true;
1139
- const deref = oas.get(item.$ref);
1075
+ const deref = resolveRef(document, item.$ref);
1140
1076
  if (!deref || !isDiscriminator(deref)) return true;
1141
1077
  const parentUnion = deref.oneOf ?? deref.anyOf;
1142
1078
  if (!parentUnion) return true;
1143
1079
  const childRef = `#/components/schemas/${name}`;
1144
1080
  const inOneOf = parentUnion.some((oneOfItem) => isReference(oneOfItem) && oneOfItem.$ref === childRef);
1145
1081
  const inMapping = Object.values(deref.discriminator.mapping ?? {}).some((v) => v === childRef);
1146
- return !inOneOf && !inMapping;
1147
- }).map((s) => convertSchema({ schema: s }, options));
1082
+ if (inOneOf || inMapping) {
1083
+ const discriminatorValue = (0, _kubb_ast.findDiscriminator)(deref.discriminator.mapping, childRef);
1084
+ if (discriminatorValue) filteredDiscriminantValues.push({
1085
+ propertyName: deref.discriminator.propertyName,
1086
+ value: discriminatorValue
1087
+ });
1088
+ return false;
1089
+ }
1090
+ return true;
1091
+ }).map((s) => parseSchema({ schema: s }, rawOptions));
1092
+ const syntheticStart = allOfMembers.length;
1148
1093
  if (Array.isArray(schema.required) && schema.required.length) {
1149
1094
  const outerKeys = schema.properties ? new Set(Object.keys(schema.properties)) : /* @__PURE__ */ new Set();
1150
1095
  const missingRequired = schema.required.filter((key) => !outerKeys.has(key));
1151
1096
  if (missingRequired.length) {
1152
1097
  const resolvedMembers = schema.allOf.flatMap((item) => {
1153
1098
  if (!isReference(item)) return [item];
1154
- const deref = oas.get(item.$ref);
1099
+ const deref = resolveRef(document, item.$ref);
1155
1100
  return deref && !isReference(deref) ? [deref] : [];
1156
1101
  });
1157
1102
  for (const key of missingRequired) for (const resolved of resolvedMembers) if (resolved.properties?.[key]) {
1158
- allOfMembers.push(convertSchema({ schema: {
1103
+ allOfMembers.push(parseSchema({ schema: {
1159
1104
  properties: { [key]: resolved.properties[key] },
1160
1105
  required: [key]
1161
- } }, options));
1106
+ } }, rawOptions));
1162
1107
  break;
1163
1108
  }
1164
1109
  }
1165
1110
  }
1166
1111
  if (schema.properties) {
1167
1112
  const { allOf: _allOf, ...schemaWithoutAllOf } = schema;
1168
- allOfMembers.push(convertSchema({ schema: schemaWithoutAllOf }, options));
1113
+ allOfMembers.push(parseSchema({ schema: schemaWithoutAllOf }, rawOptions));
1169
1114
  }
1115
+ for (const { propertyName, value } of filteredDiscriminantValues) allOfMembers.push((0, _kubb_ast.createDiscriminantNode)({
1116
+ propertyName,
1117
+ value
1118
+ }));
1170
1119
  return (0, _kubb_ast.createSchema)({
1171
1120
  type: "intersection",
1172
- members: allOfMembers,
1173
- ...buildSchemaBase(schema, name, nullable, defaultValue)
1121
+ members: [...(0, _kubb_ast.mergeAdjacentObjects)(allOfMembers.slice(0, syntheticStart)), ...(0, _kubb_ast.mergeAdjacentObjects)(allOfMembers.slice(syntheticStart))],
1122
+ ...buildSchemaNode(schema, name, nullable, defaultValue)
1174
1123
  });
1175
1124
  }
1176
1125
  /**
1177
1126
  * Converts a `oneOf` / `anyOf` schema into a `UnionSchemaNode`.
1178
- *
1179
- * Both keywords are treated identically — their members are concatenated into a single union.
1180
- * When sibling `properties` are present alongside `oneOf`/`anyOf`, each union member is
1181
- * individually intersected with the shared properties node to match the OAS pattern of
1182
- * adding common fields next to a discriminated union.
1183
1127
  */
1184
- function convertUnion({ schema, name, nullable, defaultValue, options }) {
1128
+ function convertUnion({ schema, name, nullable, defaultValue, rawOptions }) {
1129
+ function pickDiscriminatorPropertyNode(node, propertyName) {
1130
+ const discriminatorProperty = (0, _kubb_ast.narrowSchema)(node, "object")?.properties?.find((property) => property.name === propertyName);
1131
+ if (!discriminatorProperty) return null;
1132
+ return (0, _kubb_ast.createSchema)({
1133
+ type: "object",
1134
+ primitive: "object",
1135
+ properties: [discriminatorProperty]
1136
+ });
1137
+ }
1185
1138
  const unionMembers = [...schema.oneOf ?? [], ...schema.anyOf ?? []];
1186
1139
  const unionBase = {
1187
- ...buildSchemaBase(schema, name, nullable, defaultValue),
1140
+ ...buildSchemaNode(schema, name, nullable, defaultValue),
1188
1141
  discriminatorPropertyName: isDiscriminator(schema) ? schema.discriminator.propertyName : void 0
1189
1142
  };
1190
- if (schema.properties) {
1143
+ const discriminator = isDiscriminator(schema) ? schema.discriminator : void 0;
1144
+ const sharedPropertiesNode = schema.properties ? (() => {
1191
1145
  const { oneOf: _oneOf, anyOf: _anyOf, ...schemaWithoutUnion } = schema;
1192
- const propertiesNode = convertSchema({ schema: schemaWithoutUnion }, options);
1193
- return (0, _kubb_ast.createSchema)({
1146
+ return parseSchema({
1147
+ schema: discriminator ? Object.fromEntries(Object.entries(schemaWithoutUnion).filter(([key]) => key !== "discriminator")) : schemaWithoutUnion,
1148
+ name
1149
+ }, rawOptions);
1150
+ })() : void 0;
1151
+ if (sharedPropertiesNode || discriminator?.mapping) {
1152
+ const members = unionMembers.map((s) => {
1153
+ const ref = isReference(s) ? s.$ref : void 0;
1154
+ const discriminatorValue = (0, _kubb_ast.findDiscriminator)(discriminator?.mapping, ref);
1155
+ const memberNode = parseSchema({ schema: s }, rawOptions);
1156
+ if (!discriminatorValue || !discriminator) return memberNode;
1157
+ return (0, _kubb_ast.createSchema)({
1158
+ type: "intersection",
1159
+ members: [memberNode, (sharedPropertiesNode ? pickDiscriminatorPropertyNode((0, _kubb_ast.setDiscriminatorEnum)({
1160
+ node: sharedPropertiesNode,
1161
+ propertyName: discriminator.propertyName,
1162
+ values: [discriminatorValue]
1163
+ }), discriminator.propertyName) : void 0) ?? (0, _kubb_ast.createDiscriminantNode)({
1164
+ propertyName: discriminator.propertyName,
1165
+ value: discriminatorValue
1166
+ })]
1167
+ });
1168
+ });
1169
+ const unionNode = (0, _kubb_ast.createSchema)({
1194
1170
  type: "union",
1195
1171
  ...unionBase,
1196
- members: unionMembers.map((s) => (0, _kubb_ast.createSchema)({
1197
- type: "intersection",
1198
- members: [convertSchema({ schema: s }, options), propertiesNode]
1199
- }))
1172
+ members
1173
+ });
1174
+ if (!sharedPropertiesNode) return unionNode;
1175
+ return (0, _kubb_ast.createSchema)({
1176
+ type: "intersection",
1177
+ ...buildSchemaNode(schema, name, nullable, defaultValue),
1178
+ members: [unionNode, sharedPropertiesNode]
1200
1179
  });
1201
1180
  }
1202
1181
  return (0, _kubb_ast.createSchema)({
1203
1182
  type: "union",
1204
1183
  ...unionBase,
1205
- members: unionMembers.map((s) => convertSchema({ schema: s }, options))
1184
+ members: (0, _kubb_ast.simplifyUnion)(unionMembers.map((s) => parseSchema({ schema: s }, rawOptions)))
1206
1185
  });
1207
1186
  }
1208
1187
  /**
1209
- * Converts an OAS 3.1 `const` schema into either a null scalar or a single-value `EnumSchemaNode`.
1210
- * `const: null` maps to a null scalar; any other value becomes a one-item enum so that generators
1211
- * can produce a precise literal type.
1188
+ * Converts an OAS 3.1 `const` schema into a null scalar or a single-value `EnumSchemaNode`.
1212
1189
  */
1213
1190
  function convertConst({ schema, name, nullable, defaultValue }) {
1214
1191
  const constValue = schema.const;
@@ -1218,25 +1195,23 @@ function createOasParser(oas, { contentType, collisionDetection } = {}) {
1218
1195
  name,
1219
1196
  title: schema.title,
1220
1197
  description: schema.description,
1221
- deprecated: schema.deprecated,
1222
- nullable
1198
+ deprecated: schema.deprecated
1223
1199
  });
1224
1200
  return (0, _kubb_ast.createSchema)({
1225
1201
  type: "enum",
1226
1202
  primitive: getPrimitiveType(typeof constValue === "number" ? "number" : typeof constValue === "boolean" ? "boolean" : "string"),
1227
1203
  enumValues: [constValue],
1228
- ...buildSchemaBase(schema, name, nullable, defaultValue)
1204
+ ...buildSchemaNode(schema, name, nullable, defaultValue)
1229
1205
  });
1230
1206
  }
1231
1207
  /**
1232
- * Handles `format`-based special types (date/time, uuid, email, blob, etc.).
1233
- * Returns `undefined` when the format should fall through to string handling
1234
- * (i.e. `format: 'date-time'` with `dateType: false`).
1208
+ * Converts a format-annotated schema into a special-type `SchemaNode`.
1209
+ * Returns `null` when the format should fall through to string handling (`dateType: false`).
1235
1210
  */
1236
- function convertFormat({ schema, name, nullable, defaultValue, mergedOptions }) {
1237
- const base = buildSchemaBase(schema, name, nullable, defaultValue);
1211
+ function convertFormat({ schema, name, nullable, defaultValue, options }) {
1212
+ const base = buildSchemaNode(schema, name, nullable, defaultValue);
1238
1213
  if (schema.format === "int64") return (0, _kubb_ast.createSchema)({
1239
- type: mergedOptions.integerType === "bigint" ? "bigint" : "integer",
1214
+ type: options.integerType === "bigint" ? "bigint" : "integer",
1240
1215
  primitive: "integer",
1241
1216
  ...base,
1242
1217
  min: schema.minimum,
@@ -1245,8 +1220,8 @@ function createOasParser(oas, { contentType, collisionDetection } = {}) {
1245
1220
  exclusiveMaximum: typeof schema.exclusiveMaximum === "number" ? schema.exclusiveMaximum : void 0
1246
1221
  });
1247
1222
  if (schema.format === "date-time" || schema.format === "date" || schema.format === "time") {
1248
- const dateType = getDateType(mergedOptions, schema.format);
1249
- if (!dateType) return void 0;
1223
+ const dateType = getDateType(options, schema.format);
1224
+ if (!dateType) return null;
1250
1225
  if (dateType.type === "datetime") return (0, _kubb_ast.createSchema)({
1251
1226
  ...base,
1252
1227
  primitive: "string",
@@ -1261,14 +1236,19 @@ function createOasParser(oas, { contentType, collisionDetection } = {}) {
1261
1236
  representation: dateType.representation
1262
1237
  });
1263
1238
  }
1264
- const specialType = formatToSchemaType(schema.format);
1265
- if (!specialType) return void 0;
1239
+ const specialType = getSchemaType(schema.format);
1240
+ if (!specialType) return null;
1266
1241
  const specialPrimitive = specialType === "number" || specialType === "integer" || specialType === "bigint" ? specialType : "string";
1267
1242
  if (specialType === "number" || specialType === "integer" || specialType === "bigint") return (0, _kubb_ast.createSchema)({
1268
1243
  ...base,
1269
1244
  primitive: specialPrimitive,
1270
1245
  type: specialType
1271
1246
  });
1247
+ if (specialType === "url") return (0, _kubb_ast.createSchema)({
1248
+ ...base,
1249
+ primitive: "string",
1250
+ type: "url"
1251
+ });
1272
1252
  return (0, _kubb_ast.createSchema)({
1273
1253
  ...base,
1274
1254
  primitive: specialPrimitive,
@@ -1277,37 +1257,20 @@ function createOasParser(oas, { contentType, collisionDetection } = {}) {
1277
1257
  }
1278
1258
  /**
1279
1259
  * Converts an `enum` schema into an `EnumSchemaNode`.
1280
- *
1281
- * Handles several edge cases:
1282
- * - `{ type: 'array', enum }` (technically invalid OAS) — the enum is normalized into `items`.
1283
- * - `null` in enum values (OAS 3.0 nullable enum convention) — stripped and reflected as `nullable`.
1284
- * - `x-enumNames` / `x-enum-varnames` vendor extensions — produce named enum variants with explicit labels.
1285
- * - Numeric and boolean enums require a const-map representation because most generators cannot
1286
- * use string-enum syntax for non-string values.
1287
1260
  */
1288
- function convertEnum({ schema, name, nullable, type, options }) {
1289
- if (type === "array") {
1290
- const rawSchema = schema;
1291
- const normalizedItems = {
1292
- ...typeof rawSchema.items === "object" && !Array.isArray(rawSchema.items) ? rawSchema.items : {},
1293
- enum: schema.enum
1294
- };
1295
- const { enum: _enum, ...schemaWithoutEnum } = schema;
1296
- return convertSchema({
1297
- schema: {
1298
- ...schemaWithoutEnum,
1299
- items: normalizedItems
1300
- },
1301
- name
1302
- }, options);
1303
- }
1261
+ function convertEnum({ schema, name, nullable, type, rawOptions }) {
1262
+ if (type === "array") return parseSchema({
1263
+ schema: normalizeArrayEnum(schema),
1264
+ name
1265
+ }, rawOptions);
1304
1266
  const nullInEnum = schema.enum.includes(null);
1305
1267
  const filteredValues = nullInEnum ? schema.enum.filter((v) => v !== null) : schema.enum;
1306
1268
  const enumNullable = nullable || nullInEnum || void 0;
1307
1269
  const enumDefault = schema.default === null && enumNullable ? void 0 : schema.default;
1270
+ const enumPrimitive = getPrimitiveType(type);
1308
1271
  const enumBase = {
1309
1272
  type: "enum",
1310
- primitive: getPrimitiveType(type),
1273
+ primitive: enumPrimitive,
1311
1274
  name,
1312
1275
  title: schema.title,
1313
1276
  description: schema.description,
@@ -1319,138 +1282,115 @@ function createOasParser(oas, { contentType, collisionDetection } = {}) {
1319
1282
  example: schema.example
1320
1283
  };
1321
1284
  const extensionKey = enumExtensionKeys.find((key) => key in schema);
1322
- if (extensionKey) {
1323
- const rawNames = schema[extensionKey];
1324
- const uniqueNames = [...new Set(rawNames)];
1325
- const enumType = getPrimitiveType(type) === "number" || getPrimitiveType(type) === "integer" ? "number" : getPrimitiveType(type) === "boolean" ? "boolean" : "string";
1285
+ if (extensionKey || enumPrimitive === "number" || enumPrimitive === "integer" || enumPrimitive === "boolean") {
1286
+ const enumPrimitiveType = enumPrimitive === "number" || enumPrimitive === "integer" ? "number" : enumPrimitive === "boolean" ? "boolean" : "string";
1287
+ const sourceValues = extensionKey ? [...new Set(schema[extensionKey])] : [...new Set(filteredValues)];
1326
1288
  return (0, _kubb_ast.createSchema)({
1327
1289
  ...enumBase,
1328
- enumType,
1329
- namedEnumValues: uniqueNames.map((label, index) => ({
1290
+ primitive: enumPrimitiveType,
1291
+ namedEnumValues: sourceValues.map((label, index) => ({
1330
1292
  name: String(label),
1331
- value: filteredValues[index] ?? label,
1332
- format: enumType
1293
+ value: extensionKey ? filteredValues[index] ?? label : label,
1294
+ primitive: enumPrimitiveType
1333
1295
  }))
1334
1296
  });
1335
1297
  }
1336
- if (type === "number" || type === "integer") return (0, _kubb_ast.createSchema)({
1337
- ...enumBase,
1338
- enumType: "number",
1339
- namedEnumValues: [...new Set(filteredValues)].map((value) => ({
1340
- name: String(value),
1341
- value,
1342
- format: "number"
1343
- }))
1344
- });
1345
- if (type === "boolean") return (0, _kubb_ast.createSchema)({
1346
- ...enumBase,
1347
- enumType: "boolean",
1348
- namedEnumValues: [...new Set(filteredValues)].map((value) => ({
1349
- name: String(value),
1350
- value,
1351
- format: "boolean"
1352
- }))
1353
- });
1354
1298
  return (0, _kubb_ast.createSchema)({
1355
1299
  ...enumBase,
1356
1300
  enumValues: [...new Set(filteredValues)]
1357
1301
  });
1358
1302
  }
1359
1303
  /**
1360
- * Converts an object-like schema (`type: 'object'`, `properties`, `additionalProperties`,
1361
- * or `patternProperties`) into an `ObjectSchemaNode`.
1362
- *
1363
- * When a `discriminator` is present, the discriminator property's schema is replaced with an
1364
- * enum of the mapping keys so generators can produce a precise literal-union type for it.
1365
- *
1366
- * Property optionality follows OAS semantics:
1367
- * - required + not nullable → `required: true`
1368
- * - not required + not nullable → `optional: true`
1369
- * - not required + nullable → `nullish: true`
1304
+ * Converts an object-like schema into an `ObjectSchemaNode`.
1370
1305
  */
1371
- function convertObject({ schema, name, nullable, defaultValue, options, mergedOptions }) {
1372
- const resolvedSchema = applyDiscriminatorEnum(schema);
1373
- const properties = resolvedSchema.properties ? Object.entries(resolvedSchema.properties).map(([propName, propSchema]) => {
1374
- const required = Array.isArray(resolvedSchema.required) ? resolvedSchema.required.includes(propName) : !!resolvedSchema.required;
1306
+ function convertObject({ schema, name, nullable, defaultValue, rawOptions, options }) {
1307
+ const properties = schema.properties ? Object.entries(schema.properties).map(([propName, propSchema]) => {
1308
+ const required = Array.isArray(schema.required) ? schema.required.includes(propName) : !!schema.required;
1375
1309
  const resolvedPropSchema = propSchema;
1376
1310
  const propNullable = isNullable(resolvedPropSchema);
1311
+ let schemaNode = (0, _kubb_ast.setEnumName)(parseSchema({
1312
+ schema: resolvedPropSchema,
1313
+ name: (0, _kubb_ast.childName)(name, propName)
1314
+ }, rawOptions), name, propName, options.enumSuffix);
1315
+ const tupleNode = (0, _kubb_ast.narrowSchema)(schemaNode, "tuple");
1316
+ if (tupleNode?.items) {
1317
+ const namedItems = tupleNode.items.map((item) => (0, _kubb_ast.setEnumName)(item, name, propName, options.enumSuffix));
1318
+ if (namedItems.some((item, i) => item !== tupleNode.items[i])) schemaNode = {
1319
+ ...tupleNode,
1320
+ items: namedItems
1321
+ };
1322
+ }
1377
1323
  return (0, _kubb_ast.createProperty)({
1378
1324
  name: propName,
1379
1325
  schema: {
1380
- ...convertSchema({
1381
- schema: resolvedPropSchema,
1382
- name: name ? pascalCase([
1383
- name,
1384
- propName,
1385
- mergedOptions.enumSuffix
1386
- ].filter(Boolean).join(" ")) : void 0
1387
- }, options),
1388
- nullable: propNullable || void 0,
1389
- optional: !required && !propNullable ? true : void 0,
1390
- nullish: !required && propNullable ? true : void 0
1326
+ ...schemaNode,
1327
+ nullable: schemaNode.type === "null" ? void 0 : propNullable || void 0
1391
1328
  },
1392
1329
  required
1393
1330
  });
1394
1331
  }) : [];
1395
- const additionalProperties = resolvedSchema.additionalProperties;
1332
+ const additionalProperties = schema.additionalProperties;
1396
1333
  let additionalPropertiesNode;
1397
1334
  if (additionalProperties === true) additionalPropertiesNode = true;
1398
- else if (additionalProperties && Object.keys(additionalProperties).length > 0) additionalPropertiesNode = convertSchema({ schema: additionalProperties }, options);
1335
+ else if (additionalProperties && Object.keys(additionalProperties).length > 0) additionalPropertiesNode = parseSchema({ schema: additionalProperties }, rawOptions);
1399
1336
  else if (additionalProperties === false) additionalPropertiesNode = void 0;
1400
- else if (additionalProperties) additionalPropertiesNode = (0, _kubb_ast.createSchema)({ type: resolveTypeOption(mergedOptions.unknownType) });
1401
- const rawPatternProperties = "patternProperties" in resolvedSchema ? resolvedSchema.patternProperties : void 0;
1402
- const patternProperties = rawPatternProperties ? Object.fromEntries(Object.entries(rawPatternProperties).map(([pattern, patternSchema]) => [pattern, patternSchema === true || Object.keys(patternSchema).length === 0 ? (0, _kubb_ast.createSchema)({ type: resolveTypeOption(mergedOptions.unknownType) }) : convertSchema({ schema: patternSchema }, options)])) : void 0;
1403
- return (0, _kubb_ast.createSchema)({
1337
+ else if (additionalProperties) additionalPropertiesNode = (0, _kubb_ast.createSchema)({ type: typeOptionMap.get(options.unknownType) });
1338
+ const rawPatternProperties = "patternProperties" in schema ? schema.patternProperties : void 0;
1339
+ const patternProperties = rawPatternProperties ? Object.fromEntries(Object.entries(rawPatternProperties).map(([pattern, patternSchema]) => [pattern, patternSchema === true || typeof patternSchema === "object" && Object.keys(patternSchema).length === 0 ? (0, _kubb_ast.createSchema)({ type: typeOptionMap.get(options.unknownType) }) : parseSchema({ schema: patternSchema }, rawOptions)])) : void 0;
1340
+ const objectNode = (0, _kubb_ast.createSchema)({
1404
1341
  type: "object",
1405
1342
  primitive: "object",
1406
1343
  properties,
1407
1344
  additionalProperties: additionalPropertiesNode,
1408
1345
  patternProperties,
1409
- ...buildSchemaBase(schema, name, nullable, defaultValue)
1346
+ ...buildSchemaNode(schema, name, nullable, defaultValue)
1410
1347
  });
1348
+ if (isDiscriminator(schema) && schema.discriminator.mapping) {
1349
+ const discPropName = schema.discriminator.propertyName;
1350
+ return (0, _kubb_ast.setDiscriminatorEnum)({
1351
+ node: objectNode,
1352
+ propertyName: discPropName,
1353
+ values: Object.keys(schema.discriminator.mapping),
1354
+ enumName: name ? (0, _kubb_ast.enumPropName)(name, discPropName, options.enumSuffix) : void 0
1355
+ });
1356
+ }
1357
+ return objectNode;
1411
1358
  }
1412
1359
  /**
1413
1360
  * Converts an OAS 3.1 `prefixItems` tuple into a `TupleSchemaNode`.
1414
- *
1415
- * Each `prefixItems` element maps to a positional tuple slot. An optional `items` schema
1416
- * after the prefix items is mapped to the rest parameter of the tuple.
1417
1361
  */
1418
- function convertTuple({ schema, name, nullable, defaultValue, options }) {
1419
- const rawSchema = schema;
1362
+ function convertTuple({ schema, name, nullable, defaultValue, rawOptions }) {
1420
1363
  return (0, _kubb_ast.createSchema)({
1421
1364
  type: "tuple",
1422
1365
  primitive: "array",
1423
- items: rawSchema.prefixItems.map((item) => convertSchema({ schema: item }, options)),
1424
- rest: rawSchema.items ? convertSchema({ schema: rawSchema.items }, options) : void 0,
1366
+ items: (schema.prefixItems ?? []).map((item) => parseSchema({ schema: item }, rawOptions)),
1367
+ rest: schema.items ? parseSchema({ schema: schema.items }, rawOptions) : (0, _kubb_ast.createSchema)({ type: "any" }),
1425
1368
  min: schema.minItems,
1426
1369
  max: schema.maxItems,
1427
- ...buildSchemaBase(schema, name, nullable, defaultValue)
1370
+ ...buildSchemaNode(schema, name, nullable, defaultValue)
1428
1371
  });
1429
1372
  }
1430
1373
  /**
1431
1374
  * Converts a `type: 'array'` schema into an `ArraySchemaNode`.
1432
- *
1433
- * When the items schema is an inline enum, a name derived from the parent array's name and
1434
- * `enumSuffix` is forwarded so generators can emit a named enum declaration.
1435
1375
  */
1436
- function convertArray({ schema, name, nullable, defaultValue, options, mergedOptions }) {
1437
- const rawSchema = schema;
1438
- const itemName = rawSchema.items?.enum?.length && name ? pascalCase([name, mergedOptions.enumSuffix].join(" ")) : void 0;
1376
+ function convertArray({ schema, name, nullable, defaultValue, rawOptions, options }) {
1377
+ const rawItems = schema.items;
1378
+ const itemName = rawItems?.enum?.length && name ? (0, _kubb_ast.enumPropName)(void 0, name, options.enumSuffix) : void 0;
1439
1379
  return (0, _kubb_ast.createSchema)({
1440
1380
  type: "array",
1441
1381
  primitive: "array",
1442
- items: rawSchema.items ? [convertSchema({
1443
- schema: rawSchema.items,
1382
+ items: rawItems ? [parseSchema({
1383
+ schema: rawItems,
1444
1384
  name: itemName
1445
- }, options)] : [],
1385
+ }, rawOptions)] : [],
1446
1386
  min: schema.minItems,
1447
1387
  max: schema.maxItems,
1448
1388
  unique: schema.uniqueItems ?? void 0,
1449
- ...buildSchemaBase(schema, name, nullable, defaultValue)
1389
+ ...buildSchemaNode(schema, name, nullable, defaultValue)
1450
1390
  });
1451
1391
  }
1452
1392
  /**
1453
- * Converts a `type: 'string'` schema (without a special format) into a `StringSchemaNode`.
1393
+ * Converts a `type: 'string'` schema into a `StringSchemaNode`.
1454
1394
  */
1455
1395
  function convertString({ schema, name, nullable, defaultValue }) {
1456
1396
  return (0, _kubb_ast.createSchema)({
@@ -1459,11 +1399,11 @@ function createOasParser(oas, { contentType, collisionDetection } = {}) {
1459
1399
  min: schema.minLength,
1460
1400
  max: schema.maxLength,
1461
1401
  pattern: schema.pattern,
1462
- ...buildSchemaBase(schema, name, nullable, defaultValue)
1402
+ ...buildSchemaNode(schema, name, nullable, defaultValue)
1463
1403
  });
1464
1404
  }
1465
1405
  /**
1466
- * Converts a `type: 'number'` or `type: 'integer'` schema into the corresponding `SchemaNode`.
1406
+ * Converts a `type: 'number'` or `type: 'integer'` schema.
1467
1407
  */
1468
1408
  function convertNumeric({ schema, name, nullable, defaultValue }, type) {
1469
1409
  return (0, _kubb_ast.createSchema)({
@@ -1473,21 +1413,21 @@ function createOasParser(oas, { contentType, collisionDetection } = {}) {
1473
1413
  max: schema.maximum,
1474
1414
  exclusiveMinimum: typeof schema.exclusiveMinimum === "number" ? schema.exclusiveMinimum : void 0,
1475
1415
  exclusiveMaximum: typeof schema.exclusiveMaximum === "number" ? schema.exclusiveMaximum : void 0,
1476
- ...buildSchemaBase(schema, name, nullable, defaultValue)
1416
+ ...buildSchemaNode(schema, name, nullable, defaultValue)
1477
1417
  });
1478
1418
  }
1479
1419
  /**
1480
- * Converts a `type: 'boolean'` schema into a `BooleanSchemaNode`.
1420
+ * Converts a `type: 'boolean'` schema.
1481
1421
  */
1482
1422
  function convertBoolean({ schema, name, nullable, defaultValue }) {
1483
1423
  return (0, _kubb_ast.createSchema)({
1484
1424
  type: "boolean",
1485
1425
  primitive: "boolean",
1486
- ...buildSchemaBase(schema, name, nullable, defaultValue)
1426
+ ...buildSchemaNode(schema, name, nullable, defaultValue)
1487
1427
  });
1488
1428
  }
1489
1429
  /**
1490
- * Converts an explicit `type: 'null'` or `const: null` schema into a `NullSchemaNode`.
1430
+ * Converts an explicit `type: 'null'` schema.
1491
1431
  */
1492
1432
  function convertNull({ schema, name, nullable }) {
1493
1433
  return (0, _kubb_ast.createSchema)({
@@ -1501,31 +1441,22 @@ function createOasParser(oas, { contentType, collisionDetection } = {}) {
1501
1441
  });
1502
1442
  }
1503
1443
  /**
1504
- * Central dispatcher: converts an OAS `SchemaObject` into a `SchemaNode`.
1444
+ * Central dispatcher that converts an OAS `SchemaObject` into a `SchemaNode`.
1505
1445
  *
1506
- * Dispatch order (first match wins):
1507
- * 1. `$ref` pointer
1508
- * 2. `allOf` composition
1509
- * 3. `oneOf` / `anyOf` union
1510
- * 4. `const` literal (OAS 3.1)
1511
- * 5. `format`-based special type (date/time, uuid, blob, …)
1512
- * 6. OAS 3.1 `contentMediaType: 'application/octet-stream'` blob
1513
- * 7. OAS 3.1 multi-type array → union or fallthrough
1514
- * 8. Constraint-inferred type (minLength/maxLength → string; minimum/maximum → number)
1515
- * 9. `enum` values
1516
- * 10. Object / array / tuple / scalar by `type`
1517
- * 11. Empty schema fallback (`emptySchemaType` option)
1446
+ * Dispatch order (first match wins): `$ref` → `allOf` → `oneOf`/`anyOf` → `const` → `format`
1447
+ * octet-stream blob → multi-type array → constraint-inferred type → `enum` → object/array/tuple/scalar
1448
+ * empty-schema fallback (`emptySchemaType` option).
1518
1449
  */
1519
- function convertSchema({ schema, name }, options) {
1520
- const mergedOptions = {
1521
- ...DEFAULT_OPTIONS,
1522
- ...options
1450
+ function parseSchema({ schema, name }, rawOptions) {
1451
+ const options = {
1452
+ ...DEFAULT_PARSER_OPTIONS,
1453
+ ...rawOptions
1523
1454
  };
1524
1455
  const flattenedSchema = flattenSchema(schema);
1525
- if (flattenedSchema && flattenedSchema !== schema) return convertSchema({
1456
+ if (flattenedSchema && flattenedSchema !== schema) return parseSchema({
1526
1457
  schema: flattenedSchema,
1527
1458
  name
1528
- }, options);
1459
+ }, rawOptions);
1529
1460
  const nullable = isNullable(schema) || void 0;
1530
1461
  const defaultValue = schema.default === null && nullable ? void 0 : schema.default;
1531
1462
  const type = Array.isArray(schema.type) ? schema.type[0] : schema.type;
@@ -1535,8 +1466,8 @@ function createOasParser(oas, { contentType, collisionDetection } = {}) {
1535
1466
  nullable,
1536
1467
  defaultValue,
1537
1468
  type,
1538
- options,
1539
- mergedOptions
1469
+ rawOptions,
1470
+ options
1540
1471
  };
1541
1472
  if (isReference(schema)) return convertRef(ctx);
1542
1473
  if (schema.allOf?.length) return convertAllOf(ctx);
@@ -1549,18 +1480,21 @@ function createOasParser(oas, { contentType, collisionDetection } = {}) {
1549
1480
  if (schema.type === "string" && schema.contentMediaType === "application/octet-stream") return (0, _kubb_ast.createSchema)({
1550
1481
  type: "blob",
1551
1482
  primitive: "string",
1552
- ...buildSchemaBase(schema, name, nullable, defaultValue)
1483
+ ...buildSchemaNode(schema, name, nullable, defaultValue)
1553
1484
  });
1554
1485
  if (Array.isArray(schema.type) && schema.type.length > 1) {
1555
1486
  const nonNullTypes = schema.type.filter((t) => t !== "null");
1556
1487
  const arrayNullable = schema.type.includes("null") || nullable || void 0;
1557
1488
  if (nonNullTypes.length > 1) return (0, _kubb_ast.createSchema)({
1558
1489
  type: "union",
1559
- members: nonNullTypes.map((t) => convertSchema({ schema: {
1560
- ...schema,
1561
- type: t
1562
- } }, options)),
1563
- ...buildSchemaBase(schema, name, arrayNullable, defaultValue)
1490
+ members: nonNullTypes.map((t) => parseSchema({
1491
+ schema: {
1492
+ ...schema,
1493
+ type: t
1494
+ },
1495
+ name
1496
+ }, rawOptions)),
1497
+ ...buildSchemaNode(schema, name, arrayNullable, defaultValue)
1564
1498
  });
1565
1499
  }
1566
1500
  if (!type) {
@@ -1577,52 +1511,62 @@ function createOasParser(oas, { contentType, collisionDetection } = {}) {
1577
1511
  if (type === "boolean") return convertBoolean(ctx);
1578
1512
  if (type === "null") return convertNull(ctx);
1579
1513
  return (0, _kubb_ast.createSchema)({
1580
- type: resolveTypeOption(mergedOptions.emptySchemaType),
1514
+ type: typeOptionMap.get(options.emptySchemaType),
1581
1515
  name,
1582
1516
  title: schema.title,
1583
1517
  description: schema.description
1584
1518
  });
1585
1519
  }
1586
1520
  /**
1587
- * Converts a single dereferenced OAS parameter object into a `ParameterNode`.
1588
- * When the parameter has no `schema` or its schema is a `$ref`, falls back to `unknownType`.
1521
+ * Converts a dereferenced OAS parameter object into a `ParameterNode`.
1589
1522
  */
1590
1523
  function parseParameter(options, param) {
1591
- const schema = param["schema"] && !isReference(param["schema"]) ? convertSchema({ schema: param["schema"] }, options) : (0, _kubb_ast.createSchema)({ type: resolveTypeOption(options.unknownType) });
1524
+ const required = param["required"] ?? false;
1525
+ const schema = param["schema"] ? parseSchema({ schema: param["schema"] }, options) : (0, _kubb_ast.createSchema)({ type: typeOptionMap.get(options.unknownType) });
1592
1526
  return (0, _kubb_ast.createParameter)({
1593
1527
  name: param["name"],
1594
1528
  in: param["in"],
1595
- schema,
1596
- required: param["required"] ?? false
1529
+ schema: {
1530
+ ...schema,
1531
+ description: param["description"] ?? schema.description
1532
+ },
1533
+ required
1597
1534
  });
1598
1535
  }
1599
1536
  /**
1600
- * Converts an OAS `Operation` into an `OperationNode`, resolving parameters,
1601
- * request body, and all response codes into their AST node equivalents.
1537
+ * Converts an OAS `Operation` into an `OperationNode`.
1602
1538
  */
1603
- function parseOperation(options, oas, operation) {
1604
- const parameters = operation.getParameters().map((param) => {
1605
- return parseParameter(options, oas.dereferenceWithRef(param));
1606
- });
1607
- const requestBodySchema = oas.getRequestSchema(operation);
1608
- const requestBody = requestBodySchema ? convertSchema({ schema: requestBodySchema }, options) : void 0;
1539
+ function parseOperation(options, operation) {
1540
+ const parameters = getParameters(document, operation).map((param) => parseParameter(options, param));
1541
+ const requestBodySchema = getRequestSchema(document, operation, { contentType: ctx.contentType });
1542
+ const requestBodySchemaNode = requestBodySchema ? parseSchema({ schema: requestBodySchema }, options) : void 0;
1543
+ const requestBodyDescription = operation.schema.requestBody && !isReference(operation.schema.requestBody) ? operation.schema.requestBody.description : void 0;
1544
+ const requestBodyKeysToOmit = requestBodySchema?.properties ? Object.entries(requestBodySchema.properties).filter(([, prop]) => !isReference(prop) && prop.readOnly).map(([key]) => key) : void 0;
1545
+ const requestBody = requestBodySchemaNode ? {
1546
+ description: requestBodyDescription,
1547
+ schema: requestBodySchemaNode,
1548
+ keysToOmit: requestBodyKeysToOmit?.length ? requestBodyKeysToOmit : void 0
1549
+ } : void 0;
1609
1550
  const responses = operation.getResponseStatusCodes().map((statusCode) => {
1610
1551
  const responseObj = operation.getResponseByStatusCode(statusCode);
1611
- const responseSchema = oas.getResponseSchema(operation, statusCode);
1612
- const schema = responseSchema && Object.keys(responseSchema).length > 0 ? convertSchema({ schema: responseSchema }, options) : void 0;
1552
+ const responseSchema = getResponseSchema(document, operation, statusCode, { contentType: ctx.contentType });
1553
+ const schema = responseSchema && Object.keys(responseSchema).length > 0 ? parseSchema({ schema: responseSchema }, options) : (0, _kubb_ast.createSchema)({ type: typeOptionMap.get(options.emptySchemaType) });
1613
1554
  const description = typeof responseObj === "object" && responseObj !== null && !Array.isArray(responseObj) ? responseObj.description : void 0;
1614
1555
  const rawContent = typeof responseObj === "object" && responseObj !== null && !Array.isArray(responseObj) ? responseObj.content : void 0;
1556
+ const mediaType = rawContent ? getMediaType(Object.keys(rawContent)[0] ?? "") : getMediaType(operation.contentType ?? "");
1557
+ const keysToOmit = responseSchema?.properties ? Object.entries(responseSchema.properties).filter(([, prop]) => !isReference(prop) && prop.writeOnly).map(([key]) => key) : void 0;
1615
1558
  return (0, _kubb_ast.createResponse)({
1616
1559
  statusCode,
1617
1560
  description,
1618
1561
  schema,
1619
- mediaType: rawContent ? toMediaType(Object.keys(rawContent)[0] ?? "") : toMediaType(operation.contentType ?? "")
1562
+ mediaType,
1563
+ keysToOmit: keysToOmit?.length ? keysToOmit : void 0
1620
1564
  });
1621
1565
  });
1622
1566
  return (0, _kubb_ast.createOperation)({
1623
1567
  operationId: operation.getOperationId(),
1624
1568
  method: operation.method.toUpperCase(),
1625
- path: operation.path,
1569
+ path: new URLPath(operation.path).URL,
1626
1570
  tags: operation.getTags().map((tag) => tag.name),
1627
1571
  summary: operation.getSummary() || void 0,
1628
1572
  description: operation.getDescription() || void 0,
@@ -1632,165 +1576,145 @@ function createOasParser(oas, { contentType, collisionDetection } = {}) {
1632
1576
  responses
1633
1577
  });
1634
1578
  }
1635
- /**
1636
- * Converts an OpenAPI/Swagger spec (wrapped in a Kubb `Oas` instance) into
1637
- * a `RootNode` — the top-level node of the `@kubb/ast` tree.
1638
- */
1639
- function parse(options) {
1640
- const mergedOptions = {
1641
- ...DEFAULT_OPTIONS,
1642
- ...options
1643
- };
1644
- const schemas = Object.entries(schemaObjects).map(([name, schemaObject]) => convertSchema({
1645
- schema: schemaObject,
1646
- name
1647
- }, mergedOptions));
1648
- const paths = oas.getPaths();
1649
- return (0, _kubb_ast.createRoot)({
1650
- schemas,
1651
- operations: Object.entries(paths).flatMap(([_path, methods]) => Object.entries(methods).map(([, operation]) => operation ? parseOperation(mergedOptions, oas, operation) : null).filter((op) => op !== null))
1652
- });
1653
- }
1654
- /**
1655
- * Walks a `SchemaNode` tree and resolves all `ref` node names through the provided callbacks.
1656
- *
1657
- * `resolveName` handles all schema types; `resolveEnumName` (when provided) takes precedence
1658
- * for `enum` nodes, enabling a separate naming strategy for enums (e.g. different suffix).
1659
- *
1660
- * Collision-resolved names (from `nameMapping`) are applied before user-supplied resolvers.
1661
- */
1662
- function resolveRefs(node, resolveName, resolveEnumName) {
1663
- return (0, _kubb_ast.transform)(node, { schema(schemaNode) {
1664
- const schemaRef = (0, _kubb_ast.narrowSchema)(schemaNode, _kubb_ast.schemaTypes.ref);
1665
- if (schemaRef && (schemaRef.ref || schemaRef.name)) {
1666
- const rawRef = schemaRef.ref ?? schemaRef.name;
1667
- const resolved = resolveName(nameMapping.get(rawRef) ?? rawRef);
1668
- if (resolved) return {
1669
- ...schemaNode,
1670
- name: resolved
1671
- };
1672
- }
1673
- if (schemaNode.type === "enum" && schemaNode.name) {
1674
- const resolved = (resolveEnumName ?? resolveName)(schemaNode.name);
1675
- if (resolved) return {
1676
- ...schemaNode,
1677
- name: resolved
1678
- };
1679
- }
1680
- } });
1681
- }
1682
- /**
1683
- * Collects all `KubbFile.Import` descriptors needed by a `SchemaNode` tree.
1684
- *
1685
- * Walks the tree looking for `ref` nodes, verifies each `$ref` is resolvable in the spec,
1686
- * applies collision-resolved names from `nameMapping`, and calls `resolve` to obtain the
1687
- * import path and name. Returns an empty array for refs that cannot be resolved.
1688
- */
1689
- function getImports(node, resolve) {
1690
- return (0, _kubb_ast.collect)(node, { schema(schemaNode) {
1691
- if (schemaNode.type !== "ref" || !schemaNode.ref) return;
1692
- if (!oas.get(schemaNode.ref)) return;
1693
- const rawName = extractRefName(schemaNode.ref);
1694
- const result = resolve(nameMapping.get(rawName) ?? rawName);
1695
- if (!result) return;
1696
- return {
1697
- name: [result.name],
1698
- path: result.path
1699
- };
1700
- } });
1701
- }
1702
1579
  return {
1703
- parse,
1704
- convertSchema,
1705
- resolveRefs,
1706
- getImports
1580
+ parseSchema,
1581
+ parseOperation,
1582
+ parseParameter
1583
+ };
1584
+ }
1585
+ /**
1586
+ * Converts the entire OpenAPI spec into a `RootNode` (the top-level `@kubb/ast` tree).
1587
+ *
1588
+ * This is the main entry point: `OpenAPI / Swagger → Kubb AST`.
1589
+ * No code is generated here — the resulting tree is spec-agnostic and consumed by
1590
+ * downstream plugins (`plugin-ts`, `plugin-zod`, …).
1591
+ *
1592
+ * @example
1593
+ * ```ts
1594
+ * const document = await parseFromConfig(config)
1595
+ * const root = parseOas(document, { dateType: 'date', contentType: 'application/json' })
1596
+ * ```
1597
+ */
1598
+ function parseOas(document, options = {}) {
1599
+ const { contentType, ...parserOptions } = options;
1600
+ const mergedOptions = {
1601
+ ...DEFAULT_PARSER_OPTIONS,
1602
+ ...parserOptions
1603
+ };
1604
+ const { schemas: schemaObjects, nameMapping } = getSchemas(document, { contentType });
1605
+ const { parseSchema: _parseSchema, parseOperation: _parseOperation } = createSchemaParser({
1606
+ document,
1607
+ contentType
1608
+ });
1609
+ const schemas = Object.entries(schemaObjects).map(([name, schema]) => _parseSchema({
1610
+ schema,
1611
+ name
1612
+ }, mergedOptions));
1613
+ const paths = new oas.default(document).getPaths();
1614
+ return {
1615
+ root: (0, _kubb_ast.createRoot)({
1616
+ schemas,
1617
+ operations: Object.entries(paths).flatMap(([_path, methods]) => Object.entries(methods).map(([, operation]) => operation ? _parseOperation(mergedOptions, operation) : null).filter((op) => op !== null))
1618
+ }),
1619
+ nameMapping
1707
1620
  };
1708
1621
  }
1709
1622
  //#endregion
1710
1623
  //#region src/adapter.ts
1624
+ /**
1625
+ * Stable string identifier for the OAS adapter used in Kubb's adapter registry.
1626
+ */
1711
1627
  const adapterOasName = "oas";
1712
1628
  /**
1713
- * Creates an OpenAPI / Swagger adapter for Kubb.
1629
+ * Creates the default OpenAPI / Swagger adapter for Kubb.
1714
1630
  *
1715
- * This is the default adapter you can omit it from your config when using
1716
- * an OpenAPI spec, but supplying it explicitly lets you pass options.
1631
+ * Parses the spec, optionally validates it, resolves the base URL, and converts
1632
+ * everything into a `RootNode` that downstream plugins consume.
1717
1633
  *
1718
1634
  * @example
1719
1635
  * ```ts
1720
1636
  * import { defineConfig } from '@kubb/core'
1721
1637
  * import { adapterOas } from '@kubb/adapter-oas'
1638
+ * import { pluginTs } from '@kubb/plugin-ts'
1722
1639
  *
1723
1640
  * export default defineConfig({
1724
- * adapter: adapterOas({ validate: true, dateType: 'date' }),
1725
- * input: { path: './openapi.yaml' },
1726
- * plugins: [pluginTs(), pluginZod()],
1641
+ * adapter: adapterOas({ dateType: 'date', serverIndex: 0 }),
1642
+ * input: { path: './openapi.yaml' },
1643
+ * plugins: [pluginTs()],
1727
1644
  * })
1728
1645
  * ```
1729
1646
  */
1730
- const adapterOas = (0, _kubb_core.defineAdapter)((options) => {
1731
- const { validate = true, oasClass, contentType, serverIndex, serverVariables, discriminator = "strict", collisionDetection = false, dateType = "string", integerType = "number", unknownType = "any", emptySchemaType = unknownType } = options;
1647
+ const adapterOas = (0, _kubb_core.createAdapter)((options) => {
1648
+ 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;
1649
+ let nameMapping = /* @__PURE__ */ new Map();
1650
+ let parsedDocument;
1651
+ let rootNode;
1732
1652
  return {
1733
1653
  name: "oas",
1734
- options: {
1735
- validate,
1736
- oasClass,
1737
- contentType,
1738
- serverIndex,
1739
- serverVariables,
1740
- discriminator,
1741
- collisionDetection,
1742
- dateType,
1743
- integerType,
1744
- unknownType,
1745
- emptySchemaType
1746
- },
1747
- async parse(source) {
1748
- const oas = await parseFromConfig(sourceToFakeConfig(source), oasClass);
1749
- oas.setOptions({
1654
+ get options() {
1655
+ return {
1656
+ validate,
1750
1657
  contentType,
1658
+ serverIndex,
1659
+ serverVariables,
1751
1660
  discriminator,
1752
- collisionDetection
1661
+ dateType,
1662
+ integerType,
1663
+ unknownType,
1664
+ emptySchemaType,
1665
+ enumSuffix,
1666
+ nameMapping
1667
+ };
1668
+ },
1669
+ get document() {
1670
+ return parsedDocument;
1671
+ },
1672
+ get rootNode() {
1673
+ return rootNode;
1674
+ },
1675
+ getImports(node, resolve) {
1676
+ return (0, _kubb_ast.collectImports)({
1677
+ node,
1678
+ nameMapping,
1679
+ resolve: (schemaName) => {
1680
+ const result = resolve(schemaName);
1681
+ if (!result) return;
1682
+ return {
1683
+ name: [result.name],
1684
+ path: result.path
1685
+ };
1686
+ }
1753
1687
  });
1754
- if (validate) try {
1755
- await oas.validate();
1756
- } catch (_err) {}
1757
- const server = serverIndex !== void 0 ? oas.api.servers?.at(serverIndex) : void 0;
1688
+ },
1689
+ async parse(source) {
1690
+ const document = await parseFromConfig(source);
1691
+ if (validate) await validateDocument(document);
1692
+ const server = serverIndex !== void 0 ? document.servers?.at(serverIndex) : void 0;
1758
1693
  const baseURL = server?.url ? resolveServerUrl(server, serverVariables) : void 0;
1759
- return (0, _kubb_ast.createRoot)({
1760
- ...createOasParser(oas, {
1761
- contentType,
1762
- collisionDetection
1763
- }).parse({
1764
- dateType,
1765
- integerType,
1766
- unknownType,
1767
- emptySchemaType
1768
- }),
1694
+ const { root: parsedRoot, nameMapping: parsedNameMapping } = parseOas(document, {
1695
+ contentType,
1696
+ dateType,
1697
+ integerType,
1698
+ unknownType,
1699
+ emptySchemaType,
1700
+ enumSuffix
1701
+ });
1702
+ const node = discriminator === "inherit" ? applyDiscriminatorInheritance(parsedRoot) : parsedRoot;
1703
+ nameMapping = parsedNameMapping;
1704
+ parsedDocument = document;
1705
+ rootNode = (0, _kubb_ast.createRoot)({
1706
+ ...node,
1769
1707
  meta: {
1770
- title: oas.api.info?.title,
1771
- version: oas.api.info?.version,
1708
+ title: document.info?.title,
1709
+ description: document.info?.description,
1710
+ version: document.info?.version,
1772
1711
  baseURL
1773
1712
  }
1774
1713
  });
1714
+ return rootNode;
1775
1715
  }
1776
1716
  };
1777
1717
  });
1778
- function sourceToFakeConfig(source) {
1779
- switch (source.type) {
1780
- case "path": return {
1781
- root: node_path.default.dirname(source.path),
1782
- input: { path: source.path }
1783
- };
1784
- case "data": return {
1785
- root: process.cwd(),
1786
- input: { data: source.data }
1787
- };
1788
- case "paths": return {
1789
- root: source.paths[0] ? node_path.default.dirname(source.paths[0]) : process.cwd(),
1790
- input: source.paths.map((p) => ({ path: p }))
1791
- };
1792
- }
1793
- }
1794
1718
  //#endregion
1795
1719
  exports.adapterOas = adapterOas;
1796
1720
  exports.adapterOasName = adapterOasName;