@kubb/adapter-oas 5.0.0-beta.19 → 5.0.0-beta.20

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 CHANGED
@@ -21,11 +21,11 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
21
21
  enumerable: true
22
22
  }) : target, mod));
23
23
  //#endregion
24
+ let node_path = require("node:path");
25
+ node_path = __toESM(node_path, 1);
24
26
  let _kubb_core = require("@kubb/core");
25
27
  let oas = require("oas");
26
28
  oas = __toESM(oas, 1);
27
- let node_path = require("node:path");
28
- node_path = __toESM(node_path, 1);
29
29
  let _redocly_openapi_core = require("@redocly/openapi-core");
30
30
  let oas_normalize = require("oas-normalize");
31
31
  oas_normalize = __toESM(oas_normalize, 1);
@@ -33,230 +33,6 @@ let swagger2openapi = require("swagger2openapi");
33
33
  swagger2openapi = __toESM(swagger2openapi, 1);
34
34
  let oas_types = require("oas/types");
35
35
  let oas_utils = require("oas/utils");
36
- //#region src/constants.ts
37
- /**
38
- * Default parser options applied when no explicit options are provided.
39
- *
40
- * @example
41
- * ```ts
42
- * import { DEFAULT_PARSER_OPTIONS } from '@kubb/adapter-oas'
43
- *
44
- * const parser = createOasParser(oas)
45
- * const root = parser.parse({ ...DEFAULT_PARSER_OPTIONS, dateType: 'date' })
46
- * ```
47
- */
48
- const DEFAULT_PARSER_OPTIONS = {
49
- dateType: "string",
50
- integerType: "bigint",
51
- unknownType: "any",
52
- emptySchemaType: "any",
53
- enumSuffix: "enum"
54
- };
55
- /**
56
- * JSON-Pointer prefix for schemas declared under `components.schemas` in an OpenAPI document.
57
- *
58
- * Used when building or parsing `$ref` strings.
59
- *
60
- * @example
61
- * ```ts
62
- * `${SCHEMA_REF_PREFIX}Pet` // '#/components/schemas/Pet'
63
- * ```
64
- */
65
- const SCHEMA_REF_PREFIX = "#/components/schemas/";
66
- /**
67
- * OpenAPI version string written into the stub document created during multi-spec merges.
68
- */
69
- const MERGE_OPENAPI_VERSION = "3.0.0";
70
- /**
71
- * Fallback `info.title` placed in the stub document when merging multiple API files.
72
- */
73
- const MERGE_DEFAULT_TITLE = "Merged API";
74
- /**
75
- * Fallback `info.version` placed in the stub document when merging multiple API files.
76
- */
77
- const MERGE_DEFAULT_VERSION = "1.0.0";
78
- /**
79
- * Set of JSON Schema keywords that prevent a schema fragment from being inlined during `allOf` flattening.
80
- *
81
- * A fragment that contains any of these keys carries structural meaning of its own and must stay as a separate
82
- * intersection member rather than being merged into the parent.
83
- *
84
- * @example
85
- * ```ts
86
- * import { structuralKeys } from '@kubb/adapter-oas'
87
- *
88
- * const isStructural = Object.keys(fragment).some((key) => structuralKeys.has(key))
89
- * // true when fragment has e.g. 'properties' or 'oneOf'
90
- * ```
91
- */
92
- const structuralKeys = new Set([
93
- "properties",
94
- "items",
95
- "additionalProperties",
96
- "oneOf",
97
- "anyOf",
98
- "allOf",
99
- "not"
100
- ]);
101
- /**
102
- * Static map from OAS `format` strings to Kubb `SchemaType` values.
103
- *
104
- * Only formats whose AST type differs from the OAS `type` field appear here.
105
- * Formats that depend on runtime options (`int64`, `date-time`, `date`, `time`) are handled separately
106
- * in the parser. `ipv4` and `ipv6` map to their own dedicated schema types; `hostname` and
107
- * `idn-hostname` map to `'url'` as the closest generic string-format type.
108
- *
109
- * @example
110
- * ```ts
111
- * import { formatMap } from '@kubb/adapter-oas'
112
- *
113
- * formatMap['uuid'] // 'uuid'
114
- * formatMap['binary'] // 'blob'
115
- * formatMap['float'] // 'number'
116
- * ```
117
- */
118
- const formatMap = {
119
- uuid: "uuid",
120
- email: "email",
121
- "idn-email": "email",
122
- uri: "url",
123
- "uri-reference": "url",
124
- url: "url",
125
- ipv4: "ipv4",
126
- ipv6: "ipv6",
127
- hostname: "url",
128
- "idn-hostname": "url",
129
- binary: "blob",
130
- byte: "blob",
131
- int32: "integer",
132
- float: "number",
133
- double: "number"
134
- };
135
- /**
136
- * Vendor extension keys that attach human-readable labels to enum values, checked in priority order.
137
- *
138
- * @example
139
- * ```ts
140
- * import { enumExtensionKeys } from '@kubb/adapter-oas'
141
- *
142
- * const key = enumExtensionKeys.find((k) => k in schema) // 'x-enumNames' | 'x-enum-varnames' | undefined
143
- * ```
144
- */
145
- const enumExtensionKeys = ["x-enumNames", "x-enum-varnames"];
146
- /**
147
- * Maps `'any' | 'unknown' | 'void'` option strings to their `ScalarSchemaType` constant.
148
- * Replaces a plain object lookup with a `Map` for explicit key membership testing via `.has()`.
149
- */
150
- const typeOptionMap = new Map([
151
- ["any", _kubb_core.ast.schemaTypes.any],
152
- ["unknown", _kubb_core.ast.schemaTypes.unknown],
153
- ["void", _kubb_core.ast.schemaTypes.void]
154
- ]);
155
- //#endregion
156
- //#region src/discriminator.ts
157
- /**
158
- * Builds a map of child schema names → discriminator patch data by scanning the given
159
- * top-level AST schema nodes for union schemas that carry a `discriminatorPropertyName`.
160
- *
161
- * Extracted from `applyDiscriminatorInheritance` so the streaming path can call it on a
162
- * small pre-parsed subset of schemas (only the discriminator parents) rather than on all
163
- * schemas at once.
164
- */
165
- function buildDiscriminatorChildMap(schemas) {
166
- const childMap = /* @__PURE__ */ new Map();
167
- for (const schema of schemas) {
168
- let unionNode = _kubb_core.ast.narrowSchema(schema, "union");
169
- if (!unionNode) {
170
- const intersectionMembers = _kubb_core.ast.narrowSchema(schema, "intersection")?.members;
171
- if (intersectionMembers) for (const m of intersectionMembers) {
172
- const u = _kubb_core.ast.narrowSchema(m, "union");
173
- if (u) {
174
- unionNode = u;
175
- break;
176
- }
177
- }
178
- }
179
- if (!unionNode?.discriminatorPropertyName || !unionNode.members) continue;
180
- const { discriminatorPropertyName, members } = unionNode;
181
- for (const member of members) {
182
- const intersectionNode = _kubb_core.ast.narrowSchema(member, "intersection");
183
- if (!intersectionNode?.members) continue;
184
- let refNode;
185
- let objNode;
186
- for (const m of intersectionNode.members) {
187
- refNode ??= _kubb_core.ast.narrowSchema(m, "ref");
188
- objNode ??= _kubb_core.ast.narrowSchema(m, "object");
189
- }
190
- if (!refNode?.name || !objNode) continue;
191
- const prop = objNode.properties.find((p) => p.name === discriminatorPropertyName);
192
- const enumNode = prop ? _kubb_core.ast.narrowSchema(prop.schema, "enum") : void 0;
193
- if (!enumNode?.enumValues?.length) continue;
194
- const enumValues = enumNode.enumValues.filter((v) => v !== null);
195
- if (!enumValues.length) continue;
196
- const existing = childMap.get(refNode.name);
197
- if (!existing) {
198
- childMap.set(refNode.name, {
199
- propertyName: discriminatorPropertyName,
200
- enumValues: [...enumValues]
201
- });
202
- continue;
203
- }
204
- existing.enumValues.push(...enumValues);
205
- }
206
- }
207
- return childMap;
208
- }
209
- /**
210
- * Patches a single top-level `SchemaNode` with its discriminator entry (adds or replaces
211
- * the discriminant property). Used by the streaming path to apply patches inline per yield
212
- * without buffering all schemas.
213
- */
214
- function patchDiscriminatorNode(node, entry) {
215
- const objectNode = _kubb_core.ast.narrowSchema(node, "object");
216
- if (!objectNode) return node;
217
- const { propertyName, enumValues } = entry;
218
- const enumSchema = _kubb_core.ast.createSchema({
219
- type: "enum",
220
- enumValues
221
- });
222
- const newProp = _kubb_core.ast.createProperty({
223
- name: propertyName,
224
- required: true,
225
- schema: enumSchema
226
- });
227
- const existingIdx = objectNode.properties.findIndex((p) => p.name === propertyName);
228
- const newProperties = existingIdx >= 0 ? objectNode.properties.map((p, i) => i === existingIdx ? newProp : p) : [...objectNode.properties, newProp];
229
- return {
230
- ...objectNode,
231
- properties: newProperties
232
- };
233
- }
234
- /**
235
- * Injects discriminator enum values into child schemas so they know which value identifies them.
236
- *
237
- * Finds every union schema in `input.schemas` that has a `discriminatorPropertyName`, collects the
238
- * enum value each union member is mapped to, then adds (or replaces) that property on the matching
239
- * child object schema.
240
- *
241
- * Returns a new `InputNode` — the original is never mutated.
242
- *
243
- * @example
244
- * ```ts
245
- * const { root } = parseOas(document, options)
246
- * const next = applyDiscriminatorInheritance(root)
247
- * ```
248
- */
249
- function applyDiscriminatorInheritance(root) {
250
- const childMap = buildDiscriminatorChildMap(root.schemas);
251
- if (childMap.size === 0) return root;
252
- return _kubb_core.ast.transform(root, { schema(node, { parent }) {
253
- if (parent?.kind !== "Input" || !node.name) return;
254
- const entry = childMap.get(node.name);
255
- if (!entry) return;
256
- return patchDiscriminatorNode(node, entry);
257
- } });
258
- }
259
- //#endregion
260
36
  //#region ../../internals/utils/src/casing.ts
