@kubb/adapter-oas 5.5.6 → 5.6.1

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
@@ -1,35 +1,10 @@
1
1
  Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
2
- //#region \0rolldown/runtime.js
3
- var __create = Object.create;
4
- var __defProp = Object.defineProperty;
5
- var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
6
- var __getOwnPropNames = Object.getOwnPropertyNames;
7
- var __getProtoOf = Object.getPrototypeOf;
8
- var __hasOwnProp = Object.prototype.hasOwnProperty;
9
- var __copyProps = (to, from, except, desc) => {
10
- if (from && typeof from === "object" || typeof from === "function") for (var keys = __getOwnPropNames(from), i = 0, n = keys.length, key; i < n; i++) {
11
- key = keys[i];
12
- if (!__hasOwnProp.call(to, key) && key !== except) __defProp(to, key, {
13
- get: ((k) => from[k]).bind(null, key),
14
- enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable
15
- });
16
- }
17
- return to;
18
- };
19
- var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(isNodeMode || !mod || !mod.__esModule || !__hasOwnProp.call(mod, "default") ? __defProp(target, "default", {
20
- value: mod,
21
- enumerable: true
22
- }) : target, mod));
23
2
  //#endregion
24
- let _kubb_ast = require("@kubb/ast");
25
- let _kubb_core = require("@kubb/core");
3
+ let _kubb_kit = require("@kubb/kit");
26
4
  let node_fs_promises = require("node:fs/promises");
27
- let node_path = require("node:path");
28
- node_path = __toESM(node_path, 1);
29
5
  let yaml = require("yaml");
30
6
  let _scalar_openapi_upgrader = require("@scalar/openapi-upgrader");
31
7
  let api_ref_bundler = require("api-ref-bundler");
32
- let _kubb_kit = require("@kubb/kit");
33
8
  //#region src/constants.ts
34
9
  /**
35
10
  * Default parser options applied when no explicit options are provided.
@@ -153,11 +128,11 @@ const enumDescriptionKeys = ["x-enumDescriptions", "x-enum-descriptions"];
153
128
  function buildDiscriminatorChildMap(schemas) {
154
129
  const childMap = /* @__PURE__ */ new Map();
