@kubb/adapter-oas 5.0.0-beta.68 → 5.0.0-beta.69

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
@@ -612,168 +612,6 @@ async function validateDocument(document, { throwOnError = false } = {}) {
612
612
  }
613
613
  }
614
614
  //#endregion
615
- //#region src/dedupe.ts
616
- /**
617
- * Minimum occurrences before a shape is deduplicated.
618
- */
619
- const MIN_OCCURRENCES = 2;
620
- /**
621
- * Builds the shared `ref` replacement for a duplicate occurrence, carrying the
622
- * usage-slot and documentation fields that are not part of the shared type.
623
- */
624
- function createRefNode(node, target) {
625
- return _kubb_core.ast.factory.createSchema({
626
- type: "ref",
627
- name: target.name,
628
- ref: target.ref,
629
- optional: node.optional,
630
- nullish: node.nullish,
631
- readOnly: node.readOnly,
632
- writeOnly: node.writeOnly,
633
- deprecated: node.deprecated,
634
- description: node.description,
635
- default: node.default,
636
- examples: node.examples
637
- });
638
- }
639
- /**
640
- * Strips usage-slot flags from an extracted definition and applies its name.
641
- * A standalone definition is never optional, so `optional`/`nullish` are cleared.
642
- */
643
- function cleanDefinition(node, name) {
644
- return {
645
- ...node,
646
- name,
647
- optional: void 0,
648
- nullish: void 0
649
- };
650
- }
651
- /**
652
- * Returns `true` when a node is eligible for deduplication. Only enums and objects qualify, and
653
- * object shapes that take part in a circular chain are rejected so recursive structures are not
654
- * extracted (which would break the cycle).
655
- */
656
- function isCandidate(node, circularSchemas) {
657
- if (node.type === "enum") return true;
658
- if (node.type !== "object") return false;
659
- if (node.name && circularSchemas.has(node.name)) return false;
660
- return !(0, _kubb_ast_utils.containsCircularRef)(node, { circularSchemas });
661
- }
662
- /**
663
- * Produces the name for an inline shape with no existing named component, resolving
664
- * collisions against `usedNames`. Returns `null` for an unnamed shape, which stays inline.
665
- */
666
- function nameFor(node, usedNames) {
667
- const base = node.name;
668
- if (!base) return null;
669
- let name = base;
670
- let counter = 2;
671
- while (usedNames.has(name)) name = `${base}${counter++}`;
672
- usedNames.add(name);
673
- return name;
674
- }
675
- /**
676
- * Scans a forest of schema and operation nodes and produces a {@link Plan}.
677
- *
678
- * A shape that occurs at least {@link MIN_OCCURRENCES} times is deduplicated: if any occurrence is
679
- * a named top-level schema, the first one becomes the target (so other top-level duplicates and
680
- * inline copies turn into references to it). Other top-level names with the same content are
681
- * recorded as aliases. Otherwise a new definition is extracted using {@link nameFor}. The returned
682
- * plan rewrites nodes against those decisions.
683
- *
684
- * @example
685
- * ```ts
686
- * const dedupePlan = plan([...schemaNodes, ...operationNodes], { circularSchemas, usedNames })
687
- * ```
688
- */
689
- function plan(roots, context) {
690
- const { circularSchemas, usedNames } = context;
691
- const topLevelNodes = /* @__PURE__ */ new Set();
692
- const groups = /* @__PURE__ */ new Map();
693
- for (const root of roots) {
694
- if (root.kind === "Schema") topLevelNodes.add(root);
695
- for (const schemaNode of _kubb_core.ast.collect(root, { schema: (node) => node })) {
696
- if (!isCandidate(schemaNode, circularSchemas)) continue;
697
- const signature = _kubb_core.ast.signatureOf(schemaNode);
698
- const isTopLevel = topLevelNodes.has(schemaNode) && !!schemaNode.name;
699
- const group = groups.get(signature);
700
- if (group) {
701
- group.count++;
702
- if (isTopLevel) group.topLevelNames.push(schemaNode.name);
703
- } else groups.set(signature, {
704
- count: 1,
705
- representative: schemaNode,
706
- topLevelNames: isTopLevel ? [schemaNode.name] : []
707
- });
708
- }
709
- }
710
- const bySignature = /* @__PURE__ */ new Map();
711
- const byName = /* @__PURE__ */ new Map();
712
- const pendingExtractions = [];
713
- for (const [signature, group] of groups) {
714
- if (group.count < MIN_OCCURRENCES) continue;
715
- const [firstName, ...duplicateNames] = group.topLevelNames;
716
- if (firstName) {
717
- const target = {
718
- name: firstName,
719
- ref: `${SCHEMA_REF_PREFIX}${firstName}`
720
- };
721
- bySignature.set(signature, target);
722
- for (const duplicate of duplicateNames) byName.set(duplicate, target);
723
- continue;
724
- }
725
- const name = nameFor(group.representative, usedNames);
726
- if (!name) continue;
727
- bySignature.set(signature, {
728
- name,
729
- ref: `${SCHEMA_REF_PREFIX}${name}`
730
- });
731
- pendingExtractions.push({
732
- name,
733
- representative: group.representative
734
- });
735
- }
736
- function rewrite(node, skipRootMatch) {
737
- if (bySignature.size === 0 && byName.size === 0) return node;
738
- const root = node;
739
- return _kubb_core.ast.transform(node, { schema(schemaNode) {
740
- if (schemaNode.type === "ref") {
741
- const refName = schemaNode.ref ? (0, _kubb_ast_utils.extractRefName)(schemaNode.ref) : schemaNode.name;
742
- const target = refName ? byName.get(refName) : void 0;
743
- return target ? {
744
- ...schemaNode,
745
- name: target.name,
746
- ref: target.ref
747
- } : void 0;
748
- }
749
- if (skipRootMatch && schemaNode === root) return void 0;
750
- const target = bySignature.get(_kubb_core.ast.signatureOf(schemaNode));
751
- if (!target) return void 0;
752
- return createRefNode(schemaNode, target);
753
- } });
754
- }
755
- return {
756
- extracted: pendingExtractions.map(({ name, representative }) => cleanDefinition(rewrite(representative, true), name)),
757
- apply(node) {
758
- return rewrite(node, false);
759
- },
760
- applyTopLevel(node) {
761
- const target = bySignature.get(_kubb_core.ast.signatureOf(node));
762
- if (target && target.name !== node.name) return _kubb_core.ast.factory.createSchema({
763
- type: "ref",
764
- name: node.name ?? null,
765
- ref: target.ref,
766
- description: node.description,
767
- deprecated: node.deprecated
768
- });
769
- return rewrite(node, true);
770
- },
771
- isAlias(name) {
772
- return byName.has(name);
773
- }
774
- };
775
- }
776
- //#endregion
777
615
  //#region src/guards.ts
