@kubb/adapter-oas 5.0.0-beta.62 → 5.0.0-beta.64
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 +266 -175
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.ts +3 -3
- package/dist/index.js +267 -176
- package/dist/index.js.map +1 -1
- package/package.json +4 -6
- package/src/adapter.bench.ts +0 -60
- package/src/adapter.ts +0 -207
- package/src/bundler.ts +0 -71
- package/src/constants.ts +0 -136
- package/src/dialect.ts +0 -60
- package/src/discriminator.ts +0 -132
- package/src/factory.ts +0 -180
- package/src/guards.ts +0 -68
- package/src/index.ts +0 -15
- package/src/mime.ts +0 -22
- package/src/operation.ts +0 -194
- package/src/parser.ts +0 -1135
- package/src/refs.ts +0 -85
- package/src/resolvers.ts +0 -582
- package/src/schemaDiagnostics.ts +0 -76
- package/src/stream.ts +0 -264
- package/src/types.ts +0 -235
- /package/dist/{chunk-C0LytTxp.js → rolldown-runtime-C0LytTxp.js} +0 -0
package/dist/index.cjs
CHANGED
|
@@ -26,6 +26,7 @@ let node_path = require("node:path");
|
|
|
26
26
|
node_path = __toESM(node_path, 1);
|
|
27
27
|
let node_fs_promises = require("node:fs/promises");
|
|
28
28
|
let _readme_openapi_parser = require("@readme/openapi-parser");
|
|
29
|
+
let _scalar_openapi_upgrader = require("@scalar/openapi-upgrader");
|
|
29
30
|
let yaml = require("yaml");
|
|
30
31
|
let api_ref_bundler = require("api-ref-bundler");
|
|
31
32
|
let _kubb_ast_macros = require("@kubb/ast/macros");
|
|
@@ -37,10 +38,9 @@ let _kubb_ast = require("@kubb/ast");
|
|
|
37
38
|
*
|
|
38
39
|
* @example
|
|
39
40
|
* ```ts
|
|
40
|
-
* import { DEFAULT_PARSER_OPTIONS } from '@kubb/adapter-oas'
|
|
41
|
+
* import { DEFAULT_PARSER_OPTIONS, parseOas } from '@kubb/adapter-oas'
|
|
41
42
|
*
|
|
42
|
-
* const
|
|
43
|
-
* const root = parser.parse({ ...DEFAULT_PARSER_OPTIONS, dateType: 'date' })
|
|
43
|
+
* const { root } = parseOas(document, { ...DEFAULT_PARSER_OPTIONS, dateType: 'date' })
|
|
44
44
|
* ```
|
|
45
45
|
*/
|
|
46
46
|
const DEFAULT_PARSER_OPTIONS = {
|
|
@@ -111,12 +111,24 @@ const structuralKeys = new Set([
|
|
|
111
111
|
"not"
|
|
112
112
|
]);
|
|
113
113
|
/**
|
|
114
|
+
* Formats `convertFormat` maps to a dedicated type without going through `formatMap`:
|
|
115
|
+
* `int64` and the date/time family. Keep this in sync with the `convertFormat`
|
|
116
|
+
* special-cases in `parser.ts`. `isHandledFormat` reads it so the
|
|
117
|
+
* `KUBB_UNSUPPORTED_FORMAT` diagnostic and the parser agree on what is handled.
|
|
118
|
+
*/
|
|
119
|
+
const specialCasedFormats = new Set([
|
|
120
|
+
"int64",
|
|
121
|
+
"date-time",
|
|
122
|
+
"date",
|
|
123
|
+
"time"
|
|
124
|
+
]);
|
|
125
|
+
/**
|
|
114
126
|
* Static map from OAS `format` strings to Kubb `SchemaType` values.
|
|
115
127
|
*
|
|
116
128
|
* 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.
|
|
129
|
+
* Formats that depend on runtime options (`int64`, `date-time`, `date`, `time`) are handled
|
|
130
|
+
* separately in the parser. `ipv4` and `ipv6` map to their own dedicated schema types. `hostname`
|
|
131
|
+
* and `idn-hostname` map to `'url'` as the closest generic string-format type.
|
|
120
132
|
*
|
|
121
133
|
* @example
|
|
122
134
|
* ```ts
|
|
@@ -127,18 +139,6 @@ const structuralKeys = new Set([
|
|
|
127
139
|
* formatMap['float'] // 'number'
|
|
128
140
|
* ```
|
|
129
141
|
*/
|
|
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
142
|
const formatMap = {
|
|
143
143
|
uuid: "uuid",
|
|
144
144
|
email: "email",
|
|
@@ -318,19 +318,6 @@ async function read(path) {
|
|
|
318
318
|
//#endregion
|
|
319
319
|
//#region ../../internals/utils/src/object.ts
|
|
320
320
|
/**
|
|
321
|
-
* Returns `true` when `value` is a plain (non-null, non-array) object.
|
|
322
|
-
*
|
|
323
|
-
* @example
|
|
324
|
-
* ```ts
|
|
325
|
-
* isPlainObject({}) // true
|
|
326
|
-
* isPlainObject([]) // false
|
|
327
|
-
* isPlainObject(null) // false
|
|
328
|
-
* ```
|
|
329
|
-
*/
|
|
330
|
-
function isPlainObject(value) {
|
|
331
|
-
return typeof value === "object" && value !== null && Object.getPrototypeOf(value) === Object.prototype;
|
|
332
|
-
}
|
|
333
|
-
/**
|
|
334
321
|
* Recursively merges `source` into `target`, combining nested plain objects.
|
|
335
322
|
* Arrays and non-object values from `source` override the corresponding values in `target`.
|
|
336
323
|
*
|
|
@@ -597,74 +584,14 @@ async function bundleDocument(pathOrUrl) {
|
|
|
597
584
|
return await (0, api_ref_bundler.bundle)(pathOrUrl, resolver);
|
|
598
585
|
}
|
|
599
586
|
//#endregion
|
|
600
|
-
//#region src/guards.ts
|
|
601
|
-
/**
|
|
602
|
-
* Returns `true` when `doc` is a Swagger 2.0 document (no `openapi` key).
|
|
603
|
-
*
|
|
604
|
-
* @example
|
|
605
|
-
* ```ts
|
|
606
|
-
* if (isOpenApiV2Document(doc)) {
|
|
607
|
-
* // doc is OpenAPIV2.Document
|
|
608
|
-
* }
|
|
609
|
-
* ```
|
|
610
|
-
*/
|
|
611
|
-
function isOpenApiV2Document(doc) {
|
|
612
|
-
return !!doc && isPlainObject(doc) && !("openapi" in doc);
|
|
613
|
-
}
|
|
614
|
-
/**
|
|
615
|
-
* Returns `true` when a schema should be treated as nullable.
|
|
616
|
-
*
|
|
617
|
-
* Recognizes all nullable signals across OAS versions: `nullable: true` (OAS 3.0),
|
|
618
|
-
* `x-nullable: true` (vendor extension), `type: 'null'`, and `type: ['null', ...]` (OAS 3.1).
|
|
619
|
-
*
|
|
620
|
-
* @example
|
|
621
|
-
* ```ts
|
|
622
|
-
* isNullable({ type: 'string', nullable: true }) // true
|
|
623
|
-
* isNullable({ type: ['string', 'null'] }) // true
|
|
624
|
-
* isNullable({ type: 'string' }) // false
|
|
625
|
-
* ```
|
|
626
|
-
*/
|
|
627
|
-
function isNullable(schema) {
|
|
628
|
-
if ((schema?.nullable ?? schema?.["x-nullable"]) === true) return true;
|
|
629
|
-
const schemaType = schema?.type;
|
|
630
|
-
if (schemaType === "null") return true;
|
|
631
|
-
if (Array.isArray(schemaType)) return schemaType.includes("null");
|
|
632
|
-
return false;
|
|
633
|
-
}
|
|
634
|
-
/**
|
|
635
|
-
* Returns `true` when `obj` is an OpenAPI `$ref` pointer object.
|
|
636
|
-
*
|
|
637
|
-
* @example
|
|
638
|
-
* ```ts
|
|
639
|
-
* isReference({ $ref: '#/components/schemas/Pet' }) // true
|
|
640
|
-
* isReference({ type: 'string' }) // false
|
|
641
|
-
* ```
|
|
642
|
-
*/
|
|
643
|
-
function isReference(obj) {
|
|
644
|
-
return !!obj && typeof obj === "object" && "$ref" in obj;
|
|
645
|
-
}
|
|
646
|
-
/**
|
|
647
|
-
* Returns `true` when `obj` is a schema with a structured OAS 3.x `discriminator` object.
|
|
648
|
-
*
|
|
649
|
-
* @example
|
|
650
|
-
* ```ts
|
|
651
|
-
* isDiscriminator({ discriminator: { propertyName: 'type', mapping: {} } }) // true
|
|
652
|
-
* isDiscriminator({ discriminator: 'type' }) // false (Swagger 2 string form)
|
|
653
|
-
* ```
|
|
654
|
-
*/
|
|
655
|
-
function isDiscriminator(obj) {
|
|
656
|
-
const record = obj;
|
|
657
|
-
return !!obj && !!record["discriminator"] && typeof record["discriminator"] !== "string";
|
|
658
|
-
}
|
|
659
|
-
//#endregion
|
|
660
587
|
//#region src/factory.ts
|
|
661
588
|
/**
|
|
662
|
-
* Loads and
|
|
589
|
+
* Loads and bundles an OpenAPI document, returning the raw `Document`.
|
|
663
590
|
*
|
|
664
591
|
* Accepts a file path string or an already-parsed document object. File paths and URLs are
|
|
665
592
|
* bundled via `api-ref-bundler`, hoisting external file schemas into named `components.schemas`
|
|
666
593
|
* entries so generators can emit named types and imports. Swagger 2.0 documents are
|
|
667
|
-
* automatically up-converted to OpenAPI 3.0 via
|
|
594
|
+
* automatically up-converted to OpenAPI 3.0 via `@scalar/openapi-upgrader`.
|
|
668
595
|
*
|
|
669
596
|
* @example
|
|
670
597
|
* ```ts
|
|
@@ -674,13 +601,7 @@ function isDiscriminator(obj) {
|
|
|
674
601
|
*/
|
|
675
602
|
async function parseDocument(pathOrApi, { canBundle = true } = {}) {
|
|
676
603
|
if (typeof pathOrApi === "string" && canBundle) return parseDocument(await bundleDocument(pathOrApi), { canBundle: false });
|
|
677
|
-
|
|
678
|
-
if (isOpenApiV2Document(document)) {
|
|
679
|
-
const { default: swagger2openapi } = await import("swagger2openapi");
|
|
680
|
-
const { openapi } = await swagger2openapi.convertObj(document, { anchors: true });
|
|
681
|
-
return openapi;
|
|
682
|
-
}
|
|
683
|
-
return document;
|
|
604
|
+
return (0, _scalar_openapi_upgrader.upgrade)(typeof pathOrApi === "string" ? (0, yaml.parse)(pathOrApi) : pathOrApi, "3.0");
|
|
684
605
|
}
|
|
685
606
|
/**
|
|
686
607
|
* Deep-merges multiple OpenAPI documents into a single `Document`.
|
|
@@ -770,17 +691,227 @@ async function validateDocument(document, { throwOnError = false } = {}) {
|
|
|
770
691
|
}
|
|
771
692
|
}
|
|
772
693
|
//#endregion
|
|
694
|
+
//#region src/dedupe.ts
|
|
695
|
+
/**
|
|
696
|
+
* Minimum occurrences before a shape is deduplicated.
|
|
697
|
+
*/
|
|
698
|
+
const MIN_OCCURRENCES = 2;
|
|
699
|
+
/**
|
|
700
|
+
* Builds the shared `ref` replacement for a duplicate occurrence, carrying the
|
|
701
|
+
* usage-slot and documentation fields that are not part of the shared type.
|
|
702
|
+
*/
|
|
703
|
+
function createRefNode(node, target) {
|
|
704
|
+
return _kubb_core.ast.factory.createSchema({
|
|
705
|
+
type: "ref",
|
|
706
|
+
name: target.name,
|
|
707
|
+
ref: target.ref,
|
|
708
|
+
optional: node.optional,
|
|
709
|
+
nullish: node.nullish,
|
|
710
|
+
readOnly: node.readOnly,
|
|
711
|
+
writeOnly: node.writeOnly,
|
|
712
|
+
deprecated: node.deprecated,
|
|
713
|
+
description: node.description,
|
|
714
|
+
default: node.default,
|
|
715
|
+
example: node.example
|
|
716
|
+
});
|
|
717
|
+
}
|
|
718
|
+
/**
|
|
719
|
+
* Strips usage-slot flags from an extracted definition and applies its name.
|
|
720
|
+
* A standalone definition is never optional, so `optional`/`nullish` are cleared.
|
|
721
|
+
*/
|
|
722
|
+
function cleanDefinition(node, name) {
|
|
723
|
+
return {
|
|
724
|
+
...node,
|
|
725
|
+
name,
|
|
726
|
+
optional: void 0,
|
|
727
|
+
nullish: void 0
|
|
728
|
+
};
|
|
729
|
+
}
|
|
730
|
+
/**
|
|
731
|
+
* Returns `true` when a node is eligible for deduplication. Only enums and objects qualify, and
|
|
732
|
+
* object shapes that take part in a circular chain are rejected so recursive structures are not
|
|
733
|
+
* extracted (which would break the cycle).
|
|
734
|
+
*/
|
|
735
|
+
function isCandidate(node, circularSchemas) {
|
|
736
|
+
if (node.type === "enum") return true;
|
|
737
|
+
if (node.type !== "object") return false;
|
|
738
|
+
if (node.name && circularSchemas.has(node.name)) return false;
|
|
739
|
+
return !(0, _kubb_ast_utils.containsCircularRef)(node, { circularSchemas });
|
|
740
|
+
}
|
|
741
|
+
/**
|
|
742
|
+
* Produces the name for an inline shape with no existing named component, resolving
|
|
743
|
+
* collisions against `usedNames`. Returns `null` for an unnamed shape, which stays inline.
|
|
744
|
+
*/
|
|
745
|
+
function nameFor(node, usedNames) {
|
|
746
|
+
const base = node.name;
|
|
747
|
+
if (!base) return null;
|
|
748
|
+
let name = base;
|
|
749
|
+
let counter = 2;
|
|
750
|
+
while (usedNames.has(name)) name = `${base}${counter++}`;
|
|
751
|
+
usedNames.add(name);
|
|
752
|
+
return name;
|
|
753
|
+
}
|
|
754
|
+
/**
|
|
755
|
+
* Scans a forest of schema and operation nodes and produces a {@link Plan}.
|
|
756
|
+
*
|
|
757
|
+
* A shape that occurs at least {@link MIN_OCCURRENCES} times is deduplicated: if any occurrence is
|
|
758
|
+
* a named top-level schema, the first one becomes the target (so other top-level duplicates and
|
|
759
|
+
* inline copies turn into references to it). Other top-level names with the same content are
|
|
760
|
+
* recorded as aliases. Otherwise a new definition is extracted using {@link nameFor}. The returned
|
|
761
|
+
* plan rewrites nodes against those decisions.
|
|
762
|
+
*
|
|
763
|
+
* @example
|
|
764
|
+
* ```ts
|
|
765
|
+
* const dedupePlan = plan([...schemaNodes, ...operationNodes], { circularSchemas, usedNames })
|
|
766
|
+
* ```
|
|
767
|
+
*/
|
|
768
|
+
function plan(roots, context) {
|
|
769
|
+
const { circularSchemas, usedNames } = context;
|
|
770
|
+
const topLevelNodes = /* @__PURE__ */ new Set();
|
|
771
|
+
const groups = /* @__PURE__ */ new Map();
|
|
772
|
+
for (const root of roots) {
|
|
773
|
+
if (root.kind === "Schema") topLevelNodes.add(root);
|
|
774
|
+
for (const schemaNode of _kubb_core.ast.collect(root, { schema: (node) => node })) {
|
|
775
|
+
if (!isCandidate(schemaNode, circularSchemas)) continue;
|
|
776
|
+
const signature = _kubb_core.ast.signatureOf(schemaNode);
|
|
777
|
+
const isTopLevel = topLevelNodes.has(schemaNode) && !!schemaNode.name;
|
|
778
|
+
const group = groups.get(signature);
|
|
779
|
+
if (group) {
|
|
780
|
+
group.count++;
|
|
781
|
+
if (isTopLevel) group.topLevelNames.push(schemaNode.name);
|
|
782
|
+
} else groups.set(signature, {
|
|
783
|
+
count: 1,
|
|
784
|
+
representative: schemaNode,
|
|
785
|
+
topLevelNames: isTopLevel ? [schemaNode.name] : []
|
|
786
|
+
});
|
|
787
|
+
}
|
|
788
|
+
}
|
|
789
|
+
const bySignature = /* @__PURE__ */ new Map();
|
|
790
|
+
const byName = /* @__PURE__ */ new Map();
|
|
791
|
+
const pendingExtractions = [];
|
|
792
|
+
for (const [signature, group] of groups) {
|
|
793
|
+
if (group.count < MIN_OCCURRENCES) continue;
|
|
794
|
+
const [firstName, ...duplicateNames] = group.topLevelNames;
|
|
795
|
+
if (firstName) {
|
|
796
|
+
const target = {
|
|
797
|
+
name: firstName,
|
|
798
|
+
ref: `${SCHEMA_REF_PREFIX}${firstName}`
|
|
799
|
+
};
|
|
800
|
+
bySignature.set(signature, target);
|
|
801
|
+
for (const duplicate of duplicateNames) byName.set(duplicate, target);
|
|
802
|
+
continue;
|
|
803
|
+
}
|
|
804
|
+
const name = nameFor(group.representative, usedNames);
|
|
805
|
+
if (!name) continue;
|
|
806
|
+
bySignature.set(signature, {
|
|
807
|
+
name,
|
|
808
|
+
ref: `${SCHEMA_REF_PREFIX}${name}`
|
|
809
|
+
});
|
|
810
|
+
pendingExtractions.push({
|
|
811
|
+
name,
|
|
812
|
+
representative: group.representative
|
|
813
|
+
});
|
|
814
|
+
}
|
|
815
|
+
function rewrite(node, skipRootMatch) {
|
|
816
|
+
if (bySignature.size === 0 && byName.size === 0) return node;
|
|
817
|
+
const root = node;
|
|
818
|
+
return _kubb_core.ast.transform(node, { schema(schemaNode) {
|
|
819
|
+
if (schemaNode.type === "ref") {
|
|
820
|
+
const refName = schemaNode.ref ? (0, _kubb_ast_utils.extractRefName)(schemaNode.ref) : schemaNode.name;
|
|
821
|
+
const target = refName ? byName.get(refName) : void 0;
|
|
822
|
+
return target ? {
|
|
823
|
+
...schemaNode,
|
|
824
|
+
name: target.name,
|
|
825
|
+
ref: target.ref
|
|
826
|
+
} : void 0;
|
|
827
|
+
}
|
|
828
|
+
if (skipRootMatch && schemaNode === root) return void 0;
|
|
829
|
+
const target = bySignature.get(_kubb_core.ast.signatureOf(schemaNode));
|
|
830
|
+
if (!target) return void 0;
|
|
831
|
+
return createRefNode(schemaNode, target);
|
|
832
|
+
} });
|
|
833
|
+
}
|
|
834
|
+
return {
|
|
835
|
+
extracted: pendingExtractions.map(({ name, representative }) => cleanDefinition(rewrite(representative, true), name)),
|
|
836
|
+
apply(node) {
|
|
837
|
+
return rewrite(node, false);
|
|
838
|
+
},
|
|
839
|
+
applyTopLevel(node) {
|
|
840
|
+
const target = bySignature.get(_kubb_core.ast.signatureOf(node));
|
|
841
|
+
if (target && target.name !== node.name) return _kubb_core.ast.factory.createSchema({
|
|
842
|
+
type: "ref",
|
|
843
|
+
name: node.name ?? null,
|
|
844
|
+
ref: target.ref,
|
|
845
|
+
description: node.description,
|
|
846
|
+
deprecated: node.deprecated
|
|
847
|
+
});
|
|
848
|
+
return rewrite(node, true);
|
|
849
|
+
},
|
|
850
|
+
isAlias(name) {
|
|
851
|
+
return byName.has(name);
|
|
852
|
+
}
|
|
853
|
+
};
|
|
854
|
+
}
|
|
855
|
+
//#endregion
|
|
856
|
+
//#region src/guards.ts
|
|
857
|
+
/**
|
|
858
|
+
* Returns `true` when a schema should be treated as nullable.
|
|
859
|
+
*
|
|
860
|
+
* Recognizes all nullable signals across OAS versions: `nullable: true` (OAS 3.0),
|
|
861
|
+
* `x-nullable: true` (vendor extension), `type: 'null'`, and `type: ['null', ...]` (OAS 3.1).
|
|
862
|
+
*
|
|
863
|
+
* @example
|
|
864
|
+
* ```ts
|
|
865
|
+
* isNullable({ type: 'string', nullable: true }) // true
|
|
866
|
+
* isNullable({ type: ['string', 'null'] }) // true
|
|
867
|
+
* isNullable({ type: 'string' }) // false
|
|
868
|
+
* ```
|
|
869
|
+
*/
|
|
870
|
+
function isNullable(schema) {
|
|
871
|
+
if ((schema?.nullable ?? schema?.["x-nullable"]) === true) return true;
|
|
872
|
+
const schemaType = schema?.type;
|
|
873
|
+
if (schemaType === "null") return true;
|
|
874
|
+
if (Array.isArray(schemaType)) return schemaType.includes("null");
|
|
875
|
+
return false;
|
|
876
|
+
}
|
|
877
|
+
/**
|
|
878
|
+
* Returns `true` when `obj` is an OpenAPI `$ref` pointer object.
|
|
879
|
+
*
|
|
880
|
+
* @example
|
|
881
|
+
* ```ts
|
|
882
|
+
* isReference({ $ref: '#/components/schemas/Pet' }) // true
|
|
883
|
+
* isReference({ type: 'string' }) // false
|
|
884
|
+
* ```
|
|
885
|
+
*/
|
|
886
|
+
function isReference(obj) {
|
|
887
|
+
return !!obj && typeof obj === "object" && "$ref" in obj;
|
|
888
|
+
}
|
|
889
|
+
/**
|
|
890
|
+
* Returns `true` when `obj` is a schema with a structured OAS 3.x `discriminator` object.
|
|
891
|
+
*
|
|
892
|
+
* @example
|
|
893
|
+
* ```ts
|
|
894
|
+
* isDiscriminator({ discriminator: { propertyName: 'type', mapping: {} } }) // true
|
|
895
|
+
* isDiscriminator({ discriminator: 'type' }) // false (Swagger 2 string form)
|
|
896
|
+
* ```
|
|
897
|
+
*/
|
|
898
|
+
function isDiscriminator(obj) {
|
|
899
|
+
const record = obj;
|
|
900
|
+
return !!obj && !!record["discriminator"] && typeof record["discriminator"] !== "string";
|
|
901
|
+
}
|
|
902
|
+
//#endregion
|
|
773
903
|
//#region src/refs.ts
|
|
774
904
|
const _refCache = /* @__PURE__ */ new WeakMap();
|
|
775
905
|
/**
|
|
776
906
|
* Resolves a local JSON pointer reference from a document.
|
|
777
907
|
*
|
|
778
|
-
* Accepts `#/...` refs. Returns `null` for empty or non-local
|
|
779
|
-
*
|
|
908
|
+
* Accepts `#/...` refs. Returns `null` for an empty or non-local ref. When the pointer cannot be
|
|
909
|
+
* resolved, reports a `refNotFound` diagnostic into the active build and returns `null`. Outside a
|
|
910
|
+
* build there is no sink to collect it, so it throws instead.
|
|
780
911
|
*
|
|
781
912
|
* @example
|
|
782
913
|
* ```ts
|
|
783
|
-
* resolveRef<SchemaObject>(document, '#/components/schemas/Pet')
|
|
914
|
+
* resolveRef<SchemaObject>(document, '#/components/schemas/Pet')
|
|
784
915
|
* ```
|
|
785
916
|
*/
|
|
786
917
|
function resolveRef(document, $ref) {
|
|
@@ -837,23 +968,6 @@ function dereferenceWithRef(document, schema) {
|
|
|
837
968
|
//#endregion
|
|
838
969
|
//#region src/dialect.ts
|
|
839
970
|
/**
|
|
840
|
-
* Derives a schema's `optional`/`nullish` flags from a parent's `required` value and the
|
|
841
|
-
* schema's own `nullable`. How "required" and "nullable" combine is OpenAPI-specific, so it
|
|
842
|
-
* lives in the dialect.
|
|
843
|
-
*
|
|
844
|
-
* - Non-required + non-nullable → `optional: true`.
|
|
845
|
-
* - Non-required + nullable → `nullish: true`.
|
|
846
|
-
* - Required → both flags cleared.
|
|
847
|
-
*/
|
|
848
|
-
function optionality(schema, required) {
|
|
849
|
-
const nullable = schema.nullable ?? false;
|
|
850
|
-
return {
|
|
851
|
-
...schema,
|
|
852
|
-
optional: !required && !nullable ? true : void 0,
|
|
853
|
-
nullish: !required && nullable ? true : void 0
|
|
854
|
-
};
|
|
855
|
-
}
|
|
856
|
-
/**
|
|
857
971
|
* The OpenAPI / Swagger dialect, the default used by `@kubb/adapter-oas`.
|
|
858
972
|
*
|
|
859
973
|
* Implements the spec-agnostic {@link ast.SchemaDialect} contract: it isolates the
|
|
@@ -879,14 +993,14 @@ const oasDialect = _kubb_core.ast.defineDialect({
|
|
|
879
993
|
isReference,
|
|
880
994
|
isDiscriminator,
|
|
881
995
|
isBinary: (schema) => schema.type === "string" && schema.contentMediaType === "application/octet-stream",
|
|
882
|
-
resolveRef
|
|
883
|
-
|
|
884
|
-
}
|
|
996
|
+
resolveRef
|
|
997
|
+
},
|
|
998
|
+
dedupe: { plan }
|
|
885
999
|
});
|
|
886
1000
|
//#endregion
|
|
887
1001
|
//#region src/discriminator.ts
|
|
888
1002
|
/**
|
|
889
|
-
*
|
|
1003
|
+
* Maps each child schema name to its discriminator patch data by scanning the given
|
|
890
1004
|
* top-level AST schema nodes for union schemas that carry a `discriminatorPropertyName`.
|
|
891
1005
|
*
|
|
892
1006
|
* The streaming path calls this on a small pre-parsed subset of schemas (only the
|
|
@@ -1002,9 +1116,8 @@ function findDiscriminator(mapping, ref) {
|
|
|
1002
1116
|
/**
|
|
1003
1117
|
* MIME type fragments that mark a media type as JSON-like.
|
|
1004
1118
|
*
|
|
1005
|
-
*
|
|
1006
|
-
*
|
|
1007
|
-
* `application/vnd.api+json`.
|
|
1119
|
+
* A content type is JSON when it contains any of these substrings. The `+json` entry catches
|
|
1120
|
+
* structured-syntax suffixes such as `application/vnd.api+json`.
|
|
1008
1121
|
*/
|
|
1009
1122
|
const jsonMimeFragments = [
|
|
1010
1123
|
"application/json",
|
|
@@ -1100,8 +1213,8 @@ function getRequestContent({ document, operation, mediaType }) {
|
|
|
1100
1213
|
return available ? [available, content[available]] : false;
|
|
1101
1214
|
}
|
|
1102
1215
|
/**
|
|
1103
|
-
* Returns the primary request content type. Prefers a JSON-like media type (the last one wins
|
|
1104
|
-
*
|
|
1216
|
+
* Returns the primary request content type. Prefers a JSON-like media type (the last one wins
|
|
1217
|
+
* when several are declared), then the first declared one, defaulting to `'application/json'`.
|
|
1105
1218
|
*/
|
|
1106
1219
|
function getRequestContentType({ document, operation }) {
|
|
1107
1220
|
const content = getRequestBodyContent({
|
|
@@ -1196,9 +1309,9 @@ function getSchemaType(format) {
|
|
|
1196
1309
|
return formatMap[format] ?? null;
|
|
1197
1310
|
}
|
|
1198
1311
|
/**
|
|
1199
|
-
* Whether the parser maps `format` to a dedicated type. True for any `formatMap` entry
|
|
1200
|
-
* `specialCasedFormats` `convertFormat` handles directly. False means the format falls back to
|
|
1201
|
-
* base type, which is what `KUBB_UNSUPPORTED_FORMAT` flags. Reading both sources keeps the
|
|
1312
|
+
* Whether the parser maps `format` to a dedicated type. True for any `formatMap` entry, plus the
|
|
1313
|
+
* `specialCasedFormats` that `convertFormat` handles directly. False means the format falls back to
|
|
1314
|
+
* the base type, which is what `KUBB_UNSUPPORTED_FORMAT` flags. Reading both sources keeps the
|
|
1202
1315
|
* diagnostic in step with the parser as `formatMap` grows.
|
|
1203
1316
|
*/
|
|
1204
1317
|
function isHandledFormat(format) {
|
|
@@ -1216,7 +1329,7 @@ function getPrimitiveType(type) {
|
|
|
1216
1329
|
/**
|
|
1217
1330
|
* Returns all parameters for an operation, merging path-level and operation-level entries.
|
|
1218
1331
|
* Operation-level parameters override path-level ones with the same `in:name` key.
|
|
1219
|
-
* `$ref`
|
|
1332
|
+
* Each `$ref` parameter is dereferenced via `dereferenceWithRef` before merging.
|
|
1220
1333
|
*
|
|
1221
1334
|
* @example
|
|
1222
1335
|
* ```ts
|
|
@@ -1317,7 +1430,10 @@ function getRequestSchema(document, operation, options = {}) {
|
|
|
1317
1430
|
* ```ts
|
|
1318
1431
|
* flattenSchema({ allOf: [{ description: 'A pet' }], type: 'object', properties: {} })
|
|
1319
1432
|
* // { type: 'object', properties: {}, description: 'A pet' }
|
|
1433
|
+
* ```
|
|
1320
1434
|
*
|
|
1435
|
+
* @example
|
|
1436
|
+
* ```ts
|
|
1321
1437
|
* flattenSchema({ allOf: [{ $ref: '#/components/schemas/Pet' }] })
|
|
1322
1438
|
* // returned unchanged, contains a $ref
|
|
1323
1439
|
* ```
|
|
@@ -2011,7 +2127,7 @@ function createSchemaParser(ctx, dialect = oasDialect) {
|
|
|
2011
2127
|
nullable: schemaNode.type === "null" ? void 0 : propNullable || void 0
|
|
2012
2128
|
},
|
|
2013
2129
|
required
|
|
2014
|
-
}
|
|
2130
|
+
});
|
|
2015
2131
|
}) : [];
|
|
2016
2132
|
const additionalProperties = schema.additionalProperties;
|
|
2017
2133
|
const additionalPropertiesNode = (() => {
|
|
@@ -2325,7 +2441,7 @@ function createSchemaParser(ctx, dialect = oasDialect) {
|
|
|
2325
2441
|
description: param["description"] ?? schema.description
|
|
2326
2442
|
},
|
|
2327
2443
|
required
|
|
2328
|
-
}
|
|
2444
|
+
});
|
|
2329
2445
|
}
|
|
2330
2446
|
/**
|
|
2331
2447
|
* Reads the inline `requestBody` metadata (description / required) that OAS exposes
|
|
@@ -2378,7 +2494,7 @@ function createSchemaParser(ctx, dialect = oasDialect) {
|
|
|
2378
2494
|
if (!schema) return [];
|
|
2379
2495
|
return [_kubb_core.ast.factory.createContent({
|
|
2380
2496
|
contentType: ct,
|
|
2381
|
-
schema:
|
|
2497
|
+
schema: _kubb_core.ast.optionality(parseSchema({
|
|
2382
2498
|
schema,
|
|
2383
2499
|
name: requestBodyName
|
|
2384
2500
|
}, options), requestBodyMeta.required),
|
|
@@ -2581,30 +2697,15 @@ function preScan({ schemas, parseSchema, parseOperation, document, parserOptions
|
|
|
2581
2697
|
}
|
|
2582
2698
|
const circularSchemas = new Set(circularNames);
|
|
2583
2699
|
const usedNames = new Set(Object.keys(schemas));
|
|
2584
|
-
dedupePlan =
|
|
2585
|
-
|
|
2586
|
-
|
|
2587
|
-
if (node.type !== "object") return false;
|
|
2588
|
-
if (node.name && circularSchemas.has(node.name)) return false;
|
|
2589
|
-
return !(0, _kubb_ast_utils.containsCircularRef)(node, { circularSchemas });
|
|
2590
|
-
},
|
|
2591
|
-
nameFor: (node) => {
|
|
2592
|
-
const base = node.name;
|
|
2593
|
-
if (!base) return null;
|
|
2594
|
-
let name = base;
|
|
2595
|
-
let counter = 2;
|
|
2596
|
-
while (usedNames.has(name)) name = `${base}${counter++}`;
|
|
2597
|
-
usedNames.add(name);
|
|
2598
|
-
return name;
|
|
2599
|
-
},
|
|
2600
|
-
refFor: (name) => `${SCHEMA_REF_PREFIX}${name}`
|
|
2700
|
+
dedupePlan = oasDialect.dedupe.plan([...allNodes, ...operationNodes], {
|
|
2701
|
+
circularSchemas,
|
|
2702
|
+
usedNames
|
|
2601
2703
|
});
|
|
2602
|
-
for (const definition of dedupePlan.
|
|
2704
|
+
for (const definition of dedupePlan.extracted) if (definition.type === "enum" && definition.name) enumNames.push(definition.name);
|
|
2603
2705
|
}
|
|
2604
|
-
const aliasNames = dedupePlan?.aliasNames;
|
|
2605
2706
|
return {
|
|
2606
2707
|
refAliasMap,
|
|
2607
|
-
enumNames:
|
|
2708
|
+
enumNames: dedupePlan ? enumNames.filter((name) => !dedupePlan.isAlias(name)) : enumNames,
|
|
2608
2709
|
circularNames,
|
|
2609
2710
|
discriminatorChildMap,
|
|
2610
2711
|
dedupePlan
|
|
@@ -2629,39 +2730,29 @@ function preScan({ schemas, parseSchema, parseOperation, document, parserOptions
|
|
|
2629
2730
|
* ```
|
|
2630
2731
|
*/
|
|
2631
2732
|
function createInputStream({ schemas, parseSchema, parseOperation, document, parserOptions, refAliasMap, discriminatorChildMap, dedupePlan, meta }) {
|
|
2632
|
-
const rewriteTopLevelSchema = (node) => {
|
|
2633
|
-
if (!dedupePlan) return node;
|
|
2634
|
-
const canonical = dedupePlan.canonicalBySignature.get(_kubb_core.ast.signatureOf(node));
|
|
2635
|
-
if (canonical && canonical.name !== node.name) return _kubb_core.ast.factory.createSchema({
|
|
2636
|
-
type: "ref",
|
|
2637
|
-
name: node.name ?? null,
|
|
2638
|
-
ref: canonical.ref,
|
|
2639
|
-
description: node.description,
|
|
2640
|
-
deprecated: node.deprecated
|
|
2641
|
-
});
|
|
2642
|
-
return _kubb_core.ast.applyDedupe(node, dedupePlan, true);
|
|
2643
|
-
};
|
|
2644
2733
|
const schemasIterable = { [Symbol.asyncIterator]() {
|
|
2645
2734
|
return (async function* () {
|
|
2646
|
-
if (dedupePlan) for (const definition of dedupePlan.
|
|
2735
|
+
if (dedupePlan) for (const definition of dedupePlan.extracted) yield definition;
|
|
2647
2736
|
for (const [name, schema] of Object.entries(schemas)) {
|
|
2648
|
-
if (dedupePlan?.
|
|
2737
|
+
if (dedupePlan?.isAlias(name)) continue;
|
|
2649
2738
|
const alias = refAliasMap.get(name);
|
|
2650
2739
|
if (alias?.name && schemas[alias.name]) {
|
|
2651
|
-
|
|
2740
|
+
const aliasNode = {
|
|
2652
2741
|
...parseSchema({
|
|
2653
2742
|
schema: schemas[alias.name],
|
|
2654
2743
|
name: alias.name
|
|
2655
2744
|
}, parserOptions),
|
|
2656
2745
|
name
|
|
2657
|
-
}
|
|
2746
|
+
};
|
|
2747
|
+
yield dedupePlan ? dedupePlan.applyTopLevel(aliasNode) : aliasNode;
|
|
2658
2748
|
continue;
|
|
2659
2749
|
}
|
|
2660
2750
|
const parsed = parseSchema({
|
|
2661
2751
|
schema,
|
|
2662
2752
|
name
|
|
2663
2753
|
}, parserOptions);
|
|
2664
|
-
|
|
2754
|
+
const node = discriminatorChildMap?.get(name) ? patchDiscriminatorNode(parsed, discriminatorChildMap.get(name)) : parsed;
|
|
2755
|
+
yield dedupePlan ? dedupePlan.applyTopLevel(node) : node;
|
|
2665
2756
|
}
|
|
2666
2757
|
})();
|
|
2667
2758
|
} };
|
|
@@ -2669,7 +2760,7 @@ function createInputStream({ schemas, parseSchema, parseOperation, document, par
|
|
|
2669
2760
|
return (async function* () {
|
|
2670
2761
|
for (const operation of getOperations(document)) {
|
|
2671
2762
|
const node = parseOperation(parserOptions, operation);
|
|
2672
|
-
if (node) yield dedupePlan ?
|
|
2763
|
+
if (node) yield dedupePlan ? dedupePlan.apply(node) : node;
|
|
2673
2764
|
}
|
|
2674
2765
|
})();
|
|
2675
2766
|
} };
|
|
@@ -2683,7 +2774,7 @@ function createInputStream({ schemas, parseSchema, parseOperation, document, par
|
|
|
2683
2774
|
//#endregion
|
|
2684
2775
|
//#region src/adapter.ts
|
|
2685
2776
|
/**
|
|
2686
|
-
*
|
|
2777
|
+
* The `name` of `@kubb/adapter-oas`, used to identify this adapter in a Kubb config.
|
|
2687
2778
|
*/
|
|
2688
2779
|
const adapterOasName = "oas";
|
|
2689
2780
|
/**
|