anyvali 0.3.1

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.
Files changed (204) hide show
  1. package/CHANGELOG.md +44 -0
  2. package/README.md +370 -0
  3. package/VERSION +1 -0
  4. package/dist/errors.d.ts +6 -0
  5. package/dist/errors.d.ts.map +1 -0
  6. package/dist/errors.js +12 -0
  7. package/dist/errors.js.map +1 -0
  8. package/dist/format/validators.d.ts +2 -0
  9. package/dist/format/validators.d.ts.map +1 -0
  10. package/dist/format/validators.js +57 -0
  11. package/dist/format/validators.js.map +1 -0
  12. package/dist/forms/index.d.ts +57 -0
  13. package/dist/forms/index.d.ts.map +1 -0
  14. package/dist/forms/index.js +586 -0
  15. package/dist/forms/index.js.map +1 -0
  16. package/dist/index.d.ts +93 -0
  17. package/dist/index.d.ts.map +1 -0
  18. package/dist/index.js +156 -0
  19. package/dist/index.js.map +1 -0
  20. package/dist/infer.d.ts +8 -0
  21. package/dist/infer.d.ts.map +1 -0
  22. package/dist/infer.js +2 -0
  23. package/dist/infer.js.map +1 -0
  24. package/dist/interchange/document.d.ts +5 -0
  25. package/dist/interchange/document.d.ts.map +1 -0
  26. package/dist/interchange/document.js +12 -0
  27. package/dist/interchange/document.js.map +1 -0
  28. package/dist/interchange/exporter.d.ts +7 -0
  29. package/dist/interchange/exporter.d.ts.map +1 -0
  30. package/dist/interchange/exporter.js +7 -0
  31. package/dist/interchange/exporter.js.map +1 -0
  32. package/dist/interchange/importer.d.ts +4 -0
  33. package/dist/interchange/importer.d.ts.map +1 -0
  34. package/dist/interchange/importer.js +229 -0
  35. package/dist/interchange/importer.js.map +1 -0
  36. package/dist/issue-codes.d.ts +19 -0
  37. package/dist/issue-codes.d.ts.map +1 -0
  38. package/dist/issue-codes.js +18 -0
  39. package/dist/issue-codes.js.map +1 -0
  40. package/dist/parse/coerce.d.ts +16 -0
  41. package/dist/parse/coerce.d.ts.map +1 -0
  42. package/dist/parse/coerce.js +115 -0
  43. package/dist/parse/coerce.js.map +1 -0
  44. package/dist/parse/defaults.d.ts +7 -0
  45. package/dist/parse/defaults.d.ts.map +1 -0
  46. package/dist/parse/defaults.js +12 -0
  47. package/dist/parse/defaults.js.map +1 -0
  48. package/dist/parse/parser.d.ts +11 -0
  49. package/dist/parse/parser.d.ts.map +1 -0
  50. package/dist/parse/parser.js +13 -0
  51. package/dist/parse/parser.js.map +1 -0
  52. package/dist/schemas/any.d.ts +7 -0
  53. package/dist/schemas/any.d.ts.map +1 -0
  54. package/dist/schemas/any.js +12 -0
  55. package/dist/schemas/any.js.map +1 -0
  56. package/dist/schemas/array.d.ts +13 -0
  57. package/dist/schemas/array.d.ts.map +1 -0
  58. package/dist/schemas/array.js +73 -0
  59. package/dist/schemas/array.js.map +1 -0
  60. package/dist/schemas/base.d.ts +37 -0
  61. package/dist/schemas/base.d.ts.map +1 -0
  62. package/dist/schemas/base.js +285 -0
  63. package/dist/schemas/base.js.map +1 -0
  64. package/dist/schemas/bool.d.ts +8 -0
  65. package/dist/schemas/bool.d.ts.map +1 -0
  66. package/dist/schemas/bool.js +27 -0
  67. package/dist/schemas/bool.js.map +1 -0
  68. package/dist/schemas/enum.d.ts +9 -0
  69. package/dist/schemas/enum.d.ts.map +1 -0
  70. package/dist/schemas/enum.js +31 -0
  71. package/dist/schemas/enum.js.map +1 -0
  72. package/dist/schemas/index.d.ts +21 -0
  73. package/dist/schemas/index.d.ts.map +1 -0
  74. package/dist/schemas/index.js +21 -0
  75. package/dist/schemas/index.js.map +1 -0
  76. package/dist/schemas/int.d.ts +32 -0
  77. package/dist/schemas/int.d.ts.map +1 -0
  78. package/dist/schemas/int.js +108 -0
  79. package/dist/schemas/int.js.map +1 -0
  80. package/dist/schemas/intersection.d.ts +16 -0
  81. package/dist/schemas/intersection.d.ts.map +1 -0
  82. package/dist/schemas/intersection.js +58 -0
  83. package/dist/schemas/intersection.js.map +1 -0
  84. package/dist/schemas/literal.d.ts +11 -0
  85. package/dist/schemas/literal.d.ts.map +1 -0
  86. package/dist/schemas/literal.js +28 -0
  87. package/dist/schemas/literal.js.map +1 -0
  88. package/dist/schemas/never.d.ts +7 -0
  89. package/dist/schemas/never.d.ts.map +1 -0
  90. package/dist/schemas/never.js +19 -0
  91. package/dist/schemas/never.js.map +1 -0
  92. package/dist/schemas/null.d.ts +7 -0
  93. package/dist/schemas/null.d.ts.map +1 -0
  94. package/dist/schemas/null.js +24 -0
  95. package/dist/schemas/null.js.map +1 -0
  96. package/dist/schemas/nullable.d.ts +10 -0
  97. package/dist/schemas/nullable.d.ts.map +1 -0
  98. package/dist/schemas/nullable.js +29 -0
  99. package/dist/schemas/nullable.js.map +1 -0
  100. package/dist/schemas/number.d.ts +27 -0
  101. package/dist/schemas/number.d.ts.map +1 -0
  102. package/dist/schemas/number.js +134 -0
  103. package/dist/schemas/number.js.map +1 -0
  104. package/dist/schemas/object.d.ts +28 -0
  105. package/dist/schemas/object.d.ts.map +1 -0
  106. package/dist/schemas/object.js +153 -0
  107. package/dist/schemas/object.js.map +1 -0
  108. package/dist/schemas/optional.d.ts +11 -0
  109. package/dist/schemas/optional.d.ts.map +1 -0
  110. package/dist/schemas/optional.js +39 -0
  111. package/dist/schemas/optional.js.map +1 -0
  112. package/dist/schemas/record.d.ts +9 -0
  113. package/dist/schemas/record.d.ts.map +1 -0
  114. package/dist/schemas/record.js +45 -0
  115. package/dist/schemas/record.js.map +1 -0
  116. package/dist/schemas/ref.d.ts +10 -0
  117. package/dist/schemas/ref.d.ts.map +1 -0
  118. package/dist/schemas/ref.js +30 -0
  119. package/dist/schemas/ref.js.map +1 -0
  120. package/dist/schemas/string.d.ts +29 -0
  121. package/dist/schemas/string.d.ts.map +1 -0
  122. package/dist/schemas/string.js +181 -0
  123. package/dist/schemas/string.js.map +1 -0
  124. package/dist/schemas/tuple.d.ts +14 -0
  125. package/dist/schemas/tuple.d.ts.map +1 -0
  126. package/dist/schemas/tuple.js +59 -0
  127. package/dist/schemas/tuple.js.map +1 -0
  128. package/dist/schemas/union.d.ts +9 -0
  129. package/dist/schemas/union.d.ts.map +1 -0
  130. package/dist/schemas/union.js +45 -0
  131. package/dist/schemas/union.js.map +1 -0
  132. package/dist/schemas/unknown.d.ts +7 -0
  133. package/dist/schemas/unknown.d.ts.map +1 -0
  134. package/dist/schemas/unknown.js +12 -0
  135. package/dist/schemas/unknown.js.map +1 -0
  136. package/dist/types.d.ts +132 -0
  137. package/dist/types.d.ts.map +1 -0
  138. package/dist/types.js +3 -0
  139. package/dist/types.js.map +1 -0
  140. package/dist/util.d.ts +6 -0
  141. package/dist/util.d.ts.map +1 -0
  142. package/dist/util.js +12 -0
  143. package/dist/util.js.map +1 -0
  144. package/package.json +41 -0
  145. package/sdk/js/CHANGELOG.md +13 -0
  146. package/src/errors.ts +17 -0
  147. package/src/format/validators.ts +71 -0
  148. package/src/forms/index.ts +789 -0
  149. package/src/index.ts +285 -0
  150. package/src/infer.ts +12 -0
  151. package/src/interchange/document.ts +18 -0
  152. package/src/interchange/exporter.ts +12 -0
  153. package/src/interchange/importer.ts +285 -0
  154. package/src/issue-codes.ts +19 -0
  155. package/src/parse/coerce.ts +133 -0
  156. package/src/parse/defaults.ts +15 -0
  157. package/src/parse/parser.ts +19 -0
  158. package/src/schemas/any.ts +14 -0
  159. package/src/schemas/array.ts +83 -0
  160. package/src/schemas/base.ts +322 -0
  161. package/src/schemas/bool.ts +30 -0
  162. package/src/schemas/enum.ts +37 -0
  163. package/src/schemas/index.ts +30 -0
  164. package/src/schemas/int.ts +129 -0
  165. package/src/schemas/intersection.ts +81 -0
  166. package/src/schemas/literal.ts +34 -0
  167. package/src/schemas/never.ts +21 -0
  168. package/src/schemas/null.ts +26 -0
  169. package/src/schemas/nullable.ts +36 -0
  170. package/src/schemas/number.ts +151 -0
  171. package/src/schemas/object.ts +203 -0
  172. package/src/schemas/optional.ts +49 -0
  173. package/src/schemas/record.ts +55 -0
  174. package/src/schemas/ref.ts +35 -0
  175. package/src/schemas/string.ts +192 -0
  176. package/src/schemas/tuple.ts +74 -0
  177. package/src/schemas/union.ts +53 -0
  178. package/src/schemas/unknown.ts +14 -0
  179. package/src/types.ts +239 -0
  180. package/src/util.ts +9 -0
  181. package/tests/conformance/runner.test.ts +28 -0
  182. package/tests/conformance/runner.ts +137 -0
  183. package/tests/forms.test.ts +146 -0
  184. package/tests/unit/coerce.test.ts +136 -0
  185. package/tests/unit/collections.test.ts +99 -0
  186. package/tests/unit/composition.test.ts +80 -0
  187. package/tests/unit/date-format.test.ts +18 -0
  188. package/tests/unit/default-mutation.test.ts +32 -0
  189. package/tests/unit/defaults.test.ts +49 -0
  190. package/tests/unit/errors.test.ts +53 -0
  191. package/tests/unit/export.test.ts +270 -0
  192. package/tests/unit/inference.test.ts +306 -0
  193. package/tests/unit/interchange.test.ts +191 -0
  194. package/tests/unit/number.test.ts +195 -0
  195. package/tests/unit/object.test.ts +208 -0
  196. package/tests/unit/parser.test.ts +151 -0
  197. package/tests/unit/primitives.test.ts +111 -0
  198. package/tests/unit/security-recursion.test.ts +105 -0
  199. package/tests/unit/security.test.ts +945 -0
  200. package/tests/unit/shared-ref-falsepos.test.ts +33 -0
  201. package/tests/unit/string-pattern-redos.test.ts +46 -0
  202. package/tests/unit/string.test.ts +147 -0
  203. package/tsconfig.json +21 -0
  204. package/vitest.config.ts +7 -0