778
616
  /**
779
617
  * Returns `true` when a schema should be treated as nullable.
@@ -915,8 +753,7 @@ const oasDialect = _kubb_core.ast.defineDialect({
915
753
  isDiscriminator,
916
754
  isBinary: (schema) => schema.type === "string" && schema.contentMediaType === "application/octet-stream",
917
755
  resolveRef
918
- },
919
- dedupe: { plan }
756
+ }
920
757
  });
921
758
  //#endregion
922
759
  //#region src/discriminator.ts
@@ -2498,6 +2335,53 @@ function createSchemaParser(ctx, dialect = oasDialect) {
2498
2335
  };
2499
2336
  }
2500
2337
  //#endregion
2338
+ //#region src/promoteEnums.ts
2339
+ /**
2340
+ * Collects inline enums to lift to the top level, keyed by the name the parser derived for them
2341
+ * (e.g. `PetStatusEnum`). An enum already defined as a top-level component is left as-is, and a
2342
+ * name that recurs maps to the first definition so each name yields one shared type.
2343
+ */
2344
+ function collectInlineEnums(roots, topLevelNames) {
2345
+ const promoted = /* @__PURE__ */ new Map();
2346
+ for (const root of roots) {
2347
+ const isSchemaRoot = root.kind === "Schema";
2348
+ for (const node of _kubb_core.ast.collect(root, { schema: (schemaNode) => schemaNode })) {
2349
+ if (node.type !== "enum" || !node.name) continue;
2350
+ if (isSchemaRoot && node === root) continue;
2351
+ if (topLevelNames.has(node.name)) continue;
2352
+ if (!promoted.has(node.name)) promoted.set(node.name, {
2353
+ ...node,
2354
+ optional: void 0,
2355
+ nullish: void 0
2356
+ });
2357
+ }
2358
+ }
2359
+ return promoted;
2360
+ }
2361
+ /**
2362
+ * Replaces every promoted inline enum in `node` with a `ref` to its lifted definition, keeping the
2363
+ * occurrence's usage-slot and documentation fields.
2364
+ */
2365
+ function refPromotedEnums(node, promoted) {
2366
+ if (promoted.size === 0) return node;
2367
+ return _kubb_core.ast.transform(node, { schema(schemaNode) {
2368
+ if (schemaNode.type !== "enum" || !schemaNode.name || !promoted.has(schemaNode.name)) return void 0;
2369
+ return _kubb_core.ast.factory.createSchema({
2370
+ type: "ref",
2371
+ name: schemaNode.name,
2372
+ ref: `${SCHEMA_REF_PREFIX}${schemaNode.name}`,
2373
+ optional: schemaNode.optional,
2374
+ nullish: schemaNode.nullish,
2375
+ readOnly: schemaNode.readOnly,
2376
+ writeOnly: schemaNode.writeOnly,
2377
+ deprecated: schemaNode.deprecated,
2378
+ description: schemaNode.description,
2379
+ default: schemaNode.default,
2380
+ examples: schemaNode.examples
2381
+ });
2382
+ } });
2383
+ }
2384
+ //#endregion
2501
2385
  //#region src/schemaDiagnostics.ts
