@kubb/adapter-oas 5.0.0-beta.30 → 5.0.0-beta.32

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,5 +1,4 @@
1
- import { t as __name } from "./chunk--u3MIqq1.js";
2
- import * as _$_kubb_core0 from "@kubb/core";
1
+ import { t as __name } from "./chunk-C0LytTxp.js";
3
2
  import { AdapterFactoryOptions, ast } from "@kubb/core";
4
3
  import { DiscriminatorObject as DiscriminatorObject$2, MediaTypeObject as MediaTypeObject$2, OASDocument, ResponseObject as ResponseObject$2, SchemaObject as SchemaObject$2 } from "oas/types";
5
4
  import { Operation as Operation$1 } from "oas/operation";
@@ -476,6 +475,18 @@ type AdapterOasOptions = {
476
475
  * @default 'strict'
477
476
  */
478
477
  discriminator?: 'strict' | 'inherit';
478
+ /**
479
+ * Collapse structurally identical schemas and enums into a single shared definition.
480
+ *
481
+ * Duplicated inline shapes (especially enums repeated across many properties) are hoisted
482
+ * into one named schema; every other occurrence — and any structurally identical top-level
483
+ * component — becomes a `ref` to it. Equality is shape-only: documentation such as
484
+ * `description` and `example` is ignored. Enabled by default; set to `false` to keep every
485
+ * occurrence inline and produce byte-for-byte identical output to earlier versions.
486
+ *
487
+ * @default true
488
+ */
489
+ dedupe?: boolean;
479
490
  } & Partial<ast.ParserOptions>;
480
491
  /**
481
492
  * Adapter options after defaults have been applied and schema name collisions resolved.
@@ -486,6 +497,7 @@ type AdapterOasResolvedOptions = {
486
497
  serverIndex: AdapterOasOptions['serverIndex'];
487
498
  serverVariables: AdapterOasOptions['serverVariables'];
488
499
  discriminator: NonNullable<AdapterOasOptions['discriminator']>;
500
+ dedupe: NonNullable<AdapterOasOptions['dedupe']>;
489
501
  dateType: NonNullable<AdapterOasOptions['dateType']>;
490
502
  integerType: NonNullable<AdapterOasOptions['integerType']>;
491
503
  unknownType: NonNullable<AdapterOasOptions['unknownType']>;
@@ -540,7 +552,7 @@ declare const adapterOasName = "oas";
540
552
  * })
541
553
  * ```
542
554
  */
543
- declare const adapterOas: (options?: AdapterOasOptions | undefined) => _$_kubb_core0.Adapter<AdapterOas>;
555
+ declare const adapterOas: (options?: AdapterOasOptions | undefined) => import("@kubb/core").Adapter<AdapterOas>;
544
556
  //#endregion
545
557
  //#region src/factory.d.ts
546
558
  type ValidateDocumentOptions = {
package/dist/index.js CHANGED
@@ -1,4 +1,4 @@
1
- import "./chunk--u3MIqq1.js";
1
+ import "./chunk-C0LytTxp.js";
2
2
  import path from "node:path";
3
3
  import { ast, createAdapter } from "@kubb/core";
4
4
  import BaseOas from "oas";
@@ -1484,6 +1484,15 @@ function createSchemaParser(ctx, dialect = oasDialect) {
1484
1484
  }, rawOptions);
1485
1485
  const nullInEnum = schema.enum.includes(null);
1486
1486
  const filteredValues = nullInEnum ? schema.enum.filter((v) => v !== null) : schema.enum;
1487
+ if (nullInEnum && filteredValues.length === 0) return ast.createSchema({
1488
+ type: "null",
1489
+ primitive: "null",
1490
+ name,
1491
+ title: schema.title,
1492
+ description: schema.description,
1493
+ deprecated: schema.deprecated,
1494
+ format: schema.format
1495
+ });
1487
1496
  const enumNullable = nullable || nullInEnum || void 0;
1488
1497
  const enumDefault = schema.default === null && enumNullable ? void 0 : schema.default;
1489
1498
  const enumPrimitive = getPrimitiveType(type);
