@kubb/ast 5.0.0-alpha.24 → 5.0.0-alpha.25

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.
@@ -105,6 +105,14 @@ declare const schemaTypes: {
105
105
  * URL value.
106
106
  */
107
107
  readonly url: "url";
108
+ /**
109
+ * IPv4 address value.
110
+ */
111
+ readonly ipv4: "ipv4";
112
+ /**
113
+ * IPv6 address value.
114
+ */
115
+ readonly ipv6: "ipv6";
108
116
  /**
109
117
  * Binary/blob value.
110
118
  */
@@ -472,7 +480,7 @@ type ComplexSchemaType = 'tuple' | 'union' | 'intersection' | 'enum';
472
480
  /**
473
481
  * Schema types that need special handling in generators.
474
482
  */
475
- type SpecialSchemaType = 'ref' | 'datetime' | 'time' | 'uuid' | 'email' | 'url' | 'blob';
483
+ type SpecialSchemaType = 'ref' | 'datetime' | 'time' | 'uuid' | 'email' | 'url' | 'ipv4' | 'ipv6' | 'blob';
476
484
  /**
477
485
  * All schema type strings.
478
486
  */
@@ -480,7 +488,7 @@ type SchemaType = PrimitiveSchemaType | ComplexSchemaType | SpecialSchemaType;
480
488
  /**
481
489
  * Scalar schema types without extra object/array/ref structure.
482
490
  */
483
- type ScalarSchemaType = Exclude<SchemaType, 'object' | 'array' | 'tuple' | 'union' | 'intersection' | 'enum' | 'ref' | 'datetime' | 'date' | 'time' | 'string' | 'number' | 'integer' | 'bigint' | 'url'>;
491
+ type ScalarSchemaType = Exclude<SchemaType, 'object' | 'array' | 'tuple' | 'union' | 'intersection' | 'enum' | 'ref' | 'datetime' | 'date' | 'time' | 'string' | 'number' | 'integer' | 'bigint' | 'url' | 'uuid' | 'email'>;
484
492
  /**
485
493
  * Fields shared by all schema nodes.
486
494
  */
@@ -559,6 +567,10 @@ type ObjectSchemaNode = SchemaNodeBase & {
559
567
  * Schema type discriminator.
560
568
  */
561
569
  type: 'object';
570
+ /**
571
+ * Primitive type — always `'object'` for object schemas.
572
+ */
573
+ primitive: 'object';
562
574
  /**
563
575
  * Ordered object properties.
564
576
  */
@@ -566,14 +578,23 @@ type ObjectSchemaNode = SchemaNodeBase & {
566
578
  /**
567
579
  * Additional object properties behavior:
568
580
  * - `true`: allow any value
581
+ * - `false`: reject unknown properties (maps to `.strict()` in Zod)
569
582
  * - `SchemaNode`: allow values that match that schema
570
- * - `undefined`: no additional properties
583
+ * - `undefined`: no additional properties constraint (open object)
571
584
  */
572
- additionalProperties?: SchemaNode | true;
585
+ additionalProperties?: SchemaNode | boolean;
573
586
  /**
574
587
  * Pattern-based property schemas.
575
588
  */
576
589
  patternProperties?: Record<string, SchemaNode>;
590
+ /**
591
+ * Minimum number of properties allowed.
592
+ */
593
+ minProperties?: number;
594
+ /**
595
+ * Maximum number of properties allowed.
596
+ */
597
+ maxProperties?: number;
577
598
  };
578
599
  /**
579
600
  * Array-like schema (`array` or `tuple`).
@@ -736,6 +757,15 @@ type RefSchemaNode = SchemaNodeBase & {
736
757
  * Pattern copied from a sibling `pattern` field.
737
758
  */
738
759
  pattern?: string;
760
+ /**
761
+ * The fully-parsed schema that this ref resolves to.
762
+ * Populated during OAS parsing when the referenced definition can be resolved.
763
+ * `undefined` when the ref cannot be resolved or is part of a circular chain.
764
+ *
765
+ * Useful for inspecting the referenced schema's structure (e.g. `primitive`, `properties`)
766
+ * without following the reference manually.
767
+ */
768
+ schema?: SchemaNode;
739
769
  };
740
770
  /**
741
771
  * Datetime schema.
@@ -845,6 +875,10 @@ type NumberSchemaNode = SchemaNodeBase & {
845
875
  * Exclusive maximum value.
846
876
  */
847
877
  exclusiveMaximum?: number;
878
+ /**
879
+ * The value must be a multiple of this number.
880
+ */
881
+ multipleOf?: number;
848
882
  };