2502
2386
  /**
2503
2387
  * Reports the advisory diagnostics (`KUBB_UNSUPPORTED_FORMAT`, `KUBB_DEPRECATED`) for one
@@ -2555,20 +2439,21 @@ function visit(node, pointer) {
2555
2439
  //#endregion
2556
2440
  //#region src/stream.ts
2557
2441
  /**
2558
- * Reads the server URL from the document's `servers` array at `serverIndex`,
2559
- * interpolating any `serverVariables` into the URL template.
2442
+ * Reads the server URL from the document's `servers` array at `server.index`,
2443
+ * interpolating any `server.variables` into the URL template.
2560
2444
  *
2561
- * Returns `null` when `serverIndex` is omitted or out of range.
2445
+ * Returns `null` when `server.index` is omitted or out of range.
2562
2446
  *
2563
2447
  * @example Resolve the first server
2564
- * `resolveBaseUrl({ document, serverIndex: 0 })`
2448
+ * `resolveBaseUrl({ document, server: { index: 0 } })`
2565
2449
  *
2566
2450
  * @example Override a path variable
2567
- * `resolveBaseUrl({ document, serverIndex: 0, serverVariables: { version: 'v2' } })`
2451
+ * `resolveBaseUrl({ document, server: { index: 0, variables: { version: 'v2' } } })`
2568
2452
  */
