@kubb/adapter-oas 5.0.0-beta.37 → 5.0.0-beta.39

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,12 +21,12 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
21
21
  enumerable: true
22
22
  }) : target, mod));
23
23
  //#endregion
24
- let node_fs_promises = require("node:fs/promises");
25
- let node_path = require("node:path");
26
- node_path = __toESM(node_path, 1);
27
24
  let _kubb_core = require("@kubb/core");
28
25
  let oas = require("oas");
29
26
  oas = __toESM(oas, 1);
27
+ let node_path = require("node:path");
28
+ node_path = __toESM(node_path, 1);
29
+ let node_fs_promises = require("node:fs/promises");
30
30
  let _redocly_openapi_core = require("@redocly/openapi-core");
31
31
  let oas_normalize = require("oas-normalize");
32
32
  oas_normalize = __toESM(oas_normalize, 1);
@@ -34,6 +34,138 @@ let swagger2openapi = require("swagger2openapi");
34
34
  swagger2openapi = __toESM(swagger2openapi, 1);
35
35
  let oas_types = require("oas/types");
36
36
  let oas_utils = require("oas/utils");
37
+ //#region src/constants.ts
38
+ /**
39
+ * Default parser options applied when no explicit options are provided.
40
+ *
41
+ * @example
42
+ * ```ts
43
+ * import { DEFAULT_PARSER_OPTIONS } from '@kubb/adapter-oas'
44
+ *
45
+ * const parser = createOasParser(oas)
46
+ * const root = parser.parse({ ...DEFAULT_PARSER_OPTIONS, dateType: 'date' })
47
+ * ```
48
+ */
49
+ const DEFAULT_PARSER_OPTIONS = {
50
+ dateType: "string",
51
+ integerType: "bigint",
52
+ unknownType: "any",
53
+ emptySchemaType: "any",
54
+ enumSuffix: "enum"
55
+ };
56
+ /**
57
+ * JSON-Pointer prefix for schemas declared under `components.schemas` in an OpenAPI document.
58
+ *
59
+ * Used when building or parsing `$ref` strings.
60
+ *
61
+ * @example
62
+ * ```ts
63
+ * `${SCHEMA_REF_PREFIX}Pet` // '#/components/schemas/Pet'
64
+ * ```
65
+ */
66
+ const SCHEMA_REF_PREFIX = "#/components/schemas/";
67
+ /**
68
+ * OpenAPI version string written into the stub document created during multi-spec merges.
69
+ */
70
+ const MERGE_OPENAPI_VERSION = "3.0.0";
71
+ /**
72
+ * Fallback `info.title` placed in the stub document when merging multiple API files.
73
+ */
74
+ const MERGE_DEFAULT_TITLE = "Merged API";
75
+ /**
76
+ * Fallback `info.version` placed in the stub document when merging multiple API files.
77
+ */
78
+ const MERGE_DEFAULT_VERSION = "1.0.0";
79
+ /**
80
+ * Set of JSON Schema keywords that prevent a schema fragment from being inlined during `allOf` flattening.
81
+ *
82
+ * A fragment that contains any of these keys carries structural meaning of its own and must stay as a separate
83
+ * intersection member rather than being merged into the parent.
84
+ *
85
+ * @example
86
+ * ```ts
87
+ * import { structuralKeys } from '@kubb/adapter-oas'
88
+ *
89
+ * const isStructural = Object.keys(fragment).some((key) => structuralKeys.has(key))
90
+ * // true when fragment has e.g. 'properties' or 'oneOf'
91
+ * ```
92
+ */
93
+ const structuralKeys = new Set([
94
+ "properties",
95
+ "items",
96
+ "additionalProperties",
97
+ "oneOf",
98
+ "anyOf",
99
+ "allOf",
100
+ "not"
101
+ ]);
102
+ /**
103
+ * Static map from OAS `format` strings to Kubb `SchemaType` values.
104
+ *
105
+ * Only formats whose AST type differs from the OAS `type` field appear here.
106
+ * Formats that depend on runtime options (`int64`, `date-time`, `date`, `time`) are handled separately
107
+ * in the parser. `ipv4` and `ipv6` map to their own dedicated schema types; `hostname` and
108
+ * `idn-hostname` map to `'url'` as the closest generic string-format type.
109
+ *
110
+ * @example
111
+ * ```ts
112
+ * import { formatMap } from '@kubb/adapter-oas'
113
+ *
114
+ * formatMap['uuid'] // 'uuid'
115
+ * formatMap['binary'] // 'blob'
116
+ * formatMap['float'] // 'number'
117
+ * ```
118
+ */
119
+ /**
120
+ * Formats `convertFormat` maps to a dedicated type without going through `formatMap`:
121
+ * `int64` and the date/time family. Keep this in sync with the `convertFormat`
122
+ * special-cases in `parser.ts`. `isHandledFormat` reads it so the
123
+ * `KUBB_UNSUPPORTED_FORMAT` diagnostic and the parser agree on what is handled.
124
+ */
125
+ const specialCasedFormats = new Set([
126
+ "int64",
127
+ "date-time",
128
+ "date",
129
+ "time"
130
+ ]);
131
+ const formatMap = {
132
+ uuid: "uuid",
133
+ email: "email",
134
+ "idn-email": "email",
135
+ uri: "url",
136
+ "uri-reference": "url",
137
+ url: "url",
138
+ ipv4: "ipv4",
139
+ ipv6: "ipv6",
140
+ hostname: "url",
141
+ "idn-hostname": "url",
142
+ binary: "blob",
143
+ byte: "blob",
144
+ int32: "integer",
145
+ float: "number",
146
+ double: "number"
147
+ };
148
+ /**
149
+ * Vendor extension keys that attach human-readable labels to enum values, checked in priority order.
150
+ *
151
+ * @example
152
+ * ```ts
153
+ * import { enumExtensionKeys } from '@kubb/adapter-oas'
154
+ *
155
+ * const key = enumExtensionKeys.find((k) => k in schema) // 'x-enumNames' | 'x-enum-varnames' | undefined
156
+ * ```
157
+ */
158
+ const enumExtensionKeys = ["x-enumNames", "x-enum-varnames"];
159
+ /**
160
+ * Maps `'any' | 'unknown' | 'void'` option strings to their `ScalarSchemaType` constant.
161
+ * Replaces a plain object lookup with a `Map` for explicit key membership testing via `.has()`.
162
+ */
163
+ const typeOptionMap = new Map([
164
+ ["any", _kubb_core.ast.schemaTypes.any],
165
+ ["unknown", _kubb_core.ast.schemaTypes.unknown],
166
+ ["void", _kubb_core.ast.schemaTypes.void]
167
+ ]);
168
+ //#endregion
37
169
  //#region ../../internals/utils/src/casing.ts