849
883
  /**
850
884
  * Scalar schema with no extra constraints.
@@ -878,6 +912,64 @@ type UrlSchemaNode = SchemaNodeBase & {
878
912
  * OpenAPI-style path template, for example, `'/pets/{petId}'`.
879
913
  */
880
914
  path?: string;
915
+ /**
916
+ * Minimum string length.
917
+ */
918
+ min?: number;
919
+ /**
920
+ * Maximum string length.
921
+ */
922
+ max?: number;
923
+ };
924
+ /**
925
+ * Format-string schema for string-based formats that support length constraints.
926
+ *
927
+ * @example
928
+ * ```ts
929
+ * const uuidSchema: FormatStringSchemaNode = { kind: 'Schema', type: 'uuid', min: 36, max: 36 }
930
+ * ```
931
+ */
932
+ type FormatStringSchemaNode = SchemaNodeBase & {
933
+ /**
934
+ * Schema type discriminator.
935
+ */
936
+ type: 'uuid' | 'email';
937
+ /**
938
+ * Minimum string length.
939
+ */
940
+ min?: number;
941
+ /**
942
+ * Maximum string length.
943
+ */
944
+ max?: number;
945
+ };
946
+ /**
947
+ * IPv4 address schema node.
948
+ *
949
+ * @example
950
+ * ```ts
951
+ * const ipv4Schema: Ipv4SchemaNode = { kind: 'Schema', type: 'ipv4' }
952
+ * ```
953
+ */
954
+ type Ipv4SchemaNode = SchemaNodeBase & {
955
+ /**
956
+ * Schema type discriminator.
957
+ */
958
+ type: 'ipv4';
959
+ };
960
+ /**
961
+ * IPv6 address schema node.
962
+ *
963
+ * @example
964
+ * ```ts
965
+ * const ipv6Schema: Ipv6SchemaNode = { kind: 'Schema', type: 'ipv6' }
966
+ * ```
967
+ */
968
+ type Ipv6SchemaNode = SchemaNodeBase & {
969
+ /**
970
+ * Schema type discriminator.
971
+ */
972
+ type: 'ipv6';
881
973
  };
882
974
  /**
883
975
  * Mapping from schema type literals to concrete schema node types.
@@ -904,15 +996,17 @@ type SchemaNodeByType = {
904
996
  unknown: ScalarSchemaNode;
905
997
  void: ScalarSchemaNode;
906
998
  never: ScalarSchemaNode;
907
- uuid: ScalarSchemaNode;
908
- email: ScalarSchemaNode;
999
+ uuid: FormatStringSchemaNode;
1000
+ email: FormatStringSchemaNode;
909
1001
  url: UrlSchemaNode;
1002
+ ipv4: Ipv4SchemaNode;
1003
+ ipv6: Ipv6SchemaNode;
910
1004
  blob: ScalarSchemaNode;
911
1005
  };
912
1006
  /**
913
1007
  * Union of all schema node types.
914
1008
  */
915
- type SchemaNode = ObjectSchemaNode | ArraySchemaNode | UnionSchemaNode | IntersectionSchemaNode | EnumSchemaNode | RefSchemaNode | DatetimeSchemaNode | DateSchemaNode | TimeSchemaNode | StringSchemaNode | NumberSchemaNode | UrlSchemaNode | ScalarSchemaNode;
1009
+ type SchemaNode = ObjectSchemaNode | ArraySchemaNode | UnionSchemaNode | IntersectionSchemaNode | EnumSchemaNode | RefSchemaNode | DatetimeSchemaNode | DateSchemaNode | TimeSchemaNode | StringSchemaNode | NumberSchemaNode | UrlSchemaNode | FormatStringSchemaNode | Ipv4SchemaNode | Ipv6SchemaNode | ScalarSchemaNode;
916
1010
  //#endregion
