@oxog/vld 1.4.0 → 2.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (242) hide show
  1. package/CHANGELOG.md +449 -333
  2. package/README.md +1171 -1130
  3. package/dist/chunks/bigint-ZKkWLdmi.js +1628 -0
  4. package/dist/chunks/bigint-ZKkWLdmi.js.map +1 -0
  5. package/dist/{coercion/string.js → chunks/date-7Iz2BGsL.js} +271 -5
  6. package/dist/chunks/date-7Iz2BGsL.js.map +1 -0
  7. package/dist/chunks/index-BoV-4S1F.js +894 -0
  8. package/dist/chunks/index-BoV-4S1F.js.map +1 -0
  9. package/dist/chunks/index-eWXdjyyy.js +426 -0
  10. package/dist/chunks/index-eWXdjyyy.js.map +1 -0
  11. package/dist/chunks/json-nIhyRPOU.js +1934 -0
  12. package/dist/chunks/json-nIhyRPOU.js.map +1 -0
  13. package/dist/{validators/unknown.js → chunks/unknown-CjSRcELI.js} +6 -3
  14. package/dist/chunks/unknown-CjSRcELI.js.map +1 -0
  15. package/dist/cjs/errors.cjs +387 -0
  16. package/dist/cjs/errors.cjs.map +1 -0
  17. package/dist/cjs/index.cjs +9815 -0
  18. package/dist/cjs/index.cjs.map +1 -0
  19. package/dist/cjs/locales/index.cjs +2877 -0
  20. package/dist/cjs/locales/index.cjs.map +1 -0
  21. package/dist/cjs/locales/lazy.cjs +3169 -0
  22. package/dist/cjs/locales/lazy.cjs.map +1 -0
  23. package/dist/cjs/mini.cjs +7161 -0
  24. package/dist/cjs/mini.cjs.map +1 -0
  25. package/dist/cli/bin.d.ts +8 -0
  26. package/dist/cli/bin.d.ts.map +1 -0
  27. package/dist/cli/commands/benchmark.d.ts +11 -0
  28. package/dist/cli/commands/benchmark.d.ts.map +1 -0
  29. package/dist/cli/commands/validate.d.ts +11 -0
  30. package/dist/cli/commands/validate.d.ts.map +1 -0
  31. package/dist/cli/index.d.ts +59 -0
  32. package/dist/cli/index.d.ts.map +1 -0
  33. package/dist/codecs/index.d.ts.map +1 -1
  34. package/dist/codecs/index.js +4 -256
  35. package/dist/codecs/index.js.map +1 -1
  36. package/dist/coercion/boolean.d.ts.map +1 -1
  37. package/dist/coercion/index.d.ts +11 -0
  38. package/dist/coercion/index.d.ts.map +1 -0
  39. package/dist/coercion/{bigint.js → index.js} +7 -4
  40. package/dist/coercion/index.js.map +1 -0
  41. package/dist/compat/emitter.d.ts +73 -0
  42. package/dist/compat/emitter.d.ts.map +1 -0
  43. package/dist/compat/emitter.js +181 -0
  44. package/dist/compat/emitter.js.map +1 -0
  45. package/dist/compat/index.d.ts +15 -0
  46. package/dist/compat/index.d.ts.map +1 -0
  47. package/dist/compat/result.d.ts +128 -0
  48. package/dist/compat/result.d.ts.map +1 -0
  49. package/dist/compat/result.js +171 -0
  50. package/dist/compat/result.js.map +1 -0
  51. package/dist/errors.d.ts +58 -1
  52. package/dist/errors.d.ts.map +1 -1
  53. package/dist/errors.js +106 -15
  54. package/dist/errors.js.map +1 -1
  55. package/dist/events.d.ts +140 -0
  56. package/dist/events.d.ts.map +1 -0
  57. package/dist/index.d.ts +13 -1
  58. package/dist/index.d.ts.map +1 -1
  59. package/dist/index.js +230 -103
  60. package/dist/index.js.map +1 -1
  61. package/dist/kernel.d.ts +29 -0
  62. package/dist/kernel.d.ts.map +1 -0
  63. package/dist/kernel.js +405 -0
  64. package/dist/kernel.js.map +1 -0
  65. package/dist/locales/af.js +4 -2
  66. package/dist/locales/af.js.map +1 -1
  67. package/dist/locales/ar.js +4 -2
  68. package/dist/locales/ar.js.map +1 -1
  69. package/dist/locales/bn.js +4 -2
  70. package/dist/locales/bn.js.map +1 -1
  71. package/dist/locales/da.js +4 -2
  72. package/dist/locales/da.js.map +1 -1
  73. package/dist/locales/de.js +4 -2
  74. package/dist/locales/de.js.map +1 -1
  75. package/dist/locales/en.js +4 -2
  76. package/dist/locales/en.js.map +1 -1
  77. package/dist/locales/es-MX.js +4 -2
  78. package/dist/locales/es-MX.js.map +1 -1
  79. package/dist/locales/es.js +4 -2
  80. package/dist/locales/es.js.map +1 -1
  81. package/dist/locales/fi.js +4 -2
  82. package/dist/locales/fi.js.map +1 -1
  83. package/dist/locales/fr.js +4 -2
  84. package/dist/locales/fr.js.map +1 -1
  85. package/dist/locales/hi.js +4 -2
  86. package/dist/locales/hi.js.map +1 -1
  87. package/dist/locales/id.js +4 -2
  88. package/dist/locales/id.js.map +1 -1
  89. package/dist/locales/index.js +3 -91
  90. package/dist/locales/index.js.map +1 -1
  91. package/dist/locales/it.js +4 -2
  92. package/dist/locales/it.js.map +1 -1
  93. package/dist/locales/ja.js +4 -2
  94. package/dist/locales/ja.js.map +1 -1
  95. package/dist/locales/ko.js +4 -2
  96. package/dist/locales/ko.js.map +1 -1
  97. package/dist/locales/lazy.d.ts +111 -0
  98. package/dist/locales/lazy.d.ts.map +1 -0
  99. package/dist/locales/lazy.js +220 -0
  100. package/dist/locales/lazy.js.map +1 -0
  101. package/dist/locales/nl.js +4 -2
  102. package/dist/locales/nl.js.map +1 -1
  103. package/dist/locales/no.js +4 -2
  104. package/dist/locales/no.js.map +1 -1
  105. package/dist/locales/pl.js +4 -2
  106. package/dist/locales/pl.js.map +1 -1
  107. package/dist/locales/pt-BR.js +4 -2
  108. package/dist/locales/pt-BR.js.map +1 -1
  109. package/dist/locales/pt.js +4 -2
  110. package/dist/locales/pt.js.map +1 -1
  111. package/dist/locales/ru.js +4 -2
  112. package/dist/locales/ru.js.map +1 -1
  113. package/dist/locales/sv.js +4 -2
  114. package/dist/locales/sv.js.map +1 -1
  115. package/dist/locales/sw.js +4 -2
  116. package/dist/locales/sw.js.map +1 -1
  117. package/dist/locales/th.js +4 -2
  118. package/dist/locales/th.js.map +1 -1
  119. package/dist/locales/tr.js +4 -2
  120. package/dist/locales/tr.js.map +1 -1
  121. package/dist/locales/vi.js +4 -2
  122. package/dist/locales/vi.js.map +1 -1
  123. package/dist/locales/zh.js +4 -2
  124. package/dist/locales/zh.js.map +1 -1
  125. package/dist/logger.d.ts +94 -0
  126. package/dist/logger.d.ts.map +1 -0
  127. package/dist/mini.d.ts +182 -0
  128. package/dist/mini.d.ts.map +1 -0
  129. package/dist/mini.js +132 -0
  130. package/dist/mini.js.map +1 -0
  131. package/dist/pigment.d.ts +124 -0
  132. package/dist/pigment.d.ts.map +1 -0
  133. package/dist/pigment.js +178 -0
  134. package/dist/pigment.js.map +1 -0
  135. package/dist/plugins/index.d.ts +8 -0
  136. package/dist/plugins/index.d.ts.map +1 -0
  137. package/dist/plugins/types.d.ts +186 -0
  138. package/dist/plugins/types.d.ts.map +1 -0
  139. package/dist/utils/codec-utils.d.ts.map +1 -1
  140. package/dist/utils/ip-validation.d.ts.map +1 -1
  141. package/dist/validators/base.d.ts +3 -0
  142. package/dist/validators/base.d.ts.map +1 -1
  143. package/dist/validators/index.js +5 -45
  144. package/dist/validators/index.js.map +1 -1
  145. package/dist/validators/object.d.ts.map +1 -1
  146. package/dist/validators/template-literal.d.ts.map +1 -1
  147. package/package.json +153 -79
  148. package/dist/coercion/bigint.js.map +0 -1
  149. package/dist/coercion/boolean.js +0 -67
  150. package/dist/coercion/boolean.js.map +0 -1
  151. package/dist/coercion/date.js +0 -52
  152. package/dist/coercion/date.js.map +0 -1
  153. package/dist/coercion/number.js +0 -160
  154. package/dist/coercion/number.js.map +0 -1
  155. package/dist/coercion/string.js.map +0 -1
  156. package/dist/locales/backup-en.js +0 -105
  157. package/dist/locales/backup-en.js.map +0 -1
  158. package/dist/locales/types.js +0 -2
  159. package/dist/locales/types.js.map +0 -1
  160. package/dist/utils/codec-utils.js +0 -223
  161. package/dist/utils/codec-utils.js.map +0 -1
  162. package/dist/utils/deep-merge.js +0 -76
  163. package/dist/utils/deep-merge.js.map +0 -1
  164. package/dist/utils/ip-validation.js +0 -78
  165. package/dist/utils/ip-validation.js.map +0 -1
  166. package/dist/utils/security.js +0 -61
  167. package/dist/utils/security.js.map +0 -1
  168. package/dist/validators/any.js +0 -25
  169. package/dist/validators/any.js.map +0 -1
  170. package/dist/validators/array.js +0 -190
  171. package/dist/validators/array.js.map +0 -1
  172. package/dist/validators/base.js +0 -491
  173. package/dist/validators/base.js.map +0 -1
  174. package/dist/validators/base64.js +0 -65
  175. package/dist/validators/base64.js.map +0 -1
  176. package/dist/validators/bigint.js +0 -142
  177. package/dist/validators/bigint.js.map +0 -1
  178. package/dist/validators/boolean.js +0 -114
  179. package/dist/validators/boolean.js.map +0 -1
  180. package/dist/validators/codec.js +0 -182
  181. package/dist/validators/codec.js.map +0 -1
  182. package/dist/validators/custom.js +0 -50
  183. package/dist/validators/custom.js.map +0 -1
  184. package/dist/validators/date.js +0 -227
  185. package/dist/validators/date.js.map +0 -1
  186. package/dist/validators/discriminated-union.js +0 -98
  187. package/dist/validators/discriminated-union.js.map +0 -1
  188. package/dist/validators/enum.js +0 -82
  189. package/dist/validators/enum.js.map +0 -1
  190. package/dist/validators/file.js +0 -114
  191. package/dist/validators/file.js.map +0 -1
  192. package/dist/validators/function.js +0 -45
  193. package/dist/validators/function.js.map +0 -1
  194. package/dist/validators/hex.js +0 -62
  195. package/dist/validators/hex.js.map +0 -1
  196. package/dist/validators/intersection.js +0 -64
  197. package/dist/validators/intersection.js.map +0 -1
  198. package/dist/validators/json.js +0 -64
  199. package/dist/validators/json.js.map +0 -1
  200. package/dist/validators/lazy.js +0 -42
  201. package/dist/validators/lazy.js.map +0 -1
  202. package/dist/validators/literal.js +0 -45
  203. package/dist/validators/literal.js.map +0 -1
  204. package/dist/validators/map.js +0 -54
  205. package/dist/validators/map.js.map +0 -1
  206. package/dist/validators/nan.js +0 -32
  207. package/dist/validators/nan.js.map +0 -1
  208. package/dist/validators/never.js +0 -29
  209. package/dist/validators/never.js.map +0 -1
  210. package/dist/validators/null.js +0 -31
  211. package/dist/validators/null.js.map +0 -1
  212. package/dist/validators/number.js +0 -225
  213. package/dist/validators/number.js.map +0 -1
  214. package/dist/validators/object.js +0 -458
  215. package/dist/validators/object.js.map +0 -1
  216. package/dist/validators/record.js +0 -128
  217. package/dist/validators/record.js.map +0 -1
  218. package/dist/validators/set.js +0 -51
  219. package/dist/validators/set.js.map +0 -1
  220. package/dist/validators/string-bool.js +0 -127
  221. package/dist/validators/string-bool.js.map +0 -1
  222. package/dist/validators/string-formats.js +0 -120
  223. package/dist/validators/string-formats.js.map +0 -1
  224. package/dist/validators/string.js +0 -253
  225. package/dist/validators/string.js.map +0 -1
  226. package/dist/validators/symbol.js +0 -42
  227. package/dist/validators/symbol.js.map +0 -1
  228. package/dist/validators/template-literal.js +0 -101
  229. package/dist/validators/template-literal.js.map +0 -1
  230. package/dist/validators/tuple.js +0 -55
  231. package/dist/validators/tuple.js.map +0 -1
  232. package/dist/validators/uint8array.js +0 -98
  233. package/dist/validators/uint8array.js.map +0 -1
  234. package/dist/validators/undefined.js +0 -31
  235. package/dist/validators/undefined.js.map +0 -1
  236. package/dist/validators/union.js +0 -126
  237. package/dist/validators/union.js.map +0 -1
  238. package/dist/validators/unknown.js.map +0 -1
  239. package/dist/validators/void.js +0 -35
  240. package/dist/validators/void.js.map +0 -1
  241. package/dist/validators/xor.js +0 -64
  242. package/dist/validators/xor.js.map +0 -1
