@kubb/adapter-oas 5.0.0-beta.53 → 5.0.0-beta.55
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 +393 -258
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.ts +767 -10
- package/dist/index.js +393 -255
- package/dist/index.js.map +1 -1
- package/package.json +7 -7
- package/src/adapter.ts +13 -21
- package/src/bundler.ts +2 -2
- package/src/constants.ts +6 -0
- package/src/factory.ts +21 -25
- package/src/mime.ts +22 -0
- package/src/operation.ts +194 -0
- package/src/parser.ts +42 -30
- package/src/resolvers.ts +8 -9
- package/src/stream.ts +27 -23
- package/src/types.ts +46 -19
package/src/resolvers.ts
CHANGED
|
@@ -1,13 +1,12 @@
|
|
|
1
1
|
import { pascalCase } from '@internals/utils'
|
|
2
2
|
import { Diagnostics } from '@kubb/core'
|
|
3
3
|
import type { ast } from '@kubb/core'
|
|
4
|
-
import type { ParameterObject, ServerObject } from 'oas/types'
|
|
5
|
-
import { isRef } from 'oas/types'
|
|
6
|
-
import { matchesMimeType } from 'oas/utils'
|
|
7
4
|
import { formatMap, SCHEMA_REF_PREFIX, specialCasedFormats, structuralKeys } from './constants.ts'
|
|
8
5
|
import { isReference } from './guards.ts'
|
|
6
|
+
import { isJsonMimeType } from './mime.ts'
|
|
7
|
+
import { getRequestContent, getResponseByStatusCode } from './operation.ts'
|
|
9
8
|
import { dereferenceWithRef, resolveRef } from './refs.ts'
|
|
10
|
-
import type { ContentType, Document, MediaTypeObject, Operation, ResponseObject, SchemaObject } from './types.ts'
|
|
9
|
+
import type { ContentType, Document, MediaTypeObject, Operation, ParameterObject, ResponseObject, SchemaObject, ServerObject } from './types.ts'
|
|
11
10
|
|
|
12
11
|
/**
|
|
13
12
|
* Replaces `{variable}` placeholders in an OpenAPI server URL with provided values.
|
|
@@ -133,7 +132,7 @@ function getResponseBody(responseBody: boolean | ResponseObject, contentType?: s
|
|
|
133
132
|
let availableContentType: string | undefined
|
|
134
133
|
const contentTypes = Object.keys(body.content)
|
|
135
134
|
for (const mt of contentTypes) {
|
|
136
|
-
if (
|
|
135
|
+
if (isJsonMimeType(mt)) {
|
|
137
136
|
availableContentType = mt
|
|
138
137
|
break
|
|
139
138
|
}
|
|
@@ -172,7 +171,7 @@ export function getResponseSchema(document: Document, operation: Operation, stat
|
|
|
172
171
|
}
|
|
173
172
|
}
|
|
174
173
|
|
|
175
|
-
const responseBody = getResponseBody(
|
|
174
|
+
const responseBody = getResponseBody(getResponseByStatusCode({ document, operation, statusCode }), options.contentType)
|
|
176
175
|
|
|
177
176
|
if (responseBody === false) {
|
|
178
177
|
return {}
|
|
@@ -200,7 +199,7 @@ export function getRequestSchema(document: Document, operation: Operation, optio
|
|
|
200
199
|
operation.schema.requestBody = dereferenceWithRef(document, operation.schema.requestBody)
|
|
201
200
|
}
|
|
202
201
|
|
|
203
|
-
const requestBody = operation
|
|
202
|
+
const requestBody = getRequestContent({ document, operation, mediaType: options.contentType })
|
|
204
203
|
|
|
205
204
|
if (requestBody === false) {
|
|
206
205
|
return null
|
|
@@ -271,7 +270,7 @@ export function flattenSchema(schema: SchemaObject | null): SchemaObject | null
|
|
|
271
270
|
if (!schema?.allOf || schema.allOf.length === 0) return schema ?? null
|
|
272
271
|
|
|
273
272
|
const allOfFragments = schema.allOf as Array<SchemaObject>
|
|
274
|
-
if (allOfFragments.some((item) =>
|
|
273
|
+
if (allOfFragments.some((item) => isReference(item))) return schema
|
|
275
274
|
if (allOfFragments.some(hasStructuralKeywords)) return schema
|
|
276
275
|
|
|
277
276
|
const merged: SchemaObject = { ...schema }
|
|
@@ -575,7 +574,7 @@ export function getResponseBodyContentTypes(document: Document, operation: Opera
|
|
|
575
574
|
}
|
|
576
575
|
}
|
|
577
576
|
|
|
578
|
-
const responseObj =
|
|
577
|
+
const responseObj = getResponseByStatusCode({ document, operation, statusCode })
|
|
579
578
|
if (!responseObj || typeof responseObj !== 'object' || isReference(responseObj)) return []
|
|
580
579
|
|
|
581
580
|
const body = responseObj as { content?: Record<string, unknown> }
|
package/src/stream.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { ast } from '@kubb/core'
|
|
2
|
-
import type BaseOas from 'oas'
|
|
3
2
|
import { SCHEMA_REF_PREFIX } from './constants.ts'
|
|
4
3
|
import { buildDiscriminatorChildMap, patchDiscriminatorNode } from './discriminator.ts'
|
|
4
|
+
import { getOperations } from './operation.ts'
|
|
5
5
|
import type { SchemaParser } from './parser.ts'
|
|
6
6
|
import { resolveServerUrl } from './resolvers.ts'
|
|
7
7
|
import { reportSchemaDiagnostics } from './schemaDiagnostics.ts'
|
|
@@ -116,7 +116,7 @@ export function preScan({
|
|
|
116
116
|
schemas,
|
|
117
117
|
parseSchema,
|
|
118
118
|
parseOperation,
|
|
119
|
-
|
|
119
|
+
document,
|
|
120
120
|
parserOptions,
|
|
121
121
|
discriminator,
|
|
122
122
|
dedupe,
|
|
@@ -124,7 +124,7 @@ export function preScan({
|
|
|
124
124
|
schemas: Record<string, SchemaObject>
|
|
125
125
|
parseSchema: (entry: { schema: SchemaObject; name: string }, options: ast.ParserOptions) => ast.SchemaNode
|
|
126
126
|
parseOperation: SchemaParser['parseOperation']
|
|
127
|
-
|
|
127
|
+
document: Document
|
|
128
128
|
parserOptions: ast.ParserOptions
|
|
129
129
|
discriminator: AdapterOas['options']['discriminator']
|
|
130
130
|
dedupe: boolean
|
|
@@ -157,12 +157,9 @@ export function preScan({
|
|
|
157
157
|
// One extra parse pass over operations so duplicates in request/response bodies are seen.
|
|
158
158
|
// Reuses the already-parsed `allNodes` for schemas, no second schema parse.
|
|
159
159
|
const operationNodes: Array<ast.OperationNode> = []
|
|
160
|
-
for (const
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
const operationNode = parseOperation(parserOptions, operation)
|
|
164
|
-
if (operationNode) operationNodes.push(operationNode)
|
|
165
|
-
}
|
|
160
|
+
for (const operation of getOperations(document)) {
|
|
161
|
+
const operationNode = parseOperation(parserOptions, operation)
|
|
162
|
+
if (operationNode) operationNodes.push(operationNode)
|
|
166
163
|
}
|
|
167
164
|
|
|
168
165
|
dedupePlan = createDedupePlan({ schemaNodes: allNodes, operationNodes, schemaNames: Object.keys(schemas), circularNames })
|
|
@@ -172,11 +169,16 @@ export function preScan({
|
|
|
172
169
|
}
|
|
173
170
|
}
|
|
174
171
|
|
|
175
|
-
|
|
172
|
+
// Enum names that duplicate an earlier schema's content are never emitted, so they are not
|
|
173
|
+
// advertised to plugins either.
|
|
174
|
+
const aliasNames = dedupePlan?.aliasNames
|
|
175
|
+
const emittedEnumNames = aliasNames && aliasNames.size > 0 ? enumNames.filter((name) => !aliasNames.has(name)) : enumNames
|
|
176
|
+
|
|
177
|
+
return { refAliasMap, enumNames: emittedEnumNames, circularNames, discriminatorChildMap, dedupePlan }
|
|
176
178
|
}
|
|
177
179
|
|
|
178
180
|
/**
|
|
179
|
-
* Creates a lazy `
|
|
181
|
+
* Creates a lazy `InputNode<true>` from already-resolved adapter state.
|
|
180
182
|
*
|
|
181
183
|
* The schema and operation iterables each start a fresh parse pass on every
|
|
182
184
|
* `[Symbol.asyncIterator]()` call. This lets multiple plugins consume the same
|
|
@@ -187,7 +189,7 @@ export function preScan({
|
|
|
187
189
|
*
|
|
188
190
|
* @example
|
|
189
191
|
* ```ts
|
|
190
|
-
* const streamNode = createInputStream({ schemas, parseSchema, parseOperation,
|
|
192
|
+
* const streamNode = createInputStream({ schemas, parseSchema, parseOperation, document, parserOptions, refAliasMap, discriminatorChildMap, meta })
|
|
191
193
|
* for await (const schema of streamNode.schemas) {
|
|
192
194
|
* // each call to for-await restarts from the first schema
|
|
193
195
|
* }
|
|
@@ -197,7 +199,7 @@ export function createInputStream({
|
|
|
197
199
|
schemas,
|
|
198
200
|
parseSchema,
|
|
199
201
|
parseOperation,
|
|
200
|
-
|
|
202
|
+
document,
|
|
201
203
|
parserOptions,
|
|
202
204
|
refAliasMap,
|
|
203
205
|
discriminatorChildMap,
|
|
@@ -207,20 +209,20 @@ export function createInputStream({
|
|
|
207
209
|
schemas: Record<string, SchemaObject>
|
|
208
210
|
parseSchema: SchemaParser['parseSchema']
|
|
209
211
|
parseOperation: SchemaParser['parseOperation']
|
|
210
|
-
|
|
212
|
+
document: Document
|
|
211
213
|
parserOptions: ast.ParserOptions
|
|
212
214
|
refAliasMap: Map<string, ast.SchemaNode>
|
|
213
215
|
discriminatorChildMap: Map<string, DiscriminatorTarget> | null
|
|
214
216
|
dedupePlan: ast.DedupePlan | null
|
|
215
217
|
meta: ast.InputMeta
|
|
216
|
-
}): ast.
|
|
218
|
+
}): ast.InputNode<true> {
|
|
217
219
|
// Rewrites a top-level schema against the dedupe plan: a structurally identical sibling
|
|
218
220
|
// becomes a `ref` alias to the canonical one (keeping its own name); otherwise nested
|
|
219
221
|
// duplicates are collapsed while the schema's own root is preserved.
|
|
220
222
|
const rewriteTopLevelSchema = (node: ast.SchemaNode): ast.SchemaNode => {
|
|
221
223
|
if (!dedupePlan) return node
|
|
222
224
|
|
|
223
|
-
const canonical = dedupePlan.canonicalBySignature.get(ast.
|
|
225
|
+
const canonical = dedupePlan.canonicalBySignature.get(ast.signatureOf(node))
|
|
224
226
|
if (canonical && canonical.name !== node.name) {
|
|
225
227
|
return ast.createSchema({
|
|
226
228
|
type: 'ref',
|
|
@@ -231,7 +233,7 @@ export function createInputStream({
|
|
|
231
233
|
})
|
|
232
234
|
}
|
|
233
235
|
|
|
234
|
-
return ast.applyDedupe(node, dedupePlan
|
|
236
|
+
return ast.applyDedupe(node, dedupePlan, true)
|
|
235
237
|
}
|
|
236
238
|
|
|
237
239
|
const schemasIterable: AsyncIterable<ast.SchemaNode> = {
|
|
@@ -243,6 +245,11 @@ export function createInputStream({
|
|
|
243
245
|
}
|
|
244
246
|
|
|
245
247
|
for (const [name, schema] of Object.entries(schemas)) {
|
|
248
|
+
// A top-level schema whose content duplicates an earlier one is not emitted: every
|
|
249
|
+
// ref to it is repointed at the first schema with that content, so its model would
|
|
250
|
+
// be dead code.
|
|
251
|
+
if (dedupePlan?.aliasNames.has(name)) continue
|
|
252
|
+
|
|
246
253
|
// Inline ref aliases: replace the alias entry with its target's parsed node
|
|
247
254
|
// (keeping the alias name). Skip the first parse entirely for alias entries
|
|
248
255
|
// since that result is never used.
|
|
@@ -263,12 +270,9 @@ export function createInputStream({
|
|
|
263
270
|
const operationsIterable: AsyncIterable<ast.OperationNode> = {
|
|
264
271
|
[Symbol.asyncIterator]() {
|
|
265
272
|
return (async function* () {
|
|
266
|
-
for (const
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
const node = parseOperation(parserOptions, operation)
|
|
270
|
-
if (node) yield dedupePlan ? ast.applyDedupe(node, dedupePlan.canonicalBySignature) : node
|
|
271
|
-
}
|
|
273
|
+
for (const operation of getOperations(document)) {
|
|
274
|
+
const node = parseOperation(parserOptions, operation)
|
|
275
|
+
if (node) yield dedupePlan ? ast.applyDedupe(node, dedupePlan) : node
|
|
272
276
|
}
|
|
273
277
|
})()
|
|
274
278
|
},
|
package/src/types.ts
CHANGED
|
@@ -1,16 +1,10 @@
|
|
|
1
|
-
// external packages
|
|
2
|
-
|
|
3
1
|
import type { AdapterFactoryOptions } from '@kubb/core'
|
|
4
2
|
import type { ast } from '@kubb/core'
|
|
5
|
-
import type {
|
|
6
|
-
import type {
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
ResponseObject as OASResponseObject,
|
|
11
|
-
SchemaObject as OASSchemaObject,
|
|
12
|
-
} from 'oas/types'
|
|
13
|
-
import type { OpenAPIV3 } from 'openapi-types'
|
|
3
|
+
import type { JSONSchema4, JSONSchema6, JSONSchema7 } from 'json-schema'
|
|
4
|
+
import type { OpenAPIV3, OpenAPIV3_1 } from 'openapi-types'
|
|
5
|
+
import type { Operation } from './operation.ts'
|
|
6
|
+
|
|
7
|
+
export type { Operation }
|
|
14
8
|
|
|
15
9
|
/**
|
|
16
10
|
* Content-type string for selecting request/response schemas from an OpenAPI spec.
|
|
@@ -36,7 +30,18 @@ export type ContentType = 'application/json' | (string & {})
|
|
|
36
30
|
* }
|
|
37
31
|
* ```
|
|
38
32
|
*/
|
|
39
|
-
export type SchemaObject =
|
|
33
|
+
export type SchemaObject = {
|
|
34
|
+
externalDocs?: unknown
|
|
35
|
+
xml?: unknown
|
|
36
|
+
$schema?: string
|
|
37
|
+
deprecated?: boolean
|
|
38
|
+
example?: unknown
|
|
39
|
+
examples?: Array<unknown>
|
|
40
|
+
nullable?: boolean
|
|
41
|
+
readOnly?: boolean
|
|
42
|
+
writeOnly?: boolean
|
|
43
|
+
discriminator?: DiscriminatorObject
|
|
44
|
+
'x-readme-ref-name'?: string
|
|
40
45
|
/**
|
|
41
46
|
* OAS 3.0 vendor extension: marks a schema as nullable without using `type: ['null', ...]`.
|
|
42
47
|
*/
|
|
@@ -66,7 +71,7 @@ export type SchemaObject = OASSchemaObject & {
|
|
|
66
71
|
* Enum values for this schema (narrowed from `unknown[]`).
|
|
67
72
|
*/
|
|
68
73
|
enum?: Array<string | number | boolean | null>
|
|
69
|
-
}
|
|
74
|
+
} & (OpenAPIV3.SchemaObject | OpenAPIV3_1.SchemaObject | JSONSchema4 | JSONSchema6 | JSONSchema7)
|
|
70
75
|
|
|
71
76
|
/**
|
|
72
77
|
* HTTP method as a lowercase string (`'get' | 'post' | ...`).
|
|
@@ -76,17 +81,22 @@ export type HttpMethod = Lowercase<ast.HttpMethod>
|
|
|
76
81
|
/**
|
|
77
82
|
* Normalized OpenAPI document after parsing.
|
|
78
83
|
*/
|
|
79
|
-
export type Document =
|
|
84
|
+
export type Document = (OpenAPIV3.Document | OpenAPIV3_1.Document) & Record<string, unknown>
|
|
85
|
+
|
|
86
|
+
/**
|
|
87
|
+
* Single operation object (the `get`/`post`/… entry on a path item) plus any vendor extensions.
|
|
88
|
+
*/
|
|
89
|
+
export type OperationObject = (OpenAPIV3.OperationObject | OpenAPIV3_1.OperationObject) & Record<string, unknown>
|
|
80
90
|
|
|
81
91
|
/**
|
|
82
|
-
*
|
|
92
|
+
* Path item object holding the operations and shared parameters for a single URL path.
|
|
83
93
|
*/
|
|
84
|
-
export type
|
|
94
|
+
export type PathItemObject = OpenAPIV3.PathItemObject | OpenAPIV3_1.PathItemObject
|
|
85
95
|
|
|
86
96
|
/**
|
|
87
97
|
* Discriminator object for `oneOf`/`anyOf` schemas in OpenAPI.
|
|
88
98
|
*/
|
|
89
|
-
export type DiscriminatorObject =
|
|
99
|
+
export type DiscriminatorObject = OpenAPIV3.DiscriminatorObject | OpenAPIV3_1.DiscriminatorObject
|
|
90
100
|
|
|
91
101
|
/**
|
|
92
102
|
* OpenAPI reference object pointing to a schema definition via `$ref`.
|
|
@@ -96,12 +106,29 @@ export type ReferenceObject = OpenAPIV3.ReferenceObject
|
|
|
96
106
|
/**
|
|
97
107
|
* OpenAPI response object from a spec that contains schema, status code, and headers.
|
|
98
108
|
*/
|
|
99
|
-
export type ResponseObject =
|
|
109
|
+
export type ResponseObject = OpenAPIV3.ResponseObject | OpenAPIV3_1.ResponseObject
|
|
110
|
+
|
|
111
|
+
/**
|
|
112
|
+
* OpenAPI request body object that maps content types to their media type objects.
|
|
113
|
+
*/
|
|
114
|
+
export type RequestBodyObject = OpenAPIV3.RequestBodyObject | OpenAPIV3_1.RequestBodyObject
|
|
100
115
|
|
|
101
116
|
/**
|
|
102
117
|
* OpenAPI media type object that maps a content-type string to its schema.
|
|
103
118
|
*/
|
|
104
|
-
export type MediaTypeObject =
|
|
119
|
+
export type MediaTypeObject = OpenAPIV3.MediaTypeObject | OpenAPIV3_1.MediaTypeObject
|
|
120
|
+
|
|
121
|
+
/**
|
|
122
|
+
* OpenAPI parameter object, narrowed so `in` is always one of the four valid locations.
|
|
123
|
+
*/
|
|
124
|
+
export type ParameterObject = {
|
|
125
|
+
in: 'cookie' | 'header' | 'path' | 'query'
|
|
126
|
+
} & (OpenAPIV3.ParameterObject | OpenAPIV3_1.ParameterObject)
|
|
127
|
+
|
|
128
|
+
/**
|
|
129
|
+
* OpenAPI server object describing a base URL and its `{variable}` substitutions.
|
|
130
|
+
*/
|
|
131
|
+
export type ServerObject = OpenAPIV3.ServerObject | OpenAPIV3_1.ServerObject
|
|
105
132
|
|
|
106
133
|
/**
|
|
107
134
|
* Configuration options for the OpenAPI adapter. Controls spec validation,
|