@oxog/vld 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (132) hide show
  1. package/CHANGELOG.md +113 -0
  2. package/LICENSE +21 -0
  3. package/README.md +776 -0
  4. package/dist/errors.d.ts +49 -0
  5. package/dist/errors.d.ts.map +1 -0
  6. package/dist/errors.js +144 -0
  7. package/dist/errors.js.map +1 -0
  8. package/dist/index.d.ts +275 -0
  9. package/dist/index.d.ts.map +1 -0
  10. package/dist/index.js +1170 -0
  11. package/dist/index.js.map +1 -0
  12. package/dist/locales/af.d.ts +3 -0
  13. package/dist/locales/af.d.ts.map +1 -0
  14. package/dist/locales/af.js +74 -0
  15. package/dist/locales/af.js.map +1 -0
  16. package/dist/locales/ar.d.ts +3 -0
  17. package/dist/locales/ar.d.ts.map +1 -0
  18. package/dist/locales/ar.js +74 -0
  19. package/dist/locales/ar.js.map +1 -0
  20. package/dist/locales/bn.d.ts +3 -0
  21. package/dist/locales/bn.d.ts.map +1 -0
  22. package/dist/locales/bn.js +74 -0
  23. package/dist/locales/bn.js.map +1 -0
  24. package/dist/locales/da.d.ts +3 -0
  25. package/dist/locales/da.d.ts.map +1 -0
  26. package/dist/locales/da.js +74 -0
  27. package/dist/locales/da.js.map +1 -0
  28. package/dist/locales/de.d.ts +3 -0
  29. package/dist/locales/de.d.ts.map +1 -0
  30. package/dist/locales/de.js +74 -0
  31. package/dist/locales/de.js.map +1 -0
  32. package/dist/locales/en.d.ts +3 -0
  33. package/dist/locales/en.d.ts.map +1 -0
  34. package/dist/locales/en.js +74 -0
  35. package/dist/locales/en.js.map +1 -0
  36. package/dist/locales/es-MX.d.ts +3 -0
  37. package/dist/locales/es-MX.d.ts.map +1 -0
  38. package/dist/locales/es-MX.js +74 -0
  39. package/dist/locales/es-MX.js.map +1 -0
  40. package/dist/locales/es.d.ts +3 -0
  41. package/dist/locales/es.d.ts.map +1 -0
  42. package/dist/locales/es.js +62 -0
  43. package/dist/locales/es.js.map +1 -0
  44. package/dist/locales/fi.d.ts +3 -0
  45. package/dist/locales/fi.d.ts.map +1 -0
  46. package/dist/locales/fi.js +74 -0
  47. package/dist/locales/fi.js.map +1 -0
  48. package/dist/locales/fr.d.ts +3 -0
  49. package/dist/locales/fr.d.ts.map +1 -0
  50. package/dist/locales/fr.js +62 -0
  51. package/dist/locales/fr.js.map +1 -0
  52. package/dist/locales/hi.d.ts +3 -0
  53. package/dist/locales/hi.d.ts.map +1 -0
  54. package/dist/locales/hi.js +74 -0
  55. package/dist/locales/hi.js.map +1 -0
  56. package/dist/locales/id.d.ts +3 -0
  57. package/dist/locales/id.d.ts.map +1 -0
  58. package/dist/locales/id.js +74 -0
  59. package/dist/locales/id.js.map +1 -0
  60. package/dist/locales/index.d.ts +19 -0
  61. package/dist/locales/index.d.ts.map +1 -0
  62. package/dist/locales/index.js +91 -0
  63. package/dist/locales/index.js.map +1 -0
  64. package/dist/locales/it.d.ts +3 -0
  65. package/dist/locales/it.d.ts.map +1 -0
  66. package/dist/locales/it.js +74 -0
  67. package/dist/locales/it.js.map +1 -0
  68. package/dist/locales/ja.d.ts +3 -0
  69. package/dist/locales/ja.d.ts.map +1 -0
  70. package/dist/locales/ja.js +74 -0
  71. package/dist/locales/ja.js.map +1 -0
  72. package/dist/locales/ko.d.ts +3 -0
  73. package/dist/locales/ko.d.ts.map +1 -0
  74. package/dist/locales/ko.js +74 -0
  75. package/dist/locales/ko.js.map +1 -0
  76. package/dist/locales/nl.d.ts +3 -0
  77. package/dist/locales/nl.d.ts.map +1 -0
  78. package/dist/locales/nl.js +74 -0
  79. package/dist/locales/nl.js.map +1 -0
  80. package/dist/locales/no.d.ts +3 -0
  81. package/dist/locales/no.d.ts.map +1 -0
  82. package/dist/locales/no.js +74 -0
  83. package/dist/locales/no.js.map +1 -0
  84. package/dist/locales/pl.d.ts +3 -0
  85. package/dist/locales/pl.d.ts.map +1 -0
  86. package/dist/locales/pl.js +74 -0
  87. package/dist/locales/pl.js.map +1 -0
  88. package/dist/locales/pt-BR.d.ts +3 -0
  89. package/dist/locales/pt-BR.d.ts.map +1 -0
  90. package/dist/locales/pt-BR.js +74 -0
  91. package/dist/locales/pt-BR.js.map +1 -0
  92. package/dist/locales/pt.d.ts +3 -0
  93. package/dist/locales/pt.d.ts.map +1 -0
  94. package/dist/locales/pt.js +74 -0
  95. package/dist/locales/pt.js.map +1 -0
  96. package/dist/locales/ru.d.ts +3 -0
  97. package/dist/locales/ru.d.ts.map +1 -0
  98. package/dist/locales/ru.js +74 -0
  99. package/dist/locales/ru.js.map +1 -0
  100. package/dist/locales/sv.d.ts +3 -0
  101. package/dist/locales/sv.d.ts.map +1 -0
  102. package/dist/locales/sv.js +74 -0
  103. package/dist/locales/sv.js.map +1 -0
  104. package/dist/locales/sw.d.ts +3 -0
  105. package/dist/locales/sw.d.ts.map +1 -0
  106. package/dist/locales/sw.js +74 -0
  107. package/dist/locales/sw.js.map +1 -0
  108. package/dist/locales/th.d.ts +3 -0
  109. package/dist/locales/th.d.ts.map +1 -0
  110. package/dist/locales/th.js +74 -0
  111. package/dist/locales/th.js.map +1 -0
  112. package/dist/locales/tr.d.ts +3 -0
  113. package/dist/locales/tr.d.ts.map +1 -0
  114. package/dist/locales/tr.js +74 -0
  115. package/dist/locales/tr.js.map +1 -0
  116. package/dist/locales/types.d.ts +60 -0
  117. package/dist/locales/types.d.ts.map +1 -0
  118. package/dist/locales/types.js +2 -0
  119. package/dist/locales/types.js.map +1 -0
  120. package/dist/locales/vi.d.ts +3 -0
  121. package/dist/locales/vi.d.ts.map +1 -0
  122. package/dist/locales/vi.js +74 -0
  123. package/dist/locales/vi.js.map +1 -0
  124. package/dist/locales/zh.d.ts +3 -0
  125. package/dist/locales/zh.d.ts.map +1 -0
  126. package/dist/locales/zh.js +74 -0
  127. package/dist/locales/zh.js.map +1 -0
  128. package/dist/locales.d.ts +54 -0
  129. package/dist/locales.d.ts.map +1 -0
  130. package/dist/locales.js +732 -0
  131. package/dist/locales.js.map +1 -0
  132. package/package.json +73 -0
