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