@kubb/adapter-oas 5.0.0-beta.61 → 5.0.0-beta.63
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 +262 -137
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.ts +2 -2
- package/dist/index.js +262 -137
- package/dist/index.js.map +1 -1
- package/package.json +3 -3
- package/src/adapter.ts +1 -1
- package/src/constants.ts +13 -14
- package/src/dedupe.ts +237 -0
- package/src/dialect.ts +10 -6
- package/src/discriminator.ts +1 -1
- package/src/factory.ts +1 -1
- package/src/mime.ts +2 -3
- package/src/operation.ts +2 -2
- package/src/parser.ts +48 -42
- package/src/refs.ts +4 -3
- package/src/resolvers.ts +13 -5
- package/src/stream.ts +19 -81
- package/src/types.ts +1 -1
package/dist/index.cjs
CHANGED
|
@@ -37,10 +37,9 @@ let _kubb_ast = require("@kubb/ast");
|
|
|
37
37
|
*
|
|
38
38
|
* @example
|
|
39
39
|
* ```ts
|
|
40
|
-
* import { DEFAULT_PARSER_OPTIONS } from '@kubb/adapter-oas'
|
|
40
|
+
* import { DEFAULT_PARSER_OPTIONS, parseOas } from '@kubb/adapter-oas'
|
|
41
41
|
*
|
|
42
|
-
* const
|
|
43
|
-
* const root = parser.parse({ ...DEFAULT_PARSER_OPTIONS, dateType: 'date' })
|
|
42
|
+
* const { root } = parseOas(document, { ...DEFAULT_PARSER_OPTIONS, dateType: 'date' })
|
|
44
43
|
* ```
|
|
45
44
|
*/
|
|
46
45
|
const DEFAULT_PARSER_OPTIONS = {
|
|
@@ -111,12 +110,24 @@ const structuralKeys = new Set([
|
|
|
111
110
|
"not"
|
|
112
111
|
]);
|
|
113
112
|
/**
|
|
113
|
+
* Formats `convertFormat` maps to a dedicated type without going through `formatMap`:
|
|
114
|
+
* `int64` and the date/time family. Keep this in sync with the `convertFormat`
|
|
115
|
+
* special-cases in `parser.ts`. `isHandledFormat` reads it so the
|
|
116
|
+
* `KUBB_UNSUPPORTED_FORMAT` diagnostic and the parser agree on what is handled.
|
|
117
|
+
*/
|
|
118
|
+
const specialCasedFormats = new Set([
|
|
119
|
+
"int64",
|
|
120
|
+
"date-time",
|
|
121
|
+
"date",
|
|
122
|
+
"time"
|
|
123
|
+
]);
|
|
124
|
+
/**
|
|
114
125
|
* Static map from OAS `format` strings to Kubb `SchemaType` values.
|
|
115
126
|
*
|
|
116
127
|
* Only formats whose AST type differs from the OAS `type` field appear here.
|
|
117
|
-
* Formats that depend on runtime options (`int64`, `date-time`, `date`, `time`) are handled
|
|
118
|
-
* in the parser. `ipv4` and `ipv6` map to their own dedicated schema types. `hostname`
|
|
119
|
-
* `idn-hostname` map to `'url'` as the closest generic string-format type.
|
|
128
|
+
* Formats that depend on runtime options (`int64`, `date-time`, `date`, `time`) are handled
|
|
129
|
+
* separately in the parser. `ipv4` and `ipv6` map to their own dedicated schema types. `hostname`
|
|
130
|
+
* and `idn-hostname` map to `'url'` as the closest generic string-format type.
|
|
120
131
|
*
|
|
121
132
|
* @example
|
|
122
133
|
* ```ts
|
|
@@ -127,18 +138,6 @@ const structuralKeys = new Set([
|
|
|
127
138
|
* formatMap['float'] // 'number'
|
|
128
139
|
* ```
|
|
129
140
|
*/
|
|
130
|
-
/**
|
|
131
|
-
* Formats `convertFormat` maps to a dedicated type without going through `formatMap`:
|
|
132
|
-
* `int64` and the date/time family. Keep this in sync with the `convertFormat`
|
|
133
|
-
* special-cases in `parser.ts`. `isHandledFormat` reads it so the
|
|
134
|
-
* `KUBB_UNSUPPORTED_FORMAT` diagnostic and the parser agree on what is handled.
|
|
135
|
-
*/
|
|
136
|
-
const specialCasedFormats = new Set([
|
|
137
|
-
"int64",
|
|
138
|
-
"date-time",
|
|
139
|
-
"date",
|
|
140
|
-
"time"
|
|
141
|
-
]);
|
|
142
141
|
const formatMap = {
|
|
143
142
|
uuid: "uuid",
|
|
144
143
|
email: "email",
|
|
@@ -659,7 +658,7 @@ function isDiscriminator(obj) {
|
|
|
659
658
|
//#endregion
|
|
660
659
|
//#region src/factory.ts
|
|
661
660
|
/**
|
|
662
|
-
* Loads and
|
|
661
|
+
* Loads and bundles an OpenAPI document, returning the raw `Document`.
|
|
663
662
|
*
|
|
664
663
|
* Accepts a file path string or an already-parsed document object. File paths and URLs are
|
|
665
664
|
* bundled via `api-ref-bundler`, hoisting external file schemas into named `components.schemas`
|
|
@@ -770,17 +769,180 @@ async function validateDocument(document, { throwOnError = false } = {}) {
|
|
|
770
769
|
}
|
|
771
770
|
}
|
|
772
771
|
//#endregion
|
|
772
|
+
//#region src/dedupe.ts
|
|
773
|
+
/**
|
|
774
|
+
* Minimum occurrences before a shape is deduplicated.
|
|
775
|
+
*/
|
|
776
|
+
const MIN_OCCURRENCES = 2;
|
|
777
|
+
/**
|
|
778
|
+
* Builds the shared `ref` replacement for a duplicate occurrence, carrying the
|
|
779
|
+
* usage-slot and documentation fields that are not part of the shared type.
|
|
780
|
+
*/
|
|
781
|
+
function createRefNode(node, target) {
|
|
782
|
+
return _kubb_core.ast.factory.createSchema({
|
|
783
|
+
type: "ref",
|
|
784
|
+
name: target.name,
|
|
785
|
+
ref: target.ref,
|
|
786
|
+
optional: node.optional,
|
|
787
|
+
nullish: node.nullish,
|
|
788
|
+
readOnly: node.readOnly,
|
|
789
|
+
writeOnly: node.writeOnly,
|
|
790
|
+
deprecated: node.deprecated,
|
|
791
|
+
description: node.description,
|
|
792
|
+
default: node.default,
|
|
793
|
+
example: node.example
|
|
794
|
+
});
|
|
795
|
+
}
|
|
796
|
+
/**
|
|
797
|
+
* Strips usage-slot flags from an extracted definition and applies its name.
|
|
798
|
+
* A standalone definition is never optional, so `optional`/`nullish` are cleared.
|
|
799
|
+
*/
|
|
800
|
+
function cleanDefinition(node, name) {
|
|
801
|
+
return {
|
|
802
|
+
...node,
|
|
803
|
+
name,
|
|
804
|
+
optional: void 0,
|
|
805
|
+
nullish: void 0
|
|
806
|
+
};
|
|
807
|
+
}
|
|
808
|
+
/**
|
|
809
|
+
* Returns `true` when a node is eligible for deduplication. Only enums and objects qualify, and
|
|
810
|
+
* object shapes that take part in a circular chain are rejected so recursive structures are not
|
|
811
|
+
* extracted (which would break the cycle).
|
|
812
|
+
*/
|
|
813
|
+
function isCandidate(node, circularSchemas) {
|
|
814
|
+
if (node.type === "enum") return true;
|
|
815
|
+
if (node.type !== "object") return false;
|
|
816
|
+
if (node.name && circularSchemas.has(node.name)) return false;
|
|
817
|
+
return !(0, _kubb_ast_utils.containsCircularRef)(node, { circularSchemas });
|
|
818
|
+
}
|
|
819
|
+
/**
|
|
820
|
+
* Produces the name for an inline shape with no existing named component, resolving
|
|
821
|
+
* collisions against `usedNames`. Returns `null` for an unnamed shape, which stays inline.
|
|
822
|
+
*/
|
|
823
|
+
function nameFor(node, usedNames) {
|
|
824
|
+
const base = node.name;
|
|
825
|
+
if (!base) return null;
|
|
826
|
+
let name = base;
|
|
827
|
+
let counter = 2;
|
|
828
|
+
while (usedNames.has(name)) name = `${base}${counter++}`;
|
|
829
|
+
usedNames.add(name);
|
|
830
|
+
return name;
|
|
831
|
+
}
|
|
832
|
+
/**
|
|
833
|
+
* Scans a forest of schema and operation nodes and produces a {@link Plan}.
|
|
834
|
+
*
|
|
835
|
+
* A shape that occurs at least {@link MIN_OCCURRENCES} times is deduplicated: if any occurrence is
|
|
836
|
+
* a named top-level schema, the first one becomes the target (so other top-level duplicates and
|
|
837
|
+
* inline copies turn into references to it). Other top-level names with the same content are
|
|
838
|
+
* recorded as aliases. Otherwise a new definition is extracted using {@link nameFor}. The returned
|
|
839
|
+
* plan rewrites nodes against those decisions.
|
|
840
|
+
*
|
|
841
|
+
* @example
|
|
842
|
+
* ```ts
|
|
843
|
+
* const dedupePlan = plan([...schemaNodes, ...operationNodes], { circularSchemas, usedNames })
|
|
844
|
+
* ```
|
|
845
|
+
*/
|
|
846
|
+
function plan(roots, context) {
|
|
847
|
+
const { circularSchemas, usedNames } = context;
|
|
848
|
+
const topLevelNodes = /* @__PURE__ */ new Set();
|
|
849
|
+
const groups = /* @__PURE__ */ new Map();
|
|
850
|
+
for (const root of roots) {
|
|
851
|
+
if (root.kind === "Schema") topLevelNodes.add(root);
|
|
852
|
+
for (const schemaNode of _kubb_core.ast.collect(root, { schema: (node) => node })) {
|
|
853
|
+
if (!isCandidate(schemaNode, circularSchemas)) continue;
|
|
854
|
+
const signature = _kubb_core.ast.signatureOf(schemaNode);
|
|
855
|
+
const isTopLevel = topLevelNodes.has(schemaNode) && !!schemaNode.name;
|
|
856
|
+
const group = groups.get(signature);
|
|
857
|
+
if (group) {
|
|
858
|
+
group.count++;
|
|
859
|
+
if (isTopLevel) group.topLevelNames.push(schemaNode.name);
|
|
860
|
+
} else groups.set(signature, {
|
|
861
|
+
count: 1,
|
|
862
|
+
representative: schemaNode,
|
|
863
|
+
topLevelNames: isTopLevel ? [schemaNode.name] : []
|
|
864
|
+
});
|
|
865
|
+
}
|
|
866
|
+
}
|
|
867
|
+
const bySignature = /* @__PURE__ */ new Map();
|
|
868
|
+
const byName = /* @__PURE__ */ new Map();
|
|
869
|
+
const pendingExtractions = [];
|
|
870
|
+
for (const [signature, group] of groups) {
|
|
871
|
+
if (group.count < MIN_OCCURRENCES) continue;
|
|
872
|
+
const [firstName, ...duplicateNames] = group.topLevelNames;
|
|
873
|
+
if (firstName) {
|
|
874
|
+
const target = {
|
|
875
|
+
name: firstName,
|
|
876
|
+
ref: `${SCHEMA_REF_PREFIX}${firstName}`
|
|
877
|
+
};
|
|
878
|
+
bySignature.set(signature, target);
|
|
879
|
+
for (const duplicate of duplicateNames) byName.set(duplicate, target);
|
|
880
|
+
continue;
|
|
881
|
+
}
|
|
882
|
+
const name = nameFor(group.representative, usedNames);
|
|
883
|
+
if (!name) continue;
|
|
884
|
+
bySignature.set(signature, {
|
|
885
|
+
name,
|
|
886
|
+
ref: `${SCHEMA_REF_PREFIX}${name}`
|
|
887
|
+
});
|
|
888
|
+
pendingExtractions.push({
|
|
889
|
+
name,
|
|
890
|
+
representative: group.representative
|
|
891
|
+
});
|
|
892
|
+
}
|
|
893
|
+
function rewrite(node, skipRootMatch) {
|
|
894
|
+
if (bySignature.size === 0 && byName.size === 0) return node;
|
|
895
|
+
const root = node;
|
|
896
|
+
return _kubb_core.ast.transform(node, { schema(schemaNode) {
|
|
897
|
+
if (schemaNode.type === "ref") {
|
|
898
|
+
const refName = schemaNode.ref ? (0, _kubb_ast_utils.extractRefName)(schemaNode.ref) : schemaNode.name;
|
|
899
|
+
const target = refName ? byName.get(refName) : void 0;
|
|
900
|
+
return target ? {
|
|
901
|
+
...schemaNode,
|
|
902
|
+
name: target.name,
|
|
903
|
+
ref: target.ref
|
|
904
|
+
} : void 0;
|
|
905
|
+
}
|
|
906
|
+
if (skipRootMatch && schemaNode === root) return void 0;
|
|
907
|
+
const target = bySignature.get(_kubb_core.ast.signatureOf(schemaNode));
|
|
908
|
+
if (!target) return void 0;
|
|
909
|
+
return createRefNode(schemaNode, target);
|
|
910
|
+
} });
|
|
911
|
+
}
|
|
912
|
+
return {
|
|
913
|
+
extracted: pendingExtractions.map(({ name, representative }) => cleanDefinition(rewrite(representative, true), name)),
|
|
914
|
+
apply(node) {
|
|
915
|
+
return rewrite(node, false);
|
|
916
|
+
},
|
|
917
|
+
applyTopLevel(node) {
|
|
918
|
+
const target = bySignature.get(_kubb_core.ast.signatureOf(node));
|
|
919
|
+
if (target && target.name !== node.name) return _kubb_core.ast.factory.createSchema({
|
|
920
|
+
type: "ref",
|
|
921
|
+
name: node.name ?? null,
|
|
922
|
+
ref: target.ref,
|
|
923
|
+
description: node.description,
|
|
924
|
+
deprecated: node.deprecated
|
|
925
|
+
});
|
|
926
|
+
return rewrite(node, true);
|
|
927
|
+
},
|
|
928
|
+
isAlias(name) {
|
|
929
|
+
return byName.has(name);
|
|
930
|
+
}
|
|
931
|
+
};
|
|
932
|
+
}
|
|
933
|
+
//#endregion
|
|
773
934
|
//#region src/refs.ts
|
|
774
935
|
const _refCache = /* @__PURE__ */ new WeakMap();
|
|
775
936
|
/**
|
|
776
937
|
* Resolves a local JSON pointer reference from a document.
|
|
777
938
|
*
|
|
778
|
-
* Accepts `#/...` refs. Returns `null` for empty or non-local
|
|
779
|
-
*
|
|
939
|
+
* Accepts `#/...` refs. Returns `null` for an empty or non-local ref. When the pointer cannot be
|
|
940
|
+
* resolved, reports a `refNotFound` diagnostic into the active build and returns `null`. Outside a
|
|
941
|
+
* build there is no sink to collect it, so it throws instead.
|
|
780
942
|
*
|
|
781
943
|
* @example
|
|
782
944
|
* ```ts
|
|
783
|
-
* resolveRef<SchemaObject>(document, '#/components/schemas/Pet')
|
|
945
|
+
* resolveRef<SchemaObject>(document, '#/components/schemas/Pet')
|
|
784
946
|
* ```
|
|
785
947
|
*/
|
|
786
948
|
function resolveRef(document, $ref) {
|
|
@@ -855,18 +1017,21 @@ function dereferenceWithRef(document, schema) {
|
|
|
855
1017
|
* const parser = createSchemaParser(context, oasDialect) // explicit
|
|
856
1018
|
* ```
|
|
857
1019
|
*/
|
|
858
|
-
const oasDialect = _kubb_core.ast.
|
|
1020
|
+
const oasDialect = _kubb_core.ast.defineDialect({
|
|
859
1021
|
name: "oas",
|
|
860
|
-
|
|
861
|
-
|
|
862
|
-
|
|
863
|
-
|
|
864
|
-
|
|
1022
|
+
schema: {
|
|
1023
|
+
isNullable,
|
|
1024
|
+
isReference,
|
|
1025
|
+
isDiscriminator,
|
|
1026
|
+
isBinary: (schema) => schema.type === "string" && schema.contentMediaType === "application/octet-stream",
|
|
1027
|
+
resolveRef
|
|
1028
|
+
},
|
|
1029
|
+
dedupe: { plan }
|
|
865
1030
|
});
|
|
866
1031
|
//#endregion
|
|
867
1032
|
//#region src/discriminator.ts
|
|
868
1033
|
/**
|
|
869
|
-
*
|
|
1034
|
+
* Maps each child schema name to its discriminator patch data by scanning the given
|
|
870
1035
|
* top-level AST schema nodes for union schemas that carry a `discriminatorPropertyName`.
|
|
871
1036
|
*
|
|
872
1037
|
* The streaming path calls this on a small pre-parsed subset of schemas (only the
|
|
@@ -982,9 +1147,8 @@ function findDiscriminator(mapping, ref) {
|
|
|
982
1147
|
/**
|
|
983
1148
|
* MIME type fragments that mark a media type as JSON-like.
|
|
984
1149
|
*
|
|
985
|
-
*
|
|
986
|
-
*
|
|
987
|
-
* `application/vnd.api+json`.
|
|
1150
|
+
* A content type is JSON when it contains any of these substrings. The `+json` entry catches
|
|
1151
|
+
* structured-syntax suffixes such as `application/vnd.api+json`.
|
|
988
1152
|
*/
|
|
989
1153
|
const jsonMimeFragments = [
|
|
990
1154
|
"application/json",
|
|
@@ -1080,8 +1244,8 @@ function getRequestContent({ document, operation, mediaType }) {
|
|
|
1080
1244
|
return available ? [available, content[available]] : false;
|
|
1081
1245
|
}
|
|
1082
1246
|
/**
|
|
1083
|
-
* Returns the primary request content type. Prefers a JSON-like media type (the last one wins
|
|
1084
|
-
*
|
|
1247
|
+
* Returns the primary request content type. Prefers a JSON-like media type (the last one wins
|
|
1248
|
+
* when several are declared), then the first declared one, defaulting to `'application/json'`.
|
|
1085
1249
|
*/
|
|
1086
1250
|
function getRequestContentType({ document, operation }) {
|
|
1087
1251
|
const content = getRequestBodyContent({
|
|
@@ -1176,9 +1340,9 @@ function getSchemaType(format) {
|
|
|
1176
1340
|
return formatMap[format] ?? null;
|
|
1177
1341
|
}
|
|
1178
1342
|
/**
|
|
1179
|
-
* Whether the parser maps `format` to a dedicated type. True for any `formatMap` entry
|
|
1180
|
-
* `specialCasedFormats` `convertFormat` handles directly. False means the format falls back to
|
|
1181
|
-
* base type, which is what `KUBB_UNSUPPORTED_FORMAT` flags. Reading both sources keeps the
|
|
1343
|
+
* Whether the parser maps `format` to a dedicated type. True for any `formatMap` entry, plus the
|
|
1344
|
+
* `specialCasedFormats` that `convertFormat` handles directly. False means the format falls back to
|
|
1345
|
+
* the base type, which is what `KUBB_UNSUPPORTED_FORMAT` flags. Reading both sources keeps the
|
|
1182
1346
|
* diagnostic in step with the parser as `formatMap` grows.
|
|
1183
1347
|
*/
|
|
1184
1348
|
function isHandledFormat(format) {
|
|
@@ -1196,7 +1360,7 @@ function getPrimitiveType(type) {
|
|
|
1196
1360
|
/**
|
|
1197
1361
|
* Returns all parameters for an operation, merging path-level and operation-level entries.
|
|
1198
1362
|
* Operation-level parameters override path-level ones with the same `in:name` key.
|
|
1199
|
-
* `$ref`
|
|
1363
|
+
* Each `$ref` parameter is dereferenced via `dereferenceWithRef` before merging.
|
|
1200
1364
|
*
|
|
1201
1365
|
* @example
|
|
1202
1366
|
* ```ts
|
|
@@ -1297,7 +1461,10 @@ function getRequestSchema(document, operation, options = {}) {
|
|
|
1297
1461
|
* ```ts
|
|
1298
1462
|
* flattenSchema({ allOf: [{ description: 'A pet' }], type: 'object', properties: {} })
|
|
1299
1463
|
* // { type: 'object', properties: {}, description: 'A pet' }
|
|
1464
|
+
* ```
|
|
1300
1465
|
*
|
|
1466
|
+
* @example
|
|
1467
|
+
* ```ts
|
|
1301
1468
|
* flattenSchema({ allOf: [{ $ref: '#/components/schemas/Pet' }] })
|
|
1302
1469
|
* // returned unchanged, contains a $ref
|
|
1303
1470
|
* ```
|
|
@@ -1584,6 +1751,21 @@ function normalizeArrayEnum(schema) {
|
|
|
1584
1751
|
};
|
|
1585
1752
|
}
|
|
1586
1753
|
/**
|
|
1754
|
+
* Builds a `null` scalar node carrying the schema's documentation. Shared by the `const: null`
|
|
1755
|
+
* and the drf-spectacular `NullEnum` (`{ enum: [null] }`) branches, which render identically.
|
|
1756
|
+
*/
|
|
1757
|
+
function createNullSchema(schema, name) {
|
|
1758
|
+
return _kubb_core.ast.factory.createSchema({
|
|
1759
|
+
type: "null",
|
|
1760
|
+
primitive: "null",
|
|
1761
|
+
name,
|
|
1762
|
+
title: schema.title,
|
|
1763
|
+
description: schema.description,
|
|
1764
|
+
deprecated: schema.deprecated,
|
|
1765
|
+
format: schema.format
|
|
1766
|
+
});
|
|
1767
|
+
}
|
|
1768
|
+
/**
|
|
1587
1769
|
* Names the inline enums on a property's schema, and on each item when the property is a tuple, from
|
|
1588
1770
|
* the parent and property name. Wraps `macroEnumName` at the property construction site.
|
|
1589
1771
|
*/
|
|
@@ -1639,7 +1821,7 @@ function createSchemaParser(ctx, dialect = oasDialect) {
|
|
|
1639
1821
|
if (refPath && !resolvingRefs.has(refPath)) {
|
|
1640
1822
|
if (!resolvedRefCache.has(refPath)) {
|
|
1641
1823
|
try {
|
|
1642
|
-
const referenced = dialect.resolveRef(document, refPath);
|
|
1824
|
+
const referenced = dialect.schema.resolveRef(document, refPath);
|
|
1643
1825
|
if (referenced) {
|
|
1644
1826
|
resolvingRefs.add(refPath);
|
|
1645
1827
|
resolvedSchema = parseSchema({ schema: referenced }, rawOptions);
|
|
@@ -1688,13 +1870,13 @@ function createSchemaParser(ctx, dialect = oasDialect) {
|
|
|
1688
1870
|
}
|
|
1689
1871
|
const filteredDiscriminantValues = [];
|
|
1690
1872
|
const allOfMembers = schema.allOf.filter((item) => {
|
|
1691
|
-
if (!dialect.isReference(item) || !name) return true;
|
|
1692
|
-
const deref = dialect.resolveRef(document, item.$ref);
|
|
1693
|
-
if (!deref || !dialect.isDiscriminator(deref)) return true;
|
|
1873
|
+
if (!dialect.schema.isReference(item) || !name) return true;
|
|
1874
|
+
const deref = dialect.schema.resolveRef(document, item.$ref);
|
|
1875
|
+
if (!deref || !dialect.schema.isDiscriminator(deref)) return true;
|
|
1694
1876
|
const parentUnion = deref.oneOf ?? deref.anyOf;
|
|
1695
1877
|
if (!parentUnion) return true;
|
|
1696
1878
|
const childRef = `${SCHEMA_REF_PREFIX}${name}`;
|
|
1697
|
-
const inOneOf = parentUnion.some((oneOfItem) => dialect.isReference(oneOfItem) && oneOfItem.$ref === childRef);
|
|
1879
|
+
const inOneOf = parentUnion.some((oneOfItem) => dialect.schema.isReference(oneOfItem) && oneOfItem.$ref === childRef);
|
|
1698
1880
|
const inMapping = Object.values(deref.discriminator.mapping ?? {}).some((v) => v === childRef);
|
|
1699
1881
|
if (inOneOf || inMapping) {
|
|
1700
1882
|
const discriminatorValue = findDiscriminator(deref.discriminator.mapping, childRef);
|
|
@@ -1715,9 +1897,9 @@ function createSchemaParser(ctx, dialect = oasDialect) {
|
|
|
1715
1897
|
const missingRequired = schema.required.filter((key) => !outerKeys.has(key));
|
|
1716
1898
|
if (missingRequired.length) {
|
|
1717
1899
|
const resolvedMembers = schema.allOf.flatMap((item) => {
|
|
1718
|
-
if (!dialect.isReference(item)) return [item];
|
|
1719
|
-
const deref = dialect.resolveRef(document, item.$ref);
|
|
1720
|
-
return deref && !dialect.isReference(deref) ? [deref] : [];
|
|
1900
|
+
if (!dialect.schema.isReference(item)) return [item];
|
|
1901
|
+
const deref = dialect.schema.resolveRef(document, item.$ref);
|
|
1902
|
+
return deref && !dialect.schema.isReference(deref) ? [deref] : [];
|
|
1721
1903
|
});
|
|
1722
1904
|
for (const key of missingRequired) for (const resolved of resolvedMembers) if (resolved.properties?.[key]) {
|
|
1723
1905
|
allOfMembers.push(parseSchema({
|
|
@@ -1762,10 +1944,10 @@ function createSchemaParser(ctx, dialect = oasDialect) {
|
|
|
1762
1944
|
const strategy = schema.oneOf ? "one" : "any";
|
|
1763
1945
|
const unionBase = {
|
|
1764
1946
|
...buildSchemaNode(schema, name, nullable, defaultValue),
|
|
1765
|
-
discriminatorPropertyName: dialect.isDiscriminator(schema) ? schema.discriminator.propertyName : void 0,
|
|
1947
|
+
discriminatorPropertyName: dialect.schema.isDiscriminator(schema) ? schema.discriminator.propertyName : void 0,
|
|
1766
1948
|
strategy
|
|
1767
1949
|
};
|
|
1768
|
-
const discriminator = dialect.isDiscriminator(schema) ? schema.discriminator : void 0;
|
|
1950
|
+
const discriminator = dialect.schema.isDiscriminator(schema) ? schema.discriminator : void 0;
|
|
1769
1951
|
const sharedPropertiesNode = schema.properties ? (() => {
|
|
1770
1952
|
const { oneOf: _oneOf, anyOf: _anyOf, ...schemaWithoutUnion } = schema;
|
|
1771
1953
|
return parseSchema({
|
|
@@ -1775,7 +1957,7 @@ function createSchemaParser(ctx, dialect = oasDialect) {
|
|
|
1775
1957
|
})() : void 0;
|
|
1776
1958
|
if (sharedPropertiesNode || discriminator?.mapping) {
|
|
1777
1959
|
const members = unionMembers.map((s) => {
|
|
1778
|
-
const ref = dialect.isReference(s) ? s.$ref : void 0;
|
|
1960
|
+
const ref = dialect.schema.isReference(s) ? s.$ref : void 0;
|
|
1779
1961
|
const discriminatorValue = findDiscriminator(discriminator?.mapping, ref);
|
|
1780
1962
|
const memberNode = parseSchema({
|
|
1781
1963
|
schema: s,
|
|
@@ -1821,15 +2003,7 @@ function createSchemaParser(ctx, dialect = oasDialect) {
|
|
|
1821
2003
|
*/
|
|
1822
2004
|
function convertConst({ schema, name, nullable, defaultValue }) {
|
|
1823
2005
|
const constValue = schema.const;
|
|
1824
|
-
if (constValue === null) return
|
|
1825
|
-
type: "null",
|
|
1826
|
-
primitive: "null",
|
|
1827
|
-
name,
|
|
1828
|
-
title: schema.title,
|
|
1829
|
-
description: schema.description,
|
|
1830
|
-
deprecated: schema.deprecated,
|
|
1831
|
-
format: schema.format
|
|
1832
|
-
});
|
|
2006
|
+
if (constValue === null) return createNullSchema(schema, name);
|
|
1833
2007
|
const constPrimitive = getPrimitiveType(typeof constValue === "number" ? "number" : typeof constValue === "boolean" ? "boolean" : "string");
|
|
1834
2008
|
return _kubb_core.ast.factory.createSchema({
|
|
1835
2009
|
type: "enum",
|
|
@@ -1918,15 +2092,7 @@ function createSchemaParser(ctx, dialect = oasDialect) {
|
|
|
1918
2092
|
}, rawOptions);
|
|
1919
2093
|
const nullInEnum = schema.enum.includes(null);
|
|
1920
2094
|
const filteredValues = nullInEnum ? schema.enum.filter((v) => v !== null) : schema.enum;
|
|
1921
|
-
if (nullInEnum && filteredValues.length === 0) return
|
|
1922
|
-
type: "null",
|
|
1923
|
-
primitive: "null",
|
|
1924
|
-
name,
|
|
1925
|
-
title: schema.title,
|
|
1926
|
-
description: schema.description,
|
|
1927
|
-
deprecated: schema.deprecated,
|
|
1928
|
-
format: schema.format
|
|
1929
|
-
});
|
|
2095
|
+
if (nullInEnum && filteredValues.length === 0) return createNullSchema(schema, name);
|
|
1930
2096
|
const enumNullable = nullable || nullInEnum || void 0;
|
|
1931
2097
|
const enumDefault = schema.default === null && enumNullable ? void 0 : schema.default;
|
|
1932
2098
|
const enumPrimitive = getPrimitiveType(type);
|
|
@@ -1976,7 +2142,7 @@ function createSchemaParser(ctx, dialect = oasDialect) {
|
|
|
1976
2142
|
const properties = schema.properties ? Object.entries(schema.properties).map(([propName, propSchema]) => {
|
|
1977
2143
|
const required = Array.isArray(schema.required) ? schema.required.includes(propName) : !!schema.required;
|
|
1978
2144
|
const resolvedPropSchema = propSchema;
|
|
1979
|
-
const propNullable = dialect.isNullable(resolvedPropSchema);
|
|
2145
|
+
const propNullable = dialect.schema.isNullable(resolvedPropSchema);
|
|
1980
2146
|
const schemaNode = nameEnums(parseSchema({
|
|
1981
2147
|
schema: resolvedPropSchema,
|
|
1982
2148
|
name: (0, _kubb_ast_utils.childName)(name, propName)
|
|
@@ -2013,7 +2179,7 @@ function createSchemaParser(ctx, dialect = oasDialect) {
|
|
|
2013
2179
|
maxProperties: schema.maxProperties,
|
|
2014
2180
|
...buildSchemaNode(schema, name, nullable, defaultValue)
|
|
2015
2181
|
});
|
|
2016
|
-
if (dialect.isDiscriminator(schema) && schema.discriminator.mapping) {
|
|
2182
|
+
if (dialect.schema.isDiscriminator(schema) && schema.discriminator.mapping) {
|
|
2017
2183
|
const discPropName = schema.discriminator.propertyName;
|
|
2018
2184
|
const values = Object.keys(schema.discriminator.mapping);
|
|
2019
2185
|
const enumName = name ? (0, _kubb_ast_utils.enumPropName)(name, discPropName, options.enumSuffix) : void 0;
|
|
@@ -2157,7 +2323,7 @@ function createSchemaParser(ctx, dialect = oasDialect) {
|
|
|
2157
2323
|
const schemaRules = [
|
|
2158
2324
|
{
|
|
2159
2325
|
name: "ref",
|
|
2160
|
-
match: ({ schema }) => dialect.isReference(schema),
|
|
2326
|
+
match: ({ schema }) => dialect.schema.isReference(schema),
|
|
2161
2327
|
convert: convertRef
|
|
2162
2328
|
},
|
|
2163
2329
|
{
|
|
@@ -2182,7 +2348,7 @@ function createSchemaParser(ctx, dialect = oasDialect) {
|
|
|
2182
2348
|
},
|
|
2183
2349
|
{
|
|
2184
2350
|
name: "blob",
|
|
2185
|
-
match: ({ schema }) => dialect.isBinary(schema),
|
|
2351
|
+
match: ({ schema }) => dialect.schema.isBinary(schema),
|
|
2186
2352
|
convert: convertBlob
|
|
2187
2353
|
},
|
|
2188
2354
|
{
|
|
@@ -2263,7 +2429,7 @@ function createSchemaParser(ctx, dialect = oasDialect) {
|
|
|
2263
2429
|
schema: flattenedSchema,
|
|
2264
2430
|
name
|
|
2265
2431
|
}, rawOptions);
|
|
2266
|
-
const nullable = dialect.isNullable(schema) || void 0;
|
|
2432
|
+
const nullable = dialect.schema.isNullable(schema) || void 0;
|
|
2267
2433
|
const schemaCtx = {
|
|
2268
2434
|
schema,
|
|
2269
2435
|
name,
|
|
@@ -2340,7 +2506,7 @@ function createSchemaParser(ctx, dialect = oasDialect) {
|
|
|
2340
2506
|
const keys = [];
|
|
2341
2507
|
for (const key in schema.properties) {
|
|
2342
2508
|
const prop = schema.properties[key];
|
|
2343
|
-
if (prop && !dialect.isReference(prop) && prop[flag]) keys.push(key);
|
|
2509
|
+
if (prop && !dialect.schema.isReference(prop) && prop[flag]) keys.push(key);
|
|
2344
2510
|
}
|
|
2345
2511
|
return keys.length ? keys : null;
|
|
2346
2512
|
}
|
|
@@ -2357,14 +2523,14 @@ function createSchemaParser(ctx, dialect = oasDialect) {
|
|
|
2357
2523
|
const content = allContentTypes.flatMap((ct) => {
|
|
2358
2524
|
const schema = getRequestSchema(document, operation, { contentType: ct });
|
|
2359
2525
|
if (!schema) return [];
|
|
2360
|
-
return [{
|
|
2526
|
+
return [_kubb_core.ast.factory.createContent({
|
|
2361
2527
|
contentType: ct,
|
|
2362
|
-
schema: _kubb_core.ast.
|
|
2528
|
+
schema: _kubb_core.ast.optionality(parseSchema({
|
|
2363
2529
|
schema,
|
|
2364
2530
|
name: requestBodyName
|
|
2365
2531
|
}, options), requestBodyMeta.required),
|
|
2366
2532
|
keysToOmit: collectPropertyKeysByFlag(schema, "readOnly")
|
|
2367
|
-
}];
|
|
2533
|
+
})];
|
|
2368
2534
|
});
|
|
2369
2535
|
const requestBody = content.length > 0 || requestBodyMeta.description ? {
|
|
2370
2536
|
description: requestBodyMeta.description,
|
|
@@ -2389,17 +2555,17 @@ function createSchemaParser(ctx, dialect = oasDialect) {
|
|
|
2389
2555
|
keysToOmit: collectPropertyKeysByFlag(raw, "writeOnly")
|
|
2390
2556
|
};
|
|
2391
2557
|
};
|
|
2392
|
-
const content = (ctx.contentType ? [ctx.contentType] : getResponseBodyContentTypes(document, operation, statusCode)).map((contentType) => ({
|
|
2558
|
+
const content = (ctx.contentType ? [ctx.contentType] : getResponseBodyContentTypes(document, operation, statusCode)).map((contentType) => _kubb_core.ast.factory.createContent({
|
|
2393
2559
|
contentType,
|
|
2394
2560
|
...parseEntrySchema(contentType)
|
|
2395
2561
|
}));
|
|
2396
|
-
if (content.length === 0) content.push({
|
|
2562
|
+
if (content.length === 0) content.push(_kubb_core.ast.factory.createContent({
|
|
2397
2563
|
contentType: getRequestContentType({
|
|
2398
2564
|
document,
|
|
2399
2565
|
operation
|
|
2400
2566
|
}) || "application/json",
|
|
2401
2567
|
...parseEntrySchema(ctx.contentType)
|
|
2402
|
-
});
|
|
2568
|
+
}));
|
|
2403
2569
|
return _kubb_core.ast.factory.createResponse({
|
|
2404
2570
|
statusCode,
|
|
2405
2571
|
description,
|
|
@@ -2407,7 +2573,7 @@ function createSchemaParser(ctx, dialect = oasDialect) {
|
|
|
2407
2573
|
});
|
|
2408
2574
|
});
|
|
2409
2575
|
const pathItem = document.paths?.[operation.path];
|
|
2410
|
-
const pathItemDoc = pathItem && !dialect.isReference(pathItem) ? pathItem : void 0;
|
|
2576
|
+
const pathItemDoc = pathItem && !dialect.schema.isReference(pathItem) ? pathItem : void 0;
|
|
2411
2577
|
const pickDoc = (key) => {
|
|
2412
2578
|
const own = operation.schema[key];
|
|
2413
2579
|
if (typeof own === "string") return own;
|
|
@@ -2492,36 +2658,6 @@ function visit(node, pointer) {
|
|
|
2492
2658
|
//#endregion
|
|
2493
2659
|
//#region src/stream.ts
|
|
2494
2660
|
/**
|
|
2495
|
-
* Builds the deduplication plan from the already-parsed top-level schema nodes plus a
|
|
2496
|
-
* single extra parse pass over operations (so duplicates in request/response bodies are seen).
|
|
2497
|
-
*
|
|
2498
|
-
* Only enums and objects are candidates, and object shapes that reference a circular schema are
|
|
2499
|
-
* rejected to avoid hoisting recursive structures. Inline shapes reuse their context-derived
|
|
2500
|
-
* name (collision-resolved against existing component names). Shapes without a name stay inline.
|
|
2501
|
-
*/
|
|
2502
|
-
function createDedupePlan({ schemaNodes, operationNodes, schemaNames, circularNames }) {
|
|
2503
|
-
const circularSchemas = new Set(circularNames);
|
|
2504
|
-
const usedNames = new Set(schemaNames);
|
|
2505
|
-
return _kubb_core.ast.buildDedupePlan([...schemaNodes, ...operationNodes], {
|
|
2506
|
-
isCandidate: (node) => {
|
|
2507
|
-
if (node.type === "enum") return true;
|
|
2508
|
-
if (node.type !== "object") return false;
|
|
2509
|
-
if (node.name && circularSchemas.has(node.name)) return false;
|
|
2510
|
-
return !(0, _kubb_ast_utils.containsCircularRef)(node, { circularSchemas });
|
|
2511
|
-
},
|
|
2512
|
-
nameFor: (node) => {
|
|
2513
|
-
const base = node.name;
|
|
2514
|
-
if (!base) return null;
|
|
2515
|
-
let name = base;
|
|
2516
|
-
let counter = 2;
|
|
2517
|
-
while (usedNames.has(name)) name = `${base}${counter++}`;
|
|
2518
|
-
usedNames.add(name);
|
|
2519
|
-
return name;
|
|
2520
|
-
},
|
|
2521
|
-
refFor: (name) => `${SCHEMA_REF_PREFIX}${name}`
|
|
2522
|
-
});
|
|
2523
|
-
}
|
|
2524
|
-
/**
|
|
2525
2661
|
* Reads the server URL from the document's `servers` array at `serverIndex`,
|
|
2526
2662
|
* interpolating any `serverVariables` into the URL template.
|
|
2527
2663
|
*
|
|
@@ -2590,18 +2726,17 @@ function preScan({ schemas, parseSchema, parseOperation, document, parserOptions
|
|
|
2590
2726
|
const operationNode = parseOperation(parserOptions, operation);
|
|
2591
2727
|
if (operationNode) operationNodes.push(operationNode);
|
|
2592
2728
|
}
|
|
2593
|
-
|
|
2594
|
-
|
|
2595
|
-
|
|
2596
|
-
|
|
2597
|
-
|
|
2729
|
+
const circularSchemas = new Set(circularNames);
|
|
2730
|
+
const usedNames = new Set(Object.keys(schemas));
|
|
2731
|
+
dedupePlan = oasDialect.dedupe.plan([...allNodes, ...operationNodes], {
|
|
2732
|
+
circularSchemas,
|
|
2733
|
+
usedNames
|
|
2598
2734
|
});
|
|
2599
|
-
for (const definition of dedupePlan.
|
|
2735
|
+
for (const definition of dedupePlan.extracted) if (definition.type === "enum" && definition.name) enumNames.push(definition.name);
|
|
2600
2736
|
}
|
|
2601
|
-
const aliasNames = dedupePlan?.aliasNames;
|
|
2602
2737
|
return {
|
|
2603
2738
|
refAliasMap,
|
|
2604
|
-
enumNames:
|
|
2739
|
+
enumNames: dedupePlan ? enumNames.filter((name) => !dedupePlan.isAlias(name)) : enumNames,
|
|
2605
2740
|
circularNames,
|
|
2606
2741
|
discriminatorChildMap,
|
|
2607
2742
|
dedupePlan
|
|
@@ -2626,39 +2761,29 @@ function preScan({ schemas, parseSchema, parseOperation, document, parserOptions
|
|
|
2626
2761
|
* ```
|
|
2627
2762
|
*/
|
|
2628
2763
|
function createInputStream({ schemas, parseSchema, parseOperation, document, parserOptions, refAliasMap, discriminatorChildMap, dedupePlan, meta }) {
|
|
2629
|
-
const rewriteTopLevelSchema = (node) => {
|
|
2630
|
-
if (!dedupePlan) return node;
|
|
2631
|
-
const canonical = dedupePlan.canonicalBySignature.get(_kubb_core.ast.signatureOf(node));
|
|
2632
|
-
if (canonical && canonical.name !== node.name) return _kubb_core.ast.factory.createSchema({
|
|
2633
|
-
type: "ref",
|
|
2634
|
-
name: node.name ?? null,
|
|
2635
|
-
ref: canonical.ref,
|
|
2636
|
-
description: node.description,
|
|
2637
|
-
deprecated: node.deprecated
|
|
2638
|
-
});
|
|
2639
|
-
return _kubb_core.ast.applyDedupe(node, dedupePlan, true);
|
|
2640
|
-
};
|
|
2641
2764
|
const schemasIterable = { [Symbol.asyncIterator]() {
|
|
2642
2765
|
return (async function* () {
|
|
2643
|
-
if (dedupePlan) for (const definition of dedupePlan.
|
|
2766
|
+
if (dedupePlan) for (const definition of dedupePlan.extracted) yield definition;
|
|
2644
2767
|
for (const [name, schema] of Object.entries(schemas)) {
|
|
2645
|
-
if (dedupePlan?.
|
|
2768
|
+
if (dedupePlan?.isAlias(name)) continue;
|
|
2646
2769
|
const alias = refAliasMap.get(name);
|
|
2647
2770
|
if (alias?.name && schemas[alias.name]) {
|
|
2648
|
-
|
|
2771
|
+
const aliasNode = {
|
|
2649
2772
|
...parseSchema({
|
|
2650
2773
|
schema: schemas[alias.name],
|
|
2651
2774
|
name: alias.name
|
|
2652
2775
|
}, parserOptions),
|
|
2653
2776
|
name
|
|
2654
|
-
}
|
|
2777
|
+
};
|
|
2778
|
+
yield dedupePlan ? dedupePlan.applyTopLevel(aliasNode) : aliasNode;
|
|
2655
2779
|
continue;
|
|
2656
2780
|
}
|
|
2657
2781
|
const parsed = parseSchema({
|
|
2658
2782
|
schema,
|
|
2659
2783
|
name
|
|
2660
2784
|
}, parserOptions);
|
|
2661
|
-
|
|
2785
|
+
const node = discriminatorChildMap?.get(name) ? patchDiscriminatorNode(parsed, discriminatorChildMap.get(name)) : parsed;
|
|
2786
|
+
yield dedupePlan ? dedupePlan.applyTopLevel(node) : node;
|
|
2662
2787
|
}
|
|
2663
2788
|
})();
|
|
2664
2789
|
} };
|
|
@@ -2666,7 +2791,7 @@ function createInputStream({ schemas, parseSchema, parseOperation, document, par
|
|
|
2666
2791
|
return (async function* () {
|
|
2667
2792
|
for (const operation of getOperations(document)) {
|
|
2668
2793
|
const node = parseOperation(parserOptions, operation);
|
|
2669
|
-
if (node) yield dedupePlan ?
|
|
2794
|
+
if (node) yield dedupePlan ? dedupePlan.apply(node) : node;
|
|
2670
2795
|
}
|
|
2671
2796
|
})();
|
|
2672
2797
|
} };
|
|
@@ -2680,7 +2805,7 @@ function createInputStream({ schemas, parseSchema, parseOperation, document, par
|
|
|
2680
2805
|
//#endregion
|
|
2681
2806
|
//#region src/adapter.ts
|
|
2682
2807
|
/**
|
|
2683
|
-
*
|
|
2808
|
+
* The `name` of `@kubb/adapter-oas`, used to identify this adapter in a Kubb config.
|
|
2684
2809
|
*/
|
|
2685
2810
|
const adapterOasName = "oas";
|
|
2686
2811
|
/**
|