155
130
  for (const schema of schemas) {
156
- let unionNode = _kubb_ast.ast.narrowSchema(schema, "union");
131
+ let unionNode = _kubb_kit.ast.narrowSchema(schema, "union");
157
132
  if (!unionNode) {
158
- const intersectionMembers = _kubb_ast.ast.narrowSchema(schema, "intersection")?.members;
133
+ const intersectionMembers = _kubb_kit.ast.narrowSchema(schema, "intersection")?.members;
159
134
  if (intersectionMembers) for (const m of intersectionMembers) {
160
- const u = _kubb_ast.ast.narrowSchema(m, "union");
135
+ const u = _kubb_kit.ast.narrowSchema(m, "union");
161
136
  if (u) {
162
137
  unionNode = u;
163
138
  break;
@@ -167,17 +142,17 @@ function buildDiscriminatorChildMap(schemas) {
167
142
  if (!unionNode?.discriminatorPropertyName || !unionNode.members) continue;
168
143
  const { discriminatorPropertyName, members } = unionNode;
169
144
  for (const member of members) {
170
- const intersectionNode = _kubb_ast.ast.narrowSchema(member, "intersection");
145
+ const intersectionNode = _kubb_kit.ast.narrowSchema(member, "intersection");
171
146
  if (!intersectionNode?.members) continue;
172
147
  let refNode = null;
173
148
  let objNode = null;
174
149
  for (const m of intersectionNode.members) {
175
- refNode ??= _kubb_ast.ast.narrowSchema(m, "ref");
176
- objNode ??= _kubb_ast.ast.narrowSchema(m, "object");
150
+ refNode ??= _kubb_kit.ast.narrowSchema(m, "ref");
151
+ objNode ??= _kubb_kit.ast.narrowSchema(m, "object");
177
152
  }
178
153
  if (!refNode?.name || !objNode) continue;
179
154
  const prop = objNode.properties.find((p) => p.name === discriminatorPropertyName);
180
- const enumNode = prop ? _kubb_ast.ast.narrowSchema(prop.schema, "enum") : null;
155
+ const enumNode = prop ? _kubb_kit.ast.narrowSchema(prop.schema, "enum") : null;
181
156
  if (!enumNode?.enumValues?.length) continue;
182
157
  const enumValues = enumNode.enumValues.filter((v) => v !== null);
183
158
  if (!enumValues.length) continue;
@@ -203,16 +178,16 @@ function buildDiscriminatorChildMap(schemas) {
203
178
  */
204
179
  function patchDiscriminatorNode(node, entry) {
205
180
  const { propertyName, enumValues } = entry;
206
- const enumSchema = _kubb_ast.ast.factory.createSchema({
181
+ const enumSchema = _kubb_kit.ast.factory.createSchema({
207
182
  type: "enum",
208
183
  enumValues
209
184
  });
210
- const newProp = _kubb_ast.ast.factory.createProperty({
185
+ const newProp = _kubb_kit.ast.factory.createProperty({
211
186
  name: propertyName,
212
187
  required: true,
213
188
  schema: enumSchema
214
189
  });
215
- const objectNode = _kubb_ast.ast.narrowSchema(node, "object");
190
+ const objectNode = _kubb_kit.ast.narrowSchema(node, "object");
216
191
  if (objectNode) {
217
192
  const existingIdx = objectNode.properties.findIndex((p) => p.name === propertyName);
218
193
  const newProperties = existingIdx >= 0 ? objectNode.properties.map((p, i) => i === existingIdx ? newProp : p) : [...objectNode.properties, newProp];
@@ -221,14 +196,14 @@ function patchDiscriminatorNode(node, entry) {
221
196
  properties: newProperties
222
197
  };
223
198
  }
224
- const intersectionNode = _kubb_ast.ast.narrowSchema(node, "intersection");
199
+ const intersectionNode = _kubb_kit.ast.narrowSchema(node, "intersection");
225
200
  if (!intersectionNode?.members) return node;
226
- const discriminantNode = _kubb_ast.ast.factory.createSchema({
201
+ const discriminantNode = _kubb_kit.ast.factory.createSchema({
227
202
  type: "object",
228
203
  primitive: "object",
229
204
  properties: [newProp]
230
205
  });
231
- const patchedIdx = intersectionNode.members.findIndex((member) => _kubb_ast.ast.narrowSchema(member, "object")?.properties.some((p) => p.name === propertyName));
206
+ const patchedIdx = intersectionNode.members.findIndex((member) => _kubb_kit.ast.narrowSchema(member, "object")?.properties.some((p) => p.name === propertyName));
232
207
  const newMembers = patchedIdx >= 0 ? intersectionNode.members.map((member, i) => i === patchedIdx ? patchDiscriminatorNode(member, entry) : member) : [...intersectionNode.members, discriminantNode];
233
208
  return {
234
209
  ...intersectionNode,
@@ -399,8 +374,8 @@ async function fetchSource(url) {
399
374
  try {
400
375
  return await fetch(url);
401
376
  } catch (error) {
402
- throw new _kubb_core.Diagnostics.Error({
403
- code: _kubb_core.Diagnostics.code.inputUnreachable,
377
+ throw new _kubb_kit.Diagnostics.Error({
378
+ code: _kubb_kit.Diagnostics.code.inputUnreachable,
404
379
  severity: "error",
405
380
  message: `Cannot reach ${url.href}: ${describeFetchFailure(error)}`,
406
381
  help: "Check that the host is running and reachable from this machine. For a local server, start it and confirm the port matches the one in `input`.",
@@ -415,8 +390,8 @@ async function readSource(sourcePath) {
415
390
  const response = await fetchSource(url);
416
391
  if (!response.ok) {
417
392
  const status = response.statusText ? `${response.status} ${response.statusText}` : String(response.status);
418
- throw new _kubb_core.Diagnostics.Error({
419
- code: _kubb_core.Diagnostics.code.inputRequestFailed,
393
+ throw new _kubb_kit.Diagnostics.Error({
394
+ code: _kubb_kit.Diagnostics.code.inputRequestFailed,
420
395
  severity: "error",
421
396
  message: `The server at ${url.href} answered with HTTP ${status} instead of the OpenAPI document.`,
422
397
  help: helpForStatus(response.status),
@@ -453,8 +428,8 @@ async function resolveSource(sourcePath) {
453
428
  */
454
429
  async function assertInputExists(input) {
455
430
  if (urlRegExp.test(input)) return;
456
- if (!await exists(input)) throw new _kubb_core.Diagnostics.Error({
457
- code: _kubb_core.Diagnostics.code.inputNotFound,
431
+ if (!await exists(input)) throw new _kubb_kit.Diagnostics.Error({
432
+ code: _kubb_kit.Diagnostics.code.inputNotFound,
458
433
  severity: "error",
459
434
  message: `Cannot read the file set as \`input\` (or via \`kubb generate PATH\`): ${input}`,
460
435
  help: "Check that the path exists and is readable, then set it as `input` or pass it as `kubb generate PATH`.",
@@ -530,7 +505,7 @@ async function parseDocument(pathOrApi) {
530
505
  /**
531
506
  * Creates a `Document` from an `AdapterSource`.
532
507
  *
533
- * - `{ type: 'path' }` resolves and bundles a local file path or remote URL.
508
+ * - `{ type: 'path' }` bundles an absolute file path (core resolves `input`) or remote URL.
534
509
  * - `{ type: 'data' }` parses an inline string (YAML/JSON) or raw object.
535
510
  *
536
511
  * @example
@@ -541,10 +516,8 @@ async function parseDocument(pathOrApi) {
541
516
  */
542
517
  async function parseFromConfig(source) {
543
518
  if (source.type === "data") return parseDocument(typeof source.data === "string" ? (0, yaml.parse)(source.data) : structuredClone(source.data));
544
- if (urlRegExp.test(source.path)) return parseDocument(source.path);
545
- const resolved = node_path.default.resolve(node_path.default.dirname(source.path), source.path);
546
- await assertInputExists(resolved);
547
- return parseDocument(resolved);
519
+ await assertInputExists(source.path);
520
+ return parseDocument(source.path);
548
521
  }
549
522
  /**
550
523
  * Asserts the parsed input is an OpenAPI or Swagger document.
@@ -556,8 +529,8 @@ async function parseFromConfig(source) {
556
529
  */
557
530
  function assertDocument(document) {
558
531
  if (document && ("openapi" in document || "swagger" in document)) return;
559
- throw new _kubb_core.Diagnostics.Error({
560
- code: _kubb_core.Diagnostics.code.invalidDocument,
532
+ throw new _kubb_kit.Diagnostics.Error({
533
+ code: _kubb_kit.Diagnostics.code.invalidDocument,
561
534
  severity: "error",
562
535
  message: "The resolved `input` is not an OpenAPI or Swagger document: it declares no `openapi` or `swagger` version.",
563
536
  help: "Point `input` at a document that declares `openapi` or `swagger`. If you pass an object, pass the spec itself rather than a wrapper such as `{ path }` or `{ data }`.",
@@ -566,8 +539,8 @@ function assertDocument(document) {
566
539
  }
567
540
  /** Reports a validation problem as a `KUBB_INVALID_SPEC` warning, a no-op outside a build. */
568
541
  function reportInvalidSpec(message) {
569
- _kubb_core.Diagnostics.report({
570
- code: _kubb_core.Diagnostics.code.invalidSpec,
542
+ _kubb_kit.Diagnostics.report({
543
+ code: _kubb_kit.Diagnostics.code.invalidSpec,
571
544
  severity: "warning",
572
545
  message,
573
546
  help: "Fix the spec, or run `kubb validate` to list every error. Set `validate: false` on the adapter to skip this check."
@@ -673,21 +646,6 @@ const jsonMimeFragments = [
673
646
  function isJsonMimeType(mimeType) {
674
647
  return jsonMimeFragments.some((fragment) => mimeType.includes(fragment));
675
648
  }
676
- /**
677
- * Picks a media-type entry from a `content` map: the first JSON-like media type, falling back to
678
- * the first declared one. Returns `false` when `content` has no entries.
679
- *
680
- * @example
681
- * ```ts
682
- * pickContentEntry({ 'application/xml': xmlEntry, 'application/json': jsonEntry })
683
- * // ['application/json', jsonEntry]
684
- * ```
685
- */
686
- function pickContentEntry(content) {
687
- const mediaTypes = Object.keys(content);
688
- const available = mediaTypes.find(isJsonMimeType) ?? mediaTypes[0];
689
- return available ? [available, content[available]] : false;
690
- }
691
649
  //#endregion
692
650
  //#region src/model/components.ts
693
651
  /**
@@ -877,8 +835,8 @@ function resolveServerUrl(server, overrides) {
877
835
  for (const [key, variable] of Object.entries(server.variables)) {
878
836
  const value = overrides?.[key] ?? (variable.default != null ? String(variable.default) : void 0);
879
837
  if (value === void 0) continue;
880
- if (variable.enum?.length && !variable.enum.some((e) => String(e) === value)) throw new _kubb_core.Diagnostics.Error({
881
- code: _kubb_core.Diagnostics.code.invalidServerVariable,
838
+ if (variable.enum?.length && !variable.enum.some((e) => String(e) === value)) throw new _kubb_kit.Diagnostics.Error({
839
+ code: _kubb_kit.Diagnostics.code.invalidServerVariable,
882
840
  severity: "error",
883
841
  message: `Invalid server variable value '${value}' for '${key}' when resolving ${server.url}. Valid values are: ${variable.enum.join(", ")}.`,
884
842
  help: `Use one of the allowed enum values, or drop the enum on the '${key}' server variable.`,
@@ -893,10 +851,6 @@ function resolveServerUrl(server, overrides) {
893
851
  }
894
852
  //#endregion
895
853
  //#region src/operation.ts
896
- /**
897
- * Slugifies a path for the `operationId` fallback: non-alphanumerics collapse to single dashes,
898
- * with no leading or trailing dash.
899
- */
900
854
  function slugify(value) {
901
855
  return value.replace(/[^a-zA-Z0-9]/g, "-").replace(/-{2,}/g, "-").replace(/^-|-$/g, "");
902
856
  }
@@ -917,12 +871,12 @@ function getResponseStatusCodes({ schema }) {
917
871
  return Object.keys(responses).filter((key) => !key.startsWith("x-") && !!responses[key] && typeof responses[key] === "object");
918
872
  }
919
873
  /**
920
- * Returns the response object for a status code, resolving a `$ref` through `refs`. `false` when absent.
874
+ * Returns the response object for a status code, resolving a `$ref` through `refs`. `null` when absent.
921
875
  */
922
876
  function getResponseByStatusCode({ operation, refs, statusCode }) {
923
877
  const responses = operation.schema.responses;
924
- if (!responses || isReference(responses)) return false;
925
- return refs.deref(responses[statusCode]) ?? false;
878
+ if (!responses || isReference(responses)) return null;
879
+ return refs.deref(responses[statusCode]);
926
880
  }
927
881
  /**
928
882
  * Resolves the operation's request body, dereferencing a `$ref` through `refs`. Returns `null`
@@ -932,51 +886,37 @@ function getRequestBody({ operation, refs }) {
932
886
  return refs.deref(operation.schema.requestBody);
933
887
  }
934
888
  /**
935
- * Resolves the request body (a `$ref` through `refs`) and returns its content map, or
936
- * `undefined` when the operation has no request body.
937
- */
938
- function getRequestBodyContent({ operation, refs }) {
939
- return getRequestBody({
940
- operation,
941
- refs
942
- })?.content;
943
- }
944
- /**
945
- * Returns the request body media type. With `mediaType` set, returns that entry or `false`.
946
- * Otherwise picks the first JSON-like media type, then the first declared one, as a
947
- * `[mediaType, object]` tuple.
948
- */
949
- function getRequestContent({ operation, refs, mediaType }) {
950
- const content = getRequestBodyContent({
951
- operation,
952
- refs
953
- });
954
- if (!content) return false;
955
- if (mediaType) return mediaType in content ? content[mediaType] : false;
956
- return pickContentEntry(content);
957
- }
958
- /**
959
889
  * Returns the primary request content type. Prefers a JSON-like media type (the last one wins
960
890
  * when several are declared), then the first declared one, defaulting to `'application/json'`.
961
891
  */
962
892
  function getRequestContentType({ operation, refs }) {
963
- const content = getRequestBodyContent({
893
+ const content = getRequestBody({
964
894
  operation,
965
895
  refs
966
- });
896
+ })?.content;
967
897
  const mediaTypes = content ? Object.keys(content) : [];
968
898
  return mediaTypes.findLast(isJsonMimeType) ?? mediaTypes[0] ?? "application/json";
969
899
  }
900
+ /** Merges path-level and operation-level parameters; operation-level wins per `in:name`. */
901
+ function getParameters({ operation, refs }) {
902
+ const resolveParams = (params) => params.map((p) => refs.derefKeepingRef(p)).filter((p) => !!p && typeof p === "object" && "in" in p && "name" in p);
903
+ const operationParams = resolveParams(operation.schema?.parameters || []);
904
+ const pathLevelParams = resolveParams(operation.pathItem.parameters ?? []);
905
+ const paramMap = /* @__PURE__ */ new Map();
906
+ for (const p of [...pathLevelParams, ...operationParams]) if (p.name && p.in) paramMap.set(`${p.in}:${p.name}`, p);
907
+ return Array.from(paramMap.values());
908
+ }
909
+ /** Schema for one media type of a `content` map, with the binary fallback for an emptied non-JSON entry. */
910
+ function getBodySchema({ content, contentType, refs }) {
911
+ const entry = contentType ? content?.[contentType] : void 0;
912
+ if (!entry) return null;
913
+ const binary = getBinaryFallbackSchema(contentType, entry.schema);
914
+ if (binary) return binary;
915
+ return entry.schema ? refs.derefKeepingRef(entry.schema) : null;
916
+ }
970
917
  /**
971
918
  * Builds an `Operation` for every supported HTTP method on every path, in document order.
972
919
  * `x-` path keys and unresolvable path-item `$ref`s are skipped.
973
- *
974
- * @example
975
- * ```ts
976
- * for (const operation of getOperations(document, refs)) {
977
- * parseOperation(options, operation)
978
- * }
979
- * ```
980
920
  */
981
921
  function getOperations(document, refs) {
982
922
  const operations = [];
@@ -1001,32 +941,144 @@ function getOperations(document, refs) {
1001
941
  }
1002
942
  return operations;
1003
943
  }
1004
- //#endregion
1005
- //#region src/emit/schemaShape.ts
1006
- /**
1007
- * Returns the Kubb `SchemaType` for a given OAS `format` string, or `null` if not found.
1008
- * Formats not in `formatMap` (e.g., `int64`, `uint64`, `date-time`) are handled separately by parser options.
1009
- */
1010
- function getSchemaType(format) {
1011
- return formatMap[format] ?? null;
944
+ /** Property names whose schema has a truthy `readOnly` or `writeOnly` flag; `$ref` entries are skipped. */
945
+ function collectPropertyKeysByFlag(schema, flag) {
946
+ if (!schema?.properties) return null;
947
+ const keys = [];
948
+ for (const key in schema.properties) {
949
+ const prop = schema.properties[key];
950
+ if (prop && !isReference(prop) && prop[flag]) keys.push(key);
951
+ }
952
+ return keys.length ? keys : null;
953
+ }
954
+ function parseParameter({ param, parentName, options, parseSchema }) {
955
+ const schemaName = parentName && param.name ? pascalCase(`${parentName} ${param.name}`) : void 0;
956
+ const schema = param.schema ? parseSchema({
957
+ schema: param.schema,
958
+ name: schemaName
959
+ }) : _kubb_kit.ast.factory.createSchema({ type: options.unknownType });
960
+ const style = param.style;
961
+ const explode = param.explode;
962
+ return _kubb_kit.ast.factory.createParameter({
963
+ name: param.name,
964
+ in: param.in,
965
+ schema: {
966
+ ...schema,
967
+ description: param.description ?? schema.description
968
+ },
969
+ required: param.required ?? false,
970
+ ...style !== void 0 ? { style } : {},
971
+ ...explode !== void 0 ? { explode } : {}
972
+ });
1012
973
  }
1013
974
  /**
1014
- * Whether the parser maps `format` to a dedicated type. True for any `formatMap` entry, plus the
1015
- * `specialCasedFormats` that `convertFormat` handles directly (int64, uint64, date-time, date, time). False means the format falls back to
1016
- * the base type, which is what `KUBB_UNSUPPORTED_FORMAT` flags. Reading both sources keeps the
1017
- * diagnostic in step with the parser as `formatMap` grows.
975
+ * Converts an OAS `Operation` into an `OperationNode`.
1018
976
  */
1019
- function isHandledFormat(format) {
1020
- return getSchemaType(format) !== null || specialCasedFormats.has(format);
977
+ function parseOperation({ operation, ...ctx }) {
978
+ const { refs, contentType, options, parseSchema } = ctx;
979
+ const operationId = getOperationId(operation);
980
+ const operationName = operationId ? pascalCase(operationId) : void 0;
981
+ const parameters = getParameters({
982
+ operation,
983
+ refs
984
+ }).map((param) => parseParameter({
985
+ options,
986
+ parseSchema,
987
+ param,
988
+ parentName: operationName
989
+ }));
990
+ const body = getRequestBody({
991
+ operation,
992
+ refs
993
+ });
994
+ const bodyRequired = body?.required === true;
995
+ const requestBodyName = operationName ? `${operationName}Request` : void 0;
996
+ const content = (contentType ? [contentType] : Object.keys(body?.content ?? {})).flatMap((ct) => {
997
+ const schema = getBodySchema({
998
+ content: body?.content,
999
+ contentType: ct,
1000
+ refs
1001
+ });
1002
+ if (!schema) return [];
1003
+ return [_kubb_kit.ast.factory.createContent({
1004
+ contentType: ct,
1005
+ schema: _kubb_kit.ast.optionality(parseSchema({
1006
+ schema,
1007
+ name: requestBodyName
1008
+ }), bodyRequired),
1009
+ keysToOmit: collectPropertyKeysByFlag(schema, "readOnly")
1010
+ })];
1011
+ });
1012
+ const requestBody = content.length > 0 || body?.description ? {
1013
+ description: body?.description,
1014
+ required: bodyRequired || void 0,
1015
+ content: content.length > 0 ? content : void 0
1016
+ } : void 0;
1017
+ const responses = getResponseStatusCodes(operation).map((statusCode) => {
1018
+ const response = getResponseByStatusCode({
1019
+ operation,
1020
+ refs,
1021
+ statusCode
1022
+ });
1023
+ const responseName = operationName ? `${operationName}Status${statusCode}` : void 0;
1024
+ const parseEntrySchema = (ct) => {
1025
+ const raw = getBodySchema({
1026
+ content: response?.content,
1027
+ contentType: ct,
1028
+ refs
1029
+ });
1030
+ return {
1031
+ schema: raw && Object.keys(raw).length > 0 ? parseSchema({
1032
+ schema: raw,
1033
+ name: responseName
1034
+ }) : _kubb_kit.ast.factory.createSchema({ type: options.emptySchemaType }),
1035
+ keysToOmit: collectPropertyKeysByFlag(raw, "writeOnly")
1036
+ };
1037
+ };
1038
+ const responseContent = (contentType ? [contentType] : Object.keys(response?.content ?? {})).map((ct) => _kubb_kit.ast.factory.createContent({
1039
+ contentType: ct,
1040
+ ...parseEntrySchema(ct)
1041
+ }));
1042
+ if (responseContent.length === 0) responseContent.push(_kubb_kit.ast.factory.createContent({
1043
+ contentType: getRequestContentType({
1044
+ operation,
1045
+ refs
1046
+ }),
1047
+ ...parseEntrySchema(contentType)
1048
+ }));
1049
+ return _kubb_kit.ast.factory.createResponse({
1050
+ statusCode,
1051
+ description: response?.description,
1052
+ content: responseContent
1053
+ });
1054
+ });
1055
+ const pickDoc = (key) => {
1056
+ const own = operation.schema[key];
1057
+ if (typeof own === "string") return own;
1058
+ const fallback = operation.pathItem[key];
1059
+ return typeof fallback === "string" ? fallback : void 0;
1060
+ };
1061
+ return _kubb_kit.ast.factory.createOperation({
1062
+ operationId,
1063
+ protocol: "http",
1064
+ method: operation.method.toUpperCase(),
1065
+ path: operation.path,
1066
+ tags: Array.isArray(operation.schema.tags) ? operation.schema.tags.map(String) : [],
1067
+ summary: pickDoc("summary") || void 0,
1068
+ description: pickDoc("description") || void 0,
1069
+ deprecated: operation.schema.deprecated || void 0,
1070
+ parameters,
1071
+ requestBody,
1072
+ responses
1073
+ });
1021
1074
  }
1075
+ //#endregion
1076
+ //#region src/emit/schemaShape.ts
1022
1077
  /**
1023
- * Converts an OAS primitive type string to its `PrimitiveSchemaType` equivalent.
1024
- * Numeric types (`number`, `integer`, `bigint`) pass through unchanged. `boolean` maps to `'boolean'`. Everything else becomes `'string'`.
1078
+ * Whether `format` maps to a dedicated type; `false` is what `KUBB_UNSUPPORTED_FORMAT` flags.
1025
1079
  */
1026
- function getPrimitiveType(type) {
1027
- if (type === "number" || type === "integer" || type === "bigint") return type;
1028
- if (type === "boolean") return "boolean";
1029
- return "string";
1080
+ function isHandledFormat(format) {
1081
+ return formatMap[format] !== void 0 || specialCasedFormats.has(format);
1030
1082
  }
1031
1083
  /**
1032
1084
  * Resolves the `dateType` option down to the value for one format. The scalar form of
@@ -1038,51 +1090,6 @@ function resolveDateTypeValue(dateType, format) {
1038
1090
  return dateType[format === "date-time" ? "dateTime" : format] ?? "string";
1039
1091
  }
1040
1092
  /**
1041
- * Resolves the AST type descriptor for a date/time format, honoring the `dateType` option.
1042
- * Returns `null` when the resolved value is `false`, so the format falls through to `string`.
1043
- */
1044
- function getDateType(options, format) {
1045
- const value = resolveDateTypeValue(options.dateType, format);
1046
- if (!value) return null;
1047
- if (format === "date-time") {
1048
- if (value === "date") return {
1049
- type: "date",
1050
- representation: "date"
1051
- };
1052
- if (value === "stringOffset") return {
1053
- type: "datetime",
1054
- offset: true
1055
- };
1056
- if (value === "stringLocal") return {
1057
- type: "datetime",
1058
- local: true
1059
- };
1060
- return {
1061
- type: "datetime",
1062
- offset: false
1063
- };
1064
- }
1065
- if (format === "date") return {
1066
- type: "date",
1067
- representation: value === "date" ? "date" : "string"
1068
- };
1069
- return {
1070
- type: "time",
1071
- representation: value === "date" ? "date" : "string"
1072
- };
1073
- }
1074
- /**
1075
- * Reads a schema's numeric `exclusiveMinimum`/`exclusiveMaximum` bounds (the OAS 3.1 numeric
1076
- * form). Either key is `undefined` when absent or, for the legacy OAS 3.0 boolean form, not a
1077
- * number.
1078
- */
1079
- function getExclusiveBounds(schema) {
1080
- return {
1081
- exclusiveMinimum: typeof schema.exclusiveMinimum === "number" ? schema.exclusiveMinimum : void 0,
1082
- exclusiveMaximum: typeof schema.exclusiveMaximum === "number" ? schema.exclusiveMaximum : void 0
1083
- };
1084
- }
1085
- /**
1086
1093
  * Reads schema examples as an array. OAS 3.1 uses an `examples` array, but specs (including ones
1087
1094
  * labeled 3.1) still use the singular OAS 3.0 `example`, which the upgrader only converts on the
1088
1095
  * 3.0 -> 3.1 hop. Normalize both into one array so the AST node exposes only `examples`.
@@ -1091,12 +1098,6 @@ function extractExamples(schema) {
1091
1098
  if (Array.isArray(schema.examples)) return schema.examples;
1092
1099
  return schema.example !== void 0 ? [schema.example] : void 0;
1093
1100
  }
1094
- /**
1095
- * Returns `true` when `fragment` carries any JSON Schema keyword that makes it
1096
- * structurally significant on its own (see `structuralKeys`).
1097
- *
1098
- * A fragment with a structural keyword can't be safely merged into a parent schema.
1099
- */
1100
1101
  function hasStructuralKeywords(fragment) {
1101
1102
  return Object.keys(fragment).some((key) => structuralKeys.has(key));
1102
1103
  }
@@ -1104,23 +1105,19 @@ function hasStructuralKeywords(fragment) {
1104
1105
  * Flattens a keyword-only `allOf` into its parent schema.
1105
1106
  *
1106
1107
  * Only flattens when every member is a plain fragment, with no `$ref` and no structural keywords
1107
- * (see `structuralKeys`). Outer schema values take precedence over fragment values.
1108
- * Returns `null` for a `null` input, and the original schema unchanged when flattening is unsafe.
1108
+ * (see `structuralKeys`). Outer schema values take precedence over fragment values. Returns the
1109
+ * original schema unchanged when flattening is unsafe.
1109
1110
  *
1110
1111
  * @example
1111
1112
  * ```ts
1112
1113
  * flattenSchema({ allOf: [{ description: 'A pet' }], type: 'object', properties: {} })
1113
1114
  * // { type: 'object', properties: {}, description: 'A pet' }
1114
- * ```
1115
- *
1116
- * @example
1117
- * ```ts
1118
1115
  * flattenSchema({ allOf: [{ $ref: '#/components/schemas/Pet' }] })
1119
1116
  * // returned unchanged, contains a $ref
1120
1117
  * ```
1121
1118
  */
1122
1119
  function flattenSchema(schema) {
1123
- if (!schema?.allOf || schema.allOf.length === 0) return schema ?? null;
1120
+ if (!schema.allOf?.length) return schema;
1124
1121
  const allOfFragments = schema.allOf;
1125
1122
  if (allOfFragments.some((item) => isReference(item))) return schema;
1126
1123
  if (allOfFragments.some(hasStructuralKeywords)) return schema;
@@ -1137,7 +1134,7 @@ function flattenSchema(schema) {
1137
1134
  * whatever makes its node distinct; this folds both into one call.
1138
1135
  */
1139
1136
  function createNode({ schema, name, nullable, defaultValue }, extras) {
1140
- return _kubb_ast.ast.factory.createSchema({
1137
+ return _kubb_kit.ast.factory.createSchema({
1141
1138
  name,
1142
1139
  nullable,
1143
1140
  title: schema.title,
@@ -1163,12 +1160,12 @@ function createNode({ schema, name, nullable, defaultValue }, extras) {
1163
1160
  * ```
1164
1161
  */
1165
1162
  function createDiscriminantNode({ propertyName, values }) {
1166
- return _kubb_ast.ast.factory.createSchema({
1163
+ return _kubb_kit.ast.factory.createSchema({
1167
1164
  type: "object",
1168
1165
  primitive: "object",
1169
- properties: [_kubb_ast.ast.factory.createProperty({
1166
+ properties: [_kubb_kit.ast.factory.createProperty({
1170
1167
  name: propertyName,
1171
- schema: _kubb_ast.ast.factory.createSchema({
1168
+ schema: _kubb_kit.ast.factory.createSchema({
1172
1169
  type: "enum",
1173
1170
  primitive: "string",
1174
1171
  enumValues: values
@@ -1199,11 +1196,11 @@ function findDiscriminators(mapping, ref) {
1199
1196
  * at this union usage site, which is what makes this mode "preserve" (as opposed to `propagate`,
1200
1197
  * which additionally patches the child schema's own definition in a post-pass).
1201
1198
  */
1202
- function narrowUnionMembers({ unionMembers, discriminator, sharedPropertiesNode, parse, rawOptions, name, refs }) {
1199
+ function narrowUnionMembers({ unionMembers, discriminator, sharedPropertiesNode, parse, name, refs }) {
1203
1200
  function pickDiscriminatorPropertyNode(node, propertyName) {
1204
- const discriminatorProperty = _kubb_ast.ast.narrowSchema(node, "object")?.properties?.find((property) => property.name === propertyName);
1201
+ const discriminatorProperty = _kubb_kit.ast.narrowSchema(node, "object")?.properties?.find((property) => property.name === propertyName);
1205
1202
  if (!discriminatorProperty) return null;
1206
- return _kubb_ast.ast.factory.createSchema({
1203
+ return _kubb_kit.ast.factory.createSchema({
1207
1204
  type: "object",
1208
1205
  primitive: "object",
1209
1206
  properties: [discriminatorProperty]
@@ -1241,13 +1238,13 @@ function narrowUnionMembers({ unionMembers, discriminator, sharedPropertiesNode,
1241
1238
  const memberNode = parse({
1242
1239
  schema: s,
1243
1240
  name
1244
- }, rawOptions);
1241
+ });
1245
1242
  if (!discriminatorValues.length || !discriminator) return memberNode;
1246
- const narrowedDiscriminatorNode = sharedPropertiesNode ? pickDiscriminatorPropertyNode(_kubb_ast.ast.applyMacros(sharedPropertiesNode, [(0, _kubb_kit.macroDiscriminatorEnum)({
1243
+ const narrowedDiscriminatorNode = sharedPropertiesNode ? pickDiscriminatorPropertyNode(_kubb_kit.ast.applyMacros(sharedPropertiesNode, [(0, _kubb_kit.macroDiscriminatorEnum)({
1247
1244
  propertyName: discriminator.propertyName,
1248
1245
  values: discriminatorValues
1249
1246
  })], { depth: "shallow" }), discriminator.propertyName) : void 0;
1250
- return _kubb_ast.ast.factory.createSchema({
1247
+ return _kubb_kit.ast.factory.createSchema({
1251
1248
  type: "intersection",
1252
1249
  members: [memberNode, narrowedDiscriminatorNode ?? createDiscriminantNode({
1253
1250
  propertyName: discriminator.propertyName,
@@ -1427,9 +1424,9 @@ function resolveUnionSchema(schema, siblingProperties) {
1427
1424
  * Use `syncSchemaRef(node)` in printers to get a merged view of both.
1428
1425
  * Circular refs are detected in `refs.resolveNode` and leave `schema` as `null`.
1429
1426
  */
1430
- function convertRef({ schema, name, nullable, defaultValue, rawOptions, document, parse, refs, renames }) {
1427
+ function convertRef({ schema, name, nullable, defaultValue, document, parse, refs, renames }) {
1431
1428
  const refPath = schema.$ref;
1432
- const resolvedSchema = refPath ? refs.resolveNode(refPath, parse, rawOptions) : null;
1429
+ const resolvedSchema = refPath ? refs.resolveNode(refPath, parse) : null;
1433
1430
  const ctx = {
1434
1431
  schema,
1435
1432
  name,
@@ -1449,17 +1446,17 @@ function convertRef({ schema, name, nullable, defaultValue, rawOptions, document
1449
1446
  /**
1450
1447
  * Converts an `allOf` schema into a flattened node or an `IntersectionSchemaNode`.
1451
1448
  */
1452
- function convertAllOf({ schema, name, nullable, defaultValue, rawOptions, parse, refs }) {
1449
+ function convertAllOf({ schema, name, nullable, defaultValue, parse, refs }) {
1453
1450
  if (schema.allOf.length === 1 && !schema.properties && !(Array.isArray(schema.required) && schema.required.length) && schema.additionalProperties === void 0) {
1454
1451
  const [memberSchema] = schema.allOf;
1455
1452
  const memberNode = parse({
1456
1453
  schema: memberSchema,
1457
1454
  name
1458
- }, rawOptions);
1455
+ });
1459
1456
  const { kind: _kind, ...memberNodeProps } = memberNode;
1460
1457
  const mergedNullable = nullable || memberNode.nullable || void 0;
1461
1458
  const mergedDefault = schema.default === null && mergedNullable ? void 0 : schema.default ?? memberNode.default;
1462
- return _kubb_ast.ast.factory.createSchema({
1459
+ return _kubb_kit.ast.factory.createSchema({
1463
1460
  ...memberNodeProps,
1464
1461
  name,
1465
1462
  title: schema.title ?? memberNode.title,
@@ -1483,7 +1480,7 @@ function convertAllOf({ schema, name, nullable, defaultValue, rawOptions, parse,
1483
1480
  const allOfMembers = discriminatedAllOf.map((s) => isReference(s) ? s : resolveUnionSchema(s, siblingProperties)).map((s) => parse({
1484
1481
  schema: s,
1485
1482
  name
1486
- }, rawOptions));
1483
+ }));
1487
1484
  const syntheticStart = allOfMembers.length;
1488
1485
  if (Array.isArray(schema.required) && schema.required.length) {
1489
1486
  const outerKeys = schema.properties ? new Set(Object.keys(schema.properties)) : /* @__PURE__ */ new Set();
@@ -1504,7 +1501,7 @@ function convertAllOf({ schema, name, nullable, defaultValue, rawOptions, parse,
1504
1501
  allOfMembers.push(parse({
1505
1502
  schema: memberSchema,
1506
1503
  name
1507
- }, rawOptions));
1504
+ }));
1508
1505
  break;
1509
1506
  }
1510
1507
  }
@@ -1512,7 +1509,7 @@ function convertAllOf({ schema, name, nullable, defaultValue, rawOptions, parse,
1512
1509
  }
1513
1510
  if (schema.properties) {
1514
1511
  const { allOf: _allOf, ...schemaWithoutAllOf } = schema;
1515
- allOfMembers.push(parse({ schema: schemaWithoutAllOf }, rawOptions));
1512
+ allOfMembers.push(parse({ schema: schemaWithoutAllOf }));
1516
1513
  }
1517
1514
  for (const { propertyName, values } of discriminantValues) allOfMembers.push(createDiscriminantNode({
1518
1515
  propertyName,
@@ -1534,7 +1531,7 @@ function convertAllOf({ schema, name, nullable, defaultValue, rawOptions, parse,
1534
1531
  function convertUnion(context) {
1535
1532
  const annotatedEnum = convertAnnotatedEnum(context);
1536
1533
  if (annotatedEnum) return annotatedEnum;
1537
- const { schema, name, nullable, defaultValue, rawOptions, parse, refs } = context;
1534
+ const { schema, name, nullable, defaultValue, parse, refs } = context;
1538
1535
  const ctx = {
1539
1536
  schema,
1540
1537
  name,
@@ -1549,14 +1546,13 @@ function convertUnion(context) {
1549
1546
  const sharedPropertiesNode = schema.properties ? parse({
1550
1547
  schema: memberBaseSchema,
1551
1548
  name
1552
- }, rawOptions) : void 0;
1549
+ }) : void 0;
1553
1550
  if (sharedPropertiesNode || discriminator) {
1554
1551
  const members = narrowUnionMembers({
1555
1552
  unionMembers,
1556
1553
  discriminator,
1557
1554
  sharedPropertiesNode,
1558
1555
  parse,
1559
- rawOptions,
1560
1556
  name,
1561
1557
  refs
1562
1558
  });
@@ -1564,7 +1560,7 @@ function convertUnion(context) {
1564
1560
  type: "union",
1565
1561
  strategy,
1566
1562
  members,
1567
- discriminatorPropertyName: explicitDiscriminatorPropertyName ?? _kubb_ast.ast.inferDiscriminatorPropertyName(members)
1563
+ discriminatorPropertyName: explicitDiscriminatorPropertyName ?? _kubb_kit.ast.inferDiscriminatorPropertyName(members)
1568
1564
  });
1569
1565
  if (!sharedPropertiesNode) return unionNode;
1570
1566
  return createNode(ctx, {
@@ -1575,14 +1571,14 @@ function convertUnion(context) {
1575
1571
  const members = unionMembers.map((s) => parse({
1576
1572
  schema: s,
1577
1573
  name
1578
- }, rawOptions));
1574
+ }));
1579
1575
  const unionNode = createNode(ctx, {
1580
1576
  type: "union",
1581
1577
  strategy,
1582
1578
  members,
1583
- discriminatorPropertyName: explicitDiscriminatorPropertyName ?? _kubb_ast.ast.inferDiscriminatorPropertyName(members)
1579
+ discriminatorPropertyName: explicitDiscriminatorPropertyName ?? _kubb_kit.ast.inferDiscriminatorPropertyName(members)
1584
1580
  });
1585
- return _kubb_ast.ast.applyMacros(unionNode, [_kubb_kit.macroSimplifyUnion], { depth: "shallow" });
1581
+ return _kubb_kit.ast.applyMacros(unionNode, [_kubb_kit.macroSimplifyUnion], { depth: "shallow" });
1586
1582
  }
1587
1583
  /**
1588
1584
  * Converts an OAS 3.1 multi-type array (e.g. `type: ['string', 'number']`) into a `UnionSchemaNode`.
@@ -1590,7 +1586,7 @@ function convertUnion(context) {
1590
1586
  * remains; a single remaining type (e.g. `['string', 'null']`) is handled as that type instead,
1591
1587
  * with nullability already folded in.
1592
1588
  */
1593
- function convertMultiType({ schema, name, nullable, defaultValue, rawOptions, parse }) {
1589
+ function convertMultiType({ schema, name, nullable, defaultValue, parse }) {
1594
1590
  const types = schema.type;
1595
1591
  const nonNullTypes = types.filter((t) => t !== "null");
1596
1592
  return createNode({
@@ -1607,7 +1603,7 @@ function convertMultiType({ schema, name, nullable, defaultValue, rawOptions, pa
1607
1603
  type: t
1608
1604
  },
1609
1605
  name
1610
- }, rawOptions);
1606
+ });
1611
1607
  })
1612
1608
  });
1613
1609
  }
@@ -1632,12 +1628,50 @@ function normalizeArrayEnum(schema) {
1632
1628
  items: normalizedItems
1633
1629
  };
1634
1630
  }
1631
+ function getPrimitiveType(type) {
1632
+ if (type === "number" || type === "integer" || type === "bigint") return type;
1633
+ if (type === "boolean") return "boolean";
1634
+ return "string";
1635
+ }
1636
+ /** Shared by number, integer and bigint nodes; the exclusive bounds keep only the OAS 3.1 numeric form. */
1637
+ function getNumericConstraints(schema) {
1638
+ return {
1639
+ min: schema.minimum,
1640
+ max: schema.maximum,
1641
+ exclusiveMinimum: typeof schema.exclusiveMinimum === "number" ? schema.exclusiveMinimum : void 0,
1642
+ exclusiveMaximum: typeof schema.exclusiveMaximum === "number" ? schema.exclusiveMaximum : void 0,
1643
+ multipleOf: schema.multipleOf
1644
+ };
1645
+ }
1646
+ function getDateType(options, format) {
1647
+ const value = resolveDateTypeValue(options.dateType, format);
1648
+ if (format !== "date-time") return {
1649
+ type: format,
1650
+ representation: value === "date" ? "date" : "string"
1651
+ };
1652
+ if (value === "date") return {
1653
+ type: "date",
1654
+ representation: "date"
1655
+ };
1656
+ if (value === "stringOffset") return {
1657
+ type: "datetime",
1658
+ offset: true
1659
+ };
1660
+ if (value === "stringLocal") return {
1661
+ type: "datetime",
1662
+ local: true
1663
+ };
1664
+ return {
1665
+ type: "datetime",
1666
+ offset: false
1667
+ };
1668
+ }
1635
1669
  /**
1636
1670
  * Builds a `null` scalar node carrying the schema's documentation. Shared by the `const: null`
1637
1671
  * and the drf-spectacular `NullEnum` (`{ enum: [null] }`) branches, which render identically.
1638
1672
  */
1639
1673
  function createNullNode(schema, name, nullable) {
1640
- return _kubb_ast.ast.factory.createSchema({
1674
+ return _kubb_kit.ast.factory.createSchema({
1641
1675
  type: "null",
1642
1676
  primitive: "null",
1643
1677
  name,
@@ -1651,22 +1685,13 @@ function createNullNode(schema, name, nullable) {
1651
1685
  /**
1652
1686
  * Converts an OAS 3.1 `const` schema into a null scalar or a single-value `EnumSchemaNode`.
1653
1687
  */
1654
- function convertConst({ schema, name, nullable, defaultValue }) {
1688
+ function convertConst(context) {
1689
+ const { schema, name } = context;
1655
1690
  const constValue = schema.const;
1656
1691
  if (constValue === null) return createNullNode(schema, name);
1657
- const constPrimitive = getPrimitiveType((() => {
1658
- if (typeof constValue === "number") return "number";
1659
- if (typeof constValue === "boolean") return "boolean";
1660
- return "string";
1661
- })());
1662
- return createNode({
1663
- schema,
1664
- name,
1665
- nullable,
1666
- defaultValue
1667
- }, {
1692
+ return createNode(context, {
1668
1693
  type: "enum",
1669
- primitive: constPrimitive,
1694
+ primitive: getPrimitiveType(typeof constValue),
1670
1695
  enumValues: [constValue]
1671
1696
  });
1672
1697
  }
@@ -1676,36 +1701,30 @@ function convertConst({ schema, name, nullable, defaultValue }) {
1676
1701
  * a date-ish format, that `dateType` is not `false`.
1677
1702
  */
1678
1703
  function convertFormat(context) {
1679
- const { schema, name, nullable, defaultValue, options, type } = context;
1680
- const ctx = {
1681
- schema,
1682
- name,
1683
- nullable,
1684
- defaultValue
1685
- };
1704
+ const { schema, options, type } = context;
1686
1705
  if (type === "string" && numericFormats.has(schema.format)) return convertString(context);
1687
- if (schema.format === "int64" || schema.format === "uint64") return createNode(ctx, {
1706
+ if (schema.format === "int64" || schema.format === "uint64") return createNode(context, {
1688
1707
  type: options.integerType === "bigint" ? "bigint" : "integer",
1689
1708
  primitive: "integer",
1690
1709
  ...getNumericConstraints(schema)
1691
1710
  });
1692
1711
  if (schema.format === "date-time" || schema.format === "date" || schema.format === "time") {
1693
1712
  const dateType = getDateType(options, schema.format);
1694
- if (dateType.type === "datetime") return createNode(ctx, {
1713
+ if (dateType.type === "datetime") return createNode(context, {
1695
1714
  primitive: "string",
1696
1715
  type: "datetime",
1697
1716
  offset: dateType.offset,
1698
1717
  local: dateType.local
1699
1718
  });
1700
- return createNode(ctx, {
1719
+ return createNode(context, {
1701
1720
  primitive: "string",
1702
1721
  type: dateType.type,
1703
1722
  representation: dateType.representation
1704
1723
  });
1705
1724
  }
1706
- const specialType = getSchemaType(schema.format);
1707
- const isNumeric = specialType === "number" || specialType === "integer" || specialType === "bigint";
1708
- return createNode(ctx, {
1725
+ const specialType = formatMap[schema.format];
1726
+ const isNumeric = specialType === "number" || specialType === "integer";
1727
+ return createNode(context, {
1709
1728
  primitive: isNumeric ? specialType : "string",
1710
1729
  type: specialType,
1711
1730
  ...specialType === "url" || specialType === "uuid" || specialType === "email" ? {
@@ -1718,11 +1737,11 @@ function convertFormat(context) {
1718
1737
  /**
1719
1738
  * Converts an `enum` schema into an `EnumSchemaNode`.
1720
1739
  */
1721
- function convertEnum({ schema, name, nullable, type, rawOptions, parse }) {
1740
+ function convertEnum({ schema, name, nullable, type, parse }) {
1722
1741
  if (type === "array") return parse({
1723
1742
  schema: normalizeArrayEnum(schema),
1724
1743
  name
1725
- }, rawOptions);
1744
+ });
1726
1745
  const nullInEnum = schema.enum.includes(null);
1727
1746
  const filteredValues = nullInEnum ? schema.enum.filter((v) => v !== null) : schema.enum;
1728
1747
  if (nullInEnum && filteredValues.length === 0) return createNullNode(schema, name);
@@ -1774,13 +1793,9 @@ function convertEnum({ schema, name, nullable, type, rawOptions, parse }) {
1774
1793
  /**
1775
1794
  * Converts a `type: 'string'` schema into a `StringSchemaNode`.
1776
1795
  */
1777
- function convertString({ schema, name, nullable, defaultValue }) {
1778
- return createNode({
1779
- schema,
1780
- name,
1781
- nullable,
1782
- defaultValue
1783
- }, {
1796
+ function convertString(context) {
1797
+ const { schema } = context;
1798
+ return createNode(context, {
1784
1799
  type: "string",
1785
1800
  primitive: "string",
1786
1801
  min: schema.minLength,
@@ -1789,58 +1804,13 @@ function convertString({ schema, name, nullable, defaultValue }) {
1789
1804
  });
1790
1805
  }
1791
1806
  /**
1792
- * Reads the numeric constraints shared by every `number`, `integer` and `bigint` node. A `format`
1793
- * such as `double` or `int32` only narrows the type, so formatted numbers keep these too.
1794
- */
1795
- function getNumericConstraints(schema) {
1796
- return {
1797
- min: schema.minimum,
1798
- max: schema.maximum,
1799
- ...getExclusiveBounds(schema),
1800
- multipleOf: schema.multipleOf
1801
- };
1802
- }
1803
- /**
1804
1807
  * Converts a `type: 'number'` or `type: 'integer'` schema.
1805
1808
  */
1806
- function convertNumeric({ schema, name, nullable, defaultValue }, type) {
1807
- return createNode({
1808
- schema,
1809
- name,
1810
- nullable,
1811
- defaultValue
1812
- }, {
1809
+ function convertNumeric(context, type) {
1810
+ return createNode(context, {
1813
1811
  type,
1814
1812
  primitive: type,
1815
- ...getNumericConstraints(schema)
1816
- });
1817
- }
1818
- /**
1819
- * Converts a `type: 'boolean'` schema.
1820
- */
1821
- function convertBoolean({ schema, name, nullable, defaultValue }) {
1822
- return createNode({
1823
- schema,
1824
- name,
1825
- nullable,
1826
- defaultValue
1827
- }, {
1828
- type: "boolean",
1829
- primitive: "boolean"
1830
- });
1831
- }
1832
- /**
1833
- * Converts a raw binary schema into a `blob` node.
1834
- */
1835
- function convertBinary({ schema, name, nullable, defaultValue }) {
1836
- return createNode({
1837
- schema,
1838
- name,
1839
- nullable,
1840
- defaultValue
1841
- }, {
1842
- type: "blob",
1843
- primitive: "string"
1813
+ ...getNumericConstraints(context.schema)
1844
1814
  });
1845
1815
  }
1846
1816
  //#endregion
@@ -1849,9 +1819,9 @@ function convertBinary({ schema, name, nullable, defaultValue }) {
1849
1819
  * Resolves a `true` or empty-object map schema (`additionalProperties`/`patternProperties`) to
1850
1820
  * `options.unknownType`, otherwise parses it as a regular schema.
1851
1821
  */
1852
- function resolveMapSchema(mapSchema, options, parse, rawOptions) {
1853
- if (mapSchema === true || typeof mapSchema === "object" && Object.keys(mapSchema).length === 0) return _kubb_ast.ast.factory.createSchema({ type: options.unknownType });
1854
- return parse({ schema: mapSchema }, rawOptions);
1822
+ function resolveMapSchema(mapSchema, { options, parse }) {
1823
+ if (mapSchema === true || typeof mapSchema === "object" && Object.keys(mapSchema).length === 0) return _kubb_kit.ast.factory.createSchema({ type: options.unknownType });
1824
+ return parse({ schema: mapSchema });
1855
1825
  }
1856
1826
  /**
1857
1827
  * Names the inline enums on a property's schema, and on each item when the property is a tuple, from
@@ -1859,10 +1829,10 @@ function resolveMapSchema(mapSchema, options, parse, rawOptions) {
1859
1829
  */
1860
1830
  function nameEnums(node, options) {
1861
1831
  const macro = (0, _kubb_kit.macroEnumName)(options);
1862
- const named = _kubb_ast.ast.applyMacros(node, [macro], { depth: "shallow" });
1863
- const tupleNode = _kubb_ast.ast.narrowSchema(named, "tuple");
1832
+ const named = _kubb_kit.ast.applyMacros(node, [macro], { depth: "shallow" });
1833
+ const tupleNode = _kubb_kit.ast.narrowSchema(named, "tuple");
1864
1834
  if (tupleNode?.items) {
1865
- const namedItems = tupleNode.items.map((item) => _kubb_ast.ast.applyMacros(item, [macro], { depth: "shallow" }));
1835
+ const namedItems = tupleNode.items.map((item) => _kubb_kit.ast.applyMacros(item, [macro], { depth: "shallow" }));
1866
1836
  if (namedItems.some((item, i) => item !== tupleNode.items[i])) return {
1867
1837
  ...tupleNode,
1868
1838
  items: namedItems
@@ -1873,7 +1843,7 @@ function nameEnums(node, options) {
1873
1843
  /**
1874
1844
  * Converts an object-like schema into an `ObjectSchemaNode`.
1875
1845
  */
1876
- function convertObject({ schema, name, nullable, defaultValue, rawOptions, options, parse }) {
1846
+ function convertObject({ schema, name, nullable, defaultValue, options, parse }) {
1877
1847
  const properties = schema.properties ? Object.entries(schema.properties).map(([propName, propSchema]) => {
1878
1848
  const required = Array.isArray(schema.required) ? schema.required.includes(propName) : !!schema.required;
1879
1849
  const resolvedPropSchema = propSchema;
@@ -1881,12 +1851,12 @@ function convertObject({ schema, name, nullable, defaultValue, rawOptions, optio
1881
1851
  const schemaNode = nameEnums(parse({
1882
1852
  schema: resolvedPropSchema,
1883
1853
  name: (0, _kubb_kit.childName)(name, propName)
1884
- }, rawOptions), {
1854
+ }), {
1885
1855
  parentName: name,
1886
1856
  propName,
1887
1857
  enumSuffix: options.enumSuffix
1888
1858
  });
1889
- return _kubb_ast.ast.factory.createProperty({
1859
+ return _kubb_kit.ast.factory.createProperty({
1890
1860
  name: propName,
1891
1861
  schema: {
1892
1862
  ...schemaNode,
@@ -1898,11 +1868,17 @@ function convertObject({ schema, name, nullable, defaultValue, rawOptions, optio
1898
1868
  const additionalProperties = schema.additionalProperties;
1899
1869
  const additionalPropertiesNode = (() => {
1900
1870
  if (additionalProperties === true) return true;
1901
- if (additionalProperties) return resolveMapSchema(additionalProperties, options, parse, rawOptions);
1871
+ if (additionalProperties) return resolveMapSchema(additionalProperties, {
1872
+ options,
1873
+ parse
1874
+ });
1902
1875
  return additionalProperties;
1903
1876
  })();
1904
1877
  const rawPatternProperties = "patternProperties" in schema ? schema.patternProperties : void 0;
1905
- const patternProperties = rawPatternProperties ? Object.fromEntries(Object.entries(rawPatternProperties).map(([pattern, patternSchema]) => [pattern, resolveMapSchema(patternSchema, options, parse, rawOptions)])) : void 0;
1878
+ const patternProperties = rawPatternProperties ? Object.fromEntries(Object.entries(rawPatternProperties).map(([pattern, patternSchema]) => [pattern, resolveMapSchema(patternSchema, {
1879
+ options,
1880
+ parse
1881
+ })])) : void 0;
1906
1882
  const objectNode = createNode({
1907
1883
  schema,
1908
1884
  name,
@@ -1921,7 +1897,7 @@ function convertObject({ schema, name, nullable, defaultValue, rawOptions, optio
1921
1897
  const discPropName = schema.discriminator.propertyName;
1922
1898
  const values = Object.keys(schema.discriminator.mapping);
1923
1899
  const enumName = name ? (0, _kubb_kit.enumPropName)(name, discPropName, options.enumSuffix) : void 0;
1924
- return _kubb_ast.ast.applyMacros(objectNode, [(0, _kubb_kit.macroDiscriminatorEnum)({
1900
+ return _kubb_kit.ast.applyMacros(objectNode, [(0, _kubb_kit.macroDiscriminatorEnum)({
1925
1901
  propertyName: discPropName,
1926
1902
  values,
1927
1903
  enumName
@@ -1932,9 +1908,9 @@ function convertObject({ schema, name, nullable, defaultValue, rawOptions, optio
1932
1908
  /**
1933
1909
  * Converts an OAS 3.1 `prefixItems` tuple into a `TupleSchemaNode`.
1934
1910
  */
1935
- function convertTuple({ schema, name, nullable, defaultValue, rawOptions, options, parse }) {
1936
- const tupleItems = (schema.prefixItems ?? []).map((item) => parse({ schema: item }, rawOptions));
1937
- const rest = schema.items === false ? void 0 : !schema.items || schema.items === true ? _kubb_ast.ast.factory.createSchema({ type: options.unknownType }) : parse({ schema: schema.items }, rawOptions);
1911
+ function convertTuple({ schema, name, nullable, defaultValue, options, parse }) {
1912
+ const tupleItems = (schema.prefixItems ?? []).map((item) => parse({ schema: item }));
1913
+ const rest = schema.items === false ? void 0 : !schema.items || schema.items === true ? _kubb_kit.ast.factory.createSchema({ type: options.unknownType }) : parse({ schema: schema.items });
1938
1914
  return createNode({
1939
1915
  schema,
1940
1916
  name,
@@ -1952,13 +1928,13 @@ function convertTuple({ schema, name, nullable, defaultValue, rawOptions, option
1952
1928
  /**
1953
1929
  * Converts a `type: 'array'` schema into an `ArraySchemaNode`.
1954
1930
  */
1955
- function convertArray({ schema, name, nullable, defaultValue, rawOptions, options, parse }) {
1931
+ function convertArray({ schema, name, nullable, defaultValue, options, parse }) {
1956
1932
  const rawItems = schema.items;
1957
1933
  const itemName = rawItems?.enum?.length && name ? (0, _kubb_kit.enumPropName)(null, name, options.enumSuffix) : name;
1958
1934
  const items = rawItems ? [parse({
1959
1935
  schema: rawItems,
1960
1936
  name: itemName
1961
- }, rawOptions)] : [];
1937
+ })] : [];
1962
1938
  return createNode({
1963
1939
  schema,
1964
1940
  name,
@@ -2017,7 +1993,10 @@ const schemaRules = [
2017
1993
  },
2018
1994
  {
2019
1995
  match: ({ schema }) => isBinary(schema),
2020
- convert: convertBinary
1996
+ convert: (ctx) => createNode(ctx, {
1997
+ type: "blob",
1998
+ primitive: "string"
1999
+ })
2021
2000
  },
2022
2001
  {
2023
2002
  match: ({ schema, type }) => !type && (schema.minLength !== void 0 || schema.maxLength !== void 0 || schema.pattern !== void 0),
@@ -2057,7 +2036,10 @@ const schemaRules = [
2057
2036
  },
2058
2037
  {
2059
2038
  match: ({ type }) => type === "boolean",
2060
- convert: convertBoolean
2039
+ convert: (ctx) => createNode(ctx, {
2040
+ type: "boolean",
2041
+ primitive: "boolean"
2042
+ })
2061
2043
  },
2062
2044
  {
2063
2045
  match: ({ type }) => type === "null",
@@ -2065,95 +2047,94 @@ const schemaRules = [
2065
2047
  }
2066
2048
  ];
2067
2049
  //#endregion
2068
- //#region src/refs.ts
2069
- const _refCache = /* @__PURE__ */ new WeakMap();
2070
- /**
2071
- * Walks a local `#/...` JSON pointer against `document`, memoized per document. `applicable` is
2072
- * `false` for an empty or non-local ref (the caller should not treat that as a failed lookup).
2073
- * Shared by `resolveRef`'s reporting walk and `createRefs().resolve`'s silent walk, so both use
2074
- * the same trimming and caching instead of two separate implementations.
2075
- */
2076
- function walkPointer(document, $ref) {
2077
- const trimmed = $ref.trim();
2078
- if (trimmed === "" || !trimmed.startsWith("#")) return {
2079
- applicable: false,
2080
- value: null
2050
+ //#region src/parser.ts
2051
+ /**
2052
+ * Creates the schema converter bound to one OpenAPI document: `parseSchema` dispatches each
2053
+ * schema through the ordered `schemaRules` table, with the parser options merged once.
2054
+ *
2055
+ * @internal
2056
+ */
2057
+ function createSchemaParser(ctx) {
2058
+ const { document, refs, renames } = ctx;
2059
+ const options = {
2060
+ ...DEFAULT_PARSER_OPTIONS,
2061
+ ...ctx.options
2081
2062
  };
2082
- const pointer = trimmed.substring(1);
2083
- let docCache = _refCache.get(document);
2084
- if (!docCache) {
2085
- docCache = /* @__PURE__ */ new Map();
2086
- _refCache.set(document, docCache);
2063
+ function parseSchema({ schema, name }) {
2064
+ const flattenedSchema = flattenSchema(schema);
2065
+ if (flattenedSchema !== schema) return parseSchema({
2066
+ schema: flattenedSchema,
2067
+ name
2068
+ });
2069
+ const nullable = isNullable(schema) || void 0;
2070
+ const context = {
2071
+ schema,
2072
+ name,
2073
+ nullable,
2074
+ defaultValue: schema.default === null && nullable ? void 0 : schema.default,
2075
+ type: Array.isArray(schema.type) ? schema.type.find((t) => t !== "null") ?? schema.type[0] : schema.type,
2076
+ options,
2077
+ parse: parseSchema,
2078
+ document,
2079
+ refs,
2080
+ renames
2081
+ };
2082
+ for (const rule of schemaRules) if (rule.match(context)) return rule.convert(context);
2083
+ return createNode(context, { type: options.emptySchemaType });
2087
2084
  }
2088
- if (docCache.has(pointer)) return {
2089
- applicable: true,
2090
- value: docCache.get(pointer)
2091
- };
2092
- const current = pointer.split("/").filter(Boolean).map((token) => globalThis.decodeURIComponent(token).replaceAll("~1", "/").replaceAll("~0", "~")).reduce((obj, key) => obj?.[key], document);
2093
- if (current) docCache.set(pointer, current);
2094
- return {
2095
- applicable: true,
2096
- value: current ?? null
2097
- };
2085
+ return { parseSchema };
2098
2086
  }
2087
+ //#endregion
2088
+ //#region src/promoteEnums.ts
2099
2089
  /**
2100
- * Resolves a local JSON pointer reference from a document.
2101
- *
2102
- * Accepts `#/...` refs. Returns `null` for an empty or non-local ref. When the pointer cannot be
2103
- * resolved, reports a `refNotFound` diagnostic into the active build and returns `null`. Outside a
2104
- * build there is no sink to collect it, so it throws instead.
2105
- *
2106
- * @example
2107
- * ```ts
2108
- * resolveRef<SchemaObject>(document, '#/components/schemas/Pet')
2109
- * ```
2090
+ * Collects inline enums to lift to the top level, keyed by the name the parser derived for them
2091
+ * (e.g. `PetStatusEnum`). An enum already defined as a top-level component is left as-is, and a
2092
+ * name that recurs maps to the first definition so each name yields one shared type.
2110
2093
  */
2111
- function resolveRef(document, $ref) {
2112
- const { applicable, value } = walkPointer(document, $ref);
2113
- if (!applicable) return null;
2114
- if (value) return value;
2115
- const diagnostic = {
2116
- code: _kubb_core.Diagnostics.code.refNotFound,
2117
- severity: "error",
2118
- message: `Could not find a definition for ${$ref}.`,
2119
- help: "Add the schema under `components.schemas`, or fix the `$ref`. Run `kubb validate` to check the spec.",
2120
- location: {
2121
- kind: "schema",
2122
- pointer: $ref,
2123
- ref: $ref
2094
+ function collectInlineEnums(roots, topLevelNames) {
2095
+ const promoted = /* @__PURE__ */ new Map();
2096
+ for (const root of roots) {
2097
+ const isSchemaRoot = root.kind === "Schema";
2098
+ for (const node of _kubb_kit.ast.collect(root, { schema: (schemaNode) => schemaNode })) {
2099
+ if (node.type !== "enum" || !node.name) continue;
2100
+ if (isSchemaRoot && node === root) continue;
2101
+ if (topLevelNames.has(node.name)) continue;
2102
+ if (!promoted.has(node.name)) promoted.set(node.name, {
2103
+ ...node,
2104
+ optional: void 0,
2105
+ nullish: void 0
2106
+ });
2124
2107
  }
2125
- };
2126
- if (!_kubb_core.Diagnostics.report(diagnostic)) throw new _kubb_core.Diagnostics.Error(diagnostic);
2127
- return null;
2108
+ }
2109
+ return promoted;
2128
2110
  }
2129
2111
  /**
2130
- * Resolves a `$ref` object while preserving the original `$ref` field on the result.
2131
- *
2132
- * Useful for parser flows that need both dereferenced fields and pointer
2133
- * identity (for naming/import purposes). Non-reference values are returned as-is.
2134
- *
2135
- * @example
2136
- * ```ts
2137
- * dereferenceWithRef(document, { $ref: '#/components/schemas/Pet' })
2138
- * // { $ref: '#/components/schemas/Pet', type: 'object', properties: { ... } }
2139
- * ```
2112
+ * Replaces every promoted inline enum in `node` with a `ref` to its lifted definition, keeping the
2113
+ * occurrence's usage-slot and documentation fields.
2140
2114
  */
2141
- function dereferenceWithRef(document, schema) {
2142
- if (isReference(schema)) return {
2143
- ...schema,
2144
- ...resolveRef(document, schema.$ref),
2145
- $ref: schema.$ref
2146
- };
2147
- return schema;
2115
+ function refPromotedEnums(node, promoted) {
2116
+ if (promoted.size === 0) return node;
2117
+ return _kubb_kit.ast.transform(node, { schema(schemaNode) {
2118
+ if (schemaNode.type !== "enum" || !schemaNode.name || !promoted.has(schemaNode.name)) return void 0;
2119
+ return _kubb_kit.ast.factory.createSchema({
2120
+ type: "ref",
2121
+ name: schemaNode.name,
2122
+ ref: `${SCHEMA_REF_PREFIX}${schemaNode.name}`,
2123
+ optional: schemaNode.optional,
2124
+ nullish: schemaNode.nullish,
2125
+ readOnly: schemaNode.readOnly,
2126
+ writeOnly: schemaNode.writeOnly,
2127
+ deprecated: schemaNode.deprecated,
2128
+ description: schemaNode.description,
2129
+ default: schemaNode.default,
2130
+ examples: schemaNode.examples
2131
+ });
2132
+ } });
2148
2133
  }
2134
+ //#endregion
2135
+ //#region src/refs.ts
2149
2136
  /**
2150
- * Creates the `$ref` resolution service for one document.
2151
- *
2152
- * Replaces what used to be six overlapping resolvers (a reporting walk, a silent walk, an
2153
- * existence check, and a resolve-then-parse-into-a-node step, each with its own cache) with one
2154
- * pointer walk and one explicit `report` contract for a missing ref: `report: true` (the default)
2155
- * reports a `refNotFound` diagnostic (or throws outside a build), `report: false` resolves to
2156
- * `null` silently for a speculative lookup.
2137
+ * Creates the `$ref` resolution service for one document. One pointer walk backs every method.
2157
2138
  *
2158
2139
  * @example
2159
2140
  * ```ts
@@ -2163,12 +2144,31 @@ function dereferenceWithRef(document, schema) {
2163
2144
  * refs.exists('#/components/schemas/Pet')
2164
2145
  * refs.resolveNode('#/components/schemas/Pet', parseSchema)
2165
2146
  * refs.deref<ResponseObject>(operation.schema.responses?.['200'])
2147
+ * refs.derefKeepingRef(parameter)
2166
2148
  * ```
2167
2149
  */
2168
2150
  function createRefs(document) {
2151
+ const pointerCache = /* @__PURE__ */ new Map();
2169
2152
  const resolvedNodeCache = /* @__PURE__ */ new Map();
2170
- const existenceCache = /* @__PURE__ */ new Map();
2171
2153
  const resolvingRefs = /* @__PURE__ */ new Set();
2154
+ function walkPointer($ref) {
2155
+ const trimmed = $ref.trim();
2156
+ if (trimmed === "" || !trimmed.startsWith("#")) return {
2157
+ applicable: false,
2158
+ value: null
2159
+ };
2160
+ const pointer = trimmed.substring(1);
2161
+ if (pointerCache.has(pointer)) return {
2162
+ applicable: true,
2163
+ value: pointerCache.get(pointer)
2164
+ };
2165
+ const current = pointer.split("/").filter(Boolean).map((token) => globalThis.decodeURIComponent(token).replaceAll("~1", "/").replaceAll("~0", "~")).reduce((obj, key) => obj?.[key], document);
2166
+ if (current) pointerCache.set(pointer, current);
2167
+ return {
2168
+ applicable: true,
2169
+ value: current ?? null
2170
+ };
2171
+ }
2172
2172
  /**
2173
2173
  * Resolves a local `#/...` JSON pointer. Returns `null` for an empty or non-local ref.
2174
2174
  * `report: true` (default) reports a `refNotFound` diagnostic into the active build (or throws
@@ -2176,27 +2176,36 @@ function createRefs(document) {
2176
2176
  * for a speculative lookup where a missing ref is not an error.
2177
2177
  */
2178
2178
  function resolve(refPath, options) {
2179
- if (options?.report === false) {
2180
- const { applicable, value } = walkPointer(document, refPath);
2181
- return applicable ? value : null;
2182
- }
2183
- return resolveRef(document, refPath);
2179
+ const { applicable, value } = walkPointer(refPath);
2180
+ if (!applicable || value || options?.report === false) return value;
2181
+ const diagnostic = {
2182
+ code: _kubb_kit.Diagnostics.code.refNotFound,
2183
+ severity: "error",
2184
+ message: `Could not find a definition for ${refPath}.`,
2185
+ help: "Add the schema under `components.schemas`, or fix the `$ref`. Run `kubb validate` to check the spec.",
2186
+ location: {
2187
+ kind: "schema",
2188
+ pointer: refPath,
2189
+ ref: refPath
2190
+ }
2191
+ };
2192
+ if (!_kubb_kit.Diagnostics.report(diagnostic)) throw new _kubb_kit.Diagnostics.Error(diagnostic);
2193
+ return null;
2184
2194
  }
2185
2195
  /**
2186
2196
  * Returns `true` when a `$ref` path resolves to a component the document actually defines.
2187
2197
  * A circular ref still resolves to an existing target, so this stays `true` for cycles and only
2188
- * goes `false` for a `$ref` that points at a component the spec never declares. Memoized.
2198
+ * goes `false` for a `$ref` that points at a component the spec never declares.
2189
2199
  */
2190
2200
  function exists(refPath) {
2191
- if (!existenceCache.has(refPath)) existenceCache.set(refPath, !!resolve(refPath, { report: false }));
2192
- return existenceCache.get(refPath) ?? false;
2201
+ return !!resolve(refPath, { report: false });
2193
2202
  }
2194
2203
  /**
2195
2204
  * Resolves a `$ref` to its parsed node via `parse`, guarding against cycles and memoizing per
2196
2205
  * instance. Returns `null` when the ref is currently being resolved (a cycle) or cannot be
2197
2206
  * resolved (e.g. a minimal document in a unit test).
2198
2207
  */
2199
- function resolveNode(refPath, parse, rawOptions) {
2208
+ function resolveNode(refPath, parse) {
2200
2209
  if (resolvingRefs.has(refPath)) return null;
2201
2210
  if (!resolvedNodeCache.has(refPath)) {
2202
2211
  let resolved = null;
@@ -2204,7 +2213,7 @@ function createRefs(document) {
2204
2213
  const referenced = resolve(refPath);
2205
2214
  if (referenced) {
2206
2215
  resolvingRefs.add(refPath);
2207
- resolved = parse({ schema: referenced }, rawOptions);
2216
+ resolved = parse({ schema: referenced });
2208
2217
  resolvingRefs.delete(refPath);
2209
2218
  }
2210
2219
  } catch {}
@@ -2217,393 +2226,30 @@ function createRefs(document) {
2217
2226
  * resolved target. Returns `null` when the value is empty, cannot be resolved, or is still a
2218
2227
  * `$ref` after resolving (e.g. a document with no component registry). A non-`$ref` value is
2219
2228
  * returned as-is.
2220
- *
2221
- * @example
2222
- * ```ts
2223
- * refs.deref<ResponseObject>(operation.schema.responses?.['200'])
2224
- * ```
2225
2229
  */
2226
2230
  function deref(value) {
2227
2231
  if (!isReference(value)) return value ? value : null;
2228
2232
  const resolved = resolve(value.$ref);
2229
2233
  return resolved && !isReference(resolved) ? resolved : null;
2230
2234
  }
2235
+ /** Resolves a `$ref` object but keeps the `$ref` field on the result; other values pass through. */
2236
+ function derefKeepingRef(value) {
2237
+ if (isReference(value)) return {
2238
+ ...value,
2239
+ ...resolve(value.$ref),
2240
+ $ref: value.$ref
2241
+ };
2242
+ return value;
2243
+ }
2231
2244
  return {
2232
2245
  resolve,
2233
2246
  exists,
2234
2247
  resolveNode,
2235
- deref
2248
+ deref,
2249
+ derefKeepingRef
2236
2250
  };
2237
2251
  }
2238
2252
  //#endregion
2239
- //#region src/model/operations.ts
2240
- /**
2241
- * Returns all parameters for an operation, merging path-level and operation-level entries.
2242
- * Operation-level parameters override path-level ones with the same `in:name` key.
2243
- * Each `$ref` parameter is dereferenced via `dereferenceWithRef` before merging.
2244
- *
2245
- * @example
2246
- * ```ts
2247
- * getParameters({ document, operation })
2248
- * // [{ name: 'petId', in: 'path', required: true, schema: { type: 'integer' } }]
2249
- * ```
2250
- */
2251
- function getParameters({ document, operation }) {
2252
- const resolveParams = (params) => params.map((p) => dereferenceWithRef(document, p)).filter((p) => !!p && typeof p === "object" && "in" in p && "name" in p);
2253
- const operationParams = resolveParams(operation.schema?.parameters || []);
2254
- const pathLevelParams = resolveParams(operation.pathItem.parameters ?? []);
2255
- const paramMap = /* @__PURE__ */ new Map();
2256
- for (const p of pathLevelParams) if (p.name && p.in) paramMap.set(`${p.in}:${p.name}`, p);
2257
- for (const p of operationParams) if (p.name && p.in) paramMap.set(`${p.in}:${p.name}`, p);
2258
- return Array.from(paramMap.values());
2259
- }
2260
- function getResponseBody(responseBody, contentType) {
2261
- if (!responseBody) return false;
2262
- if (isReference(responseBody)) return false;
2263
- const body = responseBody;
2264
- if (!body.content) return false;
2265
- if (contentType) return contentType in body.content ? [contentType, body.content[contentType]] : false;
2266
- return pickContentEntry(body.content);
2267
- }
2268
- /**
2269
- * Returns the response schema for a given operation and HTTP status code.
2270
- *
2271
- * Returns an empty object `{}` when no response body schema is available.
2272
- *
2273
- * @example
2274
- * ```ts
2275
- * getResponseSchema({ document, operation, refs, statusCode: 200 }) // SchemaObject
2276
- * getResponseSchema({ document, operation, refs, statusCode: '4XX' }) // {}
2277
- * ```
2278
- */
2279
- function getResponseSchema({ document, operation, refs, statusCode, options = {} }) {
2280
- const responseBody = getResponseBody(getResponseByStatusCode({
2281
- operation,
2282
- refs,
2283
- statusCode
2284
- }), options.contentType);
2285
- if (responseBody === false) return {};
2286
- const [mediaType, entry] = responseBody;
2287
- const schema = entry.schema;
2288
- const binary = getBinaryFallbackSchema(mediaType, schema);
2289
- if (binary) return binary;
2290
- if (!schema) return {};
2291
- return dereferenceWithRef(document, schema);
2292
- }
2293
- /**
2294
- * Returns the request body schema for an operation, or `null` when absent.
2295
- *
2296
- * @example
2297
- * ```ts
2298
- * getRequestSchema({ document, operation, refs }) // SchemaObject | null
2299
- * ```
2300
- */
2301
- function getRequestSchema({ document, operation, refs, options = {} }) {
2302
- const requestBody = getRequestContent({
2303
- operation,
2304
- refs,
2305
- mediaType: options.contentType
2306
- });
2307
- if (requestBody === false) return null;
2308
- const mediaType = Array.isArray(requestBody) ? requestBody[0] : options.contentType;
2309
- const schema = Array.isArray(requestBody) ? requestBody[1].schema : requestBody.schema;
2310
- const binary = getBinaryFallbackSchema(mediaType, schema);
2311
- if (binary) return binary;
2312
- if (!schema) return null;
2313
- return dereferenceWithRef(document, schema);
2314
- }
2315
- /**
2316
- * Returns all request body content type keys for an operation, resolving a `$ref` requestBody
2317
- * through `refs`.
2318
- *
2319
- * @example
2320
- * ```ts
2321
- * getRequestBodyContentTypes(operation, refs)
2322
- * // ['application/json', 'multipart/form-data']
2323
- * ```
2324
- */
2325
- function getRequestBodyContentTypes(operation, refs) {
2326
- const body = getRequestBody({
2327
- operation,
2328
- refs
2329
- });
2330
- return body?.content ? Object.keys(body.content) : [];
2331
- }
2332
- /**
2333
- * Returns all response content type keys for an operation at a given status code, resolving the
2334
- * response `$ref` through `refs`.
2335
- *
2336
- * @example
2337
- * ```ts
2338
- * getResponseBodyContentTypes(operation, refs, 200)
2339
- * // ['application/json', 'application/xml']
2340
- * ```
2341
- */
2342
- function getResponseBodyContentTypes(operation, refs, statusCode) {
2343
- const responseObj = getResponseByStatusCode({
2344
- operation,
2345
- refs,
2346
- statusCode
2347
- });
2348
- if (!responseObj || typeof responseObj !== "object" || isReference(responseObj)) return [];
2349
- const body = responseObj;
2350
- return body.content ? Object.keys(body.content) : [];
2351
- }
2352
- //#endregion
2353
- //#region src/parser.ts
2354
- /**
2355
- * Creates the schema and operation converters bound to one OpenAPI document.
2356
- *
2357
- * Takes the `$ref` service for this document (shared with the rest of the pipeline, see
2358
- * `adapter.ts`) and owns the `parseSchema` recursion seam, then dispatches each schema through
2359
- * the ordered `schemaRules` table from `emit/parseSchema.ts`. Every converter is a standalone
2360
- * function that recurses through the `parse` function passed to it, so this file only wires
2361
- * state to the converters.
2362
- *
2363
- * @internal
2364
- */
2365
- function createSchemaParser(ctx) {
2366
- const document = ctx.document;
2367
- const refs = ctx.refs;
2368
- /**
2369
- * Converts an OAS `SchemaObject` into a `SchemaNode`.
2370
- *
2371
- * Builds the per-schema context, then walks the ordered {@link schemaRules} table and returns
2372
- * the first converter that produces a node. When none match, falls back to the configured
2373
- * `emptySchemaType`.
2374
- */
2375
- function parseSchema({ schema, name }, rawOptions) {
2376
- const options = {
2377
- ...DEFAULT_PARSER_OPTIONS,
2378
- ...rawOptions
2379
- };
2380
- const flattenedSchema = flattenSchema(schema);
2381
- if (flattenedSchema && flattenedSchema !== schema) return parseSchema({
2382
- schema: flattenedSchema,
2383
- name
2384
- }, rawOptions);
2385
- const nullable = isNullable(schema) || void 0;
2386
- const context = {
2387
- schema,
2388
- name,
2389
- nullable,
2390
- defaultValue: schema.default === null && nullable ? void 0 : schema.default,
2391
- type: Array.isArray(schema.type) ? schema.type.find((t) => t !== "null") ?? schema.type[0] : schema.type,
2392
- rawOptions,
2393
- options,
2394
- parse: parseSchema,
2395
- document,
2396
- refs,
2397
- renames: ctx.renames
2398
- };
2399
- for (const rule of schemaRules) if (rule.match(context)) return rule.convert(context);
2400
- const emptyType = options.emptySchemaType;
2401
- return createNode(context, { type: emptyType });
2402
- }
2403
- /**
2404
- * Converts a dereferenced OAS parameter object into a `ParameterNode`.
2405
- */
2406
- function parseParameter(options, param, parentName) {
2407
- const required = param["required"] ?? false;
2408
- const paramName = param["name"];
2409
- const schemaName = parentName && paramName ? pascalCase(`${parentName} ${paramName}`) : void 0;
2410
- const schema = param["schema"] ? parseSchema({
2411
- schema: param["schema"],
2412
- name: schemaName
2413
- }, options) : _kubb_ast.ast.factory.createSchema({ type: options.unknownType });
2414
- const style = param["style"];
2415
- const explode = param["explode"];
2416
- return _kubb_ast.ast.factory.createParameter({
2417
- name: paramName,
2418
- in: param["in"],
2419
- schema: {
2420
- ...schema,
2421
- description: param["description"] ?? schema.description
2422
- },
2423
- required,
2424
- ...style !== void 0 ? { style } : {},
2425
- ...explode !== void 0 ? { explode } : {}
2426
- });
2427
- }
2428
- /**
2429
- * Reads the inline `requestBody` metadata (description / required) that OAS exposes
2430
- * outside the schema itself, resolving a `$ref` requestBody through `refs`. Returns an
2431
- * empty object when the request body is missing or cannot be resolved.
2432
- */
2433
- function getRequestBodyMeta(operation) {
2434
- const body = getRequestBody({
2435
- operation,
2436
- refs
2437
- });
2438
- if (!body) return { required: false };
2439
- return {
2440
- description: body.description,
2441
- required: body.required === true
2442
- };
2443
- }
2444
- /**
2445
- * Collects property names whose schema has a truthy boolean flag (`readOnly` or `writeOnly`).
2446
- * `$ref` entries are skipped since their flags live on the dereferenced target.
2447
- */
2448
- function collectPropertyKeysByFlag(schema, flag) {
2449
- if (!schema?.properties) return null;
2450
- const keys = [];
2451
- for (const key in schema.properties) {
2452
- const prop = schema.properties[key];
2453
- if (prop && !isReference(prop) && prop[flag]) keys.push(key);
2454
- }
2455
- return keys.length ? keys : null;
2456
- }
2457
- /**
2458
- * Converts an OAS `Operation` into an `OperationNode`.
2459
- */
2460
- function parseOperation(options, operation) {
2461
- const operationId = getOperationId(operation);
2462
- const operationName = operationId ? pascalCase(operationId) : void 0;
2463
- const parameters = getParameters({
2464
- document,
2465
- operation
2466
- }).map((param) => parseParameter(options, param, operationName));
2467
- const allContentTypes = ctx.contentType ? [ctx.contentType] : getRequestBodyContentTypes(operation, refs);
2468
- const requestBodyMeta = getRequestBodyMeta(operation);
2469
- const requestBodyName = operationName ? `${operationName}Request` : void 0;
2470
- const content = allContentTypes.flatMap((ct) => {
2471
- const schema = getRequestSchema({
2472
- document,
2473
- operation,
2474
- refs,
2475
- options: { contentType: ct }
2476
- });
2477
- if (!schema) return [];
2478
- return [_kubb_ast.ast.factory.createContent({
2479
- contentType: ct,
2480
- schema: _kubb_ast.ast.optionality(parseSchema({
2481
- schema,
2482
- name: requestBodyName
2483
- }, options), requestBodyMeta.required),
2484
- keysToOmit: collectPropertyKeysByFlag(schema, "readOnly")
2485
- })];
2486
- });
2487
- const requestBody = content.length > 0 || requestBodyMeta.description ? {
2488
- description: requestBodyMeta.description,
2489
- required: requestBodyMeta.required || void 0,
2490
- content: content.length > 0 ? content : void 0
2491
- } : void 0;
2492
- const responses = getResponseStatusCodes(operation).map((statusCode) => {
2493
- const responseObj = getResponseByStatusCode({
2494
- operation,
2495
- refs,
2496
- statusCode
2497
- });
2498
- const responseName = operationName ? `${operationName}Status${statusCode}` : void 0;
2499
- const description = typeof responseObj === "object" && responseObj !== null ? responseObj.description : void 0;
2500
- const parseEntrySchema = (contentType) => {
2501
- const raw = getResponseSchema({
2502
- document,
2503
- operation,
2504
- refs,
2505
- statusCode,
2506
- options: { contentType }
2507
- });
2508
- return {
2509
- schema: raw && Object.keys(raw).length > 0 ? parseSchema({
2510
- schema: raw,
2511
- name: responseName
2512
- }, options) : _kubb_ast.ast.factory.createSchema({ type: options.emptySchemaType }),
2513
- keysToOmit: collectPropertyKeysByFlag(raw, "writeOnly")
2514
- };
2515
- };
2516
- const content = (ctx.contentType ? [ctx.contentType] : getResponseBodyContentTypes(operation, refs, statusCode)).map((contentType) => _kubb_ast.ast.factory.createContent({
2517
- contentType,
2518
- ...parseEntrySchema(contentType)
2519
- }));
2520
- if (content.length === 0) content.push(_kubb_ast.ast.factory.createContent({
2521
- contentType: getRequestContentType({
2522
- operation,
2523
- refs
2524
- }) || "application/json",
2525
- ...parseEntrySchema(ctx.contentType)
2526
- }));
2527
- return _kubb_ast.ast.factory.createResponse({
2528
- statusCode,
2529
- description,
2530
- content
2531
- });
2532
- });
2533
- const pickDoc = (key) => {
2534
- const own = operation.schema[key];
2535
- if (typeof own === "string") return own;
2536
- const fallback = operation.pathItem[key];
2537
- return typeof fallback === "string" ? fallback : void 0;
2538
- };
2539
- return _kubb_ast.ast.factory.createOperation({
2540
- operationId,
2541
- protocol: "http",
2542
- method: operation.method.toUpperCase(),
2543
- path: operation.path,
2544
- tags: Array.isArray(operation.schema.tags) ? operation.schema.tags.map(String) : [],
2545
- summary: pickDoc("summary") || void 0,
2546
- description: pickDoc("description") || void 0,
2547
- deprecated: operation.schema.deprecated || void 0,
2548
- parameters,
2549
- requestBody,
2550
- responses
2551
- });
2552
- }
2553
- return {
2554
- parseSchema,
2555
- parseOperation,
2556
- parseParameter
2557
- };
2558
- }
2559
- //#endregion
2560
- //#region src/promoteEnums.ts
2561
- /**
2562
- * Collects inline enums to lift to the top level, keyed by the name the parser derived for them
2563
- * (e.g. `PetStatusEnum`). An enum already defined as a top-level component is left as-is, and a
2564
- * name that recurs maps to the first definition so each name yields one shared type.
2565
- */
2566
- function collectInlineEnums(roots, topLevelNames) {
2567
- const promoted = /* @__PURE__ */ new Map();
2568
- for (const root of roots) {
2569
- const isSchemaRoot = root.kind === "Schema";
2570
- for (const node of _kubb_ast.ast.collect(root, { schema: (schemaNode) => schemaNode })) {
2571
- if (node.type !== "enum" || !node.name) continue;
2572
- if (isSchemaRoot && node === root) continue;
2573
- if (topLevelNames.has(node.name)) continue;
2574
- if (!promoted.has(node.name)) promoted.set(node.name, {
2575
- ...node,
2576
- optional: void 0,
2577
- nullish: void 0
2578
- });
2579
- }
2580
- }
2581
- return promoted;
2582
- }
2583
- /**
2584
- * Replaces every promoted inline enum in `node` with a `ref` to its lifted definition, keeping the
2585
- * occurrence's usage-slot and documentation fields.
2586
- */
2587
- function refPromotedEnums(node, promoted) {
2588
- if (promoted.size === 0) return node;
2589
- return _kubb_ast.ast.transform(node, { schema(schemaNode) {
2590
- if (schemaNode.type !== "enum" || !schemaNode.name || !promoted.has(schemaNode.name)) return void 0;
2591
- return _kubb_ast.ast.factory.createSchema({
2592
- type: "ref",
2593
- name: schemaNode.name,
2594
- ref: `${SCHEMA_REF_PREFIX}${schemaNode.name}`,
2595
- optional: schemaNode.optional,
2596
- nullish: schemaNode.nullish,
2597
- readOnly: schemaNode.readOnly,
2598
- writeOnly: schemaNode.writeOnly,
2599
- deprecated: schemaNode.deprecated,
2600
- description: schemaNode.description,
2601
- default: schemaNode.default,
2602
- examples: schemaNode.examples
2603
- });
2604
- } });
2605
- }
2606
- //#endregion
2607
2253
  //#region src/schemaDiagnostics.ts
2608
2254
  /**
2609
2255
  * Scans one freshly converted top-level schema in a single walk, so the post-convert pass never
@@ -2630,11 +2276,11 @@ function escapePointerToken(token) {
2630
2276
  }
2631
2277
  function visit(node, pointer, refs) {
2632
2278
  if (node.type === "ref") {
2633
- const refName = (0, _kubb_ast.resolveRefName)(node);
2279
+ const refName = _kubb_kit.ast.resolveRefName(node);
2634
2280
  if (refName) refs.add(refName);
2635
2281
  }
2636
- if (node.deprecated) _kubb_core.Diagnostics.report({
2637
- code: _kubb_core.Diagnostics.code.deprecated,
2282
+ if (node.deprecated) _kubb_kit.Diagnostics.report({
2283
+ code: _kubb_kit.Diagnostics.code.deprecated,
2638
2284
  severity: "info",
2639
2285
  message: "This schema is marked as deprecated.",
2640
2286
  location: {
@@ -2642,8 +2288,8 @@ function visit(node, pointer, refs) {
2642
2288
  pointer
2643
2289
  }
2644
2290
  });
2645
- if (typeof node.format === "string" && !isHandledFormat(node.format)) _kubb_core.Diagnostics.report({
2646
- code: _kubb_core.Diagnostics.code.unsupportedFormat,
2291
+ if (typeof node.format === "string" && !isHandledFormat(node.format)) _kubb_kit.Diagnostics.report({
2292
+ code: _kubb_kit.Diagnostics.code.unsupportedFormat,
2647
2293
  severity: "warning",
2648
2294
  message: `Kubb does not map the format "${node.format}" to a specific type, so it falls back to the base type.`,
2649
2295
  help: `Use a format Kubb supports, or handle "${node.format}" with a custom parser or plugin.`,
@@ -2700,20 +2346,27 @@ const adapterOasName = "oas";
2700
2346
  * })
2701
2347
  * ```
2702
2348
  */
2703
- const adapterOas = (0, _kubb_core.createAdapter)((options) => {
2349
+ const adapterOas = (0, _kubb_kit.createAdapter)((options) => {
2704
2350
  const { validate = true, contentType, server, discriminator = "preserve", enums = "inline", dateType = DEFAULT_PARSER_OPTIONS.dateType, integerType = DEFAULT_PARSER_OPTIONS.integerType, unknownType = DEFAULT_PARSER_OPTIONS.unknownType, enumSuffix = DEFAULT_PARSER_OPTIONS.enumSuffix, emptySchemaType = unknownType || DEFAULT_PARSER_OPTIONS.emptySchemaType } = options;
2705
2351
  const parserOptions = {
2706
- ...DEFAULT_PARSER_OPTIONS,
2707
2352
  dateType,
2708
2353
  integerType,
2709
2354
  unknownType,
2710
2355
  emptySchemaType,
2711
2356
  enumSuffix
2712
2357
  };
2358
+ const resolvedOptions = {
2359
+ validate,
2360
+ contentType,
2361
+ server,
2362
+ discriminator,
2363
+ enums,
2364
+ ...parserOptions
2365
+ };
2713
2366
  let parsedDocument = null;
2714
2367
  const inputCache = /* @__PURE__ */ new WeakMap();
2715
2368
  function parseInput({ document, refs, schemas, parser }) {
2716
- const { parseSchema, parseOperation } = parser;
2369
+ const { parseSchema } = parser;
2717
2370
  const parsedByName = /* @__PURE__ */ new Map();
2718
2371
  const refAliasMap = /* @__PURE__ */ new Map();
2719
2372
  const enumNames = [];
@@ -2723,7 +2376,7 @@ const adapterOas = (0, _kubb_core.createAdapter)((options) => {
2723
2376
  const node = parseSchema({
2724
2377
  schema,
2725
2378
  name
2726
- }, parserOptions);
2379
+ });
2727
2380
  parsedByName.set(name, node);
2728
2381
  const refs = scanSchema({
2729
2382
  node,
@@ -2731,33 +2384,35 @@ const adapterOas = (0, _kubb_core.createAdapter)((options) => {
2731
2384
  });
2732
2385
  if (node.name) refGraph.set(node.name, refs);
2733
2386
  if (node.type === "ref" && node.name && node.name !== name) refAliasMap.set(name, node);
2734
- if ((0, _kubb_ast.narrowSchema)(node, "enum") && node.name) enumNames.push(node.name);
2387
+ if (_kubb_kit.ast.narrowSchema(node, "enum") && node.name) enumNames.push(node.name);
2735
2388
  if (discriminator === "propagate" && (schema.oneOf ?? schema.anyOf) && schema.discriminator?.propertyName) discriminatorParentNodes.push(node);
2736
2389
  }
2737
- const circularNames = [...(0, _kubb_ast.findCircularSchemasFromGraph)(refGraph)];
2390
+ const circularNames = [..._kubb_kit.ast.findCircularSchemasFromGraph(refGraph)];
2738
2391
  const discriminatorChildMap = discriminatorParentNodes.length > 0 ? buildDiscriminatorChildMap(discriminatorParentNodes) : null;
2739
- const operationNodes = [];
2740
- for (const operation of getOperations(document, refs)) {
2741
- const operationNode = parseOperation(parserOptions, operation);
2742
- if (operationNode) operationNodes.push(operationNode);
2743
- }
2392
+ const operationNodes = getOperations(document, refs).map((operation) => parseOperation({
2393
+ operation,
2394
+ refs,
2395
+ contentType,
2396
+ options: parserOptions,
2397
+ parseSchema
2398
+ }));
2744
2399
  const promotedEnums = enums === "root" ? collectInlineEnums([...parsedByName.values(), ...operationNodes], new Set(Object.keys(schemas))) : null;
2745
2400
  if (promotedEnums) for (const name of promotedEnums.keys()) enumNames.push(name);
2746
2401
  const schemaNodes = promotedEnums ? [...promotedEnums.values()] : [];
2747
2402
  for (const name of Object.keys(schemas)) {
2748
- const alias = refAliasMap.get(name);
2749
- const node = alias?.name && parsedByName.has(alias.name) ? {
2750
- ...parsedByName.get(alias.name),
2403
+ const aliasTarget = refAliasMap.get(name)?.name;
2404
+ const aliased = aliasTarget ? parsedByName.get(aliasTarget) : void 0;
2405
+ const parsed = parsedByName.get(name);
2406
+ const child = discriminatorChildMap?.get(name);
2407
+ const base = child ? patchDiscriminatorNode(parsed, child) : parsed;
2408
+ const node = aliased ? {
2409
+ ...aliased,
2751
2410
  name
2752
- } : (() => {
2753
- const parsed = parsedByName.get(name);
2754
- const child = discriminatorChildMap?.get(name);
2755
- return child ? patchDiscriminatorNode(parsed, child) : parsed;
2756
- })();
2411
+ } : base;
2757
2412
  schemaNodes.push(promotedEnums ? refPromotedEnums(node, promotedEnums) : node);
2758
2413
  }
2759
2414
  const operations = promotedEnums ? operationNodes.map((node) => refPromotedEnums(node, promotedEnums)) : operationNodes;
2760
- return _kubb_ast.ast.factory.createInput({
2415
+ return _kubb_kit.ast.factory.createInput({
2761
2416
  schemas: schemaNodes,
2762
2417
  operations,
2763
2418
  meta: {
@@ -2775,20 +2430,7 @@ const adapterOas = (0, _kubb_core.createAdapter)((options) => {
2775
2430
  }
2776
2431
  return {
2777
2432
  name: "oas",
2778
- get options() {
2779
- return {
2780
- validate,
2781
- contentType,
2782
- server,
2783
- discriminator,
2784
- enums,
2785
- dateType,
2786
- integerType,
2787
- unknownType,
2788
- emptySchemaType,
2789
- enumSuffix
2790
- };
2791
- },
2433
+ options: resolvedOptions,
2792
2434
  get document() {
2793
2435
  return parsedDocument;
2794
2436
  },
@@ -2817,8 +2459,8 @@ const adapterOas = (0, _kubb_core.createAdapter)((options) => {
2817
2459
  parser: createSchemaParser({
2818
2460
  document,
2819
2461
  refs,
2820
- contentType,
2821
- renames
2462
+ renames,
2463
+ options: parserOptions
2822
2464
  })
2823
2465
  });
2824
2466
  })();