261
37
  /**
262
38
  * Shared implementation for camelCase and PascalCase conversion.
@@ -355,6 +131,30 @@ function mergeDeep(target, source) {
355
131
  return result;
356
132
  }
357
133
  //#endregion
134
+ //#region ../../internals/utils/src/promise.ts
135
+ /**
136
+ * Returns a wrapper that caches the result of the first invocation and replays
137
+ * it for every subsequent call, ignoring later arguments.
138
+ *
139
+ * Works for sync and async factories — for async, the cached value is the
140
+ * promise itself, so concurrent callers share one in-flight execution and
141
+ * cannot race each other.
142
+ *
143
+ * @example
144
+ * ```ts
145
+ * const loadDocument = once(async (path: string) => parse(await readFile(path)))
146
+ * const a = loadDocument('./a.yaml') // parses
147
+ * const b = loadDocument('./b.yaml') // returns the cached promise from the first call
148
+ * ```
149
+ */
150
+ function once(factory) {
151
+ let cache;
152
+ return (...args) => {
153
+ if (!cache) cache = { value: factory(...args) };
154
+ return cache.value;
155
+ };
156
+ }
157
+ //#endregion
358
158
  //#region ../../internals/utils/src/reserved.ts
359
159
  /**
360
160
  * JavaScript and Java reserved words.
@@ -604,6 +404,126 @@ var URLPath = class {
604
404
  }
605
405
  };
606
406
  //#endregion
407
+ //#region src/constants.ts
408
+ /**
409
+ * Default parser options applied when no explicit options are provided.
410
+ *
411
+ * @example
412
+ * ```ts
413
+ * import { DEFAULT_PARSER_OPTIONS } from '@kubb/adapter-oas'
414
+ *
415
+ * const parser = createOasParser(oas)
416
+ * const root = parser.parse({ ...DEFAULT_PARSER_OPTIONS, dateType: 'date' })
417
+ * ```
418
+ */
419
+ const DEFAULT_PARSER_OPTIONS = {
420
+ dateType: "string",
421
+ integerType: "bigint",
422
+ unknownType: "any",
423
+ emptySchemaType: "any",
424
+ enumSuffix: "enum"
425
+ };
426
+ /**
427
+ * JSON-Pointer prefix for schemas declared under `components.schemas` in an OpenAPI document.
428
+ *
429
+ * Used when building or parsing `$ref` strings.
430
+ *
431
+ * @example
432
+ * ```ts
433
+ * `${SCHEMA_REF_PREFIX}Pet` // '#/components/schemas/Pet'
434
+ * ```
435
+ */
436
+ const SCHEMA_REF_PREFIX = "#/components/schemas/";
437
+ /**
438
+ * OpenAPI version string written into the stub document created during multi-spec merges.
439
+ */
440
+ const MERGE_OPENAPI_VERSION = "3.0.0";
441
+ /**
442
+ * Fallback `info.title` placed in the stub document when merging multiple API files.
443
+ */
444
+ const MERGE_DEFAULT_TITLE = "Merged API";
445
+ /**
446
+ * Fallback `info.version` placed in the stub document when merging multiple API files.
447
+ */
448
+ const MERGE_DEFAULT_VERSION = "1.0.0";
449
+ /**
450
+ * Set of JSON Schema keywords that prevent a schema fragment from being inlined during `allOf` flattening.
451
+ *
452
+ * A fragment that contains any of these keys carries structural meaning of its own and must stay as a separate
453
+ * intersection member rather than being merged into the parent.
454
+ *
455
+ * @example
456
+ * ```ts
457
+ * import { structuralKeys } from '@kubb/adapter-oas'
458
+ *
459
+ * const isStructural = Object.keys(fragment).some((key) => structuralKeys.has(key))
460
+ * // true when fragment has e.g. 'properties' or 'oneOf'
461
+ * ```
462
+ */
463
+ const structuralKeys = new Set([
464
+ "properties",
465
+ "items",
466
+ "additionalProperties",
467
+ "oneOf",
468
+ "anyOf",
469
+ "allOf",
470
+ "not"
471
+ ]);
472
+ /**
473
+ * Static map from OAS `format` strings to Kubb `SchemaType` values.
474
+ *
475
+ * Only formats whose AST type differs from the OAS `type` field appear here.
476
+ * Formats that depend on runtime options (`int64`, `date-time`, `date`, `time`) are handled separately
477
+ * in the parser. `ipv4` and `ipv6` map to their own dedicated schema types; `hostname` and
478
+ * `idn-hostname` map to `'url'` as the closest generic string-format type.
479
+ *
480
+ * @example
481
+ * ```ts
482
+ * import { formatMap } from '@kubb/adapter-oas'
483
+ *
484
+ * formatMap['uuid'] // 'uuid'
485
+ * formatMap['binary'] // 'blob'
486
+ * formatMap['float'] // 'number'
487
+ * ```
488
+ */
489
+ const formatMap = {
490
+ uuid: "uuid",
491
+ email: "email",
492
+ "idn-email": "email",
493
+ uri: "url",
494
+ "uri-reference": "url",
495
+ url: "url",
496
+ ipv4: "ipv4",
497
+ ipv6: "ipv6",
498
+ hostname: "url",
499
+ "idn-hostname": "url",
500
+ binary: "blob",
501
+ byte: "blob",
502
+ int32: "integer",
503
+ float: "number",
504
+ double: "number"
505
+ };
506
+ /**
507
+ * Vendor extension keys that attach human-readable labels to enum values, checked in priority order.
508
+ *
509
+ * @example
510
+ * ```ts
511
+ * import { enumExtensionKeys } from '@kubb/adapter-oas'
512
+ *
513
+ * const key = enumExtensionKeys.find((k) => k in schema) // 'x-enumNames' | 'x-enum-varnames' | undefined
514
+ * ```
515
+ */
516
+ const enumExtensionKeys = ["x-enumNames", "x-enum-varnames"];
517
+ /**
518
+ * Maps `'any' | 'unknown' | 'void'` option strings to their `ScalarSchemaType` constant.
519
+ * Replaces a plain object lookup with a `Map` for explicit key membership testing via `.has()`.
520
+ */
521
+ const typeOptionMap = new Map([
522
+ ["any", _kubb_core.ast.schemaTypes.any],
523
+ ["unknown", _kubb_core.ast.schemaTypes.unknown],
524
+ ["void", _kubb_core.ast.schemaTypes.void]
525
+ ]);
526
+ //#endregion
607
527
  //#region src/guards.ts
