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

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.js CHANGED
@@ -814,6 +814,23 @@ function dereferenceWithRef(document, schema) {
814
814
  //#endregion
815
815
  //#region src/dialect.ts
816
816
  /**
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
+ /**
817
834
  * The OpenAPI / Swagger dialect, the default used by `@kubb/adapter-oas`.
818
835
  *
819
836
  * Implements the spec-agnostic {@link ast.SchemaDialect} contract: it isolates the
@@ -832,13 +849,16 @@ function dereferenceWithRef(document, schema) {
832
849
  * const parser = createSchemaParser(context, oasDialect) // explicit
833
850
  * ```
834
851
  */
835
- const oasDialect = ast.defineSchemaDialect({
852
+ const oasDialect = ast.defineDialect({
836
853
  name: "oas",
837
- isNullable,
838
- isReference,
839
- isDiscriminator,
840
- isBinary: (schema) => schema.type === "string" && schema.contentMediaType === "application/octet-stream",
841
- resolveRef
854
+ schema: {
855
+ isNullable,
856
+ isReference,
857
+ isDiscriminator,
858
+ isBinary: (schema) => schema.type === "string" && schema.contentMediaType === "application/octet-stream",
859
+ resolveRef,
860
+ optionality
861
+ }
842
862
  });
843
863
  //#endregion
844
864
  //#region src/discriminator.ts
@@ -1561,6 +1581,21 @@ function normalizeArrayEnum(schema) {
1561
1581
  };
1562
1582
  }
1563
1583
  /**
1584
+ * Builds a `null` scalar node carrying the schema's documentation. Shared by the `const: null`
1585
+ * and the drf-spectacular `NullEnum` (`{ enum: [null] }`) branches, which render identically.
1586
+ */
1587
+ function createNullSchema(schema, name) {
1588
+ return ast.factory.createSchema({
1589
+ type: "null",
1590
+ primitive: "null",
1591
+ name,
1592
+ title: schema.title,
1593
+ description: schema.description,
1594
+ deprecated: schema.deprecated,
1595
+ format: schema.format
1596
+ });
1597
+ }
1598
+ /**
1564
1599
  * Names the inline enums on a property's schema, and on each item when the property is a tuple, from
1565
1600
  * the parent and property name. Wraps `macroEnumName` at the property construction site.
1566
1601
  */
@@ -1616,7 +1651,7 @@ function createSchemaParser(ctx, dialect = oasDialect) {
1616
1651
  if (refPath && !resolvingRefs.has(refPath)) {
1617
1652
  if (!resolvedRefCache.has(refPath)) {
1618
1653
  try {
1619
- const referenced = dialect.resolveRef(document, refPath);
1654
+ const referenced = dialect.schema.resolveRef(document, refPath);
1620
1655
  if (referenced) {
1621
1656
  resolvingRefs.add(refPath);
1622
1657
  resolvedSchema = parseSchema({ schema: referenced }, rawOptions);
@@ -1665,13 +1700,13 @@ function createSchemaParser(ctx, dialect = oasDialect) {
1665
1700
  }
1666
1701
  const filteredDiscriminantValues = [];
1667
1702
  const allOfMembers = schema.allOf.filter((item) => {
1668
- if (!dialect.isReference(item) || !name) return true;
1669
- const deref = dialect.resolveRef(document, item.$ref);
1670
- if (!deref || !dialect.isDiscriminator(deref)) return true;
1703
+ if (!dialect.schema.isReference(item) || !name) return true;
1704
+ const deref = dialect.schema.resolveRef(document, item.$ref);
1705
+ if (!deref || !dialect.schema.isDiscriminator(deref)) return true;
1671
1706
  const parentUnion = deref.oneOf ?? deref.anyOf;
1672
1707
  if (!parentUnion) return true;
1673
1708
  const childRef = `${SCHEMA_REF_PREFIX}${name}`;
1674
- const inOneOf = parentUnion.some((oneOfItem) => dialect.isReference(oneOfItem) && oneOfItem.$ref === childRef);
1709
+ const inOneOf = parentUnion.some((oneOfItem) => dialect.schema.isReference(oneOfItem) && oneOfItem.$ref === childRef);
1675
1710
  const inMapping = Object.values(deref.discriminator.mapping ?? {}).some((v) => v === childRef);
1676
1711
  if (inOneOf || inMapping) {
1677
1712
  const discriminatorValue = findDiscriminator(deref.discriminator.mapping, childRef);
@@ -1692,9 +1727,9 @@ function createSchemaParser(ctx, dialect = oasDialect) {
1692
1727
  const missingRequired = schema.required.filter((key) => !outerKeys.has(key));
1693
1728
  if (missingRequired.length) {
1694
1729
  const resolvedMembers = schema.allOf.flatMap((item) => {
1695
- if (!dialect.isReference(item)) return [item];
1696
- const deref = dialect.resolveRef(document, item.$ref);
1697
- return deref && !dialect.isReference(deref) ? [deref] : [];
1730
+ if (!dialect.schema.isReference(item)) return [item];
1731
+ const deref = dialect.schema.resolveRef(document, item.$ref);
1732
+ return deref && !dialect.schema.isReference(deref) ? [deref] : [];
1698
1733
  });
1699
1734
  for (const key of missingRequired) for (const resolved of resolvedMembers) if (resolved.properties?.[key]) {
1700
1735
  allOfMembers.push(parseSchema({
@@ -1739,10 +1774,10 @@ function createSchemaParser(ctx, dialect = oasDialect) {
1739
1774
  const strategy = schema.oneOf ? "one" : "any";
1740
1775
  const unionBase = {
1741
1776
  ...buildSchemaNode(schema, name, nullable, defaultValue),
1742
- discriminatorPropertyName: dialect.isDiscriminator(schema) ? schema.discriminator.propertyName : void 0,
1777
+ discriminatorPropertyName: dialect.schema.isDiscriminator(schema) ? schema.discriminator.propertyName : void 0,
1743
1778
  strategy
1744
1779
  };
1745
- const discriminator = dialect.isDiscriminator(schema) ? schema.discriminator : void 0;
1780
+ const discriminator = dialect.schema.isDiscriminator(schema) ? schema.discriminator : void 0;
1746
1781
  const sharedPropertiesNode = schema.properties ? (() => {
1747
1782
  const { oneOf: _oneOf, anyOf: _anyOf, ...schemaWithoutUnion } = schema;
1748
1783
  return parseSchema({
@@ -1752,7 +1787,7 @@ function createSchemaParser(ctx, dialect = oasDialect) {
1752
1787
  })() : void 0;
1753
1788
  if (sharedPropertiesNode || discriminator?.mapping) {
1754
1789
  const members = unionMembers.map((s) => {
1755
- const ref = dialect.isReference(s) ? s.$ref : void 0;
1790
+ const ref = dialect.schema.isReference(s) ? s.$ref : void 0;
1756
1791
  const discriminatorValue = findDiscriminator(discriminator?.mapping, ref);
1757
1792
  const memberNode = parseSchema({
1758
1793
  schema: s,
@@ -1798,15 +1833,7 @@ function createSchemaParser(ctx, dialect = oasDialect) {
1798
1833
  */
1799
1834
  function convertConst({ schema, name, nullable, defaultValue }) {
1800
1835
  const constValue = schema.const;
1801
- if (constValue === null) return ast.factory.createSchema({
1802
- type: "null",
1803
- primitive: "null",
1804
- name,
1805
- title: schema.title,
1806
- description: schema.description,
1807
- deprecated: schema.deprecated,
1808
- format: schema.format
1809
- });
1836
+ if (constValue === null) return createNullSchema(schema, name);
1810
1837
  const constPrimitive = getPrimitiveType(typeof constValue === "number" ? "number" : typeof constValue === "boolean" ? "boolean" : "string");
1811
1838
  return ast.factory.createSchema({
1812
1839
  type: "enum",
@@ -1895,15 +1922,7 @@ function createSchemaParser(ctx, dialect = oasDialect) {
1895
1922
  }, rawOptions);
1896
1923
  const nullInEnum = schema.enum.includes(null);
1897
1924
  const filteredValues = nullInEnum ? schema.enum.filter((v) => v !== null) : schema.enum;
1898
- if (nullInEnum && filteredValues.length === 0) return ast.factory.createSchema({
1899
- type: "null",
1900
- primitive: "null",
1901
- name,
1902
- title: schema.title,
1903
- description: schema.description,
1904
- deprecated: schema.deprecated,
1905
- format: schema.format
1906
- });
1925
+ if (nullInEnum && filteredValues.length === 0) return createNullSchema(schema, name);
1907
1926
  const enumNullable = nullable || nullInEnum || void 0;
1908
1927
  const enumDefault = schema.default === null && enumNullable ? void 0 : schema.default;
1909
1928
  const enumPrimitive = getPrimitiveType(type);
@@ -1953,7 +1972,7 @@ function createSchemaParser(ctx, dialect = oasDialect) {
1953
1972
  const properties = schema.properties ? Object.entries(schema.properties).map(([propName, propSchema]) => {
1954
1973
  const required = Array.isArray(schema.required) ? schema.required.includes(propName) : !!schema.required;
1955
1974
  const resolvedPropSchema = propSchema;
1956
- const propNullable = dialect.isNullable(resolvedPropSchema);
1975
+ const propNullable = dialect.schema.isNullable(resolvedPropSchema);
1957
1976
  const schemaNode = nameEnums(parseSchema({
1958
1977
  schema: resolvedPropSchema,
1959
1978
  name: childName(name, propName)
@@ -1969,7 +1988,7 @@ function createSchemaParser(ctx, dialect = oasDialect) {
1969
1988
  nullable: schemaNode.type === "null" ? void 0 : propNullable || void 0
1970
1989
  },
1971
1990
  required
1972
- });
1991
+ }, dialect);
1973
1992
  }) : [];
1974
1993
  const additionalProperties = schema.additionalProperties;
1975
1994
  const additionalPropertiesNode = (() => {
@@ -1990,7 +2009,7 @@ function createSchemaParser(ctx, dialect = oasDialect) {
1990
2009
  maxProperties: schema.maxProperties,
1991
2010
  ...buildSchemaNode(schema, name, nullable, defaultValue)
1992
2011
  });
1993
- if (dialect.isDiscriminator(schema) && schema.discriminator.mapping) {
2012
+ if (dialect.schema.isDiscriminator(schema) && schema.discriminator.mapping) {
1994
2013
  const discPropName = schema.discriminator.propertyName;
1995
2014
  const values = Object.keys(schema.discriminator.mapping);
1996
2015
  const enumName = name ? enumPropName(name, discPropName, options.enumSuffix) : void 0;
@@ -2134,7 +2153,7 @@ function createSchemaParser(ctx, dialect = oasDialect) {
2134
2153
  const schemaRules = [
2135
2154
  {
2136
2155
  name: "ref",
2137
- match: ({ schema }) => dialect.isReference(schema),
2156
+ match: ({ schema }) => dialect.schema.isReference(schema),
2138
2157
  convert: convertRef
2139
2158
  },
2140
2159
  {
@@ -2159,7 +2178,7 @@ function createSchemaParser(ctx, dialect = oasDialect) {
2159
2178
  },
2160
2179
  {
2161
2180
  name: "blob",
2162
- match: ({ schema }) => dialect.isBinary(schema),
2181
+ match: ({ schema }) => dialect.schema.isBinary(schema),
2163
2182
  convert: convertBlob
2164
2183
  },
2165
2184
  {
@@ -2240,7 +2259,7 @@ function createSchemaParser(ctx, dialect = oasDialect) {
2240
2259
  schema: flattenedSchema,
2241
2260
  name
2242
2261
  }, rawOptions);
2243
- const nullable = dialect.isNullable(schema) || void 0;
2262
+ const nullable = dialect.schema.isNullable(schema) || void 0;
2244
2263
  const schemaCtx = {
2245
2264
  schema,
2246
2265
  name,
@@ -2283,7 +2302,7 @@ function createSchemaParser(ctx, dialect = oasDialect) {
2283
2302
  description: param["description"] ?? schema.description
2284
2303
  },
2285
2304
  required
2286
- });
2305
+ }, dialect);
2287
2306
  }
2288
2307
  /**
2289
2308
  * Reads the inline `requestBody` metadata (description / required) that OAS exposes
@@ -2317,7 +2336,7 @@ function createSchemaParser(ctx, dialect = oasDialect) {
2317
2336
  const keys = [];
2318
2337
  for (const key in schema.properties) {
2319
2338
  const prop = schema.properties[key];
2320
- if (prop && !dialect.isReference(prop) && prop[flag]) keys.push(key);
2339
+ if (prop && !dialect.schema.isReference(prop) && prop[flag]) keys.push(key);
2321
2340
  }
2322
2341
  return keys.length ? keys : null;
2323
2342
  }
@@ -2334,14 +2353,14 @@ function createSchemaParser(ctx, dialect = oasDialect) {
2334
2353
  const content = allContentTypes.flatMap((ct) => {
2335
2354
  const schema = getRequestSchema(document, operation, { contentType: ct });
2336
2355
  if (!schema) return [];
2337
- return [{
2356
+ return [ast.factory.createContent({
2338
2357
  contentType: ct,
2339
- schema: ast.syncOptionality(parseSchema({
2358
+ schema: dialect.schema.optionality(parseSchema({
2340
2359
  schema,
2341
2360
  name: requestBodyName
2342
2361
  }, options), requestBodyMeta.required),
2343
2362
  keysToOmit: collectPropertyKeysByFlag(schema, "readOnly")
2344
- }];
2363
+ })];
2345
2364
  });
2346
2365
  const requestBody = content.length > 0 || requestBodyMeta.description ? {
2347
2366
  description: requestBodyMeta.description,
@@ -2366,17 +2385,17 @@ function createSchemaParser(ctx, dialect = oasDialect) {
2366
2385
  keysToOmit: collectPropertyKeysByFlag(raw, "writeOnly")
2367
2386
  };
2368
2387
  };
2369
- const content = (ctx.contentType ? [ctx.contentType] : getResponseBodyContentTypes(document, operation, statusCode)).map((contentType) => ({
2388
+ const content = (ctx.contentType ? [ctx.contentType] : getResponseBodyContentTypes(document, operation, statusCode)).map((contentType) => ast.factory.createContent({
2370
2389
  contentType,
2371
2390
  ...parseEntrySchema(contentType)
2372
2391
  }));
2373
- if (content.length === 0) content.push({
2392
+ if (content.length === 0) content.push(ast.factory.createContent({
2374
2393
  contentType: getRequestContentType({
2375
2394
  document,
2376
2395
  operation
2377
2396
  }) || "application/json",
2378
2397
  ...parseEntrySchema(ctx.contentType)
2379
- });
2398
+ }));
2380
2399
  return ast.factory.createResponse({
2381
2400
  statusCode,
2382
2401
  description,
@@ -2384,7 +2403,7 @@ function createSchemaParser(ctx, dialect = oasDialect) {
2384
2403
  });
2385
2404
  });
2386
2405
  const pathItem = document.paths?.[operation.path];
2387
- const pathItemDoc = pathItem && !dialect.isReference(pathItem) ? pathItem : void 0;
2406
+ const pathItemDoc = pathItem && !dialect.schema.isReference(pathItem) ? pathItem : void 0;
2388
2407
  const pickDoc = (key) => {
2389
2408
  const own = operation.schema[key];
2390
2409
  if (typeof own === "string") return own;
@@ -2469,36 +2488,6 @@ function visit(node, pointer) {
2469
2488
  //#endregion
2470
2489
  //#region src/stream.ts
2471
2490
  /**
2472
- * Builds the deduplication plan from the already-parsed top-level schema nodes plus a
2473
- * single extra parse pass over operations (so duplicates in request/response bodies are seen).
2474
- *
2475
- * Only enums and objects are candidates, and object shapes that reference a circular schema are
2476
- * rejected to avoid hoisting recursive structures. Inline shapes reuse their context-derived
2477
- * name (collision-resolved against existing component names). Shapes without a name stay inline.
2478
- */
2479
- function createDedupePlan({ schemaNodes, operationNodes, schemaNames, circularNames }) {
2480
- const circularSchemas = new Set(circularNames);
2481
- const usedNames = new Set(schemaNames);
2482
- return ast.buildDedupePlan([...schemaNodes, ...operationNodes], {
2483
- isCandidate: (node) => {
2484
- if (node.type === "enum") return true;
2485
- if (node.type !== "object") return false;
2486
- if (node.name && circularSchemas.has(node.name)) return false;
2487
- return !containsCircularRef(node, { circularSchemas });
2488
- },
2489
- nameFor: (node) => {
2490
- const base = node.name;
2491
- if (!base) return null;
2492
- let name = base;
2493
- let counter = 2;
2494
- while (usedNames.has(name)) name = `${base}${counter++}`;
2495
- usedNames.add(name);
2496
- return name;
2497
- },
2498
- refFor: (name) => `${SCHEMA_REF_PREFIX}${name}`
2499
- });
2500
- }
2501
- /**
2502
2491
  * Reads the server URL from the document's `servers` array at `serverIndex`,
2503
2492
  * interpolating any `serverVariables` into the URL template.
2504
2493
  *
@@ -2567,11 +2556,25 @@ function preScan({ schemas, parseSchema, parseOperation, document, parserOptions
2567
2556
  const operationNode = parseOperation(parserOptions, operation);
2568
2557
  if (operationNode) operationNodes.push(operationNode);
2569
2558
  }
2570
- dedupePlan = createDedupePlan({
2571
- schemaNodes: allNodes,
2572
- operationNodes,
2573
- schemaNames: Object.keys(schemas),
2574
- circularNames
2559
+ const circularSchemas = new Set(circularNames);
2560
+ 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}`
2575
2578
  });
2576
2579
  for (const definition of dedupePlan.hoisted) if (definition.type === "enum" && definition.name) enumNames.push(definition.name);
2577
2580
  }