@kubb/adapter-oas 5.0.0-beta.36 → 5.0.0-beta.37

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@kubb/adapter-oas",
3
- "version": "5.0.0-beta.36",
3
+ "version": "5.0.0-beta.37",
4
4
  "description": "OpenAPI and Swagger adapter for Kubb. Parses and validates OAS 2.0/3.x specifications into a @kubb/ast RootNode for downstream code generation plugins.",
5
5
  "keywords": [
6
6
  "adapter",
@@ -47,7 +47,7 @@
47
47
  "oas": "^32.1.18",
48
48
  "oas-normalize": "^16.0.4",
49
49
  "swagger2openapi": "^7.0.8",
50
- "@kubb/core": "5.0.0-beta.36"
50
+ "@kubb/core": "5.0.0-beta.37"
51
51
  },
52
52
  "devDependencies": {
53
53
  "@types/swagger2openapi": "^7.0.4",
package/src/adapter.ts CHANGED
@@ -3,7 +3,7 @@ import { ast, createAdapter } from '@kubb/core'
3
3
  import type { AdapterSource } from '@kubb/core'
4
4
  import BaseOas from 'oas'
5
5
  import { DEFAULT_PARSER_OPTIONS } from './constants.ts'
6
- import { parseDocument, parseFromConfig, validateDocument } from './factory.ts'
6
+ import { assertInputExists, parseDocument, parseFromConfig, validateDocument } from './factory.ts'
7
7
  import { createSchemaParser } from './parser.ts'
8
8
  import { getSchemas } from './resolvers.ts'
9
9
  import { createInputStream, preScan, resolveBaseUrl } from './stream.ts'
@@ -144,6 +144,7 @@ export const adapterOas = createAdapter<AdapterOas>((options) => {
144
144
  return parsedDocument
145
145
  },
146
146
  async validate(input, options) {
147
+ await assertInputExists(input)
147
148
  const document = await parseDocument(input)
148
149
  await validateDocument(document, options)
149
150
  },
package/src/constants.ts CHANGED
@@ -79,6 +79,14 @@ export const structuralKeys = new Set(['properties', 'items', 'additionalPropert
79
79
  * formatMap['float'] // 'number'
80
80
  * ```
81
81
  */
82
+ /**
83
+ * Formats `convertFormat` maps to a dedicated type without going through `formatMap`:
84
+ * `int64` and the date/time family. Keep this in sync with the `convertFormat`
85
+ * special-cases in `parser.ts`. `isHandledFormat` reads it so the
86
+ * `KUBB_UNSUPPORTED_FORMAT` diagnostic and the parser agree on what is handled.
87
+ */
88
+ export const specialCasedFormats: ReadonlySet<string> = new Set(['int64', 'date-time', 'date', 'time'])
89
+
82
90
  export const formatMap = {
83
91
  uuid: 'uuid',
84
92
  email: 'email',
package/src/dialect.ts CHANGED
@@ -4,13 +4,13 @@ import { resolveRef } from './refs.ts'
4
4
  import type { SchemaObject } from './types.ts'
5
5
 
6
6
  /**
7
- * The OpenAPI / Swagger dialect the default used by `@kubb/adapter-oas`.
7
+ * The OpenAPI / Swagger dialect, the default used by `@kubb/adapter-oas`.
8
8
  *
9
9
  * Implements the spec-agnostic {@link ast.SchemaDialect} contract: it isolates the
10
10
  * decisions that differ between specs (nullability, `$ref`, discriminator, binary,
11
11
  * ref resolution) so the converter pipeline and dispatch rules stay shared. A
12
- * future adapter (e.g. AsyncAPI) ships its own dialect `type: ['null', …]`
13
- * nullability, no discriminator object, binary via `contentEncoding` and reuses
12
+ * future adapter (e.g. AsyncAPI) ships its own dialect, `type: ['null', …]`
13
+ * nullability, no discriminator object, binary via `contentEncoding` and reuses
14
14
  * the rest unchanged.
15
15
  *
16
16
  * Formats (`uuid`, `email`, dates, …) are intentionally NOT here: they are shared
package/src/factory.ts CHANGED
@@ -1,5 +1,6 @@
1
1
  import path from 'node:path'
2
- import { mergeDeep, URLPath } from '@internals/utils'
2
+ import { exists, mergeDeep, URLPath } from '@internals/utils'
3
+ import { diagnosticCode, DiagnosticError } from '@kubb/core'
3
4
  import type { AdapterSource } from '@kubb/core'
4
5
  import { bundle, loadConfig } from '@redocly/openapi-core'
5
6
  import OASNormalize from 'oas-normalize'
@@ -77,7 +78,13 @@ export async function mergeDocuments(pathOrApi: Array<string | Document>): Promi
77
78
  const documents = await Promise.all(pathOrApi.map((p) => parseDocument(p, { enablePaths: false, canBundle: false })))
78
79
 
79
80
  if (documents.length === 0) {
80
- throw new Error('No OAS documents provided for merging.')
81
+ throw new DiagnosticError({
82
+ code: diagnosticCode.inputRequired,
83
+ severity: 'error',
84
+ message: 'No OAS documents were provided for merging.',
85
+ help: 'Pass at least one path or document to `input.path`.',
86
+ location: { kind: 'config' },
87
+ })
81
88
  }
82
89
 
83
90
  const seed: Document = {
@@ -99,9 +106,9 @@ export async function mergeDocuments(pathOrApi: Array<string | Document>): Promi
99
106
  * Creates a `Document` from an `AdapterSource`.
100
107
  *
101
108
  * Handles all three source types:
102
- * - `{ type: 'path' }` resolves and bundles a local file path or remote URL.
103
- * - `{ type: 'paths' }` merges multiple file paths into a single document.
104
- * - `{ type: 'data' }` parses an inline string (YAML/JSON) or raw object.
109
+ * - `{ type: 'path' }` resolves and bundles a local file path or remote URL.
110
+ * - `{ type: 'paths' }` merges multiple file paths into a single document.
111
+ * - `{ type: 'data' }` parses an inline string (YAML/JSON) or raw object.
105
112
  *
106
113
  * @example
107
114
  * ```ts
@@ -109,7 +116,7 @@ export async function mergeDocuments(pathOrApi: Array<string | Document>): Promi
109
116
  * const document = await parseFromConfig({ type: 'data', data: '{"openapi":"3.0.0",...}' })
110
117
  * ```
111
118
  */
112
- export function parseFromConfig(source: AdapterSource): Promise<Document> {
119
+ export async function parseFromConfig(source: AdapterSource): Promise<Document> {
113
120
  if (source.type === 'data') {
114
121
  if (typeof source.data === 'object') {
115
122
  return parseDocument(structuredClone(source.data) as Document)
@@ -127,7 +134,29 @@ export function parseFromConfig(source: AdapterSource): Promise<Document> {
127
134
  return parseDocument(source.path)
128
135
  }
129
136
 
130
- return parseDocument(path.resolve(path.dirname(source.path), source.path))
137
+ const resolved = path.resolve(path.dirname(source.path), source.path)
138
+ await assertInputExists(resolved)
139
+ return parseDocument(resolved)
140
+ }
141
+
142
+ /**
143
+ * Throws a coded `KUBB_INPUT_NOT_FOUND` diagnostic when a local input path does not exist.
144
+ * URLs are skipped, and a malformed but readable file is left for `parseDocument` to surface
145
+ * its parse error instead.
146
+ */
147
+ export async function assertInputExists(input: string): Promise<void> {
148
+ if (new URLPath(input).isURL) {
149
+ return
150
+ }
151
+ if (!(await exists(input))) {
152
+ throw new DiagnosticError({
153
+ code: diagnosticCode.inputNotFound,
154
+ severity: 'error',
155
+ message: `Cannot read the file set in \`input.path\` (or via \`kubb generate PATH\`): ${input}`,
156
+ help: 'Check that the path exists and is readable, then set it in `input.path` or pass it as `kubb generate PATH`.',
157
+ location: { kind: 'config' },
158
+ })
159
+ }
131
160
  }