package/README.md ADDED
@@ -0,0 +1,776 @@
1
+ # VLD - Fast, Type-Safe Validation Library
2
+
3
+ [![npm version](https://img.shields.io/npm/v/@oxog/vld.svg)](https://www.npmjs.com/package/@oxog/vld)
4
+ [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
5
+ [![TypeScript](https://img.shields.io/badge/TypeScript-5.0+-blue.svg)](https://www.typescriptlang.org/)
6
+ [![Zero Dependencies](https://img.shields.io/badge/Dependencies-0-green.svg)](package.json)
7
+ [![Test Coverage](https://img.shields.io/badge/Coverage-95%25-brightgreen.svg)](package.json)
8
+
9
+ VLD is a blazing-fast, type-safe validation library for TypeScript and JavaScript with **full Zod feature parity**. Built with performance in mind, it provides a simple and intuitive API while maintaining excellent type inference and 27+ language internationalization support.
10
+
11
+ ## 🚀 Features
12
+
13
+ ### Core Features
14
+ - **⚡ Blazing Fast**: Optimized for V8 engine with superior performance
15
+ - **🎯 Type-Safe**: Full TypeScript support with excellent type inference
16
+ - **📦 Zero Dependencies**: Lightweight with no external dependencies
17
+ - **🌳 Tree-Shakeable**: Only import what you need
18
+ - **🔧 Composable**: Chain validations for complex schemas
19
+ - **⚠️ Advanced Error Formatting**: Tree, pretty, and flatten error utilities
20
+ - **🌍 Multi-language**: Built-in support for 27+ languages
21
+ - **✅ 95% Test Coverage**: Rigorously tested with comprehensive test suite
22
+
23
+ ### Advanced Zod-Compatible Features
24
+ - **🔄 Type Coercion**: `v.coerce.string()`, `v.coerce.number()`, `v.coerce.boolean()`, etc.
25
+ - **📊 Advanced Types**: BigInt, Symbol, Tuple, Record, Set, Map validation
26
+ - **⚡ Intersection Types**: Combine multiple schemas with intelligent merging
27
+ - **🎨 Custom Validation**: `refine()` for custom predicates and validation logic
28
+ - **🔄 Data Transformation**: `transform()` for post-validation data transformation
29
+ - **🏠 Default Values**: `default()` for handling undefined inputs elegantly
30
+ - **🛡️ Fallback Handling**: `catch()` for graceful error recovery
31
+ - **🎯 Object Utilities**: `pick()`, `omit()`, `extend()` for flexible object schemas
32
+
33
+ ## 📊 Performance
34
+
35
+ VLD is designed for speed. In our benchmarks:
36
+ - **2.91x faster** than Zod for number validation
37
+ - **3.70x faster** for boolean validation
38
+ - **3.47x faster** for array validation
39
+ - **2.30x faster** for email validation
40
+
41
+ ### 🎭 The Truth About Zod's Benchmarks
42
+
43
+ Many validation library benchmarks are misleading because they test with **reused schema instances**:
44
+
45
+ ```javascript
46
+ // What benchmarks typically test (unrealistic):
47
+ const schema = z.string();
48
+ for (let i = 0; i < 1000000; i++) {
49
+ schema.parse(data); // Same instance reused
50
+ }
51
+
52
+ // What happens in real applications:
53
+ app.post('/api/user', (req, res) => {
54
+ // New schema created for each request
55
+ const schema = z.object({
56
+ email: z.string().email(),
57
+ age: z.number().min(18)
58
+ });
59
+ schema.parse(req.body);
60
+ });
61
+ ```
62
+
63
+ When testing real-world patterns:
64
+ - **Creating new instances**: VLD is **2000x faster** than Zod
65
+ - **Reused instances**: Zod benefits from V8's singleton optimization
66
+ - **Real applications**: Schemas are often created dynamically, where VLD excels
67
+
68
+ Run `npm run benchmark:truth` to see the real performance difference.
69
+
70
+ ## 📦 Installation
71
+
72
+ ```bash
73
+ npm install @oxog/vld
74
+ # or
75
+ yarn add @oxog/vld
76
+ # or
77
+ pnpm add @oxog/vld
78
+ ```
79
+
80
+ ## 🎯 Quick Start
81
+
82
+ ```typescript
83
+ import { v } from '@oxog/vld';
84
+
85
+ // Define a schema
86
+ const userSchema = v.object({
87
+ name: v.string().min(2),
88
+ email: v.string().email(),
89
+ age: v.number().min(18).max(100),
90
+ isActive: v.boolean()
91
+ });
92
+
93
+ // Validate data
94
+ const result = userSchema.safeParse({
95
+ name: 'John Doe',
96
+ email: 'john@example.com',
97
+ age: 25,
98
+ isActive: true
99
+ });
100
+
101
+ if (result.success) {
102
+ console.log('Valid user:', result.data);
103
+ } else {
104
+ console.log('Validation error:', result.error);
105
+ }
106
+ ```
107
+
108
+ For advanced error formatting:
109
+ ```typescript
110
+ import { v, VldError, treeifyError, prettifyError, flattenError } from '@oxog/vld';
111
+ ```
112
+
113
+ ## 📖 API Reference
114
+
115
+ ### Basic Types
116
+
117
+ ```typescript
118
+ v.string() // String validation
119
+ v.number() // Number validation
120
+ v.boolean() // Boolean validation
121
+ v.bigint() // BigInt validation
122
+ v.symbol() // Symbol validation
123
+ v.date() // Date validation
124
+ v.literal() // Literal values
125
+ v.enum() // Enum values
126
+ v.any() // Any type
127
+ v.unknown() // Unknown type
128
+ v.void() // Void type
129
+ v.never() // Never type
130
+ ```
131
+
132
+ ### Advanced Types
133
+
134
+ ```typescript
135
+ // Collections
136
+ v.array(v.string()) // Array validation
137
+ v.tuple(v.string(), v.number()) // Fixed-length tuple
138
+ v.record(v.number()) // Record/dictionary validation
139
+ v.set(v.string()) // Set validation
140
+ v.map(v.string(), v.number()) // Map validation
141
+
142
+ // Objects
143
+ v.object({ // Object schema
144
+ name: v.string(),
145
+ age: v.number()
146
+ })
147
+
148
+ // Composition
149
+ v.union(v.string(), v.number()) // Union types
150
+ v.intersection(schemaA, schemaB) // Intersection types
151
+ v.optional(v.string()) // Optional fields
152
+ v.nullable(v.string()) // Nullable fields
153
+ ```
154
+
155
+ ### String Validators
156
+
157
+ ```typescript
158
+ v.string()
159
+ .min(5) // Minimum length
160
+ .max(10) // Maximum length
161
+ .length(8) // Exact length
162
+ .email() // Email format
163
+ .url() // URL format
164
+ .uuid() // UUID format
165
+ .regex(/pattern/) // Custom regex
166
+ .startsWith('prefix') // String prefix
167
+ .endsWith('suffix') // String suffix
168
+ .includes('substring') // Contains substring
169
+ .ip() // IP address (v4 or v6)
170
+ .trim() // Trim whitespace
171
+ .toLowerCase() // Convert to lowercase
172
+ .toUpperCase() // Convert to uppercase
173
+ .nonempty() // Non-empty string
174
+ ```
175
+
176
+ ### Number Validators
177
+
178
+ ```typescript
179
+ v.number()
180
+ .min(0) // Minimum value
181
+ .max(100) // Maximum value
182
+ .int() // Integer only
183
+ .positive() // Positive numbers
184
+ .negative() // Negative numbers
185
+ .nonnegative() // >= 0
186
+ .nonpositive() // <= 0
187
+ .finite() // Finite numbers
188
+ .safe() // Safe integers
189
+ .multipleOf(5) // Multiple of value
190
+ ```
191
+
192
+ ### Arrays and Objects
193
+
194
+ ```typescript
195
+ // Arrays
196
+ v.array(v.string()) // Array of strings
197
+ .min(1) // Minimum length
198
+ .max(10) // Maximum length
199
+ .length(5) // Exact length
200
+ .nonempty() // Non-empty array
201
+
202
+ // Objects
203
+ v.object({
204
+ name: v.string(),
205
+ age: v.number()
206
+ })
207
+ .partial() // All fields optional
208
+ .strict() // No extra fields
209
+ ```
210
+
211
+ ### Composite Types
212
+
213
+ ```typescript
214
+ // Optional
215
+ v.optional(v.string()) // string | undefined
216
+
217
+ // Nullable
218
+ v.nullable(v.string()) // string | null
219
+
220
+ // Union
221
+ v.union(v.string(), v.number()) // string | number
222
+
223
+ // Literal
224
+ v.literal('active') // 'active'
225
+
226
+ // Enum
227
+ v.enum('red', 'green', 'blue') // 'red' | 'green' | 'blue'
228
+ ```
229
+
230
+ ### Type Coercion
231
+
232
+ ```typescript
233
+ // Coerce strings from various types
234
+ v.coerce.string().parse(123) // "123"
235
+ v.coerce.string().parse(true) // "true"
236
+
237
+ // Coerce numbers from strings/booleans
238
+ v.coerce.number().parse("123") // 123
239
+ v.coerce.number().parse(true) // 1
240
+
241
+ // Coerce booleans from strings/numbers
242
+ v.coerce.boolean().parse("true") // true
243
+ v.coerce.boolean().parse(1) // true
244
+
245
+ // Coerce BigInt from strings/numbers
246
+ v.coerce.bigint().parse("123") // 123n
247
+ v.coerce.bigint().parse(456) // 456n
248
+
249
+ // Coerce Date from strings/timestamps
250
+ v.coerce.date().parse("2023-01-01") // Date object
251
+ v.coerce.date().parse(1672531200000) // Date object
252
+ ```
253
+
254
+ ### Object Schema Methods
255
+
256
+ ```typescript
257
+ const userSchema = v.object({
258
+ name: v.string(),
259
+ age: v.number(),
260
+ email: v.string(),
261
+ role: v.string()
262
+ });
263
+
264
+ // Pick specific fields
265
+ const publicSchema = userSchema.pick('name', 'age');
266
+ // Type: { name: string; age: number }
267
+
268
+ // Omit sensitive fields
269
+ const safeSchema = userSchema.omit('email', 'role');
270
+ // Type: { name: string; age: number }
271
+
272
+ // Extend with new fields
273
+ const extendedSchema = userSchema.extend({
274
+ isActive: v.boolean(),
275
+ lastLogin: v.date()
276
+ });
277
+ // Type: { name: string; age: number; email: string; role: string; isActive: boolean; lastLogin: Date }
278
+ ```
279
+
280
+ ### Advanced Validation Methods
281
+
282
+ ```typescript
283
+ // Custom validation with refine()
284
+ const positiveNumber = v.number()
285
+ .refine(n => n > 0, "Number must be positive");
286
+
287
+ // Data transformation with transform()
288
+ const uppercaseString = v.string()
289
+ .transform(s => s.toUpperCase());
290
+
291
+ // Default values for undefined
292
+ const withDefault = v.string().default("fallback");
293
+ withDefault.parse(undefined); // "fallback"
294
+
295
+ // Catch errors and provide fallback
296
+ const withCatch = v.number().catch(-1);
297
+ withCatch.parse("invalid"); // -1
298
+
299
+ // Method chaining
300
+ const complexSchema = v.string()
301
+ .min(3)
302
+ .transform(s => s.trim())
303
+ .refine(s => s.includes('@'), 'Must contain @')
304
+ .default('user@example.com');
305
+ ```
306
+
307
+ ### Type Inference
308
+
309
+ ```typescript
310
+ import { v, Infer } from '@oxog/vld';
311
+
312
+ const schema = v.object({
313
+ name: v.string(),
314
+ age: v.number()
315
+ });
316
+
317
+ // Automatically infer the type
318
+ type User = Infer<typeof schema>;
319
+ // { name: string; age: number }
320
+ ```
321
+
322
+ ### Error Formatting Types
323
+
324
+ ```typescript
325
+ import {
326
+ VldError, // Main error class
327
+ VldIssue, // Individual validation issue
328
+ VldErrorTree, // Nested error structure
329
+ VldFlattenedError // Flattened error structure
330
+ } from '@oxog/vld';
331
+ ```
332
+
333
+ ### Custom Error Messages
334
+
335
+ ```typescript
336
+ const schema = v.string().min(8, 'Password must be at least 8 characters');
337
+
338
+ const result = schema.safeParse('short');
339
+ if (!result.success) {
340
+ console.log(result.error.message); // 'Password must be at least 8 characters'
341
+ }
342
+ ```
343
+
344
+ ## 🌍 Internationalization (i18n)
345
+
346
+ VLD supports 27+ languages out of the box with comprehensive error messages:
347
+
348
+ ```typescript
349
+ import { v, setLocale } from '@oxog/vld';
350
+
351
+ // Default is English
352
+ const schema = v.string().min(5);
353
+ schema.safeParse('Hi'); // Error: "String must be at least 5 characters"
354
+
355
+ // Switch to Turkish
356
+ setLocale('tr');
357
+ schema.safeParse('Hi'); // Error: "Metin en az 5 karakter olmalı"
358
+
359
+ // Switch to Spanish
360
+ setLocale('es');
361
+ schema.safeParse('Hi'); // Error: "La cadena debe tener al menos 5 caracteres"
362
+
363
+ // Switch to Japanese
364
+ setLocale('ja');
365
+ schema.safeParse('Hi'); // Error: "文字列は5文字以上である必要があります"
366
+ ```
367
+
368
+ ### Supported Languages
369
+
370
+ #### Base Languages (15):
371
+ - 🇬🇧 English (`en`) - 🇹🇷 Turkish (`tr`) - 🇪🇸 Spanish (`es`) - 🇫🇷 French (`fr`) - 🇩🇪 German (`de`)
372
+ - 🇮🇹 Italian (`it`) - 🇵🇹 Portuguese (`pt`) - 🇷🇺 Russian (`ru`) - 🇯🇵 Japanese (`ja`) - 🇰🇷 Korean (`ko`)
373
+ - 🇨🇳 Chinese (`zh`) - 🇸🇦 Arabic (`ar`) - 🇮🇳 Hindi (`hi`) - 🇳🇱 Dutch (`nl`) - 🇵🇱 Polish (`pl`)
374
+
375
+ #### European Languages (4):
376
+ - 🇩🇰 Danish (`da`) - 🇸🇪 Swedish (`sv`) - 🇳🇴 Norwegian (`no`) - 🇫🇮 Finnish (`fi`)
377
+
378
+ #### Asian Languages (4):
379
+ - 🇹🇭 Thai (`th`) - 🇻🇳 Vietnamese (`vi`) - 🇮🇩 Indonesian (`id`) - 🇧🇩 Bengali (`bn`)
380
+
381
+ #### African Languages (2):
382
+ - 🇰🇪 Swahili (`sw`) - 🇿🇦 Afrikaans (`af`)
383
+
384
+ #### American Languages (2):
385
+ - 🇧🇷 Portuguese Brazil (`pt-BR`) - 🇲🇽 Spanish Mexico (`es-MX`)
386
+
387
+ **Plus 75+ additional languages** supported through comprehensive type definitions with English fallback, including Icelandic, Czech, Slovak, Hungarian, Romanian, Bulgarian, Croatian, Slovenian, Greek, Hebrew, Persian, Georgian, Armenian, and many more!
388
+
389
+ ## ⚠️ Error Handling & Formatting
390
+
391
+ VLD provides advanced error formatting utilities similar to Zod's error handling system. These utilities help you transform validation errors into user-friendly formats for different use cases.
392
+
393
+ ### Error Formatting Utilities
394
+
395
+ ```typescript
396
+ import { v, VldError, treeifyError, prettifyError, flattenError } from '@oxog/vld';
397
+
398
+ const userSchema = v.object({
399
+ username: v.string().min(3),
400
+ favoriteNumbers: v.array(v.number()),
401
+ profile: v.object({
402
+ name: v.string(),
403
+ email: v.string().email()
404
+ })
405
+ });
406
+
407
+ // This will fail validation
408
+ const result = userSchema.safeParse({
409
+ username: 'ab', // too short
410
+ favoriteNumbers: [1, 'two', 3], // 'two' is not a number
411
+ profile: {
412
+ name: '',
413
+ email: 'invalid-email'
414
+ },
415
+ extraField: 'not allowed'
416
+ });
417
+
418
+ if (!result.success) {
419
+ const error = result.error as VldError;
420
+
421
+ // 1. Tree Format - Nested structure for complex UIs
422
+ const tree = treeifyError(error);
423
+ console.log(tree);
424
+ /*
425
+ {
426
+ errors: ['Unrecognized key: "extraField"'],
427
+ properties: {
428
+ username: { errors: ['String must be at least 3 characters'] },
429
+ favoriteNumbers: {
430
+ items: [
431
+ undefined,
432
+ { errors: ['Expected number, received string'] },
433
+ undefined
434
+ ]
435
+ },
436
+ profile: {
437
+ properties: {
438
+ name: { errors: ['String cannot be empty'] },
439
+ email: { errors: ['Invalid email format'] }
440
+ }
441
+ }
442
+ }
443
+ }
444
+ */
445
+
446
+ // 2. Pretty Format - Human-readable console output
447
+ const pretty = prettifyError(error);
448
+ console.log(pretty);
449
+ /*
450
+ ✖ Unrecognized key: "extraField"
451
+ ✖ String must be at least 3 characters
452
+ → at username
453
+ ✖ Expected number, received string
454
+ → at favoriteNumbers[1]
455
+ ✖ String cannot be empty
456
+ → at profile.name
457
+ ✖ Invalid email format
458
+ → at profile.email
459
+ */
460
+
461
+ // 3. Flatten Format - Simple form validation
462
+ const flattened = flattenError(error);
463
+ console.log(flattened);
464
+ /*
465
+ {
466
+ formErrors: ['Unrecognized key: "extraField"'],
467
+ fieldErrors: {
468
+ username: ['String must be at least 3 characters'],
469
+ favoriteNumbers: ['Expected number, received string'],
470
+ profile: ['String cannot be empty', 'Invalid email format']
471
+ }
472
+ }
473
+ */
474
+ }
475
+ ```
476
+
477
+ ### Using Error Formats in Practice
478
+
479
+ #### React Form Validation
480
+ ```typescript
481
+ function UserForm() {
482
+ const [errors, setErrors] = useState<VldFlattenedError | null>(null);
483
+
484
+ const handleSubmit = (data: unknown) => {
485
+ const result = userSchema.safeParse(data);
486
+
487
+ if (!result.success) {
488
+ setErrors(flattenError(result.error as VldError));
489
+ } else {
490
+ setErrors(null);
491
+ // Process valid data
492
+ }
493
+ };
494
+
495
+ return (
496
+ <form onSubmit={handleSubmit}>
497
+ {errors?.formErrors.map(error => (
498
+ <div key={error} className="form-error">{error}</div>
499
+ ))}
500
+
501
+ <input name="username" />
502
+ {errors?.fieldErrors.username?.map(error => (
503
+ <div key={error} className="field-error">{error}</div>
504
+ ))}
505
+ </form>
506
+ );
507
+ }
508
+ ```
509
+
510
+ #### API Error Responses
511
+ ```typescript
512
+ app.post('/api/users', (req, res) => {
513
+ const result = userSchema.safeParse(req.body);
514
+
515
+ if (!result.success) {
516
+ const tree = treeifyError(result.error as VldError);
517
+ res.status(400).json({
518
+ error: 'Validation failed',
519
+ details: tree
520
+ });
521
+ } else {
522
+ // Process valid user data
523
+ }
524
+ });
525
+ ```
526
+
527
+ #### Console Debugging
528
+ ```typescript
529
+ function validateAndLog(data: unknown) {
530
+ const result = userSchema.safeParse(data);
531
+
532
+ if (!result.success) {
533
+ console.log('Validation failed:');
534
+ console.log(prettifyError(result.error as VldError));
535
+ }
536
+ }
537
+ ```
538
+
539
+ ## 🔥 Advanced Examples
540
+
541
+ ### Complex Validation with New Features
542
+
543
+ ```typescript
544
+ const postSchema = v.object({
545
+ id: v.union(v.string().uuid(), v.number()),
546
+ title: v.string().min(5).max(100),
547
+ content: v.string().min(10),
548
+ author: v.object({
549
+ name: v.string(),
550
+ email: v.string().email(),
551
+ age: v.coerce.number(), // Auto-convert to number
552
+ }),
553
+ tags: v.set(v.string()).default(new Set()), // Use Set instead of array
554
+ metadata: v.record(v.any()), // Key-value metadata
555
+ coordinates: v.tuple(v.number(), v.number()), // [lat, lng]
556
+ publishedAt: v.date().default(() => new Date()),
557
+ status: v.enum('draft', 'published', 'archived')
558
+ });
559
+
560
+ // Extend with additional fields
561
+ const blogPostSchema = postSchema.extend({
562
+ viewCount: v.bigint().default(0n),
563
+ categories: v.array(v.string()).min(1),
564
+ featured: v.boolean().default(false)
565
+ });
566
+
567
+ // Create a public version without sensitive data
568
+ const publicPostSchema = blogPostSchema
569
+ .omit('author')
570
+ .extend({
571
+ authorName: v.string()
572
+ });
573
+ ```
574
+
575
+ ### Advanced Transformations & Validation
576
+
577
+ ```typescript
578
+ // Complex email processing with coercion and transformation
579
+ const emailSchema = v.coerce.string()
580
+ .transform(s => s.toLowerCase().trim())
581
+ .refine(s => s.includes('@'), 'Must be valid email format')
582
+ .transform(s => s.replace(/\+.*@/, '@')) // Remove plus addressing
583
+ .catch('invalid@example.com');
584
+
585
+ // Process user input with fallbacks
586
+ const userInputSchema = v.object({
587
+ name: v.coerce.string()
588
+ .transform(s => s.trim())
589
+ .refine(s => s.length > 0, 'Name cannot be empty')
590
+ .default('Anonymous'),
591
+
592
+ age: v.coerce.number()
593
+ .refine(n => n >= 0 && n <= 150, 'Age must be realistic')
594
+ .catch(0),
595
+
596
+ preferences: v.record(v.any()).default({}),
597
+
598
+ tags: v.union(
599
+ v.array(v.string()),
600
+ v.coerce.string().transform(s => s.split(','))
601
+ ).default([])
602
+ });
603
+
604
+ // Intersection for combining user types
605
+ const baseUser = v.object({
606
+ id: v.string(),
607
+ name: v.string()
608
+ });
609
+
610
+ const adminUser = v.object({
611
+ role: v.literal('admin'),
612
+ permissions: v.array(v.string())
613
+ });
614
+
615
+ const adminSchema = v.intersection(baseUser, adminUser);
616
+ ```
617
+
618
+ ### Collection Validation
619
+
620
+ ```typescript
621
+ // Advanced tuple validation
622
+ const coordinatesSchema = v.tuple(
623
+ v.number().min(-90).max(90), // latitude
624
+ v.number().min(-180).max(180), // longitude
625
+ v.number().positive().optional() // altitude
626
+ );
627
+
628
+ // Map validation for configuration
629
+ const configSchema = v.map(
630
+ v.string().min(1), // keys must be non-empty strings
631
+ v.union(v.string(), v.number(), v.boolean()) // values can be mixed types
632
+ );
633
+
634
+ // Set validation for unique tags
635
+ const uniqueTagsSchema = v.set(v.string().min(1).max(20))
636
+ .refine(tags => tags.size <= 10, 'Too many tags');
637
+ ```
638
+
639
+ ### Real-world API Schema
640
+
641
+ ```typescript
642
+ // Complete API endpoint schema with all features
643
+ const apiUserSchema = v.object({
644
+ // Basic info with coercion
645
+ id: v.coerce.string(),
646
+ username: v.string()
647
+ .min(3)
648
+ .max(20)
649
+ .refine(s => /^[a-zA-Z0-9_]+$/.test(s), 'Invalid username format'),
650
+
651
+ email: v.coerce.string()
652
+ .transform(s => s.toLowerCase().trim())
653
+ .refine(s => s.includes('@'), 'Invalid email'),
654
+
655
+ // Age with fallback
656
+ age: v.coerce.number()
657
+ .min(13)
658
+ .max(120)
659
+ .catch(null),
660
+
661
+ // Preferences as key-value store
662
+ preferences: v.record(v.any()).default({}),
663
+
664
+ // Roles as a set for uniqueness
665
+ roles: v.set(v.enum('user', 'admin', 'moderator'))
666
+ .default(new Set(['user'])),
667
+
668
+ // Metadata with BigInt support
669
+ createdAt: v.coerce.date(),
670
+ userId: v.coerce.bigint(),
671
+
672
+ // Optional complex nested data
673
+ profile: v.object({
674
+ bio: v.string().max(500).default(''),
675
+ location: v.tuple(v.number(), v.number()).optional(),
676
+ socialLinks: v.record(v.string().url()).default({})
677
+ }).optional()
678
+ });
679
+
680
+ // Specialized schemas using pick/omit
681
+ const publicUserSchema = apiUserSchema.pick('username', 'profile');
682
+ const adminUserSchema = apiUserSchema.extend({
683
+ adminNotes: v.string().optional(),
684
+ lastLogin: v.date().optional()
685
+ });
686
+ ```
687
+
688
+ ### Type-Safe Forms
689
+
690
+ ```typescript
691
+ const loginSchema = v.object({
692
+ username: v.string().min(3),
693
+ password: v.string().min(8),
694
+ rememberMe: v.optional(v.boolean())
695
+ });
696
+
697
+ type LoginForm = Infer<typeof loginSchema>;
698
+
699
+ function handleLogin(data: unknown) {
700
+ const result = loginSchema.safeParse(data);
701
+
702
+ if (result.success) {
703
+ // data is now typed as LoginForm
704
+ const { username, password, rememberMe } = result.data;
705
+ // ... handle login
706
+ } else {
707
+ // Handle validation errors
708
+ console.error(result.error);
709
+ }
710
+ }
711
+ ```
712
+
713
+ ## 🎯 Why VLD?
714
+
715
+ ### 🚀 Full Zod Feature Parity + More
716
+ VLD provides **complete Zod compatibility** with all advanced features including coercion, intersections, transformations, and object utilities - plus unique features like 27+ language internationalization.
717
+
718
+ ### ⚡ Performance First
719
+ VLD is built from the ground up with performance in mind. Every line of code is optimized for the V8 engine, resulting in validation that doesn't slow down your application.
720
+
721
+ ### 🌍 Internationalization Leader
722
+ The **only** major validation library with built-in support for 27+ languages. Perfect for global applications requiring localized error messages.
723
+
724
+ ### 🎯 Real-World Testing
725
+ Our benchmarks test real-world scenarios, not just synthetic loops. VLD excels where it matters: in actual applications where schemas are created dynamically.
726
+
727
+ ### 👨‍💻 Exceptional Developer Experience
728
+ - **Full TypeScript integration** with perfect type inference
729
+ - **Intuitive Zod-compatible API** for easy migration
730
+ - **Advanced error formatting** with tree, pretty, and flatten utilities
731
+ - **95% test coverage** ensuring reliability
732
+ - **Comprehensive documentation** with real-world examples
733
+
734
+ ### 📦 Zero Dependencies
735
+ No dependencies means smaller bundle size, fewer security concerns, and better maintainability.
736
+
737
+ ### 🔄 Migration Ready
738
+ Drop-in replacement for Zod with enhanced features:
739
+ ```typescript
740
+ // Zod syntax works perfectly
741
+ const schema = v.object({
742
+ name: v.string(),
743
+ age: v.number()
744
+ }).refine(data => data.age > 0);
745
+
746
+ // Plus VLD enhancements
747
+ schema.extend({ email: v.string().email() })
748
+ .pick('name', 'email')
749
+ .catch({ name: 'Unknown', email: 'no-email@example.com' });
750
+ ```
751
+
752
+ ## 📈 Benchmarks
753
+
754
+ Run the benchmarks yourself:
755
+
756
+ ```bash
757
+ # Standard performance comparison
758
+ npm run benchmark
759
+
760
+ # Real-world performance test (reveals the truth about benchmarks)
761
+ npm run benchmark:truth
762
+ ```
763
+
764
+ ## 🤝 Contributing
765
+
766
+ Contributions are welcome! Please feel free to submit a Pull Request.
767
+
768
+ ## 🔗 Links
769
+
770
+ - [NPM Package](https://www.npmjs.com/package/@oxog/vld)
771
+ - [GitHub Repository](https://github.com/ersinkoc/vld)
772
+ - [Documentation](https://github.com/ersinkoc/vld#readme)
773
+
774
+ ---
775
+
776
+ Made with ❤️ by Ersin KOÇ