@kubb/ast 5.0.0-alpha.9 → 5.0.0-beta.75

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/guards.ts CHANGED
@@ -1,7 +1,27 @@
1
- import type { Node, NodeKind, OperationNode, ParameterNode, PropertyNode, ResponseNode, RootNode, SchemaNode, SchemaNodeByType } from './nodes/index.ts'
1
+ import type {
2
+ FunctionParameterNode,
3
+ FunctionParametersNode,
4
+ InputNode,
5
+ Node,
6
+ NodeKind,
7
+ OperationNode,
8
+ OutputNode,
9
+ ParameterGroupNode,
10
+ ParameterNode,
11
+ PropertyNode,
12
+ ResponseNode,
13
+ SchemaNode,
14
+ SchemaNodeByType,
15
+ } from './nodes/index.ts'
2
16
 
3
17
  /**
4
- * Narrows a `SchemaNode` to the specific variant matching `type`.
18
+ * Narrows a `SchemaNode` to the variant that matches `type`.
19
+ *
20
+ * @example
21
+ * ```ts
22
+ * const schema = createSchema({ type: 'string' })
23
+ * const stringNode = narrowSchema(schema, 'string') // StringSchemaNode | undefined
24
+ * ```
5
25
  */
6
26
  export function narrowSchema<T extends SchemaNode['type']>(node: SchemaNode | undefined, type: T): SchemaNodeByType[T] | undefined {
7
27
  return node?.type === type ? (node as SchemaNodeByType[T]) : undefined
@@ -12,31 +32,79 @@ function isKind<T extends Node>(kind: NodeKind) {
12
32
  }
13
33
 
14
34
  /**
15
- * Type guard for `RootNode`.
35
+ * Returns `true` when the input is an `InputNode`.
36
+ *
37
+ * @example
38
+ * ```ts
39
+ * if (isInputNode(node)) {
40
+ * console.log(node.schemas.length)
41
+ * }
42
+ * ```
16
43
  */
17
- export const isRootNode = isKind<RootNode>('Root')
44
+ export const isInputNode = isKind<InputNode>('Input')
18
45
 
19
46
  /**
20
- * Type guard for `OperationNode`.
47
+ * Returns `true` when the input is an `OutputNode`.
48
+ *
49
+ * @example
50
+ * ```ts
51
+ * if (isOutputNode(node)) {
52
+ * console.log(node.files.length)
53
+ * }
54
+ * ```
55
+ */
56
+ export const isOutputNode = isKind<OutputNode>('Output')
57
+
58
+ /**
59
+ * Returns `true` when the input is an `OperationNode`.
60
+ *
61
+ * @example
62
+ * ```ts
63
+ * if (isOperationNode(node)) {
64
+ * console.log(node.operationId)
65
+ * }
66
+ * ```
21
67
  */
22
68
  export const isOperationNode = isKind<OperationNode>('Operation')
23
69
 
24
70
  /**
25
- * Type guard for `SchemaNode`.
71
+ * Returns `true` when the input is a `SchemaNode`.
72
+ *
73
+ * @example
74
+ * ```ts
75
+ * if (isSchemaNode(node)) {
76
+ * console.log(node.type)
77
+ * }
78
+ * ```
26
79
  */
27
80
  export const isSchemaNode = isKind<SchemaNode>('Schema')
28
81
 
29
82
  /**
30
- * Type guard for `PropertyNode`.
83
+ * Returns `true` when the input is a `PropertyNode`.
31
84
  */
32
85
  export const isPropertyNode = isKind<PropertyNode>('Property')
33
86
 
34
87
  /**
35
- * Type guard for `ParameterNode`.
88
+ * Returns `true` when the input is a `ParameterNode`.
36
89
  */
37
90
  export const isParameterNode = isKind<ParameterNode>('Parameter')
38
91
 
39
92
  /**
40
- * Type guard for `ResponseNode`.
93
+ * Returns `true` when the input is a `ResponseNode`.
41
94
  */
42
95
  export const isResponseNode = isKind<ResponseNode>('Response')
96
+
97
+ /**
98
+ * Returns `true` when the input is a `FunctionParameterNode`.
99
+ */
100
+ export const isFunctionParameterNode = isKind<FunctionParameterNode>('FunctionParameter')
101
+
102
+ /**
103
+ * Returns `true` when the input is a `ParameterGroupNode`.
104
+ */
105
+ export const isParameterGroupNode = isKind<ParameterGroupNode>('ParameterGroup')
106
+
107
+ /**
108
+ * Returns `true` when the input is a `FunctionParametersNode`.
109
+ */
110
+ export const isFunctionParametersNode = isKind<FunctionParametersNode>('FunctionParameters')
package/src/index.ts CHANGED
@@ -1,7 +1,45 @@
1
- export { httpMethods, mediaTypes, nodeKinds, schemaTypes } from './constants.ts'
2
- export { createOperation, createParameter, createProperty, createResponse, createRoot, createSchema } from './factory.ts'
3
- export { isOperationNode, isParameterNode, isPropertyNode, isResponseNode, isRootNode, isSchemaNode, narrowSchema } from './guards.ts'
4
- export { definePrinter } from './printer.ts'
5
- export { buildRefMap, refMapToObject, resolveRef } from './refs.ts'
6
- export { applyParamsCasing, isPlainStringType } from './utils.ts'
1
+ export { httpMethods, isScalarPrimitive, mediaTypes, nodeKinds, schemaTypes } from './constants.ts'
2
+ export {
3
+ createArrowFunction,
4
+ createBreak,
5
+ createConst,
6
+ createExport,
7
+ createFile,
8
+ createFunction,
9
+ createFunctionParameter,
10
+ createFunctionParameters,
11
+ createImport,
12
+ createInput,
13
+ createJsx,
14
+ createOperation,
15
+ createOutput,
16
+ createParameter,
17
+ createParameterGroup,
18
+ createParamsType,
19
+ createProperty,
20
+ createResponse,
21
+ createSchema,
22
+ createSource,
23
+ createText,
24
+ createType,
25
+ syncOptionality,
26
+ } from './factory.ts'
27
+ export { isInputNode, isOperationNode, isOutputNode, isSchemaNode, narrowSchema } from './guards.ts'
28
+ export { createPrinterFactory, definePrinter } from './printer.ts'
29
+ export { extractRefName } from './refs.ts'
30
+ export { childName, collectImports, enumPropName, findDiscriminator } from './resolvers.ts'
31
+ export { mergeAdjacentObjects, setDiscriminatorEnum, setEnumName, simplifyUnion } from './transformers.ts'
32
+ export type * from './types.ts'
33
+ export {
34
+ caseParams,
35
+ collectReferencedSchemaNames,
36
+ containsCircularRef,
37
+ createDiscriminantNode,
38
+ createOperationParams,
39
+ extractStringsFromNodes,
40
+ findCircularSchemas,
41
+ isStringType,
42
+ resolveRefName,
43
+ syncSchemaRef,
44
+ } from './utils.ts'
7
45
  export { collect, transform, walk } from './visitor.ts'
package/src/infer.ts ADDED
@@ -0,0 +1,130 @@
1
+ import type {
2
+ ArraySchemaNode,
3
+ DateSchemaNode,
4
+ DatetimeSchemaNode,
5
+ EnumSchemaNode,
6
+ IntersectionSchemaNode,
7
+ NumberSchemaNode,
8
+ ObjectSchemaNode,
9
+ RefSchemaNode,
10
+ ScalarSchemaNode,
11
+ SchemaNode,
12
+ StringSchemaNode,
13
+ TimeSchemaNode,
14
+ UnionSchemaNode,
15
+ UrlSchemaNode,
16
+ } from './nodes/index.ts'
17
+
18
+ /**
19
+ * Shared parser options used by OAS-to-AST inference and parser flows.
20
+ */
21
+ export type ParserOptions = {
22
+ /**
23
+ * How `format: 'date-time'` schemas are represented. `false` falls through to a plain string.
24
+ */
25
+ dateType: false | 'string' | 'stringOffset' | 'stringLocal' | 'date'
26
+ /**
27
+ * Whether `type: 'integer'` and `format: 'int64'` produce `number` or `bigint` nodes.
28
+ */
29
+ integerType?: 'number' | 'bigint'
30
+ /**
31
+ * AST type used when no schema type can be inferred.
32
+ */
33
+ unknownType: 'any' | 'unknown' | 'void'
34
+ /**
35
+ * AST type used for completely empty schemas (`{}`).
36
+ */
37
+ emptySchemaType: 'any' | 'unknown' | 'void'
38
+ /**
39
+ * Suffix appended to derived enum names when building property schema names.
40
+ */
41
+ enumSuffix: 'enum' | (string & {})
42
+ }
43
+
44
+ /**
45
+ * Maps each `dateType` option value to the AST node produced by `format: 'date-time'`.
46
+ */
47
+ type DateTimeNodeByDateType = {
48
+ date: DateSchemaNode
49
+ string: DatetimeSchemaNode
50
+ stringOffset: DatetimeSchemaNode
51
+ stringLocal: DatetimeSchemaNode
52
+ false: StringSchemaNode
53
+ }
54
+
55
+ /**
56
+ * Resolves the AST node produced by `format: 'date-time'` based on the `dateType` option.
57
+ */
58
+ type ResolveDateTimeNode<TDateType extends ParserOptions['dateType']> = DateTimeNodeByDateType[TDateType extends keyof DateTimeNodeByDateType
59
+ ? TDateType
60
+ : 'string']
61
+
62
+ /**
63
+ * Ordered list of `[schema-shape, SchemaNode]` pairs.
64
+ * `InferSchemaNode` walks this tuple in order and returns the first matching node type.
65
+ */
66
+ type SchemaNodeMap<TDateType extends ParserOptions['dateType'] = 'string'> = [
67
+ [{ $ref: string }, RefSchemaNode],
68
+ [{ allOf: ReadonlyArray<unknown>; properties: object }, IntersectionSchemaNode],
69
+ [{ allOf: readonly [unknown, unknown, ...unknown[]] }, IntersectionSchemaNode],
70
+ [{ allOf: ReadonlyArray<unknown> }, SchemaNode],
71
+ [{ oneOf: ReadonlyArray<unknown> }, UnionSchemaNode],
72
+ [{ anyOf: ReadonlyArray<unknown> }, UnionSchemaNode],
73
+ [{ const: null }, ScalarSchemaNode],
74
+ [{ const: string | number | boolean }, EnumSchemaNode],
75
+ [{ type: ReadonlyArray<string> }, UnionSchemaNode],
76
+ [{ type: 'array'; enum: ReadonlyArray<unknown> }, ArraySchemaNode],
77
+ [{ enum: ReadonlyArray<unknown> }, EnumSchemaNode],
78
+ [{ type: 'enum' }, EnumSchemaNode],
79
+ [{ type: 'union' }, UnionSchemaNode],
80
+ [{ type: 'intersection' }, IntersectionSchemaNode],
81
+ [{ type: 'tuple' }, ArraySchemaNode],
82
+ [{ type: 'ref' }, RefSchemaNode],
83
+ [{ type: 'datetime' }, DatetimeSchemaNode],
84
+ [{ type: 'date' }, DateSchemaNode],
85
+ [{ type: 'time' }, TimeSchemaNode],
86
+ [{ type: 'url' }, UrlSchemaNode],
87
+ [{ type: 'object' }, ObjectSchemaNode],
88
+ [{ additionalProperties: boolean | {} }, ObjectSchemaNode],
89
+ [{ type: 'array' }, ArraySchemaNode],
90
+ [{ items: object }, ArraySchemaNode],
91
+ [{ prefixItems: ReadonlyArray<unknown> }, ArraySchemaNode],
92
+ [{ type: string; format: 'date-time' }, ResolveDateTimeNode<TDateType>],
93
+ [{ type: string; format: 'date' }, DateSchemaNode],
94
+ [{ type: string; format: 'time' }, TimeSchemaNode],
95
+ [{ format: 'date-time' }, ResolveDateTimeNode<TDateType>],
96
+ [{ format: 'date' }, DateSchemaNode],
97
+ [{ format: 'time' }, TimeSchemaNode],
98
+ [{ type: 'string' }, StringSchemaNode],
99
+ [{ type: 'number' }, NumberSchemaNode],
100
+ [{ type: 'integer' }, NumberSchemaNode],
101
+ [{ type: 'bigint' }, NumberSchemaNode],
102
+ [{ type: string }, ScalarSchemaNode],
103
+ [{ minLength: number }, StringSchemaNode],
104
+ [{ maxLength: number }, StringSchemaNode],
105
+ [{ pattern: string }, StringSchemaNode],
106
+ [{ minimum: number }, NumberSchemaNode],
107
+ [{ maximum: number }, NumberSchemaNode],
108
+ ]
109
+
110
+ /**
111
+ * Infers the matching AST `SchemaNode` type from an input schema shape.
112
+ */
113
+ export type InferSchemaNode<
114
+ TSchema extends object,
115
+ TDateType extends ParserOptions['dateType'] = 'string',
116
+ TEntries extends ReadonlyArray<[object, SchemaNode]> = SchemaNodeMap<TDateType>,
117
+ > = TEntries extends [infer TEntry extends [object, SchemaNode], ...infer TRest extends ReadonlyArray<[object, SchemaNode]>]
118
+ ? TSchema extends TEntry[0]
119
+ ? TEntry[1]
120
+ : InferSchemaNode<TSchema, TDateType, TRest>
121
+ : SchemaNode
122
+
123
+ /**
124
+ * Backward-compatible alias for `InferSchemaNode`.
125
+ */
126
+ export type InferSchema<
127
+ TSchema extends object,
128
+ TDateType extends ParserOptions['dateType'] = 'string',
129
+ TEntries extends ReadonlyArray<[object, SchemaNode]> = SchemaNodeMap<TDateType>,
130
+ > = InferSchemaNode<TSchema, TDateType, TEntries>
package/src/mocks.ts CHANGED
@@ -1,16 +1,24 @@
1
- import { createOperation, createParameter, createProperty, createResponse, createRoot, createSchema } from './factory.ts'
2
- import type { RootNode } from './nodes/root.ts'
1
+ import { createInput, createOperation, createParameter, createProperty, createResponse, createSchema } from './factory.ts'
2
+ import type { InputNode } from './nodes/root.ts'
3
3
 
4
4
  /**
5
- * Minimal single-resource tree: one `Pet` schema and one `getPetById` operation.
5
+ * Builds a minimal sample AST with one `Pet` schema and one `getPetById` operation.
6
6
  */
7
- export function buildSampleTree(): RootNode {
7
+ export function buildSampleTree(): InputNode {
8
8
  const petSchema = createSchema({
9
9
  type: 'object',
10
10
  name: 'Pet',
11
11
  properties: [
12
- createProperty({ name: 'id', schema: createSchema({ type: 'integer' }), required: true }),
13
- createProperty({ name: 'name', schema: createSchema({ type: 'string' }), required: true }),
12
+ createProperty({
13
+ name: 'id',
14
+ schema: createSchema({ type: 'integer' }),
15
+ required: true,
16
+ }),
17
+ createProperty({
18
+ name: 'name',
19
+ schema: createSchema({ type: 'string' }),
20
+ required: true,
21
+ }),
14
22
  ],
15
23
  })
16
24
 
@@ -19,9 +27,19 @@ export function buildSampleTree(): RootNode {
19
27
  method: 'GET',
20
28
  path: '/pets/{petId}',
21
29
  tags: ['pets'],
22
- parameters: [createParameter({ name: 'petId', in: 'path', schema: createSchema({ type: 'integer' }), required: true })],
30
+ parameters: [
31
+ createParameter({
32
+ name: 'petId',
33
+ in: 'path',
34
+ schema: createSchema({ type: 'integer' }),
35
+ required: true,
36
+ }),
37
+ ],
23
38
  responses: [
24
- createResponse({ statusCode: '200', schema: createSchema({ type: 'ref', name: 'Pet' }) }),
39
+ createResponse({
40
+ statusCode: '200',
41
+ schema: createSchema({ type: 'ref', name: 'Pet' }),
42
+ }),
25
43
  createResponse({
26
44
  statusCode: '404',
27
45
  schema: createSchema({ type: 'ref', name: 'Error' }),
@@ -29,31 +47,46 @@ export function buildSampleTree(): RootNode {
29
47
  ],
30
48
  })
31
49
 
32
- return createRoot({ schemas: [petSchema], operations: [operation] })
50
+ return createInput({ schemas: [petSchema], operations: [operation] })
33
51
  }
34
52
 
35
53
  /**
36
- * PetStore-like tree: six named schemas (`Pet`, `NewPet`, `PetList`, `Error`, `PetOrError`, `FullPet`)
37
- * and two operations (`listPets`, `createPet`).
54
+ * Builds a PetStore-like fixture AST with:
55
+ * - six named schemas (`Pet`, `NewPet`, `PetList`, `Error`, `PetOrError`, `FullPet`)
56
+ * - two operations (`listPets`, `createPet`)
38
57
  */
39
- export function buildFixture(): RootNode {
58
+ export function buildFixture(): InputNode {
40
59
  const refPet = createSchema({ type: 'ref', ref: 'Pet' })
41
60
  const refError = createSchema({ type: 'ref', ref: 'Error' })
42
61
 
43
- return createRoot({
62
+ return createInput({
44
63
  schemas: [
45
64
  createSchema({
46
65
  name: 'Pet',
47
66
  type: 'object',
48
67
  properties: [
49
- createProperty({ name: 'id', schema: createSchema({ type: 'integer' }), required: true }),
50
- createProperty({ name: 'name', schema: createSchema({ type: 'string' }), required: true }),
68
+ createProperty({
69
+ name: 'id',
70
+ schema: createSchema({ type: 'integer' }),
71
+ required: true,
72
+ }),
73
+ createProperty({
74
+ name: 'name',
75
+ schema: createSchema({ type: 'string' }),
76
+ required: true,
77
+ }),
51
78
  ],
52
79
  }),
53
80
  createSchema({
54
81
  name: 'NewPet',
55
82
  type: 'object',
56
- properties: [createProperty({ name: 'name', schema: createSchema({ type: 'string' }), required: true })],
83
+ properties: [
84
+ createProperty({
85
+ name: 'name',
86
+ schema: createSchema({ type: 'string' }),
87
+ required: true,
88
+ }),
89
+ ],
57
90
  }),
58
91
  createSchema({
59
92
  name: 'PetList',
@@ -64,11 +97,23 @@ export function buildFixture(): RootNode {
64
97
  name: 'Error',
65
98
  type: 'object',
66
99
  properties: [
67
- createProperty({ name: 'code', schema: createSchema({ type: 'integer' }), required: true }),
68
- createProperty({ name: 'message', schema: createSchema({ type: 'string' }), required: true }),
100
+ createProperty({
101
+ name: 'code',
102
+ schema: createSchema({ type: 'integer' }),
103
+ required: true,
104
+ }),
105
+ createProperty({
106
+ name: 'message',
107
+ schema: createSchema({ type: 'string' }),
108
+ required: true,
109
+ }),
69
110
  ],
70
111
  }),
71
- createSchema({ name: 'PetOrError', type: 'union', members: [refPet, refError] }),
112
+ createSchema({
113
+ name: 'PetOrError',
114
+ type: 'union',
115
+ members: [refPet, refError],
116
+ }),
72
117
  createSchema({
73
118
  name: 'FullPet',
74
119
  type: 'intersection',
@@ -76,7 +121,12 @@ export function buildFixture(): RootNode {
76
121
  refPet,
77
122
  createSchema({
78
123
  type: 'object',
79
- properties: [createProperty({ name: 'createdAt', schema: createSchema({ type: 'datetime' }) })],
124
+ properties: [
125
+ createProperty({
126
+ name: 'createdAt',
127
+ schema: createSchema({ type: 'datetime' }),
128
+ }),
129
+ ],
80
130
  }),
81
131
  ],
82
132
  }),
@@ -87,10 +137,24 @@ export function buildFixture(): RootNode {
87
137
  method: 'GET',
88
138
  path: '/pets',
89
139
  tags: ['pets'],
90
- parameters: [createParameter({ name: 'limit', in: 'query', schema: createSchema({ type: 'integer' }) })],
140
+ parameters: [
141
+ createParameter({
142
+ name: 'limit',
143
+ in: 'query',
144
+ schema: createSchema({ type: 'integer' }),
145
+ }),
146
+ ],
91
147
  responses: [
92
- createResponse({ statusCode: '200', schema: createSchema({ type: 'ref', ref: 'PetList' }), mediaType: 'application/json' }),
93
- createResponse({ statusCode: '400', schema: refError, mediaType: 'application/json' }),
148
+ createResponse({
149
+ statusCode: '200',
150
+ schema: createSchema({ type: 'ref', ref: 'PetList' }),
151
+ mediaType: 'application/json',
152
+ }),
153
+ createResponse({
154
+ statusCode: '400',
155
+ schema: refError,
156
+ mediaType: 'application/json',
157
+ }),
94
158
  ],
95
159
  }),
96
160
  createOperation({
@@ -98,8 +162,14 @@ export function buildFixture(): RootNode {
98
162
  method: 'POST',
99
163
  path: '/pets',
100
164
  tags: ['pets'],
101
- requestBody: createSchema({ type: 'ref', ref: 'NewPet' }),
102
- responses: [createResponse({ statusCode: '201', schema: refPet, mediaType: 'application/json' })],
165
+ requestBody: { content: [{ contentType: 'application/json', schema: createSchema({ type: 'ref', ref: 'NewPet' }) }] },
166
+ responses: [
167
+ createResponse({
168
+ statusCode: '201',
169
+ schema: refPet,
170
+ mediaType: 'application/json',
171
+ }),
172
+ ],
103
173
  }),
104
174
  ],
105
175
  })
package/src/nodes/base.ts CHANGED
@@ -1,16 +1,56 @@
1
1
  /**
2
- * Kind discriminant for every AST node.
2
+ * `kind` values used by AST nodes.
3
+ *
4
+ * @example
5
+ * ```ts
6
+ * const kind: NodeKind = 'Schema'
7
+ * ```
3
8
  */
4
- export type NodeKind = 'Root' | 'Operation' | 'Schema' | 'Property' | 'Parameter' | 'Response'
9
+ export type NodeKind =
10
+ | 'Input'
11
+ | 'Output'
12
+ | 'Operation'
13
+ | 'Schema'
14
+ | 'Property'
15
+ | 'Parameter'
16
+ | 'Response'
17
+ | 'FunctionParameter'
18
+ | 'ParameterGroup'
19
+ | 'FunctionParameters'
20
+ | 'Type'
21
+ | 'ParamsType'
22
+ | 'File'
23
+ | 'Import'
24
+ | 'Export'
25
+ | 'Source'
26
+ | 'Const'
27
+ | 'Function'
28
+ | 'ArrowFunction'
29
+ | 'Text'
30
+ | 'Break'
31
+ | 'Jsx'
5
32
 
6
33
  /**
7
- * Common base for all AST nodes.
34
+ * Base shape shared by all AST nodes.
35
+ *
36
+ * @example
37
+ * ```ts
38
+ * const base: BaseNode = { kind: 'Input' }
39
+ * ```
8
40
  */
9
41
  export type BaseNode = {
42
+ /**
43
+ * Node discriminator.
44
+ */
10
45
  kind: NodeKind
11
46
  }
12
47
 
13
48
  /**
14
- * Any AST node.
49
+ * Minimal node type when only `kind` is needed.
50
+ *
51
+ * @example
52
+ * ```ts
53
+ * const node: Node = { kind: 'Operation' }
54
+ * ```
15
55
  */
16
56
  export type Node = BaseNode