valrs 0.1.0
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/README.md +197 -0
- package/dist/compiler.d.ts +195 -0
- package/dist/compiler.d.ts.map +1 -0
- package/dist/compiler.js +349 -0
- package/dist/compiler.js.map +1 -0
- package/dist/error.d.ts +415 -0
- package/dist/error.d.ts.map +1 -0
- package/dist/error.js +619 -0
- package/dist/error.js.map +1 -0
- package/dist/factory.d.ts +107 -0
- package/dist/factory.d.ts.map +1 -0
- package/dist/factory.js +135 -0
- package/dist/factory.js.map +1 -0
- package/dist/index.d.ts +79 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +104 -0
- package/dist/index.js.map +1 -0
- package/dist/primitives.d.ts +99 -0
- package/dist/primitives.d.ts.map +1 -0
- package/dist/primitives.js +315 -0
- package/dist/primitives.js.map +1 -0
- package/dist/schema.d.ts +710 -0
- package/dist/schema.d.ts.map +1 -0
- package/dist/schema.js +1179 -0
- package/dist/schema.js.map +1 -0
- package/dist/streaming.d.ts +159 -0
- package/dist/streaming.d.ts.map +1 -0
- package/dist/streaming.js +692 -0
- package/dist/streaming.js.map +1 -0
- package/dist/types.d.ts +107 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +19 -0
- package/dist/types.js.map +1 -0
- package/dist/v.d.ts +1382 -0
- package/dist/v.d.ts.map +1 -0
- package/dist/v.js +2396 -0
- package/dist/v.js.map +1 -0
- package/dist/wasm.d.ts +86 -0
- package/dist/wasm.d.ts.map +1 -0
- package/dist/wasm.js +87 -0
- package/dist/wasm.js.map +1 -0
- package/package.json +89 -0
package/dist/schema.js
ADDED
|
@@ -0,0 +1,1179 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Base schema class providing the fluent API wrapper around Standard Schema.
|
|
3
|
+
*
|
|
4
|
+
* This module provides the `ValSchema` base class that all schema types extend.
|
|
5
|
+
* It wraps the internal Standard Schema implementation and exposes Zod-like methods.
|
|
6
|
+
*/
|
|
7
|
+
import { isValidationSuccess } from './types';
|
|
8
|
+
import { ValError } from './error';
|
|
9
|
+
import { VENDOR, VERSION, success } from './factory';
|
|
10
|
+
import { compileSchema } from './compiler';
|
|
11
|
+
/**
|
|
12
|
+
* Type guard to check if a SafeParseResult is successful.
|
|
13
|
+
*/
|
|
14
|
+
export function isSafeParseSuccess(result) {
|
|
15
|
+
return result.success === true;
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* Type guard to check if a SafeParseResult is a failure.
|
|
19
|
+
*/
|
|
20
|
+
export function isSafeParseError(result) {
|
|
21
|
+
return result.success === false;
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* Base schema class that provides the Zod-compatible API.
|
|
25
|
+
*
|
|
26
|
+
* All schema types extend this class, inheriting methods like `parse()`,
|
|
27
|
+
* `safeParse()`, and maintaining Standard Schema compliance via `~standard`.
|
|
28
|
+
*
|
|
29
|
+
* @template Input - The expected input type
|
|
30
|
+
* @template Output - The validated output type (defaults to Input)
|
|
31
|
+
*
|
|
32
|
+
* @example
|
|
33
|
+
* ```typescript
|
|
34
|
+
* const schema = v.string();
|
|
35
|
+
*
|
|
36
|
+
* // Throws ValError on failure
|
|
37
|
+
* const value = schema.parse('hello');
|
|
38
|
+
*
|
|
39
|
+
* // Returns { success, data } or { success, error }
|
|
40
|
+
* const result = schema.safeParse('hello');
|
|
41
|
+
* if (result.success) {
|
|
42
|
+
* console.log(result.data);
|
|
43
|
+
* }
|
|
44
|
+
* ```
|
|
45
|
+
*/
|
|
46
|
+
export class ValSchema {
|
|
47
|
+
/**
|
|
48
|
+
* Standard Schema interface for interoperability.
|
|
49
|
+
*
|
|
50
|
+
* This property makes ValSchema compatible with any library that
|
|
51
|
+
* supports the Standard Schema specification.
|
|
52
|
+
*/
|
|
53
|
+
'~standard';
|
|
54
|
+
/**
|
|
55
|
+
* Compiled validator for fast boolean checks.
|
|
56
|
+
* Lazily initialized on first check() call.
|
|
57
|
+
*/
|
|
58
|
+
_compiledCheck;
|
|
59
|
+
/**
|
|
60
|
+
* Whether this schema or any of its children contain transforms.
|
|
61
|
+
* When true, validation must go through WASM to apply transforms.
|
|
62
|
+
*/
|
|
63
|
+
_hasTransforms = false;
|
|
64
|
+
/**
|
|
65
|
+
* Returns whether this schema contains transforms.
|
|
66
|
+
* Used internally to determine validation path.
|
|
67
|
+
*/
|
|
68
|
+
hasTransforms() {
|
|
69
|
+
return this._hasTransforms;
|
|
70
|
+
}
|
|
71
|
+
/**
|
|
72
|
+
* Creates a new ValSchema wrapping a validation function.
|
|
73
|
+
*
|
|
74
|
+
* @param validateFn - Function that validates input values
|
|
75
|
+
* @param inputJsonSchemaFn - Function that generates JSON Schema for input type
|
|
76
|
+
* @param outputJsonSchemaFn - Function that generates JSON Schema for output type
|
|
77
|
+
*/
|
|
78
|
+
constructor(validateFn, inputJsonSchemaFn, outputJsonSchemaFn) {
|
|
79
|
+
const outputFn = outputJsonSchemaFn ?? inputJsonSchemaFn;
|
|
80
|
+
this['~standard'] = {
|
|
81
|
+
version: VERSION,
|
|
82
|
+
vendor: VENDOR,
|
|
83
|
+
validate: validateFn,
|
|
84
|
+
jsonSchema: {
|
|
85
|
+
input: (options) => inputJsonSchemaFn(options.target),
|
|
86
|
+
output: (options) => outputFn(options.target),
|
|
87
|
+
},
|
|
88
|
+
};
|
|
89
|
+
}
|
|
90
|
+
/**
|
|
91
|
+
* Parses the input and returns the validated value.
|
|
92
|
+
*
|
|
93
|
+
* Throws a `ValError` if validation fails.
|
|
94
|
+
*
|
|
95
|
+
* @param data - The value to validate
|
|
96
|
+
* @returns The validated value with the correct type
|
|
97
|
+
* @throws {ValError} If validation fails
|
|
98
|
+
*
|
|
99
|
+
* @example
|
|
100
|
+
* ```typescript
|
|
101
|
+
* const name = v.string().parse('Alice'); // 'Alice'
|
|
102
|
+
* const age = v.number().parse('42'); // throws ValError
|
|
103
|
+
* ```
|
|
104
|
+
*/
|
|
105
|
+
parse(data) {
|
|
106
|
+
const result = this['~standard'].validate(data);
|
|
107
|
+
if (isValidationSuccess(result)) {
|
|
108
|
+
return result.value;
|
|
109
|
+
}
|
|
110
|
+
throw new ValError(result.issues);
|
|
111
|
+
}
|
|
112
|
+
/**
|
|
113
|
+
* Parses the input and returns a result object.
|
|
114
|
+
*
|
|
115
|
+
* Never throws. Returns `{ success: true, data }` on success,
|
|
116
|
+
* or `{ success: false, error }` on failure.
|
|
117
|
+
*
|
|
118
|
+
* @param data - The value to validate
|
|
119
|
+
* @returns A result object indicating success or failure
|
|
120
|
+
*
|
|
121
|
+
* @example
|
|
122
|
+
* ```typescript
|
|
123
|
+
* const result = v.string().safeParse(input);
|
|
124
|
+
*
|
|
125
|
+
* if (result.success) {
|
|
126
|
+
* console.log(result.data); // The validated string
|
|
127
|
+
* } else {
|
|
128
|
+
* console.log(result.error.issues); // Array of validation issues
|
|
129
|
+
* }
|
|
130
|
+
* ```
|
|
131
|
+
*/
|
|
132
|
+
safeParse(data) {
|
|
133
|
+
// Fast path: use JS-compiled check for non-transform schemas
|
|
134
|
+
if (!this._hasTransforms && this.check(data)) {
|
|
135
|
+
return {
|
|
136
|
+
success: true,
|
|
137
|
+
data: data,
|
|
138
|
+
};
|
|
139
|
+
}
|
|
140
|
+
// Full validation path (WASM) for transforms or when check fails
|
|
141
|
+
const result = this['~standard'].validate(data);
|
|
142
|
+
if (isValidationSuccess(result)) {
|
|
143
|
+
return {
|
|
144
|
+
success: true,
|
|
145
|
+
data: result.value,
|
|
146
|
+
};
|
|
147
|
+
}
|
|
148
|
+
return {
|
|
149
|
+
success: false,
|
|
150
|
+
error: new ValError(result.issues),
|
|
151
|
+
};
|
|
152
|
+
}
|
|
153
|
+
/**
|
|
154
|
+
* Alias for `safeParse()` that matches Standard Schema naming.
|
|
155
|
+
*/
|
|
156
|
+
validate(data) {
|
|
157
|
+
return this.safeParse(data);
|
|
158
|
+
}
|
|
159
|
+
/**
|
|
160
|
+
* Returns the JSON Schema for this schema's input type.
|
|
161
|
+
*
|
|
162
|
+
* @param target - The JSON Schema version (e.g., 'draft-2020-12', 'draft-07', 'openapi-3.0')
|
|
163
|
+
* @returns A JSON Schema object
|
|
164
|
+
*
|
|
165
|
+
* @example
|
|
166
|
+
* ```typescript
|
|
167
|
+
* const schema = v.string();
|
|
168
|
+
* const jsonSchema = schema.toJsonSchema('draft-2020-12');
|
|
169
|
+
* // { type: 'string' }
|
|
170
|
+
* ```
|
|
171
|
+
*/
|
|
172
|
+
toJsonSchema(target = 'draft-2020-12') {
|
|
173
|
+
return this['~standard'].jsonSchema.input({ target });
|
|
174
|
+
}
|
|
175
|
+
/**
|
|
176
|
+
* Fast boolean check using JS-compiled validator.
|
|
177
|
+
*
|
|
178
|
+
* Returns true if the data passes validation, false otherwise.
|
|
179
|
+
* Does NOT apply transforms - use safeParse() for that.
|
|
180
|
+
*
|
|
181
|
+
* @param data - The value to check
|
|
182
|
+
* @returns true if valid, false otherwise
|
|
183
|
+
*
|
|
184
|
+
* @example
|
|
185
|
+
* ```typescript
|
|
186
|
+
* const schema = v.string();
|
|
187
|
+
* schema.check('hello'); // true
|
|
188
|
+
* schema.check(42); // false
|
|
189
|
+
* ```
|
|
190
|
+
*/
|
|
191
|
+
check(data) {
|
|
192
|
+
if (!this._compiledCheck) {
|
|
193
|
+
try {
|
|
194
|
+
const jsonSchema = this.toJsonSchema();
|
|
195
|
+
this._compiledCheck = compileSchema(jsonSchema);
|
|
196
|
+
}
|
|
197
|
+
catch {
|
|
198
|
+
// Fallback: always return true if compilation fails
|
|
199
|
+
this._compiledCheck = () => true;
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
return this._compiledCheck(data);
|
|
203
|
+
}
|
|
204
|
+
/**
|
|
205
|
+
* Checks if the schema is optional.
|
|
206
|
+
*
|
|
207
|
+
* Override in subclasses that support optionality.
|
|
208
|
+
*/
|
|
209
|
+
isOptional() {
|
|
210
|
+
return false;
|
|
211
|
+
}
|
|
212
|
+
/**
|
|
213
|
+
* Checks if the schema is nullable.
|
|
214
|
+
*
|
|
215
|
+
* Override in subclasses that support nullability.
|
|
216
|
+
*/
|
|
217
|
+
isNullable() {
|
|
218
|
+
return false;
|
|
219
|
+
}
|
|
220
|
+
/**
|
|
221
|
+
* Makes the schema accept undefined values.
|
|
222
|
+
*
|
|
223
|
+
* @returns A new schema that accepts T | undefined
|
|
224
|
+
*
|
|
225
|
+
* @example
|
|
226
|
+
* ```typescript
|
|
227
|
+
* const schema = v.string().optional();
|
|
228
|
+
* schema.parse(undefined); // undefined
|
|
229
|
+
* schema.parse('hello'); // 'hello'
|
|
230
|
+
* ```
|
|
231
|
+
*/
|
|
232
|
+
optional() {
|
|
233
|
+
return new ValOptional(this);
|
|
234
|
+
}
|
|
235
|
+
/**
|
|
236
|
+
* Makes the schema accept null values.
|
|
237
|
+
*
|
|
238
|
+
* @returns A new schema that accepts T | null
|
|
239
|
+
*
|
|
240
|
+
* @example
|
|
241
|
+
* ```typescript
|
|
242
|
+
* const schema = v.string().nullable();
|
|
243
|
+
* schema.parse(null); // null
|
|
244
|
+
* schema.parse('hello'); // 'hello'
|
|
245
|
+
* ```
|
|
246
|
+
*/
|
|
247
|
+
nullable() {
|
|
248
|
+
return new ValNullable(this);
|
|
249
|
+
}
|
|
250
|
+
/**
|
|
251
|
+
* Makes the schema accept null or undefined values.
|
|
252
|
+
*
|
|
253
|
+
* @returns A new schema that accepts T | null | undefined
|
|
254
|
+
*
|
|
255
|
+
* @example
|
|
256
|
+
* ```typescript
|
|
257
|
+
* const schema = v.string().nullish();
|
|
258
|
+
* schema.parse(null); // null
|
|
259
|
+
* schema.parse(undefined); // undefined
|
|
260
|
+
* schema.parse('hello'); // 'hello'
|
|
261
|
+
* ```
|
|
262
|
+
*/
|
|
263
|
+
nullish() {
|
|
264
|
+
return new ValNullish(this);
|
|
265
|
+
}
|
|
266
|
+
/**
|
|
267
|
+
* Provides a default value when the input is undefined.
|
|
268
|
+
*
|
|
269
|
+
* @param defaultValue - The default value or a function that returns it
|
|
270
|
+
* @returns A new schema that uses the default when input is undefined
|
|
271
|
+
*
|
|
272
|
+
* @example
|
|
273
|
+
* ```typescript
|
|
274
|
+
* const schema = v.string().default('anonymous');
|
|
275
|
+
* schema.parse(undefined); // 'anonymous'
|
|
276
|
+
* schema.parse('hello'); // 'hello'
|
|
277
|
+
* ```
|
|
278
|
+
*/
|
|
279
|
+
default(defaultValue) {
|
|
280
|
+
return new ValDefault(this, defaultValue);
|
|
281
|
+
}
|
|
282
|
+
/**
|
|
283
|
+
* Provides a fallback value when parsing fails.
|
|
284
|
+
*
|
|
285
|
+
* @param catchValue - The fallback value or a function that returns it
|
|
286
|
+
* @returns A new schema that uses the fallback on parse error
|
|
287
|
+
*
|
|
288
|
+
* @example
|
|
289
|
+
* ```typescript
|
|
290
|
+
* const schema = v.number().catch(0);
|
|
291
|
+
* schema.parse('not a number'); // 0
|
|
292
|
+
* schema.parse(42); // 42
|
|
293
|
+
* ```
|
|
294
|
+
*/
|
|
295
|
+
catch(catchValue) {
|
|
296
|
+
return new ValCatch(this, catchValue);
|
|
297
|
+
}
|
|
298
|
+
/**
|
|
299
|
+
* Creates an array schema with this schema as the element type.
|
|
300
|
+
*
|
|
301
|
+
* This is an alternative syntax for `v.array(schema)`.
|
|
302
|
+
*
|
|
303
|
+
* @returns A new array schema
|
|
304
|
+
*
|
|
305
|
+
* @example
|
|
306
|
+
* ```typescript
|
|
307
|
+
* const schema = v.string().array();
|
|
308
|
+
* schema.parse(['a', 'b', 'c']); // ['a', 'b', 'c']
|
|
309
|
+
*
|
|
310
|
+
* type Arr = v.infer<typeof schema>; // string[]
|
|
311
|
+
* ```
|
|
312
|
+
*/
|
|
313
|
+
array() {
|
|
314
|
+
return new ValArray(this);
|
|
315
|
+
}
|
|
316
|
+
/**
|
|
317
|
+
* Creates a union of this schema with another schema.
|
|
318
|
+
*
|
|
319
|
+
* @param other - The other schema to union with
|
|
320
|
+
* @returns A new union schema
|
|
321
|
+
*
|
|
322
|
+
* @example
|
|
323
|
+
* ```typescript
|
|
324
|
+
* const schema = v.string().or(v.number());
|
|
325
|
+
* schema.parse('hello'); // 'hello'
|
|
326
|
+
* schema.parse(42); // 42
|
|
327
|
+
*
|
|
328
|
+
* type T = v.infer<typeof schema>; // string | number
|
|
329
|
+
* ```
|
|
330
|
+
*/
|
|
331
|
+
or(other) {
|
|
332
|
+
return new ValUnion([this, other]);
|
|
333
|
+
}
|
|
334
|
+
/**
|
|
335
|
+
* Creates an intersection of this schema with another schema.
|
|
336
|
+
*
|
|
337
|
+
* @param other - The other schema to intersect with
|
|
338
|
+
* @returns A new intersection schema
|
|
339
|
+
*
|
|
340
|
+
* @example
|
|
341
|
+
* ```typescript
|
|
342
|
+
* const A = v.object({ a: v.string() });
|
|
343
|
+
* const B = v.object({ b: v.number() });
|
|
344
|
+
* const AB = A.and(B);
|
|
345
|
+
*
|
|
346
|
+
* type AB = v.infer<typeof AB>; // { a: string } & { b: number }
|
|
347
|
+
* ```
|
|
348
|
+
*/
|
|
349
|
+
and(other) {
|
|
350
|
+
return new ValIntersection(this, other);
|
|
351
|
+
}
|
|
352
|
+
// ============================================================================
|
|
353
|
+
// Transform and Refinement Methods
|
|
354
|
+
// ============================================================================
|
|
355
|
+
/**
|
|
356
|
+
* Transforms the output value after validation.
|
|
357
|
+
*
|
|
358
|
+
* Changes the output type of the schema.
|
|
359
|
+
*
|
|
360
|
+
* @param fn - Transform function that receives validated value
|
|
361
|
+
* @returns A new schema with transformed output type
|
|
362
|
+
*
|
|
363
|
+
* @example
|
|
364
|
+
* ```typescript
|
|
365
|
+
* const schema = v.string().transform(s => parseInt(s, 10));
|
|
366
|
+
* schema.parse('42'); // 42 (number)
|
|
367
|
+
*
|
|
368
|
+
* type Input = v.input<typeof schema>; // string
|
|
369
|
+
* type Output = v.output<typeof schema>; // number
|
|
370
|
+
* ```
|
|
371
|
+
*/
|
|
372
|
+
transform(fn) {
|
|
373
|
+
return new ValTransformed(this, fn);
|
|
374
|
+
}
|
|
375
|
+
/**
|
|
376
|
+
* Adds a custom validation refinement.
|
|
377
|
+
*
|
|
378
|
+
* Does not change the output type.
|
|
379
|
+
*
|
|
380
|
+
* @param predicate - Function that returns true if valid, false otherwise
|
|
381
|
+
* @param messageOrOptions - Error message or options object
|
|
382
|
+
* @returns A new schema with the refinement
|
|
383
|
+
*
|
|
384
|
+
* @example
|
|
385
|
+
* ```typescript
|
|
386
|
+
* const schema = v.string().refine(
|
|
387
|
+
* s => s.length > 0,
|
|
388
|
+
* 'Required'
|
|
389
|
+
* );
|
|
390
|
+
*
|
|
391
|
+
* const schemaWithPath = v.string().refine(
|
|
392
|
+
* s => s.includes('@'),
|
|
393
|
+
* { message: 'Must contain @', path: ['email'] }
|
|
394
|
+
* );
|
|
395
|
+
* ```
|
|
396
|
+
*/
|
|
397
|
+
refine(predicate, messageOrOptions = 'Invalid value') {
|
|
398
|
+
const options = typeof messageOrOptions === 'string'
|
|
399
|
+
? { message: messageOrOptions }
|
|
400
|
+
: messageOrOptions;
|
|
401
|
+
return new ValRefined(this, predicate, options);
|
|
402
|
+
}
|
|
403
|
+
/**
|
|
404
|
+
* Adds advanced refinement that can add multiple issues.
|
|
405
|
+
*
|
|
406
|
+
* Provides a context object for adding validation issues.
|
|
407
|
+
*
|
|
408
|
+
* @param fn - Refinement function with context
|
|
409
|
+
* @returns A new schema with the refinement
|
|
410
|
+
*
|
|
411
|
+
* @example
|
|
412
|
+
* ```typescript
|
|
413
|
+
* const schema = v.string().superRefine((val, ctx) => {
|
|
414
|
+
* if (val.length < 5) {
|
|
415
|
+
* ctx.addIssue({
|
|
416
|
+
* code: 'custom',
|
|
417
|
+
* message: 'Too short',
|
|
418
|
+
* path: [],
|
|
419
|
+
* });
|
|
420
|
+
* }
|
|
421
|
+
* if (val.length > 100) {
|
|
422
|
+
* ctx.addIssue({
|
|
423
|
+
* code: 'custom',
|
|
424
|
+
* message: 'Too long',
|
|
425
|
+
* path: [],
|
|
426
|
+
* });
|
|
427
|
+
* }
|
|
428
|
+
* });
|
|
429
|
+
* ```
|
|
430
|
+
*/
|
|
431
|
+
superRefine(fn) {
|
|
432
|
+
return new ValSuperRefined(this, fn);
|
|
433
|
+
}
|
|
434
|
+
/**
|
|
435
|
+
* Pipes the output of this schema into another schema.
|
|
436
|
+
*
|
|
437
|
+
* Useful for chaining transforms with additional validation.
|
|
438
|
+
*
|
|
439
|
+
* @param schema - The schema to pipe into
|
|
440
|
+
* @returns A new schema that validates with both schemas
|
|
441
|
+
*
|
|
442
|
+
* @example
|
|
443
|
+
* ```typescript
|
|
444
|
+
* const schema = v.string()
|
|
445
|
+
* .transform(s => parseInt(s, 10))
|
|
446
|
+
* .pipe(v.number().positive());
|
|
447
|
+
*
|
|
448
|
+
* schema.parse('42'); // 42
|
|
449
|
+
* schema.parse('-5'); // throws (not positive)
|
|
450
|
+
* ```
|
|
451
|
+
*/
|
|
452
|
+
pipe(schema) {
|
|
453
|
+
return new ValPiped(this, schema);
|
|
454
|
+
}
|
|
455
|
+
// ============================================================================
|
|
456
|
+
// Async Parsing Methods
|
|
457
|
+
// ============================================================================
|
|
458
|
+
/**
|
|
459
|
+
* Asynchronously parses the input value.
|
|
460
|
+
*
|
|
461
|
+
* Required when using async transforms or refinements.
|
|
462
|
+
*
|
|
463
|
+
* @param data - The value to validate
|
|
464
|
+
* @returns Promise resolving to the validated value
|
|
465
|
+
* @throws {ValError} If validation fails
|
|
466
|
+
*
|
|
467
|
+
* @example
|
|
468
|
+
* ```typescript
|
|
469
|
+
* const schema = v.string().refine(async (s) => {
|
|
470
|
+
* return await checkIfExists(s);
|
|
471
|
+
* }, 'Not found');
|
|
472
|
+
*
|
|
473
|
+
* const value = await schema.parseAsync('test');
|
|
474
|
+
* ```
|
|
475
|
+
*/
|
|
476
|
+
async parseAsync(data) {
|
|
477
|
+
const result = await this.safeParseAsync(data);
|
|
478
|
+
if (result.success) {
|
|
479
|
+
return result.data;
|
|
480
|
+
}
|
|
481
|
+
throw result.error;
|
|
482
|
+
}
|
|
483
|
+
/**
|
|
484
|
+
* Asynchronously parses the input and returns a result object.
|
|
485
|
+
*
|
|
486
|
+
* Never throws. Required when using async transforms or refinements.
|
|
487
|
+
*
|
|
488
|
+
* @param data - The value to validate
|
|
489
|
+
* @returns Promise resolving to result object
|
|
490
|
+
*
|
|
491
|
+
* @example
|
|
492
|
+
* ```typescript
|
|
493
|
+
* const schema = v.string().transform(async (s) => {
|
|
494
|
+
* return await lookupValue(s);
|
|
495
|
+
* });
|
|
496
|
+
*
|
|
497
|
+
* const result = await schema.safeParseAsync('key');
|
|
498
|
+
* if (result.success) {
|
|
499
|
+
* console.log(result.data);
|
|
500
|
+
* }
|
|
501
|
+
* ```
|
|
502
|
+
*/
|
|
503
|
+
async safeParseAsync(data) {
|
|
504
|
+
const result = this['~standard'].validate(data);
|
|
505
|
+
// Handle both sync and async validation results
|
|
506
|
+
const resolvedResult = result instanceof Promise ? await result : result;
|
|
507
|
+
if (isValidationSuccess(resolvedResult)) {
|
|
508
|
+
return {
|
|
509
|
+
success: true,
|
|
510
|
+
data: resolvedResult.value,
|
|
511
|
+
};
|
|
512
|
+
}
|
|
513
|
+
return {
|
|
514
|
+
success: false,
|
|
515
|
+
error: new ValError(resolvedResult.issues),
|
|
516
|
+
};
|
|
517
|
+
}
|
|
518
|
+
}
|
|
519
|
+
/**
|
|
520
|
+
* Schema for array values with validation methods.
|
|
521
|
+
*
|
|
522
|
+
* Supports chainable methods like `.min()`, `.max()`, `.length()`, `.nonempty()`.
|
|
523
|
+
* Each method returns a new schema instance (immutable).
|
|
524
|
+
*
|
|
525
|
+
* @template S - The element schema type
|
|
526
|
+
*
|
|
527
|
+
* @example
|
|
528
|
+
* ```typescript
|
|
529
|
+
* const schema = v.array(v.string());
|
|
530
|
+
* schema.parse(['a', 'b', 'c']); // ['a', 'b', 'c']
|
|
531
|
+
*
|
|
532
|
+
* const nonEmpty = v.array(v.number()).nonempty();
|
|
533
|
+
* nonEmpty.parse([1, 2]); // [1, 2]
|
|
534
|
+
* nonEmpty.parse([]); // throws
|
|
535
|
+
* ```
|
|
536
|
+
*/
|
|
537
|
+
export class ValArray extends ValSchema {
|
|
538
|
+
element;
|
|
539
|
+
validators;
|
|
540
|
+
constructor(elementSchema, validators = []) {
|
|
541
|
+
const capturedValidators = validators;
|
|
542
|
+
const validateFn = (value) => {
|
|
543
|
+
if (!Array.isArray(value)) {
|
|
544
|
+
return { issues: [{ message: 'Expected array' }] };
|
|
545
|
+
}
|
|
546
|
+
const result = [];
|
|
547
|
+
const issues = [];
|
|
548
|
+
for (let i = 0; i < value.length; i++) {
|
|
549
|
+
const elementResult = elementSchema['~standard'].validate(value[i]);
|
|
550
|
+
if (elementResult.issues !== undefined) {
|
|
551
|
+
for (const issue of elementResult.issues) {
|
|
552
|
+
issues.push({
|
|
553
|
+
message: issue.message,
|
|
554
|
+
path: [i, ...(issue.path ?? [])],
|
|
555
|
+
});
|
|
556
|
+
}
|
|
557
|
+
}
|
|
558
|
+
else {
|
|
559
|
+
result.push(elementResult.value);
|
|
560
|
+
}
|
|
561
|
+
}
|
|
562
|
+
if (issues.length > 0) {
|
|
563
|
+
return { issues };
|
|
564
|
+
}
|
|
565
|
+
// Run array-level validators
|
|
566
|
+
for (let i = 0; i < capturedValidators.length; i++) {
|
|
567
|
+
const validator = capturedValidators[i];
|
|
568
|
+
if (validator !== undefined) {
|
|
569
|
+
const issue = validator(result);
|
|
570
|
+
if (issue !== null) {
|
|
571
|
+
return issue;
|
|
572
|
+
}
|
|
573
|
+
}
|
|
574
|
+
}
|
|
575
|
+
return { value: result };
|
|
576
|
+
};
|
|
577
|
+
const inputJsonSchemaFn = (target) => ({
|
|
578
|
+
type: 'array',
|
|
579
|
+
items: elementSchema['~standard'].jsonSchema.input({ target }),
|
|
580
|
+
});
|
|
581
|
+
const outputJsonSchemaFn = (target) => ({
|
|
582
|
+
type: 'array',
|
|
583
|
+
items: elementSchema['~standard'].jsonSchema.output({ target }),
|
|
584
|
+
});
|
|
585
|
+
super(validateFn, inputJsonSchemaFn, outputJsonSchemaFn);
|
|
586
|
+
this.element = elementSchema;
|
|
587
|
+
this.validators = validators;
|
|
588
|
+
this._hasTransforms = elementSchema.hasTransforms() || validators.length > 0;
|
|
589
|
+
}
|
|
590
|
+
/**
|
|
591
|
+
* Creates a copy of this schema with additional validators.
|
|
592
|
+
*/
|
|
593
|
+
clone(additionalValidators) {
|
|
594
|
+
return new ValArray(this.element, [...this.validators, ...additionalValidators]);
|
|
595
|
+
}
|
|
596
|
+
/**
|
|
597
|
+
* Requires array to have at least `length` elements.
|
|
598
|
+
*/
|
|
599
|
+
min(length, message) {
|
|
600
|
+
return this.clone([
|
|
601
|
+
(arr) => arr.length >= length
|
|
602
|
+
? null
|
|
603
|
+
: { issues: [{ message: message ?? `Array must have at least ${length} element(s)` }] },
|
|
604
|
+
]);
|
|
605
|
+
}
|
|
606
|
+
/**
|
|
607
|
+
* Requires array to have at most `length` elements.
|
|
608
|
+
*/
|
|
609
|
+
max(length, message) {
|
|
610
|
+
return this.clone([
|
|
611
|
+
(arr) => arr.length <= length
|
|
612
|
+
? null
|
|
613
|
+
: { issues: [{ message: message ?? `Array must have at most ${length} element(s)` }] },
|
|
614
|
+
]);
|
|
615
|
+
}
|
|
616
|
+
/**
|
|
617
|
+
* Requires array to have exactly `len` elements.
|
|
618
|
+
*/
|
|
619
|
+
length(len, message) {
|
|
620
|
+
return this.clone([
|
|
621
|
+
(arr) => arr.length === len
|
|
622
|
+
? null
|
|
623
|
+
: { issues: [{ message: message ?? `Array must have exactly ${len} element(s)` }] },
|
|
624
|
+
]);
|
|
625
|
+
}
|
|
626
|
+
/**
|
|
627
|
+
* Requires array to be non-empty (at least 1 element).
|
|
628
|
+
*/
|
|
629
|
+
nonempty(message) {
|
|
630
|
+
return this.min(1, message ?? 'Array must not be empty');
|
|
631
|
+
}
|
|
632
|
+
}
|
|
633
|
+
/**
|
|
634
|
+
* Schema for union types (one of multiple schemas).
|
|
635
|
+
*
|
|
636
|
+
* @template T - Array of member schemas
|
|
637
|
+
*
|
|
638
|
+
* @example
|
|
639
|
+
* ```typescript
|
|
640
|
+
* const schema = v.union([v.string(), v.number()]);
|
|
641
|
+
* schema.parse('hello'); // 'hello'
|
|
642
|
+
* schema.parse(42); // 42
|
|
643
|
+
*
|
|
644
|
+
* type U = v.infer<typeof schema>; // string | number
|
|
645
|
+
* ```
|
|
646
|
+
*/
|
|
647
|
+
export class ValUnion extends ValSchema {
|
|
648
|
+
options;
|
|
649
|
+
constructor(options) {
|
|
650
|
+
const validateFn = (value) => {
|
|
651
|
+
for (const option of options) {
|
|
652
|
+
const result = option['~standard'].validate(value);
|
|
653
|
+
if (result.issues === undefined) {
|
|
654
|
+
return { value: result.value };
|
|
655
|
+
}
|
|
656
|
+
}
|
|
657
|
+
return {
|
|
658
|
+
issues: [{ message: `Invalid union: none of ${options.length} variants matched` }],
|
|
659
|
+
};
|
|
660
|
+
};
|
|
661
|
+
const inputJsonSchemaFn = (target) => ({
|
|
662
|
+
oneOf: options.map((o) => o['~standard'].jsonSchema.input({ target })),
|
|
663
|
+
});
|
|
664
|
+
const outputJsonSchemaFn = (target) => ({
|
|
665
|
+
oneOf: options.map((o) => o['~standard'].jsonSchema.output({ target })),
|
|
666
|
+
});
|
|
667
|
+
super(validateFn, inputJsonSchemaFn, outputJsonSchemaFn);
|
|
668
|
+
this.options = options;
|
|
669
|
+
this._hasTransforms = options.some(s => s.hasTransforms());
|
|
670
|
+
}
|
|
671
|
+
}
|
|
672
|
+
// ============================================================================
|
|
673
|
+
// ValIntersection Class
|
|
674
|
+
// ============================================================================
|
|
675
|
+
/**
|
|
676
|
+
* Schema for intersection types (all of multiple schemas).
|
|
677
|
+
*
|
|
678
|
+
* @template L - Left schema
|
|
679
|
+
* @template R - Right schema
|
|
680
|
+
*
|
|
681
|
+
* @example
|
|
682
|
+
* ```typescript
|
|
683
|
+
* const A = v.object({ a: v.string() });
|
|
684
|
+
* const B = v.object({ b: v.number() });
|
|
685
|
+
* const AB = v.intersection(A, B);
|
|
686
|
+
*
|
|
687
|
+
* type AB = v.infer<typeof AB>; // { a: string } & { b: number }
|
|
688
|
+
* ```
|
|
689
|
+
*/
|
|
690
|
+
export class ValIntersection extends ValSchema {
|
|
691
|
+
left;
|
|
692
|
+
right;
|
|
693
|
+
constructor(left, right) {
|
|
694
|
+
const validateFn = (value) => {
|
|
695
|
+
const leftResult = left['~standard'].validate(value);
|
|
696
|
+
if (leftResult.issues !== undefined) {
|
|
697
|
+
return leftResult;
|
|
698
|
+
}
|
|
699
|
+
const rightResult = right['~standard'].validate(value);
|
|
700
|
+
if (rightResult.issues !== undefined) {
|
|
701
|
+
return rightResult;
|
|
702
|
+
}
|
|
703
|
+
// Merge results for objects
|
|
704
|
+
if (typeof leftResult.value === 'object' &&
|
|
705
|
+
leftResult.value !== null &&
|
|
706
|
+
typeof rightResult.value === 'object' &&
|
|
707
|
+
rightResult.value !== null) {
|
|
708
|
+
return { value: { ...leftResult.value, ...rightResult.value } };
|
|
709
|
+
}
|
|
710
|
+
// For non-objects, just return the left result (both passed)
|
|
711
|
+
return { value: leftResult.value };
|
|
712
|
+
};
|
|
713
|
+
const inputJsonSchemaFn = (target) => ({
|
|
714
|
+
allOf: [
|
|
715
|
+
left['~standard'].jsonSchema.input({ target }),
|
|
716
|
+
right['~standard'].jsonSchema.input({ target }),
|
|
717
|
+
],
|
|
718
|
+
});
|
|
719
|
+
const outputJsonSchemaFn = (target) => ({
|
|
720
|
+
allOf: [
|
|
721
|
+
left['~standard'].jsonSchema.output({ target }),
|
|
722
|
+
right['~standard'].jsonSchema.output({ target }),
|
|
723
|
+
],
|
|
724
|
+
});
|
|
725
|
+
super(validateFn, inputJsonSchemaFn, outputJsonSchemaFn);
|
|
726
|
+
this.left = left;
|
|
727
|
+
this.right = right;
|
|
728
|
+
this._hasTransforms = left.hasTransforms() || right.hasTransforms();
|
|
729
|
+
}
|
|
730
|
+
}
|
|
731
|
+
/**
|
|
732
|
+
* Schema wrapper that makes the inner schema optional (accepts undefined).
|
|
733
|
+
*/
|
|
734
|
+
export class ValOptional extends ValSchema {
|
|
735
|
+
innerSchema;
|
|
736
|
+
constructor(schema) {
|
|
737
|
+
const validateFn = (value) => {
|
|
738
|
+
if (value === undefined) {
|
|
739
|
+
return { value: undefined };
|
|
740
|
+
}
|
|
741
|
+
return schema['~standard'].validate(value);
|
|
742
|
+
};
|
|
743
|
+
const inputJsonSchemaFn = (target) => {
|
|
744
|
+
const innerSchema = schema['~standard'].jsonSchema.input({ target });
|
|
745
|
+
return { oneOf: [innerSchema, { type: 'null' }] };
|
|
746
|
+
};
|
|
747
|
+
const outputJsonSchemaFn = (target) => {
|
|
748
|
+
const innerSchema = schema['~standard'].jsonSchema.output({ target });
|
|
749
|
+
return { oneOf: [innerSchema, { type: 'null' }] };
|
|
750
|
+
};
|
|
751
|
+
super(validateFn, inputJsonSchemaFn, outputJsonSchemaFn);
|
|
752
|
+
this.innerSchema = schema;
|
|
753
|
+
}
|
|
754
|
+
isOptional() {
|
|
755
|
+
return true;
|
|
756
|
+
}
|
|
757
|
+
/**
|
|
758
|
+
* Returns the inner schema (unwrapped).
|
|
759
|
+
*/
|
|
760
|
+
unwrap() {
|
|
761
|
+
return this.innerSchema;
|
|
762
|
+
}
|
|
763
|
+
}
|
|
764
|
+
/**
|
|
765
|
+
* Schema wrapper that makes the inner schema nullable (accepts null).
|
|
766
|
+
*/
|
|
767
|
+
export class ValNullable extends ValSchema {
|
|
768
|
+
innerSchema;
|
|
769
|
+
constructor(schema) {
|
|
770
|
+
const validateFn = (value) => {
|
|
771
|
+
if (value === null) {
|
|
772
|
+
return { value: null };
|
|
773
|
+
}
|
|
774
|
+
return schema['~standard'].validate(value);
|
|
775
|
+
};
|
|
776
|
+
const inputJsonSchemaFn = (target) => {
|
|
777
|
+
const innerSchema = schema['~standard'].jsonSchema.input({ target });
|
|
778
|
+
return { oneOf: [innerSchema, { type: 'null' }] };
|
|
779
|
+
};
|
|
780
|
+
const outputJsonSchemaFn = (target) => {
|
|
781
|
+
const innerSchema = schema['~standard'].jsonSchema.output({ target });
|
|
782
|
+
return { oneOf: [innerSchema, { type: 'null' }] };
|
|
783
|
+
};
|
|
784
|
+
super(validateFn, inputJsonSchemaFn, outputJsonSchemaFn);
|
|
785
|
+
this.innerSchema = schema;
|
|
786
|
+
}
|
|
787
|
+
isNullable() {
|
|
788
|
+
return true;
|
|
789
|
+
}
|
|
790
|
+
/**
|
|
791
|
+
* Returns the inner schema (unwrapped).
|
|
792
|
+
*/
|
|
793
|
+
unwrap() {
|
|
794
|
+
return this.innerSchema;
|
|
795
|
+
}
|
|
796
|
+
}
|
|
797
|
+
/**
|
|
798
|
+
* Schema wrapper that makes the inner schema nullish (accepts null or undefined).
|
|
799
|
+
*/
|
|
800
|
+
export class ValNullish extends ValSchema {
|
|
801
|
+
innerSchema;
|
|
802
|
+
constructor(schema) {
|
|
803
|
+
const validateFn = (value) => {
|
|
804
|
+
if (value === null) {
|
|
805
|
+
return { value: null };
|
|
806
|
+
}
|
|
807
|
+
if (value === undefined) {
|
|
808
|
+
return { value: undefined };
|
|
809
|
+
}
|
|
810
|
+
return schema['~standard'].validate(value);
|
|
811
|
+
};
|
|
812
|
+
const inputJsonSchemaFn = (target) => {
|
|
813
|
+
const innerSchema = schema['~standard'].jsonSchema.input({ target });
|
|
814
|
+
return { oneOf: [innerSchema, { type: 'null' }] };
|
|
815
|
+
};
|
|
816
|
+
const outputJsonSchemaFn = (target) => {
|
|
817
|
+
const innerSchema = schema['~standard'].jsonSchema.output({ target });
|
|
818
|
+
return { oneOf: [innerSchema, { type: 'null' }] };
|
|
819
|
+
};
|
|
820
|
+
super(validateFn, inputJsonSchemaFn, outputJsonSchemaFn);
|
|
821
|
+
this.innerSchema = schema;
|
|
822
|
+
}
|
|
823
|
+
isOptional() {
|
|
824
|
+
return true;
|
|
825
|
+
}
|
|
826
|
+
isNullable() {
|
|
827
|
+
return true;
|
|
828
|
+
}
|
|
829
|
+
/**
|
|
830
|
+
* Returns the inner schema (unwrapped).
|
|
831
|
+
*/
|
|
832
|
+
unwrap() {
|
|
833
|
+
return this.innerSchema;
|
|
834
|
+
}
|
|
835
|
+
}
|
|
836
|
+
/**
|
|
837
|
+
* Schema wrapper that provides a default value when input is undefined.
|
|
838
|
+
*/
|
|
839
|
+
export class ValDefault extends ValSchema {
|
|
840
|
+
innerSchema;
|
|
841
|
+
constructor(schema, defaultValue) {
|
|
842
|
+
const capturedDefault = defaultValue;
|
|
843
|
+
const validateFn = (value) => {
|
|
844
|
+
if (value === undefined) {
|
|
845
|
+
const resolved = typeof capturedDefault === 'function'
|
|
846
|
+
? capturedDefault()
|
|
847
|
+
: capturedDefault;
|
|
848
|
+
return { value: resolved };
|
|
849
|
+
}
|
|
850
|
+
return schema['~standard'].validate(value);
|
|
851
|
+
};
|
|
852
|
+
const inputJsonSchemaFn = (target) => {
|
|
853
|
+
return schema['~standard'].jsonSchema.input({ target });
|
|
854
|
+
};
|
|
855
|
+
const outputJsonSchemaFn = (target) => {
|
|
856
|
+
return schema['~standard'].jsonSchema.output({ target });
|
|
857
|
+
};
|
|
858
|
+
super(validateFn, inputJsonSchemaFn, outputJsonSchemaFn);
|
|
859
|
+
this.innerSchema = schema;
|
|
860
|
+
this._hasTransforms = true;
|
|
861
|
+
}
|
|
862
|
+
/**
|
|
863
|
+
* Removes the default wrapper.
|
|
864
|
+
*/
|
|
865
|
+
removeDefault() {
|
|
866
|
+
return this.innerSchema;
|
|
867
|
+
}
|
|
868
|
+
}
|
|
869
|
+
/**
|
|
870
|
+
* Schema wrapper that provides a catch value when parsing fails.
|
|
871
|
+
*/
|
|
872
|
+
export class ValCatch extends ValSchema {
|
|
873
|
+
innerSchema;
|
|
874
|
+
constructor(schema, catchValue) {
|
|
875
|
+
const capturedCatch = catchValue;
|
|
876
|
+
const validateFn = (value) => {
|
|
877
|
+
const result = schema['~standard'].validate(value);
|
|
878
|
+
if (result.issues !== undefined) {
|
|
879
|
+
const resolved = typeof capturedCatch === 'function'
|
|
880
|
+
? capturedCatch()
|
|
881
|
+
: capturedCatch;
|
|
882
|
+
return { value: resolved };
|
|
883
|
+
}
|
|
884
|
+
return result;
|
|
885
|
+
};
|
|
886
|
+
const inputJsonSchemaFn = (target) => {
|
|
887
|
+
return schema['~standard'].jsonSchema.input({ target });
|
|
888
|
+
};
|
|
889
|
+
const outputJsonSchemaFn = (target) => {
|
|
890
|
+
return schema['~standard'].jsonSchema.output({ target });
|
|
891
|
+
};
|
|
892
|
+
super(validateFn, inputJsonSchemaFn, outputJsonSchemaFn);
|
|
893
|
+
this.innerSchema = schema;
|
|
894
|
+
this._hasTransforms = true;
|
|
895
|
+
}
|
|
896
|
+
/**
|
|
897
|
+
* Removes the catch wrapper.
|
|
898
|
+
*/
|
|
899
|
+
removeCatch() {
|
|
900
|
+
return this.innerSchema;
|
|
901
|
+
}
|
|
902
|
+
}
|
|
903
|
+
// ============================================================================
|
|
904
|
+
// Transform and Refinement Schema Classes
|
|
905
|
+
// ============================================================================
|
|
906
|
+
/**
|
|
907
|
+
* Schema that applies a transform function after validation.
|
|
908
|
+
*
|
|
909
|
+
* Changes the output type of the schema.
|
|
910
|
+
*
|
|
911
|
+
* @template Input - The original input type
|
|
912
|
+
* @template Middle - The intermediate validated type
|
|
913
|
+
* @template Output - The final transformed output type
|
|
914
|
+
*/
|
|
915
|
+
export class ValTransformed extends ValSchema {
|
|
916
|
+
constructor(schema, fn) {
|
|
917
|
+
const capturedFn = fn;
|
|
918
|
+
const capturedSchema = schema;
|
|
919
|
+
const validateFn = (value) => {
|
|
920
|
+
const result = capturedSchema['~standard'].validate(value);
|
|
921
|
+
// Handle async inner validation
|
|
922
|
+
if (result instanceof Promise) {
|
|
923
|
+
return result.then((resolvedResult) => {
|
|
924
|
+
if (resolvedResult.issues !== undefined) {
|
|
925
|
+
return resolvedResult;
|
|
926
|
+
}
|
|
927
|
+
const transformed = capturedFn(resolvedResult.value);
|
|
928
|
+
if (transformed instanceof Promise) {
|
|
929
|
+
return transformed.then((v) => success(v));
|
|
930
|
+
}
|
|
931
|
+
return success(transformed);
|
|
932
|
+
});
|
|
933
|
+
}
|
|
934
|
+
if (result.issues !== undefined) {
|
|
935
|
+
return result;
|
|
936
|
+
}
|
|
937
|
+
// Apply transform
|
|
938
|
+
const transformed = capturedFn(result.value);
|
|
939
|
+
if (transformed instanceof Promise) {
|
|
940
|
+
return transformed.then((v) => success(v));
|
|
941
|
+
}
|
|
942
|
+
return success(transformed);
|
|
943
|
+
};
|
|
944
|
+
const inputJsonSchemaFn = (target) => {
|
|
945
|
+
return capturedSchema['~standard'].jsonSchema.input({ target });
|
|
946
|
+
};
|
|
947
|
+
// Output JSON schema is not available for arbitrary transforms
|
|
948
|
+
const outputJsonSchemaFn = (_target) => {
|
|
949
|
+
return {};
|
|
950
|
+
};
|
|
951
|
+
// Cast to sync validate function - async is handled by parseAsync/safeParseAsync
|
|
952
|
+
super(validateFn, inputJsonSchemaFn, outputJsonSchemaFn);
|
|
953
|
+
this._hasTransforms = true;
|
|
954
|
+
}
|
|
955
|
+
}
|
|
956
|
+
/**
|
|
957
|
+
* Schema that adds a refinement predicate.
|
|
958
|
+
*
|
|
959
|
+
* Does not change the output type.
|
|
960
|
+
*
|
|
961
|
+
* @template Input - The input type
|
|
962
|
+
* @template Output - The output type
|
|
963
|
+
*/
|
|
964
|
+
export class ValRefined extends ValSchema {
|
|
965
|
+
constructor(schema, predicate, options) {
|
|
966
|
+
const capturedPredicate = predicate;
|
|
967
|
+
const capturedOptions = options;
|
|
968
|
+
const capturedSchema = schema;
|
|
969
|
+
const validateFn = (value) => {
|
|
970
|
+
const result = capturedSchema['~standard'].validate(value);
|
|
971
|
+
// Handle async inner validation
|
|
972
|
+
if (result instanceof Promise) {
|
|
973
|
+
return result.then((resolvedResult) => {
|
|
974
|
+
if (resolvedResult.issues !== undefined) {
|
|
975
|
+
return resolvedResult;
|
|
976
|
+
}
|
|
977
|
+
return applyRefinement(resolvedResult.value);
|
|
978
|
+
});
|
|
979
|
+
}
|
|
980
|
+
if (result.issues !== undefined) {
|
|
981
|
+
return result;
|
|
982
|
+
}
|
|
983
|
+
return applyRefinement(result.value);
|
|
984
|
+
function applyRefinement(validatedValue) {
|
|
985
|
+
const predicateResult = capturedPredicate(validatedValue);
|
|
986
|
+
if (predicateResult instanceof Promise) {
|
|
987
|
+
return predicateResult.then((isValid) => {
|
|
988
|
+
if (!isValid) {
|
|
989
|
+
const issue = {
|
|
990
|
+
message: capturedOptions.message ?? 'Invalid value',
|
|
991
|
+
};
|
|
992
|
+
if (capturedOptions.path !== undefined) {
|
|
993
|
+
issue.path = capturedOptions.path;
|
|
994
|
+
}
|
|
995
|
+
return { issues: [issue] };
|
|
996
|
+
}
|
|
997
|
+
return success(validatedValue);
|
|
998
|
+
});
|
|
999
|
+
}
|
|
1000
|
+
if (!predicateResult) {
|
|
1001
|
+
const issue = {
|
|
1002
|
+
message: capturedOptions.message ?? 'Invalid value',
|
|
1003
|
+
};
|
|
1004
|
+
if (capturedOptions.path !== undefined) {
|
|
1005
|
+
issue.path = capturedOptions.path;
|
|
1006
|
+
}
|
|
1007
|
+
return { issues: [issue] };
|
|
1008
|
+
}
|
|
1009
|
+
return success(validatedValue);
|
|
1010
|
+
}
|
|
1011
|
+
};
|
|
1012
|
+
const inputJsonSchemaFn = (target) => {
|
|
1013
|
+
return capturedSchema['~standard'].jsonSchema.input({ target });
|
|
1014
|
+
};
|
|
1015
|
+
const outputJsonSchemaFn = (target) => {
|
|
1016
|
+
return capturedSchema['~standard'].jsonSchema.output({ target });
|
|
1017
|
+
};
|
|
1018
|
+
// Cast to sync validate function - async is handled by parseAsync/safeParseAsync
|
|
1019
|
+
super(validateFn, inputJsonSchemaFn, outputJsonSchemaFn);
|
|
1020
|
+
this._hasTransforms = true;
|
|
1021
|
+
}
|
|
1022
|
+
}
|
|
1023
|
+
/**
|
|
1024
|
+
* Schema that applies a superRefine function for advanced validation.
|
|
1025
|
+
*
|
|
1026
|
+
* Allows adding multiple issues via the context.
|
|
1027
|
+
*
|
|
1028
|
+
* @template Input - The input type
|
|
1029
|
+
* @template Output - The output type
|
|
1030
|
+
*/
|
|
1031
|
+
export class ValSuperRefined extends ValSchema {
|
|
1032
|
+
constructor(schema, fn) {
|
|
1033
|
+
const capturedFn = fn;
|
|
1034
|
+
const capturedSchema = schema;
|
|
1035
|
+
const validateFn = (value) => {
|
|
1036
|
+
const result = capturedSchema['~standard'].validate(value);
|
|
1037
|
+
// Handle async inner validation
|
|
1038
|
+
if (result instanceof Promise) {
|
|
1039
|
+
return result.then((resolvedResult) => {
|
|
1040
|
+
if (resolvedResult.issues !== undefined) {
|
|
1041
|
+
return resolvedResult;
|
|
1042
|
+
}
|
|
1043
|
+
return applySuperRefine(resolvedResult.value);
|
|
1044
|
+
});
|
|
1045
|
+
}
|
|
1046
|
+
if (result.issues !== undefined) {
|
|
1047
|
+
return result;
|
|
1048
|
+
}
|
|
1049
|
+
return applySuperRefine(result.value);
|
|
1050
|
+
function applySuperRefine(validatedValue) {
|
|
1051
|
+
const issues = [];
|
|
1052
|
+
const ctx = {
|
|
1053
|
+
addIssue(issue) {
|
|
1054
|
+
const newIssue = {
|
|
1055
|
+
message: issue.message,
|
|
1056
|
+
};
|
|
1057
|
+
if (issue.path !== undefined) {
|
|
1058
|
+
newIssue.path = issue.path;
|
|
1059
|
+
}
|
|
1060
|
+
issues.push(newIssue);
|
|
1061
|
+
},
|
|
1062
|
+
};
|
|
1063
|
+
const fnResult = capturedFn(validatedValue, ctx);
|
|
1064
|
+
if (fnResult instanceof Promise) {
|
|
1065
|
+
return fnResult.then(() => {
|
|
1066
|
+
if (issues.length > 0) {
|
|
1067
|
+
return { issues };
|
|
1068
|
+
}
|
|
1069
|
+
return success(validatedValue);
|
|
1070
|
+
});
|
|
1071
|
+
}
|
|
1072
|
+
if (issues.length > 0) {
|
|
1073
|
+
return { issues };
|
|
1074
|
+
}
|
|
1075
|
+
return success(validatedValue);
|
|
1076
|
+
}
|
|
1077
|
+
};
|
|
1078
|
+
const inputJsonSchemaFn = (target) => {
|
|
1079
|
+
return capturedSchema['~standard'].jsonSchema.input({ target });
|
|
1080
|
+
};
|
|
1081
|
+
const outputJsonSchemaFn = (target) => {
|
|
1082
|
+
return capturedSchema['~standard'].jsonSchema.output({ target });
|
|
1083
|
+
};
|
|
1084
|
+
// Cast to sync validate function - async is handled by parseAsync/safeParseAsync
|
|
1085
|
+
super(validateFn, inputJsonSchemaFn, outputJsonSchemaFn);
|
|
1086
|
+
this._hasTransforms = true;
|
|
1087
|
+
}
|
|
1088
|
+
}
|
|
1089
|
+
/**
|
|
1090
|
+
* Schema that pipes the output of one schema into another.
|
|
1091
|
+
*
|
|
1092
|
+
* Useful for chaining transforms with additional validation.
|
|
1093
|
+
*
|
|
1094
|
+
* @template Input - The original input type
|
|
1095
|
+
* @template Middle - The intermediate validated type
|
|
1096
|
+
* @template Output - The final output type from the second schema
|
|
1097
|
+
*/
|
|
1098
|
+
export class ValPiped extends ValSchema {
|
|
1099
|
+
constructor(first, second) {
|
|
1100
|
+
const capturedFirst = first;
|
|
1101
|
+
const capturedSecond = second;
|
|
1102
|
+
const validateFn = (value) => {
|
|
1103
|
+
const firstResult = capturedFirst['~standard'].validate(value);
|
|
1104
|
+
// Handle async first validation
|
|
1105
|
+
if (firstResult instanceof Promise) {
|
|
1106
|
+
return firstResult.then((resolvedFirst) => {
|
|
1107
|
+
if (resolvedFirst.issues !== undefined) {
|
|
1108
|
+
return resolvedFirst;
|
|
1109
|
+
}
|
|
1110
|
+
return capturedSecond['~standard'].validate(resolvedFirst.value);
|
|
1111
|
+
});
|
|
1112
|
+
}
|
|
1113
|
+
if (firstResult.issues !== undefined) {
|
|
1114
|
+
return firstResult;
|
|
1115
|
+
}
|
|
1116
|
+
return capturedSecond['~standard'].validate(firstResult.value);
|
|
1117
|
+
};
|
|
1118
|
+
const inputJsonSchemaFn = (target) => {
|
|
1119
|
+
return capturedFirst['~standard'].jsonSchema.input({ target });
|
|
1120
|
+
};
|
|
1121
|
+
const outputJsonSchemaFn = (target) => {
|
|
1122
|
+
return capturedSecond['~standard'].jsonSchema.output({ target });
|
|
1123
|
+
};
|
|
1124
|
+
// Cast to sync validate function - async is handled by parseAsync/safeParseAsync
|
|
1125
|
+
super(validateFn, inputJsonSchemaFn, outputJsonSchemaFn);
|
|
1126
|
+
this._hasTransforms = first.hasTransforms() || second.hasTransforms();
|
|
1127
|
+
}
|
|
1128
|
+
}
|
|
1129
|
+
/**
|
|
1130
|
+
* Schema that preprocesses input before validation.
|
|
1131
|
+
*
|
|
1132
|
+
* Useful for coercing input types before validation.
|
|
1133
|
+
*
|
|
1134
|
+
* @template Input - The preprocessed input type
|
|
1135
|
+
* @template Output - The validated output type
|
|
1136
|
+
*/
|
|
1137
|
+
export class ValPreprocessed extends ValSchema {
|
|
1138
|
+
constructor(preprocessFn, schema) {
|
|
1139
|
+
const capturedPreprocess = preprocessFn;
|
|
1140
|
+
const capturedSchema = schema;
|
|
1141
|
+
const validateFn = (value) => {
|
|
1142
|
+
const preprocessed = capturedPreprocess(value);
|
|
1143
|
+
return capturedSchema['~standard'].validate(preprocessed);
|
|
1144
|
+
};
|
|
1145
|
+
// Input schema is unknown since preprocessing accepts any input
|
|
1146
|
+
const inputJsonSchemaFn = (_target) => {
|
|
1147
|
+
return {};
|
|
1148
|
+
};
|
|
1149
|
+
const outputJsonSchemaFn = (target) => {
|
|
1150
|
+
return capturedSchema['~standard'].jsonSchema.output({ target });
|
|
1151
|
+
};
|
|
1152
|
+
// Cast to sync validate function - async is handled by parseAsync/safeParseAsync
|
|
1153
|
+
super(validateFn, inputJsonSchemaFn, outputJsonSchemaFn);
|
|
1154
|
+
this._hasTransforms = true;
|
|
1155
|
+
}
|
|
1156
|
+
}
|
|
1157
|
+
/**
|
|
1158
|
+
* Creates a ValSchema from an existing Standard Schema object.
|
|
1159
|
+
*
|
|
1160
|
+
* Useful for wrapping schemas created with the factory functions.
|
|
1161
|
+
*
|
|
1162
|
+
* @param standardSchema - A Standard Schema compliant object
|
|
1163
|
+
* @returns A ValSchema instance
|
|
1164
|
+
*/
|
|
1165
|
+
export function fromStandardSchema(standardSchema) {
|
|
1166
|
+
const std = standardSchema['~standard'];
|
|
1167
|
+
return new ValSchema(std.validate, (target) => std.jsonSchema.input({ target }), (target) => std.jsonSchema.output({ target }));
|
|
1168
|
+
}
|
|
1169
|
+
/**
|
|
1170
|
+
* Creates a ValSchema from a simple Standard Schema (without JSON Schema support).
|
|
1171
|
+
*
|
|
1172
|
+
* @param standardSchema - A Standard Schema compliant object
|
|
1173
|
+
* @param jsonSchemaFn - Function to generate JSON Schema
|
|
1174
|
+
* @returns A ValSchema instance
|
|
1175
|
+
*/
|
|
1176
|
+
export function fromSimpleStandardSchema(standardSchema, jsonSchemaFn) {
|
|
1177
|
+
return new ValSchema(standardSchema['~standard'].validate, jsonSchemaFn);
|
|
1178
|
+
}
|
|
1179
|
+
//# sourceMappingURL=schema.js.map
|