@kubb/ast 5.0.0-alpha.5 → 5.0.0-alpha.51

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/visitor.ts CHANGED
@@ -1,10 +1,21 @@
1
1
  import type { VisitorDepth } from './constants.ts'
2
2
  import { visitorDepths, WALK_CONCURRENCY } from './constants.ts'
3
- import type { Node, OperationNode, ParameterNode, PropertyNode, ResponseNode, RootNode, SchemaNode } from './nodes/index.ts'
3
+ import { createParameter, createProperty } from './factory.ts'
4
+ import type { InputNode, Node, OperationNode, OutputNode, ParameterNode, PropertyNode, ResponseNode, SchemaNode } from './nodes/index.ts'
4
5
 
5
6
  /**
6
- * Creates a concurrency-limiting wrapper. At most `concurrency` promises may be
7
- * in-flight simultaneously; additional calls are queued and dispatched as slots free.
7
+ * Creates a small async concurrency limiter.
8
+ *
9
+ * At most `concurrency` tasks are in flight at once. Extra tasks are queued.
10
+ *
11
+ * @example
12
+ * ```ts
13
+ * const limit = createLimit(2)
14
+ * for (const task of [taskA, taskB, taskC]) {
15
+ * await limit(() => task())
16
+ * }
17
+ * // only 2 tasks run at the same time
18
+ * ```
8
19
  */
