@kubb/adapter-oas 5.0.0-beta.82 → 5.0.0-beta.85
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 +248 -355
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js +248 -355
- package/dist/index.js.map +1 -1
- package/package.json +4 -4
package/dist/index.cjs
CHANGED
|
@@ -165,6 +165,119 @@ const enumExtensionKeys = ["x-enumNames", "x-enum-varnames"];
|
|
|
165
165
|
*/
|
|
166
166
|
const enumDescriptionKeys = ["x-enumDescriptions", "x-enum-descriptions"];
|
|
167
167
|
//#endregion
|
|
168
|
+
//#region src/discriminator.ts
|
|
169
|
+
/**
|
|
170
|
+
* Maps each child schema name to its discriminator patch data by scanning the given
|
|
171
|
+
* top-level AST schema nodes for union schemas that carry a `discriminatorPropertyName`.
|
|
172
|
+
*
|
|
173
|
+
* Called on a small pre-parsed subset of schemas (only the discriminator parents)
|
|
174
|
+
* rather than on all schemas at once.
|
|
175
|
+
*/
|
|
176
|
+
function buildDiscriminatorChildMap(schemas) {
|
|
177
|
+
const childMap = /* @__PURE__ */ new Map();
|
|
178
|
+
for (const schema of schemas) {
|
|
179
|
+
let unionNode = _kubb_ast.ast.narrowSchema(schema, "union");
|
|
180
|
+
if (!unionNode) {
|
|
181
|
+
const intersectionMembers = _kubb_ast.ast.narrowSchema(schema, "intersection")?.members;
|
|
182
|
+
if (intersectionMembers) for (const m of intersectionMembers) {
|
|
183
|
+
const u = _kubb_ast.ast.narrowSchema(m, "union");
|
|
184
|
+
if (u) {
|
|
185
|
+
unionNode = u;
|
|
186
|
+
break;
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
if (!unionNode?.discriminatorPropertyName || !unionNode.members) continue;
|
|
191
|
+
const { discriminatorPropertyName, members } = unionNode;
|
|
192
|
+
for (const member of members) {
|
|
193
|
+
const intersectionNode = _kubb_ast.ast.narrowSchema(member, "intersection");
|
|
194
|
+
if (!intersectionNode?.members) continue;
|
|
195
|
+
let refNode = null;
|
|
196
|
+
let objNode = null;
|
|
197
|
+
for (const m of intersectionNode.members) {
|
|
198
|
+
refNode ??= _kubb_ast.ast.narrowSchema(m, "ref");
|
|
199
|
+
objNode ??= _kubb_ast.ast.narrowSchema(m, "object");
|
|
200
|
+
}
|
|
201
|
+
if (!refNode?.name || !objNode) continue;
|
|
202
|
+
const prop = objNode.properties.find((p) => p.name === discriminatorPropertyName);
|
|
203
|
+
const enumNode = prop ? _kubb_ast.ast.narrowSchema(prop.schema, "enum") : null;
|
|
204
|
+
if (!enumNode?.enumValues?.length) continue;
|
|
205
|
+
const enumValues = enumNode.enumValues.filter((v) => v !== null);
|
|
206
|
+
if (!enumValues.length) continue;
|
|
207
|
+
const existing = childMap.get(refNode.name);
|
|
208
|
+
if (!existing) {
|
|
209
|
+
childMap.set(refNode.name, {
|
|
210
|
+
propertyName: discriminatorPropertyName,
|
|
211
|
+
enumValues: [...enumValues]
|
|
212
|
+
});
|
|
213
|
+
continue;
|
|
214
|
+
}
|
|
215
|
+
existing.enumValues.push(...enumValues);
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
return childMap;
|
|
219
|
+
}
|
|
220
|
+
/**
|
|
221
|
+
* Patches a single top-level `SchemaNode` with its discriminator entry (adds or replaces
|
|
222
|
+
* the discriminant property).
|
|
223
|
+
*/
|
|
224
|
+
function patchDiscriminatorNode(node, entry) {
|
|
225
|
+
const objectNode = _kubb_ast.ast.narrowSchema(node, "object");
|
|
226
|
+
if (!objectNode) return node;
|
|
227
|
+
const { propertyName, enumValues } = entry;
|
|
228
|
+
const enumSchema = _kubb_ast.ast.factory.createSchema({
|
|
229
|
+
type: "enum",
|
|
230
|
+
enumValues
|
|
231
|
+
});
|
|
232
|
+
const newProp = _kubb_ast.ast.factory.createProperty({
|
|
233
|
+
name: propertyName,
|
|
234
|
+
required: true,
|
|
235
|
+
schema: enumSchema
|
|
236
|
+
});
|
|
237
|
+
const existingIdx = objectNode.properties.findIndex((p) => p.name === propertyName);
|
|
238
|
+
const newProperties = existingIdx >= 0 ? objectNode.properties.map((p, i) => i === existingIdx ? newProp : p) : [...objectNode.properties, newProp];
|
|
239
|
+
return {
|
|
240
|
+
...objectNode,
|
|
241
|
+
properties: newProperties
|
|
242
|
+
};
|
|
243
|
+
}
|
|
244
|
+
/**
|
|
245
|
+
* Creates a single-property object schema used as a discriminator literal.
|
|
246
|
+
*
|
|
247
|
+
* @example
|
|
248
|
+
* ```ts
|
|
249
|
+
* createDiscriminantNode({ propertyName: 'type', value: 'dog' })
|
|
250
|
+
* // -> { type: 'object', properties: [{ name: 'type', required: true, schema: enum('dog') }] }
|
|
251
|
+
* ```
|
|
252
|
+
*/
|
|
253
|
+
function createDiscriminantNode({ propertyName, value }) {
|
|
254
|
+
return _kubb_ast.ast.factory.createSchema({
|
|
255
|
+
type: "object",
|
|
256
|
+
primitive: "object",
|
|
257
|
+
properties: [_kubb_ast.ast.factory.createProperty({
|
|
258
|
+
name: propertyName,
|
|
259
|
+
schema: _kubb_ast.ast.factory.createSchema({
|
|
260
|
+
type: "enum",
|
|
261
|
+
primitive: "string",
|
|
262
|
+
enumValues: [value]
|
|
263
|
+
}),
|
|
264
|
+
required: true
|
|
265
|
+
})]
|
|
266
|
+
});
|
|
267
|
+
}
|
|
268
|
+
/**
|
|
269
|
+
* Returns the discriminator key whose mapping value matches `ref`, or `null` when there is no match.
|
|
270
|
+
*
|
|
271
|
+
* @example
|
|
272
|
+
* ```ts
|
|
273
|
+
* findDiscriminator({ dog: '#/components/schemas/Dog' }, '#/components/schemas/Dog') // 'dog'
|
|
274
|
+
* ```
|
|
275
|
+
*/
|
|
276
|
+
function findDiscriminator(mapping, ref) {
|
|
277
|
+
if (!mapping || !ref) return null;
|
|
278
|
+
return Object.entries(mapping).find(([, value]) => value === ref)?.[0] ?? null;
|
|
279
|
+
}
|
|
280
|
+
//#endregion
|
|
168
281
|
//#region ../../internals/utils/src/casing.ts
|
|
169
282
|
/**
|
|
170
283
|
* Shared implementation for camelCase and PascalCase conversion.
|
|
@@ -453,6 +566,34 @@ function isDiscriminator(obj) {
|
|
|
453
566
|
return !!obj && !!record["discriminator"] && typeof record["discriminator"] !== "string";
|
|
454
567
|
}
|
|
455
568
|
//#endregion
|
|
569
|
+
//#region src/mime.ts
|
|
570
|
+
/**
|
|
571
|
+
* MIME type fragments that mark a media type as JSON-like.
|
|
572
|
+
*
|
|
573
|
+
* A content type is JSON when it contains any of these substrings. The `+json` entry catches
|
|
574
|
+
* structured-syntax suffixes such as `application/vnd.api+json`.
|
|
575
|
+
*/
|
|
576
|
+
const jsonMimeFragments = [
|
|
577
|
+
"application/json",
|
|
578
|
+
"application/x-json",
|
|
579
|
+
"text/json",
|
|
580
|
+
"text/x-json",
|
|
581
|
+
"+json"
|
|
582
|
+
];
|
|
583
|
+
/**
|
|
584
|
+
* Returns `true` when a media type string is JSON-like.
|
|
585
|
+
*
|
|
586
|
+
* @example
|
|
587
|
+
* ```ts
|
|
588
|
+
* isJsonMimeType('application/json') // true
|
|
589
|
+
* isJsonMimeType('application/vnd.api+json') // true
|
|
590
|
+
* isJsonMimeType('multipart/form-data') // false
|
|
591
|
+
* ```
|
|
592
|
+
*/
|
|
593
|
+
function isJsonMimeType(mimeType) {
|
|
594
|
+
return jsonMimeFragments.some((fragment) => mimeType.includes(fragment));
|
|
595
|
+
}
|
|
596
|
+
//#endregion
|
|
456
597
|
//#region src/refs.ts
|
|
457
598
|
const _refCache = /* @__PURE__ */ new WeakMap();
|
|
458
599
|
/**
|
|
@@ -519,179 +660,6 @@ function dereferenceWithRef(document, schema) {
|
|
|
519
660
|
return schema;
|
|
520
661
|
}
|
|
521
662
|
//#endregion
|
|
522
|
-
//#region src/dialect.ts
|
|
523
|
-
/**
|
|
524
|
-
* The OpenAPI / Swagger dialect, the default used by `@kubb/adapter-oas`.
|
|
525
|
-
*
|
|
526
|
-
* Implements the spec-agnostic {@link ast.SchemaDialect} contract: it isolates the
|
|
527
|
-
* decisions that differ between specs (nullability, `$ref`, discriminator, binary,
|
|
528
|
-
* ref resolution) so the converter pipeline and dispatch rules stay shared. A
|
|
529
|
-
* future adapter (e.g. AsyncAPI) ships its own dialect, `type: ['null', …]`
|
|
530
|
-
* nullability, no discriminator object, binary via `contentEncoding` and reuses
|
|
531
|
-
* the rest unchanged.
|
|
532
|
-
*
|
|
533
|
-
* Formats (`uuid`, `email`, dates, …) are intentionally NOT here: they are shared
|
|
534
|
-
* JSON Schema vocabulary, so the converters keep that common case.
|
|
535
|
-
*
|
|
536
|
-
* @example
|
|
537
|
-
* ```ts
|
|
538
|
-
* const parser = createSchemaParser(context) // uses oasDialect
|
|
539
|
-
* const parser = createSchemaParser(context, oasDialect) // explicit
|
|
540
|
-
* ```
|
|
541
|
-
*/
|
|
542
|
-
const oasDialect = _kubb_ast.ast.defineDialect({
|
|
543
|
-
name: "oas",
|
|
544
|
-
schema: {
|
|
545
|
-
isNullable,
|
|
546
|
-
isReference,
|
|
547
|
-
isDiscriminator,
|
|
548
|
-
isBinary: (schema) => schema.type === "string" && schema.contentMediaType === "application/octet-stream",
|
|
549
|
-
resolveRef
|
|
550
|
-
}
|
|
551
|
-
});
|
|
552
|
-
//#endregion
|
|
553
|
-
//#region src/discriminator.ts
|
|
554
|
-
/**
|
|
555
|
-
* Maps each child schema name to its discriminator patch data by scanning the given
|
|
556
|
-
* top-level AST schema nodes for union schemas that carry a `discriminatorPropertyName`.
|
|
557
|
-
*
|
|
558
|
-
* The streaming path calls this on a small pre-parsed subset of schemas (only the
|
|
559
|
-
* discriminator parents) rather than on all schemas at once.
|
|
560
|
-
*/
|
|
561
|
-
function buildDiscriminatorChildMap(schemas) {
|
|
562
|
-
const childMap = /* @__PURE__ */ new Map();
|
|
563
|
-
for (const schema of schemas) {
|
|
564
|
-
let unionNode = _kubb_ast.ast.narrowSchema(schema, "union");
|
|
565
|
-
if (!unionNode) {
|
|
566
|
-
const intersectionMembers = _kubb_ast.ast.narrowSchema(schema, "intersection")?.members;
|
|
567
|
-
if (intersectionMembers) for (const m of intersectionMembers) {
|
|
568
|
-
const u = _kubb_ast.ast.narrowSchema(m, "union");
|
|
569
|
-
if (u) {
|
|
570
|
-
unionNode = u;
|
|
571
|
-
break;
|
|
572
|
-
}
|
|
573
|
-
}
|
|
574
|
-
}
|
|
575
|
-
if (!unionNode?.discriminatorPropertyName || !unionNode.members) continue;
|
|
576
|
-
const { discriminatorPropertyName, members } = unionNode;
|
|
577
|
-
for (const member of members) {
|
|
578
|
-
const intersectionNode = _kubb_ast.ast.narrowSchema(member, "intersection");
|
|
579
|
-
if (!intersectionNode?.members) continue;
|
|
580
|
-
let refNode = null;
|
|
581
|
-
let objNode = null;
|
|
582
|
-
for (const m of intersectionNode.members) {
|
|
583
|
-
refNode ??= _kubb_ast.ast.narrowSchema(m, "ref");
|
|
584
|
-
objNode ??= _kubb_ast.ast.narrowSchema(m, "object");
|
|
585
|
-
}
|
|
586
|
-
if (!refNode?.name || !objNode) continue;
|
|
587
|
-
const prop = objNode.properties.find((p) => p.name === discriminatorPropertyName);
|
|
588
|
-
const enumNode = prop ? _kubb_ast.ast.narrowSchema(prop.schema, "enum") : null;
|
|
589
|
-
if (!enumNode?.enumValues?.length) continue;
|
|
590
|
-
const enumValues = enumNode.enumValues.filter((v) => v !== null);
|
|
591
|
-
if (!enumValues.length) continue;
|
|
592
|
-
const existing = childMap.get(refNode.name);
|
|
593
|
-
if (!existing) {
|
|
594
|
-
childMap.set(refNode.name, {
|
|
595
|
-
propertyName: discriminatorPropertyName,
|
|
596
|
-
enumValues: [...enumValues]
|
|
597
|
-
});
|
|
598
|
-
continue;
|
|
599
|
-
}
|
|
600
|
-
existing.enumValues.push(...enumValues);
|
|
601
|
-
}
|
|
602
|
-
}
|
|
603
|
-
return childMap;
|
|
604
|
-
}
|
|
605
|
-
/**
|
|
606
|
-
* Patches a single top-level `SchemaNode` with its discriminator entry (adds or replaces
|
|
607
|
-
* the discriminant property). Used by the streaming path to apply patches inline per yield
|
|
608
|
-
* without buffering all schemas.
|
|
609
|
-
*/
|
|
610
|
-
function patchDiscriminatorNode(node, entry) {
|
|
611
|
-
const objectNode = _kubb_ast.ast.narrowSchema(node, "object");
|
|
612
|
-
if (!objectNode) return node;
|
|
613
|
-
const { propertyName, enumValues } = entry;
|
|
614
|
-
const enumSchema = _kubb_ast.ast.factory.createSchema({
|
|
615
|
-
type: "enum",
|
|
616
|
-
enumValues
|
|
617
|
-
});
|
|
618
|
-
const newProp = _kubb_ast.ast.factory.createProperty({
|
|
619
|
-
name: propertyName,
|
|
620
|
-
required: true,
|
|
621
|
-
schema: enumSchema
|
|
622
|
-
});
|
|
623
|
-
const existingIdx = objectNode.properties.findIndex((p) => p.name === propertyName);
|
|
624
|
-
const newProperties = existingIdx >= 0 ? objectNode.properties.map((p, i) => i === existingIdx ? newProp : p) : [...objectNode.properties, newProp];
|
|
625
|
-
return {
|
|
626
|
-
...objectNode,
|
|
627
|
-
properties: newProperties
|
|
628
|
-
};
|
|
629
|
-
}
|
|
630
|
-
/**
|
|
631
|
-
* Creates a single-property object schema used as a discriminator literal.
|
|
632
|
-
*
|
|
633
|
-
* @example
|
|
634
|
-
* ```ts
|
|
635
|
-
* createDiscriminantNode({ propertyName: 'type', value: 'dog' })
|
|
636
|
-
* // -> { type: 'object', properties: [{ name: 'type', required: true, schema: enum('dog') }] }
|
|
637
|
-
* ```
|
|
638
|
-
*/
|
|
639
|
-
function createDiscriminantNode({ propertyName, value }) {
|
|
640
|
-
return _kubb_ast.ast.factory.createSchema({
|
|
641
|
-
type: "object",
|
|
642
|
-
primitive: "object",
|
|
643
|
-
properties: [_kubb_ast.ast.factory.createProperty({
|
|
644
|
-
name: propertyName,
|
|
645
|
-
schema: _kubb_ast.ast.factory.createSchema({
|
|
646
|
-
type: "enum",
|
|
647
|
-
primitive: "string",
|
|
648
|
-
enumValues: [value]
|
|
649
|
-
}),
|
|
650
|
-
required: true
|
|
651
|
-
})]
|
|
652
|
-
});
|
|
653
|
-
}
|
|
654
|
-
/**
|
|
655
|
-
* Returns the discriminator key whose mapping value matches `ref`, or `null` when there is no match.
|
|
656
|
-
*
|
|
657
|
-
* @example
|
|
658
|
-
* ```ts
|
|
659
|
-
* findDiscriminator({ dog: '#/components/schemas/Dog' }, '#/components/schemas/Dog') // 'dog'
|
|
660
|
-
* ```
|
|
661
|
-
*/
|
|
662
|
-
function findDiscriminator(mapping, ref) {
|
|
663
|
-
if (!mapping || !ref) return null;
|
|
664
|
-
return Object.entries(mapping).find(([, value]) => value === ref)?.[0] ?? null;
|
|
665
|
-
}
|
|
666
|
-
//#endregion
|
|
667
|
-
//#region src/mime.ts
|
|
668
|
-
/**
|
|
669
|
-
* MIME type fragments that mark a media type as JSON-like.
|
|
670
|
-
*
|
|
671
|
-
* A content type is JSON when it contains any of these substrings. The `+json` entry catches
|
|
672
|
-
* structured-syntax suffixes such as `application/vnd.api+json`.
|
|
673
|
-
*/
|
|
674
|
-
const jsonMimeFragments = [
|
|
675
|
-
"application/json",
|
|
676
|
-
"application/x-json",
|
|
677
|
-
"text/json",
|
|
678
|
-
"text/x-json",
|
|
679
|
-
"+json"
|
|
680
|
-
];
|
|
681
|
-
/**
|
|
682
|
-
* Returns `true` when a media type string is JSON-like.
|
|
683
|
-
*
|
|
684
|
-
* @example
|
|
685
|
-
* ```ts
|
|
686
|
-
* isJsonMimeType('application/json') // true
|
|
687
|
-
* isJsonMimeType('application/vnd.api+json') // true
|
|
688
|
-
* isJsonMimeType('multipart/form-data') // false
|
|
689
|
-
* ```
|
|
690
|
-
*/
|
|
691
|
-
function isJsonMimeType(mimeType) {
|
|
692
|
-
return jsonMimeFragments.some((fragment) => mimeType.includes(fragment));
|
|
693
|
-
}
|
|
694
|
-
//#endregion
|
|
695
663
|
//#region src/operation.ts
|
|
696
664
|
/**
|
|
697
665
|
* Slugifies a path for the `operationId` fallback: non-alphanumerics collapse to single dashes,
|
|
@@ -818,8 +786,56 @@ function getOperations(document) {
|
|
|
818
786
|
return operations;
|
|
819
787
|
}
|
|
820
788
|
//#endregion
|
|
789
|
+
//#region src/dialect.ts
|
|
790
|
+
/**
|
|
791
|
+
* The OpenAPI / Swagger dialect, the default used by `@kubb/adapter-oas`.
|
|
792
|
+
*
|
|
793
|
+
* Implements the spec-agnostic {@link ast.SchemaDialect} contract: it isolates the
|
|
794
|
+
* decisions that differ between specs (nullability, `$ref`, discriminator, binary,
|
|
795
|
+
* ref resolution) so the converter pipeline and dispatch rules stay shared. A
|
|
796
|
+
* future adapter (e.g. AsyncAPI) ships its own dialect, `type: ['null', …]`
|
|
797
|
+
* nullability, no discriminator object, binary via `contentEncoding` and reuses
|
|
798
|
+
* the rest unchanged.
|
|
799
|
+
*
|
|
800
|
+
* Formats (`uuid`, `email`, dates, …) are intentionally NOT here: they are shared
|
|
801
|
+
* JSON Schema vocabulary, so the converters keep that common case.
|
|
802
|
+
*
|
|
803
|
+
* @example
|
|
804
|
+
* ```ts
|
|
805
|
+
* const parser = createSchemaParser(context) // uses oasDialect
|
|
806
|
+
* const parser = createSchemaParser(context, oasDialect) // explicit
|
|
807
|
+
* ```
|
|
808
|
+
*/
|
|
809
|
+
const oasDialect = _kubb_ast.ast.defineDialect({
|
|
810
|
+
name: "oas",
|
|
811
|
+
schema: {
|
|
812
|
+
isNullable,
|
|
813
|
+
isReference,
|
|
814
|
+
isDiscriminator,
|
|
815
|
+
isBinary: (schema) => schema.type === "string" && schema.contentMediaType === "application/octet-stream",
|
|
816
|
+
resolveRef
|
|
817
|
+
}
|
|
818
|
+
});
|
|
819
|
+
//#endregion
|
|
821
820
|
//#region src/resolvers.ts
|
|
822
821
|
/**
|
|
822
|
+
* Reads the server URL from the document's `servers` array at `server.index`,
|
|
823
|
+
* interpolating any `server.variables` into the URL template.
|
|
824
|
+
*
|
|
825
|
+
* Returns `null` when `server.index` is omitted or out of range.
|
|
826
|
+
*
|
|
827
|
+
* @example Resolve the first server
|
|
828
|
+
* `resolveBaseUrl({ document, server: { index: 0 } })`
|
|
829
|
+
*
|
|
830
|
+
* @example Override a path variable
|
|
831
|
+
* `resolveBaseUrl({ document, server: { index: 0, variables: { version: 'v2' } } })`
|
|
832
|
+
*/
|
|
833
|
+
function resolveBaseUrl({ document, server }) {
|
|
834
|
+
const index = server?.index;
|
|
835
|
+
const entry = index !== void 0 ? document.servers?.at(index) : void 0;
|
|
836
|
+
return entry?.url ? resolveServerUrl(entry, server?.variables) : null;
|
|
837
|
+
}
|
|
838
|
+
/**
|
|
823
839
|
* Replaces `{variable}` placeholders in an OpenAPI server URL with provided values.
|
|
824
840
|
* Resolution order: `overrides[key]` → `variable.default` → left unreplaced.
|
|
825
841
|
* Throws if an override value is not in the variable's `enum` list.
|
|
@@ -1014,7 +1030,7 @@ function flattenSchema(schema) {
|
|
|
1014
1030
|
if (allOfFragments.some(hasStructuralKeywords)) return schema;
|
|
1015
1031
|
const { allOf: _allOf, ...rest } = schema;
|
|
1016
1032
|
const merged = rest;
|
|
1017
|
-
for (const fragment of allOfFragments) for (const [key, value] of Object.entries(fragment))
|
|
1033
|
+
for (const fragment of allOfFragments) for (const [key, value] of Object.entries(fragment)) merged[key] ??= value;
|
|
1018
1034
|
return merged;
|
|
1019
1035
|
}
|
|
1020
1036
|
/**
|
|
@@ -2191,7 +2207,7 @@ function refPromotedEnums(node, promoted) {
|
|
|
2191
2207
|
//#region src/schemaDiagnostics.ts
|
|
2192
2208
|
/**
|
|
2193
2209
|
* Reports the advisory diagnostics (`KUBB_UNSUPPORTED_FORMAT`, `KUBB_DEPRECATED`) for one
|
|
2194
|
-
* top-level schema. Walks the node the parser produced
|
|
2210
|
+
* top-level schema. Walks the node the parser produced, threading the RFC 6901
|
|
2195
2211
|
* pointer as it descends so a nested field reports against its full path
|
|
2196
2212
|
* (`#/components/schemas/Pet/properties/owner/properties/name`). Refs are not followed, so the
|
|
2197
2213
|
* resolved schema is reported under its own walk. Reports land in the active build run, are a
|
|
@@ -2243,148 +2259,6 @@ function visit(node, pointer) {
|
|
|
2243
2259
|
if (node.type === "union" || node.type === "intersection") for (const [index, member] of (node.members ?? []).entries()) visit(member, `${pointer}/members/${index}`);
|
|
2244
2260
|
}
|
|
2245
2261
|
//#endregion
|
|
2246
|
-
//#region src/stream.ts
|
|
2247
|
-
/**
|
|
2248
|
-
* Reads the server URL from the document's `servers` array at `server.index`,
|
|
2249
|
-
* interpolating any `server.variables` into the URL template.
|
|
2250
|
-
*
|
|
2251
|
-
* Returns `null` when `server.index` is omitted or out of range.
|
|
2252
|
-
*
|
|
2253
|
-
* @example Resolve the first server
|
|
2254
|
-
* `resolveBaseUrl({ document, server: { index: 0 } })`
|
|
2255
|
-
*
|
|
2256
|
-
* @example Override a path variable
|
|
2257
|
-
* `resolveBaseUrl({ document, server: { index: 0, variables: { version: 'v2' } } })`
|
|
2258
|
-
*/
|
|
2259
|
-
function resolveBaseUrl({ document, server }) {
|
|
2260
|
-
const index = server?.index;
|
|
2261
|
-
const entry = index !== void 0 ? document.servers?.at(index) : void 0;
|
|
2262
|
-
return entry?.url ? resolveServerUrl(entry, server?.variables) : null;
|
|
2263
|
-
}
|
|
2264
|
-
/**
|
|
2265
|
-
* Parses every schema once to build the lookup structures that streaming needs upfront.
|
|
2266
|
-
*
|
|
2267
|
-
* Three things happen in this single pass:
|
|
2268
|
-
* - `refAliasMap` records schemas that are pure `$ref` aliases so the streaming pass can inline them.
|
|
2269
|
-
* - `enumNames` collects the names of every enum schema so plugins skip re-scanning the stream.
|
|
2270
|
-
* - `circularNames` runs cycle detection, which requires all nodes in memory simultaneously.
|
|
2271
|
-
* The `allNodes` array is local and drops out of scope as soon as this function returns.
|
|
2272
|
-
*
|
|
2273
|
-
* After this call, only `refAliasMap` and `discriminatorChildMap` stay alive in the adapter closure.
|
|
2274
|
-
* Both are proportional to the number of aliases or discriminator parents, not total schema count.
|
|
2275
|
-
*
|
|
2276
|
-
* Each schema is parsed again during the streaming pass. This is intentional.
|
|
2277
|
-
* Holding the parsed nodes in memory here would defeat the streaming memory benefit.
|
|
2278
|
-
*
|
|
2279
|
-
* @example
|
|
2280
|
-
* ```ts
|
|
2281
|
-
* const { refAliasMap, enumNames, circularNames } = preScan({
|
|
2282
|
-
* schemas,
|
|
2283
|
-
* parseSchema,
|
|
2284
|
-
* parserOptions,
|
|
2285
|
-
* discriminator: 'preserve',
|
|
2286
|
-
* })
|
|
2287
|
-
* ```
|
|
2288
|
-
*/
|
|
2289
|
-
function preScan({ schemas, parseSchema, parseOperation, document, parserOptions, discriminator, enums = "inline" }) {
|
|
2290
|
-
const allNodes = [];
|
|
2291
|
-
const refAliasMap = /* @__PURE__ */ new Map();
|
|
2292
|
-
const enumNames = [];
|
|
2293
|
-
const discriminatorParentNodes = [];
|
|
2294
|
-
for (const [name, schema] of Object.entries(schemas)) {
|
|
2295
|
-
const node = parseSchema({
|
|
2296
|
-
schema,
|
|
2297
|
-
name
|
|
2298
|
-
}, parserOptions);
|
|
2299
|
-
allNodes.push(node);
|
|
2300
|
-
reportSchemaDiagnostics({
|
|
2301
|
-
node,
|
|
2302
|
-
name
|
|
2303
|
-
});
|
|
2304
|
-
if (node.type === "ref" && node.name && node.name !== name) refAliasMap.set(name, node);
|
|
2305
|
-
if (_kubb_ast.ast.narrowSchema(node, _kubb_ast.ast.schemaTypes.enum) && node.name) enumNames.push(node.name);
|
|
2306
|
-
if (discriminator === "propagate" && (schema.oneOf ?? schema.anyOf) && schema.discriminator?.propertyName) discriminatorParentNodes.push(node);
|
|
2307
|
-
}
|
|
2308
|
-
const circularNames = [...(0, _kubb_ast.findCircularSchemas)(allNodes)];
|
|
2309
|
-
const discriminatorChildMap = discriminatorParentNodes.length > 0 ? buildDiscriminatorChildMap(discriminatorParentNodes) : null;
|
|
2310
|
-
let promotedEnums = null;
|
|
2311
|
-
if (enums === "root" && document && parseOperation) {
|
|
2312
|
-
const operationNodes = [];
|
|
2313
|
-
for (const operation of getOperations(document)) {
|
|
2314
|
-
const operationNode = parseOperation(parserOptions, operation);
|
|
2315
|
-
if (operationNode) operationNodes.push(operationNode);
|
|
2316
|
-
}
|
|
2317
|
-
promotedEnums = collectInlineEnums([...allNodes, ...operationNodes], new Set(Object.keys(schemas)));
|
|
2318
|
-
for (const name of promotedEnums.keys()) enumNames.push(name);
|
|
2319
|
-
}
|
|
2320
|
-
return {
|
|
2321
|
-
refAliasMap,
|
|
2322
|
-
enumNames,
|
|
2323
|
-
circularNames,
|
|
2324
|
-
discriminatorChildMap,
|
|
2325
|
-
promotedEnums
|
|
2326
|
-
};
|
|
2327
|
-
}
|
|
2328
|
-
/**
|
|
2329
|
-
* Creates a lazy `InputNode<true>` from already-resolved adapter state.
|
|
2330
|
-
*
|
|
2331
|
-
* The schema and operation iterables each start a fresh parse pass on every
|
|
2332
|
-
* `[Symbol.asyncIterator]()` call. This lets multiple plugins consume the same
|
|
2333
|
-
* stream object independently without sharing a cursor or holding all nodes in memory.
|
|
2334
|
-
*
|
|
2335
|
-
* Ref aliases in `refAliasMap` are inlined during iteration: an alias entry is replaced
|
|
2336
|
-
* with its target's parsed node (but keeps the alias name) so plugins never receive bare `ref` nodes.
|
|
2337
|
-
*
|
|
2338
|
-
* @example
|
|
2339
|
-
* ```ts
|
|
2340
|
-
* const streamNode = createInputStream({ schemas, parseSchema, parseOperation, document, parserOptions, refAliasMap, discriminatorChildMap, meta })
|
|
2341
|
-
* for await (const schema of streamNode.schemas) {
|
|
2342
|
-
* // each call to for-await restarts from the first schema
|
|
2343
|
-
* }
|
|
2344
|
-
* ```
|
|
2345
|
-
*/
|
|
2346
|
-
function createInputStream({ schemas, parseSchema, parseOperation, document, parserOptions, refAliasMap, discriminatorChildMap, promotedEnums, meta }) {
|
|
2347
|
-
const schemasIterable = { [Symbol.asyncIterator]() {
|
|
2348
|
-
return (async function* () {
|
|
2349
|
-
if (promotedEnums) for (const definition of promotedEnums.values()) yield definition;
|
|
2350
|
-
for (const [name, schema] of Object.entries(schemas)) {
|
|
2351
|
-
const alias = refAliasMap.get(name);
|
|
2352
|
-
if (alias?.name && schemas[alias.name]) {
|
|
2353
|
-
const aliasNode = {
|
|
2354
|
-
...parseSchema({
|
|
2355
|
-
schema: schemas[alias.name],
|
|
2356
|
-
name: alias.name
|
|
2357
|
-
}, parserOptions),
|
|
2358
|
-
name
|
|
2359
|
-
};
|
|
2360
|
-
yield promotedEnums ? refPromotedEnums(aliasNode, promotedEnums) : aliasNode;
|
|
2361
|
-
continue;
|
|
2362
|
-
}
|
|
2363
|
-
const parsed = parseSchema({
|
|
2364
|
-
schema,
|
|
2365
|
-
name
|
|
2366
|
-
}, parserOptions);
|
|
2367
|
-
const node = discriminatorChildMap?.get(name) ? patchDiscriminatorNode(parsed, discriminatorChildMap.get(name)) : parsed;
|
|
2368
|
-
yield promotedEnums ? refPromotedEnums(node, promotedEnums) : node;
|
|
2369
|
-
}
|
|
2370
|
-
})();
|
|
2371
|
-
} };
|
|
2372
|
-
const operationsIterable = { [Symbol.asyncIterator]() {
|
|
2373
|
-
return (async function* () {
|
|
2374
|
-
for (const operation of getOperations(document)) {
|
|
2375
|
-
const node = parseOperation(parserOptions, operation);
|
|
2376
|
-
if (node) yield promotedEnums ? refPromotedEnums(node, promotedEnums) : node;
|
|
2377
|
-
}
|
|
2378
|
-
})();
|
|
2379
|
-
} };
|
|
2380
|
-
return _kubb_ast.ast.factory.createInput({
|
|
2381
|
-
stream: true,
|
|
2382
|
-
schemas: schemasIterable,
|
|
2383
|
-
operations: operationsIterable,
|
|
2384
|
-
meta
|
|
2385
|
-
});
|
|
2386
|
-
}
|
|
2387
|
-
//#endregion
|
|
2388
2262
|
//#region src/adapter.ts
|
|
2389
2263
|
/**
|
|
2390
2264
|
* The `name` of `@kubb/adapter-oas`, used to identify this adapter in a Kubb config.
|
|
@@ -2432,7 +2306,6 @@ const adapterOas = (0, _kubb_core.createAdapter)((options) => {
|
|
|
2432
2306
|
const documentCache = /* @__PURE__ */ new WeakMap();
|
|
2433
2307
|
const schemasCache = /* @__PURE__ */ new WeakMap();
|
|
2434
2308
|
const schemaParserCache = /* @__PURE__ */ new WeakMap();
|
|
2435
|
-
const preScanCache = /* @__PURE__ */ new WeakMap();
|
|
2436
2309
|
function ensureDocument(source) {
|
|
2437
2310
|
const cached = documentCache.get(source);
|
|
2438
2311
|
if (cached) return cached;
|
|
@@ -2466,35 +2339,57 @@ const adapterOas = (0, _kubb_core.createAdapter)((options) => {
|
|
|
2466
2339
|
schemaParserCache.set(document, parser);
|
|
2467
2340
|
return parser;
|
|
2468
2341
|
}
|
|
2469
|
-
function
|
|
2470
|
-
const
|
|
2471
|
-
|
|
2472
|
-
const
|
|
2473
|
-
|
|
2474
|
-
|
|
2475
|
-
|
|
2476
|
-
|
|
2477
|
-
|
|
2478
|
-
|
|
2479
|
-
|
|
2480
|
-
|
|
2481
|
-
|
|
2482
|
-
|
|
2483
|
-
|
|
2484
|
-
|
|
2485
|
-
|
|
2486
|
-
|
|
2487
|
-
|
|
2488
|
-
|
|
2489
|
-
|
|
2490
|
-
|
|
2491
|
-
|
|
2492
|
-
|
|
2493
|
-
|
|
2494
|
-
|
|
2495
|
-
|
|
2496
|
-
|
|
2497
|
-
|
|
2342
|
+
function parseInput({ document, schemas, parser }) {
|
|
2343
|
+
const { parseSchema, parseOperation } = parser;
|
|
2344
|
+
const parsedByName = /* @__PURE__ */ new Map();
|
|
2345
|
+
const refAliasMap = /* @__PURE__ */ new Map();
|
|
2346
|
+
const enumNames = [];
|
|
2347
|
+
const discriminatorParentNodes = [];
|
|
2348
|
+
for (const [name, schema] of Object.entries(schemas)) {
|
|
2349
|
+
const node = parseSchema({
|
|
2350
|
+
schema,
|
|
2351
|
+
name
|
|
2352
|
+
}, parserOptions);
|
|
2353
|
+
parsedByName.set(name, node);
|
|
2354
|
+
reportSchemaDiagnostics({
|
|
2355
|
+
node,
|
|
2356
|
+
name
|
|
2357
|
+
});
|
|
2358
|
+
if (node.type === "ref" && node.name && node.name !== name) refAliasMap.set(name, node);
|
|
2359
|
+
if ((0, _kubb_ast.narrowSchema)(node, "enum") && node.name) enumNames.push(node.name);
|
|
2360
|
+
if (discriminator === "propagate" && (schema.oneOf ?? schema.anyOf) && schema.discriminator?.propertyName) discriminatorParentNodes.push(node);
|
|
2361
|
+
}
|
|
2362
|
+
const circularNames = [...(0, _kubb_ast.findCircularSchemas)([...parsedByName.values()])];
|
|
2363
|
+
const discriminatorChildMap = discriminatorParentNodes.length > 0 ? buildDiscriminatorChildMap(discriminatorParentNodes) : null;
|
|
2364
|
+
const operationNodes = [];
|
|
2365
|
+
for (const operation of getOperations(document)) {
|
|
2366
|
+
const operationNode = parseOperation(parserOptions, operation);
|
|
2367
|
+
if (operationNode) operationNodes.push(operationNode);
|
|
2368
|
+
}
|
|
2369
|
+
let promotedEnums = null;
|
|
2370
|
+
if (enums === "root") {
|
|
2371
|
+
promotedEnums = collectInlineEnums([...parsedByName.values(), ...operationNodes], new Set(Object.keys(schemas)));
|
|
2372
|
+
for (const name of promotedEnums.keys()) enumNames.push(name);
|
|
2373
|
+
}
|
|
2374
|
+
const schemaNodes = promotedEnums ? [...promotedEnums.values()] : [];
|
|
2375
|
+
for (const name of Object.keys(schemas)) {
|
|
2376
|
+
const alias = refAliasMap.get(name);
|
|
2377
|
+
let node;
|
|
2378
|
+
if (alias?.name && parsedByName.has(alias.name)) node = {
|
|
2379
|
+
...parsedByName.get(alias.name),
|
|
2380
|
+
name
|
|
2381
|
+
};
|
|
2382
|
+
else {
|
|
2383
|
+
const parsed = parsedByName.get(name);
|
|
2384
|
+
const child = discriminatorChildMap?.get(name);
|
|
2385
|
+
node = child ? patchDiscriminatorNode(parsed, child) : parsed;
|
|
2386
|
+
}
|
|
2387
|
+
schemaNodes.push(promotedEnums ? refPromotedEnums(node, promotedEnums) : node);
|
|
2388
|
+
}
|
|
2389
|
+
const operations = promotedEnums ? operationNodes.map((node) => refPromotedEnums(node, promotedEnums)) : operationNodes;
|
|
2390
|
+
return _kubb_ast.ast.factory.createInput({
|
|
2391
|
+
schemas: schemaNodes,
|
|
2392
|
+
operations,
|
|
2498
2393
|
meta: {
|
|
2499
2394
|
title: document.info?.title,
|
|
2500
2395
|
description: document.info?.description,
|
|
@@ -2545,15 +2440,13 @@ const adapterOas = (0, _kubb_core.createAdapter)((options) => {
|
|
|
2545
2440
|
} });
|
|
2546
2441
|
},
|
|
2547
2442
|
async parse(source) {
|
|
2548
|
-
const
|
|
2549
|
-
|
|
2550
|
-
|
|
2551
|
-
schemas,
|
|
2552
|
-
|
|
2553
|
-
meta: streamNode.meta
|
|
2443
|
+
const document = await ensureDocument(source);
|
|
2444
|
+
return parseInput({
|
|
2445
|
+
document,
|
|
2446
|
+
schemas: await ensureSchemas(document),
|
|
2447
|
+
parser: ensureSchemaParser(document)
|
|
2554
2448
|
});
|
|
2555
|
-
}
|
|
2556
|
-
stream: createStream
|
|
2449
|
+
}
|
|
2557
2450
|
};
|
|
2558
2451
|
});
|
|
2559
2452
|
//#endregion
|