@oxog/vld 2.1.0 → 2.2.7
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/CHANGELOG.md +113 -0
- package/README.md +413 -1235
- package/dist/chunks/errors-core-CerMg2rq.js +1 -0
- package/dist/chunks/index-CkbfQQ4n.js +1 -0
- package/dist/chunks/index-yjpbvFgw.js +1 -0
- package/dist/chunks/ip-validation-g5LWgy9t.js +1 -0
- package/dist/chunks/security-Dy24T8N7.js +1 -0
- package/dist/cjs/chunks/errors-core-DrydF46s.cjs +1 -0
- package/dist/cjs/chunks/index-BEV9nG0N.cjs +1 -0
- package/dist/cjs/chunks/index-ZjHAxYdA.cjs +1 -0
- package/dist/cjs/chunks/ip-validation-BZqPamPC.cjs +1 -0
- package/dist/cjs/chunks/security-aXZVI7qW.cjs +1 -0
- package/dist/cjs/cli/bin.cjs +1 -505
- package/dist/cjs/cli/index.cjs +1 -209
- package/dist/cjs/codecs/index.cjs +1 -45
- package/dist/cjs/coercion/index.cjs +1 -588
- package/dist/cjs/compat/emitter.cjs +1 -184
- package/dist/cjs/compat/result.cjs +1 -189
- package/dist/cjs/errors.cjs +1 -180
- package/dist/cjs/index.cjs +1 -1894
- package/dist/cjs/kernel.cjs +1 -415
- package/dist/cjs/locales/af.cjs +1 -108
- package/dist/cjs/locales/ar.cjs +1 -108
- package/dist/cjs/locales/bn.cjs +1 -108
- package/dist/cjs/locales/da.cjs +1 -108
- package/dist/cjs/locales/de.cjs +1 -108
- package/dist/cjs/locales/en.cjs +1 -108
- package/dist/cjs/locales/es-MX.cjs +1 -107
- package/dist/cjs/locales/es.cjs +1 -96
- package/dist/cjs/locales/fi.cjs +1 -108
- package/dist/cjs/locales/fr.cjs +1 -96
- package/dist/cjs/locales/hi.cjs +1 -108
- package/dist/cjs/locales/id.cjs +1 -108
- package/dist/cjs/locales/index.cjs +1 -43
- package/dist/cjs/locales/it.cjs +1 -108
- package/dist/cjs/locales/ja.cjs +1 -108
- package/dist/cjs/locales/ko.cjs +1 -108
- package/dist/cjs/locales/lazy.cjs +1 -188
- package/dist/cjs/locales/nl.cjs +1 -108
- package/dist/cjs/locales/no.cjs +1 -108
- package/dist/cjs/locales/pl.cjs +1 -108
- package/dist/cjs/locales/pt-BR.cjs +1 -108
- package/dist/cjs/locales/pt.cjs +1 -108
- package/dist/cjs/locales/ru.cjs +1 -108
- package/dist/cjs/locales/runtime.cjs +1 -51
- package/dist/cjs/locales/sv.cjs +1 -108
- package/dist/cjs/locales/sw.cjs +1 -108
- package/dist/cjs/locales/th.cjs +1 -108
- package/dist/cjs/locales/tr.cjs +1 -108
- package/dist/cjs/locales/vi.cjs +1 -108
- package/dist/cjs/locales/zh.cjs +1 -108
- package/dist/cjs/mini.cjs +1 -242
- package/dist/cjs/pigment.cjs +1 -207
- package/dist/cjs/plugins/index.cjs +1 -12
- package/dist/cjs/registry.cjs +1 -37
- package/dist/cjs/v3/index.cjs +1 -425
- package/dist/cjs/v4/core/index.cjs +1 -894
- package/dist/cjs/v4/index.cjs +1 -425
- package/dist/cjs/v4/locales/index.cjs +1 -130
- package/dist/cjs/v4/mini/index.cjs +1 -509
- package/dist/cjs/v4-mini/index.cjs +1 -532
- package/dist/cjs/validators/any.cjs +1 -40
- package/dist/cjs/validators/array.cjs +1 -283
- package/dist/cjs/validators/base.cjs +1 -887
- package/dist/cjs/validators/base64.cjs +1 -73
- package/dist/cjs/validators/bigint.cjs +1 -232
- package/dist/cjs/validators/boolean.cjs +1 -146
- package/dist/cjs/validators/codec.cjs +1 -205
- package/dist/cjs/validators/custom.cjs +1 -71
- package/dist/cjs/validators/date.cjs +1 -278
- package/dist/cjs/validators/discriminated-union.cjs +1 -130
- package/dist/cjs/validators/enum.cjs +1 -154
- package/dist/cjs/validators/file.cjs +1 -130
- package/dist/cjs/validators/function.cjs +1 -63
- package/dist/cjs/validators/hex.cjs +1 -70
- package/dist/cjs/validators/index.cjs +1 -123
- package/dist/cjs/validators/intersection.cjs +1 -120
- package/dist/cjs/validators/json.cjs +1 -70
- package/dist/cjs/validators/lazy.cjs +1 -53
- package/dist/cjs/validators/literal.cjs +1 -64
- package/dist/cjs/validators/map.cjs +1 -218
- package/dist/cjs/validators/nan.cjs +1 -41
- package/dist/cjs/validators/never.cjs +1 -36
- package/dist/cjs/validators/null.cjs +1 -43
- package/dist/cjs/validators/number.cjs +1 -427
- package/dist/cjs/validators/object.cjs +1 -868
- package/dist/cjs/validators/promise.cjs +1 -115
- package/dist/cjs/validators/record.cjs +1 -262
- package/dist/cjs/validators/set.cjs +1 -203
- package/dist/cjs/validators/string-bool.cjs +1 -124
- package/dist/cjs/validators/string-formats.cjs +1 -168
- package/dist/cjs/validators/string.cjs +1 -12
- package/dist/cjs/validators/symbol.cjs +1 -62
- package/dist/cjs/validators/template-literal.cjs +1 -108
- package/dist/cjs/validators/tuple.cjs +1 -187
- package/dist/cjs/validators/uint8array.cjs +1 -106
- package/dist/cjs/validators/undefined.cjs +1 -43
- package/dist/cjs/validators/union.cjs +1 -191
- package/dist/cjs/validators/unknown.cjs +1 -40
- package/dist/cjs/validators/void.cjs +1 -48
- package/dist/cjs/validators/xor.cjs +1 -69
- package/dist/cli/bin.js +1 -406
- package/dist/cli/index.js +1 -205
- package/dist/codecs/index.js +1 -11
- package/dist/coercion/index.js +1 -579
- package/dist/compat/emitter.js +1 -180
- package/dist/compat/result.js +1 -170
- package/dist/errors-core.d.ts +32 -2
- package/dist/errors.js +1 -171
- package/dist/index.d.ts +315 -361
- package/dist/index.js +1 -1534
- package/dist/kernel.js +1 -409
- package/dist/locales/af.js +1 -106
- package/dist/locales/ar.js +1 -106
- package/dist/locales/bn.js +1 -106
- package/dist/locales/da.js +1 -106
- package/dist/locales/de.js +1 -106
- package/dist/locales/en.js +1 -106
- package/dist/locales/es-MX.js +1 -105
- package/dist/locales/es.js +1 -94
- package/dist/locales/fi.js +1 -106
- package/dist/locales/fr.js +1 -94
- package/dist/locales/hi.js +1 -106
- package/dist/locales/id.js +1 -106
- package/dist/locales/index.js +1 -29
- package/dist/locales/it.js +1 -106
- package/dist/locales/ja.js +1 -106
- package/dist/locales/ko.js +1 -106
- package/dist/locales/lazy.js +1 -177
- package/dist/locales/nl.js +1 -106
- package/dist/locales/no.js +1 -106
- package/dist/locales/pl.js +1 -106
- package/dist/locales/pt-BR.js +1 -106
- package/dist/locales/pt.js +1 -106
- package/dist/locales/ru.js +1 -106
- package/dist/locales/runtime.js +1 -41
- package/dist/locales/sv.js +1 -106
- package/dist/locales/sw.js +1 -106
- package/dist/locales/th.js +1 -106
- package/dist/locales/tr.js +1 -106
- package/dist/locales/vi.js +1 -106
- package/dist/locales/zh.js +1 -106
- package/dist/mini.d.ts +3 -3
- package/dist/mini.js +1 -156
- package/dist/pigment.js +1 -180
- package/dist/plugins/index.js +1 -1
- package/dist/registry.js +1 -34
- package/dist/utils/json-schema.d.ts +10 -1
- package/dist/v3/index.js +1 -46
- package/dist/v4/core/index.d.ts +28 -16
- package/dist/v4/core/index.js +1 -277
- package/dist/v4/index.js +1 -50
- package/dist/v4/locales/index.d.ts +4 -0
- package/dist/v4/locales/index.js +1 -77
- package/dist/v4/mini/index.js +1 -47
- package/dist/v4-mini/index.d.ts +1 -1
- package/dist/v4-mini/index.js +1 -75
- package/dist/validators/any.js +1 -37
- package/dist/validators/array.d.ts +7 -0
- package/dist/validators/array.js +1 -279
- package/dist/validators/base.d.ts +210 -17
- package/dist/validators/base.js +1 -868
- package/dist/validators/base64.js +1 -68
- package/dist/validators/bigint.d.ts +7 -0
- package/dist/validators/bigint.js +1 -228
- package/dist/validators/boolean.js +1 -142
- package/dist/validators/codec.js +1 -200
- package/dist/validators/custom.d.ts +20 -10
- package/dist/validators/custom.js +1 -67
- package/dist/validators/date.d.ts +2 -0
- package/dist/validators/date.js +1 -274
- package/dist/validators/discriminated-union.d.ts +4 -2
- package/dist/validators/discriminated-union.js +1 -123
- package/dist/validators/enum.d.ts +8 -2
- package/dist/validators/enum.js +1 -150
- package/dist/validators/file.js +1 -125
- package/dist/validators/function.js +1 -58
- package/dist/validators/hex.js +1 -65
- package/dist/validators/index.d.ts +1 -1
- package/dist/validators/index.js +1 -35
- package/dist/validators/intersection.js +1 -116
- package/dist/validators/json.js +1 -66
- package/dist/validators/lazy.js +1 -50
- package/dist/validators/literal.d.ts +8 -3
- package/dist/validators/literal.js +1 -60
- package/dist/validators/map.d.ts +7 -0
- package/dist/validators/map.js +1 -214
- package/dist/validators/nan.js +1 -38
- package/dist/validators/never.js +1 -32
- package/dist/validators/null.js +1 -40
- package/dist/validators/number.d.ts +25 -1
- package/dist/validators/number.js +1 -423
- package/dist/validators/object.d.ts +18 -2
- package/dist/validators/object.js +1 -864
- package/dist/validators/promise.d.ts +6 -7
- package/dist/validators/promise.js +1 -111
- package/dist/validators/record.d.ts +13 -8
- package/dist/validators/record.js +1 -258
- package/dist/validators/set.d.ts +5 -0
- package/dist/validators/set.js +1 -199
- package/dist/validators/string-bool.js +1 -120
- package/dist/validators/string-formats.d.ts +58 -62
- package/dist/validators/string-formats.js +1 -135
- package/dist/validators/string.d.ts +49 -10
- package/dist/validators/string.js +1 -4
- package/dist/validators/symbol.js +1 -58
- package/dist/validators/template-literal.js +1 -103
- package/dist/validators/tuple.d.ts +17 -13
- package/dist/validators/tuple.js +1 -183
- package/dist/validators/uint8array.js +1 -101
- package/dist/validators/undefined.js +1 -40
- package/dist/validators/union.d.ts +1 -0
- package/dist/validators/union.js +1 -187
- package/dist/validators/unknown.js +1 -37
- package/dist/validators/void.js +1 -44
- package/dist/validators/xor.d.ts +2 -2
- package/dist/validators/xor.js +1 -66
- package/package.json +240 -238
- package/dist/chunks/errors-core-BeiFUtZM.js +0 -81
- package/dist/chunks/index-0R2ntSud.js +0 -508
- package/dist/chunks/index-BvH403Yy.js +0 -88
- package/dist/chunks/security-B0Pd_vSY.js +0 -35
- package/dist/chunks/string-BJRQod3t.js +0 -530
- package/dist/cjs/chunks/errors-core-DWxLFs2f.cjs +0 -83
- package/dist/cjs/chunks/index-BkI37108.cjs +0 -538
- package/dist/cjs/chunks/index-Cc3bjUMX.cjs +0 -91
- package/dist/cjs/chunks/security-Dc3Jh13T.cjs +0 -37
- package/dist/cjs/chunks/string-BKZrc4hs.cjs +0 -533
package/README.md
CHANGED
|
@@ -1,1235 +1,413 @@
|
|
|
1
|
-
# VLD - Fast
|
|
2
|
-
|
|
3
|
-
[](https://www.npmjs.com/package/@oxog/vld) [](https://opensource.org/licenses/MIT) [](https://www.typescriptlang.org/) [](package.json) [](package.json)
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
-
|
|
12
|
-
-
|
|
13
|
-
-
|
|
14
|
-
-
|
|
15
|
-
-
|
|
16
|
-
-
|
|
17
|
-
-
|
|
18
|
-
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
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
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
age:
|
|
240
|
-
|
|
241
|
-
});
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
import {
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
v
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
//
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
v
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
v.
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
)
|
|
415
|
-
|
|
416
|
-
// Literal
|
|
417
|
-
v.literal('active') // 'active'
|
|
418
|
-
|
|
419
|
-
// Enum
|
|
420
|
-
v.enum('red', 'green', 'blue') // 'red' | 'green' | 'blue'
|
|
421
|
-
```
|
|
422
|
-
|
|
423
|
-
### Type Coercion
|
|
424
|
-
|
|
425
|
-
```typescript
|
|
426
|
-
// Coerce strings from various types
|
|
427
|
-
v.coerce.string().parse(123) // "123"
|
|
428
|
-
v.coerce.string().parse(true) // "true"
|
|
429
|
-
|
|
430
|
-
// Coerce numbers from strings/booleans
|
|
431
|
-
v.coerce.number().parse("123") // 123
|
|
432
|
-
v.coerce.number().parse(true) // 1
|
|
433
|
-
|
|
434
|
-
// Coerce booleans from strings/numbers
|
|
435
|
-
v.coerce.boolean().parse("true") // true
|
|
436
|
-
v.coerce.boolean().parse(1) // true
|
|
437
|
-
|
|
438
|
-
// Coerce BigInt from strings/numbers
|
|
439
|
-
v.coerce.bigint().parse("123") // 123n
|
|
440
|
-
v.coerce.bigint().parse(456) // 456n
|
|
441
|
-
|
|
442
|
-
// Coerce Date from strings/timestamps
|
|
443
|
-
v.coerce.date().parse("2023-01-01") // Date object
|
|
444
|
-
v.coerce.date().parse(1672531200000) // Date object
|
|
445
|
-
```
|
|
446
|
-
|
|
447
|
-
### Object Schema Methods
|
|
448
|
-
|
|
449
|
-
```typescript
|
|
450
|
-
const userSchema = v.object({
|
|
451
|
-
name: v.string(),
|
|
452
|
-
age: v.number(),
|
|
453
|
-
email: v.string(),
|
|
454
|
-
role: v.string()
|
|
455
|
-
});
|
|
456
|
-
|
|
457
|
-
// Pick specific fields
|
|
458
|
-
const publicSchema = userSchema.pick('name', 'age');
|
|
459
|
-
// Type: { name: string; age: number }
|
|
460
|
-
|
|
461
|
-
// Omit sensitive fields
|
|
462
|
-
const safeSchema = userSchema.omit('email', 'role');
|
|
463
|
-
// Type: { name: string; age: number }
|
|
464
|
-
|
|
465
|
-
// Extend with new fields
|
|
466
|
-
const extendedSchema = userSchema.extend({
|
|
467
|
-
isActive: v.boolean(),
|
|
468
|
-
lastLogin: v.date()
|
|
469
|
-
});
|
|
470
|
-
// Type: { name: string; age: number; email: string; role: string; isActive: boolean; lastLogin: Date }
|
|
471
|
-
```
|
|
472
|
-
|
|
473
|
-
### Advanced Validation Methods
|
|
474
|
-
|
|
475
|
-
```typescript
|
|
476
|
-
// Custom validation with refine()
|
|
477
|
-
const positiveNumber = v.number()
|
|
478
|
-
.refine(n => n > 0, "Number must be positive");
|
|
479
|
-
|
|
480
|
-
// Data transformation with transform()
|
|
481
|
-
const uppercaseString = v.string()
|
|
482
|
-
.transform(s => s.toUpperCase());
|
|
483
|
-
|
|
484
|
-
// Default values for undefined
|
|
485
|
-
const withDefault = v.string().default("fallback");
|
|
486
|
-
withDefault.parse(undefined); // "fallback"
|
|
487
|
-
|
|
488
|
-
// Catch errors and provide fallback
|
|
489
|
-
const withCatch = v.number().catch(-1);
|
|
490
|
-
withCatch.parse("invalid"); // -1
|
|
491
|
-
|
|
492
|
-
// Method chaining
|
|
493
|
-
const complexSchema = v.string()
|
|
494
|
-
.min(3)
|
|
495
|
-
.transform(s => s.trim())
|
|
496
|
-
.refine(s => s.includes('@'), 'Must contain @')
|
|
497
|
-
.default('user@example.com');
|
|
498
|
-
```
|
|
499
|
-
|
|
500
|
-
### Special Validators
|
|
501
|
-
|
|
502
|
-
```typescript
|
|
503
|
-
// JSON validator with optional schema
|
|
504
|
-
v.json() // Any valid JSON
|
|
505
|
-
v.json(v.object({ name: v.string() })) // Typed JSON
|
|
506
|
-
|
|
507
|
-
// Lazy for recursive schemas
|
|
508
|
-
const categorySchema = v.lazy(() =>
|
|
509
|
-
v.object({
|
|
510
|
-
name: v.string(),
|
|
511
|
-
children: v.array(categorySchema).optional()
|
|
512
|
-
})
|
|
513
|
-
);
|
|
514
|
-
|
|
515
|
-
// Custom validator
|
|
516
|
-
v.custom({
|
|
517
|
-
check: (val) => typeof val === 'string' && val.length > 0,
|
|
518
|
-
message: 'Must be a non-empty string'
|
|
519
|
-
});
|
|
520
|
-
|
|
521
|
-
// File validator
|
|
522
|
-
v.file()
|
|
523
|
-
.maxSize(5 * 1024 * 1024) // 5MB
|
|
524
|
-
.type(['image/png', 'image/jpeg']);
|
|
525
|
-
|
|
526
|
-
// Function validator
|
|
527
|
-
v.function()
|
|
528
|
-
.args(v.string(), v.number())
|
|
529
|
-
.returns(v.boolean());
|
|
530
|
-
```
|
|
531
|
-
|
|
532
|
-
### Type Inference
|
|
533
|
-
|
|
534
|
-
```typescript
|
|
535
|
-
import { v } from '@oxog/vld';
|
|
536
|
-
import type { Infer } from '@oxog/vld';
|
|
537
|
-
|
|
538
|
-
const schema = v.object({
|
|
539
|
-
name: v.string(),
|
|
540
|
-
age: v.number()
|
|
541
|
-
});
|
|
542
|
-
|
|
543
|
-
// Automatically infer the type
|
|
544
|
-
type User = Infer<typeof schema>;
|
|
545
|
-
// { name: string; age: number }
|
|
546
|
-
```
|
|
547
|
-
|
|
548
|
-
### Error Formatting Types
|
|
549
|
-
|
|
550
|
-
```typescript
|
|
551
|
-
import { VldError } from '@oxog/vld';
|
|
552
|
-
import type {
|
|
553
|
-
VldIssue, // Individual validation issue
|
|
554
|
-
VldErrorTree, // Nested error structure
|
|
555
|
-
VldFlattenedError // Flattened error structure
|
|
556
|
-
} from '@oxog/vld';
|
|
557
|
-
```
|
|
558
|
-
|
|
559
|
-
### Custom Error Messages
|
|
560
|
-
|
|
561
|
-
```typescript
|
|
562
|
-
const schema = v.string().min(8, 'Password must be at least 8 characters');
|
|
563
|
-
|
|
564
|
-
const result = schema.safeParse('short');
|
|
565
|
-
if (!result.success) {
|
|
566
|
-
console.log(result.error.message); // 'Password must be at least 8 characters'
|
|
567
|
-
}
|
|
568
|
-
```
|
|
569
|
-
|
|
570
|
-
## Internationalization (i18n)
|
|
571
|
-
|
|
572
|
-
VLD supports 27+ languages out of the box with comprehensive error messages:
|
|
573
|
-
|
|
574
|
-
```typescript
|
|
575
|
-
import { v, setLocale } from '@oxog/vld';
|
|
576
|
-
|
|
577
|
-
// Default is English
|
|
578
|
-
const schema = v.string().min(5);
|
|
579
|
-
schema.safeParse('Hi'); // Error: "String must be at least 5 characters"
|
|
580
|
-
|
|
581
|
-
// Switch to Turkish
|
|
582
|
-
setLocale('tr');
|
|
583
|
-
schema.safeParse('Hi'); // Error: "Metin en az 5 karakter olmali"
|
|
584
|
-
|
|
585
|
-
// Switch to Spanish
|
|
586
|
-
setLocale('es');
|
|
587
|
-
schema.safeParse('Hi'); // Error: "La cadena debe tener al menos 5 caracteres"
|
|
588
|
-
|
|
589
|
-
// Switch to Japanese
|
|
590
|
-
setLocale('ja');
|
|
591
|
-
schema.safeParse('Hi'); // Error: "..."
|
|
592
|
-
```
|
|
593
|
-
|
|
594
|
-
### Supported Languages
|
|
595
|
-
|
|
596
|
-
#### Base Languages (15):
|
|
597
|
-
- English (`en`) - Turkish (`tr`) - Spanish (`es`) - French (`fr`) - German (`de`)
|
|
598
|
-
- Italian (`it`) - Portuguese (`pt`) - Russian (`ru`) - Japanese (`ja`) - Korean (`ko`)
|
|
599
|
-
- Chinese (`zh`) - Arabic (`ar`) - Hindi (`hi`) - Dutch (`nl`) - Polish (`pl`)
|
|
600
|
-
|
|
601
|
-
#### European Languages (4):
|
|
602
|
-
- Danish (`da`) - Swedish (`sv`) - Norwegian (`no`) - Finnish (`fi`)
|
|
603
|
-
|
|
604
|
-
#### Asian Languages (4):
|
|
605
|
-
- Thai (`th`) - Vietnamese (`vi`) - Indonesian (`id`) - Bengali (`bn`)
|
|
606
|
-
|
|
607
|
-
#### African Languages (2):
|
|
608
|
-
- Swahili (`sw`) - Afrikaans (`af`)
|
|
609
|
-
|
|
610
|
-
#### American Languages (2):
|
|
611
|
-
- Portuguese Brazil (`pt-BR`) - Spanish Mexico (`es-MX`)
|
|
612
|
-
|
|
613
|
-
**Plus 75+ additional languages** supported through comprehensive type definitions with English fallback.
|
|
614
|
-
|
|
615
|
-
## Error Handling & Formatting
|
|
616
|
-
|
|
617
|
-
VLD provides advanced error formatting utilities similar to Zod's error handling system.
|
|
618
|
-
|
|
619
|
-
### Error Formatting Utilities
|
|
620
|
-
|
|
621
|
-
```typescript
|
|
622
|
-
import { v, VldError, treeifyError, prettifyError, flattenError } from '@oxog/vld';
|
|
623
|
-
|
|
624
|
-
const userSchema = v.object({
|
|
625
|
-
username: v.string().min(3),
|
|
626
|
-
favoriteNumbers: v.array(v.number()),
|
|
627
|
-
profile: v.object({
|
|
628
|
-
name: v.string(),
|
|
629
|
-
email: v.string().email()
|
|
630
|
-
})
|
|
631
|
-
});
|
|
632
|
-
|
|
633
|
-
const result = userSchema.safeParse({
|
|
634
|
-
username: 'ab',
|
|
635
|
-
favoriteNumbers: [1, 'two', 3],
|
|
636
|
-
profile: {
|
|
637
|
-
name: '',
|
|
638
|
-
email: 'invalid-email'
|
|
639
|
-
},
|
|
640
|
-
extraField: 'not allowed'
|
|
641
|
-
});
|
|
642
|
-
|
|
643
|
-
if (!result.success) {
|
|
644
|
-
const error = result.error as VldError;
|
|
645
|
-
|
|
646
|
-
// 1. Tree Format - Nested structure for complex UIs
|
|
647
|
-
const tree = treeifyError(error);
|
|
648
|
-
|
|
649
|
-
// 2. Pretty Format - Human-readable console output
|
|
650
|
-
const pretty = prettifyError(error);
|
|
651
|
-
|
|
652
|
-
// 3. Flatten Format - Simple form validation
|
|
653
|
-
const flattened = flattenError(error);
|
|
654
|
-
}
|
|
655
|
-
```
|
|
656
|
-
|
|
657
|
-
### Using Error Formats in Practice
|
|
658
|
-
|
|
659
|
-
#### React Form Validation
|
|
660
|
-
```typescript
|
|
661
|
-
function UserForm() {
|
|
662
|
-
const [errors, setErrors] = useState<VldFlattenedError | null>(null);
|
|
663
|
-
|
|
664
|
-
const handleSubmit = (data: unknown) => {
|
|
665
|
-
const result = userSchema.safeParse(data);
|
|
666
|
-
|
|
667
|
-
if (!result.success) {
|
|
668
|
-
setErrors(flattenError(result.error as VldError));
|
|
669
|
-
} else {
|
|
670
|
-
setErrors(null);
|
|
671
|
-
}
|
|
672
|
-
};
|
|
673
|
-
|
|
674
|
-
return (
|
|
675
|
-
<form onSubmit={handleSubmit}>
|
|
676
|
-
{errors?.formErrors.map(error => (
|
|
677
|
-
<div key={error} className="form-error">{error}</div>
|
|
678
|
-
))}
|
|
679
|
-
|
|
680
|
-
<input name="username" />
|
|
681
|
-
{errors?.fieldErrors.username?.map(error => (
|
|
682
|
-
<div key={error} className="field-error">{error}</div>
|
|
683
|
-
))}
|
|
684
|
-
</form>
|
|
685
|
-
);
|
|
686
|
-
}
|
|
687
|
-
```
|
|
688
|
-
|
|
689
|
-
#### API Error Responses
|
|
690
|
-
```typescript
|
|
691
|
-
app.post('/api/users', (req, res) => {
|
|
692
|
-
const result = userSchema.safeParse(req.body);
|
|
693
|
-
|
|
694
|
-
if (!result.success) {
|
|
695
|
-
const tree = treeifyError(result.error as VldError);
|
|
696
|
-
res.status(400).json({
|
|
697
|
-
error: 'Validation failed',
|
|
698
|
-
details: tree
|
|
699
|
-
});
|
|
700
|
-
}
|
|
701
|
-
});
|
|
702
|
-
```
|
|
703
|
-
|
|
704
|
-
## Advanced Examples
|
|
705
|
-
|
|
706
|
-
### Complex Validation with New Features
|
|
707
|
-
|
|
708
|
-
```typescript
|
|
709
|
-
const postSchema = v.object({
|
|
710
|
-
id: v.union(v.string().uuid(), v.number()),
|
|
711
|
-
title: v.string().min(5).max(100),
|
|
712
|
-
content: v.string().min(10),
|
|
713
|
-
author: v.object({
|
|
714
|
-
name: v.string(),
|
|
715
|
-
email: v.string().email(),
|
|
716
|
-
age: v.coerce.number(),
|
|
717
|
-
}),
|
|
718
|
-
tags: v.set(v.string()).default(new Set()),
|
|
719
|
-
metadata: v.record(v.any()),
|
|
720
|
-
coordinates: v.tuple(v.number(), v.number()),
|
|
721
|
-
publishedAt: v.date().default(() => new Date()),
|
|
722
|
-
status: v.enum('draft', 'published', 'archived')
|
|
723
|
-
});
|
|
724
|
-
|
|
725
|
-
// Extend with additional fields
|
|
726
|
-
const blogPostSchema = postSchema.extend({
|
|
727
|
-
viewCount: v.bigint().default(0n),
|
|
728
|
-
categories: v.array(v.string()).min(1),
|
|
729
|
-
featured: v.boolean().default(false)
|
|
730
|
-
});
|
|
731
|
-
```
|
|
732
|
-
|
|
733
|
-
### Discriminated Union
|
|
734
|
-
|
|
735
|
-
```typescript
|
|
736
|
-
const eventSchema = v.discriminatedUnion('type',
|
|
737
|
-
v.object({
|
|
738
|
-
type: v.literal('click'),
|
|
739
|
-
x: v.number(),
|
|
740
|
-
y: v.number()
|
|
741
|
-
}),
|
|
742
|
-
v.object({
|
|
743
|
-
type: v.literal('scroll'),
|
|
744
|
-
direction: v.enum('up', 'down'),
|
|
745
|
-
distance: v.number()
|
|
746
|
-
}),
|
|
747
|
-
v.object({
|
|
748
|
-
type: v.literal('keypress'),
|
|
749
|
-
key: v.string(),
|
|
750
|
-
modifiers: v.array(v.enum('ctrl', 'alt', 'shift'))
|
|
751
|
-
})
|
|
752
|
-
);
|
|
753
|
-
|
|
754
|
-
// Type-safe parsing
|
|
755
|
-
const event = eventSchema.parse({
|
|
756
|
-
type: 'click',
|
|
757
|
-
x: 100,
|
|
758
|
-
y: 200
|
|
759
|
-
});
|
|
760
|
-
```
|
|
761
|
-
|
|
762
|
-
### Recursive Schemas
|
|
763
|
-
|
|
764
|
-
```typescript
|
|
765
|
-
const categorySchema: ReturnType<typeof v.lazy> = v.lazy(() =>
|
|
766
|
-
v.object({
|
|
767
|
-
name: v.string(),
|
|
768
|
-
slug: v.string().regex(/^[a-z0-9-]+$/),
|
|
769
|
-
children: v.array(categorySchema).optional()
|
|
770
|
-
})
|
|
771
|
-
);
|
|
772
|
-
|
|
773
|
-
const category = categorySchema.parse({
|
|
774
|
-
name: 'Electronics',
|
|
775
|
-
slug: 'electronics',
|
|
776
|
-
children: [
|
|
777
|
-
{
|
|
778
|
-
name: 'Phones',
|
|
779
|
-
slug: 'phones',
|
|
780
|
-
children: [
|
|
781
|
-
{ name: 'Smartphones', slug: 'smartphones' }
|
|
782
|
-
]
|
|
783
|
-
}
|
|
784
|
-
]
|
|
785
|
-
});
|
|
786
|
-
```
|
|
787
|
-
|
|
788
|
-
### Type-Safe Forms
|
|
789
|
-
|
|
790
|
-
```typescript
|
|
791
|
-
const loginSchema = v.object({
|
|
792
|
-
username: v.string().min(3),
|
|
793
|
-
password: v.string().min(8),
|
|
794
|
-
rememberMe: v.optional(v.boolean())
|
|
795
|
-
});
|
|
796
|
-
|
|
797
|
-
type LoginForm = Infer<typeof loginSchema>;
|
|
798
|
-
|
|
799
|
-
function handleLogin(data: unknown) {
|
|
800
|
-
const result = loginSchema.safeParse(data);
|
|
801
|
-
|
|
802
|
-
if (result.success) {
|
|
803
|
-
const { username, password, rememberMe } = result.data;
|
|
804
|
-
// ... handle login
|
|
805
|
-
}
|
|
806
|
-
}
|
|
807
|
-
```
|
|
808
|
-
|
|
809
|
-
## Codecs - Bidirectional Transformations
|
|
810
|
-
|
|
811
|
-
VLD introduces **codecs** - powerful bidirectional transformations that can convert data between different representations.
|
|
812
|
-
|
|
813
|
-
### What are Codecs?
|
|
814
|
-
|
|
815
|
-
Codecs enable safe, type-checked conversions between different data formats:
|
|
816
|
-
|
|
817
|
-
```typescript
|
|
818
|
-
import { stringToNumber, jsonCodec, base64ToBytes } from '@oxog/vld';
|
|
819
|
-
|
|
820
|
-
// String to number conversion
|
|
821
|
-
const age = stringToNumber.parse('25'); // 25
|
|
822
|
-
const price = stringToNumber.encode(99.99); // "99.99"
|
|
823
|
-
|
|
824
|
-
// JSON codec
|
|
825
|
-
const userJson = jsonCodec();
|
|
826
|
-
const user = userJson.parse('{"name":"John","age":30}');
|
|
827
|
-
const jsonString = userJson.encode(user);
|
|
828
|
-
|
|
829
|
-
// Binary data
|
|
830
|
-
const bytes = base64ToBytes.parse('SGVsbG8gV29ybGQ=');
|
|
831
|
-
```
|
|
832
|
-
|
|
833
|
-
### Built-in Codecs
|
|
834
|
-
|
|
835
|
-
#### String Conversion Codecs
|
|
836
|
-
```typescript
|
|
837
|
-
import { stringToNumber, stringToInt, stringToBigInt, stringToBoolean } from '@oxog/vld';
|
|
838
|
-
|
|
839
|
-
stringToNumber.parse('42.5'); // 42.5
|
|
840
|
-
stringToInt.parse('42'); // 42
|
|
841
|
-
stringToBigInt.parse('123n'); // 123n
|
|
842
|
-
stringToBoolean.parse('true'); // true
|
|
843
|
-
```
|
|
844
|
-
|
|
845
|
-
#### Date Conversion Codecs
|
|
846
|
-
```typescript
|
|
847
|
-
import { isoDatetimeToDate, epochSecondsToDate, epochMillisToDate } from '@oxog/vld';
|
|
848
|
-
|
|
849
|
-
isoDatetimeToDate.parse('2023-12-25T10:30:00.000Z'); // Date
|
|
850
|
-
epochSecondsToDate.parse(1703505000); // Date
|
|
851
|
-
epochMillisToDate.parse(1703505000000); // Date
|
|
852
|
-
```
|
|
853
|
-
|
|
854
|
-
#### URL Codecs
|
|
855
|
-
```typescript
|
|
856
|
-
import { stringToURL, stringToHttpURL, uriComponent } from '@oxog/vld';
|
|
857
|
-
|
|
858
|
-
stringToURL.parse('https://example.com/path?q=1');
|
|
859
|
-
stringToHttpURL.parse('https://api.example.com');
|
|
860
|
-
uriComponent.parse('Hello World!'); // "Hello%20World!"
|
|
861
|
-
```
|
|
862
|
-
|
|
863
|
-
#### Binary Data Codecs
|
|
864
|
-
```typescript
|
|
865
|
-
import { base64ToBytes, hexToBytes, utf8ToBytes, bytesToUtf8 } from '@oxog/vld';
|
|
866
|
-
|
|
867
|
-
base64ToBytes.parse('SGVsbG8='); // Uint8Array
|
|
868
|
-
hexToBytes.parse('48656c6c6f'); // Uint8Array
|
|
869
|
-
utf8ToBytes.parse('Hello'); // Uint8Array
|
|
870
|
-
bytesToUtf8.parse(new Uint8Array([72, 101, 108, 108, 111])); // "Hello"
|
|
871
|
-
```
|
|
872
|
-
|
|
873
|
-
### Custom Codecs
|
|
874
|
-
|
|
875
|
-
```typescript
|
|
876
|
-
const csvToArray = v.codec(
|
|
877
|
-
v.string(),
|
|
878
|
-
v.array(v.string()),
|
|
879
|
-
{
|
|
880
|
-
decode: (csv: string) => csv.split(',').map(s => s.trim()),
|
|
881
|
-
encode: (arr: string[]) => arr.join(', ')
|
|
882
|
-
}
|
|
883
|
-
);
|
|
884
|
-
|
|
885
|
-
const tags = csvToArray.parse('react, typescript, vld');
|
|
886
|
-
// ["react", "typescript", "vld"]
|
|
887
|
-
|
|
888
|
-
const csvString = csvToArray.encode(['node', 'express', 'api']);
|
|
889
|
-
// "node, express, api"
|
|
890
|
-
```
|
|
891
|
-
|
|
892
|
-
## Plugin System
|
|
893
|
-
|
|
894
|
-
VLD v1.5.0 introduces a powerful plugin system for extending functionality.
|
|
895
|
-
|
|
896
|
-
### Creating a Plugin
|
|
897
|
-
|
|
898
|
-
```typescript
|
|
899
|
-
import { definePlugin, usePlugin, v } from '@oxog/vld';
|
|
900
|
-
|
|
901
|
-
// Define a custom plugin
|
|
902
|
-
const myPlugin = definePlugin({
|
|
903
|
-
name: 'my-plugin',
|
|
904
|
-
version: '1.0.0',
|
|
905
|
-
|
|
906
|
-
// Custom validators
|
|
907
|
-
validators: {
|
|
908
|
-
phoneNumber: () => v.string().regex(/^\+?[1-9]\d{1,14}$/),
|
|
909
|
-
postalCode: () => v.string().regex(/^\d{5}(-\d{4})?$/)
|
|
910
|
-
},
|
|
911
|
-
|
|
912
|
-
// Custom transforms
|
|
913
|
-
transforms: {
|
|
914
|
-
normalizePhone: (phone: string) => phone.replace(/[^\d+]/g, '')
|
|
915
|
-
},
|
|
916
|
-
|
|
917
|
-
// Lifecycle hooks
|
|
918
|
-
install(kernel) {
|
|
919
|
-
console.log('Plugin installed!');
|
|
920
|
-
}
|
|
921
|
-
});
|
|
922
|
-
|
|
923
|
-
// Register the plugin
|
|
924
|
-
usePlugin(myPlugin);
|
|
925
|
-
```
|
|
926
|
-
|
|
927
|
-
### Using Plugin Validators
|
|
928
|
-
|
|
929
|
-
```typescript
|
|
930
|
-
import { createVldKernel, usePlugin } from '@oxog/vld';
|
|
931
|
-
|
|
932
|
-
const kernel = createVldKernel({ debug: true });
|
|
933
|
-
|
|
934
|
-
kernel.use(myPlugin);
|
|
935
|
-
|
|
936
|
-
// Access custom validators
|
|
937
|
-
const phoneSchema = kernel.validator('phoneNumber');
|
|
938
|
-
phoneSchema.parse('+1234567890');
|
|
939
|
-
```
|
|
940
|
-
|
|
941
|
-
## Result Pattern
|
|
942
|
-
|
|
943
|
-
VLD v1.5.0 includes a functional Result pattern for error handling.
|
|
944
|
-
|
|
945
|
-
### Basic Usage
|
|
946
|
-
|
|
947
|
-
```typescript
|
|
948
|
-
import { Ok, Err, match, map, flatMap, tryCatch } from '@oxog/vld';
|
|
949
|
-
|
|
950
|
-
// Create results
|
|
951
|
-
const success = Ok(42);
|
|
952
|
-
const failure = Err(new Error('Something went wrong'));
|
|
953
|
-
|
|
954
|
-
// Pattern matching
|
|
955
|
-
const message = match(success, {
|
|
956
|
-
ok: (value) => `Got: ${value}`,
|
|
957
|
-
err: (error) => `Error: ${error.message}`
|
|
958
|
-
});
|
|
959
|
-
|
|
960
|
-
// Transform results
|
|
961
|
-
const doubled = map(success, (n) => n * 2); // Ok(84)
|
|
962
|
-
|
|
963
|
-
// Chain operations
|
|
964
|
-
const result = flatMap(success, (n) =>
|
|
965
|
-
n > 0 ? Ok(n * 2) : Err(new Error('Must be positive'))
|
|
966
|
-
);
|
|
967
|
-
|
|
968
|
-
// Safe function execution
|
|
969
|
-
const parsed = tryCatch(() => JSON.parse('{"a":1}'));
|
|
970
|
-
```
|
|
971
|
-
|
|
972
|
-
### With Validation
|
|
973
|
-
|
|
974
|
-
```typescript
|
|
975
|
-
import { v, isOk, isErr, unwrapOr } from '@oxog/vld';
|
|
976
|
-
|
|
977
|
-
const schema = v.object({
|
|
978
|
-
name: v.string(),
|
|
979
|
-
age: v.number().min(0)
|
|
980
|
-
});
|
|
981
|
-
|
|
982
|
-
const result = schema.safeParse(data);
|
|
983
|
-
|
|
984
|
-
if (isOk(result)) {
|
|
985
|
-
console.log('Valid:', result.data);
|
|
986
|
-
} else {
|
|
987
|
-
console.log('Invalid:', result.error);
|
|
988
|
-
}
|
|
989
|
-
|
|
990
|
-
// With default value
|
|
991
|
-
const user = unwrapOr(result, { name: 'Guest', age: 0 });
|
|
992
|
-
```
|
|
993
|
-
|
|
994
|
-
### Combining Results
|
|
995
|
-
|
|
996
|
-
```typescript
|
|
997
|
-
import { all, fromNullable } from '@oxog/vld';
|
|
998
|
-
|
|
999
|
-
// Combine multiple results
|
|
1000
|
-
const results = [Ok(1), Ok(2), Ok(3)];
|
|
1001
|
-
const combined = all(results); // Ok([1, 2, 3])
|
|
1002
|
-
|
|
1003
|
-
// Convert nullable to Result
|
|
1004
|
-
const maybeValue: string | null = getValue();
|
|
1005
|
-
const result = fromNullable(maybeValue, new Error('Value is null'));
|
|
1006
|
-
```
|
|
1007
|
-
|
|
1008
|
-
## CLI Tools
|
|
1009
|
-
|
|
1010
|
-
VLD includes command-line tools for validation and benchmarking.
|
|
1011
|
-
|
|
1012
|
-
### Installation
|
|
1013
|
-
|
|
1014
|
-
```bash
|
|
1015
|
-
npm install -g @oxog/vld
|
|
1016
|
-
# or use npx
|
|
1017
|
-
npx vld --help
|
|
1018
|
-
```
|
|
1019
|
-
|
|
1020
|
-
### Commands
|
|
1021
|
-
|
|
1022
|
-
```bash
|
|
1023
|
-
# Show help
|
|
1024
|
-
vld --help
|
|
1025
|
-
|
|
1026
|
-
# Validate data
|
|
1027
|
-
vld validate schema.json data.json
|
|
1028
|
-
|
|
1029
|
-
# Run benchmarks
|
|
1030
|
-
vld benchmark
|
|
1031
|
-
|
|
1032
|
-
# Show version
|
|
1033
|
-
vld --version
|
|
1034
|
-
```
|
|
1035
|
-
|
|
1036
|
-
### Programmatic CLI
|
|
1037
|
-
|
|
1038
|
-
```typescript
|
|
1039
|
-
import { createCli, vldCli } from '@oxog/vld/cli';
|
|
1040
|
-
|
|
1041
|
-
// Use the built-in CLI
|
|
1042
|
-
vldCli.run(process.argv.slice(2));
|
|
1043
|
-
|
|
1044
|
-
// Or create a custom CLI
|
|
1045
|
-
const cli = createCli('my-app', '1.0.0', 'My validation app')
|
|
1046
|
-
.command({
|
|
1047
|
-
name: 'validate',
|
|
1048
|
-
description: 'Validate data',
|
|
1049
|
-
action: async (args, options) => {
|
|
1050
|
-
// Custom validation logic
|
|
1051
|
-
}
|
|
1052
|
-
});
|
|
1053
|
-
|
|
1054
|
-
cli.run(process.argv.slice(2));
|
|
1055
|
-
```
|
|
1056
|
-
|
|
1057
|
-
## Logger & Colored Output
|
|
1058
|
-
|
|
1059
|
-
VLD includes a logging system and colored terminal output.
|
|
1060
|
-
|
|
1061
|
-
### Logger
|
|
1062
|
-
|
|
1063
|
-
```typescript
|
|
1064
|
-
import { createLogger, setLogLevel, enableDebug } from '@oxog/vld';
|
|
1065
|
-
|
|
1066
|
-
// Create a logger
|
|
1067
|
-
const logger = createLogger({ prefix: 'VLD' });
|
|
1068
|
-
|
|
1069
|
-
logger.info('Processing...');
|
|
1070
|
-
logger.warn('Deprecated feature');
|
|
1071
|
-
logger.error('Validation failed');
|
|
1072
|
-
logger.debug('Debug info');
|
|
1073
|
-
|
|
1074
|
-
// Set log level globally
|
|
1075
|
-
setLogLevel('debug');
|
|
1076
|
-
|
|
1077
|
-
// Enable debug mode
|
|
1078
|
-
enableDebug();
|
|
1079
|
-
```
|
|
1080
|
-
|
|
1081
|
-
### Colored Output (Pigment)
|
|
1082
|
-
|
|
1083
|
-
```typescript
|
|
1084
|
-
import { pigment, red, green, blue, bold, dim } from '@oxog/vld';
|
|
1085
|
-
|
|
1086
|
-
console.log(red('Error!'));
|
|
1087
|
-
console.log(green('Success!'));
|
|
1088
|
-
console.log(bold(blue('Important')));
|
|
1089
|
-
console.log(dim('Less important'));
|
|
1090
|
-
|
|
1091
|
-
// Or use the pigment object
|
|
1092
|
-
console.log(pigment.red('Error!'));
|
|
1093
|
-
console.log(pigment.bold(pigment.green('Success!')));
|
|
1094
|
-
```
|
|
1095
|
-
|
|
1096
|
-
## VLD vs. Zod
|
|
1097
|
-
|
|
1098
|
-
VLD is designed as a compelling alternative to Zod, offering full feature parity while delivering significant improvements.
|
|
1099
|
-
|
|
1100
|
-
### Feature Comparison
|
|
1101
|
-
|
|
1102
|
-
| Feature | VLD | Zod |
|
|
1103
|
-
| ----------------------- | ---------------------------------- | ------------------------------------ |
|
|
1104
|
-
| **Performance** | **Release-gated faster runtime, startup, and memory paths** | Baseline |
|
|
1105
|
-
| **Memory Usage** | **~4.85x less** overall | Baseline |
|
|
1106
|
-
| **Internationalization**| **Built-in (27+ languages)** | Requires third-party library |
|
|
1107
|
-
| **Dependencies** | **Zero** | `zod-i18n` for locales |
|
|
1108
|
-
| **Bundle Size** | Smaller | Larger |
|
|
1109
|
-
| **API** | 100% Zod-compatible | Standard Zod API |
|
|
1110
|
-
| **Plugin System** | **Built-in** | Not available |
|
|
1111
|
-
| **Result Pattern** | **Built-in** | Not available |
|
|
1112
|
-
| **CLI Tools** | **Built-in** | Not available |
|
|
1113
|
-
| **Codecs** | Built-in, bidirectional | Built-in |
|
|
1114
|
-
| **Type Inference** | Excellent | Excellent |
|
|
1115
|
-
|
|
1116
|
-
### Seamless Migration from Zod
|
|
1117
|
-
|
|
1118
|
-
```javascript
|
|
1119
|
-
// Before (Zod)
|
|
1120
|
-
import { z } from 'zod';
|
|
1121
|
-
const schema = z.string().email();
|
|
1122
|
-
|
|
1123
|
-
// After (VLD) - Exact same syntax!
|
|
1124
|
-
import { v } from '@oxog/vld';
|
|
1125
|
-
const schema = v.string().email();
|
|
1126
|
-
```
|
|
1127
|
-
|
|
1128
|
-
### Drop-in Package Subpaths
|
|
1129
|
-
|
|
1130
|
-
VLD also exposes Zod-compatible package subpaths so applications that import Zod 4 entry points can migrate by changing the package name:
|
|
1131
|
-
|
|
1132
|
-
```typescript
|
|
1133
|
-
// Before
|
|
1134
|
-
import { z } from 'zod';
|
|
1135
|
-
import * as core from 'zod/v4/core';
|
|
1136
|
-
import * as mini from 'zod/v4-mini';
|
|
1137
|
-
import * as locales from 'zod/v4/locales';
|
|
1138
|
-
|
|
1139
|
-
// After
|
|
1140
|
-
import { z } from '@oxog/vld';
|
|
1141
|
-
import * as core from '@oxog/vld/v4/core';
|
|
1142
|
-
import * as mini from '@oxog/vld/v4-mini';
|
|
1143
|
-
import * as locales from '@oxog/vld/v4/locales';
|
|
1144
|
-
```
|
|
1145
|
-
|
|
1146
|
-
Release checks enforce zero missing exports and zero `typeof` mismatches for `zod/v4`, `zod/v4-mini`, `zod/v4/mini`, `zod/v4/core`, and `zod/v4/locales` against the installed latest Zod.
|
|
1147
|
-
|
|
1148
|
-
### Real App Drop-in Verification
|
|
1149
|
-
|
|
1150
|
-
`npm run verify:drop-in` creates two temporary TypeScript applications from the same fixture:
|
|
1151
|
-
|
|
1152
|
-
- One imports and runs against real `zod`.
|
|
1153
|
-
- One imports and runs against the locally built `@oxog/vld` package.
|
|
1154
|
-
- The fixture exercises root APIs, `v4`, `v4-mini`, `v4/core`, `v4/locales`, parsing, error formatting, JSON Schema output, mini helpers, and core factories.
|
|
1155
|
-
- The normalized runtime outputs must match exactly.
|
|
1156
|
-
|
|
1157
|
-
## Benchmarks
|
|
1158
|
-
|
|
1159
|
-
### Performance Results
|
|
1160
|
-
|
|
1161
|
-
Median of 11 samples per case against Zod 4.4.3, the latest stable release at the time of measurement. The release gate also runs focused runtime, startup, memory, package, install, security, Zod parity, and real drop-in app checks before publishing.
|
|
1162
|
-
|
|
1163
|
-
| Test Case | VLD Median Performance | Improvement |
|
|
1164
|
-
|-----------|----------------|-------------|
|
|
1165
|
-
| Simple String | 719.2M ops/sec | **3.40x faster** |
|
|
1166
|
-
| Email Validation | 22.1M ops/sec | **2.87x faster** |
|
|
1167
|
-
| Top-level Email Format | 22.8M ops/sec | **3.66x faster** |
|
|
1168
|
-
| StringBool Validation | 32.7M ops/sec | **3.44x faster** |
|
|
1169
|
-
| Number Validation | 226.2M ops/sec | **27.62x faster** |
|
|
1170
|
-
| Simple Object | 37.7M ops/sec | **1.40x faster** |
|
|
1171
|
-
| Complex Object | 2.3M ops/sec | **1.02x faster** |
|
|
1172
|
-
| Array Validation | 39.8M ops/sec | **6.74x faster** |
|
|
1173
|
-
| Union Types | 38.1M ops/sec | **4.10x faster** |
|
|
1174
|
-
| Optional Values | 236.9M ops/sec | **7.57x faster** |
|
|
1175
|
-
| Nullable Values | 224.7M ops/sec | **10.71x faster** |
|
|
1176
|
-
| Nullish Values | 228.4M ops/sec | **52.92x faster** |
|
|
1177
|
-
| Default Values | 207.8M ops/sec | **9.71x faster** |
|
|
1178
|
-
| Catch Values | 232.1M ops/sec | **46.58x faster** |
|
|
1179
|
-
| SafeParse | 162.0M ops/sec | **1.80x faster** |
|
|
1180
|
-
| Type Coercion | 204.5M ops/sec | **8.72x faster** |
|
|
1181
|
-
| Enum Validation | 180.3M ops/sec | **1.32x faster** |
|
|
1182
|
-
| Discriminated Union | 19.2M ops/sec | **1.79x faster** |
|
|
1183
|
-
| Tuple Validation | 50.0M ops/sec | **7.99x faster** |
|
|
1184
|
-
| Record Validation | 10.7M ops/sec | **5.63x faster** |
|
|
1185
|
-
| Set Validation | 18.9M ops/sec | **1.84x faster** |
|
|
1186
|
-
| Map Validation | 14.8M ops/sec | **2.37x faster** |
|
|
1187
|
-
| BigInt Validation | 164.8M ops/sec | **14.88x faster** |
|
|
1188
|
-
| Date Validation | 227.2M ops/sec | **2.57x faster** |
|
|
1189
|
-
| Symbol Validation | 230.0M ops/sec | **1.01x faster** |
|
|
1190
|
-
| Any Validation | 232.5M ops/sec | **1.01x faster** |
|
|
1191
|
-
| Unknown Validation | 234.9M ops/sec | **1.04x faster** |
|
|
1192
|
-
| Function Validation | 233.6M ops/sec | **3.52x faster** |
|
|
1193
|
-
| Template Literal Validation | 39.3M ops/sec | **1.15x faster** |
|
|
1194
|
-
| Promise Async Validation | 6.3M ops/sec | **1.38x faster** |
|
|
1195
|
-
|
|
1196
|
-
**VLD won 30/30 snapshot tests. Release checks additionally enforce runtime, startup, memory, Zod subpath parity, and real drop-in app guard thresholds against Zod.**
|
|
1197
|
-
|
|
1198
|
-
### Run Benchmarks
|
|
1199
|
-
|
|
1200
|
-
```bash
|
|
1201
|
-
# Quick performance comparison
|
|
1202
|
-
npm run benchmark
|
|
1203
|
-
|
|
1204
|
-
# Median-based stable comparison
|
|
1205
|
-
npm run benchmark:stable
|
|
1206
|
-
|
|
1207
|
-
# Fast CI-friendly performance regression guard
|
|
1208
|
-
npm run benchmark:guard
|
|
1209
|
-
|
|
1210
|
-
# Full release gate: lint, source and published types, tests, exports, package and install checks, security audit, Zod parity, real drop-in app verification, and performance guards
|
|
1211
|
-
npm run release:check
|
|
1212
|
-
|
|
1213
|
-
# Memory usage comparison
|
|
1214
|
-
npm run benchmark:memory
|
|
1215
|
-
|
|
1216
|
-
# Startup time comparison
|
|
1217
|
-
npm run benchmark:startup
|
|
1218
|
-
|
|
1219
|
-
# Run all benchmarks
|
|
1220
|
-
npm run benchmark:all
|
|
1221
|
-
```
|
|
1222
|
-
|
|
1223
|
-
## Contributing
|
|
1224
|
-
|
|
1225
|
-
Contributions are welcome! Please feel free to submit a Pull Request.
|
|
1226
|
-
|
|
1227
|
-
## Links
|
|
1228
|
-
|
|
1229
|
-
- [Documentation](https://vld.oxog.dev)
|
|
1230
|
-
- [NPM Package](https://www.npmjs.com/package/@oxog/vld)
|
|
1231
|
-
- [GitHub Repository](https://github.com/ersinkoc/vld)
|
|
1232
|
-
|
|
1233
|
-
---
|
|
1234
|
-
|
|
1235
|
-
Made with Love by [Ersin KOC](https://github.com/ersinkoc)
|
|
1
|
+
# VLD - Ultra-Fast TypeScript Validation Library
|
|
2
|
+
|
|
3
|
+
[](https://www.npmjs.com/package/@oxog/vld) [](https://opensource.org/licenses/MIT) [](https://www.typescriptlang.org/) [](package.json) [](package.json) [](https://vld.oxog.dev)
|
|
4
|
+
|
|
5
|
+
**Website & Live Playground**: [https://vld.oxog.dev](https://vld.oxog.dev)
|
|
6
|
+
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
## Highlights
|
|
10
|
+
|
|
11
|
+
- **Release-Gated Speed**: 11x+ faster runtime throughput and 4.7x+ less memory consumption compared to Zod.
|
|
12
|
+
- **Zero Dependencies**: Pure TypeScript/JavaScript with zero third-party runtime bloat.
|
|
13
|
+
- **Drop-in Zod Compatibility**: Swap imports directly or use subpaths (`@oxog/vld/v4`, `@oxog/vld/mini`, `@oxog/vld/v4/core`, `@oxog/vld/v4/locales`).
|
|
14
|
+
- **Full Static Inference**: Automatic type extraction using `v.infer<typeof schema>`.
|
|
15
|
+
- **Tree-Shakeable Mini API**: Build hyper-optimized bundles with `@oxog/vld/mini`.
|
|
16
|
+
- **Built-in i18n**: Out-of-the-box error localization for 27+ languages with lazy-loading support (`@oxog/vld/locales/lazy`).
|
|
17
|
+
- **Result Pattern & Codecs**: Functional error handling (`tryCatch`, `match`, `Ok`, `Err`) and bidirectional data transformations.
|
|
18
|
+
- **100% Test Coverage**: Verified across 2500+ tests and drop-in TypeScript application suites.
|
|
19
|
+
|
|
20
|
+
---
|
|
21
|
+
|
|
22
|
+
## Installation
|
|
23
|
+
|
|
24
|
+
```bash
|
|
25
|
+
npm install @oxog/vld
|
|
26
|
+
# or
|
|
27
|
+
yarn add @oxog/vld
|
|
28
|
+
# or
|
|
29
|
+
pnpm add @oxog/vld
|
|
30
|
+
# or
|
|
31
|
+
bun add @oxog/vld
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
---
|
|
35
|
+
|
|
36
|
+
## Quick Start
|
|
37
|
+
|
|
38
|
+
```typescript
|
|
39
|
+
import { v } from '@oxog/vld';
|
|
40
|
+
|
|
41
|
+
// Define schema with chainable validations
|
|
42
|
+
const userSchema = v.object({
|
|
43
|
+
name: v.string().min(2).max(100),
|
|
44
|
+
email: v.string().email(),
|
|
45
|
+
age: v.number().int().positive().optional(),
|
|
46
|
+
role: v.enum('admin', 'user', 'guest').default('user'),
|
|
47
|
+
tags: v.array(v.string()).min(1),
|
|
48
|
+
});
|
|
49
|
+
|
|
50
|
+
// Infer TypeScript type
|
|
51
|
+
type User = v.infer<typeof userSchema>;
|
|
52
|
+
|
|
53
|
+
// Safe parsing (no throwing)
|
|
54
|
+
const result = userSchema.safeParse({
|
|
55
|
+
name: 'John Doe',
|
|
56
|
+
email: 'john@example.com',
|
|
57
|
+
age: 28,
|
|
58
|
+
tags: ['developer', 'typescript'],
|
|
59
|
+
});
|
|
60
|
+
|
|
61
|
+
if (result.success) {
|
|
62
|
+
console.log('Valid data:', result.data); // Typed as User
|
|
63
|
+
} else {
|
|
64
|
+
console.error('Validation issues:', result.error.issues);
|
|
65
|
+
}
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
---
|
|
69
|
+
|
|
70
|
+
## Core Schema API
|
|
71
|
+
|
|
72
|
+
### Primitive Validators
|
|
73
|
+
|
|
74
|
+
```typescript
|
|
75
|
+
v.string() // String validation
|
|
76
|
+
v.number() // Number validation
|
|
77
|
+
v.int() // Integer validation
|
|
78
|
+
v.int32() // 32-bit integer validation
|
|
79
|
+
v.boolean() // Boolean validation
|
|
80
|
+
v.bigint() // BigInt validation
|
|
81
|
+
v.date() // Date validation
|
|
82
|
+
v.symbol() // Symbol validation
|
|
83
|
+
v.uint8array() // Uint8Array validation
|
|
84
|
+
v.literal('active') // Literal value
|
|
85
|
+
v.enum('admin', 'user', 'guest') // Enum values
|
|
86
|
+
v.any() // Any type
|
|
87
|
+
v.unknown() // Unknown type
|
|
88
|
+
v.null() // Null
|
|
89
|
+
v.undefined() // Undefined
|
|
90
|
+
v.nullish() // Null or undefined
|
|
91
|
+
v.void() // Void
|
|
92
|
+
v.never() // Never
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
### String Formats
|
|
96
|
+
|
|
97
|
+
```typescript
|
|
98
|
+
v.string()
|
|
99
|
+
.min(3)
|
|
100
|
+
.max(100)
|
|
101
|
+
.email()
|
|
102
|
+
.url()
|
|
103
|
+
.uuid()
|
|
104
|
+
.regex(/^[a-z0-9]+$/)
|
|
105
|
+
.startsWith('https://')
|
|
106
|
+
.endsWith('.json')
|
|
107
|
+
.trim()
|
|
108
|
+
.toLowerCase();
|
|
109
|
+
|
|
110
|
+
// Top-level format helpers:
|
|
111
|
+
v.email()
|
|
112
|
+
v.uuid()
|
|
113
|
+
v.creditCard() // Luhn checksum validated
|
|
114
|
+
v.jwt()
|
|
115
|
+
v.cuid()
|
|
116
|
+
v.cuid2()
|
|
117
|
+
v.nanoid()
|
|
118
|
+
v.ulid()
|
|
119
|
+
v.ipv4()
|
|
120
|
+
v.ipv6()
|
|
121
|
+
v.iso.date()
|
|
122
|
+
v.iso.dateTime()
|
|
123
|
+
```
|
|
124
|
+
|
|
125
|
+
### Number Constraints
|
|
126
|
+
|
|
127
|
+
```typescript
|
|
128
|
+
v.number()
|
|
129
|
+
.min(0)
|
|
130
|
+
.max(100)
|
|
131
|
+
.int()
|
|
132
|
+
.positive()
|
|
133
|
+
.negative()
|
|
134
|
+
.nonnegative()
|
|
135
|
+
.multipleOf(5)
|
|
136
|
+
.finite()
|
|
137
|
+
.safe();
|
|
138
|
+
```
|
|
139
|
+
|
|
140
|
+
### Objects & Collections
|
|
141
|
+
|
|
142
|
+
```typescript
|
|
143
|
+
// Objects
|
|
144
|
+
const profileSchema = v.object({
|
|
145
|
+
username: v.string().min(3),
|
|
146
|
+
age: v.number().optional(),
|
|
147
|
+
});
|
|
148
|
+
|
|
149
|
+
// Object transformations
|
|
150
|
+
profileSchema.partial(); // All fields optional
|
|
151
|
+
profileSchema.strict(); // Reject unknown fields
|
|
152
|
+
profileSchema.passthrough(); // Keep unknown fields
|
|
153
|
+
profileSchema.pick('username');
|
|
154
|
+
profileSchema.omit('age');
|
|
155
|
+
profileSchema.extend({ bio: v.string() });
|
|
156
|
+
|
|
157
|
+
// Arrays & Collections
|
|
158
|
+
v.array(v.string()).min(1).max(10);
|
|
159
|
+
v.tuple(v.string(), v.number());
|
|
160
|
+
v.record(v.string(), v.number());
|
|
161
|
+
v.set(v.string());
|
|
162
|
+
v.map(v.string(), v.number());
|
|
163
|
+
```
|
|
164
|
+
|
|
165
|
+
### Unions & Compositions
|
|
166
|
+
|
|
167
|
+
```typescript
|
|
168
|
+
// Union
|
|
169
|
+
v.union(v.string(), v.number());
|
|
170
|
+
|
|
171
|
+
// Discriminated Union
|
|
172
|
+
const eventSchema = v.discriminatedUnion('type',
|
|
173
|
+
v.object({ type: v.literal('click'), x: v.number(), y: v.number() }),
|
|
174
|
+
v.object({ type: v.literal('scroll'), offset: v.number() })
|
|
175
|
+
);
|
|
176
|
+
|
|
177
|
+
// Intersections & XOR
|
|
178
|
+
v.intersection(schemaA, schemaB);
|
|
179
|
+
v.xor(schemaA, schemaB);
|
|
180
|
+
|
|
181
|
+
// Recursive / Lazy Schemas
|
|
182
|
+
const treeSchema: ReturnType<typeof v.lazy> = v.lazy(() =>
|
|
183
|
+
v.object({
|
|
184
|
+
id: v.string(),
|
|
185
|
+
children: v.array(treeSchema).optional(),
|
|
186
|
+
})
|
|
187
|
+
);
|
|
188
|
+
```
|
|
189
|
+
|
|
190
|
+
---
|
|
191
|
+
|
|
192
|
+
## Type Coercion & Modifiers
|
|
193
|
+
|
|
194
|
+
### Automatic Coercion (`v.coerce`)
|
|
195
|
+
|
|
196
|
+
```typescript
|
|
197
|
+
v.coerce.string().parse(123); // "123"
|
|
198
|
+
v.coerce.number().parse("42"); // 42
|
|
199
|
+
v.coerce.boolean().parse("true"); // true
|
|
200
|
+
v.coerce.bigint().parse("1000"); // 1000n
|
|
201
|
+
v.coerce.date().parse("2026-08-17"); // Date object
|
|
202
|
+
```
|
|
203
|
+
|
|
204
|
+
### Refinements, Transforms & Defaults
|
|
205
|
+
|
|
206
|
+
```typescript
|
|
207
|
+
const customSchema = v.string()
|
|
208
|
+
.transform(val => val.trim())
|
|
209
|
+
.refine(val => val.length >= 3, 'Must be at least 3 characters')
|
|
210
|
+
.default('default_value')
|
|
211
|
+
.catch('fallback_on_error');
|
|
212
|
+
|
|
213
|
+
// SuperRefine for multi-field cross validation
|
|
214
|
+
const passwordSchema = v.object({
|
|
215
|
+
password: v.string().min(8),
|
|
216
|
+
confirm: v.string(),
|
|
217
|
+
}).superRefine((data, ctx) => {
|
|
218
|
+
if (data.password !== data.confirm) {
|
|
219
|
+
ctx.addIssue({
|
|
220
|
+
code: 'custom',
|
|
221
|
+
path: ['confirm'],
|
|
222
|
+
message: 'Passwords do not match',
|
|
223
|
+
});
|
|
224
|
+
}
|
|
225
|
+
});
|
|
226
|
+
```
|
|
227
|
+
|
|
228
|
+
---
|
|
229
|
+
|
|
230
|
+
## Tree-Shakeable Mini API
|
|
231
|
+
|
|
232
|
+
For bundle-constrained applications, `@oxog/vld/mini` provides pure standalone functions with zero extra overhead:
|
|
233
|
+
|
|
234
|
+
```typescript
|
|
235
|
+
import { string, number, object, optional, array } from '@oxog/vld/mini';
|
|
236
|
+
|
|
237
|
+
const userSchema = object({
|
|
238
|
+
name: string().min(2),
|
|
239
|
+
age: optional(number().positive()),
|
|
240
|
+
roles: array(string()),
|
|
241
|
+
});
|
|
242
|
+
```
|
|
243
|
+
|
|
244
|
+
---
|
|
245
|
+
|
|
246
|
+
## Drop-in Zod Compatibility
|
|
247
|
+
|
|
248
|
+
VLD provides drop-in subpaths that mirror Zod export structures and error shapes:
|
|
249
|
+
|
|
250
|
+
```typescript
|
|
251
|
+
// Replace Zod imports seamlessly
|
|
252
|
+
import { z } from '@oxog/vld';
|
|
253
|
+
import * as core from '@oxog/vld/v4/core';
|
|
254
|
+
import * as mini from '@oxog/vld/v4-mini';
|
|
255
|
+
import * as locales from '@oxog/vld/v4/locales';
|
|
256
|
+
|
|
257
|
+
import { v, deepPartial, input, output } from '@oxog/vld';
|
|
258
|
+
```
|
|
259
|
+
|
|
260
|
+
- Structured error issues with `expected`, `received`, `minimum`, `maximum`, and `path`.
|
|
261
|
+
- Compatibility tested against latest stable Zod releases.
|
|
262
|
+
|
|
263
|
+
---
|
|
264
|
+
|
|
265
|
+
## Error Handling & Formatting
|
|
266
|
+
|
|
267
|
+
```typescript
|
|
268
|
+
import { v, VldError, treeifyError, prettifyError, flattenError } from '@oxog/vld';
|
|
269
|
+
|
|
270
|
+
const result = userSchema.safeParse(invalidData);
|
|
271
|
+
|
|
272
|
+
if (!result.success) {
|
|
273
|
+
const error = result.error as VldError;
|
|
274
|
+
|
|
275
|
+
// Flattened field errors for forms
|
|
276
|
+
const { fieldErrors, formErrors } = flattenError(error);
|
|
277
|
+
|
|
278
|
+
// Human-readable CLI / console output
|
|
279
|
+
const pretty = prettifyError(error);
|
|
280
|
+
|
|
281
|
+
// Nested tree structure for UI inspection
|
|
282
|
+
const tree = treeifyError(error);
|
|
283
|
+
}
|
|
284
|
+
```
|
|
285
|
+
|
|
286
|
+
---
|
|
287
|
+
|
|
288
|
+
## Internationalization (i18n)
|
|
289
|
+
|
|
290
|
+
VLD includes built-in translations for 27+ languages:
|
|
291
|
+
|
|
292
|
+
```typescript
|
|
293
|
+
import { v, setLocale } from '@oxog/vld';
|
|
294
|
+
|
|
295
|
+
setLocale('tr'); // Turkish error messages
|
|
296
|
+
setLocale('es'); // Spanish
|
|
297
|
+
setLocale('de'); // German
|
|
298
|
+
setLocale('ja'); // Japanese
|
|
299
|
+
setLocale('fr'); // French
|
|
300
|
+
```
|
|
301
|
+
|
|
302
|
+
### Lazy Loading for Minimal Bundles
|
|
303
|
+
|
|
304
|
+
```typescript
|
|
305
|
+
import { setLocaleAsync, preloadLocales } from '@oxog/vld/locales/lazy';
|
|
306
|
+
|
|
307
|
+
// Loads locale on demand via dynamic import()
|
|
308
|
+
await setLocaleAsync('tr');
|
|
309
|
+
|
|
310
|
+
// Preload for SSR / warm start
|
|
311
|
+
await preloadLocales(['en', 'de', 'ja']);
|
|
312
|
+
```
|
|
313
|
+
|
|
314
|
+
---
|
|
315
|
+
|
|
316
|
+
## Bidirectional Codecs
|
|
317
|
+
|
|
318
|
+
```typescript
|
|
319
|
+
import { stringToNumber, jsonCodec, base64ToBytes, hexToBytes } from '@oxog/vld';
|
|
320
|
+
|
|
321
|
+
// String to Number decode & encode
|
|
322
|
+
const num = stringToNumber.parse('42'); // 42
|
|
323
|
+
const str = stringToNumber.encode(42); // "42"
|
|
324
|
+
|
|
325
|
+
// JSON codec
|
|
326
|
+
const json = jsonCodec();
|
|
327
|
+
const parsed = json.parse('{"id":1}');
|
|
328
|
+
const encoded = json.encode(parsed);
|
|
329
|
+
|
|
330
|
+
// Binary conversions
|
|
331
|
+
const bytes = base64ToBytes.parse('SGVsbG8=');
|
|
332
|
+
```
|
|
333
|
+
|
|
334
|
+
---
|
|
335
|
+
|
|
336
|
+
## Result Pattern
|
|
337
|
+
|
|
338
|
+
Functional error handling without exceptions:
|
|
339
|
+
|
|
340
|
+
```typescript
|
|
341
|
+
import { Ok, Err, match, map, flatMap, tryCatch, isOk, isErr, unwrapOr } from '@oxog/vld';
|
|
342
|
+
|
|
343
|
+
const result = tryCatch(() => JSON.parse(rawInput));
|
|
344
|
+
|
|
345
|
+
const output = match(result, {
|
|
346
|
+
ok: data => `Success: ${data.id}`,
|
|
347
|
+
err: err => `Failed: ${err.message}`,
|
|
348
|
+
});
|
|
349
|
+
```
|
|
350
|
+
|
|
351
|
+
---
|
|
352
|
+
|
|
353
|
+
## Plugin System
|
|
354
|
+
|
|
355
|
+
```typescript
|
|
356
|
+
import { definePlugin, usePlugin, createVldKernel, v } from '@oxog/vld';
|
|
357
|
+
|
|
358
|
+
const phonePlugin = definePlugin({
|
|
359
|
+
name: 'phone-validator',
|
|
360
|
+
version: '1.0.0',
|
|
361
|
+
validators: {
|
|
362
|
+
phone: () => v.string().regex(/^\+?[1-9]\d{1,14}$/),
|
|
363
|
+
},
|
|
364
|
+
});
|
|
365
|
+
|
|
366
|
+
usePlugin(phonePlugin);
|
|
367
|
+
```
|
|
368
|
+
|
|
369
|
+
---
|
|
370
|
+
|
|
371
|
+
## Performance
|
|
372
|
+
|
|
373
|
+
VLD is optimized for modern V8 runtimes. CI gates enforce performance floors on every commit against Zod:
|
|
374
|
+
|
|
375
|
+
| Benchmark Case | VLD Throughput | Relative Speedup |
|
|
376
|
+
|----------------|----------------|------------------|
|
|
377
|
+
| Nullish Parse | ~214M ops/sec | **30.7x faster** |
|
|
378
|
+
| Number / Positive Int | ~253M ops/sec | **9.1x faster** |
|
|
379
|
+
| Discriminated Union | ~35M ops/sec | **4.1x faster** |
|
|
380
|
+
| Optional Parse | ~213M ops/sec | **3.8x faster** |
|
|
381
|
+
| Union Parse | ~39M ops/sec | **3.3x faster** |
|
|
382
|
+
| Simple String | ~620M ops/sec | **3.0x faster** |
|
|
383
|
+
| Array / Object Parse | ~49M ops/sec | **1.7x faster** |
|
|
384
|
+
|
|
385
|
+
Explore full benchmark results and interactive visual comparisons at [vld.oxog.dev/benchmark](https://vld.oxog.dev/benchmark).
|
|
386
|
+
|
|
387
|
+
### Running Benchmarks Locally
|
|
388
|
+
|
|
389
|
+
```bash
|
|
390
|
+
npm run benchmark
|
|
391
|
+
npm run benchmark:guard
|
|
392
|
+
npm run benchmark:memory
|
|
393
|
+
npm run benchmark:startup
|
|
394
|
+
npm run release:check
|
|
395
|
+
```
|
|
396
|
+
|
|
397
|
+
---
|
|
398
|
+
|
|
399
|
+
## Contributing
|
|
400
|
+
|
|
401
|
+
Contributions are warmly welcome! Please see [CONTRIBUTING.md](CONTRIBUTING.md) and [SECURITY.md](SECURITY.md) for details.
|
|
402
|
+
|
|
403
|
+
---
|
|
404
|
+
|
|
405
|
+
## Links
|
|
406
|
+
|
|
407
|
+
- **Documentation & Playground**: [https://vld.oxog.dev](https://vld.oxog.dev)
|
|
408
|
+
- **NPM Package**: [https://www.npmjs.com/package/@oxog/vld](https://www.npmjs.com/package/@oxog/vld)
|
|
409
|
+
- **GitHub Repository**: [https://github.com/ersinkoc/vld](https://github.com/ersinkoc/vld)
|
|
410
|
+
|
|
411
|
+
---
|
|
412
|
+
|
|
413
|
+
Made with Love by [Ersin KOC](https://github.com/ersinkoc)
|