@kubb/ast 5.0.0-alpha.72 → 5.0.0-alpha.74

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.72",
3
+ "version": "5.0.0-alpha.74",
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
  "ast",
package/src/constants.ts CHANGED
@@ -4,7 +4,10 @@ import type { HttpMethod } from './nodes/operation.ts'
4
4
  import type { SchemaType } from './nodes/schema.ts'
5
5
 
6
6
  /**
7
- * Traversal depth used by AST visitor utilities.
7
+ * Traversal depth for AST visitor utilities.
8
+ *
9
+ * - `'shallow'` — visits only the immediate node, skipping children.
10
+ * - `'deep'` — recursively visits all descendant nodes.
8
11
  */
9
12
  export type VisitorDepth = 'shallow' | 'deep'
10
13
 
@@ -34,15 +37,11 @@ export const nodeKinds = {
34
37
  } as const satisfies Record<string, NodeKind>
35
38
 
36
39
  /**
37
- * Canonical schema type strings used by AST schema nodes.
38
- *
39
- * These values are used across the AST as stable discriminators
40
- * (for example `schema.type === schemaTypes.object`).
40
+ * Schema type discriminators used by all AST schema nodes.
41
41
  *
42
- * The map is grouped by intent:
43
- * - primitives (`string`, `number`, `boolean`, ...)
44
- * - structural/composite (`object`, `array`, `union`, ...)
45
- * - special OpenAPI-oriented types (`ref`, `datetime`, `uuid`, ...)
42
+ * These values serve as stable discriminators across the AST (e.g., `schema.type === schemaTypes.object`).
43
+ * Grouped by category: primitives (`string`, `number`, `boolean`), structural types (`object`, `array`, `union`),
44
+ * and format-specific types (`date`, `uuid`, `email`). Use `isScalarPrimitive()` to check for scalar types.
46
45
  */
