@kubb/ast 5.0.0-alpha.15 → 5.0.0-alpha.17

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@kubb/ast",
3
- "version": "5.0.0-alpha.15",
3
+ "version": "5.0.0-alpha.17",
4
4
  "description": "Spec-agnostic AST layer for Kubb. Defines nodes, visitor pattern, and factory functions used across codegen plugins.",
5
5
  "keywords": [
6
6
  "kubb",
package/src/constants.ts CHANGED
@@ -5,7 +5,7 @@ import type { ParameterLocation } from './nodes/parameter.ts'
5
5
  import type { SchemaType } from './nodes/schema.ts'
6
6
 
7
7
  /**
8
- * Depth for schema traversal in visitor functions.
8
+ * Traversal depth used by AST visitor utilities.
9
9
  */
10
10
  export type VisitorDepth = 'shallow' | 'deep'
11
11
 
@@ -26,7 +26,21 @@ export const nodeKinds = {
26
26
  functionParameters: 'FunctionParameters',
27
27
  } as const satisfies Record<string, NodeKind>
28
28
 
29
+ /**
30
+ * Canonical schema type strings used by AST schema nodes.
31
+ *
32
+ * These values are used across the AST as stable discriminators
33
+ * (for example `schema.type === schemaTypes.object`).
34
+ *
35
+ * The map is grouped by intent:
36
+ * - primitives (`string`, `number`, `boolean`, ...)
37
+ * - structural/composite (`object`, `array`, `union`, ...)
38
+ * - special OpenAPI-oriented types (`ref`, `datetime`, `uuid`, ...)
39
+ */
29
40
  export const schemaTypes = {
41
+ /**
42
+ * Text value.
43
+ */
30
44
  string: 'string',
31
45
  /**
32
46
  * Floating-point number (`float`, `double`).
@@ -40,28 +54,93 @@ export const schemaTypes = {
40
54
  * 64-bit integer (`int64`). Only used when `integerType` is set to `'bigint'`.
41
55
  */
42
56
  bigint: 'bigint',
57
+ /**
58
+ * Boolean value
59
+ */
43
60
  boolean: 'boolean',
61
+ /**
62
+ * Explicit null value.
63
+ */
44
64
  null: 'null',
65
+ /**
66
+ * Any value (no type restriction).
67
+ */
45
68
  any: 'any',
69
+ /**
70
+ * Unknown value (must be narrowed before usage).
71
+ */
46
72
  unknown: 'unknown',
73
+ /**
74
+ * No return value (`void`).
75
+ */
47
76
  void: 'void',
77
+ /**
78
+ * Object with named properties.
79
+ */
48
80
  object: 'object',
81
+ /**
82
+ * Sequential list of items.
83
+ */
49
84
  array: 'array',
85
+ /**
86
+ * Fixed-length list with position-specific items.
87
+ */
50
88
  tuple: 'tuple',
89
+ /**
90
+ * "One of" multiple schema members.
91
+ */
51
92
  union: 'union',
93
+ /**
94
+ * "All of" multiple schema members.
95
+ */
52
96
  intersection: 'intersection',
97
+ /**
98
+ * Enum schema.
99
+ */
53
100
  enum: 'enum',
101
+ /**
102
+ * Reference to another schema.
103
+ */
54
104
  ref: 'ref',
105
+ /**
106
+ * Calendar date (for example `2026-03-24`).
107
+ */
55
108
  date: 'date',
109
+ /**
110
+ * Date-time value (for example `2026-03-24T09:00:00Z`).
111
+ */
56
112
  datetime: 'datetime',
113
+ /**
114
+ * Time-only value (for example `09:00:00`).
115
+ */
57
116
  time: 'time',
117
+ /**
118
+ * UUID value.
119
+ */
58
120
  uuid: 'uuid',
121
+ /**
122
+ * Email address value.
123
+ */
59
124
  email: 'email',
125
+ /**
126
+ * URL value.
127
+ */
60
128
  url: 'url',
129
+ /**
130
+ * Binary/blob value.
131
+ */
61
132
  blob: 'blob',
133
+ /**
134
+ * Impossible value (`never`).
135
+ */
62
136
  never: 'never',
63
137
  } as const satisfies Record<SchemaType, SchemaType>
64
138
 
139
+ /**
140
+ * Primitive scalar schema types used when simplifying union members.
141
+ */
142
+ export const SCALAR_PRIMITIVE_TYPES = new Set(['string', 'number', 'integer', 'bigint', 'boolean'] as const)
143
+
65
144
  export const httpMethods = {
66
145
  get: 'GET',
67
146
  post: 'POST',
@@ -81,12 +160,22 @@ export const parameterLocations = {
81
160
  } as const satisfies Record<ParameterLocation, ParameterLocation>
82
161
 
83
162
  /**
84
- * Default max concurrent visitor calls in `walk`.
163
+ * Default maximum number of concurrent callbacks used by `walk`.
164
+ *
165
+ * @example
166
+ * ```ts
167
+ * walk(root, { concurrency: WALK_CONCURRENCY, root: () => {} })
168
+ * ```
85
169
  */
86
170
  export const WALK_CONCURRENCY = 30
87
171
 
88
172
  /**
89
- * Fallback status code string for API spec responses.
173
+ * Fallback response status code used for catch-all responses.
174
+ *
175
+ * @example
176
+ * ```ts
177
+ * const status = DEFAULT_STATUS_CODE // 'default'
178
+ * ```
90
179
  */
91
180
  export const DEFAULT_STATUS_CODE = 'default' as const
92
181
 
package/src/factory.ts CHANGED
@@ -1,3 +1,4 @@
1
+ import type { InferSchemaNode } from './infer.ts'
1
2
  import type {
2
3
  FunctionParameterNode,
3
4
  FunctionParametersNode,
@@ -10,14 +11,39 @@ import type {
10
11
  RootNode,
11
12
  SchemaNode,
12
13
  } from './nodes/index.ts'
14
+ import { syncOptionality } from './utils.ts'
13
15
 
14
16
  /**
15
- * Distributive variant of `Omit` that preserves union members.
17
+ * Distributive `Omit` that preserves each member of a union.
18
+ *
19
+ * @example
20
+ * ```ts
21
+ * type A = { kind: 'a'; keep: string; drop: number }
22
+ * type B = { kind: 'b'; keep: boolean; drop: number }
23
+ * type Result = DistributiveOmit<A | B, 'drop'>
24
+ * // -> { kind: 'a'; keep: string } | { kind: 'b'; keep: boolean }
25
+ * ```
16
26
  */
17
27
  export type DistributiveOmit<T, K extends PropertyKey> = T extends unknown ? Omit<T, K> : never
18
28
 
29
+ type CreateSchemaObjectInput = Omit<ObjectSchemaNode, 'kind' | 'properties'> & { properties?: Array<PropertyNode> }
30
+ type CreateSchemaInput = CreateSchemaObjectInput | DistributiveOmit<Exclude<SchemaNode, ObjectSchemaNode>, 'kind'>
31
+ type CreateSchemaOutput<T extends CreateSchemaInput> = InferSchemaNode<T> & { kind: 'Schema' }
32
+
19
33
  /**
20
- * Creates a `RootNode`.
34
+ * Creates a `RootNode` with stable defaults for `schemas` and `operations`.
35
+ *
36
+ * @example
37
+ * ```ts
38
+ * const root = createRoot()
39
+ * // { kind: 'Root', schemas: [], operations: [] }
40
+ * ```
41
+ *
42
+ * @example
43
+ * ```ts
44
+ * const root = createRoot({ schemas: [petSchema] })
45
+ * // keeps default operations: []
46
+ * ```
21
47
  */
22
48
  export function createRoot(overrides: Partial<Omit<RootNode, 'kind'>> = {}): RootNode {
23
49
  return {
@@ -29,7 +55,27 @@ export function createRoot(overrides: Partial<Omit<RootNode, 'kind'>> = {}): Roo
29
55
  }
30
56
 
31
57
  /**
32
- * Creates an `OperationNode`.
58
+ * Creates an `OperationNode` with default empty arrays for `tags`, `parameters`, and `responses`.
59
+ *
60
+ * @example
61
+ * ```ts
62
+ * const operation = createOperation({
63
+ * operationId: 'getPetById',
64
+ * method: 'GET',
65
+ * path: '/pet/{petId}',
66
+ * })
67
+ * // tags, parameters, and responses are []
68
+ * ```
69
+ *
70
+ * @example
71
+ * ```ts
72
+ * const operation = createOperation({
73
+ * operationId: 'findPets',
74
+ * method: 'GET',
75
+ * path: '/pet/findByStatus',
76
+ * tags: ['pet'],
77
+ * })
78
+ * ```
33
79
  */
34
80
  export function createOperation(
35
81
  props: Pick<OperationNode, 'operationId' | 'method' | 'path'> & Partial<Omit<OperationNode, 'kind' | 'operationId' | 'method' | 'path'>>,
@@ -45,38 +91,63 @@ export function createOperation(
45
91
 
46
92
  /**
47
93
  * Creates a `SchemaNode`, narrowed to the variant of `props.type`.
48
- * For object schemas, `properties` defaults to `[]` when not provided.
94
+ * For object schemas, `properties` defaults to an empty array.
95
+ *
96
+ * @example
97
+ * ```ts
98
+ * const scalar = createSchema({ type: 'string' })
99
+ * // { kind: 'Schema', type: 'string' }
100
+ * ```
101
+ *
102
+ * @example
103
+ * ```ts
104
+ * const object = createSchema({ type: 'object' })
105
+ * // { kind: 'Schema', type: 'object', properties: [] }
106
+ * ```
107
+ *
108
+ * @example
109
+ * ```ts
110
+ * const enumSchema = createSchema({
111
+ * type: 'enum',
112
+ * primitive: 'string',
113
+ * enumValues: ['available', 'pending'],
114
+ * })
115
+ * ```
49
116
  */
50
- export function createSchema<T extends Omit<ObjectSchemaNode, 'kind' | 'properties'> & { properties?: Array<PropertyNode> }>(
51
- props: T,
52
- ): Omit<T, 'properties'> & { properties: Array<PropertyNode>; kind: 'Schema' }
53
- export function createSchema<T extends DistributiveOmit<Exclude<SchemaNode, ObjectSchemaNode>, 'kind'>>(props: T): T & { kind: 'Schema' }
54
- export function createSchema(props: DistributiveOmit<SchemaNode, 'kind'>): SchemaNode
55
- export function createSchema(props: Record<string, unknown>): Record<string, unknown> {
117
+ export function createSchema<T extends CreateSchemaInput>(props: T): CreateSchemaOutput<T>
118
+ export function createSchema(props: CreateSchemaInput): SchemaNode
119
+ export function createSchema(props: CreateSchemaInput): SchemaNode {
56
120
  if (props['type'] === 'object') {
57
- return { properties: [], ...props, kind: 'Schema' }
121
+ return { properties: [], ...props, kind: 'Schema' } as CreateSchemaOutput<typeof props>
58
122
  }
59
123
 
60
- return { ...props, kind: 'Schema' }
124
+ return { ...props, kind: 'Schema' } as CreateSchemaOutput<typeof props>
61
125
  }
62
126
 
63
127
  /**
64
- * Derives `schema.optional` and `schema.nullish` from `required` and `schema.nullable`.
65
- * This keeps `PropertyNode.required` as the single source of truth for optionality.
66
- */
67
- export function syncPropertySchema(required: boolean, schema: SchemaNode): SchemaNode {
68
- const nullable = schema.nullable ?? false
69
-
70
- return {
71
- ...schema,
72
- optional: !required && !nullable ? true : undefined,
73
- nullish: !required && nullable ? true : undefined,
74
- }
75
- }
76
-
77
- /**
78
- * Creates a `PropertyNode`. `required` defaults to `false`.
79
- * `schema.optional` and `schema.nullish` are auto-derived from `required` and `schema.nullable`.
128
+ * Creates a `PropertyNode`.
129
+ *
130
+ * `required` defaults to `false`.
131
+ * `schema.optional` and `schema.nullish` are derived from `required` and `schema.nullable`.
132
+ *
133
+ * @example
134
+ * ```ts
135
+ * const property = createProperty({
136
+ * name: 'status',
137
+ * schema: createSchema({ type: 'string' }),
138
+ * })
139
+ * // required=false, schema.optional=true
140
+ * ```
141
+ *
142
+ * @example
143
+ * ```ts
144
+ * const property = createProperty({
145
+ * name: 'status',
146
+ * required: true,
147
+ * schema: createSchema({ type: 'string', nullable: true }),
148
+ * })
149
+ * // required=true, no optional/nullish
150
+ * ```
80
151
  */
81
152
  export function createProperty(props: Pick<PropertyNode, 'name' | 'schema'> & Partial<Omit<PropertyNode, 'kind' | 'name' | 'schema'>>): PropertyNode {
82
153
  const required = props.required ?? false
@@ -85,13 +156,35 @@ export function createProperty(props: Pick<PropertyNode, 'name' | 'schema'> & Pa
85
156
  ...props,
86
157
  kind: 'Property',
87
158
  required,
88
- schema: syncPropertySchema(required, props.schema),
159
+ schema: syncOptionality(required, props.schema),
89
160
  }
90
161
  }
91
162
 
92
163
  /**
93
- * Creates a `ParameterNode`. `required` defaults to `false`.
94
- * `schema.optional` is auto-derived from `required` and `schema.nullable`.
164
+ * Creates a `ParameterNode`.
165
+ *
166
+ * `required` defaults to `false`.
167
+ * Nested schema flags are set from `required` and `schema.nullable`.
168
+ *
169
+ * @example
170
+ * ```ts
171
+ * const param = createParameter({
172
+ * name: 'petId',
173
+ * in: 'path',
174
+ * required: true,
175
+ * schema: createSchema({ type: 'string' }),
176
+ * })
177
+ * ```
178
+ *
179
+ * @example
180
+ * ```ts
181
+ * const param = createParameter({
182
+ * name: 'status',
183
+ * in: 'query',
184
+ * schema: createSchema({ type: 'string', nullable: true }),
185
+ * })
186
+ * // required=false, schema.nullish=true
187
+ * ```
95
188
  */
96
189
  export function createParameter(
97
190
  props: Pick<ParameterNode, 'name' | 'in' | 'schema'> & Partial<Omit<ParameterNode, 'kind' | 'name' | 'in' | 'schema'>>,
@@ -101,12 +194,21 @@ export function createParameter(
101
194
  ...props,
102
195
  kind: 'Parameter',
103
196
  required,
104
- schema: syncPropertySchema(required, props.schema),
197
+ schema: syncOptionality(required, props.schema),
105
198
  }
106
199
  }
107
200
 
108
201
  /**
109
202
  * Creates a `ResponseNode`.
203
+ *
204
+ * @example
205
+ * ```ts
206
+ * const response = createResponse({
207
+ * statusCode: '200',
208
+ * description: 'Success',
209
+ * schema: createSchema({ type: 'object', properties: [] }),
210
+ * })
211
+ * ```
110
212
  */
111
213
  export function createResponse(
112
214
  props: Pick<ResponseNode, 'statusCode' | 'schema'> & Partial<Omit<ResponseNode, 'kind' | 'statusCode' | 'schema'>>,
@@ -118,7 +220,36 @@ export function createResponse(
118
220
  }
119
221
 
120
222
  /**
121
- * Creates a `FunctionParameterNode`. `optional` defaults to `false`.
223
+ * Creates a single-property object schema used as a discriminator literal.
224
+ *
225
+ * @example
226
+ * ```ts
227
+ * createDiscriminantNode({ propertyName: 'type', value: 'dog' })
228
+ * // -> { type: 'object', properties: [{ name: 'type', required: true, schema: enum('dog') }] }
229
+ * ```
230
+ */
231
+ export function createDiscriminantNode({ propertyName, value }: { propertyName: string; value: string }): SchemaNode {
232
+ return createSchema({
233
+ type: 'object',
234
+ primitive: 'object',
235
+ properties: [
236
+ createProperty({
237
+ name: propertyName,
238
+ schema: createSchema({
239
+ type: 'enum',
240
+ primitive: 'string',
241
+ enumValues: [value],
242
+ }),
243
+ required: true,
244
+ }),
245
+ ],
246
+ })
247
+ }
248
+
249
+ /**
250
+ * Creates a `FunctionParameterNode`.
251
+ *
252
+ * `optional` defaults to `false`.
122
253
  *
123
254
  * @example Required typed param
124
255
  * ```ts
@@ -132,7 +263,7 @@ export function createResponse(
132
263
  * // → params?: QueryParams
133
264
  * ```
134
265
  *
135
- * @example Param with default (implicitly optional cannot combine with `optional: true`)
266
+ * @example Param with default (implicitly optional; cannot combine with `optional: true`)
136
267
  * ```ts
137
268
  * createFunctionParameter({ name: 'config', type: 'RequestConfig', default: '{}' })
138
269
  * // → config: RequestConfig = {}
@@ -149,7 +280,7 @@ export function createFunctionParameter(
149
280
  }
150
281
 
151
282
  /**
152
- * Creates an `ObjectBindingParameterNode` an object-destructured parameter group.
283
+ * Creates an `ObjectBindingParameterNode` for object-destructured parameter groups.
153
284
  *
154
285
  * @example Destructured object param
155
286
  * ```ts
@@ -164,7 +295,7 @@ export function createFunctionParameter(
164
295
  * // call → { id, name }
165
296
  * ```
166
297
  *
167
- * @example Inline — children emitted as individual top-level params
298
+ * @example Inline mode — children emitted as individual top-level parameters
168
299
  * ```ts
169
300
  * createObjectBindingParameter({
170
301
  * properties: [createFunctionParameter({ name: 'petId', type: 'string', optional: false })],
@@ -184,7 +315,7 @@ export function createObjectBindingParameter(
184
315
  }
185
316
 
186
317
  /**
187
- * Creates a `FunctionParametersNode` from an ordered list of params.
318
+ * Creates a `FunctionParametersNode` from an ordered list of parameters.
188
319
  *
189
320
  * @example
190
321
  * ```ts
@@ -195,6 +326,12 @@ export function createObjectBindingParameter(
195
326
  * ],
196
327
  * })
197
328
  * ```
329
+ *
330
+ * @example
331
+ * ```ts
332
+ * const empty = createFunctionParameters()
333
+ * // { kind: 'FunctionParameters', params: [] }
334
+ * ```
198
335
  */
199
336
  export function createFunctionParameters(props: Partial<Omit<FunctionParametersNode, 'kind'>> = {}): FunctionParametersNode {
200
337
  return {
package/src/guards.ts CHANGED
@@ -14,7 +14,13 @@ import type {
14
14
  } from './nodes/index.ts'
15
15
 
16
16
  /**
17
- * Narrows a `SchemaNode` to the specific variant matching `type`.
17
+ * Narrows a `SchemaNode` to the variant that matches `type`.
18
+ *
19
+ * @example
20
+ * ```ts
21
+ * const schema = createSchema({ type: 'string' })
22
+ * const stringNode = narrowSchema(schema, 'string') // StringSchemaNode | undefined
23
+ * ```
18
24
  */
19
25
  export function narrowSchema<T extends SchemaNode['type']>(node: SchemaNode | undefined, type: T): SchemaNodeByType[T] | undefined {
20
26
  return node?.type === type ? (node as SchemaNodeByType[T]) : undefined
@@ -25,46 +31,67 @@ function isKind<T extends Node>(kind: NodeKind) {
25
31
  }
26
32
 
27
33
  /**
28
- * Type guard for `RootNode`.
34
+ * Returns `true` when the input is a `RootNode`.
35
+ *
36
+ * @example
37
+ * ```ts
38
+ * if (isRootNode(node)) {
39
+ * console.log(node.schemas.length)
40
+ * }
41
+ * ```
29
42
  */
30
43
  export const isRootNode = isKind<RootNode>('Root')
31
44
 
32
45
  /**
33
- * Type guard for `OperationNode`.
46
+ * Returns `true` when the input is an `OperationNode`.
47
+ *
48
+ * @example
49
+ * ```ts
50
+ * if (isOperationNode(node)) {
51
+ * console.log(node.operationId)
52
+ * }
53
+ * ```
34
54
  */
35
55
  export const isOperationNode = isKind<OperationNode>('Operation')
36
56
 
37
57
  /**
38
- * Type guard for `SchemaNode`.
58
+ * Returns `true` when the input is a `SchemaNode`.
59
+ *
60
+ * @example
61
+ * ```ts
62
+ * if (isSchemaNode(node)) {
63
+ * console.log(node.type)
64
+ * }
65
+ * ```
39
66
  */
40
67
  export const isSchemaNode = isKind<SchemaNode>('Schema')
41
68
 
42
69
  /**
43
- * Type guard for `PropertyNode`.
70
+ * Returns `true` when the input is a `PropertyNode`.
44
71
  */
45
72
  export const isPropertyNode = isKind<PropertyNode>('Property')
46
73
 
47
74
  /**
48
- * Type guard for `ParameterNode`.
75
+ * Returns `true` when the input is a `ParameterNode`.
49
76
  */
50
77
  export const isParameterNode = isKind<ParameterNode>('Parameter')
51
78
 
52
79
  /**
53
- * Type guard for `ResponseNode`.
80
+ * Returns `true` when the input is a `ResponseNode`.
54
81
  */
55
82
  export const isResponseNode = isKind<ResponseNode>('Response')
56
83
 
57
84
  /**
58
- * Type guard for `FunctionParameterNode`.
85
+ * Returns `true` when the input is a `FunctionParameterNode`.
59
86
  */
60
87
  export const isFunctionParameterNode = isKind<FunctionParameterNode>('FunctionParameter')
61
88
 
62
89
  /**
63
- * Type guard for `ObjectBindingParameterNode`.
90
+ * Returns `true` when the input is an `ObjectBindingParameterNode`.
64
91
  */
65
92
  export const isObjectBindingParameterNode = isKind<ObjectBindingParameterNode>('ObjectBindingParameter')
66
93
 
67
94
  /**
68
- * Type guard for `FunctionParametersNode`.
95
+ * Returns `true` when the input is a `FunctionParametersNode`.
69
96
  */
70
97
  export const isFunctionParametersNode = isKind<FunctionParametersNode>('FunctionParameters')
package/src/index.ts CHANGED
@@ -1,5 +1,6 @@
1
- export { httpMethods, mediaTypes, nodeKinds, schemaTypes } from './constants.ts'
1
+ export { httpMethods, mediaTypes, nodeKinds, SCALAR_PRIMITIVE_TYPES, schemaTypes } from './constants.ts'
2
2
  export {
3
+ createDiscriminantNode,
3
4
  createFunctionParameter,
4
5
  createFunctionParameters,
5
6
  createObjectBindingParameter,
@@ -9,9 +10,7 @@ export {
9
10
  createResponse,
10
11
  createRoot,
11
12
  createSchema,
12
- syncPropertySchema,
13
13
  } from './factory.ts'
14
- export { functionPrinter } from './functionPrinter.ts'
15
14
  export {
16
15
  isFunctionParameterNode,
17
16
  isFunctionParametersNode,
@@ -24,7 +23,10 @@ export {
24
23
  isSchemaNode,
25
24
  narrowSchema,
26
25
  } from './guards.ts'
27
- export { definePrinter } from './printer.ts'
28
- export { buildRefMap, refMapToObject, resolveRef } from './refs.ts'
29
- export { applyParamsCasing, isPlainStringType } from './utils.ts'
26
+ export type { InferSchema, InferSchemaNode, ParserOptions } from './infer.ts'
27
+ export { defineFunctionPrinter, definePrinter, functionPrinter } from './printers/index.ts'
28
+ export { buildRefMap, extractRefName, refMapToObject, resolveRef } from './refs.ts'
29
+ export { childName, collectImports, enumPropName, findDiscriminator } from './resolvers.ts'
30
+ export { mergeAdjacentObjects, resolveNames, setDiscriminatorEnum, setEnumName, simplifyUnion } from './transformers.ts'
31
+ export { caseParams, isStringType, syncOptionality } from './utils.ts'
30
32
  export { collect, composeTransformers, transform, walk } from './visitor.ts'