@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.cjs CHANGED
@@ -1510,6 +1510,15 @@ function createSchemaParser(ctx, dialect = oasDialect) {
1510
1510
  }, rawOptions);
1511
1511
  const nullInEnum = schema.enum.includes(null);
1512
1512
  const filteredValues = nullInEnum ? schema.enum.filter((v) => v !== null) : schema.enum;
1513
+ if (nullInEnum && filteredValues.length === 0) return _kubb_core.ast.createSchema({
1514
+ type: "null",
1515
+ primitive: "null",
1516
+ name,
1517
+ title: schema.title,
1518
+ description: schema.description,
1519
+ deprecated: schema.deprecated,
1520
+ format: schema.format
1521
+ });
1513
1522
  const enumNullable = nullable || nullInEnum || void 0;
1514
1523
  const enumDefault = schema.default === null && enumNullable ? void 0 : schema.default;
1515
1524
  const enumPrimitive = getPrimitiveType(type);
@@ -2015,9 +2024,8 @@ function createSchemaParser(ctx, dialect = oasDialect) {
2015
2024
  * Builds a map of child schema names → discriminator patch data by scanning the given
2016
2025
  * top-level AST schema nodes for union schemas that carry a `discriminatorPropertyName`.
2017
2026
  *
2018
- * Extracted from `applyDiscriminatorInheritance` so the streaming path can call it on a
2019
- * small pre-parsed subset of schemas (only the discriminator parents) rather than on all
2020
- * schemas at once.
2027
+ * The streaming path calls this on a small pre-parsed subset of schemas (only the
2028
+ * discriminator parents) rather than on all schemas at once.
2021
2029
  */
2022
2030
  function buildDiscriminatorChildMap(schemas) {
2023
2031
  const childMap = /* @__PURE__ */ new Map();
@@ -2091,6 +2099,36 @@ function patchDiscriminatorNode(node, entry) {
2091
2099
  //#endregion
2092
2100
  //#region src/stream.ts
2093
2101
  /**
2102
+ * Builds the deduplication plan from the already-parsed top-level schema nodes plus a
2103
+ * single extra parse pass over operations (so duplicates in request/response bodies are seen).
2104
+ *
2105
+ * Only enums and objects are candidates, and object shapes that reference a circular schema are
2106
+ * rejected to avoid hoisting recursive structures. Inline shapes reuse their context-derived
2107
+ * name (collision-resolved against existing component names); shapes without a name stay inline.
2108
+ */
2109
+ function createDedupePlan({ schemaNodes, operationNodes, schemaNames, circularNames }) {
2110
+ const circularSchemas = new Set(circularNames);
2111
+ const usedNames = new Set(schemaNames);
2112
+ return _kubb_core.ast.buildDedupePlan([...schemaNodes, ...operationNodes], {
2113
+ isCandidate: (node) => {
2114
+ if (node.type === "enum") return true;
2115
+ if (node.type !== "object") return false;
2116
+ if (node.name && circularSchemas.has(node.name)) return false;
2117
+ return !_kubb_core.ast.containsCircularRef(node, { circularSchemas });
2118
+ },
2119
+ nameFor: (node) => {
2120
+ const base = node.name;
2121
+ if (!base) return null;
2122
+ let name = base;
2123
+ let counter = 2;
2124
+ while (usedNames.has(name)) name = `${base}${counter++}`;
2125
+ usedNames.add(name);
2126
+ return name;
2127
+ },
2128
+ refFor: (name) => `${SCHEMA_REF_PREFIX}${name}`
2129
+ });
2130
+ }
2131
+ /**
2094
2132
  * Reads the server URL from the document's `servers` array at `serverIndex`,
2095
2133
  * interpolating any `serverVariables` into the URL template.
2096
2134
  *
@@ -2131,7 +2169,7 @@ function resolveBaseUrl({ document, serverIndex, serverVariables }) {
2131
2169
  * })
2132
2170
  * ```
2133
2171
  */
2134
- function preScan({ schemas, parseSchema, parserOptions, discriminator }) {
2172
+ function preScan({ schemas, parseSchema, parseOperation, baseOas, parserOptions, discriminator, dedupe }) {
2135
2173
  const allNodes = [];
2136
2174
  const refAliasMap = /* @__PURE__ */ new Map();
2137
2175
  const enumNames = [];
@@ -2146,11 +2184,30 @@ function preScan({ schemas, parseSchema, parserOptions, discriminator }) {
2146
2184
  if (_kubb_core.ast.narrowSchema(node, _kubb_core.ast.schemaTypes.enum) && node.name) enumNames.push(node.name);
2147
2185
  if (discriminator === "inherit" && (schema.oneOf ?? schema.anyOf) && schema.discriminator?.propertyName) discriminatorParentNodes.push(node);
2148
2186
  }
2187
+ const circularNames = [..._kubb_core.ast.findCircularSchemas(allNodes)];
2188
+ const discriminatorChildMap = discriminatorParentNodes.length > 0 ? buildDiscriminatorChildMap(discriminatorParentNodes) : null;
2189
+ let dedupePlan = null;
2190
+ if (dedupe) {
2191
+ const operationNodes = [];
2192
+ for (const methods of Object.values(baseOas.getPaths())) for (const operation of Object.values(methods)) {
2193
+ if (!operation) continue;
2194
+ const operationNode = parseOperation(parserOptions, operation);
2195
+ if (operationNode) operationNodes.push(operationNode);
2196
+ }
2197
+ dedupePlan = createDedupePlan({
2198
+ schemaNodes: allNodes,
2199
+ operationNodes,
2200
+ schemaNames: Object.keys(schemas),
2201
+ circularNames
2202
+ });
2203
+ for (const definition of dedupePlan.hoisted) if (definition.type === "enum" && definition.name) enumNames.push(definition.name);
2204
+ }
2149
2205
  return {
2150
2206
  refAliasMap,
2151
2207
  enumNames,
2152
- circularNames: [..._kubb_core.ast.findCircularSchemas(allNodes)],
2153
- discriminatorChildMap: discriminatorParentNodes.length > 0 ? buildDiscriminatorChildMap(discriminatorParentNodes) : null
2208
+ circularNames,
2209
+ discriminatorChildMap,
2210
+ dedupePlan
2154
2211
  };
2155
2212
  }
2156
2213
  /**
@@ -2171,26 +2228,39 @@ function preScan({ schemas, parseSchema, parserOptions, discriminator }) {
2171
2228
  * }
2172
2229
  * ```
2173
2230
  */
2174
- function createInputStream({ schemas, parseSchema, parseOperation, baseOas, parserOptions, refAliasMap, discriminatorChildMap, meta }) {
2231
+ function createInputStream({ schemas, parseSchema, parseOperation, baseOas, parserOptions, refAliasMap, discriminatorChildMap, dedupePlan, meta }) {
2232
+ const rewriteTopLevelSchema = (node) => {
2233
+ if (!dedupePlan) return node;
2234
+ const canonical = dedupePlan.canonicalBySignature.get(_kubb_core.ast.schemaSignature(node));
2235
+ if (canonical && canonical.name !== node.name) return _kubb_core.ast.createSchema({
2236
+ type: "ref",
2237
+ name: node.name ?? null,
2238
+ ref: canonical.ref,
2239
+ description: node.description,
2240
+ deprecated: node.deprecated
2241
+ });
2242
+ return _kubb_core.ast.applyDedupe(node, dedupePlan.canonicalBySignature, true);
2243
+ };
2175
2244
  const schemasIterable = { [Symbol.asyncIterator]() {
2176
2245
  return (async function* () {
2246
+ if (dedupePlan) for (const definition of dedupePlan.hoisted) yield definition;
2177
2247
  for (const [name, schema] of Object.entries(schemas)) {
2178
2248
  const alias = refAliasMap.get(name);
2179
2249
  if (alias?.name && schemas[alias.name]) {
2180
- yield {
2250
+ yield rewriteTopLevelSchema({
2181
2251
  ...parseSchema({
2182
2252
  schema: schemas[alias.name],
2183
2253
  name: alias.name
2184
2254
  }, parserOptions),
2185
2255
  name
2186
- };
2256
+ });
2187
2257
  continue;
2188
2258
  }
2189
2259
  const parsed = parseSchema({
2190
2260
  schema,
2191
2261
  name
2192
2262
  }, parserOptions);
2193
- yield discriminatorChildMap?.get(name) ? patchDiscriminatorNode(parsed, discriminatorChildMap.get(name)) : parsed;
2263
+ yield rewriteTopLevelSchema(discriminatorChildMap?.get(name) ? patchDiscriminatorNode(parsed, discriminatorChildMap.get(name)) : parsed);
2194
2264
  }
2195
2265
  })();
2196
2266
  } };
@@ -2199,7 +2269,7 @@ function createInputStream({ schemas, parseSchema, parseOperation, baseOas, pars
2199
2269
  for (const methods of Object.values(baseOas.getPaths())) for (const operation of Object.values(methods)) {
2200
2270
  if (!operation) continue;
2201
2271
  const node = parseOperation(parserOptions, operation);
2202
- if (node) yield node;
2272
+ if (node) yield dedupePlan ? _kubb_core.ast.applyDedupe(node, dedupePlan.canonicalBySignature) : node;
2203
2273
  }
2204
2274
  })();
2205
2275
  } };
@@ -2239,7 +2309,7 @@ const adapterOasName = "oas";
2239
2309
  * ```
2240
2310
  */
2241
2311
  const adapterOas = (0, _kubb_core.createAdapter)((options) => {
2242
- 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;
2312
+ 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;
2243
2313
  const parserOptions = {
2244
2314
  ...DEFAULT_PARSER_OPTIONS,
2245
2315
  dateType,
@@ -2266,25 +2336,30 @@ const adapterOas = (0, _kubb_core.createAdapter)((options) => {
2266
2336
  document,
2267
2337
  contentType
2268
2338
  }));
2269
- const ensurePreScan = once((schemas, parseSchema) => preScan({
2339
+ const ensurePreScan = once((schemas, parseSchema, parseOperation, baseOas) => preScan({
2270
2340
  schemas,
2271
2341
  parseSchema,
2342
+ parseOperation,
2343
+ baseOas,
2272
2344
  parserOptions,
2273
- discriminator
2345
+ discriminator,
2346
+ dedupe
2274
2347
  }));
2275
2348
  async function createStream(source) {
2276
2349
  const document = await ensureDocument(source);
2277
2350
  const schemas = await ensureSchemas(document);
2278
2351
  const { parseSchema, parseOperation } = ensureSchemaParser(document);
2279
- const { refAliasMap, enumNames, circularNames, discriminatorChildMap } = ensurePreScan(schemas, parseSchema);
2352
+ const baseOas = ensureBaseOas(document);
2353
+ const { refAliasMap, enumNames, circularNames, discriminatorChildMap, dedupePlan } = ensurePreScan(schemas, parseSchema, parseOperation, baseOas);
2280
2354
  return createInputStream({
2281
2355
  schemas,
2282
2356
  parseSchema,
2283
2357
  parseOperation,
2284
- baseOas: ensureBaseOas(document),
2358
+ baseOas,
2285
2359
  parserOptions,
2286
2360
  refAliasMap,
2287
2361
  discriminatorChildMap,
2362
+ dedupePlan,
2288
2363
  meta: {
2289
2364
  title: document.info?.title,
2290
2365
  description: document.info?.description,
@@ -2308,6 +2383,7 @@ const adapterOas = (0, _kubb_core.createAdapter)((options) => {
2308
2383
  serverIndex,
2309
2384
  serverVariables,
2310
2385
  discriminator,
2386
+ dedupe,
2311
2387
  dateType,
2312
2388
  integerType,
2313
2389
  unknownType,