608
528
  /**
609
529
  * Returns `true` when `doc` is a Swagger 2.0 document (no `openapi` key).
@@ -1911,48 +1831,202 @@ function createSchemaParser(ctx) {
1911
1831
  parseParameter
1912
1832
  };
1913
1833
  }
1834
+ //#endregion
1835
+ //#region src/discriminator.ts
1914
1836
  /**
1915
- * Parses an OpenAPI specification into Kubb's universal `InputNode` AST.
1837
+ * Builds a map of child schema names discriminator patch data by scanning the given
1838
+ * top-level AST schema nodes for union schemas that carry a `discriminatorPropertyName`.
1916
1839
  *
1917
- * This is the main entry point for `@kubb/adapter-oas`. It converts OpenAPI/Swagger specs into a spec-agnostic tree
1918
- * that downstream plugins (`plugin-ts`, `plugin-zod`, etc.) consume for code generation. No code is generated here —
1919
- * the tree is a pure data structure representing all schemas and operations.
1840
+ * Extracted from `applyDiscriminatorInheritance` so the streaming path can call it on a
1841
+ * small pre-parsed subset of schemas (only the discriminator parents) rather than on all
1842
+ * schemas at once.
1843
+ */
1844
+ function buildDiscriminatorChildMap(schemas) {
1845
+ const childMap = /* @__PURE__ */ new Map();
1846
+ for (const schema of schemas) {
1847
+ let unionNode = _kubb_core.ast.narrowSchema(schema, "union");
1848
+ if (!unionNode) {
1849
+ const intersectionMembers = _kubb_core.ast.narrowSchema(schema, "intersection")?.members;
1850
+ if (intersectionMembers) for (const m of intersectionMembers) {
1851
+ const u = _kubb_core.ast.narrowSchema(m, "union");
1852
+ if (u) {
1853
+ unionNode = u;
1854
+ break;
1855
+ }
1856
+ }
1857
+ }
1858
+ if (!unionNode?.discriminatorPropertyName || !unionNode.members) continue;
1859
+ const { discriminatorPropertyName, members } = unionNode;
1860
+ for (const member of members) {
1861
+ const intersectionNode = _kubb_core.ast.narrowSchema(member, "intersection");
1862
+ if (!intersectionNode?.members) continue;
1863
+ let refNode;
1864
+ let objNode;
1865
+ for (const m of intersectionNode.members) {
1866
+ refNode ??= _kubb_core.ast.narrowSchema(m, "ref");
1867
+ objNode ??= _kubb_core.ast.narrowSchema(m, "object");
1868
+ }
1869
+ if (!refNode?.name || !objNode) continue;
1870
+ const prop = objNode.properties.find((p) => p.name === discriminatorPropertyName);
1871
+ const enumNode = prop ? _kubb_core.ast.narrowSchema(prop.schema, "enum") : void 0;
1872
+ if (!enumNode?.enumValues?.length) continue;
1873
+ const enumValues = enumNode.enumValues.filter((v) => v !== null);
1874
+ if (!enumValues.length) continue;
1875
+ const existing = childMap.get(refNode.name);
1876
+ if (!existing) {
1877
+ childMap.set(refNode.name, {
1878
+ propertyName: discriminatorPropertyName,
1879
+ enumValues: [...enumValues]
1880
+ });
1881
+ continue;
1882
+ }
1883
+ existing.enumValues.push(...enumValues);
1884
+ }
1885
+ }
1886
+ return childMap;
1887
+ }
1888
+ /**
1889
+ * Patches a single top-level `SchemaNode` with its discriminator entry (adds or replaces
1890
+ * the discriminant property). Used by the streaming path to apply patches inline per yield
1891
+ * without buffering all schemas.
1892
+ */
1893
+ function patchDiscriminatorNode(node, entry) {
1894
+ const objectNode = _kubb_core.ast.narrowSchema(node, "object");
1895
+ if (!objectNode) return node;
1896
+ const { propertyName, enumValues } = entry;
1897
+ const enumSchema = _kubb_core.ast.createSchema({
1898
+ type: "enum",
1899
+ enumValues
1900
+ });
1901
+ const newProp = _kubb_core.ast.createProperty({
1902
+ name: propertyName,
1903
+ required: true,
1904
+ schema: enumSchema
1905
+ });
1906
+ const existingIdx = objectNode.properties.findIndex((p) => p.name === propertyName);
1907
+ const newProperties = existingIdx >= 0 ? objectNode.properties.map((p, i) => i === existingIdx ? newProp : p) : [...objectNode.properties, newProp];
1908
+ return {
1909
+ ...objectNode,
1910
+ properties: newProperties
1911
+ };
1912
+ }
1913
+ //#endregion
1914
+ //#region src/stream.ts
1915
+ /**
1916
+ * Reads the server URL from the document's `servers` array at `serverIndex`,
1917
+ * interpolating any `serverVariables` into the URL template.
1918
+ *
1919
+ * Returns `undefined` when `serverIndex` is omitted or out of range.
1920
+ *
1921
+ * @example Resolve the first server
1922
+ * `resolveBaseUrl({ document, serverIndex: 0 })`
1923
+ *
1924
+ * @example Override a path variable
1925
+ * `resolveBaseUrl({ document, serverIndex: 0, serverVariables: { version: 'v2' } })`
1926
+ */
1927
+ function resolveBaseUrl({ document, serverIndex, serverVariables }) {
1928
+ const server = serverIndex !== void 0 ? document.servers?.at(serverIndex) : void 0;
1929
+ return server?.url ? resolveServerUrl(server, serverVariables) : void 0;
1930
+ }
1931
+ /**
1932
+ * Parses every schema once to build the lookup structures that streaming needs upfront.
1920
1933
  *
1921
- * Returns the AST root and a `nameMapping` for resolving schema references.
1934
+ * Three things happen in this single pass:
1935
+ * - `refAliasMap` records schemas that are pure `$ref` aliases so the streaming pass can inline them.
1936
+ * - `enumNames` collects the names of every enum schema so plugins skip re-scanning the stream.
1937
+ * - `circularNames` runs cycle detection, which requires all nodes in memory simultaneously.
1938
+ * The `allNodes` array is local and drops out of scope as soon as this function returns.
1939
+ *
1940
+ * After this call, only `refAliasMap` and `discriminatorChildMap` stay alive in the adapter closure.
1941
+ * Both are proportional to the number of aliases or discriminator parents, not total schema count.
1942
+ *
1943
+ * Each schema is parsed again during the streaming pass — this is intentional.
1944
+ * Holding the parsed nodes in memory here would defeat the streaming memory benefit.
1922
1945
  *
1923
1946
  * @example
1924
1947
  * ```ts
1925
- * import { parseOas } from '@kubb/adapter-oas'
1926
- *
1927
- * const document = await parseFromConfig(config)
1928
- * const { root, nameMapping } = parseOas(document, { dateType: 'date', contentType: 'application/json' })
1948
+ * const { refAliasMap, enumNames, circularNames } = preScan({
1949
+ * schemas,
1950
+ * parseSchema,
1951
+ * parserOptions,
1952
+ * discriminator: 'strict',
1953
+ * })
1929
1954
  * ```
1930
1955
  */