917
1011
  //#region src/nodes/parameter.d.ts
918
1012
  type ParameterLocation = 'path' | 'query' | 'header' | 'cookie';
@@ -1350,8 +1444,9 @@ declare function syncOptionality(schema: SchemaNode, required: boolean): SchemaN
1350
1444
  * ```
1351
1445
  */
1352
1446
  type DistributiveOmit<T, K extends PropertyKey> = T extends unknown ? Omit<T, K> : never;
1353
- type CreateSchemaObjectInput = Omit<ObjectSchemaNode, 'kind' | 'properties'> & {
1447
+ type CreateSchemaObjectInput = Omit<ObjectSchemaNode, 'kind' | 'properties' | 'primitive'> & {
1354
1448
  properties?: Array<PropertyNode>;
1449
+ primitive?: 'object';
1355
1450
  };
1356
1451
  type CreateSchemaInput = CreateSchemaObjectInput | DistributiveOmit<Exclude<SchemaNode, ObjectSchemaNode>, 'kind'>;
1357
1452
  type CreateSchemaOutput<T extends CreateSchemaInput> = InferSchemaNode<T> & {
@@ -1400,17 +1495,24 @@ declare function createOperation(props: Pick<OperationNode, 'operationId' | 'met
1400
1495
  /**
1401
1496
  * Creates a `SchemaNode`, narrowed to the variant of `props.type`.
1402
1497
  * For object schemas, `properties` defaults to an empty array.
1498
+ * `primitive` is automatically inferred from `type` when not explicitly provided.
1403
1499
  *
1404
1500
  * @example
1405
1501
  * ```ts
1406
1502
  * const scalar = createSchema({ type: 'string' })
1407
- * // { kind: 'Schema', type: 'string' }
1503
+ * // { kind: 'Schema', type: 'string', primitive: 'string' }
1504
+ * ```
1505
+ *
1506
+ * @example
1507
+ * ```ts
1508
+ * const uuid = createSchema({ type: 'uuid' })
1509
+ * // { kind: 'Schema', type: 'uuid', primitive: 'string' }
1408
1510
  * ```
1409
1511
  *
1410
1512
  * @example
1411
1513
  * ```ts
1412
1514
  * const object = createSchema({ type: 'object' })
