@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/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 (matchesMimeType.json(mt)) {
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(operation.getResponseByStatusCode(statusCode), options.contentType)
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.getRequestBody(options.contentType)
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) => isRef(item))) return schema
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 = operation.getResponseByStatusCode(statusCode)
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
- baseOas,
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
- baseOas: BaseOas
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 methods of Object.values(baseOas.getPaths())) {
161
- for (const operation of Object.values(methods)) {
162
- if (!operation) continue
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
- return { refAliasMap, enumNames, circularNames, discriminatorChildMap, dedupePlan }
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 `InputStreamNode` from already-resolved adapter state.
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, baseOas, parserOptions, refAliasMap, discriminatorChildMap, meta })
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
- baseOas,
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
- baseOas: BaseOas
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.InputStreamNode {
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.schemaSignature(node))
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.canonicalBySignature, true)
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 methods of Object.values(baseOas.getPaths())) {
267
- for (const operation of Object.values(methods)) {
268
- if (!operation) continue
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 { Operation as OASOperation } from 'oas/operation'
6
- import type {
7
- DiscriminatorObject as OASDiscriminatorObject,
8
- OASDocument,
9
- MediaTypeObject as OASMediaTypeObject,
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 = OASSchemaObject & {
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 = OASDocument
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
- * API operation extracted from an OpenAPI document.
92
+ * Path item object holding the operations and shared parameters for a single URL path.
83
93
  */
84
- export type Operation = OASOperation
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 = OASDiscriminatorObject
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 = OASResponseObject
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 = OASMediaTypeObject
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,