@kubb/adapter-oas 5.0.0-beta.91 → 5.0.0-beta.94
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 +660 -763
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.ts +1 -5
- package/dist/index.js +660 -763
- package/dist/index.js.map +1 -1
- package/package.json +3 -3
package/dist/index.js
CHANGED
|
@@ -5,18 +5,11 @@ import path from "node:path";
|
|
|
5
5
|
import { access, readFile } from "node:fs/promises";
|
|
6
6
|
import { compileErrors, validate } from "@readme/openapi-parser";
|
|
7
7
|
import { upgrade } from "@scalar/openapi-upgrader";
|
|
8
|
-
import { parse } from "yaml";
|
|
9
8
|
import { bundle } from "api-ref-bundler";
|
|
9
|
+
import { parse } from "yaml";
|
|
10
10
|
//#region src/constants.ts
|
|
11
11
|
/**
|
|
12
12
|
* Default parser options applied when no explicit options are provided.
|
|
13
|
-
*
|
|
14
|
-
* @example
|
|
15
|
-
* ```ts
|
|
16
|
-
* import { DEFAULT_PARSER_OPTIONS, parseOas } from '@kubb/adapter-oas'
|
|
17
|
-
*
|
|
18
|
-
* const { root } = parseOas(document, { ...DEFAULT_PARSER_OPTIONS, dateType: 'date' })
|
|
19
|
-
* ```
|
|
20
13
|
*/
|
|
21
14
|
const DEFAULT_PARSER_OPTIONS = {
|
|
22
15
|
dateType: "string",
|
|
@@ -55,14 +48,6 @@ const SUPPORTED_METHODS = /* @__PURE__ */ new Set([
|
|
|
55
48
|
*
|
|
56
49
|
* A fragment that contains any of these keys carries structural meaning of its own and must stay as a separate
|
|
57
50
|
* intersection member rather than being merged into the parent.
|
|
58
|
-
*
|
|
59
|
-
* @example
|
|
60
|
-
* ```ts
|
|
61
|
-
* import { structuralKeys } from '@kubb/adapter-oas'
|
|
62
|
-
*
|
|
63
|
-
* const isStructural = Object.keys(fragment).some((key) => structuralKeys.has(key))
|
|
64
|
-
* // true when fragment has e.g. 'properties' or 'oneOf'
|
|
65
|
-
* ```
|
|
66
51
|
*/
|
|
67
52
|
const structuralKeys = /* @__PURE__ */ new Set([
|
|
68
53
|
"properties",
|
|
@@ -92,15 +77,6 @@ const specialCasedFormats = /* @__PURE__ */ new Set([
|
|
|
92
77
|
* Formats that depend on runtime options (`int64`, `date-time`, `date`, `time`) are handled
|
|
93
78
|
* separately in the parser. `ipv4` and `ipv6` map to their own dedicated schema types. `hostname`
|
|
94
79
|
* and `idn-hostname` map to `'url'` as the closest generic string-format type.
|
|
95
|
-
*
|
|
96
|
-
* @example
|
|
97
|
-
* ```ts
|
|
98
|
-
* import { formatMap } from '@kubb/adapter-oas'
|
|
99
|
-
*
|
|
100
|
-
* formatMap['uuid'] // 'uuid'
|
|
101
|
-
* formatMap['binary'] // 'blob'
|
|
102
|
-
* formatMap['float'] // 'number'
|
|
103
|
-
* ```
|
|
104
80
|
*/
|
|
105
81
|
const formatMap = {
|
|
106
82
|
uuid: "uuid",
|
|
@@ -121,24 +97,10 @@ const formatMap = {
|
|
|
121
97
|
};
|
|
122
98
|
/**
|
|
123
99
|
* Vendor extension keys that attach human-readable labels to enum values, checked in priority order.
|
|
124
|
-
*
|
|
125
|
-
* @example
|
|
126
|
-
* ```ts
|
|
127
|
-
* import { enumExtensionKeys } from '@kubb/adapter-oas'
|
|
128
|
-
*
|
|
129
|
-
* const key = enumExtensionKeys.find((k) => k in schema) // 'x-enumNames' | 'x-enum-varnames' | undefined
|
|
130
|
-
* ```
|
|
131
100
|
*/
|
|
132
101
|
const enumExtensionKeys = ["x-enumNames", "x-enum-varnames"];
|
|
133
102
|
/**
|
|
134
103
|
* Vendor extension keys that attach human-readable descriptions to enum values, checked in priority order.
|
|
135
|
-
*
|
|
136
|
-
* @example
|
|
137
|
-
* ```ts
|
|
138
|
-
* import { enumDescriptionKeys } from '@kubb/adapter-oas'
|
|
139
|
-
*
|
|
140
|
-
* const key = enumDescriptionKeys.find((k) => k in schema) // 'x-enumDescriptions' | 'x-enum-descriptions' | undefined
|
|
141
|
-
* ```
|
|
142
104
|
*/
|
|
143
105
|
const enumDescriptionKeys = ["x-enumDescriptions", "x-enum-descriptions"];
|
|
144
106
|
//#endregion
|
|
@@ -382,7 +344,7 @@ async function read(path) {
|
|
|
382
344
|
return readFile(path, { encoding: "utf8" });
|
|
383
345
|
}
|
|
384
346
|
//#endregion
|
|
385
|
-
//#region src/
|
|
347
|
+
//#region src/factory.ts
|
|
386
348
|
const urlRegExp = /^https?:\/+/i;
|
|
387
349
|
async function readSource(sourcePath) {
|
|
388
350
|
if (urlRegExp.test(sourcePath)) {
|
|
@@ -425,8 +387,6 @@ async function bundleDocument(pathOrUrl) {
|
|
|
425
387
|
await resolver(pathOrUrl);
|
|
426
388
|
return await bundle(pathOrUrl, resolver);
|
|
427
389
|
}
|
|
428
|
-
//#endregion
|
|
429
|
-
//#region src/factory.ts
|
|
430
390
|
/**
|
|
431
391
|
* Loads and bundles an OpenAPI document, returning the raw `Document`.
|
|
432
392
|
*
|
|
@@ -496,19 +456,12 @@ async function validateDocument(document, { throwOnError = false } = {}) {
|
|
|
496
456
|
}
|
|
497
457
|
}
|
|
498
458
|
//#endregion
|
|
499
|
-
//#region src/
|
|
459
|
+
//#region src/oas.ts
|
|
500
460
|
/**
|
|
501
461
|
* Returns `true` when a schema should be treated as nullable.
|
|
502
462
|
*
|
|
503
463
|
* Recognizes all nullable signals across OAS versions: `nullable: true` (OAS 3.0),
|
|
504
464
|
* `x-nullable: true` (vendor extension), `type: 'null'`, and `type: ['null', ...]` (OAS 3.1).
|
|
505
|
-
*
|
|
506
|
-
* @example
|
|
507
|
-
* ```ts
|
|
508
|
-
* isNullable({ type: 'string', nullable: true }) // true
|
|
509
|
-
* isNullable({ type: ['string', 'null'] }) // true
|
|
510
|
-
* isNullable({ type: 'string' }) // false
|
|
511
|
-
* ```
|
|
512
465
|
*/
|
|
513
466
|
function isNullable(schema) {
|
|
514
467
|
if ((schema?.nullable ?? schema?.["x-nullable"]) === true) return true;
|
|
@@ -519,31 +472,24 @@ function isNullable(schema) {
|
|
|
519
472
|
}
|
|
520
473
|
/**
|
|
521
474
|
* Returns `true` when `obj` is an OpenAPI `$ref` pointer object.
|
|
522
|
-
*
|
|
523
|
-
* @example
|
|
524
|
-
* ```ts
|
|
525
|
-
* isReference({ $ref: '#/components/schemas/Pet' }) // true
|
|
526
|
-
* isReference({ type: 'string' }) // false
|
|
527
|
-
* ```
|
|
528
475
|
*/
|
|
529
476
|
function isReference(obj) {
|
|
530
477
|
return !!obj && typeof obj === "object" && "$ref" in obj;
|
|
531
478
|
}
|
|
532
479
|
/**
|
|
533
|
-
* Returns `true` when `obj` is a schema with a structured OAS 3.x `discriminator` object
|
|
534
|
-
*
|
|
535
|
-
* @example
|
|
536
|
-
* ```ts
|
|
537
|
-
* isDiscriminator({ discriminator: { propertyName: 'type', mapping: {} } }) // true
|
|
538
|
-
* isDiscriminator({ discriminator: 'type' }) // false (Swagger 2 string form)
|
|
539
|
-
* ```
|
|
480
|
+
* Returns `true` when `obj` is a schema with a structured OAS 3.x `discriminator` object,
|
|
481
|
+
* excluding the Swagger 2 string form.
|
|
540
482
|
*/
|
|
541
483
|
function isDiscriminator(obj) {
|
|
542
484
|
const record = obj;
|
|
543
485
|
return !!obj && !!record["discriminator"] && typeof record["discriminator"] !== "string";
|
|
544
486
|
}
|
|
545
|
-
|
|
546
|
-
|
|
487
|
+
/**
|
|
488
|
+
* Returns `true` when a schema is a binary payload: an octet-stream string body.
|
|
489
|
+
*/
|
|
490
|
+
function isBinary(schema) {
|
|
491
|
+
return schema.type === "string" && schema.contentMediaType === "application/octet-stream";
|
|
492
|
+
}
|
|
547
493
|
/**
|
|
548
494
|
* MIME type fragments that mark a media type as JSON-like.
|
|
549
495
|
*
|
|
@@ -636,6 +582,23 @@ function dereferenceWithRef(document, schema) {
|
|
|
636
582
|
};
|
|
637
583
|
return schema;
|
|
638
584
|
}
|
|
585
|
+
/**
|
|
586
|
+
* Resolves a `$ref` slot in place: when `container[key]` holds a `$ref`, replaces it with the
|
|
587
|
+
* resolved value and returns that value. Returns `null` when the slot is empty, cannot be resolved,
|
|
588
|
+
* or is still a `$ref` after resolving. A non-`$ref` value is returned untouched, without writing.
|
|
589
|
+
*
|
|
590
|
+
* @example
|
|
591
|
+
* ```ts
|
|
592
|
+
* derefInPlace<ResponseObject>({ document, container: operation.schema.responses, key: '200' })
|
|
593
|
+
* ```
|
|
594
|
+
*/
|
|
595
|
+
function derefInPlace({ document, container, key }) {
|
|
596
|
+
const value = container[key];
|
|
597
|
+
if (!isReference(value)) return value ? value : null;
|
|
598
|
+
const resolved = resolveRef(document, value.$ref);
|
|
599
|
+
container[key] = resolved;
|
|
600
|
+
return resolved && !isReference(resolved) ? resolved : null;
|
|
601
|
+
}
|
|
639
602
|
//#endregion
|
|
640
603
|
//#region src/operation.ts
|
|
641
604
|
/**
|
|
@@ -667,31 +630,22 @@ function getResponseStatusCodes({ schema }) {
|
|
|
667
630
|
function getResponseByStatusCode({ document, operation, statusCode }) {
|
|
668
631
|
const responses = operation.schema.responses;
|
|
669
632
|
if (!responses || isReference(responses)) return false;
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
if (!resolved || isReference(resolved)) return false;
|
|
676
|
-
return resolved;
|
|
677
|
-
}
|
|
678
|
-
return response;
|
|
633
|
+
return derefInPlace({
|
|
634
|
+
document,
|
|
635
|
+
container: responses,
|
|
636
|
+
key: statusCode
|
|
637
|
+
}) ?? false;
|
|
679
638
|
}
|
|
680
639
|
/**
|
|
681
640
|
* Resolves the request body (dereferencing a `$ref` in place) and returns its content map, or
|
|
682
641
|
* `undefined` when the operation has no request body.
|
|
683
642
|
*/
|
|
684
643
|
function getRequestBodyContent({ document, operation }) {
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
schema.requestBody = resolved;
|
|
691
|
-
if (!resolved || isReference(resolved)) return;
|
|
692
|
-
requestBody = resolved;
|
|
693
|
-
}
|
|
694
|
-
return requestBody.content;
|
|
644
|
+
return derefInPlace({
|
|
645
|
+
document,
|
|
646
|
+
container: operation.schema,
|
|
647
|
+
key: "requestBody"
|
|
648
|
+
})?.content;
|
|
695
649
|
}
|
|
696
650
|
/**
|
|
697
651
|
* Returns the request body media type. With `mediaType` set, returns that entry or `false`.
|
|
@@ -740,14 +694,12 @@ function getOperations(document) {
|
|
|
740
694
|
if (!paths) return operations;
|
|
741
695
|
for (const path of Object.keys(paths)) {
|
|
742
696
|
if (path.startsWith("x-")) continue;
|
|
743
|
-
|
|
697
|
+
const pathItem = derefInPlace({
|
|
698
|
+
document,
|
|
699
|
+
container: paths,
|
|
700
|
+
key: path
|
|
701
|
+
});
|
|
744
702
|
if (!pathItem) continue;
|
|
745
|
-
if (isReference(pathItem)) {
|
|
746
|
-
const resolved = resolveRef(document, pathItem.$ref);
|
|
747
|
-
paths[path] = resolved;
|
|
748
|
-
if (!resolved || isReference(resolved)) continue;
|
|
749
|
-
pathItem = resolved;
|
|
750
|
-
}
|
|
751
703
|
const item = pathItem;
|
|
752
704
|
for (const method of Object.keys(item)) {
|
|
753
705
|
if (!SUPPORTED_METHODS.has(method)) continue;
|
|
@@ -763,37 +715,6 @@ function getOperations(document) {
|
|
|
763
715
|
return operations;
|
|
764
716
|
}
|
|
765
717
|
//#endregion
|
|
766
|
-
//#region src/dialect.ts
|
|
767
|
-
/**
|
|
768
|
-
* The OpenAPI / Swagger dialect, the default used by `@kubb/adapter-oas`.
|
|
769
|
-
*
|
|
770
|
-
* Implements the spec-agnostic {@link ast.SchemaDialect} contract: it isolates the
|
|
771
|
-
* decisions that differ between specs (nullability, `$ref`, discriminator, binary,
|
|
772
|
-
* ref resolution) so the converter pipeline and dispatch rules stay shared. A
|
|
773
|
-
* future adapter (e.g. AsyncAPI) ships its own dialect, `type: ['null', …]`
|
|
774
|
-
* nullability, no discriminator object, binary via `contentEncoding` and reuses
|
|
775
|
-
* the rest unchanged.
|
|
776
|
-
*
|
|
777
|
-
* Formats (`uuid`, `email`, dates, …) are intentionally NOT here: they are shared
|
|
778
|
-
* JSON Schema vocabulary, so the converters keep that common case.
|
|
779
|
-
*
|
|
780
|
-
* @example
|
|
781
|
-
* ```ts
|
|
782
|
-
* const parser = createSchemaParser(context) // uses oasDialect
|
|
783
|
-
* const parser = createSchemaParser(context, oasDialect) // explicit
|
|
784
|
-
* ```
|
|
785
|
-
*/
|
|
786
|
-
const oasDialect = ast.defineDialect({
|
|
787
|
-
name: "oas",
|
|
788
|
-
schema: {
|
|
789
|
-
isNullable,
|
|
790
|
-
isReference,
|
|
791
|
-
isDiscriminator,
|
|
792
|
-
isBinary: (schema) => schema.type === "string" && schema.contentMediaType === "application/octet-stream",
|
|
793
|
-
resolveRef
|
|
794
|
-
}
|
|
795
|
-
});
|
|
796
|
-
//#endregion
|
|
797
718
|
//#region src/resolvers.ts
|
|
798
719
|
/**
|
|
799
720
|
* Reads the server URL from the document's `servers` array at `server.index`,
|
|
@@ -901,19 +822,19 @@ function getResponseBody(responseBody, contentType) {
|
|
|
901
822
|
if (!(contentType in body.content)) return false;
|
|
902
823
|
return body.content[contentType];
|
|
903
824
|
}
|
|
904
|
-
let availableContentType;
|
|
905
825
|
const contentTypes = Object.keys(body.content);
|
|
906
|
-
|
|
907
|
-
|
|
908
|
-
|
|
909
|
-
|
|
910
|
-
|
|
911
|
-
|
|
912
|
-
|
|
913
|
-
|
|
914
|
-
|
|
915
|
-
|
|
916
|
-
|
|
826
|
+
const availableContentType = contentTypes.find(isJsonMimeType) ?? contentTypes[0];
|
|
827
|
+
if (!availableContentType) return false;
|
|
828
|
+
return body.content[availableContentType];
|
|
829
|
+
}
|
|
830
|
+
function resolveResponseRefs(document, operation) {
|
|
831
|
+
const responses = operation.schema.responses;
|
|
832
|
+
if (!responses) return;
|
|
833
|
+
for (const key in responses) derefInPlace({
|
|
834
|
+
document,
|
|
835
|
+
container: responses,
|
|
836
|
+
key
|
|
837
|
+
});
|
|
917
838
|
}
|
|
918
839
|
/**
|
|
919
840
|
* Returns the response schema for a given operation and HTTP status code.
|
|
@@ -926,14 +847,6 @@ function getResponseBody(responseBody, contentType) {
|
|
|
926
847
|
* getResponseSchema(document, operation, '4XX') // {}
|
|
927
848
|
* ```
|
|
928
849
|
*/
|
|
929
|
-
function resolveResponseRefs(document, operation) {
|
|
930
|
-
const responses = operation.schema.responses;
|
|
931
|
-
if (!responses) return;
|
|
932
|
-
for (const key in responses) {
|
|
933
|
-
const schema = responses[key];
|
|
934
|
-
if (schema && isReference(schema)) responses[key] = resolveRef(document, schema.$ref);
|
|
935
|
-
}
|
|
936
|
-
}
|
|
937
850
|
function getResponseSchema(document, operation, statusCode, options = {}) {
|
|
938
851
|
resolveResponseRefs(document, operation);
|
|
939
852
|
const responseBody = getResponseBody(getResponseByStatusCode({
|
|
@@ -942,7 +855,7 @@ function getResponseSchema(document, operation, statusCode, options = {}) {
|
|
|
942
855
|
statusCode
|
|
943
856
|
}), options.contentType);
|
|
944
857
|
if (responseBody === false) return {};
|
|
945
|
-
const schema =
|
|
858
|
+
const schema = responseBody.schema;
|
|
946
859
|
if (!schema) return {};
|
|
947
860
|
return dereferenceWithRef(document, schema);
|
|
948
861
|
}
|
|
@@ -972,6 +885,16 @@ function getRequestSchema(document, operation, options = {}) {
|
|
|
972
885
|
return dereferenceWithRef(document, schema);
|
|
973
886
|
}
|
|
974
887
|
/**
|
|
888
|
+
* Returns `true` when `fragment` carries any JSON Schema keyword that makes it
|
|
889
|
+
* structurally significant on its own (see `structuralKeys`).
|
|
890
|
+
*
|
|
891
|
+
* A fragment with a structural keyword can't be safely merged into a parent schema.
|
|
892
|
+
*/
|
|
893
|
+
function hasStructuralKeywords(fragment) {
|
|
894
|
+
for (const key in fragment) if (structuralKeys.has(key)) return true;
|
|
895
|
+
return false;
|
|
896
|
+
}
|
|
897
|
+
/**
|
|
975
898
|
* Flattens a keyword-only `allOf` into its parent schema.
|
|
976
899
|
*
|
|
977
900
|
* Only flattens when every member is a plain fragment, with no `$ref` and no structural keywords
|
|
@@ -990,16 +913,6 @@ function getRequestSchema(document, operation, options = {}) {
|
|
|
990
913
|
* // returned unchanged, contains a $ref
|
|
991
914
|
* ```
|
|
992
915
|
*/
|
|
993
|
-
/**
|
|
994
|
-
* Returns `true` when `fragment` carries any JSON Schema keyword that makes it
|
|
995
|
-
* structurally significant on its own (see `structuralKeys`).
|
|
996
|
-
*
|
|
997
|
-
* A fragment with a structural keyword can't be safely merged into a parent schema.
|
|
998
|
-
*/
|
|
999
|
-
function hasStructuralKeywords(fragment) {
|
|
1000
|
-
for (const key in fragment) if (structuralKeys.has(key)) return true;
|
|
1001
|
-
return false;
|
|
1002
|
-
}
|
|
1003
916
|
function flattenSchema(schema) {
|
|
1004
917
|
if (!schema?.allOf || schema.allOf.length === 0) return schema ?? null;
|
|
1005
918
|
const allOfFragments = schema.allOf;
|
|
@@ -1251,7 +1164,7 @@ function getResponseBodyContentTypes(document, operation, statusCode) {
|
|
|
1251
1164
|
return body.content ? Object.keys(body.content) : [];
|
|
1252
1165
|
}
|
|
1253
1166
|
//#endregion
|
|
1254
|
-
//#region src/
|
|
1167
|
+
//#region src/converters.ts
|
|
1255
1168
|
/**
|
|
1256
1169
|
* Normalizes malformed `{ type: 'array', enum: [...] }` schemas by moving enum values into items.
|
|
1257
1170
|
*
|
|
@@ -1275,7 +1188,7 @@ function normalizeArrayEnum(schema) {
|
|
|
1275
1188
|
* Builds a `null` scalar node carrying the schema's documentation. Shared by the `const: null`
|
|
1276
1189
|
* and the drf-spectacular `NullEnum` (`{ enum: [null] }`) branches, which render identically.
|
|
1277
1190
|
*/
|
|
1278
|
-
function
|
|
1191
|
+
function createNullNode(schema, name, nullable) {
|
|
1279
1192
|
return ast.factory.createSchema({
|
|
1280
1193
|
type: "null",
|
|
1281
1194
|
primitive: "null",
|
|
@@ -1305,653 +1218,636 @@ function nameEnums(node, options) {
|
|
|
1305
1218
|
return named;
|
|
1306
1219
|
}
|
|
1307
1220
|
/**
|
|
1308
|
-
*
|
|
1309
|
-
*
|
|
1310
|
-
* Returns closures that share mutable state (`resolvingRefs` set for cycle detection).
|
|
1311
|
-
* Each converter branch (`convertRef`, `convertAllOf`, etc.) mutually recursively calls `parseSchema`,
|
|
1312
|
-
* which works because function declarations hoist.
|
|
1221
|
+
* Converts a `$ref` schema into a `RefSchemaNode`.
|
|
1313
1222
|
*
|
|
1314
|
-
*
|
|
1223
|
+
* The resolved schema is stored in `node.schema`. Usage-site sibling fields
|
|
1224
|
+
* (description, readOnly, nullable, etc.) are stored directly on the ref node.
|
|
1225
|
+
* Use `syncSchemaRef(node)` in printers to get a merged view of both.
|
|
1226
|
+
* Circular refs are detected in `resolveRefNode` and leave `schema` as `null`.
|
|
1315
1227
|
*/
|
|
1316
|
-
function
|
|
1317
|
-
const
|
|
1318
|
-
|
|
1319
|
-
|
|
1320
|
-
|
|
1321
|
-
|
|
1322
|
-
|
|
1323
|
-
|
|
1324
|
-
|
|
1325
|
-
|
|
1326
|
-
|
|
1327
|
-
|
|
1328
|
-
|
|
1329
|
-
|
|
1330
|
-
|
|
1331
|
-
|
|
1332
|
-
|
|
1333
|
-
|
|
1334
|
-
|
|
1335
|
-
|
|
1336
|
-
|
|
1337
|
-
|
|
1338
|
-
|
|
1339
|
-
|
|
1340
|
-
|
|
1341
|
-
|
|
1342
|
-
|
|
1343
|
-
|
|
1344
|
-
exists = false;
|
|
1345
|
-
}
|
|
1346
|
-
refExistence.set(refPath, exists);
|
|
1347
|
-
}
|
|
1348
|
-
return refExistence.get(refPath) ?? false;
|
|
1349
|
-
}
|
|
1350
|
-
/**
|
|
1351
|
-
* Converts a `$ref` schema into a `RefSchemaNode`.
|
|
1352
|
-
*
|
|
1353
|
-
* The resolved schema is stored in `node.schema`. Usage-site sibling fields
|
|
1354
|
-
* (description, readOnly, nullable, etc.) are stored directly on the ref node.
|
|
1355
|
-
* Use `syncSchemaRef(node)` in printers to get a merged view of both.
|
|
1356
|
-
* Circular refs are detected via `resolvingRefs` and leave `schema` as `undefined`.
|
|
1357
|
-
*/
|
|
1358
|
-
function convertRef({ schema, name, nullable, defaultValue, rawOptions }) {
|
|
1359
|
-
let resolvedSchema = null;
|
|
1360
|
-
const refPath = schema.$ref;
|
|
1361
|
-
if (refPath && !resolvingRefs.has(refPath)) {
|
|
1362
|
-
if (!resolvedRefCache.has(refPath)) {
|
|
1363
|
-
try {
|
|
1364
|
-
const referenced = dialect.schema.resolveRef(document, refPath);
|
|
1365
|
-
if (referenced) {
|
|
1366
|
-
resolvingRefs.add(refPath);
|
|
1367
|
-
resolvedSchema = parseSchema({ schema: referenced }, rawOptions);
|
|
1368
|
-
resolvingRefs.delete(refPath);
|
|
1369
|
-
}
|
|
1370
|
-
} catch {}
|
|
1371
|
-
resolvedRefCache.set(refPath, resolvedSchema);
|
|
1372
|
-
}
|
|
1373
|
-
resolvedSchema = resolvedRefCache.get(refPath) ?? null;
|
|
1374
|
-
}
|
|
1375
|
-
if (refPath && document.components && !refExists(refPath)) return ast.factory.createSchema({
|
|
1376
|
-
...buildSchemaNode(schema, name, nullable, defaultValue),
|
|
1377
|
-
type: "unknown"
|
|
1378
|
-
});
|
|
1228
|
+
function convertRef({ schema, name, nullable, defaultValue, rawOptions, document, resolveRefNode, refExists }) {
|
|
1229
|
+
const refPath = schema.$ref;
|
|
1230
|
+
const resolvedSchema = refPath ? resolveRefNode(refPath, rawOptions) : null;
|
|
1231
|
+
if (refPath && document.components && !refExists(refPath)) return ast.factory.createSchema({
|
|
1232
|
+
...buildSchemaNode(schema, name, nullable, defaultValue),
|
|
1233
|
+
type: "unknown"
|
|
1234
|
+
});
|
|
1235
|
+
return ast.factory.createSchema({
|
|
1236
|
+
...buildSchemaNode(schema, name, nullable, defaultValue),
|
|
1237
|
+
type: "ref",
|
|
1238
|
+
name: extractRefName(schema.$ref),
|
|
1239
|
+
ref: schema.$ref,
|
|
1240
|
+
schema: resolvedSchema
|
|
1241
|
+
});
|
|
1242
|
+
}
|
|
1243
|
+
/**
|
|
1244
|
+
* Converts an `allOf` schema into a flattened node or an `IntersectionSchemaNode`.
|
|
1245
|
+
*/
|
|
1246
|
+
function convertAllOf({ schema, name, nullable, defaultValue, rawOptions, parse, document }) {
|
|
1247
|
+
if (schema.allOf.length === 1 && !schema.properties && !(Array.isArray(schema.required) && schema.required.length) && schema.additionalProperties === void 0) {
|
|
1248
|
+
const [memberSchema] = schema.allOf;
|
|
1249
|
+
const memberNode = parse({
|
|
1250
|
+
schema: memberSchema,
|
|
1251
|
+
name
|
|
1252
|
+
}, rawOptions);
|
|
1253
|
+
const { kind: _kind, ...memberNodeProps } = memberNode;
|
|
1254
|
+
const mergedNullable = nullable || memberNode.nullable || void 0;
|
|
1255
|
+
const mergedDefault = schema.default === null && mergedNullable ? void 0 : schema.default ?? memberNode.default;
|
|
1379
1256
|
return ast.factory.createSchema({
|
|
1380
|
-
...
|
|
1381
|
-
|
|
1382
|
-
|
|
1383
|
-
|
|
1384
|
-
|
|
1257
|
+
...memberNodeProps,
|
|
1258
|
+
name,
|
|
1259
|
+
title: schema.title ?? memberNode.title,
|
|
1260
|
+
description: schema.description ?? memberNode.description,
|
|
1261
|
+
deprecated: schema.deprecated ?? memberNode.deprecated,
|
|
1262
|
+
nullable: mergedNullable,
|
|
1263
|
+
readOnly: schema.readOnly ?? memberNode.readOnly,
|
|
1264
|
+
writeOnly: schema.writeOnly ?? memberNode.writeOnly,
|
|
1265
|
+
default: mergedDefault,
|
|
1266
|
+
examples: extractExamples(schema) ?? memberNode.examples,
|
|
1267
|
+
pattern: schema.pattern ?? ("pattern" in memberNode ? memberNode.pattern : void 0),
|
|
1268
|
+
format: schema.format ?? memberNode.format
|
|
1385
1269
|
});
|
|
1386
1270
|
}
|
|
1387
|
-
|
|
1388
|
-
|
|
1389
|
-
|
|
1390
|
-
|
|
1391
|
-
if (
|
|
1392
|
-
|
|
1393
|
-
|
|
1394
|
-
|
|
1395
|
-
|
|
1396
|
-
|
|
1397
|
-
|
|
1398
|
-
const
|
|
1399
|
-
|
|
1400
|
-
|
|
1401
|
-
|
|
1402
|
-
name,
|
|
1403
|
-
title: schema.title ?? memberNode.title,
|
|
1404
|
-
description: schema.description ?? memberNode.description,
|
|
1405
|
-
deprecated: schema.deprecated ?? memberNode.deprecated,
|
|
1406
|
-
nullable: mergedNullable,
|
|
1407
|
-
readOnly: schema.readOnly ?? memberNode.readOnly,
|
|
1408
|
-
writeOnly: schema.writeOnly ?? memberNode.writeOnly,
|
|
1409
|
-
default: mergedDefault,
|
|
1410
|
-
examples: extractExamples(schema) ?? memberNode.examples,
|
|
1411
|
-
pattern: schema.pattern ?? ("pattern" in memberNode ? memberNode.pattern : void 0),
|
|
1412
|
-
format: schema.format ?? memberNode.format
|
|
1271
|
+
const filteredDiscriminantValues = [];
|
|
1272
|
+
const allOfMembers = schema.allOf.filter((item) => {
|
|
1273
|
+
if (!isReference(item) || !name) return true;
|
|
1274
|
+
const deref = resolveRef(document, item.$ref);
|
|
1275
|
+
if (!deref || !isDiscriminator(deref)) return true;
|
|
1276
|
+
const parentUnion = deref.oneOf ?? deref.anyOf;
|
|
1277
|
+
if (!parentUnion) return true;
|
|
1278
|
+
const childRef = `${SCHEMA_REF_PREFIX}${name}`;
|
|
1279
|
+
const inOneOf = parentUnion.some((oneOfItem) => isReference(oneOfItem) && oneOfItem.$ref === childRef);
|
|
1280
|
+
const inMapping = Object.values(deref.discriminator.mapping ?? {}).some((v) => v === childRef);
|
|
1281
|
+
if (inOneOf || inMapping) {
|
|
1282
|
+
const discriminatorValue = findDiscriminator(deref.discriminator.mapping, childRef);
|
|
1283
|
+
if (discriminatorValue) filteredDiscriminantValues.push({
|
|
1284
|
+
propertyName: deref.discriminator.propertyName,
|
|
1285
|
+
value: discriminatorValue
|
|
1413
1286
|
});
|
|
1287
|
+
return false;
|
|
1414
1288
|
}
|
|
1415
|
-
|
|
1416
|
-
|
|
1417
|
-
|
|
1418
|
-
|
|
1419
|
-
|
|
1420
|
-
|
|
1421
|
-
|
|
1422
|
-
|
|
1423
|
-
|
|
1424
|
-
|
|
1425
|
-
|
|
1426
|
-
|
|
1427
|
-
|
|
1428
|
-
|
|
1429
|
-
|
|
1430
|
-
|
|
1431
|
-
|
|
1432
|
-
|
|
1433
|
-
|
|
1434
|
-
|
|
1435
|
-
|
|
1436
|
-
|
|
1437
|
-
|
|
1438
|
-
|
|
1439
|
-
if (Array.isArray(schema.required) && schema.required.length) {
|
|
1440
|
-
const outerKeys = schema.properties ? new Set(Object.keys(schema.properties)) : /* @__PURE__ */ new Set();
|
|
1441
|
-
const missingRequired = schema.required.filter((key) => !outerKeys.has(key));
|
|
1442
|
-
if (missingRequired.length) {
|
|
1443
|
-
const resolvedMembers = schema.allOf.flatMap((item) => {
|
|
1444
|
-
if (!dialect.schema.isReference(item)) return [item];
|
|
1445
|
-
const deref = dialect.schema.resolveRef(document, item.$ref);
|
|
1446
|
-
return deref && !dialect.schema.isReference(deref) ? [deref] : [];
|
|
1447
|
-
});
|
|
1448
|
-
for (const key of missingRequired) for (const resolved of resolvedMembers) if (resolved.properties?.[key]) {
|
|
1449
|
-
allOfMembers.push(parseSchema({
|
|
1450
|
-
schema: {
|
|
1451
|
-
properties: { [key]: resolved.properties[key] },
|
|
1452
|
-
required: [key]
|
|
1453
|
-
},
|
|
1289
|
+
return true;
|
|
1290
|
+
}).map((s) => parse({
|
|
1291
|
+
schema: s,
|
|
1292
|
+
name
|
|
1293
|
+
}, rawOptions));
|
|
1294
|
+
const syntheticStart = allOfMembers.length;
|
|
1295
|
+
if (Array.isArray(schema.required) && schema.required.length) {
|
|
1296
|
+
const outerKeys = schema.properties ? new Set(Object.keys(schema.properties)) : /* @__PURE__ */ new Set();
|
|
1297
|
+
const missingRequired = schema.required.filter((key) => !outerKeys.has(key));
|
|
1298
|
+
if (missingRequired.length) {
|
|
1299
|
+
const resolvedMembers = schema.allOf.flatMap((item) => {
|
|
1300
|
+
if (!isReference(item)) return [item];
|
|
1301
|
+
const deref = resolveRef(document, item.$ref);
|
|
1302
|
+
return deref && !isReference(deref) ? [deref] : [];
|
|
1303
|
+
});
|
|
1304
|
+
for (const key of missingRequired) for (const resolved of resolvedMembers) {
|
|
1305
|
+
const prop = resolved.properties?.[key];
|
|
1306
|
+
if (prop) {
|
|
1307
|
+
const memberSchema = {
|
|
1308
|
+
properties: { [key]: prop },
|
|
1309
|
+
required: [key]
|
|
1310
|
+
};
|
|
1311
|
+
allOfMembers.push(parse({
|
|
1312
|
+
schema: memberSchema,
|
|
1454
1313
|
name
|
|
1455
1314
|
}, rawOptions));
|
|
1456
1315
|
break;
|
|
1457
1316
|
}
|
|
1458
1317
|
}
|
|
1459
1318
|
}
|
|
1460
|
-
|
|
1461
|
-
|
|
1462
|
-
|
|
1463
|
-
}
|
|
1464
|
-
|
|
1465
|
-
|
|
1466
|
-
|
|
1467
|
-
|
|
1319
|
+
}
|
|
1320
|
+
if (schema.properties) {
|
|
1321
|
+
const { allOf: _allOf, ...schemaWithoutAllOf } = schema;
|
|
1322
|
+
allOfMembers.push(parse({ schema: schemaWithoutAllOf }, rawOptions));
|
|
1323
|
+
}
|
|
1324
|
+
for (const { propertyName, value } of filteredDiscriminantValues) allOfMembers.push(createDiscriminantNode({
|
|
1325
|
+
propertyName,
|
|
1326
|
+
value
|
|
1327
|
+
}));
|
|
1328
|
+
return ast.factory.createSchema({
|
|
1329
|
+
type: "intersection",
|
|
1330
|
+
members: [...mergeAdjacentObjectsLazy(allOfMembers.slice(0, syntheticStart)), ...mergeAdjacentObjectsLazy(allOfMembers.slice(syntheticStart))],
|
|
1331
|
+
...buildSchemaNode(schema, name, nullable, defaultValue)
|
|
1332
|
+
});
|
|
1333
|
+
}
|
|
1334
|
+
/**
|
|
1335
|
+
* Converts a `oneOf` / `anyOf` schema into a `UnionSchemaNode`.
|
|
1336
|
+
*/
|
|
1337
|
+
function convertUnion({ schema, name, nullable, defaultValue, rawOptions, parse, document }) {
|
|
1338
|
+
function pickDiscriminatorPropertyNode(node, propertyName) {
|
|
1339
|
+
const discriminatorProperty = ast.narrowSchema(node, "object")?.properties?.find((property) => property.name === propertyName);
|
|
1340
|
+
if (!discriminatorProperty) return null;
|
|
1468
1341
|
return ast.factory.createSchema({
|
|
1469
|
-
type: "
|
|
1470
|
-
|
|
1471
|
-
|
|
1342
|
+
type: "object",
|
|
1343
|
+
primitive: "object",
|
|
1344
|
+
properties: [discriminatorProperty]
|
|
1472
1345
|
});
|
|
1473
1346
|
}
|
|
1474
|
-
|
|
1475
|
-
|
|
1476
|
-
|
|
1477
|
-
|
|
1478
|
-
|
|
1479
|
-
|
|
1480
|
-
|
|
1481
|
-
|
|
1482
|
-
|
|
1483
|
-
|
|
1484
|
-
|
|
1347
|
+
function resolveRefSilent($ref) {
|
|
1348
|
+
if (!$ref.startsWith("#")) return null;
|
|
1349
|
+
return decodeURIComponent($ref.substring(1)).split("/").filter(Boolean).reduce((obj, key) => obj?.[key], document) ?? null;
|
|
1350
|
+
}
|
|
1351
|
+
function implicitDiscriminantValue(member) {
|
|
1352
|
+
if (!discriminator || discriminator.mapping || !isReference(member)) return null;
|
|
1353
|
+
const value = extractRefName(member.$ref);
|
|
1354
|
+
if (!value) return null;
|
|
1355
|
+
const variant = resolveRefSilent(member.$ref);
|
|
1356
|
+
if (!variant) return null;
|
|
1357
|
+
const propertyName = discriminator.propertyName;
|
|
1358
|
+
const seen = /* @__PURE__ */ new Set([member.$ref]);
|
|
1359
|
+
function constrains(v) {
|
|
1360
|
+
const prop = v.properties?.[propertyName];
|
|
1361
|
+
const resolved = prop && isReference(prop) ? resolveRefSilent(prop.$ref) : prop;
|
|
1362
|
+
if (resolved && (Array.isArray(resolved.enum) || resolved.const !== void 0)) return true;
|
|
1363
|
+
const composition = v.allOf ?? v.oneOf ?? v.anyOf;
|
|
1364
|
+
if (!composition) return false;
|
|
1365
|
+
return composition.some((m) => {
|
|
1366
|
+
if (!isReference(m)) return constrains(m);
|
|
1367
|
+
if (seen.has(m.$ref)) return false;
|
|
1368
|
+
seen.add(m.$ref);
|
|
1369
|
+
const r = resolveRefSilent(m.$ref);
|
|
1370
|
+
return r ? constrains(r) : false;
|
|
1485
1371
|
});
|
|
1486
1372
|
}
|
|
1487
|
-
|
|
1488
|
-
|
|
1489
|
-
|
|
1490
|
-
|
|
1491
|
-
|
|
1492
|
-
|
|
1493
|
-
|
|
1494
|
-
|
|
1495
|
-
|
|
1496
|
-
|
|
1497
|
-
|
|
1498
|
-
|
|
1499
|
-
|
|
1500
|
-
|
|
1501
|
-
|
|
1502
|
-
|
|
1503
|
-
|
|
1504
|
-
|
|
1505
|
-
|
|
1506
|
-
|
|
1507
|
-
|
|
1508
|
-
|
|
1509
|
-
|
|
1510
|
-
|
|
1511
|
-
|
|
1512
|
-
|
|
1513
|
-
|
|
1514
|
-
|
|
1515
|
-
const unionMembers = [...schema.oneOf ?? [], ...schema.anyOf ?? []];
|
|
1516
|
-
const strategy = schema.oneOf ? "one" : "any";
|
|
1517
|
-
const unionBase = {
|
|
1518
|
-
...buildSchemaNode(schema, name, nullable, defaultValue),
|
|
1519
|
-
discriminatorPropertyName: dialect.schema.isDiscriminator(schema) ? schema.discriminator.propertyName : void 0,
|
|
1520
|
-
strategy
|
|
1521
|
-
};
|
|
1522
|
-
const discriminator = dialect.schema.isDiscriminator(schema) ? schema.discriminator : void 0;
|
|
1523
|
-
const { oneOf: _o, anyOf: _a, discriminator: _d, ...memberBaseSchema } = schema;
|
|
1524
|
-
const sharedPropertiesNode = schema.properties ? parseSchema({
|
|
1525
|
-
schema: memberBaseSchema,
|
|
1526
|
-
name
|
|
1527
|
-
}, rawOptions) : void 0;
|
|
1528
|
-
if (sharedPropertiesNode || discriminator) {
|
|
1529
|
-
const members = unionMembers.map((s) => {
|
|
1530
|
-
const ref = dialect.schema.isReference(s) ? s.$ref : void 0;
|
|
1531
|
-
const discriminatorValue = findDiscriminator(discriminator?.mapping, ref) ?? implicitDiscriminantValue(s);
|
|
1532
|
-
const memberNode = parseSchema({
|
|
1533
|
-
schema: s,
|
|
1534
|
-
name
|
|
1535
|
-
}, rawOptions);
|
|
1536
|
-
if (!discriminatorValue || !discriminator) return memberNode;
|
|
1537
|
-
const narrowedDiscriminatorNode = sharedPropertiesNode ? pickDiscriminatorPropertyNode(ast.applyMacros(sharedPropertiesNode, [macroDiscriminatorEnum({
|
|
1538
|
-
propertyName: discriminator.propertyName,
|
|
1539
|
-
values: [discriminatorValue]
|
|
1540
|
-
})], { depth: "shallow" }), discriminator.propertyName) : void 0;
|
|
1541
|
-
return ast.factory.createSchema({
|
|
1542
|
-
type: "intersection",
|
|
1543
|
-
members: [memberNode, narrowedDiscriminatorNode ?? createDiscriminantNode({
|
|
1544
|
-
propertyName: discriminator.propertyName,
|
|
1545
|
-
value: discriminatorValue
|
|
1546
|
-
})]
|
|
1547
|
-
});
|
|
1548
|
-
});
|
|
1549
|
-
const unionNode = ast.factory.createSchema({
|
|
1550
|
-
type: "union",
|
|
1551
|
-
...unionBase,
|
|
1552
|
-
members
|
|
1553
|
-
});
|
|
1554
|
-
if (!sharedPropertiesNode) return unionNode;
|
|
1373
|
+
return constrains(variant) ? null : value;
|
|
1374
|
+
}
|
|
1375
|
+
const unionMembers = [...schema.oneOf ?? [], ...schema.anyOf ?? []];
|
|
1376
|
+
const strategy = schema.oneOf ? "one" : "any";
|
|
1377
|
+
const unionBase = {
|
|
1378
|
+
...buildSchemaNode(schema, name, nullable, defaultValue),
|
|
1379
|
+
discriminatorPropertyName: isDiscriminator(schema) ? schema.discriminator.propertyName : void 0,
|
|
1380
|
+
strategy
|
|
1381
|
+
};
|
|
1382
|
+
const discriminator = isDiscriminator(schema) ? schema.discriminator : void 0;
|
|
1383
|
+
const { oneOf: _o, anyOf: _a, discriminator: _d, ...memberBaseSchema } = schema;
|
|
1384
|
+
const sharedPropertiesNode = schema.properties ? parse({
|
|
1385
|
+
schema: memberBaseSchema,
|
|
1386
|
+
name
|
|
1387
|
+
}, rawOptions) : void 0;
|
|
1388
|
+
if (sharedPropertiesNode || discriminator) {
|
|
1389
|
+
const members = unionMembers.map((s) => {
|
|
1390
|
+
const ref = isReference(s) ? s.$ref : void 0;
|
|
1391
|
+
const discriminatorValue = findDiscriminator(discriminator?.mapping, ref) ?? implicitDiscriminantValue(s);
|
|
1392
|
+
const memberNode = parse({
|
|
1393
|
+
schema: s,
|
|
1394
|
+
name
|
|
1395
|
+
}, rawOptions);
|
|
1396
|
+
if (!discriminatorValue || !discriminator) return memberNode;
|
|
1397
|
+
const narrowedDiscriminatorNode = sharedPropertiesNode ? pickDiscriminatorPropertyNode(ast.applyMacros(sharedPropertiesNode, [macroDiscriminatorEnum({
|
|
1398
|
+
propertyName: discriminator.propertyName,
|
|
1399
|
+
values: [discriminatorValue]
|
|
1400
|
+
})], { depth: "shallow" }), discriminator.propertyName) : void 0;
|
|
1555
1401
|
return ast.factory.createSchema({
|
|
1556
1402
|
type: "intersection",
|
|
1557
|
-
|
|
1558
|
-
|
|
1403
|
+
members: [memberNode, narrowedDiscriminatorNode ?? createDiscriminantNode({
|
|
1404
|
+
propertyName: discriminator.propertyName,
|
|
1405
|
+
value: discriminatorValue
|
|
1406
|
+
})]
|
|
1559
1407
|
});
|
|
1560
|
-
}
|
|
1408
|
+
});
|
|
1561
1409
|
const unionNode = ast.factory.createSchema({
|
|
1562
1410
|
type: "union",
|
|
1563
1411
|
...unionBase,
|
|
1564
|
-
members
|
|
1565
|
-
schema: s,
|
|
1566
|
-
name
|
|
1567
|
-
}, rawOptions))
|
|
1412
|
+
members
|
|
1568
1413
|
});
|
|
1569
|
-
|
|
1570
|
-
}
|
|
1571
|
-
/**
|
|
1572
|
-
* Converts an OAS 3.1 `const` schema into a null scalar or a single-value `EnumSchemaNode`.
|
|
1573
|
-
*/
|
|
1574
|
-
function convertConst({ schema, name, nullable, defaultValue }) {
|
|
1575
|
-
const constValue = schema.const;
|
|
1576
|
-
if (constValue === null) return createNullSchema(schema, name);
|
|
1577
|
-
const constPrimitive = getPrimitiveType(typeof constValue === "number" ? "number" : typeof constValue === "boolean" ? "boolean" : "string");
|
|
1414
|
+
if (!sharedPropertiesNode) return unionNode;
|
|
1578
1415
|
return ast.factory.createSchema({
|
|
1579
|
-
type: "
|
|
1580
|
-
|
|
1581
|
-
|
|
1582
|
-
...buildSchemaNode(schema, name, nullable, defaultValue)
|
|
1416
|
+
type: "intersection",
|
|
1417
|
+
...buildSchemaNode(schema, name, nullable, defaultValue),
|
|
1418
|
+
members: [unionNode, sharedPropertiesNode]
|
|
1583
1419
|
});
|
|
1584
1420
|
}
|
|
1585
|
-
|
|
1586
|
-
|
|
1587
|
-
|
|
1588
|
-
|
|
1589
|
-
|
|
1590
|
-
|
|
1591
|
-
|
|
1592
|
-
|
|
1593
|
-
|
|
1594
|
-
|
|
1595
|
-
|
|
1596
|
-
|
|
1597
|
-
|
|
1598
|
-
|
|
1599
|
-
|
|
1600
|
-
|
|
1601
|
-
|
|
1602
|
-
|
|
1603
|
-
|
|
1604
|
-
|
|
1605
|
-
|
|
1606
|
-
|
|
1607
|
-
|
|
1608
|
-
|
|
1609
|
-
|
|
1610
|
-
|
|
1611
|
-
|
|
1612
|
-
|
|
1613
|
-
|
|
1614
|
-
|
|
1615
|
-
|
|
1616
|
-
|
|
1617
|
-
|
|
1618
|
-
|
|
1619
|
-
|
|
1620
|
-
|
|
1621
|
-
|
|
1622
|
-
|
|
1623
|
-
|
|
1624
|
-
|
|
1625
|
-
|
|
1626
|
-
|
|
1627
|
-
|
|
1628
|
-
type: "url",
|
|
1629
|
-
min: schema.minLength,
|
|
1630
|
-
max: schema.maxLength
|
|
1631
|
-
});
|
|
1632
|
-
if (specialType === "ipv4") return ast.factory.createSchema({
|
|
1421
|
+
const unionNode = ast.factory.createSchema({
|
|
1422
|
+
type: "union",
|
|
1423
|
+
...unionBase,
|
|
1424
|
+
members: unionMembers.map((s) => parse({
|
|
1425
|
+
schema: s,
|
|
1426
|
+
name
|
|
1427
|
+
}, rawOptions))
|
|
1428
|
+
});
|
|
1429
|
+
return ast.applyMacros(unionNode, [macroSimplifyUnion], { depth: "shallow" });
|
|
1430
|
+
}
|
|
1431
|
+
/**
|
|
1432
|
+
* Converts an OAS 3.1 `const` schema into a null scalar or a single-value `EnumSchemaNode`.
|
|
1433
|
+
*/
|
|
1434
|
+
function convertConst({ schema, name, nullable, defaultValue }) {
|
|
1435
|
+
const constValue = schema.const;
|
|
1436
|
+
if (constValue === null) return createNullNode(schema, name);
|
|
1437
|
+
const constPrimitive = getPrimitiveType(typeof constValue === "number" ? "number" : typeof constValue === "boolean" ? "boolean" : "string");
|
|
1438
|
+
return ast.factory.createSchema({
|
|
1439
|
+
type: "enum",
|
|
1440
|
+
primitive: constPrimitive,
|
|
1441
|
+
enumValues: [constValue],
|
|
1442
|
+
...buildSchemaNode(schema, name, nullable, defaultValue)
|
|
1443
|
+
});
|
|
1444
|
+
}
|
|
1445
|
+
/**
|
|
1446
|
+
* Converts a format-annotated schema into a special-type `SchemaNode`.
|
|
1447
|
+
* Returns `null` when the format should fall through to string handling (`dateType: false`).
|
|
1448
|
+
*/
|
|
1449
|
+
function convertFormat({ schema, name, nullable, defaultValue, options }) {
|
|
1450
|
+
const base = buildSchemaNode(schema, name, nullable, defaultValue);
|
|
1451
|
+
if (schema.format === "int64") return ast.factory.createSchema({
|
|
1452
|
+
type: options.integerType === "bigint" ? "bigint" : "integer",
|
|
1453
|
+
primitive: "integer",
|
|
1454
|
+
...base,
|
|
1455
|
+
min: schema.minimum,
|
|
1456
|
+
max: schema.maximum,
|
|
1457
|
+
exclusiveMinimum: typeof schema.exclusiveMinimum === "number" ? schema.exclusiveMinimum : void 0,
|
|
1458
|
+
exclusiveMaximum: typeof schema.exclusiveMaximum === "number" ? schema.exclusiveMaximum : void 0
|
|
1459
|
+
});
|
|
1460
|
+
if (schema.format === "date-time" || schema.format === "date" || schema.format === "time") {
|
|
1461
|
+
const dateType = getDateType(options, schema.format);
|
|
1462
|
+
if (!dateType) return null;
|
|
1463
|
+
if (dateType.type === "datetime") return ast.factory.createSchema({
|
|
1633
1464
|
...base,
|
|
1634
1465
|
primitive: "string",
|
|
1635
|
-
type: "
|
|
1466
|
+
type: "datetime",
|
|
1467
|
+
offset: dateType.offset,
|
|
1468
|
+
local: dateType.local
|
|
1636
1469
|
});
|
|
1637
|
-
|
|
1470
|
+
return ast.factory.createSchema({
|
|
1638
1471
|
...base,
|
|
1639
1472
|
primitive: "string",
|
|
1640
|
-
type:
|
|
1473
|
+
type: dateType.type,
|
|
1474
|
+
representation: dateType.representation
|
|
1641
1475
|
});
|
|
1642
|
-
|
|
1643
|
-
|
|
1644
|
-
|
|
1645
|
-
|
|
1476
|
+
}
|
|
1477
|
+
const specialType = getSchemaType(schema.format);
|
|
1478
|
+
if (!specialType) return null;
|
|
1479
|
+
const specialPrimitive = specialType === "number" || specialType === "integer" || specialType === "bigint" ? specialType : "string";
|
|
1480
|
+
const hasLength = specialType === "url" || specialType === "uuid" || specialType === "email";
|
|
1481
|
+
return ast.factory.createSchema({
|
|
1482
|
+
...base,
|
|
1483
|
+
primitive: specialPrimitive,
|
|
1484
|
+
type: specialType,
|
|
1485
|
+
...hasLength ? {
|
|
1646
1486
|
min: schema.minLength,
|
|
1647
1487
|
max: schema.maxLength
|
|
1648
|
-
}
|
|
1649
|
-
|
|
1650
|
-
|
|
1651
|
-
|
|
1652
|
-
|
|
1653
|
-
|
|
1654
|
-
|
|
1655
|
-
|
|
1656
|
-
|
|
1657
|
-
|
|
1658
|
-
|
|
1659
|
-
|
|
1660
|
-
|
|
1661
|
-
|
|
1662
|
-
|
|
1663
|
-
|
|
1664
|
-
|
|
1665
|
-
|
|
1666
|
-
|
|
1667
|
-
|
|
1668
|
-
|
|
1669
|
-
|
|
1670
|
-
|
|
1671
|
-
|
|
1672
|
-
|
|
1673
|
-
|
|
1674
|
-
|
|
1675
|
-
|
|
1676
|
-
|
|
1677
|
-
|
|
1678
|
-
writeOnly: schema.writeOnly,
|
|
1679
|
-
default: enumDefault,
|
|
1680
|
-
examples: extractExamples(schema),
|
|
1681
|
-
format: schema.format
|
|
1682
|
-
};
|
|
1683
|
-
const extensionKey = enumExtensionKeys.find((key) => key in schema);
|
|
1684
|
-
const descriptionKey = enumDescriptionKeys.find((key) => key in schema);
|
|
1685
|
-
if (extensionKey || descriptionKey || enumPrimitive === "number" || enumPrimitive === "integer" || enumPrimitive === "boolean") {
|
|
1686
|
-
const enumPrimitiveType = enumPrimitive === "number" || enumPrimitive === "integer" ? "number" : enumPrimitive === "boolean" ? "boolean" : "string";
|
|
1687
|
-
const rawEnumNames = extensionKey ? schema[extensionKey] : void 0;
|
|
1688
|
-
const rawEnumDescriptions = descriptionKey ? schema[descriptionKey] : void 0;
|
|
1689
|
-
const uniqueValues = [...new Set(filteredValues)];
|
|
1690
|
-
const seenNames = /* @__PURE__ */ new Set();
|
|
1691
|
-
return ast.factory.createSchema({
|
|
1692
|
-
...enumBase,
|
|
1693
|
-
primitive: enumPrimitiveType,
|
|
1694
|
-
namedEnumValues: uniqueValues.map((value, index) => ({
|
|
1695
|
-
name: String(rawEnumNames?.[index] ?? value),
|
|
1696
|
-
value,
|
|
1697
|
-
primitive: enumPrimitiveType,
|
|
1698
|
-
description: rawEnumDescriptions?.[index]
|
|
1699
|
-
})).filter((entry) => {
|
|
1700
|
-
if (seenNames.has(entry.name)) return false;
|
|
1701
|
-
seenNames.add(entry.name);
|
|
1702
|
-
return true;
|
|
1703
|
-
})
|
|
1704
|
-
});
|
|
1705
|
-
}
|
|
1488
|
+
} : {}
|
|
1489
|
+
});
|
|
1490
|
+
}
|
|
1491
|
+
/**
|
|
1492
|
+
* Converts an `enum` schema into an `EnumSchemaNode`.
|
|
1493
|
+
*/
|
|
1494
|
+
function convertEnum({ schema, name, nullable, type, rawOptions, parse }) {
|
|
1495
|
+
if (type === "array") return parse({
|
|
1496
|
+
schema: normalizeArrayEnum(schema),
|
|
1497
|
+
name
|
|
1498
|
+
}, rawOptions);
|
|
1499
|
+
const nullInEnum = schema.enum.includes(null);
|
|
1500
|
+
const filteredValues = nullInEnum ? schema.enum.filter((v) => v !== null) : schema.enum;
|
|
1501
|
+
if (nullInEnum && filteredValues.length === 0) return createNullNode(schema, name);
|
|
1502
|
+
const enumNullable = nullable || nullInEnum || void 0;
|
|
1503
|
+
const enumDefault = schema.default === null && enumNullable ? void 0 : schema.default;
|
|
1504
|
+
const enumPrimitive = getPrimitiveType(type);
|
|
1505
|
+
const enumBase = {
|
|
1506
|
+
type: "enum",
|
|
1507
|
+
primitive: enumPrimitive,
|
|
1508
|
+
...buildSchemaNode(schema, name, enumNullable, enumDefault)
|
|
1509
|
+
};
|
|
1510
|
+
const extensionKey = enumExtensionKeys.find((key) => key in schema);
|
|
1511
|
+
const descriptionKey = enumDescriptionKeys.find((key) => key in schema);
|
|
1512
|
+
if (extensionKey || descriptionKey || enumPrimitive === "number" || enumPrimitive === "integer" || enumPrimitive === "boolean") {
|
|
1513
|
+
const enumPrimitiveType = enumPrimitive === "number" || enumPrimitive === "integer" ? "number" : enumPrimitive === "boolean" ? "boolean" : "string";
|
|
1514
|
+
const rawEnumNames = extensionKey ? schema[extensionKey] : void 0;
|
|
1515
|
+
const rawEnumDescriptions = descriptionKey ? schema[descriptionKey] : void 0;
|
|
1516
|
+
const uniqueValues = [...new Set(filteredValues)];
|
|
1517
|
+
const seenNames = /* @__PURE__ */ new Set();
|
|
1706
1518
|
return ast.factory.createSchema({
|
|
1707
1519
|
...enumBase,
|
|
1708
|
-
|
|
1709
|
-
|
|
1710
|
-
|
|
1711
|
-
|
|
1712
|
-
|
|
1713
|
-
|
|
1714
|
-
|
|
1715
|
-
|
|
1716
|
-
|
|
1717
|
-
|
|
1718
|
-
|
|
1719
|
-
const schemaNode = nameEnums(parseSchema({
|
|
1720
|
-
schema: resolvedPropSchema,
|
|
1721
|
-
name: childName(name, propName)
|
|
1722
|
-
}, rawOptions), {
|
|
1723
|
-
parentName: name,
|
|
1724
|
-
propName,
|
|
1725
|
-
enumSuffix: options.enumSuffix
|
|
1726
|
-
});
|
|
1727
|
-
return ast.factory.createProperty({
|
|
1728
|
-
name: propName,
|
|
1729
|
-
schema: {
|
|
1730
|
-
...schemaNode,
|
|
1731
|
-
nullable: schemaNode.type === "null" ? void 0 : propNullable || void 0
|
|
1732
|
-
},
|
|
1733
|
-
required
|
|
1734
|
-
});
|
|
1735
|
-
}) : [];
|
|
1736
|
-
const additionalProperties = schema.additionalProperties;
|
|
1737
|
-
const additionalPropertiesNode = (() => {
|
|
1738
|
-
if (additionalProperties === true) return true;
|
|
1739
|
-
if (additionalProperties === false) return false;
|
|
1740
|
-
if (additionalProperties && Object.keys(additionalProperties).length > 0) return parseSchema({ schema: additionalProperties }, rawOptions);
|
|
1741
|
-
if (additionalProperties) return ast.factory.createSchema({ type: options.unknownType });
|
|
1742
|
-
})();
|
|
1743
|
-
const rawPatternProperties = "patternProperties" in schema ? schema.patternProperties : void 0;
|
|
1744
|
-
const patternProperties = rawPatternProperties ? Object.fromEntries(Object.entries(rawPatternProperties).map(([pattern, patternSchema]) => [pattern, patternSchema === true || typeof patternSchema === "object" && Object.keys(patternSchema).length === 0 ? ast.factory.createSchema({ type: options.unknownType }) : parseSchema({ schema: patternSchema }, rawOptions)])) : void 0;
|
|
1745
|
-
const objectNode = ast.factory.createSchema({
|
|
1746
|
-
type: "object",
|
|
1747
|
-
primitive: "object",
|
|
1748
|
-
properties,
|
|
1749
|
-
additionalProperties: additionalPropertiesNode,
|
|
1750
|
-
patternProperties,
|
|
1751
|
-
minProperties: schema.minProperties,
|
|
1752
|
-
maxProperties: schema.maxProperties,
|
|
1753
|
-
...buildSchemaNode(schema, name, nullable, defaultValue)
|
|
1754
|
-
});
|
|
1755
|
-
if (dialect.schema.isDiscriminator(schema) && schema.discriminator.mapping) {
|
|
1756
|
-
const discPropName = schema.discriminator.propertyName;
|
|
1757
|
-
const values = Object.keys(schema.discriminator.mapping);
|
|
1758
|
-
const enumName = name ? enumPropName(name, discPropName, options.enumSuffix) : void 0;
|
|
1759
|
-
return ast.applyMacros(objectNode, [macroDiscriminatorEnum({
|
|
1760
|
-
propertyName: discPropName,
|
|
1761
|
-
values,
|
|
1762
|
-
enumName
|
|
1763
|
-
})], { depth: "shallow" });
|
|
1764
|
-
}
|
|
1765
|
-
return objectNode;
|
|
1766
|
-
}
|
|
1767
|
-
/**
|
|
1768
|
-
* Converts an OAS 3.1 `prefixItems` tuple into a `TupleSchemaNode`.
|
|
1769
|
-
*/
|
|
1770
|
-
function convertTuple({ schema, name, nullable, defaultValue, rawOptions }) {
|
|
1771
|
-
const tupleItems = (schema.prefixItems ?? []).map((item) => parseSchema({ schema: item }, rawOptions));
|
|
1772
|
-
const rest = schema.items === false ? void 0 : !schema.items || schema.items === true ? ast.factory.createSchema({ type: "any" }) : parseSchema({ schema: schema.items }, rawOptions);
|
|
1773
|
-
return ast.factory.createSchema({
|
|
1774
|
-
type: "tuple",
|
|
1775
|
-
primitive: "array",
|
|
1776
|
-
items: tupleItems,
|
|
1777
|
-
rest,
|
|
1778
|
-
min: schema.minItems,
|
|
1779
|
-
max: schema.maxItems,
|
|
1780
|
-
...buildSchemaNode(schema, name, nullable, defaultValue)
|
|
1520
|
+
primitive: enumPrimitiveType,
|
|
1521
|
+
namedEnumValues: uniqueValues.map((value, index) => ({
|
|
1522
|
+
name: String(rawEnumNames?.[index] ?? value),
|
|
1523
|
+
value,
|
|
1524
|
+
primitive: enumPrimitiveType,
|
|
1525
|
+
description: rawEnumDescriptions?.[index]
|
|
1526
|
+
})).filter((entry) => {
|
|
1527
|
+
if (seenNames.has(entry.name)) return false;
|
|
1528
|
+
seenNames.add(entry.name);
|
|
1529
|
+
return true;
|
|
1530
|
+
})
|
|
1781
1531
|
});
|
|
1782
1532
|
}
|
|
1783
|
-
|
|
1784
|
-
|
|
1785
|
-
|
|
1786
|
-
|
|
1787
|
-
|
|
1788
|
-
|
|
1789
|
-
|
|
1790
|
-
|
|
1791
|
-
|
|
1792
|
-
|
|
1793
|
-
|
|
1794
|
-
|
|
1795
|
-
|
|
1796
|
-
|
|
1797
|
-
|
|
1798
|
-
|
|
1799
|
-
|
|
1800
|
-
|
|
1533
|
+
return ast.factory.createSchema({
|
|
1534
|
+
...enumBase,
|
|
1535
|
+
enumValues: [...new Set(filteredValues)]
|
|
1536
|
+
});
|
|
1537
|
+
}
|
|
1538
|
+
/**
|
|
1539
|
+
* Converts an object-like schema into an `ObjectSchemaNode`.
|
|
1540
|
+
*/
|
|
1541
|
+
function convertObject({ schema, name, nullable, defaultValue, rawOptions, options, parse }) {
|
|
1542
|
+
const properties = schema.properties ? Object.entries(schema.properties).map(([propName, propSchema]) => {
|
|
1543
|
+
const required = Array.isArray(schema.required) ? schema.required.includes(propName) : !!schema.required;
|
|
1544
|
+
const resolvedPropSchema = propSchema;
|
|
1545
|
+
const propNullable = isNullable(resolvedPropSchema);
|
|
1546
|
+
const schemaNode = nameEnums(parse({
|
|
1547
|
+
schema: resolvedPropSchema,
|
|
1548
|
+
name: childName(name, propName)
|
|
1549
|
+
}, rawOptions), {
|
|
1550
|
+
parentName: name,
|
|
1551
|
+
propName,
|
|
1552
|
+
enumSuffix: options.enumSuffix
|
|
1801
1553
|
});
|
|
1802
|
-
|
|
1803
|
-
|
|
1804
|
-
|
|
1805
|
-
|
|
1806
|
-
|
|
1807
|
-
|
|
1808
|
-
|
|
1809
|
-
primitive: "string",
|
|
1810
|
-
min: schema.minLength,
|
|
1811
|
-
max: schema.maxLength,
|
|
1812
|
-
pattern: schema.pattern,
|
|
1813
|
-
...buildSchemaNode(schema, name, nullable, defaultValue)
|
|
1554
|
+
return ast.factory.createProperty({
|
|
1555
|
+
name: propName,
|
|
1556
|
+
schema: {
|
|
1557
|
+
...schemaNode,
|
|
1558
|
+
nullable: schemaNode.type === "null" ? void 0 : propNullable || void 0
|
|
1559
|
+
},
|
|
1560
|
+
required
|
|
1814
1561
|
});
|
|
1562
|
+
}) : [];
|
|
1563
|
+
const additionalProperties = schema.additionalProperties;
|
|
1564
|
+
const additionalPropertiesNode = (() => {
|
|
1565
|
+
if (additionalProperties === true) return true;
|
|
1566
|
+
if (additionalProperties === false) return false;
|
|
1567
|
+
if (additionalProperties && Object.keys(additionalProperties).length > 0) return parse({ schema: additionalProperties }, rawOptions);
|
|
1568
|
+
if (additionalProperties) return ast.factory.createSchema({ type: options.unknownType });
|
|
1569
|
+
})();
|
|
1570
|
+
const rawPatternProperties = "patternProperties" in schema ? schema.patternProperties : void 0;
|
|
1571
|
+
const patternProperties = rawPatternProperties ? Object.fromEntries(Object.entries(rawPatternProperties).map(([pattern, patternSchema]) => [pattern, patternSchema === true || typeof patternSchema === "object" && Object.keys(patternSchema).length === 0 ? ast.factory.createSchema({ type: options.unknownType }) : parse({ schema: patternSchema }, rawOptions)])) : void 0;
|
|
1572
|
+
const objectNode = ast.factory.createSchema({
|
|
1573
|
+
type: "object",
|
|
1574
|
+
primitive: "object",
|
|
1575
|
+
properties,
|
|
1576
|
+
additionalProperties: additionalPropertiesNode,
|
|
1577
|
+
patternProperties,
|
|
1578
|
+
minProperties: schema.minProperties,
|
|
1579
|
+
maxProperties: schema.maxProperties,
|
|
1580
|
+
...buildSchemaNode(schema, name, nullable, defaultValue)
|
|
1581
|
+
});
|
|
1582
|
+
if (isDiscriminator(schema) && schema.discriminator.mapping) {
|
|
1583
|
+
const discPropName = schema.discriminator.propertyName;
|
|
1584
|
+
const values = Object.keys(schema.discriminator.mapping);
|
|
1585
|
+
const enumName = name ? enumPropName(name, discPropName, options.enumSuffix) : void 0;
|
|
1586
|
+
return ast.applyMacros(objectNode, [macroDiscriminatorEnum({
|
|
1587
|
+
propertyName: discPropName,
|
|
1588
|
+
values,
|
|
1589
|
+
enumName
|
|
1590
|
+
})], { depth: "shallow" });
|
|
1815
1591
|
}
|
|
1816
|
-
|
|
1817
|
-
|
|
1818
|
-
|
|
1819
|
-
|
|
1820
|
-
|
|
1821
|
-
|
|
1822
|
-
|
|
1823
|
-
|
|
1824
|
-
|
|
1825
|
-
|
|
1826
|
-
|
|
1827
|
-
|
|
1828
|
-
|
|
1829
|
-
|
|
1592
|
+
return objectNode;
|
|
1593
|
+
}
|
|
1594
|
+
/**
|
|
1595
|
+
* Converts an OAS 3.1 `prefixItems` tuple into a `TupleSchemaNode`.
|
|
1596
|
+
*/
|
|
1597
|
+
function convertTuple({ schema, name, nullable, defaultValue, rawOptions, parse }) {
|
|
1598
|
+
const tupleItems = (schema.prefixItems ?? []).map((item) => parse({ schema: item }, rawOptions));
|
|
1599
|
+
const rest = schema.items === false ? void 0 : !schema.items || schema.items === true ? ast.factory.createSchema({ type: "any" }) : parse({ schema: schema.items }, rawOptions);
|
|
1600
|
+
return ast.factory.createSchema({
|
|
1601
|
+
type: "tuple",
|
|
1602
|
+
primitive: "array",
|
|
1603
|
+
items: tupleItems,
|
|
1604
|
+
rest,
|
|
1605
|
+
min: schema.minItems,
|
|
1606
|
+
max: schema.maxItems,
|
|
1607
|
+
...buildSchemaNode(schema, name, nullable, defaultValue)
|
|
1608
|
+
});
|
|
1609
|
+
}
|
|
1610
|
+
/**
|
|
1611
|
+
* Converts a `type: 'array'` schema into an `ArraySchemaNode`.
|
|
1612
|
+
*/
|
|
1613
|
+
function convertArray({ schema, name, nullable, defaultValue, rawOptions, options, parse }) {
|
|
1614
|
+
const rawItems = schema.items;
|
|
1615
|
+
const itemName = rawItems?.enum?.length && name ? enumPropName(null, name, options.enumSuffix) : name;
|
|
1616
|
+
const items = rawItems ? [parse({
|
|
1617
|
+
schema: rawItems,
|
|
1618
|
+
name: itemName
|
|
1619
|
+
}, rawOptions)] : [];
|
|
1620
|
+
return ast.factory.createSchema({
|
|
1621
|
+
type: "array",
|
|
1622
|
+
primitive: "array",
|
|
1623
|
+
items,
|
|
1624
|
+
min: schema.minItems,
|
|
1625
|
+
max: schema.maxItems,
|
|
1626
|
+
unique: schema.uniqueItems ?? void 0,
|
|
1627
|
+
...buildSchemaNode(schema, name, nullable, defaultValue)
|
|
1628
|
+
});
|
|
1629
|
+
}
|
|
1630
|
+
/**
|
|
1631
|
+
* Converts a `type: 'string'` schema into a `StringSchemaNode`.
|
|
1632
|
+
*/
|
|
1633
|
+
function convertString({ schema, name, nullable, defaultValue }) {
|
|
1634
|
+
return ast.factory.createSchema({
|
|
1635
|
+
type: "string",
|
|
1636
|
+
primitive: "string",
|
|
1637
|
+
min: schema.minLength,
|
|
1638
|
+
max: schema.maxLength,
|
|
1639
|
+
pattern: schema.pattern,
|
|
1640
|
+
...buildSchemaNode(schema, name, nullable, defaultValue)
|
|
1641
|
+
});
|
|
1642
|
+
}
|
|
1643
|
+
/**
|
|
1644
|
+
* Converts a `type: 'number'` or `type: 'integer'` schema.
|
|
1645
|
+
*/
|
|
1646
|
+
function convertNumeric({ schema, name, nullable, defaultValue }, type) {
|
|
1647
|
+
return ast.factory.createSchema({
|
|
1648
|
+
type,
|
|
1649
|
+
primitive: type,
|
|
1650
|
+
min: schema.minimum,
|
|
1651
|
+
max: schema.maximum,
|
|
1652
|
+
exclusiveMinimum: typeof schema.exclusiveMinimum === "number" ? schema.exclusiveMinimum : void 0,
|
|
1653
|
+
exclusiveMaximum: typeof schema.exclusiveMaximum === "number" ? schema.exclusiveMaximum : void 0,
|
|
1654
|
+
multipleOf: schema.multipleOf,
|
|
1655
|
+
...buildSchemaNode(schema, name, nullable, defaultValue)
|
|
1656
|
+
});
|
|
1657
|
+
}
|
|
1658
|
+
/**
|
|
1659
|
+
* Converts a `type: 'boolean'` schema.
|
|
1660
|
+
*/
|
|
1661
|
+
function convertBoolean({ schema, name, nullable, defaultValue }) {
|
|
1662
|
+
return ast.factory.createSchema({
|
|
1663
|
+
type: "boolean",
|
|
1664
|
+
primitive: "boolean",
|
|
1665
|
+
...buildSchemaNode(schema, name, nullable, defaultValue)
|
|
1666
|
+
});
|
|
1667
|
+
}
|
|
1668
|
+
/**
|
|
1669
|
+
* Converts a binary string schema (`type: 'string'`, `contentMediaType: 'application/octet-stream'`)
|
|
1670
|
+
* into a `blob` node.
|
|
1671
|
+
*/
|
|
1672
|
+
function convertBinary({ schema, name, nullable, defaultValue }) {
|
|
1673
|
+
return ast.factory.createSchema({
|
|
1674
|
+
type: "blob",
|
|
1675
|
+
primitive: "string",
|
|
1676
|
+
...buildSchemaNode(schema, name, nullable, defaultValue)
|
|
1677
|
+
});
|
|
1678
|
+
}
|
|
1679
|
+
/**
|
|
1680
|
+
* Converts an OAS 3.1 multi-type array (e.g. `type: ['string', 'number']`) into a `UnionSchemaNode`.
|
|
1681
|
+
*
|
|
1682
|
+
* Returns `null` when only one non-`null` type remains (e.g. `['string', 'null']`), so `parse`
|
|
1683
|
+
* falls through and handles it as that single type with nullability already folded in.
|
|
1684
|
+
*/
|
|
1685
|
+
function convertMultiType({ schema, name, nullable, defaultValue, rawOptions, parse }) {
|
|
1686
|
+
const types = schema.type;
|
|
1687
|
+
const nonNullTypes = types.filter((t) => t !== "null");
|
|
1688
|
+
if (nonNullTypes.length <= 1) return null;
|
|
1689
|
+
const arrayNullable = types.includes("null") || nullable || void 0;
|
|
1690
|
+
return ast.factory.createSchema({
|
|
1691
|
+
type: "union",
|
|
1692
|
+
members: nonNullTypes.map((t) => {
|
|
1693
|
+
return parse({
|
|
1694
|
+
schema: {
|
|
1695
|
+
...schema,
|
|
1696
|
+
type: t
|
|
1697
|
+
},
|
|
1698
|
+
name
|
|
1699
|
+
}, rawOptions);
|
|
1700
|
+
}),
|
|
1701
|
+
...buildSchemaNode(schema, name, arrayNullable, defaultValue)
|
|
1702
|
+
});
|
|
1703
|
+
}
|
|
1704
|
+
/**
|
|
1705
|
+
* Ordered schema rule table. Order is significant: composition keywords (`$ref`, `allOf`,
|
|
1706
|
+
* `oneOf`/`anyOf`) take precedence over `const`/`format`, which take precedence over the plain
|
|
1707
|
+
* `type`. The first matching rule that produces a node wins. See {@link SchemaRule} for the
|
|
1708
|
+
* match/convert/fall-through contract.
|
|
1709
|
+
*/
|
|
1710
|
+
const schemaRules = [
|
|
1711
|
+
{
|
|
1712
|
+
match: ({ schema }) => isReference(schema),
|
|
1713
|
+
convert: convertRef
|
|
1714
|
+
},
|
|
1715
|
+
{
|
|
1716
|
+
match: ({ schema }) => !!schema.allOf?.length,
|
|
1717
|
+
convert: convertAllOf
|
|
1718
|
+
},
|
|
1719
|
+
{
|
|
1720
|
+
match: ({ schema }) => !!(schema.oneOf?.length || schema.anyOf?.length),
|
|
1721
|
+
convert: convertUnion
|
|
1722
|
+
},
|
|
1723
|
+
{
|
|
1724
|
+
match: ({ schema }) => "const" in schema && schema.const !== void 0,
|
|
1725
|
+
convert: convertConst
|
|
1726
|
+
},
|
|
1727
|
+
{
|
|
1728
|
+
match: ({ schema }) => !!schema.format,
|
|
1729
|
+
convert: convertFormat
|
|
1730
|
+
},
|
|
1731
|
+
{
|
|
1732
|
+
match: ({ schema }) => isBinary(schema),
|
|
1733
|
+
convert: convertBinary
|
|
1734
|
+
},
|
|
1735
|
+
{
|
|
1736
|
+
match: ({ schema }) => Array.isArray(schema.type) && schema.type.length > 1,
|
|
1737
|
+
convert: convertMultiType
|
|
1738
|
+
},
|
|
1739
|
+
{
|
|
1740
|
+
match: ({ schema, type }) => !type && (schema.minLength !== void 0 || schema.maxLength !== void 0 || schema.pattern !== void 0),
|
|
1741
|
+
convert: convertString
|
|
1742
|
+
},
|
|
1743
|
+
{
|
|
1744
|
+
match: ({ schema, type }) => !type && (schema.minimum !== void 0 || schema.maximum !== void 0),
|
|
1745
|
+
convert: (ctx) => convertNumeric(ctx, "number")
|
|
1746
|
+
},
|
|
1747
|
+
{
|
|
1748
|
+
match: ({ schema }) => !!schema.enum?.length,
|
|
1749
|
+
convert: convertEnum
|
|
1750
|
+
},
|
|
1751
|
+
{
|
|
1752
|
+
match: ({ schema, type }) => type === "object" || !!schema.properties || !!schema.additionalProperties || "patternProperties" in schema,
|
|
1753
|
+
convert: convertObject
|
|
1754
|
+
},
|
|
1755
|
+
{
|
|
1756
|
+
match: ({ schema }) => "prefixItems" in schema,
|
|
1757
|
+
convert: convertTuple
|
|
1758
|
+
},
|
|
1759
|
+
{
|
|
1760
|
+
match: ({ schema, type }) => type === "array" || "items" in schema,
|
|
1761
|
+
convert: convertArray
|
|
1762
|
+
},
|
|
1763
|
+
{
|
|
1764
|
+
match: ({ type }) => type === "string",
|
|
1765
|
+
convert: convertString
|
|
1766
|
+
},
|
|
1767
|
+
{
|
|
1768
|
+
match: ({ type }) => type === "number",
|
|
1769
|
+
convert: (ctx) => convertNumeric(ctx, "number")
|
|
1770
|
+
},
|
|
1771
|
+
{
|
|
1772
|
+
match: ({ type }) => type === "integer",
|
|
1773
|
+
convert: (ctx) => convertNumeric(ctx, "integer")
|
|
1774
|
+
},
|
|
1775
|
+
{
|
|
1776
|
+
match: ({ type }) => type === "boolean",
|
|
1777
|
+
convert: convertBoolean
|
|
1778
|
+
},
|
|
1779
|
+
{
|
|
1780
|
+
match: ({ type }) => type === "null",
|
|
1781
|
+
convert: ({ schema, name, nullable }) => createNullNode(schema, name, nullable)
|
|
1830
1782
|
}
|
|
1783
|
+
];
|
|
1784
|
+
//#endregion
|
|
1785
|
+
//#region src/parser.ts
|
|
1786
|
+
/**
|
|
1787
|
+
* Creates the schema and operation converters bound to one OpenAPI document.
|
|
1788
|
+
*
|
|
1789
|
+
* Owns the per-instance `$ref` state (cycle detection, resolved-node cache, existence cache) and
|
|
1790
|
+
* the `parseSchema` recursion seam, then dispatches each schema through the ordered `schemaRules`
|
|
1791
|
+
* table from `converters.ts`. Every converter is a standalone function that recurses through the
|
|
1792
|
+
* `parse` function passed to it, so this file only wires state to the converters.
|
|
1793
|
+
*
|
|
1794
|
+
* @internal
|
|
1795
|
+
*/
|
|
1796
|
+
function createSchemaParser(ctx) {
|
|
1797
|
+
const document = ctx.document;
|
|
1831
1798
|
/**
|
|
1832
|
-
*
|
|
1799
|
+
* Tracks `$ref` paths that are currently being resolved to prevent infinite
|
|
1800
|
+
* recursion when schemas contain circular references (e.g. `Pet → parent → Pet`).
|
|
1833
1801
|
*/
|
|
1834
|
-
|
|
1835
|
-
return ast.factory.createSchema({
|
|
1836
|
-
type: "boolean",
|
|
1837
|
-
primitive: "boolean",
|
|
1838
|
-
...buildSchemaNode(schema, name, nullable, defaultValue)
|
|
1839
|
-
});
|
|
1840
|
-
}
|
|
1802
|
+
const resolvingRefs = /* @__PURE__ */ new Set();
|
|
1841
1803
|
/**
|
|
1842
|
-
*
|
|
1843
|
-
*
|
|
1804
|
+
* Cache of `$ref` schemas already resolved in this parser instance, keyed by ref path.
|
|
1805
|
+
*
|
|
1806
|
+
* Without it, a shared schema (e.g. `customer`) is re-expanded for every `$ref` that points at
|
|
1807
|
+
* it. In cross-referenced specs like Stripe (~1400 schemas) that becomes exponential blowup,
|
|
1808
|
+
* since one schema can be referenced from dozens of parents, each re-walking its whole subtree.
|
|
1809
|
+
* Memoizing by ref path drops the work from O(2^depth) to O(N) unique schema names.
|
|
1844
1810
|
*/
|
|
1845
|
-
|
|
1846
|
-
return ast.factory.createSchema({
|
|
1847
|
-
type: "blob",
|
|
1848
|
-
primitive: "string",
|
|
1849
|
-
...buildSchemaNode(schema, name, nullable, defaultValue)
|
|
1850
|
-
});
|
|
1851
|
-
}
|
|
1811
|
+
const resolvedRefCache = /* @__PURE__ */ new Map();
|
|
1852
1812
|
/**
|
|
1853
|
-
*
|
|
1854
|
-
*
|
|
1855
|
-
*
|
|
1856
|
-
* falls through and handles it as that single type with nullability already folded in.
|
|
1813
|
+
* Memoized record of whether a `$ref` path resolves to a node the document actually defines.
|
|
1814
|
+
* A circular ref still resolves to an existing target, so this stays `true` for cycles and only
|
|
1815
|
+
* goes `false` for a `$ref` that points at a component the spec never declares.
|
|
1857
1816
|
*/
|
|
1858
|
-
|
|
1859
|
-
|
|
1860
|
-
|
|
1861
|
-
|
|
1862
|
-
|
|
1863
|
-
|
|
1864
|
-
|
|
1865
|
-
|
|
1866
|
-
|
|
1867
|
-
|
|
1868
|
-
|
|
1869
|
-
|
|
1870
|
-
name
|
|
1871
|
-
}, rawOptions)),
|
|
1872
|
-
...buildSchemaNode(schema, name, arrayNullable, defaultValue)
|
|
1873
|
-
});
|
|
1817
|
+
const refExistence = /* @__PURE__ */ new Map();
|
|
1818
|
+
function refExists(refPath) {
|
|
1819
|
+
if (!refExistence.has(refPath)) {
|
|
1820
|
+
let exists = false;
|
|
1821
|
+
try {
|
|
1822
|
+
exists = !!resolveRef(document, refPath);
|
|
1823
|
+
} catch {
|
|
1824
|
+
exists = false;
|
|
1825
|
+
}
|
|
1826
|
+
refExistence.set(refPath, exists);
|
|
1827
|
+
}
|
|
1828
|
+
return refExistence.get(refPath) ?? false;
|
|
1874
1829
|
}
|
|
1875
1830
|
/**
|
|
1876
|
-
*
|
|
1877
|
-
* `
|
|
1878
|
-
*
|
|
1879
|
-
* match/convert/fall-through contract.
|
|
1831
|
+
* Resolves a `$ref` to its parsed node, guarding against cycles and memoizing per instance.
|
|
1832
|
+
* Returns `null` when the ref is currently being resolved (a cycle) or cannot be resolved
|
|
1833
|
+
* (e.g. a minimal document in a unit test).
|
|
1880
1834
|
*/
|
|
1881
|
-
|
|
1882
|
-
|
|
1883
|
-
|
|
1884
|
-
|
|
1885
|
-
|
|
1886
|
-
|
|
1887
|
-
|
|
1888
|
-
|
|
1889
|
-
|
|
1890
|
-
|
|
1891
|
-
|
|
1892
|
-
|
|
1893
|
-
|
|
1894
|
-
{
|
|
1895
|
-
match: ({ schema }) => "const" in schema && schema.const !== void 0,
|
|
1896
|
-
convert: convertConst
|
|
1897
|
-
},
|
|
1898
|
-
{
|
|
1899
|
-
match: ({ schema }) => !!schema.format,
|
|
1900
|
-
convert: convertFormat
|
|
1901
|
-
},
|
|
1902
|
-
{
|
|
1903
|
-
match: ({ schema }) => dialect.schema.isBinary(schema),
|
|
1904
|
-
convert: convertBlob
|
|
1905
|
-
},
|
|
1906
|
-
{
|
|
1907
|
-
match: ({ schema }) => Array.isArray(schema.type) && schema.type.length > 1,
|
|
1908
|
-
convert: convertMultiType
|
|
1909
|
-
},
|
|
1910
|
-
{
|
|
1911
|
-
match: ({ schema, type }) => !type && (schema.minLength !== void 0 || schema.maxLength !== void 0 || schema.pattern !== void 0),
|
|
1912
|
-
convert: convertString
|
|
1913
|
-
},
|
|
1914
|
-
{
|
|
1915
|
-
match: ({ schema, type }) => !type && (schema.minimum !== void 0 || schema.maximum !== void 0),
|
|
1916
|
-
convert: (ctx) => convertNumeric(ctx, "number")
|
|
1917
|
-
},
|
|
1918
|
-
{
|
|
1919
|
-
match: ({ schema }) => !!schema.enum?.length,
|
|
1920
|
-
convert: convertEnum
|
|
1921
|
-
},
|
|
1922
|
-
{
|
|
1923
|
-
match: ({ schema, type }) => type === "object" || !!schema.properties || !!schema.additionalProperties || "patternProperties" in schema,
|
|
1924
|
-
convert: convertObject
|
|
1925
|
-
},
|
|
1926
|
-
{
|
|
1927
|
-
match: ({ schema }) => "prefixItems" in schema,
|
|
1928
|
-
convert: convertTuple
|
|
1929
|
-
},
|
|
1930
|
-
{
|
|
1931
|
-
match: ({ schema, type }) => type === "array" || "items" in schema,
|
|
1932
|
-
convert: convertArray
|
|
1933
|
-
},
|
|
1934
|
-
{
|
|
1935
|
-
match: ({ type }) => type === "string",
|
|
1936
|
-
convert: convertString
|
|
1937
|
-
},
|
|
1938
|
-
{
|
|
1939
|
-
match: ({ type }) => type === "number",
|
|
1940
|
-
convert: (ctx) => convertNumeric(ctx, "number")
|
|
1941
|
-
},
|
|
1942
|
-
{
|
|
1943
|
-
match: ({ type }) => type === "integer",
|
|
1944
|
-
convert: (ctx) => convertNumeric(ctx, "integer")
|
|
1945
|
-
},
|
|
1946
|
-
{
|
|
1947
|
-
match: ({ type }) => type === "boolean",
|
|
1948
|
-
convert: convertBoolean
|
|
1949
|
-
},
|
|
1950
|
-
{
|
|
1951
|
-
match: ({ type }) => type === "null",
|
|
1952
|
-
convert: ({ schema, name, nullable }) => createNullSchema(schema, name, nullable)
|
|
1835
|
+
function resolveRefNode(refPath, rawOptions) {
|
|
1836
|
+
if (resolvingRefs.has(refPath)) return null;
|
|
1837
|
+
if (!resolvedRefCache.has(refPath)) {
|
|
1838
|
+
let resolved = null;
|
|
1839
|
+
try {
|
|
1840
|
+
const referenced = resolveRef(document, refPath);
|
|
1841
|
+
if (referenced) {
|
|
1842
|
+
resolvingRefs.add(refPath);
|
|
1843
|
+
resolved = parseSchema({ schema: referenced }, rawOptions);
|
|
1844
|
+
resolvingRefs.delete(refPath);
|
|
1845
|
+
}
|
|
1846
|
+
} catch {}
|
|
1847
|
+
resolvedRefCache.set(refPath, resolved);
|
|
1953
1848
|
}
|
|
1954
|
-
|
|
1849
|
+
return resolvedRefCache.get(refPath) ?? null;
|
|
1850
|
+
}
|
|
1955
1851
|
/**
|
|
1956
1852
|
* Converts an OAS `SchemaObject` into a `SchemaNode`.
|
|
1957
1853
|
*
|
|
@@ -1969,19 +1865,23 @@ function createSchemaParser(ctx, dialect = oasDialect) {
|
|
|
1969
1865
|
schema: flattenedSchema,
|
|
1970
1866
|
name
|
|
1971
1867
|
}, rawOptions);
|
|
1972
|
-
const nullable =
|
|
1973
|
-
const
|
|
1868
|
+
const nullable = isNullable(schema) || void 0;
|
|
1869
|
+
const context = {
|
|
1974
1870
|
schema,
|
|
1975
1871
|
name,
|
|
1976
1872
|
nullable,
|
|
1977
1873
|
defaultValue: schema.default === null && nullable ? void 0 : schema.default,
|
|
1978
1874
|
type: Array.isArray(schema.type) ? schema.type[0] : schema.type,
|
|
1979
1875
|
rawOptions,
|
|
1980
|
-
options
|
|
1876
|
+
options,
|
|
1877
|
+
parse: parseSchema,
|
|
1878
|
+
document,
|
|
1879
|
+
resolveRefNode,
|
|
1880
|
+
refExists
|
|
1981
1881
|
};
|
|
1982
1882
|
for (const rule of schemaRules) {
|
|
1983
|
-
if (!rule.match(
|
|
1984
|
-
const node = rule.convert(
|
|
1883
|
+
if (!rule.match(context)) continue;
|
|
1884
|
+
const node = rule.convert(context);
|
|
1985
1885
|
if (node) return node;
|
|
1986
1886
|
}
|
|
1987
1887
|
const emptyType = options.emptySchemaType;
|
|
@@ -2039,7 +1939,7 @@ function createSchemaParser(ctx, dialect = oasDialect) {
|
|
|
2039
1939
|
const keys = [];
|
|
2040
1940
|
for (const key in schema.properties) {
|
|
2041
1941
|
const prop = schema.properties[key];
|
|
2042
|
-
if (prop && !
|
|
1942
|
+
if (prop && !isReference(prop) && prop[flag]) keys.push(key);
|
|
2043
1943
|
}
|
|
2044
1944
|
return keys.length ? keys : null;
|
|
2045
1945
|
}
|
|
@@ -2106,7 +2006,7 @@ function createSchemaParser(ctx, dialect = oasDialect) {
|
|
|
2106
2006
|
});
|
|
2107
2007
|
});
|
|
2108
2008
|
const pathItem = document.paths?.[operation.path];
|
|
2109
|
-
const pathItemDoc = pathItem && !
|
|
2009
|
+
const pathItemDoc = pathItem && !isReference(pathItem) ? pathItem : void 0;
|
|
2110
2010
|
const pickDoc = (key) => {
|
|
2111
2011
|
const own = operation.schema[key];
|
|
2112
2012
|
if (typeof own === "string") return own;
|
|
@@ -2298,13 +2198,10 @@ const adapterOas = createAdapter((options) => {
|
|
|
2298
2198
|
function ensureSchemas(document) {
|
|
2299
2199
|
const cached = schemasCache.get(document);
|
|
2300
2200
|
if (cached) return cached;
|
|
2301
|
-
const
|
|
2302
|
-
|
|
2303
|
-
|
|
2304
|
-
|
|
2305
|
-
});
|
|
2306
|
-
schemasCache.set(document, promise);
|
|
2307
|
-
return promise;
|
|
2201
|
+
const result = getSchemas(document, { contentType });
|
|
2202
|
+
nameMapping = result.nameMapping;
|
|
2203
|
+
schemasCache.set(document, result.schemas);
|
|
2204
|
+
return result.schemas;
|
|
2308
2205
|
}
|
|
2309
2206
|
function ensureSchemaParser(document) {
|
|
2310
2207
|
const cached = schemaParserCache.get(document);
|
|
@@ -2420,7 +2317,7 @@ const adapterOas = createAdapter((options) => {
|
|
|
2420
2317
|
const document = await ensureDocument(source);
|
|
2421
2318
|
return parseInput({
|
|
2422
2319
|
document,
|
|
2423
|
-
schemas:
|
|
2320
|
+
schemas: ensureSchemas(document),
|
|
2424
2321
|
parser: ensureSchemaParser(document)
|
|
2425
2322
|
});
|
|
2426
2323
|
}
|