@kubb/adapter-oas 5.0.0-beta.57 → 5.0.0-beta.59
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 +178 -137
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.ts +9 -11
- package/dist/index.js +177 -136
- package/dist/index.js.map +1 -1
- package/package.json +3 -3
- package/src/adapter.ts +2 -2
- package/src/constants.ts +3 -3
- package/src/discriminator.ts +42 -2
- package/src/parser.ts +64 -55
- package/src/resolvers.ts +1 -1
- package/src/stream.ts +7 -6
- package/src/types.ts +9 -11
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@kubb/adapter-oas",
|
|
3
|
-
"version": "5.0.0-beta.
|
|
3
|
+
"version": "5.0.0-beta.59",
|
|
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",
|
|
@@ -46,8 +46,8 @@
|
|
|
46
46
|
"api-ref-bundler": "0.5.1",
|
|
47
47
|
"swagger2openapi": "^7.0.8",
|
|
48
48
|
"yaml": "^2.9.0",
|
|
49
|
-
"@kubb/ast": "5.0.0-beta.
|
|
50
|
-
"@kubb/core": "5.0.0-beta.
|
|
49
|
+
"@kubb/ast": "5.0.0-beta.59",
|
|
50
|
+
"@kubb/core": "5.0.0-beta.59"
|
|
51
51
|
},
|
|
52
52
|
"devDependencies": {
|
|
53
53
|
"@types/json-schema": "^7.0.15",
|
package/src/adapter.ts
CHANGED
|
@@ -191,7 +191,7 @@ export const adapterOas = createAdapter<AdapterOas>((options) => {
|
|
|
191
191
|
const result = resolve(schemaName)
|
|
192
192
|
if (!result) return null
|
|
193
193
|
|
|
194
|
-
return ast.createImport({ name: [result.name], path: result.path })
|
|
194
|
+
return ast.factory.createImport({ name: [result.name], path: result.path })
|
|
195
195
|
},
|
|
196
196
|
})
|
|
197
197
|
},
|
|
@@ -200,7 +200,7 @@ export const adapterOas = createAdapter<AdapterOas>((options) => {
|
|
|
200
200
|
|
|
201
201
|
const [schemas, operations] = await Promise.all([Array.fromAsync(streamNode.schemas), Array.fromAsync(streamNode.operations)])
|
|
202
202
|
|
|
203
|
-
return ast.createInput({ schemas, operations, meta: streamNode.meta })
|
|
203
|
+
return ast.factory.createInput({ schemas, operations, meta: streamNode.meta })
|
|
204
204
|
},
|
|
205
205
|
stream: createStream,
|
|
206
206
|
}
|
package/src/constants.ts
CHANGED
|
@@ -73,7 +73,7 @@ export const structuralKeys = new Set(['properties', 'items', 'additionalPropert
|
|
|
73
73
|
*
|
|
74
74
|
* Only formats whose AST type differs from the OAS `type` field appear here.
|
|
75
75
|
* Formats that depend on runtime options (`int64`, `date-time`, `date`, `time`) are handled separately
|
|
76
|
-
* in the parser. `ipv4` and `ipv6` map to their own dedicated schema types
|
|
76
|
+
* in the parser. `ipv4` and `ipv6` map to their own dedicated schema types. `hostname` and
|
|
77
77
|
* `idn-hostname` map to `'url'` as the closest generic string-format type.
|
|
78
78
|
*
|
|
79
79
|
* @example
|
|
@@ -126,8 +126,8 @@ export const formatMap = {
|
|
|
126
126
|
export const enumExtensionKeys = ['x-enumNames', 'x-enum-varnames'] as const
|
|
127
127
|
|
|
128
128
|
/**
|
|
129
|
-
* Maps
|
|
130
|
-
*
|
|
129
|
+
* Maps the `unknownType`/`emptySchemaType` option string to its `ScalarSchemaType` constant.
|
|
130
|
+
* A `Map` (over a plain object) lets callers test key membership with `.has()`.
|
|
131
131
|
*/
|
|
132
132
|
export const typeOptionMap = new Map<'any' | 'unknown' | 'void', ast.ScalarSchemaType>([
|
|
133
133
|
['any', ast.schemaTypes.any],
|
package/src/discriminator.ts
CHANGED
|
@@ -82,11 +82,51 @@ export function patchDiscriminatorNode(node: ast.SchemaNode, entry: { propertyNa
|
|
|
82
82
|
if (!objectNode) return node
|
|
83
83
|
|
|
84
84
|
const { propertyName, enumValues } = entry
|
|
85
|
-
const enumSchema = ast.createSchema({ type: 'enum', enumValues })
|
|
86
|
-
const newProp = ast.createProperty({ name: propertyName, required: true, schema: enumSchema })
|
|
85
|
+
const enumSchema = ast.factory.createSchema({ type: 'enum', enumValues })
|
|
86
|
+
const newProp = ast.factory.createProperty({ name: propertyName, required: true, schema: enumSchema })
|
|
87
87
|
|
|
88
88
|
const existingIdx = objectNode.properties.findIndex((p) => p.name === propertyName)
|
|
89
89
|
const newProperties = existingIdx >= 0 ? objectNode.properties.map((p, i) => (i === existingIdx ? newProp : p)) : [...objectNode.properties, newProp]
|
|
90
90
|
|
|
91
91
|
return { ...objectNode, properties: newProperties }
|
|
92
92
|
}
|
|
93
|
+
|
|
94
|
+
/**
|
|
95
|
+
* Creates a single-property object schema used as a discriminator literal.
|
|
96
|
+
*
|
|
97
|
+
* @example
|
|
98
|
+
* ```ts
|
|
99
|
+
* createDiscriminantNode({ propertyName: 'type', value: 'dog' })
|
|
100
|
+
* // -> { type: 'object', properties: [{ name: 'type', required: true, schema: enum('dog') }] }
|
|
101
|
+
* ```
|
|
102
|
+
*/
|
|
103
|
+
export function createDiscriminantNode({ propertyName, value }: { propertyName: string; value: string }): ast.SchemaNode {
|
|
104
|
+
return ast.factory.createSchema({
|
|
105
|
+
type: 'object',
|
|
106
|
+
primitive: 'object',
|
|
107
|
+
properties: [
|
|
108
|
+
ast.factory.createProperty({
|
|
109
|
+
name: propertyName,
|
|
110
|
+
schema: ast.factory.createSchema({
|
|
111
|
+
type: 'enum',
|
|
112
|
+
primitive: 'string',
|
|
113
|
+
enumValues: [value],
|
|
114
|
+
}),
|
|
115
|
+
required: true,
|
|
116
|
+
}),
|
|
117
|
+
],
|
|
118
|
+
})
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
/**
|
|
122
|
+
* Returns the discriminator key whose mapping value matches `ref`, or `null` when there is no match.
|
|
123
|
+
*
|
|
124
|
+
* @example
|
|
125
|
+
* ```ts
|
|
126
|
+
* findDiscriminator({ dog: '#/components/schemas/Dog' }, '#/components/schemas/Dog') // 'dog'
|
|
127
|
+
* ```
|
|
128
|
+
*/
|
|
129
|
+
export function findDiscriminator(mapping: Record<string, string> | undefined, ref: string | undefined): string | null {
|
|
130
|
+
if (!mapping || !ref) return null
|
|
131
|
+
return Object.entries(mapping).find(([, value]) => value === ref)?.[0] ?? null
|
|
132
|
+
}
|
package/src/parser.ts
CHANGED
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
import { pascalCase } from '@internals/utils'
|
|
2
|
-
import { childName, enumPropName, extractRefName
|
|
2
|
+
import { childName, enumPropName, extractRefName } from '@kubb/ast/utils'
|
|
3
3
|
import { ast } from '@kubb/core'
|
|
4
4
|
import { DEFAULT_PARSER_OPTIONS, enumExtensionKeys, SCHEMA_REF_PREFIX, typeOptionMap } from './constants.ts'
|
|
5
5
|
import { oasDialect, type OasDialect } from './dialect.ts'
|
|
6
|
+
import { createDiscriminantNode, findDiscriminator } from './discriminator.ts'
|
|
6
7
|
import { getOperationId, getOperations, getRequestContentType, getResponseByStatusCode, getResponseStatusCodes } from './operation.ts'
|
|
7
8
|
import {
|
|
8
9
|
buildSchemaNode,
|
|
@@ -66,11 +67,17 @@ type SchemaContext = {
|
|
|
66
67
|
* that is not convertible falls through to plain `type` handling).
|
|
67
68
|
*/
|
|
68
69
|
type SchemaRule = {
|
|
69
|
-
/**
|
|
70
|
+
/**
|
|
71
|
+
* Identifies the rule when reading the table or debugging which branch ran.
|
|
72
|
+
*/
|
|
70
73
|
name: string
|
|
71
|
-
/**
|
|
74
|
+
/**
|
|
75
|
+
* Returns `true` when this rule is responsible for the given context.
|
|
76
|
+
*/
|
|
72
77
|
match: (context: SchemaContext) => boolean
|
|
73
|
-
/**
|
|
78
|
+
/**
|
|
79
|
+
* Produces a node for the context, or `null` to fall through to the next rule.
|
|
80
|
+
*/
|
|
74
81
|
convert: (context: SchemaContext) => ast.SchemaNode | null
|
|
75
82
|
}
|
|
76
83
|
|
|
@@ -78,9 +85,9 @@ type SchemaRule = {
|
|
|
78
85
|
* Normalizes malformed `{ type: 'array', enum: [...] }` schemas by moving enum values into items.
|
|
79
86
|
*
|
|
80
87
|
* This pattern violates the OpenAPI spec but appears in real specs. The fix moves enum values
|
|
81
|
-
* from the array to its items sub-schema,
|
|
88
|
+
* from the array to its items sub-schema, so they are valid for downstream processing.
|
|
82
89
|
*
|
|
83
|
-
* @note
|
|
90
|
+
* @note A defensive measure for non-compliant specs.
|
|
84
91
|
*/
|
|
85
92
|
function normalizeArrayEnum(schema: SchemaObject): SchemaObject {
|
|
86
93
|
const isItemsObject = typeof schema.items === 'object' && !Array.isArray(schema.items)
|
|
@@ -98,7 +105,7 @@ function normalizeArrayEnum(schema: SchemaObject): SchemaObject {
|
|
|
98
105
|
*
|
|
99
106
|
* Returns closures that share mutable state (`resolvingRefs` set for cycle detection).
|
|
100
107
|
* Each converter branch (`convertRef`, `convertAllOf`, etc.) mutually recursively calls `parseSchema`,
|
|
101
|
-
*
|
|
108
|
+
* which works because function declarations hoist.
|
|
102
109
|
*
|
|
103
110
|
* @internal
|
|
104
111
|
*/
|
|
@@ -151,7 +158,7 @@ export function createSchemaParser(ctx: OasParserContext, dialect: OasDialect =
|
|
|
151
158
|
resolvedSchema = resolvedRefCache.get(refPath) ?? null
|
|
152
159
|
}
|
|
153
160
|
|
|
154
|
-
return ast.createSchema({
|
|
161
|
+
return ast.factory.createSchema({
|
|
155
162
|
...buildSchemaNode(schema, name, nullable, defaultValue),
|
|
156
163
|
type: 'ref',
|
|
157
164
|
name: extractRefName(schema.$ref!),
|
|
@@ -176,7 +183,7 @@ export function createSchemaParser(ctx: OasParserContext, dialect: OasDialect =
|
|
|
176
183
|
const mergedNullable = nullable || memberNode.nullable || undefined
|
|
177
184
|
const mergedDefault = schema.default === null && mergedNullable ? undefined : (schema.default ?? memberNode.default)
|
|
178
185
|
|
|
179
|
-
return ast.createSchema({
|
|
186
|
+
return ast.factory.createSchema({
|
|
180
187
|
...memberNodeProps,
|
|
181
188
|
name,
|
|
182
189
|
title: schema.title ?? memberNode.title,
|
|
@@ -264,10 +271,10 @@ export function createSchemaParser(ctx: OasParserContext, dialect: OasDialect =
|
|
|
264
271
|
}
|
|
265
272
|
|
|
266
273
|
for (const { propertyName, value } of filteredDiscriminantValues) {
|
|
267
|
-
allOfMembers.push(
|
|
274
|
+
allOfMembers.push(createDiscriminantNode({ propertyName, value }))
|
|
268
275
|
}
|
|
269
276
|
|
|
270
|
-
return ast.createSchema({
|
|
277
|
+
return ast.factory.createSchema({
|
|
271
278
|
type: 'intersection',
|
|
272
279
|
members: [...ast.mergeAdjacentObjectsLazy(allOfMembers.slice(0, syntheticStart)), ...ast.mergeAdjacentObjectsLazy(allOfMembers.slice(syntheticStart))],
|
|
273
280
|
...buildSchemaNode(schema, name, nullable, defaultValue),
|
|
@@ -286,7 +293,7 @@ export function createSchemaParser(ctx: OasParserContext, dialect: OasDialect =
|
|
|
286
293
|
return null
|
|
287
294
|
}
|
|
288
295
|
|
|
289
|
-
return ast.createSchema({
|
|
296
|
+
return ast.factory.createSchema({
|
|
290
297
|
type: 'object',
|
|
291
298
|
primitive: 'object',
|
|
292
299
|
properties: [discriminatorProperty],
|
|
@@ -332,12 +339,12 @@ export function createSchemaParser(ctx: OasParserContext, dialect: OasDialect =
|
|
|
332
339
|
)
|
|
333
340
|
: undefined
|
|
334
341
|
|
|
335
|
-
return ast.createSchema({
|
|
342
|
+
return ast.factory.createSchema({
|
|
336
343
|
type: 'intersection',
|
|
337
344
|
members: [
|
|
338
345
|
memberNode,
|
|
339
346
|
narrowedDiscriminatorNode ??
|
|
340
|
-
|
|
347
|
+
createDiscriminantNode({
|
|
341
348
|
propertyName: discriminator.propertyName,
|
|
342
349
|
value: discriminatorValue,
|
|
343
350
|
}),
|
|
@@ -345,7 +352,7 @@ export function createSchemaParser(ctx: OasParserContext, dialect: OasDialect =
|
|
|
345
352
|
})
|
|
346
353
|
})
|
|
347
354
|
|
|
348
|
-
const unionNode = ast.createSchema({
|
|
355
|
+
const unionNode = ast.factory.createSchema({
|
|
349
356
|
type: 'union',
|
|
350
357
|
...unionBase,
|
|
351
358
|
members,
|
|
@@ -355,14 +362,14 @@ export function createSchemaParser(ctx: OasParserContext, dialect: OasDialect =
|
|
|
355
362
|
return unionNode
|
|
356
363
|
}
|
|
357
364
|
|
|
358
|
-
return ast.createSchema({
|
|
365
|
+
return ast.factory.createSchema({
|
|
359
366
|
type: 'intersection',
|
|
360
367
|
...buildSchemaNode(schema, name, nullable, defaultValue),
|
|
361
368
|
members: [unionNode, sharedPropertiesNode],
|
|
362
369
|
})
|
|
363
370
|
}
|
|
364
371
|
|
|
365
|
-
return ast.createSchema({
|
|
372
|
+
return ast.factory.createSchema({
|
|
366
373
|
type: 'union',
|
|
367
374
|
...unionBase,
|
|
368
375
|
members: ast.simplifyUnion(unionMembers.map((s) => parseSchema({ schema: s as SchemaObject, name }, rawOptions))),
|
|
@@ -376,7 +383,7 @@ export function createSchemaParser(ctx: OasParserContext, dialect: OasDialect =
|
|
|
376
383
|
const constValue = schema.const
|
|
377
384
|
|
|
378
385
|
if (constValue === null) {
|
|
379
|
-
return ast.createSchema({
|
|
386
|
+
return ast.factory.createSchema({
|
|
380
387
|
type: 'null',
|
|
381
388
|
primitive: 'null',
|
|
382
389
|
name,
|
|
@@ -388,7 +395,7 @@ export function createSchemaParser(ctx: OasParserContext, dialect: OasDialect =
|
|
|
388
395
|
}
|
|
389
396
|
|
|
390
397
|
const constPrimitive = getPrimitiveType(typeof constValue === 'number' ? 'number' : typeof constValue === 'boolean' ? 'boolean' : 'string')
|
|
391
|
-
return ast.createSchema({
|
|
398
|
+
return ast.factory.createSchema({
|
|
392
399
|
type: 'enum',
|
|
393
400
|
primitive: constPrimitive,
|
|
394
401
|
enumValues: [constValue as string | number | boolean],
|
|
@@ -404,7 +411,7 @@ export function createSchemaParser(ctx: OasParserContext, dialect: OasDialect =
|
|
|
404
411
|
const base = buildSchemaNode(schema, name, nullable, defaultValue)
|
|
405
412
|
|
|
406
413
|
if (schema.format === 'int64') {
|
|
407
|
-
return ast.createSchema({
|
|
414
|
+
return ast.factory.createSchema({
|
|
408
415
|
type: options.integerType === 'bigint' ? 'bigint' : 'integer',
|
|
409
416
|
primitive: 'integer',
|
|
410
417
|
...base,
|
|
@@ -420,7 +427,7 @@ export function createSchemaParser(ctx: OasParserContext, dialect: OasDialect =
|
|
|
420
427
|
if (!dateType) return null
|
|
421
428
|
|
|
422
429
|
if (dateType.type === 'datetime') {
|
|
423
|
-
return ast.createSchema({
|
|
430
|
+
return ast.factory.createSchema({
|
|
424
431
|
...base,
|
|
425
432
|
primitive: 'string' as const,
|
|
426
433
|
type: 'datetime',
|
|
@@ -428,7 +435,7 @@ export function createSchemaParser(ctx: OasParserContext, dialect: OasDialect =
|
|
|
428
435
|
local: dateType.local,
|
|
429
436
|
})
|
|
430
437
|
}
|
|
431
|
-
return ast.createSchema({
|
|
438
|
+
return ast.factory.createSchema({
|
|
432
439
|
...base,
|
|
433
440
|
primitive: 'string' as const,
|
|
434
441
|
type: dateType.type,
|
|
@@ -442,14 +449,14 @@ export function createSchemaParser(ctx: OasParserContext, dialect: OasDialect =
|
|
|
442
449
|
const specialPrimitive: ast.PrimitiveSchemaType = specialType === 'number' || specialType === 'integer' || specialType === 'bigint' ? specialType : 'string'
|
|
443
450
|
|
|
444
451
|
if (specialType === 'number' || specialType === 'integer' || specialType === 'bigint') {
|
|
445
|
-
return ast.createSchema({
|
|
452
|
+
return ast.factory.createSchema({
|
|
446
453
|
...base,
|
|
447
454
|
primitive: specialPrimitive,
|
|
448
455
|
type: specialType,
|
|
449
456
|
})
|
|
450
457
|
}
|
|
451
458
|
if (specialType === 'url') {
|
|
452
|
-
return ast.createSchema({
|
|
459
|
+
return ast.factory.createSchema({
|
|
453
460
|
...base,
|
|
454
461
|
primitive: 'string' as const,
|
|
455
462
|
type: 'url',
|
|
@@ -458,21 +465,21 @@ export function createSchemaParser(ctx: OasParserContext, dialect: OasDialect =
|
|
|
458
465
|
})
|
|
459
466
|
}
|
|
460
467
|
if (specialType === 'ipv4') {
|
|
461
|
-
return ast.createSchema({
|
|
468
|
+
return ast.factory.createSchema({
|
|
462
469
|
...base,
|
|
463
470
|
primitive: 'string' as const,
|
|
464
471
|
type: 'ipv4',
|
|
465
472
|
})
|
|
466
473
|
}
|
|
467
474
|
if (specialType === 'ipv6') {
|
|
468
|
-
return ast.createSchema({
|
|
475
|
+
return ast.factory.createSchema({
|
|
469
476
|
...base,
|
|
470
477
|
primitive: 'string' as const,
|
|
471
478
|
type: 'ipv6',
|
|
472
479
|
})
|
|
473
480
|
}
|
|
474
481
|
if (specialType === 'uuid' || specialType === 'email') {
|
|
475
|
-
return ast.createSchema({
|
|
482
|
+
return ast.factory.createSchema({
|
|
476
483
|
...base,
|
|
477
484
|
primitive: 'string' as const,
|
|
478
485
|
type: specialType,
|
|
@@ -481,7 +488,7 @@ export function createSchemaParser(ctx: OasParserContext, dialect: OasDialect =
|
|
|
481
488
|
})
|
|
482
489
|
}
|
|
483
490
|
|
|
484
|
-
return ast.createSchema({
|
|
491
|
+
return ast.factory.createSchema({
|
|
485
492
|
...base,
|
|
486
493
|
primitive: specialPrimitive,
|
|
487
494
|
type: specialType as ast.ScalarSchemaType,
|
|
@@ -503,7 +510,7 @@ export function createSchemaParser(ctx: OasParserContext, dialect: OasDialect =
|
|
|
503
510
|
// render as `never` (plugin-ts) / invalid `z.enum([])` (plugin-zod). Mirror the `const: null`
|
|
504
511
|
// branch so it renders as a clean `null` (not `z.null().nullable()`).
|
|
505
512
|
if (nullInEnum && filteredValues.length === 0) {
|
|
506
|
-
return ast.createSchema({
|
|
513
|
+
return ast.factory.createSchema({
|
|
507
514
|
type: 'null',
|
|
508
515
|
primitive: 'null',
|
|
509
516
|
name,
|
|
@@ -543,7 +550,7 @@ export function createSchemaParser(ctx: OasParserContext, dialect: OasDialect =
|
|
|
543
550
|
const uniqueValues = [...new Set(filteredValues)]
|
|
544
551
|
const seenNames = new Set<string>()
|
|
545
552
|
|
|
546
|
-
return ast.createSchema({
|
|
553
|
+
return ast.factory.createSchema({
|
|
547
554
|
...enumBase,
|
|
548
555
|
primitive: enumPrimitiveType,
|
|
549
556
|
namedEnumValues: uniqueValues
|
|
@@ -560,7 +567,7 @@ export function createSchemaParser(ctx: OasParserContext, dialect: OasDialect =
|
|
|
560
567
|
})
|
|
561
568
|
}
|
|
562
569
|
|
|
563
|
-
return ast.createSchema({
|
|
570
|
+
return ast.factory.createSchema({
|
|
564
571
|
...enumBase,
|
|
565
572
|
enumValues: [...new Set(filteredValues)],
|
|
566
573
|
})
|
|
@@ -590,7 +597,7 @@ export function createSchemaParser(ctx: OasParserContext, dialect: OasDialect =
|
|
|
590
597
|
return node
|
|
591
598
|
})()
|
|
592
599
|
|
|
593
|
-
return ast.createProperty({
|
|
600
|
+
return ast.factory.createProperty({
|
|
594
601
|
name: propName,
|
|
595
602
|
schema: {
|
|
596
603
|
...schemaNode,
|
|
@@ -608,7 +615,7 @@ export function createSchemaParser(ctx: OasParserContext, dialect: OasDialect =
|
|
|
608
615
|
if (additionalProperties && Object.keys(additionalProperties).length > 0) {
|
|
609
616
|
return parseSchema({ schema: additionalProperties as SchemaObject }, rawOptions)
|
|
610
617
|
}
|
|
611
|
-
if (additionalProperties) return ast.createSchema({ type: typeOptionMap.get(options.unknownType)! })
|
|
618
|
+
if (additionalProperties) return ast.factory.createSchema({ type: typeOptionMap.get(options.unknownType)! })
|
|
612
619
|
return undefined
|
|
613
620
|
})()
|
|
614
621
|
|
|
@@ -619,7 +626,7 @@ export function createSchemaParser(ctx: OasParserContext, dialect: OasDialect =
|
|
|
619
626
|
Object.entries(rawPatternProperties).map(([pattern, patternSchema]) => [
|
|
620
627
|
pattern,
|
|
621
628
|
patternSchema === true || (typeof patternSchema === 'object' && Object.keys(patternSchema).length === 0)
|
|
622
|
-
? ast.createSchema({
|
|
629
|
+
? ast.factory.createSchema({
|
|
623
630
|
type: typeOptionMap.get(options.unknownType)!,
|
|
624
631
|
})
|
|
625
632
|
: parseSchema({ schema: patternSchema as SchemaObject }, rawOptions),
|
|
@@ -627,7 +634,7 @@ export function createSchemaParser(ctx: OasParserContext, dialect: OasDialect =
|
|
|
627
634
|
)
|
|
628
635
|
: undefined
|
|
629
636
|
|
|
630
|
-
const objectNode: ast.SchemaNode = ast.createSchema({
|
|
637
|
+
const objectNode: ast.SchemaNode = ast.factory.createSchema({
|
|
631
638
|
type: 'object',
|
|
632
639
|
primitive: 'object',
|
|
633
640
|
properties,
|
|
@@ -658,9 +665,9 @@ export function createSchemaParser(ctx: OasParserContext, dialect: OasDialect =
|
|
|
658
665
|
*/
|
|
659
666
|
function convertTuple({ schema, name, nullable, defaultValue, rawOptions }: SchemaContext): ast.SchemaNode {
|
|
660
667
|
const tupleItems = (schema.prefixItems ?? []).map((item) => parseSchema({ schema: item as SchemaObject }, rawOptions))
|
|
661
|
-
const rest = schema.items ? parseSchema({ schema: schema.items as SchemaObject }, rawOptions) : ast.createSchema({ type: 'any' })
|
|
668
|
+
const rest = schema.items ? parseSchema({ schema: schema.items as SchemaObject }, rawOptions) : ast.factory.createSchema({ type: 'any' })
|
|
662
669
|
|
|
663
|
-
return ast.createSchema({
|
|
670
|
+
return ast.factory.createSchema({
|
|
664
671
|
type: 'tuple',
|
|
665
672
|
primitive: 'array',
|
|
666
673
|
items: tupleItems,
|
|
@@ -679,7 +686,7 @@ export function createSchemaParser(ctx: OasParserContext, dialect: OasDialect =
|
|
|
679
686
|
const itemName = rawItems?.enum?.length && name ? enumPropName(null, name, options.enumSuffix) : name
|
|
680
687
|
const items = rawItems ? [parseSchema({ schema: rawItems, name: itemName }, rawOptions)] : []
|
|
681
688
|
|
|
682
|
-
return ast.createSchema({
|
|
689
|
+
return ast.factory.createSchema({
|
|
683
690
|
type: 'array',
|
|
684
691
|
primitive: 'array',
|
|
685
692
|
items,
|
|
@@ -694,7 +701,7 @@ export function createSchemaParser(ctx: OasParserContext, dialect: OasDialect =
|
|
|
694
701
|
* Converts a `type: 'string'` schema into a `StringSchemaNode`.
|
|
695
702
|
*/
|
|
696
703
|
function convertString({ schema, name, nullable, defaultValue }: SchemaContext): ast.SchemaNode {
|
|
697
|
-
return ast.createSchema({
|
|
704
|
+
return ast.factory.createSchema({
|
|
698
705
|
type: 'string',
|
|
699
706
|
primitive: 'string',
|
|
700
707
|
min: schema.minLength,
|
|
@@ -708,7 +715,7 @@ export function createSchemaParser(ctx: OasParserContext, dialect: OasDialect =
|
|
|
708
715
|
* Converts a `type: 'number'` or `type: 'integer'` schema.
|
|
709
716
|
*/
|
|
710
717
|
function convertNumeric({ schema, name, nullable, defaultValue }: SchemaContext, type: 'number' | 'integer'): ast.SchemaNode {
|
|
711
|
-
return ast.createSchema({
|
|
718
|
+
return ast.factory.createSchema({
|
|
712
719
|
type,
|
|
713
720
|
primitive: type,
|
|
714
721
|
min: schema.minimum,
|
|
@@ -724,7 +731,7 @@ export function createSchemaParser(ctx: OasParserContext, dialect: OasDialect =
|
|
|
724
731
|
* Converts a `type: 'boolean'` schema.
|
|
725
732
|
*/
|
|
726
733
|
function convertBoolean({ schema, name, nullable, defaultValue }: SchemaContext): ast.SchemaNode {
|
|
727
|
-
return ast.createSchema({
|
|
734
|
+
return ast.factory.createSchema({
|
|
728
735
|
type: 'boolean',
|
|
729
736
|
primitive: 'boolean',
|
|
730
737
|
...buildSchemaNode(schema, name, nullable, defaultValue),
|
|
@@ -735,7 +742,7 @@ export function createSchemaParser(ctx: OasParserContext, dialect: OasDialect =
|
|
|
735
742
|
* Converts an explicit `type: 'null'` schema.
|
|
736
743
|
*/
|
|
737
744
|
function convertNull({ schema, name, nullable }: SchemaContext): ast.SchemaNode {
|
|
738
|
-
return ast.createSchema({
|
|
745
|
+
return ast.factory.createSchema({
|
|
739
746
|
type: 'null',
|
|
740
747
|
primitive: 'null',
|
|
741
748
|
name,
|
|
@@ -752,7 +759,7 @@ export function createSchemaParser(ctx: OasParserContext, dialect: OasDialect =
|
|
|
752
759
|
* into a `blob` node.
|
|
753
760
|
*/
|
|
754
761
|
function convertBlob({ schema, name, nullable, defaultValue }: SchemaContext): ast.SchemaNode {
|
|
755
|
-
return ast.createSchema({
|
|
762
|
+
return ast.factory.createSchema({
|
|
756
763
|
type: 'blob',
|
|
757
764
|
primitive: 'string',
|
|
758
765
|
...buildSchemaNode(schema, name, nullable, defaultValue),
|
|
@@ -771,7 +778,7 @@ export function createSchemaParser(ctx: OasParserContext, dialect: OasDialect =
|
|
|
771
778
|
if (nonNullTypes.length <= 1) return null
|
|
772
779
|
|
|
773
780
|
const arrayNullable = types.includes('null') || nullable || undefined
|
|
774
|
-
return ast.createSchema({
|
|
781
|
+
return ast.factory.createSchema({
|
|
775
782
|
type: 'union',
|
|
776
783
|
members: nonNullTypes.map((t) => parseSchema({ schema: { ...schema, type: t } as SchemaObject, name }, rawOptions)),
|
|
777
784
|
...buildSchemaNode(schema, name, arrayNullable, defaultValue),
|
|
@@ -859,7 +866,7 @@ export function createSchemaParser(ctx: OasParserContext, dialect: OasDialect =
|
|
|
859
866
|
}
|
|
860
867
|
|
|
861
868
|
const emptyType = typeOptionMap.get(options.emptySchemaType)!
|
|
862
|
-
return ast.createSchema({
|
|
869
|
+
return ast.factory.createSchema({
|
|
863
870
|
type: emptyType as ast.ScalarSchemaType,
|
|
864
871
|
name,
|
|
865
872
|
title: schema.title,
|
|
@@ -878,9 +885,9 @@ export function createSchemaParser(ctx: OasParserContext, dialect: OasDialect =
|
|
|
878
885
|
|
|
879
886
|
const schema: ast.SchemaNode = param['schema']
|
|
880
887
|
? parseSchema({ schema: param['schema'] as SchemaObject, name: schemaName }, options)
|
|
881
|
-
: ast.createSchema({ type: typeOptionMap.get(options.unknownType)! })
|
|
888
|
+
: ast.factory.createSchema({ type: typeOptionMap.get(options.unknownType)! })
|
|
882
889
|
|
|
883
|
-
return ast.createParameter({
|
|
890
|
+
return ast.factory.createParameter({
|
|
884
891
|
name: paramName,
|
|
885
892
|
in: param['in'] as ast.ParameterLocation,
|
|
886
893
|
schema: {
|
|
@@ -996,7 +1003,7 @@ export function createSchemaParser(ctx: OasParserContext, dialect: OasDialect =
|
|
|
996
1003
|
const node =
|
|
997
1004
|
raw && Object.keys(raw).length > 0
|
|
998
1005
|
? parseSchema({ schema: raw, name: responseName }, options)
|
|
999
|
-
: ast.createSchema({ type: typeOptionMap.get(options.emptySchemaType)! })
|
|
1006
|
+
: ast.factory.createSchema({ type: typeOptionMap.get(options.emptySchemaType)! })
|
|
1000
1007
|
return { schema: node, keysToOmit: collectPropertyKeysByFlag(raw, 'writeOnly') }
|
|
1001
1008
|
}
|
|
1002
1009
|
|
|
@@ -1011,7 +1018,7 @@ export function createSchemaParser(ctx: OasParserContext, dialect: OasDialect =
|
|
|
1011
1018
|
content.push({ contentType: getRequestContentType({ document, operation }) || 'application/json', ...parseEntrySchema(ctx.contentType) })
|
|
1012
1019
|
}
|
|
1013
1020
|
|
|
1014
|
-
return ast.createResponse({
|
|
1021
|
+
return ast.factory.createResponse({
|
|
1015
1022
|
statusCode: statusCode as ast.StatusCode,
|
|
1016
1023
|
description,
|
|
1017
1024
|
content,
|
|
@@ -1027,7 +1034,7 @@ export function createSchemaParser(ctx: OasParserContext, dialect: OasDialect =
|
|
|
1027
1034
|
return typeof fallback === 'string' ? fallback : undefined
|
|
1028
1035
|
}
|
|
1029
1036
|
|
|
1030
|
-
return ast.createOperation({
|
|
1037
|
+
return ast.factory.createOperation({
|
|
1031
1038
|
operationId,
|
|
1032
1039
|
protocol: 'http',
|
|
1033
1040
|
method: operation.method.toUpperCase() as ast.HttpMethod,
|
|
@@ -1048,10 +1055,11 @@ export function createSchemaParser(ctx: OasParserContext, dialect: OasDialect =
|
|
|
1048
1055
|
/**
|
|
1049
1056
|
* Parses a single OpenAPI `SchemaObject` into a `SchemaNode`.
|
|
1050
1057
|
*
|
|
1051
|
-
*
|
|
1052
|
-
*
|
|
1058
|
+
* Reach for this when you only need one schema, not the whole spec. To parse a full spec
|
|
1059
|
+
* with its operations and schemas, call `parseOas()`.
|
|
1053
1060
|
*
|
|
1054
|
-
* @note
|
|
1061
|
+
* @note Internal state tracks `$ref` paths under resolution, so circular schemas stop
|
|
1062
|
+
* recursing instead of looping.
|
|
1055
1063
|
*
|
|
1056
1064
|
* @example
|
|
1057
1065
|
* ```ts
|
|
@@ -1072,7 +1080,8 @@ export function parseSchema(
|
|
|
1072
1080
|
* Parses an OpenAPI specification into Kubb's universal `InputNode` AST.
|
|
1073
1081
|
*
|
|
1074
1082
|
* This is the main entry point for `@kubb/adapter-oas`. It converts OpenAPI/Swagger specs into a spec-agnostic tree
|
|
1075
|
-
* that downstream plugins (`plugin-ts`, `plugin-zod`, etc.) consume for code generation. No code is generated here
|
|
1083
|
+
* that downstream plugins (`plugin-ts`, `plugin-zod`, etc.) consume for code generation. No code is generated here.
|
|
1084
|
+
* The tree is a pure data structure of all schemas and operations.
|
|
1076
1085
|
*
|
|
1077
1086
|
* Returns the AST root and a `nameMapping` for resolving schema references.
|
|
1078
1087
|
*
|
|
@@ -1105,7 +1114,7 @@ export function parseOas(
|
|
|
1105
1114
|
.map((operation) => _parseOperation(mergedOptions, operation))
|
|
1106
1115
|
.filter((op): op is ast.OperationNode => op !== null)
|
|
1107
1116
|
|
|
1108
|
-
const root = ast.createInput({ schemas, operations })
|
|
1117
|
+
const root = ast.factory.createInput({ schemas, operations })
|
|
1109
1118
|
|
|
1110
1119
|
return { root, nameMapping }
|
|
1111
1120
|
}
|
package/src/resolvers.ts
CHANGED
|
@@ -470,7 +470,7 @@ export function getSchemas(document: Document, { contentType }: GetSchemasOption
|
|
|
470
470
|
|
|
471
471
|
/**
|
|
472
472
|
* Resolves the AST type descriptor for a date/time format, honoring the `dateType` option.
|
|
473
|
-
* Returns `null` when `dateType: false`,
|
|
473
|
+
* Returns `null` when `dateType: false`, so the format falls through to `string`.
|
|
474
474
|
*/
|
|
475
475
|
export function getDateType(
|
|
476
476
|
options: ast.ParserOptions,
|
package/src/stream.ts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { containsCircularRef, findCircularSchemas } from '@kubb/ast/utils'
|
|
1
2
|
import { ast } from '@kubb/core'
|
|
2
3
|
import { SCHEMA_REF_PREFIX } from './constants.ts'
|
|
3
4
|
import { buildDiscriminatorChildMap, patchDiscriminatorNode } from './discriminator.ts'
|
|
@@ -22,7 +23,7 @@ export type PreScanResult = {
|
|
|
22
23
|
*
|
|
23
24
|
* Only enums and objects are candidates, and object shapes that reference a circular schema are
|
|
24
25
|
* rejected to avoid hoisting recursive structures. Inline shapes reuse their context-derived
|
|
25
|
-
* name (collision-resolved against existing component names)
|
|
26
|
+
* name (collision-resolved against existing component names). Shapes without a name stay inline.
|
|
26
27
|
*/
|
|
27
28
|
function createDedupePlan({
|
|
28
29
|
schemaNodes,
|
|
@@ -44,7 +45,7 @@ function createDedupePlan({
|
|
|
44
45
|
if (node.type !== 'object') return false
|
|
45
46
|
// Skip object shapes that are part of a circular chain, hoisting them would break the cycle.
|
|
46
47
|
if (node.name && circularSchemas.has(node.name)) return false
|
|
47
|
-
return !
|
|
48
|
+
return !containsCircularRef(node, { circularSchemas })
|
|
48
49
|
},
|
|
49
50
|
nameFor: (node) => {
|
|
50
51
|
const base = node.name
|
|
@@ -149,7 +150,7 @@ export function preScan({
|
|
|
149
150
|
}
|
|
150
151
|
}
|
|
151
152
|
|
|
152
|
-
const circularNames = [...
|
|
153
|
+
const circularNames = [...findCircularSchemas(allNodes)]
|
|
153
154
|
const discriminatorChildMap = discriminatorParentNodes.length > 0 ? buildDiscriminatorChildMap(discriminatorParentNodes) : null
|
|
154
155
|
|
|
155
156
|
let dedupePlan: ast.DedupePlan | null = null
|
|
@@ -217,14 +218,14 @@ export function createInputStream({
|
|
|
217
218
|
meta: ast.InputMeta
|
|
218
219
|
}): ast.InputNode<true> {
|
|
219
220
|
// Rewrites a top-level schema against the dedupe plan: a structurally identical sibling
|
|
220
|
-
// becomes a `ref` alias to the canonical one (keeping its own name)
|
|
221
|
+
// becomes a `ref` alias to the canonical one (keeping its own name). Otherwise nested
|
|
221
222
|
// duplicates are collapsed while the schema's own root is preserved.
|
|
222
223
|
const rewriteTopLevelSchema = (node: ast.SchemaNode): ast.SchemaNode => {
|
|
223
224
|
if (!dedupePlan) return node
|
|
224
225
|
|
|
225
226
|
const canonical = dedupePlan.canonicalBySignature.get(ast.signatureOf(node))
|
|
226
227
|
if (canonical && canonical.name !== node.name) {
|
|
227
|
-
return ast.createSchema({
|
|
228
|
+
return ast.factory.createSchema({
|
|
228
229
|
type: 'ref',
|
|
229
230
|
name: node.name ?? null,
|
|
230
231
|
ref: canonical.ref,
|
|
@@ -278,5 +279,5 @@ export function createInputStream({
|
|
|
278
279
|
},
|
|
279
280
|
}
|
|
280
281
|
|
|
281
|
-
return ast.
|
|
282
|
+
return ast.factory.createInput({ stream: true, schemas: schemasIterable, operations: operationsIterable, meta })
|
|
282
283
|
}
|