47
46
  export const schemaTypes = {
48
47
  /**
@@ -154,17 +153,26 @@ export const schemaTypes = {
154
153
  export type ScalarPrimitive = 'string' | 'number' | 'integer' | 'bigint' | 'boolean'
155
154
 
156
155
  /**
157
- * Primitive scalar schema types used when simplifying union members.
156
+ * Scalar primitive schema types used for union simplification and type narrowing.
157
+ *
158
+ * Use `isScalarPrimitive()` to safely check whether a type is a scalar primitive.
158
159
  */
159
160
  export const SCALAR_PRIMITIVE_TYPES = new Set<ScalarPrimitive>(['string', 'number', 'integer', 'bigint', 'boolean'])
160
161
 
161
162
  /**
162
- * Returns `true` when `type` is a scalar primitive schema type.
163
+ * Type guard that returns `true` when `type` is a scalar primitive schema type.
164
+ *
165
+ * Use this to check if a schema type can be directly assigned without wrapping (e.g., `string | number | boolean`).
163
166
  */
164
167
  export function isScalarPrimitive(type: string): type is ScalarPrimitive {
165
168
  return SCALAR_PRIMITIVE_TYPES.has(type as ScalarPrimitive)
166
169
  }
167
170
 
171
+ /**
172
+ * HTTP method identifiers used by operation nodes.
173
+ *
174
+ * Includes all standard HTTP methods (GET, POST, PUT, PATCH, DELETE, HEAD, OPTIONS, TRACE).
175
+ */
168
176
  export const httpMethods = {
169
177
  get: 'GET',
170
178
  post: 'POST',
@@ -177,19 +185,26 @@ export const httpMethods = {
177
185
  } as const satisfies Record<Lowercase<HttpMethod>, HttpMethod>
178
186
 
179
187
  /**
180
- * Default maximum number of concurrent callbacks used by `walk`.
188
+ * Default concurrency limit for `walk()` traversal utility.
181
189
  *
182
- * 30 is chosen to allow enough parallelism to overlap I/O-bound resolver calls
183
- * without overwhelming the event loop or causing excessive memory pressure during
184
- * large spec traversals.
190
+ * Set to 30 to balance I/O-bound resolver parallelism against event loop pressure and memory usage during large spec traversals.
191
+ * Use `WALK_CONCURRENCY` when calling `walk()` or override for different hardware constraints.
185
192
  *
186
193
  * @example
187
194
  * ```ts
195
+ * import { walk, WALK_CONCURRENCY } from '@kubb/ast'
196
+ *
188
197
  * walk(root, { concurrency: WALK_CONCURRENCY, root: () => {} })
189
198
  * ```
190
199
  */
191
200
  export const WALK_CONCURRENCY = 30
192
201
 
202
+ /**
203
+ * Common MIME types used in request/response content negotiation.
204
+ *
205
+ * Covers JSON, XML, form data, PDFs, images, audio, and video formats.
206
+ * Use these as keys when serializing request/response bodies.
207
+ */
193
208
  export const mediaTypes = {
194
209
  applicationJson: 'application/json',
195
210
  applicationXml: 'application/xml',
package/src/utils.ts CHANGED
@@ -22,13 +22,17 @@ import { collect } from './visitor.ts'
22
22
  const plainStringTypes = new Set<SchemaType>(['string', 'uuid', 'email', 'url', 'datetime'] as const)
23
23
 
24
24
  /**
25
- * Returns a merged schema view for a ref node, combining the resolved `node.schema`
26
- * (base from the referenced definition) with any usage-site sibling fields set directly
27
- * on the ref node (description, readOnly, nullable, deprecated, etc.).
25
+ * Merges a ref node with its resolved schema, giving usage-site fields precedence.
28
26
  *
29
- * Usage-site fields take precedence over the resolved schema's own fields when both are defined.
27
+ * Usage-site fields (`description`, `readOnly`, `nullable`, `deprecated`) on the ref node
28
+ * override the same fields in the resolved `node.schema`. Non-ref nodes are returned unchanged.
30
29
  *
31
- * For non-ref nodes the node itself is returned unchanged.
30
+ * @example
31
+ * ```ts
32
+ * // Ref with description override
33
+ * const ref = createSchema({ type: 'ref', ref: '#/components/schemas/Pet', description: 'A cute pet' })
34
+ * const merged = syncSchemaRef(ref) // merges with resolved Pet schema
35
+ * ```
32
36
  */
33
37
  export function syncSchemaRef(node: SchemaNode): SchemaNode {
34
38
  const ref = narrowSchema(node, 'ref')
@@ -45,16 +49,10 @@ export function syncSchemaRef(node: SchemaNode): SchemaNode {
45
49
  }
46
50
 
47
51
  /**
48
- * Returns `true` when a schema is emitted as a plain `string` type.
49
- *
50
- * - `string`, `uuid`, `email`, `url`, `datetime` are always plain strings.
51
- * - `date` and `time` are plain strings when their `representation` is `'string'` rather than `'date'`.
52
+ * Type guard that returns `true` when a schema emits as a plain `string` type.
52
53
  *
53
- * @example
54
- * ```ts
55
- * isStringType(createSchema({ type: 'uuid' })) // true
56
- * isStringType(createSchema({ type: 'date', representation: 'date' })) // false
57
- * ```
54
+ * Covers `string`, `uuid`, `email`, `url`, and `datetime` types. For `date` and `time`
55
+ * types, returns `true` only when `representation` is `'string'` rather than `'date'`.
58
56
  */
59
57
  export function isStringType(node: SchemaNode): boolean {
60
58
  if (plainStringTypes.has(node.type)) {
@@ -72,19 +70,9 @@ export function isStringType(node: SchemaNode): boolean {
72
70
  /**
73
71
  * Applies casing rules to parameter names and returns a new parameter array.
74
72
  *
75
- * The input array is not mutated.
76
- * If `casing` is not set, the original array is returned unchanged.
77
- *
78
- * Use this before passing parameters to schema builders so that property keys
79
- * in generated output match the desired casing while preserving
80
- * `OperationNode.parameters` for other consumers.
81
- *
82
- * @example
83
- * ```ts
84
- * const params = [createParameter({ name: 'pet_id', in: 'query', schema: createSchema({ type: 'string' }) })]
85
- * const cased = caseParams(params, 'camelcase')
86
- * // cased[0].name === 'petId'
87
- * ```
73
+ * Use this before passing parameters to schema builders so output property keys match
74
+ * the desired casing while preserving `OperationNode.parameters` for other consumers.
75
+ * The input array is not mutated. When `casing` is not set, the original array is returned unchanged.
88
76
  */
89
77
  export function caseParams(params: Array<ParameterNode>, casing: 'camelcase' | undefined): Array<ParameterNode> {
90
78
  if (!casing) {
@@ -305,20 +293,12 @@ function resolveParamsType({
305
293
  }
306
294
 
307
295
  /**
308
- * Converts an {@link OperationNode} into a {@link FunctionParametersNode}.
296
+ * Converts an `OperationNode` into function parameters for code generation.
309
297
  *
310
- * Centralizes the per-plugin `getParams()` pattern. Provide a `resolver` for
311
- * type resolution and `extraParams` for plugin-specific trailing parameters.
312
- *
313
- * @example
314
- * ```ts
315
- * const params = createOperationParams(node, {
316
- * paramsType: 'inline',
317
- * pathParamsType: 'inline',
318
- * resolver: tsResolver,
319
- * extraParams: [createFunctionParameter({ name: 'options', type: createParamsType({ variant: 'reference', name: 'Partial<RequestOptions>' }), default: '{}' })],
320
- * })
321
- * ```
298
+ * Centralizes parameter grouping logic for all plugins. Provide a `resolver` for type name resolution
299
+ * and `extraParams` for plugin-specific trailing parameters (e.g., `options` objects).
300
+ * Supports three grouping modes: `object` (single destructured param), `inline` (separate params),
301
+ * and `inlineSpread` (rest parameter). Use `CreateOperationParamsOptions` to fine-tune output.
322
302
  */
323
303
  export function createOperationParams(node: OperationNode, options: CreateOperationParamsOptions): FunctionParametersNode {
324
304
  const { paramsType, pathParamsType, paramsCasing, resolver, pathParamsDefault, extraParams = [], paramNames, typeWrapper } = options
@@ -599,9 +579,9 @@ function sortKey(node: { name?: string | Array<unknown>; isTypeOnly?: boolean; p
599
579
  }
600
580
 
601
581
  /**
602
- * Deduplicates an array of `SourceNode` objects.
603
- * Named sources are deduplicated by `name + isExportable + isTypeOnly`.
604
- * Unnamed sources are deduplicated by object reference.
582
+ * Deduplicates and merges `SourceNode` objects by `name + isExportable + isTypeOnly`.
583
+ *
584
+ * Unnamed sources are deduplicated by object reference. Returns a deduplicated array in original order.
605
585
  */
606
586
  export function combineSources(sources: Array<SourceNode>): Array<SourceNode> {
607
587
  const seen = new Map<string, SourceNode>()
@@ -613,8 +593,10 @@ export function combineSources(sources: Array<SourceNode>): Array<SourceNode> {
613
593
  }
614
594
 
615
595
  /**
616
- * Deduplicates and merges an array of `ExportNode` objects.
617
- * Exports with the same path and `isTypeOnly` flag have their names merged.
596
+ * Deduplicates and merges `ExportNode` objects by path and type.
597
+ *
598
+ * Named exports with the same path and `isTypeOnly` flag have their names merged into a single export.
599
+ * Non-array exports are deduplicated by exact identity. Returns a sorted, deduplicated array.
618
600
  */
619
601
  export function combineExports(exports: Array<ExportNode>): Array<ExportNode> {
620
602
  const result: Array<ExportNode> = []
@@ -658,9 +640,12 @@ export function combineExports(exports: Array<ExportNode>): Array<ExportNode> {
658
640
  }
659
641
 
660
642
  /**
661
- * Deduplicates and merges an array of `ImportNode` objects.
662
- * Filters out unused imports (names not referenced in `source` or re-exported).
663
- * Imports with the same path and `isTypeOnly` flag have their names merged.
643
+ * Deduplicates and merges `ImportNode` objects, filtering out unused imports.
644
+ *
645
+ * Retains imports that are referenced in `source` or re-exported. Imports with the same path and
646
+ * `isTypeOnly` flag have their names merged. Returns a sorted, deduplicated, filtered array.
647
+ *
648
+ * @note Use this when combining imports from multiple files to avoid duplicate declarations.
664
649
  */
665
650
  export function combineImports(imports: Array<ImportNode>, exports: Array<ExportNode>, source?: string): Array<ImportNode> {
666
651
  // Build a lookup of all exported names to retain imports that are re-exported
@@ -714,11 +699,10 @@ export function combineImports(imports: Array<ImportNode>, exports: Array<Export
714
699
  }
715
700
 
716
701
  /**
717
- * Recursively extracts all string content embedded in a {@link CodeNode} tree.
702
+ * Extracts all string content from a `CodeNode` tree recursively.
718
703
  *
719
- * Includes text node values, and string attribute fields (`params`, `generics`,
720
- * `returnType`, `type`) that may reference identifiers needing imports.
721
- * Used by `createFile` to build the full source string for import filtering.
704
+ * Collects text node values, identifier references in string fields (`params`, `generics`, `returnType`, `type`),
705
+ * and nested node content. Used internally to build the full source string for import filtering.
722
706
  */
723
707
  export function extractStringsFromNodes(nodes: Array<CodeNode> | undefined): string {
724
708
  if (!nodes?.length) return ''
@@ -743,9 +727,10 @@ export function extractStringsFromNodes(nodes: Array<CodeNode> | undefined): str
743
727
  }
744
728
 
745
729
  /**
746
- * Resolves the referenced schema name of a `ref` node, falling back through
747
- * `ref` → `name` → nested `schema.name`. Returns `undefined` for non-ref
748
- * nodes or when no name can be resolved.
730
+ * Resolves the schema name of a ref node, falling back through `ref` → `name` → nested `schema.name`.
731
+ *
732
+ * Returns `undefined` for non-ref nodes or when no name can be resolved. Use this to get a schema's
733
+ * identifier for type definitions or error messages.
749
734
  *
750
735
  * @example
751
736
  * ```ts
@@ -761,15 +746,12 @@ export function resolveRefName(node: SchemaNode | undefined): string | undefined
761
746
  }
762
747
 
763
748
  /**
764
- * Recursively collects every named schema referenced (transitively) from
765
- * `node` via `ref` edges. Refs are followed by name only — the resolved
766
- * `node.schema` of a ref is not traversed inline.
749
+ * Collects every named schema referenced (transitively) from a node via ref edges.
767
750
  *
768
- * @example
769
- * ```ts
770
- * const refs = collectReferencedSchemaNames(petSchema)
771
- * // => Set { 'Cat', 'Dog' }
772
- * ```
751
+ * Refs are followed by name only — the resolved `node.schema` is not traversed inline.
752
+ * Use this to determine schema dependencies, build reference graphs, or detect what schemas need to be emitted.
753
+ *
754
+ * @note Returns a Set of schema names for efficient membership testing.
773
755
  */
774
756
  export function collectReferencedSchemaNames(node: SchemaNode | undefined, out: Set<string> = new Set()): Set<string> {
775
757
  if (!node) return out
@@ -787,26 +769,13 @@ export function collectReferencedSchemaNames(node: SchemaNode | undefined, out:
787
769
  }
788
770
 
789
771
  /**
790
- * Identifies every named schema that participates in a circular dependency
791
- * chain — including direct self-loops (e.g. `TreeNode → TreeNode`) and indirect
792
- * cycles spanning multiple schemas (e.g. `Pet → Cat → Pet`).
793
- *
794
- * The returned set contains schema names. Plugins that translate schemas into
795
- * a host language can use this to wrap recursive positions in a deferred
796
- * construct (lazy getter, `z.lazy(() => …)`, etc.) and avoid runtime stack
797
- * overflows when the generated code is executed.
772
+ * Identifies all schemas that participate in circular dependency chains, including direct self-loops.
798
773
  *
799
- * Refs are followed by name only `node.schema` (the resolved referent) is
800
- * not traversed inline, which keeps the algorithm linear in the size of the
801
- * schema graph.
774
+ * Returns a Set of schema names with circular dependencies. Use this to wrap recursive schema positions
775
+ * in deferred constructs (lazy getter, `z.lazy(() => …)`) to prevent infinite recursion when generated code runs.
776
+ * Refs are followed by name only, keeping the algorithm linear in the schema graph size.
802
777
  *
803
- * @example
804
- * ```ts
805
- * const circular = findCircularSchemas(inputNode.schemas)
806
- * if (circular.has('Pet')) {
807
- * // emit lazy wrapper for any property whose schema references Pet
808
- * }
809
- * ```
778
+ * @note Call this once on the full schema graph, then use `containsCircularRef()` to check individual schemas.
810
779
  */
811
780
  export function findCircularSchemas(schemas: ReadonlyArray<SchemaNode>): Set<string> {
812
781
  const graph = new Map<string, Set<string>>()
@@ -838,21 +807,12 @@ export function findCircularSchemas(schemas: ReadonlyArray<SchemaNode>): Set<str
838
807
  }
839
808
 
840
809
  /**
841
- * Returns true when `node` (or anything nested within it) carries a `ref`
842
- * whose resolved name belongs to `circularSchemas`.
810
+ * Type guard returning `true` when a schema or anything nested within it contains a ref to a circular schema.
843
811
  *
844
- * When `excludeName` is provided, refs to that name are ignored — useful
845
- * when self-references are already handled separately from cross-schema
846
- * cycles (e.g. the faker plugin emits `undefined as any` for direct
847
- * self-recursion but a lazy getter for indirect cycles).
812
+ * Use `excludeName` to ignore refs to specific schemas (useful when self-references are handled separately).
813
+ * Commonly used with `findCircularSchemas()` to detect where lazy wrappers are needed in code generation.
848
814
  *
849
- * @example
850
- * ```ts
851
- * const circular = findCircularSchemas(schemas)
852
- * if (containsCircularRef(property.schema, { circularSchemas: circular, excludeName: 'Pet' })) {
853
- * // emit `get foo() { return fakeCat() }` instead of eager call
854
- * }
855
- * ```
815
+ * @note Returns `true` for the first matching circular ref found; use for fast dependency checks.
856
816
  */
857
817
  export function containsCircularRef(
858
818
  node: SchemaNode | undefined,