2569
- function resolveBaseUrl({ document, serverIndex, serverVariables }) {
2570
- const server = serverIndex !== void 0 ? document.servers?.at(serverIndex) : void 0;
2571
- return server?.url ? resolveServerUrl(server, serverVariables) : null;
2453
+ function resolveBaseUrl({ document, server }) {
2454
+ const index = server?.index;
2455
+ const entry = index !== void 0 ? document.servers?.at(index) : void 0;
2456
+ return entry?.url ? resolveServerUrl(entry, server?.variables) : null;
2572
2457
  }
2573
2458
  /**
2574
2459
  * Parses every schema once to build the lookup structures that streaming needs upfront.
@@ -2591,11 +2476,11 @@ function resolveBaseUrl({ document, serverIndex, serverVariables }) {
2591
2476
  * schemas,
2592
2477
  * parseSchema,
2593
2478
  * parserOptions,
2594
- * discriminator: 'strict',
2479
+ * discriminator: 'preserve',
2595
2480
  * })
2596
2481
  * ```
2597
2482
  */
2598
- function preScan({ schemas, parseSchema, parseOperation, document, parserOptions, discriminator, dedupe }) {
2483
+ function preScan({ schemas, parseSchema, parseOperation, document, parserOptions, discriminator, enums = "inline" }) {
2599
2484
  const allNodes = [];
2600
2485
  const refAliasMap = /* @__PURE__ */ new Map();
2601
2486
  const enumNames = [];
@@ -2612,31 +2497,26 @@ function preScan({ schemas, parseSchema, parseOperation, document, parserOptions
2612
2497
  });
2613
2498
  if (node.type === "ref" && node.name && node.name !== name) refAliasMap.set(name, node);
2614
2499
  if (_kubb_core.ast.narrowSchema(node, _kubb_core.ast.schemaTypes.enum) && node.name) enumNames.push(node.name);
2615
- if (discriminator === "inherit" && (schema.oneOf ?? schema.anyOf) && schema.discriminator?.propertyName) discriminatorParentNodes.push(node);
2500
+ if (discriminator === "propagate" && (schema.oneOf ?? schema.anyOf) && schema.discriminator?.propertyName) discriminatorParentNodes.push(node);
2616
2501
  }
2617
2502
  const circularNames = [...(0, _kubb_ast_utils.findCircularSchemas)(allNodes)];
2618
2503
  const discriminatorChildMap = discriminatorParentNodes.length > 0 ? buildDiscriminatorChildMap(discriminatorParentNodes) : null;
2619
- let dedupePlan = null;
2620
- if (dedupe) {
2504
+ let promotedEnums = null;
2505
+ if (enums === "root" && document && parseOperation) {
2621
2506
  const operationNodes = [];
2622
2507
  for (const operation of getOperations(document)) {
2623
2508
  const operationNode = parseOperation(parserOptions, operation);
2624
2509
  if (operationNode) operationNodes.push(operationNode);
2625
2510
  }
2626
- const circularSchemas = new Set(circularNames);
2627
- const usedNames = new Set(Object.keys(schemas));
2628
- dedupePlan = oasDialect.dedupe.plan([...allNodes, ...operationNodes], {
2629
- circularSchemas,
2630
- usedNames
2631
- });
2632
- for (const definition of dedupePlan.extracted) if (definition.type === "enum" && definition.name) enumNames.push(definition.name);
2511
+ promotedEnums = collectInlineEnums([...allNodes, ...operationNodes], new Set(Object.keys(schemas)));
2512
+ for (const name of promotedEnums.keys()) enumNames.push(name);
2633
2513
  }
2634
2514
  return {
2635
2515
  refAliasMap,
2636
- enumNames: dedupePlan ? enumNames.filter((name) => !dedupePlan.isAlias(name)) : enumNames,
2516
+ enumNames,
2637
2517
  circularNames,
2638
2518
  discriminatorChildMap,
2639
- dedupePlan
2519
+ promotedEnums
2640
2520
  };
2641
2521
  }
2642
2522
  /**
@@ -2657,12 +2537,11 @@ function preScan({ schemas, parseSchema, parseOperation, document, parserOptions
2657
2537
  * }
2658
2538
  * ```
2659
2539
  */
2660
- function createInputStream({ schemas, parseSchema, parseOperation, document, parserOptions, refAliasMap, discriminatorChildMap, dedupePlan, meta }) {
2540
+ function createInputStream({ schemas, parseSchema, parseOperation, document, parserOptions, refAliasMap, discriminatorChildMap, promotedEnums, meta }) {
2661
2541
  const schemasIterable = { [Symbol.asyncIterator]() {
2662
2542
  return (async function* () {
2663
- if (dedupePlan) for (const definition of dedupePlan.extracted) yield definition;
2543
+ if (promotedEnums) for (const definition of promotedEnums.values()) yield definition;
2664
2544
  for (const [name, schema] of Object.entries(schemas)) {
2665
- if (dedupePlan?.isAlias(name)) continue;
2666
2545
  const alias = refAliasMap.get(name);
2667
2546
  if (alias?.name && schemas[alias.name]) {
2668
2547
  const aliasNode = {
@@ -2672,7 +2551,7 @@ function createInputStream({ schemas, parseSchema, parseOperation, document, par
2672
2551
  }, parserOptions),
2673
2552
  name
2674
2553
  };
2675
- yield dedupePlan ? dedupePlan.applyTopLevel(aliasNode) : aliasNode;
2554
+ yield promotedEnums ? refPromotedEnums(aliasNode, promotedEnums) : aliasNode;
2676
2555
  continue;
2677
2556
  }
2678
2557
  const parsed = parseSchema({
@@ -2680,7 +2559,7 @@ function createInputStream({ schemas, parseSchema, parseOperation, document, par
2680
2559
  name
2681
2560
  }, parserOptions);
2682
2561
  const node = discriminatorChildMap?.get(name) ? patchDiscriminatorNode(parsed, discriminatorChildMap.get(name)) : parsed;
2683
- yield dedupePlan ? dedupePlan.applyTopLevel(node) : node;
2562
+ yield promotedEnums ? refPromotedEnums(node, promotedEnums) : node;
2684
2563
  }
2685
2564
  })();
2686
2565
  } };
@@ -2688,7 +2567,7 @@ function createInputStream({ schemas, parseSchema, parseOperation, document, par
2688
2567
  return (async function* () {
2689
2568
  for (const operation of getOperations(document)) {
2690
2569
  const node = parseOperation(parserOptions, operation);
2691
- if (node) yield dedupePlan ? dedupePlan.apply(node) : node;
2570
+ if (node) yield promotedEnums ? refPromotedEnums(node, promotedEnums) : node;
2692
2571
  }
2693
2572
  })();
2694
2573
  } };
@@ -2724,8 +2603,8 @@ const adapterOasName = "oas";
2724
2603
  * input: { path: './petStore.yaml' },
2725
2604
  * output: { path: './src/gen' },
2726
2605
  * adapter: adapterOas({
2727
- * serverIndex: 0,
2728
- * discriminator: 'inherit',
2606
+ * server: { index: 0 },
2607
+ * discriminator: 'propagate',
2729
2608
  * dateType: 'date',
2730
2609
  * }),
2731
2610
  * plugins: [pluginTs()],
@@ -2733,7 +2612,7 @@ const adapterOasName = "oas";
2733
2612
  * ```
2734
2613
  */
2735
2614
  const adapterOas = (0, _kubb_core.createAdapter)((options) => {
2736
- const { validate = true, contentType, serverIndex, serverVariables, discriminator = "strict", dedupe = true, dateType = DEFAULT_PARSER_OPTIONS.dateType, integerType = DEFAULT_PARSER_OPTIONS.integerType, unknownType = DEFAULT_PARSER_OPTIONS.unknownType, enumSuffix = DEFAULT_PARSER_OPTIONS.enumSuffix, emptySchemaType = unknownType || DEFAULT_PARSER_OPTIONS.emptySchemaType } = options;
2615
+ const { validate = true, contentType, server, discriminator = "preserve", enums = "inline", dateType = DEFAULT_PARSER_OPTIONS.dateType, integerType = DEFAULT_PARSER_OPTIONS.integerType, unknownType = DEFAULT_PARSER_OPTIONS.unknownType, enumSuffix = DEFAULT_PARSER_OPTIONS.enumSuffix, emptySchemaType = unknownType || DEFAULT_PARSER_OPTIONS.emptySchemaType } = options;
2737
2616
  const parserOptions = {
2738
2617
  ...DEFAULT_PARSER_OPTIONS,
2739
2618
  dateType,
@@ -2791,7 +2670,7 @@ const adapterOas = (0, _kubb_core.createAdapter)((options) => {
2791
2670
  document,
2792
2671
  parserOptions,
2793
2672
  discriminator,
2794
- dedupe
2673
+ enums
2795
2674
  });
2796
2675
  preScanCache.set(document, result);
2797
2676
  return result;
@@ -2800,7 +2679,7 @@ const adapterOas = (0, _kubb_core.createAdapter)((options) => {
2800
2679
  const document = await ensureDocument(source);
2801
2680
  const schemas = await ensureSchemas(document);
2802
2681
  const { parseSchema, parseOperation } = ensureSchemaParser(document);
2803
- const { refAliasMap, enumNames, circularNames, discriminatorChildMap, dedupePlan } = ensurePreScan(document, schemas, parseSchema, parseOperation);
2682
+ const { refAliasMap, enumNames, circularNames, discriminatorChildMap, promotedEnums } = ensurePreScan(document, schemas, parseSchema, parseOperation);
2804
2683
  return createInputStream({
2805
2684
  schemas,
2806
2685
  parseSchema,
@@ -2809,15 +2688,14 @@ const adapterOas = (0, _kubb_core.createAdapter)((options) => {
2809
2688
  parserOptions,
2810
2689
  refAliasMap,
2811
2690
  discriminatorChildMap,
2812
- dedupePlan,
2691
+ promotedEnums,
2813
2692
  meta: {
2814
2693
  title: document.info?.title,
2815
2694
  description: document.info?.description,
2816
2695
  version: document.info?.version,
2817
2696
  baseURL: resolveBaseUrl({
2818
2697
  document,
2819
- serverIndex,
2820
- serverVariables
2698
+ server
2821
2699
  }),
2822
2700
  circularNames,
2823
2701
  enumNames
@@ -2830,10 +2708,9 @@ const adapterOas = (0, _kubb_core.createAdapter)((options) => {
2830
2708
  return {
2831
2709
  validate,
2832
2710
  contentType,
2833
- serverIndex,
2834
- serverVariables,
2711
+ server,
2835
2712
  discriminator,
2836
- dedupe,
2713
+ enums,
2837
2714
  dateType,
2838
2715
  integerType,
2839
2716
  unknownType,