1931
- function parseOas(document, options = {}) {
1932
- const { contentType, ...parserOptions } = options;
1933
- const mergedOptions = {
1934
- ...DEFAULT_PARSER_OPTIONS,
1935
- ...parserOptions
1936
- };
1937
- const { schemas: schemaObjects, nameMapping } = getSchemas(document, { contentType });
1938
- const { parseSchema: _parseSchema, parseOperation: _parseOperation } = createSchemaParser({
1939
- document,
1940
- contentType
1941
- });
1942
- const schemas = Object.entries(schemaObjects).map(([name, schema]) => _parseSchema({
1943
- schema,
1944
- name
1945
- }, mergedOptions));
1946
- const paths = new oas.default(document).getPaths();
1947
- const operations = Object.entries(paths).flatMap(([, methods]) => Object.entries(methods).map(([, operation]) => operation ? _parseOperation(mergedOptions, operation) : null).filter((op) => op !== null));
1956
+ function preScan({ schemas, parseSchema, parserOptions, discriminator }) {
1957
+ const allNodes = [];
1958
+ const refAliasMap = /* @__PURE__ */ new Map();
1959
+ const enumNames = [];
1960
+ const discriminatorParentNodes = [];
1961
+ for (const [name, schema] of Object.entries(schemas)) {
1962
+ const node = parseSchema({
1963
+ schema,
1964
+ name
1965
+ }, parserOptions);
1966
+ allNodes.push(node);
1967
+ if (node.type === "ref" && node.name && node.name !== name) refAliasMap.set(name, node);
1968
+ if (_kubb_core.ast.narrowSchema(node, _kubb_core.ast.schemaTypes.enum) && node.name) enumNames.push(node.name);
1969
+ if (discriminator === "inherit" && (schema.oneOf ?? schema.anyOf) && schema.discriminator?.propertyName) discriminatorParentNodes.push(node);
1970
+ }
1948
1971
  return {
1949
- root: _kubb_core.ast.createInput({
1950
- schemas,
1951
- operations
1952
- }),
1953
- nameMapping
1972
+ refAliasMap,
1973
+ enumNames,
1974
+ circularNames: [..._kubb_core.ast.findCircularSchemas(allNodes)],
1975
+ discriminatorChildMap: discriminatorParentNodes.length > 0 ? buildDiscriminatorChildMap(discriminatorParentNodes) : null
1954
1976
  };
1955
1977
  }
1978
+ /**
1979
+ * Creates a lazy `InputStreamNode` from already-resolved adapter state.
1980
+ *
1981
+ * The schema and operation iterables each start a fresh parse pass on every
1982
+ * `[Symbol.asyncIterator]()` call. This lets multiple plugins consume the same
1983
+ * stream object independently without sharing a cursor or holding all nodes in memory.
1984
+ *
1985
+ * Ref aliases in `refAliasMap` are inlined during iteration: an alias entry is replaced
1986
+ * with its target's parsed node (but keeps the alias name) so plugins never receive bare `ref` nodes.
1987
+ *
1988
+ * @example
1989
+ * ```ts
1990
+ * const streamNode = createInputStream({ schemas, parseSchema, parseOperation, baseOas, parserOptions, refAliasMap, discriminatorChildMap, meta })
1991
+ * for await (const schema of streamNode.schemas) {
1992
+ * // each call to for-await restarts from the first schema
1993
+ * }
1994
+ * ```
1995
+ */
1996
+ function createInputStream({ schemas, parseSchema, parseOperation, baseOas, parserOptions, refAliasMap, discriminatorChildMap, meta }) {
1997
+ const schemasIterable = { [Symbol.asyncIterator]() {
1998
+ return (async function* () {
1999
+ for (const [name, schema] of Object.entries(schemas)) {
2000
+ const alias = refAliasMap.get(name);
2001
+ if (alias?.name && schemas[alias.name]) {
2002
+ yield {
2003
+ ...parseSchema({
2004
+ schema: schemas[alias.name],
2005
+ name: alias.name
2006
+ }, parserOptions),
2007
+ name
2008
+ };
2009
+ continue;
2010
+ }
2011
+ const parsed = parseSchema({
2012
+ schema,
2013
+ name
2014
+ }, parserOptions);
2015
+ yield discriminatorChildMap?.get(name) ? patchDiscriminatorNode(parsed, discriminatorChildMap.get(name)) : parsed;
2016
+ }
2017
+ })();
2018
+ } };
2019
+ const operationsIterable = { [Symbol.asyncIterator]() {
2020
+ return (async function* () {
2021
+ for (const methods of Object.values(baseOas.getPaths())) for (const operation of Object.values(methods)) {
2022
+ if (!operation) continue;
2023
+ const node = parseOperation(parserOptions, operation);
2024
+ if (node) yield node;
2025
+ }
2026
+ })();
2027
+ } };
2028
+ return _kubb_core.ast.createStreamInput(schemasIterable, operationsIterable, meta);
2029
+ }
1956
2030
  //#endregion
1957
2031
  //#region src/adapter.ts
1958
2032
  /**
@@ -1990,38 +2064,54 @@ const adapterOas = (0, _kubb_core.createAdapter)((options) => {
1990
2064
  };
1991
2065
  let nameMapping = /* @__PURE__ */ new Map();
1992
2066
  let parsedDocument = null;
1993
- let schemaObjects = null;
1994
- let baseOasInstance = null;
1995
- let schemaParserInstance = null;
1996
- function resolveBaseURL(document) {
1997
- const server = serverIndex !== void 0 ? document.servers?.at(serverIndex) : void 0;
1998
- return server?.url ? resolveServerUrl(server, serverVariables) : void 0;
1999
- }
2000
- async function ensureDocument(source) {
2001
- if (parsedDocument) return parsedDocument;
2067
+ const ensureDocument = once(async (source) => {
2002
2068
  const fresh = await parseFromConfig(source);
2003
2069
  if (validate) await validateDocument(fresh);
2004
2070
  parsedDocument = fresh;
2005
2071
  return fresh;
2006
- }
2007
- async function ensureSchemas(document) {
2008
- if (!schemaObjects) {
2009
- const result = getSchemas(document, { contentType });
2010
- schemaObjects = result.schemas;
2011
- nameMapping = result.nameMapping;
2012
- }
2013
- return schemaObjects;
2014
- }
2015
- function ensureBaseOas(document) {
2016
- if (!baseOasInstance) baseOasInstance = new oas.default(document);
2017
- return baseOasInstance;
2018
- }
2019
- function ensureSchemaParser(document) {
2020
- if (!schemaParserInstance) schemaParserInstance = createSchemaParser({
2021
- document,
2022
- contentType
2072
+ });
2073
+ const ensureSchemas = once(async (document) => {
2074
+ const result = getSchemas(document, { contentType });
2075
+ nameMapping = result.nameMapping;
2076
+ return result.schemas;
2077
+ });
2078
+ const ensureBaseOas = once((document) => new oas.default(document));
2079
+ const ensureSchemaParser = once((document) => createSchemaParser({
2080
+ document,
2081
+ contentType
2082
+ }));
2083
+ const ensurePreScan = once((schemas, parseSchema) => preScan({
2084
+ schemas,
2085
+ parseSchema,
2086
+ parserOptions,
2087
+ discriminator
2088
+ }));
2089
+ async function createStream(source) {
2090
+ const document = await ensureDocument(source);
2091
+ const schemas = await ensureSchemas(document);
2092
+ const { parseSchema, parseOperation } = ensureSchemaParser(document);
2093
+ const { refAliasMap, enumNames, circularNames, discriminatorChildMap } = ensurePreScan(schemas, parseSchema);
2094
+ return createInputStream({
2095
+ schemas,
2096
+ parseSchema,
2097
+ parseOperation,
2098
+ baseOas: ensureBaseOas(document),
2099
+ parserOptions,
2100
+ refAliasMap,
2101
+ discriminatorChildMap,
2102
+ meta: {
2103
+ title: document.info?.title,
2104
+ description: document.info?.description,
2105
+ version: document.info?.version,
2106
+ baseURL: resolveBaseUrl({
2107
+ document,
2108
+ serverIndex,
2109
+ serverVariables
2110
+ }),
2111
+ circularNames,
2112
+ enumNames
2113
+ }
2023
2114
  });
2024
- return schemaParserInstance;
2025
2115
  }
2026
2116
  return {
2027
2117
  name: "oas",
@@ -2061,81 +2151,20 @@ const adapterOas = (0, _kubb_core.createAdapter)((options) => {
2061
2151
  });
2062
2152
  },
2063
2153
  async parse(source) {
2064
- const document = await ensureDocument(source);
2065
- const { root: parsedRoot, nameMapping: parsedNameMapping } = parseOas(document, {
2066
- contentType,
2067
- dateType,
2068
- integerType,
2069
- unknownType,
2070
- emptySchemaType,
2071
- enumSuffix
2072
- });
2073
- const node = discriminator === "inherit" ? applyDiscriminatorInheritance(parsedRoot) : parsedRoot;
2074
- nameMapping = parsedNameMapping;
2154
+ const streamNode = await createStream(source);
2155
+ const collect = async (iter) => {
2156
+ const out = [];
2157
+ for await (const item of iter) out.push(item);
2158
+ return out;
2159
+ };
2160
+ const [schemas, operations] = await Promise.all([collect(streamNode.schemas), collect(streamNode.operations)]);
2075
2161
  return _kubb_core.ast.createInput({
2076
- ...node,
2077
- meta: {
2078
- title: document.info?.title,
2079
- description: document.info?.description,
2080
- version: document.info?.version,
2081
- baseURL: resolveBaseURL(document)
2082
- }
2162
+ schemas,
2163
+ operations,
2164
+ meta: streamNode.meta
2083
2165
  });
2084
2166
  },
2085
- async count(source) {
2086
- const document = await ensureDocument(source);
2087
- const schemas = await ensureSchemas(document);
2088
- const baseOas = ensureBaseOas(document);
2089
- const operationCount = Object.values(baseOas.getPaths()).flatMap(Object.values).filter(Boolean).length;
2090
- return {
2091
- schemas: Object.keys(schemas).length,
2092
- operations: operationCount
2093
- };
2094
- },
2095
- async stream(source) {
2096
- const document = await ensureDocument(source);
2097
- const schemas = await ensureSchemas(document);
2098
- const discriminatorChildMap = (() => {
2099
- if (discriminator !== "inherit") return null;
2100
- const { parseSchema: _preParser } = ensureSchemaParser(document);
2101
- const parentNodes = [];
2102
- for (const [name, schema] of Object.entries(schemas)) if ((schema.oneOf ?? schema.anyOf) && schema.discriminator?.propertyName) parentNodes.push(_preParser({
2103
- schema,
2104
- name
2105
- }, parserOptions));
2106
- return parentNodes.length > 0 ? buildDiscriminatorChildMap(parentNodes) : null;
2107
- })();
2108
- const schemasIterable = { [Symbol.asyncIterator]() {
2109
- return (async function* () {
2110
- const { parseSchema: _parseSchema } = ensureSchemaParser(document);
2111
- for (const [name, schema] of Object.entries(schemas)) {
2112
- const parsedNode = _parseSchema({
2113
- schema,
2114
- name
2115
- }, parserOptions);
2116
- const entry = discriminatorChildMap?.get(name);
2117
- yield entry ? patchDiscriminatorNode(parsedNode, entry) : parsedNode;
2118
- }
2119
- })();
2120
- } };
2121
- const operationsIterable = { [Symbol.asyncIterator]() {
2122
- return (async function* () {
2123
- const { parseOperation: _parseOperation } = ensureSchemaParser(document);
2124
- const paths = ensureBaseOas(document).getPaths();
2125
- for (const methods of Object.values(paths)) for (const operation of Object.values(methods)) {
2126
- if (!operation) continue;
2127
- const node = _parseOperation(parserOptions, operation);
2128
- if (node) yield node;
2129
- }
2130
- })();
2131
- } };
2132
- return _kubb_core.ast.createStreamInput(schemasIterable, operationsIterable, {
2133
- title: document.info?.title,
2134
- description: document.info?.description,
2135
- version: document.info?.version,
2136
- baseURL: resolveBaseURL(document)
2137
- });
2138
- }
2167
+ stream: createStream
2139
2168
  };
2140
2169
  });
2141
2170
  //#endregion