132
161
 
133
162
  /**
@@ -157,6 +186,6 @@ export async function validateDocument(document: Document, { throwOnError = fals
157
186
  throw error
158
187
  }
159
188
 
160
- // Validation failures are non-fatal mirror plugin-oas behavior
189
+ // Validation failures are non-fatal, mirror plugin-oas behavior
161
190
  }
162
191
  }
package/src/parser.ts CHANGED
@@ -60,7 +60,7 @@ type SchemaContext = {
60
60
  }
61
61
 
62
62
  /**
63
- * One entry in this adapter's schema-dispatch table a {@link ast.DispatchRule} specialized
63
+ * One entry in this adapter's schema-dispatch table, a {@link ast.DispatchRule} specialized
64
64
  * to OAS schema context and Kubb `SchemaNode` output. See {@link ast.dispatch} for the contract
65
65
  * a future adapter (e.g. AsyncAPI) follows: define a context type and an ordered rules table.
66
66
  */
@@ -97,7 +97,7 @@ function normalizeArrayEnum(schema: SchemaObject): SchemaObject {
97
97
  export function createSchemaParser(ctx: OasParserContext, dialect: OasDialect = oasDialect) {
98
98
  const document = ctx.document
99
99
 
100
- // Branch handlers each converts one OAS schema pattern to a SchemaNode.
100
+ // Branch handlers, each converts one OAS schema pattern to a SchemaNode.
101
101
 
102
102
  /**
103
103
  * Tracks `$ref` paths that are currently being resolved to prevent infinite
@@ -106,16 +106,12 @@ export function createSchemaParser(ctx: OasParserContext, dialect: OasDialect =
106
106
  const resolvingRefs = new Set<string>()
107
107
 
108
108
  /**
109
- * Cache of already-resolved `$ref` schemas within this parser instance.
109
+ * Cache of `$ref` schemas already resolved in this parser instance, keyed by ref path.
110
110
  *
111
- * Without this, the same referenced schema (e.g. `customer`) is fully re-expanded
112
- * every time it appears as a `$ref` in a different parent schema. In heavily
113
- * cross-referenced specs like Stripe (~1 400 schemas), this causes exponential
114
- * blowup `customer` alone may be referenced from dozens of top-level schemas,
115
- * each triggering a fresh recursive expansion of its entire sub-tree.
116
- *
117
- * Memoizing by `$ref` path reduces the overall work from O(2^depth) to O(N)
118
- * where N is the number of unique schema names.
111
+ * Without it, a shared schema (e.g. `customer`) is re-expanded for every `$ref` that points at
112
+ * it. In cross-referenced specs like Stripe (~1400 schemas) that becomes exponential blowup,
113
+ * since one schema can be referenced from dozens of parents, each re-walking its whole subtree.
114
+ * Memoizing by ref path drops the work from O(2^depth) to O(N) unique schema names.
119
115
  */
120
116
  const resolvedRefCache = new Map<string, ast.SchemaNode | null>()
121
117
 
@@ -253,7 +249,7 @@ export function createSchemaParser(ctx: OasParserContext, dialect: OasDialect =
253
249
 
254
250
  if (schema.properties) {
255
251
  const { allOf: _allOf, ...schemaWithoutAllOf } = schema
256
- // Don't pass `name` here the result must stay anonymous so it can be merged with the
252
+ // Don't pass `name` here, the result must stay anonymous so it can be merged with the
257
253
  // adjacent synthetic object in `mergeAdjacentObjectsLazy`. Nested enum qualification
258
254
  // happens upstream via `convertObject`'s `setEnumName` propagation.
259
255
  allOfMembers.push(parseSchema({ schema: schemaWithoutAllOf }, rawOptions))
@@ -495,7 +491,7 @@ export function createSchemaParser(ctx: OasParserContext, dialect: OasDialect =
495
491
  const nullInEnum = schema.enum!.includes(null)
496
492
  const filteredValues = (nullInEnum ? schema.enum!.filter((v) => v !== null) : schema.enum!) as Array<string | number | boolean>
497
493
 
498
- // drf-spectacular `NullEnum` ({ enum: [null] }) is just `null`; an empty enum node would
494
+ // drf-spectacular `NullEnum` ({ enum: [null] }) is just `null`. An empty enum node would
499
495
  // render as `never` (plugin-ts) / invalid `z.enum([])` (plugin-zod). Mirror the `const: null`
500
496
  // branch so it renders as a clean `null` (not `z.null().nullable()`).
501
497
  if (nullInEnum && filteredValues.length === 0) {
@@ -777,7 +773,7 @@ export function createSchemaParser(ctx: OasParserContext, dialect: OasDialect =
777
773
  /**
778
774
  * Ordered schema-dispatch table. Order is significant: composition keywords (`$ref`, `allOf`,
779
775
  * `oneOf`/`anyOf`) take precedence over `const`/`format`, which take precedence over the plain
780
- * `type`. The first matching rule that produces a node wins; see {@link SchemaRule} for the
776
+ * `type`. The first matching rule that produces a node wins. See {@link SchemaRule} for the
781
777
  * match/convert/fall-through contract.
782
778
  */
783
779
  const schemaRules: Array<SchemaRule> = [
@@ -1058,8 +1054,7 @@ export function parseSchema(
1058
1054
  * Parses an OpenAPI specification into Kubb's universal `InputNode` AST.
1059
1055
  *
1060
1056
  * This is the main entry point for `@kubb/adapter-oas`. It converts OpenAPI/Swagger specs into a spec-agnostic tree
1061
- * that downstream plugins (`plugin-ts`, `plugin-zod`, etc.) consume for code generation. No code is generated here
1062
- * the tree is a pure data structure representing all schemas and operations.
1057
+ * that downstream plugins (`plugin-ts`, `plugin-zod`, etc.) consume for code generation. No code is generated here, * the tree is a pure data structure representing all schemas and operations.
1063
1058
  *
1064
1059
  * Returns the AST root and a `nameMapping` for resolving schema references.
1065
1060
  *
package/src/refs.ts CHANGED
@@ -1,3 +1,4 @@
1
+ import { type Diagnostic, diagnosticCode, Diagnostics } from '@kubb/core'
1
2
  import { isReference } from './guards.ts'
2
3
  import type { Document } from './types.ts'
3
4
 
@@ -39,7 +40,17 @@ export function resolveRef<T = unknown>(document: Document, $ref: string): T | n
39
40
  .reduce((obj: unknown, key: string) => (obj as Record<string, unknown>)?.[key], document as unknown)
40
41
 
41
42
  if (!current) {
42
- throw new Error(`Could not find a definition for ${origRef}.`)
43
+ const diagnostic: Diagnostic = {
44
+ code: diagnosticCode.refNotFound,
45
+ severity: 'error',
46
+ message: `Could not find a definition for ${origRef}.`,
47
+ help: 'Add the schema under `components.schemas`, or fix the `$ref`. Run `kubb validate` to check the spec.',
48
+ location: { kind: 'schema', pointer: origRef, ref: origRef },
49
+ }
50
+ // Report the unresolved ref into the active build and resolve to null, like any
51
+ // other unresolvable ref. The build collects it and keeps going.
52
+ Diagnostics.report(diagnostic)
53
+ return null
43
54
  }
44
55
 
45
56
  docCache.set($ref, current)
package/src/resolvers.ts CHANGED
@@ -1,9 +1,10 @@
1
1
  import { pascalCase } from '@internals/utils'
2
+ import { diagnosticCode, DiagnosticError } from '@kubb/core'
2
3
  import type { ast } from '@kubb/core'
3
4
  import type { ParameterObject, ServerObject } from 'oas/types'
4
5
  import { isRef } from 'oas/types'
5
6
  import { matchesMimeType } from 'oas/utils'
6
- import { formatMap, SCHEMA_REF_PREFIX, structuralKeys } from './constants.ts'
7
+ import { formatMap, SCHEMA_REF_PREFIX, specialCasedFormats, structuralKeys } from './constants.ts'
7
8
  import { isReference } from './guards.ts'
8
9
  import { dereferenceWithRef, resolveRef } from './refs.ts'
9
10
  import type { ContentType, Document, MediaTypeObject, Operation, ResponseObject, SchemaObject } from './types.ts'
@@ -35,7 +36,13 @@ export function resolveServerUrl(server: ServerObject, overrides?: Record<string
35
36
  }
36
37
 
37
38
  if (variable.enum?.length && !variable.enum.some((e) => String(e) === value)) {
38
- throw new Error(`Invalid server variable value '${value}' for '${key}' when resolving ${server.url}. Valid values are: ${variable.enum.join(', ')}.`)
39
+ throw new DiagnosticError({
40
+ code: diagnosticCode.invalidServerVariable,
41
+ severity: 'error',
42
+ message: `Invalid server variable value '${value}' for '${key}' when resolving ${server.url}. Valid values are: ${variable.enum.join(', ')}.`,
43
+ help: `Use one of the allowed enum values, or drop the enum on the '${key}' server variable.`,
44
+ location: { kind: 'document', pointer: '#/servers' },
45
+ })
39
46
  }
40
47
 
41
48
  url = url.replaceAll(`{${key}}`, value)
@@ -52,6 +59,16 @@ export function getSchemaType(format: string): ast.SchemaType | null {
52
59
  return formatMap[format as keyof typeof formatMap] ?? null
53
60
  }
54
61
 
62
+ /**
63
+ * Whether the parser maps `format` to a dedicated type. True for any `formatMap` entry and the
64
+ * `specialCasedFormats` `convertFormat` handles directly. False means the format falls back to the
65
+ * base type, which is what `KUBB_UNSUPPORTED_FORMAT` flags. Reading both sources keeps the
66
+ * diagnostic in step with the parser as `formatMap` grows.
67
+ */
68
+ export function isHandledFormat(format: string): boolean {
69
+ return getSchemaType(format) !== null || specialCasedFormats.has(format)
70
+ }
71
+
55
72
  /**
56
73
  * Converts an OAS primitive type string to its `PrimitiveSchemaType` equivalent.
57
74
  * Numeric types (`number`, `integer`, `bigint`) pass through unchanged. `boolean` maps to `'boolean'`. Everything else becomes `'string'`.
@@ -224,7 +241,7 @@ export type GetSchemasResult = {
224
241
  /**
225
242
  * Flattens a keyword-only `allOf` into its parent schema.
226
243
  *
227
- * Only flattens when every member is a plain fragment no `$ref` and no structural keywords
244
+ * Only flattens when every member is a plain fragment, with no `$ref` and no structural keywords
228
245
  * (see `structuralKeys`). Outer schema values take precedence over fragment values.
229
246
  * Returns `null` for a `null` input, and the original schema unchanged when flattening is unsafe.
230
247
  *
@@ -234,7 +251,7 @@ export type GetSchemasResult = {
234
251
  * // { type: 'object', properties: {}, description: 'A pet' }
235
252
  *
236
253
  * flattenSchema({ allOf: [{ $ref: '#/components/schemas/Pet' }] })
237
- * // returned unchanged contains a $ref
254
+ * // returned unchanged, contains a $ref
238
255
  * ```
239
256
  */
240
257
  /**
@@ -274,7 +291,7 @@ export function flattenSchema(schema: SchemaObject | null): SchemaObject | null
274
291
  /**
275
292
  * Extracts the inline schema from a media-type `content` map.
276
293
  *
277
- * Prefers `preferredContentType` when given; otherwise uses the first key in the map.
294
+ * Prefers `preferredContentType` when given, otherwise uses the first key in the map.
278
295
  * Returns `null` when `content` is absent, the schema is missing, or the schema is a `$ref`.
279
296
  *
280
297
  * @example
@@ -512,9 +529,9 @@ export function buildSchemaNode(schema: SchemaObject, name: string | null | unde
512
529
  /**
513
530
  * Returns all request body content type keys for an operation.
514
531
  *
515
- * The requestBody is dereferenced **in-place** when it is a `$ref` the same mutation
516
- * that `getRequestSchema` already performs so that the returned list accurately reflects
517
- * the available content types even for referenced bodies.
532
+ * The requestBody is dereferenced in place when it is a `$ref` (the same mutation that
533
+ * `getRequestSchema` already performs), so the returned list accurately reflects the
534
+ * available content types even for referenced bodies.
518
535
  *
519
536
  * @example
520
537
  * ```ts
@@ -531,14 +548,14 @@ export function getRequestBodyContentTypes(document: Document, operation: Operat
531
548
  if (!body) return []
532
549
 
533
550
  // dereferenceWithRef keeps $ref but spreads all resolved fields (including `content`).
534
- // Do not bail out on isReference the content is already present on the merged object.
551
+ // Do not bail out on isReference, the content is already present on the merged object.
535
552
  return body.content ? Object.keys(body.content) : []
536
553
  }
537
554
 
538
555
  /**
539
556
  * Returns all response content type keys for an operation at a given status code.
540
557
  *
541
- * Response `$ref`s are resolved in-place first the same mutation `getResponseSchema` performs
558
+ * Response `$ref`s are resolved in place first (the same mutation `getResponseSchema` performs),
542
559
  * so the returned list reflects the available content types even for referenced responses.
543
560
  *
544
561
  * @example
@@ -0,0 +1,59 @@
1
+ import { diagnosticCode, Diagnostics } from '@kubb/core'
2
+ import type { ast } from '@kubb/core'
3
+ import { isHandledFormat } from './resolvers.ts'
4
+
5
+ /**
6
+ * Reports the advisory diagnostics (`KUBB_UNSUPPORTED_FORMAT`, `KUBB_DEPRECATED`) for one
7
+ * top-level schema. Walks the node the parser produced during `preScan`, threading the RFC 6901
8
+ * pointer as it descends so a nested field reports against its full path
9
+ * (`#/components/schemas/Pet/properties/owner/properties/name`). Refs are not followed, so the
10
+ * resolved schema is reported under its own walk. Reports land in the active build run, are a
11
+ * no-op outside one, and repeats are deduped by the build.
12
+ */
13
+ export function reportSchemaDiagnostics({ node, name }: { node: ast.SchemaNode; name: string }): void {
14
+ visit(node, `#/components/schemas/${name}`)
15
+ }
16
+
17
+ function visit(node: ast.SchemaNode, pointer: string): void {
18
+ if (node.deprecated) {
19
+ Diagnostics.report({
20
+ code: diagnosticCode.deprecated,
21
+ severity: 'info',
22
+ message: 'This schema is marked as deprecated.',
23
+ location: { kind: 'schema', pointer },
24
+ })
25
+ }
26
+
27
+ if (typeof node.format === 'string' && !isHandledFormat(node.format)) {
28
+ Diagnostics.report({
29
+ code: diagnosticCode.unsupportedFormat,
30
+ severity: 'warning',
31
+ message: `Kubb does not map the format "${node.format}" to a specific type, so it falls back to the base type.`,
32
+ help: `Use a format Kubb supports, or handle "${node.format}" with a custom parser or plugin.`,
33
+ location: { kind: 'schema', pointer },
34
+ })
35
+ }
36
+
37
+ if (node.type === 'object') {
38
+ for (const property of node.properties) {
39
+ visit(property.schema, `${pointer}/properties/${property.name}`)
40
+ }
41
+ if (node.additionalProperties && typeof node.additionalProperties === 'object') {
42
+ visit(node.additionalProperties, `${pointer}/additionalProperties`)
43
+ }
44
+ return
45
+ }
46
+
47
+ if (node.type === 'array' || node.type === 'tuple') {
48
+ for (const item of node.items ?? []) {
49
+ visit(item, `${pointer}/items`)
50
+ }
51
+ return
52
+ }
53
+
54
+ if (node.type === 'union' || node.type === 'intersection') {
55
+ for (const member of node.members ?? []) {
56
+ visit(member, pointer)
57
+ }
58
+ }
59
+ }
package/src/stream.ts CHANGED
@@ -4,6 +4,7 @@ import { SCHEMA_REF_PREFIX } from './constants.ts'
4
4
  import { buildDiscriminatorChildMap, patchDiscriminatorNode } from './discriminator.ts'
5
5
  import type { SchemaParser } from './parser.ts'
6
6
  import { resolveServerUrl } from './resolvers.ts'
7
+ import { reportSchemaDiagnostics } from './schemaDiagnostics.ts'
7
8
  import type { DiscriminatorTarget } from './discriminator.ts'
8
9
  import type { AdapterOas, Document, SchemaObject } from './types.ts'
9
10
 
@@ -41,7 +42,7 @@ function createDedupePlan({
41
42
  isCandidate: (node) => {
42
43
  if (node.type === 'enum') return true
43
44
  if (node.type !== 'object') return false
44
- // Skip object shapes that are part of a circular chain hoisting them would break the cycle.
45
+ // Skip object shapes that are part of a circular chain, hoisting them would break the cycle.
45
46
  if (node.name && circularSchemas.has(node.name)) return false
46
47
  return !ast.containsCircularRef(node, { circularSchemas })
47
48
  },
@@ -98,7 +99,7 @@ export function resolveBaseUrl({
98
99
  * After this call, only `refAliasMap` and `discriminatorChildMap` stay alive in the adapter closure.
99
100
  * Both are proportional to the number of aliases or discriminator parents, not total schema count.
100
101
  *
101
- * Each schema is parsed again during the streaming pass this is intentional.
102
+ * Each schema is parsed again during the streaming pass. This is intentional.
102
103
  * Holding the parsed nodes in memory here would defeat the streaming memory benefit.
103
104
  *
104
105
  * @example
@@ -136,6 +137,7 @@ export function preScan({
136
137
  for (const [name, schema] of Object.entries(schemas)) {
137
138
  const node = parseSchema({ schema, name }, parserOptions)
138
139
  allNodes.push(node)
140
+ reportSchemaDiagnostics({ node, name })
139
141
  if (node.type === 'ref' && node.name && node.name !== name) {
140
142
  refAliasMap.set(name, node)
141
143
  }
@@ -153,7 +155,7 @@ export function preScan({
153
155
  let dedupePlan: ast.DedupePlan | null = null
154
156
  if (dedupe) {
155
157
  // One extra parse pass over operations so duplicates in request/response bodies are seen.
156
- // Reuses the already-parsed `allNodes` for schemas no second schema parse.
158
+ // Reuses the already-parsed `allNodes` for schemas, no second schema parse.
157
159
  const operationNodes: Array<ast.OperationNode> = []
158
160
  for (const methods of Object.values(baseOas.getPaths())) {
159
161
  for (const operation of Object.values(methods)) {
package/src/types.ts CHANGED
@@ -172,9 +172,9 @@ export type AdapterOasOptions = {
172
172
  serverVariables?: Record<string, string>
173
173
  /**
174
174
  * How the `discriminator` field on `oneOf`/`anyOf` schemas is interpreted.
175
- * - `'strict'` child schemas stay exactly as written; the discriminator
175
+ * - `'strict'` child schemas stay exactly as written. The discriminator
176
176
  * narrows types at the call site but child shapes are not modified.
177
- * - `'inherit'` Kubb propagates the discriminator property as a literal
177
+ * - `'inherit'` Kubb propagates the discriminator property as a literal
178
178
  * value into each child schema, so each branch's discriminator field is
179
179
  * precisely typed.
180
180
  *
@@ -185,9 +185,9 @@ export type AdapterOasOptions = {
185
185
  * Collapse structurally identical schemas and enums into a single shared definition.
186
186
  *
187
187
  * Duplicated inline shapes (especially enums repeated across many properties) are hoisted
188
- * into one named schema; every other occurrence and any structurally identical top-level
189
- * component becomes a `ref` to it. Equality is shape-only: documentation such as
190
- * `description` and `example` is ignored. Enabled by default; set to `false` to keep every
188
+ * into one named schema. Every other occurrence, and any structurally identical top-level
189
+ * component, becomes a `ref` to it. Equality is shape-only: documentation such as
190
+ * `description` and `example` is ignored. Enabled by default. Set to `false` to keep every
191
191
  * occurrence inline and produce byte-for-byte identical output to earlier versions.
192
192
  *
193
193
  * @default true