38
170
  /**
39
171
  * Shared implementation for camelCase and PascalCase conversion.
@@ -149,30 +281,6 @@ function mergeDeep(target, source) {
149
281
  return result;
150
282
  }
151
283
  //#endregion
152
- //#region ../../internals/utils/src/promise.ts
153
- /**
154
- * Returns a wrapper that caches the result of the first invocation and replays
155
- * it for every subsequent call, ignoring later arguments.
156
- *
157
- * Works for sync and async factories — for async, the cached value is the
158
- * promise itself, so concurrent callers share one in-flight execution and
159
- * cannot race each other.
160
- *
161
- * @example
162
- * ```ts
163
- * const loadDocument = once(async (path: string) => parse(await readFile(path)))
164
- * const a = loadDocument('./a.yaml') // parses
165
- * const b = loadDocument('./b.yaml') // returns the cached promise from the first call
166
- * ```
167
- */
168
- function once(factory) {
169
- let cache;
170
- return (...args) => {
171
- if (!cache) cache = { value: factory(...args) };
172
- return cache.value;
173
- };
174
- }
175
- //#endregion
176
284
  //#region ../../internals/utils/src/reserved.ts
177
285
  /**
178
286
  * JavaScript and Java reserved words.
@@ -423,138 +531,6 @@ var URLPath = class {
423
531
  }
424
532
  };
425
533
  //#endregion
426
- //#region src/constants.ts
427
- /**
428
- * Default parser options applied when no explicit options are provided.
429
- *
430
- * @example
431
- * ```ts
432
- * import { DEFAULT_PARSER_OPTIONS } from '@kubb/adapter-oas'
433
- *
434
- * const parser = createOasParser(oas)
435
- * const root = parser.parse({ ...DEFAULT_PARSER_OPTIONS, dateType: 'date' })
436
- * ```
437
- */
438
- const DEFAULT_PARSER_OPTIONS = {
439
- dateType: "string",
440
- integerType: "bigint",
441
- unknownType: "any",
442
- emptySchemaType: "any",
443
- enumSuffix: "enum"
444
- };
445
- /**
446
- * JSON-Pointer prefix for schemas declared under `components.schemas` in an OpenAPI document.
447
- *
448
- * Used when building or parsing `$ref` strings.
449
- *
450
- * @example
451
- * ```ts
452
- * `${SCHEMA_REF_PREFIX}Pet` // '#/components/schemas/Pet'
453
- * ```
454
- */
455
- const SCHEMA_REF_PREFIX = "#/components/schemas/";
456
- /**
457
- * OpenAPI version string written into the stub document created during multi-spec merges.
458
- */
459
- const MERGE_OPENAPI_VERSION = "3.0.0";
460
- /**
461
- * Fallback `info.title` placed in the stub document when merging multiple API files.
462
- */
463
- const MERGE_DEFAULT_TITLE = "Merged API";
464
- /**
465
- * Fallback `info.version` placed in the stub document when merging multiple API files.
466
- */
467
- const MERGE_DEFAULT_VERSION = "1.0.0";
468
- /**
469
- * Set of JSON Schema keywords that prevent a schema fragment from being inlined during `allOf` flattening.
470
- *
471
- * A fragment that contains any of these keys carries structural meaning of its own and must stay as a separate
472
- * intersection member rather than being merged into the parent.
473
- *
474
- * @example
475
- * ```ts
476
- * import { structuralKeys } from '@kubb/adapter-oas'
477
- *
478
- * const isStructural = Object.keys(fragment).some((key) => structuralKeys.has(key))
479
- * // true when fragment has e.g. 'properties' or 'oneOf'
480
- * ```
481
- */
482
- const structuralKeys = new Set([
483
- "properties",
484
- "items",
485
- "additionalProperties",
486
- "oneOf",
487
- "anyOf",
488
- "allOf",
489
- "not"
490
- ]);
491
- /**
492
- * Static map from OAS `format` strings to Kubb `SchemaType` values.
493
- *
494
- * Only formats whose AST type differs from the OAS `type` field appear here.
495
- * Formats that depend on runtime options (`int64`, `date-time`, `date`, `time`) are handled separately
496
- * in the parser. `ipv4` and `ipv6` map to their own dedicated schema types; `hostname` and
497
- * `idn-hostname` map to `'url'` as the closest generic string-format type.
498
- *
499
- * @example
500
- * ```ts
501
- * import { formatMap } from '@kubb/adapter-oas'
502
- *
503
- * formatMap['uuid'] // 'uuid'
504
- * formatMap['binary'] // 'blob'
505
- * formatMap['float'] // 'number'
506
- * ```
507
- */
508
- /**
509
- * Formats `convertFormat` maps to a dedicated type without going through `formatMap`:
510
- * `int64` and the date/time family. Keep this in sync with the `convertFormat`
511
- * special-cases in `parser.ts`. `isHandledFormat` reads it so the
512
- * `KUBB_UNSUPPORTED_FORMAT` diagnostic and the parser agree on what is handled.
513
- */
514
- const specialCasedFormats = new Set([
515
- "int64",
516
- "date-time",
517
- "date",
518
- "time"
519
- ]);
520
- const formatMap = {
521
- uuid: "uuid",
522
- email: "email",
523
- "idn-email": "email",
524
- uri: "url",
525
- "uri-reference": "url",
526
- url: "url",
527
- ipv4: "ipv4",
528
- ipv6: "ipv6",
529
- hostname: "url",
530
- "idn-hostname": "url",
531
- binary: "blob",
532
- byte: "blob",
533
- int32: "integer",
534
- float: "number",
535
- double: "number"
536
- };
537
- /**
538
- * Vendor extension keys that attach human-readable labels to enum values, checked in priority order.
539
- *
540
- * @example
541
- * ```ts
542
- * import { enumExtensionKeys } from '@kubb/adapter-oas'
543
- *
544
- * const key = enumExtensionKeys.find((k) => k in schema) // 'x-enumNames' | 'x-enum-varnames' | undefined
545
- * ```
546
- */
547
- const enumExtensionKeys = ["x-enumNames", "x-enum-varnames"];
548
- /**
549
- * Maps `'any' | 'unknown' | 'void'` option strings to their `ScalarSchemaType` constant.
550
- * Replaces a plain object lookup with a `Map` for explicit key membership testing via `.has()`.
551
- */
552
- const typeOptionMap = new Map([
553
- ["any", _kubb_core.ast.schemaTypes.any],
554
- ["unknown", _kubb_core.ast.schemaTypes.unknown],
555
- ["void", _kubb_core.ast.schemaTypes.void]
556
- ]);
557
- //#endregion
558
534
  //#region src/guards.ts