1413
- * // { kind: 'Schema', type: 'object', properties: [] }
1515
+ * // { kind: 'Schema', type: 'object', primitive: 'object', properties: [] }
1414
1516
  * ```
1415
1517
  *
1416
1518
  * @example
@@ -2068,5 +2170,5 @@ declare function composeTransformers(...visitors: Array<Visitor>): Visitor;
2068
2170
  */
2069
2171
  declare function collect<T>(node: Node, options: CollectOptions<T>): Array<T>;
2070
2172
  //#endregion
2071
- export { ObjectSchemaNode as $, syncOptionality as A, HttpStatusCode as B, createParameter as C, httpMethods as Ct, createRoot as D, createResponse as E, schemaTypes as Et, RootMeta as F, ArraySchemaNode as G, StatusCode as H, RootNode as I, DatetimeSchemaNode as J, ComplexSchemaType as K, HttpMethod as L, InferSchemaNode as M, ParserOptions as N, createSchema as O, Node as P, NumberSchemaNode as Q, OperationNode as R, createOperation as S, VisitorDepth as St, createProperty as T, mediaTypes as Tt, ParameterLocation as U, MediaType as V, ParameterNode as W, EnumValueNode as X, EnumSchemaNode as Y, IntersectionSchemaNode as Z, createPrinterFactory as _, ParameterGroupNode as _t, TransformOptions as a, SchemaNodeByType as at, createFunctionParameter as b, NodeKind as bt, WalkOptions as c, StringSchemaNode as ct, transform as d, UrlSchemaNode as dt, PrimitiveSchemaType as et, walk as f, PropertyNode as ft, PrinterFactoryOptions as g, FunctionParametersNode as gt, Printer as h, FunctionParameterNode as ht, ParentOf as i, SchemaNode as it, InferSchema as j, createTypeNode as k, collect as l, TimeSchemaNode as lt, extractRefName as m, FunctionNodeType as mt, CollectOptions as n, ScalarSchemaNode as nt, Visitor as o, SchemaType as ot, RefMap as p, FunctionNode as pt, DateSchemaNode as q, CollectVisitor as r, ScalarSchemaType as rt, VisitorContext as s, SpecialSchemaType as st, AsyncVisitor as t, RefSchemaNode as tt, composeTransformers as u, UnionSchemaNode as ut, definePrinter as v, TypeNode as vt, createParameterGroup as w, isScalarPrimitive as wt, createFunctionParameters as x, ScalarPrimitive as xt, DistributiveOmit as y, BaseNode as yt, ResponseNode as z };
2072
- //# sourceMappingURL=visitor-DsdLcLjR.d.ts.map
2173
+ export { Ipv4SchemaNode as $, syncOptionality as A, HttpStatusCode as B, createParameter as C, NodeKind as Ct, createRoot as D, isScalarPrimitive as Dt, createResponse as E, httpMethods as Et, RootMeta as F, ArraySchemaNode as G, StatusCode as H, RootNode as I, DatetimeSchemaNode as J, ComplexSchemaType as K, HttpMethod as L, InferSchemaNode as M, ParserOptions as N, createSchema as O, mediaTypes as Ot, Node as P, IntersectionSchemaNode as Q, OperationNode as R, createOperation as S, BaseNode as St, createProperty as T, VisitorDepth as Tt, ParameterLocation as U, MediaType as V, ParameterNode as W, EnumValueNode as X, EnumSchemaNode as Y, FormatStringSchemaNode as Z, createPrinterFactory as _, FunctionNodeType as _t, TransformOptions as a, ScalarSchemaNode as at, createFunctionParameter as b, ParameterGroupNode as bt, WalkOptions as c, SchemaNodeByType as ct, transform as d, StringSchemaNode as dt, Ipv6SchemaNode as et, walk as f, TimeSchemaNode as ft, PrinterFactoryOptions as g, FunctionNode as gt, Printer as h, PropertyNode as ht, ParentOf as i, RefSchemaNode as it, InferSchema as j, createTypeNode as k, schemaTypes as kt, collect as l, SchemaType as lt, extractRefName as m, UrlSchemaNode as mt, CollectOptions as n, ObjectSchemaNode as nt, Visitor as o, ScalarSchemaType as ot, RefMap as p, UnionSchemaNode as pt, DateSchemaNode as q, CollectVisitor as r, PrimitiveSchemaType as rt, VisitorContext as s, SchemaNode as st, AsyncVisitor as t, NumberSchemaNode as tt, composeTransformers as u, SpecialSchemaType as ut, definePrinter as v, FunctionParameterNode as vt, createParameterGroup as w, ScalarPrimitive as wt, createFunctionParameters as x, TypeNode as xt, DistributiveOmit as y, FunctionParametersNode as yt, ResponseNode as z };
2174
+ //# sourceMappingURL=visitor-C2XD9px4.d.ts.map
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@kubb/ast",
3
- "version": "5.0.0-alpha.24",
3
+ "version": "5.0.0-alpha.25",
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
@@ -126,6 +126,14 @@ export const schemaTypes = {
126
126
  * URL value.
127
127
  */
128
128
  url: 'url',
129
+ /**
130
+ * IPv4 address value.
131
+ */
132
+ ipv4: 'ipv4',
133
+ /**
134
+ * IPv6 address value.
135
+ */
136
+ ipv6: 'ipv6',
129
137
  /**
130
138
  * Binary/blob value.
131
139
  */
package/src/factory.ts CHANGED
@@ -6,6 +6,7 @@ import type {
6
6
  OperationNode,
7
7
  ParameterGroupNode,
8
8
  ParameterNode,
9
+ PrimitiveSchemaType,
9
10
  PropertyNode,
10
11
  ResponseNode,
11
12
  RootNode,
@@ -42,7 +43,7 @@ export function syncOptionality(schema: SchemaNode, required: boolean): SchemaNo
42
43
  */
43
44
  export type DistributiveOmit<T, K extends PropertyKey> = T extends unknown ? Omit<T, K> : never
44
45
 
45
- type CreateSchemaObjectInput = Omit<ObjectSchemaNode, 'kind' | 'properties'> & { properties?: Array<PropertyNode> }
46
+ type CreateSchemaObjectInput = Omit<ObjectSchemaNode, 'kind' | 'properties' | 'primitive'> & { properties?: Array<PropertyNode>; primitive?: 'object' }
46
47
  type CreateSchemaInput = CreateSchemaObjectInput | DistributiveOmit<Exclude<SchemaNode, ObjectSchemaNode>, 'kind'>
47
48
  type CreateSchemaOutput<T extends CreateSchemaInput> = InferSchemaNode<T> & { kind: 'Schema' }
48
49
 
@@ -105,20 +106,53 @@ export function createOperation(
105
106
  }
106
107
  }
107
108
 
109
+ /**
110
+ * Maps schema `type` to its underlying `primitive`.
111
+ * Primitive types map to themselves; special string formats map to `'string'`.
112
+ * Complex types (`ref`, `enum`, `union`, `intersection`, `tuple`, `blob`) are left unset.
113
+ */
114
+ const TYPE_TO_PRIMITIVE: Partial<Record<SchemaNode['type'], PrimitiveSchemaType>> = {
115
+ string: 'string',
116
+ number: 'number',
117
+ integer: 'integer',
118
+ bigint: 'bigint',
119
+ boolean: 'boolean',
120
+ null: 'null',
121
+ any: 'any',
122
+ unknown: 'unknown',
123
+ void: 'void',
124
+ never: 'never',
125
+ object: 'object',
126
+ array: 'array',
127
+ date: 'date',
128
+ uuid: 'string',
129
+ email: 'string',
130
+ url: 'string',
131
+ datetime: 'string',
132
+ time: 'string',
133
+ }
134
+
108
135
  /**
109
136
  * Creates a `SchemaNode`, narrowed to the variant of `props.type`.
110
137
  * For object schemas, `properties` defaults to an empty array.
138
+ * `primitive` is automatically inferred from `type` when not explicitly provided.
111
139
  *
112
140
  * @example
113
141
  * ```ts
