@uniflowed/validator 0.0.0-alpha.4 → 0.0.0-alpha.5
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/action.js +309 -0
- package/collection.js +338 -0
- package/index.js +245 -763
- package/infer.js +107 -0
- package/issue.js +129 -0
- package/json-schema.js +307 -0
- package/lazy.js +93 -0
- package/namespace.js +229 -0
- package/object.js +241 -0
- package/optional.js +132 -0
- package/package.json +19 -4
- package/parse.js +116 -0
- package/pipe.js +300 -0
- package/plain-object.js +105 -0
- package/primitive.js +183 -0
- package/schema.js +388 -0
- package/union.js +229 -0
package/index.js
CHANGED
|
@@ -1,783 +1,265 @@
|
|
|
1
1
|
// @flow
|
|
2
2
|
//
|
|
3
|
-
// `@uniflowed/validator`:
|
|
4
|
-
// work identically on Node.js, Deno and Bun with no native binding.
|
|
3
|
+
// `@uniflowed/validator`: a schema is a parser, not an assertion.
|
|
5
4
|
//
|
|
6
|
-
//
|
|
5
|
+
// ```js
|
|
6
|
+
// const Account = object({
|
|
7
|
+
// email: pipe(string(), trim(), email()),
|
|
8
|
+
// age: pipe(string(), transform(Number), integer(), min(18)),
|
|
9
|
+
// tags: array(pipe(string(), nonEmpty())),
|
|
10
|
+
// });
|
|
7
11
|
//
|
|
8
|
-
//
|
|
9
|
-
//
|
|
10
|
-
//
|
|
11
|
-
//
|
|
12
|
-
//
|
|
13
|
-
//
|
|
12
|
+
// const result = safeParse(Account, await request.json());
|
|
13
|
+
// if (!result.ok) {
|
|
14
|
+
// return respond(422, flatten(result.issues));
|
|
15
|
+
// }
|
|
16
|
+
// createAccount(result.value); // { email: string, age: number, tags: … }
|
|
17
|
+
// ```
|
|
14
18
|
//
|
|
15
|
-
//
|
|
16
|
-
//
|
|
17
|
-
//
|
|
18
|
-
//
|
|
19
|
-
// concept should be a file with that concept in it, and the exports below are
|
|
20
|
-
// named individually so an application still ships only the checks it calls.
|
|
19
|
+
// `result.value.age` is a `number` and nobody wrote that down. The schema is
|
|
20
|
+
// the only description of the account that exists, and both the type the form
|
|
21
|
+
// collects and the type the application uses are read off it — that is the
|
|
22
|
+
// whole promise of this package, and `infer.js` is where it is kept.
|
|
21
23
|
//
|
|
22
|
-
//
|
|
24
|
+
// Ordinary Flow-typed JavaScript with no native binding, so it behaves
|
|
25
|
+
// identically on Node.js, Deno and Bun, and every builder is a separate named
|
|
26
|
+
// export so an application ships only the checks it calls.
|
|
23
27
|
//
|
|
24
|
-
//
|
|
25
|
-
//
|
|
28
|
+
// # The four decisions everything else follows from
|
|
29
|
+
//
|
|
30
|
+
// **A schema is a closure, plus a description of itself.** No class, no
|
|
31
|
+
// registry, no interpreter walking a description at run time. The engine that
|
|
26
32
|
// validates a `string()` *is* the four-line closure `string()` returned, which
|
|
27
|
-
// a JIT can inline into the object parser that calls it.
|
|
28
|
-
//
|
|
29
|
-
//
|
|
33
|
+
// a JIT can inline into the object parser that calls it. The description is a
|
|
34
|
+
// thunk beside it, allocated only when something asks — and it is what makes
|
|
35
|
+
// `toJsonSchema` possible at all, because a closure cannot be read.
|
|
36
|
+
// `schema.js`.
|
|
30
37
|
//
|
|
31
|
-
//
|
|
38
|
+
// **An issue says where it happened.** `["users", "2", "email"]`, as segments,
|
|
39
|
+
// not a string somebody has to parse back apart. A form binds errors to
|
|
40
|
+
// fields, and a field is a path. `issue.js`.
|
|
32
41
|
//
|
|
33
|
-
//
|
|
34
|
-
//
|
|
35
|
-
//
|
|
36
|
-
//
|
|
37
|
-
// and popped as the walk descends, and only an actual issue copies it. A
|
|
38
|
-
// thousand-row payload that validates cleanly allocates no paths at all.
|
|
42
|
+
// **Every branch of the value is visited.** A bad third row does not hide a
|
|
43
|
+
// bad seventh one; one parse reports both, because the alternative is two
|
|
44
|
+
// round trips for information that was available at once. `collection.js`,
|
|
45
|
+
// `object.js`.
|
|
39
46
|
//
|
|
40
|
-
//
|
|
47
|
+
// **Asynchrony is decided when the schema is built, not when it runs.** A
|
|
48
|
+
// schema containing a `checkAsync` is asynchronous from there up, so
|
|
49
|
+
// `safeParse` refuses it with a message instead of returning a promise where
|
|
50
|
+
// its caller expected a result. `schema.js` sets out the alternative and what
|
|
51
|
+
// it would cost every synchronous parse.
|
|
41
52
|
//
|
|
42
|
-
//
|
|
43
|
-
//
|
|
44
|
-
//
|
|
45
|
-
//
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
)
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
export
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
typeof value === "string" ? ok(value) : failIssue("type", "expected string", path),
|
|
202
|
-
});
|
|
203
|
-
}
|
|
204
|
-
|
|
205
|
-
/**
|
|
206
|
-
* A finite number.
|
|
207
|
-
*
|
|
208
|
-
* `NaN` and the infinities are rejected. They are numbers to `typeof` and
|
|
209
|
-
* disasters to arithmetic, and a validator that lets `NaN` through has not
|
|
210
|
-
* validated anything — every comparison downstream silently answers `false`.
|
|
211
|
-
*/
|
|
212
|
-
export function number(): Schema<number> {
|
|
213
|
-
return makeSchema({
|
|
214
|
-
parse: (value, path) =>
|
|
215
|
-
typeof value === "number" && Number.isFinite(value)
|
|
216
|
-
? ok(value)
|
|
217
|
-
: failIssue("type", "expected number", path),
|
|
218
|
-
});
|
|
219
|
-
}
|
|
220
|
-
|
|
221
|
-
export function boolean(): Schema<boolean> {
|
|
222
|
-
return makeSchema({
|
|
223
|
-
parse: (value, path) =>
|
|
224
|
-
typeof value === "boolean" ? ok(value) : failIssue("type", "expected boolean", path),
|
|
225
|
-
});
|
|
226
|
-
}
|
|
227
|
-
|
|
228
|
-
export function unknown(): Schema<mixed> {
|
|
229
|
-
return makeSchema({ parse: (value) => ok(value) });
|
|
230
|
-
}
|
|
231
|
-
|
|
232
|
-
export function literal<T extends string | number | boolean | null>(expected: T): Schema<T> {
|
|
233
|
-
return makeSchema({
|
|
234
|
-
parse: (value, path) =>
|
|
235
|
-
value === expected
|
|
236
|
-
? ok(expected)
|
|
237
|
-
: failIssue("literal", `expected ${String(expected)}`, path),
|
|
238
|
-
});
|
|
239
|
-
}
|
|
240
|
-
|
|
241
|
-
export function enum_<T extends string>(values: $ReadOnlyArray<T>): Schema<T> {
|
|
242
|
-
const message = `expected one of ${values.join(", ")}`;
|
|
243
|
-
return makeSchema({
|
|
244
|
-
parse: (value, path) => {
|
|
245
|
-
for (const option of values) {
|
|
246
|
-
if (value === option) {
|
|
247
|
-
return ok(option);
|
|
248
|
-
}
|
|
249
|
-
}
|
|
250
|
-
return failIssue("enum", message, path);
|
|
251
|
-
},
|
|
252
|
-
});
|
|
253
|
-
}
|
|
254
|
-
|
|
255
|
-
export function array<Item>(item: Schema<Item>): Schema<$ReadOnlyArray<Item>> {
|
|
256
|
-
return makeSchema({
|
|
257
|
-
parse: (value, path) => {
|
|
258
|
-
if (!Array.isArray(value)) {
|
|
259
|
-
return failIssue("type", "expected array", path);
|
|
260
|
-
}
|
|
261
|
-
const out: Array<Item> = [];
|
|
262
|
-
let issues: null | Array<Issue> = null;
|
|
263
|
-
for (let index = 0; index < value.length; index += 1) {
|
|
264
|
-
const result = parseAt(item, value[index], path, String(index));
|
|
265
|
-
if (result.ok) {
|
|
266
|
-
out.push(result.value);
|
|
267
|
-
} else {
|
|
268
|
-
issues = issues ?? [];
|
|
269
|
-
mergeIssues(issues, result);
|
|
270
|
-
}
|
|
271
|
-
}
|
|
272
|
-
return issues == null ? ok(out) : { ok: false, issues };
|
|
273
|
-
},
|
|
274
|
-
});
|
|
275
|
-
}
|
|
276
|
-
|
|
277
|
-
export function tuple<TItems extends $ReadOnlyArray<Schema<mixed>>>(
|
|
278
|
-
items: TItems,
|
|
279
|
-
): Schema<$ReadOnlyArray<mixed>> {
|
|
280
|
-
const arity = items.length;
|
|
281
|
-
return makeSchema({
|
|
282
|
-
parse: (value, path) => {
|
|
283
|
-
if (!Array.isArray(value)) {
|
|
284
|
-
return failIssue("type", "expected tuple", path);
|
|
285
|
-
}
|
|
286
|
-
if (value.length !== arity) {
|
|
287
|
-
return failIssue("length", `expected ${String(arity)} tuple items`, path);
|
|
288
|
-
}
|
|
289
|
-
const out: Array<mixed> = [];
|
|
290
|
-
let issues: null | Array<Issue> = null;
|
|
291
|
-
for (let index = 0; index < arity; index += 1) {
|
|
292
|
-
const result = parseAt(items[index], value[index], path, String(index));
|
|
293
|
-
if (result.ok) {
|
|
294
|
-
out.push(result.value);
|
|
295
|
-
} else {
|
|
296
|
-
issues = issues ?? [];
|
|
297
|
-
mergeIssues(issues, result);
|
|
298
|
-
}
|
|
299
|
-
}
|
|
300
|
-
return issues == null ? ok(out) : { ok: false, issues };
|
|
301
|
-
},
|
|
302
|
-
});
|
|
303
|
-
}
|
|
304
|
-
|
|
305
|
-
/**
|
|
306
|
-
* An object whose keys are not known ahead of time.
|
|
307
|
-
*
|
|
308
|
-
* Only own enumerable keys are read, so a payload carrying `__proto__` or
|
|
309
|
-
* `constructor` cannot smuggle an inherited value into the parsed result.
|
|
310
|
-
*/
|
|
311
|
-
export function record<Value>(value: Schema<Value>): Schema<{ readonly [string]: Value, ... }> {
|
|
312
|
-
return makeSchema({
|
|
313
|
-
parse: (input, path) => {
|
|
314
|
-
if (!isPlainObject(input)) {
|
|
315
|
-
return failIssue("type", "expected object", path);
|
|
316
|
-
}
|
|
317
|
-
const source = plainRecord(input);
|
|
318
|
-
const out: { [string]: Value, ... } = {};
|
|
319
|
-
let issues: null | Array<Issue> = null;
|
|
320
|
-
for (const key of Object.keys(source)) {
|
|
321
|
-
const result = parseAt(value, source[key], path, key);
|
|
322
|
-
if (result.ok) {
|
|
323
|
-
put(out, key, result.value);
|
|
324
|
-
} else {
|
|
325
|
-
issues = issues ?? [];
|
|
326
|
-
mergeIssues(issues, result);
|
|
327
|
-
}
|
|
328
|
-
}
|
|
329
|
-
// $FlowFixMe[incompatible-type] every retained key went through `value`.
|
|
330
|
-
return issues == null ? ok(out) : { ok: false, issues };
|
|
331
|
-
},
|
|
332
|
-
});
|
|
333
|
-
}
|
|
334
|
-
|
|
335
|
-
/**
|
|
336
|
-
* The first schema that accepts the value.
|
|
337
|
-
*
|
|
338
|
-
* When none do, every branch's issues are reported, because there is no way to
|
|
339
|
-
* know which branch the author meant. That is also why [`variant`] exists: a
|
|
340
|
-
* discriminated union can know, and its errors say one useful thing instead of
|
|
341
|
-
* every possible thing.
|
|
342
|
-
*/
|
|
343
|
-
export function union<T>(schemas: $ReadOnlyArray<Schema<T>>): Schema<T> {
|
|
344
|
-
return makeSchema({
|
|
345
|
-
parse: (value, path) => {
|
|
346
|
-
const issues: Array<Issue> = [];
|
|
347
|
-
for (const schema of schemas) {
|
|
348
|
-
const result = parseInternal(schema, value, path);
|
|
349
|
-
if (result.ok) {
|
|
350
|
-
return result;
|
|
351
|
-
}
|
|
352
|
-
mergeIssues(issues, result);
|
|
353
|
-
}
|
|
354
|
-
return { ok: false, issues };
|
|
355
|
-
},
|
|
356
|
-
});
|
|
357
|
-
}
|
|
358
|
-
|
|
359
|
-
/**
|
|
360
|
-
* A union chosen by the value of one key.
|
|
361
|
-
*
|
|
362
|
-
* The discriminant is read first and the matching branch is the only one run,
|
|
363
|
-
* so an invalid `{ kind: "circle", radius: "big" }` reports `expected number
|
|
364
|
-
* at radius` rather than every reason it is not a square, a triangle and a
|
|
365
|
-
* line as well. Unmatched discriminants name the ones that exist.
|
|
366
|
-
*/
|
|
367
|
-
export function variant<T>(
|
|
368
|
-
key: string,
|
|
369
|
-
branches: { readonly [string]: Schema<T>, ... },
|
|
370
|
-
): Schema<T> {
|
|
371
|
-
const known = Object.keys(branches);
|
|
372
|
-
return makeSchema({
|
|
373
|
-
parse: (value, path) => {
|
|
374
|
-
if (!isPlainObject(value)) {
|
|
375
|
-
return failIssue("type", "expected object", path);
|
|
376
|
-
}
|
|
377
|
-
const discriminant = plainRecord(value)[key];
|
|
378
|
-
if (typeof discriminant !== "string" || !Object.hasOwn(branches, discriminant)) {
|
|
379
|
-
path.push(key);
|
|
380
|
-
const failed = failIssue("variant", `expected one of ${known.join(", ")}`, path);
|
|
381
|
-
path.pop();
|
|
382
|
-
return failed;
|
|
383
|
-
}
|
|
384
|
-
return parseInternal(branches[discriminant], value, path);
|
|
385
|
-
},
|
|
386
|
-
});
|
|
387
|
-
}
|
|
388
|
-
|
|
389
|
-
/**
|
|
390
|
-
* Pairs of `[key, schema]`, resolved once when the object schema is built.
|
|
391
|
-
*
|
|
392
|
-
* `for (const key in shape)` on every parse walks the prototype chain and
|
|
393
|
-
* re-reads the same descriptors for the life of the process. The shape cannot
|
|
394
|
-
* change after construction, so the walk belongs at construction.
|
|
395
|
-
*/
|
|
396
|
-
function shapeEntries(shape: Shape): $ReadOnlyArray<[string, Schema<mixed>]> {
|
|
397
|
-
return Object.keys(shape).map((key) => [key, shape[key]]);
|
|
398
|
-
}
|
|
399
|
-
|
|
400
|
-
function schemaObject<T extends { ... }>(shape: Shape): Schema<T> {
|
|
401
|
-
const entries = shapeEntries(shape);
|
|
402
|
-
return makeSchema({
|
|
403
|
-
parse: (value, path) => {
|
|
404
|
-
if (!isPlainObject(value)) {
|
|
405
|
-
return failIssue("type", "expected object", path);
|
|
406
|
-
}
|
|
407
|
-
const record = plainRecord(value);
|
|
408
|
-
const out: { [string]: mixed, ... } = {};
|
|
409
|
-
let issues: null | Array<Issue> = null;
|
|
410
|
-
for (const [key, schema] of entries) {
|
|
411
|
-
const result = parseAt(schema, record[key], path, key);
|
|
412
|
-
if (result.ok) {
|
|
413
|
-
put(out, key, result.value);
|
|
414
|
-
} else {
|
|
415
|
-
issues = issues ?? [];
|
|
416
|
-
mergeIssues(issues, result);
|
|
417
|
-
}
|
|
418
|
-
}
|
|
419
|
-
// $FlowFixMe[incompatible-type] shape parsers have constructed every output field.
|
|
420
|
-
return issues == null ? ok(out as T) : { ok: false, issues };
|
|
421
|
-
},
|
|
422
|
-
});
|
|
423
|
-
}
|
|
424
|
-
|
|
425
|
-
/**
|
|
426
|
-
* An object that rejects keys the shape does not name.
|
|
427
|
-
*
|
|
428
|
-
* `object()` drops unknown keys silently, which is right for a tolerant reader
|
|
429
|
-
* of someone else's payload. `strictObject()` is for the other case — a
|
|
430
|
-
* configuration file, an internal API — where an unrecognised key is almost
|
|
431
|
-
* always a typo the user would rather hear about than have ignored.
|
|
432
|
-
*/
|
|
433
|
-
export function strictObject<T extends { ... }>(shape: Shape): Schema<T> {
|
|
434
|
-
const inner = schemaObject<T>(shape);
|
|
435
|
-
const allowed = new Set(Object.keys(shape));
|
|
436
|
-
return makeSchema({
|
|
437
|
-
parse: (value, path) => {
|
|
438
|
-
const result = parseInternal(inner, value, path);
|
|
439
|
-
if (!isPlainObject(value)) {
|
|
440
|
-
return result;
|
|
441
|
-
}
|
|
442
|
-
|
|
443
|
-
// The unknown-key scan runs whether or not the fields parsed. Returning
|
|
444
|
-
// early on a field failure meant `{ name: 1, extra: true }` reported the
|
|
445
|
-
// wrong type of `name` and said nothing about `extra`, so fixing the
|
|
446
|
-
// first error revealed the second — which is the whole reason this
|
|
447
|
-
// validator collects issues instead of stopping at one.
|
|
448
|
-
const issues: Array<Issue> = [];
|
|
449
|
-
if (!result.ok) {
|
|
450
|
-
mergeIssues(issues, result);
|
|
451
|
-
}
|
|
452
|
-
for (const key of Object.keys(plainRecord(value))) {
|
|
453
|
-
if (!allowed.has(key)) {
|
|
454
|
-
path.push(key);
|
|
455
|
-
issues.push(issue("unknown_key", `unexpected key ${key}`, path));
|
|
456
|
-
path.pop();
|
|
457
|
-
}
|
|
458
|
-
}
|
|
459
|
-
return issues.length === 0 ? result : { ok: false, issues };
|
|
460
|
-
},
|
|
461
|
-
});
|
|
462
|
-
}
|
|
463
|
-
|
|
464
|
-
export function partial<T extends { ... }>(shape: Shape): Schema<Partial<T>> {
|
|
465
|
-
const partialShape: { [string]: Schema<mixed>, ... } = {};
|
|
466
|
-
for (const key of Object.keys(shape)) {
|
|
467
|
-
partialShape[key] = optional(shape[key]);
|
|
468
|
-
}
|
|
469
|
-
return schemaObject<Partial<T>>(partialShape);
|
|
470
|
-
}
|
|
471
|
-
|
|
472
|
-
export { schemaObject as object };
|
|
473
|
-
|
|
474
|
-
/**
|
|
475
|
-
* A schema built on first use.
|
|
476
|
-
*
|
|
477
|
-
* The one way to write a recursive type: a comment tree cannot name its own
|
|
478
|
-
* schema in its own initialiser, but it can name a function that returns it.
|
|
479
|
-
* The result is memoised, so recursion costs one closure, not one per node.
|
|
480
|
-
*/
|
|
481
|
-
export function lazy<T>(build: () => Schema<T>): Schema<T> {
|
|
482
|
-
let built: null | Schema<T> = null;
|
|
483
|
-
return makeSchema({
|
|
484
|
-
parse: (value, path) => {
|
|
485
|
-
if (built == null) {
|
|
486
|
-
built = build();
|
|
487
|
-
}
|
|
488
|
-
return parseInternal(built, value, path);
|
|
489
|
-
},
|
|
490
|
-
});
|
|
491
|
-
}
|
|
492
|
-
|
|
493
|
-
export function optional<T>(schema: Schema<T>): Schema<void | T> {
|
|
494
|
-
return makeSchema({
|
|
495
|
-
parse: (value, path) =>
|
|
496
|
-
value === undefined ? ok(undefined) : parseInternal(schema, value, path),
|
|
497
|
-
});
|
|
498
|
-
}
|
|
499
|
-
|
|
500
|
-
export function nullable<T>(schema: Schema<T>): Schema<null | T> {
|
|
501
|
-
return makeSchema({
|
|
502
|
-
parse: (value, path) => (value === null ? ok(null) : parseInternal(schema, value, path)),
|
|
503
|
-
});
|
|
504
|
-
}
|
|
505
|
-
|
|
506
|
-
/**
|
|
507
|
-
* A schema that never fails, substituting `value` when the inner one does.
|
|
508
|
-
*
|
|
509
|
-
* For the boundary where a bad field should not sink the whole payload — a
|
|
510
|
-
* cached response, a user preference — and where the alternative is a
|
|
511
|
-
* `safeParse` and a hand-written `if` at every call site.
|
|
512
|
-
*/
|
|
513
|
-
export function fallback<T>(schema: Schema<T>, value: T): Schema<T> {
|
|
514
|
-
return makeSchema({
|
|
515
|
-
parse: (input, path) => {
|
|
516
|
-
const result = parseInternal(schema, input, path);
|
|
517
|
-
return result.ok ? result : ok(value);
|
|
518
|
-
},
|
|
519
|
-
});
|
|
520
|
-
}
|
|
521
|
-
|
|
522
|
-
/**
|
|
523
|
-
* Apply steps to a schema, left to right.
|
|
524
|
-
*
|
|
525
|
-
* Variadic, because refinement is cumulative in practice — an email is a
|
|
526
|
-
* string that is long enough *and* looks like an address — and forcing
|
|
527
|
-
* `pipe(pipe(pipe(...)))` for that is a tax on the common case.
|
|
528
|
-
*/
|
|
529
|
-
export function pipe<A>(schema: Schema<A>, ...steps: $ReadOnlyArray<Step<any, any>>): Schema<any> {
|
|
530
|
-
let piped: Schema<any> = schema;
|
|
531
|
-
for (const step of steps) {
|
|
532
|
-
piped = step(piped);
|
|
533
|
-
}
|
|
534
|
-
return piped;
|
|
535
|
-
}
|
|
536
|
-
|
|
537
|
-
export function transform<A, B>(change: (value: A) => B): Step<A, B> {
|
|
538
|
-
return (schema) =>
|
|
539
|
-
makeSchema({
|
|
540
|
-
parse: (value, path) => {
|
|
541
|
-
const result = parseInternal(schema, value, path);
|
|
542
|
-
if (!result.ok) {
|
|
543
|
-
return result;
|
|
544
|
-
}
|
|
545
|
-
return ok(change(result.value));
|
|
546
|
-
},
|
|
547
|
-
});
|
|
548
|
-
}
|
|
549
|
-
|
|
550
|
-
/**
|
|
551
|
-
* An arbitrary predicate, with the message it should report.
|
|
552
|
-
*
|
|
553
|
-
* Every other step in this module is a special case of this one. It exists so
|
|
554
|
-
* that a rule the library did not anticipate — a checksum, a business rule,
|
|
555
|
-
* one field agreeing with another — is a one-liner rather than a reason to
|
|
556
|
-
* abandon the schema and hand-roll validation.
|
|
557
|
-
*/
|
|
558
|
-
export function check<T>(predicate: (value: T) => boolean, message: string): Step<T, T> {
|
|
559
|
-
return (schema) => refine(schema, predicate, "check", message);
|
|
560
|
-
}
|
|
561
|
-
|
|
562
|
-
/**
|
|
563
|
-
* A nominal marker on an otherwise ordinary value.
|
|
564
|
-
*
|
|
565
|
-
* It does not check anything at runtime, and it is not pretending to: the
|
|
566
|
-
* point is the name in the schema, so that `UserId` and `PostId` read
|
|
567
|
-
* differently at the call site even though both are strings underneath.
|
|
568
|
-
*/
|
|
569
|
-
export function brand<T, Name extends string>(name: Name): Step<T, T> {
|
|
570
|
-
return (schema) => refine(schema, () => true, "brand", `expected brand ${name}`);
|
|
571
|
-
}
|
|
572
|
-
|
|
573
|
-
export function minLength(value: number): Step<string, string> {
|
|
574
|
-
return (schema) =>
|
|
575
|
-
refine(
|
|
576
|
-
schema,
|
|
577
|
-
(input) => input.length >= value,
|
|
578
|
-
"min_length",
|
|
579
|
-
`expected at least ${String(value)} characters`,
|
|
580
|
-
);
|
|
581
|
-
}
|
|
582
|
-
|
|
583
|
-
export function maxLength(value: number): Step<string, string> {
|
|
584
|
-
return (schema) =>
|
|
585
|
-
refine(
|
|
586
|
-
schema,
|
|
587
|
-
(input) => input.length <= value,
|
|
588
|
-
"max_length",
|
|
589
|
-
`expected at most ${String(value)} characters`,
|
|
590
|
-
);
|
|
591
|
-
}
|
|
592
|
-
|
|
593
|
-
export function startsWith(value: string): Step<string, string> {
|
|
594
|
-
return (schema) =>
|
|
595
|
-
refine(schema, (input) => input.startsWith(value), "starts_with", `expected prefix ${value}`);
|
|
596
|
-
}
|
|
597
|
-
|
|
598
|
-
export function endsWith(value: string): Step<string, string> {
|
|
599
|
-
return (schema) =>
|
|
600
|
-
refine(schema, (input) => input.endsWith(value), "ends_with", `expected suffix ${value}`);
|
|
601
|
-
}
|
|
602
|
-
|
|
603
|
-
/**
|
|
604
|
-
* A string matching `pattern`.
|
|
605
|
-
*
|
|
606
|
-
* The pattern is tested with `RegExp.prototype.test` against a fresh `lastIndex`
|
|
607
|
-
* every time, because a caller who reaches for `/g` would otherwise get a
|
|
608
|
-
* schema that alternates between accepting and rejecting the same input.
|
|
609
|
-
*/
|
|
610
|
-
export function regex(pattern: RegExp, message?: string): Step<string, string> {
|
|
611
|
-
return (schema) =>
|
|
612
|
-
refine(
|
|
613
|
-
schema,
|
|
614
|
-
(input) => {
|
|
615
|
-
pattern.lastIndex = 0;
|
|
616
|
-
return pattern.test(input);
|
|
617
|
-
},
|
|
618
|
-
"regex",
|
|
619
|
-
message ?? `expected a match for ${String(pattern)}`,
|
|
620
|
-
);
|
|
621
|
-
}
|
|
622
|
-
|
|
623
|
-
export function trim(): Step<string, string> {
|
|
624
|
-
return transform((input: string) => input.trim());
|
|
625
|
-
}
|
|
626
|
-
|
|
627
|
-
export function email(): Step<string, string> {
|
|
628
|
-
return (schema) =>
|
|
629
|
-
refine(schema, (input) => /.+@.+\..+/.test(input), "email", "expected email address");
|
|
630
|
-
}
|
|
631
|
-
|
|
632
|
-
export function min(value: number): Step<number, number> {
|
|
633
|
-
return (schema) =>
|
|
634
|
-
refine(schema, (input) => input >= value, "min", `expected at least ${String(value)}`);
|
|
635
|
-
}
|
|
636
|
-
|
|
637
|
-
export function max(value: number): Step<number, number> {
|
|
638
|
-
return (schema) =>
|
|
639
|
-
refine(schema, (input) => input <= value, "max", `expected at most ${String(value)}`);
|
|
640
|
-
}
|
|
641
|
-
|
|
642
|
-
export function integer(): Step<number, number> {
|
|
643
|
-
return (schema) => refine(schema, Number.isInteger, "integer", "expected an integer");
|
|
644
|
-
}
|
|
645
|
-
|
|
646
|
-
export function date(): Schema<Date> {
|
|
647
|
-
return makeSchema({
|
|
648
|
-
parse: (value, path) =>
|
|
649
|
-
value instanceof Date && Number.isFinite(value.getTime())
|
|
650
|
-
? ok(value)
|
|
651
|
-
: failIssue("type", "expected Date", path),
|
|
652
|
-
});
|
|
653
|
-
}
|
|
654
|
-
|
|
655
|
-
export function instance<T>(ClassValue: Class<T>): Schema<T> {
|
|
656
|
-
return makeSchema({
|
|
657
|
-
parse: (value, path) =>
|
|
658
|
-
value instanceof ClassValue ? ok(value as T) : failIssue("type", "expected instance", path),
|
|
659
|
-
});
|
|
660
|
-
}
|
|
661
|
-
|
|
662
|
-
/** Parse, or raise a [`ValidationError`] carrying every issue found. */
|
|
663
|
-
export function parse<T>(schema: Schema<T>, value: mixed): T {
|
|
664
|
-
const result = safeParse(schema, value);
|
|
665
|
-
if (result.ok) {
|
|
666
|
-
return result.value;
|
|
667
|
-
}
|
|
668
|
-
throw new ValidationError(result.issues);
|
|
669
|
-
}
|
|
670
|
-
|
|
671
|
-
/** Parse into a result, so failure is a value rather than control flow. */
|
|
672
|
-
/**
|
|
673
|
-
* A schema as a standalone function.
|
|
674
|
-
*
|
|
675
|
-
* `safeParse(schema, value)` needs both halves at the call site, which is fine
|
|
676
|
-
* where the schema is in scope and useless where it is not — a boundary that
|
|
677
|
-
* wants to validate what arrives takes a *function*, not a schema and an
|
|
678
|
-
* import of this module. `parser(User)` is that function, and it is why
|
|
679
|
-
* `@uniflowed/fetch` can check a response body without depending on the
|
|
680
|
-
* validator at all.
|
|
681
|
-
*/
|
|
682
|
-
export function parser<T>(schema: Schema<T>): (value: mixed) => Result<T> {
|
|
683
|
-
return (value: mixed) => safeParse(schema, value);
|
|
684
|
-
}
|
|
685
|
-
|
|
686
|
-
export function safeParse<T>(schema: Schema<T>, value: mixed): Result<T> {
|
|
687
|
-
return parseInternal(schema, value, []);
|
|
688
|
-
}
|
|
689
|
-
|
|
690
|
-
/**
|
|
691
|
-
* Validate a value during render.
|
|
692
|
-
*
|
|
693
|
-
* A hook rather than a plain call so the React Compiler memoises it with the
|
|
694
|
-
* rest of the component: re-rendering for an unrelated reason does not re-walk
|
|
695
|
-
* the payload.
|
|
696
|
-
*/
|
|
697
|
-
export hook useValidation<T>(schema: Schema<T>, value: mixed): Result<T> {
|
|
698
|
-
return safeParse(schema, value);
|
|
699
|
-
}
|
|
700
|
-
|
|
701
|
-
/**
|
|
702
|
-
* Every builder under one name, for callers who prefer `v.string()`.
|
|
703
|
-
*
|
|
704
|
-
* The named exports are the primary surface — they are what tree-shakes — and
|
|
705
|
-
* this is the convenience alias, typed by `typeof` so the two can never drift.
|
|
706
|
-
*/
|
|
707
|
-
export const v: {
|
|
708
|
-
readonly string: typeof string,
|
|
709
|
-
readonly number: typeof number,
|
|
710
|
-
readonly boolean: typeof boolean,
|
|
711
|
-
readonly unknown: typeof unknown,
|
|
712
|
-
readonly literal: typeof literal,
|
|
713
|
-
readonly enum: typeof enum_,
|
|
714
|
-
readonly array: typeof array,
|
|
715
|
-
readonly tuple: typeof tuple,
|
|
716
|
-
readonly record: typeof record,
|
|
717
|
-
readonly union: typeof union,
|
|
718
|
-
readonly variant: typeof variant,
|
|
719
|
-
readonly object: typeof schemaObject,
|
|
720
|
-
readonly strictObject: typeof strictObject,
|
|
721
|
-
readonly partial: typeof partial,
|
|
722
|
-
readonly lazy: typeof lazy,
|
|
723
|
-
readonly optional: typeof optional,
|
|
724
|
-
readonly nullable: typeof nullable,
|
|
725
|
-
readonly fallback: typeof fallback,
|
|
726
|
-
readonly pipe: typeof pipe,
|
|
727
|
-
readonly transform: typeof transform,
|
|
728
|
-
readonly check: typeof check,
|
|
729
|
-
readonly brand: typeof brand,
|
|
730
|
-
readonly minLength: typeof minLength,
|
|
731
|
-
readonly maxLength: typeof maxLength,
|
|
732
|
-
readonly startsWith: typeof startsWith,
|
|
733
|
-
readonly endsWith: typeof endsWith,
|
|
734
|
-
readonly regex: typeof regex,
|
|
735
|
-
readonly trim: typeof trim,
|
|
736
|
-
readonly email: typeof email,
|
|
737
|
-
readonly min: typeof min,
|
|
738
|
-
readonly max: typeof max,
|
|
739
|
-
readonly integer: typeof integer,
|
|
740
|
-
readonly date: typeof date,
|
|
741
|
-
readonly instance: typeof instance,
|
|
742
|
-
readonly parse: typeof parse,
|
|
743
|
-
readonly safeParse: typeof safeParse,
|
|
744
|
-
readonly useValidation: typeof useValidation,
|
|
745
|
-
} = {
|
|
746
|
-
string,
|
|
747
|
-
number,
|
|
53
|
+
// # How the package is laid out
|
|
54
|
+
//
|
|
55
|
+
// Fifteen modules beside this one. Nothing is under an `internal/`: each is a
|
|
56
|
+
// reasonable thing to import on purpose, and every one is reachable through a
|
|
57
|
+
// subpath so an application that only needs `parse` and `object` can say so.
|
|
58
|
+
//
|
|
59
|
+
// The leaves, which know nothing about schemas:
|
|
60
|
+
//
|
|
61
|
+
// - `plain-object.js` — reading and writing an object whose keys came from
|
|
62
|
+
// outside, without touching its prototype. The package's whole answer to
|
|
63
|
+
// `{"__proto__": …}`, in one place so that it is one answer.
|
|
64
|
+
// - `issue.js` — what a failure is, where it happened, and the error `parse`
|
|
65
|
+
// throws.
|
|
66
|
+
//
|
|
67
|
+
// The kernel:
|
|
68
|
+
//
|
|
69
|
+
// - `schema.js` — what a schema *is*: the opaque type, the two-function
|
|
70
|
+
// kernel, the description it carries, and the walk that runs one. Read this
|
|
71
|
+
// first.
|
|
72
|
+
// - `infer.js` — reading a value's type off its schema. Types only; it
|
|
73
|
+
// compiles to nothing.
|
|
74
|
+
//
|
|
75
|
+
// The vocabulary, one module per kind of thing a schema can be:
|
|
76
|
+
//
|
|
77
|
+
// - `primitive.js` — the leaves: `string`, `number`, `literal`, `enum_`, and
|
|
78
|
+
// the rest of what uf will recognise without being told how.
|
|
79
|
+
// - `object.js` — keys known when the schema was written, in the three
|
|
80
|
+
// flavours that differ only in what happens to an unknown key.
|
|
81
|
+
// - `collection.js` — arrays, tuples, records, maps and sets: containers whose
|
|
82
|
+
// contents are only known when the value arrives.
|
|
83
|
+
// - `union.js` — several schemas over one value: `union`, the discriminated
|
|
84
|
+
// `variant` that gives an error worth reading, and `intersect`.
|
|
85
|
+
// - `optional.js` — a value that might not be there, and what to put there
|
|
86
|
+
// when it is not.
|
|
87
|
+
// - `lazy.js` — a schema that does not exist yet, which is how a comment tree
|
|
88
|
+
// is spelled.
|
|
89
|
+
//
|
|
90
|
+
// The pipeline:
|
|
91
|
+
//
|
|
92
|
+
// - `pipe.js` — a schema with steps after it, the `Step` type, and the
|
|
93
|
+
// overload table that carries the output type through a `transform`.
|
|
94
|
+
// - `action.js` — the steps that come ready-made, each with the constraint
|
|
95
|
+
// that makes it visible to an exporter.
|
|
96
|
+
//
|
|
97
|
+
// The edges:
|
|
98
|
+
//
|
|
99
|
+
// - `parse.js` — the four ways to run a schema, and why there are four.
|
|
100
|
+
// - `json-schema.js` — a schema as a document somebody else can read, and an
|
|
101
|
+
// honest list of what JSON Schema could not say.
|
|
102
|
+
// - `namespace.js` — `v`, the alias that holds every builder. Separate because
|
|
103
|
+
// it is the one module that has to import all of them, and an entry point
|
|
104
|
+
// that did that would make every application carry every check.
|
|
105
|
+
//
|
|
106
|
+
// # Readiness
|
|
107
|
+
//
|
|
108
|
+
// **Implemented and tested.** The schema vocabulary above, including
|
|
109
|
+
// discriminated unions, intersections, recursive schemas, maps and sets;
|
|
110
|
+
// issue paths through every composite; both entry points in both synchronous
|
|
111
|
+
// and asynchronous forms; `InferInput` and `InferOutput` over objects, shapes,
|
|
112
|
+
// tuples, unions, variants and pipelines; a `pipe` that changes the output
|
|
113
|
+
// type with the change surviving into the inferred type; `toJsonSchema` with
|
|
114
|
+
// `$defs` for recursion and a reported list of what it could not express.
|
|
115
|
+
// `tests/library/validator.test.js` covers each of those, including
|
|
116
|
+
// `@uniflowed/form`'s resolver over both a synchronous and an asynchronous
|
|
117
|
+
// schema, and `tests/library/form.test.js` covers that resolver inside a real
|
|
118
|
+
// form.
|
|
119
|
+
//
|
|
120
|
+
// **Experimental.** [`describe`] and the [`Description`] type. The shape is
|
|
121
|
+
// right for `json-schema.js` and it is the shape a code generator should read,
|
|
122
|
+
// but no generator exists yet to prove the second half — see below — so the
|
|
123
|
+
// type may gain cases before it is stable.
|
|
124
|
+
//
|
|
125
|
+
// **Not implemented, deliberately.**
|
|
126
|
+
//
|
|
127
|
+
// - `pick`, `omit` and `required` over a *schema*. These take a shape here or
|
|
128
|
+
// not at all, and `object.js` says why: a built schema is a closure and a
|
|
129
|
+
// description, not a reified field list.
|
|
130
|
+
// - A nominal `brand`. `brand("UserId")` is a name in the description and
|
|
131
|
+
// nothing to the checker, because Flow's opaque types are declared in a
|
|
132
|
+
// module and cannot be produced by a call. `infer.js` says what to write
|
|
133
|
+
// instead when the distinction has to be enforced.
|
|
134
|
+
// - Typed field paths. Flow has no template-literal types; an `Issue`'s `path`
|
|
135
|
+
// is `$ReadOnlyArray<string>` and `@uniflowed/form` makes the same call for
|
|
136
|
+
// the same reason.
|
|
137
|
+
// - The long tail of format checks — `creditCard`, `emoji`, `mac`, `cuid2`.
|
|
138
|
+
// `action.js` says why a stale regular expression in a library is worse than
|
|
139
|
+
// a `check` an application owns.
|
|
140
|
+
// - More than eight `pipe` steps in one call. Flow cannot fold a type over a
|
|
141
|
+
// variadic list, so the arities are spelled out; a ninth step is a type
|
|
142
|
+
// error and the fix is to pipe the result of a pipe.
|
|
143
|
+
//
|
|
144
|
+
// **Not implemented, and a gap.** `uf prepare` lists a
|
|
145
|
+
// `GenerateValidatorTypes` step and nothing implements it: no crate reads a
|
|
146
|
+
// schema and writes Flow types or a JSON Schema file to disk. The half that
|
|
147
|
+
// belongs in this package — a description complete enough to generate from —
|
|
148
|
+
// is here and is exercised by `toJsonSchema`. The build-time half is not
|
|
149
|
+
// written, and this package should not grow it: walking a repository's sources
|
|
150
|
+
// is Rust's job under the same rule that puts the formatter and the checker
|
|
151
|
+
// there.
|
|
152
|
+
//
|
|
153
|
+
// # Measured, against Valibot
|
|
154
|
+
//
|
|
155
|
+
// Valibot 1.4.2 from npm, Node 25.8.1, an Apple M2 Max (12 cores, macOS 26.5),
|
|
156
|
+
// best of five timed runs after two warm-up runs, both libraries given the
|
|
157
|
+
// same payload and schemas built out of the same pieces — an object of seven
|
|
158
|
+
// fields with a nested object, an array of strings, and `minLength`,
|
|
159
|
+
// `maxLength`, `integer`, `min` and `max` steps. The two agree on every case
|
|
160
|
+
// the harness runs, which it asserts before it times anything: the same
|
|
161
|
+
// verdict, the same number of issues, the same transformed value.
|
|
162
|
+
//
|
|
163
|
+
// | Workload | uf | Valibot | |
|
|
164
|
+
// | --- | --- | --- | --- |
|
|
165
|
+
// | 1,000-record list, all valid (per record) | **0.49 µs** | 0.65 µs | 1.34x |
|
|
166
|
+
// | 1,000-record list, one field bad in ten | **0.50 µs** | 0.66 µs | 1.34x |
|
|
167
|
+
// | One form object, four fields, one transform | **0.20 µs** | 0.35 µs | 1.73x |
|
|
168
|
+
// | `safeParse(string(), "ada")` | **5.0 ns** | 15.6 ns | 3.1x |
|
|
169
|
+
// | Building the record schema | **0.47 µs** | 6.5 µs | 14x |
|
|
170
|
+
//
|
|
171
|
+
// The harness is not in the repository — it needs Valibot from npm, and this
|
|
172
|
+
// repository installs no dependency it does not ship — so it lives with the
|
|
173
|
+
// change that produced these numbers. It runs as:
|
|
174
|
+
//
|
|
175
|
+
// UF_PROJECT_ROOT=$PWD node --import ./packages/host/register.js \
|
|
176
|
+
// bench-validator.js path/to/valibot/dist/index.cjs
|
|
177
|
+
//
|
|
178
|
+
// The construction column is the one to read first: a Valibot schema is a
|
|
179
|
+
// tree of objects with an `~run` method and metadata on each node, and this
|
|
180
|
+
// package's is a closure. That is fourteen times cheaper to build and it is
|
|
181
|
+
// why the leaf parse is three times cheaper to run.
|
|
182
|
+
//
|
|
183
|
+
// The row that was *worse* is worth recording too, because finding it is what
|
|
184
|
+
// made the others true. Writing every parsed field with
|
|
185
|
+
// `Object.defineProperty` — which is how this package used to keep a
|
|
186
|
+
// `__proto__` key out of the prototype — cost more than the entire rest of the
|
|
187
|
+
// walk: the list workload took 298 µs per hundred records instead of 49, and
|
|
188
|
+
// this package was 2.4 times *slower* than Valibot rather than 1.34 times
|
|
189
|
+
// faster. `plain-object.js` has the one-line fix and the argument that it
|
|
190
|
+
// keeps the guarantee intact.
|
|
191
|
+
|
|
192
|
+
export type { FlatIssues, Issue, Path } from "./issue.js";
|
|
193
|
+
export type { Constraint, Description, Result, Schema } from "./schema.js";
|
|
194
|
+
export type {
|
|
195
|
+
Infer,
|
|
196
|
+
InferInput,
|
|
197
|
+
InferOutput,
|
|
198
|
+
ItemsInput,
|
|
199
|
+
ItemsOutput,
|
|
200
|
+
Options,
|
|
201
|
+
Shape,
|
|
202
|
+
ShapeInput,
|
|
203
|
+
ShapeOutput,
|
|
204
|
+
} from "./infer.js";
|
|
205
|
+
export type { Pipe, Step } from "./pipe.js";
|
|
206
|
+
export type { JsonSchemaExport, JsonSchemaNode, Unrepresentable } from "./json-schema.js";
|
|
207
|
+
|
|
208
|
+
export { ValidationError, flatten } from "./issue.js";
|
|
209
|
+
export { describe, isAsync } from "./schema.js";
|
|
210
|
+
export {
|
|
211
|
+
bigint,
|
|
748
212
|
boolean,
|
|
749
|
-
|
|
213
|
+
custom,
|
|
214
|
+
date,
|
|
215
|
+
enum_,
|
|
216
|
+
instance,
|
|
750
217
|
literal,
|
|
751
|
-
|
|
752
|
-
|
|
753
|
-
|
|
754
|
-
|
|
755
|
-
|
|
756
|
-
|
|
757
|
-
|
|
758
|
-
|
|
759
|
-
|
|
760
|
-
|
|
761
|
-
|
|
762
|
-
|
|
763
|
-
|
|
764
|
-
|
|
765
|
-
|
|
766
|
-
check,
|
|
767
|
-
brand,
|
|
768
|
-
minLength,
|
|
769
|
-
maxLength,
|
|
770
|
-
startsWith,
|
|
218
|
+
never,
|
|
219
|
+
null_,
|
|
220
|
+
number,
|
|
221
|
+
string,
|
|
222
|
+
undefined_,
|
|
223
|
+
unknown,
|
|
224
|
+
} from "./primitive.js";
|
|
225
|
+
export { looseObject, object, partial, strictObject } from "./object.js";
|
|
226
|
+
export { array, map, record, set, tuple } from "./collection.js";
|
|
227
|
+
export { intersect, union, variant } from "./union.js";
|
|
228
|
+
export { fallback, nullable, nullish, optional, withDefault } from "./optional.js";
|
|
229
|
+
export { lazy, lazyAsync } from "./lazy.js";
|
|
230
|
+
export { brand, check, checkAsync, pipe, refine, transform, transformAsync } from "./pipe.js";
|
|
231
|
+
export {
|
|
232
|
+
email,
|
|
771
233
|
endsWith,
|
|
234
|
+
includes,
|
|
235
|
+
integer,
|
|
236
|
+
isoDate,
|
|
237
|
+
length,
|
|
238
|
+
max,
|
|
239
|
+
maxItems,
|
|
240
|
+
maxLength,
|
|
241
|
+
min,
|
|
242
|
+
minItems,
|
|
243
|
+
minLength,
|
|
244
|
+
multipleOf,
|
|
245
|
+
nonEmpty,
|
|
772
246
|
regex,
|
|
247
|
+
startsWith,
|
|
248
|
+
toLowerCase,
|
|
249
|
+
toUpperCase,
|
|
773
250
|
trim,
|
|
774
|
-
|
|
775
|
-
|
|
776
|
-
|
|
777
|
-
|
|
778
|
-
|
|
779
|
-
instance,
|
|
251
|
+
url,
|
|
252
|
+
uuid,
|
|
253
|
+
} from "./action.js";
|
|
254
|
+
export {
|
|
255
|
+
is,
|
|
780
256
|
parse,
|
|
257
|
+
parseAsync,
|
|
258
|
+
parser,
|
|
781
259
|
safeParse,
|
|
260
|
+
safeParseAsync,
|
|
782
261
|
useValidation,
|
|
783
|
-
};
|
|
262
|
+
} from "./parse.js";
|
|
263
|
+
export { toJsonSchema } from "./json-schema.js";
|
|
264
|
+
|
|
265
|
+
export { v } from "./namespace.js";
|