9
20
  function createLimit(concurrency: number) {
10
21
  let active = 0
@@ -35,67 +46,233 @@ function createLimit(concurrency: number) {
35
46
  type LimitFn = ReturnType<typeof createLimit>
36
47
 
37
48
  /**
38
- * Shared options for `walk`, `transform`, and `collect`.
49
+ * Ordered mapping of `[NodeType, ParentType]` pairs.
50
+ *
51
+ * `ParentOf` uses this map to find parent types.
39
52
  */
40
- export type VisitorOptions = {
41
- depth?: VisitorDepth
53
+ type ParentNodeMap = [
54
+ [InputNode, undefined],
55
+ [OutputNode, undefined],
56
+ [OperationNode, InputNode],
57
+ [SchemaNode, InputNode | OperationNode | SchemaNode | PropertyNode | ParameterNode | ResponseNode],
58
+ [PropertyNode, SchemaNode],
59
+ [ParameterNode, OperationNode],
60
+ [ResponseNode, OperationNode],
61
+ ]
62
+
63
+ /**
64
+ * Resolves the parent node type for a given AST node type.
65
+ *
66
+ * This is used by visitor context so `ctx.parent` is correctly typed
67
+ * for each callback.
68
+ *
69
+ * @example
70
+ * ```ts
71
+ * type InputParent = ParentOf<InputNode>
72
+ * // undefined
73
+ * ```
74
+ *
75
+ * @example
76
+ * ```ts
77
+ * type PropertyParent = ParentOf<PropertyNode>
78
+ * // SchemaNode
79
+ * ```
80
+ *
81
+ * @example
82
+ * ```ts
83
+ * type SchemaParent = ParentOf<SchemaNode>
84
+ * // InputNode | OperationNode | SchemaNode | PropertyNode | ParameterNode | ResponseNode
85
+ * ```
86
+ */
87
+ export type ParentOf<T extends Node, TEntries extends ReadonlyArray<[Node, unknown]> = ParentNodeMap> = TEntries extends [
88
+ infer TEntry extends [Node, unknown],
89
+ ...infer TRest extends ReadonlyArray<[Node, unknown]>,
90
+ ]
91
+ ? T extends TEntry[0]
92
+ ? TEntry[1]
93
+ : ParentOf<T, TRest>
94
+ : Node
95
+
96
+ /**
97
+ * Traversal context passed as the second argument to every visitor callback.
98
+ * `parent` is typed from the current node type.
99
+ *
100
+ * @example
101
+ * ```ts
102
+ * const visitor: Visitor = {
103
+ * schema(node, { parent }) {
104
+ * // parent type is narrowed by node kind
105
+ * },
106
+ * }
107
+ * ```
108
+ */
109
+ export type VisitorContext<T extends Node = Node> = {
42
110
  /**
43
- * Maximum number of sibling nodes visited concurrently inside `walk`.
44
- * @default 30
111
+ * Parent node of the currently visited node.
112
+ * For `InputNode`, this is `undefined`.
45
113
  */
46
- concurrency?: number
114
+ parent?: ParentOf<T>
47
115
  }
48
116
 
49
117
  /**
50
- * Synchronous visitor for `transform` and `walk`.
118
+ * Synchronous visitor used by `transform`.
119
+ *
120
+ * @example
121
+ * ```ts
122
+ * const visitor: Visitor = {
123
+ * operation(node) {
124
+ * return { ...node, operationId: `x_${node.operationId}` }
125
+ * },
126
+ * }
127
+ * ```
51
128
  */
52
129
  export type Visitor = {
53
- root?(node: RootNode): void | RootNode
54
- operation?(node: OperationNode): void | OperationNode
55
- schema?(node: SchemaNode): void | SchemaNode
56
- property?(node: PropertyNode): void | PropertyNode
57
- parameter?(node: ParameterNode): void | ParameterNode
58
- response?(node: ResponseNode): void | ResponseNode
130
+ input?(node: InputNode, context: VisitorContext<InputNode>): void | InputNode
131
+ output?(node: OutputNode, context: VisitorContext<OutputNode>): void | OutputNode
132
+ operation?(node: OperationNode, context: VisitorContext<OperationNode>): void | OperationNode
133
+ schema?(node: SchemaNode, context: VisitorContext<SchemaNode>): void | SchemaNode
134
+ property?(node: PropertyNode, context: VisitorContext<PropertyNode>): void | PropertyNode
135
+ parameter?(node: ParameterNode, context: VisitorContext<ParameterNode>): void | ParameterNode
136
+ response?(node: ResponseNode, context: VisitorContext<ResponseNode>): void | ResponseNode
59
137
  }
60
138
 
139
+ /**
140
+ * Utility type for values that can be returned directly or asynchronously.
141
+ */
61
142
  type MaybePromise<T> = T | Promise<T>
62
143
 
63
144
  /**
64
145
  * Async visitor for `walk`. Synchronous `Visitor` objects are compatible.
146
+ *
147
+ * @example
148
+ * ```ts
149
+ * const visitor: AsyncVisitor = {
150
+ * async operation(node) {
151
+ * await Promise.resolve(node.operationId)
152
+ * },
153
+ * }
154
+ * ```
65
155
  */
66
156
  export type AsyncVisitor = {
67
- root?(node: RootNode): MaybePromise<void | RootNode>
68
- operation?(node: OperationNode): MaybePromise<void | OperationNode>
69
- schema?(node: SchemaNode): MaybePromise<void | SchemaNode>
70
- property?(node: PropertyNode): MaybePromise<void | PropertyNode>
71
- parameter?(node: ParameterNode): MaybePromise<void | ParameterNode>
72
- response?(node: ResponseNode): MaybePromise<void | ResponseNode>
157
+ input?(node: InputNode, context: VisitorContext<InputNode>): MaybePromise<void | InputNode>
158
+ output?(node: OutputNode, context: VisitorContext<OutputNode>): MaybePromise<void | OutputNode>
159
+ operation?(node: OperationNode, context: VisitorContext<OperationNode>): MaybePromise<void | OperationNode>
160
+ schema?(node: SchemaNode, context: VisitorContext<SchemaNode>): MaybePromise<void | SchemaNode>
161
+ property?(node: PropertyNode, context: VisitorContext<PropertyNode>): MaybePromise<void | PropertyNode>
162
+ parameter?(node: ParameterNode, context: VisitorContext<ParameterNode>): MaybePromise<void | ParameterNode>
163
+ response?(node: ResponseNode, context: VisitorContext<ResponseNode>): MaybePromise<void | ResponseNode>
73
164
  }
74
165
 
75
166
  /**
76
- * Visitor for `collect`.
167
+ * Visitor used by `collect`.
168
+ *
169
+ * @example
170
+ * ```ts
171
+ * const visitor: CollectVisitor<string> = {
172
+ * operation(node) {
173
+ * return node.operationId
174
+ * },
175
+ * }
176
+ * ```
77
177
  */
78
178
  export type CollectVisitor<T> = {
79
- root?(node: RootNode): T | undefined
80
- operation?(node: OperationNode): T | undefined
81
- schema?(node: SchemaNode): T | undefined
82
- property?(node: PropertyNode): T | undefined
83
- parameter?(node: ParameterNode): T | undefined
84
- response?(node: ResponseNode): T | undefined
179
+ input?(node: InputNode, context: VisitorContext<InputNode>): T | undefined
180
+ output?(node: OutputNode, context: VisitorContext<OutputNode>): T | undefined
181
+ operation?(node: OperationNode, context: VisitorContext<OperationNode>): T | undefined
182
+ schema?(node: SchemaNode, context: VisitorContext<SchemaNode>): T | undefined
183
+ property?(node: PropertyNode, context: VisitorContext<PropertyNode>): T | undefined
184
+ parameter?(node: ParameterNode, context: VisitorContext<ParameterNode>): T | undefined
185
+ response?(node: ResponseNode, context: VisitorContext<ResponseNode>): T | undefined
186
+ }
187
+
188
+ /**
189
+ * Options for `transform`.
190
+ *
191
+ * @example
192
+ * ```ts
193
+ * const options: TransformOptions = { depth: 'deep', schema: (node) => node }
194
+ * ```
195
+ *
196
+ * @example
197
+ * ```ts
198
+ * // Only transform the current node, not nested children
199
+ * const options: TransformOptions = { depth: 'shallow', schema: (node) => node }
200
+ * ```
201
+ */
202
+ export type TransformOptions = Visitor & {
203
+ /**
204
+ * Traversal depth (`'deep'` by default).
205
+ * @default 'deep'
206
+ */
207
+ depth?: VisitorDepth
208
+ /**
209
+ * Internal parent override used during recursion.
210
+ */
211
+ parent?: Node
212
+ }
213
+
214
+ /**
215
+ * Options for `walk`.
216
+ *
217
+ * @example
218
+ * ```ts
219
+ * const options: WalkOptions = { depth: 'deep', concurrency: 10, root: () => {} }
220
+ * ```
221
+ */
222
+ export type WalkOptions = AsyncVisitor & {
223
+ /**
224
+ * Traversal depth (`'deep'` by default).
225
+ * @default 'deep'
226
+ */
227
+ depth?: VisitorDepth
228
+ /**
229
+ * Maximum number of sibling nodes visited concurrently.
230
+ * @default 30
231
+ */
232
+ concurrency?: number
233
+ }
234
+
235
+ /**
236
+ * Options for `collect`.
237
+ *
238
+ * @example
239
+ * ```ts
240
+ * const options: CollectOptions<string> = { depth: 'shallow', schema: () => undefined }
241
+ * ```
242
+ */
243
+ export type CollectOptions<T> = CollectVisitor<T> & {
244
+ /**
245
+ * Traversal depth (`'deep'` by default).
246
+ * @default 'deep'
247
+ */
248
+ depth?: VisitorDepth
249
+ /**
250
+ * Internal parent override used during recursion.
251
+ */
252
+ parent?: Node
85
253
  }
86
254
 
87
255
  /**
88
256
  * Returns the immediate traversable children of `node`.
89
257
  *
90
- * For `Schema` nodes, children (properties, items, members) are only included
91
- * when `recurse` is `true`; shallow traversal omits them entirely.
258
+ * For `Schema` nodes, children (`properties`, `items`, `members`, and non-boolean
259
+ * `additionalProperties`) are only included
260
+ * when `recurse` is `true`; shallow mode skips them.
261
+ *
262
+ * @example
263
+ * ```ts
264
+ * const children = getChildren(operationNode, true)
265
+ * // returns parameters, requestBody schema (if present), and responses
266
+ * ```
92
267
  */
93
268
  function getChildren(node: Node, recurse: boolean): Array<Node> {
94
269
  switch (node.kind) {
95
- case 'Root':
270
+ case 'Input':
96
271
  return [...node.schemas, ...node.operations]
272
+ case 'Output':
273
+ return []
97
274
  case 'Operation':
98
- return [...node.parameters, ...(node.requestBody ? [node.requestBody] : []), ...node.responses]
275
+ return [...node.parameters, ...(node.requestBody?.schema ? [node.requestBody.schema] : []), ...node.responses]
99
276
  case 'Schema': {
100
277
  const children: Array<Node> = []
101
278
 
@@ -104,6 +281,7 @@ function getChildren(node: Node, recurse: boolean): Array<Node> {
104
281
  if ('properties' in node && node.properties.length > 0) children.push(...node.properties)
105
282
  if ('items' in node && node.items) children.push(...node.items)
106
283
  if ('members' in node && node.members) children.push(...node.members)
284
+ if ('additionalProperties' in node && node.additionalProperties && node.additionalProperties !== true) children.push(node.additionalProperties)
107
285
 
108
286
  return children
109
287
  }
@@ -113,162 +291,296 @@ function getChildren(node: Node, recurse: boolean): Array<Node> {
113
291
  return [node.schema]
114
292
  case 'Response':
115
293
  return node.schema ? [node.schema] : []
294
+ case 'FunctionParameter':
295
+ case 'ParameterGroup':
296
+ case 'FunctionParameters':
297
+ case 'Type':
298
+ return []
299
+ default:
300
+ return []
116
301
  }
117
302
  }
118
303
 
119
304
  /**
120
305
  * Depth-first traversal for side effects. Visitor return values are ignored.
121
- * Sibling nodes at each level are visited concurrently up to `options.concurrency` (default: 30).
306
+ * Sibling nodes at each level are visited concurrently up to `options.concurrency`
307
+ * (default: `WALK_CONCURRENCY`).
308
+ *
309
+ * @example
310
+ * ```ts
311
+ * await walk(root, {
312
+ * operation(node) {
313
+ * console.log(node.operationId)
314
+ * },
315
+ * })
316
+ * ```
317
+ *
318
+ * @example
319
+ * ```ts
320
+ * // Visit only the current node
321
+ * await walk(root, { depth: 'shallow', root: () => {} })
322
+ * ```
122
323
  */
123
- export async function walk(node: Node, visitor: AsyncVisitor, options: VisitorOptions = {}): Promise<void> {
324
+ export async function walk(node: Node, options: WalkOptions): Promise<void> {
124
325
  const recurse = (options.depth ?? visitorDepths.deep) === visitorDepths.deep
125
326
  const limit = createLimit(options.concurrency ?? WALK_CONCURRENCY)
126
- return _walk(node, visitor, recurse, limit)
327
+
328
+ return _walk(node, options, recurse, limit, undefined)
127
329
  }
128
330
 
129
- /**
130
- * Internal recursive walk implementation — calls visitor then recurses into children.
131
- */
132
- async function _walk(node: Node, visitor: AsyncVisitor, recurse: boolean, limit: LimitFn): Promise<void> {
331
+ async function _walk(node: Node, visitor: AsyncVisitor, recurse: boolean, limit: LimitFn, parent: Node | undefined): Promise<void> {
133
332
  switch (node.kind) {
134
- case 'Root':
135
- await limit(() => visitor.root?.(node))
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> }))
136
338
  break
137
339
  case 'Operation':
138
- await limit(() => visitor.operation?.(node))
340
+ await limit(() =>
341
+ visitor.operation?.(node, {
342
+ parent: parent as ParentOf<OperationNode>,
343
+ }),
344
+ )
139
345
  break
140
346
  case 'Schema':
141
- await limit(() => visitor.schema?.(node))
347
+ await limit(() => visitor.schema?.(node, { parent: parent as ParentOf<SchemaNode> }))
142
348
  break
143
349
  case 'Property':
144
- await limit(() => visitor.property?.(node))
350
+ await limit(() => visitor.property?.(node, { parent: parent as ParentOf<PropertyNode> }))
145
351
  break
146
352
  case 'Parameter':
147
- await limit(() => visitor.parameter?.(node))
353
+ await limit(() =>
354
+ visitor.parameter?.(node, {
355
+ parent: parent as ParentOf<ParameterNode>,
356
+ }),
357
+ )
148
358
  break
149
359
  case 'Response':
150
- await limit(() => visitor.response?.(node))
360
+ await limit(() => visitor.response?.(node, { parent: parent as ParentOf<ResponseNode> }))
361
+ break
362
+ case 'FunctionParameter':
363
+ case 'ParameterGroup':
364
+ case 'FunctionParameters':
151
365
  break
152
366
  }
153
367
 
154
368
  const children = getChildren(node, recurse)
155
- await Promise.all(children.map((child) => _walk(child, visitor, recurse, limit)))
369
+ for (const child of children) {
370
+ await _walk(child, visitor, recurse, limit, node)
371
+ }
156
372
  }
157
373
 
158
374
  /**
159
- * Depth-first immutable transformation. Visitor return values replace nodes; `undefined` keeps the original.
375
+ * Runs a depth-first immutable transform.
376
+ *
377
+ * If a visitor returns a node, it replaces the current node.
378
+ * If it returns `undefined`, the current node stays the same.
379
+ *
380
+ * @example
381
+ * ```ts
382
+ * const next = transform(root, {
383
+ * operation(node) {
384
+ * return { ...node, operationId: `prefixed_${node.operationId}` }
385
+ * },
386
+ * })
387
+ * ```
388
+ *
389
+ * @example
390
+ * ```ts
391
+ * // Shallow mode: only transform the input node
392
+ * const next = transform(root, { depth: 'shallow', root: (node) => node })
393
+ * ```
160
394
  */
161
- export function transform(node: RootNode, visitor: Visitor, options?: VisitorOptions): RootNode
162
- export function transform(node: OperationNode, visitor: Visitor, options?: VisitorOptions): OperationNode
163
- export function transform(node: SchemaNode, visitor: Visitor, options?: VisitorOptions): SchemaNode
164
- export function transform(node: PropertyNode, visitor: Visitor, options?: VisitorOptions): PropertyNode
165
- export function transform(node: ParameterNode, visitor: Visitor, options?: VisitorOptions): ParameterNode
166
- export function transform(node: ResponseNode, visitor: Visitor, options?: VisitorOptions): ResponseNode
167
- export function transform(node: Node, visitor: Visitor, options?: VisitorOptions): Node
168
- export function transform(node: Node, visitor: Visitor, options: VisitorOptions = {}): Node {
169
- const recurse = (options.depth ?? visitorDepths.deep) === visitorDepths.deep
395
+ export function transform(node: InputNode, options: TransformOptions): InputNode
396
+ export function transform(node: OutputNode, options: TransformOptions): OutputNode
397
+ export function transform(node: OperationNode, options: TransformOptions): OperationNode
398
+ export function transform(node: SchemaNode, options: TransformOptions): SchemaNode
399
+ export function transform(node: PropertyNode, options: TransformOptions): PropertyNode
400
+ export function transform(node: ParameterNode, options: TransformOptions): ParameterNode
401
+ export function transform(node: ResponseNode, options: TransformOptions): ResponseNode
402
+ export function transform(node: Node, options: TransformOptions): Node
403
+ export function transform(node: Node, options: TransformOptions): Node {
404
+ const { depth, parent, ...visitor } = options
405
+ const recurse = (depth ?? visitorDepths.deep) === visitorDepths.deep
170
406
 
171
407
  switch (node.kind) {
172
- case 'Root': {
173
- let root = node
174
- const replaced = visitor.root?.(root)
175
- if (replaced) root = replaced
408
+ case 'Input': {
409
+ let input = node
410
+ const replaced = visitor.input?.(input, {
411
+ parent: parent as ParentOf<InputNode>,
412
+ })
413
+ if (replaced) input = replaced
176
414
 
177
415
  return {
178
- ...root,
179
- schemas: root.schemas.map((s) => transform(s, visitor, options)),
180
- operations: root.operations.map((op) => transform(op, visitor, options)),
416
+ ...input,
417
+ schemas: input.schemas.map((s) => transform(s, { ...options, parent: input })),
418
+ operations: input.operations.map((op) => transform(op, { ...options, parent: input })),
181
419
  }
182
420
  }
421
+ case 'Output': {
422
+ let output = node
423
+ const replaced = visitor.output?.(output, {
424
+ parent: parent as ParentOf<OutputNode>,
425
+ })
426
+ if (replaced) output = replaced
427
+
428
+ return output
429
+ }
183
430
  case 'Operation': {
184
431
  let op = node
185
- const replaced = visitor.operation?.(op)
432
+ const replaced = visitor.operation?.(op, {
433
+ parent: parent as ParentOf<OperationNode>,
434
+ })
186
435
  if (replaced) op = replaced
187
436
 
188
437
  return {
189
438
  ...op,
190
- parameters: op.parameters.map((p) => transform(p, visitor, options)),
191
- requestBody: op.requestBody ? transform(op.requestBody, visitor, options) : undefined,
192
- responses: op.responses.map((r) => transform(r, visitor, options)),
439
+ parameters: op.parameters.map((p) => transform(p, { ...options, parent: op })),
440
+ requestBody: op.requestBody
441
+ ? {
442
+ ...op.requestBody,
443
+ schema: op.requestBody.schema ? transform(op.requestBody.schema, { ...options, parent: op }) : undefined,
444
+ }
445
+ : undefined,
446
+ responses: op.responses.map((r) => transform(r, { ...options, parent: op })),
193
447
  }
194
448
  }
195
449
  case 'Schema': {
196
450
  let schema = node
197
- const replaced = visitor.schema?.(schema)
451
+ const replaced = visitor.schema?.(schema, {
452
+ parent: parent as ParentOf<SchemaNode>,
453
+ })
198
454
  if (replaced) schema = replaced
199
455
 
456
+ const childOptions = { ...options, parent: schema }
457
+
200
458
  return {
201
459
  ...schema,
202
- ...('properties' in schema && recurse ? { properties: schema.properties.map((p) => transform(p, visitor, options)) } : {}),
203
- ...('items' in schema && recurse ? { items: schema.items?.map((i) => transform(i, visitor, options)) } : {}),
204
- ...('members' in schema && recurse ? { members: schema.members?.map((m) => transform(m, visitor, options)) } : {}),
205
- }
460
+ ...('properties' in schema && recurse
461
+ ? {
462
+ properties: schema.properties.map((p) => transform(p, childOptions)),
463
+ }
464
+ : {}),
465
+ ...('items' in schema && recurse ? { items: schema.items?.map((i) => transform(i, childOptions)) } : {}),
466
+ ...('members' in schema && recurse ? { members: schema.members?.map((m) => transform(m, childOptions)) } : {}),
467
+ ...('additionalProperties' in schema && recurse && schema.additionalProperties && schema.additionalProperties !== true
468
+ ? {
469
+ additionalProperties: transform(schema.additionalProperties, childOptions),
470
+ }
471
+ : {}),
472
+ } as SchemaNode
206
473
  }
207
474
  case 'Property': {
208
475
  let prop = node
209
- const replaced = visitor.property?.(prop)
476
+ const replaced = visitor.property?.(prop, {
477
+ parent: parent as ParentOf<PropertyNode>,
478
+ })
210
479
  if (replaced) prop = replaced
211
480
 
212
- return {
481
+ return createProperty({
213
482
  ...prop,
214
- schema: transform(prop.schema, visitor, options),
215
- }
483
+ schema: transform(prop.schema, { ...options, parent: prop }),
484
+ })
216
485
  }
217
486
  case 'Parameter': {
218
487
  let param = node
219
- const replaced = visitor.parameter?.(param)
488
+ const replaced = visitor.parameter?.(param, {
489
+ parent: parent as ParentOf<ParameterNode>,
490
+ })
220
491
  if (replaced) param = replaced
221
492
 
222
- return {
493
+ return createParameter({
223
494
  ...param,
224
- schema: transform(param.schema, visitor, options),
225
- }
495
+ schema: transform(param.schema, { ...options, parent: param }),
496
+ })
226
497
  }
227
498
  case 'Response': {
228
499
  let response = node
229
- const replaced = visitor.response?.(response)
500
+ const replaced = visitor.response?.(response, {
501
+ parent: parent as ParentOf<ResponseNode>,
502
+ })
230
503
  if (replaced) response = replaced
231
504
 
232
505
  return {
233
506
  ...response,
234
- schema: response.schema ? transform(response.schema, visitor, options) : undefined,
507
+ schema: transform(response.schema, { ...options, parent: response }),
235
508
  }
236
509
  }
510
+ case 'FunctionParameter':
511
+ case 'ParameterGroup':
512
+ case 'FunctionParameters':
513
+ case 'Type':
514
+ return node
515
+ default:
516
+ return node
237
517
  }
238
518
  }
239
-
240
519
  /**
241
- * Depth-first synchronous reduction. Collects non-`undefined` visitor return values into an array.
520
+ * Runs a depth-first synchronous collection pass.
521
+ *
522
+ * Non-`undefined` values returned by visitor callbacks are appended to the result.
523
+ *
524
+ * @example
525
+ * ```ts
526
+ * const ids = collect(root, {
527
+ * operation(node) {
528
+ * return node.operationId
529
+ * },
530
+ * })
531
+ * ```
532
+ *
533
+ * @example
534
+ * ```ts
535
+ * // Collect from only the current node
536
+ * const values = collect(root, { depth: 'shallow', root: () => 'root' })
537
+ * ```
242
538
  */
243
- export function collect<T>(node: Node, visitor: CollectVisitor<T>, options: VisitorOptions = {}): Array<T> {
244
- const recurse = (options.depth ?? visitorDepths.deep) === visitorDepths.deep
539
+ export function collect<T>(node: Node, options: CollectOptions<T>): Array<T> {
540
+ const { depth, parent, ...visitor } = options
541
+ const recurse = (depth ?? visitorDepths.deep) === visitorDepths.deep
245
542
  const results: Array<T> = []
246
543
 
247
544
  let v: T | undefined
248
545
  switch (node.kind) {
249
- case 'Root':
250
- v = visitor.root?.(node)
546
+ case 'Input':
547
+ v = visitor.input?.(node, { parent: parent as ParentOf<InputNode> })
548
+ break
549
+ case 'Output':
550
+ v = visitor.output?.(node, { parent: parent as ParentOf<OutputNode> })
251
551
  break
252
552
  case 'Operation':
253
- v = visitor.operation?.(node)
553
+ v = visitor.operation?.(node, {
554
+ parent: parent as ParentOf<OperationNode>,
555
+ })
254
556
  break
255
557
  case 'Schema':
256
- v = visitor.schema?.(node)
558
+ v = visitor.schema?.(node, { parent: parent as ParentOf<SchemaNode> })
257
559
  break
258
560
  case 'Property':
259
- v = visitor.property?.(node)
561
+ v = visitor.property?.(node, {
562
+ parent: parent as ParentOf<PropertyNode>,
563
+ })
260
564
  break
261
565
  case 'Parameter':
262
- v = visitor.parameter?.(node)
566
+ v = visitor.parameter?.(node, {
567
+ parent: parent as ParentOf<ParameterNode>,
568
+ })
263
569
  break
264
570
  case 'Response':
265
- v = visitor.response?.(node)
571
+ v = visitor.response?.(node, {
572
+ parent: parent as ParentOf<ResponseNode>,
573
+ })
574
+ break
575
+ case 'FunctionParameter':
576
+ case 'ParameterGroup':
577
+ case 'FunctionParameters':
266
578
  break
267
579
  }
268
580
  if (v !== undefined) results.push(v)
269
581
 
270
582
  for (const child of getChildren(node, recurse)) {
271
- for (const item of collect(child, visitor, options)) {
583
+ for (const item of collect(child, { ...options, parent: node })) {
272
584
  results.push(item)
273
585
  }
274
586
  }
package/dist/types.cjs DELETED
File without changes
package/dist/types.d.ts DELETED
@@ -1,2 +0,0 @@
1
- import { $ as UnionSchemaNode, A as MediaType, B as IntersectionSchemaNode, C as Node, D as OperationNode, E as HttpMethod, F as ComplexSchemaType, G as ScalarSchemaNode, H as ObjectSchemaNode, I as DateSchemaNode, J as SchemaNodeByType, K as ScalarSchemaType, L as DatetimeSchemaNode, M as ParameterLocation, N as ParameterNode, O as ResponseNode, P as ArraySchemaNode, Q as TimeSchemaNode, R as EnumSchemaNode, T as RootNode, U as PrimitiveSchemaType, V as NumberSchemaNode, W as RefSchemaNode, X as SpecialSchemaType, Y as SchemaType, Z as StringSchemaNode, d as Printer, et as UrlSchemaNode, f as PrinterFactoryOptions, g as DistributiveOmit, it as VisitorDepth, j as StatusCode, k as HttpStatusCode, m as PrinterHandlerContext, n as CollectVisitor, nt as BaseNode, p as PrinterHandler, q as SchemaNode, r as Visitor, rt as NodeKind, s as RefMap, t as AsyncVisitor, tt as PropertyNode, w as RootMeta, z as EnumValueNode } from "./visitor-CE4-xBHW.js";
2
- export { type ArraySchemaNode, type AsyncVisitor, type BaseNode, type CollectVisitor, type ComplexSchemaType, type DateSchemaNode, type DatetimeSchemaNode, type DistributiveOmit, type EnumSchemaNode, type EnumValueNode, type HttpMethod, type HttpStatusCode, type IntersectionSchemaNode, type MediaType, type Node, type NodeKind, type NumberSchemaNode, type ObjectSchemaNode, type OperationNode, type ParameterLocation, type ParameterNode, type PrimitiveSchemaType, type Printer, type PrinterFactoryOptions, type PrinterHandler, type PrinterHandlerContext, type PropertyNode, type RefMap, type RefSchemaNode, type ResponseNode, type RootMeta, type RootNode, type ScalarSchemaNode, type ScalarSchemaType, type SchemaNode, type SchemaNodeByType, type SchemaType, type SpecialSchemaType, type StatusCode, type StringSchemaNode, type TimeSchemaNode, type UnionSchemaNode, type UrlSchemaNode, type Visitor, type VisitorDepth };
package/dist/types.js DELETED
@@ -1 +0,0 @@
1
- export {};