@kubb/ast 5.0.0-alpha.30 → 5.0.0-alpha.32

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/nodes/root.ts CHANGED
@@ -8,10 +8,10 @@ import type { SchemaNode } from './schema.ts'
8
8
  *
9
9
  * @example
10
10
  * ```ts
11
- * const meta: RootMeta = { title: 'Pet API', version: '1.0.0' }
11
+ * const meta: InputMeta = { title: 'Pet API', version: '1.0.0' }
12
12
  * ```
13
13
  */
14
- export type RootMeta = {
14
+ export type InputMeta = {
15
15
  /**
16
16
  * API title (from `info.title` in OAS/AsyncAPI).
17
17
  */
@@ -32,22 +32,23 @@ export type RootMeta = {
32
32
  }
33
33
 
34
34
  /**
35
- * Root AST node that contains all schemas and operations for one API document.
35
+ * Input AST node that contains all schemas and operations for one API document.
36
+ * Produced by the adapter and consumed by all Kubb plugins.
36
37
  *
37
38
  * @example
38
39
  * ```ts
39
- * const root: RootNode = {
40
- * kind: 'Root',
40
+ * const input: InputNode = {
41
+ * kind: 'Input',
41
42
  * schemas: [],
42
43
  * operations: [],
43
44
  * }
44
45
  * ```
45
46
  */
46
- export type RootNode = BaseNode & {
47
+ export type InputNode = BaseNode & {
47
48
  /**
48
49
  * Node kind.
49
50
  */
50
- kind: 'Root'
51
+ kind: 'Input'
51
52
  /**
52
53
  * All schema nodes in the document.
53
54
  */
@@ -59,5 +60,5 @@ export type RootNode = BaseNode & {
59
60
  /**
60
61
  * Optional document metadata populated by the adapter.
61
62
  */
62
- meta?: RootMeta
63
+ meta?: InputMeta
63
64
  }
package/src/refs.ts CHANGED
@@ -1,4 +1,4 @@
1
- import type { RootNode } from './nodes/root.ts'
1
+ import type { InputNode } from './nodes/root.ts'
2
2
  import type { SchemaNode } from './nodes/schema.ts'
3
3
 
4
4
  /**
@@ -21,20 +21,20 @@ export function extractRefName(ref: string): string {
21
21
  }
22
22
 
23
23
  /**
24
- * Builds a `RefMap` from `root.schemas` using each schema's `name`.
24
+ * Builds a `RefMap` from `input.schemas` using each schema's `name`.
25
25
  *
26
26
  * Unnamed schemas are skipped.
27
27
  *
28
28
  * @example
29
29
  * ```ts
30
- * const refMap = buildRefMap(root)
30
+ * const refMap = buildRefMap(input)
31
31
  * const pet = refMap.get('Pet')
32
32
  * ```
33
33
  */
34
- export function buildRefMap(root: RootNode): RefMap {
34
+ export function buildRefMap(input: InputNode): RefMap {
35
35
  const map: RefMap = new Map()
36
36
 
37
- for (const schema of root.schemas) {
37
+ for (const schema of input.schemas) {
38
38
  if (schema.name) {
39
39
  map.set(schema.name, schema)
40
40
  }
package/src/types.ts CHANGED
@@ -3,46 +3,58 @@ export type { DistributiveOmit } from './factory.ts'
3
3
  export type { InferSchema, InferSchemaNode, ParserOptions } from './infer.ts'
4
4
  export type {
5
5
  ArraySchemaNode,
6
+ ArrowFunctionNode,
6
7
  BaseNode,
8
+ CodeNode,
7
9
  ComplexSchemaType,
10
+ ConstNode,
8
11
  DateSchemaNode,
9
12
  DatetimeSchemaNode,
10
13
  EnumSchemaNode,
11
14
  EnumValueNode,
15
+ ExportNode,
16
+ FileNode,
12
17
  FormatStringSchemaNode,
13
18
  FunctionNode,
14
19
  FunctionNodeType,
15
20
  FunctionParameterNode,
16
21
  FunctionParametersNode,
22
+ FunctionParamNode,
17
23
  HttpMethod,
18
24
  HttpStatusCode,
25
+ ImportNode,
26
+ InputMeta,
27
+ InputNode,
19
28
  IntersectionSchemaNode,
20
29
  Ipv4SchemaNode,
21
30
  Ipv6SchemaNode,
31
+ JSDocNode,
22
32
  MediaType,
23
33
  Node,
24
34
  NodeKind,
25
35
  NumberSchemaNode,
26
36
  ObjectSchemaNode,
27
37
  OperationNode,
38
+ OutputNode,
28
39
  ParameterGroupNode,
29
40
  ParameterLocation,
30
41
  ParameterNode,
42
+ ParamsTypeNode,
31
43
  PrimitiveSchemaType,
32
44
  PropertyNode,
33
45
  RefSchemaNode,
34
46
  ResponseNode,
35
- RootMeta,
36
- RootNode,
37
47
  ScalarSchemaNode,
38
48
  ScalarSchemaType,
39
49
  SchemaNode,
40
50
  SchemaNodeByType,
41
51
  SchemaType,
52
+ SourceNode,
42
53
  SpecialSchemaType,
43
54
  StatusCode,
44
55
  StringSchemaNode,
45
56
  TimeSchemaNode,
57
+ TypeDeclarationNode,
46
58
  TypeNode,
47
59
  UnionSchemaNode,
48
60
  UrlSchemaNode,
package/src/utils.ts CHANGED
@@ -1,8 +1,19 @@
1
1
  import { camelCase, isValidVarName } from '@internals/utils'
2
2
 
3
- import { createFunctionParameter, createFunctionParameters, createParameterGroup, createProperty, createSchema, createTypeNode } from './factory.ts'
3
+ import { createFunctionParameter, createFunctionParameters, createParameterGroup, createParamsType, createProperty, createSchema } from './factory.ts'
4
4
  import { narrowSchema } from './guards.ts'
5
- import type { FunctionParameterNode, FunctionParametersNode, OperationNode, ParameterGroupNode, ParameterNode, SchemaNode, TypeNode } from './nodes/index.ts'
5
+ import type {
6
+ ExportNode,
7
+ FunctionParameterNode,
8
+ FunctionParametersNode,
9
+ ImportNode,
10
+ OperationNode,
11
+ ParameterGroupNode,
12
+ ParameterNode,
13
+ ParamsTypeNode,
14
+ SchemaNode,
15
+ SourceNode,
16
+ } from './nodes/index.ts'
6
17
  import type { SchemaType } from './nodes/schema.ts'
7
18
 
8
19
  const plainStringTypes = new Set<SchemaType>(['string', 'uuid', 'email', 'url', 'datetime'] as const)
@@ -118,7 +129,7 @@ export type ParamGroupType = {
118
129
  /**
119
130
  * TypeNode for the group type.
120
131
  */
121
- type: TypeNode
132
+ type: ParamsTypeNode
122
133
  /**
123
134
  * Whether the parameter group is optional.
124
135
  */
@@ -251,9 +262,17 @@ export type CreateOperationParamsOptions = {
251
262
  typeWrapper?: (type: string) => string
252
263
  }
253
264
 
254
- function resolveType({ node, param, resolver }: { node: OperationNode; param: ParameterNode; resolver: OperationParamsResolver | undefined }): TypeNode {
265
+ function resolveParamsType({
266
+ node,
267
+ param,
268
+ resolver,
269
+ }: {
270
+ node: OperationNode
271
+ param: ParameterNode
272
+ resolver: OperationParamsResolver | undefined
273
+ }): ParamsTypeNode {
255
274
  if (!resolver) {
256
- return createTypeNode({ variant: 'reference', name: param.schema.primitive ?? 'unknown' })
275
+ return createParamsType({ variant: 'reference', name: param.schema.primitive ?? 'unknown' })
257
276
  }
258
277
 
259
278
  const individualName = resolver.resolveParamName(node, param)
@@ -269,10 +288,10 @@ function resolveType({ node, param, resolver }: { node: OperationNode; param: Pa
269
288
  const groupName = groupLocation ? groupResolvers[groupLocation].call(resolver, node, param) : undefined
270
289
 
271
290
  if (groupName && groupName !== individualName) {
272
- return createTypeNode({ variant: 'member', base: groupName, key: param.name })
291
+ return createParamsType({ variant: 'member', base: groupName, key: param.name })
273
292
  }
274
293
 
275
- return createTypeNode({ variant: 'reference', name: individualName })
294
+ return createParamsType({ variant: 'reference', name: individualName })
276
295
  }
277
296
 
278
297
  /**
@@ -287,7 +306,7 @@ function resolveType({ node, param, resolver }: { node: OperationNode; param: Pa
287
306
  * paramsType: 'inline',
288
307
  * pathParamsType: 'inline',
289
308
  * resolver: tsResolver,
290
- * extraParams: [createFunctionParameter({ name: 'options', type: createTypeNode({ variant: 'reference', name: 'Partial<RequestOptions>' }), default: '{}' })],
309
+ * extraParams: [createFunctionParameter({ name: 'options', type: createParamsType({ variant: 'reference', name: 'Partial<RequestOptions>' }), default: '{}' })],
291
310
  * })
292
311
  * ```
293
312
  */
@@ -299,10 +318,10 @@ export function createOperationParams(node: OperationNode, options: CreateOperat
299
318
  const headersName = paramNames?.headers ?? 'headers'
300
319
  const pathName = paramNames?.path ?? 'pathParams'
301
320
 
302
- const wrapType = (type: string): TypeNode => createTypeNode({ variant: 'reference', name: typeWrapper ? typeWrapper(type) : type })
303
- // Only reference TypeNodes are wrapped (they hold a plain type name string).
321
+ const wrapType = (type: string): ParamsTypeNode => createParamsType({ variant: 'reference', name: typeWrapper ? typeWrapper(type) : type })
322
+ // Only reference-variant TypeNodes are wrapped they hold a plain type name string that needs casing applied.
304
323
  // Member and struct TypeNodes are pre-resolved structured expressions and are passed through unchanged.
305
- const wrapTypeNode = (type: TypeNode): TypeNode => (type.variant === 'reference' ? wrapType(type.name) : type)
324
+ const wrapTypeNode = (type: ParamsTypeNode): ParamsTypeNode => (type.kind === 'ParamsType' && type.variant === 'reference' ? wrapType(type.name) : type)
306
325
 
307
326
  const casedParams = caseParams(node.parameters, paramsCasing)
308
327
  const pathParams = casedParams.filter((p) => p.in === 'path')
@@ -320,7 +339,7 @@ export function createOperationParams(node: OperationNode, options: CreateOperat
320
339
  if (paramsType === 'object') {
321
340
  const children: Array<FunctionParameterNode> = [
322
341
  ...pathParams.map((p) => {
323
- const type = resolveType({ node, param: p, resolver })
342
+ const type = resolveParamsType({ node, param: p, resolver })
324
343
  return createFunctionParameter({ name: p.name, type: wrapTypeNode(type), optional: !p.required })
325
344
  }),
326
345
  ...(bodyType ? [createFunctionParameter({ name: dataName, type: bodyType, optional: !bodyRequired })] : []),
@@ -338,7 +357,7 @@ export function createOperationParams(node: OperationNode, options: CreateOperat
338
357
  params.push(createFunctionParameter({ name: pathName, type: spreadType ? wrapType(spreadType) : undefined, rest: true }))
339
358
  } else {
340
359
  const pathChildren = pathParams.map((p) => {
341
- const type = resolveType({ node, param: p, resolver })
360
+ const type = resolveParamsType({ node, param: p, resolver })
342
361
  return createFunctionParameter({ name: p.name, type: wrapTypeNode(type), optional: !p.required })
343
362
  })
344
363
  params.push(
@@ -384,10 +403,10 @@ function buildGroupParam({
384
403
  params: Array<ParameterNode>
385
404
  groupType: ParamGroupType | undefined
386
405
  resolver: OperationParamsResolver | undefined
387
- wrapType: (type: string) => TypeNode
406
+ wrapType: (type: string) => ParamsTypeNode
388
407
  }): Array<FunctionParameterNode> {
389
408
  if (groupType) {
390
- const type = groupType.type.variant === 'reference' ? wrapType(groupType.type.name) : groupType.type
409
+ const type = groupType.type.kind === 'ParamsType' && groupType.type.variant === 'reference' ? wrapType(groupType.type.name) : groupType.type
391
410
  return [createFunctionParameter({ name, type, optional: groupType.optional })]
392
411
  }
393
412
  if (params.length) {
@@ -426,7 +445,7 @@ function resolveGroupType({
426
445
  return undefined
427
446
  }
428
447
  const allOptional = params.every((p) => !p.required)
429
- return { type: createTypeNode({ variant: 'reference', name: groupName }), optional: allOptional }
448
+ return { type: createParamsType({ variant: 'reference', name: groupName }), optional: allOptional }
430
449
  }
431
450
 
432
451
  /**
@@ -443,9 +462,148 @@ function toStructType({
443
462
  node: OperationNode
444
463
  params: Array<ParameterNode>
445
464
  resolver: OperationParamsResolver | undefined
446
- }): TypeNode {
447
- return createTypeNode({
465
+ }): ParamsTypeNode {
466
+ return createParamsType({
448
467
  variant: 'struct',
449
- properties: params.map((p) => ({ name: p.name, optional: !p.required, type: resolveType({ node, param: p, resolver }) })),
468
+ properties: params.map((p) => ({ name: p.name, optional: !p.required, type: resolveParamsType({ node, param: p, resolver }) })),
450
469
  })
451
470
  }
471
+
472
+ function sourceKey(source: SourceNode): string {
473
+ return `${source.name ?? source.value ?? ''}:${source.isExportable ?? false}:${source.isTypeOnly ?? false}`
474
+ }
475
+
476
+ function pathTypeKey(path: string, isTypeOnly: boolean | undefined): string {
477
+ return `${path}:${isTypeOnly ?? false}`
478
+ }
479
+
480
+ function exportKey(path: string, name: string | undefined, isTypeOnly: boolean | undefined, asAlias: boolean | undefined): string {
481
+ return `${path}:${name ?? ''}:${isTypeOnly ?? false}:${asAlias ?? ''}`
482
+ }
483
+
484
+ function importKey(path: string, name: string | undefined, isTypeOnly: boolean | undefined): string {
485
+ return `${path}:${name ?? ''}:${isTypeOnly ?? false}`
486
+ }
487
+
488
+ /**
489
+ * Computes a multi-level sort key for exports and imports:
490
+ * non-array names first (wildcards/namespace aliases); type-only before value; alphabetical path; unnamed before named.
491
+ */
492
+ function sortKey(node: { name?: string | Array<unknown>; isTypeOnly?: boolean; path: string }): string {
493
+ const isArray = Array.isArray(node.name) ? '1' : '0'
494
+ const typeOnly = node.isTypeOnly ? '0' : '1'
495
+ const hasName = node.name != null ? '1' : '0'
496
+ const name = Array.isArray(node.name) ? [...node.name].sort().join('\0') : (node.name ?? '')
497
+ return `${isArray}:${typeOnly}:${node.path}:${hasName}:${name}`
498
+ }
499
+
500
+ /**
501
+ * Deduplicates an array of `SourceNode` objects.
502
+ * Named sources are deduplicated by `name + isExportable + isTypeOnly`.
503
+ * Unnamed sources are deduplicated by `value`.
504
+ */
505
+ export function combineSources(sources: Array<SourceNode>): Array<SourceNode> {
506
+ const seen = new Map<string, SourceNode>()
507
+ for (const source of sources) {
508
+ const key = sourceKey(source)
509
+ if (!seen.has(key)) seen.set(key, source)
510
+ }
511
+ return [...seen.values()]
512
+ }
513
+
514
+ /**
515
+ * Deduplicates and merges an array of `ExportNode` objects.
516
+ * Exports with the same path and `isTypeOnly` flag have their names merged.
517
+ */
518
+ export function combineExports(exports: Array<ExportNode>): Array<ExportNode> {
519
+ const result: Array<ExportNode> = []
520
+ // Accumulates array-named exports keyed by `path:isTypeOnly` for name-merging
521
+ const namedByPath = new Map<string, ExportNode>()
522
+ // Deduplicates non-array exports by their exact identity
523
+ const seen = new Set<string>()
524
+
525
+ for (const curr of [...exports].sort((a, b) => {
526
+ const ka = sortKey(a)
527
+ const kb = sortKey(b)
528
+ return ka < kb ? -1 : ka > kb ? 1 : 0
529
+ })) {
530
+ const { name, path, isTypeOnly, asAlias } = curr
531
+
532
+ if (Array.isArray(name)) {
533
+ if (!name.length) continue
534
+
535
+ const key = pathTypeKey(path, isTypeOnly)
536
+ const existing = namedByPath.get(key)
537
+
538
+ if (existing && Array.isArray(existing.name)) {
539
+ existing.name = [...new Set([...existing.name, ...name])]
540
+ } else {
541
+ const newItem: ExportNode = { ...curr, name: [...new Set(name)] }
542
+ result.push(newItem)
543
+ namedByPath.set(key, newItem)
544
+ }
545
+ } else {
546
+ const key = exportKey(path, name, isTypeOnly, asAlias)
547
+ if (!seen.has(key)) {
548
+ result.push(curr)
549
+ seen.add(key)
550
+ }
551
+ }
552
+ }
553
+
554
+ return result
555
+ }
556
+
557
+ /**
558
+ * Deduplicates and merges an array of `ImportNode` objects.
559
+ * Filters out unused imports (names not referenced in `source` or re-exported).
560
+ * Imports with the same path and `isTypeOnly` flag have their names merged.
561
+ */
562
+ export function combineImports(imports: Array<ImportNode>, exports: Array<ExportNode>, source?: string): Array<ImportNode> {
563
+ // Build a lookup of all exported names to retain imports that are re-exported
564
+ const exportedNames = new Set(exports.flatMap((e) => (Array.isArray(e.name) ? e.name : e.name ? [e.name] : [])))
565
+ const isUsed = (importName: string): boolean => !source || source.includes(importName) || exportedNames.has(importName)
566
+
567
+ const result: Array<ImportNode> = []
568
+ // Accumulates array-named imports keyed by `path:isTypeOnly` for name-merging
569
+ const namedByPath = new Map<string, ImportNode>()
570
+ // Deduplicates non-array imports by their exact identity
571
+ const seen = new Set<string>()
572
+
573
+ for (const curr of [...imports].sort((a, b) => {
574
+ const ka = sortKey(a)
575
+ const kb = sortKey(b)
576
+ return ka < kb ? -1 : ka > kb ? 1 : 0
577
+ })) {
578
+ if (curr.path === curr.root) continue
579
+
580
+ const { path, isTypeOnly } = curr
581
+ let { name } = curr
582
+
583
+ if (Array.isArray(name)) {
584
+ name = [...new Set(name)].filter((item) => (typeof item === 'string' ? isUsed(item) : isUsed(item.propertyName)))
585
+ if (!name.length) continue
586
+
587
+ const key = pathTypeKey(path, isTypeOnly)
588
+ const existing = namedByPath.get(key)
589
+
590
+ if (existing && Array.isArray(existing.name)) {
591
+ existing.name = [...new Set([...existing.name, ...name])]
592
+ } else {
593
+ const newItem: ImportNode = { ...curr, name }
594
+ result.push(newItem)
595
+ namedByPath.set(key, newItem)
596
+ }
597
+ } else {
598
+ if (name && !isUsed(name)) continue
599
+
600
+ const key = importKey(path, name, isTypeOnly)
601
+ if (!seen.has(key)) {
602
+ result.push(curr)
603
+ seen.add(key)
604
+ }
605
+ }
606
+ }
607
+
608
+ return result
609
+ }
package/src/visitor.ts CHANGED
@@ -1,7 +1,7 @@
1
1
  import type { VisitorDepth } from './constants.ts'
2
2
  import { visitorDepths, WALK_CONCURRENCY } from './constants.ts'
3
3
  import { createParameter, createProperty } from './factory.ts'
4
- import type { Node, OperationNode, ParameterNode, PropertyNode, ResponseNode, RootNode, SchemaNode } from './nodes/index.ts'
4
+ import type { InputNode, Node, OperationNode, OutputNode, ParameterNode, PropertyNode, ResponseNode, SchemaNode } from './nodes/index.ts'
5
5
 
6
6
  /**
7
7
  * Creates a small async concurrency limiter.
@@ -51,9 +51,10 @@ type LimitFn = ReturnType<typeof createLimit>
51
51
  * `ParentOf` uses this map to find parent types.
52
52
  */
53
53
  type ParentNodeMap = [
54
- [RootNode, undefined],
55
- [OperationNode, RootNode],
56
- [SchemaNode, RootNode | OperationNode | SchemaNode | PropertyNode | ParameterNode | ResponseNode],
54
+ [InputNode, undefined],
55
+ [OutputNode, undefined],
56
+ [OperationNode, InputNode],
57
+ [SchemaNode, InputNode | OperationNode | SchemaNode | PropertyNode | ParameterNode | ResponseNode],
57
58
  [PropertyNode, SchemaNode],
58
59
  [ParameterNode, OperationNode],
59
60
  [ResponseNode, OperationNode],
@@ -67,7 +68,7 @@ type ParentNodeMap = [
67
68
  *
68
69
  * @example
69
70
  * ```ts
70
- * type RootParent = ParentOf<RootNode>
71
+ * type InputParent = ParentOf<InputNode>
71
72
  * // undefined
72
73
  * ```
73
74
  *
@@ -80,7 +81,7 @@ type ParentNodeMap = [
80
81
  * @example
81
82
  * ```ts
82
83
  * type SchemaParent = ParentOf<SchemaNode>
83
- * // RootNode | OperationNode | SchemaNode | PropertyNode | ParameterNode | ResponseNode
84
+ * // InputNode | OperationNode | SchemaNode | PropertyNode | ParameterNode | ResponseNode
84
85
  * ```
85
86
  */
86
87
  export type ParentOf<T extends Node, TEntries extends ReadonlyArray<[Node, unknown]> = ParentNodeMap> = TEntries extends [
@@ -108,7 +109,7 @@ export type ParentOf<T extends Node, TEntries extends ReadonlyArray<[Node, unkno
108
109
  export type VisitorContext<T extends Node = Node> = {
109
110
  /**
110
111
  * Parent node of the currently visited node.
111
- * For `RootNode`, this is `undefined`.
112
+ * For `InputNode`, this is `undefined`.
112
113
  */
113
114
  parent?: ParentOf<T>
114
115
  }
@@ -126,7 +127,8 @@ export type VisitorContext<T extends Node = Node> = {
126
127
  * ```
127
128
  */
128
129
  export type Visitor = {
129
- root?(node: RootNode, context: VisitorContext<RootNode>): void | RootNode
130
+ input?(node: InputNode, context: VisitorContext<InputNode>): void | InputNode
131
+ output?(node: OutputNode, context: VisitorContext<OutputNode>): void | OutputNode
130
132
  operation?(node: OperationNode, context: VisitorContext<OperationNode>): void | OperationNode
131
133
  schema?(node: SchemaNode, context: VisitorContext<SchemaNode>): void | SchemaNode
132
134
  property?(node: PropertyNode, context: VisitorContext<PropertyNode>): void | PropertyNode
@@ -152,7 +154,8 @@ type MaybePromise<T> = T | Promise<T>
152
154
  * ```
153
155
  */
154
156
  export type AsyncVisitor = {
155
- root?(node: RootNode, context: VisitorContext<RootNode>): MaybePromise<void | RootNode>
157
+ input?(node: InputNode, context: VisitorContext<InputNode>): MaybePromise<void | InputNode>
158
+ output?(node: OutputNode, context: VisitorContext<OutputNode>): MaybePromise<void | OutputNode>
156
159
  operation?(node: OperationNode, context: VisitorContext<OperationNode>): MaybePromise<void | OperationNode>
157
160
  schema?(node: SchemaNode, context: VisitorContext<SchemaNode>): MaybePromise<void | SchemaNode>
158
161
  property?(node: PropertyNode, context: VisitorContext<PropertyNode>): MaybePromise<void | PropertyNode>
@@ -173,7 +176,8 @@ export type AsyncVisitor = {
173
176
  * ```
174
177
  */
175
178
  export type CollectVisitor<T> = {
176
- root?(node: RootNode, context: VisitorContext<RootNode>): T | undefined
179
+ input?(node: InputNode, context: VisitorContext<InputNode>): T | undefined
180
+ output?(node: OutputNode, context: VisitorContext<OutputNode>): T | undefined
177
181
  operation?(node: OperationNode, context: VisitorContext<OperationNode>): T | undefined
178
182
  schema?(node: SchemaNode, context: VisitorContext<SchemaNode>): T | undefined
179
183
  property?(node: PropertyNode, context: VisitorContext<PropertyNode>): T | undefined
@@ -263,8 +267,10 @@ export type CollectOptions<T> = CollectVisitor<T> & {
263
267
  */
264
268
  function getChildren(node: Node, recurse: boolean): Array<Node> {
265
269
  switch (node.kind) {
266
- case 'Root':
270
+ case 'Input':
267
271
  return [...node.schemas, ...node.operations]
272
+ case 'Output':
273
+ return []
268
274
  case 'Operation':
269
275
  return [...node.parameters, ...(node.requestBody?.schema ? [node.requestBody.schema] : []), ...node.responses]
270
276
  case 'Schema': {
@@ -290,6 +296,8 @@ function getChildren(node: Node, recurse: boolean): Array<Node> {
290
296
  case 'FunctionParameters':
291
297
  case 'Type':
292
298
  return []
299
+ default:
300
+ return []
293
301
  }
294
302
  }
295
303
 
@@ -322,8 +330,11 @@ export async function walk(node: Node, options: WalkOptions): Promise<void> {
322
330
 
323
331
  async function _walk(node: Node, visitor: AsyncVisitor, recurse: boolean, limit: LimitFn, parent: Node | undefined): Promise<void> {
324
332
  switch (node.kind) {
325
- case 'Root':
326
- await limit(() => visitor.root?.(node, { parent: parent as ParentOf<RootNode> }))
333
+ case 'Input':
334
+ await limit(() => visitor.input?.(node, { parent: parent as ParentOf<InputNode> }))
335
+ break
336
+ case 'Output':
337
+ await limit(() => visitor.output?.(node, { parent: parent as ParentOf<OutputNode> }))
327
338
  break
328
339
  case 'Operation':
329
340
  await limit(() => visitor.operation?.(node, { parent: parent as ParentOf<OperationNode> }))
@@ -373,7 +384,8 @@ async function _walk(node: Node, visitor: AsyncVisitor, recurse: boolean, limit:
373
384
  * const next = transform(root, { depth: 'shallow', root: (node) => node })
374
385
  * ```
375
386
  */
376
- export function transform(node: RootNode, options: TransformOptions): RootNode
387
+ export function transform(node: InputNode, options: TransformOptions): InputNode
388
+ export function transform(node: OutputNode, options: TransformOptions): OutputNode
377
389
  export function transform(node: OperationNode, options: TransformOptions): OperationNode
378
390
  export function transform(node: SchemaNode, options: TransformOptions): SchemaNode
379
391
  export function transform(node: PropertyNode, options: TransformOptions): PropertyNode
@@ -385,17 +397,24 @@ export function transform(node: Node, options: TransformOptions): Node {
385
397
  const recurse = (depth ?? visitorDepths.deep) === visitorDepths.deep
386
398
 
387
399
  switch (node.kind) {
388
- case 'Root': {
389
- let root = node
390
- const replaced = visitor.root?.(root, { parent: parent as ParentOf<RootNode> })
391
- if (replaced) root = replaced
400
+ case 'Input': {
401
+ let input = node
402
+ const replaced = visitor.input?.(input, { parent: parent as ParentOf<InputNode> })
403
+ if (replaced) input = replaced
392
404
 
393
405
  return {
394
- ...root,
395
- schemas: root.schemas.map((s) => transform(s, { ...options, parent: root })),
396
- operations: root.operations.map((op) => transform(op, { ...options, parent: root })),
406
+ ...input,
407
+ schemas: input.schemas.map((s) => transform(s, { ...options, parent: input })),
408
+ operations: input.operations.map((op) => transform(op, { ...options, parent: input })),
397
409
  }
398
410
  }
411
+ case 'Output': {
412
+ let output = node
413
+ const replaced = visitor.output?.(output, { parent: parent as ParentOf<OutputNode> })
414
+ if (replaced) output = replaced
415
+
416
+ return output
417
+ }
399
418
  case 'Operation': {
400
419
  let op = node
401
420
  const replaced = visitor.operation?.(op, { parent: parent as ParentOf<OperationNode> })
@@ -462,6 +481,8 @@ export function transform(node: Node, options: TransformOptions): Node {
462
481
  case 'FunctionParameters':
463
482
  case 'Type':
464
483
  return node
484
+ default:
485
+ return node
465
486
  }
466
487
  }
467
488
 
@@ -481,8 +502,11 @@ export function transform(node: Node, options: TransformOptions): Node {
481
502
  */
482
503
  export function composeTransformers(...visitors: Array<Visitor>): Visitor {
483
504
  return {
484
- root(node, context) {
485
- return visitors.reduce<RootNode>((acc, v) => v.root?.(acc, context) ?? acc, node)
505
+ input(node, context) {
506
+ return visitors.reduce<InputNode>((acc, v) => v.input?.(acc, context) ?? acc, node)
507
+ },
508
+ output(node, context) {
509
+ return visitors.reduce<OutputNode>((acc, v) => v.output?.(acc, context) ?? acc, node)
486
510
  },
487
511
  operation(node, context) {
488
512
  return visitors.reduce<OperationNode>((acc, v) => v.operation?.(acc, context) ?? acc, node)
@@ -529,8 +553,11 @@ export function collect<T>(node: Node, options: CollectOptions<T>): Array<T> {
529
553
 
530
554
  let v: T | undefined
531
555
  switch (node.kind) {
532
- case 'Root':
533
- v = visitor.root?.(node, { parent: parent as ParentOf<RootNode> })
556
+ case 'Input':
557
+ v = visitor.input?.(node, { parent: parent as ParentOf<InputNode> })
558
+ break
559
+ case 'Output':
560
+ v = visitor.output?.(node, { parent: parent as ParentOf<OutputNode> })
534
561
  break
535
562
  case 'Operation':
536
563
  v = visitor.operation?.(node, { parent: parent as ParentOf<OperationNode> })