@kubb/adapter-oas 5.0.0-beta.62 → 5.0.0-beta.64

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- import { t as __name } from "./chunk-C0LytTxp.js";
1
+ import { t as __name } from "./rolldown-runtime-C0LytTxp.js";
2
2
  import { AdapterFactoryOptions, ast } from "@kubb/core";
3
3
 
4
4
  //#region ../../node_modules/.pnpm/@types+json-schema@7.0.15/node_modules/@types/json-schema/index.d.ts
@@ -1250,7 +1250,7 @@ type AdapterOasResolvedOptions = {
1250
1250
  enumSuffix: AdapterOasOptions['enumSuffix'];
1251
1251
  /**
1252
1252
  * Map from original `$ref` paths to their collision-resolved schema names.
1253
- * Populated after each `parse()` call.
1253
+ * Populated once the adapter resolves a spec's schemas, on the first `stream()` or `parse()`.
1254
1254
  *
1255
1255
  * @example
1256
1256
  * ```ts
@@ -1267,7 +1267,7 @@ type AdapterOas = AdapterFactoryOptions<'oas', AdapterOasOptions, AdapterOasReso
1267
1267
  //#endregion
1268
1268
  //#region src/adapter.d.ts
1269
1269
  /**
1270
- * Canonical adapter name for `@kubb/adapter-oas`. Used for driver lookups.
1270
+ * The `name` of `@kubb/adapter-oas`, used to identify this adapter in a Kubb config.
1271
1271
  */
1272
1272
  declare const adapterOasName = "oas";
1273
1273
  /**
package/dist/index.js CHANGED
@@ -1,8 +1,9 @@
1
- import "./chunk-C0LytTxp.js";
1
+ import "./rolldown-runtime-C0LytTxp.js";
2
2
  import { Diagnostics, ast, createAdapter } from "@kubb/core";
3
3
  import path from "node:path";
4
4
  import { access, readFile } from "node:fs/promises";
5
5
  import { compileErrors, validate } from "@readme/openapi-parser";
6
+ import { upgrade } from "@scalar/openapi-upgrader";
6
7
  import { parse } from "yaml";
7
8
  import { bundle } from "api-ref-bundler";
8
9
  import { macroDiscriminatorEnum, macroEnumName, macroSimplifyUnion } from "@kubb/ast/macros";
@@ -14,10 +15,9 @@ import { collect, narrowSchema } from "@kubb/ast";
14
15
  *
15
16
  * @example
16
17
  * ```ts
17
- * import { DEFAULT_PARSER_OPTIONS } from '@kubb/adapter-oas'
18
+ * import { DEFAULT_PARSER_OPTIONS, parseOas } from '@kubb/adapter-oas'
18
19
  *
19
- * const parser = createOasParser(oas)
20
- * const root = parser.parse({ ...DEFAULT_PARSER_OPTIONS, dateType: 'date' })
20
+ * const { root } = parseOas(document, { ...DEFAULT_PARSER_OPTIONS, dateType: 'date' })
21
21
  * ```
22
22
  */
23
23
  const DEFAULT_PARSER_OPTIONS = {
@@ -88,12 +88,24 @@ const structuralKeys = new Set([
88
88
  "not"
89
89
  ]);
90
90
  /**
91
+ * Formats `convertFormat` maps to a dedicated type without going through `formatMap`:
92
+ * `int64` and the date/time family. Keep this in sync with the `convertFormat`
93
+ * special-cases in `parser.ts`. `isHandledFormat` reads it so the
94
+ * `KUBB_UNSUPPORTED_FORMAT` diagnostic and the parser agree on what is handled.
95
+ */
96
+ const specialCasedFormats = new Set([
97
+ "int64",
98
+ "date-time",
99
+ "date",
100
+ "time"
101
+ ]);
102
+ /**
91
103
  * Static map from OAS `format` strings to Kubb `SchemaType` values.
92
104
  *
93
105
  * Only formats whose AST type differs from the OAS `type` field appear here.
94
- * Formats that depend on runtime options (`int64`, `date-time`, `date`, `time`) are handled separately
95
- * in the parser. `ipv4` and `ipv6` map to their own dedicated schema types. `hostname` and
96
- * `idn-hostname` map to `'url'` as the closest generic string-format type.
106
+ * Formats that depend on runtime options (`int64`, `date-time`, `date`, `time`) are handled
107
+ * separately in the parser. `ipv4` and `ipv6` map to their own dedicated schema types. `hostname`
108
+ * and `idn-hostname` map to `'url'` as the closest generic string-format type.
97
109
  *
98
110
  * @example
99
111
  * ```ts
@@ -104,18 +116,6 @@ const structuralKeys = new Set([
104
116
  * formatMap['float'] // 'number'
105
117
  * ```
106
118
  */
107
- /**
108
- * Formats `convertFormat` maps to a dedicated type without going through `formatMap`:
109
- * `int64` and the date/time family. Keep this in sync with the `convertFormat`
110
- * special-cases in `parser.ts`. `isHandledFormat` reads it so the
111
- * `KUBB_UNSUPPORTED_FORMAT` diagnostic and the parser agree on what is handled.
112
- */
113
- const specialCasedFormats = new Set([
114
- "int64",
115
- "date-time",
116
- "date",
117
- "time"
118
- ]);
119
119
  const formatMap = {
120
120
  uuid: "uuid",
121
121
  email: "email",
@@ -295,19 +295,6 @@ async function read(path) {
295
295
  //#endregion
296
296
  //#region ../../internals/utils/src/object.ts
297
297
  /**
298
- * Returns `true` when `value` is a plain (non-null, non-array) object.
299
- *
300
- * @example
301
- * ```ts
302
- * isPlainObject({}) // true
303
- * isPlainObject([]) // false
304
- * isPlainObject(null) // false
305
- * ```
306
- */
307
- function isPlainObject(value) {
308
- return typeof value === "object" && value !== null && Object.getPrototypeOf(value) === Object.prototype;
309
- }
310
- /**
311
298
  * Recursively merges `source` into `target`, combining nested plain objects.
312
299
  * Arrays and non-object values from `source` override the corresponding values in `target`.
313
300
  *
@@ -574,74 +561,14 @@ async function bundleDocument(pathOrUrl) {
574
561
  return await bundle(pathOrUrl, resolver);
575
562
  }
576
563
  //#endregion
577
- //#region src/guards.ts
578
- /**
579
- * Returns `true` when `doc` is a Swagger 2.0 document (no `openapi` key).
580
- *
581
- * @example
582
- * ```ts
583
- * if (isOpenApiV2Document(doc)) {
584
- * // doc is OpenAPIV2.Document
585
- * }
586
- * ```
587
- */
588
- function isOpenApiV2Document(doc) {
589
- return !!doc && isPlainObject(doc) && !("openapi" in doc);
590
- }
591
- /**
592
- * Returns `true` when a schema should be treated as nullable.
593
- *
594
- * Recognizes all nullable signals across OAS versions: `nullable: true` (OAS 3.0),
595
- * `x-nullable: true` (vendor extension), `type: 'null'`, and `type: ['null', ...]` (OAS 3.1).
596
- *
597
- * @example
598
- * ```ts
599
- * isNullable({ type: 'string', nullable: true }) // true
600
- * isNullable({ type: ['string', 'null'] }) // true
601
- * isNullable({ type: 'string' }) // false
602
- * ```
603
- */
604
- function isNullable(schema) {
605
- if ((schema?.nullable ?? schema?.["x-nullable"]) === true) return true;
606
- const schemaType = schema?.type;
607
- if (schemaType === "null") return true;
608
- if (Array.isArray(schemaType)) return schemaType.includes("null");
609
- return false;
610
- }
611
- /**
612
- * Returns `true` when `obj` is an OpenAPI `$ref` pointer object.
613
- *
614
- * @example
615
- * ```ts
616
- * isReference({ $ref: '#/components/schemas/Pet' }) // true
617
- * isReference({ type: 'string' }) // false
618
- * ```
619
- */
620
- function isReference(obj) {
621
- return !!obj && typeof obj === "object" && "$ref" in obj;
622
- }
623
- /**
624
- * Returns `true` when `obj` is a schema with a structured OAS 3.x `discriminator` object.
625
- *
626
- * @example
627
- * ```ts
628
- * isDiscriminator({ discriminator: { propertyName: 'type', mapping: {} } }) // true
629
- * isDiscriminator({ discriminator: 'type' }) // false (Swagger 2 string form)
630
- * ```
631
- */
632
- function isDiscriminator(obj) {
633
- const record = obj;
634
- return !!obj && !!record["discriminator"] && typeof record["discriminator"] !== "string";
635
- }
636
- //#endregion
637
564
  //#region src/factory.ts
638
565
  /**
639
- * Loads and dereferences an OpenAPI document, returning the raw `Document`.
566
+ * Loads and bundles an OpenAPI document, returning the raw `Document`.
640
567
  *
641
568
  * Accepts a file path string or an already-parsed document object. File paths and URLs are
642
569
  * bundled via `api-ref-bundler`, hoisting external file schemas into named `components.schemas`
643
570
  * entries so generators can emit named types and imports. Swagger 2.0 documents are
644
- * automatically up-converted to OpenAPI 3.0 via `swagger2openapi`.
571
+ * automatically up-converted to OpenAPI 3.0 via `@scalar/openapi-upgrader`.
645
572
  *
646
573
  * @example
647
574
  * ```ts
@@ -651,13 +578,7 @@ function isDiscriminator(obj) {
651
578
  */
652
579
  async function parseDocument(pathOrApi, { canBundle = true } = {}) {
653
580
  if (typeof pathOrApi === "string" && canBundle) return parseDocument(await bundleDocument(pathOrApi), { canBundle: false });
654
- const document = typeof pathOrApi === "string" ? parse(pathOrApi) : pathOrApi;
655
- if (isOpenApiV2Document(document)) {
656
- const { default: swagger2openapi } = await import("swagger2openapi");
657
- const { openapi } = await swagger2openapi.convertObj(document, { anchors: true });
658
- return openapi;
659
- }
660
- return document;
581
+ return upgrade(typeof pathOrApi === "string" ? parse(pathOrApi) : pathOrApi, "3.0");
661
582
  }
662
583
  /**
663
584
  * Deep-merges multiple OpenAPI documents into a single `Document`.
@@ -747,17 +668,227 @@ async function validateDocument(document, { throwOnError = false } = {}) {
747
668
  }
748
669
  }
749
670
  //#endregion
671
+ //#region src/dedupe.ts
672
+ /**
673
+ * Minimum occurrences before a shape is deduplicated.
674
+ */
675
+ const MIN_OCCURRENCES = 2;
676
+ /**
677
+ * Builds the shared `ref` replacement for a duplicate occurrence, carrying the
678
+ * usage-slot and documentation fields that are not part of the shared type.
679
+ */
680
+ function createRefNode(node, target) {
681
+ return ast.factory.createSchema({
682
+ type: "ref",
683
+ name: target.name,
684
+ ref: target.ref,
685
+ optional: node.optional,
686
+ nullish: node.nullish,
687
+ readOnly: node.readOnly,
688
+ writeOnly: node.writeOnly,
689
+ deprecated: node.deprecated,
690
+ description: node.description,
691
+ default: node.default,
692
+ example: node.example
693
+ });
694
+ }
695
+ /**
696
+ * Strips usage-slot flags from an extracted definition and applies its name.
697
+ * A standalone definition is never optional, so `optional`/`nullish` are cleared.
698
+ */
699
+ function cleanDefinition(node, name) {
700
+ return {
701
+ ...node,
702
+ name,
703
+ optional: void 0,
704
+ nullish: void 0
705
+ };
706
+ }
707
+ /**
708
+ * Returns `true` when a node is eligible for deduplication. Only enums and objects qualify, and
709
+ * object shapes that take part in a circular chain are rejected so recursive structures are not
710
+ * extracted (which would break the cycle).
711
+ */
712
+ function isCandidate(node, circularSchemas) {
713
+ if (node.type === "enum") return true;
714
+ if (node.type !== "object") return false;
715
+ if (node.name && circularSchemas.has(node.name)) return false;
716
+ return !containsCircularRef(node, { circularSchemas });
717
+ }
718
+ /**
719
+ * Produces the name for an inline shape with no existing named component, resolving
720
+ * collisions against `usedNames`. Returns `null` for an unnamed shape, which stays inline.
721
+ */
722
+ function nameFor(node, usedNames) {
723
+ const base = node.name;
724
+ if (!base) return null;
725
+ let name = base;
726
+ let counter = 2;
727
+ while (usedNames.has(name)) name = `${base}${counter++}`;
728
+ usedNames.add(name);
729
+ return name;
730
+ }
731
+ /**
732
+ * Scans a forest of schema and operation nodes and produces a {@link Plan}.
733
+ *
734
+ * A shape that occurs at least {@link MIN_OCCURRENCES} times is deduplicated: if any occurrence is
735
+ * a named top-level schema, the first one becomes the target (so other top-level duplicates and
736
+ * inline copies turn into references to it). Other top-level names with the same content are
737
+ * recorded as aliases. Otherwise a new definition is extracted using {@link nameFor}. The returned
738
+ * plan rewrites nodes against those decisions.
739
+ *
740
+ * @example
741
+ * ```ts
742
+ * const dedupePlan = plan([...schemaNodes, ...operationNodes], { circularSchemas, usedNames })
743
+ * ```
744
+ */
745
+ function plan(roots, context) {
746
+ const { circularSchemas, usedNames } = context;
747
+ const topLevelNodes = /* @__PURE__ */ new Set();
748
+ const groups = /* @__PURE__ */ new Map();
749
+ for (const root of roots) {
750
+ if (root.kind === "Schema") topLevelNodes.add(root);
751
+ for (const schemaNode of ast.collect(root, { schema: (node) => node })) {
752
+ if (!isCandidate(schemaNode, circularSchemas)) continue;
753
+ const signature = ast.signatureOf(schemaNode);
754
+ const isTopLevel = topLevelNodes.has(schemaNode) && !!schemaNode.name;
755
+ const group = groups.get(signature);
756
+ if (group) {
757
+ group.count++;
758
+ if (isTopLevel) group.topLevelNames.push(schemaNode.name);
759
+ } else groups.set(signature, {
760
+ count: 1,
761
+ representative: schemaNode,
762
+ topLevelNames: isTopLevel ? [schemaNode.name] : []
763
+ });
764
+ }
765
+ }
766
+ const bySignature = /* @__PURE__ */ new Map();
767
+ const byName = /* @__PURE__ */ new Map();
768
+ const pendingExtractions = [];
769
+ for (const [signature, group] of groups) {
770
+ if (group.count < MIN_OCCURRENCES) continue;
771
+ const [firstName, ...duplicateNames] = group.topLevelNames;
772
+ if (firstName) {
773
+ const target = {
774
+ name: firstName,
775
+ ref: `${SCHEMA_REF_PREFIX}${firstName}`
776
+ };
777
+ bySignature.set(signature, target);
778
+ for (const duplicate of duplicateNames) byName.set(duplicate, target);
779
+ continue;
780
+ }
781
+ const name = nameFor(group.representative, usedNames);
782
+ if (!name) continue;
783
+ bySignature.set(signature, {
784
+ name,
785
+ ref: `${SCHEMA_REF_PREFIX}${name}`
786
+ });
787
+ pendingExtractions.push({
788
+ name,
789
+ representative: group.representative
790
+ });
791
+ }
792
+ function rewrite(node, skipRootMatch) {
793
+ if (bySignature.size === 0 && byName.size === 0) return node;
794
+ const root = node;
795
+ return ast.transform(node, { schema(schemaNode) {
796
+ if (schemaNode.type === "ref") {
797
+ const refName = schemaNode.ref ? extractRefName(schemaNode.ref) : schemaNode.name;
798
+ const target = refName ? byName.get(refName) : void 0;
799
+ return target ? {
800
+ ...schemaNode,
801
+ name: target.name,
802
+ ref: target.ref
803
+ } : void 0;
804
+ }
805
+ if (skipRootMatch && schemaNode === root) return void 0;
806
+ const target = bySignature.get(ast.signatureOf(schemaNode));
807
+ if (!target) return void 0;
808
+ return createRefNode(schemaNode, target);
809
+ } });
810
+ }
811
+ return {
812
+ extracted: pendingExtractions.map(({ name, representative }) => cleanDefinition(rewrite(representative, true), name)),
813
+ apply(node) {
814
+ return rewrite(node, false);
815
+ },
816
+ applyTopLevel(node) {
817
+ const target = bySignature.get(ast.signatureOf(node));
818
+ if (target && target.name !== node.name) return ast.factory.createSchema({
819
+ type: "ref",
820
+ name: node.name ?? null,
821
+ ref: target.ref,
822
+ description: node.description,
823
+ deprecated: node.deprecated
824
+ });
825
+ return rewrite(node, true);
826
+ },
827
+ isAlias(name) {
828
+ return byName.has(name);
829
+ }
830
+ };
831
+ }
832
+ //#endregion
833
+ //#region src/guards.ts
834
+ /**
835
+ * Returns `true` when a schema should be treated as nullable.
836
+ *
837
+ * Recognizes all nullable signals across OAS versions: `nullable: true` (OAS 3.0),
838
+ * `x-nullable: true` (vendor extension), `type: 'null'`, and `type: ['null', ...]` (OAS 3.1).
839
+ *
840
+ * @example
841
+ * ```ts
842
+ * isNullable({ type: 'string', nullable: true }) // true
843
+ * isNullable({ type: ['string', 'null'] }) // true
844
+ * isNullable({ type: 'string' }) // false
845
+ * ```
846
+ */
847
+ function isNullable(schema) {
848
+ if ((schema?.nullable ?? schema?.["x-nullable"]) === true) return true;
849
+ const schemaType = schema?.type;
850
+ if (schemaType === "null") return true;
851
+ if (Array.isArray(schemaType)) return schemaType.includes("null");
852
+ return false;
853
+ }
854
+ /**
855
+ * Returns `true` when `obj` is an OpenAPI `$ref` pointer object.
856
+ *
857
+ * @example
858
+ * ```ts
859
+ * isReference({ $ref: '#/components/schemas/Pet' }) // true
860
+ * isReference({ type: 'string' }) // false
861
+ * ```
862
+ */
863
+ function isReference(obj) {
864
+ return !!obj && typeof obj === "object" && "$ref" in obj;
865
+ }
866
+ /**
867
+ * Returns `true` when `obj` is a schema with a structured OAS 3.x `discriminator` object.
868
+ *
869
+ * @example
870
+ * ```ts
871
+ * isDiscriminator({ discriminator: { propertyName: 'type', mapping: {} } }) // true
872
+ * isDiscriminator({ discriminator: 'type' }) // false (Swagger 2 string form)
873
+ * ```
874
+ */
875
+ function isDiscriminator(obj) {
876
+ const record = obj;
877
+ return !!obj && !!record["discriminator"] && typeof record["discriminator"] !== "string";
878
+ }
879
+ //#endregion
750
880
  //#region src/refs.ts
751
881
  const _refCache = /* @__PURE__ */ new WeakMap();
752
882
  /**
753
883
  * Resolves a local JSON pointer reference from a document.
754
884
  *
755
- * Accepts `#/...` refs. Returns `null` for empty or non-local refs.
756
- * Throws when the pointer cannot be resolved.
885
+ * Accepts `#/...` refs. Returns `null` for an empty or non-local ref. When the pointer cannot be
886
+ * resolved, reports a `refNotFound` diagnostic into the active build and returns `null`. Outside a
887
+ * build there is no sink to collect it, so it throws instead.
757
888
  *
758
889
  * @example
759
890
  * ```ts
760
- * resolveRef<SchemaObject>(document, '#/components/schemas/Pet') // SchemaObject | null
891
+ * resolveRef<SchemaObject>(document, '#/components/schemas/Pet')
761
892
  * ```
762
893
  */
763
894
  function resolveRef(document, $ref) {
@@ -814,23 +945,6 @@ function dereferenceWithRef(document, schema) {
814
945
  //#endregion
815
946
  //#region src/dialect.ts
816
947
  /**
817
- * Derives a schema's `optional`/`nullish` flags from a parent's `required` value and the
818
- * schema's own `nullable`. How "required" and "nullable" combine is OpenAPI-specific, so it
819
- * lives in the dialect.
820
- *
821
- * - Non-required + non-nullable → `optional: true`.
822
- * - Non-required + nullable → `nullish: true`.
823
- * - Required → both flags cleared.
824
- */
825
- function optionality(schema, required) {
826
- const nullable = schema.nullable ?? false;
827
- return {
828
- ...schema,
829
- optional: !required && !nullable ? true : void 0,
830
- nullish: !required && nullable ? true : void 0
831
- };
832
- }
833
- /**
834
948
  * The OpenAPI / Swagger dialect, the default used by `@kubb/adapter-oas`.
835
949
  *
836
950
  * Implements the spec-agnostic {@link ast.SchemaDialect} contract: it isolates the
@@ -856,14 +970,14 @@ const oasDialect = ast.defineDialect({
856
970
  isReference,
857
971
  isDiscriminator,
858
972
  isBinary: (schema) => schema.type === "string" && schema.contentMediaType === "application/octet-stream",
859
- resolveRef,
860
- optionality
861
- }
973
+ resolveRef
974
+ },
975
+ dedupe: { plan }
862
976
  });
863
977
  //#endregion
864
978
  //#region src/discriminator.ts
865
979
  /**
866
- * Builds a map of child schema names discriminator patch data by scanning the given
980
+ * Maps each child schema name to its discriminator patch data by scanning the given
867
981
  * top-level AST schema nodes for union schemas that carry a `discriminatorPropertyName`.
868
982
  *
869
983
  * The streaming path calls this on a small pre-parsed subset of schemas (only the
@@ -979,9 +1093,8 @@ function findDiscriminator(mapping, ref) {
979
1093
  /**
980
1094
  * MIME type fragments that mark a media type as JSON-like.
981
1095
  *
982
- * Matches the substrings used by the `oas` library's `matchesMimeType.json`, so a content type is
983
- * JSON when it contains any of these. The `+json` entry catches structured-syntax suffixes such as
984
- * `application/vnd.api+json`.
1096
+ * A content type is JSON when it contains any of these substrings. The `+json` entry catches
1097
+ * structured-syntax suffixes such as `application/vnd.api+json`.
985
1098
  */
986
1099
  const jsonMimeFragments = [
987
1100
  "application/json",
@@ -1077,8 +1190,8 @@ function getRequestContent({ document, operation, mediaType }) {
1077
1190
  return available ? [available, content[available]] : false;
1078
1191
  }
1079
1192
  /**
1080
- * Returns the primary request content type. Prefers a JSON-like media type (the last one wins,
1081
- * matching the previous behavior), then the first declared one, defaulting to `'application/json'`.
1193
+ * Returns the primary request content type. Prefers a JSON-like media type (the last one wins
1194
+ * when several are declared), then the first declared one, defaulting to `'application/json'`.
1082
1195
  */
1083
1196
  function getRequestContentType({ document, operation }) {
1084
1197
  const content = getRequestBodyContent({
@@ -1173,9 +1286,9 @@ function getSchemaType(format) {
1173
1286
  return formatMap[format] ?? null;
1174
1287
  }
1175
1288
  /**
1176
- * Whether the parser maps `format` to a dedicated type. True for any `formatMap` entry and the
1177
- * `specialCasedFormats` `convertFormat` handles directly. False means the format falls back to the
1178
- * base type, which is what `KUBB_UNSUPPORTED_FORMAT` flags. Reading both sources keeps the
1289
+ * Whether the parser maps `format` to a dedicated type. True for any `formatMap` entry, plus the
1290
+ * `specialCasedFormats` that `convertFormat` handles directly. False means the format falls back to
1291
+ * the base type, which is what `KUBB_UNSUPPORTED_FORMAT` flags. Reading both sources keeps the
1179
1292
  * diagnostic in step with the parser as `formatMap` grows.
1180
1293
  */
1181
1294
  function isHandledFormat(format) {
@@ -1193,7 +1306,7 @@ function getPrimitiveType(type) {
1193
1306
  /**
1194
1307
  * Returns all parameters for an operation, merging path-level and operation-level entries.
1195
1308
  * Operation-level parameters override path-level ones with the same `in:name` key.
1196
- * `$ref` parameters resolve via `dereferenceWithRef` for backward compatibility.
1309
+ * Each `$ref` parameter is dereferenced via `dereferenceWithRef` before merging.
1197
1310
  *
1198
1311
  * @example
1199
1312
  * ```ts
@@ -1294,7 +1407,10 @@ function getRequestSchema(document, operation, options = {}) {
1294
1407
  * ```ts
1295
1408
  * flattenSchema({ allOf: [{ description: 'A pet' }], type: 'object', properties: {} })
1296
1409
  * // { type: 'object', properties: {}, description: 'A pet' }
1410
+ * ```
1297
1411
  *
1412
+ * @example
1413
+ * ```ts
1298
1414
  * flattenSchema({ allOf: [{ $ref: '#/components/schemas/Pet' }] })
1299
1415
  * // returned unchanged, contains a $ref
1300
1416
  * ```
@@ -1988,7 +2104,7 @@ function createSchemaParser(ctx, dialect = oasDialect) {
1988
2104
  nullable: schemaNode.type === "null" ? void 0 : propNullable || void 0
1989
2105
  },
1990
2106
  required
1991
- }, dialect);
2107
+ });
1992
2108
  }) : [];
1993
2109
  const additionalProperties = schema.additionalProperties;
1994
2110
  const additionalPropertiesNode = (() => {
@@ -2302,7 +2418,7 @@ function createSchemaParser(ctx, dialect = oasDialect) {
2302
2418
  description: param["description"] ?? schema.description
2303
2419
  },
2304
2420
  required
2305
- }, dialect);
2421
+ });
2306
2422
  }
2307
2423
  /**
2308
2424
  * Reads the inline `requestBody` metadata (description / required) that OAS exposes
@@ -2355,7 +2471,7 @@ function createSchemaParser(ctx, dialect = oasDialect) {
2355
2471
  if (!schema) return [];
2356
2472
  return [ast.factory.createContent({
2357
2473
  contentType: ct,
2358
- schema: dialect.schema.optionality(parseSchema({
2474
+ schema: ast.optionality(parseSchema({
2359
2475
  schema,
2360
2476
  name: requestBodyName
2361
2477
  }, options), requestBodyMeta.required),
@@ -2558,30 +2674,15 @@ function preScan({ schemas, parseSchema, parseOperation, document, parserOptions
2558
2674
  }
2559
2675
  const circularSchemas = new Set(circularNames);
2560
2676
  const usedNames = new Set(Object.keys(schemas));
2561
- dedupePlan = ast.buildDedupePlan([...allNodes, ...operationNodes], {
2562
- isCandidate: (node) => {
2563
- if (node.type === "enum") return true;
2564
- if (node.type !== "object") return false;
2565
- if (node.name && circularSchemas.has(node.name)) return false;
2566
- return !containsCircularRef(node, { circularSchemas });
2567
- },
2568
- nameFor: (node) => {
2569
- const base = node.name;
2570
- if (!base) return null;
2571
- let name = base;
2572
- let counter = 2;
2573
- while (usedNames.has(name)) name = `${base}${counter++}`;
2574
- usedNames.add(name);
2575
- return name;
2576
- },
2577
- refFor: (name) => `${SCHEMA_REF_PREFIX}${name}`
2677
+ dedupePlan = oasDialect.dedupe.plan([...allNodes, ...operationNodes], {
2678
+ circularSchemas,
2679
+ usedNames
2578
2680
  });
2579
- for (const definition of dedupePlan.hoisted) if (definition.type === "enum" && definition.name) enumNames.push(definition.name);
2681
+ for (const definition of dedupePlan.extracted) if (definition.type === "enum" && definition.name) enumNames.push(definition.name);
2580
2682
  }
2581
- const aliasNames = dedupePlan?.aliasNames;
2582
2683
  return {
2583
2684
  refAliasMap,
2584
- enumNames: aliasNames && aliasNames.size > 0 ? enumNames.filter((name) => !aliasNames.has(name)) : enumNames,
2685
+ enumNames: dedupePlan ? enumNames.filter((name) => !dedupePlan.isAlias(name)) : enumNames,
2585
2686
  circularNames,
2586
2687
  discriminatorChildMap,
2587
2688
  dedupePlan
@@ -2606,39 +2707,29 @@ function preScan({ schemas, parseSchema, parseOperation, document, parserOptions
2606
2707
  * ```
2607
2708
  */
2608
2709
  function createInputStream({ schemas, parseSchema, parseOperation, document, parserOptions, refAliasMap, discriminatorChildMap, dedupePlan, meta }) {
2609
- const rewriteTopLevelSchema = (node) => {
2610
- if (!dedupePlan) return node;
2611
- const canonical = dedupePlan.canonicalBySignature.get(ast.signatureOf(node));
2612
- if (canonical && canonical.name !== node.name) return ast.factory.createSchema({
2613
- type: "ref",
2614
- name: node.name ?? null,
2615
- ref: canonical.ref,
2616
- description: node.description,
2617
- deprecated: node.deprecated
2618
- });
2619
- return ast.applyDedupe(node, dedupePlan, true);
2620
- };
2621
2710
  const schemasIterable = { [Symbol.asyncIterator]() {
2622
2711
  return (async function* () {
2623
- if (dedupePlan) for (const definition of dedupePlan.hoisted) yield definition;
2712
+ if (dedupePlan) for (const definition of dedupePlan.extracted) yield definition;
2624
2713
  for (const [name, schema] of Object.entries(schemas)) {
2625
- if (dedupePlan?.aliasNames.has(name)) continue;
2714
+ if (dedupePlan?.isAlias(name)) continue;
2626
2715
  const alias = refAliasMap.get(name);
2627
2716
  if (alias?.name && schemas[alias.name]) {
2628
- yield rewriteTopLevelSchema({
2717
+ const aliasNode = {
2629
2718
  ...parseSchema({
2630
2719
  schema: schemas[alias.name],
2631
2720
  name: alias.name
2632
2721
  }, parserOptions),
2633
2722
  name
2634
- });
2723
+ };
2724
+ yield dedupePlan ? dedupePlan.applyTopLevel(aliasNode) : aliasNode;
2635
2725
  continue;
2636
2726
  }
2637
2727
  const parsed = parseSchema({
2638
2728
  schema,
2639
2729
  name
2640
2730
  }, parserOptions);
2641
- yield rewriteTopLevelSchema(discriminatorChildMap?.get(name) ? patchDiscriminatorNode(parsed, discriminatorChildMap.get(name)) : parsed);
2731
+ const node = discriminatorChildMap?.get(name) ? patchDiscriminatorNode(parsed, discriminatorChildMap.get(name)) : parsed;
2732
+ yield dedupePlan ? dedupePlan.applyTopLevel(node) : node;
2642
2733
  }
2643
2734
  })();
2644
2735
  } };
@@ -2646,7 +2737,7 @@ function createInputStream({ schemas, parseSchema, parseOperation, document, par
2646
2737
  return (async function* () {
2647
2738
  for (const operation of getOperations(document)) {
2648
2739
  const node = parseOperation(parserOptions, operation);
2649
- if (node) yield dedupePlan ? ast.applyDedupe(node, dedupePlan) : node;
2740
+ if (node) yield dedupePlan ? dedupePlan.apply(node) : node;
2650
2741
  }
2651
2742
  })();
2652
2743
  } };
@@ -2660,7 +2751,7 @@ function createInputStream({ schemas, parseSchema, parseOperation, document, par
2660
2751
  //#endregion
2661
2752
  //#region src/adapter.ts
2662
2753
  /**
2663
- * Canonical adapter name for `@kubb/adapter-oas`. Used for driver lookups.
2754
+ * The `name` of `@kubb/adapter-oas`, used to identify this adapter in a Kubb config.
2664
2755
  */
2665
2756
  const adapterOasName = "oas";
2666
2757
  /**