114
142
  * const scalar = createSchema({ type: 'string' })
115
- * // { kind: 'Schema', type: 'string' }
143
+ * // { kind: 'Schema', type: 'string', primitive: 'string' }
144
+ * ```
145
+ *
146
+ * @example
147
+ * ```ts
148
+ * const uuid = createSchema({ type: 'uuid' })
149
+ * // { kind: 'Schema', type: 'uuid', primitive: 'string' }
116
150
  * ```
117
151
  *
118
152
  * @example
119
153
  * ```ts
120
154
  * const object = createSchema({ type: 'object' })
121
- * // { kind: 'Schema', type: 'object', properties: [] }
155
+ * // { kind: 'Schema', type: 'object', primitive: 'object', properties: [] }
122
156
  * ```
123
157
  *
124
158
  * @example
@@ -133,11 +167,13 @@ export function createOperation(
133
167
  export function createSchema<T extends CreateSchemaInput>(props: T): CreateSchemaOutput<T>
134
168
  export function createSchema(props: CreateSchemaInput): SchemaNode
135
169
  export function createSchema(props: CreateSchemaInput): SchemaNode {
170
+ const inferredPrimitive = TYPE_TO_PRIMITIVE[props.type as keyof typeof TYPE_TO_PRIMITIVE]
171
+
136
172
  if (props['type'] === 'object') {
137
- return { properties: [], ...props, kind: 'Schema' } as CreateSchemaOutput<typeof props>
173
+ return { properties: [], primitive: 'object', ...props, kind: 'Schema' } as CreateSchemaOutput<typeof props>
138
174
  }
139
175
 
140
- return { ...props, kind: 'Schema' } as CreateSchemaOutput<typeof props>
176
+ return { primitive: inferredPrimitive, ...props, kind: 'Schema' } as CreateSchemaOutput<typeof props>
141
177
  }
142
178
 
143
179
  /**
package/src/index.ts CHANGED
@@ -21,5 +21,5 @@ export { extractRefName } from './refs.ts'
21
21
  export { childName, collectImports, enumPropName, findDiscriminator } from './resolvers.ts'
22
22
  export { mergeAdjacentObjects, setDiscriminatorEnum, setEnumName, simplifyUnion } from './transformers.ts'
23
23
  export type { OperationParamsResolver } from './utils.ts'
24
- export { caseParams, createDiscriminantNode, createOperationParams, isStringType } from './utils.ts'
24
+ export { caseParams, createDiscriminantNode, createOperationParams, isStringType, syncSchemaRef } from './utils.ts'
25
25
  export { collect, composeTransformers, transform, walk } from './visitor.ts'
@@ -21,7 +21,10 @@ export type {
21
21
  DatetimeSchemaNode,
22
22
  EnumSchemaNode,
23
23
  EnumValueNode,
24
+ FormatStringSchemaNode,
24
25
  IntersectionSchemaNode,
26
+ Ipv4SchemaNode,
27
+ Ipv6SchemaNode,
25
28
  NumberSchemaNode,
26
29
  ObjectSchemaNode,
27
30
  PrimitiveSchemaType,
@@ -63,7 +63,7 @@ export type ComplexSchemaType = 'tuple' | 'union' | 'intersection' | 'enum'
63
63
  /**
64
64
  * Schema types that need special handling in generators.
65
65
  */
66
- export type SpecialSchemaType = 'ref' | 'datetime' | 'time' | 'uuid' | 'email' | 'url' | 'blob'
66
+ export type SpecialSchemaType = 'ref' | 'datetime' | 'time' | 'uuid' | 'email' | 'url' | 'ipv4' | 'ipv6' | 'blob'
67
67
 
68
68
  /**
69
69
  * All schema type strings.
@@ -75,7 +75,23 @@ export type SchemaType = PrimitiveSchemaType | ComplexSchemaType | SpecialSchema
75
75
  */
76
76
  export type ScalarSchemaType = Exclude<
77
77
  SchemaType,
78
- 'object' | 'array' | 'tuple' | 'union' | 'intersection' | 'enum' | 'ref' | 'datetime' | 'date' | 'time' | 'string' | 'number' | 'integer' | 'bigint' | 'url'
78
+ | 'object'
79
+ | 'array'
80
+ | 'tuple'
81
+ | 'union'
82
+ | 'intersection'
83
+ | 'enum'
84
+ | 'ref'
85
+ | 'datetime'
86
+ | 'date'
87
+ | 'time'
88
+ | 'string'
89
+ | 'number'
90
+ | 'integer'
91
+ | 'bigint'
92
+ | 'url'
93
+ | 'uuid'
94
+ | 'email'
79
95
  >
80
96
 
81
97
  /**
@@ -157,6 +173,10 @@ export type ObjectSchemaNode = SchemaNodeBase & {
157
173
  * Schema type discriminator.
158
174
  */
159
175
  type: 'object'
176
+ /**
177
+ * Primitive type — always `'object'` for object schemas.
178
+ */
179
+ primitive: 'object'
160
180
  /**
161
181
  * Ordered object properties.
162
182
  */
@@ -164,14 +184,23 @@ export type ObjectSchemaNode = SchemaNodeBase & {
164
184
  /**
165
185
  * Additional object properties behavior:
166
186
  * - `true`: allow any value
187
+ * - `false`: reject unknown properties (maps to `.strict()` in Zod)
167
188
  * - `SchemaNode`: allow values that match that schema
168
- * - `undefined`: no additional properties
189
+ * - `undefined`: no additional properties constraint (open object)
169
190
  */
170
- additionalProperties?: SchemaNode | true
191
+ additionalProperties?: SchemaNode | boolean
171
192
  /**
172
193
  * Pattern-based property schemas.
173
194
  */
174
195
  patternProperties?: Record<string, SchemaNode>
196
+ /**
197
+ * Minimum number of properties allowed.
198
+ */
199
+ minProperties?: number
200
+ /**
201
+ * Maximum number of properties allowed.
202
+ */
203
+ maxProperties?: number
175
204
  }
176
205
 
177
206
  /**
@@ -341,6 +370,15 @@ export type RefSchemaNode = SchemaNodeBase & {
341
370
  * Pattern copied from a sibling `pattern` field.
342
371
  */
343
372
  pattern?: string
373
+ /**
374
+ * The fully-parsed schema that this ref resolves to.
375
+ * Populated during OAS parsing when the referenced definition can be resolved.
376
+ * `undefined` when the ref cannot be resolved or is part of a circular chain.
377
+ *
378
+ * Useful for inspecting the referenced schema's structure (e.g. `primitive`, `properties`)
379
+ * without following the reference manually.
380
+ */
381
+ schema?: SchemaNode
344
382
  }
345
383
 
346
384
  /**
@@ -456,6 +494,10 @@ export type NumberSchemaNode = SchemaNodeBase & {
456
494
  * Exclusive maximum value.
457
495
  */
458
496
  exclusiveMaximum?: number
497
+ /**
498
+ * The value must be a multiple of this number.
499
+ */
500
+ multipleOf?: number
459
501
  }
460
502
 
461
503
  /**
@@ -491,6 +533,67 @@ export type UrlSchemaNode = SchemaNodeBase & {
491
533
  * OpenAPI-style path template, for example, `'/pets/{petId}'`.
492
534
  */
493
535
  path?: string
536
+ /**
537
+ * Minimum string length.
538
+ */
539
+ min?: number
540
+ /**
541
+ * Maximum string length.
542
+ */
543
+ max?: number
544
+ }
545
+
546
+ /**
547
+ * Format-string schema for string-based formats that support length constraints.
548
+ *
549
+ * @example
550
+ * ```ts
551
+ * const uuidSchema: FormatStringSchemaNode = { kind: 'Schema', type: 'uuid', min: 36, max: 36 }
552
+ * ```
553
+ */
554
+ export type FormatStringSchemaNode = SchemaNodeBase & {
555
+ /**
556
+ * Schema type discriminator.
557
+ */
558
+ type: 'uuid' | 'email'
559
+ /**
560
+ * Minimum string length.
561
+ */
562
+ min?: number
563
+ /**
564
+ * Maximum string length.
565
+ */
566
+ max?: number
567
+ }
568
+
569
+ /**
570
+ * IPv4 address schema node.
571
+ *
572
+ * @example
573
+ * ```ts
574
+ * const ipv4Schema: Ipv4SchemaNode = { kind: 'Schema', type: 'ipv4' }
575
+ * ```
576
+ */
577
+ export type Ipv4SchemaNode = SchemaNodeBase & {
578
+ /**
579
+ * Schema type discriminator.
580
+ */
581
+ type: 'ipv4'
582
+ }
583
+
584
+ /**
585
+ * IPv6 address schema node.
586
+ *
587
+ * @example
588
+ * ```ts
589
+ * const ipv6Schema: Ipv6SchemaNode = { kind: 'Schema', type: 'ipv6' }
590
+ * ```
591
+ */
592
+ export type Ipv6SchemaNode = SchemaNodeBase & {
593
+ /**
594
+ * Schema type discriminator.
595
+ */
596
+ type: 'ipv6'
494
597
  }
495
598
 
496
599
  /**
@@ -518,9 +621,11 @@ export type SchemaNodeByType = {
518
621
  unknown: ScalarSchemaNode
519
622
  void: ScalarSchemaNode
520
623
  never: ScalarSchemaNode
521
- uuid: ScalarSchemaNode
522
- email: ScalarSchemaNode
624
+ uuid: FormatStringSchemaNode
625
+ email: FormatStringSchemaNode
523
626
  url: UrlSchemaNode
627
+ ipv4: Ipv4SchemaNode
628
+ ipv6: Ipv6SchemaNode
524
629
  blob: ScalarSchemaNode
525
630
  }
526
631
 
@@ -540,4 +645,7 @@ export type SchemaNode =
540
645
  | StringSchemaNode
541
646
  | NumberSchemaNode
542
647
  | UrlSchemaNode
648
+ | FormatStringSchemaNode
649
+ | Ipv4SchemaNode
650
+ | Ipv6SchemaNode
543
651
  | ScalarSchemaNode
package/src/types.ts CHANGED
@@ -9,6 +9,7 @@ export type {
9
9
  DatetimeSchemaNode,
10
10
  EnumSchemaNode,
11
11
  EnumValueNode,
12
+ FormatStringSchemaNode,
12
13
  FunctionNode,
13
14
  FunctionNodeType,
14
15
  FunctionParameterNode,
@@ -16,6 +17,8 @@ export type {
16
17
  HttpMethod,
17
18
  HttpStatusCode,
18
19
  IntersectionSchemaNode,
20
+ Ipv4SchemaNode,
21
+ Ipv6SchemaNode,
19
22
  MediaType,
20
23
  Node,
21
24
  NodeKind,
package/src/utils.ts CHANGED
@@ -7,6 +7,29 @@ import type { SchemaType } from './nodes/schema.ts'
7
7
 
8
8
  const plainStringTypes = new Set<SchemaType>(['string', 'uuid', 'email', 'url', 'datetime'] as const)
9
9
 
10
+ /**
11
+ * Returns a merged schema view for a ref node, combining the resolved `node.schema`
12
+ * (base from the referenced definition) with any usage-site sibling fields set directly
13
+ * on the ref node (description, readOnly, nullable, deprecated, etc.).
14
+ *
15
+ * Usage-site fields take precedence over the resolved schema's own fields when both are defined.
16
+ *
17
+ * For non-ref nodes the node itself is returned unchanged.
18
+ */
19
+ export function syncSchemaRef(node: SchemaNode): SchemaNode {
20
+ const ref = narrowSchema(node, 'ref')
21
+
22
+ if (!ref) return node
23
+ if (!ref.schema) return node
24
+
25
+ const { kind: _kind, type: _type, name: _name, ref: _ref, schema: _schema, ...overrides } = ref
26
+
27
+ // Filter out undefined override values so they don't shadow the resolved schema's fields.
28
+ const definedOverrides = Object.fromEntries(Object.entries(overrides).filter(([, v]) => v !== undefined))
29
+
30
+ return createSchema({ ...ref.schema, ...definedOverrides })
31
+ }
32
+
10
33
  /**
11
34
  * Returns `true` when a schema is emitted as a plain `string` type.
12
35
  *
package/src/visitor.ts CHANGED
@@ -11,11 +11,9 @@ import type { Node, OperationNode, ParameterNode, PropertyNode, ResponseNode, Ro
11
11
  * @example
12
12
  * ```ts
13
13
  * const limit = createLimit(2)
14
- * await Promise.all([
15
- * limit(() => taskA()),
16
- * limit(() => taskB()),
17
- * limit(() => taskC()),
18
- * ])
14
+ * for (const task of [taskA, taskB, taskC]) {
15
+ * await limit(() => task())
16
+ * }
19
17
  * // only 2 tasks run at the same time
20
18
  * ```
21
19
  */
@@ -349,7 +347,9 @@ async function _walk(node: Node, visitor: AsyncVisitor, recurse: boolean, limit:
349
347
  }
350
348
 
351
349
  const children = getChildren(node, recurse)
352
- await Promise.all(children.map((child) => _walk(child, visitor, recurse, limit, node)))
350
+ for (const child of children) {
351
+ await _walk(child, visitor, recurse, limit, node)
352
+ }
353
353
  }
354
354
 
355
355
  /**