package/README.md CHANGED
@@ -1,1130 +1,1171 @@
1
- # VLD - Fast & Lightweight TypeScript Validation Library
2
-
3
- [![NPM Version](https://img.shields.io/npm/v/@oxog/vld.svg)](https://www.npmjs.com/package/@oxog/vld) [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT) [![TypeScript](https://img.shields.io/badge/TypeScript-5.0+-blue.svg)](https://www.typescriptlang.org/) [![Zero Dependencies](https://img.shields.io/badge/Dependencies-0-green.svg)](package.json) [![Test Coverage](https://img.shields.io/badge/Coverage-96.55%25-brightgreen.svg)](package.json)
4
-
5
- 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.
6
-
7
- ## 📚 Table of Contents
8
-
9
- - [🚀 Features](#-features)
10
- - [📊 Performance](#-performance)
11
- - [📦 Installation](#-installation)
12
- - [🎯 Quick Start](#-quick-start)
13
- - [📖 API Reference](#-api-reference)
14
- - [🌍 Internationalization (i18n)](#-internationalization-i18n)
15
- - [⚠️ Error Handling & Formatting](#️-error-handling--formatting)
16
- - [🔥 Advanced Examples](#-advanced-examples)
17
- - [🎯 Why VLD?](#-why-vld)
18
- - [🔄 Codecs - Bidirectional Transformations](#-codecs---bidirectional-transformations)
19
- - [🔄 Migrating from Zod](#-migrating-from-zod)
20
- - [📈 Benchmarks](#-benchmarks)
21
- - [🤝 Contributing](#-contributing)
22
- - [🔗 Links](#-links)
23
-
24
- ## 🚀 Features
25
-
26
- ### Core Features
27
- - **⚡ Blazing Fast**: Optimized for V8 engine with superior performance
28
- - **🎯 Type-Safe**: Full TypeScript support with excellent type inference
29
- - **📦 Zero Dependencies**: Lightweight with no external dependencies
30
- - **🌳 Tree-Shakeable**: Only import what you need
31
- - **🔧 Composable**: Chain validations for complex schemas
32
- - **⚠️ Advanced Error Formatting**: Tree, pretty, and flatten error utilities
33
- - **🌍 Multi-language**: Built-in support for 27+ languages
34
- - **✅ 96.55% Test Coverage**: Rigorously tested with 1142 passing tests
35
- - **🏆 Industry Leading Performance**: 2.52x faster than Zod on average
36
-
37
- ### Advanced Zod-Compatible Features
38
- - **🔄 Type Coercion**: `v.coerce.string()`, `v.coerce.number()`, `v.coerce.boolean()`, etc.
39
- - **📊 Advanced Types**: BigInt, Symbol, Tuple, Record, Set, Map validation
40
- - **⚡ Intersection Types**: Combine multiple schemas with intelligent merging
41
- - **🎨 Custom Validation**: `refine()` for custom predicates and validation logic
42
- - **🔄 Data Transformation**: `transform()` for post-validation data transformation
43
- - **🏠 Default Values**: `default()` for handling undefined inputs elegantly
44
- - **🛡️ Fallback Handling**: `catch()` for graceful error recovery
45
- - **🎯 Object Utilities**: `pick()`, `omit()`, `extend()` for flexible object schemas
46
-
47
- ### 🚀 **NEW in v1.4.0** - Zod 4 Full API Parity
48
- - **🌐 `v.cidrv6()`**: IPv6 CIDR block validation
49
- - **🔗 `.apply()`**: External function chaining for advanced composition
50
- - **🛡️ `.safeExtend()`**: Type-safe object extension without accidental overrides
51
-
52
- ### 🚀 Codec System - Beyond Zod
53
- - **↔️ Bidirectional Transformations**: Full encode/decode support for data conversion
54
- - **📦 19 Built-in Codecs**: String conversions, date parsing, JSON, URL, binary data
55
- - **🔗 Zod-Compatible**: All `stringToNumber`, `jsonCodec`, `base64ToBytes`, etc.
56
- - **⚡ Async Support**: Both sync and async codec operations
57
- - **🛠 Custom Codecs**: Create your own bidirectional transformations
58
- - **🎯 Type-Safe**: Full TypeScript support with perfect type inference
59
-
60
- ## 📊 Performance
61
-
62
- VLD is designed for speed and efficiency with recent optimizations delivering exceptional performance:
63
-
64
- ### Speed Benchmarks (v1.0.0 - Optimized)
65
- - **4.6x faster** for number validation with constraints
66
- - **3.6x faster** for union type validation
67
- - **2.5x faster** for email validation
68
- - **1.9x faster** for array validation
69
- - **1.7x faster** for primitive string validation
70
- - **2.8x faster** overall average performance
71
-
72
- ### Recent Optimizations (v1.0.0)
73
- - **110x improvement** in union type validation
74
- - **Simplified email regex** for maximum performance
75
- - **Inline type checks** in object validation
76
- - **Optimized loops** with direct array assignment
77
- - **SafeParse optimization** to avoid try-catch overhead
78
- - **Pre-computed keys** with Set for O(1) lookups
79
-
80
- ### Memory Efficiency
81
- - **98% less memory** for validator creation
82
- - **51% less memory** for data parsing
83
- - **86% less memory** for error handling
84
- - **78% less memory** overall average
85
-
86
- ### A Note on Real-World Benchmarking
87
-
88
- Many validation library benchmarks can be misleading because they often test with **reused schema instances**:
89
-
90
- ```javascript
91
- // What benchmarks typically test (unrealistic):
92
- const schema = z.string();
93
- for (let i = 0; i < 1000000; i++) {
94
- schema.parse(data); // Same instance reused
95
- }
96
-
97
- // What happens in real applications:
98
- app.post('/api/user', (req, res) => {
99
- // New schema created for each request
100
- const schema = z.object({
101
- email: z.string().email(),
102
- age: z.number().min(18)
103
- });
104
- schema.parse(req.body);
105
- });
106
- ```
107
-
108
- When testing real-world patterns:
109
- - **Creating new instances**: VLD is **2000x faster** than Zod
110
- - **Reused instances**: Zod benefits from V8's singleton optimization
111
- - **Real applications**: Schemas are often created dynamically, where VLD excels
112
-
113
- Run `npm run benchmark:truth` to see the real performance difference.
114
-
115
- ## 📦 Installation
116
-
117
- ```bash
118
- npm install @oxog/vld
119
- # or
120
- yarn add @oxog/vld
121
- # or
122
- pnpm add @oxog/vld
123
- ```
124
-
125
- ## 🎯 Quick Start
126
-
127
- ```typescript
128
- import { v } from '@oxog/vld';
129
-
130
- // It is recommended to import as `v` for consistency with Zod's `z`
131
- // and for a more concise syntax.
132
-
133
- // Define a schema
134
- const userSchema = v.object({
135
- name: v.string().min(2),
136
- email: v.string().email(),
137
- age: v.number().min(18).max(100),
138
- isActive: v.boolean()
139
- });
140
-
141
- // Validate data
142
- const result = userSchema.safeParse({
143
- name: 'John Doe',
144
- email: 'john@example.com',
145
- age: 25,
146
- isActive: true
147
- });
148
-
149
- if (result.success) {
150
- console.log('Valid user:', result.data);
151
- } else {
152
- console.log('Validation error:', result.error);
153
- }
154
- ```
155
-
156
- For advanced error formatting:
157
- ```typescript
158
- import { v, VldError, treeifyError, prettifyError, flattenError } from '@oxog/vld';
159
- ```
160
-
161
- ## 📖 API Reference
162
-
163
- ### Basic Types
164
-
165
- ```typescript
166
- v.string() // String validation
167
- v.number() // Number validation
168
- v.boolean() // Boolean validation
169
- v.bigint() // BigInt validation
170
- v.symbol() // Symbol validation
171
- v.date() // Date validation
172
- v.uint8array()// Uint8Array validation
173
- v.literal() // Literal values
174
- v.enum() // Enum values (supports TypeScript enums)
175
- v.any() // Any type
176
- v.unknown() // Unknown type
177
- v.void() // Void type
178
- v.never() // Never type
179
- ```
180
-
181
- ### Advanced Types
182
-
183
- ```typescript
184
- // Collections
185
- v.array(v.string()) // Array validation
186
- v.tuple(v.string(), v.number()) // Fixed-length tuple
187
- v.record(v.number()) // Record/dictionary validation
188
- v.set(v.string()) // Set validation
189
- v.map(v.string(), v.number()) // Map validation
190
-
191
- // Objects
192
- v.object({ // Object schema
193
- name: v.string(),
194
- age: v.number()
195
- })
196
-
197
- // Composition
198
- v.union(v.string(), v.number()) // Union types
199
- v.intersection(schemaA, schemaB) // Intersection types
200
- v.optional(v.string()) // Optional fields
201
- v.nullable(v.string()) // Nullable fields
202
- ```
203
-
204
- ### String Validators
205
-
206
- ```typescript
207
- v.string()
208
- .min(5) // Minimum length
209
- .max(10) // Maximum length
210
- .length(8) // Exact length
211
- .email() // Email format
212
- .url() // URL format
213
- .uuid() // UUID format
214
- .regex(/pattern/) // Custom regex
215
- .startsWith('prefix') // String prefix
216
- .endsWith('suffix') // String suffix
217
- .includes('substring') // Contains substring
218
- .ip() // IP address (v4 or v6)
219
- .trim() // Trim whitespace
220
- .toLowerCase() // Convert to lowercase
221
- .toUpperCase() // Convert to uppercase
222
- .nonempty() // Non-empty string
223
- ```
224
-
225
- ### Number Validators
226
-
227
- ```typescript
228
- v.number()
229
- .min(0) // Minimum value
230
- .max(100) // Maximum value
231
- .int() // Integer only
232
- .positive() // Positive numbers
233
- .negative() // Negative numbers
234
- .nonnegative() // >= 0
235
- .nonpositive() // <= 0
236
- .finite() // Finite numbers
237
- .safe() // Safe integers
238
- .multipleOf(5) // Multiple of value
239
- ```
240
-
241
- ### Arrays and Objects
242
-
243
- ```typescript
244
- // Arrays
245
- v.array(v.string()) // Array of strings
246
- .min(1) // Minimum length
247
- .max(10) // Maximum length
248
- .length(5) // Exact length
249
- .nonempty() // Non-empty array
250
-
251
- // Objects
252
- v.object({
253
- name: v.string(),
254
- age: v.number()
255
- })
256
- .partial() // All fields optional
257
- .strict() // No extra fields
258
- ```
259
-
260
- ### Composite Types
261
-
262
- ```typescript
263
- // Optional
264
- v.optional(v.string()) // string | undefined
265
-
266
- // Nullable
267
- v.nullable(v.string()) // string | null
268
-
269
- // Union
270
- v.union(v.string(), v.number()) // string | number
271
-
272
- // Literal
273
- v.literal('active') // 'active'
274
-
275
- // Enum
276
- v.enum('red', 'green', 'blue') // 'red' | 'green' | 'blue'
277
- ```
278
-
279
- ### Type Coercion
280
-
281
- ```typescript
282
- // Coerce strings from various types
283
- v.coerce.string().parse(123) // "123"
284
- v.coerce.string().parse(true) // "true"
285
-
286
- // Coerce numbers from strings/booleans
287
- v.coerce.number().parse("123") // 123
288
- v.coerce.number().parse(true) // 1
289
-
290
- // Coerce booleans from strings/numbers
291
- v.coerce.boolean().parse("true") // true
292
- v.coerce.boolean().parse(1) // true
293
-
294
- // Coerce BigInt from strings/numbers
295
- v.coerce.bigint().parse("123") // 123n
296
- v.coerce.bigint().parse(456) // 456n
297
-
298
- // Coerce Date from strings/timestamps
299
- v.coerce.date().parse("2023-01-01") // Date object
300
- v.coerce.date().parse(1672531200000) // Date object
301
- ```
302
-
303
- ### Object Schema Methods
304
-
305
- ```typescript
306
- const userSchema = v.object({
307
- name: v.string(),
308
- age: v.number(),
309
- email: v.string(),
310
- role: v.string()
311
- });
312
-
313
- // Pick specific fields
314
- const publicSchema = userSchema.pick('name', 'age');
315
- // Type: { name: string; age: number }
316
-
317
- // Omit sensitive fields
318
- const safeSchema = userSchema.omit('email', 'role');
319
- // Type: { name: string; age: number }
320
-
321
- // Extend with new fields
322
- const extendedSchema = userSchema.extend({
323
- isActive: v.boolean(),
324
- lastLogin: v.date()
325
- });
326
- // Type: { name: string; age: number; email: string; role: string; isActive: boolean; lastLogin: Date }
327
- ```
328
-
329
- ### Advanced Validation Methods
330
-
331
- ```typescript
332
- // Custom validation with refine()
333
- const positiveNumber = v.number()
334
- .refine(n => n > 0, "Number must be positive");
335
-
336
- // Data transformation with transform()
337
- const uppercaseString = v.string()
338
- .transform(s => s.toUpperCase());
339
-
340
- // Default values for undefined
341
- const withDefault = v.string().default("fallback");
342
- withDefault.parse(undefined); // "fallback"
343
-
344
- // Catch errors and provide fallback
345
- const withCatch = v.number().catch(-1);
346
- withCatch.parse("invalid"); // -1
347
-
348
- // Method chaining
349
- const complexSchema = v.string()
350
- .min(3)
351
- .transform(s => s.trim())
352
- .refine(s => s.includes('@'), 'Must contain @')
353
- .default('user@example.com');
354
- ```
355
-
356
- ### Type Inference
357
-
358
- ```typescript
359
- import { v, Infer } from '@oxog/vld';
360
-
361
- const schema = v.object({
362
- name: v.string(),
363
- age: v.number()
364
- });
365
-
366
- // Automatically infer the type
367
- type User = Infer<typeof schema>;
368
- // { name: string; age: number }
369
- ```
370
-
371
- ### Error Formatting Types
372
-
373
- ```typescript
374
- import {
375
- VldError, // Main error class
376
- VldIssue, // Individual validation issue
377
- VldErrorTree, // Nested error structure
378
- VldFlattenedError // Flattened error structure
379
- } from '@oxog/vld';
380
- ```
381
-
382
- ### Custom Error Messages
383
-
384
- ```typescript
385
- const schema = v.string().min(8, 'Password must be at least 8 characters');
386
-
387
- const result = schema.safeParse('short');
388
- if (!result.success) {
389
- console.log(result.error.message); // 'Password must be at least 8 characters'
390
- }
391
- ```
392
-
393
- ## 🌍 Internationalization (i18n)
394
-
395
- VLD supports 27+ languages out of the box with comprehensive error messages:
396
-
397
- ```typescript
398
- import { v, setLocale } from '@oxog/vld';
399
-
400
- // Default is English
401
- const schema = v.string().min(5);
402
- schema.safeParse('Hi'); // Error: "String must be at least 5 characters"
403
-
404
- // Switch to Turkish
405
- setLocale('tr');
406
- schema.safeParse('Hi'); // Error: "Metin en az 5 karakter olmalı"
407
-
408
- // Switch to Spanish
409
- setLocale('es');
410
- schema.safeParse('Hi'); // Error: "La cadena debe tener al menos 5 caracteres"
411
-
412
- // Switch to Japanese
413
- setLocale('ja');
414
- schema.safeParse('Hi'); // Error: "文字列は5文字以上である必要があります"
415
- ```
416
-
417
- ### Supported Languages
418
-
419
- #### Base Languages (15):
420
- - 🇬🇧 English (`en`) - 🇹🇷 Turkish (`tr`) - 🇪🇸 Spanish (`es`) - 🇫🇷 French (`fr`) - 🇩🇪 German (`de`)
421
- - 🇮🇹 Italian (`it`) - 🇵🇹 Portuguese (`pt`) - 🇷🇺 Russian (`ru`) - 🇯🇵 Japanese (`ja`) - 🇰🇷 Korean (`ko`)
422
- - 🇨🇳 Chinese (`zh`) - 🇸🇦 Arabic (`ar`) - 🇮🇳 Hindi (`hi`) - 🇳🇱 Dutch (`nl`) - 🇵🇱 Polish (`pl`)
423
-
424
- #### European Languages (4):
425
- - 🇩🇰 Danish (`da`) - 🇸🇪 Swedish (`sv`) - 🇳🇴 Norwegian (`no`) - 🇫🇮 Finnish (`fi`)
426
-
427
- #### Asian Languages (4):
428
- - 🇹🇭 Thai (`th`) - 🇻🇳 Vietnamese (`vi`) - 🇮🇩 Indonesian (`id`) - 🇧🇩 Bengali (`bn`)
429
-
430
- #### African Languages (2):
431
- - 🇰🇪 Swahili (`sw`) - 🇿🇦 Afrikaans (`af`)
432
-
433
- #### American Languages (2):
434
- - 🇧🇷 Portuguese Brazil (`pt-BR`) - 🇲🇽 Spanish Mexico (`es-MX`)
435
-
436
- **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!
437
-
438
- ## ⚠️ Error Handling & Formatting
439
-
440
- 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.
441
-
442
- ### Error Formatting Utilities
443
-
444
- ```typescript
445
- import { v, VldError, treeifyError, prettifyError, flattenError } from '@oxog/vld';
446
-
447
- // Note: Error formatting utilities like `treeifyError` are separate named exports
448
- // and are not part of the main `v` object.
449
-
450
- const userSchema = v.object({
451
- username: v.string().min(3),
452
- favoriteNumbers: v.array(v.number()),
453
- profile: v.object({
454
- name: v.string(),
455
- email: v.string().email()
456
- })
457
- });
458
-
459
- // This will fail validation
460
- const result = userSchema.safeParse({
461
- username: 'ab', // too short
462
- favoriteNumbers: [1, 'two', 3], // 'two' is not a number
463
- profile: {
464
- name: '',
465
- email: 'invalid-email'
466
- },
467
- extraField: 'not allowed'
468
- });
469
-
470
- if (!result.success) {
471
- const error = result.error as VldError;
472
-
473
- // 1. Tree Format - Nested structure for complex UIs
474
- const tree = treeifyError(error);
475
- console.log(tree);
476
- /*
477
- {
478
- errors: ['Unrecognized key: "extraField"'],
479
- properties: {
480
- username: { errors: ['String must be at least 3 characters'] },
481
- favoriteNumbers: {
482
- items: [
483
- undefined,
484
- { errors: ['Expected number, received string'] },
485
- undefined
486
- ]
487
- },
488
- profile: {
489
- properties: {
490
- name: { errors: ['String cannot be empty'] },
491
- email: { errors: ['Invalid email format'] }
492
- }
493
- }
494
- }
495
- }
496
- */
497
-
498
- // 2. Pretty Format - Human-readable console output
499
- const pretty = prettifyError(error);
500
- console.log(pretty);
501
- /*
502
- Unrecognized key: "extraField"
503
- String must be at least 3 characters
504
- → at username
505
- Expected number, received string
506
- at favoriteNumbers[1]
507
- ✖ String cannot be empty
508
- → at profile.name
509
- ✖ Invalid email format
510
- at profile.email
511
- */
512
-
513
- // 3. Flatten Format - Simple form validation
514
- const flattened = flattenError(error);
515
- console.log(flattened);
516
- /*
517
- {
518
- formErrors: ['Unrecognized key: "extraField"'],
519
- fieldErrors: {
520
- username: ['String must be at least 3 characters'],
521
- favoriteNumbers: ['Expected number, received string'],
522
- profile: ['String cannot be empty', 'Invalid email format']
523
- }
524
- }
525
- */
526
- }
527
- ```
528
-
529
- ### Using Error Formats in Practice
530
-
531
- #### React Form Validation
532
- ```typescript
533
- function UserForm() {
534
- const [errors, setErrors] = useState<VldFlattenedError | null>(null);
535
-
536
- const handleSubmit = (data: unknown) => {
537
- const result = userSchema.safeParse(data);
538
-
539
- if (!result.success) {
540
- setErrors(flattenError(result.error as VldError));
541
- } else {
542
- setErrors(null);
543
- // Process valid data
544
- }
545
- };
546
-
547
- return (
548
- <form onSubmit={handleSubmit}>
549
- {errors?.formErrors.map(error => (
550
- <div key={error} className="form-error">{error}</div>
551
- ))}
552
-
553
- <input name="username" />
554
- {errors?.fieldErrors.username?.map(error => (
555
- <div key={error} className="field-error">{error}</div>
556
- ))}
557
- </form>
558
- );
559
- }
560
- ```
561
-
562
- #### API Error Responses
563
- ```typescript
564
- app.post('/api/users', (req, res) => {
565
- const result = userSchema.safeParse(req.body);
566
-
567
- if (!result.success) {
568
- const tree = treeifyError(result.error as VldError);
569
- res.status(400).json({
570
- error: 'Validation failed',
571
- details: tree
572
- });
573
- } else {
574
- // Process valid user data
575
- }
576
- });
577
- ```
578
-
579
- #### Console Debugging
580
- ```typescript
581
- function validateAndLog(data: unknown) {
582
- const result = userSchema.safeParse(data);
583
-
584
- if (!result.success) {
585
- console.log('Validation failed:');
586
- console.log(prettifyError(result.error as VldError));
587
- }
588
- }
589
- ```
590
-
591
- ## 🔥 Advanced Examples
592
-
593
- ### Complex Validation with New Features
594
-
595
- ```typescript
596
- const postSchema = v.object({
597
- id: v.union(v.string().uuid(), v.number()),
598
- title: v.string().min(5).max(100),
599
- content: v.string().min(10),
600
- author: v.object({
601
- name: v.string(),
602
- email: v.string().email(),
603
- age: v.coerce.number(), // Auto-convert to number
604
- }),
605
- tags: v.set(v.string()).default(new Set()), // Use Set instead of array
606
- metadata: v.record(v.any()), // Key-value metadata
607
- coordinates: v.tuple(v.number(), v.number()), // [lat, lng]
608
- publishedAt: v.date().default(() => new Date()),
609
- status: v.enum('draft', 'published', 'archived')
610
- });
611
-
612
- // Extend with additional fields
613
- const blogPostSchema = postSchema.extend({
614
- viewCount: v.bigint().default(0n),
615
- categories: v.array(v.string()).min(1),
616
- featured: v.boolean().default(false)
617
- });
618
-
619
- // Create a public version without sensitive data
620
- const publicPostSchema = blogPostSchema
621
- .omit('author')
622
- .extend({
623
- authorName: v.string()
624
- });
625
- ```
626
-
627
- ### Advanced Transformations & Validation
628
-
629
- ```typescript
630
- // Complex email processing with coercion and transformation
631
- const emailSchema = v.coerce.string()
632
- .transform(s => s.toLowerCase().trim())
633
- .refine(s => s.includes('@'), 'Must be valid email format')
634
- .transform(s => s.replace(/\+.*@/, '@')) // Remove plus addressing
635
- .catch('invalid@example.com');
636
-
637
- // Process user input with fallbacks
638
- const userInputSchema = v.object({
639
- name: v.coerce.string()
640
- .transform(s => s.trim())
641
- .refine(s => s.length > 0, 'Name cannot be empty')
642
- .default('Anonymous'),
643
-
644
- age: v.coerce.number()
645
- .refine(n => n >= 0 && n <= 150, 'Age must be realistic')
646
- .catch(0),
647
-
648
- preferences: v.record(v.any()).default({}),
649
-
650
- tags: v.union(
651
- v.array(v.string()),
652
- v.coerce.string().transform(s => s.split(','))
653
- ).default([])
654
- });
655
-
656
- // Intersection for combining user types
657
- const baseUser = v.object({
658
- id: v.string(),
659
- name: v.string()
660
- });
661
-
662
- const adminUser = v.object({
663
- role: v.literal('admin'),
664
- permissions: v.array(v.string())
665
- });
666
-
667
- const adminSchema = v.intersection(baseUser, adminUser);
668
- ```
669
-
670
- ### Collection Validation
671
-
672
- ```typescript
673
- // Advanced tuple validation
674
- const coordinatesSchema = v.tuple(
675
- v.number().min(-90).max(90), // latitude
676
- v.number().min(-180).max(180), // longitude
677
- v.number().positive().optional() // altitude
678
- );
679
-
680
- // Map validation for configuration
681
- const configSchema = v.map(
682
- v.string().min(1), // keys must be non-empty strings
683
- v.union(v.string(), v.number(), v.boolean()) // values can be mixed types
684
- );
685
-
686
- // Set validation for unique tags
687
- const uniqueTagsSchema = v.set(v.string().min(1).max(20))
688
- .refine(tags => tags.size <= 10, 'Too many tags');
689
- ```
690
-
691
- ### Real-world API Schema
692
-
693
- ```typescript
694
- // Complete API endpoint schema with all features
695
- const apiUserSchema = v.object({
696
- // Basic info with coercion
697
- id: v.coerce.string(),
698
- username: v.string()
699
- .min(3)
700
- .max(20)
701
- .refine(s => /^[a-zA-Z0-9_]+$/.test(s), 'Invalid username format'),
702
-
703
- email: v.coerce.string()
704
- .transform(s => s.toLowerCase().trim())
705
- .refine(s => s.includes('@'), 'Invalid email'),
706
-
707
- // Age with fallback
708
- age: v.coerce.number()
709
- .min(13)
710
- .max(120)
711
- .catch(null),
712
-
713
- // Preferences as key-value store
714
- preferences: v.record(v.any()).default({}),
715
-
716
- // Roles as a set for uniqueness
717
- roles: v.set(v.enum('user', 'admin', 'moderator'))
718
- .default(new Set(['user'])),
719
-
720
- // Metadata with BigInt support
721
- createdAt: v.coerce.date(),
722
- userId: v.coerce.bigint(),
723
-
724
- // Optional complex nested data
725
- profile: v.object({
726
- bio: v.string().max(500).default(''),
727
- location: v.tuple(v.number(), v.number()).optional(),
728
- socialLinks: v.record(v.string().url()).default({})
729
- }).optional()
730
- });
731
-
732
- // Specialized schemas using pick/omit
733
- const publicUserSchema = apiUserSchema.pick('username', 'profile');
734
- const adminUserSchema = apiUserSchema.extend({
735
- adminNotes: v.string().optional(),
736
- lastLogin: v.date().optional()
737
- });
738
- ```
739
-
740
- ### Type-Safe Forms
741
-
742
- ```typescript
743
- const loginSchema = v.object({
744
- username: v.string().min(3),
745
- password: v.string().min(8),
746
- rememberMe: v.optional(v.boolean())
747
- });
748
-
749
- type LoginForm = Infer<typeof loginSchema>;
750
-
751
- function handleLogin(data: unknown) {
752
- const result = loginSchema.safeParse(data);
753
-
754
- if (result.success) {
755
- // data is now typed as LoginForm
756
- const { username, password, rememberMe } = result.data;
757
- // ... handle login
758
- } else {
759
- // Handle validation errors
760
- console.error(result.error);
761
- }
762
- }
763
- ```
764
-
765
- ## 🆚 VLD vs. Zod
766
-
767
- VLD is designed as a compelling alternative to Zod, offering full feature parity while delivering significant improvements in performance, bundle size, and internationalization.
768
-
769
- ### Feature Comparison
770
-
771
- | Feature | VLD | Zod |
772
- | ----------------------- | ---------------------------------- | ------------------------------------ |
773
- | **Performance** | **~2.07x faster** (average) | Baseline |
774
- | **Memory Usage** | **~78% less** overall | Baseline |
775
- | **Internationalization**| **Built-in (27+ languages)** | ❌ Requires third-party library |
776
- | **Dependencies** | **Zero** | `zod-i18n` for locales |
777
- | **Bundle Size** | Smaller | Larger |
778
- | **API** | 100% Zod-compatible | Standard Zod API |
779
- | **Codecs** | ✅ Built-in, bidirectional | ✅ Via external `zod-codecs` |
780
- | **Error Formatting** | ✅ Advanced (tree, pretty, flatten)| ✅ Advanced (tree, pretty, flatten)|
781
- | **Type Inference** | ✅ Excellent | ✅ Excellent |
782
-
783
- ### 🔄 Seamless Migration from Zod
784
-
785
- Migration is straightforward due to 100% API compatibility. You can typically just swap the import statement.
786
-
787
- ```javascript
788
- // Before (Zod)
789
- import { z } from 'zod';
790
- const schema = z.string().email();
791
-
792
- // After (VLD) - Exact same syntax!
793
- import { v } from '@oxog/vld';
794
- const schema = v.string().email();
795
- ```
796
-
797
- ## 🔄 Codecs - Bidirectional Transformations
798
-
799
- VLD introduces **codecs** - powerful bidirectional transformations that can convert data between different representations. Unlike simple transformations, codecs can both **decode** (input → output) and **encode** (output → input).
800
-
801
- ### 🎯 What are Codecs?
802
-
803
- Codecs enable safe, type-checked conversions between different data formats. They're perfect for:
804
- - **API boundaries**: Convert strings to structured data
805
- - **Database serialization**: Transform objects to/from storage formats
806
- - **Network protocols**: Handle data encoding/decoding
807
- - **Configuration parsing**: Convert config strings to typed values
808
-
809
- ### 📦 Built-in Codecs
810
-
811
- VLD provides all Zod-compatible codecs plus additional utilities:
812
-
813
- #### **String Conversion Codecs**
814
-
815
- ```typescript
816
- import { stringToNumber, stringToInt, stringToBigInt, stringToBoolean } from '@oxog/vld';
817
-
818
- // String to number conversion
819
- const age = stringToNumber.parse('25'); // 25
820
- const price = stringToNumber.encode(99.99); // "99.99"
821
-
822
- // String to integer (validates integer constraint)
823
- const count = stringToInt.parse('42'); // 42
824
- stringToInt.parse('42.5'); // ❌ Validation error: must be integer
825
-
826
- // String to BigInt for large numbers
827
- const bigNum = stringToBigInt.parse('123456789012345678901234567890'); // 123456789012345678901234567890n
828
-
829
- // String to boolean (flexible parsing)
830
- stringToBoolean.parse('true'); // true
831
- stringToBoolean.parse('1'); // true
832
- stringToBoolean.parse('yes'); // true
833
- stringToBoolean.parse('on'); // true
834
- stringToBoolean.parse('false'); // false
835
- stringToBoolean.parse('0'); // false
836
- ```
837
-
838
- #### **Date Conversion Codecs**
839
-
840
- ```typescript
841
- import { isoDatetimeToDate, epochSecondsToDate, epochMillisToDate } from '@oxog/vld';
842
-
843
- // ISO datetime string to Date
844
- const date1 = isoDatetimeToDate.parse('2023-12-25T10:30:00.000Z');
845
- console.log(date1.toISOString()); // "2023-12-25T10:30:00.000Z"
846
-
847
- // Unix epoch seconds to Date
848
- const date2 = epochSecondsToDate.parse(1703505000);
849
- console.log(date2.getFullYear()); // 2023
850
-
851
- // Unix epoch milliseconds to Date
852
- const date3 = epochMillisToDate.parse(1703505000000);
853
- console.log(date3.getMonth()); // 11 (December)
854
-
855
- // All support bidirectional conversion
856
- const backToEpoch = epochSecondsToDate.encode(new Date()); // Unix timestamp
857
- ```
858
-
859
- #### **JSON and Complex Data Codecs**
860
-
861
- ```typescript
862
- import { jsonCodec, base64Json } from '@oxog/vld';
863
-
864
- // Generic JSON codec
865
- const userJson = jsonCodec();
866
- const user = userJson.parse('{"name":"John","age":30}'); // { name: "John", age: 30 }
867
- const jsonString = userJson.encode(user); // '{"name":"John","age":30}'
868
-
869
- // JSON codec with schema validation
870
- const userSchema = v.object({
871
- name: v.string(),
872
- age: v.number()
873
- });
874
- const typedJsonCodec = jsonCodec(userSchema);
875
- const validatedUser = typedJsonCodec.parse('{"name":"John","age":30}'); // Fully typed!
876
-
877
- // Base64-encoded JSON
878
- const b64JsonCodec = base64Json(userSchema);
879
- const encoded = b64JsonCodec.encode({ name: "Alice", age: 25 }); // Base64 string
880
- const decoded = b64JsonCodec.parse(encoded); // { name: "Alice", age: 25 }
881
- ```
882
-
883
- #### **URL and Web Codecs**
884
-
885
- ```typescript
886
- import { stringToURL, stringToHttpURL, uriComponent } from '@oxog/vld';
887
-
888
- // String to URL object
889
- const url = stringToURL.parse('https://example.com/path?param=value');
890
- console.log(url.hostname); // "example.com"
891
- console.log(url.searchParams.get('param')); // "value"
892
-
893
- // Restrict to HTTP/HTTPS only
894
- const httpUrl = stringToHttpURL.parse('https://api.example.com');
895
- stringToHttpURL.parse('ftp://files.example.com'); // Error: Must be HTTP/HTTPS
896
-
897
- // URI component encoding/decoding
898
- const encoded = uriComponent.parse('Hello World! 🚀'); // "Hello%20World!%20%F0%9F%9A%80"
899
- const decoded = uriComponent.encode(encoded); // "Hello World! 🚀"
900
- ```
901
-
902
- #### **Binary Data Codecs**
903
-
904
- ```typescript
905
- import { base64ToBytes, hexToBytes, utf8ToBytes, bytesToUtf8 } from '@oxog/vld';
906
-
907
- // Base64 to byte array
908
- const bytes1 = base64ToBytes.parse('SGVsbG8gV29ybGQ='); // Uint8Array([72, 101, 108, 108, 111, 32, 87, 111, 114, 108, 100])
909
-
910
- // Hex to byte array
911
- const bytes2 = hexToBytes.parse('48656c6c6f'); // Uint8Array([72, 101, 108, 108, 111])
912
-
913
- // UTF-8 string to bytes
914
- const bytes3 = utf8ToBytes.parse('Hello! 👋'); // Uint8Array([...])
915
-
916
- // Bytes to UTF-8 string
917
- const text = bytesToUtf8.parse(bytes3); // "Hello! 👋"
918
-
919
- // All support round-trip conversion
920
- const original = 'Hello World!';
921
- const roundTrip = bytesToUtf8.parse(utf8ToBytes.parse(original)); // "Hello World!"
922
- ```
923
-
924
- ### 🛠 Custom Codecs
925
-
926
- Create your own codecs for specific use cases:
927
-
928
- ```typescript
929
- import { v } from '@oxog/vld';
930
-
931
- // Custom CSV to array codec
932
- const csvToArray = v.codec(
933
- v.string(), // Input: CSV string
934
- v.array(v.string()), // Output: Array of strings
935
- {
936
- decode: (csv: string) => csv.split(',').map(s => s.trim()),
937
- encode: (arr: string[]) => arr.join(', ')
938
- }
939
- );
940
-
941
- const tags = csvToArray.parse('react, typescript, vld'); // ["react", "typescript", "vld"]
942
- const csvString = csvToArray.encode(['node', 'express', 'api']); // "node, express, api"
943
-
944
- // Complex: Environment config codec
945
- const envConfigCodec = v.codec(
946
- v.string(),
947
- v.object({
948
- port: v.number(),
949
- debug: v.boolean(),
950
- dbUrl: v.string()
951
- }),
952
- {
953
- decode: (envString: string) => {
954
- const config = {};
955
- envString.split('\n').forEach(line => {
956
- const [key, value] = line.split('=');
957
- if (key === 'PORT') config.port = parseInt(value, 10);
958
- if (key === 'DEBUG') config.debug = value === 'true';
959
- if (key === 'DB_URL') config.dbUrl = value;
960
- });
961
- return config;
962
- },
963
- encode: (config) => [
964
- `PORT=${config.port}`,
965
- `DEBUG=${config.debug}`,
966
- `DB_URL=${config.dbUrl}`
967
- ].join('\n')
968
- }
969
- );
970
- ```
971
-
972
- ### 🚀 Advanced Codec Features
973
-
974
- #### **Async Codecs**
975
- ```typescript
976
- const asyncCodec = v.codec(
977
- v.string(),
978
- v.object({ data: v.string() }),
979
- {
980
- decode: async (str: string) => {
981
- // Simulate API call
982
- const response = await fetch(`/api/decode?data=${str}`);
983
- return response.json();
984
- },
985
- encode: async (obj) => {
986
- const response = await fetch('/api/encode', {
987
- method: 'POST',
988
- body: JSON.stringify(obj)
989
- });
990
- return response.text();
991
- }
992
- }
993
- );
994
-
995
- // Use async methods
996
- const result = await asyncCodec.parseAsync('input-data');
997
- const encoded = await asyncCodec.encodeAsync({ data: 'output' });
998
- ```
999
-
1000
- #### **Error Handling**
1001
- ```typescript
1002
- const safeParseResult = stringToNumber.safeParse('not-a-number');
1003
- if (!safeParseResult.success) {
1004
- console.error('Parse failed:', safeParseResult.error.message);
1005
- }
1006
-
1007
- const safeEncodeResult = stringToNumber.safeEncode('invalid-input');
1008
- if (!safeEncodeResult.success) {
1009
- console.error('Encode failed:', safeEncodeResult.error.message);
1010
- }
1011
- ```
1012
-
1013
- #### **JWT Payload Decoder**
1014
- ```typescript
1015
- import { jwtPayload } from '@oxog/vld';
1016
-
1017
- // Decode JWT payload (read-only)
1018
- const payloadSchema = v.object({
1019
- sub: v.string(),
1020
- name: v.string(),
1021
- iat: v.number()
1022
- });
1023
-
1024
- const decoder = jwtPayload(payloadSchema);
1025
- const token = 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c';
1026
-
1027
- const payload = decoder.parse(token);
1028
- console.log(payload.name); // "John Doe"
1029
- console.log(payload.sub); // "1234567890"
1030
- ```
1031
-
1032
- ### 🎯 Codec vs Transform
1033
-
1034
- | Feature | Codec | Transform |
1035
- |---------|--------|-----------|
1036
- | **Direction** | Bidirectional (encode/decode) | Unidirectional (transform only) |
1037
- | **Type Safety** | Input and output validation | Output validation only |
1038
- | **Use Case** | Data serialization, API boundaries | Data cleaning, formatting |
1039
- | **Performance** | Optimized for round-trips | Optimized for single direction |
1040
-
1041
- ```typescript
1042
- // Transform: One-way conversion
1043
- const upperCase = v.string().transform(s => s.toUpperCase());
1044
- const result = upperCase.parse('hello'); // "HELLO"
1045
- // No way to get back to "hello"
1046
-
1047
- // Codec: Two-way conversion
1048
- const upperCaseCodec = v.codec(
1049
- v.string(),
1050
- v.string(),
1051
- {
1052
- decode: s => s.toUpperCase(),
1053
- encode: s => s.toLowerCase()
1054
- }
1055
- );
1056
- const encoded = upperCaseCodec.parse('hello'); // "HELLO"
1057
- const original = upperCaseCodec.encode('HELLO'); // "hello"
1058
- ```
1059
-
1060
- ## 🔄 Migrating from Zod
1061
-
1062
- VLD provides 100% feature parity with Zod, making migration seamless:
1063
-
1064
- ### Simple Migration
1065
- ```javascript
1066
- // Before (Zod)
1067
- import { z } from 'zod';
1068
- const schema = z.string().email();
1069
-
1070
- // After (VLD) - Exact same syntax!
1071
- import { v } from '@oxog/vld';
1072
- const schema = v.string().email();
1073
- ```
1074
-
1075
- ### Why Migrate?
1076
- - **⚡ Performance**: 2-4x faster for most operations
1077
- - **💾 Memory**: Uses 1.18-1.82x less memory than Zod
1078
- - **🌍 Internationalization**: Built-in 27+ language support
1079
- - **📦 Bundle Size**: Smaller with zero dependencies
1080
- - **🔒 Security**: Immutable validators prevent memory leaks
1081
- - **✅ Testing**: 96.55% test coverage with 1142 tests
1082
-
1083
- ## 📈 Benchmarks
1084
-
1085
- ### Performance Results
1086
-
1087
- Latest benchmark results show VLD consistently outperforming Zod:
1088
-
1089
- | Test Case | VLD Performance | Improvement |
1090
- |-----------|----------------|-------------|
1091
- | Simple String | 44.4M ops/sec | **1.67x faster** |
1092
- | Email Validation | 18.6M ops/sec | **3.63x faster** |
1093
- | Number Validation | 22.7M ops/sec | **2.62x faster** |
1094
- | Object Validation | 7.6M ops/sec | **1.27x faster** |
1095
- | Array Validation | 6.7M ops/sec | **1.29x faster** |
1096
- | Union Types | 6.8M ops/sec | **1.54x faster** |
1097
- | Optional Values | 32.7M ops/sec | **4.52x faster** |
1098
- | Type Coercion | 18.4M ops/sec | **1.46x faster** |
1099
-
1100
- **Average: 2.52x faster than Zod**
1101
-
1102
- ### Run Benchmarks
1103
-
1104
- ```bash
1105
- # Quick performance comparison
1106
- npm run benchmark
1107
-
1108
- # Memory usage comparison
1109
- npm run benchmark:memory
1110
-
1111
- # Startup time comparison
1112
- npm run benchmark:startup
1113
-
1114
- # Run all benchmarks
1115
- npm run benchmark:all
1116
- ```
1117
-
1118
- ## 🤝 Contributing
1119
-
1120
- Contributions are welcome! Please feel free to submit a Pull Request.
1121
-
1122
- ## 🔗 Links
1123
-
1124
- - [NPM Package](https://www.npmjs.com/package/@oxog/vld)
1125
- - [GitHub Repository](https://github.com/ersinkoc/vld)
1126
- - [Documentation](https://github.com/ersinkoc/vld#readme)
1127
-
1128
- ---
1129
-
1130
- Made with ❤️ by Ersin KOÇ
1
+ # VLD - Fast & Lightweight TypeScript Validation Library
2
+
3
+ [![NPM Version](https://img.shields.io/npm/v/@oxog/vld.svg)](https://www.npmjs.com/package/@oxog/vld) [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT) [![TypeScript](https://img.shields.io/badge/TypeScript-5.0+-blue.svg)](https://www.typescriptlang.org/) [![Zero Dependencies](https://img.shields.io/badge/Dependencies-0-green.svg)](package.json) [![Test Coverage](https://img.shields.io/badge/Coverage-99.23%25-brightgreen.svg)](package.json)
4
+
5
+ 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.
6
+
7
+ ## Table of Contents
8
+
9
+ - [Features](#-features)
10
+ - [Performance](#-performance)
11
+ - [Installation](#-installation)
12
+ - [Quick Start](#-quick-start)
13
+ - [API Reference](#-api-reference)
14
+ - [Internationalization (i18n)](#-internationalization-i18n)
15
+ - [Error Handling & Formatting](#%EF%B8%8F-error-handling--formatting)
16
+ - [Advanced Examples](#-advanced-examples)
17
+ - [Why VLD?](#-why-vld)
18
+ - [Codecs - Bidirectional Transformations](#-codecs---bidirectional-transformations)
19
+ - [Plugin System](#-plugin-system)
20
+ - [Result Pattern](#-result-pattern)
21
+ - [CLI Tools](#-cli-tools)
22
+ - [Migrating from Zod](#-migrating-from-zod)
23
+ - [Benchmarks](#-benchmarks)
24
+ - [Contributing](#-contributing)
25
+ - [Links](#-links)
26
+
27
+ ## Features
28
+
29
+ ### Core Features
30
+ - **Blazing Fast**: Optimized for V8 engine with superior performance
31
+ - **Type-Safe**: Full TypeScript support with excellent type inference
32
+ - **Zero Dependencies**: Lightweight with no external dependencies
33
+ - **Tree-Shakeable**: Only import what you need
34
+ - **Composable**: Chain validations for complex schemas
35
+ - **Advanced Error Formatting**: Tree, pretty, and flatten error utilities
36
+ - **Multi-language**: Built-in support for 27+ languages
37
+ - **99.23% Test Coverage**: Rigorously tested with 1858 passing tests
38
+ - **Industry Leading Performance**: 1.98x faster than Zod on average
39
+
40
+ ### Advanced Zod-Compatible Features
41
+ - **Type Coercion**: `v.coerce.string()`, `v.coerce.number()`, `v.coerce.boolean()`, etc.
42
+ - **Advanced Types**: BigInt, Symbol, Tuple, Record, Set, Map validation
43
+ - **Intersection Types**: Combine multiple schemas with intelligent merging
44
+ - **Custom Validation**: `refine()` for custom predicates and validation logic
45
+ - **Data Transformation**: `transform()` for post-validation data transformation
46
+ - **Default Values**: `default()` for handling undefined inputs elegantly
47
+ - **Fallback Handling**: `catch()` for graceful error recovery
48
+ - **Object Utilities**: `pick()`, `omit()`, `extend()` for flexible object schemas
49
+
50
+ ### NEW in v2.0.0 - Modular Architecture
51
+
52
+ #### Tree-Shakable Mini API
53
+ ```typescript
54
+ import { string, number, object, optional } from '@oxog/vld/mini';
55
+
56
+ const schema = object({
57
+ name: string().min(1),
58
+ age: optional(number().positive()),
59
+ });
60
+ ```
61
+ - **82% smaller bundles** when using only needed validators
62
+ - Individual factory functions for optimal tree-shaking
63
+ - Full TypeScript support with identical type inference
64
+
65
+ #### Lazy Locale Loading
66
+ ```typescript
67
+ import { setLocaleAsync } from '@oxog/vld/locales';
68
+ await setLocaleAsync('tr'); // Loads Turkish on demand
69
+ ```
70
+ - **92% bundle reduction** - Only English bundled by default
71
+ - `preloadLocales()` for SSR/batch loading
72
+ - Full backwards compatibility with `setLocale()`
73
+
74
+ #### Dual ESM/CJS Build
75
+ - ESM builds for modern bundlers (Vite, esbuild)
76
+ - CJS builds for Node.js and legacy environments
77
+ - Proper `exports` field with conditional exports
78
+
79
+ ### NEW in v1.5.0 - Major Platform Release
80
+
81
+ #### Plugin System
82
+ - **`definePlugin()`**: Create custom plugins with validators, transforms, and codecs
83
+ - **`usePlugin()`**: Register plugins globally
84
+ - **Plugin Hooks**: Lifecycle hooks for validation events
85
+ - **Custom Validators**: Extend VLD with your own validators
86
+
87
+ #### Result Pattern
88
+ - **`Ok()`/`Err()`**: Functional error handling
89
+ - **`match()`**: Pattern matching on results
90
+ - **`map()`/`flatMap()`**: Transform results
91
+ - **`tryCatch()`**: Safe function execution
92
+ - **`all()`**: Combine multiple results
93
+
94
+ #### Event System
95
+ - **`createEmitter()`**: Type-safe event emitter
96
+ - **`createEventBus()`**: Global event bus
97
+ - **Validation Events**: Parse start, success, error, field validation
98
+
99
+ #### CLI Tools
100
+ - **`vld validate`**: Validate data from command line
101
+ - **`vld benchmark`**: Run performance benchmarks
102
+ - **Colored Output**: Beautiful terminal output with pigment
103
+
104
+ #### New Validators
105
+ - **`v.discriminatedUnion()`**: Discriminated union types
106
+ - **`v.xor()`**: Exclusive OR validation
107
+ - **`v.file()`**: File upload validation
108
+ - **`v.function()`**: Function validation
109
+ - **`v.custom()`**: Type-safe custom validators
110
+ - **`v.json()`**: JSON string validation with schema
111
+ - **`v.lazy()`**: Recursive schema support
112
+ - **`v.nan()`**: NaN validation
113
+ - **`v.null()`**: Null validation
114
+ - **`v.undefined()`**: Undefined validation
115
+ - **`v.templateLiteral()`**: Template literal types
116
+
117
+ #### New String Format Validators
118
+ - **`v.hostname()`**: Hostname validation
119
+ - **`v.emoji()`**: Emoji validation
120
+ - **`v.base64()`/`v.base64url()`**: Base64 format validation
121
+ - **`v.hex()`**: Hex string validation
122
+ - **`v.jwt()`**: JWT format validation
123
+ - **`v.nanoid()`/`v.cuid()`/`v.cuid2()`/`v.ulid()`**: ID format validation
124
+ - **`v.mac()`**: MAC address validation
125
+ - **`v.cidrv4()`/`v.cidrv6()`**: CIDR block validation
126
+ - **`v.e164()`**: E.164 phone number validation
127
+ - **`v.hash()`**: Hash validation (md5, sha1, sha256, sha384, sha512)
128
+ - **`v.iso.date()`/`v.iso.time()`/`v.iso.dateTime()`/`v.iso.duration()`**: ISO format validation
129
+
130
+ #### Enhanced Object Utilities
131
+ - **`v.strictObject()`**: Strict mode object validation
132
+ - **`v.looseObject()`**: Passthrough object validation
133
+ - **`v.partialRecord()`**: Partial record validation
134
+ - **`v.looseRecord()`**: Loose record validation
135
+ - **`v.int()`**: Integer shortcut
136
+ - **`v.int32()`**: 32-bit integer shortcut
137
+ - **`v.nullish()`**: Null or undefined
138
+ - **`v.NEVER`**: NEVER constant for transforms (Zod 4 parity)
139
+
140
+ ### Codec System - Beyond Zod
141
+ - **Bidirectional Transformations**: Full encode/decode support for data conversion
142
+ - **19 Built-in Codecs**: String conversions, date parsing, JSON, URL, binary data
143
+ - **Zod-Compatible**: All `stringToNumber`, `jsonCodec`, `base64ToBytes`, etc.
144
+ - **Async Support**: Both sync and async codec operations
145
+ - **Custom Codecs**: Create your own bidirectional transformations
146
+ - **Type-Safe**: Full TypeScript support with perfect type inference
147
+
148
+ ## Performance
149
+
150
+ VLD is designed for speed and efficiency with recent optimizations delivering exceptional performance:
151
+
152
+ ### Speed Benchmarks (v1.5.0)
153
+ - **3.25x faster** for email validation
154
+ - **3.23x faster** for number validation
155
+ - **3.16x faster** for optional validation
156
+ - **2.73x faster** for safeParse operations
157
+ - **2.08x faster** for enum validation
158
+ - **2.03x faster** for simple string validation
159
+ - **1.98x faster** overall average performance
160
+
161
+ ### Optimizations
162
+ - **110x improvement** in union type validation
163
+ - **Simplified email regex** for maximum performance
164
+ - **Inline type checks** in object validation
165
+ - **Optimized loops** with direct array assignment
166
+ - **SafeParse optimization** to avoid try-catch overhead
167
+ - **Pre-computed keys** with Set for O(1) lookups
168
+
169
+ ### Memory Efficiency
170
+ - **98% less memory** for validator creation
171
+ - **51% less memory** for data parsing
172
+ - **86% less memory** for error handling
173
+ - **78% less memory** overall average
174
+
175
+ ### A Note on Real-World Benchmarking
176
+
177
+ Many validation library benchmarks can be misleading because they often test with **reused schema instances**:
178
+
179
+ ```javascript
180
+ // What benchmarks typically test (unrealistic):
181
+ const schema = z.string();
182
+ for (let i = 0; i < 1000000; i++) {
183
+ schema.parse(data); // Same instance reused
184
+ }
185
+
186
+ // What happens in real applications:
187
+ app.post('/api/user', (req, res) => {
188
+ // New schema created for each request
189
+ const schema = z.object({
190
+ email: z.string().email(),
191
+ age: z.number().min(18)
192
+ });
193
+ schema.parse(req.body);
194
+ });
195
+ ```
196
+
197
+ When testing real-world patterns:
198
+ - **Creating new instances**: VLD is **2000x faster** than Zod
199
+ - **Reused instances**: Zod benefits from V8's singleton optimization
200
+ - **Real applications**: Schemas are often created dynamically, where VLD excels
201
+
202
+ Run `npm run benchmark:truth` to see the real performance difference.
203
+
204
+ ## Installation
205
+
206
+ ```bash
207
+ npm install @oxog/vld
208
+ # or
209
+ yarn add @oxog/vld
210
+ # or
211
+ pnpm add @oxog/vld
212
+ ```
213
+
214
+ ## Quick Start
215
+
216
+ ```typescript
217
+ import { v } from '@oxog/vld';
218
+
219
+ // It is recommended to import as `v` for consistency with Zod's `z`
220
+ // and for a more concise syntax.
221
+
222
+ // Define a schema
223
+ const userSchema = v.object({
224
+ name: v.string().min(2),
225
+ email: v.string().email(),
226
+ age: v.number().min(18).max(100),
227
+ isActive: v.boolean()
228
+ });
229
+
230
+ // Validate data
231
+ const result = userSchema.safeParse({
232
+ name: 'John Doe',
233
+ email: 'john@example.com',
234
+ age: 25,
235
+ isActive: true
236
+ });
237
+
238
+ if (result.success) {
239
+ console.log('Valid user:', result.data);
240
+ } else {
241
+ console.log('Validation error:', result.error);
242
+ }
243
+ ```
244
+
245
+ For advanced error formatting:
246
+ ```typescript
247
+ import { v, VldError, treeifyError, prettifyError, flattenError } from '@oxog/vld';
248
+ ```
249
+
250
+ ## API Reference
251
+
252
+ ### Basic Types
253
+
254
+ ```typescript
255
+ v.string() // String validation
256
+ v.number() // Number validation
257
+ v.int() // Integer validation (shortcut)
258
+ v.int32() // 32-bit integer validation
259
+ v.boolean() // Boolean validation
260
+ v.bigint() // BigInt validation
261
+ v.symbol() // Symbol validation
262
+ v.date() // Date validation
263
+ v.uint8array()// Uint8Array validation
264
+ v.literal() // Literal values
265
+ v.enum() // Enum values (supports TypeScript enums)
266
+ v.any() // Any type
267
+ v.unknown() // Unknown type
268
+ v.void() // Void type
269
+ v.never() // Never type
270
+ v.null() // Null type
271
+ v.undefined() // Undefined type
272
+ v.nan() // NaN type
273
+ ```
274
+
275
+ ### Advanced Types
276
+
277
+ ```typescript
278
+ // Collections
279
+ v.array(v.string()) // Array validation
280
+ v.tuple(v.string(), v.number()) // Fixed-length tuple
281
+ v.record(v.number()) // Record/dictionary validation
282
+ v.set(v.string()) // Set validation
283
+ v.map(v.string(), v.number()) // Map validation
284
+
285
+ // Objects
286
+ v.object({ // Object schema
287
+ name: v.string(),
288
+ age: v.number()
289
+ })
290
+ v.strictObject({...}) // No extra fields allowed
291
+ v.looseObject({...}) // Extra fields passed through
292
+
293
+ // Composition
294
+ v.union(v.string(), v.number()) // Union types
295
+ v.intersection(schemaA, schemaB) // Intersection types
296
+ v.discriminatedUnion('type', ...) // Discriminated union
297
+ v.xor(schemaA, schemaB) // Exclusive OR
298
+ v.optional(v.string()) // Optional fields
299
+ v.nullable(v.string()) // Nullable fields
300
+ v.nullish(v.string()) // Null or undefined
301
+ ```
302
+
303
+ ### String Validators
304
+
305
+ ```typescript
306
+ v.string()
307
+ .min(5) // Minimum length
308
+ .max(10) // Maximum length
309
+ .length(8) // Exact length
310
+ .email() // Email format
311
+ .url() // URL format
312
+ .uuid() // UUID format
313
+ .regex(/pattern/) // Custom regex
314
+ .startsWith('prefix') // String prefix
315
+ .endsWith('suffix') // String suffix
316
+ .includes('substring') // Contains substring
317
+ .ip() // IP address (v4 or v6)
318
+ .trim() // Trim whitespace
319
+ .toLowerCase() // Convert to lowercase
320
+ .toUpperCase() // Convert to uppercase
321
+ .nonempty() // Non-empty string
322
+ ```
323
+
324
+ ### String Format Validators (Top-Level)
325
+
326
+ ```typescript
327
+ v.email() // Email validation
328
+ v.uuid() // UUID validation
329
+ v.uuid({ version: 'v4' }) // UUID v4 validation
330
+ v.uuidv4() // UUID v4 shortcut
331
+ v.hostname() // Hostname validation
332
+ v.emoji() // Emoji validation
333
+ v.base64() // Base64 format
334
+ v.base64url() // Base64 URL-safe format
335
+ v.hex() // Hex string
336
+ v.jwt() // JWT format
337
+ v.nanoid() // NanoID format
338
+ v.cuid() // CUID format
339
+ v.cuid2() // CUID2 format
340
+ v.ulid() // ULID format
341
+ v.ipv4() // IPv4 address
342
+ v.ipv6() // IPv6 address
343
+ v.mac() // MAC address
344
+ v.cidrv4() // IPv4 CIDR block
345
+ v.cidrv6() // IPv6 CIDR block
346
+ v.e164() // E.164 phone number
347
+ v.hash('sha256') // Hash validation
348
+ v.iso.date() // ISO date format
349
+ v.iso.time() // ISO time format
350
+ v.iso.dateTime() // ISO datetime format
351
+ v.iso.duration() // ISO duration format
352
+ ```
353
+
354
+ ### Number Validators
355
+
356
+ ```typescript
357
+ v.number()
358
+ .min(0) // Minimum value
359
+ .max(100) // Maximum value
360
+ .int() // Integer only
361
+ .positive() // Positive numbers
362
+ .negative() // Negative numbers
363
+ .nonnegative() // >= 0
364
+ .nonpositive() // <= 0
365
+ .finite() // Finite numbers
366
+ .safe() // Safe integers
367
+ .multipleOf(5) // Multiple of value
368
+ ```
369
+
370
+ ### Arrays and Objects
371
+
372
+ ```typescript
373
+ // Arrays
374
+ v.array(v.string()) // Array of strings
375
+ .min(1) // Minimum length
376
+ .max(10) // Maximum length
377
+ .length(5) // Exact length
378
+ .nonempty() // Non-empty array
379
+
380
+ // Objects
381
+ v.object({
382
+ name: v.string(),
383
+ age: v.number()
384
+ })
385
+ .partial() // All fields optional
386
+ .strict() // No extra fields
387
+ .passthrough() // Allow extra fields
388
+ ```
389
+
390
+ ### Composite Types
391
+
392
+ ```typescript
393
+ // Optional
394
+ v.optional(v.string()) // string | undefined
395
+
396
+ // Nullable
397
+ v.nullable(v.string()) // string | null
398
+
399
+ // Nullish
400
+ v.nullish(v.string()) // string | null | undefined
401
+
402
+ // Union
403
+ v.union(v.string(), v.number()) // string | number
404
+
405
+ // Discriminated Union
406
+ v.discriminatedUnion('type',
407
+ v.object({ type: v.literal('a'), a: v.string() }),
408
+ v.object({ type: v.literal('b'), b: v.number() })
409
+ )
410
+
411
+ // Literal
412
+ v.literal('active') // 'active'
413
+
414
+ // Enum
415
+ v.enum('red', 'green', 'blue') // 'red' | 'green' | 'blue'
416
+ ```
417
+
418
+ ### Type Coercion
419
+
420
+ ```typescript
421
+ // Coerce strings from various types
422
+ v.coerce.string().parse(123) // "123"
423
+ v.coerce.string().parse(true) // "true"
424
+
425
+ // Coerce numbers from strings/booleans
426
+ v.coerce.number().parse("123") // 123
427
+ v.coerce.number().parse(true) // 1
428
+
429
+ // Coerce booleans from strings/numbers
430
+ v.coerce.boolean().parse("true") // true
431
+ v.coerce.boolean().parse(1) // true
432
+
433
+ // Coerce BigInt from strings/numbers
434
+ v.coerce.bigint().parse("123") // 123n
435
+ v.coerce.bigint().parse(456) // 456n
436
+
437
+ // Coerce Date from strings/timestamps
438
+ v.coerce.date().parse("2023-01-01") // Date object
439
+ v.coerce.date().parse(1672531200000) // Date object
440
+ ```
441
+
442
+ ### Object Schema Methods
443
+
444
+ ```typescript
445
+ const userSchema = v.object({
446
+ name: v.string(),
447
+ age: v.number(),
448
+ email: v.string(),
449
+ role: v.string()
450
+ });
451
+
452
+ // Pick specific fields
453
+ const publicSchema = userSchema.pick('name', 'age');
454
+ // Type: { name: string; age: number }
455
+
456
+ // Omit sensitive fields
457
+ const safeSchema = userSchema.omit('email', 'role');
458
+ // Type: { name: string; age: number }
459
+
460
+ // Extend with new fields
461
+ const extendedSchema = userSchema.extend({
462
+ isActive: v.boolean(),
463
+ lastLogin: v.date()
464
+ });
465
+ // Type: { name: string; age: number; email: string; role: string; isActive: boolean; lastLogin: Date }
466
+ ```
467
+
468
+ ### Advanced Validation Methods
469
+
470
+ ```typescript
471
+ // Custom validation with refine()
472
+ const positiveNumber = v.number()
473
+ .refine(n => n > 0, "Number must be positive");
474
+
475
+ // Data transformation with transform()
476
+ const uppercaseString = v.string()
477
+ .transform(s => s.toUpperCase());
478
+
479
+ // Default values for undefined
480
+ const withDefault = v.string().default("fallback");
481
+ withDefault.parse(undefined); // "fallback"
482
+
483
+ // Catch errors and provide fallback
484
+ const withCatch = v.number().catch(-1);
485
+ withCatch.parse("invalid"); // -1
486
+
487
+ // Method chaining
488
+ const complexSchema = v.string()
489
+ .min(3)
490
+ .transform(s => s.trim())
491
+ .refine(s => s.includes('@'), 'Must contain @')
492
+ .default('user@example.com');
493
+ ```
494
+
495
+ ### Special Validators
496
+
497
+ ```typescript
498
+ // JSON validator with optional schema
499
+ v.json() // Any valid JSON
500
+ v.json(v.object({ name: v.string() })) // Typed JSON
501
+
502
+ // Lazy for recursive schemas
503
+ const categorySchema = v.lazy(() =>
504
+ v.object({
505
+ name: v.string(),
506
+ children: v.array(categorySchema).optional()
507
+ })
508
+ );
509
+
510
+ // Custom validator
511
+ v.custom({
512
+ check: (val) => typeof val === 'string' && val.length > 0,
513
+ message: 'Must be a non-empty string'
514
+ });
515
+
516
+ // File validator
517
+ v.file()
518
+ .maxSize(5 * 1024 * 1024) // 5MB
519
+ .type(['image/png', 'image/jpeg']);
520
+
521
+ // Function validator
522
+ v.function()
523
+ .args(v.string(), v.number())
524
+ .returns(v.boolean());
525
+ ```
526
+
527
+ ### Type Inference
528
+
529
+ ```typescript
530
+ import { v, Infer } from '@oxog/vld';
531
+
532
+ const schema = v.object({
533
+ name: v.string(),
534
+ age: v.number()
535
+ });
536
+
537
+ // Automatically infer the type
538
+ type User = Infer<typeof schema>;
539
+ // { name: string; age: number }
540
+ ```
541
+
542
+ ### Error Formatting Types
543
+
544
+ ```typescript
545
+ import {
546
+ VldError, // Main error class
547
+ VldIssue, // Individual validation issue
548
+ VldErrorTree, // Nested error structure
549
+ VldFlattenedError // Flattened error structure
550
+ } from '@oxog/vld';
551
+ ```
552
+
553
+ ### Custom Error Messages
554
+
555
+ ```typescript
556
+ const schema = v.string().min(8, 'Password must be at least 8 characters');
557
+
558
+ const result = schema.safeParse('short');
559
+ if (!result.success) {
560
+ console.log(result.error.message); // 'Password must be at least 8 characters'
561
+ }
562
+ ```
563
+
564
+ ## Internationalization (i18n)
565
+
566
+ VLD supports 27+ languages out of the box with comprehensive error messages:
567
+
568
+ ```typescript
569
+ import { v, setLocale } from '@oxog/vld';
570
+
571
+ // Default is English
572
+ const schema = v.string().min(5);
573
+ schema.safeParse('Hi'); // Error: "String must be at least 5 characters"
574
+
575
+ // Switch to Turkish
576
+ setLocale('tr');
577
+ schema.safeParse('Hi'); // Error: "Metin en az 5 karakter olmali"
578
+
579
+ // Switch to Spanish
580
+ setLocale('es');
581
+ schema.safeParse('Hi'); // Error: "La cadena debe tener al menos 5 caracteres"
582
+
583
+ // Switch to Japanese
584
+ setLocale('ja');
585
+ schema.safeParse('Hi'); // Error: "..."
586
+ ```
587
+
588
+ ### Supported Languages
589
+
590
+ #### Base Languages (15):
591
+ - English (`en`) - Turkish (`tr`) - Spanish (`es`) - French (`fr`) - German (`de`)
592
+ - Italian (`it`) - Portuguese (`pt`) - Russian (`ru`) - Japanese (`ja`) - Korean (`ko`)
593
+ - Chinese (`zh`) - Arabic (`ar`) - Hindi (`hi`) - Dutch (`nl`) - Polish (`pl`)
594
+
595
+ #### European Languages (4):
596
+ - Danish (`da`) - Swedish (`sv`) - Norwegian (`no`) - Finnish (`fi`)
597
+
598
+ #### Asian Languages (4):
599
+ - Thai (`th`) - Vietnamese (`vi`) - Indonesian (`id`) - Bengali (`bn`)
600
+
601
+ #### African Languages (2):
602
+ - Swahili (`sw`) - Afrikaans (`af`)
603
+
604
+ #### American Languages (2):
605
+ - Portuguese Brazil (`pt-BR`) - Spanish Mexico (`es-MX`)
606
+
607
+ **Plus 75+ additional languages** supported through comprehensive type definitions with English fallback.
608
+
609
+ ## Error Handling & Formatting
610
+
611
+ VLD provides advanced error formatting utilities similar to Zod's error handling system.
612
+
613
+ ### Error Formatting Utilities
614
+
615
+ ```typescript
616
+ import { v, VldError, treeifyError, prettifyError, flattenError } from '@oxog/vld';
617
+
618
+ const userSchema = v.object({
619
+ username: v.string().min(3),
620
+ favoriteNumbers: v.array(v.number()),
621
+ profile: v.object({
622
+ name: v.string(),
623
+ email: v.string().email()
624
+ })
625
+ });
626
+
627
+ const result = userSchema.safeParse({
628
+ username: 'ab',
629
+ favoriteNumbers: [1, 'two', 3],
630
+ profile: {
631
+ name: '',
632
+ email: 'invalid-email'
633
+ },
634
+ extraField: 'not allowed'
635
+ });
636
+
637
+ if (!result.success) {
638
+ const error = result.error as VldError;
639
+
640
+ // 1. Tree Format - Nested structure for complex UIs
641
+ const tree = treeifyError(error);
642
+
643
+ // 2. Pretty Format - Human-readable console output
644
+ const pretty = prettifyError(error);
645
+
646
+ // 3. Flatten Format - Simple form validation
647
+ const flattened = flattenError(error);
648
+ }
649
+ ```
650
+
651
+ ### Using Error Formats in Practice
652
+
653
+ #### React Form Validation
654
+ ```typescript
655
+ function UserForm() {
656
+ const [errors, setErrors] = useState<VldFlattenedError | null>(null);
657
+
658
+ const handleSubmit = (data: unknown) => {
659
+ const result = userSchema.safeParse(data);
660
+
661
+ if (!result.success) {
662
+ setErrors(flattenError(result.error as VldError));
663
+ } else {
664
+ setErrors(null);
665
+ }
666
+ };
667
+
668
+ return (
669
+ <form onSubmit={handleSubmit}>
670
+ {errors?.formErrors.map(error => (
671
+ <div key={error} className="form-error">{error}</div>
672
+ ))}
673
+
674
+ <input name="username" />
675
+ {errors?.fieldErrors.username?.map(error => (
676
+ <div key={error} className="field-error">{error}</div>
677
+ ))}
678
+ </form>
679
+ );
680
+ }
681
+ ```
682
+
683
+ #### API Error Responses
684
+ ```typescript
685
+ app.post('/api/users', (req, res) => {
686
+ const result = userSchema.safeParse(req.body);
687
+
688
+ if (!result.success) {
689
+ const tree = treeifyError(result.error as VldError);
690
+ res.status(400).json({
691
+ error: 'Validation failed',
692
+ details: tree
693
+ });
694
+ }
695
+ });
696
+ ```
697
+
698
+ ## Advanced Examples
699
+
700
+ ### Complex Validation with New Features
701
+
702
+ ```typescript
703
+ const postSchema = v.object({
704
+ id: v.union(v.string().uuid(), v.number()),
705
+ title: v.string().min(5).max(100),
706
+ content: v.string().min(10),
707
+ author: v.object({
708
+ name: v.string(),
709
+ email: v.string().email(),
710
+ age: v.coerce.number(),
711
+ }),
712
+ tags: v.set(v.string()).default(new Set()),
713
+ metadata: v.record(v.any()),
714
+ coordinates: v.tuple(v.number(), v.number()),
715
+ publishedAt: v.date().default(() => new Date()),
716
+ status: v.enum('draft', 'published', 'archived')
717
+ });
718
+
719
+ // Extend with additional fields
720
+ const blogPostSchema = postSchema.extend({
721
+ viewCount: v.bigint().default(0n),
722
+ categories: v.array(v.string()).min(1),
723
+ featured: v.boolean().default(false)
724
+ });
725
+ ```
726
+
727
+ ### Discriminated Union
728
+
729
+ ```typescript
730
+ const eventSchema = v.discriminatedUnion('type',
731
+ v.object({
732
+ type: v.literal('click'),
733
+ x: v.number(),
734
+ y: v.number()
735
+ }),
736
+ v.object({
737
+ type: v.literal('scroll'),
738
+ direction: v.enum('up', 'down'),
739
+ distance: v.number()
740
+ }),
741
+ v.object({
742
+ type: v.literal('keypress'),
743
+ key: v.string(),
744
+ modifiers: v.array(v.enum('ctrl', 'alt', 'shift'))
745
+ })
746
+ );
747
+
748
+ // Type-safe parsing
749
+ const event = eventSchema.parse({
750
+ type: 'click',
751
+ x: 100,
752
+ y: 200
753
+ });
754
+ ```
755
+
756
+ ### Recursive Schemas
757
+
758
+ ```typescript
759
+ const categorySchema: ReturnType<typeof v.lazy> = v.lazy(() =>
760
+ v.object({
761
+ name: v.string(),
762
+ slug: v.string().regex(/^[a-z0-9-]+$/),
763
+ children: v.array(categorySchema).optional()
764
+ })
765
+ );
766
+
767
+ const category = categorySchema.parse({
768
+ name: 'Electronics',
769
+ slug: 'electronics',
770
+ children: [
771
+ {
772
+ name: 'Phones',
773
+ slug: 'phones',
774
+ children: [
775
+ { name: 'Smartphones', slug: 'smartphones' }
776
+ ]
777
+ }
778
+ ]
779
+ });
780
+ ```
781
+
782
+ ### Type-Safe Forms
783
+
784
+ ```typescript
785
+ const loginSchema = v.object({
786
+ username: v.string().min(3),
787
+ password: v.string().min(8),
788
+ rememberMe: v.optional(v.boolean())
789
+ });
790
+
791
+ type LoginForm = Infer<typeof loginSchema>;
792
+
793
+ function handleLogin(data: unknown) {
794
+ const result = loginSchema.safeParse(data);
795
+
796
+ if (result.success) {
797
+ const { username, password, rememberMe } = result.data;
798
+ // ... handle login
799
+ }
800
+ }
801
+ ```
802
+
803
+ ## Codecs - Bidirectional Transformations
804
+
805
+ VLD introduces **codecs** - powerful bidirectional transformations that can convert data between different representations.
806
+
807
+ ### What are Codecs?
808
+
809
+ Codecs enable safe, type-checked conversions between different data formats:
810
+
811
+ ```typescript
812
+ import { stringToNumber, jsonCodec, base64ToBytes } from '@oxog/vld';
813
+
814
+ // String to number conversion
815
+ const age = stringToNumber.parse('25'); // 25
816
+ const price = stringToNumber.encode(99.99); // "99.99"
817
+
818
+ // JSON codec
819
+ const userJson = jsonCodec();
820
+ const user = userJson.parse('{"name":"John","age":30}');
821
+ const jsonString = userJson.encode(user);
822
+
823
+ // Binary data
824
+ const bytes = base64ToBytes.parse('SGVsbG8gV29ybGQ=');
825
+ ```
826
+
827
+ ### Built-in Codecs
828
+
829
+ #### String Conversion Codecs
830
+ ```typescript
831
+ import { stringToNumber, stringToInt, stringToBigInt, stringToBoolean } from '@oxog/vld';
832
+
833
+ stringToNumber.parse('42.5'); // 42.5
834
+ stringToInt.parse('42'); // 42
835
+ stringToBigInt.parse('123n'); // 123n
836
+ stringToBoolean.parse('true'); // true
837
+ ```
838
+
839
+ #### Date Conversion Codecs
840
+ ```typescript
841
+ import { isoDatetimeToDate, epochSecondsToDate, epochMillisToDate } from '@oxog/vld';
842
+
843
+ isoDatetimeToDate.parse('2023-12-25T10:30:00.000Z'); // Date
844
+ epochSecondsToDate.parse(1703505000); // Date
845
+ epochMillisToDate.parse(1703505000000); // Date
846
+ ```
847
+
848
+ #### URL Codecs
849
+ ```typescript
850
+ import { stringToURL, stringToHttpURL, uriComponent } from '@oxog/vld';
851
+
852
+ stringToURL.parse('https://example.com/path?q=1');
853
+ stringToHttpURL.parse('https://api.example.com');
854
+ uriComponent.parse('Hello World!'); // "Hello%20World!"
855
+ ```
856
+
857
+ #### Binary Data Codecs
858
+ ```typescript
859
+ import { base64ToBytes, hexToBytes, utf8ToBytes, bytesToUtf8 } from '@oxog/vld';
860
+
861
+ base64ToBytes.parse('SGVsbG8='); // Uint8Array
862
+ hexToBytes.parse('48656c6c6f'); // Uint8Array
863
+ utf8ToBytes.parse('Hello'); // Uint8Array
864
+ bytesToUtf8.parse(new Uint8Array([72, 101, 108, 108, 111])); // "Hello"
865
+ ```
866
+
867
+ ### Custom Codecs
868
+
869
+ ```typescript
870
+ const csvToArray = v.codec(
871
+ v.string(),
872
+ v.array(v.string()),
873
+ {
874
+ decode: (csv: string) => csv.split(',').map(s => s.trim()),
875
+ encode: (arr: string[]) => arr.join(', ')
876
+ }
877
+ );
878
+
879
+ const tags = csvToArray.parse('react, typescript, vld');
880
+ // ["react", "typescript", "vld"]
881
+
882
+ const csvString = csvToArray.encode(['node', 'express', 'api']);
883
+ // "node, express, api"
884
+ ```
885
+
886
+ ## Plugin System
887
+
888
+ VLD v1.5.0 introduces a powerful plugin system for extending functionality.
889
+
890
+ ### Creating a Plugin
891
+
892
+ ```typescript
893
+ import { definePlugin, usePlugin, v } from '@oxog/vld';
894
+
895
+ // Define a custom plugin
896
+ const myPlugin = definePlugin({
897
+ name: 'my-plugin',
898
+ version: '1.0.0',
899
+
900
+ // Custom validators
901
+ validators: {
902
+ phoneNumber: () => v.string().regex(/^\+?[1-9]\d{1,14}$/),
903
+ postalCode: () => v.string().regex(/^\d{5}(-\d{4})?$/)
904
+ },
905
+
906
+ // Custom transforms
907
+ transforms: {
908
+ normalizePhone: (phone: string) => phone.replace(/[^\d+]/g, '')
909
+ },
910
+
911
+ // Lifecycle hooks
912
+ install(kernel) {
913
+ console.log('Plugin installed!');
914
+ }
915
+ });
916
+
917
+ // Register the plugin
918
+ usePlugin(myPlugin);
919
+ ```
920
+
921
+ ### Using Plugin Validators
922
+
923
+ ```typescript
924
+ import { createVldKernel, usePlugin } from '@oxog/vld';
925
+
926
+ const kernel = createVldKernel({ debug: true });
927
+
928
+ kernel.use(myPlugin);
929
+
930
+ // Access custom validators
931
+ const phoneSchema = kernel.validator('phoneNumber');
932
+ phoneSchema.parse('+1234567890');
933
+ ```
934
+
935
+ ## Result Pattern
936
+
937
+ VLD v1.5.0 includes a functional Result pattern for error handling.
938
+
939
+ ### Basic Usage
940
+
941
+ ```typescript
942
+ import { Ok, Err, match, map, flatMap, tryCatch } from '@oxog/vld';
943
+
944
+ // Create results
945
+ const success = Ok(42);
946
+ const failure = Err(new Error('Something went wrong'));
947
+
948
+ // Pattern matching
949
+ const message = match(success, {
950
+ ok: (value) => `Got: ${value}`,
951
+ err: (error) => `Error: ${error.message}`
952
+ });
953
+
954
+ // Transform results
955
+ const doubled = map(success, (n) => n * 2); // Ok(84)
956
+
957
+ // Chain operations
958
+ const result = flatMap(success, (n) =>
959
+ n > 0 ? Ok(n * 2) : Err(new Error('Must be positive'))
960
+ );
961
+
962
+ // Safe function execution
963
+ const parsed = tryCatch(() => JSON.parse('{"a":1}'));
964
+ ```
965
+
966
+ ### With Validation
967
+
968
+ ```typescript
969
+ import { v, isOk, isErr, unwrapOr } from '@oxog/vld';
970
+
971
+ const schema = v.object({
972
+ name: v.string(),
973
+ age: v.number().min(0)
974
+ });
975
+
976
+ const result = schema.safeParse(data);
977
+
978
+ if (isOk(result)) {
979
+ console.log('Valid:', result.data);
980
+ } else {
981
+ console.log('Invalid:', result.error);
982
+ }
983
+
984
+ // With default value
985
+ const user = unwrapOr(result, { name: 'Guest', age: 0 });
986
+ ```
987
+
988
+ ### Combining Results
989
+
990
+ ```typescript
991
+ import { all, fromNullable } from '@oxog/vld';
992
+
993
+ // Combine multiple results
994
+ const results = [Ok(1), Ok(2), Ok(3)];
995
+ const combined = all(results); // Ok([1, 2, 3])
996
+
997
+ // Convert nullable to Result
998
+ const maybeValue: string | null = getValue();
999
+ const result = fromNullable(maybeValue, new Error('Value is null'));
1000
+ ```
1001
+
1002
+ ## CLI Tools
1003
+
1004
+ VLD includes command-line tools for validation and benchmarking.
1005
+
1006
+ ### Installation
1007
+
1008
+ ```bash
1009
+ npm install -g @oxog/vld
1010
+ # or use npx
1011
+ npx vld --help
1012
+ ```
1013
+
1014
+ ### Commands
1015
+
1016
+ ```bash
1017
+ # Show help
1018
+ vld --help
1019
+
1020
+ # Validate data
1021
+ vld validate schema.json data.json
1022
+
1023
+ # Run benchmarks
1024
+ vld benchmark
1025
+
1026
+ # Show version
1027
+ vld --version
1028
+ ```
1029
+
1030
+ ### Programmatic CLI
1031
+
1032
+ ```typescript
1033
+ import { createCli, vldCli } from '@oxog/vld/cli';
1034
+
1035
+ // Use the built-in CLI
1036
+ vldCli.run(process.argv.slice(2));
1037
+
1038
+ // Or create a custom CLI
1039
+ const cli = createCli('my-app', '1.0.0', 'My validation app')
1040
+ .command({
1041
+ name: 'validate',
1042
+ description: 'Validate data',
1043
+ action: async (args, options) => {
1044
+ // Custom validation logic
1045
+ }
1046
+ });
1047
+
1048
+ cli.run(process.argv.slice(2));
1049
+ ```
1050
+
1051
+ ## Logger & Colored Output
1052
+
1053
+ VLD includes a logging system and colored terminal output.
1054
+
1055
+ ### Logger
1056
+
1057
+ ```typescript
1058
+ import { createLogger, setLogLevel, enableDebug } from '@oxog/vld';
1059
+
1060
+ // Create a logger
1061
+ const logger = createLogger({ prefix: 'VLD' });
1062
+
1063
+ logger.info('Processing...');
1064
+ logger.warn('Deprecated feature');
1065
+ logger.error('Validation failed');
1066
+ logger.debug('Debug info');
1067
+
1068
+ // Set log level globally
1069
+ setLogLevel('debug');
1070
+
1071
+ // Enable debug mode
1072
+ enableDebug();
1073
+ ```
1074
+
1075
+ ### Colored Output (Pigment)
1076
+
1077
+ ```typescript
1078
+ import { pigment, red, green, blue, bold, dim } from '@oxog/vld';
1079
+
1080
+ console.log(red('Error!'));
1081
+ console.log(green('Success!'));
1082
+ console.log(bold(blue('Important')));
1083
+ console.log(dim('Less important'));
1084
+
1085
+ // Or use the pigment object
1086
+ console.log(pigment.red('Error!'));
1087
+ console.log(pigment.bold(pigment.green('Success!')));
1088
+ ```
1089
+
1090
+ ## VLD vs. Zod
1091
+
1092
+ VLD is designed as a compelling alternative to Zod, offering full feature parity while delivering significant improvements.
1093
+
1094
+ ### Feature Comparison
1095
+
1096
+ | Feature | VLD | Zod |
1097
+ | ----------------------- | ---------------------------------- | ------------------------------------ |
1098
+ | **Performance** | **~1.98x faster** (average) | Baseline |
1099
+ | **Memory Usage** | **~78% less** overall | Baseline |
1100
+ | **Internationalization**| **Built-in (27+ languages)** | Requires third-party library |
1101
+ | **Dependencies** | **Zero** | `zod-i18n` for locales |
1102
+ | **Bundle Size** | Smaller | Larger |
1103
+ | **API** | 100% Zod-compatible | Standard Zod API |
1104
+ | **Plugin System** | **Built-in** | Not available |
1105
+ | **Result Pattern** | **Built-in** | Not available |
1106
+ | **CLI Tools** | **Built-in** | Not available |
1107
+ | **Codecs** | Built-in, bidirectional | Via external `zod-codecs` |
1108
+ | **Type Inference** | Excellent | Excellent |
1109
+
1110
+ ### Seamless Migration from Zod
1111
+
1112
+ ```javascript
1113
+ // Before (Zod)
1114
+ import { z } from 'zod';
1115
+ const schema = z.string().email();
1116
+
1117
+ // After (VLD) - Exact same syntax!
1118
+ import { v } from '@oxog/vld';
1119
+ const schema = v.string().email();
1120
+ ```
1121
+
1122
+ ## Benchmarks
1123
+
1124
+ ### Performance Results
1125
+
1126
+ | Test Case | VLD Performance | Improvement |
1127
+ |-----------|----------------|-------------|
1128
+ | Simple String | 73.0M ops/sec | **2.03x faster** |
1129
+ | Email Validation | 21.8M ops/sec | **3.25x faster** |
1130
+ | Number Validation | 36.3M ops/sec | **3.23x faster** |
1131
+ | Simple Object | 7.1M ops/sec | **1.02x faster** |
1132
+ | Complex Object | 1.9M ops/sec | **1.34x faster** |
1133
+ | Array Validation | 7.5M ops/sec | **1.35x faster** |
1134
+ | Union Types | 7.1M ops/sec | **1.29x faster** |
1135
+ | Optional Values | 36.1M ops/sec | **3.16x faster** |
1136
+ | SafeParse | 60.0M ops/sec | **2.73x faster** |
1137
+ | Type Coercion | 20.4M ops/sec | **1.01x faster** |
1138
+ | Enum Validation | 60.3M ops/sec | **2.08x faster** |
1139
+ | Discriminated Union | 3.6M ops/sec | Zod 1.27x faster |
1140
+
1141
+ **VLD won 11/12 tests | Average: 1.98x faster than Zod**
1142
+
1143
+ ### Run Benchmarks
1144
+
1145
+ ```bash
1146
+ # Quick performance comparison
1147
+ npm run benchmark
1148
+
1149
+ # Memory usage comparison
1150
+ npm run benchmark:memory
1151
+
1152
+ # Startup time comparison
1153
+ npm run benchmark:startup
1154
+
1155
+ # Run all benchmarks
1156
+ npm run benchmark:all
1157
+ ```
1158
+
1159
+ ## Contributing
1160
+
1161
+ Contributions are welcome! Please feel free to submit a Pull Request.
1162
+
1163
+ ## Links
1164
+
1165
+ - [Documentation](https://vld.oxog.dev)
1166
+ - [NPM Package](https://www.npmjs.com/package/@oxog/vld)
1167
+ - [GitHub Repository](https://github.com/ersinkoc/vld)
1168
+
1169
+ ---
1170
+
1171
+ Made with Love by [Ersin KOC](https://github.com/ersinkoc)