@@ -1989,9 +1998,8 @@ function createSchemaParser(ctx, dialect = oasDialect) {
1989
1998
  * Builds a map of child schema names → discriminator patch data by scanning the given
1990
1999
  * top-level AST schema nodes for union schemas that carry a `discriminatorPropertyName`.
1991
2000
  *
1992
- * Extracted from `applyDiscriminatorInheritance` so the streaming path can call it on a
1993
- * small pre-parsed subset of schemas (only the discriminator parents) rather than on all
1994
- * schemas at once.
2001
+ * The streaming path calls this on a small pre-parsed subset of schemas (only the
2002
+ * discriminator parents) rather than on all schemas at once.
1995
2003
  */
1996
2004
  function buildDiscriminatorChildMap(schemas) {
1997
2005
  const childMap = /* @__PURE__ */ new Map();
@@ -2065,6 +2073,36 @@ function patchDiscriminatorNode(node, entry) {
2065
2073
  //#endregion
2066
2074
  //#region src/stream.ts
2067
2075
  /**
2076
+ * Builds the deduplication plan from the already-parsed top-level schema nodes plus a
2077
+ * single extra parse pass over operations (so duplicates in request/response bodies are seen).
2078
+ *
2079
+ * Only enums and objects are candidates, and object shapes that reference a circular schema are
2080
+ * rejected to avoid hoisting recursive structures. Inline shapes reuse their context-derived
2081
+ * name (collision-resolved against existing component names); shapes without a name stay inline.
2082
+ */
2083
+ function createDedupePlan({ schemaNodes, operationNodes, schemaNames, circularNames }) {
2084
+ const circularSchemas = new Set(circularNames);
2085
+ const usedNames = new Set(schemaNames);
2086
+ return ast.buildDedupePlan([...schemaNodes, ...operationNodes], {
2087
+ isCandidate: (node) => {
2088
+ if (node.type === "enum") return true;
2089
+ if (node.type !== "object") return false;
2090
+ if (node.name && circularSchemas.has(node.name)) return false;
2091
+ return !ast.containsCircularRef(node, { circularSchemas });
2092
+ },
2093
+ nameFor: (node) => {
2094
+ const base = node.name;
2095
+ if (!base) return null;
2096
+ let name = base;
2097
+ let counter = 2;
2098
+ while (usedNames.has(name)) name = `${base}${counter++}`;
2099
+ usedNames.add(name);
2100
+ return name;
2101
+ },
2102
+ refFor: (name) => `${SCHEMA_REF_PREFIX}${name}`
2103
+ });
2104
+ }
2105
+ /**
2068
2106
  * Reads the server URL from the document's `servers` array at `serverIndex`,
2069
2107
  * interpolating any `serverVariables` into the URL template.
2070
2108
  *
@@ -2105,7 +2143,7 @@ function resolveBaseUrl({ document, serverIndex, serverVariables }) {
2105
2143
  * })
2106
2144
  * ```
2107
2145
  */
2108
- function preScan({ schemas, parseSchema, parserOptions, discriminator }) {
2146
+ function preScan({ schemas, parseSchema, parseOperation, baseOas, parserOptions, discriminator, dedupe }) {
2109
2147
  const allNodes = [];
2110
2148
  const refAliasMap = /* @__PURE__ */ new Map();
2111
2149
  const enumNames = [];
@@ -2120,11 +2158,30 @@ function preScan({ schemas, parseSchema, parserOptions, discriminator }) {
2120
2158
  if (ast.narrowSchema(node, ast.schemaTypes.enum) && node.name) enumNames.push(node.name);
2121
2159
  if (discriminator === "inherit" && (schema.oneOf ?? schema.anyOf) && schema.discriminator?.propertyName) discriminatorParentNodes.push(node);
2122
2160
  }
2161
+ const circularNames = [...ast.findCircularSchemas(allNodes)];
2162
+ const discriminatorChildMap = discriminatorParentNodes.length > 0 ? buildDiscriminatorChildMap(discriminatorParentNodes) : null;
2163
+ let dedupePlan = null;
2164
+ if (dedupe) {
2165
+ const operationNodes = [];
2166
+ for (const methods of Object.values(baseOas.getPaths())) for (const operation of Object.values(methods)) {
2167
+ if (!operation) continue;
2168
+ const operationNode = parseOperation(parserOptions, operation);
2169
+ if (operationNode) operationNodes.push(operationNode);
2170
+ }
2171
+ dedupePlan = createDedupePlan({
2172
+ schemaNodes: allNodes,
2173
+ operationNodes,
2174
+ schemaNames: Object.keys(schemas),
2175
+ circularNames
2176
+ });
2177
+ for (const definition of dedupePlan.hoisted) if (definition.type === "enum" && definition.name) enumNames.push(definition.name);
2178
+ }
2123
2179
  return {
2124
2180
  refAliasMap,
2125
2181
  enumNames,
2126
- circularNames: [...ast.findCircularSchemas(allNodes)],
2127
- discriminatorChildMap: discriminatorParentNodes.length > 0 ? buildDiscriminatorChildMap(discriminatorParentNodes) : null
2182
+ circularNames,
2183
+ discriminatorChildMap,
2184
+ dedupePlan
2128
2185
  };
2129
2186
  }
2130
2187
  /**
@@ -2145,26 +2202,39 @@ function preScan({ schemas, parseSchema, parserOptions, discriminator }) {
2145
2202
  * }
2146
2203
  * ```
2147
2204
  */
2148
- function createInputStream({ schemas, parseSchema, parseOperation, baseOas, parserOptions, refAliasMap, discriminatorChildMap, meta }) {
2205
+ function createInputStream({ schemas, parseSchema, parseOperation, baseOas, parserOptions, refAliasMap, discriminatorChildMap, dedupePlan, meta }) {
2206
+ const rewriteTopLevelSchema = (node) => {
2207
+ if (!dedupePlan) return node;
2208
+ const canonical = dedupePlan.canonicalBySignature.get(ast.schemaSignature(node));
2209
+ if (canonical && canonical.name !== node.name) return ast.createSchema({
2210
+ type: "ref",
2211
+ name: node.name ?? null,
2212
+ ref: canonical.ref,
2213
+ description: node.description,
2214
+ deprecated: node.deprecated
2215
+ });
2216
+ return ast.applyDedupe(node, dedupePlan.canonicalBySignature, true);
2217
+ };
2149
2218
  const schemasIterable = { [Symbol.asyncIterator]() {
2150
2219
  return (async function* () {
2220
+ if (dedupePlan) for (const definition of dedupePlan.hoisted) yield definition;
2151
2221
  for (const [name, schema] of Object.entries(schemas)) {
2152
2222
  const alias = refAliasMap.get(name);
2153
2223
  if (alias?.name && schemas[alias.name]) {
2154
- yield {
2224
+ yield rewriteTopLevelSchema({
2155
2225
  ...parseSchema({
2156
2226
  schema: schemas[alias.name],
2157
2227
  name: alias.name
2158
2228
  }, parserOptions),
2159
2229
  name
2160
- };
2230
+ });
2161
2231
  continue;
2162
2232
  }
2163
2233
  const parsed = parseSchema({
2164
2234
  schema,
2165
2235
  name
2166
2236
  }, parserOptions);
2167
- yield discriminatorChildMap?.get(name) ? patchDiscriminatorNode(parsed, discriminatorChildMap.get(name)) : parsed;
2237
+ yield rewriteTopLevelSchema(discriminatorChildMap?.get(name) ? patchDiscriminatorNode(parsed, discriminatorChildMap.get(name)) : parsed);
2168
2238
  }
2169
2239
  })();
2170
2240
  } };
@@ -2173,7 +2243,7 @@ function createInputStream({ schemas, parseSchema, parseOperation, baseOas, pars
2173
2243
  for (const methods of Object.values(baseOas.getPaths())) for (const operation of Object.values(methods)) {
2174
2244
  if (!operation) continue;
2175
2245
  const node = parseOperation(parserOptions, operation);
2176
- if (node) yield node;
2246
+ if (node) yield dedupePlan ? ast.applyDedupe(node, dedupePlan.canonicalBySignature) : node;
2177
2247
  }
2178
2248
  })();
2179
2249
  } };
@@ -2213,7 +2283,7 @@ const adapterOasName = "oas";
2213
2283
  * ```
2214
2284
  */
2215
2285
  const adapterOas = createAdapter((options) => {
2216
- const { validate = true, contentType, serverIndex, serverVariables, discriminator = "strict", 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;
2286
+ 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;
2217
2287
  const parserOptions = {
2218
2288
  ...DEFAULT_PARSER_OPTIONS,
2219
2289
  dateType,
@@ -2240,25 +2310,30 @@ const adapterOas = createAdapter((options) => {
2240
2310
  document,
2241
2311
  contentType
2242
2312
  }));
2243
- const ensurePreScan = once((schemas, parseSchema) => preScan({
2313
+ const ensurePreScan = once((schemas, parseSchema, parseOperation, baseOas) => preScan({
2244
2314
  schemas,
2245
2315
  parseSchema,
2316
+ parseOperation,
2317
+ baseOas,
2246
2318
  parserOptions,
2247
- discriminator
2319
+ discriminator,
2320
+ dedupe
2248
2321
  }));
2249
2322
  async function createStream(source) {
2250
2323
  const document = await ensureDocument(source);
2251
2324
  const schemas = await ensureSchemas(document);
2252
2325
  const { parseSchema, parseOperation } = ensureSchemaParser(document);
2253
- const { refAliasMap, enumNames, circularNames, discriminatorChildMap } = ensurePreScan(schemas, parseSchema);
2326
+ const baseOas = ensureBaseOas(document);
2327
+ const { refAliasMap, enumNames, circularNames, discriminatorChildMap, dedupePlan } = ensurePreScan(schemas, parseSchema, parseOperation, baseOas);
2254
2328
  return createInputStream({
2255
2329
  schemas,
2256
2330
  parseSchema,
2257
2331
  parseOperation,
2258
- baseOas: ensureBaseOas(document),
2332
+ baseOas,
2259
2333
  parserOptions,
2260
2334
  refAliasMap,
2261
2335
  discriminatorChildMap,
2336
+ dedupePlan,
2262
2337
  meta: {
2263
2338
  title: document.info?.title,
2264
2339
  description: document.info?.description,
@@ -2282,6 +2357,7 @@ const adapterOas = createAdapter((options) => {
2282
2357
  serverIndex,
2283
2358
  serverVariables,
2284
2359
  discriminator,
2360
+ dedupe,
2285
2361
  dateType,
2286
2362
  integerType,
2287
2363
  unknownType,