package/src/index.ts ADDED
@@ -0,0 +1,285 @@
1
+ // ---- Re-export types ----
2
+ export type {
3
+ SchemaNode,
4
+ SchemaKind,
5
+ ParseResult,
6
+ ValidationIssue,
7
+ AnyValiDocument,
8
+ ExportMode,
9
+ UnknownKeyMode,
10
+ CoercionConfig,
11
+ StringFormat,
12
+ ParseContext,
13
+ MetadataOptions,
14
+ DescribeOptions,
15
+ } from "./types.js";
16
+
17
+ export { ISSUE_CODES } from "./issue-codes.js";
18
+ export type { IssueCode } from "./issue-codes.js";
19
+
20
+ export type { SchemaAny, Infer, InferInput } from "./infer.js";
21
+
22
+ export { ValidationError } from "./errors.js";
23
+
24
+ // ---- Re-export schema classes ----
25
+ export {
26
+ BaseSchema,
27
+ ABSENT,
28
+ StringSchema,
29
+ NumberSchema,
30
+ Float32Schema,
31
+ Float64Schema,
32
+ IntSchema,
33
+ Int8Schema,
34
+ Int16Schema,
35
+ Int32Schema,
36
+ Int64Schema,
37
+ Uint8Schema,
38
+ Uint16Schema,
39
+ Uint32Schema,
40
+ Uint64Schema,
41
+ BoolSchema,
42
+ NullSchema,
43
+ AnySchema,
44
+ UnknownSchema,
45
+ NeverSchema,
46
+ LiteralSchema,
47
+ EnumSchema,
48
+ ArraySchema,
49
+ TupleSchema,
50
+ ObjectSchema,
51
+ RecordSchema,
52
+ UnionSchema,
53
+ IntersectionSchema,
54
+ OptionalSchema,
55
+ NullableSchema,
56
+ RefSchema,
57
+ } from "./schemas/index.js";
58
+
59
+ // ---- Builder functions ----
60
+
61
+ import { StringSchema } from "./schemas/string.js";
62
+ import { NumberSchema, Float32Schema, Float64Schema } from "./schemas/number.js";
63
+ import {
64
+ IntSchema,
65
+ Int8Schema,
66
+ Int16Schema,
67
+ Int32Schema,
68
+ Int64Schema,
69
+ Uint8Schema,
70
+ Uint16Schema,
71
+ Uint32Schema,
72
+ Uint64Schema,
73
+ } from "./schemas/int.js";
74
+ import { BoolSchema } from "./schemas/bool.js";
75
+ import { NullSchema } from "./schemas/null.js";
76
+ import { AnySchema } from "./schemas/any.js";
77
+ import { UnknownSchema } from "./schemas/unknown.js";
78
+ import { NeverSchema } from "./schemas/never.js";
79
+ import { LiteralSchema } from "./schemas/literal.js";
80
+ import { EnumSchema } from "./schemas/enum.js";
81
+ import { ArraySchema } from "./schemas/array.js";
82
+ import { TupleSchema } from "./schemas/tuple.js";
83
+ import { ObjectSchema } from "./schemas/object.js";
84
+ import { RecordSchema } from "./schemas/record.js";
85
+ import { UnionSchema } from "./schemas/union.js";
86
+ import { IntersectionSchema } from "./schemas/intersection.js";
87
+ import { OptionalSchema } from "./schemas/optional.js";
88
+ import { NullableSchema } from "./schemas/nullable.js";
89
+ import { BaseSchema } from "./schemas/base.js";
90
+
91
+ import type { UnknownKeyMode, ParseResult, AnyValiDocument, ExportMode } from "./types.js";
92
+
93
+ /** Create a string schema */
94
+ export function string(): StringSchema {
95
+ return new StringSchema();
96
+ }
97
+
98
+ /** Create a number (float64) schema */
99
+ export function number(): NumberSchema {
100
+ return new NumberSchema();
101
+ }
102
+
103
+ /** Create a float32 schema */
104
+ export function float32(): Float32Schema {
105
+ return new Float32Schema();
106
+ }
107
+
108
+ /** Create a float64 schema */
109
+ export function float64(): Float64Schema {
110
+ return new Float64Schema();
111
+ }
112
+
113
+ /** Create an int (int64) schema */
114
+ export function int(): IntSchema {
115
+ return new IntSchema();
116
+ }
117
+
118
+ /** Create an int8 schema */
119
+ export function int8(): Int8Schema {
120
+ return new Int8Schema();
121
+ }
122
+
123
+ /** Create an int16 schema */
124
+ export function int16(): Int16Schema {
125
+ return new Int16Schema();
126
+ }
127
+
128
+ /** Create an int32 schema */
129
+ export function int32(): Int32Schema {
130
+ return new Int32Schema();
131
+ }
132
+
133
+ /** Create an int64 schema */
134
+ export function int64(): Int64Schema {
135
+ return new Int64Schema();
136
+ }
137
+
138
+ /** Create a uint8 schema */
139
+ export function uint8(): Uint8Schema {
140
+ return new Uint8Schema();
141
+ }
142
+
143
+ /** Create a uint16 schema */
144
+ export function uint16(): Uint16Schema {
145
+ return new Uint16Schema();
146
+ }
147
+
148
+ /** Create a uint32 schema */
149
+ export function uint32(): Uint32Schema {
150
+ return new Uint32Schema();
151
+ }
152
+
153
+ /** Create a uint64 schema */
154
+ export function uint64(): Uint64Schema {
155
+ return new Uint64Schema();
156
+ }
157
+
158
+ /** Create a boolean schema */
159
+ export function bool(): BoolSchema {
160
+ return new BoolSchema();
161
+ }
162
+
163
+ /** Create a null schema. Named null_ to avoid conflict with the null keyword. */
164
+ export function null_(): NullSchema {
165
+ return new NullSchema();
166
+ }
167
+
168
+ /** Create an any schema */
169
+ export function any(): AnySchema {
170
+ return new AnySchema();
171
+ }
172
+
173
+ /** Create an unknown schema */
174
+ export function unknown(): UnknownSchema {
175
+ return new UnknownSchema();
176
+ }
177
+
178
+ /** Create a never schema */
179
+ export function never(): NeverSchema {
180
+ return new NeverSchema();
181
+ }
182
+
183
+ /** Create a literal schema */
184
+ export function literal<T extends string | number | boolean | null>(
185
+ value: T
186
+ ): LiteralSchema<T> {
187
+ return new LiteralSchema(value);
188
+ }
189
+
190
+ /** Create an enum schema. Named enum_ to avoid conflict with the enum keyword. */
191
+ export function enum_<T extends readonly (string | number)[]>(
192
+ values: T,
193
+ ): EnumSchema<T> {
194
+ return new EnumSchema(values);
195
+ }
196
+
197
+ /** Create an array schema */
198
+ export function array<T extends BaseSchema<any, any>>(
199
+ items: T,
200
+ ): ArraySchema<T> {
201
+ return new ArraySchema(items);
202
+ }
203
+
204
+ /** Create a tuple schema */
205
+ export function tuple<T extends BaseSchema<any, any>[]>(
206
+ items: [...T],
207
+ ): TupleSchema<T> {
208
+ return new TupleSchema(items);
209
+ }
210
+
211
+ /** Create an object schema */
212
+ export function object<T extends Record<string, BaseSchema<any, any>>>(
213
+ shape: T,
214
+ options?: { unknownKeys?: UnknownKeyMode },
215
+ ): ObjectSchema<T> {
216
+ return new ObjectSchema(shape, options);
217
+ }
218
+
219
+ /** Create a record schema */
220
+ export function record<T extends BaseSchema<any, any>>(
221
+ valueSchema: T,
222
+ ): RecordSchema<T> {
223
+ return new RecordSchema(valueSchema);
224
+ }
225
+
226
+ /** Create a union schema */
227
+ export function union<T extends BaseSchema<any, any>[]>(
228
+ variants: [...T],
229
+ ): UnionSchema<T> {
230
+ return new UnionSchema(variants);
231
+ }
232
+
233
+ /** Create an intersection schema */
234
+ export function intersection<T extends BaseSchema<any, any>[]>(
235
+ schemas: [...T],
236
+ ): IntersectionSchema<T> {
237
+ return new IntersectionSchema(schemas);
238
+ }
239
+
240
+ /** Wrap a schema as optional */
241
+ export function optional<T extends BaseSchema<any, any>>(
242
+ schema: T,
243
+ ): OptionalSchema<T> {
244
+ return new OptionalSchema(schema);
245
+ }
246
+
247
+ /** Wrap a schema as nullable */
248
+ export function nullable<T extends BaseSchema<any, any>>(
249
+ schema: T,
250
+ ): NullableSchema<T> {
251
+ return new NullableSchema(schema);
252
+ }
253
+
254
+ // ---- Top-level parse functions ----
255
+
256
+ /** Parse input using the given schema. Throws ValidationError on failure. */
257
+ export function parse<T>(schema: BaseSchema<unknown, T>, input: unknown): T {
258
+ return schema.parse(input);
259
+ }
260
+
261
+ /** Parse input using the given schema. Returns a result object. */
262
+ export function safeParse<T>(
263
+ schema: BaseSchema<unknown, T>,
264
+ input: unknown
265
+ ): ParseResult<T> {
266
+ return schema.safeParse(input);
267
+ }
268
+
269
+ // ---- Interchange functions ----
270
+
271
+ import { exportSchema as _exportSchema } from "./interchange/exporter.js";
272
+ import { importSchema as _importSchema } from "./interchange/importer.js";
273
+
274
+ /** Export a schema to an AnyValiDocument */
275
+ export function exportSchema(
276
+ schema: BaseSchema,
277
+ mode: ExportMode = "portable"
278
+ ): AnyValiDocument {
279
+ return _exportSchema(schema, mode);
280
+ }
281
+
282
+ /** Import an AnyValiDocument to a live schema */
283
+ export function importSchema(doc: AnyValiDocument): BaseSchema {
284
+ return _importSchema(doc);
285
+ }
package/src/infer.ts ADDED
@@ -0,0 +1,12 @@
1
+ import type { BaseSchema } from "./schemas/base.js";
2
+
3
+ /** Any AnyVali schema. Use as a generic constraint. Equivalent to Zod's ZodTypeAny. */
4
+ export type SchemaAny = BaseSchema<any, any>;
5
+
6
+ /** Extract the output type from a schema. Equivalent to Zod's z.infer. */
7
+ export type Infer<T extends SchemaAny> = T["_output"];
8
+
9
+ /** Extract the input type from a schema. */
10
+ export type InferInput<T extends SchemaAny> = T extends BaseSchema<infer I, any>
11
+ ? I
12
+ : never;
@@ -0,0 +1,18 @@
1
+ import type { AnyValiDocument } from "../types.js";
2
+
3
+ export const ANYVALI_VERSION = "1.0";
4
+ export const SCHEMA_VERSION = "1";
5
+
6
+ export function createDocument(
7
+ root: import("../types.js").SchemaNode,
8
+ definitions: Record<string, import("../types.js").SchemaNode> = {},
9
+ extensions: Record<string, Record<string, unknown>> = {}
10
+ ): AnyValiDocument {
11
+ return {
12
+ anyvaliVersion: ANYVALI_VERSION,
13
+ schemaVersion: SCHEMA_VERSION,
14
+ root,
15
+ definitions,
16
+ extensions,
17
+ };
18
+ }
@@ -0,0 +1,12 @@
1
+ import type { AnyValiDocument, ExportMode } from "../types.js";
2
+ import { BaseSchema } from "../schemas/base.js";
3
+
4
+ /**
5
+ * Export a schema to a portable AnyValiDocument.
6
+ */
7
+ export function exportSchema(
8
+ schema: BaseSchema,
9
+ mode: ExportMode = "portable"
10
+ ): AnyValiDocument {
11
+ return schema.export(mode);
12
+ }
@@ -0,0 +1,285 @@
1
+ import type {
2
+ AnyValiDocument,
3
+ SchemaNode,
4
+ } from "../types.js";
5
+ import { BaseSchema } from "../schemas/base.js";
6
+ import { StringSchema } from "../schemas/string.js";
7
+ import {
8
+ NumberSchema,
9
+ Float32Schema,
10
+ Float64Schema,
11
+ } from "../schemas/number.js";
12
+ import {
13
+ IntSchema,
14
+ Int8Schema,
15
+ Int16Schema,
16
+ Int32Schema,
17
+ Int64Schema,
18
+ Uint8Schema,
19
+ Uint16Schema,
20
+ Uint32Schema,
21
+ Uint64Schema,
22
+ } from "../schemas/int.js";
23
+ import { BoolSchema } from "../schemas/bool.js";
24
+ import { NullSchema } from "../schemas/null.js";
25
+ import { AnySchema } from "../schemas/any.js";
26
+ import { UnknownSchema } from "../schemas/unknown.js";
27
+ import { NeverSchema } from "../schemas/never.js";
28
+ import { LiteralSchema } from "../schemas/literal.js";
29
+ import { EnumSchema } from "../schemas/enum.js";
30
+ import { ArraySchema } from "../schemas/array.js";
31
+ import { TupleSchema } from "../schemas/tuple.js";
32
+ import { ObjectSchema } from "../schemas/object.js";
33
+ import { RecordSchema } from "../schemas/record.js";
34
+ import { UnionSchema } from "../schemas/union.js";
35
+ import { IntersectionSchema } from "../schemas/intersection.js";
36
+ import { OptionalSchema } from "../schemas/optional.js";
37
+ import { NullableSchema } from "../schemas/nullable.js";
38
+ import { RefSchema } from "../schemas/ref.js";
39
+ import { normalizeCoercionConfig } from "../parse/coerce.js";
40
+ import type { StringFormat, UnknownKeyMode } from "../types.js";
41
+
42
+ /**
43
+ * Import an AnyValiDocument back into a live Schema.
44
+ */
45
+ /**
46
+ * Maximum schema-document nesting depth accepted by importSchema. Bounds the
47
+ * recursive importNode walk so an untrusted, deeply nested document cannot
48
+ * exhaust the call stack (DoS). Throws a controlled error instead of a
49
+ * RangeError stack overflow.
50
+ */
51
+ const MAX_IMPORT_DEPTH = 512;
52
+
53
+ export function importSchema(doc: AnyValiDocument): BaseSchema {
54
+ const definitions = doc.definitions ?? {};
55
+ const resolvedDefs = new Map<string, BaseSchema>();
56
+
57
+ function importNode(node: any, depth: number = 0): BaseSchema {
58
+ if (depth > MAX_IMPORT_DEPTH) {
59
+ throw new Error(
60
+ `Schema document too deeply nested (max depth ${MAX_IMPORT_DEPTH} exceeded)`
61
+ );
62
+ }
63
+ const d = depth + 1;
64
+ let schema: BaseSchema;
65
+
66
+ switch (node.kind) {
67
+ case "string": {
68
+ let s = new StringSchema();
69
+ if (node.minLength !== undefined) s = s.minLength(node.minLength);
70
+ if (node.maxLength !== undefined) s = s.maxLength(node.maxLength);
71
+ if (node.pattern !== undefined) s = s.pattern(node.pattern);
72
+ if (node.startsWith !== undefined) s = s.startsWith(node.startsWith);
73
+ if (node.endsWith !== undefined) s = s.endsWith(node.endsWith);
74
+ if (node.includes !== undefined) s = s.includes(node.includes);
75
+ if (node.format !== undefined)
76
+ s = s.format(node.format as StringFormat);
77
+ schema = s;
78
+ break;
79
+ }
80
+
81
+ case "number":
82
+ case "float64": {
83
+ let s =
84
+ node.kind === "float64" ? new Float64Schema() : new NumberSchema();
85
+ schema = applyNumericConstraints(s, node);
86
+ break;
87
+ }
88
+
89
+ case "float32": {
90
+ schema = applyNumericConstraints(new Float32Schema(), node);
91
+ break;
92
+ }
93
+
94
+ case "int":
95
+ case "int64": {
96
+ schema = applyNumericConstraints(
97
+ node.kind === "int64" ? new Int64Schema() : new IntSchema(),
98
+ node
99
+ );
100
+ break;
101
+ }
102
+
103
+ case "int8":
104
+ schema = applyNumericConstraints(new Int8Schema(), node);
105
+ break;
106
+ case "int16":
107
+ schema = applyNumericConstraints(new Int16Schema(), node);
108
+ break;
109
+ case "int32":
110
+ schema = applyNumericConstraints(new Int32Schema(), node);
111
+ break;
112
+ case "uint8":
113
+ schema = applyNumericConstraints(new Uint8Schema(), node);
114
+ break;
115
+ case "uint16":
116
+ schema = applyNumericConstraints(new Uint16Schema(), node);
117
+ break;
118
+ case "uint32":
119
+ schema = applyNumericConstraints(new Uint32Schema(), node);
120
+ break;
121
+ case "uint64":
122
+ schema = applyNumericConstraints(new Uint64Schema(), node);
123
+ break;
124
+
125
+ case "bool":
126
+ schema = new BoolSchema();
127
+ break;
128
+
129
+ case "null":
130
+ schema = new NullSchema();
131
+ break;
132
+
133
+ case "any":
134
+ schema = new AnySchema();
135
+ break;
136
+
137
+ case "unknown":
138
+ schema = new UnknownSchema();
139
+ break;
140
+
141
+ case "never":
142
+ schema = new NeverSchema();
143
+ break;
144
+
145
+ case "literal": {
146
+ schema = new LiteralSchema(node.value);
147
+ break;
148
+ }
149
+
150
+ case "enum": {
151
+ schema = new EnumSchema(node.values);
152
+ break;
153
+ }
154
+
155
+ case "array": {
156
+ let s = new ArraySchema(importNode(node.items, d));
157
+ if (node.minItems !== undefined) s = s.minItems(node.minItems);
158
+ if (node.maxItems !== undefined) s = s.maxItems(node.maxItems);
159
+ schema = s;
160
+ break;
161
+ }
162
+
163
+ case "tuple": {
164
+ // Corpus uses "elements", our export uses "items"
165
+ const elements = node.elements ?? node.items;
166
+ schema = new TupleSchema(
167
+ elements.map((i: any) => importNode(i, d))
168
+ );
169
+ break;
170
+ }
171
+
172
+ case "object": {
173
+ const shape: Record<string, BaseSchema> = Object.create(null);
174
+ const requiredSet = new Set<string>(node.required ?? []);
175
+ for (const [key, propNode] of Object.entries(
176
+ node.properties ?? {}
177
+ )) {
178
+ let propSchema = importNode(propNode, d);
179
+ if (!requiredSet.has(key)) {
180
+ propSchema = new OptionalSchema(propSchema);
181
+ }
182
+ // Use defineProperty to safely handle __proto__ and other special keys
183
+ Object.defineProperty(shape, key, {
184
+ value: propSchema,
185
+ writable: true,
186
+ enumerable: true,
187
+ configurable: true,
188
+ });
189
+ }
190
+ Object.setPrototypeOf(shape, Object.prototype);
191
+ schema = new ObjectSchema(shape, {
192
+ unknownKeys:
193
+ (node.unknownKeys as UnknownKeyMode) ?? "strip",
194
+ });
195
+ break;
196
+ }
197
+
198
+ case "record": {
199
+ // Corpus uses "values", our export uses "valueSchema"
200
+ const valueNode = node.values ?? node.valueSchema;
201
+ schema = new RecordSchema(importNode(valueNode, d));
202
+ break;
203
+ }
204
+
205
+ case "union": {
206
+ schema = new UnionSchema(
207
+ node.variants.map((v: any) => importNode(v, d))
208
+ );
209
+ break;
210
+ }
211
+
212
+ case "intersection": {
213
+ schema = new IntersectionSchema(
214
+ node.allOf.map((s: any) => importNode(s, d))
215
+ );
216
+ break;
217
+ }
218
+
219
+ case "optional": {
220
+ // Corpus uses "schema", our export uses "inner"
221
+ const innerNode = node.schema ?? node.inner;
222
+ schema = new OptionalSchema(importNode(innerNode, d));
223
+ break;
224
+ }
225
+
226
+ case "nullable": {
227
+ // Corpus uses "schema", our export uses "inner"
228
+ const innerNode = node.schema ?? node.inner;
229
+ schema = new NullableSchema(importNode(innerNode, d));
230
+ break;
231
+ }
232
+
233
+ case "ref": {
234
+ const refPath = node.ref as string;
235
+ const defName = refPath.replace("#/definitions/", "");
236
+ schema = new RefSchema(refPath, () => {
237
+ if (resolvedDefs.has(defName)) {
238
+ return resolvedDefs.get(defName)!;
239
+ }
240
+ const defNode = definitions[defName];
241
+ if (!defNode) {
242
+ throw new Error(`Unresolved definition: ${defName}`);
243
+ }
244
+ const resolved = importNode(defNode);
245
+ resolvedDefs.set(defName, resolved);
246
+ return resolved;
247
+ });
248
+ break;
249
+ }
250
+
251
+ default:
252
+ throw new Error(`Unsupported schema kind: ${node.kind}`);
253
+ }
254
+
255
+ // Apply default
256
+ if (node.default !== undefined) {
257
+ schema = schema.default(node.default as any);
258
+ }
259
+
260
+ // Apply coercion config - handle both string and object formats
261
+ if (node.coerce !== undefined) {
262
+ const config = normalizeCoercionConfig(node.coerce);
263
+ schema = schema.coerce(config);
264
+ }
265
+
266
+ return schema;
267
+ }
268
+
269
+ return importNode(doc.root);
270
+ }
271
+
272
+ function applyNumericConstraints<T extends NumberSchema>(
273
+ schema: T,
274
+ node: any
275
+ ): T {
276
+ let s = schema;
277
+ if (node.min !== undefined) s = s.min(node.min) as T;
278
+ if (node.max !== undefined) s = s.max(node.max) as T;
279
+ if (node.exclusiveMin !== undefined)
280
+ s = s.exclusiveMin(node.exclusiveMin) as T;
281
+ if (node.exclusiveMax !== undefined)
282
+ s = s.exclusiveMax(node.exclusiveMax) as T;
283
+ if (node.multipleOf !== undefined) s = s.multipleOf(node.multipleOf) as T;
284
+ return s;
285
+ }
@@ -0,0 +1,19 @@
1
+ export const ISSUE_CODES = {
2
+ INVALID_TYPE: "invalid_type",
3
+ REQUIRED: "required",
4
+ UNKNOWN_KEY: "unknown_key",
5
+ TOO_SMALL: "too_small",
6
+ TOO_LARGE: "too_large",
7
+ INVALID_STRING: "invalid_string",
8
+ INVALID_NUMBER: "invalid_number",
9
+ INVALID_LITERAL: "invalid_literal",
10
+ INVALID_UNION: "invalid_union",
11
+ CUSTOM_VALIDATION_NOT_PORTABLE: "custom_validation_not_portable",
12
+ UNSUPPORTED_EXTENSION: "unsupported_extension",
13
+ UNSUPPORTED_SCHEMA_KIND: "unsupported_schema_kind",
14
+ COERCION_FAILED: "coercion_failed",
15
+ DEFAULT_INVALID: "default_invalid",
16
+ TOO_DEEP: "too_deep",
17
+ } as const;
18
+
19
+ export type IssueCode = (typeof ISSUE_CODES)[keyof typeof ISSUE_CODES];