@kubb/ast 5.0.0-beta.6 → 5.0.0-beta.61
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/LICENSE +17 -10
- package/README.md +51 -27
- package/dist/chunk-CNktS9qV.js +17 -0
- package/dist/defineMacro-BLIR6k-j.d.ts +475 -0
- package/dist/defineMacro-BTXvS8nI.js +106 -0
- package/dist/defineMacro-BTXvS8nI.js.map +1 -0
- package/dist/defineMacro-Bv9R_9a2.cjs +123 -0
- package/dist/defineMacro-Bv9R_9a2.cjs.map +1 -0
- package/dist/extractStringsFromNodes-Cja-xxx5.js +29 -0
- package/dist/extractStringsFromNodes-Cja-xxx5.js.map +1 -0
- package/dist/extractStringsFromNodes-DKgDjFO0.cjs +34 -0
- package/dist/extractStringsFromNodes-DKgDjFO0.cjs.map +1 -0
- package/dist/extractStringsFromNodes-p4mX1TQD.d.ts +14 -0
- package/dist/factory-CZNOGI-N.js +283 -0
- package/dist/factory-CZNOGI-N.js.map +1 -0
- package/dist/factory-DG1CVkEb.cjs +300 -0
- package/dist/factory-DG1CVkEb.cjs.map +1 -0
- package/dist/factory.cjs +29 -0
- package/dist/factory.d.ts +62 -0
- package/dist/factory.js +3 -0
- package/dist/index-BzjwdK2M.d.ts +2433 -0
- package/dist/index.cjs +444 -2180
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.ts +94 -3408
- package/dist/index.js +395 -2101
- package/dist/index.js.map +1 -1
- package/dist/macros.cjs +117 -0
- package/dist/macros.cjs.map +1 -0
- package/dist/macros.d.ts +59 -0
- package/dist/macros.js +115 -0
- package/dist/macros.js.map +1 -0
- package/dist/operationParams-BZ07xDm0.d.ts +204 -0
- package/dist/response-KUdWiDWw.js +658 -0
- package/dist/response-KUdWiDWw.js.map +1 -0
- package/dist/response-hnSw2NKE.cjs +1027 -0
- package/dist/response-hnSw2NKE.cjs.map +1 -0
- package/dist/types-DyDzizSf.d.ts +364 -0
- package/dist/types.cjs +0 -0
- package/dist/types.d.ts +6 -0
- package/dist/types.js +1 -0
- package/dist/utils-BLJwyza-.cjs +912 -0
- package/dist/utils-BLJwyza-.cjs.map +1 -0
- package/dist/utils-CF_-Pn_c.js +770 -0
- package/dist/utils-CF_-Pn_c.js.map +1 -0
- package/dist/utils.cjs +36 -0
- package/dist/utils.d.ts +358 -0
- package/dist/utils.js +4 -0
- package/dist/visitor-DJ6ZEJvq.js +548 -0
- package/dist/visitor-DJ6ZEJvq.js.map +1 -0
- package/dist/visitor-DpKZ9Tk0.cjs +654 -0
- package/dist/visitor-DpKZ9Tk0.cjs.map +1 -0
- package/package.json +21 -6
- package/src/constants.ts +19 -64
- package/src/dedupe.ts +239 -0
- package/src/defineMacro.ts +132 -0
- package/src/dialect.ts +53 -0
- package/src/factory.ts +67 -678
- package/src/guards.ts +10 -92
- package/src/index.ts +13 -44
- package/src/infer.ts +16 -14
- package/src/macros/index.ts +3 -0
- package/src/macros/macroDiscriminatorEnum.ts +44 -0
- package/src/macros/macroEnumName.ts +25 -0
- package/src/macros/macroSimplifyUnion.ts +50 -0
- package/src/mocks.ts +7 -127
- package/src/node.ts +128 -0
- package/src/nodes/base.ts +5 -12
- package/src/nodes/code.ts +165 -74
- package/src/nodes/content.ts +56 -0
- package/src/nodes/file.ts +97 -36
- package/src/nodes/function.ts +216 -156
- package/src/nodes/http.ts +1 -35
- package/src/nodes/index.ts +23 -15
- package/src/nodes/input.ts +140 -0
- package/src/nodes/operation.ts +122 -68
- package/src/nodes/output.ts +23 -0
- package/src/nodes/parameter.ts +33 -3
- package/src/nodes/property.ts +36 -3
- package/src/nodes/requestBody.ts +61 -0
- package/src/nodes/response.ts +58 -13
- package/src/nodes/schema.ts +93 -17
- package/src/printer.ts +48 -42
- package/src/registry.ts +75 -0
- package/src/signature.ts +207 -0
- package/src/types.ts +8 -68
- package/src/utils/codegen.ts +104 -0
- package/src/utils/extractStringsFromNodes.ts +34 -0
- package/src/utils/fileMerge.ts +184 -0
- package/src/utils/index.ts +11 -0
- package/src/utils/operationParams.ts +353 -0
- package/src/utils/refs.ts +138 -0
- package/src/utils/schemaGraph.ts +169 -0
- package/src/utils/schemaMerge.ts +34 -0
- package/src/utils/schemaTraversal.ts +86 -0
- package/src/utils/strings.ts +139 -0
- package/src/visitor.ts +227 -289
- package/dist/chunk--u3MIqq1.js +0 -8
- package/src/nodes/root.ts +0 -64
- package/src/refs.ts +0 -67
- package/src/resolvers.ts +0 -45
- package/src/transformers.ts +0 -159
- package/src/utils.ts +0 -915
|
@@ -0,0 +1,140 @@
|
|
|
1
|
+
import type { Streamable } from '@internals/utils'
|
|
2
|
+
import { defineNode } from '../node.ts'
|
|
3
|
+
import type { BaseNode } from './base.ts'
|
|
4
|
+
import type { OperationNode } from './operation.ts'
|
|
5
|
+
import type { SchemaNode } from './schema.ts'
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Metadata for an API document, populated by the adapter and available to every generator.
|
|
9
|
+
*
|
|
10
|
+
* All fields are plain JSON-serializable values, no `Set`, no `Map`, no class instances.
|
|
11
|
+
* Computed fields (`circularNames`, `enumNames`) are pre-calculated once during the adapter
|
|
12
|
+
* pre-scan so generators never need to iterate the full schema list themselves.
|
|
13
|
+
*
|
|
14
|
+
* @example
|
|
15
|
+
* ```ts
|
|
16
|
+
* const meta: InputMeta = { title: 'Pet Store', version: '1.0.0', baseURL: 'https://petstore.swagger.io/v2', circularNames: [], enumNames: [] }
|
|
17
|
+
* ```
|
|
18
|
+
*/
|
|
19
|
+
export type InputMeta = {
|
|
20
|
+
/**
|
|
21
|
+
* API title from `info.title` in the source document.
|
|
22
|
+
*/
|
|
23
|
+
title?: string
|
|
24
|
+
/**
|
|
25
|
+
* API description from `info.description` in the source document.
|
|
26
|
+
*/
|
|
27
|
+
description?: string
|
|
28
|
+
/**
|
|
29
|
+
* API version string from `info.version` in the source document.
|
|
30
|
+
*/
|
|
31
|
+
version?: string
|
|
32
|
+
/**
|
|
33
|
+
* Resolved base URL from the first matching server entry in the source document.
|
|
34
|
+
*/
|
|
35
|
+
baseURL?: string | null
|
|
36
|
+
/**
|
|
37
|
+
* Names of schemas that participate in a circular reference chain.
|
|
38
|
+
* Computed once during the adapter pre-scan, so a generator never has to
|
|
39
|
+
* call `findCircularSchemas` itself.
|
|
40
|
+
*
|
|
41
|
+
* Convert to a `Set` once at the start of a generator, not per-schema,
|
|
42
|
+
* so lookups stay O(1) without repeated allocations.
|
|
43
|
+
*
|
|
44
|
+
* @example Wrap a circular schema in z.lazy()
|
|
45
|
+
* ```ts
|
|
46
|
+
* const circular = new Set(meta.circularNames)
|
|
47
|
+
* if (circular.has(schema.name)) { ... }
|
|
48
|
+
* ```
|
|
49
|
+
*/
|
|
50
|
+
circularNames: ReadonlyArray<string>
|
|
51
|
+
/**
|
|
52
|
+
* Names of schemas whose type is `enum`.
|
|
53
|
+
* Computed once during the adapter pre-scan, so a generator never has to
|
|
54
|
+
* filter the schema list itself.
|
|
55
|
+
*
|
|
56
|
+
* Convert to a `Set` once at the start of a generator when you need repeated
|
|
57
|
+
* membership checks, so each check stays O(1) instead of an array scan.
|
|
58
|
+
*
|
|
59
|
+
* @example Check if a referenced schema is an enum
|
|
60
|
+
* `const enums = new Set(meta.enumNames)`
|
|
61
|
+
* `const isEnum = enums.has(schemaName)`
|
|
62
|
+
*/
|
|
63
|
+
enumNames: ReadonlyArray<string>
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
/**
|
|
67
|
+
* Input AST node that contains all schemas and operations for one API document.
|
|
68
|
+
* Produced by the adapter and consumed by all Kubb plugins.
|
|
69
|
+
*
|
|
70
|
+
* `Stream` switches `schemas` and `operations` between eager `Array`s (the default) and lazy
|
|
71
|
+
* `AsyncIterable`s. The streaming variant `InputNode<true>` yields nodes one at a time and makes
|
|
72
|
+
* `meta` optional, since the adapter can emit metadata before the first node is parsed.
|
|
73
|
+
*
|
|
74
|
+
* @example
|
|
75
|
+
* ```ts
|
|
76
|
+
* const input: InputNode = {
|
|
77
|
+
* kind: 'Input',
|
|
78
|
+
* schemas: [],
|
|
79
|
+
* operations: [],
|
|
80
|
+
* meta: { circularNames: [], enumNames: [] },
|
|
81
|
+
* }
|
|
82
|
+
* ```
|
|
83
|
+
*
|
|
84
|
+
* @example Streaming variant for large specs
|
|
85
|
+
* ```ts
|
|
86
|
+
* for await (const schema of inputNode.schemas) {
|
|
87
|
+
* // only this one SchemaNode is live here. Previous ones are GC-eligible
|
|
88
|
+
* }
|
|
89
|
+
* ```
|
|
90
|
+
*/
|
|
91
|
+
export type InputNode<Stream extends boolean = false> = BaseNode & {
|
|
92
|
+
/**
|
|
93
|
+
* Node kind.
|
|
94
|
+
*/
|
|
95
|
+
kind: 'Input'
|
|
96
|
+
/**
|
|
97
|
+
* All schema nodes in the document.
|
|
98
|
+
*/
|
|
99
|
+
schemas: Streamable<SchemaNode, Stream>
|
|
100
|
+
/**
|
|
101
|
+
* All operation nodes in the document.
|
|
102
|
+
*/
|
|
103
|
+
operations: Streamable<OperationNode, Stream>
|
|
104
|
+
} & (Stream extends true ? { meta?: InputMeta } : { meta: InputMeta })
|
|
105
|
+
|
|
106
|
+
/**
|
|
107
|
+
* Definition for the {@link InputNode}.
|
|
108
|
+
*/
|
|
109
|
+
export const inputDef = defineNode<InputNode, Partial<Omit<InputNode, 'kind'>>>({
|
|
110
|
+
kind: 'Input',
|
|
111
|
+
defaults: { schemas: [], operations: [], meta: { circularNames: [], enumNames: [] } },
|
|
112
|
+
children: ['schemas', 'operations'],
|
|
113
|
+
visitorKey: 'input',
|
|
114
|
+
})
|
|
115
|
+
|
|
116
|
+
/**
|
|
117
|
+
* Creates an `InputNode`. Pass `stream: true` for the streaming variant whose `schemas` and
|
|
118
|
+
* `operations` are `AsyncIterable` sources and whose `meta` is optional. Otherwise it builds the
|
|
119
|
+
* eager variant with array `schemas`/`operations` and the defaulted `meta`.
|
|
120
|
+
*
|
|
121
|
+
* @example Eager
|
|
122
|
+
* ```ts
|
|
123
|
+
* const input = createInput()
|
|
124
|
+
* // { kind: 'Input', schemas: [], operations: [] }
|
|
125
|
+
* ```
|
|
126
|
+
*
|
|
127
|
+
* @example Streaming
|
|
128
|
+
* ```ts
|
|
129
|
+
* const node = createInput({ stream: true, schemas: schemasIterable, operations: operationsIterable, meta: { title: 'My API' } })
|
|
130
|
+
* ```
|
|
131
|
+
*/
|
|
132
|
+
export function createInput<Stream extends boolean = false>(options: Partial<Omit<InputNode<Stream>, 'kind'>> & { stream?: Stream } = {}): InputNode<Stream> {
|
|
133
|
+
const { stream, ...overrides } = options
|
|
134
|
+
// Streaming inputs carry AsyncIterable sources, so skip the array/meta defaults that
|
|
135
|
+
// inputDef.create applies for the eager variant.
|
|
136
|
+
if (stream) {
|
|
137
|
+
return { kind: 'Input', ...overrides } as InputNode<Stream>
|
|
138
|
+
}
|
|
139
|
+
return inputDef.create(overrides as Partial<Omit<InputNode, 'kind'>>) as InputNode<Stream>
|
|
140
|
+
}
|
package/src/nodes/operation.ts
CHANGED
|
@@ -1,27 +1,20 @@
|
|
|
1
|
+
import { defineNode } from '../node.ts'
|
|
1
2
|
import type { BaseNode } from './base.ts'
|
|
2
3
|
import type { ParameterNode } from './parameter.ts'
|
|
4
|
+
import { createRequestBody, type RequestBodyNode, type UserRequestBody } from './requestBody.ts'
|
|
3
5
|
import type { ResponseNode } from './response.ts'
|
|
4
|
-
import type { SchemaNode } from './schema.ts'
|
|
5
6
|
|
|
6
7
|
export type HttpMethod = 'GET' | 'POST' | 'PUT' | 'PATCH' | 'DELETE' | 'HEAD' | 'OPTIONS' | 'TRACE'
|
|
7
8
|
|
|
8
9
|
/**
|
|
9
|
-
*
|
|
10
|
-
*
|
|
11
|
-
* @example
|
|
12
|
-
* ```ts
|
|
13
|
-
* const operation: OperationNode = {
|
|
14
|
-
* kind: 'Operation',
|
|
15
|
-
* operationId: 'listPets',
|
|
16
|
-
* method: 'GET',
|
|
17
|
-
* path: '/pets',
|
|
18
|
-
* tags: [],
|
|
19
|
-
* parameters: [],
|
|
20
|
-
* responses: [],
|
|
21
|
-
* }
|
|
22
|
-
* ```
|
|
10
|
+
* Transport an operation belongs to.
|
|
23
11
|
*/
|
|
24
|
-
|
|
12
|
+
type OperationProtocol = 'http'
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* Fields shared by every operation, regardless of transport.
|
|
16
|
+
*/
|
|
17
|
+
type OperationNodeBase = BaseNode & {
|
|
25
18
|
/**
|
|
26
19
|
* Node kind.
|
|
27
20
|
*/
|
|
@@ -30,15 +23,6 @@ export type OperationNode = BaseNode & {
|
|
|
30
23
|
* Operation identifier, usually from OpenAPI `operationId`.
|
|
31
24
|
*/
|
|
32
25
|
operationId: string
|
|
33
|
-
/**
|
|
34
|
-
* HTTP Method like 'GET'
|
|
35
|
-
*/
|
|
36
|
-
method: HttpMethod
|
|
37
|
-
/**
|
|
38
|
-
* OpenAPI-style path string, for example `/pets/{petId}`.
|
|
39
|
-
* Path parameters retain the `{param}` notation from the original spec.
|
|
40
|
-
*/
|
|
41
|
-
path: string
|
|
42
26
|
/**
|
|
43
27
|
* Group labels for the operation.
|
|
44
28
|
* Usually copied from OpenAPI `tags`.
|
|
@@ -57,55 +41,125 @@ export type OperationNode = BaseNode & {
|
|
|
57
41
|
*/
|
|
58
42
|
deprecated?: boolean
|
|
59
43
|
/**
|
|
60
|
-
*
|
|
44
|
+
* Query, path, header, and cookie parameters for the operation.
|
|
61
45
|
*/
|
|
62
46
|
parameters: Array<ParameterNode>
|
|
63
47
|
/**
|
|
64
|
-
* Request body
|
|
48
|
+
* Request body for the operation.
|
|
65
49
|
*/
|
|
66
|
-
requestBody?:
|
|
67
|
-
/**
|
|
68
|
-
* Human-readable request body description.
|
|
69
|
-
*/
|
|
70
|
-
description?: string
|
|
71
|
-
/**
|
|
72
|
-
* Whether the request body is required (`requestBody.required: true` in the spec).
|
|
73
|
-
* When `false` or absent, the generated `data` parameter should be optional.
|
|
74
|
-
*/
|
|
75
|
-
required?: boolean
|
|
76
|
-
/**
|
|
77
|
-
* All available content type entries for this request body.
|
|
78
|
-
*
|
|
79
|
-
* When the adapter `contentType` option is set, this array contains exactly one entry for
|
|
80
|
-
* that content type. Otherwise it contains one entry per content type declared in the spec,
|
|
81
|
-
* so that plugins can generate code for every variant (e.g. separate hooks for
|
|
82
|
-
* `application/json` and `multipart/form-data`).
|
|
83
|
-
*
|
|
84
|
-
* @example
|
|
85
|
-
* ```ts
|
|
86
|
-
* // spec has both application/json and multipart/form-data
|
|
87
|
-
* requestBody.content[0].contentType // 'application/json'
|
|
88
|
-
* requestBody.content[1].contentType // 'multipart/form-data'
|
|
89
|
-
* ```
|
|
90
|
-
*/
|
|
91
|
-
content?: Array<{
|
|
92
|
-
/**
|
|
93
|
-
* The content type for this entry (e.g. `'application/json'`).
|
|
94
|
-
*/
|
|
95
|
-
contentType: string
|
|
96
|
-
/**
|
|
97
|
-
* Request body schema for this content type.
|
|
98
|
-
*/
|
|
99
|
-
schema?: SchemaNode
|
|
100
|
-
/**
|
|
101
|
-
* Property keys to exclude from the generated request body type via `Omit<Type, Keys>`.
|
|
102
|
-
* Set when a referenced schema has `readOnly` fields that should be omitted in request types.
|
|
103
|
-
*/
|
|
104
|
-
keysToOmit?: Array<string>
|
|
105
|
-
}>
|
|
106
|
-
}
|
|
50
|
+
requestBody?: RequestBodyNode
|
|
107
51
|
/**
|
|
108
52
|
* Operation responses.
|
|
109
53
|
*/
|
|
110
54
|
responses: Array<ResponseNode>
|
|
111
55
|
}
|
|
56
|
+
|
|
57
|
+
/**
|
|
58
|
+
* Operation served over HTTP/REST (OpenAPI). `method` and `path` are guaranteed.
|
|
59
|
+
*
|
|
60
|
+
* @example
|
|
61
|
+
* ```ts
|
|
62
|
+
* const operation: HttpOperationNode = {
|
|
63
|
+
* kind: 'Operation',
|
|
64
|
+
* operationId: 'listPets',
|
|
65
|
+
* protocol: 'http',
|
|
66
|
+
* method: 'GET',
|
|
67
|
+
* path: '/pets',
|
|
68
|
+
* tags: [],
|
|
69
|
+
* parameters: [],
|
|
70
|
+
* responses: [],
|
|
71
|
+
* }
|
|
72
|
+
* ```
|
|
73
|
+
*/
|
|
74
|
+
export type HttpOperationNode = OperationNodeBase & {
|
|
75
|
+
/**
|
|
76
|
+
* Transport the operation belongs to.
|
|
77
|
+
*/
|
|
78
|
+
protocol?: 'http'
|
|
79
|
+
/**
|
|
80
|
+
* HTTP method like `'GET'`.
|
|
81
|
+
*/
|
|
82
|
+
method: HttpMethod
|
|
83
|
+
/**
|
|
84
|
+
* OpenAPI-style path string, for example `/pets/{petId}`, with `{param}` notation preserved.
|
|
85
|
+
*/
|
|
86
|
+
path: string
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
/**
|
|
90
|
+
* Operation for a non-HTTP transport. HTTP-only fields are forbidden.
|
|
91
|
+
*/
|
|
92
|
+
export type GenericOperationNode = OperationNodeBase & {
|
|
93
|
+
/**
|
|
94
|
+
* Transport the operation belongs to.
|
|
95
|
+
*/
|
|
96
|
+
protocol?: Exclude<OperationProtocol, 'http'>
|
|
97
|
+
method?: never
|
|
98
|
+
path?: never
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
/**
|
|
102
|
+
* AST node representing one API operation.
|
|
103
|
+
*
|
|
104
|
+
* Discriminated on `protocol`: an {@link HttpOperationNode} (`protocol: 'http'`) guarantees
|
|
105
|
+
* `method` and `path`, while a {@link GenericOperationNode} omits them. Narrow with
|
|
106
|
+
* `isHttpOperationNode(node)` or `node.protocol === 'http'` before reading `method`/`path`.
|
|
107
|
+
*/
|
|
108
|
+
export type OperationNode = HttpOperationNode | GenericOperationNode
|
|
109
|
+
|
|
110
|
+
type OperationInput = {
|
|
111
|
+
operationId: string
|
|
112
|
+
method?: HttpOperationNode['method']
|
|
113
|
+
path?: HttpOperationNode['path']
|
|
114
|
+
requestBody?: UserRequestBody
|
|
115
|
+
[key: string]: unknown
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
/**
|
|
119
|
+
* Definition for the {@link OperationNode}. HTTP operations (those carrying both
|
|
120
|
+
* `method` and `path`) are tagged with `protocol: 'http'`, and the request body is
|
|
121
|
+
* normalized into a `RequestBodyNode`.
|
|
122
|
+
*/
|
|
123
|
+
export const operationDef = defineNode<OperationNode, OperationInput>({
|
|
124
|
+
kind: 'Operation',
|
|
125
|
+
build: (props) => {
|
|
126
|
+
const { requestBody, ...rest } = props
|
|
127
|
+
const isHttp = rest.method !== undefined && rest.path !== undefined
|
|
128
|
+
|
|
129
|
+
return {
|
|
130
|
+
tags: [],
|
|
131
|
+
parameters: [],
|
|
132
|
+
responses: [],
|
|
133
|
+
...rest,
|
|
134
|
+
...(isHttp ? { protocol: 'http' as const } : {}),
|
|
135
|
+
requestBody: requestBody ? createRequestBody(requestBody) : undefined,
|
|
136
|
+
}
|
|
137
|
+
},
|
|
138
|
+
children: ['parameters', 'requestBody', 'responses'],
|
|
139
|
+
visitorKey: 'operation',
|
|
140
|
+
})
|
|
141
|
+
|
|
142
|
+
/**
|
|
143
|
+
* Creates an `OperationNode` with default empty arrays for `tags`, `parameters`, and `responses`.
|
|
144
|
+
*
|
|
145
|
+
* @example
|
|
146
|
+
* ```ts
|
|
147
|
+
* const operation = createOperation({ operationId: 'getPetById', method: 'GET', path: '/pet/{petId}' })
|
|
148
|
+
* // tags, parameters, and responses are []
|
|
149
|
+
* ```
|
|
150
|
+
*/
|
|
151
|
+
export function createOperation(
|
|
152
|
+
props: Pick<HttpOperationNode, 'operationId' | 'method' | 'path'> &
|
|
153
|
+
Partial<Omit<HttpOperationNode, 'kind' | 'operationId' | 'method' | 'path' | 'requestBody'>> & {
|
|
154
|
+
requestBody?: UserRequestBody
|
|
155
|
+
},
|
|
156
|
+
): HttpOperationNode
|
|
157
|
+
export function createOperation(
|
|
158
|
+
props: Pick<GenericOperationNode, 'operationId'> &
|
|
159
|
+
Partial<Omit<GenericOperationNode, 'kind' | 'operationId' | 'requestBody'>> & {
|
|
160
|
+
requestBody?: UserRequestBody
|
|
161
|
+
},
|
|
162
|
+
): GenericOperationNode
|
|
163
|
+
export function createOperation(props: OperationInput): OperationNode {
|
|
164
|
+
return operationDef.create(props)
|
|
165
|
+
}
|
package/src/nodes/output.ts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { defineNode } from '../node.ts'
|
|
1
2
|
import type { BaseNode } from './base.ts'
|
|
2
3
|
import type { FileNode } from './file.ts'
|
|
3
4
|
|
|
@@ -24,3 +25,25 @@ export type OutputNode = BaseNode & {
|
|
|
24
25
|
*/
|
|
25
26
|
files: Array<FileNode>
|
|
26
27
|
}
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* Definition for the {@link OutputNode}.
|
|
31
|
+
*/
|
|
32
|
+
export const outputDef = defineNode<OutputNode, Partial<Omit<OutputNode, 'kind'>>>({
|
|
33
|
+
kind: 'Output',
|
|
34
|
+
defaults: { files: [] },
|
|
35
|
+
visitorKey: 'output',
|
|
36
|
+
})
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* Creates an `OutputNode` with a stable default for `files`.
|
|
40
|
+
*
|
|
41
|
+
* @example
|
|
42
|
+
* ```ts
|
|
43
|
+
* const output = createOutput()
|
|
44
|
+
* // { kind: 'Output', files: [] }
|
|
45
|
+
* ```
|
|
46
|
+
*/
|
|
47
|
+
export function createOutput(overrides: Partial<Omit<OutputNode, 'kind'>> = {}): OutputNode {
|
|
48
|
+
return outputDef.create(overrides)
|
|
49
|
+
}
|
package/src/nodes/parameter.ts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { defineNode, syncOptionality } from '../node.ts'
|
|
1
2
|
import type { BaseNode } from './base.ts'
|
|
2
3
|
import type { SchemaNode } from './schema.ts'
|
|
3
4
|
|
|
@@ -18,9 +19,6 @@ export type ParameterLocation = 'path' | 'query' | 'header' | 'cookie'
|
|
|
18
19
|
* ```
|
|
19
20
|
*/
|
|
20
21
|
export type ParameterNode = BaseNode & {
|
|
21
|
-
/**
|
|
22
|
-
* Node kind.
|
|
23
|
-
*/
|
|
24
22
|
kind: 'Parameter'
|
|
25
23
|
/**
|
|
26
24
|
* Parameter name.
|
|
@@ -39,3 +37,35 @@ export type ParameterNode = BaseNode & {
|
|
|
39
37
|
*/
|
|
40
38
|
required: boolean
|
|
41
39
|
}
|
|
40
|
+
|
|
41
|
+
type UserParameterNode = Pick<ParameterNode, 'name' | 'in' | 'schema'> & Partial<Omit<ParameterNode, 'kind' | 'name' | 'in' | 'schema'>>
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* Definition for the {@link ParameterNode}. `required` defaults to `false` and the
|
|
45
|
+
* schema's `optional`/`nullish` flags are kept in sync with it.
|
|
46
|
+
*/
|
|
47
|
+
export const parameterDef = defineNode<ParameterNode, UserParameterNode>({
|
|
48
|
+
kind: 'Parameter',
|
|
49
|
+
build: (props) => {
|
|
50
|
+
const required = props.required ?? false
|
|
51
|
+
return { ...props, required, schema: syncOptionality(props.schema, required) }
|
|
52
|
+
},
|
|
53
|
+
children: ['schema'],
|
|
54
|
+
visitorKey: 'parameter',
|
|
55
|
+
rebuild: true,
|
|
56
|
+
})
|
|
57
|
+
|
|
58
|
+
/**
|
|
59
|
+
* Creates a `ParameterNode`.
|
|
60
|
+
*
|
|
61
|
+
* @example
|
|
62
|
+
* ```ts
|
|
63
|
+
* const param = createParameter({
|
|
64
|
+
* name: 'petId',
|
|
65
|
+
* in: 'path',
|
|
66
|
+
* required: true,
|
|
67
|
+
* schema: createSchema({ type: 'string' }),
|
|
68
|
+
* })
|
|
69
|
+
* ```
|
|
70
|
+
*/
|
|
71
|
+
export const createParameter = parameterDef.create
|
package/src/nodes/property.ts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { defineNode, syncOptionality } from '../node.ts'
|
|
1
2
|
import type { BaseNode } from './base.ts'
|
|
2
3
|
import type { SchemaNode } from './schema.ts'
|
|
3
4
|
|
|
@@ -15,9 +16,6 @@ import type { SchemaNode } from './schema.ts'
|
|
|
15
16
|
* ```
|
|
16
17
|
*/
|
|
17
18
|
export type PropertyNode = BaseNode & {
|
|
18
|
-
/**
|
|
19
|
-
* Node kind.
|
|
20
|
-
*/
|
|
21
19
|
kind: 'Property'
|
|
22
20
|
/**
|
|
23
21
|
* Property key.
|
|
@@ -32,3 +30,38 @@ export type PropertyNode = BaseNode & {
|
|
|
32
30
|
*/
|
|
33
31
|
required: boolean
|
|
34
32
|
}
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* Loosely-typed property accepted by `createProperty`, with `required` optional.
|
|
36
|
+
*/
|
|
37
|
+
export type UserPropertyNode = Pick<PropertyNode, 'name' | 'schema'> & Partial<Omit<PropertyNode, 'kind' | 'name' | 'schema'>>
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* Definition for the {@link PropertyNode}. `required` defaults to `false` and the
|
|
41
|
+
* schema's `optional`/`nullish` flags are kept in sync with it.
|
|
42
|
+
*/
|
|
43
|
+
export const propertyDef = defineNode<PropertyNode, UserPropertyNode>({
|
|
44
|
+
kind: 'Property',
|
|
45
|
+
build: (props) => {
|
|
46
|
+
const required = props.required ?? false
|
|
47
|
+
return { ...props, required, schema: syncOptionality(props.schema, required) }
|
|
48
|
+
},
|
|
49
|
+
children: ['schema'],
|
|
50
|
+
visitorKey: 'property',
|
|
51
|
+
rebuild: true,
|
|
52
|
+
})
|
|
53
|
+
|
|
54
|
+
/**
|
|
55
|
+
* Creates a `PropertyNode`.
|
|
56
|
+
*
|
|
57
|
+
* @example
|
|
58
|
+
* ```ts
|
|
59
|
+
* const property = createProperty({
|
|
60
|
+
* name: 'status',
|
|
61
|
+
* required: true,
|
|
62
|
+
* schema: createSchema({ type: 'string', nullable: true }),
|
|
63
|
+
* })
|
|
64
|
+
* // required=true, no optional/nullish
|
|
65
|
+
* ```
|
|
66
|
+
*/
|
|
67
|
+
export const createProperty = propertyDef.create
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
import { defineNode } from '../node.ts'
|
|
2
|
+
import type { BaseNode } from './base.ts'
|
|
3
|
+
import { type ContentNode, createContent, type UserContent } from './content.ts'
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* AST node representing an operation request body.
|
|
7
|
+
*
|
|
8
|
+
* Body schemas live exclusively inside the `content` array (one entry per content type),
|
|
9
|
+
* mirroring {@link ResponseNode}.
|
|
10
|
+
*
|
|
11
|
+
* @example
|
|
12
|
+
* ```ts
|
|
13
|
+
* const requestBody: RequestBodyNode = {
|
|
14
|
+
* kind: 'RequestBody',
|
|
15
|
+
* required: true,
|
|
16
|
+
* content: [{ kind: 'Content', contentType: 'application/json', schema: createSchema({ type: 'string' }) }],
|
|
17
|
+
* }
|
|
18
|
+
* ```
|
|
19
|
+
*/
|
|
20
|
+
export type RequestBodyNode = BaseNode & {
|
|
21
|
+
kind: 'RequestBody'
|
|
22
|
+
/**
|
|
23
|
+
* Request body description carried over from the spec.
|
|
24
|
+
*/
|
|
25
|
+
description?: string
|
|
26
|
+
/**
|
|
27
|
+
* Whether the request body is required (`requestBody.required: true` in the spec).
|
|
28
|
+
* When `false` or absent, the generated `data` parameter should be optional.
|
|
29
|
+
*/
|
|
30
|
+
required?: boolean
|
|
31
|
+
/**
|
|
32
|
+
* Content type entries for this request body.
|
|
33
|
+
*
|
|
34
|
+
* When the adapter `contentType` option is set, this array contains exactly one entry for
|
|
35
|
+
* that content type. Otherwise it contains one entry per content type declared in the spec,
|
|
36
|
+
* so plugins can generate code for every variant (for example, separate hooks for
|
|
37
|
+
* `application/json` and `multipart/form-data`).
|
|
38
|
+
*/
|
|
39
|
+
content?: Array<ContentNode>
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* Loosely-typed request body accepted by `createOperation`, normalized into a {@link RequestBodyNode}.
|
|
44
|
+
*/
|
|
45
|
+
export type UserRequestBody = Omit<RequestBodyNode, 'kind' | 'content'> & {
|
|
46
|
+
content?: Array<UserContent>
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* Definition for the {@link RequestBodyNode}, normalizing each content entry into a `ContentNode`.
|
|
51
|
+
*/
|
|
52
|
+
export const requestBodyDef = defineNode<RequestBodyNode, UserRequestBody>({
|
|
53
|
+
kind: 'RequestBody',
|
|
54
|
+
build: (props) => ({ ...props, content: props.content?.map(createContent) }),
|
|
55
|
+
children: ['content'],
|
|
56
|
+
})
|
|
57
|
+
|
|
58
|
+
/**
|
|
59
|
+
* Creates a `RequestBodyNode`, normalizing each content entry into a `ContentNode`.
|
|
60
|
+
*/
|
|
61
|
+
export const createRequestBody = requestBodyDef.create
|
package/src/nodes/response.ts
CHANGED
|
@@ -1,16 +1,22 @@
|
|
|
1
|
+
import { defineNode } from '../node.ts'
|
|
1
2
|
import type { BaseNode } from './base.ts'
|
|
2
|
-
import type
|
|
3
|
+
import { type ContentNode, createContent, type UserContent } from './content.ts'
|
|
4
|
+
import type { StatusCode } from './http.ts'
|
|
3
5
|
import type { SchemaNode } from './schema.ts'
|
|
4
6
|
|
|
5
7
|
/**
|
|
6
8
|
* AST node representing one operation response variant.
|
|
7
9
|
*
|
|
10
|
+
* Mirrors {@link OperationNode.requestBody}: the response body schemas live exclusively inside
|
|
11
|
+
* the `content` array (one entry per content type), so the same schema is never duplicated at the
|
|
12
|
+
* node root and inside `content`.
|
|
13
|
+
*
|
|
8
14
|
* @example
|
|
9
15
|
* ```ts
|
|
10
16
|
* const response: ResponseNode = {
|
|
11
17
|
* kind: 'Response',
|
|
12
18
|
* statusCode: '200',
|
|
13
|
-
* schema: createSchema({ type: 'string' }),
|
|
19
|
+
* content: [{ contentType: 'application/json', schema: createSchema({ type: 'string' }) }],
|
|
14
20
|
* }
|
|
15
21
|
* ```
|
|
16
22
|
*/
|
|
@@ -28,16 +34,55 @@ export type ResponseNode = BaseNode & {
|
|
|
28
34
|
*/
|
|
29
35
|
description?: string
|
|
30
36
|
/**
|
|
31
|
-
*
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
*
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
*
|
|
40
|
-
*
|
|
37
|
+
* All available content type entries for this response.
|
|
38
|
+
*
|
|
39
|
+
* When the adapter `contentType` option is set, this array contains exactly one entry for that
|
|
40
|
+
* content type. Otherwise it contains one entry per content type declared in the spec, so that
|
|
41
|
+
* plugins can generate a union of response types (e.g. `application/json` and `application/xml`).
|
|
42
|
+
* Body-less responses keep a single entry whose `schema` is the empty/`void` placeholder.
|
|
43
|
+
*
|
|
44
|
+
* @example
|
|
45
|
+
* ```ts
|
|
46
|
+
* // spec response declares both application/json and application/xml
|
|
47
|
+
* response.content[0].contentType // 'application/json'
|
|
48
|
+
* response.content[1].contentType // 'application/xml'
|
|
49
|
+
* ```
|
|
41
50
|
*/
|
|
42
|
-
|
|
51
|
+
content?: Array<ContentNode>
|
|
43
52
|
}
|
|
53
|
+
|
|
54
|
+
type ResponseInput = Pick<ResponseNode, 'statusCode'> &
|
|
55
|
+
Partial<Omit<ResponseNode, 'kind' | 'statusCode' | 'content'>> & {
|
|
56
|
+
content?: Array<UserContent>
|
|
57
|
+
schema?: SchemaNode
|
|
58
|
+
mediaType?: string | null
|
|
59
|
+
keysToOmit?: Array<string> | null
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
/**
|
|
63
|
+
* Definition for the {@link ResponseNode}. A single legacy `schema` (with optional
|
|
64
|
+
* `mediaType`/`keysToOmit`) is normalized into one `content` entry.
|
|
65
|
+
*/
|
|
66
|
+
export const responseDef = defineNode<ResponseNode, ResponseInput>({
|
|
67
|
+
kind: 'Response',
|
|
68
|
+
build: (props) => {
|
|
69
|
+
const { schema, mediaType, keysToOmit, content, ...rest } = props
|
|
70
|
+
const entries = content ?? (schema ? [{ contentType: mediaType ?? 'application/json', schema, keysToOmit: keysToOmit ?? null }] : undefined)
|
|
71
|
+
return { ...rest, content: entries?.map(createContent) }
|
|
72
|
+
},
|
|
73
|
+
children: ['content'],
|
|
74
|
+
visitorKey: 'response',
|
|
75
|
+
})
|
|
76
|
+
|
|
77
|
+
/**
|
|
78
|
+
* Creates a `ResponseNode`.
|
|
79
|
+
*
|
|
80
|
+
* @example
|
|
81
|
+
* ```ts
|
|
82
|
+
* const response = createResponse({
|
|
83
|
+
* statusCode: '200',
|
|
84
|
+
* content: [{ contentType: 'application/json', schema: createSchema({ type: 'object', properties: [] }) }],
|
|
85
|
+
* })
|
|
86
|
+
* ```
|
|
87
|
+
*/
|
|
88
|
+
export const createResponse = responseDef.create
|