559
535
  /**
560
536
  * Returns `true` when `doc` is a Swagger 2.0 document (no `openapi` key).
@@ -664,8 +640,8 @@ async function mergeDocuments(pathOrApi) {
664
640
  enablePaths: false,
665
641
  canBundle: false
666
642
  })));
667
- if (documents.length === 0) throw new _kubb_core.DiagnosticError({
668
- code: _kubb_core.diagnosticCode.inputRequired,
643
+ if (documents.length === 0) throw new _kubb_core.Diagnostics.Error({
644
+ code: _kubb_core.Diagnostics.code.inputRequired,
669
645
  severity: "error",
670
646
  message: "No OAS documents were provided for merging.",
671
647
  help: "Pass at least one path or document to `input.path`.",
@@ -714,8 +690,8 @@ async function parseFromConfig(source) {
714
690
  */
715
691
  async function assertInputExists(input) {
716
692
  if (new URLPath(input).isURL) return;
717
- if (!await exists(input)) throw new _kubb_core.DiagnosticError({
718
- code: _kubb_core.diagnosticCode.inputNotFound,
693
+ if (!await exists(input)) throw new _kubb_core.Diagnostics.Error({
694
+ code: _kubb_core.Diagnostics.code.inputNotFound,
719
695
  severity: "error",
720
696
  message: `Cannot read the file set in \`input.path\` (or via \`kubb generate PATH\`): ${input}`,
721
697
  help: "Check that the path exists and is readable, then set it in `input.path` or pass it as `kubb generate PATH`.",
@@ -769,7 +745,7 @@ function resolveRef(document, $ref) {
769
745
  const current = $ref.split("/").filter(Boolean).reduce((obj, key) => obj?.[key], document);
770
746
  if (!current) {
771
747
  const diagnostic = {
772
- code: _kubb_core.diagnosticCode.refNotFound,
748
+ code: _kubb_core.Diagnostics.code.refNotFound,
773
749
  severity: "error",
774
750
  message: `Could not find a definition for ${origRef}.`,
775
751
  help: "Add the schema under `components.schemas`, or fix the `$ref`. Run `kubb validate` to check the spec.",
@@ -779,7 +755,7 @@ function resolveRef(document, $ref) {
779
755
  ref: origRef
780
756
  }
781
757
  };
782
- _kubb_core.Diagnostics.report(diagnostic);
758
+ if (!_kubb_core.Diagnostics.report(diagnostic)) throw new _kubb_core.Diagnostics.Error(diagnostic);
783
759
  return null;
784
760
  }
785
761
  docCache.set($ref, current);
@@ -856,8 +832,8 @@ function resolveServerUrl(server, overrides) {
856
832
  for (const [key, variable] of Object.entries(server.variables)) {
857
833
  const value = overrides?.[key] ?? (variable.default != null ? String(variable.default) : void 0);
858
834
  if (value === void 0) continue;
859
- if (variable.enum?.length && !variable.enum.some((e) => String(e) === value)) throw new _kubb_core.DiagnosticError({
860
- code: _kubb_core.diagnosticCode.invalidServerVariable,
835
+ if (variable.enum?.length && !variable.enum.some((e) => String(e) === value)) throw new _kubb_core.Diagnostics.Error({
836
+ code: _kubb_core.Diagnostics.code.invalidServerVariable,
861
837
  severity: "error",
862
838
  message: `Invalid server variable value '${value}' for '${key}' when resolving ${server.url}. Valid values are: ${variable.enum.join(", ")}.`,
863
839
  help: `Use one of the allowed enum values, or drop the enum on the '${key}' server variable.`,
@@ -2188,11 +2164,18 @@ function patchDiscriminatorNode(node, entry) {
2188
2164
  * no-op outside one, and repeats are deduped by the build.
2189
2165
  */
2190
2166
  function reportSchemaDiagnostics({ node, name }) {
2191
- visit(node, `#/components/schemas/${name}`);
2167
+ visit(node, `#/components/schemas/${escapePointerToken(name)}`);
2168
+ }
2169
+ /**
2170
+ * Escapes a single JSON pointer reference token per RFC 6901 (`~` → `~0`, `/` → `~1`), so a
2171
+ * property name with those characters maps to a distinct pointer instead of colliding in the dedupe.
2172
+ */
2173
+ function escapePointerToken(token) {
2174
+ return token.replace(/~/g, "~0").replace(/\//g, "~1");
2192
2175
  }
2193
2176
  function visit(node, pointer) {
2194
2177
  if (node.deprecated) _kubb_core.Diagnostics.report({
2195
- code: _kubb_core.diagnosticCode.deprecated,
2178
+ code: _kubb_core.Diagnostics.code.deprecated,
2196
2179
  severity: "info",
2197
2180
  message: "This schema is marked as deprecated.",
2198
2181
  location: {
@@ -2201,7 +2184,7 @@ function visit(node, pointer) {
2201
2184
  }
2202
2185
  });
2203
2186
  if (typeof node.format === "string" && !isHandledFormat(node.format)) _kubb_core.Diagnostics.report({
2204
- code: _kubb_core.diagnosticCode.unsupportedFormat,
2187
+ code: _kubb_core.Diagnostics.code.unsupportedFormat,
2205
2188
  severity: "warning",
2206
2189
  message: `Kubb does not map the format "${node.format}" to a specific type, so it falls back to the base type.`,
2207
2190
  help: `Use a format Kubb supports, or handle "${node.format}" with a custom parser or plugin.`,
@@ -2211,15 +2194,19 @@ function visit(node, pointer) {
2211
2194
  }
2212
2195
  });
2213
2196
  if (node.type === "object") {
2214
- for (const property of node.properties) visit(property.schema, `${pointer}/properties/${property.name}`);
2197
+ for (const property of node.properties) visit(property.schema, `${pointer}/properties/${escapePointerToken(property.name)}`);
2215
2198
  if (node.additionalProperties && typeof node.additionalProperties === "object") visit(node.additionalProperties, `${pointer}/additionalProperties`);
2216
2199
  return;
2217
2200
  }
2218
- if (node.type === "array" || node.type === "tuple") {
2201
+ if (node.type === "array") {
2219
2202
  for (const item of node.items ?? []) visit(item, `${pointer}/items`);
2220
2203
  return;
2221
2204
  }
2222
- if (node.type === "union" || node.type === "intersection") for (const member of node.members ?? []) visit(member, pointer);
2205
+ if (node.type === "tuple") {
2206
+ for (const [index, item] of (node.items ?? []).entries()) visit(item, `${pointer}/items/${index}`);
2207
+ return;
2208
+ }
2209
+ if (node.type === "union" || node.type === "intersection") for (const [index, member] of (node.members ?? []).entries()) visit(member, `${pointer}/members/${index}`);
2223
2210
  }
2224
2211
  //#endregion
2225
2212
  //#region src/stream.ts
@@ -2449,37 +2436,72 @@ const adapterOas = (0, _kubb_core.createAdapter)((options) => {
2449
2436
  };
2450
2437
  let nameMapping = /* @__PURE__ */ new Map();
2451
2438
  let parsedDocument = null;
2452
- const ensureDocument = once(async (source) => {
2453
- const fresh = await parseFromConfig(source);
2454
- if (validate) await validateDocument(fresh);
2455
- parsedDocument = fresh;
2456
- return fresh;
2457
- });
2458
- const ensureSchemas = once(async (document) => {
2459
- const result = getSchemas(document, { contentType });
2460
- nameMapping = result.nameMapping;
2461
- return result.schemas;
2462
- });
2463
- const ensureBaseOas = once((document) => new oas.default(document));
2464
- const ensureSchemaParser = once((document) => createSchemaParser({
2465
- document,
2466
- contentType
2467
- }));
2468
- const ensurePreScan = once((schemas, parseSchema, parseOperation, baseOas) => preScan({
2469
- schemas,
2470
- parseSchema,
2471
- parseOperation,
2472
- baseOas,
2473
- parserOptions,
2474
- discriminator,
2475
- dedupe
2476
- }));
2439
+ const documentCache = /* @__PURE__ */ new WeakMap();
2440
+ const schemasCache = /* @__PURE__ */ new WeakMap();
2441
+ const baseOasCache = /* @__PURE__ */ new WeakMap();
2442
+ const schemaParserCache = /* @__PURE__ */ new WeakMap();
2443
+ const preScanCache = /* @__PURE__ */ new WeakMap();
2444
+ function ensureDocument(source) {
2445
+ const cached = documentCache.get(source);
2446
+ if (cached) return cached;
2447
+ const promise = (async () => {
2448
+ const fresh = await parseFromConfig(source);
2449
+ if (validate) await validateDocument(fresh);
2450
+ parsedDocument = fresh;
2451
+ return fresh;
2452
+ })();
2453
+ documentCache.set(source, promise);
2454
+ return promise;
2455
+ }
2456
+ function ensureSchemas(document) {
2457
+ const cached = schemasCache.get(document);
2458
+ if (cached) return cached;
2459
+ const promise = Promise.resolve().then(() => {
2460
+ const result = getSchemas(document, { contentType });
2461
+ nameMapping = result.nameMapping;
2462
+ return result.schemas;
2463
+ });
2464
+ schemasCache.set(document, promise);
2465
+ return promise;
2466
+ }
2467
+ function ensureBaseOas(document) {
2468
+ const cached = baseOasCache.get(document);
2469
+ if (cached) return cached;
2470
+ const baseOas = new oas.default(document);
2471
+ baseOasCache.set(document, baseOas);
2472
+ return baseOas;
2473
+ }
2474
+ function ensureSchemaParser(document) {
2475
+ const cached = schemaParserCache.get(document);
2476
+ if (cached) return cached;
2477
+ const parser = createSchemaParser({
2478
+ document,
2479
+ contentType
2480
+ });
2481
+ schemaParserCache.set(document, parser);
2482
+ return parser;
2483
+ }
2484
+ function ensurePreScan(document, schemas, parseSchema, parseOperation, baseOas) {
2485
+ const cached = preScanCache.get(document);
2486
+ if (cached) return cached;
2487
+ const result = preScan({
2488
+ schemas,
2489
+ parseSchema,
2490
+ parseOperation,
2491
+ baseOas,
2492
+ parserOptions,
2493
+ discriminator,
2494
+ dedupe
2495
+ });
2496
+ preScanCache.set(document, result);
2497
+ return result;
2498
+ }
2477
2499
  async function createStream(source) {
2478
2500
  const document = await ensureDocument(source);
2479
2501
  const schemas = await ensureSchemas(document);
2480
2502
  const { parseSchema, parseOperation } = ensureSchemaParser(document);
2481
2503
  const baseOas = ensureBaseOas(document);
2482
- const { refAliasMap, enumNames, circularNames, discriminatorChildMap, dedupePlan } = ensurePreScan(schemas, parseSchema, parseOperation, baseOas);
2504
+ const { refAliasMap, enumNames, circularNames, discriminatorChildMap, dedupePlan } = ensurePreScan(document, schemas, parseSchema, parseOperation, baseOas);
2483
2505
  return createInputStream({
2484
2506
  schemas,
2485
2507
  parseSchema,