@kubb/adapter-oas 5.5.5 → 5.6.0
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 +530 -831
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.ts +12 -18
- package/dist/index.js +480 -759
- package/dist/index.js.map +1 -1
- package/package.json +4 -6
package/dist/index.js
CHANGED
|
@@ -1,12 +1,9 @@
|
|
|
1
1
|
import "./rolldown-runtime-C0LytTxp.js";
|
|
2
|
-
import { ast,
|
|
3
|
-
import { Diagnostics, createAdapter } from "@kubb/core";
|
|
2
|
+
import { Diagnostics, ast, childName, createAdapter, enumPropName, extractRefName, macroDiscriminatorEnum, macroEnumName, macroSimplifyUnion, mergeAdjacentObjectsLazy } from "@kubb/kit";
|
|
4
3
|
import { access, readFile } from "node:fs/promises";
|
|
5
|
-
import path from "node:path";
|
|
6
4
|
import { parse } from "yaml";
|
|
7
5
|
import { upgrade } from "@scalar/openapi-upgrader";
|
|
8
6
|
import { bundle } from "api-ref-bundler";
|
|
9
|
-
import { childName, enumPropName, extractRefName, macroDiscriminatorEnum, macroEnumName, macroSimplifyUnion, mergeAdjacentObjectsLazy } from "@kubb/kit";
|
|
10
7
|
//#region src/constants.ts
|
|
11
8
|
/**
|
|
12
9
|
* Default parser options applied when no explicit options are provided.
|
|
@@ -507,7 +504,7 @@ async function parseDocument(pathOrApi) {
|
|
|
507
504
|
/**
|
|
508
505
|
* Creates a `Document` from an `AdapterSource`.
|
|
509
506
|
*
|
|
510
|
-
* - `{ type: 'path' }`
|
|
507
|
+
* - `{ type: 'path' }` bundles an absolute file path (core resolves `input`) or remote URL.
|
|
511
508
|
* - `{ type: 'data' }` parses an inline string (YAML/JSON) or raw object.
|
|
512
509
|
*
|
|
513
510
|
* @example
|
|
@@ -518,10 +515,8 @@ async function parseDocument(pathOrApi) {
|
|
|
518
515
|
*/
|
|
519
516
|
async function parseFromConfig(source) {
|
|
520
517
|
if (source.type === "data") return parseDocument(typeof source.data === "string" ? parse(source.data) : structuredClone(source.data));
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
await assertInputExists(resolved);
|
|
524
|
-
return parseDocument(resolved);
|
|
518
|
+
await assertInputExists(source.path);
|
|
519
|
+
return parseDocument(source.path);
|
|
525
520
|
}
|
|
526
521
|
/**
|
|
527
522
|
* Asserts the parsed input is an OpenAPI or Swagger document.
|
|
@@ -650,21 +645,6 @@ const jsonMimeFragments = [
|
|
|
650
645
|
function isJsonMimeType(mimeType) {
|
|
651
646
|
return jsonMimeFragments.some((fragment) => mimeType.includes(fragment));
|
|
652
647
|
}
|
|
653
|
-
/**
|
|
654
|
-
* Picks a media-type entry from a `content` map: the first JSON-like media type, falling back to
|
|
655
|
-
* the first declared one. Returns `false` when `content` has no entries.
|
|
656
|
-
*
|
|
657
|
-
* @example
|
|
658
|
-
* ```ts
|
|
659
|
-
* pickContentEntry({ 'application/xml': xmlEntry, 'application/json': jsonEntry })
|
|
660
|
-
* // ['application/json', jsonEntry]
|
|
661
|
-
* ```
|
|
662
|
-
*/
|
|
663
|
-
function pickContentEntry(content) {
|
|
664
|
-
const mediaTypes = Object.keys(content);
|
|
665
|
-
const available = mediaTypes.find(isJsonMimeType) ?? mediaTypes[0];
|
|
666
|
-
return available ? [available, content[available]] : false;
|
|
667
|
-
}
|
|
668
648
|
//#endregion
|
|
669
649
|
//#region src/model/components.ts
|
|
670
650
|
/**
|
|
@@ -870,10 +850,6 @@ function resolveServerUrl(server, overrides) {
|
|
|
870
850
|
}
|
|
871
851
|
//#endregion
|
|
872
852
|
//#region src/operation.ts
|
|
873
|
-
/**
|
|
874
|
-
* Slugifies a path for the `operationId` fallback: non-alphanumerics collapse to single dashes,
|
|
875
|
-
* with no leading or trailing dash.
|
|
876
|
-
*/
|
|
877
853
|
function slugify(value) {
|
|
878
854
|
return value.replace(/[^a-zA-Z0-9]/g, "-").replace(/-{2,}/g, "-").replace(/^-|-$/g, "");
|
|
879
855
|
}
|
|
@@ -894,12 +870,12 @@ function getResponseStatusCodes({ schema }) {
|
|
|
894
870
|
return Object.keys(responses).filter((key) => !key.startsWith("x-") && !!responses[key] && typeof responses[key] === "object");
|
|
895
871
|
}
|
|
896
872
|
/**
|
|
897
|
-
* Returns the response object for a status code, resolving a `$ref` through `refs`. `
|
|
873
|
+
* Returns the response object for a status code, resolving a `$ref` through `refs`. `null` when absent.
|
|
898
874
|
*/
|
|
899
875
|
function getResponseByStatusCode({ operation, refs, statusCode }) {
|
|
900
876
|
const responses = operation.schema.responses;
|
|
901
|
-
if (!responses || isReference(responses)) return
|
|
902
|
-
return refs.deref(responses[statusCode])
|
|
877
|
+
if (!responses || isReference(responses)) return null;
|
|
878
|
+
return refs.deref(responses[statusCode]);
|
|
903
879
|
}
|
|
904
880
|
/**
|
|
905
881
|
* Resolves the operation's request body, dereferencing a `$ref` through `refs`. Returns `null`
|
|
@@ -909,51 +885,37 @@ function getRequestBody({ operation, refs }) {
|
|
|
909
885
|
return refs.deref(operation.schema.requestBody);
|
|
910
886
|
}
|
|
911
887
|
/**
|
|
912
|
-
* Resolves the request body (a `$ref` through `refs`) and returns its content map, or
|
|
913
|
-
* `undefined` when the operation has no request body.
|
|
914
|
-
*/
|
|
915
|
-
function getRequestBodyContent({ operation, refs }) {
|
|
916
|
-
return getRequestBody({
|
|
917
|
-
operation,
|
|
918
|
-
refs
|
|
919
|
-
})?.content;
|
|
920
|
-
}
|
|
921
|
-
/**
|
|
922
|
-
* Returns the request body media type. With `mediaType` set, returns that entry or `false`.
|
|
923
|
-
* Otherwise picks the first JSON-like media type, then the first declared one, as a
|
|
924
|
-
* `[mediaType, object]` tuple.
|
|
925
|
-
*/
|
|
926
|
-
function getRequestContent({ operation, refs, mediaType }) {
|
|
927
|
-
const content = getRequestBodyContent({
|
|
928
|
-
operation,
|
|
929
|
-
refs
|
|
930
|
-
});
|
|
931
|
-
if (!content) return false;
|
|
932
|
-
if (mediaType) return mediaType in content ? content[mediaType] : false;
|
|
933
|
-
return pickContentEntry(content);
|
|
934
|
-
}
|
|
935
|
-
/**
|
|
936
888
|
* Returns the primary request content type. Prefers a JSON-like media type (the last one wins
|
|
937
889
|
* when several are declared), then the first declared one, defaulting to `'application/json'`.
|
|
938
890
|
*/
|
|
939
891
|
function getRequestContentType({ operation, refs }) {
|
|
940
|
-
const content =
|
|
892
|
+
const content = getRequestBody({
|
|
941
893
|
operation,
|
|
942
894
|
refs
|
|
943
|
-
});
|
|
895
|
+
})?.content;
|
|
944
896
|
const mediaTypes = content ? Object.keys(content) : [];
|
|
945
897
|
return mediaTypes.findLast(isJsonMimeType) ?? mediaTypes[0] ?? "application/json";
|
|
946
898
|
}
|
|
899
|
+
/** Merges path-level and operation-level parameters; operation-level wins per `in:name`. */
|
|
900
|
+
function getParameters({ operation, refs }) {
|
|
901
|
+
const resolveParams = (params) => params.map((p) => refs.derefKeepingRef(p)).filter((p) => !!p && typeof p === "object" && "in" in p && "name" in p);
|
|
902
|
+
const operationParams = resolveParams(operation.schema?.parameters || []);
|
|
903
|
+
const pathLevelParams = resolveParams(operation.pathItem.parameters ?? []);
|
|
904
|
+
const paramMap = /* @__PURE__ */ new Map();
|
|
905
|
+
for (const p of [...pathLevelParams, ...operationParams]) if (p.name && p.in) paramMap.set(`${p.in}:${p.name}`, p);
|
|
906
|
+
return Array.from(paramMap.values());
|
|
907
|
+
}
|
|
908
|
+
/** Schema for one media type of a `content` map, with the binary fallback for an emptied non-JSON entry. */
|
|
909
|
+
function getBodySchema({ content, contentType, refs }) {
|
|
910
|
+
const entry = contentType ? content?.[contentType] : void 0;
|
|
911
|
+
if (!entry) return null;
|
|
912
|
+
const binary = getBinaryFallbackSchema(contentType, entry.schema);
|
|
913
|
+
if (binary) return binary;
|
|
914
|
+
return entry.schema ? refs.derefKeepingRef(entry.schema) : null;
|
|
915
|
+
}
|
|
947
916
|
/**
|
|
948
917
|
* Builds an `Operation` for every supported HTTP method on every path, in document order.
|
|
949
918
|
* `x-` path keys and unresolvable path-item `$ref`s are skipped.
|
|
950
|
-
*
|
|
951
|
-
* @example
|
|
952
|
-
* ```ts
|
|
953
|
-
* for (const operation of getOperations(document, refs)) {
|
|
954
|
-
* parseOperation(options, operation)
|
|
955
|
-
* }
|
|
956
|
-
* ```
|
|
957
919
|
*/
|
|
958
920
|
function getOperations(document, refs) {
|
|
959
921
|
const operations = [];
|
|
@@ -978,32 +940,144 @@ function getOperations(document, refs) {
|
|
|
978
940
|
}
|
|
979
941
|
return operations;
|
|
980
942
|
}
|
|
981
|
-
|
|
982
|
-
|
|
983
|
-
|
|
984
|
-
|
|
985
|
-
|
|
986
|
-
|
|
987
|
-
|
|
988
|
-
|
|
943
|
+
/** Property names whose schema has a truthy `readOnly` or `writeOnly` flag; `$ref` entries are skipped. */
|
|
944
|
+
function collectPropertyKeysByFlag(schema, flag) {
|
|
945
|
+
if (!schema?.properties) return null;
|
|
946
|
+
const keys = [];
|
|
947
|
+
for (const key in schema.properties) {
|
|
948
|
+
const prop = schema.properties[key];
|
|
949
|
+
if (prop && !isReference(prop) && prop[flag]) keys.push(key);
|
|
950
|
+
}
|
|
951
|
+
return keys.length ? keys : null;
|
|
952
|
+
}
|
|
953
|
+
function parseParameter({ param, parentName, options, parseSchema }) {
|
|
954
|
+
const schemaName = parentName && param.name ? pascalCase(`${parentName} ${param.name}`) : void 0;
|
|
955
|
+
const schema = param.schema ? parseSchema({
|
|
956
|
+
schema: param.schema,
|
|
957
|
+
name: schemaName
|
|
958
|
+
}) : ast.factory.createSchema({ type: options.unknownType });
|
|
959
|
+
const style = param.style;
|
|
960
|
+
const explode = param.explode;
|
|
961
|
+
return ast.factory.createParameter({
|
|
962
|
+
name: param.name,
|
|
963
|
+
in: param.in,
|
|
964
|
+
schema: {
|
|
965
|
+
...schema,
|
|
966
|
+
description: param.description ?? schema.description
|
|
967
|
+
},
|
|
968
|
+
required: param.required ?? false,
|
|
969
|
+
...style !== void 0 ? { style } : {},
|
|
970
|
+
...explode !== void 0 ? { explode } : {}
|
|
971
|
+
});
|
|
989
972
|
}
|
|
990
973
|
/**
|
|
991
|
-
*
|
|
992
|
-
* `specialCasedFormats` that `convertFormat` handles directly (int64, uint64, date-time, date, time). False means the format falls back to
|
|
993
|
-
* the base type, which is what `KUBB_UNSUPPORTED_FORMAT` flags. Reading both sources keeps the
|
|
994
|
-
* diagnostic in step with the parser as `formatMap` grows.
|
|
974
|
+
* Converts an OAS `Operation` into an `OperationNode`.
|
|
995
975
|
*/
|
|
996
|
-
function
|
|
997
|
-
|
|
976
|
+
function parseOperation({ operation, ...ctx }) {
|
|
977
|
+
const { refs, contentType, options, parseSchema } = ctx;
|
|
978
|
+
const operationId = getOperationId(operation);
|
|
979
|
+
const operationName = operationId ? pascalCase(operationId) : void 0;
|
|
980
|
+
const parameters = getParameters({
|
|
981
|
+
operation,
|
|
982
|
+
refs
|
|
983
|
+
}).map((param) => parseParameter({
|
|
984
|
+
options,
|
|
985
|
+
parseSchema,
|
|
986
|
+
param,
|
|
987
|
+
parentName: operationName
|
|
988
|
+
}));
|
|
989
|
+
const body = getRequestBody({
|
|
990
|
+
operation,
|
|
991
|
+
refs
|
|
992
|
+
});
|
|
993
|
+
const bodyRequired = body?.required === true;
|
|
994
|
+
const requestBodyName = operationName ? `${operationName}Request` : void 0;
|
|
995
|
+
const content = (contentType ? [contentType] : Object.keys(body?.content ?? {})).flatMap((ct) => {
|
|
996
|
+
const schema = getBodySchema({
|
|
997
|
+
content: body?.content,
|
|
998
|
+
contentType: ct,
|
|
999
|
+
refs
|
|
1000
|
+
});
|
|
1001
|
+
if (!schema) return [];
|
|
1002
|
+
return [ast.factory.createContent({
|
|
1003
|
+
contentType: ct,
|
|
1004
|
+
schema: ast.optionality(parseSchema({
|
|
1005
|
+
schema,
|
|
1006
|
+
name: requestBodyName
|
|
1007
|
+
}), bodyRequired),
|
|
1008
|
+
keysToOmit: collectPropertyKeysByFlag(schema, "readOnly")
|
|
1009
|
+
})];
|
|
1010
|
+
});
|
|
1011
|
+
const requestBody = content.length > 0 || body?.description ? {
|
|
1012
|
+
description: body?.description,
|
|
1013
|
+
required: bodyRequired || void 0,
|
|
1014
|
+
content: content.length > 0 ? content : void 0
|
|
1015
|
+
} : void 0;
|
|
1016
|
+
const responses = getResponseStatusCodes(operation).map((statusCode) => {
|
|
1017
|
+
const response = getResponseByStatusCode({
|
|
1018
|
+
operation,
|
|
1019
|
+
refs,
|
|
1020
|
+
statusCode
|
|
1021
|
+
});
|
|
1022
|
+
const responseName = operationName ? `${operationName}Status${statusCode}` : void 0;
|
|
1023
|
+
const parseEntrySchema = (ct) => {
|
|
1024
|
+
const raw = getBodySchema({
|
|
1025
|
+
content: response?.content,
|
|
1026
|
+
contentType: ct,
|
|
1027
|
+
refs
|
|
1028
|
+
});
|
|
1029
|
+
return {
|
|
1030
|
+
schema: raw && Object.keys(raw).length > 0 ? parseSchema({
|
|
1031
|
+
schema: raw,
|
|
1032
|
+
name: responseName
|
|
1033
|
+
}) : ast.factory.createSchema({ type: options.emptySchemaType }),
|
|
1034
|
+
keysToOmit: collectPropertyKeysByFlag(raw, "writeOnly")
|
|
1035
|
+
};
|
|
1036
|
+
};
|
|
1037
|
+
const responseContent = (contentType ? [contentType] : Object.keys(response?.content ?? {})).map((ct) => ast.factory.createContent({
|
|
1038
|
+
contentType: ct,
|
|
1039
|
+
...parseEntrySchema(ct)
|
|
1040
|
+
}));
|
|
1041
|
+
if (responseContent.length === 0) responseContent.push(ast.factory.createContent({
|
|
1042
|
+
contentType: getRequestContentType({
|
|
1043
|
+
operation,
|
|
1044
|
+
refs
|
|
1045
|
+
}),
|
|
1046
|
+
...parseEntrySchema(contentType)
|
|
1047
|
+
}));
|
|
1048
|
+
return ast.factory.createResponse({
|
|
1049
|
+
statusCode,
|
|
1050
|
+
description: response?.description,
|
|
1051
|
+
content: responseContent
|
|
1052
|
+
});
|
|
1053
|
+
});
|
|
1054
|
+
const pickDoc = (key) => {
|
|
1055
|
+
const own = operation.schema[key];
|
|
1056
|
+
if (typeof own === "string") return own;
|
|
1057
|
+
const fallback = operation.pathItem[key];
|
|
1058
|
+
return typeof fallback === "string" ? fallback : void 0;
|
|
1059
|
+
};
|
|
1060
|
+
return ast.factory.createOperation({
|
|
1061
|
+
operationId,
|
|
1062
|
+
protocol: "http",
|
|
1063
|
+
method: operation.method.toUpperCase(),
|
|
1064
|
+
path: operation.path,
|
|
1065
|
+
tags: Array.isArray(operation.schema.tags) ? operation.schema.tags.map(String) : [],
|
|
1066
|
+
summary: pickDoc("summary") || void 0,
|
|
1067
|
+
description: pickDoc("description") || void 0,
|
|
1068
|
+
deprecated: operation.schema.deprecated || void 0,
|
|
1069
|
+
parameters,
|
|
1070
|
+
requestBody,
|
|
1071
|
+
responses
|
|
1072
|
+
});
|
|
998
1073
|
}
|
|
1074
|
+
//#endregion
|
|
1075
|
+
//#region src/emit/schemaShape.ts
|
|
999
1076
|
/**
|
|
1000
|
-
*
|
|
1001
|
-
* Numeric types (`number`, `integer`, `bigint`) pass through unchanged. `boolean` maps to `'boolean'`. Everything else becomes `'string'`.
|
|
1077
|
+
* Whether `format` maps to a dedicated type; `false` is what `KUBB_UNSUPPORTED_FORMAT` flags.
|
|
1002
1078
|
*/
|
|
1003
|
-
function
|
|
1004
|
-
|
|
1005
|
-
if (type === "boolean") return "boolean";
|
|
1006
|
-
return "string";
|
|
1079
|
+
function isHandledFormat(format) {
|
|
1080
|
+
return formatMap[format] !== void 0 || specialCasedFormats.has(format);
|
|
1007
1081
|
}
|
|
1008
1082
|
/**
|
|
1009
1083
|
* Resolves the `dateType` option down to the value for one format. The scalar form of
|
|
@@ -1015,51 +1089,6 @@ function resolveDateTypeValue(dateType, format) {
|
|
|
1015
1089
|
return dateType[format === "date-time" ? "dateTime" : format] ?? "string";
|
|
1016
1090
|
}
|
|
1017
1091
|
/**
|
|
1018
|
-
* Resolves the AST type descriptor for a date/time format, honoring the `dateType` option.
|
|
1019
|
-
* Returns `null` when the resolved value is `false`, so the format falls through to `string`.
|
|
1020
|
-
*/
|
|
1021
|
-
function getDateType(options, format) {
|
|
1022
|
-
const value = resolveDateTypeValue(options.dateType, format);
|
|
1023
|
-
if (!value) return null;
|
|
1024
|
-
if (format === "date-time") {
|
|
1025
|
-
if (value === "date") return {
|
|
1026
|
-
type: "date",
|
|
1027
|
-
representation: "date"
|
|
1028
|
-
};
|
|
1029
|
-
if (value === "stringOffset") return {
|
|
1030
|
-
type: "datetime",
|
|
1031
|
-
offset: true
|
|
1032
|
-
};
|
|
1033
|
-
if (value === "stringLocal") return {
|
|
1034
|
-
type: "datetime",
|
|
1035
|
-
local: true
|
|
1036
|
-
};
|
|
1037
|
-
return {
|
|
1038
|
-
type: "datetime",
|
|
1039
|
-
offset: false
|
|
1040
|
-
};
|
|
1041
|
-
}
|
|
1042
|
-
if (format === "date") return {
|
|
1043
|
-
type: "date",
|
|
1044
|
-
representation: value === "date" ? "date" : "string"
|
|
1045
|
-
};
|
|
1046
|
-
return {
|
|
1047
|
-
type: "time",
|
|
1048
|
-
representation: value === "date" ? "date" : "string"
|
|
1049
|
-
};
|
|
1050
|
-
}
|
|
1051
|
-
/**
|
|
1052
|
-
* Reads a schema's numeric `exclusiveMinimum`/`exclusiveMaximum` bounds (the OAS 3.1 numeric
|
|
1053
|
-
* form). Either key is `undefined` when absent or, for the legacy OAS 3.0 boolean form, not a
|
|
1054
|
-
* number.
|
|
1055
|
-
*/
|
|
1056
|
-
function getExclusiveBounds(schema) {
|
|
1057
|
-
return {
|
|
1058
|
-
exclusiveMinimum: typeof schema.exclusiveMinimum === "number" ? schema.exclusiveMinimum : void 0,
|
|
1059
|
-
exclusiveMaximum: typeof schema.exclusiveMaximum === "number" ? schema.exclusiveMaximum : void 0
|
|
1060
|
-
};
|
|
1061
|
-
}
|
|
1062
|
-
/**
|
|
1063
1092
|
* Reads schema examples as an array. OAS 3.1 uses an `examples` array, but specs (including ones
|
|
1064
1093
|
* labeled 3.1) still use the singular OAS 3.0 `example`, which the upgrader only converts on the
|
|
1065
1094
|
* 3.0 -> 3.1 hop. Normalize both into one array so the AST node exposes only `examples`.
|
|
@@ -1068,12 +1097,6 @@ function extractExamples(schema) {
|
|
|
1068
1097
|
if (Array.isArray(schema.examples)) return schema.examples;
|
|
1069
1098
|
return schema.example !== void 0 ? [schema.example] : void 0;
|
|
1070
1099
|
}
|
|
1071
|
-
/**
|
|
1072
|
-
* Returns `true` when `fragment` carries any JSON Schema keyword that makes it
|
|
1073
|
-
* structurally significant on its own (see `structuralKeys`).
|
|
1074
|
-
*
|
|
1075
|
-
* A fragment with a structural keyword can't be safely merged into a parent schema.
|
|
1076
|
-
*/
|
|
1077
1100
|
function hasStructuralKeywords(fragment) {
|
|
1078
1101
|
return Object.keys(fragment).some((key) => structuralKeys.has(key));
|
|
1079
1102
|
}
|
|
@@ -1081,23 +1104,19 @@ function hasStructuralKeywords(fragment) {
|
|
|
1081
1104
|
* Flattens a keyword-only `allOf` into its parent schema.
|
|
1082
1105
|
*
|
|
1083
1106
|
* Only flattens when every member is a plain fragment, with no `$ref` and no structural keywords
|
|
1084
|
-
* (see `structuralKeys`). Outer schema values take precedence over fragment values.
|
|
1085
|
-
*
|
|
1107
|
+
* (see `structuralKeys`). Outer schema values take precedence over fragment values. Returns the
|
|
1108
|
+
* original schema unchanged when flattening is unsafe.
|
|
1086
1109
|
*
|
|
1087
1110
|
* @example
|
|
1088
1111
|
* ```ts
|
|
1089
1112
|
* flattenSchema({ allOf: [{ description: 'A pet' }], type: 'object', properties: {} })
|
|
1090
1113
|
* // { type: 'object', properties: {}, description: 'A pet' }
|
|
1091
|
-
* ```
|
|
1092
|
-
*
|
|
1093
|
-
* @example
|
|
1094
|
-
* ```ts
|
|
1095
1114
|
* flattenSchema({ allOf: [{ $ref: '#/components/schemas/Pet' }] })
|
|
1096
1115
|
* // returned unchanged, contains a $ref
|
|
1097
1116
|
* ```
|
|
1098
1117
|
*/
|
|
1099
1118
|
function flattenSchema(schema) {
|
|
1100
|
-
if (!schema
|
|
1119
|
+
if (!schema.allOf?.length) return schema;
|
|
1101
1120
|
const allOfFragments = schema.allOf;
|
|
1102
1121
|
if (allOfFragments.some((item) => isReference(item))) return schema;
|
|
1103
1122
|
if (allOfFragments.some(hasStructuralKeywords)) return schema;
|
|
@@ -1176,7 +1195,7 @@ function findDiscriminators(mapping, ref) {
|
|
|
1176
1195
|
* at this union usage site, which is what makes this mode "preserve" (as opposed to `propagate`,
|
|
1177
1196
|
* which additionally patches the child schema's own definition in a post-pass).
|
|
1178
1197
|
*/
|
|
1179
|
-
function narrowUnionMembers({ unionMembers, discriminator, sharedPropertiesNode, parse,
|
|
1198
|
+
function narrowUnionMembers({ unionMembers, discriminator, sharedPropertiesNode, parse, name, refs }) {
|
|
1180
1199
|
function pickDiscriminatorPropertyNode(node, propertyName) {
|
|
1181
1200
|
const discriminatorProperty = ast.narrowSchema(node, "object")?.properties?.find((property) => property.name === propertyName);
|
|
1182
1201
|
if (!discriminatorProperty) return null;
|
|
@@ -1218,7 +1237,7 @@ function narrowUnionMembers({ unionMembers, discriminator, sharedPropertiesNode,
|
|
|
1218
1237
|
const memberNode = parse({
|
|
1219
1238
|
schema: s,
|
|
1220
1239
|
name
|
|
1221
|
-
}
|
|
1240
|
+
});
|
|
1222
1241
|
if (!discriminatorValues.length || !discriminator) return memberNode;
|
|
1223
1242
|
const narrowedDiscriminatorNode = sharedPropertiesNode ? pickDiscriminatorPropertyNode(ast.applyMacros(sharedPropertiesNode, [macroDiscriminatorEnum({
|
|
1224
1243
|
propertyName: discriminator.propertyName,
|
|
@@ -1335,6 +1354,68 @@ function convertAnnotatedEnum(context) {
|
|
|
1335
1354
|
//#endregion
|
|
1336
1355
|
//#region src/emit/converters/composition.ts
|
|
1337
1356
|
/**
|
|
1357
|
+
* Collects property schemas from all `allOf` members and outer schema properties,
|
|
1358
|
+
* recursively resolving `$ref` pointers.
|
|
1359
|
+
*/
|
|
1360
|
+
function collectSiblingProperties(allOfMembers, refs, outerProperties) {
|
|
1361
|
+
const properties = { ...outerProperties };
|
|
1362
|
+
const visited = /* @__PURE__ */ new Set();
|
|
1363
|
+
function collectFrom(item) {
|
|
1364
|
+
if (!item) return;
|
|
1365
|
+
if (isReference(item)) {
|
|
1366
|
+
if (visited.has(item.$ref)) return;
|
|
1367
|
+
visited.add(item.$ref);
|
|
1368
|
+
const resolved = refs.resolve(item.$ref, { report: false });
|
|
1369
|
+
if (resolved && !isReference(resolved)) collectFrom(resolved);
|
|
1370
|
+
return;
|
|
1371
|
+
}
|
|
1372
|
+
if (item.properties) Object.assign(properties, item.properties);
|
|
1373
|
+
if (Array.isArray(item.allOf)) for (const member of item.allOf) collectFrom(member);
|
|
1374
|
+
}
|
|
1375
|
+
for (const item of allOfMembers) collectFrom(item);
|
|
1376
|
+
return properties;
|
|
1377
|
+
}
|
|
1378
|
+
/**
|
|
1379
|
+
* Enriches union members that declare `required` properties without declaring corresponding
|
|
1380
|
+
* property schemas, populating the missing property definitions from available sibling schemas
|
|
1381
|
+
* (or an empty schema falling back to the configured emptySchemaType).
|
|
1382
|
+
*/
|
|
1383
|
+
function resolveUnionMembers(members, siblingProperties) {
|
|
1384
|
+
return members.map((member) => {
|
|
1385
|
+
if (!member || typeof member !== "object" || isReference(member)) return member;
|
|
1386
|
+
const obj = member;
|
|
1387
|
+
if (!Array.isArray(obj.required) || obj.required.length === 0) return member;
|
|
1388
|
+
const existingProps = obj.properties ?? {};
|
|
1389
|
+
const missingKeys = obj.required.filter((key) => !Object.prototype.hasOwnProperty.call(existingProps, key));
|
|
1390
|
+
if (missingKeys.length === 0) return member;
|
|
1391
|
+
const newProperties = { ...existingProps };
|
|
1392
|
+
for (const key of missingKeys) {
|
|
1393
|
+
const value = Object.prototype.hasOwnProperty.call(siblingProperties, key) ? siblingProperties[key] : {};
|
|
1394
|
+
Object.defineProperty(newProperties, key, {
|
|
1395
|
+
value,
|
|
1396
|
+
enumerable: true,
|
|
1397
|
+
writable: true,
|
|
1398
|
+
configurable: true
|
|
1399
|
+
});
|
|
1400
|
+
}
|
|
1401
|
+
return {
|
|
1402
|
+
type: obj.type ?? "object",
|
|
1403
|
+
...obj,
|
|
1404
|
+
properties: newProperties
|
|
1405
|
+
};
|
|
1406
|
+
});
|
|
1407
|
+
}
|
|
1408
|
+
/**
|
|
1409
|
+
* Enriches any `oneOf` or `anyOf` branches on a schema object with properties from sibling schemas.
|
|
1410
|
+
*/
|
|
1411
|
+
function resolveUnionSchema(schema, siblingProperties) {
|
|
1412
|
+
if (!schema.oneOf && !schema.anyOf) return schema;
|
|
1413
|
+
const next = { ...resolveUnionMembers([schema], siblingProperties)[0] ?? schema };
|
|
1414
|
+
if (schema.oneOf) next.oneOf = resolveUnionMembers(schema.oneOf, siblingProperties);
|
|
1415
|
+
if (schema.anyOf) next.anyOf = resolveUnionMembers(schema.anyOf, siblingProperties);
|
|
1416
|
+
return next;
|
|
1417
|
+
}
|
|
1418
|
+
/**
|
|
1338
1419
|
* Converts a `$ref` schema into a `RefSchemaNode`.
|
|
1339
1420
|
*
|
|
1340
1421
|
* The resolved schema is stored in `node.schema`. Usage-site sibling fields
|
|
@@ -1342,9 +1423,9 @@ function convertAnnotatedEnum(context) {
|
|
|
1342
1423
|
* Use `syncSchemaRef(node)` in printers to get a merged view of both.
|
|
1343
1424
|
* Circular refs are detected in `refs.resolveNode` and leave `schema` as `null`.
|
|
1344
1425
|
*/
|
|
1345
|
-
function convertRef({ schema, name, nullable, defaultValue,
|
|
1426
|
+
function convertRef({ schema, name, nullable, defaultValue, document, parse, refs, renames }) {
|
|
1346
1427
|
const refPath = schema.$ref;
|
|
1347
|
-
const resolvedSchema = refPath ? refs.resolveNode(refPath, parse
|
|
1428
|
+
const resolvedSchema = refPath ? refs.resolveNode(refPath, parse) : null;
|
|
1348
1429
|
const ctx = {
|
|
1349
1430
|
schema,
|
|
1350
1431
|
name,
|
|
@@ -1364,13 +1445,13 @@ function convertRef({ schema, name, nullable, defaultValue, rawOptions, document
|
|
|
1364
1445
|
/**
|
|
1365
1446
|
* Converts an `allOf` schema into a flattened node or an `IntersectionSchemaNode`.
|
|
1366
1447
|
*/
|
|
1367
|
-
function convertAllOf({ schema, name, nullable, defaultValue,
|
|
1448
|
+
function convertAllOf({ schema, name, nullable, defaultValue, parse, refs }) {
|
|
1368
1449
|
if (schema.allOf.length === 1 && !schema.properties && !(Array.isArray(schema.required) && schema.required.length) && schema.additionalProperties === void 0) {
|
|
1369
1450
|
const [memberSchema] = schema.allOf;
|
|
1370
1451
|
const memberNode = parse({
|
|
1371
1452
|
schema: memberSchema,
|
|
1372
1453
|
name
|
|
1373
|
-
}
|
|
1454
|
+
});
|
|
1374
1455
|
const { kind: _kind, ...memberNodeProps } = memberNode;
|
|
1375
1456
|
const mergedNullable = nullable || memberNode.nullable || void 0;
|
|
1376
1457
|
const mergedDefault = schema.default === null && mergedNullable ? void 0 : schema.default ?? memberNode.default;
|
|
@@ -1394,10 +1475,11 @@ function convertAllOf({ schema, name, nullable, defaultValue, rawOptions, parse,
|
|
|
1394
1475
|
name,
|
|
1395
1476
|
refs
|
|
1396
1477
|
});
|
|
1397
|
-
const
|
|
1478
|
+
const siblingProperties = collectSiblingProperties(schema.allOf, refs, schema.properties);
|
|
1479
|
+
const allOfMembers = discriminatedAllOf.map((s) => isReference(s) ? s : resolveUnionSchema(s, siblingProperties)).map((s) => parse({
|
|
1398
1480
|
schema: s,
|
|
1399
1481
|
name
|
|
1400
|
-
}
|
|
1482
|
+
}));
|
|
1401
1483
|
const syntheticStart = allOfMembers.length;
|
|
1402
1484
|
if (Array.isArray(schema.required) && schema.required.length) {
|
|
1403
1485
|
const outerKeys = schema.properties ? new Set(Object.keys(schema.properties)) : /* @__PURE__ */ new Set();
|
|
@@ -1418,7 +1500,7 @@ function convertAllOf({ schema, name, nullable, defaultValue, rawOptions, parse,
|
|
|
1418
1500
|
allOfMembers.push(parse({
|
|
1419
1501
|
schema: memberSchema,
|
|
1420
1502
|
name
|
|
1421
|
-
}
|
|
1503
|
+
}));
|
|
1422
1504
|
break;
|
|
1423
1505
|
}
|
|
1424
1506
|
}
|
|
@@ -1426,7 +1508,7 @@ function convertAllOf({ schema, name, nullable, defaultValue, rawOptions, parse,
|
|
|
1426
1508
|
}
|
|
1427
1509
|
if (schema.properties) {
|
|
1428
1510
|
const { allOf: _allOf, ...schemaWithoutAllOf } = schema;
|
|
1429
|
-
allOfMembers.push(parse({ schema: schemaWithoutAllOf }
|
|
1511
|
+
allOfMembers.push(parse({ schema: schemaWithoutAllOf }));
|
|
1430
1512
|
}
|
|
1431
1513
|
for (const { propertyName, values } of discriminantValues) allOfMembers.push(createDiscriminantNode({
|
|
1432
1514
|
propertyName,
|
|
@@ -1448,14 +1530,14 @@ function convertAllOf({ schema, name, nullable, defaultValue, rawOptions, parse,
|
|
|
1448
1530
|
function convertUnion(context) {
|
|
1449
1531
|
const annotatedEnum = convertAnnotatedEnum(context);
|
|
1450
1532
|
if (annotatedEnum) return annotatedEnum;
|
|
1451
|
-
const { schema, name, nullable, defaultValue,
|
|
1533
|
+
const { schema, name, nullable, defaultValue, parse, refs } = context;
|
|
1452
1534
|
const ctx = {
|
|
1453
1535
|
schema,
|
|
1454
1536
|
name,
|
|
1455
1537
|
nullable,
|
|
1456
1538
|
defaultValue
|
|
1457
1539
|
};
|
|
1458
|
-
const unionMembers = [...schema.oneOf ?? [], ...schema.anyOf ?? []];
|
|
1540
|
+
const unionMembers = resolveUnionMembers([...schema.oneOf ?? [], ...schema.anyOf ?? []], schema.properties ?? {});
|
|
1459
1541
|
const strategy = schema.oneOf ? "one" : "any";
|
|
1460
1542
|
const explicitDiscriminatorPropertyName = isDiscriminator(schema) ? schema.discriminator.propertyName : void 0;
|
|
1461
1543
|
const discriminator = isDiscriminator(schema) ? schema.discriminator : void 0;
|
|
@@ -1463,14 +1545,13 @@ function convertUnion(context) {
|
|
|
1463
1545
|
const sharedPropertiesNode = schema.properties ? parse({
|
|
1464
1546
|
schema: memberBaseSchema,
|
|
1465
1547
|
name
|
|
1466
|
-
}
|
|
1548
|
+
}) : void 0;
|
|
1467
1549
|
if (sharedPropertiesNode || discriminator) {
|
|
1468
1550
|
const members = narrowUnionMembers({
|
|
1469
1551
|
unionMembers,
|
|
1470
1552
|
discriminator,
|
|
1471
1553
|
sharedPropertiesNode,
|
|
1472
1554
|
parse,
|
|
1473
|
-
rawOptions,
|
|
1474
1555
|
name,
|
|
1475
1556
|
refs
|
|
1476
1557
|
});
|
|
@@ -1489,7 +1570,7 @@ function convertUnion(context) {
|
|
|
1489
1570
|
const members = unionMembers.map((s) => parse({
|
|
1490
1571
|
schema: s,
|
|
1491
1572
|
name
|
|
1492
|
-
}
|
|
1573
|
+
}));
|
|
1493
1574
|
const unionNode = createNode(ctx, {
|
|
1494
1575
|
type: "union",
|
|
1495
1576
|
strategy,
|
|
@@ -1504,7 +1585,7 @@ function convertUnion(context) {
|
|
|
1504
1585
|
* remains; a single remaining type (e.g. `['string', 'null']`) is handled as that type instead,
|
|
1505
1586
|
* with nullability already folded in.
|
|
1506
1587
|
*/
|
|
1507
|
-
function convertMultiType({ schema, name, nullable, defaultValue,
|
|
1588
|
+
function convertMultiType({ schema, name, nullable, defaultValue, parse }) {
|
|
1508
1589
|
const types = schema.type;
|
|
1509
1590
|
const nonNullTypes = types.filter((t) => t !== "null");
|
|
1510
1591
|
return createNode({
|
|
@@ -1521,7 +1602,7 @@ function convertMultiType({ schema, name, nullable, defaultValue, rawOptions, pa
|
|
|
1521
1602
|
type: t
|
|
1522
1603
|
},
|
|
1523
1604
|
name
|
|
1524
|
-
}
|
|
1605
|
+
});
|
|
1525
1606
|
})
|
|
1526
1607
|
});
|
|
1527
1608
|
}
|
|
@@ -1546,6 +1627,44 @@ function normalizeArrayEnum(schema) {
|
|
|
1546
1627
|
items: normalizedItems
|
|
1547
1628
|
};
|
|
1548
1629
|
}
|
|
1630
|
+
function getPrimitiveType(type) {
|
|
1631
|
+
if (type === "number" || type === "integer" || type === "bigint") return type;
|
|
1632
|
+
if (type === "boolean") return "boolean";
|
|
1633
|
+
return "string";
|
|
1634
|
+
}
|
|
1635
|
+
/** Shared by number, integer and bigint nodes; the exclusive bounds keep only the OAS 3.1 numeric form. */
|
|
1636
|
+
function getNumericConstraints(schema) {
|
|
1637
|
+
return {
|
|
1638
|
+
min: schema.minimum,
|
|
1639
|
+
max: schema.maximum,
|
|
1640
|
+
exclusiveMinimum: typeof schema.exclusiveMinimum === "number" ? schema.exclusiveMinimum : void 0,
|
|
1641
|
+
exclusiveMaximum: typeof schema.exclusiveMaximum === "number" ? schema.exclusiveMaximum : void 0,
|
|
1642
|
+
multipleOf: schema.multipleOf
|
|
1643
|
+
};
|
|
1644
|
+
}
|
|
1645
|
+
function getDateType(options, format) {
|
|
1646
|
+
const value = resolveDateTypeValue(options.dateType, format);
|
|
1647
|
+
if (format !== "date-time") return {
|
|
1648
|
+
type: format,
|
|
1649
|
+
representation: value === "date" ? "date" : "string"
|
|
1650
|
+
};
|
|
1651
|
+
if (value === "date") return {
|
|
1652
|
+
type: "date",
|
|
1653
|
+
representation: "date"
|
|
1654
|
+
};
|
|
1655
|
+
if (value === "stringOffset") return {
|
|
1656
|
+
type: "datetime",
|
|
1657
|
+
offset: true
|
|
1658
|
+
};
|
|
1659
|
+
if (value === "stringLocal") return {
|
|
1660
|
+
type: "datetime",
|
|
1661
|
+
local: true
|
|
1662
|
+
};
|
|
1663
|
+
return {
|
|
1664
|
+
type: "datetime",
|
|
1665
|
+
offset: false
|
|
1666
|
+
};
|
|
1667
|
+
}
|
|
1549
1668
|
/**
|
|
1550
1669
|
* Builds a `null` scalar node carrying the schema's documentation. Shared by the `const: null`
|
|
1551
1670
|
* and the drf-spectacular `NullEnum` (`{ enum: [null] }`) branches, which render identically.
|
|
@@ -1565,22 +1684,13 @@ function createNullNode(schema, name, nullable) {
|
|
|
1565
1684
|
/**
|
|
1566
1685
|
* Converts an OAS 3.1 `const` schema into a null scalar or a single-value `EnumSchemaNode`.
|
|
1567
1686
|
*/
|
|
1568
|
-
function convertConst(
|
|
1687
|
+
function convertConst(context) {
|
|
1688
|
+
const { schema, name } = context;
|
|
1569
1689
|
const constValue = schema.const;
|
|
1570
1690
|
if (constValue === null) return createNullNode(schema, name);
|
|
1571
|
-
|
|
1572
|
-
if (typeof constValue === "number") return "number";
|
|
1573
|
-
if (typeof constValue === "boolean") return "boolean";
|
|
1574
|
-
return "string";
|
|
1575
|
-
})());
|
|
1576
|
-
return createNode({
|
|
1577
|
-
schema,
|
|
1578
|
-
name,
|
|
1579
|
-
nullable,
|
|
1580
|
-
defaultValue
|
|
1581
|
-
}, {
|
|
1691
|
+
return createNode(context, {
|
|
1582
1692
|
type: "enum",
|
|
1583
|
-
primitive:
|
|
1693
|
+
primitive: getPrimitiveType(typeof constValue),
|
|
1584
1694
|
enumValues: [constValue]
|
|
1585
1695
|
});
|
|
1586
1696
|
}
|
|
@@ -1590,36 +1700,30 @@ function convertConst({ schema, name, nullable, defaultValue }) {
|
|
|
1590
1700
|
* a date-ish format, that `dateType` is not `false`.
|
|
1591
1701
|
*/
|
|
1592
1702
|
function convertFormat(context) {
|
|
1593
|
-
const { schema,
|
|
1594
|
-
const ctx = {
|
|
1595
|
-
schema,
|
|
1596
|
-
name,
|
|
1597
|
-
nullable,
|
|
1598
|
-
defaultValue
|
|
1599
|
-
};
|
|
1703
|
+
const { schema, options, type } = context;
|
|
1600
1704
|
if (type === "string" && numericFormats.has(schema.format)) return convertString(context);
|
|
1601
|
-
if (schema.format === "int64" || schema.format === "uint64") return createNode(
|
|
1705
|
+
if (schema.format === "int64" || schema.format === "uint64") return createNode(context, {
|
|
1602
1706
|
type: options.integerType === "bigint" ? "bigint" : "integer",
|
|
1603
1707
|
primitive: "integer",
|
|
1604
1708
|
...getNumericConstraints(schema)
|
|
1605
1709
|
});
|
|
1606
1710
|
if (schema.format === "date-time" || schema.format === "date" || schema.format === "time") {
|
|
1607
1711
|
const dateType = getDateType(options, schema.format);
|
|
1608
|
-
if (dateType.type === "datetime") return createNode(
|
|
1712
|
+
if (dateType.type === "datetime") return createNode(context, {
|
|
1609
1713
|
primitive: "string",
|
|
1610
1714
|
type: "datetime",
|
|
1611
1715
|
offset: dateType.offset,
|
|
1612
1716
|
local: dateType.local
|
|
1613
1717
|
});
|
|
1614
|
-
return createNode(
|
|
1718
|
+
return createNode(context, {
|
|
1615
1719
|
primitive: "string",
|
|
1616
1720
|
type: dateType.type,
|
|
1617
1721
|
representation: dateType.representation
|
|
1618
1722
|
});
|
|
1619
1723
|
}
|
|
1620
|
-
const specialType =
|
|
1621
|
-
const isNumeric = specialType === "number" || specialType === "integer"
|
|
1622
|
-
return createNode(
|
|
1724
|
+
const specialType = formatMap[schema.format];
|
|
1725
|
+
const isNumeric = specialType === "number" || specialType === "integer";
|
|
1726
|
+
return createNode(context, {
|
|
1623
1727
|
primitive: isNumeric ? specialType : "string",
|
|
1624
1728
|
type: specialType,
|
|
1625
1729
|
...specialType === "url" || specialType === "uuid" || specialType === "email" ? {
|
|
@@ -1632,11 +1736,11 @@ function convertFormat(context) {
|
|
|
1632
1736
|
/**
|
|
1633
1737
|
* Converts an `enum` schema into an `EnumSchemaNode`.
|
|
1634
1738
|
*/
|
|
1635
|
-
function convertEnum({ schema, name, nullable, type,
|
|
1739
|
+
function convertEnum({ schema, name, nullable, type, parse }) {
|
|
1636
1740
|
if (type === "array") return parse({
|
|
1637
1741
|
schema: normalizeArrayEnum(schema),
|
|
1638
1742
|
name
|
|
1639
|
-
}
|
|
1743
|
+
});
|
|
1640
1744
|
const nullInEnum = schema.enum.includes(null);
|
|
1641
1745
|
const filteredValues = nullInEnum ? schema.enum.filter((v) => v !== null) : schema.enum;
|
|
1642
1746
|
if (nullInEnum && filteredValues.length === 0) return createNullNode(schema, name);
|
|
@@ -1688,13 +1792,9 @@ function convertEnum({ schema, name, nullable, type, rawOptions, parse }) {
|
|
|
1688
1792
|
/**
|
|
1689
1793
|
* Converts a `type: 'string'` schema into a `StringSchemaNode`.
|
|
1690
1794
|
*/
|
|
1691
|
-
function convertString(
|
|
1692
|
-
|
|
1693
|
-
|
|
1694
|
-
name,
|
|
1695
|
-
nullable,
|
|
1696
|
-
defaultValue
|
|
1697
|
-
}, {
|
|
1795
|
+
function convertString(context) {
|
|
1796
|
+
const { schema } = context;
|
|
1797
|
+
return createNode(context, {
|
|
1698
1798
|
type: "string",
|
|
1699
1799
|
primitive: "string",
|
|
1700
1800
|
min: schema.minLength,
|
|
@@ -1703,58 +1803,13 @@ function convertString({ schema, name, nullable, defaultValue }) {
|
|
|
1703
1803
|
});
|
|
1704
1804
|
}
|
|
1705
1805
|
/**
|
|
1706
|
-
* Reads the numeric constraints shared by every `number`, `integer` and `bigint` node. A `format`
|
|
1707
|
-
* such as `double` or `int32` only narrows the type, so formatted numbers keep these too.
|
|
1708
|
-
*/
|
|
1709
|
-
function getNumericConstraints(schema) {
|
|
1710
|
-
return {
|
|
1711
|
-
min: schema.minimum,
|
|
1712
|
-
max: schema.maximum,
|
|
1713
|
-
...getExclusiveBounds(schema),
|
|
1714
|
-
multipleOf: schema.multipleOf
|
|
1715
|
-
};
|
|
1716
|
-
}
|
|
1717
|
-
/**
|
|
1718
1806
|
* Converts a `type: 'number'` or `type: 'integer'` schema.
|
|
1719
1807
|
*/
|
|
1720
|
-
function convertNumeric(
|
|
1721
|
-
return createNode({
|
|
1722
|
-
schema,
|
|
1723
|
-
name,
|
|
1724
|
-
nullable,
|
|
1725
|
-
defaultValue
|
|
1726
|
-
}, {
|
|
1808
|
+
function convertNumeric(context, type) {
|
|
1809
|
+
return createNode(context, {
|
|
1727
1810
|
type,
|
|
1728
1811
|
primitive: type,
|
|
1729
|
-
...getNumericConstraints(schema)
|
|
1730
|
-
});
|
|
1731
|
-
}
|
|
1732
|
-
/**
|
|
1733
|
-
* Converts a `type: 'boolean'` schema.
|
|
1734
|
-
*/
|
|
1735
|
-
function convertBoolean({ schema, name, nullable, defaultValue }) {
|
|
1736
|
-
return createNode({
|
|
1737
|
-
schema,
|
|
1738
|
-
name,
|
|
1739
|
-
nullable,
|
|
1740
|
-
defaultValue
|
|
1741
|
-
}, {
|
|
1742
|
-
type: "boolean",
|
|
1743
|
-
primitive: "boolean"
|
|
1744
|
-
});
|
|
1745
|
-
}
|
|
1746
|
-
/**
|
|
1747
|
-
* Converts a raw binary schema into a `blob` node.
|
|
1748
|
-
*/
|
|
1749
|
-
function convertBinary({ schema, name, nullable, defaultValue }) {
|
|
1750
|
-
return createNode({
|
|
1751
|
-
schema,
|
|
1752
|
-
name,
|
|
1753
|
-
nullable,
|
|
1754
|
-
defaultValue
|
|
1755
|
-
}, {
|
|
1756
|
-
type: "blob",
|
|
1757
|
-
primitive: "string"
|
|
1812
|
+
...getNumericConstraints(context.schema)
|
|
1758
1813
|
});
|
|
1759
1814
|
}
|
|
1760
1815
|
//#endregion
|
|
@@ -1763,9 +1818,9 @@ function convertBinary({ schema, name, nullable, defaultValue }) {
|
|
|
1763
1818
|
* Resolves a `true` or empty-object map schema (`additionalProperties`/`patternProperties`) to
|
|
1764
1819
|
* `options.unknownType`, otherwise parses it as a regular schema.
|
|
1765
1820
|
*/
|
|
1766
|
-
function resolveMapSchema(mapSchema, options, parse
|
|
1821
|
+
function resolveMapSchema(mapSchema, { options, parse }) {
|
|
1767
1822
|
if (mapSchema === true || typeof mapSchema === "object" && Object.keys(mapSchema).length === 0) return ast.factory.createSchema({ type: options.unknownType });
|
|
1768
|
-
return parse({ schema: mapSchema }
|
|
1823
|
+
return parse({ schema: mapSchema });
|
|
1769
1824
|
}
|
|
1770
1825
|
/**
|
|
1771
1826
|
* Names the inline enums on a property's schema, and on each item when the property is a tuple, from
|
|
@@ -1787,7 +1842,7 @@ function nameEnums(node, options) {
|
|
|
1787
1842
|
/**
|
|
1788
1843
|
* Converts an object-like schema into an `ObjectSchemaNode`.
|
|
1789
1844
|
*/
|
|
1790
|
-
function convertObject({ schema, name, nullable, defaultValue,
|
|
1845
|
+
function convertObject({ schema, name, nullable, defaultValue, options, parse }) {
|
|
1791
1846
|
const properties = schema.properties ? Object.entries(schema.properties).map(([propName, propSchema]) => {
|
|
1792
1847
|
const required = Array.isArray(schema.required) ? schema.required.includes(propName) : !!schema.required;
|
|
1793
1848
|
const resolvedPropSchema = propSchema;
|
|
@@ -1795,7 +1850,7 @@ function convertObject({ schema, name, nullable, defaultValue, rawOptions, optio
|
|
|
1795
1850
|
const schemaNode = nameEnums(parse({
|
|
1796
1851
|
schema: resolvedPropSchema,
|
|
1797
1852
|
name: childName(name, propName)
|
|
1798
|
-
}
|
|
1853
|
+
}), {
|
|
1799
1854
|
parentName: name,
|
|
1800
1855
|
propName,
|
|
1801
1856
|
enumSuffix: options.enumSuffix
|
|
@@ -1812,11 +1867,17 @@ function convertObject({ schema, name, nullable, defaultValue, rawOptions, optio
|
|
|
1812
1867
|
const additionalProperties = schema.additionalProperties;
|
|
1813
1868
|
const additionalPropertiesNode = (() => {
|
|
1814
1869
|
if (additionalProperties === true) return true;
|
|
1815
|
-
if (additionalProperties) return resolveMapSchema(additionalProperties,
|
|
1870
|
+
if (additionalProperties) return resolveMapSchema(additionalProperties, {
|
|
1871
|
+
options,
|
|
1872
|
+
parse
|
|
1873
|
+
});
|
|
1816
1874
|
return additionalProperties;
|
|
1817
1875
|
})();
|
|
1818
1876
|
const rawPatternProperties = "patternProperties" in schema ? schema.patternProperties : void 0;
|
|
1819
|
-
const patternProperties = rawPatternProperties ? Object.fromEntries(Object.entries(rawPatternProperties).map(([pattern, patternSchema]) => [pattern, resolveMapSchema(patternSchema,
|
|
1877
|
+
const patternProperties = rawPatternProperties ? Object.fromEntries(Object.entries(rawPatternProperties).map(([pattern, patternSchema]) => [pattern, resolveMapSchema(patternSchema, {
|
|
1878
|
+
options,
|
|
1879
|
+
parse
|
|
1880
|
+
})])) : void 0;
|
|
1820
1881
|
const objectNode = createNode({
|
|
1821
1882
|
schema,
|
|
1822
1883
|
name,
|
|
@@ -1846,9 +1907,9 @@ function convertObject({ schema, name, nullable, defaultValue, rawOptions, optio
|
|
|
1846
1907
|
/**
|
|
1847
1908
|
* Converts an OAS 3.1 `prefixItems` tuple into a `TupleSchemaNode`.
|
|
1848
1909
|
*/
|
|
1849
|
-
function convertTuple({ schema, name, nullable, defaultValue,
|
|
1850
|
-
const tupleItems = (schema.prefixItems ?? []).map((item) => parse({ schema: item }
|
|
1851
|
-
const rest = schema.items === false ? void 0 : !schema.items || schema.items === true ? ast.factory.createSchema({ type: options.unknownType }) : parse({ schema: schema.items }
|
|
1910
|
+
function convertTuple({ schema, name, nullable, defaultValue, options, parse }) {
|
|
1911
|
+
const tupleItems = (schema.prefixItems ?? []).map((item) => parse({ schema: item }));
|
|
1912
|
+
const rest = schema.items === false ? void 0 : !schema.items || schema.items === true ? ast.factory.createSchema({ type: options.unknownType }) : parse({ schema: schema.items });
|
|
1852
1913
|
return createNode({
|
|
1853
1914
|
schema,
|
|
1854
1915
|
name,
|
|
@@ -1866,13 +1927,13 @@ function convertTuple({ schema, name, nullable, defaultValue, rawOptions, option
|
|
|
1866
1927
|
/**
|
|
1867
1928
|
* Converts a `type: 'array'` schema into an `ArraySchemaNode`.
|
|
1868
1929
|
*/
|
|
1869
|
-
function convertArray({ schema, name, nullable, defaultValue,
|
|
1930
|
+
function convertArray({ schema, name, nullable, defaultValue, options, parse }) {
|
|
1870
1931
|
const rawItems = schema.items;
|
|
1871
1932
|
const itemName = rawItems?.enum?.length && name ? enumPropName(null, name, options.enumSuffix) : name;
|
|
1872
1933
|
const items = rawItems ? [parse({
|
|
1873
1934
|
schema: rawItems,
|
|
1874
1935
|
name: itemName
|
|
1875
|
-
}
|
|
1936
|
+
})] : [];
|
|
1876
1937
|
return createNode({
|
|
1877
1938
|
schema,
|
|
1878
1939
|
name,
|
|
@@ -1931,7 +1992,10 @@ const schemaRules = [
|
|
|
1931
1992
|
},
|
|
1932
1993
|
{
|
|
1933
1994
|
match: ({ schema }) => isBinary(schema),
|
|
1934
|
-
convert:
|
|
1995
|
+
convert: (ctx) => createNode(ctx, {
|
|
1996
|
+
type: "blob",
|
|
1997
|
+
primitive: "string"
|
|
1998
|
+
})
|
|
1935
1999
|
},
|
|
1936
2000
|
{
|
|
1937
2001
|
match: ({ schema, type }) => !type && (schema.minLength !== void 0 || schema.maxLength !== void 0 || schema.pattern !== void 0),
|
|
@@ -1971,7 +2035,10 @@ const schemaRules = [
|
|
|
1971
2035
|
},
|
|
1972
2036
|
{
|
|
1973
2037
|
match: ({ type }) => type === "boolean",
|
|
1974
|
-
convert:
|
|
2038
|
+
convert: (ctx) => createNode(ctx, {
|
|
2039
|
+
type: "boolean",
|
|
2040
|
+
primitive: "boolean"
|
|
2041
|
+
})
|
|
1975
2042
|
},
|
|
1976
2043
|
{
|
|
1977
2044
|
match: ({ type }) => type === "null",
|
|
@@ -1979,95 +2046,94 @@ const schemaRules = [
|
|
|
1979
2046
|
}
|
|
1980
2047
|
];
|
|
1981
2048
|
//#endregion
|
|
1982
|
-
//#region src/
|
|
1983
|
-
|
|
1984
|
-
|
|
1985
|
-
*
|
|
1986
|
-
*
|
|
1987
|
-
*
|
|
1988
|
-
|
|
1989
|
-
|
|
1990
|
-
|
|
1991
|
-
const
|
|
1992
|
-
|
|
1993
|
-
|
|
1994
|
-
value: null
|
|
2049
|
+
//#region src/parser.ts
|
|
2050
|
+
/**
|
|
2051
|
+
* Creates the schema converter bound to one OpenAPI document: `parseSchema` dispatches each
|
|
2052
|
+
* schema through the ordered `schemaRules` table, with the parser options merged once.
|
|
2053
|
+
*
|
|
2054
|
+
* @internal
|
|
2055
|
+
*/
|
|
2056
|
+
function createSchemaParser(ctx) {
|
|
2057
|
+
const { document, refs, renames } = ctx;
|
|
2058
|
+
const options = {
|
|
2059
|
+
...DEFAULT_PARSER_OPTIONS,
|
|
2060
|
+
...ctx.options
|
|
1995
2061
|
};
|
|
1996
|
-
|
|
1997
|
-
|
|
1998
|
-
|
|
1999
|
-
|
|
2000
|
-
|
|
2062
|
+
function parseSchema({ schema, name }) {
|
|
2063
|
+
const flattenedSchema = flattenSchema(schema);
|
|
2064
|
+
if (flattenedSchema !== schema) return parseSchema({
|
|
2065
|
+
schema: flattenedSchema,
|
|
2066
|
+
name
|
|
2067
|
+
});
|
|
2068
|
+
const nullable = isNullable(schema) || void 0;
|
|
2069
|
+
const context = {
|
|
2070
|
+
schema,
|
|
2071
|
+
name,
|
|
2072
|
+
nullable,
|
|
2073
|
+
defaultValue: schema.default === null && nullable ? void 0 : schema.default,
|
|
2074
|
+
type: Array.isArray(schema.type) ? schema.type.find((t) => t !== "null") ?? schema.type[0] : schema.type,
|
|
2075
|
+
options,
|
|
2076
|
+
parse: parseSchema,
|
|
2077
|
+
document,
|
|
2078
|
+
refs,
|
|
2079
|
+
renames
|
|
2080
|
+
};
|
|
2081
|
+
for (const rule of schemaRules) if (rule.match(context)) return rule.convert(context);
|
|
2082
|
+
return createNode(context, { type: options.emptySchemaType });
|
|
2001
2083
|
}
|
|
2002
|
-
|
|
2003
|
-
applicable: true,
|
|
2004
|
-
value: docCache.get(pointer)
|
|
2005
|
-
};
|
|
2006
|
-
const current = pointer.split("/").filter(Boolean).map((token) => globalThis.decodeURIComponent(token).replaceAll("~1", "/").replaceAll("~0", "~")).reduce((obj, key) => obj?.[key], document);
|
|
2007
|
-
if (current) docCache.set(pointer, current);
|
|
2008
|
-
return {
|
|
2009
|
-
applicable: true,
|
|
2010
|
-
value: current ?? null
|
|
2011
|
-
};
|
|
2084
|
+
return { parseSchema };
|
|
2012
2085
|
}
|
|
2086
|
+
//#endregion
|
|
2087
|
+
//#region src/promoteEnums.ts
|
|
2013
2088
|
/**
|
|
2014
|
-
*
|
|
2015
|
-
*
|
|
2016
|
-
*
|
|
2017
|
-
* resolved, reports a `refNotFound` diagnostic into the active build and returns `null`. Outside a
|
|
2018
|
-
* build there is no sink to collect it, so it throws instead.
|
|
2019
|
-
*
|
|
2020
|
-
* @example
|
|
2021
|
-
* ```ts
|
|
2022
|
-
* resolveRef<SchemaObject>(document, '#/components/schemas/Pet')
|
|
2023
|
-
* ```
|
|
2089
|
+
* Collects inline enums to lift to the top level, keyed by the name the parser derived for them
|
|
2090
|
+
* (e.g. `PetStatusEnum`). An enum already defined as a top-level component is left as-is, and a
|
|
2091
|
+
* name that recurs maps to the first definition so each name yields one shared type.
|
|
2024
2092
|
*/
|
|
2025
|
-
function
|
|
2026
|
-
const
|
|
2027
|
-
|
|
2028
|
-
|
|
2029
|
-
|
|
2030
|
-
|
|
2031
|
-
|
|
2032
|
-
|
|
2033
|
-
|
|
2034
|
-
|
|
2035
|
-
|
|
2036
|
-
|
|
2037
|
-
|
|
2093
|
+
function collectInlineEnums(roots, topLevelNames) {
|
|
2094
|
+
const promoted = /* @__PURE__ */ new Map();
|
|
2095
|
+
for (const root of roots) {
|
|
2096
|
+
const isSchemaRoot = root.kind === "Schema";
|
|
2097
|
+
for (const node of ast.collect(root, { schema: (schemaNode) => schemaNode })) {
|
|
2098
|
+
if (node.type !== "enum" || !node.name) continue;
|
|
2099
|
+
if (isSchemaRoot && node === root) continue;
|
|
2100
|
+
if (topLevelNames.has(node.name)) continue;
|
|
2101
|
+
if (!promoted.has(node.name)) promoted.set(node.name, {
|
|
2102
|
+
...node,
|
|
2103
|
+
optional: void 0,
|
|
2104
|
+
nullish: void 0
|
|
2105
|
+
});
|
|
2038
2106
|
}
|
|
2039
|
-
}
|
|
2040
|
-
|
|
2041
|
-
return null;
|
|
2107
|
+
}
|
|
2108
|
+
return promoted;
|
|
2042
2109
|
}
|
|
2043
2110
|
/**
|
|
2044
|
-
*
|
|
2045
|
-
*
|
|
2046
|
-
* Useful for parser flows that need both dereferenced fields and pointer
|
|
2047
|
-
* identity (for naming/import purposes). Non-reference values are returned as-is.
|
|
2048
|
-
*
|
|
2049
|
-
* @example
|
|
2050
|
-
* ```ts
|
|
2051
|
-
* dereferenceWithRef(document, { $ref: '#/components/schemas/Pet' })
|
|
2052
|
-
* // { $ref: '#/components/schemas/Pet', type: 'object', properties: { ... } }
|
|
2053
|
-
* ```
|
|
2111
|
+
* Replaces every promoted inline enum in `node` with a `ref` to its lifted definition, keeping the
|
|
2112
|
+
* occurrence's usage-slot and documentation fields.
|
|
2054
2113
|
*/
|
|
2055
|
-
function
|
|
2056
|
-
if (
|
|
2057
|
-
|
|
2058
|
-
|
|
2059
|
-
|
|
2060
|
-
|
|
2061
|
-
|
|
2114
|
+
function refPromotedEnums(node, promoted) {
|
|
2115
|
+
if (promoted.size === 0) return node;
|
|
2116
|
+
return ast.transform(node, { schema(schemaNode) {
|
|
2117
|
+
if (schemaNode.type !== "enum" || !schemaNode.name || !promoted.has(schemaNode.name)) return void 0;
|
|
2118
|
+
return ast.factory.createSchema({
|
|
2119
|
+
type: "ref",
|
|
2120
|
+
name: schemaNode.name,
|
|
2121
|
+
ref: `${SCHEMA_REF_PREFIX}${schemaNode.name}`,
|
|
2122
|
+
optional: schemaNode.optional,
|
|
2123
|
+
nullish: schemaNode.nullish,
|
|
2124
|
+
readOnly: schemaNode.readOnly,
|
|
2125
|
+
writeOnly: schemaNode.writeOnly,
|
|
2126
|
+
deprecated: schemaNode.deprecated,
|
|
2127
|
+
description: schemaNode.description,
|
|
2128
|
+
default: schemaNode.default,
|
|
2129
|
+
examples: schemaNode.examples
|
|
2130
|
+
});
|
|
2131
|
+
} });
|
|
2062
2132
|
}
|
|
2133
|
+
//#endregion
|
|
2134
|
+
//#region src/refs.ts
|
|
2063
2135
|
/**
|
|
2064
|
-
* Creates the `$ref` resolution service for one document.
|
|
2065
|
-
*
|
|
2066
|
-
* Replaces what used to be six overlapping resolvers (a reporting walk, a silent walk, an
|
|
2067
|
-
* existence check, and a resolve-then-parse-into-a-node step, each with its own cache) with one
|
|
2068
|
-
* pointer walk and one explicit `report` contract for a missing ref: `report: true` (the default)
|
|
2069
|
-
* reports a `refNotFound` diagnostic (or throws outside a build), `report: false` resolves to
|
|
2070
|
-
* `null` silently for a speculative lookup.
|
|
2136
|
+
* Creates the `$ref` resolution service for one document. One pointer walk backs every method.
|
|
2071
2137
|
*
|
|
2072
2138
|
* @example
|
|
2073
2139
|
* ```ts
|
|
@@ -2077,12 +2143,31 @@ function dereferenceWithRef(document, schema) {
|
|
|
2077
2143
|
* refs.exists('#/components/schemas/Pet')
|
|
2078
2144
|
* refs.resolveNode('#/components/schemas/Pet', parseSchema)
|
|
2079
2145
|
* refs.deref<ResponseObject>(operation.schema.responses?.['200'])
|
|
2146
|
+
* refs.derefKeepingRef(parameter)
|
|
2080
2147
|
* ```
|
|
2081
2148
|
*/
|
|
2082
2149
|
function createRefs(document) {
|
|
2150
|
+
const pointerCache = /* @__PURE__ */ new Map();
|
|
2083
2151
|
const resolvedNodeCache = /* @__PURE__ */ new Map();
|
|
2084
|
-
const existenceCache = /* @__PURE__ */ new Map();
|
|
2085
2152
|
const resolvingRefs = /* @__PURE__ */ new Set();
|
|
2153
|
+
function walkPointer($ref) {
|
|
2154
|
+
const trimmed = $ref.trim();
|
|
2155
|
+
if (trimmed === "" || !trimmed.startsWith("#")) return {
|
|
2156
|
+
applicable: false,
|
|
2157
|
+
value: null
|
|
2158
|
+
};
|
|
2159
|
+
const pointer = trimmed.substring(1);
|
|
2160
|
+
if (pointerCache.has(pointer)) return {
|
|
2161
|
+
applicable: true,
|
|
2162
|
+
value: pointerCache.get(pointer)
|
|
2163
|
+
};
|
|
2164
|
+
const current = pointer.split("/").filter(Boolean).map((token) => globalThis.decodeURIComponent(token).replaceAll("~1", "/").replaceAll("~0", "~")).reduce((obj, key) => obj?.[key], document);
|
|
2165
|
+
if (current) pointerCache.set(pointer, current);
|
|
2166
|
+
return {
|
|
2167
|
+
applicable: true,
|
|
2168
|
+
value: current ?? null
|
|
2169
|
+
};
|
|
2170
|
+
}
|
|
2086
2171
|
/**
|
|
2087
2172
|
* Resolves a local `#/...` JSON pointer. Returns `null` for an empty or non-local ref.
|
|
2088
2173
|
* `report: true` (default) reports a `refNotFound` diagnostic into the active build (or throws
|
|
@@ -2090,27 +2175,36 @@ function createRefs(document) {
|
|
|
2090
2175
|
* for a speculative lookup where a missing ref is not an error.
|
|
2091
2176
|
*/
|
|
2092
2177
|
function resolve(refPath, options) {
|
|
2093
|
-
|
|
2094
|
-
|
|
2095
|
-
|
|
2096
|
-
|
|
2097
|
-
|
|
2178
|
+
const { applicable, value } = walkPointer(refPath);
|
|
2179
|
+
if (!applicable || value || options?.report === false) return value;
|
|
2180
|
+
const diagnostic = {
|
|
2181
|
+
code: Diagnostics.code.refNotFound,
|
|
2182
|
+
severity: "error",
|
|
2183
|
+
message: `Could not find a definition for ${refPath}.`,
|
|
2184
|
+
help: "Add the schema under `components.schemas`, or fix the `$ref`. Run `kubb validate` to check the spec.",
|
|
2185
|
+
location: {
|
|
2186
|
+
kind: "schema",
|
|
2187
|
+
pointer: refPath,
|
|
2188
|
+
ref: refPath
|
|
2189
|
+
}
|
|
2190
|
+
};
|
|
2191
|
+
if (!Diagnostics.report(diagnostic)) throw new Diagnostics.Error(diagnostic);
|
|
2192
|
+
return null;
|
|
2098
2193
|
}
|
|
2099
2194
|
/**
|
|
2100
2195
|
* Returns `true` when a `$ref` path resolves to a component the document actually defines.
|
|
2101
2196
|
* A circular ref still resolves to an existing target, so this stays `true` for cycles and only
|
|
2102
|
-
* goes `false` for a `$ref` that points at a component the spec never declares.
|
|
2197
|
+
* goes `false` for a `$ref` that points at a component the spec never declares.
|
|
2103
2198
|
*/
|
|
2104
2199
|
function exists(refPath) {
|
|
2105
|
-
|
|
2106
|
-
return existenceCache.get(refPath) ?? false;
|
|
2200
|
+
return !!resolve(refPath, { report: false });
|
|
2107
2201
|
}
|
|
2108
2202
|
/**
|
|
2109
2203
|
* Resolves a `$ref` to its parsed node via `parse`, guarding against cycles and memoizing per
|
|
2110
2204
|
* instance. Returns `null` when the ref is currently being resolved (a cycle) or cannot be
|
|
2111
2205
|
* resolved (e.g. a minimal document in a unit test).
|
|
2112
2206
|
*/
|
|
2113
|
-
function resolveNode(refPath, parse
|
|
2207
|
+
function resolveNode(refPath, parse) {
|
|
2114
2208
|
if (resolvingRefs.has(refPath)) return null;
|
|
2115
2209
|
if (!resolvedNodeCache.has(refPath)) {
|
|
2116
2210
|
let resolved = null;
|
|
@@ -2118,7 +2212,7 @@ function createRefs(document) {
|
|
|
2118
2212
|
const referenced = resolve(refPath);
|
|
2119
2213
|
if (referenced) {
|
|
2120
2214
|
resolvingRefs.add(refPath);
|
|
2121
|
-
resolved = parse({ schema: referenced }
|
|
2215
|
+
resolved = parse({ schema: referenced });
|
|
2122
2216
|
resolvingRefs.delete(refPath);
|
|
2123
2217
|
}
|
|
2124
2218
|
} catch {}
|
|
@@ -2131,399 +2225,30 @@ function createRefs(document) {
|
|
|
2131
2225
|
* resolved target. Returns `null` when the value is empty, cannot be resolved, or is still a
|
|
2132
2226
|
* `$ref` after resolving (e.g. a document with no component registry). A non-`$ref` value is
|
|
2133
2227
|
* returned as-is.
|
|
2134
|
-
*
|
|
2135
|
-
* @example
|
|
2136
|
-
* ```ts
|
|
2137
|
-
* refs.deref<ResponseObject>(operation.schema.responses?.['200'])
|
|
2138
|
-
* ```
|
|
2139
2228
|
*/
|
|
2140
2229
|
function deref(value) {
|
|
2141
2230
|
if (!isReference(value)) return value ? value : null;
|
|
2142
2231
|
const resolved = resolve(value.$ref);
|
|
2143
2232
|
return resolved && !isReference(resolved) ? resolved : null;
|
|
2144
2233
|
}
|
|
2234
|
+
/** Resolves a `$ref` object but keeps the `$ref` field on the result; other values pass through. */
|
|
2235
|
+
function derefKeepingRef(value) {
|
|
2236
|
+
if (isReference(value)) return {
|
|
2237
|
+
...value,
|
|
2238
|
+
...resolve(value.$ref),
|
|
2239
|
+
$ref: value.$ref
|
|
2240
|
+
};
|
|
2241
|
+
return value;
|
|
2242
|
+
}
|
|
2145
2243
|
return {
|
|
2146
2244
|
resolve,
|
|
2147
2245
|
exists,
|
|
2148
2246
|
resolveNode,
|
|
2149
|
-
deref
|
|
2247
|
+
deref,
|
|
2248
|
+
derefKeepingRef
|
|
2150
2249
|
};
|
|
2151
2250
|
}
|
|
2152
2251
|
//#endregion
|
|
2153
|
-
//#region src/model/operations.ts
|
|
2154
|
-
/**
|
|
2155
|
-
* Returns all parameters for an operation, merging path-level and operation-level entries.
|
|
2156
|
-
* Operation-level parameters override path-level ones with the same `in:name` key.
|
|
2157
|
-
* Each `$ref` parameter is dereferenced via `dereferenceWithRef` before merging.
|
|
2158
|
-
*
|
|
2159
|
-
* @example
|
|
2160
|
-
* ```ts
|
|
2161
|
-
* getParameters({ document, operation })
|
|
2162
|
-
* // [{ name: 'petId', in: 'path', required: true, schema: { type: 'integer' } }]
|
|
2163
|
-
* ```
|
|
2164
|
-
*/
|
|
2165
|
-
function getParameters({ document, operation }) {
|
|
2166
|
-
const resolveParams = (params) => params.map((p) => dereferenceWithRef(document, p)).filter((p) => !!p && typeof p === "object" && "in" in p && "name" in p);
|
|
2167
|
-
const operationParams = resolveParams(operation.schema?.parameters || []);
|
|
2168
|
-
const pathLevelParams = resolveParams(operation.pathItem.parameters ?? []);
|
|
2169
|
-
const paramMap = /* @__PURE__ */ new Map();
|
|
2170
|
-
for (const p of pathLevelParams) if (p.name && p.in) paramMap.set(`${p.in}:${p.name}`, p);
|
|
2171
|
-
for (const p of operationParams) if (p.name && p.in) paramMap.set(`${p.in}:${p.name}`, p);
|
|
2172
|
-
return Array.from(paramMap.values());
|
|
2173
|
-
}
|
|
2174
|
-
function getResponseBody(responseBody, contentType) {
|
|
2175
|
-
if (!responseBody) return false;
|
|
2176
|
-
if (isReference(responseBody)) return false;
|
|
2177
|
-
const body = responseBody;
|
|
2178
|
-
if (!body.content) return false;
|
|
2179
|
-
if (contentType) return contentType in body.content ? [contentType, body.content[contentType]] : false;
|
|
2180
|
-
return pickContentEntry(body.content);
|
|
2181
|
-
}
|
|
2182
|
-
/**
|
|
2183
|
-
* Returns the response schema for a given operation and HTTP status code.
|
|
2184
|
-
*
|
|
2185
|
-
* Returns an empty object `{}` when no response body schema is available.
|
|
2186
|
-
*
|
|
2187
|
-
* @example
|
|
2188
|
-
* ```ts
|
|
2189
|
-
* getResponseSchema({ document, operation, refs, statusCode: 200 }) // SchemaObject
|
|
2190
|
-
* getResponseSchema({ document, operation, refs, statusCode: '4XX' }) // {}
|
|
2191
|
-
* ```
|
|
2192
|
-
*/
|
|
2193
|
-
function getResponseSchema({ document, operation, refs, statusCode, options = {} }) {
|
|
2194
|
-
const responseBody = getResponseBody(getResponseByStatusCode({
|
|
2195
|
-
operation,
|
|
2196
|
-
refs,
|
|
2197
|
-
statusCode
|
|
2198
|
-
}), options.contentType);
|
|
2199
|
-
if (responseBody === false) return {};
|
|
2200
|
-
const [mediaType, entry] = responseBody;
|
|
2201
|
-
const schema = entry.schema;
|
|
2202
|
-
const binary = getBinaryFallbackSchema(mediaType, schema);
|
|
2203
|
-
if (binary) return binary;
|
|
2204
|
-
if (!schema) return {};
|
|
2205
|
-
return dereferenceWithRef(document, schema);
|
|
2206
|
-
}
|
|
2207
|
-
/**
|
|
2208
|
-
* Returns the request body schema for an operation, or `null` when absent.
|
|
2209
|
-
*
|
|
2210
|
-
* @example
|
|
2211
|
-
* ```ts
|
|
2212
|
-
* getRequestSchema({ document, operation, refs }) // SchemaObject | null
|
|
2213
|
-
* ```
|
|
2214
|
-
*/
|
|
2215
|
-
function getRequestSchema({ document, operation, refs, options = {} }) {
|
|
2216
|
-
const requestBody = getRequestContent({
|
|
2217
|
-
operation,
|
|
2218
|
-
refs,
|
|
2219
|
-
mediaType: options.contentType
|
|
2220
|
-
});
|
|
2221
|
-
if (requestBody === false) return null;
|
|
2222
|
-
const mediaType = Array.isArray(requestBody) ? requestBody[0] : options.contentType;
|
|
2223
|
-
const schema = Array.isArray(requestBody) ? requestBody[1].schema : requestBody.schema;
|
|
2224
|
-
const binary = getBinaryFallbackSchema(mediaType, schema);
|
|
2225
|
-
if (binary) return binary;
|
|
2226
|
-
if (!schema) return null;
|
|
2227
|
-
return dereferenceWithRef(document, schema);
|
|
2228
|
-
}
|
|
2229
|
-
/**
|
|
2230
|
-
* Returns all request body content type keys for an operation, resolving a `$ref` requestBody
|
|
2231
|
-
* through `refs`.
|
|
2232
|
-
*
|
|
2233
|
-
* @example
|
|
2234
|
-
* ```ts
|
|
2235
|
-
* getRequestBodyContentTypes(operation, refs)
|
|
2236
|
-
* // ['application/json', 'multipart/form-data']
|
|
2237
|
-
* ```
|
|
2238
|
-
*/
|
|
2239
|
-
function getRequestBodyContentTypes(operation, refs) {
|
|
2240
|
-
const body = getRequestBody({
|
|
2241
|
-
operation,
|
|
2242
|
-
refs
|
|
2243
|
-
});
|
|
2244
|
-
return body?.content ? Object.keys(body.content) : [];
|
|
2245
|
-
}
|
|
2246
|
-
/**
|
|
2247
|
-
* Returns all response content type keys for an operation at a given status code, resolving the
|
|
2248
|
-
* response `$ref` through `refs`.
|
|
2249
|
-
*
|
|
2250
|
-
* @example
|
|
2251
|
-
* ```ts
|
|
2252
|
-
* getResponseBodyContentTypes(operation, refs, 200)
|
|
2253
|
-
* // ['application/json', 'application/xml']
|
|
2254
|
-
* ```
|
|
2255
|
-
*/
|
|
2256
|
-
function getResponseBodyContentTypes(operation, refs, statusCode) {
|
|
2257
|
-
const responseObj = getResponseByStatusCode({
|
|
2258
|
-
operation,
|
|
2259
|
-
refs,
|
|
2260
|
-
statusCode
|
|
2261
|
-
});
|
|
2262
|
-
if (!responseObj || typeof responseObj !== "object" || isReference(responseObj)) return [];
|
|
2263
|
-
const body = responseObj;
|
|
2264
|
-
return body.content ? Object.keys(body.content) : [];
|
|
2265
|
-
}
|
|
2266
|
-
//#endregion
|
|
2267
|
-
//#region src/parser.ts
|
|
2268
|
-
/**
|
|
2269
|
-
* Creates the schema and operation converters bound to one OpenAPI document.
|
|
2270
|
-
*
|
|
2271
|
-
* Takes the `$ref` service for this document (shared with the rest of the pipeline, see
|
|
2272
|
-
* `adapter.ts`) and owns the `parseSchema` recursion seam, then dispatches each schema through
|
|
2273
|
-
* the ordered `schemaRules` table from `emit/parseSchema.ts`. Every converter is a standalone
|
|
2274
|
-
* function that recurses through the `parse` function passed to it, so this file only wires
|
|
2275
|
-
* state to the converters.
|
|
2276
|
-
*
|
|
2277
|
-
* @internal
|
|
2278
|
-
*/
|
|
2279
|
-
function createSchemaParser(ctx) {
|
|
2280
|
-
const document = ctx.document;
|
|
2281
|
-
const refs = ctx.refs;
|
|
2282
|
-
/**
|
|
2283
|
-
* Converts an OAS `SchemaObject` into a `SchemaNode`.
|
|
2284
|
-
*
|
|
2285
|
-
* Builds the per-schema context, then walks the ordered {@link schemaRules} table and returns
|
|
2286
|
-
* the first converter that produces a node. When none match, falls back to the configured
|
|
2287
|
-
* `emptySchemaType`.
|
|
2288
|
-
*/
|
|
2289
|
-
function parseSchema({ schema, name }, rawOptions) {
|
|
2290
|
-
const options = {
|
|
2291
|
-
...DEFAULT_PARSER_OPTIONS,
|
|
2292
|
-
...rawOptions
|
|
2293
|
-
};
|
|
2294
|
-
const flattenedSchema = flattenSchema(schema);
|
|
2295
|
-
if (flattenedSchema && flattenedSchema !== schema) return parseSchema({
|
|
2296
|
-
schema: flattenedSchema,
|
|
2297
|
-
name
|
|
2298
|
-
}, rawOptions);
|
|
2299
|
-
const nullable = isNullable(schema) || void 0;
|
|
2300
|
-
const context = {
|
|
2301
|
-
schema,
|
|
2302
|
-
name,
|
|
2303
|
-
nullable,
|
|
2304
|
-
defaultValue: schema.default === null && nullable ? void 0 : schema.default,
|
|
2305
|
-
type: Array.isArray(schema.type) ? schema.type.find((t) => t !== "null") ?? schema.type[0] : schema.type,
|
|
2306
|
-
rawOptions,
|
|
2307
|
-
options,
|
|
2308
|
-
parse: parseSchema,
|
|
2309
|
-
document,
|
|
2310
|
-
refs,
|
|
2311
|
-
renames: ctx.renames
|
|
2312
|
-
};
|
|
2313
|
-
for (const rule of schemaRules) if (rule.match(context)) return rule.convert(context);
|
|
2314
|
-
const emptyType = options.emptySchemaType;
|
|
2315
|
-
return ast.factory.createSchema({
|
|
2316
|
-
type: emptyType,
|
|
2317
|
-
name,
|
|
2318
|
-
title: schema.title,
|
|
2319
|
-
description: schema.description,
|
|
2320
|
-
format: schema.format
|
|
2321
|
-
});
|
|
2322
|
-
}
|
|
2323
|
-
/**
|
|
2324
|
-
* Converts a dereferenced OAS parameter object into a `ParameterNode`.
|
|
2325
|
-
*/
|
|
2326
|
-
function parseParameter(options, param, parentName) {
|
|
2327
|
-
const required = param["required"] ?? false;
|
|
2328
|
-
const paramName = param["name"];
|
|
2329
|
-
const schemaName = parentName && paramName ? pascalCase(`${parentName} ${paramName}`) : void 0;
|
|
2330
|
-
const schema = param["schema"] ? parseSchema({
|
|
2331
|
-
schema: param["schema"],
|
|
2332
|
-
name: schemaName
|
|
2333
|
-
}, options) : ast.factory.createSchema({ type: options.unknownType });
|
|
2334
|
-
const style = param["style"];
|
|
2335
|
-
const explode = param["explode"];
|
|
2336
|
-
return ast.factory.createParameter({
|
|
2337
|
-
name: paramName,
|
|
2338
|
-
in: param["in"],
|
|
2339
|
-
schema: {
|
|
2340
|
-
...schema,
|
|
2341
|
-
description: param["description"] ?? schema.description
|
|
2342
|
-
},
|
|
2343
|
-
required,
|
|
2344
|
-
...style !== void 0 ? { style } : {},
|
|
2345
|
-
...explode !== void 0 ? { explode } : {}
|
|
2346
|
-
});
|
|
2347
|
-
}
|
|
2348
|
-
/**
|
|
2349
|
-
* Reads the inline `requestBody` metadata (description / required) that OAS exposes
|
|
2350
|
-
* outside the schema itself, resolving a `$ref` requestBody through `refs`. Returns an
|
|
2351
|
-
* empty object when the request body is missing or cannot be resolved.
|
|
2352
|
-
*/
|
|
2353
|
-
function getRequestBodyMeta(operation) {
|
|
2354
|
-
const body = getRequestBody({
|
|
2355
|
-
operation,
|
|
2356
|
-
refs
|
|
2357
|
-
});
|
|
2358
|
-
if (!body) return { required: false };
|
|
2359
|
-
return {
|
|
2360
|
-
description: body.description,
|
|
2361
|
-
required: body.required === true
|
|
2362
|
-
};
|
|
2363
|
-
}
|
|
2364
|
-
/**
|
|
2365
|
-
* Collects property names whose schema has a truthy boolean flag (`readOnly` or `writeOnly`).
|
|
2366
|
-
* `$ref` entries are skipped since their flags live on the dereferenced target.
|
|
2367
|
-
*/
|
|
2368
|
-
function collectPropertyKeysByFlag(schema, flag) {
|
|
2369
|
-
if (!schema?.properties) return null;
|
|
2370
|
-
const keys = [];
|
|
2371
|
-
for (const key in schema.properties) {
|
|
2372
|
-
const prop = schema.properties[key];
|
|
2373
|
-
if (prop && !isReference(prop) && prop[flag]) keys.push(key);
|
|
2374
|
-
}
|
|
2375
|
-
return keys.length ? keys : null;
|
|
2376
|
-
}
|
|
2377
|
-
/**
|
|
2378
|
-
* Converts an OAS `Operation` into an `OperationNode`.
|
|
2379
|
-
*/
|
|
2380
|
-
function parseOperation(options, operation) {
|
|
2381
|
-
const operationId = getOperationId(operation);
|
|
2382
|
-
const operationName = operationId ? pascalCase(operationId) : void 0;
|
|
2383
|
-
const parameters = getParameters({
|
|
2384
|
-
document,
|
|
2385
|
-
operation
|
|
2386
|
-
}).map((param) => parseParameter(options, param, operationName));
|
|
2387
|
-
const allContentTypes = ctx.contentType ? [ctx.contentType] : getRequestBodyContentTypes(operation, refs);
|
|
2388
|
-
const requestBodyMeta = getRequestBodyMeta(operation);
|
|
2389
|
-
const requestBodyName = operationName ? `${operationName}Request` : void 0;
|
|
2390
|
-
const content = allContentTypes.flatMap((ct) => {
|
|
2391
|
-
const schema = getRequestSchema({
|
|
2392
|
-
document,
|
|
2393
|
-
operation,
|
|
2394
|
-
refs,
|
|
2395
|
-
options: { contentType: ct }
|
|
2396
|
-
});
|
|
2397
|
-
if (!schema) return [];
|
|
2398
|
-
return [ast.factory.createContent({
|
|
2399
|
-
contentType: ct,
|
|
2400
|
-
schema: ast.optionality(parseSchema({
|
|
2401
|
-
schema,
|
|
2402
|
-
name: requestBodyName
|
|
2403
|
-
}, options), requestBodyMeta.required),
|
|
2404
|
-
keysToOmit: collectPropertyKeysByFlag(schema, "readOnly")
|
|
2405
|
-
})];
|
|
2406
|
-
});
|
|
2407
|
-
const requestBody = content.length > 0 || requestBodyMeta.description ? {
|
|
2408
|
-
description: requestBodyMeta.description,
|
|
2409
|
-
required: requestBodyMeta.required || void 0,
|
|
2410
|
-
content: content.length > 0 ? content : void 0
|
|
2411
|
-
} : void 0;
|
|
2412
|
-
const responses = getResponseStatusCodes(operation).map((statusCode) => {
|
|
2413
|
-
const responseObj = getResponseByStatusCode({
|
|
2414
|
-
operation,
|
|
2415
|
-
refs,
|
|
2416
|
-
statusCode
|
|
2417
|
-
});
|
|
2418
|
-
const responseName = operationName ? `${operationName}Status${statusCode}` : void 0;
|
|
2419
|
-
const description = typeof responseObj === "object" && responseObj !== null ? responseObj.description : void 0;
|
|
2420
|
-
const parseEntrySchema = (contentType) => {
|
|
2421
|
-
const raw = getResponseSchema({
|
|
2422
|
-
document,
|
|
2423
|
-
operation,
|
|
2424
|
-
refs,
|
|
2425
|
-
statusCode,
|
|
2426
|
-
options: { contentType }
|
|
2427
|
-
});
|
|
2428
|
-
return {
|
|
2429
|
-
schema: raw && Object.keys(raw).length > 0 ? parseSchema({
|
|
2430
|
-
schema: raw,
|
|
2431
|
-
name: responseName
|
|
2432
|
-
}, options) : ast.factory.createSchema({ type: options.emptySchemaType }),
|
|
2433
|
-
keysToOmit: collectPropertyKeysByFlag(raw, "writeOnly")
|
|
2434
|
-
};
|
|
2435
|
-
};
|
|
2436
|
-
const content = (ctx.contentType ? [ctx.contentType] : getResponseBodyContentTypes(operation, refs, statusCode)).map((contentType) => ast.factory.createContent({
|
|
2437
|
-
contentType,
|
|
2438
|
-
...parseEntrySchema(contentType)
|
|
2439
|
-
}));
|
|
2440
|
-
if (content.length === 0) content.push(ast.factory.createContent({
|
|
2441
|
-
contentType: getRequestContentType({
|
|
2442
|
-
operation,
|
|
2443
|
-
refs
|
|
2444
|
-
}) || "application/json",
|
|
2445
|
-
...parseEntrySchema(ctx.contentType)
|
|
2446
|
-
}));
|
|
2447
|
-
return ast.factory.createResponse({
|
|
2448
|
-
statusCode,
|
|
2449
|
-
description,
|
|
2450
|
-
content
|
|
2451
|
-
});
|
|
2452
|
-
});
|
|
2453
|
-
const pickDoc = (key) => {
|
|
2454
|
-
const own = operation.schema[key];
|
|
2455
|
-
if (typeof own === "string") return own;
|
|
2456
|
-
const fallback = operation.pathItem[key];
|
|
2457
|
-
return typeof fallback === "string" ? fallback : void 0;
|
|
2458
|
-
};
|
|
2459
|
-
return ast.factory.createOperation({
|
|
2460
|
-
operationId,
|
|
2461
|
-
protocol: "http",
|
|
2462
|
-
method: operation.method.toUpperCase(),
|
|
2463
|
-
path: operation.path,
|
|
2464
|
-
tags: Array.isArray(operation.schema.tags) ? operation.schema.tags.map(String) : [],
|
|
2465
|
-
summary: pickDoc("summary") || void 0,
|
|
2466
|
-
description: pickDoc("description") || void 0,
|
|
2467
|
-
deprecated: operation.schema.deprecated || void 0,
|
|
2468
|
-
parameters,
|
|
2469
|
-
requestBody,
|
|
2470
|
-
responses
|
|
2471
|
-
});
|
|
2472
|
-
}
|
|
2473
|
-
return {
|
|
2474
|
-
parseSchema,
|
|
2475
|
-
parseOperation,
|
|
2476
|
-
parseParameter
|
|
2477
|
-
};
|
|
2478
|
-
}
|
|
2479
|
-
//#endregion
|
|
2480
|
-
//#region src/promoteEnums.ts
|
|
2481
|
-
/**
|
|
2482
|
-
* Collects inline enums to lift to the top level, keyed by the name the parser derived for them
|
|
2483
|
-
* (e.g. `PetStatusEnum`). An enum already defined as a top-level component is left as-is, and a
|
|
2484
|
-
* name that recurs maps to the first definition so each name yields one shared type.
|
|
2485
|
-
*/
|
|
2486
|
-
function collectInlineEnums(roots, topLevelNames) {
|
|
2487
|
-
const promoted = /* @__PURE__ */ new Map();
|
|
2488
|
-
for (const root of roots) {
|
|
2489
|
-
const isSchemaRoot = root.kind === "Schema";
|
|
2490
|
-
for (const node of ast.collect(root, { schema: (schemaNode) => schemaNode })) {
|
|
2491
|
-
if (node.type !== "enum" || !node.name) continue;
|
|
2492
|
-
if (isSchemaRoot && node === root) continue;
|
|
2493
|
-
if (topLevelNames.has(node.name)) continue;
|
|
2494
|
-
if (!promoted.has(node.name)) promoted.set(node.name, {
|
|
2495
|
-
...node,
|
|
2496
|
-
optional: void 0,
|
|
2497
|
-
nullish: void 0
|
|
2498
|
-
});
|
|
2499
|
-
}
|
|
2500
|
-
}
|
|
2501
|
-
return promoted;
|
|
2502
|
-
}
|
|
2503
|
-
/**
|
|
2504
|
-
* Replaces every promoted inline enum in `node` with a `ref` to its lifted definition, keeping the
|
|
2505
|
-
* occurrence's usage-slot and documentation fields.
|
|
2506
|
-
*/
|
|
2507
|
-
function refPromotedEnums(node, promoted) {
|
|
2508
|
-
if (promoted.size === 0) return node;
|
|
2509
|
-
return ast.transform(node, { schema(schemaNode) {
|
|
2510
|
-
if (schemaNode.type !== "enum" || !schemaNode.name || !promoted.has(schemaNode.name)) return void 0;
|
|
2511
|
-
return ast.factory.createSchema({
|
|
2512
|
-
type: "ref",
|
|
2513
|
-
name: schemaNode.name,
|
|
2514
|
-
ref: `${SCHEMA_REF_PREFIX}${schemaNode.name}`,
|
|
2515
|
-
optional: schemaNode.optional,
|
|
2516
|
-
nullish: schemaNode.nullish,
|
|
2517
|
-
readOnly: schemaNode.readOnly,
|
|
2518
|
-
writeOnly: schemaNode.writeOnly,
|
|
2519
|
-
deprecated: schemaNode.deprecated,
|
|
2520
|
-
description: schemaNode.description,
|
|
2521
|
-
default: schemaNode.default,
|
|
2522
|
-
examples: schemaNode.examples
|
|
2523
|
-
});
|
|
2524
|
-
} });
|
|
2525
|
-
}
|
|
2526
|
-
//#endregion
|
|
2527
2252
|
//#region src/schemaDiagnostics.ts
|
|
2528
2253
|
/**
|
|
2529
2254
|
* Scans one freshly converted top-level schema in a single walk, so the post-convert pass never
|
|
@@ -2550,7 +2275,7 @@ function escapePointerToken(token) {
|
|
|
2550
2275
|
}
|
|
2551
2276
|
function visit(node, pointer, refs) {
|
|
2552
2277
|
if (node.type === "ref") {
|
|
2553
|
-
const refName = resolveRefName(node);
|
|
2278
|
+
const refName = ast.resolveRefName(node);
|
|
2554
2279
|
if (refName) refs.add(refName);
|
|
2555
2280
|
}
|
|
2556
2281
|
if (node.deprecated) Diagnostics.report({
|
|
@@ -2623,17 +2348,24 @@ const adapterOasName = "oas";
|
|
|
2623
2348
|
const adapterOas = createAdapter((options) => {
|
|
2624
2349
|
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;
|
|
2625
2350
|
const parserOptions = {
|
|
2626
|
-
...DEFAULT_PARSER_OPTIONS,
|
|
2627
2351
|
dateType,
|
|
2628
2352
|
integerType,
|
|
2629
2353
|
unknownType,
|
|
2630
2354
|
emptySchemaType,
|
|
2631
2355
|
enumSuffix
|
|
2632
2356
|
};
|
|
2357
|
+
const resolvedOptions = {
|
|
2358
|
+
validate,
|
|
2359
|
+
contentType,
|
|
2360
|
+
server,
|
|
2361
|
+
discriminator,
|
|
2362
|
+
enums,
|
|
2363
|
+
...parserOptions
|
|
2364
|
+
};
|
|
2633
2365
|
let parsedDocument = null;
|
|
2634
2366
|
const inputCache = /* @__PURE__ */ new WeakMap();
|
|
2635
2367
|
function parseInput({ document, refs, schemas, parser }) {
|
|
2636
|
-
const { parseSchema
|
|
2368
|
+
const { parseSchema } = parser;
|
|
2637
2369
|
const parsedByName = /* @__PURE__ */ new Map();
|
|
2638
2370
|
const refAliasMap = /* @__PURE__ */ new Map();
|
|
2639
2371
|
const enumNames = [];
|
|
@@ -2643,7 +2375,7 @@ const adapterOas = createAdapter((options) => {
|
|
|
2643
2375
|
const node = parseSchema({
|
|
2644
2376
|
schema,
|
|
2645
2377
|
name
|
|
2646
|
-
}
|
|
2378
|
+
});
|
|
2647
2379
|
parsedByName.set(name, node);
|
|
2648
2380
|
const refs = scanSchema({
|
|
2649
2381
|
node,
|
|
@@ -2651,29 +2383,31 @@ const adapterOas = createAdapter((options) => {
|
|
|
2651
2383
|
});
|
|
2652
2384
|
if (node.name) refGraph.set(node.name, refs);
|
|
2653
2385
|
if (node.type === "ref" && node.name && node.name !== name) refAliasMap.set(name, node);
|
|
2654
|
-
if (narrowSchema(node, "enum") && node.name) enumNames.push(node.name);
|
|
2386
|
+
if (ast.narrowSchema(node, "enum") && node.name) enumNames.push(node.name);
|
|
2655
2387
|
if (discriminator === "propagate" && (schema.oneOf ?? schema.anyOf) && schema.discriminator?.propertyName) discriminatorParentNodes.push(node);
|
|
2656
2388
|
}
|
|
2657
|
-
const circularNames = [...findCircularSchemasFromGraph(refGraph)];
|
|
2389
|
+
const circularNames = [...ast.findCircularSchemasFromGraph(refGraph)];
|
|
2658
2390
|
const discriminatorChildMap = discriminatorParentNodes.length > 0 ? buildDiscriminatorChildMap(discriminatorParentNodes) : null;
|
|
2659
|
-
const operationNodes =
|
|
2660
|
-
|
|
2661
|
-
|
|
2662
|
-
|
|
2663
|
-
|
|
2391
|
+
const operationNodes = getOperations(document, refs).map((operation) => parseOperation({
|
|
2392
|
+
operation,
|
|
2393
|
+
refs,
|
|
2394
|
+
contentType,
|
|
2395
|
+
options: parserOptions,
|
|
2396
|
+
parseSchema
|
|
2397
|
+
}));
|
|
2664
2398
|
const promotedEnums = enums === "root" ? collectInlineEnums([...parsedByName.values(), ...operationNodes], new Set(Object.keys(schemas))) : null;
|
|
2665
2399
|
if (promotedEnums) for (const name of promotedEnums.keys()) enumNames.push(name);
|
|
2666
2400
|
const schemaNodes = promotedEnums ? [...promotedEnums.values()] : [];
|
|
2667
2401
|
for (const name of Object.keys(schemas)) {
|
|
2668
|
-
const
|
|
2669
|
-
const
|
|
2670
|
-
|
|
2402
|
+
const aliasTarget = refAliasMap.get(name)?.name;
|
|
2403
|
+
const aliased = aliasTarget ? parsedByName.get(aliasTarget) : void 0;
|
|
2404
|
+
const parsed = parsedByName.get(name);
|
|
2405
|
+
const child = discriminatorChildMap?.get(name);
|
|
2406
|
+
const base = child ? patchDiscriminatorNode(parsed, child) : parsed;
|
|
2407
|
+
const node = aliased ? {
|
|
2408
|
+
...aliased,
|
|
2671
2409
|
name
|
|
2672
|
-
} :
|
|
2673
|
-
const parsed = parsedByName.get(name);
|
|
2674
|
-
const child = discriminatorChildMap?.get(name);
|
|
2675
|
-
return child ? patchDiscriminatorNode(parsed, child) : parsed;
|
|
2676
|
-
})();
|
|
2410
|
+
} : base;
|
|
2677
2411
|
schemaNodes.push(promotedEnums ? refPromotedEnums(node, promotedEnums) : node);
|
|
2678
2412
|
}
|
|
2679
2413
|
const operations = promotedEnums ? operationNodes.map((node) => refPromotedEnums(node, promotedEnums)) : operationNodes;
|
|
@@ -2695,20 +2429,7 @@ const adapterOas = createAdapter((options) => {
|
|
|
2695
2429
|
}
|
|
2696
2430
|
return {
|
|
2697
2431
|
name: "oas",
|
|
2698
|
-
|
|
2699
|
-
return {
|
|
2700
|
-
validate,
|
|
2701
|
-
contentType,
|
|
2702
|
-
server,
|
|
2703
|
-
discriminator,
|
|
2704
|
-
enums,
|
|
2705
|
-
dateType,
|
|
2706
|
-
integerType,
|
|
2707
|
-
unknownType,
|
|
2708
|
-
emptySchemaType,
|
|
2709
|
-
enumSuffix
|
|
2710
|
-
};
|
|
2711
|
-
},
|
|
2432
|
+
options: resolvedOptions,
|
|
2712
2433
|
get document() {
|
|
2713
2434
|
return parsedDocument;
|
|
2714
2435
|
},
|
|
@@ -2737,8 +2458,8 @@ const adapterOas = createAdapter((options) => {
|
|
|
2737
2458
|
parser: createSchemaParser({
|
|
2738
2459
|
document,
|
|
2739
2460
|
refs,
|
|
2740
|
-
|
|
2741
|
-
|
|
2461
|
+
renames,
|
|
2462
|
+
options: parserOptions
|
|
2742
2463
|
})
|
|
2743
2464
|
});
|
|
2744
2465
|
})();
|